/**
 * KeyPilot Chrome Extension — esbuild bundle
 * Generated on 2026-09-04T03:11:43.686Z
 */

(() => {
  var __defProp = Object.defineProperty;
  var __getOwnPropNames = Object.getOwnPropertyNames;
  var __esm = (fn, res, err) => function __init() {
    if (err) throw err[0];
    try {
      return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
    } catch (e) {
      throw err = [e], e;
    }
  };
  var __export = (target, all) => {
    for (var name in all)
      __defProp(target, name, { get: all[name], enumerable: true });
  };

  // src/config/keyboard-layouts.js
  function isBuildExcludedKeyAction(actionId) {
    const id = String(actionId || "");
    return !!id && BUILD_EXCLUDED_KEY_ACTION_SET.has(id);
  }
  function normalizeKeyboardLayoutId(raw) {
    const v = String(raw || "").trim();
    if (KNOWN_BUILTIN_LAYOUT_IDS.has(v)) return (
      /** @type {BuiltinKeyboardLayoutId} */
      v
    );
    return DEFAULT_KEYBOARD_LAYOUT_ID;
  }
  function normalizeKeyboardLayoutFamilyId(raw) {
    const v = String(raw || "").trim();
    if (v === "navigation") return "browsing";
    if (!v) return DEFAULT_KEYBOARD_LAYOUT_FAMILY_ID;
    const known = BUILTIN_KEYBOARD_LAYOUT_FAMILIES_META.some((m) => m && m.id === v);
    if (known) return v;
    if (Object.prototype.hasOwnProperty.call(LEGACY_KEYBOARD_LAYOUT_FAMILY_VARIANTS, v)) {
      return (
        /** @type {KeyboardLayoutFamilyId} */
        v
      );
    }
    return DEFAULT_KEYBOARD_LAYOUT_FAMILY_ID;
  }
  function builtinFamilySelectValue(familyId) {
    return `builtin:${normalizeKeyboardLayoutFamilyId(familyId)}`;
  }
  function parseBuiltinFamilySelectValue(value) {
    const v = String(value || "").trim();
    if (!v || v.startsWith("user:")) return null;
    if (v === "builtin") return DEFAULT_KEYBOARD_LAYOUT_FAMILY_ID;
    if (v.startsWith("builtin:")) {
      return normalizeKeyboardLayoutFamilyId(v.slice("builtin:".length));
    }
    return null;
  }
  function formatBuiltinFamilyLabel(familyId) {
    const id = normalizeKeyboardLayoutFamilyId(familyId);
    const meta = BUILTIN_KEYBOARD_LAYOUT_FAMILIES_META.find((m) => m && m.id === id);
    if (meta?.label) return `Built-in: ${meta.label}`;
    if (id === "basic-navigation") return "Built-in: Basic Navigation";
    return "Built-in: Browsing";
  }
  function normalizeKeyboardHandedness(raw) {
    const v = String(raw || "").trim().toLowerCase();
    if (v === "left" || v === "right") return (
      /** @type {KeyboardHandedness} */
      v
    );
    return DEFAULT_KEYBOARD_HANDEDNESS;
  }
  function resolveKeyboardLayoutId({ familyId, handedness } = {}) {
    const fam = normalizeKeyboardLayoutFamilyId(familyId);
    const hand = normalizeKeyboardHandedness(handedness);
    const meta = BUILTIN_KEYBOARD_LAYOUT_FAMILIES_META.find((m) => m && m.id === fam);
    const legacy = LEGACY_KEYBOARD_LAYOUT_FAMILY_VARIANTS[fam];
    const resolved = meta?.variants?.[hand] || legacy?.[hand];
    return normalizeKeyboardLayoutId(resolved);
  }
  function inferFamilyAndHandednessFromLayoutId(rawLayoutId) {
    const id = normalizeKeyboardLayoutId(rawLayoutId);
    if (id.endsWith("-left")) {
      const familyId = id.slice(0, -"-left".length);
      return {
        familyId: normalizeKeyboardLayoutFamilyId(familyId),
        handedness: "left"
      };
    }
    if (id.endsWith("-right")) {
      const familyId = id.slice(0, -"-right".length);
      return {
        familyId: normalizeKeyboardLayoutFamilyId(familyId),
        handedness: "right"
      };
    }
    return { familyId: DEFAULT_KEYBOARD_LAYOUT_FAMILY_ID, handedness: DEFAULT_KEYBOARD_HANDEDNESS };
  }
  function getInstalledKeyboardLayoutFamilyIds() {
    return BUILTIN_KEYBOARD_LAYOUT_FAMILIES_META.map((m) => m && m.id).filter(Boolean);
  }
  function nextUserCopyLayoutLabel(baseLabel, existingLayouts = []) {
    const base = String(baseLabel || "Layout").trim() || "Layout";
    const escaped = base.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
    const re = new RegExp(`^${escaped} Copy (\\d+)$`, "i");
    let maxN = 0;
    for (const l of existingLayouts || []) {
      const m = String(l?.label || "").trim().match(re);
      if (!m) continue;
      const n = Number(m[1]);
      if (Number.isFinite(n) && n > maxN) maxN = n;
    }
    return `${base} Copy ${maxN + 1}`;
  }
  function listLayoutPickerGroups(userLayouts = []) {
    const builtin = [];
    for (const fam of BUILTIN_KEYBOARD_LAYOUT_FAMILIES_META || []) {
      if (!fam?.id) continue;
      builtin.push({
        value: builtinFamilySelectValue(fam.id),
        label: String(fam.label || fam.id),
        builtIn: true
      });
    }
    const custom = [];
    for (const l of userLayouts || []) {
      if (!l?.id) continue;
      custom.push({
        value: `user:${l.id}`,
        label: String(l.label || l.id),
        builtIn: false
      });
    }
    custom.sort((a, b) => a.label.localeCompare(b.label));
    return { builtin, custom };
  }
  function upperLetter(s) {
    const ch = String(s || "");
    if (!ch) return "";
    return ch.length === 1 ? ch.toUpperCase() : ch;
  }
  function normalizeAssignmentLabels(a) {
    const keys = Array.isArray(a?.keys) ? a.keys : [];
    const first = keys[0] || "";
    const explicitDisplay = typeof a?.displayKey === "string" ? a.displayKey : "";
    const explicitKeyLabel = typeof a?.keyLabel === "string" ? a.keyLabel : "";
    if (explicitDisplay || explicitKeyLabel) {
      const dk = explicitDisplay || explicitKeyLabel;
      const kl = explicitKeyLabel || explicitDisplay;
      return { keyLabel: kl || dk || "", displayKey: dk || kl || "" };
    }
    if (typeof first === "string" && first.length === 1 && /[a-zA-Z]/.test(first)) {
      const up = upperLetter(first);
      return { keyLabel: up, displayKey: up };
    }
    return { keyLabel: String(first || ""), displayKey: String(first || "") };
  }
  function buildKeybindingsForLayout(layoutId) {
    const id = normalizeKeyboardLayoutId(layoutId);
    const layout = BUILTIN_KEYBOARD_LAYOUTS[id];
    const out = {};
    for (const [actionId, def] of Object.entries(KEYBINDING_ACTION_DEFS)) {
      if (isBuildExcludedKeyAction(actionId)) continue;
      const assign = layout?.assignments?.[actionId];
      if (!assign || !Array.isArray(assign.keys)) continue;
      const labels = normalizeAssignmentLabels(assign);
      out[actionId] = {
        keys: assign.keys.slice(),
        ...Array.isArray(assign.matchOn) ? { matchOn: assign.matchOn.slice() } : {},
        handler: def.handler,
        label: def.label,
        description: def.description,
        keyLabel: labels.keyLabel,
        keyboardClass: def.keyboardClass ?? null,
        row: def.row ?? null,
        displayKey: labels.displayKey
      };
    }
    return out;
  }
  function resolveKeybinding(actionId, keybindings) {
    const id = String(actionId || "");
    if (!id) return null;
    if (keybindings && keybindings[id]) return keybindings[id];
    return CATALOG_KEYBINDINGS[id] || null;
  }
  function buildSystemKeybindings(handedness = DEFAULT_KEYBOARD_HANDEDNESS) {
    const hand = normalizeKeyboardHandedness(handedness);
    const assignments = hand === "left" ? SYSTEM_LAYER_ASSIGNMENTS_LEFT : SYSTEM_LAYER_ASSIGNMENTS_RIGHT;
    const out = {};
    for (const actionId of SYSTEM_LAYER_ACTION_IDS) {
      const def = KEYBINDING_ACTION_DEFS[actionId];
      const assign = assignments[actionId];
      if (!def || !assign || !Array.isArray(assign.keys)) continue;
      const labels = normalizeAssignmentLabels(assign);
      out[actionId] = {
        keys: assign.keys.slice(),
        ...Array.isArray(assign.matchOn) ? { matchOn: assign.matchOn.slice() } : {},
        handler: def.handler,
        label: def.label,
        description: def.description,
        keyLabel: labels.keyLabel,
        keyboardClass: def.keyboardClass ?? null,
        row: def.row ?? null,
        displayKey: labels.displayKey,
        systemLayer: true
      };
    }
    return out;
  }
  function buildEffectiveKeybindings(layoutId, handedness = DEFAULT_KEYBOARD_HANDEDNESS) {
    return {
      ...buildKeybindingsForLayout(layoutId),
      ...buildSystemKeybindings(handedness)
    };
  }
  function pickAssignments(source, allowedIds) {
    const allowed = new Set(allowedIds);
    const out = {};
    for (const id of allowedIds) {
      if (isBuildExcludedKeyAction(id)) continue;
      if (source[id]) out[id] = source[id];
    }
    for (const [id, assignment] of Object.entries(source || {})) {
      if (isBuildExcludedKeyAction(id)) continue;
      if (allowed.has(id) && !out[id]) out[id] = assignment;
    }
    return Object.freeze(out);
  }
  function physicalSlotLabelFromBinding(binding) {
    const namedSlot = (raw) => {
      const token = String(raw || "").trim();
      if (!token) return "";
      if (token.length === 1) return /[a-z]/i.test(token) ? token.toUpperCase() : token;
      if (/^(Backspace|Escape)$/i.test(token)) {
        return token.charAt(0).toUpperCase() + token.slice(1).toLowerCase();
      }
      return "";
    };
    const s = String(binding?.displayKey || binding?.keyLabel || "").trim();
    const fromLabel = namedSlot(s);
    if (fromLabel) return fromLabel;
    if (s.includes("/")) {
      const first = s.split("/")[0];
      const fromComposite = namedSlot(first);
      if (fromComposite) return fromComposite;
    }
    const keys = Array.isArray(binding?.keys) ? binding.keys : [];
    for (const k of keys) {
      const fromKey = namedSlot(k);
      if (fromKey) return fromKey;
    }
    return "";
  }
  function letterFromAssignment(assignment) {
    if (!assignment) return "";
    const slot = physicalSlotLabelFromBinding(assignment);
    if (slot) return slot;
    if (typeof assignment.displayKey === "string" && assignment.displayKey) return assignment.displayKey;
    if (typeof assignment.keyLabel === "string" && assignment.keyLabel) return assignment.keyLabel;
    const keys = Array.isArray(assignment.keys) ? assignment.keys : [];
    for (const k of keys) {
      const s = String(k || "");
      if (!s || s === "Semicolon" || s === "Quote" || s === "Backquote") continue;
      if (s.length === 1) return s.toUpperCase();
      if (s === "Backspace" || s === "Escape") return s;
    }
    return "";
  }
  function projectKeyboardUiLayout(baseLayout, fullAssignments, allowedIds) {
    const allowed = new Set(allowedIds);
    return Object.freeze(
      (Array.isArray(baseLayout) ? baseLayout : []).map(
        (row) => Object.freeze(
          (Array.isArray(row) ? row : []).map((cell) => {
            if (!cell || cell.type !== "action" || !cell.id) return cell;
            if (isBuildExcludedKeyAction(cell.id) || !allowed.has(cell.id)) {
              if (cell.id === "DELETE" || cell.className && String(cell.className).includes("key-backspace")) {
                return Object.freeze({ type: "special", text: "Backspace", className: "key key-backspace" });
              }
              const text = letterFromAssignment(fullAssignments[cell.id]);
              if (!text) return Object.freeze({ type: "key", text: "" });
              if (text === "Backspace") {
                return Object.freeze({ type: "special", text: "Backspace", className: "key key-backspace" });
              }
              const glyph = text.length <= 3 ? text : text.slice(0, 1).toUpperCase();
              return Object.freeze({ type: "key", text: glyph.length === 1 ? glyph.toUpperCase() : glyph });
            }
            return cell;
          })
        )
      )
    );
  }
  function getKeyboardUiLayoutForLayout(layoutId, opts = {}) {
    const id = normalizeKeyboardLayoutId(layoutId);
    const base = BUILTIN_KEYBOARD_LAYOUTS[id]?.keyboardLayout || BUILTIN_KEYBOARD_LAYOUTS[DEFAULT_KEYBOARD_LAYOUT_ID].keyboardLayout;
    const include = !!(opts && opts.includeNumberRow);
    return include ? addNumberRowToKeyboardUiLayout(base) : base;
  }
  function addNumberRowToKeyboardUiLayout(layout) {
    const base = Array.isArray(layout) ? layout : [];
    try {
      const first = base[0];
      if (Array.isArray(first) && first.some((i) => i && i.type === "key" && String(i.text || "").trim() === "1")) {
        return base;
      }
    } catch {
    }
    const numberRow = Object.freeze([
      { type: "key", text: "1" },
      { type: "key", text: "2" },
      { type: "key", text: "3" },
      { type: "key", text: "4" },
      { type: "key", text: "5" },
      { type: "key", text: "6" },
      { type: "key", text: "7" },
      { type: "key", text: "8" },
      { type: "key", text: "9" },
      { type: "key", text: "0" }
    ]);
    return [numberRow, ...base];
  }
  var DEFAULT_KEYBOARD_LAYOUT_ID, DEFAULT_KEYBOARD_LAYOUT_FAMILY_ID, DEFAULT_KEYBOARD_HANDEDNESS, SOURCE_BUILD_ENABLE_MACRO_BUILDER, BUILD_ENABLE_MACRO_BUILDER, BUILD_EXCLUDED_KEY_ACTIONS, BUILD_EXCLUDED_KEY_ACTION_SET, BUILTIN_KEYBOARD_LAYOUT_META, BUILTIN_KEYBOARD_LAYOUT_FAMILIES_META, LEGACY_KEYBOARD_LAYOUT_FAMILY_VARIANTS, KNOWN_BUILTIN_LAYOUT_IDS, KEYBINDING_ACTION_DEFS, KEYBINDING_ACTION_CATEGORY_BY_ID, KEYBINDING_ACTION_CATEGORY_ORDER, CATALOG_KEYBINDINGS, ASSIGNMENTS_BROWSING_RIGHT, ASSIGNMENTS_BROWSING_LEFT, SYSTEM_LAYER_ACTION_IDS, SYSTEM_LAYER_ASSIGNMENTS_RIGHT, SYSTEM_LAYER_ASSIGNMENTS_LEFT, BASIC_NAVIGATION_ACTION_IDS, CLICK_HISTORY_ACTION_IDS, BASIC_NAVIGATION_UI_ACTION_IDS, CLICK_HISTORY_UI_ACTION_IDS, ASSIGNMENTS_BASIC_NAVIGATION_RIGHT, ASSIGNMENTS_BASIC_NAVIGATION_LEFT, ASSIGNMENTS_CLICK_HISTORY_RIGHT, ASSIGNMENTS_CLICK_HISTORY_LEFT, KEYBOARD_UI_LAYOUT_RIGHT, KEYBOARD_UI_LAYOUT_LEFT, BUILTIN_KEYBOARD_LAYOUTS;
  var init_keyboard_layouts = __esm({
    "src/config/keyboard-layouts.js"() {
      DEFAULT_KEYBOARD_LAYOUT_ID = /** @type {const} */
      "browsing-right";
      DEFAULT_KEYBOARD_LAYOUT_FAMILY_ID = /** @type {const} */
      "browsing";
      DEFAULT_KEYBOARD_HANDEDNESS = /** @type {const} */
      "right";
      SOURCE_BUILD_ENABLE_MACRO_BUILDER = false;
      BUILD_ENABLE_MACRO_BUILDER = typeof __KP_BUILD_ENABLE_MACRO_BUILDER__ !== "undefined" ? !!__KP_BUILD_ENABLE_MACRO_BUILDER__ : SOURCE_BUILD_ENABLE_MACRO_BUILDER;
      BUILD_EXCLUDED_KEY_ACTIONS = Object.freeze([
        "COLS_TOGGLE",
        // Type — "Type saved text into the focused field."
        "TYPE_CHARACTERS",
        // Data — "Read text or media under the cursor, or from a highlight."
        "GET_TEXT_AT_CURSOR",
        "GET_TEXT_RANGE",
        "GET_MEDIA_AT_CURSOR",
        // Script — "Run a user-authored JavaScript snippet against page state."
        "EXECUTE_JS",
        // Create built-in Macro Key — "Saved Macro Key instances (hotkey, burst, round-robin, and related kinds) ready to place."
        "SEND_HOTKEY",
        "SEND_BURST",
        "CYCLE_ROUND_ROBIN",
        "HOLD_CONTINUOUS",
        "CLICK_MOUSE_BUTTON",
        "REMAP_KEY",
        // Translate — "Translate highlighted or under-cursor text."
        "TRANSLATE",
        // AI — "Send selected text to AI with a prompt and result destination."
        "SEND_TEXT_TO_AI"
      ]);
      BUILD_EXCLUDED_KEY_ACTION_SET = new Set(BUILD_EXCLUDED_KEY_ACTIONS);
      BUILTIN_KEYBOARD_LAYOUT_META = Object.freeze([
        Object.freeze({
          id: (
            /** @type {const} */
            "browsing-right"
          ),
          label: "Browsing: right-handed",
          description: "Full browsing layout. Mouse: right hand. Shortcuts primarily on the left."
        }),
        Object.freeze({
          id: (
            /** @type {const} */
            "browsing-left"
          ),
          label: "Browsing: left-handed",
          description: "Full browsing layout. Mouse: left hand. Shortcuts primarily on the right."
        }),
        Object.freeze({
          id: (
            /** @type {const} */
            "basic-navigation-right"
          ),
          label: "Basic Navigation: right-handed",
          description: "Page scroll, click, tab switch, back/forward only."
        }),
        Object.freeze({
          id: (
            /** @type {const} */
            "basic-navigation-left"
          ),
          label: "Basic Navigation: left-handed",
          description: "Page scroll, click, tab switch, back/forward only."
        }),
        Object.freeze({
          id: (
            /** @type {const} */
            "click-history-right"
          ),
          label: "Navigation: right-handed",
          description: "Click element, go back, and go forward only."
        }),
        Object.freeze({
          id: (
            /** @type {const} */
            "click-history-left"
          ),
          label: "Navigation: left-handed",
          description: "Click element, go back, and go forward only."
        })
      ]);
      BUILTIN_KEYBOARD_LAYOUT_FAMILIES_META = Object.freeze([
        Object.freeze({
          id: (
            /** @type {const} */
            "browsing"
          ),
          label: "Browsing",
          builtIn: true,
          description: "Full browsing controls (scroll, tabs, click, history, tools).",
          variants: Object.freeze({
            right: (
              /** @type {const} */
              "browsing-right"
            ),
            left: (
              /** @type {const} */
              "browsing-left"
            )
          })
        }),
        Object.freeze({
          id: (
            /** @type {const} */
            "click-history"
          ),
          label: "Navigation",
          builtIn: true,
          description: "Click element, go back, and go forward.",
          variants: Object.freeze({
            right: (
              /** @type {const} */
              "click-history-right"
            ),
            left: (
              /** @type {const} */
              "click-history-left"
            )
          })
        })
      ]);
      LEGACY_KEYBOARD_LAYOUT_FAMILY_VARIANTS = Object.freeze({
        "basic-navigation": Object.freeze({
          right: (
            /** @type {const} */
            "basic-navigation-right"
          ),
          left: (
            /** @type {const} */
            "basic-navigation-left"
          )
        })
      });
      KNOWN_BUILTIN_LAYOUT_IDS = new Set(
        BUILTIN_KEYBOARD_LAYOUT_META.map((m) => m && m.id).filter(Boolean)
      );
      KEYBINDING_ACTION_DEFS = Object.freeze({
        ACTIVATE: Object.freeze({
          handler: "handleActivateKey",
          label: "Click Element",
          description: "Click the hovered element",
          details: "Activates the clickable under the cursor \u2014 the same as a left mouse click on that element. Works with links, buttons, and other interactive targets KeyPilot highlights.",
          keyboardClass: "key-activate",
          row: 2
        }),
        // Foreground new tab (switch to the new tab).
        ACTIVATE_NEW_TAB: Object.freeze({
          handler: "handleActivateNewTabKey",
          label: "Click New Tab",
          description: "Open link in a new foreground tab",
          details: "Opens the hovered link in a new tab and switches to it immediately. Use when you want to follow a link without leaving your place permanently, but still jump to the new page right away.",
          keyboardClass: "key-activate-new",
          row: 2
        }),
        // Background new tab (middle-click style; do not switch focus).
        ACTIVATE_NEW_TAB_BACKGROUND: Object.freeze({
          handler: "handleActivateNewTabBackgroundKey",
          label: "Click New Tab Background",
          description: "Open link in a new background tab",
          details: "Opens the hovered link in a new tab without switching focus \u2014 like a middle-click. Useful for queueing several links while you keep reading the current page.",
          keyboardClass: "key-activate-new-over",
          row: 2
        }),
        BACK: Object.freeze({
          handler: "handleBackKey",
          label: "Go Back",
          description: "Browser history back",
          details: "Navigates one step back in the current tab\u2019s history, equivalent to the browser Back button.",
          keyboardClass: "key-back",
          row: 2
        }),
        BACK2: Object.freeze({
          handler: "handleBackKey",
          label: "Go Back",
          description: "Browser history back",
          details: "Navigates one step back in the current tab\u2019s history, equivalent to the browser Back button. Duplicate id for layouts that expose a second Back binding.",
          keyboardClass: "key-back",
          row: 2
        }),
        FORWARD: Object.freeze({
          handler: "handleForwardKey",
          label: "Go Forward",
          description: "Browser history forward",
          details: "Navigates one step forward in the current tab\u2019s history, equivalent to the browser Forward button.",
          keyboardClass: "key-forward",
          row: 1
        }),
        DELETE: Object.freeze({
          handler: "handleDeleteKey",
          label: "Delete Mode",
          description: "Hide elements under the cursor",
          details: "Toggles Delete Mode: hover elements and remove (hide) them from the page so you can declutter layouts. Exit with Exit Focus or by toggling again.",
          keyboardClass: "key-delete",
          row: 2
        }),
        COLS_TOGGLE: Object.freeze({
          handler: "handleColsToggleKey",
          label: "Cols Toggle",
          description: "Multi-column layout under cursor",
          details: "Columnizes the element under the cursor into a multi-column layout so dense text or lists are easier to scan. Toggle again to restore the original layout.",
          keyboardClass: "key-cols",
          row: 3
        }),
        TAB_LEFT: Object.freeze({
          handler: "handleTabLeftKey",
          label: "Tab Left",
          description: "Switch to the previous tab",
          details: "Activates the tab to the left of the current one in the window\u2019s tab strip.",
          keyboardClass: "key-gray",
          row: 1
        }),
        TAB_RIGHT: Object.freeze({
          handler: "handleTabRightKey",
          label: "Tab Right",
          description: "Switch to the next tab",
          details: "Activates the tab to the right of the current one in the window\u2019s tab strip.",
          keyboardClass: "key-gray",
          row: 1
        }),
        ROOT: Object.freeze({
          handler: "handleRootKey",
          label: "Go to Site Root",
          description: "Navigate to the site origin",
          details: "Jumps to the site root (scheme + host) of the current page \u2014 useful for escaping deep paths without typing a URL.",
          keyboardClass: null,
          row: 2
        }),
        LAUNCHER: Object.freeze({
          handler: "handleLauncherKey",
          label: "Launcher",
          description: "Quick-access site launcher",
          details: "Opens the Launcher popover for jumping to favorite or configured sites without using the omnibox.",
          keyboardClass: "key-launcher-orange",
          row: 2
        }),
        TOP_SITES: Object.freeze({
          handler: "handleTopSitesKey",
          label: "Top Sites",
          description: "Toolbar, visits, and bookmarks",
          details: "Opens Top Sites: a quick list drawn from the toolbar, most-visited pages, and recent bookmarks so you can open a frequent destination in one step.",
          keyboardClass: "key-launcher-orange",
          row: 2
        }),
        CLOSE_TAB: Object.freeze({
          handler: "handleCloseTabKey",
          label: "Close Tab",
          description: "Close the current tab",
          details: "Closes the active tab. Behavior matches the browser\u2019s close-tab action for the current window.",
          keyboardClass: "key-close-tab",
          row: 3
        }),
        CANCEL: Object.freeze({
          handler: "cancelModes",
          label: "Exit Focus",
          description: "Leave modes and overlays",
          details: "Cancels the current KeyPilot mode or overlay (Delete Mode, Scroll Line, text focus helpers, and similar) and returns to normal browsing.",
          keyboardClass: null,
          row: null
        }),
        PAGE_UP_INSTANT: Object.freeze({
          handler: "handleInstantPageUp",
          label: "Page Up",
          description: "Jump one page up instantly",
          details: "Scrolls the current scroll target up by roughly one viewport without animation \u2014 faster than a smooth page-up when you need to move quickly.",
          keyboardClass: "key-scroll",
          row: 3
        }),
        PAGE_DOWN_INSTANT: Object.freeze({
          handler: "handleInstantPageDown",
          label: "Page Down",
          description: "Jump one page down instantly",
          details: "Scrolls the current scroll target down by roughly one viewport without animation \u2014 faster than a smooth page-down when you need to move quickly.",
          keyboardClass: "key-scroll",
          row: 3
        }),
        PAGE_TOP: Object.freeze({
          handler: "handlePageTop",
          label: "Scroll To Top",
          description: "Jump to top of scroll target",
          details: "Moves to the top of the current scroll target. Fade mode hides the jump; Scroll mode animates. Configure the motion style in Settings \u2192 Scrolling.",
          keyboardClass: "key-scroll",
          row: 3
        }),
        PAGE_BOTTOM: Object.freeze({
          handler: "handlePageBottom",
          label: "Scroll To Bottom",
          description: "Jump to bottom of scroll target",
          details: "Moves to the bottom of the current scroll target. Fade mode hides the jump; Scroll mode animates. Configure the motion style in Settings \u2192 Scrolling.",
          keyboardClass: "key-scroll",
          row: 3
        }),
        SCROLL_LINE: Object.freeze({
          handler: "handleScrollLineKey",
          label: "Scroll Line",
          description: "Origin-based continuous scroll",
          details: "Scrolls from a fixed origin: move the mouse away from the on-screen dot to scroll faster in that direction. Optionally enable middle-click on empty page area under Settings \u2192 Scrolling.",
          keyboardClass: "key-scroll",
          row: 3,
          mode: "scroll_line",
          cancelOnPointerDown: true,
          pointerBinding: Object.freeze({
            button: "middle",
            yieldToClickables: true,
            yieldToTextEntry: true,
            yieldToModes: Object.freeze(["text_focus", "popover", "omnibox"]),
            enabledSetting: "scroll.middleClickScrollLine"
          })
        }),
        NEW_TAB: Object.freeze({
          handler: "handleNewTabKey",
          label: "New Tab",
          description: "Open a blank new tab",
          details: "Opens a new empty tab in the current window, same as the browser\u2019s New Tab command.",
          keyboardClass: "key-gray",
          row: 1
        }),
        OPEN_POPOVER: Object.freeze({
          handler: "handleOpenPopover",
          label: "Open Popover",
          description: "Open link in a popup window",
          details: "Opens the hovered link in a KeyPilot popup window so you can peek or work in a separate chrome without a full new tab.",
          keyboardClass: "key-open-popover",
          row: 2
        }),
        PREVIEW_LINK_POPOVER: Object.freeze({
          handler: "handlePreviewLinkPopover",
          label: "Preview Link",
          description: "Preview link in a popup",
          details: "Opens Link Preview for the hovered URL in a popup window \u2014 skim the destination without committing a full navigation in the main tab.",
          keyboardClass: "key-preview-popover",
          row: 2
        }),
        POI_WEBSITE: Object.freeze({
          handler: "handlePoiWebsiteKey",
          label: "POI Website",
          description: "Open map place website",
          details: "When a map place (POI) is under the cursor, opens that place\u2019s website in Link Preview so you can visit the business or location page without leaving the map.",
          keyboardClass: "key-preview-popover",
          row: null
        }),
        POI_ADDRESS: Object.freeze({
          handler: "handlePoiAddressKey",
          label: "POI Address",
          description: "Copy map place address",
          details: "When a map place (POI) is under the cursor, copies its street address to the clipboard for pasting into directions, notes, or forms.",
          keyboardClass: null,
          row: null
        }),
        OPEN_SETTINGS_POPOVER: Object.freeze({
          handler: "handleToggleSettingsPopover",
          label: "Settings",
          description: "Open KeyPilot Settings",
          details: "Opens or closes the KeyPilot Settings popover for themes, scrolling, click mode, layouts, and other preferences.",
          keyboardClass: "key-settings-dark",
          row: null
        }),
        OMNIBOX: Object.freeze({
          handler: "handleOpenOmnibox",
          label: "Omnibox",
          description: "Address bar overlay",
          details: "Opens KeyPilot\u2019s omnibox overlay so you can type a URL or search without clicking the browser address bar.",
          keyboardClass: "key-orange",
          row: 2
        }),
        TAB_HISTORY: Object.freeze({
          handler: "handleToggleTabHistoryPopover",
          label: "Tab History",
          description: "Browse this tab\u2019s history",
          details: "Opens Tab History for the current tab so you can jump to a previously visited page in this tab\u2019s session without using the browser\u2019s native history UI.",
          keyboardClass: "key-gray",
          row: 2
        }),
        TOGGLE_KEYBOARD_HELP: Object.freeze({
          handler: "handleToggleKeyboardHelp",
          label: "KB Reference",
          description: "Show or hide the keyboard map",
          details: "Toggles the floating Keyboard Reference window that shows your current layout\u2019s keycaps and bindings.",
          keyboardClass: "key-purple",
          row: 2
        }),
        // Text select: default character-level (H on right-handed layout).
        HIGHLIGHT: Object.freeze({
          handler: "handleHighlightKey",
          label: "Text Select",
          description: "Select text and copy rich text",
          details: "Enters character-level text selection under the cursor. By default, the selection is copied as rich text so formatting is preserved when you paste.",
          keyboardClass: "key-highlight",
          row: 2
        }),
        // Rectangle region select (Y on right-handed; R free on left-handed).
        RECTANGLE_HIGHLIGHT: Object.freeze({
          handler: "handleRectangleHighlightKey",
          label: "Element Select",
          description: "Rectangle or cumulative element pick",
          details: "Selects HTML elements that intersect a dragged rectangle, or pick elements cumulatively. Useful for grabbing structure (not just plain text) from a page region.",
          keyboardClass: "key-rect-highlight",
          row: 1
        }),
        // Copy image under cursor (I on right-handed; E on left-handed — I is OPEN_POPOVER there).
        COPY_HOVERED_IMAGE: Object.freeze({
          handler: "handleCopyHoveredImageKey",
          label: "Copy Image",
          description: "Copy hovered image",
          details: "Copies the image under the cursor to the clipboard, Media Library, or both \u2014 configure the destination on the action. Prefer this when you want the image bytes or a saved library entry, not just a URL.",
          // Default key face (no tinted key-gray / family fill).
          keyboardClass: null,
          row: 1
        }),
        // Copy hyperlink under cursor (U on right-handed; no default on left — U is FORWARD there).
        COPY_HOVERED_URL: Object.freeze({
          handler: "handleCopyHoveredUrlKey",
          label: "Copy URL",
          description: "Copy hovered link URL",
          details: "Copies the URL under the cursor to the clipboard, Media Library, or both. Use this when you need the href itself rather than fetching or opening the resource.",
          keyboardClass: null,
          row: 1
        }),
        // Copy video under cursor — Actions Library only (no built-in layout key).
        COPY_HOVERED_VIDEO: Object.freeze({
          handler: "handleCopyHoveredVideoKey",
          label: "Copy Video",
          description: "Copy hovered video",
          details: "Copies the video under the cursor (file bytes to Media Library when fetchable, or the video URL to the clipboard). No default layout key \u2014 bind it in Layout Editor if you need it.",
          keyboardClass: null,
          row: null
        }),
        // Font under cursor — Actions Library only (no built-in layout key).
        FONT_INFO: Object.freeze({
          handler: "handleFontInfoKey",
          label: "Font Info",
          description: "Inspect font under the cursor",
          details: "Shows a popover with the font name, size, family, file type, and resource URL for the styled text under the cursor, and outlines that text run. No default layout key \u2014 bind it in Layout Editor if you need it.",
          keyboardClass: null,
          row: null
        }),
        // Page-wide Image / Video / Text gallery (O on right-handed; O is TAB_RIGHT on left-handed).
        PAGE_MEDIA: Object.freeze({
          handler: "handlePageMediaKey",
          label: "Page Media",
          description: "Browse media found on this page",
          details: "Opens a gallery of images, videos, documents, fonts, and URLs discovered on the current page so you can review or collect them without hunting through the DOM.",
          keyboardClass: null,
          row: 1
        }),
        // Media Library entry point (M on right-handed only — M is PAGE_DOWN_INSTANT on left-handed,
        // so this doesn't get a default binding there yet).
        OPEN_MEDIA_LIBRARY: Object.freeze({
          handler: "handleOpenMediaLibraryKey",
          label: "Media Library",
          description: "Open saved Media Library",
          details: "Opens the Media Library where items you previously copied or saved (images, videos, URLs, and related assets) are kept for reuse.",
          keyboardClass: null,
          row: 1
        }),
        // Clipboard commands (Functions palette — Clipboard category).
        CLIPBOARD_COPY: Object.freeze({
          handler: "handleClipboardCopyKey",
          label: "Copy",
          description: "Copy selection to clipboard",
          details: "Copies the current text selection to the system clipboard. Prefer this over OS shortcuts when you want Copy available as a KeyPilot layout binding.",
          keyboardClass: null,
          row: null
        }),
        CLIPBOARD_CUT: Object.freeze({
          handler: "handleClipboardCutKey",
          label: "Cut",
          description: "Cut selection to clipboard",
          details: "Cuts the current text selection to the system clipboard from the focused field or editable region.",
          keyboardClass: null,
          row: null
        }),
        CLIPBOARD_PASTE: Object.freeze({
          handler: "handleClipboardPasteKey",
          label: "Paste",
          description: "Paste into the focused field",
          details: "Pastes clipboard text into the focused text field or editable element. Bind with a modifier chord if you need it while typing.",
          keyboardClass: null,
          row: null
        }),
        CLIPBOARD_SELECT_ALL: Object.freeze({
          handler: "handleClipboardSelectAllKey",
          label: "Select All",
          description: "Select all in field or page",
          details: "Selects all text in the focused field, or the page content when nothing editable is focused \u2014 same idea as the usual Select All shortcut.",
          keyboardClass: null,
          row: null
        }),
        SELECT_WORD: Object.freeze({
          handler: "handleSelectWordKey",
          label: "Select Word",
          description: "Select the word under the cursor",
          details: "Selects the word under the KeyPilot cursor. Press again over the same word to deselect it. Exclusive vs cumulative is a popover setting (shared, not per-key). Copy reads this selection.",
          keyboardClass: null,
          row: null
        }),
        SELECT_SENTENCE: Object.freeze({
          handler: "handleSelectSentenceKey",
          label: "Select Sentence",
          description: "Select the sentence under the cursor",
          details: "Selects the sentence under the KeyPilot cursor. Press again over the same sentence to deselect it. Exclusive vs cumulative is a popover setting (shared, not per-key).",
          keyboardClass: null,
          row: null
        }),
        SELECT_PARAGRAPH: Object.freeze({
          handler: "handleSelectParagraphKey",
          label: "Select Paragraph",
          description: "Select the paragraph under the cursor",
          details: "Selects the paragraph (or nearest block) under the KeyPilot cursor. Press again over the same block to deselect it. Exclusive vs cumulative is a popover setting (shared, not per-key).",
          keyboardClass: null,
          row: null
        }),
        SELECT_IMAGE: Object.freeze({
          handler: "handleSelectImageKey",
          label: "Select Image",
          description: "Select the image under the cursor",
          details: "Selects the image under the KeyPilot cursor. Press again over the same image to deselect it. Exclusive vs cumulative is a popover setting (shared, not per-key). Copy can copy selected images.",
          keyboardClass: null,
          row: null
        }),
        // AI (Functions palette — AI category).
        SEND_TEXT_TO_AI: Object.freeze({
          handler: "handleSendTextToAiKey",
          label: "Send Text To AI",
          description: "Run AI on selected text",
          details: "Sends the selected text to AI with a configurable instruction, then routes the result to the clipboard and/or a popover. Configure the prompt and destination on the action instance.",
          keyboardClass: "key-purple",
          row: null
        })
      });
      KEYBINDING_ACTION_CATEGORY_BY_ID = Object.freeze({
        // Navigation — click / link preview / history
        ACTIVATE: "Navigation",
        ACTIVATE_NEW_TAB: "Navigation",
        ACTIVATE_NEW_TAB_BACKGROUND: "Navigation",
        PREVIEW_LINK_POPOVER: "Navigation",
        POI_WEBSITE: "Maps",
        POI_ADDRESS: "Maps",
        OPEN_POPOVER: "Navigation",
        FORWARD: "Navigation",
        BACK: "Navigation",
        BACK2: "Navigation",
        ROOT: "Navigation",
        // Tab Control
        CLOSE_TAB: "Tab Control",
        TAB_LEFT: "Tab Control",
        TAB_RIGHT: "Tab Control",
        NEW_TAB: "Tab Control",
        TAB_HISTORY: "Tab Control",
        PAGE_UP_INSTANT: "Scroll",
        PAGE_DOWN_INSTANT: "Scroll",
        PAGE_TOP: "Scroll",
        PAGE_BOTTOM: "Scroll",
        SCROLL_LINE: "Scroll",
        HIGHLIGHT: "Get Page Data",
        RECTANGLE_HIGHLIGHT: "Get Page Data",
        COPY_HOVERED_IMAGE: "Get Page Data",
        COPY_HOVERED_URL: "Get Page Data",
        COPY_HOVERED_VIDEO: "Get Page Data",
        FONT_INFO: "Get Page Data",
        PAGE_MEDIA: "Get Page Data",
        DELETE: "Select",
        COLS_TOGGLE: "Select",
        OPEN_MEDIA_LIBRARY: "Media Library",
        CLIPBOARD_COPY: "Clipboard",
        CLIPBOARD_CUT: "Clipboard",
        CLIPBOARD_PASTE: "Clipboard",
        CLIPBOARD_SELECT_ALL: "Clipboard",
        SELECT_WORD: "Clipboard",
        SELECT_SENTENCE: "Clipboard",
        SELECT_PARAGRAPH: "Clipboard",
        SELECT_IMAGE: "Clipboard",
        SEND_TEXT_TO_AI: "AI",
        LAUNCHER: "Begin URL",
        TOP_SITES: "Begin URL",
        OMNIBOX: "Begin URL",
        TOGGLE_KEYBOARD_HELP: "KeyPilot",
        OPEN_SETTINGS_POPOVER: "KeyPilot",
        CANCEL: "System"
      });
      KEYBINDING_ACTION_CATEGORY_ORDER = Object.freeze([
        "Navigation",
        "Tab Control",
        "Begin URL",
        "Get Page Data",
        "Maps",
        "Scroll",
        "Select",
        "Media Library",
        "Clipboard",
        "AI",
        "KeyPilot",
        "Tools",
        "System",
        "Other"
      ]);
      CATALOG_KEYBINDINGS = (() => {
        const out = {};
        for (const [actionId, def] of Object.entries(KEYBINDING_ACTION_DEFS)) {
          if (isBuildExcludedKeyAction(actionId)) continue;
          out[actionId] = Object.freeze({
            keys: Object.freeze([]),
            handler: def.handler,
            label: def.label,
            description: def.description,
            keyboardClass: def.keyboardClass ?? null,
            row: def.row ?? null,
            displayKey: "",
            keyLabel: ""
          });
        }
        return Object.freeze(out);
      })();
      ASSIGNMENTS_BROWSING_RIGHT = Object.freeze({
        TAB_LEFT: Object.freeze({ keys: ["q", "Q"] }),
        TAB_RIGHT: Object.freeze({ keys: ["w", "W"] }),
        OPEN_POPOVER: Object.freeze({ keys: ["p", "P"] }),
        PREVIEW_LINK_POPOVER: Object.freeze({ keys: ["e", "E"] }),
        FORWARD: Object.freeze({ keys: ["r", "R"] }),
        NEW_TAB: Object.freeze({ keys: ["t", "T"] }),
        CLOSE_TAB: Object.freeze({ keys: ["a", "A"] }),
        ROOT: Object.freeze({ keys: ["s", "S", "1", "!"], displayKey: "S", keyLabel: "S" }),
        BACK: Object.freeze({ keys: ["d", "D"] }),
        ACTIVATE: Object.freeze({ keys: ["f", "F"] }),
        ACTIVATE_NEW_TAB_BACKGROUND: Object.freeze({ keys: ["g", "G"] }),
        HIGHLIGHT: Object.freeze({ keys: ["h", "H"] }),
        TAB_HISTORY: Object.freeze({ keys: ["j", "J"] }),
        OMNIBOX: Object.freeze({ keys: ["l", "L"] }),
        TOP_SITES: Object.freeze({ keys: [";", ":", "Semicolon", "`", "~", "Backquote"], matchOn: ["key", "code"], displayKey: ";", keyLabel: ";" }),
        PAGE_TOP: Object.freeze({ keys: ["z", "Z"] }),
        PAGE_BOTTOM: Object.freeze({ keys: ["x", "X"] }),
        PAGE_UP_INSTANT: Object.freeze({ keys: ["c", "C"] }),
        PAGE_DOWN_INSTANT: Object.freeze({ keys: ["v", "V"] }),
        ACTIVATE_NEW_TAB: Object.freeze({ keys: ["b", "B"] }),
        SCROLL_LINE: Object.freeze({ keys: ["n", "N"] }),
        RECTANGLE_HIGHLIGHT: Object.freeze({ keys: ["y", "Y"] }),
        COPY_HOVERED_IMAGE: Object.freeze({ keys: ["i", "I"] }),
        COPY_HOVERED_URL: Object.freeze({ keys: ["u", "U"] }),
        PAGE_MEDIA: Object.freeze({ keys: ["o", "O"] }),
        // M is otherwise unused on the right-handed layout (it's PAGE_DOWN_INSTANT on left-handed).
        OPEN_MEDIA_LIBRARY: Object.freeze({ keys: ["m", "M"] }),
        DELETE: Object.freeze({ keys: ["Backspace"], displayKey: "Backspace", keyLabel: "Backspace" })
        // COLS_TOGGLE omitted — see BUILD_EXCLUDED_KEY_ACTIONS
      });
      ASSIGNMENTS_BROWSING_LEFT = Object.freeze({
        // Top row cluster: Q W E R T  ->  P O I U Y (mirrored)
        TAB_LEFT: Object.freeze({ keys: ["p", "P"] }),
        TAB_RIGHT: Object.freeze({ keys: ["o", "O"] }),
        OPEN_POPOVER: Object.freeze({ keys: ["i", "I"] }),
        PREVIEW_LINK_POPOVER: Object.freeze({ keys: ["w", "W"] }),
        FORWARD: Object.freeze({ keys: ["u", "U"] }),
        NEW_TAB: Object.freeze({ keys: ["y", "Y"] }),
        SCROLL_LINE: Object.freeze({ keys: ["t", "T"] }),
        // Home row cluster: A S D F G  ->  ; L K J H (mirrored-ish around center)
        CLOSE_TAB: Object.freeze({ keys: [";", ":"], displayKey: ";", keyLabel: ";" }),
        ROOT: Object.freeze({ keys: ["l", "L", "1", "!"], displayKey: "L", keyLabel: "L" }),
        BACK: Object.freeze({ keys: ["k", "K"] }),
        ACTIVATE: Object.freeze({ keys: ["j", "J"] }),
        ACTIVATE_NEW_TAB_BACKGROUND: Object.freeze({ keys: ["h", "H"] }),
        // H is background-tab open on left; G/R free for selection.
        HIGHLIGHT: Object.freeze({ keys: ["g", "G"] }),
        RECTANGLE_HIGHLIGHT: Object.freeze({ keys: ["r", "R"] }),
        // Utility actions on the left avoid colliding with J/K/L cluster.
        // (KB Reference / Settings / Esc live in the system layer, not layout assignments.)
        TAB_HISTORY: Object.freeze({ keys: ["f", "F"] }),
        OMNIBOX: Object.freeze({ keys: ["s", "S"] }),
        TOP_SITES: Object.freeze({ keys: ["a", "A", "`", "~", "Backquote"], matchOn: ["key", "code"], displayKey: "A", keyLabel: "A" }),
        // Bottom row cluster: Z X C V B  ->  / . , M N (mirrored)
        PAGE_TOP: Object.freeze({ keys: ["/", "?"], displayKey: "/", keyLabel: "/" }),
        PAGE_BOTTOM: Object.freeze({ keys: ["b", "B"] }),
        PAGE_UP_INSTANT: Object.freeze({ keys: [",", "<"], displayKey: ",", keyLabel: "," }),
        PAGE_DOWN_INSTANT: Object.freeze({ keys: ["m", "M"] }),
        ACTIVATE_NEW_TAB: Object.freeze({ keys: ["n", "N"] }),
        // I is OPEN_POPOVER on left-handed; E is free.
        COPY_HOVERED_IMAGE: Object.freeze({ keys: ["e", "E"] }),
        // COLS_TOGGLE omitted — see BUILD_EXCLUDED_KEY_ACTIONS
        DELETE: Object.freeze({ keys: ["Backspace"], displayKey: "Backspace", keyLabel: "Backspace" })
      });
      SYSTEM_LAYER_ACTION_IDS = Object.freeze([
        "CANCEL",
        "TOGGLE_KEYBOARD_HELP",
        "OPEN_SETTINGS_POPOVER"
      ]);
      SYSTEM_LAYER_ASSIGNMENTS_RIGHT = Object.freeze({
        CANCEL: Object.freeze({ keys: ["Escape"], displayKey: "Esc", keyLabel: "Esc" }),
        TOGGLE_KEYBOARD_HELP: Object.freeze({ keys: ["k", "K"] }),
        OPEN_SETTINGS_POPOVER: Object.freeze({ keys: ["'", "Quote"], matchOn: ["key", "code"], displayKey: "'" })
      });
      SYSTEM_LAYER_ASSIGNMENTS_LEFT = Object.freeze({
        CANCEL: Object.freeze({ keys: ["Escape"], displayKey: "Esc", keyLabel: "Esc" }),
        TOGGLE_KEYBOARD_HELP: Object.freeze({ keys: ["d", "D"] }),
        OPEN_SETTINGS_POPOVER: Object.freeze({ keys: ["'", "Quote"], matchOn: ["key", "code"], displayKey: "'" })
      });
      BASIC_NAVIGATION_ACTION_IDS = Object.freeze([
        "ACTIVATE",
        "TAB_LEFT",
        "TAB_RIGHT",
        "FORWARD",
        "BACK",
        "ROOT",
        "PAGE_TOP",
        "PAGE_BOTTOM",
        "PAGE_UP_INSTANT",
        "PAGE_DOWN_INSTANT"
      ]);
      CLICK_HISTORY_ACTION_IDS = Object.freeze([
        "ACTIVATE",
        "BACK",
        "ROOT",
        "FORWARD"
      ]);
      BASIC_NAVIGATION_UI_ACTION_IDS = Object.freeze([
        ...BASIC_NAVIGATION_ACTION_IDS,
        ...SYSTEM_LAYER_ACTION_IDS
      ]);
      CLICK_HISTORY_UI_ACTION_IDS = Object.freeze([
        ...CLICK_HISTORY_ACTION_IDS,
        ...SYSTEM_LAYER_ACTION_IDS
      ]);
      ASSIGNMENTS_BASIC_NAVIGATION_RIGHT = pickAssignments(ASSIGNMENTS_BROWSING_RIGHT, BASIC_NAVIGATION_ACTION_IDS);
      ASSIGNMENTS_BASIC_NAVIGATION_LEFT = pickAssignments(ASSIGNMENTS_BROWSING_LEFT, BASIC_NAVIGATION_ACTION_IDS);
      ASSIGNMENTS_CLICK_HISTORY_RIGHT = pickAssignments(ASSIGNMENTS_BROWSING_RIGHT, CLICK_HISTORY_ACTION_IDS);
      ASSIGNMENTS_CLICK_HISTORY_LEFT = pickAssignments(ASSIGNMENTS_BROWSING_LEFT, CLICK_HISTORY_ACTION_IDS);
      KEYBOARD_UI_LAYOUT_RIGHT = Object.freeze([
        [
          { type: "special", text: "Tab", className: "key key-tab" },
          { type: "action", id: "TAB_LEFT", fallbackText: "Tab Left" },
          { type: "action", id: "TAB_RIGHT", fallbackText: "Tab Right" },
          { type: "action", id: "PREVIEW_LINK_POPOVER", fallbackText: "Preview Link" },
          { type: "action", id: "FORWARD", fallbackText: "Go Forward" },
          { type: "action", id: "NEW_TAB", fallbackText: "New Tab" },
          { type: "action", id: "RECTANGLE_HIGHLIGHT", fallbackText: "Rectangle Select" },
          { type: "action", id: "COPY_HOVERED_URL", fallbackText: "Copy URL" },
          { type: "action", id: "COPY_HOVERED_IMAGE", fallbackText: "Copy Image" },
          { type: "action", id: "PAGE_MEDIA", fallbackText: "Page Media" },
          { type: "action", id: "OPEN_POPOVER", fallbackText: "Open Popover" },
          { type: "key", text: "[" },
          { type: "key", text: "]" },
          { type: "action", id: "DELETE", fallbackText: "Delete Mode", className: "key key-backspace" }
        ],
        [
          { type: "special", text: "Caps", className: "key key-caps" },
          { type: "action", id: "CLOSE_TAB", fallbackText: "Close Tab" },
          { type: "action", id: "ROOT", fallbackText: "Go to Site Root" },
          { type: "action", id: "BACK", fallbackText: "Go Back" },
          { type: "action", id: "ACTIVATE", fallbackText: "Click Element" },
          { type: "action", id: "ACTIVATE_NEW_TAB_BACKGROUND", fallbackText: "Click New Tab Background" },
          { type: "action", id: "HIGHLIGHT", fallbackText: "Text Select" },
          { type: "action", id: "TAB_HISTORY", fallbackText: "History" },
          { type: "action", id: "TOGGLE_KEYBOARD_HELP", fallbackText: "KB Reference" },
          { type: "action", id: "OMNIBOX", fallbackText: "Omnibox" },
          { type: "action", id: "TOP_SITES", fallbackText: "Top Sites" },
          { type: "action", id: "OPEN_SETTINGS_POPOVER", fallbackText: "Settings" },
          { type: "special", text: "Enter", className: "key key-enter" }
        ],
        [
          { type: "special", text: "Shift", className: "key key-shift" },
          { type: "action", id: "PAGE_TOP", fallbackText: "Scroll To Top" },
          { type: "action", id: "PAGE_BOTTOM", fallbackText: "Scroll To Bottom" },
          { type: "action", id: "PAGE_UP_INSTANT", fallbackText: "Page Up" },
          { type: "action", id: "PAGE_DOWN_INSTANT", fallbackText: "Page Down" },
          { type: "action", id: "ACTIVATE_NEW_TAB", fallbackText: "Click New Tab" },
          { type: "action", id: "SCROLL_LINE", fallbackText: "Scroll Line" },
          { type: "action", id: "OPEN_MEDIA_LIBRARY", fallbackText: "Media Library" },
          { type: "key", text: "," },
          { type: "key", text: "." },
          { type: "key", text: "/" },
          { type: "special", text: "Shift", className: "key key-shift" }
        ]
      ]);
      KEYBOARD_UI_LAYOUT_LEFT = Object.freeze([
        [
          { type: "special", text: "Tab", className: "key key-tab" },
          { type: "key", text: "Q" },
          { type: "action", id: "PREVIEW_LINK_POPOVER", fallbackText: "Preview Link" },
          // W
          { type: "action", id: "COPY_HOVERED_IMAGE", fallbackText: "Copy Image" },
          // E
          { type: "action", id: "RECTANGLE_HIGHLIGHT", fallbackText: "Rectangle Select" },
          // R
          { type: "action", id: "SCROLL_LINE", fallbackText: "Scroll Line" },
          // T
          { type: "action", id: "NEW_TAB", fallbackText: "New Tab" },
          // Y
          { type: "action", id: "FORWARD", fallbackText: "Go Forward" },
          // U
          { type: "action", id: "OPEN_POPOVER", fallbackText: "Open Popover" },
          // I
          { type: "action", id: "TAB_RIGHT", fallbackText: "Tab Right" },
          // O
          { type: "action", id: "TAB_LEFT", fallbackText: "Tab Left" },
          // P
          { type: "key", text: "[" },
          { type: "key", text: "]" },
          { type: "action", id: "DELETE", fallbackText: "Delete Mode", className: "key key-backspace" }
        ],
        [
          { type: "special", text: "Caps", className: "key key-caps" },
          { type: "action", id: "TOP_SITES", fallbackText: "Top Sites" },
          // Utility keys on the left (to avoid colliding with right-hand cluster)
          { type: "action", id: "OMNIBOX", fallbackText: "Omnibox" },
          // S
          { type: "action", id: "TOGGLE_KEYBOARD_HELP", fallbackText: "KB Reference" },
          // D
          { type: "action", id: "TAB_HISTORY", fallbackText: "History" },
          // F
          { type: "action", id: "HIGHLIGHT", fallbackText: "Text Select" },
          // G
          { type: "action", id: "ACTIVATE_NEW_TAB_BACKGROUND", fallbackText: "Click New Tab Background" },
          // H
          { type: "action", id: "ACTIVATE", fallbackText: "Click Element" },
          // J
          { type: "action", id: "BACK", fallbackText: "Go Back" },
          // K
          { type: "action", id: "ROOT", fallbackText: "Go to Site Root" },
          // L
          { type: "action", id: "CLOSE_TAB", fallbackText: "Close Tab" },
          // ;
          { type: "action", id: "OPEN_SETTINGS_POPOVER", fallbackText: "Settings" },
          // '
          { type: "special", text: "Enter", className: "key key-enter" }
        ],
        [
          { type: "special", text: "Shift", className: "key key-shift" },
          { type: "key", text: "Z" },
          { type: "key", text: "X" },
          { type: "key", text: "C" },
          { type: "key", text: "V" },
          { type: "action", id: "PAGE_BOTTOM", fallbackText: "Scroll To Bottom" },
          // B
          { type: "action", id: "ACTIVATE_NEW_TAB", fallbackText: "Click New Tab" },
          // N
          { type: "action", id: "PAGE_DOWN_INSTANT", fallbackText: "Page Down" },
          // M
          { type: "action", id: "PAGE_UP_INSTANT", fallbackText: "Page Up" },
          // ,
          { type: "key", text: "." },
          { type: "action", id: "PAGE_TOP", fallbackText: "Scroll To Top" },
          // /
          { type: "special", text: "Shift", className: "key key-shift" }
        ]
      ]);
      BUILTIN_KEYBOARD_LAYOUTS = Object.freeze({
        "browsing-right": Object.freeze({
          id: "browsing-right",
          label: "Browsing: right-handed",
          description: "Full browsing layout. Mouse: right hand. Shortcuts primarily on the left.",
          assignments: ASSIGNMENTS_BROWSING_RIGHT,
          keyboardLayout: KEYBOARD_UI_LAYOUT_RIGHT
        }),
        "browsing-left": Object.freeze({
          id: "browsing-left",
          label: "Browsing: left-handed",
          description: "Full browsing layout. Mouse: left hand. Shortcuts primarily on the right.",
          assignments: ASSIGNMENTS_BROWSING_LEFT,
          keyboardLayout: KEYBOARD_UI_LAYOUT_LEFT
        }),
        "basic-navigation-right": Object.freeze({
          id: "basic-navigation-right",
          label: "Basic Navigation: right-handed",
          description: "Page scroll, click, tab switch, back/forward only.",
          assignments: ASSIGNMENTS_BASIC_NAVIGATION_RIGHT,
          keyboardLayout: projectKeyboardUiLayout(
            KEYBOARD_UI_LAYOUT_RIGHT,
            { ...ASSIGNMENTS_BROWSING_RIGHT, ...SYSTEM_LAYER_ASSIGNMENTS_RIGHT },
            BASIC_NAVIGATION_UI_ACTION_IDS
          )
        }),
        "basic-navigation-left": Object.freeze({
          id: "basic-navigation-left",
          label: "Basic Navigation: left-handed",
          description: "Page scroll, click, tab switch, back/forward only.",
          assignments: ASSIGNMENTS_BASIC_NAVIGATION_LEFT,
          keyboardLayout: projectKeyboardUiLayout(
            KEYBOARD_UI_LAYOUT_LEFT,
            { ...ASSIGNMENTS_BROWSING_LEFT, ...SYSTEM_LAYER_ASSIGNMENTS_LEFT },
            BASIC_NAVIGATION_UI_ACTION_IDS
          )
        }),
        "click-history-right": Object.freeze({
          id: "click-history-right",
          label: "Navigation: right-handed",
          description: "Click element, go back, and go forward only.",
          assignments: ASSIGNMENTS_CLICK_HISTORY_RIGHT,
          keyboardLayout: projectKeyboardUiLayout(
            KEYBOARD_UI_LAYOUT_RIGHT,
            { ...ASSIGNMENTS_BROWSING_RIGHT, ...SYSTEM_LAYER_ASSIGNMENTS_RIGHT },
            CLICK_HISTORY_UI_ACTION_IDS
          )
        }),
        "click-history-left": Object.freeze({
          id: "click-history-left",
          label: "Navigation: left-handed",
          description: "Click element, go back, and go forward only.",
          assignments: ASSIGNMENTS_CLICK_HISTORY_LEFT,
          keyboardLayout: projectKeyboardUiLayout(
            KEYBOARD_UI_LAYOUT_LEFT,
            { ...ASSIGNMENTS_BROWSING_LEFT, ...SYSTEM_LAYER_ASSIGNMENTS_LEFT },
            CLICK_HISTORY_UI_ACTION_IDS
          )
        })
      });
    }
  });

  // src/config/constants.js
  function modeShowsClickableHover(mode) {
    return mode === MODES.NONE || mode === MODES.TEXT_FOCUS || mode === MODES.POPOVER || mode === MODES.OMNIBOX;
  }
  var KEYBINDINGS, SELECTORS, CLICKABLE_CATEGORY, CSS_CLASSES, TEXT_FOCUS_HINT_MIN_HEIGHT_PX, ELEMENT_IDS, Z_INDEX, SCROLL, MODES, INSPECTOR_KIND, ELEMENT_SELECT_AGGREGATES, ELEMENT_SELECT_LANDMARKS, ELEMENT_SELECT_ATOMS, ELEMENT_SELECT_FRAGMENTS, ELEMENT_SELECT_TAGS, CURSOR_MODE, KP_UI_FONT, COLORS, RECTANGLE_SELECTION, EDGE_ONLY_SELECTION, FEATURE_FLAGS;
  var init_constants = __esm({
    "src/config/constants.js"() {
      init_keyboard_layouts();
      KEYBINDINGS = buildEffectiveKeybindings(DEFAULT_KEYBOARD_LAYOUT_ID, DEFAULT_KEYBOARD_HANDEDNESS);
      SELECTORS = {
        CLICKABLE: "a[href], button, input, select, textarea",
        // Prefer IDL-backed checks via isTypingContext() when possible. These selectors
        // are best-effort for matches()/querySelector (note: bare <input> has no type attr).
        TEXT_INPUTS: 'input:not([type]), input[type="text"], input[type="search"], input[type="url"], input[type="email"], input[type="tel"], input[type="password"], input[type="number"], input[type="date"], input[type="datetime-local"], input[type="month"], input[type="week"], input[type="time"], textarea',
        FOCUSABLE_TEXT: 'input:not([type]), input[type="text"], input[type="search"], input[type="url"], input[type="email"], input[type="tel"], input[type="password"], input[type="number"], input[type="date"], input[type="datetime-local"], input[type="month"], input[type="week"], input[type="time"], textarea, [contenteditable="true"], [contenteditable=""], [contenteditable="plaintext-only"]'
      };
      CLICKABLE_CATEGORY = {
        /** Nothing interactive under the pointer */
        NONE: "none",
        /** Navigation: <a href>, role=link, data-kp-url rows */
        LINK: "link",
        /** Discrete actions: <button>, role=button */
        BUTTON: "button",
        /** Typing surfaces: text inputs, textarea, contenteditable */
        TEXT: "text",
        /** Video/audio surface (thumbnail or player body) */
        MEDIA: "media",
        /** Continuous value: range, role=slider, media scrub tracks */
        SLIDER: "slider",
        /** Other form/ARIA controls: checkbox, radio, select, tab, switch, … */
        CONTROL: "control",
        /** Non-semantic interactive (cursor:pointer, onclick, tracked click listener) */
        GENERIC: "generic"
      };
      CSS_CLASSES = {
        CURSOR_HIDDEN: "kpv2-cursor-hidden",
        FOCUS: "kpv2-focus",
        DELETE: "kpv2-delete",
        HIGHLIGHT: "kpv2-highlight",
        HIDDEN: "kpv2-hidden",
        RIPPLE: "kpv2-ripple",
        FOCUS_OVERLAY: "kpv2-focus-overlay",
        /** Scroll Line origin-dot + line (popover / top-layer chrome) */
        SCROLL_LINE_OVERLAY: "kpv2-scroll-line",
        /** Scroll Line: fixed frame around a nested (in-page) overflow target */
        SCROLL_LINE_TARGET: "kpv2-scroll-line-target",
        /**
         * Strategy B: in-target absolute focus ring — mounted as last child of the
         * clickable/host with local max z-index + 1. Co-located paint; scrolls with
         * the element. Preference order: A DOM outline → B this ring → C body fixed.
         */
        FOCUS_RING_INTARGET: "kpv2-focus-ring-intarget",
        /** Temporary outline that scales up on F-click activation */
        FOCUS_PULSE: "kpv2-focus-pulse",
        /** Temporary outline with a marquee/chaser light traveling the perimeter on F-click */
        FOCUS_MARQUEE: "kpv2-focus-marquee",
        /** Temporary hard flash (strobe) on F-click activation */
        FOCUS_FLASH: "kpv2-focus-flash",
        /** Temporary dashed border whose dashes chase around the perimeter on F-click */
        FOCUS_DASH: "kpv2-focus-dash",
        /** Brief dashed outline when a URL action has no navigable URL under the cursor */
        FOCUS_DASH_DENIED: "kpv2-focus-dash-denied",
        /** Temporary frame that scales (pop then shrink) when copying an image under cursor */
        IMAGE_COPY_PULSE: "kpv2-image-copy-pulse",
        DELETE_OVERLAY: "kpv2-delete-overlay",
        /**
         * Shared inspector-mode hover chrome (Delete, Cols, future pick tools).
         * Kind-specific colors applied via CSS vars / inline styles.
         */
        INSPECTOR: "kpv2-inspector",
        INSPECTOR_OVERLAY: "kpv2-inspector-overlay",
        /** Top-right companion instruction while inspector pick is active (like highlight mode) */
        INSPECTOR_MODE_INDICATOR: "kpv2-inspector-mode-indicator",
        /** @deprecated prefer INSPECTOR + kind; kept for style/compat during transition */
        COLS: "kpv2-cols",
        COLS_OVERLAY: "kpv2-cols-overlay",
        /** Applied multicol layout on the chosen target */
        COLS_ACTIVE: "kpv2-cols-active",
        /** Page-mode markers on html/body while whole-page columns are active */
        COLS_PAGE: "kpv2-cols-page",
        /** Widget shell wrapping a columnized target (outline + slip chrome) */
        COLS_SHELL: "kpv2-cols-shell",
        /** Content region inside the shell that holds the target */
        COLS_BODY: "kpv2-cols-body",
        /** Placeholder left in flow when shell is promoted to a popover */
        COLS_PLACEHOLDER: "kpv2-cols-placeholder",
        /** Slip-edit chrome (NLE-style content window scrubber) */
        COLS_SLIP_BAR: "kpv2-cols-slip-bar",
        COLS_SLIP_TRACK: "kpv2-cols-slip-track",
        COLS_SLIP_KNOB: "kpv2-cols-slip-knob",
        COLS_SLIP_LABEL: "kpv2-cols-slip-label",
        /** Slip-bar action: promote columns widget to floating popover */
        COLS_EXPAND_BTN: "kpv2-cols-expand-btn",
        /** Slip-bar action: clear columns / restore element */
        COLS_CLOSE_BTN: "kpv2-cols-close-btn",
        HIGHLIGHT_OVERLAY: "kpv2-highlight-overlay",
        HIGHLIGHT_SELECTION: "kpv2-highlight-selection",
        /** Overlay boxes for Select Image (and text fallback when CSS Highlight is unavailable) */
        UNIT_SELECT_OVERLAY: "kpv2-unit-select-overlay",
        /** Outline around the Font Info inspected text run */
        FONT_INFO_OUTLINE: "kpv2-font-info-outline",
        /** Persistent outline for elements added in cumulative inspector pick */
        INSPECTOR_PICKED: "kpv2-inspector-picked",
        INSPECTOR_PICKED_OVERLAY: "kpv2-inspector-picked-overlay",
        INSPECTOR_UNION_OVERLAY: "kpv2-inspector-union-overlay",
        TEXT_FIELD_GLOW: "kpv2-text-field-glow",
        /** Full-viewport veil used to hide instant Scroll-to-Top / Bottom jumps */
        EDGE_JUMP_FADE: "kpv2-edge-jump-fade",
        /** Corner glyph on the fade veil (Scroll To Top / Bottom) */
        EDGE_JUMP_FADE_ICON: "kpv2-edge-jump-fade-icon",
        VIEWPORT_MODAL_FRAME: "kpv2-viewport-modal-frame",
        ESC_EXIT_LABEL: "kpv2-esc-exit-label",
        /** Vertical “Esc / to / exit” sidecar left of the text-mode orange bar. */
        TEXT_FOCUS_ESC_HINT: "kpv2-text-focus-esc-hint",
        /** Vertical “F / to / select” sidecar left of a hovered text field. */
        TEXT_HOVER_ACTIVATE_HINT: "kpv2-text-hover-activate-hint",
        TEXT_FOCUS_INPUT: "kpv2-text-focus-input",
        TEXT_FOCUS_INPUT_PARENT: "kpv2-text-focus-input-parent",
        /** Modifier: focused text field uses left-edge 10px pulsating bar (default style). */
        TEXT_FOCUS_LEFT_EDGE: "kpv2-text-focus-left-edge",
        /** Input chrome is painted on a taller visual shell (Gmail/Google search pill). */
        TEXT_FOCUS_DELEGATED: "kpv2-text-focus-delegated",
        /** Focused field is too short for the SVG "press Esc to exit" hint. */
        TEXT_FOCUS_HINT_HIDDEN: "kpv2-text-focus-hint-hidden",
        TEXT_HOVER_INPUT: "kpv2-text-hover-input",
        TEXT_HOVER_INPUT_PARENT: "kpv2-text-hover-input-parent",
        /** Canvas-based focus/delete overlay host (OverlayManager) */
        CANVAS_OVERLAY: "kpv2-canvas-overlay",
        /** CSS custom-properties focus/delete overlay host (OverlayManager) */
        CSS_PROPS_OVERLAY: "kpv2-css-props-overlay",
        // Omnibox overlay UI
        OMNIBOX_BACKDROP: "kpv2-omnibox-backdrop",
        OMNIBOX_PANEL: "kpv2-omnibox-panel",
        OMNIBOX_INPUT: "kpv2-omnibox-input",
        OMNIBOX_SUGGESTIONS: "kpv2-omnibox-suggestions",
        OMNIBOX_SUGGESTION: "kpv2-omnibox-suggestion",
        OMNIBOX_EMPTY: "kpv2-omnibox-empty",
        // PopupManager (shared backdrop for modals/popups that should blur the page)
        POPUP_BACKDROP: "kpv2-popup-backdrop"
      };
      TEXT_FOCUS_HINT_MIN_HEIGHT_PX = 28;
      ELEMENT_IDS = {
        CURSOR: "kpv2-cursor",
        STYLE: "kpv2-style"
      };
      Z_INDEX = {
        // Utility layers (occasionally used for measurement elements)
        PAGE_BEHIND: -1,
        DEFAULT: 1,
        // Keep all KeyPilot UI comfortably above typical site z-index values.
        // Note: Many browsers effectively clamp very large z-index values; using a
        // high-but-safe base avoids accidental collisions and keeps ordering clear.
        _BASE: 2147483e3,
        // Low-ish KeyPilot overlays
        /** Covers page content during fade edge-jumps; below chrome + cursor */
        EDGE_JUMP_FADE: 2147483010,
        VIEWPORT_MODAL_FRAME: 2147483010,
        HIGHLIGHT_SELECTION: 2147483015,
        // PopupManager layers (kept BELOW click overlays so the green click rectangle can sit above popups)
        POPUP_BACKDROP: 2147483009,
        POPUP_PANEL_BASE: 2147483012,
        POPUP_PANEL_MAX: 2147483017,
        // Focus/delete/highlight overlays
        OVERLAYS_BELOW_2: 2147483018,
        OVERLAYS_BELOW: 2147483019,
        OVERLAYS: 2147483020,
        OVERLAYS_ABOVE: 2147483021,
        // macOS-style control strip (upper-left; stays at top; below walkthrough in z-order)
        CONTROL_STRIP: 2147483025,
        // Onboarding walkthrough (top-left, stacked below the control strip on screen).
        // z-index above the strip so if they ever overlap the panel wins; still below
        // green hover/click overlays and floating keyboard help.
        ONBOARDING_PANEL: 2147483026,
        // Cols Toggle slip-edit bar (bottom of viewport; below keyboard help / cursor)
        COLS_SLIP_BAR: 2147483030,
        // Sized OS popup for Open Popover (http(s)); extension Guide still uses iframe overlay
        POPOVER_IFRAME_MODAL: 2147483035,
        // Notifications / message overlays
        MESSAGE_BOX: 2147483040,
        DEBUG_HUD: 2147483041,
        NOTIFICATION: 2147483040,
        // Omnibox overlay (should sit above most UI, but below keyboard help + cursor)
        OMNIBOX: 2147483042,
        // Floating keyboard reference + key-click tooltip (above page UI, below cursor)
        FLOATING_KEYBOARD_HELP: 2147483045,
        KEYBINDINGS_POPOVER: 2147483046,
        // Per-key floating config panel (above sticky key popover, below cursor)
        KEY_ACTION_CONFIG: 2147483047,
        // Compact Keyboard Layout Config palette (beside Reference while editing)
        KEYBOARD_LAYOUT_CONFIG: 2147483048,
        // Custom select lists: above Keyboard Ref / layout chrome, below click ripple
        SELECT_MENU: 2147483049,
        // Click-to-place arrow (fallback when Popover API unavailable)
        LAYOUT_PLACE_ARROW: 2147483052,
        // Cursor sits above chrome; click ripple is above even that so the
        // expanding circles always remain visible.
        CURSOR: 2147483050,
        RIPPLE: 2147483051
      };
      SCROLL = Object.freeze({
        /** Legacy large page step (popover parent→iframe PAGE_UP/DOWN path) */
        PAGE_PX: 800,
        /** C / V: smaller step (default = prior 400px × 1.25) */
        HALF_PAGE_PX: 500,
        /**
         * Hold C / V: continuous rAF scroll speed (px/s). Instant per-frame deltas —
         * not CSS smooth — so overlapping animations cannot jitter.
         */
        HOLD_PX_PER_SEC: 1400,
        /**
         * Delay before continuous rAF starts after the first keydown. Keeps a quick
         * tap as a single configured step; holding past this (or first OS repeat)
         * engages continuous motion.
         */
        HOLD_RAF_START_MS: 120,
        /** Default CSS scroll-behavior for keyboard scrolling */
        BEHAVIOR: "smooth",
        /** Blur-in duration for Scroll To Top / Bottom "Fade" jump style */
        EDGE_JUMP_BLUR_MS: 140,
        /** Opaque cover transition before the instant jump */
        EDGE_JUMP_COVER_MS: 90,
        /** Soft destination reveal duration after the jump */
        EDGE_JUMP_REVEAL_MS: 160,
        /** Clear the veil after the destination reveal */
        EDGE_JUMP_CLEAR_MS: 140,
        /**
         * After the instant jump, keep the veil opaque until scroll position is
         * stable (or this timeout). Covers CSS `scroll-behavior: smooth` and
         * Lenis-style hijacks that keep interpolating after scrollTo returns.
         */
        EDGE_JUMP_SETTLE_MS: 480,
        /** Scroll Line: no scroll inside this radius from the origin dot */
        LINE_DEADZONE_PX: 12,
        /**
         * Scroll Line: ease-in power. 1 = linear, 2 = quadratic (gentle near the
         * dot, ramps harder toward the edge of the range).
         */
        LINE_CURVE_EXPONENT: 1.75,
        /** Scroll Line: offset beyond the dead zone that maps to max speed */
        LINE_CURVE_RANGE_PX: 360,
        /** Scroll Line: cap on each axis */
        LINE_MAX_PX_PER_SEC: 2400
      });
      MODES = {
        NONE: "none",
        /**
         * Shared element-pick inspector (DOM inspector style).
         * Concrete tool is state.inspectorKind (see INSPECTOR_KIND).
         * Used by Delete Mode, Cols Toggle, and future pick tools.
         */
        INSPECTOR: "inspector",
        /**
         * @deprecated Use MODES.INSPECTOR + INSPECTOR_KIND.DELETE.
         * Kept so older status strings / comparisons still resolve if needed.
         */
        DELETE: "delete",
        /**
         * @deprecated Use MODES.INSPECTOR + INSPECTOR_KIND.COLS.
         */
        COLS: "cols",
        TEXT_FOCUS: "text_focus",
        HIGHLIGHT: "highlight",
        /** Viewport-fixed origin-dot autoscroll (Scroll Line key) */
        SCROLL_LINE: "scroll_line",
        POPOVER: "popover",
        OMNIBOX: "omnibox"
      };
      INSPECTOR_KIND = Object.freeze({
        DELETE: "delete",
        COLS: "cols",
        /** Cumulative element pick for Rectangle Select (Y) alternate mode */
        RECTANGLE_PICK: "rectangle_pick"
      });
      ELEMENT_SELECT_AGGREGATES = Object.freeze([
        "table",
        "figure",
        "picture",
        "ul",
        "ol",
        "dl"
      ]);
      ELEMENT_SELECT_LANDMARKS = Object.freeze([
        "article",
        "section",
        "aside",
        "header",
        "footer",
        "main",
        "nav"
      ]);
      ELEMENT_SELECT_ATOMS = Object.freeze([
        "h1",
        "h2",
        "h3",
        "h4",
        "h5",
        "h6",
        "p",
        "blockquote",
        "pre",
        "li",
        "img",
        "video",
        "audio",
        "svg"
      ]);
      ELEMENT_SELECT_FRAGMENTS = Object.freeze([
        "a",
        "code",
        "label",
        "td",
        "th",
        "caption",
        "figcaption",
        "dt",
        "dd",
        "summary"
      ]);
      ELEMENT_SELECT_TAGS = Object.freeze([
        ...ELEMENT_SELECT_AGGREGATES,
        ...ELEMENT_SELECT_LANDMARKS,
        ...ELEMENT_SELECT_ATOMS,
        ...ELEMENT_SELECT_FRAGMENTS
      ]);
      CURSOR_MODE = Object.freeze({
        NO_CUSTOM_CURSORS: "NO-CUSTOM-CURSORS",
        CUSTOM_CURSORS: "CUSTOM-CURSORS"
      });
      KP_UI_FONT = "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif";
      COLORS = {
        // Primary cursor colors
        FOCUS_GREEN: "rgba(0,180,0,0.95)",
        FOCUS_GREEN_BRIGHT: "rgba(0,128,0,0.95)",
        DELETE_RED: "rgba(220,0,0,0.95)",
        /** Cols Toggle accent (purple, distinct from delete red / highlight blue) */
        COLS_PURPLE: "rgba(156,39,176,0.95)",
        COLS_PURPLE_BRIGHT: "rgba(186,104,200,0.95)",
        HIGHLIGHT_BLUE: "rgba(0,120,255,0.95)",
        ORANGE: "#ff8c00",
        // Focus overlay (alternate) colors (used to visually distinguish DOM-hover targeting mode)
        FOCUS_BLUE: "rgba(33,150,243,0.95)",
        // Text and background colors
        TEXT_WHITE_PRIMARY: "rgba(255,255,255,0.95)",
        TEXT_WHITE_SECONDARY: "rgba(255,255,255,0.8)",
        TEXT_GREEN_BRIGHT: "#6ced2b",
        // Background colors
        MESSAGE_BG_BROWN: "#ad6007",
        MESSAGE_BG_GREEN: "#10911b",
        // Border and shadow colors
        ORANGE_BORDER: "rgba(255,140,0,0.4)",
        ORANGE_SHADOW: "rgba(255,140,0,0.45)",
        ORANGE_SHADOW_DARK: "rgba(255,140,0,0.8)",
        ORANGE_SHADOW_LIGHT: "rgba(255,140,0,0.3)",
        GREEN_SHADOW: "rgba(0,180,0,0.45)",
        GREEN_SHADOW_BRIGHT: "rgba(0,180,0,0.5)",
        BLUE_SHADOW: "rgba(33,150,243,0.35)",
        BLUE_SHADOW_BRIGHT: "rgba(33,150,243,0.45)",
        DELETE_SHADOW: "rgba(220,0,0,0.35)",
        DELETE_SHADOW_BRIGHT: "rgba(220,0,0,0.45)",
        COLS_SHADOW: "rgba(156,39,176,0.35)",
        COLS_SHADOW_BRIGHT: "rgba(156,39,176,0.5)",
        HIGHLIGHT_SHADOW: "rgba(0,120,255,0.35)",
        HIGHLIGHT_SHADOW_BRIGHT: "rgba(0,120,255,0.45)",
        BLACK_SHADOW: "rgba(40, 40, 40, 0.7)",
        // Ripple effect colors
        RIPPLE_GREEN: "rgba(0,200,0,0.35)",
        RIPPLE_GREEN_MID: "rgba(0,200,0,0.22)",
        RIPPLE_GREEN_TRANSPARENT: "rgba(0,200,0,0)",
        // Flash animation colors
        FLASH_GREEN: "rgba(0,255,0,1)",
        FLASH_GREEN_SHADOW: "rgba(0,255,0,0.8)",
        FLASH_GREEN_GLOW: "rgba(0,255,0,0.9)",
        /** URL actions (new tab, preview, popover) when the hover target has no URL */
        FLASH_DENIED: "rgba(255,140,0,1)",
        FLASH_DENIED_SHADOW: "rgba(255,140,0,0.85)",
        FLASH_DENIED_GLOW: "rgba(255,140,0,0.7)",
        // Image-copy pulse (distinct from green F-click pulse)
        IMAGE_COPY_FRAME: "rgba(33,150,243,0.95)",
        IMAGE_COPY_FRAME_SHADOW: "rgba(33,150,243,0.55)",
        IMAGE_COPY_FRAME_GLOW: "rgba(100,180,255,0.75)",
        IMAGE_COPY_FILL: "rgba(33,150,243,0.14)",
        IMAGE_COPY_FLASH: "rgba(255,255,255,0.45)",
        // Notification colors
        NOTIFICATION_SUCCESS: "#4CAF50",
        NOTIFICATION_ERROR: "#f44336",
        NOTIFICATION_WARNING: "#ff9800",
        NOTIFICATION_INFO: "#2196F3",
        NOTIFICATION_SHADOW: "rgba(0, 0, 0, 0.15)",
        // Text field glow
        TEXT_FIELD_GLOW: "rgba(255,165,0,0.8)",
        // Highlight selection colors
        HIGHLIGHT_SELECTION_BG: "rgba(0,120,255,0.3)",
        HIGHLIGHT_SELECTION_BORDER: "rgba(0,120,255,0.6)",
        // Font Info inspected-run outline (stroke, not Text Select fill)
        FONT_INFO_OUTLINE: "rgba(255, 193, 7, 0.95)",
        FONT_INFO_OUTLINE_SHADOW: "rgba(255, 193, 7, 0.45)",
        FONT_INFO_OUTLINE_FILL: "rgba(255, 193, 7, 0.08)",
        // Solid accents for ESC exit labels (distinct from translucent overlay borders)
        ORANGE_BG: "rgba(255, 165, 0, 0.9)",
        ORANGE_TEXT: "#fff",
        ORANGE_BORDER_SOLID: "#d35400",
        FOCUS_GREEN_BG: "rgba(46, 204, 113, 0.9)",
        FOCUS_GREEN_BG_T2: "rgba(46, 204, 113, 0.4)",
        FOCUS_GREEN_TEXT: "#fff",
        FOCUS_GREEN_SOLID: "#27ae60",
        FOCUS_BLUE_BG_T2: "rgba(33,150,243,0.25)"
      };
      RECTANGLE_SELECTION = {
        // Visual rectangle settings
        MIN_WIDTH: 3,
        // Minimum rectangle width to show (pixels)
        MIN_HEIGHT: 3,
        // Minimum rectangle height to show (pixels)
        MIN_DRAG_DISTANCE: 5,
        // Minimum drag distance to start selection (pixels)
        // Visual feedback settings
        SHOW_IMMEDIATE_FEEDBACK: true,
        // Show rectangle for any movement
        HIDE_ZERO_SIZE: false,
        // Don't hide zero-size rectangles
        // Performance limits (should match browser capabilities)
        MAX_AREA_PIXELS: 5e7,
        // 50M pixels (e.g., 10000x5000) - very generous limit
        MAX_TEXT_NODES: 1e4,
        // Maximum text nodes to process - matches browser selection limits
        ENABLE_AREA_LIMIT: false,
        // Disable area limiting by default - browsers handle large selections fine
        ENABLE_NODE_LIMIT: true
        // Keep node limit as safety measure for DOM traversal performance
        // Performance notes:
        // - Area limits are disabled by default because browsers can handle enormous text selections
        // - Node limits remain enabled to prevent DOM traversal performance issues on complex pages
        // - These limits only apply to rectangle selection, not manual browser selection
        // - The clipboard is typically the real limiting factor, not the selection itself
      };
      EDGE_ONLY_SELECTION = {
        // Smart Targeting Options
        SMART_TARGETING: {
          ENABLED: true,
          // Enable smart element targeting
          TEXT_ELEMENT_TAGS: [
            // HTML tags that commonly contain text
            "p",
            "span",
            "div",
            "h1",
            "h2",
            "h3",
            "h4",
            "h5",
            "h6",
            "li",
            "td",
            "th",
            "a",
            "strong",
            "em",
            "b",
            "i",
            "u",
            "blockquote",
            "pre",
            "code",
            "label",
            "legend",
            "article",
            "section",
            "header",
            "footer",
            "main",
            "aside",
            "nav"
          ],
          SKIP_ELEMENT_TAGS: [
            // HTML tags to skip (non-text elements)
            "img",
            "video",
            "audio",
            "canvas",
            "svg",
            "iframe",
            "script",
            "style",
            "noscript",
            "object",
            "embed"
          ],
          MIN_TEXT_LENGTH: 1,
          // Minimum text content length to consider
          CHECK_COMPUTED_STYLE: true,
          // Check if element is visible via computed style
          INCLUDE_ARIA_LABELS: true,
          // Include elements with aria-label/aria-labelledby
          MAX_ELEMENTS_TO_OBSERVE: 5e3
          // Maximum elements to observe simultaneously
        },
        // Character Detection Settings
        CHARACTER_DETECTION: {
          ENABLED: true,
          // Enable edge-level character detection
          USE_RANGE_API: true,
          // Use Range API for precise character positioning
          CACHE_CHARACTER_POSITIONS: true,
          // Cache character positions using WeakMap
          CHARACTER_CACHE_SIZE: 1e3,
          // Maximum characters to cache per element
          BOUNDARY_DETECTION_PRECISION: 1,
          // Pixel precision for boundary detection
          BATCH_CHARACTER_PROCESSING: true,
          // Process characters in batches
          CHARACTER_BATCH_SIZE: 50,
          // Number of characters to process per batch
          MAX_CHARACTERS_PER_ELEMENT: 1e4
          // Maximum characters to process per element
        },
        // Cache Configuration
        CACHE_CONFIGURATION: {
          ELEMENT_CACHE_SIZE: 1e3,
          // Maximum number of elements to cache
          CHARACTER_CACHE_SIZE: 5e3,
          // Maximum number of character positions to cache
          CACHE_CLEANUP_THRESHOLD: 800,
          // Start cleanup when cache reaches this size
          CACHE_CLEANUP_BATCH_SIZE: 200,
          // Number of entries to remove during cleanup
          ENABLE_PREDICTIVE_CACHING: true,
          // Pre-cache elements likely to intersect
          PREDICTIVE_CACHE_DISTANCE: 100,
          // Distance in pixels to pre-cache elements
          CACHE_TTL_MS: 3e4,
          // Time-to-live for cached entries (30 seconds)
          ENABLE_CACHE_COMPRESSION: false
          // Enable cache compression (experimental)
        },
        // Performance Monitoring Configuration
        PERFORMANCE_MONITORING: {
          ENABLED: false,
          // Enable performance monitoring
          MONITORING_INTERVAL: 1e3,
          // How often to check performance (ms)
          COLLECT_DETAILED_METRICS: true,
          // Collect detailed performance metrics
          TRACK_CACHE_EFFICIENCY: true,
          // Track cache hit/miss ratios
          TRACK_PROCESSING_TIME: true,
          // Track processing time per operation
          TRACK_MEMORY_USAGE: true,
          // Track memory usage
          PERFORMANCE_LOG_INTERVAL: 5e3,
          // How often to log performance stats (ms)
          ENABLE_PERFORMANCE_ALERTS: true
          // Enable performance degradation alerts
        },
        // Fallback Configuration
        FALLBACK_CONFIGURATION: {
          ENABLED: true,
          // Enable automatic fallback
          FALLBACK_THRESHOLD_MS: 15,
          // Fall back to spatial if processing exceeds this
          MAX_CONSECUTIVE_FAILURES: 3,
          // Max failures before fallback
          FALLBACK_RECOVERY_ATTEMPTS: 5,
          // Attempts to recover from fallback
          FALLBACK_RECOVERY_DELAY: 2e3,
          // Delay between recovery attempts (ms)
          ENABLE_GRACEFUL_DEGRADATION: true,
          // Enable graceful performance degradation
          FALLBACK_TO_SPATIAL_METHOD: true
          // Fallback to spatial intersection method
        },
        // Performance Thresholds
        MAX_PROCESSING_TIME_MS: 10,
        // Maximum time for edge processing (ms)
        MAX_ELEMENTS_PER_UPDATE: 50,
        // Maximum elements to process per update
        FALLBACK_THRESHOLD_MS: 15,
        // Fall back to spatial if processing exceeds this
        CACHE_HIT_RATIO_THRESHOLD: 0.7,
        // Minimum acceptable cache hit ratio
        // Memory Management
        MAX_MEMORY_USAGE_MB: 50,
        // Maximum memory usage for edge-only processing
        MEMORY_CHECK_INTERVAL: 5e3,
        // How often to check memory usage (ms)
        ENABLE_MEMORY_MONITORING: true,
        // Monitor memory usage and cleanup
        GARBAGE_COLLECTION_THRESHOLD: 0.8,
        // Trigger cleanup at 80% of memory limit
        // Processing Options
        INTERSECTION_OBSERVER_THRESHOLDS: [0, 0.1, 0.5, 1],
        // Multiple thresholds for granular updates
        BATCH_PROCESSING_SIZE: 10,
        // Process elements in batches of this size
        ENABLE_ADAPTIVE_PROCESSING: true,
        // Adjust processing based on page complexity
        FRAME_RATE_TARGET: 60,
        // Target frame rate during drag operations
        // Adaptive Processing Settings (Task 2.1)
        PAGE_COMPLEXITY_ANALYSIS: {
          ENABLE_COMPLEXITY_ANALYSIS: false,
          // Enable page complexity analysis
          ELEMENT_COUNT_THRESHOLD_LOW: 500,
          // Low complexity threshold
          ELEMENT_COUNT_THRESHOLD_HIGH: 2e3,
          // High complexity threshold
          DOM_DEPTH_THRESHOLD_LOW: 10,
          // Low DOM depth threshold
          DOM_DEPTH_THRESHOLD_HIGH: 20,
          // High DOM depth threshold
          TEXT_NODE_DENSITY_THRESHOLD: 0.3,
          // Text node density threshold
          COMPLEXITY_CHECK_INTERVAL: 1e4
          // How often to analyze page complexity (ms)
        },
        FRAME_RATE_PROCESSING: {
          TARGET_FPS: 60,
          // Target frame rate during drag operations
          FRAME_TIME_BUDGET_MS: 16.67,
          // Time budget per frame (1000ms / 60fps)
          PROCESSING_TIME_BUDGET_MS: 8,
          // Max processing time per frame
          FRAME_RATE_MONITORING_WINDOW: 10,
          // Number of frames to monitor for rate calculation
          MIN_ACCEPTABLE_FPS: 30,
          // Minimum acceptable frame rate
          FRAME_RATE_ADJUSTMENT_FACTOR: 0.8
          // Reduce processing when frame rate drops
        },
        BATCH_PROCESSING: {
          ENABLE_BATCH_PROCESSING: true,
          // Enable batch processing optimization
          DEFAULT_BATCH_SIZE: 5,
          // Default batch size for processing
          MAX_BATCH_SIZE: 20,
          // Maximum batch size
          MIN_BATCH_SIZE: 1,
          // Minimum batch size
          BATCH_TIMEOUT_MS: 4,
          // Maximum time to wait for batch completion
          ADAPTIVE_BATCH_SIZING: true
          // Adjust batch size based on performance
        },
        QUALITY_ADJUSTMENTS: {
          ENABLE_QUALITY_ADJUSTMENTS: true,
          // Enable quality adjustments based on available time
          HIGH_QUALITY_TIME_THRESHOLD: 5,
          // Time threshold for high quality processing (ms)
          MEDIUM_QUALITY_TIME_THRESHOLD: 10,
          // Time threshold for medium quality processing (ms)
          LOW_QUALITY_PROCESSING_LIMIT: 20,
          // Maximum elements to process in low quality mode
          QUALITY_ADJUSTMENT_HYSTERESIS: 2
          // Frames to wait before quality adjustment
        },
        // Predictive Caching Settings (Task 2.2)
        PREDICTIVE_CACHING: {
          ENABLE_PREDICTIVE_CACHING: true,
          // Enable predictive caching strategies
          ENABLE_USER_BEHAVIOR_ANALYSIS: true,
          // Analyze user behavior patterns
          ENABLE_VIEWPORT_BASED_CACHING: true,
          // Cache based on viewport position
          ENABLE_SCROLL_PREDICTION: true,
          // Predict scroll direction and cache ahead
          // User behavior analysis
          BEHAVIOR_PATTERN_WINDOW: 20,
          // Number of recent interactions to analyze
          INTERACTION_TIMEOUT_MS: 2e3,
          // Time between interactions to consider separate
          MIN_PATTERN_CONFIDENCE: 0.6,
          // Minimum confidence to act on patterns
          PATTERN_ANALYSIS_INTERVAL: 5e3,
          // How often to analyze patterns (ms)
          // Viewport-based caching
          VIEWPORT_CACHE_MARGIN: 200,
          // Pixels beyond viewport to cache
          VIEWPORT_CACHE_SECTORS: 9,
          // Divide viewport into sectors for caching
          CACHE_WARMING_DISTANCE: 300,
          // Distance ahead to warm cache (pixels)
          VIEWPORT_UPDATE_THROTTLE: 100,
          // Throttle viewport updates (ms)
          // Scroll prediction
          SCROLL_VELOCITY_SAMPLES: 5,
          // Number of scroll samples for velocity calculation
          SCROLL_PREDICTION_DISTANCE: 500,
          // Distance to predict ahead (pixels)
          MIN_SCROLL_VELOCITY: 50,
          // Minimum velocity to trigger prediction (px/s)
          SCROLL_DIRECTION_THRESHOLD: 10,
          // Pixels to determine scroll direction
          // Cache preloading
          PRELOAD_BATCH_SIZE: 10,
          // Elements to preload per batch
          PRELOAD_THROTTLE_MS: 50,
          // Throttle between preload batches
          MAX_PRELOAD_ELEMENTS: 100,
          // Maximum elements to preload
          PRELOAD_PRIORITY_THRESHOLD: 0.7
          // Confidence threshold for high priority preload
        },
        // Debug and Monitoring
        ENABLE_PERFORMANCE_LOGGING: false,
        // Log detailed performance metrics
        ENABLE_CACHE_METRICS: false,
        // Track cache hit/miss ratios
        ENABLE_MEMORY_LOGGING: false,
        // Log memory usage (can be verbose)
        PERFORMANCE_LOG_INTERVAL: 5e3
        // How often to log performance stats (ms)
      };
      FEATURE_FLAGS = {
        // Rectangle Selection Method
        // Prefer caretRangeFromPoint (browser-native drag semantics). Edge-only IntersectionObserver
        // is off by default: a non-ancestor fixed root never reports intersections, so selection
        // stayed empty and completeSelection would not exit highlight mode.
        USE_INTELLIGENT_RECTANGLE_SELECTION: true,
        // Use browser-native caret selection instead of spatial intersection
        USE_NATIVE_SELECTION_API: true,
        // Use document.caretRangeFromPoint for efficient selection
        // Edge-Only Processing Control (experimental / heavy; off by default — see USE_EDGE_ONLY_SELECTION)
        ENABLE_EDGE_ONLY_PROCESSING: false,
        // Use edge-only intersection processing
        EDGE_ONLY_FALLBACK_ENABLED: true,
        // Allow fallback to spatial method if edge-only fails
        FORCE_EDGE_ONLY_MODE: false,
        // Force edge-only processing even if performance degrades
        ENABLE_EDGE_ONLY_CACHE: true,
        // Enable text node caching for edge-only processing
        // Enhanced RectangleIntersectionObserver Integration (Task 2)
        ENABLE_ENHANCED_RECTANGLE_OBSERVER: false,
        // Master flag for enhanced integration (Task 2.1, 2.2, 2.3)
        // Edge-Only Processing Feature Flags (Task 1.1)
        USE_EDGE_ONLY_SELECTION: false,
        // Off: broken root/target relationship; use caret/spatial instead
        ENABLE_SMART_TARGETING: true,
        // Enable smart element targeting
        ENABLE_CHARACTER_DETECTION: true,
        // Enable edge-level character detection
        ENABLE_SELECTION_CACHING: true,
        // Enable text node caching
        ENABLE_AUTOMATIC_FALLBACK: true,
        // Auto-fallback on performance issues
        ENABLE_EDGE_BATCH_PROCESSING: true,
        // Batch intersection updates
        ENABLE_PREDICTIVE_CACHING: false,
        // Predictive caching off with edge-only stack
        DETAILED_EDGE_LOGGING: false,
        // Detailed debug logging for edge processing (off for ship)
        EDGE_CACHE_SIZE_MANAGEMENT: true,
        // Enable cache size management
        EDGE_ADAPTIVE_PROCESSING: true,
        // Enable adaptive processing
        ENABLE_TEXT_ELEMENT_FILTER: true,
        // Enable TextElementFilter class
        ENABLE_EDGE_CHARACTER_DETECTOR: true,
        // Enable EdgeCharacterDetector class
        // Selection behavior options
        RECTANGLE_SELECTION_FALLBACK_TO_SPATIAL: true,
        // Fall back to spatial method if intelligent method fails
        RECTANGLE_SELECTION_SCAN_STEP: 8,
        // Pixel step size for boundary scanning (performance vs accuracy)
        RECTANGLE_SELECTION_MAX_SCAN_TIME: 50,
        // Maximum time in ms to spend scanning for boundaries
        // Clipboard options
        ENABLE_RICH_TEXT_CLIPBOARD: true,
        // Copy both plain text and HTML formatting to clipboard
        RICH_TEXT_FALLBACK_TO_PLAIN: true,
        // Fall back to plain text if rich text copying fails
        // UI feature flags
        SHOW_WINDOW_OUTLINE: false,
        // Show window outline during text mode
        // When true, KP_NEW_TAB opens pages/newtab.html. When false, opens Chrome's
        // default new tab (omit url → chrome://newtab). Custom NTP page stays in the
        // repo for later / Firefox; chrome_url_overrides is not declared in manifest.
        USE_CUSTOM_NEWTAB_PAGE: false,
        // Hover/click targeting strategy (product decision: DOM-hover only)
        // Permanent primary path: attach DOM hover listeners and drive `state.focusEl` from
        // browser-native hover targeting. RBush spatial indexing is retired (vendor removed;
        // residual index code is no-op / isolated). Activation (F) still falls back to
        // elementFromPoint if nothing is hovered.
        ENABLE_DOM_HOVER_LISTENERS: true,
        // Interactive element discovery (TreeWalker + MutationObserver + IntersectionObserver +
        // spatial culling) was built to feed RBush hit-testing. With DOM-hover as the primary
        // path it is unnecessary idle/main-thread work on every page. Keep false unless
        // re-enabling a spatial index / fixed-overlay hit-test backend.
        ENABLE_INTERACTIVE_DISCOVERY: false,
        // Wrap EventTarget.prototype.addEventListener to track click handlers for
        // non-semantic "JS-only" clickables. Costs a small tax on every listener
        // registration in the content-script world. Default on to preserve hover of
        // onclick-less delegated widgets; set false if profiling shows it matters.
        ENABLE_CLICK_LISTENER_TRACKING: true,
        // ---- Focus-ring paint (DOM-hover) ----
        // Preference order (see unused/reference-info/focus-ring-paint.md):
        //   A = DOM outline on the paint target (default; cheapest)
        //   B = in-target absolute ring (local max z-index + 1) when A cannot show
        //   C = body fixed overlay when B cannot mount (replaced elements, etc.)
        //
        // ENABLE_FOCUS_CLIP_INSET: while still on A, grade outline-offset from
        //   clip-ancestor free room: offset = clamp(minRoom - stroke, -stroke, +2).
        //   Mild bleed → mild inset (e.g. 1px room, 3px stroke → -2), not a jump
        //   to B/C. Does not mutate page overflow (that broke IMDb carousels).
        //
        // ENABLE_FOCUS_TIGHT_WRAPPER_PROMOTION: paint on a same-size clip ancestor
        //   instead of the target. Default OFF: on IMDb this promotes off
        //   <a.ipc-lockup-overlay> onto .ipc-poster so the real clickable never shows
        //   data-kp-focus.
        //
        // ENABLE_IN_TARGET_FOCUS_RING: allow strategy B when A cannot show a ring —
        //   inject position:absolute ring as last child of host (z-index maxLocal+1,
        //   border-radius from host). Falls back to strategy C if the host cannot
        //   accept children (replaced elements, etc.).
        ENABLE_FOCUS_CLIP_INSET: true,
        ENABLE_FOCUS_TIGHT_WRAPPER_PROMOTION: false,
        ENABLE_IN_TARGET_FOCUS_RING: true,
        // Debug and development flags — keep false in store builds.
        // Runtime verbose console output is Settings → About → Debug logging
        // (`kp_settings_v1.debugLogging` / `window.KEYPILOT_DEBUG`), not these flags.
        DEBUG_RECTANGLE_SELECTION: false,
        // Enable detailed logging for rectangle selection
        DEBUG_EDGE_ONLY_PROCESSING: false,
        // Enable detailed logging for edge-only processing
        SHOW_SELECTION_METHOD_IN_UI: false,
        // Show which selection method was used in notifications
        DEBUG_RECTANGLE_HUD: false,
        // Show live rectangle debugging HUD with coordinates and calls
        ENABLE_DEBUG_PANEL: false,
        // Enable upper-right debug panel showing performance metrics
        // Interactive HUD for shadow-DOM hover paint (msn.com / archive.org).
        // Shows leaf under pointer, resolved hover/paint targets, auto A/B/C choice,
        // and lets you force Auto / Auto B→C / A / B / C. Toggle: Alt+D
        // (or keyPilot.setShadowRootDebugHud).
        DEBUG_SHADOW_ROOT_HUD: false
      };
    }
  });

  // themes/schema.js
  function createProTypeTokens(stacks = {}) {
    return {
      stacks: {
        display: stacks.display || PRO_SANS,
        heading: stacks.heading || PRO_SANS,
        subhead: stacks.subhead || PRO_SANS,
        body: stacks.body || PRO_SANS,
        ui: stacks.ui || PRO_SANS,
        kbd: stacks.kbd || PRO_MONO,
        mono: stacks.mono || PRO_MONO,
        caption: stacks.caption || PRO_SANS
      },
      size: {
        display: "22px",
        h1: "22px",
        h2: "16px",
        h3: "14px",
        body: "13px",
        ui: "12px",
        kbd: "10px",
        caption: "11px",
        code: "12px"
      },
      scale: "1.25",
      weight: {
        regular: "400",
        medium: "500",
        semibold: "600",
        bold: "700"
      },
      letterSpacing: {
        display: "0.02em",
        titlebar: "0.02em",
        ui: "normal"
      },
      textTransform: {
        display: "none",
        titlebar: "none"
      },
      lineHeight: {
        tight: "1.2",
        body: "1.35",
        prose: "1.55"
      }
    };
  }
  function createTitlebarChromeTokens(overrides = {}) {
    return {
      titleWeight: "600",
      iconDisplay: "none",
      iconSize: "12px",
      kbdTransform: "none",
      kbdTracking: "0.02em",
      ...overrides
    };
  }
  function createProRadiusTokens(overrides = {}) {
    return {
      none: "0px",
      xs: "2px",
      sm: "3px",
      md: "6px",
      lg: "10px",
      pill: "999px",
      panel: "3px",
      btn: "2px",
      field: "2px",
      key: "7px",
      plate: "14px",
      ...overrides
    };
  }
  function createKeyChromeTokens(overrides = {}) {
    return {
      shading: "bevel",
      border: "1px solid rgba(0, 0, 0, 0.4)",
      cornerMode: "radius",
      cutSize: "4px",
      ...overrides
    };
  }
  function keyClipPath(cutSize) {
    const s = cutSize || "4px";
    return `polygon(${s} 0, calc(100% - ${s}) 0, 100% ${s}, 100% calc(100% - ${s}), calc(100% - ${s}) 100%, ${s} 100%, 0 calc(100% - ${s}), 0 ${s})`;
  }
  function themeToCssVars(theme) {
    const t = theme && typeof theme === "object" ? theme : {};
    const type2 = t.type || createProTypeTokens();
    const stacks = type2.stacks || {};
    const size = type2.size || {};
    const weight = type2.weight || {};
    const ls = type2.letterSpacing || {};
    const tf = type2.textTransform || {};
    const lh = type2.lineHeight || {};
    const radius = t.radius || createProRadiusTokens();
    const color4 = t.color || {};
    const effect = t.effect || {};
    const shape = t.shape || { cornerMode: "radius", cutSize: "0px" };
    const keys = t.keys || createKeyChromeTokens();
    const keyCornerCut = (keys.cornerMode || "radius") === "cut";
    const icons = t.icons || {};
    const iconColor = icons.color || {};
    const vars = {
      "--kp-theme-id": String(t.id || DEFAULT_THEME_ID),
      "--kp-font-display": stacks.display || PRO_SANS,
      "--kp-font-heading": stacks.heading || PRO_SANS,
      "--kp-font-subhead": stacks.subhead || PRO_SANS,
      "--kp-font-body": stacks.body || PRO_SANS,
      "--kp-font-ui": stacks.ui || PRO_SANS,
      "--kp-font-kbd": stacks.kbd || PRO_MONO,
      "--kp-font-mono": stacks.mono || PRO_MONO,
      "--kp-font-caption": stacks.caption || PRO_SANS,
      "--kp-type-scale": String(type2.scale || "1"),
      "--kp-type-display-size": size.display || "22px",
      "--kp-type-h1-size": size.h1 || "22px",
      "--kp-type-h2-size": size.h2 || "16px",
      "--kp-type-h3-size": size.h3 || "14px",
      "--kp-type-body-size": size.body || "13px",
      "--kp-type-ui-size": size.ui || "12px",
      "--kp-type-kbd-size": size.kbd || "10px",
      "--kp-type-caption-size": size.caption || "11px",
      "--kp-type-code-size": size.code || "12px",
      "--kp-type-weight-regular": weight.regular || "400",
      "--kp-type-weight-medium": weight.medium || "500",
      "--kp-type-weight-semibold": weight.semibold || "600",
      "--kp-type-weight-bold": weight.bold || "700",
      "--kp-type-tracking-display": ls.display || "0.02em",
      "--kp-type-tracking-titlebar": ls.titlebar || "0.02em",
      "--kp-type-tracking-ui": ls.ui || "normal",
      "--kp-type-transform-display": tf.display || "none",
      "--kp-type-transform-titlebar": tf.titlebar || "none",
      "--kp-titlebar-title-weight": t.titlebar && t.titlebar.titleWeight || "600",
      "--kp-titlebar-icon-display": t.titlebar && t.titlebar.iconDisplay || "none",
      "--kp-titlebar-icon-size": t.titlebar && t.titlebar.iconSize || "12px",
      "--kp-kbd-transform": t.titlebar && t.titlebar.kbdTransform || "none",
      "--kp-kbd-tracking": t.titlebar && t.titlebar.kbdTracking || "0.02em",
      "--kp-type-leading-tight": lh.tight || "1.2",
      "--kp-type-leading-body": lh.body || "1.35",
      "--kp-type-leading-prose": lh.prose || "1.55",
      "--kp-radius-none": radius.none || "0px",
      "--kp-radius-xs": radius.xs || "2px",
      "--kp-radius-sm": radius.sm || "3px",
      "--kp-radius-md": radius.md || "6px",
      "--kp-radius-lg": radius.lg || "10px",
      "--kp-radius-pill": radius.pill || "999px",
      "--kp-radius-panel": radius.panel || "3px",
      "--kp-radius-btn": radius.btn || "2px",
      "--kp-radius-field": radius.field || "2px",
      "--kp-radius-key": radius.key || "7px",
      "--kp-radius-plate": radius.plate || "14px",
      "--kp-color-bg": color4.bg || "#0f0f10",
      "--kp-color-panel": color4.panel || "#232323",
      "--kp-color-panel-edge": color4.panelEdge || "#3a3a3a",
      "--kp-color-panel-edge-dark": color4.panelEdgeDark || "#111",
      "--kp-color-title-top": color4.titleTop || "#4c4c4c",
      "--kp-color-title-mid": color4.titleMid || "#353535",
      "--kp-color-title-bot": color4.titleBot || "#252525",
      "--kp-color-btn-top": color4.btnTop || "#4a4a4a",
      "--kp-color-btn-mid": color4.btnMid || "#343434",
      "--kp-color-btn-bot": color4.btnBot || "#2a2a2a",
      "--kp-color-lit-top": color4.litTop || "#5a7a9a",
      "--kp-color-lit-bot": color4.litBot || "#3a5570",
      "--kp-color-lit-edge": color4.litEdge || "#2a4a66",
      "--kp-color-accent": color4.accent || "#4a90c8",
      "--kp-color-accent-2": color4.accent2 || color4.accent || "#4a90c8",
      "--kp-color-fg": color4.fg || "#ddd",
      "--kp-color-fg-dim": color4.fgDim || "#aaa",
      "--kp-color-fg-mute": color4.fgMute || "#777",
      "--kp-color-field-bg": color4.fieldBg || "#141414",
      "--kp-color-field-edge": color4.fieldEdge || "#0a0a0a",
      "--kp-color-field-inset": color4.fieldInsetTop || "#333",
      "--kp-color-hover": color4.hover || "rgba(255,255,255,0.06)",
      "--kp-color-selected": color4.selected || "rgba(74,144,200,0.22)",
      "--kp-color-selected-text": color4.selectedText || "#e8f0f8",
      "--kp-color-focus-ring": color4.focusRing || "inset 0 0 0 1px rgba(74,144,200,0.55)",
      "--kp-color-kbd-fg": color4.kbdColor || color4.fg || "#ddd",
      "--kp-titlebar-bg": (() => {
        const titleGrad = `linear-gradient(180deg, ${color4.titleTop || "#4c4c4c"} 0%, ${color4.titleMid || "#353535"} 45%, ${color4.titleBot || "#252525"} 100%)`;
        const baked = String(effect.titlebarBg || "");
        const idx = baked.lastIndexOf("linear-gradient(180deg");
        return idx > 0 ? `${baked.slice(0, idx)}${titleGrad}` : titleGrad;
      })(),
      "--kp-titlebar-border": effect.titlebarBorder || `1px solid ${color4.panelEdgeDark || "#111"}`,
      "--kp-titlebar-shadow": effect.titlebarShadow || `0 1px 0 ${color4.panelEdge || "#3a3a3a"}`,
      "--kp-panel-bg": color4.panel || effect.panelBg || "#232323",
      "--kp-panel-border": effect.panelBorder || `1px solid ${color4.panelEdgeDark || "#111"}`,
      "--kp-panel-shadow": effect.panelShadow || `0 0 0 1px ${color4.panelEdge || "#3a3a3a"} inset, 0 0 0 1px rgba(190, 190, 190, 0.52), 0 0 10px rgba(255, 255, 255, 0.14), 0 16px 40px rgba(0,0,0,0.55)`,
      "--kp-btn-bg": effect.btnBg || `linear-gradient(180deg, ${color4.btnTop || "#4a4a4a"} 0%, ${color4.btnMid || "#343434"} 50%, ${color4.btnBot || "#2a2a2a"} 100%)`,
      "--kp-btn-border": effect.btnBorder || `1px solid ${color4.panelEdgeDark || "#111"}`,
      "--kp-btn-lit-bg": effect.btnLitBg || `linear-gradient(180deg, ${color4.litTop || "#5a7a9a"} 0%, ${color4.litBot || "#3a5570"} 100%)`,
      "--kp-btn-lit-border": effect.btnLitBorder || `1px solid ${color4.litEdge || "#2a4a66"}`,
      "--kp-field-bg": effect.fieldBg || (color4.fieldBg || "#141414"),
      "--kp-field-border": effect.fieldBorder || `1px solid ${color4.fieldEdge || "#0a0a0a"}`,
      "--kp-field-shadow": effect.fieldShadow || `inset 0 1px 0 ${color4.fieldInsetTop || "#333"}`,
      "--kp-kbd-bg": effect.kbdBg || (color4.fieldBg || "#141414"),
      "--kp-kbd-border": effect.kbdBorder || `1px solid ${color4.panelEdgeDark || "#111"}`,
      "--kp-kbd-shadow": effect.kbdShadow || "none",
      "--kp-backdrop-bg": effect.backdropBg || "rgba(0,0,0,0.35)",
      "--kp-backdrop-blur": effect.backdropBlur || "blur(6px)",
      "--kp-hatch-edit": effect.hatchEdit || "repeating-linear-gradient(-45deg, rgba(180, 200, 220, 0.08) 0px, rgba(180, 200, 220, 0.08) 1px, transparent 1px, transparent 7px)",
      "--kp-hatch-edit-titlebar-bg": effect.hatchEditTitlebarBg || "linear-gradient(180deg, #646464 0%, #4a4a4a 45%, #383838 100%)",
      "--kp-hatch-edit-body-bg": effect.hatchEditBodyBg || "#1a1c20",
      "--kp-scrollbar-thumb": color4.scrollbarThumb || "#4a4a4a",
      "--kp-scrollbar-thumb-hover": color4.scrollbarThumbHover || "#5c5c5c",
      "--kp-scrollbar-track": color4.scrollbarTrack || (color4.fieldBg || "#141414"),
      "--kp-corner-mode": shape.cornerMode || "radius",
      "--kp-cut-size": shape.cutSize || "0px",
      "--kp-key-shading": keys.shading || "bevel",
      "--kp-key-border": keys.border || "1px solid rgba(0, 0, 0, 0.4)",
      "--kp-key-corner-mode": keys.cornerMode || "radius",
      "--kp-key-cut-size": keys.cutSize || "4px",
      "--kp-key-clip": keyCornerCut ? keyClipPath(keys.cutSize || "4px") : KEY_CLIP_NONE,
      "--kp-key-effective-radius": keyCornerCut ? "0px" : radius.key || "7px",
      // Used by @supports (corner-shape: bevel) upgrade (clip-path baseline otherwise).
      "--kp-key-shape-radius": keyCornerCut ? keys.cutSize || "4px" : radius.key || "7px",
      "--kp-key-corner-shape": keyCornerCut ? "bevel" : "round",
      "--kp-key-sheen-opacity": (keys.shading || "bevel") === "flat" ? "0" : "1",
      "--kp-key-shade-layer": (keys.shading || "bevel") === "flat" ? "transparent" : KEY_SHADE_BEVEL,
      "--kp-icon-chrome": iconColor.chrome || (color4.fg || "#ddd"),
      "--kp-icon-keycap": iconColor.keycap || (color4.fg || "#0c1018"),
      "--kp-icon-accent": iconColor.accent || (color4.accent || "#4a90c8"),
      "--kp-key-icon": iconColor.keycap || "#0c1018"
    };
    return vars;
  }
  function cssVarsToBlock(vars, selector = ":host, :root, [data-kp-theme]") {
    const lines = Object.entries(vars || {}).map(([k, v]) => `  ${k}: ${v};`);
    return `${selector} {
${lines.join("\n")}
}`;
  }
  function getTitlebarChromeCss() {
    return `
.kp-titlebar-icon {
  display: var(--kp-titlebar-icon-display, none);
  width: var(--kp-titlebar-icon-size, 12px);
  height: var(--kp-titlebar-icon-size, 12px);
  flex: 0 0 auto;
  background-color: var(--kp-icon-chrome, currentColor);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}
[data-kp-titlebar-shortcut],
.kp-titlebar-kbd {
  font-family: var(--kp-font-kbd, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace);
  font-size: var(--kp-type-kbd-size, 10px);
  font-weight: var(--kp-type-weight-regular, 400);
  line-height: 1.2;
  text-transform: var(--kp-kbd-transform, none);
  letter-spacing: var(--kp-kbd-tracking, 0.02em);
  padding: 1px 6px;
  border: var(--kp-kbd-border, 1px solid #111);
  border-radius: var(--kp-radius-btn, 2px);
  background: var(--kp-kbd-bg, #141414);
  color: var(--kp-color-kbd-fg, #ddd);
  box-shadow: var(--kp-kbd-shadow, none);
  box-sizing: border-box;
}
.kpv2-popover-titlebar,
[data-kp-popover-titlebar],
[data-kp-floating-keyboard-titlebar],
.kp-cfg-titlebar,
.kp-action-config-panel__titlebar,
.kp-procedure-result__titlebar,
.kp-practice-popover__header {
  letter-spacing: var(--kp-type-tracking-titlebar, 0.02em);
  text-transform: var(--kp-type-transform-titlebar, none);
}
.kpv2-popover-titlebar-title,
[data-kp-floating-keyboard-title],
.kp-cfg-title,
.kp-action-config-panel__title,
.kp-procedure-result__title,
.kp-practice-popover__title {
  font-weight: var(--kp-titlebar-title-weight, 600);
  letter-spacing: var(--kp-type-tracking-titlebar, 0.02em);
  text-transform: var(--kp-type-transform-titlebar, none);
  color: var(--kp-color-fg, inherit);
}
`.trim();
  }
  function getSelectMenuCss() {
    return `
.kp-select {
  display: inline-flex;
  align-items: stretch;
  flex: 0 0 auto;
  min-width: 0;
  box-sizing: border-box;
  font-family: var(--kp-font-ui, Helvetica, Arial, sans-serif);
}
.kp-select-trigger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  min-width: 0;
  margin: 0;
  padding: 2px 6px;
  border: var(--kp-field-border, 1px solid #0a0a0a);
  border-radius: var(--kp-radius-field, 2px);
  background: var(--kp-field-bg, #141414);
  color: var(--kp-color-fg, #ddd);
  box-shadow: var(--kp-field-shadow, none);
  font: inherit;
  font-size: 11px;
  line-height: 1.2;
  text-align: left;
  text-transform: none;
  letter-spacing: normal;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  outline: none;
  box-sizing: border-box;
}
.kp-select--titlebar .kp-select-trigger {
  width: 190px;
  height: 22px;
  margin-left: 6px;
}
.kp-select-trigger:hover {
  background: color-mix(in srgb, var(--kp-color-hover, rgba(255,255,255,0.08)) 70%, var(--kp-field-bg, #141414));
}
.kp-select-trigger:focus-visible {
  outline: 1px solid var(--kp-color-focus-ring, var(--kp-color-accent, #4a90c8));
  outline-offset: 1px;
}
.kp-select.is-open .kp-select-trigger,
.kp-select-trigger[aria-expanded="true"] {
  border-color: var(--kp-color-accent, #4a90c8);
}
.kp-select-trigger-icon,
.kp-select-item-icon {
  display: none;
  width: 12px;
  height: 12px;
  flex: 0 0 auto;
  background-color: var(--kp-icon-chrome, currentColor);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}
.kp-select-trigger-icon:not([hidden]),
.kp-select-item-icon:not([hidden]) {
  display: block;
}
.kp-select-trigger-label,
.kp-select-item-label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-transform: none;
  letter-spacing: normal;
}
.kp-select-chevron {
  width: 0;
  height: 0;
  margin-left: 2px;
  border-left: 3.5px solid transparent;
  border-right: 3.5px solid transparent;
  border-top: 4px solid currentColor;
  opacity: 0.65;
  flex: 0 0 auto;
}
.kp-select-menu {
  position: fixed;
  /* Kill UA popover centering (inset 0 / margin auto) without locking longhands. */
  margin: 0;
  top: auto;
  right: auto;
  bottom: auto;
  left: auto;
  width: max-content;
  height: fit-content;
  z-index: 2147483049;
  padding: 4px 0;
  min-width: 190px;
  max-width: min(360px, calc(100vw - 16px));
  max-height: min(320px, calc(100vh - 16px));
  overflow-x: hidden;
  overflow-y: scroll !important;
  box-sizing: border-box;
  border: var(--kp-panel-border, 1px solid #111);
  border-radius: var(--kp-radius-panel, 3px);
  background: var(--kp-panel-bg, #232323);
  box-shadow: var(--kp-panel-shadow, 0 8px 24px rgba(0,0,0,0.45));
  color: var(--kp-color-fg, #ddd);
  font-family: var(--kp-font-ui, Helvetica, Arial, sans-serif);
  font-size: 12px;
  line-height: 1.3;
  text-transform: none;
  letter-spacing: normal;
  scrollbar-color: #a8a8a8 #747474;
}
/* Blink: scrollbar-width uses overlay bars that only appear on scroll and
   suppress ::-webkit-scrollbar. Unset so the themed classic bar paints. */
@supports selector(::-webkit-scrollbar) {
  .kp-select-menu {
    scrollbar-width: unset;
    scrollbar-color: unset;
  }
}
.kp-select-menu::-webkit-scrollbar {
  -webkit-appearance: none;
  appearance: none;
  display: block !important;
  width: 10px !important;
  height: 10px !important;
  background: #747474;
}
.kp-select-menu::-webkit-scrollbar-corner {
  background: #747474;
}
.kp-select-menu::-webkit-scrollbar-track {
  background: #747474;
  border-left: 1px solid rgba(0, 0, 0, 0.3);
  border-top: 1px solid rgba(0, 0, 0, 0.3);
}
.kp-select-menu::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, #c0c0c0 0%, #a8a8a8 45%, #8d8d8d 100%);
  border: 1px solid #4a4a4a;
  border-radius: 2px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.42);
  min-height: 28px;
  min-width: 28px;
}
.kp-select-menu::-webkit-scrollbar-thumb:hover {
  background: linear-gradient(180deg, #cecece 0%, #b5b5b5 45%, #999 100%);
}
.kp-select-menu::-webkit-scrollbar-thumb:active {
  background: linear-gradient(180deg, #b5b5b5 0%, #8d8d8d 100%);
  border-color: #3d3d3d;
}
.kp-select-menu[data-kp-select-fallback="true"][hidden] {
  display: none !important;
}
.kp-select-group {
  padding: 6px 10px 4px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--kp-color-fg-mute, #777);
  pointer-events: none;
  user-select: none;
}
.kp-select-separator {
  height: 1px;
  margin: 4px 8px;
  background: var(--kp-color-field-edge, #0a0a0a);
  border: 0;
  pointer-events: none;
}
.kp-select-item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  margin: 0;
  padding: 5px 10px;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  font-weight: 400;
  text-align: left;
  text-transform: none;
  letter-spacing: normal;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  box-sizing: border-box;
  outline: none;
}
.kp-select-item:hover,
.kp-select-item.is-active {
  background: var(--kp-color-hover, rgba(255,255,255,0.08));
  outline: 1px solid var(--kp-color-focus-ring, var(--kp-color-accent, #4a90c8));
  outline-offset: -1px;
}
.kp-select-item[aria-selected="true"] {
  background: var(--kp-color-selected, rgba(74, 144, 200, 0.28));
  color: var(--kp-color-selected-text, var(--kp-color-fg, #ddd));
}
.kp-select-item .kp-titlebar-kbd {
  margin-left: auto;
  flex-shrink: 0;
}
`.trim();
  }
  function getCutCornerCss() {
    return `
.kp-chrome-window {
  overflow: hidden;
}
.kp-chrome-window:not([data-kp-corner="cut"]) {
  border-radius: var(--kp-radius-panel, 3px);
}
/* Baseline (presentation): proven clip-path chamfer */
[data-kp-corner="cut"],
:host([data-kp-corner="cut"]),
.kp-chrome-window[data-kp-corner="cut"] {
  clip-path: polygon(
    var(--kp-cut-size, 8px) 0,
    calc(100% - var(--kp-cut-size, 8px)) 0,
    100% var(--kp-cut-size, 8px),
    100% calc(100% - var(--kp-cut-size, 8px)),
    calc(100% - var(--kp-cut-size, 8px)) 100%,
    var(--kp-cut-size, 8px) 100%,
    0 calc(100% - var(--kp-cut-size, 8px)),
    0 var(--kp-cut-size, 8px)
  );
  border-radius: 0;
}
/* Upgrade: native chamfer keeps stroke + shadow on the cut edge */
@supports (corner-shape: bevel) {
  [data-kp-corner="cut"],
  :host([data-kp-corner="cut"]),
  .kp-chrome-window[data-kp-corner="cut"] {
    clip-path: none !important;
    border-radius: var(--kp-cut-size, 8px) !important;
    corner-shape: bevel;
  }
}
`.trim();
  }
  function mergeTheme(base, overrides) {
    if (!overrides || typeof overrides !== "object") return base;
    const out = { ...base };
    for (const [k, v] of Object.entries(overrides)) {
      if (v && typeof v === "object" && !Array.isArray(v) && base[k] && typeof base[k] === "object" && !Array.isArray(base[k])) {
        out[k] = mergeTheme(base[k], v);
      } else if (v !== void 0) {
        out[k] = v;
      }
    }
    return out;
  }
  function normalizeThemeId(raw) {
    const id = typeof raw === "string" ? raw.trim() : "";
    return THEME_IDS.includes(id) ? id : DEFAULT_THEME_ID;
  }
  var DEFAULT_THEME_ID, THEME_IDS, THEME_META, PRO_SANS, PRO_MONO, TYPE_ROLES, KEY_CLIP_NONE, KEY_SHADE_BEVEL;
  var init_schema = __esm({
    "themes/schema.js"() {
      DEFAULT_THEME_ID = "dark-pro";
      THEME_IDS = Object.freeze([
        "dark-pro",
        "gray-metal-pro",
        "gx-er"
      ]);
      THEME_META = Object.freeze({
        "dark-pro": { name: "Dark Pro" },
        "gray-metal-pro": { name: "Gray Metal Pro" },
        "gx-er": { name: "GX-er" }
      });
      PRO_SANS = "Helvetica, Arial, sans-serif";
      PRO_MONO = 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace';
      TYPE_ROLES = Object.freeze([
        "display",
        "heading",
        "subhead",
        "body",
        "ui",
        "kbd",
        "mono",
        "caption"
      ]);
      KEY_CLIP_NONE = "none";
      KEY_SHADE_BEVEL = "linear-gradient(180deg, rgba(255, 255, 255, 0.07) 0%, rgba(255, 255, 255, 0.02) 18%, transparent 42%)";
    }
  });

  // src/config/search-engines.js
  function normalizeSearchEngineId(raw) {
    if (raw === "google" || raw === "duckduckgo" || raw === "brave") return raw;
    return DEFAULT_SEARCH_ENGINE_ID;
  }
  function getSearchEngineMeta(engine) {
    const id = normalizeSearchEngineId(engine);
    return SEARCH_ENGINE_META[id] || SEARCH_ENGINE_META[DEFAULT_SEARCH_ENGINE_ID];
  }
  var SEARCH_ENGINE_META, DEFAULT_SEARCH_ENGINE_ID, LAUNCHER_SEARCH_SITES;
  var init_search_engines = __esm({
    "src/config/search-engines.js"() {
      SEARCH_ENGINE_META = Object.freeze({
        brave: Object.freeze({
          id: "brave",
          label: "Brave",
          homeUrl: "https://search.brave.com/",
          searchUrlPrefix: "https://search.brave.com/search?q="
        }),
        google: Object.freeze({
          id: "google",
          label: "Google",
          homeUrl: "https://www.google.com/",
          searchUrlPrefix: "https://www.google.com/search?q="
        }),
        duckduckgo: Object.freeze({
          id: "duckduckgo",
          label: "DuckDuckGo",
          homeUrl: "https://duckduckgo.com/",
          searchUrlPrefix: "https://duckduckgo.com/?q="
        })
      });
      DEFAULT_SEARCH_ENGINE_ID = /** @type {SearchEngineId} */
      "brave";
      LAUNCHER_SEARCH_SITES = Object.freeze([
        Object.freeze({ title: "Google", url: "https://google.com", isDefault: true }),
        Object.freeze({ title: "Bing", url: "https://bing.com", isDefault: true }),
        Object.freeze({ title: "DuckDuckGo", url: "https://duckduckgo.com", isDefault: true }),
        Object.freeze({ title: "Yahoo", url: "https://yahoo.com", isDefault: true }),
        Object.freeze({ title: "Brave Search", url: "https://search.brave.com", isDefault: true }),
        Object.freeze({ title: "Ecosia", url: "https://ecosia.org", isDefault: true }),
        Object.freeze({ title: "Startpage", url: "https://startpage.com", isDefault: true }),
        Object.freeze({ title: "Yandex", url: "https://yandex.com", isDefault: true })
      ]);
    }
  });

  // src/utils/storage.js
  function pickNewerStoredValue(syncVal, localVal) {
    const syncAt = syncVal && typeof syncVal === "object" ? Number(syncVal._updatedAt) : 0;
    const localAt = localVal && typeof localVal === "object" ? Number(localVal._updatedAt) : 0;
    const syncTs = Number.isFinite(syncAt) ? syncAt : 0;
    const localTs = Number.isFinite(localAt) ? localAt : 0;
    if (syncTs && localTs) return localTs >= syncTs ? localVal : syncVal;
    if (localTs && !syncTs) return localVal;
    if (syncTs && !localTs) return syncVal;
    return syncVal;
  }
  function resolveStoredAreas(syncVal, syncHas, localVal, localHas, defaultValue) {
    if (syncHas && localHas) return pickNewerStoredValue(syncVal, localVal);
    if (syncHas) return syncVal;
    if (localHas) return localVal;
    return defaultValue;
  }
  async function storageGetValue(key, defaultValue = void 0) {
    if (!key || typeof key !== "string") return defaultValue;
    let syncVal = void 0;
    let syncHas = false;
    try {
      if (chrome?.storage?.sync?.get) {
        const syncResult = await chrome.storage.sync.get([key]);
        if (syncResult && Object.prototype.hasOwnProperty.call(syncResult, key) && syncResult[key] !== void 0) {
          syncHas = true;
          syncVal = /** @type {T} */
          syncResult[key];
        }
      }
    } catch {
    }
    let localVal = void 0;
    let localHas = false;
    try {
      if (chrome?.storage?.local?.get) {
        const localResult = await chrome.storage.local.get([key]);
        if (localResult && Object.prototype.hasOwnProperty.call(localResult, key) && localResult[key] !== void 0) {
          localHas = true;
          localVal = /** @type {T} */
          localResult[key];
        }
      }
    } catch {
    }
    return (
      /** @type {T} */
      resolveStoredAreas(syncVal, syncHas, localVal, localHas, defaultValue)
    );
  }
  async function storageSetValue(key, value, opts = {}) {
    if (!key || typeof key !== "string") return false;
    const payload = { [key]: value };
    if (opts.includeTimestamp) {
      payload.timestamp = Date.now();
    }
    let wroteSync = false;
    try {
      if (chrome?.storage?.sync?.set) {
        await chrome.storage.sync.set(payload);
        wroteSync = true;
        if (!opts.dualWrite) return true;
      }
    } catch {
    }
    try {
      if (chrome?.storage?.local?.set) {
        await chrome.storage.local.set(payload);
        return true;
      }
    } catch {
    }
    return wroteSync;
  }
  async function storageSetObject(obj) {
    if (!obj || typeof obj !== "object") return false;
    try {
      if (chrome?.storage?.sync?.set) {
        await chrome.storage.sync.set(obj);
        return true;
      }
    } catch {
    }
    try {
      if (chrome?.storage?.local?.set) {
        await chrome.storage.local.set(obj);
        return true;
      }
    } catch {
    }
    return false;
  }
  var init_storage = __esm({
    "src/utils/storage.js"() {
    }
  });

  // src/utils/platform.js
  function isMacPlatform() {
    try {
      const uaPlatform = navigator.userAgentData?.platform;
      if (typeof uaPlatform === "string" && uaPlatform) {
        return uaPlatform === "macOS";
      }
    } catch {
    }
    try {
      const plat = String(navigator.platform || "");
      const ua = String(navigator.userAgent || "");
      return /^Mac/i.test(plat) || /Mac OS X/i.test(ua);
    } catch {
    }
    return false;
  }
  var init_platform = __esm({
    "src/utils/platform.js"() {
    }
  });

  // src/modules/settings-manager.js
  var settings_manager_exports = {};
  __export(settings_manager_exports, {
    CLICK_EFFECT_IDS: () => CLICK_EFFECT_IDS,
    DEFAULT_SEARCH_ENGINE_ID: () => DEFAULT_SEARCH_ENGINE_ID,
    DEFAULT_SETTINGS: () => DEFAULT_SETTINGS,
    SEARCH_ENGINE_META: () => SEARCH_ENGINE_META,
    SETTINGS_STORAGE_KEY: () => SETTINGS_STORAGE_KEY,
    TEXT_FOCUS_STYLE_IDS: () => TEXT_FOCUS_STYLE_IDS,
    buildSearchUrl: () => buildSearchUrl,
    getEngineHomeUrl: () => getEngineHomeUrl,
    getSearchEngineMeta: () => getSearchEngineMeta,
    getSettings: () => getSettings,
    normalizeActionSettings: () => normalizeActionSettings,
    normalizeActionsLibraryTableExpanded: () => normalizeActionsLibraryTableExpanded,
    normalizeCursorMode: () => normalizeCursorMode,
    normalizeFocusColor: () => normalizeFocusColor,
    normalizePaintStrategy: () => normalizePaintStrategy,
    normalizeSearchEngine: () => normalizeSearchEngine,
    normalizeTextFocusStyle: () => normalizeTextFocusStyle,
    normalizeThemeOverrides: () => normalizeThemeOverrides,
    normalizeUiThemeId: () => normalizeUiThemeId,
    resetAllSettings: () => resetAllSettings,
    scrollBehaviorFromSpeed: () => scrollBehaviorFromSpeed,
    setSettings: () => setSettings
  });
  function normalizeSearchEngine(raw) {
    return normalizeSearchEngineId(raw);
  }
  function normalizeCursorMode(raw) {
    if (raw === CURSOR_MODE.NO_CUSTOM_CURSORS || raw === CURSOR_MODE.CUSTOM_CURSORS) return raw;
    return DEFAULT_SETTINGS.cursorMode;
  }
  function normalizeThemeOverrides(raw) {
    if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
    return raw;
  }
  function normalizeUiThemeId(raw) {
    return normalizeThemeId(raw);
  }
  function normalizeBoolean(raw, fallback) {
    if (raw === true || raw === false) return raw;
    if (raw === "true") return true;
    if (raw === "false") return false;
    return !!fallback;
  }
  function normalizeCurrentKeyboardLayoutId(raw) {
    const v = String(raw || "").trim();
    if (!v) return DEFAULT_SETTINGS.currentKeyboardLayoutId;
    if (v === "builtin") return "builtin";
    if (v.startsWith("user:") && v.length > "user:".length) return v;
    return DEFAULT_SETTINGS.currentKeyboardLayoutId;
  }
  function normalizeNumber(raw, fallback, min, max) {
    const n = typeof raw === "number" ? raw : typeof raw === "string" ? Number(raw) : NaN;
    const v = Number.isFinite(n) ? n : fallback;
    const clamped = Math.min(Math.max(v, min), max);
    return clamped;
  }
  function normalizeClickCursorType(raw) {
    if (raw === "crosshair" || raw === "native_arrow" || raw === "native_pointer") return raw;
    return DEFAULT_SETTINGS.clickMode.cursor.type;
  }
  function normalizeClickEffect(raw) {
    if (raw === "flash" || raw === "dash" || raw === "marquee" || raw === "scale" || raw === "none") {
      return raw;
    }
    return DEFAULT_SETTINGS.clickMode.clickEffect;
  }
  function normalizeTextCursorType(raw) {
    if (raw === "t_square" || raw === "crosshair") return raw;
    return DEFAULT_SETTINGS.textMode.cursorType;
  }
  function normalizeTextFocusStyle(raw) {
    if (raw === "left_edge" || raw === "background_tint") return raw;
    return DEFAULT_SETTINGS.textMode.focusStyle;
  }
  function normalizeFocusColor(raw) {
    if (raw === "blue" || raw === "green") return raw;
    return DEFAULT_SETTINGS.clickMode.focusColor;
  }
  function normalizePaintStrategy(raw) {
    if (raw === "auto" || raw === "BC") return raw;
    const upper = raw == null ? "" : String(raw).trim().toUpperCase();
    if (upper === "B->C" || upper === "B\u2192C" || upper === "AUTO_BC" || upper === "AUTO-BC" || upper === "AUTO B->C" || upper === "AUTO B\u2192C") {
      return "BC";
    }
    if (upper === "AUTO" || upper === "A->B->C" || upper === "A\u2192B\u2192C") {
      return "auto";
    }
    return DEFAULT_SETTINGS.clickMode.paintStrategy;
  }
  function normalizeClickMode(raw) {
    const stored = raw && typeof raw === "object" ? raw : {};
    const storedCursor = stored.cursor && typeof stored.cursor === "object" ? stored.cursor : {};
    return {
      cursor: {
        type: normalizeClickCursorType(storedCursor.type),
        lineWidth: normalizeNumber(
          storedCursor.lineWidth,
          DEFAULT_SETTINGS.clickMode.cursor.lineWidth,
          1,
          12
        ),
        sizePixels: normalizeNumber(
          storedCursor.sizePixels,
          DEFAULT_SETTINGS.clickMode.cursor.sizePixels,
          5,
          60
        ),
        gap: normalizeNumber(
          storedCursor.gap,
          DEFAULT_SETTINGS.clickMode.cursor.gap,
          0,
          20
        )
      },
      focusColor: normalizeFocusColor(stored.focusColor),
      overlayFillEnabled: normalizeBoolean(
        stored.overlayFillEnabled,
        DEFAULT_SETTINGS.clickMode.overlayFillEnabled
      ),
      overlayShadowEnabled: normalizeBoolean(
        stored.overlayShadowEnabled,
        DEFAULT_SETTINGS.clickMode.overlayShadowEnabled
      ),
      rectangleThickness: normalizeNumber(
        stored.rectangleThickness,
        DEFAULT_SETTINGS.clickMode.rectangleThickness,
        1,
        16
      ),
      clickEffect: normalizeClickEffect(stored.clickEffect),
      keyboardLinkHoverHints: normalizeBoolean(
        stored.keyboardLinkHoverHints,
        DEFAULT_SETTINGS.clickMode.keyboardLinkHoverHints
      ),
      paintStrategy: normalizePaintStrategy(stored.paintStrategy),
      paintBackendDebugDashes: normalizeBoolean(
        stored.paintBackendDebugDashes,
        DEFAULT_SETTINGS.clickMode.paintBackendDebugDashes
      ),
      focusPadding: normalizeNumber(
        stored.focusPadding,
        DEFAULT_SETTINGS.clickMode.focusPadding,
        0,
        16
      ),
      skipForParent: normalizeBoolean(
        stored.skipForParent,
        DEFAULT_SETTINGS.clickMode.skipForParent
      )
    };
  }
  function normalizeTextMode(raw) {
    const stored = raw && typeof raw === "object" ? raw : {};
    return {
      cursorType: normalizeTextCursorType(stored.cursorType),
      labelsEnabled: normalizeBoolean(stored.labelsEnabled, DEFAULT_SETTINGS.textMode.labelsEnabled),
      strokeThickness: normalizeNumber(
        stored.strokeThickness,
        DEFAULT_SETTINGS.textMode.strokeThickness,
        1,
        16
      ),
      focusStyle: normalizeTextFocusStyle(stored.focusStyle),
      leftEdgeWidth: normalizeNumber(
        stored.leftEdgeWidth,
        DEFAULT_SETTINGS.textMode.leftEdgeWidth,
        1,
        24
      )
    };
  }
  function normalizeScrollSpeed(raw) {
    if (raw === "smooth" || raw === "instant") return raw;
    if (raw === "auto") return "instant";
    return DEFAULT_SETTINGS.scroll.speed;
  }
  function normalizeScroll(raw) {
    const stored = raw && typeof raw === "object" ? raw : {};
    const middleClickDefault = DEFAULT_SETTINGS.scroll.middleClickScrollLine;
    return {
      halfPagePx: normalizeNumber(
        stored.halfPagePx,
        DEFAULT_SETTINGS.scroll.halfPagePx,
        50,
        2e3
      ),
      speed: normalizeScrollSpeed(stored.speed),
      // Missing key → platform default (Mac on, others off). Explicit boolean is honored on any OS.
      middleClickScrollLine: normalizeBoolean(stored.middleClickScrollLine, middleClickDefault),
      linePreferPortraitTargets: normalizeBoolean(
        stored.linePreferPortraitTargets,
        DEFAULT_SETTINGS.scroll.linePreferPortraitTargets
      )
    };
  }
  function normalizeControlStrip(raw) {
    const stored = raw && typeof raw === "object" ? raw : {};
    return {
      visible: normalizeBoolean(stored.visible, DEFAULT_SETTINGS.controlStrip.visible),
      collapsed: normalizeBoolean(stored.collapsed, DEFAULT_SETTINGS.controlStrip.collapsed)
    };
  }
  function normalizePanelPositionEntry(raw, fallback) {
    const fb = fallback && typeof fallback === "object" ? fallback : {};
    if (!raw || typeof raw !== "object") {
      return {
        left: Number.isFinite(fb.left) ? fb.left : void 0,
        top: Number.isFinite(fb.top) ? fb.top : void 0,
        anchor: typeof fb.anchor === "string" ? fb.anchor : fb.anchor === null ? null : void 0
      };
    }
    const out = {};
    const left = typeof raw.left === "number" ? raw.left : typeof raw.left === "string" ? Number(raw.left) : NaN;
    const top = typeof raw.top === "number" ? raw.top : typeof raw.top === "string" ? Number(raw.top) : NaN;
    const width = typeof raw.width === "number" ? raw.width : typeof raw.width === "string" ? Number(raw.width) : NaN;
    const height = typeof raw.height === "number" ? raw.height : typeof raw.height === "string" ? Number(raw.height) : NaN;
    if (Number.isFinite(left)) out.left = left;
    if (Number.isFinite(top)) out.top = top;
    if (Number.isFinite(width) && width > 0) out.width = width;
    if (Number.isFinite(height) && height > 0) out.height = height;
    if (raw.anchor === null) {
      out.anchor = null;
    } else if (typeof raw.anchor === "string" && PANEL_ANCHOR_IDS.has(raw.anchor.trim())) {
      out.anchor = raw.anchor.trim();
    } else if (typeof fb.anchor === "string" && !Number.isFinite(left) && !Number.isFinite(top)) {
      out.anchor = fb.anchor;
    }
    if (out.left === void 0 && out.top === void 0 && out.anchor === void 0) {
      return {
        left: Number.isFinite(fb.left) ? fb.left : void 0,
        top: Number.isFinite(fb.top) ? fb.top : void 0,
        anchor: typeof fb.anchor === "string" ? fb.anchor : fb.anchor === null ? null : void 0
      };
    }
    return out;
  }
  function normalizePanelPositions(raw) {
    const stored = raw && typeof raw === "object" ? raw : {};
    return {
      keyboardReference: normalizePanelPositionEntry(
        stored.keyboardReference,
        DEFAULT_SETTINGS.panelPositions.keyboardReference
      ),
      controlStrip: normalizePanelPositionEntry(
        stored.controlStrip,
        DEFAULT_SETTINGS.panelPositions.controlStrip
      ),
      keyboardLayoutConfig: normalizePanelPositionEntry(
        stored.keyboardLayoutConfig,
        DEFAULT_SETTINGS.panelPositions.keyboardLayoutConfig
      ),
      topSites: normalizePanelPositionEntry(
        stored.topSites,
        DEFAULT_SETTINGS.panelPositions.topSites
      )
    };
  }
  function normalizeStringIdList(raw, fallback) {
    const fb = Array.isArray(fallback) ? [...fallback] : [];
    if (!Array.isArray(raw)) return fb;
    const out = [];
    const seen = /* @__PURE__ */ new Set();
    for (const v of raw) {
      if (typeof v !== "string") continue;
      const id = v.trim();
      if (!id || seen.has(id)) continue;
      seen.add(id);
      out.push(id);
    }
    return out;
  }
  function normalizeActionsLibraryTableExpanded(raw) {
    if (!Array.isArray(raw)) {
      return [...DEFAULT_SETTINGS.actionsLibraryTableExpanded];
    }
    return normalizeStringIdList(raw, DEFAULT_SETTINGS.actionsLibraryTableExpanded);
  }
  function scrollBehaviorFromSpeed(speed) {
    return normalizeScrollSpeed(speed) === "instant" ? "auto" : "smooth";
  }
  function normalizeActionSettings(raw) {
    const defaults = DEFAULT_SETTINGS.actionSettings || {};
    const stored = raw && typeof raw === "object" ? raw : {};
    const out = {};
    const keys = /* @__PURE__ */ new Set([...Object.keys(defaults), ...Object.keys(stored)]);
    for (const actionId of keys) {
      const fb = defaults[actionId] && typeof defaults[actionId] === "object" ? defaults[actionId] : {};
      const entry = stored[actionId] && typeof stored[actionId] === "object" ? stored[actionId] : {};
      const mode = typeof entry.mode === "string" && entry.mode ? entry.mode : typeof fb.mode === "string" ? fb.mode : void 0;
      const parameters = {
        ...fb.parameters && typeof fb.parameters === "object" ? fb.parameters : {},
        ...entry.parameters && typeof entry.parameters === "object" ? entry.parameters : {}
      };
      out[actionId] = { mode, parameters };
    }
    return out;
  }
  async function getSettings() {
    try {
      let stored = await storageGetValue(SETTINGS_STORAGE_KEY, null);
      if (!stored || typeof stored !== "object") stored = {};
      let familyId = normalizeKeyboardLayoutFamilyId(stored?.keyboardLayoutFamilyId);
      let handedness = normalizeKeyboardHandedness(stored?.keyboardHandedness);
      const hasNewFields = Object.prototype.hasOwnProperty.call(stored || {}, "keyboardLayoutFamilyId") || Object.prototype.hasOwnProperty.call(stored || {}, "keyboardHandedness");
      if (!hasNewFields) {
        const inferred = inferFamilyAndHandednessFromLayoutId(stored?.keyboardLayoutId);
        familyId = normalizeKeyboardLayoutFamilyId(inferred.familyId);
        handedness = normalizeKeyboardHandedness(inferred.handedness);
      }
      const resolvedLayoutId = resolveKeyboardLayoutId({ familyId, handedness });
      return {
        ...DEFAULT_SETTINGS,
        ...stored,
        themeId: normalizeThemeId(stored?.themeId),
        themeOverrides: normalizeThemeOverrides(stored?.themeOverrides),
        clickModeThemeId: typeof stored?.clickModeThemeId === "string" && stored.clickModeThemeId.trim() ? normalizeThemeId(stored.clickModeThemeId) : "",
        searchEngine: normalizeSearchEngine(stored?.searchEngine),
        cursorMode: normalizeCursorMode(stored?.cursorMode),
        keyboardLayoutFamilyId: familyId,
        keyboardHandedness: handedness,
        keyboardLayoutId: resolvedLayoutId,
        currentKeyboardLayoutId: normalizeCurrentKeyboardLayoutId(stored?.currentKeyboardLayoutId),
        keyboardReferenceKeyFeedback: normalizeBoolean(
          stored?.keyboardReferenceKeyFeedback,
          DEFAULT_SETTINGS.keyboardReferenceKeyFeedback
        ),
        keyboardReferenceShowNumberRow: normalizeBoolean(
          stored?.keyboardReferenceShowNumberRow,
          DEFAULT_SETTINGS.keyboardReferenceShowNumberRow
        ),
        keyboardReferenceCollapsed: normalizeBoolean(
          stored?.keyboardReferenceCollapsed,
          DEFAULT_SETTINGS.keyboardReferenceCollapsed
        ),
        topSitesPersistent: normalizeBoolean(
          stored?.topSitesPersistent,
          DEFAULT_SETTINGS.topSitesPersistent
        ),
        debugLogging: normalizeBoolean(
          stored?.debugLogging,
          DEFAULT_SETTINGS.debugLogging
        ),
        actionsLibraryTableExpanded: normalizeActionsLibraryTableExpanded(
          stored?.actionsLibraryTableExpanded
        ),
        actionsLibraryInstructionsExpanded: normalizeBoolean(
          stored?.actionsLibraryInstructionsExpanded,
          DEFAULT_SETTINGS.actionsLibraryInstructionsExpanded
        ),
        controlStrip: normalizeControlStrip(stored?.controlStrip),
        panelPositions: normalizePanelPositions(stored?.panelPositions),
        actionSettings: normalizeActionSettings(stored?.actionSettings),
        clickMode: normalizeClickMode(stored?.clickMode),
        textMode: normalizeTextMode(stored?.textMode),
        scroll: normalizeScroll(stored?.scroll)
      };
    } catch (_e) {
      return {
        ...DEFAULT_SETTINGS,
        controlStrip: { ...DEFAULT_SETTINGS.controlStrip },
        panelPositions: {
          keyboardReference: { ...DEFAULT_SETTINGS.panelPositions.keyboardReference },
          controlStrip: { ...DEFAULT_SETTINGS.panelPositions.controlStrip },
          keyboardLayoutConfig: { ...DEFAULT_SETTINGS.panelPositions.keyboardLayoutConfig },
          topSites: { ...DEFAULT_SETTINGS.panelPositions.topSites }
        },
        actionSettings: normalizeActionSettings(null),
        clickMode: { ...DEFAULT_SETTINGS.clickMode, cursor: { ...DEFAULT_SETTINGS.clickMode.cursor } },
        textMode: { ...DEFAULT_SETTINGS.textMode },
        scroll: { ...DEFAULT_SETTINGS.scroll },
        actionsLibraryTableExpanded: [...DEFAULT_SETTINGS.actionsLibraryTableExpanded],
        actionsLibraryInstructionsExpanded: DEFAULT_SETTINGS.actionsLibraryInstructionsExpanded,
        themeId: DEFAULT_THEME_ID,
        themeOverrides: {},
        clickModeThemeId: ""
      };
    }
  }
  async function setSettings(partial) {
    const current = await getSettings();
    const p = partial && typeof partial === "object" ? partial : {};
    const pPositions = p.panelPositions && typeof p.panelPositions === "object" ? p.panelPositions : null;
    const next = {
      ...current,
      ...p,
      controlStrip: {
        ...current.controlStrip,
        ...p.controlStrip && typeof p.controlStrip === "object" ? p.controlStrip : {}
      },
      panelPositions: {
        keyboardReference: {
          ...current.panelPositions.keyboardReference,
          ...pPositions?.keyboardReference && typeof pPositions.keyboardReference === "object" ? pPositions.keyboardReference : {}
        },
        controlStrip: {
          ...current.panelPositions.controlStrip,
          ...pPositions?.controlStrip && typeof pPositions.controlStrip === "object" ? pPositions.controlStrip : {}
        },
        keyboardLayoutConfig: {
          ...current.panelPositions.keyboardLayoutConfig || DEFAULT_SETTINGS.panelPositions.keyboardLayoutConfig,
          ...pPositions?.keyboardLayoutConfig && typeof pPositions.keyboardLayoutConfig === "object" ? pPositions.keyboardLayoutConfig : {}
        },
        topSites: {
          ...current.panelPositions.topSites || DEFAULT_SETTINGS.panelPositions.topSites,
          ...pPositions?.topSites && typeof pPositions.topSites === "object" ? pPositions.topSites : {}
        }
      },
      clickMode: {
        ...current.clickMode,
        ...p.clickMode && typeof p.clickMode === "object" ? p.clickMode : {},
        cursor: {
          ...current.clickMode.cursor,
          ...p.clickMode && typeof p.clickMode === "object" && p.clickMode.cursor && typeof p.clickMode.cursor === "object" ? p.clickMode.cursor : {}
        }
      },
      textMode: {
        ...current.textMode,
        ...p.textMode && typeof p.textMode === "object" ? p.textMode : {}
      },
      scroll: {
        ...current.scroll,
        ...p.scroll && typeof p.scroll === "object" ? p.scroll : {}
      },
      actionSettings: (() => {
        const merged = { ...current.actionSettings || {} };
        const patch = p.actionSettings && typeof p.actionSettings === "object" ? p.actionSettings : {};
        for (const [id, entry] of Object.entries(patch)) {
          const prev = merged[id] && typeof merged[id] === "object" ? merged[id] : {};
          const next2 = entry && typeof entry === "object" ? entry : {};
          merged[id] = {
            ...prev,
            ...next2,
            parameters: {
              ...prev.parameters && typeof prev.parameters === "object" ? prev.parameters : {},
              ...next2.parameters && typeof next2.parameters === "object" ? next2.parameters : {}
            }
          };
        }
        return normalizeActionSettings(merged);
      })()
    };
    next.searchEngine = normalizeSearchEngine(next.searchEngine);
    next.cursorMode = normalizeCursorMode(next.cursorMode);
    next.themeId = normalizeThemeId(next.themeId);
    next.themeOverrides = normalizeThemeOverrides(next.themeOverrides);
    next.clickModeThemeId = typeof next.clickModeThemeId === "string" && next.clickModeThemeId.trim() ? normalizeThemeId(next.clickModeThemeId) : "";
    const callerSetLayoutId = Object.prototype.hasOwnProperty.call(p, "keyboardLayoutId");
    if (callerSetLayoutId) {
      const inferred = inferFamilyAndHandednessFromLayoutId(p?.keyboardLayoutId);
      next.keyboardLayoutFamilyId = normalizeKeyboardLayoutFamilyId(inferred.familyId);
      next.keyboardHandedness = normalizeKeyboardHandedness(inferred.handedness);
      next.keyboardLayoutId = normalizeKeyboardLayoutId(p?.keyboardLayoutId);
    } else {
      next.keyboardLayoutFamilyId = normalizeKeyboardLayoutFamilyId(next.keyboardLayoutFamilyId);
      next.keyboardHandedness = normalizeKeyboardHandedness(next.keyboardHandedness);
      next.keyboardLayoutId = resolveKeyboardLayoutId({
        familyId: next.keyboardLayoutFamilyId,
        handedness: next.keyboardHandedness
      });
    }
    next.currentKeyboardLayoutId = normalizeCurrentKeyboardLayoutId(next.currentKeyboardLayoutId);
    next.keyboardReferenceKeyFeedback = normalizeBoolean(
      next.keyboardReferenceKeyFeedback,
      DEFAULT_SETTINGS.keyboardReferenceKeyFeedback
    );
    next.keyboardReferenceShowNumberRow = normalizeBoolean(
      next.keyboardReferenceShowNumberRow,
      DEFAULT_SETTINGS.keyboardReferenceShowNumberRow
    );
    next.keyboardReferenceCollapsed = normalizeBoolean(
      next.keyboardReferenceCollapsed,
      DEFAULT_SETTINGS.keyboardReferenceCollapsed
    );
    next.topSitesPersistent = normalizeBoolean(
      next.topSitesPersistent,
      DEFAULT_SETTINGS.topSitesPersistent
    );
    next.debugLogging = normalizeBoolean(
      next.debugLogging,
      DEFAULT_SETTINGS.debugLogging
    );
    next.actionsLibraryTableExpanded = normalizeActionsLibraryTableExpanded(
      next.actionsLibraryTableExpanded
    );
    next.actionsLibraryInstructionsExpanded = normalizeBoolean(
      next.actionsLibraryInstructionsExpanded,
      DEFAULT_SETTINGS.actionsLibraryInstructionsExpanded
    );
    next.controlStrip = normalizeControlStrip(next.controlStrip);
    next.panelPositions = normalizePanelPositions(next.panelPositions);
    next.actionSettings = normalizeActionSettings(next.actionSettings);
    next.clickMode = normalizeClickMode(next.clickMode);
    next.textMode = normalizeTextMode(next.textMode);
    next.scroll = normalizeScroll(next.scroll);
    next._updatedAt = Date.now();
    await storageSetValue(SETTINGS_STORAGE_KEY, next, { dualWrite: true });
    return next;
  }
  async function resetAllSettings() {
    const next = {
      ...DEFAULT_SETTINGS,
      themeOverrides: {},
      controlStrip: { ...DEFAULT_SETTINGS.controlStrip },
      panelPositions: {
        keyboardReference: { ...DEFAULT_SETTINGS.panelPositions.keyboardReference },
        controlStrip: { ...DEFAULT_SETTINGS.panelPositions.controlStrip },
        keyboardLayoutConfig: { ...DEFAULT_SETTINGS.panelPositions.keyboardLayoutConfig },
        topSites: { ...DEFAULT_SETTINGS.panelPositions.topSites }
      },
      actionSettings: normalizeActionSettings(null),
      clickMode: {
        ...DEFAULT_SETTINGS.clickMode,
        cursor: { ...DEFAULT_SETTINGS.clickMode.cursor }
      },
      textMode: { ...DEFAULT_SETTINGS.textMode },
      scroll: { ...DEFAULT_SETTINGS.scroll },
      actionsLibraryTableExpanded: [...DEFAULT_SETTINGS.actionsLibraryTableExpanded],
      actionsLibraryInstructionsExpanded: DEFAULT_SETTINGS.actionsLibraryInstructionsExpanded,
      _updatedAt: Date.now()
    };
    await storageSetValue(SETTINGS_STORAGE_KEY, next, { dualWrite: true });
    return getSettings();
  }
  function buildSearchUrl(engine, query) {
    const meta = getSearchEngineMeta(engine);
    const q = typeof query === "string" ? query : "";
    return `${meta.searchUrlPrefix}${encodeURIComponent(q)}`;
  }
  function getEngineHomeUrl(engine) {
    return getSearchEngineMeta(engine).homeUrl;
  }
  var SETTINGS_STORAGE_KEY, TEXT_FOCUS_STYLE_IDS, CLICK_EFFECT_IDS, DEFAULT_SETTINGS, PANEL_ANCHOR_IDS;
  var init_settings_manager = __esm({
    "src/modules/settings-manager.js"() {
      init_constants();
      init_schema();
      init_keyboard_layouts();
      init_search_engines();
      init_storage();
      init_platform();
      SETTINGS_STORAGE_KEY = "kp_settings_v1";
      TEXT_FOCUS_STYLE_IDS = Object.freeze(
        /** @type {const} */
        [
          "left_edge",
          "background_tint"
        ]
      );
      CLICK_EFFECT_IDS = Object.freeze(
        /** @type {const} */
        [
          "flash",
          "dash",
          "marquee",
          "scale",
          "none"
        ]
      );
      DEFAULT_SETTINGS = Object.freeze({
        themeId: DEFAULT_THEME_ID,
        themeOverrides: Object.freeze({}),
        // Last theme whose clickDefaults were written into clickMode/cursorMode.
        // Empty means never synced (adopt the active theme's click defaults once).
        clickModeThemeId: "",
        searchEngine: DEFAULT_SEARCH_ENGINE_ID,
        cursorMode: CURSOR_MODE.NO_CUSTOM_CURSORS,
        // New model:
        // - keyboardLayoutFamilyId + keyboardHandedness are the user-facing selection.
        // - keyboardLayoutId is the resolved concrete implementation (kept for back-compat + early-inject).
        keyboardLayoutFamilyId: DEFAULT_KEYBOARD_LAYOUT_FAMILY_ID,
        keyboardHandedness: DEFAULT_KEYBOARD_HANDEDNESS,
        keyboardLayoutId: DEFAULT_KEYBOARD_LAYOUT_ID,
        // Active layout selection for runtime + keyboard reference:
        // - 'builtin' uses the current built-in family + handedness selection.
        // - 'user:<layoutId>' uses a stored user layout (created/duplicated in Alt+C).
        currentKeyboardLayoutId: "builtin",
        // When true, the floating keyboard reference panel highlights keys on keydown/keyup.
        keyboardReferenceKeyFeedback: true,
        // When true, the floating keyboard reference panel includes the number row (1–0).
        // Default is off to keep the panel compact.
        keyboardReferenceShowNumberRow: false,
        // When true, the floating keyboard reference panel is titlebar-only (body hidden).
        keyboardReferenceCollapsed: false,
        // When true, Top Sites remounts on each page while left open (Keyboard Reference-style).
        topSitesPersistent: false,
        // Verbose console.log / debug / info in extension isolated worlds. Off in store builds.
        debugLogging: false,
        // Actions Library hierarchical table: expanded group keys (top-level open by default;
        // nested categories / parents start collapsed until the user opens them).
        actionsLibraryTableExpanded: Object.freeze(["functions", "macros", "macroKeys"]),
        // Actions Library placement instructions section (between titlebar and cards).
        actionsLibraryInstructionsExpanded: true,
        // Floating Control Strip (upper-left): visibility + collapsed (On/Off-only) state.
        controlStrip: Object.freeze({
          visible: true,
          collapsed: true
        }),
        // Dock / free positions for movable chrome (keyboard reference, control strip, …).
        // Anchors re-resolve on resize; free left/top reclamps inside the viewport margin.
        panelPositions: Object.freeze({
          keyboardReference: Object.freeze({ anchor: "bottom-left" }),
          controlStrip: Object.freeze({ anchor: "top-left" }),
          keyboardLayoutConfig: Object.freeze({ anchor: "middle-right" }),
          // Empty: first open stays viewport-centered until the user moves/resizes.
          topSites: Object.freeze({})
        }),
        // Per-key action settings (Keyboard Reference mode switches / config params).
        actionSettings: Object.freeze({
          RECTANGLE_HIGHLIGHT: Object.freeze({
            mode: "element",
            parameters: Object.freeze({})
          })
        }),
        clickMode: Object.freeze({
          cursor: Object.freeze({
            type: "crosshair",
            // Cursor SVG stroke width. Slider range: 1–12.
            lineWidth: 4,
            // Cursor size in pixels. Default is half of previous (was ~30px, now 15px).
            sizePixels: 10,
            // Gap between center and crosshair bars in pixels. 0 = intersecting lines, >0 = separate bars.
            gap: 6
          }),
          // Hover focus ring color (DOM-hover mode default is blue).
          focusColor: "blue",
          // When true, the focus rectangle can include a translucent fill (where applicable).
          overlayFillEnabled: false,
          // When true, draw a soft outer glow/shadow on the focus rectangle.
          overlayShadowEnabled: false,
          // Focus rectangle border thickness in px.
          rectangleThickness: 3,
          // F-key activation feedback on link-style targets (flash is the default).
          clickEffect: "flash",
          // When true, hovering a link glows matching green keys on the Keyboard Reference.
          // Off by default (opt-in via Settings → Click Mode).
          keyboardLinkHoverHints: false,
          // Default skip DOM outline (A); use in-target (B) then body-fixed (C).
          // Matches Shadow Root Debug “Auto B→C”.
          paintStrategy: "BC",
          // When true, dash A/B/C hover rings differently for paint-backend recognition.
          // Off by default (opt-in via Settings → Click Mode → Advanced).
          paintBackendDebugDashes: false,
          // Outward ring padding (px). Strategy A uses this as preferred outline-offset;
          // B/C expand their boxes by the same amount (A historically ~2px; B→C was 0).
          focusPadding: 2,
          // When a nested control shares the parent's destination (same URL), hover
          // the parent card instead. Different-destination children keep their own ring.
          skipForParent: true
        }),
        textMode: Object.freeze({
          cursorType: "t_square",
          // When true, show both labels: "Active text field" + "Press ESC to close".
          labelsEnabled: false,
          // Stroke thickness in px for orange text-mode rectangles.
          strokeThickness: 3,
          // How the focused text field is styled while in text mode.
          // left_edge: pulsating orange bar on the left inset edge (default).
          // background_tint: full-field orange wash (legacy).
          focusStyle: "left_edge",
          // Width of the left-edge pulse bar in px (when focusStyle is left_edge).
          leftEdgeWidth: 5
        }),
        scroll: Object.freeze({
          // C / V scroll distance in pixels (default = prior 400 × 1.25).
          halfPagePx: SCROLL.HALF_PAGE_PX,
          // Animation speed for keyboard scrolling: smooth (animated) or instant (jump).
          speed: SCROLL.BEHAVIOR === "smooth" ? "smooth" : "instant",
          // Middle mouse button → Scroll Line Function (empty page only). On by default on Mac.
          middleClickScrollLine: isMacPlatform(),
          // Scroll Line: skip horizontal-only landscape overflow (carousels).
          linePreferPortraitTargets: true
        })
      });
      PANEL_ANCHOR_IDS = /* @__PURE__ */ new Set([
        "top-left",
        "top-center",
        "top-right",
        "middle-left",
        "middle-right",
        "bottom-left",
        "bottom-center",
        "bottom-right"
      ]);
    }
  });

  // src/modules/state-manager.js
  init_constants();
  var StateManager = class {
    constructor() {
      this.state = {
        mode: MODES.NONE,
        lastMouse: { x: 0, y: 0 },
        focusEl: null,
        /**
         * Shared inspector hover target (Delete, Cols, future pick tools).
         * Only meaningful while mode === MODES.INSPECTOR.
         */
        inspectorEl: null,
        /** @type {string|null} INSPECTOR_KIND value while in inspector mode */
        inspectorKind: null,
        highlightEl: null,
        highlightStartPosition: null,
        currentSelection: null,
        isInitialized: false,
        popoverOpen: false,
        popoverUrl: null
      };
      this.subscribers = /* @__PURE__ */ new Set();
    }
    getState() {
      return { ...this.state };
    }
    setState(updates) {
      const prevState = { ...this.state };
      this.state = { ...this.state, ...updates };
      this.notifySubscribers(prevState, this.state);
    }
    subscribe(callback) {
      this.subscribers.add(callback);
      return () => {
        this.subscribers.delete(callback);
      };
    }
    notifySubscribers(prevState, newState) {
      for (const callback of this.subscribers) {
        try {
          callback(newState, prevState);
        } catch (error) {
          console.error("State subscriber error:", error);
        }
      }
    }
    // Convenience methods
    setMode(mode) {
      this.setState({ mode });
    }
    setMousePosition(x, y) {
      this.setState({ lastMouse: { x, y } });
    }
    setFocusElement(element) {
      this.setState({ focusEl: element });
    }
    setInspectorElement(element) {
      this.setState({ inspectorEl: element });
    }
    /**
     * Enter shared inspector pick mode for a tool kind.
     * @param {string} kind INSPECTOR_KIND value
     */
    enterInspector(kind) {
      this.setState({
        mode: MODES.INSPECTOR,
        inspectorKind: kind || null,
        inspectorEl: null,
        // Clear normal focus hover so green click chrome doesn't fight inspector outline
        focusEl: null
      });
    }
    /**
     * Exit inspector pick mode without clearing sticky page effects.
     */
    exitInspector() {
      this.setState({
        mode: MODES.NONE,
        inspectorKind: null,
        inspectorEl: null
      });
    }
    setHighlightElement(element) {
      this.setState({ highlightEl: element });
    }
    setHighlightStartPosition(position) {
      this.setState({ highlightStartPosition: position });
    }
    setCurrentSelection(selection) {
      this.setState({ currentSelection: selection });
    }
    setPopoverOpen(isOpen, url = null) {
      if (isOpen) {
        this.setState({
          mode: MODES.POPOVER,
          // Set mode when opening
          // Popovers are modal: stop tracking/clicking background page elements.
          // Clearing focus/inspector immediately also hides hover chrome until the
          // user moves the mouse over popover UI.
          focusEl: null,
          inspectorEl: null,
          inspectorKind: null,
          popoverOpen: isOpen,
          popoverUrl: url
        });
      } else {
        this.setState({
          mode: MODES.NONE,
          // Reset mode when closing
          popoverOpen: false,
          popoverUrl: null
        });
      }
    }
    clearElements() {
      this.setState({
        focusEl: null,
        inspectorEl: null,
        highlightEl: null,
        highlightStartPosition: null,
        currentSelection: null
      });
    }
    isInspectorMode() {
      return this.state.mode === MODES.INSPECTOR;
    }
    /**
     * @param {string} kind
     * @returns {boolean}
     */
    isInspectorKind(kind) {
      return this.state.mode === MODES.INSPECTOR && this.state.inspectorKind === kind;
    }
    /** @deprecated prefer isInspectorKind(INSPECTOR_KIND.DELETE) */
    isDeleteMode() {
      return this.isInspectorKind("delete") || this.state.mode === MODES.DELETE;
    }
    /** @deprecated prefer isInspectorKind(INSPECTOR_KIND.COLS) */
    isColsMode() {
      return this.isInspectorKind("cols") || this.state.mode === MODES.COLS;
    }
    isHighlightMode() {
      return this.state.mode === MODES.HIGHLIGHT;
    }
    isScrollLineMode() {
      return this.state.mode === MODES.SCROLL_LINE;
    }
    isTextFocusMode() {
      return this.state.mode === MODES.TEXT_FOCUS;
    }
    isPopoverMode() {
      return this.state.mode === MODES.POPOVER;
    }
    reset() {
      this.setState({
        mode: MODES.NONE,
        focusEl: null,
        inspectorEl: null,
        inspectorKind: null,
        highlightEl: null,
        highlightStartPosition: null,
        currentSelection: null,
        popoverOpen: false,
        popoverUrl: null
      });
    }
  };

  // src/utils/dom-context.js
  var TEXT_ENTRY_INPUT_TYPES = Object.freeze([
    "text",
    "search",
    "url",
    "email",
    "tel",
    "password",
    "number",
    "date",
    "datetime-local",
    "month",
    "week",
    "time"
  ]);
  var TEXT_ENTRY_TYPE_SET = new Set(TEXT_ENTRY_INPUT_TYPES);
  function getDeepActiveElement(root = document) {
    let active2 = null;
    try {
      active2 = root?.activeElement || null;
    } catch {
      active2 = null;
    }
    let guard = 0;
    while (active2 && guard++ < 12) {
      let next = null;
      try {
        next = active2.shadowRoot?.activeElement || null;
      } catch {
        next = null;
      }
      if (next) {
        active2 = next;
        continue;
      }
      let tag = "";
      try {
        tag = String(active2.tagName || "").toUpperCase();
      } catch {
        tag = "";
      }
      if (tag === "IFRAME" || tag === "FRAME") {
        try {
          const innerDoc = (
            /** @type {HTMLIFrameElement} */
            active2.contentDocument
          );
          const inner = innerDoc?.activeElement || null;
          if (inner && inner !== active2) {
            active2 = inner;
            continue;
          }
        } catch {
        }
      }
      break;
    }
    return active2;
  }
  function getComposedEventTarget(e) {
    if (!e) return null;
    try {
      const path = typeof e.composedPath === "function" ? e.composedPath() : null;
      if (path && path.length) {
        for (const node of path) {
          if (node && /** @type {any} */
          node.nodeType === 1) return node;
        }
      }
    } catch {
    }
    return e.target || null;
  }
  function isTypingContext(target, opts = {}) {
    if (!target) return false;
    let el2 = (
      /** @type {any} */
      target
    );
    try {
      if (el2.nodeType === 3) el2 = el2.parentElement;
    } catch {
    }
    if (!el2 || el2.nodeType !== 1) return false;
    const node = (
      /** @type {HTMLElement} */
      el2
    );
    try {
      if (node.isConnected === false) return false;
    } catch {
    }
    try {
      if (node.isContentEditable) return true;
    } catch {
    }
    try {
      let p = node.parentElement;
      let depth = 0;
      while (p && depth++ < 4) {
        if (p.isContentEditable) return true;
        p = p.parentElement;
      }
    } catch {
    }
    const tag = node.tagName?.toLowerCase?.() || "";
    if (tag === "textarea") {
      return !/** @type {HTMLTextAreaElement} */
      node.disabled;
    }
    if (opts.treatSelectAsTyping && tag === "select") {
      return !/** @type {HTMLSelectElement} */
      node.disabled;
    }
    if (tag !== "input") return false;
    const input = (
      /** @type {HTMLInputElement} */
      node
    );
    if (input.disabled || input.readOnly) return false;
    const type2 = String(input.type || "text").toLowerCase();
    return TEXT_ENTRY_TYPE_SET.has(type2);
  }
  function resolveTypingTarget(e, opts = {}) {
    const composed = getComposedEventTarget(e);
    if (isTypingContext(composed, opts)) return (
      /** @type {Element} */
      composed
    );
    const deep = getDeepActiveElement();
    if (deep && isTypingContext(deep, opts)) return deep;
    try {
      if (typeof document !== "undefined" && isTypingContext(document.activeElement, opts)) {
        return (
          /** @type {Element} */
          document.activeElement
        );
      }
    } catch {
    }
    return null;
  }
  function hasModifierKeys(e) {
    if (!e) return false;
    return !!(e.ctrlKey || e.metaKey || e.altKey || e.shiftKey);
  }
  var kpIsTypingContext = isTypingContext;
  var kpResolveTypingTarget = resolveTypingTarget;
  var kpGetDeepActiveElement = getDeepActiveElement;
  var kpGetComposedEventTarget = getComposedEventTarget;
  var kpHasModifierKeys = hasModifierKeys;

  // src/modules/event-manager.js
  var EventManager = class {
    constructor() {
      this.listeners = /* @__PURE__ */ new Map();
      this.isActive = false;
    }
    start() {
      if (this.isActive) return;
      this.addListener(document, "keydown", this.handleKeyDown.bind(this), { capture: true });
      this.addListener(document, "keyup", this.handleKeyUp.bind(this), { capture: true });
      const moveOpts = { capture: true, passive: true };
      if (typeof PointerEvent !== "undefined") {
        this.addListener(document, "pointermove", this.handleMouseMove.bind(this), moveOpts);
      } else {
        this.addListener(document, "mousemove", this.handleMouseMove.bind(this), moveOpts);
      }
      const downOpts = { capture: true };
      if (typeof PointerEvent !== "undefined") {
        this.addListener(document, "pointerdown", this.handlePointerDown.bind(this), downOpts);
      } else {
        this.addListener(document, "mousedown", this.handlePointerDown.bind(this), downOpts);
      }
      this.addListener(document, "auxclick", this.handleAuxClick.bind(this), downOpts);
      this.isActive = true;
    }
    stop() {
      if (!this.isActive) return;
      this.removeAllListeners();
      this.isActive = false;
    }
    cleanup() {
      this.stop();
      if (this.focusDetector) {
        this.focusDetector.stop();
      }
    }
    addListener(element, event, handler, options = {}) {
      const capture = !!(options && (options.capture || options === true));
      const key = `${element.constructor.name}-${event}-${capture ? "c" : "b"}`;
      if (this.listeners.has(key)) {
        this.removeListenerByKey(key);
      }
      element.addEventListener(event, handler, options);
      this.listeners.set(key, { element, event, handler, options, capture });
    }
    removeListenerByKey(key) {
      const listener = this.listeners.get(key);
      if (listener) {
        listener.element.removeEventListener(listener.event, listener.handler, listener.options);
        this.listeners.delete(key);
      }
    }
    removeListener(element, event) {
      for (const capture of [true, false]) {
        const key = `${element.constructor.name}-${event}-${capture ? "c" : "b"}`;
        this.removeListenerByKey(key);
      }
    }
    removeAllListeners() {
      for (const [_key, listener] of this.listeners) {
        listener.element.removeEventListener(listener.event, listener.handler, listener.options);
      }
      this.listeners.clear();
    }
    handleKeyDown(_e) {
    }
    handleKeyUp(_e) {
    }
    handleMouseMove(_e) {
    }
    handlePointerDown(_e) {
    }
    handleAuxClick(_e) {
    }
    handleScroll(_e) {
    }
    /**
     * @param {EventTarget|null|undefined} target
     * @returns {boolean}
     */
    isTypingContext(target) {
      return kpIsTypingContext(target);
    }
    /**
     * @returns {Element|null}
     */
    getDeepActiveElement() {
      return kpGetDeepActiveElement();
    }
    /**
     * @param {Event|null|undefined} e
     * @returns {EventTarget|null}
     */
    getComposedEventTarget(e) {
      return kpGetComposedEventTarget(e);
    }
    /**
     * @param {Event|null|undefined} e
     * @returns {Element|null}
     */
    resolveTypingTarget(e) {
      return kpResolveTypingTarget(e);
    }
    /**
     * @param {KeyboardEvent|null|undefined} e
     * @returns {boolean}
     */
    hasModifierKeys(e) {
      return kpHasModifierKeys(e);
    }
  };

  // src/modules/cursor.js
  init_constants();
  var CursorManager = class {
    constructor() {
      this.cursorEl = null;
      this.lastPosition = { x: 0, y: 0 };
      this.isStuck = false;
      this.stuckCheckInterval = null;
      this.forceUpdateCount = 0;
      this.currentMode = null;
      this.currentModeKey = null;
      this.lastOptions = {};
      this.uriCache = /* @__PURE__ */ new Map();
    }
    ensure() {
      if (this.cursorEl) return;
      try {
        const earlyApi = window.KEYPILOT_EARLY;
        if (earlyApi && !window.__KP_EARLY_HANDOFF_DONE) {
          window.__KP_EARLY_HANDOFF_DONE = true;
          try {
            const earlyPosition = typeof earlyApi.getPosition === "function" ? earlyApi.getPosition() : null;
            if (earlyPosition && typeof earlyPosition.x === "number" && typeof earlyPosition.y === "number") {
              this.lastPosition = earlyPosition;
            }
          } catch {
          }
          try {
            window.dispatchEvent(new CustomEvent("keypilot-main-loaded"));
          } catch {
          }
          if (window.KEYPILOT_DEBUG) {
            console.log("[KeyPilot] Took over from early injection, using CSS cursor");
          }
        }
      } catch {
      }
      this.cursorEl = { style: {} };
    }
    setMode(mode, options = {}) {
      if (!this.cursorEl) return;
      const {
        cursorType = null,
        crossHairQuadrantWidth = 15,
        gap = 0,
        strokeLineCap = "round",
        strokeWidth = 4,
        crossHairScalingFactor = 1,
        hasClickableElement = false
      } = options || {};
      const nextModeKey = `${mode}|${cursorType || ""}|${crossHairQuadrantWidth}|${gap}|${strokeLineCap}|${strokeWidth}|${crossHairScalingFactor}|${hasClickableElement ? 1 : 0}`;
      if (this.currentModeKey === nextModeKey) {
        return;
      }
      this.currentMode = mode;
      this.currentModeKey = nextModeKey;
      this.lastOptions = options && typeof options === "object" ? { ...options } : {};
      if (cursorType === "native_arrow") {
        document.documentElement.style.setProperty("--kpv2-cursor", "default");
        if (document.documentElement.style.cursor) document.documentElement.style.cursor = "";
        if (document.body.style.cursor) document.body.style.cursor = "";
        return;
      }
      if (cursorType === "native_pointer") {
        document.documentElement.style.setProperty("--kpv2-cursor", "pointer");
        if (document.documentElement.style.cursor) document.documentElement.style.cursor = "";
        if (document.body.style.cursor) document.body.style.cursor = "";
        return;
      }
      const cursorUri = this.getCursorDataUri(mode, options);
      const cursorValue = `url("${cursorUri}") 30 30, auto`;
      document.documentElement.style.setProperty("--kpv2-cursor", cursorValue);
      if (document.documentElement.style.cursor) document.documentElement.style.cursor = "";
      if (document.body.style.cursor) document.body.style.cursor = "";
    }
    updatePosition(x, y) {
      if (!this.cursorEl) return;
      this.lastPosition = { x, y };
    }
    getCurrentMode() {
      return this.currentMode || "none";
    }
    /**
     * Generate SVG data URI for cursor mode
     */
    getCursorDataUri(mode, options = {}) {
      const {
        cursorType = null,
        crossHairQuadrantWidth = 15,
        gap = 0,
        strokeLineCap = "round",
        strokeWidth = 4,
        crossHairScalingFactor = 1,
        hasClickableElement = false
      } = options;
      const scaledGap = gap * crossHairScalingFactor;
      const scaledWidth = crossHairQuadrantWidth * crossHairScalingFactor;
      const centerX = 30;
      const centerY = 30;
      let segmentStart, segmentEnd, segmentStart2, segmentEnd2;
      if (scaledGap === 0) {
        segmentStart = centerY - scaledWidth;
        segmentEnd = centerY;
        segmentStart2 = centerY;
        segmentEnd2 = centerY + scaledWidth;
      } else {
        segmentStart = centerY - scaledGap - scaledWidth;
        segmentEnd = centerY - scaledGap;
        segmentStart2 = centerY + scaledGap;
        segmentEnd2 = centerY + scaledGap + scaledWidth;
      }
      const cacheKey = `${mode}-${cursorType || ""}-${crossHairQuadrantWidth}-${gap}-${strokeLineCap}-${strokeWidth}-${crossHairScalingFactor}-${hasClickableElement ? 1 : 0}`;
      if (this.uriCache.has(cacheKey)) {
        return this.uriCache.get(cacheKey);
      }
      let svgContent = "";
      if (mode === "text_focus" && cursorType === "t_square") {
        const color4 = hasClickableElement ? COLORS.FOCUS_GREEN_BRIGHT : COLORS.ORANGE;
        const scale = crossHairScalingFactor || 1;
        const half = 14 * scale;
        const x = 30 - half;
        const y = 30 - half;
        const w = 2 * half;
        const h = 2 * half;
        const tHalf = half * 0.6;
        const tTopY = 30 - half * 0.45;
        const tBottomY = 30 + half * 0.7;
        svgContent = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" width="60" height="60">
        <rect x="${x}" y="${y}" width="${w}" height="${h}" fill="none" stroke="${color4}" stroke-width="${strokeWidth}" />
        <line x1="${30 - tHalf}" y1="${tTopY}" x2="${30 + tHalf}" y2="${tTopY}" stroke="${color4}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="30" y1="${tTopY}" x2="30" y2="${tBottomY}" stroke="${color4}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
      </svg>`;
      } else if (mode === "text_focus") {
        const color4 = hasClickableElement ? COLORS.FOCUS_GREEN_BRIGHT : COLORS.ORANGE;
        svgContent = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" width="60" height="60">
        <line x1="30" y1="${segmentStart}" x2="30" y2="${segmentEnd}" stroke="${color4}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="30" y1="${segmentStart2}" x2="30" y2="${segmentEnd2}" stroke="${color4}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="${segmentStart}" y1="30" x2="${segmentEnd}" y2="30" stroke="${color4}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="${segmentStart2}" y1="30" x2="${segmentEnd2}" y2="30" stroke="${color4}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
      </svg>`;
      } else if (mode === "delete") {
        svgContent = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" width="60" height="60">
        <line x1="12" y1="12" x2="48" y2="48" stroke="${COLORS.DELETE_RED}" stroke-width="5" stroke-linecap="round"/>
        <line x1="48" y1="12" x2="12" y2="48" stroke="${COLORS.DELETE_RED}" stroke-width="5" stroke-linecap="round"/>
      </svg>`;
      } else if (mode === "cols") {
        const color4 = COLORS.COLS_PURPLE || "rgba(156,39,176,0.95)";
        svgContent = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" width="60" height="60">
        <rect x="10" y="12" width="16" height="36" rx="1" fill="none" stroke="${color4}" stroke-width="3"/>
        <rect x="34" y="12" width="16" height="36" rx="1" fill="none" stroke="${color4}" stroke-width="3"/>
        <line x1="13" y1="20" x2="23" y2="20" stroke="${color4}" stroke-width="2" stroke-linecap="round"/>
        <line x1="13" y1="28" x2="23" y2="28" stroke="${color4}" stroke-width="2" stroke-linecap="round"/>
        <line x1="13" y1="36" x2="23" y2="36" stroke="${color4}" stroke-width="2" stroke-linecap="round"/>
        <line x1="37" y1="20" x2="47" y2="20" stroke="${color4}" stroke-width="2" stroke-linecap="round"/>
        <line x1="37" y1="28" x2="47" y2="28" stroke="${color4}" stroke-width="2" stroke-linecap="round"/>
        <line x1="37" y1="36" x2="47" y2="36" stroke="${color4}" stroke-width="2" stroke-linecap="round"/>
      </svg>`;
      } else if (mode === "highlight") {
        const color4 = COLORS.HIGHLIGHT_BLUE;
        svgContent = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" width="60" height="60">
        <line x1="30" y1="${segmentStart}" x2="30" y2="${segmentEnd}" stroke="${color4}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="30" y1="${segmentStart2}" x2="30" y2="${segmentEnd2}" stroke="${color4}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="${segmentStart}" y1="30" x2="${segmentEnd}" y2="30" stroke="${color4}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="${segmentStart2}" y1="30" x2="${segmentEnd2}" y2="30" stroke="${color4}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
      </svg>`;
      } else {
        const color4 = COLORS.FOCUS_GREEN_BRIGHT;
        svgContent = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" width="60" height="60">
        <line x1="30" y1="${segmentStart}" x2="30" y2="${segmentEnd}" stroke="${color4}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="30" y1="${segmentStart2}" x2="30" y2="${segmentEnd2}" stroke="${color4}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="${segmentStart}" y1="30" x2="${segmentEnd}" y2="30" stroke="${color4}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="${segmentStart2}" y1="30" x2="${segmentEnd2}" y2="30" stroke="${color4}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
      </svg>`;
      }
      const encoded = encodeURIComponent(svgContent);
      const uri = `data:image/svg+xml,${encoded}`;
      this.uriCache.set(cacheKey, uri);
      return uri;
    }
    hide() {
      if (this.cursorEl) {
        document.documentElement.style.setProperty("--kpv2-cursor", "default");
      }
    }
    show() {
      if (this.cursorEl) {
        const currentMode = this.getCurrentMode();
        this.currentMode = null;
        this.currentModeKey = null;
        this.setMode(currentMode, this.lastOptions || {});
      }
    }
    cleanup() {
      if (this.stuckCheckInterval) {
        clearInterval(this.stuckCheckInterval);
        this.stuckCheckInterval = null;
      }
      document.documentElement.style.cursor = "";
      document.body.style.cursor = "";
      document.documentElement.style.removeProperty("--kpv2-cursor");
      this.cursorEl = null;
      this.currentMode = null;
      this.currentModeKey = null;
    }
    createElement(tag, props = {}, ...children) {
      const node = document.createElement(tag);
      for (const [k, v] of Object.entries(props)) {
        if (v == null) continue;
        if (k === "className") node.className = v;
        else if (k === "text") node.textContent = v;
        else node.setAttribute(k, v);
      }
      for (const c of children) {
        if (c == null) continue;
        node.appendChild(typeof c === "string" ? document.createTextNode(c) : c);
      }
      return node;
    }
  };

  // src/modules/element-detector.js
  init_constants();

  // src/utils/element-from-point.js
  function deepElementFromPoint(x, y, doc = document) {
    let el2 = null;
    try {
      el2 = doc.elementFromPoint(x, y);
    } catch {
      return null;
    }
    let guard = 0;
    while (el2 && el2.shadowRoot && guard++ < 10) {
      let nested = null;
      try {
        nested = el2.shadowRoot.elementFromPoint(x, y);
      } catch {
        break;
      }
      if (!nested || nested === el2) {
        nested = deepestShadowElementAtPoint(el2.shadowRoot, x, y);
      }
      if (!nested || nested === el2) break;
      el2 = nested;
    }
    return el2 || null;
  }
  function deepestShadowElementAtPoint(root, x, y) {
    if (!root || typeof root.querySelectorAll !== "function") return null;
    let nodes;
    try {
      nodes = root.querySelectorAll("*");
    } catch {
      return null;
    }
    let best = null;
    let bestArea = Infinity;
    for (let i = 0; i < nodes.length; i++) {
      const n = nodes[i];
      if (!n || n.nodeType !== 1) continue;
      let r;
      try {
        r = n.getBoundingClientRect();
      } catch {
        continue;
      }
      if (!(r.width > 0) || !(r.height > 0)) continue;
      if (x < r.left || x > r.right || y < r.top || y > r.bottom) continue;
      const area = r.width * r.height;
      if (area < bestArea) {
        bestArea = area;
        best = n;
      }
    }
    return best;
  }

  // src/utils/resolve-hovered-link.js
  function composedParent(node) {
    if (!node || node.nodeType !== 1) return null;
    const el2 = (
      /** @type {Element} */
      node
    );
    if (el2.parentElement) return el2.parentElement;
    try {
      const root = typeof el2.getRootNode === "function" ? el2.getRootNode() : null;
      if (root && typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot) {
        return root.host || null;
      }
    } catch {
    }
    return null;
  }
  function pathOf(href) {
    const raw = String(href || "").trim();
    if (!raw) return "";
    try {
      if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(raw)) {
        return new URL(raw, location.href).pathname || "";
      }
    } catch {
    }
    const cut = raw.split(/[?#]/)[0];
    return cut.startsWith("/") ? cut : `/${cut}`;
  }
  function permalinkScore(a) {
    const raw = (a.getAttribute("href") || "").trim();
    if (!raw || raw === "#" || raw.toLowerCase().startsWith("javascript:")) return 0;
    let abs = "";
    try {
      abs = String(
        /** @type {HTMLAnchorElement} */
        a.href || ""
      );
    } catch {
      abs = raw;
    }
    const path = pathOf(abs || raw).toLowerCase();
    if (!path || path === "/") return 0;
    if (/\/status\/\d+\/(analytics|photo|video|quotes?|likes?|retweets?|media)\b/.test(path) || /\/(analytics|photo|video)\/\d+/.test(path) || /\/i\/(web|flow|premium|bookmarks)\b/.test(path) || path.startsWith("/intent/")) {
      return 0;
    }
    let score = 0;
    try {
      if (typeof a.querySelector === "function" && a.querySelector("time")) score += 50;
    } catch {
    }
    try {
      const rel = (a.getAttribute("rel") || "").toLowerCase();
      if (rel.includes("bookmark")) score += 40;
    } catch {
    }
    if (/\/status\/\d+\/?$/.test(path)) score += 40;
    else if (/\/status\/\d+/.test(path)) score += 20;
    if (/\/(posts|post|notes|note|statuses|activity|objects|story|stories|entry|entries|comments)\//.test(path)) {
      score += 30;
    }
    try {
      if (typeof a.querySelector === "function" && a.querySelector("h1, h2, h3")) score += 25;
    } catch {
    }
    const segs = path.replace(/\/+$/, "").split("/").filter(Boolean);
    if (segs.length >= 3) score += 8;
    else if (segs.length === 2) score += 4;
    else if (segs.length === 1) score += 1;
    return score;
  }
  function resolveDescendantPermalink(host) {
    if (!host || host.nodeType !== 1) return null;
    const anchors = [];
    const seen = /* @__PURE__ */ new Set();
    const collect = (root, depth) => {
      if (!root || depth > 4) return;
      try {
        if (typeof root.querySelectorAll === "function") {
          const list = root.querySelectorAll("a[href]");
          for (let i = 0; i < list.length; i++) {
            const a = list[i];
            if (seen.has(a)) continue;
            seen.add(a);
            anchors.push(a);
          }
        }
      } catch {
      }
      try {
        const all = root.querySelectorAll ? root.querySelectorAll("*") : [];
        for (let i = 0; i < all.length; i++) {
          const sr = all[i].shadowRoot;
          if (sr) collect(sr, depth + 1);
        }
      } catch {
      }
    };
    collect(host, 0);
    try {
      if (host.shadowRoot) collect(host.shadowRoot, 1);
    } catch {
    }
    let best = null;
    let bestScore = 0;
    for (const a of anchors) {
      const score = permalinkScore(a);
      if (score > bestScore) {
        bestScore = score;
        best = a;
      }
    }
    if (!best || bestScore < 10) return null;
    let url = "";
    try {
      url = String(
        /** @type {HTMLAnchorElement} */
        best.href || ""
      ).trim();
    } catch {
      url = "";
    }
    if (!url) return null;
    return { url, link: best };
  }
  function findPermalinkCardHost(el2) {
    let n = el2;
    let depth = 0;
    while (n && n.nodeType === 1 && n !== document.body && n !== document.documentElement && depth++ < 16) {
      const role = (n.getAttribute && n.getAttribute("role") || "").trim().toLowerCase();
      if (n.tagName === "ARTICLE" || role === "article") return n;
      n = n.parentElement || composedParent(n);
    }
    return el2;
  }
  function resolveHoveredLink(el2) {
    if (!el2 || el2.nodeType !== 1) return null;
    let probe = (
      /** @type {Element} */
      el2
    );
    let guard = 0;
    while (probe && probe.nodeType === 1 && guard++ < 12) {
      try {
        if (probe.tagName === "A") {
          const href = String(
            /** @type {HTMLAnchorElement} */
            probe.href || ""
          ).trim();
          if (href && !href.toLowerCase().startsWith("javascript:")) {
            return { url: href, link: probe };
          }
        }
      } catch {
      }
      try {
        const a = typeof probe.closest === "function" ? probe.closest("a[href]") : null;
        if (a && a.tagName === "A") {
          const href = String(
            /** @type {HTMLAnchorElement} */
            a.href || ""
          ).trim();
          if (href && !href.toLowerCase().startsWith("javascript:")) {
            return { url: href, link: a };
          }
        }
      } catch {
      }
      try {
        let roleLink = probe;
        if (roleLink.getAttribute?.("role") !== "link") {
          roleLink = roleLink.closest?.('[role="link"]') || null;
        }
        if (roleLink && roleLink.getAttribute("role") === "link" && roleLink.dataset?.kpUrl) {
          const url = String(roleLink.dataset.kpUrl || "").trim();
          if (url) return { url, link: roleLink };
        }
      } catch {
      }
      const root = typeof probe.getRootNode === "function" ? probe.getRootNode() : null;
      if (!(typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot) || !(root.host instanceof Element)) {
        break;
      }
      probe = root.host;
    }
    const unique = uniqueDescendantNavigableLink(el2);
    if (unique) return unique;
    const card = findPermalinkCardHost(
      /** @type {Element} */
      el2
    );
    return resolveDescendantPermalink(card);
  }
  function uniqueDescendantNavigableLink(host) {
    if (!host || host.nodeType !== 1) return null;
    let found = null;
    let foundDest = "";
    try {
      const list = host.querySelectorAll("a[href]");
      if (!list || !list.length || list.length > 32) return null;
      for (let i = 0; i < list.length; i++) {
        const a = list[i];
        let href = "";
        try {
          href = String(
            /** @type {HTMLAnchorElement} */
            a.href || ""
          ).trim();
        } catch {
          href = "";
        }
        if (!href || href.toLowerCase().startsWith("javascript:")) continue;
        const dest = activationDestKey(href);
        if (!dest) continue;
        if (!found) {
          found = a;
          foundDest = dest;
        } else if (dest !== foundDest) {
          return null;
        }
      }
    } catch {
      return null;
    }
    if (!found || !foundDest) return null;
    let url = "";
    try {
      url = String(
        /** @type {HTMLAnchorElement} */
        found.href || ""
      ).trim();
    } catch {
      url = "";
    }
    if (!url) return null;
    return { url, link: found };
  }
  function normalizeActivationDest(href) {
    const raw = String(href || "").trim();
    if (!raw || raw === "#" || raw.toLowerCase().startsWith("javascript:")) return "";
    try {
      const u = new URL(raw, typeof location !== "undefined" ? location.href : void 0);
      const path = (u.pathname || "/").replace(/\/+$/, "") || "/";
      return `${u.origin}${path}`.toLowerCase();
    } catch {
      return raw.split(/[?#]/)[0].replace(/\/+$/, "").toLowerCase();
    }
  }
  function activationDestKey(href) {
    const dest = normalizeActivationDest(href);
    if (!dest) return "";
    try {
      const u = new URL(String(href || "").trim(), typeof location !== "undefined" ? location.href : void 0);
      const search = String(u.search || "").toLowerCase();
      const hash = String(u.hash || "").toLowerCase();
      return dest + search + hash;
    } catch {
      return dest;
    }
  }
  function ownNavigableHref(el2) {
    if (!el2 || el2.nodeType !== 1) return "";
    try {
      if (el2.tagName === "A") {
        const href = String(
          /** @type {HTMLAnchorElement} */
          el2.href || ""
        ).trim();
        return activationDestKey(href);
      }
    } catch {
    }
    try {
      if ((el2.getAttribute("role") || "").trim().toLowerCase() === "link") {
        const data = String(el2.dataset?.kpUrl || el2.getAttribute("href") || "").trim();
        if (data) return activationDestKey(data);
      }
    } catch {
    }
    return "";
  }
  var JS_NAV_DEST_ATTRS = Object.freeze([
    "data-href",
    "data-url",
    "data-link",
    "data-nav",
    "data-destination",
    "data-kp-url"
  ]);
  function ownJsNavigableDest(el2) {
    if (!el2 || el2.nodeType !== 1) return "";
    for (let i = 0; i < JS_NAV_DEST_ATTRS.length; i++) {
      let raw = "";
      try {
        raw = String(el2.getAttribute(JS_NAV_DEST_ATTRS[i]) || "").trim();
      } catch {
        raw = "";
      }
      const dest = activationDestKey(raw);
      if (dest) return dest;
    }
    return "";
  }
  function ownOnclickToken(el2) {
    if (!el2 || el2.nodeType !== 1) return "";
    let raw = "";
    try {
      raw = String(el2.getAttribute("onclick") || "").replace(/\s+/g, " ").trim();
    } catch {
      raw = "";
    }
    return raw ? raw.slice(0, 180).toLowerCase() : "";
  }
  function isOwnActionControl(el2) {
    if (!el2 || el2.nodeType !== 1) return false;
    const tag = el2.tagName;
    if (tag === "BUTTON") return true;
    let role = "";
    try {
      role = (el2.getAttribute("role") || "").trim().toLowerCase();
    } catch {
      role = "";
    }
    if (role === "button" || role === "menuitem" || role === "tab") return true;
    try {
      if (el2.getAttribute("aria-haspopup")) return true;
    } catch {
    }
    if (tag === "INPUT") {
      let t = "";
      try {
        t = String(
          /** @type {HTMLInputElement} */
          el2.type || ""
        ).toLowerCase();
      } catch {
        t = "";
      }
      return t === "button" || t === "submit" || t === "reset" || t === "checkbox" || t === "radio";
    }
    return false;
  }
  function isImpliedPermalinkHost(el2) {
    if (!el2 || el2.nodeType !== 1) return false;
    try {
      const role = (el2.getAttribute("role") || "").trim().toLowerCase();
      if (el2.tagName === "ARTICLE" || role === "article") return true;
    } catch {
    }
    return false;
  }
  function resolveActivationIdentity(el2) {
    if (!el2 || el2.nodeType !== 1) return "";
    const own = ownNavigableHref(el2);
    if (own) return `nav:${own}`;
    if (isOwnActionControl(el2)) {
      let testid = "";
      let label = "";
      let role = "";
      try {
        testid = String(el2.getAttribute("data-testid") || "").trim();
      } catch {
      }
      try {
        label = String(el2.getAttribute("aria-label") || el2.textContent || "").replace(/\s+/g, " ").trim().slice(0, 48);
      } catch {
      }
      try {
        role = String(el2.getAttribute("role") || "").trim().toLowerCase();
      } catch {
      }
      const key = testid || label || role || el2.tagName;
      return `act:${String(key).toLowerCase()}`;
    }
    const dataDest = ownJsNavigableDest(el2);
    if (dataDest) return `nav:${dataDest}`;
    const onclickTok = ownOnclickToken(el2);
    if (onclickTok) return `js:${onclickTok}`;
    if (isImpliedPermalinkHost(el2)) {
      const perma = resolveDescendantPermalink(el2);
      if (perma?.url) {
        const dest = normalizeActivationDest(perma.url);
        if (dest) return `nav:${dest}`;
      }
    }
    return "";
  }
  function activationIdentitiesMatch(leaf, host) {
    const a = resolveActivationIdentity(leaf);
    const b = resolveActivationIdentity(host);
    return !!(a && b && a === b);
  }

  // src/modules/element-detector.js
  var ElementDetector = class {
    constructor() {
      this.CLICKABLE_ROLES = ["link", "button", "slider", "checkbox", "radio", "tab", "menuitem", "option", "switch", "treeitem", "combobox", "spinbutton"];
      this.COMPOSITE_CONTAINER_ROLES = [
        "tablist",
        "menu",
        "menubar",
        "listbox",
        "tree",
        "grid",
        "radiogroup",
        "toolbar",
        "group",
        "navigation",
        "list",
        "directory",
        "rowgroup",
        "table"
      ];
      this.CLICKABLE_SEL = "a[href], button, input, select, textarea, video[controls], audio[controls], summary";
      this.FOCUSABLE_SEL = 'a[href], button, input, select, textarea, video[controls], audio[controls], summary, [contenteditable="true"], [role="button"], [role="link"], [role="checkbox"], [role="radio"], [role="tab"], [role="menuitem"], [data-action], [data-toggle], [data-click], [data-href], [data-link], [vue-click], [ng-click]';
      this.clickHandlerElements = /* @__PURE__ */ new WeakSet();
      this.clickHandlerListeners = /* @__PURE__ */ new WeakMap();
      this._nativeCursorSuspendDepth = 0;
      this._nativeCursorWasHidden = false;
      if (FEATURE_FLAGS.ENABLE_CLICK_LISTENER_TRACKING !== false) {
        this.setupEventListenerTracking();
      }
    }
    /**
     * True when Crosshair mode is forcing `--kpv2-cursor` on the page via
     * `html.kpv2-cursor-hidden`.
     * @returns {boolean}
     */
    _isCursorOverrideActive() {
      try {
        return !!document.documentElement?.classList?.contains(CSS_CLASSES.CURSOR_HIDDEN);
      } catch {
        return false;
      }
    }
    /**
     * Run `fn` with KeyPilot's custom-cursor override temporarily disabled so
     * getComputedStyle(...).cursor reflects the page's real cursor (e.g. pointer).
     *
     * When CUSTOM_CURSORS is on, style-manager forces:
     *   html.kpv2-cursor-hidden * { cursor: var(--kpv2-cursor) !important; }
     * which makes every element report the crosshair (or other custom cursor) and
     * hides CSS cursor:pointer signals used to find non-semantic clickables.
     *
     * IMPORTANT: Toggling that class invalidates styles for the whole document.
     * Only call this when a cursor:pointer probe is actually required — never on
     * the semantic-clickable hot path (see findClickable).
     *
     * Suspend/restore is synchronous and re-entrant — no paint should occur mid-task.
     * @template T
     * @param {() => T} fn
     * @returns {T}
     */
    _withNativePageCursors(fn) {
      let html = null;
      try {
        html = document.documentElement;
      } catch {
      }
      if (!html || !html.classList) {
        return fn();
      }
      if (this._nativeCursorSuspendDepth > 0) {
        this._nativeCursorSuspendDepth++;
        try {
          return fn();
        } finally {
          this._nativeCursorSuspendDepth--;
        }
      }
      const hadHidden = html.classList.contains(CSS_CLASSES.CURSOR_HIDDEN);
      if (!hadHidden) {
        return fn();
      }
      this._nativeCursorWasHidden = true;
      try {
        html.classList.remove(CSS_CLASSES.CURSOR_HIDDEN);
      } catch {
      }
      this._nativeCursorSuspendDepth = 1;
      try {
        return fn();
      } finally {
        this._nativeCursorSuspendDepth = 0;
        try {
          html.classList.add(CSS_CLASSES.CURSOR_HIDDEN);
        } catch {
        }
        this._nativeCursorWasHidden = false;
      }
    }
    /**
     * Computed cursor with custom-cursor override suspended.
     * @param {Element} el
     * @returns {string}
     */
    _getPageComputedCursor(el2) {
      if (!el2 || !window.getComputedStyle) return "";
      return this._withNativePageCursors(() => {
        try {
          return window.getComputedStyle(el2).cursor || "";
        } catch {
          return "";
        }
      });
    }
    /**
     * True when el is a layout/composite container that should not become a
     * clickable hover target solely because of a delegated click listener.
     * @param {Element} el
     * @param {string} [role]
     * @returns {boolean}
     */
    isCompositeClickContainer(el2, role = "") {
      if (!el2 || el2.nodeType !== 1) return false;
      const r = role || el2.getAttribute && (el2.getAttribute("role") || "").trim().toLowerCase() || "";
      if (r && this.COMPOSITE_CONTAINER_ROLES.includes(r)) return true;
      const tag = el2.tagName;
      return tag === "NAV" || tag === "UL" || tag === "OL" || tag === "MENU" || tag === "TABLE" || tag === "TBODY" || tag === "THEAD" || tag === "TFOOT" || tag === "HEADER";
    }
    /**
     * Google Closure `jsaction` (Gmail thread rows, Maps, etc.).
     * Weak like a tracked listener — composite list/grid shells must not light up.
     * @param {Element|null|undefined} el
     * @returns {boolean}
     */
    hasJsActionHandler(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      try {
        const raw = el2.getAttribute && el2.getAttribute("jsaction");
        return !!(raw && String(raw).trim());
      } catch {
        return false;
      }
    }
    /**
     * @param {Element|null|undefined} el
     * @returns {string}
     */
    _jsActionToken(el2) {
      if (!el2 || el2.nodeType !== 1) return "";
      try {
        return String(el2.getAttribute && el2.getAttribute("jsaction") || "").replace(/\s+/g, " ").trim();
      } catch {
        return "";
      }
    }
    /**
     * Inbox scroller / feed shells also have jsaction; those must not become F-targets.
     * Thread rows (~40–80px) and chips stay.
     * @param {Element} el
     * @returns {boolean}
     */
    _jsActionHostTooLarge(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      try {
        const r = el2.getBoundingClientRect();
        if (!r || !(r.height > 0)) return false;
        return r.height > 160;
      } catch {
        return false;
      }
    }
    /**
     * jsaction on a chip/tab scroller (Google News `jsname=w60JDf`) must not make
     * the whole strip an F-target when the pointer is in the gap between items.
     * @param {Element} el
     * @returns {boolean}
     */
    _isJsActionMultiItemShell(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      try {
        const items = el2.querySelectorAll(
          '[role="tab"], [role="button"], button, a[href], [role="link"], [role="menuitem"], [role="option"]'
        );
        return !!(items && items.length >= 2);
      } catch {
        return false;
      }
    }
    /**
     * jsaction-only hosts that are list/strip shells, not a single row/card click.
     * @param {Element} el
     * @returns {boolean}
     */
    _isJsActionDelegationShell(el2) {
      try {
        if (this._jsActionHostTooLarge(el2)) return true;
      } catch {
      }
      try {
        if (this._isJsActionMultiItemShell(el2)) return true;
      } catch {
      }
      return false;
    }
    /**
     * Site chrome bar (full-width header/nav) — not a real F-target.
     * NVIDIA sets cursor:pointer on `nav.global-nav` / `.nav-header`, which
     * would otherwise light up the entire top bar and then sticky-trap items.
     * @param {Element} el
     * @returns {boolean}
     */
    _isFullBleedChromeBar(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      try {
        if (this.isCompositeClickContainer(el2)) return true;
      } catch {
      }
      let role = "";
      try {
        role = (el2.getAttribute && el2.getAttribute("role") || "").trim().toLowerCase();
      } catch {
      }
      try {
        if (el2.tagName === "TR" || role === "row") return false;
      } catch {
      }
      if (role === "banner" || role === "navigation") return true;
      try {
        const r = el2.getBoundingClientRect();
        const vw = window.innerWidth || 0;
        if (!(r && vw > 0 && r.width > 0 && r.height > 0)) return false;
        return r.width >= vw * 0.7 && r.height <= 120 && r.top <= 80;
      } catch {
        return false;
      }
    }
    setupEventListenerTracking() {
      const originalAddEventListener = EventTarget.prototype.addEventListener;
      EventTarget.prototype.addEventListener = function(type2, listener, options) {
        originalAddEventListener.call(this, type2, listener, options);
        if (type2 === "click" && this instanceof Element) {
          try {
            elementDetectorInstance.clickHandlerElements.add(this);
            if (listener && (typeof listener === "function" || typeof listener.handleEvent === "function")) {
              let set = elementDetectorInstance.clickHandlerListeners.get(this);
              if (!set) {
                set = /* @__PURE__ */ new Set();
                elementDetectorInstance.clickHandlerListeners.set(this, set);
              }
              set.add(listener);
            }
          } catch {
          }
        }
      };
      window.elementDetectorInstance = this;
    }
    hasTrackedClickHandler(el2) {
      return this.clickHandlerElements.has(el2);
    }
    /**
     * True when `a` and `b` share an addEventListener('click') function.
     * Action controls (buttons) never match this way — Add to Cart must keep
     * its own ring even if a parent card also has a click listener.
     * Isolated-world wrap only sees listeners registered in this world.
     * @param {Element|null|undefined} a
     * @param {Element|null|undefined} b
     * @returns {boolean}
     */
    _shareClickListener(a, b) {
      if (!a || !b || a === b || a.nodeType !== 1 || b.nodeType !== 1) return false;
      try {
        if (isOwnActionControl(a) || isOwnActionControl(b)) return false;
      } catch {
      }
      let sa = null;
      let sb = null;
      try {
        sa = this.clickHandlerListeners.get(a);
      } catch {
        sa = null;
      }
      try {
        sb = this.clickHandlerListeners.get(b);
      } catch {
        sb = null;
      }
      if (!sa || !sb || !sa.size || !sb.size) return false;
      try {
        for (const fn of sa) {
          if (sb.has(fn)) return true;
        }
      } catch {
      }
      return false;
    }
    /**
     * Host is the click surface F would actually fire (row jsaction, card URL, …).
     * @param {Element} el
     * @returns {boolean}
     */
    _hasDelegatedClickSurface(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      try {
        if (this.hasJsActionHandler(el2) || this.hasTrackedClickHandler(el2)) return true;
      } catch {
      }
      try {
        if (el2.onclick || el2.getAttribute && el2.getAttribute("onclick")) return true;
      } catch {
      }
      try {
        if (resolveActivationIdentity(el2)) return true;
      } catch {
      }
      return false;
    }
    /**
     * Gmail subject `div.xS[role=link]` has no href/onclick; F bubbles to `tr[jsaction]`.
     * Distinct widgets (star, checkbox, attachment chip) must not inherit.
     * @param {Element} leaf
     * @param {Element} host
     * @returns {boolean}
     */
    _leafInheritsHostActivation(leaf, host) {
      if (!leaf || !host || leaf === host || leaf.nodeType !== 1 || host.nodeType !== 1) {
        return false;
      }
      try {
        if (isOwnActionControl(leaf)) return false;
      } catch {
      }
      let role = "";
      try {
        role = (leaf.getAttribute && leaf.getAttribute("role") || "").trim().toLowerCase();
        if (role && role !== "link" && this.CLICKABLE_ROLES.includes(role)) return false;
      } catch {
      }
      try {
        if (resolveActivationIdentity(leaf)) return false;
      } catch {
      }
      try {
        const leafIsControl = role === "link" || typeof leaf.matches === "function" && leaf.matches(this.FOCUSABLE_SEL);
        if (leafIsControl) {
          const hostId = resolveActivationIdentity(host);
          if (hostId && hostId.slice(0, 4) === "nav:") return false;
        }
      } catch {
      }
      try {
        const leafTok = this._jsActionToken(leaf);
        const hostTok = this._jsActionToken(host);
        if (leafTok && leafTok !== hostTok) return false;
        if (hostTok) {
          let n = leaf.parentElement;
          let depth = 0;
          while (n && n !== host && n.nodeType === 1 && depth++ < 14) {
            const tok = this._jsActionToken(n);
            if (tok && tok !== hostTok) return false;
            n = n.parentElement;
          }
        }
      } catch {
      }
      try {
        return this._hasDelegatedClickSurface(host);
      } catch {
        return false;
      }
    }
    /**
     * Same F-destination: matching URL / data-href / onclick, a shared click fn,
     * or a nested label that only activates by bubbling to the host (Gmail rows).
     * @param {Element} leaf
     * @param {Element} host
     * @returns {boolean}
     */
    _skipForParentDestinationsMatch(leaf, host) {
      try {
        if (activationIdentitiesMatch(leaf, host)) return true;
      } catch {
      }
      try {
        if (this._shareClickListener(leaf, host)) return true;
      } catch {
      }
      try {
        if (this._leafInheritsHostActivation(leaf, host)) return true;
      } catch {
      }
      try {
        if (this._hostPrimaryNavMatchesLeaf(leaf, host)) return true;
      } catch {
      }
      return false;
    }
    /**
     * Leaf is the only navigable dest inside `host` (Gmail left-nav `.TO` chip:
     * short <a>Inbox</a> in a 240×32 row). Parent need not declare href/jsaction.
     * @param {Element} leaf
     * @param {Element} host
     * @returns {boolean}
     */
    _hostPrimaryNavMatchesLeaf(leaf, host) {
      if (!leaf || !host || leaf === host || leaf.nodeType !== 1 || host.nodeType !== 1) {
        return false;
      }
      try {
        if (isOwnActionControl(leaf) || isOwnActionControl(host)) return false;
      } catch {
      }
      let leafId = "";
      try {
        leafId = resolveActivationIdentity(leaf);
      } catch {
        leafId = "";
      }
      if (!leafId || leafId.slice(0, 4) !== "nav:") return false;
      try {
        const hostId = resolveActivationIdentity(host);
        if (hostId && hostId !== leafId) return false;
      } catch {
      }
      try {
        if (!this.composedContains(host, leaf)) return false;
      } catch {
        return false;
      }
      try {
        const hostText = String(host.innerText || "").replace(/\s+/g, " ").trim();
        const leafText = String(leaf.innerText || leaf.textContent || "").replace(/\s+/g, " ").trim();
        if (hostText.length >= 8 && leafText.length >= 1 && leafText.length < hostText.length * 0.45) {
          return false;
        }
      } catch {
      }
      try {
        const lr = leaf.getBoundingClientRect();
        const hr = host.getBoundingClientRect();
        if (lr.width > 0 && lr.height > 0 && hr.width > 0 && hr.height > 0) {
          const sameHeight = hr.height <= Math.max(lr.height * 1.15, lr.height + 4);
          if (sameHeight && hr.width > lr.width * 2.2 + 24) return false;
        }
      } catch {
      }
      try {
        const unique = uniqueDescendantNavigableLink(host);
        if (!unique?.link) return false;
        if (unique.link === leaf) return true;
        let leafHref = "";
        try {
          leafHref = String(
            /** @type {any} */
            leaf.href || ""
          ).trim();
        } catch {
          leafHref = "";
        }
        return !!(leafHref && unique.url === leafHref);
      } catch {
        return false;
      }
    }
    /**
     * Like Node.contains, but crosses open shadow boundaries via host hops.
     * archive.org tiles: clickable <a> in tile-dispatcher wraps <collection-tile>
     * whose internals live in a nested shadow — plain contains() is always false
     * for those descendants, which breaks sticky hover and nested-chrome checks.
     *
     * @param {Node|null|undefined} host
     * @param {Node|null|undefined} node
     * @returns {boolean}
     */
    composedContains(host, node) {
      if (!host || !node || host.nodeType !== 1) return false;
      if (host === node) return true;
      try {
        if (host.contains(node)) return true;
      } catch {
      }
      let n = node;
      let depth = 0;
      while (n && depth++ < 32) {
        if (n === host) return true;
        try {
          if (n.parentElement) {
            n = n.parentElement;
            continue;
          }
          const root = typeof n.getRootNode === "function" ? n.getRootNode() : null;
          if (root && typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot) {
            n = root.host || null;
            continue;
          }
        } catch {
          break;
        }
        break;
      }
      return false;
    }
    deepElementFromPoint(x, y) {
      return deepElementFromPoint(x, y);
    }
    /**
     * URL + element for G / B / E / P / Copy URL.
     * Ancestor <a href> first; then a descendant permalink inside a card.
     * @param {Element|null|undefined} el
     * @returns {{ url: string, link: Element }|null}
     */
    resolveHoveredLink(el2) {
      return resolveHoveredLink(el2);
    }
    /**
     * True when this element itself sets cursor:pointer (not merely inheriting it).
     * Sites like suno.com put `cursor-pointer` on <body>, which would otherwise make
     * every node look interactive via getComputedStyle().cursor.
     *
     * When KeyPilot custom cursors are enabled, page cursor styles are overridden
     * with !important — this method suspends that override before reading computed
     * styles so cursor:pointer-only clickables still get hover outlines.
     */
    hasExplicitCursorPointer(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      try {
        if (el2 === document.body || el2 === document.documentElement) return false;
      } catch {
      }
      try {
        if (el2.style && String(el2.style.cursor || "").toLowerCase() === "pointer") {
          return true;
        }
      } catch {
      }
      try {
        const cls = typeof el2.className === "string" ? el2.className : el2.className && typeof el2.className.baseVal === "string" ? el2.className.baseVal : "";
        if (cls && (/\bcursor-pointer\b/i.test(cls) || /\bcursorPointer\b/.test(cls))) {
          return true;
        }
      } catch {
      }
      try {
        if (!window.getComputedStyle) return false;
        const probe = () => {
          const own = window.getComputedStyle(el2).cursor;
          if (own !== "pointer") return false;
          let parent = el2.parentElement;
          if (!parent) {
            try {
              const root = typeof el2.getRootNode === "function" ? el2.getRootNode() : null;
              if (root && typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot) {
                parent = root.host || null;
              }
            } catch {
            }
          }
          if (!parent || parent === document.body || parent === document.documentElement) {
            return true;
          }
          return window.getComputedStyle(parent).cursor !== "pointer";
        };
        return this._isCursorOverrideActive() ? this._withNativePageCursors(probe) : probe();
      } catch {
        return false;
      }
    }
    isLikelyInteractive(el2, opts = {}) {
      if (!el2 || el2.nodeType !== 1) return false;
      try {
        if (el2 === document.body || el2 === document.documentElement) return false;
        if (el2.tagName === "BODY" || el2.tagName === "HTML") return false;
      } catch {
      }
      const allowCursor = opts && Object.prototype.hasOwnProperty.call(opts, "allowCursor") ? !!opts.allowCursor : true;
      let matchesSelector = false;
      try {
        matchesSelector = el2.matches(this.FOCUSABLE_SEL);
      } catch {
        matchesSelector = false;
      }
      const role = el2.getAttribute && (el2.getAttribute("role") || "").trim().toLowerCase() || "";
      const hasRole = role && this.CLICKABLE_ROLES.includes(role);
      const hasInlineClick = !!(el2.onclick || el2.getAttribute && el2.getAttribute("onclick"));
      const hasTrackedClick = this.hasTrackedClickHandler(el2);
      const hasJsAction = this.hasJsActionHandler(el2);
      let hasClickHandler = hasInlineClick || hasTrackedClick || hasJsAction;
      if (hasClickHandler && !matchesSelector && !hasRole && !hasInlineClick && this.isCompositeClickContainer(el2, role)) {
        hasClickHandler = false;
      }
      if (hasClickHandler && hasJsAction && !hasInlineClick && !hasTrackedClick && !matchesSelector && !hasRole && this._isJsActionDelegationShell(el2)) {
        hasClickHandler = false;
      }
      let hasCursor = false;
      if (allowCursor && !matchesSelector && !hasRole && !hasClickHandler && !this._isFullBleedChromeBar(el2)) {
        hasCursor = this.hasExplicitCursorPointer(el2);
      }
      if (window.KEYPILOT_DEBUG && (matchesSelector || hasRole || hasClickHandler || hasCursor)) {
        console.log("[KeyPilot Debug] isLikelyInteractive:", {
          tagName: el2.tagName,
          href: el2.href,
          matchesSelector,
          role,
          hasRole,
          hasClickHandler: !!hasClickHandler,
          hasTrackedClickHandler: this.hasTrackedClickHandler(el2),
          hasCursor,
          allowCursor,
          selector: this.FOCUSABLE_SEL
        });
      }
      return matchesSelector || hasRole || hasClickHandler || hasCursor;
    }
    /**
     * Whether an element is (or belongs to) a media/progress scrubber UI.
     * Used for focus styling (no link-style fill) and activation routing.
     */
    isScrubberLike(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      try {
        if (this.isNativeType(el2, "range")) return true;
        const role = (el2.getAttribute("role") || "").trim().toLowerCase();
        if (role === "slider") return true;
        if (this.looksLikeScrubTrack(el2)) return true;
        if (typeof el2.closest === "function") {
          if (el2.closest('[role="slider"], input[type="range"]')) return true;
        }
        return false;
      } catch {
        return false;
      }
    }
    /**
     * Thin, wide interactive bar — typical custom progress/seek track (Rumble, Suno presentation).
     * Strict geometry only; no "nearby control" search.
     */
    looksLikeScrubTrack(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      try {
        if (el2.tagName === "A" || el2.tagName === "BUTTON" || el2.tagName === "VIDEO" || el2.tagName === "AUDIO") {
          return false;
        }
        if (typeof el2.closest === "function") {
          if (el2.closest('button, [role="button"], a[href], select, textarea')) return false;
        }
        const role = (el2.getAttribute("role") || "").trim().toLowerCase();
        if (role && role !== "presentation" && role !== "none" && role !== "slider" && role !== "progressbar") {
          if (this.CLICKABLE_ROLES.includes(role) && role !== "slider") return false;
        }
        const rect = el2.getBoundingClientRect();
        if (!rect || rect.width < 64 || rect.height <= 0 || rect.height > 28) return false;
        if (rect.width / Math.max(rect.height, 1) < 4) return false;
        let pe = "auto";
        try {
          pe = window.getComputedStyle(el2).pointerEvents;
        } catch {
        }
        if (pe === "none") return false;
        if (this.hasExplicitCursorPointer(el2) || role === "presentation" || role === "slider" || role === "progressbar") {
          return true;
        }
        const cursor = this._getPageComputedCursor(el2);
        if (cursor !== "pointer") return false;
        let p = el2.parentElement;
        let d = 0;
        while (p && d < 3) {
          if (this.hasExplicitCursorPointer(p)) return true;
          try {
            const pr = p.getBoundingClientRect();
            if (pr.width >= rect.width * 0.9 && pr.height > 0 && pr.height <= 28) return true;
          } catch {
          }
          p = p.parentElement;
          d++;
        }
        return false;
      } catch {
        return false;
      }
    }
    /**
     * Structural range/slider for the hit element only — no proximity / playbar scanning.
     * Thumb-style players: range is a sibling of the visual track under the same trackHost.
     * @param {Element} el
     * @returns {HTMLElement|null}
     */
    findRelatedRangeOrSlider(el2) {
      if (!el2 || el2.nodeType !== 1) return null;
      try {
        if (this.isNativeType(el2, "range")) return (
          /** @type {HTMLElement} */
          el2
        );
        const role = (el2.getAttribute("role") || "").trim().toLowerCase();
        if (role === "slider") return (
          /** @type {HTMLElement} */
          el2
        );
      } catch {
      }
      try {
        if (typeof el2.closest === "function") {
          const viaClosest = el2.closest('input[type="range"], [role="slider"]');
          if (viaClosest) return (
            /** @type {HTMLElement} */
            viaClosest
          );
        }
      } catch {
      }
      let n = el2;
      let depth = 0;
      while (n && n.nodeType === 1 && depth < 5) {
        const onTrack = this.looksLikeScrubTrack(n) || (n.getAttribute?.("role") || "").trim().toLowerCase() === "presentation" && (() => {
          try {
            const r = n.getBoundingClientRect();
            return r.width >= 64 && r.height > 0 && r.height <= 28;
          } catch {
            return false;
          }
        })();
        if (onTrack && n.parentElement) {
          for (const sib of n.parentElement.children) {
            if (!sib || sib === n || sib.nodeType !== 1) continue;
            if (this.isNativeType(sib, "range")) return (
              /** @type {HTMLElement} */
              sib
            );
            const r = (sib.getAttribute?.("role") || "").trim().toLowerCase();
            if (r === "slider") return (
              /** @type {HTMLElement} */
              sib
            );
            try {
              for (const child of sib.children || []) {
                if (this.isNativeType(child, "range")) return (
                  /** @type {HTMLElement} */
                  child
                );
                const cr = (child.getAttribute?.("role") || "").trim().toLowerCase();
                if (cr === "slider") return (
                  /** @type {HTMLElement} */
                  child
                );
              }
            } catch {
            }
          }
        }
        n = n.parentElement;
        depth++;
      }
      return null;
    }
    /**
     * Resolve a scrubber for the hit element only (track / range / role=slider).
     * Does not pull in nearby play/volume buttons via playbar-wide searches.
     * @param {Element|null} el
     * @param {number} [clientX]
     * @param {number} [clientY]
     * @returns {{ kind: 'range'|'role-slider'|'track', control: HTMLElement, track: HTMLElement }|null}
     */
    resolveScrubber(el2, clientX, clientY) {
      if (!el2 || el2.nodeType !== 1) return null;
      try {
        if (el2.tagName === "A" || el2.tagName === "TEXTAREA" || el2.tagName === "BUTTON") return null;
        if (el2.tagName === "INPUT") {
          const t = (el2.getAttribute("type") || "text").toLowerCase();
          if (t !== "range") return null;
        }
        if (typeof el2.closest === "function") {
          if (el2.closest('button, [role="button"], a[href], select, textarea')) return null;
        }
      } catch {
      }
      const control = this.findRelatedRangeOrSlider(el2);
      if (control) {
        const kind = this.isNativeType(control, "range") ? "range" : "role-slider";
        const track = this.getScrubTrackElement(control, el2) || control;
        return { kind, control, track: (
          /** @type {HTMLElement} */
          track
        ) };
      }
      let n = el2;
      let depth = 0;
      while (n && n.nodeType === 1 && depth < 5) {
        if (this.looksLikeScrubTrack(n)) {
          try {
            const r = n.getBoundingClientRect();
            if (r.width > window.innerWidth * 0.98 && r.height > 40) {
              n = n.parentElement;
              depth++;
              continue;
            }
          } catch {
          }
          let track = (
            /** @type {HTMLElement} */
            n
          );
          let p = n.parentElement;
          let up = 0;
          while (p && up < 3) {
            if (this.looksLikeScrubTrack(p)) {
              try {
                const pr = p.getBoundingClientRect();
                const tr = track.getBoundingClientRect();
                if (pr.width >= tr.width * 0.9 && pr.height <= 28) track = /** @type {HTMLElement} */
                p;
              } catch {
              }
            }
            p = p.parentElement;
            up++;
          }
          return { kind: "track", control: track, track };
        }
        n = n.parentElement;
        depth++;
      }
      return null;
    }
    /**
     * For thumb-sized range inputs, return the full-width track element used for geometry/seek.
     * @param {Element} control - range or role=slider
     * @param {Element} [hintEl] - original hit element
     * @returns {HTMLElement|null}
     */
    getScrubTrackElement(control, hintEl) {
      if (!control || control.nodeType !== 1) return null;
      try {
        if ((control.getAttribute("role") || "").trim().toLowerCase() === "slider") {
          return (
            /** @type {HTMLElement} */
            control
          );
        }
      } catch {
      }
      let controlRect = null;
      try {
        controlRect = control.getBoundingClientRect();
      } catch {
        controlRect = null;
      }
      try {
        if (this.isNativeType(control, "range") && controlRect && controlRect.width >= 40) {
          return (
            /** @type {HTMLElement} */
            control
          );
        }
      } catch {
      }
      const roots = [];
      try {
        if (control.parentElement) roots.push(control.parentElement);
        if (control.parentElement?.parentElement) roots.push(control.parentElement.parentElement);
        if (control.parentElement?.parentElement?.parentElement) {
          roots.push(control.parentElement.parentElement.parentElement);
        }
        if (hintEl && hintEl !== control) roots.push(hintEl, hintEl.parentElement);
      } catch {
      }
      for (const root of roots) {
        if (!root || !root.querySelector) continue;
        try {
          const presentation = root.querySelector?.(':scope > [role="presentation"], [role="presentation"]');
          if (presentation) {
            const pr = presentation.getBoundingClientRect();
            if (pr.width > 40 && (!controlRect || pr.width > controlRect.width * 2)) {
              return (
                /** @type {HTMLElement} */
                presentation
              );
            }
          }
        } catch {
        }
        try {
          const rr = root.getBoundingClientRect();
          if (controlRect && rr.width > controlRect.width * 3 && rr.height <= 48 && rr.height > 0) {
            return (
              /** @type {HTMLElement} */
              root
            );
          }
        } catch {
        }
      }
      let n = control.parentElement;
      let depth = 0;
      while (n && depth < 6) {
        try {
          const rr = n.getBoundingClientRect();
          if (controlRect && rr.width > Math.max(controlRect.width * 3, 80) && rr.height <= 40) {
            const pres = n.querySelector?.('[role="presentation"]');
            if (pres) {
              const pr = pres.getBoundingClientRect();
              if (pr.width > 40) return (
                /** @type {HTMLElement} */
                pres
              );
            }
            return (
              /** @type {HTMLElement} */
              n
            );
          }
        } catch {
        }
        n = n.parentElement;
        depth++;
      }
      if (hintEl && this.looksLikeScrubTrack(hintEl)) {
        return (
          /** @type {HTMLElement} */
          hintEl
        );
      }
      return (
        /** @type {HTMLElement} */
        control
      );
    }
    /**
     * Walk ancestors for an interactive target.
     * @param {Element|null|undefined} el
     * @param {{ allowCursor?: boolean }} [opts]
     * @returns {Element|null}
     */
    _findClickableWalk(el2, opts = {}) {
      const allowCursor = opts.allowCursor !== false;
      let n = el2;
      let depth = 0;
      let cursorOnlyCandidate = null;
      while (n && n !== document.body && n.nodeType === 1 && depth < 20) {
        if (this.isLikelyInteractive(n, { allowCursor: false })) {
          if (window.KEYPILOT_DEBUG) {
            console.log("[KeyPilot Debug] findClickable found:", {
              tagName: n.tagName,
              href: n.href,
              className: n.className,
              depth
            });
          }
          return n;
        }
        if (allowCursor && !cursorOnlyCandidate && this.isLikelyInteractive(n, { allowCursor: true })) {
          cursorOnlyCandidate = n;
        }
        n = n.parentElement || (n.getRootNode() instanceof ShadowRoot ? n.getRootNode().host : null);
        depth++;
      }
      if (!allowCursor) return null;
      const finalResult = cursorOnlyCandidate || (el2 && this.isLikelyInteractive(el2) ? el2 : null);
      if (window.KEYPILOT_DEBUG && !finalResult && el2) {
        console.log("[KeyPilot Debug] findClickable found nothing for:", {
          tagName: el2.tagName,
          href: el2.href,
          className: el2.className
        });
      }
      return finalResult;
    }
    findClickable(el2) {
      const semantic = this._findClickableWalk(el2, { allowCursor: false });
      if (semantic) return semantic;
      if (this._isCursorOverrideActive()) {
        return null;
      }
      return this._findClickableWalk(el2, { allowCursor: true });
    }
    /**
     * True when `el` is redundant nested chrome of `host`: F on the leaf would
     * do the same thing as F on the host (same navigation / implied permalink).
     *
     * Different URL or a distinct action (like / reply / Show more) is not chrome.
     * Flyouts that only share a DOM ancestor still get their own ring.
     * @param {Element} el
     * @param {Element} host
     * @returns {boolean}
     */
    _isNestedHoverChrome(el2, host) {
      if (!el2 || !host || el2 === host || el2.nodeType !== 1 || host.nodeType !== 1) return false;
      try {
        if (!this.composedContains(host, el2)) return false;
      } catch {
        return false;
      }
      try {
        if (this._isFullBleedChromeBar(host)) return false;
      } catch {
      }
      try {
        const er = el2.getBoundingClientRect();
        const hr = host.getBoundingClientRect();
        if (er.width > 0 && er.height > 0 && hr.width > 0 && hr.height > 0) {
          const overlapW = Math.max(0, Math.min(er.right, hr.right) - Math.max(er.left, hr.left));
          const overlapH = Math.max(0, Math.min(er.bottom, hr.bottom) - Math.max(er.top, hr.top));
          const eArea = er.width * er.height;
          const overlapArea = overlapW * overlapH;
          if (overlapArea < eArea * 0.4) return false;
        }
      } catch {
      }
      try {
        return this._skipForParentDestinationsMatch(el2, host);
      } catch {
        return false;
      }
    }
    /**
     * Top-bar disclosure chip (NVIDIA Products, etc.): small painted box that
     * owns a large DOM flyout. Used to avoid sticky-hover on the chip.
     * @param {Element} el
     * @returns {boolean}
     */
    _isCompactDisclosureHost(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      let role = "";
      let hasPopup = false;
      try {
        role = (el2.getAttribute && el2.getAttribute("role") || "").trim().toLowerCase();
        hasPopup = !!(el2.getAttribute && el2.getAttribute("aria-haspopup"));
      } catch {
      }
      if (role !== "menuitem" && !hasPopup) return false;
      try {
        const r = el2.getBoundingClientRect();
        if (!r || r.width <= 0 || r.height <= 0) return false;
        return r.height <= 72 && r.width <= 280;
      } catch {
        return true;
      }
    }
    /**
     * Settings → Click Mode → Skip for parent (on by default).
     * @returns {boolean}
     */
    _skipForParentEnabled() {
      try {
        const v = window.keyPilot?._settings?.clickMode?.skipForParent;
        if (v === false) return false;
      } catch {
      }
      return true;
    }
    /**
     * Larger same-destination card/overlay to hover instead of `leaf`.
     * Sibling stretched links (Home Depot product-pod overlay) count; a
     * different-destination child (Add to Cart) is never replaced.
     * @param {Element} leaf
     * @returns {Element|null}
     */
    _findSkipForParentHost(leaf) {
      if (!this._skipForParentEnabled()) return null;
      if (!leaf || leaf.nodeType !== 1) return null;
      let leafRect = null;
      try {
        leafRect = leaf.getBoundingClientRect();
      } catch {
        leafRect = null;
      }
      if (!leafRect || !(leafRect.width > 0) || !(leafRect.height > 0)) return null;
      const leafArea = leafRect.width * leafRect.height;
      let best = null;
      let bestArea = 0;
      const consider = (el2) => {
        if (!el2 || el2 === leaf || el2.nodeType !== 1) return;
        try {
          const ok = this.isLikelyInteractive(el2, { allowCursor: true }) || this.hasJsActionHandler(el2) && !this._isJsActionDelegationShell(el2) || this._hostPrimaryNavMatchesLeaf(leaf, el2);
          if (!ok) return;
        } catch {
          return;
        }
        try {
          if (this._isFullBleedChromeBar(el2) || this.isCompositeClickContainer(el2)) return;
        } catch {
        }
        try {
          if (!this._skipForParentDestinationsMatch(leaf, el2)) return;
        } catch {
          return;
        }
        let r = null;
        try {
          r = el2.getBoundingClientRect();
        } catch {
          r = null;
        }
        if (!r || r.width < 100 || r.height < 24) return;
        const area = r.width * r.height;
        if (area < leafArea * 1.35) return;
        if (r.height > Math.max(96, leafRect.height * 4) && area > leafArea * 8) return;
        const overlapW = Math.max(0, Math.min(r.right, leafRect.right) - Math.max(r.left, leafRect.left));
        const overlapH = Math.max(0, Math.min(r.bottom, leafRect.bottom) - Math.max(r.top, leafRect.top));
        if (overlapW * overlapH < leafArea * 0.8) return;
        if (area > bestArea) {
          bestArea = area;
          best = el2;
        }
      };
      let n = leaf;
      let depth = 0;
      while (n && n !== document.body && n.nodeType === 1 && depth++ < 14) {
        consider(n);
        let parent = null;
        try {
          parent = n.parentElement || (typeof n.getRootNode === "function" && n.getRootNode() instanceof ShadowRoot ? n.getRootNode().host : null);
        } catch {
          parent = n.parentElement;
        }
        if (!parent || parent.nodeType !== 1 || parent === document.body) break;
        try {
          const kids = parent.children;
          if (kids) {
            for (let i = 0; i < kids.length; i++) consider(kids[i]);
          }
        } catch {
        }
        n = parent;
      }
      return best;
    }
    /**
     * @param {Element|null|undefined} el
     * @returns {Element|null|undefined}
     */
    _applySkipForParent(el2) {
      if (!el2 || el2.nodeType !== 1) return el2;
      try {
        const host = this._findSkipForParentHost(el2);
        if (host) return host;
      } catch {
      }
      return el2;
    }
    /**
     * Prefer a stable "host" interactive for hover chrome: row-sized role=link
     * cards and role=tab strips, instead of nested More/buttons/sub-links.
     * @param {Element} leaf
     * @returns {Element|null}
     */
    _findPreferableHoverHost(leaf) {
      if (!leaf || leaf.nodeType !== 1) return null;
      let hostLink = null;
      let hostTab = null;
      let n = leaf;
      let depth = 0;
      while (n && n !== document.body && n.nodeType === 1 && depth++ < 14) {
        const role = (n.getAttribute && n.getAttribute("role") || "").trim().toLowerCase();
        if (role === "tab") hostTab = n;
        if (role === "link") {
          try {
            const r = n.getBoundingClientRect();
            if (r.width >= 100 && r.height >= 32) hostLink = n;
          } catch {
          }
        }
        n = n.parentElement || (n.getRootNode instanceof Function && n.getRootNode() instanceof ShadowRoot ? n.getRootNode().host : null);
      }
      if (hostTab && (leaf === hostTab || this.composedContains(hostTab, leaf))) return hostTab;
      if (hostLink && this.composedContains(hostLink, leaf) && leaf !== hostLink) {
        if (this._isNestedHoverChrome(leaf, hostLink)) return hostLink;
      }
      return null;
    }
    /**
     * CSS "stretched link" metrics: `a::after { position:absolute; inset:0 }` (etc.)
     * expands the hit region to a card while getBoundingClientRect stays on the label.
     * Pattern used by msn.com `cs-responsive-card` headlines (and many card grids).
     *
     * @param {Element} el
     * @returns {{ w: number, h: number, pseudo: string, contentRect: DOMRect }|null}
     */
    _getStretchedLinkMetrics(el2) {
      if (!el2 || el2.nodeType !== 1) return null;
      const tag = el2.tagName;
      const role = (el2.getAttribute && el2.getAttribute("role") || "").trim().toLowerCase();
      if (tag !== "A" && role !== "link") return null;
      let er = null;
      try {
        er = el2.getBoundingClientRect();
      } catch {
        er = null;
      }
      if (!er || !(er.width > 0) || !(er.height > 0)) return null;
      const contentArea = er.width * er.height;
      for (const pseudo of ["::after", "::before"]) {
        let cs = null;
        try {
          cs = window.getComputedStyle(el2, pseudo);
        } catch {
          cs = null;
        }
        if (!cs) continue;
        const content = cs.content;
        if (!content || content === "none" || content === "normal") continue;
        if (cs.position !== "absolute" && cs.position !== "fixed") continue;
        if (cs.pointerEvents === "none") continue;
        const w = parseFloat(cs.width);
        const h = parseFloat(cs.height);
        if (!Number.isFinite(w) || !Number.isFinite(h) || w < 40 || h < 40) continue;
        const overlayArea = w * h;
        if (overlayArea < contentArea * 1.6) continue;
        if (w < er.width * 1.05 && h < er.height * 1.25) continue;
        return { w, h, pseudo, contentRect: er };
      }
      return null;
    }
    /**
     * Closest ancestor (incl. open-shadow hosts) whose box matches the stretched
     * pseudo size — typically the card shell (`.root` / `cs-responsive-card`).
     * Prefers a same-size semantic host (article / href-bearing custom element).
     *
     * @param {Element} el
     * @param {{ w: number, h: number }} stretch
     * @returns {Element|null}
     */
    _findStretchedLinkCoverHost(el2, stretch) {
      if (!el2 || !stretch) return null;
      let n = el2.parentElement || (typeof el2.getRootNode === "function" && el2.getRootNode() instanceof ShadowRoot ? el2.getRootNode().host : null);
      let depth = 0;
      let best = null;
      while (n && n !== document.body && n.nodeType === 1 && depth++ < 12) {
        if (n === document.documentElement) break;
        let r = null;
        try {
          r = n.getBoundingClientRect();
        } catch {
          r = null;
        }
        if (r && r.width > 0 && r.height > 0) {
          const dw = Math.abs(r.width - stretch.w);
          const dh = Math.abs(r.height - stretch.h);
          if (dw <= 6 && dh <= 6) {
            best = n;
            const role = (n.getAttribute && n.getAttribute("role") || "").trim().toLowerCase();
            const href = typeof /** @type {any} */
            n.href === "string" ? (
              /** @type {any} */
              n.href
            ) : n.getAttribute && n.getAttribute("href");
            const semantic = !!href || role === "link" || role === "article" || role === "listitem" || n.tagName === "A" || n.tagName === "ARTICLE";
            if (semantic) return n;
          } else if (best) {
            break;
          }
          if (r.width * r.height > stretch.w * stretch.h * 4) break;
        }
        n = n.parentElement || (typeof n.getRootNode === "function" && n.getRootNode() instanceof ShadowRoot ? n.getRootNode().host : null);
      }
      return best;
    }
    /**
     * Stretched-link hover: outline the card shell when the pointer is only on the
     * ::before/::after hit expansion (media / chrome); keep the link when the
     * pointer is on the visible label (headline text).
     *
     * @param {Element} leaf - semantic link from findClickable
     * @param {number|null|undefined} clientX
     * @param {number|null|undefined} clientY
     * @returns {Element|null} leaf, cover host, or null if not a stretched link
     */
    _resolveStretchedLinkHoverTarget(leaf, clientX = null, clientY = null) {
      const stretch = this._getStretchedLinkMetrics(leaf);
      if (!stretch) return null;
      const hasPoint = Number.isFinite(clientX) && Number.isFinite(clientY);
      if (hasPoint && this.pointInElementUnionBox(leaf, clientX, clientY, 1)) {
        return leaf;
      }
      const cover = this._findStretchedLinkCoverHost(leaf, stretch);
      if (!cover) return null;
      if (hasPoint && !this.pointInElementUnionBox(cover, clientX, clientY, 2)) {
        return null;
      }
      return cover;
    }
    /**
     * Large clickable sibling under non-interactive overlay chrome.
     *
     * Pattern (thenextweb.com visual cards, many news grids):
     *   .card
     *     a.card__image   ← full-bleed media link
     *     header          ← absolute overlay (topic + title); not itself clickable
     *       h2 > a        ← headline (separate primary target)
     *       span.topic
     *
     * Ancestor-only findClickable(header|topic) returns null even though the
     * media <a> sits under the pointer in the paint stack. Prefer that underlay
     * so the large rectangle gets the hover ring unless the user is on the
     * headline (or other real nested control).
     *
     * @param {Element} under - deepest hit node (not interactive)
     * @returns {Element|null}
     */
    _findSiblingUnderlayClickable(under) {
      if (!under || under.nodeType !== 1) return null;
      let n = under;
      let depth = 0;
      while (n && n !== document.body && n.nodeType === 1 && depth++ < 10) {
        const parent = n.parentElement || (typeof n.getRootNode === "function" && n.getRootNode() instanceof ShadowRoot ? n.getRootNode().host : null);
        if (!parent || parent.nodeType !== 1) break;
        if (parent === document.body || parent === document.documentElement) break;
        try {
          if (this.isCompositeClickContainer(parent)) {
            n = parent;
            continue;
          }
        } catch {
        }
        let underRect = null;
        try {
          underRect = n.getBoundingClientRect();
        } catch {
          underRect = null;
        }
        try {
          const kids = parent.children;
          if (!kids || !kids.length) {
            n = parent;
            continue;
          }
          let best = null;
          let bestArea = 0;
          for (let i = 0; i < kids.length; i++) {
            const sib = kids[i];
            if (!sib || sib === n || sib.nodeType !== 1) continue;
            if (!this.isLikelyInteractive(sib, { allowCursor: false })) continue;
            let sr = null;
            try {
              sr = sib.getBoundingClientRect();
            } catch {
              sr = null;
            }
            if (!sr || sr.width < 80 || sr.height < 40) continue;
            if (underRect) {
              const overlapW = Math.max(0, Math.min(sr.right, underRect.right) - Math.max(sr.left, underRect.left));
              const overlapH = Math.max(0, Math.min(sr.bottom, underRect.bottom) - Math.max(sr.top, underRect.top));
              const underArea = Math.max(1, underRect.width * underRect.height);
              const overlapArea = overlapW * overlapH;
              if (overlapArea < underArea * 0.55) continue;
              if (sr.width * sr.height < underArea * 0.85) continue;
            }
            const area = sr.width * sr.height;
            if (area > bestArea) {
              bestArea = area;
              best = sib;
            }
          }
          if (best) return best;
        } catch {
        }
        n = parent;
      }
      return null;
    }
    /**
     * True when (clientX, clientY) lies inside the element's union bounding box
     * (getBoundingClientRect), optionally padded. Used for multi-line link gaps:
     * hit-testing skips space between line boxes, but the union rect still covers them.
     * @param {Element} el
     * @param {number} clientX
     * @param {number} clientY
     * @param {number} [padPx]
     * @returns {boolean}
     */
    pointInElementUnionBox(el2, clientX, clientY, padPx = 2) {
      if (!el2 || el2.nodeType !== 1) return false;
      if (!Number.isFinite(clientX) || !Number.isFinite(clientY)) return false;
      let r = null;
      try {
        r = el2.getBoundingClientRect();
      } catch {
        r = null;
      }
      if (!r || !(r.width > 0) || !(r.height > 0)) return false;
      const pad = Number.isFinite(padPx) ? padPx : 2;
      return clientX >= r.left - pad && clientX <= r.right + pad && clientY >= r.top - pad && clientY <= r.bottom + pad;
    }
    /**
     * Resolve the element that should receive hover focus chrome / F-activate.
     * Stabilizes against nested controls and brief nulls while still inside the
     * previous host (generic fix for list rows, tabs, cards — not site-specific).
     *
     * @param {Element|null|undefined} underEl - Deepest node under the pointer
     * @param {Element|null|undefined} prevFocus - Previous hover focus element
     * @param {number|null|undefined} [clientX] - pointer x (for multi-line gap sticky)
     * @param {number|null|undefined} [clientY] - pointer y
     * @returns {Element|null}
     */
    resolveHoverFocusTarget(underEl, prevFocus = null, clientX = null, clientY = null) {
      const under = underEl && underEl.nodeType === 1 ? underEl : null;
      const prev = prevFocus && prevFocus.nodeType === 1 && prevFocus.isConnected ? prevFocus : null;
      const hasPoint = Number.isFinite(clientX) && Number.isFinite(clientY);
      if (prev && under) {
        if (under === prev) return prev;
        try {
          if (this.composedContains(prev, under)) {
            const flyoutAwayFromChip = hasPoint && this._isCompactDisclosureHost(prev) && !this.pointInElementUnionBox(prev, clientX, clientY, 4);
            if (!flyoutAwayFromChip) {
              const leafInside = this.findClickable(under);
              if (leafInside) {
                try {
                  const stretchTarget = this._resolveStretchedLinkHoverTarget(
                    leafInside,
                    clientX,
                    clientY
                  );
                  if (stretchTarget) return this._applySkipForParent(stretchTarget);
                } catch {
                }
              }
              if (!leafInside || leafInside === prev || this._isNestedHoverChrome(leafInside, prev)) {
                return prev;
              }
              const host2 = this._findPreferableHoverHost(leafInside);
              if (host2 === prev) return prev;
              if (host2) return this._applySkipForParent(host2);
              return this._applySkipForParent(leafInside);
            }
          }
        } catch {
        }
        try {
          if (!this.findClickable(under)) {
            const underlay = this._findSiblingUnderlayClickable(under);
            if (underlay === prev) return prev;
          }
        } catch {
        }
      }
      if (prev && hasPoint && this.pointInElementUnionBox(prev, clientX, clientY, 3)) {
        let leafAtPoint = null;
        try {
          leafAtPoint = under ? this.findClickable(under) : null;
        } catch {
          leafAtPoint = null;
        }
        if (leafAtPoint) {
          try {
            const stretchTarget = this._resolveStretchedLinkHoverTarget(
              leafAtPoint,
              clientX,
              clientY
            );
            if (stretchTarget) return this._applySkipForParent(stretchTarget);
          } catch {
          }
        }
        if (!leafAtPoint || leafAtPoint === prev || this._isNestedHoverChrome(leafAtPoint, prev)) {
          return prev;
        }
        try {
          if (this._isFullBleedChromeBar(prev) && leafAtPoint !== prev) {
            const host2 = this._findPreferableHoverHost(leafAtPoint);
            return this._applySkipForParent(host2 || leafAtPoint);
          }
        } catch {
        }
        try {
          if (!this.composedContains(prev, leafAtPoint) && this.pointInElementUnionBox(leafAtPoint, clientX, clientY, 1)) {
            const host2 = this._findPreferableHoverHost(leafAtPoint);
            return this._applySkipForParent(host2 || leafAtPoint);
          }
        } catch {
        }
        return prev;
      }
      if (!under) return null;
      const leaf = this.findClickable(under);
      if (!leaf) {
        const underlay = this._findSiblingUnderlayClickable(under);
        if (!underlay) return null;
        const underlayHost = this._findPreferableHoverHost(underlay);
        return this._applySkipForParent(underlayHost || underlay);
      }
      try {
        const stretchTarget = this._resolveStretchedLinkHoverTarget(leaf, clientX, clientY);
        if (stretchTarget) return this._applySkipForParent(stretchTarget);
      } catch {
      }
      const host = this._findPreferableHoverHost(leaf);
      return this._applySkipForParent(host || leaf);
    }
    isTextLike(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      if (el2.tagName === "TEXTAREA") return true;
      if (el2.tagName === "INPUT") {
        const t = (el2.getAttribute("type") || "text").toLowerCase();
        return ["text", "search", "url", "email", "tel", "password", "number"].includes(t);
      }
      return false;
    }
    isNativeType(el2, type2) {
      return el2 && el2.tagName === "INPUT" && (el2.getAttribute("type") || "").toLowerCase() === type2;
    }
    isContentEditable(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      return el2.isContentEditable || el2.getAttribute("contenteditable") === "true";
    }
    /**
     * Classify an interactive element into a clickable category.
     * Categories drive hover chrome, F-key feedback, and activation — sliders are
     * not treated as links even when they share a player bar with other controls.
     *
     * Priority: text > slider > button > link > media > control > generic
     *
     * @param {Element|null} el
     * @returns {string} One of CLICKABLE_CATEGORY values
     */
    getClickableCategory(el2) {
      if (!el2 || el2.nodeType !== 1) return CLICKABLE_CATEGORY.NONE;
      try {
        if (this.isTextLike(el2) || this.isContentEditable(el2)) {
          return CLICKABLE_CATEGORY.TEXT;
        }
        if (this.isNativeType(el2, "range")) return CLICKABLE_CATEGORY.SLIDER;
        const role = (el2.getAttribute?.("role") || "").trim().toLowerCase();
        if (role === "slider" || role === "progressbar") return CLICKABLE_CATEGORY.SLIDER;
        if (this.looksLikeScrubTrack(el2)) return CLICKABLE_CATEGORY.SLIDER;
        try {
          if (typeof el2.closest === "function" && el2.closest('input[type="range"], [role="slider"], [role="progressbar"]')) {
            return CLICKABLE_CATEGORY.SLIDER;
          }
        } catch {
        }
        if (el2.tagName === "BUTTON" || role === "button" || el2.tagName === "SUMMARY") {
          return CLICKABLE_CATEGORY.BUTTON;
        }
        try {
          if (typeof el2.matches === "function" && el2.matches('[role="button"]')) {
            return CLICKABLE_CATEGORY.BUTTON;
          }
        } catch {
        }
        if (el2.tagName === "A" && /** @type {any} */
        el2.href) return CLICKABLE_CATEGORY.LINK;
        if (role === "link") return CLICKABLE_CATEGORY.LINK;
        try {
          if (typeof el2.matches === "function" && el2.matches('a[href], [role="link"]')) {
            return CLICKABLE_CATEGORY.LINK;
          }
        } catch {
        }
        if (el2.tagName === "VIDEO" || el2.tagName === "AUDIO") return CLICKABLE_CATEGORY.MEDIA;
        if (el2.tagName === "SELECT") return CLICKABLE_CATEGORY.CONTROL;
        if (this.isNativeType(el2, "checkbox") || this.isNativeType(el2, "radio") || this.isNativeType(el2, "file") || this.isNativeType(el2, "color") || this.isNativeType(el2, "date") || this.isNativeType(el2, "time")) {
          return CLICKABLE_CATEGORY.CONTROL;
        }
        if ([
          "checkbox",
          "radio",
          "tab",
          "menuitem",
          "option",
          "switch",
          "treeitem",
          "combobox",
          "spinbutton"
        ].includes(role)) {
          return CLICKABLE_CATEGORY.CONTROL;
        }
        if (el2.tagName === "INPUT") {
          return CLICKABLE_CATEGORY.CONTROL;
        }
        if (this.isLikelyInteractive(el2)) return CLICKABLE_CATEGORY.GENERIC;
        return CLICKABLE_CATEGORY.NONE;
      } catch {
        return CLICKABLE_CATEGORY.NONE;
      }
    }
    /**
     * Whether this category should get "link-style" hover/activation chrome:
     * scale-up pulse, link key hints, navigational affordances.
     * Sliders and media surfaces are excluded.
     * @param {string} category
     * @returns {boolean}
     */
    isLinkStyleCategory(category) {
      return category === CLICKABLE_CATEGORY.LINK || category === CLICKABLE_CATEGORY.GENERIC;
    }
    /**
     * Whether focus fill (blue wash) should be suppressed for this category/element.
     * @param {Element|null} el
     * @returns {boolean}
     */
    shouldSuppressFocusFillForElement(el2) {
      const cat = this.getClickableCategory(el2);
      if (cat === CLICKABLE_CATEGORY.SLIDER) return true;
      if (cat === CLICKABLE_CATEGORY.TEXT) return true;
      if (cat === CLICKABLE_CATEGORY.MEDIA) {
        try {
          let root = el2;
          if (el2?.tagName === "VIDEO" && el2.parentElement) root = el2.parentElement;
          let n = root;
          let depth = 0;
          while (n && n.nodeType === 1 && depth < 5) {
            if (n === document.body || n === document.documentElement) break;
            if (n.querySelector?.('input[type="range"], [role="slider"], [role="progressbar"]')) {
              return true;
            }
            try {
              const r = n.getBoundingClientRect();
              if (r.height > window.innerHeight * 0.85 && r.width > window.innerWidth * 0.85) break;
            } catch {
            }
            n = n.parentElement;
            depth++;
          }
        } catch {
        }
        return false;
      }
      return false;
    }
  };

  // src/modules/activation-handler.js
  var ActivationHandler = class {
    constructor(elementDetector) {
      this.detector = elementDetector;
    }
    /**
     * If the given element is inside a <summary>, return that <summary>.
     * Clicking a child inside <summary> does NOT trigger <details> toggling because
     * the activation behavior is on the <summary> element itself (not ancestors).
     * @param {any} el
     * @returns {HTMLElement|null}
     */
    resolveSummaryActivator(el2) {
      try {
        if (!el2 || el2.nodeType !== 1) return null;
        const element = (
          /** @type {HTMLElement} */
          el2
        );
        if (element.tagName === "SUMMARY") return element;
        if (typeof element.closest === "function") {
          const summary = element.closest("summary");
          if (summary && summary.tagName === "SUMMARY") return (
            /** @type {HTMLElement} */
            summary
          );
        }
        if (element.tagName === "DETAILS") {
          const summary = element.querySelector(":scope > summary");
          if (summary && summary.tagName === "SUMMARY") return (
            /** @type {HTMLElement} */
            summary
          );
        }
      } catch {
      }
      return null;
    }
    /**
     * Native <select> dropdowns ignore untrusted MouseEvents. Click Element must
     * use showPicker() (or HTMLElement.click()) during the F-key user gesture.
     * @param {any} el
     * @returns {HTMLSelectElement|null}
     */
    resolveSelectActivator(el2) {
      try {
        if (!el2 || el2.nodeType !== 1) return null;
        const element = (
          /** @type {HTMLElement} */
          el2
        );
        if (element.tagName === "SELECT") return (
          /** @type {HTMLSelectElement} */
          element
        );
        if (typeof element.closest === "function") {
          const sel = element.closest("select");
          if (sel && sel.tagName === "SELECT") return (
            /** @type {HTMLSelectElement} */
            sel
          );
        }
      } catch {
      }
      return null;
    }
    /**
     * @param {HTMLSelectElement} selectEl
     * @returns {boolean}
     */
    handleSelect(selectEl) {
      if (!selectEl || selectEl.disabled) return false;
      try {
        selectEl.focus({ preventScroll: true });
      } catch {
        try {
          selectEl.focus();
        } catch {
        }
      }
      try {
        if (typeof selectEl.showPicker === "function") {
          selectEl.showPicker();
          return true;
        }
      } catch {
      }
      try {
        selectEl.click();
      } catch {
      }
      return true;
    }
    /**
     * Dispatch a "realistic" click sequence (pointer + mouse) with coordinates.
     * Many webapps (e.g. Internet Archive BookReader) rely on clientX/clientY to
     * decide behavior (page-turn zones), and `HTMLElement.click()` does not carry
     * meaningful coordinates.
     */
    dispatchClickSequence(target, clientX, clientY) {
      if (!target) return;
      const common = {
        bubbles: true,
        cancelable: true,
        composed: true,
        view: window,
        clientX,
        clientY,
        button: 0,
        buttons: 1
      };
      const hasPointer = typeof window.PointerEvent === "function";
      if (hasPointer) {
        const pCommon = {
          ...common,
          pointerId: 1,
          pointerType: "mouse",
          isPrimary: true
        };
        try {
          target.dispatchEvent(new PointerEvent("pointerover", pCommon));
        } catch {
        }
        try {
          target.dispatchEvent(new PointerEvent("pointerenter", pCommon));
        } catch {
        }
        try {
          target.dispatchEvent(new PointerEvent("pointerdown", pCommon));
        } catch {
        }
      } else {
        try {
          target.dispatchEvent(new MouseEvent("pointerover", common));
        } catch {
        }
        try {
          target.dispatchEvent(new MouseEvent("pointerenter", common));
        } catch {
        }
        try {
          target.dispatchEvent(new MouseEvent("pointerdown", common));
        } catch {
        }
      }
      try {
        target.dispatchEvent(new MouseEvent("mouseover", common));
      } catch {
      }
      try {
        target.dispatchEvent(new MouseEvent("mouseenter", common));
      } catch {
      }
      try {
        target.dispatchEvent(new MouseEvent("mousemove", common));
      } catch {
      }
      try {
        target.dispatchEvent(new MouseEvent("mousedown", common));
      } catch {
      }
      const commonUp = { ...common, buttons: 0 };
      if (hasPointer) {
        const pUp = { ...commonUp, pointerId: 1, pointerType: "mouse", isPrimary: true };
        try {
          target.dispatchEvent(new PointerEvent("pointerup", pUp));
        } catch {
        }
      } else {
        try {
          target.dispatchEvent(new MouseEvent("pointerup", commonUp));
        } catch {
        }
      }
      try {
        target.dispatchEvent(new MouseEvent("mouseup", commonUp));
      } catch {
      }
      try {
        target.dispatchEvent(new MouseEvent("click", commonUp));
      } catch {
      }
    }
    smartClick(el2, clientX, clientY, openInNewTab = false) {
      if (!el2) return false;
      try {
        const scrub = this.detector.resolveScrubber?.(el2, clientX, clientY);
        if (scrub?.control) {
          if (scrub.kind === "range" || this.detector.isNativeType(scrub.control, "range")) {
            return this.handleRange(scrub.control, clientX, clientY, scrub.track);
          }
          if (scrub.kind === "role-slider") {
            return this.handleRoleSlider(scrub.control, clientX, clientY, scrub.track);
          }
          return this.handleScrubberTrack(scrub.track || scrub.control, clientX, clientY);
        }
      } catch {
      }
      let activator = el2;
      const summaryActivator = this.resolveSummaryActivator(el2);
      if (summaryActivator) {
        activator = summaryActivator;
      }
      if (el2.closest) {
        let specificClickable;
        if (el2.tagName === "VIDEO" || el2.tagName === "AUDIO") {
          specificClickable = el2.closest("a[href]");
          if (specificClickable) {
            console.log("[KeyPilot] Found parent link for video/audio element:", specificClickable.href);
          }
        }
        if (!specificClickable) {
          if (!summaryActivator) {
            specificClickable = el2.closest('a[href], button, [role="button"], [onclick], [tabindex]');
          }
        }
        if (specificClickable) {
          activator = specificClickable;
        }
      }
      if (activator.tagName === "A" && activator.href) {
        if (openInNewTab) {
          console.log("[KeyPilot] Activating link in new tab:", activator.href);
          const originalTarget = activator.target;
          activator.target = "_blank";
          try {
            activator.click();
            return true;
          } catch (error) {
            console.log("[KeyPilot] Programmatic click failed, using window.open:", error);
            window.open(activator.href, "_blank");
            return true;
          } finally {
            if (originalTarget !== void 0) {
              activator.target = originalTarget;
            } else {
              activator.removeAttribute("target");
            }
          }
        } else {
          console.log("[KeyPilot] Activating link in same window:", activator.href);
          const originalTarget = activator.target;
          activator.target = "_self";
          try {
            activator.click();
            return true;
          } catch (error) {
            console.log("[KeyPilot] Programmatic click failed, using navigation:", error);
            window.location.href = activator.href;
            return true;
          } finally {
            if (originalTarget !== void 0) {
              activator.target = originalTarget;
            } else {
              activator.removeAttribute("target");
            }
          }
        }
      }
      let pointTarget = null;
      try {
        if (Number.isFinite(clientX) && Number.isFinite(clientY) && this.detector?.deepElementFromPoint) {
          pointTarget = this.detector.deepElementFromPoint(clientX, clientY);
        }
      } catch {
      }
      const pointSummary = this.resolveSummaryActivator(pointTarget);
      if (pointSummary) activator = pointSummary;
      if (activator && activator.tagName === "SUMMARY") {
        try {
          activator.click();
        } catch {
        }
        return true;
      }
      const primary = pointTarget || el2 || activator;
      this.dispatchClickSequence(primary, clientX, clientY);
      try {
        const primaryNode = (
          /** @type {any} */
          primary
        );
        const activatorEl = (
          /** @type {any} */
          activator
        );
        const sameControl = !!(activatorEl && primaryNode && this.detector && typeof this.detector.composedContains === "function" && (this.detector.composedContains(activatorEl, primaryNode) || this.detector.composedContains(primaryNode, activatorEl)));
        if (activator && activator !== primary && !sameControl) {
          this.dispatchClickSequence(activator, clientX, clientY);
        }
      } catch {
      }
      return true;
    }
    handleSmartActivate(target, x, y, openInNewTab = false) {
      if (!target) return false;
      target = this.resolveLabel(target);
      const selectEl = this.resolveSelectActivator(target);
      if (selectEl) {
        return this.handleSelect(selectEl);
      }
      if ((target.tagName === "VIDEO" || target.tagName === "AUDIO") && target.closest) {
        const parentLink = target.closest("a[href]");
        if (parentLink) {
          console.log("[KeyPilot] Video/audio wrapped in link, deferring to link activation");
          return false;
        }
      }
      try {
        const scrub = this.detector.resolveScrubber?.(target, x, y);
        if (scrub?.control) {
          if (scrub.kind === "range" || this.detector.isNativeType(scrub.control, "range")) {
            return this.handleRange(scrub.control, x, y, scrub.track);
          }
          if (scrub.kind === "role-slider") {
            return this.handleRoleSlider(scrub.control, x, y, scrub.track);
          }
          if (scrub.kind === "track") {
            return this.handleScrubberTrack(scrub.track || scrub.control, x, y);
          }
        }
      } catch {
      }
      if (this.detector.isNativeType(target, "radio")) {
        return this.handleRadio(target);
      }
      if (this.detector.isNativeType(target, "checkbox")) {
        return this.handleCheckbox(target);
      }
      if (this.detector.isNativeType(target, "range")) {
        return this.handleRange(target, x, y);
      }
      const role = target.getAttribute && (target.getAttribute("role") || "").trim().toLowerCase() || "";
      if (role === "slider") {
        return this.handleRoleSlider(target, x, y);
      }
      if (this.detector.isTextLike(target)) {
        return this.handleTextField(target);
      }
      if (this.detector.isContentEditable(target)) {
        return this.handleContentEditable(target);
      }
      if (target.tagName === "VIDEO" || target.tagName === "AUDIO") {
        return this.handleMediaElement(target);
      }
      return false;
    }
    resolveLabel(target) {
      let label = null;
      try {
        if (target && target.tagName === "LABEL") label = target;
        else if (target && typeof target.closest === "function") label = target.closest("label");
      } catch {
      }
      if (label && label.tagName === "LABEL") {
        const forId = label.getAttribute("for");
        if (forId) {
          const labelCtl = (label.getRootNode() || document).getElementById(forId);
          if (labelCtl) return labelCtl;
        } else {
          const ctl = label.querySelector("input, textarea, select");
          if (ctl) return ctl;
        }
      }
      return target;
    }
    handleRadio(target) {
      if (target.checked) return true;
      try {
        if (typeof target.click === "function") target.click();
      } catch {
      }
      if (!target.checked) {
        target.checked = true;
        this.dispatchInputChange(target);
      }
      return true;
    }
    handleCheckbox(target) {
      const before = !!target.checked;
      try {
        if (typeof target.click === "function") target.click();
      } catch {
      }
      if (target.checked === before) {
        target.checked = !target.checked;
        this.dispatchInputChange(target);
      }
      return true;
    }
    /**
     * Geometry used to map clientX → slider value.
     * Thumb-sized range inputs are only ~12px wide and positioned with left:%;
     * value math must use the full track host rect instead of the thumb box.
     */
    getSliderMetricsRect(control, trackHint = null) {
      let track = trackHint;
      try {
        if (!track && this.detector.getScrubTrackElement) {
          track = this.detector.getScrubTrackElement(control);
        }
      } catch {
      }
      let controlRect = null;
      let trackRect = null;
      try {
        controlRect = control.getBoundingClientRect();
      } catch {
        controlRect = null;
      }
      try {
        trackRect = track && track !== control ? track.getBoundingClientRect() : null;
      } catch {
        trackRect = null;
      }
      if (trackRect && trackRect.width > 0 && controlRect && controlRect.width > 0) {
        if (trackRect.width >= controlRect.width * 2.5) return trackRect;
      }
      if (trackRect && trackRect.width > 40 && (!controlRect || controlRect.width < 24)) {
        return trackRect;
      }
      if (controlRect && controlRect.width > 0) return controlRect;
      return trackRect;
    }
    /**
     * Closest slider / range host for a hit target (time scrubber, volume, etc.).
     * Used to scope synthetic drag-end events to one control.
     * @param {EventTarget|null|undefined} originTarget
     * @returns {Element|null}
     */
    resolveSliderRoot(originTarget) {
      try {
        const el2 = (
          /** @type {Element|null} */
          originTarget && /** @type {any} */
          originTarget.nodeType === 1 ? originTarget : null
        );
        if (!el2) return null;
        if (typeof el2.closest === "function") {
          const root = el2.closest(
            '[role="slider"], input[type="range"], [data-media-time-slider], .vds-slider, .vds-time-slider'
          );
          if (root) return root;
        }
        return el2;
      } catch {
        return null;
      }
    }
    /**
     * True when this control is a volume / non-timeline slider.
     * Volume must not trigger media.currentTime seeks or full-page drag teardown.
     * @param {Element|null|undefined} el
     * @returns {boolean}
     */
    isVolumeOrNonSeekSlider(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      try {
        const label = `${el2.getAttribute("aria-label") || ""} ${el2.getAttribute("aria-valuetext") || ""}`.toLowerCase();
        if (/\b(volume|mute|sound|loudness|gain)\b/.test(label)) return true;
        try {
          if (el2.hasAttribute("data-media-volume-slider")) return true;
        } catch {
        }
        const cls = String(
          /** @type {any} */
          el2.className || ""
        ).toLowerCase();
        if (/\b(volume|mute)[-_]?slider\b|\bvolume-?control\b|\bvds-volume\b/.test(cls)) return true;
        if (typeof el2.closest === "function") {
          const host = el2.closest(
            '[aria-label*="volume" i], [aria-label*="mute" i], [data-media-volume-slider], .vds-volume-slider'
          );
          if (host) return true;
        }
      } catch {
      }
      return false;
    }
    /**
     * Force-end a synthetic pointer drag on ONE slider tree only.
     *
     * Media scrubbers (Vidstack/vds-slider, custom React players like Suno) call
     * `setPointerCapture` on pointerdown and keep seeking until the captured
     * pointerup. Synthetic events cannot capture a real pointer id
     * (`NotFoundError: No active pointer…`), so the initial element-level up in
     * `dispatchClickSequence` often leaves `data-dragging` / isSeeking true and
     * the knob sticks to the real mouse.
     *
     * We re-fire pointerup/mouseup/pointercancel on the activated slider subtree
     * after React commits drag-start. We intentionally do NOT dispatch on
     * document/window — that can end (or drive) other sliders in the same player
     * (e.g. volume drag affecting the time scrubber).
     *
     * @param {number} clientX
     * @param {number} clientY
     * @param {number} [pointerId=1] Must match the id used in dispatchClickSequence.
     * @param {EventTarget|null} [originTarget] Element that received the down.
     */
    endSyntheticPointerDrag(clientX, clientY, pointerId = 1, originTarget = null) {
      const x = Number.isFinite(clientX) ? clientX : 0;
      const y = Number.isFinite(clientY) ? clientY : 0;
      const id = Number.isFinite(pointerId) ? pointerId : 1;
      const sliderRoot = this.resolveSliderRoot(originTarget);
      const upCommon = {
        bubbles: true,
        cancelable: true,
        composed: true,
        view: window,
        clientX: x,
        clientY: y,
        button: 0,
        buttons: 0
      };
      const pUp = {
        ...upCommon,
        pointerId: id,
        pointerType: "mouse",
        isPrimary: true
      };
      const hasPointer = typeof window.PointerEvent === "function";
      const targets = /* @__PURE__ */ new Set();
      try {
        let n = originTarget && /** @type {any} */
        originTarget.nodeType === 1 ? (
          /** @type {Element} */
          originTarget
        ) : null;
        let depth = 0;
        while (n && n.nodeType === 1 && depth < 12) {
          targets.add(n);
          if (sliderRoot && n === sliderRoot) break;
          if (!sliderRoot && (n.parentElement === document.body || n === document.documentElement)) break;
          n = n.parentElement;
          depth++;
        }
      } catch {
      }
      if (sliderRoot) targets.add(sliderRoot);
      if (originTarget) targets.add(originTarget);
      for (const t of targets) {
        if (!t || typeof t.dispatchEvent !== "function") continue;
        if (hasPointer) {
          try {
            t.dispatchEvent(new PointerEvent("pointerup", pUp));
          } catch {
          }
          try {
            t.dispatchEvent(new PointerEvent("pointercancel", pUp));
          } catch {
          }
        } else {
          try {
            t.dispatchEvent(new MouseEvent("pointerup", upCommon));
          } catch {
          }
          try {
            t.dispatchEvent(new MouseEvent("pointercancel", upCommon));
          } catch {
          }
        }
        try {
          t.dispatchEvent(new MouseEvent("mouseup", upCommon));
        } catch {
        }
      }
      try {
        for (const t of targets) {
          if (!t || typeof /** @type {any} */
          t.hasPointerCapture !== "function") continue;
          try {
            if (
              /** @type {any} */
              t.hasPointerCapture(id)
            ) {
              t.releasePointerCapture(id);
            }
          } catch {
          }
        }
      } catch {
      }
    }
    /**
     * Pointer/mouse down+up at coordinates.
     * Custom media scrubbers listen for this (not for programmatic range.value).
     * mouseup is required on some players to commit the seek.
     */
    dispatchPointerSeek(target, clientX, clientY) {
      if (!target) return;
      const clientYSafe = Number.isFinite(clientY) ? clientY : (() => {
        try {
          const r = target.getBoundingClientRect();
          return r.top + r.height / 2;
        } catch {
          return 0;
        }
      })();
      this.dispatchClickSequence(target, clientX, clientYSafe);
      const endDrag = () => {
        try {
          this.endSyntheticPointerDrag(clientX, clientYSafe, 1, target);
        } catch {
        }
      };
      try {
        queueMicrotask(endDrag);
      } catch {
        endDrag();
      }
      try {
        setTimeout(endDrag, 0);
      } catch {
      }
    }
    /**
     * True when the range input is a positioned thumb and a separate wider track owns seek geometry
     * (common custom players: small input with left:%, full-width visual track sibling).
     */
    isThumbStyleRange(control, track) {
      if (!control || !track || track === control) return false;
      try {
        const cr = control.getBoundingClientRect();
        const tr = track.getBoundingClientRect();
        if (!cr || !tr || cr.width <= 0 || tr.width <= 0) return false;
        if (this.detector?.isNativeType?.(control, "range") && cr.width >= 40) return false;
        return tr.width >= cr.width * 2.5;
      } catch {
        return false;
      }
    }
    /**
     * Containment root for "is under-point inside this scrubber?" checks.
     * Expands one level to a short track host (presentation + thumb wrapper) without
     * expanding into a full playbar that would include play/volume buttons.
     */
    getSeekContainmentRoot(track) {
      if (!track || track.nodeType !== 1) return track;
      try {
        const parent = track.parentElement;
        if (!parent || parent === document.body || parent === document.documentElement) {
          return track;
        }
        const pr = parent.getBoundingClientRect();
        if (pr.height > 0 && pr.height <= 48 && pr.width >= 64) {
          return parent;
        }
      } catch {
      }
      return track;
    }
    /**
     * Prefer the real hit-target under the cursor (progress fill / remaining bar).
     * Synthetic events on the outer presentation wrapper alone often move the knob
     * UI without committing media seek; events on the under-point node do both.
     */
    resolveSeekPointTarget(track, clientX, clientY) {
      if (!track) return track;
      try {
        if (Number.isFinite(clientX) && Number.isFinite(clientY) && this.detector?.deepElementFromPoint) {
          const under = this.detector.deepElementFromPoint(clientX, clientY);
          if (!under) return track;
          const root = this.getSeekContainmentRoot(track) || track;
          if (under === track || under === root || typeof root.contains === "function" && root.contains(under) || typeof track.contains === "function" && track.contains(under)) {
            return under;
          }
        }
      } catch {
      }
      return track;
    }
    /**
     * Find <audio>/<video> associated with a scrubber (same player shell, or duration ≈ range max).
     * @param {Element} fromEl
     * @param {HTMLInputElement|null} [rangeEl]
     * @returns {HTMLMediaElement|null}
     */
    findAssociatedMedia(fromEl, rangeEl = null) {
      try {
        let n = fromEl;
        let depth = 0;
        while (n && n.nodeType === 1 && depth < 8) {
          if (n === document.body || n === document.documentElement) break;
          if (n.tagName === "VIDEO" || n.tagName === "AUDIO") {
            return (
              /** @type {HTMLMediaElement} */
              n
            );
          }
          try {
            const list = n.querySelectorAll?.("audio, video");
            if (list && list.length) {
              let best = null;
              for (const m of list) {
                if (Number.isFinite(m.duration) && m.duration > 1) {
                  best = m;
                  break;
                }
                if (!best) best = m;
              }
              if (best) return (
                /** @type {HTMLMediaElement} */
                best
              );
            }
          } catch {
          }
          try {
            const r = n.getBoundingClientRect();
            if (r.height > window.innerHeight * 0.85 && r.width > window.innerWidth * 0.85) break;
          } catch {
          }
          n = n.parentElement;
          depth++;
        }
      } catch {
      }
      try {
        const max = rangeEl ? this.asNum(rangeEl.max, NaN) : NaN;
        if (Number.isFinite(max) && max > 1) {
          const all = document.querySelectorAll("audio, video");
          for (const m of all) {
            if (Number.isFinite(m.duration) && Math.abs(m.duration - max) < 1.5) {
              return (
                /** @type {HTMLMediaElement} */
                m
              );
            }
          }
        }
      } catch {
      }
      return null;
    }
    /**
     * Soft fallback: set media.currentTime from cursor position along the track.
     * Used after pointer seek for custom players that update the knob but not media.
     * Never runs for volume / non-timeline sliders (would scrub the video when adjusting volume).
     */
    maybeSeekAssociatedMedia(trackEl, clientX, clientY, rangeEl = null) {
      try {
        if (!trackEl || !Number.isFinite(clientX)) return;
        if (this.isVolumeOrNonSeekSlider(trackEl) || this.isVolumeOrNonSeekSlider(rangeEl)) return;
        try {
          const root = this.resolveSliderRoot(trackEl);
          if (root && this.isVolumeOrNonSeekSlider(root)) return;
        } catch {
        }
        const media = this.findAssociatedMedia(trackEl, rangeEl);
        if (!media || !Number.isFinite(media.duration) || media.duration <= 0) return;
        const metricsEl = this.getSeekContainmentRoot(trackEl) || trackEl;
        const rect = metricsEl.getBoundingClientRect();
        if (!rect || rect.width <= 0) return;
        if (rect.width < 120) return;
        const pct = this.clamp((clientX - rect.left) / rect.width, 0, 1);
        const targetTime = pct * media.duration;
        if (Math.abs(media.currentTime - targetTime) < 0.35) return;
        media.currentTime = targetTime;
      } catch {
      }
    }
    /**
     * Generic custom-scrubber seek: one pointer sequence on the element under the cursor.
     * Do not set range.value from our geometry, do not fire on a tiny thumb input,
     * do not double-dispatch on track + fill.
     */
    seekCustomScrubber(track, clientX, clientY, rangeEl = null) {
      if (!track) return false;
      const pointTarget = this.resolveSeekPointTarget(track, clientX, clientY);
      this.dispatchPointerSeek(pointTarget, clientX, clientY);
      this.maybeSeekAssociatedMedia(track, clientX, clientY, rangeEl);
      return true;
    }
    handleRange(target, clientX, clientY = null, trackHint = null) {
      let track = trackHint;
      try {
        if (!track && this.detector.getScrubTrackElement) {
          track = this.detector.getScrubTrackElement(target);
        }
      } catch {
      }
      track = track || target;
      if (this.isThumbStyleRange(target, track)) {
        return this.seekCustomScrubber(track, clientX, clientY, target);
      }
      const rect = this.getSliderMetricsRect(target, track);
      const min = this.asNum(target.min, 0);
      const max = this.asNum(target.max, 100);
      const stepAttr = target.step && target.step !== "any" ? this.asNum(target.step, 1) : "any";
      if (rect && rect.width > 0) {
        const pct = this.clamp((clientX - rect.left) / rect.width, 0, 1);
        let val = min + pct * (max - min);
        if (stepAttr !== "any" && Number.isFinite(stepAttr) && stepAttr > 0) {
          const steps = Math.round((val - min) / stepAttr);
          val = min + steps * stepAttr;
        }
        val = this.clamp(val, min, max);
        const before = target.value;
        try {
          target.value = String(val);
          if (target.value !== before) this.dispatchInputChange(target);
        } catch {
        }
      }
      this.dispatchPointerSeek(target, clientX, clientY);
      return true;
    }
    handleRoleSlider(target, clientX, clientY, trackHint = null) {
      const rect = this.getSliderMetricsRect(target, trackHint || target);
      if (rect && rect.width > 0) {
        const min = this.asNum(target.getAttribute("aria-valuemin"), 0);
        const max = this.asNum(target.getAttribute("aria-valuemax"), 100);
        const step = this.asNum(target.getAttribute("aria-step"), 1);
        const pct = this.clamp((clientX - rect.left) / rect.width, 0, 1);
        let newValue = min + pct * (max - min);
        if (step > 0) {
          const steps = Math.round((newValue - min) / step);
          newValue = min + steps * step;
        }
        newValue = this.clamp(newValue, min, max);
        const before = target.getAttribute("aria-valuenow");
        try {
          target.setAttribute("aria-valuenow", String(newValue));
        } catch {
        }
        if (String(newValue) !== before) {
          this.dispatchInputChange(target);
          try {
            target.dispatchEvent(new CustomEvent("sliderchange", {
              bubbles: true,
              detail: { value: newValue, previousValue: this.asNum(before, min) }
            }));
          } catch {
          }
        }
      }
      const seekRoot = trackHint && trackHint !== target ? trackHint : target;
      const pointTarget = this.resolveSeekPointTarget(seekRoot, clientX, clientY);
      const sequenceTarget = pointTarget || target;
      this.dispatchPointerSeek(sequenceTarget, clientX, clientY);
      if (!this.isVolumeOrNonSeekSlider(target) && !this.isVolumeOrNonSeekSlider(seekRoot)) {
        this.maybeSeekAssociatedMedia(seekRoot, clientX, clientY, null);
      }
      return true;
    }
    /**
     * Custom scrub tracks with no input/role (pure div progress bars).
     * One pointer sequence on the under-cursor hit target.
     */
    handleScrubberTrack(track, clientX, clientY) {
      if (!track) return false;
      return this.seekCustomScrubber(track, clientX, clientY, null);
    }
    handleTextField(target) {
      try {
        target.focus({ preventScroll: true });
      } catch {
        try {
          target.focus();
        } catch {
        }
      }
      try {
        const v = target.value ?? "";
        target.setSelectionRange(v.length, v.length);
      } catch {
      }
      return true;
    }
    handleContentEditable(target) {
      try {
        target.focus({ preventScroll: true });
      } catch {
        try {
          target.focus();
        } catch {
        }
      }
      try {
        const selection = window.getSelection();
        const range = document.createRange();
        if (target.childNodes.length > 0) {
          const lastNode = target.childNodes[target.childNodes.length - 1];
          if (lastNode.nodeType === Node.TEXT_NODE) {
            range.setStart(lastNode, lastNode.textContent.length);
          } else {
            range.setStartAfter(lastNode);
          }
        } else {
          range.setStart(target, 0);
        }
        range.collapse(true);
        selection.removeAllRanges();
        selection.addRange(range);
      } catch (error) {
        console.debug("Could not position cursor in contenteditable:", error);
      }
      return true;
    }
    handleMediaElement(target) {
      try {
        if (target.paused) {
          target.play();
        } else {
          target.pause();
        }
        return true;
      } catch (error) {
        console.debug("Could not control media element:", error);
        return false;
      }
    }
    dispatchInputChange(el2) {
      const opts = { bubbles: true, composed: true };
      el2.dispatchEvent(new Event("input", opts));
      el2.dispatchEvent(new Event("change", opts));
    }
    clamp(n, lo, hi) {
      return Math.min(hi, Math.max(lo, n));
    }
    asNum(v, d) {
      const n = Number(v);
      return Number.isFinite(n) ? n : d;
    }
  };

  // src/modules/focus-detector.js
  init_constants();
  var FocusDetector = class {
    constructor(stateManager, mouseCoordinateManager = null) {
      this.state = stateManager;
      this.mouseCoordinateManager = mouseCoordinateManager;
      this.currentFocusedElement = null;
      this.textElementObserver = null;
      this.textElementResizeObserver = null;
      this.documentObserver = null;
      this._disconnectObserver = null;
      this.rafId = null;
      this._onFocusIn = this.handleFocusIn.bind(this);
      this._onFocusOut = this.handleFocusOut.bind(this);
      this._shadowFocusRoot = null;
      this._onShadowFocusIn = this.handleFocusIn.bind(this);
      this._onShadowFocusOut = this.handleFocusOut.bind(this);
    }
    start() {
      if (window.KEYPILOT_DEBUG) console.log("[KeyPilot] FocusDetector starting...");
      document.addEventListener("focusin", this._onFocusIn, true);
      document.addEventListener("focusout", this._onFocusOut, true);
      this.setupDocumentObserver();
      try {
        this.checkCurrentFocus();
      } catch {
      }
      setTimeout(() => {
        this.checkCurrentFocus();
        if (window.KEYPILOT_DEBUG) console.log("[KeyPilot] Initial focus check completed");
      }, 100);
      setTimeout(() => this.checkCurrentFocus(), 500);
      if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", () => {
          try {
            this.checkCurrentFocus();
          } catch {
          }
          setTimeout(() => this.checkCurrentFocus(), 100);
        });
      }
      window.addEventListener("load", () => {
        try {
          this.checkCurrentFocus();
        } catch {
        }
        setTimeout(() => this.checkCurrentFocus(), 100);
      });
      try {
        window.addEventListener("focus", () => {
          setTimeout(() => this.checkCurrentFocus(), 0);
        });
      } catch {
      }
    }
    stop() {
      document.removeEventListener("focusin", this._onFocusIn, true);
      document.removeEventListener("focusout", this._onFocusOut, true);
      this._detachShadowFocusBridge();
      this.cleanupTextElementObservers();
      this.cleanupDocumentObserver();
      if (this.currentFocusedElement) {
        this.currentFocusedElement = null;
      }
    }
    handleFocusIn(e) {
      const composed = kpGetComposedEventTarget(e);
      const deep = this.getDeepActiveElement();
      const candidate = this.isTextInput(composed) ? composed : this.isTextInput(deep) ? deep : null;
      if (window.KEYPILOT_DEBUG) {
        console.log(
          "[KeyPilot] FocusIn event:",
          candidate?.tagName || e.target?.tagName,
          candidate?.type || e.target?.type || "N/A",
          "id:",
          candidate?.id || e.target?.id || "none"
        );
      }
      if (candidate) {
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot] Text input focused - setting text mode:", candidate.tagName, candidate.type || "N/A");
        }
        this.setTextFocus(candidate);
      }
    }
    handleFocusOut(e) {
      const composed = kpGetComposedEventTarget(e);
      let leftText = this.isTextInput(composed) || this.isTextInput(e.target) || this.currentFocusedElement && e.target === this.currentFocusedElement;
      if (!leftText && this.currentFocusedElement) {
        try {
          const tag = String(e.target?.tagName || "").toUpperCase();
          if (tag === "IFRAME" || tag === "FRAME") leftText = true;
        } catch {
        }
      }
      if (leftText) {
        console.debug("Text input blurred:", composed?.tagName || e.target?.tagName, composed?.type || e.target?.type || "N/A");
        if (this._clearTextFocusIfDisconnected()) return;
        setTimeout(() => {
          if (this._clearTextFocusIfDisconnected()) return;
          const currentlyFocused = this.getDeepActiveElement();
          console.debug("Focus check after blur - currently focused:", currentlyFocused?.tagName, currentlyFocused?.type, currentlyFocused?.id);
          if (!this.isTextInput(currentlyFocused)) {
            console.debug("Clearing text focus - no text input currently focused");
            this.clearTextFocus();
          } else {
            if (currentlyFocused !== this.currentFocusedElement) {
              this.setTextFocus(currentlyFocused);
            } else {
              console.debug("Keeping text focus - text input still focused");
            }
          }
        }, 100);
      }
    }
    checkCurrentFocus() {
      if (this._clearTextFocusIfDisconnected()) return;
      const activeElement = this.getDeepActiveElement();
      if (this.isTextInput(activeElement)) {
        if (this.currentFocusedElement !== activeElement) {
          console.debug("Text focus detected during check:", activeElement.tagName, activeElement.type || "N/A", "ID:", activeElement.id || "none");
          this.setTextFocus(activeElement);
        }
      } else if (this.currentFocusedElement) {
        console.debug("Text focus cleared during check");
        this.clearTextFocus();
      }
    }
    /**
     * Text-mode field chrome is element-styled; fixed labels track via
     * OptimizedScrollManager + ResizeObserver/input/mutation observers.
     * Perpetual rAF getBoundingClientRect loops are no longer needed.
     */
    startPositionTracking() {
      this.stopPositionTracking();
      if (this.currentFocusedElement) {
        try {
          this.lastKnownRect = this.currentFocusedElement.getBoundingClientRect();
        } catch {
        }
      }
    }
    stopPositionTracking() {
      if (this.rafId) {
        cancelAnimationFrame(this.rafId);
        this.rafId = null;
        console.debug("Position tracking stopped");
      }
    }
    setupDocumentObserver() {
      if (!window.MutationObserver) return;
      this.documentObserver = new MutationObserver((mutations) => {
        let shouldCheckFocus = false;
        mutations.forEach((mutation) => {
          if (mutation.type === "attributes") {
            const attrName = mutation.attributeName;
            if (attrName === "contenteditable") {
              shouldCheckFocus = true;
            }
          }
        });
        if (shouldCheckFocus) {
          if (this.focusCheckTimeout) {
            clearTimeout(this.focusCheckTimeout);
          }
          this.focusCheckTimeout = setTimeout(() => {
            this.checkCurrentFocus();
            this.focusCheckTimeout = null;
          }, 100);
        }
      });
      this.documentObserver.observe(document, {
        attributes: true,
        attributeFilter: ["contenteditable"],
        subtree: true
      });
      console.debug("Document MutationObserver set up for focus-related changes");
    }
    cleanupDocumentObserver() {
      if (this.documentObserver) {
        this.documentObserver.disconnect();
        this.documentObserver = null;
      }
      if (this.focusCheckTimeout) {
        clearTimeout(this.focusCheckTimeout);
        this.focusCheckTimeout = null;
      }
      console.debug("Document observer cleaned up");
    }
    getDeepActiveElement() {
      return kpGetDeepActiveElement();
    }
    isTextInput(element) {
      if (!element || element.nodeType !== 1) return false;
      try {
        if (element.isConnected === false) return false;
        if (element.classList?.contains?.(CSS_CLASSES.OMNIBOX_INPUT)) return false;
        const omniboxRoot = element.closest?.(`.${CSS_CLASSES.OMNIBOX_BACKDROP}`) || element.closest?.(`.${CSS_CLASSES.OMNIBOX_PANEL}`);
        if (omniboxRoot) return false;
        return kpIsTypingContext(element);
      } catch {
        return false;
      }
    }
    _detachShadowFocusBridge() {
      const root = this._shadowFocusRoot;
      this._shadowFocusRoot = null;
      if (!root) return;
      try {
        root.removeEventListener("focusin", this._onShadowFocusIn, true);
      } catch {
      }
      try {
        root.removeEventListener("focusout", this._onShadowFocusOut, true);
      } catch {
      }
    }
    /**
     * Listen for focus moves that stay inside an open ShadowRoot (document
     * listeners never see those — the retargeted target stays the host).
     * @param {Element} element
     */
    _attachShadowFocusBridge(element) {
      let root = null;
      try {
        root = typeof element.getRootNode === "function" ? element.getRootNode() : null;
      } catch {
        root = null;
      }
      const isShadow = !!(root && typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot);
      if (!isShadow) {
        this._detachShadowFocusBridge();
        return;
      }
      if (this._shadowFocusRoot === root) return;
      this._detachShadowFocusBridge();
      this._shadowFocusRoot = root;
      try {
        root.addEventListener("focusin", this._onShadowFocusIn, true);
      } catch {
      }
      try {
        root.addEventListener("focusout", this._onShadowFocusOut, true);
      } catch {
      }
    }
    setTextFocus(element) {
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot] Setting text focus for element:", element.tagName, element.type || "N/A");
      }
      this.currentFocusedElement = element;
      this._attachShadowFocusBridge(element);
      this.setupTextElementObservers(element);
      this.positionCursorForTextFocus(element);
      this.startPositionTracking();
      this.state.setState({
        mode: "text_focus",
        focusedTextElement: element,
        focusEl: null
        // Clear to ensure hasClickableElement starts as false
      });
    }
    /**
     * Position cursor for text focus mode
     * Uses stored coordinates if available, otherwise positions underneath the text field
     * @param {Element} element - The focused text element
     */
    positionCursorForTextFocus(element) {
      if (!element || !this.mouseCoordinateManager) {
        return;
      }
      const storedCoordinates = this.mouseCoordinateManager.lastStoredCoordinates;
      if (storedCoordinates) {
        const x = Math.min(storedCoordinates.x, window.innerWidth - 20);
        const y = Math.min(storedCoordinates.y, window.innerHeight - 20);
        const validX = Math.max(10, x);
        const validY = Math.max(10, y);
        this.state.setMousePosition(validX, validY);
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot] Positioned cursor using stored coordinates for text focus:", {
            x: validX,
            y: validY
          });
        }
      } else {
        const rect = element.getBoundingClientRect();
        const x = rect.left + rect.width / 2;
        const y = rect.bottom + 10;
        const validX = Math.min(Math.max(10, x), window.innerWidth - 20);
        const validY = Math.min(Math.max(10, y), window.innerHeight - 20);
        this.state.setMousePosition(validX, validY);
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot] Positioned cursor underneath text field:", {
            x: validX,
            y: validY,
            elementRect: rect
          });
        }
      }
    }
    clearTextFocus() {
      this.stopPositionTracking();
      this._detachShadowFocusBridge();
      this.cleanupTextElementObservers();
      this.currentFocusedElement = null;
      this.state.setState({
        mode: "none",
        focusedTextElement: null
      });
    }
    isInTextFocus() {
      return this.state.getState().mode === "text_focus";
    }
    getFocusedElement() {
      return this.currentFocusedElement;
    }
    /**
     * If the field that put us in text mode was removed (inspector rebuild, SPA
     * replace, etc.), blur/focusout often never fires — especially on macOS where
     * clicking Save does not move focus off the input. Exit text mode immediately.
     * @returns {boolean} true when text mode was cleared
     */
    _clearTextFocusIfDisconnected() {
      const el2 = this.currentFocusedElement;
      if (!el2) return false;
      let connected = true;
      try {
        connected = el2.isConnected !== false;
      } catch {
        connected = false;
      }
      if (connected) return false;
      this.clearTextFocus();
      return true;
    }
    setupTextElementObservers(element) {
      if (!element) return;
      this.cleanupTextElementObservers();
      this._watchFocusedElementDisconnected(element);
      this.lastKnownRect = element.getBoundingClientRect();
      if (window.ResizeObserver) {
        this.textElementResizeObserver = new ResizeObserver((entries) => {
          if (this.resizeTimeout) {
            clearTimeout(this.resizeTimeout);
          }
          this.resizeTimeout = setTimeout(() => {
            this.triggerOverlayUpdate();
            this.resizeTimeout = null;
          }, 16);
        });
        this.textElementResizeObserver.observe(element);
        console.debug("ResizeObserver set up for text element");
      }
      this.inputEventHandler = () => {
        if (this.inputTimeout) {
          clearTimeout(this.inputTimeout);
        }
        this.inputTimeout = setTimeout(() => {
          console.debug("Input event detected - triggering overlay update");
          this.triggerOverlayUpdate();
          this.inputTimeout = null;
        }, 16);
      };
      element.addEventListener("input", this.inputEventHandler);
      console.debug("Input event listener set up for text element");
      if (window.MutationObserver) {
        this.textElementObserver = new MutationObserver((mutations) => {
          let shouldUpdate = false;
          mutations.forEach((mutation) => {
            if (mutation.type === "attributes") {
              const attrName = mutation.attributeName;
              if (attrName === "style" || attrName === "class") {
                console.debug("MutationObserver detected attribute change:", attrName, mutation.target.tagName);
                shouldUpdate = true;
              }
              if (["rows", "cols", "width", "height"].includes(attrName)) {
                console.debug("MutationObserver detected size attribute change:", attrName);
                shouldUpdate = true;
              }
            }
            if (mutation.type === "childList") {
              console.debug("MutationObserver detected DOM structure change");
              shouldUpdate = true;
            }
            if (mutation.type === "characterData") {
              console.debug("MutationObserver detected content change");
              shouldUpdate = true;
            }
          });
          if (shouldUpdate) {
            if (this.mutationTimeout) {
              clearTimeout(this.mutationTimeout);
            }
            this.mutationTimeout = setTimeout(() => {
              this.triggerOverlayUpdate();
              this.mutationTimeout = null;
            }, 16);
          }
        });
        this.textElementObserver.observe(element, {
          attributes: true,
          attributeOldValue: true,
          characterData: true,
          childList: true,
          subtree: true
        });
        let parent = element.parentElement;
        let observedParents = 0;
        while (parent && observedParents < 2) {
          this.textElementObserver.observe(parent, {
            attributes: true,
            attributeFilter: ["style", "class"],
            childList: true
          });
          parent = parent.parentElement;
          observedParents++;
        }
        console.debug("MutationObserver set up for element and", observedParents, "parents");
      }
    }
    /**
     * Watch ancestors of the focused field so removing it from the tree exits text mode.
     * @param {Element} element
     */
    _watchFocusedElementDisconnected(element) {
      if (!window.MutationObserver || !element) return;
      if (this._disconnectObserver) {
        this._disconnectObserver.disconnect();
        this._disconnectObserver = null;
      }
      let root = null;
      try {
        root = element.getRootNode?.() || null;
      } catch {
        root = null;
      }
      let target = null;
      try {
        if (root && root.nodeType === 9) {
          target = /** @type {Document} */
          root.documentElement || root;
        } else if (root && root !== document) {
          target = root;
        } else {
          target = document.documentElement;
        }
      } catch {
        target = document.documentElement;
      }
      if (!target || target.nodeType == null) return;
      this._disconnectObserver = new MutationObserver(() => {
        this._clearTextFocusIfDisconnected();
      });
      try {
        this._disconnectObserver.observe(target, { childList: true, subtree: true });
      } catch {
        this._disconnectObserver = null;
      }
    }
    cleanupTextElementObservers() {
      if (this._disconnectObserver) {
        this._disconnectObserver.disconnect();
        this._disconnectObserver = null;
      }
      if (this.textElementObserver) {
        this.textElementObserver.disconnect();
        this.textElementObserver = null;
      }
      if (this.textElementResizeObserver) {
        this.textElementResizeObserver.disconnect();
        this.textElementResizeObserver = null;
      }
      if (this.inputEventHandler && this.currentFocusedElement) {
        this.currentFocusedElement.removeEventListener("input", this.inputEventHandler);
        this.inputEventHandler = null;
      }
      this.stopPositionTracking();
      if (this.resizeTimeout) {
        clearTimeout(this.resizeTimeout);
        this.resizeTimeout = null;
      }
      if (this.mutationTimeout) {
        clearTimeout(this.mutationTimeout);
        this.mutationTimeout = null;
      }
      if (this.inputTimeout) {
        clearTimeout(this.inputTimeout);
        this.inputTimeout = null;
      }
      console.debug("Text element observers cleaned up");
    }
    triggerOverlayUpdate() {
      if (this.currentFocusedElement && this.state.getState().mode === "text_focus") {
        this.state.setState({
          mode: "text_focus",
          focusedTextElement: this.currentFocusedElement,
          _overlayUpdateTrigger: Date.now()
          // Unique value to force update
        });
        console.debug("Triggered overlay update for text element position/size change");
      }
    }
  };

  // src/modules/overlay-manager.js
  init_constants();

  // src/modules/inspector-mode.js
  init_constants();
  var INSPECTOR_DEFS = Object.freeze({
    [INSPECTOR_KIND.DELETE]: Object.freeze({
      kind: INSPECTOR_KIND.DELETE,
      label: "Delete",
      statusMode: "delete",
      cursorMode: "delete",
      hostClass: CSS_CLASSES.DELETE,
      borderColor: COLORS.DELETE_RED,
      shadowColor: COLORS.DELETE_SHADOW,
      shadowBrightColor: COLORS.DELETE_SHADOW_BRIGHT,
      allowHtmlBody: false,
      actionId: "DELETE",
      selectionMode: "single",
      cancelOnPointerDown: true,
      instructionTemplate: "Press {key} again to delete \xB7 Esc or click cancels"
    }),
    [INSPECTOR_KIND.COLS]: Object.freeze({
      kind: INSPECTOR_KIND.COLS,
      label: "Cols",
      statusMode: "cols",
      cursorMode: "cols",
      hostClass: CSS_CLASSES.COLS,
      borderColor: COLORS.COLS_PURPLE,
      shadowColor: COLORS.COLS_SHADOW,
      shadowBrightColor: COLORS.COLS_SHADOW_BRIGHT,
      allowHtmlBody: true,
      actionId: "COLS_TOGGLE",
      selectionMode: "single",
      instructionTemplate: "Hover an element \xB7 Press {key} again to columnize \xB7 Esc cancels"
    }),
    [INSPECTOR_KIND.RECTANGLE_PICK]: Object.freeze({
      kind: INSPECTOR_KIND.RECTANGLE_PICK,
      label: "Select",
      statusMode: "highlight",
      cursorMode: "highlight",
      hostClass: CSS_CLASSES.HIGHLIGHT,
      borderColor: COLORS.HIGHLIGHT_BLUE,
      shadowColor: COLORS.HIGHLIGHT_SHADOW,
      shadowBrightColor: COLORS.HIGHLIGHT_SHADOW_BRIGHT,
      allowHtmlBody: false,
      actionId: "RECTANGLE_HIGHLIGHT",
      selectionMode: "cumulative",
      cancelOnPointerDown: true,
      instructionTemplate: "Press {key} to add \xB7 Enter to finish \xB7 Esc or click cancels"
    })
  });
  function getInspectorInstructionText(kind, confirmKey) {
    const def = getInspectorDef(kind);
    const key = String(confirmKey || (kind === INSPECTOR_KIND.COLS ? "." : "Backspace") || "").trim() || "?";
    const template = def?.instructionTemplate || "Press {key} again \xB7 Esc cancels";
    return template.replace(/\{key\}/g, key);
  }
  function getInspectorDef(kind) {
    if (!kind) return null;
    return INSPECTOR_DEFS[kind] || null;
  }
  function getInspectorCursorMode(kind) {
    return getInspectorDef(kind)?.cursorMode || "inspector";
  }
  function getInspectorStatusMode(kind) {
    return getInspectorDef(kind)?.statusMode || "inspector";
  }
  function getAllInspectorHostClasses() {
    const set = /* @__PURE__ */ new Set();
    for (const def of Object.values(INSPECTOR_DEFS)) {
      if (def.hostClass) set.add(def.hostClass);
    }
    set.add(CSS_CLASSES.INSPECTOR);
    set.add(CSS_CLASSES.INSPECTOR_PICKED);
    return [...set];
  }
  function unionElementRects(elements) {
    let left = Infinity;
    let top = Infinity;
    let right = -Infinity;
    let bottom = -Infinity;
    let any = false;
    for (const el2 of elements || []) {
      if (!el2 || el2.nodeType !== 1 || !el2.isConnected) continue;
      let rect;
      try {
        rect = el2.getBoundingClientRect();
      } catch {
        continue;
      }
      if (!rect || rect.width <= 0 && rect.height <= 0) continue;
      any = true;
      left = Math.min(left, rect.left);
      top = Math.min(top, rect.top);
      right = Math.max(right, rect.right);
      bottom = Math.max(bottom, rect.bottom);
    }
    if (!any || !Number.isFinite(left)) return null;
    return {
      left,
      top,
      width: Math.max(0, right - left),
      height: Math.max(0, bottom - top)
    };
  }
  var InspectorModeController = class {
    /**
     * @param {{
     *   state: import('./state-manager.js').StateManager,
     *   deepElementFromPoint: (x: number, y: number) => Element|null,
     *   onBeforeEnter?: (kind: string) => void,
     *   onPicksChanged?: (picks: Element[], unionRect: {left:number,top:number,width:number,height:number}|null) => void,
     * }} deps
     */
    constructor(deps) {
      this.state = deps.state;
      this.deepElementFromPoint = deps.deepElementFromPoint;
      this.onBeforeEnter = typeof deps.onBeforeEnter === "function" ? deps.onBeforeEnter : null;
      this.onPicksChanged = typeof deps.onPicksChanged === "function" ? deps.onPicksChanged : null;
      this._selectionMode = "single";
      this._picked = [];
      this._unionRect = null;
    }
    isActive() {
      return this.state.isInspectorMode?.() === true || typeof this.state.getState === "function" && this.state.getState().mode === "inspector";
    }
    /**
     * @param {string} kind
     * @returns {boolean}
     */
    isKind(kind) {
      if (!this.isActive()) return false;
      return this.state.getState().inspectorKind === kind;
    }
    getKind() {
      if (!this.isActive()) return null;
      return this.state.getState().inspectorKind || null;
    }
    getTarget() {
      return this.state.getState().inspectorEl || null;
    }
    getDef() {
      return getInspectorDef(this.getKind());
    }
    /**
     * True when the active inspector kind opted into pointerdown dismiss.
     * @returns {boolean}
     */
    cancelsOnPointerDown() {
      return this.isActive() && !!this.getDef()?.cancelOnPointerDown;
    }
    /**
     * @returns {boolean}
     */
    isCumulative() {
      return this.isActive() && this._selectionMode === "cumulative";
    }
    getSelectionMode() {
      return this._selectionMode;
    }
    /**
     * @returns {Element[]}
     */
    getPickedElements() {
      return this._picked.slice();
    }
    /**
     * @returns {{ left: number, top: number, width: number, height: number }|null}
     */
    getUnionRect() {
      return this._unionRect ? { ...this._unionRect } : null;
    }
    clearPicks() {
      this._picked = [];
      this._unionRect = null;
      try {
        this.onPicksChanged?.([], null);
      } catch {
      }
    }
    /**
     * Enter (or switch to) an inspector kind. Replaces any other inspector kind.
     * @param {string} kind
     * @param {{ selectionMode?: 'single'|'cumulative' }} [opts]
     */
    enter(kind, opts = {}) {
      if (!getInspectorDef(kind)) {
        console.warn("[KeyPilot] Unknown inspector kind:", kind);
        return;
      }
      try {
        this.onBeforeEnter?.(kind);
      } catch (e) {
        console.warn("[KeyPilot] inspector onBeforeEnter failed:", e);
      }
      this.clearPicks();
      const def = getInspectorDef(kind);
      const requested = opts.selectionMode;
      if (requested === "cumulative" || requested === "single") {
        this._selectionMode = requested;
      } else {
        this._selectionMode = def?.selectionMode === "cumulative" ? "cumulative" : "single";
      }
      this.state.enterInspector(kind);
    }
    /**
     * Exit pick mode; clear hover target and cumulative picks.
     * Does not reverse sticky effects (e.g. columns).
     */
    exit() {
      this.clearPicks();
      this._selectionMode = "single";
      this.state.exitInspector();
    }
    /**
     * Hover tracking while inspector is active.
     * @param {Element|null|undefined} under
     */
    updateHover(under) {
      const st = this.state.getState();
      if (!this.isActive()) {
        if (st.inspectorEl) this.state.setInspectorElement(null);
        return;
      }
      const next = under && under.nodeType === 1 ? under : null;
      if (next !== st.inspectorEl) {
        this.state.setInspectorElement(next);
      }
    }
    /**
     * Element under cursor for confirm: state hover, else deep hit-test.
     * @returns {Element|null}
     */
    resolveTarget() {
      const st = this.state.getState();
      if (st.inspectorEl && st.inspectorEl.isConnected) {
        return st.inspectorEl;
      }
      try {
        const { x, y } = st.lastMouse || { x: 0, y: 0 };
        return this.deepElementFromPoint?.(x, y) || null;
      } catch {
        return null;
      }
    }
    /**
     * Resolve target and exit pick mode (caller applies the action).
     * Single-shot tools (Delete, Cols).
     * @returns {Element|null}
     */
    confirmAndExit() {
      const target = this.resolveTarget();
      this.exit();
      return target;
    }
    /**
     * Cumulative: add hovered target to the pick set and stay in mode.
     * Recomputes the union bounding rect of all picks.
     * @returns {Element|null} added (or already-present) target
     */
    confirmAdd() {
      if (!this.isCumulative()) {
        return this.confirmAndExit();
      }
      const target = this.resolveTarget();
      if (!target || target.nodeType !== 1) return null;
      const def = this.getDef();
      if (!def?.allowHtmlBody) {
        if (target === document.documentElement || target === document.body) {
          return null;
        }
      }
      if (!this._picked.includes(target)) {
        this._picked.push(target);
      }
      this._picked = this._picked.filter((el2) => el2 && el2.isConnected);
      this._unionRect = unionElementRects(this._picked);
      try {
        this.onPicksChanged?.(this.getPickedElements(), this.getUnionRect());
      } catch {
      }
      return target;
    }
    /**
     * Cumulative: return picked elements and exit (caller copies / applies).
     * @returns {{ elements: Element[], unionRect: {left:number,top:number,width:number,height:number}|null }}
     */
    finalizeAndExit() {
      const elements = this.getPickedElements().filter((el2) => el2 && el2.isConnected);
      const unionRect = this.getUnionRect();
      this.exit();
      return { elements, unionRect };
    }
    /**
     * Recompute union rect after scroll (viewport coords change).
     */
    refreshUnionRect() {
      if (!this.isCumulative() || this._picked.length === 0) {
        this._unionRect = null;
        return this._unionRect;
      }
      this._picked = this._picked.filter((el2) => el2 && el2.isConnected);
      this._unionRect = unionElementRects(this._picked);
      try {
        this.onPicksChanged?.(this.getPickedElements(), this.getUnionRect());
      } catch {
      }
      return this._unionRect;
    }
  };

  // src/modules/highlight-manager.js
  init_constants();
  var HIGHLIGHT_MODE = Object.freeze({
    mode: "highlight",
    cancelOnPointerDown: true
  });
  var HighlightManager = class extends EventManager {
    constructor() {
      super();
      this.highlightOverlay = null;
      this.highlightRectangleOverlay = null;
      this.highlightSelectionOverlays = [];
      this.highlightModeIndicator = null;
      this.selectionMode = "character";
      this.characterSelectionActive = false;
      this.characterStartPosition = null;
      this.characterStartTextNode = null;
      this.characterStartOffset = 0;
      this.rectOriginPoint = null;
      this.rectOriginDocumentPoint = null;
      this.rectangleStartCaret = null;
      this.elementMatchedElements = [];
      this._elementSelectSelector = ELEMENT_SELECT_TAGS.map((t) => String(t)).join(",");
      this._elementSelectAggregates = new Set(ELEMENT_SELECT_AGGREGATES);
      this._elementSelectLandmarks = new Set(ELEMENT_SELECT_LANDMARKS);
      this._elementSelectAtoms = new Set(ELEMENT_SELECT_ATOMS);
      this.debugHUD = null;
      this.debugUpdateCount = 0;
      this.overlayVisibility = {
        highlight: true,
        highlightRectangle: true
      };
      this.overlayObserver = null;
    }
    /**
     * True when highlight/select-element mode opted into pointerdown dismiss.
     * @returns {boolean}
     */
    cancelsOnPointerDown() {
      return !!HIGHLIGHT_MODE.cancelOnPointerDown;
    }
    /**
     * Initialize the highlight manager with intersection observer
     */
    initialize(overlayObserver) {
      this.overlayObserver = overlayObserver;
    }
    /**
     * Set the selection mode
     * @param {string} mode - 'character' | 'rectangle' | 'element'
     */
    setSelectionMode(mode) {
      if (mode === "character" || mode === "rectangle" || mode === "element") {
        this.selectionMode = mode;
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Selection mode set to:", mode);
        }
      }
    }
    /**
     * Get the current selection mode
     * @returns {string} - Current selection mode
     */
    getSelectionMode() {
      return this.selectionMode;
    }
    /**
     * Initialize the highlight manager with edge-only rectangle intersection observer
     * @param {RectangleIntersectionObserver} rectangleObserver - Edge-only intersection observer
     * @param {Function} notificationCallback - Callback for user notifications
     */
    initializeEdgeOnlyProcessing(rectangleObserver, notificationCallback = null) {
      this.rectangleIntersectionObserver = rectangleObserver;
      this.edgeOnlyProcessingEnabled = FEATURE_FLAGS.USE_EDGE_ONLY_SELECTION && FEATURE_FLAGS.ENABLE_EDGE_ONLY_PROCESSING;
      if (notificationCallback && this.rectangleIntersectionObserver) {
        this.rectangleIntersectionObserver.setNotificationCallback(notificationCallback);
      }
      if (window.KEYPILOT_DEBUG && FEATURE_FLAGS.DEBUG_EDGE_ONLY_PROCESSING) {
        console.log("[KeyPilot Debug] Highlight manager initialized with edge-only processing:", {
          enabled: this.edgeOnlyProcessingEnabled,
          observer: !!this.rectangleIntersectionObserver,
          caching: FEATURE_FLAGS.ENABLE_SELECTION_CACHING,
          monitoring: FEATURE_FLAGS.ENABLE_EDGE_PERFORMANCE_MONITORING,
          notificationCallback: !!notificationCallback
        });
      }
    }
    /**
     * Create a DOM element with specified properties
     */
    createElement(tagName, properties = {}) {
      const element = document.createElement(tagName);
      if (properties.className) {
        element.className = properties.className;
      }
      if (properties.style) {
        element.style.cssText = properties.style;
      }
      return element;
    }
    /**
     * Update highlight overlay for focused element
     */
    updateHighlightOverlay(element) {
      if (!element) {
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] updateHighlightOverlay: no element provided");
        }
        this.hideHighlightOverlay();
        return;
      }
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] updateHighlightOverlay called for:", {
          tagName: element.tagName,
          className: element.className,
          id: element.id
        });
      }
      if (!this.highlightOverlay) {
        this.highlightOverlay = this.createElement("div", {
          className: CSS_CLASSES.HIGHLIGHT_OVERLAY,
          style: `
          position: fixed;
          pointer-events: none;
          z-index: ${Z_INDEX.OVERLAYS};
          border: 3px solid ${COLORS.HIGHLIGHT_BLUE};
          box-shadow: 0 0 0 2px ${COLORS.HIGHLIGHT_SHADOW}, 0 0 12px 2px ${COLORS.HIGHLIGHT_SHADOW_BRIGHT};
          background: transparent;
          will-change: transform;
        `
        });
        document.body.appendChild(this.highlightOverlay);
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Highlight overlay created and added to DOM:", {
            element: this.highlightOverlay,
            className: this.highlightOverlay.className,
            parent: this.highlightOverlay.parentElement?.tagName
          });
        }
        if (this.overlayObserver) {
          this.overlayObserver.observe(this.highlightOverlay);
        }
      }
      const rect = element.getBoundingClientRect();
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Highlight overlay positioning:", {
          rect,
          overlayExists: !!this.highlightOverlay,
          overlayVisibility: this.overlayVisibility.highlight
        });
      }
      if (rect.width > 0 && rect.height > 0) {
        this.highlightOverlay.style.left = `${rect.left}px`;
        this.highlightOverlay.style.top = `${rect.top}px`;
        this.highlightOverlay.style.width = `${rect.width}px`;
        this.highlightOverlay.style.height = `${rect.height}px`;
        this.highlightOverlay.style.display = "block";
        this.highlightOverlay.style.visibility = "visible";
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Highlight overlay positioned at:", {
            left: rect.left,
            top: rect.top,
            width: rect.width,
            height: rect.height
          });
        }
      } else {
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Highlight overlay hidden - invalid rect:", rect);
        }
        this.hideHighlightOverlay();
      }
    }
    /**
     * Hide the highlight overlay
     */
    hideHighlightOverlay() {
      if (this.highlightOverlay) {
        this.highlightOverlay.style.display = "none";
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Highlight overlay hidden");
        }
      }
    }
    /**
     * Update edge-only processing rectangle bounds for all workflows
     * @param {Object} rectOriginPoint - Origin point {x, y} (viewport coordinates)
     * @param {Object} currentPosition - Current cursor position {x, y} (viewport coordinates)
     */
    updateEdgeOnlyProcessingRectangle(rectOriginPoint, currentPosition) {
      if (!this.edgeOnlyProcessingEnabled || !this.rectangleIntersectionObserver) {
        return;
      }
      if (!rectOriginPoint || !currentPosition) {
        return;
      }
      try {
        const scrollX = window.pageXOffset || document.documentElement.scrollLeft;
        const scrollY = window.pageYOffset || document.documentElement.scrollTop;
        const originDocX = rectOriginPoint.x + scrollX;
        const originDocY = rectOriginPoint.y + scrollY;
        const currentDocX = currentPosition.x + scrollX;
        const currentDocY = currentPosition.y + scrollY;
        const rect = {
          left: Math.min(originDocX, currentDocX),
          top: Math.min(originDocY, currentDocY),
          width: Math.abs(currentDocX - originDocX),
          height: Math.abs(currentDocY - originDocY)
        };
        this.rectangleIntersectionObserver.updateRectangle(rect);
        if (window.KEYPILOT_DEBUG && FEATURE_FLAGS.DETAILED_EDGE_LOGGING) {
          console.log("[KeyPilot Debug] Edge-only processing rectangle updated:", {
            viewport: { origin: rectOriginPoint, current: currentPosition },
            document: { originDocX, originDocY, currentDocX, currentDocY },
            rect
          });
        }
      } catch (error) {
        console.warn("[KeyPilot] Error updating edge-only processing rectangle:", error);
      }
    }
    /**
     * Document-anchored origin converted to the current viewport (for caret resolve / overlay).
     * @returns {{x:number,y:number}|null}
     */
    getOriginViewportPoint() {
      if (!this.rectOriginDocumentPoint) return null;
      const scrollX = window.pageXOffset || document.documentElement.scrollLeft || 0;
      const scrollY = window.pageYOffset || document.documentElement.scrollTop || 0;
      return {
        x: this.rectOriginDocumentPoint.x - scrollX,
        y: this.rectOriginDocumentPoint.y - scrollY
      };
    }
    /**
     * Ensure the selection origin is stored in document space (scroll-stable).
     * @param {Object} viewportPoint - {x,y} viewport coordinates at anchor time
     */
    ensureRectOriginDocumentPoint(viewportPoint) {
      if (this.rectOriginDocumentPoint || !viewportPoint) return;
      const scrollX = window.pageXOffset || document.documentElement.scrollLeft || 0;
      const scrollY = window.pageYOffset || document.documentElement.scrollTop || 0;
      this.rectOriginPoint = { ...viewportPoint };
      this.rectOriginDocumentPoint = {
        x: viewportPoint.x + scrollX,
        y: viewportPoint.y + scrollY
      };
    }
    /**
     * Update the highlight rectangle overlay showing the selection area.
     * Origin is document-anchored so the rectangle stays glued to content while scrolling.
     * @param {Object} rectOriginPoint - Origin point from first press {x, y} (viewport at press; used only to seed)
     * @param {Object} currentPosition - Current cursor position {x, y} (viewport coordinates)
     */
    updateHighlightRectangleOverlay(rectOriginPoint, currentPosition) {
      if (!currentPosition) {
        this.hideHighlightRectangleOverlay();
        return;
      }
      if (!this.rectOriginDocumentPoint) {
        if (!rectOriginPoint) {
          this.hideHighlightRectangleOverlay();
          return;
        }
        this.ensureRectOriginDocumentPoint(rectOriginPoint);
        this.showDebugHUD();
      }
      const scrollX = window.pageXOffset || document.documentElement.scrollLeft || 0;
      const scrollY = window.pageYOffset || document.documentElement.scrollTop || 0;
      const currentDocumentPosition = {
        x: currentPosition.x + scrollX,
        y: currentPosition.y + scrollY
      };
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] updateHighlightRectangleOverlay called:", {
          rectOriginPoint,
          currentPosition,
          rectOriginDocumentPoint: this.rectOriginDocumentPoint,
          currentDocumentPosition,
          scrollX: window.scrollX,
          scrollY: window.scrollY
        });
      }
      if (!this.highlightRectangleOverlay) {
        this.highlightRectangleOverlay = this.createElement("div", {
          className: "kpv2-highlight-rectangle-overlay",
          style: `
          position: fixed;
          pointer-events: none;
          z-index: ${Z_INDEX.OVERLAYS_BELOW_2};
          background: ${COLORS.HIGHLIGHT_SELECTION_BG};
          border: 2px dashed ${COLORS.HIGHLIGHT_BLUE};
          box-shadow: 0 0 0 1px ${COLORS.HIGHLIGHT_SHADOW}, 0 0 8px 1px ${COLORS.HIGHLIGHT_SHADOW_BRIGHT};
          will-change: transform;
          opacity: 0.8;
          box-sizing: border-box;
        `
        });
        document.body.appendChild(this.highlightRectangleOverlay);
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Highlight rectangle overlay created and added to DOM:", {
            element: this.highlightRectangleOverlay,
            className: this.highlightRectangleOverlay.className,
            parent: this.highlightRectangleOverlay.parentElement?.tagName
          });
        }
        if (this.overlayObserver) {
          this.overlayObserver.observe(this.highlightRectangleOverlay);
        }
      }
      const documentLeft = Math.min(this.rectOriginDocumentPoint.x, currentDocumentPosition.x);
      const documentTop = Math.min(this.rectOriginDocumentPoint.y, currentDocumentPosition.y);
      const width = Math.abs(currentDocumentPosition.x - this.rectOriginDocumentPoint.x);
      const height = Math.abs(currentDocumentPosition.y - this.rectOriginDocumentPoint.y);
      const viewportLeft = documentLeft - scrollX;
      const viewportTop = documentTop - scrollY;
      const deltaX = currentDocumentPosition.x - this.rectOriginDocumentPoint.x;
      const deltaY = currentDocumentPosition.y - this.rectOriginDocumentPoint.y;
      const quadrant = this.getQuadrant(deltaX, deltaY);
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Highlight rectangle overlay positioning:", {
          documentLeft,
          documentTop,
          width,
          height,
          viewportLeft,
          viewportTop,
          rectOriginDocumentPoint: this.rectOriginDocumentPoint,
          currentDocumentPosition,
          deltaX,
          deltaY,
          quadrant,
          direction: {
            horizontal: deltaX >= 0 ? "right" : "left",
            vertical: deltaY >= 0 ? "down" : "up"
          }
        });
      }
      const calculatedValues = {
        documentLeft,
        documentTop,
        width,
        height,
        viewportLeft,
        viewportTop,
        quadrant
      };
      this.updateDebugHUD(
        rectOriginPoint,
        currentPosition,
        this.rectOriginDocumentPoint,
        currentDocumentPosition,
        calculatedValues
      );
      if (this.selectionMode === "rectangle") {
        this.updateEdgeOnlyProcessingRectangle(rectOriginPoint, currentPosition);
      }
      const shouldShowRectangle = this.shouldShowRectangle(width, height, deltaX, deltaY);
      if (shouldShowRectangle) {
        this.highlightRectangleOverlay.style.left = `${viewportLeft}px`;
        this.highlightRectangleOverlay.style.top = `${viewportTop}px`;
        this.highlightRectangleOverlay.style.width = `${width}px`;
        this.highlightRectangleOverlay.style.height = `${height}px`;
        this.highlightRectangleOverlay.style.display = "block";
        this.highlightRectangleOverlay.style.visibility = "visible";
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Highlight rectangle overlay positioned at:", {
            viewportLeft,
            viewportTop,
            width,
            height,
            quadrant,
            shouldShow: shouldShowRectangle,
            minWidth: RECTANGLE_SELECTION.MIN_WIDTH,
            minHeight: RECTANGLE_SELECTION.MIN_HEIGHT
          });
        }
      } else {
        this.highlightRectangleOverlay.style.display = "none";
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Highlight rectangle overlay hidden:", {
            width,
            height,
            shouldShow: shouldShowRectangle,
            minWidth: RECTANGLE_SELECTION.MIN_WIDTH,
            minHeight: RECTANGLE_SELECTION.MIN_HEIGHT,
            reason: width < RECTANGLE_SELECTION.MIN_WIDTH ? "width too small" : height < RECTANGLE_SELECTION.MIN_HEIGHT ? "height too small" : "other"
          });
        }
      }
    }
    /**
     * Determine which quadrant the current position is relative to the origin
     * @param {number} deltaX - Horizontal distance from origin
     * @param {number} deltaY - Vertical distance from origin
     * @returns {string} - Quadrant identifier
     */
    getQuadrant(deltaX, deltaY) {
      if (deltaX >= 0 && deltaY >= 0) return "bottom-right";
      if (deltaX < 0 && deltaY >= 0) return "bottom-left";
      if (deltaX < 0 && deltaY < 0) return "top-left";
      if (deltaX >= 0 && deltaY < 0) return "top-right";
      return "origin";
    }
    /**
     * Determine if the rectangle should be visible based on size and configuration
     * @param {number} width - Rectangle width in pixels
     * @param {number} height - Rectangle height in pixels
     * @param {number} deltaX - Horizontal distance from origin
     * @param {number} deltaY - Vertical distance from origin
     * @returns {boolean} - Whether rectangle should be shown
     */
    shouldShowRectangle(width, height, deltaX, deltaY) {
      if (RECTANGLE_SELECTION.HIDE_ZERO_SIZE && (width === 0 || height === 0)) {
        return false;
      }
      if (RECTANGLE_SELECTION.SHOW_IMMEDIATE_FEEDBACK && (Math.abs(deltaX) > 0 || Math.abs(deltaY) > 0)) {
        return true;
      }
      const meetsMinWidth = width >= RECTANGLE_SELECTION.MIN_WIDTH;
      const meetsMinHeight = height >= RECTANGLE_SELECTION.MIN_HEIGHT;
      const dragDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
      const meetsMinDrag = dragDistance >= RECTANGLE_SELECTION.MIN_DRAG_DISTANCE;
      return meetsMinWidth && meetsMinHeight || meetsMinDrag;
    }
    /**
     * Hide the highlight rectangle overlay
     */
    hideHighlightRectangleOverlay() {
      if (this.highlightRectangleOverlay) {
        this.highlightRectangleOverlay.style.display = "none";
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Highlight rectangle overlay hidden");
        }
      }
      this.resetRectangleSelection();
    }
    /**
     * Remove the highlight rectangle overlay from the DOM and drop the element
     * reference, so the next rectangle session starts with a fresh element.
     * (Hiding alone leaves a detached node that a later session would restyle
     * without re-adding it to the DOM — invisible.)
     */
    removeHighlightRectangleOverlay() {
      if (this.highlightRectangleOverlay) {
        if (this.overlayObserver) {
          try {
            this.overlayObserver.unobserve(this.highlightRectangleOverlay);
          } catch {
          }
        }
        try {
          this.highlightRectangleOverlay.remove();
        } catch {
        }
        this.highlightRectangleOverlay = null;
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Highlight rectangle overlay removed");
        }
      }
      this.resetRectangleSelection();
    }
    /**
     * Get current selection from edge-only processing
     * @returns {Selection|null} - Browser selection object or null
     */
    getEdgeOnlySelection() {
      if (!this.edgeOnlyProcessingEnabled || !this.rectangleIntersectionObserver) {
        return null;
      }
      try {
        return this.rectangleIntersectionObserver.createSelectionFromIntersection();
      } catch (error) {
        console.warn("[KeyPilot] Error getting edge-only selection:", error);
        return null;
      }
    }
    /**
     * Start character-level selection at the given position
     * Prefer native caret APIs; fall back to findTextNode only if needed.
     * @param {Object} position - Position {x, y} in viewport coordinates
     * @param {Function} findTextNodeAtPosition - Function to find text node at position
     * @param {Function} getTextOffsetAtPosition - Function to get text offset at position
     */
    startCharacterSelection(position, findTextNodeAtPosition, getTextOffsetAtPosition) {
      if (!position || typeof position.x !== "number" || typeof position.y !== "number") {
        console.warn("[KeyPilot] Invalid position for character selection:", position);
        return false;
      }
      try {
        this.clearHighlightSelectionOverlays();
        this.rectOriginPoint = { ...position };
        this.rectOriginDocumentPoint = {
          x: position.x + (window.pageXOffset || document.documentElement.scrollLeft || 0),
          y: position.y + (window.pageYOffset || document.documentElement.scrollTop || 0)
        };
        let textNode = null;
        let offset = 0;
        const caret = this.resolveCaretAtPoint(position.x, position.y, document);
        if (caret?.textNode) {
          textNode = caret.textNode;
          offset = caret.offset;
        } else if (typeof findTextNodeAtPosition === "function") {
          textNode = findTextNodeAtPosition(position.x, position.y);
          if (textNode && typeof getTextOffsetAtPosition === "function") {
            offset = getTextOffsetAtPosition(textNode, position.x, position.y);
          }
        }
        if (!textNode || textNode.nodeType !== Node.TEXT_NODE) {
          if (window.KEYPILOT_DEBUG) {
            console.log("[KeyPilot Debug] No text node found at position for character selection");
          }
          this.characterSelectionActive = false;
          this.characterStartTextNode = null;
          this.characterStartOffset = 0;
          this.characterStartPosition = { ...position };
          return false;
        }
        if (typeof offset !== "number" || offset < 0) {
          offset = 0;
        }
        const maxOff = textNode.textContent ? textNode.textContent.length : 0;
        offset = Math.max(0, Math.min(offset, maxOff));
        this.characterSelectionActive = true;
        this.characterStartPosition = { ...position };
        this.characterStartTextNode = textNode;
        this.characterStartOffset = offset;
        const ownerDocument = textNode.ownerDocument || document;
        const range = ownerDocument.createRange();
        range.setStart(textNode, offset);
        range.setEnd(textNode, offset);
        const selection = this.getSelectionForDocument(ownerDocument);
        if (selection) {
          selection.removeAllRanges();
          selection.addRange(range);
        }
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Character selection started:", {
            position,
            textNode: textNode.textContent?.substring(0, 50),
            offset,
            selectedText: textNode.textContent?.charAt(offset)
          });
        }
        return true;
      } catch (error) {
        console.error("[KeyPilot] Error starting character selection:", error);
        this.resetCharacterSelection();
        return false;
      }
    }
    /**
     * Resolve a caret (text node + offset) at viewport coordinates via native APIs.
     * Prefer caretRangeFromPoint / caretPositionFromPoint — O(1) vs TreeWalker scans.
     * @param {number} x
     * @param {number} y
     * @param {Document} [ownerDocument]
     * @returns {{ textNode: Text, offset: number }|null}
     */
    resolveCaretAtPoint(x, y, ownerDocument = document) {
      try {
        const doc = ownerDocument || document;
        if (typeof doc.caretRangeFromPoint === "function") {
          const caretRange = doc.caretRangeFromPoint(x, y);
          if (caretRange && caretRange.startContainer) {
            const node = caretRange.startContainer;
            if (node.nodeType === Node.TEXT_NODE) {
              return { textNode: node, offset: caretRange.startOffset };
            }
            if (node.nodeType === Node.ELEMENT_NODE) {
              const mapped = this.caretFromElementChildIndex(node, caretRange.startOffset);
              if (mapped) return mapped;
            }
          }
        }
        if (typeof doc.caretPositionFromPoint === "function") {
          const pos = doc.caretPositionFromPoint(x, y);
          if (pos && pos.offsetNode) {
            const node = pos.offsetNode;
            if (node.nodeType === Node.TEXT_NODE) {
              return { textNode: node, offset: pos.offset };
            }
            if (node.nodeType === Node.ELEMENT_NODE) {
              const mapped = this.caretFromElementChildIndex(node, pos.offset);
              if (mapped) return mapped;
            }
          }
        }
      } catch {
      }
      return null;
    }
    /**
     * Map a caret on an Element (child-index offset) to a text node + offset.
     * @param {Element|Node} element
     * @param {number} offset
     * @returns {{ textNode: Text, offset: number }|null}
     */
    caretFromElementChildIndex(element, offset) {
      if (!element || !element.childNodes) return null;
      const kids = element.childNodes;
      const n = kids.length;
      if (!n) return null;
      const index = typeof offset === "number" && offset >= 0 ? offset : 0;
      if (index >= n) {
        return this.caretAtEndOfNode(element);
      }
      const child = kids[index];
      if (!child) return this.caretAtEndOfNode(element);
      if (child.nodeType === Node.TEXT_NODE) {
        return { textNode: child, offset: 0 };
      }
      const firstInChild = this.firstTextCaretIn(child);
      if (firstInChild) return firstInChild;
      for (let i = index - 1; i >= 0; i--) {
        const prev = this.caretAtEndOfNode(kids[i]);
        if (prev) return prev;
      }
      return null;
    }
    /**
     * @param {Node} root
     * @returns {{ textNode: Text, offset: number }|null}
     */
    firstTextCaretIn(root) {
      if (!root) return null;
      if (root.nodeType === Node.TEXT_NODE) {
        return { textNode: root, offset: 0 };
      }
      try {
        const doc = root.ownerDocument || document;
        const walker = doc.createTreeWalker(root, NodeFilter.SHOW_TEXT);
        const node = walker.nextNode();
        if (node) return { textNode: node, offset: 0 };
      } catch {
      }
      return null;
    }
    /**
     * @param {Node} root
     * @returns {{ textNode: Text, offset: number }|null}
     */
    caretAtEndOfNode(root) {
      if (!root) return null;
      if (root.nodeType === Node.TEXT_NODE) {
        const len = root.textContent ? root.textContent.length : 0;
        return { textNode: root, offset: len };
      }
      try {
        const doc = root.ownerDocument || document;
        const walker = doc.createTreeWalker(root, NodeFilter.SHOW_TEXT);
        let last = null;
        let n;
        while (n = walker.nextNode()) last = n;
        if (last) {
          const len = last.textContent ? last.textContent.length : 0;
          return { textNode: last, offset: len };
        }
      } catch {
      }
      return null;
    }
    /**
     * Build a forward/backward Range from two caret points and apply it to Selection.
     * @param {{ textNode: Text, offset: number }} start
     * @param {{ textNode: Text, offset: number }} end
     * @returns {Selection|null}
     */
    applyCaretSelection(start, end) {
      if (!start?.textNode || !end?.textNode) return null;
      try {
        const ownerDocument = start.textNode.ownerDocument || document;
        const selection = this.getSelectionForDocument(ownerDocument);
        if (!selection) return null;
        let first = start;
        let second = end;
        if (start.textNode === end.textNode) {
          if (start.offset > end.offset) {
            first = end;
            second = start;
          }
        } else {
          const pos = start.textNode.compareDocumentPosition(end.textNode);
          if (pos & Node.DOCUMENT_POSITION_PRECEDING) {
            first = end;
            second = start;
          }
        }
        const range = ownerDocument.createRange();
        range.setStart(first.textNode, first.offset);
        range.setEnd(second.textNode, second.offset);
        selection.removeAllRanges();
        selection.addRange(range);
        return selection;
      } catch (error) {
        if (window.KEYPILOT_DEBUG) {
          console.warn("[KeyPilot] applyCaretSelection failed:", error);
        }
        return null;
      }
    }
    /**
     * Update character-level selection to the current position.
     * Fast path only: native caret APIs (like browser drag-select).
     * The legacy full-document rectangle character scan is intentionally not used
     * on the mousemove path — it freezes complex pages.
     *
     * @param {Object} currentPosition - Current position {x, y} in viewport coordinates
     * @param {Object} startPosition - Start position {x, y} in viewport coordinates  
     * @param {Function} findTextNodeAtPosition - Function to find text node at position
     * @param {Function} getTextOffsetAtPosition - Function to get text offset at position
     */
    updateCharacterSelection(currentPosition, startPosition, findTextNodeAtPosition, getTextOffsetAtPosition) {
      if (!currentPosition || typeof currentPosition.x !== "number" || typeof currentPosition.y !== "number") {
        return false;
      }
      if (this.characterStartTextNode && !this.characterStartTextNode.isConnected) {
        this.characterSelectionActive = false;
        this.characterStartTextNode = null;
        this.characterStartOffset = 0;
      }
      if (!this.characterSelectionActive || !this.characterStartTextNode) {
        const caret = this.resolveCaretAtPoint(currentPosition.x, currentPosition.y, document);
        if (caret?.textNode) {
          if (!this.rectOriginDocumentPoint && startPosition) {
            this.ensureRectOriginDocumentPoint(startPosition);
          } else if (!this.rectOriginDocumentPoint) {
            this.ensureRectOriginDocumentPoint(currentPosition);
          }
          this.characterSelectionActive = true;
          this.characterStartPosition = { ...currentPosition };
          this.characterStartTextNode = caret.textNode;
          this.characterStartOffset = caret.offset;
        } else {
          const originVp = this.getOriginViewportPoint() || startPosition;
          if (originVp) {
            this.updateHighlightRectangleOverlay(originVp, currentPosition);
          }
          return false;
        }
      }
      try {
        const originVp = this.getOriginViewportPoint() || startPosition;
        if (originVp) {
          this.updateHighlightRectangleOverlay(originVp, currentPosition);
        }
        const ownerDocument = this.characterStartTextNode.ownerDocument || document;
        const startCaret = {
          textNode: this.characterStartTextNode,
          offset: this.characterStartOffset
        };
        let endCaret = null;
        if (FEATURE_FLAGS.USE_NATIVE_SELECTION_API !== false) {
          endCaret = this.resolveCaretAtPoint(currentPosition.x, currentPosition.y, ownerDocument);
        }
        if (!endCaret) {
          return true;
        }
        const selection = this.applyCaretSelection(startCaret, endCaret);
        if (selection && window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Character selection updated (caret path):", {
            selectedText: selection.toString().substring(0, 100),
            rangeCount: selection.rangeCount,
            usedNativeCaret: true
          });
        }
        return true;
      } catch (error) {
        console.error("[KeyPilot] Error updating character selection:", error);
        return false;
      }
    }
    /**
     * Rectangle / intelligent selection: caret at origin → caret at current (browser drag semantics).
     * Origin caret is stored as node+offset (scroll-stable). Free end follows the mouse viewport.
     * Rectangle overlay is always document-anchored and safe to refresh during scroll.
     * @param {Object} startPosition - {x, y} viewport at press (seed only)
     * @param {Object} currentPosition - {x, y} viewport (live cursor)
     * @param {Function} [findTextNodeAtPosition]
     * @param {Function} [getTextOffsetAtPosition]
     * @returns {Selection|null}
     */
    updateRectangleSelectionFromCarets(startPosition, currentPosition, findTextNodeAtPosition, getTextOffsetAtPosition) {
      if (!currentPosition) return null;
      try {
        if (startPosition) {
          this.ensureRectOriginDocumentPoint(startPosition);
        }
        this.updateHighlightRectangleOverlay(startPosition, currentPosition);
        const ownerDocument = document;
        let startCaret = this.rectangleStartCaret;
        let endCaret = null;
        const startNodeGone = startCaret?.textNode && !startCaret.textNode.isConnected;
        if (!startCaret || startNodeGone) {
          const originVp = this.getOriginViewportPoint() || startPosition;
          if (originVp) {
            if (FEATURE_FLAGS.USE_NATIVE_SELECTION_API !== false) {
              startCaret = this.resolveCaretAtPoint(originVp.x, originVp.y, ownerDocument);
            }
            if (!startCaret && typeof findTextNodeAtPosition === "function") {
              const textNode = findTextNodeAtPosition(originVp.x, originVp.y);
              if (textNode && typeof getTextOffsetAtPosition === "function") {
                const offset = getTextOffsetAtPosition(textNode, originVp.x, originVp.y);
                if (typeof offset === "number" && offset >= 0) {
                  startCaret = { textNode, offset };
                }
              }
            }
            if (startCaret) {
              this.rectangleStartCaret = startCaret;
            }
          }
        }
        if (FEATURE_FLAGS.USE_NATIVE_SELECTION_API !== false) {
          endCaret = this.resolveCaretAtPoint(currentPosition.x, currentPosition.y, ownerDocument);
        }
        if (!endCaret && typeof findTextNodeAtPosition === "function") {
          const textNode = findTextNodeAtPosition(currentPosition.x, currentPosition.y);
          if (textNode && typeof getTextOffsetAtPosition === "function") {
            const offset = getTextOffsetAtPosition(textNode, currentPosition.x, currentPosition.y);
            if (typeof offset === "number" && offset >= 0) {
              endCaret = { textNode, offset };
            }
          }
        }
        if (!startCaret || !endCaret) {
          return null;
        }
        return this.applyCaretSelection(startCaret, endCaret);
      } catch (error) {
        console.warn("[KeyPilot] Error updating rectangle selection from carets:", error);
        return null;
      }
    }
    /**
     * Element-granularity rectangle selection: find semantic HTML elements that
     * intersect the drag rect; deepest match wins when both ancestor+descendant hit.
     * @param {Object} startPosition - {x, y} viewport (seed)
     * @param {Object} currentPosition - {x, y} viewport (live)
     * @returns {Element[]}
     */
    updateElementRectangleSelection(startPosition, currentPosition) {
      if (!currentPosition) {
        this.clearElementSelection();
        return [];
      }
      try {
        if (startPosition) {
          this.ensureRectOriginDocumentPoint(startPosition);
        }
        this.updateHighlightRectangleOverlay(startPosition, currentPosition);
        const rect = this.computeViewportRectFromOriginAndCursor(currentPosition);
        if (!rect || rect.width < (RECTANGLE_SELECTION.MIN_WIDTH || 3) || rect.height < (RECTANGLE_SELECTION.MIN_HEIGHT || 3)) {
          this.clearElementSelection();
          return [];
        }
        const matched = this.findSemanticElementsIntersectingRect(rect);
        this.elementMatchedElements = matched;
        this.paintElementSelectionOverlays(matched);
        return matched;
      } catch (error) {
        console.warn("[KeyPilot] Error updating element rectangle selection:", error);
        this.clearElementSelection();
        return [];
      }
    }
    /**
     * Current dashed-rect bounds in viewport coordinates (from document-anchored origin).
     * @returns {{ left: number, top: number, width: number, height: number, right: number, bottom: number }|null}
     */
    getCurrentHighlightRectangleViewportBounds() {
      if (!this.rectOriginDocumentPoint) return null;
      const scrollX = window.pageXOffset || document.documentElement.scrollLeft || 0;
      const scrollY = window.pageYOffset || document.documentElement.scrollTop || 0;
      const originVp = {
        x: this.rectOriginDocumentPoint.x - scrollX,
        y: this.rectOriginDocumentPoint.y - scrollY
      };
      let freeX = originVp.x;
      let freeY = originVp.y;
      if (this.highlightRectangleOverlay && this.highlightRectangleOverlay.style.display !== "none") {
        try {
          const r = this.highlightRectangleOverlay.getBoundingClientRect();
          if (r.width > 0 || r.height > 0) {
            return {
              left: r.left,
              top: r.top,
              width: r.width,
              height: r.height,
              right: r.right,
              bottom: r.bottom
            };
          }
        } catch {
        }
      }
      if (this.rectOriginPoint) {
        freeX = this.rectOriginPoint.x;
        freeY = this.rectOriginPoint.y;
      }
      const left = Math.min(originVp.x, freeX);
      const top = Math.min(originVp.y, freeY);
      const right = Math.max(originVp.x, freeX);
      const bottom = Math.max(originVp.y, freeY);
      return {
        left,
        top,
        width: right - left,
        height: bottom - top,
        right,
        bottom
      };
    }
    /**
     * @param {{ left: number, top: number, right: number, bottom: number, width: number, height: number }} rect
     * @param {Object} currentPosition - live cursor for free end if overlay unavailable
     * @returns {{ left: number, top: number, right: number, bottom: number, width: number, height: number }|null}
     */
    computeViewportRectFromOriginAndCursor(currentPosition) {
      if (!this.rectOriginDocumentPoint || !currentPosition) return null;
      const scrollX = window.pageXOffset || document.documentElement.scrollLeft || 0;
      const scrollY = window.pageYOffset || document.documentElement.scrollTop || 0;
      const ox = this.rectOriginDocumentPoint.x - scrollX;
      const oy = this.rectOriginDocumentPoint.y - scrollY;
      const left = Math.min(ox, currentPosition.x);
      const top = Math.min(oy, currentPosition.y);
      const right = Math.max(ox, currentPosition.x);
      const bottom = Math.max(oy, currentPosition.y);
      return {
        left,
        top,
        right,
        bottom,
        width: right - left,
        height: bottom - top
      };
    }
    /**
     * @param {{ left: number, top: number, right: number, bottom: number }} rect
     * @returns {Element[]}
     */
    findSemanticElementsIntersectingRect(rect) {
      if (!rect || !this._elementSelectSelector) return [];
      let candidates = [];
      try {
        candidates = Array.from(document.querySelectorAll(this._elementSelectSelector));
      } catch {
        return [];
      }
      const hits = [];
      for (const el2 of candidates) {
        if (!el2 || el2.nodeType !== 1) continue;
        if (this._isKeyPilotChromeElement(el2)) continue;
        const boxes = this._elementClientRects(el2);
        if (!boxes.length) continue;
        if (!this._clientRectsIntersectRect(rect, boxes)) continue;
        hits.push(el2);
      }
      return this._resolveAncestorDescendantHits(hits);
    }
    /**
     * Line boxes for overlap tests (wrapped <a> AABB corners must not count).
     * @param {Element} el
     * @returns {DOMRect[]}
     */
    _elementClientRects(el2) {
      try {
        const list = el2.getClientRects?.();
        if (list && list.length) {
          const boxes = Array.from(list).filter((r) => r && (r.width > 0 || r.height > 0));
          if (boxes.length) return boxes;
        }
      } catch {
      }
      try {
        const r = el2.getBoundingClientRect();
        if (r && (r.width > 0 || r.height > 0)) return [r];
      } catch {
      }
      return [];
    }
    /**
     * @param {{ left: number, top: number, right: number, bottom: number }} rect
     * @param {Array<{ left: number, top: number, right: number, bottom: number }>} boxes
     */
    _clientRectsIntersectRect(rect, boxes) {
      for (const b of boxes) {
        if (this._rectsIntersect(rect, b)) return true;
      }
      return false;
    }
    _elementSelectTag(el2) {
      try {
        return String(el2?.tagName || "").toLowerCase();
      } catch {
        return "";
      }
    }
    _isElementSelectLandmark(el2) {
      return this._elementSelectLandmarks.has(this._elementSelectTag(el2));
    }
    /**
     * Walk self (if includeSelf) then ancestors for the first listed tag in `tagSet`.
     * @param {Element} el
     * @param {Set<string>} tagSet
     * @param {boolean} includeSelf
     * @returns {Element|null}
     */
    _nearestElementSelectMatch(el2, tagSet, includeSelf) {
      let n = includeSelf ? el2 : el2?.parentElement;
      while (n && n.nodeType === 1) {
        if (!this._isKeyPilotChromeElement(n)) {
          const tag = this._elementSelectTag(n);
          if (tag && tagSet.has(tag)) return n;
        }
        n = n.parentElement;
      }
      return null;
    }
    /**
     * Map a hit to its article feature unit: aggregate wrapper first (table, figure,
     * list, picture), else nearest atom (p, heading, img, …).
     * @param {Element} el
     * @returns {Element}
     */
    _elementSelectFeatureTarget(el2) {
      const agg = this._nearestElementSelectMatch(el2, this._elementSelectAggregates, true);
      if (agg) return agg;
      const atom = this._nearestElementSelectMatch(el2, this._elementSelectAtoms, true);
      if (atom) return atom;
      return el2;
    }
    /**
     * Aggregates swallow descendants. Landmarks yield to inner feature units.
     * Multiple sibling units (two paragraphs) all stay.
     * @param {Element[]} elements
     * @returns {Element[]}
     */
    _resolveAncestorDescendantHits(elements) {
      if (!elements || !elements.length) return [];
      const targets = [];
      const seen = /* @__PURE__ */ new Set();
      for (const el2 of elements) {
        const target = this._elementSelectFeatureTarget(el2);
        if (!target || seen.has(target)) continue;
        if (this._isKeyPilotChromeElement(target)) continue;
        seen.add(target);
        targets.push(target);
      }
      const remaining = new Set(targets);
      return targets.filter((el2) => {
        if (this._isElementSelectLandmark(el2)) {
          for (const other of remaining) {
            if (other === el2) continue;
            try {
              if (el2.contains(other)) return false;
            } catch {
            }
          }
        }
        return true;
      });
    }
    /**
     * @param {{ left: number, top: number, right: number, bottom: number }} a
     * @param {DOMRect|{ left: number, top: number, right: number, bottom: number }} b
     */
    _rectsIntersect(a, b) {
      return !(a.right < b.left || a.left > b.right || a.bottom < b.top || a.top > b.bottom);
    }
    _isKeyPilotChromeElement(el2) {
      try {
        if (!(el2 instanceof Element)) return false;
        if (el2.closest?.(".kp-floating-keyboard-help, .kp-control-strip, .kp-keybindings-popover, .kp-action-config-panel")) {
          return true;
        }
        const cls = typeof el2.className === "string" ? el2.className : "";
        if (cls.includes("kpv2-") || cls.includes("kp-")) {
          if (el2.classList?.contains?.("kpv2-highlight-rectangle-overlay") || el2.classList?.contains?.("kpv2-highlight-selection-overlay") || el2.classList?.contains?.("kpv2-inspector-overlay") || el2.classList?.contains?.("kpv2-inspector-picked-overlay") || el2.classList?.contains?.("kpv2-inspector-union-overlay")) {
            return true;
          }
        }
        return false;
      } catch {
        return false;
      }
    }
    /**
     * Paint blue selection overlays for matched elements (not caret Selection).
     * @param {Element[]} elements
     */
    paintElementSelectionOverlays(elements) {
      this.clearHighlightSelectionOverlays();
      const list = Array.isArray(elements) ? elements : [];
      let created = 0;
      const MAX = 80;
      for (const el2 of list) {
        if (created >= MAX) break;
        if (!el2?.isConnected) continue;
        let rect;
        try {
          rect = el2.getBoundingClientRect();
        } catch {
          continue;
        }
        if (!rect || rect.width <= 0 || rect.height <= 0) continue;
        const overlay = this.createElement("div", {
          className: "kpv2-highlight-selection-overlay",
          style: `
          position: fixed;
          left: ${rect.left}px;
          top: ${rect.top}px;
          width: ${rect.width}px;
          height: ${rect.height}px;
          background: ${COLORS.HIGHLIGHT_SELECTION_BG};
          border: 1px solid ${COLORS.HIGHLIGHT_SELECTION_BORDER};
          pointer-events: none;
          z-index: ${Z_INDEX.HIGHLIGHT_SELECTION};
          box-sizing: border-box;
        `
        });
        try {
          document.body.appendChild(overlay);
          this.highlightSelectionOverlays.push(overlay);
          if (this.overlayObserver) this.overlayObserver.observe(overlay);
          created += 1;
        } catch {
        }
      }
    }
    /**
     * @returns {Element[]}
     */
    getMatchedElements() {
      return (this.elementMatchedElements || []).filter((el2) => el2 && el2.isConnected);
    }
    /**
     * Clear element-match state (does not clear dashed rect unless caller does).
     */
    clearElementSelection() {
      this.elementMatchedElements = [];
      this.clearHighlightSelectionOverlays();
    }
    /**
     * Reposition highlight rectangle + selection overlays after scroll without a mouse move.
     * Origin stays document-anchored; free end uses last known viewport mouse.
     * @param {Object} currentViewportMouse - {x,y}
     * @param {Function} [findTextNodeAtPosition]
     * @param {Function} [getTextOffsetAtPosition]
     * @returns {Selection|null|boolean}
     */
    syncSelectionToScroll(currentViewportMouse, findTextNodeAtPosition, getTextOffsetAtPosition) {
      if (!currentViewportMouse || !this.rectOriginDocumentPoint) {
        if (this.characterSelectionActive && this.characterStartTextNode) {
          return this.updateCharacterSelection(
            currentViewportMouse,
            this.getOriginViewportPoint(),
            findTextNodeAtPosition,
            getTextOffsetAtPosition
          );
        }
        return null;
      }
      if (this.selectionMode === "character" || this.characterSelectionActive) {
        return this.updateCharacterSelection(
          currentViewportMouse,
          this.getOriginViewportPoint(),
          findTextNodeAtPosition,
          getTextOffsetAtPosition
        );
      }
      if (this.selectionMode === "element") {
        return this.updateElementRectangleSelection(
          this.getOriginViewportPoint(),
          currentViewportMouse
        );
      }
      return this.updateRectangleSelectionFromCarets(
        this.getOriginViewportPoint(),
        currentViewportMouse,
        findTextNodeAtPosition,
        getTextOffsetAtPosition
      );
    }
    /**
     * Create a character selection constrained to rectangle bounds
     * @param {Object} rectBounds - Rectangle bounds {left, top, right, bottom} in document coordinates
     * @returns {Selection|null} - Browser selection object or null
     */
    createRectangleConstrainedCharacterSelection(rectBounds) {
      try {
        const ownerDocument = this.characterStartTextNode.ownerDocument || document;
        const selection = this.getSelectionForDocument(ownerDocument);
        if (!selection) {
          return null;
        }
        selection.removeAllRanges();
        const { startPosition, endPosition } = this.findRectangleBoundaryPositions(rectBounds);
        if (!startPosition || !endPosition) {
          if (window.KEYPILOT_DEBUG) {
            console.log("[KeyPilot Debug] No valid start/end positions found in rectangle");
          }
          return selection;
        }
        const range = ownerDocument.createRange();
        range.setStart(startPosition.textNode, startPosition.offset);
        range.setEnd(endPosition.textNode, endPosition.offset);
        selection.addRange(range);
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Rectangle character selection created:", {
            startText: startPosition.textNode.textContent?.substring(startPosition.offset, startPosition.offset + 10),
            endText: endPosition.textNode.textContent?.substring(Math.max(0, endPosition.offset - 10), endPosition.offset),
            selectedText: selection.toString().substring(0, 100)
          });
        }
        return selection;
      } catch (error) {
        console.error("[KeyPilot] Error creating rectangle-constrained character selection:", error);
        return null;
      }
    }
    /**
     * Find the first and last character positions within the rectangle bounds
     * @param {Object} rectBounds - Rectangle bounds in document coordinates
     * @returns {Object} - {startPosition, endPosition} with textNode and offset
     */
    findRectangleBoundaryPositions(rectBounds) {
      let startPosition = null;
      let endPosition = null;
      try {
        const intersectingTextNodes = this.findTextNodesForHighlighting(rectBounds);
        if (intersectingTextNodes.length === 0) {
          return { startPosition: null, endPosition: null };
        }
        intersectingTextNodes.sort((a, b) => {
          const comparison = a.compareDocumentPosition(b);
          if (comparison & Node.DOCUMENT_POSITION_FOLLOWING) {
            return -1;
          } else if (comparison & Node.DOCUMENT_POSITION_PRECEDING) {
            return 1;
          }
          return 0;
        });
        for (const textNode of intersectingTextNodes) {
          const firstCharOffset = this.findFirstCharacterInRectangle(textNode, rectBounds);
          if (firstCharOffset !== -1) {
            startPosition = { textNode, offset: firstCharOffset };
            break;
          }
        }
        for (let i = intersectingTextNodes.length - 1; i >= 0; i--) {
          const textNode = intersectingTextNodes[i];
          const lastCharOffset = this.findLastCharacterInRectangle(textNode, rectBounds);
          if (lastCharOffset !== -1) {
            endPosition = { textNode, offset: lastCharOffset };
            break;
          }
        }
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Rectangle boundary positions:", {
            intersectingNodes: intersectingTextNodes.length,
            startPosition: startPosition ? {
              text: startPosition.textNode.textContent?.substring(0, 30),
              offset: startPosition.offset
            } : null,
            endPosition: endPosition ? {
              text: endPosition.textNode.textContent?.substring(0, 30),
              offset: endPosition.offset
            } : null
          });
        }
        return { startPosition, endPosition };
      } catch (error) {
        console.error("[KeyPilot] Error finding rectangle boundary positions:", error);
        return { startPosition: null, endPosition: null };
      }
    }
    /**
     * Find the first character in a text node that falls within the rectangle
     * @param {Text} textNode - Text node to search
     * @param {Object} rectBounds - Rectangle bounds in document coordinates
     * @returns {number} - Character offset or -1 if none found
     */
    findFirstCharacterInRectangle(textNode, rectBounds) {
      const text = textNode.textContent;
      if (!text) return -1;
      for (let i = 0; i < text.length; i++) {
        if (this.isCharacterInRectangle(textNode, i, rectBounds)) {
          return i;
        }
      }
      return -1;
    }
    /**
     * Find the last character in a text node that falls within the rectangle
     * @param {Text} textNode - Text node to search
     * @param {Object} rectBounds - Rectangle bounds in document coordinates
     * @returns {number} - Character offset + 1 (for range end) or -1 if none found
     */
    findLastCharacterInRectangle(textNode, rectBounds) {
      const text = textNode.textContent;
      if (!text) return -1;
      for (let i = text.length - 1; i >= 0; i--) {
        if (this.isCharacterInRectangle(textNode, i, rectBounds)) {
          return i + 1;
        }
      }
      return -1;
    }
    /**
     * Find text nodes that intersect with the rectangle
     * @param {Object} rectBounds - Rectangle bounds in document coordinates
     * @returns {Text[]} - Array of intersecting text nodes
     */
    // =============================================================================
    // VISUAL HIGHLIGHTING - Text node finding functions
    // These functions are specifically for visual highlighting that shows green
    // rectangles over text for UI feedback (not actual text selections)
    // =============================================================================
    /**
     * VISUAL HIGHLIGHTING: Finds text nodes for displaying highlight rectangles
     * Used by updateHighlightOverlay() to show green rectangles over text for UI feedback
     * @param {Object} rectBounds - Rectangle bounds with left, top, right, bottom properties
     * @returns {Array} - Array of text nodes that intersect with the rectangle
     */
    findTextNodesForHighlighting(rectBounds) {
      const textNodes = [];
      try {
        const walker = document.createTreeWalker(
          document.body,
          NodeFilter.SHOW_TEXT,
          {
            acceptNode: (node2) => {
              if (!node2.textContent || !node2.textContent.trim()) {
                return NodeFilter.FILTER_REJECT;
              }
              if (this.textNodeIntersectsRectangle(node2, rectBounds)) {
                return NodeFilter.FILTER_ACCEPT;
              }
              return NodeFilter.FILTER_REJECT;
            }
          }
        );
        let node;
        while (node = walker.nextNode()) {
          textNodes.push(node);
        }
      } catch (error) {
        console.warn("[KeyPilot] Error finding text nodes in rectangle:", error);
      }
      return textNodes;
    }
    /**
     * Check if a text node intersects with the rectangle
     * @param {Text} textNode - Text node to check
     * @param {Object} rectBounds - Rectangle bounds in document coordinates
     * @returns {boolean} - True if intersects
     */
    textNodeIntersectsRectangle(textNode, rectBounds) {
      try {
        const range = document.createRange();
        range.selectNodeContents(textNode);
        const rect = range.getBoundingClientRect();
        const scrollX = window.pageXOffset || document.documentElement.scrollLeft;
        const scrollY = window.pageYOffset || document.documentElement.scrollTop;
        const nodeLeft = rect.left + scrollX;
        const nodeTop = rect.top + scrollY;
        const nodeRight = nodeLeft + rect.width;
        const nodeBottom = nodeTop + rect.height;
        return !(nodeRight < rectBounds.left || nodeLeft > rectBounds.right || nodeBottom < rectBounds.top || nodeTop > rectBounds.bottom);
      } catch (error) {
        console.warn("[KeyPilot] Error checking text node intersection:", error);
        return false;
      }
    }
    /**
     * Check if a character position is within rectangle bounds
     * @param {Text} textNode - Text node containing the character
     * @param {number} offset - Character offset within the text node
     * @param {Object} rectBounds - Rectangle bounds in document coordinates
     * @returns {boolean} - True if character is within bounds
     */
    isCharacterInRectangle(textNode, offset, rectBounds) {
      try {
        const range = document.createRange();
        range.setStart(textNode, offset);
        range.setEnd(textNode, Math.min(offset + 1, textNode.textContent.length));
        const rect = range.getBoundingClientRect();
        if (rect.width === 0 && rect.height === 0) {
          return false;
        }
        const scrollX = window.pageXOffset || document.documentElement.scrollLeft;
        const scrollY = window.pageYOffset || document.documentElement.scrollTop;
        const charLeft = rect.left + scrollX;
        const charTop = rect.top + scrollY;
        const charRight = charLeft + rect.width;
        const charBottom = charTop + rect.height;
        const charCenterX = charLeft + rect.width / 2;
        const charCenterY = charTop + rect.height / 2;
        return charCenterX >= rectBounds.left && charCenterX <= rectBounds.right && charCenterY >= rectBounds.top && charCenterY <= rectBounds.bottom;
      } catch (error) {
        console.warn("[KeyPilot] Error checking character in rectangle:", error);
        return false;
      }
    }
    /**
     * Read selected text without tearing down session state.
     * Use before async clipboard so highlight mode can exit cleanly first.
     * @returns {string}
     */
    peekCharacterSelectedText() {
      try {
        const selection = window.getSelection();
        return selection ? selection.toString() || "" : "";
      } catch {
        return "";
      }
    }
    /**
     * Complete character selection and return the selected text
     * @returns {string|null} - Selected text or null if no selection
     */
    completeCharacterSelection() {
      try {
        const selectedText = this.peekCharacterSelectedText();
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Character selection completed:", {
            selectedText: selectedText.substring(0, 100),
            length: selectedText.length,
            wasActive: this.characterSelectionActive
          });
        }
        return selectedText;
      } catch (error) {
        console.error("[KeyPilot] Error completing character selection:", error);
        return null;
      } finally {
        this.clearCharacterSelection();
      }
    }
    /**
     * Clear the current character selection without completing it
     */
    clearCharacterSelection() {
      try {
        const selection = window.getSelection();
        if (selection) {
          selection.removeAllRanges();
        }
        this.clearHighlightSelectionOverlays();
        if (this.highlightRectangleOverlay) {
          this.highlightRectangleOverlay.style.display = "none";
        }
        this.hideDebugHUD?.();
      } catch (error) {
        console.warn("[KeyPilot] Error clearing character selection:", error);
      } finally {
        this.resetCharacterSelection();
      }
    }
    /**
     * Reset character selection state
     */
    resetCharacterSelection() {
      this.characterSelectionActive = false;
      this.characterStartPosition = null;
      this.characterStartTextNode = null;
      this.characterStartOffset = 0;
      this.resetRectangleSelection();
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Character selection state reset");
      }
    }
    /**
     * Compare the document positions of two text nodes
     * @param {Text} node1 - First text node
     * @param {Text} node2 - Second text node
     * @returns {number} - Negative if node1 comes before node2, positive if after, 0 if same
     */
    compareTextNodePositions(node1, node2) {
      if (node1 === node2) {
        return 0;
      }
      try {
        const comparison = node1.compareDocumentPosition(node2);
        if (comparison & Node.DOCUMENT_POSITION_FOLLOWING) {
          return -1;
        } else if (comparison & Node.DOCUMENT_POSITION_PRECEDING) {
          return 1;
        } else {
          return 0;
        }
      } catch (error) {
        console.warn("[KeyPilot] Error comparing text node positions:", error);
        return 0;
      }
    }
    /**
     * Get selection object for the given document context
     * @param {Document} ownerDocument - Document context
     * @returns {Selection|null} - Selection object or null
     */
    getSelectionForDocument(ownerDocument) {
      try {
        if (ownerDocument && ownerDocument.getSelection) {
          return ownerDocument.getSelection();
        }
        return window.getSelection();
      } catch (error) {
        console.warn("[KeyPilot] Error getting selection for document:", error);
        return window.getSelection();
      }
    }
    /**
     * Reset rectangle selection state
     */
    resetRectangleSelection() {
      this.rectOriginPoint = null;
      this.rectOriginDocumentPoint = null;
      this.rectangleStartCaret = null;
      this.elementMatchedElements = [];
      this.debugUpdateCount = 0;
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Rectangle selection state reset");
      }
      this.hideDebugHUD();
    }
    /**
     * Toggle debug HUD feature flag
     */
    toggleDebugHUD(enabled) {
      FEATURE_FLAGS.DEBUG_RECTANGLE_HUD = enabled;
      if (!enabled) {
        this.hideDebugHUD();
      }
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Debug HUD toggled:", enabled);
      }
    }
    /**
     * Create or show the debug HUD for rectangle selection
     */
    showDebugHUD() {
      if (!FEATURE_FLAGS.DEBUG_RECTANGLE_HUD && !window.KEYPILOT_DEBUG) {
        return;
      }
      if (this.debugHUD) {
        this.debugHUD.style.display = "block";
        return;
      }
      this.debugHUD = this.createElement("div", {
        className: "kpv2-rectangle-debug-hud",
        style: `
        position: fixed;
        top: 10px;
        left: 10px;
        background: rgba(0, 0, 0, 0.9);
        color: #00ff00;
        font-family: 'Courier New', monospace;
        font-size: 12px;
        padding: 15px;
        border-radius: 8px;
        z-index: ${Z_INDEX.DEBUG_HUD};
        pointer-events: none;
        white-space: pre-line;
        min-width: 400px;
        max-width: 500px;
        border: 2px solid #00ff00;
        box-shadow: 0 0 10px rgba(0, 255, 0, 0.3);
      `
      });
      document.body.appendChild(this.debugHUD);
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Debug HUD created and shown");
      }
    }
    /**
     * Hide the debug HUD
     */
    hideDebugHUD() {
      if (this.debugHUD) {
        this.debugHUD.style.display = "none";
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Debug HUD hidden");
        }
      }
    }
    /**
     * Update the debug HUD with current rectangle information
     */
    updateDebugHUD(rectOriginPoint, currentPosition, rectOriginDocumentPoint, currentDocumentPosition, calculatedValues) {
      if (!FEATURE_FLAGS.DEBUG_RECTANGLE_HUD && !window.KEYPILOT_DEBUG || !this.debugHUD) {
        return;
      }
      this.debugUpdateCount++;
      const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
      const pageInfo = {
        scrollX: window.scrollX,
        scrollY: window.scrollY,
        innerWidth: window.innerWidth,
        innerHeight: window.innerHeight,
        documentWidth: document.documentElement.scrollWidth,
        documentHeight: document.documentElement.scrollHeight
      };
      const debugInfo = `\u{1F3AF} RECTANGLE DEBUG HUD - Update #${this.debugUpdateCount}
\u23F0 Time: ${timestamp}

\u{1F4CD} VIEWPORT COORDINATES:
  Origin: (${rectOriginPoint?.x || "null"}, ${rectOriginPoint?.y || "null"})
  Current: (${currentPosition?.x || "null"}, ${currentPosition?.y || "null"})
  Delta: (${currentPosition && rectOriginPoint ? currentPosition.x - rectOriginPoint.x : "null"}, ${currentPosition && rectOriginPoint ? currentPosition.y - rectOriginPoint.y : "null"})

\u{1F4C4} DOCUMENT COORDINATES:
  Origin: (${rectOriginDocumentPoint?.x || "null"}, ${rectOriginDocumentPoint?.y || "null"})
  Current: (${currentDocumentPosition?.x || "null"}, ${currentDocumentPosition?.y || "null"})
  Delta: (${currentDocumentPosition && rectOriginDocumentPoint ? currentDocumentPosition.x - rectOriginDocumentPoint.x : "null"}, ${currentDocumentPosition && rectOriginDocumentPoint ? currentDocumentPosition.y - rectOriginDocumentPoint.y : "null"})

\u{1F4CF} CALCULATED RECTANGLE:
  Document Left: ${calculatedValues?.documentLeft || "null"}
  Document Top: ${calculatedValues?.documentTop || "null"}
  Width: ${calculatedValues?.width || "null"}
  Height: ${calculatedValues?.height || "null"}
  Viewport Left: ${calculatedValues?.viewportLeft || "null"}
  Viewport Top: ${calculatedValues?.viewportTop || "null"}
  Quadrant: ${calculatedValues?.quadrant || "null"}

\u{1F310} PAGE CONTEXT:
  Scroll: (${pageInfo.scrollX}, ${pageInfo.scrollY})
  Viewport: ${pageInfo.innerWidth} \xD7 ${pageInfo.innerHeight}
  Document: ${pageInfo.documentWidth} \xD7 ${pageInfo.documentHeight}

\u{1F4D0} RECTANGLE INFO:
  Area: ${calculatedValues?.width * calculatedValues?.height || 0}px\xB2
  Min Width: ${RECTANGLE_SELECTION.MIN_WIDTH}px
  Min Height: ${RECTANGLE_SELECTION.MIN_HEIGHT}px
  Min Drag: ${RECTANGLE_SELECTION.MIN_DRAG_DISTANCE}px

\u{1F527} OVERLAY CALLS:
  Position: left=${calculatedValues?.viewportLeft}px, top=${calculatedValues?.viewportTop}px
  Size: width=${calculatedValues?.width}px, height=${calculatedValues?.height}px
  Drag Distance: ${Math.sqrt((currentDocumentPosition?.x - rectOriginDocumentPoint?.x) ** 2 + (currentDocumentPosition?.y - rectOriginDocumentPoint?.y) ** 2).toFixed(1)}px
  Should Show: ${this.shouldShowRectangle(calculatedValues?.width, calculatedValues?.height, currentDocumentPosition?.x - rectOriginDocumentPoint?.x, currentDocumentPosition?.y - rectOriginDocumentPoint?.y)}
  Display: ${this.shouldShowRectangle(calculatedValues?.width, calculatedValues?.height, currentDocumentPosition?.x - rectOriginDocumentPoint?.x, currentDocumentPosition?.y - rectOriginDocumentPoint?.y) ? "block" : "none"}
  Visibility: ${this.shouldShowRectangle(calculatedValues?.width, calculatedValues?.height, currentDocumentPosition?.x - rectOriginDocumentPoint?.x, currentDocumentPosition?.y - rectOriginDocumentPoint?.y) ? "visible" : "hidden"}`;
      this.debugHUD.textContent = debugInfo;
    }
    /**
     * Update selection overlays to highlight the actual selected text regions with shadow DOM support
     * @param {Selection} selection - Browser Selection object
     */
    updateHighlightSelectionOverlays(selection) {
      this.clearHighlightSelectionOverlays();
      if (!selection || selection.rangeCount === 0) {
        return;
      }
      try {
        const MAX_SELECTION_OVERLAY_RECTS = 80;
        let created = 0;
        for (let i = 0; i < selection.rangeCount; i++) {
          const range = selection.getRangeAt(i);
          created += this.createSelectionOverlaysForRangeWithShadowSupport(range, MAX_SELECTION_OVERLAY_RECTS - created);
          if (created >= MAX_SELECTION_OVERLAY_RECTS) break;
        }
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Updated highlight selection overlays with shadow DOM support:", {
            rangeCount: selection.rangeCount,
            overlayCount: this.highlightSelectionOverlays.length,
            selectedText: selection.toString().substring(0, 50)
          });
        }
      } catch (error) {
        console.warn("[KeyPilot] Error updating highlight selection overlays with shadow DOM support:", error);
        this.clearHighlightSelectionOverlays();
      }
    }
    /**
     * Create selection overlays for a specific range with shadow DOM support
     * @param {Range} range - DOM Range object
     * @param {number} [maxRects=80] - Max client rects to materialize as overlay nodes
     * @returns {number} - Number of overlays created
     */
    createSelectionOverlaysForRangeWithShadowSupport(range, maxRects = 80) {
      if (!range || range.collapsed) {
        return 0;
      }
      let created = 0;
      try {
        const rects = this.getClientRectsWithShadowSupport(range);
        const limit = Math.max(0, maxRects);
        for (let i = 0; i < rects.length && created < limit; i++) {
          const rect = rects[i];
          if (rect.width <= 0 || rect.height <= 0) {
            continue;
          }
          const overlay = this.createElement("div", {
            className: "kpv2-highlight-selection-overlay",
            style: `
            position: fixed;
            left: ${rect.left}px;
            top: ${rect.top}px;
            width: ${rect.width}px;
            height: ${rect.height}px;
            background: ${COLORS.HIGHLIGHT_SELECTION_BG};
            border: 1px solid ${COLORS.HIGHLIGHT_BLUE};
            pointer-events: none;
            z-index: ${Z_INDEX.OVERLAYS_BELOW};
            will-change: transform;
            opacity: 0.7;
          `
          });
          document.body.appendChild(overlay);
          this.highlightSelectionOverlays.push(overlay);
          created++;
          if (this.overlayObserver) {
            this.overlayObserver.observe(overlay);
          }
        }
        if (window.KEYPILOT_DEBUG && rects.length > 0) {
          console.log("[KeyPilot Debug] Created selection overlays for range with shadow DOM support:", {
            rectCount: rects.length,
            created,
            firstRect: {
              left: rects[0].left,
              top: rects[0].top,
              width: rects[0].width,
              height: rects[0].height
            }
          });
        }
      } catch (error) {
        console.warn("[KeyPilot] Error creating selection overlays for range with shadow DOM support:", error);
      }
      return created;
    }
    /**
     * Get client rectangles for a range with shadow DOM support
     * @param {Range} range - DOM Range object
     * @returns {DOMRectList|Array} - Client rectangles
     */
    getClientRectsWithShadowSupport(range) {
      try {
        const rects = range.getClientRects();
        if (rects && rects.length > 0) {
          return rects;
        }
        return this.getAlternativeClientRects(range);
      } catch (error) {
        console.warn("[KeyPilot] Error getting client rects with shadow DOM support:", error);
        return [];
      }
    }
    /**
     * Get alternative client rectangles for shadow DOM ranges
     * @param {Range} range - DOM Range object
     * @returns {Array} - Array of rectangle objects
     */
    getAlternativeClientRects(range) {
      try {
        const rects = [];
        const boundingRect = range.getBoundingClientRect();
        if (boundingRect && boundingRect.width > 0 && boundingRect.height > 0) {
          rects.push(boundingRect);
        }
        if (rects.length === 0) {
          const shadowRects = this.calculateShadowDOMRects(range);
          rects.push(...shadowRects);
        }
        return rects;
      } catch (error) {
        console.warn("[KeyPilot] Error getting alternative client rects:", error);
        return [];
      }
    }
    /**
     * Calculate rectangles for shadow DOM ranges manually
     * @param {Range} range - DOM Range object
     * @returns {Array} - Array of rectangle objects
     */
    calculateShadowDOMRects(range) {
      try {
        const rects = [];
        const startContainer = range.startContainer;
        const endContainer = range.endContainer;
        if (startContainer === endContainer && startContainer.nodeType === Node.TEXT_NODE) {
          const textRect = this.getTextNodeRect(startContainer, range.startOffset, range.endOffset);
          if (textRect) {
            rects.push(textRect);
          }
        } else {
          try {
            const boundingRect = range.getBoundingClientRect();
            if (boundingRect && boundingRect.width > 0 && boundingRect.height > 0) {
              rects.push(boundingRect);
            }
          } catch (error) {
          }
        }
        return rects;
      } catch (error) {
        console.warn("[KeyPilot] Error calculating shadow DOM rects:", error);
        return [];
      }
    }
    /**
     * Get rectangle for a portion of a text node
     * @param {Text} textNode - Text node
     * @param {number} startOffset - Start character offset
     * @param {number} endOffset - End character offset
     * @returns {DOMRect|null} - Rectangle or null
     */
    getTextNodeRect(textNode, startOffset, endOffset) {
      try {
        const ownerDocument = textNode.ownerDocument || document;
        const tempRange = ownerDocument.createRange();
        tempRange.setStart(textNode, startOffset);
        tempRange.setEnd(textNode, endOffset);
        const rect = tempRange.getBoundingClientRect();
        return rect.width > 0 && rect.height > 0 ? rect : null;
      } catch (error) {
        console.warn("[KeyPilot] Error getting text node rect:", error);
        return null;
      }
    }
    /**
     * Create highlight selection overlay for a range (legacy method for compatibility)
     */
    createHighlightSelectionOverlay(range) {
      this.createSelectionOverlaysForRangeWithShadowSupport(range);
    }
    /**
     * Clear all highlight selection overlays
     */
    clearHighlightSelectionOverlays() {
      this.highlightSelectionOverlays.forEach((overlay) => {
        if (this.overlayObserver) {
          this.overlayObserver.unobserve(overlay);
        }
        overlay.remove();
      });
      this.highlightSelectionOverlays = [];
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Cleared highlight selection overlays");
      }
    }
    /**
     * Show companion instruction overlay while selection is active.
     * @param {{ finishKey?: string }} [opts] - physical key to press again (e.g. "H" or "Y")
     */
    showHighlightModeIndicator(opts = {}) {
      const finishKeyRaw = opts.finishKey || (this.selectionMode === "character" ? "H" : "Y");
      const finishKey = String(finishKeyRaw).toUpperCase();
      const modeText = `Press ${finishKey} again to finish selection`;
      if (this.highlightModeIndicator) {
        this.highlightModeIndicator.textContent = modeText;
        this.highlightModeIndicator.style.display = "block";
        return;
      }
      this.highlightModeIndicator = this.createElement("div", {
        className: "kpv2-highlight-mode-indicator",
        style: `
        position: fixed;
        top: 20px;
        right: 20px;
        background: ${COLORS.HIGHLIGHT_BLUE};
        color: white;
        padding: 10px 14px;
        font-size: 14px;
        font-weight: bold;
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        border-radius: 6px;
        box-shadow: 0 2px 10px ${COLORS.HIGHLIGHT_SHADOW};
        z-index: ${Z_INDEX.MESSAGE_BOX};
        pointer-events: none;
        will-change: transform, opacity;
        animation: kpv2-pulse 1.5s ease-in-out infinite;
        letter-spacing: 0.01em;
      `
      });
      this.highlightModeIndicator.textContent = modeText;
      document.body.appendChild(this.highlightModeIndicator);
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Highlight mode indicator shown:", modeText);
      }
    }
    /**
     * Hide highlight mode indicator
     */
    hideHighlightModeIndicator() {
      if (this.highlightModeIndicator) {
        this.highlightModeIndicator.remove();
        this.highlightModeIndicator = null;
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Highlight mode indicator hidden");
        }
      }
    }
    /**
     * Set overlay visibility
     */
    setOverlayVisibility(overlayType, isVisible) {
      if (overlayType === "highlight" && this.highlightOverlay) {
        this.overlayVisibility.highlight = isVisible;
        this.highlightOverlay.style.visibility = isVisible ? "visible" : "hidden";
      } else if (overlayType === "highlightRectangle" && this.highlightRectangleOverlay) {
        this.overlayVisibility.highlightRectangle = isVisible;
        this.highlightRectangleOverlay.style.visibility = isVisible ? "visible" : "hidden";
      }
    }
    /**
     * Clean up all highlight overlays and resources
     */
    cleanup() {
      if (this.highlightOverlay) {
        this.highlightOverlay.remove();
        this.highlightOverlay = null;
      }
      if (this.highlightRectangleOverlay) {
        this.highlightRectangleOverlay.remove();
        this.highlightRectangleOverlay = null;
      }
      this.clearHighlightSelectionOverlays();
      if (this.highlightModeIndicator) {
        this.highlightModeIndicator.remove();
        this.highlightModeIndicator = null;
      }
      if (this.debugHUD) {
        this.debugHUD.remove();
        this.debugHUD = null;
      }
      this.resetCharacterSelection();
      this.resetRectangleSelection();
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] HighlightManager cleanup completed");
      }
    }
  };

  // src/modules/popup-manager.js
  init_constants();

  // src/utils/popover-resize.js
  function makePopoverResizable(panel, options = {}) {
    if (!panel || !(panel instanceof Element)) return null;
    try {
      if (panel.dataset && panel.dataset.kpResizable === "1") {
        return panel.__kpResizeApi || null;
      }
    } catch {
    }
    const doc = panel.ownerDocument || document;
    const mount = options.mount || panel;
    const minWidth = Math.max(160, Number(options.minWidth) || 280);
    const minHeight = Math.max(120, Number(options.minHeight) || 180);
    const margin = Math.max(0, Number(options.margin) || 8);
    const handleZ = options.zIndex != null ? String(options.zIndex) : "20";
    const aspectRatioOpt = options.aspectRatio;
    const lockAspect = aspectRatioOpt === true || typeof aspectRatioOpt === "number" && aspectRatioOpt > 0;
    const fixedAspect = typeof aspectRatioOpt === "number" && aspectRatioOpt > 0 ? aspectRatioOpt : null;
    const EDGE = 6;
    const CORNER = 12;
    const handles = [];
    let drag = null;
    let frozenIframes = [];
    let prevBodyCursor = "";
    let prevBodyUserSelect = "";
    const dirs = [
      { dir: "n", cursor: "n-resize" },
      { dir: "s", cursor: "s-resize" },
      { dir: "e", cursor: "e-resize" },
      { dir: "w", cursor: "w-resize" },
      { dir: "ne", cursor: "ne-resize" },
      { dir: "nw", cursor: "nw-resize" },
      { dir: "se", cursor: "se-resize" },
      { dir: "sw", cursor: "sw-resize" }
    ];
    try {
      const cs = doc.defaultView?.getComputedStyle?.(panel);
      if (cs && (cs.position === "static" || !cs.position)) {
        panel.style.position = "fixed";
      }
    } catch {
      panel.style.position = panel.style.position || "fixed";
    }
    const handleStyleBase = `
    position: absolute;
    z-index: ${handleZ};
    background: transparent;
    touch-action: none;
    pointer-events: auto;
  `;
    const placeHandle = (el2, dir) => {
      el2.style.top = el2.style.right = el2.style.bottom = el2.style.left = "";
      el2.style.width = el2.style.height = "";
      if (dir === "n" || dir === "s") {
        el2.style.left = `${CORNER}px`;
        el2.style.right = `${CORNER}px`;
        el2.style.height = `${EDGE}px`;
        el2.style.width = "auto";
        if (dir === "n") el2.style.top = "0";
        else el2.style.bottom = "0";
      } else if (dir === "e" || dir === "w") {
        el2.style.top = `${CORNER}px`;
        el2.style.bottom = `${CORNER}px`;
        el2.style.width = `${EDGE}px`;
        el2.style.height = "auto";
        if (dir === "e") el2.style.right = "0";
        else el2.style.left = "0";
      } else {
        el2.style.width = `${CORNER}px`;
        el2.style.height = `${CORNER}px`;
        if (dir.includes("n")) el2.style.top = "0";
        if (dir.includes("s")) el2.style.bottom = "0";
        if (dir.includes("e")) el2.style.right = "0";
        if (dir.includes("w")) el2.style.left = "0";
      }
    };
    for (const { dir, cursor } of dirs) {
      const el2 = doc.createElement("div");
      el2.className = `kpv2-popover-resize-handle kpv2-popover-resize-${dir}`;
      el2.dataset.kpResizeDir = dir;
      el2.setAttribute("aria-hidden", "true");
      el2.style.cssText = handleStyleBase + `cursor: ${cursor};`;
      placeHandle(el2, dir);
      if (dir === "se") {
        el2.style.width = "16px";
        el2.style.height = "16px";
        el2.style.display = "flex";
        el2.style.alignItems = "flex-end";
        el2.style.justifyContent = "flex-end";
        el2.style.padding = "0 2px 2px 0";
        el2.style.color = "rgba(255,255,255,0.45)";
        el2.title = "Resize";
        const svg = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
        svg.setAttribute("viewBox", "0 0 12 12");
        svg.setAttribute("width", "12");
        svg.setAttribute("height", "12");
        svg.setAttribute("aria-hidden", "true");
        svg.style.cssText = "display:block; pointer-events:none;";
        const lines = [
          "M4 11 L11 4",
          "M7 11 L11 7",
          "M10 11 L11 10"
        ];
        for (const d of lines) {
          const path = doc.createElementNS("http://www.w3.org/2000/svg", "path");
          path.setAttribute("d", d);
          path.setAttribute("fill", "none");
          path.setAttribute("stroke", "currentColor");
          path.setAttribute("stroke-width", "1.5");
          path.setAttribute("stroke-linecap", "round");
          svg.appendChild(path);
        }
        el2.appendChild(svg);
        el2.addEventListener("mouseenter", () => {
          el2.style.color = "rgba(255,255,255,0.75)";
        });
        el2.addEventListener("mouseleave", () => {
          if (!drag) el2.style.color = "rgba(255,255,255,0.45)";
        });
      }
      handles.push({ el: el2, dir });
      try {
        mount.appendChild(el2);
      } catch {
        panel.appendChild(el2);
      }
    }
    const pinGeometry = () => {
      const rect = panel.getBoundingClientRect();
      const s = panel.style;
      s.position = "fixed";
      s.transform = "none";
      try {
        s.webkitTransform = "none";
      } catch {
      }
      s.right = "auto";
      s.bottom = "auto";
      s.left = `${rect.left}px`;
      s.top = `${rect.top}px`;
      s.width = `${rect.width}px`;
      s.height = `${rect.height}px`;
      s.margin = "0";
      s.maxWidth = "none";
      s.maxHeight = "none";
      s.boxSizing = "border-box";
      return {
        left: rect.left,
        top: rect.top,
        width: rect.width,
        height: rect.height
      };
    };
    const freezeIframes = () => {
      frozenIframes = [];
      try {
        const list = [
          ...panel.querySelectorAll("iframe"),
          ...panel.shadowRoot?.querySelectorAll?.("iframe") || []
        ];
        list.forEach((iframe) => {
          frozenIframes.push(iframe);
          try {
            iframe.dataset.kpPrevPe = iframe.style.pointerEvents || "";
            iframe.style.pointerEvents = "none";
          } catch {
          }
        });
      } catch {
      }
    };
    const unfreezeIframes = () => {
      for (const iframe of frozenIframes) {
        try {
          iframe.style.pointerEvents = iframe.dataset.kpPrevPe || "";
          delete iframe.dataset.kpPrevPe;
        } catch {
        }
      }
      frozenIframes = [];
    };
    const clampRect = (left, top, width, height, opts = {}) => {
      const vw = doc.defaultView?.innerWidth ?? window.innerWidth;
      const vh = doc.defaultView?.innerHeight ?? window.innerHeight;
      const maxW = Math.max(minWidth, vw - margin * 2);
      const maxH = Math.max(minHeight, vh - margin * 2);
      const aspect = typeof opts.aspect === "number" && opts.aspect > 0 ? opts.aspect : null;
      let w = width;
      let h = height;
      let l = left;
      let t = top;
      if (aspect) {
        const minWAspect = Math.max(minWidth, minHeight * aspect);
        const minHAspect = minWAspect / aspect;
        let fitW = Math.min(Math.max(w, minWAspect), maxW);
        let fitH = fitW / aspect;
        if (fitH > maxH) {
          fitH = maxH;
          fitW = fitH * aspect;
        }
        if (fitW < minWAspect) {
          fitW = minWAspect;
          fitH = minHAspect;
        }
        if (fitH > maxH) {
          fitH = Math.min(maxH, Math.max(minHAspect, maxH));
          fitW = fitH * aspect;
        }
        w = fitW;
        h = fitH;
        if (opts.anchorRight) l = left + (width - w);
        if (opts.anchorBottom) t = top + (height - h);
      } else {
        w = Math.min(Math.max(width, minWidth), maxW);
        h = Math.min(Math.max(height, minHeight), maxH);
      }
      if (l < margin) l = margin;
      if (t < margin) t = margin;
      if (l + w > vw - margin) l = Math.max(margin, vw - margin - w);
      if (t + h > vh - margin) t = Math.max(margin, vh - margin - h);
      if (!aspect) {
        w = Math.min(w, vw - margin - l);
        h = Math.min(h, vh - margin - t);
        w = Math.max(minWidth, w);
        h = Math.max(minHeight, h);
      } else {
        const availW = vw - margin - l;
        const availH = vh - margin - t;
        if (w > availW || h > availH) {
          const scale = Math.min(availW / w, availH / h, 1);
          w = Math.max(minWidth, w * scale);
          h = w / aspect;
        }
      }
      return { left: l, top: t, width: w, height: h };
    };
    const applyRect = (r) => {
      panel.style.left = `${r.left}px`;
      panel.style.top = `${r.top}px`;
      panel.style.width = `${r.width}px`;
      panel.style.height = `${r.height}px`;
      try {
        options.onResize?.(r);
      } catch {
      }
    };
    const onPointerMove = (e) => {
      if (!drag) return;
      const dx = e.clientX - drag.startX;
      const dy = e.clientY - drag.startY;
      const { left: ol, top: ot, width: ow, height: oh } = drag.origin;
      const dir = drag.dir;
      const aspect = drag.aspect || null;
      let left = ol;
      let top = ot;
      let width = ow;
      let height = oh;
      if (aspect) {
        const moveE = dir.includes("e");
        const moveW = dir.includes("w");
        const moveS = dir.includes("s");
        const moveN = dir.includes("n");
        const horizontal = moveE || moveW;
        const vertical = moveS || moveN;
        if (horizontal && vertical) {
          const candidateW = moveE ? ow + dx : ow - dx;
          const candidateH = moveS ? oh + dy : oh - dy;
          const fromW = Math.abs(candidateW / ow);
          const fromH = Math.abs(candidateH / oh);
          if (fromW >= fromH) {
            width = candidateW;
            height = width / aspect;
          } else {
            height = candidateH;
            width = height * aspect;
          }
        } else if (horizontal) {
          width = moveE ? ow + dx : ow - dx;
          height = width / aspect;
        } else {
          height = moveS ? oh + dy : oh - dy;
          width = height * aspect;
        }
        if (moveW) left = ol + (ow - width);
        if (moveN) top = ot + (oh - height);
        applyRect(clampRect(left, top, width, height, {
          aspect,
          anchorRight: moveW,
          anchorBottom: moveN
        }));
        return;
      }
      if (dir.includes("e")) width = ow + dx;
      if (dir.includes("s")) height = oh + dy;
      if (dir.includes("w")) {
        width = ow - dx;
        left = ol + dx;
        if (width < minWidth) {
          width = minWidth;
          left = ol + ow - minWidth;
        }
      }
      if (dir.includes("n")) {
        height = oh - dy;
        top = ot + dy;
        if (height < minHeight) {
          height = minHeight;
          top = ot + oh - minHeight;
        }
      }
      applyRect(clampRect(left, top, width, height));
    };
    const endDrag = (e) => {
      if (!drag) return;
      const pointerId = drag.pointerId;
      const dir = drag.dir;
      drag = null;
      try {
        if (e && typeof e.pointerId === "number") {
        }
      } catch {
      }
      unfreezeIframes();
      try {
        doc.body.style.cursor = prevBodyCursor;
        doc.body.style.userSelect = prevBodyUserSelect;
      } catch {
      }
      doc.removeEventListener("pointermove", onPointerMove, true);
      doc.removeEventListener("pointerup", endDrag, true);
      doc.removeEventListener("pointercancel", endDrag, true);
      const se = handles.find((h) => h.dir === "se");
      if (se) se.el.style.color = "rgba(255,255,255,0.45)";
      try {
        options.onResizeEnd?.();
      } catch {
      }
      void pointerId;
      void dir;
    };
    const onPointerDown = (e, dir) => {
      if (e.pointerType === "mouse" && e.button !== 0) return;
      e.preventDefault();
      e.stopPropagation();
      try {
        options.onResizeStart?.();
      } catch {
      }
      const origin = pinGeometry();
      let aspect = fixedAspect;
      if (lockAspect && !aspect && origin.height > 0) {
        aspect = origin.width / origin.height;
      }
      drag = {
        dir,
        startX: e.clientX,
        startY: e.clientY,
        origin,
        pointerId: e.pointerId,
        aspect: aspect || void 0
      };
      freezeIframes();
      try {
        prevBodyCursor = doc.body.style.cursor || "";
        prevBodyUserSelect = doc.body.style.userSelect || "";
        const cursorMap = {
          n: "n-resize",
          s: "s-resize",
          e: "e-resize",
          w: "w-resize",
          ne: "ne-resize",
          nw: "nw-resize",
          se: "se-resize",
          sw: "sw-resize"
        };
        doc.body.style.cursor = cursorMap[dir] || "se-resize";
        doc.body.style.userSelect = "none";
      } catch {
      }
      doc.addEventListener("pointermove", onPointerMove, true);
      doc.addEventListener("pointerup", endDrag, true);
      doc.addEventListener("pointercancel", endDrag, true);
      try {
        e.currentTarget?.setPointerCapture?.(e.pointerId);
      } catch {
      }
    };
    for (const { el: el2, dir } of handles) {
      el2.addEventListener("pointerdown", (e) => onPointerDown(e, dir));
    }
    const dispose = () => {
      endDrag();
      for (const { el: el2 } of handles) {
        try {
          el2.remove();
        } catch {
        }
      }
      handles.length = 0;
      try {
        if (panel.dataset) delete panel.dataset.kpResizable;
      } catch {
      }
      try {
        delete panel.__kpResizeApi;
      } catch {
      }
    };
    try {
      panel.dataset.kpResizable = "1";
    } catch {
    }
    const api = { dispose };
    try {
      panel.__kpResizeApi = api;
    } catch {
    }
    return api;
  }

  // src/modules/popup-manager.js
  var PopupManager = class {
    /**
     * @param {object} [opts]
     * @param {Document} [opts.doc]
     * @param {(type: string, data: any) => void} [opts.onPanelChange] Optional callback for panel lifecycle events
     */
    constructor({ doc, onPanelChange } = {}) {
      this.doc = doc || document;
      this._backdrop = null;
      this._stack = [];
      this._onPanelChange = typeof onPanelChange === "function" ? onPanelChange : null;
      this._backdropClickHandler = this._backdropClickHandler.bind(this);
    }
    isOpen() {
      return this._stack.length > 0;
    }
    top() {
      return this._stack.length ? this._stack[this._stack.length - 1] : null;
    }
    /**
     * Request closing the topmost popup.
     * Prefers the popup's onRequestClose hook (so callers can synchronize app state),
     * and falls back to removing the modal directly.
     */
    requestCloseTop() {
      const top = this.top();
      if (!top) return;
      try {
        if (typeof top.onRequestClose === "function") {
          top.onRequestClose();
          return;
        }
      } catch {
      }
      this.hideModal(top.id);
    }
    /**
     * Close every modal in the stack (launcher, settings, guide, tab history, etc.).
     * Prefers each panel's onRequestClose so app state stays in sync; force-removes
     * anything that remains.
     */
    closeAll() {
      let guard = 32;
      while (this._stack.length && guard-- > 0) {
        const top = this.top();
        if (!top) break;
        const id = top.id;
        try {
          if (typeof top.onRequestClose === "function") {
            top.onRequestClose();
          }
        } catch {
        }
        if (this._stack.some((p) => p.id === id)) {
          this.hideModal(id);
        }
      }
      if (this._stack.length) {
        const leftover = this._stack.splice(0, this._stack.length);
        for (const entry of leftover) {
          try {
            entry?.resizeDispose?.();
          } catch {
          }
          try {
            entry?.panel?.remove?.();
          } catch {
          }
        }
      }
      if (this._backdrop) {
        try {
          this._backdrop.remove();
        } catch {
        }
        this._backdrop = null;
        if (this._onPanelChange) {
          try {
            this._onPanelChange("backdrop-hidden", {});
          } catch {
          }
        }
      }
    }
    /**
     * Show a modal popup panel with a shared blurred backdrop.
     * The panel is assigned a z-index *below* the click rectangle overlays.
     * Panels are resizable by default (edges + corners + SE grip icon).
     *
     * @param {object} params
     * @param {string} params.id
     * @param {HTMLElement} params.panel
     * @param {() => void} [params.onRequestClose]
     * @param {boolean} [params.resizable=true] Attach resize handles (default true)
     * @param {boolean} [params.blur=true] Blur + dim the page behind the panel
     * @param {{ minWidth?: number, minHeight?: number, margin?: number, aspectRatio?: number|true }} [params.resizeOptions]
     */
    showModal({ id, panel, onRequestClose, resizable = true, resizeOptions, blur = true } = {}) {
      if (!id || !panel) return;
      const wantBlur = blur !== false;
      const existingIdx = this._stack.findIndex((p) => p.id === id);
      if (existingIdx >= 0) {
        const existing = this._stack[existingIdx];
        existing.panel = panel;
        existing.onRequestClose = typeof onRequestClose === "function" ? onRequestClose : existing.onRequestClose;
        existing.blur = wantBlur;
        this._stack.splice(existingIdx, 1);
        this._stack.push(existing);
        this._ensureMounted();
        this._recomputeZ();
        this._ensureResizable(existing, resizable, resizeOptions);
        return;
      }
      const entry = {
        id: String(id),
        panel,
        onRequestClose: typeof onRequestClose === "function" ? onRequestClose : void 0,
        blur: wantBlur
      };
      this._stack.push(entry);
      this._ensureMounted();
      this._recomputeZ();
      this._ensureResizable(entry, resizable, resizeOptions);
      if (this._onPanelChange) {
        try {
          this._onPanelChange("panel-shown", { id: String(id), panel });
        } catch {
        }
      }
    }
    /**
     * @param {{ panel: HTMLElement, resizeDispose?: () => void }} entry
     * @param {boolean} resizable
     * @param {object} [resizeOptions]
     */
    _ensureResizable(entry, resizable, resizeOptions) {
      if (!entry?.panel) return;
      if (resizable === false) {
        try {
          entry.resizeDispose?.();
        } catch {
        }
        entry.resizeDispose = void 0;
        return;
      }
      if (typeof entry.resizeDispose === "function") return;
      try {
        const api = makePopoverResizable(entry.panel, resizeOptions || {});
        if (api && typeof api.dispose === "function") {
          entry.resizeDispose = () => {
            try {
              api.dispose();
            } catch {
            }
          };
        }
      } catch (e) {
        console.warn("[KeyPilot] Failed to attach popover resize handles:", e?.message || e);
      }
    }
    /**
     * Hide a popup by id (or the top popup if id is omitted).
     * @param {string} [id]
     */
    hideModal(id) {
      const targetId = typeof id === "string" && id ? id : this.top()?.id || null;
      if (!targetId) return;
      const idx = this._stack.findIndex((p) => p.id === targetId);
      if (idx < 0) return;
      const removed = this._stack.splice(idx, 1)[0];
      try {
        removed?.resizeDispose?.();
      } catch {
      }
      if (this._onPanelChange) {
        try {
          this._onPanelChange("panel-hidden", { id: targetId, panel: removed?.panel });
        } catch {
        }
      }
      try {
        removed?.panel?.style && (removed.panel.style.viewTransitionName = "none");
      } catch {
      }
      try {
        removed?.panel?.remove?.();
      } catch {
      }
      if (!this._stack.length) {
        try {
          if (this._backdrop) this._backdrop.style.viewTransitionName = "none";
        } catch {
        }
        try {
          this._backdrop?.remove?.();
        } catch {
        }
        this._backdrop = null;
        if (this._onPanelChange) {
          try {
            this._onPanelChange("backdrop-hidden", {});
          } catch {
          }
        }
      }
      this._recomputeZ();
      this._applyBackdropVisual();
    }
    _ensureMounted() {
      const doc = this.doc;
      if (!doc || !doc.body) return;
      if (!this._backdrop) {
        const el2 = doc.createElement("div");
        el2.className = CSS_CLASSES.POPUP_BACKDROP || "kpv2-popup-backdrop";
        Object.assign(el2.style, {
          position: "fixed",
          inset: "0",
          outline: "none",
          // zIndex assigned in _recomputeZ() before first paint when possible
          pointerEvents: "auto",
          viewTransitionName: "none"
        });
        el2.addEventListener("click", this._backdropClickHandler, true);
        this._backdrop = el2;
      }
      this._recomputeZ();
      this._applyBackdropVisual();
      const backdropJustMounted = !!(this._backdrop && !this._backdrop.isConnected);
      if (backdropJustMounted) {
        try {
          doc.body.appendChild(this._backdrop);
        } catch {
        }
        if (this._onPanelChange) {
          try {
            this._onPanelChange("backdrop-shown", { backdrop: this._backdrop });
          } catch {
          }
        }
      }
      for (const entry of this._stack) {
        const panel = entry.panel;
        if (panel && !panel.isConnected) {
          try {
            panel.style.viewTransitionName = "none";
          } catch {
          }
          try {
            doc.body.appendChild(panel);
          } catch {
          }
        }
      }
    }
    /**
     * Dim+blur unless every open panel opted out (`blur: false`).
     * Click-outside still works with a transparent catcher.
     */
    _applyBackdropVisual() {
      const el2 = this._backdrop;
      if (!el2) return;
      const wantBlur = this._stack.some((entry) => entry.blur !== false);
      if (wantBlur) {
        el2.style.background = "rgba(0,0,0,0.35)";
        el2.style.backdropFilter = "blur(6px)";
        el2.style.webkitBackdropFilter = "blur(6px)";
      } else {
        el2.style.background = "transparent";
        el2.style.backdropFilter = "none";
        el2.style.webkitBackdropFilter = "none";
      }
    }
    _recomputeZ() {
      if (this._backdrop) {
        this._backdrop.style.zIndex = String(Z_INDEX.POPUP_BACKDROP ?? Z_INDEX.VIEWPORT_MODAL_FRAME);
        this._backdrop.style.viewTransitionName = "none";
        if (this._onPanelChange && this._stack.length > 0) {
          try {
            this._onPanelChange("backdrop-updated", { backdrop: this._backdrop });
          } catch {
          }
        }
      }
      const base = Z_INDEX.POPUP_PANEL_BASE ?? Z_INDEX.VIEWPORT_MODAL_FRAME + 2;
      const max = Z_INDEX.POPUP_PANEL_MAX ?? Z_INDEX.OVERLAYS_BELOW_2 - 1;
      for (let i = 0; i < this._stack.length; i++) {
        const entry = this._stack[i];
        const panel = entry.panel;
        if (!panel) continue;
        const z = Math.min(base + i, max);
        panel.style.zIndex = String(z);
        panel.style.viewTransitionName = "none";
        if (this._onPanelChange) {
          try {
            this._onPanelChange("panel-updated", { id: entry.id, panel });
          } catch {
          }
        }
      }
    }
    _backdropClickHandler(e) {
      try {
        const path = typeof e.composedPath === "function" ? e.composedPath() : [];
        for (const n of path) {
          if (!n || n.nodeType !== 1) continue;
          const cls = n.classList;
          if (cls?.contains("kp-floating-keyboard-help") || cls?.contains("kp-control-strip") || cls?.contains("kp-layout-config-panel")) {
            return;
          }
        }
        e.preventDefault();
        e.stopPropagation();
        const top = this.top();
        if (top?.onRequestClose) top.onRequestClose();
        else this.hideModal();
      } catch {
      }
    }
  };

  // src/modules/popover-controller.js
  init_constants();

  // src/messaging/types.js
  var MSG = Object.freeze({
    // --- Extension enable / status ---
    GET_STATE: "KP_GET_STATE",
    SET_STATE: "KP_SET_STATE",
    TOGGLE_STATE: "KP_TOGGLE_STATE",
    STATE_RESPONSE: "KP_STATE_RESPONSE",
    STATE_CHANGED: "KP_STATE_CHANGED",
    UPDATE_STATE: "KP_UPDATE_STATE",
    GET_STATUS: "KP_GET_STATUS",
    STATUS: "KP_STATUS",
    // --- Transient onboarding actions ---
    TRANSIENT_ACTION: "KP_TRANSIENT_ACTION",
    /** Service worker → active tab action selected from the Keyboard Reference context menu. */
    KEYBOARD_REFERENCE_CONTEXT_ACTION: "KP_KEYBOARD_REFERENCE_CONTEXT_ACTION",
    // --- Tab / history navigation ---
    TAB_LEFT: "KP_TAB_LEFT",
    TAB_RIGHT: "KP_TAB_RIGHT",
    NEW_TAB: "KP_NEW_TAB",
    CLOSE_TAB: "KP_CLOSE_TAB",
    GO_BACK: "KP_GO_BACK",
    GO_FORWARD: "KP_GO_FORWARD",
    OPEN_URL_BACKGROUND: "KP_OPEN_URL_BACKGROUND",
    OPEN_URL_FOREGROUND: "KP_OPEN_URL_FOREGROUND",
    /** Same-tab navigate (chrome.tabs.update). Used when sandboxed iframes cannot top-navigate without a real user gesture. */
    NAVIGATE_SAME_TAB: "KP_NAVIGATE_SAME_TAB",
    // --- UI open (content-script handlers; SW may forward) ---
    OPEN_SETTINGS_POPOVER: "KP_OPEN_SETTINGS_POPOVER",
    OPEN_GUIDE_POPOVER: "KP_OPEN_GUIDE_POPOVER",
    /** Open Docs popover; optional topicId / hash deep-link. */
    OPEN_DOCS_POPOVER: "KP_OPEN_DOCS_POPOVER",
    OPEN_ONBOARDING: "KP_OPEN_ONBOARDING",
    /** Reset walkthrough progress and open it (e.g. Guide "Launch Walkthrough"). */
    LAUNCH_WALKTHROUGH: "KP_LAUNCH_WALKTHROUGH",
    // --- History / bookmarks / top sites (SW APIs for content scripts) ---
    OMNIBOX_SUGGEST: "KP_OMNIBOX_SUGGEST",
    /** Response to OMNIBOX_SUGGEST */
    OMNIBOX_SUGGESTIONS: "KP_OMNIBOX_SUGGESTIONS",
    GET_BOOKMARKS: "KP_GET_BOOKMARKS",
    BOOKMARKS_RESPONSE: "KP_BOOKMARKS_RESPONSE",
    GET_RECENT_BOOKMARKS: "KP_GET_RECENT_BOOKMARKS",
    RECENT_BOOKMARKS_RESPONSE: "KP_RECENT_BOOKMARKS_RESPONSE",
    BROWSER_HISTORY_GET: "KP_BROWSER_HISTORY_GET",
    BROWSER_HISTORY_RESULT: "KP_BROWSER_HISTORY_RESULT",
    GET_TOP_SITES: "KP_GET_TOP_SITES",
    TOP_SITES_RESPONSE: "KP_TOP_SITES_RESPONSE",
    GET_MOST_VISITED: "KP_GET_MOST_VISITED",
    MOST_VISITED_RESPONSE: "KP_MOST_VISITED_RESPONSE",
    GET_HISTORY_FOR_DOMAINS: "KP_GET_HISTORY_FOR_DOMAINS",
    HISTORY_FOR_DOMAINS_RESPONSE: "KP_HISTORY_FOR_DOMAINS_RESPONSE",
    GET_RECENT_HISTORY: "KP_GET_RECENT_HISTORY",
    RECENT_HISTORY_RESPONSE: "KP_RECENT_HISTORY_RESPONSE",
    // --- Video thumbnails for card backgrounds (official oEmbed / sync URLs) ---
    GET_VIDEO_THUMB: "KP_GET_VIDEO_THUMB",
    VIDEO_THUMB_RESPONSE: "KP_VIDEO_THUMB_RESPONSE",
    // --- Media Library (IndexedDB at extension origin; SW owns Blobs) ---
    MEDIA_LIBRARY_ADD: "KP_MEDIA_LIBRARY_ADD",
    MEDIA_LIBRARY_LIST: "KP_MEDIA_LIBRARY_LIST",
    MEDIA_LIBRARY_GET: "KP_MEDIA_LIBRARY_GET",
    MEDIA_LIBRARY_DELETE: "KP_MEDIA_LIBRARY_DELETE",
    MEDIA_LIBRARY_ZIP: "KP_MEDIA_LIBRARY_ZIP",
    /** SW → tabs: library contents changed (add/delete). Overlay reloads if open. */
    MEDIA_LIBRARY_CHANGED: "KP_MEDIA_LIBRARY_CHANGED",
    // --- Dictionary lookup (Free Dictionary API via SW; LOOKUP_WORD) ---
    DICTIONARY_LOOKUP: "KP_DICTIONARY_LOOKUP",
    // --- Per-tab navigation graph ---
    NAVGRAPH_GET: "KP_NAVGRAPH_GET",
    /** Response payload for NAVGRAPH_GET */
    NAVGRAPH_GRAPH: "KP_NAVGRAPH_GRAPH",
    NAVGRAPH_JUMP: "KP_NAVGRAPH_JUMP",
    NAVGRAPH_CLEAR: "KP_NAVGRAPH_CLEAR",
    // --- Generic ---
    SUCCESS: "KP_SUCCESS",
    ERROR: "KP_ERROR",
    /** Lightweight ack (e.g. STATUS notification received) */
    ACK: "KP_ACK",
    // --- Separate-window Link Preview / Open Popover (chrome.windows popup) ---
    OPEN_POPOVER_WINDOW: "KP_OPEN_POPOVER_WINDOW",
    CLOSE_POPOVER_WINDOW: "KP_CLOSE_POPOVER_WINDOW",
    /** SW → opener: popover window closed (OS ✕ or in-window close). */
    POPOVER_WINDOW_CLOSED: "KP_POPOVER_WINDOW_CLOSED",
    /** Popup tab → SW: am I a KeyPilot popover window? */
    AM_I_POPOVER_WINDOW: "KP_AM_I_POPOVER_WINDOW",
    // --- Parent ↔ popover iframe (window.postMessage) ---
    POPOVER_BRIDGE_INIT: "KP_POPOVER_BRIDGE_INIT",
    POPOVER_BRIDGE_READY: "KP_POPOVER_BRIDGE_READY",
    POPOVER_REQUEST_CLOSE: "KP_POPOVER_REQUEST_CLOSE",
    POPOVER_BRIDGE_KEYDOWN: "KP_POPOVER_BRIDGE_KEYDOWN",
    POPOVER_SCROLL: "KP_POPOVER_SCROLL",
    /** Guide iframe → parent: close guide and open walkthrough from a reset state. */
    POPOVER_LAUNCH_WALKTHROUGH: "KP_POPOVER_LAUNCH_WALKTHROUGH",
    // --- Parent → child frame activate (window.postMessage; third-party iframes) ---
    // Top-frame KeyPilot posts this when F/B/G lands on a cross-origin <iframe>.
    // Child frame-click-agent performs elementFromPoint + click in its own document.
    // Optional topOrigin: parent tab origin for link routing (no hardcoded domains).
    FRAME_ACTIVATE: "KP_FRAME_ACTIVATE",
    // --- Parent → child frame scroll (window.postMessage; layout scroll keys under an iframe) ---
    // Top-frame KeyPilot posts this when scroll keys land on an <iframe> shell. Child
    // frame-click-agent runs scroll-at-point (delta or edge) at local coordinates
    // (nested overflow first, then the frame document).
    FRAME_SCROLL: "KP_FRAME_SCROLL",
    // --- Child → parent pointer sync (window.postMessage) ---
    // Frame agent reports local client coords so top KeyPilot can keep lastMouse fresh
    // while the pointer is over a cross-origin (or any) iframe — parent documents do
    // not receive mousemove inside iframes. Nested agents re-bubble with translated coords.
    // Payload: { type, inside: boolean, clientX?: number, clientY?: number }
    FRAME_POINTER: "KP_FRAME_POINTER",
    // --- Child → parent: return keyboard focus to the top frame ---
    // Sent on Esc / pointer leave when the iframe had document focus (manual click).
    // Top blurs the focused <iframe> so KeyPilot keybinds work on the parent again.
    FRAME_FOCUS_RECLAIM: "KP_FRAME_FOCUS_RECLAIM",
    // --- Child → parent: typing focus inside a page iframe ---
    // Frame agent posts these on focusin/focusout of a text field in its document
    // (Gutenberg editor-canvas, etc.). Top FocusDetector peeks the same-origin
    // activeElement and enters/exits text_focus. No element is sent.
    // Payload: { type }
    FRAME_TYPING_FOCUS: "KP_FRAME_TYPING_FOCUS",
    FRAME_TYPING_BLUR: "KP_FRAME_TYPING_BLUR",
    // --- Parent → child: blur the typing field (Esc from top-frame text mode) ---
    FRAME_BLUR_TYPING: "KP_FRAME_BLUR_TYPING",
    // --- Child frame-agent → SW: inject full content-bundled.js into this frame ---
    // Used when a KeyPilot popover iframe needs full KeyPilot (cursor/overlays).
    // Thin frame-agent-bundled.js does not include the full app.
    INJECT_FULL_KEYPILOT_IN_FRAME: "KP_INJECT_FULL_KEYPILOT_IN_FRAME",
    // --- Content → SW: inject MAIN-world map.panBy bridge into the sender frame ---
    // Scroll Line uses this so isolated content can pan Leaflet/Mapbox/Google via
    // page globals. Idempotent; bridge listens for CustomEvent __kp_map_pan_v1.
    ENSURE_MAP_PAN_BRIDGE: "KP_ENSURE_MAP_PAN_BRIDGE"
  });
  var TAB_UI_FORWARD_TYPES = Object.freeze([
    MSG.OPEN_SETTINGS_POPOVER,
    MSG.OPEN_GUIDE_POPOVER,
    MSG.OPEN_DOCS_POPOVER,
    MSG.OPEN_ONBOARDING,
    MSG.LAUNCH_WALKTHROUGH
  ]);

  // src/messaging/content-runtime-router.js
  var handlersByType = /* @__PURE__ */ new Map();
  var installed = false;
  function registerContentRuntimeHandler(type2, handler) {
    if (typeof type2 !== "string" || !type2 || typeof handler !== "function") {
      return () => {
      };
    }
    const list = handlersByType.get(type2) || [];
    list.push(handler);
    handlersByType.set(type2, list);
    return () => {
      const cur = handlersByType.get(type2);
      if (!cur) return;
      const next = cur.filter((h) => h !== handler);
      if (next.length) handlersByType.set(type2, next);
      else handlersByType.delete(type2);
    };
  }
  function installContentRuntimeRouter() {
    if (installed) return;
    if (typeof chrome === "undefined" || !chrome?.runtime?.onMessage?.addListener) return;
    installed = true;
    chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
      const type2 = message && typeof message.type === "string" ? message.type : "";
      const list = type2 ? handlersByType.get(type2) : null;
      if (!list || !list.length) return;
      let asyncPending = false;
      for (const handler of list) {
        try {
          const result = handler(message, sender, sendResponse);
          if (result === true) asyncPending = true;
        } catch (e) {
          console.warn("[KeyPilot] content runtime handler failed:", type2, e);
        }
      }
      return asyncPending;
    });
  }

  // src/ui/popover-titlebar.js
  init_constants();

  // src/ui/nct-dark-ui.js
  var NCT_DARK_UI_FONT = "var(--kp-font-ui, Helvetica, Arial, sans-serif)";
  var NCT_DARK_UI_COLORS = {
    bg: "#0f0f10",
    panel: "#232323",
    panelEdge: "#3a3a3a",
    panelEdgeDark: "#111",
    titleTop: "#4c4c4c",
    titleMid: "#353535",
    titleBot: "#252525",
    btnTop: "#4a4a4a",
    btnMid: "#343434",
    btnBot: "#2a2a2a",
    litTop: "#5a7a9a",
    litBot: "#3a5570",
    litEdge: "#2a4a66",
    accent: "#4a90c8",
    fg: "#ddd",
    fgDim: "#aaa",
    fgMute: "#777",
    fieldBg: "#141414",
    fieldEdge: "#0a0a0a",
    fieldInsetTop: "#333"
  };
  var NCT_DARK_UI_PANEL_BACKGROUND = "var(--kp-panel-bg, var(--kp-color-panel, #232323))";
  var NCT_DARK_UI_PANEL_BORDER = "var(--kp-panel-border, 1px solid #111)";
  var NCT_DARK_UI_PANEL_BOX_SHADOW = "var(--kp-panel-shadow)";
  var NCT_DARK_UI_PANEL_RADIUS = "var(--kp-radius-panel, 3px)";
  var NCT_DARK_UI_BACKDROP_CLASS = "kp-nct-backdrop";
  var NCT_DARK_UI_BACKDROP_BACKGROUND = "var(--kp-backdrop-bg, rgba(0,0,0,0.35))";
  var NCT_DARK_UI_BACKDROP_BLUR = "var(--kp-backdrop-blur, blur(6px))";
  var NCT_DARK_UI_SCROLLBAR_CLASS = "kp-nct-scroll";
  function getNctDarkUiBackdropCss(opts = {}) {
    const selector = typeof opts.selector === "string" && opts.selector.trim() ? opts.selector.trim() : `.${NCT_DARK_UI_BACKDROP_CLASS}`;
    return `
${selector} {
  position: absolute;
  inset: 0;
  background: ${NCT_DARK_UI_BACKDROP_BACKGROUND};
  backdrop-filter: ${NCT_DARK_UI_BACKDROP_BLUR};
  -webkit-backdrop-filter: ${NCT_DARK_UI_BACKDROP_BLUR};
  pointer-events: auto;
}
`.trim();
  }
  function getNctDarkUiScrollbarCss(opts = {}) {
    const cls = typeof opts.className === "string" && opts.className.trim() ? opts.className.trim().replace(/^\./, "") : NCT_DARK_UI_SCROLLBAR_CLASS;
    const scope = typeof opts.scopeSelector === "string" && opts.scopeSelector.trim() ? `${opts.scopeSelector.trim()} ` : "";
    const root = `${scope}.${cls}`;
    const thumb = "var(--kp-scrollbar-thumb, #4a4a4a)";
    const thumbHover = "var(--kp-scrollbar-thumb-hover, #5c5c5c)";
    const thumbActive = "var(--kp-color-accent, #4a90c8)";
    const track = "var(--kp-scrollbar-track, #141414)";
    const edge = "var(--kp-color-panel-edge-dark, #111)";
    return `
${root} {
  scrollbar-width: thin;
  scrollbar-color: ${thumb} ${track};
}
/* Blink: scrollbar-width uses overlay bars that only appear on scroll and
   suppress ::-webkit-scrollbar. Unset so the themed classic bar paints as
   soon as the region overflows (Dark Pro tokens by default). */
@supports selector(::-webkit-scrollbar) {
  ${root} {
    scrollbar-width: unset;
    scrollbar-color: unset;
  }
}
${root}::-webkit-scrollbar,
${root} ::-webkit-scrollbar {
  -webkit-appearance: none;
  appearance: none;
  width: 10px;
  height: 10px;
  background: ${track};
}
${root}::-webkit-scrollbar-corner,
${root} ::-webkit-scrollbar-corner {
  background: ${track};
}
${root}::-webkit-scrollbar-track,
${root} ::-webkit-scrollbar-track {
  background: ${track};
  border-left: 1px solid ${edge};
  border-top: 1px solid ${edge};
}
${root}::-webkit-scrollbar-thumb,
${root} ::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, #555 0%, ${thumb} 45%, #3a3a3a 100%);
  border: 1px solid ${edge};
  border-radius: 2px;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.12);
  min-height: 28px;
  min-width: 28px;
}
${root}::-webkit-scrollbar-thumb:hover,
${root} ::-webkit-scrollbar-thumb:hover {
  background: linear-gradient(180deg, #666 0%, ${thumbHover} 45%, #444 100%);
}
${root}::-webkit-scrollbar-thumb:active,
${root} ::-webkit-scrollbar-thumb:active {
  background: linear-gradient(180deg, ${thumbActive} 0%, #3a6a94 100%);
  border-color: var(--kp-color-lit-edge, #2a4a66);
}
`.trim();
  }
  var NCT_DARK_UI_TITLEBAR_GRADIENT = "var(--kp-titlebar-bg)";
  var NCT_DARK_UI_TITLEBAR_BORDER_BOTTOM = "var(--kp-titlebar-border, 1px solid #111)";
  var NCT_DARK_UI_TITLEBAR_BOX_SHADOW = "var(--kp-titlebar-shadow)";
  var NCT_DARK_UI_TITLEBAR_TEXT_MODE_BACKGROUND = `linear-gradient(180deg, rgba(255, 140, 0, 0.28) 0%, rgba(255, 140, 0, 0.14) 45%, rgba(255, 120, 0, 0.18) 100%), linear-gradient(180deg, ${NCT_DARK_UI_COLORS.titleTop} 0%, ${NCT_DARK_UI_COLORS.titleMid} 45%, ${NCT_DARK_UI_COLORS.titleBot} 100%)`;
  var NCT_DARK_UI_TITLEBAR_TEXT_MODE_BORDER_BOTTOM = "1px solid rgba(120, 70, 20, 0.85)";
  var NCT_DARK_UI_TITLEBAR_TEXT_MODE_TITLE_COLOR = "rgba(255, 210, 150, 0.98)";
  var NCT_DARK_UI_TITLEBAR_TEXT_MODE_HINT_COLOR = "rgba(253, 186, 116, 0.95)";
  var NCT_DARK_UI_BTN_GRADIENT = "var(--kp-btn-bg)";
  var NCT_DARK_UI_BTN_BORDER = "var(--kp-btn-border, 1px solid #111)";
  var NCT_DARK_UI_BTN_RADIUS = "var(--kp-radius-btn, 2px)";
  var NCT_DARK_UI_ICON_BUTTON_OUTLINE = "inset 0 0 0 1px var(--kp-color-panel-edge, #3a3a3a)";
  var NCT_DARK_UI_BTN_LIT_GRADIENT = "var(--kp-btn-lit-bg)";
  var NCT_DARK_UI_BTN_LIT_BORDER = "var(--kp-btn-lit-border, 1px solid #2a4a66)";
  var NCT_DARK_UI_FIELD_BACKGROUND = "var(--kp-field-bg, #141414)";
  var NCT_DARK_UI_FIELD_BORDER = "var(--kp-field-border, 1px solid #0a0a0a)";
  var NCT_DARK_UI_FIELD_BOX_SHADOW = "var(--kp-field-shadow)";
  var NCT_DARK_UI_FIELD_FOCUS_BORDER = "var(--kp-color-accent, #4a90c8)";
  var NCT_DARK_UI_FIELD_FOCUS_BOX_SHADOW = "inset 0 0 0 1px color-mix(in srgb, var(--kp-color-accent, #4a90c8) 35%, transparent)";
  var NCT_DARK_UI_FOCUS_RING = "var(--kp-color-focus-ring)";
  var NCT_DARK_UI_SELECTED_TINT = "var(--kp-color-selected, rgba(74,144,200,0.22))";
  var NCT_DARK_UI_SELECTED_TEXT = "var(--kp-color-selected-text, #e8f0f8)";
  var NCT_DARK_UI_HOVER_TINT = "var(--kp-color-hover, rgba(255,255,255,0.06))";
  var NCT_DARK_UI_SCALE_CLASS = "kp-nct-scale";
  var NCT_DARK_UI_SCALE_STYLE_ATTR = "data-kp-nct-scale-styles";
  function getNctDarkUiScaleSliderCss(opts = {}) {
    const cls = typeof opts.className === "string" && opts.className.trim() ? opts.className.trim().replace(/^\./, "") : NCT_DARK_UI_SCALE_CLASS;
    const rangeWidth = typeof opts.rangeWidth === "string" && opts.rangeWidth.trim() ? opts.rangeWidth.trim() : "82px";
    return `
.${cls} {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  padding: 3px 8px;
  border: 1px solid var(--kp-color-panel-edge-dark, #111);
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  background: var(--kp-color-field-bg, #141414);
  box-shadow: 0 0 0 1px var(--kp-color-panel-edge, #3a3a3a) inset;
  color: var(--kp-color-fg, #ddd);
  font-family: ${NCT_DARK_UI_FONT};
  white-space: nowrap;
  box-sizing: border-box;
}
.${cls}-label {
  font-size: var(--kp-type-kbd-size, 10px);
  font-weight: var(--kp-type-weight-bold, 700);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--kp-color-fg-mute, #777);
  user-select: none;
}
.${cls}-edge {
  font-size: var(--kp-type-kbd-size, 10px);
  color: var(--kp-color-fg-dim, #aaa);
  font-variant-numeric: tabular-nums;
  user-select: none;
}
.${cls}-value {
  min-width: 2.6em;
  font-size: 11px;
  font-weight: 600;
  color: var(--kp-color-fg, #ddd);
  font-variant-numeric: tabular-nums;
  text-align: right;
  user-select: none;
}
.${cls}-range {
  -webkit-appearance: none;
  appearance: none;
  width: ${rangeWidth};
  height: 4px;
  border-radius: var(--kp-radius-xs, 2px);
  background: linear-gradient(90deg, var(--kp-color-panel-edge-dark, #111) 0%, var(--kp-color-accent, #4a90c8) 100%);
  outline: none;
  cursor: pointer;
  margin: 0;
  vertical-align: middle;
}
.${cls}-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 12px;
  height: 12px;
  border-radius: var(--kp-radius-xs, 2px);
  background: ${NCT_DARK_UI_BTN_LIT_GRADIENT};
  border: ${NCT_DARK_UI_BTN_LIT_BORDER};
  box-shadow: 0 1px 3px rgba(0,0,0,0.55);
  cursor: pointer;
}
.${cls}-range::-moz-range-thumb {
  width: 12px;
  height: 12px;
  border-radius: var(--kp-radius-xs, 2px);
  background: var(--kp-color-lit-bot, #3a5570);
  border: ${NCT_DARK_UI_BTN_LIT_BORDER};
  box-shadow: 0 1px 3px rgba(0,0,0,0.55);
  cursor: pointer;
}
.${cls}-range::-moz-range-track {
  height: 4px;
  border-radius: var(--kp-radius-xs, 2px);
  background: linear-gradient(90deg, var(--kp-color-panel-edge-dark, #111) 0%, var(--kp-color-accent, #4a90c8) 100%);
}
.${cls}-range:focus-visible {
  outline: none;
  box-shadow: ${NCT_DARK_UI_FOCUS_RING};
}
`.trim();
  }
  function createNctDarkUiScaleSlider(opts = {}) {
    const doc = opts.doc || document;
    const cls = typeof opts.className === "string" && opts.className.trim() ? opts.className.trim().replace(/^\./, "") : NCT_DARK_UI_SCALE_CLASS;
    const min = Number.isFinite(Number(opts.min)) ? Number(opts.min) : 0.8;
    const max = Number.isFinite(Number(opts.max)) ? Number(opts.max) : 1.75;
    const step = Number.isFinite(Number(opts.step)) ? Number(opts.step) : 0.05;
    const initial = Number.isFinite(Number(opts.value)) ? Number(opts.value) : min;
    const formatValue = typeof opts.formatValue === "function" ? opts.formatValue : (value) => {
      const n = Number(value);
      if (!Number.isFinite(n)) return "";
      return `${n.toFixed(2).replace(/\.?0+$/, "")}\xD7`;
    };
    const root = doc.createElement("label");
    root.className = cls;
    if (opts.title) root.title = String(opts.title);
    if (opts.embedStyles !== false) {
      const style = doc.createElement("style");
      style.setAttribute(NCT_DARK_UI_SCALE_STYLE_ATTR, "true");
      style.textContent = getNctDarkUiScaleSliderCss({
        className: cls,
        rangeWidth: opts.rangeWidth
      });
      root.appendChild(style);
    }
    if (opts.label) {
      const label = doc.createElement("span");
      label.className = `${cls}-label`;
      label.textContent = String(opts.label);
      root.appendChild(label);
    }
    if (opts.minLabel != null && opts.minLabel !== "") {
      const minTag = doc.createElement("span");
      minTag.className = `${cls}-edge`;
      minTag.textContent = String(opts.minLabel);
      root.appendChild(minTag);
    }
    const range = doc.createElement("input");
    range.type = "range";
    range.className = `${cls}-range`;
    range.min = String(min);
    range.max = String(max);
    range.step = String(step);
    range.value = String(initial);
    range.setAttribute(
      "aria-label",
      String(opts.ariaLabel || opts.title || opts.label || "Scale")
    );
    root.appendChild(range);
    if (opts.maxLabel != null && opts.maxLabel !== "") {
      const maxTag = doc.createElement("span");
      maxTag.className = `${cls}-edge`;
      maxTag.textContent = String(opts.maxLabel);
      root.appendChild(maxTag);
    }
    const valueEl = doc.createElement("output");
    valueEl.className = `${cls}-value`;
    valueEl.textContent = formatValue(initial);
    root.appendChild(valueEl);
    const readValue = () => {
      const n = Number(range.value);
      return Number.isFinite(n) ? n : min;
    };
    const setValue = (value, setOpts = {}) => {
      const n = Number(value);
      if (!Number.isFinite(n)) return;
      const clamped = Math.min(max, Math.max(min, n));
      range.value = String(clamped);
      valueEl.textContent = formatValue(clamped);
      valueEl.value = formatValue(clamped);
      if (!setOpts.silent && typeof opts.onInput === "function") {
        try {
          opts.onInput(clamped, null);
        } catch {
        }
      }
    };
    const onSlide = (event) => {
      const next = readValue();
      valueEl.textContent = formatValue(next);
      valueEl.value = formatValue(next);
      if (typeof opts.onInput === "function") {
        try {
          opts.onInput(next, event);
        } catch {
        }
      }
    };
    range.addEventListener("input", onSlide);
    range.addEventListener("change", onSlide);
    return {
      root,
      range,
      valueEl,
      getValue: readValue,
      setValue
    };
  }
  function applyFlashNotificationStyle(el2, opts = {}) {
    if (!el2) return;
    const color4 = String(opts.backgroundColor || "#4CAF50");
    const hasThumbnail = !!opts.hasThumbnail;
    const zIndex = opts.zIndex != null ? String(opts.zIndex) : "";
    try {
      Object.assign(el2.style, {
        position: "fixed",
        top: "16px",
        left: "50%",
        transform: "translateX(-50%)",
        // Specular bevel over the solid accent (color stays dominant).
        background: `linear-gradient(180deg, rgba(255,255,255,0.26) 0%, rgba(255,255,255,0.07) 34%, rgba(0,0,0,0.14) 100%), ${color4}`,
        backgroundColor: color4,
        color: "#fff",
        padding: hasThumbnail ? "8px 12px 8px 14px" : "8px 16px",
        borderRadius: "var(--kp-radius-btn, 2px)",
        border: "1px solid rgba(0,0,0,0.55)",
        fontSize: "var(--kp-type-ui-size, 12px)",
        fontWeight: "700",
        letterSpacing: "0.02em",
        fontFamily: NCT_DARK_UI_FONT,
        lineHeight: "1.35",
        textShadow: "0 1px 0 rgba(0,0,0,0.35)",
        zIndex,
        boxShadow: "0 0 0 1px rgba(255,255,255,0.22) inset, 0 1px 0 rgba(255,255,255,0.16) inset, 0 10px 28px rgba(0,0,0,0.50)",
        opacity: "0",
        transition: "opacity 0.18s ease-out",
        pointerEvents: "none",
        maxWidth: hasThumbnail ? "560px" : "420px",
        wordWrap: "break-word",
        textAlign: hasThumbnail ? "left" : "center",
        display: "flex",
        alignItems: "center",
        justifyContent: hasThumbnail ? "flex-start" : "center",
        gap: hasThumbnail ? "12px" : "0",
        boxSizing: "border-box"
      });
    } catch {
    }
  }
  function applyFlashNotificationThumbnailStyle(el2) {
    if (!el2) return;
    try {
      Object.assign(el2.style, {
        flex: "0 0 auto",
        maxWidth: "150px",
        maxHeight: "150px",
        borderRadius: "var(--kp-radius-btn, 2px)",
        overflow: "hidden",
        backgroundColor: "#141414",
        border: "1px solid rgba(0,0,0,0.55)",
        boxShadow: "0 0 0 1px rgba(255,255,255,0.18) inset, 0 6px 16px rgba(0,0,0,0.45)",
        lineHeight: "0"
      });
    } catch {
    }
  }

  // src/ui/preview-open-actions.js
  init_constants();
  function createOutlineIcon(doc, paths) {
    const svg = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
    svg.setAttribute("viewBox", "0 0 24 24");
    svg.setAttribute("width", "14");
    svg.setAttribute("height", "14");
    svg.setAttribute("aria-hidden", "true");
    svg.style.cssText = "flex-shrink: 0; display: block;";
    const common = {
      fill: "none",
      stroke: "currentColor",
      "stroke-width": "1.75",
      "stroke-linecap": "round",
      "stroke-linejoin": "round"
    };
    for (const p of paths || []) {
      const el2 = doc.createElementNS("http://www.w3.org/2000/svg", p.tag || "path");
      for (const [k, v] of Object.entries({ ...common, ...p.attrs || {} })) {
        el2.setAttribute(k, v);
      }
      svg.appendChild(el2);
    }
    return svg;
  }
  var TITLEBAR_BTN_STYLE = `
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  margin: 0;
  appearance: none;
  -webkit-appearance: none;
  box-sizing: border-box;
  background: ${NCT_DARK_UI_BTN_GRADIENT};
  border: ${NCT_DARK_UI_BTN_BORDER};
  color: ${NCT_DARK_UI_COLORS.fg};
  font-size: 11px;
  font-weight: 500;
  font-family: ${KP_UI_FONT};
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  text-shadow: none;
  box-shadow: none;
  padding: 4px 8px;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  cursor: pointer;
  flex-shrink: 0;
  white-space: nowrap;
  min-width: 0;
  min-height: 0;
  height: auto;
  width: auto;
  position: relative;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
`;
  function wireTitlebarBtnHover(btn) {
    btn.addEventListener("mouseenter", () => {
      btn.style.background = NCT_DARK_UI_HOVER_TINT;
      btn.style.borderColor = NCT_DARK_UI_COLORS.panelEdge;
      btn.style.color = "#fff";
    });
    btn.addEventListener("mouseleave", () => {
      btn.style.background = NCT_DARK_UI_BTN_GRADIENT;
      btn.style.borderColor = NCT_DARK_UI_COLORS.panelEdgeDark;
      btn.style.color = NCT_DARK_UI_COLORS.fg;
    });
  }
  function createTitlebarActionButton({
    doc = document,
    label,
    title,
    className,
    iconPaths,
    onClick
  } = {}) {
    const btn = doc.createElement("button");
    btn.type = "button";
    btn.className = className || "kpv2-popover-titlebar-action";
    btn.style.cssText = TITLEBAR_BTN_STYLE;
    const text = String(label || "").trim();
    if (Array.isArray(iconPaths) && iconPaths.length) {
      btn.appendChild(createOutlineIcon(doc, iconPaths));
    }
    if (text) {
      const span = doc.createElement("span");
      span.textContent = text;
      btn.appendChild(span);
    }
    const tip = title || text;
    if (tip) {
      btn.title = tip;
      btn.setAttribute("aria-label", tip);
    }
    if (typeof onClick === "function") {
      btn.addEventListener("click", (e) => {
        try {
          e.preventDefault();
          e.stopPropagation();
        } catch {
        }
        try {
          onClick(e);
        } catch {
        }
      });
    }
    wireTitlebarBtnHover(btn);
    return btn;
  }
  function createPreviewOpenActionButtons({
    doc = document,
    getUrl,
    afterOpen,
    afterOpenNewTab
  } = {}) {
    const resolveUrl3 = typeof getUrl === "function" ? getUrl : () => null;
    const actions = doc.createElement("div");
    actions.className = "kp-preview-open-actions";
    actions.style.cssText = `
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
  `;
    const openButton = doc.createElement("button");
    openButton.type = "button";
    openButton.className = "kpv2-preview-open-btn";
    openButton.style.cssText = TITLEBAR_BTN_STYLE;
    openButton.appendChild(createOutlineIcon(doc, [
      { attrs: { d: "M14 3h7v7" } },
      { attrs: { d: "M10 14L21 3" } },
      { attrs: { d: "M21 14v5a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5" } }
    ]));
    const openLabel = doc.createElement("span");
    openLabel.textContent = "Open";
    openButton.appendChild(openLabel);
    openButton.title = "Open in this tab";
    openButton.setAttribute("aria-label", "Open in this tab");
    openButton.onclick = (e) => {
      try {
        e.preventDefault();
        e.stopPropagation();
      } catch {
      }
      const url = resolveUrl3();
      if (!url) return;
      try {
        window.location.assign(url);
      } catch {
        try {
          window.location.href = url;
        } catch {
        }
      }
      try {
        afterOpen?.();
      } catch {
      }
    };
    wireTitlebarBtnHover(openButton);
    const openNewTabButton = doc.createElement("button");
    openNewTabButton.type = "button";
    openNewTabButton.className = "kpv2-preview-open-new-tab-btn";
    openNewTabButton.style.cssText = TITLEBAR_BTN_STYLE;
    openNewTabButton.appendChild(createOutlineIcon(doc, [
      { tag: "rect", attrs: { x: "8", y: "8", width: "13", height: "13", rx: "2" } },
      { tag: "path", attrs: { d: "M16 8V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h3" } }
    ]));
    const openNewTabLabel = doc.createElement("span");
    openNewTabLabel.textContent = "Open in New Tab";
    openNewTabButton.appendChild(openNewTabLabel);
    openNewTabButton.title = "Open in new tab";
    openNewTabButton.setAttribute("aria-label", "Open in new tab");
    openNewTabButton.onclick = (e) => {
      try {
        e.preventDefault();
        e.stopPropagation();
      } catch {
      }
      const url = resolveUrl3();
      if (!url) return;
      try {
        window.open(url, "_blank", "noopener,noreferrer");
      } catch {
        try {
          window.open(url, "_blank");
        } catch {
        }
      }
      try {
        afterOpenNewTab?.();
      } catch {
      }
    };
    wireTitlebarBtnHover(openNewTabButton);
    actions.appendChild(openButton);
    actions.appendChild(openNewTabButton);
    return { openButton, openNewTabButton, actions };
  }

  // themes/icons.js
  var THEME_ICON_FILES = Object.freeze({
    close: "chrome/close.svg",
    collapse: "chrome/collapse.svg",
    gear: "chrome/gear.svg",
    keyboard: "chrome/keyboard.svg",
    window: "chrome/window.svg"
  });
  var THEME_ICON_IDS = Object.freeze(Object.keys(THEME_ICON_FILES));
  function getThemeIconUrl(semanticId, theme) {
    const id = typeof semanticId === "string" ? semanticId : "";
    const baseFile = THEME_ICON_FILES[id];
    if (!baseFile || !id) return "";
    const pack = theme?.icons?.pack || "shared";
    const fallback = theme?.icons?.fallbackPack || "shared";
    const override = theme?.icons?.overrides && theme.icons.overrides[id];
    const file = typeof override === "string" && override.trim() ? override.trim() : baseFile;
    const folder = override ? pack : fallback;
    try {
      if (typeof chrome !== "undefined" && chrome.runtime?.getURL) {
        return chrome.runtime.getURL(`themes/${folder}/icons/${file}`);
      }
    } catch {
    }
    return `themes/${folder}/icons/${file}`;
  }

  // themes/index.js
  init_schema();

  // themes/dark-pro/theme.js
  init_schema();

  // themes/chrome-recipes.js
  var METAL_SPECULAR = "linear-gradient(180deg, rgba(255,255,255,0.38) 0%, rgba(255,255,255,0.08) 28%, transparent 55%)";
  function createDarkProColor() {
    return {
      bg: "#0f0f10",
      panel: "#232323",
      panelEdge: "#3a3a3a",
      panelEdgeDark: "#111",
      titleTop: "#4c4c4c",
      titleMid: "#353535",
      titleBot: "#252525",
      btnTop: "#4a4a4a",
      btnMid: "#343434",
      btnBot: "#2a2a2a",
      litTop: "#5a7a9a",
      litBot: "#3a5570",
      litEdge: "#2a4a66",
      accent: "#4a90c8",
      accent2: "#4a90c8",
      fg: "#ddd",
      fgDim: "#aaa",
      fgMute: "#777",
      fieldBg: "#141414",
      fieldEdge: "#0a0a0a",
      fieldInsetTop: "#333",
      hover: "rgba(255,255,255,0.06)",
      selected: "rgba(74,144,200,0.22)",
      selectedText: "#e8f0f8",
      focusRing: "inset 0 0 0 1px rgba(74,144,200,0.55)",
      kbdColor: "#ddd",
      scrollbarThumb: "#4a4a4a",
      scrollbarThumbHover: "#5c5c5c",
      scrollbarTrack: "#141414"
    };
  }
  function createDarkProEffect(c) {
    return {
      titlebarBg: `linear-gradient(180deg, ${c.titleTop} 0%, ${c.titleMid} 45%, ${c.titleBot} 100%)`,
      titlebarBorder: `1px solid ${c.panelEdgeDark}`,
      titlebarShadow: `0 1px 0 ${c.panelEdge}`,
      panelBg: c.panel,
      panelBorder: `1px solid ${c.panelEdgeDark}`,
      panelShadow: `0 0 0 1px ${c.panelEdge} inset, 0 0 0 1px rgba(190, 190, 190, 0.52), 0 0 10px rgba(255, 255, 255, 0.14), 0 16px 40px rgba(0,0,0,0.55)`,
      btnBg: `linear-gradient(180deg, ${c.btnTop} 0%, ${c.btnMid} 50%, ${c.btnBot} 100%)`,
      btnBorder: `1px solid ${c.panelEdgeDark}`,
      btnLitBg: `linear-gradient(180deg, ${c.litTop} 0%, ${c.litBot} 100%)`,
      btnLitBorder: `1px solid ${c.litEdge}`,
      fieldBg: c.fieldBg,
      fieldBorder: `1px solid ${c.fieldEdge}`,
      fieldShadow: `inset 0 1px 0 ${c.fieldInsetTop}`,
      kbdBg: c.fieldBg,
      kbdBorder: `1px solid ${c.panelEdgeDark}`,
      kbdShadow: "none",
      backdropBg: "rgba(0,0,0,0.35)",
      backdropBlur: "blur(6px)",
      hatchEdit: "repeating-linear-gradient(-45deg, rgba(180, 200, 220, 0.08) 0px, rgba(180, 200, 220, 0.08) 1px, transparent 1px, transparent 7px)",
      hatchEditTitlebarBg: "linear-gradient(180deg, #646464 0%, #4a4a4a 45%, #383838 100%)",
      hatchEditBodyBg: "#1a1c20"
    };
  }
  function createMetalColor() {
    return {
      bg: "#6e6e6e",
      panel: "#838383",
      panelEdge: "rgba(190,190,190,0.48)",
      panelEdgeDark: "rgba(42,52,62,0.92)",
      titleTop: "#b0b0b0",
      titleMid: "#929292",
      titleBot: "#787878",
      btnTop: "#c2c2c2",
      btnMid: "#9e9e9e",
      btnBot: "#868686",
      litTop: "#7aa0c0",
      litBot: "#4a7090",
      litEdge: "#3a5a78",
      accent: "#3a6a94",
      accent2: "#3a6a94",
      fg: "#1c1c1c",
      fgDim: "rgba(28,28,28,0.72)",
      fgMute: "rgba(28,28,28,0.55)",
      fieldBg: "#9a9a9a",
      fieldEdge: "#4a4a4a",
      fieldInsetTop: "rgba(255,255,255,0.35)",
      hover: "rgba(255,255,255,0.22)",
      selected: "rgba(58,106,148,0.28)",
      selectedText: "#0e1a24",
      focusRing: "inset 0 0 0 1px rgba(58,106,148,0.55)",
      kbdColor: "#141414",
      scrollbarThumb: "#a8a8a8",
      scrollbarThumbHover: "#b5b5b5",
      scrollbarTrack: "#747474"
    };
  }
  function createMetalEffect(c) {
    return {
      titlebarBg: `${METAL_SPECULAR}, linear-gradient(180deg, ${c.titleTop} 0%, ${c.titleMid} 45%, ${c.titleBot} 100%)`,
      titlebarBorder: "1px solid #4a4a4a",
      titlebarShadow: "0 1px 0 rgba(255,255,255,0.35)",
      panelBg: `${METAL_SPECULAR}, linear-gradient(180deg, #9a9a9a 0%, #838383 48%, #707070 100%)`,
      panelBorder: "1px solid rgba(42,52,62,0.92)",
      panelShadow: "0 0 0 1px rgba(255,255,255,0.28) inset, 0 0 0 1px rgba(190,190,190,0.48), 0 0 10px rgba(255,255,255,0.12), 0 16px 40px rgba(0,0,0,0.45)",
      btnBg: `linear-gradient(180deg, ${c.btnTop} 0%, ${c.btnMid} 50%, ${c.btnBot} 100%)`,
      btnBorder: "1px solid #4a4a4a",
      btnLitBg: `linear-gradient(180deg, ${c.litTop} 0%, ${c.litBot} 100%)`,
      btnLitBorder: `1px solid ${c.litEdge}`,
      fieldBg: c.fieldBg,
      fieldBorder: "1px solid #4a4a4a",
      fieldShadow: "inset 0 1px 0 rgba(255,255,255,0.40)",
      kbdBg: "linear-gradient(180deg, #e4e4e4 0%, #c8c8c8 45%, #b0b0b0 55%, #9a9a9a 100%)",
      kbdBorder: "1px solid #3d3d3d",
      kbdShadow: "0 1px 0 rgba(255,255,255,0.72) inset, 0 -1px 0 rgba(0,0,0,0.28) inset, 0 1px 2px rgba(0,0,0,0.32)",
      backdropBg: "rgba(40,40,40,0.35)",
      backdropBlur: "blur(6px)",
      hatchEdit: "repeating-linear-gradient(-45deg, rgba(24, 24, 24, 0.28) 0px, rgba(24, 24, 24, 0.28) 1px, transparent 1px, transparent 7px)",
      hatchEditTitlebarBg: `${METAL_SPECULAR}, linear-gradient(180deg, #b8b8b8 0%, #9a9a9a 45%, #808080 100%)`,
      hatchEditBodyBg: "#8a8a8a"
    };
  }
  function createGxColor() {
    return {
      bg: "#0a0a0c",
      panel: "#16161a",
      panelEdge: "#2a2a32",
      panelEdgeDark: "#050506",
      titleTop: "#2c2c34",
      titleMid: "#1c1c22",
      titleBot: "#121216",
      btnTop: "#3a3a44",
      btnMid: "#26262e",
      btnBot: "#1a1a20",
      litTop: "#00e5ff",
      litBot: "#0088aa",
      litEdge: "#006688",
      accent: "#00e5ff",
      accent2: "#ff2d95",
      fg: "#e8e8ef",
      fgDim: "#9aa0b0",
      fgMute: "#6a7080",
      fieldBg: "#0c0c10",
      fieldEdge: "#000",
      fieldInsetTop: "#333344",
      hover: "rgba(0,229,255,0.08)",
      selected: "rgba(0,229,255,0.18)",
      selectedText: "#f0ffff",
      focusRing: "inset 0 0 0 1px rgba(0,229,255,0.55)",
      kbdColor: "#00e5ff",
      scrollbarThumb: "#3a3a44",
      scrollbarThumbHover: "#00e5ff",
      scrollbarTrack: "#0c0c10"
    };
  }
  function createGxEffect(c) {
    return {
      titlebarBg: `linear-gradient(180deg, ${c.titleTop} 0%, ${c.titleMid} 45%, ${c.titleBot} 100%)`,
      titlebarBorder: `1px solid ${c.panelEdgeDark}`,
      titlebarShadow: `0 1px 0 ${c.accent}33`,
      panelBg: `linear-gradient(180deg, #1c1c22 0%, ${c.panel} 48%, #101014 100%)`,
      panelBorder: `1px solid ${c.panelEdgeDark}`,
      panelShadow: `0 0 0 1px ${c.panelEdge} inset, 0 0 0 1px rgba(0, 229, 255, 0.22), 0 0 14px rgba(0, 229, 255, 0.12), 0 16px 40px rgba(0,0,0,0.65)`,
      btnBg: `linear-gradient(180deg, ${c.btnTop} 0%, ${c.btnMid} 50%, ${c.btnBot} 100%)`,
      btnBorder: `1px solid ${c.panelEdgeDark}`,
      btnLitBg: `linear-gradient(180deg, ${c.litTop} 0%, ${c.litBot} 100%)`,
      btnLitBorder: `1px solid ${c.litEdge}`,
      fieldBg: c.fieldBg,
      fieldBorder: `1px solid ${c.fieldEdge}`,
      fieldShadow: `inset 0 1px 0 ${c.fieldInsetTop}`,
      kbdBg: "rgba(0, 229, 255, 0.08)",
      kbdBorder: `1px solid ${c.accent}`,
      kbdShadow: `0 0 0 1px ${c.accent}55, 0 0 8px ${c.accent}44`,
      backdropBg: "rgba(0,0,0,0.5)",
      backdropBlur: "blur(8px)",
      hatchEdit: "repeating-linear-gradient(-45deg, rgba(0, 229, 255, 0.16) 0px, rgba(0, 229, 255, 0.16) 1px, transparent 1px, transparent 7px)",
      hatchEditTitlebarBg: `linear-gradient(180deg, ${c.titleTop} 0%, ${c.titleMid} 45%, ${c.titleBot} 100%)`,
      hatchEditBodyBg: "#101014"
    };
  }

  // themes/click-defaults.js
  var NO_CUSTOM = "NO-CUSTOM-CURSORS";
  var DARK_PRO_CLICK_DEFAULTS = Object.freeze({
    cursorMode: NO_CUSTOM,
    clickMode: Object.freeze({
      cursor: Object.freeze({
        type: "crosshair",
        lineWidth: 4,
        sizePixels: 10,
        gap: 6
      }),
      focusColor: "blue",
      overlayFillEnabled: false,
      overlayShadowEnabled: false,
      rectangleThickness: 3,
      clickEffect: "flash",
      keyboardLinkHoverHints: false,
      paintStrategy: "BC",
      focusPadding: 2,
      skipForParent: true
    })
  });
  var GRAY_METAL_CLICK_DEFAULTS = Object.freeze({
    cursorMode: NO_CUSTOM,
    clickMode: Object.freeze({
      cursor: Object.freeze({
        type: "crosshair",
        lineWidth: 5,
        sizePixels: 12,
        gap: 6
      }),
      focusColor: "blue",
      overlayFillEnabled: false,
      overlayShadowEnabled: false,
      rectangleThickness: 4,
      clickEffect: "flash",
      keyboardLinkHoverHints: false,
      paintStrategy: "BC",
      focusPadding: 2,
      skipForParent: true
    })
  });
  var GX_ER_CLICK_DEFAULTS = Object.freeze({
    cursorMode: NO_CUSTOM,
    clickMode: Object.freeze({
      cursor: Object.freeze({
        type: "crosshair",
        lineWidth: 3,
        sizePixels: 14,
        gap: 8
      }),
      focusColor: "green",
      overlayFillEnabled: false,
      overlayShadowEnabled: true,
      rectangleThickness: 3,
      clickEffect: "flash",
      keyboardLinkHoverHints: false,
      paintStrategy: "BC",
      focusPadding: 2,
      skipForParent: true
    })
  });

  // themes/dark-pro/theme.js
  var color = createDarkProColor();
  var metalColor = createMetalColor();
  var DARK_PRO_THEME = Object.freeze({
    id: "dark-pro",
    meta: Object.freeze({ name: "Dark Pro" }),
    type: createProTypeTokens(),
    titlebar: createTitlebarChromeTokens(),
    keys: createKeyChromeTokens(),
    radius: createProRadiusTokens(),
    color,
    effect: createDarkProEffect(color),
    shape: Object.freeze({ cornerMode: "radius", cutSize: "0px" }),
    icons: Object.freeze({
      pack: "dark-pro",
      fallbackPack: "shared",
      overrides: Object.freeze({}),
      color: Object.freeze({
        chrome: color.fg,
        keycap: "#0c1018",
        accent: color.accent
      })
    }),
    clickDefaults: DARK_PRO_CLICK_DEFAULTS,
    surfaces: Object.freeze({
      onboarding: Object.freeze({
        color: metalColor,
        effect: createMetalEffect(metalColor),
        icons: Object.freeze({
          color: Object.freeze({
            chrome: metalColor.fg,
            keycap: "#1c1c1c",
            accent: metalColor.accent
          })
        })
      })
    })
  });

  // themes/gray-metal-pro/theme.js
  init_schema();
  var color2 = createMetalColor();
  var GRAY_METAL_PRO_THEME = Object.freeze({
    id: "gray-metal-pro",
    meta: Object.freeze({ name: "Gray Metal Pro" }),
    type: createProTypeTokens({
      ui: "Helvetica, Arial, sans-serif"
    }),
    titlebar: createTitlebarChromeTokens(),
    keys: createKeyChromeTokens(),
    radius: createProRadiusTokens({ panel: "3px", btn: "2px" }),
    color: color2,
    effect: createMetalEffect(color2),
    shape: Object.freeze({ cornerMode: "radius", cutSize: "0px" }),
    icons: Object.freeze({
      pack: "gray-metal-pro",
      fallbackPack: "shared",
      overrides: Object.freeze({}),
      color: Object.freeze({
        chrome: color2.fg,
        keycap: "#1c1c1c",
        accent: color2.accent
      })
    }),
    clickDefaults: GRAY_METAL_CLICK_DEFAULTS
  });

  // themes/gx-er/theme.js
  init_schema();
  var color3 = createGxColor();
  var type = createProTypeTokens({
    display: "'ROBOTECHGPRegular', 'TitilliumText', Helvetica, Arial, sans-serif",
    heading: "'Cubellan', 'TitilliumText', Helvetica, Arial, sans-serif",
    subhead: "'TitilliumText', Helvetica, Arial, sans-serif",
    body: "'Ezarion', 'Dosis', Helvetica, Arial, sans-serif",
    ui: "'TitilliumText', Helvetica, Arial, sans-serif",
    kbd: "'Dosis', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
    mono: "'Dosis', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
    caption: "'Ezarion', Helvetica, Arial, sans-serif"
  });
  type.letterSpacing = {
    display: "0.08em",
    titlebar: "0.06em",
    ui: "0.02em"
  };
  type.textTransform = {
    display: "uppercase",
    titlebar: "uppercase"
  };
  var GX_ER_THEME = Object.freeze({
    id: "gx-er",
    meta: Object.freeze({ name: "GX-er" }),
    type,
    titlebar: createTitlebarChromeTokens({
      titleWeight: "700",
      iconDisplay: "inline-flex",
      iconSize: "12px",
      kbdTransform: "uppercase",
      kbdTracking: "0.06em"
    }),
    keys: createKeyChromeTokens({
      shading: "flat",
      border: "1px solid rgba(0, 229, 255, 0.35)",
      cornerMode: "cut",
      cutSize: "4px"
    }),
    radius: createProRadiusTokens({
      panel: "0px",
      btn: "0px",
      field: "0px",
      xs: "0px",
      sm: "0px"
    }),
    color: color3,
    effect: createGxEffect(color3),
    shape: Object.freeze({ cornerMode: "cut", cutSize: "8px" }),
    icons: Object.freeze({
      pack: "gx-er",
      fallbackPack: "shared",
      overrides: Object.freeze({
        close: "chrome/close.svg",
        collapse: "chrome/collapse.svg"
      }),
      color: Object.freeze({
        chrome: color3.accent,
        keycap: "#001018",
        accent: color3.accent
      })
    }),
    clickDefaults: GX_ER_CLICK_DEFAULTS
  });

  // themes/index.js
  var PACKAGES = Object.freeze({
    "dark-pro": DARK_PRO_THEME,
    "gray-metal-pro": GRAY_METAL_PRO_THEME,
    "gx-er": GX_ER_THEME
  });
  function getTheme(id, overrides) {
    const key = normalizeThemeId(id);
    const base = PACKAGES[key] || PACKAGES[DEFAULT_THEME_ID];
    return mergeTheme(base, overrides && typeof overrides === "object" ? overrides : {});
  }
  function getAllThemesCss() {
    const blocks = THEME_IDS.map((id) => {
      const vars = themeToCssVars(getTheme(id));
      return cssVarsToBlock(
        vars,
        `:host([data-kp-theme="${id}"]), [data-kp-theme="${id}"]`
      );
    });
    const onboarding = themeToCssVars(
      mergeTheme(DARK_PRO_THEME, DARK_PRO_THEME.surfaces?.onboarding || {})
    );
    blocks.push(cssVarsToBlock(
      onboarding,
      `[data-kp-theme="dark-pro"][data-kp-surface="onboarding"], [data-kp-theme="dark-pro"] [data-kp-surface="onboarding"]`
    ));
    return `${blocks.join("\n")}
${getCutCornerCss()}
${getTitlebarChromeCss()}
${getSelectMenuCss()}`;
  }
  function getThemeCss(theme) {
    const vars = themeToCssVars(theme);
    const id = theme?.id || DEFAULT_THEME_ID;
    return `${cssVarsToBlock(vars, `:host, :root, [data-kp-theme="${id}"]`)}
${getCutCornerCss()}
${getTitlebarChromeCss()}
${getSelectMenuCss()}`;
  }

  // themes/font-faces.js
  function fontUrl(file) {
    try {
      if (typeof chrome !== "undefined" && chrome.runtime?.getURL) {
        return chrome.runtime.getURL(`fonts/${file}`);
      }
    } catch {
    }
    return `../fonts/${file}`;
  }
  function getThemeFontFaceCss() {
    const robotech = fontUrl("ROBOTECHGPRegular.ttf");
    const titillium = fontUrl("TitilliumTextRegular.otf");
    const titilliumBold = fontUrl("TitilliumTextBold.ttf");
    const cubellan = fontUrl("CubellanRegular.ttf");
    const ezarion = fontUrl("EzarionRegular.ttf");
    const dosis = fontUrl("DosisBook.ttf");
    return `
@font-face {
  font-family: 'ROBOTECHGPRegular';
  src: url('${robotech}') format('truetype');
  font-weight: normal;
  font-style: normal;
  font-display: block;
}
@font-face {
  font-family: 'TitilliumText';
  src: url('${titillium}') format('opentype');
  font-weight: 100 500;
  font-style: normal;
  font-display: block;
}
@font-face {
  font-family: 'TitilliumText';
  src: url('${titilliumBold}') format('truetype');
  font-weight: 600 900;
  font-style: normal;
  font-display: block;
}
@font-face {
  font-family: 'Cubellan';
  src: url('${cubellan}') format('truetype');
  font-weight: normal;
  font-style: normal;
  font-display: block;
}
@font-face {
  font-family: 'Ezarion';
  src: url('${ezarion}') format('truetype');
  font-weight: normal;
  font-style: normal;
  font-display: block;
}
@font-face {
  font-family: 'Dosis';
  src: url('${dosis}') format('truetype');
  font-weight: normal;
  font-style: normal;
  font-display: block;
}
`.trim();
  }

  // src/modules/theme-manager.js
  var STYLE_ATTR = "data-kp-theme-vars";
  var FONT_ATTR = "data-kp-theme-fonts";
  var ALL_THEMES_ATTR = "data-kp-all-themes";
  var _activeTheme = getTheme(DEFAULT_THEME_ID);
  var _listeners = /* @__PURE__ */ new Set();
  function getActiveTheme() {
    return _activeTheme;
  }
  function notify() {
    for (const fn of _listeners) {
      try {
        fn(_activeTheme);
      } catch {
      }
    }
  }
  function injectStyle(root, css, attr) {
    if (!root) return;
    const doc = root.nodeType === 9 ? root : root.ownerDocument || document;
    const mount = root.nodeType === 9 ? root.head || root.documentElement : root.host ? root : root;
    if (!doc || !mount?.appendChild) return;
    let style = null;
    try {
      style = mount.querySelector?.(`style[${attr}]`);
    } catch {
    }
    if (!style) {
      try {
        style = doc.createElement("style");
        style.setAttribute(attr, "true");
        style.textContent = css;
        mount.appendChild(style);
      } catch {
      }
      return;
    }
    if (style.textContent !== css) {
      try {
        style.textContent = css;
      } catch {
      }
    }
  }
  function applyThemeDataset(el2, theme) {
    if (!el2?.setAttribute) return;
    const id = theme?.id || DEFAULT_THEME_ID;
    try {
      el2.setAttribute("data-kp-theme", id);
    } catch {
    }
    const cut = theme?.shape?.cornerMode === "cut";
    try {
      if (cut) el2.setAttribute("data-kp-corner", "cut");
      else el2.removeAttribute("data-kp-corner");
    } catch {
    }
  }
  function applyThemeCssVars(el2, theme) {
    if (!el2?.style?.setProperty) return;
    const vars = themeToCssVars(theme);
    for (const [k, v] of Object.entries(vars)) {
      try {
        el2.style.setProperty(k, v);
      } catch {
      }
    }
  }
  function injectAllThemeMaps(root = document) {
    injectStyle(root, getThemeFontFaceCss(), FONT_ATTR);
    injectStyle(root, `${getAllThemesCss()}
${getCutCornerCss()}`, ALL_THEMES_ATTR);
  }
  var CHROME_THEME_HOST_SEL = [
    ".kp-chrome-window",
    "[data-kp-ui-shadow]",
    "[data-kp-select]",
    ".kp-select-menu-host",
    ".kp-select-menu",
    ".kpv2-settings-host",
    ".kpv2-docs-host"
  ].join(", ");
  function collectChromeThemeHosts(root, out = [], seen = /* @__PURE__ */ new Set()) {
    if (!root) return out;
    const addHost = (el2) => {
      if (!el2 || seen.has(el2)) return;
      seen.add(el2);
      out.push(el2);
      try {
        if (el2.shadowRoot) collectChromeThemeHosts(el2.shadowRoot, out, seen);
      } catch {
      }
    };
    try {
      if (root.querySelectorAll) {
        root.querySelectorAll(CHROME_THEME_HOST_SEL).forEach(addHost);
      }
    } catch {
    }
    return out;
  }
  function paintShadowTheme(shadow, theme) {
    if (!shadow) return;
    injectAllThemeMaps(shadow);
    injectStyle(shadow, getThemeCss(theme), STYLE_ATTR);
  }
  function syncThemedIconMasks(root, theme) {
    if (!root?.querySelectorAll) return;
    try {
      root.querySelectorAll("[data-kp-theme-icon]").forEach((el2) => {
        const id = el2.getAttribute("data-kp-theme-icon");
        const url = getThemeIconUrl(id, theme);
        if (!url || !el2.style) return;
        const img = `url("${String(url).replace(/"/g, '\\"')}")`;
        try {
          el2.style.webkitMaskImage = img;
        } catch {
        }
        try {
          el2.style.maskImage = img;
        } catch {
        }
      });
    } catch {
    }
  }
  function applyThemeToRoots(theme, opts = {}) {
    _activeTheme = theme || getTheme(DEFAULT_THEME_ID);
    const roots = opts.roots && opts.roots.length ? opts.roots : [document];
    const hostSet = /* @__PURE__ */ new Set();
    for (const host of opts.hosts || []) {
      if (host) hostSet.add(host);
    }
    try {
      collectChromeThemeHosts(document).forEach((el2) => hostSet.add(el2));
    } catch {
    }
    for (const root of roots) {
      collectChromeThemeHosts(root).forEach((el2) => hostSet.add(el2));
    }
    for (const root of roots) {
      if (!root) continue;
      injectAllThemeMaps(root);
      injectStyle(root, getThemeCss(_activeTheme), STYLE_ATTR);
      const el2 = root.nodeType === 9 ? root.documentElement : root.host || null;
      applyThemeDataset(el2, _activeTheme);
      if (el2) applyThemeCssVars(el2, _activeTheme);
    }
    for (const host of hostSet) {
      applyThemeDataset(host, _activeTheme);
      applyThemeCssVars(host, _activeTheme);
      if (host?.shadowRoot) {
        paintShadowTheme(host.shadowRoot, _activeTheme);
        applyThemeDataset(host.shadowRoot.host, _activeTheme);
        syncThemedIconMasks(host.shadowRoot, _activeTheme);
      }
      syncThemedIconMasks(host, _activeTheme);
    }
    for (const root of roots) {
      syncThemedIconMasks(root, _activeTheme);
    }
    try {
      applyThemeDataset(document.documentElement, _activeTheme);
      applyThemeCssVars(document.documentElement, _activeTheme);
      injectAllThemeMaps(document);
      injectStyle(document, getThemeCss(_activeTheme), STYLE_ATTR);
      syncThemedIconMasks(document, _activeTheme);
    } catch {
    }
    notify();
    try {
      const id = _activeTheme?.id;
      if (id) localStorage.setItem("kp_theme_id_v1", id);
    } catch {
    }
    try {
      const keys = _activeTheme?.keys || {};
      localStorage.setItem("kp_theme_overrides_v1", JSON.stringify({
        keys: {
          shading: keys.shading === "flat" ? "flat" : "bevel",
          cornerMode: keys.cornerMode === "cut" ? "cut" : "radius",
          cutSize: keys.cutSize || "4px",
          border: keys.border || "1px solid rgba(0, 0, 0, 0.4)"
        },
        titlebar: {
          iconDisplay: _activeTheme?.titlebar?.iconDisplay === "inline-flex" ? "inline-flex" : "none"
        }
      }));
    } catch {
    }
    return _activeTheme;
  }
  function resolveThemeFromSettings(settings) {
    const id = normalizeThemeId(settings?.themeId);
    const overrides = settings?.themeOverrides && typeof settings.themeOverrides === "object" ? settings.themeOverrides : {};
    return getTheme(id, overrides);
  }
  function applyOnboardingSurface(el2, theme = _activeTheme) {
    if (!el2?.setAttribute) return;
    try {
      el2.setAttribute("data-kp-surface", "onboarding");
    } catch {
    }
    const overlay = theme?.surfaces?.onboarding;
    if (!overlay) {
      applyThemeDataset(el2, theme);
      applyThemeCssVars(el2, theme);
      return;
    }
    const merged = mergeTheme(theme, overlay);
    applyThemeDataset(el2, theme);
    applyThemeCssVars(el2, merged);
  }
  function getThemeClickDefaults(theme = _activeTheme) {
    const d = theme?.clickDefaults || getTheme(DEFAULT_THEME_ID).clickDefaults;
    return {
      cursorMode: d.cursorMode,
      clickMode: {
        ...d.clickMode || {},
        cursor: { ...d.clickMode?.cursor || {} }
      }
    };
  }

  // src/ui/popover-titlebar.js
  var VARIANT_STYLES = {
    modal: {
      titlebar: `
      padding: 10px 14px;
      background: ${NCT_DARK_UI_TITLEBAR_GRADIENT};
      border-bottom: ${NCT_DARK_UI_TITLEBAR_BORDER_BOTTOM};
      box-shadow: ${NCT_DARK_UI_TITLEBAR_BOX_SHADOW};
      display: flex;
      justify-content: space-between;
      align-items: center;
      gap: 10px;
      flex-shrink: 0;
      min-height: 40px;
      box-sizing: border-box;
      font-family: var(--kp-font-heading, ${KP_UI_FONT});
      font-size: 14px;
      font-weight: 400;
      font-style: normal;
      line-height: 1.3;
      letter-spacing: var(--kp-type-tracking-titlebar, 0.02em);
      text-transform: var(--kp-type-transform-titlebar, none);
      -webkit-font-smoothing: antialiased;
    `,
      title: `
      font-family: inherit;
      font-size: 14px;
      font-weight: var(--kp-titlebar-title-weight, 600);
      letter-spacing: var(--kp-type-tracking-titlebar, 0.02em);
      text-transform: var(--kp-type-transform-titlebar, none);
      color: var(--kp-color-fg, #e8e8e8);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      min-width: 0;
      flex: 0 1 auto;
    `,
      hint: `
      font-family: inherit;
      color: #999;
      font-weight: normal;
      font-size: 12px;
      margin-left: 10px;
      flex-shrink: 0;
    `,
      close: `
      /* Host pages often style bare \`button\` (e.g. Slashdot margin-bottom:40px). */
      margin: 0;
      appearance: none;
      -webkit-appearance: none;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      box-sizing: border-box;
      font-family: inherit;
      background: ${NCT_DARK_UI_BTN_GRADIENT};
      border: ${NCT_DARK_UI_BTN_BORDER};
      font-size: 18px;
      font-weight: 400;
      cursor: pointer;
      color: ${NCT_DARK_UI_COLORS.fg};
      padding: 2px 8px;
      line-height: 1;
      border-radius: ${NCT_DARK_UI_BTN_RADIUS};
      flex-shrink: 0;
      min-width: 0;
      min-height: 0;
      height: auto;
      width: auto;
      text-align: center;
      text-shadow: none;
      box-shadow: ${NCT_DARK_UI_ICON_BUTTON_OUTLINE};
      position: relative;
    `
    },
    preview: {
      titlebar: `
      padding: 6px 10px;
      background: ${NCT_DARK_UI_TITLEBAR_GRADIENT};
      border-bottom: ${NCT_DARK_UI_TITLEBAR_BORDER_BOTTOM};
      box-shadow: ${NCT_DARK_UI_TITLEBAR_BOX_SHADOW};
      display: flex;
      justify-content: space-between;
      align-items: center;
      gap: 8px;
      flex-shrink: 0;
      min-height: 34px;
      box-sizing: border-box;
      user-select: none;
      -webkit-user-select: none;
      touch-action: none;
      font-family: var(--kp-font-heading, ${KP_UI_FONT});
      font-size: 12px;
      font-weight: 400;
      font-style: normal;
      line-height: 1.3;
      letter-spacing: var(--kp-type-tracking-titlebar, 0.02em);
      text-transform: var(--kp-type-transform-titlebar, none);
      -webkit-font-smoothing: antialiased;
    `,
      title: `
      font-family: inherit;
      font-size: 12px;
      font-weight: var(--kp-titlebar-title-weight, 600);
      letter-spacing: var(--kp-type-tracking-titlebar, 0.02em);
      text-transform: var(--kp-type-transform-titlebar, none);
      color: var(--kp-color-fg, #e8e8e8);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      min-width: 0;
      flex: 0 1 auto;
    `,
      hint: `
      font-family: inherit;
      color: #999;
      font-weight: normal;
      font-size: 12px;
      margin-left: 8px;
      flex-shrink: 0;
    `,
      close: `
      /* Host pages often style bare \`button\` (e.g. Slashdot margin-bottom:40px). */
      margin: 0;
      appearance: none;
      -webkit-appearance: none;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      box-sizing: border-box;
      font-family: inherit;
      background: ${NCT_DARK_UI_BTN_GRADIENT};
      border: ${NCT_DARK_UI_BTN_BORDER};
      font-size: 16px;
      font-weight: 400;
      cursor: pointer;
      color: ${NCT_DARK_UI_COLORS.fg};
      padding: 2px 6px;
      line-height: 1;
      border-radius: ${NCT_DARK_UI_BTN_RADIUS};
      flex-shrink: 0;
      min-width: 0;
      min-height: 0;
      height: auto;
      width: auto;
      text-align: center;
      text-shadow: none;
      box-shadow: ${NCT_DARK_UI_ICON_BUTTON_OUTLINE};
      position: relative;
    `
    },
    panel: {
      titlebar: `
      padding: 0 6px 0 10px;
      background: ${NCT_DARK_UI_TITLEBAR_GRADIENT};
      border-bottom: ${NCT_DARK_UI_TITLEBAR_BORDER_BOTTOM};
      box-shadow: ${NCT_DARK_UI_TITLEBAR_BOX_SHADOW};
      display: flex;
      justify-content: space-between;
      align-items: center;
      gap: 8px;
      flex-shrink: 0;
      height: 28px;
      min-height: 28px;
      max-height: 28px;
      box-sizing: border-box;
      user-select: none;
      -webkit-user-select: none;
      touch-action: none;
      cursor: grab;
      font-family: var(--kp-font-heading, ${KP_UI_FONT});
      font-size: 11px;
      font-weight: 400;
      font-style: normal;
      line-height: 1.3;
      letter-spacing: var(--kp-type-tracking-titlebar, 0.02em);
      text-transform: var(--kp-type-transform-titlebar, none);
      -webkit-font-smoothing: antialiased;
    `,
      title: `
      font-family: inherit;
      font-size: 11px;
      font-weight: var(--kp-titlebar-title-weight, 600);
      letter-spacing: var(--kp-type-tracking-titlebar, 0.02em);
      text-transform: var(--kp-type-transform-titlebar, none);
      color: var(--kp-color-fg, ${NCT_DARK_UI_COLORS.fg});
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      min-width: 0;
      flex: 0 1 auto;
      line-height: 28px;
    `,
      hint: `
      font-family: inherit;
      color: rgba(140, 145, 155, 0.95);
      font-weight: 500;
      font-size: 10px;
      margin-left: 8px;
      flex-shrink: 0;
      line-height: 28px;
    `,
      close: `
      margin: 0;
      appearance: none;
      -webkit-appearance: none;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      box-sizing: border-box;
      font-family: inherit;
      background: transparent;
      border: none;
      font-size: 15px;
      font-weight: 400;
      cursor: pointer;
      color: rgba(200, 200, 205, 0.9);
      padding: 0;
      line-height: 20px;
      border-radius: 4px;
      flex-shrink: 0;
      min-width: 22px;
      min-height: 22px;
      height: 22px;
      width: 22px;
      text-align: center;
      text-shadow: none;
      box-shadow: ${NCT_DARK_UI_ICON_BUTTON_OUTLINE};
      position: relative;
    `
    }
  };
  var KBD_STYLE = `
  font-family: var(--kp-font-kbd, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
  font-size: var(--kp-type-kbd-size, 10px);
  padding: 1px 6px;
  border: var(--kp-kbd-border, ${NCT_DARK_UI_BTN_BORDER});
  border-radius: var(--kp-radius-btn, ${NCT_DARK_UI_BTN_RADIUS});
  background: var(--kp-kbd-bg, ${NCT_DARK_UI_BTN_GRADIENT});
  color: var(--kp-color-kbd-fg, ${NCT_DARK_UI_COLORS.fg});
  box-shadow: var(--kp-kbd-shadow, none);
  text-transform: var(--kp-kbd-transform, none);
  letter-spacing: var(--kp-kbd-tracking, 0.02em);
`;
  var SHORTCUT_KBD_STYLE = `
  ${KBD_STYLE}
  font-weight: 400;
  font-size: 10px;
  line-height: 1.2;
  margin-left: 6px;
  flex-shrink: 0;
  vertical-align: middle;
  box-sizing: border-box;
`;
  function createTitlebarKbd(doc = document, label = "") {
    const kbd = doc.createElement("kbd");
    kbd.className = "kp-titlebar-kbd";
    kbd.style.cssText = KBD_STYLE;
    kbd.textContent = String(label || "");
    return kbd;
  }
  function createTitlebarLeadingIcon(doc = document, iconId = "window") {
    const el2 = doc.createElement("span");
    el2.className = "kp-titlebar-icon";
    el2.setAttribute("aria-hidden", "true");
    const id = typeof iconId === "string" && iconId.trim() ? iconId.trim() : "window";
    el2.setAttribute("data-kp-titlebar-icon", id);
    el2.setAttribute("data-kp-theme-icon", id);
    try {
      const url = getThemeIconUrl(id, getActiveTheme());
      if (url) {
        const img = `url("${String(url).replace(/"/g, '\\"')}")`;
        el2.style.webkitMaskImage = img;
        el2.style.maskImage = img;
      }
    } catch {
    }
    return el2;
  }
  function normalizeTitlebarShortcutLabel(raw) {
    let label = raw != null ? String(raw).trim() : "";
    while (label.length >= 2 && label.startsWith("(") && label.endsWith(")") || label.length >= 2 && label.startsWith("[") && label.endsWith("]")) {
      label = label.slice(1, -1).trim();
    }
    return label;
  }
  function createTitlebarShortcut(doc = document, shortcut = "") {
    const kbd = doc.createElement("kbd");
    kbd.className = "kpv2-popover-titlebar-shortcut";
    kbd.setAttribute("data-kp-titlebar-shortcut", "true");
    kbd.style.cssText = SHORTCUT_KBD_STYLE;
    const label = normalizeTitlebarShortcutLabel(shortcut);
    kbd.textContent = label;
    if (!label) kbd.style.display = "none";
    return kbd;
  }
  function setTitlebarShortcutText(el2, shortcut) {
    if (!el2) return;
    const label = normalizeTitlebarShortcutLabel(shortcut);
    if (!label) {
      el2.textContent = "";
      el2.style.display = "none";
      return;
    }
    el2.textContent = label;
    el2.style.display = "";
  }
  function createTitlebarCloseHint({
    doc = document,
    keys = ["Esc"],
    prefix = "Press",
    suffix = "",
    useKbdChips = true
  } = {}) {
    const frag = doc.createDocumentFragment();
    if (prefix) {
      frag.appendChild(doc.createTextNode(`${prefix} `));
    }
    const list = Array.isArray(keys) ? keys.filter((k) => k != null && String(k).length) : [];
    list.forEach((key, i) => {
      if (i > 0) frag.appendChild(doc.createTextNode(" / "));
      if (useKbdChips) {
        frag.appendChild(createTitlebarKbd(doc, String(key)));
      } else {
        frag.appendChild(doc.createTextNode(String(key)));
      }
    });
    if (suffix) {
      const needsSpace = !/^\s/.test(suffix);
      frag.appendChild(doc.createTextNode(needsSpace ? ` ${suffix}` : suffix));
    }
    return frag;
  }
  function createPopoverTitlebar(config = {}) {
    const doc = config.doc || document;
    const variant = config.variant === "preview" || config.variant === "panel" ? config.variant : "modal";
    const styles = VARIANT_STYLES[variant];
    const showClose = config.showClose !== false;
    const draggable = config.draggable === true || variant === "panel";
    const className = typeof config.className === "string" && config.className.trim() ? config.className.trim() : "kpv2-popover-titlebar";
    const titlebar = doc.createElement("div");
    titlebar.className = className;
    titlebar.setAttribute("data-kp-popover-titlebar", "true");
    titlebar.setAttribute("data-kp-titlebar-variant", variant);
    titlebar.style.cssText = styles.titlebar;
    if (draggable) {
      titlebar.style.cursor = "grab";
    }
    if (config.titleAttr) {
      titlebar.title = config.titleAttr;
    } else if (draggable) {
      titlebar.title = "Drag to move";
    }
    if (config.ariaLabel) {
      titlebar.setAttribute("aria-label", config.ariaLabel);
    }
    const titleContainer = doc.createElement("div");
    titleContainer.className = "kpv2-popover-titlebar-title-wrap";
    titleContainer.style.cssText = `
    display: flex;
    align-items: center;
    gap: 6px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1;
    min-width: 0;
    margin-right: 4px;
    ${draggable ? "pointer-events: none;" : ""}
  `;
    if (config.icon !== false) {
      const iconId = typeof config.icon === "string" && config.icon.trim() ? config.icon.trim() : "window";
      titleContainer.appendChild(createTitlebarLeadingIcon(doc, iconId));
    }
    const titleEl = doc.createElement("span");
    titleEl.className = "kpv2-popover-titlebar-title";
    titleEl.style.cssText = styles.title;
    titleEl.textContent = typeof config.title === "string" ? config.title : "";
    const shortcutEl = createTitlebarShortcut(doc, "");
    if (variant === "panel" || variant === "preview") {
      shortcutEl.style.fontSize = "10px";
    }
    const hintEl = doc.createElement("span");
    hintEl.className = "kpv2-popover-titlebar-hint";
    hintEl.style.cssText = styles.hint;
    titleContainer.appendChild(titleEl);
    titleContainer.appendChild(shortcutEl);
    titleContainer.appendChild(hintEl);
    titlebar.appendChild(titleContainer);
    const actionsSlot = doc.createElement("div");
    actionsSlot.className = "kpv2-popover-titlebar-actions";
    actionsSlot.style.cssText = `
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
  `;
    const actionNodes = [];
    if (Array.isArray(config.actions)) {
      for (const node of config.actions) {
        if (node) {
          actionsSlot.appendChild(node);
          actionNodes.push(node);
        }
      }
    } else if (config.actions) {
      actionsSlot.appendChild(config.actions);
      actionNodes.push(config.actions);
    }
    if (actionsSlot.childNodes.length > 0) {
      titlebar.appendChild(actionsSlot);
    }
    let closeButton = null;
    if (showClose) {
      closeButton = doc.createElement("button");
      closeButton.type = "button";
      closeButton.className = "kpv2-popover-titlebar-close";
      closeButton.style.cssText = styles.close;
      closeButton.textContent = config.closeLabel != null ? String(config.closeLabel) : "\xD7";
      closeButton.title = config.closeTitle || "Close (Esc)";
      closeButton.setAttribute("aria-label", config.closeTitle || "Close (Esc)");
      closeButton.addEventListener("click", (e) => {
        try {
          e.preventDefault();
          e.stopPropagation();
        } catch {
        }
        try {
          config.onClose?.();
        } catch {
        }
      });
      titlebar.appendChild(closeButton);
    }
    const setHint = (hint) => {
      while (hintEl.firstChild) hintEl.removeChild(hintEl.firstChild);
      if (hint == null || hint === "") {
        hintEl.style.display = "none";
        return;
      }
      hintEl.style.display = "";
      if (typeof hint === "string") {
        hintEl.textContent = hint;
      } else if (hint instanceof Node) {
        hintEl.appendChild(hint);
      } else {
        hintEl.textContent = String(hint);
      }
    };
    const setTitle = (text) => {
      titleEl.textContent = text != null ? String(text) : "";
    };
    const setShortcut = (shortcut) => {
      setTitlebarShortcutText(shortcutEl, shortcut);
    };
    setShortcut(config.shortcut);
    setHint(config.hint);
    const getInteractiveElements = () => {
      const els = [];
      if (closeButton) els.push(closeButton);
      for (const node of actionNodes) {
        if (!node) continue;
        if (node.matches?.('button, a, input, select, textarea, [role="button"]')) {
          els.push(node);
        } else if (typeof node.querySelectorAll === "function") {
          node.querySelectorAll('button, a, input, select, textarea, [role="button"]').forEach((el2) => {
            els.push(el2);
          });
        }
      }
      return els;
    };
    return {
      titlebar,
      closeButton,
      titleEl,
      shortcutEl,
      hintEl,
      titleContainer,
      actionsSlot,
      setTitle,
      setShortcut,
      setHint,
      getInteractiveElements
    };
  }
  function createUrlPopoverTitlebar(config = {}) {
    const { getUrl, extraActions, afterOpen, afterOpenNewTab, ...titlebarConfig } = config;
    const { actions: openActions } = createPreviewOpenActionButtons({
      doc: titlebarConfig.doc,
      getUrl,
      afterOpen,
      afterOpenNewTab
    });
    const extra = Array.isArray(extraActions) ? extraActions.filter(Boolean) : extraActions ? [extraActions] : [];
    return createPopoverTitlebar({
      ...titlebarConfig,
      actions: [...extra, openActions]
    });
  }

  // src/ui/kp-chrome-shadow.js
  function getChromeMountParent(doc = document) {
    try {
      return doc?.body || doc?.documentElement || null;
    } catch {
      return null;
    }
  }
  function ensureChromeHostMounted(host, doc = document) {
    if (!host) return null;
    const parent = getChromeMountParent(doc);
    if (!parent) return null;
    try {
      const current = host.parentElement;
      if (doc?.body && current === doc.documentElement) {
        doc.body.appendChild(host);
        return doc.body;
      }
      if (current !== parent) {
        parent.appendChild(host);
      }
    } catch {
    }
    return parent;
  }
  function markChromeWindow(el2) {
    if (!el2) return el2;
    try {
      el2.classList?.add("kp-chrome-window");
    } catch {
    }
    try {
      const theme = getActiveTheme();
      applyThemeDataset(el2, theme);
      applyThemeCssVars(el2, theme);
    } catch {
    }
    return el2;
  }
  function ensureOpenChromeShadow(host, opts = {}) {
    if (!host) return null;
    try {
      host.setAttribute("data-kp-ui-shadow", String(opts.id || "chrome"));
    } catch {
    }
    if (opts.chromeWindow) markChromeWindow(host);
    let shadow = null;
    try {
      shadow = host.shadowRoot || host.attachShadow({ mode: "open" });
    } catch {
      shadow = host.shadowRoot || null;
    }
    try {
      const theme = getActiveTheme();
      applyThemeDataset(host, theme);
      applyThemeCssVars(host, theme);
      if (shadow) injectAllThemeMaps(shadow);
    } catch {
    }
    return shadow;
  }
  function injectChromeStyles(root, { attr, css } = {}) {
    if (!root || !attr) return null;
    const doc = root.nodeType === 9 ? root : root.ownerDocument;
    const mount = root.nodeType === 9 ? root.head : root;
    if (!doc || !mount?.appendChild) return null;
    let style = null;
    try {
      style = mount.querySelector(`style[${attr}]`);
    } catch {
    }
    if (!style) {
      try {
        style = doc.createElement("style");
        style.setAttribute(attr, "true");
        style.textContent = String(css || "");
        mount.appendChild(style);
        return style;
      } catch {
        return null;
      }
    }
    if (style.textContent !== String(css || "")) {
      try {
        style.textContent = String(css || "");
      } catch {
      }
    }
    return style;
  }
  function getComposedEventElement(event, selector) {
    if (!event || !selector) return null;
    try {
      const path = typeof event.composedPath === "function" ? event.composedPath() : [];
      for (const node of path || []) {
        if (node?.nodeType === 1 && node.matches?.(selector)) return node;
      }
    } catch {
    }
    try {
      const target = event.target;
      return target?.nodeType === 1 && target.matches?.(selector) ? target : null;
    } catch {
      return null;
    }
  }
  function closestComposed(element, selector) {
    let node = element;
    let depth = 0;
    while (node && depth++ < 32) {
      try {
        if (node.matches?.(selector)) return node;
        if (node.parentElement) {
          node = node.parentElement;
          continue;
        }
        const root = node.getRootNode?.();
        node = root && typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot ? root.host : null;
      } catch {
        return null;
      }
    }
    return null;
  }
  function containsComposed(host, node) {
    if (!host || !node) return false;
    if (host === node) return true;
    try {
      if (host.contains(node)) return true;
    } catch {
    }
    let current = node;
    let depth = 0;
    while (current && depth++ < 32) {
      if (current === host) return true;
      const parent = current.parentElement;
      if (parent) {
        current = parent;
        continue;
      }
      const root = current.getRootNode?.();
      current = root && typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot ? root.host : null;
    }
    return false;
  }
  function isInteractiveKeyPilotOverlayElement(element) {
    return !!closestComposed(
      element,
      [
        "#kpv2-media-lib-overlay",
        ".kpv2-media-lib-overlay",
        "#kpv2-page-media-overlay",
        ".kpv2-page-media-overlay"
      ].join(", ")
    );
  }
  function isInteractiveKeyPilotOverlayClass(className) {
    const c = String(className || "");
    return c.startsWith("kpv2-media-lib-") || c.startsWith("kpv2-page-media-");
  }
  function isClickableKeyPilotChromeElement(element) {
    if (isInteractiveKeyPilotOverlayElement(element)) return true;
    return !!closestComposed(
      element,
      [
        ".kp-floating-keyboard-help",
        ".kp-keybindings-popover",
        ".kp-select",
        ".kp-select-menu-host",
        ".kp-select-menu",
        ".kp-action-config-panel",
        ".kp-control-strip",
        ".kp-onboarding-panel",
        ".kp-layout-config-panel",
        ".kpv2-top-sites-panel",
        ".kpv2-tab-history-panel",
        ".kpv2-settings-host",
        ".kpv2-docs-host",
        ".kpv2-popover-container",
        ".kp-launcher-container",
        // OS popup Link Preview / Open Popover: injected titlebar (Open, New Tab, ×)
        "#kpv2-popover-window-chrome-host",
        "[data-kp-popover-window-chrome]",
        ".kpv2-popover-window-titlebar"
      ].join(", ")
    );
  }
  function isKeyPilotChromeElement(element) {
    if (isInteractiveKeyPilotOverlayElement(element)) return false;
    return !!closestComposed(
      element,
      [
        "[data-kp-ui-shadow]",
        ".kp-floating-keyboard-help",
        ".kp-select",
        ".kp-select-menu-host",
        ".kp-select-menu",
        ".kp-control-strip",
        ".kp-onboarding-panel",
        ".kp-layout-config-panel",
        ".kpv2-top-sites-panel",
        ".kp-action-config-panel",
        ".kp-practice-popover",
        ".kp-procedure-result",
        ".kpv2-tab-history-panel",
        ".kp-launcher-container",
        ".kpv2-popover-container",
        ".kpv2-omnibox-backdrop",
        ".kpv2-page-media-overlay",
        '[id^="kpv2-"]'
      ].join(", ")
    );
  }

  // src/utils/preview-url.js
  function preferHttpsForPreview(url) {
    const s = String(url || "").trim();
    if (!s) return s;
    if (/^http:\/\//i.test(s)) {
      return `https://${s.slice("http://".length)}`;
    }
    return s;
  }
  function isHttpPopoverUrl(url) {
    const s = String(url || "").trim();
    if (!s) return false;
    try {
      const proto = new URL(s).protocol;
      return proto === "http:" || proto === "https:";
    } catch {
      return /^https?:\/\//i.test(s);
    }
  }

  // src/modules/popover-bridge-init.js
  var SCROLL_KEY_SLOTS = Object.freeze([
    "pageUp",
    "pageDown",
    "pageUpInstant",
    "pageDownInstant",
    "pageTop",
    "pageBottom"
  ]);
  var BINDING_ID_BY_SLOT = Object.freeze({
    pageUp: "PAGE_UP",
    pageDown: "PAGE_DOWN",
    pageUpInstant: "PAGE_UP_INSTANT",
    pageDownInstant: "PAGE_DOWN_INSTANT",
    pageTop: "PAGE_TOP",
    pageBottom: "PAGE_BOTTOM"
  });
  var SLOT_BY_FUNCTION_ID = Object.freeze({
    PAGE_UP: "pageUp",
    PAGE_DOWN: "pageDown",
    PAGE_UP_INSTANT: "pageUpInstant",
    PAGE_DOWN_INSTANT: "pageDownInstant",
    PAGE_TOP: "pageTop",
    PAGE_BOTTOM: "pageBottom"
  });
  var DEFAULT_POPOVER_SCROLL_KEYS = Object.freeze({
    pageUp: Object.freeze([]),
    pageDown: Object.freeze([]),
    pageUpInstant: Object.freeze(["c", "C"]),
    pageDownInstant: Object.freeze(["v", "V"]),
    pageTop: Object.freeze(["z", "Z"]),
    pageBottom: Object.freeze(["x", "X"])
  });
  function uniqueKeys(keys) {
    const seen = /* @__PURE__ */ new Set();
    const out = [];
    for (const raw of keys) {
      const k = String(raw || "");
      if (!k || seen.has(k)) continue;
      seen.add(k);
      out.push(k);
    }
    return out;
  }
  function keysFromBinding(binding) {
    return uniqueKeys(Array.isArray(binding?.keys) ? binding.keys.map(String) : []);
  }
  function expandSlotKey(slotKey) {
    const s = String(slotKey || "").trim();
    if (!s || s.includes("+")) return [];
    if (s.length === 1) {
      return uniqueKeys([s, s.toLowerCase(), s.toUpperCase()]);
    }
    return [s];
  }
  function resolveSlotFunctionId(assigned, userActions) {
    if (!assigned || assigned.type !== "function") return "";
    const id = String(assigned.id || "");
    if (!id) return "";
    if (id.startsWith("action:")) {
      const inst = (userActions || []).find((a) => a && a.id === id);
      return inst?.functionId ? String(inst.functionId) : "";
    }
    return id;
  }
  function collectPopoverScrollKeysFromSlots(slots, userActions) {
    const out = {};
    for (const slot of SCROLL_KEY_SLOTS) out[slot] = [];
    for (const [slotKey, assigned] of Object.entries(slots || {})) {
      const functionId = resolveSlotFunctionId(assigned, userActions);
      const dest = SLOT_BY_FUNCTION_ID[functionId];
      if (!dest) continue;
      out[dest].push(...expandSlotKey(slotKey));
    }
    for (const slot of SCROLL_KEY_SLOTS) out[slot] = uniqueKeys(out[slot]);
    return (
      /** @type {PopoverScrollKeys} */
      out
    );
  }
  function collectPopoverScrollKeysFromBindings(keybindings) {
    const KB = keybindings || {};
    const out = {};
    for (const slot of SCROLL_KEY_SLOTS) {
      out[slot] = keysFromBinding(KB[BINDING_ID_BY_SLOT[slot]]);
    }
    return (
      /** @type {PopoverScrollKeys} */
      out
    );
  }
  function collectPopoverScrollKeysFromKeyPilot(kp) {
    try {
      const sel = String(kp?._currentKeyboardLayoutId || "");
      if (sel.startsWith("user:") && kp._currentKeySlotMap) {
        return collectPopoverScrollKeysFromSlots(kp._currentKeySlotMap, kp._currentUserActions);
      }
    } catch {
    }
    return collectPopoverScrollKeysFromBindings(kp?.keybindings);
  }
  function popoverScrollKeyMatches(scrollKeys, slot, key) {
    const list = scrollKeys?.[slot];
    return Array.isArray(list) && list.includes(key);
  }
  function postPopoverBridgeInit(win, opts = {}) {
    if (!win || typeof win.postMessage !== "function") return;
    const closeKeys = Array.isArray(opts.closeKeys) && opts.closeKeys.length ? opts.closeKeys.map(String) : void 0;
    const scrollKeys = opts.scrollKeys || collectPopoverScrollKeysFromKeyPilot(
      opts.keyPilot || (typeof window !== "undefined" ? window.__KeyPilotInstance || window.keyPilot : null)
    );
    const payload = { type: MSG.POPOVER_BRIDGE_INIT };
    if (closeKeys) payload.closeKeys = closeKeys;
    if (scrollKeys) payload.scrollKeys = scrollKeys;
    try {
      win.postMessage(payload, "*");
    } catch {
    }
  }

  // src/modules/lazy-page-ui.js
  var PAGE_UI_BUNDLES = Object.freeze({
    docs: "pages/docs-bundled.js",
    settings: "pages/settings-bundled.js"
  });
  var cache = /* @__PURE__ */ new Map();
  function pageUiBundlePath(id) {
    const rel = PAGE_UI_BUNDLES[id];
    if (!rel) throw new Error(`Unknown page UI bundle: ${id}`);
    return rel;
  }
  function pageUiBundleUrl(id) {
    if (typeof chrome?.runtime?.getURL !== "function") {
      throw new Error("chrome.runtime.getURL is required to load page UI");
    }
    return chrome.runtime.getURL(pageUiBundlePath(id));
  }
  function loadPageUi(id) {
    const hit = cache.get(id);
    if (hit) return hit;
    const href = pageUiBundleUrl(id);
    const pending = import(href);
    cache.set(id, pending);
    pending.catch(() => {
      cache.delete(id);
    });
    return pending;
  }
  function loadDocsUi() {
    return loadPageUi("docs");
  }
  function loadSettingsUi() {
    return loadPageUi("settings");
  }

  // src/modules/popover-controller.js
  var PopoverController = class {
    /** @param {import('./overlay-manager.js').OverlayManager} host */
    constructor(host) {
      this.host = host;
      this.popoverContainer = null;
      this.popoverIframeElement = null;
      this.popoverIframeWindow = null;
      this.popoverMessageHandler = null;
      this.popoverInitTimer = null;
      this.popoverBridgeReady = false;
      this.popoverCloseButton = null;
      this.popoverKeyHandler = null;
      this._popoverLastMouse = { x: null, y: null };
      this._popoverMouseTrackerInstalled = false;
      this._popoverHybridFocusCleanup = null;
      this._docsHost = null;
      this._docsUnmount = null;
      this._docsUi = null;
      this._settingsUi = null;
      this._inPagePopoverKind = null;
      this._docsFontScale = 1.25;
      this._popoverWindowId = null;
      this._popoverWindowTabId = null;
      this._popoverWindowUrl = null;
      this._popoverWindowKind = null;
      this._popoverWindowMsgHandler = null;
      this._popoverPopupId = "kpv2-iframe-popover";
    }
    get popupManager() {
      return this.host.popupManager;
    }
    createElement(tag, props = {}) {
      return this.host.createElement(tag, props);
    }
    clearPopoverWindowTracking() {
      this._popoverWindowId = null;
      this._popoverWindowTabId = null;
      this._popoverWindowUrl = null;
      this._popoverWindowKind = null;
    }
    _focusPopoverIframe(iframe) {
      if (!iframe) return;
      try {
        iframe.focus();
      } catch {
      }
      try {
        iframe.contentWindow?.focus?.();
      } catch {
      }
    }
    /**
     * Hybrid focus: keys drive the iframe page when the pointer is over content;
     * when the pointer is over chrome (titlebar / actions / close), focus returns
     * to the parent so Esc/E/P and chrome controls stay reliable.
     *
     * @param {{
     *   iframe: HTMLIFrameElement,
     *   chromeEls?: Array<HTMLElement|null|undefined>,
     *   focusChromeEl?: HTMLElement|null
     * }} opts
     */
    _installPopoverHybridFocus({ iframe, chromeEls, focusChromeEl } = {}) {
      try {
        this._popoverHybridFocusCleanup?.();
      } catch {
      }
      this._popoverHybridFocusCleanup = null;
      if (!iframe) return;
      const chrome2 = (Array.isArray(chromeEls) ? chromeEls : []).filter(Boolean);
      let zone = null;
      const focusChrome = () => {
        const el2 = focusChromeEl || chrome2[0] || this.popoverContainer;
        try {
          el2?.focus?.();
        } catch {
        }
      };
      const focusIframe = () => this._focusPopoverIframe(iframe);
      const setZone = (next) => {
        if (next === zone) return;
        zone = next;
        if (next === "iframe") focusIframe();
        else if (next === "chrome") focusChrome();
      };
      const onChromeEnter = () => setZone("chrome");
      const onIframeEnter = () => setZone("iframe");
      for (const el2 of chrome2) {
        try {
          el2.addEventListener("pointerenter", onChromeEnter, true);
        } catch {
        }
      }
      try {
        iframe.addEventListener("pointerenter", onIframeEnter, true);
      } catch {
      }
      this._popoverHybridFocusCleanup = () => {
        for (const el2 of chrome2) {
          try {
            el2.removeEventListener("pointerenter", onChromeEnter, true);
          } catch {
          }
        }
        try {
          iframe.removeEventListener("pointerenter", onIframeEnter, true);
        } catch {
        }
      };
    }
    /**
     * Ensure we listen for SW popover-window closed messages once.
     */
    _ensurePopoverWindowMessageListener() {
      if (this._popoverWindowMsgHandler) return;
      this._popoverWindowMsgHandler = (message) => {
        try {
          if (!message || typeof message.type !== "string") return;
          if (message.type === MSG.POPOVER_WINDOW_CLOSED) {
            if (typeof message.windowId === "number" && this._popoverWindowId === message.windowId) {
              this._popoverWindowId = null;
              this._popoverWindowTabId = null;
              this._popoverWindowUrl = null;
              this._popoverWindowKind = null;
            }
          }
        } catch (e) {
          console.warn("[KeyPilot] Popover window message handler failed:", e?.message || e);
        }
      };
      try {
        installContentRuntimeRouter();
        this._popoverWindowMsgDispose = registerContentRuntimeHandler(
          MSG.POPOVER_WINDOW_CLOSED,
          this._popoverWindowMsgHandler
        );
      } catch {
      }
    }
    /**
     * @param {'preview'|'modal'} kind
     * @param {string} url
     * @param {{ mouseX?: number }} [opts]
     * @returns {{ width: number, height: number, left: number, top: number }}
     */
    _computePopoverWindowBounds(kind, url, opts = {}) {
      const availW = Math.max(320, Number(window.screen?.availWidth) || window.innerWidth || 1200);
      const availH = Math.max(240, Number(window.screen?.availHeight) || window.innerHeight || 800);
      const screenLeft = Number(window.screen?.availLeft) || 0;
      const screenTop = Number(window.screen?.availTop) || 0;
      const margin = 20;
      let width;
      let height;
      if (kind === "modal") {
        const pt = 40 * (96 / 72);
        width = Math.max(480, Math.min(availW - margin * 2, (window.innerWidth || availW) - pt));
        height = Math.max(360, Math.min(availH - margin * 2, (window.innerHeight || availH) - pt));
      } else {
        width = 600;
        height = Math.max(200, availH - margin * 2);
      }
      const mouseX = Number.isFinite(opts.mouseX) ? opts.mouseX : (window.innerWidth || availW) / 2;
      let left = screenLeft + Math.round((window.screenX || 0) + mouseX - width / 2);
      left = Math.max(screenLeft + margin, Math.min(left, screenLeft + availW - width - margin));
      const top = screenTop + margin;
      return { width: Math.round(width), height: Math.round(height), left, top };
    }
    /**
     * Open a sized OS popup window for Link Preview / Open Popover.
     * @param {object} opts
     * @param {string} opts.url
     * @param {'preview'|'modal'} [opts.kind='preview']
     * @param {string[]} [opts.closeKeys]
     * @param {number} [opts.mouseX]
     * @param {number} [opts.width]
     * @param {number} [opts.height]
     * @param {number} [opts.left]
     * @param {number} [opts.top]
     * @returns {Promise<boolean>}
     */
    async _openPopoverWindow(opts = {}) {
      this._ensurePopoverWindowMessageListener();
      const url = String(opts.url || "").trim();
      if (!url) return false;
      const kind = opts.kind === "modal" ? "modal" : "preview";
      const closeKeys = Array.isArray(opts.closeKeys) && opts.closeKeys.length ? opts.closeKeys.map(String) : kind === "modal" ? ["Escape", "p", "P"] : ["Escape", "e", "E"];
      const bounds = this._computePopoverWindowBounds(kind, url, { mouseX: opts.mouseX });
      const width = typeof opts.width === "number" ? opts.width : bounds.width;
      const height = typeof opts.height === "number" ? opts.height : bounds.height;
      const left = typeof opts.left === "number" ? opts.left : bounds.left;
      const top = typeof opts.top === "number" ? opts.top : bounds.top;
      try {
        this._popoverWindowId = null;
        this._popoverWindowTabId = null;
        this._popoverWindowUrl = null;
        this._popoverWindowKind = null;
        const res = await chrome.runtime.sendMessage({
          type: MSG.OPEN_POPOVER_WINDOW,
          url,
          kind,
          closeKeys,
          width,
          height,
          left,
          top
        });
        if (res?.type === MSG.ERROR || typeof res?.windowId !== "number") {
          console.warn("[KeyPilot] Failed to open popover window:", res?.error || res);
          try {
            window.__KeyPilotInstance?.state?.setPopoverOpen?.(false, null);
          } catch {
          }
          return false;
        }
        this._popoverWindowId = res.windowId;
        this._popoverWindowTabId = typeof res.tabId === "number" ? res.tabId : null;
        this._popoverWindowUrl = url;
        this._popoverWindowKind = kind;
        return true;
      } catch (e) {
        console.warn("[KeyPilot] OPEN_POPOVER_WINDOW failed:", e?.message || e);
        try {
          window.__KeyPilotInstance?.state?.setPopoverOpen?.(false, null);
        } catch {
        }
        return false;
      }
    }
    async _closeTrackedPopoverWindow() {
      const windowId = this._popoverWindowId;
      this._popoverWindowId = null;
      this._popoverWindowTabId = null;
      this._popoverWindowUrl = null;
      this._popoverWindowKind = null;
      if (typeof windowId !== "number") return;
      try {
        await chrome.runtime.sendMessage({
          type: MSG.CLOSE_POPOVER_WINDOW,
          windowId,
          // Opener-initiated close: SW still notifies, but we already cleared local ids.
          notifyOpener: false,
          reason: "opener_hide"
        });
      } catch {
      }
    }
    /**
     * Apply the Docs titlebar text-size slider to the in-page Docs host.
     * @param {number} scale
     */
    setDocsFontScale(scale) {
      const n = Number(scale);
      if (!Number.isFinite(n) || n < 0.8 || n > 1.75) return;
      this._docsFontScale = n;
      try {
        this._docsHost?.style?.setProperty("--docs-font-scale", String(n));
        const app = this._docsHost?.shadowRoot?.querySelector?.(".docs-app");
        app?.style?.setProperty("--docs-font-scale", String(n));
      } catch {
      }
    }
    /**
     * Docs popover without an iframe: same modal chrome as Settings/Guide, but the
     * article/nav live in an open shadow tree so KeyPilot hover, F-click, and
     * Text Mode work like Tab History / Launcher.
     * @param {object} opts
     * @param {string} [opts.title]
     * @param {string} [opts.hintKeyLabel]
     * @param {string} [opts.width]
     * @param {string} [opts.height]
     * @param {HTMLElement|HTMLElement[]|null} [opts.actions]
     * @param {string} [opts.topicId]
     * @param {string} [opts.hash]
     * @param {(target: object) => void} [opts.onNavigateDeepLink]
     */
    async showInPageDocsPopover(opts = {}) {
      let docsUi;
      try {
        docsUi = await loadDocsUi();
      } catch (err) {
        console.warn("[KeyPilot] Failed to load Docs UI bundle:", err);
        return;
      }
      this._docsUi = docsUi;
      this.hidePopover();
      const requestClosePopover = () => {
        try {
          if (window.__KeyPilotInstance && typeof window.__KeyPilotInstance.handleClosePopover === "function") {
            window.__KeyPilotInstance.handleClosePopover();
            return;
          }
        } catch {
        }
        this.hidePopover();
      };
      this.popoverContainer = this.createElement("div", {
        className: "kpv2-popover-container kpv2-docs-popover kp-chrome-window",
        tabindex: "-1",
        role: "dialog",
        "aria-modal": "true",
        style: `
        position: fixed;
        inset: 0;
        width: ${opts.width || "calc(100vw - 40pt)"};
        height: ${opts.height || "calc(100vh - 40pt)"};
        max-width: calc(100vw - 40pt);
        max-height: calc(100vh - 40pt);
        margin: auto;
        background: ${NCT_DARK_UI_PANEL_BACKGROUND};
        border-radius: ${NCT_DARK_UI_PANEL_RADIUS};
        border: ${NCT_DARK_UI_PANEL_BORDER};
        box-shadow: ${NCT_DARK_UI_PANEL_BOX_SHADOW};
        display: flex;
        flex-direction: column;
        overflow: hidden;
        font-family: ${KP_UI_FONT};
        font-size: 14px;
        line-height: 1.3;
        letter-spacing: normal;
      `
      });
      markChromeWindow(this.popoverContainer);
      const chromeHost = this.createElement("div", {
        className: "kpv2-popover-chrome-host",
        style: `
        display: flex;
        flex: 0 0 auto;
        flex-direction: column;
        min-height: 0;
      `
      });
      const chromeShadow = ensureOpenChromeShadow(chromeHost, { id: "docs-popover" });
      const chromeMount = chromeShadow || chromeHost;
      const titlebarApi = createPopoverTitlebar({
        title: opts.title && String(opts.title).trim() || "KeyPilot Docs",
        shortcut: opts.hintKeyLabel || "Alt + H",
        icon: "window",
        variant: "modal",
        showClose: true,
        onClose: requestClosePopover,
        closeTitle: "Close (Esc)",
        hint: createTitlebarCloseHint({
          keys: [opts.hintKeyLabel || "Alt+H", "Esc"],
          suffix: "Use the same keyboard navigation controls."
        }),
        className: "kpv2-popover-titlebar",
        actions: opts.actions || null
      });
      const header = titlebarApi.titlebar;
      this.popoverCloseButton = titlebarApi.closeButton;
      chromeMount.appendChild(header);
      this.popoverContainer.appendChild(chromeHost);
      const bodyHost = this.createElement("div", {
        className: "kpv2-docs-host",
        style: `
        flex: 1 1 auto;
        min-height: 0;
        display: flex;
        flex-direction: column;
        background: var(--kp-color-bg, #0f0f10);
      `
      });
      const shadow = ensureOpenChromeShadow(bodyHost, { id: "docs-app" }) || bodyHost.shadowRoot;
      try {
        const link = document.createElement("link");
        link.rel = "stylesheet";
        link.href = chrome.runtime.getURL("pages/docs.css");
        shadow.appendChild(link);
      } catch {
      }
      this._docsHost = bodyHost;
      this._inPagePopoverKind = "docs";
      this._docsUnmount = docsUi.mountDocsApp(shadow, {
        embedded: true,
        onClose: requestClosePopover,
        fontScale: this._docsFontScale,
        initialTopic: opts.topicId || null,
        initialHash: opts.hash || null,
        onNavigateDeepLink: typeof opts.onNavigateDeepLink === "function" ? opts.onNavigateDeepLink : null
      });
      this.setDocsFontScale(this._docsFontScale);
      this.popoverContainer.appendChild(bodyHost);
      this.popupManager?.showModal?.({
        id: this._popoverPopupId,
        panel: this.popoverContainer,
        onRequestClose: requestClosePopover
      });
      document.body.style.overflow = "hidden";
      const handlePopoverKeyDown = (e) => {
        if (e.key !== "Escape") return;
        try {
          const kp = window.__KeyPilotInstance;
          const st = kp?.state?.getState?.();
          if (st?.mode === MODES.TEXT_FOCUS || st?.focusedTextElement) {
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();
            kp.handleEscapeFromTextFocus(st);
            return;
          }
        } catch {
        }
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        requestClosePopover();
      };
      document.addEventListener("keydown", handlePopoverKeyDown, true);
      this.popoverContainer.addEventListener("keydown", handlePopoverKeyDown, true);
      this.popoverKeyHandler = handlePopoverKeyDown;
      try {
        this.popoverContainer.focus();
      } catch {
      }
    }
    /**
     * Navigate the open in-page Docs popover to a topic.
     * @param {string} topicId
     * @param {string} [hash]
     * @returns {boolean}
     */
    setDocsTopic(topicId, hash) {
      if (this._inPagePopoverKind !== "docs") return false;
      return this._docsUi?.navigateDocsApp?.(topicId, hash) || false;
    }
    /**
     * Settings popover without an iframe — same chrome as Docs.
     * @param {object} opts
     * @param {string} [opts.panelId]
     */
    async showInPageSettingsPopover(opts = {}) {
      let settingsUi;
      try {
        settingsUi = await loadSettingsUi();
      } catch (err) {
        console.warn("[KeyPilot] Failed to load Settings UI bundle:", err);
        return;
      }
      this._settingsUi = settingsUi;
      this.hidePopover();
      const requestClosePopover = () => {
        try {
          if (window.__KeyPilotInstance && typeof window.__KeyPilotInstance.handleClosePopover === "function") {
            window.__KeyPilotInstance.handleClosePopover();
            return;
          }
        } catch {
        }
        this.hidePopover();
      };
      this.popoverContainer = this.createElement("div", {
        className: "kpv2-popover-container kpv2-settings-popover kp-chrome-window",
        tabindex: "-1",
        role: "dialog",
        "aria-modal": "true",
        style: `
        position: fixed;
        inset: 0;
        width: ${opts.width || "calc(100vw - 40pt)"};
        height: ${opts.height || "calc(100vh - 40pt)"};
        max-width: calc(100vw - 40pt);
        max-height: calc(100vh - 40pt);
        margin: auto;
        background: ${NCT_DARK_UI_PANEL_BACKGROUND};
        border-radius: ${NCT_DARK_UI_PANEL_RADIUS};
        border: ${NCT_DARK_UI_PANEL_BORDER};
        box-shadow: ${NCT_DARK_UI_PANEL_BOX_SHADOW};
        display: flex;
        flex-direction: column;
        overflow: hidden;
        font-family: ${KP_UI_FONT};
        font-size: 14px;
        line-height: 1.3;
        letter-spacing: normal;
      `
      });
      markChromeWindow(this.popoverContainer);
      const chromeHost = this.createElement("div", {
        className: "kpv2-popover-chrome-host",
        style: `
        display: flex;
        flex: 0 0 auto;
        flex-direction: column;
        min-height: 0;
      `
      });
      const chromeShadow = ensureOpenChromeShadow(chromeHost, { id: "settings-popover" });
      const chromeMount = chromeShadow || chromeHost;
      const titlebarApi = createPopoverTitlebar({
        title: opts.title && String(opts.title).trim() || "KeyPilot Settings",
        shortcut: opts.hintKeyLabel || "'",
        icon: "gear",
        variant: "modal",
        showClose: true,
        onClose: requestClosePopover,
        closeTitle: "Close (Esc)",
        hint: createTitlebarCloseHint({
          keys: [opts.hintKeyLabel || "'", "Esc"],
          suffix: "Use the same keyboard navigation controls."
        }),
        className: "kpv2-popover-titlebar",
        actions: opts.actions || null
      });
      this.popoverCloseButton = titlebarApi.closeButton;
      chromeMount.appendChild(titlebarApi.titlebar);
      this.popoverContainer.appendChild(chromeHost);
      const bodyHost = this.createElement("div", {
        className: "kpv2-settings-host",
        style: `
        flex: 1 1 auto;
        min-height: 0;
        display: flex;
        flex-direction: column;
        background: var(--kp-color-bg, #0f0f10);
      `
      });
      const shadow = ensureOpenChromeShadow(bodyHost, { id: "settings-app" }) || bodyHost.shadowRoot;
      this._docsHost = null;
      this._inPagePopoverKind = "settings";
      this._docsUnmount = await settingsUi.mountSettingsApp(shadow, {
        embedded: true,
        onClose: requestClosePopover,
        initialPanel: opts.panelId || null
      });
      this.popoverContainer.appendChild(bodyHost);
      this.popupManager?.showModal?.({
        id: this._popoverPopupId,
        panel: this.popoverContainer,
        onRequestClose: requestClosePopover
      });
      document.body.style.overflow = "hidden";
      const handlePopoverKeyDown = (e) => {
        if (e.key !== "Escape") return;
        try {
          const kp = window.__KeyPilotInstance;
          const st = kp?.state?.getState?.();
          if (st?.mode === MODES.TEXT_FOCUS || st?.focusedTextElement) {
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();
            kp.handleEscapeFromTextFocus(st);
            return;
          }
        } catch {
        }
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        requestClosePopover();
      };
      document.addEventListener("keydown", handlePopoverKeyDown, true);
      this.popoverContainer.addEventListener("keydown", handlePopoverKeyDown, true);
      this.popoverKeyHandler = handlePopoverKeyDown;
      try {
        this.popoverContainer.focus();
      } catch {
      }
    }
    /**
     * Switch the active panel on the open in-page Settings popover.
     * @param {string} panelId
     * @returns {boolean}
     */
    setSettingsPanel(panelId) {
      if (this._inPagePopoverKind !== "settings") return false;
      return this._settingsUi?.setActiveSettingsPanel?.(panelId) || false;
    }
    /**
     * Show popover with iframe (extension Guide/settings pages) or OS window (http(s)).
     * http(s) Open Popover always opens a sized OS popup. Settings/Guide keep the
     * in-page iframe overlay with {@link createPopoverTitlebar}.
     *
     * @param {string} url - The URL to load in the popover
     * @param {object} [opts]
     * @param {string} [opts.title] - Optional title for the titlebar (defaults to url)
     * @param {string} [opts.hintKeyLabel] - Optional key label in the titlebar hint (defaults to 'P')
     * @param {boolean} [opts.showClose=true] - Whether to show the titlebar close button
     * @param {string|Node|null} [opts.titlebarHint] - Override titlebar hint (string or Node)
     * @param {HTMLElement|HTMLElement[]|null} [opts.actions] - Controls placed before the close button
     * @param {string[]} [opts.closeKeys] - Keys forwarded from iframe that should request close (defaults to ['Escape','p','P'])
     * @param {string} [opts.width] - Optional fixed width (e.g., '920px', overrides viewport-minus-20pt)
     * @param {string} [opts.height] - Optional fixed height (e.g., '600px', overrides viewport-minus-20pt)
     */
    showPopover(url, opts = {}) {
      this.hidePopover();
      const closeKeys = Array.isArray(opts?.closeKeys) && opts.closeKeys.length ? opts.closeKeys.map(String) : ["Escape", "p", "P"];
      if (isHttpPopoverUrl(url)) {
        void this._openPopoverWindow({
          url: preferHttpsForPreview(url),
          kind: "modal",
          closeKeys
        });
        return;
      }
      const titleText = opts && typeof opts.title === "string" && opts.title.trim() ? opts.title.trim() : String(url || "");
      const hintKeyLabel = opts && typeof opts.hintKeyLabel === "string" && opts.hintKeyLabel.trim() ? opts.hintKeyLabel.trim() : "P";
      const requestClosePopover = () => {
        try {
          if (window.__KeyPilotInstance && typeof window.__KeyPilotInstance.handleClosePopover === "function") {
            window.__KeyPilotInstance.handleClosePopover();
            return;
          }
        } catch (_e) {
        }
        this.hidePopover();
      };
      const ensureTopMouseTracking = () => {
        if (this._popoverMouseTrackerInstalled) return;
        this._popoverMouseTrackerInstalled = true;
        const update = (e) => {
          try {
            if (!e) return;
            if (typeof e.clientX === "number") this._popoverLastMouse.x = e.clientX;
            if (typeof e.clientY === "number") this._popoverLastMouse.y = e.clientY;
          } catch {
          }
        };
        try {
          document.addEventListener("mousemove", update, true);
        } catch {
        }
        try {
          document.addEventListener("pointermove", update, true);
        } catch {
        }
      };
      const clickCloseIfHovered = () => {
        try {
          const btn = this.popoverCloseButton;
          if (!btn) return false;
          const x = this._popoverLastMouse.x;
          const y = this._popoverLastMouse.y;
          if (typeof x !== "number" || typeof y !== "number") return false;
          const el2 = document.elementFromPoint(x, y);
          const shadowEl = btn.getRootNode?.()?.elementFromPoint?.(x, y);
          if (el2 === btn || btn.contains(el2) || shadowEl === btn || btn.contains(shadowEl)) {
            try {
              btn.click();
            } catch {
            }
            return true;
          }
          return false;
        } catch {
          return false;
        }
      };
      this.popoverContainer = this.createElement("div", {
        className: "kpv2-popover-container kp-chrome-window",
        tabindex: "-1",
        role: "dialog",
        "aria-modal": "true",
        style: `
        position: fixed;
        inset: 0;                  /* top: 0; left: 0; bottom: 0; right: 0; */
        width: ${opts.width || "calc(100vw - 40pt)"};
        height: ${opts.height || "calc(100vh - 40pt)"};
        max-width: calc(100vw - 40pt);
        max-height: calc(100vh - 40pt);
        margin: auto;              /* this is what centers it perfectly */
        background: ${NCT_DARK_UI_PANEL_BACKGROUND};
        border-radius: ${NCT_DARK_UI_PANEL_RADIUS};
        border: ${NCT_DARK_UI_PANEL_BORDER};
        box-shadow: ${NCT_DARK_UI_PANEL_BOX_SHADOW};
        display: flex;
        flex-direction: column;
        overflow: hidden;
        font-family: ${KP_UI_FONT};
        font-size: 14px;
        line-height: 1.3;
        letter-spacing: normal;
      `
      });
      markChromeWindow(this.popoverContainer);
      const chromeHost = this.createElement("div", {
        className: "kpv2-popover-chrome-host",
        style: `
        display: flex;
        flex: 0 0 auto;
        flex-direction: column;
        min-height: 0;
      `
      });
      const chromeShadow = ensureOpenChromeShadow(chromeHost, { id: "iframe-popover" });
      const chromeMount = chromeShadow || chromeHost;
      let iframeRef = null;
      this.popoverBridgeReady = false;
      const showClose = opts?.showClose !== false;
      const titlebarHint = opts?.titlebarHint !== void 0 ? opts.titlebarHint : createTitlebarCloseHint({
        keys: [hintKeyLabel, "Esc"],
        suffix: "Use the same keyboard navigation controls."
      });
      const titlebarApi = createPopoverTitlebar({
        title: titleText,
        shortcut: hintKeyLabel || null,
        icon: "window",
        variant: "modal",
        showClose,
        onClose: requestClosePopover,
        closeTitle: "Close (Esc)",
        hint: titlebarHint,
        className: "kpv2-popover-titlebar",
        actions: opts?.actions || null
      });
      const header = titlebarApi.titlebar;
      const closeButton = titlebarApi.closeButton;
      this.popoverCloseButton = closeButton;
      ensureTopMouseTracking();
      const errorContainer = this.createElement("div", {
        className: "kpv2-popover-error",
        style: `
        flex: 1;
        display: none;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        padding: 40px;
        text-align: center;
        background: #f9f9f9;
      `
      });
      const errorIcon = this.createElement("div", {
        style: `
        font-size: 48px;
        margin-bottom: 16px;
        color: #999;
      `
      });
      errorIcon.textContent = "\u{1F6AB}";
      errorContainer.appendChild(errorIcon);
      const errorTitle = this.createElement("div", {
        style: `
        font-size: 18px;
        font-weight: 600;
        color: #333;
        margin-bottom: 8px;
      `
      });
      errorTitle.textContent = "Cannot Display Page";
      errorContainer.appendChild(errorTitle);
      const errorMessage = this.createElement("div", {
        style: `
        font-size: 14px;
        color: #666;
        margin-bottom: 24px;
        max-width: 400px;
      `
      });
      errorMessage.textContent = "This website prevents embedding in iframes for security reasons.";
      errorContainer.appendChild(errorMessage);
      const openInTabButton = this.createElement("button", {
        style: `
        background: #4CAF50;
        color: white;
        border: none;
        padding: 10px 20px;
        border-radius: 4px;
        cursor: pointer;
        font-size: 14px;
        font-weight: 500;
      `
      });
      openInTabButton.textContent = "Open in New Tab";
      openInTabButton.onclick = () => {
        window.open(url, "_blank");
        requestClosePopover();
      };
      errorContainer.appendChild(openInTabButton);
      const iframeStyle = `
        flex: 1;
        border: none;
        width: 100%;
        height: 100%;
      `;
      const iframe = this.createElement("iframe", {
        src: url,
        tabindex: "0",
        style: iframeStyle
      });
      iframeRef = iframe;
      this.popoverIframeElement = iframe;
      this.popoverIframeWindow = iframe.contentWindow || null;
      const sendBridgeInit = () => {
        postPopoverBridgeInit(iframe.contentWindow, { closeKeys });
      };
      const showLoadError = () => {
        iframe.style.display = "none";
        chromeHost.style.flex = "1 1 auto";
        errorContainer.style.display = "flex";
      };
      iframe.onerror = () => {
        console.log("[KeyPilot] Iframe load error detected");
        showLoadError();
      };
      let loadTimeout = null;
      const armLoadTimeout = () => {
        if (loadTimeout) {
          try {
            clearTimeout(loadTimeout);
          } catch {
          }
        }
        loadTimeout = setTimeout(() => {
          console.log("[KeyPilot] Iframe load timeout - showing error as fallback");
          showLoadError();
        }, 3e4);
      };
      iframe.onload = () => {
        try {
          const srcAttr = iframe.getAttribute("src") || "";
          if (srcAttr === "about:blank" || iframe.src === "about:blank") {
            return;
          }
        } catch {
        }
        try {
          clearTimeout(loadTimeout);
        } catch {
        }
        loadTimeout = null;
        console.log("[KeyPilot] Iframe loaded successfully");
        sendBridgeInit();
        try {
          const childDoc = iframe.contentDocument;
          if (childDoc) {
            window.__KeyPilotInstance?.styleManager?.injectIntoForeignDocument?.(childDoc);
          }
        } catch {
        }
      };
      chromeMount.appendChild(header);
      chromeMount.appendChild(errorContainer);
      this.popoverContainer.appendChild(chromeHost);
      this.popoverContainer.appendChild(iframe);
      this.popupManager?.showModal?.({
        id: this._popoverPopupId,
        panel: this.popoverContainer,
        onRequestClose: requestClosePopover
      });
      armLoadTimeout();
      sendBridgeInit();
      try {
        let attemptsLeft = 6;
        this.popoverInitTimer = setInterval(() => {
          if (!this.popoverContainer || attemptsLeft <= 0) {
            clearInterval(this.popoverInitTimer);
            this.popoverInitTimer = null;
            return;
          }
          attemptsLeft -= 1;
          sendBridgeInit();
        }, 250);
      } catch {
      }
      document.body.style.overflow = "hidden";
      const handlePopoverKeyDown = (e) => {
        console.log("[KeyPilot] Popover key event:", e.key, "Target:", e.target, "Active element:", document.activeElement);
        if (e.key === "Escape") {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          requestClosePopover();
          return;
        }
      };
      document.addEventListener("keydown", handlePopoverKeyDown, true);
      this.popoverContainer.addEventListener("keydown", handlePopoverKeyDown, true);
      this.popoverKeyHandler = handlePopoverKeyDown;
      this.popoverMessageHandler = (event) => {
        const data = event?.data;
        if (!data || typeof data.type !== "string") return;
        if (this.popoverIframeWindow && event.source !== this.popoverIframeWindow) return;
        if (data.type === MSG.POPOVER_BRIDGE_READY) {
          this.popoverBridgeReady = true;
          this._focusPopoverIframe(iframeRef);
          this._installPopoverHybridFocus({
            iframe: iframeRef,
            chromeEls: [header, ...titlebarApi.getInteractiveElements()].filter(Boolean),
            focusChromeEl: closeButton || header
          });
          return;
        }
        if (data.type === MSG.POPOVER_REQUEST_CLOSE) {
          if (closeKeys.includes(String(data.key))) requestClosePopover();
        }
        if (data.type === MSG.POPOVER_LAUNCH_WALKTHROUGH) {
          requestClosePopover();
          try {
            const ob = window.__KeyPilotOnboarding;
            if (ob && typeof ob.resetTutorial === "function") {
              void ob.resetTutorial();
            }
          } catch {
          }
          return;
        }
        if (data.type === MSG.POPOVER_BRIDGE_KEYDOWN) {
          const k = String(data.key || "");
          if (k === "f" || k === "F") {
            if (clickCloseIfHovered()) return;
            try {
              window.__KeyPilotInstance?.handleActivateKey?.();
            } catch {
            }
          }
        }
      };
      window.addEventListener("message", this.popoverMessageHandler, true);
      try {
        (closeButton || header)?.focus?.();
      } catch (_e) {
        try {
          this.popoverContainer.focus();
        } catch (_e2) {
        }
      }
    }
    /**
     * Move focus out of the popover (esp. its iframe) before the node is removed.
     * Otherwise Chrome often hands focus to the browser omnibox, which steals keys
     * from the page (notably on New Tab after a second E closes Link Preview).
     */
    _restoreFocusFromPopover() {
      const iframe = this.popoverIframeElement;
      const container = this.popoverContainer;
      if (!iframe && !container) return;
      let active2 = null;
      try {
        active2 = document.activeElement;
      } catch {
      }
      const focusInPopover = !!(iframe && active2 === iframe) || !!(container && active2 instanceof Node && container.contains(active2));
      try {
        iframe?.blur?.();
      } catch {
      }
      if (focusInPopover) {
        try {
          active2?.blur?.();
        } catch {
        }
      }
      try {
        window.focus();
      } catch {
      }
      try {
        const body = document.body;
        if (body) {
          if (!body.hasAttribute("tabindex")) {
            body.setAttribute("tabindex", "-1");
          }
          body.focus({ preventScroll: true });
        }
      } catch {
      }
    }
    hidePopover(opts = {}) {
      const closeWindow = opts.closeWindow !== false;
      if (closeWindow) {
        void this._closeTrackedPopoverWindow();
      }
      try {
        this._restoreFocusFromPopover();
      } catch {
      }
      if (this.popoverInitTimer) {
        try {
          clearInterval(this.popoverInitTimer);
        } catch {
        }
        this.popoverInitTimer = null;
      }
      if (this.popoverMessageHandler) {
        try {
          window.removeEventListener("message", this.popoverMessageHandler, true);
        } catch {
        }
        this.popoverMessageHandler = null;
      }
      this.popoverIframeWindow = null;
      this.popoverIframeElement = null;
      this.popoverBridgeReady = false;
      this.popoverCloseButton = null;
      if (this._docsUnmount) {
        try {
          this._docsUnmount();
        } catch {
        }
        this._docsUnmount = null;
      }
      this._docsHost = null;
      this._inPagePopoverKind = null;
      if (this.popoverKeyHandler) {
        document.removeEventListener("keydown", this.popoverKeyHandler, true);
        if (this.popoverContainer) {
          this.popoverContainer.removeEventListener("keydown", this.popoverKeyHandler, true);
        }
        this.popoverKeyHandler = null;
      }
      if (this._popoverHybridFocusCleanup) {
        try {
          this._popoverHybridFocusCleanup();
        } catch {
        }
        this._popoverHybridFocusCleanup = null;
      }
      if (this.popoverContainer) {
        try {
          this.popupManager?.hideModal?.(this._popoverPopupId);
        } catch {
          try {
            this.popoverContainer.remove();
          } catch {
          }
        }
        this.popoverContainer = null;
      }
      try {
        window.focus();
        if (document.body && document.activeElement !== document.body) {
          document.body.focus({ preventScroll: true });
        }
      } catch {
      }
      document.body.style.overflow = "";
    }
    /**
     * Post a message to the popover iframe bridge (if present).
     * @param {any} message
     * @returns {boolean} Whether a postMessage was attempted successfully
     */
    postMessageToPopoverIframe(message) {
      const win = this.popoverIframeWindow;
      if (!win) return false;
      try {
        win.postMessage(message, "*");
        return true;
      } catch {
        return false;
      }
    }
    scrollPopoverBy(deltaY, behavior = "smooth") {
      return this.postMessageToPopoverIframe({
        type: MSG.POPOVER_SCROLL,
        command: "scrollBy",
        delta: deltaY,
        behavior
      });
    }
    scrollPopoverToTop(behavior = "smooth") {
      return this.postMessageToPopoverIframe({
        type: MSG.POPOVER_SCROLL,
        command: "scrollToTop",
        behavior
      });
    }
    scrollPopoverToBottom(behavior = "smooth") {
      return this.postMessageToPopoverIframe({
        type: MSG.POPOVER_SCROLL,
        command: "scrollToBottom",
        behavior
      });
    }
    /**
     * Check if popover is currently open (overlay iframe or OS popup window).
     * @returns {boolean}
     */
    isPopoverOpen() {
      return this.popoverContainer !== null || this._popoverWindowId != null;
    }
    /**
     * Show Link Preview in a sized OS popup window (windows-only; no in-page iframe).
     * @param {string} url - URL to preview
     * @param {Object} opts - Options including mouseX for window placement
     * @param {string[]} [opts.closeKeys]
     * @param {number} [opts.mouseX]
     */
    async showPreviewPopover(url, opts = {}) {
      this.hidePopover();
      url = preferHttpsForPreview(url);
      await this._openPopoverWindow({
        url,
        kind: "preview",
        closeKeys: Array.isArray(opts?.closeKeys) && opts.closeKeys.length ? opts.closeKeys.map(String) : ["Escape", "e", "E"],
        mouseX: opts.mouseX
      });
    }
  };

  // src/modules/focus-overlay.js
  init_constants();
  init_settings_manager();
  var FOCUS_OVERLAY_METHOD_NAMES = Object.freeze(
    [
      "setDomHoverFocusColorsEnabled",
      "usesElementFocusStyling",
      "setScrollPaintPreferA",
      "_getNonTextFocusPalette",
      "getFocusCategory",
      "shouldSuppressFocusFill",
      "isVideoLikeElement",
      "initCanvasRenderer",
      "cleanupCanvasRenderer",
      "updateFocusOverlayCanvas",
      "hideFocusOverlayCanvas",
      "initCSSCustomPropsRenderer",
      "cleanupCSSCustomPropsRenderer",
      "updateFocusOverlayCSSCustomProps",
      "hideFocusOverlayCSSCustomProps",
      "setModeSettings",
      "_getClickModeSettings",
      "_settingsPaintOverride",
      "_effectivePaintOverride",
      "_getTextModeSettings",
      "setHoverClickLabelText",
      "formatHoverLabelText",
      "formatActiveTextFieldLabel",
      "ensureTextModeLabels",
      "updateTextModeLabels",
      "hideTextModeLabels",
      "setTextFocusEscKeyLabel",
      "setTextHoverActivateKeyLabel",
      "_escapeHintText",
      "_renderTextFocusEscHint",
      "_renderTextHoverActivateHint",
      "ensureTextFocusEscHint",
      "ensureTextHoverActivateHint",
      "_overlayZIndexAboveTarget",
      "_positionTextFieldSidecar",
      "updateTextFocusEscHint",
      "hideTextFocusEscHint",
      "updateTextHoverActivateHint",
      "hideTextHoverActivateHint",
      "refreshTextHoverActivateHint",
      "_clearTextFocusElementStyling",
      "_getNearbyInputWrappers",
      "_resolveTextFocusPaintHost",
      "_isTightTextFieldWrapper",
      "_isHoverOnActiveTextField",
      "_ensureStylesForElement",
      "_applyTextFocusElementStyling",
      "_shouldHideTextFocusHint",
      "_syncTextFocusHintHidden",
      "_clearTextHoverElementStyling",
      "_applyTextHoverElementStyling",
      "updateFocusOverlay",
      "_installFocusClipCacheInvalidation",
      "_intersectViewportRects",
      "_viewportRectsOverlap",
      "_clipViewportRectToVisible",
      "_findFocusClipContext",
      "_isProbablyClippedByAncestorOverflow",
      "_outerFocusRingWouldBeClipped",
      "_styleClipsOverflow",
      "_styleClipsSelfFor",
      "_styleClipsPaintContain",
      "_styleClipsClipPath",
      "_styleClipsSelf",
      "_composedParent",
      "_shadowInternalClipWrappers",
      "_hostClipsViaInternalShadowWrapper",
      "_isInShadowTree",
      "_getOpenShadowRoot",
      "_shadowHasDefaultSlot",
      "_canMountVisibleChildOnHost",
      "_enqueueShadowPiercingChildren",
      "_findInShadowInTargetMount",
      "_preferredFocusOutlineOffsetPx",
      "_resolveAbsoluteClipFramePaintHost",
      "_minFocusOutlineRoomPx",
      "_computeGradedFocusOutlineOffset",
      "_wouldUseInsetFocusOutline",
      "_strategyAIsViable",
      "_shouldUseFixedFocusOverlay",
      "_isMediaTextSplitCard",
      "_mediaTextShellHasCompetingDest",
      "_focusFillsMediaTextCardShell",
      "_resolveMediaTextCardShell",
      "_isBlockishPaintHost",
      "_liftMediaTextCardPaintHost",
      "_isMediaLikeCoverElement",
      "_hasEdgeFlushMediaCover",
      "_hasDescendantMediaStrip",
      "_hasFullBleedCoveringContent",
      "_hasObscuringFullBleedChild",
      "_isReplacedOrVoidElement",
      "_ensureShadowRootRingHost",
      "_inTargetHostSizeOk",
      "_isViableInTargetHost",
      "_isInsideCssMultiColumn",
      "_findSameSizeInTargetDescendant",
      "_visualFocusRectForInTarget",
      "_resolveInTargetHost",
      "_findInShadowInTargetMountNear",
      "_maxLocalZIndex",
      "_ensureInTargetRingEl",
      "_restoreInTargetHostPosition",
      "hideInTargetFocusRing",
      "_positiveClientRectCount",
      "_visualContentUnionRect",
      "_isFragmentedInlineFocusTarget",
      "updateFocusOverlayInTarget",
      "updateFocusOverlayElementStyling",
      "_findLargestVisibleDescendant",
      "_findDominantReplacedPaintChild",
      "_resolveElementForFocusStyling",
      "_stripFocusStylingFromElement",
      "clearElementFocusStyling",
      "_deepStripFocusMarkers",
      "updateFocusOverlayDOM",
      "hideFocusOverlay",
      "hideFocusOverlayDOM",
      "setRenderingMode",
      "initRenderingMode",
      "cleanupRenderingMode",
      "fadeOutFocusOverlay"
    ]
  );
  var FocusOverlayPainter = class _FocusOverlayPainter {
    /** @param {import('./overlay-manager.js').OverlayManager} host */
    constructor(host) {
      this.host = host;
      return new Proxy(this, {
        get(target, prop, receiver) {
          if (prop === "host") return host;
          if (prop in _FocusOverlayPainter.prototype || Object.prototype.hasOwnProperty.call(target, prop)) {
            const val2 = Reflect.get(target, prop, receiver);
            return typeof val2 === "function" ? val2.bind(receiver) : val2;
          }
          const val = host[prop];
          return typeof val === "function" ? val.bind(host) : val;
        },
        set(_target, prop, value) {
          if (prop === "host") return true;
          host[prop] = value;
          return true;
        }
      });
    }
    setDomHoverFocusColorsEnabled(enabled) {
      this._useDomHoverFocusColors = !!enabled;
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] DOM hover focus colors enabled:", this._useDomHoverFocusColors);
      }
    }
    /**
     * True when focus chrome is painted on/with the target element (strategies
     * A or B) and therefore scrolls with the page. False when strategy C
     * (body-level fixed overlay) must be repositioned on scroll.
     * @returns {boolean}
     */
    usesElementFocusStyling() {
      return !!this._useDomHoverFocusColors && !this._focusPaintUsesFixedOverlay;
    }
    /**
     * Ask hover paint to use A for the rest of this scroll gesture (C rings
     * stay pinned to the last viewport rect).
     * @param {boolean} on
     */
    setScrollPaintPreferA(on) {
      const next = !!on;
      if (this._preferADuringScroll === next) return;
      this._preferADuringScroll = next;
      const el2 = this._lastFocusElement;
      if (el2 && el2.isConnected) {
        try {
          this.updateFocusOverlay(el2);
        } catch {
        }
      }
    }
    /**
     * Focus ring palette from click-mode settings (blue | green).
     * Falls back to blue when DOM-hover element styling is active and no setting is loaded.
     * @param {'blue'|'green'|string|null|undefined} [focusColor]
     */
    _getNonTextFocusPalette(focusColor) {
      const color4 = focusColor === "green" || focusColor === "blue" ? focusColor : this._getClickModeSettings().focusColor || "blue";
      if (color4 === "green") {
        return {
          borderColor: COLORS.FOCUS_GREEN,
          shadowColor: COLORS.GREEN_SHADOW,
          shadowBrightColor: COLORS.GREEN_SHADOW_BRIGHT,
          backgroundColor: COLORS.FOCUS_GREEN_BG_T2
        };
      }
      return {
        borderColor: COLORS.FOCUS_BLUE,
        shadowColor: COLORS.BLUE_SHADOW,
        shadowBrightColor: COLORS.BLUE_SHADOW_BRIGHT,
        backgroundColor: COLORS.FOCUS_BLUE_BG_T2
      };
    }
    /**
     * Resolve clickable category for focus chrome decisions.
     * @param {Element|null} element
     * @returns {string}
     */
    getFocusCategory(element) {
      try {
        const detector = window.keyPilot?.detector || window.keyPilot?.elementDetector;
        if (detector && typeof detector.getClickableCategory === "function") {
          return detector.getClickableCategory(element);
        }
      } catch {
      }
      return CLICKABLE_CATEGORY.NONE;
    }
    /**
     * Suppress the semi-transparent blue focus fill by category:
     * - slider: never (progress bars are not link-like)
     * - media with seek chrome in the player shell: no fill (players, not thumbnails)
     * - text: no blue wash
     *
     * Links / generic / media thumbnails keep the fill.
     *
     * @param {Element|null} element
     * @returns {boolean}
     */
    shouldSuppressFocusFill(element) {
      try {
        if (!element || !(element instanceof Element)) return false;
        const detector = window.keyPilot?.detector || window.keyPilot?.elementDetector;
        if (detector && typeof detector.shouldSuppressFocusFillForElement === "function") {
          return detector.shouldSuppressFocusFillForElement(element);
        }
        const cat = this.getFocusCategory(element);
        return cat === CLICKABLE_CATEGORY.SLIDER || cat === CLICKABLE_CATEGORY.TEXT;
      } catch {
        return false;
      }
    }
    /**
     * Media / slider surfaces for F-key scale-pulse skip (not link-style).
     * @param {Element|null} element
     * @returns {boolean}
     */
    isVideoLikeElement(element) {
      try {
        if (!element || !(element instanceof Element)) return false;
        const cat = this.getFocusCategory(element);
        if (cat === CLICKABLE_CATEGORY.SLIDER || cat === CLICKABLE_CATEGORY.MEDIA) return true;
        if (element.tagName === "VIDEO" || element.tagName === "AUDIO") return true;
        return !!(element.querySelector && element.querySelector("video"));
      } catch {
        return false;
      }
    }
    // Canvas-based rendering backend
    initCanvasRenderer() {
      if (this.canvasOverlay) return;
      this.canvasOverlay = document.createElement("canvas");
      this.canvasOverlay.className = CSS_CLASSES.CANVAS_OVERLAY || "kpv2-canvas-overlay";
      this.canvasOverlay.style.cssText = `
      position: fixed;
      top: 0;
      left: 0;
      width: 100vw;
      height: 100vh;
      pointer-events: none;
      z-index: ${Z_INDEX.OVERLAYS};
      will-change: transform;
    `;
      this.canvasOverlay.width = window.innerWidth;
      this.canvasOverlay.height = window.innerHeight;
      this.canvasContext = this.canvasOverlay.getContext("2d");
      document.body.appendChild(this.canvasOverlay);
      this._canvasResizeHandler = () => {
        this.canvasOverlay.width = window.innerWidth;
        this.canvasOverlay.height = window.innerHeight;
      };
      window.addEventListener("resize", this._canvasResizeHandler);
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Canvas renderer initialized");
      }
    }
    cleanupCanvasRenderer() {
      if (this.canvasOverlay) {
        if (this._canvasResizeHandler) {
          window.removeEventListener("resize", this._canvasResizeHandler);
          this._canvasResizeHandler = null;
        }
        this.canvasOverlay.remove();
        this.canvasOverlay = null;
        this.canvasContext = null;
      }
    }
    updateFocusOverlayCanvas(element, mode = MODES.NONE, rectOverride = null) {
      if (!this.canvasContext || !element) {
        this.hideFocusOverlayCanvas();
        return;
      }
      try {
        if (element.tagName === "IFRAME") {
          const isPopoverIframe = this.popoverIframeElement && element === this.popoverIframeElement;
          const isModalIframe = !!(element.classList && element.classList.contains("modal-iframe"));
          if (isPopoverIframe || isModalIframe) {
            this.hideFocusOverlayCanvas();
            return;
          }
        }
      } catch {
      }
      const rawRect = rectOverride && typeof rectOverride === "object" ? rectOverride : this.getBestRect(element);
      const rect = this._clipViewportRectToVisible(element, rawRect);
      if (!rect || rect.width <= 0 || rect.height <= 0) {
        this.hideFocusOverlayCanvas();
        return;
      }
      const isTextInput = element.matches && element.matches(SELECTORS.FOCUSABLE_TEXT);
      const suppressFill = this.shouldSuppressFocusFill(element);
      const isVeryLarge = rect.width > 512 && rect.height > 512;
      const clickSettings = this._getClickModeSettings();
      let borderColor, shadowColor, backgroundColor;
      if (isTextInput) {
        borderColor = COLORS.ORANGE;
        shadowColor = COLORS.ORANGE_SHADOW;
        backgroundColor = "transparent";
      } else {
        const p = this._getNonTextFocusPalette(clickSettings.focusColor);
        borderColor = p.borderColor;
        shadowColor = p.shadowColor;
        backgroundColor = suppressFill || isVeryLarge ? "transparent" : p.backgroundColor;
      }
      const { rectangleThickness, overlayFillEnabled, overlayShadowEnabled } = clickSettings;
      if (!isTextInput && !suppressFill && !isVeryLarge && overlayFillEnabled === false) {
        backgroundColor = "transparent";
      }
      this.canvasContext.clearRect(0, 0, this.canvasOverlay.width, this.canvasOverlay.height);
      if (backgroundColor !== "transparent") {
        this.canvasContext.fillStyle = backgroundColor;
        this.canvasContext.fillRect(rect.left, rect.top, rect.width, rect.height);
      }
      this.canvasContext.strokeStyle = borderColor;
      this.canvasContext.lineWidth = rectangleThickness;
      this.canvasContext.strokeRect(rect.left, rect.top, rect.width, rect.height);
      if (overlayShadowEnabled !== false) {
        this.canvasContext.shadowColor = shadowColor;
        this.canvasContext.shadowBlur = 4;
        this.canvasContext.strokeRect(rect.left, rect.top, rect.width, rect.height);
        this.canvasContext.shadowBlur = 0;
      }
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Canvas focus overlay updated:", {
          rect,
          borderColor,
          backgroundColor
        });
      }
    }
    hideFocusOverlayCanvas() {
      if (this.canvasContext) {
        this.canvasContext.clearRect(0, 0, this.canvasOverlay.width, this.canvasOverlay.height);
      }
    }
    // CSS Custom Properties rendering backend
    initCSSCustomPropsRenderer() {
      if (this.cssCustomPropsOverlay) return;
      this.cssCustomPropsOverlay = document.createElement("div");
      this.cssCustomPropsOverlay.className = CSS_CLASSES.CSS_PROPS_OVERLAY || "kpv2-css-props-overlay";
      const p = this._getNonTextFocusPalette();
      this.cssCustomPropsOverlay.style.cssText = `
      --rect-x: 0px;
      --rect-y: 0px;
      --rect-width: 0px;
      --rect-height: 0px;
      --rect-border-color: ${p.borderColor};
      --rect-background: transparent;
      --rect-shadow-color: ${p.shadowColor};
      --rect-shadow-bright-color: ${p.shadowBrightColor};
      --rect-border-thickness: 2px;

      position: fixed;
      left: var(--rect-x);
      top: var(--rect-y);
      width: var(--rect-width);
      height: var(--rect-height);
      border: var(--rect-border-thickness) solid var(--rect-border-color);
      background: var(--rect-background);
      box-shadow: 0 0 0 2px var(--rect-shadow-color), 0 0 10px 2px var(--rect-shadow-bright-color);
      pointer-events: none;
      z-index: ${Z_INDEX.OVERLAYS};
      will-change: transform;
    `;
      document.body.appendChild(this.cssCustomPropsOverlay);
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] CSS Custom Properties renderer initialized");
      }
    }
    cleanupCSSCustomPropsRenderer() {
      if (this.cssCustomPropsOverlay) {
        this.cssCustomPropsOverlay.remove();
        this.cssCustomPropsOverlay = null;
      }
    }
    updateFocusOverlayCSSCustomProps(element, mode = MODES.NONE, rectOverride = null) {
      if (!this.cssCustomPropsOverlay || !element) {
        this.hideFocusOverlayCSSCustomProps();
        return;
      }
      try {
        if (element.tagName === "IFRAME") {
          const isPopoverIframe = this.popoverIframeElement && element === this.popoverIframeElement;
          const isModalIframe = !!(element.classList && element.classList.contains("modal-iframe"));
          if (isPopoverIframe || isModalIframe) {
            this.hideFocusOverlayCSSCustomProps();
            return;
          }
        }
      } catch {
      }
      const rawRect = rectOverride && typeof rectOverride === "object" ? rectOverride : this.getBestRect(element);
      const rect = this._clipViewportRectToVisible(element, rawRect);
      if (!rect || rect.width <= 0 || rect.height <= 0) {
        this.hideFocusOverlayCSSCustomProps();
        return;
      }
      const isTextInput = element.matches && element.matches(SELECTORS.FOCUSABLE_TEXT);
      const suppressFill = this.shouldSuppressFocusFill(element);
      const isVeryLarge = rect.width > 512 && rect.height > 512;
      const clickSettings = this._getClickModeSettings();
      let borderColor, shadowColor, backgroundColor, shadowBrightColor;
      if (isTextInput) {
        borderColor = COLORS.ORANGE;
        shadowColor = COLORS.ORANGE_SHADOW;
        shadowBrightColor = COLORS.ORANGE_SHADOW;
        backgroundColor = "transparent";
      } else {
        const p = this._getNonTextFocusPalette(clickSettings.focusColor);
        borderColor = p.borderColor;
        shadowColor = p.shadowColor;
        shadowBrightColor = p.shadowBrightColor;
        backgroundColor = suppressFill || isVeryLarge ? "transparent" : p.backgroundColor;
      }
      const { rectangleThickness, overlayFillEnabled, overlayShadowEnabled } = clickSettings;
      if (!isTextInput && !suppressFill && !isVeryLarge && overlayFillEnabled === false) {
        backgroundColor = "transparent";
      }
      const overlay = this.cssCustomPropsOverlay;
      overlay.style.setProperty("--rect-x", rect.left + "px");
      overlay.style.setProperty("--rect-y", rect.top + "px");
      overlay.style.setProperty("--rect-width", rect.width + "px");
      overlay.style.setProperty("--rect-height", rect.height + "px");
      overlay.style.setProperty("--rect-border-color", borderColor);
      overlay.style.setProperty("--rect-background", backgroundColor);
      overlay.style.setProperty("--rect-shadow-color", shadowColor);
      overlay.style.setProperty("--rect-shadow-bright-color", shadowBrightColor);
      overlay.style.setProperty("--rect-border-thickness", rectangleThickness + "px");
      if (overlayShadowEnabled === false) {
        overlay.style.boxShadow = "none";
      } else {
        overlay.style.boxShadow = "0 0 0 2px var(--rect-shadow-color), 0 0 10px 2px var(--rect-shadow-bright-color)";
      }
      try {
        const radius = this._resolveElementBorderRadius(element);
        overlay.style.borderRadius = radius || "0";
        overlay.style.boxSizing = "border-box";
      } catch {
        overlay.style.borderRadius = "0";
      }
      overlay.style.display = "block";
      overlay.style.visibility = "visible";
      overlay.style.opacity = "1";
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] CSS Custom Props focus overlay updated:", {
          rect,
          borderColor,
          backgroundColor
        });
      }
    }
    hideFocusOverlayCSSCustomProps() {
      if (this.cssCustomPropsOverlay) {
        this.cssCustomPropsOverlay.style.setProperty("--rect-width", "0px");
        this.cssCustomPropsOverlay.style.setProperty("--rect-height", "0px");
        this.cssCustomPropsOverlay.style.display = "none";
      }
    }
    /**
     * Apply click/text mode settings from chrome.storage (via KeyPilot).
     * @param {object|null|undefined} settings
     */
    setModeSettings(settings) {
      const prevPaint = this._modeSettings?.clickMode && typeof this._modeSettings.clickMode === "object" ? this._modeSettings.clickMode.paintStrategy : void 0;
      const s = settings && typeof settings === "object" ? settings : {};
      this._modeSettings = {
        clickMode: s.clickMode && typeof s.clickMode === "object" ? s.clickMode : null,
        textMode: s.textMode && typeof s.textMode === "object" ? s.textMode : null
      };
      try {
        const focused = this._textFocusCurrentElement;
        if (focused && focused.isConnected) {
          this._textFocusCurrentElement = null;
          this._applyTextFocusElementStyling(focused);
        }
      } catch {
      }
      try {
        const nextPaint = this._getClickModeSettings().paintStrategy;
        if (prevPaint !== nextPaint && this._shadowDebugHudEnabled) {
          this._shadowDebugPaintOverride = this._settingsPaintOverride();
          this._refreshShadowDebugHudButtons();
        }
      } catch {
      }
      try {
        const focusEl = window.keyPilot?.state?.getState?.()?.focusEl || window.keyPilot?.intersectionManager?.getDomHoveredElement?.() || null;
        if (focusEl) this.updateFocusOverlay(focusEl);
      } catch {
      }
    }
    _getClickModeSettings() {
      const cm = this._modeSettings?.clickMode && typeof this._modeSettings.clickMode === "object" ? this._modeSettings.clickMode : {};
      const def = DEFAULT_SETTINGS.clickMode || {};
      const rectangleThickness = Number(cm.rectangleThickness);
      const thickness = Number.isFinite(rectangleThickness) ? Math.min(Math.max(rectangleThickness, 1), 16) : Number(def.rectangleThickness) || 3;
      const overlayFillEnabled = cm.overlayFillEnabled === true;
      const overlayShadowEnabled = cm.overlayShadowEnabled === true;
      const focusColor = cm.focusColor === "green" ? "green" : "blue";
      const rawEffect = cm.clickEffect;
      const clickEffect = rawEffect === "flash" || rawEffect === "dash" || rawEffect === "marquee" || rawEffect === "scale" || rawEffect === "none" ? rawEffect : def.clickEffect || "flash";
      const paintStrategy = cm.paintStrategy === "auto" || cm.paintStrategy === "BC" ? cm.paintStrategy : def.paintStrategy === "auto" ? "auto" : "BC";
      const paintBackendDebugDashes = cm.paintBackendDebugDashes === true;
      const padRaw = Number(cm.focusPadding);
      const focusPadding = Number.isFinite(padRaw) ? Math.min(Math.max(padRaw, 0), 16) : Math.min(Math.max(Number(def.focusPadding) || 2, 0), 16);
      return {
        rectangleThickness: thickness,
        overlayFillEnabled,
        overlayShadowEnabled,
        focusColor,
        clickEffect,
        paintStrategy,
        paintBackendDebugDashes,
        focusPadding
      };
    }
    /**
     * Settings → Advanced paint mode as a HUD-style override token.
     * @returns {'BC'|null} null = full Auto (A→B→C)
     */
    _settingsPaintOverride() {
      try {
        return this._getClickModeSettings().paintStrategy === "BC" ? "BC" : null;
      } catch {
        return DEFAULT_SETTINGS.clickMode?.paintStrategy === "BC" ? "BC" : null;
      }
    }
    /**
     * Active paint override: HUD temporary choice while open, else Settings Advanced.
     * @returns {'A'|'B'|'C'|'BC'|null}
     */
    _effectivePaintOverride() {
      if (this._shadowDebugHudEnabled) {
        return this._shadowDebugPaintOverride;
      }
      return this._settingsPaintOverride();
    }
    _getTextModeSettings() {
      const tm = this._modeSettings?.textMode && typeof this._modeSettings.textMode === "object" ? { ...DEFAULT_SETTINGS.textMode, ...this._modeSettings.textMode } : DEFAULT_SETTINGS.textMode;
      const strokeThickness = Number(tm.strokeThickness);
      const thickness = Number.isFinite(strokeThickness) ? Math.min(Math.max(strokeThickness, 1), 16) : 3;
      const leftEdgeRaw = Number(tm.leftEdgeWidth);
      const leftEdgeWidth = Number.isFinite(leftEdgeRaw) ? Math.min(Math.max(leftEdgeRaw, 1), 24) : Number(DEFAULT_SETTINGS.textMode.leftEdgeWidth) || 5;
      const focusStyle = tm.focusStyle === "background_tint" ? "background_tint" : "left_edge";
      return {
        strokeThickness: thickness,
        labelsEnabled: tm.labelsEnabled,
        focusStyle,
        leftEdgeWidth
      };
    }
    setHoverClickLabelText(text) {
      this.hoverClickLabelText = String(text || "F clicks");
      if (this.escExitLabelHover) this.escExitLabelHover.innerHTML = this.formatActiveTextFieldLabel();
    }
    formatHoverLabelText(text) {
      const countdownMatch = text.match(/^(\d+)\s+(F clicks?)$/);
      if (countdownMatch) {
        const [_, number, rest] = countdownMatch;
        return `<span class="countdown-number">${number}</span> <kbd>F</kbd> clicks`;
      } else {
        return text.replace(/^F/, "<kbd>F</kbd>");
      }
    }
    formatActiveTextFieldLabel() {
      const suffix = this.hoverClickLabelText ? this.formatHoverLabelText(this.hoverClickLabelText) : "";
      return suffix ? `Active text field &nbsp;\u2022&nbsp; ${suffix}` : "Active text field";
    }
    ensureTextModeLabels() {
      if (!this.escExitLabelHover) {
        this.escExitLabelHover = this.createElement("div", {
          className: CSS_CLASSES.ESC_EXIT_LABEL,
          style: `
          position: fixed;
          pointer-events: none;
          z-index: ${Z_INDEX.OVERLAYS_ABOVE};
          will-change: transform, opacity;
        `
        });
        this.escExitLabelHover.innerHTML = this.formatActiveTextFieldLabel();
        document.body.appendChild(this.escExitLabelHover);
        if (this.overlayObserver) this.overlayObserver.observe(this.escExitLabelHover);
      } else {
        this.escExitLabelHover.innerHTML = this.formatActiveTextFieldLabel();
      }
      if (!this.escExitLabelText) {
        this.escExitLabelText = this.createElement("div", {
          className: CSS_CLASSES.ESC_EXIT_LABEL,
          style: `
          position: fixed;
          pointer-events: none;
          z-index: ${Z_INDEX.OVERLAYS_ABOVE};
          will-change: transform, opacity;
        `
        });
        this.escExitLabelText.innerHTML = "Press <kbd>ESC</kbd> to close";
        document.body.appendChild(this.escExitLabelText);
        if (this.overlayObserver) this.overlayObserver.observe(this.escExitLabelText);
      } else {
        this.escExitLabelText.innerHTML = "Press <kbd>ESC</kbd> to close";
      }
    }
    updateTextModeLabels(element) {
      if (!element) {
        this.hideTextModeLabels();
        return;
      }
      const { labelsEnabled } = this._getTextModeSettings();
      if (!labelsEnabled) {
        this.hideTextModeLabels();
        return;
      }
      this.ensureTextModeLabels();
      const rect = this.getBestRect(element);
      if (!rect || rect.width <= 0 || rect.height <= 0) {
        this.hideTextModeLabels();
        return;
      }
      const active2 = this.escExitLabelHover;
      const esc = this.escExitLabelText;
      const activeH = active2?.offsetHeight || 24;
      const escH = esc?.offsetHeight || 24;
      const activeW = active2?.offsetWidth || 180;
      const escW = esc?.offsetWidth || 180;
      const pad = 8;
      let activeTop = rect.top - activeH - pad;
      let escTop = rect.top + rect.height + pad;
      if (activeTop < pad) activeTop = rect.top + rect.height + pad;
      if (escTop + escH > window.innerHeight - pad) escTop = rect.top - escH - pad;
      if (Math.abs(activeTop - escTop) < Math.min(activeH, escH) + 4) {
        if (escTop >= rect.top + rect.height) {
          escTop = escTop + activeH + 6;
        } else {
          escTop = escTop - activeH - 6;
        }
      }
      const clampLeft = (w) => Math.min(Math.max(rect.left, pad), window.innerWidth - w - pad);
      const activeLeft = clampLeft(activeW);
      const escLeft = clampLeft(escW);
      if (active2) {
        active2.style.left = `${activeLeft}px`;
        active2.style.top = `${Math.min(Math.max(activeTop, pad), window.innerHeight - activeH - pad)}px`;
        active2.style.display = "block";
        active2.style.visibility = "visible";
      }
      if (esc) {
        esc.style.left = `${escLeft}px`;
        esc.style.top = `${Math.min(Math.max(escTop, pad), window.innerHeight - escH - pad)}px`;
        esc.style.display = "block";
        esc.style.visibility = "visible";
      }
    }
    hideTextModeLabels() {
      if (this.escExitLabelText) this.escExitLabelText.style.display = "none";
      if (this.escExitLabelHover) this.escExitLabelHover.style.display = "none";
    }
    /**
     * Layout-aware cancel key shown in the vertical text-mode sidecar.
     * @param {string} label
     */
    setTextFocusEscKeyLabel(label) {
      const next = String(label || "").trim() || "Esc";
      if (next === this._textFocusEscKeyLabel) return;
      this._textFocusEscKeyLabel = next;
      if (this.textFocusEscHint) this._renderTextFocusEscHint();
    }
    /**
     * Layout-aware activate key shown in the vertical text-hover sidecar.
     * @param {string} label
     */
    setTextHoverActivateKeyLabel(label) {
      const next = String(label || "").trim() || "F";
      if (next === this._textHoverActivateKeyLabel) return;
      this._textHoverActivateKeyLabel = next;
      if (this.textHoverActivateHint) this._renderTextHoverActivateHint();
    }
    _escapeHintText(value) {
      return String(value || "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
    }
    _renderTextFocusEscHint() {
      if (!this.textFocusEscHint) return;
      const key = this._escapeHintText(this._textFocusEscKeyLabel || "Esc");
      this.textFocusEscHint.innerHTML = `<kbd>${key}</kbd><span>to</span><span>exit</span>`;
    }
    _renderTextHoverActivateHint() {
      if (!this.textHoverActivateHint) return;
      const key = this._escapeHintText(this._textHoverActivateKeyLabel || "F");
      this.textHoverActivateHint.innerHTML = `<kbd>${key}</kbd><span>to</span><span>select</span>`;
    }
    ensureTextFocusEscHint() {
      if (this.textFocusEscHint) return this.textFocusEscHint;
      this.textFocusEscHint = this.createElement("div", {
        className: CSS_CLASSES.TEXT_FOCUS_ESC_HINT,
        style: `
        position: fixed;
        pointer-events: none;
        z-index: ${Z_INDEX.OVERLAYS_ABOVE};
      `
      });
      this._renderTextFocusEscHint();
      document.body.appendChild(this.textFocusEscHint);
      return this.textFocusEscHint;
    }
    ensureTextHoverActivateHint() {
      if (this.textHoverActivateHint) return this.textHoverActivateHint;
      this.textHoverActivateHint = this.createElement("div", {
        className: CSS_CLASSES.TEXT_HOVER_ACTIVATE_HINT,
        style: `
        position: fixed;
        pointer-events: none;
        z-index: ${Z_INDEX.OVERLAYS_ABOVE};
      `
      });
      this._renderTextHoverActivateHint();
      document.body.appendChild(this.textHoverActivateHint);
      return this.textHoverActivateHint;
    }
    /**
     * Body-level sidecars use OVERLAYS_ABOVE, which sits *below* KP chrome
     * (onboarding, practice popover, settings). Fields inside those open
     * shadows still need the hint painted on `document.body` (host overflow
     * would clip an in-shadow mount), so lift z-index above the target’s
     * light-DOM stacking context — still below the cursor.
     * @param {Element|null|undefined} el
     * @returns {number}
     */
    _overlayZIndexAboveTarget(el2) {
      const floor = Z_INDEX.OVERLAYS_ABOVE;
      const cap = (Z_INDEX.CURSOR || 2147483050) - 1;
      let maxZ = 0;
      let node = el2;
      const seen = /* @__PURE__ */ new Set();
      while (node && node.nodeType && !seen.has(node)) {
        seen.add(node);
        if (node.nodeType === 1) {
          try {
            let zi = parseInt(node.style?.zIndex, 10);
            if (!Number.isFinite(zi)) {
              zi = parseInt(window.getComputedStyle(node).zIndex, 10);
            }
            if (Number.isFinite(zi) && zi > maxZ) maxZ = zi;
          } catch {
          }
        }
        if (node.parentElement) {
          node = node.parentElement;
          continue;
        }
        try {
          const root = typeof node.getRootNode === "function" ? node.getRootNode() : null;
          node = root && root.host && root.host !== node ? root.host : null;
        } catch {
          node = null;
        }
      }
      return Math.min(Math.max(floor, maxZ + 1), cap);
    }
    /**
     * Position a vertical sidecar immediately left of a text field’s left edge.
     * @param {HTMLElement} hint
     * @param {DOMRect|{left:number,top:number,width:number,height:number}} rect
     * @param {{ fallbackWidth?: number, fallbackHeight?: number, targetEl?: Element|null }} [opts]
     */
    _positionTextFieldSidecar(hint, rect, opts = {}) {
      if (!hint || !rect) return;
      hint.style.display = "flex";
      const w = hint.offsetWidth || opts.fallbackWidth || 28;
      const h = hint.offsetHeight || opts.fallbackHeight || 36;
      const gap = 4;
      hint.style.left = `${Math.round(rect.left - w - gap)}px`;
      hint.style.top = `${Math.round(rect.top + (rect.height - h) / 2)}px`;
      hint.style.visibility = "visible";
      try {
        hint.style.zIndex = String(this._overlayZIndexAboveTarget(opts.targetEl || null));
      } catch {
      }
    }
    /**
     * Stack “Esc / to / exit” immediately left of the orange left-edge bar.
     * @param {Element|null} element
     */
    updateTextFocusEscHint(element) {
      if (!element) {
        this.hideTextFocusEscHint();
        return;
      }
      const host = this._textFocusPaintHost && this._textFocusPaintHost.isConnected ? this._textFocusPaintHost : element;
      const rect = this.getBestRect(host);
      if (!rect || rect.width <= 0 || rect.height <= 0) {
        this.hideTextFocusEscHint();
        return;
      }
      this._positionTextFieldSidecar(this.ensureTextFocusEscHint(), rect, {
        fallbackWidth: 28,
        fallbackHeight: 36,
        targetEl: host
      });
    }
    hideTextFocusEscHint() {
      if (this.textFocusEscHint) this.textFocusEscHint.style.display = "none";
    }
    /**
     * Stack “F / to / select” left of a hovered text field.
     * @param {Element|null} element
     */
    updateTextHoverActivateHint(element) {
      if (!element) {
        this.hideTextHoverActivateHint();
        return;
      }
      const rect = this.getBestRect(element);
      if (!rect || rect.width <= 0 || rect.height <= 0) {
        this.hideTextHoverActivateHint();
        return;
      }
      this._positionTextFieldSidecar(this.ensureTextHoverActivateHint(), rect, {
        fallbackWidth: 28,
        fallbackHeight: 36,
        targetEl: element
      });
    }
    hideTextHoverActivateHint() {
      if (this.textHoverActivateHint) this.textHoverActivateHint.style.display = "none";
    }
    /** Reposition the hover activate sidecar after scroll/resize when one is active. */
    refreshTextHoverActivateHint() {
      if (!this._textHoverCurrentElement || !this._textHoverCurrentElement.isConnected) {
        this.hideTextHoverActivateHint();
        return;
      }
      this.updateTextHoverActivateHint(this._textHoverCurrentElement);
    }
    _clearTextFocusElementStyling() {
      if (!this._textFocusStyledElements || this._textFocusStyledElements.size === 0) {
        this._textFocusCurrentElement = null;
        this._textFocusPaintHost = null;
        this._textFocusAppliedStyle = null;
        this.hideTextFocusEscHint();
        return;
      }
      try {
        for (const el2 of this._textFocusStyledElements) {
          if (!el2 || el2.nodeType !== 1) continue;
          try {
            el2.classList.remove(CSS_CLASSES.TEXT_FOCUS_INPUT);
            el2.classList.remove(CSS_CLASSES.TEXT_FOCUS_INPUT_PARENT);
            el2.classList.remove(CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE);
            try {
              el2.classList.remove(CSS_CLASSES.TEXT_FOCUS_DELEGATED);
            } catch {
            }
            try {
              el2.classList.remove(CSS_CLASSES.TEXT_FOCUS_HINT_HIDDEN);
            } catch {
            }
          } catch {
          }
        }
      } finally {
        this._textFocusStyledElements.clear();
        this._textFocusCurrentElement = null;
        this._textFocusPaintHost = null;
        this._textFocusAppliedStyle = null;
        this.hideTextFocusEscHint();
      }
    }
    _getNearbyInputWrappers(inputEl) {
      const parents = [];
      if (!inputEl || inputEl.nodeType !== 1) return parents;
      const inputRect = this.getBestRect(inputEl);
      if (!inputRect || inputRect.width <= 0 || inputRect.height <= 0) return parents;
      const maxDepth = 4;
      const maxPad = 28;
      const maxAreaMultiple = 4;
      let p = inputEl.parentElement;
      let depth = 0;
      while (p && depth++ < maxDepth) {
        if (p === document.body || p === document.documentElement) break;
        let r;
        try {
          r = p.getBoundingClientRect();
        } catch {
          break;
        }
        if (!r || r.width <= 0 || r.height <= 0) {
          p = p.parentElement;
          continue;
        }
        const dx = Math.max(Math.abs(r.left - inputRect.left), Math.abs(r.right - inputRect.right));
        const dy = Math.max(Math.abs(r.top - inputRect.top), Math.abs(r.bottom - inputRect.bottom));
        const inputArea = inputRect.width * inputRect.height;
        const area = r.width * r.height;
        const closeEnough = dx <= maxPad && dy <= maxPad;
        const notTooBig = area <= inputArea * maxAreaMultiple && r.width <= inputRect.width + maxPad * 2 && r.height <= inputRect.height + maxPad * 2;
        if (!closeEnough || !notTooBig) break;
        parents.push(p);
        p = p.parentElement;
      }
      return parents;
    }
    /**
     * Visual box for text-mode chrome. Google/Gmail search (and similar) wrap a
     * short `position:absolute` <input> in a taller pill; the left-edge bar must
     * span that pill, not the single-line control.
     *
     * Do **not** promote to a parent that only stacks a caption/label above the
     * field (onboarding practice popover: `div > label + input|textarea`) — that
     * makes the hover ring taller than the control and can paint as a non-text
     * (blue) box when the paint target is no longer the field.
     *
     * @param {Element} inputEl
     * @returns {Element}
     */
    _resolveTextFocusPaintHost(inputEl) {
      if (!inputEl || inputEl.nodeType !== 1) return inputEl;
      let ir;
      try {
        ir = inputEl.getBoundingClientRect();
      } catch {
        ir = null;
      }
      if (!ir || ir.width < 4 || ir.height < 2) return inputEl;
      let best = inputEl;
      let bestH = ir.height;
      let p = inputEl.parentElement;
      let depth = 0;
      while (p && depth++ < 14) {
        if (p === document.body || p === document.documentElement) break;
        let r;
        try {
          r = p.getBoundingClientRect();
        } catch {
          r = null;
        }
        if (!r || r.width <= 0 || r.height <= 0) {
          p = p.parentElement;
          continue;
        }
        if (r.width > ir.width + 180) break;
        if (r.height > 84 && r.height > ir.height * 3.5) break;
        try {
          const kids = p.children;
          if (kids && kids.length) {
            let labelAbove = false;
            for (let i = 0; i < kids.length; i++) {
              const child = kids[i];
              if (!child || child === inputEl || child.nodeType !== 1) continue;
              if (inputEl.contains(child)) continue;
              const tag = String(child.tagName || "").toUpperCase();
              const isLabelish = tag === "LABEL" || tag === "SPAN" || tag === "DIV" || tag === "P";
              if (!isLabelish) continue;
              try {
                if (typeof child.contains === "function" && child.contains(inputEl)) continue;
              } catch {
              }
              let cr = null;
              try {
                cr = child.getBoundingClientRect();
              } catch {
                cr = null;
              }
              if (!cr || !(cr.height > 4) || !(cr.width > 4)) continue;
              if (cr.bottom <= ir.top + 4 && cr.top < ir.top - 2) {
                labelAbove = true;
                break;
              }
            }
            if (labelAbove) break;
          }
        } catch {
        }
        let display = "";
        try {
          display = String(getComputedStyle(p).display || "");
        } catch {
          display = "";
        }
        const skipDisplay = display === "table-row" || display === "table-row-group";
        const leftDelta = Math.abs(r.left - ir.left);
        const similarColumn = leftDelta <= 72 && r.width >= ir.width * 0.85;
        if (similarColumn && !skipDisplay && r.height >= bestH - 0.5 && r.height <= 84) {
          best = p;
          bestH = r.height;
        }
        p = p.parentElement;
      }
      return best;
    }
    /**
     * Whether `wrapper` is the same visual box as `field` (padded chrome, not a
     * card/section). Used so hover-outline suppression still applies when
     * findClickable promotes a tight input shell.
     * @param {Element} wrapper
     * @param {Element} field
     * @returns {boolean}
     */
    _isTightTextFieldWrapper(wrapper, field) {
      if (!wrapper || !field || wrapper === field) return false;
      let wr;
      let fr;
      try {
        wr = wrapper.getBoundingClientRect();
        fr = this.getBestRect(field);
      } catch {
        return false;
      }
      if (!wr || !fr || wr.width <= 0 || wr.height <= 0 || fr.width <= 0 || fr.height <= 0) {
        return false;
      }
      const maxPad = 40;
      const dx = Math.max(Math.abs(wr.left - fr.left), Math.abs(wr.right - fr.right));
      const dy = Math.max(Math.abs(wr.top - fr.top), Math.abs(wr.bottom - fr.bottom));
      return dx <= Math.max(maxPad, 72) && dy <= Math.max(maxPad, 40) && wr.width <= fr.width + 180 && wr.height <= Math.max(fr.height + maxPad * 2, 84);
    }
    /**
     * Hover/paint target is the text-mode focused field, a child inside it
     * (contenteditable), or a tight same-box wrapper. Those already show the
     * left-edge bar (or wash); a full hover outline on the same box flickers.
     * @param {Element|null|undefined} hoverEl
     * @param {Element|null|undefined} [activeEl]
     * @returns {boolean}
     */
    _isHoverOnActiveTextField(hoverEl, activeEl = this._textFocusCurrentElement) {
      if (!hoverEl || hoverEl.nodeType !== 1) return false;
      const active2 = activeEl && activeEl.nodeType === 1 ? activeEl : this._textFocusCurrentElement;
      if (!active2 || active2.nodeType !== 1) return false;
      try {
        if (!active2.isConnected) return false;
      } catch {
      }
      if (hoverEl === active2) return true;
      try {
        if (this._textFocusStyledElements && this._textFocusStyledElements.has(hoverEl)) return true;
      } catch {
      }
      try {
        if (typeof active2.contains === "function" && active2.contains(hoverEl)) return true;
      } catch {
      }
      try {
        if (typeof hoverEl.contains === "function" && hoverEl.contains(active2)) {
          return this._isTightTextFieldWrapper(hoverEl, active2);
        }
      } catch {
      }
      return false;
    }
    /**
     * Lazy-inject KeyPilot CSS into the open ShadowRoot that owns `el` (if any).
     * No-op for light DOM. See StyleManager.ensureStylesForNode.
     * @param {Element|null|undefined} el
     */
    _ensureStylesForElement(el2) {
      try {
        const sm = window.keyPilot?.styleManager;
        if (sm && typeof sm.ensureStylesForNode === "function") {
          sm.ensureStylesForNode(el2);
        }
      } catch {
      }
    }
    _applyTextFocusElementStyling(inputEl) {
      if (!inputEl || inputEl.nodeType !== 1) {
        this._clearTextFocusElementStyling();
        return;
      }
      const { focusStyle } = this._getTextModeSettings();
      const useLeftEdge = focusStyle !== "background_tint";
      const paintHost = useLeftEdge ? this._resolveTextFocusPaintHost(inputEl) || inputEl : inputEl;
      const delegated = useLeftEdge && paintHost !== inputEl;
      const hideHint = this._shouldHideTextFocusHint(inputEl);
      if (this._textFocusCurrentElement === inputEl && this._textFocusPaintHost === paintHost && this._textFocusStyledElements.size > 0 && this._textFocusAppliedStyle === focusStyle) {
        try {
          inputEl.classList.add(CSS_CLASSES.TEXT_FOCUS_INPUT);
          if (useLeftEdge && !delegated) inputEl.classList.add(CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE);
          else inputEl.classList.remove(CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE);
          if (delegated) inputEl.classList.add(CSS_CLASSES.TEXT_FOCUS_DELEGATED);
          else inputEl.classList.remove(CSS_CLASSES.TEXT_FOCUS_DELEGATED);
          this._syncTextFocusHintHidden(inputEl, hideHint);
          if (delegated) {
            paintHost.classList.add(CSS_CLASSES.TEXT_FOCUS_INPUT_PARENT);
            paintHost.classList.add(CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE);
          }
        } catch {
        }
        return;
      }
      this._clearTextFocusElementStyling();
      this._textFocusCurrentElement = inputEl;
      this._textFocusPaintHost = paintHost;
      this._textFocusAppliedStyle = focusStyle;
      try {
        this._ensureStylesForElement(inputEl);
        inputEl.classList.add(CSS_CLASSES.TEXT_FOCUS_INPUT);
        if (useLeftEdge && !delegated) inputEl.classList.add(CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE);
        if (delegated) inputEl.classList.add(CSS_CLASSES.TEXT_FOCUS_DELEGATED);
        this._syncTextFocusHintHidden(inputEl, hideHint);
        this._textFocusStyledElements.add(inputEl);
      } catch {
      }
      if (delegated) {
        try {
          this._ensureStylesForElement(paintHost);
          paintHost.classList.add(CSS_CLASSES.TEXT_FOCUS_INPUT_PARENT);
          paintHost.classList.add(CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE);
          this._textFocusStyledElements.add(paintHost);
        } catch {
        }
      }
      if (!useLeftEdge) {
        const parents = this._getNearbyInputWrappers(inputEl);
        for (const p of parents) {
          try {
            this._ensureStylesForElement(p);
            p.classList.add(CSS_CLASSES.TEXT_FOCUS_INPUT_PARENT);
            this._textFocusStyledElements.add(p);
          } catch {
          }
        }
      }
    }
    /**
     * SVG "press Esc to exit" needs vertical room; compact chrome fields (e.g.
     * Keyboard Layout Config) keep focus chrome without the label.
     * @param {Element} inputEl
     * @returns {boolean}
     */
    _shouldHideTextFocusHint(inputEl) {
      try {
        const rect = typeof inputEl.getBoundingClientRect === "function" ? inputEl.getBoundingClientRect() : null;
        const h = Number(rect?.height) || 0;
        return h > 0 && h < TEXT_FOCUS_HINT_MIN_HEIGHT_PX;
      } catch {
        return false;
      }
    }
    /**
     * @param {Element} inputEl
     * @param {boolean} hide
     */
    _syncTextFocusHintHidden(inputEl, hide) {
      try {
        if (hide) inputEl.classList.add(CSS_CLASSES.TEXT_FOCUS_HINT_HIDDEN);
        else inputEl.classList.remove(CSS_CLASSES.TEXT_FOCUS_HINT_HIDDEN);
      } catch {
      }
    }
    _clearTextHoverElementStyling() {
      if (!this._textHoverStyledElements || this._textHoverStyledElements.size === 0) {
        this._textHoverCurrentElement = null;
        this.hideTextHoverActivateHint();
        return;
      }
      try {
        for (const el2 of this._textHoverStyledElements) {
          if (!el2 || el2.nodeType !== 1) continue;
          try {
            el2.classList.remove(CSS_CLASSES.TEXT_HOVER_INPUT);
            el2.classList.remove(CSS_CLASSES.TEXT_HOVER_INPUT_PARENT);
          } catch {
          }
        }
      } finally {
        this._textHoverStyledElements.clear();
        this._textHoverCurrentElement = null;
        this.hideTextHoverActivateHint();
      }
    }
    _applyTextHoverElementStyling(inputEl) {
      if (!inputEl || inputEl.nodeType !== 1) {
        this._clearTextHoverElementStyling();
        return;
      }
      if (this._textHoverCurrentElement === inputEl && this._textHoverStyledElements.size > 0) {
        try {
          inputEl.classList.add(CSS_CLASSES.TEXT_HOVER_INPUT);
        } catch {
        }
        this.updateTextHoverActivateHint(inputEl);
        return;
      }
      this._clearTextHoverElementStyling();
      this._textHoverCurrentElement = inputEl;
      try {
        this._ensureStylesForElement(inputEl);
        inputEl.classList.add(CSS_CLASSES.TEXT_HOVER_INPUT);
        this._textHoverStyledElements.add(inputEl);
      } catch {
      }
      this.updateTextHoverActivateHint(inputEl);
    }
    updateFocusOverlay(element, mode = MODES.NONE, rectOverride = null) {
      try {
        this._lastFocusElement = element || null;
        if (element) {
          const r = rectOverride && typeof rectOverride === "object" ? rectOverride : this.getBestRect(element);
          this._lastFocusRect = r && r.width > 0 && r.height > 0 ? { left: r.left, top: r.top, width: r.width, height: r.height } : null;
        } else {
          this._lastFocusRect = null;
        }
      } catch {
        this._lastFocusElement = element || null;
        this._lastFocusRect = null;
      }
      if (this._isHoverOnActiveTextField(element)) {
        try {
          this._clearTextHoverElementStyling();
        } catch {
        }
        this.hideFocusOverlay();
        return;
      }
      try {
        const isTextInput = element && element.matches && element.matches(SELECTORS.FOCUSABLE_TEXT);
        if (isTextInput) {
          this._applyTextHoverElementStyling(element);
        } else {
          this._clearTextHoverElementStyling();
        }
      } catch {
        try {
          this._clearTextHoverElementStyling();
        } catch {
        }
      }
      if (this._useDomHoverFocusColors) {
        try {
          this.hideFocusOverlayCanvas();
        } catch {
        }
        try {
          this.hideFocusOverlayCSSCustomProps();
        } catch {
        }
        if (!element) {
          this._focusPaintUsesFixedOverlay = false;
          this._focusPaintUsesInTargetRing = false;
          try {
            this.clearElementFocusStyling();
          } catch {
          }
          try {
            this.hideInTargetFocusRing();
          } catch {
          }
          try {
            this.hideFocusOverlayDOM();
          } catch {
          }
          try {
            this._updateShadowRootDebugHud(null, null, {
              inShadow: false,
              autoStrategy: "\u2014",
              appliedStrategy: "\u2014",
              override: this._shadowDebugPaintOverride
            });
          } catch {
          }
          return;
        }
        let paintEl = this._resolveFocusPaintElement(element) || element;
        let cardShell = null;
        try {
          cardShell = this._resolveMediaTextCardShell(element);
        } catch {
          cardShell = null;
        }
        let inShadow = false;
        try {
          inShadow = this._isInShadowTree(element);
        } catch {
          inShadow = false;
        }
        let isShadowHost = false;
        try {
          isShadowHost = !!this._getOpenShadowRoot(element);
        } catch {
          isShadowHost = false;
        }
        const shadowPaint = inShadow || isShadowHost;
        let isTextInputFocus = false;
        try {
          isTextInputFocus = !!(element.matches && element.matches(SELECTORS.FOCUSABLE_TEXT));
        } catch {
          isTextInputFocus = false;
        }
        let needsEscapeHatch = false;
        try {
          needsEscapeHatch = !!cardShell || this._shouldUseFixedFocusOverlay(element);
        } catch {
          needsEscapeHatch = !!cardShell;
        }
        let autoStrategy = "A";
        if (shadowPaint || needsEscapeHatch) {
          let preferShadowTextA = false;
          if (isTextInputFocus && inShadow && !isShadowHost) {
            try {
              preferShadowTextA = this._ensureStylesForElement(element) !== false;
            } catch {
              preferShadowTextA = false;
            }
          }
          if (preferShadowTextA) {
            autoStrategy = "A";
          } else {
            let canB = false;
            try {
              canB = !!(FEATURE_FLAGS && FEATURE_FLAGS.ENABLE_IN_TARGET_FOCUS_RING) && !!this._resolveInTargetHost(element);
            } catch {
              canB = false;
            }
            autoStrategy = canB ? "B" : this._strategyAIsViable(element) ? "A" : "C";
          }
        }
        const override = this._effectivePaintOverride();
        let strategy = autoStrategy;
        let forceIframeA = false;
        try {
          forceIframeA = !!(element.ownerDocument && element.ownerDocument !== document);
        } catch {
          forceIframeA = false;
        }
        let forceOnboardingA = false;
        try {
          forceOnboardingA = !!closestComposed(element, ".kp-onboarding-panel");
        } catch {
          forceOnboardingA = false;
        }
        let forceOwnedChromeA = false;
        try {
          forceOwnedChromeA = !!closestComposed(
            element,
            ".kpv2-settings-host, .kpv2-docs-host, .kpv2-popover-container"
          );
        } catch {
          forceOwnedChromeA = false;
        }
        if (forceIframeA || forceOnboardingA || forceOwnedChromeA) {
          autoStrategy = "A";
          strategy = "A";
        } else if (override === "A" || override === "B" || override === "C") {
          strategy = override;
        } else if (override === "BC") {
          let canB = false;
          try {
            canB = !!(FEATURE_FLAGS && FEATURE_FLAGS.ENABLE_IN_TARGET_FOCUS_RING) && !!this._resolveInTargetHost(element);
          } catch {
            canB = false;
          }
          strategy = canB ? "B" : this._strategyAIsViable(element) ? "A" : "C";
        }
        if (this._preferADuringScroll && strategy === "C") {
          strategy = "A";
        }
        try {
          this._updateShadowRootDebugHud(element, paintEl, {
            inShadow,
            autoStrategy,
            appliedStrategy: strategy,
            override
          });
        } catch {
        }
        if (strategy === "A") {
          this._focusPaintUsesFixedOverlay = false;
          this._focusPaintUsesInTargetRing = false;
          try {
            this.hideInTargetFocusRing();
          } catch {
          }
          try {
            this.hideFocusOverlayDOM();
          } catch {
          }
          return this.updateFocusOverlayElementStyling(element, mode);
        }
        try {
          this.clearElementFocusStyling();
        } catch {
        }
        if (strategy === "B") {
          let usedInTarget = false;
          try {
            usedInTarget = this.updateFocusOverlayInTarget(element, mode);
          } catch {
            usedInTarget = false;
          }
          if (usedInTarget) {
            this._focusPaintUsesInTargetRing = true;
            this._focusPaintUsesFixedOverlay = false;
            try {
              this.hideFocusOverlayDOM();
            } catch {
            }
            try {
              this._updateShadowRootDebugHud(element, paintEl, {
                inShadow,
                autoStrategy,
                appliedStrategy: "B",
                override
              });
            } catch {
            }
            return;
          }
          if (isClickableKeyPilotChromeElement(element) || this._strategyAIsViable(element)) {
            this._focusPaintUsesFixedOverlay = false;
            this._focusPaintUsesInTargetRing = false;
            try {
              this.hideInTargetFocusRing();
            } catch {
            }
            try {
              this.hideFocusOverlayDOM();
            } catch {
            }
            try {
              this._updateShadowRootDebugHud(element, paintEl, {
                inShadow,
                autoStrategy,
                appliedStrategy: "A",
                override
              });
            } catch {
            }
            return this.updateFocusOverlayElementStyling(element, mode);
          }
        }
        this._focusPaintUsesInTargetRing = false;
        try {
          this.hideInTargetFocusRing();
        } catch {
        }
        this._focusPaintUsesFixedOverlay = true;
        const paintForC = rectOverride ? element : paintEl;
        const cResult = this.updateFocusOverlayDOM(paintForC, mode, rectOverride, {
          colorFrom: element
        });
        if (cResult === "occluded") {
          this._focusPaintUsesFixedOverlay = false;
          if (isClickableKeyPilotChromeElement(element) || this._strategyAIsViable(element)) {
            try {
              this._updateShadowRootDebugHud(element, paintEl, {
                inShadow,
                autoStrategy,
                appliedStrategy: "A",
                override
              });
            } catch {
            }
            return this.updateFocusOverlayElementStyling(element, mode);
          }
        }
        try {
          this._updateShadowRootDebugHud(element, paintEl, {
            inShadow,
            autoStrategy,
            appliedStrategy: "C",
            override
          });
        } catch {
        }
        return cResult;
      }
      switch (this.renderingMode) {
        case "canvas":
          return this.updateFocusOverlayCanvas(element, mode, rectOverride);
        case "css-custom-props":
          return this.updateFocusOverlayCSSCustomProps(element, mode, rectOverride);
        case "dom":
        default:
          return this.updateFocusOverlayDOM(element, mode, rectOverride);
      }
    }
    _installFocusClipCacheInvalidation() {
      if (this._focusClipInvalidateBound) return;
      this._focusClipInvalidateBound = () => {
        try {
          this._focusClipContextCache = /* @__PURE__ */ new WeakMap();
        } catch {
        }
      };
      try {
        window.addEventListener("resize", this._focusClipInvalidateBound, { passive: true });
      } catch {
      }
      try {
        document.addEventListener("keypilot:scroll-end", this._focusClipInvalidateBound, { passive: true });
      } catch {
      }
    }
    /**
     * Intersect two viewport boxes. Returns null when the result is empty.
     * @param {{ left: number, top: number, width: number, height: number }} a
     * @param {{ left: number, top: number, width: number, height: number }} b
     * @returns {{ left: number, top: number, width: number, height: number }|null}
     */
    _intersectViewportRects(a, b) {
      if (!a || !b) return null;
      const left = Math.max(a.left, b.left);
      const top = Math.max(a.top, b.top);
      const right = Math.min(a.left + a.width, b.left + b.width);
      const bottom = Math.min(a.top + a.height, b.top + b.height);
      const width = right - left;
      const height = bottom - top;
      if (!(width > 0.5) || !(height > 0.5)) return null;
      return { left, top, width, height };
    }
    /**
     * True when two viewport boxes overlap. Abspos flyouts (Epoch Times header
     * `group-hover` mega-menu) are DOM descendants of an `overflow-x-auto` bar
     * but paint entirely *outside* that bar — the overflow box is not a clipper
     * for that target, so do not use it to skip A or zero out C.
     * @param {{ left?: number, top?: number, width?: number, height?: number, right?: number, bottom?: number }|null|undefined} a
     * @param {{ left?: number, top?: number, width?: number, height?: number, right?: number, bottom?: number }|null|undefined} b
     * @returns {boolean}
     */
    _viewportRectsOverlap(a, b) {
      if (!a || !b) return false;
      const aL = Number(a.left);
      const aT = Number(a.top);
      const aR = Number(a.right != null ? a.right : aL + Number(a.width));
      const aB = Number(a.bottom != null ? a.bottom : aT + Number(a.height));
      const bL = Number(b.left);
      const bT = Number(b.top);
      const bR = Number(b.right != null ? b.right : bL + Number(b.width));
      const bB = Number(b.bottom != null ? b.bottom : bT + Number(b.height));
      if (![aL, aT, aR, aB, bL, bT, bR, bB].every(Number.isFinite)) return false;
      return aR > bL + 0.5 && aL < bR - 0.5 && aB > bT + 0.5 && aT < bB - 0.5;
    }
    /**
     * Shrink a strategy-C / flash viewport box to the portion still visible after
     * ancestor overflow / paint-containment clipping (and the viewport itself).
     *
     * `getBoundingClientRect()` ignores overflow clips, so a body-fixed overlay
     * sized to the raw box paints into scrolled-away / overflow:hidden regions.
     * Intersecting with every clipping ancestor matches what the user can see.
     *
     * @param {Element|null|undefined} element - paint target (for ancestor walk)
     * @param {{ left?: number, top?: number, width?: number, height?: number }|null|undefined} rect
     * @returns {{ left: number, top: number, width: number, height: number }|null}
     */
    _clipViewportRectToVisible(element, rect) {
      let out = this._asPositiveViewportRect(rect);
      if (!out) return null;
      try {
        const vw = Number(window.innerWidth) || 0;
        const vh = Number(window.innerHeight) || 0;
        if (vw > 0 && vh > 0) {
          out = this._intersectViewportRects(out, { left: 0, top: 0, width: vw, height: vh });
          if (!out) return null;
        }
      } catch {
      }
      if (!element || element.nodeType !== 1) return out;
      const origin = out;
      const intersectNode = (n) => {
        if (!n || n.nodeType !== 1) return true;
        let ar = null;
        try {
          ar = n.getBoundingClientRect();
        } catch {
          ar = null;
        }
        const cr = this._asPositiveViewportRect(ar);
        if (!cr) return true;
        if (origin && !this._viewportRectsOverlap(origin, cr)) return true;
        out = this._intersectViewportRects(out, cr);
        return !!out;
      };
      try {
        let n = this._composedParent(element);
        let depth = 0;
        while (n && n.nodeType === 1 && depth++ < 24) {
          if (n === document.documentElement || n === document.body) {
            n = this._composedParent(n);
            continue;
          }
          let cs = null;
          try {
            cs = window.getComputedStyle(n);
          } catch {
            cs = null;
          }
          let cvClip = false;
          try {
            const cv = cs?.contentVisibility || cs?.getPropertyValue?.("content-visibility");
            cvClip = cv === "auto" || cv === "hidden";
          } catch {
          }
          if (this._styleClipsSelf(cs) || cvClip) {
            if (!intersectNode(n)) return null;
          }
          try {
            const sr = this._getOpenShadowRoot(n);
            const kids = sr && sr.children;
            if (kids) {
              for (let i = 0; i < kids.length && i < 6; i++) {
                const w = kids[i];
                if (!w || w.nodeType !== 1) continue;
                if ((w.tagName || "").toUpperCase() === "STYLE") continue;
                let wcs = null;
                try {
                  wcs = window.getComputedStyle(w);
                } catch {
                  wcs = null;
                }
                if (!this._styleClipsSelf(wcs)) continue;
                if (!intersectNode(w)) return null;
              }
            }
          } catch {
          }
          n = this._composedParent(n);
        }
      } catch {
      }
      return out;
    }
    /**
     * Find ancestors that would clip an outer focus ring around `element`.
     *
     * Used only when FEATURE_FLAGS.ENABLE_FOCUS_CLIP_INSET and/or
     * ENABLE_FOCUS_TIGHT_WRAPPER_PROMOTION are on (see constants.js for tentative
     * purpose). Never opens page overflow — that broke carousels (IMDb).
     *
     * Results are cached briefly (WeakMap + TTL + geometry fingerprint) so hover
     * thrash does not re-walk getComputedStyle ancestors every paint.
     *
     * @param {Element} element
     * @returns {{
     *   clippers: Element[],
     *   tightWrapper: Element|null
     * }}
     *   clippers — ancestors that would clip a positive outline-offset ring
     *   tightWrapper — nearest clipper nearly the same size as the target
     */
    _findFocusClipContext(element) {
      const emptyClippers = [];
      const emptyTight = null;
      if (!element || element.nodeType !== 1) {
        return { clippers: emptyClippers, tightWrapper: emptyTight };
      }
      let er;
      try {
        er = element.getBoundingClientRect();
      } catch {
        return { clippers: emptyClippers, tightWrapper: emptyTight };
      }
      if (!er || !(er.width > 0) || !(er.height > 0)) {
        return { clippers: emptyClippers, tightWrapper: emptyTight };
      }
      try {
        const cached = this._focusClipContextCache?.get?.(element);
        if (cached) {
          const age = Date.now() - (cached.ts || 0);
          const geoClose = Math.abs(cached.left - er.left) < 1 && Math.abs(cached.top - er.top) < 1 && Math.abs(cached.width - er.width) < 1 && Math.abs(cached.height - er.height) < 1;
          if (age < (this._focusClipContextTtlMs || 500) && geoClose) {
            return { clippers: cached.clippers, tightWrapper: cached.tightWrapper };
          }
        }
      } catch {
      }
      const clippers = [];
      let tightWrapper = null;
      const pad = 8;
      const needLeft = er.left - pad;
      const needTop = er.top - pad;
      const needRight = er.right + pad;
      const needBottom = er.bottom + pad;
      try {
        let n = this._composedParent(element);
        let depth = 0;
        while (n && n.nodeType === 1 && depth++ < 24) {
          if (n === document.documentElement || n === document.body) {
            n = this._composedParent(n);
            continue;
          }
          const cs = window.getComputedStyle(n);
          if (!cs) {
            n = this._composedParent(n);
            continue;
          }
          const ox = cs.overflowX;
          const oy = cs.overflowY;
          const clipsOverflow = ox && ox !== "visible" || oy && oy !== "visible";
          let cvClip = false;
          try {
            const cv = cs.contentVisibility || cs.getPropertyValue("content-visibility");
            cvClip = cv === "auto" || cv === "hidden";
          } catch {
          }
          let containClip = false;
          try {
            const contain = String(cs.contain || "");
            containClip = contain.includes("paint") || contain.includes("strict") || contain === "content" || contain.split(/\s+/).includes("content");
          } catch {
          }
          if (clipsOverflow || cvClip || containClip) {
            let ar;
            try {
              ar = n.getBoundingClientRect();
            } catch {
              clippers.push(n);
              n = this._composedParent(n);
              continue;
            }
            const clipsRing = ar.left > needLeft + 0.5 || ar.top > needTop + 0.5 || ar.right < needRight - 0.5 || ar.bottom < needBottom - 0.5;
            const similarSize = Math.abs(ar.width - er.width) < 20 && Math.abs(ar.height - er.height) < 20;
            if (!this._viewportRectsOverlap(ar, er)) {
              n = this._composedParent(n);
              continue;
            }
            if (clipsRing || cvClip && similarSize) {
              clippers.push(n);
              if (!tightWrapper && similarSize) {
                tightWrapper = n;
              }
            }
          }
          try {
            const extra = this._shadowInternalClipWrappers(n, er, needLeft, needTop, needRight, needBottom);
            for (let wi = 0; wi < extra.clippers.length; wi++) {
              clippers.push(extra.clippers[wi]);
            }
            if (!tightWrapper && extra.tightWrapper) {
              tightWrapper = extra.tightWrapper;
            }
          } catch {
          }
          n = this._composedParent(n);
        }
      } catch {
      }
      try {
        this._focusClipContextCache?.set?.(element, {
          clippers,
          tightWrapper,
          ts: Date.now(),
          left: er.left,
          top: er.top,
          width: er.width,
          height: er.height
        });
      } catch {
      }
      return { clippers, tightWrapper };
    }
    /**
     * True when an ancestor is likely to clip *outer* focus chrome.
     * @param {Element} element
     * @returns {boolean}
     */
    _isProbablyClippedByAncestorOverflow(element) {
      try {
        const ctx = this._findFocusClipContext(element);
        return !!(ctx.clippers && ctx.clippers.length);
      } catch {
        return false;
      }
    }
    /**
     * Rect-based check: would a positive outline-offset ring around the hover
     * target be clipped (or sit flush with zero room) inside a clipping parent?
     *
     * Compares `getBoundingClientRect()` of the paint target to each overflow/
     * contain clip ancestor. If the target box is inset-or-flush with a clipper
     * such that outline-offset > 0 has nowhere to paint, return true.
     *
     * This alone does **not** mean we should use a fixed overlay — inset element
     * outlines often still work (control strip segments, toolbar buttons).
     * See `_shouldUseFixedFocusOverlay`.
     *
     * @param {Element} element - hover focus target (clickable)
     * @returns {boolean}
     */
    _outerFocusRingWouldBeClipped(element) {
      if (!element || element.nodeType !== 1) return false;
      let paintEl = element;
      try {
        paintEl = this._resolveElementForFocusStyling(element) || element;
      } catch {
        paintEl = element;
      }
      let er = null;
      try {
        er = paintEl.getBoundingClientRect();
      } catch {
        return false;
      }
      if (!er || !(er.width > 0) || !(er.height > 0)) return false;
      const pad = 8;
      let ctx = { clippers: [], tightWrapper: null };
      try {
        ctx = this._findFocusClipContext(paintEl);
      } catch {
        ctx = { clippers: [], tightWrapper: null };
      }
      const clippers = ctx && ctx.clippers || [];
      if (!clippers.length) return false;
      for (let i = 0; i < clippers.length; i++) {
        const c = clippers[i];
        if (!c || c.nodeType !== 1) continue;
        let ar = null;
        try {
          ar = c.getBoundingClientRect();
        } catch {
          ar = null;
        }
        if (!ar) continue;
        const roomLeft = er.left - ar.left;
        const roomTop = er.top - ar.top;
        const roomRight = ar.right - er.right;
        const roomBottom = ar.bottom - er.bottom;
        if (roomLeft < pad - 0.5 || roomTop < pad - 0.5 || roomRight < pad - 0.5 || roomBottom < pad - 0.5) {
          return true;
        }
      }
      return false;
    }
    /**
     * True when computed overflow would clip descendants / outer decorations.
     * @param {CSSStyleDeclaration|null|undefined} cs
     * @returns {boolean}
     */
    _styleClipsOverflow(cs) {
      return !!(cs && (cs.overflow && cs.overflow !== "visible" || cs.overflowX && cs.overflowX !== "visible" || cs.overflowY && cs.overflowY !== "visible"));
    }
    /**
     * Chrome's used value for replaced elements is often `overflow: clip` even
     * when the author never clipped. That is not a wrapper clip; do not treat
     * it as one (it was sending every <img> down the A-dead → C path).
     * @param {Element|null|undefined} el
     * @param {CSSStyleDeclaration|null|undefined} cs
     * @returns {boolean}
     */
    _styleClipsSelfFor(el2, cs) {
      if (!cs) return false;
      if (el2 && this._isReplacedOrVoidElement(el2)) {
        const ox = String(cs.overflowX || cs.overflow || "");
        const oy = String(cs.overflowY || cs.overflow || "");
        const authorClip = ox && ox !== "visible" && ox !== "clip" || oy && oy !== "visible" && oy !== "clip";
        return authorClip || this._styleClipsPaintContain(cs) || this._styleClipsClipPath(cs);
      }
      return this._styleClipsSelf(cs);
    }
    /**
     * True when CSS containment clips paint/ink (including outer outlines) to the
     * padding edge. `contain: content` implies paint containment — common on
     * msn.com card shells (`div.root { contain: content }`).
     * @param {CSSStyleDeclaration|null|undefined} cs
     * @returns {boolean}
     */
    _styleClipsPaintContain(cs) {
      if (!cs) return false;
      try {
        const contain = String(cs.contain || "");
        if (!contain || contain === "none") return false;
        return contain.includes("paint") || contain.includes("strict") || contain === "content" || contain.split(/\s+/).includes("content");
      } catch {
        return false;
      }
    }
    /**
     * clip-path (other than none) clips descendants the same way overflow does.
     * Keyboard Ref keycaps use --kp-key-clip for truncated corners.
     * @param {CSSStyleDeclaration|null|undefined} cs
     * @returns {boolean}
     */
    _styleClipsClipPath(cs) {
      if (!cs) return false;
      try {
        const clip = String(cs.clipPath || cs.webkitClipPath || "");
        return !!(clip && clip !== "none");
      } catch {
        return false;
      }
    }
    _styleClipsSelf(cs) {
      return this._styleClipsOverflow(cs) || this._styleClipsPaintContain(cs) || this._styleClipsClipPath(cs);
    }
    /**
     * Parent across open shadow boundaries (parentElement, else ShadowRoot.host).
     * @param {Node|null|undefined} node
     * @returns {Element|null}
     */
    _composedParent(node) {
      if (!node || node.nodeType !== 1) return null;
      if (node.parentElement) return node.parentElement;
      try {
        const root = typeof node.getRootNode === "function" ? node.getRootNode() : null;
        if (root && typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot) {
          return root.host || null;
        }
      } catch {
      }
      return null;
    }
    /**
     * Detect clip ancestors hiding *inside* an open shadow host's own root —
     * e.g. msn.com `cs-responsive-card` shadow is `div.root{overflow:hidden} > slot`.
     * The slotted (light-DOM) content is a child of the *host* in the light tree,
     * so `getComputedStyle(host)` never sees the wrapper's `overflow`/`contain`,
     * even though it visually clips the slotted content in the composed tree.
     *
     * Only checks the shadow root's direct children (the common single-wrapper
     * pattern) — not a full recursive walk — to stay cheap on hover.
     *
     * @param {Element} host - composed ancestor being visited by _findFocusClipContext
     * @param {DOMRect} er - target element's rect
     * @param {number} needLeft
     * @param {number} needTop
     * @param {number} needRight
     * @param {number} needBottom
     * @returns {{ clippers: Element[], tightWrapper: Element|null }}
     */
    _shadowInternalClipWrappers(host, er, needLeft, needTop, needRight, needBottom) {
      const clippers = [];
      let tightWrapper = null;
      const sr = this._getOpenShadowRoot(host);
      if (!sr) return { clippers, tightWrapper };
      let kids;
      try {
        kids = sr.children;
      } catch {
        return { clippers, tightWrapper };
      }
      if (!kids) return { clippers, tightWrapper };
      for (let i = 0; i < kids.length && i < 6; i++) {
        const w = kids[i];
        if (!w || w.nodeType !== 1) continue;
        if ((w.tagName || "").toUpperCase() === "STYLE") continue;
        let wcs = null;
        try {
          wcs = window.getComputedStyle(w);
        } catch {
          wcs = null;
        }
        if (!this._styleClipsSelf(wcs)) continue;
        let war = null;
        try {
          war = w.getBoundingClientRect();
        } catch {
          war = null;
        }
        if (!war) continue;
        const clipsRing = war.left > needLeft + 0.5 || war.top > needTop + 0.5 || war.right < needRight - 0.5 || war.bottom < needBottom - 0.5;
        const similarSize = Math.abs(war.width - er.width) < 20 && Math.abs(war.height - er.height) < 20;
        if (clipsRing) {
          clippers.push(w);
          if (!tightWrapper && similarSize) tightWrapper = w;
        }
      }
      return { clippers, tightWrapper };
    }
    /**
     * True when `el` is itself an open-shadow host whose top-level shadow
     * wrapper clips (`overflow`/`contain`), even though `el`'s own light-DOM-
     * facing computed style does not. Boolean sibling of
     * `_shadowInternalClipWrappers` for callers that only need presence, not
     * outline-room rect math (e.g. `_shouldUseFixedFocusOverlay`'s selfClips).
     * @param {Element} el
     * @returns {boolean}
     */
    _hostClipsViaInternalShadowWrapper(el2) {
      const sr = this._getOpenShadowRoot(el2);
      if (!sr) return false;
      let kids;
      try {
        kids = sr.children;
      } catch {
        return false;
      }
      if (!kids) return false;
      for (let i = 0; i < kids.length && i < 6; i++) {
        const w = kids[i];
        if (!w || w.nodeType !== 1) continue;
        if ((w.tagName || "").toUpperCase() === "STYLE") continue;
        let wcs = null;
        try {
          wcs = window.getComputedStyle(w);
        } catch {
          wcs = null;
        }
        if (this._styleClipsSelf(wcs)) return true;
      }
      return false;
    }
    /**
     * True when `el` lives under a ShadowRoot (open or closed).
     * @param {Node|null|undefined} el
     * @returns {boolean}
     */
    _isInShadowTree(el2) {
      if (!el2 || typeof el2.getRootNode !== "function") return false;
      try {
        const root = el2.getRootNode();
        return !!(typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot);
      } catch {
        return false;
      }
    }
    /**
     * Open shadow root on `el`, if any.
     * @param {Element|null|undefined} el
     * @returns {ShadowRoot|null}
     */
    _getOpenShadowRoot(el2) {
      if (!el2 || el2.nodeType !== 1) return null;
      try {
        return el2.shadowRoot || null;
      } catch {
        return null;
      }
    }
    /**
     * True when an open shadow has a default (unnamed) slot that can project
     * light-DOM children. Slotless Lit hosts (archive.org media-button /
     * collection-tile) render light children with 0×0 — strategy B must not
     * mount there.
     * @param {ShadowRoot|null|undefined} shadowRoot
     * @returns {boolean}
     */
    _shadowHasDefaultSlot(shadowRoot) {
      if (!shadowRoot || typeof shadowRoot.querySelectorAll !== "function") return false;
      try {
        const slots = shadowRoot.querySelectorAll("slot");
        for (let i = 0; i < slots.length; i++) {
          const slot = slots[i];
          if (!slot) continue;
          const name = slot.getAttribute && slot.getAttribute("name") || "";
          if (!name) return true;
        }
      } catch {
      }
      return false;
    }
    /**
     * True when appending a child to `host` will participate in layout/paint.
     * Light DOM elements: yes. Open shadow hosts: only with a default slot.
     * @param {Element|null|undefined} host
     * @returns {boolean}
     */
    _canMountVisibleChildOnHost(host) {
      if (!host || host.nodeType !== 1) return false;
      if (this._isReplacedOrVoidElement(host)) return false;
      if (typeof host.appendChild !== "function") return false;
      const sr = this._getOpenShadowRoot(host);
      if (!sr) return true;
      return this._shadowHasDefaultSlot(sr);
    }
    /**
     * Push light children and (for open shadow hosts) top-level shadow children
     * onto `queue`, skipping KeyPilot injected style nodes.
     * @param {Element} el
     * @param {Element[]} queue
     */
    _enqueueShadowPiercingChildren(el2, queue) {
      if (!el2 || el2.nodeType !== 1 || !queue) return;
      try {
        const kids = el2.children;
        if (kids) {
          for (let i = 0; i < kids.length; i++) {
            if (kids[i]?.nodeType === 1) queue.push(kids[i]);
          }
        }
      } catch {
      }
      const sr = this._getOpenShadowRoot(el2);
      if (!sr) return;
      try {
        const skids = sr.children;
        if (!skids) return;
        for (let i = 0; i < skids.length; i++) {
          const k = skids[i];
          if (!k || k.nodeType !== 1) continue;
          try {
            if (k.id === "keypilot-shadow-styles" || k.id === "kp-probe-style") continue;
            if ((k.tagName || "").toUpperCase() === "STYLE" && typeof k.id === "string" && k.id.startsWith("keypilot")) {
              continue;
            }
          } catch {
          }
          queue.push(k);
        }
      } catch {
      }
    }
    /**
     * Inside an open-shadow host, find a non-replaced element that can hold the
     * in-target ring (strategy B). Prefers the largest visible box.
     * @param {Element} host
     * @returns {Element|null}
     */
    _findInShadowInTargetMount(host) {
      const sr = this._getOpenShadowRoot(host);
      if (!sr) return null;
      let best = null;
      let bestArea = 0;
      let visited = 0;
      const maxNodes = 48;
      const queue = [];
      this._enqueueShadowPiercingChildren(host, queue);
      while (queue.length && visited < maxNodes) {
        const cur = queue.shift();
        if (!cur || cur.nodeType !== 1) continue;
        visited++;
        if (!this._isReplacedOrVoidElement(cur) && this._canMountVisibleChildOnHost(cur)) {
          let r = null;
          try {
            r = cur.getBoundingClientRect();
          } catch {
            r = null;
          }
          if (r && r.width >= 8 && r.height >= 8) {
            const area = r.width * r.height;
            if (area > bestArea) {
              bestArea = area;
              best = cur;
            }
          }
        }
        this._enqueueShadowPiercingChildren(cur, queue);
      }
      return best;
    }
    /**
     * Preferred outer outline-offset (px) when clip ancestors leave enough room.
     * Driven by Click Mode → Advanced → Padding (historical path-A default: 2).
     */
    _preferredFocusOutlineOffsetPx() {
      try {
        return this._getClickModeSettings().focusPadding;
      } catch {
        return 2;
      }
    }
    /**
     * Absolute/fixed hit-link flush inside an overflow-hidden thumb frame
     * (Rumble `a.videostream__link` in `.thumbnail__thumb--live`). Graded inset
     * on the link sits under the frame's border (e.g. live red 2px). Paint the
     * frame instead so path A can use a normal outer outline around it.
     * @param {Element|null|undefined} el
     * @returns {Element|null}
     */
    _resolveAbsoluteClipFramePaintHost(el2) {
      if (!el2 || el2.nodeType !== 1) return null;
      let pos = "";
      try {
        pos = String(window.getComputedStyle(el2).position || "");
      } catch {
        return null;
      }
      if (pos !== "absolute" && pos !== "fixed") return null;
      let ir = null;
      try {
        ir = el2.getBoundingClientRect();
      } catch {
        ir = null;
      }
      if (!ir || ir.width < 16 || ir.height < 16) return null;
      let p = null;
      try {
        p = this._composedParent(el2);
      } catch {
        p = el2.parentElement;
      }
      let depth = 0;
      while (p && p.nodeType === 1 && depth++ < 3) {
        if (p === document.body || p === document.documentElement) break;
        let cs = null;
        try {
          cs = window.getComputedStyle(p);
        } catch {
          cs = null;
        }
        if (!cs || !this._styleClipsSelf(cs)) {
          try {
            p = this._composedParent(p);
          } catch {
            p = p.parentElement;
          }
          continue;
        }
        let pr = null;
        try {
          pr = p.getBoundingClientRect();
        } catch {
          pr = null;
        }
        if (!pr || pr.width < 16 || pr.height < 16) {
          try {
            p = this._composedParent(p);
          } catch {
            p = p.parentElement;
          }
          continue;
        }
        if (pr.width > ir.width + 24 || pr.height > ir.height + 24) break;
        if (pr.width + 2 < ir.width || pr.height + 2 < ir.height) {
          try {
            p = this._composedParent(p);
          } catch {
            p = p.parentElement;
          }
          continue;
        }
        return p;
      }
      return null;
    }
    /**
     * Minimum free space outside the border box across clipping ancestors.
     * Infinity when there are no clippers (outer ring unconstrained).
     *
     * @param {Element} paintEl
     * @returns {number}
     */
    _minFocusOutlineRoomPx(paintEl) {
      if (!paintEl || paintEl.nodeType !== 1) return Infinity;
      let er = null;
      try {
        er = paintEl.getBoundingClientRect();
      } catch {
        return Infinity;
      }
      if (!er || !(er.width > 0) || !(er.height > 0)) return Infinity;
      let clippers = [];
      try {
        const ctx = this._findFocusClipContext(paintEl);
        clippers = ctx && ctx.clippers || [];
      } catch {
        clippers = [];
      }
      let minRoom = Infinity;
      try {
        const vw = window.innerWidth || 0;
        const vh = window.innerHeight || 0;
        if (vw > 0 && vh > 0) {
          minRoom = Math.min(er.left, er.top, vw - er.right, vh - er.bottom);
        }
      } catch {
      }
      if (!clippers.length) {
        return Number.isFinite(minRoom) ? minRoom : Infinity;
      }
      for (let i = 0; i < clippers.length; i++) {
        const c = clippers[i];
        if (!c || c.nodeType !== 1) continue;
        let ar = null;
        try {
          ar = c.getBoundingClientRect();
        } catch {
          ar = null;
        }
        if (!ar) continue;
        if (!this._viewportRectsOverlap(ar, er)) continue;
        const roomLeft = er.left - ar.left;
        const roomTop = er.top - ar.top;
        const roomRight = ar.right - er.right;
        const roomBottom = ar.bottom - er.bottom;
        minRoom = Math.min(minRoom, roomLeft, roomTop, roomRight, roomBottom);
      }
      return Number.isFinite(minRoom) ? minRoom : Infinity;
    }
    /**
     * Graded outline-offset (px) for path A.
     *
     * Outer ring needs roughly (offset + stroke) outside the border box.
     * Given free room R outside the box to the nearest clip edge:
     *   offset = clamp(R - stroke, -stroke, preferredOuter)
     *
     * Examples (stroke 3, preferred outer 2):
     *   R ≥ 5  → +2 (full outer)
     *   R = 4  → +1
     *   R = 1  → -2 (mild inset; 1px bleed does not force full -3)
     *   R ≤ 0  → -3 (full inset)
     *
     * @param {Element} paintEl
     * @param {number} [ringWidthPx]
     * @returns {number}
     */
    _computeGradedFocusOutlineOffset(paintEl, ringWidthPx = 3) {
      const preferred = this._preferredFocusOutlineOffsetPx();
      const stroke = Math.min(Math.max(Number(ringWidthPx) || 3, 1), 16);
      if (!(FEATURE_FLAGS && FEATURE_FLAGS.ENABLE_FOCUS_CLIP_INSET)) {
        return preferred;
      }
      if (!paintEl || paintEl.nodeType !== 1) return preferred;
      const room = this._minFocusOutlineRoomPx(paintEl);
      if (!Number.isFinite(room) || room === Infinity) {
        return preferred;
      }
      const available = room - 0.5;
      let offset = available - stroke;
      if (offset > preferred) offset = preferred;
      if (offset < -stroke) offset = -stroke;
      const rounded = Math.round(offset * 100) / 100;
      return rounded;
    }
    /**
     * True when graded path-A offset paints **inside** the border box (negative
     * offset). Outer/zero offset is not covered by full-bleed children; negative
     * inset can be. Used to gate escape hatch B/C.
     *
     * @param {Element} paintEl
     * @param {number} [ringWidthPx]
     * @returns {boolean}
     */
    _wouldUseInsetFocusOutline(paintEl, ringWidthPx) {
      if (!paintEl || paintEl.nodeType !== 1) return false;
      if (!(FEATURE_FLAGS && FEATURE_FLAGS.ENABLE_FOCUS_CLIP_INSET)) {
        return false;
      }
      let stroke = ringWidthPx;
      if (stroke == null || !Number.isFinite(Number(stroke))) {
        try {
          stroke = this._getClickModeSettings().rectangleThickness;
        } catch {
          stroke = 3;
        }
      }
      const offset = this._computeGradedFocusOutlineOffset(paintEl, stroke);
      return offset < -0.25;
    }
    /**
     * True when strategy A (CSS outline on the node) can still show a ring.
     * Used so B-fail / Auto B→C does not jump to C for ordinary boxes (Settings
     * fieldset, KP chrome) that never needed a body-fixed overlay.
     * @param {Element|null|undefined} element
     * @returns {boolean}
     */
    _strategyAIsViable(element) {
      if (!element || element.nodeType !== 1) return false;
      let paintEl = element;
      try {
        paintEl = this._resolveFocusPaintElement(element) || element;
      } catch {
        paintEl = element;
      }
      try {
        if (this._positiveClientRectCount(paintEl) >= 2) return false;
        const shell = this._resolveMediaTextCardShell(element);
        if (shell && this._positiveClientRectCount(shell) >= 2) return false;
      } catch {
      }
      try {
        if (this._shouldUseFixedFocusOverlay(element)) return false;
      } catch {
      }
      try {
        this._ensureStylesForElement(element);
      } catch {
      }
      return true;
    }
    /**
     * True when **element-level** focus paint (strategy A) cannot show a visible
     * ring, so we must use an escape hatch (strategy B in-target, else C body fixed).
     *
     * Policy (outline-first = A preferred):
     * - Outer outline clipped by a parent alone, target still fully inside the
     *   clipper (room ≥ 0) → **graded element inset** on A (ENABLE_FOCUS_CLIP_INSET).
     *   Example: control-strip buttons inside `overflow:hidden` shells. Body fixed
     *   (C) would also sit under high z-index KP chrome and stay invisible.
     * - Target **overflows** a clipping ancestor (negative free room, e.g. Ars
     *   headline with `-mt-1` above `overflow-hidden` card shell) → graded inset
     *   cannot expose that edge; use B/C.
     * - Target clips itself **and** is covered by full-bleed media/pseudos →
     *   inset outline is under the cover; use B/C (TNW cards, etc.).
     * - Same when cover is only an **edge media strip** (msn.com card image on the
     *   top half): inset outline shows on the text half and vanishes under media.
     * - Target does not clip, but a full-size child stacking surface would cover
     *   an **inset** outline (e.g. newtab top-site tiles inside a scroller that
     *   forces inset). If outer outline still has room, stay on A — do not
     *   treat "has media child" alone as failure (ganjingworld thumbnails).
     *
     * @param {Element} element
     * @returns {boolean}
     */
    _shouldUseFixedFocusOverlay(element) {
      if (!element || element.nodeType !== 1) return false;
      let paintEl = element;
      try {
        paintEl = this._resolveElementForFocusStyling(element) || element;
      } catch {
        paintEl = element;
      }
      try {
        const frame = this._resolveAbsoluteClipFramePaintHost(paintEl);
        if (frame) paintEl = frame;
      } catch {
      }
      let er = null;
      try {
        er = paintEl.getBoundingClientRect();
      } catch {
        return false;
      }
      if (!er || !(er.width > 0) || !(er.height > 0)) return false;
      try {
        const shell = this._resolveMediaTextCardShell(paintEl) || this._resolveMediaTextCardShell(element);
        if (shell && this._positiveClientRectCount(shell) >= 2) {
          return true;
        }
      } catch {
      }
      try {
        const room = this._minFocusOutlineRoomPx(paintEl);
        if (Number.isFinite(room) && room < -0.5) {
          return true;
        }
      } catch {
      }
      try {
        if (this._isReplacedOrVoidElement(paintEl) && this._isMediaLikeCoverElement(paintEl) && this._wouldUseInsetFocusOutline(paintEl)) {
          return true;
        }
      } catch {
      }
      let selfClips = false;
      try {
        selfClips = this._styleClipsSelfFor(paintEl, window.getComputedStyle(paintEl));
      } catch {
      }
      if (!selfClips) {
        try {
          selfClips = this._hostClipsViaInternalShadowWrapper(paintEl);
        } catch {
        }
      }
      if (selfClips && (this._hasFullBleedCoveringContent(paintEl, er) || this._hasEdgeFlushMediaCover(paintEl, er))) {
        return true;
      }
      if (this._hasObscuringFullBleedChild(paintEl, er) || this._hasEdgeFlushMediaCover(paintEl, er)) {
        if (this._wouldUseInsetFocusOutline(paintEl)) {
          return true;
        }
      }
      return false;
    }
    /**
     * Card-sized box with a flush media strip (image on top / side rail) and a
     * complementary text/headline region.
     *
     * Used only when the hover target itself is (roughly) that card-sized
     * clickable — e.g. Tom's Hardware `a.article-link` wrapping image+title.
     * Separate image / headline `<a>`s that share a URL (arstechnica.com cards)
     * must keep their own paint boxes; see `_focusFillsMediaTextCardShell`.
     * @param {Element} el
     * @returns {boolean}
     */
    _isMediaTextSplitCard(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      let box = null;
      try {
        box = this._visualContentUnionRect(el2);
      } catch {
        box = null;
      }
      if (!box) {
        try {
          box = el2.getBoundingClientRect();
        } catch {
          box = null;
        }
      }
      if (!box || box.width < 160 || box.height < 140) return false;
      if (box.width > 920 || box.height > 780) return false;
      try {
        const vw = window.innerWidth || 0;
        if (vw > 0 && box.width > vw * 0.72 && box.height > 420) return false;
      } catch {
      }
      let hasStrip = false;
      try {
        hasStrip = this._hasEdgeFlushMediaCover(el2, box);
      } catch {
        hasStrip = false;
      }
      if (!hasStrip) {
        try {
          hasStrip = this._hasDescendantMediaStrip(el2, box);
        } catch {
          hasStrip = false;
        }
      }
      if (!hasStrip) return false;
      let text = "";
      try {
        text = String(el2.innerText || el2.textContent || "").replace(/\s+/g, " ").trim();
      } catch {
        text = "";
      }
      return text.length >= 8;
    }
    /**
     * True when `shell` contains a navigable/action dest different from `fromEl`.
     * Rumble video cards: thumbnail+title share the watch URL, but the channel
     * link is a separate F-target — do not paint the whole card for that hover.
     * @param {Element} shell
     * @param {Element} fromEl
     * @returns {boolean}
     */
    _mediaTextShellHasCompetingDest(shell, fromEl) {
      if (!shell || !fromEl || shell === fromEl) return false;
      let fromId = "";
      try {
        fromId = resolveActivationIdentity(fromEl);
      } catch {
        fromId = "";
      }
      if (!fromId) return false;
      let links = [];
      try {
        links = Array.from(shell.querySelectorAll('a[href], [role="link"]'));
      } catch {
        return false;
      }
      for (let i = 0; i < links.length && i < 24; i++) {
        const a = links[i];
        if (!a || a === fromEl) continue;
        let id = "";
        try {
          id = resolveActivationIdentity(a);
        } catch {
          id = "";
        }
        if (id && id !== fromId) return true;
      }
      return false;
    }
    /**
     * True when `focusEl` already paints most of `shell` — i.e. a single
     * card-sized clickable wrapping image+text, not a nested headline/thumb link.
     * @param {Element} focusEl
     * @param {Element} shell
     * @returns {boolean}
     */
    _focusFillsMediaTextCardShell(focusEl, shell) {
      if (!focusEl || !shell || focusEl.nodeType !== 1 || shell.nodeType !== 1) return false;
      if (focusEl === shell) return true;
      let fr = null;
      let sr = null;
      try {
        fr = this._visualContentUnionRect(focusEl) || focusEl.getBoundingClientRect();
      } catch {
        fr = null;
      }
      try {
        sr = this._visualContentUnionRect(shell) || shell.getBoundingClientRect();
      } catch {
        sr = null;
      }
      if (!fr || !sr || !(fr.width > 0) || !(fr.height > 0) || !(sr.width > 0) || !(sr.height > 0)) {
        return false;
      }
      const fArea = fr.width * fr.height;
      const sArea = sr.width * sr.height;
      if (fArea < sArea * 0.78) return false;
      if (fr.width < sr.width * 0.82) return false;
      if (fr.height < sr.height * 0.78) return false;
      return true;
    }
    /**
     * Smallest ancestor (incl. `el`) that is an image+text split card **and** is
     * essentially the same box as the hover target (wrapping card link).
     * Stops when the box balloons into a carousel / multi-card row.
     * Does not promote nested headline/image links up to the outer card.
     * @param {Element|null|undefined} el
     * @returns {Element|null}
     */
    _resolveMediaTextCardShell(el2) {
      if (!el2 || el2.nodeType !== 1) return null;
      let found = null;
      try {
        if (this._isMediaTextSplitCard(el2) && !this._mediaTextShellHasCompetingDest(el2, el2)) {
          found = el2;
        }
      } catch {
      }
      if (!found) {
        let ir = null;
        try {
          ir = this._visualContentUnionRect(el2) || el2.getBoundingClientRect();
        } catch {
          ir = null;
        }
        if (!ir || ir.width < 4 || ir.height < 4) return null;
        let p = null;
        try {
          p = this._composedParent(el2);
        } catch {
          p = el2.parentElement;
        }
        let depth = 0;
        while (p && p.nodeType === 1 && depth++ < 10) {
          if (p === document.body || p === document.documentElement) break;
          let r = null;
          try {
            r = p.getBoundingClientRect();
          } catch {
            r = null;
          }
          if (!r || r.width < 8 || r.height < 8) {
            try {
              p = this._composedParent(p);
            } catch {
              p = p.parentElement;
            }
            continue;
          }
          if (r.width > ir.width * 1.4 + 48) break;
          if (r.height > ir.height * 1.55 + 32) break;
          if (r.height > 780) break;
          try {
            if (this._isMediaTextSplitCard(p) && !this._mediaTextShellHasCompetingDest(p, el2)) {
              found = p;
              break;
            }
          } catch {
          }
          try {
            p = this._composedParent(p);
          } catch {
            p = p.parentElement;
          }
        }
      }
      if (!found) return null;
      const shell = this._liftMediaTextCardPaintHost(found);
      if (!shell) return null;
      try {
        if (!this._focusFillsMediaTextCardShell(el2, shell)) return null;
      } catch {
        return null;
      }
      return shell;
    }
    /**
     * Block/flex/grid (or inline-block) host that can take an absolute ring.
     * @param {Element} el
     * @returns {boolean}
     */
    _isBlockishPaintHost(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      try {
        const d = String(window.getComputedStyle(el2).display || "");
        if (!d || d === "none" || d === "contents" || d === "inline") return false;
        return true;
      } catch {
        return false;
      }
    }
    /**
     * Inline stacked cards (Tom's Hardware `a.article-link`) fragment into
     * image + headline boxes. Strategy B needs a same-column block wrapper
     * (`div.feature-block-item-wrapper`) as the containing block.
     * @param {Element} shell
     * @returns {Element}
     */
    _liftMediaTextCardPaintHost(shell) {
      if (!shell || shell.nodeType !== 1) return shell;
      try {
        if (this._isBlockishPaintHost(shell) && !this._isFragmentedInlineFocusTarget(shell) && this._canMountVisibleChildOnHost(shell)) {
          return shell;
        }
      } catch {
      }
      let ir = null;
      try {
        ir = this._visualContentUnionRect(shell) || shell.getBoundingClientRect();
      } catch {
        ir = null;
      }
      if (!ir || ir.width < 8 || ir.height < 8) return shell;
      let p = null;
      try {
        p = this._composedParent(shell);
      } catch {
        p = shell.parentElement;
      }
      let depth = 0;
      while (p && p.nodeType === 1 && depth++ < 6) {
        if (p === document.body || p === document.documentElement) break;
        let r = null;
        try {
          r = p.getBoundingClientRect();
        } catch {
          r = null;
        }
        if (!r || r.width < 8 || r.height < 8) {
          try {
            p = this._composedParent(p);
          } catch {
            p = p.parentElement;
          }
          continue;
        }
        if (r.width > ir.width * 1.12 + 12) break;
        if (r.height > ir.height * 1.25 + 24) break;
        try {
          if (this._isBlockishPaintHost(p) && !this._isFragmentedInlineFocusTarget(p) && this._canMountVisibleChildOnHost(p)) {
            return p;
          }
        } catch {
        }
        try {
          p = this._composedParent(p);
        } catch {
          p = p.parentElement;
        }
      }
      return shell;
    }
    /**
     * Media-like node that can paint over an inset focus outline.
     * @param {Element} el
     * @returns {boolean}
     */
    _isMediaLikeCoverElement(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      const tag = (el2.tagName || "").toUpperCase();
      if (tag === "IMG" || tag === "VIDEO" || tag === "PICTURE" || tag === "CANVAS" || tag === "SVG") {
        return true;
      }
      try {
        const cs = window.getComputedStyle(el2);
        const bg = cs && cs.backgroundImage;
        if (bg && bg !== "none") return true;
      } catch {
      }
      return false;
    }
    /**
     * True when a media surface covers a substantial edge strip of `el` (e.g.
     * card hero image on the top ~half). Full-bleed (85%×85%) is handled by
     * `_hasFullBleedCoveringContent`; this catches the common "media header"
     * layout where strategy A inset outlines only survive on the text half.
     *
     * Walks direct shadow-piercing children plus one level into near-full
     * wrappers (card → `.root` → `img.media`). Does **not** alone force B/C —
     * callers still require self-clip or graded inset.
     *
     * @param {Element} el
     * @param {DOMRect|ClientRect} [er]
     * @returns {boolean}
     */
    _hasEdgeFlushMediaCover(el2, er) {
      if (!el2 || el2.nodeType !== 1) return false;
      let box = er;
      try {
        if (!box) box = el2.getBoundingClientRect();
      } catch {
        return false;
      }
      if (!box || !(box.width > 0) || !(box.height > 0)) return false;
      const candidates = [];
      try {
        this._enqueueShadowPiercingChildren(el2, candidates);
        const nDirect = candidates.length;
        for (let i = 0; i < nDirect && i < 12; i++) {
          const wrap = candidates[i];
          if (!wrap || wrap.nodeType !== 1) continue;
          let wr = null;
          try {
            wr = wrap.getBoundingClientRect();
          } catch {
            wr = null;
          }
          if (!wr || !(wr.width > 0) || !(wr.height > 0)) {
            const wrapTag2 = String(wrap.tagName || "").toUpperCase();
            if (wrapTag2 === "PICTURE" || wrapTag2 === "FIGURE") {
              this._enqueueShadowPiercingChildren(wrap, candidates);
            }
            continue;
          }
          if (wr.width >= box.width * 0.85 && wr.height >= box.height * 0.28) {
            this._enqueueShadowPiercingChildren(wrap, candidates);
          }
          const wrapTag = String(wrap.tagName || "").toUpperCase();
          if (wrapTag === "PICTURE" || wrapTag === "FIGURE" || wrapTag === "A") {
            this._enqueueShadowPiercingChildren(wrap, candidates);
          }
        }
      } catch {
      }
      const edgeTol = 3;
      for (let i = 0; i < candidates.length && i < 36; i++) {
        const child = candidates[i];
        if (!child || child.nodeType !== 1) continue;
        if (!this._isMediaLikeCoverElement(child)) continue;
        let cr = null;
        try {
          cr = child.getBoundingClientRect();
        } catch {
          cr = null;
        }
        if (!cr || !(cr.width > 8) || !(cr.height > 8)) continue;
        const fracW = cr.width / box.width;
        const fracH = cr.height / box.height;
        const majorSpan = fracW >= 0.85 || fracH >= 0.85;
        const stripDepth = fracW >= 0.28 && fracH >= 0.28;
        if (!majorSpan || !stripDepth) continue;
        if (fracW >= 0.85 && fracH >= 0.85) continue;
        const flushTop = Math.abs(cr.top - box.top) <= edgeTol;
        const flushBottom = Math.abs(cr.bottom - box.bottom) <= edgeTol;
        const flushLeft = Math.abs(cr.left - box.left) <= edgeTol;
        const flushRight = Math.abs(cr.right - box.right) <= edgeTol;
        const topCap = flushTop && flushLeft && flushRight;
        const bottomCap = flushBottom && flushLeft && flushRight;
        const leftRail = flushLeft && flushTop && flushBottom;
        const rightRail = flushRight && flushTop && flushBottom;
        if (topCap || bottomCap || leftRail || rightRail) return true;
      }
      return false;
    }
    /**
     * Deeper strip detector for stacked cards whose media sits more than one
     * wrapper below the visual shell (0x0 picture, NVIDIA teaser image
     * link). Cheap query of replaced media; same flush-cap geometry as
     * `_hasEdgeFlushMediaCover`.
     * @param {Element} el
     * @param {DOMRect|ClientRect} [er]
     * @returns {boolean}
     */
    _hasDescendantMediaStrip(el2, er) {
      if (!el2 || el2.nodeType !== 1) return false;
      let box = er;
      try {
        if (!box) box = el2.getBoundingClientRect();
      } catch {
        return false;
      }
      if (!box || !(box.width > 0) || !(box.height > 0)) return false;
      const media = [];
      try {
        const nodes = el2.querySelectorAll("img, video, canvas, picture");
        for (let i = 0; i < nodes.length && media.length < 20; i++) {
          const n = nodes[i];
          if (!n || n.nodeType !== 1) continue;
          const tag = String(n.tagName || "").toUpperCase();
          if (tag === "PICTURE") {
            try {
              const img = n.querySelector("img");
              if (img) media.push(img);
            } catch {
            }
            continue;
          }
          media.push(n);
        }
      } catch {
      }
      const edgeTol = 8;
      for (let i = 0; i < media.length; i++) {
        const child = media[i];
        let cr = null;
        try {
          cr = child.getBoundingClientRect();
        } catch {
          cr = null;
        }
        if (!cr || !(cr.width > 8) || !(cr.height > 8)) continue;
        const fracW = cr.width / box.width;
        const fracH = cr.height / box.height;
        const majorSpan = fracW >= 0.82 || fracH >= 0.82;
        const stripDepth = fracW >= 0.28 && fracH >= 0.28;
        if (!majorSpan || !stripDepth) continue;
        if (fracW >= 0.85 && fracH >= 0.85) continue;
        const flushTop = Math.abs(cr.top - box.top) <= edgeTol;
        const flushBottom = Math.abs(cr.bottom - box.bottom) <= edgeTol;
        const flushLeft = Math.abs(cr.left - box.left) <= edgeTol;
        const flushRight = Math.abs(cr.right - box.right) <= edgeTol;
        const topCap = flushTop && flushLeft && flushRight;
        const bottomCap = flushBottom && flushLeft && flushRight;
        const leftRail = flushLeft && flushTop && flushBottom;
        const rightRail = flushRight && flushTop && flushBottom;
        if (topCap || bottomCap || leftRail || rightRail) return true;
      }
      return false;
    }
    /**
     * True when absolute/fixed children or full-size pseudos cover most of `el`,
     * so an inset outline paints under (or is obscured by) that content.
     * @param {Element} el
     * @param {DOMRect|ClientRect} [er]
     * @returns {boolean}
     */
    _hasFullBleedCoveringContent(el2, er) {
      if (!el2 || el2.nodeType !== 1) return false;
      let box = er;
      try {
        if (!box) box = el2.getBoundingClientRect();
      } catch {
        return false;
      }
      if (!box || !(box.width > 0) || !(box.height > 0)) return false;
      const nearlyFills = (w, h) => w >= box.width * 0.85 && h >= box.height * 0.85;
      try {
        for (const pseudo of [":before", ":after"]) {
          const pcs = window.getComputedStyle(el2, pseudo);
          if (!pcs || !pcs.content || pcs.content === "none") continue;
          if (pcs.position !== "absolute" && pcs.position !== "fixed") continue;
          const w = parseFloat(pcs.width);
          const h = parseFloat(pcs.height);
          if (Number.isFinite(w) && Number.isFinite(h) && nearlyFills(w, h)) {
            return true;
          }
          if (pcs.content === '""' || pcs.content === "''") {
            const top = parseFloat(pcs.top);
            const left = parseFloat(pcs.left);
            const right = parseFloat(pcs.right);
            const bottom = parseFloat(pcs.bottom);
            const hasInset = (Number.isFinite(top) || Number.isFinite(bottom)) && (Number.isFinite(left) || Number.isFinite(right));
            if (hasInset) return true;
          }
        }
      } catch {
      }
      try {
        const kids = [];
        this._enqueueShadowPiercingChildren(el2, kids);
        const nDirect = kids.length;
        for (let i = 0; i < nDirect && i < 12; i++) {
          const wrap = kids[i];
          if (!wrap || wrap.nodeType !== 1) continue;
          let wr = null;
          try {
            wr = wrap.getBoundingClientRect();
          } catch {
            wr = null;
          }
          if (wr && nearlyFills(wr.width, wr.height)) {
            this._enqueueShadowPiercingChildren(wrap, kids);
            continue;
          }
          const wrapTag = String(wrap.tagName || "").toUpperCase();
          if (wrapTag === "A" || wrapTag === "PICTURE" || wrapTag === "FIGURE" || !wr || !(wr.width > 2) || !(wr.height > 2)) {
            this._enqueueShadowPiercingChildren(wrap, kids);
          }
        }
        for (let i = 0; i < kids.length && i < 36; i++) {
          const child = kids[i];
          if (!child || child.nodeType !== 1) continue;
          let cr = null;
          try {
            cr = child.getBoundingClientRect();
          } catch {
            cr = null;
          }
          if (!cr || !nearlyFills(cr.width, cr.height)) continue;
          let cs = null;
          try {
            cs = window.getComputedStyle(child);
          } catch {
            cs = null;
          }
          if (!cs) continue;
          if (cs.position === "absolute" || cs.position === "fixed") return true;
          if (this._isMediaLikeCoverElement(child)) return true;
        }
      } catch {
      }
      return false;
    }
    /**
     * True when a direct child fills most of `el` and would paint over an inset
     * outline applied to `el` (stacking-context / clipped media surface).
     *
     * Pattern: clickable wrapper (overflow:visible) > visual tile
     * (overflow:hidden, isolation:isolate, full-bleed ::before / img).
     *
     * Also considers top-level open-shadow children (archive.org / MSN hosts
     * often have empty light DOM with the visual tree only in shadow).
     *
     * @param {Element} el
     * @param {DOMRect|ClientRect} [er]
     * @returns {boolean}
     */
    _hasObscuringFullBleedChild(el2, er) {
      if (!el2 || el2.nodeType !== 1) return false;
      let box = er;
      try {
        if (!box) box = el2.getBoundingClientRect();
      } catch {
        return false;
      }
      if (!box || !(box.width > 0) || !(box.height > 0)) return false;
      const nearlyFills = (w, h) => w >= box.width * 0.85 && h >= box.height * 0.85;
      try {
        const kids = [];
        this._enqueueShadowPiercingChildren(el2, kids);
        const nDirect = kids.length;
        for (let i = 0; i < nDirect && i < 12; i++) {
          const wrap = kids[i];
          if (!wrap || wrap.nodeType !== 1) continue;
          const wrapTag = String(wrap.tagName || "").toUpperCase();
          let wr = null;
          try {
            wr = wrap.getBoundingClientRect();
          } catch {
            wr = null;
          }
          if (wrapTag === "A" || wrapTag === "PICTURE" || wrapTag === "FIGURE" || !wr || !(wr.width > 2) || !(wr.height > 2)) {
            this._enqueueShadowPiercingChildren(wrap, kids);
          }
        }
        if (!kids.length) return false;
        for (let i = 0; i < kids.length; i++) {
          const child = kids[i];
          if (!child || child.nodeType !== 1) continue;
          let cr = null;
          try {
            cr = child.getBoundingClientRect();
          } catch {
            cr = null;
          }
          if (!cr || !nearlyFills(cr.width, cr.height)) continue;
          let cs = null;
          try {
            cs = window.getComputedStyle(child);
          } catch {
            cs = null;
          }
          if (!cs) continue;
          const childClips = this._styleClipsSelf(cs);
          const stacks = cs.isolation === "isolate" || cs.transform && cs.transform !== "none" || cs.filter && cs.filter !== "none" || cs.opacity !== "" && Number(cs.opacity) < 1 || cs.position !== "static" && cs.zIndex !== "auto" || typeof cs.willChange === "string" && /(?:^|,\s*)(transform|opacity|filter|isolation)(?:\s*,|$)/i.test(cs.willChange);
          if (cs.position === "absolute" || cs.position === "fixed") {
            return true;
          }
          if (childClips && this._hasFullBleedCoveringContent(child, cr)) {
            return true;
          }
          if (this._isMediaLikeCoverElement(child)) {
            return true;
          }
          if (stacks && (childClips || this._hasFullBleedCoveringContent(child, cr))) {
            return true;
          }
        }
      } catch {
      }
      return false;
    }
    /**
     * Tags that cannot accept element children (replaced / void). Strategy B
     * (in-target ring) cannot mount inside these — fall back to strategy C.
     * @param {Element|null|undefined} el
     * @returns {boolean}
     */
    _isReplacedOrVoidElement(el2) {
      if (!el2 || el2.nodeType !== 1) return true;
      const tag = (el2.tagName || "").toUpperCase();
      return tag === "IMG" || tag === "VIDEO" || tag === "AUDIO" || tag === "CANVAS" || tag === "IFRAME" || tag === "EMBED" || tag === "OBJECT" || tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || tag === "OPTION" || tag === "BR" || tag === "HR" || tag === "SOURCE" || tag === "TRACK" || tag === "AREA" || tag === "COL" || tag === "SVG" || // prefer not to inject under SVG unless needed
      tag === "MATH";
    }
    /**
     * Dedicated full-bleed mount layer as last child of an open ShadowRoot.
     * Sized to the shadow host; ring paints above all shadow content.
     * @param {ShadowRoot} shadowRoot
     * @returns {Element|null}
     */
    _ensureShadowRootRingHost(shadowRoot) {
      if (!shadowRoot || typeof shadowRoot.appendChild !== "function") return null;
      const ceHost = shadowRoot.host;
      if (!ceHost || ceHost.nodeType !== 1) return null;
      try {
        const cs = window.getComputedStyle(ceHost);
        if (cs && cs.position === "static") {
          if (!this._inTargetHostPosRestore || this._inTargetHostPosRestore.host !== ceHost) {
            this._inTargetHostPosRestore = {
              host: ceHost,
              prev: ceHost.style.position || ""
            };
            ceHost.style.position = "relative";
          }
        }
      } catch {
      }
      let mount = null;
      try {
        const kids = shadowRoot.children;
        if (kids) {
          for (let i = 0; i < kids.length; i++) {
            const k = kids[i];
            if (k && k.nodeType === 1 && k.getAttribute?.("data-kp-shadow-b-host") === "1") {
              mount = k;
              break;
            }
          }
        }
      } catch {
      }
      if (!mount) {
        try {
          mount = document.createElement("div");
          mount.setAttribute("data-kp-shadow-b-host", "1");
          mount.setAttribute("aria-hidden", "true");
          mount.style.setProperty("position", "absolute", "important");
          mount.style.setProperty("inset", "0", "important");
          mount.style.setProperty("box-sizing", "border-box", "important");
          mount.style.setProperty("pointer-events", "none", "important");
          mount.style.setProperty("margin", "0", "important");
          mount.style.setProperty("padding", "0", "important");
          mount.style.setProperty("border", "0", "important");
          mount.style.setProperty("background", "transparent", "important");
          mount.style.setProperty("overflow", "visible", "important");
          mount.style.setProperty("z-index", "2147483000", "important");
        } catch {
          return null;
        }
      }
      try {
        if (mount.parentNode !== shadowRoot || shadowRoot.lastElementChild !== mount) {
          shadowRoot.appendChild(mount);
        }
      } catch {
        return null;
      }
      return mount;
    }
    /**
     * True when candidate rect is not much larger than the focus target rect.
     * Prevents strategy B from mounting on a whole list/panel shadow host when
     * focusEl is a small row link (archive.org "Archive News").
     * @param {DOMRect|ClientRect|null|undefined} candidate
     * @param {DOMRect|ClientRect|null|undefined} focusRect
     * @returns {boolean}
     */
    _inTargetHostSizeOk(candidate, focusRect) {
      if (!candidate || !(candidate.width > 0) || !(candidate.height > 0)) return false;
      if (!focusRect || !(focusRect.width > 0) || !(focusRect.height > 0)) return true;
      const fArea = focusRect.width * focusRect.height;
      const cArea = candidate.width * candidate.height;
      if (fArea < 1) return true;
      return cArea <= fArea * 1.4 && candidate.width <= focusRect.width * 1.3 + 8 && candidate.height <= focusRect.height * 1.3 + 8;
    }
    /**
     * True when `el` can host an absolute inset:0 ring (not replaced, not a
     * bare-inline / multi-fragment box).
     * @param {Element|null|undefined} el
     * @returns {boolean}
     */
    _isViableInTargetHost(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      if (this._isReplacedOrVoidElement(el2)) return false;
      if (this._isFragmentedInlineFocusTarget(el2)) return false;
      if (this._isInsideCssMultiColumn(el2)) return false;
      return !!this._canMountVisibleChildOnHost(el2);
    }
    /**
     * CSS `columns` / `column-count` break absolute containing blocks (Blink
     * paints abspos children in the first column). Strategy B must not mount
     * there — Epoch Times header mega-menu section links use `columns-2`.
     * @param {Element|null|undefined} el
     * @returns {boolean}
     */
    _isInsideCssMultiColumn(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      let n = el2;
      let depth = 0;
      while (n && n.nodeType === 1 && depth++ < 12) {
        if (n === document.body || n === document.documentElement) break;
        try {
          const cs = window.getComputedStyle(n);
          const count = parseInt(String(cs.columnCount || ""), 10);
          if (Number.isFinite(count) && count >= 2) return true;
        } catch {
        }
        try {
          n = this._composedParent(n);
        } catch {
          n = n.parentElement;
        }
      }
      return false;
    }
    /**
     * Same-size block descendant that can take the B ring (inline <a> wrapping
     * a clipped thumb).
     * @param {Element} start
     * @param {DOMRect|ClientRect|null|undefined} focusRect
     * @returns {Element|null}
     */
    _findSameSizeInTargetDescendant(start, focusRect) {
      if (!start || start.nodeType !== 1) return null;
      const queue = [];
      try {
        const kids = start.children;
        if (kids) {
          for (let i = 0; i < kids.length; i++) {
            if (kids[i]?.nodeType === 1) queue.push(kids[i]);
          }
        }
      } catch {
        return null;
      }
      let seen = 0;
      while (queue.length && seen++ < 16) {
        const cur = queue.shift();
        if (!cur || cur.nodeType !== 1) continue;
        if (this._isViableInTargetHost(cur)) {
          let r = null;
          try {
            r = cur.getBoundingClientRect();
          } catch {
            r = null;
          }
          if (r && r.width >= 8 && r.height >= 8 && this._inTargetHostSizeOk(r, focusRect)) {
            return cur;
          }
        }
        try {
          const kids = cur.children;
          if (kids) {
            for (let i = 0; i < kids.length; i++) {
              if (kids[i]?.nodeType === 1) queue.push(kids[i]);
            }
          }
        } catch {
        }
      }
      return null;
    }
    /**
     * Box used to accept/reject a B host. Prefer the visual media box over an
     * inline <a> line box so a same-size clip wrap matches and a larger
     * caption/card parent does not.
     * @param {Element} element
     * @param {Element|null|undefined} paintEl
     * @returns {DOMRect|ClientRect|null}
     */
    _visualFocusRectForInTarget(element, paintEl) {
      const boxes = [];
      const push = (el2) => {
        if (!el2 || el2.nodeType !== 1) return;
        try {
          const union = this._visualContentUnionRect(el2);
          if (union && union.width > 1 && union.height > 1) boxes.push(union);
        } catch {
        }
        try {
          const r = el2.getBoundingClientRect();
          if (r && r.width > 1 && r.height > 1) boxes.push(r);
        } catch {
        }
      };
      push(element);
      if (paintEl && paintEl !== element) push(paintEl);
      try {
        const media = this._findDominantReplacedPaintChild(element);
        if (media) push(media);
      } catch {
      }
      if (paintEl && paintEl !== element) {
        try {
          const media = this._findDominantReplacedPaintChild(paintEl);
          if (media) push(media);
        } catch {
        }
      }
      if (!boxes.length) return null;
      let best = boxes[0];
      let bestArea = best.width * best.height;
      for (let i = 1; i < boxes.length; i++) {
        const a = boxes[i].width * boxes[i].height;
        if (a > bestArea) {
          bestArea = a;
          best = boxes[i];
        }
      }
      return best;
    }
    /**
     * Host for an in-target absolute ring: paint-resolved clickable that can
     * accept a last-child ring (sibling above full-bleed media tiles).
     *
     * Ring box must track focusEl geometry. For open-shadow targets, mount on the
     * sized clickable when possible; only use a full ShadowRoot layer when the
     * shadow host itself is roughly the focus box (card tiles). Never fall back
     * to "largest node in the shadow" — that selects list containers.
     *
     * @param {Element} element - hover focusEl
     * @returns {Element|null}
     */
    _resolveInTargetHost(element) {
      if (!element || element.nodeType !== 1) return null;
      try {
        const shell = this._resolveMediaTextCardShell(element);
        if (shell && this._canMountVisibleChildOnHost(shell)) {
          let sr = null;
          try {
            sr = shell.getBoundingClientRect();
          } catch {
            sr = null;
          }
          if (sr && sr.width >= 8 && sr.height >= 8) return shell;
        }
      } catch {
      }
      let paintEl = element;
      try {
        paintEl = this._resolveElementForFocusStyling(element) || element;
      } catch {
        paintEl = element;
      }
      try {
        const frame = this._resolveAbsoluteClipFramePaintHost(paintEl);
        if (frame) paintEl = frame;
      } catch {
      }
      if (!paintEl || paintEl.nodeType !== 1) return null;
      try {
        if (!paintEl.isConnected) return null;
      } catch {
        return null;
      }
      let focusRect = this._visualFocusRectForInTarget(element, paintEl);
      try {
        const root = typeof paintEl.getRootNode === "function" ? paintEl.getRootNode() : null;
        if (root && typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot) {
          if (this._isViableInTargetHost(element)) {
            let er = null;
            try {
              er = element.getBoundingClientRect();
            } catch {
              er = null;
            }
            if (er && er.width >= 8 && er.height >= 8) return element;
          }
          if (this._isViableInTargetHost(paintEl)) {
            let pr = null;
            try {
              pr = paintEl.getBoundingClientRect();
            } catch {
              pr = null;
            }
            if (pr && pr.width >= 8 && pr.height >= 8) {
              return paintEl;
            }
          }
          let n2 = this._composedParent(paintEl);
          let depth2 = 0;
          while (n2 && n2.nodeType === 1 && n2 !== root.host && depth2++ < 8) {
            try {
              if (typeof n2.getRootNode === "function" && n2.getRootNode() !== root) break;
            } catch {
              break;
            }
            if (this._isViableInTargetHost(n2)) {
              let nr = null;
              try {
                nr = n2.getBoundingClientRect();
              } catch {
                nr = null;
              }
              if (nr && nr.width >= 8 && nr.height >= 8 && this._inTargetHostSizeOk(nr, focusRect)) {
                return n2;
              }
            }
            n2 = this._composedParent(n2);
          }
          let hr = null;
          try {
            hr = root.host?.getBoundingClientRect?.();
          } catch {
            hr = null;
          }
          const hostCollapsed = !!(hr && hr.width > 0 && hr.height < 2);
          if (hostCollapsed || this._inTargetHostSizeOk(hr, focusRect)) {
            const layer = this._ensureShadowRootRingHost(root);
            if (layer) return layer;
          }
        }
      } catch {
      }
      if (this._isViableInTargetHost(paintEl)) {
        let pr = null;
        try {
          pr = paintEl.getBoundingClientRect();
        } catch {
          pr = null;
        }
        if (pr && pr.width >= 8 && pr.height >= 8) return paintEl;
      }
      let nearChild = this._findSameSizeInTargetDescendant(paintEl, focusRect);
      if (!nearChild && element !== paintEl) {
        nearChild = this._findSameSizeInTargetDescendant(element, focusRect);
      }
      if (nearChild) return nearChild;
      if (this._getOpenShadowRoot(paintEl)) {
        const inner = this._findInShadowInTargetMountNear(paintEl, focusRect);
        if (inner) return inner;
      }
      let n = this._composedParent(paintEl);
      let depth = 0;
      while (n && n.nodeType === 1 && depth++ < 8) {
        if (n === document.body || n === document.documentElement) break;
        if (this._isViableInTargetHost(n)) {
          let nr = null;
          try {
            nr = n.getBoundingClientRect();
          } catch {
            nr = null;
          }
          if (nr && nr.width >= 8 && nr.height >= 8 && this._inTargetHostSizeOk(nr, focusRect)) {
            return n;
          }
        }
        if (this._getOpenShadowRoot(n)) {
          const inner = this._findInShadowInTargetMountNear(n, focusRect);
          if (inner) return inner;
        }
        n = this._composedParent(n);
      }
      return null;
    }
    /**
     * Like `_findInShadowInTargetMount`, but rejects nodes much larger than
     * `focusRect` so list/panel wrappers lose to the row link box.
     * @param {Element} host
     * @param {DOMRect|ClientRect|null|undefined} focusRect
     * @returns {Element|null}
     */
    _findInShadowInTargetMountNear(host, focusRect) {
      const sr = this._getOpenShadowRoot(host);
      if (!sr) return null;
      let best = null;
      let bestScore = -1;
      let visited = 0;
      const maxNodes = 48;
      const queue = [];
      this._enqueueShadowPiercingChildren(host, queue);
      const focusArea = focusRect && focusRect.width > 0 && focusRect.height > 0 ? focusRect.width * focusRect.height : 0;
      while (queue.length && visited < maxNodes) {
        const cur = queue.shift();
        if (!cur || cur.nodeType !== 1) continue;
        visited++;
        if (!this._isReplacedOrVoidElement(cur) && this._canMountVisibleChildOnHost(cur)) {
          let r = null;
          try {
            r = cur.getBoundingClientRect();
          } catch {
            r = null;
          }
          if (r && r.width >= 8 && r.height >= 8 && this._inTargetHostSizeOk(r, focusRect)) {
            const area = r.width * r.height;
            const score = focusArea > 0 ? -Math.abs(area - focusArea) : area;
            if (score > bestScore) {
              bestScore = score;
              best = cur;
            }
          }
        }
        this._enqueueShadowPiercingChildren(cur, queue);
      }
      return best;
    }
    /**
     * Max numeric z-index among host pseudos and descendants (excluding the ring).
     * Direct children are not enough: Epoch Times SHORT VIDEOS put the play
     * overlay / poster several levels down, so a sibling-only scan stays at 0
     * and the ring's `max+1` is still under that layer.
     * @param {Element} host
     * @returns {number}
     */
    _maxLocalZIndex(host) {
      let max = 0;
      const consider = (z) => {
        if (z == null || z === "" || z === "auto") return;
        const n = parseInt(String(z), 10);
        if (!Number.isFinite(n)) return;
        if (n > 1e6) return;
        max = Math.max(max, n);
      };
      try {
        for (const pseudo of [":before", ":after"]) {
          const ps = window.getComputedStyle(host, pseudo);
          if (ps) consider(ps.zIndex);
        }
      } catch {
      }
      const ringClass = CSS_CLASSES.FOCUS_RING_INTARGET || "kpv2-focus-ring-intarget";
      const queue = [];
      try {
        const kids = host.children;
        if (kids) {
          for (let i = 0; i < kids.length; i++) {
            if (kids[i]?.nodeType === 1) queue.push(kids[i]);
          }
        }
      } catch {
        return max;
      }
      let seen = 0;
      const maxNodes = 40;
      while (queue.length && seen++ < maxNodes) {
        const child = queue.shift();
        if (!child || child.nodeType !== 1) continue;
        if (child === this._inTargetRing) continue;
        try {
          if (child.classList && child.classList.contains(ringClass)) continue;
          if (child.hasAttribute && child.hasAttribute("data-kp-ephemeral-effect")) continue;
        } catch {
        }
        let cs = null;
        try {
          cs = window.getComputedStyle(child);
        } catch {
          cs = null;
        }
        if (cs && cs.zIndex !== "auto") consider(cs.zIndex);
        try {
          const kids = child.children;
          if (kids) {
            for (let i = 0; i < kids.length && queue.length < maxNodes; i++) {
              if (kids[i]?.nodeType === 1) queue.push(kids[i]);
            }
          }
        } catch {
        }
      }
      return max;
    }
    /**
     * Ensure singleton in-target ring element exists.
     * @returns {HTMLElement}
     */
    _ensureInTargetRingEl() {
      if (this._inTargetRing && this._inTargetRing.nodeType === 1) {
        return this._inTargetRing;
      }
      const ringClass = CSS_CLASSES.FOCUS_RING_INTARGET || "kpv2-focus-ring-intarget";
      this._inTargetRing = this.createElement("div", {
        className: ringClass,
        style: `
        position: absolute !important;
        inset: 0 !important;
        box-sizing: border-box !important;
        pointer-events: none !important;
        margin: 0 !important;
        padding: 0 !important;
        border-style: solid !important;
        background: transparent !important;
        transform: none !important;
        filter: none !important;
        display: none;
      `
      });
      try {
        this._inTargetRing.setAttribute("data-kp-focus-ring", "1");
        this._inTargetRing.setAttribute("aria-hidden", "true");
      } catch {
      }
      return this._inTargetRing;
    }
    /**
     * Restore position mutation on a previous in-target host, if any.
     */
    _restoreInTargetHostPosition() {
      const restore = this._inTargetHostPosRestore;
      this._inTargetHostPosRestore = null;
      if (!restore || !restore.host) return;
      try {
        if (restore.prev == null || restore.prev === "") {
          restore.host.style.removeProperty("position");
        } else {
          restore.host.style.position = restore.prev;
        }
      } catch {
      }
    }
    /**
     * Detach / hide the in-target absolute focus ring and restore host position.
     */
    hideInTargetFocusRing() {
      this._focusPaintUsesInTargetRing = false;
      const ring = this._inTargetRing;
      if (ring) {
        try {
          ring.style.display = "none";
        } catch {
        }
        try {
          if (ring.parentNode) ring.parentNode.removeChild(ring);
        } catch {
        }
      }
      this._restoreInTargetHostPosition();
      this._inTargetHost = null;
    }
    /**
     * Count getClientRects() boxes with a real painted area.
     * `<picture>` / `<source>` leftover 0×0 rects (theepochtimes.com thumbs)
     * must not count as line-box fragments.
     * @param {Element|null|undefined} el
     * @returns {number}
     */
    _positiveClientRectCount(el2) {
      if (!el2 || el2.nodeType !== 1) return 0;
      try {
        const rects = el2.getClientRects();
        if (!rects || !rects.length) return 0;
        let n = 0;
        for (let i = 0; i < rects.length; i++) {
          const r = rects[i];
          if (r && r.width > 1 && r.height > 1) n++;
        }
        return n;
      } catch {
        return 0;
      }
    }
    /**
     * Painted union of `el` plus descendant media. Blink omits a tall <img>
     * from an inline host's getBoundingClientRect / getClientRects when the
     * <a> also wraps a display:block title (Rumble `a.category__link`).
     * Strategy B's inset:0 ring follows that under-measured line box unless
     * we size against this union.
     *
     * @param {Element|null|undefined} el
     * @returns {{ left: number, top: number, right: number, bottom: number, width: number, height: number, x: number, y: number }|null}
     */
    _visualContentUnionRect(el2) {
      if (!el2 || el2.nodeType !== 1) return null;
      let left = Infinity;
      let top = Infinity;
      let right = -Infinity;
      let bottom = -Infinity;
      let any = false;
      const absorb = (r) => {
        if (!r || !(r.width > 1) || !(r.height > 1)) return;
        left = Math.min(left, r.left);
        top = Math.min(top, r.top);
        right = Math.max(right, r.right);
        bottom = Math.max(bottom, r.bottom);
        any = true;
      };
      try {
        absorb(el2.getBoundingClientRect());
      } catch {
      }
      try {
        const rects = el2.getClientRects();
        if (rects) {
          for (let i = 0; i < rects.length && i < 16; i++) absorb(rects[i]);
        }
      } catch {
      }
      try {
        const media = el2.querySelectorAll("img, video, canvas, svg");
        for (let i = 0; i < media.length && i < 12; i++) {
          const n = media[i];
          if (!n || n.nodeType !== 1) continue;
          let r = null;
          try {
            r = n.getBoundingClientRect();
          } catch {
            r = null;
          }
          if (!r || r.width < 8 || r.height < 8) continue;
          try {
            const host = el2.getBoundingClientRect();
            if (host && host.width > 8 && r.width > host.width * 1.35 + 24) continue;
          } catch {
          }
          absorb(r);
        }
      } catch {
      }
      if (!any || !Number.isFinite(left) || !Number.isFinite(top)) return null;
      const width = right - left;
      const height = bottom - top;
      if (!(width > 1) || !(height > 1)) return null;
      return { left, top, right, bottom, width, height, x: left, y: top };
    }
    /**
     * Multi-line / wrapped inline clickables fragment into several line boxes.
     * Absolute `inset:0` on an inline host sizes to one fragment (often first line
     * width only) — do not mount B on that host (climb to a same-size block).
     * @param {Element|null|undefined} el
     * @returns {boolean}
     */
    _isFragmentedInlineFocusTarget(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      if (this._positiveClientRectCount(el2) >= 2) return true;
      try {
        const display = String(window.getComputedStyle(el2)?.display || "");
        if (display === "inline") return true;
      } catch {
      }
      return false;
    }
    /**
     * Strategy B: mount an absolute focus ring as last child of the host with
     * z-index = max(local siblings)+1 and border-radius from the visual host.
     *
     * @param {Element} element
     * @param {string} [mode]
     * @returns {boolean} true if ring was mounted
     */
    updateFocusOverlayInTarget(element, mode = MODES.NONE) {
      if (!(FEATURE_FLAGS && FEATURE_FLAGS.ENABLE_IN_TARGET_FOCUS_RING)) {
        return false;
      }
      if (!element || element.nodeType !== 1) return false;
      let cardHost = null;
      try {
        cardHost = this._resolveMediaTextCardShell(element);
      } catch {
        cardHost = null;
      }
      const host = cardHost || this._resolveInTargetHost(element);
      if (!host) return false;
      if (this._isFragmentedInlineFocusTarget(host)) {
        return false;
      }
      try {
        if (host.tagName === "IFRAME") {
          const isPopoverIframe = this.popoverIframeElement && host === this.popoverIframeElement;
          const isModalIframe = !!(host.classList && host.classList.contains("modal-iframe"));
          if (isPopoverIframe || isModalIframe) return false;
        }
      } catch {
      }
      const ring = this._ensureInTargetRingEl();
      let radius = "0";
      try {
        radius = this._resolveElementBorderRadius(host) || this._resolveElementBorderRadius(element) || "0";
      } catch {
        radius = "0";
      }
      if (this._inTargetHost && this._inTargetHost !== host) {
        this._restoreInTargetHostPosition();
      }
      try {
        const cs = window.getComputedStyle(host);
        if (cs && cs.position === "static") {
          if (!this._inTargetHostPosRestore || this._inTargetHostPosRestore.host !== host) {
            this._inTargetHostPosRestore = {
              host,
              prev: host.style.position || ""
            };
            host.style.position = "relative";
          }
        } else if (this._inTargetHostPosRestore && this._inTargetHostPosRestore.host === host) {
          if (cs && cs.position !== "static" && host.style.position === "relative") {
          }
        }
      } catch {
      }
      try {
        if (ring.parentNode !== host || host.lastElementChild !== ring) {
          host.appendChild(ring);
        }
      } catch {
        this.hideInTargetFocusRing();
        return false;
      }
      this._inTargetHost = host;
      const isTextInput = element.matches && element.matches(SELECTORS.FOCUSABLE_TEXT);
      const suppressFill = this.shouldSuppressFocusFill(element);
      const {
        rectangleThickness,
        overlayFillEnabled,
        overlayShadowEnabled,
        focusColor
      } = this._getClickModeSettings();
      const ringWidthPx = Math.min(Math.max(Number(rectangleThickness) || 3, 1), 16);
      let borderColor;
      let shadowColor;
      let shadowBright;
      let backgroundColor = "transparent";
      if (isTextInput) {
        borderColor = COLORS.ORANGE;
        shadowColor = COLORS.ORANGE_SHADOW;
        shadowBright = COLORS.ORANGE_SHADOW;
      } else {
        const p = this._getNonTextFocusPalette(focusColor);
        borderColor = p.borderColor;
        shadowColor = p.shadowColor;
        shadowBright = p.shadowBrightColor;
        if (!suppressFill && overlayFillEnabled === true) {
          backgroundColor = p.backgroundColor || "transparent";
        }
      }
      const zLocal = this._maxLocalZIndex(host) + 1;
      let z = zLocal;
      try {
        if (this._isInShadowTree(host)) z = Math.max(z, 2147483e3);
        else z = Math.max(z, 2);
      } catch {
        z = Math.max(zLocal, 2);
      }
      let focusPad = 0;
      try {
        focusPad = Math.max(0, Number(this._getClickModeSettings().focusPadding) || 0);
      } catch {
        focusPad = 0;
      }
      let hostClipsSelf = false;
      try {
        hostClipsSelf = this._styleClipsSelf(window.getComputedStyle(host));
      } catch {
        hostClipsSelf = false;
      }
      let clipRoom = Infinity;
      try {
        clipRoom = this._minFocusOutlineRoomPx(host);
      } catch {
        clipRoom = Infinity;
      }
      const outwardBlocked = hostClipsSelf || Number.isFinite(clipRoom) && clipRoom < focusPad + 0.5;
      try {
        ring.style.setProperty("position", "absolute", "important");
        let edgePx = 0;
        if (!outwardBlocked && focusPad > 0) {
          edgePx = -focusPad;
        }
        const edge = `${edgePx}px`;
        ring.style.setProperty("top", edge, "important");
        ring.style.setProperty("right", edge, "important");
        ring.style.setProperty("bottom", edge, "important");
        ring.style.setProperty("left", edge, "important");
        ring.style.setProperty("box-sizing", "border-box", "important");
        ring.style.setProperty("pointer-events", "none", "important");
        ring.style.setProperty("margin", "0", "important");
        ring.style.setProperty("padding", "0", "important");
        ring.style.setProperty("width", "auto", "important");
        ring.style.setProperty("height", "auto", "important");
        ring.style.setProperty("z-index", String(z), "important");
        ring.style.setProperty("border-width", `${ringWidthPx}px`, "important");
        const borderStyle = this._getClickModeSettings().paintBackendDebugDashes ? "dotted" : "solid";
        ring.style.setProperty("border-style", borderStyle, "important");
        ring.style.setProperty("border-color", borderColor, "important");
        ring.style.setProperty("border-radius", radius, "important");
        ring.style.setProperty("background", backgroundColor, "important");
        ring.style.setProperty("transform", "none", "important");
        ring.style.setProperty("transform-origin", "center", "important");
        ring.style.setProperty("scale", "none", "important");
        ring.style.setProperty("translate", "none", "important");
        ring.style.setProperty("rotate", "none", "important");
        ring.style.setProperty("filter", "none", "important");
        ring.style.setProperty("mix-blend-mode", "normal", "important");
        ring.style.setProperty("isolation", "isolate", "important");
        if (overlayShadowEnabled === false) {
          ring.style.setProperty("box-shadow", "none", "important");
        } else {
          ring.style.setProperty(
            "box-shadow",
            `0 0 0 2px ${shadowColor}, 0 0 10px 2px ${shadowBright}`,
            "important"
          );
        }
        ring.style.setProperty("display", "block", "important");
        ring.style.setProperty("visibility", "visible", "important");
        ring.style.setProperty("opacity", "1", "important");
      } catch {
        this.hideInTargetFocusRing();
        return false;
      }
      try {
        const rr = ring.getBoundingClientRect();
        if (!rr || !(rr.width > 1) || !(rr.height > 1)) {
          this.hideInTargetFocusRing();
          return false;
        }
        let fr = null;
        try {
          fr = element.getBoundingClientRect();
        } catch {
          fr = null;
        }
        if (fr && fr.width > 16 && rr.width < fr.width * 0.85) {
          this.hideInTargetFocusRing();
          return false;
        }
        let hostBox = null;
        try {
          hostBox = host.getBoundingClientRect();
        } catch {
          hostBox = null;
        }
        const padSlack = focusPad * 2 + 2;
        if (hostBox && hostBox.width > 1 && hostBox.height > 1 && (rr.width > hostBox.width * 1.35 + 4 + padSlack || rr.height > hostBox.height * 1.35 + 4 + padSlack)) {
          let hostClips = false;
          try {
            hostClips = this._styleClipsSelf(window.getComputedStyle(host));
          } catch {
            hostClips = false;
          }
          if (hostClips) {
            this.hideInTargetFocusRing();
            return false;
          }
        }
      } catch {
        this.hideInTargetFocusRing();
        return false;
      }
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] In-target focus ring mounted:", {
          host: host.tagName,
          hostClass: (host.className || "").toString().slice(0, 60),
          zIndex: z,
          borderRadius: radius,
          borderColor,
          mode
        });
      }
      return true;
    }
    /**
     * Update focus overlay using element styling (for DOM hover mode)
     * Styles the element directly instead of creating overlay elements
     */
    updateFocusOverlayElementStyling(element, mode = MODES.NONE) {
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] updateFocusOverlayElementStyling called with:", {
          element: element?.tagName,
          mode,
          _useDomHoverFocusColors: this._useDomHoverFocusColors
        });
      }
      this.clearElementFocusStyling();
      if (!element) {
        return;
      }
      let stylingTarget = this._resolveElementForFocusStyling(element) || element;
      try {
        const frame = this._resolveAbsoluteClipFramePaintHost(stylingTarget);
        if (frame) stylingTarget = frame;
      } catch {
      }
      let clipCtx = { clippers: [], tightWrapper: null };
      const clipInsetOn = !!(FEATURE_FLAGS && FEATURE_FLAGS.ENABLE_FOCUS_CLIP_INSET);
      const tightPromoteOn = !!(FEATURE_FLAGS && FEATURE_FLAGS.ENABLE_FOCUS_TIGHT_WRAPPER_PROMOTION);
      if (clipInsetOn || tightPromoteOn) {
        try {
          clipCtx = this._findFocusClipContext(stylingTarget);
        } catch {
          clipCtx = { clippers: [], tightWrapper: null };
        }
        if (tightPromoteOn && clipCtx.tightWrapper && clipCtx.tightWrapper.nodeType === 1) {
          stylingTarget = clipCtx.tightWrapper;
        }
      }
      try {
        if (stylingTarget.tagName === "IFRAME") {
          const isPopoverIframe = this.popoverIframeElement && stylingTarget === this.popoverIframeElement;
          const isModalIframe = !!(stylingTarget.classList && stylingTarget.classList.contains("modal-iframe"));
          if (isPopoverIframe || isModalIframe) {
            return;
          }
        }
      } catch {
      }
      const categorySource = element;
      const isTextInput = categorySource.matches && categorySource.matches(SELECTORS.FOCUSABLE_TEXT);
      const suppressFill = this.shouldSuppressFocusFill(categorySource);
      const {
        rectangleThickness,
        overlayFillEnabled,
        overlayShadowEnabled,
        focusColor
      } = this._getClickModeSettings();
      const palette = this._getNonTextFocusPalette(focusColor);
      const ringColor = isTextInput ? COLORS.ORANGE : palette.borderColor;
      const ringWidthPx = Math.min(Math.max(Number(rectangleThickness) || 3, 1), 16);
      const ringWidth = `${ringWidthPx}px`;
      const shadowColor = isTextInput ? COLORS.ORANGE_SHADOW : palette.shadowColor;
      const shadowBright = isTextInput ? COLORS.ORANGE_SHADOW : palette.shadowBrightColor;
      let ringBgColor = "transparent";
      if (!suppressFill && overlayFillEnabled !== false) {
        ringBgColor = isTextInput ? "rgba(255,140,0,0.2)" : palette.backgroundColor || "rgba(33,150,243,0.25)";
      }
      const boxShadow = overlayShadowEnabled === false ? "none" : `0 0 0 2px ${shadowColor}, 0 0 10px 2px ${shadowBright}`;
      const outlineOffsetPx = clipInsetOn ? this._computeGradedFocusOutlineOffset(stylingTarget, ringWidthPx) : this._preferredFocusOutlineOffsetPx();
      const useInset = outlineOffsetPx < -0.25;
      this._ensureStylesForElement(stylingTarget);
      stylingTarget.style.setProperty("--keypilot-focus-ring-color", ringColor);
      stylingTarget.style.setProperty("--keypilot-focus-ring-width", ringWidth);
      stylingTarget.style.setProperty("--keypilot-focus-outline-offset", `${outlineOffsetPx}px`);
      stylingTarget.style.setProperty("--keypilot-focus-shadow-color", shadowColor);
      stylingTarget.style.setProperty("--keypilot-focus-ring-bg-color", ringBgColor);
      stylingTarget.style.setProperty("--keypilot-focus-box-shadow", boxShadow);
      try {
        const outlineStyle = this._getClickModeSettings().paintBackendDebugDashes ? "dashed" : "solid";
        stylingTarget.style.setProperty("--keypilot-focus-outline-style", outlineStyle);
      } catch {
        stylingTarget.style.setProperty("--keypilot-focus-outline-style", "solid");
      }
      try {
        const ownRadius = this._readNonZeroBorderRadius(stylingTarget);
        if (!ownRadius) {
          const visualRadius = this._resolveElementBorderRadius(stylingTarget);
          if (visualRadius) {
            stylingTarget.style.setProperty("border-radius", visualRadius, "important");
            stylingTarget.setAttribute("data-kp-focus-radius-set", "1");
          }
        }
      } catch {
      }
      stylingTarget.classList.add("keypilot-focus-element");
      if (ringBgColor !== "transparent") {
        stylingTarget.classList.add("keypilot-focus-element--fill");
      } else {
        stylingTarget.classList.remove("keypilot-focus-element--fill");
      }
      try {
        stylingTarget.setAttribute("data-kp-focus", "1");
        if (useInset) stylingTarget.setAttribute("data-kp-focus-inset", "1");
        else stylingTarget.removeAttribute("data-kp-focus-inset");
      } catch {
      }
      if (useInset) stylingTarget.classList.add("keypilot-focus-element--inset");
      else stylingTarget.classList.remove("keypilot-focus-element--inset");
      let foreignDoc = false;
      try {
        foreignDoc = !!(stylingTarget.ownerDocument && stylingTarget.ownerDocument !== document);
      } catch {
      }
      if (this._isInShadowTree(stylingTarget) || foreignDoc) {
        try {
          stylingTarget.setAttribute("data-kp-focus-inline", "1");
          stylingTarget.style.setProperty(
            "outline",
            `${ringWidth} solid ${ringColor}`,
            "important"
          );
          stylingTarget.style.setProperty(
            "outline-offset",
            `${outlineOffsetPx}px`,
            "important"
          );
          stylingTarget.style.setProperty("box-shadow", boxShadow, "important");
        } catch {
        }
      }
      this._currentStyledElement = stylingTarget;
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Applied element styling:", {
          tagName: stylingTarget.tagName,
          originalTagName: element?.tagName,
          styledDifferentElement: stylingTarget !== element,
          outlineOffsetPx,
          useInset,
          clipInsetFlag: clipInsetOn,
          tightPromoteFlag: tightPromoteOn,
          tightWrapper: !!(clipCtx && clipCtx.tightWrapper),
          minRoom: this._minFocusOutlineRoomPx(stylingTarget),
          ringColor,
          isTextInput
        });
      }
    }
    /**
     * Largest in-tree descendant with a positive box (for collapsed anchors that
     * only wrap position:absolute media — e.g. Breitbart video carousel thumbs).
     * Pierces open shadow roots (archive.org media-button / collection-tile hosts
     * often have empty light DOM with the visual tree only in shadow).
     * @param {HTMLElement} element
     * @returns {HTMLElement|null}
     */
    _findLargestVisibleDescendant(element) {
      if (!element || element.nodeType !== 1) return null;
      let best = null;
      let bestArea = 0;
      let visited = 0;
      const maxNodes = 48;
      const queue = [];
      this._enqueueShadowPiercingChildren(element, queue);
      while (queue.length && visited < maxNodes) {
        const cur = queue.shift();
        if (!cur || cur.nodeType !== 1) continue;
        visited++;
        let r = null;
        try {
          r = cur.getBoundingClientRect();
        } catch {
          r = null;
        }
        if (r && r.width >= 8 && r.height >= 8) {
          const area = r.width * r.height;
          if (area > bestArea) {
            bestArea = area;
            best = /** @type {HTMLElement} */
            cur;
          }
        }
        this._enqueueShadowPiercingChildren(cur, queue);
      }
      return best;
    }
    /**
     * Inline host whose border/line box is much smaller than a replaced child
     * (phoronix.com logo: `a { display:inline }` → line-box ~1em tall, child
     * `<img>` is the real visual). Outline/flash on the host uses the line box
     * and misses the image; prefer the replaced child for paint.
     *
     * @param {Element} el
     * @returns {Element|null}
     */
    _findDominantReplacedPaintChild(el2) {
      if (!el2 || el2.nodeType !== 1) return null;
      let display = "";
      try {
        display = String(window.getComputedStyle(el2)?.display || "");
      } catch {
        display = "";
      }
      if (display && !display.startsWith("inline") && display !== "contents") {
        return null;
      }
      let hostBox = null;
      try {
        hostBox = el2.getBoundingClientRect();
      } catch {
        hostBox = null;
      }
      const hostArea = hostBox && hostBox.width > 0 && hostBox.height > 0 ? hostBox.width * hostBox.height : 0;
      let best = null;
      let bestArea = 0;
      const consider = (node) => {
        if (!node || node.nodeType !== 1 || node === el2) return;
        if (!this._isMediaLikeCoverElement(node) && !this._isReplacedOrVoidElement(node)) {
          const tag = String(node.tagName || "").toUpperCase();
          if (tag !== "IMG" && tag !== "VIDEO" && tag !== "CANVAS" && tag !== "SVG") return;
        }
        let r = null;
        try {
          r = node.getBoundingClientRect();
        } catch {
          r = null;
        }
        if (!r || !(r.width >= 8) || !(r.height >= 8)) return;
        const area = r.width * r.height;
        if (area <= bestArea) return;
        if (hostArea > 0) {
          const taller = r.height > hostBox.height * 1.2 + 2;
          const wider = r.width > hostBox.width * 1.05 + 2;
          const biggerArea = area > hostArea * 1.35;
          if (!taller && !wider && !biggerArea) return;
        }
        bestArea = area;
        best = node;
      };
      try {
        const kids = el2.children;
        if (kids) {
          for (let i = 0; i < kids.length; i++) {
            const k = kids[i];
            if (!k || k.nodeType !== 1) continue;
            const tag = String(k.tagName || "").toUpperCase();
            if (tag === "PICTURE") {
              try {
                const img = k.querySelector("img");
                if (img) consider(img);
              } catch {
              }
            } else {
              consider(k);
            }
          }
        }
      } catch {
      }
      if (!best) {
        try {
          const media = el2.querySelectorAll("img, video, canvas, svg");
          for (let i = 0; i < media.length && i < 12; i++) consider(media[i]);
        } catch {
        }
      }
      return best;
    }
    /**
     * Choose the best element to apply the focus ring styling to.
     *
     * Problems:
     * 1) Inline anchors that wrap only position:absolute media collapse to 0×0
     *    (Breitbart video thumbs) — outline on the <a> is invisible.
     * 2) Inline anchors that wrap a larger replaced child (phoronix logo img):
     *    host getBoundingClientRect is only the line box (~1em); outline on the
     *    <a> protrudes / mismatches the image. Paint the <img> instead.
     * 3) Inline elements that contain block children can be split into multiple
     *    inline fragments, causing outline/box-shadow to render as disjoint pieces.
     * 4) Open-shadow custom-element hosts can be collapsed (archive.org
     *    media-button ~79×0) while the real clickable lives inside the shadow.
     *
     * Strategy:
     * - If the element has no usable box, style the largest visible descendant
     *   (piercing open shadows) or a sized composed parent that wraps abspos content.
     * - If inline host is dominated by a larger replaced child, style that child.
     * - If `element.getClientRects()` indicates fragmentation (2+ rects) and
     *   element is inline-ish, find the largest single-rect descendant.
     * - Otherwise, return `element`.
     *
     * @param {HTMLElement} element
     * @returns {HTMLElement}
     */
    _resolveElementForFocusStyling(element) {
      if (!element || element.nodeType !== 1) return element;
      let br = null;
      try {
        br = element.getBoundingClientRect();
      } catch {
        br = null;
      }
      if (!br || br.width < 2 || br.height < 2) {
        const descendant = this._findLargestVisibleDescendant(element);
        if (descendant) return descendant;
        try {
          let p = this._composedParent(element);
          let hops = 0;
          while (p && p.nodeType === 1 && hops++ < 4) {
            if (p === document.body || p === document.documentElement) break;
            let pr = null;
            try {
              pr = p.getBoundingClientRect();
            } catch {
              pr = null;
            }
            if (pr && pr.width >= 8 && pr.height >= 8) {
              return (
                /** @type {HTMLElement} */
                p
              );
            }
            p = this._composedParent(p);
          }
        } catch {
        }
        return element;
      }
      try {
        const replaced = this._findDominantReplacedPaintChild(element);
        if (replaced) return (
          /** @type {HTMLElement} */
          replaced
        );
      } catch {
      }
      let rects = null;
      try {
        rects = element.getClientRects();
      } catch {
        rects = null;
      }
      if (!rects || rects.length < 2) return element;
      let display = "";
      try {
        display = String(window.getComputedStyle(element)?.display || "");
      } catch {
        display = "";
      }
      const inlineish = display.startsWith("inline");
      if (!inlineish) return element;
      let uLeft = Infinity, uTop = Infinity, uRight = -Infinity, uBottom = -Infinity;
      try {
        for (const r of rects) {
          uLeft = Math.min(uLeft, r.left);
          uTop = Math.min(uTop, r.top);
          uRight = Math.max(uRight, r.right);
          uBottom = Math.max(uBottom, r.bottom);
        }
      } catch {
        return element;
      }
      if (!Number.isFinite(uLeft) || !Number.isFinite(uTop) || !Number.isFinite(uRight) || !Number.isFinite(uBottom)) return element;
      let best = null;
      let bestArea = 0;
      let nodesVisited = 0;
      const maxNodes = 40;
      const maxDepth = 4;
      const queue = [{ el: element, depth: 0 }];
      while (queue.length && nodesVisited < maxNodes) {
        const cur = queue.shift();
        const curEl = cur?.el;
        const d = cur?.depth ?? 0;
        if (!curEl || curEl.nodeType !== 1) continue;
        nodesVisited++;
        if (curEl !== element) {
          let cr = null;
          try {
            cr = curEl.getClientRects();
          } catch {
            cr = null;
          }
          if (cr && cr.length === 1) {
            const r0 = cr[0];
            const w = Number(r0.width);
            const h = Number(r0.height);
            const area = (Number.isFinite(w) ? w : 0) * (Number.isFinite(h) ? h : 0);
            if (area > bestArea) {
              const overlaps = !(r0.right <= uLeft || r0.left >= uRight || r0.bottom <= uTop || r0.top >= uBottom);
              if (overlaps) {
                bestArea = area;
                best = curEl;
              }
            }
          }
        }
        if (d >= maxDepth) continue;
        try {
          const childList = [];
          this._enqueueShadowPiercingChildren(curEl, childList);
          for (let i = 0; i < childList.length; i++) {
            const k = childList[i];
            if (k && k.nodeType === 1) queue.push({ el: (
              /** @type {HTMLElement} */
              k
            ), depth: d + 1 });
            if (queue.length > maxNodes) break;
          }
        } catch {
        }
      }
      return best || element;
    }
    /**
     * Strip KeyPilot focus ring classes/vars from a single element.
     * @param {Element|null|undefined} el
     */
    _stripFocusStylingFromElement(el2) {
      if (!el2 || el2.nodeType !== 1) return;
      try {
        el2.classList.remove("keypilot-focus-element");
        el2.classList.remove("keypilot-focus-element--inset");
        el2.classList.remove("keypilot-focus-element--fill");
        try {
          el2.removeAttribute("data-kp-focus");
          el2.removeAttribute("data-kp-focus-inset");
          el2.style.removeProperty("--keypilot-focus-outline-offset");
          el2.style.removeProperty("--keypilot-focus-outline-style");
          el2.style.removeProperty("--keypilot-focus-ring-color");
          el2.style.removeProperty("--keypilot-focus-ring-width");
          el2.style.removeProperty("--keypilot-focus-shadow-color");
          el2.style.removeProperty("--keypilot-focus-ring-bg-color");
          el2.style.removeProperty("--keypilot-focus-box-shadow");
          if (el2.getAttribute("data-kp-focus-radius-set") === "1") {
            el2.style.removeProperty("border-radius");
            el2.removeAttribute("data-kp-focus-radius-set");
          }
          if (el2.getAttribute("data-kp-focus-inline") === "1") {
            el2.style.removeProperty("outline");
            el2.style.removeProperty("outline-offset");
            el2.style.removeProperty("box-shadow");
            el2.removeAttribute("data-kp-focus-inline");
          }
        } catch {
        }
        el2.style.removeProperty("--keypilot-focus-ring-color");
        el2.style.removeProperty("--keypilot-focus-ring-width");
        el2.style.removeProperty("--keypilot-focus-shadow-color");
        el2.style.removeProperty("--keypilot-focus-ring-bg-color");
        el2.style.removeProperty("--keypilot-focus-box-shadow");
        el2.style.removeProperty("filter");
      } catch {
      }
    }
    /**
     * Clear focus styling from the currently styled element.
     * Also sweeps the owning open ShadowRoot (and document) for leftovers —
     * Lit re-renders / resolve-target swaps can strand the class on a node we
     * no longer hold in `_currentStyledElement`, which looks like "stuck" hover.
     *
     * @param {{ deep?: boolean }} [opts] - deep: walk all open shadow roots
     *   (used on full hide / mode switch to kill ghost rings on archive.org).
     */
    clearElementFocusStyling(opts = {}) {
      const deep = !!(opts && opts.deep);
      const primary = this._currentStyledElement;
      this._currentStyledElement = null;
      this._stripFocusStylingFromElement(primary);
      let root = null;
      try {
        root = primary && typeof primary.getRootNode === "function" ? primary.getRootNode() : null;
      } catch {
        root = null;
      }
      const roots = [];
      if (root && typeof root.querySelectorAll === "function") roots.push(root);
      try {
        roots.push(document);
      } catch {
      }
      for (const r of roots) {
        try {
          const stranded = r.querySelectorAll(
            ".keypilot-focus-element, .keypilot-focus-element--inset, [data-kp-focus], [data-kp-focus-inline]"
          );
          for (const el2 of stranded) {
            this._stripFocusStylingFromElement(el2);
          }
        } catch {
        }
      }
      if (deep) {
        try {
          this._deepStripFocusMarkers(document);
        } catch {
        }
      }
    }
    /**
     * Walk open shadow trees and strip any leftover hover-ring markers.
     * @param {Document|ShadowRoot|Element} root
     * @param {number} [depth]
     */
    _deepStripFocusMarkers(root, depth = 0) {
      if (!root || depth > 24) return;
      try {
        if (typeof root.querySelectorAll === "function") {
          const stranded = root.querySelectorAll(
            ".keypilot-focus-element, .keypilot-focus-element--inset, [data-kp-focus], [data-kp-focus-inline]"
          );
          for (const el2 of stranded) {
            this._stripFocusStylingFromElement(el2);
          }
        }
      } catch {
      }
      try {
        const base = root.nodeType === 9 ? root.documentElement : root;
        if (!base) return;
        if (base.nodeType === 1 && base.shadowRoot) {
          this._deepStripFocusMarkers(base.shadowRoot, depth + 1);
        }
        const w = document.createTreeWalker(base, NodeFilter.SHOW_ELEMENT, null);
        let n;
        while (n = w.nextNode()) {
          if (n.shadowRoot) {
            this._deepStripFocusMarkers(n.shadowRoot, depth + 1);
          }
        }
      } catch {
      }
    }
    /**
     * @param {Element|null} element - paint / geometry target
     * @param {string} [mode]
     * @param {object|null} [rectOverride]
     * @param {{ colorFrom?: Element|null }} [opts] - semantic source for text→orange
     *   when geometry was lifted to a wrapper
     */
    updateFocusOverlayDOM(element, mode = MODES.NONE, rectOverride = null, opts = null) {
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] updateFocusOverlayDOM called with:", {
          element: element?.tagName,
          mode,
          rectOverride,
          _useDomHoverFocusColors: this._useDomHoverFocusColors
        });
      }
      if (!element) {
        this.hideFocusOverlay();
        return;
      }
      try {
        if (element.tagName === "IFRAME") {
          const isPopoverIframe = this.popoverIframeElement && element === this.popoverIframeElement;
          const isModalIframe = !!(element.classList && element.classList.contains("modal-iframe"));
          if (isPopoverIframe || isModalIframe) {
            this.hideFocusOverlay();
            return;
          }
        }
      } catch {
      }
      const colorEl = opts && opts.colorFrom && opts.colorFrom.nodeType === 1 ? opts.colorFrom : element;
      const isTextInput = colorEl.matches && colorEl.matches(SELECTORS.FOCUSABLE_TEXT);
      const suppressFill = this.shouldSuppressFocusFill(colorEl);
      let rect = rectOverride && typeof rectOverride === "object" ? rectOverride : this.getBestRect(element);
      rect = this._clipViewportRectToVisible(element, rect);
      if (!rect || !(rect.width > 0) || !(rect.height > 0)) {
        this.hideFocusOverlay();
        return "hidden";
      }
      try {
        const pad = Math.max(0, Number(this._getClickModeSettings().focusPadding) || 0);
        if (pad > 0) {
          rect = {
            left: rect.left - pad,
            top: rect.top - pad,
            width: rect.width + pad * 2,
            height: rect.height + pad * 2,
            right: (rect.right != null ? rect.right : rect.left + rect.width) + pad,
            bottom: (rect.bottom != null ? rect.bottom : rect.top + rect.height) + pad
          };
        }
      } catch {
      }
      try {
        const source = opts && opts.colorFrom && opts.colorFrom.nodeType === 1 ? opts.colorFrom : element;
        const inset = this._insetCRectForOcclusion(source, rect);
        if (!inset) {
          this.hideFocusOverlay();
          return "occluded";
        }
        rect = inset;
      } catch {
      }
      const isVeryLarge = rect && rect.width > 512 && rect.height > 512;
      let borderColor, shadowColor, backgroundColor;
      if (isTextInput) {
        borderColor = COLORS.ORANGE;
        shadowColor = COLORS.ORANGE_SHADOW;
        backgroundColor = "transparent";
      } else {
        const p = this._getNonTextFocusPalette();
        borderColor = p.borderColor;
        shadowColor = p.shadowColor;
        backgroundColor = suppressFill || isVeryLarge ? "transparent" : p.backgroundColor;
      }
      const {
        rectangleThickness,
        overlayFillEnabled,
        overlayShadowEnabled,
        focusColor
      } = this._getClickModeSettings();
      if (!isTextInput && !suppressFill && !isVeryLarge && overlayFillEnabled === false) {
        backgroundColor = "transparent";
      }
      if (!isTextInput) {
        const p = this._getNonTextFocusPalette(focusColor);
        borderColor = p.borderColor;
        shadowColor = p.shadowColor;
        if (overlayFillEnabled !== false && !suppressFill && !isVeryLarge) {
          backgroundColor = p.backgroundColor;
        }
      }
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] updateFocusOverlay called for:", {
          tagName: element.tagName,
          className: element.className,
          text: element.textContent?.substring(0, 30),
          mode,
          isTextInput,
          suppressFill,
          isVeryLarge,
          borderColor
        });
      }
      if (!this.focusOverlay) {
        this.focusOverlay = this.createElement("div", {
          className: CSS_CLASSES.FOCUS_OVERLAY,
          style: `
          position: fixed;
          left: 0;
          top: 0;
          pointer-events: none;
          z-index: ${Z_INDEX.OVERLAYS};
          background: ${backgroundColor};
          will-change: transform, width, height;
        `
        });
        document.body.appendChild(this.focusOverlay);
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Focus overlay created and added to DOM:", {
            element: this.focusOverlay,
            className: this.focusOverlay.className,
            style: this.focusOverlay.style.cssText,
            parent: this.focusOverlay.parentElement?.tagName
          });
        }
        if (this.overlayObserver) {
          this.overlayObserver.observe(this.focusOverlay);
        }
      }
      this.focusOverlay.style.opacity = "1";
      const borderStyle = this._getClickModeSettings().paintBackendDebugDashes ? "double" : "solid";
      this.focusOverlay.style.border = `${rectangleThickness}px ${borderStyle} ${borderColor}`;
      this.focusOverlay.style.background = backgroundColor;
      this.focusOverlay.style.boxSizing = "border-box";
      try {
        const radius = this._resolveElementBorderRadius(element);
        this.focusOverlay.style.borderRadius = radius || "0";
      } catch {
        this.focusOverlay.style.borderRadius = "0";
      }
      if (overlayShadowEnabled === false) {
        this.focusOverlay.style.boxShadow = "none";
      } else {
        const brightShadowColor = isTextInput ? COLORS.ORANGE_SHADOW : this._getNonTextFocusPalette(focusColor).shadowBrightColor;
        this.focusOverlay.style.boxShadow = `0 0 0 2px ${shadowColor}, 0 0 10px 2px ${brightShadowColor}`;
      }
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Focus overlay positioning:", {
          rect,
          overlayExists: !!this.focusOverlay,
          overlayVisibility: this.overlayVisibility.focus
        });
      }
      if (rect.width > 0 && rect.height > 0) {
        this.focusOverlay.style.transform = `translate3d(${rect.left}px, ${rect.top}px, 0)`;
        this.focusOverlay.style.width = `${rect.width}px`;
        this.focusOverlay.style.height = `${rect.height}px`;
        this.focusOverlay.style.display = "block";
        this.focusOverlay.style.visibility = "visible";
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Focus overlay positioned at:", {
            left: rect.left,
            top: rect.top,
            width: rect.width,
            height: rect.height
          });
        }
      } else {
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Focus overlay hidden - invalid rect:", rect);
        }
        this.hideFocusOverlay();
      }
    }
    // Unified hide interface that switches between rendering modes
    hideFocusOverlay() {
      if (this._useDomHoverFocusColors) {
        this._focusPaintUsesFixedOverlay = false;
        this._focusPaintUsesInTargetRing = false;
        this.clearElementFocusStyling({ deep: false });
        this.hideInTargetFocusRing();
        this.hideFocusOverlayDOM();
        return;
      }
      switch (this.renderingMode) {
        case "canvas":
          return this.hideFocusOverlayCanvas();
        case "css-custom-props":
          return this.hideFocusOverlayCSSCustomProps();
        case "dom":
        default:
          return this.hideFocusOverlayDOM();
      }
    }
    hideFocusOverlayDOM() {
      if (this.focusOverlay) {
        this.focusOverlay.style.display = "none";
      }
    }
    /**
     * Set the rendering mode for overlays
     * @param {string} mode - 'dom', 'canvas', or 'css-custom-props'
     */
    setRenderingMode(mode) {
      if (!["dom", "canvas", "css-custom-props"].includes(mode)) {
        console.warn("[KeyPilot] Invalid rendering mode:", mode);
        return;
      }
      this.cleanupRenderingMode();
      this.renderingMode = mode;
      this.initRenderingMode();
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Rendering mode changed to:", mode);
      }
    }
    initRenderingMode() {
      switch (this.renderingMode) {
        case "canvas":
          this.initCanvasRenderer();
          break;
        case "css-custom-props":
          this.initCSSCustomPropsRenderer();
          break;
        case "dom":
        default:
          break;
      }
    }
    cleanupRenderingMode() {
      switch (this.renderingMode) {
        case "canvas":
          this.cleanupCanvasRenderer();
          break;
        case "css-custom-props":
          this.cleanupCSSCustomPropsRenderer();
          break;
        case "dom":
        default:
          break;
      }
    }
    /**
     * Fade out the focus overlay rect, then hide it.
     * Useful after clicks that mutate the DOM (accordions, menus, etc.) so we don't leave a stale rect.
     */
    fadeOutFocusOverlay(durationMs = 120) {
      if (this.renderingMode === "dom" && this.focusOverlay) {
        if (this.focusOverlay.style.display === "none") return;
        this.focusOverlay.style.transition = `opacity ${durationMs}ms ease-out`;
        this.focusOverlay.style.opacity = "0";
        window.setTimeout(() => {
          if (!this.focusOverlay) return;
          if (this.focusOverlay.style.opacity === "0") {
            this.hideFocusOverlay();
          }
          if (this.focusOverlay) {
            this.focusOverlay.style.transition = "";
          }
        }, durationMs);
      } else {
        this.hideFocusOverlay();
      }
    }
  };
  function installFocusOverlayPainter(OverlayManager2) {
    for (const name of FOCUS_OVERLAY_METHOD_NAMES) {
      OverlayManager2.prototype[name] = function(...args) {
        return this.focusOverlayPainter[name](...args);
      };
    }
  }

  // src/modules/overlay-manager.js
  init_settings_manager();

  // src/utils/scroll-at-point.js
  var EDGE_EPS = 1;
  function isInstantScrollBehavior(behavior) {
    return behavior === "auto" || behavior === "instant";
  }
  function collectScrollBehaviorNodes(el2, doc) {
    const nodes = [];
    const add = (n) => {
      if (n && n.nodeType === 1 && !nodes.includes(n)) nodes.push(n);
    };
    add(el2);
    try {
      add(doc?.scrollingElement);
      add(doc?.documentElement);
      add(doc?.body);
    } catch {
    }
    return nodes;
  }
  function forceCssScrollBehaviorAuto(nodes) {
    const saved = [];
    for (const node of nodes) {
      try {
        const style = node.style;
        if (!style) continue;
        saved.push({
          node,
          had: style.getPropertyValue("scroll-behavior") !== "",
          value: style.getPropertyValue("scroll-behavior")
        });
        style.setProperty("scroll-behavior", "auto", "important");
      } catch {
      }
    }
    return () => {
      for (const { node, had, value } of saved) {
        try {
          if (had) node.style.setProperty("scroll-behavior", value);
          else node.style.removeProperty("scroll-behavior");
        } catch {
        }
      }
    };
  }
  function tryHijackInstant(win, left, top) {
    if (!win) return;
    const inst = win.lenis;
    if (inst && typeof inst.scrollTo === "function") {
      try {
        inst.scrollTo(top, { immediate: true, force: true, lock: false });
      } catch {
        try {
          inst.scrollTo(top, { immediate: true });
        } catch {
        }
      }
    }
    const loco = win.locoScroll || win.locomotiveScroll;
    if (loco && typeof loco.scrollTo === "function") {
      try {
        loco.scrollTo(top, { duration: 0, disableLerp: true, immediate: true });
      } catch {
      }
    }
  }
  function applyInstantScrollTo(el2, left, top, doc = document, win = window) {
    if (!el2) return false;
    const L = Number(left) || 0;
    const T = Number(top) || 0;
    const restore = forceCssScrollBehaviorAuto(collectScrollBehaviorNodes(el2, doc));
    try {
      try {
        void el2.offsetHeight;
      } catch {
      }
      try {
        if (typeof el2.scrollTo === "function") {
          try {
            el2.scrollTo({ left: L, top: T, behavior: "instant" });
          } catch {
            el2.scrollTo(L, T);
          }
        }
      } catch {
      }
      try {
        el2.scrollLeft = L;
        el2.scrollTop = T;
      } catch {
      }
      if (isDocumentScrollRoot(el2, doc) && win && typeof win.scrollTo === "function") {
        try {
          try {
            win.scrollTo({ left: L, top: T, behavior: "instant" });
          } catch {
            win.scrollTo(L, T);
          }
        } catch {
        }
      }
      tryHijackInstant(win, L, T);
      return true;
    } finally {
      restore();
    }
  }
  function readScrollPoint(el2, doc = document, win = window) {
    try {
      if (el2 && !isDocumentScrollRoot(el2, doc)) {
        return { x: Number(el2.scrollLeft) || 0, y: Number(el2.scrollTop) || 0 };
      }
    } catch {
    }
    try {
      return {
        x: Number(win.scrollX ?? win.pageXOffset) || 0,
        y: Number(win.scrollY ?? win.pageYOffset) || 0
      };
    } catch {
      return { x: 0, y: 0 };
    }
  }
  function waitForScrollSettle(el2, opts = {}) {
    const doc = opts.doc || el2?.ownerDocument || document;
    const win = opts.win || doc.defaultView || window;
    const timeoutMs = Number.isFinite(Number(opts.timeoutMs)) ? Math.max(0, Number(opts.timeoutMs)) : 480;
    return new Promise((resolve) => {
      let last = readScrollPoint(el2, doc, win);
      let stableFrames = 0;
      const t0 = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
      let done = false;
      const finish = () => {
        if (done) return;
        done = true;
        resolve();
      };
      const tick = () => {
        if (done) return;
        const now = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
        const p = readScrollPoint(el2, doc, win);
        if (Math.abs(p.x - last.x) < 1 && Math.abs(p.y - last.y) < 1) stableFrames += 1;
        else stableFrames = 0;
        last = p;
        if (stableFrames >= 2 || now - t0 >= timeoutMs) {
          finish();
          return;
        }
        requestAnimationFrame(tick);
      };
      requestAnimationFrame(tick);
    });
  }
  function composedParent2(node) {
    if (!node || node.nodeType !== 1) return null;
    const el2 = (
      /** @type {Element} */
      node
    );
    if (el2.parentElement) return el2.parentElement;
    try {
      const root = typeof el2.getRootNode === "function" ? el2.getRootNode() : null;
      if (root && typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot) {
        return root.host || null;
      }
    } catch {
    }
    return null;
  }
  function overflowAllowsScroll(overflow) {
    const o = String(overflow || "").toLowerCase();
    return o === "auto" || o === "scroll" || o === "overlay";
  }
  function isDocumentScrollRoot(el2, doc) {
    try {
      const se = doc.scrollingElement;
      if (se && el2 === se) return true;
      if (el2 === doc.documentElement || el2 === doc.body) return true;
    } catch {
    }
    return false;
  }
  function getScrollCapacity(el2, doc = document) {
    if (!el2 || el2.nodeType !== 1) {
      return { canY: false, canX: false, maxTop: 0, maxLeft: 0 };
    }
    let maxTop = 0;
    let maxLeft = 0;
    try {
      maxTop = Math.max(0, (el2.scrollHeight || 0) - (el2.clientHeight || 0));
      maxLeft = Math.max(0, (el2.scrollWidth || 0) - (el2.clientWidth || 0));
    } catch {
      return { canY: false, canX: false, maxTop: 0, maxLeft: 0 };
    }
    if (isDocumentScrollRoot(el2, doc)) {
      return {
        canY: maxTop > EDGE_EPS,
        canX: maxLeft > EDGE_EPS,
        maxTop,
        maxLeft
      };
    }
    let oy = "";
    let ox = "";
    try {
      const cs = (el2.ownerDocument?.defaultView || window).getComputedStyle(el2);
      oy = cs?.overflowY || "";
      ox = cs?.overflowX || "";
    } catch {
      return { canY: false, canX: false, maxTop: 0, maxLeft: 0 };
    }
    return {
      canY: overflowAllowsScroll(oy) && maxTop > EDGE_EPS,
      canX: overflowAllowsScroll(ox) && maxLeft > EDGE_EPS,
      maxTop,
      maxLeft
    };
  }
  function canScrollInDirection(el2, axis, sign) {
    if (!el2) return false;
    try {
      if (axis === "y") {
        const top = el2.scrollTop || 0;
        if (sign < 0) return top > EDGE_EPS;
        const max2 = Math.max(0, (el2.scrollHeight || 0) - (el2.clientHeight || 0));
        return top < max2 - EDGE_EPS;
      }
      const left = el2.scrollLeft || 0;
      if (sign < 0) return left > EDGE_EPS;
      const max = Math.max(0, (el2.scrollWidth || 0) - (el2.clientWidth || 0));
      return left < max - EDGE_EPS;
    } catch {
      return false;
    }
  }
  function scrollElementBy(el2, deltaX, deltaY, behavior = "smooth", doc = document, win = window) {
    if (!el2) return false;
    const dx = Number(deltaX) || 0;
    const dy = Number(deltaY) || 0;
    if (!dx && !dy) return false;
    if (isInstantScrollBehavior(behavior)) {
      const left = (Number(el2.scrollLeft) || 0) + dx;
      const top = (Number(el2.scrollTop) || 0) + dy;
      return applyInstantScrollTo(el2, left, top, doc, win);
    }
    const opts = { left: dx, top: dy, behavior };
    try {
      if (typeof el2.scrollBy === "function") {
        el2.scrollBy(opts);
        return true;
      }
    } catch {
    }
    try {
      if (behavior === "smooth" && typeof el2.scrollTo === "function") {
        el2.scrollTo({
          left: (el2.scrollLeft || 0) + dx,
          top: (el2.scrollTop || 0) + dy,
          behavior
        });
        return true;
      }
    } catch {
    }
    try {
      if (dx) el2.scrollLeft = (el2.scrollLeft || 0) + dx;
      if (dy) el2.scrollTop = (el2.scrollTop || 0) + dy;
      return true;
    } catch {
    }
    if (isDocumentScrollRoot(el2, doc) && win && typeof win.scrollBy === "function") {
      try {
        win.scrollBy(opts);
        return true;
      } catch {
        try {
          win.scrollBy(dx, dy);
          return true;
        } catch {
        }
      }
    }
    return false;
  }
  function pickAxis(cap, el2, sign) {
    if (cap.canY && canScrollInDirection(el2, "y", sign)) return "y";
    if (cap.canX && canScrollInDirection(el2, "x", sign)) return "x";
    return null;
  }
  function findScrollTargetAtPoint(clientX, clientY, sign, ctx = {}) {
    const doc = ctx.doc || document;
    const x = Number(clientX);
    const y = Number(clientY);
    if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
    let start = deepElementFromPoint(x, y, doc);
    if (!start) {
      const se = doc.scrollingElement || doc.documentElement || doc.body;
      if (se) {
        const cap = getScrollCapacity(se, doc);
        const axis = pickAxis(cap, se, sign);
        if (axis) return { el: se, axis };
      }
      return null;
    }
    if (start.nodeType !== 1) {
      start = start.parentElement || /** @type {Element|null} */
      composedParent2(start);
    }
    let n = (
      /** @type {Element|null} */
      start
    );
    let depth = 0;
    let seenDocRoot = null;
    while (n && n.nodeType === 1 && depth++ < 64) {
      if (n.tagName === "IFRAME" || n.tagName === "FRAME") {
        return null;
      }
      try {
        const id = n.id || "";
        if (id === "kpv2-cursor" || id === "kpv2-frame-hover" || typeof id === "string" && id.startsWith("kpv2-")) {
          n = composedParent2(n);
          continue;
        }
        if (n.classList) {
          let skip = false;
          n.classList.forEach((c) => {
            if (typeof c === "string" && c.startsWith("kpv2-")) skip = true;
          });
          if (skip) {
            n = composedParent2(n);
            continue;
          }
        }
      } catch {
      }
      const cap = getScrollCapacity(n, doc);
      if (cap.canY || cap.canX) {
        if (isDocumentScrollRoot(n, doc)) {
          seenDocRoot = n;
          n = composedParent2(n);
          continue;
        }
        const axis = pickAxis(cap, n, sign);
        if (axis) return { el: n, axis };
      }
      n = composedParent2(n);
    }
    const candidates = [];
    try {
      if (doc.scrollingElement) candidates.push(doc.scrollingElement);
    } catch {
    }
    try {
      if (doc.documentElement) candidates.push(doc.documentElement);
    } catch {
    }
    try {
      if (doc.body) candidates.push(doc.body);
    } catch {
    }
    if (seenDocRoot) candidates.push(seenDocRoot);
    const tried = /* @__PURE__ */ new Set();
    for (const el2 of candidates) {
      if (!el2 || tried.has(el2)) continue;
      tried.add(el2);
      const cap = getScrollCapacity(el2, doc);
      const axis = pickAxis(cap, el2, sign);
      if (axis) return { el: el2, axis };
    }
    return null;
  }
  function isKeyPilotScrollChrome(n) {
    try {
      const id = n.id || "";
      if (id === "kpv2-cursor" || id === "kpv2-frame-hover" || typeof id === "string" && id.startsWith("kpv2-")) {
        return true;
      }
      if (n.classList) {
        let skip = false;
        n.classList.forEach((c) => {
          if (typeof c === "string" && c.startsWith("kpv2-")) skip = true;
        });
        if (skip) return true;
      }
    } catch {
    }
    return false;
  }
  function isWideOverflowTarget(el2) {
    if (!el2 || el2.nodeType !== 1) return false;
    let r = null;
    try {
      r = el2.getBoundingClientRect();
    } catch {
      r = null;
    }
    if (!r || !(r.width > 1) || !(r.height > 1)) return false;
    return r.width > r.height + 1;
  }
  function isCarouselLikeOverflowTarget(el2, cap) {
    if (!el2 || !cap || !cap.canX || cap.canY) return false;
    return isWideOverflowTarget(el2);
  }
  function findScrollableAtPoint(clientX, clientY, ctx = {}) {
    const doc = ctx.doc || document;
    const skipWide = !!ctx.skipWideTargets;
    const x = Number(clientX);
    const y = Number(clientY);
    if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
    let start = deepElementFromPoint(x, y, doc);
    if (!start) {
      const se = doc.scrollingElement || doc.documentElement || doc.body;
      if (!se) return null;
      const cap = getScrollCapacity(se, doc);
      if (cap.canX || cap.canY) return { el: se, canX: cap.canX, canY: cap.canY };
      return null;
    }
    if (start.nodeType !== 1) {
      start = start.parentElement || /** @type {Element|null} */
      composedParent2(start);
    }
    let n = (
      /** @type {Element|null} */
      start
    );
    let depth = 0;
    let seenDocRoot = null;
    while (n && n.nodeType === 1 && depth++ < 64) {
      if (n.tagName === "IFRAME" || n.tagName === "FRAME") {
        return null;
      }
      if (isKeyPilotScrollChrome(n)) {
        n = composedParent2(n);
        continue;
      }
      const cap = getScrollCapacity(n, doc);
      if (cap.canY || cap.canX) {
        if (isDocumentScrollRoot(n, doc)) {
          seenDocRoot = n;
          n = composedParent2(n);
          continue;
        }
        if (skipWide && isCarouselLikeOverflowTarget(n, cap)) {
          n = composedParent2(n);
          continue;
        }
        return { el: n, canX: cap.canX, canY: cap.canY };
      }
      n = composedParent2(n);
    }
    const candidates = [];
    try {
      if (doc.scrollingElement) candidates.push(doc.scrollingElement);
    } catch {
    }
    try {
      if (doc.documentElement) candidates.push(doc.documentElement);
    } catch {
    }
    try {
      if (doc.body) candidates.push(doc.body);
    } catch {
    }
    if (seenDocRoot) candidates.push(seenDocRoot);
    const tried = /* @__PURE__ */ new Set();
    for (const el2 of candidates) {
      if (!el2 || tried.has(el2)) continue;
      tried.add(el2);
      const cap = getScrollCapacity(el2, doc);
      if (cap.canX || cap.canY) return { el: el2, canX: cap.canX, canY: cap.canY };
    }
    return null;
  }
  function scrollByAtPoint(clientX, clientY, deltaX, deltaY, behavior = "auto", ctx = {}) {
    const doc = ctx.doc || document;
    const win = ctx.win || (doc.defaultView || window);
    let dx = Number(deltaX) || 0;
    let dy = Number(deltaY) || 0;
    if (!dx && !dy) return { scrolled: false, el: null };
    const target = findScrollableAtPoint(clientX, clientY, { doc, win });
    if (!target) {
      try {
        const se = doc.scrollingElement || doc.documentElement || doc.body;
        if (se && isInstantScrollBehavior(behavior)) {
          const ok2 = applyInstantScrollTo(
            se,
            (Number(se.scrollLeft) || 0) + dx,
            (Number(se.scrollTop) || 0) + dy,
            doc,
            win
          );
          return { scrolled: ok2, el: se };
        }
        if (win && typeof win.scrollBy === "function") {
          win.scrollBy({ left: dx, top: dy, behavior });
          return { scrolled: true, el: se || null };
        }
      } catch {
      }
      return { scrolled: false, el: null };
    }
    if (!target.canX) dx = 0;
    if (!target.canY) dy = 0;
    if (!dx && !dy) return { scrolled: false, el: target.el };
    const ok = scrollElementBy(target.el, dx, dy, behavior, doc, win);
    return { scrolled: ok, el: target.el };
  }
  function scrollAtPoint(clientX, clientY, sign, deltaPx, behavior = "smooth", ctx = {}) {
    const doc = ctx.doc || document;
    const win = ctx.win || (doc.defaultView || window);
    const amount = Math.abs(Number(deltaPx)) || 0;
    const s = sign < 0 ? -1 : 1;
    if (!amount) {
      return { scrolled: false, axis: null, el: null };
    }
    const target = findScrollTargetAtPoint(clientX, clientY, s, { doc, win });
    if (!target) {
      try {
        const se = doc.scrollingElement || doc.documentElement || doc.body;
        if (se && isInstantScrollBehavior(behavior)) {
          const ok2 = applyInstantScrollTo(
            se,
            Number(se.scrollLeft) || 0,
            (Number(se.scrollTop) || 0) + s * amount,
            doc,
            win
          );
          return { scrolled: ok2, axis: "y", el: se };
        }
        if (win && typeof win.scrollBy === "function") {
          win.scrollBy({ top: s * amount, left: 0, behavior });
          return { scrolled: true, axis: "y", el: se || null };
        }
      } catch {
      }
      return { scrolled: false, axis: null, el: null };
    }
    const { el: el2, axis } = target;
    const dx = axis === "x" ? s * amount : 0;
    const dy = axis === "y" ? s * amount : 0;
    const ok = scrollElementBy(el2, dx, dy, behavior, doc, win);
    return { scrolled: ok, axis, el: el2 };
  }
  function scrollElementToEdge(el2, axis, sign, behavior = "smooth", doc = document, win = window) {
    if (!el2 || axis !== "y" && axis !== "x") return false;
    const s = sign < 0 ? -1 : 1;
    let left = 0;
    let top = 0;
    try {
      if (axis === "y") {
        left = el2.scrollLeft || 0;
        top = s < 0 ? 0 : Math.max(0, (el2.scrollHeight || 0) - (el2.clientHeight || 0));
      } else {
        top = el2.scrollTop || 0;
        left = s < 0 ? 0 : Math.max(0, (el2.scrollWidth || 0) - (el2.clientWidth || 0));
      }
    } catch {
      return false;
    }
    if (isInstantScrollBehavior(behavior)) {
      return applyInstantScrollTo(el2, left, top, doc, win);
    }
    const opts = { left, top, behavior };
    try {
      if (typeof el2.scrollTo === "function") {
        el2.scrollTo(opts);
        return true;
      }
    } catch {
    }
    try {
      if (axis === "y") el2.scrollTop = top;
      else el2.scrollLeft = left;
      return true;
    } catch {
    }
    if (isDocumentScrollRoot(el2, doc) && win && typeof win.scrollTo === "function") {
      try {
        win.scrollTo(opts);
        return true;
      } catch {
        try {
          if (axis === "y") win.scrollTo(win.pageXOffset || 0, top);
          else win.scrollTo(left, win.pageYOffset || 0);
          return true;
        } catch {
        }
      }
    }
    return false;
  }
  function scrollToEdgeAtPoint(clientX, clientY, sign, behavior = "smooth", ctx = {}) {
    const doc = ctx.doc || document;
    const win = ctx.win || (doc.defaultView || window);
    const s = sign < 0 ? -1 : 1;
    const target = findScrollTargetAtPoint(clientX, clientY, s, { doc, win });
    if (!target) {
      try {
        if (win && typeof win.scrollTo === "function") {
          const se = doc.scrollingElement || doc.documentElement || doc.body;
          const top = s < 0 ? 0 : Math.max(0, (se?.scrollHeight || doc.body?.scrollHeight || 0) - (win.innerHeight || 0));
          const left = win.pageXOffset || 0;
          if (se && isInstantScrollBehavior(behavior)) {
            applyInstantScrollTo(se, left, top, doc, win);
          } else {
            win.scrollTo({ top, left, behavior });
          }
          return { scrolled: true, axis: "y", el: se || null };
        }
      } catch {
      }
      return { scrolled: false, axis: null, el: null };
    }
    const { el: el2, axis } = target;
    const ok = scrollElementToEdge(el2, axis, s, behavior, doc, win);
    return { scrolled: ok, axis, el: el2 };
  }

  // src/modules/overlay-manager.js
  var EDGE_JUMP_ICON_PATHS = Object.freeze({
    // Up arrow + horizontal bar at tip (top)
    top: "M233.4 105.4c12.5-12.5 32.8-12.5 45.3 0l96 96c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L288 205.3V384c0 17.7-14.3 32-32 32s-32-14.3-32-32V205.3l-41.4 41.4c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l96-96zM64 64c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H96C78.3 96 64 81.7 64 64z",
    // Down arrow + horizontal bar at tip (bottom)
    bottom: "M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l96-96c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L288 306.7V128c0-17.7-14.3-32-32-32s-32 14.3-32 32V306.7l-41.4-41.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l96 96zM64 448c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H96c-17.7 0-32-14.3-32-32z"
  });
  var OverlayManager = class {
    constructor() {
      this.renderingMode = "dom";
      this.canvasOverlay = null;
      this.canvasContext = null;
      this.cssCustomPropsOverlay = null;
      this.focusOverlay = null;
      this.inspectorOverlay = null;
      this._inspectorOverlayKind = null;
      this.inspectorModeIndicator = null;
      this._inspectorIndicatorOpts = null;
      this.deleteOverlay = null;
      this.colsOverlay = null;
      this.viewportModalFrame = null;
      this.escExitLabelText = null;
      this.escExitLabelHover = null;
      this.textFocusEscHint = null;
      this._textFocusEscKeyLabel = "Esc";
      this.textHoverActivateHint = null;
      this._textHoverActivateKeyLabel = "F";
      this.hoverClickLabelText = "F clicks";
      this._edgeJumpFadeEl = null;
      this._edgeJumpFadeToken = 0;
      this.fontInfoOverlays = [];
      this.popupManager = new PopupManager();
      this.popover = new PopoverController(this);
      this.highlightManager = new HighlightManager();
      this.overlayObserver = null;
      this.resizeObserver = null;
      this.overlayVisibility = {
        focus: true,
        delete: true,
        escExitLabel: true
      };
      this._modeSettings = {
        clickMode: null,
        textMode: null
      };
      this.debugPanel = null;
      this.debugPanelUpdateInterval = null;
      this._shadowDebugHud = null;
      this._shadowDebugHudEnabled = !!(FEATURE_FLAGS && FEATURE_FLAGS.DEBUG_SHADOW_ROOT_HUD);
      this._shadowDebugPaintOverride = DEFAULT_SETTINGS.clickMode && DEFAULT_SETTINGS.clickMode.paintStrategy === "BC" ? "BC" : null;
      this._shadowDebugLastInfo = null;
      this._useDomHoverFocusColors = false;
      this._focusPaintUsesFixedOverlay = false;
      this._focusPaintUsesInTargetRing = false;
      this._preferADuringScroll = false;
      this._inTargetRing = null;
      this._inTargetHost = null;
      this._inTargetHostPosRestore = null;
      this._textFocusCurrentElement = null;
      this._textFocusStyledElements = /* @__PURE__ */ new Set();
      this._textFocusPaintHost = null;
      this._textFocusAppliedStyle = null;
      this._textHoverCurrentElement = null;
      this._textHoverStyledElements = /* @__PURE__ */ new Set();
      this._lastFocusElement = null;
      this._lastFocusRect = null;
      this._focusClipContextCache = /* @__PURE__ */ new WeakMap();
      this._focusClipContextTtlMs = 500;
      this._focusClipInvalidateBound = null;
      this.focusOverlayPainter = new FocusOverlayPainter(this);
      this._installFocusClipCacheInvalidation();
      this._activeEphemeralEffects = /* @__PURE__ */ new Set();
      this._ephemeralEffectLifecycleInstalled = false;
      this._ephemeralEffectLifecycleDispose = null;
      this.setupOverlayObserver();
      this.highlightManager.initialize(this.overlayObserver);
    }
    setupOverlayObserver() {
      this.overlayObserver = new IntersectionObserver(
        (entries) => {
          entries.forEach((entry) => {
            const overlay = entry.target;
            const isVisible = entry.intersectionRatio > 0;
            if (overlay === this.focusOverlay) {
              this.overlayVisibility.focus = isVisible;
              if (!this._useDomHoverFocusColors) {
                overlay.style.visibility = isVisible ? "visible" : "hidden";
              } else {
                overlay.style.visibility = "visible";
              }
            } else if (overlay === this.inspectorOverlay || overlay === this.deleteOverlay) {
              this.overlayVisibility.delete = isVisible;
              overlay.style.visibility = isVisible ? "visible" : "hidden";
            } else if (overlay === this.escExitLabelText) {
              this.overlayVisibility.escExitLabel = isVisible;
              overlay.style.visibility = isVisible ? "visible" : "hidden";
            } else if (overlay === this.escExitLabelHover) {
              this.overlayVisibility.escExitLabel = isVisible;
              overlay.style.visibility = isVisible ? "visible" : "hidden";
            }
          });
        },
        {
          rootMargin: "10px",
          threshold: [0, 1]
        }
      );
    }
    /**
     * @param {Element|null} focusEl
     * @param {Element|null} inspectorEl shared pick target (Delete/Cols/…)
     * @param {string} mode
     * @param {Element|null} [focusedTextElement]
     * @param {DOMRect|object|null} [focusRectOverride]
     * @param {string|null} [inspectorKind] INSPECTOR_KIND when mode is inspector
     */
    updateOverlays(focusEl, inspectorEl, mode, focusedTextElement = null, focusRectOverride = null, inspectorKind = null) {
      const showClickModeFocus = modeShowsClickableHover(mode);
      if (window.KEYPILOT_DEBUG && focusEl) {
        console.log("[KeyPilot Debug] Updating overlays:", {
          focusElement: focusEl.tagName,
          mode,
          willShowFocus: showClickModeFocus,
          focusedTextElement: focusedTextElement?.tagName
        });
      }
      if (mode === "text_focus" && focusedTextElement) {
        this._applyTextFocusElementStyling(focusedTextElement);
      } else {
        this._clearTextFocusElementStyling();
      }
      if (showClickModeFocus) {
        this.updateFocusOverlay(focusEl, mode, focusRectOverride);
        if (mode === "text_focus") {
          if (focusedTextElement) {
            this.updateTextModeLabels(focusedTextElement);
            this.updateTextFocusEscHint(focusedTextElement);
          } else {
            this.hideTextModeLabels();
            this.hideTextFocusEscHint();
          }
        } else {
          this.hideTextModeLabels();
          this.hideTextFocusEscHint();
        }
      } else {
        this.hideFocusOverlay();
      }
      if (!focusEl) {
        this._clearTextHoverElementStyling();
      }
      this.updateViewportModalFrame(mode === "text_focus" && FEATURE_FLAGS.SHOW_WINDOW_OUTLINE);
      const inInspector = mode === MODES.INSPECTOR || mode === "delete" || mode === "cols";
      if (inInspector) {
        const kind = inspectorKind || (mode === "delete" ? "delete" : mode === "cols" ? "cols" : null);
        this.updateInspectorOverlay(inspectorEl, kind);
        this.showInspectorModeIndicator({ kind });
      } else {
        this.hideInspectorOverlay();
        this.hideInspectorModeIndicator();
        this.clearInspectorPickedOverlays();
      }
      if (mode === "highlight") {
        this.highlightManager.hideHighlightOverlay();
        this.highlightManager.showHighlightModeIndicator();
      } else {
        this.highlightManager.hideHighlightOverlay();
        this.highlightManager.hideHighlightModeIndicator();
        this.highlightManager.hideHighlightRectangleOverlay();
      }
    }
    updateInspectorOverlay(element, kind = null) {
      if (!element) {
        this.hideInspectorOverlay();
        return;
      }
      const def = getInspectorDef(kind);
      const border = def?.borderColor || COLORS.DELETE_RED;
      const shadow = def?.shadowColor || COLORS.DELETE_SHADOW;
      const shadowBright = def?.shadowBrightColor || COLORS.DELETE_SHADOW_BRIGHT;
      if (!this.inspectorOverlay) {
        this.inspectorOverlay = this.createElement("div", {
          className: CSS_CLASSES.INSPECTOR_OVERLAY || CSS_CLASSES.DELETE_OVERLAY,
          style: `
          position: fixed;
          pointer-events: none;
          z-index: ${Z_INDEX.OVERLAYS};
          border: 3px solid ${border};
          box-shadow: 0 0 0 2px ${shadow}, 0 0 12px 2px ${shadowBright};
          background: transparent;
          will-change: transform;
        `
        });
        document.body.appendChild(this.inspectorOverlay);
        this.deleteOverlay = this.inspectorOverlay;
        if (this.overlayObserver) {
          this.overlayObserver.observe(this.inspectorOverlay);
        }
      }
      if (kind && kind !== this._inspectorOverlayKind) {
        this._inspectorOverlayKind = kind;
        this.inspectorOverlay.style.border = `3px solid ${border}`;
        this.inspectorOverlay.style.boxShadow = `0 0 0 2px ${shadow}, 0 0 12px 2px ${shadowBright}`;
        try {
          this.inspectorOverlay.setAttribute("data-kp-inspector-kind", kind);
        } catch {
        }
      }
      const rect = this.getBestRect(element);
      if (rect.width > 0 && rect.height > 0) {
        this.inspectorOverlay.style.left = `${rect.left}px`;
        this.inspectorOverlay.style.top = `${rect.top}px`;
        this.inspectorOverlay.style.width = `${rect.width}px`;
        this.inspectorOverlay.style.height = `${rect.height}px`;
        this.inspectorOverlay.style.display = "block";
        this.inspectorOverlay.style.visibility = "visible";
      } else {
        this.hideInspectorOverlay();
      }
    }
    hideInspectorOverlay() {
      if (this.inspectorOverlay) {
        this.inspectorOverlay.style.display = "none";
      }
    }
    /**
     * Paint persistent outlines for cumulatively picked elements + union rect.
     * @param {Element[]} elements
     * @param {{ left: number, top: number, width: number, height: number }|null} unionRect
     * @param {string|null|undefined} kind
     */
    updateInspectorPickedOverlays(elements, unionRect, kind = null) {
      const def = getInspectorDef(kind);
      const border = def?.borderColor || COLORS.HIGHLIGHT_BLUE;
      const fill = COLORS.HIGHLIGHT_SELECTION_BG || "rgba(0,120,255,0.3)";
      if (!this._inspectorPickedOverlays) this._inspectorPickedOverlays = [];
      for (const ov of this._inspectorPickedOverlays) {
        try {
          ov.remove();
        } catch {
        }
      }
      this._inspectorPickedOverlays = [];
      const list = Array.isArray(elements) ? elements : [];
      for (const el2 of list) {
        if (!el2 || !el2.isConnected) continue;
        const rect = this.getBestRect(el2);
        if (rect.width <= 0 || rect.height <= 0) continue;
        const ov = this.createElement("div", {
          className: CSS_CLASSES.INSPECTOR_PICKED_OVERLAY || "kpv2-inspector-picked-overlay",
          style: `
          position: fixed;
          pointer-events: none;
          z-index: ${Z_INDEX.OVERLAYS_BELOW};
          left: ${rect.left}px;
          top: ${rect.top}px;
          width: ${rect.width}px;
          height: ${rect.height}px;
          border: 2px solid ${border};
          background: ${fill};
          box-sizing: border-box;
        `
        });
        try {
          document.body.appendChild(ov);
        } catch {
        }
        this._inspectorPickedOverlays.push(ov);
      }
      if (!this._inspectorUnionOverlay) {
        this._inspectorUnionOverlay = this.createElement("div", {
          className: CSS_CLASSES.INSPECTOR_UNION_OVERLAY || "kpv2-inspector-union-overlay",
          style: `
          position: fixed;
          pointer-events: none;
          z-index: ${Z_INDEX.OVERLAYS_BELOW_2};
          border: 2px dashed ${border};
          background: transparent;
          box-sizing: border-box;
          display: none;
        `
        });
        try {
          document.body.appendChild(this._inspectorUnionOverlay);
        } catch {
        }
      }
      if (unionRect && unionRect.width > 0 && unionRect.height > 0) {
        this._inspectorUnionOverlay.style.left = `${unionRect.left}px`;
        this._inspectorUnionOverlay.style.top = `${unionRect.top}px`;
        this._inspectorUnionOverlay.style.width = `${unionRect.width}px`;
        this._inspectorUnionOverlay.style.height = `${unionRect.height}px`;
        this._inspectorUnionOverlay.style.border = `2px dashed ${border}`;
        this._inspectorUnionOverlay.style.display = "block";
      } else {
        this._inspectorUnionOverlay.style.display = "none";
      }
    }
    clearInspectorPickedOverlays() {
      if (this._inspectorPickedOverlays) {
        for (const ov of this._inspectorPickedOverlays) {
          try {
            ov.remove();
          } catch {
          }
        }
        this._inspectorPickedOverlays = [];
      }
      if (this._inspectorUnionOverlay) {
        this._inspectorUnionOverlay.style.display = "none";
      }
    }
    /**
     * Remember confirm-key / kind for the top-right instruction chip.
     * Call when entering inspector so updateOverlays can keep the chip visible.
     * @param {{ kind?: string|null, confirmKey?: string }} opts
     */
    setInspectorModeIndicatorOpts(opts = {}) {
      this._inspectorIndicatorOpts = {
        kind: opts.kind || null,
        confirmKey: opts.confirmKey || ""
      };
    }
    /**
     * Top-right companion instruction while inspector pick is active
     * (same pattern as Text Select "Press H again to finish selection").
     * @param {{ kind?: string|null, confirmKey?: string, message?: string }} [opts]
     */
    showInspectorModeIndicator(opts = {}) {
      const prev = this._inspectorIndicatorOpts || {};
      const kind = opts.kind || prev.kind || null;
      const confirmKey = opts.confirmKey || prev.confirmKey || "";
      if (opts.kind || opts.confirmKey) {
        this._inspectorIndicatorOpts = { kind, confirmKey };
      }
      const def = getInspectorDef(kind);
      const modeText = opts.message && String(opts.message).trim() || getInspectorInstructionText(kind, confirmKey);
      const bg = def?.borderColor || COLORS.COLS_PURPLE;
      const shadow = def?.shadowColor || COLORS.COLS_SHADOW;
      if (this.inspectorModeIndicator) {
        this.inspectorModeIndicator.textContent = modeText;
        this.inspectorModeIndicator.style.display = "block";
        this.inspectorModeIndicator.style.background = bg;
        this.inspectorModeIndicator.style.boxShadow = `0 2px 10px ${shadow}`;
        try {
          if (kind) this.inspectorModeIndicator.setAttribute("data-kp-inspector-kind", kind);
        } catch {
        }
        return;
      }
      this.inspectorModeIndicator = this.createElement("div", {
        className: CSS_CLASSES.INSPECTOR_MODE_INDICATOR || "kpv2-inspector-mode-indicator",
        style: `
        position: fixed;
        top: 20px;
        right: 20px;
        background: ${bg};
        color: white;
        padding: 10px 14px;
        font-size: 14px;
        font-weight: bold;
        font-family: ${KP_UI_FONT};
        border-radius: 6px;
        box-shadow: 0 2px 10px ${shadow};
        z-index: ${Z_INDEX.MESSAGE_BOX};
        pointer-events: none;
        max-width: min(360px, calc(100vw - 40px));
        line-height: 1.35;
        will-change: transform, opacity;
        animation: kpv2-pulse 1.5s ease-in-out infinite;
        letter-spacing: 0.01em;
      `
      });
      try {
        if (kind) this.inspectorModeIndicator.setAttribute("data-kp-inspector-kind", kind);
      } catch {
      }
      this.inspectorModeIndicator.textContent = modeText;
      try {
        document.body.appendChild(this.inspectorModeIndicator);
      } catch {
        try {
          document.documentElement.appendChild(this.inspectorModeIndicator);
        } catch {
        }
      }
    }
    hideInspectorModeIndicator() {
      if (this.inspectorModeIndicator) {
        try {
          this.inspectorModeIndicator.remove();
        } catch {
        }
        this.inspectorModeIndicator = null;
      }
      this._inspectorIndicatorOpts = null;
    }
    /** @deprecated use updateInspectorOverlay(el, kind) */
    updateDeleteOverlay(element) {
      this.updateInspectorOverlay(element, "delete");
    }
    /** @deprecated use hideInspectorOverlay() */
    hideDeleteOverlay() {
      this.hideInspectorOverlay();
    }
    /** @deprecated use updateInspectorOverlay(el, 'cols') */
    updateColsOverlay(element) {
      this.updateInspectorOverlay(element, "cols");
    }
    /** @deprecated use hideInspectorOverlay() */
    hideColsOverlay() {
      this.hideInspectorOverlay();
    }
    // SELECTION RECTANGLE FUNCTIONALITY ONLY
    updateHighlightRectangleOverlay(startPosition, currentPosition) {
      return this.highlightManager.updateHighlightRectangleOverlay(startPosition, currentPosition);
    }
    // SELECTION RECTANGLE FUNCTIONALITY ONLY
    hideHighlightRectangleOverlay() {
      return this.highlightManager.hideHighlightRectangleOverlay();
    }
    // SELECTION RECTANGLE FUNCTIONALITY ONLY
    removeHighlightRectangleOverlay() {
      return this.highlightManager.removeHighlightRectangleOverlay();
    }
    // SELECTION RECTANGLE FUNCTIONALITY ONLY
    updateHighlightSelectionOverlays(selection) {
      return this.highlightManager.updateHighlightSelectionOverlays(selection);
    }
    // SELECTION RECTANGLE FUNCTIONALITY ONLY
    clearHighlightSelectionOverlays() {
      return this.highlightManager.clearHighlightSelectionOverlays();
    }
    // SELECTION RECTANGLE FUNCTIONALITY ONLY
    setSelectionMode(mode) {
      return this.highlightManager.setSelectionMode(mode);
    }
    // SELECTION RECTANGLE FUNCTIONALITY ONLY
    getSelectionMode() {
      return this.highlightManager.getSelectionMode();
    }
    // SELECTION RECTANGLE FUNCTIONALITY ONLY
    startCharacterSelection(position, findTextNodeAtPosition, getTextOffsetAtPosition) {
      return this.highlightManager.startCharacterSelection(position, findTextNodeAtPosition, getTextOffsetAtPosition);
    }
    // SELECTION RECTANGLE FUNCTIONALITY ONLY
    updateCharacterSelection(currentPosition, startPosition, findTextNodeAtPosition, getTextOffsetAtPosition) {
      return this.highlightManager.updateCharacterSelection(currentPosition, startPosition, findTextNodeAtPosition, getTextOffsetAtPosition);
    }
    // SELECTION RECTANGLE FUNCTIONALITY ONLY
    updateRectangleSelectionFromCarets(startPosition, currentPosition, findTextNodeAtPosition, getTextOffsetAtPosition) {
      return this.highlightManager.updateRectangleSelectionFromCarets(
        startPosition,
        currentPosition,
        findTextNodeAtPosition,
        getTextOffsetAtPosition
      );
    }
    updateElementRectangleSelection(startPosition, currentPosition) {
      return this.highlightManager.updateElementRectangleSelection(startPosition, currentPosition);
    }
    getMatchedElements() {
      return this.highlightManager.getMatchedElements?.() || [];
    }
    clearElementSelection() {
      return this.highlightManager.clearElementSelection?.();
    }
    /**
     * Live-refresh highlight rectangle + selection during scroll (document-anchored origin).
     */
    syncHighlightSelectionToScroll(currentViewportMouse, findTextNodeAtPosition, getTextOffsetAtPosition) {
      return this.highlightManager.syncSelectionToScroll(
        currentViewportMouse,
        findTextNodeAtPosition,
        getTextOffsetAtPosition
      );
    }
    // SELECTION RECTANGLE FUNCTIONALITY ONLY
    peekCharacterSelectedText() {
      return this.highlightManager.peekCharacterSelectedText();
    }
    // SELECTION RECTANGLE FUNCTIONALITY ONLY
    completeCharacterSelection() {
      return this.highlightManager.completeCharacterSelection();
    }
    // SELECTION RECTANGLE FUNCTIONALITY ONLY
    clearCharacterSelection() {
      return this.highlightManager.clearCharacterSelection();
    }
    // SELECTION RECTANGLE FUNCTIONALITY ONLY
    /**
     * Create selection overlays for a specific range with shadow DOM support
     * @param {Range} range - DOM Range object
     */
    createSelectionOverlaysForRangeWithShadowSupport(range) {
      if (!range || range.collapsed) {
        return;
      }
      try {
        const rects = this.getClientRectsWithShadowSupport(range);
        for (let i = 0; i < rects.length; i++) {
          const rect = rects[i];
          if (rect.width <= 0 || rect.height <= 0) {
            continue;
          }
          const overlay = this.createElement("div", {
            className: CSS_CLASSES.HIGHLIGHT_SELECTION,
            style: `
            position: fixed;
            left: ${rect.left}px;
            top: ${rect.top}px;
            width: ${rect.width}px;
            height: ${rect.height}px;
            background: ${COLORS.HIGHLIGHT_SELECTION_BG};
            border: 1px solid ${COLORS.HIGHLIGHT_SELECTION_BORDER};
            pointer-events: none;
            z-index: ${Z_INDEX.OVERLAYS_BELOW};
            will-change: transform;
          `
          });
          document.body.appendChild(overlay);
          this.highlightSelectionOverlays.push(overlay);
          if (this.overlayObserver) {
            this.overlayObserver.observe(overlay);
          }
        }
        if (window.KEYPILOT_DEBUG && rects.length > 0) {
          console.log("[KeyPilot Debug] Created selection overlays for range with shadow DOM support:", {
            rectCount: rects.length,
            firstRect: {
              left: rects[0].left,
              top: rects[0].top,
              width: rects[0].width,
              height: rects[0].height
            }
          });
        }
      } catch (error) {
        console.warn("[KeyPilot] Error creating selection overlays for range with shadow DOM support:", error);
      }
    }
    // SELECTION RECTANGLE FUNCTIONALITY ONLY
    /**
     * Get client rectangles for a range with shadow DOM support
     * @param {Range} range - DOM Range object
     * @returns {DOMRectList|Array} - Client rectangles
     */
    getClientRectsWithShadowSupport(range) {
      try {
        const rects = range.getClientRects();
        if (rects && rects.length > 0) {
          return rects;
        }
        return this.getAlternativeClientRects(range);
      } catch (error) {
        console.warn("[KeyPilot] Error getting client rects with shadow DOM support:", error);
        return [];
      }
    }
    // SELECTION RECTANGLE FUNCTIONALITY ONLY
    /**
     * Get alternative client rectangles for shadow DOM ranges
     * @param {Range} range - DOM Range object
     * @returns {Array} - Array of rectangle objects
     */
    getAlternativeClientRects(range) {
      try {
        const rects = [];
        const boundingRect = range.getBoundingClientRect();
        if (boundingRect && boundingRect.width > 0 && boundingRect.height > 0) {
          rects.push(boundingRect);
        }
        if (rects.length === 0) {
          const shadowRects = this.calculateShadowDOMRects(range);
          rects.push(...shadowRects);
        }
        return rects;
      } catch (error) {
        console.warn("[KeyPilot] Error getting alternative client rects:", error);
        return [];
      }
    }
    // SELECTION RECTANGLE FUNCTIONALITY ONLY
    /**
     * Calculate rectangles for shadow DOM ranges manually
     * @param {Range} range - DOM Range object
     * @returns {Array} - Array of rectangle objects
     */
    calculateShadowDOMRects(range) {
      try {
        const rects = [];
        const startContainer = range.startContainer;
        const endContainer = range.endContainer;
        if (startContainer === endContainer && startContainer.nodeType === Node.TEXT_NODE) {
          const textRect = this.getTextNodeRect(startContainer, range.startOffset, range.endOffset);
          if (textRect) {
            rects.push(textRect);
          }
        } else {
          try {
            const boundingRect = range.getBoundingClientRect();
            if (boundingRect && boundingRect.width > 0 && boundingRect.height > 0) {
              rects.push(boundingRect);
            }
          } catch (error) {
          }
        }
        return rects;
      } catch (error) {
        console.warn("[KeyPilot] Error calculating shadow DOM rects:", error);
        return [];
      }
    }
    // SELECTION RECTANGLE FUNCTIONALITY ONLY
    /**
     * Get rectangle for a portion of a text node
     * @param {Text} textNode - Text node
     * @param {number} startOffset - Start character offset
     * @param {number} endOffset - End character offset
     * @returns {DOMRect|null} - Rectangle or null
     */
    getTextNodeRect(textNode, startOffset, endOffset) {
      try {
        const ownerDocument = textNode.ownerDocument || document;
        const tempRange = ownerDocument.createRange();
        tempRange.setStart(textNode, startOffset);
        tempRange.setEnd(textNode, endOffset);
        const rect = tempRange.getBoundingClientRect();
        return rect.width > 0 && rect.height > 0 ? rect : null;
      } catch (error) {
        console.warn("[KeyPilot] Error getting text node rect:", error);
        return null;
      }
    }
    /**
     * Create selection overlays for a specific range (legacy method for compatibility)
     * @param {Range} range - DOM Range object
     */
    createSelectionOverlaysForRange(range) {
      this.createSelectionOverlaysForRangeWithShadowSupport(range);
    }
    calculateLabelPosition(elementRect, labelHeight) {
      const viewportHeight = window.innerHeight;
      const viewportWidth = window.innerWidth;
      const belowPosition = {
        left: elementRect.left,
        top: elementRect.top + elementRect.height + 8,
        position: "below"
      };
      if (belowPosition.top + labelHeight > viewportHeight) {
        const abovePosition = {
          left: elementRect.left,
          top: elementRect.top - labelHeight - 8,
          position: "above"
        };
        if (abovePosition.top < 0) {
          return {
            left: elementRect.left + elementRect.width + 8,
            top: elementRect.top,
            position: "right"
          };
        }
        return abovePosition;
      }
      return belowPosition;
    }
    updateEscExitLabelText(element) {
      if (!element) {
        this.hideEscExitLabelText();
        return;
      }
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] updateEscExitLabelText called for:", {
          tagName: element.tagName,
          className: element.className,
          id: element.id
        });
      }
      if (!this.escExitLabelText) {
        this.escExitLabelText = this.createElement("div", {
          className: CSS_CLASSES.ESC_EXIT_LABEL,
          style: `
          position: fixed;
          pointer-events: none;
          z-index: ${Z_INDEX.OVERLAYS_ABOVE};
          will-change: transform, opacity;
          font-family: Arial, sans-serif;
          font-size: 14px;
          padding: 4px 8px;
          border-radius: 4px;
          white-space: nowrap;
          background-color: ${COLORS.ORANGE_BG};
          color: ${COLORS.ORANGE_TEXT};
          border: 1px solid ${COLORS.ORANGE_BORDER_SOLID};
        `
        });
        this.escExitLabelText.innerHTML = "Press <kbd>ESC</kbd> to exit";
        document.body.appendChild(this.escExitLabelText);
        this.labelHeight = this.escExitLabelText.offsetHeight;
        if (this.overlayObserver) this.overlayObserver.observe(this.escExitLabelText);
      }
      const rect = this.getBestRect(element);
      if (rect.width > 0 && rect.height > 0) {
        const position = this.calculateLabelPosition(rect, this.labelHeight);
        this.escExitLabelText.style.left = `${position.left}px`;
        this.escExitLabelText.style.top = `${position.top}px`;
        this.escExitLabelText.style.display = "block";
        this.escExitLabelText.style.visibility = "visible";
      } else {
        this.hideEscExitLabelText();
      }
    }
    updateEscExitLabelHover(element) {
      if (!element) {
        this.hideEscExitLabelHover();
        return;
      }
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] updateEscExitLabelHover called for:", {
          tagName: element.tagName,
          className: element.className,
          id: element.id
        });
      }
      if (!this.escExitLabelHover) {
        this.escExitLabelHover = this.createElement("div", {
          className: CSS_CLASSES.ESC_EXIT_LABEL,
          style: `
          position: fixed;
          pointer-events: none;
          z-index: ${Z_INDEX.OVERLAYS_ABOVE};
          will-change: transform, opacity;
          font-family: Arial, sans-serif;
          font-size: 14px;
          padding: 4px 8px;
          border-radius: 4px;
          white-space: nowrap;
          background-color: ${COLORS.FOCUS_GREEN_BG};
          color: ${COLORS.FOCUS_GREEN_TEXT};
          border: 1px solid ${COLORS.FOCUS_GREEN_SOLID};
        `
        });
        this.escExitLabelHover.innerHTML = this.formatHoverLabelText(this.hoverClickLabelText || "F clicks");
        document.body.appendChild(this.escExitLabelHover);
        this.labelHeight = this.escExitLabelHover.offsetHeight;
        if (this.overlayObserver) this.overlayObserver.observe(this.escExitLabelHover);
      } else {
        this.escExitLabelHover.innerHTML = this.formatHoverLabelText(this.hoverClickLabelText || "F clicks");
      }
      const rect = this.getBestRect(element);
      if (rect.width > 0 && rect.height > 0) {
        const position = this.calculateLabelPosition(rect, this.labelHeight);
        this.escExitLabelHover.style.left = `${position.left}px`;
        this.escExitLabelHover.style.top = `${position.top}px`;
        this.escExitLabelHover.style.display = "block";
        this.escExitLabelHover.style.visibility = "visible";
      } else {
        this.hideEscExitLabelHover();
      }
    }
    hideEscExitLabelText() {
      if (this.escExitLabelText) this.escExitLabelText.style.display = "none";
    }
    hideEscExitLabelHover() {
      if (this.escExitLabelHover) this.escExitLabelHover.style.display = "none";
    }
    hideEscExitLabel() {
      this.hideEscExitLabelText();
      this.hideEscExitLabelHover();
    }
    /**
     * Host-element class paint for focus + inspector hover.
     * @param {Element|null} focusEl
     * @param {Element|null} inspectorEl
     * @param {Element|null} prevFocusEl
     * @param {Element|null} prevInspectorEl
     * @param {string|null} [inspectorKind]
     */
    updateElementClasses(focusEl, inspectorEl, prevFocusEl, prevInspectorEl, inspectorKind = null) {
      const hostClasses = getAllInspectorHostClasses();
      if (prevFocusEl && prevFocusEl !== focusEl) {
        prevFocusEl.classList.remove(CSS_CLASSES.FOCUS);
      }
      if (prevInspectorEl && prevInspectorEl !== inspectorEl) {
        for (const c of hostClasses) {
          try {
            prevInspectorEl.classList.remove(c);
          } catch {
          }
        }
      }
      if (focusEl) {
        this._ensureStylesForElement(focusEl);
        focusEl.classList.add(CSS_CLASSES.FOCUS);
      }
      if (inspectorEl) {
        this._ensureStylesForElement(inspectorEl);
        const def = getInspectorDef(inspectorKind);
        try {
          inspectorEl.classList.add(CSS_CLASSES.INSPECTOR);
        } catch {
        }
        if (def?.hostClass) {
          try {
            inspectorEl.classList.add(def.hostClass);
          } catch {
          }
        }
      }
    }
    /**
     * Translate a rect from `element`'s viewport into the top-frame viewport.
     * Same-origin iframe fields report coordinates local to the canvas.
     * @param {Element} element
     * @param {{ left: number, top: number, width: number, height: number }} rect
     * @returns {{ left: number, top: number, width: number, height: number }}
     */
    _rectInTopViewport(element, rect) {
      if (!rect) return { left: 0, top: 0, width: 0, height: 0 };
      const out = {
        left: rect.left,
        top: rect.top,
        width: rect.width,
        height: rect.height
      };
      let win = null;
      try {
        win = element.ownerDocument?.defaultView || null;
      } catch {
        win = null;
      }
      let guard = 0;
      while (win && win !== window && guard++ < 8) {
        let frameEl = null;
        try {
          frameEl = win.frameElement;
        } catch {
          frameEl = null;
        }
        if (!frameEl) break;
        let fr = null;
        try {
          fr = frameEl.getBoundingClientRect();
        } catch {
          fr = null;
        }
        if (!fr) break;
        out.left += fr.left;
        out.top += fr.top;
        try {
          win = win.parent;
        } catch {
          break;
        }
      }
      return out;
    }
    getBestRect(element) {
      if (!element) return { left: 0, top: 0, width: 0, height: 0 };
      let rect = this._rectInTopViewport(element, element.getBoundingClientRect());
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] getBestRect for element:", {
          tagName: element.tagName,
          originalRect: {
            left: rect.left,
            top: rect.top,
            width: rect.width,
            height: rect.height
          }
        });
      }
      if (rect.height === 0 && element.children.length > 0) {
        for (const child of element.children) {
          const childRect = this._rectInTopViewport(child, child.getBoundingClientRect());
          if (childRect.height > 0) {
            if (element.tagName.toLowerCase() === "a") {
              const finalRect = {
                left: Math.min(rect.left, childRect.left),
                top: childRect.top,
                width: Math.max(rect.width, childRect.width),
                height: childRect.height
              };
              if (window.KEYPILOT_DEBUG) {
                console.log("[KeyPilot Debug] Using child rect for link:", finalRect);
              }
              return finalRect;
            }
            if (window.KEYPILOT_DEBUG) {
              console.log("[KeyPilot Debug] Using child rect:", childRect);
            }
            return childRect;
          }
        }
      }
      if (rect.height === 0 && element.textContent && element.textContent.trim()) {
        const finalRect = {
          left: rect.left,
          top: rect.top,
          width: Math.max(rect.width, 20),
          // Minimum width
          height: Math.max(rect.height, 20)
          // Minimum height
        };
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Using minimum dimensions:", finalRect);
        }
        return finalRect;
      }
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Using original rect:", rect);
      }
      return rect;
    }
    /**
     * KeyPilot-injected paint nodes. Must not contribute visual metrics
     * (a leftover in-target ring with `border-radius: 50%` is full-size).
     * @param {Element|null|undefined} el
     * @returns {boolean}
     */
    _isKeyPilotPaintNode(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      try {
        if (el2 === this._inTargetRing) return true;
        if (el2.getAttribute?.("data-kp-focus-ring") === "1") return true;
        if (el2.getAttribute?.("data-kp-shadow-b-host") === "1") return true;
        const cls = el2.classList;
        if (cls && (cls.contains(CSS_CLASSES.FOCUS_RING_INTARGET || "kpv2-focus-ring-intarget") || cls.contains("keypilot-focus-element"))) {
          return true;
        }
      } catch {
      }
      return false;
    }
    /**
     * Read a non-zero computed border-radius from an element.
     * @param {Element|null|undefined} el
     * @returns {string|null}
     */
    _readNonZeroBorderRadius(el2) {
      try {
        if (!el2 || el2.nodeType !== 1) return null;
        if (this._isKeyPilotPaintNode(el2)) return null;
        const cs = window.getComputedStyle(el2);
        if (!cs) return null;
        let radius = String(cs.borderRadius || "").trim();
        if (!radius) {
          const tl = cs.borderTopLeftRadius || "0";
          const tr = cs.borderTopRightRadius || "0";
          const br = cs.borderBottomRightRadius || "0";
          const bl = cs.borderBottomLeftRadius || "0";
          radius = `${tl} ${tr} ${br} ${bl}`.trim();
        }
        if (!radius) return null;
        const tokens = radius.replace(/,/g, " ").split(/\s+/).filter(Boolean);
        if (tokens.length > 0 && tokens.every((t) => /^(0|0px|0%)$/i.test(t))) {
          return null;
        }
        return radius;
      } catch {
        return null;
      }
    }
    /**
     * Turn percentage corner radii into px using `el`'s box. Raw `50%` on a
     * circular play button must not be copied onto a tall video (ellipse).
     * @param {string} radius
     * @param {Element} el
     * @returns {string}
     */
    _percentRadiusToPx(radius, el2) {
      const raw = String(radius || "").trim();
      if (!raw || !/%/.test(raw) || !el2 || el2.nodeType !== 1) return raw;
      let w = 0;
      let h = 0;
      try {
        const r = el2.getBoundingClientRect();
        w = r && r.width ? r.width : 0;
        h = r && r.height ? r.height : 0;
      } catch {
        return raw;
      }
      if (!(w > 0) || !(h > 0)) return raw;
      const pctTo = (token, axis) => {
        const m = String(token).trim().match(/^(-?[\d.]+)%$/);
        if (!m) return token;
        const px = parseFloat(m[1]) / 100 * axis;
        if (!Number.isFinite(px)) return token;
        const rounded = Math.round(px * 100) / 100;
        return `${rounded}px`;
      };
      const convertList = (list, axis) => list.trim().split(/\s+/).filter(Boolean).map((t) => pctTo(t, axis)).join(" ");
      if (raw.includes("/")) {
        const [xs, ys] = raw.split("/");
        return `${convertList(xs, w)} / ${convertList(ys, h)}`;
      }
      const tokens = raw.split(/\s+/).filter(Boolean);
      const xStr = tokens.map((t) => pctTo(t, w)).join(" ");
      const yStr = tokens.map((t) => pctTo(t, h)).join(" ");
      return xStr === yStr ? xStr : `${xStr} / ${yStr}`;
    }
    /**
     * Non-zero radius from `el`, with % resolved against `el`'s own box.
     * @param {Element|null|undefined} el
     * @returns {string|null}
     */
    _radiusCssFromElement(el2) {
      const raw = this._readNonZeroBorderRadius(el2);
      if (!raw) return null;
      return this._percentRadiusToPx(raw, el2);
    }
    /**
     * Resolve a CSS border-radius that matches the visual shape of the activation target.
     * Prefer the element itself; if it is not rounded (common for <a> wrapping a card/image),
     * use a large rounded descendant that fills most of the host box.
     *
     * @param {Element|null|undefined} element
     * @returns {string|null} CSS border-radius value, or null to keep stylesheet default
     */
    _resolveElementBorderRadius(element) {
      if (!element || element.nodeType !== 1) return null;
      if (this._isKeyPilotPaintNode(element)) return null;
      const own = this._radiusCssFromElement(element);
      if (own) return own;
      try {
        let parentRect = null;
        let parentArea = 0;
        try {
          parentRect = element.getBoundingClientRect();
          parentArea = Math.max(0, (parentRect.width || 0) * (parentRect.height || 0));
        } catch {
          parentArea = 0;
        }
        const candidates = [];
        try {
          if (element.children && element.children.length) {
            for (const child of element.children) {
              if (!this._isKeyPilotPaintNode(child)) candidates.push(child);
            }
          }
        } catch {
        }
        try {
          const media = element.querySelectorAll?.("img, svg, video, picture");
          if (media && media.length) {
            for (const m of media) {
              if (!this._isKeyPilotPaintNode(m)) candidates.push(m);
            }
          }
        } catch {
        }
        let best = null;
        let bestArea = 0;
        for (const c of candidates) {
          const radius = this._radiusCssFromElement(c);
          if (!radius) continue;
          let area = 0;
          try {
            const cr = c.getBoundingClientRect();
            area = Math.max(0, (cr.width || 0) * (cr.height || 0));
          } catch {
            area = 0;
          }
          if (area >= bestArea) {
            bestArea = area;
            best = radius;
          }
        }
        if (best && (parentArea <= 0 || bestArea >= parentArea * 0.35)) {
          return best;
        }
        if (parentRect && parentRect.width >= 8 && parentRect.height >= 8) {
          let p = null;
          try {
            p = this._composedParent(element);
          } catch {
            p = element.parentElement;
          }
          let depth = 0;
          while (p && p.nodeType === 1 && depth++ < 6) {
            if (p === document.body || p === document.documentElement) break;
            if (this._isKeyPilotPaintNode(p)) {
              try {
                p = this._composedParent(p);
              } catch {
                p = p.parentElement;
              }
              continue;
            }
            let r = null;
            try {
              r = p.getBoundingClientRect();
            } catch {
              r = null;
            }
            if (!r || r.width < 8 || r.height < 8) {
              try {
                p = this._composedParent(p);
              } catch {
                p = p.parentElement;
              }
              continue;
            }
            if (r.width > parentRect.width * 1.12 + 12) break;
            if (r.height > parentRect.height * 1.2 + 16) break;
            const ar = this._radiusCssFromElement(p);
            if (ar) return ar;
            try {
              p = this._composedParent(p);
            } catch {
              p = p.parentElement;
            }
          }
        }
      } catch {
      }
      return null;
    }
    /**
     * Paint node for hover ring / F-click flash — same pipeline as updateFocusOverlay.
     * Image+text cards → stacked shell; else focus-styling resolve.
     *
     * Text fields stay on the control itself for hover outline size/color. The
     * taller Gmail-style pill host is only used by `_applyTextFocusElementStyling`
     * for the focused left-edge bar — not for hover (avoids label+field wrappers
     * in onboarding practice popover).
     * @param {Element|null|undefined} element
     * @returns {Element|null}
     */
    _resolveFocusPaintElement(element) {
      if (!element || element.nodeType !== 1) return null;
      let cardShell = null;
      try {
        cardShell = this._resolveMediaTextCardShell(element);
      } catch {
        cardShell = null;
      }
      try {
        if (cardShell) return cardShell;
        const styled = this._resolveElementForFocusStyling(element) || element;
        try {
          const frame = this._resolveAbsoluteClipFramePaintHost(styled);
          if (frame) return frame;
        } catch {
        }
        return styled;
      } catch {
        return element;
      }
    }
    /**
     * Normalize a DOMRect-like into a plain viewport box, or null if degenerate.
     * @param {{ left?: number, top?: number, width?: number, height?: number }|null|undefined} r
     * @returns {{ left: number, top: number, width: number, height: number }|null}
     */
    _asPositiveViewportRect(r) {
      if (!r) return null;
      const left = Number(r.left);
      const top = Number(r.top);
      const width = Number(r.width);
      const height = Number(r.height);
      if (!(width > 0) || !(height > 0)) return null;
      if (!Number.isFinite(left) || !Number.isFinite(top)) return null;
      return { left, top, width, height };
    }
    /**
     * True when hover focus is strategy A (CSS outline on the styled element).
     * Inline outlines follow line-box fragments — fixed union ghosts do not.
     * @returns {boolean}
     */
    _isDomOutlineFocusPaint() {
      return !!(this._useDomHoverFocusColors && !this._focusPaintUsesFixedOverlay && !this._focusPaintUsesInTargetRing && this._currentStyledElement && this._currentStyledElement.nodeType === 1);
    }
    /**
     * Flash strategy A's live outline in place (correct for inline / multi-line fragments).
     * @param {Element} styledEl
     * @param {number} cleanupMs
     */
    _flashDomOutlineColors(styledEl, cleanupMs) {
      if (!styledEl || styledEl.nodeType !== 1 || !styledEl.isConnected) return false;
      try {
        const prevTimer = styledEl._kpOutlineFlashTimer;
        if (prevTimer) {
          clearTimeout(prevTimer);
          styledEl._kpOutlineFlashTimer = 0;
        }
      } catch {
      }
      const prevColor = styledEl.style.getPropertyValue("--keypilot-focus-ring-color");
      const prevShadow = styledEl.style.getPropertyValue("--keypilot-focus-box-shadow");
      let prevInlineOutline = "";
      let prevInlineOffset = "";
      let prevInlineShadow = "";
      const hadInline = styledEl.getAttribute?.("data-kp-focus-inline") === "1";
      if (hadInline) {
        try {
          prevInlineOutline = styledEl.style.getPropertyValue("outline");
          prevInlineOffset = styledEl.style.getPropertyValue("outline-offset");
          prevInlineShadow = styledEl.style.getPropertyValue("box-shadow");
        } catch {
        }
      }
      const ringWidth = styledEl.style.getPropertyValue("--keypilot-focus-ring-width") || "3px";
      const ringOffset = styledEl.style.getPropertyValue("--keypilot-focus-outline-offset") || "2px";
      const flashShadow = `0 0 0 2px ${COLORS.FLASH_GREEN_SHADOW}, 0 0 16px 3px ${COLORS.FLASH_GREEN_GLOW}`;
      try {
        styledEl.style.setProperty("--keypilot-focus-ring-color", COLORS.FLASH_GREEN);
        styledEl.style.setProperty("--keypilot-focus-box-shadow", flashShadow);
        if (hadInline) {
          styledEl.style.setProperty(
            "outline",
            `${ringWidth} solid ${COLORS.FLASH_GREEN}`,
            "important"
          );
          styledEl.style.setProperty("outline-offset", ringOffset, "important");
          styledEl.style.setProperty("box-shadow", flashShadow, "important");
        }
      } catch {
        return false;
      }
      const ms = Math.max(200, Number(cleanupMs) || 500);
      try {
        styledEl._kpOutlineFlashTimer = setTimeout(() => {
          styledEl._kpOutlineFlashTimer = 0;
          try {
            if (this._currentStyledElement !== styledEl) return;
            if (prevColor) {
              styledEl.style.setProperty("--keypilot-focus-ring-color", prevColor);
            } else {
              styledEl.style.removeProperty("--keypilot-focus-ring-color");
            }
            if (prevShadow) {
              styledEl.style.setProperty("--keypilot-focus-box-shadow", prevShadow);
            } else {
              styledEl.style.removeProperty("--keypilot-focus-box-shadow");
            }
            if (hadInline) {
              if (prevInlineOutline) {
                styledEl.style.setProperty("outline", prevInlineOutline, "important");
              }
              if (prevInlineOffset) {
                styledEl.style.setProperty("outline-offset", prevInlineOffset, "important");
              }
              if (prevInlineShadow) {
                styledEl.style.setProperty("box-shadow", prevInlineShadow, "important");
              }
            }
          } catch {
          }
        }, ms);
      } catch {
      }
      return true;
    }
    /**
     * Live viewport box of the painted blue hover ring (strategy B ring or C
     * body-fixed overlay). Null when hover is strategy A (DOM outline only).
     * @returns {{ left: number, top: number, width: number, height: number }|null}
     */
    _resolveLiveBlueHoverPaintRect() {
      try {
        if (this._focusPaintUsesFixedOverlay && this.focusOverlay && this.focusOverlay.style.display !== "none") {
          const r = this._asPositiveViewportRect(this.focusOverlay.getBoundingClientRect());
          if (r) return r;
        }
      } catch {
      }
      try {
        if (this._focusPaintUsesInTargetRing && this._inTargetRing && this._inTargetRing.isConnected) {
          let shown = true;
          try {
            const cs = window.getComputedStyle(this._inTargetRing);
            shown = !!(cs && cs.display !== "none" && cs.visibility !== "hidden");
          } catch {
          }
          if (shown) {
            const r = this._asPositiveViewportRect(this._inTargetRing.getBoundingClientRect());
            if (r) return r;
          }
        }
      } catch {
      }
      return null;
    }
    /**
     * Expand a content box by strategy-A outline-offset so a fixed green flash
     * sits where the blue CSS outline sits (outside the border box).
     * @param {{ left: number, top: number, width: number, height: number }} rect
     * @param {Element|null|undefined} paintEl
     * @returns {{ left: number, top: number, width: number, height: number }}
     */
    _expandRectForStrategyAOutline(rect, paintEl) {
      if (!rect) return rect;
      let offsetPx = 0;
      try {
        if (paintEl && paintEl.nodeType === 1) {
          const fromVar = paintEl.style?.getPropertyValue?.("--keypilot-focus-outline-offset");
          const raw = fromVar && String(fromVar).trim() || (window.getComputedStyle(paintEl).outlineOffset || "");
          const n = parseFloat(raw);
          if (Number.isFinite(n)) offsetPx = n;
        }
      } catch {
      }
      if (!(offsetPx > 0.25)) return rect;
      return {
        left: rect.left - offsetPx,
        top: rect.top - offsetPx,
        width: rect.width + 2 * offsetPx,
        height: rect.height + 2 * offsetPx
      };
    }
    /**
     * Viewport boxes for F-click ghosts. Prefer the live blue paint box (B ring /
     * C overlay). For strategy A, one clipped rect per getClientRects() line box
     * (matches outline fragments), expanded by outline-offset.
     *
     * @param {Element|null|undefined} activationTarget
     * @returns {{ paintEl: Element|null, rects: Array<{ left: number, top: number, width: number, height: number }> }}
     */
    _resolveClickEffectRects(activationTarget = null) {
      const source = activationTarget && activationTarget.nodeType === 1 ? activationTarget : this._currentStyledElement || this._lastFocusElement;
      let paintEl = null;
      try {
        if (this._useDomHoverFocusColors && !this._focusPaintUsesFixedOverlay && !this._focusPaintUsesInTargetRing && this._currentStyledElement && this._currentStyledElement.nodeType === 1 && this._currentStyledElement.isConnected) {
          paintEl = this._currentStyledElement;
        }
      } catch {
      }
      if (!paintEl) {
        paintEl = this._resolveFocusPaintElement(source) || source || null;
      }
      try {
        if (this._focusPaintUsesInTargetRing && this._inTargetHost && this._inTargetHost.nodeType === 1 && this._inTargetHost.isConnected && source && (this._lastFocusElement === source || this._inTargetHost === source || this._inTargetHost === paintEl || typeof this._inTargetHost.contains === "function" && this._inTargetHost.contains(source) || typeof source.contains === "function" && source.contains(this._inTargetHost))) {
          paintEl = this._inTargetHost;
        }
      } catch {
      }
      try {
        const live = this._resolveLiveBlueHoverPaintRect();
        if (live) {
          return { paintEl, rects: [live] };
        }
      } catch {
      }
      try {
        if (paintEl && paintEl.nodeType === 1) {
          const replaced = this._findDominantReplacedPaintChild(paintEl);
          if (replaced) paintEl = replaced;
        }
      } catch {
      }
      try {
        if (paintEl && paintEl.nodeType === 1 && paintEl.isConnected) {
          const isReplaced = this._isReplacedOrVoidElement(paintEl) || this._isMediaLikeCoverElement(paintEl);
          let clientRects = null;
          try {
            clientRects = paintEl.getClientRects();
          } catch {
            clientRects = null;
          }
          const fragmented = !isReplaced && this._isFragmentedInlineFocusTarget(paintEl);
          if (!isReplaced && clientRects && clientRects.length >= 1 && (fragmented || clientRects.length >= 2)) {
            const out = [];
            for (let i = 0; i < clientRects.length; i++) {
              const expanded = this._expandRectForStrategyAOutline(
                this._asPositiveViewportRect(clientRects[i]),
                paintEl
              );
              const clipped = this._clipViewportRectToVisible(paintEl, expanded);
              if (clipped) out.push(clipped);
            }
            if (out.length) return { paintEl, rects: out };
          }
        }
      } catch {
      }
      const box = this._resolveClickEffectBox(activationTarget);
      if (box && box.rect) {
        const expanded = this._expandRectForStrategyAOutline(box.rect, box.paintEl || paintEl);
        const clipped = this._clipViewportRectToVisible(box.paintEl || paintEl, expanded) || expanded;
        return { paintEl: box.paintEl || paintEl, rects: [clipped] };
      }
      return { paintEl, rects: [] };
    }
    /**
     * Viewport box for F-click flash / pulse: body-fixed imitation of the blue
     * hover paint box (including fragmented / bare-inline union via getBestRect).
     *
     * @param {Element|null|undefined} activationTarget
     * @returns {{ paintEl: Element|null, rect: { left: number, top: number, width: number, height: number } }|null}
     */
    _resolveClickEffectBox(activationTarget = null) {
      const source = activationTarget && activationTarget.nodeType === 1 ? activationTarget : this._currentStyledElement || this._lastFocusElement;
      let paintEl = this._resolveFocusPaintElement(source) || source || null;
      try {
        if (this._focusPaintUsesInTargetRing && this._inTargetHost && this._inTargetHost.nodeType === 1 && this._inTargetHost.isConnected && source && (this._lastFocusElement === source || this._inTargetHost === source || this._inTargetHost === paintEl || typeof this._inTargetHost.contains === "function" && this._inTargetHost.contains(source) || typeof source.contains === "function" && source.contains(this._inTargetHost))) {
          paintEl = this._inTargetHost;
        }
      } catch {
      }
      try {
        const live = this._resolveLiveBlueHoverPaintRect();
        if (live) return { paintEl, rect: live };
      } catch {
      }
      try {
        if (paintEl && paintEl.nodeType === 1 && paintEl.isConnected) {
          const raw = this._expandRectForStrategyAOutline(
            this._asPositiveViewportRect(this.getBestRect(paintEl)),
            paintEl
          );
          const r = this._clipViewportRectToVisible(paintEl, raw);
          if (r) return { paintEl, rect: r };
        }
      } catch {
      }
      try {
        if (this.focusOverlay && this.focusOverlay.style.display !== "none") {
          const r = this._asPositiveViewportRect(this.focusOverlay.getBoundingClientRect());
          if (r) return { paintEl, rect: r };
        }
      } catch {
      }
      try {
        if (this.cssCustomPropsOverlay && this.cssCustomPropsOverlay.style.display !== "none") {
          const r = this._asPositiveViewportRect(this.cssCustomPropsOverlay.getBoundingClientRect());
          if (r) return { paintEl, rect: r };
        }
      } catch {
      }
      const lastRaw = this._asPositiveViewportRect(this._lastFocusRect);
      const last = paintEl ? this._clipViewportRectToVisible(paintEl, lastRaw) : lastRaw;
      if (last) return { paintEl, rect: last };
      return null;
    }
    /**
     * Resolve the viewport rect of the current focus outline for activation feedback.
     * @returns {{ left: number, top: number, width: number, height: number }|null}
     */
    _getFocusPulseRect() {
      const box = this._resolveClickEffectBox(null);
      return box ? box.rect : null;
    }
    /**
     * Install one-shot page lifecycle hooks that wipe fixed-position click effects
     * during navigation / hide (SPA soft-nav, full load, bfcache, tab background).
     */
    _installEphemeralEffectLifecycle() {
      if (this._ephemeralEffectLifecycleInstalled) return;
      this._ephemeralEffectLifecycleInstalled = true;
      const clear = () => {
        try {
          this.clearEphemeralEffects();
        } catch {
        }
      };
      const bindings = [];
      const bind = (target, type2, handler, options) => {
        try {
          target.addEventListener(type2, handler, options);
          bindings.push([target, type2, handler, options]);
        } catch {
        }
      };
      bind(window, "pagehide", clear, true);
      bind(window, "beforeunload", clear, true);
      bind(window, "popstate", clear, true);
      bind(window, "hashchange", clear, true);
      bind(document, "visibilitychange", () => {
        if (document.visibilityState === "hidden") clear();
      }, true);
      bind(window, "pageshow", (e) => {
        if (e && e.persisted) clear();
      }, true);
      try {
        this._ephemeralLastHref = String(location.href || "");
        this._ephemeralOnUrlMaybeChanged = () => {
          const next = String(location.href || "");
          if (next !== this._ephemeralLastHref) {
            this._ephemeralLastHref = next;
            clear();
          }
        };
      } catch {
      }
      this._ephemeralEffectLifecycleDispose = () => {
        try {
          this._ephemeralLastHref = null;
          this._ephemeralOnUrlMaybeChanged = null;
        } catch {
        }
        for (const [target, type2, handler, options] of bindings) {
          try {
            target.removeEventListener(type2, handler, options);
          } catch {
          }
        }
        bindings.length = 0;
        this._ephemeralEffectLifecycleInstalled = false;
        this._ephemeralEffectLifecycleDispose = null;
      };
    }
    /**
     * Remove every temporary click / image-copy effect overlay immediately.
     */
    clearEphemeralEffects() {
      const entries = Array.from(this._activeEphemeralEffects || []);
      for (const entry of entries) {
        try {
          entry.teardown?.();
        } catch {
        }
      }
      this._activeEphemeralEffects?.clear?.();
    }
    /**
     * Hits to ignore while probing occlusion (C overlay, B ring, F-click ghosts).
     * @param {Element} hit
     * @param {Element|null|undefined} extraIgnore
     * @returns {boolean}
     */
    _isOcclusionProbeIgnoreHit(hit, extraIgnore = null) {
      if (!hit || hit.nodeType !== 1) return true;
      if (extraIgnore && hit === extraIgnore) return true;
      if (hit === this._inTargetRing || hit === this.focusOverlay) return true;
      try {
        if (hit.hasAttribute && hit.hasAttribute("data-kp-ephemeral-effect")) return true;
        if (hit.hasAttribute && hit.hasAttribute("data-kp-focus-ring")) return true;
        const ringClass = CSS_CLASSES.FOCUS_RING_INTARGET || "kpv2-focus-ring-intarget";
        if (hit.classList && (hit.classList.contains(ringClass) || hit.classList.contains(CSS_CLASSES.FOCUS_OVERLAY))) {
          return true;
        }
      } catch {
      }
      return false;
    }
    /**
     * True when the top meaningful hit at (x,y) is sourceEl (or inside / wrapping it).
     * @param {Element} sourceEl
     * @param {number} x
     * @param {number} y
     * @returns {boolean}
     */
    _isPointOnSource(sourceEl, x, y) {
      if (!sourceEl || !Number.isFinite(x) || !Number.isFinite(y)) return true;
      let stack = null;
      try {
        if (typeof document.elementsFromPoint === "function") {
          stack = document.elementsFromPoint(x, y);
        }
      } catch {
        stack = null;
      }
      if (!stack || !stack.length) return true;
      for (let i = 0; i < stack.length; i++) {
        const hit = stack[i];
        if (this._isOcclusionProbeIgnoreHit(hit)) continue;
        return !!(hit === sourceEl || containsComposed(sourceEl, hit) || containsComposed(hit, sourceEl));
      }
      return true;
    }
    /**
     * Shrink a strategy-C viewport rect so covered edges (sticky header, sibling
     * layer) are pulled in. Four-corner samples; an edge is inset when both of
     * its corners are covered, or one corner plus that edge's midpoint.
     * @param {Element} sourceEl
     * @param {{ left: number, top: number, width: number, height: number }} rect
     * @returns {{ left: number, top: number, width: number, height: number }|null}
     */
    _insetCRectForOcclusion(sourceEl, rect) {
      if (!sourceEl || sourceEl.nodeType !== 1 || !rect) return rect || null;
      let box = this._asPositiveViewportRect(rect);
      if (!box) return null;
      if (typeof document.elementsFromPoint !== "function") return box;
      const MIN = 8;
      const pad = () => Math.max(1, Math.min(6, box.width / 4, box.height / 4));
      const onSrc = (x, y) => this._isPointOnSource(sourceEl, x, y);
      const corners = () => {
        const p = pad();
        return {
          tl: !onSrc(box.left + p, box.top + p),
          tr: !onSrc(box.left + box.width - p, box.top + p),
          bl: !onSrc(box.left + p, box.top + box.height - p),
          br: !onSrc(box.left + box.width - p, box.top + box.height - p)
        };
      };
      const edgeCovered = (side, c) => {
        const p = pad();
        if (side === "top") {
          if (c.tl && c.tr) return true;
          if (c.tl || c.tr) return !onSrc(box.left + box.width / 2, box.top + p);
        } else if (side === "bottom") {
          if (c.bl && c.br) return true;
          if (c.bl || c.br) return !onSrc(box.left + box.width / 2, box.top + box.height - p);
        } else if (side === "left") {
          if (c.tl && c.bl) return true;
          if (c.tl || c.bl) return !onSrc(box.left + p, box.top + box.height / 2);
        } else if (side === "right") {
          if (c.tr && c.br) return true;
          if (c.tr || c.br) return !onSrc(box.left + box.width - p, box.top + box.height / 2);
        }
        return false;
      };
      const shrinkEdge = (side) => {
        const p = pad();
        if (side === "top" || side === "bottom") {
          let lo = 0;
          let hi = Math.max(0, box.height - MIN);
          for (let i = 0; i < 8 && hi - lo > 1; i++) {
            const mid = (lo + hi) / 2;
            const y = side === "top" ? box.top + mid + p : box.top + box.height - mid - p;
            const clear = onSrc(box.left + p, y) && onSrc(box.left + box.width - p, y);
            if (clear) hi = mid;
            else lo = mid;
          }
          const cut = hi;
          if (!(cut > 0.5)) return;
          if (side === "top") {
            box = { left: box.left, top: box.top + cut, width: box.width, height: box.height - cut };
          } else {
            box = { left: box.left, top: box.top, width: box.width, height: box.height - cut };
          }
        } else {
          let lo = 0;
          let hi = Math.max(0, box.width - MIN);
          for (let i = 0; i < 8 && hi - lo > 1; i++) {
            const mid = (lo + hi) / 2;
            const x = side === "left" ? box.left + mid + p : box.left + box.width - mid - p;
            const clear = onSrc(x, box.top + p) && onSrc(x, box.top + box.height - p);
            if (clear) hi = mid;
            else lo = mid;
          }
          const cut = hi;
          if (!(cut > 0.5)) return;
          if (side === "left") {
            box = { left: box.left + cut, top: box.top, width: box.width - cut, height: box.height };
          } else {
            box = { left: box.left, top: box.top, width: box.width - cut, height: box.height };
          }
        }
      };
      let guard = 0;
      while (guard++ < 4) {
        if (box.width < MIN || box.height < MIN) return null;
        const c = corners();
        const sides = [];
        if (edgeCovered("top", c)) sides.push("top");
        if (edgeCovered("bottom", c)) sides.push("bottom");
        if (edgeCovered("left", c)) sides.push("left");
        if (edgeCovered("right", c)) sides.push("right");
        if (!sides.length) break;
        if (sides.length === 4) return null;
        for (const side of sides) shrinkEdge(side);
      }
      if (!box || box.width < MIN || box.height < MIN) return null;
      return box;
    }
    /**
     * True when a foreign stacking layer now paints over the source center
     * (lightbox / modal portal). IntersectionObserver cannot detect z-index
     * occlusion — this is the real "covered away" check for fixed ghosts.
     *
     * @param {Element} sourceEl
     * @param {Element|null|undefined} pulse - ephemeral effect to ignore in the stack
     * @returns {boolean}
     */
    _isEphemeralSourceOccluded(sourceEl, pulse = null) {
      if (!sourceEl || sourceEl.nodeType !== 1 || !sourceEl.isConnected) return true;
      let rect = null;
      try {
        rect = sourceEl.getBoundingClientRect();
      } catch {
        return true;
      }
      if (!rect || !(rect.width > 0) || !(rect.height > 0)) return true;
      const x = rect.left + rect.width / 2;
      const y = rect.top + rect.height / 2;
      const vw = typeof window !== "undefined" ? window.innerWidth : 0;
      const vh = typeof window !== "undefined" ? window.innerHeight : 0;
      if (!(x >= 0 && y >= 0 && x <= vw && y <= vh)) return false;
      let stack = null;
      try {
        if (typeof document.elementsFromPoint === "function") {
          stack = document.elementsFromPoint(x, y);
        }
      } catch {
        stack = null;
      }
      if (!stack || !stack.length) return false;
      for (let i = 0; i < stack.length; i++) {
        const hit = stack[i];
        if (this._isOcclusionProbeIgnoreHit(hit, pulse)) continue;
        if (hit === sourceEl || containsComposed(sourceEl, hit) || containsComposed(hit, sourceEl)) {
          return false;
        }
        return true;
      }
      return false;
    }
    /**
     * Track a temporary click effect so it is removed when the source target is
     * gone/hidden, occluded (fixed path), or the page navigates — not only when
     * the CSS animation ends.
     *
     * @param {Element} pulse
     * @param {Element|null|undefined} sourceEl
     * @param {{ left: number, top: number, width: number, height: number }|null|undefined} originRect
     * @param {number} cleanupMs
     * @param {{ checkOcclusion?: boolean, minVisibleMs?: number }} [opts]
     *   minVisibleMs: keep the ghost through source teardown/occlusion until this
     *   many ms have elapsed (F-click flash must survive the click's SPA unmount).
     */
    _trackEphemeralEffect(pulse, sourceEl, originRect, cleanupMs, opts = {}) {
      if (!pulse) return;
      this._installEphemeralEffectLifecycle();
      const checkOcclusion = opts?.checkOcclusion === true;
      const minVisibleMs = Number.isFinite(opts?.minVisibleMs) ? Math.max(0, opts.minVisibleMs) : 0;
      const visibleSince = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
      const earlyTeardownAllowed = () => {
        if (minVisibleMs <= 0) return true;
        const now = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
        return now - visibleSince >= minVisibleMs;
      };
      const entry = {
        pulse,
        sourceEl: sourceEl && sourceEl.nodeType === 1 ? sourceEl : null,
        originRect: originRect ? {
          left: originRect.left,
          top: originRect.top,
          width: originRect.width,
          height: originRect.height
        } : null,
        checkOcclusion,
        rafId: 0,
        timeoutId: 0,
        io: (
          /** @type {IntersectionObserver|null} */
          null
        ),
        teardown: () => {
        }
      };
      let tornDown = false;
      const teardown = () => {
        if (tornDown) return;
        tornDown = true;
        this._activeEphemeralEffects.delete(entry);
        if (entry.rafId) {
          try {
            cancelAnimationFrame(entry.rafId);
          } catch {
          }
          entry.rafId = 0;
        }
        if (entry.timeoutId) {
          try {
            clearTimeout(entry.timeoutId);
          } catch {
          }
          entry.timeoutId = 0;
        }
        if (entry.io) {
          try {
            entry.io.disconnect();
          } catch {
          }
          entry.io = null;
        }
        try {
          if (pulse.isConnected) pulse.remove();
        } catch {
        }
      };
      entry.teardown = teardown;
      this._activeEphemeralEffects.add(entry);
      if (entry.sourceEl) {
        try {
          entry.io = new IntersectionObserver(
            (records) => {
              if (!earlyTeardownAllowed()) return;
              for (const r of records) {
                if (!r.isIntersecting || r.intersectionRatio <= 0) {
                  teardown();
                  return;
                }
              }
            },
            { root: null, threshold: 0 }
          );
          entry.io.observe(entry.sourceEl);
        } catch {
          entry.io = null;
        }
      }
      const origin = entry.originRect;
      const tick = () => {
        if (tornDown) return;
        try {
          const onUrl = this._ephemeralOnUrlMaybeChanged;
          if (typeof onUrl === "function") onUrl();
          if (tornDown) return;
        } catch {
        }
        const src = entry.sourceEl;
        if (src && earlyTeardownAllowed()) {
          if (!src.isConnected) {
            teardown();
            return;
          }
          try {
            const r = src.getBoundingClientRect();
            if (!(r.width > 0) || !(r.height > 0)) {
              teardown();
              return;
            }
            if (origin) {
              const dx = Math.abs(r.left - origin.left);
              const dy = Math.abs(r.top - origin.top);
              const dw = Math.abs(r.width - origin.width);
              const dh = Math.abs(r.height - origin.height);
              if (dx > 48 || dy > 48 || dw > 64 || dh > 64) {
                teardown();
                return;
              }
            }
            try {
              const cs = window.getComputedStyle(src);
              if (cs) {
                const op = parseFloat(cs.opacity);
                if (cs.display === "none" || cs.visibility === "hidden" || Number.isFinite(op) && op < 0.05) {
                  teardown();
                  return;
                }
              }
            } catch {
            }
            if (checkOcclusion && this._isEphemeralSourceOccluded(src, pulse)) {
              teardown();
              return;
            }
          } catch {
            teardown();
            return;
          }
        }
        if (!pulse.isConnected) {
          teardown();
          return;
        }
        entry.rafId = requestAnimationFrame(tick);
      };
      entry.rafId = requestAnimationFrame(tick);
      try {
        pulse.addEventListener("animationend", teardown, { once: true });
      } catch {
      }
      const ms = Number.isFinite(cleanupMs) ? Math.max(200, cleanupMs) : 1e3;
      entry.timeoutId = setTimeout(teardown, ms);
    }
    /**
     * Border-radius for F-click / image-copy ghosts. Prefer the live blue hover
     * paint (B ring or C overlay), then visual resolve on the source elements.
     * Always returns a CSS value (defaults to `0`) so flash corners match the box.
     *
     * @param {...(Element|null|undefined)} sources
     * @returns {string}
     */
    _resolveClickEffectBorderRadius(...sources) {
      try {
        if (this._focusPaintUsesInTargetRing && this._inTargetRing && this._inTargetRing.isConnected) {
          const raw = this._inTargetRing.style?.getPropertyValue?.("border-radius") || window.getComputedStyle(this._inTargetRing).borderRadius || "";
          const trimmed = String(raw).trim();
          if (trimmed) return trimmed;
        }
      } catch {
      }
      try {
        if (this._focusPaintUsesFixedOverlay && this.focusOverlay && this.focusOverlay.style.display !== "none") {
          const raw = this.focusOverlay.style?.borderRadius || window.getComputedStyle(this.focusOverlay).borderRadius || "";
          const trimmed = String(raw).trim();
          if (trimmed) return trimmed;
        }
      } catch {
      }
      try {
        const styled = this._currentStyledElement;
        if (styled && styled.nodeType === 1 && styled.isConnected && !this._focusPaintUsesInTargetRing && !this._focusPaintUsesFixedOverlay) {
          const raw = styled.style?.getPropertyValue?.("border-radius") || window.getComputedStyle(styled).borderRadius || "";
          const trimmed = String(raw).trim();
          if (trimmed && !/^(0|0px)(\s+(0|0px))*$/i.test(trimmed.replace(/\s*\/\s*/g, " "))) {
            return trimmed;
          }
        }
      } catch {
      }
      for (const src of sources) {
        if (!src || src.nodeType !== 1) continue;
        try {
          const r = this._resolveElementBorderRadius(src);
          if (r) return r;
        } catch {
        }
      }
      return "0";
    }
    /**
     * Apply border-radius onto an ephemeral flash node (div or dash SVG).
     * @param {Element} pulse
     * @param {string|null|undefined} borderRadius
     * @param {{ width?: number, height?: number }|null} [box] - for SVG rx/ry refresh
     */
    _applyEphemeralBorderRadius(pulse, borderRadius, box = null) {
      const radius = borderRadius && String(borderRadius).trim() || "0";
      if (!pulse) return;
      try {
        pulse.style.setProperty("border-radius", radius, "important");
      } catch {
      }
      try {
        if (typeof SVGElement !== "undefined" && pulse instanceof SVGElement) {
          const shape = pulse.querySelector?.("rect");
          if (shape) {
            const w = Math.max(1, Number(box?.width) || parseFloat(pulse.getAttribute("width")) || 1);
            const h = Math.max(1, Number(box?.height) || parseFloat(pulse.getAttribute("height")) || 1);
            const stroke = 3;
            const rw = Math.max(1, w - stroke);
            const rh = Math.max(1, h - stroke);
            const rx = Math.min(this._borderRadiusToSvgRx(radius, w, h), rw / 2, rh / 2);
            shape.setAttribute("rx", String(rx));
            shape.setAttribute("ry", String(rx));
          }
        }
      } catch {
      }
    }
    /**
     * Approximate border-radius in CSS px for SVG rx/ry from a CSS border-radius string.
     * @param {string|null|undefined} borderRadius
     * @param {number} width
     * @param {number} height
     * @returns {number}
     */
    _borderRadiusToSvgRx(borderRadius, width, height) {
      if (!borderRadius) return 0;
      const first = String(borderRadius).trim().split(/\s+/)[0] || "";
      if (!first) return 0;
      if (/^(0|0px|0%)$/i.test(first)) return 0;
      if (first.endsWith("%")) {
        const p = parseFloat(first);
        if (!Number.isFinite(p)) return 0;
        return Math.max(0, Math.min(width, height) * (p / 100));
      }
      const n = parseFloat(first);
      return Number.isFinite(n) ? Math.max(0, n) : 0;
    }
    /**
     * Map clickEffect id → CSS class + safety cleanup ms.
     * @param {string} clickEffect
     * @returns {{ className: string, cleanupMs: number } | null}
     */
    _clickEffectPresentation(clickEffect) {
      switch (clickEffect) {
        case "flash":
          return { className: CSS_CLASSES.FOCUS_FLASH, cleanupMs: 500 };
        case "scale":
          return { className: CSS_CLASSES.FOCUS_PULSE, cleanupMs: 800 };
        case "marquee":
          return { className: CSS_CLASSES.FOCUS_MARQUEE, cleanupMs: 1200 };
        case "dash":
          return { className: CSS_CLASSES.FOCUS_DASH, cleanupMs: 1100 };
        default:
          return null;
      }
    }
    /**
     * Build the dash-chase SVG overlay (dashed stroke travels the perimeter).
     * @param {{ left: number, top: number, width: number, height: number }} rect
     * @param {string|null|undefined} borderRadius
     * @returns {SVGSVGElement}
     */
    _createDashChasePulse(rect, borderRadius) {
      const w = Math.max(1, rect.width);
      const h = Math.max(1, rect.height);
      const stroke = 3;
      const inset = stroke / 2;
      const rw = Math.max(1, w - stroke);
      const rh = Math.max(1, h - stroke);
      const rx = Math.min(this._borderRadiusToSvgRx(borderRadius, w, h), rw / 2, rh / 2);
      const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
      svg.setAttribute("class", CSS_CLASSES.FOCUS_DASH);
      svg.setAttribute("aria-hidden", "true");
      svg.setAttribute("data-kp-ephemeral-effect", "dash");
      svg.setAttribute("width", String(w));
      svg.setAttribute("height", String(h));
      svg.style.left = `${rect.left}px`;
      svg.style.top = `${rect.top}px`;
      svg.style.width = `${w}px`;
      svg.style.height = `${h}px`;
      const shape = document.createElementNS("http://www.w3.org/2000/svg", "rect");
      shape.setAttribute("x", String(inset));
      shape.setAttribute("y", String(inset));
      shape.setAttribute("width", String(rw));
      shape.setAttribute("height", String(rh));
      shape.setAttribute("rx", String(rx));
      shape.setAttribute("ry", String(rx));
      shape.setAttribute("fill", "none");
      shape.setAttribute("stroke", COLORS.FLASH_GREEN);
      shape.setAttribute("stroke-width", String(stroke));
      shape.setAttribute("stroke-linecap", "round");
      shape.setAttribute("vector-effect", "non-scaling-stroke");
      const straight = 2 * (Math.max(0, rw - 2 * rx) + Math.max(0, rh - 2 * rx));
      const corners = 2 * Math.PI * rx;
      const peri = Math.max(24, straight + corners);
      const dash = Math.max(10, Math.min(36, peri * 0.09));
      const gap = Math.max(6, Math.min(22, peri * 0.055));
      shape.setAttribute("stroke-dasharray", `${dash} ${gap}`);
      shape.style.setProperty("--kp-dash-peri", String(peri));
      shape.classList.add(`${CSS_CLASSES.FOCUS_DASH}-stroke`);
      svg.appendChild(shape);
      return svg;
    }
    /**
     * Local z-index for an ephemeral in-target flash — sit above the live B ring.
     * @param {Element} host
     * @returns {number}
     */
    _inTargetEphemeralZIndex(host) {
      let z = this._maxLocalZIndex(host) + 1;
      try {
        if (this._inTargetRing && this._inTargetRing.parentNode === host) {
          const raw = this._inTargetRing.style?.getPropertyValue?.("z-index") || window.getComputedStyle(this._inTargetRing).zIndex;
          const n = parseInt(String(raw), 10);
          if (Number.isFinite(n)) z = Math.max(z, n + 1);
        }
      } catch {
      }
      try {
        if (this._isInShadowTree(host)) z = Math.max(z, 2147483e3);
      } catch {
      }
      return z;
    }
    /**
     * Restyle a fixed-position click-effect node as an absolute inset:0 sibling
     * inside the strategy-B host (co-located; naturally under lightboxes).
     * @param {HTMLElement|SVGSVGElement} pulse
     * @param {Element} host
     * @param {string|null|undefined} borderRadius
     * @param {string} clickEffect
     * @param {{ width: number, height: number }|null} [box]
     */
    _applyInTargetEphemeralLayout(pulse, host, borderRadius, clickEffect, box = null) {
      const z = this._inTargetEphemeralZIndex(host);
      const isSvg = typeof SVGElement !== "undefined" && pulse instanceof SVGElement;
      const lockTransform = clickEffect !== "scale" && clickEffect !== "marquee";
      pulse.style.setProperty("position", "absolute", "important");
      pulse.style.setProperty("inset", "0", "important");
      pulse.style.setProperty("left", "0", "important");
      pulse.style.setProperty("top", "0", "important");
      pulse.style.setProperty("right", "0", "important");
      pulse.style.setProperty("bottom", "0", "important");
      pulse.style.setProperty("margin", "0", "important");
      pulse.style.setProperty("pointer-events", "none", "important");
      pulse.style.setProperty("z-index", String(z), "important");
      pulse.style.setProperty("box-sizing", "border-box", "important");
      if (isSvg && box) {
        pulse.style.setProperty("width", `${Math.max(1, box.width)}px`, "important");
        pulse.style.setProperty("height", `${Math.max(1, box.height)}px`, "important");
        try {
          pulse.setAttribute("width", String(Math.max(1, box.width)));
          pulse.setAttribute("height", String(Math.max(1, box.height)));
        } catch {
        }
      } else {
        pulse.style.setProperty("width", "auto", "important");
        pulse.style.setProperty("height", "auto", "important");
      }
      this._applyEphemeralBorderRadius(pulse, borderRadius, box);
      if (lockTransform) {
        pulse.style.setProperty("transform", "none", "important");
        pulse.style.setProperty("transform-origin", "center", "important");
        pulse.style.setProperty("scale", "none", "important");
        pulse.style.setProperty("translate", "none", "important");
        pulse.style.setProperty("rotate", "none", "important");
      }
    }
    /**
     * When hover paint is already strategy B, mount the green flash as an absolute
     * sibling inside the same host so lightbox portals naturally cover it.
     * @param {string} clickEffect
     * @param {{ className: string, cleanupMs: number }} presentation
     * @param {Element|null|undefined} activationEl
     * @returns {boolean}
     */
    _tryFlashInTargetEffect(clickEffect, presentation, activationEl) {
      if (!this._focusPaintUsesInTargetRing) return false;
      const host = this._inTargetHost;
      if (!host || host.nodeType !== 1 || !host.isConnected) return false;
      try {
        if (activationEl && activationEl.nodeType === 1 && host !== activationEl && !(typeof host.contains === "function" && host.contains(activationEl)) && !(typeof activationEl.contains === "function" && activationEl.contains(host)) && !containsComposed(host, activationEl) && !containsComposed(activationEl, host)) {
          return false;
        }
      } catch {
      }
      let borderRadius = "0";
      try {
        borderRadius = this._resolveClickEffectBorderRadius(host, activationEl);
      } catch {
        borderRadius = "0";
      }
      let hostRect = null;
      try {
        hostRect = this._asPositiveViewportRect(host.getBoundingClientRect());
      } catch {
        hostRect = null;
      }
      if (!hostRect) return false;
      const w = Math.max(1, host.clientWidth || hostRect.width);
      const h = Math.max(1, host.clientHeight || hostRect.height);
      let pulse;
      try {
        if (clickEffect === "dash") {
          pulse = this._createDashChasePulse({ left: 0, top: 0, width: w, height: h }, borderRadius);
          this._applyInTargetEphemeralLayout(pulse, host, borderRadius, clickEffect, { width: w, height: h });
        } else {
          pulse = document.createElement("div");
          pulse.className = presentation.className;
          pulse.setAttribute("aria-hidden", "true");
          pulse.setAttribute("data-kp-ephemeral-effect", clickEffect);
          this._applyInTargetEphemeralLayout(pulse, host, borderRadius, clickEffect);
        }
        host.appendChild(pulse);
        this._trackEphemeralEffect(
          pulse,
          host,
          hostRect,
          presentation.cleanupMs,
          { checkOcclusion: false }
        );
        return true;
      } catch (e) {
        try {
          if (pulse && pulse.isConnected) pulse.remove();
        } catch {
        }
        if (window.KEYPILOT_DEBUG) {
          console.warn("[KeyPilot] in-target focus pulse failed:", e);
        }
        return false;
      }
    }
    /**
     * Body-fixed green ghosts that copy the blue hover box (A or C, or B fallthrough).
     * Uses occlusion cleanup so lightbox portals do not leave a green linger.
     *
     * @param {string} clickEffect
     * @param {{ className: string, cleanupMs: number }} presentation
     * @param {Element|null|undefined} el
     */
    _flashFixedClickEffect(clickEffect, presentation, el2) {
      const { paintEl, rects } = this._resolveClickEffectRects(el2);
      if (!rects || !rects.length) return false;
      const radiusSource = paintEl && paintEl.nodeType === 1 ? paintEl : el2;
      let borderRadius = "0";
      try {
        borderRadius = this._resolveClickEffectBorderRadius(
          this._inTargetHost,
          radiusSource,
          el2
        );
      } catch {
        borderRadius = "0";
      }
      const trackEl = paintEl && paintEl.nodeType === 1 ? paintEl : el2;
      let mounted = false;
      for (let i = 0; i < rects.length; i++) {
        const liveRect = rects[i];
        if (!liveRect) continue;
        try {
          let pulse;
          if (clickEffect === "dash") {
            pulse = this._createDashChasePulse(liveRect, borderRadius);
          } else {
            pulse = document.createElement("div");
            pulse.className = presentation.className;
            pulse.setAttribute("aria-hidden", "true");
            pulse.setAttribute("data-kp-ephemeral-effect", clickEffect);
            pulse.style.left = `${liveRect.left}px`;
            pulse.style.top = `${liveRect.top}px`;
            pulse.style.width = `${liveRect.width}px`;
            pulse.style.height = `${liveRect.height}px`;
          }
          this._applyEphemeralBorderRadius(pulse, borderRadius, liveRect);
          document.body.appendChild(pulse);
          try {
            void pulse.offsetWidth;
          } catch {
          }
          this._trackEphemeralEffect(
            pulse,
            trackEl,
            liveRect,
            presentation.cleanupMs,
            { checkOcclusion: true, minVisibleMs: 140 }
          );
          mounted = true;
        } catch (e) {
          if (window.KEYPILOT_DEBUG) {
            console.warn("[KeyPilot] focus pulse failed:", e);
          }
        }
      }
      return mounted;
    }
    /**
     * F-key activation feedback for link-style targets.
     *
     * Always a body-fixed ghost (not an in-target sibling). Click / SPA
     * re-render would unmount an in-host pulse with the target before paint.
     * Occlusion cleanup still drops the ghost once a lightbox covers the source,
     * after a short min-visible window.
     *
     * Effect style comes from settings (clickMode.clickEffect):
     *   - flash (default): hard strobe border + glow
     *   - dash: dashed border chases around the perimeter
     *   - marquee: solid chaser light travels around the perimeter
     *   - scale: outline expands and fades out
     *   - none: no animation
     *
     * Whether any effect runs is decided solely by CLICKABLE_CATEGORY via
     * ElementDetector.isLinkStyleCategory (LINK + GENERIC only). Sliders, media,
     * buttons, text, and controls never get the pulse.
     *
     * @param {Element|null} [activationTarget] - Element that was F-activated.
     *   Prefer this over last-hover focus so category matches the real target.
     * @returns {boolean} true when a pulse node was mounted
     */
    flashFocusOverlay(activationTarget = null) {
      const el2 = activationTarget && activationTarget.nodeType === 1 ? activationTarget : this._currentStyledElement || this._lastFocusElement;
      try {
        const detector = window.keyPilot?.detector || window.keyPilot?.elementDetector;
        const cat = detector && typeof detector.getClickableCategory === "function" ? detector.getClickableCategory(el2) : this.getFocusCategory(el2);
        const showEffect = detector && typeof detector.isLinkStyleCategory === "function" ? detector.isLinkStyleCategory(cat) : cat === CLICKABLE_CATEGORY.LINK || cat === CLICKABLE_CATEGORY.GENERIC;
        if (!showEffect) return false;
      } catch {
      }
      const { clickEffect } = this._getClickModeSettings();
      if (clickEffect === "none") return false;
      const presentation = this._clickEffectPresentation(clickEffect);
      if (!presentation) return false;
      if (el2 && el2.nodeType === 1) {
        if (!el2.isConnected) return false;
        try {
          const cs = window.getComputedStyle(el2);
          if (cs && (cs.display === "none" || cs.visibility === "hidden")) return false;
        } catch {
        }
      }
      return !!this._flashFixedClickEffect(clickEffect, presentation, el2);
    }
    /**
     * Click New Tab / Background, Preview Link, Open Popover when the hover
     * target has no navigable URL.
     * Always a dashed orange strobe (not the user clickEffect, and not gated on
     * link-style category) so buttons, text, and empty space still get a "nope".
     *
     * @param {Element|null} [activationTarget]
     * @param {{ x?: number, y?: number }} [point] cursor fallback when there is no box
     * @returns {boolean}
     */
    flashDeniedDashOutline(activationTarget = null, point = null) {
      const el2 = activationTarget && activationTarget.nodeType === 1 ? activationTarget : this._currentStyledElement || this._lastFocusElement;
      const skipHuge = el2 === document.documentElement || el2 === document.body;
      const source = skipHuge ? null : el2;
      let rects = [];
      let paintEl = source;
      if (source) {
        try {
          const resolved = this._resolveClickEffectRects(source);
          paintEl = resolved.paintEl || source;
          rects = resolved.rects || [];
        } catch {
        }
      } else {
        try {
          if (this.focusOverlay && this.focusOverlay.style.display !== "none") {
            const r = this._asPositiveViewportRect(this.focusOverlay.getBoundingClientRect());
            if (r) rects = [r];
          }
        } catch {
        }
      }
      if (!rects.length) {
        const x = Number(point?.x);
        const y = Number(point?.y);
        if (Number.isFinite(x) && Number.isFinite(y)) {
          const size = 36;
          rects = [{ left: x - size / 2, top: y - size / 2, width: size, height: size }];
        }
      }
      if (!rects.length) return false;
      const radiusSource = paintEl && paintEl.nodeType === 1 ? paintEl : source;
      let borderRadius = "0";
      try {
        borderRadius = this._resolveClickEffectBorderRadius(
          this._inTargetHost,
          radiusSource,
          source
        );
      } catch {
        borderRadius = "0";
      }
      const trackEl = paintEl && paintEl.nodeType === 1 ? paintEl : source;
      let mounted = false;
      for (let i = 0; i < rects.length; i++) {
        const liveRect = rects[i];
        if (!liveRect) continue;
        try {
          const pulse = document.createElement("div");
          pulse.className = CSS_CLASSES.FOCUS_DASH_DENIED;
          pulse.setAttribute("aria-hidden", "true");
          pulse.setAttribute("data-kp-ephemeral-effect", "dash-denied");
          pulse.style.left = `${liveRect.left}px`;
          pulse.style.top = `${liveRect.top}px`;
          pulse.style.width = `${liveRect.width}px`;
          pulse.style.height = `${liveRect.height}px`;
          this._applyEphemeralBorderRadius(pulse, borderRadius, liveRect);
          document.body.appendChild(pulse);
          try {
            void pulse.offsetWidth;
          } catch {
          }
          this._trackEphemeralEffect(
            pulse,
            trackEl,
            liveRect,
            700,
            { checkOcclusion: false, minVisibleMs: 120 }
          );
          mounted = true;
        } catch (e) {
          if (window.KEYPILOT_DEBUG) {
            console.warn("[KeyPilot] denied dash outline failed:", e);
          }
        }
      }
      return mounted;
    }
    /**
     * Image-copy feedback: blue photo-frame over the source element that flashes,
     * pops slightly, then shrinks away (distinct from the green F-click expand pulse).
     *
     * @param {Element|null|undefined} element - Image or background-image host element
     */
    flashImageCopyPulse(element) {
      if (!element || element.nodeType !== 1) return;
      let rect = null;
      try {
        rect = element.getBoundingClientRect();
      } catch {
        return;
      }
      if (!rect) return;
      const w = Number(rect.width) || 0;
      const h = Number(rect.height) || 0;
      if (w < 2 || h < 2) return;
      const maxW = Math.max(48, (typeof window !== "undefined" ? window.innerWidth : 1200) * 0.92);
      const maxH = Math.max(48, (typeof window !== "undefined" ? window.innerHeight : 800) * 0.92);
      let left = rect.left;
      let top = rect.top;
      let width = w;
      let height = h;
      if (width > maxW) {
        left += (width - maxW) / 2;
        width = maxW;
      }
      if (height > maxH) {
        top += (height - maxH) / 2;
        height = maxH;
      }
      try {
        if (!element.isConnected) return;
        const pulse = document.createElement("div");
        pulse.className = CSS_CLASSES.IMAGE_COPY_PULSE;
        pulse.setAttribute("aria-hidden", "true");
        pulse.setAttribute("data-kp-ephemeral-effect", "image-copy");
        pulse.style.left = `${left}px`;
        pulse.style.top = `${top}px`;
        pulse.style.width = `${width}px`;
        pulse.style.height = `${height}px`;
        const borderRadius = this._resolveClickEffectBorderRadius(element);
        this._applyEphemeralBorderRadius(pulse, borderRadius, { width, height });
        document.body.appendChild(pulse);
        this._trackEphemeralEffect(
          pulse,
          element,
          { left, top, width, height },
          900,
          { checkOcclusion: true }
        );
      } catch (e) {
        if (window.KEYPILOT_DEBUG) {
          console.warn("[KeyPilot] image-copy pulse failed:", e);
        }
      }
    }
    /**
     * Outline the Font Info inspected text run (stroke rects, not Text Select fill).
     * @param {Range|null|undefined} range
     */
    showFontInfoOutline(range) {
      this.hideFontInfoOutline();
      if (!range || range.collapsed) return;
      let rects;
      try {
        rects = this.getClientRectsWithShadowSupport(range);
      } catch {
        rects = [];
      }
      if (!rects || !rects.length) return;
      const maxRects = 40;
      const n = Math.min(rects.length, maxRects);
      for (let i = 0; i < n; i++) {
        const rect = rects[i];
        const w = Number(rect.width) || 0;
        const h = Number(rect.height) || 0;
        if (w <= 0 || h <= 0) continue;
        try {
          const overlay = this.createElement("div", {
            className: CSS_CLASSES.FONT_INFO_OUTLINE,
            style: `
            position: fixed;
            left: ${rect.left}px;
            top: ${rect.top}px;
            width: ${w}px;
            height: ${h}px;
            box-sizing: border-box;
            background: ${COLORS.FONT_INFO_OUTLINE_FILL};
            border: 2px solid ${COLORS.FONT_INFO_OUTLINE};
            box-shadow: 0 0 0 1px ${COLORS.FONT_INFO_OUTLINE_SHADOW};
            pointer-events: none;
            z-index: ${Z_INDEX.OVERLAYS_BELOW};
          `
          });
          overlay.setAttribute("aria-hidden", "true");
          document.body.appendChild(overlay);
          this.fontInfoOverlays.push(overlay);
        } catch {
        }
      }
    }
    hideFontInfoOutline() {
      const list = this.fontInfoOverlays || [];
      for (const el2 of list) {
        try {
          el2.remove();
        } catch {
        }
      }
      this.fontInfoOverlays = [];
    }
    createViewportModalFrame() {
      if (this.viewportModalFrame) {
        return this.viewportModalFrame;
      }
      this.viewportModalFrame = this.createElement("div", {
        className: CSS_CLASSES.VIEWPORT_MODAL_FRAME,
        style: `
        display: none;
      `
      });
      document.body.appendChild(this.viewportModalFrame);
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Viewport modal frame created and added to DOM:", {
          element: this.viewportModalFrame,
          className: this.viewportModalFrame.className,
          parent: this.viewportModalFrame.parentElement?.tagName
        });
      }
      return this.viewportModalFrame;
    }
    showViewportModalFrame() {
      if (!this.viewportModalFrame) {
        this.createViewportModalFrame();
      }
      this.viewportModalFrame.style.display = "block";
      if (!this.resizeObserver && window.ResizeObserver) {
        this.resizeObserver = new ResizeObserver((entries) => {
          if (this.resizeTimeout) {
            clearTimeout(this.resizeTimeout);
          }
          this.resizeTimeout = setTimeout(() => {
            this.updateViewportModalFrameSize();
            this.resizeTimeout = null;
          }, 16);
        });
        this.resizeObserver.observe(document.documentElement);
        if (document.body) {
          this.resizeObserver.observe(document.body);
        }
      }
      if (!window.ResizeObserver) {
        this.windowResizeHandler = this.debounce(() => {
          this.updateViewportModalFrameSize();
        }, 16);
        window.addEventListener("resize", this.windowResizeHandler);
        window.addEventListener("orientationchange", this.windowResizeHandler);
      }
      this.fullscreenHandler = () => {
        setTimeout(() => {
          this.updateViewportModalFrameSize();
        }, 100);
      };
      document.addEventListener("fullscreenchange", this.fullscreenHandler);
      document.addEventListener("webkitfullscreenchange", this.fullscreenHandler);
      document.addEventListener("mozfullscreenchange", this.fullscreenHandler);
      document.addEventListener("MSFullscreenChange", this.fullscreenHandler);
      if (window.visualViewport) {
        this.visualViewportHandler = () => {
          this.updateViewportModalFrameSize();
        };
        window.visualViewport.addEventListener("resize", this.visualViewportHandler);
      }
      this.updateViewportModalFrameSize();
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Viewport modal frame shown with enhanced resize handling");
      }
    }
    hideViewportModalFrame() {
      if (this.viewportModalFrame) {
        this.viewportModalFrame.style.display = "none";
      }
      if (this.resizeObserver) {
        this.resizeObserver.disconnect();
        this.resizeObserver = null;
      }
      if (this.resizeTimeout) {
        clearTimeout(this.resizeTimeout);
        this.resizeTimeout = null;
      }
      if (this.windowResizeHandler) {
        window.removeEventListener("resize", this.windowResizeHandler);
        window.removeEventListener("orientationchange", this.windowResizeHandler);
        this.windowResizeHandler = null;
      }
      if (this.fullscreenHandler) {
        document.removeEventListener("fullscreenchange", this.fullscreenHandler);
        document.removeEventListener("webkitfullscreenchange", this.fullscreenHandler);
        document.removeEventListener("mozfullscreenchange", this.fullscreenHandler);
        document.removeEventListener("MSFullscreenChange", this.fullscreenHandler);
        this.fullscreenHandler = null;
      }
      if (this.visualViewportHandler && window.visualViewport) {
        window.visualViewport.removeEventListener("resize", this.visualViewportHandler);
        this.visualViewportHandler = null;
      }
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Viewport modal frame hidden and all listeners cleaned up");
      }
    }
    updateViewportModalFrame(show) {
      if (show) {
        this.showViewportModalFrame();
      } else {
        this.hideViewportModalFrame();
      }
    }
    updateViewportModalFrameSize() {
      if (!this.viewportModalFrame || this.viewportModalFrame.style.display === "none") {
        return;
      }
      let viewportWidth, viewportHeight;
      if (window.visualViewport) {
        viewportWidth = window.visualViewport.width;
        viewportHeight = window.visualViewport.height;
      } else {
        viewportWidth = window.innerWidth || document.documentElement.clientWidth;
        viewportHeight = window.innerHeight || document.documentElement.clientHeight;
      }
      const isFullscreen = !!(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement);
      if (!isFullscreen) {
        const windowWidth = window.outerWidth;
        const windowHeight = window.outerHeight;
        const widthDiff = Math.abs(windowWidth - viewportWidth);
        const heightDiff = Math.abs(windowHeight - viewportHeight);
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Viewport size analysis:", {
            viewportWidth,
            viewportHeight,
            windowWidth,
            windowHeight,
            widthDiff,
            heightDiff,
            isFullscreen,
            visualViewportAvailable: !!window.visualViewport
          });
        }
      }
      this.viewportModalFrame.style.width = `${viewportWidth}px`;
      this.viewportModalFrame.style.height = `${viewportHeight}px`;
      this.viewportModalFrame.style.left = "0px";
      this.viewportModalFrame.style.top = "0px";
      if (window.visualViewport) {
        this.viewportModalFrame.style.left = `${window.visualViewport.offsetLeft}px`;
        this.viewportModalFrame.style.top = `${window.visualViewport.offsetTop}px`;
      }
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Viewport modal frame size updated:", {
          width: `${viewportWidth}px`,
          height: `${viewportHeight}px`,
          left: this.viewportModalFrame.style.left,
          top: this.viewportModalFrame.style.top,
          isFullscreen,
          zoomLevel: window.devicePixelRatio || 1
        });
      }
    }
    cleanup() {
      try {
        this.hidePopover();
      } catch {
      }
      try {
        this.popupManager?.closeAll?.();
      } catch {
      }
      try {
        this.clearEphemeralEffects();
      } catch {
      }
      try {
        this._ephemeralEffectLifecycleDispose?.();
      } catch {
      }
      if (this.overlayObserver) {
        this.overlayObserver.disconnect();
        this.overlayObserver = null;
      }
      if (this.resizeObserver) {
        this.resizeObserver.disconnect();
        this.resizeObserver = null;
      }
      if (this.resizeTimeout) {
        clearTimeout(this.resizeTimeout);
        this.resizeTimeout = null;
      }
      if (this.windowResizeHandler) {
        window.removeEventListener("resize", this.windowResizeHandler);
        window.removeEventListener("orientationchange", this.windowResizeHandler);
        this.windowResizeHandler = null;
      }
      if (this.fullscreenHandler) {
        document.removeEventListener("fullscreenchange", this.fullscreenHandler);
        document.removeEventListener("webkitfullscreenchange", this.fullscreenHandler);
        document.removeEventListener("mozfullscreenchange", this.fullscreenHandler);
        document.removeEventListener("MSFullscreenChange", this.fullscreenHandler);
        this.fullscreenHandler = null;
      }
      if (this.visualViewportHandler && window.visualViewport) {
        window.visualViewport.removeEventListener("resize", this.visualViewportHandler);
        this.visualViewportHandler = null;
      }
      this.cleanupRenderingMode();
      if (this.focusOverlay) {
        this.focusOverlay.remove();
        this.focusOverlay = null;
      }
      if (this.inspectorOverlay) {
        this.inspectorOverlay.remove();
        this.inspectorOverlay = null;
      }
      this.deleteOverlay = null;
      this.colsOverlay = null;
      this._inspectorOverlayKind = null;
      this.hideInspectorModeIndicator();
      if (this.highlightManager) {
        this.highlightManager.cleanup();
      }
      this._removeEdgeJumpFadeEl();
      if (this.viewportModalFrame) {
        this.viewportModalFrame.remove();
        this.viewportModalFrame = null;
      }
      if (this.escExitLabelText) {
        this.escExitLabelText.remove();
        this.escExitLabelText = null;
      }
      if (this.escExitLabelHover) {
        this.escExitLabelHover.remove();
        this.escExitLabelHover = null;
      }
      if (this.textFocusEscHint) {
        this.textFocusEscHint.remove();
        this.textFocusEscHint = null;
      }
      if (this.textHoverActivateHint) {
        this.textHoverActivateHint.remove();
        this.textHoverActivateHint = null;
      }
      this.cleanupDebugPanel();
      this.cleanupShadowRootDebugHud();
      this.hideFontInfoOutline();
    }
    createElement(tag, props = {}) {
      const element = document.createElement(tag);
      for (const [key, value] of Object.entries(props)) {
        if (key === "className") {
          element.className = value;
        } else if (key === "style") {
          element.style.cssText = value;
        } else {
          element.setAttribute(key, value);
        }
      }
      return element;
    }
    // Utility method for debouncing function calls
    debounce(func, wait) {
      let timeout;
      return function executedFunction(...args) {
        const later = () => {
          clearTimeout(timeout);
          func(...args);
        };
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
      };
    }
    get popoverContainer() {
      return this.popover.popoverContainer;
    }
    set popoverContainer(v) {
      this.popover.popoverContainer = v;
    }
    get popoverIframeElement() {
      return this.popover.popoverIframeElement;
    }
    set popoverIframeElement(v) {
      this.popover.popoverIframeElement = v;
    }
    get popoverIframeWindow() {
      return this.popover.popoverIframeWindow;
    }
    set popoverIframeWindow(v) {
      this.popover.popoverIframeWindow = v;
    }
    get popoverCloseButton() {
      return this.popover.popoverCloseButton;
    }
    set popoverCloseButton(v) {
      this.popover.popoverCloseButton = v;
    }
    get _popoverWindowUrl() {
      return this.popover._popoverWindowUrl;
    }
    set _popoverWindowUrl(v) {
      this.popover._popoverWindowUrl = v;
    }
    get _popoverWindowId() {
      return this.popover._popoverWindowId;
    }
    set _popoverWindowId(v) {
      this.popover._popoverWindowId = v;
    }
    get _popoverWindowTabId() {
      return this.popover._popoverWindowTabId;
    }
    set _popoverWindowTabId(v) {
      this.popover._popoverWindowTabId = v;
    }
    get _popoverWindowKind() {
      return this.popover._popoverWindowKind;
    }
    set _popoverWindowKind(v) {
      this.popover._popoverWindowKind = v;
    }
    clearPopoverWindowTracking() {
      this.popover.clearPopoverWindowTracking();
    }
    setDocsFontScale(scale) {
      return this.popover.setDocsFontScale(scale);
    }
    showInPageDocsPopover(opts = {}) {
      return this.popover.showInPageDocsPopover(opts);
    }
    setDocsTopic(topicId, hash) {
      return this.popover.setDocsTopic(topicId, hash);
    }
    showInPageSettingsPopover(opts = {}) {
      return this.popover.showInPageSettingsPopover(opts);
    }
    setSettingsPanel(panelId) {
      return this.popover.setSettingsPanel(panelId);
    }
    showPopover(url, opts = {}) {
      return this.popover.showPopover(url, opts);
    }
    hidePopover(opts = {}) {
      return this.popover.hidePopover(opts);
    }
    postMessageToPopoverIframe(message) {
      return this.popover.postMessageToPopoverIframe(message);
    }
    scrollPopoverBy(deltaY, behavior = "smooth") {
      return this.popover.scrollPopoverBy(deltaY, behavior);
    }
    scrollPopoverToTop(behavior = "smooth") {
      return this.popover.scrollPopoverToTop(behavior);
    }
    scrollPopoverToBottom(behavior = "smooth") {
      return this.popover.scrollPopoverToBottom(behavior);
    }
    isPopoverOpen() {
      return this.popover.isPopoverOpen();
    }
    showPreviewPopover(url, opts = {}) {
      return this.popover.showPreviewPopover(url, opts);
    }
    /**
     * Cover the overflow box that will jump, run `onCovered` (instant scroll),
     * wait for the scroller to stop moving, then uncover.
     * Nested scrollers get their client box; the document root gets the viewport.
     * @param {() => void} onCovered
     * @param {{ durationMs?: number, coverEl?: Element|null, coverRect?: { left: number, top: number, width: number, height: number }|null, edge?: 'top'|'bottom'|null }} [opts]
     * @returns {Promise<void>}
     */
    async runEdgeJumpFade(onCovered, opts = {}) {
      const durationMs = Number.isFinite(Number(opts.durationMs)) ? Math.max(80, Number(opts.durationMs)) : null;
      let reduced = false;
      try {
        reduced = !!window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;
      } catch {
      }
      if (reduced) {
        try {
          onCovered?.();
        } catch {
        }
        return;
      }
      const token = ++this._edgeJumpFadeToken;
      const el2 = this._ensureEdgeJumpFadeEl();
      this._positionEdgeJumpFadeEl(el2, opts.coverEl || null, opts.coverRect || null);
      const bg = this._resolveEdgeJumpFadeColor(opts.coverEl || null);
      const blurMs = durationMs ?? SCROLL.EDGE_JUMP_BLUR_MS;
      const coverMs = durationMs === null ? SCROLL.EDGE_JUMP_COVER_MS : Math.round(durationMs * 0.5);
      const revealMs = durationMs ?? SCROLL.EDGE_JUMP_REVEAL_MS;
      const clearMs = durationMs === null ? SCROLL.EDGE_JUMP_CLEAR_MS : Math.round(durationMs * 0.75);
      this._syncEdgeJumpFadeIcon(el2, opts.edge, bg);
      el2.style.transition = "none";
      el2.style.opacity = "0";
      el2.style.backdropFilter = "none";
      el2.style.webkitBackdropFilter = "none";
      el2.style.background = this._withEdgeJumpFadeAlpha(bg, 0);
      await this._fadeEdgeJumpEl(el2, 1, blurMs, {
        blurPx: 10,
        background: this._withEdgeJumpFadeAlpha(bg, 0.42)
      });
      if (token !== this._edgeJumpFadeToken) return;
      await this._fadeEdgeJumpEl(el2, 1, coverMs, {
        blurPx: 0,
        background: this._withEdgeJumpFadeAlpha(bg, 1)
      });
      try {
        onCovered?.();
      } catch {
      }
      if (token !== this._edgeJumpFadeToken) return;
      await waitForScrollSettle(opts.coverEl || null, {
        timeoutMs: SCROLL.EDGE_JUMP_SETTLE_MS
      });
      if (token !== this._edgeJumpFadeToken) return;
      await this._fadeEdgeJumpEl(el2, 1, revealMs, {
        blurPx: 10,
        background: this._withEdgeJumpFadeAlpha(bg, 0.42)
      });
      if (token !== this._edgeJumpFadeToken) return;
      await this._fadeEdgeJumpEl(el2, 0, clearMs, {
        blurPx: 0,
        background: this._withEdgeJumpFadeAlpha(bg, 0)
      });
      if (token === this._edgeJumpFadeToken) this._removeEdgeJumpFadeEl();
    }
    /**
     * @returns {HTMLElement}
     */
    _ensureEdgeJumpFadeEl() {
      if (this._edgeJumpFadeEl && this._edgeJumpFadeEl.isConnected) return this._edgeJumpFadeEl;
      const el2 = document.createElement("div");
      el2.className = CSS_CLASSES.EDGE_JUMP_FADE;
      el2.setAttribute("aria-hidden", "true");
      const host = document.documentElement || document.body;
      try {
        host.appendChild(el2);
      } catch {
        document.body?.appendChild(el2);
      }
      this._edgeJumpFadeEl = el2;
      return el2;
    }
    /**
     * Pin the veil to the visible overflow box (or the viewport for the document).
     * @param {HTMLElement} veil
     * @param {Element|null} coverEl
     * @param {{ left: number, top: number, width: number, height: number }|null} coverRect
     */
    _positionEdgeJumpFadeEl(veil, coverEl, coverRect) {
      let left = 0;
      let top = 0;
      let width = window.innerWidth || 0;
      let height = window.innerHeight || 0;
      if (coverRect && Number.isFinite(coverRect.width) && Number.isFinite(coverRect.height)) {
        left = coverRect.left;
        top = coverRect.top;
        width = coverRect.width;
        height = coverRect.height;
      } else if (coverEl) {
        try {
          const doc = coverEl.ownerDocument || document;
          const se = doc.scrollingElement;
          const isRoot = coverEl === se || coverEl === doc.documentElement || coverEl === doc.body;
          if (isRoot) {
            left = 0;
            top = 0;
            width = window.innerWidth || 0;
            height = window.innerHeight || 0;
          } else {
            const r = coverEl.getBoundingClientRect();
            left = r.left;
            top = r.top;
            width = r.width;
            height = r.height;
          }
        } catch {
        }
      }
      veil.style.left = `${Math.round(left)}px`;
      veil.style.top = `${Math.round(top)}px`;
      veil.style.width = `${Math.max(0, Math.round(width))}px`;
      veil.style.height = `${Math.max(0, Math.round(height))}px`;
    }
    /**
     * @param {Element|null} coverEl
     * @returns {string}
     */
    _resolveEdgeJumpFadeColor(coverEl) {
      const isTransparent = (c) => {
        const s = String(c || "").trim().toLowerCase();
        if (!s || s === "transparent") return true;
        const m = s.match(/^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)(?:\s*,\s*([\d.]+))?\s*\)$/);
        if (m && m[4] !== void 0 && Number(m[4]) === 0) return true;
        return false;
      };
      try {
        let node = coverEl;
        let hops = 0;
        while (node && hops++ < 12) {
          const c = getComputedStyle(node).backgroundColor;
          if (!isTransparent(c)) return c;
          node = node.parentElement;
        }
        const html = getComputedStyle(document.documentElement);
        const body = document.body ? getComputedStyle(document.body) : html;
        for (const c of [body.backgroundColor, html.backgroundColor]) {
          if (!isTransparent(c)) return c;
        }
      } catch {
      }
      try {
        if (window.matchMedia?.("(prefers-color-scheme: dark)")?.matches) return "#111";
      } catch {
      }
      return "#fff";
    }
    /**
     * @param {string} color
     * @returns {string}
     */
    _edgeJumpIconFill(color4) {
      const s = String(color4 || "").trim().toLowerCase();
      let r = 255;
      let g = 255;
      let b = 255;
      const hex = s.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);
      if (hex) {
        const h = hex[1];
        if (h.length === 3) {
          r = parseInt(h[0] + h[0], 16);
          g = parseInt(h[1] + h[1], 16);
          b = parseInt(h[2] + h[2], 16);
        } else {
          r = parseInt(h.slice(0, 2), 16);
          g = parseInt(h.slice(2, 4), 16);
          b = parseInt(h.slice(4, 6), 16);
        }
      } else {
        const m = s.match(/^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/);
        if (m) {
          r = Number(m[1]);
          g = Number(m[2]);
          b = Number(m[3]);
        }
      }
      const y = (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255;
      return y > 0.55 ? "rgba(28, 28, 28, 0.62)" : "rgba(255, 255, 255, 0.78)";
    }
    /**
     * Convert the resolved veil color to a translucent tint without applying
     * opacity to the child icon. Computed styles normally return rgb(...), but
     * keep hex support for the dark/light fallbacks above.
     * @param {string} color
     * @param {number} alpha
     * @returns {string}
     */
    _withEdgeJumpFadeAlpha(color4, alpha) {
      const s = String(color4 || "").trim().toLowerCase();
      let r;
      let g;
      let b;
      const hex = s.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);
      if (hex) {
        const h = hex[1];
        r = parseInt(h.length === 3 ? h[0] + h[0] : h.slice(0, 2), 16);
        g = parseInt(h.length === 3 ? h[1] + h[1] : h.slice(2, 4), 16);
        b = parseInt(h.length === 3 ? h[2] + h[2] : h.slice(4, 6), 16);
      } else {
        const rgb = s.match(
          /^rgba?\(\s*([\d.]+)[,\s]+([\d.]+)[,\s]+([\d.]+)(?:\s*[,/]\s*[\d.]+)?\s*\)$/
        );
        if (rgb) {
          r = Number(rgb[1]);
          g = Number(rgb[2]);
          b = Number(rgb[3]);
        }
      }
      if (![r, g, b].every(Number.isFinite)) return `rgba(255, 255, 255, ${alpha})`;
      const a = Math.max(0, Math.min(1, Number(alpha)));
      return `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${a})`;
    }
    /**
     * Corner SVG: top-left for Scroll To Top, bottom-left for Scroll To Bottom.
     * @param {HTMLElement} veil
     * @param {'top'|'bottom'|string|null|undefined} edge
     * @param {string} backgroundColor
     */
    _syncEdgeJumpFadeIcon(veil, edge, backgroundColor) {
      const dir = edge === "bottom" ? "bottom" : "top";
      const pathD = EDGE_JUMP_ICON_PATHS[dir];
      let svg = veil.querySelector?.(`.${CSS_CLASSES.EDGE_JUMP_FADE_ICON}`);
      if (!svg) {
        svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        svg.setAttribute("class", CSS_CLASSES.EDGE_JUMP_FADE_ICON);
        svg.setAttribute("aria-hidden", "true");
        svg.setAttribute("viewBox", "0 0 512 512");
        svg.setAttribute("width", "56");
        svg.setAttribute("height", "56");
        const path2 = document.createElementNS("http://www.w3.org/2000/svg", "path");
        path2.setAttribute("d", pathD);
        svg.appendChild(path2);
        veil.appendChild(svg);
      } else {
        const path2 = svg.querySelector("path");
        if (path2) path2.setAttribute("d", pathD);
      }
      svg.setAttribute("data-kp-edge", dir);
      const fill = this._edgeJumpIconFill(backgroundColor);
      svg.setAttribute("fill", fill);
      const path = svg.querySelector("path");
      if (path) path.setAttribute("fill", fill);
    }
    /**
     * @param {HTMLElement} el
     * @param {number} opacity
     * @param {number} ms
     * @param {{ blurPx?: number, background?: string }} [state]
     * @returns {Promise<void>}
     */
    _fadeEdgeJumpEl(el2, opacity, ms, state = {}) {
      return new Promise((resolve) => {
        let settled = false;
        const done = () => {
          if (settled) return;
          settled = true;
          el2.removeEventListener("transitionend", onEnd);
          resolve();
        };
        const onEnd = (e) => {
          if (e.target !== el2 || e.propertyName && !["opacity", "backdrop-filter", "-webkit-backdrop-filter", "background-color"].includes(e.propertyName)) return;
          done();
        };
        el2.addEventListener("transitionend", onEnd);
        el2.style.transition = [
          `opacity ${ms}ms ease`,
          `backdrop-filter ${ms}ms ease`,
          `-webkit-backdrop-filter ${ms}ms ease`,
          `background-color ${ms}ms ease`
        ].join(", ");
        const apply = () => {
          el2.style.opacity = String(opacity);
          const blurPx = Number(state.blurPx) || 0;
          const blur = blurPx > 0 ? `blur(${blurPx}px)` : "none";
          el2.style.backdropFilter = blur;
          el2.style.webkitBackdropFilter = blur;
          if (state.background) el2.style.background = state.background;
        };
        requestAnimationFrame(() => requestAnimationFrame(apply));
        setTimeout(done, ms + 80);
      });
    }
    _removeEdgeJumpFadeEl() {
      this._edgeJumpFadeToken += 1;
      const el2 = this._edgeJumpFadeEl;
      this._edgeJumpFadeEl = null;
      if (!el2) return;
      try {
        el2.remove();
      } catch {
      }
    }
    /**
     * Scroll the popover iframe by a delta (in pixels).
     * @param {number} deltaY
     * @param {'smooth'|'auto'} behavior
     */
    // =============================================================================
    // DEBUG PANEL - Performance metrics display in upper-right corner
    // =============================================================================
    /**
     * Initialize debug panel if enabled
     */
    initDebugPanel() {
      if (!FEATURE_FLAGS.ENABLE_DEBUG_PANEL) return;
      this.createDebugPanel();
      this.startDebugPanelUpdates();
    }
    /**
     * Create the debug panel element
     */
    createDebugPanel() {
      if (this.debugPanel) return;
      this.debugPanel = document.createElement("div");
      this.debugPanel.id = "kpv2-debug-panel";
      this.debugPanel.style.cssText = `
      position: fixed;
      top: 10px;
      right: 10px;
      background: rgba(0, 0, 0, 0.9);
      color: #00ff00;
      font-family: monospace;
      font-size: 11px;
      padding: 8px 12px;
      border-radius: 4px;
      border: 1px solid #333;
      z-index: ${Z_INDEX.DEBUG_HUD};
      pointer-events: none;
      max-width: 300px;
      white-space: pre-wrap;
      line-height: 1.4;
    `;
      document.body.appendChild(this.debugPanel);
    }
    /**
     * Start periodic updates of debug panel data
     */
    startDebugPanelUpdates() {
      if (!this.debugPanel) return;
      this.updateDebugPanel();
      this.debugPanelUpdateInterval = setInterval(() => {
        this.updateDebugPanel();
      }, 2e3);
    }
    /**
     * Update debug panel with current metrics
     */
    updateDebugPanel() {
      if (!this.debugPanel || !FEATURE_FLAGS.ENABLE_DEBUG_PANEL) return;
      const data = this.collectDebugData();
      const content = this.formatDebugContent(data);
      this.debugPanel.textContent = content;
    }
    /**
     * Collect debug data from various sources
     */
    collectDebugData() {
      const intersectionManager = window.keyPilot?.intersectionManager;
      const complexPageDetector = intersectionManager?.complexPageDetector;
      const currentState = window.keyPilot?.state?.getState?.() || {};
      const hoverElement = currentState.focusEl;
      let hoverInfo = "None";
      let clickableReasons = [];
      if (hoverElement) {
        const tagName = hoverElement.tagName?.toLowerCase() || "unknown";
        const className = hoverElement.className ? `.${hoverElement.className.split(" ")[0]}` : "";
        const id = hoverElement.id ? `#${hoverElement.id}` : "";
        const href = hoverElement.href ? ` \u2192 ${hoverElement.href.substring(0, 30)}...` : "";
        const text = hoverElement.textContent?.trim().substring(0, 20) || "";
        hoverInfo = `${tagName}${id}${className}${href ? href : text ? ` "${text}..."` : ""}`;
        clickableReasons = this.analyzeClickableReasons(hoverElement);
      }
      const isComplexPage = complexPageDetector ? complexPageDetector.complexityLevel !== "low" : false;
      return {
        // Page complexity
        complexityLevel: complexPageDetector?.complexityLevel || "unknown",
        isComplexPage,
        // Hover information
        hoverElement: hoverInfo,
        clickableReasons,
        // Element counts
        totalElements: document.querySelectorAll("*").length,
        interactiveElements: document.querySelectorAll(
          'a[href], button, input, select, textarea, [role="button"], [role="link"], [contenteditable="true"], [onclick]'
        ).length,
        // IO metrics
        ioObservations: intersectionManager?.observedInteractiveElements?.size || 0,
        visibleElements: intersectionManager?.visibleInteractiveElements?.size || 0,
        // RBush metrics
        rbushEnabled: intersectionManager?._rtreeEnabled?.() || false,
        rbushItems: intersectionManager?._rtreeItemsByElement?.size || 0,
        rbushQueries: intersectionManager?.metrics?.rtreeQueries || 0,
        rbushHits: intersectionManager?.metrics?.rtreeHits || 0,
        // Performance metrics
        cacheHits: intersectionManager?.metrics?.cacheHits || 0,
        cacheMisses: intersectionManager?.metrics?.cacheMisses || 0,
        observerUpdates: intersectionManager?.metrics?.observerUpdates || 0,
        // Culling stats (only for medium/high complexity sites)
        culledCount: isComplexPage ? intersectionManager?.metrics?.culledCount || 0 : 0,
        totalCulled: isComplexPage ? intersectionManager?.metrics?.totalCulled || 0 : 0,
        // Rendering mode
        renderingMode: this.renderingMode || "unknown",
        // Memory estimate
        estimatedMemory: this.estimateMemoryUsage()
      };
    }
    /**
     * Estimate memory usage of various components
     */
    estimateMemoryUsage() {
      const intersectionManager = window.keyPilot?.intersectionManager;
      let memoryKB = 0;
      if (intersectionManager?._rtreeItemsByElement) {
        memoryKB += intersectionManager._rtreeItemsByElement.size * 0.5;
      }
      if (intersectionManager?.elementPositionCache) {
        memoryKB += intersectionManager.elementPositionCache.size * 1;
      }
      return Math.round(memoryKB);
    }
    /**
     * Analyze why an element is considered clickable
     */
    analyzeClickableReasons(element) {
      if (!element || element.nodeType !== 1) return [];
      const reasons = [];
      const focusableSel = 'a[href], button, input, select, textarea, video, audio, [contenteditable="true"], [role="button"], [role="link"], [role="checkbox"], [role="radio"], [role="tab"], [data-action], [data-toggle], [data-click], [data-href], [data-link], [vue-click], [ng-click]';
      const matchesSelector = element.matches && element.matches(focusableSel);
      if (matchesSelector) {
        reasons.push("CSS Selector Match");
      }
      const role = element.getAttribute && (element.getAttribute("role") || "").trim().toLowerCase() || "";
      const clickableRoles = ["link", "button", "slider", "checkbox", "radio", "tab", "menuitem", "option", "switch", "treeitem", "combobox", "spinbutton"];
      const hasRole = role && clickableRoles.includes(role);
      if (hasRole) {
        reasons.push(`ARIA Role: "${role}"`);
      }
      const hasOnClick = element.onclick || element.getAttribute("onclick");
      const elementDetector = window.keyPilot?.elementDetector;
      const hasTrackedClickHandler = elementDetector?.hasTrackedClickHandler?.(element);
      if (hasOnClick) {
        reasons.push("onclick Attribute");
      }
      if (hasTrackedClickHandler) {
        reasons.push("Event Listener (click)");
      }
      try {
        const hasCursor = elementDetector && typeof elementDetector.hasExplicitCursorPointer === "function" ? elementDetector.hasExplicitCursorPointer(element) : !!(window.getComputedStyle && window.getComputedStyle(element).cursor === "pointer");
        if (hasCursor) {
          reasons.push("CSS cursor: pointer");
        }
      } catch {
      }
      if (reasons.length === 0) {
        try {
          const elementDetector2 = window.keyPilot?.elementDetector;
          if (elementDetector2 && typeof elementDetector2.isLikelyInteractive === "function") {
            const isClickable = elementDetector2.isLikelyInteractive(element);
            if (isClickable) {
              reasons.push("Likely Interactive (detected by element detector)");
            }
          }
        } catch (e) {
          const intersectionManager = window.keyPilot?.intersectionManager;
          if (intersectionManager?.observedInteractiveElements?.has?.(element)) {
            reasons.push("Observed as Interactive Element");
          }
        }
      }
      return reasons;
    }
    /**
     * Format debug data into readable display content
     */
    formatDebugContent(data) {
      const cacheHitRate = data.cacheHits + data.cacheMisses > 0 ? Math.round(data.cacheHits / (data.cacheHits + data.cacheMisses) * 100) : 0;
      const rbushHitRate = data.rbushQueries > 0 ? Math.round(data.rbushHits / data.rbushQueries * 100) : 0;
      const cullingSection = data.isComplexPage ? `

\u{1F5D1}\uFE0F  Spatial Culling
Last Cull: ${data.culledCount.toLocaleString()}
Total Culled: ${data.totalCulled.toLocaleString()}` : "";
      const clickableReasonsSection = data.clickableReasons.length > 0 ? `
\u{1F50D} Clickable Because:
${data.clickableReasons.map((reason) => `  \u2022 ${reason}`).join("\n")}` : "";
      return `KeyPilot Debug Panel
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
Complexity: ${data.complexityLevel.toUpperCase()} ${data.isComplexPage ? "\u{1F534}" : "\u{1F7E2}"}
Renderer: ${data.renderingMode.toUpperCase()}

\u{1F3AF} Hover: ${data.hoverElement}${clickableReasonsSection}

\u{1F4CA} Elements
Total: ${data.totalElements.toLocaleString()}
Interactive: ${data.interactiveElements.toLocaleString()}

\u{1F441}\uFE0F  IO Observer
Observed: ${data.ioObservations.toLocaleString()}
Visible: ${data.visibleElements.toLocaleString()}

\u{1F333} RBush Index
Enabled: ${data.rbushEnabled ? "\u2705" : "\u274C"}
Items: ${data.rbushItems.toLocaleString()}
Queries: ${data.rbushQueries.toLocaleString()}
Hit Rate: ${rbushHitRate}%${cullingSection}

\u26A1 Performance
Cache Hits: ${data.cacheHits.toLocaleString()} (${cacheHitRate}%)
Observer Updates: ${data.observerUpdates.toLocaleString()}

\u{1F4BE} Memory: ~${data.estimatedMemory}KB`;
    }
    /**
     * Clean up debug panel
     */
    cleanupDebugPanel() {
      if (this.debugPanelUpdateInterval) {
        clearInterval(this.debugPanelUpdateInterval);
        this.debugPanelUpdateInterval = null;
      }
      if (this.debugPanel) {
        this.debugPanel.remove();
        this.debugPanel = null;
      }
    }
    // =============================================================================
    // SHADOW ROOT DEBUG HUD — leaf / focus / paint + Auto | Auto B→C | A|B|C
    // =============================================================================
    /**
     * Enable or disable the interactive shadow-root paint debug HUD.
     * Runtime: `keyPilot.setShadowRootDebugHud(true)`
     * @param {boolean} enabled
     */
    setShadowRootDebugHud(enabled) {
      this._shadowDebugHudEnabled = !!enabled;
      try {
        if (FEATURE_FLAGS) FEATURE_FLAGS.DEBUG_SHADOW_ROOT_HUD = !!enabled;
      } catch {
      }
      if (this._shadowDebugHudEnabled) {
        this._shadowDebugPaintOverride = this._settingsPaintOverride();
        this._ensureShadowRootDebugHud();
        try {
          const focusEl = window.keyPilot?.state?.getState?.()?.focusEl || window.keyPilot?.intersectionManager?.getDomHoveredElement?.() || null;
          if (focusEl) this.updateFocusOverlay(focusEl);
          else this._updateShadowRootDebugHud(null, null, {
            inShadow: false,
            autoStrategy: "\u2014",
            appliedStrategy: "\u2014",
            override: this._shadowDebugPaintOverride
          });
        } catch {
        }
      } else {
        this._shadowDebugPaintOverride = null;
        this.cleanupShadowRootDebugHud();
        try {
          const focusEl = window.keyPilot?.state?.getState?.()?.focusEl || null;
          if (focusEl) this.updateFocusOverlay(focusEl);
        } catch {
        }
      }
    }
    /**
     * Force paint strategy while the HUD is open.
     * @param {'A'|'B'|'C'|'BC'|null|string} strategy
     *   null / 'auto' clears override (full A→B→C auto).
     *   'BC' / 'B->C' / 'AUTO_BC' = Auto B→C (skip A; try B then C).
     */
    setShadowDebugPaintStrategy(strategy) {
      const raw = strategy == null ? null : String(strategy).trim();
      const upper = raw ? raw.toUpperCase() : null;
      if (upper === "A" || upper === "B" || upper === "C") {
        this._shadowDebugPaintOverride = upper;
      } else if (upper === "BC" || upper === "B->C" || upper === "B\u2192C" || upper === "AUTO_BC" || upper === "AUTO-BC" || upper === "AUTO B->C" || upper === "AUTO B\u2192C") {
        this._shadowDebugPaintOverride = "BC";
      } else {
        this._shadowDebugPaintOverride = null;
      }
      this._refreshShadowDebugHudButtons();
      try {
        const focusEl = window.keyPilot?.state?.getState?.()?.focusEl || window.keyPilot?.intersectionManager?.getDomHoveredElement?.() || null;
        if (focusEl) this.updateFocusOverlay(focusEl);
      } catch {
      }
    }
    /** @returns {boolean} */
    isShadowRootDebugHudEnabled() {
      return !!this._shadowDebugHudEnabled;
    }
    cleanupShadowRootDebugHud() {
      if (this._shadowDebugHud) {
        try {
          this._shadowDebugHud.remove();
        } catch {
        }
        this._shadowDebugHud = null;
      }
    }
    /**
     * Compact element description for the HUD.
     * @param {Element|null|undefined} el
     * @returns {{ line: string, detail: string }}
     */
    _describeElForShadowDebug(el2) {
      if (!el2 || el2.nodeType !== 1) {
        return { line: "(none)", detail: "" };
      }
      const tag = el2.tagName || "?";
      const id = el2.id ? `#${el2.id}` : "";
      let cls = "";
      try {
        const cn = typeof el2.className === "string" ? el2.className : el2.className && el2.className.baseVal || "";
        const parts = String(cn).trim().split(/\s+/).filter(Boolean).slice(0, 2);
        cls = parts.length ? "." + parts.join(".") : "";
      } catch {
      }
      let wh = "?\xD7?";
      try {
        const r = el2.getBoundingClientRect();
        wh = `${Math.round(r.width)}\xD7${Math.round(r.height)}`;
      } catch {
      }
      let inShadow = false;
      const hosts = [];
      try {
        inShadow = this._isInShadowTree(el2);
        let n = el2;
        let depth = 0;
        while (n && depth++ < 8) {
          const root = typeof n.getRootNode === "function" ? n.getRootNode() : null;
          if (root && typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot) {
            hosts.push(root.host?.tagName || "HOST");
            n = root.host;
          } else {
            break;
          }
        }
      } catch {
      }
      let href = "";
      try {
        if (el2.href) href = String(el2.href).slice(0, 56);
      } catch {
      }
      const line = `${tag}${id}${cls}`.slice(0, 72);
      const detailParts = [
        wh,
        inShadow ? `shadow:${hosts.join("\u2190") || "?"}` : "light",
        href ? href : null
      ].filter(Boolean);
      return { line, detail: detailParts.join(" \xB7 ") };
    }
    _ensureShadowRootDebugHud() {
      if (this._shadowDebugHud && this._shadowDebugHud.isConnected) {
        try {
          if (!this._shadowDebugHud.querySelector('[data-kp-shadow-dbg-strategy="BC"]')) {
            this._shadowDebugHud.remove();
            this._shadowDebugHud = null;
          }
        } catch {
        }
      }
      if (this._shadowDebugHud && this._shadowDebugHud.isConnected) {
        this._shadowDebugHud.style.display = "block";
        this._refreshShadowDebugHudButtons();
        return this._shadowDebugHud;
      }
      const hud = document.createElement("div");
      hud.id = "kpv2-shadow-debug-hud";
      hud.className = "kpv2-shadow-debug-hud";
      hud.setAttribute("data-kp-ui", "1");
      hud.setAttribute("aria-label", "KeyPilot shadow root debug HUD");
      hud.style.cssText = `
      position: fixed;
      bottom: 12px;
      left: 12px;
      z-index: ${Z_INDEX.DEBUG_HUD};
      width: min(420px, calc(100vw - 24px));
      max-height: min(50vh, 420px);
      overflow: auto;
      background: rgba(12, 16, 22, 0.94);
      color: #d7ffe8;
      font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
      font-size: 11px;
      line-height: 1.35;
      padding: 10px 12px 12px;
      border-radius: 8px;
      border: 1px solid #3d8f6a;
      box-shadow: 0 8px 28px rgba(0,0,0,0.45);
      pointer-events: auto;
      user-select: text;
    `;
      hud.innerHTML = `
      <div style="display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px;">
        <strong style="color:#7CFFB2;letter-spacing:0.02em;">Shadow Root Debug</strong>
        <button type="button" data-kp-shadow-dbg="close"
          style="background:#243028;color:#cfe;border:1px solid #3d8f6a;border-radius:4px;padding:2px 8px;cursor:pointer;font:inherit;">\xD7</button>
      </div>
      <div data-kp-shadow-dbg-body style="white-space:pre-wrap;margin-bottom:10px;color:#b8eccf;"></div>
      <div style="display:flex;flex-wrap:wrap;gap:6px;align-items:center;">
        <span style="color:#8ab89a;">Paint:</span>
        <button type="button" data-kp-shadow-dbg-strategy="auto">Auto</button>
        <button type="button" data-kp-shadow-dbg-strategy="BC">Auto B\u2192C</button>
        <button type="button" data-kp-shadow-dbg-strategy="A">A outline</button>
        <button type="button" data-kp-shadow-dbg-strategy="B">B in-target</button>
        <button type="button" data-kp-shadow-dbg-strategy="C">C fixed</button>
      </div>
      <div style="margin-top:8px;color:#6f9a80;font-size:10px;">
        Auto = A\u2192B\u2192C \xB7 Auto B\u2192C = skip A \xB7 A outline \xB7 B in-host \xB7 C body fixed
      </div>
    `;
      try {
        hud.querySelectorAll("button[data-kp-shadow-dbg-strategy]").forEach((btn) => {
          btn.style.cssText = `
          background:#1a2820;color:#cfe;border:1px solid #3d5a48;border-radius:4px;
          padding:4px 8px;cursor:pointer;font:inherit;
        `;
        });
      } catch {
      }
      const stop = (e) => {
        try {
          e.stopPropagation();
        } catch {
        }
        try {
          e.stopImmediatePropagation?.();
        } catch {
        }
      };
      hud.addEventListener("pointerdown", stop, true);
      hud.addEventListener("pointerup", stop, true);
      hud.addEventListener("click", (e) => {
        stop(e);
        const t = e.target;
        if (!(t instanceof Element)) return;
        if (t.getAttribute("data-kp-shadow-dbg") === "close") {
          this.setShadowRootDebugHud(false);
          return;
        }
        const strat = t.getAttribute("data-kp-shadow-dbg-strategy");
        if (strat === "auto") this.setShadowDebugPaintStrategy(null);
        else if (strat === "A" || strat === "B" || strat === "C" || strat === "BC") {
          this.setShadowDebugPaintStrategy(strat);
        }
      }, true);
      document.body.appendChild(hud);
      this._shadowDebugHud = hud;
      this._refreshShadowDebugHudButtons();
      return hud;
    }
    _refreshShadowDebugHudButtons() {
      const hud = this._shadowDebugHud;
      if (!hud) return;
      const active2 = this._shadowDebugPaintOverride;
      try {
        hud.querySelectorAll("button[data-kp-shadow-dbg-strategy]").forEach((btn) => {
          const key = btn.getAttribute("data-kp-shadow-dbg-strategy");
          const isOn = key === "auto" && !active2 || key === active2;
          btn.style.background = isOn ? "#2f6b4a" : "#1a2820";
          btn.style.borderColor = isOn ? "#7CFFB2" : "#3d5a48";
          btn.style.color = isOn ? "#eafff2" : "#cfe";
          btn.style.fontWeight = isOn ? "700" : "400";
        });
      } catch {
      }
    }
    /**
     * Refresh leaf line after sticky-host pointer moves (focusEl/paint unchanged).
     * Called from IntersectionObserverManager — does not re-run A/B/C paint.
     */
    refreshShadowRootDebugHudLeaf() {
      if (!this._shadowDebugHudEnabled) return;
      const last = this._shadowDebugLastInfo;
      let focusEl = last?.focus || null;
      try {
        if (!focusEl || !focusEl.isConnected) {
          focusEl = window.keyPilot?.state?.getState?.()?.focusEl || window.keyPilot?.intersectionManager?.getDomHoveredElement?.() || null;
        }
      } catch {
        focusEl = last?.focus || null;
      }
      let paintEl = last?.paint || null;
      if (focusEl && (!paintEl || !paintEl.isConnected)) {
        try {
          paintEl = this._resolveElementForFocusStyling(focusEl) || focusEl;
        } catch {
          paintEl = focusEl;
        }
      }
      let inShadow = !!last?.inShadow;
      try {
        if (focusEl) inShadow = this._isInShadowTree(focusEl);
      } catch {
      }
      this._updateShadowRootDebugHud(focusEl, paintEl, {
        inShadow,
        autoStrategy: last?.auto || "\u2014",
        appliedStrategy: last?.applied || "\u2014",
        override: this._shadowDebugPaintOverride
      });
    }
    /**
     * Refresh HUD text from current hover / paint decision.
     * @param {Element|null} focusEl
     * @param {Element|null} paintEl
     * @param {{ inShadow: boolean, autoStrategy: string, appliedStrategy: string, override: string|null }} meta
     */
    _updateShadowRootDebugHud(focusEl, paintEl, meta) {
      if (!this._shadowDebugHudEnabled) return;
      const hud = this._ensureShadowRootDebugHud();
      if (!hud) return;
      let leaf = null;
      try {
        leaf = window.keyPilot?.intersectionManager?.getDomHoverLeaf?.() || window.__KP_HOVER_LEAF || null;
      } catch {
        leaf = null;
      }
      const leafDesc = this._describeElForShadowDebug(leaf);
      const focusDesc = this._describeElForShadowDebug(focusEl);
      const paintDesc = this._describeElForShadowDebug(paintEl);
      const override = meta?.override || null;
      const autoStrategy = meta?.autoStrategy || "\u2014";
      const applied = meta?.appliedStrategy || "\u2014";
      const inShadow = !!meta?.inShadow;
      this._shadowDebugLastInfo = {
        leaf,
        focus: focusEl,
        paint: paintEl,
        auto: autoStrategy,
        applied,
        inShadow
      };
      const body = hud.querySelector("[data-kp-shadow-dbg-body]");
      if (!body) return;
      const paintSame = focusEl && paintEl && focusEl === paintEl ? "same as focus" : paintEl ? "resolved (may pierce shadow)" : "\u2014";
      const overrideLabel = override === "BC" ? "Auto B\u2192C" : override ? `forced ${override}` : null;
      body.textContent = `Leaf (under pointer)
  ${leafDesc.line}
  ${leafDesc.detail || "\u2014"}

Hover target (focusEl / F-activate)
  ${focusDesc.line}
  ${focusDesc.detail || "\u2014"}

Paint target (outline box)
  ${paintDesc.line}
  ${paintDesc.detail || "\u2014"}
  (${paintSame})

Strategy
  focus in shadow: ${inShadow ? "YES" : "no"}
  auto would use:  ${autoStrategy}
  applied now:     ${applied}${overrideLabel ? `  (${overrideLabel})` : "  (auto)"}`;
      this._refreshShadowDebugHudButtons();
    }
  };
  installFocusOverlayPainter(OverlayManager);

  // src/modules/style-manager.js
  init_constants();
  var BLUE_TINT_FILTER_ID = "keypilot-blue-tint-filter";
  var BLUE_TINT_SVG_ID = "keypilot-blue-tint-filter-svg";
  function buildKeyPilotPrintCss() {
    const hide = [
      // Cursor + style filter host
      `#${ELEMENT_IDS.CURSOR}`,
      `#${BLUE_TINT_SVG_ID}`,
      // OverlayManager backends
      `.${CSS_CLASSES.CANVAS_OVERLAY}`,
      `.${CSS_CLASSES.CSS_PROPS_OVERLAY}`,
      `.${CSS_CLASSES.FOCUS_OVERLAY}`,
      `.${CSS_CLASSES.DELETE_OVERLAY}`,
      `.${CSS_CLASSES.INSPECTOR_OVERLAY}`,
      `.${CSS_CLASSES.INSPECTOR_MODE_INDICATOR}`,
      `.${CSS_CLASSES.COLS_OVERLAY}`,
      `.${CSS_CLASSES.COLS_SLIP_BAR}`,
      `.${CSS_CLASSES.HIGHLIGHT_OVERLAY}`,
      `.${CSS_CLASSES.HIGHLIGHT_SELECTION}`,
      `.${CSS_CLASSES.UNIT_SELECT_OVERLAY}`,
      `.${CSS_CLASSES.RIPPLE}`,
      `.${CSS_CLASSES.FOCUS_PULSE}`,
      `.${CSS_CLASSES.FOCUS_FLASH}`,
      `.${CSS_CLASSES.FOCUS_DASH}`,
      `.${CSS_CLASSES.FOCUS_DASH_DENIED}`,
      `.${CSS_CLASSES.FOCUS_MARQUEE}`,
      `.${CSS_CLASSES.IMAGE_COPY_PULSE}`,
      `.${CSS_CLASSES.EDGE_JUMP_FADE}`,
      `.${CSS_CLASSES.VIEWPORT_MODAL_FRAME}`,
      `.${CSS_CLASSES.ESC_EXIT_LABEL}`,
      `.${CSS_CLASSES.TEXT_FOCUS_ESC_HINT}`,
      `.${CSS_CLASSES.TEXT_HOVER_ACTIVATE_HINT}`,
      // Omnibox + modal chrome
      `.${CSS_CLASSES.OMNIBOX_BACKDROP}`,
      `.${CSS_CLASSES.OMNIBOX_PANEL}`,
      `.${CSS_CLASSES.POPUP_BACKDROP}`,
      // Other injected chrome (hard-coded class names in modules)
      ".kpv2-toggle-notification",
      ".kpv2-flash-notification",
      ".kpv2-tab-history-panel",
      ".kpv2-popover-container",
      ".kpv2-preview-popover-container",
      ".kpv2-page-media-overlay",
      "#kpv2-debug-panel",
      "#kpv2-shadow-debug-hud",
      ".kpv2-shadow-debug-hud",
      "#kpv2-rectangle-intersection-root",
      // Light-DOM UI roots (kp-*)
      ".kp-control-strip",
      ".kp-floating-keyboard-help",
      ".kp-select-menu-host",
      ".kp-select-menu",
      ".kpv2-top-sites-panel",
      ".kp-onboarding-panel",
      ".kp-launcher-container",
      ".kp-keybindings-popover",
      // Early-inject / attribute markers
      "[data-kp-control-strip]",
      "[data-kp-early-control-strip]",
      "[data-kp-early-floating-keyboard]",
      "[data-kp-ephemeral-effect]"
    ].join(",\n    ");
    return `
      /* Hide KeyPilot chrome + neutralize host decorations when printing / saving as PDF.
         Placed last so !important print rules beat earlier screen !important rules. */
      @media print {
        ${hide} {
          display: none !important;
          visibility: hidden !important;
        }

        /* Tier B: host-page hover / mode decorations \u2014 reset paint, keep the element. */
        .keypilot-focus-element,
        [data-kp-focus],
        .${CSS_CLASSES.FOCUS},
        .${CSS_CLASSES.DELETE},
        .${CSS_CLASSES.COLS},
        .${CSS_CLASSES.INSPECTOR},
        .${CSS_CLASSES.HIGHLIGHT} {
          outline: none !important;
          outline-offset: 0 !important;
          box-shadow: none !important;
          filter: none !important;
        }

        .keypilot-focus-element,
        [data-kp-focus="1"] {
          background: transparent !important;
        }

        .${CSS_CLASSES.TEXT_HOVER_INPUT},
        .${CSS_CLASSES.TEXT_HOVER_INPUT_PARENT},
        .${CSS_CLASSES.TEXT_FOCUS_INPUT},
        .${CSS_CLASSES.TEXT_FOCUS_INPUT_PARENT} {
          background-image: none !important;
          background-color: revert !important;
          color: revert !important;
          text-shadow: none !important;
          caret-color: auto !important;
          filter: none !important;
          box-shadow: none !important;
          outline: none !important;
        }

        .${CSS_CLASSES.TEXT_HOVER_INPUT}::placeholder,
        .${CSS_CLASSES.TEXT_FOCUS_INPUT}::placeholder {
          color: revert !important;
          text-shadow: none !important;
        }
      }
  `;
  }
  function buildTextInputHintDataUri(text, opts = {}) {
    const raw = String(text || "").trim() || " ";
    const fill = String(opts.fill || "rgba(90, 45, 0, 0.78)");
    const stroke = String(opts.stroke || "rgba(255,255,255,0.55)");
    const fontSize = Number.isFinite(opts.fontSize) ? opts.fontSize : 11;
    const safe = raw.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
    const width = Math.max(48, Math.ceil(raw.length * fontSize * 0.58) + 10);
    const height = fontSize + 5;
    const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}"><text x="0" y="${fontSize}" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif" font-size="${fontSize}" font-weight="600" fill="${fill}" paint-order="stroke" stroke="${stroke}" stroke-width="2.5">${safe}</text></svg>`;
    return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
  }
  var StyleManager = class {
    constructor() {
      this.injectedStyles = /* @__PURE__ */ new Set();
      this.shadowRootStyles = /* @__PURE__ */ new Map();
      this._foreignDocuments = /* @__PURE__ */ new Set();
      this.isEnabled = true;
      this.cursorOverridesEnabled = false;
      this._textHoverHintLabel = "F to select";
      this._textFocusHintLabel = "press Esc to exit";
      this._textHoverHintUri = buildTextInputHintDataUri(this._textHoverHintLabel);
      this._textFocusHintUri = buildTextInputHintDataUri(this._textFocusHintLabel, {
        fill: COLORS.ORANGE,
        stroke: "#000"
      });
    }
    /**
     * Update layout-aware hint copy and push CSS variables onto the document.
     * @param {{ hover?: string, focus?: string }} labels
     */
    setTextInputHintLabels(labels = {}) {
      if (typeof labels.hover === "string" && labels.hover.trim()) {
        this._textHoverHintLabel = labels.hover.trim();
        this._textHoverHintUri = buildTextInputHintDataUri(this._textHoverHintLabel);
      }
      if (typeof labels.focus === "string" && labels.focus.trim()) {
        this._textFocusHintLabel = labels.focus.trim();
        this._textFocusHintUri = buildTextInputHintDataUri(this._textFocusHintLabel, {
          fill: COLORS.ORANGE,
          stroke: "#000"
        });
      }
      this._applyTextInputHintCssVars();
    }
    /**
     * Write hint background-image CSS vars on documentElement (and any tracked shadow roots).
     */
    _applyTextInputHintCssVars() {
      const hoverUri = this._textHoverHintUri || "none";
      const focusUri = this._textFocusHintUri || "none";
      try {
        document.documentElement.style.setProperty("--kpv2-text-hover-hint-image", hoverUri);
        document.documentElement.style.setProperty("--kpv2-text-focus-hint-image", focusUri);
      } catch {
      }
      try {
        for (const doc of this._foreignDocuments) {
          if (!doc || doc === document) continue;
          let alive = false;
          try {
            alive = !!doc.documentElement && doc.defaultView != null;
          } catch {
            alive = false;
          }
          if (!alive) {
            this._foreignDocuments.delete(doc);
            continue;
          }
          try {
            doc.documentElement.style.setProperty("--kpv2-text-hover-hint-image", hoverUri);
            doc.documentElement.style.setProperty("--kpv2-text-focus-hint-image", focusUri);
          } catch {
          }
        }
      } catch {
      }
      try {
        for (const shadowRoot of this.shadowRootStyles.keys()) {
          const host = shadowRoot?.host;
          if (host && host.style) {
            host.style.setProperty("--kpv2-text-hover-hint-image", hoverUri);
            host.style.setProperty("--kpv2-text-focus-hint-image", focusUri);
          }
        }
      } catch {
      }
    }
    setCursorOverridesEnabled(enabled) {
      const next = !!enabled;
      if (this.cursorOverridesEnabled === next) return;
      this.cursorOverridesEnabled = next;
      try {
        if (this.cursorOverridesEnabled) {
          document.documentElement.classList.add(CSS_CLASSES.CURSOR_HIDDEN);
        } else {
          document.documentElement.classList.remove(CSS_CLASSES.CURSOR_HIDDEN);
        }
      } catch {
      }
      try {
        const mainStyle = document.getElementById(ELEMENT_IDS.STYLE);
        if (mainStyle && typeof mainStyle.textContent === "string") {
          mainStyle.textContent = this._buildMainCSS();
        }
      } catch {
      }
      try {
        for (const [shadowRoot, styleEl] of this.shadowRootStyles) {
          if (!shadowRoot || !styleEl) continue;
          try {
            styleEl.textContent = this._buildShadowCSS();
          } catch {
          }
        }
      } catch {
      }
    }
    _buildMainCSS() {
      const cursorCSS = this.cursorOverridesEnabled ? `
      html.${CSS_CLASSES.CURSOR_HIDDEN} * {
        cursor: var(--kpv2-cursor, auto) !important;
      }

      /* Ensure cursor overrides work on all interactive elements */
      html.${CSS_CLASSES.CURSOR_HIDDEN} a[href],
      html.${CSS_CLASSES.CURSOR_HIDDEN} button,
      html.${CSS_CLASSES.CURSOR_HIDDEN} input,
      html.${CSS_CLASSES.CURSOR_HIDDEN} select,
      html.${CSS_CLASSES.CURSOR_HIDDEN} textarea,
      html.${CSS_CLASSES.CURSOR_HIDDEN} [role="button"],
      html.${CSS_CLASSES.CURSOR_HIDDEN} [role="link"],
      html.${CSS_CLASSES.CURSOR_HIDDEN} [onclick],
      html.${CSS_CLASSES.CURSOR_HIDDEN} [tabindex] {
        cursor: var(--kpv2-cursor, auto) !important;
      }
      ` : "";
      return `
      ${cursorCSS}
      
      .${CSS_CLASSES.FOCUS} { 
        filter: brightness(1.2) !important; 
      }
      
      .${CSS_CLASSES.DELETE} { 
        filter: brightness(0.8) contrast(1.2) !important; 
      }

      .${CSS_CLASSES.COLS} {
        filter: brightness(1.05) saturate(1.15) !important;
      }
      
      .${CSS_CLASSES.HIDDEN} { 
        display: none !important; 
      }

      .${CSS_CLASSES.EDGE_JUMP_FADE} {
        position: fixed;
        left: 0;
        top: 0;
        width: 0;
        height: 0;
        box-sizing: border-box;
        z-index: ${Z_INDEX.EDGE_JUMP_FADE};
        pointer-events: none;
        opacity: 0;
        background: #fff;
        overflow: hidden;
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        will-change: opacity, backdrop-filter, -webkit-backdrop-filter, background-color, left, top, width, height;
      }

      .${CSS_CLASSES.EDGE_JUMP_FADE_ICON} {
        position: absolute;
        left: 100px;
        width: 56px;
        height: 56px;
        pointer-events: none;
        filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.28));
      }

      .${CSS_CLASSES.EDGE_JUMP_FADE_ICON}[data-kp-edge="top"] {
        top: 100px;
        bottom: auto;
      }

      .${CSS_CLASSES.EDGE_JUMP_FADE_ICON}[data-kp-edge="bottom"] {
        bottom: 100px;
        top: auto;
      }
      
      @keyframes kpv2-ripple { 
        0% { transform: translate(-50%, -50%) scale(0.25); opacity: 0.35; }
        60% { transform: translate(-50%, -50%) scale(1); opacity: 0.2; }
        100% { transform: translate(-50%, -50%) scale(1.6); opacity: 0; }
      }

      /* F-key activation: outline scales up and fades out from the click target. */
      @keyframes kpv2-focus-pulse {
        0% {
          transform: scale(1);
          opacity: 1;
        }
        55% {
          transform: scale(1.12);
          opacity: 0.85;
        }
        100% {
          transform: scale(1.28);
          opacity: 0;
        }
      }

      /* F-key activation: hard strobe on the outline (minimal motion). */
      @keyframes kpv2-focus-flash {
        0% {
          opacity: 1;
          border-color: #ffffff;
          box-shadow:
            0 0 0 1px ${COLORS.FLASH_GREEN},
            0 0 8px 1px ${COLORS.FLASH_GREEN_GLOW};
        }
        40% {
          opacity: 1;
          border-color: ${COLORS.FLASH_GREEN};
          box-shadow:
            0 0 0 1px ${COLORS.FLASH_GREEN_SHADOW},
            0 0 5px 0 ${COLORS.FLASH_GREEN_GLOW};
        }
        100% {
          opacity: 0;
          border-color: ${COLORS.FLASH_GREEN};
          box-shadow:
            0 0 0 0 transparent,
            0 0 0 0 transparent;
        }
      }

      /* F-key activation: marquee chaser travels once around the perimeter, then fades. */
      @keyframes kpv2-focus-marquee-spin {
        0% {
          transform: translate(-50%, -50%) rotate(0deg);
          opacity: 1;
        }
        78% {
          opacity: 1;
        }
        100% {
          transform: translate(-50%, -50%) rotate(360deg);
          opacity: 0;
        }
      }

      @keyframes kpv2-focus-marquee-fade {
        0%, 70% {
          opacity: 1;
        }
        100% {
          opacity: 0;
        }
      }

      /* F-key activation: dashed stroke marches around the rect perimeter. */
      @keyframes kpv2-focus-dash-chase {
        0% {
          stroke-dashoffset: 0;
          opacity: 1;
        }
        78% {
          opacity: 1;
        }
        100% {
          stroke-dashoffset: calc(-1 * var(--kp-dash-peri, 200));
          opacity: 0;
        }
      }

      /*
       * Image copy (I-key): shutter flash \u2192 slight pop \u2192 shrink away
       * (read as "captured / pocketed" rather than the F-click expand-out pulse).
       */
      @keyframes kpv2-image-copy-pulse {
        0% {
          transform: scale(0.98);
          opacity: 0;
          background-color: ${COLORS.IMAGE_COPY_FLASH};
        }
        14% {
          transform: scale(1);
          opacity: 1;
          background-color: ${COLORS.IMAGE_COPY_FLASH};
        }
        32% {
          transform: scale(1.06);
          opacity: 1;
          background-color: ${COLORS.IMAGE_COPY_FILL};
        }
        100% {
          transform: scale(0.58);
          opacity: 0;
          background-color: ${COLORS.IMAGE_COPY_FILL};
        }
      }
      
      .${CSS_CLASSES.RIPPLE} { 
        position: fixed; 
        left: 0; 
        top: 0; 
        z-index: ${Z_INDEX.RIPPLE}; 
        pointer-events: none; 
        width: 46px; 
        height: 46px; 
        border-radius: 50%; 
        background: radial-gradient(circle, ${COLORS.RIPPLE_GREEN} 0%, ${COLORS.RIPPLE_GREEN_MID} 60%, ${COLORS.RIPPLE_GREEN_TRANSPARENT} 70%); 
        animation: kpv2-ripple 420ms ease-out forwards; 
      }

      .${CSS_CLASSES.FOCUS_PULSE} {
        position: fixed;
        left: 0;
        top: 0;
        pointer-events: none;
        z-index: ${Z_INDEX.OVERLAYS_ABOVE};
        box-sizing: border-box;
        border: 3px solid ${COLORS.FLASH_GREEN};
        border-radius: 0;
        box-shadow:
          0 0 0 2px ${COLORS.FLASH_GREEN_SHADOW},
          0 0 18px 4px ${COLORS.FLASH_GREEN_GLOW};
        background: transparent;
        transform-origin: center center;
        will-change: transform, opacity;
        animation: kpv2-focus-pulse 420ms cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
      }

      /* Flash / strobe: brief bright border + glow, almost no geometry motion. */
      .${CSS_CLASSES.FOCUS_FLASH} {
        position: fixed;
        left: 0;
        top: 0;
        pointer-events: none;
        z-index: ${Z_INDEX.OVERLAYS_ABOVE};
        box-sizing: border-box;
        border: 3px solid ${COLORS.FLASH_GREEN};
        border-radius: 0;
        background: transparent;
        will-change: opacity, box-shadow, border-color;
        animation: kpv2-focus-flash 320ms ease-out forwards;
      }

      /* Dash chase: SVG host; stroke animation lives on the child rect. */
      .${CSS_CLASSES.FOCUS_DASH} {
        position: fixed;
        left: 0;
        top: 0;
        pointer-events: none;
        z-index: ${Z_INDEX.OVERLAYS_ABOVE};
        overflow: visible;
        filter: drop-shadow(0 0 6px ${COLORS.FLASH_GREEN_GLOW});
      }

      .${CSS_CLASSES.FOCUS_DASH}-stroke {
        will-change: stroke-dashoffset, opacity;
        animation: kpv2-focus-dash-chase 720ms linear forwards;
      }

      @keyframes kpv2-focus-dash-denied {
        0% { opacity: 1; }
        32% { opacity: 0.12; }
        52% { opacity: 1; }
        100% { opacity: 0; }
      }

      .${CSS_CLASSES.FOCUS_DASH_DENIED} {
        position: fixed;
        left: 0;
        top: 0;
        pointer-events: none;
        z-index: ${Z_INDEX.OVERLAYS_ABOVE};
        box-sizing: border-box;
        border: 3px dashed ${COLORS.FLASH_DENIED};
        border-radius: 0;
        background: transparent;
        box-shadow:
          0 0 0 1px ${COLORS.FLASH_DENIED_SHADOW},
          0 0 10px 1px ${COLORS.FLASH_DENIED_GLOW};
        will-change: opacity;
        animation: kpv2-focus-dash-denied 480ms ease-out forwards;
      }

      /*
       * Marquee click effect: a bright segment races around the element border
       * (theater marquee / runway lights). Mask keeps only a thin ring visible.
       */
      .${CSS_CLASSES.FOCUS_MARQUEE} {
        position: fixed;
        left: 0;
        top: 0;
        pointer-events: none;
        z-index: ${Z_INDEX.OVERLAYS_ABOVE};
        box-sizing: border-box;
        border-radius: 0;
        overflow: hidden;
        padding: 3px;
        background: transparent;
        /* Ring-only mask: paint border, punch out the interior. */
        -webkit-mask:
          linear-gradient(#fff 0 0) content-box,
          linear-gradient(#fff 0 0);
        -webkit-mask-composite: xor;
        mask:
          linear-gradient(#fff 0 0) content-box,
          linear-gradient(#fff 0 0);
        mask-composite: exclude;
        animation: kpv2-focus-marquee-fade 720ms ease-out forwards;
      }

      .${CSS_CLASSES.FOCUS_MARQUEE}::before {
        content: '';
        position: absolute;
        top: 50%;
        left: 50%;
        width: 220%;
        height: 220%;
        transform: translate(-50%, -50%) rotate(0deg);
        background: conic-gradient(
          from 0deg,
          transparent 0deg,
          transparent 250deg,
          ${COLORS.FLASH_GREEN_SHADOW} 280deg,
          ${COLORS.FLASH_GREEN} 310deg,
          #ffffff 328deg,
          ${COLORS.FLASH_GREEN} 342deg,
          transparent 360deg
        );
        box-shadow: 0 0 14px 2px ${COLORS.FLASH_GREEN_GLOW};
        will-change: transform, opacity;
        animation: kpv2-focus-marquee-spin 720ms linear forwards;
      }

      .${CSS_CLASSES.IMAGE_COPY_PULSE} {
        position: fixed;
        left: 0;
        top: 0;
        pointer-events: none;
        z-index: ${Z_INDEX.OVERLAYS_ABOVE};
        box-sizing: border-box;
        border: 3px solid ${COLORS.IMAGE_COPY_FRAME};
        border-radius: 6px;
        box-shadow:
          0 0 0 2px ${COLORS.IMAGE_COPY_FRAME_SHADOW},
          0 0 22px 6px ${COLORS.IMAGE_COPY_FRAME_GLOW};
        background-color: ${COLORS.IMAGE_COPY_FILL};
        transform-origin: center center;
        will-change: transform, opacity, background-color;
        animation: kpv2-image-copy-pulse 520ms cubic-bezier(0.2, 0.75, 0.25, 1) forwards;
      }
      
      .${CSS_CLASSES.FOCUS_OVERLAY} { 
        position: fixed; 
        pointer-events: none; 
        z-index: ${Z_INDEX.OVERLAYS}; 
        border: 3px solid var(--keypilot-focus-ring-color, ${COLORS.FOCUS_BLUE}); 
        box-shadow: 0 0 0 2px var(--keypilot-focus-ring-shadow, ${COLORS.BLUE_SHADOW}), 0 0 10px 2px var(--keypilot-focus-ring-shadow-bright, ${COLORS.BLUE_SHADOW_BRIGHT}); 
        background: transparent; 
      }
      
      .${CSS_CLASSES.DELETE_OVERLAY} { 
        position: fixed; 
        pointer-events: none; 
        z-index: ${Z_INDEX.OVERLAYS}; 
        border: 3px solid ${COLORS.DELETE_RED}; 
        box-shadow: 0 0 0 2px ${COLORS.DELETE_SHADOW}, 0 0 12px 2px ${COLORS.DELETE_SHADOW_BRIGHT}; 
        background: transparent; 
      }

      .${CSS_CLASSES.COLS_OVERLAY},
      .${CSS_CLASSES.INSPECTOR_OVERLAY} {
        position: fixed;
        pointer-events: none;
        z-index: ${Z_INDEX.OVERLAYS};
        border: 3px solid ${COLORS.COLS_PURPLE};
        box-shadow: 0 0 0 2px ${COLORS.COLS_SHADOW}, 0 0 12px 2px ${COLORS.COLS_SHADOW_BRIGHT};
        background: transparent;
      }

      .${CSS_CLASSES.INSPECTOR} {
        filter: brightness(1.05) saturate(1.1) !important;
      }

      /* Applied multicol target \u2014 variables set by ColumnLayoutManager */
      .${CSS_CLASSES.COLS_ACTIVE} {
        column-width: var(--kpv2-cols-width, 40ch) !important;
        column-gap: var(--kpv2-cols-gap, 1.5rem) !important;
        column-fill: auto !important;
        height: var(--kpv2-cols-height, 100vh) !important;
        max-height: var(--kpv2-cols-height, 100vh) !important;
        overflow-x: auto !important;
        overflow-y: hidden !important;
        box-sizing: border-box !important;
        width: 100% !important;
        max-width: 100% !important;
        scrollbar-width: none !important;
      }
      .${CSS_CLASSES.COLS_ACTIVE}::-webkit-scrollbar {
        display: none !important;
        width: 0 !important;
        height: 0 !important;
      }

      /* Column widget shell (outline + integrated slip bar) */
      .${CSS_CLASSES.COLS_SHELL} {
        box-sizing: border-box;
      }
      .${CSS_CLASSES.COLS_BODY} {
        min-width: 0;
        min-height: 0;
      }
      .${CSS_CLASSES.COLS_SLIP_BAR} {
        box-sizing: border-box;
      }
      .${CSS_CLASSES.COLS_SLIP_BAR}[data-kp-slip-empty="1"] .${CSS_CLASSES.COLS_SLIP_TRACK} {
        opacity: 0.45;
      }
      .${CSS_CLASSES.COLS_EXPAND_BTN}:focus-visible,
      .${CSS_CLASSES.COLS_CLOSE_BTN}:focus-visible {
        outline: 2px solid ${COLORS.COLS_PURPLE_BRIGHT};
        outline-offset: 1px;
      }

      /* Page-mode markers (html/body): critical layout is set inline by ColumnLayoutManager. */
      
      .${CSS_CLASSES.HIGHLIGHT_OVERLAY} { 
        position: fixed; 
        pointer-events: none; 
        z-index: ${Z_INDEX.OVERLAYS}; 
        border: 3px solid ${COLORS.HIGHLIGHT_BLUE}; 
        box-shadow: 0 0 0 2px ${COLORS.HIGHLIGHT_SHADOW}, 0 0 12px 2px ${COLORS.HIGHLIGHT_SHADOW_BRIGHT}; 
        background: transparent; 
      }
      
      .${CSS_CLASSES.HIGHLIGHT_SELECTION} { 
        position: fixed; 
        pointer-events: none; 
        z-index: ${Z_INDEX.HIGHLIGHT_SELECTION}; 
        background: ${COLORS.HIGHLIGHT_SELECTION_BG}; 
        border: 1px solid ${COLORS.HIGHLIGHT_SELECTION_BORDER}; 
      }

      .${CSS_CLASSES.UNIT_SELECT_OVERLAY} {
        position: fixed;
        pointer-events: none;
        z-index: ${Z_INDEX.HIGHLIGHT_SELECTION};
        background: ${COLORS.HIGHLIGHT_SELECTION_BG};
        border: 1px solid ${COLORS.HIGHLIGHT_SELECTION_BORDER};
        box-sizing: border-box;
      }

      ::highlight(kp-unit-select) {
        background-color: ${COLORS.HIGHLIGHT_SELECTION_BG};
      }

      /* Omnibox overlay */
      .${CSS_CLASSES.OMNIBOX_BACKDROP} {
        position: fixed;
        inset: 0;
        z-index: ${Z_INDEX.OMNIBOX};
        background: rgba(0,0,0,0.35);
        backdrop-filter: blur(6px);
        -webkit-backdrop-filter: blur(6px);
      }
      .${CSS_CLASSES.OMNIBOX_PANEL} {
        position: absolute;
        top: 18vh;
        left: 50%;
        transform: translateX(-50%);
        width: min(880px, calc(100vw - 32px));
        border-radius: 14px;
        border: 1px solid ${COLORS.ORANGE_BORDER};
        box-shadow: 0 18px 60px rgba(0,0,0,0.55);
        background: rgba(20, 20, 20, 0.88);
        overflow: hidden;
      }
      .${CSS_CLASSES.OMNIBOX_INPUT} {
        width: 100%;
        box-sizing: border-box;
        border: none;
        outline: none;
        padding: 14px 16px;
        font-size: 18px;
        font-weight: 500;
        color: ${COLORS.TEXT_WHITE_PRIMARY};
        background: rgba(0,0,0,0);
        font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
      }
      .${CSS_CLASSES.OMNIBOX_SUGGESTIONS} {
        border-top: 1px solid rgba(255,140,0,0.15);
        max-height: 40vh;
        overflow-y: auto;
      }
      .${CSS_CLASSES.OMNIBOX_SUGGESTION} {
        padding: 10px 16px;
        cursor: default;
      }
      .${CSS_CLASSES.OMNIBOX_EMPTY} {
        padding: 10px 16px;
        font-size: 13px;
        color: ${COLORS.TEXT_WHITE_SECONDARY};
        font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
      }
      
      #${ELEMENT_IDS.CURSOR} { 
        position: fixed !important; 
        left: var(--cursor-x, 0) !important; 
        top: var(--cursor-y, 0) !important; 
        transform: translate(-50%, -50%) !important; 
        z-index: ${Z_INDEX.CURSOR} !important; 
        pointer-events: none !important;
        display: block !important;
        visibility: visible !important;
        will-change: transform, left, top !important;
      }
      
      .${CSS_CLASSES.VIEWPORT_MODAL_FRAME} {
        position: fixed;
        top: 0;
        left: 0;
        width: 100vw;
        height: 100vh;
        border: 9pt solid ${COLORS.ORANGE};
        opacity: 0.7;
        pointer-events: none;
        z-index: ${Z_INDEX.VIEWPORT_MODAL_FRAME};
        box-sizing: border-box;
        will-change: transform;
      }
      
      @keyframes kpv2-pulse { 
        0% { opacity: 0.7; }
        50% { opacity: 1; }
        100% { opacity: 0.7; }
      }

      /* Text-mode focus: inset left-edge bar pulse (width via --kpv2-text-left-edge-width). */
      @keyframes kpv2-text-left-edge-pulse {
        0%, 100% {
          box-shadow: inset var(--kpv2-text-left-edge-width, 5px) 0 0 0 ${COLORS.ORANGE_SHADOW};
        }
        50% {
          box-shadow: inset var(--kpv2-text-left-edge-width, 5px) 0 0 0 ${COLORS.ORANGE};
        }
      }
      
      .${CSS_CLASSES.ESC_EXIT_LABEL} {
        position: fixed;
        pointer-events: none;
        z-index: ${Z_INDEX.OVERLAYS_ABOVE};
        background: ${COLORS.ORANGE};
        color: white;
        padding: 4px 8px;
        font-size: 12px;
        font-weight: bold;
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        white-space: nowrap;
        border-radius: 2px;
        animation: kpv2-pulse 1.5s ease-in-out infinite;
        will-change: transform, opacity;
      }

      .${CSS_CLASSES.ESC_EXIT_LABEL} kbd {
        background: rgba(255, 255, 255, 0.2);
        border: 1px solid rgba(255, 255, 255, 0.3);
        border-radius: 2px;
        padding: 1px 3px;
        font-family: monospace;
        font-size: 11px;
        font-weight: bold;
        color: white;
      }

      .${CSS_CLASSES.TEXT_FOCUS_ESC_HINT},
      .${CSS_CLASSES.TEXT_HOVER_ACTIVATE_HINT} {
        position: fixed;
        pointer-events: none;
        z-index: ${Z_INDEX.OVERLAYS_ABOVE};
        display: flex;
        flex-direction: column;
        align-items: flex-end;
        justify-content: center;
        gap: 1px;
        margin: 0;
        padding: 0;
        background: none;
        border: none;
        color: ${COLORS.ORANGE};
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        font-size: 10px;
        font-weight: 600;
        line-height: 1.15;
        letter-spacing: 0.02em;
        text-align: right;
        text-shadow: 0 0 3px #000, 0 1px 1px #000;
        white-space: nowrap;
        will-change: transform;
      }

      .${CSS_CLASSES.TEXT_FOCUS_ESC_HINT} kbd,
      .${CSS_CLASSES.TEXT_HOVER_ACTIVATE_HINT} kbd {
        display: inline-block;
        background: rgba(0, 0, 0, 0.62);
        border: 1px solid ${COLORS.ORANGE};
        border-bottom-width: 2px;
        border-radius: 3px;
        padding: 0 3px;
        font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
        font-size: 10px;
        font-weight: 700;
        line-height: 1.3;
        color: ${COLORS.ORANGE};
        text-shadow: none;
      }

      .${CSS_CLASSES.ESC_EXIT_LABEL} .countdown-number {
        background: rgba(0, 0, 0, 0.8);
        color: white;
        padding: 1px 3px;
        border-radius: 2px;
        font-weight: bold;
        font-size: 11px;
      }
      
      /* Add left padding to focused text inputs */
      input:focus,
      textarea:focus,
      [contenteditable="true"]:focus,
      [contenteditable=""]:focus {
        padding-left: 5pt !important;
      }

      /* Text inputs: hover / focus treatment + hint labels.
         - Hover: orange outline only + vertical \u201CF / to / select\u201D sidecar
         - Focus background_tint: full orange wash
         - Focus left_edge (default): pulsating left inset bar
         - Text mode: vertical \u201CEsc / to / exit\u201D overlay sits left of the orange bar
         Do not override the field\u2019s own color / text-shadow / caret / placeholder. */
      /* Background-tint focus parents get a wash; left-edge parents stay clean. */
      .${CSS_CLASSES.TEXT_FOCUS_INPUT_PARENT}:not(.${CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE}) {
        background-color: var(--kpv2-text-input-focus-bg, rgba(255, 140, 0, 0.42)) !important;
      }

      /* Hover: marker class only \u2014 hint is a sidecar overlay (no in-field SVG). */
      .${CSS_CLASSES.TEXT_HOVER_INPUT} {
        background-image: none !important;
      }

      /* Focus (background tint style): wash only \u2014 Esc hint is a sidecar overlay. */
      .${CSS_CLASSES.TEXT_FOCUS_INPUT}:not(.${CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE}):not(.${CSS_CLASSES.TEXT_FOCUS_DELEGATED}) {
        background-color: var(--kpv2-text-input-focus-bg, rgba(255, 140, 0, 0.42)) !important;
      }

      /* Field chrome painted on a taller wrapper \u2014 keep the input itself clean. */
      .${CSS_CLASSES.TEXT_FOCUS_INPUT}.${CSS_CLASSES.TEXT_FOCUS_DELEGATED} {
        background-color: transparent !important;
        background-image: none !important;
        box-shadow: none !important;
        animation: none !important;
      }

      /* Left-edge bar on the visual shell (Gmail pill, etc.), not only the <input>. */
      .${CSS_CLASSES.TEXT_FOCUS_INPUT_PARENT}.${CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE} {
        box-shadow: inset var(--kpv2-text-left-edge-width, 5px) 0 0 0 ${COLORS.ORANGE} !important;
        animation: kpv2-text-left-edge-pulse 1.5s ease-in-out infinite !important;
        will-change: box-shadow;
      }

      /* Focus (default left-edge style): inset orange bar that pulses. */
      .${CSS_CLASSES.TEXT_FOCUS_INPUT}.${CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE} {
        padding-left: calc(var(--kpv2-text-left-edge-width, 5px) + 4px) !important;
        background-color: transparent !important;
        background-image: none !important;
        box-shadow: inset var(--kpv2-text-left-edge-width, 5px) 0 0 0 ${COLORS.ORANGE} !important;
        animation: kpv2-text-left-edge-pulse 1.5s ease-in-out infinite !important;
        will-change: box-shadow;
      }

      /* Short fields: keep focus chrome, hide the Esc SVG (it clips / overlaps text). */
      .${CSS_CLASSES.TEXT_FOCUS_INPUT}.${CSS_CLASSES.TEXT_FOCUS_HINT_HIDDEN} {
        background-image: none !important;
      }

      /*
       * Element styling for DOM hover mode.
       * Prefer [data-kp-focus] over class alone: SPAs (e.g. X) often reconcile
       * className on hover and strip unknown classes, while data-* attributes
       * usually survive. Keep the class for backwards compatibility / DevTools.
       * Outline-first \u2014 many sites transition/zero box-shadow.
       */
      .keypilot-focus-element,
      [data-kp-focus="1"] {
        outline: var(--keypilot-focus-ring-width, 3px) var(--keypilot-focus-outline-style, solid) var(--keypilot-focus-ring-color, #2196f3) !important;
        /* Graded by OverlayManager: +2 outer default \u2192 mild shrink \u2192 full inset */
        outline-offset: var(--keypilot-focus-outline-offset, 2px) !important;
        box-shadow: var(--keypilot-focus-box-shadow, none) !important;
        filter: none !important;
      }

      /* --inset kept for diagnostics; offset always comes from the graded var */
      .keypilot-focus-element.keypilot-focus-element--inset,
      [data-kp-focus="1"][data-kp-focus-inset="1"] {
        outline: var(--keypilot-focus-ring-width, 3px) var(--keypilot-focus-outline-style, solid) var(--keypilot-focus-ring-color, #2196f3) !important;
        outline-offset: var(--keypilot-focus-outline-offset, calc(-1 * var(--keypilot-focus-ring-width, 3px))) !important;
        box-shadow: var(--keypilot-focus-box-shadow, none) !important;
        filter: none !important;
      }

      /* Translucent fill wash (thumbnails/links) only when a fill color is set.
         Base ring rules never override the element's own background on hover. */
      .keypilot-focus-element--fill {
        background: var(--keypilot-focus-ring-bg-color, transparent) !important;
      }

      /* Text-mode focused field: left-edge/wash only \u2014 never a hover outline. */
      .${CSS_CLASSES.TEXT_FOCUS_INPUT}.keypilot-focus-element,
      .${CSS_CLASSES.TEXT_FOCUS_INPUT}[data-kp-focus="1"],
      .${CSS_CLASSES.TEXT_FOCUS_INPUT_PARENT}.keypilot-focus-element,
      .${CSS_CLASSES.TEXT_FOCUS_INPUT_PARENT}[data-kp-focus="1"] {
        outline: none !important;
        outline-offset: 0 !important;
      }

      /* If a ring marker still lands on the field, keep the left-edge pulse. */
      .keypilot-focus-element.${CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE},
      [data-kp-focus="1"].${CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE} {
        box-shadow: inset var(--kpv2-text-left-edge-width, 5px) 0 0 0 ${COLORS.ORANGE} !important;
        animation: kpv2-text-left-edge-pulse 1.5s ease-in-out infinite !important;
      }

      /*
       * Floating keyboard reference: host pages (e.g. Zapier) can override UA
       * [hidden]{display:none} with author display rules. Keep closed panels
       * invisible without relying on the attribute alone. Match early-inject.
       */
      .kp-floating-keyboard-help[hidden],
      .kp-floating-keyboard-help.${CSS_CLASSES.HIDDEN},
      .kp-floating-keyboard-help[aria-hidden="true"] {
        display: none !important;
        visibility: hidden !important;
        pointer-events: none !important;
      }

      ${buildKeyPilotPrintCss()}
    `;
    }
    _buildShadowCSS() {
      const cursorCSS = this.cursorOverridesEnabled ? `
      /* Cursor override inside shadow DOM (archive.org and other web-components).
         Shadow roots don't inherit document-level CSS selectors like html.kpv2-cursor-hidden *,
         so we mirror the cursor rule via :host-context(). */
      :host-context(html.${CSS_CLASSES.CURSOR_HIDDEN}),
      :host-context(html.${CSS_CLASSES.CURSOR_HIDDEN}) * {
        cursor: var(--kpv2-cursor, auto) !important;
      }
      ` : "";
      return `
      ${cursorCSS}

      .${CSS_CLASSES.FOCUS} { 
        filter: brightness(1.2) !important; 
      }
      
      .${CSS_CLASSES.DELETE} { 
        filter: brightness(0.8) contrast(1.2) !important; 
      }

      .${CSS_CLASSES.COLS} {
        filter: brightness(1.05) saturate(1.15) !important;
      }
      
      .${CSS_CLASSES.HIDDEN} { 
        display: none !important; 
      }
      
      /* Add left padding to focused text inputs in shadow DOM */
      input:focus,
      textarea:focus,
      [contenteditable="true"]:focus,
      [contenteditable=""]:focus {
        padding-left: 5pt !important;
      }

      @keyframes kpv2-text-left-edge-pulse {
        0%, 100% {
          box-shadow: inset var(--kpv2-text-left-edge-width, 5px) 0 0 0 ${COLORS.ORANGE_SHADOW};
        }
        50% {
          box-shadow: inset var(--kpv2-text-left-edge-width, 5px) 0 0 0 ${COLORS.ORANGE};
        }
      }

      /* Text inputs: same hover/focus treatment inside shadow DOM */
      .${CSS_CLASSES.TEXT_FOCUS_INPUT_PARENT}:not(.${CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE}) {
        background-color: var(--kpv2-text-input-focus-bg, rgba(255, 140, 0, 0.42)) !important;
      }

      .${CSS_CLASSES.TEXT_HOVER_INPUT} {
        background-image: none !important;
      }

      .${CSS_CLASSES.TEXT_FOCUS_INPUT}:not(.${CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE}):not(.${CSS_CLASSES.TEXT_FOCUS_DELEGATED}) {
        background-color: var(--kpv2-text-input-focus-bg, rgba(255, 140, 0, 0.42)) !important;
      }

      .${CSS_CLASSES.TEXT_FOCUS_INPUT}.${CSS_CLASSES.TEXT_FOCUS_DELEGATED} {
        background-color: transparent !important;
        background-image: none !important;
        box-shadow: none !important;
        animation: none !important;
      }

      .${CSS_CLASSES.TEXT_FOCUS_INPUT_PARENT}.${CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE} {
        box-shadow: inset var(--kpv2-text-left-edge-width, 5px) 0 0 0 ${COLORS.ORANGE} !important;
        animation: kpv2-text-left-edge-pulse 1.5s ease-in-out infinite !important;
        will-change: box-shadow;
      }

      .${CSS_CLASSES.TEXT_FOCUS_INPUT}.${CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE} {
        padding-left: calc(var(--kpv2-text-left-edge-width, 5px) + 4px) !important;
        background-color: transparent !important;
        background-image: none !important;
        box-shadow: inset var(--kpv2-text-left-edge-width, 5px) 0 0 0 ${COLORS.ORANGE} !important;
        animation: kpv2-text-left-edge-pulse 1.5s ease-in-out infinite !important;
        will-change: box-shadow;
      }

      .${CSS_CLASSES.TEXT_FOCUS_INPUT}.${CSS_CLASSES.TEXT_FOCUS_HINT_HIDDEN} {
        background-image: none !important;
      }

      /* Element styling for DOM hover mode in shadow DOM (settings-driven ring). */
      .keypilot-focus-element,
      [data-kp-focus="1"] {
        outline: var(--keypilot-focus-ring-width, 3px) var(--keypilot-focus-outline-style, solid) var(--keypilot-focus-ring-color, #2196f3) !important;
        /* Graded by OverlayManager: +2 outer default \u2192 mild shrink \u2192 full inset */
        outline-offset: var(--keypilot-focus-outline-offset, 2px) !important;
        box-shadow: var(--keypilot-focus-box-shadow, none) !important;
        filter: none !important;
      }

      /* --inset kept for diagnostics; offset always comes from the graded var */
      .keypilot-focus-element.keypilot-focus-element--inset,
      [data-kp-focus="1"][data-kp-focus-inset="1"] {
        outline: var(--keypilot-focus-ring-width, 3px) var(--keypilot-focus-outline-style, solid) var(--keypilot-focus-ring-color, #2196f3) !important;
        outline-offset: var(--keypilot-focus-outline-offset, calc(-1 * var(--keypilot-focus-ring-width, 3px))) !important;
        box-shadow: var(--keypilot-focus-box-shadow, none) !important;
        filter: none !important;
      }

      /* Translucent fill wash (thumbnails/links) only when a fill color is set.
         Base ring rules never override the element's own background on hover. */
      .keypilot-focus-element--fill {
        background: var(--keypilot-focus-ring-bg-color, transparent) !important;
      }

      /* Text-mode focused field: left-edge/wash only \u2014 never a hover outline. */
      .${CSS_CLASSES.TEXT_FOCUS_INPUT}.keypilot-focus-element,
      .${CSS_CLASSES.TEXT_FOCUS_INPUT}[data-kp-focus="1"],
      .${CSS_CLASSES.TEXT_FOCUS_INPUT_PARENT}.keypilot-focus-element,
      .${CSS_CLASSES.TEXT_FOCUS_INPUT_PARENT}[data-kp-focus="1"] {
        outline: none !important;
        outline-offset: 0 !important;
      }

      /* If a ring marker still lands on the field, keep the left-edge pulse. */
      .keypilot-focus-element.${CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE},
      [data-kp-focus="1"].${CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE} {
        box-shadow: inset var(--kpv2-text-left-edge-width, 5px) 0 0 0 ${COLORS.ORANGE} !important;
        animation: kpv2-text-left-edge-pulse 1.5s ease-in-out infinite !important;
      }

      ${buildKeyPilotPrintCss()}
    `;
    }
    injectSharedStyles() {
      if (this.injectedStyles.has("main") || !this.isEnabled) return;
      const css = this._buildMainCSS();
      this.injectCSS(css, ELEMENT_IDS.STYLE);
      this.injectedStyles.add("main");
      this._ensureBlueTintFilterInDocument();
      this._applyTextInputHintCssVars();
      if (this.cursorOverridesEnabled) {
        document.documentElement.classList.add(CSS_CLASSES.CURSOR_HIDDEN);
      } else {
        document.documentElement.classList.remove(CSS_CLASSES.CURSOR_HIDDEN);
      }
    }
    _ensureBlueTintFilterInDocument() {
      this._ensureBlueTintFilterInRoot(document);
    }
    _ensureBlueTintFilterInShadowRoot(shadowRoot) {
      this._ensureBlueTintFilterInRoot(shadowRoot);
    }
    _ensureBlueTintFilterInRoot(root) {
      try {
        const queryRoot = root && typeof root.querySelector === "function" ? root : document;
        const existing = queryRoot.querySelector(`#${BLUE_TINT_SVG_ID}`);
        if (existing) return;
        const ns = "http://www.w3.org/2000/svg";
        const svg = document.createElementNS(ns, "svg");
        svg.setAttribute("id", BLUE_TINT_SVG_ID);
        svg.setAttribute("aria-hidden", "true");
        svg.style.position = "absolute";
        svg.style.width = "0";
        svg.style.height = "0";
        svg.style.overflow = "hidden";
        svg.style.pointerEvents = "none";
        const defs = document.createElementNS(ns, "defs");
        const filter = document.createElementNS(ns, "filter");
        filter.setAttribute("id", BLUE_TINT_FILTER_ID);
        filter.setAttribute("color-interpolation-filters", "sRGB");
        const matrix = document.createElementNS(ns, "feColorMatrix");
        matrix.setAttribute("type", "matrix");
        matrix.setAttribute(
          "values",
          "0.82 0.06 0.06 0 0  0.06 0.82 0.06 0 0  0.12 0.12 1.05 0 0.12  0 0 0 1 0"
        );
        const transfer = document.createElementNS(ns, "feComponentTransfer");
        const fr = document.createElementNS(ns, "feFuncR");
        const fg = document.createElementNS(ns, "feFuncG");
        const fb = document.createElementNS(ns, "feFuncB");
        fr.setAttribute("type", "linear");
        fr.setAttribute("slope", "1.08");
        fr.setAttribute("intercept", "0.03");
        fg.setAttribute("type", "linear");
        fg.setAttribute("slope", "1.08");
        fg.setAttribute("intercept", "0.03");
        fb.setAttribute("type", "linear");
        fb.setAttribute("slope", "1.10");
        fb.setAttribute("intercept", "0.04");
        transfer.appendChild(fr);
        transfer.appendChild(fg);
        transfer.appendChild(fb);
        filter.appendChild(matrix);
        filter.appendChild(transfer);
        defs.appendChild(filter);
        svg.appendChild(defs);
        const parent = root instanceof ShadowRoot ? root : document.body || document.documentElement || document;
        parent.appendChild(svg);
      } catch {
      }
    }
    injectCSS(css, id) {
      const existing = document.getElementById(id);
      if (existing) return;
      try {
        const style = document.createElement("style");
        style.id = id;
        style.textContent = css;
        document.head.appendChild(style);
      } catch (error) {
        console.warn("[StyleManager] Cannot inject CSS on this page:", error.message);
      }
    }
    /**
     * Ensure KeyPilot CSS applies for a node that may live inside open shadow DOM.
     * Document-level styles do not pierce shadow boundaries, so we inject into the
     * owning open ShadowRoot on first use (lazy). Safe / idempotent.
     *
     * @param {Node|null|undefined} node
     * @returns {boolean} true if styles are available for this node (document or injected shadow)
     */
    ensureStylesForNode(node) {
      if (!this.isEnabled || !node) return false;
      let root = null;
      try {
        root = typeof node.getRootNode === "function" ? node.getRootNode() : null;
      } catch {
        root = null;
      }
      if (!root || root === document) {
        return true;
      }
      if (root.nodeType === 9) {
        return this.injectIntoForeignDocument(
          /** @type {Document} */
          root
        );
      }
      if (typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot) {
        const ok = this.injectIntoShadowRoot(root);
        if (ok) {
          try {
            window.keyPilot?.shadowDOMManager?.trackShadowRoot?.(root);
          } catch {
          }
        }
        return ok;
      }
      return false;
    }
    /**
     * Inject text-mode / hover chrome CSS into a same-origin child document.
     * Parent `document` styles do not apply inside an iframe.
     * @param {Document} doc
     * @returns {boolean}
     */
    injectIntoForeignDocument(doc) {
      if (!doc || !this.isEnabled || doc === document) return !!doc;
      try {
        const id = ELEMENT_IDS.STYLE || "kpv2-style";
        let style = typeof doc.getElementById === "function" ? doc.getElementById(id) : null;
        if (!style) {
          style = doc.createElement("style");
          style.id = id;
          style.textContent = this._buildShadowCSS();
          const head = doc.head || doc.documentElement;
          if (!head) return false;
          head.appendChild(style);
        }
        this._foreignDocuments.add(doc);
        try {
          const hoverUri = this._textHoverHintUri || "none";
          const focusUri = this._textFocusHintUri || "none";
          doc.documentElement.style.setProperty("--kpv2-text-hover-hint-image", hoverUri);
          doc.documentElement.style.setProperty("--kpv2-text-focus-hint-image", focusUri);
        } catch {
        }
        return true;
      } catch {
        return false;
      }
    }
    /**
     * Inject KeyPilot shadow CSS + blue-tint filter into an open shadow root.
     * Idempotent and wipe-resistant: Lit/SPA re-renders often replace shadow
     * children and drop our <style>. We always re-check the DOM (not only our
     * session Set) so archive.org-style nested open roots keep focus outlines.
     *
     * Used by:
     * - attachShadow pre-warm (ShadowDOMManager)
     * - lazy ensureStylesForNode on first hover/focus styling
     *
     * @param {ShadowRoot} shadowRoot
     * @returns {boolean}
     */
    injectIntoShadowRoot(shadowRoot) {
      if (!shadowRoot || !this.isEnabled) return false;
      try {
        let style = typeof shadowRoot.querySelector === "function" ? shadowRoot.querySelector("#keypilot-shadow-styles") : null;
        if (!style) {
          const css = this._buildShadowCSS();
          style = document.createElement("style");
          style.id = "keypilot-shadow-styles";
          style.textContent = css;
          shadowRoot.appendChild(style);
        }
        this._ensureBlueTintFilterInShadowRoot(shadowRoot);
        this.injectedStyles.add(shadowRoot);
        this.shadowRootStyles.set(shadowRoot, style);
        try {
          const host = shadowRoot.host;
          if (host?.style) {
            host.style.setProperty("--kpv2-text-hover-hint-image", this._textHoverHintUri || "none");
            host.style.setProperty("--kpv2-text-focus-hint-image", this._textFocusHintUri || "none");
          }
        } catch {
        }
        return true;
      } catch (error) {
        if (window.KEYPILOT_DEBUG) {
          console.warn("[StyleManager] Failed to inject styles into shadow root:", error);
        }
        return false;
      }
    }
    /**
     * Completely remove all KeyPilot CSS styles from the page
     * Used when extension is toggled off
     */
    removeAllStyles() {
      document.documentElement.classList.remove(CSS_CLASSES.CURSOR_HIDDEN);
      const mainStyle = document.getElementById(ELEMENT_IDS.STYLE);
      if (mainStyle) {
        mainStyle.remove();
      }
      for (const [shadowRoot, styleElement] of this.shadowRootStyles) {
        if (styleElement && styleElement.parentNode) {
          styleElement.remove();
        }
      }
      try {
        const svg = document.getElementById(BLUE_TINT_SVG_ID);
        if (svg) svg.remove();
      } catch {
      }
      for (const shadowRoot of this.shadowRootStyles.keys()) {
        try {
          const svg = shadowRoot && shadowRoot.querySelector ? shadowRoot.querySelector(`#${BLUE_TINT_SVG_ID}`) : null;
          if (svg) svg.remove();
        } catch {
        }
      }
      this.removeAllKeyPilotClasses();
      this.injectedStyles.clear();
      this.shadowRootStyles.clear();
      this.isEnabled = false;
    }
    /**
     * Restore all KeyPilot CSS styles to the page
     * Used when extension is toggled back on
     */
    restoreAllStyles() {
      this.isEnabled = true;
      this.injectSharedStyles();
      this._applyTextInputHintCssVars();
    }
    /**
     * Remove all KeyPilot CSS classes from DOM elements
     */
    removeAllKeyPilotClasses() {
      const classesToRemove = [
        CSS_CLASSES.FOCUS,
        CSS_CLASSES.DELETE,
        CSS_CLASSES.COLS,
        CSS_CLASSES.INSPECTOR,
        CSS_CLASSES.COLS_ACTIVE,
        CSS_CLASSES.COLS_PAGE,
        CSS_CLASSES.HIGHLIGHT,
        CSS_CLASSES.HIDDEN,
        CSS_CLASSES.RIPPLE,
        CSS_CLASSES.VIEWPORT_MODAL_FRAME,
        CSS_CLASSES.ESC_EXIT_LABEL,
        CSS_CLASSES.TEXT_FOCUS_ESC_HINT,
        CSS_CLASSES.TEXT_HOVER_ACTIVATE_HINT,
        CSS_CLASSES.HIGHLIGHT_OVERLAY,
        CSS_CLASSES.HIGHLIGHT_SELECTION,
        CSS_CLASSES.UNIT_SELECT_OVERLAY,
        CSS_CLASSES.TEXT_FOCUS_INPUT,
        CSS_CLASSES.TEXT_FOCUS_INPUT_PARENT,
        CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE,
        CSS_CLASSES.TEXT_FOCUS_DELEGATED,
        CSS_CLASSES.TEXT_FOCUS_HINT_HIDDEN,
        CSS_CLASSES.TEXT_HOVER_INPUT,
        CSS_CLASSES.TEXT_HOVER_INPUT_PARENT
      ];
      classesToRemove.forEach((className) => {
        const elements = document.querySelectorAll(`.${className}`);
        elements.forEach((el2) => el2.classList.remove(className));
      });
      for (const shadowRoot of this.shadowRootStyles.keys()) {
        classesToRemove.forEach((className) => {
          const elements = shadowRoot.querySelectorAll(`.${className}`);
          elements.forEach((el2) => el2.classList.remove(className));
        });
      }
    }
    /**
     * Check if styles are currently enabled
     */
    isStylesEnabled() {
      return this.isEnabled;
    }
    cleanup() {
      this.removeAllStyles();
    }
  };

  // src/modules/shadow-dom-manager.js
  var ShadowDOMManager = class {
    constructor(styleManager) {
      this.styleManager = styleManager;
      this.shadowRoots = /* @__PURE__ */ new Set();
      this.originalAttachShadow = null;
    }
    setup() {
      this.patchAttachShadow();
      this.processExistingShadowRoots();
    }
    /**
     * Track a shadow root we've injected (or intend to use) for optional IO discovery.
     * @param {ShadowRoot} root
     */
    trackShadowRoot(root) {
      if (root) {
        try {
          this.shadowRoots.add(root);
        } catch {
        }
      }
    }
    patchAttachShadow() {
      if (this.originalAttachShadow) return;
      const original = Element.prototype.attachShadow;
      this.originalAttachShadow = original;
      const styleManager = this.styleManager;
      const manager = this;
      Element.prototype.attachShadow = function attachShadowPatched(init) {
        const root = original.call(this, init);
        try {
          if (init && init.mode === "open" && root && !this.hasAttribute?.("data-kp-ui-shadow")) {
            styleManager.injectIntoShadowRoot(root);
            manager.trackShadowRoot(root);
          }
        } catch (error) {
          console.warn("[KeyPilot] Failed to inject styles into shadow root:", error);
        }
        return root;
      };
    }
    /**
     * Pre-warm open shadow roots already in the tree, including nested roots
     * (archive.org: app-root → ia-topnav → primary-nav → media-button → …).
     * Lazy ensureStylesForNode remains the wipe-resistant correctness path.
     */
    processExistingShadowRoots() {
      const injectTree = (root) => {
        if (!root) return;
        const walker = document.createTreeWalker(
          root,
          NodeFilter.SHOW_ELEMENT,
          null,
          false
        );
        let node;
        while (node = walker.nextNode()) {
          if (!node.shadowRoot) continue;
          if (node.hasAttribute?.("data-kp-ui-shadow")) continue;
          try {
            this.styleManager.injectIntoShadowRoot(node.shadowRoot);
            this.trackShadowRoot(node.shadowRoot);
            injectTree(node.shadowRoot);
          } catch (error) {
            console.warn("[KeyPilot] Failed to inject styles into existing shadow root:", error);
          }
        }
      };
      injectTree(document.documentElement);
    }
    cleanup() {
      if (this.originalAttachShadow) {
        Element.prototype.attachShadow = this.originalAttachShadow;
        this.originalAttachShadow = null;
      }
      this.shadowRoots.clear();
    }
  };

  // src/modules/intersection-observer-manager.js
  init_constants();
  var IntersectionObserverManager = class {
    constructor(elementDetector) {
      this.elementDetector = elementDetector;
      this.shadowDOMManager = null;
      this.interactiveObserver = null;
      this.overlayObserver = null;
      this.visibleInteractiveElements = /* @__PURE__ */ new Set();
      this.elementPositionCache = /* @__PURE__ */ new Map();
      this._domHoverEnabled = false;
      this._domHoveredElement = null;
      this._domHoverLeaf = null;
      this._domHoverOnChange = null;
      this._domHoverUseDelegation = true;
      this._domHoverDelegationAttached = false;
      this._domHoverAttachedElements = /* @__PURE__ */ new Set();
      this._domHoverMetrics = { attached: 0, skipped: 0, delegated: 0 };
      this._domHoverRehealRAF = 0;
      this._shadowDebugLeafRAF = 0;
      this._boundDocPointerMoveReheal = (e) => {
        try {
          this._maybeTrackShadowDebugLeaf(e);
          if (!this._domHoverEnabled || !this._domHoveredElement) return;
          if (this._domHoverRehealRAF) return;
          const el2 = this._domHoveredElement;
          let hasMarker = false;
          try {
            hasMarker = typeof el2.hasAttribute === "function" && el2.hasAttribute("data-kp-focus") || !!(el2.classList && el2.classList.contains("keypilot-focus-element"));
          } catch {
          }
          if (hasMarker) return;
          this._domHoverRehealRAF = requestAnimationFrame(() => {
            this._domHoverRehealRAF = 0;
            try {
              this._rehealDomHoverFocusStyling(this._domHoveredElement);
            } catch {
            }
          });
        } catch {
        }
      };
      this._boundDomHoverEnter = (e) => {
        try {
          if (!this._domHoverEnabled) return;
          const el2 = e?.currentTarget;
          if (!el2 || el2.nodeType !== 1) return;
          try {
            window.__KP_HOVERED_INTERACTIVE_EL = el2;
          } catch {
          }
          this._domHoveredElement = el2;
          if (typeof this._domHoverOnChange === "function") {
            try {
              this._domHoverOnChange(
                /** @type {HTMLElement} */
                el2
              );
            } catch {
            }
          }
        } catch {
        }
      };
      this._boundDomHoverLeave = (e) => {
        try {
          if (!this._domHoverEnabled) return;
          const el2 = e?.currentTarget;
          if (!el2 || el2.nodeType !== 1) return;
          if (this._domHoveredElement !== el2) return;
          this._domHoveredElement = null;
          try {
            window.__KP_HOVERED_INTERACTIVE_EL = null;
          } catch {
          }
          if (typeof this._domHoverOnChange === "function") {
            try {
              this._domHoverOnChange(null);
            } catch {
            }
          }
        } catch {
        }
      };
      this._boundDocPointerOver = (e) => {
        try {
          if (!this._domHoverEnabled) return;
          const path = e && typeof e.composedPath === "function" ? e.composedPath() : null;
          let raw = null;
          if (Array.isArray(path)) {
            for (const n of path) {
              if (n && n.nodeType === 1) {
                raw = n;
                break;
              }
            }
          }
          if (!raw && e?.target && e.target.nodeType === 1) raw = e.target;
          if (!raw || raw.nodeType !== 1) return;
          const el2 = (
            /** @type {HTMLElement} */
            raw
          );
          if (this._isKeyPilotUiElement(el2)) return;
          const leafChanged = this._setDomHoverLeaf(el2);
          const x = typeof e.clientX === "number" ? e.clientX : null;
          const y = typeof e.clientY === "number" ? e.clientY : null;
          this._resolveAndPublishDomHover(el2, x, y, { leafChanged });
        } catch {
        }
      };
      this._boundDocPointerOut = (e) => {
        try {
          if (!this._domHoverEnabled) return;
          const rt = e?.relatedTarget;
          if (rt && rt.nodeType === 1) {
            try {
              if (rt.tagName === "HTML" || rt.tagName === "BODY") {
              } else {
                return;
              }
            } catch {
              return;
            }
          }
          if (!this._domHoveredElement) return;
          this._setDomHoveredElement(null);
        } catch {
        }
      };
      this._boundWindowBlur = () => {
        try {
          if (!this._domHoverEnabled) return;
          if (!this._domHoveredElement) return;
          this._setDomHoveredElement(null);
        } catch {
        }
      };
      this.cacheUpdateTimeout = null;
      this.interactiveSelector = 'a[href], button, input, select, textarea, [role="button"], [role="link"], [role="tab"], [contenteditable="true"], [onclick]';
      this.observedInteractiveElements = /* @__PURE__ */ new Set();
      this.mutationObserver = null;
      this._pendingAddedRoots = /* @__PURE__ */ new Set();
      this._pendingRemovedRoots = [];
      this._pendingAttributeTargets = /* @__PURE__ */ new Set();
      this._mutationProcessScheduled = false;
      this._mutationIdleHandle = null;
      this._discoverScheduled = false;
      this._discoverIdleHandle = null;
      this._discoverWalker = null;
      this._discoverCursor = null;
      this._discoverDone = false;
      this._rtree = null;
      this._rtreeItemsByElement = /* @__PURE__ */ new Map();
      this._rtreeReady = false;
      this._rtreeDisabledByDomHover = true;
      this._rtreeMaxEntries = 16;
      this._rtreeRemoveEquals = null;
      this._destIndex = /* @__PURE__ */ new Map();
      this._displayRectCache = {
        version: 0,
        destination: null,
        anchorEl: null,
        // Store the rect in *page coordinates* (minX/minY/maxX/maxY). We convert to viewport
        // coordinates on demand so scrolling doesn't stale-cache the overlay position.
        pageRect: null
      };
      this._rtreeVersion = 0;
      this.metrics = {
        cacheHits: 0,
        cacheMisses: 0,
        observerUpdates: 0,
        rtreeQueries: 0,
        rtreeHits: 0,
        rtreeFallbacks: 0,
        culledCount: 0,
        totalCulled: 0
      };
      this.rbushDebugOverlays = [];
      this.rbushDebugOverlayTimeout = null;
      this.complexPageDetector = {
        isAnalyzed: false,
        complexityLevel: "unknown",
        lastAnalysis: 0,
        analysisInterval: 3e4,
        // Re-analyze every 30 seconds (cheap, but no need to spam)
        // Static metrics (computed once)
        staticMetrics: {
          hasInfiniteScroll: false,
          isSocialMedia: false,
          urlPatterns: []
        },
        // Dynamic metrics (updated periodically)
        dynamicMetrics: {
          observerCount: 0,
          visibleCount: 0,
          rbushItems: 0,
          pendingMutations: 0,
          rtreeHitRate: 0
        },
        // IO adaptation settings
        ioAdaptation: {
          rootMargin: "100px",
          threshold: [0, 0.1, 0.5, 1],
          maxObservations: 1e3,
          spatialCullDistance: 200,
          batchSize: 50
        }
      };
    }
    /**
     * Check if a parent container should be focused instead of an individual clickable element.
     * If the hovered element is clickable and its parent has similar clickable properties,
     * and all children of the parent have similar properties, focus the parent instead.
     *
     * Important (ganjingworld / video cards): do **not** promote a tight media
     * thumbnail link up into a larger card shell that also has onclick and wraps
     * title/meta. That makes a good thumb outline jump to an outer box a moment
     * later when the card handler hydrates.
     *
     * @param {HTMLElement} element
     * @returns {HTMLElement}
     */
    _findParentContainerForClickable(element) {
      if (!element || element.nodeType !== 1) return element;
      try {
        if (!this.elementDetector?.isLikelyInteractive(element)) return element;
        const parent = element.parentElement;
        if (!parent || parent.nodeType !== 1) return element;
        try {
          if (this.elementDetector.isCompositeClickContainer?.(parent)) return element;
        } catch {
        }
        if (!this.elementDetector.isLikelyInteractive(parent)) return element;
        try {
          const er = element.getBoundingClientRect();
          const pr = parent.getBoundingClientRect();
          if (er.width > 0 && er.height > 0 && pr.width > 0 && pr.height > 0) {
            const eArea = er.width * er.height;
            const pArea = pr.width * pr.height;
            if (pArea > eArea * 1.18 || pr.height > er.height * 1.12 || pr.width > er.width * 1.12) {
              return element;
            }
          }
        } catch {
        }
        const elementProfile = this._getClickabilityProfile(element);
        const parentProfile = this._getClickabilityProfile(parent);
        if (!this._profilesAreSimilar(elementProfile, parentProfile)) return element;
        const children = Array.from(parent.children);
        const clickableChildren = children.filter((child) => {
          if (child.nodeType !== 1) return false;
          return this.elementDetector.isLikelyInteractive(child);
        });
        if (clickableChildren.length === 0) return element;
        const allChildrenSimilar = clickableChildren.every((child) => {
          const childProfile = this._getClickabilityProfile(child);
          return this._profilesAreSimilar(elementProfile, childProfile);
        });
        if (allChildrenSimilar) {
          if (window.KEYPILOT_DEBUG) {
            console.log("[KeyPilot Debug] Focusing parent container instead of child element:", {
              originalElement: element,
              parentElement: parent,
              elementProfile,
              parentProfile
            });
          }
          return parent;
        }
      } catch (error) {
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Error in parent container detection:", error);
        }
      }
      return element;
    }
    /**
     * Get a profile of what makes an element clickable
     * @param {HTMLElement} element
     * @returns {Object} profile object
     */
    _getClickabilityProfile(element) {
      const profile = {
        tagName: element.tagName,
        href: "",
        role: "",
        hasClickHandler: false,
        hasCursorPointer: false,
        inputType: "",
        isContentEditable: false
      };
      try {
        if (element.tagName === "A") {
          profile.href = this._getNormalizedDestination(element);
        }
        profile.role = (element.getAttribute && element.getAttribute("role") || "").trim().toLowerCase();
        profile.hasClickHandler = !!(element.onclick || element.getAttribute("onclick") || this.elementDetector?.hasTrackedClickHandler(element));
        if (!profile.href && !profile.role && !profile.hasClickHandler) {
          try {
            if (this.elementDetector && typeof this.elementDetector.hasExplicitCursorPointer === "function") {
              profile.hasCursorPointer = !!this.elementDetector.hasExplicitCursorPointer(element);
            } else {
              profile.hasCursorPointer = !!(window.getComputedStyle && window.getComputedStyle(element).cursor === "pointer");
            }
          } catch {
            profile.hasCursorPointer = false;
          }
        }
        if (element.tagName === "INPUT") {
          profile.inputType = (element.getAttribute("type") || "text").toLowerCase();
        }
        profile.isContentEditable = !!(element.isContentEditable || element.getAttribute("contenteditable") === "true");
      } catch (error) {
      }
      return profile;
    }
    /**
     * Check if two clickability profiles are similar enough to be considered
     * part of the same interactive unit
     * @param {Object} profile1
     * @param {Object} profile2
     * @returns {boolean}
     */
    _profilesAreSimilar(profile1, profile2) {
      if (!profile1 || !profile2) return false;
      const a1 = profile1.tagName === "A";
      const a2 = profile2.tagName === "A";
      if (a1 || a2) {
        if (a1 && a2) {
          return !!(profile1.href && profile1.href === profile2.href);
        }
        if (profile1.role && profile1.role === profile2.role) {
          return true;
        }
        return false;
      }
      if (profile1.tagName === profile2.tagName) {
        if (profile1.tagName === "INPUT") {
          return profile1.inputType === profile2.inputType;
        }
        return true;
      }
      if (profile1.role && profile1.role === profile2.role) {
        return true;
      }
      if (profile1.href && profile1.href === profile2.href) {
        return true;
      }
      if (profile1.hasClickHandler && profile2.hasClickHandler && profile1.tagName === profile2.tagName) {
        return true;
      }
      if (profile1.hasCursorPointer && profile2.hasCursorPointer) {
        return true;
      }
      if (profile1.isContentEditable && profile2.isContentEditable) {
        return true;
      }
      return false;
    }
    /**
     * Extract and normalize a destination string for an element.
     * For now we only use <a href> to keep semantics reliable.
     * @param {HTMLElement} element
     * @returns {string} normalized destination or '' if none
     */
    _getNormalizedDestination(element) {
      try {
        if (!element || element.nodeType !== 1) return "";
        if (element.tagName !== "A") return "";
        const hrefAttr = element.getAttribute && element.getAttribute("href");
        if (!hrefAttr) return "";
        const resolved = element.href || hrefAttr;
        try {
          return new URL(resolved, window.location.href).href;
        } catch {
          return String(resolved || "");
        }
      } catch {
        return "";
      }
    }
    /**
     * Set the shadow DOM manager for shadow root discovery
     * @param {Object} shadowDOMManager
     */
    setShadowDOMManager(shadowDOMManager) {
      this.shadowDOMManager = shadowDOMManager;
    }
    /**
     * Discover interactive elements inside tracked shadow roots
     * @param {number} maxElements - Maximum elements to discover this slice
     * @param {Function} timeRemaining - Function to check remaining idle time
     * @returns {number} - Number of elements discovered
     */
    discoverShadowDOMElements(maxElements = 50, timeRemaining = () => 0) {
      if (!this.shadowDOMManager?.shadowRoots?.size) return 0;
      if (typeof querySelectorAllDeep !== "function") return 0;
      let discovered = 0;
      const cap = Math.max(0, Number(this.getIOAdaptation().maxObservations) || 0);
      for (const shadowRoot of this.shadowDOMManager.shadowRoots) {
        if (discovered >= maxElements) break;
        if (timeRemaining() < 1) break;
        try {
          const elements = querySelectorAllDeep(this.interactiveSelector, shadowRoot);
          for (const el2 of elements) {
            if (discovered >= maxElements) break;
            if (cap > 0 && this.observedInteractiveElements.size >= cap) break;
            if (!this.isElementObserved(el2)) {
              this.observeInteractiveElement(el2);
              discovered++;
            }
          }
        } catch (error) {
          if (window.KEYPILOT_DEBUG) {
            console.warn("[KeyPilot] Error discovering elements in shadow root:", error);
          }
        }
      }
      return discovered;
    }
    /**
     * Publish a new DOM-hover focus target (and debug hook).
     * @param {HTMLElement|null} next
     */
    _setDomHoveredElement(next) {
      const el2 = next && next.nodeType === 1 ? (
        /** @type {HTMLElement} */
        next
      ) : null;
      this._domHoveredElement = el2;
      if (!el2) {
        this._setDomHoverLeaf(null);
      }
      try {
        window.__KP_HOVERED_INTERACTIVE_EL = el2;
      } catch {
      }
      if (typeof this._domHoverOnChange === "function") {
        try {
          this._domHoverOnChange(el2);
        } catch {
        }
      }
    }
    /**
     * @param {Element|null|undefined} el
     * @returns {boolean} true when the stored leaf reference changed
     */
    _setDomHoverLeaf(el2) {
      const next = el2 && el2.nodeType === 1 ? (
        /** @type {HTMLElement} */
        el2
      ) : null;
      if (this._domHoverLeaf === next) return false;
      this._domHoverLeaf = next;
      try {
        window.__KP_HOVER_LEAF = next;
      } catch {
      }
      return true;
    }
    /**
     * Deepest element under the pointer from the last pointerover (composedPath).
     * @returns {HTMLElement|null}
     */
    getDomHoverLeaf() {
      return this._domHoverLeaf && this._domHoverLeaf.nodeType === 1 ? (
        /** @type {HTMLElement} */
        this._domHoverLeaf
      ) : null;
    }
    /**
     * Resolve focusEl from a leaf under the pointer and publish via _setDomHoveredElement.
     * Shared by pointerover and (for open-shadow leaves) pointermove re-resolve.
     *
     * @param {Element} el - deepest leaf
     * @param {number|null|undefined} clientX
     * @param {number|null|undefined} clientY
     * @param {{ leafChanged?: boolean }} [opts]
     */
    _resolveAndPublishDomHover(el2, clientX, clientY, opts = {}) {
      if (!el2 || el2.nodeType !== 1) return;
      const leafChanged = !!opts.leafChanged;
      let clickable = null;
      try {
        if (this.elementDetector?.resolveHoverFocusTarget) {
          clickable = this.elementDetector.resolveHoverFocusTarget(
            el2,
            this._domHoveredElement,
            clientX,
            clientY
          );
        } else if (this.elementDetector?.findClickable) {
          clickable = this.elementDetector.findClickable(el2);
        } else {
          clickable = el2;
        }
      } catch {
        clickable = el2;
      }
      if (!clickable) {
        let inShadow = false;
        try {
          inShadow = el2.getRootNode() instanceof ShadowRoot;
        } catch {
          inShadow = false;
        }
        if (inShadow && Number.isFinite(clientX) && Number.isFinite(clientY)) {
          try {
            const shadowElements = this.queryInteractiveAtPoint(clientX, clientY, 20);
            if (shadowElements.length > 0) {
              const shadowLeaf = shadowElements[0];
              clickable = this.elementDetector?.resolveHoverFocusTarget ? this.elementDetector.resolveHoverFocusTarget(
                shadowLeaf,
                this._domHoveredElement,
                clientX,
                clientY
              ) : shadowLeaf;
            }
          } catch (error) {
            if (window.KEYPILOT_DEBUG) {
              console.warn("[KeyPilot] Shadow-piercing query failed:", error);
            }
          }
        }
      }
      if (!clickable && this._domHoveredElement && Number.isFinite(clientX) && Number.isFinite(clientY)) {
        try {
          if (this.elementDetector?.pointInElementUnionBox?.(
            this._domHoveredElement,
            clientX,
            clientY,
            3
          )) {
            clickable = this._domHoveredElement;
          }
        } catch {
        }
      }
      if (clickable) {
        clickable = this._findParentContainerForClickable(clickable);
      }
      let next = clickable && clickable.nodeType === 1 ? (
        /** @type {HTMLElement} */
        clickable
      ) : null;
      try {
        if (next && (next.tagName === "HTML" || next.tagName === "BODY")) next = null;
      } catch {
      }
      if (this._domHoveredElement && !this._domHoveredElement.isConnected) {
        this._domHoveredElement = null;
      }
      if (next === this._domHoveredElement) {
        this._rehealDomHoverFocusStyling(next);
        if (leafChanged) this._notifyShadowDebugHudLeafChanged();
        return;
      }
      this._setDomHoveredElement(next);
    }
    /**
     * Track deepest composedPath leaf on pointermove. When the leaf changes inside
     * an open shadow tree, re-resolve focusEl (pointerover alone was insufficient
     * after the closest()-sticky bug, and some Lit trees are noisy on over/out).
     * Also re-resolves while hovering CSS stretched links (`a::after { inset:0 }`)
     * even when the leaf is unchanged — the hit node stays the headline across the
     * whole card (msn.com), so media vs title is geometry-only.
     * Also refreshes the shadow debug HUD leaf line.
     * @param {Event|null|undefined} e
     */
    _maybeTrackShadowDebugLeaf(e) {
      try {
        if (!e) return;
        const path = typeof e.composedPath === "function" ? e.composedPath() : null;
        let raw = null;
        if (Array.isArray(path)) {
          for (const n of path) {
            if (n && n.nodeType === 1) {
              raw = n;
              break;
            }
          }
        }
        if (!raw && e.target && e.target.nodeType === 1) raw = e.target;
        if (!raw || raw.nodeType !== 1) return;
        if (this._isKeyPilotUiElement(raw)) return;
        const leafChanged = this._setDomHoverLeaf(raw);
        const x = typeof e.clientX === "number" ? e.clientX : null;
        const y = typeof e.clientY === "number" ? e.clientY : null;
        let needsResolve = leafChanged;
        if (!needsResolve && Number.isFinite(x) && Number.isFinite(y) && this.elementDetector) {
          try {
            const clickable = this.elementDetector.findClickable ? this.elementDetector.findClickable(raw) : null;
            if (clickable && typeof this.elementDetector._getStretchedLinkMetrics === "function" && this.elementDetector._getStretchedLinkMetrics(clickable)) {
              needsResolve = true;
            }
          } catch {
          }
        }
        if (!needsResolve) return;
        this._pendingDomHoverResolve = {
          leaf: raw,
          x,
          y,
          leafChanged: !!leafChanged
        };
        if (this._shadowDebugLeafRAF) return;
        const hudOn = !!window.keyPilot?.overlayManager?.isShadowRootDebugHudEnabled?.();
        this._shadowDebugLeafRAF = requestAnimationFrame(() => {
          this._shadowDebugLeafRAF = 0;
          const pending = this._pendingDomHoverResolve;
          this._pendingDomHoverResolve = null;
          if (!pending?.leaf) return;
          try {
            this._resolveAndPublishDomHover(
              pending.leaf,
              pending.x,
              pending.y,
              { leafChanged: !!pending.leafChanged }
            );
            if (hudOn) this._notifyShadowDebugHudLeafChanged();
          } catch {
          }
        });
      } catch {
      }
    }
    _notifyShadowDebugHudLeafChanged() {
      try {
        window.keyPilot?.overlayManager?.refreshShadowRootDebugHudLeaf?.();
      } catch {
      }
    }
    /**
     * Re-ensure CSS and re-apply focus ring markers if a SPA wiped them
     * while the pointer stayed on the same clickable.
     * Cheap no-op when markers are already present.
     *
     * Important: when hover paint uses strategy B (in-target ring) or C (body
     * fixed overlay) for overflow-clipped media cards — see
     * unused/reference-info/focus-ring-paint.md — `data-kp-focus` is
     * intentionally absent on the clickable. Do NOT treat that as a wipe and
     * force `updateFocusOverlayElementStyling` (A only) — that fights the ring,
     * applies inset outlines under full-bleed content, and on sites with
     * `transition: all` on the card makes the outline appear then vanish under
     * the site's :hover scrim.
     *
     * @param {Element|null|undefined} el
     */
    _rehealDomHoverFocusStyling(el2) {
      if (!el2 || el2.nodeType !== 1 || !el2.isConnected) return;
      try {
        window.keyPilot?.styleManager?.ensureStylesForNode?.(el2);
      } catch {
      }
      const om = window.keyPilot?.overlayManager || null;
      try {
        if (om && om._focusPaintUsesFixedOverlay) {
          const fo = om.focusOverlay;
          const hidden = !fo || fo.style.display === "none" || fo.style.visibility === "hidden" || fo.style.opacity === "0";
          if (hidden && typeof om.updateFocusOverlay === "function") {
            om.updateFocusOverlay(el2);
          }
          return;
        }
      } catch {
      }
      try {
        if (om && om._focusPaintUsesInTargetRing) {
          const ring = om._inTargetRing;
          const host = om._inTargetHost;
          const missing = !ring || !ring.isConnected || !host || !host.isConnected || ring.parentNode !== host || ring.style.display === "none";
          if (missing && typeof om.updateFocusOverlay === "function") {
            om.updateFocusOverlay(el2);
          }
          return;
        }
      } catch {
      }
      let missingFocus = false;
      try {
        missingFocus = !el2.hasAttribute?.("data-kp-focus") && !(el2.classList && el2.classList.contains("keypilot-focus-element"));
      } catch {
        missingFocus = true;
      }
      if (!missingFocus) return;
      try {
        if (om && typeof om.updateFocusOverlay === "function") {
          om.updateFocusOverlay(el2);
          return;
        }
        if (om && typeof om.updateFocusOverlayElementStyling === "function") {
          om.updateFocusOverlayElementStyling(el2);
          return;
        }
      } catch {
      }
      try {
        window.keyPilot?.state?.setState?.({ _overlayUpdateTrigger: Date.now() });
      } catch {
      }
    }
    /**
     * Enable/disable DOM hover listener mode.
     * @param {boolean} enabled
     * @param {(el: HTMLElement|null) => void} [onChange]
     */
    setDomHoverListenersEnabled(enabled, onChange) {
      const next = !!enabled;
      this._domHoverEnabled = next;
      this._domHoverOnChange = typeof onChange === "function" ? onChange : null;
      this._rtreeDisabledByDomHover = true;
      if (this._domHoverUseDelegation) {
        if (next) {
          this._domHoverAttachDelegated();
          this._checkInitialMousePosition();
        } else {
          this._domHoverDetachDelegated();
        }
      } else {
        try {
          for (const el2 of this.observedInteractiveElements) {
            if (next) this._domHoverAttach(el2);
            else this._domHoverDetach(el2);
          }
        } catch {
        }
      }
      if (!next) {
        this._domHoveredElement = null;
        try {
          window.__KP_HOVERED_INTERACTIVE_EL = null;
        } catch {
        }
        if (typeof this._domHoverOnChange === "function") {
          try {
            this._domHoverOnChange(null);
          } catch {
          }
        }
      }
    }
    /**
     * Re-resolve clickable hover at a viewport point (after temporarily detaching listeners).
     * @param {number} clientX
     * @param {number} clientY
     */
    resyncDomHoverAtPoint(clientX, clientY) {
      if (!this._domHoverEnabled) return;
      const x = Number(clientX);
      const y = Number(clientY);
      if (!Number.isFinite(x) || !Number.isFinite(y)) return;
      let el2 = null;
      try {
        el2 = this.elementDetector?.deepElementFromPoint ? this.elementDetector.deepElementFromPoint(x, y) : document.elementFromPoint(x, y);
      } catch {
        el2 = null;
      }
      if (!el2 || el2.nodeType !== 1) {
        this._setDomHoveredElement(null);
        return;
      }
      try {
        if (this._isKeyPilotUiElement(el2)) {
          this._setDomHoveredElement(null);
          return;
        }
      } catch {
      }
      this._resolveAndPublishDomHover(
        /** @type {Element} */
        el2,
        x,
        y,
        { leafChanged: true }
      );
    }
    /**
     * @returns {HTMLElement|null}
     */
    getDomHoveredElement() {
      return this._domHoveredElement && this._domHoveredElement.nodeType === 1 ? (
        /** @type {HTMLElement} */
        this._domHoveredElement
      ) : null;
    }
    _rectsRoughlyEqual(a, b, tolPx = 1) {
      const t = Math.max(0, Number(tolPx) || 0);
      if (!a || !b) return false;
      return Math.abs(a.left - b.left) <= t && Math.abs(a.top - b.top) <= t && Math.abs(a.right - b.right) <= t && Math.abs(a.bottom - b.bottom) <= t;
    }
    _domHoverShouldAttach(el2) {
      try {
        if (!el2 || el2.nodeType !== 1) return false;
        if (el2.tagName === "A") {
          const parent = el2.parentElement;
          if (parent && parent.nodeType === 1) {
            let parentMatches = false;
            try {
              parentMatches = !!(parent.matches && parent.matches(this.interactiveSelector));
            } catch {
              parentMatches = false;
            }
            if (parentMatches) {
              let r1 = null;
              let r2 = null;
              try {
                r1 = el2.getBoundingClientRect();
              } catch {
                r1 = null;
              }
              try {
                r2 = parent.getBoundingClientRect();
              } catch {
                r2 = null;
              }
              if (r1 && r2 && this._rectsRoughlyEqual(r1, r2, 1)) return false;
            }
          }
        }
        return true;
      } catch {
        return false;
      }
    }
    _domHoverAttach(el2) {
      if (this._domHoverUseDelegation) return;
      if (!this._domHoverEnabled) return;
      if (!el2 || el2.nodeType !== 1) return;
      if (this._domHoverAttachedElements.has(el2)) return;
      if (!this._domHoverShouldAttach(el2)) {
        this._domHoverMetrics.skipped++;
        return;
      }
      try {
        el2.addEventListener("mouseenter", this._boundDomHoverEnter, true);
        el2.addEventListener("mouseleave", this._boundDomHoverLeave, true);
        this._domHoverAttachedElements.add(el2);
        this._domHoverMetrics.attached++;
      } catch {
      }
    }
    _domHoverDetach(el2) {
      if (this._domHoverUseDelegation) return;
      if (!el2 || el2.nodeType !== 1) return;
      if (!this._domHoverAttachedElements.has(el2)) return;
      try {
        el2.removeEventListener("mouseenter", this._boundDomHoverEnter, true);
        el2.removeEventListener("mouseleave", this._boundDomHoverLeave, true);
      } catch {
      }
      this._domHoverAttachedElements.delete(el2);
      if (this._domHoveredElement === el2) {
        this._domHoveredElement = null;
        try {
          window.__KP_HOVERED_INTERACTIVE_EL = null;
        } catch {
        }
        if (typeof this._domHoverOnChange === "function") {
          try {
            this._domHoverOnChange(null);
          } catch {
          }
        }
      }
    }
    _isKeyPilotUiElement(el2) {
      try {
        if (!el2 || el2 === document.documentElement || el2 === document.body) return false;
        if (isInteractiveKeyPilotOverlayElement(el2)) return false;
        if (isClickableKeyPilotChromeElement(el2)) return false;
        let n = el2;
        let guard = 0;
        while (n && n.nodeType === 1 && guard++ < 12) {
          if (n === document.documentElement || n === document.body) break;
          const id = typeof n.id === "string" ? n.id : "";
          if (id && id.startsWith("kpv2-") && id !== "kpv2-media-lib-overlay" && id !== "kpv2-page-media-overlay") {
            return true;
          }
          const cl = n.classList;
          if (cl && cl.length) {
            for (const c of cl) {
              if (typeof c !== "string" || !c.startsWith("kpv2-")) continue;
              if (isInteractiveKeyPilotOverlayClass(c) || c === "kpv2-cursor-hidden" || c === "kpv2-focus" || c === "kpv2-delete" || c === "kpv2-highlight" || c === "kpv2-hidden" || c === "kpv2-cols" || c === "kpv2-cols-active" || c === "kpv2-cols-page" || c === "kpv2-inspector" || c === "kpv2-inspector-picked" || c.startsWith("kpv2-text-") || c.startsWith("kpv2-focus-") && !c.includes("overlay")) {
                continue;
              }
              return true;
            }
          }
          n = n.parentElement;
        }
      } catch {
      }
      return false;
    }
    _domHoverAttachDelegated() {
      if (!this._domHoverEnabled) return;
      if (this._domHoverDelegationAttached) return;
      try {
        const hasPointer = typeof PointerEvent !== "undefined";
        if (hasPointer) {
          window.addEventListener("pointerover", this._boundDocPointerOver, true);
          window.addEventListener("pointerout", this._boundDocPointerOut, true);
          window.addEventListener("pointermove", this._boundDocPointerMoveReheal, { capture: true, passive: true });
        } else {
          window.addEventListener("mouseover", this._boundDocPointerOver, true);
          window.addEventListener("mouseout", this._boundDocPointerOut, true);
          window.addEventListener("mousemove", this._boundDocPointerMoveReheal, { capture: true, passive: true });
        }
        window.addEventListener("blur", this._boundWindowBlur, true);
        this._domHoverUsesPointer = hasPointer;
        this._domHoverDelegationAttached = true;
        this._domHoverMetrics.delegated++;
      } catch {
      }
    }
    _domHoverDetachDelegated() {
      if (!this._domHoverDelegationAttached) return;
      try {
        if (this._domHoverUsesPointer !== false) {
          window.removeEventListener("pointerover", this._boundDocPointerOver, true);
          window.removeEventListener("pointerout", this._boundDocPointerOut, true);
          window.removeEventListener("pointermove", this._boundDocPointerMoveReheal, { capture: true, passive: true });
        }
        if (this._domHoverUsesPointer !== true) {
          window.removeEventListener("mouseover", this._boundDocPointerOver, true);
          window.removeEventListener("mouseout", this._boundDocPointerOut, true);
          window.removeEventListener("mousemove", this._boundDocPointerMoveReheal, { capture: true, passive: true });
        }
        window.removeEventListener("blur", this._boundWindowBlur, true);
      } catch {
      }
      if (this._domHoverRehealRAF) {
        try {
          cancelAnimationFrame(this._domHoverRehealRAF);
        } catch {
        }
        this._domHoverRehealRAF = 0;
      }
      this._domHoverDelegationAttached = false;
    }
    /**
     * Check if mouse is currently positioned over a clickable element when DOM hover mode is enabled
     * Adds a one-time mousemove listener to capture the first mouse movement
     */
    _checkInitialMousePosition() {
      if (!this._domHoverEnabled) return;
      const handleFirstMousemove = (e) => {
        try {
          window.removeEventListener("mousemove", handleFirstMousemove, true);
          const mouseX = e.clientX;
          const mouseY = e.clientY;
          const elementAtPoint = this.elementDetector?.deepElementFromPoint ? this.elementDetector.deepElementFromPoint(mouseX, mouseY) : document.elementFromPoint(mouseX, mouseY);
          if (!elementAtPoint || elementAtPoint.nodeType !== 1) return;
          if (this._isKeyPilotUiElement(elementAtPoint)) return;
          let clickable = null;
          try {
            clickable = this.elementDetector?.resolveHoverFocusTarget ? this.elementDetector.resolveHoverFocusTarget(
              elementAtPoint,
              this._domHoveredElement,
              mouseX,
              mouseY
            ) : this.elementDetector?.findClickable ? this.elementDetector.findClickable(elementAtPoint) : elementAtPoint;
          } catch {
            clickable = elementAtPoint;
          }
          if (clickable) clickable = this._findParentContainerForClickable(clickable);
          if (!clickable && elementAtPoint.getRootNode() instanceof ShadowRoot) {
            try {
              const shadowElements = this.queryInteractiveAtPoint(mouseX, mouseY, 20);
              if (shadowElements.length > 0) {
                const leaf = shadowElements[0];
                clickable = this.elementDetector?.resolveHoverFocusTarget ? this.elementDetector.resolveHoverFocusTarget(
                  leaf,
                  this._domHoveredElement,
                  mouseX,
                  mouseY
                ) : leaf;
              }
            } catch (error) {
              if (window.KEYPILOT_DEBUG) {
                console.warn("[KeyPilot] Shadow-piercing query failed:", error);
              }
            }
          }
          if (clickable && clickable.nodeType === 1) {
            let finalClickable = (
              /** @type {HTMLElement} */
              clickable
            );
            try {
              if (finalClickable.tagName === "HTML" || finalClickable.tagName === "BODY") {
                finalClickable = null;
              }
            } catch {
            }
            if (!finalClickable) return;
            if (finalClickable !== this._domHoveredElement) {
              this._domHoveredElement = finalClickable;
              try {
                window.__KP_HOVERED_INTERACTIVE_EL = finalClickable;
              } catch {
              }
              if (typeof this._domHoverOnChange === "function") {
                try {
                  this._domHoverOnChange(finalClickable);
                } catch {
                }
              }
            }
          }
        } catch (error) {
          if (window.KEYPILOT_DEBUG) {
            console.warn("[KeyPilot] Error in first mousemove check:", error);
          }
        }
      };
      window.addEventListener("mousemove", handleFirstMousemove, true);
    }
    // =============================================================================
    // COMPLEX PAGE DETECTION - Adaptive IO behavior for performance optimization
    // Detects complex pages (Twitter, Facebook, etc.) and adapts IO settings accordingly
    // =============================================================================
    /**
     * Analyze page complexity and determine optimal IO settings
     */
    analyzePageComplexity() {
      const now = Date.now();
      if (this.complexPageDetector.isAnalyzed && now - this.complexPageDetector.lastAnalysis < this.complexPageDetector.analysisInterval) {
        return this.complexPageDetector.complexityLevel;
      }
      this.performStaticAnalysis();
      this.performDynamicAnalysis();
      this.determineComplexityLevel();
      this.adaptIOStrategy();
      this.complexPageDetector.isAnalyzed = true;
      this.complexPageDetector.lastAnalysis = now;
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Complex page analysis completed:", {
          complexityLevel: this.complexPageDetector.complexityLevel,
          staticMetrics: this.complexPageDetector.staticMetrics,
          dynamicMetrics: this.complexPageDetector.dynamicMetrics,
          ioAdaptation: this.complexPageDetector.ioAdaptation
        });
      }
      return this.complexPageDetector.complexityLevel;
    }
    /**
     * Static analysis: URL patterns, initial DOM structure, known complex sites
     */
    performStaticAnalysis() {
      const url = window.location.href;
      const hostname = window.location.hostname;
      this.complexPageDetector.staticMetrics.urlPatterns = [];
      if (url.includes("twitter.com") || url.includes("x.com")) {
        this.complexPageDetector.staticMetrics.urlPatterns.push("twitter");
        this.complexPageDetector.staticMetrics.isSocialMedia = true;
      }
      if (url.includes("facebook.com") || url.includes("instagram.com")) {
        this.complexPageDetector.staticMetrics.urlPatterns.push("facebook");
        this.complexPageDetector.staticMetrics.isSocialMedia = true;
      }
      if (url.includes("reddit.com")) {
        this.complexPageDetector.staticMetrics.urlPatterns.push("reddit");
        this.complexPageDetector.staticMetrics.isSocialMedia = true;
      }
      this.complexPageDetector.staticMetrics.hasInfiniteScroll = hostname.includes("twitter") || hostname.includes("x.com") || hostname.includes("facebook") || hostname.includes("instagram") || hostname.includes("reddit");
    }
    /**
     * Dynamic analysis: Current DOM state, performance metrics, user interaction patterns
     */
    performDynamicAnalysis() {
      this.complexPageDetector.dynamicMetrics.observerCount = this.observedInteractiveElements.size;
      this.complexPageDetector.dynamicMetrics.visibleCount = this.visibleInteractiveElements.size;
      this.complexPageDetector.dynamicMetrics.rbushItems = this._rtreeItemsByElement?.size || 0;
      this.complexPageDetector.dynamicMetrics.pendingMutations = (this._pendingAddedRoots?.size || 0) + (this._pendingRemovedRoots?.length || 0) + (this._pendingAttributeTargets?.size || 0);
      const queries = Number(this.metrics?.rtreeQueries) || 0;
      const hits = Number(this.metrics?.rtreeHits) || 0;
      this.complexPageDetector.dynamicMetrics.rtreeHitRate = queries > 0 ? hits / queries : 0;
    }
    /**
     * Determine complexity level based on all metrics
     */
    determineComplexityLevel() {
      const staticMetrics = this.complexPageDetector.staticMetrics;
      const dynamicMetrics = this.complexPageDetector.dynamicMetrics;
      let complexityScore = 0;
      if (staticMetrics.isSocialMedia) complexityScore += 30;
      if (staticMetrics.hasInfiniteScroll) complexityScore += 20;
      if (dynamicMetrics.observerCount > 200) complexityScore += 20;
      if (dynamicMetrics.observerCount > 600) complexityScore += 20;
      if (dynamicMetrics.rbushItems > 500) complexityScore += 10;
      if (dynamicMetrics.rbushItems > 2e3) complexityScore += 20;
      if (dynamicMetrics.pendingMutations > 200) complexityScore += 15;
      if (dynamicMetrics.pendingMutations > 1e3) complexityScore += 25;
      if (complexityScore >= 50) {
        this.complexPageDetector.complexityLevel = "high";
      } else if (complexityScore >= 25) {
        this.complexPageDetector.complexityLevel = "medium";
      } else {
        this.complexPageDetector.complexityLevel = "low";
      }
    }
    /**
     * Adapt IO strategy based on complexity level
     */
    adaptIOStrategy() {
      const level = this.complexPageDetector.complexityLevel;
      const adaptation = this.complexPageDetector.ioAdaptation;
      switch (level) {
        case "high":
          adaptation.rootMargin = "25px";
          adaptation.threshold = [0, 1];
          adaptation.maxObservations = 200;
          adaptation.spatialCullDistance = 100;
          adaptation.batchSize = 20;
          break;
        case "medium":
          adaptation.rootMargin = "50px";
          adaptation.threshold = [0, 0.5, 1];
          adaptation.maxObservations = 500;
          adaptation.spatialCullDistance = 150;
          adaptation.batchSize = 30;
          break;
        case "low":
        default:
          adaptation.rootMargin = "100px";
          adaptation.threshold = [0, 0.1, 0.5, 1];
          adaptation.maxObservations = 1e3;
          adaptation.spatialCullDistance = 200;
          adaptation.batchSize = 50;
          break;
      }
    }
    /**
     * Check if current page is complex
     */
    isComplexPage() {
      return this.analyzePageComplexity() !== "low";
    }
    /**
     * Get current IO adaptation settings
     */
    getIOAdaptation() {
      return this.complexPageDetector.ioAdaptation;
    }
    /**
     * Get extended viewport bounds for spatial culling
     * @param {number} margin - Additional margin around viewport in pixels
     * @returns {Object} - Bounds object with left, top, right, bottom
     */
    getViewportBounds(margin = 200) {
      const viewportWidth = window.innerWidth;
      const viewportHeight = window.innerHeight;
      const scrollX = window.pageXOffset || document.documentElement.scrollLeft || 0;
      const scrollY = window.pageYOffset || document.documentElement.scrollTop || 0;
      return {
        left: scrollX - margin,
        top: scrollY - margin,
        right: scrollX + viewportWidth + margin,
        bottom: scrollY + viewportHeight + margin,
        width: viewportWidth + 2 * margin,
        height: viewportHeight + 2 * margin
      };
    }
    /**
     * Find interactive elements within spatial bounds (for complex page optimization)
     * @param {Object} bounds - Viewport bounds with left, top, right, bottom
     * @param {number} maxElements - Maximum number of elements to return
     * @returns {Array} - Array of elements within bounds
     */
    findElementsInSpatialBounds(bounds, maxElements = 500) {
      const interactiveElements = document.querySelectorAll(this.interactiveSelector);
      const elementsInBounds = [];
      for (const element of interactiveElements) {
        if (elementsInBounds.length >= maxElements) break;
        try {
          const rect = element.getBoundingClientRect();
          const scrollX = window.pageXOffset || document.documentElement.scrollLeft || 0;
          const scrollY = window.pageYOffset || document.documentElement.scrollTop || 0;
          const elementLeft = rect.left + scrollX;
          const elementTop = rect.top + scrollY;
          const elementRight = rect.right + scrollX;
          const elementBottom = rect.bottom + scrollY;
          if (elementRight > bounds.left && elementLeft < bounds.right && elementBottom > bounds.top && elementTop < bounds.bottom) {
            elementsInBounds.push(element);
          }
        } catch (error) {
          continue;
        }
      }
      return elementsInBounds;
    }
    /**
     * Periodically cull observations for elements outside viewport (complex pages only)
     */
    startSpatialCulling() {
      if (!this.isComplexPage()) return;
      if (this._spatialCullingInterval) {
        clearInterval(this._spatialCullingInterval);
      }
      const adaptation = this.getIOAdaptation();
      this._spatialCullingInterval = setInterval(() => {
        if (!this.interactiveObserver || !this.isComplexPage()) {
          this.stopSpatialCulling();
          return;
        }
        const viewportBounds = this.getViewportBounds(adaptation.spatialCullDistance);
        const elementsToCull = [];
        for (const element of this.observedInteractiveElements) {
          try {
            const rect = element.getBoundingClientRect();
            const scrollX = window.pageXOffset || document.documentElement.scrollLeft || 0;
            const scrollY = window.pageYOffset || document.documentElement.scrollTop || 0;
            const elementLeft = rect.left + scrollX;
            const elementTop = rect.top + scrollY;
            const elementRight = rect.right + scrollX;
            const elementBottom = rect.bottom + scrollY;
            if (elementRight < viewportBounds.left || elementLeft > viewportBounds.right || elementBottom < viewportBounds.top || elementTop > viewportBounds.bottom) {
              elementsToCull.push(element);
            }
          } catch (error) {
            elementsToCull.push(element);
          }
        }
        if (elementsToCull.length > 0) {
          elementsToCull.forEach((element) => {
            this.unobserveInteractiveElement(element);
          });
          this.metrics.culledCount = elementsToCull.length;
          this.metrics.totalCulled += elementsToCull.length;
          if (window.KEYPILOT_DEBUG) {
            console.log("[KeyPilot Debug] Spatial culling removed observations:", {
              culledCount: elementsToCull.length,
              totalCulled: this.metrics.totalCulled,
              remainingObservations: this.observedInteractiveElements.size
            });
          }
        }
      }, 3e3);
    }
    /**
     * Stop spatial culling
     */
    stopSpatialCulling() {
      if (this._spatialCullingInterval) {
        clearInterval(this._spatialCullingInterval);
        this._spatialCullingInterval = null;
      }
    }
    async init() {
      await this.setupSpatialIndex();
      this.setupOverlayObserver();
      if (FEATURE_FLAGS.ENABLE_INTERACTIVE_DISCOVERY) {
        this.setupInteractiveElementObserver();
        this.setupMutationObserver();
        if (this.interactiveObserver) {
          this.startPeriodicCacheUpdate();
          if (this.isComplexPage()) {
            this.startSpatialCulling();
          }
        }
      } else if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Interactive discovery skipped (DOM-hover path)");
      }
    }
    async setupSpatialIndex() {
      this._rtree = null;
      this._rtreeReady = false;
      this._rtreeDisabledByDomHover = true;
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] RBush spatial index skipped - DOM-hover only targeting");
      }
    }
    /**
     * @deprecated RBush retired; no-op kept for API compatibility until tree-shake.
     */
    waitForRBush() {
      return Promise.resolve();
    }
    setupMutationObserver() {
      if (!window.MutationObserver) return;
      if (this.mutationObserver) return;
      try {
        this.mutationObserver = new MutationObserver((mutations) => {
          let sawWork = false;
          for (const mutation of mutations) {
            if (mutation.type === "childList") {
              for (const n of mutation.addedNodes) {
                if (n && n.nodeType === 1) {
                  this._pendingAddedRoots.add(n);
                  sawWork = true;
                }
              }
              for (const n of mutation.removedNodes) {
                if (n && n.nodeType === 1) {
                  this._pendingRemovedRoots.push(n);
                  sawWork = true;
                }
              }
            } else if (mutation.type === "attributes") {
              const t = mutation.target;
              if (t && t.nodeType === 1) {
                this._pendingAttributeTargets.add(t);
                sawWork = true;
              }
            }
          }
          if (sawWork) {
            this.scheduleProcessMutations();
          }
        });
        this.mutationObserver.observe(document, {
          childList: true,
          subtree: true,
          attributes: true,
          attributeFilter: ["href", "role", "onclick", "contenteditable", "aria-disabled", "disabled"]
        });
      } catch (e) {
        console.warn("[KeyPilot] Failed to set up MutationObserver for interactive discovery:", e);
        this.mutationObserver = null;
      }
    }
    scheduleProcessMutations() {
      if (this._mutationProcessScheduled) return;
      this._mutationProcessScheduled = true;
      const run = (deadline) => {
        this._mutationProcessScheduled = false;
        this._mutationIdleHandle = null;
        this.processPendingMutations(deadline);
      };
      if (typeof window.requestIdleCallback === "function") {
        this._mutationIdleHandle = window.requestIdleCallback(run, { timeout: 500 });
      } else {
        this._mutationIdleHandle = window.setTimeout(() => run({ timeRemaining: () => 0 }), 0);
      }
    }
    processPendingMutations(deadline) {
      if (!this.interactiveObserver) {
        this._pendingAddedRoots.clear();
        this._pendingRemovedRoots.length = 0;
        this._pendingAttributeTargets.clear();
        return;
      }
      const timeRemaining = typeof deadline?.timeRemaining === "function" ? () => deadline.timeRemaining() : () => 0;
      while (this._pendingRemovedRoots.length > 0) {
        const root = this._pendingRemovedRoots.pop();
        this.unobserveInteractiveInSubtree(root);
        if (timeRemaining() < 2) break;
      }
      if (timeRemaining() >= 2 && this._pendingAttributeTargets.size > 0) {
        const batch = [];
        for (const el2 of this._pendingAttributeTargets) {
          batch.push(el2);
          this._pendingAttributeTargets.delete(el2);
          if (batch.length >= 50) break;
        }
        for (const el2 of batch) {
          this.refreshInteractiveObservationForElement(el2);
          if (timeRemaining() < 2) break;
        }
      }
      if (timeRemaining() >= 2 && this._pendingAddedRoots.size > 0) {
        const batchRoots = [];
        for (const r of this._pendingAddedRoots) {
          batchRoots.push(r);
          this._pendingAddedRoots.delete(r);
          if (batchRoots.length >= 10) break;
        }
        for (const r of batchRoots) {
          this.observeInteractiveInSubtree(r);
          if (timeRemaining() < 2) break;
        }
      }
      if (this._pendingRemovedRoots.length > 0 || this._pendingAddedRoots.size > 0 || this._pendingAttributeTargets.size > 0) {
        this.scheduleProcessMutations();
      }
    }
    refreshInteractiveObservationForElement(el2) {
      if (!el2 || el2.nodeType !== 1) return;
      let shouldObserve = false;
      try {
        shouldObserve = !!(el2.matches && el2.matches(this.interactiveSelector));
      } catch {
        shouldObserve = false;
      }
      if (!shouldObserve) {
        try {
          shouldObserve = !!this.elementDetector?.isLikelyInteractive?.(el2);
        } catch {
          shouldObserve = false;
        }
      }
      if (shouldObserve) {
        this.observeInteractiveElement(el2);
      } else {
        this.unobserveInteractiveElement(el2);
      }
    }
    observeInteractiveElement(el2) {
      if (!this.interactiveObserver || !el2 || el2.nodeType !== 1) return;
      if (this.observedInteractiveElements.has(el2)) return;
      try {
        this.interactiveObserver.observe(el2);
        this.observedInteractiveElements.add(el2);
        this._domHoverAttach(el2);
      } catch {
      }
    }
    unobserveInteractiveElement(el2) {
      if (!this.interactiveObserver || !el2 || el2.nodeType !== 1) return;
      if (!this.observedInteractiveElements.has(el2)) return;
      try {
        this.interactiveObserver.unobserve(el2);
      } catch {
      }
      this._domHoverDetach(el2);
      this.observedInteractiveElements.delete(el2);
      this.visibleInteractiveElements.delete(el2);
      this.elementPositionCache.delete(el2);
      this._rtreeRemoveElement(el2);
    }
    observeInteractiveInSubtree(root) {
      if (!root || root.nodeType !== 1) return;
      try {
        if (root.matches && root.matches(this.interactiveSelector)) {
          this.observeInteractiveElement(root);
        }
      } catch {
      }
      try {
        if (root.querySelectorAll) {
          const matches = root.querySelectorAll(this.interactiveSelector);
          matches.forEach((el2) => this.observeInteractiveElement(el2));
        }
      } catch {
      }
    }
    unobserveInteractiveInSubtree(root) {
      if (!root || root.nodeType !== 1) return;
      this.unobserveInteractiveElement(root);
      try {
        if (root.querySelectorAll) {
          const matches = root.querySelectorAll(this.interactiveSelector);
          matches.forEach((el2) => this.unobserveInteractiveElement(el2));
        }
      } catch {
      }
    }
    setupInteractiveElementObserver() {
      this.analyzePageComplexity();
      const adaptation = this.getIOAdaptation();
      try {
        this.interactiveObserver = new IntersectionObserver(
          (entries) => {
            this.metrics.observerUpdates++;
            entries.forEach((entry) => {
              const element = entry.target;
              if (entry.isIntersecting) {
                this.visibleInteractiveElements.add(element);
                this.updateElementPositionCache(element, element.getBoundingClientRect());
              } else {
                this.visibleInteractiveElements.delete(element);
                this.elementPositionCache.delete(element);
                this._rtreeRemoveElement(element);
              }
            });
          },
          {
            // Adaptive root margin based on page complexity
            rootMargin: adaptation.rootMargin,
            // Adaptive thresholds based on page complexity
            threshold: adaptation.threshold
          }
        );
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] IntersectionObserver created with adaptive settings:", {
            complexityLevel: this.complexPageDetector.complexityLevel,
            rootMargin: adaptation.rootMargin,
            threshold: adaptation.threshold,
            maxObservations: adaptation.maxObservations
          });
        }
      } catch (error) {
        console.warn("[KeyPilot] Failed to create IntersectionObserver for interactive elements:", error);
        this.interactiveObserver = null;
      }
    }
    setupOverlayObserver() {
      try {
        this.overlayObserver = new IntersectionObserver(
          (entries) => {
            entries.forEach((entry) => {
              const overlay = entry.target;
              if (entry.intersectionRatio === 0) {
                overlay.style.visibility = "hidden";
              } else {
                overlay.style.visibility = "visible";
              }
            });
          },
          {
            rootMargin: "50px",
            threshold: [0, 1]
          }
        );
      } catch (error) {
        console.warn("[KeyPilot] Failed to create IntersectionObserver for overlays:", error);
        this.overlayObserver = null;
      }
    }
    startPeriodicCacheUpdate() {
      this.scheduleDiscoverInteractiveElements();
    }
    scheduleDiscoverInteractiveElements() {
      if (!this.interactiveObserver) return;
      if (this._discoverScheduled) return;
      this._discoverScheduled = true;
      const run = (deadline) => {
        this._discoverScheduled = false;
        this._discoverIdleHandle = null;
        try {
          this.discoverInteractiveElements(deadline);
        } catch (e) {
          console.warn("[KeyPilot] Failed to discover interactive elements:", e);
        }
      };
      if (typeof window.requestIdleCallback === "function") {
        this._discoverIdleHandle = window.requestIdleCallback(run, { timeout: 1e3 });
      } else {
        this._discoverIdleHandle = window.setTimeout(() => run({ timeRemaining: () => 0 }), 0);
      }
    }
    resetDiscoveryAndSchedule() {
      this._discoverWalker = null;
      this._discoverCursor = null;
      this._discoverDone = false;
      this._discoverScheduled = false;
      this.scheduleDiscoverInteractiveElements();
      this.refreshVisibleElementPositions();
    }
    refreshVisibleElementPositions() {
      if (!this._rtreeEnabled()) return;
      try {
        for (const element of this.visibleInteractiveElements) {
          if (!element || element.nodeType !== 1) continue;
          try {
            const rect = element.getBoundingClientRect();
            this.updateElementPositionCache(element, rect);
          } catch {
          }
        }
      } catch {
      }
    }
    _ensureDiscoverWalker() {
      if (this._discoverWalker && this._discoverCursor) return;
      try {
        const root = document.body || document.documentElement;
        if (!root) return;
        this._discoverWalker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
        this._discoverCursor = this._discoverWalker.currentNode;
      } catch {
        this._discoverWalker = null;
        this._discoverCursor = null;
      }
    }
    discoverInteractiveElements(deadline) {
      if (!this.interactiveObserver) {
        return;
      }
      const adaptation = this.getIOAdaptation();
      const timeRemaining = typeof deadline?.timeRemaining === "function" ? () => deadline.timeRemaining() : () => 0;
      const cap = Math.max(0, Number(adaptation.maxObservations) || 0);
      if (cap > 0 && this.observedInteractiveElements.size >= cap) {
        this._discoverDone = true;
        return;
      }
      this._ensureDiscoverWalker();
      if (!this._discoverWalker) return;
      const margin = Math.max(0, Number(adaptation.spatialCullDistance) || 0);
      const vw = window.innerWidth || 0;
      const vh = window.innerHeight || 0;
      let observedThisSlice = 0;
      const maxPerSlice = Math.max(10, Number(adaptation.batchSize) || 50);
      while (this._discoverCursor) {
        if (observedThisSlice >= maxPerSlice) break;
        if (observedThisSlice > 0 && timeRemaining() < 2) break;
        const el2 = this._discoverCursor;
        try {
          this._discoverCursor = this._discoverWalker.nextNode();
        } catch {
          this._discoverCursor = null;
        }
        if (!el2 || el2.nodeType !== 1) continue;
        if (cap > 0 && this.observedInteractiveElements.size >= cap) {
          this._discoverDone = true;
          break;
        }
        if (this.isElementObserved(el2)) continue;
        let matches = false;
        try {
          matches = !!(el2.matches && el2.matches(this.interactiveSelector));
        } catch {
          matches = false;
        }
        if (!matches) continue;
        let rect;
        try {
          rect = el2.getBoundingClientRect();
        } catch {
          rect = null;
        }
        if (!rect) continue;
        if (rect.bottom < -margin || rect.top > vh + margin || rect.right < -margin || rect.left > vw + margin) continue;
        this.observeInteractiveElement(el2);
        observedThisSlice++;
      }
      if (timeRemaining() > 1) {
        const shadowDiscovered = this.discoverShadowDOMElements(maxPerSlice - observedThisSlice, timeRemaining);
        observedThisSlice += shadowDiscovered;
      }
      if (!this._discoverDone && this._discoverCursor && (cap === 0 || this.observedInteractiveElements.size < cap)) {
        this.scheduleDiscoverInteractiveElements();
      } else {
        this._discoverDone = true;
      }
      this.cleanupRemovedElements();
    }
    isElementObserved(element) {
      return this.observedInteractiveElements.has(element) || this.visibleInteractiveElements.has(element) || this.elementPositionCache.has(element);
    }
    cleanupRemovedElements() {
      if (!this.interactiveObserver) {
        return;
      }
      for (const element of this.observedInteractiveElements) {
        try {
          if (element && element.isConnected === false) {
            this.unobserveInteractiveElement(element);
            continue;
          }
        } catch {
        }
        if (!document.contains(element)) {
          this.unobserveInteractiveElement(element);
        }
      }
    }
    updateElementPositionCache(element, rect) {
      this.elementPositionCache.set(element, {
        left: rect.left,
        top: rect.top,
        right: rect.right,
        bottom: rect.bottom,
        width: rect.width,
        height: rect.height,
        timestamp: Date.now()
      });
      this._rtreeUpsertElementRect(element);
    }
    _rtreeEnabled() {
      return false;
    }
    _getScrollXY() {
      const scrollX = typeof window !== "undefined" && typeof window.scrollX === "number" ? window.scrollX : window.pageXOffset || 0;
      const scrollY = typeof window !== "undefined" && typeof window.scrollY === "number" ? window.scrollY : window.pageYOffset || 0;
      return { scrollX, scrollY };
    }
    _destIndexAdd(element, destination) {
      if (!destination) return;
      let set = this._destIndex.get(destination);
      if (!set) {
        set = /* @__PURE__ */ new Set();
        this._destIndex.set(destination, set);
      }
      set.add(element);
    }
    _destIndexRemove(element, destination) {
      if (!destination) return;
      const set = this._destIndex.get(destination);
      if (!set) return;
      set.delete(element);
      if (set.size === 0) this._destIndex.delete(destination);
    }
    /**
     * Compute z-index for an element (including handling auto and stacking contexts)
     * @param {HTMLElement} element
     * @returns {number}
     */
    _computeZIndex(element) {
      if (!element || element.nodeType !== 1) return 0;
      try {
        const style = window.getComputedStyle(element);
        const zIndex = style.zIndex;
        if (zIndex === "auto") return 0;
        const parsed = parseInt(zIndex, 10);
        return Number.isFinite(parsed) ? parsed : 0;
      } catch {
        return 0;
      }
    }
    _rtreeUpsertElementRect(element) {
      if (!this._rtreeEnabled()) return;
      if (!element || element.nodeType !== 1) return;
      let clientRects;
      try {
        clientRects = element.getClientRects();
      } catch (e) {
        const rect = element.getBoundingClientRect();
        clientRects = rect ? [rect] : [];
      }
      if (!clientRects || clientRects.length === 0) return;
      let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
      for (const rect of clientRects) {
        minX = Math.min(minX, rect.left);
        minY = Math.min(minY, rect.top);
        maxX = Math.max(maxX, rect.right);
        maxY = Math.max(maxY, rect.bottom);
      }
      const { scrollX, scrollY } = this._getScrollXY();
      minX = Number(minX) + scrollX;
      minY = Number(minY) + scrollY;
      maxX = Number(maxX) + scrollX;
      maxY = Number(maxY) + scrollY;
      if (!Number.isFinite(minX) || !Number.isFinite(minY) || !Number.isFinite(maxX) || !Number.isFinite(maxY)) return;
      if (maxX <= minX || maxY <= minY) return;
      const zIndex = this._computeZIndex(element);
      const existing = this._rtreeItemsByElement.get(element);
      if (existing) {
        try {
          this._rtree.remove(existing);
        } catch {
        }
        const nextDest = this._getNormalizedDestination(element);
        const prevDest = existing.destination || "";
        if (prevDest !== nextDest) {
          this._destIndexRemove(element, prevDest);
          this._destIndexAdd(element, nextDest);
          existing.destination = nextDest;
        }
        existing.minX = minX;
        existing.minY = minY;
        existing.maxX = maxX;
        existing.maxY = maxY;
        existing.zIndex = zIndex;
        try {
          this._rtree.insert(existing);
        } catch {
        }
        this._rtreeVersion++;
        return;
      }
      const destination = this._getNormalizedDestination(element);
      if (destination) {
        const containingElements = this._rtree.search({
          minX: minX - 1,
          // Slight expansion to handle edge cases
          minY: minY - 1,
          maxX: maxX + 1,
          maxY: maxY + 1
        }).filter((item2) => {
          return item2.element !== element && // Not the same element
          item2.minX <= minX && item2.minY <= minY && item2.maxX >= maxX && item2.maxY >= maxY && item2.element.tagName === "A" && (item2.destination || "") === destination;
        });
        if (containingElements.length > 0) {
          if (window.KEYPILOT_DEBUG) {
            console.log("[KeyPilot Debug] Omitting enclosed link element:", {
              smallerElement: element,
              linkDestination: destination,
              enclosedBy: containingElements[0].element
            });
          }
          return;
        }
        try {
          const enclosedCandidates = this._rtree.search({
            minX: minX - 1,
            minY: minY - 1,
            maxX: maxX + 1,
            maxY: maxY + 1
          }) || [];
          for (const it of enclosedCandidates) {
            const el2 = it && it.element;
            if (!el2 || el2 === element) continue;
            if (it.minX >= minX && it.minY >= minY && it.maxX <= maxX && it.maxY <= maxY && (it.destination || "") === destination) {
              const strictlyInside = it.minX > minX || it.minY > minY || it.maxX < maxX || it.maxY < maxY;
              if (!strictlyInside) continue;
              this._rtreeRemoveElement(el2);
            }
          }
        } catch {
        }
      }
      if (element.tagName === "A") {
        try {
          const images = element.querySelectorAll("img");
          for (const img of images) {
            let imgRects;
            try {
              imgRects = img.getClientRects();
            } catch (e) {
              const imgRect = img.getBoundingClientRect();
              imgRects = imgRect ? [imgRect] : [];
            }
            if (imgRects && imgRects.length > 0) {
              let imgMinX = Infinity, imgMinY = Infinity, imgMaxX = -Infinity, imgMaxY = -Infinity;
              for (const rect of imgRects) {
                imgMinX = Math.min(imgMinX, rect.left);
                imgMinY = Math.min(imgMinY, rect.top);
                imgMaxX = Math.max(imgMaxX, rect.right);
                imgMaxY = Math.max(imgMaxY, rect.bottom);
              }
              imgMinX = imgMinX + scrollX;
              imgMinY = imgMinY + scrollY;
              imgMaxX = imgMaxX + scrollX;
              imgMaxY = imgMaxY + scrollY;
              if (imgMinX < minX) minX = imgMinX;
              if (imgMinY < minY) minY = imgMinY;
              if (imgMaxX > maxX) maxX = imgMaxX;
              if (imgMaxY > maxY) maxY = imgMaxY;
            }
          }
        } catch (e) {
        }
      }
      const item = { minX, minY, maxX, maxY, element, zIndex, destination };
      this._rtreeItemsByElement.set(element, item);
      try {
        this._rtree.insert(item);
      } catch {
      }
      this._destIndexAdd(element, destination);
      this._rtreeVersion++;
    }
    _rtreeRemoveElement(element) {
      if (!this._rtreeReady || !this._rtree) return;
      const item = this._rtreeItemsByElement.get(element);
      if (!item) return;
      this._rtreeItemsByElement.delete(element);
      try {
        this._rtree.remove(item);
      } catch {
      }
      try {
        this._destIndexRemove(element, item.destination || "");
      } catch {
      }
      this._rtreeVersion++;
    }
    _rectIntersects(a, b) {
      if (!a || !b) return false;
      return !(a.maxX <= b.minX || a.minX >= b.maxX || a.maxY <= b.minY || a.minY >= b.maxY);
    }
    _rectExpandedIntersects(a, b, tolPx = 0) {
      const t = Math.max(0, Number(tolPx) || 0);
      const aa = {
        minX: a.minX - t,
        minY: a.minY - t,
        maxX: a.maxX + t,
        maxY: a.maxY + t
      };
      return this._rectIntersects(aa, b);
    }
    _unionRects(items) {
      let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
      for (const it of items) {
        if (!it) continue;
        minX = Math.min(minX, Number(it.minX));
        minY = Math.min(minY, Number(it.minY));
        maxX = Math.max(maxX, Number(it.maxX));
        maxY = Math.max(maxY, Number(it.maxY));
      }
      if (!Number.isFinite(minX) || !Number.isFinite(minY) || !Number.isFinite(maxX) || !Number.isFinite(maxY)) return null;
      if (maxX <= minX || maxY <= minY) return null;
      return { minX, minY, maxX, maxY };
    }
    _rectContainsPoint(rect, px, py) {
      return px >= rect.minX && px <= rect.maxX && py >= rect.minY && py <= rect.maxY;
    }
    _toViewportRect(pageRect) {
      if (!pageRect) return null;
      const { scrollX, scrollY } = this._getScrollXY();
      const left = pageRect.minX - scrollX;
      const top = pageRect.minY - scrollY;
      const width = pageRect.maxX - pageRect.minX;
      const height = pageRect.maxY - pageRect.minY;
      if (!Number.isFinite(left) || !Number.isFinite(top) || !Number.isFinite(width) || !Number.isFinite(height)) return null;
      if (width <= 0 || height <= 0) return null;
      return { left, top, width, height };
    }
    /**
     * Compute a "display rectangle" for an element that may be part of a cluster
     * of adjacent/overlapping links with the same destination.
     *
     * This is display-only: we still select a real DOM element for click behavior.
     *
     * Safety rule:
     * - Do NOT return a coalesced rect if it would intersect any RBush item that has
     *   a different destination (conservative: avoids obscuring other links).
     *
     * @param {HTMLElement} element
     * @param {object} [opts]
     * @param {number} [opts.tolerancePx] proximity threshold for clustering
     * @returns {{left:number,top:number,width:number,height:number}|null}
     */
    getDisplayRectForElement(element, opts = {}) {
      if (!this._rtreeEnabled()) return null;
      if (!element || element.nodeType !== 1) return null;
      const destination = this._getNormalizedDestination(element);
      if (!destination) return null;
      if (this._displayRectCache && this._displayRectCache.version === this._rtreeVersion && this._displayRectCache.destination === destination && this._displayRectCache.anchorEl === element) {
        return this._toViewportRect(this._displayRectCache.pageRect) || null;
      }
      const anchorItem = this._rtreeItemsByElement.get(element);
      if (!anchorItem) return null;
      const tol = Math.max(0, Number(opts.tolerancePx));
      const tolerancePx = Number.isFinite(tol) ? tol : 6;
      const set = this._destIndex.get(destination);
      if (!set || set.size < 2) {
        this._displayRectCache = { version: this._rtreeVersion, destination, anchorEl: element, pageRect: anchorItem };
        return this._toViewportRect(anchorItem);
      }
      const items = [];
      for (const el2 of set) {
        const it = this._rtreeItemsByElement.get(el2);
        if (!it) continue;
        try {
          if (el2 && el2.isConnected === false) {
            this._rtreeRemoveElement(el2);
            continue;
          }
        } catch {
        }
        items.push(it);
      }
      if (items.length < 2) {
        this._displayRectCache = { version: this._rtreeVersion, destination, anchorEl: element, pageRect: anchorItem };
        return this._toViewportRect(anchorItem);
      }
      const visited = /* @__PURE__ */ new Set();
      const queue = [anchorItem];
      visited.add(anchorItem);
      while (queue.length) {
        const cur = queue.pop();
        for (const other of items) {
          if (visited.has(other)) continue;
          if (this._rectExpandedIntersects(cur, other, tolerancePx) || this._rectExpandedIntersects(other, cur, tolerancePx)) {
            visited.add(other);
            queue.push(other);
          }
        }
      }
      if (visited.size < 2) {
        this._displayRectCache = { version: this._rtreeVersion, destination, anchorEl: element, pageRect: anchorItem };
        return this._toViewportRect(anchorItem);
      }
      const component = Array.from(visited);
      const unionPageRect = this._unionRects(component);
      if (!unionPageRect) {
        this._displayRectCache = { version: this._rtreeVersion, destination, anchorEl: element, pageRect: anchorItem };
        return this._toViewportRect(anchorItem);
      }
      try {
        const hits = this._rtree.search(unionPageRect) || [];
        for (const it of hits) {
          if (!it || !it.element) continue;
          if (component.includes(it)) continue;
          const otherDest = it.destination || "";
          if (otherDest && otherDest !== destination) {
            this._displayRectCache = { version: this._rtreeVersion, destination, anchorEl: element, pageRect: anchorItem };
            return this._toViewportRect(anchorItem);
          }
        }
      } catch {
      }
      this._displayRectCache = { version: this._rtreeVersion, destination, anchorEl: element, pageRect: unionPageRect };
      return this._toViewportRect(unionPageRect);
    }
    // =============================================================================
    // OCCLUSION NOTE
    // We intentionally do NOT maintain custom “negative regions” in RBush anymore.
    //
    // Generalized occlusion is handled by pairing RBush (fast bbox candidate generation)
    // with a single DOM hit-test (`elementFromPoint` / `deepElementFromPoint`) at the
    // cursor. We only accept candidates that are in the ancestor chain of the topmost
    // hit-tested element. This naturally respects third-party modals, menus, lightboxes,
    // backdrops, and any other overlays.
    // =============================================================================
    // =============================================================================
    // RBUSH SPATIAL INDEX - Mouse coordinate detection functions
    // These functions use the RBush spatial index to quickly find interactive
    // elements at specific mouse coordinates for hover/focus detection
    // =============================================================================
    /**
     * MOUSE COORDINATE DETECTION: Query indexed interactive elements that intersect a point
     * Uses RBush spatial index for fast coordinate-based lookups during mouse movement
     * @param {number} x - X coordinate
     * @param {number} y - Y coordinate
     * @param {number} radiusPx - Optional radius for point queries
     * @returns {Array} - Array of interactive elements at the coordinates
     */
    queryInteractiveAtPoint(x, y, radiusPx = 0) {
      if (!this._rtreeEnabled()) return [];
      const px = Number(x);
      const py = Number(y);
      const r = Math.max(0, Number(radiusPx) || 0);
      if (!Number.isFinite(px) || !Number.isFinite(py)) return [];
      const scrollX = typeof window !== "undefined" && typeof window.scrollX === "number" ? window.scrollX : window.pageXOffset || 0;
      const scrollY = typeof window !== "undefined" && typeof window.scrollY === "number" ? window.scrollY : window.pageYOffset || 0;
      const pageX = px + scrollX;
      const pageY = py + scrollY;
      this.metrics.rtreeQueries++;
      const bbox = { minX: pageX - r, minY: pageY - r, maxX: pageX + r, maxY: pageY + r };
      let items = [];
      try {
        items = this._rtree.search(bbox) || [];
      } catch {
        items = [];
      }
      if (!items.length) return [];
      this.metrics.rtreeHits++;
      const out = [];
      for (const it of items) {
        const el2 = it && it.element;
        if (!el2 || el2.nodeType !== 1) continue;
        try {
          if (el2.isConnected === false) {
            this._rtreeRemoveElement(el2);
            continue;
          }
        } catch {
        }
        out.push(el2);
      }
      if (FEATURE_FLAGS.ENABLE_DEBUG_PANEL && out.length > 0) {
        this.showRBushDebugOverlays(out);
      }
      return out;
    }
    /**
     * Get z-index for an element from the rbush item (if available) or compute it
     * @param {HTMLElement} element
     * @returns {number}
     */
    getZIndexForElement(element) {
      if (!element) return 0;
      const item = this._rtreeItemsByElement.get(element);
      if (item && typeof item.zIndex === "number") {
        return item.zIndex;
      }
      return this._computeZIndex(element);
    }
    /**
     * Pick the best interactive element from a set of RBush candidates, gated by the
     * topmost DOM hit-test element under the cursor (`underEl`).
     *
     * Rules:
     * - If `underEl` exists, ONLY accept candidates that are on the ancestor chain of `underEl`
     *   (shadow-host aware). If none match, return null (prevents “clicking through” overlays).
     * - If `underEl` is null/unknown, fall back to smallest-area candidate.
     *
     * @param {HTMLElement[]} candidates
     * @param {HTMLElement|null} underEl
     * @returns {HTMLElement|null}
     */
    pickBestInteractiveFromCandidates(candidates, underEl) {
      if (!candidates || !candidates.length) return null;
      if (underEl && underEl.nodeType === 1) {
        const useSet = candidates.length > 16;
        const candSet = useSet ? new Set(candidates) : null;
        let n = underEl;
        let depth = 0;
        while (n && depth++ < 20) {
          if (useSet) {
            if (candSet.has(n)) return n;
          } else {
            if (candidates.includes(n)) return n;
          }
          try {
            const root = n.getRootNode && n.getRootNode();
            n = n.parentElement || (root instanceof ShadowRoot ? root.host : null);
          } catch {
            n = n.parentElement;
          }
        }
        return null;
      }
      let best = null;
      let bestArea = Infinity;
      for (const el2 of candidates) {
        const rect = this.elementPositionCache.get(el2);
        const w = rect && Number(rect.width);
        const h = rect && Number(rect.height);
        const area = (Number.isFinite(w) ? w : 0) * (Number.isFinite(h) ? h : 0);
        if (area > 0 && area < bestArea) {
          bestArea = area;
          best = el2;
        }
      }
      return best;
    }
    /**
     * MOUSE COORDINATE DETECTION: Best-effort mapping from cursor position to interactive element
     * Uses RBush spatial index as a fast pre-filter to find the most likely interactive element
     * at mouse coordinates, avoiding expensive DOM queries during mouse movement.
     *
     * Strategy:
     * - Query spatial index at point.
     * - Prefer a candidate that is `underEl` or an ancestor of `underEl` (shadow-host aware).
     * - If none match the `underEl` chain, return null (prevents “click through” on overlays).
     * - If `underEl` is null/unknown, fall back to smallest-area candidate (best-effort).
     */
    findBestInteractiveForUnderPoint({ x, y, underEl }) {
      if (!this._rtreeEnabled()) return null;
      const candidates = this.queryInteractiveAtPoint(x, y, 0);
      const best = this.pickBestInteractiveFromCandidates(candidates, underEl);
      return best || null;
    }
    /**
     * MOUSE COORDINATE DETECTION: Track element at mouse position for performance metrics and caching
     * Maintains RBush spatial index and performance tracking during mouse movement
     * Optionally accepts pre-computed values to avoid redundant DOM queries in hot paths
     */
    trackElementAtPoint(x, y, element = null, clickable = null) {
      let resolvedElement = null;
      let resolvedClickable = null;
      if (!element && !clickable) {
        resolvedElement = this.elementDetector.deepElementFromPoint(x, y);
        resolvedClickable = this.elementDetector.findClickable(resolvedElement);
      } else {
        resolvedElement = element || clickable || null;
        resolvedClickable = clickable || (resolvedElement ? this.elementDetector.findClickable(resolvedElement) : null);
      }
      if (resolvedClickable && this.visibleInteractiveElements.has(resolvedClickable)) {
        this.metrics.cacheHits++;
      } else {
        this.metrics.cacheMisses++;
      }
      if (resolvedClickable && this.interactiveObserver && !this.visibleInteractiveElements.has(resolvedClickable)) {
        let rect = null;
        try {
          rect = resolvedClickable.getBoundingClientRect();
        } catch {
          rect = null;
        }
        if (rect && rect.width > 0 && rect.height > 0 && rect.bottom > 0 && rect.right > 0 && rect.top < window.innerHeight && rect.left < window.innerWidth) {
          this.visibleInteractiveElements.add(resolvedClickable);
          try {
            this.interactiveObserver.observe(resolvedClickable);
          } catch {
          }
          this.updateElementPositionCache(resolvedClickable, rect);
        }
      }
      return resolvedClickable;
    }
    // Legacy method name for compatibility
    findInteractiveElementAtPoint(x, y) {
      return this.trackElementAtPoint(x, y);
    }
    isPointInRect(x, y, rect) {
      return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
    }
    rectsAreClose(rect1, rect2, tolerance = 5) {
      return Math.abs(rect1.left - rect2.left) <= tolerance && Math.abs(rect1.top - rect2.top) <= tolerance && Math.abs(rect1.width - rect2.width) <= tolerance && Math.abs(rect1.height - rect2.height) <= tolerance;
    }
    isElementVisible(element) {
      const rect = element.getBoundingClientRect();
      return rect.width > 0 && rect.height > 0 && rect.bottom > 0 && rect.right > 0 && rect.top < window.innerHeight && rect.left < window.innerWidth;
    }
    // Observe overlay elements for visibility optimization
    observeOverlay(overlayElement) {
      if (this.overlayObserver && overlayElement) {
        this.overlayObserver.observe(overlayElement);
      }
    }
    unobserveOverlay(overlayElement) {
      if (this.overlayObserver && overlayElement) {
        this.overlayObserver.unobserve(overlayElement);
      }
    }
    // Get performance metrics
    getMetrics() {
      const totalQueries = this.metrics.cacheHits + this.metrics.cacheMisses;
      const cacheHitRate = totalQueries > 0 ? (this.metrics.cacheHits / totalQueries * 100).toFixed(1) : 0;
      return {
        ...this.metrics,
        cacheHitRate: `${cacheHitRate}%`,
        visibleElements: this.visibleInteractiveElements.size,
        cachedPositions: this.elementPositionCache.size
      };
    }
    /**
     * Show blue debug overlays for elements returned from RBush tree queries
     * Only active when ENABLE_DEBUG_PANEL is true
     */
    showRBushDebugOverlays(elements) {
      if (!FEATURE_FLAGS.ENABLE_DEBUG_PANEL || !elements?.length) return;
      this.clearRBushDebugOverlays();
      elements.forEach((element) => {
        try {
          const rect = element.getBoundingClientRect();
          if (rect.width === 0 || rect.height === 0) return;
          const overlay = document.createElement("div");
          overlay.style.cssText = `
          position: fixed;
          left: ${rect.left}px;
          top: ${rect.top}px;
          width: ${rect.width}px;
          height: ${rect.height}px;
          background-color: rgba(0,120,255,0.2);
          border: 2px solid rgba(0,120,255,0.9);
          pointer-events: none;
          z-index: 2147483040;
          box-sizing: border-box;
        `;
          document.body.appendChild(overlay);
          this.rbushDebugOverlays.push(overlay);
        } catch (e) {
        }
      });
      if (this.rbushDebugOverlayTimeout) {
        clearTimeout(this.rbushDebugOverlayTimeout);
      }
      this.rbushDebugOverlayTimeout = setTimeout(() => {
        this.clearRBushDebugOverlays();
      }, 2e3);
    }
    /**
     * Clear all RBush debug overlays
     */
    clearRBushDebugOverlays() {
      this.rbushDebugOverlays.forEach((overlay) => {
        if (overlay && overlay.parentNode) {
          overlay.parentNode.removeChild(overlay);
        }
      });
      this.rbushDebugOverlays = [];
      if (this.rbushDebugOverlayTimeout) {
        clearTimeout(this.rbushDebugOverlayTimeout);
        this.rbushDebugOverlayTimeout = null;
      }
    }
    // Cleanup method
    cleanup() {
      try {
        this.setDomHoverListenersEnabled(false, null);
      } catch {
      }
      try {
        this._domHoverAttachedElements.clear();
      } catch {
      }
      if (this.mutationObserver) {
        try {
          this.mutationObserver.disconnect();
        } catch {
        }
        this.mutationObserver = null;
      }
      if (this._mutationIdleHandle) {
        try {
          if (typeof window.cancelIdleCallback === "function") {
            window.cancelIdleCallback(this._mutationIdleHandle);
          } else {
            clearTimeout(this._mutationIdleHandle);
          }
        } catch {
        }
        this._mutationIdleHandle = null;
      }
      this._pendingAddedRoots.clear();
      this._pendingRemovedRoots.length = 0;
      this._pendingAttributeTargets.clear();
      this._mutationProcessScheduled = false;
      if (this._discoverIdleHandle) {
        try {
          if (typeof window.cancelIdleCallback === "function") {
            window.cancelIdleCallback(this._discoverIdleHandle);
          } else {
            clearTimeout(this._discoverIdleHandle);
          }
        } catch {
        }
        this._discoverIdleHandle = null;
      }
      this._discoverScheduled = false;
      this._discoverWalker = null;
      this._discoverCursor = null;
      this._discoverDone = false;
      if (this.interactiveObserver) {
        this.interactiveObserver.disconnect();
        this.interactiveObserver = null;
      }
      if (this.overlayObserver) {
        this.overlayObserver.disconnect();
        this.overlayObserver = null;
      }
      if (this.cacheUpdateTimeout) {
        clearTimeout(this.cacheUpdateTimeout);
        this.cacheUpdateTimeout = null;
      }
      if (this.cacheUpdateInterval) {
        clearInterval(this.cacheUpdateInterval);
        this.cacheUpdateInterval = null;
      }
      this.visibleInteractiveElements.clear();
      this.elementPositionCache.clear();
      this.observedInteractiveElements.clear();
      if (this._rtree && this._rtree.clear) {
        try {
          this._rtree.clear();
        } catch {
        }
      }
      this._rtree = null;
      this._rtreeReady = false;
      this._rtreeItemsByElement.clear();
      this.stopSpatialCulling();
      this.clearRBushDebugOverlays();
    }
  };

  // src/modules/optimized-scroll-manager.js
  var OptimizedScrollManager = class {
    /**
     * @param {*} overlayManager
     * @param {*} stateManager
     * @param {{ onScrollFrame?: () => void }} [hooks] - optional live scroll hooks (e.g. highlight refresh)
     */
    constructor(overlayManager, stateManager, hooks = {}) {
      this.overlayManager = overlayManager;
      this.stateManager = stateManager;
      this.onScrollFrame = typeof hooks?.onScrollFrame === "function" ? hooks.onScrollFrame : null;
      this.isScrolling = false;
      this.scrollTimeout = null;
      this.scrollStartTime = 0;
      this.scrollObserver = null;
      this.scrollSensitiveElements = /* @__PURE__ */ new Set();
      this.throttledScrollHandler = this.throttle(this.handleScrollThrottled.bind(this), 16);
      this.boundScrollHandler = null;
      this.stateUnsubscribe = null;
      this.scrollMetrics = {
        scrollEvents: 0,
        overlayUpdates: 0,
        throttledCalls: 0,
        averageScrollDuration: 0
      };
    }
    init() {
      this.setupScrollObserver();
      this.setupScrollListeners();
      this.setupStateSubscription();
      this.observeCurrentStateElements();
    }
    /**
     * True when focus chrome is CSS on the element (scrolls with the page).
     * False when a fixed canvas/DOM overlay must be repositioned on scroll.
     * @returns {boolean}
     */
    _usesElementFocusStyling() {
      try {
        if (typeof this.overlayManager?.usesElementFocusStyling === "function") {
          return !!this.overlayManager.usesElementFocusStyling();
        }
        return !!this.overlayManager?._useDomHoverFocusColors;
      } catch {
        return true;
      }
    }
    observeCurrentStateElements() {
      const currentState = this.stateManager?.getState?.();
      if (!currentState) return;
      if (!this._usesElementFocusStyling()) {
        this.observeElementForScroll(currentState.focusEl);
      }
      this.observeElementForScroll(
        currentState.inspectorEl || currentState.deleteEl || currentState.colsEl
      );
    }
    setupScrollObserver() {
      this.scrollObserver = new IntersectionObserver(
        (entries) => {
          for (const entry of entries) {
            const element = entry.target;
            if (entry.isIntersecting) {
              this.scrollSensitiveElements.add(element);
            } else {
              this.scrollSensitiveElements.delete(element);
              this.hideFixedOverlaysForElement(element);
            }
          }
        },
        {
          rootMargin: "20px",
          threshold: [0, 1]
        }
      );
    }
    setupScrollListeners() {
      this.boundScrollHandler = this.handleScroll.bind(this);
      document.addEventListener("scroll", this.boundScrollHandler, {
        passive: true,
        capture: true
      });
    }
    setupStateSubscription() {
      this.stateUnsubscribe = this.stateManager.subscribe((newState, prevState) => {
        const elementFocus = this._usesElementFocusStyling();
        if (newState.focusEl !== prevState.focusEl) {
          if (prevState.focusEl) this.unobserveElementForScroll(prevState.focusEl);
          if (!elementFocus && newState.focusEl) {
            this.observeElementForScroll(newState.focusEl);
          }
        }
        const prevInsp = prevState.inspectorEl || prevState.deleteEl || prevState.colsEl;
        const nextInsp = newState.inspectorEl || newState.deleteEl || newState.colsEl;
        if (nextInsp !== prevInsp) {
          if (prevInsp) this.unobserveElementForScroll(prevInsp);
          if (nextInsp) this.observeElementForScroll(nextInsp);
        }
      });
    }
    handleScroll(_event) {
      this.scrollMetrics.scrollEvents++;
      if (!this.isScrolling) {
        this.isScrolling = true;
        this.scrollStartTime = performance.now();
        if (!this._usesElementFocusStyling()) {
          try {
            this.overlayManager?.setScrollPaintPreferA?.(true);
          } catch {
          }
        }
      }
      this.throttledScrollHandler();
      if (this.scrollTimeout) {
        clearTimeout(this.scrollTimeout);
      }
      this.scrollTimeout = setTimeout(() => {
        this.handleScrollEnd();
      }, 280);
    }
    handleScrollThrottled() {
      this.scrollMetrics.throttledCalls++;
      let currentState;
      try {
        currentState = this.stateManager.getState();
      } catch {
        return;
      }
      if (!currentState) return;
      const mode = currentState.mode;
      let didOverlayWork = false;
      if (this.onScrollFrame) {
        try {
          this.onScrollFrame();
          didOverlayWork = true;
        } catch {
        }
      }
      const inspectorEl = currentState.inspectorEl || currentState.deleteEl || currentState.colsEl;
      if (inspectorEl && inspectorEl.isConnected) {
        this._updateInspectorOverlay(inspectorEl);
        didOverlayWork = true;
      }
      try {
        const kp = typeof window !== "undefined" ? window.__KeyPilotInstance : null;
        if (kp?.inspector?.isCumulative?.()) {
          const union = kp.inspector.refreshUnionRect?.();
          const picks = kp.inspector.getPickedElements?.() || [];
          kp.overlayManager?.updateInspectorPickedOverlays?.(
            picks,
            union,
            kp.inspector.getKind?.()
          );
          didOverlayWork = true;
        }
      } catch {
      }
      if (mode === "text_focus" && currentState.focusedTextElement && currentState.focusedTextElement.isConnected) {
        try {
          this.overlayManager?.updateTextModeLabels?.(currentState.focusedTextElement);
          this.overlayManager?.updateTextFocusEscHint?.(currentState.focusedTextElement);
          didOverlayWork = true;
        } catch {
        }
      }
      try {
        if (this.overlayManager?.refreshTextHoverActivateHint) {
          this.overlayManager.refreshTextHoverActivateHint();
          didOverlayWork = true;
        }
      } catch {
      }
      if (!this._usesElementFocusStyling() && currentState.focusEl && currentState.focusEl.isConnected) {
        try {
          this.overlayManager?.updateFocusOverlay?.(currentState.focusEl);
          didOverlayWork = true;
        } catch {
        }
      }
      if (didOverlayWork) {
        this.scrollMetrics.overlayUpdates++;
      }
    }
    _updateInspectorOverlay(element) {
      const kind = this.stateManager.getState()?.inspectorKind || null;
      if (typeof this.overlayManager?.updateInspectorOverlay === "function") {
        this.overlayManager.updateInspectorOverlay(element, kind);
      } else {
        this.overlayManager?.updateDeleteOverlay?.(element);
      }
    }
    observeElementForScroll(element) {
      if (element && this.scrollObserver) {
        try {
          this.scrollObserver.observe(element);
        } catch {
        }
        this.scrollSensitiveElements.add(element);
      }
    }
    unobserveElementForScroll(element) {
      if (element && this.scrollObserver) {
        try {
          this.scrollObserver.unobserve(element);
        } catch {
        }
        this.scrollSensitiveElements.delete(element);
      }
    }
    /**
     * Hide fixed-position overlays for a target that left the viewport.
     * Does not clear element-styled focus rings.
     * @param {Element} element
     */
    hideFixedOverlaysForElement(element) {
      const currentState = this.stateManager.getState();
      if (!currentState) return;
      if (!this._usesElementFocusStyling() && currentState.focusEl === element) {
        try {
          this.overlayManager?.hideFocusOverlay?.();
        } catch {
        }
      }
      const inspectorEl = currentState.inspectorEl || currentState.deleteEl || currentState.colsEl;
      if (inspectorEl === element) {
        try {
          if (typeof this.overlayManager?.hideInspectorOverlay === "function") {
            this.overlayManager.hideInspectorOverlay();
          } else {
            this.overlayManager?.hideDeleteOverlay?.();
          }
        } catch {
        }
      }
    }
    handleScrollEnd() {
      const scrollDuration = performance.now() - this.scrollStartTime;
      this.scrollMetrics.averageScrollDuration = (this.scrollMetrics.averageScrollDuration + scrollDuration) / 2;
      this.isScrolling = false;
      try {
        this.overlayManager?.setScrollPaintPreferA?.(false);
      } catch {
      }
      let currentState;
      try {
        currentState = this.stateManager.getState();
      } catch {
        currentState = null;
      }
      if (currentState?.lastMouse && currentState.lastMouse.x >= 0 && currentState.lastMouse.y >= 0) {
        try {
          document.dispatchEvent(
            new CustomEvent("keypilot:scroll-end", {
              detail: {
                mouseX: currentState.lastMouse.x,
                mouseY: currentState.lastMouse.y
              }
            })
          );
        } catch {
        }
      }
      this.cleanupScrollObservers();
    }
    cleanupScrollObservers() {
      for (const element of [...this.scrollSensitiveElements]) {
        if (!element || !element.isConnected) {
          this.unobserveElementForScroll(element);
        }
      }
    }
    throttle(func, limit) {
      let inThrottle = false;
      return (...args) => {
        if (inThrottle) return;
        func.apply(this, args);
        inThrottle = true;
        setTimeout(() => {
          inThrottle = false;
        }, limit);
      };
    }
    getScrollMetrics() {
      const throttleRatio = this.scrollMetrics.scrollEvents > 0 ? (this.scrollMetrics.throttledCalls / this.scrollMetrics.scrollEvents * 100).toFixed(1) : 0;
      return {
        ...this.scrollMetrics,
        throttleRatio: `${throttleRatio}%`,
        activeSensitiveElements: this.scrollSensitiveElements.size,
        isCurrentlyScrolling: this.isScrolling,
        elementFocusStyling: this._usesElementFocusStyling()
      };
    }
    cleanup() {
      if (this.scrollObserver) {
        try {
          this.scrollObserver.disconnect();
        } catch {
        }
        this.scrollObserver = null;
      }
      if (this.scrollTimeout) {
        clearTimeout(this.scrollTimeout);
        this.scrollTimeout = null;
      }
      if (this.stateUnsubscribe) {
        try {
          this.stateUnsubscribe();
        } catch {
        }
        this.stateUnsubscribe = null;
      }
      this.scrollSensitiveElements.clear();
      if (this.boundScrollHandler) {
        document.removeEventListener("scroll", this.boundScrollHandler, { capture: true });
        this.boundScrollHandler = null;
      }
    }
  };

  // src/modules/rectangle-intersection-observer.js
  init_constants();

  // src/modules/text-element-filter.js
  init_constants();
  var TextElementFilter = class {
    constructor() {
      const config = EDGE_ONLY_SELECTION.SMART_TARGETING;
      this.textContainingTags = new Set(config.TEXT_ELEMENT_TAGS);
      this.skipElementTags = new Set(config.SKIP_ELEMENT_TAGS);
      this.minTextLength = config.MIN_TEXT_LENGTH;
      this.checkComputedStyle = config.CHECK_COMPUTED_STYLE;
      this.includeAriaLabels = config.INCLUDE_ARIA_LABELS;
      this.maxElementsToObserve = config.MAX_ELEMENTS_TO_OBSERVE;
      this.stats = {
        totalElementsScanned: 0,
        textElementsFound: 0,
        elementsSkipped: 0,
        processingTime: 0
      };
    }
    /**
     * Find all text-containing elements within a root element
     * @param {Element} rootElement - The root element to search within
     * @returns {Array<Element>} Array of elements that contain or are likely to contain text
     */
    findTextElements(rootElement) {
      if (!FEATURE_FLAGS.ENABLE_TEXT_ELEMENT_FILTER) {
        return Array.from(rootElement.querySelectorAll("*"));
      }
      const startTime = performance.now();
      const textElements = [];
      let elementsScanned = 0;
      let elementsSkipped = 0;
      const walker = document.createTreeWalker(
        rootElement,
        NodeFilter.SHOW_ELEMENT,
        {
          acceptNode: (element2) => {
            elementsScanned++;
            if (textElements.length >= this.maxElementsToObserve) {
              return NodeFilter.FILTER_REJECT;
            }
            if (this.isTextContaining(element2)) {
              return NodeFilter.FILTER_ACCEPT;
            } else {
              elementsSkipped++;
              return NodeFilter.FILTER_SKIP;
            }
          }
        }
      );
      let element;
      while (element = walker.nextNode()) {
        textElements.push(element);
      }
      const processingTime = performance.now() - startTime;
      this.stats.totalElementsScanned += elementsScanned;
      this.stats.textElementsFound += textElements.length;
      this.stats.elementsSkipped += elementsSkipped;
      this.stats.processingTime += processingTime;
      return textElements;
    }
    /**
     * Check if an element contains or is likely to contain text
     * @param {Element} element - The element to check
     * @returns {boolean} True if the element contains or is likely to contain text
     */
    isTextContaining(element) {
      if (this.skipElementTags.has(element.tagName.toLowerCase())) {
        return false;
      }
      if (this.checkComputedStyle && !this.isElementVisible(element)) {
        return false;
      }
      if (this.textContainingTags.has(element.tagName.toLowerCase())) {
        const textContent = element.textContent ? element.textContent.trim() : "";
        if (textContent.length >= this.minTextLength) {
          return true;
        }
      }
      if (this.hasDirectTextContent(element)) {
        return true;
      }
      if (this.includeAriaLabels && this.hasAriaText(element)) {
        return true;
      }
      if (this.isTextInput(element)) {
        return true;
      }
      if (this.hasTextLikeContent(element)) {
        return true;
      }
      return false;
    }
    /**
     * Check if an element has direct text content (not from child elements)
     * @param {Element} element - The element to check
     * @returns {boolean} True if the element has direct text content
     */
    hasDirectTextContent(element) {
      const textNodes = Array.from(element.childNodes).filter(
        (node) => node.nodeType === Node.TEXT_NODE
      );
      const textContent = textNodes.map((node) => node.textContent).join("").trim();
      return textContent.length >= this.minTextLength;
    }
    /**
     * Check if an element has ARIA text content
     * @param {Element} element - The element to check
     * @returns {boolean} True if the element has ARIA text content
     */
    hasAriaText(element) {
      const ariaLabel = element.getAttribute("aria-label");
      const ariaLabelledBy = element.getAttribute("aria-labelledby");
      const ariaDescribedBy = element.getAttribute("aria-describedby");
      if (ariaLabel && ariaLabel.trim().length >= this.minTextLength) {
        return true;
      }
      if (ariaLabelledBy) {
        const labelElement = document.getElementById(ariaLabelledBy);
        if (labelElement && labelElement.textContent.trim().length >= this.minTextLength) {
          return true;
        }
      }
      if (ariaDescribedBy) {
        const descElement = document.getElementById(ariaDescribedBy);
        if (descElement && descElement.textContent.trim().length >= this.minTextLength) {
          return true;
        }
      }
      return false;
    }
    /**
     * Check if an element is a text input
     * @param {Element} element - The element to check
     * @returns {boolean} True if the element is a text input
     */
    isTextInput(element) {
      const tagName = element.tagName.toLowerCase();
      if (tagName === "textarea") {
        return true;
      }
      if (tagName === "input") {
        const type2 = element.type ? element.type.toLowerCase() : "text";
        const textInputTypes = [
          "text",
          "search",
          "url",
          "email",
          "tel",
          "password",
          "number"
        ];
        return textInputTypes.includes(type2);
      }
      if (element.contentEditable === "true") {
        return true;
      }
      return false;
    }
    /**
     * Check if an element has text-like content (placeholder, title, alt text, etc.)
     * @param {Element} element - The element to check
     * @returns {boolean} True if the element has text-like content
     */
    hasTextLikeContent(element) {
      const textAttributes = ["placeholder", "title", "alt"];
      for (const attr of textAttributes) {
        const value = element.getAttribute(attr);
        if (value && value.trim().length >= this.minTextLength) {
          return true;
        }
      }
      if (element.tagName.toLowerCase() === "input" && this.isTextInput(element) && element.value && element.value.trim().length >= this.minTextLength) {
        return true;
      }
      return false;
    }
    /**
     * Check if an element is visible using computed styles
     * @param {Element} element - The element to check
     * @returns {boolean} True if the element is visible
     */
    isElementVisible(element) {
      try {
        if (!element.isConnected) {
          return true;
        }
        if (document.body.children.length <= 1) {
          return true;
        }
        const style = window.getComputedStyle(element);
        if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") {
          return false;
        }
        const rect = element.getBoundingClientRect();
        if (rect.width === 0 && rect.height === 0 && element.isConnected) {
          return false;
        }
        return true;
      } catch (error) {
        return true;
      }
    }
    /**
     * Filter an array of elements to only include text-containing ones
     * @param {Array<Element>} elements - Array of elements to filter
     * @returns {Array<Element>} Filtered array of text-containing elements
     */
    filterTextElements(elements) {
      if (!FEATURE_FLAGS.ENABLE_TEXT_ELEMENT_FILTER) {
        return elements;
      }
      const startTime = performance.now();
      const filteredElements = elements.filter((element) => this.isTextContaining(element));
      const processingTime = performance.now() - startTime;
      this.stats.totalElementsScanned += elements.length;
      this.stats.textElementsFound += filteredElements.length;
      this.stats.elementsSkipped += elements.length - filteredElements.length;
      this.stats.processingTime += processingTime;
      return filteredElements;
    }
    /**
     * Get performance statistics for the filter
     * @returns {Object} Performance statistics
     */
    getStats() {
      const totalElements = this.stats.totalElementsScanned;
      const reductionPercentage = totalElements > 0 ? (this.stats.elementsSkipped / totalElements * 100).toFixed(1) : 0;
      return {
        totalElementsScanned: this.stats.totalElementsScanned,
        textElementsFound: this.stats.textElementsFound,
        elementsSkipped: this.stats.elementsSkipped,
        reductionPercentage: `${reductionPercentage}%`,
        averageProcessingTime: this.stats.processingTime / Math.max(1, this.stats.totalElementsScanned),
        totalProcessingTime: this.stats.processingTime
      };
    }
    /**
     * Reset performance statistics
     */
    resetStats() {
      this.stats = {
        totalElementsScanned: 0,
        textElementsFound: 0,
        elementsSkipped: 0,
        processingTime: 0
      };
    }
    /**
     * Update configuration from constants (useful for runtime configuration changes)
     */
    updateConfiguration() {
      const config = EDGE_ONLY_SELECTION.SMART_TARGETING;
      this.textContainingTags = new Set(config.TEXT_ELEMENT_TAGS);
      this.skipElementTags = new Set(config.SKIP_ELEMENT_TAGS);
      this.minTextLength = config.MIN_TEXT_LENGTH;
      this.checkComputedStyle = config.CHECK_COMPUTED_STYLE;
      this.includeAriaLabels = config.INCLUDE_ARIA_LABELS;
      this.maxElementsToObserve = config.MAX_ELEMENTS_TO_OBSERVE;
    }
  };

  // src/modules/edge-character-detector.js
  init_constants();
  var EdgeCharacterDetector = class {
    constructor() {
      const config = EDGE_ONLY_SELECTION.CHARACTER_DETECTION;
      this.enabled = config.ENABLED;
      this.useRangeAPI = config.USE_RANGE_API;
      this.cacheCharacterPositions = config.CACHE_CHARACTER_POSITIONS;
      this.characterCacheSize = config.CHARACTER_CACHE_SIZE;
      this.boundaryDetectionPrecision = config.BOUNDARY_DETECTION_PRECISION;
      this.batchCharacterProcessing = config.BATCH_CHARACTER_PROCESSING;
      this.characterBatchSize = config.CHARACTER_BATCH_SIZE;
      this.maxCharactersPerElement = config.MAX_CHARACTERS_PER_ELEMENT;
      this.characterCache = /* @__PURE__ */ new WeakMap();
      this.stats = {
        charactersDetected: 0,
        cacheHits: 0,
        cacheMisses: 0,
        processingTime: 0,
        elementsProcessed: 0
      };
    }
    /**
     * Detect characters at the boundary level when an element intersects with a rectangle
     * @param {Element} element - The element to detect characters in
     * @param {Object} rectangle - The rectangle bounds {left, top, right, bottom}
     * @returns {Array<Object>} Array of character objects that intersect with the rectangle
     */
    detectAtBoundary(element, rectangle) {
      if (!FEATURE_FLAGS.ENABLE_EDGE_CHARACTER_DETECTOR || !this.enabled) {
        return [];
      }
      const startTime = performance.now();
      let characterPositions = [];
      try {
        if (this.cacheCharacterPositions) {
          characterPositions = this.characterCache.get(element);
          if (characterPositions) {
            this.stats.cacheHits++;
          } else {
            this.stats.cacheMisses++;
            characterPositions = this.calculateCharacterPositions(element);
            this.characterCache.set(element, characterPositions);
          }
        } else {
          characterPositions = this.calculateCharacterPositions(element);
        }
        const intersectingCharacters = this.findIntersectingCharacters(characterPositions, rectangle);
        const processingTime = performance.now() - startTime;
        this.stats.charactersDetected += intersectingCharacters.length;
        this.stats.processingTime += processingTime;
        this.stats.elementsProcessed++;
        return intersectingCharacters;
      } catch (error) {
        console.warn("[EdgeCharacterDetector] Error detecting characters at boundary:", error);
        return [];
      }
    }
    /**
     * Calculate character positions for all text nodes within an element
     * @param {Element} element - The element to calculate character positions for
     * @returns {Array<Object>} Array of character position objects
     */
    calculateCharacterPositions(element) {
      const characters = [];
      const textNodes = this.getTextNodes(element);
      for (const textNode of textNodes) {
        const nodeCharacters = this.calculateTextNodeCharacterPositions(textNode);
        characters.push(...nodeCharacters);
        if (characters.length >= this.maxCharactersPerElement) {
          break;
        }
      }
      return characters.slice(0, this.maxCharactersPerElement);
    }
    /**
     * Calculate character positions for a specific text node
     * @param {Text} textNode - The text node to process
     * @returns {Array<Object>} Array of character position objects
     */
    calculateTextNodeCharacterPositions(textNode) {
      const characters = [];
      const text = textNode.textContent;
      if (!text || text.length === 0) {
        return characters;
      }
      if (this.batchCharacterProcessing) {
        return this.calculateCharacterPositionsBatched(textNode, text);
      } else {
        return this.calculateCharacterPositionsSequential(textNode, text);
      }
    }
    /**
     * Calculate character positions using batched processing
     * @param {Text} textNode - The text node to process
     * @param {string} text - The text content
     * @returns {Array<Object>} Array of character position objects
     */
    calculateCharacterPositionsBatched(textNode, text) {
      const characters = [];
      const batchSize = this.characterBatchSize;
      for (let i = 0; i < text.length; i += batchSize) {
        const endIndex = Math.min(i + batchSize, text.length);
        const batchCharacters = this.calculateCharacterBatch(textNode, text, i, endIndex);
        characters.push(...batchCharacters);
      }
      return characters;
    }
    /**
     * Calculate character positions for a batch of characters
     * @param {Text} textNode - The text node to process
     * @param {string} text - The text content
     * @param {number} startIndex - Start index of the batch
     * @param {number} endIndex - End index of the batch
     * @returns {Array<Object>} Array of character position objects
     */
    calculateCharacterBatch(textNode, text, startIndex, endIndex) {
      const characters = [];
      for (let i = startIndex; i < endIndex; i++) {
        const char = text[i];
        if (char.trim().length === 0) {
          continue;
        }
        try {
          const characterData = this.calculateSingleCharacterPosition(textNode, i, char);
          if (characterData) {
            characters.push(characterData);
          }
        } catch (error) {
          continue;
        }
      }
      return characters;
    }
    /**
     * Calculate character positions using sequential processing
     * @param {Text} textNode - The text node to process
     * @param {string} text - The text content
     * @returns {Array<Object>} Array of character position objects
     */
    calculateCharacterPositionsSequential(textNode, text) {
      const characters = [];
      for (let i = 0; i < text.length; i++) {
        const char = text[i];
        if (char.trim().length === 0) {
          continue;
        }
        try {
          const characterData = this.calculateSingleCharacterPosition(textNode, i, char);
          if (characterData) {
            characters.push(characterData);
          }
        } catch (error) {
          continue;
        }
      }
      return characters;
    }
    /**
     * Calculate position for a single character using Range API
     * @param {Text} textNode - The text node containing the character
     * @param {number} index - Index of the character within the text node
     * @param {string} char - The character
     * @returns {Object|null} Character position object or null if positioning fails
     */
    calculateSingleCharacterPosition(textNode, index, char) {
      if (!this.useRangeAPI) {
        return null;
      }
      try {
        const range = document.createRange();
        range.setStart(textNode, index);
        range.setEnd(textNode, index + 1);
        const rect = range.getBoundingClientRect();
        if (rect.width > 0 && rect.height > 0) {
          return {
            char,
            node: textNode,
            index,
            rect: {
              left: rect.left,
              top: rect.top,
              right: rect.right,
              bottom: rect.bottom,
              width: rect.width,
              height: rect.height
            },
            range: range.cloneRange()
          };
        }
      } catch (error) {
      }
      return null;
    }
    /**
     * Find characters that intersect with the given rectangle
     * @param {Array<Object>} characterPositions - Array of character position objects
     * @param {Object} rectangle - The rectangle bounds {left, top, right, bottom}
     * @returns {Array<Object>} Array of intersecting character objects
     */
    findIntersectingCharacters(characterPositions, rectangle) {
      const intersectingCharacters = [];
      for (const character of characterPositions) {
        if (this.isCharacterInRectangle(character, rectangle)) {
          intersectingCharacters.push(character);
        }
      }
      return intersectingCharacters;
    }
    /**
     * Check if a character intersects with a rectangle
     * @param {Object} character - Character position object
     * @param {Object} rectangle - Rectangle bounds {left, top, right, bottom}
     * @returns {boolean} True if the character intersects with the rectangle
     */
    isCharacterInRectangle(character, rectangle) {
      const charRect = character.rect;
      const precision = this.boundaryDetectionPrecision;
      return !(charRect.right < rectangle.left - precision || charRect.left > rectangle.right + precision || charRect.bottom < rectangle.top - precision || charRect.top > rectangle.bottom + precision);
    }
    /**
     * Get all text nodes within an element
     * @param {Element} element - The element to search for text nodes
     * @returns {Array<Text>} Array of text nodes
     */
    getTextNodes(element) {
      const textNodes = [];
      const walker = document.createTreeWalker(
        element,
        NodeFilter.SHOW_TEXT,
        {
          acceptNode: (node2) => {
            return node2.textContent && node2.textContent.trim().length > 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
          }
        }
      );
      let node;
      while (node = walker.nextNode()) {
        textNodes.push(node);
      }
      return textNodes;
    }
    /**
     * Clear the character position cache
     */
    clearCache() {
      this.characterCache = /* @__PURE__ */ new WeakMap();
    }
    /**
     * Get performance statistics
     * @returns {Object} Performance statistics
     */
    getStats() {
      const totalCacheAccess = this.stats.cacheHits + this.stats.cacheMisses;
      const cacheHitRate = totalCacheAccess > 0 ? (this.stats.cacheHits / totalCacheAccess * 100).toFixed(1) : 0;
      return {
        charactersDetected: this.stats.charactersDetected,
        cacheHits: this.stats.cacheHits,
        cacheMisses: this.stats.cacheMisses,
        cacheHitRate: `${cacheHitRate}%`,
        averageProcessingTime: this.stats.processingTime / Math.max(1, this.stats.elementsProcessed),
        totalProcessingTime: this.stats.processingTime,
        elementsProcessed: this.stats.elementsProcessed
      };
    }
    /**
     * Reset performance statistics
     */
    resetStats() {
      this.stats = {
        charactersDetected: 0,
        cacheHits: 0,
        cacheMisses: 0,
        processingTime: 0,
        elementsProcessed: 0
      };
    }
    /**
     * Update configuration from constants (useful for runtime configuration changes)
     */
    updateConfiguration() {
      const config = EDGE_ONLY_SELECTION.CHARACTER_DETECTION;
      this.enabled = config.ENABLED;
      this.useRangeAPI = config.USE_RANGE_API;
      this.cacheCharacterPositions = config.CACHE_CHARACTER_POSITIONS;
      this.characterCacheSize = config.CHARACTER_CACHE_SIZE;
      this.boundaryDetectionPrecision = config.BOUNDARY_DETECTION_PRECISION;
      this.batchCharacterProcessing = config.BATCH_CHARACTER_PROCESSING;
      this.characterBatchSize = config.CHARACTER_BATCH_SIZE;
      this.maxCharactersPerElement = config.MAX_CHARACTERS_PER_ELEMENT;
    }
    /**
     * Create a selection range from intersecting characters
     * @param {Array<Object>} characters - Array of character objects
     * @returns {Range|null} DOM Range covering the characters, or null if no characters
     */
    createSelectionRange(characters) {
      if (!characters || characters.length === 0) {
        return null;
      }
      try {
        const sortedCharacters = this.sortCharactersByDocumentOrder(characters);
        const firstChar = sortedCharacters[0];
        const lastChar = sortedCharacters[sortedCharacters.length - 1];
        const range = document.createRange();
        range.setStart(firstChar.node, firstChar.index);
        range.setEnd(lastChar.node, lastChar.index + 1);
        return range;
      } catch (error) {
        console.warn("[EdgeCharacterDetector] Error creating selection range:", error);
        return null;
      }
    }
    /**
     * Sort characters by their position in the document
     * @param {Array<Object>} characters - Array of character objects
     * @returns {Array<Object>} Sorted array of character objects
     */
    sortCharactersByDocumentOrder(characters) {
      return characters.sort((a, b) => {
        const nodeComparison = a.node.compareDocumentPosition(b.node);
        if (nodeComparison & Node.DOCUMENT_POSITION_FOLLOWING) {
          return -1;
        } else if (nodeComparison & Node.DOCUMENT_POSITION_PRECEDING) {
          return 1;
        } else {
          return a.index - b.index;
        }
      });
    }
    /**
     * Get character count for an element (useful for performance estimation)
     * @param {Element} element - The element to count characters in
     * @returns {number} Approximate character count
     */
    getCharacterCount(element) {
      const textNodes = this.getTextNodes(element);
      let totalCharacters = 0;
      for (const textNode of textNodes) {
        totalCharacters += textNode.textContent.trim().length;
      }
      return Math.min(totalCharacters, this.maxCharactersPerElement);
    }
  };

  // src/modules/rectangle-intersection-observer.js
  var RectangleIntersectionObserver = class {
    constructor() {
      this.textObserver = null;
      this.rectangleRoot = null;
      this.intersectingTextElements = /* @__PURE__ */ new Set();
      this.intersectingTextNodes = /* @__PURE__ */ new Set();
      this.onIntersectionChange = null;
      this.enhancedIntegrationEnabled = FEATURE_FLAGS.ENABLE_ENHANCED_RECTANGLE_OBSERVER;
      if (this.enhancedIntegrationEnabled) {
        this.textElementFilter = new TextElementFilter();
        this.observedElements = /* @__PURE__ */ new Set();
        this.smartTargetingEnabled = EDGE_ONLY_SELECTION.SMART_TARGETING.ENABLED;
        this.edgeCharacterDetector = new EdgeCharacterDetector();
        this.edgeCharacterDetectionEnabled = EDGE_ONLY_SELECTION.CHARACTER_DETECTION.ENABLED;
      } else {
        this.textElementFilter = null;
        this.observedElements = null;
        this.smartTargetingEnabled = false;
        this.edgeCharacterDetector = null;
        this.edgeCharacterDetectionEnabled = false;
      }
      ;
      this.adaptiveProcessor = {
        enabled: EDGE_ONLY_SELECTION.ENABLE_ADAPTIVE_PROCESSING,
        // Page complexity analysis
        pageComplexity: {
          elementCount: 0,
          domDepth: 0,
          textNodeDensity: 0,
          complexityLevel: "medium",
          // low, medium, high
          lastAnalysis: 0,
          analysisInterval: EDGE_ONLY_SELECTION.PAGE_COMPLEXITY_ANALYSIS.COMPLEXITY_CHECK_INTERVAL
        },
        // Frame rate monitoring
        frameRateMonitor: {
          targetFps: EDGE_ONLY_SELECTION.FRAME_RATE_PROCESSING.TARGET_FPS,
          frameTimeBudget: EDGE_ONLY_SELECTION.FRAME_RATE_PROCESSING.FRAME_TIME_BUDGET_MS,
          processingTimeBudget: EDGE_ONLY_SELECTION.FRAME_RATE_PROCESSING.PROCESSING_TIME_BUDGET_MS,
          frameTimes: [],
          currentFps: 60,
          lastFrameTime: 0,
          frameCount: 0,
          monitoringWindow: EDGE_ONLY_SELECTION.FRAME_RATE_PROCESSING.FRAME_RATE_MONITORING_WINDOW
        },
        // Batch processing optimization
        batchProcessor: {
          enabled: EDGE_ONLY_SELECTION.BATCH_PROCESSING.ENABLE_BATCH_PROCESSING,
          currentBatchSize: EDGE_ONLY_SELECTION.BATCH_PROCESSING.DEFAULT_BATCH_SIZE,
          maxBatchSize: EDGE_ONLY_SELECTION.BATCH_PROCESSING.MAX_BATCH_SIZE,
          minBatchSize: EDGE_ONLY_SELECTION.BATCH_PROCESSING.MIN_BATCH_SIZE,
          batchTimeout: EDGE_ONLY_SELECTION.BATCH_PROCESSING.BATCH_TIMEOUT_MS,
          pendingBatch: [],
          batchTimer: null,
          adaptiveSizing: EDGE_ONLY_SELECTION.BATCH_PROCESSING.ADAPTIVE_BATCH_SIZING
        },
        // Quality adjustment system
        qualityController: {
          enabled: EDGE_ONLY_SELECTION.QUALITY_ADJUSTMENTS.ENABLE_QUALITY_ADJUSTMENTS,
          currentQuality: "high",
          // high, medium, low
          qualityHistory: [],
          adjustmentHysteresis: EDGE_ONLY_SELECTION.QUALITY_ADJUSTMENTS.QUALITY_ADJUSTMENT_HYSTERESIS,
          hysteresisCounter: 0,
          thresholds: {
            high: EDGE_ONLY_SELECTION.QUALITY_ADJUSTMENTS.HIGH_QUALITY_TIME_THRESHOLD,
            medium: EDGE_ONLY_SELECTION.QUALITY_ADJUSTMENTS.MEDIUM_QUALITY_TIME_THRESHOLD,
            lowProcessingLimit: EDGE_ONLY_SELECTION.QUALITY_ADJUSTMENTS.LOW_QUALITY_PROCESSING_LIMIT
          }
        }
      };
      this.predictiveCaching = {
        enabled: EDGE_ONLY_SELECTION.PREDICTIVE_CACHING.ENABLE_PREDICTIVE_CACHING,
        // User behavior analysis
        behaviorAnalyzer: {
          enabled: EDGE_ONLY_SELECTION.PREDICTIVE_CACHING.ENABLE_USER_BEHAVIOR_ANALYSIS,
          interactionHistory: [],
          patterns: /* @__PURE__ */ new Map(),
          // Pattern type -> confidence score
          lastAnalysis: 0,
          analysisInterval: EDGE_ONLY_SELECTION.PREDICTIVE_CACHING.PATTERN_ANALYSIS_INTERVAL,
          patternWindow: EDGE_ONLY_SELECTION.PREDICTIVE_CACHING.BEHAVIOR_PATTERN_WINDOW,
          interactionTimeout: EDGE_ONLY_SELECTION.PREDICTIVE_CACHING.INTERACTION_TIMEOUT_MS,
          minConfidence: EDGE_ONLY_SELECTION.PREDICTIVE_CACHING.MIN_PATTERN_CONFIDENCE
        },
        // Viewport-based caching
        viewportCaching: {
          enabled: EDGE_ONLY_SELECTION.PREDICTIVE_CACHING.ENABLE_VIEWPORT_BASED_CACHING,
          currentViewport: { x: 0, y: 0, width: 0, height: 0 },
          cacheMargin: EDGE_ONLY_SELECTION.PREDICTIVE_CACHING.VIEWPORT_CACHE_MARGIN,
          cacheSectors: EDGE_ONLY_SELECTION.PREDICTIVE_CACHING.VIEWPORT_CACHE_SECTORS,
          sectorCache: /* @__PURE__ */ new Map(),
          // Sector ID -> cached elements
          lastViewportUpdate: 0,
          updateThrottle: EDGE_ONLY_SELECTION.PREDICTIVE_CACHING.VIEWPORT_UPDATE_THROTTLE
        },
        // Scroll prediction
        scrollPredictor: {
          enabled: EDGE_ONLY_SELECTION.PREDICTIVE_CACHING.ENABLE_SCROLL_PREDICTION,
          scrollHistory: [],
          currentVelocity: { x: 0, y: 0 },
          predictedPosition: { x: 0, y: 0 },
          velocitySamples: EDGE_ONLY_SELECTION.PREDICTIVE_CACHING.SCROLL_VELOCITY_SAMPLES,
          predictionDistance: EDGE_ONLY_SELECTION.PREDICTIVE_CACHING.SCROLL_PREDICTION_DISTANCE,
          minVelocity: EDGE_ONLY_SELECTION.PREDICTIVE_CACHING.MIN_SCROLL_VELOCITY,
          directionThreshold: EDGE_ONLY_SELECTION.PREDICTIVE_CACHING.SCROLL_DIRECTION_THRESHOLD,
          lastScrollTime: 0
        },
        // Cache preloading
        preloader: {
          enabled: true,
          preloadQueue: [],
          preloadCache: /* @__PURE__ */ new WeakMap(),
          // Element -> preload data
          batchSize: EDGE_ONLY_SELECTION.PREDICTIVE_CACHING.PRELOAD_BATCH_SIZE,
          throttleMs: EDGE_ONLY_SELECTION.PREDICTIVE_CACHING.PRELOAD_THROTTLE_MS,
          maxElements: EDGE_ONLY_SELECTION.PREDICTIVE_CACHING.MAX_PRELOAD_ELEMENTS,
          priorityThreshold: EDGE_ONLY_SELECTION.PREDICTIVE_CACHING.PRELOAD_PRIORITY_THRESHOLD,
          lastPreloadTime: 0,
          preloadTimer: null
        }
      };
      this.fallbackHandler = {
        active: false,
        reason: null,
        fallbackStartTime: null,
        spatialIntersectionCallback: null,
        recoveryAttempts: 0,
        maxRecoveryAttempts: 3,
        recoveryTestInterval: 3e4,
        // Test recovery every 30 seconds
        lastRecoveryTest: 0
      };
      this.notificationSystem = {
        enabled: true,
        lastNotificationTime: 0,
        notificationCooldownMs: 1e4,
        // 10 second cooldown between notifications
        showPerformanceNotifications: FEATURE_FLAGS.SHOW_SELECTION_METHOD_IN_UI,
        notificationCallback: null
      };
      this.metrics = {
        elementsObserved: 0,
        intersectionUpdates: 0,
        textNodesFound: 0,
        lastUpdateTime: 0,
        // Edge-only processing metrics
        edgeProcessingTime: 0,
        edgeElementsProcessed: 0,
        elementsEntering: 0,
        elementsLeaving: 0,
        // Text node traversal metrics
        totalTraversalTime: 0,
        traversalCount: 0,
        cacheHits: 0,
        cacheMisses: 0,
        // Performance comparison metrics
        estimatedFullTraversalTime: 0,
        actualEdgeProcessingTime: 0,
        efficiencyGainPercent: 0,
        // Smart targeting metrics (Task 2.1)
        totalElementsScanned: 0,
        elementsSkipped: 0,
        elementReductionPercentage: 0,
        smartTargetingTime: 0,
        dynamicElementsAdded: 0,
        // Character detection metrics (Task 2.2)
        charactersDetected: 0,
        characterDetectionTime: 0,
        characterCacheHits: 0,
        characterCacheMisses: 0
      };
      this.textNodeCache = /* @__PURE__ */ new WeakMap();
      this.cacheAccessOrder = /* @__PURE__ */ new WeakMap();
      this.lruRefs = /* @__PURE__ */ new Map();
      this.lruTimestamps = /* @__PURE__ */ new Map();
      this.cacheCleanupRegistry = new FinalizationRegistry((elementId) => {
        this.lruRefs.delete(elementId);
        this.lruTimestamps.delete(elementId);
        this.cacheEfficiencyMetrics.hotElements.delete(elementId);
        this.cacheEfficiencyMetrics.coldElements.delete(elementId);
        this.cacheSize = Math.max(0, this.cacheSize - 1);
      });
      this.cacheSize = 0;
      this.maxCacheSize = EDGE_ONLY_SELECTION.CACHE_SIZE_LIMIT;
      this.cleanupThreshold = EDGE_ONLY_SELECTION.CACHE_CLEANUP_THRESHOLD;
      this.cleanupBatchSize = EDGE_ONLY_SELECTION.CACHE_CLEANUP_BATCH_SIZE;
      this.cacheManagementTimer = null;
      this.memoryMonitoringTimer = null;
      this.lastCacheOptimization = Date.now();
      this.cacheEfficiencyMetrics = {
        totalCacheOperations: 0,
        cacheHitStreak: 0,
        longestHitStreak: 0,
        cacheMissStreak: 0,
        longestMissStreak: 0,
        averageAccessTime: 0,
        totalAccessTime: 0,
        hotElements: /* @__PURE__ */ new Map(),
        // Track frequently accessed elements
        coldElements: /* @__PURE__ */ new Set(),
        // Track rarely accessed elements
        evictionHistory: [],
        // Track evicted elements for analysis
        memoryPressureEvents: 0,
        adaptiveCleanupTriggers: 0
      };
      this.elementStates = /* @__PURE__ */ new Map();
      this.lastRectangleUpdate = 0;
      this.debugLogging = window.KEYPILOT_DEBUG || false;
    }
    /**
     * Initialize the rectangle intersection observer
     * @param {Function} onIntersectionChange - Callback when intersection changes
     * @param {Function} notificationCallback - Optional callback for user notifications
     */
    initialize(onIntersectionChange, notificationCallback = null) {
      this.onIntersectionChange = onIntersectionChange;
      if (notificationCallback) {
        this.setNotificationCallback(notificationCallback);
      }
      this.createRectangleRoot();
      this.setupTextObserver();
      this.discoverTextElements();
      this.startCacheManagement();
      if (this.adaptiveProcessor.enabled) {
        this.initializeAdaptiveProcessing();
      }
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Rectangle intersection observer initialized with performance monitoring:", {
          performanceMonitoringEnabled: this.legacyPerformanceMonitor.enabled,
          fallbackThreshold: this.legacyPerformanceMonitor.thresholdMs + "ms",
          maxConsecutiveSlowOps: this.legacyPerformanceMonitor.maxConsecutiveSlowOps,
          notificationSystemEnabled: this.notificationSystem.enabled,
          adaptiveProcessingEnabled: this.adaptiveProcessor.enabled
        });
      }
    }
    /**
     * Initialize adaptive processing system (Task 2.1)
     */
    initializeAdaptiveProcessing() {
      this.analyzePageComplexity();
      if (this.adaptiveProcessor.batchProcessor.enabled) {
        this.initializeBatchProcessing();
      }
      if (this.predictiveCaching.enabled) {
        this.initializePredictiveCaching();
      }
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Adaptive processing initialized:", {
          pageComplexity: this.adaptiveProcessor.pageComplexity.complexityLevel,
          elementCount: this.adaptiveProcessor.pageComplexity.elementCount,
          domDepth: this.adaptiveProcessor.pageComplexity.domDepth,
          textNodeDensity: this.adaptiveProcessor.pageComplexity.textNodeDensity,
          batchProcessingEnabled: this.adaptiveProcessor.batchProcessor.enabled,
          currentBatchSize: this.adaptiveProcessor.batchProcessor.currentBatchSize,
          predictiveCachingEnabled: this.predictiveCaching.enabled,
          enhancedAnalyticsEnabled: this.enhancedAnalytics.enabled
        });
      }
    }
    /**
     * Analyze page complexity for adaptive processing (Task 2.1)
     */
    analyzePageComplexity() {
      const startTime = performance.now();
      const allElements = document.querySelectorAll("*");
      this.adaptiveProcessor.pageComplexity.elementCount = allElements.length;
      let maxDepth = 0;
      const calculateDepth = (element, depth = 0) => {
        maxDepth = Math.max(maxDepth, depth);
        for (const child of element.children) {
          calculateDepth(child, depth + 1);
        }
      };
      calculateDepth(document.body);
      this.adaptiveProcessor.pageComplexity.domDepth = maxDepth;
      const textElements = document.querySelectorAll("p, div, span, h1, h2, h3, h4, h5, h6, li, td, th, a");
      const textElementsWithContent = Array.from(textElements).filter(
        (el2) => el2.textContent && el2.textContent.trim().length > 0
      );
      this.adaptiveProcessor.pageComplexity.textNodeDensity = textElementsWithContent.length / Math.max(allElements.length, 1);
      const { elementCount, domDepth, textNodeDensity } = this.adaptiveProcessor.pageComplexity;
      const config = EDGE_ONLY_SELECTION.PAGE_COMPLEXITY_ANALYSIS;
      if (elementCount > config.ELEMENT_COUNT_THRESHOLD_HIGH || domDepth > config.DOM_DEPTH_THRESHOLD_HIGH) {
        this.adaptiveProcessor.pageComplexity.complexityLevel = "high";
      } else if (elementCount > config.ELEMENT_COUNT_THRESHOLD_LOW || domDepth > config.DOM_DEPTH_THRESHOLD_LOW) {
        this.adaptiveProcessor.pageComplexity.complexityLevel = "medium";
      } else {
        this.adaptiveProcessor.pageComplexity.complexityLevel = "low";
      }
      this.adjustProcessingForComplexity();
      this.adaptiveProcessor.pageComplexity.lastAnalysis = Date.now();
      const analysisTime = performance.now() - startTime;
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Page complexity analysis completed:", {
          elementCount,
          domDepth,
          textNodeDensity: textNodeDensity.toFixed(3),
          complexityLevel: this.adaptiveProcessor.pageComplexity.complexityLevel,
          analysisTime: analysisTime.toFixed(2) + "ms",
          adjustedBatchSize: this.adaptiveProcessor.batchProcessor.currentBatchSize,
          adjustedProcessingBudget: this.adaptiveProcessor.frameRateMonitor.processingTimeBudget
        });
      }
    }
    /**
     * Adjust processing parameters based on page complexity (Task 2.1)
     */
    adjustProcessingForComplexity() {
      const complexity = this.adaptiveProcessor.pageComplexity.complexityLevel;
      const batchProcessor = this.adaptiveProcessor.batchProcessor;
      const frameMonitor = this.adaptiveProcessor.frameRateMonitor;
      switch (complexity) {
        case "high":
          batchProcessor.currentBatchSize = Math.max(
            batchProcessor.minBatchSize,
            Math.floor(batchProcessor.currentBatchSize * 0.6)
          );
          frameMonitor.processingTimeBudget = Math.floor(frameMonitor.processingTimeBudget * 0.7);
          break;
        case "medium":
          batchProcessor.currentBatchSize = Math.floor(
            (batchProcessor.minBatchSize + batchProcessor.maxBatchSize) / 2
          );
          frameMonitor.processingTimeBudget = Math.floor(frameMonitor.processingTimeBudget * 0.85);
          break;
        case "low":
          batchProcessor.currentBatchSize = Math.min(
            batchProcessor.maxBatchSize,
            Math.floor(batchProcessor.currentBatchSize * 1.2)
          );
          frameMonitor.processingTimeBudget = EDGE_ONLY_SELECTION.FRAME_RATE_PROCESSING.PROCESSING_TIME_BUDGET_MS;
          break;
      }
      batchProcessor.currentBatchSize = Math.max(
        batchProcessor.minBatchSize,
        Math.min(batchProcessor.maxBatchSize, batchProcessor.currentBatchSize)
      );
    }
    /**
     * Start frame rate monitoring for adaptive processing (Task 2.1)
     */
    startFrameRateMonitoring() {
      const monitor = this.adaptiveProcessor.frameRateMonitor;
      const updateFrameRate = () => {
        const currentTime = performance.now();
        if (monitor.lastFrameTime > 0) {
          const frameTime = currentTime - monitor.lastFrameTime;
          monitor.frameTimes.push(frameTime);
          if (monitor.frameTimes.length > monitor.monitoringWindow) {
            monitor.frameTimes.shift();
          }
          if (monitor.frameTimes.length > 0) {
            const avgFrameTime = monitor.frameTimes.reduce((a, b) => a + b, 0) / monitor.frameTimes.length;
            monitor.currentFps = Math.min(60, 1e3 / avgFrameTime);
          }
          this.adjustProcessingForFrameRate();
        }
        monitor.lastFrameTime = currentTime;
        monitor.frameCount++;
        requestAnimationFrame(updateFrameRate);
      };
      requestAnimationFrame(updateFrameRate);
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Frame rate monitoring started:", {
          targetFps: monitor.targetFps,
          frameTimeBudget: monitor.frameTimeBudget + "ms",
          processingTimeBudget: monitor.processingTimeBudget + "ms"
        });
      }
    }
    /**
     * Adjust processing parameters based on frame rate (Task 2.1)
     */
    adjustProcessingForFrameRate() {
      const monitor = this.adaptiveProcessor.frameRateMonitor;
      const batchProcessor = this.adaptiveProcessor.batchProcessor;
      const qualityController = this.adaptiveProcessor.qualityController;
      const minAcceptableFps = EDGE_ONLY_SELECTION.FRAME_RATE_PROCESSING.MIN_ACCEPTABLE_FPS;
      if (monitor.currentFps < minAcceptableFps) {
        const adjustmentFactor = EDGE_ONLY_SELECTION.FRAME_RATE_PROCESSING.FRAME_RATE_ADJUSTMENT_FACTOR;
        if (batchProcessor.adaptiveSizing) {
          batchProcessor.currentBatchSize = Math.max(
            batchProcessor.minBatchSize,
            Math.floor(batchProcessor.currentBatchSize * adjustmentFactor)
          );
        }
        monitor.processingTimeBudget = Math.max(
          2,
          // Minimum 2ms
          Math.floor(monitor.processingTimeBudget * adjustmentFactor)
        );
        if (qualityController.enabled && qualityController.currentQuality !== "low") {
          this.adjustProcessingQuality("low");
        }
        if (this.debugLogging) {
          console.warn("[KeyPilot Debug] Frame rate adjustment triggered:", {
            currentFps: monitor.currentFps.toFixed(1),
            minAcceptableFps,
            newBatchSize: batchProcessor.currentBatchSize,
            newProcessingBudget: monitor.processingTimeBudget + "ms",
            newQuality: qualityController.currentQuality
          });
        }
      } else if (monitor.currentFps > monitor.targetFps * 0.9) {
        if (batchProcessor.currentBatchSize < batchProcessor.maxBatchSize) {
          batchProcessor.currentBatchSize = Math.min(
            batchProcessor.maxBatchSize,
            batchProcessor.currentBatchSize + 1
          );
        }
        if (qualityController.enabled && qualityController.currentQuality !== "high") {
          this.adjustProcessingQuality("high");
        }
      }
    }
    /**
     * Initialize batch processing system (Task 2.1)
     */
    initializeBatchProcessing() {
      const batchProcessor = this.adaptiveProcessor.batchProcessor;
      if (batchProcessor.batchTimer) {
        clearTimeout(batchProcessor.batchTimer);
      }
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Batch processing initialized:", {
          enabled: batchProcessor.enabled,
          defaultBatchSize: batchProcessor.currentBatchSize,
          maxBatchSize: batchProcessor.maxBatchSize,
          batchTimeout: batchProcessor.batchTimeout + "ms",
          adaptiveSizing: batchProcessor.adaptiveSizing
        });
      }
    }
    /**
     * Adjust processing quality based on available time (Task 2.1)
     * @param {string} targetQuality - Target quality level ('high', 'medium', 'low')
     */
    adjustProcessingQuality(targetQuality) {
      const qualityController = this.adaptiveProcessor.qualityController;
      if (!qualityController.enabled) {
        return;
      }
      if (qualityController.currentQuality !== targetQuality) {
        qualityController.hysteresisCounter++;
        if (qualityController.hysteresisCounter >= qualityController.adjustmentHysteresis) {
          qualityController.currentQuality = targetQuality;
          qualityController.hysteresisCounter = 0;
          qualityController.qualityHistory.push({
            quality: targetQuality,
            timestamp: Date.now()
          });
          if (qualityController.qualityHistory.length > 10) {
            qualityController.qualityHistory.shift();
          }
          if (this.debugLogging) {
            console.log("[KeyPilot Debug] Processing quality adjusted:", {
              newQuality: targetQuality,
              hysteresisCounter: qualityController.hysteresisCounter,
              qualityHistory: qualityController.qualityHistory.length
            });
          }
        }
      } else {
        qualityController.hysteresisCounter = 0;
      }
    }
    /**
     * Initialize predictive caching system (Task 2.2)
     */
    initializePredictiveCaching() {
      if (this.predictiveCaching.viewportCaching.enabled) {
        this.initializeViewportCaching();
      }
      if (this.predictiveCaching.scrollPredictor.enabled) {
        this.initializeScrollPrediction();
      }
      this.startCachePreloading();
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Predictive caching initialized:", {
          viewportCaching: this.predictiveCaching.viewportCaching.enabled,
          scrollPrediction: this.predictiveCaching.scrollPredictor.enabled,
          behaviorAnalysis: this.predictiveCaching.behaviorAnalyzer.enabled,
          preloading: this.predictiveCaching.preloader.enabled
        });
      }
    }
    /**
     * Initialize viewport-based caching (Task 2.2)
     */
    initializeViewportCaching() {
      const viewportCaching = this.predictiveCaching.viewportCaching;
      this.updateViewportInfo();
      const handleViewportChange = () => {
        const now = Date.now();
        if (now - viewportCaching.lastViewportUpdate > viewportCaching.updateThrottle) {
          this.updateViewportInfo();
          this.warmViewportCache();
          viewportCaching.lastViewportUpdate = now;
        }
      };
      window.addEventListener("resize", handleViewportChange);
      window.addEventListener("scroll", handleViewportChange);
      this.warmViewportCache();
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Viewport caching initialized:", {
          viewport: viewportCaching.currentViewport,
          cacheMargin: viewportCaching.cacheMargin,
          cacheSectors: viewportCaching.cacheSectors
        });
      }
    }
    /**
     * Update viewport information (Task 2.2)
     */
    updateViewportInfo() {
      const viewportCaching = this.predictiveCaching.viewportCaching;
      viewportCaching.currentViewport = {
        x: window.scrollX,
        y: window.scrollY,
        width: window.innerWidth,
        height: window.innerHeight
      };
    }
    /**
     * Warm cache for elements in and around the viewport (Task 2.2)
     */
    warmViewportCache() {
      const viewportCaching = this.predictiveCaching.viewportCaching;
      const viewport = viewportCaching.currentViewport;
      const margin = viewportCaching.cacheMargin;
      const cacheArea = {
        left: viewport.x - margin,
        top: viewport.y - margin,
        right: viewport.x + viewport.width + margin,
        bottom: viewport.y + viewport.height + margin
      };
      const elementsInArea = this.findElementsInArea(cacheArea);
      elementsInArea.forEach((element) => {
        if (!this.textNodeCache.has(element)) {
          this.preloadElementTextNodes(element, "viewport");
        }
      });
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Viewport cache warmed:", {
          cacheArea,
          elementsFound: elementsInArea.length,
          cacheSize: this.cacheSize
        });
      }
    }
    /**
     * Initialize scroll prediction (Task 2.2)
     */
    initializeScrollPrediction() {
      const scrollPredictor = this.predictiveCaching.scrollPredictor;
      let lastScrollTime = 0;
      let lastScrollPosition = { x: window.scrollX, y: window.scrollY };
      const handleScroll = () => {
        const currentTime = performance.now();
        const currentPosition = { x: window.scrollX, y: window.scrollY };
        if (lastScrollTime > 0) {
          const deltaTime = currentTime - lastScrollTime;
          const deltaX = currentPosition.x - lastScrollPosition.x;
          const deltaY = currentPosition.y - lastScrollPosition.y;
          if (deltaTime > 0) {
            const velocityX = deltaX / deltaTime * 1e3;
            const velocityY = deltaY / deltaTime * 1e3;
            this.updateScrollVelocity(velocityX, velocityY);
            this.predictScrollPosition();
            this.preloadForScrollDirection();
          }
        }
        lastScrollTime = currentTime;
        lastScrollPosition = currentPosition;
        scrollPredictor.lastScrollTime = currentTime;
      };
      let scrollTimeout;
      window.addEventListener("scroll", () => {
        if (scrollTimeout) {
          clearTimeout(scrollTimeout);
        }
        scrollTimeout = setTimeout(handleScroll, 16);
      });
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Scroll prediction initialized");
      }
    }
    /**
     * Update scroll velocity tracking (Task 2.2)
     * @param {number} velocityX - Horizontal velocity in px/s
     * @param {number} velocityY - Vertical velocity in px/s
     */
    updateScrollVelocity(velocityX, velocityY) {
      const scrollPredictor = this.predictiveCaching.scrollPredictor;
      scrollPredictor.scrollHistory.push({
        velocityX,
        velocityY,
        timestamp: performance.now()
      });
      if (scrollPredictor.scrollHistory.length > scrollPredictor.velocitySamples) {
        scrollPredictor.scrollHistory.shift();
      }
      if (scrollPredictor.scrollHistory.length > 0) {
        const avgVelocityX = scrollPredictor.scrollHistory.reduce((sum, sample) => sum + sample.velocityX, 0) / scrollPredictor.scrollHistory.length;
        const avgVelocityY = scrollPredictor.scrollHistory.reduce((sum, sample) => sum + sample.velocityY, 0) / scrollPredictor.scrollHistory.length;
        scrollPredictor.currentVelocity = { x: avgVelocityX, y: avgVelocityY };
      }
    }
    /**
     * Predict future scroll position (Task 2.2)
     */
    predictScrollPosition() {
      const scrollPredictor = this.predictiveCaching.scrollPredictor;
      const currentPosition = { x: window.scrollX, y: window.scrollY };
      const velocity = scrollPredictor.currentVelocity;
      const predictionTimeMs = 500;
      const predictionTimeS = predictionTimeMs / 1e3;
      scrollPredictor.predictedPosition = {
        x: currentPosition.x + velocity.x * predictionTimeS,
        y: currentPosition.y + velocity.y * predictionTimeS
      };
      if (this.debugLogging && (Math.abs(velocity.x) > scrollPredictor.minVelocity || Math.abs(velocity.y) > scrollPredictor.minVelocity)) {
        console.log("[KeyPilot Debug] Scroll prediction updated:", {
          currentPosition,
          velocity,
          predictedPosition: scrollPredictor.predictedPosition,
          predictionTime: predictionTimeMs + "ms"
        });
      }
    }
    /**
     * Preload elements in predicted scroll direction (Task 2.2)
     */
    preloadForScrollDirection() {
      const scrollPredictor = this.predictiveCaching.scrollPredictor;
      const velocity = scrollPredictor.currentVelocity;
      const minVelocity = scrollPredictor.minVelocity;
      if (Math.abs(velocity.x) < minVelocity && Math.abs(velocity.y) < minVelocity) {
        return;
      }
      const currentPosition = { x: window.scrollX, y: window.scrollY };
      const predictionDistance = scrollPredictor.predictionDistance;
      let preloadArea;
      if (Math.abs(velocity.y) > Math.abs(velocity.x)) {
        if (velocity.y > 0) {
          preloadArea = {
            left: currentPosition.x,
            top: currentPosition.y + window.innerHeight,
            right: currentPosition.x + window.innerWidth,
            bottom: currentPosition.y + window.innerHeight + predictionDistance
          };
        } else {
          preloadArea = {
            left: currentPosition.x,
            top: currentPosition.y - predictionDistance,
            right: currentPosition.x + window.innerWidth,
            bottom: currentPosition.y
          };
        }
      } else {
        if (velocity.x > 0) {
          preloadArea = {
            left: currentPosition.x + window.innerWidth,
            top: currentPosition.y,
            right: currentPosition.x + window.innerWidth + predictionDistance,
            bottom: currentPosition.y + window.innerHeight
          };
        } else {
          preloadArea = {
            left: currentPosition.x - predictionDistance,
            top: currentPosition.y,
            right: currentPosition.x,
            bottom: currentPosition.y + window.innerHeight
          };
        }
      }
      const elementsToPreload = this.findElementsInArea(preloadArea);
      elementsToPreload.forEach((element) => {
        if (!this.textNodeCache.has(element)) {
          this.preloadElementTextNodes(element, "scroll_prediction");
        }
      });
      if (this.debugLogging && elementsToPreload.length > 0) {
        console.log("[KeyPilot Debug] Scroll-based preloading:", {
          velocity,
          preloadArea,
          elementsPreloaded: elementsToPreload.length
        });
      }
    }
    /**
     * Initialize user behavior analysis (Task 2.2)
     */
    initializeBehaviorAnalysis() {
      const behaviorAnalyzer = this.predictiveCaching.behaviorAnalyzer;
      const originalUpdateRectangle = this.updateRectangle.bind(this);
      this.updateRectangle = (rect) => {
        this.recordUserInteraction("rectangle_update", { rect });
        return originalUpdateRectangle(rect);
      };
      setInterval(() => {
        this.analyzeBehaviorPatterns();
      }, behaviorAnalyzer.analysisInterval);
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Behavior analysis initialized");
      }
    }
    /**
     * Record user interaction for behavior analysis (Task 2.2)
     * @param {string} type - Type of interaction
     * @param {Object} data - Interaction data
     */
    recordUserInteraction(type2, data) {
      const behaviorAnalyzer = this.predictiveCaching.behaviorAnalyzer;
      const interaction = {
        type: type2,
        data,
        timestamp: Date.now(),
        viewport: {
          x: window.scrollX,
          y: window.scrollY,
          width: window.innerWidth,
          height: window.innerHeight
        }
      };
      behaviorAnalyzer.interactionHistory.push(interaction);
      if (behaviorAnalyzer.interactionHistory.length > behaviorAnalyzer.patternWindow) {
        behaviorAnalyzer.interactionHistory.shift();
      }
    }
    /**
     * Analyze user behavior patterns (Task 2.2)
     */
    analyzeBehaviorPatterns() {
      const behaviorAnalyzer = this.predictiveCaching.behaviorAnalyzer;
      const interactions = behaviorAnalyzer.interactionHistory;
      if (interactions.length < 3) {
        return;
      }
      this.analyzeRectanglePatterns(interactions);
      this.analyzeScrollPatterns(interactions);
      this.applyPredictiveCaching();
      behaviorAnalyzer.lastAnalysis = Date.now();
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Behavior patterns analyzed:", {
          interactions: interactions.length,
          patterns: Array.from(behaviorAnalyzer.patterns.entries())
        });
      }
    }
    /**
     * Analyze rectangle selection patterns (Task 2.2)
     * @param {Array} interactions - User interaction history
     */
    analyzeRectanglePatterns(interactions) {
      const behaviorAnalyzer = this.predictiveCaching.behaviorAnalyzer;
      const rectangleInteractions = interactions.filter((i) => i.type === "rectangle_update");
      if (rectangleInteractions.length < 2) {
        return;
      }
      const rectangleSizes = rectangleInteractions.map((i) => ({
        width: i.data.rect.width,
        height: i.data.rect.height,
        area: i.data.rect.width * i.data.rect.height
      }));
      const avgArea = rectangleSizes.reduce((sum, size) => sum + size.area, 0) / rectangleSizes.length;
      const sizeVariance = rectangleSizes.reduce((sum, size) => sum + Math.pow(size.area - avgArea, 2), 0) / rectangleSizes.length;
      const consistentSizing = sizeVariance < avgArea * 0.1;
      if (consistentSizing) {
        behaviorAnalyzer.patterns.set("consistent_rectangle_size", 0.8);
      }
      const positions = rectangleInteractions.map((i) => ({
        x: i.data.rect.left,
        y: i.data.rect.top
      }));
      const positionClusters = this.findPositionClusters(positions);
      if (positionClusters.length > 0) {
        behaviorAnalyzer.patterns.set("repeated_areas", 0.7);
      }
    }
    /**
     * Find position clusters in user interactions (Task 2.2)
     * @param {Array} positions - Array of {x, y} positions
     * @returns {Array} - Array of position clusters
     */
    findPositionClusters(positions) {
      const clusters = [];
      const clusterRadius = 100;
      positions.forEach((pos) => {
        let addedToCluster = false;
        for (const cluster of clusters) {
          const distance = Math.sqrt(
            Math.pow(pos.x - cluster.center.x, 2) + Math.pow(pos.y - cluster.center.y, 2)
          );
          if (distance <= clusterRadius) {
            cluster.positions.push(pos);
            cluster.center.x = cluster.positions.reduce((sum, p) => sum + p.x, 0) / cluster.positions.length;
            cluster.center.y = cluster.positions.reduce((sum, p) => sum + p.y, 0) / cluster.positions.length;
            addedToCluster = true;
            break;
          }
        }
        if (!addedToCluster) {
          clusters.push({
            center: { x: pos.x, y: pos.y },
            positions: [pos]
          });
        }
      });
      return clusters.filter((cluster) => cluster.positions.length > 1);
    }
    /**
     * Analyze scroll patterns (Task 2.2)
     * @param {Array} interactions - User interaction history
     */
    analyzeScrollPatterns(interactions) {
      const behaviorAnalyzer = this.predictiveCaching.behaviorAnalyzer;
      const viewportChanges = interactions.map((i) => i.viewport);
      if (viewportChanges.length < 3) {
        return;
      }
      let verticalScrolls = 0;
      let horizontalScrolls = 0;
      for (let i = 1; i < viewportChanges.length; i++) {
        const prev = viewportChanges[i - 1];
        const curr = viewportChanges[i];
        const deltaY = Math.abs(curr.y - prev.y);
        const deltaX = Math.abs(curr.x - prev.x);
        if (deltaY > 10) verticalScrolls++;
        if (deltaX > 10) horizontalScrolls++;
      }
      if (verticalScrolls > horizontalScrolls * 2) {
        behaviorAnalyzer.patterns.set("vertical_scrolling_preference", 0.8);
      } else if (horizontalScrolls > verticalScrolls * 2) {
        behaviorAnalyzer.patterns.set("horizontal_scrolling_preference", 0.8);
      }
    }
    /**
     * Apply predictive caching based on detected patterns (Task 2.2)
     */
    applyPredictiveCaching() {
      const behaviorAnalyzer = this.predictiveCaching.behaviorAnalyzer;
      const patterns = behaviorAnalyzer.patterns;
      patterns.forEach((confidence, pattern) => {
        if (confidence >= behaviorAnalyzer.minConfidence) {
          switch (pattern) {
            case "consistent_rectangle_size":
              this.preloadForConsistentSizes();
              break;
            case "repeated_areas":
              this.preloadRepeatedAreas();
              break;
            case "vertical_scrolling_preference":
              this.preloadVerticalScrollAreas();
              break;
            case "horizontal_scrolling_preference":
              this.preloadHorizontalScrollAreas();
              break;
          }
        }
      });
    }
    /**
     * Start cache preloading system (Task 2.2)
     */
    startCachePreloading() {
      const preloader = this.predictiveCaching.preloader;
      const processPreloadQueue = () => {
        if (preloader.preloadQueue.length === 0) {
          return;
        }
        const now = Date.now();
        if (now - preloader.lastPreloadTime < preloader.throttleMs) {
          return;
        }
        const batch = preloader.preloadQueue.splice(0, preloader.batchSize);
        batch.forEach((request) => {
          this.executePreloadRequest(request);
        });
        preloader.lastPreloadTime = now;
        if (this.debugLogging && batch.length > 0) {
          console.log("[KeyPilot Debug] Preload batch processed:", {
            batchSize: batch.length,
            remainingQueue: preloader.preloadQueue.length
          });
        }
      };
      preloader.preloadTimer = setInterval(processPreloadQueue, preloader.throttleMs);
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Cache preloading started");
      }
    }
    /**
     * Start cache management and monitoring timers
     */
    startCacheManagement() {
      if (EDGE_ONLY_SELECTION.ENABLE_MEMORY_MONITORING) {
        this.memoryMonitoringTimer = setInterval(() => {
          this.monitorMemoryUsage();
        }, EDGE_ONLY_SELECTION.MEMORY_CHECK_INTERVAL);
      }
      this.cacheManagementTimer = setInterval(() => {
        this.optimizeCacheEfficiency();
      }, EDGE_ONLY_SELECTION.PERFORMANCE_MONITORING_INTERVAL);
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Cache management timers started:", {
          memoryMonitoring: EDGE_ONLY_SELECTION.ENABLE_MEMORY_MONITORING,
          memoryCheckInterval: EDGE_ONLY_SELECTION.MEMORY_CHECK_INTERVAL + "ms",
          cacheOptimizationInterval: EDGE_ONLY_SELECTION.PERFORMANCE_MONITORING_INTERVAL + "ms"
        });
      }
    }
    /**
     * Create a dynamic root element that represents the selection rectangle
     */
    createRectangleRoot() {
      this.rectangleRoot = document.createElement("div");
      this.rectangleRoot.id = "kpv2-rectangle-intersection-root";
      this.rectangleRoot.style.cssText = `
      position: fixed;
      pointer-events: none;
      z-index: -1;
      background: transparent;
      border: none;
      visibility: hidden;
      left: 0px;
      top: 0px;
      width: 0px;
      height: 0px;
    `;
      document.body.appendChild(this.rectangleRoot);
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Rectangle intersection root created");
      }
    }
    /**
     * Setup the intersection observer for text elements
     */
    setupTextObserver() {
      try {
        this.textObserver = new IntersectionObserver(
          (entries) => this.handleIntersectionChanges(entries),
          {
            root: this.rectangleRoot,
            rootMargin: "0px",
            threshold: [0, 0.1, 0.5, 1]
            // Multiple thresholds for better granularity
          }
        );
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Text intersection observer created");
        }
      } catch (error) {
        console.warn("[KeyPilot] Failed to create IntersectionObserver for rectangle selection:", error);
        this.textObserver = null;
      }
    }
    /**
     * Handle intersection changes from the observer - ENHANCED EDGE-ONLY PROCESSING WITH ADAPTIVE PROCESSING
     * Only processes elements entering/leaving rectangle boundaries for maximum efficiency
     * @param {Array} entries - Intersection observer entries
     */
    handleIntersectionChanges(entries) {
      this.metrics.intersectionUpdates++;
      this.metrics.lastUpdateTime = performance.now();
      if (this.adaptiveProcessor.enabled) {
        this.handleAdaptiveIntersectionChanges(entries);
      } else {
        this.handleStandardIntersectionChanges(entries);
      }
    }
    /**
     * Handle intersection changes with adaptive processing (Task 2.1)
     * @param {Array} entries - Intersection observer entries
     */
    handleAdaptiveIntersectionChanges(entries) {
      const startTime = performance.now();
      const frameMonitor = this.adaptiveProcessor.frameRateMonitor;
      const batchProcessor = this.adaptiveProcessor.batchProcessor;
      const qualityController = this.adaptiveProcessor.qualityController;
      const availableTime = frameMonitor.processingTimeBudget;
      const estimatedProcessingTime = entries.length * 2;
      if (qualityController.enabled) {
        if (estimatedProcessingTime > qualityController.thresholds.medium) {
          this.adjustProcessingQuality("low");
        } else if (estimatedProcessingTime > qualityController.thresholds.high) {
          this.adjustProcessingQuality("medium");
        } else {
          this.adjustProcessingQuality("high");
        }
      }
      if (batchProcessor.enabled && entries.length > batchProcessor.currentBatchSize) {
        this.processBatchedIntersectionChanges(entries, startTime, availableTime);
      } else {
        this.processImmediateIntersectionChanges(entries, startTime, availableTime);
      }
      const now = Date.now();
      if (now - this.adaptiveProcessor.pageComplexity.lastAnalysis > this.adaptiveProcessor.pageComplexity.analysisInterval) {
        requestAnimationFrame(() => this.analyzePageComplexity());
      }
    }
    /**
     * Process intersection changes in batches (Task 2.1)
     * @param {Array} entries - Intersection observer entries
     * @param {number} startTime - Processing start time
     * @param {number} availableTime - Available processing time budget
     */
    processBatchedIntersectionChanges(entries, startTime, availableTime) {
      const batchProcessor = this.adaptiveProcessor.batchProcessor;
      const qualityController = this.adaptiveProcessor.qualityController;
      batchProcessor.pendingBatch.push(...entries);
      if (batchProcessor.batchTimer) {
        clearTimeout(batchProcessor.batchTimer);
      }
      const shouldProcessImmediately = batchProcessor.pendingBatch.length >= batchProcessor.currentBatchSize || qualityController.currentQuality === "low";
      if (shouldProcessImmediately) {
        this.processPendingBatch(startTime, availableTime);
      } else {
        batchProcessor.batchTimer = setTimeout(() => {
          this.processPendingBatch(performance.now(), availableTime);
        }, batchProcessor.batchTimeout);
      }
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Batch processing scheduled:", {
          entriesAdded: entries.length,
          pendingBatchSize: batchProcessor.pendingBatch.length,
          currentBatchSize: batchProcessor.currentBatchSize,
          processedImmediately: shouldProcessImmediately,
          qualityLevel: qualityController.currentQuality
        });
      }
    }
    /**
     * Process the pending batch of intersection changes (Task 2.1)
     * @param {number} startTime - Processing start time
     * @param {number} availableTime - Available processing time budget
     */
    processPendingBatch(startTime, availableTime) {
      const batchProcessor = this.adaptiveProcessor.batchProcessor;
      const qualityController = this.adaptiveProcessor.qualityController;
      if (batchProcessor.pendingBatch.length === 0) {
        return;
      }
      const entriesToProcess = batchProcessor.pendingBatch.splice(0, batchProcessor.currentBatchSize);
      let maxElementsToProcess = entriesToProcess.length;
      if (qualityController.enabled && qualityController.currentQuality === "low") {
        maxElementsToProcess = Math.min(
          maxElementsToProcess,
          qualityController.thresholds.lowProcessingLimit
        );
      }
      const processedEntries = entriesToProcess.slice(0, maxElementsToProcess);
      this.processIntersectionEntries(processedEntries, startTime);
      if (batchProcessor.pendingBatch.length > 0) {
        requestAnimationFrame(() => {
          this.processPendingBatch(performance.now(), availableTime);
        });
      }
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Batch processed:", {
          entriesProcessed: processedEntries.length,
          remainingInBatch: batchProcessor.pendingBatch.length,
          qualityLevel: qualityController.currentQuality,
          maxElementsLimit: maxElementsToProcess,
          processingTime: (performance.now() - startTime).toFixed(2) + "ms"
        });
      }
    }
    /**
     * Process intersection changes immediately without batching (Task 2.1)
     * @param {Array} entries - Intersection observer entries
     * @param {number} startTime - Processing start time
     * @param {number} availableTime - Available processing time budget
     */
    processImmediateIntersectionChanges(entries, startTime, availableTime) {
      const qualityController = this.adaptiveProcessor.qualityController;
      let maxElementsToProcess = entries.length;
      if (qualityController.enabled && qualityController.currentQuality === "low") {
        maxElementsToProcess = Math.min(
          maxElementsToProcess,
          qualityController.thresholds.lowProcessingLimit
        );
      }
      const entriesToProcess = entries.slice(0, maxElementsToProcess);
      this.processIntersectionEntries(entriesToProcess, startTime);
      if (maxElementsToProcess < entries.length && this.debugLogging) {
        console.log("[KeyPilot Debug] Quality-limited processing:", {
          totalEntries: entries.length,
          processedEntries: maxElementsToProcess,
          skippedEntries: entries.length - maxElementsToProcess,
          qualityLevel: qualityController.currentQuality
        });
      }
    }
    /**
     * Process intersection entries with standard logic (Task 2.1)
     * @param {Array} entries - Intersection observer entries to process
     * @param {number} startTime - Processing start time
     */
    processIntersectionEntries(entries, startTime) {
      let elementsProcessed = 0;
      let elementsEntering = 0;
      let elementsLeaving = 0;
      let hasChanges = false;
      entries.forEach((entry) => {
        const element = entry.target;
        const elementId = this.getElementId(element);
        const wasIntersecting = this.elementStates.get(elementId) || false;
        const isIntersecting = entry.isIntersecting;
        elementsProcessed++;
        if (isIntersecting && !wasIntersecting) {
          this.elementStates.set(elementId, true);
          this.intersectingTextElements.add(element);
          if (this.enhancedIntegrationEnabled && this.edgeCharacterDetectionEnabled && this.edgeCharacterDetector) {
            this.handleElementEnteringWithCharacterDetection(element);
          } else {
            this.extractTextNodesFromElement(element);
          }
          elementsEntering++;
          hasChanges = true;
          if (this.debugLogging) {
            console.log("[KeyPilot Debug] Element ENTERING rectangle:", {
              element: element.tagName,
              className: element.className || "none",
              id: element.id || "none",
              textLength: element.textContent?.length || 0,
              boundingRect: element.getBoundingClientRect(),
              intersectionRatio: entry.intersectionRatio
            });
          }
        } else if (!isIntersecting && wasIntersecting) {
          this.elementStates.set(elementId, false);
          this.intersectingTextElements.delete(element);
          if (this.enhancedIntegrationEnabled && this.edgeCharacterDetectionEnabled && this.edgeCharacterDetector) {
            this.handleElementLeavingWithCharacterDetection(element);
          } else {
            this.removeTextNodesFromElement(element);
          }
          elementsLeaving++;
          hasChanges = true;
          if (this.debugLogging) {
            console.log("[KeyPilot Debug] Element LEAVING rectangle:", {
              element: element.tagName,
              className: element.className || "none",
              id: element.id || "none",
              textLength: element.textContent?.length || 0,
              intersectionRatio: entry.intersectionRatio
            });
          }
        }
      });
      const processingTime = performance.now() - startTime;
      this.metrics.edgeProcessingTime = processingTime;
      this.metrics.edgeElementsProcessed = elementsProcessed;
      this.metrics.elementsEntering += elementsEntering;
      this.metrics.elementsLeaving += elementsLeaving;
      this.metrics.actualEdgeProcessingTime += processingTime;
      this.updatePerformanceComparison(elementsProcessed, processingTime);
      if (hasChanges && this.onIntersectionChange) {
        this.onIntersectionChange({
          intersectingElements: Array.from(this.intersectingTextElements),
          intersectingTextNodes: Array.from(this.intersectingTextNodes),
          metrics: this.getMetrics(),
          edgeProcessing: {
            elementsProcessed,
            elementsEntering,
            elementsLeaving,
            processingTime,
            efficiency: elementsProcessed > 0 ? (processingTime / elementsProcessed).toFixed(2) + "ms/element" : "N/A",
            performanceGain: this.calculatePerformanceGain()
          },
          adaptiveProcessing: this.adaptiveProcessor.enabled ? {
            pageComplexity: this.adaptiveProcessor.pageComplexity.complexityLevel,
            currentFps: this.adaptiveProcessor.frameRateMonitor.currentFps.toFixed(1),
            batchSize: this.adaptiveProcessor.batchProcessor.currentBatchSize,
            qualityLevel: this.adaptiveProcessor.qualityController.currentQuality
          } : null
        });
      }
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Enhanced edge-only intersection update with adaptive processing:", {
          entriesProcessed: entries.length,
          elementsProcessed,
          elementsEntering,
          elementsLeaving,
          processingTime: processingTime.toFixed(2) + "ms",
          intersectingElements: this.intersectingTextElements.size,
          intersectingTextNodes: this.intersectingTextNodes.size,
          hasChanges,
          efficiency: elementsProcessed > 0 ? (processingTime / elementsProcessed).toFixed(2) + "ms/element" : "N/A",
          cacheHitRatio: this.getCacheHitRatio(),
          performanceGain: this.calculatePerformanceGain(),
          adaptiveProcessing: this.adaptiveProcessor.enabled ? {
            pageComplexity: this.adaptiveProcessor.pageComplexity.complexityLevel,
            currentFps: this.adaptiveProcessor.frameRateMonitor.currentFps.toFixed(1),
            batchSize: this.adaptiveProcessor.batchProcessor.currentBatchSize,
            qualityLevel: this.adaptiveProcessor.qualityController.currentQuality
          } : null
        });
      }
    }
    /**
     * Handle intersection changes with standard processing (fallback)
     * @param {Array} entries - Intersection observer entries
     */
    handleStandardIntersectionChanges(entries) {
      const startTime = performance.now();
      this.processIntersectionEntries(entries, startTime);
    }
    /**
     * Extract text nodes from an element and add to intersection set
     * ENHANCED EDGE-ONLY OPTIMIZATION: Only called for elements entering rectangle
     * @param {Element} element - Element to extract text nodes from
     */
    extractTextNodesFromElement(element) {
      const startTime = performance.now();
      if (this.textNodeCache.has(element)) {
        const cachedNodes = this.textNodeCache.get(element);
        cachedNodes.forEach((node2) => this.intersectingTextNodes.add(node2));
        this.metrics.cacheHits++;
        this.cacheEfficiencyMetrics.cacheHitStreak++;
        this.cacheEfficiencyMetrics.cacheMissStreak = 0;
        this.cacheEfficiencyMetrics.longestHitStreak = Math.max(
          this.cacheEfficiencyMetrics.longestHitStreak,
          this.cacheEfficiencyMetrics.cacheHitStreak
        );
        this.updateCacheAccess(element);
        if (this.debugLogging) {
          console.log("[KeyPilot Debug] Used cached text nodes (FAST PATH):", {
            element: element.tagName,
            className: element.className || "none",
            cachedNodes: cachedNodes.length,
            processingTime: (performance.now() - startTime).toFixed(2) + "ms",
            cacheHitRatio: this.getCacheHitRatio(),
            cacheSize: this.cacheSize,
            hitStreak: this.cacheEfficiencyMetrics.cacheHitStreak
          });
        }
        return;
      }
      this.metrics.cacheMisses++;
      this.cacheEfficiencyMetrics.cacheMissStreak++;
      this.cacheEfficiencyMetrics.cacheHitStreak = 0;
      this.cacheEfficiencyMetrics.longestMissStreak = Math.max(
        this.cacheEfficiencyMetrics.longestMissStreak,
        this.cacheEfficiencyMetrics.cacheMissStreak
      );
      const textNodes = [];
      const walker = document.createTreeWalker(
        element,
        NodeFilter.SHOW_TEXT,
        {
          acceptNode: (node2) => {
            if (!node2.textContent || !node2.textContent.trim()) {
              return NodeFilter.FILTER_REJECT;
            }
            const parent = node2.parentElement;
            if (parent && this.isNonContentElement(parent)) {
              return NodeFilter.FILTER_REJECT;
            }
            return NodeFilter.FILTER_ACCEPT;
          }
        }
      );
      let node;
      while (node = walker.nextNode()) {
        textNodes.push(node);
        this.intersectingTextNodes.add(node);
      }
      this.cacheTextNodes(element, textNodes);
      const processingTime = performance.now() - startTime;
      this.metrics.textNodesFound += textNodes.length;
      this.metrics.totalTraversalTime += processingTime;
      this.metrics.traversalCount++;
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Extracted text nodes from NEW element (SLOW PATH):", {
          element: element.tagName,
          className: element.className || "none",
          textNodesFound: textNodes.length,
          processingTime: processingTime.toFixed(2) + "ms",
          totalTextNodes: this.intersectingTextNodes.size,
          avgTraversalTime: (this.metrics.totalTraversalTime / this.metrics.traversalCount).toFixed(2) + "ms",
          cacheHitRatio: this.getCacheHitRatio(),
          cacheEfficiency: this.calculateCacheEfficiency()
        });
      }
    }
    /**
     * Remove text nodes from an element from the intersection set
     * @param {Element} element - Element to remove text nodes from
     */
    removeTextNodesFromElement(element) {
      const cachedNodes = this.textNodeCache.get(element);
      if (cachedNodes) {
        cachedNodes.forEach((node) => this.intersectingTextNodes.delete(node));
      }
    }
    /**
     * Check if an element is a non-content element that should be excluded
     * @param {Element} element - Element to check
     * @returns {boolean} - True if element should be excluded
     */
    isNonContentElement(element) {
      const tagName = (element.tagName || "").toLowerCase();
      const excludedTags = ["script", "style", "meta", "link", "title", "noscript", "template"];
      if (tagName && excludedTags.includes(tagName)) {
        return true;
      }
      const rawClassName = element.className;
      const className = typeof rawClassName === "string" ? rawClassName : rawClassName && typeof rawClassName.baseVal === "string" ? rawClassName.baseVal : typeof element.getAttribute === "function" ? element.getAttribute("class") || "" : "";
      const excludedClasses = ["footer", "header", "nav", "sidebar", "advertisement"];
      return excludedClasses.some((cls) => className.includes(cls));
    }
    /**
     * Generate a unique identifier for an element for state tracking
     * @param {Element} element - Element to generate ID for
     * @returns {string} - Unique element identifier
     */
    getElementId(element) {
      if (!element._keypilotId) {
        element._keypilotId = "kp_" + Math.random().toString(36).substr(2, 9);
      }
      return element._keypilotId;
    }
    /**
     * Update performance comparison metrics
     * @param {number} elementsProcessed - Number of elements processed in this update
     * @param {number} processingTime - Time taken for this update
     */
    updatePerformanceComparison(elementsProcessed, processingTime) {
      const avgElementTime = this.metrics.traversalCount > 0 ? this.metrics.totalTraversalTime / this.metrics.traversalCount : 5;
      const estimatedFullTime = avgElementTime * this.metrics.elementsObserved;
      this.metrics.estimatedFullTraversalTime += estimatedFullTime;
      if (this.metrics.estimatedFullTraversalTime > 0) {
        this.metrics.efficiencyGainPercent = (this.metrics.estimatedFullTraversalTime - this.metrics.actualEdgeProcessingTime) / this.metrics.estimatedFullTraversalTime * 100;
      }
      if (this.enhancedIntegrationEnabled && this.performanceMonitoringEnabled) {
        this.recordPerformanceComparison({
          estimatedTraditionalTime: estimatedFullTime,
          actualOptimizedTime: processingTime
        });
      }
    }
    /**
     * Calculate current cache hit ratio
     * @returns {string} - Cache hit ratio as percentage
     */
    getCacheHitRatio() {
      const totalCacheAttempts = this.metrics.cacheHits + this.metrics.cacheMisses;
      if (totalCacheAttempts === 0) return "0%";
      return (this.metrics.cacheHits / totalCacheAttempts * 100).toFixed(1) + "%";
    }
    /**
     * Calculate cache efficiency metrics
     * @returns {Object} - Cache efficiency data
     */
    calculateCacheEfficiency() {
      return this.getCacheEfficiencyMetrics();
    }
    /**
     * Calculate overall performance gain from edge-only processing
     * @returns {string} - Performance gain description
     */
    calculatePerformanceGain() {
      if (this.metrics.estimatedFullTraversalTime === 0) {
        return "Calculating...";
      }
      const actualTime = this.metrics.actualEdgeProcessingTime;
      const estimatedTime = this.metrics.estimatedFullTraversalTime;
      if (estimatedTime <= actualTime) {
        return "No gain detected";
      }
      const gainPercent = (estimatedTime - actualTime) / estimatedTime * 100;
      const speedupFactor = estimatedTime / actualTime;
      return `${gainPercent.toFixed(1)}% faster (${speedupFactor.toFixed(1)}x speedup)`;
    }
    /**
     * Get element reduction metrics from smart targeting (Task 2.1)
     * @returns {Object} Element reduction performance metrics
     */
    getElementReductionMetrics() {
      if (!this.enhancedIntegrationEnabled || !this.textElementFilter) {
        return {
          totalElementsScanned: 0,
          elementsObserved: this.metrics.elementsObserved,
          elementsSkipped: 0,
          reductionPercentage: 0,
          smartTargetingEnabled: false,
          averageProcessingTime: 0,
          totalProcessingTime: 0,
          dynamicElementsAdded: 0
        };
      }
      const filterStats = this.textElementFilter.getStats();
      this.metrics.totalElementsScanned = filterStats.totalElementsScanned;
      this.metrics.elementsSkipped = filterStats.elementsSkipped;
      this.metrics.elementReductionPercentage = parseFloat(filterStats.reductionPercentage);
      this.metrics.smartTargetingTime = filterStats.totalProcessingTime;
      return {
        totalElementsScanned: this.metrics.totalElementsScanned,
        elementsObserved: this.metrics.elementsObserved,
        elementsSkipped: this.metrics.elementsSkipped,
        reductionPercentage: this.metrics.elementReductionPercentage,
        smartTargetingEnabled: this.smartTargetingEnabled,
        averageProcessingTime: filterStats.averageProcessingTime,
        totalProcessingTime: this.metrics.smartTargetingTime,
        dynamicElementsAdded: this.metrics.dynamicElementsAdded
      };
    }
    /**
     * Get character detection metrics from edge-level processing (Task 2.2)
     * @returns {Object} Character detection performance metrics
     */
    getCharacterDetectionMetrics() {
      const detectorStats = this.edgeCharacterDetector.getStats();
      this.metrics.characterDetectionTime = detectorStats.totalProcessingTime;
      this.metrics.characterCacheHits = detectorStats.cacheHits;
      this.metrics.characterCacheMisses = detectorStats.cacheMisses;
      return {
        charactersDetected: this.metrics.charactersDetected,
        characterDetectionEnabled: this.edgeCharacterDetectionEnabled,
        cacheHits: this.metrics.characterCacheHits,
        cacheMisses: this.metrics.characterCacheMisses,
        cacheHitRate: detectorStats.cacheHitRate,
        averageProcessingTime: detectorStats.averageProcessingTime,
        totalProcessingTime: this.metrics.characterDetectionTime,
        elementsProcessed: detectorStats.elementsProcessed
      };
    }
    /**
     * Get comprehensive performance metrics from PerformanceMonitor (Task 2.3)
     * @returns {Object} Comprehensive performance metrics
     */
    getPerformanceMetrics() {
      return this.getPerformanceMonitorMetrics();
    }
    /**
     * Get performance comparison statistics (Task 2.3)
     * @returns {Object} Performance comparison between optimized and traditional approaches
     */
    getPerformanceComparison() {
      return this.getComparisonStats();
    }
    /**
     * Get trend analysis results (Task 2.3)
     * @returns {Object} Performance trend analysis
     */
    getPerformanceTrends() {
      return this.getTrendAnalysis();
    }
    /**
     * Export complete performance data for analysis (Task 2.3)
     * @returns {Object} Complete performance data export
     */
    exportPerformanceData() {
      if (!this.performanceMonitoringEnabled) {
        return null;
      }
      return {
        performanceData: this.exportData(),
        elementReduction: this.getElementReductionMetrics(),
        characterDetection: this.getCharacterDetectionMetrics(),
        legacyMetrics: this.getMetrics(),
        configuration: {
          smartTargetingEnabled: this.smartTargetingEnabled,
          edgeCharacterDetectionEnabled: this.edgeCharacterDetectionEnabled,
          performanceMonitoringEnabled: this.performanceMonitoringEnabled
        }
      };
    }
    /**
     * Update the rectangle bounds for intersection detection
     * @param {Object} rect - Rectangle with left, top, width, height properties
     */
    updateRectangle(rect) {
      if (!this.rectangleRoot) {
        return;
      }
      this.currentRectangle = {
        left: rect.left,
        top: rect.top,
        right: rect.left + rect.width,
        bottom: rect.top + rect.height,
        width: rect.width,
        height: rect.height
      };
      this.rectangleRoot.style.left = `${rect.left}px`;
      this.rectangleRoot.style.top = `${rect.top}px`;
      this.rectangleRoot.style.width = `${rect.width}px`;
      this.rectangleRoot.style.height = `${rect.height}px`;
      this.lastRectangleUpdate = performance.now();
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Rectangle bounds updated:", {
          left: rect.left,
          top: rect.top,
          width: rect.width,
          height: rect.height,
          observedElements: this.metrics.elementsObserved,
          characterDetectionEnabled: this.edgeCharacterDetectionEnabled
        });
      }
    }
    /**
     * Discover and observe all text-containing elements in the document
     * Uses smart element targeting to reduce observed elements by 95%+
     */
    discoverTextElements() {
      if (!this.textObserver) {
        return;
      }
      const startTime = performance.now();
      let textElements;
      if (this.enhancedIntegrationEnabled && this.smartTargetingEnabled && this.textElementFilter) {
        textElements = this.textElementFilter.findTextElements(document.body);
        const filterStats = this.textElementFilter.getStats();
        this.metrics.elementsObserved = 0;
        textElements.forEach((element) => {
          this.textObserver.observe(element);
          this.observedElements.add(element);
          this.metrics.elementsObserved++;
        });
        if (this.enhancedIntegrationEnabled && this.performanceMonitoringEnabled) {
          this.recordSmartTargeting({
            elementReductionPercentage: parseFloat(filterStats.reductionPercentage),
            smartTargetingTime: performance.now() - startTime,
            dynamicElementsAdded: 0
          });
        }
        if (this.debugLogging) {
          console.log("[KeyPilot Debug] Smart element targeting completed:", {
            totalElementsScanned: filterStats.totalElementsScanned,
            textElementsFound: filterStats.textElementsFound,
            reductionPercentage: filterStats.reductionPercentage,
            observedElements: this.metrics.elementsObserved,
            processingTime: (performance.now() - startTime).toFixed(2) + "ms"
          });
        }
      } else {
        const textContainerSelectors = [
          "p",
          "div",
          "span",
          "h1",
          "h2",
          "h3",
          "h4",
          "h5",
          "h6",
          "li",
          "td",
          "th",
          "blockquote",
          "pre",
          "code",
          "a",
          "strong",
          "em",
          "b",
          "i",
          "u",
          "article",
          "section",
          "main",
          "aside",
          "[contenteditable]"
        ].join(", ");
        const allElements = document.querySelectorAll(textContainerSelectors);
        textElements = Array.from(allElements).filter((element) => this.elementContainsText(element));
        textElements.forEach((element) => {
          this.textObserver.observe(element);
          this.observedElements.add(element);
          this.metrics.elementsObserved++;
        });
        if (this.debugLogging) {
          console.log("[KeyPilot Debug] Traditional element discovery completed:", {
            totalElements: allElements.length,
            observedElements: this.metrics.elementsObserved,
            processingTime: (performance.now() - startTime).toFixed(2) + "ms"
          });
        }
      }
    }
    /**
     * Handle element entering rectangle with edge-level character detection (Task 2.2)
     * @param {Element} element - Element entering the rectangle
     */
    handleElementEnteringWithCharacterDetection(element) {
      if (!this.currentRectangle) {
        this.extractTextNodesFromElement(element);
        return;
      }
      const startTime = performance.now();
      try {
        const intersectingCharacters = this.edgeCharacterDetector.detectAtBoundary(
          element,
          this.currentRectangle
        );
        this.addCharactersToSelection(intersectingCharacters);
        const processingTime = performance.now() - startTime;
        this.metrics.edgeProcessingTime += processingTime;
        if (this.enhancedIntegrationEnabled && this.performanceMonitoringEnabled) {
          const detectorStats = this.edgeCharacterDetector.getStats();
          this.recordCharacterDetection({
            charactersDetected: intersectingCharacters.length,
            detectionTime: processingTime,
            cacheHits: detectorStats.cacheHits,
            cacheMisses: detectorStats.cacheMisses
          });
        }
        if (this.debugLogging) {
          console.log("[KeyPilot Debug] Element entering with character detection:", {
            element: element.tagName,
            className: element.className || "none",
            charactersDetected: intersectingCharacters.length,
            processingTime: processingTime.toFixed(2) + "ms",
            cacheHitRate: this.edgeCharacterDetector.getStats().cacheHitRate
          });
        }
      } catch (error) {
        console.warn("[KeyPilot] Error in character detection, falling back to traditional method:", error);
        this.extractTextNodesFromElement(element);
      }
    }
    /**
     * Handle element leaving rectangle with edge-level character detection (Task 2.2)
     * @param {Element} element - Element leaving the rectangle
     */
    handleElementLeavingWithCharacterDetection(element) {
      const startTime = performance.now();
      try {
        this.removeElementCharactersFromSelection(element);
        const processingTime = performance.now() - startTime;
        this.metrics.edgeProcessingTime += processingTime;
        if (this.debugLogging) {
          console.log("[KeyPilot Debug] Element leaving with character detection:", {
            element: element.tagName,
            className: element.className || "none",
            processingTime: processingTime.toFixed(2) + "ms"
          });
        }
      } catch (error) {
        console.warn("[KeyPilot] Error in character removal, falling back to traditional method:", error);
        this.removeTextNodesFromElement(element);
      }
    }
    /**
     * Add characters to the current selection (Task 2.2)
     * @param {Array<Object>} characters - Array of character objects to add
     */
    addCharactersToSelection(characters) {
      characters.forEach((character) => {
        this.intersectingTextNodes.add(character.node);
      });
      this.metrics.charactersDetected = (this.metrics.charactersDetected || 0) + characters.length;
    }
    /**
     * Remove all characters from an element from the selection (Task 2.2)
     * @param {Element} element - Element whose characters should be removed
     */
    removeElementCharactersFromSelection(element) {
      const textNodes = this.edgeCharacterDetector.getTextNodes(element);
      textNodes.forEach((textNode) => {
        this.intersectingTextNodes.delete(textNode);
      });
    }
    /**
     * Handle dynamic content discovery with smart element filtering (Task 2.1)
     * @param {Array<Element>} newElements - New elements to potentially observe
     */
    discoverDynamicTextElements(newElements) {
      if (!this.textObserver || !newElements || newElements.length === 0) {
        return;
      }
      const startTime = performance.now();
      let elementsAdded = 0;
      if (this.smartTargetingEnabled) {
        const textElements = this.textElementFilter.filterTextElements(newElements);
        textElements.forEach((element) => {
          if (!this.observedElements.has(element)) {
            this.textObserver.observe(element);
            this.observedElements.add(element);
            this.metrics.elementsObserved++;
            this.metrics.dynamicElementsAdded++;
            elementsAdded++;
          }
        });
        if (this.debugLogging && elementsAdded > 0) {
          console.log("[KeyPilot Debug] Dynamic content discovery with smart targeting:", {
            newElementsScanned: newElements.length,
            textElementsFound: textElements.length,
            elementsAdded,
            totalObserved: this.metrics.elementsObserved,
            processingTime: (performance.now() - startTime).toFixed(2) + "ms"
          });
        }
      } else {
        newElements.forEach((element) => {
          if (!this.observedElements.has(element) && this.elementContainsText(element)) {
            this.textObserver.observe(element);
            this.observedElements.add(element);
            this.metrics.elementsObserved++;
            this.metrics.dynamicElementsAdded++;
            elementsAdded++;
          }
        });
        if (this.debugLogging && elementsAdded > 0) {
          console.log("[KeyPilot Debug] Dynamic content discovery (traditional):", {
            newElementsScanned: newElements.length,
            elementsAdded,
            totalObserved: this.metrics.elementsObserved,
            processingTime: (performance.now() - startTime).toFixed(2) + "ms"
          });
        }
      }
    }
    /**
     * Check if an element contains meaningful text content
     * @param {Element} element - Element to check
     * @returns {boolean} - True if element contains text
     */
    elementContainsText(element) {
      const textContent = element.textContent || "";
      if (!textContent.trim()) {
        return false;
      }
      if (this.isNonContentElement(element)) {
        return false;
      }
      const hasVisibleText = textContent.replace(/\s+/g, " ").trim().length > 0;
      return hasVisibleText;
    }
    /**
     * Create a text selection from the currently intersecting text nodes
     * @returns {Selection|null} - Browser selection object or null
     */
    createSelectionFromIntersection() {
      if (this.intersectingTextNodes.size === 0) {
        return null;
      }
      try {
        const textNodes = Array.from(this.intersectingTextNodes);
        textNodes.sort((a, b) => {
          const comparison = a.compareDocumentPosition(b);
          if (comparison & Node.DOCUMENT_POSITION_FOLLOWING) {
            return -1;
          } else if (comparison & Node.DOCUMENT_POSITION_PRECEDING) {
            return 1;
          }
          return 0;
        });
        const selection = window.getSelection();
        selection.removeAllRanges();
        const range = document.createRange();
        range.setStart(textNodes[0], 0);
        range.setEnd(textNodes[textNodes.length - 1], textNodes[textNodes.length - 1].textContent.length);
        selection.addRange(range);
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Selection created from intersection:", {
            textNodes: textNodes.length,
            selectedText: selection.toString().substring(0, 100) + "..."
          });
        }
        return selection;
      } catch (error) {
        console.warn("[KeyPilot] Error creating selection from intersection:", error);
        return null;
      }
    }
    /**
     * Monitor performance thresholds and trigger fallback if needed
     * @param {number} processingTime - Time taken for this processing cycle
     * @param {number} elementsProcessed - Number of elements processed
     */
    /**
     * Evaluate recovery attempt and decide whether to stay recovered or return to fallback
     */
    evaluateRecovery() {
      const averageTime = this.calculateAverageProcessingTime();
      const recoverySuccessful = averageTime < this.legacyPerformanceMonitor.thresholdMs && this.legacyPerformanceMonitor.consecutiveSlowOperations === 0;
      if (recoverySuccessful) {
        this.fallbackHandler.active = false;
        this.fallbackHandler.reason = null;
        this.performanceState.fallbackTriggered = false;
        this.notifyRecoverySuccessful();
        if (this.debugLogging) {
          console.log("[KeyPilot Debug] Recovery successful - edge-only processing restored:", {
            averageTime: averageTime.toFixed(2) + "ms",
            threshold: this.legacyPerformanceMonitor.thresholdMs + "ms",
            recoveryAttempt: this.fallbackHandler.recoveryAttempts
          });
        }
      } else {
        this.fallbackHandler.active = true;
        if (this.fallbackHandler.recoveryAttempts < this.fallbackHandler.maxRecoveryAttempts) {
          this.scheduleRecoveryAttempt();
        }
        if (this.debugLogging) {
          console.warn("[KeyPilot Debug] Recovery failed - returning to fallback mode:", {
            averageTime: averageTime.toFixed(2) + "ms",
            threshold: this.legacyPerformanceMonitor.thresholdMs + "ms",
            recoveryAttempt: this.fallbackHandler.recoveryAttempts,
            maxAttempts: this.fallbackHandler.maxRecoveryAttempts
          });
        }
      }
    }
    /**
     * Get cache hit ratio as numeric value
     * @returns {number} - Cache hit ratio (0-1)
     */
    /**
     * Notify user about performance issues
     * @param {Object} alert - Performance alert object
     */
    notifyPerformanceIssue(alert) {
      if (!this.notificationSystem.enabled || !this.notificationSystem.showPerformanceNotifications) {
        return;
      }
      const currentTime = Date.now();
      if (currentTime - this.notificationSystem.lastNotificationTime < this.notificationSystem.notificationCooldownMs) {
        return;
      }
      let message = "";
      switch (alert.type) {
        case "performance_regression":
          message = `KeyPilot: Performance regression detected (${alert.data.degradationFactor}x slower)`;
          break;
        case "low_cache_efficiency":
          message = `KeyPilot: Low cache efficiency (${alert.data.cacheHitRatio})`;
          break;
        case "high_processing_time":
          message = `KeyPilot: High processing time (${alert.data.processingTime})`;
          break;
        default:
          message = `KeyPilot: Performance issue detected (${alert.type})`;
      }
      this.showNotification(message, "warning");
      this.notificationSystem.lastNotificationTime = currentTime;
    }
    /**
     * Notify user about fallback activation
     * @param {string} reason - Reason for fallback
     * @param {Object} data - Additional data
     */
    notifyFallbackActivated(reason, data) {
      if (!this.notificationSystem.enabled || !this.notificationSystem.showPerformanceNotifications) {
        return;
      }
      const message = `KeyPilot: Switched to fallback selection method due to performance issues`;
      this.showNotification(message, "info");
    }
    /**
     * Notify user about successful recovery
     */
    notifyRecoverySuccessful() {
      if (!this.notificationSystem.enabled || !this.notificationSystem.showPerformanceNotifications) {
        return;
      }
      const message = `KeyPilot: Optimized selection method restored`;
      this.showNotification(message, "success");
    }
    /**
     * Show notification to user
     * @param {string} message - Notification message
     * @param {string} type - Notification type (success, warning, error, info)
     */
    showNotification(message, type2 = "info") {
      if (this.notificationSystem.notificationCallback) {
        this.notificationSystem.notificationCallback(message, type2);
      } else {
        const logMethod = type2 === "error" ? "error" : type2 === "warning" ? "warn" : "log";
        console[logMethod](`[KeyPilot Notification] ${message}`);
      }
    }
    /**
     * Set notification callback for user notifications
     * @param {Function} callback - Callback function for notifications
     */
    setNotificationCallback(callback) {
      this.notificationSystem.notificationCallback = callback;
    }
    /**
     * Check if fallback mode is active
     * @returns {boolean} - True if in fallback mode
     */
    isFallbackActive() {
      return this.fallbackHandler.active;
    }
    /**
     * Get current performance status
     * @returns {Object} - Performance status information
     */
    getPerformanceStatus() {
      return {
        fallbackActive: this.fallbackHandler.active,
        fallbackReason: this.fallbackHandler.reason,
        fallbackStartTime: this.fallbackHandler.fallbackStartTime,
        recoveryAttempts: this.fallbackHandler.recoveryAttempts,
        maxRecoveryAttempts: this.fallbackHandler.maxRecoveryAttempts,
        performanceAlerts: this.performanceState.alerts,
        regressionDetected: this.performanceState.regressionDetected,
        baselinePerformance: this.performanceState.baselinePerformance,
        currentAveragePerformance: this.calculateAverageProcessingTime(),
        consecutiveSlowOperations: this.legacyPerformanceMonitor.consecutiveSlowOperations,
        thresholdMs: this.legacyPerformanceMonitor.thresholdMs
      };
    }
    /**
     * Clean up resources and stop timers
     */
    /**
     * Get comprehensive performance metrics with enhanced edge-only processing data
     * @returns {Object} - Complete performance metrics object
     */
    getMetrics() {
      const avgTraversalTime = this.metrics.traversalCount > 0 ? (this.metrics.totalTraversalTime / this.metrics.traversalCount).toFixed(2) : 0;
      const avgEdgeProcessingTime = this.metrics.intersectionUpdates > 0 ? (this.metrics.actualEdgeProcessingTime / this.metrics.intersectionUpdates).toFixed(2) : 0;
      const cacheEfficiency = this.calculateCacheEfficiency();
      return {
        // Basic metrics
        elementsObserved: this.metrics.elementsObserved,
        intersectionUpdates: this.metrics.intersectionUpdates,
        intersectingElements: this.intersectingTextElements.size,
        intersectingTextNodes: this.intersectingTextNodes.size,
        textNodesFound: this.metrics.textNodesFound,
        lastUpdateTime: this.metrics.lastUpdateTime,
        // Edge-only processing metrics
        edgeProcessingTime: this.metrics.edgeProcessingTime.toFixed(2) + "ms",
        edgeElementsProcessed: this.metrics.edgeElementsProcessed,
        elementsEntering: this.metrics.elementsEntering,
        elementsLeaving: this.metrics.elementsLeaving,
        avgEdgeProcessingTime: avgEdgeProcessingTime + "ms",
        // Text node traversal metrics
        totalTraversalTime: this.metrics.totalTraversalTime.toFixed(2) + "ms",
        traversalCount: this.metrics.traversalCount,
        avgTraversalTime: avgTraversalTime + "ms",
        // Cache performance metrics
        cacheHits: this.metrics.cacheHits,
        cacheMisses: this.metrics.cacheMisses,
        cacheHitRatio: cacheEfficiency.hitRatio,
        cacheEfficiency,
        cacheEvictions: this.metrics.cacheEvictions || 0,
        cacheSize: this.cacheSize,
        maxCacheSize: this.maxCacheSize,
        // Advanced cache management metrics
        memoryUsage: this.metrics.memoryUsage ? {
          current: this.metrics.memoryUsage.current.toFixed(2) + "MB",
          peak: this.metrics.memoryUsage.peak.toFixed(2) + "MB",
          average: this.metrics.memoryUsage.average.toFixed(2) + "MB",
          cleanupTriggers: this.metrics.memoryUsage.cleanupTriggers
        } : null,
        cacheUtilization: (this.cacheSize / this.maxCacheSize * 100).toFixed(1) + "%",
        cleanupThreshold: this.cleanupThreshold,
        cleanupBatchSize: this.cleanupBatchSize,
        // Performance comparison metrics
        estimatedFullTraversalTime: this.metrics.estimatedFullTraversalTime.toFixed(2) + "ms",
        actualEdgeProcessingTime: this.metrics.actualEdgeProcessingTime.toFixed(2) + "ms",
        efficiencyGainPercent: this.metrics.efficiencyGainPercent.toFixed(1) + "%",
        performanceGain: this.calculatePerformanceGain(),
        // State tracking
        elementStatesTracked: this.elementStates.size,
        debugLogging: this.debugLogging,
        // Performance monitoring and fallback status
        performanceMonitoring: {
          enabled: this.legacyPerformanceMonitor.enabled,
          fallbackActive: this.fallbackHandler.active,
          fallbackReason: this.fallbackHandler.reason,
          consecutiveSlowOperations: this.legacyPerformanceMonitor.consecutiveSlowOperations,
          thresholdMs: this.legacyPerformanceMonitor.thresholdMs,
          baselinePerformance: this.performanceState.baselinePerformance ? this.performanceState.baselinePerformance.toFixed(2) + "ms" : "Not established",
          currentAveragePerformance: this.calculateAverageProcessingTime().toFixed(2) + "ms",
          regressionDetected: this.performanceState.regressionDetected,
          recoveryAttempts: this.fallbackHandler.recoveryAttempts,
          maxRecoveryAttempts: this.fallbackHandler.maxRecoveryAttempts,
          recentAlerts: this.performanceState.alerts.slice(-3),
          // Last 3 alerts
          processingTimeWindow: this.legacyPerformanceMonitor.processingTimes.map((t) => t.toFixed(2) + "ms")
        }
      };
    }
    /**
     * Enhanced cache access tracking for LRU with detailed metrics
     * @param {Element} element - Element that was accessed
     */
    updateCacheAccess(element) {
      const startTime = performance.now();
      const elementId = this.getElementId(element);
      const currentTime = Date.now();
      const existingRecord = this.cacheAccessOrder.get(element);
      const accessRecord = {
        timestamp: currentTime,
        accessCount: existingRecord ? existingRecord.accessCount + 1 : 1,
        elementId,
        // Store elementId for LRU ordering
        lastAccessTime: existingRecord ? existingRecord.timestamp : currentTime,
        accessFrequency: 0
        // Will be calculated
      };
      if (existingRecord) {
        const timeDiff2 = currentTime - existingRecord.timestamp;
        accessRecord.accessFrequency = timeDiff2 > 0 ? accessRecord.accessCount / (timeDiff2 / 6e4) : 0;
      }
      this.cacheAccessOrder.set(element, accessRecord);
      this.lruRefs.set(elementId, new WeakRef(element));
      this.lruTimestamps.set(elementId, currentTime);
      if (!existingRecord) {
        this.cacheCleanupRegistry.register(element, elementId);
      }
      this.updateElementTemperature(elementId, accessRecord);
      const accessTime = performance.now() - startTime;
      this.cacheEfficiencyMetrics.totalCacheOperations++;
      this.cacheEfficiencyMetrics.totalAccessTime += accessTime;
      this.cacheEfficiencyMetrics.averageAccessTime = this.cacheEfficiencyMetrics.totalAccessTime / this.cacheEfficiencyMetrics.totalCacheOperations;
      if (this.debugLogging && accessRecord.accessCount > 1) {
        console.log("[KeyPilot Debug] Enhanced cache access tracking:", {
          elementId: elementId.substring(0, 10) + "...",
          accessCount: accessRecord.accessCount,
          accessFrequency: accessRecord.accessFrequency.toFixed(2) + " accesses/min",
          timeSinceLastAccess: timeDiff ? (timeDiff / 1e3).toFixed(1) + "s" : "first access",
          accessTime: accessTime.toFixed(3) + "ms"
        });
      }
    }
    /**
     * Update element temperature classification for cache optimization
     * @param {string} elementId - Element identifier
     * @param {Object} accessRecord - Access record with frequency data
     */
    updateElementTemperature(elementId, accessRecord) {
      const { accessCount, accessFrequency } = accessRecord;
      const HOT_ACCESS_THRESHOLD = 5;
      const HOT_FREQUENCY_THRESHOLD = 2;
      const COLD_ACCESS_THRESHOLD = 1;
      const COLD_TIME_THRESHOLD = 3e5;
      this.cacheEfficiencyMetrics.coldElements.delete(elementId);
      if (accessCount >= HOT_ACCESS_THRESHOLD || accessFrequency >= HOT_FREQUENCY_THRESHOLD) {
        this.cacheEfficiencyMetrics.hotElements.set(elementId, {
          accessCount,
          accessFrequency,
          lastAccess: accessRecord.timestamp,
          priority: accessCount * accessFrequency
          // Combined priority score
        });
      }
      const currentTime = Date.now();
      for (const [hotElementId, hotRecord] of this.cacheEfficiencyMetrics.hotElements.entries()) {
        if (currentTime - hotRecord.lastAccess > COLD_TIME_THRESHOLD) {
          this.cacheEfficiencyMetrics.hotElements.delete(hotElementId);
          this.cacheEfficiencyMetrics.coldElements.add(hotElementId);
        }
      }
    }
    /**
     * Enhanced cache text nodes with comprehensive size monitoring and LRU management
     * @param {Element} element - Element to cache text nodes for
     * @param {Array} textNodes - Text nodes to cache
     */
    cacheTextNodes(element, textNodes) {
      this.monitorCacheSize();
      if (this.cacheSize >= this.cleanupThreshold) {
        this.evictLeastRecentlyUsed();
      }
      if (this.cacheSize >= this.maxCacheSize) {
        const aggressiveCleanupSize = Math.floor(this.maxCacheSize * 0.3);
        this.performTargetedEviction(aggressiveCleanupSize);
      }
      this.textNodeCache.set(element, textNodes);
      this.updateCacheAccess(element);
      this.cacheSize++;
      this.updateCacheEfficiencyMetrics(textNodes.length);
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Cached text nodes for element:", {
          element: element.tagName,
          className: element.className || "none",
          textNodesCount: textNodes.length,
          cacheSize: this.cacheSize,
          maxCacheSize: this.maxCacheSize,
          cacheUtilization: (this.cacheSize / this.maxCacheSize * 100).toFixed(1) + "%",
          hitRatio: this.getCacheHitRatio()
        });
      }
    }
    /**
     * Enhanced cache size monitoring with adaptive thresholds and memory pressure detection
     */
    monitorCacheSize() {
      const utilizationPercent = this.cacheSize / this.maxCacheSize * 100;
      const currentTime = Date.now();
      if (!this.metrics.maxCacheUtilization) {
        this.metrics.maxCacheUtilization = 0;
        this.metrics.avgCacheUtilization = 0;
        this.metrics.cacheMonitoringCount = 0;
        this.metrics.lastMonitoringTime = currentTime;
        this.metrics.utilizationHistory = [];
        this.metrics.memoryPressureScore = 0;
        this.metrics.cacheEvictions = 0;
      }
      this.metrics.maxCacheUtilization = Math.max(this.metrics.maxCacheUtilization, utilizationPercent);
      this.metrics.cacheMonitoringCount++;
      this.metrics.avgCacheUtilization = (this.metrics.avgCacheUtilization * (this.metrics.cacheMonitoringCount - 1) + utilizationPercent) / this.metrics.cacheMonitoringCount;
      this.metrics.utilizationHistory.push({
        timestamp: currentTime,
        utilization: utilizationPercent,
        cacheSize: this.cacheSize,
        accessOrderSize: this.lruRefs.size
      });
      if (this.metrics.utilizationHistory.length > 50) {
        this.metrics.utilizationHistory.shift();
      }
      this.calculateMemoryPressureScore();
      const adaptiveThresholds = this.calculateAdaptiveCleanupThresholds();
      if (utilizationPercent > 90) {
        this.cacheEfficiencyMetrics.memoryPressureEvents++;
        if (this.debugLogging) {
          console.warn("[KeyPilot Debug] High cache utilization detected:", {
            currentSize: this.cacheSize,
            maxSize: this.maxCacheSize,
            utilization: utilizationPercent.toFixed(1) + "%",
            accessOrderSize: this.lruRefs.size,
            memoryPressureScore: this.metrics.memoryPressureScore,
            adaptiveThresholds
          });
        }
      }
    }
    /**
     * Calculate memory pressure score based on cache utilization trends
     */
    /**
     * Calculate adaptive cleanup thresholds based on usage patterns
     */
    /**
     * Evict least recently used cache entries with intelligent prioritization
     */
    evictLeastRecentlyUsed() {
      const targetEvictions = this.cleanupBatchSize;
      const evictionCandidates = [];
      for (const [elementId, timestamp] of this.lruTimestamps.entries()) {
        const weakRef = this.lruRefs.get(elementId);
        const element = weakRef?.deref();
        if (!element) {
          this.lruRefs.delete(elementId);
          this.lruTimestamps.delete(elementId);
          continue;
        }
        const accessRecord = this.cacheAccessOrder.get(element);
        if (!accessRecord) {
          this.lruRefs.delete(elementId);
          this.lruTimestamps.delete(elementId);
          continue;
        }
        if (!this.textNodeCache.has(element)) {
          this.cacheAccessOrder.delete(element);
          this.lruRefs.delete(elementId);
          this.lruTimestamps.delete(elementId);
          this.cacheSize = Math.max(0, this.cacheSize - 1);
          continue;
        }
        const timeSinceAccess = Date.now() - accessRecord.timestamp;
        const accessFrequency = accessRecord.accessFrequency || 0;
        const isHot = this.cacheEfficiencyMetrics.hotElements.has(elementId);
        const priority = this.calculateEvictionPriority(accessRecord, timeSinceAccess, isHot);
        evictionCandidates.push({
          elementId,
          element,
          priority,
          accessRecord,
          timeSinceAccess
        });
      }
      evictionCandidates.sort((a, b) => a.priority - b.priority);
      const evicted = [];
      for (let i = 0; i < Math.min(targetEvictions, evictionCandidates.length); i++) {
        const candidate = evictionCandidates[i];
        this.textNodeCache.delete(candidate.element);
        this.cacheAccessOrder.delete(candidate.element);
        this.lruRefs.delete(candidate.elementId);
        this.lruTimestamps.delete(candidate.elementId);
        this.cacheSize = Math.max(0, this.cacheSize - 1);
        evicted.push({
          elementId: candidate.elementId,
          priority: candidate.priority,
          timeSinceAccess: candidate.timeSinceAccess,
          accessCount: candidate.accessRecord.accessCount
        });
      }
      this.metrics.cacheEvictions += evicted.length;
      this.cacheEfficiencyMetrics.evictionHistory.push(...evicted);
      this.cacheEfficiencyMetrics.adaptiveCleanupTriggers++;
      if (this.cacheEfficiencyMetrics.evictionHistory.length > 100) {
        this.cacheEfficiencyMetrics.evictionHistory.splice(0, 50);
      }
      if (this.debugLogging && evicted.length > 0) {
        console.log("[KeyPilot Debug] LRU cache eviction completed:", {
          evictedCount: evicted.length,
          targetEvictions,
          newCacheSize: this.cacheSize,
          cacheUtilization: (this.cacheSize / this.maxCacheSize * 100).toFixed(1) + "%",
          avgEvictionPriority: (evicted.reduce((sum, e) => sum + e.priority, 0) / evicted.length).toFixed(2),
          oldestEvicted: Math.max(...evicted.map((e) => e.timeSinceAccess)) / 1e3 + "s ago"
        });
      }
    }
    /**
     * Calculate eviction priority for a cache entry
     * @param {Object} accessRecord - Access record for the element
     * @param {number} timeSinceAccess - Time since last access in ms
     * @param {boolean} isHot - Whether element is classified as hot
     * @returns {number} Priority score (lower = more likely to evict)
     */
    calculateEvictionPriority(accessRecord, timeSinceAccess, isHot) {
      const baseScore = 100;
      const timeScore = Math.min(50, timeSinceAccess / 6e4);
      const frequencyScore = Math.max(0, 30 - accessRecord.accessCount * 5);
      const hotProtection = isHot ? -40 : 0;
      const accessFrequencyProtection = Math.min(20, (accessRecord.accessFrequency || 0) * -10);
      return baseScore + timeScore + frequencyScore + hotProtection + accessFrequencyProtection;
    }
    /**
     * Perform targeted eviction to reach a specific cache size
     * @param {number} targetEvictions - Number of entries to evict
     */
    /**
     * Implement cache warming strategies for frequently accessed elements
     */
    implementCacheWarming() {
      if (!EDGE_ONLY_SELECTION.ENABLE_PREDICTIVE_CACHING) {
        return;
      }
      const viewport = {
        left: window.scrollX,
        top: window.scrollY,
        right: window.scrollX + window.innerWidth,
        bottom: window.scrollY + window.innerHeight
      };
      const expandedViewport = {
        left: viewport.left - EDGE_ONLY_SELECTION.PREDICTIVE_CACHE_DISTANCE,
        top: viewport.top - EDGE_ONLY_SELECTION.PREDICTIVE_CACHE_DISTANCE,
        right: viewport.right + EDGE_ONLY_SELECTION.PREDICTIVE_CACHE_DISTANCE,
        bottom: viewport.bottom + EDGE_ONLY_SELECTION.PREDICTIVE_CACHE_DISTANCE
      };
      const uncachedElements = [];
      const allElements = document.querySelectorAll("p, div, span, h1, h2, h3, h4, h5, h6, li, td, th");
      for (const element of allElements) {
        if (this.textNodeCache.has(element)) continue;
        if (!this.elementContainsText(element)) continue;
        const rect = element.getBoundingClientRect();
        const elementBounds = {
          left: rect.left + window.scrollX,
          top: rect.top + window.scrollY,
          right: rect.right + window.scrollX,
          bottom: rect.bottom + window.scrollY
        };
        if (this.isElementInBounds(elementBounds, expandedViewport)) {
          uncachedElements.push(element);
        }
      }
      const maxWarmingElements = Math.min(10, uncachedElements.length);
      const startTime = performance.now();
      for (let i = 0; i < maxWarmingElements; i++) {
        const element = uncachedElements[i];
        const textNodes = this.extractTextNodesForCaching(element);
        if (textNodes.length > 0) {
          this.cacheTextNodes(element, textNodes);
        }
        if (performance.now() - startTime > 5) break;
      }
      if (this.debugLogging && maxWarmingElements > 0) {
        console.log("[KeyPilot Debug] Cache warming completed:", {
          elementsWarmed: maxWarmingElements,
          totalCandidates: uncachedElements.length,
          warmingTime: (performance.now() - startTime).toFixed(2) + "ms",
          newCacheSize: this.cacheSize,
          cacheUtilization: (this.cacheSize / this.maxCacheSize * 100).toFixed(1) + "%"
        });
      }
    }
    /**
     * Check if element bounds intersect with target bounds
     * @param {Object} elementBounds - Element bounds
     * @param {Object} targetBounds - Target bounds to check against
     * @returns {boolean} True if bounds intersect
     */
    isElementInBounds(elementBounds, targetBounds) {
      return !(elementBounds.right < targetBounds.left || elementBounds.left > targetBounds.right || elementBounds.bottom < targetBounds.top || elementBounds.top > targetBounds.bottom);
    }
    /**
     * Extract text nodes for caching purposes (without adding to intersection set)
     * @param {Element} element - Element to extract text nodes from
     * @returns {Array} Array of text nodes
     */
    extractTextNodesForCaching(element) {
      const textNodes = [];
      const walker = document.createTreeWalker(
        element,
        NodeFilter.SHOW_TEXT,
        {
          acceptNode: (node2) => {
            if (!node2.textContent || !node2.textContent.trim()) {
              return NodeFilter.FILTER_REJECT;
            }
            const parent = node2.parentElement;
            if (parent && this.isNonContentElement(parent)) {
              return NodeFilter.FILTER_REJECT;
            }
            return NodeFilter.FILTER_ACCEPT;
          }
        }
      );
      let node;
      while (node = walker.nextNode()) {
        textNodes.push(node);
      }
      return textNodes;
    }
    /**
     * Update cache efficiency metrics
     * @param {number} textNodesCount - Number of text nodes cached
     */
    /**
     * Get comprehensive cache efficiency metrics
     * @returns {Object} Cache efficiency metrics
     */
    /**
     * Monitor memory usage and trigger cleanup if necessary
     */
    monitorMemoryUsage() {
      if (!EDGE_ONLY_SELECTION.ENABLE_MEMORY_MONITORING) {
        return;
      }
      const estimatedMemoryUsage = this.estimateMemoryUsage();
      const memoryLimitMB = EDGE_ONLY_SELECTION.MAX_MEMORY_USAGE_MB;
      const memoryUsagePercent = estimatedMemoryUsage / memoryLimitMB * 100;
      if (!this.metrics.memoryUsage) {
        this.metrics.memoryUsage = {
          current: 0,
          peak: 0,
          average: 0,
          measurements: 0,
          lastCleanup: Date.now(),
          cleanupTriggers: 0
        };
      }
      this.metrics.memoryUsage.current = estimatedMemoryUsage;
      this.metrics.memoryUsage.peak = Math.max(this.metrics.memoryUsage.peak, estimatedMemoryUsage);
      this.metrics.memoryUsage.measurements++;
      this.metrics.memoryUsage.average = (this.metrics.memoryUsage.average * (this.metrics.memoryUsage.measurements - 1) + estimatedMemoryUsage) / this.metrics.memoryUsage.measurements;
      if (memoryUsagePercent > EDGE_ONLY_SELECTION.GARBAGE_COLLECTION_THRESHOLD * 100) {
        this.triggerMemoryCleanup();
      }
      if (this.debugLogging && this.metrics.memoryUsage.measurements % 20 === 0) {
        console.log("[KeyPilot Debug] Memory usage monitoring:", {
          currentUsage: estimatedMemoryUsage.toFixed(2) + "MB",
          usagePercent: memoryUsagePercent.toFixed(1) + "%",
          peakUsage: this.metrics.memoryUsage.peak.toFixed(2) + "MB",
          averageUsage: this.metrics.memoryUsage.average.toFixed(2) + "MB",
          memoryLimit: memoryLimitMB + "MB",
          cacheSize: this.cacheSize,
          cleanupTriggers: this.metrics.memoryUsage.cleanupTriggers
        });
      }
    }
    /**
     * Estimate memory usage of the cache system
     * @returns {number} Estimated memory usage in MB
     */
    estimateMemoryUsage() {
      const avgTextNodesPerElement = 5;
      const avgTextNodeSize = 100;
      const cacheOverhead = 200;
      const textNodeMemory = this.cacheSize * avgTextNodesPerElement * avgTextNodeSize;
      const cacheMetadataMemory = this.cacheSize * cacheOverhead;
      const accessOrderMemory = this.lruRefs.size * 200;
      const metricsMemory = 5e4;
      const totalBytes = textNodeMemory + cacheMetadataMemory + accessOrderMemory + metricsMemory;
      return totalBytes / (1024 * 1024);
    }
    /**
     * Trigger memory cleanup when usage is too high
     */
    triggerMemoryCleanup() {
      const startTime = performance.now();
      const initialCacheSize = this.cacheSize;
      const targetEvictions = Math.floor(this.cacheSize * 0.4);
      this.performTargetedEviction(targetEvictions);
      this.cleanupStaleAccessRecords();
      this.metrics.memoryUsage.cleanupTriggers++;
      this.metrics.memoryUsage.lastCleanup = Date.now();
      const cleanupTime = performance.now() - startTime;
      const evictedCount = initialCacheSize - this.cacheSize;
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Memory cleanup triggered:", {
          reason: "High memory usage",
          initialCacheSize,
          finalCacheSize: this.cacheSize,
          evictedEntries: evictedCount,
          cleanupTime: cleanupTime.toFixed(2) + "ms",
          estimatedMemoryFreed: (evictedCount * 0.5).toFixed(2) + "KB",
          newMemoryUsage: this.estimateMemoryUsage().toFixed(2) + "MB"
        });
      }
    }
    /**
     * Clean up stale access records that no longer have corresponding cache entries
     */
    cleanupStaleAccessRecords() {
      const staleRecords = [];
      for (const [elementId, weakRef] of this.lruRefs.entries()) {
        const element = weakRef.deref();
        if (!element) {
          staleRecords.push(elementId);
          continue;
        }
        if (!this.textNodeCache.has(element)) {
          staleRecords.push(elementId);
        }
      }
      staleRecords.forEach((elementId) => {
        this.cacheAccessOrder.delete(this.lruRefs.get(elementId)?.deref());
        this.lruRefs.delete(elementId);
        this.lruTimestamps.delete(elementId);
        this.cacheEfficiencyMetrics.coldElements.delete(elementId);
        this.cacheEfficiencyMetrics.hotElements.delete(elementId);
        this.cacheSize = Math.max(0, this.cacheSize - 1);
      });
      if (this.debugLogging && staleRecords.length > 0) {
        console.log("[KeyPilot Debug] Cleaned up stale access records:", {
          staleRecords: staleRecords.length,
          remainingAccessRecords: this.lruRefs.size,
          cacheSize: this.cacheSize
        });
      }
    }
    /**
     * Implement cache efficiency optimization based on usage patterns
     */
    optimizeCacheEfficiency() {
      const efficiency = this.getCacheEfficiencyMetrics();
      const hitRatioNumeric = parseFloat(efficiency.hitRatio) / 100;
      if (hitRatioNumeric < EDGE_ONLY_SELECTION.CACHE_HIT_RATIO_THRESHOLD) {
        this.implementCacheOptimizations(hitRatioNumeric);
      }
      if (EDGE_ONLY_SELECTION.ENABLE_PREDICTIVE_CACHING) {
        const utilizationPercent = this.cacheSize / this.maxCacheSize * 100;
        if (utilizationPercent < 70) {
          this.implementCacheWarming();
        }
      }
      this.adjustCacheParameters();
    }
    /**
     * Implement cache optimizations when hit ratio is low
     * @param {number} currentHitRatio - Current cache hit ratio (0-1)
     */
    implementCacheOptimizations(currentHitRatio) {
      const memoryUsage = this.estimateMemoryUsage();
      const memoryLimit = EDGE_ONLY_SELECTION.MAX_MEMORY_USAGE_MB;
      if (memoryUsage < memoryLimit * 0.7 && this.maxCacheSize < 2e3) {
        const newMaxSize = Math.min(2e3, Math.floor(this.maxCacheSize * 1.2));
        this.maxCacheSize = newMaxSize;
        if (this.debugLogging) {
          console.log("[KeyPilot Debug] Increased cache size for better hit ratio:", {
            oldMaxSize: Math.floor(newMaxSize / 1.2),
            newMaxSize,
            currentHitRatio: (currentHitRatio * 100).toFixed(1) + "%",
            memoryUsage: memoryUsage.toFixed(2) + "MB"
          });
        }
      }
      if (currentHitRatio < 0.5) {
        this.cleanupThreshold = Math.min(
          this.maxCacheSize * 0.9,
          this.cleanupThreshold * 1.1
        );
      }
      this.adjustHotElementProtection(currentHitRatio);
    }
    /**
     * Adjust hot element protection based on cache performance
     * @param {number} currentHitRatio - Current cache hit ratio (0-1)
     */
    adjustHotElementProtection(currentHitRatio) {
      const protectionMultiplier = Math.max(0.5, currentHitRatio);
      for (const [elementId, hotRecord] of this.cacheEfficiencyMetrics.hotElements.entries()) {
        hotRecord.priority *= 1 + (1 - protectionMultiplier);
      }
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Adjusted hot element protection:", {
          currentHitRatio: (currentHitRatio * 100).toFixed(1) + "%",
          protectionMultiplier: protectionMultiplier.toFixed(2),
          hotElements: this.cacheEfficiencyMetrics.hotElements.size
        });
      }
    }
    /**
     * Adjust cache parameters based on performance metrics
     */
    adjustCacheParameters() {
      const metrics = this.getMetrics();
      const efficiency = this.getCacheEfficiencyMetrics();
      if (this.metrics.cacheEvictions > 0) {
        const evictionsPerUpdate = this.metrics.cacheEvictions / Math.max(1, this.metrics.intersectionUpdates);
        if (evictionsPerUpdate > 0.1) {
          this.cleanupBatchSize = Math.min(500, Math.floor(this.cleanupBatchSize * 1.2));
        } else if (evictionsPerUpdate < 0.01) {
          this.cleanupBatchSize = Math.max(50, Math.floor(this.cleanupBatchSize * 0.9));
        }
      }
      if (this.debugLogging && this.metrics.intersectionUpdates % 100 === 0) {
        console.log("[KeyPilot Debug] Cache parameter adjustment:", {
          cleanupBatchSize: this.cleanupBatchSize,
          cleanupThreshold: this.cleanupThreshold,
          maxCacheSize: this.maxCacheSize,
          cacheEvictions: this.metrics.cacheEvictions,
          intersectionUpdates: this.metrics.intersectionUpdates,
          hitRatio: efficiency.hitRatio
        });
      }
    }
    /**
     * Calculate memory pressure score based on utilization trends
     */
    calculateMemoryPressureScore() {
      if (this.metrics.utilizationHistory.length < 5) {
        this.metrics.memoryPressureScore = 0;
        return;
      }
      const recent = this.metrics.utilizationHistory.slice(-5);
      const trend = recent[recent.length - 1].utilization - recent[0].utilization;
      const avgUtilization = recent.reduce((sum, entry) => sum + entry.utilization, 0) / recent.length;
      const volatility = this.calculateUtilizationVolatility(recent);
      this.metrics.memoryPressureScore = avgUtilization / 100 * 0.5 + Math.max(0, trend / 100) * 0.3 + volatility * 0.2;
    }
    /**
     * Calculate utilization volatility for memory pressure assessment
     * @param {Array} utilizationData - Recent utilization measurements
     * @returns {number} - Volatility score (0-1)
     */
    calculateUtilizationVolatility(utilizationData) {
      if (utilizationData.length < 2) return 0;
      const changes = [];
      for (let i = 1; i < utilizationData.length; i++) {
        changes.push(Math.abs(utilizationData[i].utilization - utilizationData[i - 1].utilization));
      }
      const avgChange = changes.reduce((sum, change) => sum + change, 0) / changes.length;
      return Math.min(1, avgChange / 20);
    }
    /**
     * Calculate adaptive cleanup thresholds based on system state
     * @returns {Object} - Threshold configuration
     */
    calculateAdaptiveCleanupThresholds() {
      const baseThresholds = { maintenance: 70, preventive: 85, emergency: 95 };
      const pressureAdjustment = this.metrics.memoryPressureScore * 10;
      return {
        maintenance: Math.max(60, baseThresholds.maintenance - pressureAdjustment),
        preventive: Math.max(75, baseThresholds.preventive - pressureAdjustment),
        emergency: Math.max(85, baseThresholds.emergency - pressureAdjustment)
      };
    }
    /**
     * Determine if maintenance cleanup should be performed
     * @returns {boolean} - True if maintenance cleanup is needed
     */
    shouldPerformMaintenanceCleanup() {
      const timeSinceLastCleanup = Date.now() - (this.metrics.lastCleanupTime || 0);
      const hasStaleElements = this.cacheEfficiencyMetrics.coldElements.size > 10;
      const lowCacheEfficiency = this.getCacheHitRatio() < "80%";
      return timeSinceLastCleanup > 6e4 && (hasStaleElements || lowCacheEfficiency);
    }
    /**
     * Perform targeted eviction of a specific number of cache entries
     * @param {number} targetEvictions - Number of entries to evict
     */
    performTargetedEviction(targetEvictions) {
      if (targetEvictions <= 0 || this.cacheSize === 0) {
        return;
      }
      const startTime = performance.now();
      const originalCleanupBatchSize = this.cleanupBatchSize;
      this.cleanupBatchSize = Math.min(targetEvictions, this.cacheSize);
      this.evictLeastRecentlyUsed();
      this.cleanupBatchSize = originalCleanupBatchSize;
      const evictionTime = performance.now() - startTime;
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Targeted cache eviction completed:", {
          targetEvictions,
          evictionTime: evictionTime.toFixed(2) + "ms",
          newCacheSize: this.cacheSize,
          newUtilization: (this.cacheSize / this.maxCacheSize * 100).toFixed(1) + "%"
        });
      }
    }
    /**
     * Update cache efficiency metrics when adding new entries
     * @param {number} textNodesCount - Number of text nodes being cached
     */
    updateCacheEfficiencyMetrics(textNodesCount) {
      if (!this.metrics.totalTextNodesCached) {
        this.metrics.totalTextNodesCached = 0;
        this.metrics.avgTextNodesPerElement = 0;
        this.metrics.cacheEntryCount = 0;
      }
      this.metrics.totalTextNodesCached += textNodesCount;
      this.metrics.cacheEntryCount++;
      this.metrics.avgTextNodesPerElement = this.metrics.totalTextNodesCached / this.metrics.cacheEntryCount;
    }
    /**
     * Intelligent cache eviction strategy that considers element temperature and access patterns
     * @param {string} mode - Eviction mode: 'maintenance', 'preventive', 'emergency'
     * @param {number} targetEvictions - Number of entries to evict
     */
    performIntelligentEviction(mode = "preventive", targetEvictions = null) {
      if (!targetEvictions) {
        targetEvictions = Math.min(this.cleanupBatchSize, this.cacheSize);
      }
      const startTime = performance.now();
      let evictedCount = 0;
      const evictionStrategy = this.selectEvictionStrategy(mode);
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Starting intelligent cache eviction:", {
          mode,
          targetEvictions,
          strategy: evictionStrategy,
          cacheSize: this.cacheSize,
          hotElements: this.cacheEfficiencyMetrics.hotElements.size,
          coldElements: this.cacheEfficiencyMetrics.coldElements.size
        });
      }
      switch (evictionStrategy) {
        case "cold-first":
          evictedCount = this.evictColdElementsFirst(targetEvictions);
          break;
        case "lru-standard":
          evictedCount = this.evictLeastRecentlyUsed(targetEvictions);
          break;
        case "frequency-based":
          evictedCount = this.evictLowFrequencyElements(targetEvictions);
          break;
        case "hybrid":
          evictedCount = this.evictHybridStrategy(targetEvictions);
          break;
        default:
          evictedCount = this.evictLeastRecentlyUsed(targetEvictions);
      }
      const evictionTime = performance.now() - startTime;
      this.metrics.lastCleanupTime = Date.now();
      this.cacheEfficiencyMetrics.evictionHistory.push({
        timestamp: Date.now(),
        mode,
        strategy: evictionStrategy,
        targetEvictions,
        actualEvictions: evictedCount,
        evictionTime,
        efficiency: targetEvictions > 0 ? evictedCount / targetEvictions * 100 : 0
      });
      if (this.cacheEfficiencyMetrics.evictionHistory.length > 20) {
        this.cacheEfficiencyMetrics.evictionHistory.shift();
      }
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Intelligent cache eviction completed:", {
          mode,
          strategy: evictionStrategy,
          targetEvictions,
          actualEvictions: evictedCount,
          evictionTime: evictionTime.toFixed(2) + "ms",
          efficiency: (evictedCount / targetEvictions * 100).toFixed(1) + "%",
          newCacheSize: this.cacheSize,
          remainingHotElements: this.cacheEfficiencyMetrics.hotElements.size
        });
      }
      return evictedCount;
    }
    /**
     * Select optimal eviction strategy based on cache state and mode
     * @param {string} mode - Eviction mode
     * @returns {string} - Selected eviction strategy
     */
    selectEvictionStrategy(mode) {
      const coldElementsRatio = this.cacheEfficiencyMetrics.coldElements.size / this.cacheSize;
      const hotElementsRatio = this.cacheEfficiencyMetrics.hotElements.size / this.cacheSize;
      switch (mode) {
        case "maintenance":
          return coldElementsRatio > 0.3 ? "cold-first" : "lru-standard";
        case "preventive":
          return coldElementsRatio > 0.2 ? "hybrid" : "frequency-based";
        case "emergency":
          return hotElementsRatio < 0.5 ? "cold-first" : "lru-standard";
        default:
          return "lru-standard";
      }
    }
    /**
     * Evict cold elements first (elements with low access frequency)
     * @param {number} targetEvictions - Number of entries to evict
     * @returns {number} - Number of elements actually evicted
     */
    evictColdElementsFirst(targetEvictions) {
      let evictedCount = 0;
      const elementsToEvict = [];
      for (const elementId of this.cacheEfficiencyMetrics.coldElements) {
        if (evictedCount >= targetEvictions) break;
        const weakRef = this.lruRefs.get(elementId);
        const element = weakRef?.deref();
        const accessRecord = element ? this.cacheAccessOrder.get(element) : null;
        if (element && accessRecord) {
          elementsToEvict.push(element);
          this.cacheAccessOrder.delete(element);
          this.lruRefs.delete(elementId);
          this.lruTimestamps.delete(elementId);
          this.cacheEfficiencyMetrics.coldElements.delete(elementId);
          evictedCount++;
        }
      }
      if (evictedCount < targetEvictions) {
        const remainingTarget = targetEvictions - evictedCount;
        evictedCount += this.evictLeastRecentlyUsed(remainingTarget);
      }
      elementsToEvict.forEach((element) => {
        if (this.textNodeCache.has(element)) {
          this.textNodeCache.delete(element);
        }
      });
      this.cacheSize = Math.max(0, this.cacheSize - evictedCount);
      return evictedCount;
    }
    /**
     * Evict elements with low access frequency
     * @param {number} targetEvictions - Number of entries to evict
     * @returns {number} - Number of elements actually evicted
     */
    evictLowFrequencyElements(targetEvictions) {
      const sortedByFrequency = Array.from(this.lruRefs.entries()).map(([elementId, weakRef]) => {
        const element = weakRef.deref();
        const accessRecord = element ? this.cacheAccessOrder.get(element) : null;
        return { elementId, element, accessRecord };
      }).filter((item) => item.element && item.accessRecord).sort((a, b) => (a.accessRecord.accessFrequency || 0) - (b.accessRecord.accessFrequency || 0));
      let evictedCount = 0;
      const elementsToEvict = [];
      for (let i = 0; i < Math.min(targetEvictions, sortedByFrequency.length); i++) {
        const { elementId, element, accessRecord } = sortedByFrequency[i];
        if (element) {
          elementsToEvict.push(element);
          this.cacheAccessOrder.delete(element);
          this.lruRefs.delete(elementId);
          this.lruTimestamps.delete(elementId);
          this.cacheEfficiencyMetrics.hotElements.delete(elementId);
          this.cacheEfficiencyMetrics.coldElements.delete(elementId);
          evictedCount++;
        }
      }
      elementsToEvict.forEach((element) => {
        if (this.textNodeCache.has(element)) {
          this.textNodeCache.delete(element);
        }
      });
      this.cacheSize = Math.max(0, this.cacheSize - evictedCount);
      return evictedCount;
    }
    /**
     * Hybrid eviction strategy combining multiple factors
     * @param {number} targetEvictions - Number of entries to evict
     * @returns {number} - Number of elements actually evicted
     */
    evictHybridStrategy(targetEvictions) {
      const elementScores = [];
      const currentTime = Date.now();
      for (const [elementId, weakRef] of this.lruRefs.entries()) {
        const element = weakRef.deref();
        const accessRecord = element ? this.cacheAccessOrder.get(element) : null;
        if (!element || !accessRecord) continue;
        const timeSinceAccess = currentTime - accessRecord.timestamp;
        const accessFrequency = accessRecord.accessFrequency || 0;
        const accessCount = accessRecord.accessCount || 1;
        const timeScore = Math.min(1, timeSinceAccess / 3e5);
        const frequencyScore = Math.max(0, 1 - accessFrequency / 5);
        const countScore = Math.max(0, 1 - accessCount / 10);
        const compositeScore = timeScore * 0.4 + frequencyScore * 0.3 + countScore * 0.3;
        elementScores.push({
          elementId,
          element,
          accessRecord,
          compositeScore
        });
      }
      elementScores.sort((a, b) => b.compositeScore - a.compositeScore);
      let evictedCount = 0;
      const elementsToEvict = [];
      for (let i = 0; i < Math.min(targetEvictions, elementScores.length); i++) {
        const { elementId, element, accessRecord } = elementScores[i];
        if (element) {
          elementsToEvict.push(element);
          this.cacheAccessOrder.delete(element);
          this.lruRefs.delete(elementId);
          this.lruTimestamps.delete(elementId);
          this.cacheEfficiencyMetrics.hotElements.delete(elementId);
          this.cacheEfficiencyMetrics.coldElements.add(elementId);
          evictedCount++;
        }
      }
      elementsToEvict.forEach((element) => {
        if (this.textNodeCache.has(element)) {
          this.textNodeCache.delete(element);
        }
      });
      this.cacheSize = Math.max(0, this.cacheSize - evictedCount);
      return evictedCount;
    }
    /**
     * Perform aggressive cache cleanup when standard LRU eviction is insufficient
     */
    performAggressiveCleanup() {
      const startTime = performance.now();
      const initialSize = this.lruTimestamps.size;
      const currentTime = Date.now();
      const staleThreshold = 3e5;
      let staleEntriesRemoved = 0;
      for (const [elementId, timestamp] of this.lruTimestamps.entries()) {
        if (currentTime - timestamp > staleThreshold) {
          const weakRef = this.lruRefs.get(elementId);
          const element = weakRef?.deref();
          if (element) {
            this.cacheAccessOrder.delete(element);
          }
          this.lruRefs.delete(elementId);
          this.lruTimestamps.delete(elementId);
          this.cacheEfficiencyMetrics.hotElements.delete(elementId);
          this.cacheEfficiencyMetrics.coldElements.delete(elementId);
          staleEntriesRemoved++;
        }
      }
      this.cacheSize = Math.max(0, this.cacheSize - staleEntriesRemoved);
      const cleanupTime = performance.now() - startTime;
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Aggressive cache cleanup completed:", {
          initialAccessOrderSize: initialSize,
          staleEntriesRemoved,
          finalAccessOrderSize: this.lruTimestamps.size,
          estimatedCacheSize: this.cacheSize,
          cleanupTime: cleanupTime.toFixed(2) + "ms"
        });
      }
    }
    /**
     * Get comprehensive cache efficiency metrics including enhanced LRU performance data
     * @returns {Object} - Complete cache efficiency analysis
     */
    getCacheEfficiencyMetrics() {
      const totalAttempts = this.metrics.cacheHits + this.metrics.cacheMisses;
      const hitRatio = totalAttempts > 0 ? this.metrics.cacheHits / totalAttempts : 0;
      const evictions = this.metrics.cacheEvictions || 0;
      const avgEvictionTime = evictions > 0 ? (this.metrics.totalEvictionTime || 0) / evictions : 0;
      const cacheUtilization = this.maxCacheSize > 0 ? this.cacheSize / this.maxCacheSize : 0;
      const accessOrderAccuracy = this.cacheSize > 0 ? this.lruTimestamps.size / this.cacheSize : 1;
      const hotElementRatio = this.cacheSize > 0 ? this.cacheEfficiencyMetrics.hotElements.size / this.cacheSize : 0;
      const coldElementRatio = this.cacheSize > 0 ? this.cacheEfficiencyMetrics.coldElements.size / this.cacheSize : 0;
      const recentEvictions = this.cacheEfficiencyMetrics.evictionHistory.slice(-5);
      const avgEvictionEfficiency = recentEvictions.length > 0 ? recentEvictions.reduce((sum, ev) => sum + ev.efficiency, 0) / recentEvictions.length : 0;
      const performanceTrend = this.calculateCachePerformanceTrend();
      return {
        // Basic metrics
        hitRatio: (hitRatio * 100).toFixed(1) + "%",
        hits: this.metrics.cacheHits,
        misses: this.metrics.cacheMisses,
        totalAttempts,
        // Size and utilization metrics
        cacheSize: this.cacheSize,
        maxCacheSize: this.maxCacheSize,
        utilization: (cacheUtilization * 100).toFixed(1) + "%",
        accessOrderSize: this.lruRefs.size,
        accessOrderAccuracy: (accessOrderAccuracy * 100).toFixed(1) + "%",
        // Eviction metrics
        evictions,
        avgEvictionTime: avgEvictionTime.toFixed(2) + "ms",
        avgEvictionEfficiency: avgEvictionEfficiency.toFixed(1) + "%",
        adaptiveCleanupTriggers: this.cacheEfficiencyMetrics.adaptiveCleanupTriggers,
        // Temperature-based metrics
        hotElements: this.cacheEfficiencyMetrics.hotElements.size,
        coldElements: this.cacheEfficiencyMetrics.coldElements.size,
        hotElementRatio: (hotElementRatio * 100).toFixed(1) + "%",
        coldElementRatio: (coldElementRatio * 100).toFixed(1) + "%",
        // Performance metrics
        averageAccessTime: this.cacheEfficiencyMetrics.averageAccessTime.toFixed(3) + "ms",
        totalCacheOperations: this.cacheEfficiencyMetrics.totalCacheOperations,
        memoryPressureEvents: this.cacheEfficiencyMetrics.memoryPressureEvents,
        memoryPressureScore: (this.metrics.memoryPressureScore || 0).toFixed(2),
        // Streak metrics
        longestHitStreak: this.cacheEfficiencyMetrics.longestHitStreak,
        longestMissStreak: this.cacheEfficiencyMetrics.longestMissStreak,
        currentHitStreak: this.cacheEfficiencyMetrics.cacheHitStreak,
        currentMissStreak: this.cacheEfficiencyMetrics.cacheMissStreak,
        // Advanced efficiency calculations
        memoryEfficiency: this.cacheSize > 0 ? (this.metrics.cacheHits / this.cacheSize).toFixed(2) : "0.00",
        accessEfficiency: totalAttempts > 0 ? (this.metrics.cacheHits / totalAttempts).toFixed(3) : "0.000",
        performanceTrend,
        // Recent eviction history summary
        recentEvictions: recentEvictions.map((ev) => ({
          mode: ev.mode,
          strategy: ev.strategy,
          efficiency: ev.efficiency.toFixed(1) + "%",
          evictionTime: ev.evictionTime.toFixed(2) + "ms"
        }))
      };
    }
    /**
     * Calculate cache performance trend based on recent metrics
     * @returns {Object} - Performance trend analysis
     */
    calculateCachePerformanceTrend() {
      if (!this.metrics.utilizationHistory || this.metrics.utilizationHistory.length < 3) {
        return { trend: "insufficient_data", direction: "stable", confidence: 0 };
      }
      const recent = this.metrics.utilizationHistory.slice(-5);
      const utilizationTrend = recent[recent.length - 1].utilization - recent[0].utilization;
      const avgUtilization = recent.reduce((sum, entry) => sum + entry.utilization, 0) / recent.length;
      let direction = "stable";
      if (utilizationTrend > 5) direction = "increasing";
      else if (utilizationTrend < -5) direction = "decreasing";
      const changes = [];
      for (let i = 1; i < recent.length; i++) {
        changes.push(recent[i].utilization - recent[i - 1].utilization);
      }
      const avgChange = changes.reduce((sum, change) => sum + change, 0) / changes.length;
      const consistency = changes.filter(
        (change) => avgChange > 0 && change > 0 || avgChange < 0 && change < 0 || Math.abs(change) < 2
      ).length / changes.length;
      return {
        trend: direction,
        direction,
        confidence: (consistency * 100).toFixed(0) + "%",
        avgUtilization: avgUtilization.toFixed(1) + "%",
        utilizationChange: utilizationTrend.toFixed(1) + "%",
        volatility: this.calculateUtilizationVolatility(recent).toFixed(2)
      };
    }
    /**
     * Generate comprehensive cache efficiency report for analysis and debugging
     * @returns {Object} - Detailed cache performance report
     */
    generateCacheEfficiencyReport() {
      const report = {
        timestamp: (/* @__PURE__ */ new Date()).toISOString(),
        summary: this.getCacheEfficiencyMetrics(),
        // Detailed cache state analysis
        cacheState: {
          totalElements: this.cacheSize,
          trackedElements: this.lruTimestamps.size,
          hotElements: Array.from(this.cacheEfficiencyMetrics.hotElements.entries()).map(([id, data]) => ({
            elementId: id.substring(0, 10) + "...",
            accessCount: data.accessCount,
            accessFrequency: data.accessFrequency.toFixed(2) + " accesses/min",
            priority: data.priority.toFixed(2),
            lastAccess: new Date(data.lastAccess).toLocaleTimeString()
          })).slice(0, 10),
          // Top 10 hot elements
          coldElementsCount: this.cacheEfficiencyMetrics.coldElements.size,
          memoryPressureScore: (this.metrics.memoryPressureScore || 0).toFixed(2)
        },
        // Performance analysis
        performance: {
          averageAccessTime: this.cacheEfficiencyMetrics.averageAccessTime.toFixed(3) + "ms",
          totalOperations: this.cacheEfficiencyMetrics.totalCacheOperations,
          operationsPerSecond: this.calculateOperationsPerSecond(),
          efficiencyTrend: this.calculateCachePerformanceTrend(),
          memoryPressureEvents: this.cacheEfficiencyMetrics.memoryPressureEvents
        },
        // Eviction analysis
        evictionAnalysis: {
          totalEvictions: this.metrics.cacheEvictions || 0,
          adaptiveCleanups: this.cacheEfficiencyMetrics.adaptiveCleanupTriggers,
          recentEvictions: this.cacheEfficiencyMetrics.evictionHistory.slice(-10),
          evictionStrategies: this.analyzeEvictionStrategies()
        },
        // Recommendations
        recommendations: this.generateCacheOptimizationRecommendations()
      };
      return report;
    }
    /**
     * Calculate cache operations per second for performance analysis
     * @returns {number} - Operations per second
     */
    calculateOperationsPerSecond() {
      if (!this.metrics.lastUpdateTime || this.cacheEfficiencyMetrics.totalCacheOperations === 0) {
        return 0;
      }
      const timeElapsed = (Date.now() - this.metrics.lastUpdateTime) / 1e3;
      return timeElapsed > 0 ? (this.cacheEfficiencyMetrics.totalCacheOperations / timeElapsed).toFixed(2) : 0;
    }
    /**
     * Analyze eviction strategies used and their effectiveness
     * @returns {Object} - Eviction strategy analysis
     */
    analyzeEvictionStrategies() {
      const strategies = {};
      this.cacheEfficiencyMetrics.evictionHistory.forEach((eviction) => {
        if (!strategies[eviction.strategy]) {
          strategies[eviction.strategy] = {
            count: 0,
            totalEfficiency: 0,
            totalTime: 0,
            avgEfficiency: 0,
            avgTime: 0
          };
        }
        const strategy = strategies[eviction.strategy];
        strategy.count++;
        strategy.totalEfficiency += eviction.efficiency;
        strategy.totalTime += eviction.evictionTime;
        strategy.avgEfficiency = strategy.totalEfficiency / strategy.count;
        strategy.avgTime = strategy.totalTime / strategy.count;
      });
      Object.keys(strategies).forEach((key) => {
        const strategy = strategies[key];
        strategy.avgEfficiency = strategy.avgEfficiency.toFixed(1) + "%";
        strategy.avgTime = strategy.avgTime.toFixed(2) + "ms";
      });
      return strategies;
    }
    /**
     * Generate cache optimization recommendations based on current performance
     * @returns {Array} - List of optimization recommendations
     */
    generateCacheOptimizationRecommendations() {
      const recommendations = [];
      const metrics = this.getCacheEfficiencyMetrics();
      const hitRatio = parseFloat(metrics.hitRatio);
      const utilization = parseFloat(metrics.utilization);
      if (hitRatio < 70) {
        recommendations.push({
          type: "performance",
          priority: "high",
          issue: "Low cache hit ratio",
          recommendation: "Consider increasing cache size or improving element access patterns",
          currentValue: metrics.hitRatio,
          targetValue: "80%+"
        });
      }
      if (utilization > 90) {
        recommendations.push({
          type: "memory",
          priority: "medium",
          issue: "High cache utilization",
          recommendation: "Consider increasing max cache size or more aggressive cleanup",
          currentValue: metrics.utilization,
          targetValue: "70-85%"
        });
      }
      const coldRatio = parseFloat(metrics.coldElementRatio);
      if (coldRatio > 30) {
        recommendations.push({
          type: "efficiency",
          priority: "medium",
          issue: "High ratio of cold elements",
          recommendation: "Enable more frequent maintenance cleanup to remove unused elements",
          currentValue: metrics.coldElementRatio,
          targetValue: "<20%"
        });
      }
      const memoryPressure = parseFloat(metrics.memoryPressureScore);
      if (memoryPressure > 0.7) {
        recommendations.push({
          type: "memory",
          priority: "high",
          issue: "High memory pressure detected",
          recommendation: "Enable adaptive processing and consider reducing cache size limits",
          currentValue: metrics.memoryPressureScore,
          targetValue: "<0.5"
        });
      }
      const evictionEfficiency = parseFloat(metrics.avgEvictionEfficiency);
      if (evictionEfficiency < 80) {
        recommendations.push({
          type: "performance",
          priority: "low",
          issue: "Low eviction efficiency",
          recommendation: "Review element tracking accuracy and cleanup strategies",
          currentValue: metrics.avgEvictionEfficiency,
          targetValue: "90%+"
        });
      }
      return recommendations;
    }
    /**
     * Force cache cleanup - useful for memory management
     */
    forceCacheCleanup() {
      const oldSize = this.cacheSize;
      this.textNodeCache = /* @__PURE__ */ new WeakMap();
      this.cacheAccessOrder = /* @__PURE__ */ new WeakMap();
      this.lruRefs.clear();
      this.lruTimestamps.clear();
      this.cacheEfficiencyMetrics.hotElements.clear();
      this.cacheEfficiencyMetrics.coldElements.clear();
      this.cacheSize = 0;
      this.cacheEfficiencyMetrics.cacheHitStreak = 0;
      this.cacheEfficiencyMetrics.cacheMissStreak = 0;
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Enhanced forced cache cleanup:", {
          previousSize: oldSize,
          newSize: this.cacheSize,
          accessOrderCleared: true,
          temperatureTrackingCleared: true,
          streaksReset: true
        });
      }
    }
    /**
     * Cleanup the intersection observer and resources
     */
    cleanup() {
      if (this.textObserver) {
        this.textObserver.disconnect();
        this.textObserver = null;
      }
      if (this.rectangleRoot && this.rectangleRoot.parentNode) {
        this.rectangleRoot.parentNode.removeChild(this.rectangleRoot);
        this.rectangleRoot = null;
      }
      this.intersectingTextElements.clear();
      this.intersectingTextNodes.clear();
      this.elementStates.clear();
      this.textNodeCache = /* @__PURE__ */ new WeakMap();
      this.cacheAccessOrder = /* @__PURE__ */ new WeakMap();
      this.lruRefs.clear();
      this.lruTimestamps.clear();
      this.cacheSize = 0;
      this.cacheEfficiencyMetrics.hotElements.clear();
      this.cacheEfficiencyMetrics.coldElements.clear();
      this.cacheEfficiencyMetrics.evictionHistory = [];
      this.cacheEfficiencyMetrics.totalCacheOperations = 0;
      this.cacheEfficiencyMetrics.cacheHitStreak = 0;
      this.cacheEfficiencyMetrics.longestHitStreak = 0;
      this.cacheEfficiencyMetrics.cacheMissStreak = 0;
      this.cacheEfficiencyMetrics.longestMissStreak = 0;
      this.cacheEfficiencyMetrics.averageAccessTime = 0;
      this.cacheEfficiencyMetrics.totalAccessTime = 0;
      this.cacheEfficiencyMetrics.memoryPressureEvents = 0;
      this.cacheEfficiencyMetrics.adaptiveCleanupTriggers = 0;
      if (this.adaptiveProcessor && this.adaptiveProcessor.enabled) {
        this.cleanupAdaptiveProcessing();
      }
      Object.keys(this.metrics).forEach((key) => {
        if (typeof this.metrics[key] === "number") {
          this.metrics[key] = 0;
        }
      });
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Enhanced rectangle intersection observer cleaned up");
      }
    }
    /**
     * Clean up adaptive processing resources (Task 2.1)
     */
    cleanupAdaptiveProcessing() {
      const batchProcessor = this.adaptiveProcessor.batchProcessor;
      if (batchProcessor.batchTimer) {
        clearTimeout(batchProcessor.batchTimer);
        batchProcessor.batchTimer = null;
      }
      batchProcessor.pendingBatch = [];
      this.adaptiveProcessor.frameRateMonitor.frameTimes = [];
      this.adaptiveProcessor.qualityController.qualityHistory = [];
      if (this.predictiveCaching && this.predictiveCaching.enabled) {
        this.cleanupPredictiveCaching();
      }
      if (this.enhancedAnalytics && this.enhancedAnalytics.enabled) {
        this.cleanupEnhancedAnalytics();
      }
      if (this.performanceMonitoringEnabled) {
        this.performanceState.isActive = false;
      }
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Adaptive processing cleaned up");
      }
    }
    /**
     * Clean up predictive caching resources (Task 2.2)
     */
    cleanupPredictiveCaching() {
      const preloader = this.predictiveCaching.preloader;
      if (preloader.preloadTimer) {
        clearInterval(preloader.preloadTimer);
        preloader.preloadTimer = null;
      }
      preloader.preloadQueue = [];
      this.predictiveCaching.behaviorAnalyzer.interactionHistory = [];
      this.predictiveCaching.behaviorAnalyzer.patterns.clear();
      this.predictiveCaching.scrollPredictor.scrollHistory = [];
      this.predictiveCaching.viewportCaching.sectorCache.clear();
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Predictive caching cleaned up");
      }
    }
    /**
     * Clean up enhanced analytics resources (Task 2.3)
     */
    cleanupEnhancedAnalytics() {
      const analytics = this.enhancedAnalytics;
      if (analytics.dashboard.updateTimer) {
        clearInterval(analytics.dashboard.updateTimer);
        analytics.dashboard.updateTimer = null;
      }
      if (analytics.trendAnalyzer.analysisTimer) {
        clearInterval(analytics.trendAnalyzer.analysisTimer);
        analytics.trendAnalyzer.analysisTimer = null;
      }
      if (analytics.memoryAnalyzer.samplingTimer) {
        clearInterval(analytics.memoryAnalyzer.samplingTimer);
        analytics.memoryAnalyzer.samplingTimer = null;
      }
      Object.keys(analytics.dashboard.currentMetrics).forEach((metric) => {
        analytics.dashboard.currentMetrics[metric] = [];
      });
      analytics.trendAnalyzer.trends.clear();
      analytics.regressionDetector.regressions = [];
      analytics.memoryAnalyzer.memorySamples = [];
      analytics.comparisonAnalyzer.edgeOnlySamples = [];
      analytics.comparisonAnalyzer.spatialSamples = [];
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Enhanced analytics cleaned up");
      }
    }
    /**
     * Update analytics with new performance data (Task 2.3)
     * @param {Object} data - Current performance data
     */
    updateAnalyticsWithNewData(data) {
      this.updateRegressionDetection(data);
      this.updateComparisonAnalytics(data);
    }
    /**
     * Update regression detection with new data (Task 2.3)
     * @param {Object} data - Current performance data
     */
    updateRegressionDetection(data) {
      const regressionDetector = this.enhancedAnalytics.regressionDetector;
      regressionDetector.baselineMetrics.forEach((baseline, metric) => {
        if (data[metric] !== void 0) {
          baseline.samples.push(data[metric]);
          if (baseline.samples.length > regressionDetector.detectionWindow) {
            baseline.samples.shift();
          }
          if (!baseline.established && baseline.samples.length >= 10) {
            baseline.baseline = baseline.samples.reduce((a, b) => a + b, 0) / baseline.samples.length;
            baseline.established = true;
          }
          if (baseline.established) {
            this.checkForRegression(metric, data[metric], baseline);
          }
        }
      });
    }
    /**
     * Check for performance regression (Task 2.3)
     * @param {string} metric - Metric name
     * @param {number} currentValue - Current metric value
     * @param {Object} baseline - Baseline data
     */
    checkForRegression(metric, currentValue, baseline) {
      const regressionDetector = this.enhancedAnalytics.regressionDetector;
      let isRegression = false;
      switch (metric) {
        case "processingTime":
        case "memoryUsage":
          isRegression = currentValue > baseline.baseline * regressionDetector.thresholdFactor;
          break;
        case "cacheHitRatio":
        case "performanceGain":
          isRegression = currentValue < baseline.baseline / regressionDetector.thresholdFactor;
          break;
      }
      if (isRegression) {
        regressionDetector.consecutivePoorSamples++;
        if (regressionDetector.consecutivePoorSamples >= regressionDetector.consecutiveThreshold) {
          this.alertPerformanceRegression(metric, currentValue, baseline.baseline);
          regressionDetector.consecutivePoorSamples = 0;
        }
      } else {
        const isRecovery = this.isPerformanceRecovery(metric, currentValue, baseline.baseline);
        if (isRecovery && regressionDetector.consecutivePoorSamples > 0) {
          regressionDetector.consecutivePoorSamples = 0;
          if (this.debugLogging) {
            console.log("[KeyPilot Debug] Performance recovery detected:", {
              metric,
              currentValue,
              baseline: baseline.baseline
            });
          }
        }
      }
    }
    /**
     * Check if current performance indicates recovery (Task 2.3)
     * @param {string} metric - Metric name
     * @param {number} currentValue - Current metric value
     * @param {number} baselineValue - Baseline metric value
     * @returns {boolean} - True if performance has recovered
     */
    isPerformanceRecovery(metric, currentValue, baselineValue) {
      const recoveryThreshold = this.enhancedAnalytics.regressionDetector.recoveryThreshold;
      switch (metric) {
        case "processingTime":
        case "memoryUsage":
          return currentValue <= baselineValue * (1 + (1 - recoveryThreshold));
        case "cacheHitRatio":
        case "performanceGain":
          return currentValue >= baselineValue * recoveryThreshold;
        default:
          return false;
      }
    }
    /**
     * Update comparison analytics (Task 2.3)
     * @param {Object} data - Current performance data
     */
    updateComparisonAnalytics(data) {
      const comparisonAnalyzer = this.enhancedAnalytics.comparisonAnalyzer;
      comparisonAnalyzer.edgeOnlySamples.push({
        processingTime: data.processingTime,
        memoryUsage: data.memoryUsage,
        cacheEfficiency: data.cacheHitRatio,
        timestamp: data.timestamp
      });
      if (comparisonAnalyzer.edgeOnlySamples.length > comparisonAnalyzer.sampleSize) {
        comparisonAnalyzer.edgeOnlySamples.shift();
      }
      if (comparisonAnalyzer.edgeOnlySamples.length >= 20) {
        this.performStatisticalComparison();
      }
    }
    /**
     * Perform statistical comparison between methods (Task 2.3)
     */
    performStatisticalComparison() {
      const comparisonAnalyzer = this.enhancedAnalytics.comparisonAnalyzer;
      const now = Date.now();
      if (now - comparisonAnalyzer.lastComparison < 3e4) {
        return;
      }
      const edgeSamples = comparisonAnalyzer.edgeOnlySamples;
      const edgeProcessingTimes = edgeSamples.map((s) => s.processingTime);
      const edgeMemoryUsage = edgeSamples.map((s) => s.memoryUsage);
      const edgeStats = {
        processingTime: this.calculateStatistics(edgeProcessingTimes),
        memoryUsage: this.calculateStatistics(edgeMemoryUsage)
      };
      const estimatedSpatialStats = this.estimateSpatialMethodPerformance(edgeStats);
      comparisonAnalyzer.comparisonResults.set("processingTime", {
        edgeOnlyMean: edgeStats.processingTime.mean,
        spatialMean: estimatedSpatialStats.processingTime.mean,
        edgeOnlyStdDev: edgeStats.processingTime.stdDev,
        spatialStdDev: estimatedSpatialStats.processingTime.stdDev,
        significantDifference: edgeStats.processingTime.mean < estimatedSpatialStats.processingTime.mean,
        effectSize: this.calculateEffectSize(edgeStats.processingTime, estimatedSpatialStats.processingTime),
        lastComparison: now
      });
      comparisonAnalyzer.lastComparison = now;
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Statistical comparison completed:", {
          edgeOnlyProcessingTime: edgeStats.processingTime.mean.toFixed(2) + "ms",
          estimatedSpatialProcessingTime: estimatedSpatialStats.processingTime.mean.toFixed(2) + "ms",
          performanceImprovement: ((estimatedSpatialStats.processingTime.mean - edgeStats.processingTime.mean) / estimatedSpatialStats.processingTime.mean * 100).toFixed(1) + "%"
        });
      }
    }
    /**
     * Calculate basic statistics for a dataset (Task 2.3)
     * @param {Array} data - Array of numbers
     * @returns {Object} - Statistics object
     */
    calculateStatistics(data) {
      if (data.length === 0) {
        return { mean: 0, stdDev: 0, min: 0, max: 0, median: 0 };
      }
      const sorted = [...data].sort((a, b) => a - b);
      const mean = data.reduce((a, b) => a + b, 0) / data.length;
      const variance = data.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / data.length;
      const stdDev = Math.sqrt(variance);
      const median = sorted.length % 2 === 0 ? (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2 : sorted[Math.floor(sorted.length / 2)];
      return {
        mean,
        stdDev,
        min: sorted[0],
        max: sorted[sorted.length - 1],
        median
      };
    }
    /**
     * Estimate spatial method performance based on edge-only data (Task 2.3)
     * @param {Object} edgeStats - Edge-only method statistics
     * @returns {Object} - Estimated spatial method statistics
     */
    estimateSpatialMethodPerformance(edgeStats) {
      const spatialMultiplier = 25;
      return {
        processingTime: {
          mean: edgeStats.processingTime.mean * spatialMultiplier,
          stdDev: edgeStats.processingTime.stdDev * spatialMultiplier
        },
        memoryUsage: {
          mean: edgeStats.memoryUsage.mean * 1.5,
          // Spatial uses more memory
          stdDev: edgeStats.memoryUsage.stdDev * 1.5
        }
      };
    }
    /**
     * Calculate effect size between two methods (Task 2.3)
     * @param {Object} method1Stats - Statistics for method 1
     * @param {Object} method2Stats - Statistics for method 2
     * @returns {number} - Cohen's d effect size
     */
    calculateEffectSize(method1Stats, method2Stats) {
      const pooledStdDev = Math.sqrt(
        (Math.pow(method1Stats.stdDev, 2) + Math.pow(method2Stats.stdDev, 2)) / 2
      );
      if (pooledStdDev === 0) return 0;
      return Math.abs(method1Stats.mean - method2Stats.mean) / pooledStdDev;
    }
    /**
     * Alert on performance trend (Task 2.3)
     * @param {string} metric - Metric name
     * @param {Object} trendData - Trend analysis data
     */
    alertPerformanceTrend(metric, trendData) {
      if (this.debugLogging) {
        console.warn("[KeyPilot Debug] Performance trend alert:", {
          metric,
          trend: trendData.trend,
          confidence: trendData.confidence.toFixed(2),
          slope: trendData.slope.toFixed(4)
        });
      }
    }
    /**
     * Alert on performance regression (Task 2.3)
     * @param {string} metric - Metric name
     * @param {number} currentValue - Current metric value
     * @param {number} baselineValue - Baseline metric value
     */
    alertPerformanceRegression(metric, currentValue, baselineValue) {
      const degradationPercent = ((currentValue - baselineValue) / baselineValue * 100).toFixed(1);
      if (this.debugLogging) {
        console.warn("[KeyPilot Debug] Performance regression detected:", {
          metric,
          currentValue: currentValue.toFixed(2),
          baselineValue: baselineValue.toFixed(2),
          degradation: degradationPercent + "%"
        });
      }
    }
    /**
     * Detect memory leaks (Task 2.3)
     */
    detectMemoryLeaks() {
      const memoryAnalyzer = this.enhancedAnalytics.memoryAnalyzer;
      const samples = memoryAnalyzer.memorySamples;
      if (samples.length < memoryAnalyzer.leakDetectionWindow) {
        return;
      }
      const recentSamples = samples.slice(-memoryAnalyzer.leakDetectionWindow);
      const memoryValues = recentSamples.map((s) => s.totalMemory);
      const timestamps = recentSamples.map((s) => s.timestamp);
      const regression = this.calculateLinearRegression(timestamps, memoryValues);
      const avgMemory = memoryValues.reduce((a, b) => a + b, 0) / memoryValues.length;
      const growthRate = regression.slope / avgMemory;
      const wasLeakDetected = memoryAnalyzer.leakDetected;
      memoryAnalyzer.leakDetected = growthRate > memoryAnalyzer.growthThreshold && regression.rSquared > 0.7;
      if (memoryAnalyzer.leakDetected && !wasLeakDetected) {
        if (this.debugLogging) {
          console.warn("[KeyPilot Debug] Memory leak detected:", {
            growthRate: (growthRate * 100).toFixed(2) + "%",
            confidence: regression.rSquared.toFixed(2),
            avgMemory: avgMemory.toFixed(1) + "MB"
          });
        }
      }
    }
    /**
     * Detect memory pressure (Task 2.3)
     * @param {Object} sample - Memory usage sample
     */
    detectMemoryPressure(sample) {
      const memoryAnalyzer = this.enhancedAnalytics.memoryAnalyzer;
      const pressureThreshold = memoryAnalyzer.pressureThreshold;
      const memoryPressure = sample.heapUsed / sample.heapTotal;
      const wasPressureDetected = memoryAnalyzer.pressureDetected;
      memoryAnalyzer.pressureDetected = memoryPressure > pressureThreshold;
      if (memoryAnalyzer.pressureDetected && !wasPressureDetected) {
        if (this.debugLogging) {
          console.warn("[KeyPilot Debug] Memory pressure detected:", {
            heapUsed: sample.heapUsed.toFixed(1) + "MB",
            heapTotal: sample.heapTotal.toFixed(1) + "MB",
            pressure: (memoryPressure * 100).toFixed(1) + "%",
            threshold: (pressureThreshold * 100).toFixed(1) + "%"
          });
        }
        this.performAggressiveCleanup();
      }
    }
    /**
     * Get cache hit ratio as numeric value (Task 2.3)
     * @returns {number} - Cache hit ratio (0-1)
     */
    getCacheHitRatioNumeric() {
      const totalAttempts = this.metrics.cacheHits + this.metrics.cacheMisses;
      return totalAttempts > 0 ? this.metrics.cacheHits / totalAttempts : 0;
    }
    /**
     * Get performance gain as numeric value (Task 2.3)
     * @returns {number} - Performance gain (0-1)
     */
    getPerformanceGainNumeric() {
      if (this.metrics.estimatedFullTraversalTime === 0) {
        return 0;
      }
      const actualTime = this.metrics.actualEdgeProcessingTime;
      const estimatedTime = this.metrics.estimatedFullTraversalTime;
      return Math.max(0, (estimatedTime - actualTime) / estimatedTime);
    }
    /**
     * Get estimated memory usage (Task 2.3)
     * @returns {number} - Estimated memory usage in MB
     */
    getEstimatedMemoryUsage() {
      const cacheMemory = this.cacheSize * 1e-3;
      const systemMemory = 2;
      return cacheMemory + systemMemory;
    }
    /**
     * Get detailed memory usage breakdown (Task 2.3)
     * @returns {Object} - Detailed memory usage information
     */
    getDetailedMemoryUsage() {
      const cacheMemory = this.cacheSize * 1e-3;
      const systemMemory = 2;
      const total = cacheMemory + systemMemory;
      let heapUsed = total;
      let heapTotal = total * 2;
      if (performance.memory) {
        heapUsed = performance.memory.usedJSHeapSize / (1024 * 1024);
        heapTotal = performance.memory.totalJSHeapSize / (1024 * 1024);
      }
      return {
        total,
        cache: cacheMemory,
        system: systemMemory,
        heapUsed,
        heapTotal
      };
    }
    /**
     * Find elements in a specific area (Task 2.2)
     * @param {Object} area - Area with left, top, right, bottom properties
     * @returns {Array} - Elements in the area
     */
    findElementsInArea(area) {
      const elements = [];
      const textContainerSelectors = [
        "p",
        "div",
        "span",
        "h1",
        "h2",
        "h3",
        "h4",
        "h5",
        "h6",
        "li",
        "td",
        "th",
        "blockquote",
        "pre",
        "code",
        "a",
        "strong",
        "em",
        "b",
        "i",
        "u",
        "article",
        "section",
        "main",
        "aside"
      ].join(", ");
      const allElements = document.querySelectorAll(textContainerSelectors);
      for (const element of allElements) {
        if (!this.elementContainsText(element)) {
          continue;
        }
        const rect = element.getBoundingClientRect();
        const elementArea = {
          left: rect.left + window.scrollX,
          top: rect.top + window.scrollY,
          right: rect.right + window.scrollX,
          bottom: rect.bottom + window.scrollY
        };
        if (elementArea.right >= area.left && elementArea.left <= area.right && elementArea.bottom >= area.top && elementArea.top <= area.bottom) {
          elements.push(element);
        }
      }
      return elements;
    }
    /**
     * Preload text nodes for an element (Task 2.2)
     * @param {Element} element - Element to preload
     * @param {string} reason - Reason for preloading
     */
    preloadElementTextNodes(element, reason) {
      const preloader = this.predictiveCaching.preloader;
      if (this.textNodeCache.has(element) || preloader.preloadCache.has(element)) {
        return;
      }
      preloader.preloadQueue.push({
        element,
        reason,
        priority: this.calculatePreloadPriority(element, reason),
        timestamp: Date.now()
      });
      preloader.preloadQueue.sort((a, b) => b.priority - a.priority);
      if (preloader.preloadQueue.length > preloader.maxElements) {
        preloader.preloadQueue = preloader.preloadQueue.slice(0, preloader.maxElements);
      }
    }
    /**
     * Calculate preload priority for an element (Task 2.2)
     * @param {Element} element - Element to calculate priority for
     * @param {string} reason - Reason for preloading
     * @returns {number} - Priority score (0-1)
     */
    calculatePreloadPriority(element, reason) {
      let priority = 0.5;
      switch (reason) {
        case "viewport":
          priority += 0.3;
          break;
        case "scroll_prediction":
          priority += 0.4;
          break;
        case "behavior_pattern":
          priority += 0.2;
          break;
      }
      const textLength = element.textContent?.length || 0;
      if (textLength > 100) {
        priority += 0.1;
      }
      const rect = element.getBoundingClientRect();
      if (rect.width > 0 && rect.height > 0) {
        priority += 0.1;
      }
      const tagName = element.tagName.toLowerCase();
      if (["p", "div", "article", "section"].includes(tagName)) {
        priority += 0.1;
      }
      return Math.min(1, Math.max(0, priority));
    }
    /**
     * Execute a preload request (Task 2.2)
     * @param {Object} request - Preload request
     */
    executePreloadRequest(request) {
      const { element, reason, priority } = request;
      const preloader = this.predictiveCaching.preloader;
      try {
        const textNodes = [];
        const walker = document.createTreeWalker(
          element,
          NodeFilter.SHOW_TEXT,
          {
            acceptNode: (node2) => {
              if (!node2.textContent || !node2.textContent.trim()) {
                return NodeFilter.FILTER_REJECT;
              }
              const parent = node2.parentElement;
              if (parent && this.isNonContentElement(parent)) {
                return NodeFilter.FILTER_REJECT;
              }
              return NodeFilter.FILTER_ACCEPT;
            }
          }
        );
        let node;
        while (node = walker.nextNode()) {
          textNodes.push(node);
        }
        this.textNodeCache.set(element, textNodes);
        this.cacheTextNodes(element, textNodes);
        preloader.preloadCache.set(element, {
          reason,
          priority,
          timestamp: Date.now(),
          textNodeCount: textNodes.length
        });
        if (this.debugLogging) {
          console.log("[KeyPilot Debug] Element preloaded:", {
            element: element.tagName,
            reason,
            priority: priority.toFixed(2),
            textNodes: textNodes.length
          });
        }
      } catch (error) {
        console.warn("[KeyPilot] Error preloading element:", error);
      }
    }
    /**
     * Preload elements for consistent rectangle sizes (Task 2.2)
     */
    preloadForConsistentSizes() {
      const viewport = this.predictiveCaching.viewportCaching.currentViewport;
      const expandedArea = {
        left: viewport.x - 100,
        top: viewport.y - 100,
        right: viewport.x + viewport.width + 100,
        bottom: viewport.y + viewport.height + 100
      };
      const elements = this.findElementsInArea(expandedArea);
      elements.forEach((element) => {
        this.preloadElementTextNodes(element, "behavior_pattern");
      });
    }
    /**
     * Preload elements in repeated areas (Task 2.2)
     */
    preloadRepeatedAreas() {
      const behaviorAnalyzer = this.predictiveCaching.behaviorAnalyzer;
      const recentInteractions = behaviorAnalyzer.interactionHistory.slice(-5);
      recentInteractions.forEach((interaction) => {
        if (interaction.type === "rectangle_update") {
          const rect = interaction.data.rect;
          const area = {
            left: rect.left - 50,
            top: rect.top - 50,
            right: rect.left + rect.width + 50,
            bottom: rect.top + rect.height + 50
          };
          const elements = this.findElementsInArea(area);
          elements.forEach((element) => {
            this.preloadElementTextNodes(element, "behavior_pattern");
          });
        }
      });
    }
    /**
     * Preload elements for vertical scrolling (Task 2.2)
     */
    preloadVerticalScrollAreas() {
      const viewport = this.predictiveCaching.viewportCaching.currentViewport;
      const preloadArea = {
        left: viewport.x,
        top: viewport.y + viewport.height,
        right: viewport.x + viewport.width,
        bottom: viewport.y + viewport.height + 300
      };
      const elements = this.findElementsInArea(preloadArea);
      elements.forEach((element) => {
        this.preloadElementTextNodes(element, "behavior_pattern");
      });
    }
    /**
     * Preload elements for horizontal scrolling (Task 2.2)
     */
    preloadHorizontalScrollAreas() {
      const viewport = this.predictiveCaching.viewportCaching.currentViewport;
      const preloadArea = {
        left: viewport.x + viewport.width,
        top: viewport.y,
        right: viewport.x + viewport.width + 300,
        bottom: viewport.y + viewport.height
      };
      const elements = this.findElementsInArea(preloadArea);
      elements.forEach((element) => {
        this.preloadElementTextNodes(element, "behavior_pattern");
      });
    }
    /**
     * Initialize enhanced performance monitoring and analytics (Task 2.3)
     */
    initializeEnhancedAnalytics() {
      if (this.enhancedAnalytics.dashboard.enabled) {
        this.initializePerformanceDashboard();
      }
      if (this.enhancedAnalytics.trendAnalyzer.enabled) {
        this.initializeTrendAnalysis();
      }
      if (this.enhancedAnalytics.regressionDetector.enabled) {
        this.initializeRegressionDetection();
      }
      if (this.enhancedAnalytics.memoryAnalyzer.enabled) {
        this.initializeMemoryAnalytics();
      }
      if (this.enhancedAnalytics.comparisonAnalyzer.enabled) {
        this.initializeComparisonAnalytics();
      }
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Enhanced analytics initialized:", {
          dashboard: this.enhancedAnalytics.dashboard.enabled,
          trendAnalysis: this.enhancedAnalytics.trendAnalyzer.enabled,
          regressionDetection: this.enhancedAnalytics.regressionDetector.enabled,
          memoryAnalytics: this.enhancedAnalytics.memoryAnalyzer.enabled,
          comparisonAnalytics: this.enhancedAnalytics.comparisonAnalyzer.enabled
        });
      }
    }
    /**
     * Initialize real-time performance dashboard (Task 2.3)
     */
    initializePerformanceDashboard() {
      const dashboard = this.enhancedAnalytics.dashboard;
      dashboard.updateTimer = setInterval(() => {
        this.updatePerformanceDashboard();
      }, dashboard.updateInterval);
      Object.keys(dashboard.currentMetrics).forEach((metric) => {
        dashboard.currentMetrics[metric] = [];
      });
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Performance dashboard initialized:", {
          updateInterval: dashboard.updateInterval + "ms",
          historyWindow: dashboard.historyWindow,
          metricsBuffer: dashboard.metricsBuffer
        });
      }
    }
    /**
     * Update real-time performance dashboard (Task 2.3)
     */
    updatePerformanceDashboard() {
      const dashboard = this.enhancedAnalytics.dashboard;
      const now = Date.now();
      const currentData = {
        timestamp: now,
        processingTime: this.metrics.edgeProcessingTime || 0,
        cacheHitRatio: this.getCacheHitRatioNumeric(),
        memoryUsage: this.getEstimatedMemoryUsage(),
        elementsProcessed: this.metrics.edgeElementsProcessed || 0,
        performanceGain: this.getPerformanceGainNumeric(),
        frameRate: this.adaptiveProcessor?.frameRateMonitor?.currentFps || 60
      };
      Object.keys(currentData).forEach((metric) => {
        if (metric !== "timestamp") {
          dashboard.currentMetrics[metric].push({
            value: currentData[metric],
            timestamp: now
          });
          if (dashboard.currentMetrics[metric].length > dashboard.historyWindow) {
            dashboard.currentMetrics[metric].shift();
          }
        }
      });
      this.updateAnalyticsWithNewData(currentData);
      dashboard.lastUpdate = now;
      if (this.debugLogging && now % 1e4 < dashboard.updateInterval) {
        console.log("[KeyPilot Debug] Dashboard updated:", {
          processingTime: currentData.processingTime.toFixed(2) + "ms",
          cacheHitRatio: (currentData.cacheHitRatio * 100).toFixed(1) + "%",
          memoryUsage: currentData.memoryUsage.toFixed(1) + "MB",
          performanceGain: (currentData.performanceGain * 100).toFixed(1) + "%",
          frameRate: currentData.frameRate.toFixed(1) + "fps"
        });
      }
    }
    /**
     * Initialize trend analysis system (Task 2.3)
     */
    initializeTrendAnalysis() {
      const trendAnalyzer = this.enhancedAnalytics.trendAnalyzer;
      trendAnalyzer.analysisTimer = setInterval(() => {
        this.analyzePerfomanceTrends();
      }, trendAnalyzer.analysisInterval);
      const metricsToTrack = [
        "processingTime",
        "cacheHitRatio",
        "memoryUsage",
        "performanceGain",
        "frameRate"
      ];
      metricsToTrack.forEach((metric) => {
        trendAnalyzer.trends.set(metric, {
          samples: [],
          trend: "stable",
          // 'improving', 'degrading', 'stable'
          confidence: 0,
          slope: 0,
          lastAnalysis: 0
        });
      });
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Trend analysis initialized:", {
          analysisInterval: trendAnalyzer.analysisInterval + "ms",
          analysisWindow: trendAnalyzer.analysisWindow,
          metricsTracked: metricsToTrack.length
        });
      }
    }
    /**
     * Analyze performance trends (Task 2.3)
     */
    analyzePerfomanceTrends() {
      const trendAnalyzer = this.enhancedAnalytics.trendAnalyzer;
      const dashboard = this.enhancedAnalytics.dashboard;
      trendAnalyzer.trends.forEach((trendData, metric) => {
        const metricData = dashboard.currentMetrics[metric];
        if (metricData.length < trendAnalyzer.analysisWindow) {
          return;
        }
        const recentSamples = metricData.slice(-trendAnalyzer.analysisWindow);
        const values = recentSamples.map((sample) => sample.value);
        const timestamps = recentSamples.map((sample) => sample.timestamp);
        const regression = this.calculateLinearRegression(timestamps, values);
        let trend = "stable";
        const normalizedSlope = Math.abs(regression.slope) / (Math.max(...values) - Math.min(...values) + 1e-3);
        if (normalizedSlope > trendAnalyzer.significanceThreshold) {
          if (regression.slope > 0) {
            trend = metric === "processingTime" || metric === "memoryUsage" ? "degrading" : "improving";
          } else {
            trend = metric === "processingTime" || metric === "memoryUsage" ? "improving" : "degrading";
          }
        }
        trendData.trend = trend;
        trendData.confidence = regression.rSquared;
        trendData.slope = regression.slope;
        trendData.lastAnalysis = Date.now();
        if (trendData.confidence > trendAnalyzer.confidenceThreshold && trend === "degrading") {
          this.alertPerformanceTrend(metric, trendData);
        }
      });
      trendAnalyzer.lastAnalysis = Date.now();
      if (this.debugLogging) {
        const trendSummary = {};
        trendAnalyzer.trends.forEach((data, metric) => {
          trendSummary[metric] = {
            trend: data.trend,
            confidence: data.confidence.toFixed(2)
          };
        });
        console.log("[KeyPilot Debug] Trend analysis completed:", trendSummary);
      }
    }
    /**
     * Calculate linear regression for trend analysis (Task 2.3)
     * @param {Array} x - X values (timestamps)
     * @param {Array} y - Y values (metric values)
     * @returns {Object} - Regression results
     */
    calculateLinearRegression(x, y) {
      const n = x.length;
      if (n === 0) return { slope: 0, intercept: 0, rSquared: 0 };
      const minX = Math.min(...x);
      const normalizedX = x.map((val) => val - minX);
      const sumX = normalizedX.reduce((a, b) => a + b, 0);
      const sumY = y.reduce((a, b) => a + b, 0);
      const sumXY = normalizedX.reduce((sum, xi, i) => sum + xi * y[i], 0);
      const sumXX = normalizedX.reduce((sum, xi) => sum + xi * xi, 0);
      const sumYY = y.reduce((sum, yi) => sum + yi * yi, 0);
      const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
      const intercept = (sumY - slope * sumX) / n;
      const yMean = sumY / n;
      const ssRes = y.reduce((sum, yi, i) => {
        const predicted = slope * normalizedX[i] + intercept;
        return sum + Math.pow(yi - predicted, 2);
      }, 0);
      const ssTot = y.reduce((sum, yi) => sum + Math.pow(yi - yMean, 2), 0);
      const rSquared = ssTot === 0 ? 1 : 1 - ssRes / ssTot;
      return { slope, intercept, rSquared: Math.max(0, rSquared) };
    }
    /**
     * Initialize regression detection system (Task 2.3)
     */
    initializeRegressionDetection() {
      const regressionDetector = this.enhancedAnalytics.regressionDetector;
      const metricsToMonitor = [
        "processingTime",
        "cacheHitRatio",
        "memoryUsage",
        "performanceGain"
      ];
      metricsToMonitor.forEach((metric) => {
        regressionDetector.baselineMetrics.set(metric, {
          baseline: null,
          samples: [],
          established: false
        });
      });
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Regression detection initialized:", {
          detectionWindow: regressionDetector.detectionWindow,
          thresholdFactor: regressionDetector.thresholdFactor,
          consecutiveThreshold: regressionDetector.consecutiveThreshold
        });
      }
    }
    /**
     * Initialize memory analytics system (Task 2.3)
     */
    initializeMemoryAnalytics() {
      const memoryAnalyzer = this.enhancedAnalytics.memoryAnalyzer;
      memoryAnalyzer.samplingTimer = setInterval(() => {
        this.sampleMemoryUsage();
      }, memoryAnalyzer.samplingInterval);
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Memory analytics initialized:", {
          samplingInterval: memoryAnalyzer.samplingInterval + "ms",
          leakDetectionWindow: memoryAnalyzer.leakDetectionWindow,
          growthThreshold: (memoryAnalyzer.growthThreshold * 100).toFixed(1) + "%"
        });
      }
    }
    /**
     * Sample memory usage for analytics (Task 2.3)
     */
    sampleMemoryUsage() {
      const memoryAnalyzer = this.enhancedAnalytics.memoryAnalyzer;
      const memoryUsage = this.getDetailedMemoryUsage();
      const sample = {
        timestamp: Date.now(),
        totalMemory: memoryUsage.total,
        cacheMemory: memoryUsage.cache,
        systemMemory: memoryUsage.system,
        heapUsed: memoryUsage.heapUsed,
        heapTotal: memoryUsage.heapTotal
      };
      memoryAnalyzer.memorySamples.push(sample);
      if (memoryAnalyzer.memorySamples.length > memoryAnalyzer.leakDetectionWindow) {
        memoryAnalyzer.memorySamples.shift();
      }
      this.detectMemoryLeaks();
      this.detectMemoryPressure(sample);
    }
    /**
     * Initialize comparison analytics system (Task 2.3)
     */
    initializeComparisonAnalytics() {
      const comparisonAnalyzer = this.enhancedAnalytics.comparisonAnalyzer;
      const metricsToCompare = [
        "processingTime",
        "accuracy",
        "memoryUsage",
        "cacheEfficiency"
      ];
      metricsToCompare.forEach((metric) => {
        comparisonAnalyzer.comparisonResults.set(metric, {
          edgeOnlyMean: 0,
          spatialMean: 0,
          edgeOnlyStdDev: 0,
          spatialStdDev: 0,
          significantDifference: false,
          pValue: 1,
          effectSize: 0,
          lastComparison: 0
        });
      });
      if (this.debugLogging) {
        console.log("[KeyPilot Debug] Comparison analytics initialized:", {
          sampleSize: comparisonAnalyzer.sampleSize,
          confidenceInterval: comparisonAnalyzer.confidenceInterval,
          significanceThreshold: comparisonAnalyzer.significanceThreshold
        });
      }
    }
    // ===== INTEGRATED PERFORMANCE MONITORING METHODS =====
    /**
     * Initialize integrated performance monitoring system
     */
    initializePerformanceMonitoring() {
      if (!this.performanceMonitoringEnabled) {
        return;
      }
      const config = PERFORMANCE_MONITORING;
      this.performanceMetrics = {
        // Element processing metrics
        elementsObserved: 0,
        elementsProcessed: 0,
        elementsEntering: 0,
        elementsLeaving: 0,
        // Processing time metrics
        totalProcessingTime: 0,
        averageProcessingTime: 0,
        maxProcessingTime: 0,
        minProcessingTime: Infinity,
        // Character detection metrics
        charactersDetected: 0,
        characterDetectionTime: 0,
        characterCacheHits: 0,
        characterCacheMisses: 0,
        // Smart targeting metrics
        elementReductionPercentage: 0,
        smartTargetingTime: 0,
        dynamicElementsAdded: 0,
        // Cache efficiency metrics
        cacheHitRate: 0,
        cacheEfficiency: 0,
        memoryUsage: 0,
        // Performance comparison metrics
        estimatedTraditionalTime: 0,
        actualOptimizedTime: 0,
        performanceGainPercentage: 0,
        speedupFactor: 1
      };
      this.performanceHistory = {
        processingTimes: [],
        elementCounts: [],
        cacheHitRates: [],
        performanceGains: [],
        timestamps: []
      };
      this.performanceState = {
        startTime: 0,
        sessionStartTime: Date.now(),
        totalOperations: 0,
        lastUpdate: 0,
        metricsBufferSize: config.METRICS_BUFFER_SIZE || 100,
        performanceThreshold: config.PERFORMANCE_THRESHOLD_MS || 50,
        baselinePerformance: null,
        regressionDetected: false,
        performanceDegradationFactor: 2,
        fallbackTriggered: false,
        isActive: true,
        alerts: []
      };
      this.performanceAlerts = {
        performanceDegradation: false,
        highProcessingTime: false,
        lowCacheEfficiency: false,
        memoryPressure: false
      };
    }
    /**
     * Add performance alert to integrated system
     */
    addPerformanceAlert(type2, data) {
      if (!this.performanceMonitoringEnabled) {
        return;
      }
      const alert = {
        type: type2,
        data,
        timestamp: Date.now(),
        id: `alert_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
      };
      this.performanceState.alerts.push(alert);
      if (this.performanceState.alerts.length > 50) {
        this.performanceState.alerts = this.performanceState.alerts.slice(-50);
      }
    }
    /**
     * Start performance monitoring session
     */
    startPerformanceMonitoring() {
      if (!this.performanceMonitoringEnabled) {
        return;
      }
      this.performanceState.startTime = performance.now();
      this.performanceState.sessionStartTime = Date.now();
      this.resetPerformanceMetrics();
    }
    /**
     * Record element processing performance metrics
     * @param {Object} data - Processing data
     */
    recordElementProcessing(data) {
      if (!this.performanceMonitoringEnabled) {
        return;
      }
      const {
        elementsProcessed = 0,
        elementsEntering = 0,
        elementsLeaving = 0,
        processingTime = 0,
        elementsObserved = 0
      } = data;
      this.performanceMetrics.elementsProcessed += elementsProcessed;
      this.performanceMetrics.elementsEntering += elementsEntering;
      this.performanceMetrics.elementsLeaving += elementsLeaving;
      this.performanceMetrics.elementsObserved = elementsObserved;
      this.performanceMetrics.totalProcessingTime += processingTime;
      this.updateProcessingTimeStats(processingTime);
      this.addToPerformanceHistory("processingTimes", processingTime);
      this.addToPerformanceHistory("elementCounts", elementsProcessed);
      this.addToPerformanceHistory("timestamps", Date.now());
      this.checkPerformanceAlerts(processingTime);
      this.performanceState.totalOperations++;
      this.performanceState.lastUpdate = Date.now();
    }
    /**
     * Record character detection performance metrics
     * @param {Object} data - Character detection data
     */
    recordCharacterDetection(data) {
      if (!this.performanceMonitoringEnabled) {
        return;
      }
      const {
        charactersDetected = 0,
        detectionTime = 0,
        cacheHits = 0,
        cacheMisses = 0
      } = data;
      this.performanceMetrics.charactersDetected += charactersDetected;
      this.performanceMetrics.characterDetectionTime += detectionTime;
      this.performanceMetrics.characterCacheHits += cacheHits;
      this.performanceMetrics.characterCacheMisses += cacheMisses;
      const totalCacheAccess = this.performanceMetrics.characterCacheHits + this.performanceMetrics.characterCacheMisses;
      this.performanceMetrics.cacheHitRate = totalCacheAccess > 0 ? this.performanceMetrics.characterCacheHits / totalCacheAccess * 100 : 0;
      this.addToPerformanceHistory("cacheHitRates", this.performanceMetrics.cacheHitRate);
    }
    /**
     * Record smart targeting performance metrics
     * @param {Object} data - Smart targeting data
     */
    recordSmartTargeting(data) {
      if (!this.performanceMonitoringEnabled) {
        return;
      }
      const {
        elementReductionPercentage = 0,
        smartTargetingTime = 0,
        dynamicElementsAdded = 0
      } = data;
      this.performanceMetrics.elementReductionPercentage = elementReductionPercentage;
      this.performanceMetrics.smartTargetingTime += smartTargetingTime;
      this.performanceMetrics.dynamicElementsAdded += dynamicElementsAdded;
    }
    /**
     * Record performance comparison data
     * @param {Object} data - Comparison data
     */
    recordPerformanceComparison(data) {
      if (!this.performanceMonitoringEnabled) {
        return;
      }
      const {
        estimatedTraditionalTime = 0,
        actualOptimizedTime = 0
      } = data;
      this.performanceMetrics.estimatedTraditionalTime += estimatedTraditionalTime;
      this.performanceMetrics.actualOptimizedTime += actualOptimizedTime;
      if (this.performanceMetrics.estimatedTraditionalTime > 0) {
        this.performanceMetrics.performanceGainPercentage = (this.performanceMetrics.estimatedTraditionalTime - this.performanceMetrics.actualOptimizedTime) / this.performanceMetrics.estimatedTraditionalTime * 100;
        this.performanceMetrics.speedupFactor = this.performanceMetrics.estimatedTraditionalTime / Math.max(this.performanceMetrics.actualOptimizedTime, 1);
      }
      this.addToPerformanceHistory("performanceGains", this.performanceMetrics.performanceGainPercentage);
    }
    /**
     * Update processing time statistics
     * @param {number} processingTime - Current processing time
     */
    updateProcessingTimeStats(processingTime) {
      if (processingTime > this.performanceMetrics.maxProcessingTime) {
        this.performanceMetrics.maxProcessingTime = processingTime;
      }
      if (processingTime < this.performanceMetrics.minProcessingTime) {
        this.performanceMetrics.minProcessingTime = processingTime;
      }
      const totalOps = this.performanceState.totalOperations + 1;
      this.performanceMetrics.averageProcessingTime = (this.performanceMetrics.averageProcessingTime * (totalOps - 1) + processingTime) / totalOps;
    }
    /**
     * Add data to performance history with buffer management
     * @param {string} metric - Metric name
     * @param {*} value - Value to add
     */
    addToPerformanceHistory(metric, value) {
      if (!this.performanceHistory[metric]) {
        this.performanceHistory[metric] = [];
      }
      this.performanceHistory[metric].push(value);
      if (this.performanceHistory[metric].length > this.performanceState.metricsBufferSize) {
        this.performanceHistory[metric].shift();
      }
    }
    /**
     * Check for performance alerts
     * @param {number} processingTime - Current processing time
     */
    /**
     * Get current performance metrics
     * @returns {Object} Current metrics
     */
    getPerformanceMonitorMetrics() {
      if (!this.performanceMonitoringEnabled) {
        return null;
      }
      return {
        ...this.performanceMetrics,
        alerts: { ...this.performanceAlerts },
        sessionDuration: Date.now() - this.performanceState.sessionStartTime,
        totalOperations: this.performanceState.totalOperations,
        lastUpdate: this.performanceState.lastUpdate
      };
    }
    /**
     * Get performance comparison statistics
     * @returns {Object} Comparison statistics
     */
    getComparisonStats() {
      if (!this.performanceMonitoringEnabled) {
        return null;
      }
      const optimizedSamples = this.performanceHistory.processingTimes;
      if (optimizedSamples.length === 0) {
        return null;
      }
      const optimizedAvg = optimizedSamples.reduce((sum, val) => sum + val, 0) / optimizedSamples.length;
      const traditionalAvg = this.performanceMetrics.estimatedTraditionalTime / Math.max(this.performanceState.totalOperations, 1);
      return {
        optimizedAverage: optimizedAvg,
        traditionalAverage: traditionalAvg,
        speedupFactor: traditionalAvg / Math.max(optimizedAvg, 1),
        performanceGain: traditionalAvg > 0 ? (traditionalAvg - optimizedAvg) / traditionalAvg * 100 : 0,
        sampleSize: optimizedSamples.length
      };
    }
    /**
     * Get trend analysis results
     * @returns {Object} Trend analysis
     */
    getTrendAnalysis() {
      if (!this.performanceMonitoringEnabled) {
        return null;
      }
      const processingTimes = this.performanceHistory.processingTimes;
      const cacheHitRates = this.performanceHistory.cacheHitRates;
      return {
        processingTimeTrend: this.calculateTrend(processingTimes),
        cacheEfficiencyTrend: this.calculateTrend(cacheHitRates),
        dataPoints: processingTimes.length,
        lastAnalysis: Date.now()
      };
    }
    /**
     * Calculate trend for a data series
     * @param {Array<number>} data - Data series
     * @returns {string} Trend direction
     */
    calculateTrend(data) {
      if (data.length < 3) {
        return "insufficient_data";
      }
      const recent = data.slice(-Math.min(10, data.length));
      const firstHalf = recent.slice(0, Math.floor(recent.length / 2));
      const secondHalf = recent.slice(Math.floor(recent.length / 2));
      const firstAvg = firstHalf.reduce((sum, val) => sum + val, 0) / firstHalf.length;
      const secondAvg = secondHalf.reduce((sum, val) => sum + val, 0) / secondHalf.length;
      const threshold = firstAvg * 0.1;
      if (secondAvg > firstAvg + threshold) {
        return "increasing";
      } else if (secondAvg < firstAvg - threshold) {
        return "decreasing";
      } else {
        return "stable";
      }
    }
    /**
     * Reset performance metrics for new session
     */
    resetPerformanceMetrics() {
      if (!this.performanceMonitoringEnabled) {
        return;
      }
      this.performanceMetrics = {
        elementsObserved: 0,
        elementsProcessed: 0,
        elementsEntering: 0,
        elementsLeaving: 0,
        totalProcessingTime: 0,
        averageProcessingTime: 0,
        maxProcessingTime: 0,
        minProcessingTime: Infinity,
        charactersDetected: 0,
        characterDetectionTime: 0,
        characterCacheHits: 0,
        characterCacheMisses: 0,
        elementReductionPercentage: 0,
        smartTargetingTime: 0,
        dynamicElementsAdded: 0,
        cacheHitRate: 0,
        cacheEfficiency: 0,
        memoryUsage: 0,
        estimatedTraditionalTime: 0,
        actualOptimizedTime: 0,
        performanceGainPercentage: 0,
        speedupFactor: 1
      };
      this.performanceState.totalOperations = 0;
    }
    /**
     * Export complete performance data
     * @returns {Object} Complete performance data
     */
    exportData() {
      if (!this.performanceMonitoringEnabled) {
        return null;
      }
      return {
        metrics: this.getPerformanceMonitorMetrics(),
        history: { ...this.performanceHistory },
        comparison: this.getComparisonStats(),
        trends: this.getTrendAnalysis(),
        configuration: {
          enabled: this.performanceMonitoringEnabled,
          metricsBufferSize: this.performanceState.metricsBufferSize,
          performanceThreshold: this.performanceState.performanceThreshold
        }
      };
    }
  };

  // src/utils/extension-context.js
  var _contextInvalidated = false;
  function isExtensionContextValid() {
    if (_contextInvalidated) return false;
    try {
      if (typeof chrome === "undefined" || !chrome.runtime || !chrome.runtime.id) {
        _contextInvalidated = true;
        return false;
      }
      return true;
    } catch {
      _contextInvalidated = true;
      return false;
    }
  }
  function isExtensionContextInvalidatedError(error) {
    if (!error) return false;
    const msg = typeof error === "string" ? error : error && typeof error === "object" && "message" in error ? String(
      /** @type {{ message?: unknown }} */
      error.message || ""
    ) : String(error);
    return /Extension context invalidated/i.test(msg);
  }
  function markExtensionContextInvalidated() {
    _contextInvalidated = true;
  }
  function noteExtensionContextError(error) {
    if (isExtensionContextInvalidatedError(error)) {
      markExtensionContextInvalidated();
      return true;
    }
    if (!isExtensionContextValid()) {
      return true;
    }
    return false;
  }
  function safeRuntimeSendMessage(message, opts = {}) {
    if (!isExtensionContextValid()) {
      try {
        opts.onInvalidated?.();
      } catch {
      }
      return false;
    }
    try {
      const result = chrome.runtime.sendMessage(message);
      if (result && typeof result.then === "function") {
        result.then((response) => {
          try {
            opts.onResponse?.(response);
          } catch {
          }
        }).catch((error) => {
          if (noteExtensionContextError(error)) {
            try {
              opts.onInvalidated?.();
            } catch {
            }
            return;
          }
          try {
            opts.onError?.(error);
          } catch {
          }
        });
      }
      return true;
    } catch (error) {
      if (noteExtensionContextError(error)) {
        try {
          opts.onInvalidated?.();
        } catch {
        }
        return false;
      }
      try {
        opts.onError?.(error);
      } catch {
      }
      return false;
    }
  }

  // src/modules/mouse-coordinate-manager.js
  var MouseCoordinateManager = class {
    constructor() {
      this.storageKey = "keypilot_last_mouse_coordinates";
      this.lastStoredCoordinates = null;
      this.isMonitoringInactive = false;
      this.inactiveMouseHandler = null;
      this.beforeUnloadHandler = null;
      this.currentMousePosition = { x: 0, y: 0 };
      this.loadStoredCoordinates();
      this.setupBeforeUnloadListener();
    }
    /**
     * Persist coordinates to localStorage (works even when extension context is dead).
     * @param {object} coordinates
     */
    _storeCoordinatesLocally(coordinates) {
      try {
        localStorage.setItem(this.storageKey, JSON.stringify(coordinates));
      } catch {
      }
    }
    /**
     * Store mouse coordinates to Chrome storage
     * @param {number} x - X coordinate
     * @param {number} y - Y coordinate
     * @param {string} url - Current page URL for context
     */
    async storeCoordinates(x, y, url = window.location.href) {
      const coordinates = {
        x,
        y,
        url,
        timestamp: Date.now(),
        viewport: {
          width: window.innerWidth,
          height: window.innerHeight
        }
      };
      try {
        if (isExtensionContextValid()) {
          if (chrome?.storage?.sync) {
            await chrome.storage.sync.set({ [this.storageKey]: coordinates });
          } else if (chrome?.storage?.local) {
            await chrome.storage.local.set({ [this.storageKey]: coordinates });
          }
        }
        this._storeCoordinatesLocally(coordinates);
        this.lastStoredCoordinates = coordinates;
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot] Mouse coordinates stored:", coordinates);
        }
      } catch (error) {
        if (noteExtensionContextError(error)) {
          this._storeCoordinatesLocally(coordinates);
          this.lastStoredCoordinates = coordinates;
          if (window.KEYPILOT_DEBUG) {
            console.warn("[KeyPilot] Extension context invalidated; stored mouse coordinates in localStorage only");
          }
          return;
        }
        console.warn("[KeyPilot] Failed to store mouse coordinates:", error);
      }
    }
    /**
     * Load stored mouse coordinates from Chrome storage
     * @returns {Promise<Object|null>} Stored coordinates or null if none found
     */
    async loadStoredCoordinates() {
      try {
        let result = null;
        if (isExtensionContextValid()) {
          if (chrome?.storage?.sync) {
            const syncResult = await chrome.storage.sync.get([this.storageKey]);
            result = syncResult[this.storageKey];
          }
          if (!result && chrome?.storage?.local) {
            const localResult = await chrome.storage.local.get([this.storageKey]);
            result = localResult[this.storageKey];
          }
        }
        if (!result) {
          const localStorageData = localStorage.getItem(this.storageKey);
          if (localStorageData) {
            result = JSON.parse(localStorageData);
          }
        }
        this.lastStoredCoordinates = result;
        if (window.KEYPILOT_DEBUG && result) {
          console.log("[KeyPilot] Loaded stored mouse coordinates:", result);
        }
        return result;
      } catch (error) {
        if (noteExtensionContextError(error)) {
          try {
            const localStorageData = localStorage.getItem(this.storageKey);
            if (localStorageData) {
              this.lastStoredCoordinates = JSON.parse(localStorageData);
              return this.lastStoredCoordinates;
            }
          } catch {
          }
          return null;
        }
        console.warn("[KeyPilot] Failed to load stored mouse coordinates:", error);
        return null;
      }
    }
    /**
     * Get the best cursor position for initialization
     * Uses stored coordinates if available, otherwise falls back to upper left with margin
     * @returns {Object} Object with x, y coordinates
     */
    getInitialCursorPosition() {
      if (this.lastStoredCoordinates) {
        const stored = this.lastStoredCoordinates;
        const x = Math.min(stored.x, window.innerWidth - 20);
        const y = Math.min(stored.y, window.innerHeight - 20);
        const validX = Math.max(10, x);
        const validY = Math.max(10, y);
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot] Using stored coordinates for cursor position:", {
            original: { x: stored.x, y: stored.y },
            adjusted: { x: validX, y: validY }
          });
        }
        return { x: validX, y: validY };
      }
      const fallbackX = 10;
      const fallbackY = 10;
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot] No stored coordinates, using fallback position:", {
          x: fallbackX,
          y: fallbackY
        });
      }
      return { x: fallbackX, y: fallbackY };
    }
    /**
     * Update current mouse position for beforeunload storage
     * @param {number} x - X coordinate
     * @param {number} y - Y coordinate
     */
    updateCurrentMousePosition(x, y) {
      this.currentMousePosition.x = x;
      this.currentMousePosition.y = y;
    }
    /**
     * Set up beforeunload listener to store coordinates when leaving page
     */
    setupBeforeUnloadListener() {
      this.beforeUnloadHandler = () => {
        if (this.currentMousePosition.x !== 0 || this.currentMousePosition.y !== 0) {
          this.storeCoordinatesSync(this.currentMousePosition.x, this.currentMousePosition.y);
          if (window.KEYPILOT_DEBUG) {
            console.log("[KeyPilot] Page unloading, stored coordinates:", this.currentMousePosition);
          }
        }
      };
      window.addEventListener("beforeunload", this.beforeUnloadHandler);
      window.addEventListener("pagehide", this.beforeUnloadHandler);
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot] Set up beforeunload coordinate storage");
      }
    }
    /**
     * Synchronous coordinate storage for beforeunload events
     * @param {number} x - X coordinate
     * @param {number} y - Y coordinate
     */
    storeCoordinatesSync(x, y) {
      const coordinates = {
        x,
        y,
        url: window.location.href,
        timestamp: Date.now(),
        viewport: {
          width: window.innerWidth,
          height: window.innerHeight
        },
        storedOnUnload: true
      };
      try {
        this._storeCoordinatesLocally(coordinates);
        this.lastStoredCoordinates = coordinates;
        if (isExtensionContextValid() && chrome?.storage?.local) {
          chrome.storage.local.set({ [this.storageKey]: coordinates }).catch((error) => {
            noteExtensionContextError(error);
          });
        }
      } catch (error) {
        if (noteExtensionContextError(error)) return;
        if (window.KEYPILOT_DEBUG) {
          console.warn("[KeyPilot] Failed to store coordinates on beforeunload:", error);
        }
      }
    }
    /**
     * Handle link click event to store coordinates (legacy method, now optional)
     * @param {number} x - X coordinate where link was clicked
     * @param {number} y - Y coordinate where link was clicked
     * @param {Element} linkElement - The link element that was clicked
     */
    handleLinkClick(x, y, linkElement) {
      this.updateCurrentMousePosition(x, y);
      if (linkElement && linkElement.tagName === "A" && linkElement.href) {
        this.storeCoordinates(x, y, linkElement.href);
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot] Link clicked, storing coordinates:", {
            x,
            y,
            href: linkElement.href,
            linkText: linkElement.textContent?.substring(0, 50)
          });
        }
      }
    }
    /**
     * Start monitoring mouse events when application is not active
     * Uses Page Visibility API to detect when page is not active
     */
    startInactiveMouseMonitoring() {
      if (this.isMonitoringInactive) {
        return;
      }
      if (typeof document.hidden === "undefined") {
        console.warn("[KeyPilot] Page Visibility API not available, cannot monitor inactive mouse events");
        return;
      }
      this.isMonitoringInactive = true;
      this.inactiveMouseHandler = (event) => {
        if (document.hidden) {
          this.storeCoordinates(event.clientX, event.clientY);
          if (window.KEYPILOT_DEBUG) {
            console.log("[KeyPilot] Mouse event captured while page inactive:", {
              x: event.clientX,
              y: event.clientY,
              hidden: document.hidden
            });
          }
        }
      };
      document.addEventListener("mousemove", this.inactiveMouseHandler, {
        passive: true,
        capture: true
      });
      document.addEventListener("visibilitychange", () => {
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot] Page visibility changed:", {
            hidden: document.hidden,
            visibilityState: document.visibilityState
          });
        }
      });
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot] Started inactive mouse monitoring");
      }
    }
    /**
     * Stop monitoring mouse events when application is not active
     */
    stopInactiveMouseMonitoring() {
      if (!this.isMonitoringInactive) {
        return;
      }
      this.isMonitoringInactive = false;
      if (this.inactiveMouseHandler) {
        document.removeEventListener("mousemove", this.inactiveMouseHandler, {
          passive: true,
          capture: true
        });
        this.inactiveMouseHandler = null;
      }
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot] Stopped inactive mouse monitoring");
      }
    }
    /**
     * Clear stored coordinates
     */
    async clearStoredCoordinates() {
      try {
        if (isExtensionContextValid()) {
          if (chrome?.storage?.sync) {
            await chrome.storage.sync.remove([this.storageKey]);
          }
          if (chrome?.storage?.local) {
            await chrome.storage.local.remove([this.storageKey]);
          }
        }
        localStorage.removeItem(this.storageKey);
        this.lastStoredCoordinates = null;
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot] Cleared stored mouse coordinates");
        }
      } catch (error) {
        if (noteExtensionContextError(error)) {
          try {
            localStorage.removeItem(this.storageKey);
          } catch {
          }
          this.lastStoredCoordinates = null;
          return;
        }
        console.warn("[KeyPilot] Failed to clear stored coordinates:", error);
      }
    }
    /**
     * Get debug information about stored coordinates
     * @returns {Object} Debug information
     */
    getDebugInfo() {
      return {
        hasStoredCoordinates: !!this.lastStoredCoordinates,
        storedCoordinates: this.lastStoredCoordinates,
        isMonitoringInactive: this.isMonitoringInactive,
        storageKey: this.storageKey,
        pageVisibilitySupported: typeof document.hidden !== "undefined",
        currentVisibility: {
          hidden: document.hidden,
          visibilityState: document.visibilityState
        }
      };
    }
    /**
     * Cleanup method to be called when the extension is disabled or unloaded
     */
    cleanup() {
      this.stopInactiveMouseMonitoring();
      if (this.beforeUnloadHandler) {
        window.removeEventListener("beforeunload", this.beforeUnloadHandler);
        window.removeEventListener("pagehide", this.beforeUnloadHandler);
        this.beforeUnloadHandler = null;
      }
    }
  };

  // src/modules/column-layout-manager.js
  init_constants();
  var SLIP_BAR_HEIGHT = "28px";
  var SLIP_BAR_HEIGHT_PX_FALLBACK = 28;
  var SHELL_PAD_PX = 10;
  var COL_GAP_PX = 24;
  var COL_WIDTH_MIN_PX = 280;
  var COL_WIDTH_MAX_PX = 420;
  var COL_WIDTH_IDEAL_PX = 360;
  var SLIP_END_RATIO = 0.86;
  var SLIP_END_PX = 160;
  var WAKE_THROTTLE_MS = 850;
  var MUTATION_REMEASURE_MS = 80;
  var ColumnLayoutManager = class {
    constructor() {
      this._active = false;
      this._target = null;
      this._pageMode = false;
      this._popoverMode = false;
      this._snapshots = [];
      this._slipOffset = 0;
      this._maxSlip = 0;
      this._shell = null;
      this._body = null;
      this._placeholder = null;
      this._slipBar = null;
      this._track = null;
      this._knob = null;
      this._expandBtn = null;
      this._closeBtn = null;
      this._resizeApi = null;
      this._dragging = false;
      this._onPointerMove = null;
      this._onPointerUp = null;
      this._onResize = null;
      this._resizeObserver = null;
      this._mutationObserver = null;
      this._mutationTimer = 0;
      this._measureRaf = 0;
      this._lastWakeAt = 0;
      this._wakeInFlight = false;
      this._wakeRestoreTimer = 0;
      this._origParent = null;
      this._origNext = null;
    }
    isActive() {
      return this._active;
    }
    isPageMode() {
      return this._active && this._pageMode;
    }
    isPopoverMode() {
      return this._active && this._popoverMode;
    }
    getTarget() {
      return this._target;
    }
    /**
     * @param {Element|null|undefined} element
     * @returns {boolean} true if columns were applied
     */
    apply(element) {
      if (this._active) {
        this.clear();
      }
      let target = element;
      if (!target || !target.isConnected) return false;
      if (this._isKeyPilotNode(target)) return false;
      let pageMode = false;
      try {
        if (target === document.documentElement || target === document.body) {
          target = document.body;
          pageMode = true;
        }
      } catch {
        return false;
      }
      if (!target || !target.isConnected) return false;
      if (!pageMode) {
        try {
          if (!target.parentNode) return false;
        } catch {
          return false;
        }
      }
      this._snapshots = [];
      this._snapshot(target);
      if (pageMode) {
        try {
          this._snapshot(document.documentElement);
        } catch {
        }
      }
      this._target = target;
      this._pageMode = pageMode;
      this._popoverMode = false;
      this._slipOffset = 0;
      this._maxSlip = 0;
      try {
        if (pageMode) {
          this._buildPageShell();
        } else {
          this._wrapTargetInShell(target);
        }
      } catch (err) {
        console.warn("[KeyPilot] Column shell failed:", err);
        this.clear();
        return false;
      }
      this._applyMetrics();
      try {
        target.classList.add(CSS_CLASSES.COLS_ACTIVE);
        if (pageMode) {
          document.documentElement.classList.add(CSS_CLASSES.COLS_PAGE);
          document.body.classList.add(CSS_CLASSES.COLS_PAGE);
        }
      } catch {
        this.clear();
        return false;
      }
      this._active = true;
      this._bindResize();
      this._bindContentObserver();
      this._scheduleRemeasure();
      this._scheduleScrollShellIntoViewport();
      try {
        setTimeout(() => {
          if (this._active) this._maybeWakeInfiniteScrollLoaders({ force: true });
        }, 120);
      } catch {
      }
      return true;
    }
    clear() {
      this._unbindContentObserver();
      this._unbindResize();
      this._disposeResizeApi();
      this._cancelWakeLoaders();
      this._teardownSlipBar();
      if (this._target) {
        try {
          this._target.classList.remove(CSS_CLASSES.COLS_ACTIVE);
        } catch {
        }
      }
      try {
        document.documentElement.classList.remove(CSS_CLASSES.COLS_PAGE);
        document.body.classList.remove(CSS_CLASSES.COLS_PAGE);
      } catch {
      }
      this._unwrapShell();
      this._restoreSnapshots();
      try {
        document.documentElement.style.removeProperty("--kpv2-cols-width");
        document.documentElement.style.removeProperty("--kpv2-cols-gap");
        document.documentElement.style.removeProperty("--kpv2-cols-height");
        document.documentElement.style.removeProperty("--kpv2-cols-slip-reserve");
      } catch {
      }
      this._active = false;
      this._target = null;
      this._pageMode = false;
      this._popoverMode = false;
      this._slipOffset = 0;
      this._maxSlip = 0;
      this._snapshots = [];
      this._origParent = null;
      this._origNext = null;
      this._lastWakeAt = 0;
      this._wakeInFlight = false;
    }
    /**
     * @param {number} offsetPx
     */
    setSlipOffset(offsetPx) {
      if (!this._active || !this._target) return;
      const max = this._maxSlip > 0 ? this._maxSlip : 0;
      const next = Math.max(0, Math.min(max, Number(offsetPx) || 0));
      this._slipOffset = next;
      this._applySlipToTarget();
      this._updateKnobPosition();
      this._maybeWakeInfiniteScrollLoaders();
    }
    /**
     * Normalized 0..1 slip position.
     * @param {number} t
     */
    setSlipNormalized(t) {
      const max = this._maxSlip > 0 ? this._maxSlip : 0;
      const n = Math.max(0, Math.min(1, Number(t) || 0));
      this.setSlipOffset(n * max);
    }
    // ---------------------------------------------------------------------------
    // Internals
    // ---------------------------------------------------------------------------
    /**
     * @param {Element} el
     * @returns {boolean}
     */
    _isKeyPilotNode(el2) {
      try {
        if (!el2 || el2.nodeType !== 1) return true;
        const root = el2.getRootNode?.();
        if (typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot && root.host?.hasAttribute?.("data-kp-ui-shadow")) {
          return true;
        }
        if (el2.closest?.("[data-kp-control-strip], [data-kp-early-control-strip], [data-kp-early-floating-keyboard]")) {
          return true;
        }
        if (el2.closest?.(`.${CSS_CLASSES.COLS_SHELL}, .${CSS_CLASSES.COLS_SLIP_BAR}`)) {
          return true;
        }
        const cls = typeof el2.className === "string" ? el2.className : "";
        if (cls.includes("kpv2-") || cls.includes("kp-")) return true;
        let cur = el2.parentElement;
        let depth = 0;
        while (cur && depth < 6) {
          const c = typeof cur.className === "string" ? cur.className : "";
          if (c.includes("kpv2-cols-slip") || c.includes("kpv2-cols-shell") || c.includes("kp-control-strip") || c.includes("kp-floating-keyboard") || c.includes("kpv2-omnibox") || c.includes("kpv2-popup")) {
            return true;
          }
          cur = cur.parentElement;
          depth += 1;
        }
      } catch {
      }
      return false;
    }
    /**
     * @param {Element} el
     */
    _snapshot(el2) {
      if (!el2) return;
      let styleAttr = null;
      try {
        styleAttr = el2.getAttribute("style");
      } catch {
        styleAttr = null;
      }
      this._snapshots.push({ el: el2, styleAttr });
    }
    _restoreSnapshots() {
      for (const snap of this._snapshots) {
        try {
          if (!snap?.el) continue;
          if (snap.styleAttr == null || snap.styleAttr === "") {
            snap.el.removeAttribute("style");
          } else {
            snap.el.setAttribute("style", snap.styleAttr);
          }
        } catch {
        }
      }
      this._snapshots = [];
    }
    /**
     * Wrap a non-body target in the widget shell (content + slip chrome).
     * @param {Element} target
     */
    _wrapTargetInShell(target) {
      const parent = target.parentNode;
      if (!parent) throw new Error("no parent");
      this._origParent = parent;
      this._origNext = target.nextSibling;
      const shell = document.createElement("div");
      shell.className = CSS_CLASSES.COLS_SHELL;
      shell.setAttribute("data-kp-cols-shell", "true");
      shell.setAttribute("role", "region");
      shell.setAttribute("aria-label", "Column layout");
      const body = document.createElement("div");
      body.className = CSS_CLASSES.COLS_BODY;
      body.setAttribute("data-kp-cols-body", "true");
      let contentHeight = 0;
      try {
        contentHeight = target.getBoundingClientRect().height || 0;
      } catch {
        contentHeight = 0;
      }
      this._applyShellChrome(shell, body, { pageMode: false, contentHeight });
      parent.insertBefore(shell, target);
      body.appendChild(target);
      shell.appendChild(body);
      this._shell = shell;
      this._body = body;
      this._buildSlipBar(shell);
      this._attachResize(shell);
    }
    /**
     * Page mode: body is the column target; shell is a fixed viewport frame that
     * holds the slip bar as bottom chrome (content is not reparented).
     */
    _buildPageShell() {
      const shell = document.createElement("div");
      shell.className = `${CSS_CLASSES.COLS_SHELL} ${CSS_CLASSES.COLS_SHELL}--page`;
      shell.setAttribute("data-kp-cols-shell", "true");
      shell.setAttribute("data-kp-cols-page-frame", "true");
      shell.setAttribute("role", "region");
      shell.setAttribute("aria-label", "Page column layout");
      Object.assign(shell.style, {
        position: "fixed",
        left: "12px",
        right: "12px",
        bottom: "10px",
        top: "auto",
        height: "auto",
        zIndex: String(Z_INDEX.COLS_SLIP_BAR),
        display: "flex",
        flexDirection: "column",
        boxSizing: "border-box",
        padding: "0",
        borderRadius: "8px",
        border: `2px solid ${COLORS.COLS_PURPLE}`,
        background: "linear-gradient(180deg, rgba(28, 18, 36, 0.92) 0%, rgba(18, 12, 24, 0.94) 100%)",
        boxShadow: `0 8px 28px ${COLORS.COLS_SHADOW}, 0 0 0 1px ${COLORS.COLS_SHADOW_BRIGHT}`,
        fontFamily: KP_UI_FONT,
        overflow: "hidden",
        pointerEvents: "auto",
        backdropFilter: "blur(10px)",
        WebkitBackdropFilter: "blur(10px)"
      });
      try {
        const slipH = this._slipBarHeightPx();
        const bs = document.body.style;
        bs.setProperty("outline", `2px solid ${COLORS.COLS_PURPLE}`, "important");
        bs.setProperty("outline-offset", "-2px", "important");
        bs.setProperty("border-radius", "6px", "important");
        bs.setProperty("box-shadow", `inset 0 0 0 1px ${COLORS.COLS_SHADOW_BRIGHT}`, "important");
        bs.setProperty("padding-bottom", `${slipH + 20}px`, "important");
        bs.setProperty("box-sizing", "border-box", "important");
      } catch {
      }
      this._shell = shell;
      this._body = null;
      this._buildSlipBar(shell);
      try {
        document.documentElement.appendChild(shell);
      } catch {
        try {
          document.body.appendChild(shell);
        } catch {
        }
      }
    }
    /**
     * Max shell height in CSS px — never taller than the visual viewport.
     * Leaves a small margin so the widget doesn't kiss browser chrome.
     * @returns {number}
     */
    _maxShellHeightPx() {
      const vv = window.visualViewport;
      const vh = Math.max(200, Math.floor(vv && vv.height || window.innerHeight || 768));
      return Math.max(180, vh - 24);
    }
    /**
     * @param {HTMLElement} shell
     * @param {HTMLElement} body
     * @param {{ pageMode?: boolean, contentHeight?: number }} [opts]
     */
    _applyShellChrome(shell, body, opts = {}) {
      const vv = window.visualViewport;
      const vh = Math.max(200, Math.floor(vv && vv.height || window.innerHeight || 768));
      const slipH = this._slipBarHeightPx();
      const maxShellH = this._maxShellHeightPx();
      const contentH = Math.max(120, Math.floor(Number(opts.contentHeight) || vh * 0.45));
      const shellH = Math.min(maxShellH, contentH + SHELL_PAD_PX + slipH + 8);
      Object.assign(shell.style, {
        position: "relative",
        display: "flex",
        flexDirection: "column",
        width: "100%",
        maxWidth: "100%",
        maxHeight: `${maxShellH}px`,
        height: `${shellH}px`,
        boxSizing: "border-box",
        padding: `${SHELL_PAD_PX}px`,
        paddingBottom: "0",
        margin: "8px 0",
        borderRadius: "8px",
        border: `2px solid ${COLORS.COLS_PURPLE}`,
        background: "linear-gradient(180deg, rgba(28, 18, 36, 0.14) 0%, rgba(18, 12, 24, 0.10) 100%)",
        boxShadow: `0 8px 28px ${COLORS.COLS_SHADOW}, 0 0 0 1px ${COLORS.COLS_SHADOW_BRIGHT}`,
        fontFamily: KP_UI_FONT,
        overflow: "hidden",
        zIndex: "auto",
        pointerEvents: "auto"
      });
      Object.assign(body.style, {
        flex: "1 1 auto",
        minHeight: "0",
        minWidth: "0",
        width: "100%",
        overflow: "hidden",
        boxSizing: "border-box",
        position: "relative",
        // Inner pad so content doesn't hug the outline
        padding: "4px",
        borderRadius: "4px"
      });
    }
    /**
     * Re-assert shell height ≤ viewport after window resize / user drag.
     */
    _clampShellToViewport() {
      if (!this._shell || this._pageMode) return;
      const maxShellH = this._maxShellHeightPx();
      try {
        const s = this._shell.style;
        s.maxHeight = `${maxShellH}px`;
        const rect = this._shell.getBoundingClientRect();
        if (rect.height > maxShellH + 1) {
          s.height = `${maxShellH}px`;
        }
      } catch {
      }
    }
    /**
     * After Cols Toggle: if the shell is at maxHeight (fills the viewport band)
     * or its vertical extent is clipped by the viewport, scroll the document so
     * the top of the widget sits at the top of the viewport.
     *
     * Skipped for page mode / floating popover (those are fixed to the viewport).
     */
    _scrollShellIntoViewportIfNeeded() {
      if (!this._active || !this._shell || this._pageMode || this._popoverMode) return;
      try {
        if (!this._shell.isConnected) return;
        const rect = this._shell.getBoundingClientRect();
        if (!(rect.width > 0 && rect.height > 0)) return;
        const vv = window.visualViewport;
        const vh = Math.max(1, Math.floor(vv && vv.height || window.innerHeight || 0));
        const maxShellH = this._maxShellHeightPx();
        const atMaxHeight = rect.height >= maxShellH - 2;
        const outOfViewport = rect.top < -1 || rect.bottom > vh + 1 || rect.bottom < 0 || rect.top > vh;
        if (!atMaxHeight && !outOfViewport) return;
        const pageY = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
        const vvOffsetTop = vv && typeof vv.offsetTop === "number" ? vv.offsetTop : 0;
        const targetTop = Math.max(0, Math.round(pageY + rect.top - vvOffsetTop));
        try {
          window.scrollTo({ top: targetTop, left: window.pageXOffset || 0, behavior: "auto" });
        } catch {
          window.scrollTo(window.pageXOffset || 0, targetTop);
        }
      } catch {
      }
    }
    /**
     * Wait one or two frames so shell metrics/layout settle before scrolling.
     */
    _scheduleScrollShellIntoViewport() {
      try {
        requestAnimationFrame(() => {
          requestAnimationFrame(() => {
            this._scrollShellIntoViewportIfNeeded();
          });
        });
      } catch {
        this._scrollShellIntoViewportIfNeeded();
      }
    }
    _applyMetrics() {
      const slipReserve = this._slipBarHeightPx();
      const vv = window.visualViewport;
      const vw = Math.max(320, Math.floor(vv && vv.width || window.innerWidth || 1024));
      const vh = Math.max(200, Math.floor(vv && vv.height || window.innerHeight || 768));
      const maxFrameH = Math.max(80, vh - 8);
      this._clampShellToViewport();
      let availableW = Math.max(200, vw - 16);
      let frameH = Math.max(120, Math.min(maxFrameH, vh - slipReserve - 24));
      if (this._shell && !this._pageMode) {
        try {
          const shellRect = this._shell.getBoundingClientRect();
          if (shellRect.width > 40) {
            availableW = Math.max(160, Math.floor(shellRect.width - SHELL_PAD_PX * 2 - 8));
          }
          if (this._body) {
            const bodyRect = this._body.getBoundingClientRect();
            if (bodyRect.height > 40) {
              frameH = Math.max(80, Math.floor(bodyRect.height));
            } else if (shellRect.height > 40) {
              frameH = Math.max(80, Math.floor(shellRect.height - slipReserve - SHELL_PAD_PX));
            }
          }
        } catch {
        }
      } else if (this._pageMode) {
        frameH = Math.max(120, Math.min(maxFrameH, vh - slipReserve - 24));
        availableW = Math.max(200, vw - 32);
      }
      frameH = Math.min(frameH, maxFrameH);
      let colW = COL_WIDTH_IDEAL_PX;
      const nAtIdeal = Math.max(1, Math.floor((availableW + COL_GAP_PX) / (COL_WIDTH_IDEAL_PX + COL_GAP_PX)));
      if (nAtIdeal <= 1) {
        colW = Math.min(COL_WIDTH_MAX_PX, Math.max(COL_WIDTH_MIN_PX, availableW));
      } else {
        const totalGaps = (nAtIdeal - 1) * COL_GAP_PX;
        colW = Math.min(
          COL_WIDTH_MAX_PX,
          Math.max(COL_WIDTH_MIN_PX, Math.floor((availableW - totalGaps) / nAtIdeal))
        );
      }
      try {
        const root = document.documentElement;
        root.style.setProperty("--kpv2-cols-width", `${colW}px`);
        root.style.setProperty("--kpv2-cols-gap", `${COL_GAP_PX}px`);
        root.style.setProperty("--kpv2-cols-height", `${frameH}px`);
        root.style.setProperty("--kpv2-cols-slip-reserve", `${slipReserve}px`);
      } catch {
      }
      const t = this._target;
      if (!t) return;
      try {
        const s = (
          /** @type {HTMLElement} */
          t.style
        );
        s.setProperty("column-width", `${colW}px`, "important");
        s.setProperty("column-gap", `${COL_GAP_PX}px`, "important");
        s.setProperty("column-fill", "auto", "important");
        s.setProperty("height", `${frameH}px`, "important");
        s.setProperty("max-height", `${frameH}px`, "important");
        s.setProperty("overflow-x", "auto", "important");
        s.setProperty("overflow-y", "hidden", "important");
        s.setProperty("box-sizing", "border-box", "important");
        s.setProperty("width", "100%", "important");
        s.setProperty("max-width", "100%", "important");
        s.setProperty("scrollbar-width", "none", "important");
        try {
          s.setProperty("msOverflowStyle", "none", "important");
        } catch {
        }
        if (this._pageMode) {
          s.setProperty("margin", "0", "important");
          s.setProperty("padding-bottom", "0", "important");
          try {
            const hs = document.documentElement.style;
            hs.setProperty("overflow", "hidden", "important");
            hs.setProperty("height", `${vh}px`, "important");
            hs.setProperty("max-height", `${vh}px`, "important");
          } catch {
          }
        }
      } catch {
      }
    }
    _slipBarHeightPx() {
      try {
        if (this._slipBar) {
          const h = this._slipBar.getBoundingClientRect().height;
          if (h > 0) return Math.ceil(h);
        }
      } catch {
      }
      return SLIP_BAR_HEIGHT_PX_FALLBACK;
    }
    _applySlipToTarget() {
      if (!this._target) return;
      try {
        this._target.scrollLeft = this._slipOffset;
      } catch {
      }
    }
    _scheduleRemeasure() {
      if (this._measureRaf) {
        try {
          cancelAnimationFrame(this._measureRaf);
        } catch {
        }
      }
      this._measureRaf = requestAnimationFrame(() => {
        this._measureRaf = 0;
        this._remeasureSlip();
      });
    }
    _remeasureSlip() {
      if (!this._active || !this._target) return;
      try {
        const el2 = (
          /** @type {HTMLElement} */
          this._target
        );
        const prevMax = this._maxSlip;
        const max = Math.max(0, (el2.scrollWidth || 0) - (el2.clientWidth || 0));
        this._maxSlip = max;
        if (this._slipOffset > max) {
          this._slipOffset = max;
          this._applySlipToTarget();
        } else {
          this._applySlipToTarget();
        }
        this._updateKnobPosition();
        if (this._slipBar) {
          this._slipBar.setAttribute("data-kp-slip-empty", max <= 0 ? "1" : "0");
          try {
            this._slipBar.setAttribute(
              "data-kp-slip-growing",
              max > prevMax + 8 ? "1" : "0"
            );
          } catch {
          }
        }
        if (max > prevMax + 8 && this._isNearSlipEnd()) {
          this._maybeWakeInfiniteScrollLoaders({ force: false });
        }
      } catch {
      }
    }
    /**
     * Watch target DOM growth (infinite-scroll appends, lazy sections).
     * ResizeObserver only sees border-box changes; scrollWidth growth needs this.
     */
    _bindContentObserver() {
      this._unbindContentObserver();
      if (!this._target || typeof MutationObserver === "undefined") return;
      try {
        this._mutationObserver = new MutationObserver((records) => {
          if (!this._active) return;
          let meaningful = false;
          for (const rec of records) {
            if (rec.type === "childList" && (rec.addedNodes && rec.addedNodes.length || rec.removedNodes && rec.removedNodes.length)) {
              meaningful = true;
              break;
            }
            if (rec.type === "characterData") {
              meaningful = true;
              break;
            }
          }
          if (!meaningful) return;
          if (this._mutationTimer) {
            try {
              clearTimeout(this._mutationTimer);
            } catch {
            }
          }
          this._mutationTimer = setTimeout(() => {
            this._mutationTimer = 0;
            if (!this._active) return;
            this._applyMetrics();
            this._scheduleRemeasure();
          }, MUTATION_REMEASURE_MS);
        });
        this._mutationObserver.observe(this._target, {
          childList: true,
          subtree: true,
          characterData: true
        });
      } catch {
        this._mutationObserver = null;
      }
    }
    _unbindContentObserver() {
      if (this._mutationTimer) {
        try {
          clearTimeout(this._mutationTimer);
        } catch {
        }
        this._mutationTimer = 0;
      }
      if (this._mutationObserver) {
        try {
          this._mutationObserver.disconnect();
        } catch {
        }
        this._mutationObserver = null;
      }
    }
    /**
     * @returns {boolean}
     */
    _isNearSlipEnd() {
      const max = this._maxSlip;
      if (!(max > 0)) return true;
      const offset = this._slipOffset;
      if (offset >= Math.max(0, max - SLIP_END_PX)) return true;
      if (offset / max >= SLIP_END_RATIO) return true;
      return false;
    }
    /**
     * When the user slips near the end of columnized content, nudge the page so
     * infinite-scroll / IntersectionObserver loaders can fetch more. New nodes
     * are picked up by MutationObserver and extend the slip range.
     *
     * @param {{ force?: boolean }} [opts]
     */
    _maybeWakeInfiniteScrollLoaders(opts = {}) {
      if (!this._active || !this._target) return;
      if (!opts.force && !this._isNearSlipEnd()) return;
      const now = Date.now();
      if (!opts.force && now - this._lastWakeAt < WAKE_THROTTLE_MS) return;
      if (this._wakeInFlight) return;
      this._lastWakeAt = now;
      this._wakeInFlight = true;
      try {
        this._wakeInfiniteScrollLoaders();
      } catch (err) {
        if (window.KEYPILOT_DEBUG) {
          console.warn("[KeyPilot] Cols progressive wake failed:", err);
        }
      } finally {
        try {
          if (this._wakeRestoreTimer) clearTimeout(this._wakeRestoreTimer);
        } catch {
        }
        this._wakeRestoreTimer = setTimeout(() => {
          this._wakeRestoreTimer = 0;
          this._wakeInFlight = false;
          if (this._active) this._scheduleRemeasure();
        }, 320);
      }
    }
    /**
     * Best-effort: fire scroll listeners + bring tail content toward the
     * viewport so IntersectionObserver sentinels can activate.
     */
    _wakeInfiniteScrollLoaders() {
      const target = (
        /** @type {HTMLElement|null} */
        this._target
      );
      if (!target) return;
      const savedSlip = this._slipOffset;
      const savedPageX = window.pageXOffset || 0;
      const savedPageY = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
      try {
        target.scrollLeft = savedSlip;
      } catch {
      }
      try {
        window.dispatchEvent(new Event("scroll", { bubbles: false }));
        document.dispatchEvent(new Event("scroll", { bubbles: true }));
        target.dispatchEvent(new Event("scroll", { bubbles: false }));
      } catch {
      }
      const html = document.documentElement;
      let unlockedPage = false;
      let prevHtmlOverflow = null;
      let prevHtmlHeight = null;
      try {
        if (this._pageMode) {
          prevHtmlOverflow = html.style.getPropertyValue("overflow") || null;
          prevHtmlHeight = html.style.getPropertyValue("height") || null;
          html.style.setProperty("overflow", "auto", "important");
          html.style.removeProperty("height");
          html.style.removeProperty("max-height");
          unlockedPage = true;
        }
        const maxY = Math.max(
          0,
          Math.max(
            document.documentElement.scrollHeight || 0,
            document.body?.scrollHeight || 0
          ) - (window.innerHeight || 0)
        );
        let nudgeY = savedPageY + 48;
        if (nudgeY > maxY) nudgeY = Math.max(0, maxY);
        if (Math.abs(nudgeY - savedPageY) < 1 && maxY > 0) {
          nudgeY = Math.max(0, savedPageY - 1);
        }
        try {
          window.scrollTo({ top: nudgeY, left: savedPageX, behavior: "auto" });
        } catch {
          window.scrollTo(savedPageX, nudgeY);
        }
        try {
          window.dispatchEvent(new Event("scroll", { bubbles: false }));
        } catch {
        }
      } catch {
      }
      const tail = this._findTailSentinel(target);
      if (tail) {
        try {
          tail.scrollIntoView({ block: "nearest", inline: "nearest", behavior: "auto" });
        } catch {
          try {
            tail.scrollIntoView(false);
          } catch {
          }
        }
      }
      const restore = () => {
        try {
          if (unlockedPage) {
            if (prevHtmlOverflow) {
              html.style.setProperty("overflow", prevHtmlOverflow, "important");
            } else {
              html.style.setProperty("overflow", "hidden", "important");
            }
            if (prevHtmlHeight) {
              html.style.setProperty("height", prevHtmlHeight, "important");
              html.style.setProperty("max-height", prevHtmlHeight, "important");
            } else {
              this._applyMetrics();
            }
          }
          try {
            window.scrollTo({ top: savedPageY, left: savedPageX, behavior: "auto" });
          } catch {
            window.scrollTo(savedPageX, savedPageY);
          }
          this._slipOffset = savedSlip;
          this._applySlipToTarget();
          this._scheduleRemeasure();
        } catch {
        }
      };
      try {
        requestAnimationFrame(() => {
          requestAnimationFrame(restore);
        });
      } catch {
        setTimeout(restore, 32);
      }
      try {
        setTimeout(() => {
          if (this._active) this._scheduleRemeasure();
        }, 200);
        setTimeout(() => {
          if (this._active) this._scheduleRemeasure();
        }, 600);
      } catch {
      }
    }
    _cancelWakeLoaders() {
      this._wakeInFlight = false;
      if (this._wakeRestoreTimer) {
        try {
          clearTimeout(this._wakeRestoreTimer);
        } catch {
        }
        this._wakeRestoreTimer = 0;
      }
    }
    /**
     * Prefer a substantial element near the end of the columnized tree
     * (infinite-scroll sentinels often sit after the last card).
     * @param {Element} root
     * @returns {Element|null}
     */
    _findTailSentinel(root) {
      if (!root) return null;
      try {
        const nodes = root.querySelectorAll("*");
        const start = Math.max(0, nodes.length - 60);
        for (let i = nodes.length - 1; i >= start; i--) {
          const n = nodes[i];
          if (!n || n.nodeType !== 1) continue;
          if (this._isKeyPilotNode(n)) continue;
          try {
            const r = n.getBoundingClientRect();
            if (r.width >= 0 && r.height >= 0) {
              if (r.width > 1 || r.height > 1 || i > nodes.length - 8) {
                return n;
              }
            }
          } catch {
          }
        }
        return root.lastElementChild || root;
      } catch {
        return root.lastElementChild || root;
      }
    }
    _bindResize() {
      this._unbindResize();
      this._onResize = () => {
        if (!this._active) return;
        this._applyMetrics();
        this._scheduleRemeasure();
      };
      try {
        window.addEventListener("resize", this._onResize, { passive: true });
        if (window.visualViewport) {
          window.visualViewport.addEventListener("resize", this._onResize, { passive: true });
        }
      } catch {
      }
      try {
        if (typeof ResizeObserver !== "undefined") {
          this._resizeObserver = new ResizeObserver(() => {
            this._applyMetrics();
            this._scheduleRemeasure();
          });
          if (this._shell) this._resizeObserver.observe(this._shell);
          if (this._body) this._resizeObserver.observe(this._body);
          if (this._target) this._resizeObserver.observe(this._target);
        }
      } catch {
        this._resizeObserver = null;
      }
    }
    _unbindResize() {
      if (this._onResize) {
        try {
          window.removeEventListener("resize", this._onResize);
        } catch {
        }
        try {
          if (window.visualViewport) {
            window.visualViewport.removeEventListener("resize", this._onResize);
          }
        } catch {
        }
        this._onResize = null;
      }
      if (this._resizeObserver) {
        try {
          this._resizeObserver.disconnect();
        } catch {
        }
        this._resizeObserver = null;
      }
      if (this._measureRaf) {
        try {
          cancelAnimationFrame(this._measureRaf);
        } catch {
        }
        this._measureRaf = 0;
      }
    }
    /**
     * @param {HTMLElement} shell
     */
    _attachResize(shell) {
      this._disposeResizeApi();
      try {
        this._resizeApi = makePopoverResizable(shell, {
          minWidth: 280,
          minHeight: 180,
          margin: 8,
          zIndex: 30,
          onResize: () => {
            this._clampShellToViewport();
            this._applyMetrics();
            this._scheduleRemeasure();
          },
          onResizeEnd: () => {
            this._clampShellToViewport();
            this._applyMetrics();
            this._scheduleRemeasure();
          }
        });
        try {
          shell.style.maxHeight = `${this._maxShellHeightPx()}px`;
        } catch {
        }
      } catch {
        this._resizeApi = null;
      }
    }
    _disposeResizeApi() {
      if (this._resizeApi) {
        try {
          this._resizeApi.dispose();
        } catch {
        }
        this._resizeApi = null;
      }
      if (this._shell) {
        try {
          if (this._shell.dataset) delete this._shell.dataset.kpResizable;
        } catch {
        }
      }
    }
    /**
     * @param {HTMLElement} parent
     */
    _buildSlipBar(parent) {
      this._teardownSlipBar();
      const bar = document.createElement("div");
      bar.className = CSS_CLASSES.COLS_SLIP_BAR;
      bar.setAttribute("data-kp-cols-slip", "true");
      bar.setAttribute("role", "group");
      bar.setAttribute("aria-label", "Column slip controls");
      const barMount = ensureOpenChromeShadow(bar, { id: "columns-slip-controls" }) || bar;
      Object.assign(bar.style, {
        position: "relative",
        left: "auto",
        right: "auto",
        bottom: "auto",
        // Bleed to shell edges (cancel shell horizontal padding when present)
        marginLeft: this._pageMode ? "0" : `-${SHELL_PAD_PX}px`,
        marginRight: this._pageMode ? "0" : `-${SHELL_PAD_PX}px`,
        width: this._pageMode ? "100%" : `calc(100% + ${SHELL_PAD_PX * 2}px)`,
        maxWidth: "none",
        flex: "0 0 auto",
        height: SLIP_BAR_HEIGHT,
        minHeight: SLIP_BAR_HEIGHT,
        zIndex: "2",
        display: "flex",
        alignItems: "center",
        gap: "8px",
        padding: "0 10px",
        boxSizing: "border-box",
        borderRadius: "0 0 6px 6px",
        borderTop: `1px solid ${COLORS.COLS_SHADOW_BRIGHT}`,
        background: "linear-gradient(180deg, rgba(40, 24, 52, 0.96) 0%, rgba(24, 14, 34, 0.98) 100%)",
        boxShadow: "inset 0 1px 0 rgba(255,255,255,0.06)",
        fontFamily: KP_UI_FONT,
        color: "rgba(255,255,255,0.9)",
        userSelect: "none",
        pointerEvents: "auto",
        backdropFilter: "none",
        WebkitBackdropFilter: "none"
      });
      const label = document.createElement("span");
      label.className = CSS_CLASSES.COLS_SLIP_LABEL;
      label.textContent = "Slip";
      Object.assign(label.style, {
        fontSize: "11px",
        fontWeight: "600",
        letterSpacing: "0.04em",
        textTransform: "uppercase",
        opacity: "0.85",
        flex: "0 0 auto",
        color: COLORS.COLS_PURPLE_BRIGHT
      });
      const track = document.createElement("div");
      track.className = CSS_CLASSES.COLS_SLIP_TRACK;
      track.setAttribute("role", "slider");
      track.setAttribute("aria-label", "Slip edit \u2014 shift column content");
      track.setAttribute("aria-valuemin", "0");
      track.setAttribute("aria-valuemax", "100");
      track.setAttribute("aria-valuenow", "0");
      track.tabIndex = 0;
      Object.assign(track.style, {
        position: "relative",
        flex: "1 1 auto",
        minWidth: "48px",
        height: "10px",
        borderRadius: "2px",
        background: "rgba(255,255,255,0.12)",
        border: "1px solid rgba(255,255,255,0.14)",
        cursor: "pointer"
      });
      const knob = document.createElement("div");
      knob.className = CSS_CLASSES.COLS_SLIP_KNOB;
      Object.assign(knob.style, {
        position: "absolute",
        top: "50%",
        left: "0%",
        width: "28px",
        height: "16px",
        marginTop: "-8px",
        marginLeft: "-14px",
        borderRadius: "2px",
        background: COLORS.COLS_PURPLE_BRIGHT,
        border: "1px solid rgba(255,255,255,0.35)",
        boxShadow: `0 1px 4px ${COLORS.COLS_SHADOW_BRIGHT}`,
        cursor: "grab",
        boxSizing: "border-box",
        touchAction: "none"
      });
      track.appendChild(knob);
      const expandBtn = this._createChromeButton({
        className: CSS_CLASSES.COLS_EXPAND_BTN,
        title: "Open columns in popover",
        ariaLabel: "Open columns in full-size popover",
        onClick: () => this._promoteToPopover()
      });
      expandBtn.appendChild(this._createViewportIcon());
      const closeBtn = this._createChromeButton({
        className: CSS_CLASSES.COLS_CLOSE_BTN,
        title: "Close columns (restore element)",
        ariaLabel: "Close columns and restore element",
        onClick: () => this.clear(),
        label: "\xD7",
        semiTransparent: true
      });
      barMount.appendChild(label);
      barMount.appendChild(track);
      barMount.appendChild(expandBtn);
      barMount.appendChild(closeBtn);
      const onPointerDown = (e) => {
        if (e.button != null && e.button !== 0) return;
        e.preventDefault();
        e.stopPropagation();
        this._dragging = true;
        try {
          knob.style.cursor = "grabbing";
        } catch {
        }
        try {
          track.setPointerCapture?.(e.pointerId);
        } catch {
        }
        this._slipFromClientX(e.clientX);
      };
      this._onPointerMove = (e) => {
        if (!this._dragging) return;
        e.preventDefault();
        this._slipFromClientX(e.clientX);
      };
      this._onPointerUp = (e) => {
        if (!this._dragging) return;
        this._dragging = false;
        try {
          knob.style.cursor = "grab";
        } catch {
        }
        try {
          track.releasePointerCapture?.(e.pointerId);
        } catch {
        }
      };
      track.addEventListener("pointerdown", onPointerDown);
      knob.addEventListener("pointerdown", onPointerDown);
      window.addEventListener("pointermove", this._onPointerMove, { passive: false });
      window.addEventListener("pointerup", this._onPointerUp, { passive: true });
      window.addEventListener("pointercancel", this._onPointerUp, { passive: true });
      bar._kpOnPointerDown = onPointerDown;
      parent.appendChild(bar);
      this._slipBar = bar;
      this._track = track;
      this._knob = knob;
      this._expandBtn = expandBtn;
      this._closeBtn = closeBtn;
      this._updateKnobPosition();
    }
    /**
     * @param {{
     *   className: string,
     *   title: string,
     *   ariaLabel: string,
     *   onClick: () => void,
     *   label?: string,
     *   semiTransparent?: boolean
     * }} opts
     * @returns {HTMLButtonElement}
     */
    _createChromeButton(opts) {
      const btn = document.createElement("button");
      btn.type = "button";
      btn.className = opts.className;
      btn.title = opts.title;
      btn.setAttribute("aria-label", opts.ariaLabel);
      Object.assign(btn.style, {
        margin: "0",
        appearance: "none",
        WebkitAppearance: "none",
        display: "inline-flex",
        alignItems: "center",
        justifyContent: "center",
        boxSizing: "border-box",
        flex: "0 0 auto",
        width: "26px",
        height: "22px",
        padding: "0",
        borderRadius: "4px",
        border: `1px solid ${COLORS.COLS_PURPLE_BRIGHT}`,
        background: opts.semiTransparent ? "rgba(156, 39, 176, 0.28)" : "rgba(156, 39, 176, 0.45)",
        color: "rgba(255,255,255,0.92)",
        fontFamily: KP_UI_FONT,
        fontSize: opts.label ? "16px" : "12px",
        fontWeight: "500",
        lineHeight: "1",
        cursor: "pointer",
        opacity: opts.semiTransparent ? "0.72" : "0.92",
        boxShadow: `0 1px 3px ${COLORS.COLS_SHADOW}`,
        transition: "opacity 120ms ease, background 120ms ease"
      });
      if (opts.label) btn.textContent = opts.label;
      btn.addEventListener("mouseenter", () => {
        btn.style.opacity = "1";
        btn.style.background = "rgba(156, 39, 176, 0.55)";
      });
      btn.addEventListener("mouseleave", () => {
        btn.style.opacity = opts.semiTransparent ? "0.72" : "0.92";
        btn.style.background = opts.semiTransparent ? "rgba(156, 39, 176, 0.28)" : "rgba(156, 39, 176, 0.45)";
      });
      btn.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        try {
          opts.onClick();
        } catch (err) {
          console.warn("[KeyPilot] Cols chrome action failed:", err);
        }
      });
      return btn;
    }
    /**
     * Full-size / expand-to-viewport icon (corner brackets).
     * @returns {SVGSVGElement}
     */
    _createViewportIcon() {
      const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
      svg.setAttribute("viewBox", "0 0 16 16");
      svg.setAttribute("width", "14");
      svg.setAttribute("height", "14");
      svg.setAttribute("aria-hidden", "true");
      svg.style.cssText = "display:block; pointer-events:none;";
      const paths = [
        // frame
        "M3 5 V3 H5",
        "M11 3 H13 V5",
        "M13 11 V13 H11",
        "M5 13 H3 V11",
        // inner diamond-ish rectangle
        "M5 5 H11 V11 H5 Z"
      ];
      for (const d of paths) {
        const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
        path.setAttribute("d", d);
        path.setAttribute("fill", "none");
        path.setAttribute("stroke", "currentColor");
        path.setAttribute("stroke-width", "1.4");
        path.setAttribute("stroke-linecap", "round");
        path.setAttribute("stroke-linejoin", "round");
        svg.appendChild(path);
      }
      return svg;
    }
    /**
     * Promote the column widget into a floating popover (Link Preview–style shell).
     */
    _promoteToPopover() {
      if (!this._active || !this._shell) return;
      if (this._pageMode) {
        this._popoverMode = true;
        try {
          Object.assign(this._shell.style, {
            left: "10vw",
            right: "10vw",
            bottom: "8vh",
            width: "auto",
            maxWidth: "none",
            zIndex: String(Z_INDEX.POPOVER_IFRAME_MODAL)
          });
        } catch {
        }
        this._applyMetrics();
        this._scheduleRemeasure();
        return;
      }
      if (this._popoverMode) {
        this._sizeShellAsPopover({ large: true });
        this._applyMetrics();
        this._scheduleRemeasure();
        return;
      }
      const shell = this._shell;
      let rect;
      try {
        rect = shell.getBoundingClientRect();
      } catch {
        rect = { width: 480, height: 360, left: 40, top: 40 };
      }
      try {
        if (!this._placeholder && shell.parentNode) {
          const ph = document.createElement("div");
          ph.className = CSS_CLASSES.COLS_PLACEHOLDER;
          ph.setAttribute("data-kp-cols-placeholder", "true");
          ph.setAttribute("aria-hidden", "true");
          Object.assign(ph.style, {
            width: `${Math.max(1, Math.floor(rect.width))}px`,
            height: `${Math.max(1, Math.floor(rect.height))}px`,
            margin: shell.style.margin || "8px 0",
            boxSizing: "border-box",
            pointerEvents: "none",
            visibility: "hidden"
          });
          shell.parentNode.insertBefore(ph, shell);
          this._placeholder = ph;
        }
      } catch {
      }
      try {
        document.documentElement.appendChild(shell);
      } catch {
        try {
          document.body.appendChild(shell);
        } catch {
        }
      }
      this._popoverMode = true;
      this._sizeShellAsPopover({ large: false, fromRect: rect });
      try {
        Object.assign(shell.style, {
          background: "linear-gradient(180deg, rgb(22, 16, 28) 0%, rgb(14, 10, 18) 100%)",
          border: `1px solid ${COLORS.COLS_PURPLE}`,
          boxShadow: `0 12px 40px rgba(0,0,0,0.65), 0 0 0 1px ${COLORS.COLS_SHADOW_BRIGHT}`,
          zIndex: String(Z_INDEX.POPOVER_IFRAME_MODAL)
        });
      } catch {
      }
      this._attachResize(shell);
      this._applyMetrics();
      this._scheduleRemeasure();
    }
    /**
     * @param {{ large?: boolean, fromRect?: { width: number, height: number, left?: number, top?: number } }} [opts]
     */
    _sizeShellAsPopover(opts = {}) {
      const shell = this._shell;
      if (!shell) return;
      const margin = 16;
      const vw = window.innerWidth || 1024;
      const vh = window.innerHeight || 768;
      const large = !!opts.large;
      const w = large ? Math.max(320, vw - margin * 2) : Math.min(Math.max(420, opts.fromRect?.width || 640), Math.floor(vw * 0.8));
      const h = large ? Math.max(240, vh - margin * 2) : Math.min(Math.max(280, opts.fromRect?.height || 480), Math.floor(vh * 0.8));
      const left = Math.max(margin, Math.floor((vw - w) / 2));
      const top = Math.max(margin, Math.floor((vh - h) / 2));
      const maxShellH = this._maxShellHeightPx();
      Object.assign(shell.style, {
        position: "fixed",
        left: `${left}px`,
        top: `${top}px`,
        right: "auto",
        bottom: "auto",
        width: `${w}px`,
        height: `${Math.min(h, maxShellH)}px`,
        maxWidth: `${Math.max(280, vw - margin * 2)}px`,
        maxHeight: `${maxShellH}px`,
        margin: "0",
        transform: "none",
        inset: "auto"
      });
    }
    /**
     * @param {number} clientX
     */
    _slipFromClientX(clientX) {
      if (!this._track) return;
      try {
        const rect = this._track.getBoundingClientRect();
        if (rect.width <= 0) return;
        const t = (clientX - rect.left) / rect.width;
        this.setSlipNormalized(t);
      } catch {
      }
    }
    _updateKnobPosition() {
      if (!this._knob || !this._slipBar) return;
      const max = this._maxSlip > 0 ? this._maxSlip : 0;
      const t = max > 0 ? this._slipOffset / max : 0;
      const pct = Math.max(0, Math.min(1, t)) * 100;
      try {
        this._knob.style.left = `${pct}%`;
        this._track?.setAttribute("aria-valuenow", String(Math.round(pct)));
        this._slipBar.setAttribute("aria-valuenow", String(Math.round(pct)));
      } catch {
      }
    }
    _teardownSlipBar() {
      this._dragging = false;
      if (this._onPointerMove) {
        try {
          window.removeEventListener("pointermove", this._onPointerMove);
        } catch {
        }
        this._onPointerMove = null;
      }
      if (this._onPointerUp) {
        try {
          window.removeEventListener("pointerup", this._onPointerUp);
        } catch {
        }
        try {
          window.removeEventListener("pointercancel", this._onPointerUp);
        } catch {
        }
        this._onPointerUp = null;
      }
      if (this._slipBar) {
        try {
          const down = this._slipBar._kpOnPointerDown;
          if (down && this._track) {
            this._track.removeEventListener("pointerdown", down);
            this._knob?.removeEventListener("pointerdown", down);
          }
        } catch {
        }
        try {
          this._slipBar.remove();
        } catch {
        }
      }
      this._slipBar = null;
      this._track = null;
      this._knob = null;
      this._expandBtn = null;
      this._closeBtn = null;
    }
    /**
     * Restore target to original parent and remove shell / placeholder.
     */
    _unwrapShell() {
      const target = this._target;
      const shell = this._shell;
      const placeholder = this._placeholder;
      if (target && shell && !this._pageMode) {
        try {
          const body = this._body;
          let parent = this._origParent;
          let next = this._origNext;
          if (!parent || !parent.isConnected) {
            if (placeholder && placeholder.parentNode) {
              parent = placeholder.parentNode;
              next = placeholder;
            } else if (shell.parentNode) {
              parent = shell.parentNode;
              next = shell.nextSibling;
            }
          }
          if (parent) {
            if (next && next.parentNode === parent) {
              parent.insertBefore(target, next);
            } else {
              parent.appendChild(target);
            }
          } else if (body && body.contains(target)) {
            try {
              if (shell.parentNode) shell.parentNode.insertBefore(target, shell);
            } catch {
            }
          }
        } catch {
        }
      }
      if (placeholder) {
        try {
          placeholder.remove();
        } catch {
        }
      }
      if (shell) {
        try {
          shell.remove();
        } catch {
        }
      }
      this._shell = null;
      this._body = null;
      this._placeholder = null;
    }
  };

  // src/keypilot.js
  init_constants();

  // src/utils/kp-deep-link.js
  var KP_SETTINGS_PANEL_IDS = Object.freeze([
    "overview",
    "appearance",
    "keyboard",
    "click-mode",
    "text-mode",
    "scrolling",
    "cursor",
    "control-strip",
    "search",
    "about"
  ]);
  function isKpDeepLink(href) {
    return /^kp:\/\//i.test(String(href || "").trim());
  }
  function parseKpDeepLink(href) {
    const raw = String(href || "").trim();
    if (!raw) return null;
    if (raw.includes("..")) return null;
    let url;
    try {
      url = new URL(raw);
    } catch {
      return null;
    }
    if (url.protocol !== "kp:") return null;
    let kind = (url.hostname || "").toLowerCase();
    let pathId = (url.pathname || "").replace(/^\/+|\/+$/g, "");
    if (!kind && pathId) {
      const parts = pathId.split("/").filter(Boolean);
      kind = (parts[0] || "").toLowerCase();
      pathId = parts.slice(1).join("/");
    }
    if (kind !== "settings" && kind !== "docs") return null;
    if (pathId.includes("/")) return null;
    const id = String(pathId || "").trim();
    if (!id) return null;
    if (!/^[a-z0-9][a-z0-9_-]*$/i.test(id)) return null;
    const hash = (url.hash || "").replace(/^#/, "").trim() || void 0;
    return { kind, id, ...hash ? { hash } : {} };
  }
  function buildKpDeepLink(opts) {
    const kind = opts?.kind === "settings" || opts?.kind === "docs" ? opts.kind : null;
    const id = String(opts?.id || "").trim();
    if (!kind || !id || !/^[a-z0-9][a-z0-9_-]*$/i.test(id)) return "";
    const hash = String(opts?.hash || "").replace(/^#/, "").trim();
    return hash ? `kp://${kind}/${id}#${hash}` : `kp://${kind}/${id}`;
  }
  function normalizeSettingsPanelId(panelId) {
    const id = String(panelId || "").trim();
    return KP_SETTINGS_PANEL_IDS.includes(id) ? id : null;
  }

  // src/modules/popover-window-chrome.js
  var CHROME_HOST_ID = "kpv2-popover-window-chrome-host";
  var TITLEBAR_HEIGHT_PX = 44;
  async function queryPopoverWindowInfo() {
    try {
      if (typeof chrome === "undefined" || !chrome.runtime?.sendMessage) return null;
      const res = await chrome.runtime.sendMessage({ type: MSG.AM_I_POPOVER_WINDOW });
      if (res?.isPopoverWindow) return res;
    } catch {
    }
    return null;
  }
  async function installPopoverWindowChrome(info) {
    if (window !== window.top) return null;
    if (window.__KP_POPOVER_WINDOW_CHROME) return window.__KP_POPOVER_WINDOW_CHROME;
    const meta = info || await queryPopoverWindowInfo();
    if (!meta?.isPopoverWindow) return null;
    const originalUrl = preferHttpsForPreview(meta.originalUrl || location.href);
    const kind = meta.kind === "modal" ? "modal" : "preview";
    const closeKeys = Array.isArray(meta.closeKeys) && meta.closeKeys.length ? meta.closeKeys.map(String) : kind === "modal" ? ["Escape", "p", "P"] : ["Escape", "e", "E"];
    try {
      window.__KP_POPOVER_WINDOW = true;
      window.__KP_POPOVER_CLOSE_KEYS = closeKeys;
    } catch {
    }
    const requestClose = () => {
      try {
        void chrome.runtime.sendMessage({
          type: MSG.CLOSE_POPOVER_WINDOW,
          reason: "chrome_close"
        });
      } catch {
      }
    };
    let host = document.getElementById(CHROME_HOST_ID);
    if (!host) {
      host = document.createElement("div");
      host.id = CHROME_HOST_ID;
    }
    host.setAttribute("data-kp-popover-window-chrome", "1");
    host.style.cssText = `
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: ${TITLEBAR_HEIGHT_PX}px;
    z-index: 2147483646;
    pointer-events: none;
  `;
    ensureChromeHostMounted(host);
    const shadow = ensureOpenChromeShadow(host, { id: "popover-window-chrome" });
    const mount = shadow || host;
    try {
      mount.textContent = "";
    } catch {
    }
    const shell = document.createElement("div");
    shell.style.cssText = `
    pointer-events: auto;
    height: ${TITLEBAR_HEIGHT_PX}px;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    background: #121212;
    border-bottom: 1px solid rgba(255,255,255,0.12);
    box-shadow: 0 2px 8px rgba(0,0,0,0.35);
  `;
    const titleText = kind === "modal" ? "Open Popover" : "Link Preview";
    const hint = kind === "modal" ? "Press Esc / P to hide" : "Press Esc / E to hide";
    const titlebarApi = createUrlPopoverTitlebar({
      title: titleText,
      shortcut: kind === "modal" ? "P" : "E",
      variant: "preview",
      showClose: true,
      onClose: requestClose,
      closeTitle: "Close (Esc)",
      hint,
      className: "kpv2-popover-window-titlebar",
      getUrl: () => originalUrl || location.href,
      afterOpen: requestClose,
      afterOpenNewTab: requestClose
    });
    shell.appendChild(titlebarApi.titlebar);
    mount.appendChild(shell);
    const spacerId = "kpv2-popover-window-chrome-spacer";
    let spacer = document.getElementById(spacerId);
    if (!spacer) {
      spacer = document.createElement("div");
      spacer.id = spacerId;
      spacer.setAttribute("aria-hidden", "true");
      spacer.style.cssText = `
      display: block;
      width: 100%;
      height: ${TITLEBAR_HEIGHT_PX}px;
      flex-shrink: 0;
      pointer-events: none;
    `;
      try {
        const parent = document.body || document.documentElement;
        parent.insertBefore(spacer, parent.firstChild);
      } catch {
      }
    }
    const dispose = () => {
      try {
        window.__KP_POPOVER_WINDOW = false;
        window.__KP_POPOVER_CLOSE_KEYS = null;
      } catch {
      }
      try {
        host.remove();
      } catch {
      }
      try {
        spacer?.remove();
      } catch {
      }
      try {
        delete window.__KP_POPOVER_WINDOW_CHROME;
      } catch {
      }
    };
    const api = { dispose, host, titlebarApi };
    window.__KP_POPOVER_WINDOW_CHROME = api;
    return api;
  }

  // src/keypilot.js
  init_keyboard_layouts();

  // src/ui/keybindings-ui.js
  init_constants();

  // src/ui/keybindings-ui-shared.js
  init_keyboard_layouts();
  var KEYBINDINGS_UI_STYLE_ATTR = "data-kp-keybindings-ui-style";
  var KEYBINDINGS_UI_ROOT_CLASS = "kp-keybindings-ui";
  var KEYBINDINGS_UI_FONT_STYLE_ATTR = "data-kp-keybindings-fonts";
  var KEYBINDINGS_UI_FONT_PRELOAD_ATTR = "data-kp-keybindings-font-preload";
  var KEYBINDINGS_UI_FONT_PLACEHOLDERS = {
    ROBOTECH: "__KP_FONT_ROBOTECH_URL__",
    TITILLIUM: "__KP_FONT_TITILLIUM_URL__",
    TITILLIUM_BOLD: "__KP_FONT_TITILLIUM_BOLD_URL__",
    CUBELLAN: "__KP_FONT_CUBELLAN_URL__",
    EZARION: "__KP_FONT_EZARION_URL__",
    DOSIS: "__KP_FONT_DOSIS_URL__"
  };
  var KEYBINDINGS_KEYBOARD_LAYOUT = getKeyboardUiLayoutForLayout(DEFAULT_KEYBOARD_LAYOUT_ID);
  var FA_SOLID_PATHS = Object.freeze({
    // Navigation / history
    "arrow-left": "M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l192 192c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.3 288 480 288c17.7 0 32-14.3 32-32s-14.3-32-32-32l-370.7 0 137.4-137.4c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-192 192z",
    "arrow-right": "M502.6 278.6c12.5-12.5 12.5-32.8 0-45.3l-192-192c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L402.7 224 32 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l370.7 0-137.4 137.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l192-192z",
    "arrow-up": "M233.4 105.4c12.5-12.5 32.8-12.5 45.3 0l192 192c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L256 173.3 86.6 342.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l192-192z",
    "arrow-down": "M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z",
    "angles-up": "M233.4 105.4c12.5-12.5 32.8-12.5 45.3 0l192 192c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L256 173.3 86.6 342.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l192-192zm0 160c12.5-12.5 32.8-12.5 45.3 0l192 192c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L256 333.3 86.6 502.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l192-192z",
    "angles-down": "M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192zm0-160c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 178.7 86.6 9.4C74.1-3.1 53.8-3.1 41.3 9.4s-12.5 32.8 0 45.3l192 192z",
    "chevron-left": "M41.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.3 256 246.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z",
    "chevron-right": "M470.6 278.6c12.5-12.5 12.5-32.8 0-45.3l-160-160c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L402.7 256 265.4 393.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l160-160z",
    // Clicks / tabs / windows (Font Awesome Free solid-style glyphs)
    "hand-pointer": "M320 0c17.7 0 32 14.3 32 32V176h16c17.7 0 32 14.3 32 32s-14.3 32-32 32H352v16c0 17.7-14.3 32-32 32s-32-14.3-32-32V240H272v16c0 17.7-14.3 32-32 32s-32-14.3-32-32V240H192v80c0 53 43 96 96 96h32c53 0 96-43 96-96V224h32c17.7 0 32-14.3 32-32s-14.3-32-32-32H416V32c0-17.7-14.3-32-32-32H320zM192 96c0-17.7-14.3-32-32-32H128C57.3 64 0 121.3 0 192v96c0 53 43 96 96 96h32c17.7 0 32-14.3 32-32s-14.3-32-32-32H96c-17.7 0-32-14.3-32-32V192c0-35.3 28.7-64 64-64h32c17.7 0 32-14.3 32-32z",
    "arrow-up-right-from-square": "M320 0c-17.7 0-32 14.3-32 32s14.3 32 32 32h82.7L201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L448 109.3V192c0 17.7 14.3 32 32 32s32-14.3 32-32V32c0-17.7-14.3-32-32-32H320zM80 32C35.8 32 0 67.8 0 112V432c0 44.2 35.8 80 80 80H400c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32V432c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V112c0-8.8 7.2-16 16-16H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H80z",
    "clone": "M64 464H288c8.8 0 16-7.2 16-16V384h48v64c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V224c0-35.3 28.7-64 64-64h64v48H64c-8.8 0-16 7.2-16 16V448c0 8.8 7.2 16 16 16zM224 0c-35.3 0-64 28.7-64 64V288c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V64c0-35.3-28.7-64-64-64H224zm0 48H448c8.8 0 16 7.2 16 16V288c0 8.8-7.2 16-16 16H224c-8.8 0-16-7.2-16-16V64c0-8.8 7.2-16 16-16z",
    "window-maximize": "M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zm32 96H416c17.7 0 32 14.3 32 32v32H64V160c0-17.7 14.3-32 32-32z",
    "eye": "M256 96c-89.6 0-168.5 48.8-212.7 122.3c-7.3 12.1-7.3 27.3 0 39.4C87.5 331.2 166.4 380 256 380s168.5-48.8 212.7-122.3c7.3-12.1 7.3-27.3 0-39.4C424.5 144.8 345.6 96 256 96zm0 224a96 96 0 1 1 0-192 96 96 0 1 1 0 192zm0-144a48 48 0 1 0 0 96 48 48 0 1 0 0-96z",
    "plus": "M256 80c0-17.7-14.3-32-32-32s-32 14.3-32 32V224H48c-17.7 0-32 14.3-32 32s14.3 32 32 32H192V432c0 17.7 14.3 32 32 32s32-14.3 32-32V288H400c17.7 0 32-14.3 32-32s-14.3-32-32-32H256V80z",
    "folder-plus": "M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H298.5c-17 0-33.3-6.7-45.3-18.7L226.7 50.7C214.7 38.7 198.5 32 181.5 32H64zM232 248v-48c0-13.3 10.7-24 24-24s24 10.7 24 24v48h48c13.3 0 24 10.7 24 24s-10.7 24-24 24H280v48c0 13.3-10.7 24-24 24s-24-10.7-24-24V296H184c-13.3 0-24-10.7-24-24s10.7-24 24-24h48z",
    "xmark": "M256 48a208 208 0 1 1 0 416 208 208 0 1 1 0-416zm0 464A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM175 175c9.4-9.4 24.6-9.4 33.9 0l47 47 47-47c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-47 47 47 47c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-47-47-47 47c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l47-47-47-47c-9.4-9.4-9.4-24.6 0-33.9z",
    "trash": "M135.2 17.7L128 32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H384l-7.2-14.3C372.4 6.8 361.3 0 349.2 0H162.8c-12.1 0-23.2 6.8-28.6 17.7zM32 128V448c0 35.3 28.7 64 64 64H416c35.3 0 64-28.7 64-64V128H32zm112 64c8.8 0 16 7.2 16 16V400c0 8.8-7.2 16-16 16s-16-7.2-16-16V208c0-8.8 7.2-16 16-16zm96 0c8.8 0 16 7.2 16 16V400c0 8.8-7.2 16-16 16s-16-7.2-16-16V208c0-8.8 7.2-16 16-16zm96 0c8.8 0 16 7.2 16 16V400c0 8.8-7.2 16-16 16s-16-7.2-16-16V208c0-8.8 7.2-16 16-16z",
    "delete-left": "M576 128c0-35.3-28.7-64-64-64H205.3c-17 0-33.3 6.7-45.3 18.7L9.4 233.4c-6 6-9.4 14.1-9.4 22.6s3.4 16.6 9.4 22.6L160 429.3c12 12 28.3 18.7 45.3 18.7H512c35.3 0 64-28.7 64-64V128zM271 175c9.4-9.4 24.6-9.4 33.9 0l47 47 47-47c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-47 47 47 47c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-47-47-47 47c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l47-47-47-47c-9.4-9.4-9.4-24.6 0-33.9z",
    // UI / utility
    // Font Awesome Free solid "gear" (512 viewBox)
    "gear": "M495.9 166.1c3.3 12.7 .9 26.3-7.1 36.1l-37.3 45.7c2.1 11.1 3.2 22.6 3.2 34.3s-1.1 23.2-3.2 34.3l37.3 45.7c8 9.8 10.4 23.4 7.1 36.1c-6.3 24.2-17.7 46.6-33.1 66.3c-8.1 10.3-21.2 14.9-33.9 12.1l-57.5-12.7c-17.9 15.3-38.4 27.3-60.7 35.4l-13.7 57.5c-2.9 12.1-12.9 21.1-25.4 22.4c-24.2 2.6-49.1 2.6-73.3 0c-12.5-1.3-22.5-10.3-25.4-22.4l-13.7-57.5c-22.3-8.1-42.8-20.1-60.7-35.4L71.6 436.6c-12.7 2.8-25.8-1.8-33.9-12.1C22.3 404.8 10.9 382.4 4.6 358.2c-3.3-12.7-.9-26.3 7.1-36.1l37.3-45.7c-2.1-11.1-3.2-22.6-3.2-34.3s1.1-23.2 3.2-34.3L11.7 161.9c-8-9.8-10.4-23.4-7.1-36.1C10.9 101.6 22.3 79.2 37.7 59.5c8.1-10.3 21.2-14.9 33.9-12.1l57.5 12.7c17.9-15.3 38.4-27.3 60.7-35.4L203.5 17.2c2.9-12.1 12.9-21.1 25.4-22.4c24.2-2.6 49.1-2.6 73.3 0c12.5 1.3 22.5 10.3 25.4 22.4l13.7 57.5c22.3 8.1 42.8 20.1 60.7 35.4l57.5-12.7c12.7-2.8 25.8 1.8 33.9 12.1c15.4 19.7 26.8 42.1 33.1 66.3zM256 336a80 80 0 1 0 0-160 80 80 0 1 0 0 160z",
    "magnifying-glass": "M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z",
    "keyboard": "M0 96C0 60.7 28.7 32 64 32H448c35.3 0 64 28.7 64 64V416c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V96zm128 64v32h32V160H128zm64 0v32h32V160H192zm64 0v32h32V160H256zm64 0v32h32V160H320zm64 0v32h32V160H384zM96 256v32h64V256H96zm96 0v32h32V256H192zm64 0v32h32V256H256zm64 0v32h32V256H320zm64 0v32h32V256H384zm64 0v32h32V256H448zM128 352v32H384V352H128z",
    "clock-rotate-left": "M256 0C114.6 0 0 114.6 0 256S114.6 512 256 512c53 0 102-16.2 142.7-43.9c10.8-7.4 13.6-22.3 6.2-33.1s-22.3-13.6-33.1-6.2C340.8 449.1 299.6 464 256 464C141.1 464 48 370.9 48 256S141.1 48 256 48c60.7 0 115.5 26.1 153.4 67.7l-33.5 33.5c-9.4 9.4-2.7 25.5 10.5 25.5H456c13.3 0 24-10.7 24-24V56c0-13.2-16.1-19.9-25.5-10.5L418.7 81.3C368.5 31.4 315.1 0 256 0zM232 120c0-13.3-10.7-24-24-24s-24 10.7-24 24V256c0 6.4 2.5 12.5 7 17l72 72c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-65-65V120z",
    "rocket": "M156.6 384.9L125.7 354c-8.5-8.5-11.5-20.8-7.7-32.2c3-8.9 7-20.5 11.8-33.8L24 288c-8.6 0-16.6-4.6-20.9-12.1s-4.2-16.7 .2-24.1l52.5-88.5c13-21.9 36.5-35.3 61.9-35.3h82.3c2.4-4 4.8-7.7 7.2-11.3C289.1-4.1 411.1-8.1 483.9 5.3c11.6 2.1 20.6 11.2 22.8 22.8c13.4 72.9 9.3 194.8-111.4 276.7c-3.5 2.4-7.3 4.8-11.3 7.2v82.3c0 25.4-13.4 49-35.3 61.9l-88.5 52.5c-7.4 4.4-16.6 4.5-24.1 .2s-12.1-12.2-12.1-20.9V384.9c-13.3 4.8-24.9 8.8-33.8 11.8c-11.4 3.7-23.7 .7-32.2-7.8zM215.3 237.3c28.3-28.3 73.1-31.3 105.4-8.5L200.5 348.5c-22.8-32.3-19.8-77.1 8.5-105.4l6.3-5.8z",
    "house": "M575.8 255.5c0 18-15 32.1-32 32.1h-32l.7 160.2c.2 35.5-28.5 64.3-64 64.3H392c-22.1 0-40-17.9-40-40V448 384c0-17.7-14.3-32-32-32H256c-17.7 0-32 14.3-32 32v64 24c0 22.1-17.9 40-40 40H128.1c-35.3 0-64-28.7-64-64V287.6H32c-18 0-32-14-32-32.1c0-9 3-17 10-24L266.4 8c7-7 15-8 22-8s15 2 21 7L564.8 231.5c8 7 12 15 11 24z",
    "ban": "M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM175 175c9.4-9.4 24.6-9.4 33.9 0L368 334.1c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0L175 208.9c-9.4-9.4-9.4-24.6 0-33.9z",
    // Special keys
    "arrow-right-to-bracket": "M512 256c0 17.7-14.3 32-32 32H178.7l73.4 73.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3l128-128c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3L178.7 224H480c17.7 0 32 14.3 32 32zM0 128C0 92.7 28.7 64 64 64H192c17.7 0 32 14.3 32 32s-14.3 32-32 32H64V384H192c17.7 0 32 14.3 32 32s-14.3 32-32 32H64c-35.3 0-64-28.7-64-64V128z",
    "turn-down": "M54.6 310.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L128 293.3V64c0-17.7 14.3-32 32-32H352c17.7 0 32 14.3 32 32s-14.3 32-32 32H192V293.3l28.1-28.1c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3l-80 80c-12.5 12.5-32.8 12.5-45.3 0l-80-80z",
    "up-long": "M278.6 9.4c-12.5-12.5-32.8-12.5-45.3 0l-128 128c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L224 109.3V480c0 17.7 14.3 32 32 32s32-14.3 32-32V109.3l73.4 73.4c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-128-128z",
    "arrow-up-from-line": "M233.4 105.4c12.5-12.5 32.8-12.5 45.3 0l96 96c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L288 205.3V384c0 17.7-14.3 32-32 32s-32-14.3-32-32V205.3l-41.4 41.4c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l96-96zM64 448c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H96c-17.7 0-32-14.3-32-32z",
    "arrow-down-to-line": "M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l96-96c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L288 306.7V128c0-17.7-14.3-32-32-32s-32 14.3-32 32V306.7l-41.4-41.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l96 96zM64 64c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H96C78.3 96 64 81.7 64 64z",
    "circle": "M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z",
    // FA Free solid "code" — Execute JS
    "code": "M318.4 73.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L402.7 248 273.1 377.6c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l152-152c12.5-12.5 12.5-32.8 0-45.3l-152-152zm-124.8 0c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-152 152c-12.5 12.5-12.5 32.8 0 45.3l152 152c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.3 248 193.6 163.7c12.5-12.5 12.5-32.8 0-45.3z",
    // Text / rectangle selection
    // FA Free solid "font" (A glyph) — text select
    "font": "M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c6.1 10.4 6.1 23.3 0 33.7s-17.4 16.5-29.9 16.5H35.4c-12.5 0-23.8-6.6-29.9-16.5s-6.1-23.3 0-33.7l216-368C228.7 39.5 241.8 32 256 32zm0 88.4L96.7 392h318.6L256 120.4z",
    // FA Free solid "i-cursor" — caret / character select
    "i-cursor": "M128 64c0-17.7 14.3-32 32-32H352c17.7 0 32 14.3 32 32s-14.3 32-32 32H288v128h64c17.7 0 32 14.3 32 32s-14.3 32-32 32H288v128h64c17.7 0 32 14.3 32 32s-14.3 32-32 32H160c-17.7 0-32-14.3-32-32s14.3-32 32-32h64V288H160c-17.7 0-32-14.3-32-32s14.3-32 32-32h64V96H160c-17.7 0-32-14.3-32-32z",
    // FA Free solid "vector-square" — rectangle marquee corners
    "vector-square": "M32 32C14.3 32 0 46.3 0 64v64c0 17.7 14.3 32 32 32s32-14.3 32-32V96h64c17.7 0 32-14.3 32-32s-14.3-32-32-32H32zM32 320c-17.7 0-32 14.3-32 32v64c0 17.7 14.3 32 32 32h64c17.7 0 32-14.3 32-32s-14.3-32-32-32H64V352c0-17.7-14.3-32-32-32zM320 64c0 17.7 14.3 32 32 32h64v64c0 17.7 14.3 32 32 32s32-14.3 32-32V64c0-17.7-14.3-32-32-32H352c-17.7 0-32 14.3-32 32zM480 320c-17.7 0-32 14.3-32 32v64H384c-17.7 0-32 14.3-32 32s14.3 32 32 32h64c17.7 0 32-14.3 32-32V352c0-17.7-14.3-32-32-32z",
    // FA Free solid "image" — copy hovered image
    "image": "M0 96C0 60.7 28.7 32 64 32H448c35.3 0 64 28.7 64 64V416c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6h96 32H424c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z",
    // FA Free solid "video"
    "video": "M0 128C0 92.7 28.7 64 64 64H320c35.3 0 64 28.7 64 64V384c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V128zM559.1 99.8c10.4 5.6 16.9 16.4 16.9 28.2V384c0 11.8-6.5 22.6-16.9 28.2s-23 6-32.9 1.3L384 337.1V174.9l142.2-76.4c9.8-4.7 22.4-4.3 32.9 1.3z",
    // FA Free solid "link" — copy hovered URL
    "link": "M326.612 185.391c59.747 59.809 58.927 155.698.36 214.59-.11.12-.24.25-.36.37l-67.2 67.2c-59.27 59.27-155.699 59.262-214.96 0-59.27-59.26-59.27-155.7 0-214.96l37.106-37.106c9.84-9.84 26.786-3.3 27.294 10.606.648 17.722 3.826 35.527 9.69 52.721 1.986 5.822.567 12.262-3.783 16.612l-13.087 13.087c-28.026 28.026-28.026 73.66 0 101.682 28.026 28.026 73.66 28.026 101.682 0l67.2-67.2c28.026-28.026 28.026-73.66 0-101.682-3.794-3.808-7.368-5.703-10.954-6.817-10.756-3.356-22.666 1.983-27.085 12.227-5.575 12.941-17.35 20.326-30.978 20.326-12.802 0-22.414-11.312-19.101-23.541 6.027-22.318 9.025-49.922 2.753-73.389-13.415-49.971.392-102.811 37.393-139.813 59.17-59.117 154.849-59.262 214.096-.31zM213.388 326.609c-59.747-59.809-58.927-155.698-.36-214.59.11-.12.24-.25.36-.37l67.2-67.2c59.27-59.27 155.699-59.262 214.96 0 59.27 59.26 59.27 155.7 0 214.96l-37.106 37.106c-9.84 9.84-26.786 3.3-27.294-10.606-.648-17.722-3.826-35.527-9.69-52.721-1.986-5.822-.567-12.262 3.783-16.612l13.087-13.087c28.026-28.026 28.026-73.66 0-101.682-28.026-28.026-73.66-28.026-101.682 0l-67.2 67.2c-28.026 28.026-28.026 73.66 0 101.682 3.794 3.808 7.368 5.703 10.954 6.817 10.756 3.356 22.666-1.983 27.085-12.227 5.575-12.941 17.35-20.326 30.978-20.326 12.802 0 22.414 11.312 19.101 23.541-6.027 22.318-9.025 49.922-2.753 73.389 13.415 49.971-.392 102.811-37.393 139.813-59.17 59.117-154.849 59.262-214.096.31z",
    // FA Free solid "table-columns" — Cols Toggle multicol layout
    "table-columns": "M0 96C0 60.7 28.7 32 64 32H448c35.3 0 64 28.7 64 64V416c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V96zm64 64V416H224V160H64zm320 0H288V416H448V160z",
    // Library / macro / clipboard / AI (Config card keycaps + Reference parity)
    "clipboard": "M192 0c35.3 0 64 28.7 64 64l0 32 112 0c35.3 0 64 28.7 64 64l0 288c0 35.3-28.7 64-64 64L64 512c-35.3 0-64-28.7-64-64L0 160c0-35.3 28.7-64 64-64l112 0 0-32c0-35.3 28.7-64 64-64zm0 64l0 32 64 0 0-32c0-17.7-14.3-32-32-32s-32 14.3-32 32zM64 160l0 288c0 17.7 14.3 32 32 32l256 0c17.7 0 32-14.3 32-32l0-288c0-17.7-14.3-32-32-32L64 128c-17.7 0-32 14.3-32 32z",
    "scissors": "M44.6 66.2l117.5 117.5c-4.7 8-7.1 17-7.1 26.3c0 26.5 21.5 48 48 48s48-21.5 48-48s-21.5-48-48-48c-4.8 0-9.4 .7-13.7 2L44.6 66.2C39.3 60.9 30.7 60.9 25.4 66.2S20.1 80.1 25.4 85.4L44.6 66.2zM203.2 237.8L85.4 355.6c-5.3 5.3-5.3 13.9 0 19.2s13.9 5.3 19.2 0l117.8-117.8c4.3 1.3 8.9 2 13.7 2c26.5 0 48-21.5 48-48s-21.5-48-48-48c-9.3 0-18.3 2.4-26.3 7.1zM432 144c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm0 256c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zM162.6 466.2c5.3 5.3 13.9 5.3 19.2 0L467.4 180.6c5.3-5.3 5.3-13.9 0-19.2s-13.9-5.3-19.2 0L162.6 447c-5.3 5.3-5.3 13.9 0 19.2z",
    "robot": "M32 160c0-35.3 28.7-64 64-64l32 0 0-32c0-17.7 14.3-32 32-32s32 14.3 32 32l0 32 128 0 0-32c0-17.7 14.3-32 32-32s32 14.3 32 32l0 32 32 0c35.3 0 64 28.7 64 64l0 32 32 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-32 0 0 128c0 35.3-28.7 64-64 64l-16 0 0 48c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-48-128 0 0 48c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-48-16 0c-35.3 0-64-28.7-64-64l0-128-32 0c-17.7 0-32-14.3-32-32s14.3-32 32-32l32 0 0-32zm96 64a32 32 0 1 0 0 64 32 32 0 1 0 0-64zm128 32a32 32 0 1 0 64 0 32 32 0 1 0-64 0z",
    "book": "M96 0C43 0 0 43 0 96L0 416c0 53 43 96 96 96l288 0 32 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l0-64c17.7 0 32-14.3 32-32l0-320c0-17.7-14.3-32-32-32L384 0 96 0zM384 416l0 32L96 448c-17.7 0-32-14.3-32-32s14.3-32 32-32l288 0zM112 64l160 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-160 0c-8.8 0-16-7.2-16-16s7.2-16 16-16zm0 96l160 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-160 0c-8.8 0-16-7.2-16-16s7.2-16 16-16zm0 96l96 0c8.8 0 16 7.2 16 16s-7.2 16-16 16l-96 0c-8.8 0-16-7.2-16-16s7.2-16 16-16z",
    "language": "M478.3 73.8c-5.7-10.7-16.8-17.3-28.7-17.3l-51.1 0c-12.5 0-24.7 4.9-33.7 13.7L192 242.7 96 146.7c-9-8.8-21.2-13.7-33.7-13.7L11.2 133C-.7 133-11.8 139.6-17.5 150.3S-24 174.1-18.5 185.3L73.1 352 18.5 454.7C13 465.9 16.1 478.9 24.9 486.6S47.1 496 58.5 490.5L160 432.9 261.5 490.5c11.4 5.5 24.9 2.6 33.7-5.1s11.9-20.7 6.4-32.1L246.9 352 338.5 185.3c5.5-11.2 2.4-24.2-6.4-31.9zM192 309.3L128 192l64 117.3zm192-245.3L480 192 384 64z",
    "bolt": "M234.5 5.7c13.9-5 29.1-.6 38.2 10.9l144 176c9.2 11.2 11.2 27.1 5.1 40.3s-19.2 21.1-33.3 21.1L320 254V432c0 26.5-21.5 48-48 48H176c-26.5 0-48-21.5-48-48V254H53.5c-14.1 0-27.1-7.9-33.3-21.1s-4.1-29 5.1-40.3l144-176c9.2-11.5 24.3-15.9 38.2-10.9z",
    "arrows-rotate": "M105.1 202.6c7.7-21.8 20.2-42.3 37.8-59.1c62.5-62.5 163.8-62.5 226.3 0L417.3 192 384 192c-17.7 0-32 14.3-32 32s14.3 32 32 32l96 0 16 0c17.7 0 32-14.3 32-32l0-96c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 36.7L425.4 89.9C332.4-3.1 181.2-3.1 88.2 89.9c-29.1 29.1-48.5 64.9-56.5 103.5c-3.8 18.5 10.1 36.9 29.1 36.9c14.2 0 26.8-9.9 30.3-23.7zM406.9 309.4c-7.7 21.8-20.2 42.3-37.8 59.1c-62.5 62.5-163.8 62.5-226.3 0L94.7 320l33.3 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L32 256l-16 0C-1.7 256-16 270.3-16 288l0 96c0 17.7 14.3 32 32 32s32-14.3 32-32l0-36.7 33.9 33.9C186.8 515.1 338 515.1 431 422.1c29.1-29.1 48.5-64.9 56.5-103.5c3.8-18.5-10.1-36.9-29.1-36.9c-14.2 0-26.8 9.9-30.3 23.7z",
    "layer-group": "M32 96l224-80 224 80L256 176 32 96zM32 192l224 80 224-80 0 32L256 304 32 224l0-32zm0 96l224 80 224-80 0 32L256 400 32 320l0-32z",
    "globe": "M352 256c0 22.2-1.2 43.6-3.3 64H163.3c-2.2-20.4-3.3-41.8-3.3-64s1.2-43.6 3.3-64H348.7c2.2 20.4 3.3 41.8 3.3 64zm28.8-64H503.9c5.3 20.5 8.1 41.9 8.1 64s-2.8 43.5-8.1 64H380.8c2.1-20.6 3.2-42 3.2-64s-1.1-43.4-3.2-64zm112.6-32H376.7c-10-63.9-29.8-117.4-55.3-151.6c78.3 20.7 142 77.5 171.9 151.6zm-196.8 0H167.7c6.1-36.4 15.5-68.6 27-94.7c10.5-23.6 22.2-40.7 33.5-51.5C239.4 3.2 248.7 0 256 0s16.6 3.2 27.8 13.8c11.3 10.8 23 27.9 33.5 51.5c11.6 26 20.9 58.2 27 94.7zm-144 0H18.6C48.6 85.9 112.2 29.1 190.6 8.4C165.1 42.6 145.3 96.1 135.3 160zM8.1 192H131.2c-2.1 20.6-3.2 42-3.2 64s1.1 43.4 3.2 64H8.1C2.8 299.5 0 278.1 0 256s2.8-43.5 8.1-64zM194.7 446.6c-11.6-26-20.9-58.2-27-94.6H344.3c-6.1 36.4-15.5 68.6-27 94.6c-10.5 23.6-22.2 40.7-33.5 51.5C272.6 508.8 263.3 512 256 512s-16.6-3.2-27.8-13.8c-11.3-10.8-23-27.9-33.5-51.5zM135.3 352c10 63.9 29.8 117.4 55.3 151.6C112.2 482.9 48.6 426.1 18.6 352H135.3zm358.1 0c-30 74.1-93.6 130.9-171.9 151.6c25.5-34.2 45.2-87.7 55.3-151.6H493.4z",
    "location-dot": "M256 64c-70.7 0-128 57.3-128 128c0 82.4 92.3 197.6 118.7 227.5c4.8 5.4 13.8 5.4 18.6 0C291.7 389.6 384 274.4 384 192c0-70.7-57.3-128-128-128zm0 176a48 48 0 1 1 0-96 48 48 0 1 1 0 96z"
  });
  var KEYBOARD_ACTION_ICON_IDS = Object.freeze({
    ACTIVATE: "hand-pointer",
    ACTIVATE_NEW_TAB: "arrow-up-right-from-square",
    ACTIVATE_NEW_TAB_BACKGROUND: "clone",
    BACK: "arrow-left",
    BACK2: "arrow-left",
    FORWARD: "arrow-right",
    DELETE: "trash",
    COLS_TOGGLE: "table-columns",
    TAB_LEFT: "chevron-left",
    TAB_RIGHT: "chevron-right",
    ROOT: "house",
    LAUNCHER: "rocket",
    TOP_SITES: "layer-group",
    CLOSE_TAB: "xmark",
    CANCEL: "ban",
    PAGE_UP_INSTANT: "arrow-up",
    PAGE_DOWN_INSTANT: "arrow-down",
    PAGE_TOP: "arrow-up-from-line",
    PAGE_BOTTOM: "arrow-down-to-line",
    SCROLL_LINE: "circle",
    NEW_TAB: "folder-plus",
    OPEN_POPOVER: "window-maximize",
    PREVIEW_LINK_POPOVER: "eye",
    POI_WEBSITE: "globe",
    POI_ADDRESS: "location-dot",
    OPEN_SETTINGS_POPOVER: "gear",
    OMNIBOX: "magnifying-glass",
    TAB_HISTORY: "clock-rotate-left",
    TOGGLE_KEYBOARD_HELP: "keyboard",
    // Selection tools (recently re-enabled; were missing from the icon map)
    HIGHLIGHT: "i-cursor",
    RECTANGLE_HIGHLIGHT: "vector-square",
    COPY_HOVERED_IMAGE: "image",
    COPY_HOVERED_URL: "link",
    COPY_HOVERED_VIDEO: "video",
    FONT_INFO: "font",
    PAGE_MEDIA: "image",
    OPEN_MEDIA_LIBRARY: "image",
    // Function Library (Config cards + placeable Actions)
    CLIPBOARD_COPY: "clipboard",
    CLIPBOARD_CUT: "scissors",
    CLIPBOARD_PASTE: "clipboard",
    CLIPBOARD_SELECT_ALL: "font",
    SELECT_WORD: "font",
    SELECT_SENTENCE: "font",
    SELECT_PARAGRAPH: "font",
    SELECT_IMAGE: "image",
    SEND_TEXT_TO_AI: "robot",
    SEND_HOTKEY: "keyboard",
    SEND_BURST: "bolt",
    CYCLE_ROUND_ROBIN: "arrows-rotate",
    HOLD_CONTINUOUS: "circle",
    CLICK_MOUSE_BUTTON: "hand-pointer",
    REMAP_KEY: "keyboard",
    TYPE_CHARACTERS: "font",
    EXECUTE_JS: "code",
    GET_TEXT_AT_CURSOR: "i-cursor",
    GET_TEXT_RANGE: "font",
    GET_MEDIA_AT_CURSOR: "image",
    LOOKUP_WORD: "book",
    TRANSLATE: "language",
    SHOW_POPOVER: "window-maximize",
    ADD_URL_TO_MEDIA_LIBRARY: "plus",
    FETCH_URL_FOR_MEDIA_LIBRARY: "arrow-down"
  });
  var ACCURATE_GEAR_PATH_16 = "M9.405 1.05c-.413-1.4-2.397-1.4-2.81 0l-.1.34a1.464 1.464 0 0 1-2.275.819l-.31-.17c-1.283-.698-2.686.705-1.987 1.987l.169.311c.446.82.023 1.841-.82 2.275l-.34.1c-1.4.413-1.4 2.397 0 2.81l.34.1a1.464 1.464 0 0 1 .82 2.275l-.17.31c-.698 1.283.705 2.686 1.987 1.987l.311-.169a1.464 1.464 0 0 1 2.275.82l.1.34c.413 1.4 2.397 1.4 2.81 0l.1-.34a1.464 1.464 0 0 1 2.275-.82l.31.17c1.283.698 2.686-.705 1.987-1.987l-.169-.311a1.464 1.464 0 0 1 .82-2.275l.34-.1c1.4-.413 1.4-2.397 0-2.81l-.34-.1a1.464 1.464 0 0 1-.82-2.275l.17-.31c.698-1.283-.705-2.686-1.987-1.987l-.311.169a1.464 1.464 0 0 1-2.275-.82zM8 10.93a2.93 2.93 0 1 1 0-5.86 2.93 2.93 0 0 1 0 5.86z";
  function faIconDataUri(pathD, fill = "black", viewBox = "0 0 512 512") {
    const safeFill = String(fill || "black").replace(/"/g, "");
    const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${viewBox}" fill="${safeFill}"><path d="${pathD}"/></svg>`;
    return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
  }
  function getActionIconDataUri(actionId, opts = {}) {
    const iconName = KEYBOARD_ACTION_ICON_IDS[actionId];
    const pathD = iconName === "gear" ? ACCURATE_GEAR_PATH_16 : iconName ? FA_SOLID_PATHS[iconName] : null;
    if (!pathD) return "";
    return faIconDataUri(
      pathD,
      opts.fill || "white",
      iconName === "gear" ? "0 0 16 16" : void 0
    );
  }
  function keycapMaterial(t) {
    return `
  --kp-key-face: ${t.face};
  --kp-key-mid: ${t.mid};
  --kp-key-deep: ${t.deep};
  --kp-key-icon: ${t.icon};
  --kp-key-glow: ${t.glow || "transparent"};
`;
  }
  function getKeyboardKeyIconCss() {
    const iconUris = {};
    for (const [name, pathD] of Object.entries(FA_SOLID_PATHS)) {
      iconUris[name] = name === "gear" ? faIconDataUri(ACCURATE_GEAR_PATH_16, "black", "0 0 16 16") : faIconDataUri(pathD);
    }
    const lines = [];
    lines.push(`
.${KEYBINDINGS_UI_ROOT_CLASS} .key > .key-bg-icon {
  position: absolute;
  inset: 7%;
  z-index: 0;
  pointer-events: none;
  /*
   * Transparent until an action rule applies both a mask and paint color.
   * mask-image:none + solid background-color otherwise paints a dark rectangle
   * (was visible on unmapped action keys like COPY_HOVERED_IMAGE before its icon).
   */
  background-color: transparent;
  background-image: none;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center center;
  mask-position: center center;
  -webkit-mask-size: 86% 86%;
  mask-size: 86% 86%;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: 0.92;
}

/*
 * IMPORTANT: do NOT force position:relative on all non-icon children.
 * Action names (.key-main) and letter labels (.key-label) are absolutely
 * layered; a later relative rule would put letters back into flex flow.
 */

.${KEYBINDINGS_UI_ROOT_CLASS} .key:hover > .key-bg-icon {
  opacity: 1;
}

/* Keys without functions/macros never paint an icon layer */
.${KEYBINDINGS_UI_ROOT_CLASS} .key:not([data-kp-action-id]):not([data-kp-macro-id]) > .key-bg-icon {
  display: none;
}

/*
 * Solid darken overlay for keydown feedback (more reliable than filter).
 * No opacity transition: when content-script replaces early-inject CSS (font URLs),
 * a transitioned opacity:0 rule animates from the UA default (1\u21920) and every key
 * briefly looks pressed \u2014 most noticeable on colored caps like K (KB Reference).
 */
.${KEYBINDINGS_UI_ROOT_CLASS} .key > .key-press-overlay {
  position: absolute;
  inset: 0;
  z-index: 6;
  box-sizing: border-box;
  border-radius: inherit;
  background: rgba(0, 0, 0, 0.78);
  opacity: 0;
  pointer-events: none;
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.kp-key-pressed > .key-press-overlay,
.${KEYBINDINGS_UI_ROOT_CLASS} .key > .key-press-overlay.is-on {
  opacity: 1;
}
`);
    for (const [actionId, iconName] of Object.entries(KEYBOARD_ACTION_ICON_IDS)) {
      const uri = iconUris[iconName];
      if (!uri) continue;
      lines.push(
        `.${KEYBINDINGS_UI_ROOT_CLASS} .key[data-kp-action-id="${actionId}"] > .key-bg-icon { -webkit-mask-image: ${uri}; mask-image: ${uri}; background-color: var(--kp-key-icon, #0c1018); }`
      );
    }
    if (iconUris["layer-group"]) {
      lines.push(
        `.${KEYBINDINGS_UI_ROOT_CLASS} .key[data-kp-macro-id] > .key-bg-icon { -webkit-mask-image: ${iconUris["layer-group"]}; mask-image: ${iconUris["layer-group"]}; background-color: var(--kp-key-icon, #0c1018); }`
      );
    }
    return lines.join("\n");
  }
  function ensureKeyBackgroundIcon(doc, keyEl) {
    if (!doc || !keyEl) return;
    try {
      if (keyEl.querySelector(":scope > .key-bg-icon")) return;
    } catch {
      if (keyEl.querySelector(".key-bg-icon")) return;
    }
    const icon = doc.createElement("span");
    icon.className = "key-bg-icon";
    icon.setAttribute("aria-hidden", "true");
    keyEl.insertBefore(icon, keyEl.firstChild);
  }
  function ensureKeyPressOverlay(doc, keyEl) {
    if (!doc || !keyEl) return null;
    let overlay = null;
    try {
      overlay = keyEl.querySelector(":scope > .key-press-overlay");
    } catch {
      overlay = keyEl.querySelector(".key-press-overlay");
    }
    if (overlay) return overlay;
    overlay = doc.createElement("span");
    overlay.className = "key-press-overlay";
    overlay.setAttribute("aria-hidden", "true");
    overlay.style.opacity = "0";
    overlay.style.transition = "none";
    keyEl.appendChild(overlay);
    return overlay;
  }
  function setKeyPressedState(keyEl, pressed, doc) {
    if (!keyEl) return;
    const d = doc || keyEl.ownerDocument || (typeof document !== "undefined" ? document : null);
    if (pressed) {
      keyEl.classList.add("kp-key-pressed");
      const overlay = d ? ensureKeyPressOverlay(d, keyEl) : null;
      if (overlay) overlay.classList.add("is-on");
    } else {
      keyEl.classList.remove("kp-key-pressed");
      let overlay = null;
      try {
        overlay = keyEl.querySelector(":scope > .key-press-overlay");
      } catch {
        overlay = keyEl.querySelector(".key-press-overlay");
      }
      if (overlay) overlay.classList.remove("is-on");
    }
  }
  function getKeybindingsUiFontFaceCss(fontUrls = {}) {
    const urlRobotech = fontUrls.robotech || KEYBINDINGS_UI_FONT_PLACEHOLDERS.ROBOTECH;
    const urlTitillium = fontUrls.titillium || KEYBINDINGS_UI_FONT_PLACEHOLDERS.TITILLIUM;
    const urlTitilliumBold = fontUrls.titilliumBold || KEYBINDINGS_UI_FONT_PLACEHOLDERS.TITILLIUM_BOLD;
    const urlCubellan = fontUrls.cubellan || KEYBINDINGS_UI_FONT_PLACEHOLDERS.CUBELLAN;
    const urlEzarion = fontUrls.ezarion || KEYBINDINGS_UI_FONT_PLACEHOLDERS.EZARION;
    const urlDosis = fontUrls.dosis || KEYBINDINGS_UI_FONT_PLACEHOLDERS.DOSIS;
    return `
@font-face {
  font-family: "ROBOTECHGPRegular";
  src: url("${urlRobotech}") format("truetype");
  font-weight: normal;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: "TitilliumText";
  src: url("${urlTitillium}") format("opentype");
  font-weight: 100 500;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: "TitilliumText";
  src: url("${urlTitilliumBold}") format("truetype");
  font-weight: 600 900;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: "Cubellan";
  src: url("${urlCubellan}") format("truetype");
  font-weight: normal;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: "Ezarion";
  src: url("${urlEzarion}") format("truetype");
  font-weight: normal;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: "Dosis";
  src: url("${urlDosis}") format("truetype");
  font-weight: normal;
  font-style: normal;
  font-display: block;
}
`.trim();
  }
  function preloadKeybindingsUiFonts(doc, fontUrls) {
    if (!doc || !fontUrls) return;
    const head = doc.head || doc.documentElement;
    if (!head?.appendChild) return;
    const entries = [
      { id: "dosis", href: fontUrls.dosis, type: "font/ttf" },
      { id: "robotech", href: fontUrls.robotech, type: "font/ttf" },
      { id: "titillium", href: fontUrls.titillium, type: "font/otf" },
      { id: "titilliumBold", href: fontUrls.titilliumBold, type: "font/ttf" },
      { id: "cubellan", href: fontUrls.cubellan, type: "font/ttf" },
      { id: "ezarion", href: fontUrls.ezarion, type: "font/ttf" }
    ];
    for (const { id, href, type: type2 } of entries) {
      if (!href || String(href).includes("__KP_FONT_")) continue;
      try {
        if (head.querySelector(`link[${KEYBINDINGS_UI_FONT_PRELOAD_ATTR}="${id}"]`)) continue;
      } catch {
      }
      try {
        const link = doc.createElement("link");
        link.rel = "preload";
        link.as = "font";
        link.type = type2;
        link.href = href;
        link.crossOrigin = "anonymous";
        link.setAttribute(KEYBINDINGS_UI_FONT_PRELOAD_ATTR, id);
        head.appendChild(link);
      } catch {
      }
    }
    try {
      if (fontUrls.dosis && !String(fontUrls.dosis).includes("__KP_FONT_") && doc.fonts?.load) {
        void doc.fonts.load('10px "Dosis"');
      }
    } catch {
    }
  }
  function getKeybindingsUiCss({ zKeybindingsPopover, fontUrls } = {}) {
    const z = Number.isFinite(zKeybindingsPopover) ? zKeybindingsPopover : Number(zKeybindingsPopover);
    const zIndex = Number.isFinite(z) ? z : 2147483046;
    const urlRobotech = fontUrls && fontUrls.robotech || KEYBINDINGS_UI_FONT_PLACEHOLDERS.ROBOTECH;
    const urlTitillium = fontUrls && fontUrls.titillium || KEYBINDINGS_UI_FONT_PLACEHOLDERS.TITILLIUM;
    const urlTitilliumBold = fontUrls && fontUrls.titilliumBold || KEYBINDINGS_UI_FONT_PLACEHOLDERS.TITILLIUM_BOLD;
    const urlCubellan = fontUrls && fontUrls.cubellan || KEYBINDINGS_UI_FONT_PLACEHOLDERS.CUBELLAN;
    const urlEzarion = fontUrls && fontUrls.ezarion || KEYBINDINGS_UI_FONT_PLACEHOLDERS.EZARION;
    const urlDosis = fontUrls && fontUrls.dosis || KEYBINDINGS_UI_FONT_PLACEHOLDERS.DOSIS;
    const keyIconCss = getKeyboardKeyIconCss();
    const fontFaceCss = getKeybindingsUiFontFaceCss({
      robotech: urlRobotech,
      titillium: urlTitillium,
      titilliumBold: urlTitilliumBold,
      cubellan: urlCubellan,
      ezarion: urlEzarion,
      dosis: urlDosis
    });
    return `
/* KeyPilot Keybindings UI (injected) */
${fontFaceCss}

/* Style isolation: all keyboard rules are scoped so host page CSS won't override them */
.${KEYBINDINGS_UI_ROOT_CLASS} {
  --kp-accent: #5be2f1;
}

/* \u2500\u2500 Keyboard plate (pro app tray) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
.keyboard-visual.${KEYBINDINGS_UI_ROOT_CLASS} {
  --kp-kb-surface: #12151c;
  --kp-kb-well: #0a0c11;
  --kp-kb-rim: rgba(255, 255, 255, 0.08);
  box-sizing: border-box;
  width: 100%;
  padding: 5px;
  border-radius: 14px;
  border: 1px solid var(--kp-kb-rim);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.035) 0%, transparent 28%),
    radial-gradient(120% 80% at 50% 0%, rgba(91, 226, 241, 0.05) 0%, transparent 55%),
    linear-gradient(180deg, #161a22 0%, var(--kp-kb-surface) 45%, var(--kp-kb-well) 100%);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.06) inset,
    0 -1px 0 rgba(0, 0, 0, 0.45) inset,
    0 12px 28px rgba(0, 0, 0, 0.45),
    0 2px 0 rgba(0, 0, 0, 0.35);
  font-family: "Dosis", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  font-size: 10px;
  line-height: 1.1;
  user-select: none;
}

.${KEYBINDINGS_UI_ROOT_CLASS} .keyboard-row {
  display: flex;
  justify-content: center;
  align-items: stretch;
  margin-bottom: 7px;
  gap: 5px;
  width: 100%;
}

.${KEYBINDINGS_UI_ROOT_CLASS} .keyboard-row:last-child {
  margin-bottom: 0;
}

/* \u2500\u2500 Base keycap: low-profile chiclet (flat + lightly realistic) \u2500 */
.${KEYBINDINGS_UI_ROOT_CLASS} .key {
  --kp-key-face: #3d4454;
  --kp-key-mid: #343a48;
  --kp-key-deep: #2c313e;
  --kp-key-icon: #1a1e28;
  --kp-key-glow: transparent;

  position: relative;
  /*
   * This UI lives in the page's light DOM. Some sites apply high-priority
   * button resets (large min-heights, padding, and white focus rings), which
   * can briefly win during the early-shell \u2192 bundled-style handoff.
   */
  box-sizing: border-box !important;
  margin: 0 !important;
  font-family: inherit;
  font-size: inherit;
  line-height: inherit;
  cursor: default;
  appearance: none !important;
  -webkit-appearance: none !important;
  outline: none !important;
  color: rgba(248, 250, 252, 0.94);
  text-align: center;
  overflow: hidden;

  /* Equal geometry for alphanumeric keys */
  flex: 1 1 0 !important;
  min-width: 0 !important;
  width: 0 !important;
  height: 50px !important;
  min-height: 50px !important;
  max-height: 50px !important;
  /* Block layout: letter/name layers are absolutely positioned (not flex-flow) */
  display: block !important;
  padding: 0 !important;
  border-radius: var(--kp-key-effective-radius, var(--kp-radius-key, 7px));
  clip-path: var(--kp-key-clip, none);

  /*
   * Low-profile key: nearly flat face, thin rim, soft ground shadow.
   * Reads more like a real chiclet key than a heavy 3D bevel.
   */
  border: var(--kp-key-border, 1px solid rgba(0, 0, 0, 0.4));
  border-top-color: rgba(255, 255, 255, 0.1);
  border-bottom-color: rgba(0, 0, 0, 0.5);

  background:
    var(--kp-key-shade-layer, linear-gradient(180deg,
      rgba(255, 255, 255, 0.07) 0%,
      rgba(255, 255, 255, 0.02) 18%,
      transparent 42%)),
    linear-gradient(180deg,
      var(--kp-key-face) 0%,
      var(--kp-key-mid) 70%,
      var(--kp-key-deep) 100%);

  box-shadow:
    0 1px 0 rgba(0, 0, 0, 0.45),
    0 2px 4px rgba(0, 0, 0, 0.22),
    inset 0 1px 0 rgba(255, 255, 255, 0.1),
    inset 0 -1px 0 rgba(0, 0, 0, 0.18);

  transition:
    transform 100ms ease,
    box-shadow 100ms ease,
    filter 100ms ease,
    border-color 100ms ease,
    background 100ms ease;
}

/* Upgrade: corner-shape so key borders follow cut corners (clip-path baseline above).
 * Cut themes force --kp-key-effective-radius: 0px for the clip-path path; the
 * upgrade must win so bevel length comes from --kp-key-shape-radius. */
@supports (corner-shape: bevel) {
  .${KEYBINDINGS_UI_ROOT_CLASS} .key {
    clip-path: none !important;
    border-radius: var(--kp-key-shape-radius, var(--kp-key-effective-radius, var(--kp-radius-key, 7px))) !important;
    corner-shape: var(--kp-key-corner-shape, round);
  }
}

/* Minimal face sheen (not a tall sculpted plate) */
.${KEYBINDINGS_UI_ROOT_CLASS} .key::before {
  content: '';
  position: absolute;
  z-index: 0;
  top: 1px;
  left: 2px;
  right: 2px;
  height: 38%;
  border-radius: 5px 5px 40% 40%;
  background: linear-gradient(180deg,
    rgba(255, 255, 255, 0.08) 0%,
    rgba(255, 255, 255, 0.02) 60%,
    transparent 100%);
  pointer-events: none;
  opacity: var(--kp-key-sheen-opacity, 1);
}

/* Prevent UA :disabled washout on edit-readonly keycaps (still non-interactive). */
.${KEYBINDINGS_UI_ROOT_CLASS} .key:disabled {
  opacity: 1;
  color: inherit;
  cursor: default;
}

.${KEYBINDINGS_UI_ROOT_CLASS} [data-kp-action-id] {
  cursor: pointer;
}

.${KEYBINDINGS_UI_ROOT_CLASS} [data-kp-action-id]:hover {
  filter: brightness(1.07);
  border-top-color: rgba(255, 255, 255, 0.14);
  box-shadow:
    0 1px 0 rgba(0, 0, 0, 0.4),
    0 3px 8px rgba(0, 0, 0, 0.28),
    0 0 0 1px rgba(255, 255, 255, 0.04),
    0 0 10px var(--kp-key-glow),
    inset 0 1px 0 rgba(255, 255, 255, 0.12),
    inset 0 -1px 0 rgba(0, 0, 0, 0.16);
}

/*
 * Layered key legend (independent of each other):
 * - .key-main  = action name \u2014 upper band only
 * - .key-label = physical key letter \u2014 always pinned bottom-center
 * - .key-text  = special / unassigned glyph
 */
.${KEYBINDINGS_UI_ROOT_CLASS} .key > .key-main {
  position: absolute;
  z-index: 1;
  /* Pin action name to the top of the key face */
  top: 3px;
  left: 2px;
  right: 2px;
  bottom: auto;
  /* Leave a fixed bottom band for the letter; never push it */
  height: auto;
  max-height: 30px;
  box-sizing: border-box;
  margin: 0;
  padding: 0 1px;
  font-size: 11px;
  font-weight: 650;
  letter-spacing: 0.02em;
  line-height: 1.1;
  opacity: 0.9;
  text-transform: uppercase;
  color: rgba(248, 250, 252, 0.94);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.4);
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  word-break: break-word;
  hyphens: auto;
  text-align: center;
  pointer-events: none;
}

/* Letter / chrome labels: fixed bottom layer on every key */
.${KEYBINDINGS_UI_ROOT_CLASS} .key > .key-label,
.${KEYBINDINGS_UI_ROOT_CLASS} .key > .key-text {
  position: absolute;
  z-index: 2;
  left: 0;
  right: 0;
  bottom: 3px;
  top: auto;
  transform: none;
  box-sizing: border-box;
  margin: 0;
  padding: 0 2px;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  line-height: 1;
  text-align: center;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.45);
  pointer-events: none;
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key > .key-label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.03em;
  color: var(--kp-accent, #5be2f1);
}

/* Edit-mode slot delete: fixed overlay on the keycap, independent of .key-main. */
.${KEYBINDINGS_UI_ROOT_CLASS} .key > .kp-key-delete-overlay {
  position: absolute;
  inset: 0;
  z-index: 8;
  pointer-events: none;
}
.${KEYBINDINGS_UI_ROOT_CLASS} .key > .kp-key-delete-overlay > .kp-key-delete {
  position: absolute;
  top: 1px;
  right: 1px;
  left: auto;
  bottom: auto;
  width: 14px;
  height: 14px;
  min-width: 14px;
  min-height: 14px;
  margin: 0;
  padding: 0;
  border: none;
  border-radius: 3px;
  display: none;
  align-items: center;
  justify-content: center;
  line-height: 12px;
  font-size: 11px;
  font-weight: 700;
  cursor: pointer;
  color: rgba(248, 250, 252, 0.95);
  background: rgba(220, 50, 50, 0.85);
  pointer-events: auto;
}
.${KEYBINDINGS_UI_ROOT_CLASS} .key:hover > .kp-key-delete-overlay > .kp-key-delete,
.${KEYBINDINGS_UI_ROOT_CLASS} .key:focus-within > .kp-key-delete-overlay > .kp-key-delete {
  display: flex;
}
.${KEYBINDINGS_UI_ROOT_CLASS} .key > .kp-key-delete-overlay > .kp-key-delete:hover {
  background: rgba(255, 70, 70, 1);
}

/* Edit-mode plate hatch (same steel lines as Keyboard Layout Config). */
.keyboard-visual.${KEYBINDINGS_UI_ROOT_CLASS}.kp-kb-edit-hatch {
  background:
    var(--kp-hatch-edit, repeating-linear-gradient(-45deg, rgba(180, 200, 220, 0.08) 0px, rgba(180, 200, 220, 0.08) 1px, transparent 1px, transparent 7px)),
    linear-gradient(180deg, rgba(255, 255, 255, 0.06) 0%, transparent 28%),
    radial-gradient(120% 80% at 50% 0%, rgba(91, 226, 241, 0.07) 0%, transparent 55%),
    linear-gradient(180deg, #222833 0%, #1a1f28 45%, #13161e 100%) !important;
}

/* Special chrome labels (Tab/Caps/\u2026) */
.${KEYBINDINGS_UI_ROOT_CLASS} .key > .key-text {
  font-size: 10px;
  font-weight: 650;
  letter-spacing: 0.02em;
  color: rgba(248, 250, 252, 0.92);
}

/* Unassigned letter-only keys: still bottom-centered (same letter layer) */
.${KEYBINDINGS_UI_ROOT_CLASS} .key:not([data-kp-action-id]):not(.key-tab):not(.key-caps):not(.key-enter):not(.key-shift):not(.key-backspace) > .key-text {
  bottom: 3px;
  top: auto;
  transform: none;
  font-size: 12px;
  font-weight: 700;
  color: var(--kp-accent, #5be2f1);
}

/* Special keys: wider, same height */
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-tab,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-caps,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-enter,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-shift,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-backspace {
  height: 50px;
  min-height: 50px;
  max-height: 50px;
  width: auto;
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-tab { flex: 1.25 1 0; }
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-caps { flex: 1.35 1 0; }
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-enter { flex: 1.55 1 0; }
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-shift { flex: 1.65 1 0; }
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-backspace { flex: 1.55 1 0; }

/* \u2500\u2500 Color families (muted pro tints + darker icon color) \u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-activate,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-activate-new,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-activate-new-over,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-tab-right,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-new-tab,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-open-popover,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-preview-popover,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-page-up,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-page-down,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-page-up-instant,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-page-down-instant,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-help,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-omnibox {
  ${keycapMaterial({
      face: "#2f8f5b",
      mid: "#247a4c",
      deep: "#17633a",
      icon: "#0d3a22",
      glow: "rgba(34, 197, 94, 0.18)"
    })}
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-back,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-forward,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-scroll-top,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-scroll-bottom {
  ${keycapMaterial({
      face: "#2f7ea8",
      mid: "#256b92",
      deep: "#1a5475",
      icon: "#0d3044",
      glow: "rgba(56, 189, 248, 0.16)"
    })}
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-delete,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-close-tab {
  ${keycapMaterial({
      face: "#b84a4a",
      mid: "#9e3b3b",
      deep: "#7a2b2b",
      icon: "#401616",
      glow: "rgba(248, 113, 113, 0.16)"
    })}
}

/* Selection tools: indigo family (distinct from green activate / blue nav / amber unused) */
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-highlight,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-rect-highlight {
  ${keycapMaterial({
      face: "#5b6fd4",
      mid: "#4a5cbb",
      deep: "#3949a0",
      icon: "#1a2258",
      glow: "rgba(99, 102, 241, 0.18)"
    })}
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-scroll {
  ${keycapMaterial({
      face: "#7a5638",
      mid: "#63452c",
      deep: "#4a3320",
      icon: "#26180f",
      glow: "rgba(180, 120, 70, 0.12)"
    })}
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-purple,
.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-cols {
  ${keycapMaterial({
      face: "#7a4ab8",
      mid: "#663d9e",
      deep: "#4e2e7a",
      icon: "#281646",
      glow: "rgba(167, 139, 250, 0.14)"
    })}
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-orange {
  ${keycapMaterial({
      face: "#c97a28",
      mid: "#a8641e",
      deep: "#834f16",
      icon: "#3f250a",
      glow: "rgba(255, 165, 0, 0.14)"
    })}
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-launcher-orange {
  ${keycapMaterial({
      face: "#a06a3a",
      mid: "#85562e",
      deep: "#664122",
      icon: "#322010",
      glow: "rgba(184, 115, 51, 0.12)"
    })}
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-settings-dark {
  ${keycapMaterial({
      face: "#3a4250",
      mid: "#2a313c",
      deep: "#1a1f28",
      icon: "#0c0f14",
      glow: "rgba(148, 163, 184, 0.1)"
    })}
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-gray {
  ${keycapMaterial({
      face: "#5a6270",
      mid: "#484f5c",
      deep: "#343a45",
      icon: "#1a1e26",
      glow: "rgba(148, 163, 184, 0.1)"
    })}
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-launcher {
  ${keycapMaterial({
      face: "#2a8fa3",
      mid: "#22788a",
      deep: "#185e6d",
      icon: "#0c343c",
      glow: "rgba(34, 211, 238, 0.14)"
    })}
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-hatched {
  position: relative;
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-hatched::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-image:
    repeating-linear-gradient(
      45deg,
      rgba(200, 200, 200, 0.4) 0px,
      rgba(200, 200, 200, 0.4) 1px,
      transparent 1px,
      transparent 4px
    );
  pointer-events: none;
  border-radius: 4px;
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-radial-overlay::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: radial-gradient(circle at center,
    rgba(150, 150, 150, 0.3) 0%,
    rgba(120, 120, 120, 0.25) 30%,
    transparent 70%);
  pointer-events: none;
  border-radius: 4px;
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-checkerboard-overlay::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-image:
    radial-gradient(circle at 25% 25%, rgba(160, 160, 160, 0.3) 2px, transparent 2px),
    radial-gradient(circle at 75% 75%, rgba(160, 160, 160, 0.3) 2px, transparent 2px);
  background-size: 8px 8px;
  pointer-events: none;
  border-radius: 4px;
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-stripes-overlay::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-image:
    linear-gradient(90deg, transparent 0%, transparent 40%, rgba(180, 180, 180, 0.25) 40%, rgba(180, 180, 180, 0.25) 60%, transparent 60%);
  background-size: 6px 100%;
  pointer-events: none;
  border-radius: 4px;
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-crosshatch-overlay::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-image:
    repeating-linear-gradient(45deg, rgba(170, 170, 170, 0.25) 0px, rgba(170, 170, 170, 0.25) 1px, transparent 1px, transparent 4px),
    repeating-linear-gradient(-45deg, rgba(170, 170, 170, 0.25) 0px, rgba(170, 170, 170, 0.25) 1px, transparent 1px, transparent 4px);
  pointer-events: none;
  border-radius: 4px;
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-noise-overlay::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background:
    linear-gradient(45deg, rgba(80, 80, 80, 0.2) 25%, transparent 25%),
    linear-gradient(-45deg, rgba(80, 80, 80, 0.2) 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, rgba(80, 80, 80, 0.2) 75%),
    linear-gradient(-45deg, transparent 75%, rgba(80, 80, 80, 0.2) 75%);
  background-size: 4px 4px;
  pointer-events: none;
  border-radius: 4px;
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-conic-overlay::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: conic-gradient(
    from 45deg,
    rgba(100, 100, 100, 0.2) 0deg,
    rgba(120, 120, 120, 0.25) 90deg,
    rgba(140, 140, 140, 0.2) 180deg,
    rgba(100, 100, 100, 0.2) 360deg
  );
  pointer-events: none;
  border-radius: 4px;
}

.${KEYBINDINGS_UI_ROOT_CLASS} .key.key-dashed-overlay::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  box-shadow: inset 0 0 0 2px rgba(140, 140, 140, 0.4);
  pointer-events: none;
  border-radius: 4px;
}

/*
 * Keydown/keyup press feedback is a dedicated .key-press-overlay element
 * (see ensureKeyPressOverlay / setKeyPressedState). Avoid filter/transform
 * on the key itself \u2014 those were hard to see on colored keycaps.
 */
.${KEYBINDINGS_UI_ROOT_CLASS} .key.kp-key-pressed {
  outline: none;
}

/*
 * Text / typing mode:
 * - Default keys: plain typing chiclets (no function color/icon/label) + crisp orange outline.
 * - Countdown-armed actions (.kp-key-text-mode-active): full function chrome restored
 *   (color fill, FA icon, action label) with the key's own material glow (green for
 *   Click Element). The live-action set is TEXT_MODE_COUNTDOWN_ACTION_IDS.
 */
.${KEYBINDINGS_UI_ROOT_CLASS}.kp-text-mode-filter .key:not(.kp-key-text-mode-active) {
  /* Orange-cast chiclet face (same family as titlebar / control-strip text mode). */
  --kp-key-face: #5a4834;
  --kp-key-mid: #4a3a28;
  --kp-key-deep: #3a2c1c;
  --kp-key-icon: #1a1e28;
  --kp-key-glow: transparent;
  opacity: 1;
  filter: none;
  border-color: rgba(255, 140, 0, 0.85) !important;
  border-top-color: rgba(255, 170, 70, 0.9) !important;
  border-bottom-color: rgba(200, 100, 0, 0.9) !important;
  /* Crisp orange outline \u2014 no soft glow bloom. */
  box-shadow:
    0 0 0 1px rgba(255, 140, 0, 0.8),
    0 1px 0 rgba(0, 0, 0, 0.45),
    0 2px 4px rgba(0, 0, 0, 0.22),
    inset 0 1px 0 rgba(255, 200, 120, 0.12),
    inset 0 -1px 0 rgba(0, 0, 0, 0.18) !important;
  background:
    linear-gradient(180deg,
      rgba(255, 140, 0, 0.18) 0%,
      rgba(255, 140, 0, 0.06) 28%,
      transparent 55%),
    linear-gradient(180deg,
      rgba(255, 255, 255, 0.07) 0%,
      rgba(255, 255, 255, 0.02) 18%,
      transparent 42%),
    linear-gradient(180deg,
      var(--kp-key-face) 0%,
      var(--kp-key-mid) 70%,
      var(--kp-key-deep) 100%);
}
.${KEYBINDINGS_UI_ROOT_CLASS}.kp-text-mode-filter .key:not(.kp-key-text-mode-active) > .key-bg-icon,
.${KEYBINDINGS_UI_ROOT_CLASS}.kp-text-mode-filter .key:not(.kp-key-text-mode-active) > .key-main {
  display: none !important;
}
/* Pattern overlays (hatch / checkerboard / \u2026) \u2014 plain face only while typing. */
.${KEYBINDINGS_UI_ROOT_CLASS}.kp-text-mode-filter .key:not(.kp-key-text-mode-active)::after {
  display: none !important;
}
/*
 * Center physical letters on every plain key. !important beats the higher-specificity
 * unassigned key-text rule (cyan / bottom-pinned) that would otherwise leave empty
 * keys like U O [ ] N , / looking different from assigned key-label keys.
 */
.${KEYBINDINGS_UI_ROOT_CLASS}.kp-text-mode-filter .key:not(.kp-key-text-mode-active) > .key-label,
.${KEYBINDINGS_UI_ROOT_CLASS}.kp-text-mode-filter .key:not(.kp-key-text-mode-active) > .key-text {
  top: 50% !important;
  bottom: auto !important;
  left: 0 !important;
  right: 0 !important;
  transform: translateY(-50%) !important;
  font-size: 14px !important;
  font-weight: 700 !important;
  letter-spacing: 0.02em !important;
  color: rgba(248, 250, 252, 0.94) !important;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.45) !important;
}
.${KEYBINDINGS_UI_ROOT_CLASS}.kp-text-mode-filter .key.kp-key-text-mode-disabled {
  pointer-events: none;
  cursor: default;
}
/* Countdown-live keys: keep assigned color fill / icon / labels; emphasize material glow. */
.${KEYBINDINGS_UI_ROOT_CLASS}.kp-text-mode-filter .key.kp-key-text-mode-active {
  opacity: 1;
  filter: none;
  z-index: 2;
  outline: none;
  pointer-events: auto;
  cursor: pointer;
  box-shadow:
    0 0 0 2px rgba(34, 197, 94, 0.5),
    0 0 14px 3px var(--kp-key-glow, rgba(34, 197, 94, 0.35)),
    0 0 24px 4px rgba(34, 197, 94, 0.22),
    0 1px 0 rgba(0, 0, 0, 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.14),
    inset 0 -1px 0 rgba(0, 0, 0, 0.18) !important;
}

/*
 * Link-hover hint: when the page pointer is over a link and the keyboard
 * reference is open, highlight the keys that activate / open that link.
 */
.${KEYBINDINGS_UI_ROOT_CLASS} .key.kp-key-link-hint {
  z-index: 2;
  outline: none;
  filter: brightness(1.12) saturate(1.15);
  border-color: rgba(91, 226, 241, 0.85) !important;
  border-top-color: rgba(180, 245, 255, 0.95) !important;
  border-bottom-color: rgba(40, 180, 200, 0.9) !important;
  box-shadow:
    0 0 0 2px rgba(91, 226, 241, 0.55),
    0 0 14px 3px rgba(91, 226, 241, 0.55),
    0 0 28px 6px rgba(56, 189, 248, 0.28),
    0 1px 0 rgba(0, 0, 0, 0.35),
    inset 0 0 0 1px rgba(255, 255, 255, 0.22),
    inset 0 1px 0 rgba(255, 255, 255, 0.18) !important;
  animation: kp-key-link-hint-pulse 1.35s ease-in-out infinite;
}

@keyframes kp-key-link-hint-pulse {
  0%, 100% {
    box-shadow:
      0 0 0 2px rgba(91, 226, 241, 0.5),
      0 0 12px 2px rgba(91, 226, 241, 0.45),
      0 0 22px 4px rgba(56, 189, 248, 0.22),
      0 1px 0 rgba(0, 0, 0, 0.35),
      inset 0 0 0 1px rgba(255, 255, 255, 0.18),
      inset 0 1px 0 rgba(255, 255, 255, 0.14);
  }
  50% {
    box-shadow:
      0 0 0 3px rgba(120, 240, 255, 0.75),
      0 0 18px 5px rgba(91, 226, 241, 0.7),
      0 0 34px 10px rgba(56, 189, 248, 0.38),
      0 1px 0 rgba(0, 0, 0, 0.35),
      inset 0 0 0 1px rgba(255, 255, 255, 0.28),
      inset 0 1px 0 rgba(255, 255, 255, 0.2);
  }
}

/* Popover (tooltip) \u2014 matches the hovered key material via CSS vars.
 * Uses the HTML Popover API (top layer) so it can escape the keyboard panel's
 * overflow:hidden and sit above/below keys outside the panel bounds.
 *
 * Do NOT set inset with !important: that locks left/top longhands and beats
 * JS style.left/top, pinning every tooltip at the viewport origin. Override UA
 * popover defaults with non-important longhands + margin:0 instead. */
.kp-keybindings-popover {
  --kp-key-face: #3d4454;
  --kp-key-mid: #343a48;
  --kp-key-deep: #2c313e;
  --kp-key-icon: #1a1e28;

  position: fixed !important;
  /* Kill UA popover centering (inset 0 / margin auto) without locking longhands. */
  margin: 0 !important;
  top: auto;
  right: auto;
  bottom: auto;
  left: auto;
  width: max-content;
  height: fit-content;
  overflow: visible;
  box-sizing: border-box;

  z-index: ${zIndex};
  max-width: min(300px, calc(100vw - 20px));
  min-width: 160px;
  color: rgba(248, 250, 252, 0.95);
  border-radius: 8px;
  padding: 10px 12px;
  font-size: 12px;
  line-height: 1.35;
  pointer-events: none; /* hover tooltips shouldn't steal pointer */

  /* Same low-profile key face treatment as .key */
  border: 1px solid rgba(0, 0, 0, 0.4);
  border-top-color: rgba(255, 255, 255, 0.12);
  border-bottom-color: rgba(0, 0, 0, 0.5);
  background:
    linear-gradient(180deg,
      rgba(255, 255, 255, 0.08) 0%,
      rgba(255, 255, 255, 0.02) 18%,
      transparent 42%),
    linear-gradient(180deg,
      var(--kp-key-face) 0%,
      var(--kp-key-mid) 70%,
      var(--kp-key-deep) 100%);
  box-shadow:
    0 1px 0 rgba(0, 0, 0, 0.45),
    0 10px 24px rgba(0, 0, 0, 0.4),
    inset 0 1px 0 rgba(255, 255, 255, 0.1),
    inset 0 -1px 0 rgba(0, 0, 0, 0.18);
}

/* Closed: both attribute + Popover API states */
.kp-keybindings-popover:not(:popover-open):not([data-kp-popover-open="true"]),
.kp-keybindings-popover[hidden] {
  display: none !important;
}

/* Open via Popover API or legacy fallback flag */
.kp-keybindings-popover:popover-open,
.kp-keybindings-popover[data-kp-popover-open="true"] {
  display: block;
  /* Re-assert after :popover-open (UA may reapply margin/inset). */
  margin: 0 !important;
  position: fixed !important;
}

.kp-keybindings-popover .kp-popover-head {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 0 0 6px 0;
}

.kp-keybindings-popover .kp-popover-icon {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  border-radius: 6px;
  border: 1px solid rgba(0, 0, 0, 0.3);
  border-top-color: rgba(255, 255, 255, 0.1);
  /* Glyph uses same darker icon color as keys */
  background-color: var(--kp-key-icon);
  background-image: none;
  background-repeat: no-repeat;
  background-position: center;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: 62% 62%;
  mask-size: 62% 62%;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    inset 0 -1px 0 rgba(0, 0, 0, 0.2);
}

.kp-keybindings-popover .kp-popover-icon[hidden] {
  display: none;
}

.kp-keybindings-popover .kp-popover-title-wrap {
  min-width: 0;
  flex: 1 1 auto;
}

.kp-keybindings-popover .kp-popover-title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin: 0 0 3px 0;
}

.kp-keybindings-popover .kp-popover-title {
  font-weight: 700;
  margin: 0;
  min-width: 0;
  color: rgba(248, 250, 252, 0.96);
  letter-spacing: 0.01em;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.35);
}

.kp-keybindings-popover .kp-popover-settings-hint {
  flex: 0 0 auto;
  font-size: 10px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.82);
  letter-spacing: 0.02em;
  white-space: nowrap;
  line-height: 1.3;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(0, 0, 0, 0.32);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
}

.kp-keybindings-popover .kp-popover-settings-hint[hidden] {
  display: none !important;
}

.kp-keybindings-popover .kp-popover-keys {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
  color: rgba(255, 255, 255, 0.72);
  margin: 0;
  font-size: 11px;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
}

.kp-keybindings-popover .kp-popover-desc {
  margin: 0;
  color: rgba(248, 250, 252, 0.9);
  opacity: 0.95;
  font-size: 11.5px;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
}

.kp-keybindings-popover[data-kp-popover-pinned="true"] {
  pointer-events: auto;
  max-width: min(340px, calc(100vw - 20px));
  background:
    linear-gradient(180deg,
      rgba(0, 0, 0, 0.4) 0%,
      rgba(0, 0, 0, 0.55) 100%),
    linear-gradient(180deg,
      rgba(255, 255, 255, 0.06) 0%,
      rgba(255, 255, 255, 0.01) 18%,
      transparent 42%),
    linear-gradient(180deg,
      var(--kp-key-face) 0%,
      var(--kp-key-mid) 70%,
      var(--kp-key-deep) 100%);
  outline: 1.5px solid color-mix(in srgb, var(--kp-key-face) 45%, white);
  outline-offset: 0;
  border-color: color-mix(in srgb, var(--kp-key-face) 35%, black);
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.55),
    0 10px 24px rgba(0, 0, 0, 0.5),
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    inset 0 -1px 0 rgba(0, 0, 0, 0.28);
}

.kp-keybindings-popover .kp-popover-settings {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.kp-keybindings-popover .kp-popover-settings[hidden] {
  display: none !important;
}

.kp-popover-setting-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.kp-popover-setting-label {
  font-size: 10px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(248, 250, 252, 0.55);
}

.kp-popover-mode-switch {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.kp-popover-mode-btn {
  appearance: none;
  flex: 1 1 auto;
  min-width: 0;
  border: 1px solid rgba(0, 0, 0, 0.4);
  border-radius: 6px;
  padding: 5px 8px;
  font: inherit;
  font-size: 11px;
  color: rgba(248, 250, 252, 0.92);
  background: rgba(0, 0, 0, 0.22);
  cursor: pointer;
}

.kp-popover-mode-btn[aria-pressed="true"] {
  background: rgba(255, 255, 255, 0.16);
  border-color: rgba(255, 255, 255, 0.28);
}

.kp-popover-config-btn {
  appearance: none;
  align-self: flex-start;
  border: 1px solid rgba(0, 0, 0, 0.4);
  border-radius: 6px;
  padding: 5px 10px;
  font: inherit;
  font-size: 11px;
  color: inherit;
  background: rgba(0, 0, 0, 0.28);
  cursor: pointer;
}

.kp-keybindings-popover::before {
  content: "";
  position: absolute;
  width: 0;
  height: 0;
  left: var(--kp-arrow-left, 18px);
  border: 9px solid transparent;
}

.kp-keybindings-popover[data-placement="top"]::before {
  top: 100%;
  border-top-color: rgba(0, 0, 0, 0.45);
}

.kp-keybindings-popover[data-placement="top"]::after {
  content: "";
  position: absolute;
  width: 0;
  height: 0;
  left: var(--kp-arrow-left, 18px);
  top: calc(100% - 1px);
  border: 8px solid transparent;
  border-top-color: var(--kp-key-deep);
}

.kp-keybindings-popover[data-kp-popover-pinned="true"][data-placement="top"]::after {
  border-top-color: color-mix(in srgb, var(--kp-key-deep) 45%, black);
}

.kp-keybindings-popover[data-kp-popover-pinned="true"][data-placement="top"]::before {
  border-top-color: color-mix(in srgb, var(--kp-key-face) 45%, white);
}

.kp-keybindings-popover[data-placement="bottom"]::before {
  bottom: 100%;
  border-bottom-color: rgba(255, 255, 255, 0.12);
}

.kp-keybindings-popover[data-placement="bottom"]::after {
  content: "";
  position: absolute;
  width: 0;
  height: 0;
  left: var(--kp-arrow-left, 18px);
  bottom: calc(100% - 1px);
  border: 8px solid transparent;
  border-bottom-color: var(--kp-key-face);
}

.kp-keybindings-popover[data-kp-popover-pinned="true"][data-placement="bottom"]::after {
  border-bottom-color: color-mix(in srgb, var(--kp-key-face) 45%, black);
}

.kp-keybindings-popover[data-kp-popover-pinned="true"][data-placement="bottom"]::before {
  border-bottom-color: color-mix(in srgb, var(--kp-key-face) 45%, white);
}

/* Font Awesome-style faded key background icons (behind white labels) */
${keyIconCss}

/*
 * TEMP suspended: Floating Keyboard Reference flex-scale keys with panel resize.
 * Keys use the global fixed 50px keycap rules again. Re-enable together with
 * makePopoverResizable in floating-keyboard-help.js when resuming this work.
 *
 * .kp-floating-keyboard-help .kp-floating-keyboard-help__keyboard {
 *   box-sizing: border-box;
 *   flex: 1 1 auto;
 *   min-height: 0;
 *   width: 100%;
 *   height: 100%;
 *   display: flex;
 *   flex-direction: column;
 * }
 * .kp-floating-keyboard-help .keyboard-visual.${KEYBINDINGS_UI_ROOT_CLASS} {
 *   flex: 1 1 auto;
 *   min-height: 0;
 *   width: 100%;
 *   height: 100%;
 *   display: flex;
 *   flex-direction: column;
 *   gap: 7px;
 *   padding: clamp(4px, 1.2%, 10px);
 * }
 * .kp-floating-keyboard-help .${KEYBINDINGS_UI_ROOT_CLASS} .keyboard-row {
 *   flex: 1 1 0;
 *   min-height: 0;
 *   margin-bottom: 0;
 *   gap: clamp(3px, 0.7%, 7px);
 *   align-items: stretch;
 * }
 * .kp-floating-keyboard-help .${KEYBINDINGS_UI_ROOT_CLASS} .key,
 * .kp-floating-keyboard-help .${KEYBINDINGS_UI_ROOT_CLASS} .key.key-tab,
 * .kp-floating-keyboard-help .${KEYBINDINGS_UI_ROOT_CLASS} .key.key-caps,
 * .kp-floating-keyboard-help .${KEYBINDINGS_UI_ROOT_CLASS} .key.key-enter,
 * .kp-floating-keyboard-help .${KEYBINDINGS_UI_ROOT_CLASS} .key.key-shift,
 * .kp-floating-keyboard-help .${KEYBINDINGS_UI_ROOT_CLASS} .key.key-backspace {
 *   height: 100%;
 *   min-height: 0;
 *   max-height: none;
 *   border-radius: clamp(5px, 12%, 10px);
 *   container-type: size;
 *   container-name: kp-key;
 * }
 * .kp-floating-keyboard-help .${KEYBINDINGS_UI_ROOT_CLASS} .key > .key-main {
 *   font-size: clamp(9px, 18cqh, 15px);
 *   max-height: 55%;
 *   top: clamp(2px, 8cqh, 8px);
 * }
 * .kp-floating-keyboard-help .${KEYBINDINGS_UI_ROOT_CLASS} .key > .key-label {
 *   font-size: clamp(10px, 22cqh, 18px);
 *   bottom: clamp(2px, 8cqh, 8px);
 * }
 * .kp-floating-keyboard-help .${KEYBINDINGS_UI_ROOT_CLASS} .key > .key-text {
 *   font-size: clamp(9px, 18cqh, 15px);
 *   bottom: clamp(2px, 8cqh, 8px);
 * }
 */
`;
  }

  // src/ui/key-action-settings.js
  init_constants();

  // src/utils/panel-position.js
  var PANEL_POSITION_MARGIN_PX = 8;
  var PANEL_SNAP_THRESHOLD_PX = 56;
  var PANEL_DRAG_MOVE_THRESHOLD_PX = 3;
  function makePanelResizable(panel, opts = {}) {
    if (!panel || !panel.style) return () => {
    };
    const mount = opts.mount || panel.shadowRoot || panel;
    if (!mount?.appendChild) return () => {
    };
    const minWidth = Math.max(1, Number(opts.minWidth) || 320);
    const minHeight = Math.max(1, Number(opts.minHeight) || 220);
    const margin = Math.max(0, Number(opts.margin) || PANEL_POSITION_MARGIN_PX);
    const edges = Array.isArray(opts.edges) && opts.edges.length ? opts.edges : ["right", "bottom", "bottom-right"];
    const handles = [];
    const clampSize = (width, height) => {
      const rect = panel.getBoundingClientRect();
      const viewport = getViewportSize();
      return {
        width: Math.round(Math.max(minWidth, Math.min(width, Math.max(minWidth, viewport.width - rect.left - margin)))),
        height: Math.round(Math.max(minHeight, Math.min(height, Math.max(minHeight, viewport.height - rect.top - margin))))
      };
    };
    const applySize = (width, height) => {
      const size = clampSize(width, height);
      panel.style.width = `${size.width}px`;
      panel.style.height = `${size.height}px`;
      try {
        opts.onResize?.(size);
      } catch {
      }
      return size;
    };
    for (const edge of edges) {
      if (!["right", "bottom", "bottom-right"].includes(edge)) continue;
      const handle = document.createElement("div");
      handle.setAttribute("role", "separator");
      handle.setAttribute("aria-orientation", edge === "right" ? "vertical" : "horizontal");
      handle.setAttribute("aria-label", "Resize window");
      handle.title = "Resize window";
      handle.tabIndex = 0;
      Object.assign(handle.style, {
        position: "absolute",
        zIndex: "2",
        userSelect: "none",
        WebkitUserSelect: "none",
        touchAction: "none",
        ...edge === "right" ? { top: "8px", right: "0", bottom: "8px", width: "8px", cursor: "ew-resize" } : edge === "bottom" ? { right: "8px", bottom: "0", left: "8px", height: "8px", cursor: "ns-resize" } : {
          right: "0",
          bottom: "0",
          width: "14px",
          height: "14px",
          cursor: "nwse-resize",
          backgroundImage: "repeating-linear-gradient(135deg, transparent 0 3px, rgba(220,220,220,0.42) 3px 4px, transparent 4px 6px)",
          backgroundClip: "content-box",
          padding: "3px",
          boxSizing: "border-box"
        }
      });
      handle.addEventListener("pointerdown", (event) => {
        if (event.button !== 0) return;
        event.preventDefault();
        event.stopPropagation();
        const start = panel.getBoundingClientRect();
        const startX = event.clientX;
        const startY = event.clientY;
        try {
          handle.setPointerCapture(event.pointerId);
        } catch {
        }
        const onMove = (moveEvent) => {
          const width = start.width + (edge === "bottom" ? 0 : moveEvent.clientX - startX);
          const height = start.height + (edge === "right" ? 0 : moveEvent.clientY - startY);
          applySize(width, height);
        };
        const finish = () => {
          const rect = panel.getBoundingClientRect();
          const size = { width: Math.round(rect.width), height: Math.round(rect.height) };
          try {
            opts.onResizeEnd?.(size);
          } catch {
          }
          try {
            handle.releasePointerCapture(event.pointerId);
          } catch {
          }
          handle.removeEventListener("pointermove", onMove);
          handle.removeEventListener("pointerup", finish);
          handle.removeEventListener("pointercancel", finish);
        };
        handle.addEventListener("pointermove", onMove);
        handle.addEventListener("pointerup", finish);
        handle.addEventListener("pointercancel", finish);
      }, true);
      handle.addEventListener("keydown", (event) => {
        const horizontal = event.key === "ArrowLeft" || event.key === "ArrowRight";
        const vertical = event.key === "ArrowUp" || event.key === "ArrowDown";
        if (!horizontal && !vertical) return;
        if (edge === "right" && !horizontal || edge === "bottom" && !vertical) return;
        event.preventDefault();
        const step = event.shiftKey ? 40 : 10;
        const rect = panel.getBoundingClientRect();
        const size = applySize(
          rect.width + (event.key === "ArrowRight" ? step : event.key === "ArrowLeft" ? -step : 0),
          rect.height + (event.key === "ArrowDown" ? step : event.key === "ArrowUp" ? -step : 0)
        );
        try {
          opts.onResizeEnd?.(size);
        } catch {
        }
      }, true);
      mount.appendChild(handle);
      handles.push(handle);
    }
    return () => {
      for (const handle of handles) {
        try {
          handle.remove();
        } catch {
        }
      }
    };
  }
  var PANEL_ANCHORS = Object.freeze([
    "top-left",
    "top-center",
    "top-right",
    "middle-left",
    "middle-right",
    "bottom-left",
    "bottom-center",
    "bottom-right"
  ]);
  function normalizePanelAnchor(raw) {
    if (typeof raw !== "string") return null;
    const a = raw.trim();
    return PANEL_ANCHORS.includes(
      /** @type {PanelAnchor} */
      a
    ) ? (
      /** @type {PanelAnchor} */
      a
    ) : null;
  }
  function normalizePanelPositionState(raw, fallback = null) {
    const fb = fallback && typeof fallback === "object" ? fallback : null;
    if (!raw || typeof raw !== "object") {
      return fb ? {
        left: Number.isFinite(fb.left) ? fb.left : void 0,
        top: Number.isFinite(fb.top) ? fb.top : void 0,
        anchor: normalizePanelAnchor(fb.anchor)
      } : null;
    }
    const left = Number(raw.left);
    const top = Number(raw.top);
    const anchor = normalizePanelAnchor(raw.anchor);
    const hasLeft = Number.isFinite(left);
    const hasTop = Number.isFinite(top);
    if (!anchor && !hasLeft && !hasTop) {
      return fb ? {
        left: Number.isFinite(fb.left) ? fb.left : void 0,
        top: Number.isFinite(fb.top) ? fb.top : void 0,
        anchor: normalizePanelAnchor(fb.anchor)
      } : null;
    }
    const out = {};
    if (hasLeft) out.left = left;
    if (hasTop) out.top = top;
    if (anchor) out.anchor = anchor;
    else if (raw.anchor === null) out.anchor = null;
    return out;
  }
  function getViewportSize(opts = null) {
    let fallbackW = 0;
    let fallbackH = 0;
    if (typeof window !== "undefined") {
      try {
        const de = document.documentElement;
        fallbackW = Math.max(de?.clientWidth || 0, window.innerWidth || 0);
        fallbackH = Math.max(de?.clientHeight || 0, window.innerHeight || 0);
      } catch {
        fallbackW = window.innerWidth || 0;
        fallbackH = window.innerHeight || 0;
      }
    }
    const w = opts && Number.isFinite(opts.width) ? opts.width : fallbackW;
    const h = opts && Number.isFinite(opts.height) ? opts.height : fallbackH;
    return {
      width: Math.max(0, Math.round(w) || 0),
      height: Math.max(0, Math.round(h) || 0)
    };
  }
  function clampPanelPosition(args) {
    const margin = Math.max(0, Number(args.margin));
    const m = Number.isFinite(margin) ? margin : PANEL_POSITION_MARGIN_PX;
    const vp = getViewportSize({
      width: args.viewportWidth,
      height: args.viewportHeight
    });
    const w = Math.max(0, Number(args.width) || 0);
    const h = Math.max(0, Number(args.height) || 0);
    const maxLeft = Math.max(m, vp.width - w - m);
    const maxTop = Math.max(m, vp.height - h - m);
    const left = Number(args.left);
    const top = Number(args.top);
    return {
      left: Math.max(m, Math.min(Number.isFinite(left) ? left : m, maxLeft)),
      top: Math.max(m, Math.min(Number.isFinite(top) ? top : m, maxTop))
    };
  }
  function positionForAnchor(anchor, size) {
    const a = normalizePanelAnchor(anchor);
    const margin = Math.max(0, Number.isFinite(Number(size.margin)) ? Number(size.margin) : PANEL_POSITION_MARGIN_PX);
    const vp = getViewportSize({
      width: size.viewportWidth,
      height: size.viewportHeight
    });
    const w = Math.max(0, Number(size.width) || 0);
    const h = Math.max(0, Number(size.height) || 0);
    const leftMin = margin;
    const topMin = margin;
    const leftMax = Math.max(margin, vp.width - w - margin);
    const topMax = Math.max(margin, vp.height - h - margin);
    const leftCenter = Math.round((vp.width - w) / 2);
    const topCenter = Math.round((vp.height - h) / 2);
    let left = leftMin;
    let top = topMin;
    switch (a) {
      case "top-left":
        left = leftMin;
        top = topMin;
        break;
      case "top-center":
        left = leftCenter;
        top = topMin;
        break;
      case "top-right":
        left = leftMax;
        top = topMin;
        break;
      case "middle-left":
        left = leftMin;
        top = topCenter;
        break;
      case "middle-right":
        left = leftMax;
        top = topCenter;
        break;
      case "bottom-left":
        left = leftMin;
        top = topMax;
        break;
      case "bottom-center":
        left = leftCenter;
        top = topMax;
        break;
      case "bottom-right":
        left = leftMax;
        top = topMax;
        break;
      default:
        left = leftMin;
        top = topMin;
        return { left, top, anchor: null };
    }
    const clamped = clampPanelPosition({
      left,
      top,
      width: w,
      height: h,
      margin,
      viewportWidth: vp.width,
      viewportHeight: vp.height
    });
    return { left: clamped.left, top: clamped.top, anchor: a };
  }
  function getPanelSnapTargets(size) {
    const out = [];
    for (const anchor of PANEL_ANCHORS) {
      const p = positionForAnchor(anchor, size);
      if (p.anchor) {
        out.push({ anchor: p.anchor, left: p.left, top: p.top });
      }
    }
    return out;
  }
  function findNearestPanelSnap(args) {
    const threshold = Math.max(
      0,
      Number.isFinite(Number(args.threshold)) ? Number(args.threshold) : PANEL_SNAP_THRESHOLD_PX
    );
    const targets = getPanelSnapTargets(args);
    let best = null;
    for (const t of targets) {
      const dx = t.left - args.left;
      const dy = t.top - args.top;
      const d = Math.hypot(dx, dy);
      if (d <= threshold && (!best || d < best.distance)) {
        best = { ...t, distance: d };
      }
    }
    return best;
  }
  function resolvePanelPosition(stored, size) {
    const margin = Math.max(0, Number.isFinite(Number(size.margin)) ? Number(size.margin) : PANEL_POSITION_MARGIN_PX);
    const w = Math.max(0, Number(size.width) || 0);
    const h = Math.max(0, Number(size.height) || 0);
    const state = normalizePanelPositionState(stored);
    const defaultAnchor = normalizePanelAnchor(size.defaultAnchor);
    if (state?.anchor) {
      return positionForAnchor(state.anchor, {
        width: w,
        height: h,
        margin,
        viewportWidth: size.viewportWidth,
        viewportHeight: size.viewportHeight
      });
    }
    if (state && Number.isFinite(state.left) && Number.isFinite(state.top)) {
      const clamped2 = clampPanelPosition({
        left: (
          /** @type {number} */
          state.left
        ),
        top: (
          /** @type {number} */
          state.top
        ),
        width: w,
        height: h,
        margin,
        viewportWidth: size.viewportWidth,
        viewportHeight: size.viewportHeight
      });
      return { left: clamped2.left, top: clamped2.top, anchor: null };
    }
    if (defaultAnchor) {
      return positionForAnchor(defaultAnchor, {
        width: w,
        height: h,
        margin,
        viewportWidth: size.viewportWidth,
        viewportHeight: size.viewportHeight
      });
    }
    const clamped = clampPanelPosition({
      left: margin,
      top: margin,
      width: w,
      height: h,
      margin,
      viewportWidth: size.viewportWidth,
      viewportHeight: size.viewportHeight
    });
    return { left: clamped.left, top: clamped.top, anchor: null };
  }
  function pinPanelGeometry(panel, opts = {}) {
    const pinWidth = opts.pinWidth !== false;
    const pinHeight = opts.pinHeight === true;
    const rect = panel.getBoundingClientRect();
    const s = panel.style;
    s.position = "fixed";
    s.transform = "none";
    try {
      s.webkitTransform = "none";
    } catch {
    }
    s.right = "auto";
    s.bottom = "auto";
    s.left = `${rect.left}px`;
    s.top = `${rect.top}px`;
    if (pinWidth) {
      s.width = `${rect.width}px`;
    }
    if (pinHeight) {
      s.height = `${rect.height}px`;
    }
    s.margin = "0";
    s.boxSizing = "border-box";
    return {
      left: rect.left,
      top: rect.top,
      width: rect.width,
      height: rect.height
    };
  }
  function measurePanelSize(panel) {
    let width = 0;
    let height = 0;
    try {
      width = panel.offsetWidth || 0;
      height = panel.offsetHeight || 0;
    } catch {
    }
    if (width <= 0 || height <= 0) {
      try {
        const rect = panel.getBoundingClientRect();
        if (width <= 0) width = rect.width || 0;
        if (height <= 0) height = rect.height || 0;
      } catch {
      }
    }
    if (width <= 0) {
      try {
        width = panel.scrollWidth || 0;
      } catch {
      }
    }
    if (height <= 0) {
      try {
        height = panel.scrollHeight || 0;
      } catch {
      }
    }
    return { width: Math.max(0, width), height: Math.max(0, height) };
  }
  function writePanelPosition(panel, resolved, opts = {}) {
    const s = panel.style;
    s.position = "fixed";
    s.transform = "none";
    try {
      s.webkitTransform = "none";
    } catch {
    }
    s.right = "auto";
    s.bottom = "auto";
    s.left = `${Math.round(resolved.left)}px`;
    s.top = `${Math.round(resolved.top)}px`;
    s.margin = "0";
    if (opts.pinSize) {
      if (opts.width > 0) s.width = `${Math.round(opts.width)}px`;
      if (opts.height > 0) s.height = `${Math.round(opts.height)}px`;
      s.maxWidth = "none";
      s.maxHeight = "none";
    }
    try {
      if (resolved.anchor) panel.setAttribute("data-kp-panel-anchor", resolved.anchor);
      else panel.removeAttribute("data-kp-panel-anchor");
    } catch {
    }
  }
  function applyPanelPosition(panel, position, options = {}) {
    if (!panel || !panel.style) {
      return { left: 0, top: 0, anchor: null };
    }
    const measured = measurePanelSize(panel);
    let width = Number.isFinite(options.width) ? (
      /** @type {number} */
      options.width
    ) : measured.width;
    let height = Number.isFinite(options.height) ? (
      /** @type {number} */
      options.height
    ) : measured.height;
    const margin = Math.max(0, Number.isFinite(Number(options.margin)) ? Number(options.margin) : PANEL_POSITION_MARGIN_PX);
    const fallbackW = Number.isFinite(options.fallbackWidth) ? options.fallbackWidth : 0;
    const fallbackH = Number.isFinite(options.fallbackHeight) ? options.fallbackHeight : 0;
    if (width < 32) width = fallbackW > 0 ? fallbackW : 320;
    if (height < 32) height = fallbackH > 0 ? fallbackH : 160;
    let resolved = resolvePanelPosition(position, {
      width,
      height,
      margin,
      defaultAnchor: options.defaultAnchor
    });
    writePanelPosition(panel, resolved, {
      pinSize: options.pinSize,
      width,
      height
    });
    try {
      const live = measurePanelSize(panel);
      const liveW = live.width || width;
      const liveH = live.height || height;
      if (liveW > 0 && liveH > 0) {
        const state = normalizePanelPositionState(position);
        const anchor = normalizePanelAnchor(state?.anchor) || normalizePanelAnchor(resolved.anchor);
        if (anchor) {
          resolved = positionForAnchor(anchor, {
            width: liveW,
            height: liveH,
            margin
          });
        } else {
          const left = Number.isFinite(resolved.left) ? resolved.left : margin;
          const top = Number.isFinite(resolved.top) ? resolved.top : margin;
          const clamped = clampPanelPosition({
            left,
            top,
            width: liveW,
            height: liveH,
            margin
          });
          resolved = { left: clamped.left, top: clamped.top, anchor: null };
        }
        writePanelPosition(panel, resolved, {
          pinSize: options.pinSize,
          width: liveW,
          height: liveH
        });
        const painted = panel.getBoundingClientRect();
        const vp = getViewportSize();
        const spills = painted.bottom > vp.height - margin + 1 || painted.right > vp.width - margin + 1 || painted.top < margin - 1 || painted.left < margin - 1;
        if (spills && painted.width > 0 && painted.height > 0) {
          const fixed = clampPanelPosition({
            left: painted.left,
            top: painted.top,
            width: painted.width,
            height: painted.height,
            margin
          });
          resolved = {
            left: fixed.left,
            top: fixed.top,
            anchor: anchor || null
          };
          if (!anchor) resolved.anchor = null;
          writePanelPosition(panel, resolved);
        }
      }
    } catch {
    }
    return resolved;
  }
  function finalizePanelDragPosition(args) {
    const margin = Math.max(0, Number.isFinite(Number(args.margin)) ? Number(args.margin) : PANEL_POSITION_MARGIN_PX);
    const snapEnabled = args.snap !== false;
    const clamped = clampPanelPosition({
      left: args.left,
      top: args.top,
      width: args.width,
      height: args.height,
      margin
    });
    if (snapEnabled) {
      const snap = findNearestPanelSnap({
        left: clamped.left,
        top: clamped.top,
        width: args.width,
        height: args.height,
        margin,
        threshold: args.snapThreshold
      });
      if (snap) {
        return {
          left: Math.round(snap.left),
          top: Math.round(snap.top),
          anchor: snap.anchor
        };
      }
    }
    return {
      left: Math.round(clamped.left),
      top: Math.round(clamped.top),
      anchor: null
    };
  }
  function makePanelDraggable(panel, handle, options = {}) {
    if (!panel || !(panel instanceof Element) || !handle || !(handle instanceof Element)) {
      return null;
    }
    try {
      if (handle.dataset && handle.dataset.kpPanelDraggable === "1") {
        return handle.__kpPanelDragApi || null;
      }
    } catch {
    }
    const margin = Math.max(0, Number.isFinite(Number(options.margin)) ? Number(options.margin) : PANEL_POSITION_MARGIN_PX);
    const snapThreshold = Math.max(
      0,
      Number.isFinite(Number(options.snapThreshold)) ? Number(options.snapThreshold) : PANEL_SNAP_THRESHOLD_PX
    );
    const moveThreshold = Math.max(
      0,
      Number.isFinite(Number(options.moveThresholdPx)) ? Number(options.moveThresholdPx) : PANEL_DRAG_MOVE_THRESHOLD_PX
    );
    const snapEnabled = options.snap !== false;
    const pinWidth = options.pinWidth !== false;
    const pinHeight = options.pinHeight === true;
    const excludeSelector = typeof options.excludeSelector === "string" && options.excludeSelector ? options.excludeSelector : "";
    const cursorGrab = options.cursorGrab || "grab";
    const cursorGrabbing = options.cursorGrabbing || "grabbing";
    let dragState = null;
    const measure = () => {
      const rect = panel.getBoundingClientRect();
      return {
        width: panel.offsetWidth || rect.width || 0,
        height: panel.offsetHeight || rect.height || 0
      };
    };
    const onPointerMove = (e) => {
      if (!dragState || !panel.isConnected) return;
      const dx = e.clientX - dragState.startX;
      const dy = e.clientY - dragState.startY;
      if (!dragState.moved) {
        if (Math.abs(dx) < moveThreshold && Math.abs(dy) < moveThreshold) return;
        dragState.moved = true;
        try {
          handle.style.cursor = cursorGrabbing;
        } catch {
        }
        try {
          options.onMoveStart?.();
        } catch {
        }
      }
      const size = measure();
      const next = clampPanelPosition({
        left: dragState.originLeft + dx,
        top: dragState.originTop + dy,
        width: size.width,
        height: size.height,
        margin
      });
      panel.style.left = `${next.left}px`;
      panel.style.top = `${next.top}px`;
      panel.style.right = "auto";
      panel.style.bottom = "auto";
      try {
        panel.removeAttribute("data-kp-panel-anchor");
      } catch {
      }
      try {
        options.onMove?.(next);
      } catch {
      }
    };
    const endDrag = (e) => {
      if (!dragState) return;
      const state = dragState;
      const pointerId = state.pointerId;
      dragState = null;
      try {
        handle.style.cursor = cursorGrab;
      } catch {
      }
      try {
        if (e && typeof e.pointerId === "number") handle.releasePointerCapture(e.pointerId);
        else if (typeof pointerId === "number") handle.releasePointerCapture(pointerId);
      } catch {
      }
      document.removeEventListener("pointermove", onPointerMove, true);
      document.removeEventListener("pointerup", endDrag, true);
      document.removeEventListener("pointercancel", endDrag, true);
      if (!panel.isConnected) {
        try {
          options.onMoveEnd?.({ left: state.originLeft, top: state.originTop, anchor: null, moved: false });
        } catch {
        }
        return;
      }
      const size = measure();
      const rect = panel.getBoundingClientRect();
      const finalPos = state.moved ? finalizePanelDragPosition({
        left: rect.left,
        top: rect.top,
        width: size.width,
        height: size.height,
        margin,
        snapThreshold,
        snap: snapEnabled
      }) : {
        left: Math.round(rect.left),
        top: Math.round(rect.top),
        anchor: normalizePanelAnchor(panel.getAttribute?.("data-kp-panel-anchor"))
      };
      if (state.moved) {
        applyPanelPosition(panel, finalPos, { margin, pinSize: false });
      }
      try {
        options.onMoveEnd?.({
          left: finalPos.left,
          top: finalPos.top,
          anchor: finalPos.anchor ?? null,
          moved: !!state.moved
        });
      } catch {
      }
    };
    const onPointerDown = (e) => {
      if (!panel.isConnected) return;
      if (e.pointerType === "mouse" && e.button !== 0) return;
      if (excludeSelector) {
        try {
          const t = (
            /** @type {Element|null} */
            e.target instanceof Element ? e.target : e.target?.parentElement
          );
          if (t?.closest?.(excludeSelector)) return;
        } catch {
        }
      }
      e.preventDefault();
      e.stopPropagation();
      const origin = pinPanelGeometry(panel, { pinWidth, pinHeight });
      dragState = {
        startX: e.clientX,
        startY: e.clientY,
        originLeft: origin.left,
        originTop: origin.top,
        pointerId: e.pointerId,
        moved: false
      };
      try {
        handle.setPointerCapture(e.pointerId);
      } catch {
      }
      document.addEventListener("pointermove", onPointerMove, true);
      document.addEventListener("pointerup", endDrag, true);
      document.addEventListener("pointercancel", endDrag, true);
    };
    try {
      handle.style.cursor = handle.style.cursor || cursorGrab;
      handle.style.touchAction = handle.style.touchAction || "none";
      handle.style.userSelect = handle.style.userSelect || "none";
    } catch {
    }
    handle.addEventListener("pointerdown", onPointerDown);
    const api = {
      dispose: () => {
        endDrag();
        try {
          handle.removeEventListener("pointerdown", onPointerDown);
        } catch {
        }
        try {
          if (handle.dataset) delete handle.dataset.kpPanelDraggable;
        } catch {
        }
        try {
          delete handle.__kpPanelDragApi;
        } catch {
        }
      }
    };
    try {
      handle.dataset.kpPanelDraggable = "1";
      handle.__kpPanelDragApi = api;
    } catch {
    }
    return api;
  }

  // src/config/function-library.js
  init_keyboard_layouts();

  // src/config/macro-keys.js
  var MACRO_KEY_KIND_DEFS = Object.freeze([
    Object.freeze({
      id: (
        /** @type {const} */
        "hotkey"
      ),
      label: "Combination / Hotkey",
      description: "Send a modifier chord",
      details: "One key sends a modifier chord such as Ctrl+C, Win+R, or Ctrl+Shift+Esc. Configure the chord on the Action Instance.",
      keyboardClass: "key-orange"
    }),
    Object.freeze({
      id: (
        /** @type {const} */
        "burst"
      ),
      label: "Burst Keys",
      description: "Type a short key sequence",
      details: "One press types a short sequence of keystrokes (e.g. a\u2192b\u2192c or Ctrl+C \u2192 Ctrl+V \u2192 Enter). Configure the steps on the Action Instance.",
      keyboardClass: "key-purple"
    }),
    Object.freeze({
      id: (
        /** @type {const} */
        "roundRobin"
      ),
      label: "Round Robin",
      description: "Cycle through stroke options",
      details: "Each press advances a cycle of strokes (A, then B, then C, then A\u2026). Useful when one key should rotate through several outputs.",
      keyboardClass: "key-scroll"
    }),
    Object.freeze({
      id: (
        /** @type {const} */
        "continuous"
      ),
      label: "Continue / Continuous",
      description: "Repeat a stroke until stopped",
      details: "Press to start sending a stroke repeatedly; press again to stop. Configure the repeated stroke on the Action Instance.",
      keyboardClass: "key-highlight"
    }),
    Object.freeze({
      id: (
        /** @type {const} */
        "mouse"
      ),
      label: "Synthetic Mouse",
      description: "Click under the cursor",
      details: "Synthesizes a left, middle, or right click under the cursor without moving your physical mouse buttons. Choose the button on the Action Instance.",
      keyboardClass: "key-activate"
    }),
    Object.freeze({
      id: (
        /** @type {const} */
        "key"
      ),
      label: "Normal Key",
      description: "Remap this slot to another key",
      details: "Remaps this layout slot so pressing it sends a different key (e.g. F sends 1). Configure the target key on the Action Instance.",
      keyboardClass: "key-gray"
    })
  ]);
  var KNOWN_KINDS = new Set(MACRO_KEY_KIND_DEFS.map((d) => d.id));
  function normalizeKeyStroke(raw) {
    const src = raw && typeof raw === "object" ? raw : {};
    const key = String(src.key || "").trim();
    const out = { key: key || "" };
    if (typeof src.code === "string" && src.code.trim()) out.code = src.code.trim();
    if (src.ctrl === true) out.ctrl = true;
    if (src.alt === true) out.alt = true;
    if (src.shift === true) out.shift = true;
    if (src.meta === true) out.meta = true;
    return out;
  }
  function isKeyStrokeValid(stroke) {
    return !!(stroke && typeof stroke.key === "string" && stroke.key.trim());
  }
  function formatKeyStroke(stroke) {
    if (!isKeyStrokeValid(stroke)) return "(empty)";
    const parts = [];
    if (stroke.ctrl) parts.push("Ctrl");
    if (stroke.alt) parts.push("Alt");
    if (stroke.shift) parts.push("Shift");
    if (stroke.meta) parts.push("Win");
    const key = String(stroke.key);
    const pretty = key.length === 1 ? key.toUpperCase() : key;
    parts.push(pretty);
    return parts.join("+");
  }
  function defaultMacroKeyConfig(kind) {
    switch (kind) {
      case "hotkey":
        return { stroke: normalizeKeyStroke({ key: "c", ctrl: true }) };
      case "burst":
        return {
          steps: [
            normalizeKeyStroke({ key: "a" }),
            normalizeKeyStroke({ key: "b" }),
            normalizeKeyStroke({ key: "c" })
          ],
          gapMs: 40
        };
      case "roundRobin":
        return {
          items: [
            normalizeKeyStroke({ key: "a" }),
            normalizeKeyStroke({ key: "b" }),
            normalizeKeyStroke({ key: "c" })
          ]
        };
      case "continuous":
        return {
          stroke: normalizeKeyStroke({ key: "w" }),
          intervalMs: 50
        };
      case "mouse":
        return { button: "left" };
      case "key":
        return { stroke: normalizeKeyStroke({ key: "1" }) };
      default:
        return {};
    }
  }
  function normalizeMacroKeyConfig(kind, raw) {
    const src = raw && typeof raw === "object" ? raw : {};
    const base = defaultMacroKeyConfig(kind);
    switch (kind) {
      case "hotkey":
      case "key":
        return { stroke: normalizeKeyStroke(src.stroke || base.stroke) };
      case "burst": {
        const stepsRaw = Array.isArray(src.steps) ? src.steps : base.steps;
        const steps = stepsRaw.map((s) => normalizeKeyStroke(s)).filter((s) => s.key);
        const gap = Number(src.gapMs);
        return {
          steps: steps.length ? steps : base.steps,
          gapMs: Number.isFinite(gap) ? Math.max(0, Math.min(2e3, gap)) : 40
        };
      }
      case "roundRobin": {
        const itemsRaw = Array.isArray(src.items) ? src.items : base.items;
        const items = itemsRaw.map((s) => normalizeKeyStroke(s)).filter((s) => s.key);
        return { items: items.length ? items : base.items };
      }
      case "continuous": {
        const interval = Number(src.intervalMs);
        return {
          stroke: normalizeKeyStroke(src.stroke || base.stroke),
          intervalMs: Number.isFinite(interval) ? Math.max(10, Math.min(5e3, interval)) : 50
        };
      }
      case "mouse": {
        const button = String(src.button || "left").toLowerCase();
        return {
          button: button === "middle" || button === "right" || button === "left" ? button : "left"
        };
      }
      default:
        return { ...base };
    }
  }
  function summarizeMacroKey(mk) {
    if (!mk || !mk.kind) return "";
    const cfg = normalizeMacroKeyConfig(mk.kind, mk.config);
    switch (mk.kind) {
      case "hotkey":
      case "key":
        return formatKeyStroke(cfg.stroke);
      case "burst":
        return (cfg.steps || []).map(formatKeyStroke).join(" \u2192 ");
      case "roundRobin":
        return (cfg.items || []).map(formatKeyStroke).join(" / ");
      case "continuous":
        return `${formatKeyStroke(cfg.stroke)} @ ${cfg.intervalMs}ms`;
      case "mouse":
        return `${cfg.button} click`;
      default:
        return String(mk.kind);
    }
  }
  function macroKeyKeyboardClass(kind) {
    const def = MACRO_KEY_KIND_DEFS.find((d) => d.id === kind);
    return def?.keyboardClass || "key-purple";
  }
  var MACRO_BUILDER_STEP_TYPES = Object.freeze([
    Object.freeze({ id: "function", label: "Function", description: "Run a Function Library entry (including Macro Keys)." }),
    Object.freeze({ id: "wait", label: "Wait", description: "Pause for N milliseconds." }),
    Object.freeze({ id: "gate", label: "Gate", description: "If condition fails, skip following steps." }),
    Object.freeze({ id: "stop", label: "Stop", description: "End the macro immediately." }),
    Object.freeze({ id: "runMacro", label: "Run Macro", description: "Call another macro (cycle-guarded)." })
  ]);

  // src/utils/key-chord.js
  var CHORD_SLOT_KEY_PREFIX = "CHORD:";
  function normalizeChordKeyToken(rawKey) {
    const k = String(rawKey || "").trim();
    if (!k) return "";
    if (k.length === 1) return k.toUpperCase();
    const named = {
      " ": "SPACE",
      "Spacebar": "SPACE",
      "Escape": "ESC",
      "ArrowUp": "UP",
      "ArrowDown": "DOWN",
      "ArrowLeft": "LEFT",
      "ArrowRight": "RIGHT"
    };
    return (named[k] || k).toUpperCase();
  }
  function buildChordSlotKey(chord) {
    const key = normalizeChordKeyToken(chord?.key);
    if (!key) return "";
    const mods = [];
    if (chord?.ctrl) mods.push("CTRL");
    if (chord?.alt) mods.push("ALT");
    if (chord?.shift) mods.push("SHIFT");
    if (chord?.meta) mods.push("META");
    if (!mods.length) return "";
    return `${CHORD_SLOT_KEY_PREFIX}${mods.join("+")}+${key}`;
  }
  function chordSlotKeyFromEvent(e) {
    if (!e) return "";
    return buildChordSlotKey({
      key: e.key,
      ctrl: !!e.ctrlKey,
      alt: !!e.altKey,
      shift: !!e.shiftKey,
      meta: !!e.metaKey
    });
  }
  function isChordSlotKey(slotKey) {
    return typeof slotKey === "string" && slotKey.startsWith(CHORD_SLOT_KEY_PREFIX);
  }
  function parseChordSlotKey(slotKey) {
    if (!isChordSlotKey(slotKey)) return null;
    const parts = slotKey.slice(CHORD_SLOT_KEY_PREFIX.length).split("+").filter(Boolean);
    if (parts.length < 2) return null;
    const key = parts[parts.length - 1];
    const mods = new Set(parts.slice(0, -1));
    return {
      key,
      ctrl: mods.has("CTRL"),
      alt: mods.has("ALT"),
      shift: mods.has("SHIFT"),
      meta: mods.has("META")
    };
  }
  function formatChordSlotKeyLabel(slotKey) {
    const chord = parseChordSlotKey(slotKey);
    if (!chord) return String(slotKey || "");
    const parts = [];
    if (chord.ctrl) parts.push("Ctrl");
    if (chord.alt) parts.push("Alt");
    if (chord.shift) parts.push("Shift");
    if (chord.meta) parts.push("Win");
    parts.push(chord.key.length === 1 ? chord.key.toUpperCase() : chord.key);
    return parts.join("+");
  }

  // src/modules/action-result-delivery.js
  init_constants();

  // src/ui/procedure-result-popover.js
  init_constants();
  var STYLE_ATTR2 = "data-kp-procedure-result-style";
  var ROOT_CLASS = "kp-procedure-result";
  var _root = null;
  var _dragApi = null;
  function ensureStyles(root) {
    injectChromeStyles(root, { attr: STYLE_ATTR2, css: `
:host {
  position: fixed;
  z-index: ${Z_INDEX.KEY_ACTION_CONFIG || 2147483047};
  width: min(420px, calc(100vw - 24px));
  max-height: min(70vh, 520px);
  display: flex;
  flex-direction: column;
  color: ${NCT_DARK_UI_COLORS.fg};
  font-family: ${KP_UI_FONT || "system-ui, sans-serif"};
  font-size: 12px;
  line-height: 1.45;
  border-radius: ${NCT_DARK_UI_PANEL_RADIUS};
  border: ${NCT_DARK_UI_PANEL_BORDER};
  background: ${NCT_DARK_UI_PANEL_BACKGROUND};
  box-shadow: ${NCT_DARK_UI_PANEL_BOX_SHADOW};
  box-sizing: border-box;
}
:host([hidden]) { display: none !important; }
.${ROOT_CLASS}__titlebar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 10px;
  cursor: grab;
  user-select: none;
  background: ${NCT_DARK_UI_TITLEBAR_GRADIENT};
  border-bottom: ${NCT_DARK_UI_TITLEBAR_BORDER_BOTTOM};
  flex: 0 0 auto;
  letter-spacing: var(--kp-type-tracking-titlebar, 0.02em);
  text-transform: var(--kp-type-transform-titlebar, none);
}
.${ROOT_CLASS}__title {
  font-weight: var(--kp-titlebar-title-weight, 600);
  font-size: 12px;
  letter-spacing: var(--kp-type-tracking-titlebar, 0.02em);
  text-transform: var(--kp-type-transform-titlebar, none);
  color: var(--kp-color-fg, inherit);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.${ROOT_CLASS}__close {
  appearance: none;
  background: ${NCT_DARK_UI_BTN_GRADIENT};
  border: ${NCT_DARK_UI_BTN_BORDER};
  color: inherit;
  width: 22px;
  height: 22px;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
}
.${ROOT_CLASS}__body {
  padding: 10px 12px;
  overflow: auto;
  flex: 1 1 auto;
  white-space: pre-wrap;
  word-break: break-word;
}
.${ROOT_CLASS}__actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  padding: 8px 12px 10px;
  border-top: 1px solid rgba(0,0,0,0.35);
  flex: 0 0 auto;
}
.${ROOT_CLASS}__btn {
  appearance: none;
  border: ${NCT_DARK_UI_BTN_BORDER};
  background: ${NCT_DARK_UI_BTN_GRADIENT};
  color: ${NCT_DARK_UI_COLORS.fg};
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  padding: 5px 10px;
  font: inherit;
  cursor: pointer;
}
.${ROOT_CLASS}__btn:hover {
  background: ${NCT_DARK_UI_HOVER_TINT};
}
.${ROOT_CLASS}__btn[data-primary="true"] {
  background: ${NCT_DARK_UI_BTN_LIT_GRADIENT};
  border: ${NCT_DARK_UI_BTN_LIT_BORDER};
  color: #e8f0f8;
}
` });
  }
  function showProcedureResultPopover(opts = {}) {
    const doc = document;
    if (!_root) {
      _root = doc.createElement("div");
      _root.className = ROOT_CLASS;
      _root.setAttribute("role", "dialog");
      _root.setAttribute("aria-label", "Procedure result");
      const shadowRoot = ensureOpenChromeShadow(_root, { id: "procedure-result", chromeWindow: true });
      const panelRoot2 = shadowRoot || _root;
      ensureStyles(panelRoot2);
      panelRoot2.innerHTML = `
      <div class="${ROOT_CLASS}__titlebar" data-kp-result-drag="true">
        <div class="${ROOT_CLASS}__title"></div>
        <button type="button" class="${ROOT_CLASS}__close" aria-label="Close">\xD7</button>
      </div>
      <div class="${ROOT_CLASS}__body"></div>
      <div class="${ROOT_CLASS}__actions">
        <button type="button" class="${ROOT_CLASS}__btn" data-kp-result-copy="true">Copy</button>
        <button type="button" class="${ROOT_CLASS}__btn" data-primary="true" data-kp-result-close="true">Done</button>
      </div>
    `;
      doc.body.appendChild(_root);
      panelRoot2.querySelector(`.${ROOT_CLASS}__close`)?.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        hideProcedureResultPopover();
      });
      panelRoot2.querySelector('[data-kp-result-close="true"]')?.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        hideProcedureResultPopover();
      });
      const handle = panelRoot2.querySelector('[data-kp-result-drag="true"]');
      _dragApi = makePanelDraggable(_root, handle, {
        excludeSelector: `.${ROOT_CLASS}__close`
      });
    }
    const panelRoot = _root.shadowRoot || _root;
    ensureStyles(panelRoot);
    const titleEl = panelRoot.querySelector(`.${ROOT_CLASS}__title`);
    if (titleEl) titleEl.textContent = String(opts.title || "Result");
    const body = panelRoot.querySelector(`.${ROOT_CLASS}__body`);
    if (body) {
      const text = String(opts.text ?? "");
      if (opts.html) {
        body.innerHTML = "";
        body.textContent = text;
      } else {
        body.textContent = text;
      }
    }
    const copyBtn = panelRoot.querySelector('[data-kp-result-copy="true"]');
    if (copyBtn) {
      copyBtn.onclick = async (e) => {
        e.preventDefault();
        e.stopPropagation();
        let ok = false;
        try {
          ok = !!await opts.onCopy?.();
        } catch {
          ok = false;
        }
        if (!ok) {
          try {
            await navigator.clipboard.writeText(String(opts.text ?? ""));
            ok = true;
          } catch {
          }
        }
        copyBtn.textContent = ok ? "Copied" : "Copy failed";
        setTimeout(() => {
          try {
            copyBtn.textContent = "Copy";
          } catch {
          }
        }, 1200);
      };
    }
    _root.hidden = false;
    const margin = 16;
    const vw = window.innerWidth || 800;
    const vh = window.innerHeight || 600;
    const left = Math.max(margin, Math.round(vw * 0.5 - 210));
    const top = Math.max(margin, Math.round(vh * 0.18));
    _root.style.left = `${left}px`;
    _root.style.top = `${top}px`;
    _root.style.right = "auto";
    _root.style.bottom = "auto";
  }
  function hideProcedureResultPopover() {
    if (_root) _root.hidden = true;
  }

  // src/utils/media-library-transfer.js
  function blobToDataUrl(blob) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(
        /** @type {string} */
        reader.result || ""
      );
      reader.onerror = () => reject(reader.error || new Error("FileReader failed"));
      reader.readAsDataURL(blob);
    });
  }
  function dataUrlToBlob(dataUrl, fallbackMime = "application/octet-stream") {
    if (!dataUrl || typeof dataUrl !== "string" || !dataUrl.startsWith("data:")) {
      return null;
    }
    const comma = dataUrl.indexOf(",");
    if (comma < 0) return null;
    const header = dataUrl.slice(5, comma);
    const payload = dataUrl.slice(comma + 1);
    const isBase64 = /;base64/i.test(header);
    const mimeMatch = header.match(/^([^;,]*)/);
    const mime = (mimeMatch && mimeMatch[1] ? mimeMatch[1] : "").trim() || fallbackMime;
    try {
      if (isBase64) {
        const binary = atob(payload);
        const len = binary.length;
        const bytes = new Uint8Array(len);
        for (let i = 0; i < len; i++) {
          bytes[i] = binary.charCodeAt(i);
        }
        return new Blob([bytes], { type: mime });
      }
      const decoded = decodeURIComponent(payload);
      return new Blob([decoded], { type: mime });
    } catch (e) {
      console.warn("[MediaLibrary] dataUrlToBlob failed:", e?.message || e);
      return null;
    }
  }

  // src/utils/video-url-utils.js
  var MAX_INLINE_VIDEO_BYTES = 8 * 1024 * 1024;
  function isStreamingManifestUrl(url) {
    const s = String(url || "").trim();
    if (!s) return false;
    const path = s.split("?")[0].split("#")[0].toLowerCase();
    if (/\.(m3u8|mpd|ism)(\/|$)/i.test(path)) return true;
    if (/\/(manifest|master\.m3u8|playlist\.m3u8|index\.m3u8)(\/|$)/i.test(path)) return true;
    return /[?&](format=m3u8|type=m3u8|playlist=m3u8)\b/i.test(s);
  }
  function isProgressiveMediaUrl(url) {
    const s = String(url || "").trim();
    if (!s) return false;
    if (/^(blob:|data:video\/)/i.test(s)) return true;
    if (!/^https?:/i.test(s)) return false;
    if (isStreamingManifestUrl(s)) return false;
    const path = s.split("?")[0].split("#")[0].toLowerCase();
    return /\.(mp4|webm|ogv|ogg|mov|m4v|mkv|m4a)(\/|$)/i.test(path) || /\/video\//i.test(path);
  }
  function isServiceWorkerFetchableVideoUrl(url) {
    const s = String(url || "").trim();
    if (!/^https?:\/\//i.test(s)) return false;
    return !isStreamingManifestUrl(s);
  }

  // src/modules/media-library-client.js
  async function send(message) {
    if (!isExtensionContextValid()) {
      throw new Error("Extension context invalidated");
    }
    const response = await chrome.runtime.sendMessage(message);
    if (!response) {
      throw new Error("No response from Media Library");
    }
    return response;
  }
  async function fetchMediaBlob(url) {
    const src = String(url || "");
    if (!src) return null;
    try {
      if (/^data:/i.test(src)) {
        const res2 = await fetch(src);
        const blob2 = await res2.blob();
        return blob2 && blob2.size > 0 ? blob2 : null;
      }
      const res = await fetch(src, { credentials: "omit", cache: "force-cache" });
      if (!res.ok) return null;
      const blob = await res.blob();
      return blob && blob.size > 0 ? blob : null;
    } catch {
      return null;
    }
  }
  async function addImageToMediaLibrary(input) {
    if (!(input?.blob instanceof Blob) || input.blob.size <= 0) {
      return { success: false, duplicate: false, error: "No image data" };
    }
    let dataUrl = "";
    try {
      dataUrl = await blobToDataUrl(input.blob);
    } catch (e) {
      return { success: false, duplicate: false, error: e?.message || "No image data" };
    }
    if (!dataUrl) {
      return { success: false, duplicate: false, error: "No image data" };
    }
    return send({
      type: MSG.MEDIA_LIBRARY_ADD,
      kind: "image",
      dataUrl,
      mime: input.mime || input.blob.type || "",
      sourceUrl: input.sourceUrl || "",
      pageUrl: input.pageUrl || ""
    });
  }
  async function addUrlToMediaLibrary(input) {
    const sourceUrl = String(input?.sourceUrl || input?.url || "").trim();
    if (!sourceUrl) {
      return { success: false, duplicate: false, error: "No URL" };
    }
    return send({
      type: MSG.MEDIA_LIBRARY_ADD,
      kind: "url",
      sourceUrl,
      pageUrl: input?.pageUrl || ""
    });
  }
  async function addVideoToMediaLibrary(input) {
    const sourceUrl = String(input?.sourceUrl || "").trim();
    const canSwFetch = isServiceWorkerFetchableVideoUrl(sourceUrl);
    let dataUrl = typeof input?.dataUrl === "string" ? input.dataUrl : "";
    const fileBlob = input?.blob instanceof Blob && input.blob.size > 0 ? input.blob : null;
    if (!dataUrl && fileBlob && !canSwFetch && fileBlob.size <= MAX_INLINE_VIDEO_BYTES) {
      try {
        dataUrl = await blobToDataUrl(fileBlob);
      } catch {
        dataUrl = "";
      }
    }
    let thumbDataUrl = typeof input?.thumbDataUrl === "string" ? input.thumbDataUrl : "";
    if (!thumbDataUrl && input?.thumbBlob instanceof Blob && input.thumbBlob.size > 0) {
      try {
        thumbDataUrl = await blobToDataUrl(input.thumbBlob);
      } catch {
        thumbDataUrl = "";
      }
    }
    if (!dataUrl && !sourceUrl) {
      return { success: false, duplicate: false, error: "No video" };
    }
    return send({
      type: MSG.MEDIA_LIBRARY_ADD,
      kind: "video",
      dataUrl,
      mime: input?.mime || fileBlob?.type || "",
      sourceUrl,
      pageUrl: input?.pageUrl || "",
      thumbDataUrl,
      width: Number(input?.width) || 0,
      height: Number(input?.height) || 0,
      /** Ask SW to fetch http(s) bytes when we did not inline a dataUrl. */
      fetchSource: canSwFetch && !dataUrl
    });
  }
  async function addDocumentToMediaLibrary(input) {
    const sourceUrl = String(input?.sourceUrl || "").trim();
    const canSwFetch = isServiceWorkerFetchableVideoUrl(sourceUrl);
    let dataUrl = typeof input?.dataUrl === "string" ? input.dataUrl : "";
    const fileBlob = input?.blob instanceof Blob && input.blob.size > 0 ? input.blob : null;
    if (!dataUrl && fileBlob && !canSwFetch && fileBlob.size <= MAX_INLINE_VIDEO_BYTES) {
      try {
        dataUrl = await blobToDataUrl(fileBlob);
      } catch {
        dataUrl = "";
      }
    }
    if (!dataUrl && !sourceUrl) {
      return { success: false, duplicate: false, error: "No document" };
    }
    return send({
      type: MSG.MEDIA_LIBRARY_ADD,
      kind: "document",
      dataUrl,
      mime: input?.mime || fileBlob?.type || "",
      sourceUrl,
      pageUrl: input?.pageUrl || "",
      fetchSource: canSwFetch && !dataUrl
    });
  }
  async function fetchUrlIntoMediaLibrary(input) {
    const sourceUrl = String(input?.sourceUrl || input?.url || "").trim();
    if (!sourceUrl) {
      return { success: false, duplicate: false, error: "No URL" };
    }
    const canSwFetch = isServiceWorkerFetchableVideoUrl(sourceUrl);
    const dataUrl = /^data:/i.test(sourceUrl) ? sourceUrl : "";
    if (!canSwFetch && !dataUrl) {
      return { success: false, duplicate: false, error: "Could not fetch file" };
    }
    return send({
      type: MSG.MEDIA_LIBRARY_ADD,
      kind: "file",
      sourceUrl,
      dataUrl,
      pageUrl: input?.pageUrl || "",
      fetchSource: canSwFetch && !dataUrl
    });
  }
  async function listMediaLibrary(opts = {}) {
    return send({
      type: MSG.MEDIA_LIBRARY_LIST,
      kind: opts.kind || "image",
      domain: opts.domain || "",
      includeThumbs: opts.includeThumbs !== false
    });
  }
  async function getMediaLibraryOriginal(id) {
    const response = await send({
      type: MSG.MEDIA_LIBRARY_GET,
      id: String(id || "")
    });
    const mime = response?.item?.mime || response?.mime || "application/octet-stream";
    const blob = dataUrlToBlob(response?.dataUrl, mime);
    return { ...response, blob };
  }
  async function deleteMediaLibraryItems(ids) {
    return send({
      type: MSG.MEDIA_LIBRARY_DELETE,
      ids: Array.isArray(ids) ? ids : []
    });
  }
  async function zipMediaLibrary(opts = {}) {
    const response = await send({
      type: MSG.MEDIA_LIBRARY_ZIP,
      ids: opts.ids || null,
      kind: opts.kind || "image",
      domain: opts.domain || ""
    });
    const blob = dataUrlToBlob(response?.dataUrl, "application/zip");
    return { ...response, blob };
  }
  function downloadBlob(blob, filename) {
    if (!(blob instanceof Blob)) return;
    const objectUrl = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = objectUrl;
    a.download = filename || "download";
    a.rel = "noopener";
    a.style.display = "none";
    document.documentElement.appendChild(a);
    a.click();
    a.remove();
    setTimeout(() => {
      try {
        URL.revokeObjectURL(objectUrl);
      } catch {
      }
    }, 4e3);
  }

  // src/modules/action-result-delivery.js
  var ACTION_RESULT_DESTINATIONS = Object.freeze({
    CLIPBOARD: "clipboard",
    POPOVER: "popover",
    BOTH: "both",
    MODIFY_PAGE: "modifyPage",
    MEDIA_LIBRARY: "mediaLibrary",
    CLIPBOARD_AND_MEDIA_LIBRARY: "clipboardAndMediaLibrary",
    SCRAPBOOK: "scrapbook"
  });
  var DESTINATION_LABELS = Object.freeze({
    [ACTION_RESULT_DESTINATIONS.CLIPBOARD]: "Clipboard",
    [ACTION_RESULT_DESTINATIONS.POPOVER]: "Popover",
    [ACTION_RESULT_DESTINATIONS.BOTH]: "Clipboard and popover",
    [ACTION_RESULT_DESTINATIONS.MODIFY_PAGE]: "Replace in page",
    [ACTION_RESULT_DESTINATIONS.MEDIA_LIBRARY]: "Media Library",
    [ACTION_RESULT_DESTINATIONS.CLIPBOARD_AND_MEDIA_LIBRARY]: "Both",
    [ACTION_RESULT_DESTINATIONS.SCRAPBOOK]: "Scrapbook (coming soon)"
  });
  function buildResultDestinationParameter(applicableDestinations) {
    const ids = Array.isArray(applicableDestinations) && applicableDestinations.length ? applicableDestinations : [ACTION_RESULT_DESTINATIONS.CLIPBOARD];
    return Object.freeze({
      id: "destination",
      label: "Destination",
      type: "enum",
      defaultValue: ids[0],
      options: Object.freeze(ids.map((id) => Object.freeze({ id, label: DESTINATION_LABELS[id] || id })))
    });
  }
  function normalizeActionResultDestination(raw, fallback = ACTION_RESULT_DESTINATIONS.CLIPBOARD) {
    const v = String(raw || "").trim();
    if (Object.prototype.hasOwnProperty.call(DESTINATION_LABELS, v)) {
      return (
        /** @type {ActionResultDestination} */
        v
      );
    }
    return fallback;
  }
  function destinationFlags(destination) {
    const d = normalizeActionResultDestination(destination);
    const clipboardAndPopover = d === ACTION_RESULT_DESTINATIONS.BOTH;
    const clipboardAndMedia = d === ACTION_RESULT_DESTINATIONS.CLIPBOARD_AND_MEDIA_LIBRARY;
    return {
      clipboard: d === ACTION_RESULT_DESTINATIONS.CLIPBOARD || clipboardAndPopover || clipboardAndMedia,
      popover: d === ACTION_RESULT_DESTINATIONS.POPOVER || clipboardAndPopover,
      modifyPage: d === ACTION_RESULT_DESTINATIONS.MODIFY_PAGE,
      mediaLibrary: d === ACTION_RESULT_DESTINATIONS.MEDIA_LIBRARY || clipboardAndMedia
    };
  }
  async function deliverActionResult(kp, opts = {}) {
    const flags = destinationFlags(opts.destination);
    const out = { clipboard: false, popover: false, modifyPage: false, mediaLibrary: false };
    if (flags.mediaLibrary) {
      const blob = opts.blob;
      if (blob instanceof Blob && blob.size > 0) {
        try {
          const result = await addImageToMediaLibrary({
            blob,
            mime: opts.mime || blob.type || "",
            sourceUrl: opts.sourceUrl || "",
            pageUrl: opts.pageUrl || (typeof location !== "undefined" ? String(location.href || "") : "")
          });
          out.mediaLibrary = !!(result?.success || result?.duplicate);
          if (!flags.clipboard && typeof kp?.showFlashNotification === "function") {
            if (result?.duplicate) {
              kp.showFlashNotification("Already in Media Library", COLORS.NOTIFICATION_INFO, blob);
            } else if (result?.success) {
              kp.showFlashNotification(
                opts.successMessage || "Saved to Media Library",
                COLORS.NOTIFICATION_SUCCESS,
                blob
              );
            } else {
              kp.showFlashNotification(
                result?.error || "Could not save to Media Library",
                COLORS.NOTIFICATION_ERROR
              );
            }
          }
        } catch {
          if (!flags.clipboard && typeof kp?.showFlashNotification === "function") {
            kp.showFlashNotification("Could not save to Media Library", COLORS.NOTIFICATION_ERROR);
          }
        }
      } else {
        const href = String(opts.sourceUrl || opts.text || "").trim();
        try {
          const result = await addUrlToMediaLibrary({
            sourceUrl: href,
            pageUrl: opts.pageUrl || (typeof location !== "undefined" ? String(location.href || "") : "")
          });
          out.mediaLibrary = !!(result?.success || result?.duplicate);
          if (!flags.clipboard && typeof kp?.showFlashNotification === "function") {
            if (result?.duplicate) {
              kp.showFlashNotification("Already in Media Library", COLORS.NOTIFICATION_INFO);
            } else if (result?.success) {
              kp.showFlashNotification(
                opts.successMessage || "URL saved to Media Library",
                COLORS.NOTIFICATION_SUCCESS
              );
            } else {
              kp.showFlashNotification(
                result?.error || "Could not save URL to Media Library",
                COLORS.NOTIFICATION_ERROR
              );
            }
          }
        } catch {
          if (!flags.clipboard && typeof kp?.showFlashNotification === "function") {
            kp.showFlashNotification("Could not save URL to Media Library", COLORS.NOTIFICATION_ERROR);
          }
        }
      }
    }
    const text = String(opts.text ?? "");
    if (flags.modifyPage) {
      if (typeof opts.onModifyPage === "function") {
        try {
          out.modifyPage = !!await opts.onModifyPage(text);
        } catch (e) {
          console.warn("[KeyPilot] onModifyPage handler failed:", e);
          out.modifyPage = false;
        }
      }
      if (!out.modifyPage) {
        flags.popover = true;
      }
    }
    if (flags.clipboard) {
      try {
        const payload = opts.html ? { plainText: text, htmlContent: String(opts.html), hasRichContent: true } : text;
        out.clipboard = !!await kp?.copyToClipboard?.(payload);
      } catch {
        out.clipboard = false;
      }
      if (out.clipboard && typeof kp?.showFlashNotification === "function") {
        try {
          const successMessage = opts.successMessage || (flags.mediaLibrary && out.mediaLibrary ? "Copied and saved to Media Library" : "Copied to clipboard");
          kp.showFlashNotification(
            successMessage,
            COLORS.NOTIFICATION_SUCCESS,
            flags.mediaLibrary ? opts.blob : void 0
          );
        } catch {
        }
      } else if (flags.clipboard && !out.clipboard && typeof kp?.showFlashNotification === "function") {
        try {
          kp.showFlashNotification("Could not copy to clipboard", COLORS.NOTIFICATION_ERROR);
        } catch {
        }
      }
    }
    if (flags.popover) {
      try {
        showProcedureResultPopover({
          title: opts.title || "Result",
          text,
          html: opts.html || null,
          onCopy: async () => {
            try {
              const payload = opts.html ? { plainText: text, htmlContent: String(opts.html), hasRichContent: true } : text;
              return !!await kp?.copyToClipboard?.(payload);
            } catch {
              return false;
            }
          }
        });
        out.popover = true;
      } catch (e) {
        console.warn("[KeyPilot] Failed to show result popover:", e);
        out.popover = false;
      }
    }
    return out;
  }

  // src/modules/ai-text-service.js
  function formatSendTextToAiRequest(prompt, text) {
    const p = String(prompt || "").trim();
    const t = String(text || "").trim();
    if (!p) return t;
    if (!t) return p;
    return `${p}: ${t}`;
  }
  function isWordLookupAiAvailable() {
    return false;
  }
  function filterFunctionParameterOptions(functionId, param) {
    const options = Array.isArray(param?.options) ? param.options : [];
    if (functionId === "LOOKUP_WORD" && param?.id === "source" && !isWordLookupAiAvailable()) {
      return options.filter((o) => o && o.id !== "ai");
    }
    return options;
  }
  function shouldShowFunctionParameter(functionId, param) {
    if (!param) return false;
    if (functionId === "LOOKUP_WORD" && param.id === "source" && !isWordLookupAiAvailable()) {
      return false;
    }
    return true;
  }
  function getLanguageModelCtor() {
    try {
      if (typeof globalThis.LanguageModel === "function") return globalThis.LanguageModel;
    } catch {
    }
    try {
      if (typeof globalThis.ai?.languageModel?.create === "function") {
        return globalThis.ai.languageModel;
      }
    } catch {
    }
    return null;
  }
  async function isLanguageModelAvailable(LM) {
    try {
      if (typeof LM.availability === "function") {
        const a = await LM.availability();
        return a === "available" || a === "readily" || a === "after-download";
      }
    } catch {
    }
    try {
      if (typeof LM.capabilities === "function") {
        const c = await LM.capabilities();
        const avail = c?.available || c?.availability;
        return avail === "readily" || avail === "available" || avail === "after-download";
      }
    } catch {
    }
    return typeof LM.create === "function";
  }
  async function sendTextToAi(opts = {}) {
    const prompt = String(opts.prompt || "").trim();
    const text = String(opts.text || "").trim();
    const request = formatSendTextToAiRequest(prompt, text);
    if (!text) {
      return { ok: false, error: "No text selected", request };
    }
    if (!prompt) {
      return { ok: false, error: "Prompt is empty \u2014 set Instruction in Config", request };
    }
    const LM = getLanguageModelCtor();
    if (LM) {
      try {
        const available = await isLanguageModelAvailable(LM);
        if (!available) {
          return {
            ok: false,
            error: "On-device AI is not available in this browser",
            request,
            provider: "languageModel"
          };
        }
        const session = await LM.create({
          signal: opts.signal
        });
        try {
          const result = await session.prompt(request, { signal: opts.signal });
          const out = String(result ?? "").trim();
          if (!out) {
            return { ok: false, error: "AI returned an empty response", request, provider: "languageModel" };
          }
          return { ok: true, text: out, request, provider: "languageModel" };
        } finally {
          try {
            session.destroy?.();
          } catch {
          }
        }
      } catch (e) {
        const msg = e?.message || String(e) || "AI request failed";
        return { ok: false, error: msg, request, provider: "languageModel" };
      }
    }
    return {
      ok: false,
      error: "No AI provider available (Chrome Prompt API not supported here)",
      request
    };
  }

  // src/config/function-library.js
  var TEXT_ACTIVE_BUILTIN_FUNCTION_IDS = /* @__PURE__ */ new Set([
    "TYPE_CHARACTERS"
  ]);
  var CLIPBOARD_OR_MEDIA_LIBRARY_DESTINATIONS = Object.freeze([
    ACTION_RESULT_DESTINATIONS.CLIPBOARD,
    ACTION_RESULT_DESTINATIONS.MEDIA_LIBRARY,
    ACTION_RESULT_DESTINATIONS.CLIPBOARD_AND_MEDIA_LIBRARY
  ]);
  var VIDEO_COPY_DESTINATIONS = Object.freeze([
    ACTION_RESULT_DESTINATIONS.MEDIA_LIBRARY,
    ACTION_RESULT_DESTINATIONS.CLIPBOARD_AND_MEDIA_LIBRARY,
    ACTION_RESULT_DESTINATIONS.CLIPBOARD
  ]);
  var BUILTIN_FUNCTION_DATA_TAGS = Object.freeze({
    COPY_HOVERED_IMAGE: Object.freeze({
      dataSource: "underCursor",
      dataKind: "media",
      destinations: CLIPBOARD_OR_MEDIA_LIBRARY_DESTINATIONS
    }),
    COPY_HOVERED_URL: Object.freeze({
      dataSource: "underCursor",
      dataKind: "text",
      destinations: CLIPBOARD_OR_MEDIA_LIBRARY_DESTINATIONS
    }),
    POI_WEBSITE: Object.freeze({
      dataSource: "underCursor",
      dataKind: "text"
    }),
    POI_ADDRESS: Object.freeze({
      dataSource: "underCursor",
      dataKind: "text",
      destinations: Object.freeze([
        ACTION_RESULT_DESTINATIONS.CLIPBOARD,
        ACTION_RESULT_DESTINATIONS.MEDIA_LIBRARY
      ])
    }),
    COPY_HOVERED_VIDEO: Object.freeze({
      dataSource: "underCursor",
      dataKind: "media",
      destinations: VIDEO_COPY_DESTINATIONS
    }),
    FONT_INFO: Object.freeze({
      dataSource: "underCursor",
      dataKind: "text",
      destinations: Object.freeze([ACTION_RESULT_DESTINATIONS.POPOVER])
    }),
    // Whole-page scan (not under-cursor); opens a tabbed overlay rather than a sink.
    PAGE_MEDIA: Object.freeze({ dataSource: "none", dataKind: "media" }),
    OPEN_MEDIA_LIBRARY: Object.freeze({ dataSource: "none" }),
    SEND_TEXT_TO_AI: Object.freeze({
      dataSource: "textRange",
      dataKind: "text",
      destinations: Object.freeze([
        ACTION_RESULT_DESTINATIONS.CLIPBOARD,
        ACTION_RESULT_DESTINATIONS.POPOVER,
        ACTION_RESULT_DESTINATIONS.BOTH
      ])
    }),
    CLIPBOARD_COPY: Object.freeze({ dataSource: "none" }),
    CLIPBOARD_CUT: Object.freeze({ dataSource: "none" }),
    CLIPBOARD_PASTE: Object.freeze({ dataSource: "none" }),
    CLIPBOARD_SELECT_ALL: Object.freeze({ dataSource: "none" }),
    SELECT_WORD: Object.freeze({ dataSource: "underCursor", dataKind: "text" }),
    SELECT_SENTENCE: Object.freeze({ dataSource: "underCursor", dataKind: "text" }),
    SELECT_PARAGRAPH: Object.freeze({ dataSource: "underCursor", dataKind: "text" }),
    SELECT_IMAGE: Object.freeze({ dataSource: "underCursor", dataKind: "media" })
  });
  var FIXED_KEY_FUNCTION_IDS = Object.freeze([
    "SEND_TEXT_TO_AI",
    "RECTANGLE_HIGHLIGHT",
    "HIGHLIGHT",
    "COPY_HOVERED_IMAGE",
    "COPY_HOVERED_URL",
    "COPY_HOVERED_VIDEO",
    "POI_ADDRESS",
    "PAGE_TOP",
    "PAGE_BOTTOM",
    "PREVIEW_LINK_POPOVER",
    "OPEN_POPOVER"
  ]);
  var UNIT_SELECT_FUNCTION_IDS = Object.freeze([
    "SELECT_WORD",
    "SELECT_SENTENCE",
    "SELECT_PARAGRAPH",
    "SELECT_IMAGE"
  ]);
  var EDGE_SCROLL_PARAMETERS = Object.freeze([
    Object.freeze({
      id: "mode",
      label: "Jump style",
      type: "enum",
      defaultValue: "fade",
      options: Object.freeze([
        Object.freeze({ id: "fade", label: "Fade" }),
        Object.freeze({ id: "smooth", label: "Scroll" })
      ])
    })
  ]);
  var UNIT_SELECT_MODE_PARAMETERS = Object.freeze([
    Object.freeze({
      id: "mode",
      label: "Selection mode",
      type: "enum",
      defaultValue: "exclusive",
      options: Object.freeze([
        Object.freeze({ id: "exclusive", label: "Exclusive" }),
        Object.freeze({ id: "cumulative", label: "Cumulative" })
      ])
    })
  ]);
  var BUILTIN_FUNCTION_PARAMETER_OVERRIDES = Object.freeze({
    PAGE_TOP: EDGE_SCROLL_PARAMETERS,
    PAGE_BOTTOM: EDGE_SCROLL_PARAMETERS,
    SELECT_WORD: UNIT_SELECT_MODE_PARAMETERS,
    SELECT_SENTENCE: UNIT_SELECT_MODE_PARAMETERS,
    SELECT_PARAGRAPH: UNIT_SELECT_MODE_PARAMETERS,
    SELECT_IMAGE: UNIT_SELECT_MODE_PARAMETERS,
    HIGHLIGHT: Object.freeze([
      Object.freeze({
        id: "mode",
        label: "Copy as",
        type: "enum",
        defaultValue: "rich",
        options: Object.freeze([
          Object.freeze({ id: "rich", label: "Rich text" }),
          Object.freeze({ id: "plain", label: "Plain text" })
        ])
      })
    ]),
    RECTANGLE_HIGHLIGHT: Object.freeze([
      Object.freeze({
        id: "mode",
        label: "Selection mode",
        type: "enum",
        defaultValue: "element",
        options: Object.freeze([
          Object.freeze({ id: "element", label: "Element rectangle" }),
          Object.freeze({ id: "cumulative", label: "Pick cumulative" })
        ])
      })
    ]),
    SEND_TEXT_TO_AI: Object.freeze([
      Object.freeze({
        id: "prompt",
        label: "Instruction",
        type: "string",
        multiline: true,
        defaultValue: "Translate to English",
        placeholder: "e.g. Translate to English"
      }),
      buildResultDestinationParameter([
        ACTION_RESULT_DESTINATIONS.CLIPBOARD,
        ACTION_RESULT_DESTINATIONS.POPOVER,
        ACTION_RESULT_DESTINATIONS.BOTH
      ])
    ]),
    COPY_HOVERED_IMAGE: Object.freeze([
      buildResultDestinationParameter(CLIPBOARD_OR_MEDIA_LIBRARY_DESTINATIONS)
    ]),
    COPY_HOVERED_URL: Object.freeze([
      buildResultDestinationParameter(CLIPBOARD_OR_MEDIA_LIBRARY_DESTINATIONS)
    ]),
    COPY_HOVERED_VIDEO: Object.freeze([
      buildResultDestinationParameter(VIDEO_COPY_DESTINATIONS)
    ]),
    POI_ADDRESS: Object.freeze([
      Object.freeze({
        id: "action",
        label: "Action",
        type: "enum",
        defaultValue: "copy",
        options: Object.freeze([
          Object.freeze({ id: "copy", label: "Copy address" })
        ])
      }),
      Object.freeze({
        id: "format",
        label: "Format",
        type: "enum",
        defaultValue: "txt",
        options: Object.freeze([
          Object.freeze({ id: "txt", label: "Txt" }),
          Object.freeze({ id: "vcard", label: "vCard" })
        ])
      }),
      buildResultDestinationParameter([
        ACTION_RESULT_DESTINATIONS.CLIPBOARD,
        ACTION_RESULT_DESTINATIONS.MEDIA_LIBRARY
      ])
    ])
  });
  function docsUrl(topicId, hash) {
    return buildKpDeepLink({ kind: "docs", id: topicId, ...hash ? { hash } : {} });
  }
  var FUNCTION_DOCS_URL_BY_ID = Object.freeze({
    ACTIVATE: docsUrl("browsing-click"),
    ACTIVATE_NEW_TAB: docsUrl("browsing-click"),
    ACTIVATE_NEW_TAB_BACKGROUND: docsUrl("browsing-click"),
    PREVIEW_LINK_POPOVER: docsUrl("tools-previews"),
    OPEN_POPOVER: docsUrl("tools-previews"),
    FORWARD: docsUrl("browsing-tabs"),
    BACK: docsUrl("browsing-tabs"),
    BACK2: docsUrl("browsing-tabs"),
    ROOT: docsUrl("browsing-tabs"),
    CLOSE_TAB: docsUrl("browsing-tabs"),
    TAB_LEFT: docsUrl("browsing-tabs"),
    TAB_RIGHT: docsUrl("browsing-tabs"),
    NEW_TAB: docsUrl("browsing-tabs"),
    TAB_HISTORY: docsUrl("tools-tab-history"),
    PAGE_UP_INSTANT: docsUrl("browsing-scroll"),
    PAGE_DOWN_INSTANT: docsUrl("browsing-scroll"),
    PAGE_TOP: docsUrl("browsing-scroll"),
    PAGE_BOTTOM: docsUrl("browsing-scroll"),
    SCROLL_LINE: docsUrl("browsing-scroll"),
    HIGHLIGHT: docsUrl("browsing-select"),
    RECTANGLE_HIGHLIGHT: docsUrl("browsing-select"),
    COPY_HOVERED_IMAGE: docsUrl("media-copy"),
    COPY_HOVERED_URL: docsUrl("media-copy"),
    COPY_HOVERED_VIDEO: docsUrl("media-copy"),
    FONT_INFO: docsUrl("functions", "font-info"),
    PAGE_MEDIA: docsUrl("media-page"),
    DELETE: docsUrl("browsing-modes"),
    COLS_TOGGLE: docsUrl("browsing-modes"),
    OPEN_MEDIA_LIBRARY: docsUrl("media-library"),
    CLIPBOARD_COPY: docsUrl("browsing-select"),
    CLIPBOARD_CUT: docsUrl("browsing-select"),
    CLIPBOARD_PASTE: docsUrl("browsing-select"),
    CLIPBOARD_SELECT_ALL: docsUrl("browsing-select"),
    SELECT_WORD: docsUrl("browsing-select"),
    SELECT_SENTENCE: docsUrl("browsing-select"),
    SELECT_PARAGRAPH: docsUrl("browsing-select"),
    SELECT_IMAGE: docsUrl("browsing-select"),
    SEND_TEXT_TO_AI: docsUrl("functions", "send-text-to-ai"),
    LAUNCHER: docsUrl("tools-launcher"),
    TOP_SITES: docsUrl("tools-top-sites"),
    OMNIBOX: docsUrl("tools-omnibox"),
    TOGGLE_KEYBOARD_HELP: docsUrl("keyboard-reference"),
    OPEN_SETTINGS_POPOVER: docsUrl("settings"),
    CANCEL: docsUrl("browsing-modes"),
    POI_WEBSITE: docsUrl("functions", "poi"),
    POI_ADDRESS: docsUrl("functions", "poi"),
    TYPE_CHARACTERS: docsUrl("functions", "type-characters"),
    EXECUTE_JS: docsUrl("execute-js"),
    GET_TEXT_AT_CURSOR: docsUrl("functions", "get-text-at-cursor"),
    GET_TEXT_RANGE: docsUrl("functions", "get-text-at-cursor"),
    GET_MEDIA_AT_CURSOR: docsUrl("functions", "get-text-at-cursor"),
    LOOKUP_WORD: docsUrl("functions", "lookup-word"),
    TRANSLATE: docsUrl("functions", "translate"),
    SHOW_POPOVER: docsUrl("functions", "show-popover"),
    ADD_URL_TO_MEDIA_LIBRARY: docsUrl("functions", "media-library-functions"),
    FETCH_URL_FOR_MEDIA_LIBRARY: docsUrl("functions", "media-library-functions"),
    SEND_HOTKEY: docsUrl("functions", "keystrokes"),
    SEND_BURST: docsUrl("functions", "keystrokes"),
    CYCLE_ROUND_ROBIN: docsUrl("functions", "keystrokes"),
    HOLD_CONTINUOUS: docsUrl("functions", "keystrokes"),
    CLICK_MOUSE_BUTTON: docsUrl("functions", "keystrokes"),
    REMAP_KEY: docsUrl("functions", "keystrokes")
  });
  var DEFAULT_FUNCTION_DOCS_URL = docsUrl("functions");
  function getFunctionDocsUrl(functionId) {
    const id = String(functionId || "");
    return FUNCTION_DOCS_URL_BY_ID[id] || DEFAULT_FUNCTION_DOCS_URL;
  }
  function withDocsUrl(def) {
    if (!def || !def.id) return def;
    const url = getFunctionDocsUrl(def.id);
    return Object.freeze({ ...def, docsUrl: url });
  }
  var KEYSTROKE_FUNCTION_CATEGORY = "Keystrokes";
  var TEXT_FUNCTION_CATEGORY = "Type";
  var DATA_FUNCTION_CATEGORY = "Data";
  var LOOKUP_FUNCTION_CATEGORY = "Lookup";
  var TRANSLATE_FUNCTION_CATEGORY = "Translate";
  var DISPLAY_FUNCTION_CATEGORY = "Display";
  var SCRIPT_FUNCTION_CATEGORY = "Script";
  var MEDIA_LIBRARY_FUNCTION_CATEGORY = "Media Library";
  var FUNCTION_ID_BY_MACRO_KEY_KIND = Object.freeze({
    hotkey: "SEND_HOTKEY",
    burst: "SEND_BURST",
    roundRobin: "CYCLE_ROUND_ROBIN",
    continuous: "HOLD_CONTINUOUS",
    mouse: "CLICK_MOUSE_BUTTON",
    key: "REMAP_KEY"
  });
  var MACRO_KEY_KIND_BY_FUNCTION_ID = Object.freeze(
    Object.fromEntries(Object.entries(FUNCTION_ID_BY_MACRO_KEY_KIND).map(([k, v]) => [v, k]))
  );
  function buildKeystrokeFunctionDefs() {
    const out = {};
    for (const kindDef of MACRO_KEY_KIND_DEFS) {
      const functionId = FUNCTION_ID_BY_MACRO_KEY_KIND[kindDef.id];
      if (!functionId || isBuildExcludedKeyAction(functionId)) continue;
      out[functionId] = withDocsUrl(Object.freeze({
        id: functionId,
        label: kindDef.label,
        description: kindDef.description,
        ...kindDef.details ? { details: kindDef.details } : {},
        handler: "handleLegacyMacroKeyFunction",
        category: KEYSTROKE_FUNCTION_CATEGORY,
        keyboardClass: kindDef.keyboardClass,
        // Non-empty sentinel so isFunctionInstantiable() is true; the actual per-field schema
        // for these kinds lives in macro-keys.js's kind-specific config shapes (steps[], stroke, …)
        // and isn't representable in the generic FunctionParameterDef shape yet.
        parameters: Object.freeze([
          Object.freeze({ id: "config", label: "Configuration", type: "string" })
        ]),
        legacyMacroKeyKind: kindDef.id
      }));
    }
    return out;
  }
  var TYPE_CHARACTERS_FUNCTION_DEF = Object.freeze({
    id: "TYPE_CHARACTERS",
    label: "Type Characters",
    description: "Type saved text into a field",
    details: "Types configured text into the focused field each time the key is pressed. Create multiple Action Instances of this Function \u2014 each with its own text \u2014 and place them on different keys.",
    handler: "handleTypeCharactersKey",
    category: TEXT_FUNCTION_CATEGORY,
    keyboardClass: "key-purple",
    dataSource: "none",
    worksWhileTyping: TEXT_ACTIVE_BUILTIN_FUNCTION_IDS.has("TYPE_CHARACTERS"),
    parameters: Object.freeze([
      Object.freeze({
        id: "text",
        label: "Text to type",
        type: "string",
        multiline: true,
        defaultValue: "",
        placeholder: "e.g. your email address, a signature, a snippet\u2026"
      })
    ])
  });
  var EXECUTE_JS_SCRIPT_PLACEHOLDER = "// Bindings: kpHoveredClickable, kpHoverLeaf, kpFocusedTextField,\n//   kpMode, kpPageUrl, kpSelection, kpPriorResult\n// Callbacks (only if checked below): showPopover, copyToClipboard, notify\n// Example:\n// return kpHoveredClickable && kpHoveredClickable.textContent;";
  var EXECUTE_JS_FUNCTION_DEF = Object.freeze({
    id: "EXECUTE_JS",
    label: "Execute JS",
    description: "Run a pasted JS snippet",
    details: "Runs a pasted JavaScript snippet in the content-script isolated world with the hovered clickable and other page state. Optional callbacks (popover, clipboard, notify) are injected only when checked on the Action Instance.",
    handler: "handleExecuteJsKey",
    category: SCRIPT_FUNCTION_CATEGORY,
    keyboardClass: "key-purple",
    dataSource: "underCursor",
    parameters: Object.freeze([
      Object.freeze({
        id: "script",
        label: "Script",
        type: "string",
        multiline: true,
        rows: 10,
        defaultValue: "",
        placeholder: EXECUTE_JS_SCRIPT_PLACEHOLDER
      }),
      Object.freeze({
        id: "cbShowPopover",
        label: "Show result in popover (showPopover)",
        type: "boolean",
        defaultValue: false,
        group: "Callbacks"
      }),
      Object.freeze({
        id: "cbCopyToClipboard",
        label: "Copy to clipboard (copyToClipboard)",
        type: "boolean",
        defaultValue: false,
        group: "Callbacks"
      }),
      Object.freeze({
        id: "cbNotify",
        label: "Flash notification (notify)",
        type: "boolean",
        defaultValue: false,
        group: "Callbacks"
      })
    ])
  });
  function buildBuiltinActionFunctionDefs() {
    const out = {};
    for (const [id, def] of Object.entries(KEYBINDING_ACTION_DEFS)) {
      if (isBuildExcludedKeyAction(id)) continue;
      out[id] = withDocsUrl(Object.freeze({
        id,
        label: def.label,
        description: def.description,
        ...def.details ? { details: def.details } : {},
        handler: def.handler,
        category: KEYBINDING_ACTION_CATEGORY_BY_ID[id] || "Other",
        keyboardClass: def.keyboardClass ?? null,
        // No `parameters` by default: most built-ins remain simple/non-instantiable Functions.
        // A few (SEND_TEXT_TO_AI, RECTANGLE_HIGHLIGHT, HIGHLIGHT, COPY_HOVERED_IMAGE,
        // COPY_HOVERED_URL, COPY_HOVERED_VIDEO, PREVIEW_LINK_POPOVER, OPEN_POPOVER)
        // get their schema below from
        // BUILTIN_FUNCTION_PARAMETER_OVERRIDES — see KEY_ACTION_ARCHITECTURE.md "Migration mapping".
        ...TEXT_ACTIVE_BUILTIN_FUNCTION_IDS.has(id) ? { worksWhileTyping: true } : {},
        ...def.mode ? { mode: def.mode } : {},
        ...def.cancelOnPointerDown ? { cancelOnPointerDown: true } : {},
        ...def.pointerBinding ? { pointerBinding: def.pointerBinding } : {},
        ...BUILTIN_FUNCTION_DATA_TAGS[id] || {},
        ...BUILTIN_FUNCTION_PARAMETER_OVERRIDES[id] ? { parameters: BUILTIN_FUNCTION_PARAMETER_OVERRIDES[id] } : {}
      }));
    }
    return out;
  }
  function buildDataAcquisitionFunctionDefs() {
    const granularityOptions = (ids) => ({
      id: "granularity",
      label: "Granularity",
      type: "enum",
      defaultValue: ids[0],
      options: ids.map((id) => ({
        id,
        label: id === "word" ? "Word" : id === "sentence" ? "Sentence" : id === "paragraph" ? "Paragraph" : "Hyperlink"
      }))
    });
    return {
      GET_TEXT_AT_CURSOR: Object.freeze({
        id: "GET_TEXT_AT_CURSOR",
        label: "Get Text At Cursor",
        description: "Copy text under the cursor",
        details: "Reads the word, sentence, paragraph, or hyperlink under the cursor (choose granularity on the Action Instance) and copies it to the clipboard.",
        handler: "handleGetTextAtCursorKey",
        category: DATA_FUNCTION_CATEGORY,
        dataSource: "underCursor",
        dataKind: "text",
        destinations: Object.freeze([ACTION_RESULT_DESTINATIONS.CLIPBOARD]),
        parameters: Object.freeze([Object.freeze(granularityOptions(["word", "sentence", "paragraph", "hyperlink"]))])
      }),
      GET_TEXT_RANGE: Object.freeze({
        id: "GET_TEXT_RANGE",
        label: "Get Text Range",
        description: "Pass selection to the next Macro Step",
        details: "Reads the current highlight/selection into the next Macro Step. A data primitive \u2014 add it as a Macro Step, not as a key action (use Copy for that).",
        handler: "handleGetTextRangeKey",
        category: DATA_FUNCTION_CATEGORY,
        dataSource: "textRange",
        dataKind: "text",
        assignableToKey: false
      }),
      GET_MEDIA_AT_CURSOR: Object.freeze({
        id: "GET_MEDIA_AT_CURSOR",
        label: "Get Media At Cursor",
        description: "Copy media under the cursor",
        details: "Reads the image, video, or audio under the cursor (choose kind on the Action Instance) and copies it \u2014 or its URL \u2014 to the clipboard.",
        handler: "handleGetMediaAtCursorKey",
        category: DATA_FUNCTION_CATEGORY,
        dataSource: "underCursor",
        dataKind: "media",
        destinations: Object.freeze([ACTION_RESULT_DESTINATIONS.CLIPBOARD]),
        parameters: Object.freeze([Object.freeze({
          id: "kind",
          label: "Media kind",
          type: "enum",
          defaultValue: "image",
          options: Object.freeze([
            Object.freeze({ id: "image", label: "Image" }),
            Object.freeze({ id: "video", label: "Video" }),
            Object.freeze({ id: "audio", label: "Audio" })
          ])
        })])
      }),
      LOOKUP_WORD: Object.freeze({
        id: "LOOKUP_WORD",
        label: "Lookup Word",
        description: "Dictionary definition under cursor",
        details: "Shows a Free Dictionary API definition popover for the word under the cursor. No AI setup required; optionally switch the source to Ask AI on the Action Instance.",
        handler: "handleLookupWordKey",
        category: LOOKUP_FUNCTION_CATEGORY,
        dataSource: "underCursor",
        dataKind: "text",
        destinations: Object.freeze([ACTION_RESULT_DESTINATIONS.POPOVER]),
        parameters: Object.freeze([
          Object.freeze({
            id: "source",
            label: "Source",
            type: "enum",
            defaultValue: "dictionary",
            options: Object.freeze([
              Object.freeze({ id: "dictionary", label: "Dictionary" }),
              Object.freeze({ id: "ai", label: "Ask AI instead" })
            ])
          })
        ])
      }),
      TRANSLATE: Object.freeze({
        id: "TRANSLATE",
        label: "Translate",
        description: "Translate selection or under-cursor text",
        details: "Translates the highlighted text, or the word/sentence/paragraph under the cursor when nothing is highlighted. Configure target language and whether the result replaces page text or opens in a popover.",
        handler: "handleTranslateKey",
        category: TRANSLATE_FUNCTION_CATEGORY,
        dataSource: "underCursor",
        dataKind: "text",
        destinations: Object.freeze([ACTION_RESULT_DESTINATIONS.MODIFY_PAGE, ACTION_RESULT_DESTINATIONS.POPOVER]),
        parameters: Object.freeze([
          Object.freeze(granularityOptions(["sentence", "word", "paragraph"])),
          Object.freeze({
            id: "targetLanguage",
            label: "Target language",
            type: "string",
            defaultValue: "English",
            placeholder: "e.g. English, Spanish, Japanese\u2026"
          }),
          buildResultDestinationParameter([
            ACTION_RESULT_DESTINATIONS.MODIFY_PAGE,
            ACTION_RESULT_DESTINATIONS.POPOVER
          ])
        ])
      }),
      SHOW_POPOVER: Object.freeze({
        id: "SHOW_POPOVER",
        label: "Show Popover",
        description: "Show prior Macro Step result",
        details: "Renders the previous Macro Step\u2019s result (or configured fallback text) in a popover. A display primitive \u2014 add it as a Macro Step, not as a key action.",
        handler: "handleShowPopoverKey",
        category: DISPLAY_FUNCTION_CATEGORY,
        dataSource: "none",
        destinations: Object.freeze([ACTION_RESULT_DESTINATIONS.POPOVER]),
        assignableToKey: false,
        parameters: Object.freeze([Object.freeze({
          id: "content",
          label: "Content",
          type: "string",
          multiline: true,
          defaultValue: "",
          placeholder: "Fallback text if the previous step produced none\u2026"
        })])
      }),
      ADD_URL_TO_MEDIA_LIBRARY: Object.freeze({
        id: "ADD_URL_TO_MEDIA_LIBRARY",
        label: "Add URL to Media Library",
        description: "Save hovered href (no download)",
        details: "Stores the hyperlink under the cursor itself (its href) in Media Library \u2014 does not download the resource the link points to.",
        handler: "handleAddUrlToMediaLibraryKey",
        category: MEDIA_LIBRARY_FUNCTION_CATEGORY,
        dataSource: "underCursor",
        dataKind: "text",
        destinations: Object.freeze([ACTION_RESULT_DESTINATIONS.MEDIA_LIBRARY])
      }),
      FETCH_URL_FOR_MEDIA_LIBRARY: Object.freeze({
        id: "FETCH_URL_FOR_MEDIA_LIBRARY",
        label: "Fetch URL for Media Library",
        description: "Download linked file into library",
        details: "Fetches the resource the hyperlink under the cursor points to (for example a .pdf, .mp3, or .mp4) and stores it in Media Library. Images, videos, and documents are classified from the file type. Web pages are not downloaded \u2014 use Add URL to Media Library for those.",
        handler: "handleFetchUrlForMediaLibraryKey",
        category: MEDIA_LIBRARY_FUNCTION_CATEGORY,
        dataSource: "urlFetch",
        dataKind: "file",
        destinations: Object.freeze([ACTION_RESULT_DESTINATIONS.MEDIA_LIBRARY])
      })
    };
  }
  function omitBuildExcludedFunctions(defs) {
    return Object.fromEntries(
      Object.entries(defs).filter(([id]) => !isBuildExcludedKeyAction(id))
    );
  }
  var FUNCTION_LIBRARY = Object.freeze(omitBuildExcludedFunctions({
    ...buildBuiltinActionFunctionDefs(),
    ...buildKeystrokeFunctionDefs(),
    [TYPE_CHARACTERS_FUNCTION_DEF.id]: withDocsUrl(TYPE_CHARACTERS_FUNCTION_DEF),
    [EXECUTE_JS_FUNCTION_DEF.id]: withDocsUrl(EXECUTE_JS_FUNCTION_DEF),
    ...Object.fromEntries(
      Object.entries(buildDataAcquisitionFunctionDefs()).map(([id, def]) => [id, withDocsUrl(def)])
    )
  }));
  var FUNCTION_CATEGORY_ORDER = Object.freeze([
    "Navigation",
    "Tab Control",
    "Begin URL",
    "Get Page Data",
    "Maps",
    "Scroll",
    "Select",
    "Clipboard",
    TEXT_FUNCTION_CATEGORY,
    KEYSTROKE_FUNCTION_CATEGORY,
    DATA_FUNCTION_CATEGORY,
    LOOKUP_FUNCTION_CATEGORY,
    TRANSLATE_FUNCTION_CATEGORY,
    DISPLAY_FUNCTION_CATEGORY,
    SCRIPT_FUNCTION_CATEGORY,
    MEDIA_LIBRARY_FUNCTION_CATEGORY,
    "AI",
    "KeyPilot",
    "Tools",
    "System",
    "Other"
  ]);
  var FUNCTION_CATEGORY_DESCRIPTIONS = Object.freeze({
    Navigation: "Click links, preview, and move through browsing history.",
    "Tab Control": "Open, close, switch, and review tabs.",
    "Begin URL": "Jump to a URL via Launcher, Top Sites, or the omnibox.",
    "Get Page Data": "Capture text, images, video, URLs, fonts, and other page media.",
    Maps: "Open a place\u2019s website or address from a map pin under the cursor.",
    Scroll: "Scroll the page by line, page, or to the top/bottom.",
    Select: "Delete or toggle multi-column selection helpers.",
    Clipboard: "Copy, cut, paste, select-all, and select word, sentence, paragraph, or image under the cursor.",
    [TEXT_FUNCTION_CATEGORY]: "Type saved text into the focused field.",
    [KEYSTROKE_FUNCTION_CATEGORY]: "Send keystrokes, chords, bursts, and mouse remaps.",
    [DATA_FUNCTION_CATEGORY]: "Read text or media under the cursor, or from a highlight.",
    [LOOKUP_FUNCTION_CATEGORY]: "Look up a definition for the word under the cursor.",
    [TRANSLATE_FUNCTION_CATEGORY]: "Translate highlighted or under-cursor text.",
    [DISPLAY_FUNCTION_CATEGORY]: "Show a previous Macro Step\u2019s result in a popover.",
    [SCRIPT_FUNCTION_CATEGORY]: "Run a custom JavaScript snippet against page state.",
    [MEDIA_LIBRARY_FUNCTION_CATEGORY]: "Save links or fetched files into Media Library.",
    AI: "Send selected text to AI with a prompt and result destination.",
    KeyPilot: "Open KeyPilot chrome \u2014 keyboard reference, settings, and modes.",
    Tools: "Utility overlays and helpers.",
    System: "Cancel the current KeyPilot gesture or mode.",
    Other: "Uncategorized Functions."
  });
  var LIBRARY_SECTION_DESCRIPTIONS = Object.freeze({
    macros: "Multi-step sequences you can place on a key. Customize a stock macro to fork an editable copy.",
    macroKeys: "Saved Macro Key instances (hotkey, burst, round-robin, and related kinds) ready to place."
  });
  var FUNCTION_LIBRARY_ITEM_ORDER = Object.freeze({
    // Navigation
    ACTIVATE: 10,
    ACTIVATE_NEW_TAB: 20,
    ACTIVATE_NEW_TAB_BACKGROUND: 30,
    PREVIEW_LINK_POPOVER: 40,
    POI_WEBSITE: 45,
    POI_ADDRESS: 46,
    OPEN_POPOVER: 50,
    FORWARD: 60,
    BACK: 70,
    BACK2: 80,
    ROOT: 90,
    // Tab Control
    CLOSE_TAB: 110,
    TAB_LEFT: 120,
    TAB_RIGHT: 130,
    NEW_TAB: 140,
    TAB_HISTORY: 150,
    // Begin URL
    TOP_SITES: 155,
    LAUNCHER: 160,
    OMNIBOX: 170,
    // Get Page Data
    COPY_HOVERED_IMAGE: 200,
    COPY_HOVERED_VIDEO: 201,
    COPY_HOVERED_URL: 202,
    FONT_INFO: 203,
    PAGE_MEDIA: 205,
    RECTANGLE_HIGHLIGHT: 210,
    HIGHLIGHT: 220,
    // Clipboard
    CLIPBOARD_COPY: 230,
    CLIPBOARD_CUT: 231,
    CLIPBOARD_PASTE: 232,
    CLIPBOARD_SELECT_ALL: 233,
    SELECT_WORD: 234,
    SELECT_SENTENCE: 235,
    SELECT_PARAGRAPH: 236,
    SELECT_IMAGE: 237,
    // KeyPilot
    TOGGLE_KEYBOARD_HELP: 280,
    OPEN_SETTINGS_POPOVER: 290
  });
  function sortFunctionDefsForLibrary(defs) {
    return [...defs || []].sort((a, b) => {
      const oa = FUNCTION_LIBRARY_ITEM_ORDER[a?.id] ?? 1e4;
      const ob = FUNCTION_LIBRARY_ITEM_ORDER[b?.id] ?? 1e4;
      if (oa !== ob) return oa - ob;
      return String(a?.label || a?.id || "").localeCompare(String(b?.label || b?.id || ""));
    });
  }
  function getFunctionDef(functionId) {
    const id = String(functionId || "");
    return id && FUNCTION_LIBRARY[id] || null;
  }
  function listFunctionDefs() {
    return Object.values(FUNCTION_LIBRARY);
  }
  function listPointerBoundFunctionDefs() {
    return listFunctionDefs().filter((d) => d && d.pointerBinding);
  }
  function getFunctionCategory(functionId) {
    return getFunctionDef(functionId)?.category || "Other";
  }
  function getFunctionCategoryDescription(category) {
    const cat = String(category || "");
    return FUNCTION_CATEGORY_DESCRIPTIONS[cat] || "";
  }
  function isFunctionInstantiable(functionId) {
    const def = getFunctionDef(functionId);
    if (!def?.parameters || def.parameters.length === 0) return false;
    if (functionId === "LOOKUP_WORD" && !isWordLookupAiAvailable()) return false;
    if (UNIT_SELECT_FUNCTION_IDS.includes(functionId)) return false;
    return true;
  }
  function functionAssignableToKey(functionId) {
    const def = getFunctionDef(functionId);
    if (!def) return false;
    return def.assignableToKey !== false;
  }
  function defaultFunctionParameters(functionId) {
    const def = getFunctionDef(functionId);
    if (!def) return {};
    if (def.legacyMacroKeyKind) return { config: defaultMacroKeyConfig(def.legacyMacroKeyKind) };
    const out = {};
    for (const p of def.parameters || []) {
      out[p.id] = p.defaultValue;
    }
    return out;
  }
  function normalizeFunctionParameters(functionId, raw) {
    const def = getFunctionDef(functionId);
    if (!def) return {};
    const src = raw && typeof raw === "object" ? raw : {};
    if (def.legacyMacroKeyKind) {
      return { config: normalizeMacroKeyConfig(def.legacyMacroKeyKind, src.config) };
    }
    const defaults = defaultFunctionParameters(functionId);
    const out = {};
    for (const p of def.parameters || []) {
      const v = src[p.id];
      switch (p.type) {
        case "boolean":
          out[p.id] = typeof v === "boolean" ? v : !!defaults[p.id];
          break;
        case "number": {
          const n = Number(v);
          out[p.id] = Number.isFinite(n) ? n : defaults[p.id];
          break;
        }
        case "enum":
          out[p.id] = (p.options || []).some((o) => o.id === v) ? v : defaults[p.id];
          break;
        default:
          out[p.id] = v !== void 0 ? String(v) : defaults[p.id] ?? "";
      }
    }
    return out;
  }
  function summarizeFunctionParameters(functionId, parameters) {
    const def = getFunctionDef(functionId);
    if (!def) return "";
    if (def.legacyMacroKeyKind) {
      return summarizeMacroKey({ kind: def.legacyMacroKeyKind, config: parameters?.config });
    }
    if (functionId === "TYPE_CHARACTERS") {
      const text = String(parameters?.text || "");
      if (!text) return "(empty)";
      return text.length > 24 ? `${text.slice(0, 24)}\u2026` : text;
    }
    if (functionId === "SHOW_POPOVER") {
      const text = String(parameters?.content || "").trim();
      if (!text) return "(previous step)";
      return text.length > 24 ? `${text.slice(0, 24)}\u2026` : text;
    }
    if (functionId === "EXECUTE_JS") {
      const lines = String(parameters?.script || "").split(/\r?\n/);
      for (const line of lines) {
        const t = line.trim();
        if (!t || t.startsWith("//") || t.startsWith("*") || t.startsWith("/*")) continue;
        return t.length > 32 ? `${t.slice(0, 32)}\u2026` : t;
      }
      return String(parameters?.script || "").trim() ? "(script)" : "(empty)";
    }
    return "";
  }
  function macroKeyKindFromFunctionId(functionId) {
    return MACRO_KEY_KIND_BY_FUNCTION_ID[String(functionId || "")] || null;
  }
  function functionWorksWhileTyping(functionId) {
    return !!getFunctionDef(functionId)?.worksWhileTyping;
  }
  function functionCancelsOnPointerDown(mode) {
    const m = String(mode || "");
    if (!m || m === "none") return false;
    for (const def of Object.values(FUNCTION_LIBRARY)) {
      if (def?.mode === m && def.cancelOnPointerDown) return true;
    }
    return false;
  }
  function validateFunctionSlotKey(functionId, slotKey) {
    const def = getFunctionDef(functionId);
    if (!def) return { ok: false, reason: `Unknown Function: ${functionId}` };
    if (def.assignableToKey === false) {
      return {
        ok: false,
        reason: `"${def.label}" is a Macro Step, not a key action.`
      };
    }
    if (def.worksWhileTyping && !isChordSlotKey(slotKey)) {
      return {
        ok: false,
        reason: `"${def.label}" must run while a text field is focused, so it can only be bound to a modifier-key combination (e.g. Ctrl+Alt+\u2026), not a plain key.`
      };
    }
    return { ok: true };
  }

  // src/modules/keyboard-layout-store.js
  init_keyboard_layouts();

  // src/config/stock-macros.js
  var STOCK_MACRO_ID_PREFIX = "stock:";
  var STOCK_MACROS = Object.freeze([
    Object.freeze({
      id: `${STOCK_MACRO_ID_PREFIX}ai-assist-flow`,
      label: "AI Assist Flow",
      icon: "placeholder",
      steps: Object.freeze([
        Object.freeze({ kind: "function", functionId: "HIGHLIGHT", parameters: {} }),
        Object.freeze({ kind: "wait", ms: 80 }),
        Object.freeze({ kind: "function", functionId: "CLIPBOARD_COPY", parameters: {} }),
        Object.freeze({ kind: "function", functionId: "OPEN_POPOVER", parameters: {} })
      ])
    }),
    Object.freeze({
      id: `${STOCK_MACRO_ID_PREFIX}quick-nav`,
      label: "Quick Nav",
      icon: "placeholder",
      steps: Object.freeze([
        Object.freeze({ kind: "function", functionId: "PAGE_DOWN_INSTANT", parameters: {}, delayMsBefore: 0 }),
        Object.freeze({ kind: "function", functionId: "TAB_RIGHT", parameters: {}, delayMsBefore: 50 })
      ])
    }),
    Object.freeze({
      id: `${STOCK_MACRO_ID_PREFIX}clip-search`,
      label: "Clip & Search",
      icon: "placeholder",
      steps: Object.freeze([
        Object.freeze({ kind: "function", functionId: "CLIPBOARD_COPY", parameters: {} }),
        Object.freeze({ kind: "gate", op: "truthy", left: "prior", thenSkip: 1 }),
        Object.freeze({ kind: "function", functionId: "OMNIBOX", parameters: {} })
      ])
    })
  ]);
  function isStockMacroId(id) {
    return String(id || "").startsWith(STOCK_MACRO_ID_PREFIX);
  }
  function getStockMacroById(id) {
    const key = String(id || "");
    const found = STOCK_MACROS.find((m) => m.id === key);
    return found || null;
  }
  function listStockMacros() {
    return STOCK_MACROS.map((m) => ({
      id: m.id,
      label: m.label,
      icon: m.icon,
      steps: m.steps.map((s) => ({ ...s, parameters: s.parameters ? { ...s.parameters } : void 0 }))
    }));
  }
  function resolveMacroById(id, userMacros) {
    const key = String(id || "");
    if (!key) return null;
    const users = Array.isArray(userMacros) ? userMacros : [];
    const user = users.find((m) => m && m.id === key);
    if (user) {
      return {
        id: user.id,
        label: String(user.label || "Macro"),
        steps: Array.isArray(user.steps) ? user.steps : [],
        stock: false,
        baseStockMacroId: user.baseStockMacroId
      };
    }
    const stock = getStockMacroById(key);
    if (!stock) return null;
    return {
      id: stock.id,
      label: stock.label,
      steps: stock.steps.map((s) => ({ ...s })),
      stock: true
    };
  }

  // src/modules/keyboard-layout-store.js
  var KEYBOARD_LAYOUT_STORE_KEY = "kp_keyboard_layout_store_v1";
  function getEmptyKeyboardLayoutStore() {
    return {
      version: 1,
      layouts: {},
      macros: {},
      actions: {}
    };
  }
  function nowMs() {
    return Date.now();
  }
  function genId(prefix) {
    try {
      const id = typeof crypto !== "undefined" && crypto && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : `${nowMs()}_${Math.random().toString(16).slice(2)}`;
      return `${prefix}${id}`;
    } catch {
      return `${prefix}${nowMs()}_${Math.random().toString(16).slice(2)}`;
    }
  }
  function normalizeSlotLabel(raw) {
    const s = String(raw || "").trim();
    if (!s) return "";
    return s.length === 1 ? s.toUpperCase() : s;
  }
  var HANDEDNESS_SLOT_PAIRS = Object.freeze([
    ["Q", "P"],
    ["W", "O"],
    ["E", "I"],
    ["R", "U"],
    ["T", "Y"],
    ["A", ";"],
    ["S", "L"],
    ["D", "K"],
    ["F", "J"],
    ["G", "H"],
    ["Z", "/"],
    ["X", "."],
    ["C", ","],
    ["V", "M"],
    ["B", "N"]
  ]);
  var MIRRORED_PHYSICAL_SLOT = Object.freeze(Object.fromEntries(
    HANDEDNESS_SLOT_PAIRS.flatMap(([a, b]) => [[a, b], [b, a]])
  ));
  function mirrorKeyboardLayoutSlotKey(slotKey) {
    const key = String(slotKey || "");
    const chordPrefix = "CHORD:";
    if (key.startsWith(chordPrefix)) {
      const parts = key.slice(chordPrefix.length).split("+");
      const last = parts.pop();
      if (!last) return key;
      const mirrored = MIRRORED_PHYSICAL_SLOT[String(last).toUpperCase()];
      return mirrored ? `${chordPrefix}${[...parts, mirrored].join("+")}` : key;
    }
    return MIRRORED_PHYSICAL_SLOT[key.toUpperCase()] || key;
  }
  function slotLabelFromBinding(binding) {
    return physicalSlotLabelFromBinding(binding);
  }
  function normalizeMacroStep(step) {
    if (!step || typeof step !== "object") return null;
    const rawKind = String(step.kind || (step.functionId ? "function" : "")).trim();
    const kind = rawKind === "run_macro" ? "runMacro" : rawKind;
    if (kind === "wait") {
      const ms = Math.max(0, Math.floor(Number(step.ms) || 0));
      return { kind: "wait", ms };
    }
    if (kind === "stop") {
      return { kind: "stop" };
    }
    if (kind === "runMacro") {
      const macroId = String(step.macroId || "").trim();
      if (!macroId) return null;
      return { kind: "runMacro", macroId };
    }
    if (kind === "gate") {
      const thenSkip = Math.max(0, Math.floor(Number(step.thenSkip) || 0));
      const out = {
        kind: "gate",
        op: String(step.op || "truthy"),
        left: String(step.left || "prior")
      };
      if (step.leftKey != null && String(step.leftKey)) out.leftKey = String(step.leftKey);
      if (step.right !== void 0) out.right = step.right;
      if (thenSkip > 0) out.thenSkip = thenSkip;
      return out;
    }
    if (kind === "function" || step.functionId) {
      const functionId = String(step.functionId || "");
      const def = getFunctionDef(functionId);
      if (!def) return null;
      const out = {
        kind: "function",
        functionId: def.id,
        parameters: normalizeFunctionParameters(def.id, step?.parameters)
      };
      const delay = Number(step?.delayMsBefore);
      if (Number.isFinite(delay) && delay > 0) out.delayMsBefore = delay;
      return out;
    }
    return null;
  }
  function normalizeStoredMacros(rawMacros) {
    const src = rawMacros && typeof rawMacros === "object" ? rawMacros : {};
    const out = {};
    for (const [id, m] of Object.entries(src)) {
      if (!m || typeof m !== "object") continue;
      const rawSteps = Array.isArray(m.steps) ? m.steps : Array.isArray(m.actions) ? m.actions : [];
      const macro = {
        id: String(m.id || id),
        label: String(m.label || "Macro"),
        icon: m.icon,
        steps: rawSteps.map(normalizeMacroStep).filter(Boolean),
        createdAt: Number.isFinite(m.createdAt) ? m.createdAt : nowMs(),
        updatedAt: Number.isFinite(m.updatedAt) ? m.updatedAt : nowMs()
      };
      if (m.baseStockMacroId) macro.baseStockMacroId = String(m.baseStockMacroId);
      out[id] = macro;
    }
    return out;
  }
  async function getKeyboardLayoutStore() {
    try {
      const result = await chrome.storage.sync.get(KEYBOARD_LAYOUT_STORE_KEY);
      const stored = result && result[KEYBOARD_LAYOUT_STORE_KEY] ? result[KEYBOARD_LAYOUT_STORE_KEY] : null;
      if (stored && typeof stored === "object" && stored.version === 1) {
        return {
          version: 1,
          layouts: stored.layouts && typeof stored.layouts === "object" ? stored.layouts : {},
          macros: normalizeStoredMacros(stored.macros),
          actions: stored.actions && typeof stored.actions === "object" ? stored.actions : {}
        };
      }
    } catch {
    }
    return getEmptyKeyboardLayoutStore();
  }
  async function setKeyboardLayoutStore(next) {
    try {
      await chrome.storage.sync.set({ [KEYBOARD_LAYOUT_STORE_KEY]: next });
    } catch {
    }
  }
  async function listUserKeyboardLayouts() {
    const st = await getKeyboardLayoutStore();
    return Object.values(st.layouts || {});
  }
  async function getUserKeyboardLayoutById(id) {
    const st = await getKeyboardLayoutStore();
    const key = String(id || "");
    const v = st.layouts && typeof st.layouts === "object" ? st.layouts[key] : null;
    return v && typeof v === "object" ? v : null;
  }
  async function listUserMacros() {
    const st = await getKeyboardLayoutStore();
    return Object.values(st.macros || {});
  }
  async function createEmptyUserKeyboardLayout({ baseBuiltinLayoutId, label, includeNumberRow } = {}) {
    const baseId = String(baseBuiltinLayoutId || "");
    const uiLayout = getKeyboardUiLayoutForLayout(baseId, { includeNumberRow: !!includeNumberRow });
    const slots = {};
    for (const row of uiLayout || []) {
      for (const item of row || []) {
        if (!item) continue;
        if (item.type === "special") continue;
        if (item.type === "key") {
          const s = normalizeSlotLabel(item.text);
          if (s) slots[s] = slots[s] ?? null;
        }
      }
    }
    const t = nowMs();
    const layout = {
      id: genId("layout:"),
      label: String(label || "New Layout"),
      builtIn: false,
      baseBuiltinLayoutId: baseId,
      slots,
      createdAt: t,
      updatedAt: t
    };
    return await upsertUserKeyboardLayout(layout);
  }
  async function deleteUserKeyboardLayout(id) {
    const st = await getKeyboardLayoutStore();
    const key = String(id || "");
    if (!key) return;
    try {
      delete st.layouts[key];
    } catch {
    }
    await setKeyboardLayoutStore(st);
    try {
      const { getSettings: getSettings2, setSettings: setSettings2 } = await Promise.resolve().then(() => (init_settings_manager(), settings_manager_exports));
      const settings = await getSettings2();
      const cur = String(settings?.currentKeyboardLayoutId || "");
      if (cur === `user:${key}`) {
        await setSettings2({ currentKeyboardLayoutId: "builtin" });
      }
    } catch {
    }
  }
  async function importUserKeyboardLayout(raw) {
    const payload = raw && raw.type === "kp-layout" && raw.layout ? raw.layout : raw;
    if (!payload || typeof payload !== "object") return null;
    const slots = payload.slots && typeof payload.slots === "object" ? payload.slots : null;
    if (!slots) return null;
    const t = nowMs();
    const layout = {
      id: genId("layout:"),
      label: String(payload.label || "Imported Layout"),
      builtIn: false,
      baseBuiltinLayoutId: payload.baseBuiltinLayoutId ? String(payload.baseBuiltinLayoutId) : void 0,
      slots,
      createdAt: t,
      updatedAt: t
    };
    return await upsertUserKeyboardLayout(layout);
  }
  function exportUserKeyboardLayout(layout) {
    return {
      type: "kp-layout",
      version: 1,
      layout
    };
  }
  async function createUserMacro({ label } = {}) {
    const st = await getKeyboardLayoutStore();
    const id = genId("macro:");
    const t = nowMs();
    const macro = {
      id,
      label: String(label || "New Macro"),
      icon: "placeholder",
      steps: [],
      createdAt: t,
      updatedAt: t
    };
    st.macros[id] = macro;
    await setKeyboardLayoutStore(st);
    return macro;
  }
  async function getUserMacroById(id) {
    const st = await getKeyboardLayoutStore();
    const key = String(id || "");
    const m = st.macros && st.macros[key] ? st.macros[key] : null;
    return m || null;
  }
  async function upsertUserMacro(macro) {
    if (!macro?.id) return null;
    const st = await getKeyboardLayoutStore();
    const t = nowMs();
    const prev = st.macros && st.macros[macro.id] ? st.macros[macro.id] : null;
    const m = {
      id: String(macro.id),
      label: String(macro.label || prev?.label || "Macro"),
      icon: macro.icon ?? prev?.icon,
      steps: (Array.isArray(macro.steps) ? macro.steps : prev?.steps || []).map(normalizeMacroStep).filter(Boolean),
      createdAt: Number.isFinite(prev?.createdAt) ? prev.createdAt : Number.isFinite(macro.createdAt) ? macro.createdAt : t,
      updatedAt: t
    };
    const baseStock = macro.baseStockMacroId ?? prev?.baseStockMacroId;
    if (baseStock) m.baseStockMacroId = String(baseStock);
    if (!st.macros || typeof st.macros !== "object") st.macros = {};
    st.macros[m.id] = m;
    await setKeyboardLayoutStore(st);
    return m;
  }
  async function forkStockMacroToUser(stockMacroId, { label } = {}) {
    const stockId = String(stockMacroId || "");
    if (!isStockMacroId(stockId)) return null;
    const stock = getStockMacroById(stockId);
    if (!stock) return null;
    const st = await getKeyboardLayoutStore();
    const id = genId("macro:");
    const t = nowMs();
    const macro = {
      id,
      label: String(label || `${stock.label} (user)`),
      icon: stock.icon || "placeholder",
      baseStockMacroId: stock.id,
      steps: (stock.steps || []).map((s) => normalizeMacroStep({ ...s })).filter(Boolean),
      createdAt: t,
      updatedAt: t
    };
    if (!st.macros || typeof st.macros !== "object") st.macros = {};
    st.macros[id] = macro;
    await setKeyboardLayoutStore(st);
    return macro;
  }
  async function deleteUserMacro(id) {
    const st = await getKeyboardLayoutStore();
    const key = String(id || "");
    if (!key || !st.macros) return;
    try {
      delete st.macros[key];
    } catch {
    }
    await setKeyboardLayoutStore(st);
  }
  async function listUserActions() {
    const st = await getKeyboardLayoutStore();
    return Object.values(st.actions || {}).filter(Boolean);
  }
  async function getUserActionById(id) {
    const st = await getKeyboardLayoutStore();
    const key = String(id || "");
    const a = st.actions && st.actions[key] ? st.actions[key] : null;
    return a || null;
  }
  async function createUserAction({ functionId, label, parameters } = {}) {
    const def = getFunctionDef(functionId);
    if (!def) return null;
    const st = await getKeyboardLayoutStore();
    const id = genId("action:");
    const t = nowMs();
    const action = {
      id,
      functionId: def.id,
      label: String(label || def.label),
      parameters: normalizeFunctionParameters(def.id, parameters || defaultFunctionParameters(def.id)),
      createdAt: t,
      updatedAt: t
    };
    if (!st.actions || typeof st.actions !== "object") st.actions = {};
    st.actions[id] = action;
    await setKeyboardLayoutStore(st);
    return action;
  }
  async function upsertUserAction(action) {
    const def = getFunctionDef(action?.functionId);
    if (!def || !action?.id) return null;
    const st = await getKeyboardLayoutStore();
    const t = nowMs();
    const prev = st.actions && st.actions[action.id] ? st.actions[action.id] : null;
    const a = {
      id: String(action.id),
      functionId: def.id,
      label: String(action.label || prev?.label || def.label),
      parameters: normalizeFunctionParameters(def.id, action.parameters),
      createdAt: Number.isFinite(prev?.createdAt) ? prev.createdAt : Number.isFinite(action.createdAt) ? action.createdAt : t,
      updatedAt: t
    };
    if (!st.actions || typeof st.actions !== "object") st.actions = {};
    st.actions[a.id] = a;
    await setKeyboardLayoutStore(st);
    return a;
  }
  async function deleteUserAction(id) {
    const st = await getKeyboardLayoutStore();
    const key = String(id || "");
    if (!key || !st.actions) return;
    try {
      delete st.actions[key];
    } catch {
    }
    await setKeyboardLayoutStore(st);
  }
  var BUILTIN_FUNCTION_ACTION_ID_PREFIX = "action:builtin:";
  function builtinFunctionUserActionId(functionId) {
    return `${BUILTIN_FUNCTION_ACTION_ID_PREFIX}${String(functionId || "")}`;
  }
  async function getOrCreateBuiltinFunctionUserAction(functionId) {
    const def = getFunctionDef(functionId);
    if (!def) return null;
    const id = builtinFunctionUserActionId(def.id);
    const st = await getKeyboardLayoutStore();
    const existing = st.actions && st.actions[id] ? st.actions[id] : null;
    if (existing) return existing;
    const t = nowMs();
    const action = {
      id,
      functionId: def.id,
      label: def.label,
      parameters: defaultFunctionParameters(def.id),
      createdAt: t,
      updatedAt: t
    };
    if (!st.actions || typeof st.actions !== "object") st.actions = {};
    st.actions[id] = action;
    await setKeyboardLayoutStore(st);
    return action;
  }
  async function setBuiltinFunctionUserActionParameter(functionId, paramId, value) {
    const current = await getOrCreateBuiltinFunctionUserAction(functionId);
    if (!current) return null;
    return await upsertUserAction({
      ...current,
      parameters: { ...current.parameters, [paramId]: value }
    });
  }
  async function upsertUserKeyboardLayout(layout) {
    const st = await getKeyboardLayoutStore();
    const t = nowMs();
    const l = {
      ...layout,
      id: String(layout?.id || genId("layout:")),
      label: String(layout?.label || "Custom Layout"),
      builtIn: false,
      slots: layout && typeof layout.slots === "object" ? layout.slots : {},
      createdAt: Number.isFinite(layout?.createdAt) ? layout.createdAt : t,
      updatedAt: t
    };
    st.layouts[l.id] = l;
    await setKeyboardLayoutStore(st);
    return l;
  }
  async function setUserKeyboardLayoutHandedness(layout, handedness) {
    if (!layout?.id) return null;
    const baseId = String(layout.baseBuiltinLayoutId || "");
    const inferred = inferFamilyAndHandednessFromLayoutId(baseId);
    const targetHand = normalizeKeyboardHandedness(handedness);
    if (inferred.handedness === targetHand) return layout;
    const slots = {};
    for (const [slotKey, assignment] of Object.entries(layout.slots || {})) {
      const mirroredKey = mirrorKeyboardLayoutSlotKey(slotKey);
      slots[mirroredKey] = assignment && typeof assignment === "object" ? { type: String(assignment.type), id: String(assignment.id) } : null;
    }
    const nextBaseBuiltinLayoutId = resolveKeyboardLayoutId({
      familyId: inferred.familyId,
      handedness: targetHand
    });
    return await upsertUserKeyboardLayout({
      ...layout,
      baseBuiltinLayoutId: nextBaseBuiltinLayoutId,
      slots
    });
  }
  async function setUserKeyboardLayoutSlot(layoutId, slotKey, item) {
    const key = String(slotKey || "");
    if (!key) return { ok: false, reason: "Missing slot key." };
    if (item && item.type === "function") {
      let functionId = String(item.id || "");
      if (functionId.startsWith("action:")) {
        const instance = await getUserActionById(functionId);
        if (!instance) return { ok: false, reason: "Action instance not found." };
        functionId = instance.functionId;
      }
      const check = validateFunctionSlotKey(functionId, key);
      if (!check.ok) return { ok: false, reason: check.reason };
    }
    const layout = await getUserKeyboardLayoutById(layoutId);
    if (!layout) return { ok: false, reason: "Layout not found." };
    const slots = { ...layout.slots && typeof layout.slots === "object" ? layout.slots : {} };
    if (item) {
      slots[key] = { type: item.type, id: String(item.id) };
    } else {
      delete slots[key];
    }
    const next = await upsertUserKeyboardLayout({ ...layout, slots });
    return { ok: true, layout: next };
  }
  async function duplicateBuiltinLayoutToUserLayout({ builtinLayoutId, label } = {}) {
    const baseId = String(builtinLayoutId || "");
    const { handedness } = inferFamilyAndHandednessFromLayoutId(baseId);
    const kb = buildEffectiveKeybindings(baseId, handedness);
    const uiLayout = getKeyboardUiLayoutForLayout(baseId, { includeNumberRow: true });
    const slots = {};
    for (const row of uiLayout || []) {
      for (const item of row || []) {
        if (!item) continue;
        if (item.type === "special") continue;
        if (item.type === "key") {
          const s = normalizeSlotLabel(item.text);
          if (s) slots[s] = slots[s] ?? null;
          continue;
        }
        if (item.type === "action") {
          const binding = kb && kb[item.id];
          const slot = slotLabelFromBinding(binding);
          if (!slot) continue;
          const functionId = String(item.id);
          if (functionWorksWhileTyping(functionId)) continue;
          slots[slot] = { type: "function", id: functionId };
        }
      }
    }
    const t = nowMs();
    const layout = {
      id: genId("layout:"),
      label: String(label || "Custom Layout"),
      builtIn: false,
      baseBuiltinLayoutId: baseId,
      slots,
      createdAt: t,
      updatedAt: t
    };
    return await upsertUserKeyboardLayout(layout);
  }
  async function duplicateUserKeyboardLayout({ source, label } = {}) {
    if (!source || typeof source !== "object") {
      throw new Error("duplicateUserKeyboardLayout requires a source layout");
    }
    const slots = {};
    try {
      for (const [key, val] of Object.entries(source.slots || {})) {
        if (!key) continue;
        if (val && typeof val === "object" && val.type && val.id) {
          slots[key] = { type: String(val.type), id: String(val.id) };
        } else {
          slots[key] = null;
        }
      }
    } catch {
    }
    const t = nowMs();
    const layout = {
      id: genId("layout:"),
      label: String(label || `${String(source.label || "Layout").trim() || "Layout"} copy`),
      builtIn: false,
      baseBuiltinLayoutId: source.baseBuiltinLayoutId ? String(source.baseBuiltinLayoutId) : void 0,
      slots,
      createdAt: t,
      updatedAt: t
    };
    return await upsertUserKeyboardLayout(layout);
  }

  // src/modules/settings-path.js
  function clampNumber(n, min, max) {
    const v = typeof n === "number" ? n : Number(n);
    if (!Number.isFinite(v)) return min;
    return Math.min(max, Math.max(min, v));
  }

  // src/modules/action-config-schema.js
  var ACTION_RADIO_PARAMETER_IDS = Object.freeze(["mode", "action", "format", "destination"]);
  function controlTypeForParameter(param, opts = {}) {
    if (!param) return "text";
    if (param.type === "boolean") return "toggle";
    if (param.type === "number") return "range";
    if (param.type === "enum") {
      const radioIds = opts.radioParamIds || [];
      return radioIds.includes(param.id) ? "radio" : "enum";
    }
    if (param.multiline) return "textarea";
    return "text";
  }
  function parameterToControlSpec(param, opts = {}) {
    if (!param || !param.id) return null;
    const type2 = controlTypeForParameter(param, opts);
    const spec = {
      type: type2,
      path: param.id,
      label: param.label || param.id,
      group: String(param.group || ""),
      defaultValue: param.defaultValue
    };
    if (type2 === "enum" || type2 === "radio" || type2 === "select") {
      spec.widget = type2 === "radio" ? "radio" : "select";
      spec.options = Array.isArray(param.options) ? param.options : [];
    }
    if (type2 === "range") {
      if (param.min != null) spec.min = param.min;
      if (param.max != null) spec.max = param.max;
      if (param.step != null) spec.step = param.step;
    }
    if (param.placeholder) spec.placeholder = String(param.placeholder);
    if (param.multiline) spec.multiline = true;
    if (param.rows != null) spec.rows = param.rows;
    return spec;
  }
  function buildActionControlSchema(parameters, opts = {}) {
    const shouldShow = opts.shouldShow || (() => true);
    const filterOptions = opts.filterOptions;
    const specs = [];
    for (const param of parameters || []) {
      if (!param || !shouldShow(param)) continue;
      const spec = parameterToControlSpec(param, opts);
      if (!spec) continue;
      if ((spec.type === "enum" || spec.type === "radio" || spec.type === "select") && filterOptions) {
        spec.options = filterOptions(param);
      }
      specs.push(spec);
    }
    return specs;
  }
  function groupActionControlSpecs(specs) {
    const groups = [];
    for (const spec of specs || []) {
      if (!spec) continue;
      const group = String(spec.group || "");
      const last = groups[groups.length - 1];
      if (last && last.group === group) last.specs.push(spec);
      else groups.push({ group, specs: [spec] });
    }
    return groups;
  }
  function normalizeControlValue(spec, raw) {
    if (!spec) return raw;
    switch (spec.type) {
      case "toggle":
        return !!raw;
      case "range": {
        const n = typeof raw === "number" ? raw : Number(raw);
        if (!Number.isFinite(n)) return spec.defaultValue;
        if (spec.min != null && spec.max != null) return clampNumber(n, spec.min, spec.max);
        return n;
      }
      case "enum":
      case "select":
      case "radio": {
        const options = spec.options || [];
        return options.some((o) => o && o.id === raw) ? raw : spec.defaultValue;
      }
      default:
        return raw !== void 0 && raw !== null ? String(raw) : spec.defaultValue ?? "";
    }
  }
  function normalizeActionParametersFromSchema(specs, raw) {
    const src = raw && typeof raw === "object" ? raw : {};
    const out = {};
    for (const spec of specs || []) {
      const has = Object.prototype.hasOwnProperty.call(src, spec.path);
      out[spec.path] = normalizeControlValue(spec, has ? src[spec.path] : spec.defaultValue);
    }
    return out;
  }

  // src/modules/action-config-controller.js
  var ActionConfigController = class {
    constructor() {
      this.state = { functionId: "", parameters: {} };
      this._listeners = /* @__PURE__ */ new Set();
      this._disposed = false;
      this._specs = [];
      this._persist = null;
    }
    get disposed() {
      return this._disposed;
    }
    /**
     * @param {(state: ActionConfigState) => void} fn
     * @returns {() => void}
     */
    subscribe(fn) {
      if (typeof fn !== "function" || this._disposed) return () => {
      };
      this._listeners.add(fn);
      return () => {
        this._listeners.delete(fn);
      };
    }
    _emit() {
      if (this._disposed) return;
      for (const fn of this._listeners) {
        try {
          fn(this.state);
        } catch {
        }
      }
    }
    /**
     * @returns {import('./action-config-schema.js').ActionControlSpec[]}
     */
    schema() {
      return this._specs;
    }
    /**
     * @param {{
     *   functionId?: string,
     *   snapshot?: Record<string, any>|null,
     *   parameters?: Array<import('../config/function-library.js').FunctionParameterDef>|null,
     *   radioParamIds?: readonly string[],
     *   persist?: (functionId: string, paramId: string, value: any) => any
     * }} [opts]
     */
    load(opts = {}) {
      if (this._disposed) return this.state;
      const functionId = String(opts.functionId || "");
      const def = functionId ? getFunctionDef(functionId) : null;
      const parameterDefs = opts.parameters || def?.parameters || [];
      this._specs = buildActionControlSchema(parameterDefs, {
        radioParamIds: opts.radioParamIds,
        shouldShow: (param) => shouldShowFunctionParameter(functionId, param),
        filterOptions: (param) => filterFunctionParameterOptions(functionId, param)
      });
      this._persist = typeof opts.persist === "function" ? opts.persist : null;
      const snapshot = opts.snapshot && typeof opts.snapshot === "object" ? opts.snapshot : {};
      let parameters;
      if (def) {
        parameters = normalizeFunctionParameters(functionId, {
          ...defaultFunctionParameters(functionId),
          ...snapshot
        });
      } else {
        parameters = normalizeActionParametersFromSchema(this._specs, snapshot);
      }
      this.state = { functionId, parameters };
      this._emit();
      return this.state;
    }
    /**
     * @param {string} path parameter id
     * @param {any} value
     */
    async update(path, value) {
      if (this._disposed) return this.state;
      const spec = this._specs.find((s) => s.path === path);
      const fieldValue = spec ? normalizeControlValue(spec, value) : value;
      const functionId = this.state.functionId;
      const merged = { ...this.state.parameters, [path]: fieldValue };
      const def = functionId ? getFunctionDef(functionId) : null;
      const parameters = def ? normalizeFunctionParameters(functionId, merged) : normalizeActionParametersFromSchema(this._specs, merged);
      this.state = { functionId, parameters };
      if (this._persist) {
        try {
          await this._persist(functionId, path, parameters[path]);
        } catch {
        }
      }
      this._emit();
      return this.state;
    }
    dispose() {
      this._disposed = true;
      this._listeners.clear();
      this._persist = null;
      this._specs = [];
    }
  };
  function createActionConfigController() {
    return new ActionConfigController();
  }

  // src/ui/select-menu.js
  init_constants();
  var STYLE_ATTR3 = "data-kp-select-menu-style";
  var SELECT_LIST_HOST_CSS = `
:host {
  all: initial;
  display: block !important;
  position: fixed !important;
  z-index: ${Z_INDEX.SELECT_MENU} !important;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  overflow: visible !important;
  clip-path: none !important;
  pointer-events: none;
}
:host([hidden]) {
  display: none !important;
}
.kp-select-menu {
  pointer-events: auto;
}
`.trim();
  var _idSeq = 0;
  function readComposedSurface(el2) {
    let node = el2;
    try {
      while (node) {
        const surface = node.getAttribute?.("data-kp-surface");
        if (surface) return String(surface);
        const next = node.parentElement;
        if (next) {
          node = next;
          continue;
        }
        const root = node.getRootNode?.();
        node = root && root !== node && root.host ? root.host : null;
      }
    } catch {
    }
    return "";
  }
  function getBodyMount(doc) {
    try {
      const d = doc?.defaultView?.document || doc || document;
      return d.body || d.documentElement || null;
    } catch {
      return document.body || document.documentElement || null;
    }
  }
  function ensureSelectMenuStyles(root) {
    if (!root) return;
    injectChromeStyles(root, {
      attr: STYLE_ATTR3,
      css: `${getTitlebarChromeCss()}
${getSelectMenuCss()}`
    });
  }
  function createSelectIcon(doc, iconId, className) {
    const id = typeof iconId === "string" && iconId.trim() ? iconId.trim() : "";
    if (!id) return null;
    const el2 = doc.createElement("span");
    el2.className = className;
    el2.setAttribute("aria-hidden", "true");
    el2.setAttribute("data-kp-theme-icon", id);
    try {
      const url = getThemeIconUrl(id, getActiveTheme());
      if (url) {
        const img = `url("${String(url).replace(/"/g, '\\"')}")`;
        el2.style.webkitMaskImage = img;
        el2.style.maskImage = img;
      }
    } catch {
    }
    return el2;
  }
  function themeListHost(list) {
    try {
      const theme = getActiveTheme();
      applyThemeDataset(list, theme);
      applyThemeCssVars(list, theme);
    } catch {
    }
  }
  function createSelectMenu(config = {}) {
    const doc = config.doc || document;
    const variant = config.variant === "field" ? "field" : "titlebar";
    const listId = `kp-select-list-${++_idSeq}`;
    let options = Array.isArray(config.options) ? config.options.slice() : [];
    let currentValue = config.value != null ? String(config.value) : null;
    let disabled = false;
    let fallbackOpen = false;
    let listHost = null;
    const root = doc.createElement("div");
    root.className = `kp-select kp-select--${variant}${config.className ? ` ${config.className}` : ""}`;
    root.setAttribute("data-kp-select", "true");
    const trigger = doc.createElement("button");
    trigger.type = "button";
    trigger.className = "kp-select-trigger";
    trigger.setAttribute("role", "combobox");
    trigger.setAttribute("aria-haspopup", "listbox");
    trigger.setAttribute("aria-expanded", "false");
    trigger.setAttribute("aria-controls", listId);
    if (config.ariaLabel) trigger.setAttribute("aria-label", config.ariaLabel);
    const triggerIcon = doc.createElement("span");
    triggerIcon.className = "kp-select-trigger-icon";
    triggerIcon.setAttribute("aria-hidden", "true");
    triggerIcon.hidden = true;
    const triggerLabel = doc.createElement("span");
    triggerLabel.className = "kp-select-trigger-label";
    const chevron = doc.createElement("span");
    chevron.className = "kp-select-chevron";
    chevron.setAttribute("aria-hidden", "true");
    trigger.appendChild(triggerIcon);
    trigger.appendChild(triggerLabel);
    trigger.appendChild(chevron);
    root.appendChild(trigger);
    const list = doc.createElement("div");
    list.id = listId;
    list.className = "kp-select-menu";
    list.setAttribute("role", "listbox");
    if (config.ariaLabel) list.setAttribute("aria-label", config.ariaLabel);
    try {
      list.style.setProperty("position", "fixed", "important");
    } catch {
    }
    try {
      list.style.setProperty("z-index", String(Z_INDEX.SELECT_MENU), "important");
    } catch {
      list.style.zIndex = String(Z_INDEX.SELECT_MENU);
    }
    list.setAttribute("data-kp-select-fallback", "true");
    list.hidden = true;
    try {
      list.style.setProperty("display", "none", "important");
    } catch {
    }
    const stopDrag = (e) => {
      try {
        e.stopPropagation();
      } catch {
      }
    };
    trigger.addEventListener("pointerdown", stopDrag, true);
    trigger.addEventListener("mousedown", stopDrag, true);
    list.addEventListener("pointerdown", stopDrag, true);
    list.addEventListener("mousedown", stopDrag, true);
    const choiceOptions = () => options.filter((o) => o && o.type !== "group" && o.type !== "separator" && o.value != null);
    const findChoice = (value) => choiceOptions().find((o) => String(o.value) === String(value)) || null;
    const syncTrigger = () => {
      const choice = findChoice(currentValue);
      const label = choice?.label != null ? String(choice.label) : currentValue ? String(currentValue) : "";
      triggerLabel.textContent = label;
      const iconId = choice?.icon;
      let showIcon = false;
      if (iconId) {
        try {
          triggerIcon.setAttribute("data-kp-theme-icon", iconId);
          const url = getThemeIconUrl(iconId, getActiveTheme());
          if (url) {
            const img = `url("${String(url).replace(/"/g, '\\"')}")`;
            triggerIcon.style.webkitMaskImage = img;
            triggerIcon.style.maskImage = img;
            showIcon = true;
          }
        } catch {
        }
      }
      if (!showIcon) {
        triggerIcon.removeAttribute("data-kp-theme-icon");
        triggerIcon.style.webkitMaskImage = "";
        triggerIcon.style.maskImage = "";
      }
      triggerIcon.hidden = !showIcon;
    };
    const isListOpen = () => fallbackOpen;
    const setExpanded = (open) => {
      try {
        trigger.setAttribute("aria-expanded", open ? "true" : "false");
      } catch {
      }
      try {
        root.classList.toggle("is-open", !!open);
      } catch {
      }
    };
    const positionList = () => {
      let tr;
      try {
        tr = trigger.getBoundingClientRect();
      } catch {
        return;
      }
      if (!tr) return;
      const minW = Math.max(tr.width, variant === "titlebar" ? 190 : 160);
      list.style.minWidth = `${Math.round(minW)}px`;
      list.style.left = `${Math.round(tr.left)}px`;
      list.style.top = `${Math.round(tr.bottom + 4)}px`;
      let lr;
      try {
        lr = list.getBoundingClientRect();
      } catch {
        return;
      }
      const spaceBelow = window.innerHeight - tr.bottom;
      const openUp = lr.height + 8 > spaceBelow && tr.top > spaceBelow;
      if (openUp) {
        list.style.top = `${Math.max(8, Math.round(tr.top - lr.height - 4))}px`;
      }
      try {
        lr = list.getBoundingClientRect();
      } catch {
        return;
      }
      if (lr.right > window.innerWidth - 8) {
        list.style.left = `${Math.max(8, Math.round(window.innerWidth - lr.width - 8))}px`;
      }
      if (lr.left < 8) list.style.left = "8px";
    };
    const setHostOpen = (open) => {
      if (!listHost) return;
      listHost.hidden = !open;
      try {
        if (open) listHost.style.removeProperty("display");
        else listHost.style.setProperty("display", "none", "important");
      } catch {
      }
    };
    const closeFallback = () => {
      fallbackOpen = false;
      list.hidden = true;
      try {
        list.style.setProperty("display", "none", "important");
      } catch {
      }
      setHostOpen(false);
      setExpanded(false);
      try {
        doc.removeEventListener("pointerdown", onDocPointerDown, true);
      } catch {
      }
    };
    const onDocPointerDown = (e) => {
      const path = typeof e.composedPath === "function" ? e.composedPath() : [];
      if (path.includes(list) || path.includes(trigger) || path.includes(root)) return;
      closeFallback();
    };
    const syncListSurface = () => {
      const surface = readComposedSurface(trigger) || readComposedSurface(root);
      try {
        if (surface) {
          list.setAttribute("data-kp-surface", surface);
          listHost?.setAttribute("data-kp-surface", surface);
        } else {
          list.removeAttribute("data-kp-surface");
          listHost?.removeAttribute("data-kp-surface");
        }
      } catch {
      }
    };
    const openList = () => {
      if (disabled) return;
      mountList();
      syncListSurface();
      if (listHost) themeListHost(listHost);
      themeListHost(list);
      fallbackOpen = true;
      setHostOpen(true);
      list.hidden = false;
      try {
        list.style.setProperty("display", "block", "important");
      } catch {
      }
      setExpanded(true);
      positionList();
      try {
        doc.addEventListener("pointerdown", onDocPointerDown, true);
      } catch {
      }
    };
    const closeList = () => {
      closeFallback();
    };
    const toggleList = () => {
      if (isListOpen()) closeList();
      else openList();
    };
    const pickValue = (value) => {
      const next = String(value);
      const prev = currentValue;
      currentValue = next;
      paintSelection();
      syncTrigger();
      closeList();
      if (typeof config.onChange === "function" && next !== prev) {
        try {
          config.onChange(next, prev);
        } catch {
        }
      }
    };
    const paintSelection = () => {
      const items = list.querySelectorAll(".kp-select-item");
      for (const btn of items) {
        const selected = btn.getAttribute("data-kp-select-value") === String(currentValue);
        try {
          btn.setAttribute("aria-selected", selected ? "true" : "false");
        } catch {
        }
        try {
          btn.classList.toggle("is-active", false);
        } catch {
        }
      }
    };
    const rebuildList = () => {
      while (list.firstChild) list.removeChild(list.firstChild);
      for (const opt of options) {
        if (!opt) continue;
        if (opt.type === "separator") {
          const hr = doc.createElement("div");
          hr.className = "kp-select-separator";
          hr.setAttribute("role", "separator");
          list.appendChild(hr);
          continue;
        }
        if (opt.type === "group") {
          const g = doc.createElement("div");
          g.className = "kp-select-group";
          g.textContent = String(opt.label || "");
          list.appendChild(g);
          continue;
        }
        if (opt.value == null) continue;
        const value = String(opt.value);
        const btn = doc.createElement("button");
        btn.type = "button";
        btn.className = "kp-select-item";
        btn.setAttribute("role", "option");
        btn.setAttribute("data-kp-select-value", value);
        btn.setAttribute("aria-selected", value === String(currentValue) ? "true" : "false");
        if (opt.disabled) btn.disabled = true;
        const icon = createSelectIcon(doc, opt.icon, "kp-select-item-icon");
        if (icon) btn.appendChild(icon);
        const lab = doc.createElement("span");
        lab.className = "kp-select-item-label";
        lab.textContent = String(opt.label || value);
        btn.appendChild(lab);
        if (opt.shortcut) {
          const kbd = createTitlebarKbd(doc, opt.shortcut);
          btn.appendChild(kbd);
        }
        btn.addEventListener("click", (e) => {
          try {
            e.preventDefault();
            e.stopPropagation();
          } catch {
          }
          if (btn.disabled) return;
          pickValue(value);
        });
        btn.addEventListener("pointerdown", stopDrag, true);
        list.appendChild(btn);
      }
      paintSelection();
      syncTrigger();
    };
    trigger.addEventListener("click", (e) => {
      try {
        e.stopPropagation();
      } catch {
      }
      try {
        e.preventDefault();
      } catch {
      }
      if (disabled) return;
      toggleList();
    });
    trigger.addEventListener("keydown", (e) => {
      const key = e?.key;
      if (key === "ArrowDown" || key === "Enter" || key === " ") {
        try {
          e.preventDefault();
          e.stopPropagation();
        } catch {
        }
        if (!isListOpen()) openList();
      } else if (key === "Escape" && isListOpen()) {
        try {
          e.preventDefault();
          e.stopPropagation();
        } catch {
        }
        closeList();
      }
    });
    list.addEventListener("keydown", (e) => {
      if (e?.key === "Escape") {
        try {
          e.preventDefault();
        } catch {
        }
        closeList();
        try {
          trigger.focus();
        } catch {
        }
      }
    });
    const ensureListHost = () => {
      if (listHost) return listHost;
      listHost = doc.createElement("div");
      listHost.className = "kp-select-menu-host";
      listHost.setAttribute("data-kp-select", "true");
      listHost.hidden = true;
      try {
        listHost.style.setProperty("display", "none", "important");
      } catch {
      }
      const shadow = ensureOpenChromeShadow(listHost, { id: "select-menu" });
      try {
        listHost.removeAttribute("data-kp-corner");
      } catch {
      }
      try {
        listHost.classList.remove("kp-chrome-window");
      } catch {
      }
      if (shadow) {
        injectChromeStyles(shadow, {
          attr: STYLE_ATTR3,
          css: `${SELECT_LIST_HOST_CSS}
${getTitlebarChromeCss()}
${getSelectMenuCss()}`
        });
        try {
          shadow.appendChild(list);
        } catch {
        }
      } else {
        try {
          listHost.appendChild(list);
        } catch {
        }
      }
      themeListHost(listHost);
      themeListHost(list);
      syncListSurface();
      return listHost;
    };
    const mountList = () => {
      const host = ensureListHost();
      const parent = getBodyMount(doc);
      if (parent && host.parentNode !== parent) {
        try {
          parent.appendChild(host);
        } catch {
        }
      }
    };
    const triggerRoot = () => {
      try {
        return trigger.getRootNode?.() || null;
      } catch {
        return null;
      }
    };
    rebuildList();
    mountList();
    try {
      const rn = triggerRoot();
      if (rn && rn !== doc) ensureSelectMenuStyles(rn);
    } catch {
    }
    return {
      root,
      list,
      trigger,
      getValue() {
        return currentValue;
      },
      setValue(value, opts = {}) {
        currentValue = value != null ? String(value) : null;
        paintSelection();
        syncTrigger();
        if (!opts.silent && typeof config.onChange === "function") {
          try {
            config.onChange(currentValue, null);
          } catch {
          }
        }
      },
      setOptions(next) {
        options = Array.isArray(next) ? next.slice() : [];
        rebuildList();
      },
      setDisabled(next) {
        disabled = !!next;
        trigger.disabled = disabled;
        if (disabled) closeList();
      },
      open: openList,
      close: closeList,
      destroy() {
        closeList();
        try {
          doc.removeEventListener("pointerdown", onDocPointerDown, true);
        } catch {
        }
        try {
          listHost?.remove();
        } catch {
          try {
            listHost?.parentNode?.removeChild(listHost);
          } catch {
          }
        }
        listHost = null;
        try {
          list.remove();
        } catch {
          try {
            list.parentNode?.removeChild(list);
          } catch {
          }
        }
        try {
          root.remove();
        } catch {
        }
      }
    };
  }
  function enhanceNativeSelect(select, opts = {}) {
    if (!select || select.dataset?.kpSelectEnhanced === "true") return null;
    const doc = select.ownerDocument || document;
    const options = Array.from(select.options || []).map((option) => ({
      value: String(option.value),
      label: String(option.textContent || option.label || option.value),
      disabled: !!option.disabled
    }));
    const menu = createSelectMenu({
      doc,
      ariaLabel: select.getAttribute("aria-label") || void 0,
      value: select.value,
      variant: opts.variant === "titlebar" ? "titlebar" : "field",
      className: opts.className || select.className || "",
      options,
      onChange: (value) => {
        select.value = value;
        try {
          select.dispatchEvent(new Event("change", { bubbles: true }));
        } catch {
          try {
            select.dispatchEvent(new doc.defaultView.Event("change", { bubbles: true }));
          } catch {
          }
        }
      }
    });
    select.dataset.kpSelectEnhanced = "true";
    select.setAttribute("aria-hidden", "true");
    try {
      select.style.setProperty("display", "none", "important");
    } catch {
    }
    try {
      select.parentNode?.insertBefore(menu.root, select.nextSibling);
    } catch {
    }
    menu.setDisabled(!!select.disabled);
    let observer = null;
    const api = {
      ...menu,
      syncFromNative() {
        menu.setOptions(Array.from(select.options || []).map((option) => ({
          value: String(option.value),
          label: String(option.textContent || option.label || option.value),
          disabled: !!option.disabled
        })));
        menu.setValue(select.value, { silent: true });
        menu.setDisabled(!!select.disabled);
      },
      destroy() {
        try {
          observer?.disconnect?.();
        } catch {
        }
        observer = null;
        menu.destroy();
      }
    };
    try {
      const target = doc.documentElement || doc.body;
      if (target && typeof MutationObserver !== "undefined") {
        observer = new MutationObserver(() => {
          if (!select.isConnected) api.destroy();
        });
        observer.observe(target, { childList: true, subtree: true });
      }
    } catch {
    }
    select._kpSelectMenu = api;
    return api;
  }

  // src/modules/action-config-binder.js
  function appendActionConfigFields(host, ctx) {
    if (!host || !ctx?.controller) return;
    const { controller } = ctx;
    const live = ctx.live !== false;
    const classes = ctx.classes || {};
    const listenOpts = ctx.signal ? { signal: ctx.signal, capture: true } : { capture: true };
    const values = controller.state?.parameters || {};
    for (const { group, specs } of groupActionControlSpecs(controller.schema())) {
      if (group) {
        const heading = host.ownerDocument.createElement("div");
        if (classes.group) heading.className = classes.group;
        heading.textContent = group;
        heading.style.marginTop = "8px";
        heading.style.fontWeight = "600";
        host.appendChild(heading);
      }
      for (const spec of specs) {
        host.appendChild(renderField(host.ownerDocument, spec, values[spec.path], {
          controller,
          live,
          classes,
          listenOpts
        }));
      }
    }
  }
  function renderField(doc, spec, current, ctx) {
    const { controller, live, classes, listenOpts } = ctx;
    const row = doc.createElement("div");
    if (classes.row) row.className = classes.row;
    else row.style.cssText = "display:flex;flex-direction:column;gap:4px;";
    const label = doc.createElement(spec.type === "toggle" ? "div" : "label");
    if (classes.label) label.className = classes.label;
    label.textContent = spec.label || spec.path;
    row.appendChild(label);
    const applyControlClass = spec.type !== "toggle" || classes.applyControlClassToToggle;
    let control;
    const widget = spec.widget || (spec.type === "radio" ? "radio" : spec.type === "select" ? "select" : null);
    if (spec.type === "toggle") {
      const input = doc.createElement("input");
      input.type = "checkbox";
      input.checked = !!current;
      input.addEventListener("change", () => {
        void controller.update(spec.path, !!input.checked);
      }, listenOpts);
      control = input;
    } else if (spec.type === "radio" || widget === "radio") {
      const wrap = doc.createElement("div");
      wrap.setAttribute("role", "radiogroup");
      wrap.setAttribute("aria-label", spec.label || spec.path);
      const name = `kp-action-${spec.path}`;
      for (const optionDef of spec.options || []) {
        const optLabel = doc.createElement("label");
        const radio = doc.createElement("input");
        radio.type = "radio";
        radio.name = name;
        radio.value = optionDef.id;
        radio.checked = optionDef.id === current;
        radio.addEventListener("change", () => {
          if (!radio.checked) return;
          void controller.update(spec.path, radio.value);
        }, listenOpts);
        optLabel.appendChild(radio);
        optLabel.appendChild(doc.createTextNode(optionDef.label));
        wrap.appendChild(optLabel);
      }
      control = wrap;
    } else if (spec.type === "enum" || spec.type === "select" || widget === "select") {
      const select = doc.createElement("select");
      for (const optionDef of spec.options || []) {
        const option = doc.createElement("option");
        option.value = optionDef.id;
        option.textContent = optionDef.label;
        option.selected = optionDef.id === current;
        select.appendChild(option);
      }
      select.addEventListener("change", () => {
        void controller.update(spec.path, select.value);
      }, listenOpts);
      control = select;
    } else if (spec.type === "range") {
      const input = doc.createElement("input");
      input.type = "number";
      if (spec.min != null) input.min = String(spec.min);
      if (spec.max != null) input.max = String(spec.max);
      if (spec.step != null) input.step = String(spec.step);
      input.value = current != null ? String(current) : "";
      input.addEventListener("change", () => {
        const n = Number(input.value);
        void controller.update(spec.path, Number.isFinite(n) ? n : spec.defaultValue);
      }, listenOpts);
      control = input;
    } else if (spec.type === "textarea" || spec.multiline) {
      const textarea = doc.createElement("textarea");
      textarea.setAttribute("data-multiline", "true");
      const rows = Number(spec.rows);
      textarea.rows = Number.isFinite(rows) && rows > 0 ? rows : 3;
      textarea.value = current == null ? "" : String(current);
      const commit = () => {
        void controller.update(spec.path, textarea.value);
      };
      textarea.addEventListener("change", commit, listenOpts);
      if (live) textarea.addEventListener("input", commit, listenOpts);
      control = textarea;
    } else {
      const input = doc.createElement("input");
      input.type = "text";
      input.value = current == null ? "" : String(current);
      const commit = () => {
        void controller.update(spec.path, input.value);
      };
      input.addEventListener("change", commit, listenOpts);
      if (live) input.addEventListener("input", commit, listenOpts);
      control = input;
    }
    if (applyControlClass && classes.control && "className" in control) {
      control.className = classes.control;
    }
    if (spec.placeholder && "placeholder" in control) {
      control.placeholder = String(spec.placeholder);
    }
    row.appendChild(control);
    if (control?.tagName === "SELECT") {
      enhanceNativeSelect(
        /** @type {HTMLSelectElement} */
        control
      );
    }
    return row;
  }

  // src/ui/key-action-settings.js
  var MODE_PARAMETER_ID = "mode";
  var DESTINATION_PARAMETER_ID = "destination";
  var INLINE_ENUM_PARAMETER_IDS = Object.freeze(["mode", "action", "format", "destination"]);
  function getActionSettingsDef(actionId) {
    const def = getFunctionDef(actionId);
    if (!def || !def.parameters || def.parameters.length === 0) return null;
    const modeParam = def.parameters.find((p) => p && p.id === MODE_PARAMETER_ID && p.type === "enum");
    return {
      modes: modeParam ? modeParam.options : void 0,
      defaultMode: modeParam ? modeParam.defaultValue : void 0,
      parameters: def.parameters
    };
  }
  function actionHasModes(actionId) {
    const def = getActionSettingsDef(actionId);
    return !!(def?.modes && def.modes.length > 0);
  }
  function nonModeParameters(actionId) {
    const def = getActionSettingsDef(actionId);
    return (def?.parameters || []).filter((p) => p && p.id !== MODE_PARAMETER_ID);
  }
  function configPanelParameters(actionId) {
    return nonModeParameters(actionId).filter(
      (p) => p && !INLINE_ENUM_PARAMETER_IDS.includes(p.id) && shouldShowFunctionParameter(actionId, p)
    );
  }
  function getActionInlineEnumDefs(actionId) {
    const def = getActionSettingsDef(actionId);
    return (def?.parameters || []).filter(
      (p) => p && p.type === "enum" && INLINE_ENUM_PARAMETER_IDS.includes(p.id)
    );
  }
  function getActionDestinationDef(actionId) {
    const def = getActionSettingsDef(actionId);
    const param = def?.parameters?.find((p) => p && p.id === DESTINATION_PARAMETER_ID && p.type === "enum");
    return param || null;
  }
  function actionHasParameters(actionId) {
    return configPanelParameters(actionId).length > 0;
  }
  function actionHasDestination(actionId) {
    return !!getActionDestinationDef(actionId);
  }
  function getActionMode(parameters, actionId) {
    const def = getActionSettingsDef(actionId);
    const fallback = def?.defaultMode || def?.modes?.[0]?.id || "element";
    const stored = parameters?.[MODE_PARAMETER_ID];
    if (typeof stored === "string" && def?.modes?.some((m) => m.id === stored)) {
      return stored;
    }
    return fallback;
  }
  function getActionParameter(parameters, actionId, paramId) {
    const def = getActionSettingsDef(actionId);
    const paramDef = def?.parameters?.find((p) => p && p.id === paramId) || null;
    const stored = parameters?.[paramId];
    if (stored !== void 0) return stored;
    return paramDef ? paramDef.defaultValue : void 0;
  }
  async function setActionMode(actionId, modeId) {
    const def = getActionSettingsDef(actionId);
    if (!def?.modes?.some((m) => m.id === modeId)) {
      throw new Error(`Unknown mode ${modeId} for action ${actionId}`);
    }
    const action = await setBuiltinFunctionUserActionParameter(actionId, MODE_PARAMETER_ID, modeId);
    notifyActionSettingsChanged(actionId, action);
    return action;
  }
  async function setActionParameter(actionId, paramId, value) {
    const action = await setBuiltinFunctionUserActionParameter(actionId, paramId, value);
    notifyActionSettingsChanged(actionId, action);
    return action;
  }
  function notifyActionSettingsChanged(actionId, action) {
    try {
      document.dispatchEvent(new CustomEvent("keypilot:action-settings-changed", {
        detail: { actionId, action }
      }));
    } catch {
    }
  }
  var CONFIG_PANEL_STYLE_ATTR = "data-kp-action-config-style";
  function ensureConfigPanelStyles(root) {
    injectChromeStyles(root, { attr: CONFIG_PANEL_STYLE_ATTR, css: `
:host {
  position: fixed;
  z-index: ${Z_INDEX.KEY_ACTION_CONFIG || Z_INDEX.KEYBINDINGS_POPOVER + 1};
  min-width: 240px;
  max-width: min(360px, calc(100vw - 24px));
  color: ${NCT_DARK_UI_COLORS.fg};
  font-family: ${KP_UI_FONT};
  font-size: 12px;
  line-height: 1.4;
  border-radius: ${NCT_DARK_UI_PANEL_RADIUS};
  border: ${NCT_DARK_UI_PANEL_BORDER};
  background: ${NCT_DARK_UI_PANEL_BACKGROUND};
  box-shadow: ${NCT_DARK_UI_PANEL_BOX_SHADOW};
  box-sizing: border-box;
}
:host([hidden]) { display: none !important; }
.kp-action-config-panel__titlebar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 10px;
  cursor: grab;
  user-select: none;
  background: ${NCT_DARK_UI_TITLEBAR_GRADIENT};
  border-bottom: ${NCT_DARK_UI_TITLEBAR_BORDER_BOTTOM};
  letter-spacing: var(--kp-type-tracking-titlebar, 0.02em);
  text-transform: var(--kp-type-transform-titlebar, none);
}
.kp-action-config-panel__title {
  font-weight: var(--kp-titlebar-title-weight, 600);
  font-size: 12px;
  letter-spacing: var(--kp-type-tracking-titlebar, 0.02em);
  text-transform: var(--kp-type-transform-titlebar, none);
  color: var(--kp-color-fg, inherit);
}
.kp-action-config-panel__close {
  appearance: none;
  background: ${NCT_DARK_UI_BTN_GRADIENT};
  border: ${NCT_DARK_UI_BTN_BORDER};
  color: inherit;
  width: 22px;
  height: 22px;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
}
.kp-action-config-panel__body {
  padding: 10px 12px 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.kp-action-config-panel__row {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.kp-action-config-panel__label {
  opacity: 0.85;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.kp-action-config-panel__control {
  appearance: none;
  width: 100%;
  box-sizing: border-box;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  border: ${NCT_DARK_UI_FIELD_BORDER};
  background: ${NCT_DARK_UI_FIELD_BACKGROUND};
  box-shadow: ${NCT_DARK_UI_FIELD_BOX_SHADOW};
  color: inherit;
  padding: 6px 8px;
  font: inherit;
}
.kp-action-config-panel__control:focus {
  outline: none;
  border-color: ${NCT_DARK_UI_FIELD_FOCUS_BORDER};
  box-shadow: ${NCT_DARK_UI_FIELD_FOCUS_BOX_SHADOW};
}
.kp-action-config-panel__control[data-multiline="true"] {
  min-height: 64px;
  resize: vertical;
  line-height: 1.35;
}
.kp-action-config-panel__empty {
  opacity: 0.7;
  font-style: italic;
}
` });
  }
  var KeyActionConfigPanel = class {
    constructor() {
      this.root = null;
      this.shadowRoot = null;
      this.actionId = null;
      this._dragApi = null;
      this.onSettingsChanged = null;
    }
    /**
     * @param {string} actionId
     * @param {{ title?: string, anchorRect?: DOMRect|null }} [opts]
     */
    async open(actionId, opts = {}) {
      const def = getActionSettingsDef(actionId);
      if (!def) return;
      const doc = document;
      this.actionId = actionId;
      if (!this.root) {
        this.root = doc.createElement("div");
        this.root.className = "kp-action-config-panel";
        this.root.setAttribute("role", "dialog");
        this.shadowRoot = ensureOpenChromeShadow(this.root, { id: "action-config", chromeWindow: true });
        const panelRoot2 = this.shadowRoot || this.root;
        ensureConfigPanelStyles(panelRoot2);
        panelRoot2.innerHTML = `
        <div class="kp-action-config-panel__titlebar" data-kp-config-drag="true">
          <div class="kp-action-config-panel__title"></div>
          <button type="button" class="kp-action-config-panel__close" aria-label="Close">\xD7</button>
        </div>
        <div class="kp-action-config-panel__body"></div>
      `;
        doc.body.appendChild(this.root);
        const closeBtn = panelRoot2.querySelector(".kp-action-config-panel__close");
        closeBtn?.addEventListener("click", (e) => {
          e.preventDefault();
          e.stopPropagation();
          this.close();
        });
        const handle = panelRoot2.querySelector('[data-kp-config-drag="true"]');
        this._dragApi = makePanelDraggable(this.root, handle, {
          excludeSelector: ".kp-action-config-panel__close"
        });
      }
      const panelRoot = this.shadowRoot || this.root.shadowRoot || this.root;
      ensureConfigPanelStyles(panelRoot);
      const titleEl = panelRoot.querySelector(".kp-action-config-panel__title");
      if (titleEl) titleEl.textContent = opts.title || `${actionId} settings`;
      await this._renderBody(actionId, def);
      this.root.hidden = false;
      const margin = 12;
      const vw = window.innerWidth || 800;
      const vh = window.innerHeight || 600;
      const rect = opts.anchorRect;
      let left = rect ? rect.right + 10 : Math.round(vw * 0.5 - 120);
      let top = rect ? rect.top : Math.round(vh * 0.25);
      left = Math.max(margin, Math.min(left, vw - margin - 240));
      top = Math.max(margin, Math.min(top, vh - margin - 80));
      this.root.style.left = `${Math.round(left)}px`;
      this.root.style.top = `${Math.round(top)}px`;
      this.root.style.right = "auto";
      this.root.style.bottom = "auto";
    }
    close() {
      if (this.root) this.root.hidden = true;
      this.actionId = null;
    }
    isOpen() {
      return !!(this.root && !this.root.hidden);
    }
    /**
     * @param {string} actionId
     * @param {ActionSettingsDef} def
     */
    async _renderBody(actionId, def) {
      const panelRoot = this.shadowRoot || this.root?.shadowRoot || this.root;
      const body = panelRoot?.querySelector(".kp-action-config-panel__body");
      if (!body) return;
      body.replaceChildren();
      const action = await getOrCreateBuiltinFunctionUserAction(actionId);
      const storedParams = action?.parameters || {};
      const params = configPanelParameters(actionId);
      if (params.length === 0) {
        const empty = document.createElement("div");
        empty.className = "kp-action-config-panel__empty";
        empty.textContent = "No configurable parameters for this key.";
        body.appendChild(empty);
        return;
      }
      if (actionId === "EXECUTE_JS") {
        const hint = document.createElement("div");
        hint.className = "kp-action-config-panel__empty";
        hint.textContent = "Bindings: kpHoveredClickable, kpHoverLeaf, kpFocusedTextField, kpMode, kpPageUrl, kpSelection, kpPriorResult. Callbacks are functions only when checked. See Keyboard Layout Editor Inspector \u2192 docs icon, or Execute JS in KeyPilot Docs.";
        body.appendChild(hint);
      }
      const controller = createActionConfigController();
      controller.load({
        functionId: actionId,
        snapshot: storedParams,
        parameters: params,
        persist: async (functionId, paramId, value) => {
          const next = await setActionParameter(functionId, paramId, value);
          try {
            this.onSettingsChanged?.(next);
          } catch {
          }
          return next;
        }
      });
      appendActionConfigFields(body, {
        controller,
        live: false,
        classes: {
          row: "kp-action-config-panel__row",
          label: "kp-action-config-panel__label",
          control: "kp-action-config-panel__control",
          group: "kp-action-config-panel__label",
          applyControlClassToToggle: true
        }
      });
    }
    dispose() {
      try {
        this._dragApi?.dispose?.();
      } catch {
      }
      this._dragApi = null;
      try {
        this.root?.remove();
      } catch {
      }
      this.root = null;
      this.shadowRoot = null;
      this.actionId = null;
    }
  };
  var _sharedConfigPanel = null;
  function getSharedKeyActionConfigPanel() {
    if (!_sharedConfigPanel) {
      _sharedConfigPanel = new KeyActionConfigPanel();
    }
    return _sharedConfigPanel;
  }

  // src/ui/keybindings-ui.js
  init_keyboard_layouts();
  var _activePopoverContext = null;
  var _pinnedActionId = null;
  var _pinnedKeyEl = null;
  var _settingsRenderGen = 0;
  var KEY_INFO_POPOVER_INNER_HTML = `
      <div class="kp-popover-head">
        <div class="kp-popover-icon" aria-hidden="true"></div>
        <div class="kp-popover-title-wrap">
          <div class="kp-popover-title-row">
            <div class="kp-popover-title"></div>
            <div class="kp-popover-settings-hint" hidden>Click to show settings</div>
          </div>
          <div class="kp-popover-keys"></div>
        </div>
      </div>
      <p class="kp-popover-desc"></p>
      <div class="kp-popover-settings" hidden></div>
    `;
  function actionHasSettings(actionId) {
    return actionHasModes(actionId) || actionHasDestination(actionId) || getActionInlineEnumDefs(actionId).length > 0 || actionHasParameters(actionId);
  }
  function getRuntimeFontUrls() {
    try {
      const getURL = typeof chrome !== "undefined" && chrome && chrome.runtime && chrome.runtime.getURL ? chrome.runtime.getURL.bind(chrome.runtime) : null;
      if (!getURL) return null;
      return {
        robotech: getURL("fonts/ROBOTECHGPRegular.ttf"),
        titillium: getURL("fonts/TitilliumTextRegular.otf"),
        titilliumBold: getURL("fonts/TitilliumTextBold.ttf"),
        cubellan: getURL("fonts/CubellanRegular.ttf"),
        ezarion: getURL("fonts/EzarionRegular.ttf"),
        dosis: getURL("fonts/DosisBook.ttf")
      };
    } catch {
      return null;
    }
  }
  function getStyleCss() {
    return getKeybindingsUiCss({
      zKeybindingsPopover: Z_INDEX.KEYBINDINGS_POPOVER,
      fontUrls: getRuntimeFontUrls()
    });
  }
  function ensureStylesInjected(root = document) {
    const fontUrls = getRuntimeFontUrls();
    const css = getStyleCss();
    const attrName = typeof KEYBINDINGS_UI_STYLE_ATTR === "string" && KEYBINDINGS_UI_STYLE_ATTR ? KEYBINDINGS_UI_STYLE_ATTR : "data-kp-keybindings-ui-style";
    injectChromeStyles(root, { attr: attrName, css });
    try {
      const doc = root && root.nodeType === 9 ? root : root?.ownerDocument || document;
      preloadKeybindingsUiFonts(doc, fontUrls);
      if (doc && fontUrls) {
        injectChromeStyles(doc, {
          attr: KEYBINDINGS_UI_FONT_STYLE_ATTR,
          css: getKeybindingsUiFontFaceCss(fontUrls)
        });
      }
    } catch {
    }
  }
  function clearElement(el2) {
    while (el2 && el2.firstChild) el2.removeChild(el2.firstChild);
  }
  function el(doc, tag, className, text) {
    const node = doc.createElement(tag);
    if (className) node.className = className;
    if (text !== void 0) node.textContent = text;
    return node;
  }
  function updateExistingKeyboardDOM({ container, keybindings }) {
    const doc = container.ownerDocument || document;
    try {
      container.querySelectorAll(".key:not([data-kp-action-id]) > .key-bg-icon").forEach((el2) => {
        try {
          el2.remove();
        } catch {
        }
      });
    } catch {
    }
    try {
      container.querySelectorAll(".key").forEach((keyEl) => {
        ensureKeyPressOverlay(doc, keyEl);
      });
    } catch {
    }
    const actionEls = container.querySelectorAll("[data-kp-action-id]");
    if (!actionEls || actionEls.length === 0) return false;
    for (const keyEl of actionEls) {
      const actionId = keyEl.dataset.kpActionId;
      const binding = keybindings && keybindings[actionId];
      const baseClass = keyEl.dataset.kpBaseClass || "key";
      const keyboardClass = binding && binding.keyboardClass ? String(binding.keyboardClass) : "";
      keyEl.className = `${baseClass}${keyboardClass ? " " + keyboardClass : ""}`;
      ensureKeyBackgroundIcon(doc, keyEl);
      ensureKeyPressOverlay(doc, keyEl);
      const title = binding && (binding.description || binding.label) || actionId;
      try {
        keyEl.removeAttribute("title");
      } catch {
      }
      keyEl.setAttribute("aria-label", title);
      const main = keyEl.querySelector(".key-main");
      if (main) main.textContent = binding && binding.label || actionId;
      const labelText = binding && (binding.displayKey || binding.keyLabel) || "";
      const existingLabel = keyEl.querySelector(".key-label");
      if (labelText) {
        if (existingLabel) existingLabel.textContent = labelText;
        else keyEl.appendChild(el(doc, "div", "key-label", labelText));
      } else if (existingLabel) {
        existingLabel.remove();
      }
    }
    return true;
  }
  function renderKeybindingsKeyboard({ container, keybindings, keyboardLayout, layoutId, attachPopovers = true } = {}) {
    if (!container) return;
    const doc = container.ownerDocument || document;
    ensureStylesInjected(container.getRootNode?.() || doc);
    const layout = keyboardLayout && Array.isArray(keyboardLayout) ? keyboardLayout : KEYBINDINGS_KEYBOARD_LAYOUT;
    const layoutKey = typeof layoutId === "string" ? layoutId : "";
    let existingVisual = null;
    try {
      existingVisual = container.querySelector(":scope > .keyboard-visual");
    } catch {
      try {
        existingVisual = container.querySelector(".keyboard-visual");
      } catch {
      }
    }
    if (existingVisual && existingVisual.dataset && existingVisual.dataset.kpKeyboardBuilt === "true") {
      const existingLayoutKey = String(existingVisual.dataset.kpLayoutId || "");
      const canReuse = !!layoutKey && existingLayoutKey === layoutKey;
      if (canReuse) {
        if (updateExistingKeyboardDOM({ container, keybindings })) {
          if (attachPopovers) {
            attachKeyPopoverBehavior({ root: container, keybindings });
          } else {
            detachKeyPopoverBehavior(container);
          }
          return;
        }
      }
    }
    clearElement(container);
    const visual = el(doc, "div", `keyboard-visual ${KEYBINDINGS_UI_ROOT_CLASS}`);
    visual.dataset.kpKeyboardBuilt = "true";
    if (layoutKey) visual.dataset.kpLayoutId = layoutKey;
    container.appendChild(visual);
    for (const row of layout) {
      const rowEl = el(doc, "div", "keyboard-row");
      visual.appendChild(rowEl);
      for (const item of row) {
        if (item.type === "special") {
          const keyEl2 = el(doc, "div", item.className || "key");
          keyEl2.appendChild(el(doc, "span", "key-text", item.text));
          ensureKeyPressOverlay(doc, keyEl2);
          rowEl.appendChild(keyEl2);
          continue;
        }
        if (item.type === "key") {
          const keyEl2 = el(doc, "div", item.className || "key");
          keyEl2.appendChild(el(doc, "span", "key-text", item.text));
          ensureKeyPressOverlay(doc, keyEl2);
          rowEl.appendChild(keyEl2);
          continue;
        }
        const binding = keybindings && keybindings[item.id];
        const baseClass = item.className || "key";
        const className = `${baseClass}${binding && binding.keyboardClass ? " " + binding.keyboardClass : ""}`;
        const keyEl = el(doc, "button", className);
        keyEl.dataset.kpActionId = item.id;
        keyEl.dataset.kpBaseClass = baseClass;
        keyEl.type = "button";
        keyEl.tabIndex = -1;
        try {
          keyEl.removeAttribute("title");
        } catch {
        }
        keyEl.setAttribute(
          "aria-label",
          binding && (binding.description || binding.label) || item.fallbackText || item.id
        );
        ensureKeyBackgroundIcon(doc, keyEl);
        const main = el(
          doc,
          "div",
          "key-main",
          binding && binding.label || item.fallbackText || item.id
        );
        keyEl.appendChild(main);
        const labelText = binding && binding.displayKey || binding && binding.keyLabel || "";
        if (labelText) {
          keyEl.appendChild(el(doc, "div", "key-label", labelText));
        }
        ensureKeyPressOverlay(doc, keyEl);
        rowEl.appendChild(keyEl);
      }
    }
    if (attachPopovers) {
      attachKeyPopoverBehavior({
        root: container,
        keybindings
      });
    } else {
      detachKeyPopoverBehavior(container);
    }
  }
  function detachKeyPopoverBehavior(root) {
    if (!root || !root._kpKeyHandlers) return;
    try {
      unpinKeyPopover();
    } catch {
    }
    const keyElements = root.querySelectorAll("[data-kp-action-id]");
    const h = root._kpKeyHandlers;
    keyElements.forEach((keyEl) => {
      try {
        if (h.enter) keyEl.removeEventListener("pointerenter", h.enter);
        if (h.leave) keyEl.removeEventListener("pointerleave", h.leave);
        if (h.focusin) keyEl.removeEventListener("focusin", h.focusin);
        if (h.focusout) keyEl.removeEventListener("focusout", h.focusout);
        if (h.click) keyEl.removeEventListener("click", h.click);
      } catch {
      }
    });
    try {
      if (h.docKeydown) document.removeEventListener("keydown", h.docKeydown, true);
      if (h.docPointerDown) document.removeEventListener("pointerdown", h.docPointerDown, true);
      if (h.resize) window.removeEventListener("resize", h.resize, true);
    } catch {
    }
    root._kpKeyHandlers = null;
    try {
      if (_activePopoverContext && _activePopoverContext.root === root) {
        _activePopoverContext = null;
      }
    } catch {
    }
  }
  function supportsPopoverApi(el2) {
    try {
      return !!(el2 && typeof el2.showPopover === "function" && typeof HTMLElement !== "undefined" && "popover" in HTMLElement.prototype);
    } catch {
      return false;
    }
  }
  function ensurePopover(doc, _container) {
    ensureStylesInjected(doc);
    if (!doc || !doc.body) return null;
    let pop = doc.body.querySelector('.kp-keybindings-popover[data-kp-key-info-popover="true"]') || doc.body.querySelector(".kp-keybindings-popover");
    if (!pop) {
      try {
        const legacy = doc.querySelector(".kp-floating-keyboard-help .kp-keybindings-popover");
        if (legacy) {
          pop = legacy;
          doc.body.appendChild(pop);
        }
      } catch {
      }
    }
    if (!pop) {
      pop = doc.createElement("div");
      pop.className = "kp-keybindings-popover";
      pop.setAttribute("data-kp-key-info-popover", "true");
      pop.setAttribute("data-placement", "top");
      pop.setAttribute("role", "tooltip");
      pop.innerHTML = KEY_INFO_POPOVER_INNER_HTML;
      doc.body.appendChild(pop);
      try {
        pop.hidden = true;
      } catch {
      }
      try {
        pop.removeAttribute("data-kp-popover-open");
      } catch {
      }
    } else {
      try {
        pop.setAttribute("data-kp-key-info-popover", "true");
      } catch {
      }
      if (pop.parentElement !== doc.body) {
        try {
          doc.body.appendChild(pop);
        } catch {
        }
      }
      if (!pop.querySelector(".kp-popover-icon")) {
        try {
          pop.setAttribute("role", "tooltip");
          pop.innerHTML = KEY_INFO_POPOVER_INNER_HTML;
        } catch {
        }
      }
      if (!pop.querySelector(".kp-popover-settings")) {
        try {
          const settingsHost = doc.createElement("div");
          settingsHost.className = "kp-popover-settings";
          settingsHost.hidden = true;
          pop.appendChild(settingsHost);
        } catch {
        }
      }
      if (!pop.querySelector(".kp-popover-settings-hint")) {
        try {
          const titleEl = pop.querySelector(".kp-popover-title");
          const wrap = pop.querySelector(".kp-popover-title-wrap") || titleEl?.parentElement;
          if (wrap && titleEl) {
            let row = wrap.querySelector(".kp-popover-title-row");
            if (!row) {
              row = doc.createElement("div");
              row.className = "kp-popover-title-row";
              wrap.insertBefore(row, titleEl);
              row.appendChild(titleEl);
            }
            const hint = doc.createElement("div");
            hint.className = "kp-popover-settings-hint";
            hint.hidden = true;
            hint.textContent = "Click to show settings";
            row.appendChild(hint);
          }
        } catch {
        }
      }
    }
    try {
      if (supportsPopoverApi(pop) && pop.popover !== "manual") {
        pop.popover = "manual";
      }
    } catch {
      try {
        if (!pop.hasAttribute("popover")) pop.setAttribute("popover", "manual");
      } catch {
      }
    }
    return pop;
  }
  function hidePopover(pop, opts = {}) {
    if (!pop) return;
    if (opts.clearPinned !== false) {
      _pinnedActionId = null;
      _pinnedKeyEl = null;
      _settingsRenderGen += 1;
      try {
        pop.removeAttribute("data-kp-popover-pinned");
      } catch {
      }
      try {
        pop.style.pointerEvents = "";
      } catch {
      }
    }
    try {
      if (supportsPopoverApi(pop) && typeof pop.hidePopover === "function") {
        try {
          if (pop.matches?.(":popover-open")) pop.hidePopover();
        } catch {
          try {
            pop.hidePopover();
          } catch {
          }
        }
      }
    } catch {
    }
    try {
      pop.hidden = true;
    } catch {
    }
    try {
      pop.removeAttribute("data-kp-popover-open");
    } catch {
    }
    try {
      const iconEl = pop.querySelector(".kp-popover-icon");
      if (iconEl) {
        iconEl.style.backgroundImage = "";
        iconEl.style.webkitMaskImage = "";
        iconEl.style.maskImage = "";
        iconEl.style.backgroundColor = "";
        iconEl.hidden = true;
      }
      ["--kp-key-face", "--kp-key-mid", "--kp-key-deep", "--kp-key-icon"].forEach((prop) => {
        try {
          pop.style.removeProperty(prop);
        } catch {
        }
      });
      const settingsHost = pop.querySelector(".kp-popover-settings");
      if (settingsHost) {
        settingsHost.hidden = true;
        settingsHost.replaceChildren();
      }
    } catch {
    }
  }
  function openPopoverElement(pop) {
    if (!pop) return;
    try {
      pop.hidden = false;
    } catch {
    }
    try {
      pop.setAttribute("data-kp-popover-open", "true");
    } catch {
    }
    if (supportsPopoverApi(pop) && typeof pop.showPopover === "function") {
      try {
        if (!pop.matches?.(":popover-open")) pop.showPopover();
      } catch {
        try {
          pop.showPopover();
        } catch {
        }
      }
    }
  }
  function clamp(n, min, max) {
    return Math.max(min, Math.min(max, n));
  }
  function applyKeyMaterialToPopover(pop, targetEl) {
    if (!pop || !targetEl) return;
    try {
      const cs = (targetEl.ownerDocument || document).defaultView?.getComputedStyle?.(targetEl);
      if (!cs) return;
      const face = (cs.getPropertyValue("--kp-key-face") || "").trim();
      const mid = (cs.getPropertyValue("--kp-key-mid") || "").trim();
      const deep = (cs.getPropertyValue("--kp-key-deep") || "").trim();
      const icon = (cs.getPropertyValue("--kp-key-icon") || "").trim();
      if (face) pop.style.setProperty("--kp-key-face", face);
      if (mid) pop.style.setProperty("--kp-key-mid", mid);
      if (deep) pop.style.setProperty("--kp-key-deep", deep);
      if (icon) pop.style.setProperty("--kp-key-icon", icon);
    } catch {
    }
  }
  function showPopoverForTarget({ doc, pop, targetEl, binding, actionId, pinned = false }) {
    if (!doc || !pop || !targetEl) return;
    const titleEl = pop.querySelector(".kp-popover-title");
    const keysEl = pop.querySelector(".kp-popover-keys");
    const descEl = pop.querySelector(".kp-popover-desc");
    const iconEl = pop.querySelector(".kp-popover-icon");
    const hintEl = pop.querySelector(".kp-popover-settings-hint");
    const title = binding && binding.label || actionId;
    const keys = binding && (binding.displayKey || binding.keyLabel) || "";
    const desc = binding && (binding.description || binding.label) || "";
    const iconMaskUri = getActionIconDataUri(actionId, { fill: "black" });
    applyKeyMaterialToPopover(pop, targetEl);
    if (titleEl) titleEl.textContent = title;
    if (keysEl) keysEl.textContent = keys ? `Key: ${keys}` : "";
    if (descEl) descEl.textContent = desc;
    if (hintEl) {
      hintEl.textContent = "Click to show settings";
      const showHint = !pinned && actionHasSettings(actionId);
      hintEl.hidden = !showHint;
    }
    if (iconEl) {
      if (iconMaskUri) {
        iconEl.hidden = false;
        iconEl.style.backgroundImage = "none";
        iconEl.style.backgroundColor = "var(--kp-key-icon)";
        iconEl.style.webkitMaskImage = iconMaskUri;
        iconEl.style.maskImage = iconMaskUri;
        iconEl.style.webkitMaskRepeat = "no-repeat";
        iconEl.style.maskRepeat = "no-repeat";
        iconEl.style.webkitMaskPosition = "center";
        iconEl.style.maskPosition = "center";
        iconEl.style.webkitMaskSize = "62% 62%";
        iconEl.style.maskSize = "62% 62%";
      } else {
        iconEl.hidden = true;
        iconEl.style.backgroundImage = "";
        iconEl.style.webkitMaskImage = "";
        iconEl.style.maskImage = "";
        iconEl.style.backgroundColor = "";
      }
    }
    try {
      if (pinned) {
        pop.setAttribute("data-kp-popover-pinned", "true");
        pop.style.pointerEvents = "auto";
        void renderPopoverSettings({ doc, pop, targetEl, binding, actionId });
      } else {
        _settingsRenderGen += 1;
        pop.removeAttribute("data-kp-popover-pinned");
        pop.style.pointerEvents = "";
        const settingsHost = pop.querySelector(".kp-popover-settings");
        if (settingsHost) {
          settingsHost.hidden = true;
          settingsHost.replaceChildren();
        }
      }
    } catch {
    }
    const targetRect = targetEl.getBoundingClientRect();
    openPopoverElement(pop);
    const placeAt = (leftPx, topPx) => {
      try {
        try {
          pop.style.removeProperty("inset");
        } catch {
        }
        pop.style.setProperty("position", "fixed", "important");
        pop.style.setProperty("margin", "0", "important");
        pop.style.setProperty("right", "auto", "important");
        pop.style.setProperty("bottom", "auto", "important");
        pop.style.setProperty("left", `${Math.round(leftPx)}px`, "important");
        pop.style.setProperty("top", `${Math.round(topPx)}px`, "important");
      } catch {
        try {
          pop.style.removeProperty("inset");
        } catch {
        }
        pop.style.position = "fixed";
        pop.style.margin = "0";
        pop.style.right = "auto";
        pop.style.bottom = "auto";
        pop.style.left = `${Math.round(leftPx)}px`;
        pop.style.top = `${Math.round(topPx)}px`;
      }
    };
    const margin = 10;
    const gap = 10;
    placeAt(-9999, -9999);
    const popRect = pop.getBoundingClientRect();
    const popW = popRect.width || pop.offsetWidth || 160;
    const popH = popRect.height || pop.offsetHeight || 80;
    const vw = Math.max(
      doc.documentElement?.clientWidth || 0,
      (typeof window !== "undefined" ? window.innerWidth : 0) || 0
    );
    const vh = Math.max(
      doc.documentElement?.clientHeight || 0,
      (typeof window !== "undefined" ? window.innerHeight : 0) || 0
    );
    const spaceAbove = targetRect.top;
    const spaceBelow = vh - targetRect.bottom;
    const needs = popH + gap + margin;
    const placeAbove = spaceAbove >= needs || spaceAbove >= spaceBelow && spaceAbove >= gap + 24;
    const placement = placeAbove ? "top" : "bottom";
    pop.setAttribute("data-placement", placement);
    const targetCenterX = targetRect.left + targetRect.width / 2;
    let left = targetCenterX - popW / 2;
    const maxLeft = Math.max(margin, vw - margin - popW);
    left = clamp(left, margin, maxLeft);
    let top;
    if (placement === "top") {
      top = targetRect.top - gap - popH;
      if (top < margin) top = margin;
    } else {
      top = targetRect.bottom + gap;
      const maxTop = Math.max(margin, vh - margin - popH);
      if (top > maxTop) top = maxTop;
    }
    placeAt(left, top);
    const arrowLeft = clamp(targetCenterX - left - 9, 12, Math.max(12, popW - 24));
    pop.style.setProperty("--kp-arrow-left", `${Math.round(arrowLeft)}px`);
  }
  function paintPopoverSettings({ doc, pop, targetEl, binding, actionId, parameters }) {
    const host = pop.querySelector(".kp-popover-settings");
    if (!host) return;
    const inlineEnums = getActionInlineEnumDefs(actionId);
    const hasModes = actionHasModes(actionId);
    const hasDestination = actionHasDestination(actionId);
    const hasParams = actionHasParameters(actionId);
    if (!inlineEnums.length && !hasModes && !hasDestination && !hasParams) {
      host.hidden = true;
      host.replaceChildren();
      return;
    }
    host.hidden = false;
    host.replaceChildren();
    const enums = inlineEnums.length ? inlineEnums : [
      ...hasModes ? [{ id: "mode", ...getActionSettingsDef(actionId) || {} }] : [],
      ...hasDestination ? [getActionDestinationDef(actionId)].filter(Boolean) : []
    ];
    for (const param of enums) {
      if (!param || !param.id) continue;
      const options = param.options || (param.id === "mode" ? getActionSettingsDef(actionId)?.modes : null) || [];
      if (!options.length) continue;
      const current = param.id === "mode" ? getActionMode(parameters, actionId) : getActionParameter(parameters, actionId, param.id);
      const row = doc.createElement("div");
      row.className = "kp-popover-setting-row";
      const label = doc.createElement("div");
      label.className = "kp-popover-setting-label";
      label.textContent = param.label || param.id;
      row.appendChild(label);
      const wrap = doc.createElement("div");
      wrap.className = "kp-popover-mode-switch";
      wrap.setAttribute("role", "group");
      wrap.setAttribute("aria-label", param.label || param.id);
      wrap.dataset.paramId = param.id;
      for (const opt of options) {
        const btn = doc.createElement("button");
        btn.type = "button";
        btn.className = "kp-popover-mode-btn";
        btn.dataset.optionId = opt.id;
        btn.textContent = opt.label;
        btn.setAttribute("aria-pressed", opt.id === current ? "true" : "false");
        btn.addEventListener("click", async (e) => {
          e.preventDefault();
          e.stopPropagation();
          try {
            if (param.id === "mode") await setActionMode(actionId, opt.id);
            else await setActionParameter(actionId, param.id, opt.id);
            wrap.querySelectorAll(".kp-popover-mode-btn").forEach((el2) => {
              el2.setAttribute("aria-pressed", el2.dataset.optionId === opt.id ? "true" : "false");
            });
          } catch (err) {
            console.warn("[KeyPilot] Failed to set action parameter:", param.id, err);
          }
        });
        wrap.appendChild(btn);
      }
      row.appendChild(wrap);
      host.appendChild(row);
    }
    if (hasParams) {
      const configBtn = doc.createElement("button");
      configBtn.type = "button";
      configBtn.className = "kp-popover-config-btn";
      configBtn.textContent = "Config";
      configBtn.addEventListener("click", async (e) => {
        e.preventDefault();
        e.stopPropagation();
        const panel = getSharedKeyActionConfigPanel();
        await panel.open(actionId, {
          title: binding && binding.label || actionId,
          anchorRect: targetEl.getBoundingClientRect()
        });
      });
      host.appendChild(configBtn);
    }
  }
  async function renderPopoverSettings({ doc, pop, targetEl, binding, actionId }) {
    const host = pop.querySelector(".kp-popover-settings");
    if (!host) return;
    const gen = ++_settingsRenderGen;
    paintPopoverSettings({ doc, pop, targetEl, binding, actionId, parameters: null });
    if (gen !== _settingsRenderGen) return;
    let builtinAction = null;
    try {
      builtinAction = await getOrCreateBuiltinFunctionUserAction(actionId);
    } catch {
      builtinAction = null;
    }
    if (gen !== _settingsRenderGen) return;
    paintPopoverSettings({
      doc,
      pop,
      targetEl,
      binding,
      actionId,
      parameters: builtinAction?.parameters
    });
  }
  function emitKeyboardHelpKeyHover(detail = {}) {
    try {
      const now = Date.now();
      if (emitKeyboardHelpKeyHover._lastAt && now - emitKeyboardHelpKeyHover._lastAt < 120) return;
      emitKeyboardHelpKeyHover._lastAt = now;
      document.dispatchEvent(new CustomEvent("keypilot:action", {
        detail: {
          action: "hover",
          isKeyboardHelpKey: true,
          actionId: detail.actionId ? String(detail.actionId) : null,
          timestamp: now
        }
      }));
    } catch {
    }
  }
  function pinKeyPopover(actionId, opts = {}) {
    if (!actionId) return false;
    const ctx = _activePopoverContext;
    const root = ctx?.root;
    const keybindings = opts.keybindings || ctx?.keybindings;
    if (!root || !keybindings) return false;
    const doc = root.ownerDocument || document;
    const pop = ensurePopover(doc, null);
    if (!pop) return false;
    let keyEl = opts.keyEl || null;
    if (!keyEl) {
      try {
        keyEl = root.querySelector(`[data-kp-action-id="${CSS.escape(actionId)}"]`);
      } catch {
        keyEl = root.querySelector(`[data-kp-action-id="${actionId}"]`);
      }
    }
    if (!keyEl) return false;
    const binding = resolveKeybinding(actionId, keybindings);
    if (!binding) return false;
    if (_pinnedActionId === actionId && _pinnedKeyEl === keyEl) {
      if (root._kpKeyHandlers) {
        try {
          clearTimeout(root._kpKeyHandlers.hideTimer);
        } catch {
        }
        root._kpKeyHandlers.hideTimer = null;
      }
      _pinnedActionId = null;
      _pinnedKeyEl = null;
      showPopoverForTarget({ doc, pop, targetEl: keyEl, binding, actionId, pinned: false });
      return true;
    }
    _pinnedActionId = actionId;
    _pinnedKeyEl = keyEl;
    if (root._kpKeyHandlers) {
      try {
        clearTimeout(root._kpKeyHandlers.hideTimer);
      } catch {
      }
      root._kpKeyHandlers.hideTimer = null;
    }
    showPopoverForTarget({ doc, pop, targetEl: keyEl, binding, actionId, pinned: true });
    emitKeyboardHelpKeyHover({ actionId, keyEl });
    return true;
  }
  function inspectKeyActionFromAnchor(actionId, opts = {}) {
    const anchorEl = opts.anchorEl;
    if (!actionId || !anchorEl) return false;
    const doc = anchorEl.ownerDocument || document;
    const pop = ensurePopover(doc, null);
    if (!pop) return false;
    const keybindings = opts.keybindings || _activePopoverContext?.keybindings || {};
    const binding = opts.binding || resolveKeybinding(actionId, keybindings);
    if (!binding) return false;
    _pinnedActionId = actionId;
    _pinnedKeyEl = anchorEl;
    showPopoverForTarget({ doc, pop, targetEl: anchorEl, binding, actionId, pinned: true });
    emitKeyboardHelpKeyHover({ actionId, keyEl: anchorEl });
    return true;
  }
  function unpinKeyPopover() {
    const doc = document;
    const pop = doc.body?.querySelector?.('.kp-keybindings-popover[data-kp-key-info-popover="true"]') || doc.body?.querySelector?.(".kp-keybindings-popover");
    hidePopover(pop);
  }
  function attachKeyPopoverBehavior({ root, keybindings }) {
    if (!root) return;
    const doc = root.ownerDocument || document;
    const pop = ensurePopover(doc, null);
    if (!pop) return;
    _activePopoverContext = { root, keybindings };
    if (!root._kpKeyHandlers) {
      root._kpKeyHandlers = {
        enter: null,
        leave: null,
        focusin: null,
        focusout: null,
        click: null,
        docKeydown: null,
        docPointerDown: null,
        resize: null,
        hideTimer: null
      };
    }
    const clearHideTimer = () => {
      if (root._kpKeyHandlers.hideTimer != null) {
        try {
          clearTimeout(root._kpKeyHandlers.hideTimer);
        } catch {
        }
        root._kpKeyHandlers.hideTimer = null;
      }
    };
    const scheduleHide = () => {
      if (_pinnedActionId) return;
      clearHideTimer();
      root._kpKeyHandlers.hideTimer = setTimeout(() => {
        if (_pinnedActionId) return;
        hidePopover(pop);
        root._kpKeyHandlers.hideTimer = null;
      }, 60);
    };
    const showForKeyEl = (keyEl, { pinned = false } = {}) => {
      if (!keyEl || !keyEl.dataset?.kpActionId) return;
      try {
        if (keyEl.classList?.contains("kp-key-text-mode-disabled")) return;
      } catch {
      }
      const actionId = keyEl.dataset.kpActionId;
      const binding = resolveKeybinding(actionId, keybindings);
      if (!binding) return;
      clearHideTimer();
      if (pinned) {
        _pinnedActionId = actionId;
        _pinnedKeyEl = keyEl;
      }
      showPopoverForTarget({
        doc,
        pop,
        targetEl: keyEl,
        binding,
        actionId,
        pinned: pinned || _pinnedActionId === actionId
      });
      emitKeyboardHelpKeyHover({ actionId, keyEl });
    };
    const keyElements = root.querySelectorAll("[data-kp-action-id]");
    if (root._kpKeyHandlers.enter) {
      keyElements.forEach((keyEl) => {
        try {
          if (root._kpKeyHandlers.enter) keyEl.removeEventListener("pointerenter", root._kpKeyHandlers.enter);
          if (root._kpKeyHandlers.leave) keyEl.removeEventListener("pointerleave", root._kpKeyHandlers.leave);
          if (root._kpKeyHandlers.focusin) keyEl.removeEventListener("focusin", root._kpKeyHandlers.focusin);
          if (root._kpKeyHandlers.focusout) keyEl.removeEventListener("focusout", root._kpKeyHandlers.focusout);
          if (root._kpKeyHandlers.click) keyEl.removeEventListener("click", root._kpKeyHandlers.click);
        } catch {
        }
      });
    }
    function handleKeyEnter(e) {
      const keyEl = e.currentTarget;
      if (_pinnedActionId && keyEl?.dataset?.kpActionId !== _pinnedActionId) return;
      showForKeyEl(keyEl, { pinned: false });
    }
    function handleKeyLeave() {
      scheduleHide();
    }
    function handleKeyFocusIn(e) {
      showForKeyEl(e.currentTarget, { pinned: false });
    }
    function handleKeyFocusOut() {
      scheduleHide();
    }
    function handleKeyClick(e) {
      e.preventDefault();
      e.stopPropagation();
      if (e && e.isTrusted === false) return;
      const keyEl = e.currentTarget;
      const actionId = keyEl?.dataset?.kpActionId;
      if (_pinnedActionId && _pinnedKeyEl === keyEl && actionId === _pinnedActionId) {
        _pinnedActionId = null;
        _pinnedKeyEl = null;
        showForKeyEl(keyEl, { pinned: false });
        return;
      }
      showForKeyEl(keyEl, { pinned: true });
    }
    root._kpKeyHandlers.enter = handleKeyEnter;
    root._kpKeyHandlers.leave = handleKeyLeave;
    root._kpKeyHandlers.focusin = handleKeyFocusIn;
    root._kpKeyHandlers.focusout = handleKeyFocusOut;
    root._kpKeyHandlers.click = handleKeyClick;
    if (keyElements.length === 0) {
      console.warn("[KeyPilot] No key elements found for popover behavior in:", root);
    }
    keyElements.forEach((keyEl) => {
      keyEl.addEventListener("pointerenter", handleKeyEnter);
      keyEl.addEventListener("pointerleave", handleKeyLeave);
      keyEl.addEventListener("focusin", handleKeyFocusIn);
      keyEl.addEventListener("focusout", handleKeyFocusOut);
      keyEl.addEventListener("click", handleKeyClick);
    });
    if (!root._kpKeyHandlers.docKeydown) {
      let handleDocKeydown = function(e) {
        if (e.key === "Escape") hidePopover(pop);
      }, handleDocPointerDown = function(e) {
        if (!_pinnedActionId) return;
        const t = e.target;
        if (!(t instanceof Element)) return;
        if (containsComposed(pop, t) || getComposedEventElement(e, ".kp-keybindings-popover")) return;
        const keyInPath = getComposedEventElement(e, ".key");
        if (_pinnedKeyEl && (containsComposed(_pinnedKeyEl, t) || containsComposed(_pinnedKeyEl, keyInPath))) return;
        try {
          if (closestComposed(keyInPath || t, ".kp-action-config-panel")) return;
        } catch {
        }
        hidePopover(pop);
      }, handleResize = function() {
        hidePopover(pop);
      };
      root._kpKeyHandlers.docKeydown = handleDocKeydown;
      root._kpKeyHandlers.docPointerDown = handleDocPointerDown;
      root._kpKeyHandlers.resize = handleResize;
      doc.addEventListener("keydown", handleDocKeydown, true);
      doc.addEventListener("pointerdown", handleDocPointerDown, true);
      window.addEventListener("resize", handleResize);
    }
  }

  // src/ui/floating-keyboard-help.js
  init_constants();

  // src/ui/popup-theme-vars.js
  var POPUP_THEME_VARS = {
    "--fg": "#f8fafc",
    "--bg": "#0f172a",
    "--surface": "#1e293b",
    "--surface-light": "#334155",
    "--muted": "#94a3b8",
    "--ok": "#10b981",
    "--warn": "#f59e0b",
    "--err": "#44c7ef",
    "--brand": "#3b82f6",
    "--brand-dark": "#1e40af",
    "--border": "#334155",
    "--border2": "#4d617b"
  };
  function applyPopupThemeVars(targetEl) {
    if (!targetEl || !targetEl.style) return;
    try {
      for (const [k, v] of Object.entries(POPUP_THEME_VARS)) {
        targetEl.style.setProperty(k, v);
      }
    } catch {
    }
  }

  // src/ui/floating-keyboard-help.js
  init_settings_manager();
  init_keyboard_layouts();

  // src/ui/keyboard-layout-config-panel.js
  init_constants();
  init_keyboard_layouts();
  init_settings_manager();

  // src/ui/macro-key-editor.js
  function buildStrokeEditor(doc, stroke, onChange) {
    const row = doc.createElement("div");
    row.className = "kp-mk-stroke";
    const keyInput = doc.createElement("input");
    keyInput.type = "text";
    keyInput.className = "kp-cfg-field kp-mk-key-input";
    keyInput.placeholder = "Key";
    keyInput.value = stroke?.key || "";
    keyInput.setAttribute("aria-label", "Key");
    keyInput.addEventListener("input", () => {
      onChange(normalizeKeyStroke({
        ...stroke,
        key: keyInput.value
      }));
    }, true);
    const mkCheck = (name, checked) => {
      const lab = doc.createElement("label");
      lab.className = "kp-mk-mod";
      const cb = doc.createElement("input");
      cb.type = "checkbox";
      cb.checked = !!checked;
      cb.addEventListener("change", () => {
        onChange(normalizeKeyStroke({
          ...stroke,
          key: keyInput.value,
          ctrl: name === "ctrl" ? cb.checked : stroke?.ctrl,
          alt: name === "alt" ? cb.checked : stroke?.alt,
          shift: name === "shift" ? cb.checked : stroke?.shift,
          meta: name === "meta" ? cb.checked : stroke?.meta
        }));
      }, true);
      lab.appendChild(cb);
      lab.appendChild(doc.createTextNode(name === "meta" ? "Win" : name[0].toUpperCase() + name.slice(1)));
      return lab;
    };
    row.appendChild(mkCheck("ctrl", stroke?.ctrl));
    row.appendChild(mkCheck("alt", stroke?.alt));
    row.appendChild(mkCheck("shift", stroke?.shift));
    row.appendChild(mkCheck("meta", stroke?.meta));
    row.appendChild(keyInput);
    return row;
  }
  function createMacroKeyEditor(opts) {
    const doc = opts.doc || document;
    const mk = opts.macroKey;
    const kindDef = MACRO_KEY_KIND_DEFS.find((d) => d.id === mk.kind);
    let draft = {
      ...mk,
      config: normalizeMacroKeyConfig(mk.kind, mk.config)
    };
    const root = doc.createElement("div");
    root.className = "kp-mk-editor";
    root.setAttribute("data-kp-macro-key-editor", "true");
    const title = doc.createElement("div");
    title.className = "kp-mk-editor-title";
    title.textContent = `Configure: ${kindDef?.label || mk.kind}`;
    root.appendChild(title);
    const labelLab = doc.createElement("label");
    labelLab.className = "kp-mk-field-label";
    labelLab.textContent = "Name";
    const labelInput = doc.createElement("input");
    labelInput.type = "text";
    labelInput.className = "kp-cfg-field";
    labelInput.value = draft.label || "";
    labelInput.addEventListener("input", () => {
      draft = { ...draft, label: labelInput.value };
      opts.onChange?.(draft);
    }, true);
    root.appendChild(labelLab);
    root.appendChild(labelInput);
    const body = doc.createElement("div");
    body.className = "kp-mk-editor-body";
    root.appendChild(body);
    const emitConfig = (config) => {
      draft = { ...draft, config: normalizeMacroKeyConfig(mk.kind, config) };
      opts.onChange?.(draft);
      preview.textContent = formatPreview(draft);
    };
    const preview = doc.createElement("div");
    preview.className = "kp-mk-preview";
    preview.textContent = formatPreview(draft);
    if (mk.kind === "hotkey" || mk.kind === "key" || mk.kind === "continuous") {
      const strokeLab = doc.createElement("div");
      strokeLab.className = "kp-mk-field-label";
      strokeLab.textContent = mk.kind === "continuous" ? "Key to hold/repeat" : "Key / chord";
      body.appendChild(strokeLab);
      body.appendChild(buildStrokeEditor(doc, draft.config.stroke, (stroke) => {
        emitConfig({ ...draft.config, stroke });
      }));
      if (mk.kind === "continuous") {
        const intLab = doc.createElement("div");
        intLab.className = "kp-mk-field-label";
        intLab.textContent = "Interval (ms)";
        const intInput = doc.createElement("input");
        intInput.type = "number";
        intInput.className = "kp-cfg-field";
        intInput.min = "10";
        intInput.max = "5000";
        intInput.value = String(draft.config.intervalMs || 50);
        intInput.addEventListener("input", () => {
          emitConfig({ ...draft.config, intervalMs: Number(intInput.value) });
        }, true);
        body.appendChild(intLab);
        body.appendChild(intInput);
      }
    } else if (mk.kind === "burst" || mk.kind === "roundRobin") {
      const listKey = mk.kind === "burst" ? "steps" : "items";
      const listLab = doc.createElement("div");
      listLab.className = "kp-mk-field-label";
      listLab.textContent = mk.kind === "burst" ? "Sequence" : "Cycle items";
      body.appendChild(listLab);
      const listHost = doc.createElement("div");
      listHost.className = "kp-mk-stroke-list";
      body.appendChild(listHost);
      const redrawList = () => {
        listHost.replaceChildren();
        const arr = Array.isArray(draft.config[listKey]) ? draft.config[listKey] : [];
        arr.forEach((stroke, index) => {
          const wrap = doc.createElement("div");
          wrap.className = "kp-mk-stroke-row";
          const idx = doc.createElement("span");
          idx.className = "kp-mk-stroke-idx";
          idx.textContent = String(index + 1);
          wrap.appendChild(idx);
          wrap.appendChild(buildStrokeEditor(doc, stroke, (next) => {
            const copy = arr.slice();
            copy[index] = next;
            emitConfig({ ...draft.config, [listKey]: copy });
          }));
          const rm = doc.createElement("button");
          rm.type = "button";
          rm.className = "kp-cfg-btn";
          rm.textContent = "\xD7";
          rm.title = "Remove";
          rm.addEventListener("click", () => {
            const copy = arr.slice();
            copy.splice(index, 1);
            emitConfig({ ...draft.config, [listKey]: copy });
            redrawList();
          }, true);
          wrap.appendChild(rm);
          listHost.appendChild(wrap);
        });
      };
      redrawList();
      const addBtn = doc.createElement("button");
      addBtn.type = "button";
      addBtn.className = "kp-cfg-btn";
      addBtn.textContent = "Add stroke";
      addBtn.addEventListener("click", () => {
        const arr = Array.isArray(draft.config[listKey]) ? draft.config[listKey].slice() : [];
        arr.push(normalizeKeyStroke({ key: "" }));
        emitConfig({ ...draft.config, [listKey]: arr });
        redrawList();
      }, true);
      body.appendChild(addBtn);
      if (mk.kind === "burst") {
        const gapLab = doc.createElement("div");
        gapLab.className = "kp-mk-field-label";
        gapLab.textContent = "Gap between strokes (ms)";
        const gapInput = doc.createElement("input");
        gapInput.type = "number";
        gapInput.className = "kp-cfg-field";
        gapInput.min = "0";
        gapInput.max = "2000";
        gapInput.value = String(draft.config.gapMs ?? 40);
        gapInput.addEventListener("input", () => {
          emitConfig({ ...draft.config, gapMs: Number(gapInput.value) });
        }, true);
        body.appendChild(gapLab);
        body.appendChild(gapInput);
      }
      const origEmit = emitConfig;
    } else if (mk.kind === "mouse") {
      const lab = doc.createElement("div");
      lab.className = "kp-mk-field-label";
      lab.textContent = "Mouse button";
      body.appendChild(lab);
      const row = doc.createElement("div");
      row.className = "kp-mk-mouse-row";
      for (const button of ["left", "middle", "right"]) {
        const b = doc.createElement("button");
        b.type = "button";
        b.className = "kp-cfg-btn";
        b.textContent = button;
        b.setAttribute("aria-pressed", draft.config.button === button ? "true" : "false");
        b.addEventListener("click", () => {
          emitConfig({ button });
          row.querySelectorAll("button").forEach((el2) => {
            el2.setAttribute("aria-pressed", el2.textContent === button ? "true" : "false");
          });
        }, true);
        row.appendChild(b);
      }
      body.appendChild(row);
    }
    root.appendChild(preview);
    const actions = doc.createElement("div");
    actions.className = "kp-mk-editor-actions";
    const saveBtn = doc.createElement("button");
    saveBtn.type = "button";
    saveBtn.className = "kp-cfg-btn";
    saveBtn.textContent = "Save";
    saveBtn.addEventListener("click", () => {
      opts.onChange?.(draft);
      opts.onSave?.();
    }, true);
    const cancelBtn = doc.createElement("button");
    cancelBtn.type = "button";
    cancelBtn.className = "kp-cfg-btn";
    cancelBtn.textContent = "Cancel";
    cancelBtn.addEventListener("click", () => opts.onCancel?.(), true);
    actions.appendChild(saveBtn);
    actions.appendChild(cancelBtn);
    if (typeof opts.onDelete === "function") {
      const delBtn = doc.createElement("button");
      delBtn.type = "button";
      delBtn.className = "kp-cfg-btn";
      delBtn.textContent = "Delete";
      delBtn.addEventListener("click", () => opts.onDelete?.(), true);
      actions.appendChild(delBtn);
    }
    root.appendChild(actions);
    return root;
  }
  function formatPreview(mk) {
    const cfg = normalizeMacroKeyConfig(mk.kind, mk.config);
    if (mk.kind === "hotkey" || mk.kind === "key") return formatKeyStroke(cfg.stroke);
    if (mk.kind === "burst") return (cfg.steps || []).map(formatKeyStroke).join(" \u2192 ");
    if (mk.kind === "roundRobin") return (cfg.items || []).map(formatKeyStroke).join(" / ");
    if (mk.kind === "continuous") return `${formatKeyStroke(cfg.stroke)} every ${cfg.intervalMs}ms`;
    if (mk.kind === "mouse") return `${cfg.button} click`;
    return "";
  }

  // src/ui/hierarchical-table.js
  var DEFAULT_CLASS_NAMES = Object.freeze({
    wrap: "kp-hier-table-wrap",
    table: "kp-hier-table",
    rowGroup: "kp-hier-row-group",
    rowLeaf: "kp-hier-row-leaf",
    twisty: "kp-hier-twisty",
    labelCell: "kp-hier-label-cell",
    labelText: "kp-hier-label-text"
  });
  function getHierarchicalTableCss(opts = {}) {
    const root = typeof opts.rootSelector === "string" && opts.rootSelector.trim() ? opts.rootSelector.trim() : "";
    const p = root ? `${root} ` : "";
    return `
${p}.kp-hier-table-wrap {
  width: 100%;
  overflow: auto;
  border: 1px solid rgba(120, 140, 100, 0.12);
  border-radius: 2px;
  background: rgba(8, 10, 8, 0.35);
}
${p}.kp-hier-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 11px;
  table-layout: fixed;
}
${p}.kp-hier-table th {
  text-align: left;
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #8a9a78;
  padding: 6px 8px;
  border-bottom: 1px solid rgba(120, 140, 100, 0.18);
  background: #121410;
  position: sticky;
  top: 0;
  z-index: 1;
}
${p}.kp-hier-table td {
  padding: 5px 8px;
  border-bottom: 1px solid rgba(255,255,255,0.04);
  vertical-align: middle;
  color: #c8d4e0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
${p}.kp-hier-table tr.kp-hier-row-group {
  background: rgba(255,255,255,0.03);
  cursor: pointer;
}
${p}.kp-hier-table tr.kp-hier-row-group td {
  font-weight: 700;
  font-size: 10px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #9a8aaa;
}
${p}.kp-hier-table tr.kp-hier-row-leaf:hover {
  background: rgba(74, 144, 200, 0.12);
}
${p}.kp-hier-twisty {
  appearance: none;
  width: 18px;
  height: 18px;
  padding: 0;
  margin: 0 4px 0 0;
  border: 0;
  background: transparent;
  color: #9aacbe;
  cursor: pointer;
  font-size: 10px;
  line-height: 18px;
  flex: 0 0 auto;
}
${p}.kp-hier-label-cell {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
${p}.kp-hier-label-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
`.trim();
  }
  function resolveClassNames(overrides = {}) {
    return {
      wrap: overrides.wrap || DEFAULT_CLASS_NAMES.wrap,
      table: overrides.table || DEFAULT_CLASS_NAMES.table,
      rowGroup: overrides.rowGroup || DEFAULT_CLASS_NAMES.rowGroup,
      rowLeaf: overrides.rowLeaf || DEFAULT_CLASS_NAMES.rowLeaf,
      twisty: overrides.twisty || DEFAULT_CLASS_NAMES.twisty,
      labelCell: overrides.labelCell || DEFAULT_CLASS_NAMES.labelCell,
      labelText: overrides.labelText || DEFAULT_CLASS_NAMES.labelText
    };
  }
  function fillCell(doc, cell, content) {
    if (content == null || content === "") {
      cell.textContent = "";
      return;
    }
    if (typeof content === "string" || typeof content === "number") {
      cell.textContent = String(content);
      return;
    }
    if (content instanceof Node) {
      cell.appendChild(content);
      return;
    }
    cell.textContent = String(content);
  }
  function createHierarchicalLabelCell(opts = {}) {
    const doc = opts.doc || document;
    const cn = resolveClassNames(opts.classNames);
    const depth = Math.max(0, Number(opts.depth) || 0);
    const indentPx = Number.isFinite(opts.indentPx) ? Number(opts.indentPx) : 14;
    const cell = doc.createElement("div");
    cell.className = cn.labelCell;
    cell.style.paddingLeft = `${depth * indentPx}px`;
    if (opts.leading instanceof HTMLElement) {
      cell.appendChild(opts.leading);
    }
    const text = doc.createElement("span");
    text.className = cn.labelText;
    text.textContent = opts.text != null ? String(opts.text) : "";
    cell.appendChild(text);
    return cell;
  }
  function createHierarchicalTable(config = {}) {
    const doc = config.doc || document;
    const columns = Array.isArray(config.columns) ? config.columns.filter(Boolean) : [];
    if (!columns.length) {
      throw new Error("createHierarchicalTable: columns required");
    }
    const cn = resolveClassNames(config.classNames);
    const indentPx = Number.isFinite(config.indentPx) ? Number(config.indentPx) : 14;
    const isGroupExpanded = typeof config.isGroupExpanded === "function" ? config.isGroupExpanded : () => true;
    const onToggleGroup = typeof config.onToggleGroup === "function" ? config.onToggleGroup : () => {
    };
    const wrap = doc.createElement("div");
    wrap.className = [cn.wrap, config.wrapClassName].filter(Boolean).join(" ");
    const table = doc.createElement("table");
    table.className = [cn.table, config.tableClassName].filter(Boolean).join(" ");
    if (config.ariaLabel) table.setAttribute("aria-label", config.ariaLabel);
    const colgroup = doc.createElement("colgroup");
    for (const col of columns) {
      const el2 = doc.createElement("col");
      if (col.className) el2.className = col.className;
      if (col.width) el2.style.width = String(col.width);
      colgroup.appendChild(el2);
    }
    table.appendChild(colgroup);
    const thead = doc.createElement("thead");
    const hr = doc.createElement("tr");
    for (const col of columns) {
      const th = doc.createElement("th");
      th.textContent = col.label != null ? String(col.label) : "";
      hr.appendChild(th);
    }
    thead.appendChild(hr);
    table.appendChild(thead);
    const tbody = doc.createElement("tbody");
    table.appendChild(tbody);
    wrap.appendChild(table);
    const createLabelCell = (opts = {}) => createHierarchicalLabelCell({
      doc,
      depth: opts.depth,
      indentPx,
      leading: opts.leading,
      text: opts.text,
      classNames: cn
    });
    const appendGroupRow = (opts) => {
      const groupKey = String(opts.groupKey || "");
      const label = opts.label != null ? String(opts.label) : "";
      const depth = Math.max(0, Number(opts.depth) || 0);
      const count = Number(opts.count) || 0;
      const expanded = !!isGroupExpanded(groupKey);
      const tr = doc.createElement("tr");
      tr.className = [cn.rowGroup, opts.className].filter(Boolean).join(" ");
      tr.dataset.kpGroupKey = groupKey;
      if (opts.dataset) {
        for (const [k, v] of Object.entries(opts.dataset)) {
          if (v != null) tr.dataset[k] = String(v);
        }
      }
      const tdLabel = doc.createElement("td");
      const twisty = doc.createElement("button");
      twisty.type = "button";
      twisty.className = cn.twisty;
      twisty.setAttribute("aria-expanded", expanded ? "true" : "false");
      twisty.setAttribute("aria-label", expanded ? `Collapse ${label}` : `Expand ${label}`);
      twisty.textContent = expanded ? "\u25BE" : "\u25B8";
      twisty.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        onToggleGroup(groupKey);
      }, true);
      tdLabel.appendChild(createLabelCell({
        depth,
        leading: twisty,
        text: count > 0 ? `${label} (${count})` : label
      }));
      tr.appendChild(tdLabel);
      const extra = Array.isArray(opts.cells) ? opts.cells : [];
      for (let i = 1; i < columns.length; i++) {
        const td = doc.createElement("td");
        const isLast = i === columns.length - 1;
        if (isLast && opts.trailing instanceof HTMLElement) {
          td.appendChild(opts.trailing);
        } else {
          fillCell(doc, td, extra[i - 1]);
        }
        tr.appendChild(td);
      }
      tr.addEventListener("click", (e) => {
        if (e.target?.closest?.("button, a, input, select, textarea")) return;
        onToggleGroup(groupKey);
      }, true);
      tbody.appendChild(tr);
      return expanded;
    };
    const appendLeafRow = (opts) => {
      const tr = doc.createElement("tr");
      tr.className = [cn.rowLeaf, opts.className].filter(Boolean).join(" ");
      if (opts.title) tr.title = String(opts.title);
      if (opts.dataset) {
        for (const [k, v] of Object.entries(opts.dataset)) {
          if (v != null) tr.dataset[k] = String(v);
        }
      }
      const cells = Array.isArray(opts.cells) ? opts.cells : [];
      const depth = opts.depth;
      for (let i = 0; i < columns.length; i++) {
        const td = doc.createElement("td");
        let content = cells[i];
        if (i === 0 && depth != null && (typeof content === "string" || content == null)) {
          content = createLabelCell({
            depth,
            text: content != null ? String(content) : ""
          });
        }
        fillCell(doc, td, content);
        tr.appendChild(td);
      }
      if (typeof opts.onClick === "function") {
        tr.addEventListener("click", (e) => {
          if (e.target?.closest?.("button, a, input, select, textarea")) return;
          opts.onClick(e);
        }, true);
      }
      tbody.appendChild(tr);
      return tr;
    };
    const appendHintRow = (text, depth = 1) => {
      const tr = doc.createElement("tr");
      tr.className = cn.rowLeaf;
      const td = doc.createElement("td");
      td.colSpan = columns.length;
      td.style.paddingLeft = `${8 + Math.max(0, depth) * indentPx}px`;
      td.style.color = "#6a7a58";
      td.style.whiteSpace = "normal";
      td.textContent = text != null ? String(text) : "";
      tr.appendChild(td);
      tbody.appendChild(tr);
      return tr;
    };
    return {
      root: wrap,
      wrap,
      table,
      thead,
      tbody,
      columns,
      createLabelCell,
      appendGroupRow,
      appendLeafRow,
      appendHintRow
    };
  }

  // src/ui/onboarding-shared.js
  var ONBOARDING_STORAGE_KEYS = {
    ACTIVE: "keypilot_onboarding_active",
    PROGRESS: "keypilot_onboarding_progress"
  };
  var ONBOARDING_PANEL_CLASS = "kp-onboarding-panel";
  var ONBOARDING_DEFAULT_TITLE = "Welcome to KeyPilot";
  var ONBOARDING_REOPEN_TIP = "Tip: Press Alt + I to re-open this walkthrough later.";
  var ONBOARDING_PANEL_Z_FALLBACK = 2147483026;
  var ONBOARDING_PANEL_SCALE = 1.1;
  var ONBOARDING_DEFAULT_LEFT_PX = 16;
  var ONBOARDING_DEFAULT_TOP_PX = 16;
  var ONBOARDING_METAL_SPECULAR = "linear-gradient(180deg, rgba(255,255,255,0.38) 0%, rgba(255,255,255,0.08) 28%, transparent 55%)";
  var ONBOARDING_METAL = {
    fg: "#1c1c1c",
    fgDim: "rgba(28,28,28,0.72)",
    fgMute: "rgba(28,28,28,0.55)",
    panelBg: `${ONBOARDING_METAL_SPECULAR}, linear-gradient(180deg, #9a9a9a 0%, #838383 48%, #707070 100%)`,
    titlebarBg: `${ONBOARDING_METAL_SPECULAR}, linear-gradient(180deg, #b0b0b0 0%, #929292 45%, #787878 100%)`,
    footerBg: "linear-gradient(180deg, #8a8a8a 0%, #767676 100%)",
    btnBg: "linear-gradient(180deg, #c2c2c2 0%, #9e9e9e 50%, #868686 100%)",
    panelBorder: "1px solid rgba(42,52,62,0.92)",
    panelShadow: "0 0 0 1px rgba(255,255,255,0.28) inset, 0 0 0 1px rgba(190,190,190,0.48), 0 0 10px rgba(255,255,255,0.12), 0 16px 40px rgba(0,0,0,0.45)",
    titlebarBorder: "1px solid #4a4a4a",
    titlebarShadow: "0 1px 0 rgba(255,255,255,0.35)",
    footerBorder: "1px solid rgba(0,0,0,0.28)",
    btnBorder: "1px solid #4a4a4a",
    btnShadow: "0 1px 0 rgba(255,255,255,0.40) inset, 0 -1px 0 rgba(0,0,0,0.18) inset",
    rowBg: "rgba(255,255,255,0.18)",
    rowBorder: "1px solid rgba(0,0,0,0.18)",
    rowDoneBg: "rgba(46, 204, 113, 0.22)",
    checkBorder: "1px solid rgba(0,0,0,0.35)",
    kbdBg: "linear-gradient(180deg, #e4e4e4 0%, #c8c8c8 45%, #b0b0b0 55%, #9a9a9a 100%)",
    kbdBorder: "1px solid #3d3d3d",
    kbdColor: "#141414",
    kbdShadow: "0 1px 0 rgba(255,255,255,0.72) inset, 0 -1px 0 rgba(0,0,0,0.28) inset, 0 1px 2px rgba(0,0,0,0.32)"
  };
  var ONBOARDING_STRIP_TOP_PX = 16;
  var ONBOARDING_STRIP_HEIGHT_PX = 28;
  var ONBOARDING_BELOW_STRIP_GAP_PX = 8;
  function getOnboardingTopBelowControlStrip() {
    try {
      const strip = document.querySelector(".kp-control-strip");
      if (strip && strip.isConnected) {
        const cs = typeof window.getComputedStyle === "function" ? window.getComputedStyle(strip) : null;
        const hidden = strip.hidden === true || strip.getAttribute("hidden") !== null || strip.style && strip.style.display === "none" || cs && (cs.display === "none" || cs.visibility === "hidden");
        if (!hidden) {
          const rect = strip.getBoundingClientRect();
          if (rect && rect.height > 0) {
            return Math.max(
              ONBOARDING_DEFAULT_TOP_PX,
              Math.round(rect.bottom + ONBOARDING_BELOW_STRIP_GAP_PX)
            );
          }
        }
      }
    } catch {
    }
    return ONBOARDING_STRIP_TOP_PX + ONBOARDING_STRIP_HEIGHT_PX + ONBOARDING_BELOW_STRIP_GAP_PX;
  }
  function positionOnboardingBelowControlStrip(root) {
    if (!root) return;
    try {
      root.style.top = `${getOnboardingTopBelowControlStrip()}px`;
    } catch {
    }
  }
  function createEmptyProgress(slideId = null) {
    return {
      slideId: slideId || null,
      completedTaskIds: [],
      onEnterDoneSlideIds: [],
      completed: false,
      timestamp: Date.now()
    };
  }
  function cloneProgress(progress) {
    const p = progress && typeof progress === "object" ? progress : {};
    return {
      slideId: typeof p.slideId === "string" ? p.slideId : null,
      completedTaskIds: Array.isArray(p.completedTaskIds) ? p.completedTaskIds.map(String) : [],
      onEnterDoneSlideIds: Array.isArray(p.onEnterDoneSlideIds) ? p.onEnterDoneSlideIds.map(String) : [],
      completed: !!p.completed,
      timestamp: typeof p.timestamp === "number" ? p.timestamp : Date.now()
    };
  }
  function arraysEqualString(a, b) {
    const aa = Array.isArray(a) ? a : [];
    const bb = Array.isArray(b) ? b : [];
    if (aa.length !== bb.length) return false;
    for (let i = 0; i < aa.length; i++) {
      if (String(aa[i]) !== String(bb[i])) return false;
    }
    return true;
  }
  function progressEqual(a, b) {
    const aa = cloneProgress(a);
    const bb = cloneProgress(b);
    return aa.slideId === bb.slideId && aa.completed === bb.completed && aa.timestamp === bb.timestamp && arraysEqualString(aa.completedTaskIds, bb.completedTaskIds) && arraysEqualString(aa.onEnterDoneSlideIds, bb.onEnterDoneSlideIds);
  }
  function isSlideComplete(slide, completedTaskIds) {
    const tasks = slide?.tasks || [];
    if (!tasks.length) return true;
    const set = completedTaskIds instanceof Set ? completedTaskIds : new Set(Array.isArray(completedTaskIds) ? completedTaskIds.map(String) : []);
    for (const t of tasks) {
      if (!t?.id) continue;
      if (!set.has(String(t.id))) return false;
    }
    return true;
  }
  function renderKeyboardKeysInto(el2, text) {
    if (!el2) return;
    try {
      el2.textContent = "";
    } catch {
    }
    const parts = String(text || "").split(/`/);
    for (let i = 0; i < parts.length; i++) {
      const part = parts[i];
      if (!part) continue;
      if (i % 2 === 1) {
        const k = document.createElement("kbd");
        k.textContent = part;
        el2.appendChild(k);
      } else {
        el2.appendChild(document.createTextNode(part));
      }
    }
  }
  function getOnboardingPanelCss(opts = {}) {
    const includeVt = opts.includeViewTransitions !== false;
    const s = (sel) => `:host ${sel}, .${ONBOARDING_PANEL_CLASS} ${sel}`;
    let css = `${s("kbd")} {
        display: inline-block;
        font-family: var(--kp-font-kbd, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
        font-size: var(--kp-type-kbd-size, 11px);
        font-weight: var(--kp-type-weight-bold, 700);
        line-height: 1.2;
        letter-spacing: 0.02em;
        padding: 2px 7px;
        margin: 0 1px;
        border: var(--kp-kbd-border, ${ONBOARDING_METAL.kbdBorder});
        border-radius: var(--kp-radius-sm, 4px);
        background: var(--kp-kbd-bg, ${ONBOARDING_METAL.kbdBg});
        color: var(--kp-color-kbd-fg, ${ONBOARDING_METAL.kbdColor});
        box-shadow: var(--kp-kbd-shadow, ${ONBOARDING_METAL.kbdShadow});
        vertical-align: baseline;
        white-space: nowrap;
      }
      ${s('[data-kp-onboarding-overlay="true"]')} {
        background: rgba(0, 0, 0, 0.35);
        backdrop-filter: blur(6px);
        -webkit-backdrop-filter: blur(6px);
      }
      ${s('[data-kp-onboarding-overlay="true"] > div')} {
        border-radius: 3px;
        border: ${ONBOARDING_METAL.panelBorder};
        background: ${ONBOARDING_METAL.panelBg};
        box-shadow: ${ONBOARDING_METAL.panelShadow};
        color: ${ONBOARDING_METAL.fg};
        font-family: Helvetica, Arial, sans-serif;
      }
      ${s('[data-kp-onboarding-overlay-title="true"]')} {
        color: ${ONBOARDING_METAL.fg};
        font-weight: 800;
      }
      ${s('[data-kp-onboarding-overlay-message="true"]')} {
        color: ${ONBOARDING_METAL.fgDim};
      }
      ${s('button[data-kp-onboarding-overlay-secondary="true"]')} {
        border-radius: 2px;
        border: ${ONBOARDING_METAL.btnBorder};
        background: ${ONBOARDING_METAL.btnBg};
        box-shadow: ${ONBOARDING_METAL.btnShadow};
        color: ${ONBOARDING_METAL.fg};
        font-weight: 700;
      }
      ${s('button[data-kp-onboarding-overlay-primary="true"]')} {
        border-radius: 2px;
        border: 1px solid #2a4a66;
        background: linear-gradient(180deg, #5a7a9a 0%, #3a5570 100%);
        box-shadow: 0 1px 0 rgba(255,255,255,0.28) inset, 0 -1px 0 rgba(0,0,0,0.28) inset;
        color: #e8f0f8;
        font-weight: 700;
      }
      /* Next incomplete checklist row \u2014 same light-blue glow language as the toggle-off arrow. */
      @keyframes kp-onboarding-next-task-glow {
        0%, 100% {
          box-shadow:
            0 0 0 1px rgba(33, 150, 243, 0.40),
            0 0 10px rgba(120, 210, 255, 0.45),
            0 0 18px rgba(120, 210, 255, 0.25);
        }
        50% {
          box-shadow:
            0 0 0 1px rgba(33, 150, 243, 0.70),
            0 0 16px rgba(120, 210, 255, 0.75),
            0 0 32px rgba(120, 210, 255, 0.45);
        }
      }
      ${s('[data-kp-onboarding-task-next="true"]')} {
        animation: kp-onboarding-next-task-glow 1.5s ease-in-out infinite;
        will-change: box-shadow;
      }
      /* Brief flash + check pop when a checklist step completes. */
      @keyframes kp-onboarding-check-flash {
        0% {
          transform: scale(0.55);
          filter: brightness(2);
          box-shadow: 0 0 0 0 rgba(46, 204, 113, 0.95);
        }
        45% {
          transform: scale(1.28);
          filter: brightness(1.45);
          box-shadow:
            0 0 0 5px rgba(46, 204, 113, 0.40),
            0 0 16px rgba(46, 204, 113, 0.75);
        }
        100% {
          transform: scale(1);
          filter: brightness(1);
          box-shadow: 0 0 0 2px rgba(46, 204, 113, 0.18);
        }
      }
      @keyframes kp-onboarding-check-pop {
        0% { opacity: 0; transform: scale(0.15); }
        55% { opacity: 1; transform: scale(1.2); }
        100% { opacity: 1; transform: scale(1); }
      }
      ${s(".kp-onboarding-check-flash")} {
        animation: kp-onboarding-check-flash 420ms cubic-bezier(0.2, 0.9, 0.25, 1.15) both;
        will-change: transform, box-shadow, filter;
      }
      ${s(".kp-onboarding-check-flash > div")} {
        animation: kp-onboarding-check-pop 380ms cubic-bezier(0.2, 0.9, 0.25, 1.1) both;
      }
      @media (prefers-reduced-motion: reduce) {
        ${s('[data-kp-onboarding-task-next="true"]')} {
          animation: none;
        }
        ${s(".kp-onboarding-check-flash")},
        ${s(".kp-onboarding-check-flash > div")} {
          animation: none;
        }
      }`;
    if (includeVt) {
      css += `
      @keyframes kpOnboardingSlideOut {
        to { transform: translateX(calc(var(--kp-onboarding-slide-dir, 1) * -100%)); opacity: 0.0; }
      }
      @keyframes kpOnboardingSlideIn {
        from { transform: translateX(calc(var(--kp-onboarding-slide-dir, 1) * 100%)); opacity: 0.0; }
        to { transform: translateX(0%); opacity: 1.0; }
      }
      ::view-transition-old(kp-onboarding-slide-surface) {
        animation: kpOnboardingSlideOut 220ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
      }
      ::view-transition-new(kp-onboarding-slide-surface) {
        animation: kpOnboardingSlideIn 220ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
      }`;
    }
    return css;
  }
  function assignStyle(el2, styles) {
    if (!el2 || !styles) return;
    try {
      Object.assign(el2.style, styles);
    } catch {
    }
  }
  function mkIconBtn(doc, label, dataAttr, aria) {
    const b = doc.createElement("button");
    b.type = "button";
    b.textContent = label;
    b.setAttribute(dataAttr, "true");
    if (aria) b.setAttribute("aria-label", aria);
    assignStyle(b, {
      width: "28px",
      height: "28px",
      borderRadius: "2px",
      border: ONBOARDING_METAL.btnBorder,
      background: ONBOARDING_METAL.btnBg,
      boxShadow: ONBOARDING_METAL.btnShadow,
      color: ONBOARDING_METAL.fg,
      cursor: "pointer",
      fontSize: "14px",
      fontWeight: "700",
      lineHeight: "26px",
      padding: "0",
      display: "inline-flex",
      alignItems: "center",
      justifyContent: "center"
    });
    return b;
  }
  function createOnboardingShell(doc, opts = {}) {
    const zIndex = opts.zIndex != null ? opts.zIndex : ONBOARDING_PANEL_Z_FALLBACK;
    const initiallyHidden = opts.initiallyHidden !== false;
    const includeVt = opts.includeViewTransitions !== false;
    const root = doc.createElement("div");
    root.className = ONBOARDING_PANEL_CLASS;
    root.hidden = initiallyHidden;
    if (opts.early) root.dataset.kpEarlyOnboarding = "true";
    try {
      root.setAttribute("data-kp-surface", "onboarding");
    } catch {
    }
    root.setAttribute("role", "dialog");
    root.setAttribute("aria-label", "KeyPilot onboarding walkthrough");
    try {
      root.setAttribute("data-kp-ui-shadow", "onboarding");
    } catch {
    }
    let shell = root;
    try {
      shell = root.shadowRoot || root.attachShadow({ mode: "open" });
    } catch {
    }
    assignStyle(root, {
      position: "fixed",
      left: `${ONBOARDING_DEFAULT_LEFT_PX}px`,
      // Prefer sitting below the control strip so the strip is not pushed down.
      top: `${getOnboardingTopBelowControlStrip()}px`,
      width: "360px",
      maxWidth: "calc(100vw - 24px)",
      maxHeight: "calc(100vh - 24px)",
      display: initiallyHidden ? "none" : "flex",
      flexDirection: "column",
      overflow: "hidden",
      zIndex: String(zIndex),
      background: "var(--kp-panel-bg)",
      color: "var(--kp-color-fg)",
      border: "var(--kp-panel-border)",
      borderRadius: "var(--kp-radius-panel, 3px)",
      boxShadow: "var(--kp-panel-shadow)",
      fontFamily: "var(--kp-font-ui, Helvetica, Arial, sans-serif)",
      pointerEvents: initiallyHidden ? "none" : "auto",
      zoom: String(ONBOARDING_PANEL_SCALE)
    });
    try {
      opts.applyTheme?.(root);
    } catch {
    }
    const style = doc.createElement("style");
    style.textContent = getOnboardingPanelCss({ includeViewTransitions: includeVt });
    shell.appendChild(style);
    const header = doc.createElement("div");
    assignStyle(header, {
      display: "flex",
      alignItems: "center",
      justifyContent: "space-between",
      gap: "10px",
      padding: "10px 12px",
      background: ONBOARDING_METAL.titlebarBg,
      borderBottom: ONBOARDING_METAL.titlebarBorder,
      boxShadow: ONBOARDING_METAL.titlebarShadow
    });
    const titleWrap = doc.createElement("div");
    assignStyle(titleWrap, {
      display: "flex",
      flexDirection: "column",
      gap: "2px",
      minWidth: "0"
    });
    const titleEl = doc.createElement("div");
    titleEl.textContent = String(opts.title || ONBOARDING_DEFAULT_TITLE);
    titleEl.setAttribute("data-kp-onboarding-title", "true");
    assignStyle(titleEl, {
      fontSize: "13px",
      fontWeight: "800",
      letterSpacing: "0.2px",
      color: ONBOARDING_METAL.fg,
      whiteSpace: "nowrap",
      overflow: "hidden",
      textOverflow: "ellipsis"
    });
    const stepEl = doc.createElement("div");
    stepEl.textContent = String(opts.stepText || "1 / 1");
    stepEl.setAttribute("data-kp-onboarding-step", "true");
    assignStyle(stepEl, {
      fontSize: "12px",
      fontWeight: "600",
      color: ONBOARDING_METAL.fgDim
    });
    titleWrap.appendChild(titleEl);
    const navWrap = doc.createElement("div");
    assignStyle(navWrap, {
      display: "inline-flex",
      alignItems: "center",
      gap: "8px",
      flex: "0 0 auto"
    });
    const stepWrap = doc.createElement("div");
    assignStyle(stepWrap, {
      display: "inline-flex",
      alignItems: "center",
      gap: "6px"
    });
    const prevBtn = mkIconBtn(doc, "\u2190", "data-kp-onboarding-prev", "Previous slide");
    const nextBtn = mkIconBtn(doc, "\u2192", "data-kp-onboarding-next", "Next slide");
    if (opts.navDisabled) {
      prevBtn.disabled = true;
      nextBtn.disabled = true;
    }
    stepWrap.appendChild(prevBtn);
    stepWrap.appendChild(stepEl);
    stepWrap.appendChild(nextBtn);
    const resetBtn = doc.createElement("button");
    resetBtn.type = "button";
    resetBtn.textContent = "Reset";
    resetBtn.setAttribute("data-kp-onboarding-reset", "true");
    assignStyle(resetBtn, {
      height: "28px",
      borderRadius: "2px",
      border: ONBOARDING_METAL.btnBorder,
      background: ONBOARDING_METAL.btnBg,
      boxShadow: ONBOARDING_METAL.btnShadow,
      color: ONBOARDING_METAL.fg,
      cursor: "pointer",
      fontSize: "12px",
      fontWeight: "700",
      padding: "0 10px",
      lineHeight: "26px"
    });
    const closeBtn = doc.createElement("button");
    closeBtn.type = "button";
    closeBtn.textContent = "\xD7";
    closeBtn.setAttribute("aria-label", "Close onboarding walkthrough");
    closeBtn.setAttribute("data-kp-onboarding-close", "true");
    assignStyle(closeBtn, {
      width: "28px",
      height: "28px",
      borderRadius: "2px",
      border: ONBOARDING_METAL.btnBorder,
      background: ONBOARDING_METAL.btnBg,
      boxShadow: ONBOARDING_METAL.btnShadow,
      color: ONBOARDING_METAL.fg,
      cursor: "pointer",
      fontSize: "18px",
      fontWeight: "700",
      lineHeight: "26px",
      padding: "0",
      flex: "0 0 auto"
    });
    navWrap.appendChild(closeBtn);
    header.appendChild(titleWrap);
    header.appendChild(navWrap);
    const body = doc.createElement("div");
    body.setAttribute("data-kp-onboarding-body", "true");
    assignStyle(body, {
      flex: "1",
      overflowY: "auto",
      minHeight: "0",
      position: "relative"
    });
    const slideSurface = doc.createElement("div");
    slideSurface.setAttribute("data-kp-onboarding-slide-surface", "true");
    assignStyle(slideSurface, { padding: "12px" });
    body.appendChild(slideSurface);
    const footer = doc.createElement("div");
    assignStyle(footer, {
      display: "flex",
      alignItems: "center",
      justifyContent: "space-between",
      gap: "10px",
      padding: "10px 12px",
      background: ONBOARDING_METAL.footerBg,
      borderTop: ONBOARDING_METAL.footerBorder
    });
    footer.appendChild(stepWrap);
    footer.appendChild(resetBtn);
    shell.appendChild(header);
    shell.appendChild(body);
    shell.appendChild(footer);
    return {
      root,
      body,
      slideSurface,
      titleEl,
      stepEl,
      prevBtn,
      nextBtn,
      resetBtn,
      closeBtn
    };
  }
  function queryOnboardingShellRefs(root) {
    if (!root) return null;
    const shell = root.shadowRoot || root;
    const body = shell.querySelector('[data-kp-onboarding-body="true"]') || shell.querySelector(":scope > div[data-kp-onboarding-body]");
    if (!body) return null;
    let slideSurface = body.querySelector('[data-kp-onboarding-slide-surface="true"]');
    if (!slideSurface) {
      slideSurface = document.createElement("div");
      slideSurface.setAttribute("data-kp-onboarding-slide-surface", "true");
      assignStyle(slideSurface, { padding: "12px" });
      try {
        body.appendChild(slideSurface);
      } catch {
      }
    }
    return {
      root,
      body,
      slideSurface,
      titleEl: shell.querySelector('[data-kp-onboarding-title="true"]'),
      stepEl: shell.querySelector('[data-kp-onboarding-step="true"]'),
      prevBtn: shell.querySelector('button[data-kp-onboarding-prev="true"]'),
      nextBtn: shell.querySelector('button[data-kp-onboarding-next="true"]'),
      resetBtn: shell.querySelector('button[data-kp-onboarding-reset="true"]'),
      closeBtn: shell.querySelector('button[data-kp-onboarding-close="true"]')
    };
  }
  function setOnboardingPanelVisible(root, visible) {
    if (!root) return;
    const show = !!visible;
    try {
      root.hidden = !show;
      root.style.display = show ? "flex" : "none";
      root.style.pointerEvents = show ? "auto" : "none";
      root.style.zoom = String(ONBOARDING_PANEL_SCALE);
      if (show) positionOnboardingBelowControlStrip(root);
    } catch {
    }
  }
  function applyTaskRowInteractive(row, { uncheckable, onTaskRowClick }) {
    if (!row) return;
    try {
      if (uncheckable) {
        row.setAttribute("data-kp-onboarding-uncheckable", "true");
        row.style.cursor = "pointer";
        row.title = "Click to uncheck (undo last completed step)";
        row.setAttribute("role", "button");
        row.tabIndex = 0;
      } else {
        row.removeAttribute("data-kp-onboarding-uncheckable");
        row.style.cursor = "";
        row.removeAttribute("title");
        row.removeAttribute("role");
        row.removeAttribute("tabindex");
      }
    } catch {
    }
    try {
      if (row._kpOnboardingTaskClick) {
        row.removeEventListener("click", row._kpOnboardingTaskClick);
        row._kpOnboardingTaskClick = null;
      }
    } catch {
    }
    if (uncheckable && typeof onTaskRowClick === "function") {
      row._kpOnboardingTaskClick = onTaskRowClick;
      try {
        row.addEventListener("click", onTaskRowClick);
      } catch {
      }
    }
  }
  var NEXT_TASK_BORDER = "1px solid rgba(120, 210, 255, 0.65)";
  var NEXT_TASK_BG = "rgb(231, 231, 231)";
  var NEXT_TASK_GLOW = "0 0 0 1px rgba(33, 150, 243, 0.45), 0 0 12px rgba(120, 210, 255, 0.55), 0 0 24px rgba(120, 210, 255, 0.35)";
  function playCheckboxCompleteFlash(box) {
    if (!box) return;
    try {
      box.classList.remove("kp-onboarding-check-flash");
      void box.offsetWidth;
      box.classList.add("kp-onboarding-check-flash");
    } catch {
    }
  }
  function applyTaskRowVisual(row, task, done, opts = {}) {
    if (!row) return;
    const isNext = !!(!done && opts.isNext);
    let wasDone = false;
    try {
      wasDone = row.getAttribute("data-kp-onboarding-task-done") === "true";
    } catch {
    }
    try {
      if (isNext) row.setAttribute("data-kp-onboarding-task-next", "true");
      else row.removeAttribute("data-kp-onboarding-task-next");
    } catch {
    }
    try {
      if (done) row.setAttribute("data-kp-onboarding-task-done", "true");
      else row.removeAttribute("data-kp-onboarding-task-done");
    } catch {
    }
    assignStyle(row, {
      background: done ? ONBOARDING_METAL.rowDoneBg : isNext ? NEXT_TASK_BG : ONBOARDING_METAL.rowBg,
      border: isNext ? NEXT_TASK_BORDER : ONBOARDING_METAL.rowBorder,
      boxShadow: isNext ? NEXT_TASK_GLOW : "none"
    });
    const box = row.querySelector(':scope > div[aria-hidden="true"]') || row.firstElementChild;
    const textEl = box && box.nextElementSibling || row.querySelector(":scope > div:last-child");
    if (box) {
      try {
        box.classList.remove("kp-onboarding-check-flash");
      } catch {
      }
      assignStyle(box, {
        border: done ? "1px solid rgba(46, 204, 113, 0.9)" : isNext ? "1px solid rgba(120, 210, 255, 0.75)" : ONBOARDING_METAL.checkBorder,
        background: done ? "rgba(46, 204, 113, 0.85)" : "rgba(255,255,255,0.25)",
        boxShadow: done ? "0 0 0 2px rgba(46, 204, 113, 0.18)" : isNext ? "0 0 0 2px rgba(33, 150, 243, 0.28)" : "none"
      });
      try {
        const existingCheck = box.querySelector(":scope > div");
        if (done) {
          if (!existingCheck) {
            const check = document.createElement("div");
            check.textContent = "\u2713";
            assignStyle(check, {
              position: "absolute",
              inset: "0",
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              fontSize: "13px",
              fontWeight: "800",
              color: "#0b1410"
            });
            box.appendChild(check);
          }
        } else if (existingCheck) {
          box.removeChild(existingCheck);
        }
      } catch {
      }
      if (done && !wasDone) {
        playCheckboxCompleteFlash(box);
      }
    }
    if (textEl) {
      try {
        renderKeyboardKeysInto(textEl, task.label || task.id);
        assignStyle(textEl, {
          color: done ? ONBOARDING_METAL.fgDim : ONBOARDING_METAL.fg,
          opacity: done ? "0.95" : "1"
        });
      } catch {
      }
    }
    applyTaskRowInteractive(row, {
      uncheckable: !!(done && opts.uncheckable),
      onTaskRowClick: opts.onTaskRowClick
    });
  }
  function createTaskRow(doc, task, done, opts = {}) {
    const row = doc.createElement("div");
    row.setAttribute("data-kp-onboarding-task-id", task.id);
    if (done) {
      try {
        row.setAttribute("data-kp-onboarding-task-done", "true");
      } catch {
      }
    }
    const isNext = !!(!done && opts.isNext);
    if (isNext) {
      try {
        row.setAttribute("data-kp-onboarding-task-next", "true");
      } catch {
      }
    }
    assignStyle(row, {
      display: "flex",
      alignItems: "flex-start",
      gap: "10px",
      padding: "8px 10px",
      borderRadius: "3px",
      border: isNext ? NEXT_TASK_BORDER : ONBOARDING_METAL.rowBorder,
      background: done ? ONBOARDING_METAL.rowDoneBg : isNext ? NEXT_TASK_BG : ONBOARDING_METAL.rowBg,
      boxShadow: isNext ? NEXT_TASK_GLOW : "none"
    });
    const box = doc.createElement("div");
    box.setAttribute("aria-hidden", "true");
    assignStyle(box, {
      width: "18px",
      height: "18px",
      borderRadius: "2px",
      border: done ? "1px solid rgba(46, 204, 113, 0.9)" : isNext ? "1px solid rgba(120, 210, 255, 0.75)" : ONBOARDING_METAL.checkBorder,
      background: done ? "rgba(46, 204, 113, 0.85)" : "rgba(255,255,255,0.25)",
      boxShadow: done ? "0 0 0 2px rgba(46, 204, 113, 0.18)" : isNext ? "0 0 0 2px rgba(33, 150, 243, 0.28)" : "none",
      flex: "0 0 auto",
      marginTop: "1px",
      position: "relative"
    });
    if (done) {
      const check = doc.createElement("div");
      check.textContent = "\u2713";
      assignStyle(check, {
        position: "absolute",
        inset: "0",
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        fontSize: "13px",
        fontWeight: "800",
        color: "#0b1410"
      });
      box.appendChild(check);
      if (opts.animateComplete) {
        try {
          requestAnimationFrame(() => playCheckboxCompleteFlash(box));
        } catch {
          playCheckboxCompleteFlash(box);
        }
      }
    }
    const text = doc.createElement("div");
    renderKeyboardKeysInto(text, task.label || task.id);
    assignStyle(text, {
      fontSize: "13px",
      lineHeight: "1.35",
      color: done ? ONBOARDING_METAL.fgDim : ONBOARDING_METAL.fg,
      opacity: done ? "0.95" : "1"
    });
    row.appendChild(box);
    row.appendChild(text);
    applyTaskRowInteractive(row, {
      uncheckable: !!(done && opts.uncheckable),
      onTaskRowClick: opts.onTaskRowClick
    });
    return row;
  }
  function renderOnboardingSlideSurface(surface, params = {}) {
    if (!surface) return;
    const doc = params.doc || surface.ownerDocument || document;
    const tasks = (params.tasks || []).filter((t) => t && t.id);
    const completedSet = params.completedTaskIds instanceof Set ? params.completedTaskIds : new Set(Array.isArray(params.completedTaskIds) ? params.completedTaskIds.map(String) : []);
    const lastCompletedTaskId = params.lastCompletedTaskId != null ? String(params.lastCompletedTaskId) : "";
    const onTaskRowClick = typeof params.onTaskRowClick === "function" ? params.onTaskRowClick : null;
    const onCloseClick = typeof params.onCloseClick === "function" ? params.onCloseClick : null;
    const bodyTextStr = String(params.bodyText || "").trim();
    const forceRebuild = !!params.forceRebuild;
    const showTip = params.showTip === true;
    const showCloseButton = params.showCloseButton === true;
    const nextTaskId = (tasks.find((t) => !completedSet.has(t.id)) || null)?.id || null;
    const existingRows = surface.querySelectorAll("[data-kp-onboarding-task-id]");
    const existingBody = surface.querySelector('[data-kp-onboarding-body-text="true"]');
    const canUpdateInPlace = !forceRebuild && existingRows.length === tasks.length && tasks.every((t, i) => existingRows[i]?.getAttribute("data-kp-onboarding-task-id") === t.id);
    try {
      if (bodyTextStr) {
        if (existingBody && canUpdateInPlace) {
          existingBody.textContent = "";
          const lines = bodyTextStr.split("\n");
          lines.forEach((line, i) => {
            if (i > 0) existingBody.appendChild(doc.createElement("br"));
            renderKeyboardKeysInto(
              (() => {
                const s = doc.createElement("span");
                existingBody.appendChild(s);
                return s;
              })(),
              line
            );
          });
          existingBody.style.display = "block";
          existingBody.style.marginBottom = tasks.length || showCloseButton ? "12px" : "0px";
        }
      } else if (existingBody && canUpdateInPlace) {
        existingBody.style.display = "none";
        existingBody.textContent = "";
      }
    } catch {
    }
    if (canUpdateInPlace) {
      tasks.forEach((task, i) => {
        const done = completedSet.has(task.id);
        applyTaskRowVisual(existingRows[i], task, done, {
          isNext: !!(nextTaskId && task.id === nextTaskId),
          uncheckable: done && task.id === lastCompletedTaskId,
          onTaskRowClick
        });
      });
      try {
        const existingTip = surface.querySelector('[data-kp-onboarding-tip="true"]');
        if (!showTip && existingTip) existingTip.remove();
      } catch {
      }
      syncCloseTutorialButton(surface, { show: showCloseButton, onClick: onCloseClick, doc });
      return;
    }
    while (surface.firstChild) surface.removeChild(surface.firstChild);
    if (bodyTextStr) {
      const body = doc.createElement("div");
      body.setAttribute("data-kp-onboarding-body-text", "true");
      const lines = bodyTextStr.split("\n");
      lines.forEach((line, i) => {
        if (i > 0) body.appendChild(doc.createElement("br"));
        const span = doc.createElement("span");
        renderKeyboardKeysInto(span, line);
        body.appendChild(span);
      });
      assignStyle(body, {
        fontSize: "13px",
        lineHeight: "1.45",
        color: ONBOARDING_METAL.fg,
        marginBottom: tasks.length || showCloseButton ? "12px" : "0px",
        opacity: "0.95"
      });
      surface.appendChild(body);
    }
    const list = doc.createElement("div");
    assignStyle(list, {
      display: "flex",
      flexDirection: "column",
      gap: "8px"
    });
    for (const task of tasks) {
      const done = completedSet.has(task.id);
      list.appendChild(createTaskRow(doc, task, done, {
        isNext: !!(nextTaskId && task.id === nextTaskId),
        uncheckable: done && task.id === lastCompletedTaskId,
        animateComplete: !!(done && lastCompletedTaskId && task.id === lastCompletedTaskId),
        onTaskRowClick
      }));
    }
    surface.appendChild(list);
    if (showTip) {
      const tip = doc.createElement("div");
      tip.setAttribute("data-kp-onboarding-tip", "true");
      tip.textContent = ONBOARDING_REOPEN_TIP;
      assignStyle(tip, {
        marginTop: "10px",
        fontSize: "12px",
        lineHeight: "1.35",
        color: ONBOARDING_METAL.fgMute
      });
      surface.appendChild(tip);
    }
    syncCloseTutorialButton(surface, { show: showCloseButton, onClick: onCloseClick, doc });
  }
  function syncCloseTutorialButton(surface, opts = {}) {
    if (!surface) return;
    const doc = opts.doc || surface.ownerDocument || document;
    const show = opts.show === true;
    const onClick = typeof opts.onClick === "function" ? opts.onClick : null;
    let btn = null;
    try {
      btn = surface.querySelector('button[data-kp-onboarding-close-tutorial="true"]');
    } catch {
    }
    if (!show) {
      try {
        if (btn) btn.remove();
      } catch {
      }
      return;
    }
    if (!btn) {
      btn = doc.createElement("button");
      btn.type = "button";
      btn.setAttribute("data-kp-onboarding-close-tutorial", "true");
      btn.textContent = "Close Tutorial";
      assignStyle(btn, {
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        width: "fit-content",
        marginLeft: "auto",
        alignSelf: "flex-end",
        height: "32px",
        borderRadius: "2px",
        border: "1px solid rgba(30, 120, 70, 0.75)",
        background: "linear-gradient(180deg, rgba(255,255,255,0.28) 0%, rgba(255,255,255,0.06) 35%, transparent 60%), linear-gradient(180deg, #7dcf9a 0%, #4caf72 50%, #3a8f5a 100%)",
        boxShadow: "0 1px 0 rgba(255,255,255,0.35) inset, 0 -1px 0 rgba(0,0,0,0.18) inset",
        color: "#0b1410",
        cursor: "pointer",
        fontSize: "12px",
        fontWeight: "800",
        padding: "0 14px",
        lineHeight: "30px",
        fontFamily: "inherit"
      });
      surface.appendChild(btn);
    } else {
      try {
        btn.style.marginLeft = "auto";
        btn.style.alignSelf = "flex-end";
        btn.style.width = "fit-content";
        btn.style.display = "flex";
      } catch {
      }
    }
    try {
      if (btn._kpOnboardingCloseClick) {
        btn.removeEventListener("click", btn._kpOnboardingCloseClick);
        btn._kpOnboardingCloseClick = null;
      }
    } catch {
    }
    if (onClick) {
      btn._kpOnboardingCloseClick = onClick;
      try {
        btn.addEventListener("click", onClick);
      } catch {
      }
    }
  }
  function updateOnboardingChrome(refs, params = {}) {
    if (!refs) return;
    const idx = Number(params.slideIndex) || 0;
    const total = Math.max(1, Number(params.slideCount) || 1);
    try {
      if (refs.titleEl) refs.titleEl.textContent = String(params.title || ONBOARDING_DEFAULT_TITLE);
    } catch {
    }
    try {
      if (refs.stepEl) refs.stepEl.textContent = `${idx + 1} / ${total}`;
    } catch {
    }
    try {
      if (refs.root && params.slideId != null) {
        refs.root.dataset.kpOnboardingSlideId = String(params.slideId || "");
      }
    } catch {
    }
    try {
      if (refs.prevBtn) refs.prevBtn.disabled = idx <= 0;
    } catch {
    }
    try {
      if (refs.nextBtn) refs.nextBtn.disabled = idx >= total - 1;
    } catch {
    }
  }
  var ONBOARDING_OVERLAY_PRIMARY_BTN = {
    border: "1px solid #2a4a66",
    background: "linear-gradient(180deg, #5a7a9a 0%, #3a5570 100%)",
    boxShadow: "0 1px 0 rgba(255,255,255,0.28) inset, 0 -1px 0 rgba(0,0,0,0.28) inset",
    color: "#e8f0f8"
  };
  function applyOnboardingOverlayChrome(refs) {
    const overlayEl = refs?.overlayEl || null;
    if (!overlayEl) return;
    assignStyle(overlayEl, {
      position: "absolute",
      inset: "0",
      padding: "14px",
      alignItems: "center",
      justifyContent: "center",
      background: "rgba(0,0,0,0.35)",
      backdropFilter: "blur(6px)",
      WebkitBackdropFilter: "blur(6px)",
      zIndex: "20",
      boxSizing: "border-box",
      overflow: "hidden"
    });
    let card = null;
    try {
      card = overlayEl.querySelector(":scope > div") || Array.from(overlayEl.children || []).find((el2) => el2 && el2.tagName === "DIV") || null;
    } catch {
      card = overlayEl.firstElementChild?.tagName === "DIV" ? overlayEl.firstElementChild : null;
    }
    if (card) {
      assignStyle(card, {
        width: "100%",
        maxWidth: "320px",
        borderRadius: "3px",
        border: ONBOARDING_METAL.panelBorder,
        background: ONBOARDING_METAL.panelBg,
        boxShadow: ONBOARDING_METAL.panelShadow,
        padding: "14px 14px 12px 14px",
        fontFamily: "Helvetica, Arial, sans-serif",
        color: ONBOARDING_METAL.fg,
        boxSizing: "border-box"
      });
    }
    if (refs.titleEl) {
      assignStyle(refs.titleEl, {
        fontSize: "14px",
        fontWeight: "800",
        letterSpacing: "0.2px",
        marginBottom: "8px",
        color: ONBOARDING_METAL.fg
      });
    }
    if (refs.msgEl) {
      assignStyle(refs.msgEl, {
        fontSize: "13px",
        lineHeight: "1.35",
        color: ONBOARDING_METAL.fgDim,
        whiteSpace: "pre-wrap"
      });
    }
    const styleBtn = (b, variant) => {
      if (!b) return;
      const primary = variant === "primary";
      assignStyle(b, {
        minHeight: "28px",
        height: "auto",
        borderRadius: "2px",
        border: primary ? ONBOARDING_OVERLAY_PRIMARY_BTN.border : ONBOARDING_METAL.btnBorder,
        background: primary ? ONBOARDING_OVERLAY_PRIMARY_BTN.background : ONBOARDING_METAL.btnBg,
        boxShadow: primary ? ONBOARDING_OVERLAY_PRIMARY_BTN.boxShadow : ONBOARDING_METAL.btnShadow,
        color: primary ? ONBOARDING_OVERLAY_PRIMARY_BTN.color : ONBOARDING_METAL.fg,
        cursor: "pointer",
        fontFamily: "Helvetica, Arial, sans-serif",
        fontSize: "12px",
        fontWeight: "700",
        padding: "4px 12px",
        lineHeight: "20px",
        whiteSpace: "normal",
        textAlign: "center"
      });
    };
    styleBtn(refs.secondaryBtn, "secondary");
    styleBtn(refs.primaryBtn, "primary");
  }
  function ensureOnboardingOverlay(host, doc) {
    if (!host) return null;
    const d = doc || host.ownerDocument || document;
    let root = host;
    try {
      if (!host.classList?.contains?.(ONBOARDING_PANEL_CLASS)) {
        root = host.closest?.(`.${ONBOARDING_PANEL_CLASS}`) || host;
        if (!root.classList?.contains?.(ONBOARDING_PANEL_CLASS)) {
          const shadow = host.getRootNode?.();
          if (shadow?.host?.classList?.contains?.(ONBOARDING_PANEL_CLASS)) root = shadow.host;
        }
      }
    } catch {
      root = host;
    }
    const shell = root.shadowRoot || root;
    const existing = shell.querySelector('[data-kp-onboarding-overlay="true"]');
    if (existing) {
      try {
        if (existing.parentElement !== shell) shell.appendChild(existing);
      } catch {
      }
      const refs2 = {
        overlayEl: existing,
        titleEl: existing.querySelector('[data-kp-onboarding-overlay-title="true"]'),
        msgEl: existing.querySelector('[data-kp-onboarding-overlay-message="true"]'),
        primaryBtn: existing.querySelector('button[data-kp-onboarding-overlay-primary="true"]'),
        secondaryBtn: existing.querySelector('button[data-kp-onboarding-overlay-secondary="true"]')
      };
      try {
        applyOnboardingOverlayChrome(refs2);
      } catch {
      }
      return refs2;
    }
    const overlay = d.createElement("div");
    overlay.setAttribute("data-kp-onboarding-overlay", "true");
    overlay.hidden = true;
    assignStyle(overlay, {
      display: "none",
      pointerEvents: "none"
    });
    const card = d.createElement("div");
    const titleEl = d.createElement("div");
    titleEl.setAttribute("data-kp-onboarding-overlay-title", "true");
    titleEl.textContent = "Nice!";
    const msgEl = d.createElement("div");
    msgEl.setAttribute("data-kp-onboarding-overlay-message", "true");
    msgEl.textContent = "";
    const btnRow = d.createElement("div");
    assignStyle(btnRow, {
      display: "flex",
      flexWrap: "wrap",
      alignItems: "center",
      justifyContent: "flex-end",
      gap: "8px",
      marginTop: "12px"
    });
    const secondaryBtn = d.createElement("button");
    secondaryBtn.type = "button";
    secondaryBtn.hidden = true;
    secondaryBtn.textContent = "";
    secondaryBtn.setAttribute("data-kp-onboarding-overlay-secondary", "true");
    const primaryBtn = d.createElement("button");
    primaryBtn.type = "button";
    primaryBtn.textContent = "OK";
    primaryBtn.setAttribute("data-kp-onboarding-overlay-primary", "true");
    btnRow.appendChild(secondaryBtn);
    btnRow.appendChild(primaryBtn);
    card.appendChild(titleEl);
    card.appendChild(msgEl);
    card.appendChild(btnRow);
    overlay.appendChild(card);
    shell.appendChild(overlay);
    const refs = { overlayEl: overlay, titleEl, msgEl, primaryBtn, secondaryBtn };
    applyOnboardingOverlayChrome(refs);
    return refs;
  }
  function setOnboardingOverlayOpen(overlayEl, open, root = null) {
    if (!overlayEl) return;
    try {
      overlayEl.hidden = !open;
      if (open) {
        overlayEl.style.setProperty("display", "flex", "important");
        overlayEl.style.setProperty("pointer-events", "auto", "important");
      } else {
        overlayEl.style.removeProperty("display");
        overlayEl.style.removeProperty("pointer-events");
        overlayEl.style.display = "none";
        overlayEl.style.pointerEvents = "none";
      }
    } catch {
    }
    try {
      if (root) {
        if (open) root.dataset.kpOnboardingOverlayOpen = "true";
        else delete root.dataset.kpOnboardingOverlayOpen;
      }
    } catch {
    }
  }

  // src/ui/keyboard-layout-config-panel.js
  var KP_LAYOUT_ITEM_MIME = "application/x-kp-layout-item";
  var KP_MACRO_STEP_MIME = "application/x-kp-macro-step";
  function macroKeyLikeFromUserAction(action) {
    const kind = macroKeyKindFromFunctionId(action?.functionId);
    if (!kind) return null;
    return {
      id: action.id,
      kind,
      label: String(action.label || ""),
      config: action.parameters && action.parameters.config || {}
    };
  }
  var CONFIG_POSITION_MARGIN_PX = Math.max(PANEL_POSITION_MARGIN_PX, 16);
  var CONFIG_KEY_SIZE_PX = 46;
  var CONFIG_PANEL_WIDTH_PX = 960;
  var CONFIG_PANEL_MIN_HEIGHT_PX = 460;
  var CONFIG_INSPECTOR_WIDTH_PX = 280;
  var CONFIG_STYLE_ATTR = "data-kp-layout-config-panel-style";
  var CONFIG_STYLE_VERSION = "v20";
  var CONFIG_ICON_SPRITE_ATTR = "data-kp-layout-config-icons";
  var CONFIG_PLACE_ARROW_STYLE_ATTR = "data-kp-layout-place-arrow-style";
  function resolveConfigPanelHeightPx(savedHeight) {
    const n = Number(savedHeight);
    if (Number.isFinite(n) && n > 0) return Math.round(n);
    try {
      const vh = window.innerHeight || document.documentElement?.clientHeight || 800;
      return Math.max(CONFIG_PANEL_MIN_HEIGHT_PX, Math.round(vh - CONFIG_POSITION_MARGIN_PX * 2));
    } catch {
      return 520;
    }
  }
  var PLACE_ARROW_CSS = `
.kp-layout-place-arrow {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  margin: 0;
  padding: 0;
  border: none;
  overflow: hidden;
  pointer-events: none;
  background: transparent;
  z-index: ${Z_INDEX.LAYOUT_PLACE_ARROW};
}
.kp-layout-place-arrow svg {
  width: 100%;
  height: 100%;
  display: block;
}
`.trim();
  var CONFIG_ICON_SYMBOLS = Object.freeze([
    ["kp-cfg-i-kb", "M1 4h14v9H1V4zm2 2v2h2V6H3zm3 0v2h2V6H6zm3 0v2h2V6H9zm3 0v2h2V6h-2zM3 9v2h8V9H3z"],
    ["kp-cfg-i-lib", "M2 2h4v12H2V2zm5 0h2v12H7V2zm3 1h4v11h-4V3z"],
    ["kp-cfg-i-docs", "M3 1.5h10v13H4.6A1.6 1.6 0 0 1 3 12.9V1.5zm1.5 2.5h7M4.5 7h7M4.5 9.5h5"],
    ["kp-cfg-i-create", "M8 1l2 4h4l-3 3 1 5-4-2-4 2 1-5-3-3h4L8 1zm0 4l-.7 1.4H5.8l1.1.9-.4 1.5L8 8.2l1.5.6-.4-1.5 1.1-.9H8.7L8 5z"],
    ["kp-cfg-i-eye", "M8 3c4 0 7 5 7 5s-3 5-7 5-7-5-7-5 3-5 7-5zm0 2a3 3 0 100 6 3 3 0 000-6zm0 2a1 1 0 110 2 1 1 0 010-2z"],
    ["kp-cfg-i-close", "M3 2l5 5 5-5 1 1-5 5 5 5-1 1-5-5-5 5-1-1 5-5-5-5 1-1z"],
    ["kp-cfg-i-check", "M2 8l2-2 3 3 5-5 2 2-7 7L2 8z"],
    ["kp-cfg-i-plus", "M7 2h2v5h5v2H9v5H7V9H2V7h5V2z"],
    ["kp-cfg-i-copy", "M5 2h9v9h-2V4H5V2zm-3 3h9v9H2V5zm2 2v5h5V7H4z"],
    ["kp-cfg-i-trash", "M6 1h4l1 2h4v2H1V3h4l1-2zM3 6h10l-1 9H4L3 6zm3 2v5h1V8H6zm3 0v5h1V8H9z"],
    ["kp-cfg-i-import", "M8 1l4 4H9v7H7V5H4l4-4zM2 12h12v3H2v-3z"],
    ["kp-cfg-i-export", "M7 1h2v7h3l-4 4-4-4h3V1zm-5 11h12v3H2v-3z"],
    ["kp-cfg-i-expand", "M2 2h5v2H4v3H2V2zm7 0h5v5h-2V4H9V2zM2 9h2v3h3v2H2V9zm10 0h2v5H9v-2h3V9z"],
    ["kp-cfg-i-collapse", "M5 5H2V3h5v5H5V5zm4 0V3h5v2h-3v3H9V5zM5 9v3H2v2h5V9H5zm4 0h2v3h3v2H9V9z"],
    /** Collapse Inspector (dock to the right). */
    ["kp-cfg-i-collapse-right", "M8 2l6 6-6 6-1.4-1.4L10.2 9H2V7h8.2L6.6 3.4 8 2z"],
    /** Collapse User Macros (fold downward). */
    ["kp-cfg-i-collapse-down", "M2 8l6 6 6-6-1.4-1.4L9 10.2V2H7v8.2L3.4 6.6 2 8z"],
    ["kp-cfg-i-expand-up", "M8 2l5 5H9v7H7V7H3l5-5zm-6 11h12v2H2v-2z"],
    ["kp-cfg-i-place", "M8 1a5 5 0 015 5c0 3.5-5 9-5 9S3 9.5 3 6a5 5 0 015-5zm0 3a2 2 0 100 4 2 2 0 000-4z"],
    ["kp-cfg-i-search", "M6.5 2a4.5 4.5 0 013.5 7.3L14 13l-1 1-4.2-4A4.5 4.5 0 116.5 2zm0 2a2.5 2.5 0 100 5 2.5 2.5 0 000-5z"],
    ["kp-cfg-i-chord", "M4 2h3v3H4V2zm5 0h3v3H9V2zM2 7h3v3H2V7zm4.5 0h3v3h-3V7zM11 7h3v3h-3V7zM4 12h3v2H4v-2zm5 0h3v2H9v-2z"],
    ["kp-cfg-i-chevron", "M3 6l5 5 5-5H3z"],
    ["kp-cfg-i-more", "M3 6.5a1.5 1.5 0 110 3 1.5 1.5 0 010-3zm5 0a1.5 1.5 0 110 3 1.5 1.5 0 010-3zm5 0a1.5 1.5 0 110 3 1.5 1.5 0 010-3z"],
    ["kp-cfg-i-all", "M2 2h5v5H2V2zm7 0h5v5H9V2zM2 9h5v5H2V9zm7 0h5v5H9V9z"],
    ["kp-cfg-i-macro", "M3 2h10v2H3V2zm0 4h10v2H3V6zm0 4h6v2H3v-2zm8 0h2v4H8v-2h3v-2z"],
    ["kp-cfg-i-keycap", "M3 3h10v10H3V3zm2 2v6h6V5H5z"],
    ["kp-cfg-i-nav", "M8 1l5 6H9v8H7V7H3l5-6z"],
    ["kp-cfg-i-click", "M7 1v7.2L5.2 6.4 4 7.6 8 12l4-4.4-1.2-1.2L9 8.2V1H7zm-4 11h10v2H3v-2z"],
    ["kp-cfg-i-scroll", "M8 1l3 3H9v3H7V4H5l3-3zm0 14l-3-3h2V9h2v3h2l-3 3zM3 7h10v2H3V7z"],
    ["kp-cfg-i-tabs", "M1 3h5l1 2h8v9H1V3zm2 4v5h10V7H3z"],
    ["kp-cfg-i-type", "M2 3h12v2H9v8H7V5H2V3zm1 10h10v2H3v-2z"],
    ["kp-cfg-i-code", "M5 3L1 8l4 5 1.4-1.1L3.5 8 6.4 4.1 5 3zm6 0l-1.4 1.1L12.5 8l-2.9 3.9L11 13l4-5-4-5z"],
    ["kp-cfg-i-ai", "M8 1l1.5 4H14l-3.5 2.7L12 13 8 10.2 4 13l1.5-5.3L2 5h4.5L8 1z"],
    ["kp-cfg-i-select", "M3 3h4v2H5v2H3V3zm6 0h4v4h-2V5H9V3zM3 9h2v2h2v2H3V9zm8 2h2v4H9v-2h2v-2z"],
    ["kp-cfg-i-image", "M2 3h12v10H2V3zm2 2v6h8V5H4zm1 1h3v2H5V6zm4 3l2 3H5l1.5-2L8 12l1-1z"],
    ["kp-cfg-i-data", "M2 3c0-1.5 2.7-2.5 6-2.5S14 1.5 14 3v10c0 1.5-2.7 2.5-6 2.5S2 14.5 2 13V3zm2 0c0 .4 1.6 1 4 1s4-.6 4-1-1.6-1-4-1-4 .6-4 1z"],
    ["kp-cfg-i-lookup", "M7 2a5 5 0 013.9 8.1L14 13l-1 1-3.1-3.1A5 5 0 117 2zm0 2a3 3 0 100 6 3 3 0 000-6z"],
    ["kp-cfg-i-translate", "M2 3h5v2H5.2A9 9 0 017 8.5L5.7 9.5A8 8 0 014 5H2V3zm7 0h5v2h-1.5l-.8 2H14v2h-2.8l-.7 2H14v2H9l.6-2H8v-2h1.3L10 5H8V3h1z"],
    ["kp-cfg-i-display", "M2 3h12v8H9v1h3v2H4v-2h3v-1H2V3zm2 2v4h8V5H4z"],
    ["kp-cfg-i-burst", "M8 1l1 4 4 .5-3 2.8.9 4.2L8 10.5 5.1 12.5 6 8.3 3 5.5 7 5 8 1zm-5 12h10v2H3v-2z"],
    ["kp-cfg-i-cycle", "M13 3v4h-4V5h1.3A4 4 0 108 12a4 4 0 003.5-2h2.1A6 6 0 114 4.2V2h2v4H2V2h2v1.3A6 6 0 0113 3z"],
    ["kp-cfg-i-hold", "M7 1h2v7H7V1zm-2 8h6l2 2v4H3v-4l2-2zm2 2v2h2v-2H7z"],
    ["kp-cfg-i-mouse", "M5 2h6a3 3 0 013 3v6a3 3 0 01-3 3H5a3 3 0 01-3-3V5a3 3 0 013-3zm3 1v4H5.2A2 2 0 015 5V4a1 1 0 011-1h2z"],
    ["kp-cfg-i-hotkey", "M2 4h5v2H4v2h3v2H2V4zm7 0h5v8H9V4zm2 2v4h1V6h-1z"],
    ["kp-cfg-i-back", "M8 2l-6 6 6 6 1.4-1.4L5.8 9H14V7H5.8l3.6-3.6L8 2z"],
    ["kp-cfg-i-forward", "M8 2l6 6-6 6-1.4-1.4L10.2 9H2V7h8.2L6.6 3.4 8 2z"]
  ]);
  var LIBRARY_KEY_ICON_BY_FUNCTION_ID = Object.freeze({
    HIGHLIGHT: "kp-cfg-i-select",
    RECTANGLE_HIGHLIGHT: "kp-cfg-i-select",
    CLIPBOARD_COPY: "kp-cfg-i-copy",
    SELECT_WORD: "kp-cfg-i-select",
    SELECT_SENTENCE: "kp-cfg-i-select",
    SELECT_PARAGRAPH: "kp-cfg-i-select",
    SELECT_IMAGE: "kp-cfg-i-image",
    OPEN_POPOVER: "kp-cfg-i-display",
    PAGE_DOWN_INSTANT: "kp-cfg-i-scroll",
    PAGE_UP_INSTANT: "kp-cfg-i-scroll",
    SCROLL_LINE: "kp-cfg-i-scroll",
    TAB_RIGHT: "kp-cfg-i-tabs",
    TAB_LEFT: "kp-cfg-i-tabs",
    OMNIBOX: "kp-cfg-i-search",
    SEND_TEXT_TO_AI: "kp-cfg-i-ai",
    TYPE_CHARACTERS: "kp-cfg-i-type",
    EXECUTE_JS: "kp-cfg-i-code",
    GET_TEXT_AT_CURSOR: "kp-cfg-i-data",
    GET_TEXT_RANGE: "kp-cfg-i-data",
    GET_MEDIA_AT_CURSOR: "kp-cfg-i-image",
    PAGE_MEDIA: "kp-cfg-i-image",
    LOOKUP_WORD: "kp-cfg-i-lookup",
    FONT_INFO: "kp-cfg-i-data",
    TRANSLATE: "kp-cfg-i-translate",
    SHOW_POPOVER: "kp-cfg-i-display"
  });
  var LIBRARY_KEY_ICON_BY_CATEGORY = Object.freeze({
    Navigation: "kp-cfg-i-nav",
    "Tab Control": "kp-cfg-i-tabs",
    "Begin URL": "kp-cfg-i-search",
    "Get Page Data": "kp-cfg-i-data",
    KeyPilot: "kp-cfg-i-kb",
    Click: "kp-cfg-i-click",
    Tabs: "kp-cfg-i-tabs",
    Navigate: "kp-cfg-i-nav",
    Scroll: "kp-cfg-i-scroll",
    Select: "kp-cfg-i-select",
    Clipboard: "kp-cfg-i-copy",
    Text: "kp-cfg-i-type",
    Tools: "kp-cfg-i-display",
    Script: "kp-cfg-i-code",
    Data: "kp-cfg-i-data",
    Lookup: "kp-cfg-i-lookup",
    Translate: "kp-cfg-i-translate",
    Display: "kp-cfg-i-display",
    Media: "kp-cfg-i-image"
  });
  function nextDuplicateUserLayoutLabel(sourceLabel, existingLayouts = []) {
    const base = String(sourceLabel || "Layout").trim() || "Layout";
    const userN = base.match(/^User Layout(?: (\d+))?$/i);
    if (userN) {
      let maxN = 1;
      for (const l of existingLayouts || []) {
        const m = String(l?.label || "").trim().match(/^User Layout(?: (\d+))?$/i);
        if (!m) continue;
        const n2 = m[1] ? Number(m[1]) : 1;
        if (Number.isFinite(n2) && n2 > maxN) maxN = n2;
      }
      return `User Layout ${maxN + 1}`;
    }
    const labels = new Set(
      (existingLayouts || []).map((l) => String(l?.label || "").trim()).filter(Boolean)
    );
    const copy1 = `${base} copy`;
    if (!labels.has(copy1)) return copy1;
    let n = 2;
    while (labels.has(`${base} copy ${n}`)) n += 1;
    return `${base} copy ${n}`;
  }
  function mkCfgIcon(doc, symbolId) {
    const svg = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
    svg.setAttribute("class", "kp-cfg-ico");
    svg.setAttribute("aria-hidden", "true");
    svg.setAttribute("focusable", "false");
    const use = doc.createElementNS("http://www.w3.org/2000/svg", "use");
    use.setAttribute("href", `#${symbolId}`);
    svg.appendChild(use);
    return svg;
  }
  function setCfgBtnLabel(btn, label, iconId) {
    if (!btn) return;
    const doc = btn.ownerDocument || document;
    btn.textContent = String(label || "");
    if (iconId) {
      try {
        btn.prepend(mkCfgIcon(doc, iconId));
      } catch {
      }
    }
  }
  function ensureConfigIconSprite(root) {
    const doc = root?.ownerDocument;
    if (!root || !doc) return;
    const existing = root.querySelector(`svg[${CONFIG_ICON_SPRITE_ATTR}]`);
    if (existing?.getAttribute(CONFIG_ICON_SPRITE_ATTR) === CONFIG_STYLE_VERSION) return;
    try {
      existing?.remove?.();
    } catch {
    }
    try {
      root.querySelectorAll(`svg[${CONFIG_ICON_SPRITE_ATTR}]`).forEach((el2) => el2.remove());
    } catch {
    }
    const svg = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
    svg.setAttribute(CONFIG_ICON_SPRITE_ATTR, CONFIG_STYLE_VERSION);
    svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    svg.setAttribute("width", "0");
    svg.setAttribute("height", "0");
    svg.setAttribute("aria-hidden", "true");
    Object.assign(svg.style, { position: "absolute", width: "0", height: "0", overflow: "hidden" });
    const defs = doc.createElementNS("http://www.w3.org/2000/svg", "defs");
    for (const [id, d] of CONFIG_ICON_SYMBOLS) {
      const symbol = doc.createElementNS("http://www.w3.org/2000/svg", "symbol");
      symbol.setAttribute("id", id);
      symbol.setAttribute("viewBox", "0 0 16 16");
      const path = doc.createElementNS("http://www.w3.org/2000/svg", "path");
      path.setAttribute("d", d);
      symbol.appendChild(path);
      defs.appendChild(symbol);
    }
    svg.appendChild(defs);
    root.appendChild(svg);
  }
  var MACRO_LOGIC_DEFS = Object.freeze([
    Object.freeze({ kind: "wait", label: "Wait", description: "Pause for N ms" }),
    Object.freeze({
      kind: "gate",
      label: "Gate",
      description: "Continue only if the previous step\u2019s result\u2026"
    }),
    Object.freeze({ kind: "stop", label: "Stop", description: "End the macro now" }),
    Object.freeze({ kind: "runMacro", label: "Run Macro", description: "Call another macro" })
  ]);
  var MACRO_GATE_OPS = Object.freeze([
    Object.freeze({
      op: "truthy",
      label: "Has a value",
      hint: "Has a value \u2014 previous step\u2019s result is present or true (truthy)",
      needsRight: false
    }),
    Object.freeze({
      op: "falsy",
      label: "Is empty",
      hint: "Is empty \u2014 previous step\u2019s result is missing or false (falsy)",
      needsRight: false
    }),
    Object.freeze({
      op: "eq",
      label: "Equals",
      hint: "Equals (eq) the compare-to value",
      needsRight: true
    }),
    Object.freeze({
      op: "neq",
      label: "Does not equal",
      hint: "Does not equal (neq) the compare-to value",
      needsRight: true
    }),
    Object.freeze({
      op: "gt",
      label: "Greater than",
      hint: "Greater than (gt) the compare-to value",
      needsRight: true
    }),
    Object.freeze({
      op: "lt",
      label: "Less than",
      hint: "Less than (lt) the compare-to value",
      needsRight: true
    })
  ]);
  function getMacroGateOpDef(op) {
    const key = String(op || "truthy");
    return MACRO_GATE_OPS.find((d) => d.op === key) || MACRO_GATE_OPS[0];
  }
  function abbreviateLabel(label) {
    const text = String(label || "").trim();
    if (!text) return "?";
    const words = text.split(/[\s/_-]+/).filter(Boolean);
    if (words.length > 1) {
      return words.slice(0, 3).map((w) => w[0]).join("").toUpperCase();
    }
    return text.length <= 4 ? text : text.slice(0, 4);
  }
  var KeyboardLayoutConfigPanel = class {
    /**
     * @param {object} [opts]
     * @param {(info: { state: LayoutConfigState }) => void} [opts.onChange]
     * @param {() => void} [opts.onClose]
     */
    constructor({ onChange, onClose } = {}) {
      this._kp = null;
      this._onChange = typeof onChange === "function" ? onChange : null;
      this._onClose = typeof onClose === "function" ? onClose : null;
      this.root = null;
      this.shadowRoot = null;
      this._listEl = null;
      this._layoutCombo = null;
      this._layoutComboInput = null;
      this._layoutComboList = null;
      this._layoutComboToggle = null;
      this._setCurrentBtn = null;
      this._layoutOptsWrap = null;
      this._layoutOptsBtn = null;
      this._layoutOptsMenu = null;
      this._handednessSeg = null;
      this._leftHandedMenuToggle = null;
      this._showNumRowToggle = null;
      this._settingsStorageListener = null;
      this._searchInput = null;
      this._macroKeysActionsRow = null;
      this._inspectorBody = null;
      this._macroKeyEditorHost = null;
      this._inspectorPane = null;
      this._inspectorRail = null;
      this._mainRow = null;
      this._createPane = null;
      this._createBody = null;
      this._createToggleBtn = null;
      this._createExpandUpBtn = null;
      this._createModeSeg = null;
      this._libraryCreateSplitter = null;
      this._workspaceInspectorSplitter = null;
      this._scriptPanel = null;
      this._macroKeyPanel = null;
      this._macroNameInput = null;
      this._scriptStepsHost = null;
      this._scriptStockBanner = null;
      this._scriptMetaEl = null;
      this._placeHintEl = null;
      this._placePulseEl = null;
      this._titlebarPlaceMsgEl = null;
      this._libInstructionsEl = null;
      this._libInstructionsToggleEl = null;
      this._libInstructionsExpanded = true;
      this._libViewSegEl = null;
      this._addStepSelect = null;
      this._addStepDelayInput = null;
      this._libTabsEl = null;
      this._fnCategorySelect = null;
      this._fnCategoryWrap = null;
      this._currentBadge = null;
      this._dragDispose = null;
      this._resizeDispose = null;
      this._positionHydrated = false;
      this._macroStepDragFrom = -1;
      this._libPrimaryTab = "all";
      this._libFunctionCategory = "";
      this._libViewMode = "cards";
      this._tableExpanded = new Set(DEFAULT_SETTINGS.actionsLibraryTableExpanded || [
        "functions",
        "macros",
        "macroKeys"
      ]);
      this._tableExpandedHydrated = false;
      this._inspectorOpen = true;
      this._createOpen = false;
      this._createMaximized = false;
      this._createPrevHeightPx = null;
      this._selectedLibraryFunctionId = "";
      this._createMode = "script";
      this._macroDraft = null;
      this._selectedStepIndex = -1;
      this._inspectorSelection = null;
      this._macroKeyDraft = null;
      this._chordCaptureCleanup = null;
      this._pendingChordMods = /* @__PURE__ */ Object.create(null);
      this._placeItem = null;
      this._placeSourceEl = null;
      this._placeArrowEl = null;
      this._placePointerBound = false;
      this._onPlacePointerMove = this._onPlacePointerMove.bind(this);
      this._onPlaceKeyDown = this._onPlaceKeyDown.bind(this);
      this._onPlacePointerDown = this._onPlacePointerDown.bind(this);
      this._onPlaceClick = this._onPlaceClick.bind(this);
      this._panelPosition = {
        ...DEFAULT_SETTINGS.panelPositions.keyboardLayoutConfig
      };
      this._st = {
        mode: "builtin",
        builtinLayoutId: DEFAULT_KEYBOARD_LAYOUT_ID,
        userLayoutId: null,
        userLayout: null,
        userLayouts: [],
        macros: [],
        stockMacros: [],
        actions: [],
        macroKeys: []
      };
    }
    isOpen() {
      return !!(this.root && this.root.isConnected && !this.root.hidden);
    }
    /** @returns {LayoutConfigState} */
    getState() {
      return this._st;
    }
    /**
     * @param {any} kp KeyPilot instance
     * @param {{ duplicateBuiltin?: boolean }} [opts] When true (default), entering edit on a
     *   built-in layout creates "{Family} Copy N" and switches to it.
     */
    async show(kp, { duplicateBuiltin = true } = {}) {
      this._kp = kp || null;
      if (window !== window.top) return;
      this._ensure();
      const builtinId = String(kp?._keyboardLayoutId || DEFAULT_KEYBOARD_LAYOUT_ID);
      this._st.builtinLayoutId = builtinId;
      let adoptedCopy = null;
      try {
        const sel = String(kp?._currentKeyboardLayoutId || "builtin");
        if (sel.startsWith("user:")) {
          const id = sel.slice("user:".length);
          this._st.mode = "user";
          this._st.userLayoutId = id;
        } else if (duplicateBuiltin) {
          const layouts = await listUserKeyboardLayouts();
          const label = nextUserCopyLayoutLabel(this._familyBaseLabel(), layouts);
          const created = await duplicateBuiltinLayoutToUserLayout({
            builtinLayoutId: this._st.builtinLayoutId,
            label
          });
          if (created?.id) {
            adoptedCopy = created;
            this._st.mode = "user";
            this._st.userLayoutId = created.id;
            this._st.userLayout = created;
            const nextId = `user:${created.id}`;
            try {
              await setSettings({ currentKeyboardLayoutId: nextId });
            } catch {
            }
            try {
              if (kp) {
                kp._currentKeyboardLayoutId = nextId;
                kp._currentUserLayout = created;
                if (kp._settings) kp._settings.currentKeyboardLayoutId = nextId;
              }
            } catch {
            }
          } else {
            this._st.mode = "builtin";
            this._st.userLayoutId = null;
            this._st.userLayout = null;
          }
        } else {
          this._st.mode = "builtin";
          this._st.userLayoutId = null;
          this._st.userLayout = null;
        }
      } catch {
      }
      await this._reloadStore();
      if (adoptedCopy && kp && typeof kp.applyLiveUserLayout === "function") {
        try {
          void kp.applyLiveUserLayout(this._st.userLayout || adoptedCopy, {
            macros: this._st.macros,
            actions: this._st.actions
          });
        } catch {
        }
      }
      this._setVisible(true);
      try {
        this._syncHandednessUi();
      } catch {
      }
      this._emitChange();
    }
    hide() {
      this._cancelPlaceMode();
      this._stopChordCapture();
      this._setVisible(false);
    }
    /** @deprecated Kept for callers that previously used Save and Close; prefer hide(). */
    async _saveAndClose() {
      try {
        await this._persistUserLayout();
      } catch {
      }
      this.hide();
      try {
        this._onClose?.();
      } catch {
      }
    }
    cleanup() {
      this._cancelPlaceMode();
      this._stopChordCapture();
      try {
        if (this._settingsStorageListener) {
          chrome?.storage?.onChanged?.removeListener?.(this._settingsStorageListener);
        }
      } catch {
      }
      this._settingsStorageListener = null;
      try {
        this._dragDispose?.();
      } catch {
      }
      this._dragDispose = null;
      try {
        this._resizeDispose?.();
      } catch {
      }
      this._resizeDispose = null;
      try {
        if (this.root && this.root.parentNode) this.root.parentNode.removeChild(this.root);
      } catch {
      }
      this.root = null;
      this.shadowRoot = null;
      this._listEl = null;
      this._handednessSeg = null;
      this._leftHandedMenuToggle = null;
      this._kp = null;
    }
    /**
     * Create an empty user layout, select it for editing, and optionally make it current.
     * @param {{ setCurrent?: boolean, label?: string }} [opts]
     * @returns {Promise<any|null>} created layout or null
     */
    async createNewLayout({ setCurrent = true, label = "New Layout" } = {}) {
      try {
        const created = await createEmptyUserKeyboardLayout({
          baseBuiltinLayoutId: this._st.builtinLayoutId,
          label,
          includeNumberRow: true
        });
        if (!created?.id) {
          this._notify("Failed to create layout.", "error");
          return null;
        }
        this._st.userLayouts = await listUserKeyboardLayouts();
        this._st.mode = "user";
        this._st.userLayoutId = created.id;
        this._st.userLayout = created;
        this._renderLayoutSelect();
        this._renderRightList();
        this._emitChange();
        if (setCurrent) {
          const nextId = `user:${created.id}`;
          try {
            await setSettings({ currentKeyboardLayoutId: nextId });
          } catch {
          }
          try {
            const kp = this._kp;
            if (kp) {
              kp._currentKeyboardLayoutId = nextId;
              kp._currentUserLayout = created;
              if (kp._settings) kp._settings.currentKeyboardLayoutId = nextId;
              if (typeof kp.applyLiveUserLayout === "function") {
                void kp.applyLiveUserLayout(created, {
                  macros: this._st.macros,
                  actions: this._st.actions
                });
              } else if (typeof kp._refreshCurrentKeyboardLayoutFromSettings === "function") {
                void kp._refreshCurrentKeyboardLayoutFromSettings();
              }
            }
          } catch {
          }
        }
        this._notify(`Created "${created.label || "New Layout"}".`, "success");
        return created;
      } catch {
        this._notify("Failed to create layout.", "error");
        return null;
      }
    }
    /**
     * Duplicate the currently selected layout into a new editable user layout,
     * select it for editing, and optionally make it current.
     * Built-in → seeded copy of Built-in. User layout → deep copy of that layout's slots.
     * @param {{ setCurrent?: boolean, label?: string, builtinLayoutId?: string }} [opts]
     *   `builtinLayoutId` duplicates that built-in even when a user layout is selected.
     * @returns {Promise<any|null>} created layout or null
     */
    async duplicateLayout({ setCurrent = true, label, builtinLayoutId } = {}) {
      try {
        const layouts = await listUserKeyboardLayouts();
        const fromBuiltinId = builtinLayoutId ? String(builtinLayoutId) : "";
        const sourceUser = !fromBuiltinId && this._st.mode === "user" && this._st.userLayout ? this._st.userLayout : null;
        let nextLabel = label ? String(label) : "";
        if (!nextLabel) {
          if (sourceUser) {
            nextLabel = nextDuplicateUserLayoutLabel(
              sourceUser.label || "Layout",
              layouts
            );
          } else {
            const seedId = fromBuiltinId || this._st.builtinLayoutId;
            nextLabel = nextUserCopyLayoutLabel(this._familyBaseLabelFor(seedId), layouts) || "Custom Layout";
          }
        }
        const created = sourceUser ? await duplicateUserKeyboardLayout({ source: sourceUser, label: nextLabel }) : await duplicateBuiltinLayoutToUserLayout({
          builtinLayoutId: fromBuiltinId || this._st.builtinLayoutId,
          label: nextLabel
        });
        if (!created?.id) {
          this._notify("Failed to duplicate layout.", "error");
          return null;
        }
        this._st.userLayouts = await listUserKeyboardLayouts();
        this._st.mode = "user";
        this._st.userLayoutId = created.id;
        this._st.userLayout = created;
        this._renderLayoutSelect();
        this._renderRightList();
        this._emitChange();
        if (setCurrent) {
          const nextId = `user:${created.id}`;
          try {
            await setSettings({ currentKeyboardLayoutId: nextId });
          } catch {
          }
          try {
            const kp = this._kp;
            if (kp) {
              kp._currentKeyboardLayoutId = nextId;
              kp._currentUserLayout = created;
              if (kp._settings) kp._settings.currentKeyboardLayoutId = nextId;
              if (typeof kp.applyLiveUserLayout === "function") {
                void kp.applyLiveUserLayout(created, {
                  macros: this._st.macros,
                  actions: this._st.actions
                });
              } else if (typeof kp._refreshCurrentKeyboardLayoutFromSettings === "function") {
                void kp._refreshCurrentKeyboardLayoutFromSettings();
              }
            }
          } catch {
          }
        }
        this._notify(`Duplicated "${created.label || nextLabel}".`, "success");
        return created;
      } catch {
        this._notify("Failed to duplicate layout.", "error");
        return null;
      }
    }
    /**
     * Called by Keyboard Reference after a drop/delete so the palette assignment badges update.
     * @param {any} layout
     */
    syncUserLayout(layout) {
      if (!layout || !layout.id) return;
      this._st.mode = "user";
      this._st.userLayoutId = layout.id;
      this._st.userLayout = layout;
      try {
        const idx = (this._st.userLayouts || []).findIndex((l) => l && l.id === layout.id);
        if (idx >= 0) this._st.userLayouts[idx] = layout;
        else this._st.userLayouts = [...this._st.userLayouts || [], layout];
      } catch {
      }
      this._renderLayoutSelect();
      this._renderRightList();
      try {
        this._renderInspector();
      } catch {
      }
    }
    _emitChange() {
      try {
        this._onChange?.({ state: this._st });
      } catch {
      }
    }
    _setVisible(visible) {
      if (!this.root) return;
      if (visible) {
        try {
          this.root.hidden = false;
        } catch {
        }
        try {
          this.root.classList.remove("kpv2-hidden");
        } catch {
        }
        try {
          this.root.style.setProperty("display", "flex", "important");
          this.root.style.setProperty("pointer-events", "auto", "important");
        } catch {
          try {
            this.root.style.display = "flex";
          } catch {
          }
        }
        this._applyPanelPositionNow();
      } else {
        try {
          this.root.hidden = true;
        } catch {
        }
        try {
          this.root.classList.add("kpv2-hidden");
        } catch {
        }
        try {
          this.root.style.setProperty("display", "none", "important");
          this.root.style.setProperty("pointer-events", "none", "important");
        } catch {
          try {
            this.root.style.display = "none";
          } catch {
          }
        }
      }
    }
    _ensureStylesInjected(doc, shadowRoot) {
      try {
        if (!doc || !shadowRoot) return;
        try {
          doc.head?.querySelectorAll(`style[${CONFIG_STYLE_ATTR}]`).forEach((el2) => el2.remove());
          doc.querySelectorAll(`svg[${CONFIG_ICON_SPRITE_ATTR}]`).forEach((el2) => el2.remove());
        } catch {
        }
        ensureConfigIconSprite(shadowRoot);
        const attr = KEYBINDINGS_UI_STYLE_ATTR || "data-kp-keybindings-ui-style";
        const getURL = typeof chrome !== "undefined" && chrome?.runtime?.getURL ? chrome.runtime.getURL.bind(chrome.runtime) : null;
        const fontUrls = getURL ? {
          robotech: getURL("fonts/ROBOTECHGPRegular.ttf"),
          titillium: getURL("fonts/TitilliumTextRegular.otf"),
          titilliumBold: getURL("fonts/TitilliumTextBold.ttf"),
          cubellan: getURL("fonts/CubellanRegular.ttf"),
          ezarion: getURL("fonts/EzarionRegular.ttf"),
          dosis: getURL("fonts/DosisBook.ttf")
        } : void 0;
        const css = getKeybindingsUiCss({ zKeybindingsPopover: Z_INDEX.KEYBINDINGS_POPOVER, fontUrls });
        injectChromeStyles(shadowRoot, { attr, css });
        injectChromeStyles(shadowRoot, { attr: CONFIG_STYLE_ATTR, css: this._getPanelCss() });
        injectChromeStyles(doc, { attr: CONFIG_PLACE_ARROW_STYLE_ATTR, css: PLACE_ARROW_CSS });
      } catch {
      }
    }
    /**
     * NCT dark UI stylesheet for Layout Config chrome (Layout B mock tokens).
     * @returns {string}
     */
    _getPanelCss() {
      const c = NCT_DARK_UI_COLORS;
      const accentA = (a) => `rgba(74,144,200,${a})`;
      return `
.kp-layout-config-panel {
  font-family: var(--kp-font-ui, ${NCT_DARK_UI_FONT});
  font-size: var(--kp-type-ui-size, 12px);
  line-height: var(--kp-type-leading-body, 1.35);
  color: var(--kp-color-fg, ${ONBOARDING_METAL.fg});
  background-color: var(--kp-color-panel, #838383) !important;
  background: var(--kp-panel-bg) !important;
}
.kp-layout-config-panel .kp-cfg-ico {
  width: 12px;
  height: 12px;
  flex: 0 0 auto;
  fill: currentColor;
  color: var(--kp-icon-chrome, currentColor);
  display: inline-block;
  vertical-align: -1px;
}
.kp-layout-config-panel .kp-cfg-titlebar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  height: 28px;
  min-height: 28px;
  max-height: 28px;
  box-sizing: border-box;
  padding: 0 6px 0 10px;
  margin: 0;
  border-bottom: var(--kp-titlebar-border, ${ONBOARDING_METAL.titlebarBorder});
  box-shadow: var(--kp-titlebar-shadow, ${ONBOARDING_METAL.titlebarShadow});
  background: var(--kp-hatch-edit), var(--kp-titlebar-bg, ${ONBOARDING_METAL.titlebarBg});
  font-family: var(--kp-font-heading, ${NCT_DARK_UI_FONT});
  letter-spacing: var(--kp-type-tracking-titlebar, 0.02em);
  text-transform: var(--kp-type-transform-titlebar, none);
  flex: 0 0 auto;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
  position: relative;
}
.kp-layout-config-panel[data-kp-place-targeting="true"] .kp-cfg-titlebar {
  animation: kp-cfg-place-titlebar-pulse 1.15s ease-in-out infinite;
  isolation: isolate;
}
.kp-layout-config-panel[data-kp-place-targeting="true"] .kp-cfg-titlebar::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: rgba(144, 82, 255, 0.38);
  animation: kp-cfg-place-hatch-tint 1.15s ease-in-out infinite;
}
.kp-layout-config-panel[data-kp-place-targeting="true"] .kp-cfg-titlebar > * {
  position: relative;
  z-index: 1;
}
@keyframes kp-cfg-place-titlebar-pulse {
  0%, 100% { filter: brightness(1); box-shadow: inset 0 0 0 0 rgba(144, 82, 255, 0), var(--kp-titlebar-shadow, ${ONBOARDING_METAL.titlebarShadow}); }
  50% { filter: brightness(1.12); box-shadow: inset 0 0 0 2px rgba(193, 151, 255, 0.88), 0 0 14px rgba(144, 82, 255, 0.62); }
}
@keyframes kp-cfg-place-hatch-tint {
  0%, 100% { opacity: 0.08; }
  50% { opacity: 0.58; }
}
.kp-layout-config-panel .kp-cfg-titlebar-place-msg {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 3;
  max-width: calc(100% - 180px);
  padding: 2px 10px;
  border: 1px solid rgba(193, 151, 255, 0.78);
  border-radius: var(--kp-radius-btn, ${NCT_DARK_UI_BTN_RADIUS});
  background: rgba(104, 55, 190, 0.42);
  color: #f0e4ff;
  font-size: 11px;
  font-weight: 650;
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.28) inset;
}
.kp-layout-config-panel .kp-cfg-titlebar-place-msg[hidden] {
  display: none !important;
}
.kp-layout-config-panel .kp-cfg-titlebar-start {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  flex: 1 1 auto;
}
.kp-layout-config-panel .kp-cfg-title {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-weight: var(--kp-titlebar-title-weight, 700);
  letter-spacing: var(--kp-type-tracking-titlebar, 0.02em);
  text-transform: var(--kp-type-transform-titlebar, none);
  color: var(--kp-color-fg, ${ONBOARDING_METAL.fg});
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin: 0;
  padding: 0;
  line-height: 28px;
  min-width: 0;
}
.kp-layout-config-panel .kp-cfg-title-shortcut {
  font-family: var(--kp-font-kbd, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
  font-size: var(--kp-type-kbd-size, 10px);
  font-weight: var(--kp-type-weight-regular, 400);
  line-height: 1.2;
  text-transform: var(--kp-kbd-transform, none);
  letter-spacing: var(--kp-kbd-tracking, 0.02em);
  padding: 1px 6px;
  margin-left: 2px;
  border: var(--kp-kbd-border, ${ONBOARDING_METAL.kbdBorder});
  border-radius: var(--kp-radius-btn, ${NCT_DARK_UI_BTN_RADIUS});
  background: var(--kp-kbd-bg, ${ONBOARDING_METAL.kbdBg});
  color: var(--kp-color-kbd-fg, ${ONBOARDING_METAL.kbdColor});
  box-shadow: var(--kp-kbd-shadow, ${ONBOARDING_METAL.kbdShadow});
  flex-shrink: 0;
  box-sizing: border-box;
}
.kp-layout-config-panel .kp-cfg-titlebar-end {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  min-width: 0;
}
.kp-layout-config-panel .kp-cfg-autosaves-chip {
  flex: 0 0 auto;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.03em;
  line-height: 1.2;
  white-space: nowrap;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid rgba(28, 28, 28, 0.18);
  background: rgba(255, 255, 255, 0.42);
  color: ${ONBOARDING_METAL.fgDim};
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.55);
  pointer-events: none;
  user-select: none;
  box-sizing: border-box;
}
.kp-layout-config-panel .kp-cfg-close {
  height: 22px;
  min-height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  border-radius: 999px;
  border: 1px solid rgba(28, 28, 28, 0.18);
  background: rgba(255, 255, 255, 0.42);
  color: ${ONBOARDING_METAL.fgDim};
  cursor: pointer;
  padding: 0 8px;
  margin: 0;
  flex: 0 0 auto;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.55);
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1;
  white-space: nowrap;
  box-sizing: border-box;
}
.kp-layout-config-panel .kp-cfg-close .kp-cfg-ico {
  flex: 0 0 auto;
}
.kp-layout-config-panel .kp-cfg-close:hover {
  color: ${ONBOARDING_METAL.fg};
  background: rgba(255, 255, 255, 0.58);
  border-color: rgba(28, 28, 28, 0.28);
}
.kp-layout-config-panel .kp-cfg-body {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 0;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  /* Content view uses the same light gray metal as the onboarding panel. */
  background: ${ONBOARDING_METAL.panelBg};
  color: ${ONBOARDING_METAL.fg};
}
.kp-layout-config-panel [data-kp-layout-list].${KEYBINDINGS_UI_ROOT_CLASS} {
  gap: 12px;
}
.kp-layout-config-panel .kp-cfg-strip {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 0;
  border: 0;
  border-bottom: ${ONBOARDING_METAL.titlebarBorder};
  border-radius: 0;
  /* Same cool mid-gray metal as onboarding panel chrome. */
  background: ${ONBOARDING_METAL.panelBg};
  color: ${ONBOARDING_METAL.fg};
  box-shadow: ${ONBOARDING_METAL.titlebarShadow};
  flex: 0 0 auto;
  overflow: visible;
}
.kp-layout-config-panel .kp-cfg-strip .kp-cfg-strip-label {
  color: ${ONBOARDING_METAL.fgDim};
}
.kp-layout-config-panel .kp-cfg-strip .kp-cfg-hint {
  color: ${ONBOARDING_METAL.fgMute};
}
.kp-layout-config-panel .kp-cfg-strip .kp-cfg-tool-group {
  border: ${ONBOARDING_METAL.btnBorder};
  background: ${ONBOARDING_METAL.btnBg};
  box-shadow: ${ONBOARDING_METAL.btnShadow};
}
.kp-layout-config-panel .kp-cfg-strip .kp-cfg-tool-group .kp-cfg-btn {
  border: none;
  border-right: 1px solid rgba(0, 0, 0, 0.22);
  background: transparent;
  box-shadow: none;
  color: ${ONBOARDING_METAL.fg};
}
.kp-layout-config-panel .kp-cfg-strip .kp-cfg-tool-group .kp-cfg-btn:last-child {
  border-right: 0;
}
.kp-layout-config-panel .kp-cfg-strip .kp-cfg-tool-group .kp-cfg-btn:hover:not(:disabled) {
  filter: none;
  background: rgba(255, 255, 255, 0.22);
}
.kp-layout-config-panel .kp-cfg-strip .kp-cfg-handedness-seg {
  border: ${ONBOARDING_METAL.btnBorder};
  background: ${ONBOARDING_METAL.btnBg};
  box-shadow: ${ONBOARDING_METAL.btnShadow};
}
.kp-layout-config-panel .kp-cfg-strip .kp-cfg-handedness-seg .kp-cfg-seg-btn {
  border-right-color: rgba(0, 0, 0, 0.22);
  color: ${ONBOARDING_METAL.fgDim};
}
.kp-layout-config-panel .kp-cfg-strip .kp-cfg-handedness-seg .kp-cfg-seg-btn:hover {
  color: ${ONBOARDING_METAL.fg};
  background: rgba(255, 255, 255, 0.3);
}
.kp-layout-config-panel .kp-cfg-strip .kp-cfg-handedness-seg .kp-cfg-seg-btn[aria-selected="true"] {
  background: ${NCT_DARK_UI_SELECTED_TINT};
  color: ${NCT_DARK_UI_SELECTED_TEXT};
  box-shadow: none;
}
.kp-layout-config-panel .kp-cfg-strip .kp-cfg-tool-sep {
  background: rgba(0, 0, 0, 0.28);
}
.kp-layout-config-panel .kp-cfg-strip .kp-cfg-layout-identity .kp-cfg-btn,
.kp-layout-config-panel .kp-cfg-strip .kp-cfg-layout-primary .kp-cfg-btn:not(.kp-cfg-btn-lit):not([aria-pressed="true"]),
.kp-layout-config-panel .kp-cfg-strip .kp-cfg-opts-wrap > .kp-cfg-btn {
  border: ${ONBOARDING_METAL.btnBorder};
  background: ${ONBOARDING_METAL.btnBg};
  color: ${ONBOARDING_METAL.fg};
  box-shadow: ${ONBOARDING_METAL.btnShadow};
}
.kp-layout-config-panel .kp-cfg-strip .kp-cfg-layout-identity .kp-cfg-btn:hover:not(:disabled),
.kp-layout-config-panel .kp-cfg-strip .kp-cfg-layout-primary .kp-cfg-btn:not(.kp-cfg-btn-lit):not([aria-pressed="true"]):hover:not(:disabled),
.kp-layout-config-panel .kp-cfg-strip .kp-cfg-opts-wrap > .kp-cfg-btn:hover:not(:disabled) {
  filter: brightness(1.06);
  background: linear-gradient(180deg, #d0d0d0 0%, #aeaeae 50%, #969696 100%);
}
.kp-layout-config-panel .kp-cfg-strip .kp-cfg-btn.kp-cfg-btn-lit,
.kp-layout-config-panel .kp-cfg-strip .kp-cfg-btn[aria-pressed="true"] {
  background: ${NCT_DARK_UI_BTN_LIT_GRADIENT};
  border: ${NCT_DARK_UI_BTN_LIT_BORDER};
  color: ${NCT_DARK_UI_SELECTED_TEXT};
  box-shadow: inset 0 1px 0 rgba(200,220,240,0.18);
  filter: none;
}
.kp-layout-config-panel .kp-cfg-strip-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
}
.kp-layout-config-panel .kp-cfg-layout-strip {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 10px;
  height: 32px;
  padding: 0 10px;
  min-width: 0;
  overflow: visible;
}
.kp-layout-config-panel .kp-cfg-layout-identity {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 1 auto;
  min-width: 0;
  height: 22px;
}
.kp-layout-config-panel .kp-cfg-strip-label {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: ${ONBOARDING_METAL.fgDim};
  white-space: nowrap;
  line-height: 22px;
  margin: 0;
}
.kp-layout-config-panel .kp-cfg-layout-combo {
  position: relative;
  display: flex;
  align-items: stretch;
  width: 240px;
  flex: 0 1 240px;
  min-width: 160px;
  height: 22px;
  border: ${NCT_DARK_UI_FIELD_BORDER};
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  background: ${NCT_DARK_UI_FIELD_BACKGROUND};
  box-shadow: ${NCT_DARK_UI_FIELD_BOX_SHADOW};
}
.kp-layout-config-panel .kp-cfg-layout-combo:focus-within {
  border-color: ${NCT_DARK_UI_FIELD_FOCUS_BORDER};
  box-shadow: ${NCT_DARK_UI_FIELD_FOCUS_BOX_SHADOW};
}
.kp-layout-config-panel .kp-cfg-layout-combo.is-builtin .kp-cfg-combo-input {
  color: #9aacbe;
}
.kp-layout-config-panel .kp-cfg-combo-input {
  flex: 1 1 auto;
  min-width: 0;
  width: auto;
  height: 100%;
  border: 0 !important;
  border-radius: 0 !important;
  background: transparent !important;
  box-shadow: none !important;
  padding: 0 8px !important;
  color: ${c.fg};
  font: 11px/22px ${NCT_DARK_UI_FONT};
  outline: none;
}
.kp-layout-config-panel .kp-cfg-combo-current-badge {
  display: none;
  align-items: center;
  gap: 3px;
  flex: 0 0 auto;
  margin-right: 4px;
  padding: 0 5px 0 4px;
  height: 16px;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  background: ${accentA(0.22)};
  color: #9ec8e8;
  font: 8px/1 ${NCT_DARK_UI_FONT};
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  pointer-events: none;
  user-select: none;
}
.kp-layout-config-panel .kp-cfg-layout-combo.is-current .kp-cfg-combo-current-badge {
  display: inline-flex;
}
.kp-layout-config-panel .kp-cfg-combo-current-badge .kp-cfg-ico {
  width: 8px;
  height: 8px;
}
.kp-layout-config-panel .kp-cfg-combo-toggle {
  flex: 0 0 22px;
  width: 22px;
  height: 100%;
  padding: 0;
  border: 0;
  border-left: 1px solid ${c.fieldEdge};
  border-radius: 0;
  background: linear-gradient(180deg, #3a4450 0%, #2a323c 55%, #222830 100%);
  color: #b8c4d0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.kp-layout-config-panel .kp-cfg-combo-toggle:hover {
  filter: brightness(1.1);
  color: #e8f0f8;
}
.kp-layout-config-panel .kp-cfg-combo-toggle .kp-cfg-ico {
  width: 9px;
  height: 9px;
}
.kp-layout-config-panel .kp-cfg-combo-list {
  position: absolute;
  top: calc(100% + 2px);
  left: 0;
  right: 0;
  z-index: 40;
  margin: 0;
  padding: 2px;
  list-style: none;
  max-height: 180px;
  overflow: auto;
  border: 1px solid #1a2430;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  background: #1a222c;
  box-shadow: 0 10px 24px rgba(0,0,0,0.5);
}
.kp-layout-config-panel .kp-cfg-combo-list[hidden] {
  display: none !important;
}
.kp-layout-config-panel .kp-cfg-combo-group {
  padding: 5px 8px 2px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(170, 180, 195, 0.72);
  pointer-events: none;
  user-select: none;
}
.kp-layout-config-panel .kp-cfg-combo-group:not(:first-child) {
  margin-top: 4px;
  padding-top: 6px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}
.kp-layout-config-panel .kp-cfg-combo-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  text-align: left;
  padding: 4px 8px;
  border: 0;
  border-radius: 1px;
  background: transparent;
  color: #c8d4e0;
  cursor: pointer;
  font: 11px/1.3 ${NCT_DARK_UI_FONT};
}
.kp-layout-config-panel .kp-cfg-combo-option:hover,
.kp-layout-config-panel .kp-cfg-combo-option.is-active {
  background: ${NCT_DARK_UI_SELECTED_TINT};
  color: ${NCT_DARK_UI_SELECTED_TEXT};
}
.kp-layout-config-panel .kp-cfg-combo-option-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.kp-layout-config-panel .kp-cfg-combo-option-current {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  flex: 0 0 auto;
  font-size: 8px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #9ec8e8;
}
.kp-layout-config-panel .kp-cfg-combo-option-current .kp-cfg-ico {
  width: 8px;
  height: 8px;
}
.kp-layout-config-panel .kp-cfg-layout-tools {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  height: 22px;
}
.kp-layout-config-panel .kp-cfg-layout-primary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  margin-left: auto;
  height: 22px;
}
.kp-layout-config-panel .kp-cfg-handedness-seg {
  flex: 0 0 auto;
  height: 22px;
  align-self: center;
}
.kp-layout-config-panel .kp-cfg-handedness-seg[hidden] {
  display: none !important;
}
.kp-layout-config-panel .kp-cfg-handedness-seg .kp-cfg-seg-btn {
  height: 100%;
  padding: 0 8px;
  font-size: 9px;
}
.kp-layout-config-panel .kp-cfg-tool-group {
  display: inline-flex;
  gap: 0;
  align-items: stretch;
  height: 22px;
  border: ${NCT_DARK_UI_BTN_BORDER};
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  overflow: hidden;
  background: ${NCT_DARK_UI_BTN_GRADIENT};
}
.kp-layout-config-panel .kp-cfg-tool-group .kp-cfg-btn {
  border: none;
  border-radius: 0;
  border-right: 1px solid ${c.panelEdgeDark};
  background: transparent;
  box-shadow: none;
  height: 100%;
  padding: 0 7px;
}
.kp-layout-config-panel .kp-cfg-tool-group .kp-cfg-btn:last-child {
  border-right: 0;
}
.kp-layout-config-panel .kp-cfg-tool-group .kp-cfg-btn.kp-cfg-btn-icon {
  width: 26px;
  padding: 0;
}
.kp-layout-config-panel .kp-cfg-btn.kp-cfg-btn-icon {
  width: 22px;
  min-width: 22px;
  padding: 0;
}
.kp-layout-config-panel .kp-cfg-tool-sep {
  width: 1px;
  align-self: stretch;
  min-height: 18px;
  background: ${c.panelEdgeDark};
  margin: 0 2px;
}
.kp-layout-config-panel .kp-cfg-spacer {
  flex: 1 1 auto;
}
.kp-layout-config-panel .kp-cfg-opts-wrap {
  position: relative;
  flex: 0 0 auto;
}
.kp-layout-config-panel .kp-cfg-opts-menu {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 40;
  min-width: 160px;
  padding: 6px 8px;
  border: 1px solid #1a2430;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  background: #1a222c;
  box-shadow: 0 10px 24px rgba(0,0,0,0.5);
}
.kp-layout-config-panel .kp-cfg-opts-menu[hidden] {
  display: none !important;
}
.kp-layout-config-panel .kp-cfg-opts-menu .kp-cfg-opts-check {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 11px;
  color: #c8d4e0;
  cursor: pointer;
  white-space: nowrap;
  user-select: none;
}
.kp-layout-config-panel .kp-cfg-strip-hint {
  padding: 0 10px 6px;
  margin: 0;
}
.kp-layout-config-panel .kp-cfg-main {
  display: flex;
  flex-direction: row;
  align-items: stretch;
  gap: 0;
  flex: 1 1 auto;
  min-height: 0;
  margin: 6px 8px 8px;
  --kp-cfg-inspector-width: ${CONFIG_INSPECTOR_WIDTH_PX}px;
  --kp-cfg-create-height: 46%;
}
.kp-layout-config-panel .kp-cfg-workspace {
  display: flex;
  flex-direction: column;
  gap: 0;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
}
.kp-layout-config-panel .kp-cfg-pane {
  display: flex;
  flex-direction: column;
  min-height: 0;
  border: ${ONBOARDING_METAL.panelBorder};
  border-radius: ${NCT_DARK_UI_PANEL_RADIUS};
  background: linear-gradient(180deg, #9d9d9d 0%, #8b8b8b 55%, #7d7d7d 100%);
  color: ${ONBOARDING_METAL.fg};
  overflow: hidden;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.22) inset;
}
.kp-layout-config-panel .kp-cfg-pane-library {
  flex: 1 1 auto;
  min-height: 0;
  background:
    linear-gradient(180deg, rgba(90, 120, 70, 0.22), rgba(55, 75, 45, 0.14)),
    linear-gradient(180deg, #6e6e6e 0%, #5a5a5a 55%, #4a4a4a 100%);
  border-color: rgba(52, 66, 40, 0.92);
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.16) inset,
    0 0 10px rgba(120, 170, 80, 0.1);
}
.kp-layout-config-panel .kp-cfg-pane-library .kp-cfg-pane-hdr {
  flex: 0 0 auto;
}
.kp-layout-config-panel .kp-cfg-pane-library .kp-cfg-pane-scroll {
  /* Keep list shrinkable so the User Macros splitter can reach the titlebar. */
  min-height: 0;
}
.kp-layout-config-panel .kp-cfg-pane-library .kp-cfg-pane-hdr {
  background: linear-gradient(180deg, #a8b596 0%, #8d9a7c 45%, #7a8669 100%);
  color: ${ONBOARDING_METAL.fg};
  border-bottom-color: rgba(52, 66, 40, 0.92);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.3);
}
.kp-layout-config-panel .kp-cfg-pane-library .kp-cfg-pane-title {
  color: ${ONBOARDING_METAL.fg};
}
.kp-layout-config-panel .kp-cfg-pane-hdr {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  padding: 4px 8px;
  min-height: 22px;
  border-bottom: ${ONBOARDING_METAL.titlebarBorder};
  background: ${ONBOARDING_METAL.titlebarBg};
  color: ${ONBOARDING_METAL.fg};
  flex: 0 0 auto;
}
.kp-layout-config-panel .kp-cfg-pane-title {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: ${ONBOARDING_METAL.fgDim};
  white-space: nowrap;
}
.kp-layout-config-panel .kp-cfg-pane-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 8px;
}
.kp-layout-config-panel .kp-cfg-seg {
  display: inline-flex;
  gap: 0;
  padding: 0;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  border: ${NCT_DARK_UI_BTN_BORDER};
  background: ${NCT_DARK_UI_BTN_GRADIENT};
  overflow: hidden;
}
.kp-layout-config-panel .kp-cfg-seg-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0 8px;
  height: 18px;
  border: none;
  border-right: 1px solid ${c.panelEdgeDark};
  border-radius: 0;
  background: transparent;
  color: ${c.fgDim};
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.02em;
  cursor: pointer;
  white-space: nowrap;
  font-family: inherit;
}
.kp-layout-config-panel .kp-cfg-seg-btn .kp-cfg-ico {
  width: 10px;
  height: 10px;
  opacity: 0.85;
}
.kp-layout-config-panel .kp-cfg-seg-btn:last-child {
  border-right: 0;
}
.kp-layout-config-panel .kp-cfg-seg-btn:hover {
  color: ${c.fg};
  background: ${NCT_DARK_UI_HOVER_TINT};
}
.kp-layout-config-panel .kp-cfg-seg-btn[aria-selected="true"] {
  background: ${NCT_DARK_UI_SELECTED_TINT};
  color: ${NCT_DARK_UI_SELECTED_TEXT};
  box-shadow: ${NCT_DARK_UI_FOCUS_RING};
}
.kp-layout-config-panel .kp-cfg-search-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  flex: 1 1 130px;
  min-width: 110px;
  max-width: 240px;
}
.kp-layout-config-panel .kp-cfg-search-wrap .kp-cfg-ico {
  position: absolute;
  left: 6px;
  width: 10px;
  height: 10px;
  opacity: 0.55;
  pointer-events: none;
}
.kp-layout-config-panel .kp-cfg-search {
  width: 100%;
  flex: 1 1 auto;
  min-width: 0;
  max-width: none;
  padding: 2px 7px 2px 22px;
  height: 18px;
  font-size: 10px;
  background: #e8e8e8;
  border-color: #4a4a4a;
  color: ${ONBOARDING_METAL.fg};
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.28);
}
/* Controls that sit on the light metal pane header / library instructions. */
.kp-layout-config-panel .kp-cfg-pane-hdr .kp-cfg-seg,
.kp-layout-config-panel .kp-cfg-lib-instructions .kp-cfg-seg {
  border: ${ONBOARDING_METAL.btnBorder};
  background: ${ONBOARDING_METAL.btnBg};
  box-shadow: ${ONBOARDING_METAL.btnShadow};
}
.kp-layout-config-panel .kp-cfg-pane-hdr .kp-cfg-seg-btn,
.kp-layout-config-panel .kp-cfg-lib-instructions .kp-cfg-seg-btn {
  border-right-color: rgba(0, 0, 0, 0.22);
  color: ${ONBOARDING_METAL.fgDim};
}
.kp-layout-config-panel .kp-cfg-pane-hdr .kp-cfg-seg-btn:hover,
.kp-layout-config-panel .kp-cfg-lib-instructions .kp-cfg-seg-btn:hover {
  color: ${ONBOARDING_METAL.fg};
  background: rgba(255, 255, 255, 0.3);
}
.kp-layout-config-panel .kp-cfg-fn-cat-wrap {
  display: inline-flex;
  align-items: stretch;
  box-sizing: border-box;
  width: 140px;
  min-width: 100px;
  max-width: min(320px, 100%);
  height: 18px;
  padding: 0;
  border: 0;
  background: transparent;
  box-shadow: none;
  color: ${ONBOARDING_METAL.fg};
  resize: horizontal;
  overflow: hidden;
}
.kp-layout-config-panel .kp-cfg-fn-cat-wrap[hidden] {
  display: none !important;
}
.kp-layout-config-panel .kp-cfg-fn-cat {
  width: 100%;
  min-width: 0;
  max-width: none;
  padding: 0 6px;
  height: 100%;
  font-size: 9px;
  border: 0;
  background: transparent;
  box-sizing: border-box;
}
/* Custom select copies .kp-cfg-field onto the wrapper; chrome belongs on the trigger. */
.kp-layout-config-panel .kp-cfg-fn-cat-wrap .kp-select {
  display: flex;
  flex: 1 1 auto;
  width: 100%;
  height: 100%;
  min-width: 0;
  padding: 0;
  margin: 0;
  border: 0;
  background: transparent;
  box-shadow: none;
}
.kp-layout-config-panel .kp-cfg-fn-cat-wrap .kp-select-trigger {
  width: 100%;
  height: 100%;
  padding: 0 6px;
  border: 1px solid #4a4a4a;
  border-radius: var(--kp-radius-btn, ${NCT_DARK_UI_BTN_RADIUS});
  background: #e8e8e8;
  color: ${ONBOARDING_METAL.fg};
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.28);
  font-size: inherit;
}
.kp-layout-config-panel .kp-cfg-lib-instructions {
  flex: 0 0 auto;
  margin: 0;
  padding: 6px 8px 8px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.28);
  background: rgba(0, 0, 0, 0.12);
  font-size: 12px;
  line-height: 1.4;
  color: rgba(235, 235, 235, 0.82);
}
.kp-layout-config-panel .kp-cfg-lib-instructions-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
  flex: 0 0 auto;
  text-align: left;
}
.kp-layout-config-panel .kp-cfg-lib-instructions-toggle:hover {
  color: rgba(255, 255, 255, 0.95);
}
.kp-layout-config-panel .kp-cfg-lib-instructions-toggle .kp-cfg-ico {
  width: 12px;
  height: 12px;
  flex: 0 0 auto;
  transition: transform 0.12s ease;
}
.kp-layout-config-panel .kp-cfg-lib-instructions.is-collapsed .kp-cfg-lib-instructions-toggle .kp-cfg-ico {
  transform: rotate(-90deg);
}
.kp-layout-config-panel .kp-cfg-lib-instructions-head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 3px;
}
.kp-layout-config-panel .kp-cfg-lib-instructions-head .kp-cfg-seg {
  flex: 0 0 auto;
  order: -1;
}
.kp-layout-config-panel .kp-cfg-lib-instructions.is-collapsed .kp-cfg-lib-instructions-head {
  margin-bottom: 0;
}
.kp-layout-config-panel .kp-cfg-lib-instructions-title {
  font-weight: 700;
  font-size: 13px;
  color: rgba(245, 245, 245, 0.94);
  margin: 0;
  flex: 0 0 auto;
}
.kp-layout-config-panel .kp-cfg-lib-instructions-body {
  margin: 0;
  font-size: 12px;
  line-height: 1.4;
}
.kp-layout-config-panel .kp-cfg-lib-instructions.is-collapsed .kp-cfg-lib-instructions-body {
  display: none;
}
.kp-layout-config-panel .kp-cfg-lib-place-pulse {
  flex: 1 1 180px;
  min-width: 0;
  font-size: 11px;
  font-weight: 650;
  line-height: 1.3;
  padding: 4px 8px;
  border: 1px solid rgba(193, 151, 255, 0.72);
  border-radius: var(--kp-radius-btn, ${NCT_DARK_UI_BTN_RADIUS});
  background: rgba(104, 55, 190, 0.34);
  color: #f0e4ff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.28) inset;
  animation: kp-cfg-place-pulse 1.15s ease-in-out infinite;
}
@keyframes kp-cfg-place-pulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(144, 82, 255, 0.18), 0 0 0 1px rgba(0, 0, 0, 0.28) inset; }
  50% { opacity: 0.72; box-shadow: 0 0 12px 2px rgba(144, 82, 255, 0.62), 0 0 0 1px rgba(0, 0, 0, 0.28) inset; }
}
${getHierarchicalTableCss({ rootSelector: ".kp-layout-config-panel" })}
.kp-layout-config-panel .kp-hier-table tr.kp-cfg-lib-row-inspecting {
  background: rgba(74, 144, 200, 0.18);
  outline: 1px solid rgba(74, 144, 200, 0.35);
}
.kp-layout-config-panel .kp-hier-table tr.kp-cfg-lib-row-place {
  box-shadow: inset 2px 0 0 ${c.accent};
}
.kp-layout-config-panel .kp-hier-label-cell .kp-cfg-ico {
  width: 12px;
  height: 12px;
  opacity: 0.9;
  flex: 0 0 auto;
}
.kp-layout-config-panel [data-kp-layout-list].${KEYBINDINGS_UI_ROOT_CLASS} .kp-hier-table .key.kp-cfg-lib-key {
  flex: 0 0 auto !important;
  width: 22px !important;
  min-width: 22px !important;
  max-width: 22px !important;
  height: 22px !important;
  min-height: 22px !important;
  max-height: 22px !important;
  border-radius: var(--kp-key-shape-radius, var(--kp-key-effective-radius, var(--kp-radius-key, 7px)));
  corner-shape: var(--kp-key-corner-shape, round);
  cursor: grab;
  padding: 0 !important;
  margin: 0;
  display: block;
  position: relative;
  overflow: visible;
  box-sizing: border-box;
  box-shadow:
    2px 2px 1px rgba(0, 0, 0, 0.88),
    0 1px 0 rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.1),
    inset 0 -1px 0 rgba(0, 0, 0, 0.18);
}
.kp-layout-config-panel [data-kp-layout-list].${KEYBINDINGS_UI_ROOT_CLASS} .kp-hier-table .key.kp-cfg-lib-key:active {
  cursor: grabbing;
}
.kp-layout-config-panel [data-kp-layout-list].${KEYBINDINGS_UI_ROOT_CLASS} .kp-hier-table .key.kp-cfg-lib-key > .key-bg-icon {
  inset: 10%;
  -webkit-mask-size: 82% 82%;
  mask-size: 82% 82%;
  opacity: 0.9;
}
.kp-layout-config-panel [data-kp-layout-list].${KEYBINDINGS_UI_ROOT_CLASS} .kp-hier-table .key.kp-cfg-lib-key > .key-main,
.kp-layout-config-panel [data-kp-layout-list].${KEYBINDINGS_UI_ROOT_CLASS} .kp-hier-table .key.kp-cfg-lib-key > .key-label {
  display: none !important;
}
.kp-layout-config-panel [data-kp-layout-list].${KEYBINDINGS_UI_ROOT_CLASS} .kp-hier-table .key.kp-cfg-lib-key.kp-place-source {
  outline: 2px solid ${c.accent};
  outline-offset: 0;
}
.kp-layout-config-panel .kp-hier-table .kp-cfg-card-actions {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 4px;
  justify-content: flex-end;
}
.kp-layout-config-panel .kp-cfg-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 12px;
  padding: 2px 2px 8px;
  flex: 0 0 auto;
  font-size: 9px;
  color: #6a7a58;
}
.kp-layout-config-panel .kp-cfg-legend span {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.kp-layout-config-panel .kp-cfg-legend i {
  display: inline-block;
  width: 12px;
  height: 10px;
  border-radius: 1px;
  border: 1px solid #444;
  box-sizing: border-box;
}
.kp-layout-config-panel .kp-cfg-legend .kp-cfg-sw-stock-fn {
  background: #2a2e32;
  border-color: #3a4550;
}
.kp-layout-config-panel .kp-cfg-legend .kp-cfg-sw-user {
  background: #2a2e32;
  border-color: ${accentA(0.45)};
}
.kp-layout-config-panel .kp-cfg-legend .kp-cfg-sw-stock-macro {
  background: #243430;
  border-left: 3px solid #5a9a8a;
}
.kp-layout-config-panel .kp-cfg-legend .kp-cfg-sw-user-macro {
  background: #2c2434;
  border-left: 3px solid #9a7ab8;
}
.kp-layout-config-panel .kp-cfg-category {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0 0 14px;
  padding: 10px 8px 8px;
  border: 1px solid rgba(120, 140, 100, 0.1);
  border-radius: 2px;
  background: rgba(8, 10, 8, 0.35);
  position: relative;
}
.kp-layout-config-panel .kp-cfg-category-title {
  position: absolute;
  top: -6px;
  left: 8px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 0 5px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #8a9a78;
  background: #121410;
}
.kp-layout-config-panel .kp-cfg-desc-fieldset {
  margin: 4px 0 2px;
  padding: 6px 8px 7px;
  min-width: 0;
  border: 1px solid rgba(120, 140, 100, 0.14);
  border-radius: 2px;
  background: rgba(0, 0, 0, 0.18);
}
.kp-layout-config-panel .kp-cfg-desc-line {
  margin: 0;
  font-size: 11px;
  line-height: 1.35;
  color: #9aaa90;
}
.kp-layout-config-panel .kp-cfg-category[data-kp-section="macros"] .kp-cfg-desc-fieldset {
  border-color: rgba(154, 122, 184, 0.16);
}
.kp-layout-config-panel .kp-cfg-category[data-kp-section="macros"] .kp-cfg-desc-line {
  color: #a090b0;
}
.kp-layout-config-panel .kp-cfg-category[data-kp-section="macroKeys"] .kp-cfg-desc-fieldset {
  border-color: rgba(90, 140, 180, 0.16);
}
.kp-layout-config-panel .kp-cfg-category[data-kp-section="macroKeys"] .kp-cfg-desc-line {
  color: #8a9aaa;
}
.kp-layout-config-panel .kp-cfg-category[data-kp-section="macros"] {
  border-color: rgba(154, 122, 184, 0.12);
  background: rgba(10, 8, 14, 0.35);
}
.kp-layout-config-panel .kp-cfg-category[data-kp-section="macros"] .kp-cfg-category-title {
  color: #9a8aaa;
}
.kp-layout-config-panel .kp-cfg-category[data-kp-section="macroKeys"] {
  border-color: rgba(90, 140, 180, 0.12);
  background: rgba(8, 10, 14, 0.35);
}
.kp-layout-config-panel .kp-cfg-category[data-kp-section="macroKeys"] .kp-cfg-category-title {
  color: #7a92a8;
}
.kp-layout-config-panel .kp-cfg-macro-subgroups {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 2px;
}
.kp-layout-config-panel .kp-cfg-macro-fieldset {
  margin: 0;
  padding: 10px 8px 8px;
  min-width: 0;
  border: 1px solid rgba(154, 122, 184, 0.08);
  border-radius: 2px;
  background: rgba(0, 0, 0, 0.12);
}
.kp-layout-config-panel .kp-cfg-macro-fieldset[data-macro-group="stock"] {
  border-color: rgba(120, 180, 150, 0.1);
  background: rgba(8, 14, 12, 0.18);
}
.kp-layout-config-panel .kp-cfg-macro-fieldset[data-macro-group="user"] {
  border-color: rgba(170, 130, 210, 0.1);
  background: rgba(14, 10, 18, 0.18);
}
.kp-layout-config-panel .kp-cfg-macro-fieldset > legend {
  padding: 0 6px;
  margin-left: 2px;
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #7a6a8a;
}
.kp-layout-config-panel .kp-cfg-macro-fieldset[data-macro-group="stock"] > legend {
  color: #6a9080;
}
.kp-layout-config-panel .kp-cfg-macro-fieldset[data-macro-group="user"] > legend {
  color: #9a88b0;
}
.kp-layout-config-panel .kp-cfg-subgroup-empty {
  margin: 0;
  padding: 4px 2px;
  font-size: 10px;
  color: #6a5a7a;
  font-style: italic;
}
.kp-layout-config-panel .kp-cfg-instance-fieldset {
  grid-column: 1 / -1;
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
  padding: 10px 8px 8px;
  min-width: 0;
  border: 1px solid #3a4550;
  border-radius: 2px;
  background: rgba(8, 12, 16, 0.28);
}
.kp-layout-config-panel .kp-cfg-instance-fieldset > legend {
  padding: 0 6px;
  margin-left: 2px;
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #8a9aaa;
}
.kp-layout-config-panel .kp-cfg-instance-new {
  align-self: flex-start;
}
.kp-layout-config-panel .kp-cfg-key-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
  justify-content: stretch;
  width: 100%;
  margin: 0;
  padding: 0;
}
.kp-layout-config-panel .kp-cfg-main.kp-cfg-inspector-open .kp-cfg-key-grid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.kp-layout-config-panel .kp-cfg-key-grid.kp-cfg-grid-macros {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.kp-layout-config-panel .kp-cfg-main.kp-cfg-inspector-open .kp-cfg-key-grid.kp-cfg-grid-macros {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.kp-layout-config-panel .kp-cfg-item {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 7px;
  min-width: 0;
  padding: 5px;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  border: ${ONBOARDING_METAL.btnBorder};
  background: ${ONBOARDING_METAL.btnBg};
  color: ${ONBOARDING_METAL.fg};
  box-shadow: ${ONBOARDING_METAL.btnShadow};
  box-sizing: border-box;
  cursor: default;
}
.kp-layout-config-panel .kp-cfg-item.kp-cfg-variant-stock-fn,
.kp-layout-config-panel .kp-cfg-item.kp-cfg-variant-configurable-fn {
  border-color: #4a4a4a;
}
.kp-layout-config-panel .kp-cfg-item.kp-cfg-variant-stock-macro {
  background: linear-gradient(180deg, #c4cfc6 0%, #a4b0a6 50%, #8e9a90 100%);
  border-color: #3f5a50;
  border-left: 3px solid #3f7a68;
}
.kp-layout-config-panel .kp-cfg-item.kp-cfg-variant-user-macro {
  background: linear-gradient(180deg, #cbc2d4 0%, #aba1b6 50%, #948aa0 100%);
  border-color: #4f4260;
  border-left: 3px solid #6d519a;
}
.kp-layout-config-panel .kp-cfg-item.kp-cfg-variant-macro-key {
  background: linear-gradient(180deg, #bdbdbd 0%, #a0a0a0 50%, #8c8c8c 100%);
  border-color: #4a4a4a;
}
.kp-layout-config-panel .kp-cfg-item.kp-cfg-variant-stock-macro .key {
  background: linear-gradient(180deg, #3a5a50, #2a4038);
  color: #c8ebe0;
}
.kp-layout-config-panel .kp-cfg-item.kp-cfg-variant-user-macro .key {
  background: linear-gradient(180deg, #5a4a6a, #3a3048);
  color: #e0d0f0;
}
.kp-layout-config-panel .kp-cfg-item.kp-cfg-variant-user-macro .kp-cfg-card-name {
  color: #3a2a4a;
  font-style: italic;
}
.kp-layout-config-panel .kp-cfg-item:hover {
  filter: brightness(1.06);
}
.kp-layout-config-panel .kp-cfg-item.kp-place-source-item {
  border-color: ${c.accent};
  box-shadow: 0 0 0 1px ${accentA(0.4)};
}
.kp-layout-config-panel .kp-cfg-item.kp-cfg-item-inspecting {
  border-color: ${c.accent};
  background: linear-gradient(180deg, #b6cfe4 0%, #9bb8d0 50%, #869fb6 100%);
}
.kp-layout-config-panel .kp-cfg-card-meta {
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 1 1 auto;
  min-width: 0;
}
.kp-layout-config-panel .kp-cfg-card-name {
  font-size: 11px;
  font-weight: 600;
  line-height: 1.25;
  color: ${ONBOARDING_METAL.fg};
  word-break: break-word;
}
.kp-layout-config-panel .kp-cfg-card-sub {
  font-size: 10px;
  color: ${ONBOARDING_METAL.fgMute};
  line-height: 1.25;
  word-break: break-word;
}
.kp-layout-config-panel .kp-cfg-card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  align-items: center;
}
.kp-layout-config-panel .kp-cfg-item .kp-cfg-btn {
  border: ${ONBOARDING_METAL.btnBorder};
  background: linear-gradient(180deg, #d2d2d2 0%, #b2b2b2 50%, #9a9a9a 100%);
  color: ${ONBOARDING_METAL.fg};
  box-shadow: ${ONBOARDING_METAL.btnShadow};
}
.kp-layout-config-panel .kp-cfg-item .kp-cfg-btn:hover:not(:disabled) {
  filter: brightness(1.06);
  background: linear-gradient(180deg, #dcdcdc 0%, #bcbcbc 50%, #a4a4a4 100%);
}
.kp-layout-config-panel [data-kp-layout-list].${KEYBINDINGS_UI_ROOT_CLASS} .kp-cfg-item .key {
  flex: 0 0 auto !important;
  width: ${CONFIG_KEY_SIZE_PX}px !important;
  min-width: ${CONFIG_KEY_SIZE_PX}px !important;
  max-width: ${CONFIG_KEY_SIZE_PX}px !important;
  height: ${CONFIG_KEY_SIZE_PX}px !important;
  min-height: ${CONFIG_KEY_SIZE_PX}px !important;
  max-height: ${CONFIG_KEY_SIZE_PX}px !important;
  cursor: pointer;
  position: relative;
  overflow: visible;
  border-radius: var(--kp-key-shape-radius, var(--kp-key-effective-radius, var(--kp-radius-key, 7px)));
  corner-shape: var(--kp-key-corner-shape, round);
  box-shadow:
    2px 2px 1px rgba(0, 0, 0, 0.88),
    0 1px 0 rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.1),
    inset 0 -1px 0 rgba(0, 0, 0, 0.18);
}
.kp-layout-config-panel [data-kp-layout-list].${KEYBINDINGS_UI_ROOT_CLASS} .kp-cfg-item .key:hover {
  box-shadow:
    2px 2px 1px rgba(0, 0, 0, 0.92),
    0 1px 0 rgba(0, 0, 0, 0.4),
    0 3px 8px rgba(0, 0, 0, 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.12),
    inset 0 -1px 0 rgba(0, 0, 0, 0.16);
}
.kp-layout-config-panel [data-kp-layout-list].${KEYBINDINGS_UI_ROOT_CLASS} .key.kp-place-source {
  outline: 2px solid ${c.accent};
  outline-offset: -1px;
}
.kp-layout-config-panel [data-kp-layout-list].${KEYBINDINGS_UI_ROOT_CLASS} .key > .key-bg-icon {
  inset: 10%;
  -webkit-mask-size: 78% 78%;
  mask-size: 78% 78%;
  opacity: 0.88;
}
.kp-layout-config-panel [data-kp-layout-list].${KEYBINDINGS_UI_ROOT_CLASS} .key .key-main {
  top: 4px !important;
  left: 3px !important;
  right: 3px !important;
  max-width: 100%;
  max-height: calc(${CONFIG_KEY_SIZE_PX}px - 22px) !important;
  overflow: hidden;
  text-overflow: clip;
  white-space: normal !important;
  word-break: break-word;
  overflow-wrap: anywhere;
  display: block !important;
  -webkit-line-clamp: unset !important;
  line-clamp: unset !important;
  font-size: 11px !important;
  line-height: 1.15 !important;
  text-transform: none;
}
.kp-layout-config-panel [data-kp-layout-list].${KEYBINDINGS_UI_ROOT_CLASS} .key .key-main .kp-cfg-label-line {
  display: block;
}
.kp-layout-config-panel .kp-cfg-inspect {
  flex: 0 0 auto;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 2px 6px;
  height: 18px;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  border: ${NCT_DARK_UI_BTN_BORDER};
  background: ${NCT_DARK_UI_BTN_GRADIENT};
  color: ${c.fg};
  cursor: pointer;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1.2;
  text-align: center;
  white-space: nowrap;
  font-family: inherit;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.06);
}
.kp-layout-config-panel .kp-cfg-inspect .kp-cfg-ico {
  width: 10px;
  height: 10px;
}
.kp-layout-config-panel .kp-cfg-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 4px 8px;
  height: 22px;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  border: ${NCT_DARK_UI_BTN_BORDER};
  background: ${NCT_DARK_UI_BTN_GRADIENT};
  color: ${c.fg};
  cursor: pointer;
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  font-family: inherit;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.06);
}
.kp-layout-config-panel .kp-cfg-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.kp-layout-config-panel .kp-cfg-btn:hover:not(:disabled) {
  background: linear-gradient(180deg, #545454 0%, #3c3c3c 50%, #323232 100%);
}
.kp-layout-config-panel .kp-cfg-btn.kp-cfg-btn-lit,
.kp-layout-config-panel .kp-cfg-btn[aria-pressed="true"] {
  background: ${NCT_DARK_UI_BTN_LIT_GRADIENT};
  border: ${NCT_DARK_UI_BTN_LIT_BORDER};
  color: ${NCT_DARK_UI_SELECTED_TEXT};
  box-shadow: inset 0 1px 0 rgba(200,220,240,0.18);
}
.kp-layout-config-panel .kp-cfg-field {
  width: 100%;
  padding: 4px 8px;
  height: 22px;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  border: ${NCT_DARK_UI_FIELD_BORDER};
  background: ${NCT_DARK_UI_FIELD_BACKGROUND};
  color: ${c.fg};
  outline: none;
  font-size: 11px;
  font-family: inherit;
  box-sizing: border-box;
  box-shadow: ${NCT_DARK_UI_FIELD_BOX_SHADOW};
}
.kp-layout-config-panel .kp-cfg-field:focus {
  border-color: ${NCT_DARK_UI_FIELD_FOCUS_BORDER};
  box-shadow: ${NCT_DARK_UI_FIELD_FOCUS_BOX_SHADOW};
}
.kp-layout-config-panel select.kp-cfg-field {
  padding-right: 20px;
}
.kp-layout-config-panel .kp-mk-kind-grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 6px;
  width: 100%;
  margin-bottom: 8px;
}
@media (max-width: 900px) {
  .kp-layout-config-panel .kp-mk-kind-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
.kp-layout-config-panel .kp-mk-kind-btn {
  text-align: left;
  padding: 8px 9px;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  border: 1px solid #4a3a5a;
  background: linear-gradient(180deg, #2c2434, #221c28);
  color: #e0d0f0;
  cursor: pointer;
  font-size: 11px;
  line-height: 1.3;
  font-family: inherit;
}
.kp-layout-config-panel .kp-mk-kind-btn strong {
  display: block;
  font-size: 11px;
  margin-bottom: 2px;
}
.kp-layout-config-panel .kp-mk-kind-btn span {
  display: block;
  opacity: 0.7;
  font-size: 10px;
}
.kp-layout-config-panel .kp-mk-kind-btn:hover {
  border-color: #9a7ab8;
  background: linear-gradient(180deg, #342a40, #281e30);
}
.kp-layout-config-panel .kp-mk-editor {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  border: 1px solid #2a4058;
  background: rgba(25, 40, 55, 0.35);
  margin-bottom: 8px;
}
.kp-layout-config-panel .kp-mk-editor-title {
  font-size: 11px;
  font-weight: 700;
}
.kp-layout-config-panel .kp-mk-field-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #7a92a8;
}
.kp-layout-config-panel .kp-mk-stroke,
.kp-layout-config-panel .kp-mk-stroke-row,
.kp-layout-config-panel .kp-mk-mouse-row,
.kp-layout-config-panel .kp-cfg-mod-toggles,
.kp-layout-config-panel .kp-mk-editor-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}
.kp-layout-config-panel .kp-cfg-mod-toggles .kp-cfg-btn {
  min-width: 52px;
  justify-content: center;
}
.kp-layout-config-panel .kp-cfg-mod-hint {
  font-size: 10px;
  line-height: 1.35;
  color: #8aa0b4;
}
.kp-layout-config-panel .kp-mk-key-input {
  width: 72px;
  min-width: 56px;
  flex: 0 0 auto;
}
.kp-layout-config-panel .kp-mk-mod {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 10px;
  opacity: 0.9;
}
.kp-layout-config-panel .kp-mk-stroke-idx {
  width: 16px;
  font-size: 10px;
  opacity: 0.7;
}
.kp-layout-config-panel .kp-mk-preview {
  font-size: 11px;
  opacity: 0.85;
  padding: 4px 0;
}
.kp-cfg-btn[data-capturing="true"] {
  background: #ffb020 !important;
  color: #221a05 !important;
  border-color: #ffb020 !important;
}
.kp-cfg-btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 3px;
}
.kp-cfg-badge {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 1px 5px;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  border: 1px solid #8a6040;
  background: #5a4030;
  color: #ffcf9e;
  white-space: nowrap;
  align-self: flex-start;
}
.kp-cfg-badge.kp-cfg-badge-stock {
  border-color: #3a5a50;
  background: #243430;
  color: #5a9a8a;
}
.kp-cfg-badge.kp-cfg-badge-user {
  border-color: #5a4a6a;
  background: #2c2434;
  color: #9a7ab8;
}
.kp-layout-config-panel .kp-cfg-pane-inspector {
  flex: 0 0 auto;
  width: var(--kp-cfg-inspector-width);
  min-width: var(--kp-cfg-inspector-width);
  background:
    linear-gradient(180deg, rgba(50, 80, 110, 0.22), rgba(25, 40, 55, 0.12)),
    #161a20;
  border-color: #2a4058;
  box-shadow:
    0 0 0 1px rgba(140, 190, 230, 0.3),
    0 0 14px rgba(80, 140, 190, 0.14),
    inset 0 1px 0 rgba(190, 220, 255, 0.2),
    inset 0 0 0 1px rgba(90, 140, 180, 0.18);
}
.kp-layout-config-panel .kp-cfg-pane-inspector.kp-cfg-collapsed {
  width: 22px;
  min-width: 22px;
}
.kp-layout-config-panel .kp-cfg-inspector-rail {
  display: none;
  flex: 1 1 auto;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: none;
  background: linear-gradient(180deg, #3a5068, #2a3c50);
  color: #c0d4e8;
  cursor: pointer;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  writing-mode: vertical-rl;
  padding: 8px 0;
  font-family: inherit;
}
.kp-layout-config-panel .kp-cfg-inspector-rail:hover {
  background: linear-gradient(180deg, #4a6080, #364a62);
  color: #e8f4ff;
}
.kp-layout-config-panel .kp-cfg-pane-inspector.kp-cfg-collapsed .kp-cfg-inspector-rail {
  display: flex;
}
.kp-layout-config-panel .kp-cfg-pane-inspector.kp-cfg-collapsed .kp-cfg-inspector-expanded {
  display: none;
}
.kp-layout-config-panel .kp-cfg-inspector-expanded {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1 1 auto;
}
.kp-layout-config-panel .kp-cfg-pane-inspector .kp-cfg-pane-hdr {
  background: linear-gradient(180deg, #3a5068, #2a3c50);
  color: #c0d4e8;
  border-bottom-color: #152230;
}
.kp-layout-config-panel .kp-cfg-pane-inspector .kp-cfg-pane-title {
  color: #c0d4e8;
}
.kp-layout-config-panel .kp-cfg-dock-empty {
  font-size: 11px;
  opacity: 0.65;
  line-height: 1.4;
  margin: 0;
  color: #8a9aaa;
}
.kp-layout-config-panel .kp-cfg-dock-title {
  font-size: 12px;
  font-weight: 700;
  margin-bottom: 2px;
  word-break: break-word;
  color: #d8e4f0;
}
.kp-layout-config-panel .kp-cfg-dock-title-row {
  display: flex;
  align-items: flex-start;
  gap: 6px;
}
.kp-layout-config-panel .kp-cfg-dock-title-row .kp-cfg-dock-title {
  flex: 1 1 auto;
  min-width: 0;
}
.kp-layout-config-panel .kp-cfg-dock-docs {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  margin: 0;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.04);
  color: #9ec0ff;
  cursor: pointer;
}
.kp-layout-config-panel .kp-cfg-dock-docs:hover {
  background: rgba(158, 192, 255, 0.12);
  border-color: rgba(158, 192, 255, 0.35);
}
.kp-layout-config-panel .kp-cfg-dock-docs .kp-cfg-ico {
  width: 13px;
  height: 13px;
}
.kp-layout-config-panel .kp-cfg-assign-kbd {
  font-family: var(--kp-font-kbd, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
  font-size: var(--kp-type-kbd-size, 10px);
  font-weight: var(--kp-type-weight-regular, 400);
  line-height: 1.2;
  text-transform: var(--kp-kbd-transform, none);
  letter-spacing: var(--kp-kbd-tracking, 0.02em);
  padding: 1px 6px;
  border: var(--kp-kbd-border, ${ONBOARDING_METAL.kbdBorder});
  border-radius: var(--kp-radius-btn, ${NCT_DARK_UI_BTN_RADIUS});
  background: var(--kp-kbd-bg, ${ONBOARDING_METAL.kbdBg});
  color: var(--kp-color-kbd-fg, ${ONBOARDING_METAL.kbdColor});
  box-shadow: var(--kp-kbd-shadow, ${ONBOARDING_METAL.kbdShadow});
  box-sizing: border-box;
}
.kp-layout-config-panel .kp-cfg-dock-subtitle {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #7a92a8;
  margin: 8px 0 4px;
}
.kp-layout-config-panel .kp-cfg-dock-rows {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 2px 8px;
  font-size: 10px;
  margin: 0 0 6px;
}
.kp-layout-config-panel .kp-cfg-dock-rows dt {
  color: #7a92a8;
  white-space: nowrap;
}
.kp-layout-config-panel .kp-cfg-dock-rows dd {
  margin: 0;
  color: #d8e4f0;
  word-break: break-word;
}
.kp-layout-config-panel .kp-cfg-dock-steps {
  list-style: none;
  margin: 0 0 8px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.kp-layout-config-panel .kp-cfg-dock-steps li {
  display: flex;
  gap: 6px;
  font-size: 10px;
  padding: 3px 5px;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  background: rgba(255,255,255,0.04);
}
.kp-layout-config-panel .kp-cfg-dock-steps .kp-cfg-dock-step-idx {
  opacity: 0.5;
  min-width: 12px;
}
.kp-layout-config-panel .kp-cfg-dock-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 8px;
  margin-bottom: 12px;
}
.kp-layout-config-panel .kp-cfg-assign {
  margin: 8px 0;
}
.kp-layout-config-panel .kp-cfg-assign-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 11px;
}
.kp-layout-config-panel .kp-cfg-assign-table th {
  text-align: left;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #7a92a8;
  padding: 2px 4px 4px 0;
  border-bottom: 1px solid rgba(140, 190, 230, 0.18);
}
.kp-layout-config-panel .kp-cfg-assign-table td {
  padding: 4px 4px 4px 0;
  color: #d8e4f0;
  vertical-align: middle;
  border-bottom: 1px solid rgba(255,255,255,0.04);
}
.kp-layout-config-panel .kp-cfg-assign-table td:last-child {
  text-align: right;
  width: 1%;
  white-space: nowrap;
  padding-right: 0;
}
.kp-layout-config-panel .kp-cfg-create-body {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1 1 auto;
  padding: 8px;
  gap: 8px;
  /* Inner panes own their scrolling so the script footer stays pinned. */
  overflow: hidden;
}
.kp-layout-config-panel .kp-cfg-script-panel,
.kp-layout-config-panel .kp-cfg-macrokey-panel {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1 1 auto;
  min-height: 0;
}
.kp-layout-config-panel .kp-cfg-script-panel[hidden],
.kp-layout-config-panel .kp-cfg-macrokey-panel[hidden] {
  display: none;
}
.kp-layout-config-panel .kp-cfg-macrokey-panel {
  overflow: auto;
}
.kp-layout-config-panel .kp-cfg-script-panel > .kp-cfg-script-toolbar,
.kp-layout-config-panel .kp-cfg-script-panel > .kp-cfg-stock-banner,
.kp-layout-config-panel .kp-cfg-script-panel > .kp-cfg-strip-row {
  flex: 0 0 auto;
}
.kp-layout-config-panel .kp-cfg-pane-create {
  flex: 0 0 auto;
  background:
    linear-gradient(180deg, rgba(90, 60, 120, 0.18), rgba(50, 35, 70, 0.08)),
    #1a161e;
  border-color: #4a3a5a;
  box-shadow:
    0 0 0 1px rgba(180, 140, 220, 0.28),
    0 0 14px rgba(120, 80, 160, 0.12),
    inset 0 1px 0 rgba(220, 190, 255, 0.14),
    inset 0 0 0 1px rgba(140, 100, 180, 0.14);
}
.kp-layout-config-panel .kp-cfg-workspace.kp-cfg-no-macro-builder > .kp-cfg-pane-create,
.kp-layout-config-panel .kp-cfg-workspace.kp-cfg-no-macro-builder > .kp-cfg-split-h {
  display: none !important;
}
.kp-layout-config-panel .kp-cfg-pane-create .kp-cfg-pane-hdr {
  background: linear-gradient(180deg, #4a3a58, #342844);
  color: #d8c8e8;
  border-bottom-color: #1a1220;
}
.kp-layout-config-panel .kp-cfg-pane-create .kp-cfg-pane-title {
  color: #d8c8e8;
}
.kp-layout-config-panel .kp-cfg-pane-create.kp-cfg-open {
  flex: 0 0 var(--kp-cfg-create-height);
  min-height: 210px;
}
.kp-layout-config-panel .kp-cfg-pane-create:not(.kp-cfg-open) .kp-cfg-create-body {
  display: none;
}
.kp-layout-config-panel .kp-cfg-pane-create.kp-cfg-maximized {
  /* Fill remaining workspace under the Actions Library titlebar. */
  flex: 1 1 auto !important;
  min-height: 0 !important;
  max-height: none;
}
.kp-layout-config-panel .kp-cfg-pane-create.kp-cfg-maximized > .kp-cfg-pane-hdr {
  flex: 0 0 auto;
  flex-shrink: 0;
}
.kp-layout-config-panel .kp-cfg-pane-create.kp-cfg-maximized > .kp-cfg-create-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
}
.kp-layout-config-panel .kp-cfg-workspace.kp-cfg-create-maximized .kp-cfg-pane-library .kp-cfg-pane-scroll {
  /* Inline styles on the list set display:flex \u2014 must win with !important. */
  display: none !important;
  flex: 0 0 0 !important;
  min-height: 0 !important;
  height: 0 !important;
  overflow: hidden !important;
  padding: 0 !important;
}
.kp-layout-config-panel .kp-cfg-workspace.kp-cfg-create-maximized .kp-cfg-pane-library {
  flex: 0 0 auto;
  min-height: 0;
  overflow: hidden;
}
.kp-layout-config-panel .kp-cfg-workspace.kp-cfg-create-maximized {
  overflow: hidden;
}
.kp-layout-config-panel .kp-cfg-workspace.kp-cfg-create-maximized .kp-cfg-split-h {
  display: none;
}
.kp-layout-config-panel .kp-cfg-split {
  position: relative;
  z-index: 2;
  flex: 0 0 10px;
  padding: 0;
  border: 0;
  background: #1a1a1a;
}
.kp-layout-config-panel .kp-cfg-split::after {
  content: "";
  position: absolute;
  inset: -3px;
}
.kp-layout-config-panel .kp-cfg-split::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  background: #6a6a6a;
  border-radius: 1px;
  pointer-events: none;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45);
}
.kp-layout-config-panel .kp-cfg-split:hover,
.kp-layout-config-panel .kp-cfg-split:focus-visible,
.kp-layout-config-panel .kp-cfg-split.kp-cfg-split-dragging {
  outline: none;
  background: #3a5570;
}
.kp-layout-config-panel .kp-cfg-split:hover::before,
.kp-layout-config-panel .kp-cfg-split:focus-visible::before,
.kp-layout-config-panel .kp-cfg-split.kp-cfg-split-dragging::before {
  background: #b0d0e8;
}
.kp-layout-config-panel .kp-cfg-split-v {
  cursor: col-resize;
  border-left: 1px solid #0a0a0a;
  border-right: 1px solid #3a3a3a;
  background: linear-gradient(90deg, #2a2a2a, #1c1c1c);
}
.kp-layout-config-panel .kp-cfg-split-v::before {
  width: 2.5px;
  height: 45px;
}
.kp-layout-config-panel .kp-cfg-split-h {
  cursor: row-resize;
  border-top: 1px solid #0a0a0a;
  border-bottom: 1px solid #3a3a3a;
  background: linear-gradient(180deg, #2a2a2a, #1c1c1c);
}
.kp-layout-config-panel .kp-cfg-split-h::before {
  width: 45px;
  height: 2.5px;
}
.kp-layout-config-panel .kp-cfg-script-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}
.kp-layout-config-panel .kp-cfg-script-toolbar .kp-cfg-macro-name {
  width: auto;
  flex: 1 1 140px;
  min-width: 110px;
  max-width: 220px;
}
.kp-layout-config-panel .kp-cfg-stock-banner {
  font-size: 10px;
  line-height: 1.35;
  padding: 5px 7px;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  border: 1px solid rgba(120,150,255,0.4);
  background: rgba(120,150,255,0.12);
  color: #cdd8ff;
}
.kp-layout-config-panel .kp-cfg-script-canvas {
  display: flex;
  flex-direction: row;
  gap: 0;
  align-items: stretch;
  flex: 1 1 auto;
  min-height: 88px;
  overflow: hidden;
  border: 1px solid #3a2a4a;
  border-radius: 2px;
  background: linear-gradient(180deg, rgba(20, 14, 28, 0.65), rgba(14, 10, 20, 0.9));
  box-shadow: inset 0 1px 0 rgba(154, 122, 184, 0.08);
}
.kp-layout-config-panel .kp-cfg-logic-palette {
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 0 0 132px;
  width: 132px;
  min-width: 120px;
  max-width: 140px;
  margin: 6px 0 6px 6px;
  padding: 10px 6px 6px;
  border: 1px solid rgba(154, 122, 184, 0.12);
  border-radius: 2px;
  background: rgba(0, 0, 0, 0.22);
  overflow: auto;
}
.kp-layout-config-panel .kp-cfg-logic-palette > legend {
  padding: 0 5px;
  margin-left: 2px;
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #9a88b0;
}
.kp-layout-config-panel .kp-cfg-logic-chip {
  text-align: left;
  padding: 5px 6px;
  border-radius: 2px;
  border: 1px solid #3a3048;
  background: linear-gradient(180deg, #2a2234, #221c2c);
  color: #ddd0e8;
  cursor: pointer;
  line-height: 1.25;
  font-family: inherit;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.kp-layout-config-panel .kp-cfg-logic-chip:hover {
  border-color: #9a7ab8;
  color: #f0e8f8;
}
.kp-layout-config-panel .kp-cfg-logic-chip strong {
  display: block;
  font-size: 9px;
  font-weight: 700;
  color: #e8d8f8;
}
.kp-layout-config-panel .kp-cfg-logic-chip span {
  display: block;
  font-size: 7px;
  color: #8a7a9a;
  opacity: 1;
}
.kp-layout-config-panel .kp-cfg-steps-pane {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
  overflow: auto;
  padding: 6px;
}
.kp-layout-config-panel .kp-cfg-script-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  flex: 1 1 auto;
  min-height: 88px;
  padding: 12px;
  text-align: center;
  color: #7a6a8a;
  font-size: 10px;
  line-height: 1.4;
  box-sizing: border-box;
}
.kp-layout-config-panel .kp-cfg-script-empty strong {
  color: #b8a0d0;
  font-size: 11px;
  font-weight: 700;
}
.kp-layout-config-panel .kp-cfg-cycle-warn {
  margin: 6px 0 0;
  padding: 5px 7px;
  border-radius: 2px;
  border: 1px solid rgba(200, 120, 80, 0.45);
  background: rgba(80, 40, 20, 0.35);
  color: #f0c090;
  font-size: 10px;
  line-height: 1.35;
}
.kp-layout-config-panel .kp-cfg-hdr-actions {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex: 0 0 auto;
  margin-left: auto;
}
.kp-layout-config-panel .kp-cfg-step {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  padding: 5px 6px;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  border: 1px solid #3a3a3a;
  background: #222;
  position: relative;
}
.kp-layout-config-panel .kp-cfg-step.kp-cfg-step-selected {
  border-color: ${c.accent};
  background: ${NCT_DARK_UI_SELECTED_TINT};
}
.kp-layout-config-panel .kp-cfg-step.kp-cfg-step-dragging {
  opacity: 0.45;
}
.kp-layout-config-panel .kp-cfg-step.kp-cfg-step-drag-over-before::before,
.kp-layout-config-panel .kp-cfg-step.kp-cfg-step-drag-over-after::after {
  content: '';
  position: absolute;
  left: 4px;
  right: 4px;
  height: 2px;
  border-radius: 1px;
  background: ${c.accent};
  pointer-events: none;
  z-index: 1;
}
.kp-layout-config-panel .kp-cfg-step.kp-cfg-step-drag-over-before::before {
  top: -1px;
}
.kp-layout-config-panel .kp-cfg-step.kp-cfg-step-drag-over-after::after {
  bottom: -1px;
}
.kp-layout-config-panel .kp-cfg-step-grip {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 18px;
  margin: 0;
  padding: 0;
  border: none;
  border-radius: 2px;
  background: transparent;
  color: #7a7a7a;
  font-size: 11px;
  line-height: 1;
  letter-spacing: -1px;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
}
.kp-layout-config-panel .kp-cfg-step-grip:hover {
  color: #b0b0b0;
  background: rgba(255, 255, 255, 0.06);
}
.kp-layout-config-panel .kp-cfg-step-grip:active {
  cursor: grabbing;
}
.kp-layout-config-panel .kp-cfg-step-idx {
  opacity: 0.5;
  font-size: 10px;
  min-width: 13px;
}
.kp-layout-config-panel .kp-cfg-step-label {
  font-size: 11px;
  font-weight: 600;
  flex: 1 1 110px;
  min-width: 90px;
  word-break: break-word;
}
.kp-layout-config-panel .kp-cfg-step-sub {
  display: block;
  font-size: 9px;
  font-weight: 400;
  opacity: 0.6;
}
.kp-layout-config-panel .kp-cfg-step-fields {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px;
}
.kp-layout-config-panel .kp-cfg-step-fields label {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 9px;
  opacity: 0.8;
}
.kp-layout-config-panel .kp-cfg-step-summary {
  font-size: 9px;
  font-weight: 400;
  opacity: 0.75;
  line-height: 1.3;
  max-width: 280px;
}
.kp-layout-config-panel .kp-cfg-gate-advanced {
  margin-top: 4px;
}
.kp-layout-config-panel .kp-cfg-gate-advanced > summary {
  cursor: pointer;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #7a92a8;
}
.kp-layout-config-panel .kp-cfg-step-fields .kp-cfg-field {
  width: auto;
  padding: 2px 5px;
  height: 18px;
  font-size: 10px;
}
.kp-layout-config-panel .kp-cfg-step-fields input[type="number"].kp-cfg-field {
  width: 62px;
}
.kp-layout-config-panel .kp-cfg-step-fields input[type="text"].kp-cfg-field {
  width: 80px;
}
.kp-layout-config-panel .kp-cfg-step-ops {
  display: inline-flex;
  gap: 3px;
  margin-left: auto;
}
.kp-layout-config-panel .kp-cfg-step-ops .kp-cfg-btn {
  padding: 0 6px;
  height: 18px;
}
.kp-layout-config-panel .kp-cfg-hint {
  font-size: 10px;
  color: ${c.fgMute};
  line-height: 1.35;
}
.kp-layout-config-panel .kp-cfg-hint-link {
  display: inline;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: #3a6ea8;
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}
${getNctDarkUiScrollbarCss({ scopeSelector: ".kp-layout-config-panel" })}

/* -------------------------------------------------------------------------
 * Light Metal Pro GUI completion layer
 * Keep semantic keycap / selection colors, but make all surrounding Config
 * chrome use the same light metal language as onboarding.
 * ---------------------------------------------------------------------- */
.kp-layout-config-panel .kp-cfg-layout-combo,
.kp-layout-config-panel .kp-cfg-field {
  border: ${ONBOARDING_METAL.kbdBorder};
  background: #e8e8e8;
  color: ${ONBOARDING_METAL.fg};
  box-shadow:
    inset 0 1px 2px rgba(0,0,0,0.28),
    0 1px 0 rgba(255,255,255,0.28);
}
.kp-layout-config-panel .kp-cfg-layout-combo:focus-within,
.kp-layout-config-panel .kp-cfg-field:focus {
  border-color: #355f82;
  box-shadow:
    inset 0 1px 2px rgba(0,0,0,0.24),
    0 0 0 1px rgba(74,144,200,0.55);
}
.kp-layout-config-panel .kp-cfg-combo-input,
.kp-layout-config-panel .kp-cfg-layout-combo.is-builtin .kp-cfg-combo-input {
  color: ${ONBOARDING_METAL.fg};
}
.kp-layout-config-panel .kp-cfg-combo-toggle {
  border-left-color: rgba(0,0,0,0.3);
  background: ${ONBOARDING_METAL.btnBg};
  color: ${ONBOARDING_METAL.fg};
  box-shadow: ${ONBOARDING_METAL.btnShadow};
}
.kp-layout-config-panel .kp-cfg-combo-toggle:hover {
  color: ${ONBOARDING_METAL.fg};
  filter: brightness(1.08);
}
.kp-layout-config-panel .kp-cfg-combo-current-badge,
.kp-layout-config-panel .kp-cfg-combo-option-current {
  background: rgba(74,144,200,0.2);
  color: #234f73;
}
.kp-layout-config-panel .kp-cfg-combo-list,
.kp-layout-config-panel .kp-cfg-opts-menu {
  border: ${ONBOARDING_METAL.panelBorder};
  background: ${ONBOARDING_METAL.panelBg};
  color: ${ONBOARDING_METAL.fg};
  box-shadow:
    0 0 0 1px rgba(255,255,255,0.28) inset,
    0 10px 24px rgba(0,0,0,0.42);
}
.kp-layout-config-panel .kp-cfg-combo-group {
  color: ${ONBOARDING_METAL.fgDim};
}
.kp-layout-config-panel .kp-cfg-combo-group:not(:first-child) {
  border-top-color: rgba(0,0,0,0.2);
}
.kp-layout-config-panel .kp-cfg-combo-option,
.kp-layout-config-panel .kp-cfg-opts-menu .kp-cfg-opts-check {
  color: ${ONBOARDING_METAL.fg};
}
.kp-layout-config-panel .kp-cfg-combo-option:hover,
.kp-layout-config-panel .kp-cfg-combo-option.is-active {
  background: rgba(255,255,255,0.3);
  color: ${ONBOARDING_METAL.fg};
}

.kp-layout-config-panel .kp-cfg-tool-group,
.kp-layout-config-panel .kp-cfg-seg {
  border: ${ONBOARDING_METAL.btnBorder};
  background: ${ONBOARDING_METAL.btnBg};
  box-shadow: ${ONBOARDING_METAL.btnShadow};
}
.kp-layout-config-panel .kp-cfg-tool-group .kp-cfg-btn,
.kp-layout-config-panel .kp-cfg-seg-btn {
  border-right-color: rgba(0,0,0,0.22);
  background: transparent;
  color: ${ONBOARDING_METAL.fgDim};
  box-shadow: none;
}
.kp-layout-config-panel .kp-cfg-tool-group .kp-cfg-btn:hover:not(:disabled),
.kp-layout-config-panel .kp-cfg-seg-btn:hover {
  background: rgba(255,255,255,0.28);
  color: ${ONBOARDING_METAL.fg};
}
.kp-layout-config-panel .kp-cfg-seg-btn[aria-selected="true"] {
  background: linear-gradient(180deg, #bad3e7 0%, #8fb1cd 100%);
  color: #17354d;
  box-shadow: inset 0 0 0 1px rgba(53,95,130,0.55);
}
.kp-layout-config-panel .kp-cfg-tool-sep {
  background: rgba(0,0,0,0.28);
}
.kp-layout-config-panel .kp-cfg-btn,
.kp-layout-config-panel .kp-cfg-inspect {
  border: ${ONBOARDING_METAL.btnBorder};
  background: ${ONBOARDING_METAL.btnBg};
  color: ${ONBOARDING_METAL.fg};
  box-shadow: ${ONBOARDING_METAL.btnShadow};
}
.kp-layout-config-panel .kp-cfg-btn:hover:not(:disabled),
.kp-layout-config-panel .kp-cfg-inspect:hover {
  background: linear-gradient(180deg, #d0d0d0 0%, #aeaeae 50%, #969696 100%);
  color: ${ONBOARDING_METAL.fg};
}
.kp-layout-config-panel .kp-cfg-btn.kp-cfg-btn-lit,
.kp-layout-config-panel .kp-cfg-btn[aria-pressed="true"] {
  background: ${NCT_DARK_UI_BTN_LIT_GRADIENT};
  border: ${NCT_DARK_UI_BTN_LIT_BORDER};
  color: ${NCT_DARK_UI_SELECTED_TEXT};
  box-shadow: inset 0 1px 0 rgba(200,220,240,0.18);
}
.kp-layout-config-panel .kp-cfg-hint,
.kp-layout-config-panel .kp-mk-field-label,
.kp-layout-config-panel .kp-cfg-mod-hint {
  color: ${ONBOARDING_METAL.fgMute};
}

.kp-layout-config-panel .kp-cfg-legend {
  color: ${ONBOARDING_METAL.fgMute};
}
.kp-layout-config-panel .kp-cfg-legend .kp-cfg-sw-stock-fn {
  background: #b0b0b0;
  border-color: #4a4a4a;
}
.kp-layout-config-panel .kp-cfg-legend .kp-cfg-sw-user {
  background: #b0b0b0;
  border-color: ${accentA(0.65)};
}
.kp-layout-config-panel .kp-cfg-legend .kp-cfg-sw-stock-macro {
  background: #a4b0a6;
  border-left-color: #3f7a68;
}
.kp-layout-config-panel .kp-cfg-legend .kp-cfg-sw-user-macro {
  background: #aba1b6;
  border-left-color: #6d519a;
}
.kp-layout-config-panel .kp-cfg-category,
.kp-layout-config-panel .kp-cfg-macro-fieldset,
.kp-layout-config-panel .kp-cfg-instance-fieldset {
  border-color: rgba(0,0,0,0.35);
  background: rgba(0,0,0,0.18);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.08);
}
.kp-layout-config-panel .kp-cfg-category[data-kp-section="macros"],
.kp-layout-config-panel .kp-cfg-macro-fieldset[data-macro-group="user"] {
  border-color: rgba(90,55,120,0.4);
  background: rgba(40, 28, 52, 0.35);
}
.kp-layout-config-panel .kp-cfg-category[data-kp-section="macroKeys"],
.kp-layout-config-panel .kp-cfg-macro-fieldset[data-macro-group="stock"] {
  border-color: rgba(45,90,75,0.4);
  background: rgba(28, 42, 34, 0.32);
}
.kp-layout-config-panel .kp-cfg-category-title {
  color: rgba(240,240,240,0.78);
  background: #4a4a4a;
}
.kp-layout-config-panel .kp-cfg-category[data-kp-section="macros"] .kp-cfg-category-title,
.kp-layout-config-panel .kp-cfg-category[data-kp-section="macroKeys"] .kp-cfg-category-title,
.kp-layout-config-panel .kp-cfg-macro-fieldset > legend,
.kp-layout-config-panel .kp-cfg-macro-fieldset[data-macro-group="stock"] > legend,
.kp-layout-config-panel .kp-cfg-macro-fieldset[data-macro-group="user"] > legend,
.kp-layout-config-panel .kp-cfg-instance-fieldset > legend {
  color: rgba(240,240,240,0.72);
}
.kp-layout-config-panel .kp-cfg-desc-fieldset {
  border-color: rgba(0,0,0,0.3);
  background: rgba(0,0,0,0.22);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.05);
}
.kp-layout-config-panel .kp-cfg-desc-line {
  color: rgba(240,240,240,0.62);
}
.kp-layout-config-panel .kp-cfg-subgroup-empty {
  color: rgba(240,240,240,0.55);
}

.kp-layout-config-panel .kp-mk-kind-btn,
.kp-layout-config-panel .kp-cfg-logic-chip {
  border: ${ONBOARDING_METAL.btnBorder};
  border-left: 3px solid #6d519a;
  background: ${ONBOARDING_METAL.btnBg};
  color: ${ONBOARDING_METAL.fg};
  box-shadow: ${ONBOARDING_METAL.btnShadow};
}
.kp-layout-config-panel .kp-mk-kind-btn:hover,
.kp-layout-config-panel .kp-cfg-logic-chip:hover {
  border-color: #6d519a;
  background: linear-gradient(180deg, #d2cad9 0%, #b5aabb 50%, #9f94a5 100%);
  color: ${ONBOARDING_METAL.fg};
}
.kp-layout-config-panel .kp-cfg-logic-chip strong,
.kp-layout-config-panel .kp-cfg-logic-chip span {
  color: ${ONBOARDING_METAL.fg};
}
.kp-layout-config-panel .kp-cfg-logic-chip span {
  opacity: 0.62;
}
.kp-layout-config-panel .kp-mk-editor {
  border-color: rgba(35,79,115,0.55);
  background: rgba(205,222,235,0.34);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.24);
}

.kp-layout-config-panel .kp-cfg-pane-inspector {
  background:
    linear-gradient(180deg, rgba(100,155,200,0.17), rgba(65,110,150,0.08)),
    linear-gradient(180deg, #9d9d9d 0%, #8b8b8b 55%, #7d7d7d 100%);
  border-color: #355f82;
  box-shadow:
    0 0 0 1px rgba(255,255,255,0.24) inset,
    0 0 12px rgba(60,115,160,0.18);
}
.kp-layout-config-panel .kp-cfg-inspector-rail,
.kp-layout-config-panel .kp-cfg-pane-inspector .kp-cfg-pane-hdr {
  background: linear-gradient(180deg, #b3c8d8 0%, #91aabd 48%, #778fa2 100%);
  color: #18364e;
  border-bottom-color: #355f82;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.35);
}
.kp-layout-config-panel .kp-cfg-inspector-rail:hover {
  background: linear-gradient(180deg, #c1d4e2 0%, #9eb7ca 48%, #849caf 100%);
  color: #102b40;
}
.kp-layout-config-panel .kp-cfg-pane-inspector .kp-cfg-pane-title,
.kp-layout-config-panel .kp-cfg-dock-title,
.kp-layout-config-panel .kp-cfg-dock-rows dd {
  color: ${ONBOARDING_METAL.fg};
}
.kp-layout-config-panel .kp-cfg-dock-title {
  font-weight: 700;
}
.kp-layout-config-panel .kp-cfg-dock-empty,
.kp-layout-config-panel .kp-cfg-dock-subtitle,
.kp-layout-config-panel .kp-cfg-dock-rows dt {
  color: ${ONBOARDING_METAL.fgMute};
}
.kp-layout-config-panel .kp-cfg-dock-steps li {
  border: ${ONBOARDING_METAL.rowBorder};
  background: ${ONBOARDING_METAL.rowBg};
}
.kp-layout-config-panel .kp-cfg-assign-table th {
  color: ${ONBOARDING_METAL.fgDim};
  border-bottom-color: rgba(0,0,0,0.28);
}
.kp-layout-config-panel .kp-cfg-assign-table td {
  color: ${ONBOARDING_METAL.fg};
  border-bottom-color: rgba(0,0,0,0.15);
}

.kp-layout-config-panel .kp-cfg-pane-create {
  background:
    linear-gradient(180deg, rgba(145,100,180,0.17), rgba(100,70,130,0.08)),
    linear-gradient(180deg, #9d9d9d 0%, #8b8b8b 55%, #7d7d7d 100%);
  border-color: #604574;
  box-shadow:
    0 0 0 1px rgba(255,255,255,0.24) inset,
    0 0 12px rgba(105,65,140,0.16);
}
.kp-layout-config-panel .kp-cfg-pane-create .kp-cfg-pane-hdr {
  background: linear-gradient(180deg, #c7b8d2 0%, #aa97b8 48%, #907ca0 100%);
  color: #30213b;
  border-bottom-color: #604574;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.35);
}
.kp-layout-config-panel .kp-cfg-pane-create .kp-cfg-pane-title {
  color: #30213b;
}
.kp-layout-config-panel .kp-cfg-stock-banner {
  background: rgba(195,205,235,0.55);
  color: #27355a;
}
.kp-layout-config-panel .kp-cfg-script-canvas {
  border-color: rgba(75,50,95,0.48);
  background: rgba(255,255,255,0.16);
  box-shadow:
    inset 0 1px 2px rgba(0,0,0,0.18),
    0 1px 0 rgba(255,255,255,0.2);
}
.kp-layout-config-panel .kp-cfg-logic-palette {
  border-color: rgba(90,55,120,0.26);
  background: rgba(255,255,255,0.14);
}
.kp-layout-config-panel .kp-cfg-logic-palette > legend,
.kp-layout-config-panel .kp-cfg-script-empty,
.kp-layout-config-panel .kp-cfg-script-empty strong {
  color: ${ONBOARDING_METAL.fgMute};
}
.kp-layout-config-panel .kp-cfg-script-empty strong {
  color: ${ONBOARDING_METAL.fg};
}
.kp-layout-config-panel .kp-cfg-cycle-warn {
  background: rgba(226,174,105,0.38);
  color: #512c12;
}
.kp-layout-config-panel .kp-cfg-step {
  border: ${ONBOARDING_METAL.rowBorder};
  background: ${ONBOARDING_METAL.rowBg};
  color: ${ONBOARDING_METAL.fg};
}
.kp-layout-config-panel .kp-cfg-step.kp-cfg-step-selected {
  border-color: #355f82;
  background: rgba(155,190,220,0.55);
}
.kp-layout-config-panel .kp-cfg-step-grip {
  color: ${ONBOARDING_METAL.fgDim};
}
.kp-layout-config-panel .kp-cfg-step-grip:hover {
  color: ${ONBOARDING_METAL.fg};
  background: rgba(0, 0, 0, 0.08);
}

.kp-layout-config-panel .kp-cfg-split {
  background: #454545;
}
.kp-layout-config-panel .kp-cfg-split-v {
  border-left-color: rgba(0,0,0,0.3);
  border-right-color: rgba(255,255,255,0.2);
  background: linear-gradient(90deg, #555, #343434);
}
.kp-layout-config-panel .kp-cfg-split-h {
  border-top-color: rgba(0,0,0,0.3);
  border-bottom-color: rgba(255,255,255,0.2);
  background: linear-gradient(180deg, #555, #343434);
}
.kp-layout-config-panel .kp-cfg-split:hover,
.kp-layout-config-panel .kp-cfg-split:focus-visible,
.kp-layout-config-panel .kp-cfg-split.kp-cfg-split-dragging {
  background: #6689a5;
}

.kp-layout-config-panel .kp-hier-table-wrap {
  border: ${ONBOARDING_METAL.rowBorder};
  background: rgba(255,255,255,0.14);
}
.kp-layout-config-panel .kp-hier-table th {
  background: linear-gradient(180deg, #a8a8a8 0%, #909090 100%);
  color: ${ONBOARDING_METAL.fgDim};
  border-bottom-color: rgba(0,0,0,0.28);
}
.kp-layout-config-panel .kp-hier-table td {
  color: ${ONBOARDING_METAL.fg};
  border-bottom-color: rgba(0,0,0,0.14);
}
.kp-layout-config-panel .kp-hier-table tr.kp-hier-row-group,
.kp-layout-config-panel .kp-hier-table tr.kp-hier-row-group td {
  background: rgba(255,255,255,0.2);
  color: ${ONBOARDING_METAL.fgDim};
}
.kp-layout-config-panel .kp-hier-twisty {
  color: ${ONBOARDING_METAL.fgDim};
}

/* Light-metal scrollbar overrides the shared dark scrollbar for this panel. */
.kp-layout-config-panel .${NCT_DARK_UI_SCROLLBAR_CLASS} {
  scrollbar-color: #a8a8a8 #747474;
}
.kp-layout-config-panel .${NCT_DARK_UI_SCROLLBAR_CLASS}::-webkit-scrollbar-corner,
.kp-layout-config-panel .${NCT_DARK_UI_SCROLLBAR_CLASS} ::-webkit-scrollbar-corner,
.kp-layout-config-panel .${NCT_DARK_UI_SCROLLBAR_CLASS}::-webkit-scrollbar-track,
.kp-layout-config-panel .${NCT_DARK_UI_SCROLLBAR_CLASS} ::-webkit-scrollbar-track {
  background: #747474;
  border-color: rgba(0,0,0,0.3);
}
.kp-layout-config-panel .${NCT_DARK_UI_SCROLLBAR_CLASS}::-webkit-scrollbar-thumb,
.kp-layout-config-panel .${NCT_DARK_UI_SCROLLBAR_CLASS} ::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, #c0c0c0 0%, #a8a8a8 45%, #8d8d8d 100%);
  border-color: #4a4a4a;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.42);
}
.kp-layout-config-panel .${NCT_DARK_UI_SCROLLBAR_CLASS}::-webkit-scrollbar-thumb:hover,
.kp-layout-config-panel .${NCT_DARK_UI_SCROLLBAR_CLASS} ::-webkit-scrollbar-thumb:hover {
  background: linear-gradient(180deg, #cecece 0%, #b5b5b5 45%, #999 100%);
}

/* 125% readability pass for Config chrome, controls, and their labels. */
.kp-layout-config-panel {
  font-size: 15px;
}
.kp-layout-config-panel .kp-cfg-ico {
  width: 15px;
  height: 15px;
}
.kp-layout-config-panel .kp-cfg-titlebar {
  height: 35px;
  min-height: 35px;
  max-height: 35px;
  padding: 0 8px 0 13px;
}
.kp-layout-config-panel .kp-cfg-title {
  font-size: 13.75px;
  line-height: 35px;
}
.kp-layout-config-panel .kp-cfg-autosaves-chip {
  font-size: 10.5px;
  padding: 3px 9px;
}
.kp-layout-config-panel .kp-cfg-close {
  height: 28px;
  min-height: 28px;
  font-size: 12px;
  padding: 0 8px;
  gap: 5px;
}
.kp-layout-config-panel .kp-cfg-layout-strip {
  height: 40px;
  padding: 0 13px;
}
.kp-layout-config-panel .kp-cfg-layout-identity,
.kp-layout-config-panel .kp-cfg-layout-tools,
.kp-layout-config-panel .kp-cfg-layout-primary,
.kp-layout-config-panel .kp-cfg-tool-group,
.kp-layout-config-panel .kp-cfg-layout-combo {
  height: 28px;
}
.kp-layout-config-panel .kp-cfg-handedness-seg {
  height: 28px;
}
.kp-layout-config-panel .kp-cfg-handedness-seg .kp-cfg-seg-btn {
  font-size: 11px;
  padding: 0 10px;
}
.kp-layout-config-panel .kp-cfg-strip-label {
  font-size: 11.25px;
  line-height: 28px;
}
.kp-layout-config-panel .kp-cfg-combo-input {
  padding: 0 10px !important;
  font: 13.75px/28px ${NCT_DARK_UI_FONT};
}
.kp-layout-config-panel .kp-cfg-combo-toggle {
  flex-basis: 28px;
  width: 28px;
}
.kp-layout-config-panel .kp-cfg-btn {
  height: 28px;
  padding: 5px 10px;
  font-size: 13.75px;
}
.kp-layout-config-panel .kp-cfg-tool-group .kp-cfg-btn {
  padding: 0 9px;
}
.kp-layout-config-panel .kp-cfg-btn.kp-cfg-btn-icon {
  width: 28px;
  min-width: 28px;
}
.kp-layout-config-panel .kp-cfg-tool-group .kp-cfg-btn.kp-cfg-btn-icon {
  width: 33px;
}
.kp-layout-config-panel .kp-cfg-pane-hdr {
  gap: 8px;
  padding: 5px 10px;
  min-height: 28px;
}
.kp-layout-config-panel .kp-cfg-pane-title {
  font-size: 12.5px;
}
.kp-layout-config-panel .kp-cfg-seg-btn {
  height: 23px;
  padding: 0 10px;
  font-size: 11.25px;
}
.kp-layout-config-panel .kp-cfg-search,
.kp-layout-config-panel .kp-cfg-fn-cat,
.kp-layout-config-panel .kp-cfg-fn-cat-wrap,
.kp-layout-config-panel .kp-cfg-inspect {
  height: 23px;
  font-size: 12.5px;
}
.kp-layout-config-panel .kp-cfg-field {
  height: 28px;
  padding: 5px 10px;
  font-size: 13.75px;
}
.kp-layout-config-panel .kp-select.kp-cfg-field {
  height: auto;
  padding: 0;
  border: 0;
  background: transparent;
  box-shadow: none;
}
.kp-layout-config-panel .kp-select-trigger {
  height: 28px;
  padding: 5px 10px;
  font-size: 13.75px;
  background: #e8e8e8;
  border: ${ONBOARDING_METAL.kbdBorder};
  color: ${ONBOARDING_METAL.fg};
  box-shadow:
    inset 0 1px 2px rgba(0, 0, 0, 0.28),
    0 1px 0 rgba(255, 255, 255, 0.28);
}
.kp-layout-config-panel .kp-cfg-fn-cat-wrap .kp-select,
.kp-layout-config-panel .kp-cfg-fn-cat-wrap .kp-select.kp-cfg-field {
  height: 100%;
  padding: 0;
}
.kp-layout-config-panel .kp-cfg-fn-cat-wrap .kp-select-trigger {
  height: 100%;
  font-size: 12.5px;
}
.kp-layout-config-panel .kp-cfg-hint,
.kp-layout-config-panel .kp-cfg-card-sub,
.kp-layout-config-panel .kp-cfg-dock-rows,
.kp-layout-config-panel .kp-cfg-dock-steps li {
  font-size: 12.5px;
}
.kp-layout-config-panel .kp-cfg-card-name,
.kp-layout-config-panel .kp-cfg-dock-title {
  font-size: 13.75px;
  font-weight: 700;
}
.kp-layout-config-panel .kp-cfg-lib-instructions-title {
  font-size: 14.5px;
}
.kp-layout-config-panel .kp-cfg-lib-instructions-body,
.kp-layout-config-panel .kp-cfg-lib-place-pulse {
  font-size: 12.5px;
}
`.trim().replaceAll(".kp-layout-config-panel", ":host");
    }
    _applyProChrome(root) {
      if (!root || !root.style) return;
      const savedWidth = Number(this._panelPosition?.width);
      const heightPx = resolveConfigPanelHeightPx(this._panelPosition?.height);
      Object.assign(root.style, {
        position: "fixed",
        width: `${Number.isFinite(savedWidth) && savedWidth > 0 ? savedWidth : CONFIG_PANEL_WIDTH_PX}px`,
        height: `${heightPx}px`,
        maxWidth: `calc(100vw - ${CONFIG_POSITION_MARGIN_PX * 2}px)`,
        maxHeight: `calc(100vh - ${CONFIG_POSITION_MARGIN_PX * 2}px)`,
        flexDirection: "column",
        overflow: "hidden",
        boxSizing: "border-box",
        zIndex: String(Z_INDEX.KEYBOARD_LAYOUT_CONFIG),
        background: "var(--kp-panel-bg, var(--kp-color-panel))",
        color: "var(--kp-color-fg)",
        border: "var(--kp-panel-border)",
        borderRadius: NCT_DARK_UI_PANEL_RADIUS,
        boxShadow: "var(--kp-panel-shadow)",
        fontFamily: NCT_DARK_UI_FONT
      });
      applyPopupThemeVars(root);
    }
    _applyPanelPositionNow() {
      if (!this.root) return;
      try {
        const resolved = applyPanelPosition(this.root, this._panelPosition, {
          margin: CONFIG_POSITION_MARGIN_PX,
          defaultAnchor: "middle-right",
          fallbackWidth: CONFIG_PANEL_WIDTH_PX,
          fallbackHeight: resolveConfigPanelHeightPx(this._panelPosition?.height)
        });
        if (resolved && !resolved.anchor) {
          this._panelPosition = { ...this._panelPosition, left: resolved.left, top: resolved.top, anchor: null };
        } else if (resolved?.anchor) {
          this._panelPosition = { ...this._panelPosition, left: resolved.left, top: resolved.top, anchor: resolved.anchor };
        }
      } catch {
      }
    }
    async _persistPosition(next) {
      const normalized = normalizePanelPositionState(
        next,
        DEFAULT_SETTINGS.panelPositions.keyboardLayoutConfig
      ) || { ...DEFAULT_SETTINGS.panelPositions.keyboardLayoutConfig };
      this._panelPosition = {
        ...this._panelPosition,
        left: normalized.left,
        top: normalized.top,
        anchor: normalized.anchor === void 0 ? null : normalized.anchor,
        ...Number.isFinite(Number(next?.width)) && Number(next.width) > 0 ? { width: Number(next.width) } : {},
        ...Number.isFinite(Number(next?.height)) && Number(next.height) > 0 ? { height: Number(next.height) } : {}
      };
      try {
        await setSettings({ panelPositions: { keyboardLayoutConfig: { ...this._panelPosition } } });
      } catch {
      }
    }
    _ensure() {
      if (this.root && this.root.isConnected) {
        this._applyProChrome(this.root);
        return;
      }
      const doc = document;
      const root = doc.createElement("div");
      root.className = "kp-layout-config-panel";
      root.setAttribute("role", "dialog");
      root.setAttribute("aria-label", "Keyboard layout editor");
      try {
        root.setAttribute("data-kp-surface", "onboarding");
      } catch {
      }
      root.hidden = true;
      this._applyProChrome(root);
      const shadowRoot = ensureOpenChromeShadow(root, { id: "keyboard-layout-config", chromeWindow: true });
      if (!shadowRoot) return;
      this._ensureStylesInjected(doc, shadowRoot);
      const header = doc.createElement("div");
      header.className = "kp-cfg-titlebar";
      header.title = "Drag to move";
      const title = doc.createElement("div");
      title.className = "kp-cfg-title";
      title.appendChild(mkCfgIcon(doc, "kp-cfg-i-kb"));
      const titleText = doc.createElement("span");
      titleText.textContent = "Keyboard Layout Editor";
      title.appendChild(titleText);
      const titleShortcut = doc.createElement("kbd");
      titleShortcut.className = "kp-cfg-title-shortcut";
      titleShortcut.setAttribute("data-kp-titlebar-shortcut", "true");
      titleShortcut.textContent = "Alt + C";
      titleShortcut.title = "Toggle with Alt+C";
      const autosavesChip = doc.createElement("span");
      autosavesChip.className = "kp-cfg-autosaves-chip";
      autosavesChip.textContent = "Autosaves";
      autosavesChip.title = "Layout changes save automatically";
      const closeBtn = doc.createElement("button");
      closeBtn.type = "button";
      closeBtn.className = "kp-cfg-close";
      closeBtn.setAttribute("aria-label", "Close layout editor");
      closeBtn.title = "Close";
      const closeLabel = doc.createElement("span");
      closeLabel.textContent = "Close";
      closeBtn.appendChild(closeLabel);
      closeBtn.appendChild(mkCfgIcon(doc, "kp-cfg-i-close"));
      closeBtn.addEventListener("click", () => {
        this.hide();
        try {
          this._onClose?.();
        } catch {
        }
      }, true);
      const titleStart = doc.createElement("div");
      titleStart.className = "kp-cfg-titlebar-start";
      titleStart.appendChild(title);
      titleStart.appendChild(titleShortcut);
      const titleEnd = doc.createElement("div");
      titleEnd.className = "kp-cfg-titlebar-end";
      titleEnd.appendChild(autosavesChip);
      titleEnd.appendChild(closeBtn);
      const titlebarPlaceMsg = doc.createElement("div");
      titlebarPlaceMsg.className = "kp-cfg-titlebar-place-msg";
      titlebarPlaceMsg.setAttribute("role", "status");
      titlebarPlaceMsg.hidden = true;
      titlebarPlaceMsg.textContent = "Move the mouse and place the action on the keyboard.";
      header.appendChild(titleStart);
      header.appendChild(titleEnd);
      header.appendChild(titlebarPlaceMsg);
      const body = doc.createElement("div");
      body.className = "kp-cfg-body";
      const mkBtn = (text, action) => {
        const b = doc.createElement("button");
        b.type = "button";
        b.className = "kp-cfg-btn";
        b.textContent = text;
        if (action) b.dataset.kpCfgAction = action;
        return b;
      };
      const mkStripLabel = (text) => {
        const el2 = doc.createElement("span");
        el2.className = "kp-cfg-strip-label";
        el2.textContent = text;
        return el2;
      };
      const mkToolGroup = (label) => {
        const g = doc.createElement("div");
        g.className = "kp-cfg-tool-group";
        g.setAttribute("role", "group");
        g.setAttribute("aria-label", label);
        return g;
      };
      const mkSep = () => {
        const s = doc.createElement("span");
        s.className = "kp-cfg-tool-sep";
        s.setAttribute("aria-hidden", "true");
        return s;
      };
      const mkSpacer = () => {
        const s = doc.createElement("span");
        s.className = "kp-cfg-spacer";
        return s;
      };
      const mkIconBtn2 = (action, title2, iconId) => {
        const b = mkBtn("", action);
        b.classList.add("kp-cfg-btn-icon");
        b.title = title2;
        b.setAttribute("aria-label", title2);
        b.appendChild(mkCfgIcon(doc, iconId));
        return b;
      };
      const strip = doc.createElement("div");
      strip.className = "kp-cfg-strip";
      const layoutStrip = doc.createElement("div");
      layoutStrip.className = "kp-cfg-layout-strip";
      const identity = doc.createElement("div");
      identity.className = "kp-cfg-layout-identity";
      const layoutLabel = doc.createElement("label");
      layoutLabel.className = "kp-cfg-strip-label";
      layoutLabel.setAttribute("for", "kp-cfg-layout-combo-input");
      layoutLabel.textContent = "Layout";
      const layoutCombo = doc.createElement("div");
      layoutCombo.className = "kp-cfg-layout-combo";
      const layoutComboInput = doc.createElement("input");
      layoutComboInput.id = "kp-cfg-layout-combo-input";
      layoutComboInput.className = "kp-cfg-field kp-cfg-combo-input";
      layoutComboInput.type = "text";
      layoutComboInput.autocomplete = "off";
      layoutComboInput.setAttribute("role", "combobox");
      layoutComboInput.setAttribute("aria-autocomplete", "list");
      layoutComboInput.setAttribute("aria-expanded", "false");
      layoutComboInput.setAttribute("aria-controls", "kp-cfg-layout-combo-list");
      layoutComboInput.setAttribute("aria-label", "Layout");
      layoutComboInput.title = "Select or rename layout";
      const currentBadge = doc.createElement("span");
      currentBadge.className = "kp-cfg-combo-current-badge";
      currentBadge.appendChild(mkCfgIcon(doc, "kp-cfg-i-check"));
      currentBadge.appendChild(doc.createTextNode("Current"));
      currentBadge.hidden = true;
      currentBadge.setAttribute("aria-hidden", "true");
      const layoutComboToggle = doc.createElement("button");
      layoutComboToggle.type = "button";
      layoutComboToggle.className = "kp-cfg-combo-toggle";
      layoutComboToggle.title = "Show layouts";
      layoutComboToggle.setAttribute("aria-label", "Show layouts");
      layoutComboToggle.tabIndex = -1;
      layoutComboToggle.appendChild(mkCfgIcon(doc, "kp-cfg-i-chevron"));
      const layoutComboList = doc.createElement("ul");
      layoutComboList.id = "kp-cfg-layout-combo-list";
      layoutComboList.className = "kp-cfg-combo-list";
      layoutComboList.setAttribute("role", "listbox");
      layoutComboList.hidden = true;
      layoutCombo.appendChild(layoutComboInput);
      layoutCombo.appendChild(currentBadge);
      layoutCombo.appendChild(layoutComboToggle);
      layoutCombo.appendChild(layoutComboList);
      const setCurrentBtn = mkBtn("Set to current", "set-current");
      setCurrentBtn.title = "Set as current layout";
      identity.appendChild(layoutLabel);
      identity.appendChild(layoutCombo);
      identity.appendChild(setCurrentBtn);
      const tools = doc.createElement("div");
      tools.className = "kp-cfg-layout-tools";
      tools.setAttribute("aria-label", "Layout actions");
      const editGroup = mkToolGroup("Edit");
      const newBtn = mkIconBtn2("new", "New layout", "kp-cfg-i-plus");
      const duplicateBtn = mkIconBtn2("duplicate", "Duplicate layout", "kp-cfg-i-copy");
      const deleteBtn = mkIconBtn2("delete", "Delete layout", "kp-cfg-i-trash");
      editGroup.appendChild(newBtn);
      editGroup.appendChild(duplicateBtn);
      editGroup.appendChild(deleteBtn);
      const transferGroup = mkToolGroup("Transfer");
      const importBtn = mkIconBtn2("import", "Import layout", "kp-cfg-i-import");
      const exportBtn = mkIconBtn2("export", "Export layout", "kp-cfg-i-export");
      transferGroup.appendChild(importBtn);
      transferGroup.appendChild(exportBtn);
      tools.appendChild(editGroup);
      tools.appendChild(mkSep());
      tools.appendChild(transferGroup);
      const primary = doc.createElement("div");
      primary.className = "kp-cfg-layout-primary";
      const handednessSeg = doc.createElement("div");
      handednessSeg.className = "kp-cfg-seg kp-cfg-handedness-seg";
      handednessSeg.setAttribute("role", "tablist");
      handednessSeg.setAttribute("aria-label", "Keyboard handedness");
      handednessSeg.hidden = true;
      for (const mode of [
        { id: "left", label: "Left", title: "Left-handed layout" },
        { id: "right", label: "Right", title: "Right-handed layout" }
      ]) {
        const b = doc.createElement("button");
        b.type = "button";
        b.className = "kp-cfg-seg-btn";
        b.setAttribute("role", "tab");
        b.dataset.kpHandedness = mode.id;
        b.textContent = mode.label;
        b.title = mode.title;
        b.addEventListener("click", () => {
          void this._setKeyboardHandedness(mode.id);
        }, true);
        handednessSeg.appendChild(b);
      }
      const optsWrap = doc.createElement("div");
      optsWrap.className = "kp-cfg-opts-wrap";
      const optsBtn = mkIconBtn2("layout-opts", "Layout options", "kp-cfg-i-more");
      optsBtn.setAttribute("aria-haspopup", "true");
      optsBtn.setAttribute("aria-expanded", "false");
      optsBtn.setAttribute("aria-controls", "kp-cfg-layout-opts-menu");
      const optsMenu = doc.createElement("div");
      optsMenu.id = "kp-cfg-layout-opts-menu";
      optsMenu.className = "kp-cfg-opts-menu";
      optsMenu.setAttribute("role", "menu");
      optsMenu.hidden = true;
      const leftHandLabel = doc.createElement("label");
      leftHandLabel.className = "kp-cfg-opts-check";
      leftHandLabel.setAttribute("role", "menuitemcheckbox");
      const leftHandedMenuToggle = doc.createElement("input");
      leftHandedMenuToggle.type = "checkbox";
      const leftHandText = doc.createElement("span");
      leftHandText.textContent = "Left-handed";
      leftHandLabel.appendChild(leftHandedMenuToggle);
      leftHandLabel.appendChild(leftHandText);
      optsMenu.appendChild(leftHandLabel);
      const numRowLabel = doc.createElement("label");
      numRowLabel.className = "kp-cfg-opts-check";
      numRowLabel.setAttribute("role", "menuitemcheckbox");
      const showNumRowToggle = doc.createElement("input");
      showNumRowToggle.type = "checkbox";
      const numRowText = doc.createElement("span");
      numRowText.textContent = "Show number row";
      numRowLabel.appendChild(showNumRowToggle);
      numRowLabel.appendChild(numRowText);
      optsMenu.appendChild(numRowLabel);
      optsWrap.appendChild(optsBtn);
      optsWrap.appendChild(optsMenu);
      primary.appendChild(handednessSeg);
      primary.appendChild(optsWrap);
      const importFile = doc.createElement("input");
      importFile.type = "file";
      importFile.accept = "application/json,.json";
      importFile.hidden = true;
      layoutStrip.appendChild(identity);
      layoutStrip.appendChild(tools);
      layoutStrip.appendChild(primary);
      strip.appendChild(layoutStrip);
      strip.appendChild(importFile);
      const mainRow = doc.createElement("div");
      mainRow.className = "kp-cfg-main";
      const workspace = doc.createElement("div");
      workspace.className = "kp-cfg-workspace";
      if (!BUILD_ENABLE_MACRO_BUILDER) workspace.classList.add("kp-cfg-no-macro-builder");
      const libraryPane = doc.createElement("section");
      libraryPane.className = "kp-cfg-pane kp-cfg-pane-library";
      libraryPane.setAttribute("aria-label", "Actions Library");
      const libraryHdr = doc.createElement("div");
      libraryHdr.className = "kp-cfg-pane-hdr";
      const libraryTitle = doc.createElement("span");
      libraryTitle.className = "kp-cfg-pane-title";
      libraryTitle.appendChild(mkCfgIcon(doc, "kp-cfg-i-lib"));
      libraryTitle.appendChild(doc.createTextNode("Actions Library"));
      const searchWrap = doc.createElement("div");
      searchWrap.className = "kp-cfg-search-wrap";
      searchWrap.appendChild(mkCfgIcon(doc, "kp-cfg-i-search"));
      const search = doc.createElement("input");
      search.type = "search";
      search.className = "kp-cfg-field kp-cfg-search";
      search.placeholder = "Search\u2026";
      searchWrap.appendChild(search);
      const libTabs = doc.createElement("div");
      libTabs.className = "kp-cfg-seg";
      libTabs.setAttribute("role", "tablist");
      libTabs.setAttribute("aria-label", "Library filter");
      const fnCategorySelect = doc.createElement("select");
      fnCategorySelect.className = "kp-cfg-field kp-cfg-fn-cat";
      fnCategorySelect.setAttribute("aria-label", "Function category");
      const fnCategoryWrap = doc.createElement("div");
      fnCategoryWrap.className = "kp-cfg-fn-cat-wrap";
      fnCategoryWrap.hidden = true;
      fnCategoryWrap.appendChild(fnCategorySelect);
      libraryHdr.appendChild(libraryTitle);
      libraryHdr.appendChild(searchWrap);
      libraryHdr.appendChild(libTabs);
      libraryHdr.appendChild(fnCategoryWrap);
      const list = doc.createElement("div");
      list.setAttribute("data-kp-layout-list", "true");
      list.className = `${KEYBINDINGS_UI_ROOT_CLASS} kp-cfg-pane-scroll ${NCT_DARK_UI_SCROLLBAR_CLASS}`;
      Object.assign(list.style, {
        display: "flex",
        flexDirection: "column",
        gap: "10px",
        flex: "1 1 auto",
        // Allow shrink-to-titlebar so the User Macros splitter can reach the library header.
        minHeight: "0",
        overflow: "auto"
      });
      const viewSeg = doc.createElement("div");
      viewSeg.className = "kp-cfg-seg";
      viewSeg.setAttribute("role", "tablist");
      viewSeg.setAttribute("aria-label", "Library layout");
      for (const mode of [
        { id: "cards", label: "Cards" },
        { id: "table", label: "Table" }
      ]) {
        const b = doc.createElement("button");
        b.type = "button";
        b.className = "kp-cfg-seg-btn";
        b.setAttribute("role", "tab");
        b.dataset.kpLibView = mode.id;
        b.textContent = mode.label;
        b.addEventListener("click", () => {
          this._libViewMode = /** @type {'cards'|'table'} */
          mode.id;
          this._syncLibViewSeg();
          this._renderRightList();
        }, true);
        viewSeg.appendChild(b);
      }
      const instructions = doc.createElement("div");
      instructions.className = "kp-cfg-lib-instructions";
      instructions.setAttribute("aria-label", "Layout assignment instructions");
      const instructionsHead = doc.createElement("div");
      instructionsHead.className = "kp-cfg-lib-instructions-head";
      const instructionsToggle = doc.createElement("button");
      instructionsToggle.type = "button";
      instructionsToggle.className = "kp-cfg-lib-instructions-toggle";
      instructionsToggle.setAttribute("aria-expanded", "true");
      instructionsToggle.setAttribute("aria-controls", "kp-cfg-lib-instructions-body");
      instructionsToggle.title = "Collapse instructions";
      instructionsToggle.appendChild(mkCfgIcon(doc, "kp-cfg-i-chevron"));
      const instructionsTitle = doc.createElement("span");
      instructionsTitle.className = "kp-cfg-lib-instructions-title";
      instructionsTitle.textContent = "Instructions:";
      instructionsToggle.appendChild(instructionsTitle);
      const placePulse = doc.createElement("div");
      placePulse.className = "kp-cfg-lib-place-pulse";
      placePulse.setAttribute("role", "status");
      placePulse.hidden = true;
      placePulse.textContent = "Click the desired key cap location on the Keyboard Reference.";
      instructionsHead.appendChild(viewSeg);
      instructionsHead.appendChild(instructionsToggle);
      instructionsHead.appendChild(placePulse);
      const instructionsBody = doc.createElement("p");
      instructionsBody.className = "kp-cfg-lib-instructions-body";
      instructionsBody.id = "kp-cfg-lib-instructions-body";
      instructionsBody.textContent = "Click a key cap below once and move the mouse. Use the placement arrow to place the action on the keyboard.";
      instructions.appendChild(instructionsHead);
      instructions.appendChild(instructionsBody);
      instructionsToggle.addEventListener("click", (e) => {
        try {
          e.preventDefault();
          e.stopPropagation();
        } catch {
        }
        this._setLibInstructionsExpanded(!this._libInstructionsExpanded, { persist: true });
      }, true);
      libraryPane.appendChild(libraryHdr);
      libraryPane.appendChild(instructions);
      libraryPane.appendChild(list);
      const createPane = doc.createElement("aside");
      createPane.className = "kp-cfg-pane kp-cfg-pane-create";
      createPane.setAttribute("aria-label", "User Macros");
      const createHdr = doc.createElement("div");
      createHdr.className = "kp-cfg-pane-hdr";
      const createTitle = doc.createElement("span");
      createTitle.className = "kp-cfg-pane-title";
      createTitle.appendChild(mkCfgIcon(doc, "kp-cfg-i-create"));
      createTitle.appendChild(doc.createTextNode("User Macros"));
      const createModeSeg = doc.createElement("div");
      createModeSeg.className = "kp-cfg-seg";
      createModeSeg.setAttribute("role", "tablist");
      createModeSeg.setAttribute("aria-label", "User Macros mode");
      for (const mode of [{ id: "script", label: "Macro Script" }, { id: "macroKey", label: "Macro Key" }]) {
        const b = doc.createElement("button");
        b.type = "button";
        b.className = "kp-cfg-seg-btn";
        b.setAttribute("role", "tab");
        b.dataset.kpCreateMode = mode.id;
        b.textContent = mode.label;
        b.addEventListener("click", () => this._setCreateMode(mode.id, { open: true }), true);
        createModeSeg.appendChild(b);
      }
      const createToggleBtn = mkBtn("Expand", "toggle-create");
      createToggleBtn.title = "Show/hide the User Macros builder";
      createToggleBtn.prepend(mkCfgIcon(doc, "kp-cfg-i-expand"));
      const expandUpBtn = mkBtn("Expand up", "expand-create-up");
      expandUpBtn.classList.add("kp-cfg-btn-expand-up");
      expandUpBtn.title = "Expand upward to the Actions Library titlebar";
      expandUpBtn.hidden = true;
      expandUpBtn.prepend(mkCfgIcon(doc, "kp-cfg-i-expand-up"));
      const createHdrActions = doc.createElement("span");
      createHdrActions.className = "kp-cfg-hdr-actions";
      createHdrActions.appendChild(expandUpBtn);
      createHdrActions.appendChild(createToggleBtn);
      createHdr.appendChild(createTitle);
      createHdr.appendChild(createModeSeg);
      createHdr.appendChild(createHdrActions);
      createHdr.setAttribute("role", "button");
      createHdr.tabIndex = 0;
      createHdr.setAttribute("aria-expanded", "false");
      createHdr.title = "Expand User Macros";
      createHdr.addEventListener("click", (e) => {
        if (e.target?.closest?.("button, .kp-cfg-seg, [data-kp-create-mode]")) return;
        this._setCreateOpen(!this._createOpen);
      }, true);
      createHdr.addEventListener("keydown", (e) => {
        if (e.key !== "Enter" && e.key !== " ") return;
        if (e.target !== createHdr) return;
        e.preventDefault();
        this._setCreateOpen(!this._createOpen);
      }, true);
      const createBody = doc.createElement("div");
      createBody.className = "kp-cfg-create-body";
      const scriptPanel = doc.createElement("div");
      scriptPanel.className = "kp-cfg-script-panel";
      const scriptToolbar = doc.createElement("div");
      scriptToolbar.className = "kp-cfg-script-toolbar";
      const macroNameInput = doc.createElement("input");
      macroNameInput.type = "text";
      macroNameInput.className = "kp-cfg-field kp-cfg-macro-name";
      macroNameInput.placeholder = "Macro name";
      const newMacroBtn = mkBtn("New Macro", "macro-new");
      const saveMacroBtn = mkBtn("Save", "macro-save");
      saveMacroBtn.classList.add("kp-cfg-btn-lit");
      saveMacroBtn.prepend(mkCfgIcon(doc, "kp-cfg-i-check"));
      const placeMacroBtn = mkBtn("Place", "macro-place");
      placeMacroBtn.prepend(mkCfgIcon(doc, "kp-cfg-i-place"));
      const runMacroBtn = mkBtn("Run", "macro-run");
      scriptToolbar.appendChild(mkStripLabel("Macro"));
      scriptToolbar.appendChild(macroNameInput);
      scriptToolbar.appendChild(newMacroBtn);
      scriptToolbar.appendChild(saveMacroBtn);
      scriptToolbar.appendChild(placeMacroBtn);
      scriptToolbar.appendChild(runMacroBtn);
      const stockBanner = doc.createElement("div");
      stockBanner.className = "kp-cfg-stock-banner";
      stockBanner.textContent = "Stock macro \u2014 Save creates your editable copy. The original stays unchanged.";
      stockBanner.hidden = true;
      const scriptCanvas = doc.createElement("div");
      scriptCanvas.className = "kp-cfg-script-canvas";
      const logicPalette = doc.createElement("fieldset");
      logicPalette.className = "kp-cfg-logic-palette";
      logicPalette.setAttribute("aria-label", "Logic and primitive steps");
      const logicLegend = doc.createElement("legend");
      logicLegend.textContent = "Logic";
      logicPalette.appendChild(logicLegend);
      for (const def of MACRO_LOGIC_DEFS) {
        const chip = doc.createElement("button");
        chip.type = "button";
        chip.className = "kp-cfg-logic-chip";
        chip.dataset.kpLogicKind = def.kind;
        const strong = doc.createElement("strong");
        strong.textContent = def.label;
        const span = doc.createElement("span");
        span.textContent = def.description;
        chip.appendChild(strong);
        chip.appendChild(span);
        chip.addEventListener("click", () => this._addDraftLogicStep(def.kind), true);
        logicPalette.appendChild(chip);
      }
      const jsChip = doc.createElement("button");
      jsChip.type = "button";
      jsChip.className = "kp-cfg-logic-chip";
      jsChip.dataset.kpPrimitiveFunction = "EXECUTE_JS";
      const jsStrong = doc.createElement("strong");
      jsStrong.textContent = "Execute JS";
      const jsSpan = doc.createElement("span");
      jsSpan.textContent = "Run a pasted script";
      jsChip.appendChild(jsStrong);
      jsChip.appendChild(jsSpan);
      jsChip.addEventListener("click", () => this._addDraftFunctionStep("EXECUTE_JS"), true);
      logicPalette.appendChild(jsChip);
      if (getFunctionDef("GET_TEXT_RANGE")) {
        const rangeChip = doc.createElement("button");
        rangeChip.type = "button";
        rangeChip.className = "kp-cfg-logic-chip";
        rangeChip.dataset.kpPrimitiveFunction = "GET_TEXT_RANGE";
        const rangeStrong = doc.createElement("strong");
        rangeStrong.textContent = "Get Text Range";
        const rangeSpan = doc.createElement("span");
        rangeSpan.textContent = "Capture the current selection for the next step";
        rangeChip.appendChild(rangeStrong);
        rangeChip.appendChild(rangeSpan);
        rangeChip.addEventListener("click", () => this._addDraftFunctionStep("GET_TEXT_RANGE"), true);
        logicPalette.appendChild(rangeChip);
      }
      const popoverChip = doc.createElement("button");
      popoverChip.type = "button";
      popoverChip.className = "kp-cfg-logic-chip";
      popoverChip.dataset.kpPrimitiveFunction = "SHOW_POPOVER";
      const popoverStrong = doc.createElement("strong");
      popoverStrong.textContent = "Show Popover";
      const popoverSpan = doc.createElement("span");
      popoverSpan.textContent = "Route prior result to a popover";
      popoverChip.appendChild(popoverStrong);
      popoverChip.appendChild(popoverSpan);
      popoverChip.addEventListener("click", () => this._addDraftFunctionStep("SHOW_POPOVER"), true);
      logicPalette.appendChild(popoverChip);
      const stepsPane = doc.createElement("div");
      stepsPane.className = "kp-cfg-steps-pane";
      scriptCanvas.appendChild(logicPalette);
      scriptCanvas.appendChild(stepsPane);
      const addStepRow = doc.createElement("div");
      addStepRow.className = "kp-cfg-strip-row";
      const addStepSelect = doc.createElement("select");
      addStepSelect.className = "kp-cfg-field";
      Object.assign(addStepSelect.style, { width: "auto", flex: "1 1 150px", maxWidth: "240px" });
      addStepSelect.setAttribute("aria-label", "Function to add as a step");
      for (const def of listFunctionDefs()) {
        const opt = doc.createElement("option");
        opt.value = def.id;
        opt.textContent = def.label;
        addStepSelect.appendChild(opt);
      }
      const addStepDelayLabel = doc.createElement("label");
      Object.assign(addStepDelayLabel.style, {
        display: "inline-flex",
        alignItems: "center",
        gap: "4px",
        fontSize: "10px",
        opacity: "0.8"
      });
      const addStepDelayText = doc.createElement("span");
      addStepDelayText.textContent = "delay ms";
      const addStepDelayInput = doc.createElement("input");
      addStepDelayInput.type = "number";
      addStepDelayInput.min = "0";
      addStepDelayInput.step = "10";
      addStepDelayInput.value = "0";
      addStepDelayInput.className = "kp-cfg-field";
      Object.assign(addStepDelayInput.style, { width: "68px", padding: "4px 5px" });
      addStepDelayLabel.appendChild(addStepDelayText);
      addStepDelayLabel.appendChild(addStepDelayInput);
      const addStepBtn = mkBtn("+ Add step", "macro-add-step");
      addStepBtn.title = "Append the selected Actions Library Function (or the dropdown pick)";
      addStepRow.appendChild(addStepSelect);
      addStepRow.appendChild(addStepDelayLabel);
      addStepRow.appendChild(addStepBtn);
      const scriptFooter = doc.createElement("div");
      scriptFooter.className = "kp-cfg-strip-row";
      const placeHint = doc.createElement("span");
      placeHint.className = "kp-cfg-hint";
      placeHint.textContent = "Click a Logic chip, Get Text Range, Show Popover, or Execute JS, or pick a Function below and Add step. Save adds it to Macros.";
      const scriptMeta = doc.createElement("span");
      scriptMeta.className = "kp-cfg-hint";
      scriptMeta.textContent = "0 steps";
      scriptFooter.appendChild(placeHint);
      scriptFooter.appendChild(mkSpacer());
      scriptFooter.appendChild(scriptMeta);
      scriptPanel.appendChild(scriptToolbar);
      scriptPanel.appendChild(stockBanner);
      scriptPanel.appendChild(scriptCanvas);
      scriptPanel.appendChild(addStepRow);
      scriptPanel.appendChild(scriptFooter);
      const macroKeyPanel = doc.createElement("div");
      macroKeyPanel.className = "kp-cfg-macrokey-panel";
      macroKeyPanel.hidden = true;
      const macroKeysActionsRow = doc.createElement("div");
      Object.assign(macroKeysActionsRow.style, { display: "flex", flexDirection: "column", gap: "6px" });
      macroKeyPanel.appendChild(macroKeysActionsRow);
      createBody.appendChild(scriptPanel);
      createBody.appendChild(macroKeyPanel);
      createPane.appendChild(createHdr);
      createPane.appendChild(createBody);
      const libraryCreateSplitter = doc.createElement("div");
      libraryCreateSplitter.className = "kp-cfg-split kp-cfg-split-h";
      libraryCreateSplitter.setAttribute("role", "separator");
      libraryCreateSplitter.setAttribute("aria-orientation", "horizontal");
      libraryCreateSplitter.setAttribute("aria-label", "Resize User Macros pane");
      libraryCreateSplitter.tabIndex = 0;
      workspace.appendChild(libraryPane);
      workspace.appendChild(libraryCreateSplitter);
      workspace.appendChild(createPane);
      const inspectorPane = doc.createElement("aside");
      inspectorPane.className = "kp-cfg-pane kp-cfg-pane-inspector";
      inspectorPane.setAttribute("aria-label", "Inspector");
      const inspectorRail = doc.createElement("button");
      inspectorRail.type = "button";
      inspectorRail.className = "kp-cfg-inspector-rail";
      inspectorRail.appendChild(mkCfgIcon(doc, "kp-cfg-i-eye"));
      const railLabel = doc.createElement("span");
      railLabel.textContent = "Inspector";
      inspectorRail.appendChild(railLabel);
      inspectorRail.title = "Expand Inspector";
      inspectorRail.addEventListener("click", () => this._setInspectorOpen(true), true);
      const inspectorExpanded = doc.createElement("div");
      inspectorExpanded.className = "kp-cfg-inspector-expanded";
      const inspectorHdr = doc.createElement("div");
      inspectorHdr.className = "kp-cfg-pane-hdr";
      const inspectorTitle = doc.createElement("span");
      inspectorTitle.className = "kp-cfg-pane-title";
      inspectorTitle.appendChild(mkCfgIcon(doc, "kp-cfg-i-eye"));
      inspectorTitle.appendChild(doc.createTextNode("Inspector"));
      const collapseInspectorBtn = mkBtn("Collapse", "collapse-inspector");
      collapseInspectorBtn.prepend(mkCfgIcon(doc, "kp-cfg-i-collapse-right"));
      collapseInspectorBtn.addEventListener("click", () => this._setInspectorOpen(false), true);
      inspectorHdr.appendChild(inspectorTitle);
      inspectorHdr.appendChild(mkSpacer());
      inspectorHdr.appendChild(collapseInspectorBtn);
      const inspectorBody = doc.createElement("div");
      inspectorBody.className = "kp-cfg-pane-scroll";
      inspectorExpanded.appendChild(inspectorHdr);
      inspectorExpanded.appendChild(inspectorBody);
      inspectorPane.appendChild(inspectorRail);
      inspectorPane.appendChild(inspectorExpanded);
      const workspaceInspectorSplitter = doc.createElement("div");
      workspaceInspectorSplitter.className = "kp-cfg-split kp-cfg-split-v";
      workspaceInspectorSplitter.setAttribute("role", "separator");
      workspaceInspectorSplitter.setAttribute("aria-orientation", "vertical");
      workspaceInspectorSplitter.setAttribute("aria-label", "Resize Inspector");
      workspaceInspectorSplitter.tabIndex = 0;
      mainRow.appendChild(workspace);
      mainRow.appendChild(workspaceInspectorSplitter);
      mainRow.appendChild(inspectorPane);
      body.appendChild(strip);
      body.appendChild(mainRow);
      shadowRoot.appendChild(header);
      shadowRoot.appendChild(body);
      (doc.body || doc.documentElement).appendChild(root);
      this.root = root;
      this.shadowRoot = shadowRoot;
      this._listEl = list;
      this._layoutCombo = layoutCombo;
      this._layoutComboInput = layoutComboInput;
      this._layoutComboList = layoutComboList;
      this._layoutComboToggle = layoutComboToggle;
      this._setCurrentBtn = setCurrentBtn;
      this._layoutOptsWrap = optsWrap;
      this._layoutOptsBtn = optsBtn;
      this._layoutOptsMenu = optsMenu;
      this._handednessSeg = handednessSeg;
      this._leftHandedMenuToggle = leftHandedMenuToggle;
      this._searchInput = search;
      this._currentBadge = currentBadge;
      this._showNumRowToggle = showNumRowToggle;
      this._mainRow = mainRow;
      this._libTabsEl = libTabs;
      this._fnCategorySelect = fnCategorySelect;
      this._fnCategoryWrap = fnCategoryWrap;
      this._createPane = createPane;
      this._createBody = createBody;
      this._createToggleBtn = createToggleBtn;
      this._createExpandUpBtn = expandUpBtn;
      this._createModeSeg = createModeSeg;
      this._libraryCreateSplitter = libraryCreateSplitter;
      this._workspaceInspectorSplitter = workspaceInspectorSplitter;
      this._scriptPanel = scriptPanel;
      this._macroKeyPanel = macroKeyPanel;
      this._macroNameInput = macroNameInput;
      this._scriptStepsHost = stepsPane;
      this._scriptStockBanner = stockBanner;
      this._scriptMetaEl = scriptMeta;
      this._placeHintEl = placeHint;
      this._placePulseEl = placePulse;
      this._titlebarPlaceMsgEl = titlebarPlaceMsg;
      this._libInstructionsEl = instructions;
      this._libInstructionsToggleEl = instructionsToggle;
      this._libViewSegEl = viewSeg;
      this._addStepSelect = addStepSelect;
      this._addStepDelayInput = addStepDelayInput;
      this._macroKeysActionsRow = macroKeysActionsRow;
      this._inspectorPane = inspectorPane;
      this._inspectorRail = inspectorRail;
      this._inspectorBody = inspectorBody;
      this._macroKeyEditorHost = inspectorBody;
      this._renderLibraryTabs();
      this._syncLibViewSeg();
      this._renderFunctionCategorySelect();
      enhanceNativeSelect(fnCategorySelect);
      enhanceNativeSelect(addStepSelect);
      this._setLibInstructionsExpanded(this._libInstructionsExpanded, { persist: false });
      this._setInspectorOpen(this._inspectorOpen);
      this._setCreateOpen(this._createOpen);
      this._setCreateMode(this._createMode);
      this._resetMacroDraft();
      this._renderInspector();
      this._wireSplitters();
      try {
        const api = makePanelDraggable(root, header, {
          margin: CONFIG_POSITION_MARGIN_PX,
          excludeSelector: 'button[aria-label="Close layout editor"], .kp-cfg-close',
          onMoveEnd: (state) => {
            if (!state?.moved) return;
            void this._persistPosition({
              left: state.left,
              top: state.top,
              anchor: state.anchor
            });
          }
        });
        this._dragDispose = api?.dispose || null;
      } catch {
      }
      try {
        this._resizeDispose = makePanelResizable(root, {
          mount: shadowRoot,
          minWidth: 720,
          minHeight: CONFIG_PANEL_MIN_HEIGHT_PX,
          margin: CONFIG_POSITION_MARGIN_PX,
          onResizeEnd: (size) => {
            void this._persistPosition({
              ...this._panelPosition,
              width: size.width,
              height: size.height
            });
          }
        });
      } catch {
      }
      void (async () => {
        try {
          const settings = await getSettings();
          const pos = settings?.panelPositions?.keyboardLayoutConfig;
          if (pos && typeof pos === "object") {
            const normalized = normalizePanelPositionState(
              pos,
              DEFAULT_SETTINGS.panelPositions.keyboardLayoutConfig
            );
            this._panelPosition = {
              ...normalized || this._panelPosition,
              ...Number.isFinite(Number(pos.width)) && Number(pos.width) > 0 ? { width: Number(pos.width) } : {},
              ...Number.isFinite(Number(pos.height)) && Number(pos.height) > 0 ? { height: Number(pos.height) } : {}
            };
            this._applyProChrome(root);
          }
          try {
            showNumRowToggle.checked = !!settings?.keyboardReferenceShowNumberRow;
          } catch {
          }
          try {
            this._syncHandednessUi(settings?.keyboardHandedness);
          } catch {
          }
          this._applyTableExpandedFromSettings(settings?.actionsLibraryTableExpanded);
          this._setLibInstructionsExpanded(
            settings?.actionsLibraryInstructionsExpanded !== false,
            { persist: false }
          );
          this._positionHydrated = true;
          if (this.isOpen()) this._applyPanelPositionNow();
          try {
            this._renderRightList();
          } catch {
          }
        } catch {
        }
      })();
      search.addEventListener("input", () => this._renderRightList(), true);
      fnCategorySelect.addEventListener("change", () => {
        this._libFunctionCategory = String(fnCategorySelect.value || "");
        this._renderRightList();
      }, true);
      createToggleBtn.addEventListener("click", (e) => {
        e.stopPropagation();
        this._setCreateOpen(!this._createOpen);
      }, true);
      expandUpBtn.addEventListener("click", (e) => {
        e.stopPropagation();
        this._toggleCreateMaximized();
      }, true);
      macroNameInput.addEventListener("input", () => {
        if (!this._macroDraft || this._macroDraft.stock) return;
        this._macroDraft.label = String(macroNameInput.value || "");
        this._macroDraft.dirty = true;
      }, true);
      newMacroBtn.addEventListener("click", () => this._startNewMacroDraft(), true);
      saveMacroBtn.addEventListener("click", () => {
        void this._saveMacroDraft();
      }, true);
      placeMacroBtn.addEventListener("click", () => {
        void this._placeMacroDraft();
      }, true);
      runMacroBtn.addEventListener("click", () => {
        void this._runMacroDraft();
      }, true);
      addStepBtn.addEventListener("click", () => {
        const fromLib = String(this._selectedLibraryFunctionId || "");
        const fromSelect = String(addStepSelect.value || "");
        const functionId = fromLib && getFunctionDef(fromLib) ? fromLib : fromSelect;
        const delay = Math.max(0, Math.floor(Number(addStepDelayInput.value) || 0));
        this._addDraftFunctionStep(functionId, { delayMsBefore: delay });
      }, true);
      layoutComboToggle.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        this._setLayoutComboOpen(!!layoutComboList.hidden);
      }, true);
      layoutComboList.addEventListener("click", (e) => {
        const opt = e.target?.closest?.("[data-kp-layout-id]");
        if (!opt) return;
        e.preventDefault();
        e.stopPropagation();
        void this._selectLayoutByValue(String(opt.getAttribute("data-kp-layout-id") || ""));
        this._setLayoutComboOpen(false);
      }, true);
      layoutComboInput.addEventListener("keydown", (e) => {
        if (e.key === "ArrowDown") {
          e.preventDefault();
          this._setLayoutComboOpen(true);
          return;
        }
        if (e.key === "Escape") {
          e.preventDefault();
          this._setLayoutComboOpen(false);
          this._renderLayoutSelect();
          return;
        }
        if (e.key === "Enter") {
          e.preventDefault();
          void this._commitLayoutComboRename();
          this._setLayoutComboOpen(false);
        }
      }, true);
      layoutComboInput.addEventListener("change", () => {
        void this._commitLayoutComboRename();
      }, true);
      layoutComboInput.addEventListener("focus", () => {
      }, true);
      doc.addEventListener("click", (e) => {
        const t = e.target;
        if (!(t instanceof Node)) return;
        if (containsComposed(layoutCombo, t)) return;
        this._setLayoutComboOpen(false);
        if (containsComposed(optsWrap, t)) return;
        this._setLayoutOptsOpen(false);
      }, true);
      optsBtn.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        this._setLayoutOptsOpen(!!optsMenu.hidden);
      }, true);
      newBtn.addEventListener("click", async () => {
        await this.createNewLayout();
      }, true);
      duplicateBtn.addEventListener("click", async () => {
        await this.duplicateLayout();
      }, true);
      deleteBtn.addEventListener("click", async () => {
        if (this._st.mode !== "user" || !this._st.userLayoutId) return;
        const deletedId = String(this._st.userLayoutId);
        try {
          await deleteUserKeyboardLayout(deletedId);
          this._st.userLayouts = await listUserKeyboardLayouts();
          const remaining = (this._st.userLayouts || []).filter((l) => l && l.id);
          if (remaining.length) {
            const next = remaining[0];
            this._st.mode = "user";
            this._st.userLayoutId = next.id;
            this._st.userLayout = next;
            this._renderLayoutSelect();
            this._renderRightList();
            this._emitChange();
          } else {
            await this.duplicateLayout({ setCurrent: true });
          }
          try {
            const kp = this._kp;
            const cur = String(kp?._currentKeyboardLayoutId || kp?._settings?.currentKeyboardLayoutId || "");
            if (cur === `user:${deletedId}` && this._st.userLayoutId) {
              const nextId = `user:${this._st.userLayoutId}`;
              await setSettings({ currentKeyboardLayoutId: nextId });
              if (kp) {
                kp._currentKeyboardLayoutId = nextId;
                kp._currentUserLayout = this._st.userLayout;
                if (kp._settings) kp._settings.currentKeyboardLayoutId = nextId;
              }
            }
            if (typeof kp?._refreshCurrentKeyboardLayoutFromSettings === "function") {
              void kp._refreshCurrentKeyboardLayoutFromSettings();
            } else {
              kp?.floatingKeyboardHelp?.setActiveLayoutSelection?.({
                currentKeyboardLayoutId: String(kp?._currentKeyboardLayoutId || "builtin"),
                userLayout: kp?._currentUserLayout || null,
                userMacros: kp?._currentUserMacros || []
              });
            }
          } catch {
          }
        } catch {
          this._notify("Failed to delete layout.", "error");
        }
      }, true);
      exportBtn.addEventListener("click", async () => {
        if (this._st.mode !== "user" || !this._st.userLayout) return;
        try {
          const payload = exportUserKeyboardLayout(this._st.userLayout);
          const json = JSON.stringify(payload, null, 2);
          const blob = new Blob([json], { type: "application/json" });
          const url = URL.createObjectURL(blob);
          const a = doc.createElement("a");
          a.href = url;
          const safe = String(this._st.userLayout.label || "layout").replaceAll(/[^a-zA-Z0-9-_]+/g, "_");
          a.download = `keypilot-layout-${safe}.json`;
          doc.body.appendChild(a);
          a.click();
          a.remove();
          URL.revokeObjectURL(url);
        } catch {
          this._notify("Failed to export layout.", "error");
        }
      }, true);
      importBtn.addEventListener("click", () => {
        try {
          importFile.click();
        } catch {
        }
      }, true);
      importFile.addEventListener("change", async () => {
        const file = importFile.files && importFile.files[0] ? importFile.files[0] : null;
        if (!file) return;
        try {
          const text = await file.text();
          const raw = JSON.parse(text);
          const created = await importUserKeyboardLayout(raw);
          if (!created) {
            this._notify("Invalid layout file.", "error");
            return;
          }
          this._st.userLayouts = await listUserKeyboardLayouts();
          this._st.mode = "user";
          this._st.userLayoutId = created.id;
          this._st.userLayout = created;
          this._renderLayoutSelect();
          this._renderRightList();
          this._emitChange();
        } catch {
          this._notify("Failed to import layout.", "error");
        } finally {
          try {
            importFile.value = "";
          } catch {
          }
        }
      }, true);
      setCurrentBtn.addEventListener("click", async () => {
        try {
          const v = this._selectedLayoutValue();
          if (v.startsWith("user:")) {
            await setSettings({ currentKeyboardLayoutId: v });
            if (this._kp?._settings) this._kp._settings.currentKeyboardLayoutId = v;
            try {
              if (this._st.userLayout && `user:${this._st.userLayout.id}` === v) {
                await this._kp?.applyLiveUserLayout?.(this._st.userLayout, {
                  setAsCurrent: true,
                  macros: this._st.macros,
                  actions: this._st.actions
                });
              } else {
                await this._kp?._refreshCurrentKeyboardLayoutFromSettings?.();
              }
            } catch {
            }
          } else {
            const familyId = parseBuiltinFamilySelectValue(v) || "browsing";
            await setSettings({
              currentKeyboardLayoutId: "builtin",
              keyboardLayoutFamilyId: familyId
            });
            if (this._kp?._settings) {
              this._kp._settings.currentKeyboardLayoutId = "builtin";
              this._kp._settings.keyboardLayoutFamilyId = familyId;
            }
            try {
              this._kp?._applyKeyboardLayoutFromSettings?.();
              await this._kp?._refreshCurrentKeyboardLayoutFromSettings?.();
            } catch {
            }
          }
          this._notify("Set as current keyboard layout.", "success");
          this._renderLayoutSelect();
          this._emitChange();
        } catch {
          this._notify("Failed to set current keyboard layout.", "error");
        }
      }, true);
      showNumRowToggle.addEventListener("change", async () => {
        try {
          const on = !!showNumRowToggle.checked;
          await setSettings({ keyboardReferenceShowNumberRow: on });
          if (this._kp?._settings) this._kp._settings.keyboardReferenceShowNumberRow = on;
          try {
            this._kp?._applyKeyboardLayoutFromSettings?.();
          } catch {
          }
          this._emitChange();
        } catch {
        }
      }, true);
      leftHandedMenuToggle.addEventListener("change", async () => {
        try {
          await this._setKeyboardHandedness(leftHandedMenuToggle.checked ? "left" : "right");
          this._setLayoutOptsOpen(false);
        } catch {
        }
      }, true);
      if (!this._settingsStorageListener) {
        this._settingsStorageListener = (changes, area) => {
          try {
            if (area && area !== "sync" && area !== "local") return;
            const entry = changes && changes[SETTINGS_STORAGE_KEY];
            if (!entry || !entry.newValue) return;
            const hand = entry.newValue.keyboardHandedness;
            this._syncHandednessUi(hand);
            try {
              showNumRowToggle.checked = !!entry.newValue.keyboardReferenceShowNumberRow;
            } catch {
            }
          } catch {
          }
        };
        try {
          chrome?.storage?.onChanged?.addListener?.(this._settingsStorageListener);
        } catch {
        }
      }
    }
    /**
     * Current handedness from KeyPilot settings (fallback: right).
     * @param {any} [raw]
     * @returns {'left'|'right'}
     */
    _currentHandedness(raw) {
      const fromArg = raw != null ? raw : void 0;
      const fromKp = this._kp?._settings?.keyboardHandedness;
      return normalizeKeyboardHandedness(
        fromArg !== void 0 ? fromArg : fromKp ?? DEFAULT_SETTINGS.keyboardHandedness
      );
    }
    /**
     * Show Left|Right segment only while left-handed is active; keep [...] menu checkbox in sync.
     * @param {any} [rawHandedness]
     */
    _syncHandednessUi(rawHandedness) {
      const hand = this._currentHandedness(rawHandedness);
      const seg = this._handednessSeg;
      if (seg) {
        seg.hidden = hand !== "left";
        try {
          seg.querySelectorAll("[data-kp-handedness]").forEach((btn) => {
            const on = btn.getAttribute("data-kp-handedness") === hand;
            btn.setAttribute("aria-selected", on ? "true" : "false");
          });
        } catch {
        }
      }
      if (this._leftHandedMenuToggle) {
        this._leftHandedMenuToggle.checked = hand === "left";
      }
    }
    /**
     * Persist handedness and refresh layout chrome (Keyboard Reference + this panel).
     * Selecting Right hides the LAYOUT segment; Left (via Settings or [...]) shows it again.
     * @param {'left'|'right'|string} hand
     */
    async _setKeyboardHandedness(hand) {
      const next = normalizeKeyboardHandedness(hand);
      const prev = this._currentHandedness();
      if (next === prev) {
        this._syncHandednessUi(next);
        return;
      }
      try {
        if (typeof this._kp?.setKeyboardHandedness === "function") {
          await this._kp.setKeyboardHandedness(next);
          if (this._st.mode === "user" && this._st.userLayout) {
            await this._reloadStore();
          }
        } else {
          await setSettings({ keyboardHandedness: next });
        }
      } catch {
      }
      try {
        if (this._kp?._settings) this._kp._settings.keyboardHandedness = next;
      } catch {
      }
      try {
        const familyId = normalizeKeyboardLayoutFamilyId(
          this._kp?._settings?.keyboardLayoutFamilyId
        );
        this._st.builtinLayoutId = resolveKeyboardLayoutId({ familyId, handedness: next });
      } catch {
      }
      try {
        this._kp?._applyKeyboardLayoutFromSettings?.();
      } catch {
      }
      this._syncHandednessUi(next);
      try {
        this._renderLayoutSelect();
      } catch {
      }
      this._emitChange();
    }
    _notify(message, type2) {
      try {
        this._kp?.overlayManager?.showNotification?.(message, type2);
      } catch {
      }
    }
    /** Refresh `_st.actions` from the store and re-derive the Macro Keys tab's filtered view. */
    async _reloadActions() {
      try {
        this._st.actions = await listUserActions();
      } catch {
        this._st.actions = [];
      }
      this._st.macroKeys = this._st.actions.map(macroKeyLikeFromUserAction).filter(Boolean);
    }
    async _reloadStore() {
      try {
        this._st.userLayouts = await listUserKeyboardLayouts();
      } catch {
        this._st.userLayouts = [];
      }
      try {
        this._st.macros = await listUserMacros();
      } catch {
        this._st.macros = [];
      }
      try {
        this._st.stockMacros = listStockMacros();
      } catch {
        this._st.stockMacros = [];
      }
      await this._reloadActions();
      if (this._st.mode === "user" && this._st.userLayoutId) {
        const missingId = String(this._st.userLayoutId);
        const found = this._st.userLayouts.find((l) => l && l.id === missingId) || null;
        this._st.userLayout = found;
        if (!found) {
          this._st.mode = "builtin";
          this._st.userLayoutId = null;
          try {
            const kp = this._kp;
            const cur = String(kp?._currentKeyboardLayoutId || kp?._settings?.currentKeyboardLayoutId || "");
            if (cur === `user:${missingId}`) {
              await setSettings({ currentKeyboardLayoutId: "builtin" });
              if (kp) {
                kp._currentKeyboardLayoutId = "builtin";
                kp._currentUserLayout = null;
                kp._currentKeySlotMap = null;
                if (kp._settings) kp._settings.currentKeyboardLayoutId = "builtin";
              }
            }
          } catch {
          }
        }
      }
      this._renderLayoutSelect();
      this._renderMacroKeyKindButtons();
      this._renderRightList();
    }
    _isReadOnly() {
      return this._st.mode !== "user";
    }
    /** User Macros / Macro Builder chrome. Off in v1; runtime still executes bound macros. */
    _macroBuilderEnabled() {
      return BUILD_ENABLE_MACRO_BUILDER === true;
    }
    /**
     * Configured Macro Keys + "Create built-in Macro Key" library chrome.
     * Off while those Function ids are in BUILD_EXCLUDED_KEY_ACTIONS.
     */
    _macroKeysLibraryEnabled() {
      return listFunctionDefs().some((d) => d && d.legacyMacroKeyKind);
    }
    async _persistUserLayout() {
      if (this._st.mode !== "user" || !this._st.userLayout) return;
      try {
        this._st.userLayout = await upsertUserKeyboardLayout(this._st.userLayout);
      } catch {
      }
    }
    _renderMacroKeyKindButtons() {
      this._fillMacroKeyKindCreateGrid(this._macroKeysActionsRow);
    }
    /**
     * @param {HTMLElement|null} host
     */
    _fillMacroKeyKindCreateGrid(host) {
      if (!host) return;
      if (!this._macroKeysLibraryEnabled()) {
        host.replaceChildren();
        return;
      }
      host.replaceChildren();
      const title = document.createElement("div");
      title.className = "kp-cfg-category-title";
      title.textContent = "Create built-in Macro Key";
      host.appendChild(title);
      const grid = document.createElement("div");
      grid.className = "kp-mk-kind-grid";
      for (const def of MACRO_KEY_KIND_DEFS) {
        const btn = document.createElement("button");
        btn.type = "button";
        btn.className = "kp-mk-kind-btn";
        btn.innerHTML = `<strong>${def.label}</strong><span>${def.description}</span>`;
        btn.addEventListener("click", () => {
          void this._createAndEditMacroKey(def.id);
        }, true);
        grid.appendChild(btn);
      }
      host.appendChild(grid);
    }
    async _createAndEditMacroKey(kind) {
      try {
        const functionId = FUNCTION_ID_BY_MACRO_KEY_KIND[kind];
        const createdAction = functionId ? await createUserAction({ functionId }) : null;
        const created = createdAction ? macroKeyLikeFromUserAction(createdAction) : null;
        if (!created) {
          this._notify("Failed to create macro key.", "error");
          return;
        }
        await this._reloadActions();
        this._openMacroKeyEditor(created);
        this._renderRightList();
      } catch {
        this._notify("Failed to create macro key.", "error");
      }
    }
    /**
     * Clear the Inspector dock — the single Config-side editor surface (Macro Key config,
     * generic Action Instance parameters, Macro summary). Only one can be shown at a time.
     */
    _closeMacroKeyEditor() {
      this._macroKeyDraft = null;
      this._inspectorSelection = null;
      this._renderInspector();
      try {
        this._renderRightList();
      } catch {
      }
    }
    /** Small helpers for the Inspector dock's shared chrome. */
    _dockTitle(text, subtitle, opts = {}) {
      const frag = document.createDocumentFragment();
      const docsUrl2 = String(opts.docsUrl || "").trim();
      const title = document.createElement("div");
      title.className = "kp-cfg-dock-title";
      title.textContent = String(text || "");
      if (docsUrl2) {
        const row = document.createElement("div");
        row.className = "kp-cfg-dock-title-row";
        row.appendChild(title);
        const docsBtn = document.createElement("button");
        docsBtn.type = "button";
        docsBtn.className = "kp-cfg-dock-docs";
        docsBtn.title = "Open KeyPilot documentation for this action";
        docsBtn.setAttribute("aria-label", "Open documentation");
        try {
          docsBtn.appendChild(mkCfgIcon(document, "kp-cfg-i-docs"));
        } catch {
        }
        docsBtn.addEventListener("click", (e) => {
          e.preventDefault();
          e.stopPropagation();
          this._openFunctionDocs(docsUrl2);
        }, true);
        row.appendChild(docsBtn);
        frag.appendChild(row);
      } else {
        frag.appendChild(title);
      }
      if (subtitle) {
        const sub = document.createElement("div");
        sub.className = "kp-cfg-dock-subtitle";
        sub.textContent = String(subtitle);
        frag.appendChild(sub);
      }
      return frag;
    }
    /**
     * Open Docs at a Function's kp:// docsUrl (closes Config so the docs popover can show).
     * @param {string} docsUrl
     */
    _openFunctionDocs(docsUrl2) {
      const href = String(docsUrl2 || "").trim();
      if (!href) return;
      const kp = this._kp || typeof window !== "undefined" && window.__KeyPilotInstance || null;
      try {
        if (kp && typeof kp.navigateKpDeepLink === "function") {
          kp.navigateKpDeepLink(href);
          return;
        }
      } catch {
      }
      try {
        if (kp && typeof kp.handleOpenDocsPopover === "function") {
          kp.handleOpenDocsPopover({ topicId: "functions" });
        }
      } catch {
      }
    }
    /** @param {Array<[string, string]>} pairs */
    _dockRows(pairs) {
      const dl = document.createElement("dl");
      dl.className = "kp-cfg-dock-rows";
      for (const [key, value] of pairs) {
        if (value == null || value === "") continue;
        const dt = document.createElement("dt");
        dt.textContent = key;
        const dd = document.createElement("dd");
        dd.textContent = String(value);
        dl.appendChild(dt);
        dl.appendChild(dd);
      }
      return dl;
    }
    /** @param {Array<{ label: string, onClick: () => void, title?: string, icon?: string }>} buttons */
    _dockActions(buttons) {
      const row = document.createElement("div");
      row.className = "kp-cfg-dock-actions";
      for (const spec of buttons) {
        if (!spec) continue;
        const btn = document.createElement("button");
        btn.type = "button";
        btn.className = "kp-cfg-btn";
        setCfgBtnLabel(btn, spec.label, spec.icon);
        if (spec.title) btn.title = spec.title;
        btn.addEventListener("click", spec.onClick, true);
        row.appendChild(btn);
      }
      return row;
    }
    /**
     * Inspector table of keyboard slots this library item is assigned to, with per-row delete.
     * @param {{ type: string, id: string }} item
     * @returns {HTMLElement}
     */
    _renderAssignmentTable(item) {
      const wrap = document.createElement("div");
      wrap.className = "kp-cfg-assign";
      const sub = document.createElement("div");
      sub.className = "kp-cfg-dock-subtitle";
      sub.textContent = "Assigned keys";
      wrap.appendChild(sub);
      const keys = this._findSlotKeysForItem(item).slice().sort((a, b) => formatChordSlotKeyLabel(a).localeCompare(formatChordSlotKeyLabel(b)));
      if (!keys.length) {
        const empty = document.createElement("p");
        empty.className = "kp-cfg-dock-empty";
        empty.textContent = "Not assigned to any key.";
        wrap.appendChild(empty);
        return wrap;
      }
      const table = document.createElement("table");
      table.className = "kp-cfg-assign-table";
      const thead = document.createElement("thead");
      const hr = document.createElement("tr");
      const thKey = document.createElement("th");
      thKey.textContent = "Key";
      const thAct = document.createElement("th");
      thAct.textContent = "";
      hr.appendChild(thKey);
      hr.appendChild(thAct);
      thead.appendChild(hr);
      table.appendChild(thead);
      const tbody = document.createElement("tbody");
      for (const slot of keys) {
        const tr = document.createElement("tr");
        const tdKey = document.createElement("td");
        const kbd = document.createElement("kbd");
        kbd.className = "kp-cfg-assign-kbd";
        kbd.textContent = formatChordSlotKeyLabel(slot);
        tdKey.appendChild(kbd);
        const tdAct = document.createElement("td");
        const del = document.createElement("button");
        del.type = "button";
        del.className = "kp-cfg-btn";
        setCfgBtnLabel(del, "Delete", "kp-cfg-i-trash");
        del.title = `Remove from ${formatChordSlotKeyLabel(slot)}`;
        del.setAttribute("aria-label", `Remove assignment from ${formatChordSlotKeyLabel(slot)}`);
        del.addEventListener("click", (e) => {
          e.preventDefault();
          e.stopPropagation();
          void this._clearSlotKey(slot);
        }, true);
        tdAct.appendChild(del);
        tr.appendChild(tdKey);
        tr.appendChild(tdAct);
        tbody.appendChild(tr);
      }
      table.appendChild(tbody);
      wrap.appendChild(table);
      return wrap;
    }
    /**
     * Render the Inspector dock for the current selection. Every Config-side editor lands here
     * instead of the old inline host + key popover pair, so only one editing surface is ever
     * open. (Keyboard Reference keycaps keep their own popovers.)
     */
    _renderInspector() {
      const host = this._inspectorBody;
      if (!host) return;
      host.replaceChildren();
      if (this._createOpen && this._createMode === "script" && this._macroDraft) {
        this._renderMacroDraftInspector(host);
        return;
      }
      const sel = this._inspectorSelection;
      if (!sel || !sel.id) {
        const draft = this._macroDraft;
        if (draft && (draft.id || (draft.steps || []).length)) {
          this._renderMacroDraftInspector(host);
          return;
        }
        const empty = document.createElement("p");
        empty.className = "kp-cfg-dock-empty";
        empty.textContent = "Select a card in the Actions Library to inspect it.";
        host.appendChild(empty);
        return;
      }
      if (sel.type === "macro") {
        this._renderMacroInspector(host, sel.id);
        return;
      }
      const mk = (this._st.macroKeys || []).find((m) => m && m.id === sel.id) || null;
      if (mk) {
        this._renderMacroKeyEditorInto(host, mk);
        return;
      }
      const inst = (this._st.actions || []).find((a) => a && a.id === sel.id) || null;
      const instDef = inst ? getFunctionDef(inst.functionId) : null;
      if (inst && instDef) {
        this._renderActionParamsEditorInto(host, instDef, inst);
        return;
      }
      this._renderFunctionInspector(host, sel.id);
    }
    /**
     * Stock (non-instantiable) Function: identity + Place, plus the two fixed-physical-key
     * Functions' existing "Config" popover and the chord binder for `worksWhileTyping`.
     * @param {HTMLElement} host
     * @param {string} functionId
     */
    _renderFunctionInspector(host, functionId) {
      const def = getFunctionDef(functionId);
      const kb = this._kp?.keybindings || buildKeybindingsForLayout(this._st.builtinLayoutId);
      const actionDef = KEYBINDING_ACTION_DEFS?.[functionId] || null;
      const binding = kb && kb[functionId] || null;
      const label = String(def?.label || actionDef?.label || binding?.label || functionId);
      if (!def && !actionDef && !binding) {
        const empty = document.createElement("p");
        empty.className = "kp-cfg-dock-empty";
        empty.textContent = "No inspector details for this item.";
        host.appendChild(empty);
        return;
      }
      host.appendChild(this._dockTitle(label, "Stock function", {
        docsUrl: def?.docsUrl || getFunctionDocsUrl(functionId)
      }));
      const about = String(def?.description || actionDef?.description || binding?.description || "");
      const details = String(
        def?.details || actionDef?.details || binding?.details || ""
      );
      host.appendChild(this._dockRows([
        ["Category", def ? getFunctionCategory(def.id) || "Other" : ""],
        ["About", about],
        ["Description", details],
        [def?.worksWhileTyping ? "Note" : "", def?.worksWhileTyping ? "Requires a modifier combo" : ""]
      ]));
      host.appendChild(this._renderAssignmentTable({ type: "function", id: functionId }));
      const actions = [];
      if (functionAssignableToKey(functionId)) {
        actions.push({
          label: "Place on keyboard",
          icon: "kp-cfg-i-place",
          title: "Then click a Keyboard Reference key",
          onClick: () => this._beginPlaceModeFromLibrary({ type: "function", id: functionId })
        });
      }
      if (def && FIXED_KEY_FUNCTION_IDS.includes(def.id) && (actionHasParameters(def.id) || actionHasDestination(def.id))) {
        actions.push({
          label: "Config\u2026",
          onClick: async () => {
            await this._openFixedKeySettings(def, host);
          }
        });
      }
      if (actions.length) host.appendChild(this._dockActions(actions));
      if (def?.worksWhileTyping) {
        host.appendChild(this._renderModifierToggles({ type: "function", id: functionId }, def));
        host.appendChild(this._renderBindChordButton({ type: "function", id: functionId }, def));
      }
    }
    /**
     * @param {HTMLElement} host
     * @param {string} macroId
     */
    _renderMacroInspector(host, macroId) {
      const macro = this._findMacroById(macroId);
      if (!macro) {
        const empty = document.createElement("p");
        empty.className = "kp-cfg-dock-empty";
        empty.textContent = "Macro not found.";
        host.appendChild(empty);
        return;
      }
      const stock = !!macro.stock;
      const steps = Array.isArray(macro.steps) ? macro.steps : [];
      host.appendChild(this._dockTitle(String(macro.label || "Macro"), stock ? "Stock macro" : "User macro"));
      host.appendChild(this._dockRows([
        ["Based on", macro.baseStockMacroId || ""],
        ["Steps", String(steps.length)]
      ]));
      host.appendChild(this._renderAssignmentTable({ type: "macro", id: macro.id }));
      host.appendChild(this._renderStepSummaryList(steps));
      const inspectorActions = [
        {
          label: "Place on keyboard",
          icon: "kp-cfg-i-place",
          onClick: () => this._beginPlaceModeFromLibrary({ type: "macro", id: macro.id })
        },
        {
          label: "Run",
          onClick: () => {
            void this._kp?._runMacroById?.(macro.id);
          }
        }
      ];
      if (this._macroBuilderEnabled()) {
        inspectorActions.push({
          label: stock ? "Customize" : "Edit steps",
          title: stock ? "Open an editable copy in the User Macros builder" : "Open in the User Macros builder",
          onClick: () => this._openMacroStepsEditor(macro)
        }, {
          label: "Duplicate",
          onClick: () => {
            void this._duplicateMacro(macro.id);
          }
        });
      }
      if (!stock) {
        inspectorActions.push({
          label: "Delete",
          onClick: () => {
            void this._deleteMacro(macro.id);
          }
        });
      }
      host.appendChild(this._dockActions(inspectorActions));
    }
    /**
     * Summary card for an unsaved User Macros draft (nothing to select in the library yet).
     * @param {HTMLElement} host
     */
    _renderMacroDraftInspector(host) {
      const draft = this._macroDraft;
      if (!draft) return;
      host.appendChild(this._dockTitle(
        String(draft.label || "Untitled Macro"),
        draft.stock ? "Stock macro (unsaved copy)" : "User macro (unsaved)"
      ));
      host.appendChild(this._dockRows([
        ["Based on", draft.baseStockMacroId || ""],
        ["Steps", String((draft.steps || []).length)]
      ]));
      if (draft.id) {
        host.appendChild(this._renderAssignmentTable({ type: "macro", id: draft.id }));
      }
      const warn = this._cycleWarningFor(draft.id, draft.steps || []);
      if (warn) {
        const p = document.createElement("p");
        p.className = "kp-cfg-cycle-warn";
        p.textContent = warn;
        host.appendChild(p);
      }
      host.appendChild(this._renderStepSummaryList(draft.steps || [], { selectable: true }));
      if (this._selectedStepIndex >= 0 && draft.steps?.[this._selectedStepIndex]) {
        host.appendChild(this._renderDraftStepEditor(this._selectedStepIndex));
      }
      host.appendChild(this._dockActions([
        { label: "Save", onClick: () => {
          void this._saveMacroDraft();
        } },
        { label: "Place", icon: "kp-cfg-i-place", onClick: () => {
          void this._placeMacroDraft();
        } },
        { label: "Run", onClick: () => {
          void this._runMacroDraft();
        } }
      ]));
    }
    /**
     * @param {any[]} steps
     * @param {{ selectable?: boolean }} [opts]
     * @returns {HTMLElement}
     */
    _renderStepSummaryList(steps, { selectable = false } = {}) {
      if (!Array.isArray(steps) || !steps.length) {
        const empty = document.createElement("p");
        empty.className = "kp-cfg-dock-empty";
        empty.textContent = "No steps yet.";
        return empty;
      }
      const ol = document.createElement("ol");
      ol.className = "kp-cfg-dock-steps";
      steps.forEach((step, index) => {
        const li = document.createElement("li");
        if (selectable) {
          li.tabIndex = 0;
          li.title = "Edit this step";
          if (this._selectedStepIndex === index) li.classList.add("kp-cfg-step-selected");
          const select = () => {
            this._selectedStepIndex = index;
            this._renderMacroBuilderSteps();
            this._renderInspector();
          };
          li.addEventListener("click", select, true);
          li.addEventListener("keydown", (e) => {
            if (e.key === "Enter" || e.key === " ") {
              e.preventDefault();
              select();
            }
          }, true);
        }
        const idx = document.createElement("span");
        idx.className = "kp-cfg-dock-step-idx";
        idx.textContent = String(index + 1);
        const body = document.createElement("div");
        const label = document.createElement("div");
        label.textContent = this._stepLabel(step);
        body.appendChild(label);
        const summary = this._stepSummary(step);
        if (summary) {
          const sub = document.createElement("div");
          sub.className = "kp-cfg-step-sub";
          sub.textContent = summary;
          body.appendChild(sub);
        }
        li.appendChild(idx);
        li.appendChild(body);
        ol.appendChild(li);
      });
      return ol;
    }
    /**
     * Edit the selected draft step in the Inspector. The canvas keeps its compact, ordered view;
     * detailed Function parameters and Logic controls belong in this dock.
     * @param {number} index
     * @returns {HTMLElement}
     */
    _renderDraftStepEditor(index) {
      const step = this._macroDraft?.steps?.[index];
      const wrap = document.createElement("section");
      wrap.className = "kp-mk-editor";
      if (!step) return wrap;
      const title = document.createElement("div");
      title.className = "kp-mk-editor-title";
      title.textContent = `Edit step ${index + 1}: ${this._stepLabel(step)}`;
      wrap.appendChild(title);
      const patch = (changes) => {
        if (!this._macroDraft?.steps?.[index]) return;
        Object.assign(this._macroDraft.steps[index], changes);
        this._macroDraft.dirty = true;
        this._renderMacroBuilder();
        this._renderInspector();
      };
      const field = (labelText, control) => {
        const row = document.createElement("label");
        row.style.cssText = "display:flex;flex-direction:column;gap:4px;";
        const label = document.createElement("span");
        label.className = "kp-mk-field-label";
        label.textContent = labelText;
        row.appendChild(label);
        row.appendChild(control);
        wrap.appendChild(row);
      };
      const numberInput = (label, value, onChange, { step: increment = 10 } = {}) => {
        const input = document.createElement("input");
        input.type = "number";
        input.min = "0";
        input.step = String(increment);
        input.className = "kp-cfg-field";
        input.value = String(Math.max(0, Math.floor(Number(value) || 0)));
        input.addEventListener("change", () => onChange(Math.max(0, Math.floor(Number(input.value) || 0))), true);
        field(label, input);
      };
      const textInput = (label, value, onChange, placeholder = "") => {
        const input = document.createElement("input");
        input.type = "text";
        input.className = "kp-cfg-field";
        input.value = value == null ? "" : String(value);
        input.placeholder = placeholder;
        input.addEventListener("change", () => onChange(input.value), true);
        field(label, input);
      };
      const kind = String(step.kind || (step.functionId ? "function" : ""));
      if (kind === "wait") {
        numberInput("Wait (ms)", step.ms, (ms) => patch({ ms }));
      } else if (kind === "gate") {
        const gateHint = document.createElement("p");
        gateHint.className = "kp-cfg-hint";
        gateHint.textContent = "Uses the previous Function step\u2019s return value.";
        wrap.appendChild(gateHint);
        const op = document.createElement("select");
        op.className = "kp-cfg-field";
        const currentOp = String(step.op || "truthy");
        for (const def of MACRO_GATE_OPS) {
          const option = document.createElement("option");
          option.value = def.op;
          option.textContent = def.label;
          option.title = def.hint;
          option.selected = currentOp === def.op;
          op.appendChild(option);
        }
        const opDef = getMacroGateOpDef(currentOp);
        op.title = opDef.hint;
        op.addEventListener("change", () => patch({ op: op.value }), true);
        field("Condition", op);
        enhanceNativeSelect(op);
        if (opDef.needsRight) {
          textInput("Compare to", step.right, (right) => patch({ right }), "Value to compare against");
        }
        numberInput(
          "If this fails, skip the next N steps",
          step.thenSkip,
          (thenSkip) => patch({ thenSkip }),
          { step: 1 }
        );
        const skipNote = document.createElement("p");
        skipNote.className = "kp-cfg-hint";
        skipNote.textContent = "N is following steps (not including this Gate). 0 continues at the next step.";
        wrap.appendChild(skipNote);
        const advanced = document.createElement("details");
        advanced.className = "kp-cfg-gate-advanced";
        if (step.leftKey) advanced.open = true;
        const summary = document.createElement("summary");
        summary.textContent = "Advanced";
        advanced.appendChild(summary);
        const keyRow = document.createElement("label");
        keyRow.style.cssText = "display:flex;flex-direction:column;gap:4px;margin-top:6px;";
        const keyLabel = document.createElement("span");
        keyLabel.className = "kp-mk-field-label";
        keyLabel.textContent = "Read a field from the previous result";
        const keyInput = document.createElement("input");
        keyInput.type = "text";
        keyInput.className = "kp-cfg-field";
        keyInput.value = step.leftKey == null ? "" : String(step.leftKey);
        keyInput.placeholder = "Optional object property (leftKey)";
        keyInput.title = "leftKey \u2014 when the previous result is an object, read this property";
        keyInput.addEventListener("change", () => patch({ leftKey: keyInput.value }), true);
        keyRow.appendChild(keyLabel);
        keyRow.appendChild(keyInput);
        advanced.appendChild(keyRow);
        wrap.appendChild(advanced);
      } else if (kind === "runMacro") {
        const select = document.createElement("select");
        select.className = "kp-cfg-field";
        const none = document.createElement("option");
        none.value = "";
        none.textContent = "\u2014 pick macro \u2014";
        select.appendChild(none);
        for (const macro of this._listSelectableMacros(this._macroDraft?.id || "")) {
          const option = document.createElement("option");
          option.value = macro.id;
          option.textContent = macro.stock ? `${macro.label} (stock)` : macro.label;
          option.selected = String(step.macroId || "") === macro.id;
          select.appendChild(option);
        }
        select.addEventListener("change", () => patch({ macroId: select.value }), true);
        field("Macro", select);
        enhanceNativeSelect(select);
        if (!String(step.macroId || "").trim()) {
          const missing = document.createElement("p");
          missing.className = "kp-cfg-cycle-warn";
          missing.textContent = "Pick a macro. Saving without one removes this step.";
          wrap.appendChild(missing);
        }
      } else if (kind === "stop") {
        const hint = document.createElement("p");
        hint.className = "kp-cfg-hint";
        hint.textContent = "Stop ends the macro immediately when reached.";
        wrap.appendChild(hint);
      } else {
        numberInput("Delay before (ms)", step.delayMsBefore, (delayMsBefore) => patch({ delayMsBefore }));
        const def = getFunctionDef(step.functionId);
        this._appendFunctionParameterFields(wrap, def, step.parameters || {}, (paramId, value) => {
          patch({ parameters: { ...step.parameters || {}, [paramId]: value } });
        }, { live: false });
      }
      return wrap;
    }
    /**
     * Shared Function parameter controls (instance editor + macro step inspector).
     * Renders optional `group` headings (e.g. Callbacks) and taller textareas via `rows`.
     * @param {HTMLElement} host
     * @param {{ id?: string, parameters?: any[] }|null|undefined} def
     * @param {Record<string, any>} values
     * @param {(paramId: string, value: any) => void} onChange
     * @param {{ live?: boolean }} [opts] `live: false` uses `change` only (avoids re-render focus loss).
     */
    _appendFunctionParameterFields(host, def, values, onChange, { live = true } = {}) {
      if (!host) return;
      if (def?.id === "EXECUTE_JS") {
        const hint = document.createElement("p");
        hint.className = "kp-cfg-hint";
        hint.appendChild(document.createTextNode(
          "Bindings: kpHoveredClickable, kpHoverLeaf, kpFocusedTextField, kpMode, kpPageUrl, kpSelection, kpPriorResult. Callbacks are functions only when checked. "
        ));
        const docsLink = document.createElement("button");
        docsLink.type = "button";
        docsLink.className = "kp-cfg-hint-link";
        docsLink.textContent = "Open Execute JS help";
        docsLink.addEventListener("click", (e) => {
          e.preventDefault();
          this._openFunctionDocs(def.docsUrl || getFunctionDocsUrl("EXECUTE_JS"));
        }, true);
        hint.appendChild(docsLink);
        host.appendChild(hint);
      }
      const controller = createActionConfigController();
      controller.load({
        functionId: def?.id || "",
        snapshot: values,
        parameters: def?.parameters,
        persist: (_functionId, paramId, value) => {
          onChange(paramId, value);
        }
      });
      appendActionConfigFields(host, {
        controller,
        live,
        classes: {
          label: "kp-mk-field-label",
          control: "kp-cfg-field",
          group: "kp-mk-field-label"
        }
      });
    }
    /**
     * @param {{ id: string, kind: string, label: string, config: Record<string, any> }} macroKey
     */
    _openMacroKeyEditor(macroKey) {
      if (!macroKey || !macroKey.id) return;
      this._inspectorSelection = { type: "function", id: String(macroKey.id) };
      this._setInspectorOpen(true);
      this._renderInspector();
      try {
        this._renderRightList();
      } catch {
      }
    }
    /**
     * @param {HTMLElement} host
     * @param {{ id: string, kind: string, label: string, config: Record<string, any> }} macroKey
     */
    _renderMacroKeyEditorInto(host, macroKey) {
      this._macroKeyDraft = { ...macroKey, config: { ...macroKey.config || {} } };
      const mkFunctionId = FUNCTION_ID_BY_MACRO_KEY_KIND[macroKey.kind] || "";
      host.appendChild(this._dockTitle(String(macroKey.label || macroKey.kind || "Macro Key"), "Macro Key", {
        docsUrl: getFunctionDef(mkFunctionId)?.docsUrl || getFunctionDocsUrl(mkFunctionId)
      }));
      host.appendChild(this._renderAssignmentTable({ type: "function", id: macroKey.id }));
      host.appendChild(this._dockActions([{
        label: "Place on keyboard",
        icon: "kp-cfg-i-place",
        onClick: () => this._beginPlaceModeFromLibrary({ type: "function", id: macroKey.id })
      }]));
      const editor = createMacroKeyEditor({
        macroKey: this._macroKeyDraft,
        onChange: (draft) => {
          this._macroKeyDraft = draft;
        },
        onSave: async () => {
          try {
            const draft = this._macroKeyDraft;
            const functionId = FUNCTION_ID_BY_MACRO_KEY_KIND[draft.kind];
            const saved = functionId ? await upsertUserAction({
              id: draft.id,
              functionId,
              label: draft.label,
              parameters: { config: draft.config }
            }) : null;
            await this._reloadActions();
            this._closeMacroKeyEditor();
            this._renderRightList();
            this._notify(saved ? "Macro key saved." : "Failed to save macro key.", saved ? "success" : "error");
          } catch {
            this._notify("Failed to save macro key.", "error");
          }
        },
        onCancel: () => this._closeMacroKeyEditor(),
        onDelete: async () => {
          try {
            await deleteUserAction(macroKey.id);
            await this._reloadActions();
            this._closeMacroKeyEditor();
            this._renderRightList();
            this._notify("Macro key deleted.", "success");
          } catch {
            this._notify("Failed to delete macro key.", "error");
          }
        }
      });
      host.appendChild(editor);
    }
    /**
     * Destination / extra parameters for Functions still bound via a canonical Action Instance
     * (`FIXED_KEY_FUNCTION_IDS`). Destination-only Functions (Copy Image / URL / Video) use the
     * same key-info popover switch as Keyboard Reference; Functions with other fields open Config.
     * @param {import('../config/function-library.js').FunctionDef} def
     * @param {HTMLElement|null} [anchorEl]
     */
    async _openFixedKeySettings(def, anchorEl) {
      if (!def?.id) return;
      try {
        this._cancelPlaceMode();
      } catch {
      }
      if (actionHasParameters(def.id)) {
        try {
          const panel = getSharedKeyActionConfigPanel();
          await panel.open(def.id, {
            title: def.label,
            anchorRect: anchorEl?.getBoundingClientRect?.() || null
          });
        } catch {
        }
        return;
      }
      if (actionHasDestination(def.id) && anchorEl) {
        try {
          inspectKeyActionFromAnchor(def.id, {
            anchorEl,
            keybindings: this._kp?.keybindings || buildKeybindingsForLayout(this._st.builtinLayoutId)
          });
        } catch {
        }
      }
    }
    /**
     * Generic parameter editor for any instantiable Function's Action Instance (e.g. Type
     * Characters) that is *not* a `legacyMacroKeyKind` (those use {@link _openMacroKeyEditor})
     * and not one of the two fixed-physical-key Functions (those keep the `key-action-settings.js`
     * "Config" popover — see `FIXED_KEY_FUNCTION_IDS`). Values apply live per-field, matching
     * `key-action-settings.js`'s `KeyActionConfigPanel`.
     * @param {import('../config/function-library.js').FunctionDef} def
     * @param {import('../modules/keyboard-layout-store.js').UserAction} instance
     */
    _openActionParamsEditor(def, instance) {
      if (!def || !instance) return;
      this._inspectorSelection = { type: "function", id: String(instance.id) };
      this._setInspectorOpen(true);
      this._renderInspector();
      try {
        this._renderRightList();
      } catch {
      }
    }
    /**
     * @param {HTMLElement} host
     * @param {import('../config/function-library.js').FunctionDef} def
     * @param {import('../modules/keyboard-layout-store.js').UserAction} instance
     */
    _renderActionParamsEditorInto(host, def, instance) {
      const draft = { ...instance, parameters: { ...instance.parameters || {} } };
      host.appendChild(this._dockTitle(
        String(instance.label || def.label),
        summarizeFunctionParameters(instance.functionId, instance.parameters) || "Configured instance",
        { docsUrl: def.docsUrl || getFunctionDocsUrl(def.id) }
      ));
      host.appendChild(this._renderAssignmentTable({ type: "function", id: instance.id }));
      if (functionAssignableToKey(def.id)) {
        host.appendChild(this._dockActions([{
          label: "Place on keyboard",
          icon: "kp-cfg-i-place",
          onClick: () => this._beginPlaceModeFromLibrary({ type: "function", id: instance.id })
        }]));
      }
      const wrap = document.createElement("div");
      wrap.className = "kp-mk-editor";
      const title = document.createElement("div");
      title.className = "kp-mk-editor-title";
      title.textContent = `${def.label} settings`;
      wrap.appendChild(title);
      if (def.worksWhileTyping) {
        wrap.appendChild(this._renderModifierToggles({ type: "function", id: instance.id }, def));
      }
      this._appendFunctionParameterFields(wrap, def, draft.parameters || {}, (paramId, value) => {
        draft.parameters[paramId] = value;
      });
      const actions = document.createElement("div");
      actions.className = "kp-mk-editor-actions";
      const saveBtn = document.createElement("button");
      saveBtn.type = "button";
      saveBtn.className = "kp-cfg-btn";
      saveBtn.textContent = "Save";
      saveBtn.addEventListener("click", async () => {
        try {
          const saved = await upsertUserAction(draft);
          await this._reloadActions();
          try {
            if (this._st.userLayout) {
              await this._kp?.applyLiveUserLayout?.(this._st.userLayout, { actions: this._st.actions });
            }
          } catch {
          }
          if (saved?.id) {
            this._inspectorSelection = { type: "function", id: String(saved.id) };
          }
          this._renderInspector();
          try {
            this._renderRightList();
          } catch {
          }
          this._notify(saved ? "Saved." : "Failed to save.", saved ? "success" : "error");
        } catch {
          this._notify("Failed to save.", "error");
        }
      }, true);
      actions.appendChild(saveBtn);
      const cancelBtn = document.createElement("button");
      cancelBtn.type = "button";
      cancelBtn.className = "kp-cfg-btn";
      cancelBtn.textContent = "Cancel";
      cancelBtn.addEventListener("click", () => this._closeMacroKeyEditor(), true);
      actions.appendChild(cancelBtn);
      const deleteBtn = document.createElement("button");
      deleteBtn.type = "button";
      deleteBtn.className = "kp-cfg-btn";
      deleteBtn.textContent = "Delete instance";
      deleteBtn.addEventListener("click", async () => {
        try {
          await deleteUserAction(instance.id);
          await this._reloadActions();
          this._closeMacroKeyEditor();
          this._renderRightList();
          this._notify("Deleted.", "success");
        } catch {
          this._notify("Failed to delete.", "error");
        }
      }, true);
      actions.appendChild(deleteBtn);
      wrap.appendChild(actions);
      host.appendChild(wrap);
    }
    /**
     * Load a Macro into the User Macros builder and show its summary in the Inspector dock.
     * Stock macros open read-only-by-name with a fork banner — Save writes a user copy carrying
     * `baseStockMacroId`, exactly like duplicating a built-in keyboard layout.
     * @param {{ id: string, label?: string, steps?: any[], baseStockMacroId?: string }} macro
     */
    _openMacroStepsEditor(macro) {
      if (!this._macroBuilderEnabled()) {
        this._notify("Macro Builder is not in this build. Use Execute JS for custom sequences.", "info");
        return;
      }
      if (!macro || !macro.id) return;
      const id = String(macro.id);
      const current = this._macroDraft;
      if (current?.dirty && String(current.id || "") === id) {
        this._setCreateMode("script", { open: true });
        this._inspectorSelection = null;
        this._setInspectorOpen(true);
        this._renderMacroBuilder();
        this._renderInspector();
        return;
      }
      if (current?.dirty && !this._confirmDiscardDirtyMacroDraft()) return;
      const stock = isStockMacroId(id);
      const source = stock ? getStockMacroById(id) || macro : macro;
      this._macroDraft = {
        id,
        label: String(source.label || "Macro"),
        steps: (Array.isArray(source.steps) ? source.steps : []).map((s) => ({ ...s })),
        stock,
        baseStockMacroId: stock ? id : source.baseStockMacroId || null,
        dirty: false
      };
      this._selectedStepIndex = -1;
      this._setCreateMode("script", { open: true });
      this._renderMacroBuilder();
      this._inspectorSelection = null;
      this._setInspectorOpen(true);
      this._renderInspector();
      try {
        this._renderRightList();
      } catch {
      }
    }
    /**
     * @returns {boolean} false when the user cancelled discarding unsaved work.
     */
    _confirmDiscardDirtyMacroDraft() {
      if (!this._macroDraft?.dirty) return true;
      if (typeof window.confirm !== "function") return true;
      return window.confirm("This macro has unsaved changes. Discard them?");
    }
    _startNewMacroDraft() {
      if (!this._macroBuilderEnabled()) {
        this._notify("Macro Builder is not in this build. Use Execute JS for custom sequences.", "info");
        return;
      }
      if (!this._confirmDiscardDirtyMacroDraft()) return;
      this._resetMacroDraft(`Macro ${(this._st.macros || []).length + 1}`);
      this._setCreateMode("script", { open: true });
      this._inspectorSelection = null;
      this._setInspectorOpen(true);
      this._renderInspector();
    }
    /** @param {string} [label] */
    _resetMacroDraft(label) {
      this._macroDraft = {
        id: null,
        label: String(label || "Untitled Macro"),
        steps: [],
        stock: false,
        baseStockMacroId: null,
        dirty: false
      };
      this._selectedStepIndex = -1;
      this._renderMacroBuilder();
    }
    /** Sync the builder's name field / banner / meta with the current draft, then redraw steps. */
    _renderMacroBuilder() {
      const draft = this._macroDraft;
      if (this._macroNameInput) {
        this._macroNameInput.value = String(draft?.label || "");
        this._macroNameInput.readOnly = !!draft?.stock;
        this._macroNameInput.title = draft?.stock ? "Stock macro name \u2014 Save creates an editable copy" : "Macro name";
      }
      if (this._scriptStockBanner) this._scriptStockBanner.hidden = !draft?.stock;
      const count = (draft?.steps || []).length;
      if (this._scriptMetaEl) this._scriptMetaEl.textContent = `${count} step${count === 1 ? "" : "s"}`;
      this._renderMacroBuilderSteps();
    }
    _renderMacroBuilderSteps() {
      const host = this._scriptStepsHost;
      if (!host) return;
      host.replaceChildren();
      const draft = this._macroDraft;
      const steps = Array.isArray(draft?.steps) ? draft.steps : [];
      if (!steps.length) {
        const empty = document.createElement("div");
        empty.className = "kp-cfg-script-empty";
        const strong = document.createElement("strong");
        strong.textContent = "Build a macro script";
        const span = document.createElement("span");
        span.textContent = "Click a Logic chip, Get Text Range, Show Popover, or Execute JS, or pick a Function below and Add step.";
        empty.appendChild(strong);
        empty.appendChild(span);
        host.appendChild(empty);
        return;
      }
      steps.forEach((step, index) => {
        host.appendChild(this._renderMacroStepRow(step, index, steps.length));
      });
    }
    /**
     * One editable row in the User Macros canvas: index, label, kind-specific fields
     * (delay before for Function steps; Logic fields for the rest) and reorder/remove ops.
     * @param {any} step
     * @param {number} index
     * @param {number} total
     * @returns {HTMLElement}
     */
    _renderMacroStepRow(step, index, total) {
      const row = document.createElement("div");
      row.className = "kp-cfg-step";
      if (this._selectedStepIndex === index) row.classList.add("kp-cfg-step-selected");
      row.dataset.kpStepIndex = String(index);
      row.addEventListener("click", () => {
        this._selectedStepIndex = index;
        this._renderMacroBuilderSteps();
        this._renderInspector();
      }, false);
      const grip = document.createElement("span");
      grip.className = "kp-cfg-step-grip";
      grip.textContent = "\u22EE\u22EE";
      grip.title = "Drag to reorder";
      grip.setAttribute("aria-hidden", "true");
      grip.draggable = true;
      row.appendChild(grip);
      const idx = document.createElement("span");
      idx.className = "kp-cfg-step-idx";
      idx.textContent = String(index + 1);
      row.appendChild(idx);
      const label = document.createElement("div");
      label.className = "kp-cfg-step-label";
      label.textContent = this._stepLabel(step);
      const kind = String(step?.kind || (step?.functionId ? "function" : ""));
      if (kind !== "function") {
        const sub = document.createElement("span");
        sub.className = "kp-cfg-step-sub";
        sub.textContent = kind;
        label.appendChild(sub);
      }
      row.appendChild(label);
      const fields = document.createElement("div");
      fields.className = "kp-cfg-step-fields";
      const patch = (changes) => {
        Object.assign(step, changes);
        if (this._macroDraft) this._macroDraft.dirty = true;
        if (this._scriptMetaEl) {
          const count = (this._macroDraft?.steps || []).length;
          this._scriptMetaEl.textContent = `${count} step${count === 1 ? "" : "s"}`;
        }
        this._renderInspector();
      };
      const mkNumber = (labelText, value, onChange, title) => {
        const wrap = document.createElement("label");
        if (title) wrap.title = title;
        const text = document.createElement("span");
        text.textContent = labelText;
        const input = document.createElement("input");
        input.type = "number";
        input.min = "0";
        input.step = "10";
        input.className = "kp-cfg-field";
        input.value = String(Math.max(0, Math.floor(Number(value) || 0)));
        input.addEventListener("change", () => {
          onChange(Math.max(0, Math.floor(Number(input.value) || 0)));
        }, true);
        wrap.appendChild(text);
        wrap.appendChild(input);
        return wrap;
      };
      if (kind === "wait") {
        fields.appendChild(mkNumber("ms", step.ms, (v) => patch({ ms: v }), "Pause duration"));
      } else if (kind === "gate") {
        const sum = document.createElement("span");
        sum.className = "kp-cfg-step-summary";
        sum.textContent = this._stepSummary(step);
        sum.title = "Select this step to edit the condition in the Inspector";
        fields.appendChild(sum);
      } else if (kind === "runMacro") {
        const wrap = document.createElement("label");
        const text = document.createElement("span");
        text.textContent = "macro";
        const select = document.createElement("select");
        select.className = "kp-cfg-field";
        const none = document.createElement("option");
        none.value = "";
        none.textContent = "(pick macro)";
        select.appendChild(none);
        for (const m of this._listSelectableMacros(this._macroDraft?.id || "")) {
          const o = document.createElement("option");
          o.value = m.id;
          o.textContent = m.stock ? `${m.label} (stock)` : m.label;
          if (String(step.macroId || "") === m.id) o.selected = true;
          select.appendChild(o);
        }
        select.addEventListener("change", () => patch({ macroId: select.value }), true);
        wrap.appendChild(text);
        wrap.appendChild(select);
        fields.appendChild(wrap);
        enhanceNativeSelect(select);
      } else if (kind === "function") {
        fields.appendChild(mkNumber(
          "delay ms",
          step.delayMsBefore,
          (v) => patch({ delayMsBefore: v }),
          "Wait this long before running the step"
        ));
      }
      row.appendChild(fields);
      const ops = document.createElement("div");
      ops.className = "kp-cfg-step-ops";
      const mkOp = (text, title, disabled, onClick) => {
        const b = document.createElement("button");
        b.type = "button";
        b.className = "kp-cfg-btn";
        b.textContent = text;
        b.title = title;
        b.disabled = !!disabled;
        b.addEventListener("click", (e) => {
          e.preventDefault();
          e.stopPropagation();
          onClick();
        }, true);
        return b;
      };
      ops.appendChild(mkOp("\u2191", "Move up", index === 0, () => this._moveDraftStep(index, index - 1)));
      ops.appendChild(mkOp("\u2193", "Move down", index === total - 1, () => this._moveDraftStep(index, index + 1)));
      ops.appendChild(mkOp("\xD7", "Remove step", false, () => this._removeDraftStep(index)));
      row.appendChild(ops);
      this._bindMacroStepDrag(row, grip, index);
      return row;
    }
    /**
     * HTML5 drag-reorder for a macro script step row (grip is the drag source so inputs stay usable).
     * @param {HTMLElement} row
     * @param {HTMLElement} grip
     * @param {number} index
     */
    _bindMacroStepDrag(row, grip, index) {
      grip.addEventListener("dragstart", (e) => {
        e.stopPropagation();
        this._macroStepDragFrom = index;
        row.classList.add("kp-cfg-step-dragging");
        try {
          e.dataTransfer.effectAllowed = "move";
          e.dataTransfer.setData(KP_MACRO_STEP_MIME, String(index));
          e.dataTransfer.setData("text/plain", String(index));
        } catch {
        }
      }, true);
      grip.addEventListener("dragend", () => {
        this._macroStepDragFrom = -1;
        this._clearMacroStepDragOver();
        row.classList.remove("kp-cfg-step-dragging");
      }, true);
      row.addEventListener("dragover", (e) => {
        if (!this._isMacroStepDrag(e)) return;
        e.preventDefault();
        e.stopPropagation();
        try {
          e.dataTransfer.dropEffect = "move";
        } catch {
        }
        const rect = row.getBoundingClientRect();
        const before = e.clientY < rect.top + rect.height / 2;
        this._clearMacroStepDragOver();
        row.classList.add(before ? "kp-cfg-step-drag-over-before" : "kp-cfg-step-drag-over-after");
      }, true);
      row.addEventListener("dragleave", (e) => {
        if (!row.contains(
          /** @type {Node} */
          e.relatedTarget
        )) {
          row.classList.remove("kp-cfg-step-drag-over-before", "kp-cfg-step-drag-over-after");
        }
      }, true);
      row.addEventListener("drop", (e) => {
        if (!this._isMacroStepDrag(e)) return;
        e.preventDefault();
        e.stopPropagation();
        const from = this._macroStepDragFrom >= 0 ? this._macroStepDragFrom : Number(e.dataTransfer?.getData(KP_MACRO_STEP_MIME) || e.dataTransfer?.getData("text/plain") || -1);
        this._clearMacroStepDragOver();
        row.classList.remove("kp-cfg-step-dragging");
        this._macroStepDragFrom = -1;
        if (!Number.isFinite(from) || from < 0) return;
        const rect = row.getBoundingClientRect();
        const before = e.clientY < rect.top + rect.height / 2;
        let to = before ? index : index + 1;
        if (from < to) to -= 1;
        if (to === from) return;
        this._moveDraftStep(from, to);
      }, true);
    }
    /** @param {DragEvent} e */
    _isMacroStepDrag(e) {
      if (this._macroStepDragFrom >= 0) return true;
      try {
        const types = e.dataTransfer?.types;
        if (!types) return false;
        return Array.from(types).includes(KP_MACRO_STEP_MIME);
      } catch {
        return false;
      }
    }
    _clearMacroStepDragOver() {
      const host = this._scriptStepsHost;
      if (!host) return;
      host.querySelectorAll(".kp-cfg-step-drag-over-before, .kp-cfg-step-drag-over-after").forEach((el2) => {
        el2.classList.remove("kp-cfg-step-drag-over-before", "kp-cfg-step-drag-over-after");
      });
    }
    /**
     * User macros plus the stock catalog, for the Run Macro step picker.
     * @param {string} excludeId
     * @returns {Array<{ id: string, label: string, stock: boolean }>}
     */
    _listSelectableMacros(excludeId) {
      const out = [];
      for (const m of this._st.macros || []) {
        if (!m || !m.id || m.id === excludeId) continue;
        out.push({ id: m.id, label: String(m.label || "Macro"), stock: false });
      }
      for (const m of this._st.stockMacros || []) {
        if (!m || !m.id || m.id === excludeId) continue;
        out.push({ id: m.id, label: String(m.label || "Macro"), stock: true });
      }
      return out;
    }
    /** @param {string} kind */
    _addDraftLogicStep(kind) {
      if (!this._macroDraft) this._resetMacroDraft();
      let step = null;
      if (kind === "wait") step = { kind: "wait", ms: 100 };
      else if (kind === "gate") step = { kind: "gate", op: "truthy", left: "prior", thenSkip: 1 };
      else if (kind === "stop") step = { kind: "stop" };
      else if (kind === "runMacro") step = { kind: "runMacro", macroId: "" };
      if (!step) return;
      this._macroDraft.steps.push(step);
      this._macroDraft.dirty = true;
      this._selectedStepIndex = this._macroDraft.steps.length - 1;
      this._setCreateMode("script", { open: true });
      this._renderMacroBuilder();
      this._renderInspector();
    }
    /**
     * @param {string} functionId
     * @param {{ delayMsBefore?: number, parameters?: Record<string, any> }} [opts]
     */
    _addDraftFunctionStep(functionId, { delayMsBefore = 0, parameters } = {}) {
      const def = getFunctionDef(functionId);
      if (!def) {
        this._notify("Unknown function.", "error");
        return;
      }
      if (!this._macroDraft) this._resetMacroDraft();
      const step = {
        kind: "function",
        functionId: def.id,
        parameters: parameters ? { ...parameters } : { ...defaultFunctionParameters(def.id) }
      };
      const delay = Math.max(0, Math.floor(Number(delayMsBefore) || 0));
      if (delay > 0) step.delayMsBefore = delay;
      this._macroDraft.steps.push(step);
      this._macroDraft.dirty = true;
      this._selectedStepIndex = this._macroDraft.steps.length - 1;
      this._setCreateMode("script", { open: true });
      this._renderMacroBuilder();
      this._renderInspector();
    }
    /**
     * @param {number} from
     * @param {number} to
     */
    _moveDraftStep(from, to) {
      const steps = this._macroDraft?.steps;
      if (!Array.isArray(steps) || !steps[from]) return;
      const next = Math.max(0, Math.min(to, steps.length - 1));
      if (next === from) return;
      const [moved] = steps.splice(from, 1);
      steps.splice(next, 0, moved);
      this._macroDraft.dirty = true;
      this._selectedStepIndex = next;
      this._renderMacroBuilder();
      this._renderInspector();
    }
    /** @param {number} index */
    _removeDraftStep(index) {
      const steps = this._macroDraft?.steps;
      if (!Array.isArray(steps) || !steps[index]) return;
      steps.splice(index, 1);
      this._macroDraft.dirty = true;
      this._selectedStepIndex = -1;
      this._renderMacroBuilder();
      this._renderInspector();
    }
    /**
     * Commit the draft. A stock draft forks into a new user macro (`baseStockMacroId` kept); a
     * draft with no id creates one; otherwise the existing user macro is updated in place.
     * @returns {Promise<any|null>}
     */
    async _saveMacroDraft() {
      const draft = this._macroDraft;
      if (!draft) return null;
      const typed = String(this._macroNameInput?.value || draft.label || "").trim();
      const steps = (draft.steps || []).map(normalizeMacroStep).filter(Boolean);
      if (!steps.length) {
        this._notify("Add at least one step before saving.", "error");
        return null;
      }
      try {
        let saved = null;
        if (draft.stock) {
          const stockId = String(draft.baseStockMacroId || draft.id || "");
          const label = /\(custom\)\s*$/.test(typed) ? typed : `${typed || "Macro"} (custom)`;
          const forked = await forkStockMacroToUser(stockId, { label });
          if (forked) saved = await upsertUserMacro({ ...forked, label, steps });
        } else if (!draft.id) {
          const created = await createUserMacro({ label: typed || "Untitled Macro" });
          if (created) saved = await upsertUserMacro({ ...created, label: typed || created.label, steps });
        } else {
          saved = await upsertUserMacro({
            id: draft.id,
            label: typed || draft.label || "Macro",
            steps,
            baseStockMacroId: draft.baseStockMacroId || void 0
          });
        }
        if (!saved) {
          this._notify("Failed to save macro.", "error");
          return null;
        }
        this._st.macros = await listUserMacros();
        this._macroDraft = {
          id: saved.id,
          label: String(saved.label || ""),
          steps: (saved.steps || []).map((s) => ({ ...s })),
          stock: false,
          baseStockMacroId: saved.baseStockMacroId || null,
          dirty: false
        };
        this._renderMacroBuilder();
        this._inspectorSelection = { type: "macro", id: saved.id };
        this._renderInspector();
        this._renderRightList();
        try {
          if (this._st.userLayout) {
            await this._kp?.applyLiveUserLayout?.(this._st.userLayout, {
              macros: this._st.macros,
              actions: this._st.actions
            });
          }
        } catch {
        }
        this._emitChange();
        this._notify(draft.stock ? `Forked stock macro \u2192 "${saved.label}".` : "Macro saved.", "success");
        return saved;
      } catch {
        this._notify("Failed to save macro.", "error");
        return null;
      }
    }
    /** Save first when needed, then enter place mode with the resulting user macro. */
    async _placeMacroDraft() {
      let id = this._macroDraft?.id || null;
      if (!id || this._macroDraft?.dirty || this._macroDraft?.stock) {
        const saved = await this._saveMacroDraft();
        id = saved?.id || null;
      }
      if (!id) return;
      this._beginPlaceModeFromLibrary({ type: "macro", id });
    }
    async _runMacroDraft() {
      const draft = this._macroDraft;
      if (!draft) return;
      let id = draft.id || null;
      if (!id || draft.dirty || draft.stock) {
        const saved = await this._saveMacroDraft();
        id = saved?.id || null;
      }
      if (!id) return;
      try {
        await this._kp?._runMacroById?.(id);
      } catch {
      }
    }
    /**
     * Copy any macro — stock or user — into a new *user* macro, then open it in the builder.
     * @param {string} macroId
     */
    async _duplicateMacro(macroId) {
      const source = this._findMacroById(macroId);
      if (!source) return;
      try {
        const baseLabel = String(source.label || "Macro").replace(/\s*\(custom\)\s*$/, "");
        const label = `${baseLabel} copy`;
        let created = null;
        if (source.stock) {
          created = await forkStockMacroToUser(source.id, { label });
        } else {
          const blank = await createUserMacro({ label });
          created = blank ? await upsertUserMacro({
            ...blank,
            label,
            steps: (source.steps || []).map((s) => ({ ...s })),
            baseStockMacroId: source.baseStockMacroId || void 0
          }) : null;
        }
        if (!created) {
          this._notify("Failed to duplicate macro.", "error");
          return;
        }
        this._st.macros = await listUserMacros();
        this._renderRightList();
        this._openMacroStepsEditor(created);
        this._emitChange();
        this._notify(`Duplicated \u2192 ${created.label}.`, "success");
      } catch {
        this._notify("Failed to duplicate macro.", "error");
      }
    }
    /** @param {string} macroId */
    async _deleteMacro(macroId) {
      if (isStockMacroId(macroId)) {
        this._notify("Stock macros cannot be deleted.", "error");
        return;
      }
      const found = this._findMacroById(macroId);
      const label = found?.label || "this macro";
      const ok = typeof window.confirm !== "function" ? true : window.confirm(`Delete "${label}"? This cannot be undone.`);
      if (!ok) return;
      try {
        await deleteUserMacro(macroId);
        this._st.macros = (this._st.macros || []).filter((m) => m && m.id !== macroId);
        if (this._macroDraft?.id === macroId) this._resetMacroDraft();
        this._closeMacroKeyEditor();
        this._renderRightList();
        this._emitChange();
        this._notify("Macro deleted.", "success");
      } catch {
        this._notify("Failed to delete macro.", "error");
      }
    }
    /**
     * @param {string} macroId
     * @returns {{ id: string, label: string, steps: any[], stock: boolean, baseStockMacroId?: string }|null}
     */
    _findMacroById(macroId) {
      const id = String(macroId || "");
      if (!id) return null;
      const user = (this._st.macros || []).find((m) => m && m.id === id) || null;
      if (user) {
        return {
          id: user.id,
          label: String(user.label || "Macro"),
          steps: Array.isArray(user.steps) ? user.steps : [],
          stock: false,
          baseStockMacroId: user.baseStockMacroId
        };
      }
      const stock = (this._st.stockMacros || []).find((m) => m && m.id === id) || getStockMacroById(id);
      if (!stock) return null;
      return {
        id: stock.id,
        label: String(stock.label || "Macro"),
        steps: Array.isArray(stock.steps) ? stock.steps : [],
        stock: true
      };
    }
    /**
     * @param {any} step
     * @returns {string}
     */
    _stepLabel(step) {
      const kind = String(step?.kind || (step?.functionId ? "function" : ""));
      if (kind === "function") return getFunctionDef(step.functionId)?.label || String(step.functionId || "Function");
      const def = MACRO_LOGIC_DEFS.find((d) => d.kind === kind);
      return def ? def.label : kind || "Step";
    }
    /**
     * @param {any} step
     * @returns {string}
     */
    _stepSummary(step) {
      const kind = String(step?.kind || (step?.functionId ? "function" : ""));
      if (kind === "wait") return `${Math.max(0, Number(step.ms) || 0)} ms`;
      if (kind === "stop") return "end run";
      if (kind === "gate") {
        const def = getMacroGateOpDef(step.op);
        const skip = Math.max(0, Number(step.thenSkip) || 0);
        const key = step.leftKey ? ` \xB7 field ${step.leftKey}` : "";
        const right = def.needsRight && step.right !== void 0 && step.right !== "" ? ` "${step.right}"` : "";
        return `${def.label}${right}${key} \xB7 skip next ${skip}`;
      }
      if (kind === "runMacro") {
        const nested = step.macroId ? this._findMacroById(step.macroId) : null;
        return nested ? nested.label : step.macroId || "(pick macro)";
      }
      const parts = [];
      const summary = summarizeFunctionParameters(step.functionId, step.parameters);
      if (summary) parts.push(summary);
      if (step.delayMsBefore) parts.push(`delay ${step.delayMsBefore} ms`);
      return parts.join(" \xB7 ");
    }
    /**
     * @param {import('../modules/keyboard-layout-store.js').UserMacro} updated
     */
    _syncMacroInState(updated) {
      try {
        const idx = (this._st.macros || []).findIndex((m) => m && m.id === updated.id);
        if (idx >= 0) this._st.macros[idx] = updated;
      } catch {
      }
      try {
        this._renderRightList();
      } catch {
      }
    }
    /**
     * @returns {string} `user:<id>` or `builtin:<familyId>`
     */
    _selectedLayoutValue() {
      if (this._st.mode === "user" && this._st.userLayoutId) {
        return `user:${this._st.userLayoutId}`;
      }
      const inferred = inferFamilyAndHandednessFromLayoutId(this._st.builtinLayoutId);
      return builtinFamilySelectValue(inferred.familyId);
    }
    /**
     * @returns {string}
     */
    _builtinLayoutDisplayName() {
      try {
        const inferred = inferFamilyAndHandednessFromLayoutId(this._st.builtinLayoutId);
        const familyId = normalizeKeyboardLayoutFamilyId(
          this._kp?._settings?.keyboardLayoutFamilyId || inferred.familyId
        );
        return formatBuiltinFamilyLabel(familyId);
      } catch {
      }
      return "Built-in: Browsing";
    }
    /** @param {boolean} open */
    _setLayoutComboOpen(open) {
      const list = this._layoutComboList;
      const input = this._layoutComboInput;
      if (!list || !input) return;
      list.hidden = !open;
      try {
        input.setAttribute("aria-expanded", open ? "true" : "false");
      } catch {
      }
    }
    /** @param {boolean} open */
    _setLayoutOptsOpen(open) {
      const menu = this._layoutOptsMenu;
      const btn = this._layoutOptsBtn;
      if (!menu) return;
      menu.hidden = !open;
      try {
        btn?.setAttribute("aria-expanded", open ? "true" : "false");
      } catch {
      }
    }
    /**
     * Select a layout for editing — same behavior as the Layout combo.
     * Used by the Keyboard Reference titlebar select during edit mode.
     * @param {string} value
     */
    async selectLayoutByValue(value) {
      await this._selectLayoutByValue(value);
    }
    /**
     * @param {string} value
     */
    async _selectLayoutByValue(value) {
      const v = String(value || "");
      if (v.startsWith("user:")) {
        const id = v.slice("user:".length);
        const found = this._st.userLayouts.find((l) => l && l.id === id) || null;
        this._st.mode = "user";
        this._st.userLayoutId = id;
        this._st.userLayout = found;
        this._renderLayoutSelect();
        this._renderRightList();
        this._emitChange();
        return;
      }
      const familyId = parseBuiltinFamilySelectValue(v) || "browsing";
      const handedness = inferFamilyAndHandednessFromLayoutId(
        this._kp?._keyboardLayoutId || this._st.builtinLayoutId
      ).handedness;
      const builtinLayoutId = resolveKeyboardLayoutId({ familyId, handedness });
      const familyName = this._familyBaseLabelFor(builtinLayoutId);
      const ok = typeof window.confirm === "function" ? window.confirm(
        `"${familyName}" is a built-in layout and cannot be edited.

Duplicate it and edit the copy?`
      ) : false;
      if (!ok) {
        this._renderLayoutSelect();
        this._emitChange();
        return;
      }
      this._st.builtinLayoutId = builtinLayoutId;
      await this.duplicateLayout({ builtinLayoutId });
    }
    async _commitLayoutComboRename() {
      const input = this._layoutComboInput;
      if (!input) return;
      if (this._st.mode !== "user" || !this._st.userLayout) {
        this._renderLayoutSelect();
        return;
      }
      const next = String(input.value || "").trim() || "Custom Layout";
      if (next === String(this._st.userLayout.label || "")) return;
      this._st.userLayout.label = next;
      await this._persistUserLayout();
      this._st.userLayouts = await listUserKeyboardLayouts();
      this._renderLayoutSelect();
      this._emitChange();
    }
    _renderLayoutSelect() {
      const combo = this._layoutCombo;
      const input = this._layoutComboInput;
      const list = this._layoutComboList;
      if (!combo || !input || !list) return;
      const selected = this._selectedLayoutValue();
      const current = String(
        this._kp?._currentKeyboardLayoutId || this._kp?._settings?.currentKeyboardLayoutId || "builtin"
      );
      const currentFamily = normalizeKeyboardLayoutFamilyId(
        this._kp?._settings?.keyboardLayoutFamilyId
      );
      const currentKey = current.startsWith("user:") ? current : builtinFamilySelectValue(currentFamily);
      const selectedKey = selected;
      const isCurrent = currentKey === selectedKey;
      const readOnly = this._isReadOnly();
      const builtinName = this._builtinLayoutDisplayName();
      const groups = listLayoutPickerGroups(this._st.userLayouts || []);
      list.replaceChildren();
      const appendGroup = (heading, items) => {
        const hdr = document.createElement("li");
        hdr.className = "kp-cfg-combo-group";
        hdr.setAttribute("role", "presentation");
        hdr.textContent = heading;
        list.appendChild(hdr);
        for (const opt of items || []) {
          if (!opt?.value) continue;
          const li = document.createElement("li");
          li.setAttribute("role", "none");
          const btn = document.createElement("button");
          btn.type = "button";
          btn.className = "kp-cfg-combo-option" + (opt.value === selected ? " is-active" : "") + (opt.value === currentKey ? " is-current" : "");
          btn.setAttribute("role", "option");
          btn.setAttribute("data-kp-layout-id", opt.value);
          btn.setAttribute("aria-selected", opt.value === selected ? "true" : "false");
          const name = document.createElement("span");
          name.className = "kp-cfg-combo-option-name";
          name.textContent = opt.label;
          btn.appendChild(name);
          if (opt.value === currentKey) {
            const chip = document.createElement("span");
            chip.className = "kp-cfg-combo-option-current";
            chip.appendChild(mkCfgIcon(document, "kp-cfg-i-check"));
            chip.appendChild(document.createTextNode("Current"));
            btn.appendChild(chip);
          }
          li.appendChild(btn);
          list.appendChild(li);
        }
      };
      appendGroup("Built-In", groups.builtin);
      appendGroup("Custom", groups.custom);
      if (this.shadowRoot?.activeElement !== input) {
        input.value = readOnly ? builtinName : String(this._st.userLayout?.label || "Custom Layout");
      }
      input.readOnly = readOnly;
      combo.classList.toggle("is-builtin", readOnly);
      combo.classList.toggle("is-current", isCurrent);
      const badge = this._currentBadge;
      if (badge) {
        badge.hidden = !isCurrent;
        badge.setAttribute("aria-hidden", isCurrent ? "false" : "true");
      }
      if (this._setCurrentBtn) this._setCurrentBtn.disabled = isCurrent;
      try {
        const root = this.shadowRoot;
        if (!root) return;
        const byAction = (action) => root.querySelector(`.kp-cfg-btn[data-kp-cfg-action="${action}"]`);
        const dup = byAction("duplicate");
        if (dup) dup.style.display = readOnly ? "" : "none";
        for (const action of ["delete", "export"]) {
          const btn = byAction(action);
          if (btn) btn.disabled = readOnly || !this._st.userLayoutId;
        }
      } catch {
      }
    }
    /** @returns {boolean} */
    _isKeyboardReferenceVisible() {
      try {
        const kp = this._kp;
        const help = kp?.floatingKeyboardHelp;
        if (help && typeof help.isVisible === "function") return !!help.isVisible();
        return !!kp?._keyboardHelpVisible;
      } catch {
        return false;
      }
    }
    /** @param {boolean} open */
    _setInspectorOpen(open) {
      this._inspectorOpen = !!open;
      const pane = this._inspectorPane;
      if (pane) pane.classList.toggle("kp-cfg-collapsed", !this._inspectorOpen);
      try {
        this._mainRow?.classList.toggle("kp-cfg-inspector-open", this._inspectorOpen);
      } catch {
      }
    }
    /** @param {boolean} open */
    _setCreateOpen(open) {
      if (!this._macroBuilderEnabled()) open = false;
      this._createOpen = !!open;
      if (!this._createOpen) this._createMaximized = false;
      const pane = this._createPane;
      if (pane) {
        pane.classList.toggle("kp-cfg-open", this._createOpen);
        pane.classList.toggle("kp-cfg-maximized", this._createOpen && this._createMaximized);
      }
      try {
        this._createPane?.parentElement?.classList.toggle(
          "kp-cfg-create-maximized",
          this._createOpen && this._createMaximized
        );
      } catch {
      }
      const hdr = this._createPane?.querySelector?.(".kp-cfg-pane-hdr");
      if (hdr) {
        hdr.setAttribute("aria-expanded", this._createOpen ? "true" : "false");
        hdr.title = this._createOpen ? "Collapse User Macros" : "Expand User Macros";
      }
      const btn = this._createToggleBtn;
      if (btn) {
        const label = this._createOpen ? "Collapse" : "Expand";
        const icoId = this._createOpen ? "kp-cfg-i-collapse-down" : "kp-cfg-i-expand";
        btn.replaceChildren();
        btn.appendChild(mkCfgIcon(document, icoId));
        btn.appendChild(document.createTextNode(label));
        btn.title = this._createOpen ? "Collapse User Macros" : "Show/hide the User Macros builder";
      }
      this._syncCreateExpandUpBtn();
    }
    _toggleCreateMaximized() {
      if (!this._createOpen) this._setCreateOpen(true);
      if (this._createMaximized) {
        this._createMaximized = false;
        if (this._createPrevHeightPx != null && this._mainRow) {
          this._mainRow.style.setProperty("--kp-cfg-create-height", `${this._createPrevHeightPx}px`);
        }
      } else {
        const height = this._createPane?.getBoundingClientRect().height;
        this._createPrevHeightPx = Number.isFinite(height) ? Math.round(height) : null;
        this._createMaximized = true;
      }
      this._setCreateOpen(true);
    }
    _syncCreateExpandUpBtn() {
      const btn = this._createExpandUpBtn;
      if (!btn) return;
      const show = this._createOpen;
      btn.hidden = !show;
      if (!show) return;
      const maxed = this._createMaximized;
      btn.replaceChildren();
      btn.appendChild(mkCfgIcon(document, maxed ? "kp-cfg-i-collapse" : "kp-cfg-i-expand-up"));
      btn.appendChild(document.createTextNode(maxed ? "Restore" : "Expand up"));
      btn.title = maxed ? "Restore User Macros height" : "Expand upward to the Actions Library titlebar";
    }
    /**
     * Add mockup-parity resize controls between Workspace/Inspector and Library/User Macros.
     * Values remain local to this panel session; they are presentation preferences, not layout data.
     */
    _wireSplitters() {
      const vertical = this._workspaceInspectorSplitter;
      const horizontal = this._libraryCreateSplitter;
      const row = this._mainRow;
      const workspace = this._createPane?.parentElement;
      if (!vertical || !horizontal || !row || !workspace) return;
      const setInspectorWidth = (width) => {
        const next = Math.max(200, Math.min(480, Math.round(width)));
        row.style.setProperty("--kp-cfg-inspector-width", `${next}px`);
        if (!this._inspectorOpen) this._setInspectorOpen(true);
      };
      const libraryHeaderBottom = () => {
        const libraryHeader = workspace.querySelector(".kp-cfg-pane-library > .kp-cfg-pane-hdr");
        return libraryHeader?.getBoundingClientRect().bottom ?? workspace.getBoundingClientRect().top;
      };
      const maxCreateHeight = () => {
        const bounds = workspace.getBoundingClientRect();
        const splitterHeight = horizontal.getBoundingClientRect().height || 0;
        return Math.max(210, Math.round(bounds.bottom - libraryHeaderBottom() - splitterHeight));
      };
      const setCreateHeight = (height) => {
        const next = Math.max(210, Math.min(maxCreateHeight(), Math.round(height)));
        row.style.setProperty("--kp-cfg-create-height", `${next}px`);
        if (!this._createOpen) this._setCreateOpen(true);
      };
      const wireDrag = (el2, axis, onMove) => {
        el2.addEventListener("pointerdown", (event) => {
          if (event.button !== 0) return;
          event.preventDefault();
          el2.classList.add("kp-cfg-split-dragging");
          try {
            el2.setPointerCapture(event.pointerId);
          } catch {
          }
          const move = (e) => onMove(e);
          const finish = () => {
            el2.classList.remove("kp-cfg-split-dragging");
            try {
              el2.releasePointerCapture(event.pointerId);
            } catch {
            }
            el2.removeEventListener("pointermove", move);
            el2.removeEventListener("pointerup", finish);
            el2.removeEventListener("pointercancel", finish);
          };
          el2.addEventListener("pointermove", move);
          el2.addEventListener("pointerup", finish);
          el2.addEventListener("pointercancel", finish);
        }, true);
        el2.addEventListener("keydown", (event) => {
          const isDecrease = axis === "x" ? event.key === "ArrowLeft" : event.key === "ArrowDown";
          const isIncrease = axis === "x" ? event.key === "ArrowRight" : event.key === "ArrowUp";
          if (!isDecrease && !isIncrease) return;
          event.preventDefault();
          const amount = event.shiftKey ? 40 : 10;
          if (axis === "x") {
            const width = this._inspectorPane?.getBoundingClientRect().width || CONFIG_INSPECTOR_WIDTH_PX;
            setInspectorWidth(width + (isIncrease ? amount : -amount));
          } else {
            const height = this._createPane?.getBoundingClientRect().height || 210;
            setCreateHeight(height + (isIncrease ? amount : -amount));
          }
        }, true);
      };
      wireDrag(vertical, "x", (event) => {
        const bounds = row.getBoundingClientRect();
        const width = bounds.right - event.clientX;
        if (width <= 40) {
          this._setInspectorOpen(false);
          return;
        }
        setInspectorWidth(width);
      });
      wireDrag(horizontal, "y", (event) => {
        const bounds = workspace.getBoundingClientRect();
        const splitterHeight = horizontal.getBoundingClientRect().height || 0;
        const minTop = libraryHeaderBottom() + splitterHeight;
        const top = Math.max(event.clientY, minTop);
        const height = bounds.bottom - top;
        if (height <= 36) {
          this._setCreateOpen(false);
          return;
        }
        setCreateHeight(height);
      });
    }
    /**
     * @param {'script'|'macroKey'} mode
     * @param {{ open?: boolean }} [opts]
     */
    _setCreateMode(mode, { open = false } = {}) {
      const wantMacroKey = mode === "macroKey" && this._macroKeysLibraryEnabled();
      this._createMode = wantMacroKey ? "macroKey" : "script";
      if (this._scriptPanel) this._scriptPanel.hidden = this._createMode !== "script";
      if (this._macroKeyPanel) this._macroKeyPanel.hidden = this._createMode !== "macroKey";
      try {
        for (const btn of this._createModeSeg?.querySelectorAll("[data-kp-create-mode]") || []) {
          if (btn.dataset.kpCreateMode === "macroKey") {
            btn.hidden = !this._macroKeysLibraryEnabled();
          }
          const on = btn.dataset.kpCreateMode === this._createMode;
          btn.setAttribute("aria-selected", on ? "true" : "false");
        }
      } catch {
      }
      if (open) this._setCreateOpen(true);
    }
    _renderLibraryTabs() {
      const host = this._libTabsEl;
      if (!host) return;
      host.replaceChildren();
      const tabs = [
        { id: "all", label: "All", icon: "kp-cfg-i-all" },
        { id: "functions", label: "Functions", icon: "kp-cfg-i-lib" },
        ...this._macroBuilderEnabled() ? [{ id: "macros", label: "Macros", icon: "kp-cfg-i-macro" }] : [],
        ...this._macroKeysLibraryEnabled() ? [{ id: "macroKeys", label: "Macro Keys", icon: "kp-cfg-i-keycap" }] : []
      ];
      if (!this._macroBuilderEnabled() && this._libPrimaryTab === "macros") {
        this._libPrimaryTab = "all";
      }
      if (!this._macroKeysLibraryEnabled() && this._libPrimaryTab === "macroKeys") {
        this._libPrimaryTab = "all";
      }
      for (const tab of tabs) {
        const btn = document.createElement("button");
        btn.type = "button";
        btn.className = "kp-cfg-seg-btn";
        btn.setAttribute("role", "tab");
        btn.dataset.kpLibTab = tab.id;
        btn.appendChild(mkCfgIcon(document, tab.icon));
        btn.appendChild(document.createTextNode(` ${tab.label}`));
        btn.setAttribute("aria-selected", this._libPrimaryTab === tab.id ? "true" : "false");
        btn.addEventListener("click", () => {
          this._libPrimaryTab = /** @type {any} */
          tab.id;
          this._renderLibraryTabs();
          this._renderRightList();
        }, true);
        host.appendChild(btn);
      }
      const select = this._fnCategorySelect;
      const wrap = this._fnCategoryWrap || select?.parentElement;
      const show = this._libPrimaryTab === "functions";
      if (select) select.hidden = !show;
      if (wrap && wrap !== select) wrap.hidden = !show;
    }
    _syncLibViewSeg() {
      const host = this._libViewSegEl;
      if (!host) return;
      for (const btn of host.querySelectorAll("button[data-kp-lib-view]")) {
        const on = btn.dataset.kpLibView === this._libViewMode;
        btn.setAttribute("aria-selected", on ? "true" : "false");
      }
    }
    /**
     * @param {string} key
     * @returns {boolean}
     */
    _isTableGroupExpanded(key) {
      return !!this._tableExpanded?.has(key);
    }
    /**
     * @param {string} key
     */
    _toggleTableGroup(key) {
      if (!this._tableExpanded) {
        this._tableExpanded = new Set(DEFAULT_SETTINGS.actionsLibraryTableExpanded || []);
      }
      if (this._tableExpanded.has(key)) this._tableExpanded.delete(key);
      else this._tableExpanded.add(key);
      this._renderRightList();
      void this._persistTableExpanded();
    }
    async _persistTableExpanded() {
      try {
        const keys = [...this._tableExpanded || []];
        await setSettings({ actionsLibraryTableExpanded: keys });
      } catch {
      }
    }
    /**
     * Expand/collapse the Actions Library placement instructions section.
     * @param {boolean} expanded
     * @param {{ persist?: boolean }} [opts]
     */
    _setLibInstructionsExpanded(expanded, opts = {}) {
      const next = !!expanded;
      this._libInstructionsExpanded = next;
      const el2 = this._libInstructionsEl;
      const toggle = this._libInstructionsToggleEl || el2?.querySelector?.(".kp-cfg-lib-instructions-toggle");
      try {
        if (el2) el2.classList.toggle("is-collapsed", !next);
      } catch {
      }
      try {
        if (toggle) {
          toggle.setAttribute("aria-expanded", next ? "true" : "false");
          toggle.title = next ? "Collapse instructions" : "Expand instructions";
        }
      } catch {
      }
      if (opts.persist !== false) {
        void this._persistLibInstructionsExpanded();
      }
    }
    async _persistLibInstructionsExpanded() {
      try {
        await setSettings({
          actionsLibraryInstructionsExpanded: !!this._libInstructionsExpanded
        });
      } catch {
      }
    }
    /**
     * @param {string[]|undefined|null} keys
     */
    _applyTableExpandedFromSettings(keys) {
      const list = Array.isArray(keys) ? keys.filter((k) => typeof k === "string" && k.trim()).map((k) => k.trim()) : [...DEFAULT_SETTINGS.actionsLibraryTableExpanded || []];
      this._tableExpanded = new Set(list);
      this._tableExpandedHydrated = true;
    }
    _renderFunctionCategorySelect() {
      const select = this._fnCategorySelect;
      if (!select) return;
      select.replaceChildren();
      const all = document.createElement("option");
      all.value = "";
      all.textContent = "All categories";
      select.appendChild(all);
      const cats = /* @__PURE__ */ new Set();
      for (const def of listFunctionDefs()) {
        if (!def || def.legacyMacroKeyKind || def.assignableToKey === false) continue;
        cats.add(getFunctionCategory(def.id) || "Other");
      }
      const order = Array.isArray(FUNCTION_CATEGORY_ORDER) ? FUNCTION_CATEGORY_ORDER : [];
      const sorted = [
        ...order.filter((c) => cats.has(c)),
        ...[...cats].filter((c) => !order.includes(c)).sort()
      ];
      for (const cat of sorted) {
        const opt = document.createElement("option");
        opt.value = cat;
        opt.textContent = cat;
        select.appendChild(opt);
      }
      select.value = this._libFunctionCategory || "";
      try {
        select._kpSelectMenu?.syncFromNative?.();
      } catch {
      }
    }
    _buildBuiltinSlotMap() {
      const inferred = inferFamilyAndHandednessFromLayoutId(this._st.builtinLayoutId);
      const kb = this._kp?.keybindings || buildEffectiveKeybindings(
        this._st.builtinLayoutId,
        inferred.handedness
      );
      const map = {};
      for (const [actionId, binding] of Object.entries(kb || {})) {
        const slot = physicalSlotLabelFromBinding(binding);
        if (!slot) continue;
        map[slot] = { type: "function", id: String(actionId) };
      }
      return map;
    }
    _getEditableSlotMap() {
      if (this._st.mode === "user" && this._st.userLayout && typeof this._st.userLayout.slots === "object") {
        return this._st.userLayout.slots;
      }
      return this._buildBuiltinSlotMap();
    }
    /**
     * Split a key label into line elements (one word per line when multi-word).
     * @param {HTMLElement} mainEl
     * @param {string} label
     */
    _fillKeyLabelLines(mainEl, label) {
      if (!mainEl) return;
      mainEl.replaceChildren();
      const parts = String(label || "").trim().split(/\s+/).filter(Boolean);
      if (!parts.length) {
        mainEl.textContent = "";
        return;
      }
      if (parts.length === 1) {
        const line = document.createElement("span");
        line.className = "kp-cfg-label-line";
        line.textContent = parts[0];
        mainEl.appendChild(line);
        return;
      }
      for (const part of parts) {
        const line = document.createElement("span");
        line.className = "kp-cfg-label-line";
        line.textContent = part;
        mainEl.appendChild(line);
      }
    }
    /**
     * Select a library item into the Inspector dock. Config-side inspection is dock-only now —
     * the Keyboard Reference keycaps keep their own key-info popovers.
     * @param {{ type: string, id: string }} item
     */
    _inspectItem(item) {
      if (!item || !item.id) return;
      try {
        this._cancelPlaceMode();
      } catch {
      }
      this._inspectorSelection = { type: String(item.type), id: String(item.id) };
      this._setInspectorOpen(true);
      this._renderInspector();
      this._renderRightList();
    }
    /**
     * Enter place mode for an item selected from the dock, anchoring the arrow on its library
     * keycap when one is rendered.
     * @param {{ type: string, id: string }} item
     */
    _beginPlaceModeFromLibrary(item) {
      if (!item || !item.type || !item.id) return;
      let sourceEl = null;
      try {
        const id = CSS.escape ? CSS.escape(String(item.id)) : String(item.id);
        sourceEl = this._listEl?.querySelector?.(
          `.key[data-kp-item-type="${item.type}"][data-kp-item-id="${id}"], tr[data-kp-item-type="${item.type}"][data-kp-item-id="${id}"]`
        ) || null;
      } catch {
      }
      this._beginPlaceMode(item, sourceEl);
    }
    /**
     * Short Description fieldset shown above cards in an Actions Library section.
     * @param {string} text
     * @returns {HTMLElement|null}
     */
    _createSectionDescriptionFieldset(text) {
      const line = String(text || "").trim();
      if (!line) return null;
      const fieldset = document.createElement("fieldset");
      fieldset.className = "kp-cfg-desc-fieldset";
      fieldset.setAttribute("aria-label", "Description");
      const p = document.createElement("p");
      p.className = "kp-cfg-desc-line";
      p.textContent = line;
      fieldset.appendChild(p);
      return fieldset;
    }
    _renderRightList() {
      const list = this._listEl;
      if (!list) return;
      const q = String(this._searchInput?.value || "").trim().toLowerCase();
      list.replaceChildren();
      this._syncLibViewSeg();
      const slots = this._getEditableSlotMap();
      const assignedInfoByItemKey = /* @__PURE__ */ new Map();
      try {
        for (const [slot, v] of Object.entries(slots || {})) {
          if (!v || !v.type || !v.id) continue;
          const k = `${v.type}:${v.id}`;
          const prev = assignedInfoByItemKey.get(k) || { count: 0, first: "" };
          assignedInfoByItemKey.set(k, { count: prev.count + 1, first: prev.first || String(slot) });
        }
      } catch {
      }
      const tab = this._libPrimaryTab;
      const showMacros = this._macroBuilderEnabled() && (tab === "all" || tab === "macros");
      const showMacroKeys = this._macroKeysLibraryEnabled() && (tab === "all" || tab === "macroKeys");
      const showFunctions = tab === "all" || tab === "functions";
      if (this._libViewMode === "table") {
        this._renderLibraryTable({
          list,
          q,
          tab,
          showMacros,
          showMacroKeys,
          showFunctions,
          assignedInfoByItemKey
        });
        return;
      }
      const appendKeyItem = ({
        type: type2,
        id,
        label,
        sublabel,
        keyboardClass,
        infoKey,
        badge,
        badgeClass,
        variant,
        functionId,
        kind
      }) => {
        const item = document.createElement("div");
        item.className = `kp-cfg-item${variant ? ` kp-cfg-variant-${variant}` : ""}`;
        item.dataset.kpItemType = type2;
        item.dataset.kpItemId = id;
        item.draggable = true;
        if (this._inspectorSelection && this._inspectorSelection.type === type2 && this._inspectorSelection.id === id) {
          item.classList.add("kp-cfg-item-inspecting");
        }
        const keyEl = document.createElement("button");
        keyEl.type = "button";
        keyEl.className = `key${keyboardClass ? " " + keyboardClass : ""}`;
        keyEl.draggable = true;
        keyEl.title = `${label} \u2014 click to place, or drag onto a Keyboard Reference key`;
        keyEl.dataset.kpItemType = type2;
        keyEl.dataset.kpItemId = id;
        const iconFunctionId = type2 === "function" ? String(functionId || id || "") : "";
        if (type2 === "function" && iconFunctionId) {
          try {
            keyEl.setAttribute("data-kp-action-id", iconFunctionId);
          } catch {
          }
          ensureKeyBackgroundIcon(document, keyEl);
        } else if (type2 === "macro") {
          try {
            keyEl.setAttribute("data-kp-macro-id", String(id));
          } catch {
          }
          ensureKeyBackgroundIcon(document, keyEl);
        }
        if (this._placeItem && this._placeItem.type === type2 && this._placeItem.id === id) {
          keyEl.classList.add("kp-place-source");
          item.classList.add("kp-place-source-item");
        }
        const main = document.createElement("div");
        main.className = "key-main";
        this._fillKeyLabelLines(main, abbreviateLabel(label));
        const info = assignedInfoByItemKey.get(infoKey) || null;
        if (info && info.count > 0) {
          const lab = document.createElement("div");
          lab.className = "key-label";
          lab.textContent = info.count > 1 ? `\xD7${info.count}` : String(info.first || "");
          keyEl.appendChild(main);
          keyEl.appendChild(lab);
        } else {
          keyEl.appendChild(main);
        }
        const setDragData = (e) => {
          try {
            e.dataTransfer?.setData?.(KP_LAYOUT_ITEM_MIME, JSON.stringify({ type: type2, id }));
            e.dataTransfer.effectAllowed = "copy";
          } catch {
          }
        };
        keyEl.addEventListener("dragstart", setDragData, true);
        item.addEventListener("dragstart", (e) => {
          if (e.target !== item && !(e.target instanceof Node && keyEl.contains(e.target))) return;
          setDragData(e);
        }, true);
        keyEl.addEventListener("click", (e) => {
          e.preventDefault();
          e.stopPropagation();
          this._beginPlaceMode({ type: type2, id }, keyEl);
        }, true);
        item.addEventListener("click", (e) => {
          if (e.target?.closest?.("button, a, input, select, textarea")) return;
          this._inspectItem({ type: type2, id });
          if (type2 === "function" && getFunctionDef(id)) {
            this._selectedLibraryFunctionId = String(id);
            if (this._addStepSelect) {
              this._addStepSelect.value = String(id);
              this._addStepSelect._kpSelectMenu?.syncFromNative?.();
            }
          }
        }, true);
        const meta = document.createElement("div");
        meta.className = "kp-cfg-card-meta";
        const name = document.createElement("div");
        name.className = "kp-cfg-card-name";
        name.textContent = label;
        meta.appendChild(name);
        if (sublabel) {
          const sub = document.createElement("div");
          sub.className = "kp-cfg-card-sub";
          sub.textContent = sublabel;
          meta.appendChild(sub);
        }
        const actionsRow = document.createElement("div");
        actionsRow.className = "kp-cfg-card-actions";
        if (badge) {
          const chip = document.createElement("span");
          chip.className = `kp-cfg-badge${badgeClass ? " " + badgeClass : ""}`;
          chip.textContent = badge;
          actionsRow.appendChild(chip);
        }
        meta.appendChild(actionsRow);
        item.appendChild(keyEl);
        item.appendChild(meta);
        return item;
      };
      const cardActions = (card) => card.querySelector(".kp-cfg-card-actions") || card;
      const allDefs = showFunctions ? listFunctionDefs().filter((d) => {
        if (!d || d.legacyMacroKeyKind || d.assignableToKey === false) return false;
        if (tab === "functions" && this._libFunctionCategory) {
          return (getFunctionCategory(d.id) || "Other") === this._libFunctionCategory;
        }
        return true;
      }) : [];
      const filteredDefs = sortFunctionDefsForLibrary(
        q ? allDefs.filter((d) => `${d.id} ${d.label} ${d.description || ""} ${d.details || ""} ${getFunctionCategory(d.id)}`.toLowerCase().includes(q)) : allDefs
      );
      const byCat = /* @__PURE__ */ new Map();
      for (const d of filteredDefs) {
        const cat = getFunctionCategory(d.id) || "Other";
        if (!byCat.has(cat)) byCat.set(cat, []);
        byCat.get(cat).push(d);
      }
      const order = Array.isArray(FUNCTION_CATEGORY_ORDER) ? FUNCTION_CATEGORY_ORDER : [];
      const cats = [
        ...order.filter((c) => byCat.has(c)),
        ...[...byCat.keys()].filter((c) => !order.includes(c)).sort()
      ];
      for (const cat of cats) {
        const items = byCat.get(cat) || [];
        if (!items.length) continue;
        const section = document.createElement("div");
        section.className = "kp-cfg-category";
        section.dataset.kpSection = `fn-${String(cat).toLowerCase().replace(/\s+/g, "-")}`;
        const title = document.createElement("div");
        title.className = "kp-cfg-category-title";
        title.textContent = cat;
        section.appendChild(title);
        const catDesc = this._createSectionDescriptionFieldset(getFunctionCategoryDescription(cat));
        if (catDesc) section.appendChild(catDesc);
        const grid = document.createElement("div");
        grid.className = "kp-cfg-key-grid";
        for (const def of items) {
          const isFixedKey = FIXED_KEY_FUNCTION_IDS.includes(def.id);
          if (!isFixedKey && isFunctionInstantiable(def.id)) {
            const instances = (this._st.actions || []).filter((a) => a && a.functionId === def.id);
            const fieldset = document.createElement("fieldset");
            fieldset.className = "kp-cfg-instance-fieldset";
            const legend = document.createElement("legend");
            legend.textContent = def.label;
            fieldset.appendChild(legend);
            const addBtn = document.createElement("button");
            addBtn.type = "button";
            addBtn.className = "kp-cfg-btn kp-cfg-instance-new";
            addBtn.textContent = `New ${def.label}`;
            addBtn.title = def.description || `Create a new ${def.label} instance`;
            addBtn.addEventListener("click", async (e) => {
              e.preventDefault();
              e.stopPropagation();
              try {
                const created = await createUserAction({ functionId: def.id });
                if (created) {
                  await this._reloadActions();
                  this._renderRightList();
                  this._openActionParamsEditor(def, created);
                }
              } catch {
                this._notify("Failed to create instance.", "error");
              }
            }, true);
            fieldset.appendChild(addBtn);
            if (instances.length) {
              const instGrid = document.createElement("div");
              instGrid.className = "kp-cfg-key-grid";
              instances.forEach((inst, index) => {
                const itemEl2 = appendKeyItem({
                  type: "function",
                  id: inst.id,
                  label: instances.length > 1 ? `${def.label} #${index + 1}` : def.label,
                  sublabel: summarizeFunctionParameters(inst.functionId, inst.parameters),
                  keyboardClass: def.keyboardClass || "",
                  infoKey: `function:${inst.id}`,
                  variant: "configurable-fn",
                  functionId: def.id
                });
                const edit = document.createElement("button");
                edit.type = "button";
                edit.className = "kp-cfg-inspect";
                edit.textContent = "Edit";
                edit.title = `Configure ${def.label}`;
                edit.addEventListener("click", (e) => {
                  e.preventDefault();
                  e.stopPropagation();
                  this._openActionParamsEditor(def, inst);
                }, true);
                edit.addEventListener("pointerdown", (e) => e.stopPropagation(), true);
                cardActions(itemEl2).appendChild(edit);
                if (def.worksWhileTyping) {
                  itemEl2.style.flexWrap = "wrap";
                  cardActions(itemEl2).appendChild(this._renderBindChordButton({ type: "function", id: inst.id }, def));
                }
                instGrid.appendChild(itemEl2);
              });
              fieldset.appendChild(instGrid);
            }
            grid.appendChild(fieldset);
            continue;
          }
          const itemEl = appendKeyItem({
            type: "function",
            id: def.id,
            label: def.label,
            sublabel: def.description || "",
            keyboardClass: def.keyboardClass || "",
            infoKey: `function:${def.id}`,
            variant: isFunctionInstantiable(def.id) ? "configurable-fn" : "stock-fn",
            functionId: def.id
          });
          if (isFixedKey && (actionHasParameters(def.id) || actionHasDestination(def.id))) {
            const conf = document.createElement("button");
            conf.type = "button";
            conf.className = "kp-cfg-inspect";
            conf.textContent = "Config";
            conf.title = `Configure ${def.label}`;
            conf.addEventListener("click", async (e) => {
              e.preventDefault();
              e.stopPropagation();
              await this._openFixedKeySettings(def, itemEl);
            }, true);
            conf.addEventListener("pointerdown", (e) => e.stopPropagation(), true);
            cardActions(itemEl).appendChild(conf);
          }
          if (def.worksWhileTyping) {
            itemEl.style.flexWrap = "wrap";
            cardActions(itemEl).appendChild(this._renderBindChordButton({ type: "function", id: def.id }, def));
          }
          grid.appendChild(itemEl);
        }
        section.appendChild(grid);
        list.appendChild(section);
      }
      if (showMacros) {
        const matchesQuery = (m) => !q || String(m?.label || "").toLowerCase().includes(q);
        const stockMacros = (this._st.stockMacros || []).filter((m) => m && m.id && matchesQuery(m));
        const userMacros = (this._st.macros || []).filter((m) => m && m.id && matchesQuery(m));
        const macrosSection = document.createElement("div");
        macrosSection.className = "kp-cfg-category";
        macrosSection.dataset.kpSection = "macros";
        const macrosTitle = document.createElement("div");
        macrosTitle.className = "kp-cfg-category-title";
        macrosTitle.textContent = "Macros";
        const newMacroBtn = document.createElement("button");
        newMacroBtn.type = "button";
        newMacroBtn.className = "kp-cfg-inspect";
        newMacroBtn.textContent = "+ New Macro";
        newMacroBtn.title = "Start an empty macro in the User Macros builder";
        newMacroBtn.addEventListener("click", () => this._startNewMacroDraft(), true);
        macrosTitle.appendChild(newMacroBtn);
        macrosSection.appendChild(macrosTitle);
        const macrosDesc = this._createSectionDescriptionFieldset(LIBRARY_SECTION_DESCRIPTIONS.macros);
        if (macrosDesc) macrosSection.appendChild(macrosDesc);
        const subgroups = document.createElement("div");
        subgroups.className = "kp-cfg-macro-subgroups";
        const renderMacroFieldset = (groupId, legendText, macros, stock, emptyText) => {
          const fieldset = document.createElement("fieldset");
          fieldset.className = "kp-cfg-macro-fieldset";
          fieldset.dataset.macroGroup = groupId;
          const legendEl = document.createElement("legend");
          legendEl.textContent = legendText;
          fieldset.appendChild(legendEl);
          if (!macros.length) {
            const empty = document.createElement("p");
            empty.className = "kp-cfg-subgroup-empty";
            empty.textContent = emptyText;
            fieldset.appendChild(empty);
            subgroups.appendChild(fieldset);
            return;
          }
          const grid = document.createElement("div");
          grid.className = "kp-cfg-key-grid kp-cfg-grid-macros";
          for (const m of macros) {
            const stepCount = Array.isArray(m.steps) ? m.steps.length : 0;
            const itemEl = appendKeyItem({
              type: "macro",
              id: m.id,
              label: String(m.label || "Macro"),
              sublabel: `${stepCount} step${stepCount === 1 ? "" : "s"}`,
              keyboardClass: "key-purple",
              infoKey: `macro:${m.id}`,
              badge: stock ? "Stock" : "User",
              badgeClass: stock ? "kp-cfg-badge-stock" : "kp-cfg-badge-user",
              variant: stock ? "stock-macro" : "user-macro"
            });
            const edit = document.createElement("button");
            edit.type = "button";
            edit.className = "kp-cfg-inspect";
            edit.textContent = stock ? "Customize" : "Edit steps";
            edit.title = stock ? `Open an editable copy of ${m.label || "Macro"}` : `Edit steps for ${m.label || "Macro"}`;
            edit.addEventListener("click", (e) => {
              e.preventDefault();
              e.stopPropagation();
              this._openMacroStepsEditor(m);
            }, true);
            edit.addEventListener("pointerdown", (e) => e.stopPropagation(), true);
            cardActions(itemEl).appendChild(edit);
            grid.appendChild(itemEl);
          }
          fieldset.appendChild(grid);
          subgroups.appendChild(fieldset);
        };
        renderMacroFieldset(
          "user",
          "User",
          userMacros,
          false,
          'None yet \u2014 click "+ New Macro", or Customize a stock macro.'
        );
        if (stockMacros.length || !q) {
          renderMacroFieldset("stock", "Stock", stockMacros, true, "None match.");
        }
        macrosSection.appendChild(subgroups);
        list.appendChild(macrosSection);
      }
      const macroKeys = showMacroKeys && Array.isArray(this._st.macroKeys) ? this._st.macroKeys : [];
      const macroKeyItems = q ? macroKeys.filter((m) => {
        const hay = `${m?.label || ""} ${m?.kind || ""} ${summarizeMacroKey(m)}`.toLowerCase();
        return hay.includes(q);
      }) : macroKeys;
      if (showMacroKeys && (macroKeyItems.length || !q)) {
        const section = document.createElement("div");
        section.className = "kp-cfg-category";
        section.dataset.kpSection = "macroKeys";
        const title = document.createElement("div");
        title.className = "kp-cfg-category-title";
        title.textContent = "Configured Macro Keys";
        section.appendChild(title);
        const mkDesc = this._createSectionDescriptionFieldset(LIBRARY_SECTION_DESCRIPTIONS.macroKeys);
        if (mkDesc) section.appendChild(mkDesc);
        if (!this._macroBuilderEnabled()) {
          const createHost = document.createElement("div");
          createHost.className = "kp-cfg-macrokey-inline-create";
          this._fillMacroKeyKindCreateGrid(createHost);
          section.appendChild(createHost);
        }
        if (macroKeyItems.length) {
          const grid = document.createElement("div");
          grid.className = "kp-cfg-key-grid";
          for (const m of macroKeyItems) {
            if (!m || !m.id) continue;
            const itemEl = appendKeyItem({
              // Macro Keys are Action Instances of a `legacyMacroKeyKind` Function (see
              // function-library.js) — placing one on a slot writes the same `type: 'function'`
              // SlotAssignment shape as any other Function/Action Instance.
              type: "function",
              id: m.id,
              label: String(m.label || m.kind || "Macro Key"),
              sublabel: summarizeMacroKey(m),
              keyboardClass: macroKeyKeyboardClass(m.kind),
              infoKey: `function:${m.id}`,
              badge: String(m.kind || ""),
              variant: "macro-key",
              kind: m.kind,
              functionId: FUNCTION_ID_BY_MACRO_KEY_KIND[m.kind] || ""
            });
            const conf = document.createElement("button");
            conf.type = "button";
            conf.className = "kp-cfg-inspect";
            conf.textContent = "Edit";
            conf.title = `Configure ${m.label || m.kind}`;
            conf.addEventListener("click", (e) => {
              e.preventDefault();
              e.stopPropagation();
              this._openMacroKeyEditor(m);
            }, true);
            conf.addEventListener("pointerdown", (e) => e.stopPropagation(), true);
            cardActions(itemEl).appendChild(conf);
            grid.appendChild(itemEl);
          }
          section.appendChild(grid);
        } else {
          const empty = document.createElement("div");
          empty.className = "kp-cfg-hint";
          empty.textContent = this._macroBuilderEnabled() ? "Create one from User Macros \u2192 Macro Key, configure it, then click its keycap to place it." : "Create a Macro Key above, then click its keycap to place it.";
          section.appendChild(empty);
        }
        list.appendChild(section);
      }
    }
    /**
     * Hierarchical Actions Library table (Phase 7). Same filters/data as the cards path.
     * @param {{
     *   list: HTMLElement,
     *   q: string,
     *   tab: string,
     *   showMacros: boolean,
     *   showMacroKeys: boolean,
     *   showFunctions: boolean,
     *   assignedInfoByItemKey: Map<string, { count: number, first: string }>
     * }} ctx
     */
    _renderLibraryTable(ctx) {
      const { list, q, tab, showMacros, showMacroKeys, showFunctions, assignedInfoByItemKey } = ctx;
      const hier = createHierarchicalTable({
        columns: [
          { key: "label", label: "Label", className: "kp-cfg-col-label", width: "34%" },
          { key: "kind", label: "Kind", className: "kp-cfg-col-kind", width: "14%" },
          { key: "summary", label: "Summary", className: "kp-cfg-col-summary", width: "22%" },
          { key: "slots", label: "Slots", className: "kp-cfg-col-slots", width: "12%" },
          { key: "actions", label: "Actions", className: "kp-cfg-col-actions", width: "18%" }
        ],
        ariaLabel: "Actions Library table",
        isGroupExpanded: (key) => this._isTableGroupExpanded(key),
        onToggleGroup: (key) => this._toggleTableGroup(key)
      });
      const appendGroupRow = ({ groupKey, label, depth = 0, count = 0, kind = "", trailing = null }) => hier.appendGroupRow({
        groupKey,
        label,
        depth,
        count,
        cells: [kind || "", "", ""],
        trailing
      });
      const appendLeafRow = ({
        type: type2,
        id,
        label,
        kind,
        summary,
        depth = 1,
        variant,
        functionId,
        iconKind,
        keyboardClass = "",
        actions = []
      }) => {
        const keyEl = document.createElement("button");
        keyEl.type = "button";
        keyEl.className = `key kp-cfg-lib-key${keyboardClass ? ` ${keyboardClass}` : ""}`;
        keyEl.draggable = true;
        keyEl.title = `${label} \u2014 drag onto a Keyboard Reference key, or click to place`;
        keyEl.dataset.kpItemType = type2;
        keyEl.dataset.kpItemId = id;
        const iconFunctionId = type2 === "function" ? String(functionId || id || "") : "";
        if (type2 === "function" && iconFunctionId) {
          try {
            keyEl.setAttribute("data-kp-action-id", iconFunctionId);
          } catch {
          }
          ensureKeyBackgroundIcon(document, keyEl);
        } else if (type2 === "macro") {
          try {
            keyEl.setAttribute("data-kp-macro-id", String(id));
          } catch {
          }
          ensureKeyBackgroundIcon(document, keyEl);
        }
        if (this._placeItem && this._placeItem.type === type2 && this._placeItem.id === id) {
          keyEl.classList.add("kp-place-source");
        }
        const setDragData = (e) => {
          try {
            e.dataTransfer?.setData?.(KP_LAYOUT_ITEM_MIME, JSON.stringify({ type: type2, id }));
            e.dataTransfer.effectAllowed = "copy";
          } catch {
          }
        };
        keyEl.addEventListener("dragstart", (e) => {
          e.stopPropagation();
          setDragData(e);
        }, true);
        keyEl.addEventListener("click", (e) => {
          e.preventDefault();
          e.stopPropagation();
          this._beginPlaceMode({ type: type2, id }, keyEl);
        }, true);
        const labelCell = hier.createLabelCell({
          depth,
          leading: keyEl,
          text: label
        });
        const info = assignedInfoByItemKey.get(`${type2}:${id}`) || null;
        let slotsText = "\u2014";
        let slotsTitle = "";
        if (info && info.count > 0) {
          slotsText = info.count > 1 ? `\xD7${info.count}` : String(info.first || "");
          slotsTitle = info.count > 1 ? `Assigned to ${info.count} slots` : `Assigned to ${info.first}`;
        }
        const actionsRow = document.createElement("div");
        actionsRow.className = "kp-cfg-card-actions";
        const placeBtn = document.createElement("button");
        placeBtn.type = "button";
        placeBtn.className = "kp-cfg-inspect";
        setCfgBtnLabel(placeBtn, "Place", "kp-cfg-i-place");
        placeBtn.title = `Place ${label} on a keyboard slot`;
        placeBtn.addEventListener("click", (e) => {
          e.preventDefault();
          e.stopPropagation();
          this._beginPlaceMode({ type: type2, id }, keyEl);
        }, true);
        actionsRow.appendChild(placeBtn);
        for (const act of actions) {
          const b = document.createElement("button");
          b.type = "button";
          b.className = "kp-cfg-inspect";
          b.textContent = act.label;
          if (act.title) b.title = act.title;
          b.addEventListener("click", (e) => {
            e.preventDefault();
            e.stopPropagation();
            act.onClick(e);
          }, true);
          actionsRow.appendChild(b);
        }
        const summaryEl = document.createElement("span");
        summaryEl.textContent = summary || "";
        if (summary) summaryEl.title = summary;
        const slotsEl = document.createElement("span");
        slotsEl.textContent = slotsText;
        if (slotsTitle) slotsEl.title = slotsTitle;
        const classNames = [];
        if (this._inspectorSelection && this._inspectorSelection.type === type2 && this._inspectorSelection.id === id) {
          classNames.push("kp-cfg-lib-row-inspecting");
        }
        if (this._placeItem && this._placeItem.type === type2 && this._placeItem.id === id) {
          classNames.push("kp-cfg-lib-row-place");
        }
        hier.appendLeafRow({
          className: classNames.join(" "),
          dataset: {
            kpItemType: type2,
            kpItemId: id
          },
          cells: [labelCell, kind || "", summaryEl, slotsEl, actionsRow],
          onClick: () => {
            this._inspectItem({ type: type2, id });
            if (type2 === "function" && getFunctionDef(id)) {
              this._selectedLibraryFunctionId = String(id);
              if (this._addStepSelect) {
                this._addStepSelect.value = String(id);
                this._addStepSelect._kpSelectMenu?.syncFromNative?.();
              }
            }
          }
        });
      };
      const appendHintRow = (text, depth = 1) => {
        hier.appendHintRow(text, depth);
      };
      const allDefs = showFunctions ? listFunctionDefs().filter((d) => {
        if (!d || d.legacyMacroKeyKind || d.assignableToKey === false) return false;
        if (tab === "functions" && this._libFunctionCategory) {
          return (getFunctionCategory(d.id) || "Other") === this._libFunctionCategory;
        }
        return true;
      }) : [];
      const filteredDefs = sortFunctionDefsForLibrary(
        q ? allDefs.filter((d) => `${d.id} ${d.label} ${d.description || ""} ${d.details || ""} ${getFunctionCategory(d.id)}`.toLowerCase().includes(q)) : allDefs
      );
      const byCat = /* @__PURE__ */ new Map();
      for (const d of filteredDefs) {
        const cat = getFunctionCategory(d.id) || "Other";
        if (!byCat.has(cat)) byCat.set(cat, []);
        byCat.get(cat).push(d);
      }
      const order = Array.isArray(FUNCTION_CATEGORY_ORDER) ? FUNCTION_CATEGORY_ORDER : [];
      const cats = [
        ...order.filter((c) => byCat.has(c)),
        ...[...byCat.keys()].filter((c) => !order.includes(c)).sort()
      ];
      if (showFunctions && cats.length) {
        if (appendGroupRow({
          groupKey: "functions",
          label: "Functions",
          depth: 0,
          count: filteredDefs.length,
          kind: "Group"
        })) {
          for (const cat of cats) {
            const items = byCat.get(cat) || [];
            if (!items.length) continue;
            const catKey = `functions:${String(cat).toLowerCase().replace(/\s+/g, "-")}`;
            if (!appendGroupRow({
              groupKey: catKey,
              label: cat,
              depth: 1,
              count: items.length,
              kind: "Category"
            })) continue;
            for (const def of items) {
              const isFixedKey = FIXED_KEY_FUNCTION_IDS.includes(def.id);
              if (!isFixedKey && isFunctionInstantiable(def.id)) {
                const instances = (this._st.actions || []).filter((a) => a && a.functionId === def.id);
                const parentKey = `fn:${def.id}`;
                const addBtn = document.createElement("button");
                addBtn.type = "button";
                addBtn.className = "kp-cfg-inspect";
                addBtn.textContent = `+ New`;
                addBtn.title = def.description || `Create a new ${def.label} instance`;
                addBtn.addEventListener("click", async (e) => {
                  e.preventDefault();
                  e.stopPropagation();
                  try {
                    const created = await createUserAction({ functionId: def.id });
                    if (created) {
                      await this._reloadActions();
                      this._renderRightList();
                      this._openActionParamsEditor(def, created);
                    }
                  } catch {
                    this._notify("Failed to create instance.", "error");
                  }
                }, true);
                if (appendGroupRow({
                  groupKey: parentKey,
                  label: def.label,
                  depth: 2,
                  count: instances.length,
                  kind: "Configurable",
                  trailing: addBtn
                })) {
                  if (!instances.length) {
                    appendHintRow("No instances yet \u2014 click + New.", 3);
                  }
                  instances.forEach((inst, index) => {
                    appendLeafRow({
                      type: "function",
                      id: inst.id,
                      label: instances.length > 1 ? `${def.label} #${index + 1}` : def.label,
                      kind: "Instance",
                      summary: summarizeFunctionParameters(inst.functionId, inst.parameters),
                      depth: 3,
                      variant: "configurable-fn",
                      functionId: def.id,
                      keyboardClass: def.keyboardClass || "",
                      actions: [{
                        label: "Edit",
                        title: `Configure ${def.label}`,
                        onClick: () => this._openActionParamsEditor(def, inst)
                      }]
                    });
                  });
                }
                continue;
              }
              const leafActions = [];
              if (isFixedKey && (actionHasParameters(def.id) || actionHasDestination(def.id))) {
                leafActions.push({
                  label: "Config",
                  title: `Configure ${def.label}`,
                  onClick: async () => {
                    await this._openFixedKeySettings(def, list);
                  }
                });
              }
              appendLeafRow({
                type: "function",
                id: def.id,
                label: def.label,
                kind: isFunctionInstantiable(def.id) ? "Configurable" : "Stock function",
                summary: def.description || "",
                depth: 2,
                variant: isFunctionInstantiable(def.id) ? "configurable-fn" : "stock-fn",
                functionId: def.id,
                keyboardClass: def.keyboardClass || "",
                actions: leafActions
              });
            }
          }
        }
      }
      if (showMacros) {
        const matchesQuery = (m) => !q || String(m?.label || "").toLowerCase().includes(q);
        const stockMacros = (this._st.stockMacros || []).filter((m) => m && m.id && matchesQuery(m));
        const userMacros = (this._st.macros || []).filter((m) => m && m.id && matchesQuery(m));
        const macroCount = userMacros.length + stockMacros.length;
        const newMacroBtn = document.createElement("button");
        newMacroBtn.type = "button";
        newMacroBtn.className = "kp-cfg-inspect";
        newMacroBtn.textContent = "+ New Macro";
        newMacroBtn.title = "Start an empty macro in the User Macros builder";
        newMacroBtn.addEventListener("click", (e) => {
          e.preventDefault();
          e.stopPropagation();
          this._startNewMacroDraft();
        }, true);
        if (appendGroupRow({
          groupKey: "macros",
          label: "Macros",
          depth: 0,
          count: macroCount,
          kind: "Group",
          trailing: newMacroBtn
        })) {
          if (appendGroupRow({
            groupKey: "macros:user",
            label: "User",
            depth: 1,
            count: userMacros.length,
            kind: "User macros"
          })) {
            if (!userMacros.length) {
              appendHintRow('None yet \u2014 click "+ New Macro", or Customize a stock macro.', 2);
            }
            for (const m of userMacros) {
              const stepCount = Array.isArray(m.steps) ? m.steps.length : 0;
              appendLeafRow({
                type: "macro",
                id: m.id,
                label: String(m.label || "Macro"),
                kind: "User macro",
                summary: `${stepCount} step${stepCount === 1 ? "" : "s"}`,
                depth: 2,
                variant: "user-macro",
                keyboardClass: "key-purple",
                actions: [{
                  label: "Edit steps",
                  title: `Edit steps for ${m.label || "Macro"}`,
                  onClick: () => this._openMacroStepsEditor(m)
                }]
              });
            }
          }
          if (stockMacros.length || !q) {
            if (appendGroupRow({
              groupKey: "macros:stock",
              label: "Stock",
              depth: 1,
              count: stockMacros.length,
              kind: "Stock macros"
            })) {
              if (!stockMacros.length) {
                appendHintRow("None match.", 2);
              }
              for (const m of stockMacros) {
                const stepCount = Array.isArray(m.steps) ? m.steps.length : 0;
                appendLeafRow({
                  type: "macro",
                  id: m.id,
                  label: String(m.label || "Macro"),
                  kind: "Stock macro",
                  summary: `${stepCount} step${stepCount === 1 ? "" : "s"}`,
                  depth: 2,
                  variant: "stock-macro",
                  keyboardClass: "key-purple",
                  actions: [{
                    label: "Customize",
                    title: `Open an editable copy of ${m.label || "Macro"}`,
                    onClick: () => this._openMacroStepsEditor(m)
                  }]
                });
              }
            }
          }
        }
      }
      const macroKeys = showMacroKeys && Array.isArray(this._st.macroKeys) ? this._st.macroKeys : [];
      const macroKeyItems = q ? macroKeys.filter((m) => {
        const hay = `${m?.label || ""} ${m?.kind || ""} ${summarizeMacroKey(m)}`.toLowerCase();
        return hay.includes(q);
      }) : macroKeys;
      if (showMacroKeys && (macroKeyItems.length || !q)) {
        if (appendGroupRow({
          groupKey: "macroKeys",
          label: "Configured Macro Keys",
          depth: 0,
          count: macroKeyItems.length,
          kind: "Group"
        })) {
          if (!macroKeyItems.length) {
            appendHintRow(
              this._macroBuilderEnabled() ? "Create one from User Macros \u2192 Macro Key, configure it, then Place it." : "Switch to Cards view to create a Macro Key, then place it.",
              1
            );
          }
          for (const m of macroKeyItems) {
            if (!m || !m.id) continue;
            appendLeafRow({
              type: "function",
              id: m.id,
              label: String(m.label || m.kind || "Macro Key"),
              kind: String(m.kind || "Macro Key"),
              summary: summarizeMacroKey(m),
              depth: 1,
              variant: "macro-key",
              iconKind: m.kind,
              functionId: FUNCTION_ID_BY_MACRO_KEY_KIND[m.kind] || "",
              keyboardClass: macroKeyKeyboardClass(m.kind),
              actions: [{
                label: "Edit",
                title: `Configure ${m.label || m.kind}`,
                onClick: () => this._openMacroKeyEditor(m)
              }]
            });
          }
        }
      }
      list.appendChild(hier.root);
    }
    /** @param {{ type: string, id: string }} item */
    _itemKey(item) {
      return `${item?.type || ""}:${item?.id || ""}`;
    }
    /**
     * Bare Function id for a library/inspector item (`functionId`, or the item id when it *is* a Function).
     * @param {{ type: string, id: string }} item
     * @returns {string}
     */
    _functionIdForItem(item) {
      if (!item || item.type !== "function") return "";
      const id = String(item.id || "");
      if (getFunctionDef(id)) return id;
      const inst = (this._st.actions || []).find((a) => a && a.id === id);
      return inst ? String(inst.functionId || "") : "";
    }
    /** @param {{ ctrl?: boolean, alt?: boolean, shift?: boolean, meta?: boolean }|null|undefined} mods */
    _hasAnyChordMod(mods) {
      return !!(mods && (mods.ctrl || mods.alt || mods.shift || mods.meta));
    }
    /**
     * @param {{ type: string, id: string }} item
     * @returns {string[]}
     */
    _findSlotKeysForItem(item) {
      const slots = this._getEditableSlotMap() || {};
      const wantedType = String(item?.type || "");
      const id = String(item?.id || "");
      const instanceIds = /* @__PURE__ */ new Set();
      if (wantedType === "function" && id && getFunctionDef(id)) {
        for (const action of this._st.actions || []) {
          if (action && action.functionId === id && action.id) instanceIds.add(String(action.id));
        }
      }
      const typeMatches = (type2) => {
        const t = String(type2 || "");
        if (wantedType === "function") return t === "function" || t === "action";
        return t === wantedType;
      };
      const out = [];
      for (const [key, val] of Object.entries(slots)) {
        if (!val || !typeMatches(val.type)) continue;
        const vid = String(val.id || "");
        if (vid === id || instanceIds.has(vid)) out.push(key);
      }
      return out;
    }
    /**
     * Current modifier set for an item: bound chord slot first, else pending inspector toggles.
     * @param {{ type: string, id: string }} item
     * @returns {{ ctrl: boolean, alt: boolean, shift: boolean, meta: boolean }}
     */
    _getChordModsForItem(item) {
      for (const slot of this._findSlotKeysForItem(item)) {
        const chord = parseChordSlotKey(slot);
        if (chord) {
          return {
            ctrl: !!chord.ctrl,
            alt: !!chord.alt,
            shift: !!chord.shift,
            meta: !!chord.meta
          };
        }
      }
      const pending = this._pendingChordMods?.[this._itemKey(item)];
      if (pending) {
        return {
          ctrl: !!pending.ctrl,
          alt: !!pending.alt,
          shift: !!pending.shift,
          meta: !!pending.meta
        };
      }
      return { ctrl: false, alt: false, shift: false, meta: false };
    }
    /**
     * @param {{ type: string, id: string }} item
     * @param {{ ctrl?: boolean, alt?: boolean, shift?: boolean, meta?: boolean }} mods
     */
    _setPendingChordMods(item, mods) {
      if (!this._pendingChordMods) this._pendingChordMods = /* @__PURE__ */ Object.create(null);
      this._pendingChordMods[this._itemKey(item)] = {
        ctrl: !!mods?.ctrl,
        alt: !!mods?.alt,
        shift: !!mods?.shift,
        meta: !!mods?.meta
      };
    }
    /**
     * @param {{ ctrl?: boolean, alt?: boolean, shift?: boolean, meta?: boolean }} mods
     * @param {string} [keyToken]
     */
    _formatModsPreview(mods, keyToken = "Key") {
      const slot = buildChordSlotKey({
        key: keyToken,
        ctrl: !!mods?.ctrl,
        alt: !!mods?.alt,
        shift: !!mods?.shift,
        meta: !!mods?.meta
      });
      return slot ? formatChordSlotKeyLabel(slot) : "";
    }
    /**
     * @param {{ type: string, id: string }} item
     * @param {HTMLElement} hintEl
     */
    _syncModifierHint(item, hintEl) {
      if (!hintEl) return;
      const slots = this._findSlotKeysForItem(item).filter((s) => isChordSlotKey(s));
      if (slots.length) {
        hintEl.textContent = `Bound as ${slots.map((s) => formatChordSlotKeyLabel(s)).join(", ")}`;
        return;
      }
      const preview = this._formatModsPreview(this._getChordModsForItem(item), "Key");
      hintEl.textContent = preview ? `Required while typing. Place on a key to bind as ${preview}.` : "Required while typing. Turn on at least one modifier, then Place on keyboard.";
    }
    /**
     * Inspector control: Ctrl / Alt / Shift / Win as pressed-state toggle buttons.
     * @param {{ type: string, id: string }} item
     * @param {import('../config/function-library.js').FunctionDef} def
     */
    _renderModifierToggles(item, def) {
      const wrap = document.createElement("div");
      wrap.style.cssText = "display:flex;flex-direction:column;gap:4px;";
      const label = document.createElement("div");
      label.className = "kp-mk-field-label";
      label.textContent = "Modifiers";
      wrap.appendChild(label);
      const mods = this._getChordModsForItem(item);
      const row = document.createElement("div");
      row.className = "kp-cfg-mod-toggles";
      row.setAttribute("role", "group");
      row.setAttribute("aria-label", "Modifier keys");
      const spec = [
        ["ctrl", "Ctrl"],
        ["alt", "Alt"],
        ["shift", "Shift"],
        ["meta", "Win"]
      ];
      const buttons = {};
      for (const [id, caption] of spec) {
        const btn = document.createElement("button");
        btn.type = "button";
        btn.className = "kp-cfg-btn";
        btn.textContent = caption;
        btn.setAttribute("aria-pressed", mods[id] ? "true" : "false");
        btn.title = mods[id] ? `${caption} on \u2014 click to turn off` : `${caption} off \u2014 click to turn on`;
        btn.addEventListener("click", async (e) => {
          e.preventDefault();
          e.stopPropagation();
          const next = { ...mods, [id]: !mods[id] };
          if (!this._hasAnyChordMod(next)) {
            this._notify("At least one modifier is required so this can run while typing.", "error");
            return;
          }
          const bound = this._findSlotKeysForItem(item).length > 0;
          const ok = await this._rewriteItemSlotsWithMods(item, next, def.id);
          if (!ok && bound) return;
          mods.ctrl = !!next.ctrl;
          mods.alt = !!next.alt;
          mods.shift = !!next.shift;
          mods.meta = !!next.meta;
          this._setPendingChordMods(item, mods);
          for (const [modId, caption2] of spec) {
            const b = buttons[modId];
            if (!b) continue;
            const on = !!mods[modId];
            b.setAttribute("aria-pressed", on ? "true" : "false");
            b.title = on ? `${caption2} on \u2014 click to turn off` : `${caption2} off \u2014 click to turn on`;
          }
          this._syncModifierHint(item, hint);
          try {
            this._renderRightList();
          } catch {
          }
        }, true);
        buttons[id] = btn;
        row.appendChild(btn);
      }
      wrap.appendChild(row);
      const hint = document.createElement("div");
      hint.className = "kp-cfg-mod-hint";
      this._syncModifierHint(item, hint);
      wrap.appendChild(hint);
      return wrap;
    }
    /**
     * When placing a `worksWhileTyping` Function, turn a bare Reference key into a chord slot.
     * @param {{ type: string, id: string }} item
     * @param {string} slotLabel
     * @returns {string}
     */
    _resolvePlaceSlotKey(item, slotLabel) {
      const slot = String(slotLabel || "").trim();
      if (!slot) return "";
      if (isChordSlotKey(slot)) return slot;
      const functionId = this._functionIdForItem(item);
      if (!functionWorksWhileTyping(functionId)) return slot;
      const mods = this._getChordModsForItem(item);
      if (!this._hasAnyChordMod(mods)) {
        this._notify("Turn on at least one modifier in the Inspector, then place the key.", "error");
        return "";
      }
      const chord = buildChordSlotKey({
        key: slot,
        ctrl: mods.ctrl,
        alt: mods.alt,
        shift: mods.shift,
        meta: mods.meta
      });
      if (!chord) {
        this._notify("Turn on at least one modifier in the Inspector, then place the key.", "error");
        return "";
      }
      const check = validateFunctionSlotKey(functionId, chord);
      if (!check.ok) {
        this._notify(check.reason, "error");
        return "";
      }
      return chord;
    }
    /**
     * Rewrite existing layout slots for `item` to use `mods`. No-op (success) when nothing is bound yet.
     * @param {{ type: string, id: string }} item
     * @param {{ ctrl?: boolean, alt?: boolean, shift?: boolean, meta?: boolean }} mods
     * @param {string} functionId
     * @returns {Promise<boolean>}
     */
    async _rewriteItemSlotsWithMods(item, mods, functionId) {
      if (!this._hasAnyChordMod(mods)) {
        this._notify("At least one modifier is required so this can run while typing.", "error");
        return false;
      }
      this._setPendingChordMods(item, mods);
      const layoutId = this._st.userLayout?.id;
      const oldKeys = this._findSlotKeysForItem(item);
      if (!layoutId || !oldKeys.length) return true;
      const moves = [];
      for (const oldKey of oldKeys) {
        const parsed = parseChordSlotKey(oldKey);
        const keyToken = parsed?.key || String(oldKey);
        const nextKey = buildChordSlotKey({
          key: keyToken,
          ctrl: !!mods.ctrl,
          alt: !!mods.alt,
          shift: !!mods.shift,
          meta: !!mods.meta
        });
        if (!nextKey) {
          this._notify("At least one modifier is required so this can run while typing.", "error");
          return false;
        }
        const check = validateFunctionSlotKey(functionId, nextKey);
        if (!check.ok) {
          this._notify(check.reason, "error");
          return false;
        }
        if (nextKey !== oldKey) moves.push({ oldKey, nextKey });
      }
      if (!moves.length) return true;
      const slots = this._st.userLayout?.slots && typeof this._st.userLayout.slots === "object" ? this._st.userLayout.slots : {};
      for (const { nextKey } of moves) {
        const occupant = slots[nextKey];
        if (occupant && !(occupant.type === item.type && String(occupant.id) === String(item.id))) {
          this._notify(`Already bound: ${formatChordSlotKeyLabel(nextKey)}.`, "error");
          return false;
        }
      }
      try {
        for (const { oldKey, nextKey } of moves) {
          const assigned = await setUserKeyboardLayoutSlot(layoutId, nextKey, {
            type: item.type,
            id: item.id
          });
          if (!assigned.ok) {
            this._notify(assigned.reason || "Could not bind key.", "error");
            return false;
          }
          this._st.userLayout = assigned.layout;
          const cleared = await setUserKeyboardLayoutSlot(layoutId, oldKey, null);
          if (cleared.ok) this._st.userLayout = cleared.layout;
        }
        this.syncUserLayout(this._st.userLayout);
        this._emitChange();
        try {
          await this._kp?.applyLiveUserLayout?.(this._st.userLayout, {
            macros: this._st.macros,
            actions: this._st.actions
          });
        } catch {
        }
        return true;
      } catch {
        this._notify("Failed to update modifiers.", "error");
        return false;
      }
    }
    /**
     * A "Needs modifier" badge + "Bind modifier combo" button, appended to a placeable item for any
     * `worksWhileTyping` Function/instance — see {@link _captureAndAssignChord}.
     * @param {{ type: string, id: string }} item
     * @param {import('../config/function-library.js').FunctionDef} def
     */
    _renderBindChordButton(item, def) {
      const row = document.createElement("div");
      row.className = "kp-cfg-btn-row";
      row.style.flexBasis = "100%";
      const badge = document.createElement("span");
      badge.className = "kp-cfg-badge";
      badge.textContent = "Needs modifier";
      badge.title = "Must be bound to a modifier-key combination so it can run while a text field is focused.";
      row.appendChild(badge);
      const bindBtn = document.createElement("button");
      bindBtn.type = "button";
      bindBtn.className = "kp-cfg-btn";
      bindBtn.style.padding = "3px 6px";
      setCfgBtnLabel(bindBtn, "Bind modifier combo", "kp-cfg-i-chord");
      bindBtn.dataset.kpBindIcon = "kp-cfg-i-chord";
      bindBtn.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        this._captureAndAssignChord(item, def.id, bindBtn);
      }, true);
      bindBtn.addEventListener("pointerdown", (e) => e.stopPropagation(), true);
      row.appendChild(bindBtn);
      return row;
    }
    _familyBaseLabel() {
      return this._familyBaseLabelFor(this._st.builtinLayoutId);
    }
    /**
     * Short family name for copy labels ("Browsing"), not the "Built-in: …" picker label.
     * @param {string} [builtinLayoutId]
     */
    _familyBaseLabelFor(builtinLayoutId) {
      try {
        const inferred = inferFamilyAndHandednessFromLayoutId(builtinLayoutId || this._st.builtinLayoutId);
        const familyId = normalizeKeyboardLayoutFamilyId(inferred.familyId || "browsing");
        const meta = (BUILTIN_KEYBOARD_LAYOUT_FAMILIES_META || []).find((m) => m && m.id === familyId);
        return String(meta?.label || familyId || "Layout");
      } catch {
        return "Layout";
      }
    }
    /**
     * @param {{ type: string, id: string }} item
     * @param {HTMLElement} sourceEl
     */
    _beginPlaceMode(item, sourceEl) {
      if (!item || !item.type || !item.id) return;
      if (this._placeItem && this._placeItem.type === item.type && this._placeItem.id === item.id) {
        this._cancelPlaceMode();
        return;
      }
      const functionId = this._functionIdForItem(item);
      if (functionWorksWhileTyping(functionId) && !this._hasAnyChordMod(this._getChordModsForItem(item))) {
        const def = getFunctionDef(functionId);
        this._notify(
          `"${def?.label || "This action"}" must use a modifier so it can run while typing. Turn one on in the Inspector.`,
          "error"
        );
        if (!this._inspectorSelection || this._inspectorSelection.type !== item.type || this._inspectorSelection.id !== item.id) {
          this._inspectItem(item);
        }
        return;
      }
      if (functionId && !functionAssignableToKey(functionId)) {
        const def = getFunctionDef(functionId);
        this._notify(
          `"${def?.label || "This action"}" is a Macro Step, not a key action.`,
          "error"
        );
        return;
      }
      this._cancelPlaceMode();
      this._placeItem = { type: String(item.type), id: String(item.id) };
      this._placeSourceEl = sourceEl || null;
      this._ensurePlaceArrow();
      this._bindPlacePointer();
      this._renderRightList();
      const placeHint = functionWorksWhileTyping(functionId) ? `Place mode \u2014 click a Keyboard Reference key (binds as ${this._formatModsPreview(this._getChordModsForItem(item), "Key")}; Esc to cancel).` : "Place mode \u2014 click a Keyboard Reference key (Esc to cancel).";
      this._setPlaceHint(placeHint);
      this._syncPlacePulse();
      try {
        this._kp?.floatingKeyboardHelp?.setPlaceTargeting?.({
          item: this._placeItem,
          onPlace: (slot) => {
            void this._placeOnSlot(slot);
          }
        });
      } catch {
      }
      try {
        const r = sourceEl?.getBoundingClientRect?.();
        if (r) this._updatePlaceArrow(r.left + r.width / 2, r.top + r.height / 2, r.left + r.width / 2 + 40, r.top + r.height / 2);
      } catch {
      }
    }
    isPlaceModeActive() {
      return !!(this._placeItem && this._placeItem.type && this._placeItem.id);
    }
    /** Public alias for Escape / outside-click cancel. */
    cancelPlaceMode() {
      this._cancelPlaceMode();
    }
    _cancelPlaceMode() {
      if (!this._placeItem && !this._placeArrowEl && !this._placePointerBound) return;
      this._placeItem = null;
      this._placeSourceEl = null;
      this._unbindPlacePointer();
      this._teardownPlaceArrow();
      try {
        this._kp?.floatingKeyboardHelp?.clearPlaceTargeting?.();
      } catch {
      }
      try {
        this._renderRightList();
      } catch {
      }
      this._setPlaceHint("Add Logic steps or Functions. Save commits the macro to Macros.");
      this._syncPlacePulse();
    }
    _syncPlacePulse() {
      const on = this.isPlaceModeActive();
      if (this._placePulseEl) this._placePulseEl.hidden = !on;
      if (this._titlebarPlaceMsgEl) this._titlebarPlaceMsgEl.hidden = !on;
      try {
        if (this.root) {
          if (on) this.root.setAttribute("data-kp-place-targeting", "true");
          else this.root.removeAttribute("data-kp-place-targeting");
        }
      } catch {
      }
    }
    /** @param {string} text */
    _setPlaceHint(text) {
      if (this._placeHintEl) this._placeHintEl.textContent = String(text || "");
    }
    /**
     * Lightweight cycle warning for nested Run Macro steps in the current draft.
     * @param {string|null|undefined} selfId
     * @param {any[]} steps
     * @returns {string}
     */
    _cycleWarningFor(selfId, steps) {
      const self = String(selfId || "");
      const list = Array.isArray(steps) ? steps : [];
      for (const step of list) {
        if ((step?.kind || "") !== "runMacro") continue;
        const nested = String(step.macroId || "");
        if (!nested) continue;
        if (self && nested === self) return "This macro calls itself \u2014 that will be blocked at runtime.";
        const nestedMacro = this._findMacroById(nested);
        const nestedSteps = nestedMacro?.steps || [];
        if (self && nestedSteps.some((s) => s?.kind === "runMacro" && String(s.macroId || "") === self)) {
          return `Cycle risk with \u201C${nestedMacro?.label || nested}\u201D \u2014 runtime will stop nested loops.`;
        }
      }
      return "";
    }
    _bindPlacePointer() {
      if (this._placePointerBound) return;
      try {
        document.addEventListener("pointermove", this._onPlacePointerMove, true);
        document.addEventListener("pointerdown", this._onPlacePointerDown, true);
        document.addEventListener("click", this._onPlaceClick, true);
        document.addEventListener("keydown", this._onPlaceKeyDown, true);
        this._placePointerBound = true;
      } catch {
      }
    }
    _unbindPlacePointer() {
      if (!this._placePointerBound) return;
      try {
        document.removeEventListener("pointermove", this._onPlacePointerMove, true);
      } catch {
      }
      try {
        document.removeEventListener("pointerdown", this._onPlacePointerDown, true);
      } catch {
      }
      try {
        document.removeEventListener("click", this._onPlaceClick, true);
      } catch {
      }
      try {
        document.removeEventListener("keydown", this._onPlaceKeyDown, true);
      } catch {
      }
      this._placePointerBound = false;
    }
    /** Keyboard Reference slot that should receive the place. */
    _isKeyboardRefSlotEvent(e) {
      try {
        const slot = getComposedEventElement(e, "[data-kp-slot]");
        return !!closestComposed(slot, ".kp-floating-keyboard-help");
      } catch {
        return false;
      }
    }
    /** Palette keycap — click toggles/switches source via `_beginPlaceMode`. */
    _isPlaceSourceKeyEvent(e) {
      try {
        const source = getComposedEventElement(e, ".key[data-kp-item-type][data-kp-item-id]");
        return !!(source && source.getRootNode?.() === this.shadowRoot);
      } catch {
        return false;
      }
    }
    _onPlacePointerMove(e) {
      if (!this._placeItem) return;
      try {
        let source = this._placeSourceEl;
        if (!source || !source.isConnected) {
          source = this._listEl?.querySelector?.(
            `.key[data-kp-item-type="${this._placeItem.type}"][data-kp-item-id="${CSS.escape?.(this._placeItem.id) || this._placeItem.id}"]`
          ) || null;
          this._placeSourceEl = source;
        }
        if (!source) return;
        const r = source.getBoundingClientRect();
        const x0 = r.left + r.width / 2;
        const y0 = r.top + r.height / 2;
        this._updatePlaceArrow(x0, y0, e.clientX, e.clientY);
      } catch {
      }
    }
    /**
     * Cancel place mode on pointerdown outside Config / Keyboard Reference.
     * Inside Config, `_onPlaceClick` cancels (covers mouse and Click Element).
     * @param {PointerEvent} e
     */
    _onPlacePointerDown(e) {
      if (!this._placeItem) return;
      if (typeof e.button === "number" && e.button !== 0) return;
      try {
        if (this._isKeyboardRefSlotEvent(e)) return;
        if (this._isPlaceSourceKeyEvent(e)) return;
        if (getComposedEventElement(e, ".kp-layout-config-panel")) return;
        e.preventDefault();
        e.stopPropagation();
        try {
          e.stopImmediatePropagation();
        } catch {
        }
        this._cancelPlaceMode();
      } catch {
        this._cancelPlaceMode();
      }
    }
    /**
     * Click inside Keyboard Layout Editor (mouse or Click Element) ends place mode,
     * except palette keycaps (toggle/switch) and Keyboard Reference slots (place).
     * Deferred so the originating click can still inspect / activate controls.
     * @param {MouseEvent} e
     */
    _onPlaceClick(e) {
      if (!this._placeItem) return;
      try {
        if (this._isKeyboardRefSlotEvent(e)) return;
        if (this._isPlaceSourceKeyEvent(e)) return;
        if (!getComposedEventElement(e, ".kp-layout-config-panel")) return;
        queueMicrotask(() => {
          try {
            this._cancelPlaceMode();
          } catch {
          }
        });
      } catch {
        this._cancelPlaceMode();
      }
    }
    _onPlaceKeyDown(e) {
      if (!this._placeItem) return;
      const isEsc = e.key === "Escape" || e.key === "Esc" || e.code === "Escape";
      if (!isEsc) return;
      e.preventDefault();
      e.stopPropagation();
      try {
        e.stopImmediatePropagation();
      } catch {
      }
      this._cancelPlaceMode();
    }
    _ensurePlaceArrow() {
      const doc = document;
      if (this._placeArrowEl && this._placeArrowEl.isConnected) return;
      const el2 = doc.createElement("div");
      el2.className = "kp-layout-place-arrow";
      el2.setAttribute("data-kp-layout-place-arrow", "true");
      el2.setAttribute("aria-hidden", "true");
      el2.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
  <defs>
    <marker id="kp-place-arrowhead" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
      <path d="M0,0 L0,6 L9,3 z" fill="rgba(74,144,200,0.95)" />
    </marker>
  </defs>
  <line x1="0" y1="0" x2="0" y2="0" stroke="rgba(74,144,200,0.9)" stroke-width="2.5"
    marker-end="url(#kp-place-arrowhead)" stroke-linecap="round" />
</svg>`.trim();
      try {
        if (typeof HTMLElement !== "undefined" && "popover" in HTMLElement.prototype) {
          el2.popover = "manual";
        }
      } catch {
      }
      (doc.body || doc.documentElement).appendChild(el2);
      this._placeArrowEl = el2;
      try {
        if (typeof el2.showPopover === "function") el2.showPopover();
      } catch {
      }
    }
    _teardownPlaceArrow() {
      const el2 = this._placeArrowEl;
      this._placeArrowEl = null;
      if (!el2) return;
      try {
        if (typeof el2.hidePopover === "function" && el2.matches?.(":popover-open")) el2.hidePopover();
      } catch {
      }
      try {
        el2.remove();
      } catch {
      }
    }
    _updatePlaceArrow(x0, y0, x1, y1) {
      const el2 = this._placeArrowEl;
      if (!el2) return;
      const line = el2.querySelector("line");
      if (!line) return;
      try {
        line.setAttribute("x1", String(x0));
        line.setAttribute("y1", String(y0));
        line.setAttribute("x2", String(x1));
        line.setAttribute("y2", String(y1));
      } catch {
      }
    }
    /**
     * Ensure a user layout is active (auto-dup from built-in if needed), then assign.
     * @param {string} slotLabel
     */
    async _placeOnSlot(slotLabel) {
      let slot = String(slotLabel || "").trim().toUpperCase();
      if (this._placeItem && slot) {
        slot = this._resolvePlaceSlotKey(this._placeItem, slot);
        if (!slot) return;
        await this._assignSlotKey(slot, this._placeItem);
      }
      this._cancelPlaceMode();
    }
    /**
     * Ensure a user layout is active (auto-dup from built-in on first edit).
     * @returns {Promise<{ layout: any, becameCurrent: boolean }|null>}
     */
    async _ensureEditableUserLayout() {
      let becameCurrent = false;
      if (this._st.mode !== "user" || !this._st.userLayout) {
        const base = this._familyBaseLabel();
        const layouts = await listUserKeyboardLayouts();
        const label = nextUserCopyLayoutLabel(base, layouts);
        const created = await duplicateBuiltinLayoutToUserLayout({
          builtinLayoutId: this._st.builtinLayoutId,
          label
        });
        this._st.userLayouts = await listUserKeyboardLayouts();
        this._st.mode = "user";
        this._st.userLayoutId = created.id;
        this._st.userLayout = created;
        this._renderLayoutSelect();
        becameCurrent = true;
      }
      if (!this._st.userLayout || !this._st.userLayout.id) return null;
      return { layout: this._st.userLayout, becameCurrent };
    }
    /**
     * Apply a slot write to live state, Keyboard Reference, and the Inspector.
     * @param {any} layout
     * @param {boolean} becameCurrent
     */
    async _afterSlotWrite(layout, becameCurrent) {
      this._st.userLayout = layout;
      this.syncUserLayout(this._st.userLayout);
      this._emitChange();
      try {
        await this._kp?.applyLiveUserLayout?.(this._st.userLayout, {
          setAsCurrent: becameCurrent || String(this._kp?._currentKeyboardLayoutId || "") === `user:${this._st.userLayout.id}`,
          macros: this._st.macros,
          actions: this._st.actions
        });
      } catch {
      }
    }
    /**
     * Ensure a user layout is active (auto-dup from built-in if needed), then assign `item` to
     * `slotKey` — a bare key label ("Q") or a modifier-chord slot key ("CHORD:CTRL+ALT+Q", see
     * utils/key-chord.js). Goes through {@link setUserKeyboardLayoutSlot} so the
     * `worksWhileTyping` chord-vs-bare-key rule is enforced here exactly like everywhere else that
     * writes a slot, instead of mutating `layout.slots` directly.
     * @param {string} slotKey
     * @param {{ type: string, id: string }} item
     */
    async _assignSlotKey(slotKey, item) {
      const slot = String(slotKey || "").trim();
      if (!item || !item.type || !item.id || !slot) return;
      try {
        const ensured = await this._ensureEditableUserLayout();
        if (!ensured) return;
        const result = await setUserKeyboardLayoutSlot(ensured.layout.id, slot, { type: item.type, id: item.id });
        if (!result.ok) {
          this._notify(result.reason || "Could not bind key.", "error");
          return;
        }
        await this._afterSlotWrite(result.layout, ensured.becameCurrent);
        try {
          this._renderInspector();
        } catch {
        }
      } catch {
        this._notify("Failed to bind key.", "error");
      }
    }
    /**
     * Clear one keyboard slot. Auto-duplicates a built-in layout first, same as placing.
     * @param {string} slotKey
     */
    async _clearSlotKey(slotKey) {
      const slot = String(slotKey || "").trim();
      if (!slot) return;
      try {
        const ensured = await this._ensureEditableUserLayout();
        if (!ensured) return;
        const result = await setUserKeyboardLayoutSlot(ensured.layout.id, slot, null);
        if (!result.ok) {
          this._notify(result.reason || "Could not remove key.", "error");
          return;
        }
        await this._afterSlotWrite(result.layout, ensured.becameCurrent);
        try {
          this._renderInspector();
        } catch {
        }
        this._notify(`Removed ${formatChordSlotKeyLabel(slot)}.`, "success");
      } catch {
        this._notify("Failed to remove key.", "error");
      }
    }
    _stopChordCapture() {
      if (this._chordCaptureCleanup) {
        try {
          this._chordCaptureCleanup();
        } catch {
        }
        this._chordCaptureCleanup = null;
      }
      try {
        for (const btn of this.shadowRoot?.querySelectorAll?.('[data-capturing="true"]') || []) {
          delete btn.dataset.capturing;
          if (btn.dataset.kpBindLabel) {
            setCfgBtnLabel(btn, btn.dataset.kpBindLabel, btn.dataset.kpBindIcon || "");
          }
        }
      } catch {
      }
    }
    /**
     * Listen for the next keydown and, if it has a modifier held, bind `item` to the resulting
     * chord slot key — used for `worksWhileTyping` Functions that must run while a text field is
     * focused. The Keyboard Reference's click-to-place flow only ever targets bare physical keys.
     * See utils/key-chord.js.
     * @param {{ type: string, id: string }} item
     * @param {string} functionId Used only for the `validateFunctionSlotKey` check.
     * @param {HTMLButtonElement} btn
     */
    _captureAndAssignChord(item, functionId, btn) {
      if (!item || !btn) return;
      this._cancelPlaceMode();
      this._stopChordCapture();
      btn.dataset.kpBindLabel = "Bind modifier combo";
      btn.dataset.kpBindIcon = btn.dataset.kpBindIcon || "kp-cfg-i-chord";
      btn.dataset.capturing = "true";
      setCfgBtnLabel(btn, "Press keys\u2026 (Esc)");
      const onKeyDown = async (ev) => {
        ev.preventDefault();
        ev.stopPropagation();
        try {
          ev.stopImmediatePropagation();
        } catch {
        }
        this._stopChordCapture();
        if (ev.key === "Escape") return;
        const hasMods = !!(ev.ctrlKey || ev.altKey || ev.shiftKey || ev.metaKey);
        if (!hasMods) {
          this._notify("Hold a modifier key (Ctrl/Alt/Shift) while pressing the key.", "error");
          return;
        }
        const slotKey = buildChordSlotKey({
          key: ev.key,
          ctrl: ev.ctrlKey,
          alt: ev.altKey,
          shift: ev.shiftKey,
          meta: ev.metaKey
        });
        if (!slotKey) return;
        const check = validateFunctionSlotKey(functionId, slotKey);
        if (!check.ok) {
          this._notify(check.reason, "error");
          return;
        }
        this._setPendingChordMods(item, {
          ctrl: !!ev.ctrlKey,
          alt: !!ev.altKey,
          shift: !!ev.shiftKey,
          meta: !!ev.metaKey
        });
        await this._assignSlotKey(slotKey, item);
        this._notify(`Bound to ${formatChordSlotKeyLabel(slotKey)}.`, "success");
        if (this._inspectorSelection && this._inspectorSelection.type === item.type && this._inspectorSelection.id === item.id) {
          try {
            this._renderInspector();
          } catch {
          }
        }
      };
      document.addEventListener("keydown", onKeyDown, { capture: true, once: true });
      this._chordCaptureCleanup = () => {
        try {
          document.removeEventListener("keydown", onKeyDown, { capture: true });
        } catch {
        }
      };
    }
  };

  // src/ui/keyboard-layout-configurator.js
  var _configPanel = null;
  function isKeyboardLayoutEditMode(kp) {
    try {
      return !!(kp?.floatingKeyboardHelp && typeof kp.floatingKeyboardHelp.isEditMode === "function" && kp.floatingKeyboardHelp.isEditMode());
    } catch {
      return false;
    }
  }
  function exitKeyboardLayoutEditMode(kp) {
    try {
      if (_configPanel) _configPanel.hide();
    } catch {
    }
    try {
      kp?.floatingKeyboardHelp?.setEditMode?.(false);
    } catch {
    }
    try {
      void kp?._refreshCurrentKeyboardLayoutFromSettings?.();
    } catch {
    }
  }
  function openKeyboardLayoutConfigurator(kp, opts = {}) {
    if (!kp) return;
    const createNew = !!opts.createNew;
    const createDuplicate = !!opts.createDuplicate;
    try {
      if (isKeyboardLayoutEditMode(kp)) {
        try {
          void _configPanel?.show?.(kp, { duplicateBuiltin: false }).then(async () => {
            if (createNew) {
              try {
                await _configPanel?.createNewLayout?.();
              } catch {
              }
            } else if (createDuplicate) {
              try {
                await _configPanel?.duplicateLayout?.();
              } catch {
              }
            }
          });
        } catch {
        }
        return;
      }
      try {
        if (typeof kp.applyKeyboardHelpVisibility === "function") {
          kp.applyKeyboardHelpVisibility(true, { persist: false });
        } else {
          kp.floatingKeyboardHelp?.show?.();
        }
      } catch {
      }
      try {
        unpinKeyPopover();
      } catch {
      }
      if (!_configPanel) {
        _configPanel = new KeyboardLayoutConfigPanel({
          onChange: ({ state }) => {
            try {
              kp.floatingKeyboardHelp?.setEditLayout?.(state);
            } catch {
            }
          },
          onClose: () => {
            try {
              kp.floatingKeyboardHelp?.setEditMode?.(false);
            } catch {
            }
            try {
              void kp?._refreshCurrentKeyboardLayoutFromSettings?.();
            } catch {
            }
          }
        });
      }
      const help = kp.floatingKeyboardHelp;
      if (help && typeof help.setEditMode === "function") {
        help.setEditMode(true, {
          getConfigPanel: () => _configPanel,
          onLayoutPersisted: (layout) => {
            try {
              _configPanel?.syncUserLayout?.(layout);
            } catch {
            }
            try {
              const st = _configPanel?.getState?.();
              void kp?.applyLiveUserLayout?.(layout, {
                macros: st?.macros,
                actions: st?.actions
              });
            } catch {
            }
          }
        });
      }
      void _configPanel.show(kp, {
        duplicateBuiltin: !createNew && !createDuplicate
      }).then(async () => {
        try {
          kp.floatingKeyboardHelp?.setEditLayout?.(_configPanel.getState());
        } catch {
        }
        if (createNew) {
          try {
            await _configPanel.createNewLayout();
          } catch {
          }
        } else if (createDuplicate) {
          try {
            await _configPanel.duplicateLayout();
          } catch {
          }
        }
      });
    } catch {
    }
  }
  function toggleKeyboardLayoutConfigurator(kp) {
    if (!kp) return;
    if (isKeyboardLayoutEditMode(kp)) {
      exitKeyboardLayoutEditMode(kp);
      return;
    }
    openKeyboardLayoutConfigurator(kp);
  }

  // src/ui/floating-keyboard-help.js
  var KEYBOARD_POSITION_MARGIN_PX = Math.max(PANEL_POSITION_MARGIN_PX, 16);
  var KEYBOARD_MAX_VIEWPORT_INSET_PX = KEYBOARD_POSITION_MARGIN_PX * 2;
  var LAYOUT_SELECT_EDIT_VALUE = "__edit_layouts__";
  var LAYOUT_SELECT_NEW_VALUE = "__new_layout__";
  var LAYOUT_SELECT_DUP_VALUE = "__duplicate_layout__";
  var LAYOUT_SELECT_ONBOARDING_VALUE = "__onboarding_tutorial__";
  var LAYOUT_SELECT_DOCS_VALUE = "__docs_help__";
  var LAYOUT_SELECT_SETTINGS_VALUE = "__settings__";
  var TEXT_MODE_COUNTDOWN_ACTION_IDS = /* @__PURE__ */ new Set(["ACTIVATE"]);
  function paintFloatingKeyboardRootVisible(root, visible) {
    if (!root) return;
    const show = !!visible;
    try {
      root.hidden = !show;
    } catch {
    }
    try {
      if (show) root.classList.remove("kpv2-hidden");
      else root.classList.add("kpv2-hidden");
    } catch {
    }
    try {
      root.style.setProperty("display", show ? "flex" : "none", "important");
      root.style.setProperty("pointer-events", show ? "auto" : "none", "important");
      root.style.setProperty("visibility", show ? "visible" : "hidden", "important");
    } catch {
      try {
        root.style.display = show ? "flex" : "none";
        root.style.pointerEvents = show ? "auto" : "none";
        root.style.visibility = show ? "visible" : "hidden";
      } catch {
      }
    }
    try {
      root.setAttribute("aria-hidden", show ? "false" : "true");
    } catch {
    }
  }
  function isFloatingKeyboardRootPaintedVisible(root) {
    if (!root || !root.isConnected) return false;
    if (root.hidden) return false;
    try {
      if (root.getAttribute("aria-hidden") === "true") return false;
    } catch {
    }
    try {
      if (root.classList?.contains("kpv2-hidden")) return false;
    } catch {
    }
    try {
      if (root.style && root.style.display === "none") return false;
    } catch {
    }
    try {
      if (root.style && root.style.visibility === "hidden") return false;
    } catch {
    }
    return true;
  }
  var FloatingKeyboardHelp = class {
    /**
     * @param {Object} params
     * @param {Record<string, any>} params.keybindings
     * @param {any[]} [params.keyboardLayout]
     * @param {string} [params.layoutId]
     * @param {import('../modules/settings-manager.js').PanelPositionSettings|null} [params.panelPosition]
     *   Optional known dock/free position (from KeyPilot settings). When provided,
     *   the first show paints at this location instead of flashing the default corner.
     * @param {() => any} [params.getKeyPilot] Accessor for the owning KeyPilot instance
     *   (used by "Edit Keyboard Layout…" in the layout dropdown).
     */
    constructor({ keybindings, keyboardLayout, layoutId, panelPosition, getKeyPilot } = {}) {
      this.keybindings = keybindings || {};
      this.keyboardLayout = keyboardLayout || null;
      this.layoutId = typeof layoutId === "string" ? layoutId : "";
      this.root = null;
      this.shadowRoot = null;
      this.keyboardContainer = null;
      this._keyboardBody = null;
      this.closeBtn = null;
      this.hintEl = null;
      this._shortcutEl = null;
      this._titlebar = null;
      this._exitTextModeBtn = null;
      this._closeEditorBtn = null;
      this._collapseBtn = null;
      this._collapsed = false;
      this._collapseEpoch = 0;
      this._onCloseClick = this._onCloseClick.bind(this);
      this._onCollapseClick = this._onCollapseClick.bind(this);
      this._onExitTextModeClick = this._onExitTextModeClick.bind(this);
      this._onCloseEditorClick = this._onCloseEditorClick.bind(this);
      this._onLayoutSelectChange = this._onLayoutSelectChange.bind(this);
      this._onHandednessChipClick = this._onHandednessChipClick.bind(this);
      this._layoutSelectApi = null;
      this._getKeyPilot = typeof getKeyPilot === "function" ? getKeyPilot : null;
      this._pressedLabels = /* @__PURE__ */ new Set();
      this._keyElsByLabel = /* @__PURE__ */ new Map();
      this._keyElsByActionId = /* @__PURE__ */ new Map();
      this._keydownBound = false;
      this._onDocKeyDown = this._onDocKeyDown.bind(this);
      this._onDocKeyUp = this._onDocKeyUp.bind(this);
      this._onWinBlur = this._onWinBlur.bind(this);
      this._linkHoverHintActive = false;
      this._linkHoverHintActionIds = /* @__PURE__ */ new Set();
      this._textModeFilterActive = false;
      this._textModeCountdownArmed = false;
      this._textModeCountdownActionIds = new Set(TEXT_MODE_COUNTDOWN_ACTION_IDS);
      this._keyFeedbackEnabled = true;
      this._settingsBound = false;
      this._onStorageChanged = this._onStorageChanged.bind(this);
      this._windowChromeBound = false;
      this._resizeDispose = null;
      this._dragDispose = null;
      this._slotDnDAbort = null;
      this._hostGuard = null;
      this._panelPosition = {
        ...DEFAULT_SETTINGS.panelPositions.keyboardReference
      };
      this._positionHydrated = false;
      this._showGeneration = 0;
      this._earlyAdoptSignaled = false;
      this._reuseEarlyCustomPaint = false;
      this._positionApplyScheduled = false;
      this._onWinResizePosition = this._onWinResizePosition.bind(this);
      this._suppressPositionPersist = false;
      this._layoutSelectEl = null;
      this._handednessChip = null;
      this._layoutTitleEl = null;
      this._currentKeyboardLayoutId = "builtin";
      this._currentUserLayout = null;
      this._currentUserMacros = [];
      this._currentUserActions = [];
      this._renderToken = 0;
      this._editMode = false;
      this._getConfigPanel = null;
      this._onLayoutPersisted = null;
      this._editLayoutState = null;
      this._placeHoverSlot = null;
      this._placeItem = null;
      this._onPlaceSlot = null;
      if (panelPosition && typeof panelPosition === "object") {
        this._seedPanelPosition(panelPosition, { hydrated: true });
      }
    }
    /**
     * Update in-memory dock/free position (does not paint unless root exists).
     * @param {import('../modules/settings-manager.js').PanelPositionSettings|null|undefined} next
     * @param {{ hydrated?: boolean }} [opts]
     */
    _seedPanelPosition(next, opts = {}) {
      const normalized = normalizePanelPositionState(
        next,
        DEFAULT_SETTINGS.panelPositions.keyboardReference
      ) || { ...DEFAULT_SETTINGS.panelPositions.keyboardReference };
      this._panelPosition = {
        left: normalized.left,
        top: normalized.top,
        anchor: normalized.anchor === void 0 ? null : normalized.anchor
      };
      if (opts.hydrated) this._positionHydrated = true;
    }
    /**
     * Public: seed position from already-loaded KeyPilot settings before show().
     * @param {import('../modules/settings-manager.js').PanelPositionSettings|null|undefined} next
     */
    setPanelPositionFromSettings(next) {
      if (!next || typeof next !== "object") return;
      this._seedPanelPosition(next, { hydrated: true });
      if (this.root) this._applyPanelPositionNow();
    }
    /**
     * Read left/top (and optional anchor attr) already painted on a shell element.
     * @param {HTMLElement|null} el
     * @returns {import('../modules/settings-manager.js').PanelPositionSettings|null}
     */
    _readPositionFromDom(el2) {
      if (!el2 || !el2.style) return null;
      try {
        const left = parseFloat(el2.style.left);
        const top = parseFloat(el2.style.top);
        if (!Number.isFinite(left) || !Number.isFinite(top)) return null;
        let anchor = null;
        try {
          const attr = el2.getAttribute("data-kp-panel-anchor");
          if (attr) anchor = attr;
        } catch {
        }
        return { left, top, anchor: anchor || null };
      } catch {
        return null;
      }
    }
    setKeybindings(keybindings) {
      this.keybindings = keybindings || {};
      if (this.root && !this.root.hidden) {
        this._render();
      }
    }
    setKeyboardLayout({ keyboardLayout, layoutId } = {}) {
      this.keyboardLayout = keyboardLayout || null;
      this.layoutId = typeof layoutId === "string" ? layoutId : "";
      if (this.root && !this.root.hidden) {
        this._render();
      }
    }
    /**
     * Apply keybindings + visual layout together so a handedness change cannot
     * paint the new key positions with leftover mappings (or the reverse).
     * @param {{ keybindings?: Record<string, any>, keyboardLayout?: any[], layoutId?: string }} params
     */
    setLayoutChrome({ keybindings, keyboardLayout, layoutId } = {}) {
      if (keybindings) this.keybindings = keybindings;
      if (keyboardLayout) this.keyboardLayout = keyboardLayout;
      if (typeof layoutId === "string") this.layoutId = layoutId;
      if (this.root && !this.root.hidden) {
        this._render();
      }
    }
    /**
     * @param {object} params
     * @param {string} params.currentKeyboardLayoutId
     * @param {any|null} [params.userLayout]
     * @param {any[]} [params.userMacros]
     */
    setActiveLayoutSelection({ currentKeyboardLayoutId, userLayout, userMacros, userActions } = {}) {
      this._currentKeyboardLayoutId = typeof currentKeyboardLayoutId === "string" ? currentKeyboardLayoutId : "builtin";
      this._currentUserLayout = userLayout || null;
      this._currentUserMacros = Array.isArray(userMacros) ? userMacros : [];
      this._currentUserActions = Array.isArray(userActions) ? userActions : [];
      if (this.root && !this.root.hidden) {
        this._render();
      }
    }
    isEditMode() {
      return !!this._editMode;
    }
    /**
     * @param {boolean} on
     * @param {{ getConfigPanel?: () => any, onLayoutPersisted?: (layout: any) => void }} [opts]
     */
    setEditMode(on, opts = {}) {
      const next = !!on;
      this._editMode = next;
      this._getConfigPanel = next && typeof opts.getConfigPanel === "function" ? opts.getConfigPanel : null;
      this._onLayoutPersisted = next && typeof opts.onLayoutPersisted === "function" ? opts.onLayoutPersisted : null;
      if (!next) {
        this._editLayoutState = null;
        this._placeItem = null;
        this._placeHoverSlot = null;
        this._onPlaceSlot = null;
      }
      try {
        if (this.root) {
          if (next) this.root.setAttribute("data-kp-edit-mode", "true");
          else this.root.removeAttribute("data-kp-edit-mode");
        }
      } catch {
      }
      this._syncPlaceTargetingAttr();
      this._ensureEditModeStyles();
      if (next) {
        try {
          unpinKeyPopover();
        } catch {
        }
        try {
          if (this.keyboardContainer) detachKeyPopoverBehavior(this.keyboardContainer);
        } catch {
        }
      }
      try {
        if (this.hintEl) {
          if (next) {
            while (this.hintEl.firstChild) this.hintEl.removeChild(this.hintEl.firstChild);
            this.hintEl.hidden = false;
            this.hintEl.style.display = "inline-flex";
            this.hintEl.appendChild(document.createTextNode("Editing \u2014 Alt+C to exit"));
            this.hintEl.setAttribute("aria-label", "Editing layout \u2014 Alt+C to exit");
          } else {
            while (this.hintEl.firstChild) this.hintEl.removeChild(this.hintEl.firstChild);
            this.hintEl.hidden = true;
            this.hintEl.style.display = "none";
            const b = this.keybindings && this.keybindings.TOGGLE_KEYBOARD_HELP;
            const key = b && (b.displayKey || b.keyLabel) ? String(b.displayKey || b.keyLabel) : "K";
            this._setToggleShortcut(key);
          }
        }
      } catch {
      }
      try {
        this._layoutSelectApi?.setDisabled?.(false);
      } catch {
      }
      this._applyEditModeHatch(next);
      this._syncEscExitButton();
      this._syncCloseEditorButton();
      if (this.root && !this.root.hidden) this._render();
    }
    /**
     * Modes where Esc leaves the mode (not normal browsing). The titlebar
     * "Exit [Esc]" button is shown only for these.
     */
    _isEscExitMode(mode) {
      return mode === MODES.TEXT_FOCUS || mode === MODES.HIGHLIGHT || mode === MODES.INSPECTOR || mode === MODES.DELETE || mode === MODES.COLS;
    }
    _shouldShowEscExitButton() {
      if (this._editMode) return false;
      try {
        const kp = typeof this._getKeyPilot === "function" ? this._getKeyPilot() : null;
        const mode = kp?.state?.getState?.()?.mode;
        if (this._isEscExitMode(mode)) return true;
      } catch {
      }
      return false;
    }
    /**
     * Titlebar CTA shown only while a mode that Esc can exit is active
     * (text / typing mode, Text Select, inspector pick, …).
     */
    _syncEscExitButton() {
      try {
        const visible = this._shouldShowEscExitButton();
        if (visible) this._ensureExitTextModeButton();
        this._setExitBtnShown(this._exitTextModeBtn, visible);
      } catch {
      }
    }
    /** @param {HTMLElement|null} btn @param {boolean} shown */
    _setExitBtnShown(btn, shown) {
      if (!btn) return;
      btn.hidden = !shown;
      try {
        btn.setAttribute("aria-hidden", shown ? "false" : "true");
      } catch {
      }
      btn.style.setProperty("display", shown ? "inline-flex" : "none", "important");
    }
    /** Public: KeyPilot calls this on mode changes. */
    syncEscExitButton() {
      this._syncEscExitButton();
    }
    _ensureExitTextModeButton() {
      const header = this._titlebar || this.shadowRoot?.querySelector?.('[data-kp-floating-keyboard-titlebar="true"]') || null;
      if (!header) return;
      let btn = this._exitTextModeBtn && this._exitTextModeBtn.isConnected ? this._exitTextModeBtn : header.querySelector('button[data-kp-floating-keyboard-exit-text="true"]');
      if (!btn) {
        const doc = header.ownerDocument || document;
        btn = doc.createElement("button");
        btn.type = "button";
        btn.setAttribute("data-kp-floating-keyboard-exit-text", "true");
        btn.setAttribute("aria-label", "Exit text mode (Esc)");
        btn.title = "Exit text mode (Esc)";
        Object.assign(btn.style, {
          marginLeft: "6px",
          padding: "0 7px",
          height: "22px",
          minHeight: "22px",
          borderRadius: NCT_DARK_UI_BTN_RADIUS,
          border: NCT_DARK_UI_BTN_BORDER,
          background: NCT_DARK_UI_BTN_GRADIENT,
          color: NCT_DARK_UI_COLORS.fg,
          outline: "none",
          fontSize: "11px",
          fontWeight: "600",
          fontFamily: NCT_DARK_UI_FONT,
          lineHeight: "20px",
          whiteSpace: "nowrap",
          cursor: "pointer",
          flex: "0 0 auto",
          display: "none",
          alignItems: "center",
          gap: "5px"
        });
        btn.hidden = true;
        btn.setAttribute("aria-hidden", "true");
        btn.style.setProperty("display", "none", "important");
        btn.appendChild(doc.createTextNode("Exit"));
        const kbd = createTitlebarKbd(doc, "Esc");
        try {
          kbd.style.fontSize = "10px";
        } catch {
        }
        btn.appendChild(kbd);
      }
      const layoutSelect = this._layoutSelectEl || header.querySelector('[data-kp-floating-keyboard-layout-select="true"]');
      try {
        if (layoutSelect) {
          if (btn.previousSibling !== layoutSelect) {
            header.insertBefore(btn, layoutSelect.nextSibling);
          }
        } else if (!btn.isConnected) {
          header.appendChild(btn);
        }
      } catch {
        try {
          if (!btn.isConnected) header.appendChild(btn);
        } catch {
        }
      }
      try {
        btn.removeEventListener("click", this._onExitTextModeClick);
      } catch {
      }
      btn.addEventListener("click", this._onExitTextModeClick);
      try {
        btn.addEventListener("pointerdown", (e) => {
          try {
            e.stopPropagation();
          } catch {
          }
        });
      } catch {
      }
      this._exitTextModeBtn = btn;
      this._setExitBtnShown(btn, this._shouldShowEscExitButton());
    }
    _onExitTextModeClick(e) {
      try {
        e?.preventDefault?.();
        e?.stopPropagation?.();
      } catch {
      }
      try {
        const kp = typeof this._getKeyPilot === "function" ? this._getKeyPilot() : null;
        if (!kp) return;
        const st = typeof kp.state?.getState === "function" ? kp.state.getState() : null;
        if (st?.mode === MODES.TEXT_FOCUS && typeof kp.handleEscapeFromTextFocus === "function") {
          kp.handleEscapeFromTextFocus(st);
          return;
        }
        if (typeof kp.cancelModes === "function") {
          kp.cancelModes();
          return;
        }
        kp.focusDetector?.clearTextFocus?.();
      } catch {
      }
    }
    /**
     * Titlebar CTA shown only while Keyboard Layout Editor (Alt+C) is open.
     * Placed immediately after the "Editing — Alt+C to exit" hint.
     */
    _syncCloseEditorButton() {
      try {
        const visible = !!this._editMode;
        if (visible) this._ensureCloseEditorButton();
        this._setExitBtnShown(this._closeEditorBtn, visible);
      } catch {
      }
    }
    _ensureCloseEditorButton() {
      const header = this._titlebar || this.shadowRoot?.querySelector?.('[data-kp-floating-keyboard-titlebar="true"]') || null;
      if (!header) return;
      let btn = this._closeEditorBtn && this._closeEditorBtn.isConnected ? this._closeEditorBtn : header.querySelector('button[data-kp-floating-keyboard-close-editor="true"]');
      if (!btn) {
        const doc = header.ownerDocument || document;
        btn = doc.createElement("button");
        btn.type = "button";
        btn.setAttribute("data-kp-floating-keyboard-close-editor", "true");
        btn.setAttribute("aria-label", "Close layout editor (Alt+C)");
        btn.title = "Close layout editor (Alt+C)";
        Object.assign(btn.style, {
          marginLeft: "6px",
          padding: "0 7px",
          height: "22px",
          minHeight: "22px",
          borderRadius: NCT_DARK_UI_BTN_RADIUS,
          border: NCT_DARK_UI_BTN_BORDER,
          background: NCT_DARK_UI_BTN_GRADIENT,
          color: NCT_DARK_UI_COLORS.fg,
          outline: "none",
          fontSize: "11px",
          fontWeight: "600",
          fontFamily: NCT_DARK_UI_FONT,
          lineHeight: "20px",
          whiteSpace: "nowrap",
          cursor: "pointer",
          flex: "0 0 auto",
          display: "none",
          alignItems: "center",
          gap: "5px"
        });
        btn.hidden = true;
        btn.setAttribute("aria-hidden", "true");
        btn.style.setProperty("display", "none", "important");
        btn.appendChild(doc.createTextNode("Close Editor"));
      }
      const hintEl = this.hintEl || header.querySelector('[data-kp-floating-keyboard-hint="true"]');
      try {
        if (hintEl) {
          if (btn.previousSibling !== hintEl) {
            header.insertBefore(btn, hintEl.nextSibling);
          }
        } else if (!btn.isConnected) {
          const collapseBtn = this._collapseBtn || header.querySelector('button[data-kp-floating-keyboard-collapse="true"]');
          if (collapseBtn) header.insertBefore(btn, collapseBtn);
          else if (this.closeBtn) header.insertBefore(btn, this.closeBtn);
          else header.appendChild(btn);
        }
      } catch {
        try {
          if (!btn.isConnected) header.appendChild(btn);
        } catch {
        }
      }
      try {
        btn.removeEventListener("click", this._onCloseEditorClick);
      } catch {
      }
      btn.addEventListener("click", this._onCloseEditorClick);
      try {
        btn.addEventListener("pointerdown", (e) => {
          try {
            e.stopPropagation();
          } catch {
          }
        });
      } catch {
      }
      this._closeEditorBtn = btn;
      this._setExitBtnShown(btn, !!this._editMode);
    }
    _onCloseEditorClick(e) {
      try {
        e?.preventDefault?.();
        e?.stopPropagation?.();
      } catch {
      }
      try {
        const kp = typeof this._getKeyPilot === "function" ? this._getKeyPilot() : null;
        if (!kp) return;
        exitKeyboardLayoutEditMode(kp);
      } catch {
      }
    }
    /**
     * Sync editing layout from the Config panel.
     * @param {any} state LayoutConfigState-like
     */
    setEditLayout(state) {
      this._editLayoutState = state || null;
      if (this._editMode && this.root && !this.root.hidden) {
        this._render();
      }
    }
    /**
     * Begin/end click-to-place targeting on Reference slots.
     * @param {{ item?: { type: string, id: string }|null, onPlace?: ((slot: string) => void)|null }} [opts]
     */
    setPlaceTargeting({ item = null, onPlace = null } = {}) {
      this._placeItem = item && item.type && item.id ? { type: String(item.type), id: String(item.id) } : null;
      this._onPlaceSlot = this._placeItem && typeof onPlace === "function" ? onPlace : null;
      this._placeHoverSlot = null;
      this._syncPlaceTargetingAttr();
      if (this._editMode && this.root && !this.root.hidden) this._render();
    }
    /**
     * @param {string|null} slotLabel
     */
    setPlaceHoverSlot(slotLabel) {
      const next = slotLabel ? String(slotLabel) : null;
      if (this._placeHoverSlot === next) return;
      this._placeHoverSlot = next;
      if (this._editMode && this.root && !this.root.hidden) this._render();
    }
    clearPlaceTargeting() {
      this.setPlaceTargeting({ item: null, onPlace: null });
    }
    isPlaceTargetingActive() {
      return !!(this._placeItem && this._placeItem.type && this._placeItem.id);
    }
    _syncPlaceTargetingAttr() {
      try {
        if (!this.root) return;
        if (this.isPlaceTargetingActive()) this.root.setAttribute("data-kp-place-targeting", "true");
        else this.root.removeAttribute("data-kp-place-targeting");
      } catch {
      }
    }
    /**
     * Cancel click-to-place (Escape / outside click). Prefers the Config panel
     * so the SVG arrow and palette highlight clear together.
     * @returns {boolean} true if place mode was active and canceled
     */
    cancelPlaceTargeting() {
      if (!this.isPlaceTargetingActive()) return false;
      try {
        const panel = typeof this._getConfigPanel === "function" ? this._getConfigPanel() : null;
        if (panel && typeof panel.cancelPlaceMode === "function") {
          panel.cancelPlaceMode();
          return true;
        }
      } catch {
      }
      this.clearPlaceTargeting();
      return true;
    }
    isVisible() {
      return isFloatingKeyboardRootPaintedVisible(this.root);
    }
    /**
     * Show/hide must set both the `hidden` attribute and inline display.
     * Our panel chrome uses display:flex; without clearing it, hide() can fail on
     * pages that weaken or override [hidden]{display:none} (Zapier author CSS does
     * this). Use !important + kpv2-hidden so host sheets cannot re-show the panel.
     * Also clear/set `visibility` — early-inject stamps visibility:hidden !important
     * on a closed shell, and adopting that shell without clearing it leaves the
     * panel layout-present but invisible.
     * @param {boolean} visible
     */
    _setRootVisible(visible) {
      if (!this.root) return;
      if (visible) {
        try {
          ensureChromeHostMounted(this.root);
        } catch {
        }
        paintFloatingKeyboardRootVisible(this.root, true);
        this._bindHostGuard();
      } else {
        this._unbindHostGuard();
        paintFloatingKeyboardRootVisible(this.root, false);
      }
    }
    /**
     * early-inject keeps a host-guard until this event. Fire once we own the shell
     * so SPA remounts do not fight the main panel after adopt.
     */
    _signalEarlyKeyboardHelpAdopted() {
      if (this._earlyAdoptSignaled) return;
      this._earlyAdoptSignaled = true;
      try {
        window.dispatchEvent(new CustomEvent("keypilot-keyboard-help-adopted"));
      } catch {
      }
    }
    show() {
      if (window !== window.top) return;
      const gen = ++this._showGeneration;
      this._ensure();
      this._bindSettingsSync();
      this._refreshKeyFeedbackSetting();
      this._hydrateCollapsedFromSettings();
      this._applyPanelPositionNow();
      const reveal = ({ render = true } = {}) => {
        if (gen !== this._showGeneration) return;
        if (!this.root || !this.root.isConnected) return;
        this._applyPanelPositionNow();
        this._setRootVisible(true);
        this._clearPressed();
        this._scrubPressOverlays();
        if (render) this._render();
        this._schedulePanelPositionAfterLayout();
        this._bindKeydownFeedback();
        try {
          const mode = window.__KeyPilotInstance?.state?.getState?.()?.mode;
          const inText = String(mode || "") === "text_focus";
          this.setTextModeFilter(inText || this._textModeFilterActive);
        } catch {
          if (this._textModeFilterActive) this._applyTextModeFilterClasses(true);
        }
      };
      const revealAfterLayoutReady = () => {
        const layoutSel = String(
          this._getKeyPilot?.()?._settings?.currentKeyboardLayoutId || this._currentKeyboardLayoutId || ""
        );
        if (layoutSel.startsWith("user:") && !this._earlyCustomPaintMatches(layoutSel)) {
          void this._renderAsync().finally(() => reveal({ render: false }));
          return;
        }
        reveal();
      };
      if (this._positionHydrated) {
        revealAfterLayoutReady();
        void this._refreshPanelPosition();
        return;
      }
      void this._refreshPanelPosition().finally(() => {
        if (gen !== this._showGeneration) return;
        this._positionHydrated = true;
        revealAfterLayoutReady();
      });
    }
    hide() {
      this._showGeneration += 1;
      if (this._editMode) {
        try {
          const panel = this._getConfigPanel?.();
          panel?.hide?.();
        } catch {
        }
        this.setEditMode(false);
      }
      this._setRootVisible(false);
      try {
        this._layoutSelectApi?.close?.();
      } catch {
      }
      this.setLinkHoverHints(false);
      this.setTextModeFilter(false);
      this._unbindKeydownFeedback();
      this._unbindSettingsSync();
    }
    toggle() {
      if (this.isVisible()) this.hide();
      else this.show();
    }
    cleanup() {
      this._showGeneration += 1;
      this._unbindHostGuard();
      try {
        this._slotDnDAbort?.abort?.();
      } catch {
      }
      this._slotDnDAbort = null;
      try {
        if (this.closeBtn) this.closeBtn.removeEventListener("click", this._onCloseClick);
      } catch {
      }
      try {
        if (this._collapseBtn) this._collapseBtn.removeEventListener("click", this._onCollapseClick);
      } catch {
      }
      try {
        if (this._exitTextModeBtn) {
          this._exitTextModeBtn.removeEventListener("click", this._onExitTextModeClick);
        }
      } catch {
      }
      try {
        if (this._closeEditorBtn) {
          this._closeEditorBtn.removeEventListener("click", this._onCloseEditorClick);
        }
      } catch {
      }
      try {
        this._handednessChip?.removeEventListener("click", this._onHandednessChipClick);
      } catch {
      }
      try {
        this._layoutSelectApi?.destroy?.();
      } catch {
      }
      this._layoutSelectApi = null;
      this._layoutSelectEl = null;
      this._handednessChip = null;
      this._unbindWindowChrome();
      this._unbindKeydownFeedback();
      this._unbindSettingsSync();
      try {
        this._posResizeObserver?.disconnect?.();
      } catch {
      }
      this._posResizeObserver = null;
      try {
        if (this.root && this.root.parentNode) this.root.parentNode.removeChild(this.root);
      } catch {
      }
      this.root = null;
      this.shadowRoot = null;
      this.keyboardContainer = null;
      this._keyboardBody = null;
      this.closeBtn = null;
      this._titlebar = null;
      this._exitTextModeBtn = null;
      this._closeEditorBtn = null;
      this._collapseBtn = null;
    }
    /**
     * Panel shell chrome shared by create + early-inject adopt paths.
     * Position is applied separately via the shared panel-position system
     * (default anchor: bottom-left).
     * @param {HTMLElement} root
     */
    _applyProPanelChrome(root) {
      if (!root || !root.style) return;
      let show = true;
      try {
        if (root.hidden || root.getAttribute("aria-hidden") === "true" || root.classList?.contains("kpv2-hidden") || root.style.display === "none" || root.style.visibility === "hidden") {
          show = false;
        }
      } catch {
      }
      Object.assign(root.style, {
        position: "fixed",
        width: "760px",
        // Symmetric inset on all sides (matches KEYBOARD_POSITION_MARGIN_PX).
        maxWidth: `calc(100vw - ${KEYBOARD_MAX_VIEWPORT_INSET_PX}px)`,
        maxHeight: `calc(100vh - ${KEYBOARD_MAX_VIEWPORT_INSET_PX}px)`,
        flexDirection: "column",
        overflow: "hidden",
        boxSizing: "border-box",
        zIndex: String(Z_INDEX.FLOATING_KEYBOARD_HELP),
        background: NCT_DARK_UI_PANEL_BACKGROUND,
        color: "var(--kp-color-fg, #ddd)",
        // NCT dark UI panel rim (dual-edge inset rim).
        border: NCT_DARK_UI_PANEL_BORDER,
        borderRadius: NCT_DARK_UI_PANEL_RADIUS,
        boxShadow: NCT_DARK_UI_PANEL_BOX_SHADOW,
        fontFamily: NCT_DARK_UI_FONT
      });
      paintFloatingKeyboardRootVisible(root, show);
      applyPopupThemeVars(root);
    }
    /**
     * Apply current in-memory panel position to the root element.
     * Re-reads live size so free positions cannot stay below the fold after keyboard paint.
     */
    _applyPanelPositionNow() {
      if (!this.root) return;
      try {
        const resolved = applyPanelPosition(this.root, this._panelPosition, {
          margin: KEYBOARD_POSITION_MARGIN_PX,
          defaultAnchor: "bottom-left",
          // Used when the panel has not laid out yet (height 0) so free tops
          // cannot pin to vh−margin and then grow off the bottom of the screen.
          fallbackWidth: 760,
          fallbackHeight: 200
        });
        if (resolved && !resolved.anchor) {
          this._panelPosition = {
            left: resolved.left,
            top: resolved.top,
            anchor: null
          };
        } else if (resolved?.anchor) {
          this._panelPosition = {
            left: resolved.left,
            top: resolved.top,
            anchor: resolved.anchor
          };
        }
      } catch {
      }
    }
    /**
     * Re-apply after layout (keyboard rows often size one frame after show).
     */
    _schedulePanelPositionAfterLayout() {
      this._applyPanelPositionNow();
      try {
        requestAnimationFrame(() => {
          this._applyPanelPositionNow();
          requestAnimationFrame(() => this._applyPanelPositionNow());
        });
      } catch {
        this._applyPanelPositionNow();
      }
      try {
        if (typeof ResizeObserver === "undefined" || !this.root) return;
        try {
          this._posResizeObserver?.disconnect?.();
        } catch {
        }
        let fires = 0;
        this._posResizeObserver = new ResizeObserver(() => {
          this._applyPanelPositionNow();
          if (++fires >= 4) {
            try {
              this._posResizeObserver?.disconnect?.();
            } catch {
            }
            this._posResizeObserver = null;
          }
        });
        this._posResizeObserver.observe(this.root);
      } catch {
      }
    }
    /**
     * @param {import('../modules/settings-manager.js').PanelPositionSettings|null|undefined} next
     * @param {{ persist?: boolean }} [opts]
     */
    _setPanelPosition(next, opts = {}) {
      this._seedPanelPosition(next, { hydrated: true });
      this._applyPanelPositionNow();
      if (opts.persist && !this._suppressPositionPersist) {
        this._persistPanelPosition(this._panelPosition);
      }
    }
    async _refreshPanelPosition() {
      try {
        const settings = await getSettings();
        const stored = settings?.panelPositions?.keyboardReference;
        this._suppressPositionPersist = true;
        this._setPanelPosition(stored, { persist: false });
        this._suppressPositionPersist = false;
        this._positionHydrated = true;
        if (this.isVisible()) {
          this._schedulePanelPositionAfterLayout();
        } else {
          this._applyPanelPositionNow();
        }
        try {
          const pos = this._panelPosition;
          if (pos && pos.anchor == null && Number.isFinite(pos.left) && Number.isFinite(pos.top)) {
            const before = stored && typeof stored === "object" ? stored : null;
            const moved = !before || Math.round(Number(before.top)) !== Math.round(pos.top) || Math.round(Number(before.left)) !== Math.round(pos.left);
            if (moved && this.isVisible()) {
              void this._persistPanelPosition(pos);
            }
          }
        } catch {
        }
      } catch {
        this._suppressPositionPersist = false;
        this._positionHydrated = true;
      }
    }
    /**
     * @param {import('../modules/settings-manager.js').PanelPositionSettings} position
     */
    async _persistPanelPosition(position) {
      try {
        await setSettings({
          panelPositions: {
            keyboardReference: {
              left: position.left,
              top: position.top,
              anchor: position.anchor === void 0 ? null : position.anchor
            }
          }
        });
      } catch {
      }
    }
    _onWinResizePosition() {
      if (!this.root || !this.isVisible()) return;
      if (this._positionApplyScheduled) return;
      this._positionApplyScheduled = true;
      try {
        requestAnimationFrame(() => {
          this._positionApplyScheduled = false;
          this._applyPanelPositionNow();
        });
      } catch {
        this._positionApplyScheduled = false;
        this._applyPanelPositionNow();
      }
    }
    /**
     * Compact, dark window-style titlebar (drag handle).
     * @param {HTMLElement|null} header
     * @param {{ titleEl?: HTMLElement|null, shortcutEl?: HTMLElement|null, hintEl?: HTMLElement|null, closeBtn?: HTMLElement|null }} [parts]
     */
    _applyCompactTitlebar(header, parts = {}) {
      if (!header || !header.style) return;
      Object.assign(header.style, {
        display: "flex",
        alignItems: "center",
        justifyContent: "flex-start",
        gap: "8px",
        height: "28px",
        minHeight: "28px",
        maxHeight: "28px",
        boxSizing: "border-box",
        padding: "0 6px 0 10px",
        margin: "0",
        borderBottom: NCT_DARK_UI_TITLEBAR_BORDER_BOTTOM,
        background: NCT_DARK_UI_TITLEBAR_GRADIENT,
        flex: "0 0 auto",
        cursor: "grab",
        userSelect: "none",
        WebkitUserSelect: "none",
        touchAction: "none"
      });
      try {
        header.title = header.title || "Drag to move";
      } catch {
      }
      const titleEl = parts.titleEl || header.querySelector('[data-kp-floating-keyboard-title="true"]') || header.firstElementChild;
      if (titleEl && titleEl.style) {
        Object.assign(titleEl.style, {
          fontSize: "11px",
          fontWeight: "var(--kp-titlebar-title-weight, 600)",
          letterSpacing: "var(--kp-type-tracking-titlebar, 0.02em)",
          textTransform: "var(--kp-type-transform-titlebar, none)",
          color: "var(--kp-color-fg, #ddd)",
          lineHeight: "28px",
          whiteSpace: "nowrap",
          overflow: "hidden",
          textOverflow: "ellipsis",
          margin: "0",
          padding: "0",
          flex: "0 1 auto",
          minWidth: "0"
        });
      }
      const shortcutEl = parts.shortcutEl || header.querySelector('[data-kp-floating-keyboard-shortcut="true"]') || header.querySelector('[data-kp-titlebar-shortcut="true"]');
      if (shortcutEl && shortcutEl.style) {
        Object.assign(shortcutEl.style, {
          marginLeft: "0",
          flexShrink: "0"
        });
      }
      const hintEl = parts.hintEl || header.querySelector('[data-kp-floating-keyboard-hint="true"]');
      if (hintEl && hintEl.style) {
        Object.assign(hintEl.style, {
          display: hintEl.hidden ? "none" : "inline-flex",
          alignItems: "center",
          gap: "4px",
          marginLeft: "0",
          fontSize: "10px",
          fontWeight: "500",
          letterSpacing: "0",
          color: "rgba(140, 145, 155, 0.95)",
          padding: "0 4px",
          borderRadius: "0",
          border: "none",
          background: "transparent",
          lineHeight: "28px",
          whiteSpace: "nowrap"
        });
      }
      const collapseBtn = parts.collapseBtn || header.querySelector('button[data-kp-floating-keyboard-collapse="true"]');
      if (collapseBtn && collapseBtn.style) {
        collapseBtn.style.marginLeft = "auto";
        collapseBtn.style.flex = "0 0 auto";
      }
      const closeBtn = parts.closeBtn || header.querySelector('button[data-kp-floating-keyboard-close="true"]') || header.querySelector('button[aria-label="Close keyboard reference"]');
      if (closeBtn && closeBtn.style) {
        Object.assign(closeBtn.style, {
          width: "22px",
          height: "22px",
          minWidth: "22px",
          minHeight: "22px",
          borderRadius: "4px",
          border: "none",
          background: "transparent",
          color: "rgba(200, 200, 205, 0.9)",
          cursor: "pointer",
          fontSize: "15px",
          lineHeight: "20px",
          padding: "0",
          margin: "0",
          flex: "0 0 auto",
          boxShadow: NCT_DARK_UI_ICON_BUTTON_OUTLINE
        });
      }
      if (this._editMode) this._applyEditModeHatch(true, header);
    }
    /**
     * Body that wraps `.kp-floating-keyboard-help__keyboard` — no chrome padding.
     * @param {HTMLElement|null} body
     */
    _applyKeyboardBodyChrome(body) {
      if (!body || !body.style) return;
      Object.assign(body.style, {
        padding: "0",
        margin: "0",
        border: "none",
        background: "transparent",
        flex: "1 1 auto",
        minHeight: "0",
        // Fixed key sizes for now (resize/flex-scale temporarily suspended).
        overflow: "auto"
      });
      if (this._editMode) this._applyEditModeHatch(true, null, body);
    }
    /**
     * Keyboard host chrome.
     * TEMP: plain block layout with fixed key sizes (flex-fill suspended with resize).
     * @param {HTMLElement|null} keyboardContainer
     */
    _applyKeyboardHostChrome(keyboardContainer) {
      if (!keyboardContainer || !keyboardContainer.style) return;
      Object.assign(keyboardContainer.style, {
        width: "100%",
        boxSizing: "border-box"
      });
    }
    /**
     * Titlebar drag (shared panel-position: margin clamp + corner/edge snap)
     * + shared edge/corner resize with SE grip (resize currently suspended).
     */
    _bindWindowChrome() {
      if (this._windowChromeBound || !this.root) return;
      const panel = this.root;
      const header = this._titlebar || this.shadowRoot?.querySelector?.('[data-kp-floating-keyboard-titlebar="true"]') || panel.firstElementChild;
      if (!header) return;
      this._titlebar = header;
      this._windowChromeBound = true;
      try {
        const api = makePanelDraggable(panel, header, {
          margin: KEYBOARD_POSITION_MARGIN_PX,
          excludeSelector: 'button[data-kp-floating-keyboard-close="true"], button[aria-label="Close keyboard reference"], .kpv2-popover-resize-handle',
          onMoveEnd: (state) => {
            if (!state?.moved) return;
            this._setPanelPosition(
              {
                left: state.left,
                top: state.top,
                anchor: state.anchor
              },
              { persist: true }
            );
          }
        });
        this._dragDispose = api?.dispose || null;
      } catch (err) {
        console.warn("[KeyPilot] Failed to make keyboard reference draggable:", err?.message || err);
        this._dragDispose = null;
      }
      try {
        window.addEventListener("resize", this._onWinResizePosition, true);
      } catch {
      }
      this._resizeDispose = null;
      this._applyPanelPositionNow();
    }
    _unbindWindowChrome() {
      try {
        this._dragDispose?.();
      } catch {
      }
      this._dragDispose = null;
      try {
        this._resizeDispose?.();
      } catch {
      }
      this._resizeDispose = null;
      try {
        window.removeEventListener("resize", this._onWinResizePosition, true);
      } catch {
      }
      this._windowChromeBound = false;
    }
    /**
     * React/Next can remove foreign chrome hosts from <html>/<body>.
     * Reattach while the keyboard reference is still supposed to be visible.
     */
    _bindHostGuard() {
      if (this._hostGuard || typeof MutationObserver === "undefined") return;
      const root = this.root;
      if (!root) return;
      try {
        this._hostGuard = new MutationObserver(() => {
          if (!this.root || this.root.isConnected) return;
          try {
            ensureChromeHostMounted(this.root);
          } catch {
          }
        });
        this._hostGuard.observe(document.documentElement, { childList: true, subtree: true });
      } catch {
        this._hostGuard = null;
      }
    }
    _unbindHostGuard() {
      try {
        this._hostGuard?.disconnect?.();
      } catch {
      }
      this._hostGuard = null;
    }
    _ensure() {
      if (this.root && this.root.isConnected) {
        this._bindWindowChrome();
        this._bindHostGuard();
        this._signalEarlyKeyboardHelpAdopted();
        return;
      }
      if (this.root && !this.root.isConnected) {
        try {
          ensureChromeHostMounted(this.root);
        } catch {
        }
        this._bindWindowChrome();
        this._bindHostGuard();
        this._signalEarlyKeyboardHelpAdopted();
        if (this.root && this.root.isConnected) return;
      }
      try {
        const existing = document.querySelector('.kp-floating-keyboard-help[data-kp-early-floating-keyboard="true"]');
        if (existing && existing.isConnected) {
          const shadowRoot2 = ensureOpenChromeShadow(existing, { id: "keyboard-help", chromeWindow: true });
          const shell2 = shadowRoot2 || existing;
          const keyboardContainer2 = shell2.querySelector(".kp-floating-keyboard-help__keyboard");
          const closeBtn2 = shell2.querySelector('button[data-kp-floating-keyboard-close="true"]') || shell2.querySelector('button[aria-label="Close keyboard reference"]');
          const header2 = shell2.querySelector('[data-kp-floating-keyboard-titlebar="true"]') || shell2.firstElementChild;
          const body2 = keyboardContainer2?.parentElement || null;
          const collapseBtn = shell2.querySelector('button[data-kp-floating-keyboard-collapse="true"]');
          const hintEl = shell2.querySelector('[data-kp-floating-keyboard-hint="true"]');
          const titleEl = shell2.querySelector('[data-kp-floating-keyboard-title="true"]') || (header2 ? header2.querySelector("div:not([data-kp-floating-keyboard-hint])") : null);
          let shortcutEl = shell2.querySelector('[data-kp-floating-keyboard-shortcut="true"]') || shell2.querySelector('[data-kp-titlebar-shortcut="true"]');
          if (!shortcutEl && header2 && titleEl) {
            shortcutEl = createTitlebarShortcut(document, "K");
            shortcutEl.setAttribute("data-kp-floating-keyboard-shortcut", "true");
            shortcutEl.title = "Toggle keyboard reference";
            try {
              if (titleEl.nextSibling) header2.insertBefore(shortcutEl, titleEl.nextSibling);
              else header2.appendChild(shortcutEl);
            } catch {
              try {
                header2.appendChild(shortcutEl);
              } catch {
              }
            }
          }
          if (header2 && titleEl && !header2.querySelector('[data-kp-floating-keyboard-icon="true"], .kp-titlebar-icon')) {
            const leadingIcon2 = createTitlebarLeadingIcon(document, "keyboard");
            leadingIcon2.setAttribute("data-kp-floating-keyboard-icon", "true");
            try {
              header2.insertBefore(leadingIcon2, titleEl);
            } catch {
            }
          }
          let layoutSelect2 = shell2.querySelector('[data-kp-floating-keyboard-layout-select="true"]');
          if (header2) {
            layoutSelect2 = this._installLayoutSelect(layoutSelect2, header2, hintEl);
            this._ensureHandednessChip(header2, layoutSelect2);
          } else if (layoutSelect2) {
            this._installLayoutSelect(layoutSelect2, layoutSelect2.parentElement, layoutSelect2.nextSibling);
          }
          if (!this._positionHydrated) {
            const fromDom = this._readPositionFromDom(existing);
            if (fromDom) this._seedPanelPosition(fromDom, { hydrated: true });
          }
          try {
            if (hintEl && !this._editMode) {
              while (hintEl.firstChild) hintEl.removeChild(hintEl.firstChild);
              hintEl.hidden = true;
              hintEl.style.display = "none";
            }
          } catch {
          }
          try {
            this._applyProPanelChrome(existing);
            this._applyCompactTitlebar(header2, { titleEl, shortcutEl, hintEl, closeBtn: closeBtn2 });
            this._applyKeyboardBodyChrome(body2);
            this._applyKeyboardHostChrome(keyboardContainer2);
          } catch {
          }
          if (keyboardContainer2) {
            this.root = existing;
            this.shadowRoot = shadowRoot2;
            this.keyboardContainer = keyboardContainer2;
            this._keyboardBody = body2;
            this.closeBtn = closeBtn2 || null;
            this.hintEl = hintEl || null;
            this._shortcutEl = shortcutEl || null;
            this._titlebar = header2 || null;
            this._layoutSelectEl = layoutSelect2 || null;
            this._layoutTitleEl = titleEl || null;
            this._collapseBtn = collapseBtn || null;
            try {
              if (existing.getAttribute("data-kp-collapsed") === "true") this._collapsed = true;
            } catch {
            }
            if (this.closeBtn) {
              try {
                this.closeBtn.removeEventListener("click", this._onCloseClick);
              } catch {
              }
              this.closeBtn.addEventListener("click", this._onCloseClick);
              this.closeBtn.addEventListener("pointerdown", (e) => e.stopPropagation(), true);
            }
            this._ensureCollapseButton();
            this._applyCollapsedLayout();
            this._bindWindowChrome();
            try {
              ensureStylesInjected(shadowRoot2 || existing);
            } catch {
            }
            this._bindHostGuard();
            this._signalEarlyKeyboardHelpAdopted();
            this._reuseEarlyCustomPaint = true;
            return;
          }
        }
      } catch {
      }
      const root = document.createElement("div");
      root.className = "kp-floating-keyboard-help";
      root.hidden = true;
      root.setAttribute("role", "dialog");
      root.setAttribute("aria-label", "KeyPilot keyboard reference");
      const shadowRoot = ensureOpenChromeShadow(root, { id: "keyboard-help", chromeWindow: true });
      const shell = shadowRoot || root;
      this._applyProPanelChrome(root);
      const header = document.createElement("div");
      header.setAttribute("data-kp-floating-keyboard-titlebar", "true");
      const title = document.createElement("div");
      title.textContent = "Keyboard Reference";
      title.setAttribute("data-kp-floating-keyboard-title", "true");
      const leadingIcon = createTitlebarLeadingIcon(document, "keyboard");
      leadingIcon.setAttribute("data-kp-floating-keyboard-icon", "true");
      const shortcut = createTitlebarShortcut(document, "K");
      shortcut.setAttribute("data-kp-floating-keyboard-shortcut", "true");
      shortcut.title = "Toggle keyboard reference";
      const hint = document.createElement("div");
      hint.setAttribute("data-kp-floating-keyboard-hint", "true");
      hint.hidden = true;
      const layoutSelect = this._installLayoutSelect(null, header, hint);
      const closeBtn = document.createElement("button");
      closeBtn.type = "button";
      closeBtn.textContent = "\xD7";
      closeBtn.setAttribute("aria-label", "Close keyboard reference");
      closeBtn.setAttribute("data-kp-floating-keyboard-close", "true");
      closeBtn.addEventListener("click", this._onCloseClick);
      closeBtn.addEventListener("pointerdown", (e) => e.stopPropagation(), true);
      header.appendChild(leadingIcon);
      header.appendChild(title);
      header.appendChild(shortcut);
      header.appendChild(layoutSelect);
      this._ensureHandednessChip(header, layoutSelect);
      header.appendChild(hint);
      header.appendChild(closeBtn);
      this._applyCompactTitlebar(header, {
        titleEl: title,
        shortcutEl: shortcut,
        hintEl: hint,
        closeBtn
      });
      const body = document.createElement("div");
      body.setAttribute("data-kp-floating-keyboard-body", "true");
      this._applyKeyboardBodyChrome(body);
      const keyboardContainer = document.createElement("div");
      keyboardContainer.className = "kp-floating-keyboard-help__keyboard";
      this._applyKeyboardHostChrome(keyboardContainer);
      body.appendChild(keyboardContainer);
      shell.appendChild(header);
      shell.appendChild(body);
      (document.body || document.documentElement).appendChild(root);
      try {
        ensureChromeHostMounted(root);
      } catch {
      }
      this.root = root;
      this.shadowRoot = shadowRoot;
      this.keyboardContainer = keyboardContainer;
      this._keyboardBody = body;
      this.closeBtn = closeBtn;
      this.hintEl = hint;
      this._shortcutEl = shortcut;
      this._titlebar = header;
      this._layoutSelectEl = layoutSelect;
      this._layoutTitleEl = title;
      this._ensureCollapseButton();
      this._applyCollapsedLayout();
      this._bindWindowChrome();
      try {
        ensureStylesInjected(shadowRoot || root);
      } catch {
      }
      this._bindHostGuard();
      this._signalEarlyKeyboardHelpAdopted();
    }
    _ensureCollapseButton() {
      const header = this._titlebar || this.shadowRoot?.querySelector?.('[data-kp-floating-keyboard-titlebar="true"]') || null;
      if (!header) return;
      let btn = this._collapseBtn || header.querySelector('button[data-kp-floating-keyboard-collapse="true"]');
      if (!btn) {
        btn = document.createElement("button");
        btn.type = "button";
        btn.setAttribute("data-kp-floating-keyboard-collapse", "true");
        header.insertBefore(btn, this.closeBtn || null);
      }
      Object.assign(btn.style, {
        width: "22px",
        height: "22px",
        minWidth: "22px",
        minHeight: "22px",
        borderRadius: "4px",
        border: "none",
        background: "transparent",
        color: "rgba(200, 200, 205, 0.9)",
        cursor: "pointer",
        fontSize: "14px",
        lineHeight: "20px",
        padding: "0",
        margin: "0",
        marginLeft: "auto",
        flex: "0 0 auto",
        boxShadow: NCT_DARK_UI_ICON_BUTTON_OUTLINE
      });
      try {
        btn.removeEventListener("click", this._onCollapseClick);
      } catch {
      }
      btn.addEventListener("click", this._onCollapseClick);
      btn.addEventListener("pointerdown", (e) => e.stopPropagation(), true);
      this._collapseBtn = btn;
    }
    _applyCollapsedLayout() {
      const collapsed = !!this._collapsed;
      try {
        if (this._keyboardBody) this._keyboardBody.style.display = collapsed ? "none" : "block";
        if (this.root) this.root.setAttribute("data-kp-collapsed", collapsed ? "true" : "false");
        if (this._collapseBtn) {
          this._collapseBtn.textContent = collapsed ? "\u25B8" : "\u25BE";
          this._collapseBtn.setAttribute("aria-label", collapsed ? "Expand keyboard reference" : "Collapse keyboard reference");
          this._collapseBtn.title = collapsed ? "Expand" : "Collapse";
        }
      } catch {
      }
    }
    /**
     * Collapse / expand the keyboard body (titlebar stays).
     * @param {boolean} collapsed
     * @param {{ persist?: boolean }} [opts]
     */
    setCollapsed(collapsed, { persist = false } = {}) {
      const next = !!collapsed;
      this._collapseEpoch = (this._collapseEpoch || 0) + 1;
      if (this._collapsed === next) {
        this._applyCollapsedLayout();
        this._applyPanelPositionNow();
        if (persist) {
          try {
            void setSettings({ keyboardReferenceCollapsed: next });
          } catch {
          }
        }
        return;
      }
      this._collapsed = next;
      this._applyCollapsedLayout();
      this._applyPanelPositionNow();
      if (persist) {
        try {
          void setSettings({ keyboardReferenceCollapsed: next });
        } catch {
        }
      }
    }
    _onCollapseClick(e) {
      try {
        e?.preventDefault?.();
        e?.stopPropagation?.();
      } catch {
      }
      this.setCollapsed(!this._collapsed, { persist: true });
    }
    async _hydrateCollapsedFromSettings() {
      try {
        const epoch = this._collapseEpoch || 0;
        const settings = await getSettings();
        if ((this._collapseEpoch || 0) !== epoch) return;
        this.setCollapsed(!!settings?.keyboardReferenceCollapsed, { persist: false });
      } catch {
      }
    }
    /**
     * Title-adjacent toggle shortcut (key label is layout-aware).
     * @param {string} keyLabel
     */
    _setToggleShortcut(keyLabel) {
      const key = String(keyLabel || "K").trim() || "K";
      const el2 = this._shortcutEl || this._titlebar?.querySelector?.('[data-kp-floating-keyboard-shortcut="true"]') || this._titlebar?.querySelector?.('[data-kp-titlebar-shortcut="true"]') || null;
      if (!el2) return;
      this._shortcutEl = el2;
      try {
        if (el2.textContent === key && el2.style.display !== "none") return;
      } catch {
      }
      setTitlebarShortcutText(el2, key);
      try {
        el2.title = `Toggle with ${key}`;
        el2.setAttribute("aria-label", `Toggle keyboard reference (${key})`);
      } catch {
      }
    }
    /**
     * @deprecated Prefer {@link _setToggleShortcut}; kept for early-inject adopt paths.
     * @param {HTMLElement|null} hintEl
     * @param {string} keyLabel
     */
    _setToggleHint(hintEl, keyLabel) {
      this._setToggleShortcut(keyLabel);
      if (hintEl && !this._editMode) {
        try {
          while (hintEl.firstChild) hintEl.removeChild(hintEl.firstChild);
          hintEl.hidden = true;
          hintEl.style.display = "none";
        } catch {
        }
      }
    }
    _render() {
      if (!this.keyboardContainer) return;
      try {
        if (!this._editMode) {
          const b = this.keybindings && this.keybindings.TOGGLE_KEYBOARD_HELP;
          const key = b && (b.displayKey || b.keyLabel) ? String(b.displayKey || b.keyLabel) : "K";
          this._setToggleShortcut(key);
        }
      } catch {
      }
      this._syncEscExitButton();
      this._syncCloseEditorButton();
      void this._renderAsync();
    }
    /**
     * Restore the normal layout title after leaving typing / text mode.
     */
    _restoreLayoutTitle() {
      try {
        if (!this._layoutTitleEl || this._editMode) return;
        this._layoutTitleEl.textContent = "Keyboard Reference";
      } catch {
      }
    }
    /**
     * Steel hatch on titlebar + body while editing (matches Keyboard Layout Config).
     * Applied as a class plus inline background so it wins over compact-titlebar inline chrome.
     * @param {boolean} on
     * @param {HTMLElement|null} [header]
     * @param {HTMLElement|null} [body]
     */
    _applyEditModeHatch(on, header = null, body = null) {
      const hatch = "var(--kp-hatch-edit)";
      const titlebar = header || this._titlebar;
      const keyboardBody = body || this._keyboardBody;
      try {
        titlebar?.classList.toggle("kp-kb-edit-hatch", !!on);
        keyboardBody?.classList.toggle("kp-kb-edit-hatch", !!on);
      } catch {
      }
      try {
        if (titlebar?.style) {
          if (on) {
            titlebar.style.backgroundImage = `${hatch}, var(--kp-hatch-edit-titlebar-bg, linear-gradient(180deg, #646464 0%, #4a4a4a 45%, #383838 100%))`;
          } else {
            titlebar.style.background = NCT_DARK_UI_TITLEBAR_GRADIENT;
          }
        }
      } catch {
      }
      try {
        if (keyboardBody?.style) {
          if (on) {
            keyboardBody.style.backgroundColor = "var(--kp-hatch-edit-body-bg, #1a1c20)";
            keyboardBody.style.backgroundImage = hatch;
          } else {
            keyboardBody.style.background = "transparent";
            keyboardBody.style.backgroundImage = "";
            keyboardBody.style.backgroundColor = "";
          }
        }
      } catch {
      }
    }
    /**
     * Orange-cast titlebar + warm title color while typing (text mode).
     * Edit-mode hatch styles win when both attributes are present.
     */
    _ensureTextModeStyles() {
      try {
        const root = this.shadowRoot || this.root?.ownerDocument || document;
        const css = `
/* Typing / text mode: orange cast over the NCT titlebar bevel (COLORS.ORANGE #ff8c00). */
:host([data-kp-text-mode="true"]:not([data-kp-edit-mode="true"])) [data-kp-floating-keyboard-titlebar="true"] {
  background: ${NCT_DARK_UI_TITLEBAR_TEXT_MODE_BACKGROUND} !important;
  border-bottom: ${NCT_DARK_UI_TITLEBAR_TEXT_MODE_BORDER_BOTTOM} !important;
}
:host([data-kp-text-mode="true"]:not([data-kp-edit-mode="true"])) [data-kp-floating-keyboard-title="true"] {
  color: ${NCT_DARK_UI_TITLEBAR_TEXT_MODE_TITLE_COLOR} !important;
}
:host([data-kp-text-mode="true"]:not([data-kp-edit-mode="true"])) [data-kp-floating-keyboard-hint="true"] {
  color: ${NCT_DARK_UI_TITLEBAR_TEXT_MODE_HINT_COLOR} !important;
}
      `.trim();
        injectChromeStyles(root, { attr: "data-kp-keyboard-text-mode-style", css });
      } catch {
      }
    }
    /**
     * Sync root attribute, title, and hint for typing / text mode.
     * @param {boolean} active
     */
    _applyTextModeChrome(active2) {
      const on = Boolean(active2);
      try {
        if (this.root) {
          if (on) this.root.setAttribute("data-kp-text-mode", "true");
          else this.root.removeAttribute("data-kp-text-mode");
        }
      } catch {
      }
      this._ensureTextModeStyles();
      this._syncEscExitButton();
      if (this._editMode) return;
      try {
        if (this._layoutTitleEl) {
          if (on) {
            this._layoutTitleEl.textContent = "Keyboard Reference \u2014 Typing";
          } else {
            this._restoreLayoutTitle();
          }
        }
      } catch {
      }
      try {
        if (this.hintEl && !on && !this._editMode) {
          const b = this.keybindings && this.keybindings.TOGGLE_KEYBOARD_HELP;
          const key = b && (b.displayKey || b.keyLabel) ? String(b.displayKey || b.keyLabel) : "K";
          this._setToggleShortcut(key);
        }
      } catch {
      }
    }
    _ensureEditModeStyles() {
      try {
        const root = this.shadowRoot || this.root?.ownerDocument || document;
        const css = `
/* Edit-mode plate hatch is on .keyboard-visual.kp-kb-edit-hatch (keybindings-ui-shared). */
/* Same hatch over a lightened titlebar bevel (overrides inline background shorthand). */
:host([data-kp-edit-mode="true"]) [data-kp-floating-keyboard-titlebar="true"],
:host [data-kp-floating-keyboard-titlebar="true"].kp-kb-edit-hatch {
  background-image:
    var(--kp-hatch-edit),
    var(--kp-hatch-edit-titlebar-bg, linear-gradient(180deg, #646464 0%, #4a4a4a 45%, #383838 100%)) !important;
}
:host([data-kp-edit-mode="true"]) [data-kp-floating-keyboard-body="true"],
:host [data-kp-floating-keyboard-body="true"].kp-kb-edit-hatch {
  background-color: var(--kp-hatch-edit-body-bg, #1a1c20) !important;
  background-image: var(--kp-hatch-edit) !important;
}
:host([data-kp-place-targeting="true"]) [data-kp-floating-keyboard-titlebar="true"] {
  animation: kp-kb-place-titlebar-pulse 1.15s ease-in-out infinite;
  position: relative;
  isolation: isolate;
}
:host([data-kp-place-targeting="true"]) [data-kp-floating-keyboard-titlebar="true"]::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: rgba(144, 82, 255, 0.38);
  animation: kp-kb-place-hatch-tint 1.15s ease-in-out infinite;
}
:host([data-kp-place-targeting="true"]) [data-kp-floating-keyboard-titlebar="true"] > * {
  position: relative;
  z-index: 1;
}
@keyframes kp-kb-place-titlebar-pulse {
  0%, 100% { filter: brightness(1); box-shadow: inset 0 0 0 0 rgba(144, 82, 255, 0); }
  50% { filter: brightness(1.12); box-shadow: inset 0 0 0 2px rgba(193, 151, 255, 0.88), 0 0 14px rgba(144, 82, 255, 0.62); }
}
@keyframes kp-kb-place-hatch-tint {
  0%, 100% { opacity: 0.08; }
  50% { opacity: 0.58; }
}
/* Lighten the panel chrome fill while editing. */
:host([data-kp-edit-mode="true"]) {
  background-color: #2e2e2e !important;
}
:host([data-kp-edit-mode="true"]) .keyboard-visual.${KEYBINDINGS_UI_ROOT_CLASS} .key {
  position: relative;
  cursor: grab;
}
:host([data-kp-edit-mode="true"]) .keyboard-visual.${KEYBINDINGS_UI_ROOT_CLASS} .key[data-kp-edit-readonly="true"] {
  cursor: default;
  opacity: 0.9;
}
/* Drop / place hover: use border + box-shadow \u2014 base .key sets outline:none !important. */
:host([data-kp-edit-mode="true"]) .key.kp-drop-target,
:host([data-kp-edit-mode="true"]) .key.kp-place-preview {
  z-index: 2;
  border-color: rgba(91, 226, 241, 0.95) !important;
  border-top-color: rgba(180, 245, 255, 1) !important;
  border-bottom-color: rgba(40, 180, 200, 0.95) !important;
  filter: brightness(1.1) saturate(1.1);
  box-shadow:
    0 0 0 2px rgba(91, 226, 241, 0.75),
    0 0 14px 3px rgba(91, 226, 241, 0.45),
    inset 0 0 0 2px rgba(91, 226, 241, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.22) !important;
}
      `.trim();
        injectChromeStyles(root, { attr: "data-kp-keyboard-edit-mode-style", css });
      } catch {
      }
    }
    /**
     * @param {() => any} getKeyPilot
     */
    setKeyPilotAccessor(getKeyPilot) {
      this._getKeyPilot = typeof getKeyPilot === "function" ? getKeyPilot : null;
    }
    /**
     * Shared change handler for the titlebar layout select.
     * In edit mode this switches the layout being edited (same as Config's Layout combo).
     * Outside edit mode it sets the current/active layout.
     * @param {string} raw
     */
    async _onLayoutSelectChange(raw) {
      const api = this._layoutSelectApi;
      const v = String(raw || "builtin");
      if (v === LAYOUT_SELECT_EDIT_VALUE || v === LAYOUT_SELECT_NEW_VALUE || v === LAYOUT_SELECT_DUP_VALUE || v === LAYOUT_SELECT_ONBOARDING_VALUE || v === LAYOUT_SELECT_DOCS_VALUE || v === LAYOUT_SELECT_SETTINGS_VALUE) {
        const prev = this._layoutSelectValueForCurrent();
        try {
          api?.setValue?.(prev, { silent: true });
        } catch {
        }
        try {
          const kp = this._getKeyPilot?.();
          if (v === LAYOUT_SELECT_ONBOARDING_VALUE) {
            const ob = window.__KeyPilotOnboarding;
            if (ob && typeof ob.resetTutorial === "function") {
              void ob.resetTutorial();
            } else if (ob && typeof ob.setActive === "function") {
              ob.setActive(true);
            }
          } else if (v === LAYOUT_SELECT_DOCS_VALUE) {
            if (kp && typeof kp.handleOpenDocsPopover === "function") {
              kp.handleOpenDocsPopover();
            } else if (kp && typeof kp.handleToggleDocsPopover === "function") {
              kp.handleToggleDocsPopover();
            }
          } else if (v === LAYOUT_SELECT_SETTINGS_VALUE) {
            if (kp && typeof kp.handleOpenSettingsPopover === "function") {
              kp.handleOpenSettingsPopover();
            }
          } else if (v === LAYOUT_SELECT_EDIT_VALUE) {
            openKeyboardLayoutConfigurator(kp || null, {});
          } else {
            openKeyboardLayoutConfigurator(kp || null, {
              createNew: v === LAYOUT_SELECT_NEW_VALUE,
              createDuplicate: v === LAYOUT_SELECT_DUP_VALUE
            });
          }
        } catch {
        }
        return;
      }
      if (this._editMode) {
        try {
          const panel = typeof this._getConfigPanel === "function" ? this._getConfigPanel() : null;
          if (panel && typeof panel.selectLayoutByValue === "function") {
            await panel.selectLayoutByValue(v);
            try {
              api?.setValue?.(this._layoutSelectValueForCurrent(), { silent: true });
            } catch {
            }
            return;
          }
        } catch {
        }
      }
      try {
        const familyId = parseBuiltinFamilySelectValue(v);
        if (familyId) {
          await setSettings({
            currentKeyboardLayoutId: "builtin",
            keyboardLayoutFamilyId: familyId
          });
        } else {
          await setSettings({ currentKeyboardLayoutId: v });
        }
      } catch {
      }
    }
    /**
     * Apply a titlebar layout-menu selection received from the service worker.
     * @param {string} value
     */
    selectLayoutByValue(value) {
      this._ensure();
      void this._onLayoutSelectChange(value);
    }
    /**
     * Current select value for the active layout (builtin family or user id).
     * While editing, follows the Config panel's selection rather than the live current layout.
     * @returns {string}
     */
    _layoutSelectValueForCurrent() {
      if (this._editMode && this._editLayoutState) {
        const st = this._editLayoutState;
        if (st.mode === "user" && st.userLayoutId) return `user:${st.userLayoutId}`;
        try {
          const inferred = inferFamilyAndHandednessFromLayoutId(
            st.builtinLayoutId || this.layoutId
          );
          return builtinFamilySelectValue(inferred.familyId);
        } catch {
        }
      }
      const sel = String(this._currentKeyboardLayoutId || "builtin");
      if (sel.startsWith("user:")) return sel;
      const familyId = normalizeKeyboardLayoutFamilyId(
        this._getKeyPilot?.()?._settings?.keyboardLayoutFamilyId
      );
      return builtinFamilySelectValue(familyId);
    }
    /**
     * Mount or replace the titlebar layout picker with the custom select control.
     * @param {Element|null} existing
     * @param {Element|null} header
     * @param {Node|null} [beforeNode]
     * @returns {HTMLElement|null}
     */
    _installLayoutSelect(existing, header, beforeNode = null) {
      if (this._layoutSelectApi && this._layoutSelectEl?.isConnected) {
        this._wireLayoutSelect(this._layoutSelectEl);
        return this._layoutSelectEl;
      }
      try {
        this._layoutSelectApi?.destroy?.();
      } catch {
      }
      this._layoutSelectApi = null;
      const menu = createSelectMenu({
        doc: document,
        ariaLabel: "Current keyboard layout",
        variant: "titlebar",
        value: this._layoutSelectValueForCurrent(),
        onChange: (value) => {
          void this._onLayoutSelectChange(value);
        }
      });
      menu.root.setAttribute("data-kp-floating-keyboard-layout-select", "true");
      this._layoutSelectApi = menu;
      this._layoutSelectEl = menu.root;
      const parent = existing && existing.parentNode || header;
      if (existing && existing.parentNode) {
        try {
          existing.parentNode.replaceChild(menu.root, existing);
        } catch {
          try {
            parent?.appendChild?.(menu.root);
          } catch {
          }
        }
      } else if (parent) {
        try {
          const hintNode = beforeNode || header && header.querySelector?.('[data-kp-floating-keyboard-hint="true"]');
          if (hintNode && hintNode.parentNode === parent) parent.insertBefore(menu.root, hintNode);
          else parent.appendChild(menu.root);
        } catch {
          try {
            parent.appendChild(menu.root);
          } catch {
          }
        }
      }
      this._wireLayoutSelect(menu.root);
      return menu.root;
    }
    /**
     * Status/action chip shown only for the left-handed layout. It intentionally
     * lives beside the picker so the active mapping is visible even for custom
     * layouts, whose names do not otherwise encode handedness.
     * @param {Element|null} header
     * @param {HTMLElement|null} layoutSelect
     */
    _ensureHandednessChip(header, layoutSelect) {
      if (!header || !layoutSelect) return null;
      let chip = this._handednessChip || header.querySelector?.('[data-kp-floating-keyboard-handedness-chip="true"]');
      if (!chip) {
        chip = document.createElement("button");
        chip.type = "button";
        chip.hidden = true;
        chip.setAttribute("data-kp-floating-keyboard-handedness-chip", "true");
        chip.setAttribute("aria-label", "Turn off left-handed layout");
        chip.title = "Turn off left-handed layout";
        Object.assign(chip.style, {
          display: "none",
          alignItems: "center",
          gap: "3px",
          flex: "0 0 auto",
          border: "1px solid rgba(155, 205, 255, 0.58)",
          borderRadius: "10px",
          padding: "1px 6px",
          color: "#dceeff",
          background: "rgba(44, 104, 166, 0.38)",
          font: "600 10px/16px inherit",
          whiteSpace: "nowrap",
          cursor: "pointer"
        });
        const label = document.createElement("span");
        label.textContent = "Lefthanded";
        const close = document.createElement("span");
        close.textContent = "\xD7";
        close.setAttribute("aria-hidden", "true");
        close.setAttribute("data-kp-handedness-chip-close", "true");
        Object.assign(close.style, { display: "none", fontSize: "13px", lineHeight: "12px" });
        chip.append(label, close);
        chip.addEventListener("pointerdown", (event) => event.stopPropagation(), true);
        chip.addEventListener("mouseenter", () => {
          close.style.display = "inline";
        });
        chip.addEventListener("mouseleave", () => {
          close.style.display = "none";
        });
        chip.addEventListener("click", this._onHandednessChipClick);
      }
      if (chip.previousSibling !== layoutSelect) {
        try {
          header.insertBefore(chip, layoutSelect.nextSibling);
        } catch {
        }
      }
      this._handednessChip = chip;
      return chip;
    }
    /**
     * @param {any} rawHandedness
     */
    _syncHandednessChip(rawHandedness) {
      const chip = this._handednessChip;
      if (!chip) return;
      const leftHanded = normalizeKeyboardHandedness(rawHandedness) === "left";
      chip.hidden = !leftHanded;
      chip.style.display = leftHanded ? "inline-flex" : "none";
    }
    async _onHandednessChipClick(event) {
      try {
        event?.stopPropagation?.();
      } catch {
      }
      const kp = this._getKeyPilot?.();
      try {
        if (typeof kp?.setKeyboardHandedness === "function") {
          await kp.setKeyboardHandedness("right");
        } else {
          await setSettings({ keyboardHandedness: "right" });
        }
      } catch {
      }
      this._syncHandednessChip("right");
      this._render();
    }
    /**
     * Wire (or re-wire) the layout select once.
     * @param {HTMLElement|null} layoutSelect
     */
    _wireLayoutSelect(layoutSelect) {
      if (!layoutSelect) return;
      if (layoutSelect.dataset.kpLayoutSelectWired === "true") return;
      layoutSelect.dataset.kpLayoutSelectWired = "true";
      layoutSelect.addEventListener("pointerdown", (e) => e.stopPropagation(), true);
      layoutSelect.addEventListener("mousedown", (e) => e.stopPropagation(), true);
    }
    async _refreshLayoutSelectOptions() {
      const api = this._layoutSelectApi;
      if (!api) return;
      try {
        const layouts = await listUserKeyboardLayouts();
        const groups = listLayoutPickerGroups(layouts);
        const options = [];
        const known = /* @__PURE__ */ new Set();
        const appendGroup = (heading, items) => {
          if (options.length) options.push({ type: "separator" });
          options.push({ type: "group", label: heading });
          for (const item of items || []) {
            if (!item?.value) continue;
            options.push({
              value: item.value,
              label: item.label,
              icon: item.icon,
              shortcut: item.shortcut
            });
            known.add(item.value);
          }
        };
        appendGroup("Built-In", groups.builtin);
        known.add("builtin");
        appendGroup("Custom", groups.custom);
        if (!this._editMode) {
          appendGroup("Keyboard Layout Editor", [
            { value: LAYOUT_SELECT_EDIT_VALUE, label: "Edit Keyboard Layout\u2026", shortcut: "Alt + C" },
            { value: LAYOUT_SELECT_NEW_VALUE, label: "New Blank Keyboard Layout" },
            { value: LAYOUT_SELECT_DUP_VALUE, label: "New Duplicate Keyboard Layout" }
          ]);
        }
        appendGroup("KeyPilot", [
          { value: LAYOUT_SELECT_ONBOARDING_VALUE, label: "Onboarding Tutorial", shortcut: "Alt + I" },
          { value: LAYOUT_SELECT_DOCS_VALUE, label: "KeyPilot Documentation", shortcut: "Alt + H" },
          { value: LAYOUT_SELECT_SETTINGS_VALUE, label: "KeyPilot Settings", shortcut: "'" }
        ]);
        let v = this._layoutSelectValueForCurrent();
        if (!known.has(v) && !String(v).startsWith("user:")) {
          v = builtinFamilySelectValue("browsing");
          if (!this._editMode) {
            this._currentKeyboardLayoutId = "builtin";
            this._currentUserLayout = null;
          }
        } else if (String(v).startsWith("user:") && !known.has(v)) {
          v = builtinFamilySelectValue("browsing");
          if (!this._editMode) {
            this._currentKeyboardLayoutId = "builtin";
            this._currentUserLayout = null;
          }
        }
        api.setOptions(options);
        api.setValue(v, { silent: true });
      } catch {
      }
    }
    _earlyCustomPaintMatches(sel) {
      const wanted = String(sel || "");
      if (!wanted.startsWith("user:") || !this.keyboardContainer) return false;
      let visual = null;
      try {
        visual = this.keyboardContainer.querySelector(":scope > .keyboard-visual");
      } catch {
      }
      if (!visual) {
        try {
          visual = this.keyboardContainer.querySelector(".keyboard-visual");
        } catch {
        }
      }
      if (!visual || !visual.dataset) return false;
      return visual.dataset.kpKeyboardBuilt === "true" && String(visual.dataset.kpLayoutId || "") === wanted;
    }
    async _renderAsync() {
      const token = ++this._renderToken;
      if (!this.keyboardContainer) return;
      await this._refreshLayoutSelectOptions();
      let settings = null;
      try {
        settings = await getSettings();
      } catch {
      }
      if (token !== this._renderToken) return;
      this._syncHandednessChip(settings?.keyboardHandedness);
      if (this._editMode) {
        await this._renderEditModeKeyboard({ token, settings });
        return;
      }
      const selRaw = String(settings && settings.currentKeyboardLayoutId || this._currentKeyboardLayoutId || "builtin");
      let sel = selRaw;
      this._currentKeyboardLayoutId = sel;
      try {
        if (this._layoutTitleEl) {
          const nextTitle = this._textModeFilterActive && !this._editMode ? "Keyboard Reference \u2014 Typing" : "Keyboard Reference";
          if (this._layoutTitleEl.textContent !== nextTitle) {
            this._layoutTitleEl.textContent = nextTitle;
          }
        }
      } catch {
      }
      if (!sel.startsWith("user:")) {
        try {
          const showNumberRow = !!(settings && settings.keyboardReferenceShowNumberRow);
          const handedness = normalizeKeyboardHandedness(settings?.keyboardHandedness);
          const layoutId = resolveKeyboardLayoutId({
            familyId: settings?.keyboardLayoutFamilyId,
            handedness
          }) || this.layoutId || "browsing-right";
          this.layoutId = layoutId;
          const uiLayout = getKeyboardUiLayoutForLayout(layoutId, {
            includeNumberRow: showNumberRow
          });
          this.keyboardLayout = uiLayout;
          const keybindings = buildEffectiveKeybindings(layoutId, handedness);
          this.keybindings = keybindings;
          renderKeybindingsKeyboard({
            container: this.keyboardContainer,
            keybindings,
            keyboardLayout: uiLayout,
            layoutId,
            attachPopovers: true
          });
          this._rebuildKeyIndex();
        } catch (e) {
          this.keyboardContainer.textContent = "Unable to render keyboard reference on this page.";
          console.warn("[KeyPilot] Failed to render floating keyboard reference:", e);
        }
        return;
      }
      try {
        if (this._reuseEarlyCustomPaint && this._earlyCustomPaintMatches(sel)) {
          this._reuseEarlyCustomPaint = false;
          const id2 = sel.slice("user:".length);
          const userLayout2 = this._currentUserLayout && this._currentUserLayout.id === id2 ? this._currentUserLayout : await getUserKeyboardLayoutById(id2);
          const macros2 = Array.isArray(this._currentUserMacros) && this._currentUserMacros.length ? this._currentUserMacros : await listUserMacros();
          const actions2 = Array.isArray(this._currentUserActions) && this._currentUserActions.length ? this._currentUserActions : await listUserActions();
          if (token !== this._renderToken) return;
          if (userLayout2) {
            this._currentUserLayout = userLayout2;
            this._currentUserMacros = macros2;
            this._currentUserActions = actions2;
            const baseId2 = String(userLayout2.baseBuiltinLayoutId || this.layoutId || "browsing-right");
            const showNumberRow2 = !!(settings && settings.keyboardReferenceShowNumberRow);
            this.layoutId = baseId2;
            this.keyboardLayout = getKeyboardUiLayoutForLayout(baseId2, { includeNumberRow: showNumberRow2 });
            const baseHand2 = inferFamilyAndHandednessFromLayoutId(baseId2).handedness;
            this.keybindings = buildEffectiveKeybindings(baseId2, baseHand2);
            try {
              attachKeyPopoverBehavior({ root: this.keyboardContainer, keybindings: this.keybindings });
            } catch {
            }
            this._rebuildKeyIndex();
            return;
          }
        }
        const id = sel.slice("user:".length);
        const userLayout = this._currentUserLayout && this._currentUserLayout.id === id ? this._currentUserLayout : await getUserKeyboardLayoutById(id);
        const macros = Array.isArray(this._currentUserMacros) && this._currentUserMacros.length ? this._currentUserMacros : await listUserMacros();
        const actions = Array.isArray(this._currentUserActions) && this._currentUserActions.length ? this._currentUserActions : await listUserActions();
        if (token !== this._renderToken) return;
        if (!userLayout) {
          this._currentKeyboardLayoutId = "builtin";
          this._currentUserLayout = null;
          try {
            await setSettings({ currentKeyboardLayoutId: "builtin" });
          } catch {
          }
          try {
            if (this._layoutTitleEl) {
              this._layoutTitleEl.textContent = this._textModeFilterActive && !this._editMode ? "Keyboard Reference \u2014 Typing" : "Keyboard Reference";
            }
          } catch {
          }
          try {
            const showNumberRow2 = !!(settings && settings.keyboardReferenceShowNumberRow);
            const handedness = normalizeKeyboardHandedness(settings?.keyboardHandedness);
            const layoutId = resolveKeyboardLayoutId({
              familyId: settings?.keyboardLayoutFamilyId,
              handedness
            }) || this.layoutId || "browsing-right";
            this.layoutId = layoutId;
            const uiLayout2 = getKeyboardUiLayoutForLayout(layoutId, {
              includeNumberRow: showNumberRow2
            });
            this.keyboardLayout = uiLayout2;
            const keybindings = buildEffectiveKeybindings(layoutId, handedness);
            this.keybindings = keybindings;
            renderKeybindingsKeyboard({
              container: this.keyboardContainer,
              keybindings,
              keyboardLayout: uiLayout2,
              layoutId,
              attachPopovers: true
            });
            this._rebuildKeyIndex();
            await this._refreshLayoutSelectOptions();
          } catch (e) {
            this.keyboardContainer.textContent = "Unable to render keyboard reference on this page.";
            console.warn("[KeyPilot] Failed to render floating keyboard reference after layout fallback:", e);
          }
          return;
        }
        const baseId = String(userLayout.baseBuiltinLayoutId || this.layoutId || "browsing-right");
        const showNumberRow = !!(settings && settings.keyboardReferenceShowNumberRow);
        const uiLayout = getKeyboardUiLayoutForLayout(baseId, { includeNumberRow: showNumberRow });
        const baseHand = inferFamilyAndHandednessFromLayoutId(baseId).handedness;
        const baseKb = buildEffectiveKeybindings(baseId, baseHand);
        this._renderSlotKeyboard({
          container: this.keyboardContainer,
          uiLayout,
          slots: userLayout.slots || {},
          macros,
          actions,
          keybindings: baseKb,
          editMode: false
        });
        this._rebuildKeyIndex();
      } catch (e) {
        this.keyboardContainer.textContent = "Unable to render custom keyboard layout.";
        console.warn("[KeyPilot] Failed to render custom keyboard reference:", e);
      }
    }
    /**
     * @param {{ token: number, settings: any }} params
     */
    async _renderEditModeKeyboard({ token, settings }) {
      const st = this._editLayoutState;
      const mode = st?.mode === "user" ? "user" : "builtin";
      const readOnly = mode !== "user";
      try {
        if (this._layoutTitleEl) {
          this._layoutTitleEl.textContent = "Keyboard Reference";
        }
      } catch {
      }
      let slots = {};
      let macros = Array.isArray(st?.macros) ? st.macros : this._currentUserMacros || [];
      let actions = Array.isArray(st?.actions) ? st.actions : this._currentUserActions || [];
      let userLayout = null;
      let baseId = "";
      if (mode === "user" && st?.userLayout) {
        userLayout = st.userLayout;
        slots = userLayout.slots && typeof userLayout.slots === "object" ? userLayout.slots : {};
        baseId = String(userLayout.baseBuiltinLayoutId || "");
      } else {
        for (const [actionId, binding] of Object.entries(this.keybindings || {})) {
          const slot = physicalSlotLabelFromBinding(binding);
          if (!slot) continue;
          slots[slot] = { type: "function", id: String(actionId) };
        }
      }
      if (!baseId) {
        const handedness = normalizeKeyboardHandedness(settings?.keyboardHandedness);
        baseId = resolveKeyboardLayoutId({
          familyId: settings?.keyboardLayoutFamilyId,
          handedness
        }) || this.layoutId || "browsing-right";
      }
      if (!macros.length) {
        try {
          macros = await listUserMacros();
        } catch {
          macros = [];
        }
      }
      if (!actions.length) {
        try {
          actions = await listUserActions();
        } catch {
          actions = [];
        }
      }
      if (token !== this._renderToken) return;
      const showNumberRow = !!(settings && settings.keyboardReferenceShowNumberRow);
      const uiLayout = getKeyboardUiLayoutForLayout(baseId, { includeNumberRow: showNumberRow });
      const baseHand = inferFamilyAndHandednessFromLayoutId(baseId).handedness;
      const baseKb = buildEffectiveKeybindings(baseId, baseHand);
      this._renderSlotKeyboard({
        container: this.keyboardContainer,
        uiLayout,
        slots,
        macros,
        actions,
        keybindings: baseKb,
        editMode: true,
        readOnly,
        userLayout
      });
      this._rebuildKeyIndex();
    }
    /**
     * Render a user layout from physical key slots (Functions/Action Instances/macros assigned to
     * keys).
     * @param {{
     *   container: HTMLElement,
     *   uiLayout: any[],
     *   slots: Record<string, any>,
     *   macros?: any[],
     *   actions?: any[],
     *   keybindings?: Record<string, any>,
     *   editMode?: boolean,
     *   readOnly?: boolean,
     *   userLayout?: any|null
     * }} params
     */
    _renderSlotKeyboard({
      container,
      uiLayout,
      slots,
      macros,
      actions,
      keybindings,
      editMode = false,
      readOnly = true,
      userLayout = null
    } = {}) {
      if (!container) return;
      const doc = container.ownerDocument || document;
      try {
        this._slotDnDAbort?.abort?.();
      } catch {
      }
      this._slotDnDAbort = null;
      try {
        ensureStylesInjected(container.getRootNode?.() || doc);
      } catch {
      }
      try {
        detachKeyPopoverBehavior(container);
      } catch {
      }
      container.innerHTML = "";
      const visual = doc.createElement("div");
      visual.className = `keyboard-visual ${KEYBINDINGS_UI_ROOT_CLASS}${editMode ? " kp-kb-edit-hatch" : ""}`;
      container.appendChild(visual);
      const macroLabelById = /* @__PURE__ */ new Map();
      try {
        for (const m of macros || []) {
          if (m && m.id) macroLabelById.set(String(m.id), String(m.label || "Macro"));
        }
      } catch {
      }
      const actionById = /* @__PURE__ */ new Map();
      try {
        for (const a of actions || []) {
          if (a && a.id) actionById.set(String(a.id), a);
        }
      } catch {
      }
      const resolveFunctionSlot = (id) => {
        const key = String(id || "");
        if (key.startsWith("action:")) {
          const instance = actionById.get(key);
          const functionId = String(instance?.functionId || "");
          const def2 = functionId ? getFunctionDef(functionId) : null;
          return {
            label: String(instance?.label || def2?.label || "Configured Function"),
            keyboardClass: String(def2?.keyboardClass || ""),
            // Prefer the bound Function id so icon CSS / popovers / link hints match built-in keys.
            functionId: functionId || key
          };
        }
        const def = getFunctionDef(key);
        return {
          label: String(def?.label || key),
          keyboardClass: String(def?.keyboardClass || ""),
          functionId: key
        };
      };
      const kb = keybindings || this.keybindings || {};
      const actionSlotLabelFromItem = (item) => {
        const binding = kb && kb[item.id];
        return physicalSlotLabelFromBinding(binding);
      };
      const editable = !!(editMode && !readOnly && userLayout && typeof userLayout.slots === "object");
      const markReadonly = !!(editMode && readOnly);
      const placeActive = !!(editMode && this._placeItem && typeof this._onPlaceSlot === "function");
      const placeItem = placeActive ? this._placeItem : null;
      const placeHoverSlot = placeActive ? this._placeHoverSlot : null;
      const persistSlots = async () => {
        if (!editable || !userLayout) return;
        try {
          const saved = await upsertUserKeyboardLayout(userLayout);
          if (this._editLayoutState) {
            this._editLayoutState.userLayout = saved;
            this._editLayoutState.mode = "user";
            this._editLayoutState.userLayoutId = saved.id;
          }
          try {
            this._onLayoutPersisted?.(saved);
          } catch {
          }
        } catch {
        }
      };
      const applyDropToSlot = async (slotLabel, data) => {
        if (!editable || !userLayout) return;
        const slotMap = userLayout.slots;
        const fromSlot = data.fromSlot ? String(data.fromSlot) : "";
        const nextItem = { type: String(data.type), id: String(data.id) };
        if (nextItem.type !== "function" && nextItem.type !== "macro") return;
        if (!nextItem.id) return;
        const targetPrev = slotMap[slotLabel] || null;
        if (fromSlot && fromSlot !== slotLabel) {
          slotMap[slotLabel] = nextItem;
          slotMap[fromSlot] = targetPrev;
        } else {
          slotMap[slotLabel] = nextItem;
        }
        await persistSlots();
        this._render();
      };
      const clearSlot = async (slotLabel) => {
        if (!editable || !userLayout) return;
        userLayout.slots[slotLabel] = null;
        await persistSlots();
        this._render();
      };
      const renderSlot = (slotLabel, assigned, extraClass = "") => {
        const previewing = !!(placeItem && placeHoverSlot === slotLabel);
        const displayAssigned = previewing ? placeItem : assigned;
        const btn = doc.createElement("button");
        btn.type = "button";
        btn.tabIndex = -1;
        let keyboardClass = "";
        let resolvedFn = null;
        if (displayAssigned && displayAssigned.type === "function") {
          resolvedFn = resolveFunctionSlot(displayAssigned.id);
          keyboardClass = resolvedFn.keyboardClass;
        } else if (displayAssigned && displayAssigned.type === "macro") {
          keyboardClass = "key-purple";
        }
        const chromeClass = extraClass.split(/\s+/).filter((c) => c && c !== "key" && c !== keyboardClass).join(" ");
        btn.className = `key${keyboardClass ? " " + keyboardClass : ""}${chromeClass ? " " + chromeClass : ""}${previewing ? " kp-place-preview" : ""}`;
        btn.dataset.kpBaseClass = "key";
        btn.dataset.kpSlot = slotLabel;
        if (markReadonly && !placeActive) {
          btn.disabled = true;
          btn.setAttribute("data-kp-edit-readonly", "true");
        } else if (placeActive) {
          btn.disabled = false;
          btn.draggable = false;
        } else {
          btn.disabled = false;
          btn.draggable = !!(editMode && editable && assigned);
        }
        if (displayAssigned && displayAssigned.type === "function" && resolvedFn) {
          try {
            btn.setAttribute("data-kp-action-id", String(resolvedFn.functionId));
          } catch {
          }
          const binding = kb[resolvedFn.functionId];
          const aria = binding && (binding.description || binding.label) || resolvedFn.label;
          try {
            btn.removeAttribute("title");
          } catch {
          }
          btn.setAttribute("aria-label", aria);
        }
        if (displayAssigned && displayAssigned.type === "macro") {
          try {
            btn.setAttribute("data-kp-macro-id", String(displayAssigned.id));
          } catch {
          }
          const macroLabel = macroLabelById.get(String(displayAssigned.id)) || "Macro";
          try {
            btn.removeAttribute("title");
          } catch {
          }
          btn.setAttribute("aria-label", macroLabel);
        }
        if (displayAssigned && (displayAssigned.type === "function" || displayAssigned.type === "macro")) {
          ensureKeyBackgroundIcon(doc, btn);
        }
        const main = doc.createElement("div");
        main.className = "key-main";
        if (displayAssigned && displayAssigned.type === "macro") {
          main.textContent = macroLabelById.get(String(displayAssigned.id)) || "Macro";
        } else if (displayAssigned && displayAssigned.type === "function" && resolvedFn) {
          main.textContent = resolvedFn.label;
        } else {
          main.textContent = "";
        }
        const label = doc.createElement("div");
        label.className = "key-label";
        label.textContent = slotLabel;
        btn.appendChild(main);
        btn.appendChild(label);
        ensureKeyPressOverlay(doc, btn);
        if (editMode && editable && assigned && !placeActive) {
          const overlay = doc.createElement("div");
          overlay.className = "kp-key-delete-overlay";
          const del = doc.createElement("button");
          del.type = "button";
          del.className = "kp-key-delete";
          del.textContent = "\xD7";
          del.setAttribute("aria-label", `Remove action from ${slotLabel}`);
          del.title = "Remove";
          del.addEventListener("click", (e) => {
            e.preventDefault();
            e.stopPropagation();
            void clearSlot(slotLabel);
          }, true);
          del.addEventListener("pointerdown", (e) => e.stopPropagation(), true);
          del.addEventListener("mousedown", (e) => e.stopPropagation(), true);
          overlay.appendChild(del);
          btn.appendChild(overlay);
        }
        if (placeActive) {
          btn.addEventListener("pointerenter", () => {
            this.setPlaceHoverSlot(slotLabel);
          }, true);
          btn.addEventListener("pointerleave", () => {
            if (this._placeHoverSlot === slotLabel) this.setPlaceHoverSlot(null);
          }, true);
          btn.addEventListener("click", (e) => {
            e.preventDefault();
            e.stopPropagation();
            try {
              this._onPlaceSlot?.(slotLabel);
            } catch {
            }
          }, true);
        } else if (editMode && editable) {
          const clearOtherDropTargets = () => {
            try {
              visual.querySelectorAll(".key.kp-drop-target").forEach((el2) => {
                if (el2 !== btn) el2.classList.remove("kp-drop-target");
              });
            } catch {
            }
          };
          const markDropTarget = (e) => {
            e.preventDefault();
            try {
              const allowed = String(e.dataTransfer?.effectAllowed || "");
              e.dataTransfer.dropEffect = allowed === "move" ? "move" : "copy";
            } catch {
            }
            clearOtherDropTargets();
            btn.classList.add("kp-drop-target");
          };
          btn.addEventListener("dragenter", markDropTarget, true);
          btn.addEventListener("dragover", markDropTarget, true);
          btn.addEventListener("dragleave", (e) => {
            const related = e.relatedTarget;
            if (related instanceof Node && btn.contains(related)) return;
            btn.classList.remove("kp-drop-target");
          }, true);
          btn.addEventListener("drop", (e) => {
            e.preventDefault();
            btn.classList.remove("kp-drop-target");
            try {
              visual.querySelectorAll(".key.kp-drop-target").forEach((el2) => {
                el2.classList.remove("kp-drop-target");
              });
            } catch {
            }
            try {
              const raw = e.dataTransfer?.getData?.(KP_LAYOUT_ITEM_MIME) || "";
              const data = raw ? JSON.parse(raw) : null;
              if (!data || !data.type || !data.id) return;
              void applyDropToSlot(slotLabel, data);
            } catch {
            }
          }, true);
          btn.addEventListener("dragstart", (e) => {
            if (!assigned) return;
            try {
              const payload = JSON.stringify({ type: assigned.type, id: assigned.id, fromSlot: slotLabel });
              e.dataTransfer?.setData?.(KP_LAYOUT_ITEM_MIME, payload);
              e.dataTransfer.effectAllowed = "move";
            } catch {
            }
          }, true);
        }
        return btn;
      };
      for (const row of uiLayout || []) {
        const rowEl = doc.createElement("div");
        rowEl.className = "keyboard-row";
        visual.appendChild(rowEl);
        for (const item of row || []) {
          if (!item) continue;
          if (item.type === "special") {
            const sp = doc.createElement("div");
            sp.className = String(item.className || "key");
            const text = doc.createElement("span");
            text.className = "key-text";
            text.textContent = String(item.text || "");
            sp.appendChild(text);
            ensureKeyPressOverlay(doc, sp);
            rowEl.appendChild(sp);
            continue;
          }
          let extraClass = "";
          let slotLabel = "";
          if (item.type === "action" && (item.id === "DELETE" || String(item.className || "").includes("key-backspace"))) {
            slotLabel = "Backspace";
            extraClass = String(item.className || "key key-backspace");
          } else if (item.type === "key") {
            slotLabel = String(item.text || "").trim().toUpperCase();
          } else if (item.type === "action") {
            slotLabel = actionSlotLabelFromItem(item);
          }
          if (!slotLabel) {
            const empty = doc.createElement("div");
            empty.className = "key";
            empty.style.visibility = "hidden";
            rowEl.appendChild(empty);
            continue;
          }
          const assigned = slots && typeof slots === "object" ? slots[slotLabel] || null : null;
          rowEl.appendChild(renderSlot(slotLabel, assigned, extraClass));
        }
      }
      if (editMode && editable) {
        try {
          const ac = new AbortController();
          this._slotDnDAbort = ac;
          const clearDropTargets = () => {
            try {
              visual.querySelectorAll(".key.kp-drop-target").forEach((el2) => {
                el2.classList.remove("kp-drop-target");
              });
            } catch {
            }
          };
          doc.addEventListener("dragend", clearDropTargets, { capture: true, signal: ac.signal });
          visual.addEventListener("dragleave", (e) => {
            const related = e.relatedTarget;
            if (related instanceof Node && visual.contains(related)) return;
            clearDropTargets();
          }, { capture: true, signal: ac.signal });
        } catch {
        }
      }
      if (!editMode) {
        try {
          attachKeyPopoverBehavior({ root: container, keybindings: kb });
        } catch {
        }
      }
    }
    /**
     * Public entry points used by KeyPilot's capture-phase handler.
     * KeyPilot calls stopImmediatePropagation() on claimed shortcuts, which would
     * otherwise prevent document-level listeners registered later from seeing keydown.
     * @param {KeyboardEvent} e
     */
    reflectKeyDown(e) {
      this._onDocKeyDown(e);
    }
    /**
     * @param {KeyboardEvent} e
     */
    reflectKeyUp(e) {
      this._onDocKeyUp(e);
    }
    _bindKeydownFeedback() {
      if (this._keydownBound) return;
      try {
        document.addEventListener("keydown", this._onDocKeyDown, true);
        document.addEventListener("keyup", this._onDocKeyUp, true);
        window.addEventListener("blur", this._onWinBlur, true);
        this._keydownBound = true;
      } catch {
      }
    }
    _unbindKeydownFeedback() {
      if (!this._keydownBound) return;
      try {
        document.removeEventListener("keydown", this._onDocKeyDown, true);
      } catch {
      }
      try {
        document.removeEventListener("keyup", this._onDocKeyUp, true);
      } catch {
      }
      try {
        window.removeEventListener("blur", this._onWinBlur, true);
      } catch {
      }
      this._keydownBound = false;
      this._clearPressed();
    }
    _bindSettingsSync() {
      if (this._settingsBound) return;
      try {
        if (chrome?.storage?.onChanged?.addListener) {
          chrome.storage.onChanged.addListener(this._onStorageChanged);
          this._settingsBound = true;
        }
      } catch {
      }
    }
    _unbindSettingsSync() {
      if (!this._settingsBound) return;
      try {
        chrome?.storage?.onChanged?.removeListener?.(this._onStorageChanged);
      } catch {
      }
      this._settingsBound = false;
    }
    async _refreshKeyFeedbackSetting() {
      try {
        const settings = await getSettings();
        this._setKeyFeedbackEnabled(!!settings.keyboardReferenceKeyFeedback);
      } catch {
      }
    }
    _onStorageChanged(changes, area) {
      try {
        if (area !== "sync" && area !== "local") return;
        const entry = changes && changes[SETTINGS_STORAGE_KEY];
        if (!entry || !entry.newValue) return;
        this._setKeyFeedbackEnabled(!!entry.newValue.keyboardReferenceKeyFeedback);
        if (Object.prototype.hasOwnProperty.call(entry.newValue, "keyboardReferenceCollapsed")) {
          this.setCollapsed(!!entry.newValue.keyboardReferenceCollapsed, { persist: false });
        }
        const nextPos = entry.newValue.panelPositions?.keyboardReference;
        if (nextPos && typeof nextPos === "object") {
          this._suppressPositionPersist = true;
          this._setPanelPosition(nextPos, { persist: false });
          this._suppressPositionPersist = false;
        }
        try {
          if (typeof entry.newValue.currentKeyboardLayoutId === "string") {
            this._currentKeyboardLayoutId = entry.newValue.currentKeyboardLayoutId;
          }
          if (this.root && !this.root.hidden) this._render();
        } catch {
        }
      } catch {
      }
    }
    _setKeyFeedbackEnabled(enabled) {
      const next = !!enabled;
      if (this._keyFeedbackEnabled === next) return;
      this._keyFeedbackEnabled = next;
      if (!next) this._clearPressed();
    }
    _onWinBlur() {
      this._clearPressed();
    }
    _normalizeLabel(s) {
      return String(s || "").trim().toUpperCase();
    }
    /**
     * Normalize an event/binding key token into the label(s) used on the keyboard UI.
     * @param {string} token
     * @returns {string[]}
     */
    _labelsFromToken(token) {
      const raw = String(token || "").trim();
      if (!raw || raw === " ") return [];
      const upper = this._normalizeLabel(raw);
      if (!upper) return [];
      if (upper === "CAPSLOCK" || upper === "CAPS") return ["CAPS"];
      if (upper === "ESCAPE" || upper === "ESC") return ["ESC"];
      if (upper === "CONTROL" || upper === "CTRL" || upper === "ALT" || upper === "META" || upper === "OS") {
        return [];
      }
      if (upper === "SHIFT") return ["SHIFT"];
      if (upper === "ENTER" || upper === "RETURN") return ["ENTER"];
      if (upper === "TAB") return ["TAB"];
      if (upper === "BACKSPACE") return ["BACKSPACE"];
      if (upper === "SEMICOLON") return [";"];
      if (upper === "QUOTE") return ["'"];
      if (upper === "BACKQUOTE" || upper === "BACKTICK") return ["`"];
      if (upper === "BRACKETLEFT") return ["["];
      if (upper === "BRACKETRIGHT") return ["]"];
      if (upper === "COMMA") return [","];
      if (upper === "PERIOD") return ["."];
      if (upper === "SLASH") return ["/"];
      if (upper === "MINUS") return ["-"];
      if (upper === "EQUAL") return ["="];
      if (upper === "BACKSLASH") return ["\\"];
      if (upper === ":") return [";"];
      if (upper === "?") return ["/"];
      if (upper === ">") return ["."];
      if (upper === "<") return [","];
      if (upper === '"') return ["'"];
      if (upper === "~") return ["`"];
      if (upper === "{") return ["["];
      if (upper === "}") return ["]"];
      if (upper === "_") return ["-"];
      if (upper === "+") return ["="];
      if (upper === "|") return ["\\"];
      if (upper === "!") return ["1"];
      if (upper === "@") return ["2"];
      if (upper === "#") return ["3"];
      if (upper === "$") return ["4"];
      if (upper === "%") return ["5"];
      if (upper === "^") return ["6"];
      if (upper === "&") return ["7"];
      if (upper === "*") return ["8"];
      if (upper === "(") return ["9"];
      if (upper === ")") return ["0"];
      return [upper];
    }
    _labelsFromKeyboardEvent(e) {
      const out = [];
      const seen = /* @__PURE__ */ new Set();
      const pushAll = (tokens) => {
        for (const t of tokens || []) {
          if (!t || seen.has(t)) continue;
          seen.add(t);
          out.push(t);
        }
      };
      const key = e && typeof e.key === "string" ? e.key : "";
      if (key) pushAll(this._labelsFromToken(key));
      const code = e && typeof e.code === "string" ? e.code : "";
      if (code) {
        if (/^Key[A-Z]$/i.test(code)) pushAll([code.slice(3).toUpperCase()]);
        else if (/^Digit[0-9]$/.test(code)) pushAll([code.slice(5)]);
        else pushAll(this._labelsFromToken(code));
      }
      return out;
    }
    /**
     * @param {Map<string, HTMLElement[]>} map
     * @param {string} label
     * @param {HTMLElement} keyEl
     */
    _indexLabel(map, label, keyEl) {
      const tokens = [];
      const norm = this._normalizeLabel(label);
      if (norm) tokens.push(norm);
      if (norm && /[/|,]/.test(norm)) {
        for (const part of norm.split(/[/|,]+/)) {
          const p = this._normalizeLabel(part);
          if (p) tokens.push(p);
        }
      }
      const expanded = /* @__PURE__ */ new Set();
      for (const t of tokens) {
        for (const alias of this._labelsFromToken(t)) {
          if (alias) expanded.add(alias);
        }
        if (t) expanded.add(t);
      }
      for (const token of expanded) {
        const arr = map.get(token) || [];
        if (!arr.includes(keyEl)) arr.push(keyEl);
        map.set(token, arr);
      }
    }
    _rebuildKeyIndex() {
      if (!this.keyboardContainer) return;
      const map = /* @__PURE__ */ new Map();
      const byAction = /* @__PURE__ */ new Map();
      const bindings = this.keybindings || {};
      const keyEls = this.keyboardContainer.querySelectorAll(".key");
      for (const keyEl of keyEls) {
        const labelEl = keyEl.querySelector?.(".key-label");
        const textEl = keyEl.querySelector?.(".key-text");
        if (labelEl && labelEl.textContent) {
          this._indexLabel(map, labelEl.textContent, keyEl);
        }
        if (textEl && textEl.textContent) {
          this._indexLabel(map, textEl.textContent, keyEl);
        }
        if (!labelEl && !textEl) {
          this._indexLabel(map, keyEl.textContent, keyEl);
        }
        const actionId = keyEl.dataset?.kpActionId ? String(keyEl.dataset.kpActionId) : "";
        if (actionId) {
          const arr = byAction.get(actionId) || [];
          if (!arr.includes(keyEl)) arr.push(keyEl);
          byAction.set(actionId, arr);
          const binding = bindings[actionId];
          const keys = binding && Array.isArray(binding.keys) ? binding.keys : [];
          for (const k of keys) {
            this._indexLabel(map, k, keyEl);
          }
          if (binding?.displayKey) this._indexLabel(map, binding.displayKey, keyEl);
          if (binding?.keyLabel) this._indexLabel(map, binding.keyLabel, keyEl);
        }
      }
      this._keyElsByLabel = map;
      this._keyElsByActionId = byAction;
      this._scrubPressOverlays();
      for (const label of this._pressedLabels) {
        const els = this._keyElsByLabel.get(label);
        if (!els) continue;
        for (const el2 of els) setKeyPressedState(el2, true);
      }
      if (this._linkHoverHintActive) {
        this._applyLinkHoverHintClasses(true);
      }
      if (this._textModeFilterActive) {
        this._applyTextModeFilterClasses(true);
      }
    }
    /**
     * While a text field has focus, switch the Keyboard Reference into typing mode:
     * plain keycaps (no function chrome) with an orange glow ring, and an orange-cast titlebar.
     * Countdown-aware actions ({@link TEXT_MODE_COUNTDOWN_ACTION_IDS}) light up with full
     * function chrome only while the hover countdown is armed via setTextModeActivateArmed(true).
     * @param {boolean} active
     */
    setTextModeFilter(active2) {
      const next = Boolean(active2);
      if (!next) {
        this._textModeCountdownArmed = false;
      }
      if (this._textModeFilterActive === next) {
        if (next && this.isVisible()) {
          this._applyTextModeChrome(true);
          this._applyTextModeFilterClasses(true);
        }
        return;
      }
      this._applyTextModeFilterClasses(false);
      this._textModeFilterActive = next;
      if (!next) this._textModeCountdownArmed = false;
      try {
        const kbRoot = this.shadowRoot?.querySelector?.(".kp-keybindings-ui") || this.keyboardContainer;
        if (kbRoot) {
          if (next) kbRoot.classList.add("kp-text-mode-filter");
          else kbRoot.classList.remove("kp-text-mode-filter");
        }
      } catch {
      }
      this._applyTextModeChrome(next);
      if (next && this.isVisible()) {
        this._applyTextModeFilterClasses(true);
      }
    }
    /**
     * During text mode, light up countdown-aware actions
     * ({@link TEXT_MODE_COUNTDOWN_ACTION_IDS}, currently Click Element) only while a
     * clickable is under the cursor and the hover-click countdown is running.
     * @param {boolean} armed
     */
    setTextModeActivateArmed(armed) {
      const next = Boolean(armed);
      if (!this._textModeFilterActive) {
        this._textModeCountdownArmed = false;
        return;
      }
      if (this._textModeCountdownArmed === next) {
        if (next && this.isVisible()) this._applyTextModeFilterClasses(true);
        return;
      }
      this._textModeCountdownArmed = next;
      if (this.isVisible()) this._applyTextModeFilterClasses(true);
    }
    /**
     * Replace the set of action ids that light up when the text-mode countdown is armed.
     * @param {Iterable<string>} actionIds
     */
    setTextModeCountdownActionIds(actionIds) {
      const next = /* @__PURE__ */ new Set();
      try {
        for (const id of actionIds || []) {
          const s = String(id || "").trim();
          if (s) next.add(s);
        }
      } catch {
      }
      this._textModeCountdownActionIds = next.size ? next : new Set(TEXT_MODE_COUNTDOWN_ACTION_IDS);
      if (this._textModeFilterActive && this.isVisible()) {
        this._applyTextModeFilterClasses(true);
      }
    }
    /**
     * @param {boolean} on
     */
    _applyTextModeFilterClasses(on) {
      try {
        const root = this.shadowRoot || this.keyboardContainer || this.root;
        if (!root) return;
        const countdownArmed = !!(on && this._textModeCountdownArmed);
        const liveIds = this._textModeCountdownActionIds instanceof Set ? this._textModeCountdownActionIds : TEXT_MODE_COUNTDOWN_ACTION_IDS;
        const keys = root.querySelectorAll(".key");
        for (const el2 of keys) {
          if (!el2) continue;
          el2.classList.remove("kp-key-text-mode-disabled");
          el2.classList.remove("kp-key-text-mode-active");
          if (!on) continue;
          const actionId = el2.getAttribute("data-kp-action-id") || "";
          if (countdownArmed && actionId && liveIds.has(actionId)) {
            el2.classList.add("kp-key-text-mode-active");
          } else {
            el2.classList.add("kp-key-text-mode-disabled");
          }
        }
        const kbRoot = this.shadowRoot?.querySelector?.(".kp-keybindings-ui") || this.keyboardContainer;
        if (kbRoot) {
          if (on) kbRoot.classList.add("kp-text-mode-filter");
          else kbRoot.classList.remove("kp-text-mode-filter");
        }
        if (on) this._applyTextModeChrome(true);
      } catch {
      }
    }
    /**
     * Highlight keyboard keys that activate/open a hovered page link.
     * @param {boolean} active
     * @param {string[]} [actionIds] defaults to click + popover link actions
     */
    setLinkHoverHints(active2, actionIds) {
      const next = Boolean(active2);
      const ids = Array.isArray(actionIds) && actionIds.length ? actionIds.map(String) : ["ACTIVATE", "OPEN_POPOVER", "PREVIEW_LINK_POPOVER", "ACTIVATE_NEW_TAB", "ACTIVATE_NEW_TAB_BACKGROUND"];
      this._applyLinkHoverHintClasses(false);
      this._linkHoverHintActive = next;
      this._linkHoverHintActionIds = new Set(next ? ids : []);
      if (next && this.isVisible()) {
        this._applyLinkHoverHintClasses(true);
      }
    }
    /**
     * @param {boolean} on
     */
    _applyLinkHoverHintClasses(on) {
      try {
        const ids = this._linkHoverHintActionIds;
        if (!ids || ids.size === 0) return;
        for (const actionId of ids) {
          const els = this._keyElsByActionId.get(actionId);
          if (!els) continue;
          for (const el2 of els) {
            if (on) el2.classList.add("kp-key-link-hint");
            else el2.classList.remove("kp-key-link-hint");
          }
        }
      } catch {
      }
    }
    _setPressed(label, pressed) {
      const norm = this._normalizeLabel(label);
      if (!norm) return;
      const els = this._keyElsByLabel.get(norm);
      if (!els) return;
      for (const el2 of els) {
        setKeyPressedState(el2, pressed);
      }
    }
    _clearPressed() {
      for (const label of this._pressedLabels) {
        this._setPressed(label, false);
      }
      this._pressedLabels.clear();
    }
    /**
     * Strip any leftover press-feedback classes/overlays on the current keyboard DOM
     * (e.g. early-inject shell before content re-render).
     */
    _scrubPressOverlays() {
      try {
        const root = this.keyboardContainer || this.root;
        if (!root?.querySelectorAll) return;
        root.querySelectorAll(".key.kp-key-pressed").forEach((el2) => {
          try {
            setKeyPressedState(el2, false);
          } catch {
          }
        });
        root.querySelectorAll(".key-press-overlay.is-on").forEach((el2) => {
          try {
            el2.classList.remove("is-on");
          } catch {
          }
        });
      } catch {
      }
    }
    _onDocKeyDown(e) {
      try {
        if (!this.isVisible()) return;
        if (!this._keyFeedbackEnabled) return;
        const labels = this._labelsFromKeyboardEvent(e);
        if (!labels || labels.length === 0) return;
        for (const label of labels) {
          if (this._pressedLabels.has(label)) continue;
          this._pressedLabels.add(label);
          this._setPressed(label, true);
        }
      } catch {
      }
    }
    _onDocKeyUp(e) {
      try {
        if (!this.isVisible()) return;
        if (!this._keyFeedbackEnabled) return;
        const labels = this._labelsFromKeyboardEvent(e);
        if (!labels || labels.length === 0) return;
        for (const label of labels) {
          this._pressedLabels.delete(label);
          this._setPressed(label, false);
        }
      } catch {
      }
    }
    _onCloseClick(e) {
      try {
        e.preventDefault();
        e.stopPropagation();
      } catch {
      }
      try {
        const kp = window?.__KeyPilotInstance;
        if (kp && typeof kp.applyKeyboardHelpVisibility === "function") {
          kp.applyKeyboardHelpVisibility(false, { persist: true });
          return;
        }
      } catch {
      }
      this.hide();
    }
  };
  if (typeof window !== "undefined") {
    window.FloatingKeyboardHelp = FloatingKeyboardHelp;
  }

  // src/ui/control-strip.js
  init_constants();
  init_settings_manager();

  // src/utils/chrome-layout-cache.js
  var CHROME_LAYOUT_CACHE_KEY = "kp_chrome_layout_v1";
  function peekChromeLayoutCache() {
    try {
      const raw = localStorage.getItem(CHROME_LAYOUT_CACHE_KEY);
      if (!raw) return null;
      const o = JSON.parse(raw);
      return o && typeof o === "object" && !Array.isArray(o) ? o : null;
    } catch {
      return null;
    }
  }
  function cacheChromeLayout(patch) {
    if (!patch || typeof patch !== "object") return;
    try {
      const prev = peekChromeLayoutCache() || {};
      const next = { ...prev };
      if (patch.panelPositions && typeof patch.panelPositions === "object") {
        next.panelPositions = {
          ...prev.panelPositions || {},
          ...patch.panelPositions
        };
      }
      if (patch.controlStrip && typeof patch.controlStrip === "object") {
        next.controlStrip = {
          ...prev.controlStrip || {},
          ...patch.controlStrip
        };
      }
      if (typeof patch.keyboardHelpVisible === "boolean") {
        next.keyboardHelpVisible = patch.keyboardHelpVisible;
      }
      if (typeof patch.keyboardReferenceCollapsed === "boolean") {
        next.keyboardReferenceCollapsed = patch.keyboardReferenceCollapsed;
      }
      localStorage.setItem(CHROME_LAYOUT_CACHE_KEY, JSON.stringify(next));
    } catch {
    }
  }

  // src/ui/control-strip.js
  var CONTROL_STRIP_ROOT_CLASS = "kp-control-strip";
  var DEFAULT_TOP_PX = 16;
  var DEFAULT_LEFT_PX = 16;
  var STRIP_HEIGHT_PX = 28;
  var STRIP_POSITION_MARGIN_PX = Math.max(PANEL_POSITION_MARGIN_PX, 16);
  var ControlStrip = class {
    /**
     * @param {ControlStripHandlers} [handlers]
     */
    constructor(handlers = {}) {
      this.root = null;
      this.shadowRoot = null;
      this._modulesEl = null;
      this._statusBtn = null;
      this._statusDot = null;
      this._statusLabel = null;
      this._moveBtn = null;
      this._keyboardBtn = null;
      this._settingsBtn = null;
      this._collapseBtn = null;
      this._closeBtn = null;
      this._enabled = true;
      this._textMode = false;
      this._collapsed = true;
      this._collapsedExplicit = false;
      this._keyboardActive = false;
      this._desiredVisible = true;
      this._panelPosition = { ...DEFAULT_SETTINGS.panelPositions.controlStrip };
      try {
        const cached = peekChromeLayoutCache();
        const pos = cached?.panelPositions?.controlStrip;
        if (pos && typeof pos === "object") {
          const left = Number(pos.left);
          const top = Number(pos.top);
          const hasFree = Number.isFinite(left) && Number.isFinite(top);
          this._panelPosition = {
            ...hasFree ? { left, top } : this._panelPosition,
            anchor: pos.anchor !== void 0 ? pos.anchor : hasFree ? null : this._panelPosition.anchor
          };
        }
        if (typeof cached?.keyboardHelpVisible === "boolean") {
          this._keyboardActive = cached.keyboardHelpVisible;
        }
      } catch {
      }
      this._suppressPositionPersist = false;
      this._dragDispose = null;
      this._dragBound = false;
      this._settingsBound = false;
      this._onStorageChanged = this._onStorageChanged.bind(this);
      this._handlers = {
        onToggleEnabled: typeof handlers.onToggleEnabled === "function" ? handlers.onToggleEnabled : null,
        onToggleKeyboard: typeof handlers.onToggleKeyboard === "function" ? handlers.onToggleKeyboard : null,
        onOpenSettings: typeof handlers.onOpenSettings === "function" ? handlers.onOpenSettings : null,
        onCollapseChange: typeof handlers.onCollapseChange === "function" ? handlers.onCollapseChange : null,
        onClose: typeof handlers.onClose === "function" ? handlers.onClose : null
      };
      this._onStatusClick = this._onStatusClick.bind(this);
      this._onKeyboardClick = this._onKeyboardClick.bind(this);
      this._onSettingsClick = this._onSettingsClick.bind(this);
      this._onCollapseClick = this._onCollapseClick.bind(this);
      this._onCloseClick = this._onCloseClick.bind(this);
      this._onDocMutate = this._onDocMutate.bind(this);
      this._onWinResize = this._onWinResize.bind(this);
      this._observer = null;
      this._observeBound = false;
      this._hostGuard = null;
    }
    /**
     * @param {Partial<ControlStripHandlers>} handlers
     */
    setHandlers(handlers = {}) {
      if (typeof handlers.onToggleEnabled === "function") this._handlers.onToggleEnabled = handlers.onToggleEnabled;
      if (typeof handlers.onToggleKeyboard === "function") this._handlers.onToggleKeyboard = handlers.onToggleKeyboard;
      if (typeof handlers.onOpenSettings === "function") this._handlers.onOpenSettings = handlers.onOpenSettings;
      if (typeof handlers.onCollapseChange === "function") this._handlers.onCollapseChange = handlers.onCollapseChange;
      if (typeof handlers.onClose === "function") this._handlers.onClose = handlers.onClose;
    }
    isVisible() {
      return !!(this.root && this.root.isConnected && this.root.hidden === false);
    }
    isCollapsed() {
      return !!this._collapsed;
    }
    show() {
      if (window !== window.top) return;
      this._desiredVisible = true;
      this._ensure();
      try {
        ensureChromeHostMounted(this.root);
      } catch {
      }
      this.root.hidden = false;
      this.root.style.display = "flex";
      this.root.style.pointerEvents = "auto";
      this._applyCollapsedLayout();
      this._bindDrag();
      this._bindSettingsSync();
      this._refreshPanelPosition();
      this._applyPanelPositionNow();
      this._syncOnboardingOffset();
      this._bindOnboardingWatch();
      this._bindHostGuard();
    }
    hide() {
      this._desiredVisible = false;
      this._unbindHostGuard();
      if (this.root) {
        this.root.hidden = true;
        this.root.style.display = "none";
        this.root.style.pointerEvents = "none";
      }
      this._unbindOnboardingWatch();
    }
    /**
     * Apply desired visibility without changing the persisted "desired" flag when already set.
     * @param {boolean} visible
     */
    setVisible(visible) {
      if (visible) this.show();
      else this.hide();
    }
    /**
     * @param {boolean} collapsed
     * @param {{ notify?: boolean }} [opts]
     */
    setCollapsed(collapsed, opts = {}) {
      const next = !!collapsed;
      this._collapsedExplicit = true;
      if (this._collapsed === next) {
        this._applyCollapsedLayout();
        this._bindDrag();
        return;
      }
      this._collapsed = next;
      this._applyCollapsedLayout();
      this._bindDrag();
      this._applyPanelPositionNow();
      this._syncOnboardingOffset();
      if (opts.notify !== false && this._handlers.onCollapseChange) {
        try {
          this._handlers.onCollapseChange(this._collapsed);
        } catch {
        }
      }
    }
    /**
     * @param {boolean} enabled
     */
    setEnabledState(enabled) {
      this._enabled = !!enabled;
      if (!this._enabled) this._textMode = false;
      this._renderStatus();
    }
    /**
     * Reflect text-field focus (text mode) on the On/Off status segment and strip chrome.
     * When active, the green ON indicator turns orange and the strip gets the same
     * orange titlebar cast as the Keyboard Reference window.
     * @param {boolean} active
     */
    setTextModeActive(active2) {
      const next = !!active2 && !!this._enabled;
      if (this._textMode === next) {
        this._renderStatus();
        return;
      }
      this._textMode = next;
      this._renderStatus();
    }
    /**
     * Apply / clear the shared text-mode orange cast on the strip root.
     * Status segment stays transparent so the cast (and titlebar bevel) show through.
     * @param {boolean} textMode
     */
    _applyTextModeChrome(textMode) {
      if (!this.root || !this.root.style) return;
      try {
        if (textMode) this.root.setAttribute("data-kp-text-mode", "true");
        else this.root.removeAttribute("data-kp-text-mode");
      } catch {
      }
      try {
        this.root.style.background = textMode ? NCT_DARK_UI_TITLEBAR_TEXT_MODE_BACKGROUND : NCT_DARK_UI_TITLEBAR_GRADIENT;
      } catch {
      }
      try {
        if (this._statusBtn?.style) this._statusBtn.style.background = "transparent";
      } catch {
      }
    }
    /**
     * @param {boolean} active
     */
    setKeyboardHelpActive(active2) {
      this._keyboardActive = !!active2;
      this._renderKeyboard();
      try {
        cacheChromeLayout({ keyboardHelpVisible: this._keyboardActive });
      } catch {
      }
    }
    cleanup() {
      this._unbindHostGuard();
      this._unbindOnboardingWatch();
      this._unbindDrag();
      this._unbindSettingsSync();
      try {
        if (this._statusBtn) this._statusBtn.removeEventListener("click", this._onStatusClick);
        if (this._keyboardBtn) this._keyboardBtn.removeEventListener("click", this._onKeyboardClick);
        if (this._settingsBtn) this._settingsBtn.removeEventListener("click", this._onSettingsClick);
        if (this._collapseBtn) this._collapseBtn.removeEventListener("click", this._onCollapseClick);
        if (this._closeBtn) this._closeBtn.removeEventListener("click", this._onCloseClick);
      } catch {
      }
      try {
        if (this.root && this.root.parentNode) this.root.parentNode.removeChild(this.root);
      } catch {
      }
      this.root = null;
      this.shadowRoot = null;
      this._modulesEl = null;
      this._statusBtn = null;
      this._statusDot = null;
      this._statusLabel = null;
      this._moveBtn = null;
      this._keyboardBtn = null;
      this._settingsBtn = null;
      this._collapseBtn = null;
      this._closeBtn = null;
    }
    _ensure() {
      if (this.root && this.root.isConnected) {
        this._ensureMoveHandle();
        this._bindDrag();
        return;
      }
      try {
        const existing = document.querySelector('.kp-control-strip[data-kp-early-control-strip="true"]');
        if (existing && existing.isConnected) {
          const shadowRoot2 = ensureOpenChromeShadow(existing, { id: "control-strip", chromeWindow: true });
          const shell2 = shadowRoot2 || existing;
          const statusBtn2 = shell2.querySelector('[data-kp-control-strip-status="true"]');
          const statusDot2 = shell2.querySelector('[data-kp-control-strip-status-dot="true"]');
          const statusLabel2 = shell2.querySelector('[data-kp-control-strip-status-label="true"]');
          const modules2 = shell2.querySelector('[data-kp-control-strip-modules="true"]');
          const keyboardBtn2 = shell2.querySelector('[data-kp-control-strip-keyboard="true"]');
          const settingsBtn2 = shell2.querySelector('[data-kp-control-strip-settings="true"]');
          const collapseBtn2 = shell2.querySelector('[data-kp-control-strip-collapse="true"]');
          const closeBtn2 = shell2.querySelector('[data-kp-control-strip-close="true"]');
          if (statusBtn2 && modules2 && keyboardBtn2 && settingsBtn2 && collapseBtn2 && closeBtn2) {
            try {
              this._ensureSegmentIcon(keyboardBtn2, "TOGGLE_KEYBOARD_HELP");
            } catch {
            }
            try {
              this._ensureSegmentIcon(settingsBtn2, "OPEN_SETTINGS_POPOVER");
            } catch {
            }
            this.root = existing;
            this.shadowRoot = shadowRoot2;
            this._modulesEl = modules2;
            this._statusBtn = statusBtn2;
            this._statusDot = statusDot2;
            this._statusLabel = statusLabel2;
            this._keyboardBtn = keyboardBtn2;
            this._settingsBtn = settingsBtn2;
            this._collapseBtn = collapseBtn2;
            this._closeBtn = closeBtn2;
            if (!this._collapsedExplicit) {
              try {
                const earlyCollapsed = existing.getAttribute("data-kp-collapsed");
                if (earlyCollapsed === "true") this._collapsed = true;
                else if (earlyCollapsed === "false") this._collapsed = false;
              } catch {
              }
            }
            try {
              this.root.style.background = NCT_DARK_UI_TITLEBAR_GRADIENT;
              this.root.style.color = "var(--kp-color-fg, #ddd)";
              this.root.style.border = NCT_DARK_UI_PANEL_BORDER;
              this.root.style.borderRadius = NCT_DARK_UI_PANEL_RADIUS;
              this.root.style.boxShadow = NCT_DARK_UI_PANEL_BOX_SHADOW;
              this.root.style.fontFamily = NCT_DARK_UI_FONT;
            } catch {
            }
            try {
              if (statusBtn2 && statusBtn2.style) {
                statusBtn2.style.background = "transparent";
                statusBtn2.style.color = NCT_DARK_UI_COLORS.fg;
              }
            } catch {
            }
            this._ensureMoveHandle();
            this._bindButtonHandlers();
            try {
              if (keyboardBtn2.getAttribute("aria-pressed") === "true") this._keyboardActive = true;
            } catch {
            }
            try {
              const fromDom = this._readPositionFromDom(existing);
              if (fromDom) {
                this._panelPosition = {
                  ...this._panelPosition,
                  left: fromDom.left,
                  top: fromDom.top,
                  anchor: fromDom.anchor
                };
              }
            } catch {
            }
            this._renderStatus();
            this._renderKeyboard();
            this._applyCollapsedLayout();
            this._bindDrag();
            this._applyPanelPositionNow();
            return;
          }
        }
      } catch {
      }
      const root = document.createElement("div");
      root.className = CONTROL_STRIP_ROOT_CLASS;
      root.hidden = true;
      root.setAttribute("role", "toolbar");
      root.setAttribute("aria-label", "KeyPilot control strip");
      root.setAttribute("data-kp-control-strip", "true");
      const shadowRoot = ensureOpenChromeShadow(root, { id: "control-strip", chromeWindow: true });
      const shell = shadowRoot || root;
      Object.assign(root.style, {
        position: "fixed",
        left: `${DEFAULT_LEFT_PX}px`,
        top: `${DEFAULT_TOP_PX}px`,
        height: `${STRIP_HEIGHT_PX}px`,
        minHeight: `${STRIP_HEIGHT_PX}px`,
        maxHeight: `${STRIP_HEIGHT_PX}px`,
        display: "none",
        flexDirection: "row",
        alignItems: "stretch",
        zIndex: String(Z_INDEX.CONTROL_STRIP || 2147483025),
        background: NCT_DARK_UI_TITLEBAR_GRADIENT,
        color: "rgba(248, 250, 252, 0.95)",
        // NCT dark UI panel rim (dark outer edge).
        border: NCT_DARK_UI_PANEL_BORDER,
        borderRadius: NCT_DARK_UI_PANEL_RADIUS,
        boxShadow: NCT_DARK_UI_PANEL_BOX_SHADOW,
        fontFamily: NCT_DARK_UI_FONT,
        pointerEvents: "auto",
        overflow: "hidden",
        boxSizing: "border-box",
        userSelect: "none",
        WebkitUserSelect: "none"
      });
      applyPopupThemeVars(root);
      const statusBtn = this._createSegmentButton({
        ariaLabel: "Toggle KeyPilot on or off",
        title: "Toggle KeyPilot (Alt+K)"
      });
      statusBtn.setAttribute("data-kp-control-strip-status", "true");
      const statusInner = document.createElement("span");
      Object.assign(statusInner.style, {
        display: "inline-flex",
        alignItems: "center",
        gap: "6px",
        pointerEvents: "none"
      });
      const statusDot = document.createElement("span");
      Object.assign(statusDot.style, {
        width: "7px",
        height: "7px",
        borderRadius: "50%",
        flex: "0 0 auto",
        background: "rgba(16, 185, 129, 0.95)",
        boxShadow: "0 0 0 2px rgba(16, 185, 129, 0.2)"
      });
      statusDot.setAttribute("aria-hidden", "true");
      statusDot.setAttribute("data-kp-control-strip-status-dot", "true");
      const statusLabel = document.createElement("span");
      statusLabel.textContent = "ON";
      statusLabel.setAttribute("data-kp-control-strip-status-label", "true");
      Object.assign(statusLabel.style, {
        fontSize: "11px",
        fontWeight: "700",
        letterSpacing: "0.06em",
        lineHeight: "1"
      });
      statusInner.appendChild(statusDot);
      statusInner.appendChild(statusLabel);
      statusBtn.appendChild(statusInner);
      const modules = document.createElement("div");
      modules.setAttribute("data-kp-control-strip-modules", "true");
      Object.assign(modules.style, {
        display: "flex",
        flexDirection: "row",
        alignItems: "stretch",
        flex: "0 0 auto"
      });
      const moveBtn = this._createMoveHandleButton();
      const keyboardBtn = this._createSegmentButton({
        ariaLabel: "Toggle keyboard reference",
        title: "Keyboard reference",
        text: "KB",
        iconActionId: "TOGGLE_KEYBOARD_HELP"
      });
      keyboardBtn.setAttribute("data-kp-control-strip-keyboard", "true");
      const settingsBtn = this._createSegmentButton({
        ariaLabel: "Open KeyPilot settings",
        title: "Settings",
        text: "Settings",
        iconActionId: "OPEN_SETTINGS_POPOVER"
      });
      settingsBtn.setAttribute("data-kp-control-strip-settings", "true");
      modules.appendChild(moveBtn);
      modules.appendChild(keyboardBtn);
      modules.appendChild(settingsBtn);
      const collapseBtn = this._createSegmentButton({
        ariaLabel: "Collapse control strip",
        title: "Collapse",
        text: "\u25C0",
        compact: true
      });
      collapseBtn.setAttribute("data-kp-control-strip-collapse", "true");
      const closeBtn = this._createSegmentButton({
        ariaLabel: "Close control strip",
        title: "Close (Alt+J to show again)",
        text: "\xD7",
        compact: true,
        last: true
      });
      closeBtn.setAttribute("data-kp-control-strip-close", "true");
      shell.appendChild(statusBtn);
      shell.appendChild(modules);
      shell.appendChild(collapseBtn);
      shell.appendChild(closeBtn);
      (document.body || document.documentElement).appendChild(root);
      try {
        ensureChromeHostMounted(root);
      } catch {
      }
      this.root = root;
      this.shadowRoot = shadowRoot;
      this._modulesEl = modules;
      this._statusBtn = statusBtn;
      this._statusDot = statusDot;
      this._statusLabel = statusLabel;
      this._moveBtn = moveBtn;
      this._keyboardBtn = keyboardBtn;
      this._settingsBtn = settingsBtn;
      this._collapseBtn = collapseBtn;
      this._closeBtn = closeBtn;
      this._bindButtonHandlers();
      this._renderStatus();
      this._renderKeyboard();
      this._applyCollapsedLayout();
      this._bindDrag();
      this._applyPanelPositionNow();
    }
    /**
     * Drag grip shown only when the strip is expanded.
     * @returns {HTMLElement}
     */
    _createMoveHandleButton() {
      const btn = this._createSegmentButton({
        ariaLabel: "Move control strip",
        title: "Drag to move",
        text: "\u283F",
        compact: true
      });
      btn.setAttribute("data-kp-control-strip-move", "true");
      try {
        btn.style.cursor = "grab";
        btn.style.touchAction = "none";
        btn.style.letterSpacing = "0";
        btn.style.fontSize = "12px";
        btn.style.opacity = "0.85";
      } catch {
      }
      btn.addEventListener("click", (e) => {
        try {
          e.preventDefault();
          e.stopPropagation();
        } catch {
        }
      });
      return btn;
    }
    /**
     * Ensure early-inject shells gain a move handle without rebuilding the strip.
     */
    _ensureMoveHandle() {
      if (!this.root || !this._modulesEl) return;
      let moveBtn = this.shadowRoot?.querySelector('[data-kp-control-strip-move="true"]');
      if (!moveBtn) {
        moveBtn = this._createMoveHandleButton();
        try {
          this._modulesEl.insertBefore(moveBtn, this._modulesEl.firstChild);
        } catch {
          try {
            this._modulesEl.appendChild(moveBtn);
          } catch {
          }
        }
      }
      this._moveBtn = moveBtn;
    }
    _applyPanelPositionNow() {
      if (!this.root) return;
      try {
        applyPanelPosition(this.root, this._panelPosition, {
          margin: STRIP_POSITION_MARGIN_PX,
          defaultAnchor: "top-left"
        });
      } catch {
      }
    }
    /**
     * Read left/top already painted on the early shell so adopt does not snap to top-left.
     * @param {HTMLElement|null} el
     * @returns {import('../modules/settings-manager.js').PanelPositionSettings|null}
     */
    _readPositionFromDom(el2) {
      if (!el2 || !el2.style) return null;
      try {
        const left = parseFloat(el2.style.left);
        const top = parseFloat(el2.style.top);
        if (!Number.isFinite(left) || !Number.isFinite(top)) return null;
        let anchor = null;
        try {
          const attr = el2.getAttribute("data-kp-panel-anchor");
          if (attr) anchor = attr;
        } catch {
        }
        return { left, top, anchor: anchor || null };
      } catch {
        return null;
      }
    }
    /**
     * @param {import('../modules/settings-manager.js').PanelPositionSettings|null|undefined} next
     * @param {{ persist?: boolean }} [opts]
     */
    _setPanelPosition(next, opts = {}) {
      const normalized = normalizePanelPositionState(
        next,
        DEFAULT_SETTINGS.panelPositions.controlStrip
      ) || { ...DEFAULT_SETTINGS.panelPositions.controlStrip };
      this._panelPosition = {
        left: normalized.left,
        top: normalized.top,
        anchor: normalized.anchor === void 0 ? null : normalized.anchor
      };
      this._applyPanelPositionNow();
      this._syncOnboardingOffset();
      try {
        cacheChromeLayout({
          panelPositions: {
            controlStrip: {
              left: this._panelPosition.left,
              top: this._panelPosition.top,
              anchor: this._panelPosition.anchor === void 0 ? null : this._panelPosition.anchor
            }
          }
        });
      } catch {
      }
      if (opts.persist && !this._suppressPositionPersist) {
        this._persistPanelPosition(this._panelPosition);
      }
    }
    async _refreshPanelPosition() {
      try {
        const settings = await getSettings();
        const stored = settings?.panelPositions?.controlStrip;
        this._suppressPositionPersist = true;
        this._setPanelPosition(stored, { persist: false });
        this._suppressPositionPersist = false;
      } catch {
        this._suppressPositionPersist = false;
      }
    }
    /**
     * @param {import('../modules/settings-manager.js').PanelPositionSettings} position
     */
    async _persistPanelPosition(position) {
      try {
        await setSettings({
          panelPositions: {
            controlStrip: {
              left: position.left,
              top: position.top,
              anchor: position.anchor === void 0 ? null : position.anchor
            }
          }
        });
      } catch {
      }
    }
    _bindDrag() {
      if (!this.root || !this._moveBtn) return;
      if (this._collapsed) {
        this._unbindDrag();
        return;
      }
      if (this._dragBound) return;
      try {
        const api = makePanelDraggable(this.root, this._moveBtn, {
          margin: STRIP_POSITION_MARGIN_PX,
          // Keep width/height content-driven so expand/collapse still works.
          pinWidth: false,
          pinHeight: false,
          onMove: () => {
            this._syncOnboardingOffset();
          },
          onMoveEnd: (state) => {
            if (!state?.moved) return;
            this._setPanelPosition(
              {
                left: state.left,
                top: state.top,
                anchor: state.anchor
              },
              { persist: true }
            );
          }
        });
        this._dragDispose = api?.dispose || null;
        this._dragBound = true;
      } catch (err) {
        console.warn("[KeyPilot] Failed to make control strip draggable:", err?.message || err);
        this._dragDispose = null;
        this._dragBound = false;
      }
    }
    _unbindDrag() {
      try {
        this._dragDispose?.();
      } catch {
      }
      this._dragDispose = null;
      this._dragBound = false;
    }
    _bindSettingsSync() {
      if (this._settingsBound) return;
      try {
        if (chrome?.storage?.onChanged?.addListener) {
          chrome.storage.onChanged.addListener(this._onStorageChanged);
          this._settingsBound = true;
        }
      } catch {
      }
    }
    _unbindSettingsSync() {
      if (!this._settingsBound) return;
      try {
        chrome?.storage?.onChanged?.removeListener?.(this._onStorageChanged);
      } catch {
      }
      this._settingsBound = false;
    }
    _onStorageChanged(changes, area) {
      try {
        if (area !== "sync" && area !== "local") return;
        const entry = changes && changes[SETTINGS_STORAGE_KEY];
        if (!entry || !entry.newValue) return;
        const nextPos = entry.newValue.panelPositions?.controlStrip;
        if (nextPos && typeof nextPos === "object") {
          this._suppressPositionPersist = true;
          this._setPanelPosition(nextPos, { persist: false });
          this._suppressPositionPersist = false;
        }
      } catch {
      }
    }
    /**
     * Attach click handlers (safe to call after early-inject adoption).
     */
    _bindButtonHandlers() {
      try {
        if (this._statusBtn) {
          this._statusBtn.removeEventListener("click", this._onStatusClick);
          this._statusBtn.addEventListener("click", this._onStatusClick);
        }
        if (this._keyboardBtn) {
          this._keyboardBtn.removeEventListener("click", this._onKeyboardClick);
          this._keyboardBtn.addEventListener("click", this._onKeyboardClick);
        }
        if (this._settingsBtn) {
          this._settingsBtn.removeEventListener("click", this._onSettingsClick);
          this._settingsBtn.addEventListener("click", this._onSettingsClick);
        }
        if (this._collapseBtn) {
          this._collapseBtn.removeEventListener("click", this._onCollapseClick);
          this._collapseBtn.addEventListener("click", this._onCollapseClick);
        }
        if (this._closeBtn) {
          this._closeBtn.removeEventListener("click", this._onCloseClick);
          this._closeBtn.addEventListener("click", this._onCloseClick);
        }
      } catch {
      }
    }
    /**
     * Prepend action icon into a segment button if missing (early shell is text-only).
     * @param {HTMLElement|null} btn
     * @param {string} actionId
     */
    _ensureSegmentIcon(btn, actionId) {
      if (!btn || !actionId) return;
      try {
        if (btn.querySelector('img[data-kp-control-strip-icon="true"]')) return;
        const icon = this._createActionIcon(actionId);
        if (!icon) return;
        icon.setAttribute("data-kp-control-strip-icon", "true");
        btn.insertBefore(icon, btn.firstChild);
      } catch {
      }
    }
    /**
     * Inline monochrome icon for a KeyPilot action (keyboard, gear, etc.).
     * @param {string} actionId
     * @param {string} [color]
     * @returns {HTMLElement|null}
     */
    _createActionIcon(actionId, color4 = NCT_DARK_UI_COLORS.fg) {
      try {
        const cssUri = getActionIconDataUri(actionId, { fill: color4 });
        if (!cssUri) return null;
        const match = String(cssUri).match(/^url\("(.+)"\)$/);
        const src = match ? match[1] : null;
        if (!src) return null;
        const img = document.createElement("img");
        img.src = src;
        img.alt = "";
        img.setAttribute("aria-hidden", "true");
        Object.assign(img.style, {
          width: "14px",
          height: "14px",
          display: "block",
          pointerEvents: "none",
          flex: "0 0 auto",
          opacity: "0.95"
        });
        return img;
      } catch {
        return null;
      }
    }
    /**
     * @param {{
     *   ariaLabel: string,
     *   title?: string,
     *   text?: string,
     *   iconActionId?: string,
     *   primary?: boolean,
     *   compact?: boolean,
     *   last?: boolean
     * }} opts
     */
    _createSegmentButton(opts) {
      const btn = document.createElement("button");
      btn.type = "button";
      btn.setAttribute("aria-label", opts.ariaLabel);
      if (opts.title) btn.title = opts.title;
      if (opts.iconActionId) {
        const icon = this._createActionIcon(opts.iconActionId);
        if (icon) {
          try {
            icon.setAttribute("data-kp-control-strip-icon", "true");
          } catch {
          }
          btn.appendChild(icon);
        }
      }
      if (opts.text) {
        const label = document.createElement("span");
        label.textContent = opts.text;
        Object.assign(label.style, {
          pointerEvents: "none",
          lineHeight: "1"
        });
        btn.appendChild(label);
      }
      Object.assign(btn.style, {
        display: "inline-flex",
        alignItems: "center",
        justifyContent: "center",
        gap: "4px",
        height: "100%",
        minHeight: `${STRIP_HEIGHT_PX - 2}px`,
        margin: "0",
        padding: opts.compact ? "0 8px" : "0 10px",
        border: "none",
        borderRight: opts.last ? "none" : `1px solid ${NCT_DARK_UI_COLORS.panelEdgeDark}`,
        borderRadius: "0",
        background: opts.primary ? NCT_DARK_UI_BTN_LIT_GRADIENT : "transparent",
        color: opts.primary ? NCT_DARK_UI_SELECTED_TEXT : NCT_DARK_UI_COLORS.fg,
        fontSize: "11px",
        fontWeight: "600",
        letterSpacing: "0.01em",
        lineHeight: "1",
        cursor: "pointer",
        flex: "0 0 auto",
        boxShadow: "none",
        outline: "none",
        whiteSpace: "nowrap"
      });
      btn.addEventListener("mouseenter", () => {
        try {
          btn.style.background = opts.primary ? NCT_DARK_UI_BTN_GRADIENT : NCT_DARK_UI_HOVER_TINT;
        } catch {
        }
      });
      btn.addEventListener("mouseleave", () => {
        try {
          if (btn === this._keyboardBtn && this._keyboardActive) {
            btn.style.background = NCT_DARK_UI_SELECTED_TINT;
          } else {
            btn.style.background = opts.primary ? NCT_DARK_UI_BTN_LIT_GRADIENT : "transparent";
          }
        } catch {
        }
      });
      btn.addEventListener("focus", () => {
        try {
          btn.style.boxShadow = NCT_DARK_UI_FOCUS_RING;
        } catch {
        }
      });
      btn.addEventListener("blur", () => {
        try {
          btn.style.boxShadow = "none";
        } catch {
        }
      });
      return btn;
    }
    _applyCollapsedLayout() {
      if (!this.root) return;
      const collapsed = !!this._collapsed;
      if (this._modulesEl) {
        this._modulesEl.style.display = collapsed ? "none" : "flex";
      }
      if (this._moveBtn) {
        this._moveBtn.style.display = collapsed ? "none" : "inline-flex";
      }
      if (this._closeBtn) {
        this._closeBtn.style.display = collapsed ? "none" : "inline-flex";
        this._closeBtn.style.boxShadow = NCT_DARK_UI_ICON_BUTTON_OUTLINE;
      }
      if (this._collapseBtn) {
        this._collapseBtn.textContent = collapsed ? "\u25B6" : "\u25C0";
        this._collapseBtn.setAttribute(
          "aria-label",
          collapsed ? "Expand control strip" : "Collapse control strip"
        );
        this._collapseBtn.title = collapsed ? "Expand" : "Collapse";
        this._collapseBtn.style.borderRight = "none";
        if (!collapsed && this._closeBtn) {
          this._collapseBtn.style.borderRight = `1px solid ${NCT_DARK_UI_COLORS.panelEdgeDark}`;
        }
        this._collapseBtn.style.boxShadow = NCT_DARK_UI_ICON_BUTTON_OUTLINE;
      }
      try {
        this.root.setAttribute("data-kp-collapsed", collapsed ? "true" : "false");
      } catch {
      }
    }
    _renderStatus() {
      if (!this._statusLabel || !this._statusDot) return;
      const on = !!this._enabled;
      const textMode = on && !!this._textMode;
      this._statusLabel.textContent = on ? "ON" : "OFF";
      let dotBg;
      let dotShadow;
      let labelColor;
      if (!on) {
        dotBg = "rgba(148, 163, 184, 0.85)";
        dotShadow = "0 0 0 2px rgba(148, 163, 184, 0.15)";
        labelColor = "rgba(148, 163, 184, 0.95)";
      } else if (textMode) {
        dotBg = "rgba(255, 140, 0, 0.95)";
        dotShadow = "0 0 0 2px rgba(255, 140, 0, 0.25)";
        labelColor = "rgba(253, 186, 116, 0.98)";
      } else {
        dotBg = "rgba(16, 185, 129, 0.95)";
        dotShadow = "0 0 0 2px rgba(16, 185, 129, 0.2)";
        labelColor = "rgba(167, 243, 208, 0.95)";
      }
      this._statusDot.style.background = dotBg;
      this._statusDot.style.boxShadow = dotShadow;
      this._statusLabel.style.color = labelColor;
      this._applyTextModeChrome(textMode);
      if (this._statusBtn) {
        this._statusBtn.setAttribute("aria-pressed", on ? "true" : "false");
        try {
          this._statusBtn.setAttribute("data-kp-text-mode", textMode ? "true" : "false");
        } catch {
        }
        this._statusBtn.title = !on ? "KeyPilot is off \u2014 click to turn on (Alt+K)" : textMode ? "KeyPilot is on (text mode) \u2014 click to turn off (Alt+K)" : "KeyPilot is on \u2014 click to turn off (Alt+K)";
      }
    }
    _renderKeyboard() {
      if (!this._keyboardBtn) return;
      const active2 = !!this._keyboardActive;
      this._keyboardBtn.setAttribute("aria-pressed", active2 ? "true" : "false");
      this._keyboardBtn.style.background = active2 ? NCT_DARK_UI_SELECTED_TINT : "transparent";
      this._keyboardBtn.style.color = active2 ? NCT_DARK_UI_SELECTED_TEXT : NCT_DARK_UI_COLORS.fg;
      this._keyboardBtn.title = active2 ? "Hide keyboard reference" : "Show keyboard reference";
    }
    _onStatusClick(e) {
      try {
        e.preventDefault();
        e.stopPropagation();
      } catch {
      }
      if (this._handlers.onToggleEnabled) {
        try {
          this._handlers.onToggleEnabled();
        } catch {
        }
      }
    }
    _onKeyboardClick(e) {
      try {
        e.preventDefault();
        e.stopPropagation();
      } catch {
      }
      if (this._handlers.onToggleKeyboard) {
        try {
          this._handlers.onToggleKeyboard();
        } catch {
        }
      }
    }
    _onSettingsClick(e) {
      try {
        e.preventDefault();
        e.stopPropagation();
      } catch {
      }
      if (this._handlers.onOpenSettings) {
        try {
          this._handlers.onOpenSettings();
        } catch {
        }
      }
    }
    _onCollapseClick(e) {
      try {
        e.preventDefault();
        e.stopPropagation();
      } catch {
      }
      this.setCollapsed(!this._collapsed, { notify: true });
    }
    _onCloseClick(e) {
      try {
        e.preventDefault();
        e.stopPropagation();
      } catch {
      }
      this.hide();
      if (this._handlers.onClose) {
        try {
          this._handlers.onClose();
        } catch {
        }
      }
    }
    /**
     * Reposition the walkthrough relative to the strip.
     * Does not force the strip back to the default top-left — user position is preserved.
     */
    _syncOnboardingOffset() {
      if (!this.root) return;
      try {
        const panel = document.querySelector(".kp-onboarding-panel");
        if (panel && panel.isConnected) {
          const hidden = panel.hidden === true || panel.getAttribute("hidden") !== null || panel.style.display === "none";
          if (!hidden) positionOnboardingBelowControlStrip(panel);
        }
      } catch {
      }
    }
    _bindOnboardingWatch() {
      if (this._observeBound) return;
      try {
        if (typeof MutationObserver !== "undefined") {
          this._observer = new MutationObserver(() => {
            this._syncOnboardingOffset();
          });
          this._observer.observe(document.documentElement || document.body, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ["hidden", "style", "class"]
          });
        }
      } catch {
      }
      try {
        window.addEventListener("resize", this._onWinResize, true);
      } catch {
      }
      this._observeBound = true;
    }
    /**
     * React/Next can remove foreign chrome hosts from <html>/<body>.
     * Reattach while the strip is still supposed to be visible.
     */
    _bindHostGuard() {
      if (this._hostGuard || typeof MutationObserver === "undefined") return;
      const root = this.root;
      if (!root) return;
      try {
        this._hostGuard = new MutationObserver(() => {
          if (!this._desiredVisible || !this.root || this.root.isConnected) return;
          try {
            ensureChromeHostMounted(this.root);
            this.root.hidden = false;
            this.root.style.display = "flex";
            this.root.style.pointerEvents = "auto";
          } catch {
          }
        });
        this._hostGuard.observe(document.documentElement, { childList: true, subtree: true });
      } catch {
        this._hostGuard = null;
      }
    }
    _unbindHostGuard() {
      try {
        this._hostGuard?.disconnect?.();
      } catch {
      }
      this._hostGuard = null;
    }
    _unbindOnboardingWatch() {
      if (!this._observeBound) return;
      try {
        if (this._observer) {
          this._observer.disconnect();
          this._observer = null;
        }
      } catch {
      }
      try {
        window.removeEventListener("resize", this._onWinResize, true);
      } catch {
      }
      this._observeBound = false;
    }
    _onDocMutate() {
      this._syncOnboardingOffset();
    }
    _onWinResize() {
      this._applyPanelPositionNow();
      this._syncOnboardingOffset();
    }
  };

  // src/modules/omnibox-manager.js
  init_constants();
  init_settings_manager();

  // src/ui/url-listing.js
  var GENERIC_FAVICON_SVG = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  <path fill="#ffffff" fill-opacity="0.78" d="M12 2c5.52 0 10 4.48 10 10s-4.48 10-10 10S2 17.52 2 12 6.48 2 12 2Zm0 2c-1.5 0-2.93.35-4.2.98.58.5 1.27 1.33 1.9 2.6.82-.28 1.74-.44 2.3-.48V4Zm2 0v3.1c.56.04 1.48.2 2.3.48.63-1.27 1.32-2.1 1.9-2.6A7.95 7.95 0 0 0 14 4Zm-6.72 2.2A7.99 7.99 0 0 0 4.07 11h3.21c.06-1.39.29-2.65.63-3.74-.24-.5-.45-.84-.63-1.06ZM19.93 11A7.99 7.99 0 0 0 16.72 6.2c-.18.22-.39.56-.63 1.06.34 1.09.57 2.35.63 3.74h3.21ZM9.3 8.73c-.24.8-.41 1.74-.46 2.77h3.16V9.15c-.9.05-1.85.22-2.7.58Zm5.4 0c-.85-.36-1.8-.53-2.7-.58v2.35h3.16c-.05-1.03-.22-1.97-.46-2.77ZM4.07 13a7.99 7.99 0 0 0 3.21 4.8c.18-.22.39-.56.63-1.06-.34-1.09-.57-2.35-.63-3.74H4.07Zm4.77 0c.05 1.03.22 1.97.46 2.77.85.36 1.8.53 2.7.58V13H8.84Zm3.16 3.85c-.56-.04-1.48-.2-2.3-.48-.63 1.27-1.32 2.1-1.9 2.6A7.95 7.95 0 0 0 12 20v-3.15Zm2 0V20c1.5 0 2.93-.35 4.2-.98-.58-.5-1.27-1.33-1.9-2.6-.82.28-1.74.44-2.3.48Zm1.16-3.85H12v3.35c.9-.05 1.85-.22 2.7-.58.24-.8.41-1.74.46-2.77Zm1.56 3.74c.24.5.45.84.63 1.06a7.99 7.99 0 0 0 3.21-4.8h-3.21c-.06 1.39-.29 2.65-.63 3.74Z"/>
</svg>
`.trim();
  var GENERIC_FAVICON_DATA_URL = `data:image/svg+xml;utf8,${encodeURIComponent(GENERIC_FAVICON_SVG)}`;
  function getChromeFavicon2Url(url, size = 32) {
    const u = String(url || "").trim();
    const s = Number(size) || 32;
    return `chrome://favicon2/?size=${encodeURIComponent(String(s))}&pageUrl=${encodeURIComponent(u)}`;
  }
  function stripWwwPrefix(hostOrUrlPart) {
    return String(hostOrUrlPart || "").replace(/^www\./i, "");
  }
  function formatUrlForDisplay(url) {
    const u = String(url || "").trim();
    if (!u) return "";
    try {
      let cleanUrl = u.replace(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//, "");
      cleanUrl = stripWwwPrefix(cleanUrl);
      return cleanUrl;
    } catch {
      return stripWwwPrefix(u.replace(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//, ""));
    }
  }
  function extractDomain(url) {
    try {
      const hostname = new URL(String(url || "").trim()).hostname || "";
      return stripWwwPrefix(hostname);
    } catch {
      return "";
    }
  }
  function parseUrlForThreeLineDisplay(rawUrl) {
    const input = String(rawUrl || "").trim();
    if (!input) return { domain: "", path: "" };
    try {
      const u = new URL(input, "https://example.invalid");
      const scheme = (u.protocol || "").replace(/:$/, "");
      const hostOnly = stripWwwPrefix(u.hostname || "");
      const host = hostOnly + (u.port ? `:${u.port}` : "");
      const domain = host || scheme || stripWwwPrefix(input);
      const pathname = u.pathname || "";
      const rest = `${pathname || ""}${u.search || ""}${u.hash || ""}`;
      const path = rest || (host ? "/" : "");
      return { domain, path };
    } catch {
      const m = input.match(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\/([^\/?#]+)([^\s]*)?$/);
      if (m) {
        const domain = stripWwwPrefix(m[1] || input);
        const path = m[2] || "/";
        return { domain, path };
      }
      return { domain: stripWwwPrefix(input), path: "" };
    }
  }
  function extractPath(url) {
    try {
      const urlObj = new URL(String(url || "").trim());
      const path = `${urlObj.pathname || ""}${urlObj.search || ""}${urlObj.hash || ""}`;
      return path === "/" ? "" : path;
    } catch {
      return "";
    }
  }
  function getExtensionFaviconUrl(pageUrl, size = 32) {
    const u = String(pageUrl || "").trim();
    const s = Number(size) || 32;
    try {
      if (typeof chrome !== "undefined" && chrome?.runtime?.getURL) {
        const url = new URL(chrome.runtime.getURL("/_favicon/"));
        url.searchParams.set("pageUrl", u);
        url.searchParams.set("size", String(s));
        return url.toString();
      }
    } catch {
    }
    return getChromeFavicon2Url(u, s);
  }
  function attachFaviconWithUpgrade(img, pageUrl, opts = {}) {
    if (!img) return;
    const url = String(pageUrl || "").trim();
    const displaySize = Math.max(12, Number(opts.displaySize) || 32);
    const fallback = typeof opts.fallbackUrl === "string" && opts.fallbackUrl ? opts.fallbackUrl : GENERIC_FAVICON_DATA_URL;
    try {
      img.alt = img.alt || "";
      img.referrerPolicy = "no-referrer";
      img.decoding = "async";
      img.loading = img.loading || "lazy";
    } catch {
    }
    const chromeUrl = getExtensionFaviconUrl(url, Math.min(128, Math.max(32, displaySize * 2)));
    try {
      img.src = chromeUrl;
    } catch {
    }
    img.addEventListener("error", () => {
      try {
        img.src = fallback;
        img.dataset.kpFaviconFallback = "true";
      } catch {
      }
    }, { once: true });
  }
  function createFaviconImg(doc, url, { size = 18, faviconUrl, fallbackUrl, highRes: _highRes = true } = {}) {
    const d = doc || document;
    const img = (
      /** @type {HTMLImageElement} */
      d.createElement("img")
    );
    img.alt = "";
    img.referrerPolicy = "no-referrer";
    img.decoding = "async";
    img.loading = "lazy";
    img.width = size;
    img.height = size;
    const fallback = typeof fallbackUrl === "string" && fallbackUrl ? fallbackUrl : GENERIC_FAVICON_DATA_URL;
    if (typeof faviconUrl === "string" && faviconUrl) {
      img.src = faviconUrl;
      img.addEventListener(
        "error",
        () => {
          attachFaviconWithUpgrade(img, url, {
            displaySize: size,
            fallbackUrl: fallback
          });
        },
        { once: true }
      );
      return img;
    }
    attachFaviconWithUpgrade(img, url, {
      displaySize: size,
      fallbackUrl: fallback
    });
    return img;
  }
  function createUrlListingContainer({
    doc,
    view = "list",
    className = "",
    style = {},
    useInlineStyles = true,
    maxHeight,
    scrollY = true
  } = {}) {
    const d = doc || document;
    const el2 = d.createElement("div");
    if (className) el2.className = className;
    el2.dataset.kpUrlListingView = view;
    if (useInlineStyles) {
      Object.assign(el2.style, {
        display: view === "grid" ? "grid" : "block",
        ...scrollY ? { overflowY: "auto" } : {},
        ...maxHeight ? { maxHeight } : {}
      });
    }
    if (style && typeof style === "object") {
      Object.assign(el2.style, style);
    }
    return el2;
  }
  function renderUrlListing({
    container,
    items,
    view = "list",
    classNames = {},
    useInlineStyles = true,
    emptyText,
    getTitle,
    getUrl,
    getMeta,
    getFaviconUrl,
    showFavicon = false,
    showUrlLine = true,
    showMetaLine = false,
    selectedIndex = -1,
    isSelected,
    getRowDataset,
    rowTag = "div",
    decorateRow,
    onRowMouseEnter,
    onRowMouseDown,
    onRowClick
  }) {
    if (!container) return;
    const doc = container.ownerDocument || document;
    container.dataset.kpUrlListingView = view;
    if (classNames.container) container.classList.add(classNames.container);
    container.textContent = "";
    if (!Array.isArray(items) || !items.length) {
      if (typeof emptyText === "string" && emptyText) {
        const empty = doc.createElement("div");
        empty.textContent = emptyText;
        if (useInlineStyles) {
          Object.assign(empty.style, {
            padding: "10px 16px",
            fontSize: "13px",
            color: "rgba(255,255,255,0.55)",
            fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, sans-serif"
          });
        }
        container.appendChild(empty);
      }
      return;
    }
    if (useInlineStyles) {
      if (view === "grid") {
        Object.assign(container.style, {
          display: "grid",
          gridTemplateColumns: "repeat(auto-fill, minmax(180px, 1fr))",
          gap: "10px"
        });
      } else {
        if (container.style.display === "") container.style.display = "block";
      }
    }
    for (let idx = 0; idx < items.length; idx += 1) {
      const item = items[idx];
      const row = (
        /** @type {HTMLElement} */
        doc.createElement(rowTag)
      );
      const selected = typeof isSelected === "function" ? Boolean(isSelected(item, idx)) : idx === selectedIndex;
      if (classNames.row) row.className = classNames.row;
      if (selected && classNames.rowSelected) row.classList.add(classNames.rowSelected);
      row.dataset.kpUrlListingIndex = String(idx);
      if (typeof getRowDataset === "function") {
        const data = getRowDataset(item, idx) || {};
        for (const [k, v] of Object.entries(data)) {
          if (!k) continue;
          row.dataset[k] = String(v);
        }
      }
      const rawUrlText = typeof getUrl === "function" ? String(getUrl(item, idx) || "") : "";
      if (rowTag === "a" && rawUrlText && row instanceof HTMLAnchorElement) {
        row.href = rawUrlText;
      }
      const content = doc.createElement("div");
      const text = doc.createElement("div");
      const titleEl = doc.createElement("div");
      const metaEl = doc.createElement("div");
      const urlEl = doc.createElement("div");
      let faviconEl = null;
      const titleText = typeof getTitle === "function" ? String(getTitle(item, idx) || "") : rawUrlText || "";
      const metaText = typeof getMeta === "function" ? String(getMeta(item, idx) || "") : "";
      titleEl.textContent = titleText;
      metaEl.textContent = metaText;
      if (rawUrlText) {
        const cleanUrl = formatUrlForDisplay(rawUrlText);
        const domainMatch = cleanUrl.match(/^([^\/]+)/);
        if (domainMatch) {
          const domain = domainMatch[1];
          const rest = cleanUrl.slice(domain.length);
          urlEl.innerHTML = `<strong>${domain}</strong>${rest}`;
        } else {
          urlEl.textContent = cleanUrl;
        }
      } else {
        urlEl.textContent = "";
      }
      if (classNames.content) content.className = classNames.content;
      if (classNames.text) text.className = classNames.text;
      if (classNames.title) titleEl.className = classNames.title;
      if (classNames.meta) metaEl.className = classNames.meta;
      if (classNames.url) urlEl.className = classNames.url;
      if (useInlineStyles) {
        Object.assign(row.style, {
          userSelect: "none",
          textDecoration: "none",
          color: "inherit"
        });
        Object.assign(content.style, {
          display: "flex",
          alignItems: view === "grid" ? "flex-start" : "center",
          gap: "10px"
        });
        Object.assign(text.style, {
          minWidth: "0",
          flex: "1 1 auto"
        });
        Object.assign(titleEl.style, {
          fontSize: "13px",
          fontWeight: view === "grid" ? "700" : "700",
          color: "rgba(255,255,255,0.92)",
          overflow: "hidden",
          textOverflow: "ellipsis",
          whiteSpace: "nowrap",
          fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, sans-serif"
        });
        Object.assign(urlEl.style, {
          marginTop: "2px",
          fontSize: "11px",
          color: "rgba(255,255,255,0.62)",
          overflow: "hidden",
          textOverflow: "ellipsis",
          whiteSpace: "nowrap",
          fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, sans-serif"
        });
        Object.assign(metaEl.style, {
          marginTop: "2px",
          fontSize: "11px",
          color: "rgba(255,255,255,0.62)",
          overflow: "hidden",
          textOverflow: "ellipsis",
          whiteSpace: "nowrap",
          fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, sans-serif"
        });
        if (view === "grid") {
          Object.assign(row.style, {
            padding: "10px 10px",
            borderRadius: "12px",
            cursor: "pointer",
            background: selected ? "rgba(255,140,0,0.16)" : "rgba(0,0,0,0.10)",
            border: selected ? "1px solid rgba(255,140,0,0.35)" : "1px solid rgba(255,255,255,0.10)"
          });
        } else {
          Object.assign(row.style, {
            display: "block",
            padding: "8px 10px",
            borderRadius: "10px",
            cursor: "pointer",
            background: selected ? "rgba(255,140,0,0.18)" : "rgba(0,0,0,0)",
            border: selected ? "1px solid rgba(255,140,0,0.35)" : "1px solid rgba(255,255,255,0.06)",
            margin: "6px 6px 6px 6px"
          });
        }
      }
      if (showFavicon) {
        const faviconUrl = typeof getFaviconUrl === "function" ? String(getFaviconUrl(item, idx) || "") : "";
        faviconEl = createFaviconImg(doc, rawUrlText, { size: 18, faviconUrl });
        if (classNames.favicon) faviconEl.className = classNames.favicon;
        if (useInlineStyles) {
          Object.assign(faviconEl.style, {
            width: "18px",
            height: "18px",
            borderRadius: "4px",
            background: "rgba(255,255,255,0.08)",
            flex: "0 0 auto"
          });
        }
        content.appendChild(faviconEl);
      }
      text.appendChild(titleEl);
      if (showMetaLine) text.appendChild(metaEl);
      if (showUrlLine) text.appendChild(urlEl);
      content.appendChild(text);
      row.appendChild(content);
      if (typeof onRowClick === "function") {
        try {
          row.setAttribute("role", "link");
          if (typeof row.tabIndex !== "number" || row.tabIndex < 0) row.tabIndex = 0;
          if (rawUrlText) row.dataset.kpUrl = rawUrlText;
          if (titleText) row.setAttribute("aria-label", titleText);
        } catch {
        }
      }
      if (typeof onRowMouseEnter === "function") {
        row.addEventListener("mouseenter", () => onRowMouseEnter({ row, item, idx }), { passive: true });
      }
      if (typeof onRowMouseDown === "function") {
        row.addEventListener("mousedown", (event) => onRowMouseDown({ row, item, idx, event }), true);
      }
      if (typeof onRowClick === "function") {
        row.addEventListener("click", (event) => onRowClick({ row, item, idx, event }), true);
        row.addEventListener("keydown", (event) => {
          try {
            const k = event && typeof event.key === "string" ? event.key : "";
            if (k !== "Enter" && k !== " ") return;
            event.preventDefault();
            onRowClick({ row, item, idx, event: (
              /** @type {any} */
              event
            ) });
          } catch {
          }
        }, true);
      }
      const parts = {
        row,
        content,
        text,
        titleEl,
        metaEl,
        urlEl,
        faviconEl
      };
      if (typeof decorateRow === "function") {
        decorateRow({ row, item, idx, parts });
      }
      container.appendChild(row);
    }
  }

  // src/modules/omnibox-manager.js
  var OmniboxManager = class {
    /**
     * @param {object} opts
     * @param {() => void} [opts.onClose]
     */
    constructor({ onClose } = {}) {
      this.onClose = typeof onClose === "function" ? onClose : null;
      this._open = false;
      this._backdrop = null;
      this._panel = null;
      this._input = null;
      this._list = null;
      this._suggestions = [];
      this._selectedIndex = -1;
      this._userNavigatedList = false;
      this._debounceTimer = null;
      this._lastQuery = "";
      this._mouseNavEnabled = false;
      this._lastMouseX = null;
      this._lastMouseY = null;
      this._searchEngine = "brave";
      this._settingsListenerInstalled = false;
      this._onBackdropClick = this._onBackdropClick.bind(this);
      this._onInput = this._onInput.bind(this);
      this._onKeyDown = this._onKeyDown.bind(this);
      this._onListMouseMove = this._onListMouseMove.bind(this);
      this._installSettingsListener();
      this._refreshSettings();
    }
    isOpen() {
      return this._open;
    }
    show(initialValue = "") {
      if (this._open) {
        if (this._input) {
          this._input.focus();
          this._input.select();
        }
        return;
      }
      this._open = true;
      this._ensureDom();
      this._userNavigatedList = false;
      this._mouseNavEnabled = false;
      this._lastMouseX = null;
      this._lastMouseY = null;
      if (this._input) {
        this._input.value = typeof initialValue === "string" ? initialValue : "";
        this._input.focus();
        try {
          this._input.select();
        } catch {
        }
      }
      this._scheduleSuggest();
    }
    hide() {
      if (!this._open) return;
      this._open = false;
      this._clearDebounce();
      this._suggestions = [];
      this._selectedIndex = -1;
      this._userNavigatedList = false;
      this._mouseNavEnabled = false;
      this._lastMouseX = null;
      this._lastMouseY = null;
      this._lastQuery = "";
      try {
        this._backdrop?.removeEventListener("click", this._onBackdropClick, true);
        this._input?.removeEventListener("input", this._onInput, true);
        this._input?.removeEventListener("keydown", this._onKeyDown, true);
        this._list?.removeEventListener("mousemove", this._onListMouseMove, true);
      } catch {
      }
      try {
        this._backdrop?.remove();
      } catch {
      }
      this._backdrop = null;
      this._panel = null;
      this._input = null;
      this._list = null;
      if (this.onClose) {
        try {
          this.onClose();
        } catch {
        }
      }
    }
    _ensureDom() {
      if (this._backdrop && this._input) return;
      const doc = document;
      const backdrop = doc.createElement("div");
      backdrop.className = CSS_CLASSES.OMNIBOX_BACKDROP;
      const shadowRoot = ensureOpenChromeShadow(backdrop, { id: "omnibox" });
      const shell = shadowRoot || backdrop;
      Object.assign(backdrop.style, {
        position: "fixed",
        inset: "0",
        zIndex: String(Z_INDEX.OMNIBOX),
        background: "rgba(0,0,0,0.35)",
        backdropFilter: "blur(6px)",
        WebkitBackdropFilter: "blur(6px)"
      });
      backdrop.addEventListener("click", this._onBackdropClick, true);
      const panel = doc.createElement("div");
      panel.className = CSS_CLASSES.OMNIBOX_PANEL;
      Object.assign(panel.style, {
        position: "absolute",
        top: "18vh",
        left: "50%",
        transform: "translateX(-50%)",
        width: "min(880px, calc(100vw - 32px))",
        borderRadius: "14px",
        border: "1px solid rgba(255,140,0,0.35)",
        boxShadow: "0 18px 60px rgba(0,0,0,0.55)",
        background: "rgba(20, 20, 20, 0.88)",
        overflow: "hidden"
      });
      panel.addEventListener("click", (e) => {
        e.stopPropagation();
      }, true);
      const input = doc.createElement("input");
      input.className = CSS_CLASSES.OMNIBOX_INPUT;
      input.setAttribute("type", "text");
      input.setAttribute("spellcheck", "false");
      input.setAttribute("autocomplete", "off");
      input.setAttribute("autocapitalize", "off");
      input.setAttribute("autocorrect", "off");
      input.setAttribute("inputmode", "search");
      input.setAttribute("placeholder", "Search or enter address");
      Object.assign(input.style, {
        width: "100%",
        boxSizing: "border-box",
        border: "none",
        outline: "none",
        padding: "14px 16px",
        fontSize: "18px",
        fontWeight: "500",
        color: "rgba(255,255,255,0.95)",
        background: "rgba(0,0,0,0)",
        fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, sans-serif"
      });
      input.addEventListener("input", this._onInput, true);
      input.addEventListener("keydown", this._onKeyDown, true);
      const list = createUrlListingContainer({
        doc,
        view: "list",
        className: CSS_CLASSES.OMNIBOX_SUGGESTIONS,
        maxHeight: "40vh",
        useInlineStyles: true,
        style: {
          borderTop: "1px solid rgba(255,140,0,0.15)"
        }
      });
      list.addEventListener("mousemove", this._onListMouseMove, { passive: true, capture: true });
      panel.appendChild(input);
      panel.appendChild(list);
      shell.appendChild(panel);
      doc.body.appendChild(backdrop);
      this._backdrop = backdrop;
      this._panel = panel;
      this._input = input;
      this._list = list;
      this._renderSuggestions();
    }
    _onBackdropClick(e) {
      e.preventDefault();
      e.stopPropagation();
      this.hide();
    }
    _onInput() {
      this._scheduleSuggest();
    }
    _onKeyDown(e) {
      if (!this._open) return;
      const key = e.key;
      if (key === "Escape") {
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        this.hide();
        return;
      }
      if (key === "ArrowDown" || key === "ArrowUp") {
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        this._userNavigatedList = true;
        this._mouseNavEnabled = false;
        this._moveSelection(key === "ArrowDown" ? 1 : -1);
        return;
      }
      if (key === "Enter") {
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        this._commit();
        return;
      }
      e.stopPropagation();
      e.stopImmediatePropagation();
    }
    /**
     * True when this keydown is already headed at omnibox chrome (open shadow).
     * @param {Event|null|undefined} e
     * @returns {boolean}
     */
    eventTargetsOmnibox(e) {
      if (!this._open || !e) return false;
      try {
        const path = typeof e.composedPath === "function" ? e.composedPath() : null;
        const nodes = [this._input, this._panel, this._list, this._backdrop];
        if (Array.isArray(path)) {
          for (const n of path) {
            if (nodes.includes(n)) return true;
          }
        }
      } catch {
      }
      return false;
    }
    /**
     * Reclaim focus and insert a printable key when the page (not the input)
     * was the event target — document-capture stop would otherwise drop the
     * character, and leaving it alone lets page shortcuts steal it.
     * @param {KeyboardEvent} e
     */
    focusAndInsertKey(e) {
      if (!this._open || !this._input) return;
      try {
        this._input.focus();
      } catch {
      }
      if (!e || e.ctrlKey || e.metaKey || e.altKey) return;
      const key = e.key;
      if (!key || key.length !== 1) return;
      const input = this._input;
      const value = String(input.value || "");
      const start = Number.isInteger(input.selectionStart) ? input.selectionStart : value.length;
      const end = Number.isInteger(input.selectionEnd) ? input.selectionEnd : start;
      input.value = value.slice(0, start) + key + value.slice(end);
      const pos = start + key.length;
      try {
        input.setSelectionRange(pos, pos);
      } catch {
      }
      this._onInput();
    }
    _moveSelection(delta) {
      const count = this._suggestions.length;
      if (!count) {
        this._selectedIndex = -1;
        this._renderSuggestions();
        return;
      }
      let next = this._selectedIndex;
      if (delta < 0 && next === 0) {
        next = -1;
      } else if (next === -1) {
        next = delta > 0 ? 0 : -1;
      } else {
        next = (next + delta + count) % count;
      }
      this._selectedIndex = next;
      this._renderSuggestions();
      this._scrollSelectedIntoView();
    }
    /**
     * Enable mouse-driven selection only after a real pointer move.
     * A cursor that is merely resting on a row (common when the omnibox opens)
     * must not override keyboard arrow selection.
     * @param {MouseEvent} e
     */
    _onListMouseMove(e) {
      if (!this._open) return;
      const x = e.clientX;
      const y = e.clientY;
      if (this._lastMouseX === null || this._lastMouseY === null) {
        this._lastMouseX = x;
        this._lastMouseY = y;
        this._mouseNavEnabled = true;
        this._selectRowUnderPoint(x, y);
        return;
      }
      if (x === this._lastMouseX && y === this._lastMouseY) return;
      this._lastMouseX = x;
      this._lastMouseY = y;
      this._mouseNavEnabled = true;
      this._selectRowUnderPoint(x, y);
    }
    /**
     * Select the suggestion row currently under the pointer (if any).
     * Used after enabling mouse nav so hover works even when mouseenter does not
     * re-fire (e.g. cursor stayed inside the same row across a re-render).
     * @param {number} clientX
     * @param {number} clientY
     */
    _selectRowUnderPoint(clientX, clientY) {
      if (!this._list || !this._suggestions.length) return;
      try {
        const el2 = document.elementFromPoint(clientX, clientY);
        const row = el2?.closest?.("[data-kp-omnibox-index]");
        if (!row || !this._list.contains(row)) return;
        const idx = Number.parseInt(row.dataset.kpOmniboxIndex || "", 10);
        if (!Number.isFinite(idx) || idx < 0 || idx >= this._suggestions.length) return;
        if (idx === this._selectedIndex) return;
        this._selectedIndex = idx;
        this._userNavigatedList = true;
        this._renderSuggestions();
      } catch {
      }
    }
    _scrollSelectedIntoView() {
      try {
        const el2 = this._list?.querySelector?.(`[data-kp-omnibox-index="${this._selectedIndex}"]`);
        if (el2 && typeof el2.scrollIntoView === "function") {
          el2.scrollIntoView({ block: "nearest" });
        }
      } catch {
      }
    }
    _commit() {
      const raw = (this._input?.value || "").trim();
      const selected = this._selectedIndex >= 0 ? this._suggestions[this._selectedIndex] : null;
      const allowSelected = this._selectedIndex >= 0 && (this._userNavigatedList || selected?.source === "domain");
      const target = allowSelected && selected?.url ? selected.url : raw;
      if (!target) {
        this.hide();
        return;
      }
      const url = this._toUrlOrSearch(target);
      this.hide();
      try {
        window.location.assign(url);
      } catch {
        try {
          window.location.href = url;
        } catch {
        }
      }
    }
    _installSettingsListener() {
      if (this._settingsListenerInstalled) return;
      this._settingsListenerInstalled = true;
      try {
        chrome.storage.onChanged.addListener((changes, area) => {
          if (area !== "sync") return;
          if (!changes || !changes[SETTINGS_STORAGE_KEY]) return;
          this._refreshSettings();
        });
      } catch {
      }
    }
    async _refreshSettings() {
      try {
        const settings = await getSettings();
        this._searchEngine = normalizeSearchEngine(settings?.searchEngine);
      } catch {
        this._searchEngine = "brave";
      }
    }
    _toUrlOrSearch(input) {
      const text = String(input || "").trim();
      if (!text) return getEngineHomeUrl(this._searchEngine);
      if (/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(text)) return text;
      if (isLikelyFilenameQuery(text)) {
        return buildSearchUrl(this._searchEngine, text);
      }
      if (/\s/.test(text)) {
        return buildSearchUrl(this._searchEngine, text);
      }
      const looksLikeHost = /(^localhost\b)/i.test(text) || /(^\d{1,3}(\.\d{1,3}){3}\b)/.test(text) || /([a-zA-Z0-9-]+\.[a-zA-Z]{2,})([\/:?#]|$)/.test(text);
      if (looksLikeHost) {
        return `https://${text}`;
      }
      return buildSearchUrl(this._searchEngine, text);
    }
    _clearDebounce() {
      if (this._debounceTimer) {
        clearTimeout(this._debounceTimer);
        this._debounceTimer = null;
      }
    }
    _scheduleSuggest() {
      this._clearDebounce();
      this._debounceTimer = setTimeout(() => {
        this._debounceTimer = null;
        this._fetchSuggestions();
      }, 90);
    }
    async _fetchSuggestions() {
      if (!this._open) return;
      const query = (this._input?.value || "").trim();
      this._lastQuery = query;
      let suggestions = [];
      try {
        if (typeof chrome !== "undefined" && chrome.runtime && typeof chrome.runtime.sendMessage === "function") {
          const resp = await chrome.runtime.sendMessage({
            type: MSG.OMNIBOX_SUGGEST,
            query,
            maxResults: 12
          });
          if (resp && resp.type === MSG.OMNIBOX_SUGGESTIONS && Array.isArray(resp.suggestions)) {
            suggestions = resp.suggestions.filter((s) => s && typeof s.url === "string" && s.url.trim()).slice(0, 12);
          }
        }
      } catch {
      }
      if (!this._open) return;
      const current = (this._input?.value || "").trim();
      if (current !== this._lastQuery) return;
      this._suggestions = suggestions;
      this._selectedIndex = -1;
      this._userNavigatedList = false;
      this._renderSuggestions();
    }
    _renderSuggestions() {
      if (!this._list) return;
      renderUrlListing({
        container: this._list,
        items: this._suggestions,
        view: "list",
        useInlineStyles: true,
        emptyText: "No suggestions",
        getTitle: (s) => s.title || s.url,
        getUrl: (s) => s.url,
        showFavicon: true,
        showMetaLine: false,
        showUrlLine: true,
        selectedIndex: this._selectedIndex,
        decorateRow: ({ row, item, idx, parts }) => {
          row.className = CSS_CLASSES.OMNIBOX_SUGGESTION;
          row.dataset.kpOmniboxIndex = String(idx);
          const selected = idx === this._selectedIndex;
          Object.assign(row.style, {
            padding: "10px 16px",
            cursor: "default",
            background: selected ? "rgba(255,140,0,0.18)" : "rgba(0,0,0,0)",
            borderLeft: selected ? "3px solid rgba(255,140,0,0.85)" : "3px solid transparent",
            borderTop: "none",
            borderRight: "none",
            borderBottom: "none",
            borderRadius: "0",
            margin: "0",
            border: "none"
          });
          Object.assign(parts.titleEl.style, {
            fontSize: "14px",
            fontWeight: "600"
          });
          Object.assign(parts.urlEl.style, {
            fontSize: "12px"
          });
          if (parts.faviconEl) {
            Object.assign(parts.faviconEl.style, {
              width: "18px",
              height: "18px",
              borderRadius: "4px"
            });
          }
        },
        onRowMouseEnter: ({ idx }) => {
          if (!this._mouseNavEnabled) return;
          if (idx === this._selectedIndex) return;
          this._selectedIndex = idx;
          this._userNavigatedList = true;
          this._renderSuggestions();
        },
        onRowMouseDown: ({ idx, event }) => {
          event.preventDefault();
          event.stopPropagation();
          this._selectedIndex = idx;
          this._userNavigatedList = true;
          this._mouseNavEnabled = true;
          this._commit();
        }
      });
    }
  };
  function isLikelyFilenameQuery(text) {
    const s = String(text || "").trim();
    if (!s) return false;
    if (/\s/.test(s)) return false;
    if (/[\/?#:]/.test(s)) return false;
    if (!/^[^.\s@]+\.[a-z0-9]+$/i.test(s)) return false;
    const ext = s.split(".").pop()?.toLowerCase() || "";
    const filenameExts = /* @__PURE__ */ new Set([
      "js",
      "mjs",
      "cjs",
      "css",
      "html",
      "htm",
      "json",
      "xml",
      "txt",
      "md"
    ]);
    return filenameExts.has(ext);
  }

  // src/utils/youtube-thumb.js
  function extractYouTubeVideoId(url) {
    if (!url || typeof url !== "string") return null;
    try {
      const urlObj = new URL(url);
      const hostname = urlObj.hostname.replace(/^www\./, "").toLowerCase();
      if (!hostname.includes("youtube.com") && !hostname.includes("youtu.be")) {
        return null;
      }
      if (hostname === "youtu.be" || hostname.endsWith(".youtu.be")) {
        const videoId = urlObj.pathname.slice(1).split("/")[0].split("?")[0].split("&")[0];
        if (videoId && videoId.length === 11) return videoId;
        return null;
      }
      if (hostname.includes("youtube.com")) {
        if (urlObj.pathname === "/watch" && urlObj.searchParams.has("v")) {
          const videoId = urlObj.searchParams.get("v");
          if (videoId && videoId.length === 11) return videoId;
        }
        const embedMatch = urlObj.pathname.match(/^\/embed\/([a-zA-Z0-9_-]{11})/);
        if (embedMatch) return embedMatch[1];
        const vMatch = urlObj.pathname.match(/^\/v\/([a-zA-Z0-9_-]{11})/);
        if (vMatch) return vMatch[1];
        const shortsMatch = urlObj.pathname.match(/^\/shorts\/([a-zA-Z0-9_-]{11})/);
        if (shortsMatch) return shortsMatch[1];
      }
      return null;
    } catch {
      return null;
    }
  }
  function getYouTubeThumbnailUrl(videoId, quality = "hqdefault") {
    if (!videoId || typeof videoId !== "string") return null;
    const valid = ["default", "mqdefault", "hqdefault", "sddefault", "maxresdefault"];
    const q = valid.includes(quality) ? quality : "hqdefault";
    return `https://img.youtube.com/vi/${videoId}/${q}.jpg`;
  }
  function getYouTubeThumbnailUrlForPage(pageUrl, quality = "hqdefault") {
    const id = extractYouTubeVideoId(pageUrl);
    if (!id) return null;
    return getYouTubeThumbnailUrl(id, quality);
  }
  function extractDailymotionVideoId(url) {
    if (!url || typeof url !== "string") return null;
    try {
      const urlObj = new URL(url);
      const hostname = urlObj.hostname.replace(/^www\./, "").toLowerCase();
      if (!hostname.includes("dailymotion.com") && !hostname.includes("dai.ly")) {
        return null;
      }
      if (hostname === "dai.ly" || hostname.endsWith(".dai.ly")) {
        const id = urlObj.pathname.slice(1).split("/")[0];
        return id || null;
      }
      const videoMatch = urlObj.pathname.match(/^\/video\/([a-zA-Z0-9]+)/);
      if (videoMatch) return videoMatch[1];
      const embedMatch = urlObj.pathname.match(/^\/embed\/video\/([a-zA-Z0-9]+)/);
      if (embedMatch) return embedMatch[1];
      return null;
    } catch {
      return null;
    }
  }
  function getDailymotionThumbnailUrl(videoId) {
    if (!videoId || typeof videoId !== "string") return null;
    return `https://www.dailymotion.com/thumbnail/video/${encodeURIComponent(videoId)}`;
  }
  function isVideoSiteUrl(pageUrl) {
    if (!pageUrl || typeof pageUrl !== "string") return false;
    try {
      const host = new URL(pageUrl).hostname.replace(/^www\./, "").toLowerCase();
      return host.includes("youtube.com") || host.includes("youtu.be") || host.includes("rumble.com") || host.includes("odysee.com") || host.includes("vimeo.com") || host.includes("dailymotion.com") || host.includes("dai.ly") || host.includes("twitch.tv") || host.includes("bitchute.com") || host.includes("kick.com");
    } catch {
      return false;
    }
  }
  function getSyncVideoThumbnailUrlForPage(pageUrl, youtubeQuality = "hqdefault") {
    const yt = getYouTubeThumbnailUrlForPage(pageUrl, youtubeQuality);
    if (yt) return yt;
    const dmId = extractDailymotionVideoId(pageUrl);
    if (dmId) return getDailymotionThumbnailUrl(dmId);
    return null;
  }

  // src/utils/thumb-load-queue.js
  var DEFAULT_CONCURRENCY = 4;
  var resolvedCache = /* @__PURE__ */ new Map();
  var inFlight = /* @__PURE__ */ new Map();
  var decodedUrls = /* @__PURE__ */ new Set();
  var decodeInFlight = /* @__PURE__ */ new Map();
  var active = 0;
  var waitQueue = [];
  function pump(concurrency = DEFAULT_CONCURRENCY) {
    while (active < concurrency && waitQueue.length) {
      waitQueue.sort((a, b) => b.priority - a.priority);
      const next = waitQueue.shift();
      if (!next) break;
      active += 1;
      try {
        next.run();
      } catch {
        active = Math.max(0, active - 1);
      }
    }
  }
  function enqueueThumbJob(fn, opts = {}) {
    const priority = Number(opts.priority) || 0;
    const concurrency = Number(opts.concurrency) || DEFAULT_CONCURRENCY;
    return new Promise((resolve, reject) => {
      const run = () => {
        Promise.resolve().then(fn).then(resolve, reject).finally(() => {
          active = Math.max(0, active - 1);
          pump(concurrency);
        });
      };
      waitQueue.push({ priority, run });
      pump(concurrency);
    });
  }
  function getCachedCardThumb(pageUrl) {
    const key = String(pageUrl || "").trim();
    if (!key) return null;
    return resolvedCache.get(key) || null;
  }
  function setCachedCardThumb(pageUrl, resolved) {
    const key = String(pageUrl || "").trim();
    if (!key || !resolved?.url) return;
    resolvedCache.set(key, { url: resolved.url, source: resolved.source || "unknown" });
  }
  function preloadThumbImage(imageUrl) {
    const url = String(imageUrl || "").trim();
    if (!url) return Promise.resolve(false);
    if (decodedUrls.has(url)) return Promise.resolve(true);
    const existing = decodeInFlight.get(url);
    if (existing) return existing;
    const promise = new Promise((resolve) => {
      try {
        const img = new Image();
        img.decoding = "async";
        img.onload = () => {
          decodedUrls.add(url);
          decodeInFlight.delete(url);
          resolve(true);
        };
        img.onerror = () => {
          decodeInFlight.delete(url);
          resolve(false);
        };
        img.src = url;
      } catch {
        decodeInFlight.delete(url);
        resolve(false);
      }
    });
    decodeInFlight.set(url, promise);
    return promise;
  }
  async function resolveCardThumbQueued(pageUrl, resolver, opts = {}) {
    const key = String(pageUrl || "").trim();
    if (!key || typeof resolver !== "function") return null;
    const cached = resolvedCache.get(key);
    if (cached?.url) {
      if (opts.preload !== false) await preloadThumbImage(cached.url);
      return cached;
    }
    const pending = inFlight.get(key);
    if (pending) {
      const result = await pending;
      if (result?.url && opts.preload !== false) await preloadThumbImage(result.url);
      return result;
    }
    const job = enqueueThumbJob(async () => {
      try {
        const resolved = await resolver(key);
        if (resolved?.url) {
          setCachedCardThumb(key, resolved);
          if (opts.preload !== false) await preloadThumbImage(resolved.url);
          return resolvedCache.get(key) || resolved;
        }
        return null;
      } finally {
        inFlight.delete(key);
      }
    }, { priority: opts.priority });
    inFlight.set(key, job);
    return job;
  }
  function observeThumbVisibility(el2, onVisible, opts = {}) {
    if (!el2 || typeof onVisible !== "function") return () => {
    };
    if (typeof IntersectionObserver !== "function") {
      onVisible();
      return () => {
      };
    }
    let done = false;
    const root = opts.root === void 0 ? null : opts.root;
    const rootMargin = opts.rootMargin || "100% 100% 100% 100%";
    const io = new IntersectionObserver(
      (entries) => {
        for (const entry of entries) {
          if (!entry.isIntersecting || done) continue;
          done = true;
          try {
            io.disconnect();
          } catch {
          }
          onVisible();
          break;
        }
      },
      { root, rootMargin, threshold: 0.01 }
    );
    try {
      io.observe(el2);
    } catch {
      onVisible();
      return () => {
      };
    }
    return () => {
      done = true;
      try {
        io.disconnect();
      } catch {
      }
    };
  }

  // src/utils/firefox-data-consent.js
  var FIREFOX_VIDEO_THUMBNAIL_DATA_TYPES = Object.freeze([
    "browsingActivity"
  ]);
  function firefoxPermissionsApi() {
    try {
      return globalThis.browser?.permissions || null;
    } catch {
      return null;
    }
  }
  async function hasFirefoxVideoThumbnailConsent() {
    const permissions = firefoxPermissionsApi();
    if (!permissions || typeof permissions.getAll !== "function") return true;
    try {
      const granted = await permissions.getAll();
      const dataCollection = Array.isArray(granted?.data_collection) ? granted.data_collection : [];
      return FIREFOX_VIDEO_THUMBNAIL_DATA_TYPES.every((type2) => dataCollection.includes(type2));
    } catch {
      return false;
    }
  }

  // src/ui/page-thumb-ui.js
  function buildDarkenedThumbBackground(imageUrl, topAlpha = 0.55, bottomAlpha = 0.78) {
    const url = String(imageUrl || "").trim();
    if (!url) return "";
    const t = Math.max(0, Math.min(1, Number(topAlpha) || 0.7));
    const b = Math.max(0, Math.min(1, Number(bottomAlpha) || 0.85));
    const safe = url.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
    return `linear-gradient(to bottom, rgba(0,0,0,${t}) 0%, rgba(0,0,0,${b}) 100%), url("${safe}") center / cover no-repeat`;
  }
  async function requestVideoThumb(pageUrl) {
    const url = String(pageUrl || "").trim();
    if (!url || !isVideoSiteUrl(url)) return null;
    if (!await hasFirefoxVideoThumbnailConsent()) return null;
    const sync = getSyncVideoThumbnailUrlForPage(url, "hqdefault");
    if (sync) {
      const source = extractYouTubeVideoId(url) ? "youtube" : "dailymotion";
      return { url: sync, source };
    }
    try {
      if (typeof chrome === "undefined" || !chrome.runtime?.sendMessage) {
        return null;
      }
      const response = await chrome.runtime.sendMessage({
        type: MSG.GET_VIDEO_THUMB,
        pageUrl: url
      });
      if (response && response.type === MSG.VIDEO_THUMB_RESPONSE && response.success && typeof response.thumbUrl === "string" && response.thumbUrl) {
        return {
          url: response.thumbUrl,
          source: typeof response.source === "string" ? response.source : "video"
        };
      }
    } catch {
    }
    return null;
  }
  async function resolveCardBackgroundImage(pageUrl, opts = {}) {
    const videoPrefer = opts.videoPrefer !== false && opts.youtubePrefer !== false;
    if (videoPrefer) {
      const video = await requestVideoThumb(pageUrl);
      if (video?.url) return video;
    }
    return null;
  }
  function applyCardBackground(el2, pageUrl, opts = {}) {
    if (!el2) {
      return { refresh: async () => {
      }, dispose: () => {
      } };
    }
    const fallbackSolid = opts.fallbackSolid || "#2a2a2a";
    const hoverSolid = opts.hoverSolid || "#333";
    const manageHover = opts.manageHover === true;
    const videoPrefer = opts.videoPrefer !== false && opts.youtubePrefer !== false;
    const lazy = opts.lazy === true;
    const lazyRoot = opts.lazyRoot === void 0 ? null : opts.lazyRoot;
    const lazyRootMargin = opts.lazyRootMargin || "100% 100% 100% 100%";
    const priority = Number(opts.priority) || 0;
    const idleTop = opts.idleTop != null ? opts.idleTop : 0.55;
    const idleBottom = opts.idleBottom != null ? opts.idleBottom : 0.78;
    const hoverTop = opts.hoverTop != null ? opts.hoverTop : 0.32;
    const hoverBottom = opts.hoverBottom != null ? opts.hoverBottom : 0.52;
    const useCssVar = opts.useCssVar === true;
    const cssVarName = opts.cssVarName || "--kp-page-thumb";
    const readyClass = opts.readyClass || "kp-has-page-thumb";
    let disposed = false;
    let hovering = false;
    let thumbUrl = null;
    let thumbSource = null;
    let unobserve = null;
    const paintSolid = (hover) => {
      if (useCssVar) {
        try {
          el2.classList.remove(readyClass);
          el2.style.removeProperty(cssVarName);
        } catch {
        }
        return;
      }
      el2.style.background = hover ? hoverSolid : fallbackSolid;
    };
    const paintThumb = (url, hover) => {
      if (useCssVar) {
        try {
          const safe = String(url).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
          el2.style.setProperty(cssVarName, `url("${safe}")`);
          el2.classList.add(readyClass);
          if (hover) el2.classList.add("kp-page-thumb-hover");
          else el2.classList.remove("kp-page-thumb-hover");
        } catch {
        }
        return;
      }
      const top = hover ? hoverTop : idleTop;
      const bottom = hover ? hoverBottom : idleBottom;
      el2.style.background = buildDarkenedThumbBackground(url, top, bottom);
    };
    const paint = () => {
      if (disposed || !el2.isConnected) return;
      if (thumbUrl) paintThumb(thumbUrl, hovering);
      else paintSolid(hovering);
    };
    const onEnter = () => {
      hovering = true;
      paint();
    };
    const onLeave = () => {
      hovering = false;
      paint();
    };
    if (manageHover) {
      el2.addEventListener("mouseenter", onEnter);
      el2.addEventListener("mouseleave", onLeave);
    }
    const setThumb = (url, source) => {
      thumbUrl = url;
      thumbSource = source;
      try {
        el2.dataset.kpThumbSource = source || "";
        el2.dataset.kpThumbUrl = url || "";
        el2.dataset.kpThumbReady = url ? "1" : "0";
      } catch {
      }
      paint();
    };
    const setThumbWhenReady = async (url, source) => {
      if (disposed || !url) return;
      const ok = await preloadThumbImage(url);
      if (disposed || !el2.isConnected) return;
      if (!ok) return;
      setThumb(url, source);
    };
    paintSolid(false);
    const applyKnownThumb = async () => {
      if (disposed) return;
      const cached = getCachedCardThumb(pageUrl);
      if (cached?.url) {
        await setThumbWhenReady(cached.url, cached.source);
        return;
      }
      if (!videoPrefer) return;
      const sync = getSyncVideoThumbnailUrlForPage(pageUrl, "hqdefault");
      if (!sync) return;
      const source = extractYouTubeVideoId(pageUrl) ? "youtube" : "dailymotion";
      setCachedCardThumb(pageUrl, { url: sync, source });
      await setThumbWhenReady(sync, source);
    };
    const refresh = async () => {
      if (disposed) return;
      if (thumbUrl && thumbSource && thumbSource !== "capture") return;
      const resolved = await resolveCardThumbQueued(
        pageUrl,
        (url) => resolveCardBackgroundImage(url, { videoPrefer }),
        { priority, preload: true }
      );
      if (disposed || !el2.isConnected) return;
      if (!resolved?.url) return;
      setThumb(resolved.url, resolved.source);
    };
    const startLoad = () => {
      if (disposed) return;
      void (async () => {
        await applyKnownThumb();
        if (disposed) return;
        if (thumbUrl && thumbSource && thumbSource !== "capture") return;
        await refresh();
      })();
    };
    if (lazy) {
      unobserve = observeThumbVisibility(el2, startLoad, {
        root: lazyRoot,
        rootMargin: lazyRootMargin
      });
    } else {
      startLoad();
    }
    const dispose = () => {
      disposed = true;
      try {
        unobserve?.();
      } catch {
      }
      unobserve = null;
      if (manageHover) {
        el2.removeEventListener("mouseenter", onEnter);
        el2.removeEventListener("mouseleave", onLeave);
      }
    };
    return { refresh, dispose };
  }

  // src/modules/tab-history-popover.js
  function formatVisitDateTime(timestamp) {
    const n = Number(timestamp);
    if (!Number.isFinite(n) || n <= 0) return null;
    try {
      const d = new Date(n);
      const now = /* @__PURE__ */ new Date();
      const isToday = d.getFullYear() === now.getFullYear() && d.getMonth() === now.getMonth() && d.getDate() === now.getDate();
      const time = d.toLocaleTimeString(void 0, {
        hour: "numeric",
        minute: "2-digit"
      });
      if (isToday) return time;
      const sameYear = d.getFullYear() === now.getFullYear();
      const date = d.toLocaleDateString(void 0, {
        month: "short",
        day: "numeric",
        ...sameYear ? {} : { year: "numeric" }
      });
      return `${date}, ${time}`;
    } catch {
      return null;
    }
  }
  function renderThreeLineUrlListingEntry({ item, parts }) {
    const url = String(item?.url || "").trim();
    const title = String(item?.title || "").trim();
    const { domain, path } = parseUrlForThreeLineDisplay(url);
    parts.titleEl.textContent = domain || url || "";
    parts.metaEl.textContent = title;
    parts.urlEl.textContent = path;
    const visitedLabel = formatVisitDateTime(item?.lastVisitTime);
    let visitedEl = parts.visitedEl;
    if (!visitedEl && parts.text) {
      const doc = parts.text.ownerDocument || document;
      visitedEl = doc.createElement("div");
      visitedEl.className = "kp-url-visited";
      parts.text.appendChild(visitedEl);
      parts.visitedEl = visitedEl;
    }
    if (visitedEl) {
      if (visitedLabel) {
        visitedEl.textContent = visitedLabel;
        visitedEl.hidden = false;
        visitedEl.removeAttribute("aria-hidden");
      } else {
        visitedEl.textContent = "";
        visitedEl.hidden = true;
        visitedEl.setAttribute("aria-hidden", "true");
      }
    }
  }
  function attachPageThumbToHistoryCard(row, url) {
    const pageUrl = String(url || "").trim();
    if (!row || !pageUrl) return;
    applyCardBackground(row, pageUrl, {
      fallbackSolid: "",
      hoverSolid: "",
      manageHover: false,
      youtubePrefer: true,
      useCssVar: true,
      cssVarName: "--kp-page-thumb",
      readyClass: "kp-has-page-thumb"
    });
  }
  var HISTORY_CARD_CLASS_NAMES = {
    row: "kp-url-row",
    rowSelected: "kp-url-row--selected",
    content: "kp-url-content",
    text: "kp-url-text",
    title: "kp-url-domain",
    meta: "kp-url-title",
    url: "kp-url-path",
    favicon: "kp-url-favicon"
  };
  var TabHistoryPopover = class {
    /**
     * @param {object} opts
     * @param {import('./popup-manager.js').PopupManager} opts.popupManager
     * @param {(open: boolean) => void} [opts.onStateChange] - Called when popover opens/closes
     */
    constructor({ popupManager, onStateChange } = {}) {
      this.popupManager = popupManager || null;
      this._onStateChange = typeof onStateChange === "function" ? onStateChange : null;
      this._popupId = "kpv2-tab-history-popover";
      this._panel = null;
      this._tabList = null;
      this._tabStatus = null;
      this._browserList = null;
      this._browserStatus = null;
      this._open = false;
      this._lastGraph = null;
    }
    isOpen() {
      return this._open;
    }
    toggle() {
      if (this._open) this.hide();
      else this.show();
    }
    show() {
      if (!this.popupManager) return;
      if (this._open) return;
      this._open = true;
      this._ensureDom();
      this._injectStyles();
      this.popupManager.showModal({
        id: this._popupId,
        panel: this._panel,
        onRequestClose: () => this.hide(),
        resizable: false
      });
      this._onStateChange?.(true);
      this._loadAndRender();
    }
    hide() {
      if (!this.popupManager) return;
      if (!this._open) return;
      this._open = false;
      this._onStateChange?.(false);
      try {
        this.popupManager.hideModal(this._popupId);
      } catch {
      }
    }
    _injectStyles() {
      const css = `
      .kpv2-tab-history-panel {
        --kp-history-side-margin: 24px;
        --kp-history-card-w: 140px;
        box-sizing: border-box;
        position: fixed;
        left: 50%;
        top: 12vh;
        transform: translateX(-50%);
        width: calc(100vw - (2 * var(--kp-history-side-margin)));
        max-width: none;
        max-height: 80vh;
        display: flex;
        flex-direction: column;
        border-radius: ${NCT_DARK_UI_PANEL_RADIUS};
        border: 1px solid ${NCT_DARK_UI_COLORS.panelEdgeDark};
        background: ${NCT_DARK_UI_COLORS.panel};
        box-shadow: ${NCT_DARK_UI_PANEL_BOX_SHADOW};
        overflow: hidden;
        outline: none;
        font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
      }

      .kpv2-tab-history-panel .kpv2-history-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 12px 16px;
        border-bottom: 1px solid rgba(255,140,0,0.14);
        background: ${NCT_DARK_UI_TITLEBAR_GRADIENT};
        flex: 0 0 auto;
      }

      .kpv2-tab-history-panel .kpv2-history-title-wrap {
        display: flex;
        align-items: baseline;
        gap: 10px;
        min-width: 0;
      }

      .kpv2-tab-history-panel .kpv2-history-title {
        font-size: 14px;
        font-weight: 700;
        color: rgba(255,255,255,0.92);
      }

      .kpv2-tab-history-panel .kpv2-history-subtitle {
        font-size: 12px;
        font-weight: 600;
        color: rgba(255,140,0,0.85);
        white-space: nowrap;
      }

      .kpv2-tab-history-panel .kpv2-history-close {
        margin: 0;
        appearance: none;
        -webkit-appearance: none;
        box-sizing: border-box;
        width: 34px;
        height: 34px;
        border-radius: ${NCT_DARK_UI_BTN_RADIUS};
        border: ${NCT_DARK_UI_BTN_BORDER};
        background: ${NCT_DARK_UI_BTN_GRADIENT};
        color: ${NCT_DARK_UI_COLORS.fg};
        font-size: 22px;
        line-height: 1;
        cursor: pointer;
        flex: 0 0 auto;
        box-shadow: none;
        text-shadow: none;
        padding: 0;
      }

      .kpv2-tab-history-panel .kpv2-history-body {
        display: flex;
        flex-direction: column;
        gap: 12px;
        padding: 12px;
        flex: 1 1 auto;
        min-height: 0;
        overflow: auto;
      }

      .kpv2-tab-history-panel .kpv2-history-section {
        display: flex;
        flex-direction: column;
        min-width: 0;
        border: 1px solid rgba(255,255,255,0.08);
        border-radius: 12px;
        overflow: hidden;
        background: rgba(0,0,0,0.18);
        flex: 0 0 auto;
      }

      .kpv2-tab-history-panel .kpv2-history-section-header {
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: 10px;
        padding: 10px 12px;
        border-bottom: 1px solid rgba(255,255,255,0.08);
        background: rgba(0,0,0,0.18);
      }

      .kpv2-tab-history-panel .kpv2-history-section-title {
        font-size: 13px;
        font-weight: 800;
        color: rgba(255,255,255,0.9);
      }

      .kpv2-tab-history-panel .kpv2-history-section-hint {
        font-size: 11px;
        font-weight: 600;
        color: rgba(255,255,255,0.55);
        white-space: nowrap;
      }

      .kpv2-tab-history-panel .kpv2-history-section-status {
        padding: 6px 12px;
        border-bottom: 1px solid rgba(255,255,255,0.06);
        font-size: 12px;
        color: rgba(255,255,255,0.65);
      }

      /* Horizontal card rail */
      .kpv2-tab-history-panel .kpv2-history-rail {
        display: flex !important;
        flex-direction: row;
        flex-wrap: nowrap;
        align-items: stretch;
        gap: 10px;
        overflow-x: auto;
        overflow-y: hidden;
        padding: 10px 10px 12px;
        scroll-snap-type: x proximity;
        scrollbar-width: thin;
        scrollbar-color: rgba(255,255,255,0.2) rgba(0,0,0,0.1);
        min-height: calc(var(--kp-history-card-w) * 4 / 3 + 22px);
      }

      /* Vertical history cards */
      .kpv2-tab-history-panel .kp-url-row {
        box-sizing: border-box;
        display: block;
        position: relative;
        flex: 0 0 auto;
        width: var(--kp-history-card-w);
        aspect-ratio: 3 / 4;
        height: auto;
        padding: 12px 10px;
        border-radius: 12px;
        cursor: pointer;
        text-decoration: none;
        color: inherit;
        user-select: none;
        scroll-snap-align: start;
        background: linear-gradient(180deg, rgba(255,255,255,0.10) 0%, rgba(255,255,255,0.05) 100%);
        border: 1px solid rgba(255,255,255,0.12);
        box-shadow: 0 4px 12px rgba(0,0,0,0.25), 0 2px 4px rgba(0,0,0,0.15);
        margin: 0;
        min-width: 0;
        overflow: hidden;
        transition: transform 0.12s ease, box-shadow 0.12s ease, border-color 0.12s ease, background 0.12s ease;
      }

      .kpv2-tab-history-panel .kp-url-row:hover {
        background: linear-gradient(180deg, rgba(255,255,255,0.15) 0%, rgba(255,255,255,0.08) 100%);
        box-shadow: 0 8px 18px rgba(0,0,0,0.38), 0 3px 6px rgba(0,0,0,0.20);
        transform: translateY(-2px);
      }

      .kpv2-tab-history-panel .kp-url-row:focus-visible {
        outline: none;
        box-shadow: 0 4px 12px rgba(0,0,0,0.25), 0 2px 4px rgba(0,0,0,0.15), 0 0 0 3px rgba(255,140,0,0.22);
        border-color: rgba(255,140,0,0.55);
      }

      .kpv2-tab-history-panel .kp-url-row--selected {
        background: linear-gradient(180deg, rgba(255,140,0,0.22) 0%, rgba(255,140,0,0.10) 100%);
        border-color: rgba(255,140,0,0.55);
        box-shadow: 0 4px 14px rgba(255,140,0,0.18), 0 2px 4px rgba(0,0,0,0.15);
      }

      .kpv2-tab-history-panel .kp-url-row--selected:hover {
        background: linear-gradient(180deg, rgba(255,140,0,0.28) 0%, rgba(255,140,0,0.12) 100%);
      }

      /*
       * Page thumbs paint via ::before so the media layer is full-bleed under the
       * 1px border (same pattern as New Tab cards).
       */
      .kpv2-tab-history-panel .kp-url-row.kp-has-page-thumb {
        background-color: #0a0a0a;
        background-image: none;
        border-color: rgba(255, 255, 255, 0.1);
        isolation: isolate;
      }

      .kpv2-tab-history-panel .kp-url-row.kp-has-page-thumb::before {
        content: "";
        position: absolute;
        inset: -1px;
        z-index: 0;
        pointer-events: none;
        border-radius: inherit;
        background-color: #0a0a0a;
        background-image:
          linear-gradient(to bottom, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.78) 100%),
          var(--kp-page-thumb);
        background-size: 100% 100%, cover;
        background-position: center, center;
        background-repeat: no-repeat, no-repeat;
      }

      .kpv2-tab-history-panel .kp-url-row.kp-has-page-thumb:hover {
        background-color: #0a0a0a;
        background-image: none;
      }

      .kpv2-tab-history-panel .kp-url-row.kp-has-page-thumb:hover::before {
        background-image:
          linear-gradient(to bottom, rgba(0, 0, 0, 0.32) 0%, rgba(0, 0, 0, 0.52) 100%),
          var(--kp-page-thumb);
      }

      .kpv2-tab-history-panel .kp-url-row.kp-has-page-thumb.kp-url-row--selected::before {
        background-image:
          linear-gradient(to bottom, rgba(255, 140, 0, 0.35) 0%, rgba(0, 0, 0, 0.72) 100%),
          var(--kp-page-thumb);
      }

      .kpv2-tab-history-panel .kp-url-row.kp-has-page-thumb.kp-url-row--selected:hover::before {
        background-image:
          linear-gradient(to bottom, rgba(255, 140, 0, 0.28) 0%, rgba(0, 0, 0, 0.55) 100%),
          var(--kp-page-thumb);
      }

      .kpv2-tab-history-panel .kp-url-row.kp-has-page-thumb > * {
        position: relative;
        z-index: 1;
      }

      .kpv2-tab-history-panel .kp-url-row.kp-has-page-thumb .kp-url-domain,
      .kpv2-tab-history-panel .kp-url-row.kp-has-page-thumb .kp-url-title,
      .kpv2-tab-history-panel .kp-url-row.kp-has-page-thumb .kp-url-path,
      .kpv2-tab-history-panel .kp-url-row.kp-has-page-thumb .kp-url-visited {
        text-shadow: 0 1px 2px rgba(0, 0, 0, 0.65);
      }

      .kpv2-tab-history-panel .kp-url-content {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: 8px;
        min-width: 0;
        height: 100%;
      }

      .kpv2-tab-history-panel .kp-url-favicon {
        width: 32px;
        height: 32px;
        border-radius: 8px;
        background: rgba(255,255,255,0.08);
        flex: 0 0 auto;
      }

      .kpv2-tab-history-panel .kp-url-text {
        min-width: 0;
        width: 100%;
        flex: 1 1 auto;
        display: flex;
        flex-direction: column;
        gap: 3px;
        overflow: hidden;
      }

      .kpv2-tab-history-panel .kp-url-domain {
        font-size: 11px;
        font-weight: 800;
        letter-spacing: 0.15px;
        color: rgba(255, 200, 130, 0.92);
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        max-width: 100%;
      }

      .kpv2-tab-history-panel .kp-url-title {
        font-size: 12px;
        font-weight: 650;
        color: rgba(255,255,255,0.92);
        overflow: hidden;
        display: -webkit-box;
        -webkit-box-orient: vertical;
        -webkit-line-clamp: 3;
        line-clamp: 3;
        white-space: normal;
        word-break: break-word;
        max-width: 100%;
        line-height: 1.25;
      }

      .kpv2-tab-history-panel .kp-url-path {
        font-size: 10px;
        color: rgba(255,255,255,0.55);
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        max-width: 100%;
      }

      .kpv2-tab-history-panel .kp-url-visited {
        font-size: 10px;
        font-weight: 550;
        color: rgba(255,255,255,0.62);
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        max-width: 100%;
        margin-top: auto;
        padding-top: 4px;
        letter-spacing: 0.01em;
      }

      .kpv2-tab-history-panel .kp-url-branch-badge {
        position: absolute;
        top: 8px;
        right: 8px;
        font-size: 10px;
        font-weight: 700;
        padding: 2px 7px;
        border-radius: 999px;
        border: 1px solid rgba(255,140,0,0.35);
        background: rgba(255,140,0,0.16);
        color: rgba(255,140,0,0.95);
        line-height: 1.2;
        pointer-events: none;
      }

      /* Scrollbars (vertical body + horizontal rails) */
      .kpv2-tab-history-panel ::-webkit-scrollbar {
        width: 8px;
        height: 8px;
      }
      .kpv2-tab-history-panel ::-webkit-scrollbar-track {
        background: rgba(0, 0, 0, 0.1);
      }
      .kpv2-tab-history-panel ::-webkit-scrollbar-thumb {
        background: rgba(255, 255, 255, 0.2);
        border-radius: 4px;
      }
      .kpv2-tab-history-panel ::-webkit-scrollbar-thumb:hover {
        background: rgba(255, 255, 255, 0.3);
      }
    `;
      const shadowRoot = this._panel?.shadowRoot || null;
      const localCss = shadowRoot ? css.replace(".kpv2-tab-history-panel {", ":host {").replaceAll(".kpv2-tab-history-panel ", "") : css;
      injectChromeStyles(shadowRoot || document, {
        attr: "data-kp-tab-history-styles",
        css: localCss
      });
    }
    _ensureDom() {
      if (this._panel && this._tabList && this._tabStatus && this._browserList && this._browserStatus) return;
      const doc = document;
      const panel = doc.createElement("div");
      panel.className = "kpv2-tab-history-panel";
      panel.setAttribute("role", "dialog");
      panel.setAttribute("aria-label", "History");
      const shadowRoot = ensureOpenChromeShadow(panel, { id: "tab-history" });
      const shell = shadowRoot || panel;
      const header = doc.createElement("div");
      header.className = "kpv2-history-header";
      const titleWrap = doc.createElement("div");
      titleWrap.className = "kpv2-history-title-wrap";
      const title = doc.createElement("div");
      title.className = "kpv2-history-title";
      title.textContent = "History";
      const subtitle = doc.createElement("div");
      subtitle.className = "kpv2-history-subtitle";
      subtitle.textContent = "Browser history \xB7 Tab history";
      titleWrap.appendChild(title);
      titleWrap.appendChild(subtitle);
      const closeBtn = doc.createElement("button");
      closeBtn.type = "button";
      closeBtn.className = "kpv2-history-close";
      closeBtn.textContent = "\xD7";
      closeBtn.setAttribute("aria-label", "Close");
      closeBtn.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        this.hide();
      }, true);
      header.appendChild(titleWrap);
      header.appendChild(closeBtn);
      const body = doc.createElement("div");
      body.className = "kpv2-history-body";
      const makeSection = ({ titleText }) => {
        const section = doc.createElement("section");
        section.className = "kpv2-history-section";
        const sectionHeader = doc.createElement("div");
        sectionHeader.className = "kpv2-history-section-header";
        const sectionTitle = doc.createElement("div");
        sectionTitle.className = "kpv2-history-section-title";
        sectionTitle.textContent = titleText;
        const sectionHint = doc.createElement("div");
        sectionHint.className = "kpv2-history-section-hint";
        sectionHint.textContent = "Click to navigate";
        sectionHeader.appendChild(sectionTitle);
        sectionHeader.appendChild(sectionHint);
        const sectionStatus = doc.createElement("div");
        sectionStatus.className = "kpv2-history-section-status";
        sectionStatus.textContent = "Loading\u2026";
        const rail = createUrlListingContainer({
          doc,
          view: "list",
          useInlineStyles: false,
          scrollY: false,
          className: "kpv2-history-rail",
          style: {
            // Rail layout is owned by CSS; keep a minimal fallback if styles lag.
            display: "flex",
            flexDirection: "row",
            overflowX: "auto",
            overflowY: "hidden"
          }
        });
        rail.setAttribute("role", "list");
        section.appendChild(sectionHeader);
        section.appendChild(sectionStatus);
        section.appendChild(rail);
        return { section, sectionStatus, rail };
      };
      const tabSection = makeSection({ titleText: "Tab history" });
      const browserSection = makeSection({ titleText: "Browser history" });
      shell.appendChild(header);
      body.appendChild(browserSection.section);
      body.appendChild(tabSection.section);
      shell.appendChild(body);
      this._panel = panel;
      this._tabStatus = tabSection.sectionStatus;
      this._tabList = tabSection.rail;
      this._browserStatus = browserSection.sectionStatus;
      this._browserList = browserSection.rail;
    }
    async _loadAndRender() {
      if (!this._open) return;
      if (!this._tabStatus || !this._tabList || !this._browserStatus || !this._browserList) return;
      this._tabStatus.textContent = "Loading tab history\u2026";
      this._tabList.textContent = "";
      this._browserStatus.textContent = "Loading browser history\u2026";
      this._browserList.textContent = "";
      await Promise.allSettled([
        this._loadAndRenderTabHistory(),
        this._loadAndRenderBrowserHistory()
      ]);
    }
    async _loadAndRenderTabHistory() {
      if (!this._open) return;
      if (!this._tabStatus || !this._tabList) return;
      let resp = null;
      try {
        resp = await chrome.runtime.sendMessage({ type: MSG.NAVGRAPH_GET });
      } catch {
        resp = null;
      }
      if (!this._open) return;
      if (!resp || resp.type !== MSG.NAVGRAPH_GRAPH || !resp.graph) {
        this._tabStatus.textContent = "Tab history unavailable.";
        return;
      }
      this._lastGraph = resp.graph;
      this._renderGraph(resp.graph);
    }
    async _loadAndRenderBrowserHistory() {
      if (!this._open) return;
      if (!this._browserStatus || !this._browserList) return;
      let resp = null;
      try {
        resp = await chrome.runtime.sendMessage({
          type: MSG.BROWSER_HISTORY_GET,
          query: "",
          maxResults: 40,
          days: 14
        });
      } catch {
        resp = null;
      }
      if (!this._open) return;
      const items = Array.isArray(resp?.items) ? resp.items : [];
      if (!resp || resp.type !== MSG.BROWSER_HISTORY_RESULT) {
        this._browserStatus.textContent = "Browser history unavailable.";
        return;
      }
      if (!items.length) {
        this._browserStatus.textContent = "No recent browser history.";
        this._browserList.textContent = "";
        return;
      }
      this._browserStatus.textContent = "Click an entry to navigate. Press Esc to close.";
      this._renderBrowserHistory(items);
    }
    _renderGraph(graph) {
      if (!this._tabStatus || !this._tabList) return;
      const nodes = Array.isArray(graph.nodes) ? graph.nodes : [];
      const edges = Array.isArray(graph.edges) ? graph.edges : [];
      const cursorId = graph.cursorId;
      if (!nodes.length) {
        this._tabStatus.textContent = "No tab history yet.";
        return;
      }
      this._tabStatus.textContent = "Click an entry to navigate. Press Esc to close.";
      const nodeById = /* @__PURE__ */ new Map();
      for (const n of nodes) {
        if (n && typeof n.id === "number") nodeById.set(n.id, n);
      }
      const structuralKinds = /* @__PURE__ */ new Set(["navigate", "programmatic", "reload"]);
      const children = /* @__PURE__ */ new Map();
      const incoming = /* @__PURE__ */ new Map();
      for (const e of edges) {
        if (!e || !structuralKinds.has(e.kind)) continue;
        const fromId = e.fromId;
        const toId = e.toId;
        if (typeof fromId !== "number" || typeof toId !== "number") continue;
        if (!nodeById.has(fromId) || !nodeById.has(toId)) continue;
        if (!children.has(fromId)) children.set(fromId, []);
        children.get(fromId).push(toId);
        incoming.set(toId, (incoming.get(toId) || 0) + 1);
      }
      const roots = [];
      for (const n of nodes) {
        if (!n || typeof n.id !== "number") continue;
        if (!incoming.get(n.id)) roots.push(n.id);
      }
      const getLastSeen = (id) => {
        const n = nodeById.get(id);
        return Number(n?.tsLastSeen) || Number(n?.tsCreated) || 0;
      };
      const subtreeScoreCache = /* @__PURE__ */ new Map();
      const computeSubtreeScore = (id, seen = /* @__PURE__ */ new Set()) => {
        if (subtreeScoreCache.has(id)) return subtreeScoreCache.get(id);
        if (seen.has(id)) return getLastSeen(id);
        seen.add(id);
        let best = getLastSeen(id);
        const kids = children.get(id) || [];
        for (const k of kids) {
          best = Math.max(best, computeSubtreeScore(k, seen));
        }
        subtreeScoreCache.set(id, best);
        return best;
      };
      roots.sort((a, b) => computeSubtreeScore(b) - computeSubtreeScore(a));
      const rows = [];
      const visited = /* @__PURE__ */ new Set();
      const walk = (id, depth) => {
        if (visited.has(id)) return;
        visited.add(id);
        rows.push({ id, depth: Math.min(depth, 12) });
        const kids = (children.get(id) || []).slice();
        kids.sort((a, b) => getLastSeen(b) - getLastSeen(a));
        for (const k of kids) walk(k, depth + 1);
      };
      for (const r of roots) walk(r, 0);
      if (rows.length < nodeById.size) {
        const remaining = [];
        for (const id of nodeById.keys()) {
          if (!visited.has(id)) remaining.push(id);
        }
        remaining.sort((a, b) => getLastSeen(b) - getLastSeen(a));
        for (const id of remaining) rows.push({ id, depth: 0 });
      }
      const renderItems = [];
      for (const r of rows) {
        const n = nodeById.get(r.id);
        if (!n) continue;
        renderItems.push({
          id: r.id,
          depth: r.depth,
          node: n,
          branchCount: (children.get(r.id) || []).length
        });
      }
      const doRender = () => {
        renderUrlListing({
          container: this._tabList,
          items: renderItems,
          view: "list",
          useInlineStyles: false,
          classNames: HISTORY_CARD_CLASS_NAMES,
          rowTag: "a",
          getTitle: (it) => (it.node?.title || it.node?.url || "").toString(),
          getUrl: (it) => String(it.node?.url || ""),
          showFavicon: true,
          showMetaLine: true,
          showUrlLine: true,
          isSelected: (it) => it.id === cursorId,
          onRowClick: async ({ item, event }) => {
            event.preventDefault();
            event.stopPropagation();
            const url = typeof item?.node?.url === "string" ? item.node.url : "";
            if (!url) return;
            try {
              await chrome.runtime.sendMessage({ type: MSG.NAVGRAPH_JUMP, url });
            } catch {
            }
            this.hide();
          },
          decorateRow: ({ row, item, idx, parts }) => {
            row.dataset.kpTabHistoryId = String(item.id);
            row.setAttribute("role", "listitem");
            const visitTs = Number(item.node?.tsLastSeen) || Number(item.node?.tsCreated) || 0;
            renderThreeLineUrlListingEntry({
              item: {
                url: item.node?.url,
                title: item.node?.title,
                lastVisitTime: visitTs
              },
              parts
            });
            attachPageThumbToHistoryCard(row, item.node?.url);
            if (item.branchCount > 1) {
              const badge = document.createElement("div");
              badge.className = "kp-url-branch-badge";
              badge.textContent = `+${item.branchCount - 1}`;
              badge.setAttribute("aria-label", `${item.branchCount - 1} more branches`);
              row.appendChild(badge);
            }
            row.dataset.kpUrlListingIndex = String(idx);
          }
        });
        if (this._tabList) {
          this._tabList.classList.add("kpv2-history-rail");
          const selected = this._tabList.querySelector(".kp-url-row--selected");
          if (selected && typeof selected.scrollIntoView === "function") {
            try {
              selected.scrollIntoView({ block: "nearest", inline: "nearest", behavior: "smooth" });
            } catch {
              try {
                selected.scrollIntoView();
              } catch {
              }
            }
          }
        }
      };
      try {
        if (typeof document !== "undefined" && typeof document.startViewTransition === "function") {
          document.startViewTransition(() => doRender());
        } else {
          doRender();
        }
      } catch {
        doRender();
      }
    }
    _renderBrowserHistory(items) {
      if (!this._browserList) return;
      const seen = /* @__PURE__ */ new Set();
      const deduped = [];
      for (const it of items) {
        const url = typeof it?.url === "string" ? it.url.trim() : "";
        if (!url) continue;
        if (seen.has(url)) continue;
        seen.add(url);
        deduped.push({
          url,
          title: (it?.title || url || "").toString(),
          lastVisitTime: Number(it?.lastVisitTime) || 0
        });
      }
      const doRender = () => {
        renderUrlListing({
          container: this._browserList,
          items: deduped,
          view: "list",
          useInlineStyles: false,
          classNames: HISTORY_CARD_CLASS_NAMES,
          rowTag: "a",
          getTitle: (it) => it.title,
          getUrl: (it) => it.url,
          showFavicon: true,
          showMetaLine: true,
          showUrlLine: true,
          onRowClick: async ({ item, event }) => {
            event.preventDefault();
            event.stopPropagation();
            try {
              await chrome.runtime.sendMessage({ type: MSG.NAVGRAPH_JUMP, url: item.url });
            } catch {
              try {
                window.location.assign(item.url);
              } catch {
              }
            }
            this.hide();
          },
          decorateRow: ({ row, item, parts }) => {
            row.setAttribute("role", "listitem");
            renderThreeLineUrlListingEntry({ item, parts });
            attachPageThumbToHistoryCard(row, item.url);
          }
        });
        if (this._browserList) {
          this._browserList.classList.add("kpv2-history-rail");
        }
      };
      try {
        if (typeof document !== "undefined" && typeof document.startViewTransition === "function") {
          document.startViewTransition(() => doRender());
        } else {
          doRender();
        }
      } catch {
        doRender();
      }
    }
  };

  // src/config/launcher-sites.js
  init_search_engines();
  var SOCIAL = Object.freeze([
    { title: "Instagram", url: "https://instagram.com", description: "Photo and short-video social network", seed: true },
    { title: "Facebook", url: "https://facebook.com", description: "Friends, groups, and community feed", seed: true },
    { title: "X (Twitter)", url: "https://x.com", description: "Real-time posts and public conversation", seed: true },
    { title: "Reddit", url: "https://reddit.com", description: "Topic communities and discussion threads", seed: true },
    { title: "Bluesky", url: "https://bsky.app", description: "Decentralized microblogging network", seed: true },
    { title: "LinkedIn", url: "https://linkedin.com", description: "Professional networking and careers", seed: true },
    { title: "Threads", url: "https://threads.net", description: "Text-focused social feed from Meta", seed: true },
    { title: "Mastodon", url: "https://mastodon.social", description: "Federated open-source social network", seed: true },
    { title: "TikTok", url: "https://tiktok.com", description: "Short-form video discovery and creation" },
    { title: "WhatsApp", url: "https://whatsapp.com", description: "Encrypted messaging and group chats" },
    { title: "Pinterest", url: "https://pinterest.com", description: "Visual boards for ideas and inspiration" },
    { title: "Discord", url: "https://discord.com", description: "Voice, chat, and community servers" },
    { title: "Telegram", url: "https://telegram.org", description: "Cloud messaging and large channels" },
    { title: "Snapchat", url: "https://snapchat.com", description: "Ephemeral snaps and Stories" },
    { title: "Messenger", url: "https://messenger.com", description: "Facebook messaging app on the web" },
    { title: "Nextdoor", url: "https://nextdoor.com", description: "Neighborhood news and local chat" },
    { title: "Tumblr", url: "https://tumblr.com", description: "Blogs, fandoms, and creative posts" },
    { title: "Quora", url: "https://quora.com", description: "Q&A from experts and communities" },
    { title: "Medium", url: "https://medium.com", description: "Long-form writing and essays" },
    { title: "Behance", url: "https://behance.net", description: "Creative portfolios and project showcases" },
    { title: "Dribbble", url: "https://dribbble.com", description: "Design shots and creative hiring" },
    { title: "Goodreads", url: "https://goodreads.com", description: "Book reviews, lists, and reading goals" },
    { title: "Flickr", url: "https://flickr.com", description: "Photo hosting and photographer community" },
    { title: "Patreon", url: "https://patreon.com", description: "Support creators with memberships" },
    { title: "Substack Notes", url: "https://substack.com", description: "Writer network and short social posts" }
  ]);
  var NEWS = Object.freeze([
    { title: "CNN", url: "https://cnn.com", description: "Breaking news and US/world coverage", seed: true },
    { title: "BBC News", url: "https://bbc.com/news", description: "International news from the BBC", seed: true },
    { title: "NY Times", url: "https://nytimes.com", description: "In-depth reporting and analysis", seed: true },
    { title: "Reuters", url: "https://reuters.com", description: "Wire-service news and business headlines", seed: true },
    { title: "The Guardian", url: "https://theguardian.com", description: "UK and global news and opinion", seed: true },
    { title: "AP News", url: "https://apnews.com", description: "Associated Press wire and photos", seed: true },
    { title: "Google News", url: "https://news.google.com", description: "Personalized news aggregator" },
    { title: "Yahoo News", url: "https://news.yahoo.com", description: "Aggregated headlines and original stories" },
    { title: "MSN", url: "https://msn.com", description: "Microsoft news portal and lifestyle feeds" },
    { title: "Fox News", url: "https://foxnews.com", description: "US news and opinion programming" },
    { title: "NBC News", url: "https://nbcnews.com", description: "Network news and investigations" },
    { title: "ABC News", url: "https://abcnews.go.com", description: "US broadcast news and live updates" },
    { title: "CBS News", url: "https://cbsnews.com", description: "National news and 60 Minutes" },
    { title: "USA Today", url: "https://usatoday.com", description: "National newspaper and sports coverage" },
    { title: "Washington Post", url: "https://washingtonpost.com", description: "Politics and national reporting" },
    { title: "Wall Street Journal", url: "https://wsj.com", description: "Business and finance journalism", seed: true },
    { title: "NPR", url: "https://npr.org", description: "Public radio news and podcasts" },
    { title: "CNBC", url: "https://cnbc.com", description: "Markets, business, and finance TV" },
    { title: "Bloomberg", url: "https://bloomberg.com", description: "Global markets and financial news" },
    { title: "The Atlantic", url: "https://theatlantic.com", description: "Long-form culture and politics" },
    { title: "Politico", url: "https://politico.com", description: "Politics and policy reporting" },
    { title: "Axios", url: "https://axios.com", description: "Short, scannable news briefings" },
    { title: "The Hill", url: "https://thehill.com", description: "Congress, campaigns, and policy" },
    { title: "Al Jazeera", url: "https://aljazeera.com", description: "International news and documentaries" },
    { title: "Time", url: "https://time.com", description: "Magazine news and covers" },
    { title: "Newsweek", url: "https://newsweek.com", description: "Weekly news and opinion magazine" },
    { title: "Daily Mail", url: "https://dailymail.co.uk", description: "UK tabloid news and celebrity", seed: true },
    { title: "The Epoch Times", url: "https://theepochtimes.com", description: "Independent news and China coverage" },
    { title: "NTD", url: "https://ntd.com", description: "New Tang Dynasty news and video" },
    { title: "New York Post", url: "https://nypost.com", description: "NY metro news and entertainment", seed: true },
    { title: "Substack", url: "https://substack.com", description: "Independent newsletters and writers" },
    { title: "TechCrunch", url: "https://techcrunch.com", description: "Startups, tech, and venture news" }
  ]);
  var PRODUCTIVITY = Object.freeze([
    { title: "Gmail", url: "https://gmail.com", description: "Google email and inbox", seed: true },
    { title: "Google Calendar", url: "https://calendar.google.com", description: "Schedules, events, and reminders", seed: true },
    { title: "Google Drive", url: "https://drive.google.com", description: "Cloud files and shared folders", seed: true },
    { title: "Google Docs", url: "https://docs.google.com", description: "Collaborative documents and sheets", seed: true },
    { title: "Notion", url: "https://notion.so", description: "Notes, wikis, and project databases", seed: true },
    { title: "Slack", url: "https://slack.com", description: "Team chat and workplace channels", seed: true },
    { title: "Trello", url: "https://trello.com", description: "Kanban boards for tasks", seed: true },
    { title: "Outlook", url: "https://outlook.live.com", description: "Microsoft email and calendar", seed: true },
    { title: "Microsoft Teams", url: "https://teams.microsoft.com", description: "Meetings, chat, and team hubs" },
    { title: "OneDrive", url: "https://onedrive.live.com", description: "Microsoft cloud storage" },
    { title: "Dropbox", url: "https://dropbox.com", description: "File sync and shared folders" },
    { title: "Evernote", url: "https://evernote.com", description: "Notes, clippings, and notebooks" },
    { title: "Monday.com", url: "https://monday.com", description: "Work OS for teams and projects" },
    { title: "Airtable", url: "https://airtable.com", description: "Spreadsheet-database hybrid apps" },
    { title: "Figma", url: "https://figma.com", description: "Collaborative UI and design files" },
    { title: "Canva", url: "https://canva.com", description: "Easy graphic design templates" },
    { title: "Zoom", url: "https://zoom.us", description: "Video meetings and webinars" },
    { title: "Google Meet", url: "https://meet.google.com", description: "Google video calls" },
    { title: "GitHub", url: "https://github.com", description: "Code hosting and pull requests" },
    { title: "GitLab", url: "https://gitlab.com", description: "DevOps platform and Git repos" },
    { title: "Linear", url: "https://linear.app", description: "Issue tracking for product teams" },
    { title: "Jira", url: "https://atlassian.com/software/jira", description: "Agile project and issue tracking" },
    { title: "Confluence", url: "https://www.atlassian.com/software/confluence", description: "Team wikis and documentation" },
    { title: "Miro", url: "https://miro.com", description: "Online whiteboard for workshops", seed: true },
    { title: "Obsidian Publish", url: "https://obsidian.md", description: "Local-first knowledge base notes" },
    { title: "iCloud", url: "https://icloud.com", description: "Apple mail, photos, and Drive", seed: true }
  ]);
  var VIDEOS = Object.freeze([
    {
      title: "YouTube",
      url: "https://youtube.com",
      description: "World\u2019s largest video hosting platform",
      searchUrlPrefix: "https://www.youtube.com/results?search_query=",
      seed: true
    },
    {
      title: "Rumble",
      url: "https://rumble.com",
      description: "Video hosting with creator-focused payouts",
      searchUrlPrefix: "https://rumble.com/search/all?q=",
      seed: true
    },
    {
      title: "Twitch",
      url: "https://twitch.tv",
      description: "Live streaming for games and creators",
      searchUrlPrefix: "https://www.twitch.tv/search?term=",
      seed: true
    },
    {
      title: "Vimeo",
      url: "https://vimeo.com",
      description: "High-quality creator and pro video hosting",
      searchUrlPrefix: "https://vimeo.com/search?q=",
      seed: true
    },
    {
      title: "Dailymotion",
      url: "https://dailymotion.com",
      description: "Video sharing and publisher player network",
      searchUrlPrefix: "https://www.dailymotion.com/search/",
      seed: true
    },
    {
      title: "Odysee",
      url: "https://odysee.com",
      description: "Decentralized video on the LBRY network",
      searchUrlPrefix: "https://odysee.com/$/search?q=",
      seed: true
    },
    {
      title: "TikTok",
      url: "https://tiktok.com",
      description: "Short-form vertical video feed",
      searchUrlPrefix: "https://www.tiktok.com/search?q="
    },
    {
      title: "Ganjing World",
      url: "https://ganjingworld.com",
      description: "Clean family-friendly video platform",
      searchUrlPrefix: "https://www.ganjingworld.com/search?s=",
      seed: true
    },
    {
      title: "Kick",
      url: "https://kick.com",
      description: "Live streaming alternative for creators",
      searchUrlPrefix: "https://kick.com/search?query="
    },
    { title: "YouTube Music", url: "https://music.youtube.com", description: "Music streaming powered by YouTube" },
    { title: "YouTube Studio", url: "https://studio.youtube.com", description: "Creator dashboard for YouTube channels" },
    { title: "Curiosity Stream", url: "https://curiositystream.com", description: "Documentaries and nonfiction video" },
    { title: "TED", url: "https://ted.com", description: "Ideas talks and TED conferences" },
    { title: "Facebook Watch", url: "https://facebook.com/watch", description: "Shows and videos on Facebook" },
    { title: "Instagram Reels", url: "https://instagram.com/reels", description: "Short clips on Instagram" },
    { title: "Archive Video", url: "https://archive.org/details/movies", description: "Public-domain and archived films" },
    { title: "Plex Discover", url: "https://watch.plex.tv", description: "Free movies and TV via Plex" },
    { title: "Plex", url: "https://plex.tv", description: "Free movies and TV via Plex" }
  ]);
  var ENTERTAINMENT = Object.freeze([
    { title: "Netflix", url: "https://netflix.com", description: "Subscription movies and TV originals", seed: true },
    { title: "Disney+", url: "https://disneyplus.com", description: "Disney, Marvel, Star Wars, and Pixar", seed: true },
    { title: "Hulu", url: "https://hulu.com", description: "On-demand TV and current-season shows", seed: true },
    { title: "YouTube", url: "https://youtube.com", description: "Watch videos and live streams", seed: true },
    { title: "HBO Max", url: "https://max.com", description: "HBO series, movies, and Max originals", seed: true },
    { title: "Prime Video", url: "https://primevideo.com", description: "Amazon streaming movies and shows", seed: true },
    { title: "Paramount+", url: "https://paramountplus.com", description: "CBS, Paramount, and sports streams", seed: true },
    { title: "Peacock", url: "https://peacocktv.com", description: "NBCUniversal streaming library", seed: true },
    { title: "Spotify", url: "https://spotify.com", description: "Music, podcasts, and playlists", seed: true },
    { title: "Suno", url: "https://suno.com", description: "AI music generation and songs", seed: true },
    { title: "Apple TV+", url: "https://tv.apple.com", description: "Apple original series and films" },
    { title: "Crunchyroll", url: "https://crunchyroll.com", description: "Anime streaming and simulcasts" },
    { title: "Tubi", url: "https://tubitv.com", description: "Free ad-supported movies and TV" },
    { title: "Pluto TV", url: "https://pluto.tv", description: "Free live TV channels and on-demand" },
    { title: "IMDb", url: "https://imdb.com", description: "Movie database, ratings, and trailers" },
    { title: "Rotten Tomatoes", url: "https://rottentomatoes.com", description: "Critics and audience movie scores" },
    { title: "Letterboxd", url: "https://letterboxd.com", description: "Film diary and social reviews" },
    { title: "SoundCloud", url: "https://soundcloud.com", description: "Independent music and audio tracks" },
    { title: "Bandcamp", url: "https://bandcamp.com", description: "Buy music directly from artists" },
    { title: "Pandora", url: "https://pandora.com", description: "Personalized radio and music stations" },
    { title: "Twitch", url: "https://twitch.tv", description: "Live games and creator streams" },
    { title: "Steam", url: "https://store.steampowered.com", description: "PC game store and library" },
    { title: "Epic Games", url: "https://store.epicgames.com", description: "Epic Games Store and free games" },
    { title: "Xbox", url: "https://xbox.com", description: "Xbox consoles, Game Pass, and store" },
    { title: "PlayStation", url: "https://playstation.com", description: "PlayStation games and PS Plus" },
    { title: "Nintendo", url: "https://nintendo.com", description: "Nintendo Switch games and news" },
    { title: "Goodreads", url: "https://goodreads.com", description: "Track books and reading lists" },
    { title: "Audible", url: "https://audible.com", description: "Audiobooks and spoken-word content" },
    { title: "Podcasts (Apple)", url: "https://podcasts.apple.com", description: "Apple Podcasts directory and shows" }
  ]);
  var SHOPPING = Object.freeze([
    { title: "Amazon", url: "https://amazon.com", description: "Everything marketplace and Prime shipping", seed: true },
    { title: "eBay", url: "https://ebay.com", description: "Auctions and buy-it-now marketplace", seed: true },
    { title: "Walmart", url: "https://walmart.com", description: "Retail goods with store pickup", seed: true },
    { title: "Target", url: "https://target.com", description: "Home, apparel, and everyday essentials", seed: true },
    { title: "Etsy", url: "https://etsy.com", description: "Handmade, vintage, and craft goods", seed: true },
    { title: "Best Buy", url: "https://bestbuy.com", description: "Consumer electronics and appliances" },
    { title: "Costco", url: "https://costco.com", description: "Membership warehouse club shopping" },
    { title: "AliExpress", url: "https://aliexpress.com", description: "Global marketplace from Chinese sellers" },
    { title: "Temu", url: "https://temu.com", description: "Discount marketplace for everyday goods" },
    { title: "Wayfair", url: "https://wayfair.com", description: "Furniture and home d\xE9cor online" },
    { title: "Home Depot", url: "https://homedepot.com", description: "Home improvement and building supplies" },
    { title: "Lowe's", url: "https://lowes.com", description: "Hardware, appliances, and DIY tools" },
    { title: "IKEA", url: "https://ikea.com", description: "Affordable furniture and home goods" },
    { title: "Apple Store", url: "https://apple.com/shop", description: "Apple devices, accessories, and trade-in" },
    { title: "Newegg", url: "https://newegg.com", description: "PC parts and tech gear" },
    { title: "Craigslist", url: "https://craigslist.org", description: "Local classifieds and personals" },
    { title: "Facebook Marketplace", url: "https://facebook.com/marketplace", description: "Local buy/sell via Facebook" },
    { title: "Shopify", url: "https://shopify.com", description: "E-commerce platform for online stores" },
    { title: "Nike", url: "https://nike.com", description: "Athletic shoes, apparel, and gear" },
    { title: "Adidas", url: "https://adidas.com", description: "Sportswear, sneakers, and training gear" },
    { title: "Zappos", url: "https://zappos.com", description: "Shoes and apparel with free returns" },
    { title: "Nordstrom", url: "https://nordstrom.com", description: "Department store fashion and beauty" },
    { title: "Macy's", url: "https://macys.com", description: "Clothing, home, and department store" },
    { title: "Sephora", url: "https://sephora.com", description: "Beauty products and makeup" },
    { title: "Ulta", url: "https://ulta.com", description: "Beauty retail and salon products" },
    { title: "Chewy", url: "https://chewy.com", description: "Pet food, supplies, and pharmacy" },
    { title: "Ticketmaster", url: "https://ticketmaster.com", description: "Event tickets for concerts and sports" },
    { title: "Rakuten", url: "https://rakuten.com", description: "Cash-back shopping portal" }
  ]);
  var AI = Object.freeze([
    { title: "ChatGPT", url: "https://chat.openai.com", description: "OpenAI conversational AI assistant", seed: true },
    { title: "Claude", url: "https://claude.ai", description: "Anthropic AI assistant for writing and code", seed: true },
    { title: "Grok", url: "https://grok.com", description: "xAI chatbot with real-time knowledge", seed: true },
    { title: "Gemini", url: "https://gemini.google.com", description: "Google multimodal AI assistant", seed: true },
    { title: "Copilot", url: "https://copilot.microsoft.com", description: "Microsoft AI chat and Office help", seed: true },
    { title: "Perplexity", url: "https://perplexity.ai", description: "AI search with cited answers", seed: true },
    { title: "Poe", url: "https://poe.com", description: "Multi-bot AI chat hub", seed: true },
    { title: "Character.AI", url: "https://character.ai", description: "Chat with custom AI characters", seed: true },
    { title: "Hugging Face", url: "https://huggingface.co/chat", description: "Open models and AI chat demos", seed: true },
    { title: "OpenAI", url: "https://openai.com", description: "OpenAI research, API, and products" },
    { title: "Anthropic", url: "https://anthropic.com", description: "Claude maker and AI safety company" },
    { title: "Google AI Studio", url: "https://aistudio.google.com", description: "Build and test Gemini prompts" },
    { title: "NotebookLM", url: "https://notebooklm.google.com", description: "AI research notebooks from your sources" },
    { title: "Midjourney", url: "https://midjourney.com", description: "AI image generation community" },
    { title: "Suno", url: "https://suno.com", description: "Generate songs with AI", seed: true },
    { title: "Leonardo AI", url: "https://leonardo.ai", description: "AI image and asset generation" },
    { title: "Ideogram", url: "https://ideogram.ai", description: "AI images with strong text rendering" },
    { title: "Runway", url: "https://runwayml.com", description: "AI video generation and editing" },
    { title: "ElevenLabs", url: "https://elevenlabs.io", description: "AI voice synthesis and cloning" },
    { title: "Groq", url: "https://groq.com", description: "Ultra-fast LLM inference platform" },
    { title: "Mistral", url: "https://chat.mistral.ai", description: "European open-weight AI chat" },
    { title: "DeepSeek", url: "https://chat.deepseek.com", description: "Reasoning-focused AI chat models" },
    { title: "Cursor", url: "https://cursor.com", description: "AI-powered code editor" },
    { title: "Replicate", url: "https://replicate.com", description: "Run open ML models via API" },
    { title: "Together AI", url: "https://together.ai", description: "Hosted open-source LLM inference" }
  ]);
  var ARCHIVE = Object.freeze([
    { title: "Internet Archive", url: "https://archive.org", description: "Digital library of books, media, and web", seed: true },
    { title: "Web", url: "https://web.archive.org", description: "Wayback Machine archived web pages", seed: true },
    { title: "Texts", url: "https://archive.org/details/texts", description: "Books and texts collection", seed: true },
    { title: "Video", url: "https://archive.org/details/movies", description: "Movies and video archive", seed: true },
    { title: "Audio", url: "https://archive.org/details/audio", description: "Audio recordings and music archive", seed: true },
    { title: "Software", url: "https://archive.org/details/software", description: "Vintage software and apps archive", seed: true },
    { title: "Images", url: "https://archive.org/details/image", description: "Image and photo collections", seed: true },
    { title: "Open Library", url: "https://openlibrary.org", description: "Borrow and catalog books online" },
    { title: "TV News", url: "https://archive.org/details/tv", description: "Searchable TV news archive" },
    { title: "Wayback Machine", url: "https://web.archive.org", description: "Browse historical snapshots of the web" },
    { title: "Archive-It", url: "https://archive-it.org", description: "Curated web archiving for institutions" },
    { title: "Smithsonian Open Access", url: "https://www.si.edu/openaccess", description: "Free Smithsonian collection images" },
    { title: "Europeana", url: "https://europeana.eu", description: "European cultural heritage portal" },
    { title: "Project Gutenberg", url: "https://gutenberg.org", description: "Free public-domain ebooks" },
    { title: "HathiTrust", url: "https://hathitrust.org", description: "Digitized library books and research" },
    { title: "Digital Public Library", url: "https://dp.la", description: "US libraries and museums portal" },
    { title: "Library of Congress", url: "https://loc.gov", description: "US national library collections" },
    { title: "Wikimedia Commons", url: "https://commons.wikimedia.org", description: "Free media files for Wikipedia" },
    { title: "Wikipedia", url: "https://wikipedia.org", description: "Free encyclopedia anyone can edit" },
    { title: "Wikisource", url: "https://wikisource.org", description: "Free source texts and documents" }
  ]);
  var SEARCH_ENGINE_DESCRIPTIONS = Object.freeze({
    Google: "General web search from Google",
    Bing: "Microsoft Bing web search",
    DuckDuckGo: "Privacy-focused web search",
    Yahoo: "Yahoo Search portal",
    "Brave Search": "Independent search from Brave",
    Ecosia: "Search that plants trees",
    Startpage: "Private Google results proxy"
  });
  var LAUNCHER_SITE_CATALOG = Object.freeze({
    social: SOCIAL,
    news: NEWS,
    productivity: PRODUCTIVITY,
    videos: VIDEOS,
    entertainment: ENTERTAINMENT,
    shopping: SHOPPING,
    ai: AI,
    archive: ARCHIVE,
    searches: Object.freeze(
      LAUNCHER_SEARCH_SITES.map(
        (s) => Object.freeze({
          title: s.title,
          url: s.url,
          description: SEARCH_ENGINE_DESCRIPTIONS[s.title] || `${s.title} web search`,
          seed: true,
          isDefault: true
        })
      )
    )
  });
  var LAUNCHER_CATALOG_CATEGORY_KEYS = Object.freeze(
    Object.keys(LAUNCHER_SITE_CATALOG)
  );
  function getLauncherCatalog(categoryKey) {
    return LAUNCHER_SITE_CATALOG[categoryKey] || [];
  }

  // src/utils/launch-deck.js
  init_storage();
  var LAUNCH_DECK_STATE_KEY = "kpLaunchDeckState_v1";
  var LEGACY_HIDDEN_LAUNCH_DECK_KEY = "kpLauncherHiddenLaunchDeck";
  function normalizeLaunchDeckUrl(url) {
    try {
      const u = new URL(String(url || "").trim());
      const host = (u.hostname || "").toLowerCase();
      const path = (u.pathname || "").replace(/\/+$/, "");
      return `${u.protocol}//${host}${path}${u.search || ""}`;
    } catch {
      return String(url || "").trim().toLowerCase();
    }
  }
  function launchDeckHost(url) {
    try {
      return new URL(String(url || "").trim()).hostname.toLowerCase().replace(/^www\./, "");
    } catch {
      return "";
    }
  }
  function emptyCategoryDeckState() {
    return { order: [], removed: [], custom: [] };
  }
  function normalizeCategoryState(raw) {
    const out = emptyCategoryDeckState();
    if (!raw || typeof raw !== "object") return out;
    if (Array.isArray(raw.order)) {
      out.order = raw.order.map((u) => normalizeLaunchDeckUrl(String(u || ""))).filter(Boolean);
    }
    if (Array.isArray(raw.removed)) {
      out.removed = raw.removed.map((u) => normalizeLaunchDeckUrl(String(u || ""))).filter(Boolean);
    }
    if (Array.isArray(raw.custom)) {
      out.custom = raw.custom.map((c) => {
        const url = String(c?.url || "").trim();
        const key = normalizeLaunchDeckUrl(url);
        if (!key) return null;
        return {
          title: String(c?.title || "").trim() || key,
          url
        };
      }).filter(Boolean);
    }
    return out;
  }
  function normalizeLaunchDeckState(raw) {
    const state = /* @__PURE__ */ Object.create(null);
    for (const key of LAUNCHER_CATALOG_CATEGORY_KEYS) {
      state[key] = normalizeCategoryState(raw?.[key]);
    }
    return state;
  }
  function migrateHiddenIntoRemoved(state, hiddenUrls) {
    const next = normalizeLaunchDeckState(state);
    const hidden = (Array.isArray(hiddenUrls) ? hiddenUrls : []).map((u) => normalizeLaunchDeckUrl(String(u || ""))).filter(Boolean);
    if (!hidden.length) return next;
    for (const key of LAUNCHER_CATALOG_CATEGORY_KEYS) {
      const removed = new Set(next[key].removed);
      for (const h of hidden) removed.add(h);
      next[key].removed = Array.from(removed);
    }
    return next;
  }
  async function loadLaunchDeckState() {
    try {
      const raw = await storageGetValue(LAUNCH_DECK_STATE_KEY, null);
      let state = normalizeLaunchDeckState(raw);
      const legacy = await storageGetValue(LEGACY_HIDDEN_LAUNCH_DECK_KEY, null);
      if (Array.isArray(legacy) && legacy.length) {
        state = migrateHiddenIntoRemoved(state, legacy);
        await persistLaunchDeckState(state);
        try {
          await storageSetValue(LEGACY_HIDDEN_LAUNCH_DECK_KEY, []);
        } catch {
        }
      }
      return state;
    } catch (err) {
      console.warn("[launch-deck] Failed to load state:", err);
      return normalizeLaunchDeckState(null);
    }
  }
  async function persistLaunchDeckState(state) {
    try {
      await storageSetValue(LAUNCH_DECK_STATE_KEY, normalizeLaunchDeckState(state));
    } catch (err) {
      console.warn("[launch-deck] Failed to persist state:", err);
    }
  }
  function getCategoryDeckState(state, categoryKey) {
    return state?.[categoryKey] || emptyCategoryDeckState();
  }
  function sitePathPrefix(url) {
    try {
      const path = new URL(String(url || "").trim()).pathname || "";
      if (!path || path === "/") return "";
      return path.replace(/\/+$/, "");
    } catch {
      return "";
    }
  }
  function itemMatchesLaunchSite(itemUrl, site, siblings = null) {
    const siteHost = launchDeckHost(site?.url);
    const itemHost = launchDeckHost(itemUrl);
    if (!siteHost || !itemHost) return false;
    if (itemHost !== siteHost && !itemHost.endsWith("." + siteHost)) {
      return false;
    }
    const sitePath = sitePathPrefix(site.url);
    let itemPath = "";
    try {
      itemPath = (new URL(String(itemUrl || "").trim()).pathname || "").replace(/\/+$/, "");
      if (itemPath === "/") itemPath = "";
    } catch {
      return false;
    }
    if (sitePath) {
      return itemPath === sitePath || itemPath.startsWith(`${sitePath}/`);
    }
    const sibs = Array.isArray(siblings) ? siblings : [];
    for (const sib of sibs) {
      if (!sib?.url || sib.url === site.url) continue;
      const sibHost = launchDeckHost(sib.url);
      const sibPath = sitePathPrefix(sib.url);
      if (!sibPath || !sibHost) continue;
      const hostOk = itemHost === sibHost || itemHost.endsWith("." + sibHost);
      if (!hostOk) continue;
      if (itemPath === sibPath || itemPath.startsWith(`${sibPath}/`)) {
        return false;
      }
    }
    return true;
  }
  function siteVisitStats(site, history2, siblings) {
    let visitCount = 0;
    let lastVisitTime = 0;
    if (!Array.isArray(history2) || !history2.length) {
      return { visitCount, lastVisitTime };
    }
    for (const item of history2) {
      if (!itemMatchesLaunchSite(item?.url, site, siblings)) continue;
      const n = Number(item.visitCount);
      visitCount += Number.isFinite(n) && n > 0 ? n : 1;
      const t = Number(item.lastVisitTime) || 0;
      if (t > lastVisitTime) lastVisitTime = t;
    }
    return { visitCount, lastVisitTime };
  }
  function composeLaunchDeck(categoryKey, opts = {}) {
    if (opts.showDeck === false) return [];
    const catalog = getLauncherCatalog(categoryKey);
    if (!catalog.length && categoryKey !== "searches") return [];
    const catState = getCategoryDeckState(opts.state, categoryKey);
    const removed = new Set(catState.removed || []);
    const history2 = Array.isArray(opts.history) ? opts.history : [];
    const siblings = catalog;
    const byKey = /* @__PURE__ */ new Map();
    const addCandidate = (entry, flags = {}) => {
      const key = normalizeLaunchDeckUrl(entry.url);
      if (!key || removed.has(key)) return;
      const stats = siteVisitStats(entry, history2, siblings);
      const prev = byKey.get(key);
      const next = {
        title: entry.title || key,
        url: entry.url,
        seed: !!entry.seed,
        isDefault: entry.isDefault !== void 0 ? !!entry.isDefault : !!entry.seed,
        searchUrlPrefix: entry.searchUrlPrefix,
        visitCount: stats.visitCount,
        lastVisitTime: stats.lastVisitTime,
        custom: !!flags.custom,
        ...flags
      };
      if (!prev) {
        byKey.set(key, next);
        return;
      }
      byKey.set(key, {
        ...prev,
        ...next,
        title: prev.title || next.title,
        searchUrlPrefix: prev.searchUrlPrefix || next.searchUrlPrefix,
        visitCount: Math.max(prev.visitCount || 0, next.visitCount || 0),
        lastVisitTime: Math.max(prev.lastVisitTime || 0, next.lastVisitTime || 0),
        seed: prev.seed || next.seed,
        custom: prev.custom || next.custom
      });
    };
    for (const site of catalog) {
      if (site.seed) addCandidate(site);
    }
    for (const site of catalog) {
      if (site.seed) continue;
      const stats = siteVisitStats(site, history2, siblings);
      if (stats.visitCount > 0) addCandidate(site);
    }
    for (const custom of catState.custom || []) {
      addCandidate(custom, { custom: true, seed: false, isDefault: false });
    }
    for (const orderedUrl of catState.order || []) {
      const key = normalizeLaunchDeckUrl(orderedUrl);
      if (!key || removed.has(key) || byKey.has(key)) continue;
      const fromCatalog = catalog.find(
        (s) => normalizeLaunchDeckUrl(s.url) === key
      );
      if (fromCatalog) {
        addCandidate(fromCatalog);
        continue;
      }
      const fromCustom = (catState.custom || []).find(
        (c) => normalizeLaunchDeckUrl(c.url) === key
      );
      if (fromCustom) addCandidate(fromCustom, { custom: true });
    }
    const candidates = Array.from(byKey.values());
    const order = Array.isArray(catState.order) ? catState.order : [];
    const hasOrder = order.length > 0;
    if (hasOrder) {
      const orderIndex = new Map(
        order.map((u, i) => [normalizeLaunchDeckUrl(u), i])
      );
      const ordered = [];
      const unordered = [];
      for (const item of candidates) {
        const key = normalizeLaunchDeckUrl(item.url);
        if (orderIndex.has(key)) ordered.push(item);
        else unordered.push(item);
      }
      ordered.sort(
        (a, b) => (orderIndex.get(normalizeLaunchDeckUrl(a.url)) ?? 0) - (orderIndex.get(normalizeLaunchDeckUrl(b.url)) ?? 0)
      );
      unordered.sort(compareByVisitsThenCatalog);
      return [...ordered, ...unordered];
    }
    return candidates.slice().sort(compareByVisitsThenCatalog);
  }
  function compareByVisitsThenCatalog(a, b) {
    const va = Number(a.visitCount) || 0;
    const vb = Number(b.visitCount) || 0;
    if (vb !== va) return vb - va;
    const la = Number(a.lastVisitTime) || 0;
    const lb = Number(b.lastVisitTime) || 0;
    if (lb !== la) return lb - la;
    if (!!b.seed !== !!a.seed) return a.seed ? 1 : -1;
    return String(a.title || "").localeCompare(String(b.title || ""));
  }
  function ensureCategoryDeckState(state, categoryKey) {
    if (!state[categoryKey]) state[categoryKey] = emptyCategoryDeckState();
    return state[categoryKey];
  }
  function removeFromLaunchDeck(state, categoryKey, url) {
    const next = normalizeLaunchDeckState(state);
    if (!LAUNCHER_CATALOG_CATEGORY_KEYS.includes(categoryKey)) return next;
    const cat = ensureCategoryDeckState(next, categoryKey);
    const key = normalizeLaunchDeckUrl(url);
    if (!key) return next;
    if (!cat.removed.includes(key)) cat.removed.push(key);
    cat.order = cat.order.filter((u) => normalizeLaunchDeckUrl(u) !== key);
    cat.custom = cat.custom.filter((c) => normalizeLaunchDeckUrl(c.url) !== key);
    return next;
  }
  function addToLaunchDeck(state, categoryKey, site) {
    const next = normalizeLaunchDeckState(state);
    if (!LAUNCHER_CATALOG_CATEGORY_KEYS.includes(categoryKey)) return next;
    const cat = ensureCategoryDeckState(next, categoryKey);
    const key = normalizeLaunchDeckUrl(site.url);
    if (!key) return next;
    cat.removed = cat.removed.filter((u) => u !== key);
    if (!cat.order.includes(key)) cat.order.push(key);
    const inCatalog = getLauncherCatalog(categoryKey).some(
      (s) => normalizeLaunchDeckUrl(s.url) === key
    );
    if (!inCatalog && !site.fromCatalog) {
      const exists = cat.custom.some((c) => normalizeLaunchDeckUrl(c.url) === key);
      if (!exists) {
        cat.custom.push({
          title: site.title || key,
          url: site.url
        });
      }
    }
    return next;
  }
  function setLaunchDeckOrder(state, categoryKey, urlsInOrder) {
    const next = normalizeLaunchDeckState(state);
    if (!LAUNCHER_CATALOG_CATEGORY_KEYS.includes(categoryKey)) return next;
    const cat = ensureCategoryDeckState(next, categoryKey);
    cat.order = (urlsInOrder || []).map((u) => normalizeLaunchDeckUrl(u)).filter(Boolean);
    return next;
  }
  function getAddableCatalogSites(categoryKey, currentDeck) {
    const onDeck = new Set(
      (currentDeck || []).map((s) => normalizeLaunchDeckUrl(s?.url)).filter(Boolean)
    );
    return getLauncherCatalog(categoryKey).filter((s) => !onDeck.has(normalizeLaunchDeckUrl(s.url))).map((s) => ({ title: s.title, url: s.url }));
  }

  // src/modules/launcher-popover.js
  init_storage();
  var LAUNCHER_NAV_STATE_KEY = "kpLauncherNavState_v1";
  var LAUNCHER_TAB_ICONS = {
    launchDeck: [
      { attrs: { d: "M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z" } },
      { attrs: { d: "M12 15l-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z" } },
      { attrs: { d: "M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0" } },
      { attrs: { d: "M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5" } }
    ],
    bookmarks: [
      { attrs: { d: "M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" } }
    ],
    history: [
      { tag: "circle", attrs: { cx: "12", cy: "12", r: "10" } },
      { attrs: { d: "M12 6v6l4 2" } }
    ],
    social: [
      { attrs: { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" } },
      { tag: "circle", attrs: { cx: "9", cy: "7", r: "4" } },
      { attrs: { d: "M22 21v-2a4 4 0 0 0-3-3.87" } },
      { attrs: { d: "M16 3.13a4 4 0 0 1 0 7.75" } }
    ],
    news: [
      { attrs: { d: "M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 1-2 2Zm0 0a2 2 0 0 1-2-2v-9c0-1.1.9-2 2-2h2" } },
      { attrs: { d: "M18 14h-8" } },
      { attrs: { d: "M15 18h-5" } },
      { attrs: { d: "M10 6h8v4h-8V6z" } }
    ],
    productivity: [
      { attrs: { d: "M13 2L3 14h9l-1 8 10-12h-9l1-8z" } }
    ],
    videos: [
      { tag: "rect", attrs: { x: "2", y: "6", width: "14", height: "12", rx: "2" } },
      { attrs: { d: "m16 10 6-3v10l-6-3z" } }
    ],
    entertainment: [
      { attrs: { d: "M20.2 6 3 11l-.9-2.4c-.3-1.1.3-2.2 1.3-2.5l13.5-4c1.1-.3 2.2.3 2.5 1.3Z" } },
      { attrs: { d: "m6.2 5.3 3.1 3.9" } },
      { attrs: { d: "m12.4 3.4 3.1 4" } },
      { attrs: { d: "M3 11h18v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2Z" } }
    ],
    shopping: [
      { attrs: { d: "M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z" } },
      { attrs: { d: "M3 6h18" } },
      { attrs: { d: "M16 10a4 4 0 0 1-8 0" } }
    ],
    ai: [
      { attrs: { d: "M12 3l1.6 4.4L18 9l-4.4 1.6L12 15l-1.6-4.4L6 9l4.4-1.6L12 3z" } },
      { attrs: { d: "M19 14l.7 2 2 .7-2 .7-.7 2-.7-2-2-.7 2-.7.7-2z" } }
    ],
    archive: [
      { tag: "rect", attrs: { x: "2", y: "3", width: "20", height: "5", rx: "1" } },
      { attrs: { d: "M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8" } },
      { attrs: { d: "M10 12h4" } }
    ],
    searches: [
      { tag: "circle", attrs: { cx: "11", cy: "11", r: "7" } },
      { attrs: { d: "m20 20-3.5-3.5" } }
    ]
  };
  var LauncherPopover = class {
    constructor(keypilot) {
      this._keypilot = keypilot;
      this._container = null;
      this._shadowRoot = null;
      this._shell = null;
      this._tabListContainer = null;
      this._gridContainer = null;
      this._searchInput = null;
      this._currentCategory = "launchDeck";
      this._categorySubTabs = {};
      this._categorySiteFilters = /* @__PURE__ */ Object.create(null);
      this._currentSheet = 0;
      this._itemsPerSheet = 60;
      this._categories = null;
      this._isOpen = false;
      this._openGen = 0;
      this._searchQuery = "";
      this._categoryOrder = ["launchDeck", "bookmarks", "history", "social", "news", "productivity", "videos", "entertainment", "shopping", "ai", "archive", "searches"];
      this._showDefaultSites = true;
      this._launchDeckState = /* @__PURE__ */ Object.create(null);
      this._launchDeckEditMode = false;
      this._launchDeckEditBar = null;
      this._addSitePicker = null;
      this._defaultSites = Object.fromEntries(
        Object.entries(LAUNCHER_SITE_CATALOG).map(([key, list]) => [
          key,
          list.map((s) => ({
            title: s.title,
            url: s.url,
            isDefault: !!s.seed,
            seed: !!s.seed,
            searchUrlPrefix: s.searchUrlPrefix
          }))
        ])
      );
      this._historyLoaded = /* @__PURE__ */ Object.create(null);
      this._cachedTopSites = null;
      this._cachedBookmarks = null;
      this._cachedRecentHistory = null;
      this._boundContainerKeyDown = null;
      this._categorySearchInput = null;
      this._headerSearchDrafts = /* @__PURE__ */ Object.create(null);
      this._videosSearchSiteUrl = "https://youtube.com";
      this._categorySubTabConfig = {
        launchDeck: ["favorites", "history", "search"],
        bookmarks: ["favorites", "history", "search"],
        history: ["favorites", "history", "search"],
        social: ["sites", "favorites", "history", "search"],
        news: ["sites", "favorites", "history", "search"],
        productivity: ["sites", "favorites", "history", "search"],
        videos: ["sites", "favorites", "history", "search"],
        entertainment: ["sites", "favorites", "history", "search"],
        shopping: ["sites", "favorites", "history", "search"],
        ai: ["sites", "favorites", "history", "search"],
        archive: ["sites", "favorites", "history", "search"],
        searches: ["sites", "favorites", "history", "search"]
      };
      this._categoryPageTemplates = {
        archive: {
          renderHeaderSearch: (doc) => this._createArchiveSearchBar(doc)
        },
        videos: {
          renderHeaderSearch: (doc) => this._createVideosSearchBar(doc)
        }
      };
      this._currentPreviewUrl = null;
    }
    /**
     * Show the launcher popover immediately, then enrich data in the background.
     * Blocking on bookmarks/history before paint made open feel very slow and
     * allowed Escape-during-load to race with a late UI mount.
     */
    async show() {
      if (this._isOpen) return;
      this._isOpen = true;
      const gen = ++this._openGen;
      this._historyLoaded = /* @__PURE__ */ Object.create(null);
      this._cachedTopSites = null;
      this._cachedBookmarks = null;
      this._cachedRecentHistory = null;
      this._launchDeckEditMode = false;
      await this._loadLaunchDeckState();
      if (!this._stillOpen(gen)) return;
      await this._loadNavState();
      if (!this._stillOpen(gen)) return;
      this._initCategoriesWithDefaults();
      this._buildUI();
      if (!this._stillOpen(gen)) return;
      this._keypilot.overlayManager?.popupManager?.showModal({
        id: "launcher-popover",
        panel: this._container,
        onRequestClose: () => this.hide()
      });
      this._renderCategory(this._currentCategory);
      await this._loadSharedData(gen);
      if (!this._stillOpen(gen)) return;
      await this._ensureCategoryHistory(this._currentCategory, gen);
    }
    /**
     * Show the launcher popover with the Search tab focused
     */
    async showWithSearchFocus() {
      await this.show();
      const gen = this._openGen;
      setTimeout(() => {
        if (!this._stillOpen(gen)) return;
        this._activateSearchTab({ focus: true });
      }, 0);
    }
    /**
     * Hide the launcher popover
     */
    hide() {
      if (!this._isOpen && !this._container) return;
      this._isOpen = false;
      this._openGen++;
      void this._persistNavState();
      this._launchDeckEditMode = false;
      this._closeAddSitePicker();
      try {
        this._searchInput?.blur?.();
      } catch {
      }
      try {
        this._categorySearchInput?.blur?.();
      } catch {
      }
      try {
        this._keypilot?.focusDetector?.clearTextFocus?.();
      } catch {
      }
      if (this._container && this._boundContainerKeyDown) {
        try {
          this._container.removeEventListener("keydown", this._boundContainerKeyDown, true);
        } catch {
        }
      }
      this._boundContainerKeyDown = null;
      this._keypilot.overlayManager?.popupManager?.hideModal("launcher-popover");
      if (this._container && this._container.parentNode) {
        try {
          this._container.parentNode.removeChild(this._container);
        } catch {
        }
      }
      this._container = null;
      this._shadowRoot = null;
      this._shell = null;
      this._tabListContainer = null;
      this._gridContainer = null;
      this._searchInput = null;
      this._categorySearchInput = null;
      this._headerSearchSlot = null;
      this._headerTitle = null;
      this._headerDescription = null;
      this._primarySubTabRow = null;
      this._siteFilterRow = null;
      this._clearBtn = null;
      this._previewArea = null;
      this._currentPreviewUrl = null;
      this._currentSheet = 0;
      this._searchQuery = "";
      this._headerSearchDrafts = /* @__PURE__ */ Object.create(null);
      this._videosSearchSiteUrl = "https://youtube.com";
      this._categorySiteFilters = /* @__PURE__ */ Object.create(null);
      this._historyLoaded = /* @__PURE__ */ Object.create(null);
      this._cachedTopSites = null;
      this._cachedBookmarks = null;
      this._cachedRecentHistory = null;
    }
    /** @param {number} gen */
    _stillOpen(gen) {
      return this._isOpen && this._openGen === gen;
    }
    /**
     * Check if launcher is open
     */
    isOpen() {
      return this._isOpen;
    }
    /**
     * Extract unique domains from default site list for a category
     */
    _getDefaultDomains(categoryKey) {
      if (!this._defaultSites[categoryKey]) return [];
      const seen = /* @__PURE__ */ new Set();
      const domains = [];
      for (const site of this._defaultSites[categoryKey]) {
        const domain = extractDomain(site.url);
        if (!domain || seen.has(domain)) continue;
        seen.add(domain);
        domains.push(domain);
      }
      return domains;
    }
    /**
     * Pathname without trailing slash (empty for `/`).
     * @param {string} url
     * @returns {string}
     */
    _sitePathPrefix(url) {
      try {
        const path = new URL(String(url || "").trim()).pathname || "";
        if (!path || path === "/") return "";
        return path.replace(/\/+$/, "");
      } catch {
        return "";
      }
    }
    /**
     * Extra history search strings for Sites that include a path
     * (e.g. archive.org/details/texts) so collection visits aren't missed.
     * @param {string} categoryKey
     * @returns {string[]}
     */
    _getHistorySearchQueries(categoryKey) {
      const sites = this._defaultSites[categoryKey];
      if (!Array.isArray(sites)) return [];
      const queries = [];
      const seen = /* @__PURE__ */ new Set();
      for (const site of sites) {
        const path = this._sitePathPrefix(site?.url);
        if (!path) continue;
        const domain = extractDomain(site.url);
        if (!domain) continue;
        const q = `${domain}${path}`;
        if (seen.has(q)) continue;
        seen.add(q);
        queries.push(q);
        queries.push(`https://${q}`);
      }
      return queries;
    }
    /**
     * Immediate category shell using composed Launch Decks (seeds only until history loads).
     * Domain-history tabs start empty and fill in when first selected.
     * `sites` = Launch Deck; `favorites` = bookmarks; `history` = visits.
     */
    _initCategoriesWithDefaults() {
      const emptyLists = () => ({ sites: [], history: [], favorites: [] });
      this._categories = {
        launchDeck: {
          label: "Launch Deck",
          description: "Toolbar bookmarks and your most visited sites",
          ...emptyLists()
        },
        bookmarks: {
          label: "Bookmarks",
          description: "Your saved bookmarks and frequently visited sites",
          ...emptyLists()
        },
        history: {
          label: "Recent",
          description: "Sites you have visited most recently",
          ...emptyLists()
        },
        social: {
          label: "Social Media",
          description: "Stay connected across social networks",
          sites: [],
          history: [],
          favorites: []
        },
        news: {
          label: "News",
          description: "Headlines and reporting from major outlets",
          sites: [],
          history: [],
          favorites: []
        },
        productivity: {
          label: "Productivity",
          description: "Mail, docs, calendars, and work tools",
          sites: [],
          history: [],
          favorites: []
        },
        videos: {
          label: "Videos",
          description: "Watch and search video sites",
          sites: [],
          history: [],
          favorites: []
        },
        entertainment: {
          label: "Entertainment",
          description: "Streaming and entertainment destinations",
          sites: [],
          history: [],
          favorites: []
        },
        shopping: {
          label: "Shopping",
          description: "Stores and marketplaces",
          sites: [],
          history: [],
          favorites: []
        },
        ai: {
          label: "AI",
          description: "Chatbots and AI assistants",
          sites: [],
          history: [],
          favorites: []
        },
        archive: {
          label: "Internet Archive",
          description: "Search and browse the Internet Archive library",
          sites: [],
          history: [],
          favorites: []
        },
        searches: {
          label: "Searches",
          description: "Search engines and recent web searches",
          sites: [],
          history: [],
          favorites: []
        }
      };
      this._recomposeCatalogDecks();
      this._initDefaultSubTabs();
    }
    /**
     * Rebuild composed Launch Decks for all catalog categories.
     * @param {string[]} [onlyKeys]
     */
    _recomposeCatalogDecks(onlyKeys = null) {
      if (!this._categories) return;
      const keys = onlyKeys || LAUNCHER_CATALOG_CATEGORY_KEYS;
      for (const key of keys) {
        if (!this._categories[key]) continue;
        if (!LAUNCHER_CATALOG_CATEGORY_KEYS.includes(key)) continue;
        const history2 = this._categories[key].history || [];
        this._categories[key].sites = composeLaunchDeck(key, {
          state: this._launchDeckState,
          history: history2,
          showDeck: this._showDefaultSites
        });
      }
    }
    /**
     * @returns {boolean}
     */
    _isCatalogCategory(categoryKey = this._currentCategory) {
      return LAUNCHER_CATALOG_CATEGORY_KEYS.includes(categoryKey);
    }
    /**
     * Pick default sub-tab per category from current item counts.
     * Prefers Sites → Favorites → History when present.
     * @param {{ force?: boolean, onlyKeys?: string[] }} [opts]
     */
    _initDefaultSubTabs(opts = {}) {
      const force = !!opts.force;
      const onlyKeys = Array.isArray(opts.onlyKeys) ? new Set(opts.onlyKeys) : null;
      for (const categoryKey in this._categories) {
        if (onlyKeys && !onlyKeys.has(categoryKey)) continue;
        const subTabConfig = this._categorySubTabConfig[categoryKey] || ["favorites", "history"];
        const current = this._categorySubTabs[categoryKey];
        if (!force && subTabConfig.includes(current)) continue;
        const sitesCount = this._categories[categoryKey].sites?.length || 0;
        const historyCount = this._categories[categoryKey].history?.length || 0;
        const favoritesCount = this._categories[categoryKey].favorites?.length || 0;
        if (categoryKey === "launchDeck" && subTabConfig.includes("favorites")) {
          this._categorySubTabs[categoryKey] = "favorites";
        } else if (sitesCount > 0 && subTabConfig.includes("sites")) {
          this._categorySubTabs[categoryKey] = "sites";
        } else if (favoritesCount > 0 && subTabConfig.includes("favorites")) {
          this._categorySubTabs[categoryKey] = "favorites";
        } else if (historyCount > 0 && subTabConfig.includes("history")) {
          this._categorySubTabs[categoryKey] = "history";
        } else {
          this._categorySubTabs[categoryKey] = subTabConfig.includes("history") ? "history" : subTabConfig.find((t) => t !== "search") || subTabConfig[0];
        }
      }
    }
    /**
     * Keep the stored sub-tab valid for a category's current config.
     * @param {string} categoryKey
     */
    _ensureValidSubTab(categoryKey) {
      const cfg = this._categorySubTabConfig[categoryKey] || ["favorites", "history", "search"];
      const current = this._categorySubTabs[categoryKey];
      if (cfg.includes(current)) return;
      if (categoryKey === "launchDeck" && cfg.includes("favorites")) {
        this._categorySubTabs[categoryKey] = "favorites";
        return;
      }
      this._categorySubTabs[categoryKey] = cfg.find((t) => t !== "search") || cfg[0];
    }
    /**
     * Restore last selected category + sub-tabs. Launch Deck is the default
     * until the user has chosen another tab.
     */
    async _loadNavState() {
      try {
        const raw = await storageGetValue(LAUNCHER_NAV_STATE_KEY, null);
        const category = raw && typeof raw.category === "string" && this._categoryOrder.includes(raw.category) ? raw.category : "launchDeck";
        this._currentCategory = category;
        if (raw && raw.subTabs && typeof raw.subTabs === "object") {
          for (const [key, value] of Object.entries(raw.subTabs)) {
            const cfg = this._categorySubTabConfig[key];
            if (cfg && cfg.includes(value)) {
              this._categorySubTabs[key] = value;
            }
          }
        }
        this._ensureValidSubTab(this._currentCategory);
      } catch (err) {
        console.warn("[LauncherPopover] Failed to load launcher nav state:", err);
        this._currentCategory = "launchDeck";
      }
    }
    /**
     * Persist the active category and per-category sub-tab so the next open
     * returns to the same place.
     */
    _persistNavState() {
      const payload = {
        category: this._currentCategory,
        subTabs: { ...this._categorySubTabs }
      };
      void storageSetValue(LAUNCHER_NAV_STATE_KEY, payload).catch((err) => {
        console.warn("[LauncherPopover] Failed to persist launcher nav state:", err);
      });
    }
    /**
     * Load bookmarks + top sites + recent history once (parallel).
     * Domain history is lazy per category.
     * @param {number} gen
     */
    async _loadSharedData(gen) {
      try {
        const [bookmarks, topSites, recentHistory] = await Promise.all([
          this._getBookmarks(),
          this._getTopSites(),
          this._getRecentHistory()
        ]);
        if (!this._stillOpen(gen) || !this._categories) return;
        this._cachedBookmarks = bookmarks;
        this._cachedTopSites = topSites;
        this._cachedRecentHistory = recentHistory;
        this._categories.launchDeck.sites = [];
        this._categories.launchDeck.favorites = this._filterToolbarBookmarks(bookmarks);
        this._categories.launchDeck.history = topSites;
        this._categories.bookmarks.sites = [];
        this._categories.bookmarks.favorites = bookmarks;
        this._categories.bookmarks.history = topSites;
        this._categories.history.sites = [];
        this._categories.history.favorites = [];
        this._categories.history.history = topSites;
        const searchDomains = this._getDefaultDomains("searches");
        this._categories.searches.favorites = this._filterByDomains(bookmarks, searchDomains);
        this._categories.searches.history = [];
        const themeKeys = [
          "social",
          "news",
          "productivity",
          "videos",
          "entertainment",
          "shopping",
          "ai",
          "archive"
        ];
        for (const key of themeKeys) {
          if (!this._categories[key]) continue;
          const domains = this._getDefaultDomains(key);
          this._categories[key].favorites = this._filterByDomains(bookmarks, domains);
        }
        this._historyLoaded.launchDeck = true;
        this._historyLoaded.bookmarks = true;
        this._historyLoaded.history = true;
        this._initDefaultSubTabs({
          onlyKeys: ["launchDeck", "bookmarks", "history", "searches"]
        });
        try {
          this._updateSubTabsUI?.();
        } catch {
        }
        try {
          this._updateSubTabStyles?.();
        } catch {
        }
        try {
          this._updateTabCounts?.();
        } catch {
        }
        if (this._gridContainer) {
          this._renderCategory(this._currentCategory);
        }
      } catch (error) {
        console.error("[LauncherPopover] Error loading shared data:", error);
      }
    }
    /**
     * Fetch Sites-domain history for a category the first time it's viewed.
     * History consistently tracks visits to pages under that category's Sites list.
     * @param {string} categoryKey
     * @param {number} [gen]
     */
    async _ensureCategoryHistory(categoryKey, gen = this._openGen) {
      if (!categoryKey || !this._categories?.[categoryKey]) return;
      if (this._historyLoaded[categoryKey]) return;
      const domains = this._getDefaultDomains(categoryKey);
      if (!domains.length) {
        this._historyLoaded[categoryKey] = true;
        return;
      }
      this._historyLoaded[categoryKey] = "pending";
      try {
        const history2 = await this._getHistoryForDomains(domains, {
          queries: this._getHistorySearchQueries(categoryKey),
          maxResults: 300,
          days: 30
        });
        if (!this._stillOpen(gen) || !this._categories?.[categoryKey]) return;
        this._categories[categoryKey].history = history2;
        if (Array.isArray(this._cachedBookmarks)) {
          this._categories[categoryKey].favorites = this._filterByDomains(
            this._cachedBookmarks,
            domains
          );
        }
        if (this._isCatalogCategory(categoryKey)) {
          this._recomposeCatalogDecks([categoryKey]);
        }
        this._historyLoaded[categoryKey] = true;
        this._ensureValidSiteFilter(categoryKey);
        const sitesCount = this._categories[categoryKey].sites?.length || 0;
        const favoritesCount = this._categories[categoryKey].favorites?.length || 0;
        if (history2.length > 0 && sitesCount === 0 && favoritesCount === 0) {
          const subTabConfig = this._categorySubTabConfig[categoryKey] || ["favorites", "history"];
          this._categorySubTabs[categoryKey] = subTabConfig.includes("history") ? "history" : subTabConfig[0];
        }
        try {
          this._updateSubTabsUI?.();
        } catch {
        }
        try {
          this._updateSubTabStyles?.();
        } catch {
        }
        try {
          this._updateTabCounts?.();
        } catch {
        }
        if (this._currentCategory === categoryKey && this._gridContainer) {
          this._renderCategory(categoryKey);
        }
      } catch (error) {
        console.error("[LauncherPopover] Error loading category history:", categoryKey, error);
        this._historyLoaded[categoryKey] = true;
      }
    }
    /**
     * Apply "Show Launch Deck" checkbox: recompose or clear catalog decks.
     */
    _applyDefaultSitesVisibility() {
      if (!this._categories) return;
      if (!this._showDefaultSites) {
        this._launchDeckEditMode = false;
        this._closeAddSitePicker();
      }
      this._recomposeCatalogDecks();
    }
    /**
     * @returns {Promise<void>}
     */
    async _loadLaunchDeckState() {
      try {
        this._launchDeckState = await loadLaunchDeckState();
      } catch (err) {
        console.warn("[LauncherPopover] Failed to load Launch Deck state:", err);
        this._launchDeckState = /* @__PURE__ */ Object.create(null);
      }
    }
    /**
     * @returns {Promise<void>}
     */
    async _persistLaunchDeckState() {
      try {
        await persistLaunchDeckState(this._launchDeckState);
      } catch (err) {
        console.warn("[LauncherPopover] Failed to persist Launch Deck state:", err);
      }
    }
    /**
     * Remove a Launch Deck card for the active catalog category.
     * @param {string} url
     */
    async _hideLaunchDeckItem(url) {
      const categoryKey = this._currentCategory;
      if (!this._isCatalogCategory(categoryKey)) return;
      this._launchDeckState = removeFromLaunchDeck(
        this._launchDeckState,
        categoryKey,
        url
      );
      void this._persistLaunchDeckState();
      this._recomposeCatalogDecks([categoryKey]);
      const key = normalizeLaunchDeckUrl(url);
      if (this._currentPreviewUrl && normalizeLaunchDeckUrl(this._currentPreviewUrl) === key) {
        try {
          this._hidePreview();
        } catch {
        }
      }
      try {
        this._updateTabCounts?.();
      } catch {
      }
      try {
        this._updateSubTabsUI?.();
      } catch {
      }
      try {
        this._updateLaunchDeckEditBar?.();
      } catch {
      }
      if (this._gridContainer) {
        this._renderCategory(this._currentCategory);
      }
    }
    /**
     * Persist current composed deck order for a category.
     * @param {string} categoryKey
     */
    async _persistCurrentDeckOrder(categoryKey) {
      if (!this._isCatalogCategory(categoryKey)) return;
      const urls = (this._categories?.[categoryKey]?.sites || []).map((s) => s?.url).filter(Boolean);
      this._launchDeckState = setLaunchDeckOrder(
        this._launchDeckState,
        categoryKey,
        urls
      );
      await this._persistLaunchDeckState();
    }
    /**
     * Move a Launch Deck card up/down while editing.
     * @param {string} url
     * @param {-1|1} delta
     */
    async _moveLaunchDeckItem(url, delta) {
      const categoryKey = this._currentCategory;
      if (!this._isCatalogCategory(categoryKey) || !this._launchDeckEditMode) return;
      const sites = this._categories?.[categoryKey]?.sites;
      if (!Array.isArray(sites) || !sites.length) return;
      const key = normalizeLaunchDeckUrl(url);
      const idx = sites.findIndex((s) => normalizeLaunchDeckUrl(s.url) === key);
      if (idx < 0) return;
      const nextIdx = idx + delta;
      if (nextIdx < 0 || nextIdx >= sites.length) return;
      const next = sites.slice();
      const [item] = next.splice(idx, 1);
      next.splice(nextIdx, 0, item);
      this._categories[categoryKey].sites = next;
      await this._persistCurrentDeckOrder(categoryKey);
      this._renderCategory(categoryKey);
    }
    /**
     * Add a site to the current Launch Deck (from picker or custom URL).
     * @param {{ title: string, url: string, fromCatalog?: boolean }} site
     */
    async _addLaunchDeckSite(site) {
      const categoryKey = this._currentCategory;
      if (!this._isCatalogCategory(categoryKey)) return;
      const catBefore = this._launchDeckState?.[categoryKey];
      if (!catBefore?.order?.length) {
        const existing = (this._categories?.[categoryKey]?.sites || []).map((s) => s.url).filter(Boolean);
        if (existing.length) {
          this._launchDeckState = setLaunchDeckOrder(
            this._launchDeckState,
            categoryKey,
            existing
          );
        }
      }
      this._launchDeckState = addToLaunchDeck(
        this._launchDeckState,
        categoryKey,
        site
      );
      await this._persistLaunchDeckState();
      this._recomposeCatalogDecks([categoryKey]);
      this._closeAddSitePicker();
      try {
        this._updateTabCounts?.();
      } catch {
      }
      try {
        this._updateSubTabsUI?.();
      } catch {
      }
      this._renderCategory(categoryKey);
    }
    /**
     * @param {boolean} enabled
     */
    _setLaunchDeckEditMode(enabled) {
      if (!this._isCatalogCategory()) {
        this._launchDeckEditMode = false;
      } else {
        this._launchDeckEditMode = !!enabled;
      }
      if (!this._launchDeckEditMode) this._closeAddSitePicker();
      this._updateLaunchDeckEditBar();
      this._renderCategory(this._currentCategory);
    }
    _closeAddSitePicker() {
      if (this._addSitePicker?.parentNode) {
        try {
          this._addSitePicker.remove();
        } catch {
        }
      }
      this._addSitePicker = null;
    }
    /**
     * Bookmarks that live on Chrome's bookmarks bar (toolbar), including
     * URLs nested in folders on the bar.
     * @param {Array<{url?: string, isToolbar?: boolean, parentId?: string}>} bookmarks
     * @returns {Array}
     */
    _filterToolbarBookmarks(bookmarks) {
      if (!Array.isArray(bookmarks) || !bookmarks.length) return [];
      return bookmarks.filter(
        (b) => !!(b?.url && (b.isToolbar === true || b.parentId === "1"))
      );
    }
    /**
     * Get bookmarks via message passing to background script
     */
    async _getBookmarks() {
      try {
        const response = await chrome.runtime.sendMessage({
          type: MSG.GET_BOOKMARKS
        });
        if (response && response.success && response.bookmarks) {
          return response.bookmarks;
        }
        console.warn("[LauncherPopover] Failed to get bookmarks:", response?.error);
        return [];
      } catch (error) {
        console.warn("[LauncherPopover] Bookmarks message failed:", error);
        return [];
      }
    }
    /**
     * Get top visited sites from history via message passing
     */
    async _getTopSites() {
      try {
        const response = await chrome.runtime.sendMessage({
          type: MSG.GET_TOP_SITES,
          // Background already aggregates to top 100 domains; 250 raw hits is enough.
          maxResults: 250,
          days: 30
        });
        if (response && response.success && response.topSites) {
          return response.topSites;
        }
        console.warn("[LauncherPopover] Failed to get top sites:", response?.error);
        return [];
      } catch (error) {
        console.error("[LauncherPopover] Error getting top sites:", error);
        return [];
      }
    }
    /**
     * Recent browser history rows (for bookmark ↔ visit intersection).
     */
    async _getRecentHistory() {
      try {
        const response = await chrome.runtime.sendMessage({
          type: MSG.GET_RECENT_HISTORY,
          maxResults: 1e3,
          days: 30
        });
        if (response && response.success && Array.isArray(response.items)) {
          return response.items;
        }
        console.warn("[LauncherPopover] Failed to get recent history:", response?.error);
        return [];
      } catch (error) {
        console.error("[LauncherPopover] Error getting recent history:", error);
        return [];
      }
    }
    /**
     * Normalize a URL for bookmark ↔ history matching.
     * @param {string} url
     * @returns {string}
     */
    _normalizeVisitUrl(url) {
      return normalizeLaunchDeckUrl(url);
    }
    /**
     * Bookmarks Launch Deck: up to `limit` bookmarked URLs ranked by visit frequency.
     * Exact history URL match preferred; otherwise host-level visit totals.
     * @param {Array<{title?: string, url?: string, dateAdded?: number}>} bookmarks
     * @param {Array<{url?: string, visitCount?: number, lastVisitTime?: number}>} historyItems
     * @param {number} [limit]
     * @returns {Array<{title: string, url: string, dateAdded?: number, visitCount: number, lastVisitTime: number, isDefault: boolean}>}
     */
    _buildMostVisitedBookmarkedSites(bookmarks, historyItems, limit = 50) {
      if (!Array.isArray(bookmarks) || !bookmarks.length) return [];
      const history2 = Array.isArray(historyItems) ? historyItems : [];
      const cap = Math.max(1, Math.min(100, Number(limit) || 50));
      const visitsByUrl = /* @__PURE__ */ new Map();
      const lastByUrl = /* @__PURE__ */ new Map();
      const visitsByHost = /* @__PURE__ */ new Map();
      const lastByHost = /* @__PURE__ */ new Map();
      for (const item of history2) {
        if (!item?.url) continue;
        const key = this._normalizeVisitUrl(item.url);
        const vc = Number(item.visitCount) || 0;
        const lt = Number(item.lastVisitTime) || 0;
        if (vc > 0) {
          visitsByUrl.set(key, Math.max(visitsByUrl.get(key) || 0, vc));
        }
        if (lt > 0) {
          lastByUrl.set(key, Math.max(lastByUrl.get(key) || 0, lt));
        }
        try {
          const host = new URL(item.url).hostname.toLowerCase().replace(/^www\./, "");
          if (!host) continue;
          visitsByHost.set(host, (visitsByHost.get(host) || 0) + Math.max(vc, 0));
          lastByHost.set(host, Math.max(lastByHost.get(host) || 0, lt));
        } catch {
        }
      }
      const bestByUrl = /* @__PURE__ */ new Map();
      for (const bm of bookmarks) {
        if (!bm?.url) continue;
        const key = this._normalizeVisitUrl(bm.url);
        let visitCount = visitsByUrl.get(key) || 0;
        let lastVisitTime = lastByUrl.get(key) || 0;
        if (visitCount <= 0) {
          try {
            const host = new URL(bm.url).hostname.toLowerCase().replace(/^www\./, "");
            visitCount = visitsByHost.get(host) || 0;
            lastVisitTime = lastByHost.get(host) || 0;
          } catch {
          }
        }
        if (visitCount <= 0) continue;
        const prev = bestByUrl.get(key);
        if (!prev || visitCount > prev.visitCount || visitCount === prev.visitCount && lastVisitTime > prev.lastVisitTime) {
          bestByUrl.set(key, {
            title: bm.title || extractDomain(bm.url) || "Untitled",
            url: bm.url,
            dateAdded: bm.dateAdded,
            visitCount,
            lastVisitTime,
            isDefault: true
          });
        }
      }
      return Array.from(bestByUrl.values()).sort((a, b) => {
        const scoreDiff = b.visitCount - a.visitCount;
        if (scoreDiff !== 0) return scoreDiff;
        return (b.lastVisitTime || 0) - (a.lastVisitTime || 0);
      }).slice(0, cap);
    }
    /**
     * Get history for specific domains via message passing
     * @param {string[]} domains
     * @param {{ queries?: string[], maxResults?: number, days?: number }} [opts]
     */
    async _getHistoryForDomains(domains, opts = {}) {
      try {
        const response = await chrome.runtime.sendMessage({
          type: MSG.GET_HISTORY_FOR_DOMAINS,
          domains,
          queries: Array.isArray(opts.queries) ? opts.queries : [],
          days: opts.days || 30,
          maxResults: opts.maxResults || 300
        });
        if (response && response.success && response.history) {
          return response.history;
        }
        console.warn("[LauncherPopover] Failed to get history for domains:", response?.error);
        return [];
      } catch (error) {
        console.error("[LauncherPopover] Error getting history for domains:", error);
        return [];
      }
    }
    /**
     * Extract recent search queries from already-fetched top-site rows (no extra API call).
     * @param {Array<{title?: string, url?: string}>} topSites
     */
    _extractRecentSearches(topSites) {
      if (!Array.isArray(topSites)) return [];
      const searches = [];
      const seenQueries = /* @__PURE__ */ new Set();
      for (const item of topSites) {
        if (!item?.url) continue;
        try {
          const url = new URL(item.url);
          let query = null;
          if (url.hostname.includes("google.com") && url.pathname === "/search") {
            query = url.searchParams.get("q");
          } else if (url.hostname.includes("bing.com") && url.pathname === "/search") {
            query = url.searchParams.get("q");
          } else if (url.hostname.includes("duckduckgo.com")) {
            query = url.searchParams.get("q");
          } else if (url.hostname.includes("yahoo.com") && url.pathname === "/search") {
            query = url.searchParams.get("p");
          }
          if (query && !seenQueries.has(query)) {
            seenQueries.add(query);
            searches.push({
              title: query,
              url: item.url
            });
          }
        } catch {
        }
      }
      return searches.slice(0, 50);
    }
    /**
     * Filter items by matching domains
     */
    _filterByDomains(items, domains) {
      return items.filter((item) => {
        const itemDomain = extractDomain(item.url);
        if (!itemDomain) return false;
        return domains.some((domain) => itemDomain === domain || itemDomain.endsWith("." + domain));
      });
    }
    /**
     * Whether an item URL belongs to a Sites entry.
     * Path-aware: `/details/texts` only matches that collection prefix.
     * Host-aware: `web.archive.org` is not claimed by parent `archive.org`.
     * Root sites exclude URLs claimed by more-specific sibling path sites.
     * @param {string} itemUrl
     * @param {{ url?: string, title?: string }} site
     * @param {Array<{ url?: string, title?: string }>} [siblings]
     */
    _itemMatchesSite(itemUrl, site, siblings = null) {
      const siteDomain = extractDomain(site?.url);
      const itemDomain = extractDomain(itemUrl);
      if (!siteDomain || !itemDomain) return false;
      const sibs = Array.isArray(siblings) ? siblings : [];
      if (itemDomain !== siteDomain) {
        const exactHostSibling = sibs.find((s) => {
          if (!s?.url || s.url === site.url) return false;
          return extractDomain(s.url) === itemDomain;
        });
        if (exactHostSibling) return false;
        if (!itemDomain.endsWith("." + siteDomain)) return false;
      }
      const sitePath = this._sitePathPrefix(site.url);
      let itemPath = "";
      try {
        itemPath = (new URL(String(itemUrl || "").trim()).pathname || "").replace(/\/+$/, "");
        if (itemPath === "/") itemPath = "";
      } catch {
        return false;
      }
      if (sitePath) {
        return itemPath === sitePath || itemPath.startsWith(`${sitePath}/`);
      }
      for (const sib of sibs) {
        if (!sib?.url || sib.url === site.url) continue;
        const sibDomain = extractDomain(sib.url);
        const sibPath = this._sitePathPrefix(sib.url);
        if (!sibPath || !sibDomain) continue;
        const hostOk = itemDomain === sibDomain || itemDomain.endsWith("." + sibDomain);
        if (!hostOk) continue;
        if (itemPath === sibPath || itemPath.startsWith(`${sibPath}/`)) {
          return false;
        }
      }
      return true;
    }
    /**
     * Sum of Chrome history visitCounts for URLs belonging to a Launch Deck site.
     * @param {{ url?: string }} site
     * @param {Array<{url?: string, visitCount?: number}>} history
     * @param {Array<{url?: string}>} siblings
     * @returns {number}
     */
    _siteVisitScore(site, history2, siblings) {
      if (!Array.isArray(history2) || !history2.length) return 0;
      let score = 0;
      for (const item of history2) {
        if (!this._itemMatchesSite(item?.url, site, siblings)) continue;
        const n = Number(item.visitCount);
        score += Number.isFinite(n) && n > 0 ? n : 1;
      }
      return score;
    }
    /**
     * Most recent lastVisitTime among history rows for a Launch Deck site.
     * @param {{ url?: string }} site
     * @param {Array<{url?: string, lastVisitTime?: number}>} history
     * @param {Array<{url?: string}>} siblings
     * @returns {number}
     */
    _siteLastVisitTime(site, history2, siblings) {
      if (!Array.isArray(history2) || !history2.length) return 0;
      let latest = 0;
      for (const item of history2) {
        if (!this._itemMatchesSite(item?.url, site, siblings)) continue;
        const t = Number(item.lastVisitTime) || 0;
        if (t > latest) latest = t;
      }
      return latest;
    }
    /**
     * Curated Sites with ≥1 matching item in the given list (favorites or history).
     * Site-filter subtabs never show a 0-count entry.
     * @param {string} categoryKey
     * @param {'favorites'|'history'} listKey
     * @returns {Array<{title: string, url: string}>}
     */
    _getSitesWithPrimaryItems(categoryKey, listKey) {
      const sites = this._defaultSites[categoryKey];
      const items = this._categories?.[categoryKey]?.[listKey];
      if (!Array.isArray(sites) || !sites.length || !Array.isArray(items) || !items.length) {
        return [];
      }
      return sites.filter(
        (site) => items.some((item) => this._itemMatchesSite(item?.url, site, sites))
      );
    }
    /**
     * Keep the category's site filter valid for Favorites/History.
     * Filter set depends on the active primary sub-tab (bookmarks vs visits).
     * @param {string} categoryKey
     */
    _ensureValidSiteFilter(categoryKey) {
      const primary = this._categorySubTabs[categoryKey] || "history";
      if (primary !== "favorites" && primary !== "history") {
        this._categorySiteFilters[categoryKey] = null;
        return;
      }
      const sitesWithItems = this._getSitesWithPrimaryItems(categoryKey, primary);
      const current = this._categorySiteFilters[categoryKey];
      if (!sitesWithItems.length) {
        this._categorySiteFilters[categoryKey] = null;
        return;
      }
      const stillValid = sitesWithItems.some((s) => s.url === current);
      if (!stillValid) {
        this._categorySiteFilters[categoryKey] = sitesWithItems[0].url;
      }
    }
    /**
     * Items for the active primary sub-tab, optionally filtered by Sites domain/path.
     * Launch Deck is sorted by visit frequency (most visited first).
     * @param {string} categoryKey
     * @returns {Array}
     */
    _getActiveSubTabItems(categoryKey) {
      const category = this._categories?.[categoryKey];
      if (!category) return [];
      const currentSubTab = this._categorySubTabs[categoryKey] || "history";
      if (currentSubTab === "search") {
        return this._getCategorySearchResults(categoryKey);
      }
      let items = category[currentSubTab] || [];
      if (currentSubTab === "sites") {
        if (this._isCatalogCategory(categoryKey)) {
          return items;
        }
        const sites = this._defaultSites[categoryKey] || [];
        const history2 = category.history || [];
        return [...items].map((item) => {
          const lastVisitTime = Number(item?.lastVisitTime) > 0 ? Number(item.lastVisitTime) : this._siteLastVisitTime(item, history2, sites);
          return lastVisitTime > 0 ? { ...item, lastVisitTime } : item;
        });
      }
      if (currentSubTab === "favorites" || currentSubTab === "history") {
        const siteUrl = this._categorySiteFilters[categoryKey];
        if (siteUrl) {
          const sites = this._defaultSites[categoryKey] || [];
          const site = sites.find((s) => s.url === siteUrl);
          if (site) {
            items = items.filter((item) => this._itemMatchesSite(item?.url, site, sites));
          }
        }
      }
      return items;
    }
    /**
     * @param {number|string|null|undefined} timestamp
     * @returns {string|null}
     */
    _formatCardDate(timestamp) {
      const n = Number(timestamp);
      if (!Number.isFinite(n) || n <= 0) return null;
      try {
        return new Date(n).toLocaleDateString(void 0, {
          year: "numeric",
          month: "short",
          day: "numeric"
        });
      } catch {
        return null;
      }
    }
    /**
     * @param {number|string|null|undefined} dateAdded
     * @returns {string|null}
     */
    _formatBookmarkAddedDate(dateAdded) {
      return this._formatCardDate(dateAdded);
    }
    /**
     * Absolute bottom date strip used on Launch Deck / URL listing cards.
     * @param {Document} doc
     * @param {string} text
     * @param {string} [className]
     * @returns {HTMLElement}
     */
    _createCardDateOverlay(doc, text, className = "kp-launcher-card-visited-on") {
      const overlay = doc.createElement("div");
      overlay.className = className;
      overlay.textContent = text;
      overlay.style.cssText = `
      position: absolute;
      left: 0;
      right: 0;
      bottom: 0;
      padding: 6px 12px;
      background: rgba(0, 0, 0, 0.72);
      border-top: 1px solid rgba(255, 255, 255, 0.08);
      color: #c8c8c8;
      font-size: 11px;
      font-weight: 500;
      letter-spacing: 0.01em;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      pointer-events: none;
      z-index: 2;
    `;
      return overlay;
    }
    /**
     * Filter items by keywords in URL or title
     */
    _filterByKeywords(items, keywords) {
      return items.filter((item) => {
        const searchText = `${item.title} ${item.url}`.toLowerCase();
        return keywords.some((keyword) => searchText.includes(keyword));
      });
    }
    /**
     * Reset launcher controls inside its isolated shadow tree.
     *
     * @param {ShadowRoot|null} shadowRoot
     */
    _injectScopedStyles(shadowRoot) {
      if (!shadowRoot) return;
      injectChromeStyles(shadowRoot, {
        attr: "data-kp-launcher-scope",
        css: `
      .kp-launcher-shell button,
      .kp-launcher-shell [role="button"],
      .kp-launcher-shell input,
      .kp-launcher-shell select,
      .kp-launcher-shell textarea {
        border-radius: 0;
      }
      `
      });
    }
    /**
     * Build the launcher UI
     */
    _buildUI() {
      const doc = document;
      this._container = doc.createElement("div");
      this._container.className = "kp-launcher-container";
      this._container.setAttribute("role", "dialog");
      this._container.setAttribute("aria-label", "Launcher");
      this._container.tabIndex = -1;
      this._container.style.cssText = `
      position: fixed;
      inset: 60px;
      transform: translateZ(0);
      width: auto;
      height: auto;
      isolation: isolate;
      contain: layout style paint;
      will-change: transform;
      backface-visibility: hidden;
      -webkit-backface-visibility: hidden;
      -webkit-transform: translateZ(0);
    `;
      this._shadowRoot = ensureOpenChromeShadow(this._container, { id: "launcher", chromeWindow: true });
      const mount = this._shadowRoot || this._container;
      this._injectScopedStyles(this._shadowRoot);
      this._shell = doc.createElement("div");
      this._shell.className = "kp-launcher-shell";
      this._shell.style.cssText = `
      width: 100%;
      height: 100%;
      background: ${NCT_DARK_UI_PANEL_BACKGROUND};
      border: ${NCT_DARK_UI_PANEL_BORDER};
      border-radius: ${NCT_DARK_UI_PANEL_RADIUS};
      display: flex;
      overflow: hidden;
      box-shadow: ${NCT_DARK_UI_PANEL_BOX_SHADOW};
      box-sizing: border-box;
    `;
      mount.appendChild(this._shell);
      this._boundContainerKeyDown = (e) => {
        if (e.key === "Escape" || e.code === "Escape") {
          e.preventDefault();
          e.stopPropagation();
          try {
            e.stopImmediatePropagation();
          } catch {
          }
          if (this._launchDeckEditMode) {
            this._setLaunchDeckEditMode(false);
            return;
          }
          this.hide();
        }
      };
      this._container.addEventListener("keydown", this._boundContainerKeyDown, true);
      const sidebar = doc.createElement("div");
      sidebar.className = "kp-launcher-sidebar";
      sidebar.style.cssText = `
      width: 220px;
      background: #0f0f0f;
      border-right: 1px solid #333;
      display: flex;
      flex-direction: column;
      overflow-y: auto;
    `;
      const brand = doc.createElement("div");
      brand.className = "kp-launcher-brand";
      brand.style.cssText = `
      box-sizing: border-box;
      height: 48px;
      min-height: 48px;
      max-height: 48px;
      padding: 6px 16px;
      background: ${NCT_DARK_UI_TITLEBAR_GRADIENT};
      border-bottom: ${NCT_DARK_UI_TITLEBAR_BORDER_BOTTOM};
      display: flex;
      flex-direction: column;
      justify-content: center;
      flex-shrink: 0;
    `;
      const brandTitle = doc.createElement("h2");
      brandTitle.className = "kp-launcher-title";
      brandTitle.textContent = "Launcher";
      brandTitle.style.cssText = `
      margin: 0;
      font-size: 14px;
      font-weight: 600;
      line-height: 1.2;
      color: #fff;
    `;
      const brandSubtitle = doc.createElement("p");
      brandSubtitle.className = "kp-launcher-subtitle";
      brandSubtitle.textContent = "Quick access to your favorite sites";
      brandSubtitle.style.cssText = `
      margin: 2px 0 0 0;
      font-size: 11px;
      line-height: 1.2;
      color: #888;
    `;
      brand.appendChild(brandTitle);
      brand.appendChild(brandSubtitle);
      sidebar.appendChild(brand);
      this._tabListContainer = doc.createElement("div");
      this._tabListContainer.className = "kp-launcher-tabs";
      this._tabListContainer.style.cssText = `
      display: flex;
      flex-direction: column;
      padding: 12px 0;
      flex: 1;
      min-height: 0;
      overflow-y: auto;
    `;
      if (this._categories) {
        const categoryKeys = Object.keys(this._categories);
        categoryKeys.forEach((categoryKey, index) => {
          const category = this._categories[categoryKey];
          const tab = this._createTab(categoryKey, category);
          this._tabListContainer.appendChild(tab);
          if (index < categoryKeys.length - 1) {
            const divider = doc.createElement("div");
            divider.className = "kp-launcher-tab-divider";
            divider.setAttribute("aria-hidden", "true");
            divider.style.cssText = `
            height: 1px;
            margin: 2px 16px;
            background: #2a2a2a;
            flex-shrink: 0;
            pointer-events: none;
          `;
            this._tabListContainer.appendChild(divider);
          }
        });
      }
      sidebar.appendChild(this._tabListContainer);
      const checkboxContainer = doc.createElement("div");
      checkboxContainer.style.cssText = `
      padding: 12px 16px;
      border-top: 1px solid #333;
      margin-top: auto;
    `;
      const checkboxLabel = doc.createElement("label");
      checkboxLabel.style.cssText = `
      display: flex;
      align-items: center;
      gap: 8px;
      color: #888;
      font-size: 12px;
      cursor: pointer;
    `;
      const checkbox = doc.createElement("input");
      checkbox.type = "checkbox";
      checkbox.checked = this._showDefaultSites;
      checkbox.style.cssText = `
      cursor: pointer;
    `;
      checkbox.addEventListener("change", (e) => {
        this._showDefaultSites = e.target.checked;
        this._applyDefaultSitesVisibility();
        const cat = this._currentCategory;
        const active2 = this._categorySubTabs[cat];
        if (active2 === "sites" && (this._categories?.[cat]?.sites?.length || 0) === 0) {
          const cfg = this._categorySubTabConfig[cat] || ["favorites", "history", "search"];
          const fav = this._categories?.[cat]?.favorites?.length || 0;
          const hist = this._categories?.[cat]?.history?.length || 0;
          if (fav > 0 && cfg.includes("favorites")) this._categorySubTabs[cat] = "favorites";
          else if (hist > 0 && cfg.includes("history")) this._categorySubTabs[cat] = "history";
          else this._categorySubTabs[cat] = cfg.find((t) => t !== "sites" && t !== "search") || "history";
        }
        this._persistNavState();
        this._updateTabCounts();
        this._updateSubTabsUI();
        this._updateSubTabStyles();
        this._renderCategory(this._currentCategory);
      });
      const checkboxText = doc.createElement("span");
      checkboxText.textContent = "Show Launch Deck";
      checkboxLabel.appendChild(checkbox);
      checkboxLabel.appendChild(checkboxText);
      checkboxContainer.appendChild(checkboxLabel);
      sidebar.appendChild(checkboxContainer);
      const contentArea = doc.createElement("div");
      contentArea.className = "kp-launcher-content";
      contentArea.style.cssText = `
      flex: 1;
      display: flex;
      flex-direction: column;
      overflow: hidden;
    `;
      const header = doc.createElement("div");
      header.className = "kp-launcher-header";
      header.style.cssText = `
      box-sizing: border-box;
      height: 48px;
      min-height: 48px;
      max-height: 48px;
      padding: 6px 16px;
      border-bottom: ${NCT_DARK_UI_TITLEBAR_BORDER_BOTTOM};
      background: ${NCT_DARK_UI_TITLEBAR_GRADIENT};
      display: flex;
      align-items: center;
      gap: 16px;
      flex-shrink: 0;
    `;
      const titleBlock = doc.createElement("div");
      titleBlock.className = "kp-launcher-title-block";
      titleBlock.style.cssText = `
      flex: 0 1 auto;
      min-width: 0;
      display: flex;
      flex-direction: column;
      justify-content: center;
    `;
      this._headerTitle = doc.createElement("h2");
      this._headerTitle.className = "kp-launcher-category-title";
      this._headerTitle.style.cssText = `
      margin: 0;
      font-size: 14px;
      font-weight: 600;
      line-height: 1.2;
      color: #fff;
    `;
      this._headerDescription = doc.createElement("p");
      this._headerDescription.className = "kp-launcher-category-description";
      this._headerDescription.style.cssText = `
      margin: 2px 0 0 0;
      font-size: 11px;
      line-height: 1.2;
      color: #888;
    `;
      titleBlock.appendChild(this._headerTitle);
      titleBlock.appendChild(this._headerDescription);
      this._headerSearchSlot = doc.createElement("div");
      this._headerSearchSlot.className = "kp-launcher-header-search";
      this._headerSearchSlot.style.cssText = `
      flex: 1 1 280px;
      display: none;
      justify-content: flex-end;
      min-width: 220px;
      max-width: 580px;
      margin-left: auto;
    `;
      header.appendChild(titleBlock);
      header.appendChild(this._headerSearchSlot);
      this._subTabContainer = doc.createElement("div");
      this._subTabContainer.className = "kp-launcher-subtabs";
      this._subTabContainer.style.cssText = `
      display: flex;
      flex-direction: column;
      gap: 0;
      border-bottom: 1px solid #333;
      background: #0f0f0f;
    `;
      this._primarySubTabRow = doc.createElement("div");
      this._primarySubTabRow.className = "kp-launcher-subtabs-primary";
      this._primarySubTabRow.style.cssText = `
      display: flex;
      gap: 8px;
      align-items: stretch;
      padding: 16px 24px 0;
      flex-wrap: wrap;
    `;
      this._siteFilterRow = doc.createElement("div");
      this._siteFilterRow.className = "kp-launcher-subtabs-sites";
      this._siteFilterRow.style.cssText = `
      display: none;
      flex-wrap: wrap;
      gap: 6px 8px;
      align-items: center;
      padding: 10px 24px 12px;
      border-top: 1px solid #2a2a2a;
    `;
      this._subTabContainer.appendChild(this._primarySubTabRow);
      this._subTabContainer.appendChild(this._siteFilterRow);
      this._updateContentHeader(this._currentCategory);
      this._updateSubTabsUI();
      this._updateHeaderPageSearch(this._currentCategory);
      this._gridContainer = doc.createElement("div");
      this._gridContainer.className = "kp-launcher-grid-container";
      this._gridContainer.style.cssText = `
      flex: 1;
      padding: 24px;
      overflow-y: auto;
      overflow-x: hidden;
      min-height: 0;
    `;
      const footer = doc.createElement("div");
      footer.className = "kp-launcher-footer";
      footer.style.cssText = `
      padding: 16px 24px;
      border-top: 1px solid #333;
      background: #0f0f0f;
      display: flex;
      justify-content: space-between;
      align-items: center;
      gap: 16px;
      flex-wrap: wrap;
    `;
      const footerLeft = doc.createElement("div");
      footerLeft.className = "kp-launcher-footer-left";
      footerLeft.style.cssText = `
      display: flex;
      align-items: center;
      gap: 12px;
      flex-wrap: wrap;
      min-width: 0;
      flex: 1 1 auto;
    `;
      const hint = doc.createElement("div");
      hint.className = "kp-launcher-footer-hint";
      hint.style.cssText = "color: #888; font-size: 13px;";
      hint.innerHTML = "Press <strong>\u2191\u2193</strong> for tabs \u2022 <strong>/</strong> to search \u2022 <strong>F</strong> to open \u2022 <strong>Esc</strong> to close";
      this._launchDeckEditBar = doc.createElement("div");
      this._launchDeckEditBar.className = "kp-launcher-edit-bar";
      this._launchDeckEditBar.style.cssText = `
      display: none;
      flex-wrap: wrap;
      gap: 8px;
      align-items: center;
    `;
      footerLeft.appendChild(hint);
      footerLeft.appendChild(this._launchDeckEditBar);
      const navControls = doc.createElement("div");
      navControls.className = "kp-launcher-nav-controls";
      navControls.style.cssText = `
      display: flex;
      gap: 12px;
      align-items: center;
      flex-shrink: 0;
    `;
      const upBtn = this._createNavButton("\u2191", "C", () => this._scrollUp());
      navControls.appendChild(upBtn);
      const downBtn = this._createNavButton("\u2193", "V", () => this._scrollDown());
      navControls.appendChild(downBtn);
      footer.appendChild(footerLeft);
      footer.appendChild(navControls);
      this._updateLaunchDeckEditBar();
      contentArea.appendChild(header);
      contentArea.appendChild(this._subTabContainer);
      contentArea.appendChild(this._gridContainer);
      contentArea.appendChild(footer);
      const previewArea = doc.createElement("div");
      previewArea.className = "kp-launcher-preview-area";
      previewArea.style.cssText = `
      width: 0;
      overflow: hidden;
      flex-shrink: 0;
    `;
      this._previewArea = previewArea;
      this._shell.appendChild(sidebar);
      this._shell.appendChild(contentArea);
      this._shell.appendChild(previewArea);
      doc.body.appendChild(this._container);
    }
    /**
     * Count items across a category's configured sub-tabs.
     * Skips the virtual Search tab (derived from other lists).
     * @param {string} categoryKey
     * @returns {number}
     */
    _categoryItemCount(categoryKey) {
      const category = this._categories?.[categoryKey];
      if (!category) return 0;
      const subTabConfig = this._categorySubTabConfig[categoryKey] || ["favorites", "history", "search"];
      let total = 0;
      for (const key of subTabConfig) {
        if (key === "search") continue;
        total += Array.isArray(category[key]) ? category[key].length : 0;
      }
      return total;
    }
    /**
     * Count for a single sub-tab list.
     * @param {string} categoryKey
     * @param {string} subTabType
     * @returns {number}
     */
    _subTabItemCount(categoryKey, subTabType) {
      if (subTabType === "search") {
        return this._getCategorySearchResults(categoryKey).length;
      }
      const list = this._categories?.[categoryKey]?.[subTabType];
      return Array.isArray(list) ? list.length : 0;
    }
    /**
     * Deduped Sites/Favorites/History matches for the current search query.
     * @param {string} categoryKey
     * @param {string} [query]
     * @returns {Array<{title?: string, url?: string, isDefault?: boolean}>}
     */
    _getCategorySearchResults(categoryKey, query = this._searchQuery) {
      const category = this._categories?.[categoryKey];
      const q = String(query || "").toLowerCase().trim();
      if (!category || !q) return [];
      const sources = ["sites", "favorites", "history"];
      const seen = /* @__PURE__ */ new Set();
      const results = [];
      for (const key of sources) {
        const list = category[key];
        if (!Array.isArray(list)) continue;
        for (const item of list) {
          const url = item?.url || "";
          if (!url || seen.has(url)) continue;
          const title = (item.title || "").toLowerCase();
          if (title.includes(q) || url.toLowerCase().includes(q)) {
            seen.add(url);
            results.push(item);
          }
        }
      }
      return results;
    }
    /**
     * Switch to the Search sub-tab and optionally focus the search field.
     * @param {{ focus?: boolean }} [opts]
     */
    _activateSearchTab(opts = {}) {
      const focus = opts.focus !== false;
      this._categorySubTabs[this._currentCategory] = "search";
      this._currentSheet = 0;
      this._updateSiteFilterTabsUI();
      this._updateSubTabStyles();
      this._renderCategory(this._currentCategory);
      this._updateTabCounts();
      this._persistNavState();
      if (focus && this._searchInput) {
        try {
          this._searchInput.focus();
        } catch {
        }
      }
    }
    /**
     * Create a right-aligned count badge for tab labels.
     * @param {Document} doc
     * @param {number} count
     * @param {{ muted?: boolean }} [opts]
     */
    _createCountBadge(doc, count, opts = {}) {
      const badge = doc.createElement("span");
      badge.className = "kp-launcher-tab-count";
      badge.textContent = String(count);
      badge.style.cssText = `
      margin-left: auto;
      flex-shrink: 0;
      font-size: 12px;
      font-weight: 600;
      font-variant-numeric: tabular-nums;
      color: ${opts.muted ? "#666" : "#888"};
      min-width: 1.25em;
      text-align: right;
    `;
      return badge;
    }
    /**
     * Create a tab button
     */
    _createTab(categoryKey, category) {
      const doc = document;
      const tab = doc.createElement("button");
      tab.className = "kp-launcher-tab";
      tab.dataset.category = categoryKey;
      const isActive = categoryKey === this._currentCategory;
      tab.style.cssText = `
      display: flex;
      align-items: center;
      gap: 12px;
      padding: 12px 20px;
      margin: 4px 8px;
      background: ${isActive ? "#2a2a2a" : "transparent"};
      border: 1px solid ${isActive ? "#444" : "transparent"};
      border-radius: 8px;
      color: ${isActive ? "#fff" : "#888"};
      font-size: 14px;
      font-weight: 500;
      cursor: pointer;
      transition: all 0.2s;
      text-align: left;
      width: calc(100% - 16px);
      box-sizing: border-box;
    `;
      const icon = this._createTabIcon(doc, categoryKey);
      const label = doc.createElement("span");
      label.className = "kp-launcher-tab-label";
      label.textContent = category.label;
      label.style.cssText = `
      flex: 1;
      min-width: 0;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    `;
      const count = this._createCountBadge(doc, this._categoryItemCount(categoryKey));
      count.dataset.role = "count";
      tab.appendChild(icon);
      tab.appendChild(label);
      tab.appendChild(count);
      tab.addEventListener("click", () => {
        this._selectCategory(categoryKey);
      });
      tab.addEventListener("mouseenter", () => {
        if (categoryKey !== this._currentCategory) {
          tab.style.background = "#1f1f1f";
          tab.style.borderColor = "#333";
        }
      });
      tab.addEventListener("mouseleave", () => {
        if (categoryKey !== this._currentCategory) {
          tab.style.background = "transparent";
          tab.style.borderColor = "transparent";
        }
      });
      return tab;
    }
    /**
     * Outline SVG for a left-rail category tab.
     * @param {Document} doc
     * @param {string} categoryKey
     * @returns {SVGElement}
     */
    _createTabIcon(doc, categoryKey) {
      const paths = LAUNCHER_TAB_ICONS[categoryKey] || LAUNCHER_TAB_ICONS.searches;
      const svg = createOutlineIcon(doc, paths);
      svg.setAttribute("width", "18");
      svg.setAttribute("height", "18");
      svg.classList.add("kp-launcher-tab-icon");
      svg.style.cssText = "flex-shrink: 0; display: block; color: inherit;";
      return svg;
    }
    /**
     * Switch the primary category tab and remember it for the next open.
     * @param {string} categoryKey
     */
    _selectCategory(categoryKey) {
      if (!categoryKey || !this._categories?.[categoryKey]) return;
      this._currentCategory = categoryKey;
      this._currentSheet = 0;
      this._ensureValidSubTab(categoryKey);
      this._updateContentHeader(categoryKey);
      this._updateSubTabsUI();
      this._updateHeaderPageSearch(categoryKey);
      this._renderCategory(categoryKey);
      this._updateTabStyles();
      this._updateSubTabStyles();
      this._persistNavState();
      void this._ensureCategoryHistory(categoryKey);
    }
    /**
     * Create a sub-tab button for sites/favorites/history
     */
    _createSubTab(type2, label) {
      const doc = document;
      const subTab = doc.createElement("button");
      subTab.className = "kp-launcher-subtab";
      subTab.dataset.type = type2;
      const currentSubTab = this._categorySubTabs[this._currentCategory] || "history";
      const isActive = type2 === currentSubTab;
      const count = this._subTabItemCount(this._currentCategory, type2);
      subTab.style.cssText = `
      display: inline-flex;
      align-items: center;
      gap: 10px;
      padding: 10px 20px;
      background: ${isActive ? "#2a2a2a" : "transparent"};
      border: none;
      border-bottom: 2px solid ${isActive ? "#4CAF50" : "transparent"};
      color: ${isActive ? "#fff" : "#888"};
      font-size: 14px;
      font-weight: 500;
      cursor: pointer;
      transition: all 0.2s;
      min-width: 120px;
    `;
      const labelEl = doc.createElement("span");
      labelEl.className = "kp-launcher-subtab-label";
      labelEl.textContent = label;
      labelEl.style.cssText = "flex: 1; text-align: left;";
      const countEl = this._createCountBadge(doc, count, { muted: !isActive });
      countEl.dataset.role = "count";
      subTab.appendChild(labelEl);
      subTab.appendChild(countEl);
      subTab.addEventListener("click", () => {
        this._categorySubTabs[this._currentCategory] = type2;
        this._currentSheet = 0;
        if (type2 !== "sites" && this._launchDeckEditMode) {
          this._launchDeckEditMode = false;
          this._closeAddSitePicker();
        }
        if (type2 === "favorites" || type2 === "history") {
          this._ensureValidSiteFilter(this._currentCategory);
        }
        this._updateSiteFilterTabsUI();
        this._updateLaunchDeckEditBar();
        this._renderCategory(this._currentCategory);
        this._updateSubTabStyles();
        this._persistNavState();
      });
      subTab.addEventListener("mouseenter", () => {
        const active2 = this._categorySubTabs[this._currentCategory] || "history";
        if (type2 !== active2) {
          subTab.style.color = "#fff";
        }
      });
      subTab.addEventListener("mouseleave", () => {
        const active2 = this._categorySubTabs[this._currentCategory] || "history";
        if (type2 !== active2) {
          subTab.style.color = "#888";
        }
      });
      return subTab;
    }
    /**
     * Update sub-tab styles to reflect active sub-tab
     */
    _updateSubTabStyles() {
      if (!this._subTabContainer) return;
      const subTabs = this._subTabContainer.querySelectorAll(".kp-launcher-subtab");
      const currentSubTab = this._categorySubTabs[this._currentCategory] || "favorites";
      subTabs.forEach((subTab) => {
        const isActive = subTab.dataset.type === currentSubTab;
        subTab.style.background = isActive ? "#2a2a2a" : "transparent";
        subTab.style.borderBottomColor = isActive ? "#4CAF50" : "transparent";
        if (subTab.dataset.type !== "search") {
          subTab.style.color = isActive ? "#fff" : "#888";
        } else {
          subTab.setAttribute("aria-selected", isActive ? "true" : "false");
          const input = subTab.querySelector(".kp-launcher-search-input");
          if (input) {
            input.style.borderColor = isActive ? "#666" : "#444";
            input.style.background = isActive ? "#333" : "#2a2a2a";
          }
        }
        const countEl = subTab.querySelector('[data-role="count"]');
        if (countEl) {
          countEl.style.color = isActive ? "#888" : "#666";
        }
      });
    }
    /**
     * Refresh count badges on sidebar tabs and current sub-tabs.
     */
    _updateTabCounts() {
      if (this._tabListContainer) {
        const tabs = this._tabListContainer.querySelectorAll(".kp-launcher-tab");
        tabs.forEach((tab) => {
          const key = tab.dataset.category;
          const countEl = tab.querySelector('[data-role="count"]');
          if (countEl && key) {
            countEl.textContent = String(this._categoryItemCount(key));
          }
        });
      }
      if (this._subTabContainer) {
        const subTabs = this._subTabContainer.querySelectorAll(".kp-launcher-subtab");
        subTabs.forEach((subTab) => {
          const type2 = subTab.dataset.type;
          const countEl = subTab.querySelector('[data-role="count"]');
          if (countEl && type2) {
            countEl.textContent = String(this._subTabItemCount(this._currentCategory, type2));
          }
        });
      }
    }
    /**
     * Update sub-tabs UI based on current category configuration
     */
    _updateSubTabsUI() {
      if (!this._subTabContainer) return;
      const primaryRow = this._primarySubTabRow || this._subTabContainer;
      const hadSearchFocus = !!this._searchInput && document.activeElement === this._searchInput;
      const searchCaret = hadSearchFocus ? this._searchInput.selectionStart ?? this._searchInput.value.length : null;
      primaryRow.innerHTML = "";
      this._searchInput = null;
      this._clearBtn = null;
      const subTabConfig = this._categorySubTabConfig[this._currentCategory] || ["favorites", "history", "search"];
      const subTabLabels = {
        sites: "Launch Deck",
        favorites: this._currentCategory === "launchDeck" ? "Toolbar Bookmarks" : "Favorites",
        history: this._currentCategory === "launchDeck" ? "Top Visited" : "History",
        search: "Search"
      };
      subTabConfig.forEach((subTabType) => {
        if (subTabType === "search") {
          primaryRow.appendChild(this._createSearchSubTab());
          return;
        }
        const label = subTabLabels[subTabType] || subTabType;
        const subTab = this._createSubTab(subTabType, label);
        primaryRow.appendChild(subTab);
      });
      this._ensureValidSiteFilter(this._currentCategory);
      this._updateSiteFilterTabsUI();
      this._updateLaunchDeckEditBar();
      if (hadSearchFocus && this._searchInput) {
        try {
          this._searchInput.focus();
          const pos = searchCaret ?? this._searchInput.value.length;
          this._searchInput.setSelectionRange(pos, pos);
        } catch {
        }
      }
    }
    /**
     * Edit / Done / Add controls in the footer (next to the keyboard hint).
     */
    _updateLaunchDeckEditBar() {
      const bar = this._launchDeckEditBar;
      if (!bar) return;
      const onSites = (this._categorySubTabs[this._currentCategory] || "history") === "sites";
      const canEdit = this._isCatalogCategory() && this._showDefaultSites && onSites;
      if (!canEdit) {
        if (this._launchDeckEditMode) this._launchDeckEditMode = false;
        this._closeAddSitePicker();
        bar.style.display = "none";
        bar.innerHTML = "";
        return;
      }
      const doc = document;
      bar.style.display = "inline-flex";
      bar.innerHTML = "";
      const editing = !!this._launchDeckEditMode;
      const toggleBtn = doc.createElement("button");
      toggleBtn.type = "button";
      toggleBtn.textContent = editing ? "Done" : "Edit Launch Deck";
      toggleBtn.style.cssText = `
      padding: 5px 10px;
      border-radius: 6px;
      border: 1px solid ${editing ? "#5a9e6f" : "#444"};
      background: ${editing ? "#1e3a28" : "#1a1a1a"};
      color: ${editing ? "#9fd4ae" : "#ccc"};
      font-size: 12px;
      font-weight: 600;
      cursor: pointer;
      white-space: nowrap;
    `;
      toggleBtn.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        this._setLaunchDeckEditMode(!this._launchDeckEditMode);
      });
      bar.appendChild(toggleBtn);
      if (!editing) return;
      const editHint = doc.createElement("span");
      editHint.textContent = "Reorder, remove, or add \u2022 Esc exits edit";
      editHint.style.cssText = "color: #777; font-size: 12px; white-space: nowrap;";
      bar.appendChild(editHint);
      const addBtn = doc.createElement("button");
      addBtn.type = "button";
      addBtn.textContent = "Add site\u2026";
      addBtn.style.cssText = `
      padding: 5px 10px;
      border-radius: 6px;
      border: 1px solid #444;
      background: #1a1a1a;
      color: #ccc;
      font-size: 12px;
      font-weight: 600;
      cursor: pointer;
      white-space: nowrap;
    `;
      addBtn.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        this._openAddSitePicker();
      });
      bar.appendChild(addBtn);
    }
    /**
     * Filterable catalog picker + custom URL field.
     */
    _openAddSitePicker() {
      this._closeAddSitePicker();
      if (!this._isCatalogCategory() || !this._launchDeckEditMode) return;
      const doc = document;
      const categoryKey = this._currentCategory;
      const currentDeck = this._categories?.[categoryKey]?.sites || [];
      const addable = getAddableCatalogSites(categoryKey, currentDeck);
      const overlay = doc.createElement("div");
      overlay.className = "kp-launcher-add-picker";
      overlay.style.cssText = `
      position: absolute;
      inset: 0;
      z-index: 40;
      background: rgba(0, 0, 0, 0.55);
      display: flex;
      align-items: flex-start;
      justify-content: center;
      padding: 48px 24px;
    `;
      const panel = doc.createElement("div");
      panel.style.cssText = `
      width: min(480px, 100%);
      max-height: min(70vh, 560px);
      overflow: hidden;
      display: flex;
      flex-direction: column;
      background: #1a1a1a;
      border: 1px solid #444;
      border-radius: 10px;
      box-shadow: 0 16px 40px rgba(0, 0, 0, 0.45);
    `;
      const header = doc.createElement("div");
      header.style.cssText = `
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 12px 14px;
      border-bottom: 1px solid #333;
    `;
      const title = doc.createElement("div");
      title.textContent = "Add to Launch Deck";
      title.style.cssText = "flex: 1; color: #fff; font-weight: 600; font-size: 14px;";
      const closeBtn = doc.createElement("button");
      closeBtn.type = "button";
      closeBtn.textContent = "\u2715";
      closeBtn.style.cssText = `
      border: none; background: transparent; color: #888; cursor: pointer; font-size: 16px;
    `;
      closeBtn.addEventListener("click", () => this._closeAddSitePicker());
      header.appendChild(title);
      header.appendChild(closeBtn);
      const filter = doc.createElement("input");
      filter.type = "text";
      filter.placeholder = "Filter catalog\u2026";
      filter.style.cssText = `
      margin: 10px 14px 0;
      padding: 8px 10px;
      border-radius: 6px;
      border: 1px solid #444;
      background: #111;
      color: #eee;
      font-size: 13px;
    `;
      const list = doc.createElement("div");
      list.style.cssText = `
      flex: 1;
      overflow-y: auto;
      padding: 8px 10px 12px;
      display: flex;
      flex-direction: column;
      gap: 4px;
      min-height: 120px;
    `;
      const renderList = (q = "") => {
        list.innerHTML = "";
        const needle = String(q || "").toLowerCase().trim();
        const rows = addable.filter((s) => {
          if (!needle) return true;
          return s.title.toLowerCase().includes(needle) || s.url.toLowerCase().includes(needle);
        });
        if (!rows.length) {
          const empty = doc.createElement("div");
          empty.textContent = needle ? "No matching sites" : "All catalog sites are already on your deck";
          empty.style.cssText = "color: #666; font-size: 13px; padding: 16px 8px;";
          list.appendChild(empty);
          return;
        }
        for (const site of rows) {
          const row = doc.createElement("button");
          row.type = "button";
          row.style.cssText = `
          display: flex;
          flex-direction: column;
          align-items: flex-start;
          gap: 2px;
          padding: 8px 10px;
          border: 1px solid #333;
          border-radius: 6px;
          background: #222;
          color: #eee;
          cursor: pointer;
          text-align: left;
        `;
          const t = doc.createElement("div");
          t.textContent = site.title;
          t.style.cssText = "font-size: 13px; font-weight: 600;";
          const u = doc.createElement("div");
          u.textContent = site.url;
          u.style.cssText = "font-size: 11px; color: #888;";
          row.appendChild(t);
          row.appendChild(u);
          row.addEventListener("click", () => {
            void this._addLaunchDeckSite({
              title: site.title,
              url: site.url,
              fromCatalog: true
            });
          });
          list.appendChild(row);
        }
      };
      filter.addEventListener("input", () => renderList(filter.value));
      const customRow = doc.createElement("div");
      customRow.style.cssText = `
      display: flex;
      gap: 8px;
      padding: 10px 14px 14px;
      border-top: 1px solid #333;
    `;
      const customInput = doc.createElement("input");
      customInput.type = "url";
      customInput.placeholder = "https://example.com";
      customInput.style.cssText = `
      flex: 1;
      padding: 8px 10px;
      border-radius: 6px;
      border: 1px solid #444;
      background: #111;
      color: #eee;
      font-size: 13px;
    `;
      const customBtn = doc.createElement("button");
      customBtn.type = "button";
      customBtn.textContent = "Add URL";
      customBtn.style.cssText = `
      padding: 8px 12px;
      border-radius: 6px;
      border: 1px solid #5a9e6f;
      background: #1e3a28;
      color: #9fd4ae;
      font-size: 12px;
      font-weight: 600;
      cursor: pointer;
    `;
      const submitCustom = () => {
        let raw = String(customInput.value || "").trim();
        if (!raw) return;
        if (!/^https?:\/\//i.test(raw)) raw = `https://${raw}`;
        try {
          const u = new URL(raw);
          void this._addLaunchDeckSite({
            title: u.hostname.replace(/^www\./, ""),
            url: u.href,
            fromCatalog: false
          });
        } catch {
          customInput.style.borderColor = "#c44";
        }
      };
      customBtn.addEventListener("click", submitCustom);
      customInput.addEventListener("keydown", (e) => {
        if (e.key === "Enter") {
          e.preventDefault();
          submitCustom();
        }
      });
      customRow.appendChild(customInput);
      customRow.appendChild(customBtn);
      panel.appendChild(header);
      panel.appendChild(filter);
      panel.appendChild(list);
      panel.appendChild(customRow);
      overlay.appendChild(panel);
      overlay.addEventListener("click", (e) => {
        if (e.target === overlay) this._closeAddSitePicker();
      });
      const host = this._shell || this._container || doc.body;
      host.appendChild(overlay);
      this._addSitePicker = overlay;
      renderList();
      try {
        filter.focus();
      } catch {
      }
    }
    /**
     * Second-row site filters under Favorites / History.
     * Favorites: only Sites with ≥1 bookmark. History: only Sites with ≥1 visit.
     * Never renders a 0-count site pill.
     */
    _updateSiteFilterTabsUI() {
      if (!this._siteFilterRow) return;
      const categoryKey = this._currentCategory;
      const primary = this._categorySubTabs[categoryKey] || "history";
      const showFilters = primary === "favorites" || primary === "history";
      const allSites = this._defaultSites[categoryKey] || [];
      const sitesWithItems = showFilters ? this._getSitesWithPrimaryItems(categoryKey, primary) : [];
      this._siteFilterRow.innerHTML = "";
      if (!showFilters || !sitesWithItems.length) {
        this._siteFilterRow.style.display = "none";
        return;
      }
      this._ensureValidSiteFilter(categoryKey);
      const activeSiteUrl = this._categorySiteFilters[categoryKey];
      this._siteFilterRow.style.display = "flex";
      const primaryItems = this._categories?.[categoryKey]?.[primary] || [];
      for (const site of sitesWithItems) {
        const count = primaryItems.filter(
          (item) => this._itemMatchesSite(item?.url, site, allSites)
        ).length;
        if (count <= 0) continue;
        const isActive = site.url === activeSiteUrl;
        const btn = document.createElement("button");
        btn.type = "button";
        btn.className = "kp-launcher-site-filter-tab";
        btn.dataset.siteUrl = site.url;
        btn.style.cssText = `
        display: inline-flex;
        align-items: center;
        gap: 8px;
        padding: 6px 12px;
        background: ${isActive ? "#2a2a2a" : "transparent"};
        border: 1px solid ${isActive ? "#555" : "#333"};
        border-radius: 999px;
        color: ${isActive ? "#fff" : "#888"};
        font-size: 12px;
        font-weight: 500;
        cursor: pointer;
        transition: all 0.15s;
        max-width: 100%;
      `;
        const labelEl = document.createElement("span");
        labelEl.textContent = site.title;
        labelEl.style.cssText = `
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      `;
        const countEl = this._createCountBadge(document, count, { muted: !isActive });
        countEl.dataset.role = "site-count";
        btn.appendChild(labelEl);
        btn.appendChild(countEl);
        btn.addEventListener("click", () => {
          this._categorySiteFilters[categoryKey] = site.url;
          this._currentSheet = 0;
          this._updateSiteFilterTabsUI();
          this._renderCategory(categoryKey);
        });
        btn.addEventListener("mouseenter", () => {
          if (site.url !== this._categorySiteFilters[categoryKey]) {
            btn.style.color = "#fff";
            btn.style.borderColor = "#444";
          }
        });
        btn.addEventListener("mouseleave", () => {
          if (site.url !== this._categorySiteFilters[categoryKey]) {
            btn.style.color = "#888";
            btn.style.borderColor = "#333";
          }
        });
        this._siteFilterRow.appendChild(btn);
      }
    }
    /**
     * Search sites tab — lives in the sub-tab bar after History.
     * Typing here activates the Search tab and shows matches in the grid.
     * @returns {HTMLElement}
     */
    _createSearchSubTab() {
      const doc = document;
      const currentSubTab = this._categorySubTabs[this._currentCategory] || "history";
      const isActive = currentSubTab === "search";
      const count = this._subTabItemCount(this._currentCategory, "search");
      const subTab = doc.createElement("div");
      subTab.className = "kp-launcher-subtab kp-launcher-search-subtab";
      subTab.dataset.type = "search";
      subTab.setAttribute("role", "tab");
      subTab.setAttribute("aria-selected", isActive ? "true" : "false");
      subTab.style.cssText = `
      display: inline-flex;
      align-items: center;
      gap: 8px;
      padding: 6px 12px 8px;
      margin-left: auto;
      background: ${isActive ? "#2a2a2a" : "transparent"};
      border: none;
      border-bottom: 2px solid ${isActive ? "#4CAF50" : "transparent"};
      color: ${isActive ? "#fff" : "#888"};
      font-size: 14px;
      font-weight: 500;
      min-width: 220px;
      max-width: 320px;
      flex: 1 1 240px;
      box-sizing: border-box;
      cursor: text;
      transition: all 0.2s;
    `;
      const fieldWrap = doc.createElement("div");
      fieldWrap.className = "kp-launcher-search-container";
      fieldWrap.style.cssText = `
      position: relative;
      flex: 1;
      min-width: 0;
    `;
      const input = doc.createElement("input");
      input.type = "search";
      input.placeholder = "Filter results...";
      input.className = "kp-launcher-search-input";
      input.setAttribute("aria-label", "Filter results");
      input.autocomplete = "off";
      input.spellcheck = false;
      input.value = this._searchQuery || "";
      input.style.cssText = `
      width: 100%;
      padding: 6px 28px 6px 10px;
      background: ${isActive ? "#333" : "#2a2a2a"};
      border: 1px solid ${isActive ? "#666" : "#444"};
      border-radius: 6px;
      color: #fff;
      font-size: 13px;
      outline: none;
      box-sizing: border-box;
      transition: all 0.2s;
    `;
      const clearBtn = doc.createElement("button");
      clearBtn.type = "button";
      clearBtn.textContent = "\xD7";
      clearBtn.className = "kp-launcher-search-clear";
      clearBtn.setAttribute("aria-label", "Clear search");
      clearBtn.style.cssText = `
      position: absolute;
      right: 4px;
      top: 50%;
      transform: translateY(-50%);
      margin: 0;
      appearance: none;
      -webkit-appearance: none;
      background: transparent;
      border: none;
      box-shadow: none;
      color: #888;
      font-size: 18px;
      line-height: 1;
      cursor: pointer;
      padding: 2px 6px;
      display: ${this._searchQuery.trim() ? "block" : "none"};
      transition: color 0.2s;
    `;
      const countEl = this._createCountBadge(doc, count, { muted: !isActive });
      countEl.dataset.role = "count";
      const applyQuery = (raw) => {
        this._searchQuery = String(raw || "");
        this._currentSheet = 0;
        const hasQuery = !!this._searchQuery.trim();
        clearBtn.style.display = hasQuery ? "block" : "none";
        if (this._categorySubTabs[this._currentCategory] !== "search") {
          this._categorySubTabs[this._currentCategory] = "search";
          this._persistNavState();
        }
        this._updateSubTabStyles();
        this._renderCategory(this._currentCategory);
        this._updateTabCounts();
      };
      input.addEventListener("input", (e) => {
        applyQuery(e.target.value);
      });
      input.addEventListener("focus", () => {
        input.style.borderColor = "#666";
        input.style.background = "#333";
        if (this._categorySubTabs[this._currentCategory] !== "search") {
          this._categorySubTabs[this._currentCategory] = "search";
          this._currentSheet = 0;
          this._updateSubTabStyles();
          this._renderCategory(this._currentCategory);
          this._updateTabCounts();
          this._persistNavState();
        }
      });
      input.addEventListener("blur", () => {
        const active2 = this._categorySubTabs[this._currentCategory] === "search";
        input.style.borderColor = active2 ? "#666" : "#444";
        input.style.background = active2 ? "#333" : "#2a2a2a";
      });
      input.addEventListener("keydown", (e) => {
        if (e.key === "Escape" || e.code === "Escape") {
          e.preventDefault();
          e.stopPropagation();
          try {
            e.stopImmediatePropagation();
          } catch {
          }
          this.hide();
        }
      });
      clearBtn.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        input.value = "";
        applyQuery("");
        try {
          input.focus();
        } catch {
        }
      });
      clearBtn.addEventListener("mouseenter", () => {
        clearBtn.style.color = "#fff";
      });
      clearBtn.addEventListener("mouseleave", () => {
        clearBtn.style.color = "#888";
      });
      subTab.addEventListener("click", (e) => {
        if (e.target === clearBtn || clearBtn.contains(e.target)) return;
        this._activateSearchTab({ focus: true });
      });
      fieldWrap.appendChild(input);
      fieldWrap.appendChild(clearBtn);
      subTab.appendChild(fieldWrap);
      subTab.appendChild(countEl);
      this._searchInput = input;
      this._clearBtn = clearBtn;
      return subTab;
    }
    /**
     * Create navigation button (up/down)
     */
    _createNavButton(arrow, keyLabel, onClick) {
      const doc = document;
      const btn = doc.createElement("button");
      btn.style.cssText = `
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 8px 16px;
      background: #2a2a2a;
      border: 1px solid #444;
      border-radius: 6px;
      color: #fff;
      font-size: 14px;
      cursor: pointer;
      transition: all 0.2s;
    `;
      const arrowSpan = doc.createElement("span");
      arrowSpan.textContent = arrow;
      arrowSpan.style.fontSize = "16px";
      const keySpan = doc.createElement("kbd");
      keySpan.textContent = keyLabel;
      keySpan.style.cssText = `
      padding: 2px 6px;
      background: #1a1a1a;
      border: 1px solid #555;
      border-radius: 3px;
      font-size: 12px;
      font-family: monospace;
    `;
      btn.appendChild(arrowSpan);
      btn.appendChild(keySpan);
      btn.addEventListener("click", onClick);
      btn.addEventListener("mouseenter", () => {
        btn.style.background = "#333";
        btn.style.borderColor = "#555";
      });
      btn.addEventListener("mouseleave", () => {
        btn.style.background = "#2a2a2a";
        btn.style.borderColor = "#444";
      });
      return btn;
    }
    /**
     * Update tab styles to reflect active category
     */
    _updateTabStyles() {
      const tabs = this._tabListContainer.querySelectorAll(".kp-launcher-tab");
      tabs.forEach((tab) => {
        const isActive = tab.dataset.category === this._currentCategory;
        tab.style.background = isActive ? "#2a2a2a" : "transparent";
        tab.style.borderColor = isActive ? "#444" : "transparent";
        tab.style.color = isActive ? "#fff" : "#888";
      });
    }
    /**
     * Render a category's page template + card grid.
     * Header page search is managed separately via `_updateHeaderPageSearch`.
     */
    _renderCategory(categoryKey) {
      if (!this._categories || !this._categories[categoryKey]) return;
      this._gridContainer.innerHTML = "";
      const currentSubTab = this._categorySubTabs[categoryKey] || "history";
      const items = this._getActiveSubTabItems(categoryKey);
      const page = document.createElement("div");
      page.className = "kp-launcher-page";
      page.style.cssText = `
      display: flex;
      flex-direction: column;
      gap: 16px;
      min-height: 100%;
    `;
      const template = this._categoryPageTemplates[categoryKey];
      if (template?.renderBeforeCards) {
        try {
          const before = template.renderBeforeCards(document);
          if (before) page.appendChild(before);
        } catch (err) {
          console.warn("[LauncherPopover] Category page template failed:", categoryKey, err);
        }
      }
      const startIdx = this._currentSheet * this._itemsPerSheet;
      const endIdx = startIdx + this._itemsPerSheet;
      const sheetItems = items.slice(startIdx, endIdx);
      const grid = document.createElement("div");
      grid.className = "kp-launcher-grid";
      grid.style.cssText = `
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
      gap: 16px;
      align-content: start;
      flex: 1;
    `;
      const cardVariant = currentSubTab === "sites" ? "launch" : "listing";
      sheetItems.forEach((item) => {
        const card = this._createGridCard(item, {
          variant: cardVariant,
          showAddedOn: currentSubTab === "favorites"
        });
        grid.appendChild(card);
      });
      if (sheetItems.length === 0) {
        const empty = document.createElement("div");
        empty.style.cssText = `
        grid-column: 1 / -1;
        text-align: center;
        padding: 40px;
        color: #666;
        font-size: 14px;
      `;
        if (currentSubTab === "search") {
          empty.textContent = this._searchQuery.trim() ? "No sites match your search" : "Type to search sites in this category";
        } else if (categoryKey === "launchDeck" && currentSubTab === "favorites") {
          empty.textContent = "No toolbar bookmarks";
        } else if (categoryKey === "launchDeck" && currentSubTab === "history") {
          empty.textContent = "No top visited sites";
        } else {
          empty.textContent = "No items in this category";
        }
        grid.appendChild(empty);
      }
      page.appendChild(grid);
      this._gridContainer.appendChild(page);
      try {
        this._updateTabCounts?.();
      } catch {
      }
    }
    /**
     * Update the content-area header to the active category name + description.
     * @param {string} [categoryKey]
     */
    _updateContentHeader(categoryKey = this._currentCategory) {
      const category = this._categories?.[categoryKey];
      if (this._headerTitle) {
        this._headerTitle.textContent = category?.label || categoryKey || "Launcher";
      }
      if (this._headerDescription) {
        this._headerDescription.textContent = category?.description || "Quick access to sites in this category";
      }
    }
    /**
     * Fill/clear the header page-search slot for the active category template.
     * Convention: categories may expose `renderHeaderSearch` to place a search
     * box to the right of the active tab title/description.
     * @param {string} [categoryKey]
     */
    _updateHeaderPageSearch(categoryKey = this._currentCategory) {
      if (!this._headerSearchSlot) return;
      const template = this._categoryPageTemplates[categoryKey];
      const render = template?.renderHeaderSearch;
      const mountedKey = this._headerSearchSlot.dataset.categoryKey || "";
      if (typeof render !== "function") {
        this._headerSearchSlot.innerHTML = "";
        delete this._headerSearchSlot.dataset.categoryKey;
        this._headerSearchSlot.style.display = "none";
        this._categorySearchInput = null;
        return;
      }
      if (mountedKey === categoryKey && this._headerSearchSlot.firstChild) {
        this._headerSearchSlot.style.display = "flex";
        return;
      }
      const hadFocus = !!this._categorySearchInput && document.activeElement === this._categorySearchInput;
      const caret = hadFocus ? this._categorySearchInput.selectionStart ?? this._categorySearchInput.value.length : null;
      this._headerSearchSlot.innerHTML = "";
      this._categorySearchInput = null;
      try {
        const el2 = render(document);
        if (el2) {
          this._headerSearchSlot.appendChild(el2);
          this._headerSearchSlot.dataset.categoryKey = categoryKey;
          this._headerSearchSlot.style.display = "flex";
        } else {
          delete this._headerSearchSlot.dataset.categoryKey;
          this._headerSearchSlot.style.display = "none";
        }
      } catch (err) {
        console.warn("[LauncherPopover] Header page search failed:", categoryKey, err);
        delete this._headerSearchSlot.dataset.categoryKey;
        this._headerSearchSlot.style.display = "none";
      }
      if (hadFocus && this._categorySearchInput) {
        try {
          this._categorySearchInput.focus();
          const pos = caret ?? this._categorySearchInput.value.length;
          this._categorySearchInput.setSelectionRange(pos, pos);
        } catch {
        }
      }
    }
    /**
     * Open category header-search results in the KeyPilot OS preview popup.
     * @param {string} categoryKey
     * @param {string} url
     * @param {string} [title]
     */
    _showCategoryPageResults(categoryKey, url, title = "Search results") {
      if (!categoryKey || !url) return;
      void this._openPreviewWindow(url);
    }
    /**
     * Build search URL from a Sites listing that carries `searchUrlPrefix`.
     * @param {{ searchUrlPrefix?: string, title?: string, url?: string }} site
     * @param {string} query
     * @returns {string|null}
     */
    _buildSiteSearchUrl(site, query) {
      const q = String(query || "").trim();
      const prefix = site?.searchUrlPrefix;
      if (!q || !prefix) return null;
      return `${prefix}${encodeURIComponent(q)}`;
    }
    /**
     * Video Sites that expose a search URL prefix.
     * @returns {Array<{title: string, url: string, searchUrlPrefix: string, isDefault?: boolean}>}
     */
    _getVideoSearchSites() {
      return (this._defaultSites.videos || []).filter((s) => !!s.searchUrlPrefix);
    }
    /**
     * Shared chrome for header page-search bars (Archive, Videos, …).
     * @param {Document} doc
     * @param {{
     *   className: string,
     *   label: string,
     *   inputId: string,
     *   placeholder: string,
     *   ariaLabel: string,
     *   draftKey: string,
     *   leadingControl?: HTMLElement|null,
     *   maxWidth?: string,
     *   buildUrl: (query: string) => (string|null),
     *   resultsTitle: (query: string) => string
     * }} opts
     * @returns {HTMLElement}
     */
    _createHeaderPageSearchBar(doc, opts) {
      const {
        className,
        label: labelText,
        inputId,
        placeholder,
        ariaLabel,
        draftKey,
        leadingControl = null,
        maxWidth = "420px",
        buildUrl,
        resultsTitle
      } = opts;
      const wrap = doc.createElement("div");
      wrap.className = `kp-launcher-page-header-search ${className || ""}`.trim();
      wrap.style.cssText = `
      display: flex;
      flex-direction: row;
      align-items: center;
      gap: 8px;
      width: 100%;
      max-width: ${maxWidth};
    `;
      const label = doc.createElement("label");
      label.textContent = labelText;
      label.htmlFor = inputId;
      label.style.cssText = `
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
    `;
      const row = doc.createElement("div");
      row.style.cssText = `
      display: flex;
      gap: 8px;
      align-items: stretch;
      width: 100%;
    `;
      if (leadingControl) {
        row.appendChild(leadingControl);
      }
      const input = doc.createElement("input");
      input.type = "search";
      input.id = inputId;
      input.className = "kp-launcher-page-search-input";
      input.placeholder = placeholder;
      input.setAttribute("aria-label", ariaLabel);
      input.autocomplete = "off";
      input.spellcheck = false;
      input.value = this._headerSearchDrafts[draftKey] || "";
      input.style.cssText = `
      flex: 1;
      min-width: 0;
      padding: 4px 10px;
      background: #2a2a2a;
      border: 1px solid #444;
      border-radius: 6px;
      color: #fff;
      font-size: 12px;
      outline: none;
      box-sizing: border-box;
    `;
      const submitBtn = doc.createElement("button");
      submitBtn.type = "button";
      submitBtn.className = "kp-launcher-page-search-submit";
      submitBtn.textContent = "Search";
      submitBtn.style.cssText = `
      padding: 4px 10px;
      background: #2a2a2a;
      border: 1px solid #444;
      border-radius: 6px;
      color: #fff;
      font-size: 12px;
      font-weight: 500;
      cursor: pointer;
      white-space: nowrap;
      transition: background 0.15s, border-color 0.15s;
    `;
      const submit = () => {
        const q = (input.value || "").trim();
        this._headerSearchDrafts[draftKey] = input.value || "";
        if (!q) {
          input.focus();
          return;
        }
        const url = buildUrl(q);
        if (!url) {
          input.focus();
          return;
        }
        this._showCategoryPageResults(draftKey, url, resultsTitle(q));
      };
      input.addEventListener("input", () => {
        this._headerSearchDrafts[draftKey] = input.value;
      });
      input.addEventListener("keydown", (e) => {
        if (e.key === "Enter") {
          e.preventDefault();
          e.stopPropagation();
          submit();
          return;
        }
        if (e.key === "Escape" || e.code === "Escape") {
          e.preventDefault();
          e.stopPropagation();
          try {
            e.stopImmediatePropagation();
          } catch {
          }
          this.hide();
        }
      });
      input.addEventListener("focus", () => {
        input.style.borderColor = "#666";
        input.style.background = "#333";
      });
      input.addEventListener("blur", () => {
        input.style.borderColor = "#444";
        input.style.background = "#2a2a2a";
      });
      submitBtn.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        submit();
      });
      submitBtn.addEventListener("mouseenter", () => {
        submitBtn.style.background = "#333";
        submitBtn.style.borderColor = "#555";
      });
      submitBtn.addEventListener("mouseleave", () => {
        submitBtn.style.background = "#2a2a2a";
        submitBtn.style.borderColor = "#444";
      });
      row.appendChild(input);
      row.appendChild(submitBtn);
      wrap.appendChild(label);
      wrap.appendChild(row);
      this._categorySearchInput = input;
      return wrap;
    }
    /**
     * Internet Archive header search — same destination as archive.org's main library
     * search ("Search the Archive"): /search?query=…
     * @param {Document} doc
     * @returns {HTMLElement}
     */
    _createArchiveSearchBar(doc) {
      return this._createHeaderPageSearchBar(doc, {
        className: "kp-launcher-archive-search",
        label: "Search the Archive",
        inputId: "kp-launcher-archive-search-input",
        placeholder: "Texts, movies, software, music, websites\u2026",
        ariaLabel: "Search the Internet Archive",
        draftKey: "archive",
        maxWidth: "420px",
        buildUrl: (q) => `https://archive.org/search?query=${encodeURIComponent(q)}`,
        resultsTitle: (q) => `Archive \xB7 ${q}`
      });
    }
    /**
     * Videos header search with a Sites dropdown (uses each site's searchUrlPrefix).
     * @param {Document} doc
     * @returns {HTMLElement}
     */
    _createVideosSearchBar(doc) {
      const sites = this._getVideoSearchSites();
      if (!sites.length) return null;
      const selected = sites.find((s) => s.url === this._videosSearchSiteUrl) || sites[0];
      this._videosSearchSiteUrl = selected.url;
      const select = doc.createElement("select");
      select.className = "kp-launcher-videos-search-site";
      select.setAttribute("aria-label", "Video site to search");
      select.style.cssText = `
      flex: 0 0 auto;
      max-width: 140px;
      padding: 10px 8px;
      background: #2a2a2a;
      border: 1px solid #444;
      border-radius: 8px;
      color: #fff;
      font-size: 13px;
      outline: none;
      cursor: pointer;
      box-sizing: border-box;
    `;
      for (const site of sites) {
        const opt = doc.createElement("option");
        opt.value = site.url;
        opt.textContent = site.title;
        if (site.url === this._videosSearchSiteUrl) opt.selected = true;
        select.appendChild(opt);
      }
      select.addEventListener("change", () => {
        this._videosSearchSiteUrl = select.value;
      });
      select.addEventListener("focus", () => {
        select.style.borderColor = "#666";
        select.style.background = "#333";
      });
      select.addEventListener("blur", () => {
        select.style.borderColor = "#444";
        select.style.background = "#2a2a2a";
      });
      return this._createHeaderPageSearchBar(doc, {
        className: "kp-launcher-videos-search",
        label: "Search Videos",
        inputId: "kp-launcher-videos-search-input",
        placeholder: "Search videos\u2026",
        ariaLabel: "Search videos",
        draftKey: "videos",
        leadingControl: select,
        maxWidth: "560px",
        buildUrl: (q) => {
          const site = this._getVideoSearchSites().find((s) => s.url === this._videosSearchSiteUrl) || this._getVideoSearchSites()[0];
          return this._buildSiteSearchUrl(site, q);
        },
        resultsTitle: (q) => {
          const site = this._getVideoSearchSites().find((s) => s.url === this._videosSearchSiteUrl) || this._getVideoSearchSites()[0];
          return `${site?.title || "Videos"} \xB7 ${q}`;
        }
      });
    }
    /**
     * Create a grid card for a website
     */
    /**
     * @param {{ title?: string, url?: string, isDefault?: boolean, dateAdded?: number }} item
     * @param {{ variant?: 'launch' | 'listing', showAddedOn?: boolean }} [opts]
     */
    _createGridCard(item, opts = {}) {
      const isLaunch = opts.variant === "launch";
      return isLaunch ? this._createLaunchDeckCard(item) : this._createListingCard(item, { showAddedOn: !!opts.showAddedOn });
    }
    /**
     * Launch Deck tiles — destination launchers, not history/bookmark listings.
     * @param {{ title?: string, url?: string, lastVisitTime?: number }} item
     */
    _createLaunchDeckCard(item) {
      const doc = document;
      const domain = extractDomain(item.url) || String(item.url || "");
      const visitedLabel = this._formatCardDate(item.lastVisitTime);
      const container = doc.createElement("div");
      container.className = "kp-launcher-card-container kp-launcher-launch-card";
      container.style.cssText = `
      display: flex;
      flex-direction: column;
      background: linear-gradient(165deg, #2c3238 0%, #1a1e22 100%);
      border: 1px solid #3d4a55;
      border-radius: 12px;
      overflow: hidden;
      min-height: 148px;
      transition: border-color 0.2s, transform 0.2s, box-shadow 0.2s;
      position: relative;
      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
    `;
      const accent = doc.createElement("div");
      accent.setAttribute("aria-hidden", "true");
      accent.style.cssText = `
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      height: 3px;
      background: linear-gradient(90deg, #5a9e6f 0%, #3d7a52 55%, transparent 100%);
      z-index: 2;
      pointer-events: none;
    `;
      applyCardBackground(container, item.url, {
        fallbackSolid: "#1a1e22",
        hoverSolid: "#242a30",
        manageHover: true,
        videoPrefer: true,
        youtubePrefer: true,
        lazy: true,
        lazyRoot: this._gridContainer,
        lazyRootMargin: "100% 100% 100% 100%",
        priority: 1
      });
      const mainLink = doc.createElement("a");
      mainLink.href = item.url;
      mainLink.target = "_blank";
      mainLink.rel = "noopener noreferrer";
      mainLink.className = "kp-launcher-card-main";
      mainLink.style.cssText = `
      flex: 1;
      min-width: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      text-align: center;
      padding: 28px 20px ${visitedLabel ? "36px" : "18px"};
      text-decoration: none;
      color: inherit;
      cursor: pointer;
      overflow: hidden;
      position: relative;
      z-index: 1;
      gap: 10px;
    `;
      const iconWell = doc.createElement("div");
      iconWell.style.cssText = `
      width: 56px;
      height: 56px;
      border-radius: 14px;
      background: rgba(0, 0, 0, 0.35);
      border: 1px solid rgba(255, 255, 255, 0.08);
      display: flex;
      align-items: center;
      justify-content: center;
      flex-shrink: 0;
      box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
    `;
      const favicon = createFaviconImg(doc, item.url, { size: 36 });
      favicon.style.cssText = `
      width: 36px;
      height: 36px;
      border-radius: 8px;
      flex-shrink: 0;
    `;
      iconWell.appendChild(favicon);
      const title = doc.createElement("div");
      title.textContent = item.title || domain;
      title.style.cssText = `
      font-size: 15px;
      font-weight: 600;
      letter-spacing: 0.01em;
      color: #f2f2f2;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      max-width: 100%;
    `;
      const domainEl = doc.createElement("div");
      domainEl.textContent = domain;
      domainEl.style.cssText = `
      font-size: 11px;
      color: #8a939c;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      max-width: 100%;
    `;
      const launchHint = doc.createElement("div");
      launchHint.textContent = "Launch \u2192";
      launchHint.style.cssText = `
      margin-top: 4px;
      font-size: 11px;
      font-weight: 600;
      letter-spacing: 0.04em;
      text-transform: uppercase;
      color: #7cbc8e;
      opacity: 0.85;
    `;
      const editing = this._launchDeckEditMode && this._isCatalogCategory(this._currentCategory);
      if (editing) {
        launchHint.textContent = "Editing";
        launchHint.style.color = "#c9a86c";
        mainLink.href = "#";
        mainLink.removeAttribute("target");
        mainLink.style.cursor = "default";
        mainLink.addEventListener("click", (e) => {
          e.preventDefault();
          e.stopPropagation();
        });
      }
      mainLink.appendChild(iconWell);
      mainLink.appendChild(title);
      mainLink.appendChild(domainEl);
      mainLink.appendChild(launchHint);
      if (visitedLabel) {
        mainLink.appendChild(
          this._createCardDateOverlay(doc, `Visited on ${visitedLabel}`)
        );
      }
      const footer = doc.createElement("div");
      footer.style.cssText = `
      display: flex;
      align-items: stretch;
      border-top: 1px solid rgba(255, 255, 255, 0.06);
      position: relative;
      z-index: 1;
      flex-shrink: 0;
    `;
      if (editing) {
        const mkEditBtn = (label, titleText, onClick) => {
          const btn = doc.createElement("button");
          btn.type = "button";
          btn.textContent = label;
          btn.title = titleText;
          btn.style.cssText = `
          flex: 1;
          padding: 10px 8px;
          background: rgba(0, 0, 0, 0.25);
          border: none;
          border-left: 1px solid rgba(255, 255, 255, 0.08);
          color: #9aa3ab;
          cursor: pointer;
          font-size: 14px;
          font-weight: 600;
          transition: background 0.15s, color 0.15s;
        `;
          btn.addEventListener("click", (e) => {
            e.preventDefault();
            e.stopPropagation();
            onClick();
          });
          btn.addEventListener("mouseenter", () => {
            btn.style.background = "rgba(0, 0, 0, 0.4)";
            btn.style.color = "#fff";
          });
          btn.addEventListener("mouseleave", () => {
            btn.style.background = "rgba(0, 0, 0, 0.25)";
            btn.style.color = "#9aa3ab";
          });
          return btn;
        };
        const upBtn = mkEditBtn("\u2191", "Move up", () => {
          void this._moveLaunchDeckItem(item.url, -1);
        });
        upBtn.style.borderLeft = "none";
        const downBtn = mkEditBtn("\u2193", "Move down", () => {
          void this._moveLaunchDeckItem(item.url, 1);
        });
        const removeBtn = mkEditBtn("\u2715", "Remove from Launch Deck", () => {
          void this._hideLaunchDeckItem(item.url);
        });
        removeBtn.addEventListener("mouseenter", () => {
          removeBtn.style.color = "#ffb4b4";
        });
        container.draggable = true;
        container.addEventListener("dragstart", (e) => {
          e.dataTransfer?.setData("text/plain", item.url || "");
          container.style.opacity = "0.6";
        });
        container.addEventListener("dragend", () => {
          container.style.opacity = "1";
        });
        container.addEventListener("dragover", (e) => {
          e.preventDefault();
        });
        container.addEventListener("drop", (e) => {
          e.preventDefault();
          e.stopPropagation();
          const fromUrl = e.dataTransfer?.getData("text/plain");
          const toUrl = item.url;
          if (!fromUrl || !toUrl || fromUrl === toUrl) return;
          const sites = this._categories?.[this._currentCategory]?.sites;
          if (!Array.isArray(sites)) return;
          const fromKey = normalizeLaunchDeckUrl(fromUrl);
          const toKey = normalizeLaunchDeckUrl(toUrl);
          const fromIdx = sites.findIndex(
            (s) => normalizeLaunchDeckUrl(s.url) === fromKey
          );
          const toIdx = sites.findIndex(
            (s) => normalizeLaunchDeckUrl(s.url) === toKey
          );
          if (fromIdx < 0 || toIdx < 0) return;
          const next = sites.slice();
          const [moved] = next.splice(fromIdx, 1);
          next.splice(toIdx, 0, moved);
          this._categories[this._currentCategory].sites = next;
          void this._persistCurrentDeckOrder(this._currentCategory).then(() => {
            this._renderCategory(this._currentCategory);
          });
        });
        footer.appendChild(upBtn);
        footer.appendChild(downBtn);
        footer.appendChild(removeBtn);
        container.appendChild(accent);
        container.appendChild(mainLink);
        container.appendChild(footer);
        return container;
      }
      const previewBtn = doc.createElement("button");
      previewBtn.type = "button";
      previewBtn.className = "kp-launcher-card-preview";
      previewBtn.style.cssText = `
      flex: 1;
      padding: 8px 12px;
      background: rgba(0, 0, 0, 0.25);
      border: none;
      color: #9aa3ab;
      cursor: pointer;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: 3px;
      font-size: 16px;
      font-weight: 500;
      transition: background 0.15s, color 0.15s;
    `;
      const launchEye = doc.createElement("span");
      launchEye.setAttribute("aria-hidden", "true");
      launchEye.textContent = "\u{1F441}";
      const launchPreviewLabel = doc.createElement("span");
      launchPreviewLabel.textContent = "Preview";
      launchPreviewLabel.style.cssText = `
      font-size: 10px;
      font-weight: 600;
      letter-spacing: 0.02em;
      line-height: 1;
    `;
      previewBtn.appendChild(launchEye);
      previewBtn.appendChild(launchPreviewLabel);
      previewBtn.title = "Preview / close preview";
      previewBtn.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        this._showPreview(item.url);
      });
      previewBtn.addEventListener("mouseenter", () => {
        previewBtn.style.background = "rgba(0, 0, 0, 0.4)";
        previewBtn.style.color = "#fff";
      });
      previewBtn.addEventListener("mouseleave", () => {
        previewBtn.style.background = "rgba(0, 0, 0, 0.25)";
        previewBtn.style.color = "#9aa3ab";
      });
      footer.appendChild(previewBtn);
      container.appendChild(accent);
      container.appendChild(mainLink);
      container.appendChild(footer);
      container.addEventListener("mouseenter", () => {
        container.style.borderColor = "#5a7a68";
        container.style.transform = "translateY(-3px)";
        container.style.boxShadow = "inset 0 1px 0 rgba(255, 255, 255, 0.08), 0 10px 28px rgba(0, 0, 0, 0.35)";
        launchHint.style.opacity = "1";
        launchHint.style.color = "#9fd4ae";
      });
      container.addEventListener("mouseleave", () => {
        container.style.borderColor = "#3d4a55";
        container.style.transform = "translateY(0)";
        container.style.boxShadow = "inset 0 1px 0 rgba(255, 255, 255, 0.06)";
        launchHint.style.opacity = "0.85";
        launchHint.style.color = "#7cbc8e";
      });
      mainLink.addEventListener("click", (e) => {
        e.stopPropagation();
        this.hide();
      });
      return container;
    }
    /**
     * Favorites / History / Search listing cards.
     * @param {{ title?: string, url?: string, isDefault?: boolean, dateAdded?: number, lastVisitTime?: number }} item
     * @param {{ showAddedOn?: boolean }} [opts]
     */
    _createListingCard(item, opts = {}) {
      const doc = document;
      const domain = extractDomain(item.url) || String(item.url || "");
      const path = extractPath(item.url);
      const visitedLabel = this._formatCardDate(item.lastVisitTime);
      const addedLabel = !visitedLabel && opts.showAddedOn ? this._formatBookmarkAddedDate(item.dateAdded) : null;
      const dateOverlayText = visitedLabel ? `Visited on ${visitedLabel}` : addedLabel ? `Added on ${addedLabel}` : null;
      const container = doc.createElement("div");
      container.className = "kp-launcher-card-container kp-launcher-listing-card";
      container.style.cssText = `
      display: flex;
      background: #2a2a2a;
      border: 1px solid #333;
      border-radius: 8px;
      overflow: hidden;
      min-height: 100px;
      transition: all 0.2s;
      position: relative;
    `;
      applyCardBackground(container, item.url, {
        fallbackSolid: "#2a2a2a",
        hoverSolid: "#333",
        manageHover: true,
        videoPrefer: true,
        youtubePrefer: true,
        lazy: true,
        lazyRoot: this._gridContainer,
        lazyRootMargin: "100% 100% 100% 100%",
        priority: 1
      });
      const mainLink = doc.createElement("a");
      mainLink.href = item.url;
      mainLink.target = "_blank";
      mainLink.rel = "noopener noreferrer";
      mainLink.className = "kp-launcher-card-main";
      mainLink.style.cssText = `
      flex: 1;
      min-width: 0;
      display: flex;
      flex-direction: column;
      padding: 20px;
      padding-bottom: ${dateOverlayText ? "36px" : "20px"};
      text-decoration: none;
      color: inherit;
      cursor: pointer;
      overflow: hidden;
      position: relative;
      z-index: 1;
    `;
      const favicon = createFaviconImg(doc, item.url, { size: 32 });
      favicon.style.cssText = `
      width: 32px;
      height: 32px;
      margin-bottom: 12px;
      border-radius: 6px;
      flex-shrink: 0;
    `;
      const title = doc.createElement("div");
      title.textContent = item.title || domain;
      title.style.cssText = `
      font-size: 14px;
      font-weight: 500;
      color: #fff;
      margin-bottom: 4px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    `;
      const domainEl = doc.createElement("div");
      domainEl.textContent = domain;
      domainEl.style.cssText = `
      font-size: 12px;
      color: #888;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    `;
      const pathEl = doc.createElement("div");
      pathEl.textContent = path;
      pathEl.style.cssText = `
      font-size: 11px;
      color: #666;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      margin-top: 2px;
      ${path ? "" : "display: none;"}
    `;
      mainLink.appendChild(favicon);
      mainLink.appendChild(title);
      mainLink.appendChild(domainEl);
      mainLink.appendChild(pathEl);
      if (dateOverlayText) {
        mainLink.appendChild(
          this._createCardDateOverlay(
            doc,
            dateOverlayText,
            visitedLabel ? "kp-launcher-card-visited-on" : "kp-launcher-card-added-on"
          )
        );
      }
      const previewBtn = doc.createElement("button");
      previewBtn.className = "kp-launcher-card-preview";
      previewBtn.style.cssText = `
      width: 80px;
      flex-shrink: 0;
      background: #1f1f1f;
      border: none;
      border-left: 1px solid #333;
      color: #888;
      cursor: pointer;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: 4px;
      font-size: 22px;
      transition: all 0.2s;
    `;
      const listingEye = doc.createElement("span");
      listingEye.setAttribute("aria-hidden", "true");
      listingEye.textContent = "\u{1F441}";
      const listingPreviewLabel = doc.createElement("span");
      listingPreviewLabel.textContent = "Preview";
      listingPreviewLabel.style.cssText = `
      font-size: 10px;
      font-weight: 600;
      letter-spacing: 0.02em;
      line-height: 1;
    `;
      previewBtn.appendChild(listingEye);
      previewBtn.appendChild(listingPreviewLabel);
      previewBtn.title = "Preview / close preview";
      previewBtn.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        this._showPreview(item.url);
      });
      previewBtn.addEventListener("mouseenter", () => {
        previewBtn.style.background = "#2a2a2a";
        previewBtn.style.color = "#fff";
      });
      previewBtn.addEventListener("mouseleave", () => {
        previewBtn.style.background = "#1f1f1f";
        previewBtn.style.color = "#888";
      });
      container.appendChild(mainLink);
      container.appendChild(previewBtn);
      container.addEventListener("mouseenter", () => {
        container.style.borderColor = "#444";
        container.style.transform = "translateY(-2px)";
      });
      container.addEventListener("mouseleave", () => {
        container.style.borderColor = "#333";
        container.style.transform = "translateY(0)";
      });
      mainLink.addEventListener("click", (e) => {
        e.stopPropagation();
        this.hide();
      });
      return container;
    }
    /**
     * Open a URL in the same KeyPilot OS popup used by Link Preview.
     * @param {string} url
     * @param {{ closeKeys?: string[] }} [opts]
     * @returns {Promise<boolean>}
     */
    async _openPreviewWindow(url, opts = {}) {
      const href = preferHttpsForPreview(String(url || "").trim());
      if (!href || href.startsWith("file://")) return false;
      const om = window.__KeyPilotInstance?.overlayManager;
      if (!om || typeof om._openPopoverWindow !== "function") return false;
      return om._openPopoverWindow({
        url: href,
        kind: "preview",
        closeKeys: opts.closeKeys || ["Escape", "e", "E"]
      });
    }
    /**
     * Close any KeyPilot OS preview popup opened from the launcher.
     */
    _hidePreview() {
      this._currentPreviewUrl = null;
      try {
        window.__KeyPilotInstance?.overlayManager?.hidePopover?.();
      } catch {
      }
      if (this._previewArea) {
        this._previewArea.style.width = "0";
      }
    }
    /**
     * Open Link Preview OS popup for a launcher card URL.
     * Clicking preview again for the same URL closes the popup.
     */
    _showPreview(url) {
      if (url && String(url).startsWith("file://")) {
        console.warn("[LauncherPopover] Cannot preview file:// URLs due to CSP restrictions");
        return;
      }
      const href = preferHttpsForPreview(String(url || "").trim());
      const om = window.__KeyPilotInstance?.overlayManager;
      if (href && this._currentPreviewUrl && preferHttpsForPreview(this._currentPreviewUrl) === href && om?.isPopoverOpen?.() && preferHttpsForPreview(String(om._popoverWindowUrl || "")) === href) {
        this._hidePreview();
        return;
      }
      this._currentPreviewUrl = href || null;
      void this._openPreviewWindow(url);
    }
    /**
     * Scroll to previous sheet
     */
    _scrollUp() {
      if (this._currentSheet > 0) {
        this._currentSheet--;
        this._renderCategory(this._currentCategory);
      }
    }
    /**
     * Scroll to next sheet
     */
    _scrollDown() {
      if (!this._categories || !this._categories[this._currentCategory]) return;
      const items = this._getActiveSubTabItems(this._currentCategory);
      const maxSheets = Math.ceil(items.length / this._itemsPerSheet);
      if (this._currentSheet < maxSheets - 1) {
        this._currentSheet++;
        this._renderCategory(this._currentCategory);
      }
    }
    /**
     * Navigate to previous category tab
     */
    _navigateToPreviousTab() {
      const currentIndex = this._categoryOrder.indexOf(this._currentCategory);
      if (currentIndex > 0) {
        this._selectCategory(this._categoryOrder[currentIndex - 1]);
      }
    }
    /**
     * Navigate to next category tab
     */
    _navigateToNextTab() {
      const currentIndex = this._categoryOrder.indexOf(this._currentCategory);
      if (currentIndex < this._categoryOrder.length - 1) {
        this._selectCategory(this._categoryOrder[currentIndex + 1]);
      }
    }
    /**
     * Handle keyboard events (C/V for scrolling, Arrow keys for tab navigation, Esc to close)
     */
    handleKeyDown(e) {
      if (!this._isOpen) return false;
      const key = e.key.toLowerCase();
      if (key === "escape") {
        e.preventDefault();
        e.stopPropagation();
        if (this._launchDeckEditMode) {
          this._setLaunchDeckEditMode(false);
          return true;
        }
        this.hide();
        return true;
      }
      const active2 = document.activeElement;
      const isSearchFocused = this._searchInput && active2 === this._searchInput || this._categorySearchInput && active2 === this._categorySearchInput;
      if (isSearchFocused && key !== "escape") {
        return false;
      }
      if (key === "/" && !isSearchFocused) {
        e.preventDefault();
        e.stopPropagation();
        this._activateSearchTab({ focus: true });
        return true;
      }
      if (key === "arrowup") {
        e.preventDefault();
        e.stopPropagation();
        this._navigateToPreviousTab();
        return true;
      }
      if (key === "arrowdown") {
        e.preventDefault();
        e.stopPropagation();
        this._navigateToNextTab();
        return true;
      }
      if (key === "c") {
        e.preventDefault();
        e.stopPropagation();
        this._scrollUp();
        return true;
      }
      if (key === "v") {
        e.preventDefault();
        e.stopPropagation();
        this._scrollDown();
        return true;
      }
      return false;
    }
  };

  // src/modules/top-sites-popover.js
  init_constants();
  init_settings_manager();
  init_storage();

  // src/ui/keypilot-hub.js
  init_settings_manager();
  var KP_KEYBOARD_HELP_STORAGE_KEY = "keypilot_keyboard_help_visible";
  var ONBOARDING_ACTIVE_STORAGE_KEY = "keypilot_onboarding_active";
  var KEYPILOT_HUB_CARDS = Object.freeze([
    Object.freeze({
      action: "keyboard",
      title: "Keyboard Reference",
      icon: "TOGGLE_KEYBOARD_HELP",
      hint: "K",
      role: "switch"
    }),
    Object.freeze({
      action: "docs",
      title: "Help / Docs",
      icon: "LOOKUP_WORD",
      hint: "Alt+H",
      meta: "Documentation",
      role: "button"
    }),
    Object.freeze({
      action: "tutorial",
      title: "Onboarding Tutorial",
      icon: "LAUNCHER",
      hint: "Alt+I",
      meta: "Walkthrough",
      role: "button"
    }),
    Object.freeze({
      action: "settings",
      title: "Settings",
      icon: "OPEN_SETTINGS_POPOVER",
      hint: "'",
      meta: "Preferences",
      role: "button"
    }),
    Object.freeze({
      action: "control-strip",
      title: "Control Strip",
      icon: "TOP_SITES",
      hint: "Alt+J",
      role: "switch"
    })
  ]);
  async function queryKeyboardHelpVisible() {
    try {
      const syncResult = await chrome.storage.sync.get([KP_KEYBOARD_HELP_STORAGE_KEY]);
      if (typeof syncResult?.[KP_KEYBOARD_HELP_STORAGE_KEY] === "boolean") {
        return syncResult[KP_KEYBOARD_HELP_STORAGE_KEY];
      }
    } catch {
    }
    try {
      const localResult = await chrome.storage.local.get([KP_KEYBOARD_HELP_STORAGE_KEY]);
      if (typeof localResult?.[KP_KEYBOARD_HELP_STORAGE_KEY] === "boolean") {
        return localResult[KP_KEYBOARD_HELP_STORAGE_KEY];
      }
    } catch {
    }
    return true;
  }
  async function setKeyboardHelpVisible(visible) {
    const desired = Boolean(visible);
    const payload = { [KP_KEYBOARD_HELP_STORAGE_KEY]: desired, timestamp: Date.now() };
    try {
      await chrome.storage.sync.set(payload);
    } catch {
    }
    try {
      await chrome.storage.local.set(payload);
    } catch {
    }
    return desired;
  }
  async function queryControlStripVisible() {
    try {
      const settings = await getSettings();
      return settings?.controlStrip?.visible !== false;
    } catch {
      return true;
    }
  }
  async function setControlStripVisible(visible) {
    const desired = Boolean(visible);
    await setSettings({ controlStrip: { visible: desired } });
    return desired;
  }
  async function queryOnboardingActive() {
    try {
      const syncResult = await chrome.storage.sync.get([ONBOARDING_ACTIVE_STORAGE_KEY]);
      if (typeof syncResult?.[ONBOARDING_ACTIVE_STORAGE_KEY] === "boolean") {
        return syncResult[ONBOARDING_ACTIVE_STORAGE_KEY];
      }
    } catch {
    }
    try {
      const localResult = await chrome.storage.local.get([ONBOARDING_ACTIVE_STORAGE_KEY]);
      if (typeof localResult?.[ONBOARDING_ACTIVE_STORAGE_KEY] === "boolean") {
        return localResult[ONBOARDING_ACTIVE_STORAGE_KEY];
      }
    } catch {
    }
    return false;
  }
  function paintHubToggleCard(card, on, statusText) {
    if (!card) return;
    const visible = Boolean(on);
    card.classList.toggle("is-on", visible);
    if (card.getAttribute("role") === "switch") {
      card.setAttribute("aria-checked", visible ? "true" : "false");
    }
    const status = card.querySelector("[data-status]");
    if (status) status.textContent = statusText ?? (visible ? "ON" : "OFF");
  }
  function paintHubOnboardingCard(card, active2) {
    if (!card) return;
    const on = Boolean(active2);
    card.classList.toggle("is-on", on);
    const status = card.querySelector("[data-status]");
    if (status) status.textContent = on ? "ACTIVE" : "Walkthrough";
  }
  async function refreshKeypilotHubCards(root) {
    if (!root) return;
    const keyboard = root.querySelector('[data-action="keyboard"]');
    const controlStrip = root.querySelector('[data-action="control-strip"]');
    const tutorial = root.querySelector('[data-action="tutorial"]');
    try {
      paintHubToggleCard(keyboard, await queryKeyboardHelpVisible());
    } catch {
      paintHubToggleCard(keyboard, true);
    }
    try {
      paintHubToggleCard(controlStrip, await queryControlStripVisible());
    } catch {
      paintHubToggleCard(controlStrip, true);
    }
    try {
      paintHubOnboardingCard(tutorial, await queryOnboardingActive());
    } catch {
      paintHubOnboardingCard(tutorial, false);
    }
  }
  function createKeypilotHubCard(doc, def) {
    const btn = doc.createElement("button");
    btn.type = "button";
    btn.className = "kp-url-row kp-hub-card";
    btn.dataset.action = def.action;
    btn.setAttribute("aria-label", def.title);
    if (def.role === "switch") {
      btn.setAttribute("role", "switch");
      btn.setAttribute("aria-checked", "false");
    }
    const content = doc.createElement("span");
    content.className = "kp-url-content";
    const icon = doc.createElement("span");
    icon.className = "kp-url-favicon kp-hub-icon";
    icon.setAttribute("aria-hidden", "true");
    const uri = getActionIconDataUri(def.icon, { fill: "rgba(255,255,255,0.94)" });
    if (uri) icon.style.setProperty("--kp-hub-icon", uri);
    const text = doc.createElement("span");
    text.className = "kp-url-text";
    const title = doc.createElement("span");
    title.className = "kp-url-domain";
    title.textContent = def.title;
    const meta = doc.createElement("span");
    meta.className = "kp-url-title";
    meta.dataset.status = "";
    meta.textContent = def.role === "switch" ? "\u2026" : def.meta || def.hint;
    const path = doc.createElement("span");
    path.className = "kp-url-path";
    path.textContent = def.hint;
    text.append(title, meta, path);
    content.append(icon, text);
    btn.append(content);
    if (def.role === "switch") {
      const sw = doc.createElement("span");
      sw.className = "kp-hub-switch";
      sw.setAttribute("aria-hidden", "true");
      const track = doc.createElement("span");
      track.className = "kp-hub-switch-track";
      const thumb = doc.createElement("span");
      thumb.className = "kp-hub-switch-thumb";
      track.append(thumb);
      sw.append(track);
      btn.append(sw);
    }
    return btn;
  }

  // src/modules/top-sites-popover.js
  var TAB_STATE_KEY = "kpTopSitesSelectedTab_v1";
  var POPUP_ID = "kpv2-top-sites-popover";
  var DEFAULT_WIDTH_PX = 600;
  var CARD_ROW_PX = 80;
  var CARD_GAP_PX = 12;
  var GRID_PAD_Y_PX = 24;
  var TITLEBAR_PX = 28;
  var TABSTRIP_PX = 44;
  var STATUS_PX = 22;
  var PANEL_BORDER_Y_PX = 2;
  var THREE_ROW_GRID_PX = CARD_ROW_PX * 3 + CARD_GAP_PX * 2 + GRID_PAD_Y_PX;
  var DEFAULT_HEIGHT_PX = TITLEBAR_PX + TABSTRIP_PX + STATUS_PX + THREE_ROW_GRID_PX + PANEL_BORDER_Y_PX + 8;
  var MIN_WIDTH_PX = 480;
  var MIN_HEIGHT_PX = TITLEBAR_PX + TABSTRIP_PX + STATUS_PX + THREE_ROW_GRID_PX + PANEL_BORDER_Y_PX;
  var POSITION_MARGIN_PX = PANEL_POSITION_MARGIN_PX;
  var TABS = Object.freeze([
    Object.freeze({ id: "toolbar", label: "Toolbar" }),
    Object.freeze({ id: "mostVisited", label: "Most Visited" }),
    Object.freeze({ id: "recentBookmarks", label: "Recent Bookmarks" }),
    Object.freeze({ id: "keypilot", label: "KeyPilot" })
  ]);
  var EMPTY_COPY = Object.freeze({
    toolbar: "No bookmarks on the Bookmarks bar.",
    mostVisited: "No frequently visited sites yet.",
    recentBookmarks: "No recent bookmarks.",
    keypilot: "No KeyPilot tools."
  });
  var CARD_CLASS_NAMES = {
    row: "kp-url-row",
    rowSelected: "kp-url-row--selected",
    content: "kp-url-content",
    text: "kp-url-text",
    title: "kp-url-domain",
    meta: "kp-url-title",
    url: "kp-url-path",
    favicon: "kp-url-favicon"
  };
  var GEAR_PATH = "M495.9 166.1c3.3 12.7 .9 26.3-7.1 36.1l-37.3 45.7c2.1 11.1 3.2 22.6 3.2 34.3s-1.1 23.2-3.2 34.3l37.3 45.7c8 9.8 10.4 23.4 7.1 36.1c-6.3 24.2-17.7 46.6-33.1 66.3c-8.1 10.3-21.2 14.9-33.9 12.1l-57.5-12.7c-17.9 15.3-38.4 27.3-60.7 35.4l-13.7 57.5c-2.9 12.1-12.9 21.1-25.4 22.4c-24.2 2.6-49.1 2.6-73.3 0c-12.5-1.3-22.5-10.3-25.4-22.4l-13.7-57.5c-22.3-8.1-42.8-20.1-60.7-35.4L71.6 436.6c-12.7 2.8-25.8-1.8-33.9-12.1C22.3 404.8 10.9 382.4 4.6 358.2c-3.3-12.7-.9-26.3 7.1-36.1l37.3-45.7c-2.1-11.1-3.2-22.6-3.2-34.3s1.1-23.2 3.2-34.3L11.7 161.9c-8-9.8-10.4-23.4-7.1-36.1C10.9 101.6 22.3 79.2 37.7 59.5c8.1-10.3 21.2-14.9 33.9-12.1l57.5 12.7c17.9-15.3 38.4-27.3 60.7-35.4L203.5 17.2c2.9-12.1 12.9-21.1 25.4-22.4c24.2-2.6 49.1-2.6 73.3 0c12.5 1.3 22.5 10.3 25.4 22.4l13.7 57.5c22.3 8.1 42.8 20.1 60.7 35.4l57.5-12.7c12.7-2.8 25.8 1.8 33.9 12.1c15.4 19.7 26.8 42.1 33.1 66.3zM256 336a80 80 0 1 0 0-160 80 80 0 1 0 0 160z";
  function renderCardText({ item, parts }) {
    const url = String(item?.url || "").trim();
    const title = String(item?.title || "").trim();
    const { domain, path } = parseUrlForThreeLineDisplay(url);
    parts.titleEl.textContent = domain || url || "";
    parts.metaEl.textContent = title && title !== domain ? title : "";
    parts.urlEl.textContent = path === "/" ? "" : path;
  }
  function attachPageThumb(row, url) {
    const pageUrl = String(url || "").trim();
    if (!row || !pageUrl) return;
    applyCardBackground(row, pageUrl, {
      fallbackSolid: "",
      hoverSolid: "",
      manageHover: false,
      youtubePrefer: true,
      useCssVar: true,
      cssVarName: "--kp-page-thumb",
      readyClass: "kp-has-page-thumb"
    });
  }
  function hasStoredPosition(pos) {
    if (!pos || typeof pos !== "object") return false;
    return Number.isFinite(Number(pos.left)) || Number.isFinite(Number(pos.top)) || typeof pos.anchor === "string" && pos.anchor;
  }
  var TopSitesPopover = class {
    /**
     * @param {object} opts
     * @param {import('./popup-manager.js').PopupManager} opts.popupManager
     * @param {(visible: boolean) => void} [opts.onVisibilityChange]
     */
    constructor({ popupManager, onVisibilityChange } = {}) {
      this.popupManager = popupManager || null;
      this._onVisibilityChange = typeof onVisibilityChange === "function" ? onVisibilityChange : null;
      this._panel = null;
      this._titlebar = null;
      this._tabList = null;
      this._grid = null;
      this._status = null;
      this._gearBtn = null;
      this._menu = null;
      this._open = false;
      this._openGen = 0;
      this._persistent = false;
      this._hostedInPopupManager = false;
      this._suppressPositionPersist = false;
      this._panelPosition = { ...DEFAULT_SETTINGS.panelPositions.topSites };
      this._dragDispose = null;
      this._resizeDispose = null;
      this._docClickBound = false;
      this._storageBound = false;
      this._onDocClick = this._onDocClick.bind(this);
      this._onStorageChanged = this._onStorageChanged.bind(this);
      this._onWinResize = this._onWinResize.bind(this);
      this._tab = "toolbar";
      this._selectedIndex = 0;
      this._items = [];
      this._bindSettingsSync();
    }
    isOpen() {
      return this._open;
    }
    isPersistent() {
      return !!this._persistent;
    }
    toggle() {
      if (this._open) this.hide();
      else void this.show();
    }
    /**
     * @param {{ persistClosed?: boolean }} [opts]
     */
    async show() {
      if (this._open) return;
      this._open = true;
      const gen = ++this._openGen;
      await this._loadTabState();
      await this._hydrateFromSettings();
      if (!this._stillOpen(gen)) return;
      this._ensureDom();
      this._injectStyles();
      this._syncTabButtons();
      this._syncPersistMenu();
      this._applySizeAndPosition();
      this._applyHost();
      this._attachMoveResize();
      try {
        this._panel?.focus?.({ preventScroll: true });
      } catch {
      }
      await this._loadAndRender(gen);
    }
    /**
     * @param {{ persistClosed?: boolean }} [opts]
     */
    hide(opts = {}) {
      if (!this._open && !this._panel) return;
      const persistClosed = opts.persistClosed !== false;
      this._open = false;
      this._openGen += 1;
      void this._persistTabState();
      this._disposeChrome();
      this._closeMenu();
      try {
        this.popupManager?.hideModal?.(POPUP_ID);
      } catch {
      }
      this._hostedInPopupManager = false;
      try {
        this._panel?.remove?.();
      } catch {
      }
      this._panel = null;
      this._titlebar = null;
      this._tabList = null;
      this._grid = null;
      this._status = null;
      this._gearBtn = null;
      this._menu = null;
      this._items = [];
      this._selectedIndex = 0;
      if (persistClosed && this._persistent) {
        try {
          this._onVisibilityChange?.(false);
        } catch {
        }
      }
    }
    /**
     * @param {KeyboardEvent} e
     * @returns {boolean}
     */
    handleKeyDown(e) {
      if (!this._open) return false;
      const key = e.key;
      const code = e.code;
      if (key === "Escape" || code === "Escape") {
        if (this._menu && !this._menu.hidden) {
          e.preventDefault();
          e.stopPropagation();
          try {
            e.stopImmediatePropagation();
          } catch {
          }
          this._closeMenu();
          return true;
        }
        e.preventDefault();
        e.stopPropagation();
        try {
          e.stopImmediatePropagation();
        } catch {
        }
        this.hide();
        return true;
      }
      if (key >= "1" && key <= "9") {
        const idx = Number(key) - 1;
        const tab = TABS[idx];
        if (tab) {
          e.preventDefault();
          e.stopPropagation();
          void this._selectTab(tab.id);
          return true;
        }
      }
      if (key === "Tab") {
        e.preventDefault();
        e.stopPropagation();
        this._cycleTab(e.shiftKey ? -1 : 1);
        return true;
      }
      if (key === "ArrowLeft" || key === "ArrowRight") {
        const atTablist = this._eventInTabList(e);
        if (atTablist || e.altKey) {
          e.preventDefault();
          e.stopPropagation();
          this._cycleTab(key === "ArrowLeft" ? -1 : 1);
          return true;
        }
        e.preventDefault();
        e.stopPropagation();
        this._moveSelection(key === "ArrowLeft" ? -1 : 1);
        return true;
      }
      if (key === "ArrowUp" || key === "ArrowDown") {
        e.preventDefault();
        e.stopPropagation();
        const cols = this._gridColumnCount();
        this._moveSelection(key === "ArrowUp" ? -cols : cols);
        return true;
      }
      if (key === "Home") {
        e.preventDefault();
        e.stopPropagation();
        this._setSelectedIndex(0);
        return true;
      }
      if (key === "End") {
        e.preventDefault();
        e.stopPropagation();
        this._setSelectedIndex(this._items.length - 1);
        return true;
      }
      if (key === "Enter" || key === " ") {
        const item = this._items[this._selectedIndex];
        if (this._tab === "keypilot" && item?.action) {
          e.preventDefault();
          e.stopPropagation();
          void this._activateHubAction(item.action);
          return true;
        }
        if (item?.url) {
          e.preventDefault();
          e.stopPropagation();
          void this._openUrl(item.url);
          return true;
        }
      }
      if (key === "PageUp" || key === "PageDown") {
        e.preventDefault();
        e.stopPropagation();
        try {
          const grid = this._grid;
          if (grid) {
            const delta = key === "PageUp" ? -grid.clientHeight : grid.clientHeight;
            grid.scrollBy({ top: delta, behavior: "smooth" });
          }
        } catch {
        }
        return true;
      }
      return false;
    }
    /** @param {number} gen */
    _stillOpen(gen) {
      return this._open && this._openGen === gen;
    }
    async _loadTabState() {
      try {
        const raw = await storageGetValue(TAB_STATE_KEY, "toolbar");
        const id = String(raw || "");
        this._tab = TABS.some((t) => t.id === id) ? (
          /** @type {TopSitesTabId} */
          id
        ) : "toolbar";
      } catch {
        this._tab = "toolbar";
      }
    }
    _persistTabState() {
      return storageSetValue(TAB_STATE_KEY, this._tab).catch(() => {
      });
    }
    async _hydrateFromSettings() {
      try {
        const settings = await getSettings();
        this._persistent = !!settings?.topSitesPersistent;
        const stored = settings?.panelPositions?.topSites;
        const normalized = normalizePanelPositionState(
          stored,
          DEFAULT_SETTINGS.panelPositions.topSites
        );
        this._panelPosition = {
          ...normalized || {},
          ...Number.isFinite(Number(stored?.width)) && Number(stored.width) > 0 ? { width: Number(stored.width) } : {},
          ...Number.isFinite(Number(stored?.height)) && Number(stored.height) > 0 ? { height: Number(stored.height) } : {}
        };
      } catch {
        this._persistent = false;
      }
    }
    _bindSettingsSync() {
      if (this._storageBound) return;
      try {
        if (chrome?.storage?.onChanged?.addListener) {
          chrome.storage.onChanged.addListener(this._onStorageChanged);
          this._storageBound = true;
        }
      } catch {
      }
    }
    _onStorageChanged(changes, area) {
      try {
        if (area !== "sync" && area !== "local") return;
        if (this._open && this._tab === "keypilot") {
          const help = changes?.[KP_KEYBOARD_HELP_STORAGE_KEY];
          const onboarding = changes?.[ONBOARDING_ACTIVE_STORAGE_KEY];
          const settingsEntry = changes?.[SETTINGS_STORAGE_KEY];
          const cs = settingsEntry?.newValue?.controlStrip;
          if (help || onboarding || cs && typeof cs.visible === "boolean") {
            void this._refreshHubStatus();
          }
        }
        const entry = changes && changes[SETTINGS_STORAGE_KEY];
        if (!entry || !entry.newValue) return;
        const next = entry.newValue;
        if (Object.prototype.hasOwnProperty.call(next, "topSitesPersistent")) {
          const persistent = !!next.topSitesPersistent;
          if (persistent !== this._persistent) {
            this._persistent = persistent;
            this._syncPersistMenu();
            if (this._open) this._applyHost();
          }
        }
        const nextPos = next.panelPositions?.topSites;
        if (nextPos && typeof nextPos === "object" && this._open && this._panel) {
          this._suppressPositionPersist = true;
          const normalized = normalizePanelPositionState(
            nextPos,
            DEFAULT_SETTINGS.panelPositions.topSites
          );
          this._panelPosition = {
            ...normalized || this._panelPosition,
            ...Number.isFinite(Number(nextPos.width)) && Number(nextPos.width) > 0 ? { width: Number(nextPos.width) } : {},
            ...Number.isFinite(Number(nextPos.height)) && Number(nextPos.height) > 0 ? { height: Number(nextPos.height) } : {}
          };
          this._applySizeAndPosition();
          this._suppressPositionPersist = false;
        }
      } catch {
        this._suppressPositionPersist = false;
      }
    }
    _applyHost() {
      if (!this._panel) return;
      if (this._persistent) {
        if (this._hostedInPopupManager) {
          try {
            this.popupManager?.hideModal?.(POPUP_ID);
          } catch {
          }
          this._hostedInPopupManager = false;
        }
        try {
          this._panel.style.zIndex = String(Z_INDEX.POPUP_PANEL_BASE);
        } catch {
        }
        try {
          ensureChromeHostMounted(this._panel);
        } catch {
        }
        return;
      }
      if (!this.popupManager) {
        try {
          ensureChromeHostMounted(this._panel);
        } catch {
        }
        return;
      }
      this.popupManager.showModal({
        id: POPUP_ID,
        panel: this._panel,
        onRequestClose: () => this.hide(),
        resizable: false,
        blur: false
      });
      this._hostedInPopupManager = true;
    }
    _panelSize() {
      const w = Number(this._panelPosition?.width);
      const h = Number(this._panelPosition?.height);
      return {
        width: Math.max(MIN_WIDTH_PX, Number.isFinite(w) && w > 0 ? w : DEFAULT_WIDTH_PX),
        height: Math.max(MIN_HEIGHT_PX, Number.isFinite(h) && h > 0 ? h : DEFAULT_HEIGHT_PX)
      };
    }
    _applySizeAndPosition() {
      const panel = this._panel;
      if (!panel) return;
      const size = this._panelSize();
      try {
        panel.style.width = `${size.width}px`;
        panel.style.height = `${size.height}px`;
        panel.style.maxWidth = "";
        panel.style.maxHeight = "";
        panel.style.transform = "none";
      } catch {
      }
      const pos = this._panelPosition;
      if (hasStoredPosition(pos)) {
        try {
          const resolved = applyPanelPosition(panel, pos, {
            margin: POSITION_MARGIN_PX,
            fallbackWidth: size.width,
            fallbackHeight: size.height,
            width: size.width,
            height: size.height,
            pinSize: true
          });
          if (resolved) {
            this._panelPosition = {
              ...this._panelPosition,
              left: resolved.left,
              top: resolved.top,
              anchor: resolved.anchor === void 0 ? null : resolved.anchor
            };
          }
        } catch {
        }
        return;
      }
      const vp = getViewportSize();
      const left = Math.round(Math.max(POSITION_MARGIN_PX, (vp.width - size.width) / 2));
      const top = Math.round(Math.max(POSITION_MARGIN_PX, (vp.height - size.height) / 2));
      try {
        applyPanelPosition(panel, { left, top, anchor: null }, {
          margin: POSITION_MARGIN_PX,
          fallbackWidth: size.width,
          fallbackHeight: size.height,
          width: size.width,
          height: size.height,
          pinSize: true
        });
      } catch {
      }
      this._panelPosition = { ...this._panelPosition, left, top, anchor: null };
    }
    _attachMoveResize() {
      this._disposeChrome();
      const panel = this._panel;
      const handle = this._titlebar;
      if (!panel || !handle) return;
      const shadowRoot = panel.shadowRoot || panel;
      try {
        const api = makePanelDraggable(panel, handle, {
          margin: POSITION_MARGIN_PX,
          excludeSelector: "button, .kpv2-ts-menu, .kpv2-popover-titlebar-actions, .kpv2-popover-titlebar-close",
          onMoveEnd: (state) => {
            if (!state?.moved) return;
            void this._persistPosition({
              left: state.left,
              top: state.top,
              anchor: state.anchor,
              width: this._panelPosition.width,
              height: this._panelPosition.height
            });
          }
        });
        this._dragDispose = api?.dispose || null;
      } catch {
      }
      try {
        this._resizeDispose = makePanelResizable(panel, {
          mount: shadowRoot,
          minWidth: MIN_WIDTH_PX,
          minHeight: MIN_HEIGHT_PX,
          margin: POSITION_MARGIN_PX,
          onResizeEnd: (size) => {
            void this._persistPosition({
              ...this._panelPosition,
              width: size.width,
              height: size.height
            });
          }
        });
      } catch {
      }
      try {
        window.addEventListener("resize", this._onWinResize);
      } catch {
      }
    }
    _disposeChrome() {
      try {
        this._dragDispose?.();
      } catch {
      }
      try {
        this._resizeDispose?.();
      } catch {
      }
      this._dragDispose = null;
      this._resizeDispose = null;
      try {
        window.removeEventListener("resize", this._onWinResize);
      } catch {
      }
    }
    _onWinResize() {
      if (!this._open || !this._panel) return;
      this._applySizeAndPosition();
    }
    async _persistPosition(next) {
      if (this._suppressPositionPersist) return;
      const normalized = normalizePanelPositionState(
        next,
        DEFAULT_SETTINGS.panelPositions.topSites
      ) || {};
      this._panelPosition = {
        ...this._panelPosition,
        left: normalized.left,
        top: normalized.top,
        anchor: normalized.anchor === void 0 ? null : normalized.anchor,
        ...Number.isFinite(Number(next?.width)) && Number(next.width) > 0 ? { width: Number(next.width) } : {},
        ...Number.isFinite(Number(next?.height)) && Number(next.height) > 0 ? { height: Number(next.height) } : {}
      };
      try {
        await setSettings({ panelPositions: { topSites: { ...this._panelPosition } } });
      } catch {
      }
    }
    async _setPersistent(next) {
      const persistent = !!next;
      this._persistent = persistent;
      this._syncPersistMenu();
      try {
        await setSettings({ topSitesPersistent: persistent });
      } catch {
      }
      if (this._open) this._applyHost();
      try {
        this._onVisibilityChange?.(persistent && this._open, { applyUi: false });
      } catch {
      }
    }
    _syncPersistMenu() {
      const item = this._menu?.querySelector?.("[data-kp-ts-persist]");
      if (!item) return;
      item.setAttribute("aria-checked", this._persistent ? "true" : "false");
    }
    _toggleMenu() {
      if (!this._menu) return;
      const open = this._menu.hidden;
      if (open) this._openMenu();
      else this._closeMenu();
    }
    _openMenu() {
      if (!this._menu) return;
      this._menu.hidden = false;
      this._gearBtn?.setAttribute("aria-expanded", "true");
      if (!this._docClickBound) {
        try {
          document.addEventListener("pointerdown", this._onDocClick, true);
          this._docClickBound = true;
        } catch {
        }
      }
    }
    _closeMenu() {
      if (this._menu) this._menu.hidden = true;
      this._gearBtn?.setAttribute("aria-expanded", "false");
      if (this._docClickBound) {
        try {
          document.removeEventListener("pointerdown", this._onDocClick, true);
        } catch {
        }
        this._docClickBound = false;
      }
    }
    _onDocClick(e) {
      const t = e.target;
      if (!(t instanceof Node)) return;
      if (this._gearBtn?.contains?.(t) || this._menu?.contains?.(t)) return;
      const path = typeof e.composedPath === "function" ? e.composedPath() : [];
      if (path.includes(this._gearBtn) || path.includes(this._menu)) return;
      this._closeMenu();
    }
    _createGearButton(doc) {
      const btn = doc.createElement("button");
      btn.type = "button";
      btn.className = "kpv2-ts-gear";
      btn.title = "Top Sites options";
      btn.setAttribute("aria-label", "Top Sites options");
      btn.setAttribute("aria-haspopup", "menu");
      btn.setAttribute("aria-expanded", "false");
      const img = doc.createElement("img");
      img.alt = "";
      img.setAttribute("aria-hidden", "true");
      const cssUri = getActionIconDataUri("OPEN_SETTINGS_POPOVER", { fill: "rgba(200,200,205,0.95)" });
      const match = String(cssUri || "").match(/^url\("(.+)"\)$/);
      if (match) {
        img.src = match[1];
      } else {
        img.src = `data:image/svg+xml,${encodeURIComponent(
          `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="rgba(200,200,205,0.95)"><path d="${GEAR_PATH}"/></svg>`
        )}`;
      }
      btn.appendChild(img);
      btn.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        this._toggleMenu();
      }, true);
      return btn;
    }
    _createPersistMenu(doc) {
      const menu = doc.createElement("div");
      menu.className = "kpv2-ts-menu";
      menu.setAttribute("role", "menu");
      menu.hidden = true;
      const item = doc.createElement("button");
      item.type = "button";
      item.className = "kpv2-ts-menu-item";
      item.setAttribute("role", "menuitemcheckbox");
      item.dataset.kpTsPersist = "true";
      item.setAttribute("aria-checked", "false");
      item.textContent = "Keep open across pages";
      item.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        void this._setPersistent(!this._persistent);
        this._closeMenu();
      }, true);
      menu.appendChild(item);
      return menu;
    }
    _injectStyles() {
      const c = NCT_DARK_UI_COLORS;
      const css = `
      .kpv2-top-sites-panel {
        box-sizing: border-box;
        position: fixed;
        left: 50%;
        top: 50%;
        width: ${DEFAULT_WIDTH_PX}px;
        height: ${DEFAULT_HEIGHT_PX}px;
        display: flex;
        flex-direction: column;
        border-radius: ${NCT_DARK_UI_PANEL_RADIUS};
        border: ${NCT_DARK_UI_PANEL_BORDER};
        background: ${NCT_DARK_UI_PANEL_BACKGROUND};
        box-shadow: ${NCT_DARK_UI_PANEL_BOX_SHADOW};
        overflow: hidden;
        outline: none;
        font-family: ${NCT_DARK_UI_FONT};
        color: ${c.fg};
        z-index: ${Z_INDEX.POPUP_PANEL_BASE};
      }

      .kpv2-top-sites-panel .kpv2-ts-gear {
        margin: 0;
        appearance: none;
        -webkit-appearance: none;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        box-sizing: border-box;
        width: 22px;
        height: 22px;
        min-width: 22px;
        min-height: 22px;
        padding: 0;
        border: none;
        border-radius: 4px;
        background: transparent;
        color: rgba(200, 200, 205, 0.9);
        cursor: pointer;
      }

      .kpv2-top-sites-panel .kpv2-ts-gear img {
        width: 13px;
        height: 13px;
        display: block;
        pointer-events: none;
        opacity: 0.92;
      }

      .kpv2-top-sites-panel .kpv2-ts-gear:hover,
      .kpv2-top-sites-panel .kpv2-ts-gear[aria-expanded="true"] {
        background: rgba(255, 255, 255, 0.08);
      }

      .kpv2-top-sites-panel .kpv2-ts-menu {
        position: absolute;
        top: 28px;
        right: 28px;
        z-index: 8;
        min-width: 220px;
        padding: 4px;
        border-radius: ${NCT_DARK_UI_BTN_RADIUS};
        border: ${NCT_DARK_UI_BTN_BORDER};
        background: ${NCT_DARK_UI_PANEL_BACKGROUND};
        box-shadow: ${NCT_DARK_UI_PANEL_BOX_SHADOW};
      }

      .kpv2-top-sites-panel .kpv2-ts-menu[hidden] {
        display: none !important;
      }

      .kpv2-top-sites-panel .kpv2-ts-menu-item {
        appearance: none;
        -webkit-appearance: none;
        display: flex;
        align-items: center;
        gap: 8px;
        width: 100%;
        margin: 0;
        padding: 8px 10px;
        border: none;
        border-radius: 6px;
        background: transparent;
        color: ${c.fg};
        font-family: inherit;
        font-size: 12px;
        font-weight: 600;
        text-align: left;
        cursor: pointer;
      }

      .kpv2-top-sites-panel .kpv2-ts-menu-item::before {
        content: "";
        width: 12px;
        height: 12px;
        flex: 0 0 auto;
        border-radius: 3px;
        border: 1px solid ${c.panelEdgeDark};
        background: transparent;
        box-sizing: border-box;
      }

      .kpv2-top-sites-panel .kpv2-ts-menu-item[aria-checked="true"]::before {
        background: ${c.litEdge};
        border-color: ${c.litEdge};
      }

      .kpv2-top-sites-panel .kpv2-ts-menu-item:hover {
        background: rgba(255, 255, 255, 0.08);
      }

      .kpv2-top-sites-panel .kpv2-ts-tabstrip {
        display: flex;
        align-items: flex-end;
        gap: 4px;
        padding: 8px 12px 0;
        min-height: 36px;
        flex: 0 0 auto;
        background: ${NCT_DARK_UI_PANEL_BACKGROUND};
        border-bottom: 1px solid ${c.panelEdgeDark};
      }

      .kpv2-top-sites-panel .kpv2-ts-tab[data-tab-id="keypilot"] {
        margin-left: auto;
      }

      .kpv2-top-sites-panel .kpv2-ts-tab {
        appearance: none;
        -webkit-appearance: none;
        position: relative;
        margin: 0 0 -1px;
        padding: 8px 14px 10px;
        border: 1px solid ${c.panelEdgeDark};
        border-bottom: none;
        border-radius: 9px 9px 0 0;
        background: ${NCT_DARK_UI_BTN_GRADIENT};
        color: ${c.fgDim};
        font-family: inherit;
        font-size: 12px;
        font-weight: 700;
        letter-spacing: 0.01em;
        line-height: 1.15;
        cursor: pointer;
        box-shadow: inset 0 1px 0 rgba(255,255,255,0.14);
        z-index: 0;
      }

      .kpv2-top-sites-panel .kpv2-ts-tab:hover {
        color: ${c.fg};
        background: linear-gradient(180deg, #555 0%, ${c.btnMid} 55%, ${c.btnBot} 100%);
      }

      .kpv2-top-sites-panel .kpv2-ts-tab[aria-selected="true"] {
        color: ${c.fg};
        background: ${NCT_DARK_UI_PANEL_BACKGROUND};
        z-index: 1;
        padding-top: 10px;
        padding-bottom: 12px;
        box-shadow:
          inset 0 1px 0 rgba(255,255,255,0.16),
          0 0 0 1px ${c.litEdge};
      }

      .kpv2-top-sites-panel .kpv2-ts-tab:focus-visible {
        outline: none;
        box-shadow: inset 0 0 0 1px rgba(74,144,200,0.65);
      }

      .kpv2-top-sites-panel .kpv2-ts-body {
        display: flex;
        flex-direction: column;
        flex: 1 1 auto;
        min-height: 0;
        background: ${NCT_DARK_UI_PANEL_BACKGROUND};
      }

      .kpv2-top-sites-panel .kpv2-ts-status {
        padding: 6px 14px 0;
        font-size: 11px;
        font-weight: 600;
        color: ${c.fgMute};
        flex: 0 0 auto;
      }

      .kpv2-top-sites-panel .kpv2-ts-grid {
        display: grid !important;
        grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
        grid-auto-rows: 80px;
        gap: 12px;
        padding: 10px 14px 14px;
        flex: 1 1 auto;
        min-height: 0;
        overflow-x: hidden;
        overflow-y: auto;
        align-items: start;
        align-content: start;
      }

      .kpv2-top-sites-panel .kpv2-ts-empty {
        grid-column: 1 / -1;
        padding: 28px 12px;
        text-align: center;
        font-size: 13px;
        font-weight: 600;
        color: ${c.fgMute};
      }

      .kpv2-top-sites-panel .kp-url-row {
        box-sizing: border-box;
        display: block;
        position: relative;
        align-self: start;
        width: 100%;
        height: 80px;
        min-height: 80px;
        max-height: 80px;
        padding: 8px 9px;
        border-radius: 10px;
        cursor: pointer;
        text-decoration: none;
        color: inherit;
        user-select: none;
        overflow: hidden;
        margin: 0;
        min-width: 0;
        background: linear-gradient(180deg, ${c.btnTop} 0%, ${c.btnMid} 48%, ${c.btnBot} 100%);
        border: 1px solid ${c.panelEdgeDark};
        box-shadow:
          inset 0 1px 0 rgba(255,255,255,0.18),
          inset 0 -1px 0 rgba(0,0,0,0.45),
          0 4px 10px rgba(0,0,0,0.38);
      }

      .kpv2-top-sites-panel .kp-url-row:hover {
        transform: translateY(-1px);
        box-shadow:
          inset 0 1px 0 rgba(255,255,255,0.22),
          inset 0 -1px 0 rgba(0,0,0,0.5),
          0 8px 16px rgba(0,0,0,0.45);
      }

      .kpv2-top-sites-panel .kp-url-row:focus-visible,
      .kpv2-top-sites-panel .kp-url-row--selected {
        outline: none;
        box-shadow:
          inset 0 1px 0 rgba(255,255,255,0.18),
          0 0 0 2px ${c.litEdge},
          0 6px 14px rgba(0,0,0,0.4);
      }

      .kpv2-top-sites-panel .kp-url-content {
        position: relative;
        z-index: 1;
        display: flex;
        flex-direction: column;
        height: 100%;
        min-height: 0;
        gap: 4px;
      }

      .kpv2-top-sites-panel .kp-url-favicon {
        width: 22px;
        height: 22px;
        border-radius: 4px;
        flex: 0 0 auto;
        background: rgba(0,0,0,0.28);
      }

      .kpv2-top-sites-panel .kp-url-text {
        min-width: 0;
        flex: 1 1 auto;
        display: flex;
        flex-direction: column;
        gap: 2px;
        justify-content: flex-end;
      }

      .kpv2-top-sites-panel .kp-url-domain,
      .kpv2-top-sites-panel .kp-url-title,
      .kpv2-top-sites-panel .kp-url-path {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      .kpv2-top-sites-panel .kp-url-domain {
        font-size: 12px;
        font-weight: 800;
        color: rgba(255,255,255,0.94);
      }

      .kpv2-top-sites-panel .kp-url-title {
        font-size: 11px;
        font-weight: 600;
        color: rgba(255,255,255,0.78);
      }

      .kpv2-top-sites-panel .kp-url-path {
        font-size: 10px;
        color: rgba(255,255,255,0.55);
      }

      .kpv2-top-sites-panel .kp-url-row.kp-has-page-thumb {
        background-color: #0a0a0a;
        background-image: none;
        isolation: isolate;
      }

      .kpv2-top-sites-panel .kp-url-row.kp-has-page-thumb::before {
        content: "";
        position: absolute;
        inset: -1px;
        z-index: 0;
        pointer-events: none;
        border-radius: inherit;
        background-color: #0a0a0a;
        background-image:
          linear-gradient(to bottom, rgba(0,0,0,0.42) 0%, rgba(0,0,0,0.78) 100%),
          var(--kp-page-thumb);
        background-size: 100% 100%, cover;
        background-position: center, center;
        background-repeat: no-repeat, no-repeat;
      }

      .kpv2-top-sites-panel .kp-url-row.kp-has-page-thumb > * {
        position: relative;
        z-index: 1;
      }

      .kpv2-top-sites-panel .kp-url-row.kp-has-page-thumb .kp-url-domain,
      .kpv2-top-sites-panel .kp-url-row.kp-has-page-thumb .kp-url-title,
      .kpv2-top-sites-panel .kp-url-row.kp-has-page-thumb .kp-url-path {
        text-shadow: 0 1px 2px rgba(0,0,0,0.85);
      }

      .kpv2-top-sites-panel .kp-hub-card .kp-hub-icon {
        background-color: rgba(255,255,255,0.94);
        background-image: none;
        -webkit-mask: var(--kp-hub-icon) center / contain no-repeat;
        mask: var(--kp-hub-icon) center / contain no-repeat;
      }

      .kpv2-top-sites-panel .kp-hub-card.is-on {
        box-shadow:
          inset 0 1px 0 rgba(255,255,255,0.18),
          0 0 0 2px ${c.litEdge},
          0 6px 14px rgba(0,0,0,0.4);
      }

      .kpv2-top-sites-panel .kp-hub-card.is-on .kp-url-title {
        color: #7dcea0;
      }

      .kpv2-top-sites-panel .kp-hub-card .kp-hub-switch {
        position: absolute;
        top: 8px;
        right: 8px;
        z-index: 2;
        pointer-events: none;
      }

      .kpv2-top-sites-panel .kp-hub-card .kp-hub-switch-track {
        display: block;
        box-sizing: border-box;
        width: 32px;
        height: 16px;
        padding: 2px;
        border-radius: 3px;
        border: 1px solid rgba(248, 113, 113, 0.4);
        background: rgba(248, 113, 113, 0.18);
        transition: background 140ms ease, border-color 140ms ease;
      }

      .kpv2-top-sites-panel .kp-hub-card .kp-hub-switch-thumb {
        display: block;
        width: 10px;
        height: 10px;
        border-radius: 2px;
        background: rgba(255, 255, 255, 0.88);
        box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
        transform: translateX(0);
        transition: transform 140ms ease, background 140ms ease;
      }

      .kpv2-top-sites-panel .kp-hub-card.is-on .kp-hub-switch-track,
      .kpv2-top-sites-panel .kp-hub-card[aria-checked="true"] .kp-hub-switch-track {
        border-color: rgba(16, 185, 129, 0.5);
        background: rgba(16, 185, 129, 0.28);
      }

      .kpv2-top-sites-panel .kp-hub-card.is-on .kp-hub-switch-thumb,
      .kpv2-top-sites-panel .kp-hub-card[aria-checked="true"] .kp-hub-switch-thumb {
        transform: translateX(16px);
        background: #ecfdf5;
      }

      ${getNctDarkUiScrollbarCss({ scopeSelector: ".kpv2-top-sites-panel" })}
    `;
      const shadowRoot = this._panel?.shadowRoot || null;
      const localCss = shadowRoot ? css.replace(".kpv2-top-sites-panel {", ":host {").replaceAll(".kpv2-top-sites-panel ", "") : css;
      injectChromeStyles(shadowRoot || document, {
        attr: "data-kp-top-sites-styles",
        css: localCss
      });
    }
    _ensureDom() {
      if (this._panel && this._tabList && this._grid) return;
      const doc = document;
      const panel = doc.createElement("div");
      panel.className = "kpv2-top-sites-panel";
      panel.setAttribute("role", "dialog");
      panel.setAttribute("aria-label", "Top Sites");
      panel.tabIndex = -1;
      const shadowRoot = ensureOpenChromeShadow(panel, { id: "top-sites", chromeWindow: true });
      const shell = shadowRoot || panel;
      this._gearBtn = this._createGearButton(doc);
      this._menu = this._createPersistMenu(doc);
      const titlebarApi = createPopoverTitlebar({
        doc,
        title: "Top Sites",
        shortcut: ";",
        icon: "window",
        variant: "panel",
        draggable: true,
        closeTitle: "Close",
        onClose: () => this.hide(),
        actions: [this._gearBtn],
        className: "kpv2-popover-titlebar kpv2-ts-titlebar"
      });
      this._titlebar = titlebarApi.titlebar;
      this._titlebarApi = titlebarApi;
      const tabstrip = doc.createElement("div");
      tabstrip.className = "kpv2-ts-tabstrip";
      tabstrip.setAttribute("role", "tablist");
      tabstrip.setAttribute("aria-label", "Top Sites and KeyPilot");
      for (const tab of TABS) {
        const btn = doc.createElement("button");
        btn.type = "button";
        btn.className = "kpv2-ts-tab";
        btn.id = `kp-ts-tab-${tab.id}`;
        btn.setAttribute("role", "tab");
        btn.dataset.tabId = tab.id;
        btn.textContent = tab.label;
        btn.addEventListener("click", (e) => {
          e.preventDefault();
          e.stopPropagation();
          void this._selectTab(tab.id);
        }, true);
        tabstrip.appendChild(btn);
      }
      const body = doc.createElement("div");
      body.className = "kpv2-ts-body";
      const status = doc.createElement("div");
      status.className = "kpv2-ts-status";
      status.textContent = "";
      const grid = createUrlListingContainer({
        doc,
        view: "grid",
        useInlineStyles: false,
        className: `kpv2-ts-grid ${NCT_DARK_UI_SCROLLBAR_CLASS}`,
        scrollY: true
      });
      grid.setAttribute("role", "tabpanel");
      grid.id = "kp-ts-panel";
      body.appendChild(status);
      body.appendChild(grid);
      shell.appendChild(this._titlebar);
      shell.appendChild(this._menu);
      shell.appendChild(tabstrip);
      shell.appendChild(body);
      this._panel = panel;
      this._tabList = tabstrip;
      this._grid = grid;
      this._status = status;
    }
    _syncTabButtons() {
      if (!this._tabList) return;
      const buttons = this._tabList.querySelectorAll(".kpv2-ts-tab");
      buttons.forEach((btn) => {
        const selected = btn.dataset.tabId === this._tab;
        btn.setAttribute("aria-selected", selected ? "true" : "false");
        btn.tabIndex = selected ? 0 : -1;
      });
      if (this._grid) {
        this._grid.setAttribute("aria-labelledby", `kp-ts-tab-${this._tab}`);
      }
    }
    /**
     * @param {TopSitesTabId} tabId
     */
    async _selectTab(tabId) {
      if (!TABS.some((t) => t.id === tabId)) return;
      this._tab = tabId;
      this._selectedIndex = 0;
      this._syncTabButtons();
      void this._persistTabState();
      await this._loadAndRender(this._openGen);
    }
    /** @param {number} dir */
    _cycleTab(dir) {
      const idx = TABS.findIndex((t) => t.id === this._tab);
      const next = TABS[(idx + dir + TABS.length) % TABS.length];
      if (next) void this._selectTab(next.id);
    }
    /**
     * @param {KeyboardEvent} e
     */
    _eventInTabList(e) {
      const t = e.target;
      if (!(t instanceof Element) || !this._tabList) return false;
      return this._tabList.contains(t) || t.classList?.contains?.("kpv2-ts-tab");
    }
    _gridColumnCount() {
      const grid = this._grid;
      if (!grid) return 4;
      try {
        const style = getComputedStyle(grid);
        const cols = style.gridTemplateColumns;
        if (cols && cols !== "none") return Math.max(1, cols.split(" ").length);
      } catch {
      }
      return 4;
    }
    /** @param {number} delta */
    _moveSelection(delta) {
      if (!this._items.length) return;
      this._setSelectedIndex(this._selectedIndex + delta);
    }
    /** @param {number} index */
    _setSelectedIndex(index) {
      if (!this._items.length) {
        this._selectedIndex = 0;
        return;
      }
      const max = this._items.length - 1;
      this._selectedIndex = Math.max(0, Math.min(max, index));
      this._paintSelection();
      const row = this._grid?.querySelector?.(
        `.kp-url-row[data-kp-url-listing-index="${this._selectedIndex}"]`
      );
      try {
        row?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
      } catch {
      }
    }
    _paintSelection() {
      if (!this._grid) return;
      const rows = this._grid.querySelectorAll(".kp-url-row");
      rows.forEach((row) => {
        const idx = Number(row.dataset.kpUrlListingIndex);
        row.classList.toggle("kp-url-row--selected", idx === this._selectedIndex);
      });
    }
    async _loadAndRender(gen) {
      if (!this._stillOpen(gen) || !this._grid || !this._status) return;
      this._status.textContent = "Loading\u2026";
      this._grid.textContent = "";
      this._items = [];
      if (this._tab === "keypilot") {
        this._renderHubCards();
        return;
      }
      let items = [];
      try {
        items = await this._fetchItems(this._tab);
      } catch {
        items = [];
      }
      if (!this._stillOpen(gen)) return;
      this._items = items;
      this._selectedIndex = 0;
      this._status.textContent = items.length ? `${items.length} site${items.length === 1 ? "" : "s"}` : "";
      this._renderGrid(items);
    }
    /**
     * @param {TopSitesTabId} tabId
     * @returns {Promise<Array<{title?: string, url?: string}>>}
     */
    async _fetchItems(tabId) {
      if (tabId === "keypilot") return [];
      if (tabId === "toolbar") {
        const response2 = await chrome.runtime.sendMessage({ type: MSG.GET_BOOKMARKS });
        const bookmarks = Array.isArray(response2?.bookmarks) ? response2.bookmarks : [];
        return this._usableSites(bookmarks.filter(
          (b) => !!(b?.url && (b.isToolbar === true || b.parentId === "1"))
        ));
      }
      if (tabId === "mostVisited") {
        const response2 = await chrome.runtime.sendMessage({ type: MSG.GET_MOST_VISITED });
        return this._usableSites(Array.isArray(response2?.sites) ? response2.sites : []);
      }
      const response = await chrome.runtime.sendMessage({
        type: MSG.GET_RECENT_BOOKMARKS,
        maxResults: 24
      });
      return this._usableSites(Array.isArray(response?.bookmarks) ? response.bookmarks : []);
    }
    /**
     * @param {Array<{title?: string, url?: string}>} items
     * @returns {Array<{title?: string, url?: string}>}
     */
    _usableSites(items) {
      return (Array.isArray(items) ? items : []).filter(
        (item) => /^https?:/i.test(String(item?.url || ""))
      );
    }
    /**
     * @param {Array<{title?: string, url?: string}>} items
     */
    _renderGrid(items) {
      if (!this._grid) return;
      const emptyText = EMPTY_COPY[this._tab] || "Nothing to show.";
      if (!items.length) {
        this._grid.textContent = "";
        const empty = (this._grid.ownerDocument || document).createElement("div");
        empty.className = "kpv2-ts-empty";
        empty.textContent = emptyText;
        this._grid.appendChild(empty);
        return;
      }
      renderUrlListing({
        container: this._grid,
        items,
        view: "grid",
        useInlineStyles: false,
        classNames: CARD_CLASS_NAMES,
        rowTag: "a",
        getTitle: (it) => it.title || it.url,
        getUrl: (it) => String(it.url || ""),
        showFavicon: true,
        showMetaLine: true,
        showUrlLine: true,
        selectedIndex: this._selectedIndex,
        onRowClick: ({ item, event }) => {
          event.preventDefault();
          event.stopPropagation();
          void this._openUrl(item.url);
        },
        decorateRow: ({ row, item, idx, parts }) => {
          row.setAttribute("role", "link");
          renderCardText({ item, parts });
          attachPageThumb(row, item.url);
          if (idx === this._selectedIndex) {
            row.classList.add("kp-url-row--selected");
          }
        }
      });
    }
    _renderHubCards() {
      if (!this._grid) return;
      const doc = this._grid.ownerDocument || document;
      this._grid.textContent = "";
      this._items = KEYPILOT_HUB_CARDS.map((def) => ({ ...def }));
      this._selectedIndex = 0;
      this._status.textContent = `${KEYPILOT_HUB_CARDS.length} tools`;
      KEYPILOT_HUB_CARDS.forEach((def, idx) => {
        const row = createKeypilotHubCard(doc, def);
        row.dataset.kpUrlListingIndex = String(idx);
        if (idx === this._selectedIndex) {
          row.classList.add("kp-url-row--selected");
        }
        row.addEventListener("click", (e) => {
          e.preventDefault();
          e.stopPropagation();
          this._setSelectedIndex(idx);
          void this._activateHubAction(def.action);
        });
        this._grid.appendChild(row);
      });
      void this._refreshHubStatus();
    }
    _refreshHubStatus() {
      return refreshKeypilotHubCards(this._grid);
    }
    /**
     * @param {import('../ui/keypilot-hub.js').KeypilotHubAction} action
     */
    async _activateHubAction(action) {
      if (action === "keyboard") {
        const card = this._grid?.querySelector?.('[data-action="keyboard"]');
        const next = card?.getAttribute("aria-checked") !== "true";
        try {
          await setKeyboardHelpVisible(next);
        } catch {
        }
        await this._refreshHubStatus();
        return;
      }
      if (action === "control-strip") {
        const card = this._grid?.querySelector?.('[data-action="control-strip"]');
        const next = card?.getAttribute("aria-checked") !== "true";
        try {
          await setControlStripVisible(next);
        } catch {
        }
        await this._refreshHubStatus();
        return;
      }
      const type2 = action === "settings" ? MSG.OPEN_SETTINGS_POPOVER : action === "docs" ? MSG.OPEN_DOCS_POPOVER : action === "tutorial" ? MSG.OPEN_ONBOARDING : null;
      if (!type2) return;
      this.hide({ persistClosed: !this._persistent });
      try {
        await chrome.runtime.sendMessage({ type: type2 });
      } catch {
      }
    }
    /**
     * @param {string} url
     */
    async _openUrl(url) {
      const href = String(url || "").trim();
      if (!href) return;
      this.hide({ persistClosed: !this._persistent });
      try {
        await chrome.runtime.sendMessage({ type: MSG.NAVIGATE_SAME_TAB, url: href });
      } catch {
        try {
          window.location.assign(href);
        } catch {
        }
      }
    }
  };

  // src/keypilot.js
  init_settings_manager();

  // src/utils/debug.js
  init_settings_manager();
  var consoleWrapped = false;
  var storageListenerInstalled = false;
  function isKeyPilotDebugEnabled() {
    try {
      return !!globalThis.KEYPILOT_DEBUG;
    } catch {
      return false;
    }
  }
  function applyDebugSetting(enabled) {
    try {
      globalThis.KEYPILOT_DEBUG = !!enabled;
    } catch {
    }
  }
  function installKeyPilotDebugConsole() {
    if (consoleWrapped) return;
    consoleWrapped = true;
    applyDebugSetting(!!globalThis.KEYPILOT_DEBUG);
    const origLog = console.log.bind(console);
    const origDebug = console.debug.bind(console);
    const origInfo = console.info.bind(console);
    console.log = (...args) => {
      if (isKeyPilotDebugEnabled()) origLog(...args);
    };
    console.debug = (...args) => {
      if (isKeyPilotDebugEnabled()) origDebug(...args);
    };
    console.info = (...args) => {
      if (isKeyPilotDebugEnabled()) origInfo(...args);
    };
  }
  function applyFromStoredSettings(raw) {
    applyDebugSetting(!!(raw && typeof raw === "object" && raw.debugLogging));
  }
  async function startKeyPilotDebugFromSettings() {
    installKeyPilotDebugConsole();
    try {
      const settings = await getSettings();
      applyDebugSetting(!!settings?.debugLogging);
    } catch {
      applyDebugSetting(false);
    }
    if (storageListenerInstalled) return;
    storageListenerInstalled = true;
    try {
      if (!chrome?.storage?.onChanged?.addListener) return;
      chrome.storage.onChanged.addListener((changes, area) => {
        if (area !== "sync" && area !== "local") return;
        const ch = changes?.[SETTINGS_STORAGE_KEY];
        if (!ch) return;
        applyFromStoredSettings(ch.newValue);
      });
    } catch {
    }
  }

  // src/modules/macro-key-runtime.js
  var _roundRobinIndex = /* @__PURE__ */ new Map();
  var _continuousTimers = /* @__PURE__ */ new Map();
  function describe(mk) {
    return `${mk.label || mk.kind}: ${summarizeMacroKey(mk)}`;
  }
  async function runLegacyMacroKeyFunction(instanceId, functionId, parameters, opts = {}) {
    const kind = macroKeyKindFromFunctionId(functionId);
    if (!kind) return { ok: false, message: `Unknown keystroke function: ${functionId}` };
    return runKeystrokeFunction(String(instanceId || functionId), kind, parameters?.config, opts);
  }
  async function runKeystrokeFunction(id, kind, rawConfig, opts) {
    const mk = { kind, config: rawConfig };
    const cfg = normalizeMacroKeyConfig(kind, rawConfig);
    let message = "";
    switch (kind) {
      case "hotkey":
      case "key": {
        message = `Macro Key (stub): would send ${formatKeyStroke(cfg.stroke)}`;
        break;
      }
      case "burst": {
        const seq = (cfg.steps || []).map(formatKeyStroke).join(" \u2192 ");
        message = `Macro Key (stub): would burst ${seq} (gap ${cfg.gapMs}ms)`;
        break;
      }
      case "roundRobin": {
        const items = Array.isArray(cfg.items) ? cfg.items : [];
        if (!items.length) {
          message = "Macro Key: round robin has no items.";
          break;
        }
        const prev = _roundRobinIndex.get(id) || 0;
        const idx = prev % items.length;
        _roundRobinIndex.set(id, idx + 1);
        message = `Macro Key (stub): round robin [${idx + 1}/${items.length}] \u2192 ${formatKeyStroke(items[idx])}`;
        break;
      }
      case "continuous": {
        if (_continuousTimers.has(id)) {
          try {
            clearInterval(_continuousTimers.get(id)?.timerId);
          } catch {
          }
          _continuousTimers.delete(id);
          message = `Macro Key: continuous STOPPED (${formatKeyStroke(cfg.stroke)})`;
        } else {
          const strokeLabel = formatKeyStroke(cfg.stroke);
          const intervalMs = Number(cfg.intervalMs) || 50;
          const timerId = setInterval(() => {
          }, intervalMs);
          _continuousTimers.set(id, { timerId, strokeLabel });
          message = `Macro Key (stub): continuous STARTED ${strokeLabel} every ${intervalMs}ms \u2014 press again to stop`;
        }
        break;
      }
      case "mouse": {
        message = `Macro Key (stub): would ${cfg.button}-click under cursor`;
        break;
      }
      default:
        message = `Macro Key (stub): ${describe(mk)}`;
    }
    try {
      opts.notify?.(message, "info");
    } catch {
    }
    return { ok: true, message };
  }

  // src/modules/execute-js-runtime.js
  var EXECUTE_JS_TIMEOUT_MS = 8e3;
  var EXECUTE_JS_BINDING_NAMES = Object.freeze([
    "kpHoveredClickable",
    "kpHoverLeaf",
    "kpFocusedTextField",
    "kpMode",
    "kpPageUrl",
    "kpSelection",
    "kpPriorResult",
    "showPopover",
    "copyToClipboard",
    "notify"
  ]);
  async function runUserExecuteJs(source, bindings, timeoutMs = EXECUTE_JS_TIMEOUT_MS) {
    const code = String(source || "");
    const names = EXECUTE_JS_BINDING_NAMES.slice();
    const values = names.map((name) => bindings && Object.prototype.hasOwnProperty.call(bindings, name) ? bindings[name] : void 0);
    let fn;
    try {
      const AsyncFunction = Object.getPrototypeOf(async function() {
      }).constructor;
      fn = new AsyncFunction(...names, code);
    } catch (err) {
      const msg = err && err.message ? String(err.message) : String(err || "syntax error");
      throw new Error(`Syntax error: ${msg}`);
    }
    const run = Promise.resolve().then(() => fn(...values));
    const ms = Number(timeoutMs);
    const limit = Number.isFinite(ms) && ms > 0 ? ms : EXECUTE_JS_TIMEOUT_MS;
    let timer = 0;
    const timeout = new Promise((_, reject) => {
      timer = setTimeout(() => reject(new Error("Execute JS timed out")), limit);
    });
    try {
      return await Promise.race([run, timeout]);
    } finally {
      try {
        clearTimeout(timer);
      } catch {
      }
    }
  }
  function stringifyExecuteJsValue(value) {
    if (value == null) return "";
    if (typeof value === "string") return value;
    if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
      return String(value);
    }
    try {
      return JSON.stringify(value, null, 2);
    } catch {
      try {
        return String(value);
      } catch {
        return "";
      }
    }
  }

  // src/utils/text-at-point.js
  var BLOCK_SELECTOR = "p, li, blockquote, td, th, dd, dt, figcaption, article, section, h1, h2, h3, h4, h5, h6, pre, div";
  function caretRangeAtPoint(x, y, doc) {
    try {
      if (typeof doc.caretRangeFromPoint === "function") {
        return doc.caretRangeFromPoint(x, y);
      }
    } catch {
    }
    try {
      if (typeof doc.caretPositionFromPoint === "function") {
        const pos = doc.caretPositionFromPoint(x, y);
        if (pos && pos.offsetNode) {
          const r = doc.createRange();
          r.setStart(pos.offsetNode, pos.offset);
          r.collapse(true);
          return r;
        }
      }
    } catch {
    }
    return null;
  }
  function wordSegmentAt(text, offset) {
    try {
      if (typeof Intl === "undefined" || typeof Intl.Segmenter !== "function") return null;
      const segments = [...new Intl.Segmenter(void 0, { granularity: "word" }).segment(text)];
      let idx = segments.findIndex((s) => offset >= s.index && offset < s.index + s.segment.length);
      if (idx === -1) idx = segments.length - 1;
      if (idx < 0) return null;
      if (!segments[idx].isWordLike) {
        const before = [...segments.slice(0, idx)].reverse().find((s) => s.isWordLike);
        const after = segments.slice(idx + 1).find((s) => s.isWordLike);
        const chosen = after || before;
        return chosen ? { segment: chosen.segment, index: chosen.index } : null;
      }
      return { segment: segments[idx].segment, index: segments[idx].index };
    } catch {
      return null;
    }
  }
  function sentenceSegmentAt(text, offset) {
    try {
      if (typeof Intl === "undefined" || typeof Intl.Segmenter !== "function") return null;
      const segments = [...new Intl.Segmenter(void 0, { granularity: "sentence" }).segment(text)];
      let found = segments.find((s) => offset >= s.index && offset < s.index + s.segment.length);
      if (!found && segments.length) {
        found = segments[segments.length - 1];
      }
      if (!found) return null;
      return { segment: found.segment, index: found.index };
    } catch {
    }
    return null;
  }
  function hyperlinkAtPoint(x, y, doc) {
    try {
      const el2 = doc.elementFromPoint(x, y);
      const a = el2?.closest?.("a[href]");
      return a ? String(a.href || "") : "";
    } catch {
      return "";
    }
  }
  function elementFromNode(node) {
    if (!node) return null;
    if (node.nodeType === Node.ELEMENT_NODE) return (
      /** @type {Element} */
      node
    );
    return node.parentElement || null;
  }
  function closestBlockAtPoint(x, y, doc) {
    try {
      const range = caretRangeAtPoint(x, y, doc);
      const fromCaret = elementFromNode(range?.startContainer);
      let hit = fromCaret;
      if (!hit) {
        try {
          hit = doc.elementFromPoint(x, y);
        } catch {
          hit = null;
        }
      }
      if (!hit || hit.nodeType !== 1) return null;
      return hit.closest?.(BLOCK_SELECTOR) || hit;
    } catch {
      return null;
    }
  }
  function textPiecesIn(root) {
    const pieces = [];
    let text = "";
    if (!root) return { text, pieces };
    const doc = root.ownerDocument || document;
    let walker;
    try {
      walker = doc.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
        acceptNode(n2) {
          if (!n2 || !n2.textContent) return NodeFilter.FILTER_REJECT;
          const p = n2.parentElement;
          if (p && /^(SCRIPT|STYLE|NOSCRIPT|TEXTAREA)$/i.test(p.tagName)) return NodeFilter.FILTER_REJECT;
          return NodeFilter.FILTER_ACCEPT;
        }
      });
    } catch {
      return { text, pieces };
    }
    let n = walker.nextNode();
    while (n) {
      const start = text.length;
      const chunk = String(n.textContent || "");
      text += chunk;
      pieces.push({ node: (
        /** @type {Text} */
        n
      ), start, end: text.length });
      n = walker.nextNode();
    }
    return { text, pieces };
  }
  function rangeFromTextOffsets(doc, pieces, start, end) {
    if (!pieces.length || end <= start) return null;
    let startNode = null;
    let startOff = 0;
    let endNode = null;
    let endOff = 0;
    for (const p of pieces) {
      if (!startNode && start >= p.start && start < p.end) {
        startNode = p.node;
        startOff = start - p.start;
      }
      if (start === p.end && !startNode) {
        startNode = p.node;
        startOff = p.node.textContent ? p.node.textContent.length : 0;
      }
      if (end > p.start && end <= p.end) {
        endNode = p.node;
        endOff = end - p.start;
      }
    }
    if (!startNode && pieces[0]) {
      startNode = pieces[0].node;
      startOff = 0;
    }
    if (!endNode && pieces.length) {
      const last = pieces[pieces.length - 1];
      endNode = last.node;
      endOff = last.node.textContent ? last.node.textContent.length : 0;
    }
    if (!startNode || !endNode) return null;
    try {
      const range = doc.createRange();
      range.setStart(startNode, Math.max(0, startOff));
      range.setEnd(endNode, Math.max(0, endOff));
      return range;
    } catch {
      return null;
    }
  }
  function caretOffsetInPieces(caretRange, pieces) {
    const node = caretRange?.startContainer;
    const off = Number(caretRange?.startOffset) || 0;
    if (node && node.nodeType === Node.TEXT_NODE) {
      const p = pieces.find((x) => x.node === node);
      if (p) return p.start + Math.min(Math.max(0, off), p.end - p.start);
    }
    if (pieces.length) return pieces[0].start;
    return 0;
  }
  function paragraphAtPoint(x, y, doc) {
    try {
      const block = closestBlockAtPoint(x, y, doc);
      if (!block) return { text: "", range: null };
      const text = String(block.innerText ?? block.textContent ?? "").trim();
      if (!text) return { text: "", range: null };
      let range = null;
      try {
        range = doc.createRange();
        range.selectNodeContents(block);
      } catch {
        range = null;
      }
      return { text, range };
    } catch {
      return { text: "", range: null };
    }
  }
  function sentenceAtPoint(x, y, doc) {
    const block = closestBlockAtPoint(x, y, doc);
    if (!block) return { text: "", range: null };
    const { text, pieces } = textPiecesIn(block);
    if (!text.trim() || !pieces.length) return { text: "", range: null };
    const caret = caretRangeAtPoint(x, y, doc);
    const offset = caret ? caretOffsetInPieces(caret, pieces) : 0;
    const found = sentenceSegmentAt(text, offset);
    if (!found || !String(found.segment || "").trim()) return { text: "", range: null };
    const end = Math.min(found.index + found.segment.length, text.length);
    const range = rangeFromTextOffsets(doc, pieces, found.index, end);
    return { text: found.segment.trim(), range };
  }
  function getTextAtPoint(x, y, opts = {}) {
    const granularity = opts.granularity || "word";
    const doc = opts.doc || document;
    if (granularity === "hyperlink") {
      return { text: hyperlinkAtPoint(x, y, doc), range: null };
    }
    if (granularity === "paragraph") {
      return paragraphAtPoint(x, y, doc);
    }
    if (granularity === "sentence") {
      return sentenceAtPoint(x, y, doc);
    }
    const caretRange = caretRangeAtPoint(x, y, doc);
    const node = caretRange?.startContainer;
    if (!node || node.nodeType !== Node.TEXT_NODE) return { text: "", range: null };
    const full = String(node.textContent || "");
    const offset = Math.min(Math.max(0, caretRange.startOffset), full.length);
    const found = wordSegmentAt(full, offset);
    if (!found || !found.segment.trim()) return { text: "", range: null };
    let range = null;
    try {
      range = doc.createRange();
      range.setStart(node, found.index);
      range.setEnd(node, Math.min(found.index + found.segment.length, full.length));
    } catch {
      range = null;
    }
    return { text: found.segment.trim(), range };
  }

  // src/modules/unit-selection-manager.js
  init_constants();
  var UNIT_SELECT_HIGHLIGHT_NAME = "kp-unit-select";
  function rangeKey(range) {
    try {
      const s = range.startContainer;
      const e = range.endContainer;
      return `r:${s === e ? "1" : "2"}:${nodeToken(s)}:${range.startOffset}:${nodeToken(e)}:${range.endOffset}`;
    } catch {
      return `r:${Math.random()}`;
    }
  }
  function nodeToken(node) {
    if (!node) return "";
    if (node.nodeType === Node.TEXT_NODE) {
      const parent = node.parentElement;
      const idx = parent ? Array.prototype.indexOf.call(parent.childNodes, node) : -1;
      return `t:${parent ? parent.tagName : ""}:${idx}:${String(node.textContent || "").length}`;
    }
    if (node.nodeType === Node.ELEMENT_NODE) {
      const el2 = (
        /** @type {Element} */
        node
      );
      return `e:${el2.tagName}:${el2.id || ""}:${el2.className || ""}`;
    }
    return String(node.nodeType);
  }
  function elementKey(el2) {
    try {
      return `e:${el2.tagName}:${el2.id || ""}:${el2.src || el2.currentSrc || ""}`;
    } catch {
      return `e:${Math.random()}`;
    }
  }
  function rangeIsLive(range) {
    if (!range) return false;
    try {
      const n = range.commonAncestorContainer;
      return !!(n && (n.nodeType === Node.DOCUMENT_NODE || n.isConnected || n.parentNode));
    } catch {
      return false;
    }
  }
  function rangePlainText(range) {
    try {
      return String(range.toString() || "").replace(/\s+\n/g, "\n").trim();
    } catch {
      return "";
    }
  }
  function rangeHtml(range) {
    try {
      const doc = range.startContainer?.ownerDocument || document;
      const div = doc.createElement("div");
      div.appendChild(range.cloneContents());
      return div.innerHTML || "";
    } catch {
      return "";
    }
  }
  function highlightsApi(doc) {
    try {
      const win = doc?.defaultView || window;
      return win && typeof win.Highlight === "function" && win.CSS && win.CSS.highlights ? win : null;
    } catch {
      return null;
    }
  }
  var UnitSelectionManager = class {
    constructor() {
      this.items = [];
      this.overlays = [];
      this._highlightDocs = /* @__PURE__ */ new Set();
    }
    /** @returns {boolean} */
    hasItems() {
      this._prune();
      return this.items.length > 0;
    }
    /** @returns {UnitSelectItem[]} */
    getItems() {
      this._prune();
      return this.items.slice();
    }
    /**
     * @param {UnitSelectItem} unit
     * @param {'exclusive'|'cumulative'} mode
     * @returns {{ added: boolean, removed: boolean, count: number }}
     */
    toggle(unit, mode) {
      this._prune();
      if (!unit || !unit.key) return { added: false, removed: false, count: this.items.length };
      const idx = this.items.findIndex((it) => it.key === unit.key);
      const exclusive = mode !== "cumulative";
      if (exclusive) {
        if (idx >= 0 && this.items.length === 1) {
          this.items = [];
          this.paint();
          return { added: false, removed: true, count: 0 };
        }
        this.items = [unit];
        this.paint();
        return { added: true, removed: false, count: 1 };
      }
      if (idx >= 0) {
        this.items.splice(idx, 1);
        this.paint();
        return { added: false, removed: true, count: this.items.length };
      }
      this.items.push(unit);
      this.paint();
      return { added: true, removed: false, count: this.items.length };
    }
    clear() {
      this.items = [];
      this.paint();
    }
    /** Drop detached ranges / elements. */
    _prune() {
      this.items = this.items.filter((it) => {
        if (it.kind === "image") {
          return !!(it.element && it.element.isConnected);
        }
        return rangeIsLive(it.range);
      });
    }
    /**
     * @returns {{ plainText: string, htmlContent: string, hasRichContent: boolean }}
     */
    getClipboardContent() {
      this._prune();
      const plainParts = [];
      const htmlParts = [];
      const ordered = this.items.slice().sort((a, b) => {
        try {
          if (a.range && b.range) {
            const pos = a.range.compareBoundaryPoints(Range.START_TO_START, b.range);
            return pos;
          }
        } catch {
        }
        return 0;
      });
      for (const it of ordered) {
        if (it.kind === "image" && it.element) {
          const el2 = it.element;
          const tag = String(el2.tagName || "").toUpperCase();
          const alt = (el2.getAttribute?.("alt") || "").trim();
          const src = it.url || el2.currentSrc || el2.src || el2.getAttribute?.("src") || "";
          plainParts.push(alt || src || tag);
          try {
            htmlParts.push(el2.outerHTML || (src ? `<img src="${escapeAttr(src)}">` : ""));
          } catch {
            if (src) htmlParts.push(`<img src="${escapeAttr(src)}">`);
          }
          continue;
        }
        if (it.range) {
          const t = rangePlainText(it.range);
          if (t) plainParts.push(t);
          const h = rangeHtml(it.range);
          if (h) htmlParts.push(h);
        }
      }
      const plainText = plainParts.filter(Boolean).join("\n\n");
      const htmlContent = htmlParts.filter(Boolean).join("");
      return {
        plainText,
        htmlContent,
        hasRichContent: !!htmlContent && htmlContent !== plainText
      };
    }
    /** @returns {string} */
    getPlainText() {
      return this.getClipboardContent().plainText;
    }
    /**
     * True when every live item is an image (Copy should use the image clipboard path).
     * @returns {boolean}
     */
    isImagesOnly() {
      this._prune();
      return this.items.length > 0 && this.items.every((it) => it.kind === "image");
    }
    /** @returns {UnitSelectItem[]} */
    getImageItems() {
      this._prune();
      return this.items.filter((it) => it.kind === "image" && it.element);
    }
    paint() {
      this._prune();
      this._clearOverlays();
      this._clearHighlights();
      const textRanges = [];
      const overlayTargets = [];
      for (const it of this.items) {
        if (it.kind === "image" && it.element) {
          overlayTargets.push(it.element);
          continue;
        }
        if (it.range) textRanges.push(it.range);
      }
      const byDoc = /* @__PURE__ */ new Map();
      for (const range of textRanges) {
        const doc = range.startContainer?.ownerDocument || document;
        if (!byDoc.has(doc)) byDoc.set(doc, []);
        byDoc.get(doc).push(range);
      }
      for (const [doc, ranges] of byDoc) {
        const win = highlightsApi(doc);
        if (win) {
          try {
            win.CSS.highlights.set(UNIT_SELECT_HIGHLIGHT_NAME, new win.Highlight(...ranges));
            this._highlightDocs.add(doc);
            continue;
          } catch {
          }
        }
        for (const range of ranges) overlayTargets.push(range);
      }
      this._paintOverlayTargets(overlayTargets);
    }
    /** Reposition overlay boxes after scroll (CSS highlights follow layout automatically). */
    refreshOverlays() {
      if (!this.items.length) return;
      this._prune();
      if (!this.overlays.length && !this.items.some((it) => it.kind === "image")) return;
      this.paint();
    }
    _clearHighlights() {
      for (const doc of this._highlightDocs) {
        try {
          const win = highlightsApi(doc);
          win?.CSS?.highlights?.delete?.(UNIT_SELECT_HIGHLIGHT_NAME);
        } catch {
        }
      }
      this._highlightDocs.clear();
    }
    _clearOverlays() {
      for (const el2 of this.overlays) {
        try {
          el2.remove();
        } catch {
        }
      }
      this.overlays = [];
    }
    /**
     * @param {Array<Element|Range>} targets
     */
    _paintOverlayTargets(targets) {
      const seen = /* @__PURE__ */ new Set();
      for (const target of targets) {
        let rects;
        try {
          if (target && target.nodeType === 1) {
            rects = [
              /** @type {Element} */
              target.getBoundingClientRect()
            ];
          } else if (target && typeof target.getClientRects === "function") {
            rects = Array.from(target.getClientRects());
          } else {
            continue;
          }
        } catch {
          continue;
        }
        for (const rect of rects) {
          if (!rect || rect.width <= 0 || rect.height <= 0) continue;
          const sig = `${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`;
          if (seen.has(sig)) continue;
          seen.add(sig);
          const overlay = document.createElement("div");
          overlay.className = CSS_CLASSES.UNIT_SELECT_OVERLAY;
          overlay.style.cssText = `
          position: fixed;
          left: ${rect.left}px;
          top: ${rect.top}px;
          width: ${rect.width}px;
          height: ${rect.height}px;
          background: ${COLORS.HIGHLIGHT_SELECTION_BG};
          border: 1px solid ${COLORS.HIGHLIGHT_SELECTION_BORDER};
          pointer-events: none;
          z-index: ${Z_INDEX.HIGHLIGHT_SELECTION};
          box-sizing: border-box;
        `;
          try {
            document.body.appendChild(overlay);
            this.overlays.push(overlay);
          } catch {
          }
        }
      }
    }
  };
  function unitFromTextRange(kind, range, text) {
    if (!range || !rangeIsLive(range)) return null;
    const t = (text != null ? String(text) : rangePlainText(range)).trim();
    if (!t) return null;
    return {
      kind: (
        /** @type {UnitSelectKind} */
        kind
      ),
      key: `${kind}:${rangeKey(range)}`,
      range
    };
  }
  function unitFromImageElement(element, url) {
    if (!element || !element.isConnected) return null;
    return {
      kind: "image",
      key: `image:${elementKey(element)}`,
      element,
      url: url || null
    };
  }
  function escapeAttr(s) {
    return String(s).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
  }

  // src/utils/font-at-point.js
  var GENERIC_FAMILIES = /* @__PURE__ */ new Set([
    "serif",
    "sans-serif",
    "monospace",
    "cursive",
    "fantasy",
    "system-ui",
    "ui-serif",
    "ui-sans-serif",
    "ui-monospace",
    "ui-rounded",
    "emoji",
    "math",
    "fangsong",
    "kai",
    "inherit",
    "initial",
    "unset",
    "revert",
    "revert-layer"
  ]);
  var INLINE_DISPLAY = /* @__PURE__ */ new Set([
    "inline",
    "inline-block",
    "inline-flex",
    "inline-grid",
    "contents",
    "ruby",
    "ruby-base",
    "ruby-text",
    "inline-table"
  ]);
  function unquoteFamily(value) {
    return String(value || "").trim().replace(/^['"]+|['"]+$/g, "").trim();
  }
  function parseFontFamilyStack(fontFamily) {
    const out = [];
    const re = /(?:^|,)\s*("[^"]*"|'[^']*'|[^,]+)/g;
    let m;
    const src = String(fontFamily || "");
    while (m = re.exec(src)) {
      const name = unquoteFamily(m[1]);
      if (name) out.push(name);
    }
    return out;
  }
  function fontRunKey(el2) {
    if (!el2 || el2.nodeType !== 1) return "";
    try {
      const cs = el2.ownerDocument.defaultView.getComputedStyle(el2);
      return [
        cs.fontFamily,
        cs.fontSize,
        cs.fontWeight,
        cs.fontStyle,
        cs.fontStretch || ""
      ].join("");
    } catch {
      return "";
    }
  }
  function isInlineLevel(el2) {
    if (!el2 || el2.nodeType !== 1) return false;
    try {
      const d = el2.ownerDocument.defaultView.getComputedStyle(el2).display;
      return INLINE_DISPLAY.has(String(d || "").toLowerCase());
    } catch {
      return false;
    }
  }
  function nearestBlockAncestor(node) {
    let el2 = node?.nodeType === Node.ELEMENT_NODE ? node : node?.parentElement;
    while (el2) {
      if (!isInlineLevel(el2)) return el2;
      el2 = el2.parentElement;
    }
    return node?.ownerDocument?.body || null;
  }
  function resolveTextCaret(x, y, doc) {
    const caret = caretRangeAtPoint(x, y, doc);
    if (!caret?.startContainer) return null;
    const node = caret.startContainer;
    if (node.nodeType === Node.TEXT_NODE) {
      return { textNode: node, offset: caret.startOffset };
    }
    if (node.nodeType === Node.ELEMENT_NODE && node.childNodes?.length) {
      const child = node.childNodes[Math.min(caret.startOffset, node.childNodes.length - 1)];
      if (child?.nodeType === Node.TEXT_NODE) {
        return { textNode: child, offset: 0 };
      }
    }
    return null;
  }
  function expandSameFontRun(origin, key) {
    const fallback = {
      startNode: origin,
      startOffset: 0,
      endNode: origin,
      endOffset: String(origin.textContent || "").length
    };
    const block = nearestBlockAncestor(origin);
    const doc = origin.ownerDocument;
    const nodes = [];
    try {
      const walker = doc.createTreeWalker(block || doc.body, NodeFilter.SHOW_TEXT);
      let n = walker.nextNode();
      while (n) {
        nodes.push(n);
        n = walker.nextNode();
      }
    } catch {
      return fallback;
    }
    const idx = nodes.indexOf(origin);
    if (idx < 0) return fallback;
    let start = idx;
    let end = idx;
    while (start > 0) {
      const prev = nodes[start - 1];
      const el2 = prev.parentElement;
      if (!el2 || fontRunKey(el2) !== key) break;
      start -= 1;
    }
    while (end < nodes.length - 1) {
      const next = nodes[end + 1];
      const el2 = next.parentElement;
      if (!el2 || fontRunKey(el2) !== key) break;
      end += 1;
    }
    const startNode = nodes[start];
    const endNode = nodes[end];
    return {
      startNode,
      startOffset: 0,
      endNode,
      endOffset: String(endNode.textContent || "").length
    };
  }
  function parseFontSrc(src, baseHref) {
    const urls = [];
    const urlRe = /url\(\s*(['"]?)([^'")]+)\1\s*\)(?:\s*format\(\s*(['"]?)([^'")]+)\3\s*\))?/gi;
    let m;
    while (m = urlRe.exec(String(src || ""))) {
      let abs = m[2];
      try {
        abs = new URL(m[2], baseHref || void 0).href;
      } catch {
      }
      urls.push({ url: abs, format: String(m[4] || "").trim().toLowerCase() });
    }
    const local = /local\(/i.test(String(src || ""));
    return { urls, local };
  }
  function fontFileTypeLabel(format, url) {
    const blob = `${format || ""} ${url || ""}`.toLowerCase();
    if (/\.woff2\b/.test(blob) || blob.includes("woff2")) return ".woff2";
    if (/\.woff\b/.test(blob) || /\bwoff\b/.test(blob)) return ".woff";
    if (/\.otf\b/.test(blob) || blob.includes("opentype")) return ".otf";
    if (/\.ttf\b/.test(blob) || blob.includes("truetype")) return ".ttf";
    if (/\.eot\b/.test(blob) || blob.includes("embedded-opentype")) return ".eot";
    if (/\.svg\b/.test(blob) && blob.includes("font")) return ".svg";
    return "";
  }
  function collectFontFaceRules(doc) {
    const out = [];
    const sheets = [];
    try {
      sheets.push(...doc.styleSheets);
    } catch {
    }
    try {
      if (doc.adoptedStyleSheets?.length) sheets.push(...doc.adoptedStyleSheets);
    } catch {
    }
    for (const sheet of sheets) {
      let rules;
      try {
        rules = sheet.cssRules;
      } catch {
        continue;
      }
      if (!rules) continue;
      const base = sheet.href || doc.baseURI || void 0;
      for (const rule of rules) {
        try {
          const isFace = typeof CSSRule !== "undefined" && rule.type === CSSRule.FONT_FACE_RULE || String(rule.constructor?.name || "") === "CSSFontFaceRule";
          if (!isFace) continue;
        } catch {
          continue;
        }
        const family = unquoteFamily(rule.style.getPropertyValue("font-family"));
        if (!family) continue;
        const src = rule.style.getPropertyValue("src");
        const parsed = parseFontSrc(src, base);
        out.push({
          family,
          weight: rule.style.getPropertyValue("font-weight") || "normal",
          style: rule.style.getPropertyValue("font-style") || "normal",
          ...parsed
        });
      }
    }
    return out;
  }
  function detectUsedFamily(sample, cs, families) {
    const size = cs.fontSize || "16px";
    const weight = cs.fontWeight || "400";
    const style = cs.fontStyle || "normal";
    const stretch = cs.fontStretch || "normal";
    const glyph = sample && sample.trim() ? sample.slice(0, 8) : "HmgWil";
    try {
      for (const family of families) {
        if (GENERIC_FAMILIES.has(family.toLowerCase())) continue;
        const spec = `${style} ${weight} ${stretch} ${size} "${family}"`;
        if (typeof document.fonts?.check === "function" && document.fonts.check(spec, glyph)) {
          return family;
        }
      }
    } catch {
    }
    try {
      const canvas = document.createElement("canvas");
      const ctx = canvas.getContext("2d");
      if (!ctx) return families[0] || "";
      const generics = ["monospace", "serif", "sans-serif"];
      const widths = {};
      for (const g of generics) {
        ctx.font = `${style} ${weight} ${size} ${g}`;
        widths[g] = ctx.measureText(glyph).width;
      }
      for (const family of families) {
        if (GENERIC_FAMILIES.has(family.toLowerCase())) continue;
        let distinct = false;
        for (const g of generics) {
          ctx.font = `${style} ${weight} ${size} "${family}", ${g}`;
          if (Math.abs(ctx.measureText(glyph).width - widths[g]) > 0.5) {
            distinct = true;
            break;
          }
        }
        if (distinct) return family;
      }
    } catch {
    }
    return families.find((f) => !GENERIC_FAMILIES.has(f.toLowerCase())) || families[0] || "";
  }
  function normalizeWeight(w) {
    const s = String(w || "normal").trim().toLowerCase();
    if (s === "normal") return "400";
    if (s === "bold") return "700";
    return s;
  }
  function resolveFontFile(family, cs, doc) {
    const want = String(family || "").toLowerCase();
    const faces = collectFontFaceRules(doc).filter(
      (f) => unquoteFamily(f.family).toLowerCase() === want
    );
    const fw = normalizeWeight(cs.fontWeight);
    const st = String(cs.fontStyle || "normal").toLowerCase();
    const ranked = faces.map((f) => {
      let score = 0;
      const ffw = normalizeWeight(f.weight);
      if (ffw === fw || ffw.includes(" ") && ffw.includes(fw)) score += 2;
      if (String(f.style || "normal").toLowerCase() === st) score += 2;
      if (f.urls.length) score += 1;
      return { f, score };
    }).sort((a, b) => b.score - a.score);
    const pick = ranked[0]?.f || null;
    if (pick?.urls?.length) {
      const first = pick.urls[0];
      return {
        fileType: fontFileTypeLabel(first.format, first.url) || ".woff2",
        resourceUrl: first.url,
        sourceKind: "webfont"
      };
    }
    if (pick?.local) {
      return { fileType: "local", resourceUrl: "", sourceKind: "local" };
    }
    try {
      const entries = typeof performance?.getEntriesByType === "function" ? performance.getEntriesByType("resource") : [];
      const needle = family.replace(/\s+/g, "").toLowerCase();
      for (const e of entries) {
        const name = String(e?.name || "");
        const low = name.toLowerCase();
        if (!/\.(woff2?|ttf|otf|eot)(\?|#|$)/i.test(low)) continue;
        if (needle && low.replace(/[^a-z0-9]/g, "").includes(needle.replace(/[^a-z0-9]/g, ""))) {
          return {
            fileType: fontFileTypeLabel("", name),
            resourceUrl: name,
            sourceKind: "webfont"
          };
        }
      }
    } catch {
    }
    return { fileType: "local", resourceUrl: "", sourceKind: "local" };
  }
  function inspectFontAtPoint(x, y, opts = {}) {
    const doc = opts.doc || document;
    const caret = resolveTextCaret(x, y, doc);
    if (!caret?.textNode) return null;
    const el2 = caret.textNode.parentElement;
    if (!el2) return null;
    const key = fontRunKey(el2);
    const run = expandSameFontRun(caret.textNode, key);
    let range = null;
    try {
      range = doc.createRange();
      range.setStart(run.startNode, run.startOffset);
      range.setEnd(run.endNode, run.endOffset);
    } catch {
      return null;
    }
    if (!range || range.collapsed) return null;
    let cs;
    try {
      cs = el2.ownerDocument.defaultView.getComputedStyle(el2);
    } catch {
      return null;
    }
    const familyStack = String(cs.fontFamily || "");
    const families = parseFontFamilyStack(familyStack);
    const sample = String(caret.textNode.textContent || "").slice(
      Math.max(0, caret.offset - 1),
      Math.min(String(caret.textNode.textContent || "").length, caret.offset + 8)
    );
    const usedFamily = detectUsedFamily(sample, cs, families);
    const file = resolveFontFile(usedFamily || families[0] || "", cs, doc);
    let specifiedSize = "";
    try {
      specifiedSize = String(el2.style?.fontSize || "").trim();
    } catch {
    }
    return {
      range,
      element: el2,
      sampleText: String(range.toString() || "").replace(/\s+/g, " ").trim().slice(0, 80),
      familyStack,
      usedFamily: usedFamily || families[0] || "",
      size: String(cs.fontSize || ""),
      specifiedSize,
      weight: String(cs.fontWeight || ""),
      style: String(cs.fontStyle || ""),
      stretch: String(cs.fontStretch || ""),
      fileType: file.fileType,
      resourceUrl: file.resourceUrl,
      sourceKind: file.sourceKind
    };
  }
  var SKIP_FONT_SCAN_TAGS = /* @__PURE__ */ new Set([
    "SCRIPT",
    "STYLE",
    "LINK",
    "META",
    "NOSCRIPT",
    "BR",
    "HR",
    "SOURCE",
    "TRACK",
    "PARAM",
    "HEAD",
    "TITLE",
    "TEMPLATE"
  ]);
  function isKpChromeEl(el2) {
    if (!el2 || el2.nodeType !== 1) return false;
    try {
      const id = typeof el2.id === "string" ? el2.id : "";
      if (id && (id.startsWith("kpv2-") || id.startsWith("kp-"))) return true;
    } catch {
    }
    try {
      const cls = typeof el2.className === "string" ? el2.className : el2.className && typeof el2.className.baseVal === "string" ? el2.className.baseVal : "";
      if (cls && (/\bkpv2-/.test(cls) || /\bkp-/.test(cls))) return true;
    } catch {
    }
    return false;
  }
  function isBrowserInternalFontUrl(url) {
    return /^(chrome|chrome-extension|chrome-untrusted):/i.test(String(url || "").trim());
  }
  function customFileRank(ext) {
    const e = String(ext || "").replace(/^\./, "").toLowerCase();
    if (e === "woff2") return 0;
    if (e === "otf") return 1;
    if (e === "ttf") return 2;
    if (e === "woff") return 3;
    if (e === "eot") return 4;
    if (e && e !== "local" && e !== "font") return 5;
    return 20;
  }
  function collectPageFonts(root = document) {
    const doc = (
      /** @type {Document} */
      root && /** @type {any} */
      root.nodeType === 9 ? root : (
        /** @type {Element} */
        root?.ownerDocument || document
      )
    );
    const scanRoot = root && /** @type {any} */
    root.nodeType === 1 ? root : doc.body || doc.documentElement;
    const used = /* @__PURE__ */ new Map();
    const MAX = 4e3;
    let n = 0;
    try {
      const list = scanRoot?.querySelectorAll?.("*") || [];
      for (let i = 0; i < list.length && n < MAX; i++) {
        const el2 = list[i];
        if (!el2 || el2.nodeType !== 1) continue;
        const tag = String(el2.tagName || "").toUpperCase();
        if (SKIP_FONT_SCAN_TAGS.has(tag)) continue;
        if (isKpChromeEl(el2)) continue;
        n += 1;
        let cs;
        try {
          cs = doc.defaultView.getComputedStyle(el2);
        } catch {
          continue;
        }
        if (!cs) continue;
        if (cs.display === "none" || cs.visibility === "hidden") continue;
        const families = parseFontFamilyStack(cs.fontFamily);
        if (!families.length) continue;
        const family = detectUsedFamily("Hg", cs, families) || families[0];
        if (!family || GENERIC_FAMILIES.has(family.toLowerCase())) continue;
        const key = family.toLowerCase();
        if (!used.has(key)) {
          used.set(key, {
            family,
            el: el2,
            weight: String(cs.fontWeight || ""),
            style: String(cs.fontStyle || "")
          });
        }
      }
    } catch {
    }
    const faces = collectFontFaceRules(doc);
    const items = [];
    const seen = /* @__PURE__ */ new Set();
    const push = (item) => {
      if (isBrowserInternalFontUrl(item?.url)) return;
      const key = [
        String(item.fontFamily || "").toLowerCase(),
        String(item.url || ""),
        String(item.fontWeight || ""),
        String(item.fontStyle || "")
      ].join("");
      if (seen.has(key)) return;
      seen.add(key);
      items.push(item);
    };
    for (const face of faces) {
      const first = (face.urls || []).find((u) => u?.url && !isBrowserInternalFontUrl(u.url)) || null;
      const url = first?.url || "";
      const fileType = fontFileTypeLabel(first?.format || "", url);
      const ext = (fileType || "").replace(/^\./, "") || (url ? "font" : "local");
      const familyKey = unquoteFamily(face.family).toLowerCase();
      const rec = used.get(familyKey);
      const isCustom = !!url;
      if (!isCustom && !rec) continue;
      push({
        category: "font",
        kind: isCustom ? "custom" : "local",
        url,
        element: rec?.el || null,
        label: face.family,
        ext,
        fontFamily: face.family,
        fontWeight: face.weight || rec?.weight || "",
        fontStyle: face.style || rec?.style || "",
        sourceKind: isCustom ? "webfont" : "local"
      });
    }
    try {
      const entries = typeof performance?.getEntriesByType === "function" ? performance.getEntriesByType("resource") : [];
      for (const rec of used.values()) {
        const already = items.some((it) => String(it.fontFamily || "").toLowerCase() === rec.family.toLowerCase() && it.url);
        if (already) continue;
        const needle = rec.family.replace(/\s+/g, "").toLowerCase();
        for (const e of entries) {
          const name = String(e?.name || "");
          if (isBrowserInternalFontUrl(name)) continue;
          const low = name.toLowerCase();
          if (!/\.(woff2?|ttf|otf|eot)(\?|#|$)/i.test(low)) continue;
          if (needle && low.replace(/[^a-z0-9]/g, "").includes(needle.replace(/[^a-z0-9]/g, ""))) {
            const fileType = fontFileTypeLabel("", name);
            push({
              category: "font",
              kind: "custom",
              url: name,
              element: rec.el,
              label: rec.family,
              ext: (fileType || "").replace(/^\./, "") || "font",
              fontFamily: rec.family,
              fontWeight: rec.weight,
              fontStyle: rec.style,
              sourceKind: "webfont"
            });
            break;
          }
        }
      }
    } catch {
    }
    for (const rec of used.values()) {
      const has = items.some((it) => String(it.fontFamily || "").toLowerCase() === rec.family.toLowerCase());
      if (has) continue;
      push({
        category: "font",
        kind: "local",
        url: "",
        element: rec.el,
        label: rec.family,
        ext: "local",
        fontFamily: rec.family,
        fontWeight: rec.weight,
        fontStyle: rec.style,
        sourceKind: "local"
      });
    }
    items.sort((a, b) => {
      const ca = a.kind === "custom" ? 0 : 1;
      const cb = b.kind === "custom" ? 0 : 1;
      if (ca !== cb) return ca - cb;
      const ra = customFileRank(a.ext);
      const rb = customFileRank(b.ext);
      if (ra !== rb) return ra - rb;
      return String(a.label || "").localeCompare(String(b.label || ""));
    });
    return items;
  }

  // src/ui/font-info-popover.js
  init_constants();
  var STYLE_ATTR4 = "data-kp-font-info-style";
  var ROOT_CLASS2 = "kp-font-info";
  var _root2 = null;
  var _dragApi2 = null;
  var _onClose = null;
  function ensureStyles2(root) {
    injectChromeStyles(root, { attr: STYLE_ATTR4, css: `
:host {
  position: fixed;
  z-index: ${Z_INDEX.KEY_ACTION_CONFIG || 2147483047};
  width: min(440px, calc(100vw - 24px));
  max-height: min(70vh, 560px);
  display: flex;
  flex-direction: column;
  color: ${NCT_DARK_UI_COLORS.fg};
  font-family: ${KP_UI_FONT || "system-ui, sans-serif"};
  font-size: 12px;
  line-height: 1.45;
  border-radius: ${NCT_DARK_UI_PANEL_RADIUS};
  border: ${NCT_DARK_UI_PANEL_BORDER};
  background: ${NCT_DARK_UI_PANEL_BACKGROUND};
  box-shadow: ${NCT_DARK_UI_PANEL_BOX_SHADOW};
  box-sizing: border-box;
}
:host([hidden]) { display: none !important; }
.${ROOT_CLASS2}__titlebar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 10px;
  cursor: grab;
  user-select: none;
  background: ${NCT_DARK_UI_TITLEBAR_GRADIENT};
  border-bottom: ${NCT_DARK_UI_TITLEBAR_BORDER_BOTTOM};
  flex: 0 0 auto;
  letter-spacing: var(--kp-type-tracking-titlebar, 0.02em);
  text-transform: var(--kp-type-transform-titlebar, none);
}
.${ROOT_CLASS2}__title {
  font-weight: var(--kp-titlebar-title-weight, 600);
  font-size: 12px;
  color: var(--kp-color-fg, inherit);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.${ROOT_CLASS2}__close {
  appearance: none;
  background: ${NCT_DARK_UI_BTN_GRADIENT};
  border: ${NCT_DARK_UI_BTN_BORDER};
  color: inherit;
  width: 22px;
  height: 22px;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
}
.${ROOT_CLASS2}__body {
  padding: 10px 12px;
  overflow: auto;
  flex: 1 1 auto;
}
.${ROOT_CLASS2}__rows {
  display: grid;
  grid-template-columns: 88px 1fr;
  gap: 6px 10px;
  align-items: start;
}
.${ROOT_CLASS2}__label {
  color: ${NCT_DARK_UI_COLORS.fgMute};
  font-size: 11px;
  padding-top: 1px;
}
.${ROOT_CLASS2}__value {
  word-break: break-word;
  min-width: 0;
}
.${ROOT_CLASS2}__sample {
  margin: 0 0 10px;
  padding: 8px 10px;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  background: rgba(0,0,0,0.22);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.${ROOT_CLASS2}__url {
  display: block;
  color: inherit;
  text-decoration: underline;
  word-break: break-all;
}
.${ROOT_CLASS2}__actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  padding: 8px 12px 10px;
  border-top: 1px solid rgba(0,0,0,0.35);
  flex: 0 0 auto;
}
.${ROOT_CLASS2}__btn {
  appearance: none;
  border: ${NCT_DARK_UI_BTN_BORDER};
  background: ${NCT_DARK_UI_BTN_GRADIENT};
  color: ${NCT_DARK_UI_COLORS.fg};
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  padding: 5px 10px;
  font: inherit;
  cursor: pointer;
}
.${ROOT_CLASS2}__btn:hover {
  background: ${NCT_DARK_UI_HOVER_TINT};
}
.${ROOT_CLASS2}__btn[data-primary="true"] {
  background: ${NCT_DARK_UI_BTN_LIT_GRADIENT};
  border: ${NCT_DARK_UI_BTN_LIT_BORDER};
  color: #e8f0f8;
}
.${ROOT_CLASS2}__btn:disabled {
  opacity: 0.45;
  cursor: default;
}
` });
  }
  function filenameFromUrl(url, fileType) {
    try {
      const path = new URL(url, location.href).pathname || "";
      const base = path.split("/").filter(Boolean).pop() || "";
      if (base) return decodeURIComponent(base);
    } catch {
    }
    const ext = fileType && fileType.startsWith(".") ? fileType : ".woff2";
    return `font${ext}`;
  }
  function summaryText(info) {
    const lines = [
      `Name: ${info.usedFamily || ""}`,
      `Family: ${info.familyStack || ""}`,
      `Size: ${info.size || ""}`,
      `Weight: ${info.weight || ""}`,
      `Style: ${info.style || ""}`,
      `Stretch: ${info.stretch || ""}`,
      `File type: ${info.fileType || (info.sourceKind === "local" ? "local" : "")}`,
      `URL: ${info.resourceUrl || "(local / system font)"}`
    ];
    return lines.join("\n");
  }
  function closeInternal() {
    if (_root2) _root2.hidden = true;
    const cb = _onClose;
    _onClose = null;
    try {
      cb?.();
    } catch {
    }
  }
  function showFontInfoPopover(info = {}, anchor = null) {
    const doc = document;
    if (!_root2) {
      _root2 = doc.createElement("div");
      _root2.className = ROOT_CLASS2;
      _root2.setAttribute("role", "dialog");
      _root2.setAttribute("aria-label", "Font Info");
      const shadowRoot = ensureOpenChromeShadow(_root2, { id: "font-info", chromeWindow: true });
      const panelRoot2 = shadowRoot || _root2;
      ensureStyles2(panelRoot2);
      panelRoot2.innerHTML = `
      <div class="${ROOT_CLASS2}__titlebar" data-kp-font-info-drag="true">
        <div class="${ROOT_CLASS2}__title">Font Info</div>
        <button type="button" class="${ROOT_CLASS2}__close" aria-label="Close">\xD7</button>
      </div>
      <div class="${ROOT_CLASS2}__body"></div>
      <div class="${ROOT_CLASS2}__actions">
        <button type="button" class="${ROOT_CLASS2}__btn" data-kp-font-download="true">Download</button>
        <button type="button" class="${ROOT_CLASS2}__btn" data-kp-font-copy="true">Copy</button>
        <button type="button" class="${ROOT_CLASS2}__btn" data-primary="true" data-kp-font-close="true">Done</button>
      </div>
    `;
      doc.body.appendChild(_root2);
      panelRoot2.querySelector(`.${ROOT_CLASS2}__close`)?.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        hideFontInfoPopover();
      });
      panelRoot2.querySelector('[data-kp-font-close="true"]')?.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        hideFontInfoPopover();
      });
      const handle = panelRoot2.querySelector('[data-kp-font-info-drag="true"]');
      _dragApi2 = makePanelDraggable(_root2, handle, {
        excludeSelector: `.${ROOT_CLASS2}__close`
      });
    }
    _onClose = typeof info.onClose === "function" ? info.onClose : null;
    const panelRoot = _root2.shadowRoot || _root2;
    ensureStyles2(panelRoot);
    const body = panelRoot.querySelector(`.${ROOT_CLASS2}__body`);
    if (body) {
      body.replaceChildren();
      if (info.sampleText) {
        const sample = doc.createElement("div");
        sample.className = `${ROOT_CLASS2}__sample`;
        sample.textContent = info.sampleText;
        try {
          sample.style.fontFamily = info.familyStack || "inherit";
          sample.style.fontSize = "16px";
          sample.style.fontWeight = info.weight || "inherit";
          sample.style.fontStyle = info.style || "inherit";
        } catch {
        }
        body.appendChild(sample);
      }
      const rows = doc.createElement("div");
      rows.className = `${ROOT_CLASS2}__rows`;
      const sizeValue = info.size && info.specifiedSize && info.specifiedSize !== info.size ? `${info.size} (${info.specifiedSize})` : info.size || "";
      const fileType = info.fileType || (info.sourceKind === "local" ? "local" : "");
      const pairs = [
        ["Name", info.usedFamily || ""],
        ["Family", info.familyStack || ""],
        ["Size", sizeValue],
        ["Weight", info.weight || ""],
        ["Style", info.style || ""],
        ["Stretch", info.stretch || ""],
        ["File type", fileType]
      ];
      for (const [label, value] of pairs) {
        const l = doc.createElement("div");
        l.className = `${ROOT_CLASS2}__label`;
        l.textContent = label;
        const v = doc.createElement("div");
        v.className = `${ROOT_CLASS2}__value`;
        v.textContent = value;
        rows.appendChild(l);
        rows.appendChild(v);
      }
      const urlLabel = doc.createElement("div");
      urlLabel.className = `${ROOT_CLASS2}__label`;
      urlLabel.textContent = "URL";
      const urlVal = doc.createElement("div");
      urlVal.className = `${ROOT_CLASS2}__value`;
      if (info.resourceUrl) {
        const a = doc.createElement("a");
        a.className = `${ROOT_CLASS2}__url`;
        a.href = info.resourceUrl;
        a.target = "_blank";
        a.rel = "noopener noreferrer";
        a.textContent = info.resourceUrl;
        urlVal.appendChild(a);
      } else {
        urlVal.textContent = "Local / system font";
      }
      rows.appendChild(urlLabel);
      rows.appendChild(urlVal);
      body.appendChild(rows);
    }
    const copyBtn = panelRoot.querySelector('[data-kp-font-copy="true"]');
    if (copyBtn) {
      copyBtn.textContent = "Copy";
      copyBtn.onclick = async (e) => {
        e.preventDefault();
        e.stopPropagation();
        let ok = false;
        try {
          await navigator.clipboard.writeText(summaryText(info));
          ok = true;
        } catch {
        }
        copyBtn.textContent = ok ? "Copied" : "Copy failed";
        setTimeout(() => {
          try {
            copyBtn.textContent = "Copy";
          } catch {
          }
        }, 1200);
      };
    }
    const dlBtn = panelRoot.querySelector('[data-kp-font-download="true"]');
    if (dlBtn) {
      const canDownload = !!info.resourceUrl && info.sourceKind !== "local";
      dlBtn.disabled = !canDownload;
      dlBtn.textContent = "Download";
      dlBtn.onclick = async (e) => {
        e.preventDefault();
        e.stopPropagation();
        if (!canDownload) return;
        dlBtn.disabled = true;
        dlBtn.textContent = "Downloading\u2026";
        let ok = false;
        try {
          const res = await fetch(info.resourceUrl);
          if (!res.ok) throw new Error(String(res.status));
          const blob = await res.blob();
          const href = URL.createObjectURL(blob);
          const a = doc.createElement("a");
          a.href = href;
          a.download = filenameFromUrl(info.resourceUrl, info.fileType);
          a.rel = "noopener";
          doc.body.appendChild(a);
          a.click();
          a.remove();
          setTimeout(() => {
            try {
              URL.revokeObjectURL(href);
            } catch {
            }
          }, 4e3);
          ok = true;
        } catch {
          ok = false;
        }
        dlBtn.textContent = ok ? "Downloaded" : "Download failed";
        dlBtn.disabled = !ok && canDownload ? false : !canDownload;
        if (ok) {
          setTimeout(() => {
            try {
              dlBtn.textContent = "Download";
              dlBtn.disabled = !canDownload;
            } catch {
            }
          }, 1400);
        }
      };
    }
    _root2.hidden = false;
    const margin = 16;
    const vw = window.innerWidth || 800;
    const vh = window.innerHeight || 600;
    let left = Number.isFinite(anchor?.left) ? Math.round(anchor.left) : Math.max(margin, Math.round(vw * 0.5 - 220));
    let top = Number.isFinite(anchor?.top) ? Math.round(anchor.top) : Math.max(margin, Math.round(vh * 0.18));
    left = Math.max(margin, Math.min(left, vw - 280));
    top = Math.max(margin, Math.min(top, vh - 160));
    _root2.style.left = `${left}px`;
    _root2.style.top = `${top}px`;
    _root2.style.right = "auto";
    _root2.style.bottom = "auto";
  }
  function hideFontInfoPopover() {
    closeInternal();
  }
  function isFontInfoPopoverOpen() {
    return !!(_root2 && !_root2.hidden);
  }

  // src/utils/dictionary-lookup.js
  function normalizeWordForLookup(raw) {
    let w = String(raw || "").trim();
    if (!w) return "";
    w = w.replace(/^[^\p{L}\p{N}]+/u, "").replace(/[^\p{L}\p{N}]+$/u, "");
    w = w.trim().toLowerCase();
    return w;
  }

  // src/keypilot.js
  init_storage();

  // src/utils/image-utils.js
  var MAX_ANCESTOR_DEPTH = 12;
  var MATERIALIZE_TIMEOUT_MS = 8e3;
  var MAX_SVG_RASTER_EDGE = 4096;
  function isKeyPilotChrome(el2) {
    if (!el2 || el2.nodeType !== 1) return false;
    try {
      const id = typeof el2.id === "string" ? el2.id : "";
      if (id && (id === "kpv2-cursor" || id.startsWith("kpv2-"))) return true;
    } catch {
    }
    try {
      const cls = typeof el2.className === "string" ? el2.className : el2.className && typeof el2.className.baseVal === "string" ? el2.className.baseVal : "";
      if (cls && (/\bkpv2-/.test(cls) || /\bkp-/.test(cls))) return true;
    } catch {
    }
    return false;
  }
  function isHtmlImage(el2) {
    return !!(el2 && el2.nodeType === 1 && el2.tagName === "IMG");
  }
  function isHtmlVideo(el2) {
    return !!(el2 && el2.nodeType === 1 && String(el2.tagName || "").toUpperCase() === "VIDEO");
  }
  function getVideoPosterUrl(video) {
    if (!video) return "";
    try {
      const poster = typeof video.poster === "string" && video.poster ? video.poster.trim() : "";
      if (isUsableImageUrl(poster)) return resolveUrl(poster, video);
    } catch {
    }
    try {
      const attr = video.getAttribute && video.getAttribute("poster") || "";
      const trimmed = String(attr || "").trim();
      if (isUsableImageUrl(trimmed)) return resolveUrl(trimmed, video);
    } catch {
    }
    return "";
  }
  function videoHasDrawableFrame(video) {
    if (!video) return false;
    try {
      const ready = typeof video.readyState === "number" ? video.readyState : 0;
      const w = Number(video.videoWidth) || 0;
      const h = Number(video.videoHeight) || 0;
      return ready >= 2 && w > 0 && h > 0;
    } catch {
      return false;
    }
  }
  function videoLikelyShowingPoster(video) {
    if (!video || !getVideoPosterUrl(video)) return false;
    try {
      const played = video.played;
      const neverPlayed = !played || played.length === 0;
      if (neverPlayed && video.paused && !video.ended) return true;
    } catch {
    }
    return !videoHasDrawableFrame(video);
  }
  function isSvgRoot(el2) {
    if (!el2 || el2.nodeType !== 1) return false;
    try {
      if (typeof SVGSVGElement !== "undefined" && el2 instanceof SVGSVGElement) return true;
    } catch {
    }
    return String(el2.tagName || "").toLowerCase() === "svg";
  }
  function getSvgRoot(el2) {
    if (!el2 || el2.nodeType !== 1) return null;
    if (isSvgRoot(el2)) return el2;
    try {
      const owner = (
        /** @type {any} */
        el2.ownerSVGElement
      );
      if (owner && isSvgRoot(owner)) return owner;
    } catch {
    }
    try {
      if (typeof el2.closest === "function") {
        const svg = el2.closest("svg");
        if (svg) return svg;
      }
    } catch {
    }
    return null;
  }
  function getImgUrl(img) {
    try {
      const current = typeof img.currentSrc === "string" && img.currentSrc ? img.currentSrc.trim() : "";
      if (current) return current;
    } catch {
    }
    try {
      const src = typeof img.src === "string" && img.src ? img.src.trim() : "";
      if (src) return src;
    } catch {
    }
    try {
      const attr = img.getAttribute && img.getAttribute("src") || "";
      return String(attr || "").trim();
    } catch {
      return "";
    }
  }
  function isUsableImageUrl(url) {
    if (!url || typeof url !== "string") return false;
    const u = url.trim();
    if (!u || u === "about:blank") return false;
    return true;
  }
  function extractFirstBackgroundImageUrl(cssValue) {
    if (!cssValue || typeof cssValue !== "string") return null;
    const value = cssValue.trim();
    if (!value || value === "none") return null;
    const urlRe = /url\s*\(\s*(['"]?)([^'")]+)\1\s*\)/gi;
    let match;
    while ((match = urlRe.exec(value)) !== null) {
      const raw = (match[2] || "").trim();
      if (!raw) continue;
      if (/^(none|inherit|initial|unset|revert)$/i.test(raw)) continue;
      return raw;
    }
    return null;
  }
  function resolveUrl(url, element) {
    if (!url) return "";
    if (/^(data:|blob:|https?:|chrome-extension:)/i.test(url)) return url;
    try {
      const base = element && element.ownerDocument && element.ownerDocument.baseURI || (typeof document !== "undefined" ? document.baseURI : "") || "";
      return new URL(url, base).href;
    } catch {
      return url;
    }
  }
  function getParentOrHost(el2) {
    let next = null;
    try {
      next = el2.parentElement;
    } catch {
      next = null;
    }
    if (!next) {
      try {
        const root = el2.getRootNode && el2.getRootNode();
        if (root && /** @type {any} */
        root.host) next = /** @type {any} */
        root.host;
      } catch {
      }
    }
    return next;
  }
  function pointInRect(x, y, rect) {
    if (!rect) return false;
    const w = Number(rect.width) || 0;
    const h = Number(rect.height) || 0;
    if (w < 1 || h < 1) return false;
    return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
  }
  function elementContainsPoint(el2, x, y) {
    try {
      return pointInRect(x, y, el2.getBoundingClientRect());
    } catch {
      return false;
    }
  }
  function sourceFromSelf(el2) {
    if (!el2 || el2.nodeType !== 1 || isKeyPilotChrome(el2)) return null;
    if (isHtmlImage(el2)) {
      const url = getImgUrl(
        /** @type {HTMLImageElement} */
        el2
      );
      if (isUsableImageUrl(url)) {
        return { kind: "img", element: el2, url: resolveUrl(url, el2) };
      }
    }
    if (isHtmlVideo(el2)) {
      const video = (
        /** @type {HTMLVideoElement} */
        el2
      );
      const poster = getVideoPosterUrl(video);
      if (poster || videoHasDrawableFrame(video)) {
        return {
          kind: "video",
          element: el2,
          url: poster || (typeof video.currentSrc === "string" ? video.currentSrc : null)
        };
      }
    }
    if (isSvgRoot(el2)) {
      return { kind: "svg", element: el2, url: null };
    }
    try {
      const svgRoot = getSvgRoot(el2);
      if (svgRoot && svgRoot !== el2 && !isKeyPilotChrome(svgRoot)) {
        return { kind: "svg", element: svgRoot, url: null };
      }
    } catch {
    }
    try {
      const style = window.getComputedStyle && window.getComputedStyle(el2);
      const bg = style ? String(style.backgroundImage || "") : "";
      if (bg && bg !== "none" && /url\s*\(/i.test(bg)) {
        const extracted = extractFirstBackgroundImageUrl(bg);
        if (extracted && isUsableImageUrl(extracted)) {
          return {
            kind: "background",
            element: el2,
            url: resolveUrl(extracted, el2)
          };
        }
      }
    } catch {
    }
    return null;
  }
  function findImageInSubtree(root, x, y) {
    if (!root || root.nodeType !== 1) return null;
    try {
      if (String(root.tagName || "").toLowerCase() === "picture") {
        const img = root.querySelector("img");
        if (img) {
          const fromImg = sourceFromSelf(img);
          if (fromImg) return fromImg;
        }
      }
    } catch {
    }
    try {
      const imgs = root.querySelectorAll("img");
      let fallback = null;
      for (let i = 0; i < imgs.length; i++) {
        const img = imgs[i];
        if (isKeyPilotChrome(img)) continue;
        const url = getImgUrl(
          /** @type {HTMLImageElement} */
          img
        );
        if (!isUsableImageUrl(url)) continue;
        const src = {
          kind: (
            /** @type {const} */
            "img"
          ),
          element: img,
          url: resolveUrl(url, img)
        };
        if (elementContainsPoint(img, x, y)) return src;
        if (!fallback) fallback = src;
      }
      if (fallback && imgs.length === 1) {
        const tag = String(root.tagName || "").toLowerCase();
        const cls = typeof root.className === "string" ? root.className : "";
        const tight = tag === "a" || tag === "picture" || tag === "figure" || tag === "button" || /image|thumb|media|photo|cover|figure|poster/i.test(cls);
        if (tight) return fallback;
      }
    } catch {
    }
    try {
      const svgs = root.querySelectorAll("svg");
      for (let i = 0; i < svgs.length; i++) {
        const svg = svgs[i];
        if (isKeyPilotChrome(svg)) continue;
        if (!isSvgRoot(svg)) continue;
        if (elementContainsPoint(svg, x, y)) {
          return { kind: "svg", element: svg, url: null };
        }
      }
    } catch {
    }
    try {
      const videos = root.querySelectorAll("video");
      for (let i = 0; i < videos.length; i++) {
        const video = (
          /** @type {HTMLVideoElement} */
          videos[i]
        );
        if (isKeyPilotChrome(video)) continue;
        if (!elementContainsPoint(video, x, y)) continue;
        const fromVideo = sourceFromSelf(video);
        if (fromVideo) return fromVideo;
      }
    } catch {
    }
    return null;
  }
  function findImageSourceFromElement(start, x, y) {
    if (!start) return null;
    try {
      const svgHit = getSvgRoot(start);
      if (svgHit && !isKeyPilotChrome(svgHit)) {
        return { kind: "svg", element: svgHit, url: null };
      }
    } catch {
    }
    let el2 = start;
    let depth = 0;
    while (el2 && el2.nodeType === 1 && depth < MAX_ANCESTOR_DEPTH) {
      if (isKeyPilotChrome(el2)) {
        el2 = getParentOrHost(el2);
        depth++;
        continue;
      }
      const selfHit = sourceFromSelf(el2);
      if (selfHit) return selfHit;
      const nested = findImageInSubtree(el2, x, y);
      if (nested) return nested;
      el2 = getParentOrHost(el2);
      depth++;
    }
    return null;
  }
  function findImageFromPointStack(x, y) {
    let stack = null;
    try {
      if (typeof document.elementsFromPoint === "function") {
        stack = document.elementsFromPoint(x, y);
      }
    } catch {
      stack = null;
    }
    if (!stack || !stack.length) return null;
    const limit = Math.min(stack.length, 24);
    for (let i = 0; i < limit; i++) {
      const el2 = stack[i];
      if (!el2 || el2.nodeType !== 1) continue;
      if (isKeyPilotChrome(el2)) continue;
      const selfHit = sourceFromSelf(el2);
      if (selfHit) return selfHit;
    }
    return null;
  }
  function findHoveredImageSource2(clientX, clientY, options = {}) {
    if (typeof clientX !== "number" || typeof clientY !== "number" || !Number.isFinite(clientX) || !Number.isFinite(clientY)) {
      return null;
    }
    const fromStack = findImageFromPointStack(clientX, clientY);
    if (fromStack) return fromStack;
    const hitFn = typeof options.elementFromPoint === "function" ? options.elementFromPoint : deepElementFromPoint;
    let hit = null;
    try {
      hit = hitFn(clientX, clientY);
    } catch {
      hit = null;
    }
    if (!hit || hit.nodeType !== 1) return null;
    return findImageSourceFromElement(hit, clientX, clientY);
  }
  function canvasToPngBlob(canvas) {
    return new Promise((resolve) => {
      try {
        canvas.toBlob((b) => resolve(b || null), "image/png");
      } catch {
        resolve(null);
      }
    });
  }
  async function blobToPng(blob) {
    if (!blob) return null;
    if (blob.type === "image/png") return blob;
    const isSvg = /svg/i.test(blob.type || "") || typeof blob.type === "string" && blob.type.includes("svg");
    if (!isSvg) {
      try {
        if (typeof createImageBitmap === "function") {
          const bitmap = await createImageBitmap(blob);
          try {
            const canvas = document.createElement("canvas");
            canvas.width = bitmap.width || 1;
            canvas.height = bitmap.height || 1;
            const ctx = canvas.getContext("2d");
            if (!ctx) return null;
            ctx.drawImage(bitmap, 0, 0);
            return await canvasToPngBlob(canvas);
          } finally {
            try {
              bitmap.close();
            } catch {
            }
          }
        }
      } catch (err) {
        console.warn("[KeyPilot] createImageBitmap PNG convert failed:", err);
      }
    }
    try {
      const objectUrl = URL.createObjectURL(blob);
      try {
        const img = await loadHtmlImage(objectUrl);
        const canvas = document.createElement("canvas");
        canvas.width = Math.max(1, img.naturalWidth || img.width || 1);
        canvas.height = Math.max(1, img.naturalHeight || img.height || 1);
        const ctx = canvas.getContext("2d");
        if (!ctx) return null;
        ctx.drawImage(img, 0, 0);
        return await canvasToPngBlob(canvas);
      } finally {
        try {
          URL.revokeObjectURL(objectUrl);
        } catch {
        }
      }
    } catch (err) {
      console.warn("[KeyPilot] Image element PNG convert failed:", err);
      return null;
    }
  }
  function loadHtmlImage(src) {
    return new Promise((resolve, reject) => {
      const img = new Image();
      try {
        img.crossOrigin = "anonymous";
      } catch {
      }
      const timer = setTimeout(() => {
        cleanup();
        reject(new Error("Image load timed out"));
      }, MATERIALIZE_TIMEOUT_MS);
      const cleanup = () => {
        clearTimeout(timer);
        img.onload = null;
        img.onerror = null;
      };
      img.onload = () => {
        cleanup();
        resolve(img);
      };
      img.onerror = () => {
        cleanup();
        reject(new Error("Image failed to load"));
      };
      img.src = src;
    });
  }
  async function fetchImageBlob(url) {
    if (!url) return null;
    const controller = typeof AbortController !== "undefined" ? new AbortController() : null;
    const timer = setTimeout(() => {
      try {
        controller?.abort();
      } catch {
      }
    }, MATERIALIZE_TIMEOUT_MS);
    try {
      const res = await fetch(url, {
        signal: controller?.signal,
        credentials: "include",
        mode: "cors",
        cache: "force-cache"
      });
      if (!res.ok) {
        console.warn("[KeyPilot] Image fetch failed:", res.status, url.slice(0, 120));
        return null;
      }
      const blob = await res.blob();
      return blob || null;
    } catch (err) {
      try {
        const res = await fetch(url, {
          credentials: "omit",
          mode: "cors",
          cache: "force-cache"
        });
        if (!res.ok) return null;
        return await res.blob();
      } catch (err2) {
        console.warn("[KeyPilot] Image fetch error:", err2?.message || err2);
        return null;
      }
    } finally {
      clearTimeout(timer);
    }
  }
  function getSvgRasterSize(svg) {
    let cssW = 0;
    let cssH = 0;
    try {
      const rect = svg.getBoundingClientRect();
      cssW = rect.width || 0;
      cssH = rect.height || 0;
    } catch {
    }
    if (cssW < 1 || cssH < 1) {
      try {
        const vb = (
          /** @type {any} */
          svg.viewBox?.baseVal
        );
        if (vb && vb.width > 0 && vb.height > 0) {
          cssW = vb.width;
          cssH = vb.height;
        }
      } catch {
      }
    }
    if (cssW < 1) cssW = 64;
    if (cssH < 1) cssH = 64;
    const dpr = Math.min(
      typeof window !== "undefined" && window.devicePixelRatio ? window.devicePixelRatio : 1,
      2
    );
    let width = Math.round(cssW * dpr);
    let height = Math.round(cssH * dpr);
    const maxEdge = Math.max(width, height);
    if (maxEdge > MAX_SVG_RASTER_EDGE) {
      const scale = MAX_SVG_RASTER_EDGE / maxEdge;
      width = Math.max(1, Math.round(width * scale));
      height = Math.max(1, Math.round(height * scale));
    }
    return { width: Math.max(1, width), height: Math.max(1, height) };
  }
  function serializeInlineSvg(svg) {
    if (!svg || !isSvgRoot(svg)) return null;
    let clone;
    try {
      clone = /** @type {Element} */
      svg.cloneNode(true);
    } catch {
      return null;
    }
    try {
      if (!clone.getAttribute("xmlns")) {
        clone.setAttribute("xmlns", "http://www.w3.org/2000/svg");
      }
      if (!clone.getAttribute("xmlns:xlink")) {
        clone.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
      }
    } catch {
    }
    try {
      const rect = svg.getBoundingClientRect();
      if (!clone.getAttribute("width") && rect.width > 0) {
        clone.setAttribute("width", String(rect.width));
      }
      if (!clone.getAttribute("height") && rect.height > 0) {
        clone.setAttribute("height", String(rect.height));
      }
    } catch {
    }
    try {
      if (!clone.getAttribute("viewBox")) {
        const vb = (
          /** @type {any} */
          svg.viewBox?.baseVal
        );
        if (vb && vb.width > 0 && vb.height > 0) {
          clone.setAttribute("viewBox", `${vb.x} ${vb.y} ${vb.width} ${vb.height}`);
        } else {
          const w = parseFloat(clone.getAttribute("width") || "") || 0;
          const h = parseFloat(clone.getAttribute("height") || "") || 0;
          if (w > 0 && h > 0) {
            clone.setAttribute("viewBox", `0 0 ${w} ${h}`);
          }
        }
      }
    } catch {
    }
    try {
      const refs = clone.querySelectorAll("image, use");
      refs.forEach((node) => {
        for (const attr of ["href", "xlink:href"]) {
          try {
            const val = node.getAttribute(attr);
            if (!val || val.startsWith("#") || /^(data:|blob:)/i.test(val)) continue;
            node.setAttribute(attr, resolveUrl(val, svg));
          } catch {
          }
        }
      });
    } catch {
    }
    try {
      const serializer = new XMLSerializer();
      let xml = serializer.serializeToString(clone);
      if (!/xmlns=/.test(xml)) {
        xml = xml.replace(
          /<svg\b/,
          '<svg xmlns="http://www.w3.org/2000/svg"'
        );
      }
      return xml;
    } catch (err) {
      console.warn("[KeyPilot] SVG serialize failed:", err);
      return null;
    }
  }
  async function materializeInlineSvg(svg) {
    if (!svg || !isSvgRoot(svg)) return null;
    const xml = serializeInlineSvg(svg);
    if (!xml) return null;
    const { width, height } = getSvgRasterSize(svg);
    const svgBlob = new Blob([xml], { type: "image/svg+xml;charset=utf-8" });
    const objectUrl = URL.createObjectURL(svgBlob);
    try {
      const img = await loadHtmlImage(objectUrl);
      const canvas = document.createElement("canvas");
      canvas.width = width;
      canvas.height = height;
      const ctx = canvas.getContext("2d");
      if (!ctx) return null;
      ctx.clearRect(0, 0, width, height);
      ctx.drawImage(img, 0, 0, width, height);
      const png = await canvasToPngBlob(canvas);
      if (!png || png.size === 0) return null;
      return { blob: png, mimeType: "image/png" };
    } catch (err) {
      console.warn("[KeyPilot] Inline SVG rasterize failed:", err);
      return null;
    } finally {
      try {
        URL.revokeObjectURL(objectUrl);
      } catch {
      }
    }
  }
  async function captureVideoFrameBlob(video) {
    if (!videoHasDrawableFrame(video)) return null;
    try {
      const width = Math.max(1, video.videoWidth || 0);
      const height = Math.max(1, video.videoHeight || 0);
      const canvas = document.createElement("canvas");
      canvas.width = width;
      canvas.height = height;
      const ctx = canvas.getContext("2d");
      if (!ctx) return null;
      ctx.drawImage(video, 0, 0, width, height);
      const png = await canvasToPngBlob(canvas);
      if (!png || png.size === 0) return null;
      return { blob: png, mimeType: "image/png" };
    } catch (err) {
      console.warn("[KeyPilot] Video frame capture failed:", err?.message || err);
      return null;
    }
  }
  async function materializeVideoElement(video) {
    if (!isHtmlVideo(video)) return null;
    const posterUrl = getVideoPosterUrl(video);
    const preferPoster = videoLikelyShowingPoster(video);
    if (preferPoster && posterUrl) {
      const fromPoster = await materializeImageBlob(posterUrl, {});
      if (fromPoster) return fromPoster;
      const frameFallback = await captureVideoFrameBlob(video);
      if (frameFallback) return frameFallback;
      return null;
    }
    const fromFrame = await captureVideoFrameBlob(video);
    if (fromFrame) return fromFrame;
    if (posterUrl) {
      return materializeImageBlob(posterUrl, {});
    }
    return null;
  }
  async function materializeImageBlob(url, options = {}) {
    if (!isUsableImageUrl(url)) return null;
    const el2 = options.element;
    if (isHtmlImage(el2)) {
      try {
        const img = (
          /** @type {HTMLImageElement} */
          el2
        );
        if (img.complete && (img.naturalWidth > 0 || img.naturalHeight > 0)) {
          const canvas = document.createElement("canvas");
          canvas.width = Math.max(1, img.naturalWidth || img.width || 1);
          canvas.height = Math.max(1, img.naturalHeight || img.height || 1);
          const ctx = canvas.getContext("2d");
          if (ctx) {
            ctx.drawImage(img, 0, 0);
            const png2 = await canvasToPngBlob(canvas);
            if (png2 && png2.size > 0) {
              return { blob: png2, mimeType: "image/png" };
            }
          }
        }
      } catch {
      }
    }
    const raw = await fetchImageBlob(url);
    if (!raw || raw.size === 0) return null;
    const png = await blobToPng(raw);
    if (png && png.size > 0) {
      return { blob: png, mimeType: "image/png" };
    }
    const type2 = raw.type && raw.type.startsWith("image/") ? raw.type : "image/png";
    return { blob: raw, mimeType: type2 };
  }
  async function getHoveredImage(clientX, clientY, options = {}) {
    const source = findHoveredImageSource2(clientX, clientY, options);
    if (!source) return null;
    let materialized = null;
    if (source.kind === "svg") {
      materialized = await materializeInlineSvg(source.element);
    } else if (source.kind === "video") {
      materialized = await materializeVideoElement(
        /** @type {HTMLVideoElement} */
        source.element
      );
    } else {
      materialized = await materializeImageBlob(source.url || "", { element: source.element });
    }
    if (!materialized) return null;
    return {
      kind: source.kind,
      element: source.element,
      url: source.url,
      blob: materialized.blob,
      mimeType: materialized.mimeType
    };
  }

  // src/utils/video-utils.js
  var MAX_ANCESTOR_DEPTH2 = 12;
  function isKeyPilotChrome2(el2) {
    if (!el2 || el2.nodeType !== 1) return false;
    try {
      const id = typeof el2.id === "string" ? el2.id : "";
      if (id && (id === "kpv2-cursor" || id.startsWith("kpv2-"))) return true;
    } catch {
    }
    try {
      const cls = typeof el2.className === "string" ? el2.className : el2.className && typeof el2.className.baseVal === "string" ? el2.className.baseVal : "";
      if (cls && (/\bkpv2-/.test(cls) || /\bkp-/.test(cls))) return true;
    } catch {
    }
    return false;
  }
  function getParentOrHost2(el2) {
    let next = null;
    try {
      next = el2.parentElement;
    } catch {
      next = null;
    }
    if (!next) {
      try {
        const root = el2.getRootNode && el2.getRootNode();
        if (root && /** @type {any} */
        root.host) next = /** @type {any} */
        root.host;
      } catch {
      }
    }
    return next;
  }
  function pointInRect2(x, y, rect) {
    if (!rect) return false;
    const w = Number(rect.width) || 0;
    const h = Number(rect.height) || 0;
    if (w < 1 || h < 1) return false;
    return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
  }
  function elementContainsPoint2(el2, x, y) {
    try {
      return pointInRect2(x, y, el2.getBoundingClientRect());
    } catch {
      return false;
    }
  }
  function videoFromSelf(el2) {
    if (!isHtmlVideo(el2) || isKeyPilotChrome2(el2)) return null;
    return (
      /** @type {HTMLVideoElement} */
      el2
    );
  }
  function findVideoInSubtree(root, x, y) {
    if (!root || root.nodeType !== 1) return null;
    try {
      if (typeof root.querySelectorAll !== "function") return null;
      const videos = root.querySelectorAll("video");
      for (let i = 0; i < videos.length; i++) {
        const video = (
          /** @type {HTMLVideoElement} */
          videos[i]
        );
        if (isKeyPilotChrome2(video)) continue;
        if (!elementContainsPoint2(video, x, y)) continue;
        return video;
      }
    } catch {
    }
    return null;
  }
  function findVideoFromElement(start, x, y) {
    let el2 = start;
    let depth = 0;
    while (el2 && el2.nodeType === 1 && depth < MAX_ANCESTOR_DEPTH2) {
      if (isKeyPilotChrome2(el2)) {
        el2 = getParentOrHost2(el2);
        depth++;
        continue;
      }
      const self = videoFromSelf(el2);
      if (self) return self;
      const nested = findVideoInSubtree(el2, x, y);
      if (nested) return nested;
      el2 = getParentOrHost2(el2);
      depth++;
    }
    return null;
  }
  function findVideoFromPointStack(x, y) {
    let stack = null;
    try {
      if (typeof document.elementsFromPoint === "function") {
        stack = document.elementsFromPoint(x, y);
      }
    } catch {
      stack = null;
    }
    if (!stack || !stack.length) return null;
    const limit = Math.min(stack.length, 24);
    for (let i = 0; i < limit; i++) {
      const el2 = stack[i];
      if (!el2 || el2.nodeType !== 1) continue;
      if (isKeyPilotChrome2(el2)) continue;
      const self = videoFromSelf(el2);
      if (self) return self;
      const nested = findVideoInSubtree(el2, x, y);
      if (nested) return nested;
    }
    return null;
  }
  function absolutizeUrl(raw, base) {
    const s = String(raw || "").trim();
    if (!s) return "";
    try {
      return new URL(s, base || (typeof document !== "undefined" ? document.baseURI : void 0)).href;
    } catch {
      return s;
    }
  }
  function videoUsesMediaSource(video) {
    try {
      const srcObject = (
        /** @type {any} */
        video.srcObject
      );
      if (srcObject && typeof MediaSource !== "undefined" && srcObject instanceof MediaSource) {
        return true;
      }
    } catch {
    }
    return false;
  }
  function collectVideoSourceUrls(video) {
    const out = [];
    const seen = /* @__PURE__ */ new Set();
    const base = video?.ownerDocument?.baseURI || (typeof document !== "undefined" ? document.baseURI : "");
    const push = (raw) => {
      const href = absolutizeUrl(raw, base);
      if (!href || seen.has(href)) return;
      seen.add(href);
      out.push(href);
    };
    try {
      const cur = typeof video.currentSrc === "string" ? video.currentSrc.trim() : "";
      if (cur) push(cur);
    } catch {
    }
    try {
      const src = typeof video.src === "string" ? video.src.trim() : "";
      if (src) push(src);
    } catch {
    }
    try {
      const sources = video.querySelectorAll?.("source[src]");
      if (sources) {
        for (let i = 0; i < sources.length; i++) {
          push(sources[i].getAttribute("src") || "");
        }
      }
    } catch {
    }
    return out;
  }
  function pickPreferredFileUrl(urls, opts = {}) {
    const list = Array.isArray(urls) ? urls.filter(Boolean) : [];
    if (!list.length) return "";
    const progressive = list.filter((u) => isProgressiveMediaUrl(u));
    if (opts.usesMediaSource) {
      const httpProg = progressive.filter((u) => /^https?:/i.test(u));
      if (httpProg.length) return httpProg[0];
      const http2 = list.filter((u) => isServiceWorkerFetchableVideoUrl(u));
      if (http2.length) return http2[0];
      return "";
    }
    if (progressive.length) {
      const httpProg = progressive.filter((u) => /^https?:/i.test(u));
      if (httpProg.length) return httpProg[0];
      return progressive[0];
    }
    const http = list.filter((u) => isServiceWorkerFetchableVideoUrl(u));
    if (http.length) return http[0];
    return list[0] || "";
  }
  function pickClipboardVideoUrl(urls, fileUrl = "") {
    const list = Array.isArray(urls) ? urls.filter(Boolean) : [];
    const https = list.filter((u) => /^https?:\/\//i.test(u));
    if (https.length) {
      const prog = https.filter((u) => isProgressiveMediaUrl(u) && !isStreamingManifestUrl(u));
      if (prog.length) return prog[0];
      const nonManifest = https.filter((u) => !isStreamingManifestUrl(u));
      return nonManifest[0] || https[0];
    }
    if (fileUrl && !/^blob:/i.test(fileUrl)) return fileUrl;
    return fileUrl || list[0] || "";
  }
  async function fetchPageLocalVideoBlob(url) {
    const src = String(url || "").trim();
    if (!src) return null;
    if (!/^(blob:|data:)/i.test(src)) return null;
    try {
      const blob = await fetchMediaBlob(src);
      return blob && blob.size > 0 ? blob : null;
    } catch {
      return null;
    }
  }
  async function getHoveredVideo(clientX, clientY, options = {}) {
    if (!Number.isFinite(clientX) || !Number.isFinite(clientY)) return null;
    let video = findVideoFromPointStack(clientX, clientY);
    if (!video) {
      const hitFn = typeof options.elementFromPoint === "function" ? options.elementFromPoint : deepElementFromPoint;
      let hit = null;
      try {
        hit = hitFn(clientX, clientY);
      } catch {
        hit = null;
      }
      if (hit && hit.nodeType === 1) {
        video = findVideoFromElement(hit, clientX, clientY);
      }
    }
    if (!video) return null;
    const usesMediaSource = videoUsesMediaSource(video);
    const candidates = collectVideoSourceUrls(video);
    const fileUrl = pickPreferredFileUrl(candidates, { usesMediaSource });
    const currentSrc = pickClipboardVideoUrl(candidates, fileUrl);
    const posterUrl = getVideoPosterUrl(video);
    const videoWidth = Number(video.videoWidth) || 0;
    const videoHeight = Number(video.videoHeight) || 0;
    let duration = 0;
    try {
      const d = Number(video.duration);
      if (Number.isFinite(d) && d > 0) duration = d;
    } catch {
    }
    let thumbBlob = null;
    let thumbMime = "image/png";
    try {
      const thumb = await materializeVideoElement(video);
      if (thumb?.blob) {
        thumbBlob = thumb.blob;
        thumbMime = thumb.mimeType || "image/png";
      }
    } catch {
    }
    let blob = null;
    let mimeType = "";
    if (fileUrl && /^(blob:|data:)/i.test(fileUrl) && !usesMediaSource) {
      try {
        blob = await fetchPageLocalVideoBlob(fileUrl);
        if (blob && blob.size > 0) {
          mimeType = blob.type || "";
        } else {
          blob = null;
        }
      } catch {
        blob = null;
      }
    }
    if (!thumbBlob && !currentSrc && !fileUrl && !videoHasDrawableFrame(video)) return null;
    return {
      element: video,
      currentSrc,
      fileUrl: fileUrl || currentSrc,
      posterUrl,
      videoWidth,
      videoHeight,
      duration,
      thumbBlob,
      thumbMime,
      blob,
      mimeType,
      usesMediaSource
    };
  }

  // src/utils/image-dpi.js
  function parseDpiFromImageBytes(buf) {
    if (!buf || buf.byteLength < 24) return null;
    const view = new DataView(buf);
    if (view.getUint32(0) === 2303741511 && view.getUint32(4) === 218765834) {
      let offset2 = 8;
      while (offset2 + 12 <= view.byteLength) {
        const len = view.getUint32(offset2);
        const type2 = String.fromCharCode(
          view.getUint8(offset2 + 4),
          view.getUint8(offset2 + 5),
          view.getUint8(offset2 + 6),
          view.getUint8(offset2 + 7)
        );
        const dataStart = offset2 + 8;
        if (type2 === "pHYs" && len >= 9 && dataStart + 9 <= view.byteLength) {
          const ppux = view.getUint32(dataStart);
          const unit = view.getUint8(dataStart + 8);
          if (unit === 1 && ppux > 0) return Math.round(ppux * 0.0254);
          return null;
        }
        offset2 += 12 + len;
        if (type2 === "IEND") break;
      }
      return null;
    }
    if (view.getUint16(0) !== 65496) return null;
    let offset = 2;
    while (offset + 4 <= view.byteLength) {
      if (view.getUint8(offset) !== 255) break;
      const marker = view.getUint8(offset + 1);
      const size = view.getUint16(offset + 2);
      if (marker === 225 && size >= 8) {
        const start = offset + 4;
        const end = offset + 2 + size;
        if (start + 6 > view.byteLength) break;
        const head = String.fromCharCode(
          view.getUint8(start),
          view.getUint8(start + 1),
          view.getUint8(start + 2),
          view.getUint8(start + 3)
        );
        if (head === "Exif") {
          const tiffStart = start + 6;
          if (tiffStart + 8 > view.byteLength) break;
          const le = view.getUint16(tiffStart) === 18761;
          const u16 = (o) => le ? view.getUint16(o, true) : view.getUint16(o, false);
          const u32 = (o) => le ? view.getUint32(o, true) : view.getUint32(o, false);
          const ifd0 = tiffStart + u32(tiffStart + 4);
          if (ifd0 + 2 > end) break;
          const entries = u16(ifd0);
          let xRes = null;
          let yRes = null;
          let unit = 2;
          for (let i = 0; i < entries; i++) {
            const e = ifd0 + 2 + i * 12;
            if (e + 12 > end) break;
            const tag = u16(e);
            const type2 = u16(e + 2);
            const count = u32(e + 4);
            const valOff = e + 8;
            const readRational = (off) => {
              if (off + 8 > view.byteLength) return null;
              const num = u32(off);
              const den = u32(off + 4);
              if (!den) return null;
              return num / den;
            };
            if (tag === 282 && type2 === 5 && count === 1) {
              xRes = readRational(tiffStart + u32(valOff));
            } else if (tag === 283 && type2 === 5 && count === 1) {
              yRes = readRational(tiffStart + u32(valOff));
            } else if (tag === 296 && type2 === 3 && count === 1) {
              unit = u16(valOff);
            }
          }
          const res = xRes || yRes;
          if (res && res > 0) {
            if (unit === 3) return Math.round(res * 2.54);
            return Math.round(res);
          }
        }
      }
      if (size < 2) break;
      offset += 2 + size;
      if (marker === 218) break;
    }
    return null;
  }

  // src/utils/page-media-utils.js
  var IMAGE_EXTS = Object.freeze([
    "jpg",
    "jpeg",
    "png",
    "gif",
    "webp",
    "svg",
    "avif",
    "ico",
    "bmp",
    "tif",
    "tiff"
  ]);
  var VIDEO_EXTS = Object.freeze([
    "mp4",
    "mov",
    "webm",
    "m4v",
    "mkv",
    "avi",
    "mpg",
    "mpeg"
  ]);
  var TEXT_EXTS = Object.freeze([
    "pdf",
    "md",
    "markdown",
    "rtf",
    "txt",
    "doc",
    "docx",
    "odt",
    "csv"
  ]);
  var IMAGE_EXT_SET = new Set(IMAGE_EXTS);
  var VIDEO_EXT_SET = new Set(VIDEO_EXTS);
  var TEXT_EXT_SET = new Set(TEXT_EXTS);
  var MIME_TO_EXT = Object.freeze({
    "image/jpeg": "jpg",
    "image/jpg": "jpg",
    "image/pjpeg": "jpg",
    "image/png": "png",
    "image/gif": "gif",
    "image/webp": "webp",
    "image/avif": "avif",
    "image/svg+xml": "svg",
    "image/svg": "svg",
    "image/bmp": "bmp",
    "image/x-icon": "ico",
    "image/vnd.microsoft.icon": "ico",
    "image/tiff": "tif",
    "image/tif": "tif",
    "image/heic": "heic",
    "image/heif": "heif",
    "video/mp4": "mp4",
    "video/webm": "webm",
    "video/quicktime": "mov",
    "video/x-m4v": "m4v",
    "application/pdf": "pdf"
  });
  function extFromMimeType(mime) {
    const m = String(mime || "").toLowerCase().split(";")[0].trim();
    if (!m) return "";
    if (MIME_TO_EXT[m]) return MIME_TO_EXT[m];
    if (m.startsWith("image/")) {
      const sub = m.slice(6).split("+")[0];
      if (sub === "jpeg") return "jpg";
      if (sub === "svg") return "svg";
      return sub || "";
    }
    if (m.startsWith("video/")) {
      const sub = m.slice(6).split("+")[0];
      return sub || "";
    }
    return "";
  }
  function categoryFromMimeType(mime) {
    const m = String(mime || "").toLowerCase().split(";")[0].trim();
    if (!m) return null;
    if (m.startsWith("image/")) return "image";
    if (m.startsWith("video/")) return "video";
    if (m === "application/pdf" || m.startsWith("text/")) return "text";
    return null;
  }
  function extFromDataUrl(url) {
    const m = String(url || "").match(/^data:([a-z0-9.+/-]+)/i);
    if (!m) return "";
    return extFromMimeType(m[1]);
  }
  function normalizeStoredExt(ext) {
    const e = String(ext || "").toLowerCase();
    if (e === "jpeg") return "jpg";
    if (e === "tiff") return "tif";
    if (e === "svg+xml") return "svg";
    return e;
  }
  var URL_TAB_EXCLUDE_EXTS = Object.freeze([
    "css",
    "scss",
    "sass",
    "less",
    "js",
    "mjs",
    "cjs",
    "jsx",
    "ts",
    "tsx",
    "map",
    "woff",
    "woff2",
    "ttf",
    "otf",
    "eot",
    "wasm",
    "json"
  ]);
  var URL_TAB_EXCLUDE_EXT_SET = /* @__PURE__ */ new Set([
    ...URL_TAB_EXCLUDE_EXTS,
    ...IMAGE_EXTS,
    ...VIDEO_EXTS
  ]);
  var PAGE_URL_EXTS = Object.freeze([
    "html",
    "htm",
    "php",
    "py",
    "asp",
    "aspx",
    "jsp",
    "cgi",
    "xhtml",
    "shtml",
    "phtml"
  ]);
  var PAGE_URL_EXT_SET = new Set(PAGE_URL_EXTS);
  var MAX_SHADOW_DEPTH = 5;
  var MAX_ELEMENTS = 8e3;
  function isKeyPilotChrome3(el2) {
    if (!el2 || el2.nodeType !== 1) return false;
    try {
      const id = typeof el2.id === "string" ? el2.id : "";
      if (id && (id === "kpv2-cursor" || id.startsWith("kpv2-"))) return true;
    } catch {
    }
    try {
      const cls = typeof el2.className === "string" ? el2.className : el2.className && typeof el2.className.baseVal === "string" ? el2.className.baseVal : "";
      if (cls && (/\bkpv2-/.test(cls) || /\bkp-/.test(cls))) return true;
    } catch {
    }
    return false;
  }
  function isUsableUrl(url) {
    if (!url || typeof url !== "string") return false;
    const u = url.trim();
    if (!u || u === "about:blank") return false;
    if (/^javascript:/i.test(u)) return false;
    return true;
  }
  function resolveUrl2(url, element) {
    if (!url) return "";
    if (/^(data:|blob:|https?:|chrome-extension:|file:)/i.test(url)) return url;
    try {
      const base = element && element.ownerDocument && element.ownerDocument.baseURI || (typeof document !== "undefined" ? document.baseURI : "") || "";
      return new URL(url, base).href;
    } catch {
      return url;
    }
  }
  function extensionFromUrl(url) {
    if (!url || typeof url !== "string") return "";
    const trimmed = url.trim();
    if (!trimmed || /^(data:|blob:)/i.test(trimmed)) return "";
    try {
      const u = new URL(trimmed, typeof document !== "undefined" ? document.baseURI : void 0);
      const path = u.pathname || "";
      const last = path.split("/").pop() || "";
      const dot = last.lastIndexOf(".");
      if (dot < 0 || dot === last.length - 1) return "";
      return last.slice(dot + 1).toLowerCase();
    } catch {
      const clean = trimmed.split(/[?#]/)[0] || "";
      const last = clean.split("/").pop() || "";
      const dot = last.lastIndexOf(".");
      if (dot < 0 || dot === last.length - 1) return "";
      return last.slice(dot + 1).toLowerCase();
    }
  }
  function categoryFromExtension(ext) {
    const e = String(ext || "").toLowerCase();
    if (IMAGE_EXT_SET.has(e)) return "image";
    if (VIDEO_EXT_SET.has(e)) return "video";
    if (TEXT_EXT_SET.has(e)) return "text";
    return null;
  }
  function urlTabGroupForUrl(url, ext) {
    const e = String(ext || extensionFromUrl(url) || "").toLowerCase();
    if (!e || PAGE_URL_EXT_SET.has(e)) return "page";
    if (TEXT_EXT_SET.has(e)) return "document";
    if (IMAGE_EXT_SET.has(e)) return "image";
    if (VIDEO_EXT_SET.has(e)) return "video";
    try {
      const u = new URL(url, typeof document !== "undefined" ? document.baseURI : void 0);
      const last = (u.pathname || "").split("/").pop() || "";
      if (!last || !last.includes(".")) return "page";
      if (u.hash || u.search) return "page";
    } catch {
    }
    return "other";
  }
  function domainFromUrl(url) {
    const s = String(url || "").trim();
    if (!s) return "(unknown)";
    if (/^data:/i.test(s)) return "data:";
    if (/^blob:/i.test(s)) return "blob:";
    if (/^mailto:/i.test(s)) return "mailto:";
    if (/^tel:/i.test(s)) return "tel:";
    if (/^javascript:/i.test(s)) return "javascript:";
    try {
      const u = new URL(s, typeof document !== "undefined" ? document.baseURI : void 0);
      return u.hostname || u.protocol.replace(/:$/, "") || "(unknown)";
    } catch {
      return "(unknown)";
    }
  }
  function urlPathDisplay(url) {
    const s = String(url || "").trim();
    if (!s) return "";
    if (/^(data|blob|mailto|tel|javascript):/i.test(s)) return s;
    try {
      const u = new URL(s, typeof document !== "undefined" ? document.baseURI : void 0);
      const path = `${u.pathname || "/"}${u.search || ""}${u.hash || ""}`;
      return path || "/";
    } catch {
      return s;
    }
  }
  function directoryPrefixesFromPathname(pathname) {
    const raw = String(pathname || "/");
    const parts = raw.split("/").filter(Boolean);
    if (!parts.length) return [];
    const dirs = [];
    let acc = "";
    for (let i = 0; i < parts.length; i++) {
      const part = parts[i];
      const isLast = i === parts.length - 1;
      const looksLikeFile = isLast && part.includes(".");
      if (looksLikeFile) break;
      acc += `/${part}`;
      dirs.push(`${acc}/`);
    }
    return dirs;
  }
  function urlPathRelativeToPrefix(url, prefix) {
    const full = urlPathDisplay(url);
    const p = String(prefix || "");
    if (!p) return full;
    try {
      const u = new URL(String(url || ""), typeof document !== "undefined" ? document.baseURI : void 0);
      let path = u.pathname || "/";
      const suffix = `${u.search || ""}${u.hash || ""}`;
      const prefixNoSlash = p.endsWith("/") ? p.slice(0, -1) : p;
      if (path === prefixNoSlash) return `/${suffix}` || "/";
      if (path.startsWith(p)) {
        const rest = path.slice(p.length);
        return `/${rest}${suffix}`;
      }
      if (path.startsWith(prefixNoSlash + "/")) {
        const rest = path.slice(prefixNoSlash.length + 1);
        return `/${rest}${suffix}`;
      }
    } catch {
    }
    return full;
  }
  function groupUrlItemsByPathPrefix(items, minSize = 2) {
    const list = Array.isArray(items) ? items.slice() : [];
    const min = Math.max(2, Number(minSize) || 2);
    const prefixCount = /* @__PURE__ */ new Map();
    const prepared = [];
    for (const item of list) {
      let pathname = "/";
      try {
        pathname = new URL(String(item?.url || ""), typeof document !== "undefined" ? document.baseURI : void 0).pathname || "/";
      } catch {
        pathname = "/";
      }
      const prefixes = directoryPrefixesFromPathname(pathname);
      for (const pref of prefixes) {
        prefixCount.set(pref, (prefixCount.get(pref) || 0) + 1);
      }
      prepared.push({ item, prefixes });
    }
    const buckets = /* @__PURE__ */ new Map();
    for (const { item, prefixes } of prepared) {
      let best = "";
      for (const pref of prefixes) {
        if ((prefixCount.get(pref) || 0) >= min) best = pref;
      }
      if (!buckets.has(best)) buckets.set(best, []);
      buckets.get(best).push(item);
    }
    const groups = [];
    for (const [prefix, groupItems] of buckets) {
      groupItems.sort(compareUrlTabItems);
      groups.push({ prefix, items: groupItems });
    }
    groups.sort((a, b) => {
      if (!a.prefix && b.prefix) return 1;
      if (a.prefix && !b.prefix) return -1;
      return a.prefix.localeCompare(b.prefix);
    });
    return groups;
  }
  function urlTabGroupSortOrder(group) {
    if (group === "page") return 0;
    if (group === "document") return 1;
    if (group === "image") return 2;
    if (group === "video") return 3;
    return 4;
  }
  function urlLandmarkSortOrder(landmark) {
    const k = String(landmark || "");
    if (k === "article") return 0;
    if (k === "main") return 1;
    if (k === "content") return 2;
    if (k === "body") return 3;
    if (k === "aside") return 4;
    if (k === "nav") return 5;
    if (k === "header") return 6;
    if (k === "footer") return 7;
    if (k === "dialog") return 8;
    return 3;
  }
  function landmarkKindFromNode(node) {
    if (!node || node.nodeType !== 1) return "";
    const tag = String(node.tagName || "").toUpperCase();
    let role = "";
    try {
      role = String(node.getAttribute?.("role") || "").trim().toLowerCase();
    } catch {
    }
    const roles = role ? role.split(/\s+/) : [];
    if (tag === "DIALOG" || roles.includes("dialog") || roles.includes("alertdialog")) return "dialog";
    try {
      if (String(node.getAttribute?.("aria-modal") || "") === "true") return "dialog";
    } catch {
    }
    if (tag === "ARTICLE" || roles.includes("article")) return "article";
    if (tag === "MAIN" || roles.includes("main")) return "main";
    if (tag === "NAV" || roles.includes("navigation") || tag === "MENU") return "nav";
    if (tag === "ASIDE" || roles.includes("complementary")) return "aside";
    if (roles.includes("search") || roles.includes("searchbox")) return "search";
    if (tag === "HEADER" || roles.includes("banner")) return "header";
    if (tag === "FOOTER" || roles.includes("contentinfo")) return "footer";
    if (tag === "FIGURE" || tag === "BLOCKQUOTE" || tag === "CITE" || tag === "TABLE") return "content";
    if (tag === "SECTION" || roles.includes("region")) return "section";
    return "";
  }
  function walkComposedAncestors(el2, visit) {
    let node = el2;
    let depth = 0;
    while (node && depth++ < 64) {
      if (node.nodeType === 1) visit(node);
      if (node.parentElement) {
        node = node.parentElement;
        continue;
      }
      try {
        const root = typeof node.getRootNode === "function" ? node.getRootNode() : null;
        node = root && typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot ? root.host : null;
      } catch {
        break;
      }
    }
  }
  function urlLandmarkFromElement(el2) {
    if (!el2 || el2.nodeType !== 1) return "body";
    let inArticle = false;
    let inMain = false;
    let closest = "";
    walkComposedAncestors(el2, (node) => {
      const kind = landmarkKindFromNode(node);
      if (!kind) return;
      if (kind === "article") inArticle = true;
      if (kind === "main") inMain = true;
      if (!closest) closest = kind;
    });
    if (closest === "dialog") return "dialog";
    if (inArticle) return "article";
    if (inMain) {
      if (closest === "aside") return "aside";
      if (closest === "nav" || closest === "search") return "nav";
      if (closest === "header") return "header";
      if (closest === "footer") return "footer";
      return "main";
    }
    if (closest === "aside") return "aside";
    if (closest === "nav" || closest === "search") return "nav";
    if (closest === "header") return "header";
    if (closest === "footer") return "footer";
    if (closest === "content") return "content";
    return "body";
  }
  function urlLandmarkForItem(item) {
    if (item?.urlLandmark) return item.urlLandmark;
    return urlLandmarkFromElement(item?.element || null);
  }
  function compareUrlTabItems(a, b) {
    const ga = urlTabGroupSortOrder(a?.urlGroup || urlTabGroupForUrl(a?.url, a?.ext));
    const gb = urlTabGroupSortOrder(b?.urlGroup || urlTabGroupForUrl(b?.url, b?.ext));
    if (ga !== gb) return ga - gb;
    const la = urlLandmarkSortOrder(urlLandmarkForItem(a));
    const lb = urlLandmarkSortOrder(urlLandmarkForItem(b));
    if (la !== lb) return la - lb;
    return String(a?.url || "").localeCompare(String(b?.url || ""));
  }
  function shouldExcludeFromUrlTab(url, ext, el2 = null) {
    if (!url) return true;
    if (/^(chrome-extension|chrome|blob):/i.test(url)) return true;
    if (/^data:(image|video)\b/i.test(url)) return true;
    const e = String(ext || extensionFromUrl(url) || "").toLowerCase();
    if (e && URL_TAB_EXCLUDE_EXT_SET.has(e)) return true;
    try {
      if (el2 && el2.nodeType === 1) {
        const tag = String(el2.tagName || "").toUpperCase();
        if (tag === "SCRIPT" || tag === "STYLE") return true;
        if (tag === "IMG" || tag === "VIDEO" || tag === "PICTURE" || tag === "SOURCE" || tag === "TRACK") return true;
        if (tag === "INPUT" && String(el2.getAttribute("type") || "").toLowerCase() === "image") return true;
        if (tag === "LINK") {
          const rel = String(el2.getAttribute("rel") || "").toLowerCase();
          if (/\bstylesheet\b|\bpreload\b|\bmodulepreload\b/.test(rel)) return true;
          const as = String(el2.getAttribute("as") || "").toLowerCase();
          if (as === "script" || as === "style" || as === "font" || as === "image" || as === "video") return true;
        }
      }
    } catch {
    }
    return false;
  }
  function filenameFromUrl2(url) {
    if (!url || typeof url !== "string") return "";
    if (/^data:/i.test(url)) return "data-url";
    if (/^blob:/i.test(url)) return "blob";
    try {
      const u = new URL(url, typeof document !== "undefined" ? document.baseURI : void 0);
      const last = (u.pathname || "").split("/").pop() || "";
      return decodeURIComponent(last) || u.hostname || url.slice(0, 40);
    } catch {
      const last = (url.split(/[?#]/)[0] || "").split("/").pop() || "";
      return last || url.slice(0, 40);
    }
  }
  function getImgUrl2(img) {
    try {
      const current = typeof img.currentSrc === "string" && img.currentSrc ? img.currentSrc.trim() : "";
      if (current) return current;
    } catch {
    }
    try {
      const src = typeof img.src === "string" && img.src ? img.src.trim() : "";
      if (src) return src;
    } catch {
    }
    const lazyAttrs = [
      "src",
      "data-src",
      "data-lazy-src",
      "data-original",
      "data-lazy",
      "data-url",
      "data-image",
      "data-bg",
      "data-thumb",
      "data-thumbnail"
    ];
    for (const attr of lazyAttrs) {
      try {
        const v = img.getAttribute && img.getAttribute(attr) || "";
        const trimmed = String(v || "").trim();
        if (isUsableUrl(trimmed) && !/\s/.test(trimmed.split(",")[0])) return trimmed;
      } catch {
      }
    }
    try {
      const srcset = img.getAttribute && img.getAttribute("srcset") || "";
      if (srcset) {
        const first = String(srcset).split(",")[0]?.trim().split(/\s+/)[0] || "";
        if (isUsableUrl(first)) return first;
      }
    } catch {
    }
    return "";
  }
  function getVideoSourceUrls(video) {
    const out = [];
    try {
      const current = typeof video.currentSrc === "string" && video.currentSrc ? video.currentSrc.trim() : "";
      if (isUsableUrl(current)) out.push(current);
    } catch {
    }
    try {
      const src = typeof video.src === "string" && video.src ? video.src.trim() : "";
      if (isUsableUrl(src)) out.push(src);
    } catch {
    }
    try {
      const sources = video.querySelectorAll("source");
      for (let i = 0; i < sources.length; i++) {
        const s = sources[i];
        const src = s.getAttribute && s.getAttribute("src") || "";
        if (isUsableUrl(src)) out.push(String(src).trim());
      }
    } catch {
    }
    return out;
  }
  function getVideoPosterUrl2(video) {
    try {
      const poster = typeof video.poster === "string" && video.poster ? video.poster.trim() : "";
      if (isUsableUrl(poster)) return poster;
    } catch {
    }
    try {
      const attr = video.getAttribute && video.getAttribute("poster") || "";
      if (isUsableUrl(attr)) return String(attr).trim();
    } catch {
    }
    return "";
  }
  function harvestAttrUrls(el2) {
    const out = [];
    const attrs = ["href", "src", "data-src", "data-href", "poster", "action", "formaction", "cite", "data"];
    for (const name of attrs) {
      try {
        const v = el2.getAttribute && el2.getAttribute(name) || "";
        const trimmed = String(v || "").trim();
        if (isUsableUrl(trimmed)) out.push(trimmed);
      } catch {
      }
    }
    try {
      const srcset = el2.getAttribute && el2.getAttribute("srcset") || "";
      if (srcset) {
        const parts = String(srcset).split(",");
        for (const part of parts) {
          const first = part.trim().split(/\s+/)[0] || "";
          if (isUsableUrl(first)) out.push(first);
        }
      }
    } catch {
    }
    return out;
  }
  function walkElements(root, visit, shadowDepth = 0) {
    if (!root) return;
    let count = 0;
    const walk = (node) => {
      let list = null;
      try {
        if (node.querySelectorAll) list = node.querySelectorAll("*");
      } catch {
        list = null;
      }
      if (!list) return;
      for (let i = 0; i < list.length; i++) {
        if (count >= MAX_ELEMENTS) return;
        const el2 = list[i];
        if (!el2 || el2.nodeType !== 1) continue;
        if (isKeyPilotChrome3(el2)) continue;
        count++;
        visit(el2);
        if (shadowDepth < MAX_SHADOW_DEPTH) {
          try {
            const sr = (
              /** @type {Element} */
              el2.shadowRoot
            );
            if (sr) walkElements(sr, visit, shadowDepth + 1);
          } catch {
          }
        }
      }
    };
    walk(root);
  }
  function linkTitleFromElement(el2) {
    if (!el2 || el2.nodeType !== 1) return "";
    try {
      const aria = el2.getAttribute?.("aria-label");
      if (aria && String(aria).trim()) return String(aria).replace(/\s+/g, " ").trim();
      const title = el2.getAttribute?.("title");
      if (title && String(title).trim()) return String(title).replace(/\s+/g, " ").trim();
      const alt = el2.getAttribute?.("alt");
      if (alt && String(alt).trim()) return String(alt).replace(/\s+/g, " ").trim();
      let text = "";
      try {
        text = String(
          /** @type {any} */
          el2.innerText || el2.textContent || ""
        );
      } catch {
        text = String(el2.textContent || "");
      }
      text = text.replace(/\s+/g, " ").trim();
      if (!text) return "";
      if (text.length > 120) return `${text.slice(0, 119)}\u2026`;
      return text;
    } catch {
      return "";
    }
  }
  function collectPageMedia(root = document) {
    const byUrl = /* @__PURE__ */ new Map();
    const allPageUrls = /* @__PURE__ */ new Map();
    const notePageUrl = (rawUrl, el2 = null, kind = "url") => {
      if (!isUsableUrl(rawUrl)) return;
      const trimmed = String(rawUrl).trim();
      if (/^(javascript|mailto|tel|data):/i.test(trimmed)) return;
      const resolved = resolveUrl2(trimmed, el2);
      if (!isUsableUrl(resolved)) return;
      if (/^(javascript|mailto|tel|data):/i.test(resolved)) return;
      const ext = extensionFromUrl(resolved);
      if (shouldExcludeFromUrlTab(resolved, ext, el2)) return;
      const linkTitle = linkTitleFromElement(el2);
      const urlLandmark = urlLandmarkFromElement(el2);
      if (allPageUrls.has(resolved)) {
        const existing = allPageUrls.get(resolved);
        if (existing) {
          const existingRank = urlLandmarkSortOrder(urlLandmarkForItem(existing));
          const nextRank = urlLandmarkSortOrder(urlLandmark);
          if (nextRank < existingRank) {
            if (el2) existing.element = el2;
            existing.urlLandmark = urlLandmark;
          } else if (!existing.element && el2) {
            existing.element = el2;
            existing.urlLandmark = urlLandmark;
          }
          if (linkTitle && (!existing.label || existing.label === resolved)) {
            existing.label = linkTitle;
          }
        }
        return;
      }
      allPageUrls.set(resolved, {
        category: "url",
        kind: kind || "url",
        url: resolved,
        element: el2,
        label: linkTitle || filenameFromUrl2(resolved) || resolved,
        ext,
        urlGroup: urlTabGroupForUrl(resolved, ext),
        urlLandmark
      });
    };
    const add = (raw) => {
      const resolved = resolveUrl2(raw.url, raw.element || null);
      if (!isUsableUrl(resolved)) return;
      if (raw.category !== "image" && raw.category !== "video") {
        notePageUrl(resolved, raw.element || null, raw.kind || raw.category);
      }
      if (raw.category === "url") return;
      if (byUrl.has(resolved)) {
        const existing = byUrl.get(resolved);
        const nextLandmark = urlLandmarkFromElement(raw.element || null);
        if (existing && urlLandmarkSortOrder(nextLandmark) < urlLandmarkSortOrder(urlLandmarkForItem(existing))) {
          if (raw.element) existing.element = raw.element;
          existing.urlLandmark = nextLandmark;
        }
        return;
      }
      const placeholderExts = /* @__PURE__ */ new Set(["img", "bg", "video"]);
      const rawExt = normalizeStoredExt(raw.ext);
      const fromUrl = normalizeStoredExt(extensionFromUrl(resolved));
      const fromMime = normalizeStoredExt(
        extFromMimeType(raw.mimeType) || extFromDataUrl(resolved)
      );
      const ext = (rawExt && !placeholderExts.has(rawExt) ? rawExt : "") || fromUrl || fromMime || rawExt || "";
      const label = raw.label || filenameFromUrl2(resolved) || resolved.slice(0, 48);
      const item = {
        category: raw.category,
        kind: raw.kind || raw.category,
        url: resolved,
        element: raw.element || null,
        label,
        ext,
        urlLandmark: urlLandmarkFromElement(raw.element || null)
      };
      if (typeof raw.width === "number" && raw.width > 0) item.width = raw.width;
      if (typeof raw.height === "number" && raw.height > 0) item.height = raw.height;
      if (typeof raw.fileSizeBytes === "number" && raw.fileSizeBytes > 0) item.fileSizeBytes = raw.fileSizeBytes;
      if (typeof raw.dpi === "number" && raw.dpi > 0) item.dpi = raw.dpi;
      if (raw.mimeType) item.mimeType = String(raw.mimeType);
      if (raw.posterUrl && isUsableUrl(raw.posterUrl)) {
        item.posterUrl = resolveUrl2(String(raw.posterUrl), raw.element || null);
      }
      if (raw.thumbUrl && isUsableUrl(raw.thumbUrl)) item.thumbUrl = String(raw.thumbUrl);
      byUrl.set(resolved, item);
    };
    const addByExtension = (rawUrl, el2, kind) => {
      notePageUrl(rawUrl, el2, kind || "link");
      const resolved = resolveUrl2(rawUrl, el2);
      if (!isUsableUrl(resolved)) return;
      const ext = extensionFromUrl(resolved);
      const category = categoryFromExtension(ext);
      if (!category) return;
      add({ category, kind: kind || category, url: resolved, element: el2, ext });
    };
    walkElements(root, (el2) => {
      const tag = String(el2.tagName || "").toUpperCase();
      const skipHarvest = tag === "SCRIPT" || tag === "STYLE" || tag === "IMG" || tag === "VIDEO" || tag === "PICTURE" || tag === "SOURCE" || tag === "TRACK" || tag === "LINK" && /\bstylesheet\b|\bpreload\b|\bmodulepreload\b/i.test(String(el2.getAttribute("rel") || ""));
      if (!skipHarvest) {
        const harvested = harvestAttrUrls(el2);
        for (const u of harvested) notePageUrl(u, el2, tag.toLowerCase());
        try {
          const data = el2.getAttribute("data") || "";
          if (isUsableUrl(data)) notePageUrl(data, el2, "data");
        } catch {
        }
        try {
          const action = el2.getAttribute("action") || "";
          if (isUsableUrl(action)) notePageUrl(action, el2, "action");
        } catch {
        }
        try {
          const cite = el2.getAttribute("cite") || "";
          if (isUsableUrl(cite)) notePageUrl(cite, el2, "cite");
        } catch {
        }
      }
      if (tag === "IMG") {
        const img = (
          /** @type {HTMLImageElement} */
          el2
        );
        const url = getImgUrl2(img);
        if (isUsableUrl(url)) {
          const resolved = resolveUrl2(url, el2);
          const ext = extensionFromUrl(resolved);
          const nw = Number(img.naturalWidth) || 0;
          const nh = Number(img.naturalHeight) || 0;
          add({
            category: "image",
            kind: "img",
            url: resolved,
            element: el2,
            ext: ext || "img",
            label: img.alt || filenameFromUrl2(resolved),
            width: nw > 0 ? nw : void 0,
            height: nh > 0 ? nh : void 0
          });
        }
      }
      if (tag === "INPUT" && String(
        /** @type {HTMLInputElement} */
        el2.type || ""
      ).toLowerCase() === "image") {
        try {
          const src = (
            /** @type {HTMLInputElement} */
            el2.src || el2.getAttribute("src") || ""
          );
          if (isUsableUrl(src)) {
            add({ category: "image", kind: "input-image", url: resolveUrl2(src, el2), element: el2 });
          }
        } catch {
        }
      }
      try {
        const style = window.getComputedStyle && window.getComputedStyle(el2);
        const bg = style ? String(style.backgroundImage || "") : "";
        if (bg && bg !== "none" && /url\s*\(/i.test(bg)) {
          const extracted = extractFirstBackgroundImageUrl(bg);
          if (extracted && isUsableUrl(extracted)) {
            const resolved = resolveUrl2(extracted, el2);
            const ext = extensionFromUrl(resolved);
            if (categoryFromExtension(ext) === "image" || /^(data:image|blob:)/i.test(resolved) || !ext) {
              if (categoryFromExtension(ext) !== "video" && categoryFromExtension(ext) !== "text") {
                add({
                  category: "image",
                  kind: "background",
                  url: resolved,
                  element: el2,
                  ext: ext || "bg"
                });
              }
            }
          }
        }
      } catch {
      }
      if (tag === "VIDEO") {
        const video = (
          /** @type {HTMLVideoElement} */
          el2
        );
        const posterRaw = getVideoPosterUrl2(video);
        const posterResolved = posterRaw ? resolveUrl2(posterRaw, el2) : "";
        if (posterResolved) {
          add({
            category: "image",
            kind: "video-poster",
            url: posterResolved,
            element: el2,
            label: filenameFromUrl2(posterResolved)
          });
        }
        const sources = getVideoSourceUrls(video);
        for (const src of sources) {
          const resolved = resolveUrl2(src, el2);
          const ext = extensionFromUrl(resolved);
          add({
            category: "video",
            kind: "video",
            url: resolved,
            element: el2,
            ext: ext || "video",
            label: filenameFromUrl2(resolved),
            posterUrl: posterResolved || void 0
          });
        }
      }
      if (tag === "SOURCE") {
        const typeAttr = String(el2.getAttribute("type") || "").toLowerCase();
        const urls = harvestAttrUrls(el2);
        for (const u of urls) {
          notePageUrl(u, el2, "source");
          const resolved = resolveUrl2(u, el2);
          if (!isUsableUrl(resolved)) continue;
          const ext = normalizeStoredExt(
            extensionFromUrl(resolved) || extFromMimeType(typeAttr) || extFromDataUrl(resolved)
          );
          const category = categoryFromExtension(ext) || categoryFromMimeType(typeAttr);
          if (!category) continue;
          add({
            category,
            kind: "source",
            url: resolved,
            element: el2,
            ext,
            mimeType: typeAttr || void 0
          });
        }
      }
      if (tag === "A" || tag === "AREA" || tag === "LINK" || tag === "EMBED" || tag === "OBJECT") {
        const urls = harvestAttrUrls(el2);
        for (const u of urls) addByExtension(u, el2, tag.toLowerCase());
        if (tag === "OBJECT" || tag === "EMBED") {
          try {
            const data = el2.getAttribute("data") || "";
            if (isUsableUrl(data)) addByExtension(data, el2, tag.toLowerCase());
          } catch {
          }
        }
      }
      if (tag !== "IMG" && tag !== "VIDEO" && tag !== "SOURCE") {
        const urls = harvestAttrUrls(el2);
        for (const u of urls) addByExtension(u, el2, "link");
      }
    });
    const pageTextItems = collectPageTextItems(root);
    const fontItems = collectPageFonts(root);
    return [...byUrl.values(), ...allPageUrls.values(), ...pageTextItems, ...fontItems];
  }
  var MAX_PAGE_TEXT_BLOCKS = 40;
  var MIN_PAGE_TEXT_CHARS = 40;
  var PAGE_TEXT_WHOLE_CONTAINERS = Object.freeze([
    { selector: "article", kind: "article", label: "Article" },
    { selector: '[role="article"]', kind: "article", label: "Article" },
    { selector: "main", kind: "main", label: "Main" },
    { selector: '[role="main"]', kind: "main", label: "Main" }
  ]);
  function collectPageTextItems(root = document) {
    const items = [];
    const seenText = /* @__PURE__ */ new Set();
    const seenEls = /* @__PURE__ */ new Set();
    const normalizeKey = (text) => String(text || "").replace(/\s+/g, " ").trim().toLowerCase();
    const isVisibleWhole = (el2) => {
      if (!el2 || isKeyPilotChrome3(el2)) return false;
      try {
        if (el2.getAttribute && el2.getAttribute("aria-hidden") === "true") return false;
        const style = window.getComputedStyle && window.getComputedStyle(el2);
        if (style) {
          if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") {
            return false;
          }
        }
      } catch {
      }
      return true;
    };
    const pushBlock = (kind, text, el2, label) => {
      const cleaned = String(text || "").replace(/\s+\n/g, "\n").replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
      if (cleaned.length < MIN_PAGE_TEXT_CHARS) return;
      const key = normalizeKey(cleaned);
      if (!key || seenText.has(key)) return;
      seenText.add(key);
      if (items.length >= MAX_PAGE_TEXT_BLOCKS) return;
      const preview = cleaned.length > 96 ? `${cleaned.slice(0, 95)}\u2026` : cleaned;
      items.push({
        category: "pageText",
        kind,
        url: "",
        element: el2,
        label: label || preview,
        ext: "txt",
        text: cleaned,
        fileSizeBytes: unescape(encodeURIComponent(cleaned)).length
      });
    };
    const scope = root && /** @type {any} */
    root.body || (root && root.nodeType === 1 ? root : null) || document;
    const articles = [];
    const mains = [];
    for (const { selector, kind } of PAGE_TEXT_WHOLE_CONTAINERS) {
      let nodes = [];
      try {
        nodes = Array.from(
          /** @type {any} */
          scope.querySelectorAll?.(selector) || []
        );
      } catch {
        nodes = [];
      }
      for (const el2 of nodes) {
        if (!(el2 instanceof Element) || seenEls.has(el2) || !isVisibleWhole(el2)) continue;
        let nestedInSame = false;
        for (const prev of kind === "article" ? articles : mains) {
          if (prev.contains(el2)) {
            nestedInSame = true;
            break;
          }
        }
        if (nestedInSame) continue;
        if (kind === "article") {
          for (let i = articles.length - 1; i >= 0; i--) {
            if (el2.contains(articles[i])) articles.splice(i, 1);
          }
          articles.push(el2);
        } else {
          for (let i = mains.length - 1; i >= 0; i--) {
            if (el2.contains(mains[i])) mains.splice(i, 1);
          }
          mains.push(el2);
        }
        seenEls.add(el2);
      }
    }
    const chosen = [];
    if (articles.length) {
      for (const el2 of articles) chosen.push({ el: el2, kind: "article", label: "Article" });
    } else {
      for (const el2 of mains) chosen.push({ el: el2, kind: "main", label: "Main" });
    }
    for (const { el: el2, kind, label } of chosen) {
      if (items.length >= MAX_PAGE_TEXT_BLOCKS) break;
      let text = "";
      try {
        text = String(
          /** @type {any} */
          el2.innerText || el2.textContent || ""
        );
      } catch {
        text = String(el2.textContent || "");
      }
      let title = label;
      try {
        const h = el2.querySelector("h1, h2, h3");
        const ht = h ? String(
          /** @type {any} */
          h.innerText || h.textContent || ""
        ).trim() : "";
        if (ht) title = ht.length > 72 ? `${ht.slice(0, 71)}\u2026` : ht;
      } catch {
      }
      pushBlock(kind, text, el2, title);
    }
    return items;
  }
  function groupPageMediaByCategory(items) {
    const groups = { image: [], video: [], text: [], url: [], pageText: [], font: [] };
    for (const item of items || []) {
      if (item?.category === "image") groups.image.push(item);
      else if (item?.category === "video") groups.video.push(item);
      else if (item?.category === "text") groups.text.push(item);
      else if (item?.category === "url") groups.url.push(item);
      else if (item?.category === "pageText") groups.pageText.push(item);
      else if (item?.category === "font") groups.font.push(item);
    }
    groups.url.sort(compareUrlTabItems);
    return groups;
  }
  var IMAGE_SIZE_GROUP_DEFS = Object.freeze([
    Object.freeze({ id: "xl", label: "Extra Large", sortOrder: 0, minEdge: 2561, maxEdge: null }),
    Object.freeze({ id: "large", label: "Large", sortOrder: 1, minEdge: 1281, maxEdge: 2560 }),
    Object.freeze({ id: "medium", label: "Medium", sortOrder: 2, minEdge: 641, maxEdge: 1280 }),
    Object.freeze({ id: "small", label: "Small", sortOrder: 3, minEdge: 257, maxEdge: 640 }),
    Object.freeze({ id: "tiny", label: "Tiny", sortOrder: 4, minEdge: 1, maxEdge: 256 }),
    Object.freeze({ id: "unknown", label: "Unknown size", sortOrder: 5, minEdge: 0, maxEdge: 0 })
  ]);
  function isVideoPosterItem(item) {
    return !!(item && item.category === "image" && item.kind === "video-poster");
  }
  function partitionImageItems(items) {
    const photos = [];
    const posters = [];
    for (const item of items || []) {
      if (!item || item.category !== "image") continue;
      if (isVideoPosterItem(item)) posters.push(item);
      else photos.push(item);
    }
    return { photos, posters };
  }
  function sortImageItems(items, mode, opts = {}) {
    const list = (items || []).filter(Boolean).slice();
    const m = mode === "size-asc" || mode === "page" ? mode : "size-desc";
    const byLandmark = !!opts?.prioritizeLandmarks;
    if (m === "page" && !byLandmark) return list;
    const dir = m === "size-asc" ? 1 : -1;
    list.sort((a, b) => {
      if (byLandmark) {
        const la = urlLandmarkSortOrder(urlLandmarkForItem(a));
        const lb = urlLandmarkSortOrder(urlLandmarkForItem(b));
        if (la !== lb) return la - lb;
      }
      if (m === "page") return 0;
      const aa = (Number(a.width) || 0) * (Number(a.height) || 0);
      const bb = (Number(b.width) || 0) * (Number(b.height) || 0);
      const aKnown = aa > 0;
      const bKnown = bb > 0;
      if (aKnown !== bKnown) return aKnown ? -1 : 1;
      if (aa !== bb) return dir * (aa - bb);
      const sa = Number(a.fileSizeBytes) || 0;
      const sb = Number(b.fileSizeBytes) || 0;
      if (sa !== sb) return dir * (sa - sb);
      return String(a.label || a.url || "").localeCompare(String(b.label || b.url || ""));
    });
    return list;
  }
  function formatFileSize(bytes) {
    const n = Number(bytes);
    if (!Number.isFinite(n) || n <= 0) return "\u2014";
    if (n < 1024) return `${Math.round(n)} B`;
    if (n < 1024 * 1024) return `${(n / 1024).toFixed(n < 10 * 1024 ? 1 : 0)} KB`;
    return `${(n / (1024 * 1024)).toFixed(n < 10 * 1024 * 1024 ? 1 : 0)} MB`;
  }
  function formatImageFileType(item) {
    const ext = String(item?.ext || "").toLowerCase();
    if (ext && ext !== "img" && ext !== "bg" && ext !== "video-poster") {
      return ext.toUpperCase() === "JPEG" ? "JPG" : ext.toUpperCase();
    }
    const mime = String(item?.mimeType || "").toLowerCase();
    if (mime.startsWith("image/")) {
      const sub = mime.slice(6).split("+")[0].split(";")[0];
      if (sub === "jpeg") return "JPG";
      if (sub === "svg+xml" || sub === "svg") return "SVG";
      if (sub) return sub.toUpperCase();
    }
    if (/^data:image\//i.test(item?.url || "")) {
      const m = String(item.url).match(/^data:image\/([a-z0-9.+-]+)/i);
      if (m) {
        const sub = m[1].toLowerCase();
        if (sub === "jpeg") return "JPG";
        if (sub === "svg+xml") return "SVG";
        return sub.toUpperCase();
      }
    }
    return "IMG";
  }
  function formatImageDimensions(item) {
    const w = Number(item?.width) || 0;
    const h = Number(item?.height) || 0;
    if (w > 0 && h > 0) return `${w}\xD7${h}`;
    return "\u2014";
  }
  function formatImageDpi(item) {
    const d = Number(item?.dpi);
    if (!Number.isFinite(d) || d <= 0) return "\u2014";
    return `${Math.round(d)} dpi`;
  }
  function dataUrlByteLength(url) {
    if (!url || !/^data:/i.test(url)) return null;
    try {
      const comma = url.indexOf(",");
      if (comma < 0) return null;
      const meta = url.slice(0, comma);
      const data = url.slice(comma + 1);
      if (/;base64/i.test(meta)) {
        const len = data.length;
        const pads = data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0;
        return Math.max(0, Math.floor(len * 3 / 4) - pads);
      }
      return unescape(encodeURIComponent(data)).length;
    } catch {
      return null;
    }
  }
  async function enrichImageMetadata(item) {
    if (!item || item.category !== "image" || !item.url) return item;
    try {
      const el2 = item.element;
      if (el2 && el2.tagName === "IMG") {
        const img = (
          /** @type {HTMLImageElement} */
          el2
        );
        const nw = Number(img.naturalWidth) || 0;
        const nh = Number(img.naturalHeight) || 0;
        if (nw > 0 && nh > 0) {
          item.width = nw;
          item.height = nh;
        }
      }
    } catch {
    }
    const dataBytes = dataUrlByteLength(item.url);
    if (dataBytes != null) item.fileSizeBytes = dataBytes;
    const needDims = !(Number(item.width) > 0 && Number(item.height) > 0);
    const needSize = !(Number(item.fileSizeBytes) > 0);
    const needDpi = !(Number(item.dpi) > 0);
    if (needDims) {
      try {
        const dims = await loadImageDimensions(item.url);
        if (dims) {
          item.width = dims.width;
          item.height = dims.height;
        }
      } catch {
      }
    }
    if ((needSize || needDpi) && !/^data:/i.test(item.url)) {
      try {
        const meta = await fetchImageNetworkMeta(item.url, { needSize, needDpi });
        if (meta.fileSizeBytes != null && !(Number(item.fileSizeBytes) > 0)) {
          item.fileSizeBytes = meta.fileSizeBytes;
        }
        if (meta.dpi != null && !(Number(item.dpi) > 0)) item.dpi = meta.dpi;
        if (meta.mimeType && !item.mimeType) item.mimeType = meta.mimeType;
        if (meta.ext && (!item.ext || item.ext === "img" || item.ext === "bg")) {
          item.ext = meta.ext;
        }
      } catch {
      }
    }
    if (needDpi && /^data:/i.test(item.url) && !(Number(item.dpi) > 0)) {
      try {
        const res = await fetch(item.url);
        const buf = await res.arrayBuffer();
        const dpi = parseDpiFromImageBytes(buf.slice(0, Math.min(buf.byteLength, 65536)));
        if (dpi) item.dpi = dpi;
        if (!item.mimeType) {
          const ct = res.headers.get("content-type");
          if (ct) item.mimeType = ct;
        }
      } catch {
      }
    }
    return item;
  }
  function loadImageDimensions(url) {
    return new Promise((resolve) => {
      try {
        const img = new Image();
        let settled = false;
        const done = (val) => {
          if (settled) return;
          settled = true;
          resolve(val);
        };
        img.onload = () => {
          const w = Number(img.naturalWidth) || 0;
          const h = Number(img.naturalHeight) || 0;
          done(w > 0 && h > 0 ? { width: w, height: h } : null);
        };
        img.onerror = () => done(null);
        try {
          img.decoding = "async";
        } catch {
        }
        img.src = url;
        setTimeout(() => done(null), 8e3);
      } catch {
        resolve(null);
      }
    });
  }
  async function fetchImageNetworkMeta(url, opts = {}) {
    const out = {};
    const needSize = opts.needSize !== false;
    const needDpi = !!opts.needDpi;
    if (needSize) {
      try {
        const head = await fetch(url, { method: "HEAD", credentials: "omit", cache: "force-cache" });
        if (head.ok) {
          const len = head.headers.get("content-length");
          if (len && /^\d+$/.test(len)) out.fileSizeBytes = Number(len);
          const ct = head.headers.get("content-type");
          if (ct) {
            out.mimeType = ct;
            const fromMime = extFromMimeType(ct);
            if (fromMime) out.ext = fromMime;
          }
        }
      } catch {
      }
    }
    if (needDpi || out.fileSizeBytes == null) {
      try {
        let res = await fetch(url, {
          method: "GET",
          credentials: "omit",
          cache: "force-cache",
          headers: needDpi ? { Range: "bytes=0-65535" } : void 0
        });
        if (!res.ok && res.status !== 206) {
          res = await fetch(url, { method: "GET", credentials: "omit", cache: "force-cache" });
        }
        if (res.ok || res.status === 206) {
          const ct = res.headers.get("content-type");
          if (ct && !out.mimeType) out.mimeType = ct;
          if (ct && !out.ext) {
            const fromMime = extFromMimeType(ct);
            if (fromMime) out.ext = fromMime;
          }
          if (out.fileSizeBytes == null) {
            const cr = res.headers.get("content-range");
            const m = cr && cr.match(/\/(\d+)\s*$/);
            if (m) out.fileSizeBytes = Number(m[1]);
            else {
              const len = res.headers.get("content-length");
              if (len && /^\d+$/.test(len) && res.status !== 206) out.fileSizeBytes = Number(len);
            }
          }
          if (needDpi) {
            const buf = await res.arrayBuffer();
            const dpi = parseDpiFromImageBytes(buf);
            if (dpi) out.dpi = dpi;
            if (out.fileSizeBytes == null && res.status !== 206) {
              out.fileSizeBytes = buf.byteLength;
            }
          }
        }
      } catch {
      }
    }
    return out;
  }
  async function enrichImageItems(items, opts = {}) {
    const list = (items || []).filter((i) => i && i.category === "image");
    const concurrency = Math.max(1, Math.min(8, Number(opts.concurrency) || 4));
    let cursor = 0;
    const worker = async () => {
      while (cursor < list.length) {
        const index = cursor++;
        const item = list[index];
        try {
          await enrichImageMetadata(item);
        } catch {
        }
        if (typeof opts.onProgress === "function") {
          try {
            opts.onProgress(item, index);
          } catch {
          }
        }
      }
    };
    await Promise.all(Array.from({ length: Math.min(concurrency, list.length) }, () => worker()));
    return list;
  }
  async function resolveVideoThumbnail(item) {
    if (!item || item.category !== "video") return null;
    if (item.thumbUrl && isUsableUrl(item.thumbUrl)) return item.thumbUrl;
    if (item.posterUrl && isUsableUrl(item.posterUrl)) {
      item.thumbUrl = item.posterUrl;
      return item.posterUrl;
    }
    try {
      const el2 = item.element;
      if (el2 && String(el2.tagName || "").toUpperCase() === "VIDEO") {
        const fromEl = await captureFrameFromVideoElement(
          /** @type {HTMLVideoElement} */
          el2
        );
        if (fromEl) {
          item.thumbUrl = fromEl;
          return fromEl;
        }
      }
    } catch {
    }
    try {
      const fromUrl = await captureFrameFromVideoUrl(item.url);
      if (fromUrl) {
        item.thumbUrl = fromUrl;
        return fromUrl;
      }
    } catch {
    }
    return null;
  }
  async function captureFrameFromVideoElement(video) {
    if (!video) return null;
    try {
      const ready = typeof video.readyState === "number" ? video.readyState : 0;
      const w = Number(video.videoWidth) || 0;
      const h = Number(video.videoHeight) || 0;
      if (ready < 2 || w < 1 || h < 1) return null;
      return canvasFrameToObjectUrl(video, w, h);
    } catch {
      return null;
    }
  }
  function captureFrameFromVideoUrl(url) {
    if (!isUsableUrl(url) || /^data:/i.test(url)) return Promise.resolve(null);
    return new Promise((resolve) => {
      let settled = false;
      const done = (val) => {
        if (settled) return;
        settled = true;
        try {
          video.removeAttribute("src");
          video.load();
        } catch {
        }
        try {
          video.remove();
        } catch {
        }
        resolve(val);
      };
      const video = document.createElement("video");
      video.muted = true;
      video.playsInline = true;
      video.preload = "auto";
      try {
        video.crossOrigin = "anonymous";
      } catch {
      }
      const timer = setTimeout(() => done(null), 1e4);
      const seekAndCapture = async () => {
        try {
          const dur = Number(video.duration);
          const t = Number.isFinite(dur) && dur > 0 ? Math.min(0.5, Math.max(0.05, dur * 0.05)) : 0.1;
          if (Math.abs((Number(video.currentTime) || 0) - t) > 0.01) {
            await new Promise((res, rej) => {
              const onSeeked = () => {
                cleanup();
                res(void 0);
              };
              const onErr = () => {
                cleanup();
                rej(new Error("seek"));
              };
              const cleanup = () => {
                video.removeEventListener("seeked", onSeeked);
                video.removeEventListener("error", onErr);
              };
              video.addEventListener("seeked", onSeeked, { once: true });
              video.addEventListener("error", onErr, { once: true });
              try {
                video.currentTime = t;
              } catch (e) {
                cleanup();
                rej(e);
              }
            });
          }
          const w = Number(video.videoWidth) || 0;
          const h = Number(video.videoHeight) || 0;
          if (w < 1 || h < 1) {
            clearTimeout(timer);
            done(null);
            return;
          }
          const urlOut = await canvasFrameToObjectUrl(video, w, h);
          clearTimeout(timer);
          done(urlOut);
        } catch {
          clearTimeout(timer);
          done(null);
        }
      };
      video.addEventListener("loadeddata", () => {
        seekAndCapture();
      }, { once: true });
      video.addEventListener("error", () => {
        clearTimeout(timer);
        done(null);
      }, { once: true });
      try {
        video.src = url;
      } catch {
        clearTimeout(timer);
        done(null);
      }
    });
  }
  async function canvasFrameToObjectUrl(video, width, height) {
    try {
      const canvas = document.createElement("canvas");
      canvas.width = Math.max(1, width);
      canvas.height = Math.max(1, height);
      const ctx = canvas.getContext("2d");
      if (!ctx) return null;
      ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
      const blob = await new Promise((resolve) => {
        try {
          canvas.toBlob(resolve, "image/jpeg", 0.85);
        } catch {
          resolve(null);
        }
      });
      if (!blob || blob.size < 1) return null;
      return URL.createObjectURL(blob);
    } catch {
      return null;
    }
  }
  var PAGE_MEDIA_EXTENSIONS = Object.freeze({
    image: IMAGE_EXTS,
    video: VIDEO_EXTS,
    text: TEXT_EXTS
  });

  // src/ui/page-media-overlay.js
  init_constants();
  init_storage();

  // src/ui/segmented-control.js
  init_constants();
  function createSegmentedControl(config = {}) {
    const doc = config.doc || document;
    const options = Array.isArray(config.options) ? config.options.filter(Boolean) : [];
    const className = typeof config.className === "string" && config.className.trim() ? config.className.trim() : "kp-segmented-control";
    let currentValue = null;
    if (config.value != null && options.some((o) => o.value === config.value)) {
      currentValue = String(config.value);
    } else if (options.length) {
      currentValue = String(options[0].value);
    }
    const root = doc.createElement("div");
    root.className = className;
    root.setAttribute("role", "radiogroup");
    if (config.ariaLabel) {
      root.setAttribute("aria-label", config.ariaLabel);
    }
    root.style.cssText = `
    display: inline-flex;
    align-items: stretch;
    flex-shrink: 0;
    border: 1px solid ${NCT_DARK_UI_COLORS.panelEdge};
    border-radius: ${NCT_DARK_UI_BTN_RADIUS};
    overflow: hidden;
    background: rgba(0, 0, 0, 0.25);
    font-family: ${KP_UI_FONT};
  `;
    const buttons = [];
    const applySelectionStyles = () => {
      for (const btn of buttons) {
        const selected = btn.dataset.value === currentValue;
        btn.setAttribute("aria-checked", selected ? "true" : "false");
        btn.dataset.selected = selected ? "1" : "0";
        btn.style.background = selected ? NCT_DARK_UI_SELECTED_TINT : "transparent";
        btn.style.color = selected ? NCT_DARK_UI_SELECTED_TEXT : NCT_DARK_UI_COLORS.fgDim;
        btn.style.fontWeight = selected ? "600" : "500";
      }
    };
    const setValue = (value, opts = {}) => {
      const next = value != null ? String(value) : null;
      if (!options.some((o) => o.value === next)) return;
      const prev = currentValue;
      if (prev === next) {
        applySelectionStyles();
        return;
      }
      currentValue = next;
      applySelectionStyles();
      if (!opts.silent && typeof config.onChange === "function") {
        try {
          config.onChange(currentValue, prev);
        } catch {
        }
      }
    };
    options.forEach((opt, index) => {
      const btn = doc.createElement("button");
      btn.type = "button";
      btn.className = "kp-segmented-control-btn";
      btn.dataset.value = String(opt.value);
      btn.setAttribute("role", "radio");
      btn.textContent = opt.label != null ? String(opt.label) : String(opt.value);
      if (opt.title) btn.title = opt.title;
      btn.setAttribute(
        "aria-label",
        opt.ariaLabel || opt.title || (opt.label != null ? String(opt.label) : String(opt.value))
      );
      btn.style.cssText = `
      display: inline-flex;
      align-items: center;
      justify-content: center;
      margin: 0;
      padding: 3px 8px;
      border: none;
      border-right: ${index < options.length - 1 ? `1px solid ${NCT_DARK_UI_COLORS.panelEdge}` : "none"};
      border-radius: 0;
      background: transparent;
      color: ${NCT_DARK_UI_COLORS.fgDim};
      font-size: 11px;
      font-weight: 500;
      font-family: ${KP_UI_FONT};
      line-height: 1.2;
      letter-spacing: normal;
      text-transform: none;
      cursor: pointer;
      white-space: nowrap;
      transition: background 0.12s ease, color 0.12s ease;
    `;
      btn.addEventListener("mouseenter", () => {
        if (btn.dataset.selected === "1") return;
        btn.style.background = NCT_DARK_UI_HOVER_TINT;
        btn.style.color = NCT_DARK_UI_COLORS.fg;
      });
      btn.addEventListener("mouseleave", () => {
        applySelectionStyles();
      });
      btn.addEventListener("click", (e) => {
        try {
          e.preventDefault();
          e.stopPropagation();
        } catch {
        }
        setValue(String(opt.value));
      });
      buttons.push(btn);
      root.appendChild(btn);
    });
    applySelectionStyles();
    return {
      root,
      buttons,
      getValue: () => currentValue,
      setValue,
      getInteractiveElements: () => buttons.slice()
    };
  }

  // src/ui/page-media-overlay.js
  var OVERLAY_ID = "kpv2-page-media-overlay";
  var IMAGE_SCALE_STORAGE_KEY = "kp_page_media_image_scale";
  var IMAGE_SCALE_SLIDER_MIN = 1;
  var IMAGE_SCALE_SLIDER_MAX = 2.5;
  var IMAGE_SCALE_SLIDER_STEP = 0.25;
  var IMAGE_SCALE_DEFAULT = 1.5;
  var IMAGE_ASPECT_STORAGE_KEY = "kp_page_media_image_aspect";
  var IMAGE_ASPECT_DEFAULT = (
    /** @type {ImageAspectMode} */
    "square"
  );
  var IMAGE_SORT_STORAGE_KEY = "kp_page_media_image_sort";
  var IMAGE_SORT_DEFAULT = (
    /** @type {ImageSortMode} */
    "size-desc"
  );
  var IMAGE_LANDMARK_STORAGE_KEY = "kp_page_media_image_landmark";
  var IMAGE_LANDMARK_DEFAULT = true;
  var URL_VIEW_STORAGE_KEY = "kp_page_media_url_view";
  var URL_VIEW_DEFAULT = (
    /** @type {UrlViewMode} */
    "table"
  );
  var HIDDEN_PAGE_MEDIA_TABS = /* @__PURE__ */ new Set(["pageText", "url"]);
  var _overlay = null;
  var _onClose2 = null;
  var _keyHandler = null;
  var _activeTab = "image";
  var _items = [];
  var _fullViewIndex = 0;
  var _prevOverflow = null;
  var _enrichGen = 0;
  var _cardByItem = /* @__PURE__ */ new WeakMap();
  var _imageFlatList = [];
  var _notify = () => {
  };
  var _onSendToMediaLibrary = async () => {
  };
  var _imageScaleSlider = IMAGE_SCALE_DEFAULT;
  var _imageAspectMode = IMAGE_ASPECT_DEFAULT;
  var _aspectControl = null;
  var _imageSortMode = IMAGE_SORT_DEFAULT;
  var _sortControl = null;
  var _imagePrioritizeLandmarks = IMAGE_LANDMARK_DEFAULT;
  var _imageLandmarkCheck = null;
  var _urlViewMode = URL_VIEW_DEFAULT;
  var _urlViewControl = null;
  var _urlTableExpanded = /* @__PURE__ */ new Set();
  var _urlTableExpandedReady = false;
  function getOverlayRoot() {
    return _overlay?.shadowRoot || _overlay;
  }
  function isPageMediaOverlayOpen() {
    return !!_overlay && !!document.getElementById(OVERLAY_ID);
  }
  function requestClosePageMediaOverlay() {
    if (!isPageMediaOverlayOpen()) return false;
    const modal = getOverlayRoot()?.querySelector(".kpv2-page-media-fullview");
    if (modal && modal.classList.contains("is-open")) {
      closeFullView();
      return true;
    }
    closePageMediaOverlay();
    return true;
  }
  function closePageMediaOverlay() {
    if (_keyHandler) {
      try {
        document.removeEventListener("keydown", _keyHandler, true);
      } catch {
      }
      _keyHandler = null;
    }
    if (_regroupTimer != null) {
      try {
        clearTimeout(_regroupTimer);
      } catch {
      }
      _regroupTimer = null;
    }
    if (_overlay) {
      try {
        _overlay.remove();
      } catch {
      }
      _overlay = null;
    }
    if (_prevOverflow != null) {
      try {
        document.body.style.overflow = _prevOverflow;
      } catch {
      }
      _prevOverflow = null;
    }
    const cb = _onClose2;
    _onClose2 = null;
    _items = [];
    _imageFlatList = [];
    _cardByItem = /* @__PURE__ */ new WeakMap();
    _enrichGen += 1;
    _notify = () => {
    };
    _onSendToMediaLibrary = async () => {
    };
    _imageScaleSlider = IMAGE_SCALE_DEFAULT;
    _imageAspectMode = IMAGE_ASPECT_DEFAULT;
    _aspectControl = null;
    _imageSortMode = IMAGE_SORT_DEFAULT;
    _sortControl = null;
    _imagePrioritizeLandmarks = IMAGE_LANDMARK_DEFAULT;
    _imageLandmarkCheck = null;
    _urlViewControl = null;
    _urlTableExpanded = /* @__PURE__ */ new Set();
    _urlTableExpandedReady = false;
    if (typeof cb === "function") {
      try {
        cb();
      } catch {
      }
    }
  }
  function normalizeImageScaleSlider(raw) {
    let n = Number(raw);
    if (!Number.isFinite(n)) return IMAGE_SCALE_DEFAULT;
    if (n >= 0 && n < IMAGE_SCALE_SLIDER_MIN) {
      n = 1 + n / IMAGE_SCALE_SLIDER_MAX * (IMAGE_SCALE_SLIDER_MAX - 1);
    }
    const clamped = Math.min(IMAGE_SCALE_SLIDER_MAX, Math.max(IMAGE_SCALE_SLIDER_MIN, n));
    const steps = Math.round((clamped - IMAGE_SCALE_SLIDER_MIN) / IMAGE_SCALE_SLIDER_STEP);
    return Math.round((IMAGE_SCALE_SLIDER_MIN + steps * IMAGE_SCALE_SLIDER_STEP) * 100) / 100;
  }
  function formatImageScaleReadout(slider) {
    const v = normalizeImageScaleSlider(slider);
    const rounded = Math.round(v * 100) / 100;
    const text = rounded % 1 === 0 ? String(rounded) : rounded.toFixed(2).replace(/0+$/, "").replace(/\.$/, "");
    return `${text}\xD7`;
  }
  async function loadImageScalePreference() {
    try {
      const stored = await storageGetValue(IMAGE_SCALE_STORAGE_KEY, IMAGE_SCALE_DEFAULT);
      _imageScaleSlider = normalizeImageScaleSlider(
        stored === void 0 || stored === null ? IMAGE_SCALE_DEFAULT : stored
      );
    } catch {
      _imageScaleSlider = IMAGE_SCALE_DEFAULT;
    }
  }
  function normalizeImageAspectMode(raw) {
    return raw === "original" ? "original" : "square";
  }
  async function loadImageAspectPreference() {
    try {
      const stored = await storageGetValue(IMAGE_ASPECT_STORAGE_KEY, IMAGE_ASPECT_DEFAULT);
      _imageAspectMode = normalizeImageAspectMode(stored);
    } catch {
      _imageAspectMode = IMAGE_ASPECT_DEFAULT;
    }
  }
  function persistImageAspectPreference(mode) {
    _imageAspectMode = normalizeImageAspectMode(mode);
    try {
      void storageSetValue(IMAGE_ASPECT_STORAGE_KEY, _imageAspectMode);
    } catch {
    }
  }
  function normalizeImageSortMode(raw) {
    if (raw === "size-asc" || raw === "page") return raw;
    return "size-desc";
  }
  async function loadImageSortPreference() {
    try {
      const stored = await storageGetValue(IMAGE_SORT_STORAGE_KEY, IMAGE_SORT_DEFAULT);
      _imageSortMode = normalizeImageSortMode(stored);
    } catch {
      _imageSortMode = IMAGE_SORT_DEFAULT;
    }
  }
  function persistImageSortPreference(mode) {
    _imageSortMode = normalizeImageSortMode(mode);
    try {
      void storageSetValue(IMAGE_SORT_STORAGE_KEY, _imageSortMode);
    } catch {
    }
  }
  function normalizeImageLandmarkPreference(raw) {
    if (raw === false || raw === "false" || raw === 0) return false;
    if (raw === true || raw === "true" || raw === 1) return true;
    return IMAGE_LANDMARK_DEFAULT;
  }
  async function loadImageLandmarkPreference() {
    try {
      const stored = await storageGetValue(IMAGE_LANDMARK_STORAGE_KEY, IMAGE_LANDMARK_DEFAULT);
      _imagePrioritizeLandmarks = normalizeImageLandmarkPreference(
        stored === void 0 || stored === null ? IMAGE_LANDMARK_DEFAULT : stored
      );
    } catch {
      _imagePrioritizeLandmarks = IMAGE_LANDMARK_DEFAULT;
    }
  }
  function persistImageLandmarkPreference(on) {
    _imagePrioritizeLandmarks = !!on;
    try {
      void storageSetValue(IMAGE_LANDMARK_STORAGE_KEY, _imagePrioritizeLandmarks);
    } catch {
    }
  }
  function applyImageAspectMode(mode = _imageAspectMode) {
    _imageAspectMode = normalizeImageAspectMode(mode);
    if (!_overlay) return;
    const content = getOverlayRoot()?.querySelector(".kpv2-page-media-content");
    if (content instanceof HTMLElement) {
      content.classList.toggle("is-aspect-square", _imageAspectMode === "square");
      content.classList.toggle("is-aspect-original", _imageAspectMode === "original");
    }
    if (_aspectControl) {
      _aspectControl.setValue(_imageAspectMode, { silent: true });
    }
    if (_sortControl) {
      _sortControl.setValue(_imageSortMode, { silent: true });
    }
    requestAnimationFrame(() => {
      if (!_overlay) return;
      const thumbs = getOverlayRoot()?.querySelectorAll(".kpv2-page-media-card-image .kpv2-page-media-thumb") || [];
      for (const thumb of thumbs) {
        if (!(thumb instanceof HTMLImageElement)) continue;
        const wrap = thumb.closest(".kpv2-page-media-thumb-wrap");
        if (wrap instanceof HTMLElement) applyThumbFitMode(thumb, wrap);
      }
    });
  }
  function updateAspectToolbarVisibility() {
    if (!_overlay) return;
    const bar = getOverlayRoot()?.querySelector(".kpv2-page-media-aspect-bar");
    if (!(bar instanceof HTMLElement)) return;
    const show = _activeTab === "image";
    bar.hidden = !show;
    bar.setAttribute("aria-hidden", show ? "false" : "true");
  }
  function updateUrlViewToolbarVisibility() {
    if (!_overlay) return;
    const bar = getOverlayRoot()?.querySelector(".kpv2-page-media-url-view-bar");
    if (!(bar instanceof HTMLElement)) return;
    const show = _activeTab === "url";
    bar.hidden = !show;
    bar.setAttribute("aria-hidden", show ? "false" : "true");
  }
  function normalizeUrlViewMode(raw) {
    return raw === "list" ? "list" : "table";
  }
  async function loadUrlViewPreference() {
    try {
      const stored = await storageGetValue(URL_VIEW_STORAGE_KEY, URL_VIEW_DEFAULT);
      _urlViewMode = normalizeUrlViewMode(stored);
    } catch {
      _urlViewMode = URL_VIEW_DEFAULT;
    }
  }
  function persistUrlViewPreference(mode) {
    _urlViewMode = normalizeUrlViewMode(mode);
    try {
      void storageSetValue(URL_VIEW_STORAGE_KEY, _urlViewMode);
    } catch {
    }
  }
  function isUrlTableGroupExpanded(key) {
    return !!_urlTableExpanded?.has(key);
  }
  function toggleUrlTableGroup(key) {
    if (!_urlTableExpanded) _urlTableExpanded = /* @__PURE__ */ new Set();
    if (_urlTableExpanded.has(key)) _urlTableExpanded.delete(key);
    else _urlTableExpanded.add(key);
    _urlTableExpandedReady = true;
    if (_activeTab === "url") renderGrid();
  }
  function ensureUrlTableExpandedDefaults(domainKeys) {
    if (_urlTableExpandedReady) return;
    _urlTableExpanded = new Set(
      (Array.isArray(domainKeys) ? domainKeys : []).filter((k) => typeof k === "string" && k)
    );
    _urlTableExpandedReady = true;
  }
  function buildUrlViewToolbar() {
    const bar = document.createElement("div");
    bar.className = "kpv2-page-media-url-view-bar";
    bar.hidden = _activeTab !== "url";
    bar.setAttribute("aria-hidden", _activeTab === "url" ? "false" : "true");
    const label = document.createElement("span");
    label.className = "kpv2-page-media-url-view-label";
    label.textContent = "URL View:";
    _urlViewControl = createSegmentedControl({
      className: "kpv2-page-media-url-view-seg",
      ariaLabel: "URL list layout",
      value: _urlViewMode,
      options: [
        { value: "table", label: "Table", title: "Hierarchical table by domain and path" },
        { value: "list", label: "List", title: "Horizontal cards grouped by domain and path" }
      ],
      onChange: (value) => {
        const mode = normalizeUrlViewMode(value);
        persistUrlViewPreference(mode);
        if (_activeTab === "url") renderGrid();
      }
    });
    bar.appendChild(label);
    bar.appendChild(_urlViewControl.root);
    return bar;
  }
  function buildImageAspectToolbar() {
    const bar = document.createElement("div");
    bar.className = "kpv2-page-media-aspect-bar";
    bar.hidden = _activeTab !== "image";
    bar.setAttribute("aria-hidden", _activeTab === "image" ? "false" : "true");
    const label = document.createElement("span");
    label.className = "kpv2-page-media-aspect-label";
    label.textContent = "Card Aspect Ratio:";
    _aspectControl = createSegmentedControl({
      className: "kpv2-page-media-aspect-seg",
      ariaLabel: "Card aspect ratio",
      value: _imageAspectMode,
      options: [
        { value: "square", label: "Square Crop", title: "Square crop thumbnails" },
        { value: "original", label: "Img Original", title: "Keep each image\u2019s original aspect ratio" }
      ],
      onChange: (value) => {
        const mode = normalizeImageAspectMode(value);
        persistImageAspectPreference(mode);
        applyImageAspectMode(mode);
      }
    });
    bar.appendChild(label);
    bar.appendChild(_aspectControl.root);
    const sortWrap = document.createElement("div");
    sortWrap.className = "kpv2-page-media-sort-wrap";
    const sortLabel = document.createElement("span");
    sortLabel.className = "kpv2-page-media-aspect-label";
    sortLabel.textContent = "Sort:";
    _sortControl = createSegmentedControl({
      className: "kpv2-page-media-sort-seg",
      ariaLabel: "Image sort order",
      value: _imageSortMode,
      options: [
        { value: "size-desc", label: "Size:Descending", title: "Largest pixel area first" },
        { value: "size-asc", label: "Size:Ascending", title: "Smallest pixel area first" },
        { value: "page", label: "Page Order", title: "Order images appear on the page" }
      ],
      onChange: (value) => {
        const mode = normalizeImageSortMode(value);
        persistImageSortPreference(mode);
        if (_activeTab === "image") renderImageGrid({ preserveScroll: true });
      }
    });
    sortWrap.appendChild(sortLabel);
    sortWrap.appendChild(_sortControl.root);
    bar.appendChild(sortWrap);
    const landmarkWrap = document.createElement("label");
    landmarkWrap.className = "kpv2-page-media-landmark-check";
    landmarkWrap.title = "Show images in articles and main content before site header, nav, and footer";
    _imageLandmarkCheck = document.createElement("input");
    _imageLandmarkCheck.type = "checkbox";
    _imageLandmarkCheck.checked = _imagePrioritizeLandmarks;
    _imageLandmarkCheck.setAttribute("aria-label", "Prioritize article and main content images");
    _imageLandmarkCheck.addEventListener("change", () => {
      persistImageLandmarkPreference(!!_imageLandmarkCheck?.checked);
      if (_activeTab === "image") renderImageGrid({ preserveScroll: true });
    });
    const landmarkText = document.createElement("span");
    landmarkText.textContent = "Article first";
    landmarkWrap.appendChild(_imageLandmarkCheck);
    landmarkWrap.appendChild(landmarkText);
    bar.appendChild(landmarkWrap);
    return bar;
  }
  function persistImageScalePreference(slider) {
    const v = normalizeImageScaleSlider(slider);
    _imageScaleSlider = v;
    try {
      void storageSetValue(IMAGE_SCALE_STORAGE_KEY, v);
    } catch {
    }
  }
  function applyImageScale(slider = _imageScaleSlider) {
    _imageScaleSlider = normalizeImageScaleSlider(slider);
    if (!_overlay) return;
    const content = getOverlayRoot()?.querySelector(".kpv2-page-media-content");
    if (content instanceof HTMLElement) {
      content.style.setProperty("--kpv2-pm-image-scale", String(_imageScaleSlider));
    }
    const readout = getOverlayRoot()?.querySelector(".kpv2-page-media-scale-value");
    if (readout) readout.textContent = formatImageScaleReadout(_imageScaleSlider);
    const range = getOverlayRoot()?.querySelector(".kpv2-page-media-scale-range");
    if (range instanceof HTMLInputElement) {
      range.value = String(_imageScaleSlider);
    }
    requestAnimationFrame(() => {
      if (!_overlay) return;
      const thumbs = getOverlayRoot()?.querySelectorAll(".kpv2-page-media-thumb") || [];
      for (const thumb of thumbs) {
        if (!(thumb instanceof HTMLImageElement)) continue;
        const wrap = thumb.closest(".kpv2-page-media-thumb-wrap");
        if (wrap instanceof HTMLElement) applyThumbFitMode(thumb, wrap);
      }
    });
  }
  function buildImageScaleControl() {
    const { root } = createNctDarkUiScaleSlider({
      label: "Scale",
      title: "Overlay content scale (1\xD7\u20132.5\xD7, all tabs)",
      ariaLabel: "Page Media content scale",
      min: IMAGE_SCALE_SLIDER_MIN,
      max: IMAGE_SCALE_SLIDER_MAX,
      step: IMAGE_SCALE_SLIDER_STEP,
      value: _imageScaleSlider,
      minLabel: "1\xD7",
      maxLabel: "2.5\xD7",
      formatValue: formatImageScaleReadout,
      rangeWidth: "88px",
      // Styles come from the overlay stylesheet via getNctDarkUiScaleSliderCss().
      embedStyles: false,
      onInput: (next) => {
        applyImageScale(next);
        persistImageScalePreference(next);
      }
    });
    root.classList.add("kpv2-page-media-scale");
    root.querySelector(".kp-nct-scale-range")?.classList.add("kpv2-page-media-scale-range");
    root.querySelector(".kp-nct-scale-value")?.classList.add("kpv2-page-media-scale-value");
    return root;
  }
  async function openPageMediaOverlay({ items, onClose, onNotify, onSendToMediaLibrary } = (
    /** @type {any} */
    {}
  )) {
    closePageMediaOverlay();
    _items = Array.isArray(items) ? items.slice() : [];
    _onClose2 = typeof onClose === "function" ? onClose : null;
    _notify = typeof onNotify === "function" ? onNotify : () => {
    };
    _onSendToMediaLibrary = typeof onSendToMediaLibrary === "function" ? onSendToMediaLibrary : async () => {
      _notify("Could not send to Media Library", "error");
    };
    await loadImageScalePreference();
    await loadImageAspectPreference();
    await loadImageSortPreference();
    await loadImageLandmarkPreference();
    await loadUrlViewPreference();
    _urlTableExpanded = /* @__PURE__ */ new Set();
    _urlTableExpandedReady = false;
    const groups = groupPageMediaByCategory(_items);
    if (groups.image.length) _activeTab = "image";
    else if (groups.video.length) _activeTab = "video";
    else if (!HIDDEN_PAGE_MEDIA_TABS.has("pageText") && groups.pageText.length) _activeTab = "pageText";
    else if (groups.text.length) _activeTab = "text";
    else if (groups.font.length) _activeTab = "font";
    else if (!HIDDEN_PAGE_MEDIA_TABS.has("url") && groups.url.length) _activeTab = "url";
    else _activeTab = "image";
    const overlay = document.createElement("div");
    overlay.id = OVERLAY_ID;
    overlay.className = "kpv2-page-media-overlay";
    overlay.setAttribute("role", "dialog");
    overlay.setAttribute("aria-label", "Page Media");
    overlay.style.cssText = `
    position: fixed;
    inset: 0;
    z-index: ${Z_INDEX.POPUP_PANEL_MAX + 20};
  `;
    const shadowRoot = ensureOpenChromeShadow(overlay, { id: "page-media" });
    const mount = shadowRoot || overlay;
    ensureStyles3(mount);
    const backdrop = document.createElement("div");
    backdrop.className = NCT_DARK_UI_BACKDROP_CLASS;
    backdrop.setAttribute("aria-hidden", "true");
    const shell = document.createElement("div");
    shell.className = "kpv2-page-media-shell";
    const header = document.createElement("div");
    header.className = "kpv2-page-media-header";
    const titleWrap = document.createElement("div");
    titleWrap.className = "kpv2-page-media-title-wrap";
    const title = document.createElement("h2");
    title.className = "kpv2-page-media-title";
    title.textContent = "Page Media";
    const total = document.createElement("span");
    total.className = "kpv2-page-media-count";
    const mediaCount = groups.image.length + groups.video.length + groups.text.length + (HIDDEN_PAGE_MEDIA_TABS.has("pageText") ? 0 : groups.pageText.length);
    total.textContent = String(mediaCount || groups.font.length || (HIDDEN_PAGE_MEDIA_TABS.has("url") ? 0 : groups.url.length));
    const extraBits = [];
    if (groups.font.length) extraBits.push(`${groups.font.length} fonts`);
    if (!HIDDEN_PAGE_MEDIA_TABS.has("url") && groups.url.length) {
      extraBits.push(`${groups.url.length} unique URLs`);
    }
    total.title = extraBits.length ? `${mediaCount} media \xB7 ${extraBits.join(" \xB7 ")}` : `${mediaCount} media`;
    titleWrap.appendChild(title);
    titleWrap.appendChild(total);
    const tabs = document.createElement("div");
    tabs.className = "kpv2-page-media-tabs";
    tabs.setAttribute("role", "tablist");
    const tabButtons = {};
    const tabDefs = (
      /** @type {const} */
      [
        { id: "image", label: "Image" },
        { id: "video", label: "Video" },
        { id: "pageText", label: "Text" },
        { id: "text", label: "Docs" },
        { id: "font", label: "Fonts" },
        { id: "url", label: "URLs" }
      ]
    );
    for (const { id, label } of tabDefs) {
      if (HIDDEN_PAGE_MEDIA_TABS.has(id)) continue;
      const count = groups[id].length;
      const btn = document.createElement("button");
      btn.type = "button";
      btn.className = "kpv2-page-media-tab";
      btn.setAttribute("role", "tab");
      btn.dataset.tab = id;
      btn.disabled = count === 0;
      btn.setAttribute("aria-selected", id === _activeTab ? "true" : "false");
      if (id === _activeTab) btn.classList.add("is-active");
      const name = document.createElement("span");
      name.textContent = label;
      const badge = document.createElement("span");
      badge.className = "kpv2-page-media-tab-badge";
      badge.textContent = String(count);
      btn.appendChild(name);
      btn.appendChild(badge);
      btn.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        if (btn.disabled) return;
        setActiveTab(id);
      }, true);
      tabs.appendChild(btn);
      tabButtons[id] = btn;
    }
    const closeBtn = document.createElement("button");
    closeBtn.type = "button";
    closeBtn.className = "kpv2-page-media-close";
    closeBtn.textContent = "Close";
    closeBtn.addEventListener("click", (e) => {
      e.preventDefault();
      e.stopPropagation();
      closePageMediaOverlay();
    }, true);
    header.appendChild(titleWrap);
    header.appendChild(buildImageScaleControl());
    header.appendChild(tabs);
    header.appendChild(closeBtn);
    const content = document.createElement("div");
    content.className = `kpv2-page-media-content ${NCT_DARK_UI_SCROLLBAR_CLASS}`;
    content.id = "kpv2-page-media-grid";
    const fullView = document.createElement("div");
    fullView.className = "kpv2-page-media-fullview";
    fullView.setAttribute("aria-hidden", "true");
    const prevBtn = document.createElement("button");
    prevBtn.type = "button";
    prevBtn.className = "kpv2-page-media-nav kpv2-page-media-nav-prev";
    prevBtn.textContent = "\u2039";
    prevBtn.addEventListener("click", (e) => {
      e.preventDefault();
      e.stopPropagation();
      navigateFullView(-1);
    }, true);
    const nextBtn = document.createElement("button");
    nextBtn.type = "button";
    nextBtn.className = "kpv2-page-media-nav kpv2-page-media-nav-next";
    nextBtn.textContent = "\u203A";
    nextBtn.addEventListener("click", (e) => {
      e.preventDefault();
      e.stopPropagation();
      navigateFullView(1);
    }, true);
    const fullMediaHost = document.createElement("div");
    fullMediaHost.className = "kpv2-page-media-fullmedia";
    fullMediaHost.addEventListener("click", (e) => e.stopPropagation(), true);
    const counter = document.createElement("div");
    counter.className = "kpv2-page-media-fullcounter";
    fullView.appendChild(prevBtn);
    fullView.appendChild(fullMediaHost);
    fullView.appendChild(nextBtn);
    fullView.appendChild(counter);
    fullView.addEventListener("click", () => closeFullView(), true);
    shell.appendChild(header);
    shell.appendChild(buildImageAspectToolbar());
    if (!HIDDEN_PAGE_MEDIA_TABS.has("url")) {
      shell.appendChild(buildUrlViewToolbar());
    }
    shell.appendChild(content);
    shell.appendChild(fullView);
    mount.appendChild(backdrop);
    mount.appendChild(shell);
    overlay._tabButtons = tabButtons;
    overlay._content = content;
    overlay._fullView = fullView;
    overlay._fullMediaHost = fullMediaHost;
    overlay._fullCounter = counter;
    document.body.appendChild(overlay);
    _overlay = overlay;
    applyImageScale(_imageScaleSlider);
    applyImageAspectMode(_imageAspectMode);
    updateAspectToolbarVisibility();
    updateUrlViewToolbarVisibility();
    if (_urlViewControl) {
      _urlViewControl.setValue(_urlViewMode, { silent: true });
    }
    try {
      _prevOverflow = document.body.style.overflow || "";
      document.body.style.overflow = "hidden";
    } catch {
    }
    _keyHandler = (e) => {
      if (!isPageMediaOverlayOpen()) return;
      const isEsc = e.key === "Escape" || e.key === "Esc" || e.code === "Escape";
      const modal = _overlay && /** @type {any} */
      _overlay._fullView;
      const fullOpen = !!(modal && modal.classList.contains("is-open"));
      if (fullOpen) {
        if (isEsc) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          closeFullView();
          return;
        }
        if (e.key === "ArrowLeft") {
          e.preventDefault();
          e.stopPropagation();
          navigateFullView(-1);
          return;
        }
        if (e.key === "ArrowRight") {
          e.preventDefault();
          e.stopPropagation();
          navigateFullView(1);
          return;
        }
      } else if (isEsc) {
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        closePageMediaOverlay();
      }
    };
    document.addEventListener("keydown", _keyHandler, true);
    renderGrid();
    if (_activeTab === "image") startImageEnrichment();
    applyImageAspectMode(_imageAspectMode);
    updateAspectToolbarVisibility();
    updateUrlViewToolbarVisibility();
  }
  function setActiveTab(tab) {
    if (HIDDEN_PAGE_MEDIA_TABS.has(tab)) return;
    _activeTab = tab;
    if (!_overlay) return;
    const buttons = (
      /** @type {any} */
      _overlay._tabButtons || {}
    );
    for (const id of Object.keys(buttons)) {
      const btn = buttons[id];
      if (!btn) continue;
      const on = id === tab;
      btn.classList.toggle("is-active", on);
      btn.setAttribute("aria-selected", on ? "true" : "false");
    }
    renderGrid();
    if (tab === "image") startImageEnrichment();
    applyImageScale(_imageScaleSlider);
    applyImageAspectMode(_imageAspectMode);
    updateAspectToolbarVisibility();
    updateUrlViewToolbarVisibility();
  }
  function startImageEnrichment() {
    const gen = ++_enrichGen;
    const images = groupPageMediaByCategory(_items).image;
    if (!images.length) return;
    enrichImageItems(images, {
      concurrency: 4,
      onProgress: () => {
        if (gen !== _enrichGen || !isPageMediaOverlayOpen()) return;
        if (_activeTab !== "image") return;
        renderImageGrid({ preserveScroll: true });
      }
    }).catch(() => {
    });
  }
  function renderGrid() {
    if (!_overlay) return;
    const content = (
      /** @type {HTMLElement} */
      /** @type {any} */
      _overlay._content
    );
    if (!content) return;
    if (_activeTab === "image") {
      renderImageGrid({ preserveScroll: false });
      return;
    }
    while (content.firstChild) content.removeChild(content.firstChild);
    content.classList.remove("is-image-tab", "is-video-tab", "is-url-tab", "is-page-text-tab", "is-font-tab");
    const groups = groupPageMediaByCategory(_items);
    const list = groups[
      /** @type {'video'|'text'|'url'|'pageText'|'font'} */
      _activeTab
    ] || [];
    if (!list.length) {
      const empty = document.createElement("div");
      empty.className = "kpv2-page-media-empty";
      empty.textContent = _activeTab === "url" ? "No URLs found on this page." : _activeTab === "pageText" ? "No article or main text found on this page." : _activeTab === "font" ? "No fonts found on this page." : `No ${_activeTab === "text" ? "document" : _activeTab} files found on this page.`;
      content.appendChild(empty);
      return;
    }
    if (_activeTab === "video") {
      content.classList.add("is-video-tab");
      const grid = document.createElement("div");
      grid.className = "kpv2-page-media-size-grid";
      list.forEach((item, index) => {
        grid.appendChild(buildVideoCard(item, () => onItemActivate(list, index)));
      });
      content.appendChild(grid);
      return;
    }
    if (_activeTab === "url") {
      content.classList.add("is-url-tab");
      if (_urlViewMode === "list") {
        content.appendChild(buildUrlListView(list));
      } else {
        content.appendChild(buildUrlTableView(list));
      }
      return;
    }
    if (_activeTab === "font") {
      content.classList.add("is-font-tab");
      content.appendChild(buildFontListView(list));
      return;
    }
    if (_activeTab === "pageText") {
      content.classList.add("is-page-text-tab");
      const listEl = document.createElement("div");
      listEl.className = "kpv2-page-media-text-list";
      list.forEach((item, index) => {
        listEl.appendChild(buildPageTextRow(item, () => onItemActivate(list, index)));
      });
      content.appendChild(listEl);
      return;
    }
    list.forEach((item, index) => {
      content.appendChild(buildFileCard(item, () => onItemActivate(list, index)));
    });
  }
  function renderImageGrid(opts = {}) {
    if (!_overlay) return;
    const content = (
      /** @type {HTMLElement} */
      /** @type {any} */
      _overlay._content
    );
    if (!content) return;
    const scrollTop = opts.preserveScroll ? content.scrollTop : 0;
    while (content.firstChild) content.removeChild(content.firstChild);
    content.classList.add("is-image-tab");
    _cardByItem = /* @__PURE__ */ new WeakMap();
    const images = groupPageMediaByCategory(_items).image;
    if (!images.length) {
      _imageFlatList = [];
      const empty = document.createElement("div");
      empty.className = "kpv2-page-media-empty";
      empty.textContent = "No image files found on this page.";
      content.appendChild(empty);
      return;
    }
    const { photos, posters } = partitionImageItems(images);
    const flat = [];
    const photoItems = sortImageItems(photos, _imageSortMode, {
      prioritizeLandmarks: _imagePrioritizeLandmarks
    });
    const posterItems = sortImageItems(posters, _imageSortMode, {
      prioritizeLandmarks: _imagePrioritizeLandmarks
    });
    if (photoItems.length) {
      const grid = document.createElement("div");
      grid.className = "kpv2-page-media-size-grid";
      for (const item of photoItems) {
        const flatIndex = flat.length;
        flat.push(item);
        const card = buildImageCard(item, () => onItemActivate(flat, flatIndex));
        _cardByItem.set(item, card);
        grid.appendChild(card);
      }
      content.appendChild(grid);
    }
    if (posterItems.length) {
      const section = document.createElement("section");
      section.className = "kpv2-page-media-size-group";
      section.dataset.sizeGroup = "video-posters";
      const heading = document.createElement("h3");
      heading.className = "kpv2-page-media-size-heading";
      const title = document.createElement("span");
      title.textContent = "Video posters";
      const count = document.createElement("span");
      count.className = "kpv2-page-media-size-count";
      count.textContent = String(posterItems.length);
      heading.appendChild(title);
      heading.appendChild(count);
      section.appendChild(heading);
      const grid = document.createElement("div");
      grid.className = "kpv2-page-media-size-grid";
      for (const item of posterItems) {
        const flatIndex = flat.length;
        flat.push(item);
        const card = buildImageCard(item, () => onItemActivate(flat, flatIndex));
        _cardByItem.set(item, card);
        grid.appendChild(card);
      }
      section.appendChild(grid);
      content.appendChild(section);
    }
    if (!flat.length) {
      const empty = document.createElement("div");
      empty.className = "kpv2-page-media-empty";
      empty.textContent = "No image files found on this page.";
      content.appendChild(empty);
    }
    _imageFlatList = flat;
    if (opts.preserveScroll) {
      try {
        content.scrollTop = scrollTop;
      } catch {
      }
    }
    applyImageAspectMode(_imageAspectMode);
  }
  function suppressOverlayTextRange(el2) {
    if (!el2) return;
    const stop = (e) => {
      try {
        if (e.button != null && e.button !== 0) return;
        e.preventDefault();
      } catch {
      }
    };
    el2.addEventListener("mousedown", stop, true);
    el2.addEventListener("selectstart", stop, true);
  }
  function clearDocumentTextRange() {
    try {
      window.getSelection()?.removeAllRanges();
    } catch {
    }
  }
  function buildImageCard(item, onActivate) {
    const card = document.createElement("div");
    card.className = "kpv2-page-media-card kpv2-page-media-card-image";
    card.setAttribute("role", "button");
    card.tabIndex = 0;
    card.title = item.url;
    const thumbWrap = document.createElement("div");
    thumbWrap.className = "kpv2-page-media-thumb-wrap";
    if (item.url) {
      const thumb = document.createElement("img");
      thumb.className = "kpv2-page-media-thumb";
      thumb.loading = "lazy";
      thumb.alt = item.label || "";
      thumb.draggable = false;
      thumb.src = item.url;
      thumb.addEventListener("error", () => {
        thumb.remove();
        const ph = document.createElement("div");
        ph.className = "kpv2-page-media-glyph";
        ph.textContent = (item.ext || "IMG").toUpperCase();
        thumbWrap.appendChild(ph);
      }, { once: true });
      thumb.addEventListener("load", () => {
        applyThumbFitMode(thumb, thumbWrap);
        const w = Number(thumb.naturalWidth) || 0;
        const h = Number(thumb.naturalHeight) || 0;
        if (w > 0 && h > 0) {
          const had = Number(item.width) > 0 && Number(item.height) > 0;
          item.width = w;
          item.height = h;
          updateImageCardMeta(card, item);
          if (!had && _activeTab === "image") {
            scheduleImageRegroup();
          }
        }
      }, { once: true });
      if (thumb.complete && thumb.naturalWidth > 0) {
        applyThumbFitMode(thumb, thumbWrap);
      }
      thumbWrap.appendChild(thumb);
    } else {
      const ph = document.createElement("div");
      ph.className = "kpv2-page-media-glyph";
      ph.textContent = (item.ext || "IMG").toUpperCase();
      thumbWrap.appendChild(ph);
    }
    thumbWrap.appendChild(buildHoverActions(item));
    card.appendChild(buildMetaRow(item));
    card.appendChild(thumbWrap);
    const info = document.createElement("div");
    info.className = "kpv2-page-media-info";
    const name = document.createElement("div");
    name.className = "kpv2-page-media-name";
    name.textContent = truncate(item.label || item.url, 36);
    const meta = document.createElement("div");
    meta.className = "kpv2-page-media-meta";
    fillImageFooterMeta(meta, item);
    info.appendChild(name);
    info.appendChild(meta);
    card.appendChild(info);
    const activate = (e) => {
      if (e.target && /** @type {Element} */
      e.target.closest?.(".kpv2-page-media-actions")) return;
      e.preventDefault();
      e.stopPropagation();
      onActivate();
      clearDocumentTextRange();
    };
    card.addEventListener("click", activate, true);
    card.addEventListener("keydown", (e) => {
      if (e.key === "Enter" || e.key === " ") activate(e);
    }, true);
    suppressOverlayTextRange(card);
    return card;
  }
  function buildVideoCard(item, onActivate) {
    const card = document.createElement("div");
    card.className = "kpv2-page-media-card kpv2-page-media-card-video";
    card.setAttribute("role", "button");
    card.tabIndex = 0;
    card.title = item.url;
    card.appendChild(buildMetaRow(item));
    const thumbWrap = document.createElement("div");
    thumbWrap.className = "kpv2-page-media-thumb-wrap";
    const glyph = document.createElement("div");
    glyph.className = "kpv2-page-media-glyph";
    glyph.textContent = (item.ext || "VID").toUpperCase();
    thumbWrap.appendChild(glyph);
    const thumb = document.createElement("img");
    thumb.className = "kpv2-page-media-thumb";
    thumb.alt = item.label || "Video";
    thumb.draggable = false;
    thumb.hidden = true;
    thumbWrap.appendChild(thumb);
    thumbWrap.appendChild(buildHoverActions(item));
    card.appendChild(thumbWrap);
    const info = document.createElement("div");
    info.className = "kpv2-page-media-info";
    const name = document.createElement("div");
    name.className = "kpv2-page-media-name";
    name.textContent = truncate(item.label || item.url, 36);
    const meta = document.createElement("div");
    meta.className = "kpv2-page-media-meta";
    fillExtChipMeta(meta, item, item.ext ? "" : "video");
    info.appendChild(name);
    info.appendChild(meta);
    card.appendChild(info);
    const activate = (e) => {
      if (e.target && /** @type {Element} */
      e.target.closest?.(".kpv2-page-media-actions")) return;
      e.preventDefault();
      e.stopPropagation();
      onActivate();
      clearDocumentTextRange();
    };
    card.addEventListener("click", activate, true);
    card.addEventListener("keydown", (e) => {
      if (e.key === "Enter" || e.key === " ") activate(e);
    }, true);
    suppressOverlayTextRange(card);
    resolveVideoThumbnail(item).then((thumbUrl) => {
      if (!thumbUrl || !card.isConnected) return;
      thumb.onload = () => {
        glyph.remove();
        thumb.hidden = false;
        applyThumbFitMode(thumb, thumbWrap);
        const w = Number(thumb.naturalWidth) || 0;
        const h = Number(thumb.naturalHeight) || 0;
        if (w > 0 && h > 0) {
          item.width = w;
          item.height = h;
          updateMetaRow(card, item);
        }
      };
      thumb.onerror = () => {
        thumb.remove();
      };
      thumb.src = thumbUrl;
    }).catch(() => {
    });
    return card;
  }
  function buildPageTextRow(item, onActivate) {
    const row = document.createElement("div");
    row.className = "kpv2-page-media-text-row";
    if (item.kind === "article" || item.kind === "main" || item.kind === "full-page") {
      row.classList.add("is-full-page");
    }
    row.setAttribute("role", "button");
    row.tabIndex = 0;
    row.title = "Click to copy";
    const main = document.createElement("div");
    main.className = "kpv2-page-media-text-main";
    const kind = document.createElement("div");
    kind.className = "kpv2-page-media-text-kind";
    kind.textContent = formatPageTextKind(item);
    const body = document.createElement("div");
    body.className = "kpv2-page-media-text-body";
    body.textContent = item.text || item.label || "";
    const meta = document.createElement("div");
    meta.className = "kpv2-page-media-text-meta";
    const chars = String(item.text || "").length;
    meta.textContent = `${chars.toLocaleString()} chars \xB7 ${formatFileSize(item.fileSizeBytes)}`;
    main.appendChild(kind);
    main.appendChild(body);
    main.appendChild(meta);
    const actions = buildHoverActions(item);
    actions.classList.add("kpv2-page-media-text-actions");
    row.appendChild(main);
    row.appendChild(actions);
    const activate = (e) => {
      if (e.target && /** @type {Element} */
      e.target.closest?.(".kpv2-page-media-actions")) return;
      e.preventDefault();
      e.stopPropagation();
      onActivate();
      clearDocumentTextRange();
    };
    row.addEventListener("click", activate, true);
    row.addEventListener("keydown", (e) => {
      if (e.key === "Enter" || e.key === " ") activate(e);
    }, true);
    return row;
  }
  function formatPageTextKind(item) {
    const k = String(item?.kind || "");
    if (k === "article") return "Article";
    if (k === "main") return "Main";
    if (k === "full-page") return "Full page";
    return k || "Text";
  }
  function groupUrlItemsByDomain(list) {
    const byDomain = /* @__PURE__ */ new Map();
    for (const item of list) {
      const domain = domainFromUrl(item.url);
      if (!byDomain.has(domain)) byDomain.set(domain, []);
      byDomain.get(domain).push(item);
    }
    let pageHost = "";
    try {
      pageHost = String(location.hostname || "");
    } catch {
      pageHost = "";
    }
    const domains = Array.from(byDomain.keys()).sort((a, b) => {
      if (pageHost) {
        if (a === pageHost && b !== pageHost) return -1;
        if (b === pageHost && a !== pageHost) return 1;
      }
      return a.localeCompare(b, void 0, { sensitivity: "base" });
    });
    return { pageHost, domains, byDomain };
  }
  function urlItemKindLabel(item) {
    const group = item?.urlGroup || "";
    if (group === "page") return "Page";
    if (group === "document") return "Document";
    if (group === "image") return "Image";
    if (group === "video") return "Video";
    if (item?.ext) return `.${item.ext}`;
    if (item?.kind && item.kind !== "url") return String(item.kind);
    return "Link";
  }
  function buildFontListView(list) {
    const wrap = document.createElement("div");
    wrap.className = "kpv2-page-media-font-list";
    const custom = [];
    const local = [];
    for (const item of list || []) {
      if (/^(chrome|chrome-extension|chrome-untrusted):/i.test(String(item?.url || ""))) continue;
      if (item?.kind === "custom" || item?.sourceKind === "webfont") custom.push(item);
      else local.push(item);
    }
    const addSection = (title, items) => {
      if (!items.length) return;
      const section = document.createElement("section");
      section.className = "kpv2-page-media-font-group";
      const heading = document.createElement("h3");
      heading.className = "kpv2-page-media-font-heading";
      const name = document.createElement("span");
      name.textContent = title;
      const count = document.createElement("span");
      count.className = "kpv2-page-media-font-count";
      count.textContent = String(items.length);
      heading.appendChild(name);
      heading.appendChild(count);
      section.appendChild(heading);
      const rows = document.createElement("div");
      rows.className = "kpv2-page-media-font-rows";
      items.forEach((item) => {
        const index = list.indexOf(item);
        rows.appendChild(buildFontRow(item, () => onItemActivate(list, index)));
      });
      section.appendChild(rows);
      wrap.appendChild(section);
    };
    addSection("Custom", custom);
    addSection("System", local);
    return wrap;
  }
  function buildFontRow(item, onActivate) {
    const row = document.createElement("div");
    row.className = "kpv2-page-media-font-row";
    row.setAttribute("role", "button");
    row.tabIndex = 0;
    row.title = item.url || item.label || "";
    const sample = document.createElement("div");
    sample.className = "kpv2-page-media-font-sample";
    sample.textContent = "Ag";
    sample.setAttribute("aria-hidden", "true");
    try {
      const family = String(item.fontFamily || item.label || "").replace(/"/g, "");
      if (family) sample.style.fontFamily = `"${family}", sans-serif`;
      if (item.fontWeight) sample.style.fontWeight = String(item.fontWeight);
      if (item.fontStyle) sample.style.fontStyle = String(item.fontStyle);
    } catch {
    }
    const main = document.createElement("div");
    main.className = "kpv2-page-media-font-main";
    const name = document.createElement("div");
    name.className = "kpv2-page-media-font-name";
    name.textContent = item.label || item.fontFamily || "Font";
    const meta = document.createElement("div");
    meta.className = "kpv2-page-media-font-meta";
    const type2 = document.createElement("span");
    type2.className = "kpv2-page-media-font-type";
    const ext = String(item.ext || "").replace(/^\./, "");
    type2.textContent = ext && ext !== "local" && ext !== "font" ? `.${ext}` : "local";
    meta.appendChild(type2);
    if (item.fontWeight) {
      const w = document.createElement("span");
      w.textContent = String(item.fontWeight);
      meta.appendChild(w);
    }
    if (item.fontStyle && String(item.fontStyle).toLowerCase() !== "normal") {
      const s = document.createElement("span");
      s.textContent = String(item.fontStyle);
      meta.appendChild(s);
    }
    main.appendChild(name);
    main.appendChild(meta);
    if (item.url) {
      const urlEl = document.createElement("div");
      urlEl.className = "kpv2-page-media-font-url";
      urlEl.textContent = item.url;
      main.appendChild(urlEl);
    }
    row.appendChild(sample);
    row.appendChild(main);
    const actions = buildHoverActions(item);
    actions.classList.add("kpv2-page-media-font-actions");
    row.appendChild(actions);
    const activate = (e) => {
      if (e.target && /** @type {Element} */
      e.target.closest?.(".kpv2-page-media-actions")) return;
      e.preventDefault();
      e.stopPropagation();
      onActivate();
      clearDocumentTextRange();
    };
    row.addEventListener("click", activate, true);
    row.addEventListener("keydown", (e) => {
      if (e.key === "Enter" || e.key === " ") activate(e);
    }, true);
    return row;
  }
  function buildUrlListView(list) {
    const listEl = document.createElement("div");
    listEl.className = "kpv2-page-media-url-list";
    const { pageHost, domains, byDomain } = groupUrlItemsByDomain(list);
    const flat = [];
    for (const domain of domains) {
      const items = byDomain.get(domain) || [];
      items.sort(compareUrlTabItems);
      const section = document.createElement("section");
      section.className = "kpv2-page-media-url-group";
      if (pageHost && domain === pageHost) section.classList.add("is-current-domain");
      const heading = document.createElement("h3");
      heading.className = "kpv2-page-media-size-heading kpv2-page-media-url-domain";
      const title = document.createElement("span");
      title.textContent = domain;
      const count = document.createElement("span");
      count.className = "kpv2-page-media-size-count";
      count.textContent = String(items.length);
      heading.appendChild(title);
      heading.appendChild(count);
      section.appendChild(heading);
      const rows = document.createElement("div");
      rows.className = "kpv2-page-media-url-group-rows";
      const pathGroups = groupUrlItemsByPathPrefix(items, 2);
      for (const { prefix, items: pathItems } of pathGroups) {
        const grid = document.createElement("div");
        grid.className = "kpv2-page-media-url-card-grid";
        if (prefix) {
          const sub = document.createElement("div");
          sub.className = "kpv2-page-media-url-subpath";
          const subHeading = document.createElement("h4");
          subHeading.className = "kpv2-page-media-url-subpath-heading";
          const subTitle = document.createElement("span");
          subTitle.textContent = prefix;
          const subCount = document.createElement("span");
          subCount.className = "kpv2-page-media-size-count";
          subCount.textContent = String(pathItems.length);
          subHeading.appendChild(subTitle);
          subHeading.appendChild(subCount);
          sub.appendChild(subHeading);
          sub.appendChild(grid);
          rows.appendChild(sub);
        } else {
          rows.appendChild(grid);
        }
        for (const item of pathItems) {
          const flatIndex = flat.length;
          flat.push(item);
          grid.appendChild(buildUrlRow(item, () => onItemActivate(flat, flatIndex), {
            displayUrl: prefix ? urlPathRelativeToPrefix(item.url, prefix) : urlPathDisplay(item.url)
          }));
        }
      }
      section.appendChild(rows);
      listEl.appendChild(section);
    }
    return listEl;
  }
  function buildUrlTableView(list) {
    const { pageHost, domains, byDomain } = groupUrlItemsByDomain(list);
    const domainKeys = domains.map((domain) => `domain:${domain}`);
    ensureUrlTableExpandedDefaults(domainKeys);
    const hier = createHierarchicalTable({
      columns: [
        { key: "path", label: "Path / URL", width: "52%" },
        { key: "kind", label: "Kind", width: "12%" },
        { key: "title", label: "Title", width: "20%" },
        { key: "actions", label: "Actions", width: "16%" }
      ],
      ariaLabel: "Page URLs hierarchical table",
      wrapClassName: "kpv2-page-media-url-table-wrap",
      isGroupExpanded: isUrlTableGroupExpanded,
      onToggleGroup: toggleUrlTableGroup
    });
    const flat = [];
    for (const domain of domains) {
      const items = byDomain.get(domain) || [];
      items.sort(compareUrlTabItems);
      const domainKey = `domain:${domain}`;
      const isCurrent = !!(pageHost && domain === pageHost);
      if (!hier.appendGroupRow({
        groupKey: domainKey,
        label: domain,
        depth: 0,
        count: items.length,
        cells: [isCurrent ? "This page" : "Domain", ""],
        className: isCurrent ? "is-current-domain" : ""
      })) continue;
      const pathGroups = groupUrlItemsByPathPrefix(items, 2);
      for (const { prefix, items: pathItems } of pathGroups) {
        const leafItems = pathItems;
        let leafDepth = 1;
        let pathPrefix = "";
        if (prefix) {
          const pathKey = `path:${domain}:${prefix}`;
          if (!hier.appendGroupRow({
            groupKey: pathKey,
            label: prefix,
            depth: 1,
            count: pathItems.length,
            cells: ["Path", ""]
          })) continue;
          leafDepth = 2;
          pathPrefix = prefix;
        }
        for (const item of leafItems) {
          const flatIndex = flat.length;
          flat.push(item);
          const displayUrl = pathPrefix ? urlPathRelativeToPrefix(item.url, pathPrefix) : urlPathDisplay(item.url);
          const linkTitle = String(item.label || "").trim();
          const titleText = linkTitle && linkTitle !== item.url && linkTitle !== displayUrl ? linkTitle : "";
          const actions = buildHoverActions(item);
          actions.classList.add("kpv2-page-media-url-actions");
          actions.classList.add("kpv2-page-media-url-table-actions");
          hier.appendLeafRow({
            depth: leafDepth,
            title: item.url,
            cells: [
              displayUrl,
              buildUrlKindCell(item),
              titleText,
              actions
            ],
            onClick: () => onItemActivate(flat, flatIndex)
          });
        }
      }
    }
    return hier.root;
  }
  function buildUrlRow(item, onActivate, opts = {}) {
    const row = document.createElement("div");
    row.className = "kpv2-page-media-url-row kpv2-page-media-url-card";
    row.setAttribute("role", "button");
    row.tabIndex = 0;
    row.title = item.url;
    const main = document.createElement("div");
    main.className = "kpv2-page-media-url-main";
    const displayUrl = opts.displayUrl || item.url;
    const linkTitle = String(item.label || "").trim();
    const showTitle = !!(linkTitle && linkTitle !== item.url && linkTitle !== displayUrl);
    if (showTitle) {
      const titleEl = document.createElement("div");
      titleEl.className = "kpv2-page-media-url-title";
      titleEl.textContent = linkTitle;
      main.appendChild(titleEl);
    }
    const urlEl = document.createElement("div");
    urlEl.className = "kpv2-page-media-url-text";
    urlEl.textContent = displayUrl;
    const meta = document.createElement("div");
    meta.className = "kpv2-page-media-url-meta";
    fillExtChipMeta(meta, item, item.kind && item.kind !== "url" ? item.kind : item.ext ? "" : "link");
    main.appendChild(urlEl);
    main.appendChild(meta);
    const actions = buildHoverActions(item);
    actions.classList.add("kpv2-page-media-url-actions");
    row.appendChild(main);
    row.appendChild(actions);
    const activate = (e) => {
      if (e.target && /** @type {Element} */
      e.target.closest?.(".kpv2-page-media-actions")) return;
      e.preventDefault();
      e.stopPropagation();
      onActivate();
      clearDocumentTextRange();
    };
    row.addEventListener("click", activate, true);
    row.addEventListener("keydown", (e) => {
      if (e.key === "Enter" || e.key === " ") activate(e);
    }, true);
    return row;
  }
  function buildFileCard(item, onActivate) {
    const card = document.createElement("div");
    card.className = "kpv2-page-media-card";
    card.setAttribute("role", "button");
    card.tabIndex = 0;
    card.title = item.url;
    const mediaWrap = document.createElement("div");
    mediaWrap.className = "kpv2-page-media-thumb-wrap";
    const glyph = document.createElement("div");
    glyph.className = "kpv2-page-media-glyph";
    glyph.textContent = (item.ext || (_activeTab === "video" ? "VID" : "DOC")).toUpperCase();
    mediaWrap.appendChild(glyph);
    mediaWrap.appendChild(buildHoverActions(item));
    card.appendChild(buildMetaRow(item));
    card.appendChild(mediaWrap);
    const info = document.createElement("div");
    info.className = "kpv2-page-media-info";
    const name = document.createElement("div");
    name.className = "kpv2-page-media-name";
    name.textContent = truncate(item.label || item.url, 42);
    const meta = document.createElement("div");
    meta.className = "kpv2-page-media-meta";
    fillExtChipMeta(meta, item, item.ext ? "" : item.kind || "");
    info.appendChild(name);
    info.appendChild(meta);
    card.appendChild(info);
    const activate = (e) => {
      if (e.target && /** @type {Element} */
      e.target.closest?.(".kpv2-page-media-actions")) return;
      e.preventDefault();
      e.stopPropagation();
      onActivate();
      clearDocumentTextRange();
    };
    card.addEventListener("click", activate, true);
    card.addEventListener("keydown", (e) => {
      if (e.key === "Enter" || e.key === " ") activate(e);
    }, true);
    return card;
  }
  function buildHoverActions(item) {
    const bar = document.createElement("div");
    bar.className = "kpv2-page-media-actions";
    bar.setAttribute("role", "group");
    bar.setAttribute("aria-label", "Item actions");
    const mk = (label, title, handler) => {
      const btn = document.createElement("button");
      btn.type = "button";
      btn.className = "kpv2-page-media-action";
      btn.textContent = label;
      btn.title = title;
      btn.addEventListener("click", async (e) => {
        e.preventDefault();
        e.stopPropagation();
        try {
          await handler();
        } catch (err) {
          console.warn("[KeyPilot] Page Media action failed:", err);
          _notify("Action failed", "error");
        }
      }, true);
      return btn;
    };
    bar.appendChild(mk("Copy", "Copy to pasteboard", () => copyItemToPasteboard(item)));
    if (item?.category !== "pageText" && item?.category !== "font") {
      bar.appendChild(mk("Send", "Send to Media Library", () => sendItemToMediaLibrary(item)));
    }
    if (item?.url || item?.category === "pageText") {
      bar.appendChild(mk("Download", "Download file", () => downloadItem(item)));
    }
    return bar;
  }
  async function copyItemToPasteboard(item) {
    if (item?.category === "font") {
      const bits = [
        item.fontFamily || item.label || "",
        item.ext && item.ext !== "local" ? `.${String(item.ext).replace(/^\./, "")}` : "local",
        item.url || ""
      ].filter(Boolean);
      const text = bits.join("\n");
      try {
        if (navigator.clipboard?.writeText && text) {
          await navigator.clipboard.writeText(text);
          _notify("Font info copied to pasteboard", "success");
          return;
        }
      } catch {
      }
      _notify("Could not copy to pasteboard", "error");
      return;
    }
    const pageText = item?.category === "pageText" ? String(item.text || "") : "";
    if (pageText) {
      try {
        if (navigator.clipboard?.writeText) {
          await navigator.clipboard.writeText(pageText);
          _notify("Text copied to pasteboard", "success");
          return;
        }
      } catch {
      }
      _notify("Could not copy to pasteboard", "error");
      return;
    }
    if (!item?.url) {
      _notify("Nothing to copy", "info");
      return;
    }
    if (item.category === "image") {
      try {
        const blob = await fetchItemBlob(item.url);
        if (blob && blob.size > 0) {
          const mime = blob.type && blob.type.startsWith("image/") ? blob.type : item.mimeType && String(item.mimeType).startsWith("image/") ? String(item.mimeType) : "image/png";
          const ok = await writeImageBlobToClipboard(blob, mime);
          if (ok) {
            _notify("Copied to pasteboard", "success");
            return;
          }
        }
      } catch (err) {
        console.warn("[KeyPilot] image copy failed, falling back to URL:", err);
      }
    }
    try {
      if (navigator.clipboard?.writeText) {
        await navigator.clipboard.writeText(item.url);
        _notify(
          item.category === "image" ? "Image URL copied" : "URL copied to pasteboard",
          "success"
        );
        return;
      }
    } catch {
    }
    _notify("Could not copy to pasteboard", "error");
  }
  async function sendItemToMediaLibrary(item) {
    try {
      await _onSendToMediaLibrary(item);
    } catch (err) {
      console.warn("[KeyPilot] send to Media Library failed:", err);
      _notify("Could not send to Media Library", "error");
    }
  }
  async function downloadItem(item) {
    if (item?.category === "pageText" && item.text) {
      try {
        const blob = new Blob([String(item.text)], { type: "text/plain;charset=utf-8" });
        const objectUrl = URL.createObjectURL(blob);
        const name = item.kind === "article" ? "article.txt" : item.kind === "main" ? "main.txt" : item.kind === "full-page" ? "page-text.txt" : `page-text-${item.kind || "block"}.txt`;
        triggerDownload(objectUrl, name);
        setTimeout(() => {
          try {
            URL.revokeObjectURL(objectUrl);
          } catch {
          }
        }, 2e3);
        _notify("Download started", "success");
        return;
      } catch {
        _notify("Could not download", "error");
        return;
      }
    }
    if (!item?.url) {
      _notify("Nothing to download", "info");
      return;
    }
    const filename = safeDownloadFilename(item);
    try {
      const blob = await fetchItemBlob(item.url);
      if (blob && blob.size > 0) {
        const objectUrl = URL.createObjectURL(blob);
        triggerDownload(objectUrl, filename);
        setTimeout(() => {
          try {
            URL.revokeObjectURL(objectUrl);
          } catch {
          }
        }, 2e3);
        _notify("Download started", "success");
        return;
      }
    } catch {
    }
    try {
      triggerDownload(item.url, filename);
      _notify("Download started", "success");
    } catch {
      _notify("Could not download", "error");
    }
  }
  async function fetchItemBlob(url) {
    if (!url) return null;
    if (/^data:/i.test(url)) {
      const res2 = await fetch(url);
      return await res2.blob();
    }
    const res = await fetch(url, { credentials: "omit", cache: "force-cache" });
    if (!res.ok) return null;
    return await res.blob();
  }
  async function writeImageBlobToClipboard(blob, mimeType) {
    if (!navigator.clipboard || typeof navigator.clipboard.write !== "function") return false;
    const type2 = mimeType && String(mimeType).startsWith("image/") ? String(mimeType) : blob.type && blob.type.startsWith("image/") ? blob.type : "image/png";
    let outBlob = blob;
    let outType = type2;
    if (type2 !== "image/png" && typeof createImageBitmap === "function") {
      try {
        const bmp = await createImageBitmap(blob);
        const canvas = document.createElement("canvas");
        canvas.width = bmp.width;
        canvas.height = bmp.height;
        const ctx = canvas.getContext("2d");
        if (ctx) {
          ctx.drawImage(bmp, 0, 0);
          const png = await new Promise((resolve) => canvas.toBlob(resolve, "image/png"));
          if (png && png.size > 0) {
            outBlob = png;
            outType = "image/png";
          }
        }
        try {
          bmp.close();
        } catch {
        }
      } catch {
      }
    }
    try {
      const item = new ClipboardItem({
        [outType]: Promise.resolve(outBlob)
      });
      await navigator.clipboard.write([item]);
      return true;
    } catch {
      try {
        const item = new ClipboardItem({ [outType]: outBlob });
        await navigator.clipboard.write([item]);
        return true;
      } catch {
        return false;
      }
    }
  }
  function triggerDownload(url, filename) {
    const a = document.createElement("a");
    a.href = url;
    a.download = filename || "download";
    a.rel = "noopener";
    a.style.display = "none";
    document.documentElement.appendChild(a);
    a.click();
    a.remove();
  }
  function safeDownloadFilename(item) {
    let name = String(item.label || "").trim();
    if (!name || /^https?:/i.test(name)) {
      try {
        const u = new URL(item.url, document.baseURI);
        name = decodeURIComponent((u.pathname || "").split("/").pop() || "") || "download";
      } catch {
        name = "download";
      }
    }
    name = name.replace(/[\\/:*?"<>|]+/g, "_").slice(0, 120) || "download";
    const ext = String(item.ext || "").toLowerCase();
    if (ext && !new RegExp(`\\.${ext}$`, "i").test(name)) {
      name += `.${ext}`;
    }
    return name;
  }
  var _regroupTimer = null;
  function scheduleImageRegroup() {
    if (_regroupTimer != null) return;
    _regroupTimer = window.setTimeout(() => {
      _regroupTimer = null;
      if (!isPageMediaOverlayOpen() || _activeTab !== "image") return;
      renderImageGrid({ preserveScroll: true });
    }, 250);
  }
  function applyThumbFitMode(thumb, wrap) {
    if (!thumb || !wrap) return;
    const nw = Number(thumb.naturalWidth) || 0;
    const nh = Number(thumb.naturalHeight) || 0;
    if (!(nw > 0 && nh > 0)) return;
    if (_imageAspectMode === "original") {
      wrap.style.aspectRatio = `${nw} / ${nh}`;
      thumb.classList.remove("is-cover");
      thumb.classList.add("is-native", "is-original");
      return;
    }
    wrap.style.aspectRatio = "";
    thumb.classList.remove("is-original");
    let cw = Number(wrap.clientWidth) || 0;
    let ch = Number(wrap.clientHeight) || 0;
    if (!(cw > 0 && ch > 0)) {
      try {
        const rect = wrap.getBoundingClientRect();
        cw = Number(rect.width) || cw;
        ch = Number(rect.height) || ch;
      } catch {
      }
    }
    if (!(cw > 0 && ch > 0)) {
      requestAnimationFrame(() => applyThumbFitMode(thumb, wrap));
      return;
    }
    const largerThanFrame = nw > cw || nh > ch;
    thumb.classList.toggle("is-cover", largerThanFrame);
    thumb.classList.toggle("is-native", !largerThanFrame);
  }
  function updateImageCardMeta(card, item) {
    const meta = card.querySelector(".kpv2-page-media-meta");
    if (meta) fillImageFooterMeta(meta, item);
    updateMetaRow(card, item);
  }
  function buildMetaRow(item) {
    const row = document.createElement("div");
    row.className = "kpv2-page-media-meta-row";
    const dims = document.createElement("span");
    dims.className = "kpv2-page-media-badge kpv2-page-media-badge-dims";
    dims.textContent = formatImageDimensions(item);
    const size = document.createElement("span");
    size.className = "kpv2-page-media-badge kpv2-page-media-badge-size";
    size.textContent = formatFileSize(item?.fileSizeBytes);
    row.appendChild(dims);
    row.appendChild(size);
    return row;
  }
  function updateMetaRow(card, item) {
    const dims = card.querySelector(".kpv2-page-media-badge-dims");
    if (dims) dims.textContent = formatImageDimensions(item);
    const size = card.querySelector(".kpv2-page-media-badge-size");
    if (size) size.textContent = formatFileSize(item?.fileSizeBytes);
  }
  function fillImageFooterMeta(host, item) {
    const dpi = formatImageDpi(item);
    fillExtChipMeta(host, item, dpi && dpi !== "\u2014" ? dpi : "");
  }
  function normalizeExtChipKey(label) {
    const raw = String(label || "").replace(/^\./, "").toLowerCase();
    if (raw === "jpeg") return "jpg";
    if (raw === "tiff") return "tif";
    if (raw === "svg+xml") return "svg";
    return raw || "unknown";
  }
  function formatExtChipLabel(item) {
    if (item?.category === "image") return formatImageFileType(item);
    const ext = String(item?.ext || "").toLowerCase();
    if (!ext || ext === "video" || ext === "img" || ext === "bg") return "";
    return ext === "jpeg" ? "JPG" : ext.toUpperCase();
  }
  function buildExtChip(label) {
    const chip = document.createElement("span");
    chip.className = "kpv2-page-media-ext-chip";
    const text = String(label || "").replace(/^\./, "");
    chip.dataset.ext = normalizeExtChipKey(text);
    chip.textContent = text;
    return chip;
  }
  function fillExtChipMeta(host, item, extra) {
    while (host.firstChild) host.removeChild(host.firstChild);
    const label = formatExtChipLabel(item);
    if (label) host.appendChild(buildExtChip(label));
    const extraText = String(extra || "").trim();
    if (extraText) {
      const rest = document.createElement("span");
      rest.className = "kpv2-page-media-meta-extra";
      rest.textContent = extraText;
      host.appendChild(rest);
    }
    if (!host.firstChild) {
      host.textContent = "link";
    }
  }
  function buildUrlKindCell(item) {
    const wrap = document.createElement("span");
    wrap.className = "kpv2-page-media-url-kind";
    const label = formatExtChipLabel(item);
    if (label) wrap.appendChild(buildExtChip(label));
    else wrap.textContent = urlItemKindLabel(item);
    return wrap;
  }
  function onItemActivate(list, index) {
    const item = list[index];
    if (!item) return;
    if (_activeTab === "pageText") {
      copyItemToPasteboard(item);
      return;
    }
    if (_activeTab === "text" || _activeTab === "url" || _activeTab === "font") {
      if (item.url) openUrl(item.url);
      else copyItemToPasteboard(item);
      return;
    }
    if (_activeTab === "video") {
      openFullView(list, index, "video");
      return;
    }
    openFullView(list, index, "image");
  }
  function openFullView(list, index, mode) {
    if (!_overlay) return;
    _fullViewIndex = index;
    _overlay._fullList = list;
    _overlay._fullMode = mode;
    const modal = (
      /** @type {HTMLElement} */
      /** @type {any} */
      _overlay._fullView
    );
    const host = (
      /** @type {HTMLElement} */
      /** @type {any} */
      _overlay._fullMediaHost
    );
    const counter = (
      /** @type {HTMLElement} */
      /** @type {any} */
      _overlay._fullCounter
    );
    if (!modal || !host) return;
    while (host.firstChild) host.removeChild(host.firstChild);
    const item = list[index];
    if (!item) return;
    if (mode === "video") {
      const video = document.createElement("video");
      video.className = "kpv2-page-media-fullvideo";
      video.controls = true;
      video.autoplay = true;
      video.src = item.url;
      host.appendChild(video);
      const openLink = document.createElement("a");
      openLink.className = "kpv2-page-media-openlink";
      openLink.href = item.url;
      openLink.target = "_blank";
      openLink.rel = "noopener noreferrer";
      openLink.textContent = "Open in new tab";
      openLink.addEventListener("click", (e) => e.stopPropagation(), true);
      host.appendChild(openLink);
    } else {
      const img = document.createElement("img");
      img.className = "kpv2-page-media-fullimage";
      img.src = item.url;
      img.alt = item.label || "";
      img.draggable = false;
      suppressOverlayTextRange(img);
      host.appendChild(img);
    }
    clearDocumentTextRange();
    if (counter) counter.textContent = `${index + 1} / ${list.length}`;
    modal.classList.add("is-open");
    modal.setAttribute("aria-hidden", "false");
  }
  function closeFullView() {
    if (!_overlay) return;
    const modal = (
      /** @type {HTMLElement} */
      /** @type {any} */
      _overlay._fullView
    );
    const host = (
      /** @type {HTMLElement} */
      /** @type {any} */
      _overlay._fullMediaHost
    );
    if (host) {
      try {
        const v = host.querySelector("video");
        if (v) v.pause();
      } catch {
      }
      while (host.firstChild) host.removeChild(host.firstChild);
    }
    if (modal) {
      modal.classList.remove("is-open");
      modal.setAttribute("aria-hidden", "true");
    }
  }
  function navigateFullView(direction) {
    if (!_overlay) return;
    const list = (
      /** @type {any} */
      _overlay._fullList
    );
    const mode = (
      /** @type {any} */
      _overlay._fullMode || "image"
    );
    if (!Array.isArray(list) || !list.length) return;
    _fullViewIndex = (_fullViewIndex + direction + list.length) % list.length;
    openFullView(list, _fullViewIndex, mode);
  }
  function openUrl(url) {
    if (!url) return;
    try {
      window.open(url, "_blank", "noopener,noreferrer");
    } catch {
      try {
        const a = document.createElement("a");
        a.href = url;
        a.target = "_blank";
        a.rel = "noopener noreferrer";
        a.click();
      } catch {
      }
    }
  }
  function truncate(s, n) {
    const t = String(s || "");
    if (t.length <= n) return t;
    return t.slice(0, Math.max(0, n - 1)) + "\u2026";
  }
  function ensureStyles3(root) {
    if (!root) return;
    const c = NCT_DARK_UI_COLORS;
    const css = `
.kpv2-page-media-shell {
  position: absolute;
  inset: 10pt;
  z-index: 1;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: rgba(15, 15, 16, 0.92);
  border: ${NCT_DARK_UI_PANEL_BORDER};
  border-radius: ${NCT_DARK_UI_PANEL_RADIUS};
  box-shadow: ${NCT_DARK_UI_PANEL_BOX_SHADOW};
  font-family: ${NCT_DARK_UI_FONT};
  color: ${c.fg};
  user-select: none;
  -webkit-user-select: none;
}
${getNctDarkUiBackdropCss()}
${getNctDarkUiScrollbarCss()}
.kpv2-page-media-header {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 10px 14px;
  background: ${NCT_DARK_UI_TITLEBAR_GRADIENT};
  border-bottom: ${NCT_DARK_UI_TITLEBAR_BORDER_BOTTOM};
  flex-shrink: 0;
}
.kpv2-page-media-aspect-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  flex-shrink: 0;
  padding: 8px 14px;
  background: ${c.panel};
  border-bottom: 1px solid ${c.panelEdgeDark};
  box-shadow: 0 1px 0 ${c.panelEdge} inset;
}
.kpv2-page-media-aspect-bar[hidden] {
  display: none !important;
}
.kpv2-page-media-aspect-label {
  font-size: 11px;
  font-weight: 600;
  color: ${c.fgDim};
  letter-spacing: 0.02em;
  user-select: none;
  white-space: nowrap;
}
.kpv2-page-media-aspect-seg,
.kpv2-page-media-sort-seg {
  font-size: 11px;
}
.kpv2-page-media-sort-wrap {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: 8px;
  padding-left: 14px;
  border-left: 1px solid ${c.panelEdgeDark};
}
.kpv2-page-media-landmark-check {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: 8px;
  padding-left: 14px;
  border-left: 1px solid ${c.panelEdgeDark};
  font-size: 11px;
  font-weight: 600;
  color: ${c.fgDim};
  letter-spacing: 0.02em;
  user-select: none;
  white-space: nowrap;
  cursor: pointer;
}
.kpv2-page-media-landmark-check input {
  width: 13px;
  height: 13px;
  margin: 0;
  accent-color: ${c.accent || "#8fb36a"};
  cursor: pointer;
}
.kpv2-page-media-url-view-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
  padding: 8px 14px;
  background: ${c.panel};
  border-bottom: 1px solid ${c.panelEdgeDark};
  box-shadow: 0 1px 0 ${c.panelEdge} inset;
}
.kpv2-page-media-url-view-bar[hidden] {
  display: none !important;
}
.kpv2-page-media-url-view-label {
  font-size: 11px;
  font-weight: 600;
  color: ${c.fgDim};
  letter-spacing: 0.02em;
  user-select: none;
  white-space: nowrap;
}
.kpv2-page-media-url-view-seg {
  font-size: 11px;
}
${getHierarchicalTableCss()}
.kpv2-page-media-content .kpv2-page-media-url-table-wrap,
.kpv2-page-media-url-table-wrap {
  overflow: visible;
  border: 1px solid rgba(120, 140, 100, 0.12);
  border-radius: 2px;
  background: rgba(8, 10, 8, 0.35);
}
.kpv2-page-media-url-table-wrap .kp-hier-table tr.kp-hier-row-group.is-current-domain td {
  color: #b8c4d8;
}
.kpv2-page-media-url-table-wrap .kp-hier-table .kpv2-page-media-url-table-actions {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 4px;
  justify-content: flex-end;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s ease;
}
.kpv2-page-media-url-table-wrap .kp-hier-table tr.kp-hier-row-leaf:hover .kpv2-page-media-url-table-actions,
.kpv2-page-media-url-table-wrap .kp-hier-table tr.kp-hier-row-leaf:focus-within .kpv2-page-media-url-table-actions {
  opacity: 1;
  pointer-events: auto;
}
.kpv2-page-media-url-table-wrap .kp-hier-table td:last-child {
  overflow: visible;
  white-space: normal;
}
.kpv2-page-media-title-wrap {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 120px;
  flex-shrink: 0;
}
${getNctDarkUiScaleSliderCss({ rangeWidth: "88px" })}
.kpv2-page-media-scale.kp-nct-scale {
  display: inline-flex;
}
.kpv2-page-media-title {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  color: ${c.fg};
}
.kpv2-page-media-count {
  font-size: 11px;
  padding: 2px 8px;
  border-radius: 2px;
  background: ${c.fieldBg};
  border: 1px solid ${c.panelEdgeDark};
  color: ${c.fgDim};
}
.kpv2-page-media-tabs {
  display: flex;
  gap: 4px;
  flex: 1;
  justify-content: center;
  min-width: 0;
}
.kpv2-page-media-tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border: ${NCT_DARK_UI_BTN_BORDER};
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  background: ${NCT_DARK_UI_BTN_GRADIENT};
  color: ${c.fgDim};
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.kpv2-page-media-tab:disabled {
  opacity: 0.35;
  cursor: default;
}
.kpv2-page-media-tab.is-active {
  background: ${NCT_DARK_UI_BTN_LIT_GRADIENT};
  border: ${NCT_DARK_UI_BTN_LIT_BORDER};
  color: #e8f0f8;
}
.kpv2-page-media-tab-badge {
  font-size: 10px;
  opacity: 0.85;
}
.kpv2-page-media-close {
  padding: 5px 12px;
  border: ${NCT_DARK_UI_BTN_BORDER};
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  background: ${NCT_DARK_UI_BTN_GRADIENT};
  color: ${c.fg};
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.kpv2-page-media-close:hover {
  color: #fff;
}
.kpv2-page-media-content {
  flex: 1;
  overflow: auto;
  padding: 16px;
  display: grid;
  --kpv2-pm-image-scale: 1.5;
  grid-template-columns: repeat(auto-fill, minmax(calc(180px * var(--kpv2-pm-image-scale, 1.5)), 1fr));
  gap: calc(12px * var(--kpv2-pm-image-scale, 1.5));
  align-content: flex-start;
}
.kpv2-page-media-content.is-image-tab,
.kpv2-page-media-content.is-video-tab {
  display: block;
  padding: 12px 16px 24px;
}
.kpv2-page-media-content .kpv2-page-media-size-grid {
  grid-template-columns: repeat(auto-fill, minmax(calc(140px * var(--kpv2-pm-image-scale, 1.5)), 1fr));
  gap: calc(12px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-meta-row {
  min-height: calc(22px * var(--kpv2-pm-image-scale, 1.5));
  padding: calc(5px * var(--kpv2-pm-image-scale, 1.5)) calc(8px * var(--kpv2-pm-image-scale, 1.5));
  gap: calc(8px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-badge {
  font-size: calc(10px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-info {
  padding: calc(8px * var(--kpv2-pm-image-scale, 1.5)) calc(10px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-name {
  font-size: calc(11px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-meta {
  margin-top: calc(4px * var(--kpv2-pm-image-scale, 1.5));
  font-size: calc(10px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-ext-chip {
  font-size: calc(9px * var(--kpv2-pm-image-scale, 1.5));
  padding: calc(1px * var(--kpv2-pm-image-scale, 1.5)) calc(7px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-size-heading {
  font-size: calc(12px * var(--kpv2-pm-image-scale, 1.5));
  margin-bottom: calc(10px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-size-count {
  font-size: calc(11px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-action {
  font-size: calc(10px * var(--kpv2-pm-image-scale, 1.5));
  padding: calc(5px * var(--kpv2-pm-image-scale, 1.5)) calc(4px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-glyph {
  font-size: calc(18px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-empty {
  font-size: calc(13px * var(--kpv2-pm-image-scale, 1.5));
  padding: calc(48px * var(--kpv2-pm-image-scale, 1.5)) calc(16px * var(--kpv2-pm-image-scale, 1.5));
}
/* Text tab */
.kpv2-page-media-content .kpv2-page-media-text-list {
  gap: calc(6px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-text-row {
  gap: calc(10px * var(--kpv2-pm-image-scale, 1.5));
  padding: calc(10px * var(--kpv2-pm-image-scale, 1.5)) calc(12px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-text-kind {
  font-size: calc(10px * var(--kpv2-pm-image-scale, 1.5));
  margin-bottom: calc(4px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-text-body {
  font-size: calc(12px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-text-meta {
  margin-top: calc(6px * var(--kpv2-pm-image-scale, 1.5));
  font-size: calc(10px * var(--kpv2-pm-image-scale, 1.5));
}
/* URLs tab */
.kpv2-page-media-content .kpv2-page-media-url-list {
  gap: calc(14px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-url-group {
  padding: calc(10px * var(--kpv2-pm-image-scale, 1.5)) calc(12px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-url-domain {
  font-size: calc(12px * var(--kpv2-pm-image-scale, 1.5));
  margin-bottom: calc(10px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-url-group-rows {
  gap: calc(12px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-url-subpath {
  gap: calc(8px * var(--kpv2-pm-image-scale, 1.5));
  padding: calc(8px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-url-subpath-heading {
  font-size: calc(11px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-url-card-grid {
  gap: calc(10px * var(--kpv2-pm-image-scale, 1.5));
  grid-template-columns: repeat(auto-fill, minmax(calc(180px * var(--kpv2-pm-image-scale, 1.5)), 1fr));
}
.kpv2-page-media-content .kpv2-page-media-url-row {
  gap: calc(6px * var(--kpv2-pm-image-scale, 1.5));
  padding: calc(10px * var(--kpv2-pm-image-scale, 1.5)) calc(10px * var(--kpv2-pm-image-scale, 1.5));
  min-height: calc(88px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-url-title {
  font-size: calc(12px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-url-text {
  font-size: calc(11px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-url-meta {
  font-size: calc(10px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content.is-url-tab {
  display: block;
  padding: 8px 12px 24px;
}
.kpv2-page-media-content.is-font-tab {
  display: block;
  padding: 8px 12px 24px;
}
.kpv2-page-media-font-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.kpv2-page-media-font-heading {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 0 0 8px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: ${c.fgDim};
}
.kpv2-page-media-font-count {
  font-weight: 600;
  color: ${c.fgMute};
  letter-spacing: 0;
  text-transform: none;
}
.kpv2-page-media-font-rows {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.kpv2-page-media-font-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid ${c.panelEdgeDark};
  border-radius: 3px;
  background: ${c.panel};
  box-shadow: 0 0 0 1px ${c.panelEdge} inset;
  cursor: pointer;
  color: inherit;
  position: relative;
}
.kpv2-page-media-font-row:hover {
  border-color: ${c.accent};
}
.kpv2-page-media-font-row:hover .kpv2-page-media-actions,
.kpv2-page-media-font-row:focus-within .kpv2-page-media-actions {
  opacity: 1;
  pointer-events: auto;
}
.kpv2-page-media-font-actions.kpv2-page-media-actions {
  position: static;
  left: auto;
  right: auto;
  bottom: auto;
  flex: 0 0 auto;
  width: auto;
  max-width: 200px;
  opacity: 0;
  pointer-events: none;
  margin-top: 2px;
}
.kpv2-page-media-font-sample {
  flex: 0 0 56px;
  width: 56px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 28px;
  line-height: 1;
  background: rgba(0,0,0,0.22);
  border-radius: 3px;
  overflow: hidden;
}
.kpv2-page-media-font-main {
  flex: 1;
  min-width: 0;
}
.kpv2-page-media-font-name {
  font-size: 13px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.kpv2-page-media-font-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 4px;
  font-size: 11px;
  color: ${c.fgDim};
}
.kpv2-page-media-font-type {
  font-weight: 700;
  letter-spacing: 0.03em;
  color: ${c.accent};
}
.kpv2-page-media-font-url {
  margin-top: 4px;
  font-size: 11px;
  color: ${c.fgMute};
  word-break: break-all;
}
.kpv2-page-media-content .kpv2-page-media-font-list {
  gap: calc(16px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-font-heading {
  font-size: calc(12px * var(--kpv2-pm-image-scale, 1.5));
  margin-bottom: calc(8px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-font-row {
  gap: calc(12px * var(--kpv2-pm-image-scale, 1.5));
  padding: calc(10px * var(--kpv2-pm-image-scale, 1.5)) calc(12px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-font-sample {
  flex-basis: calc(56px * var(--kpv2-pm-image-scale, 1.5));
  width: calc(56px * var(--kpv2-pm-image-scale, 1.5));
  height: calc(48px * var(--kpv2-pm-image-scale, 1.5));
  font-size: calc(28px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-font-name {
  font-size: calc(13px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-font-meta,
.kpv2-page-media-content .kpv2-page-media-font-url {
  font-size: calc(11px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-url-list {
  display: flex;
  flex-direction: column;
  gap: 14px;
  align-items: stretch;
}
.kpv2-page-media-url-group {
  margin-bottom: 0;
  padding: 10px 12px 10px;
  border: 1px solid ${c.panelEdgeDark};
  border-radius: 3px;
  background: ${c.panel};
  box-shadow: 0 0 0 1px ${c.panelEdge} inset;
  min-width: 0;
}
.kpv2-page-media-url-group.is-current-domain {
  border-color: ${c.accent};
  background: linear-gradient(180deg, rgba(74,144,200,0.10) 0%, ${c.panel} 36%);
}
.kpv2-page-media-url-domain {
  margin: 0 0 10px;
  padding: 0 2px;
  color: ${c.accent};
  text-transform: none;
  letter-spacing: 0;
  font-size: 12px;
  font-weight: 700;
  word-break: break-all;
}
.kpv2-page-media-url-group-rows {
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: stretch;
}
.kpv2-page-media-url-subpath {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px;
  border: 1px solid ${c.panelEdgeDark};
  border-radius: 2px;
  background: rgba(0, 0, 0, 0.18);
  min-width: 0;
  width: 100%;
  max-width: 100%;
}
.kpv2-page-media-url-subpath-heading {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 0;
  padding: 0 2px;
  font-size: 11px;
  font-weight: 600;
  color: ${c.fgDim};
  word-break: break-all;
}
.kpv2-page-media-url-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 10px;
  align-content: start;
}
.kpv2-page-media-url-group .kpv2-page-media-url-row {
  margin-bottom: 0;
  min-width: 0;
  width: 100%;
  max-width: none;
}
.kpv2-page-media-content.is-page-text-tab {
  display: block;
  padding: 8px 12px 24px;
}
.kpv2-page-media-text-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.kpv2-page-media-text-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid ${c.panelEdgeDark};
  border-radius: 3px;
  background: ${c.panel};
  box-shadow: 0 0 0 1px ${c.panelEdge} inset;
  cursor: pointer;
  color: inherit;
}
.kpv2-page-media-text-row.is-full-page {
  border-color: ${c.accent};
  background: linear-gradient(180deg, rgba(74,144,200,0.12) 0%, ${c.panel} 40%);
}
.kpv2-page-media-text-row:hover {
  border-color: ${c.accent};
}
.kpv2-page-media-text-row:hover .kpv2-page-media-text-actions,
.kpv2-page-media-text-row:focus-within .kpv2-page-media-text-actions {
  opacity: 1;
  pointer-events: auto;
}
.kpv2-page-media-text-main {
  flex: 1;
  min-width: 0;
}
.kpv2-page-media-text-kind {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: ${c.accent};
  margin-bottom: 4px;
}
.kpv2-page-media-text-body {
  font-size: 12px;
  color: ${c.fg};
  line-height: 1.45;
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 9.5em;
  overflow: hidden;
}
.kpv2-page-media-text-row.is-full-page .kpv2-page-media-text-body {
  max-height: 14em;
}
.kpv2-page-media-text-meta {
  margin-top: 6px;
  font-size: 10px;
  color: ${c.fgMute};
}
.kpv2-page-media-text-actions.kpv2-page-media-actions {
  position: static;
  left: auto;
  right: auto;
  bottom: auto;
  flex: 0 0 auto;
  width: auto;
  max-width: 220px;
  opacity: 0;
  pointer-events: none;
  margin-top: 2px;
}
.kpv2-page-media-url-row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: space-between;
  gap: 6px;
  padding: 10px;
  min-height: 88px;
  border: 1px solid ${c.panelEdgeDark};
  border-radius: 3px;
  background: ${c.fieldBg};
  box-shadow: 0 0 0 1px ${c.panelEdge} inset;
  cursor: pointer;
  color: inherit;
  overflow: hidden;
  position: relative;
}
.kpv2-page-media-url-row:hover {
  border-color: ${c.accent};
}
.kpv2-page-media-url-row:hover .kpv2-page-media-url-actions,
.kpv2-page-media-url-row:focus-within .kpv2-page-media-url-actions {
  opacity: 1;
  pointer-events: auto;
}
.kpv2-page-media-url-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.kpv2-page-media-url-title {
  font-size: 12px;
  font-weight: 600;
  color: ${c.fg};
  line-height: 1.35;
  word-break: break-word;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.kpv2-page-media-url-text {
  font-size: 11px;
  color: ${c.fgDim};
  word-break: break-all;
  line-height: 1.35;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.kpv2-page-media-url-meta {
  margin-top: 2px;
  font-size: 10px;
  color: ${c.fgMute};
  text-transform: none;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
}
.kpv2-page-media-url-actions.kpv2-page-media-actions {
  position: absolute;
  left: 6px;
  right: 6px;
  bottom: 6px;
  flex: 0 0 auto;
  width: auto;
  max-width: none;
  opacity: 0;
  pointer-events: none;
}
.kpv2-page-media-size-group {
  margin-bottom: 18px;
}
.kpv2-page-media-size-heading {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 0 0 10px;
  padding: 0 2px;
  font-size: 12px;
  font-weight: 600;
  color: ${c.fgDim};
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.kpv2-page-media-size-count {
  font-size: 11px;
  font-weight: 500;
  color: ${c.fgMute};
  text-transform: none;
  letter-spacing: 0;
}
.kpv2-page-media-size-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 12px;
  align-content: flex-start;
}
.kpv2-page-media-empty {
  grid-column: 1 / -1;
  text-align: center;
  padding: 48px 16px;
  color: ${c.fgMute};
  font-size: 13px;
}
.kpv2-page-media-card {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  padding: 0;
  margin: 0;
  border: 1px solid ${c.panelEdgeDark};
  border-radius: 3px;
  background: ${c.panel};
  box-shadow: 0 0 0 1px ${c.panelEdge} inset;
  overflow: hidden;
  cursor: pointer;
  text-align: left;
  color: inherit;
  font: inherit;
}
.kpv2-page-media-card:hover {
  border-color: ${c.accent};
}
.kpv2-page-media-card:hover .kpv2-page-media-actions,
.kpv2-page-media-card:focus-within .kpv2-page-media-actions {
  opacity: 1;
  pointer-events: auto;
}
.kpv2-page-media-thumb-wrap {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  /* Checkerboard so transparent PNGs / WebPs read clearly */
  background-color: #1a1a1a;
  background-image:
    linear-gradient(45deg, #2a2a2a 25%, transparent 25%),
    linear-gradient(-45deg, #2a2a2a 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #2a2a2a 75%),
    linear-gradient(-45deg, transparent 75%, #2a2a2a 75%);
  background-size: 16px 16px;
  background-position: 0 0, 0 8px, 8px -8px, -8px 0;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
.kpv2-page-media-meta-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 5px 8px;
  background: ${c.fieldBg};
  border-bottom: 1px solid ${c.panelEdgeDark};
  min-height: 22px;
  box-sizing: border-box;
}
.kpv2-page-media-badge {
  max-width: 50%;
  font-size: 10px;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: 0.01em;
  color: ${c.fgDim};
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.kpv2-page-media-badge-dims {
  text-align: left;
}
.kpv2-page-media-badge-size {
  text-align: right;
  margin-left: auto;
}
.kpv2-page-media-actions {
  position: absolute;
  left: 6px;
  right: 6px;
  bottom: 6px;
  display: flex;
  gap: 4px;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s ease;
  z-index: 2;
}
.kpv2-page-media-action {
  flex: 1;
  min-width: 0;
  padding: 5px 4px;
  border: ${NCT_DARK_UI_BTN_BORDER};
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  background: ${NCT_DARK_UI_BTN_GRADIENT};
  color: ${c.fg};
  font: inherit;
  font-size: 10px;
  font-weight: 600;
  line-height: 1.2;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0,0,0,0.45);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.kpv2-page-media-action:hover {
  background: ${NCT_DARK_UI_BTN_LIT_GRADIENT};
  border: ${NCT_DARK_UI_BTN_LIT_BORDER};
  color: #e8f0f8;
}
.kpv2-page-media-thumb {
  display: block;
  background: transparent;
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
}
.kpv2-page-media-thumb.is-cover {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}
.kpv2-page-media-thumb.is-native {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  object-fit: none;
  object-position: center;
}
.kpv2-page-media-content.is-aspect-original .kpv2-page-media-card-image .kpv2-page-media-thumb-wrap {
  aspect-ratio: auto;
  min-height: 72px;
}
.kpv2-page-media-content.is-aspect-original .kpv2-page-media-card-image .kpv2-page-media-thumb.is-original {
  width: 100%;
  height: auto;
  max-width: 100%;
  max-height: none;
  object-fit: contain;
  object-position: center;
}
.kpv2-page-media-glyph {
  width: 100%;
  aspect-ratio: 1 / 1;
  min-height: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: ${c.fieldBg};
  color: ${c.accent};
  font-size: 18px;
  font-weight: 700;
  letter-spacing: 0.04em;
}
.kpv2-page-media-thumb-wrap .kpv2-page-media-glyph {
  position: absolute;
  inset: 0;
  min-height: 0;
  aspect-ratio: auto;
}
.kpv2-page-media-info {
  padding: 8px 10px;
  border-top: 1px solid ${c.panelEdgeDark};
}
.kpv2-page-media-name {
  font-size: 11px;
  color: ${c.fg};
  word-break: break-all;
  line-height: 1.3;
}
.kpv2-page-media-meta {
  margin-top: 4px;
  font-size: 10px;
  color: ${c.fgMute};
  line-height: 1.35;
  letter-spacing: 0.01em;
  text-transform: none;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
}
.kpv2-page-media-url-kind {
  display: inline-flex;
  align-items: center;
}
.kpv2-page-media-meta-extra {
  color: ${c.fgMute};
}
.kpv2-page-media-ext-chip {
  display: inline-flex;
  align-items: center;
  padding: 1px 7px;
  border-radius: 999px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.04em;
  line-height: 1.4;
  border: 1px solid transparent;
  white-space: nowrap;
  color: ${c.fgDim};
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.12);
}
.kpv2-page-media-ext-chip[data-ext="jpg"] {
  color: #f6d7a8;
  background: rgba(196, 120, 28, 0.28);
  border-color: rgba(232, 168, 64, 0.5);
}
.kpv2-page-media-ext-chip[data-ext="png"] {
  color: #b8d4f8;
  background: rgba(48, 110, 196, 0.3);
  border-color: rgba(96, 160, 236, 0.5);
}
.kpv2-page-media-ext-chip[data-ext="gif"] {
  color: #f4c4e4;
  background: rgba(176, 48, 140, 0.3);
  border-color: rgba(228, 96, 188, 0.5);
}
.kpv2-page-media-ext-chip[data-ext="webp"] {
  color: #c4ecc0;
  background: rgba(40, 140, 72, 0.28);
  border-color: rgba(88, 196, 112, 0.5);
}
.kpv2-page-media-ext-chip[data-ext="avif"] {
  color: #b8f0ea;
  background: rgba(16, 140, 148, 0.3);
  border-color: rgba(64, 212, 204, 0.5);
}
.kpv2-page-media-ext-chip[data-ext="svg"] {
  color: #f8c8a0;
  background: rgba(196, 96, 32, 0.28);
  border-color: rgba(236, 140, 64, 0.5);
}
.kpv2-page-media-ext-chip[data-ext="ico"] {
  color: #d0d4dc;
  background: rgba(120, 128, 140, 0.28);
  border-color: rgba(168, 176, 188, 0.45);
}
.kpv2-page-media-ext-chip[data-ext="bmp"] {
  color: #e8d0b0;
  background: rgba(140, 92, 40, 0.3);
  border-color: rgba(196, 140, 72, 0.45);
}
.kpv2-page-media-ext-chip[data-ext="tif"] {
  color: #dcc8f8;
  background: rgba(112, 64, 176, 0.3);
  border-color: rgba(168, 120, 228, 0.5);
}
.kpv2-page-media-ext-chip[data-ext="heic"],
.kpv2-page-media-ext-chip[data-ext="heif"] {
  color: #c8e0f8;
  background: rgba(32, 88, 160, 0.32);
  border-color: rgba(80, 148, 220, 0.5);
}
.kpv2-page-media-ext-chip[data-ext="mp4"],
.kpv2-page-media-ext-chip[data-ext="m4v"] {
  color: #f8c0c0;
  background: rgba(176, 40, 48, 0.3);
  border-color: rgba(228, 88, 88, 0.5);
}
.kpv2-page-media-ext-chip[data-ext="webm"] {
  color: #d4f4a8;
  background: rgba(88, 140, 24, 0.3);
  border-color: rgba(148, 204, 56, 0.5);
}
.kpv2-page-media-ext-chip[data-ext="mov"] {
  color: #f0d8b0;
  background: rgba(160, 96, 24, 0.3);
  border-color: rgba(220, 148, 56, 0.5);
}
.kpv2-page-media-ext-chip[data-ext="pdf"] {
  color: #f8c4c0;
  background: rgba(176, 32, 40, 0.32);
  border-color: rgba(228, 72, 72, 0.55);
}
.kpv2-page-media-ext-chip[data-ext="md"],
.kpv2-page-media-ext-chip[data-ext="markdown"] {
  color: #c8d8f8;
  background: rgba(56, 80, 160, 0.3);
  border-color: rgba(112, 140, 220, 0.5);
}
.kpv2-page-media-ext-chip[data-ext="txt"] {
  color: #d4d8dc;
  background: rgba(100, 108, 116, 0.28);
  border-color: rgba(160, 168, 176, 0.4);
}
.kpv2-page-media-fullview {
  display: none;
  position: absolute;
  inset: 0;
  z-index: 1;
  background: rgba(0, 0, 0, 0.92);
  align-items: center;
  justify-content: center;
  cursor: zoom-out;
}
.kpv2-page-media-fullview.is-open {
  display: flex;
}
.kpv2-page-media-fullmedia {
  max-width: 90vw;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  cursor: default;
}
.kpv2-page-media-fullimage {
  max-width: 90vw;
  max-height: 85vh;
  object-fit: contain;
  border-radius: 3px;
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
}
.kpv2-page-media-fullvideo {
  max-width: 90vw;
  max-height: 80vh;
  background: #000;
}
.kpv2-page-media-openlink {
  color: ${c.accent};
  font-size: 12px;
}
.kpv2-page-media-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 40px;
  height: 40px;
  border: ${NCT_DARK_UI_BTN_BORDER};
  border-radius: 50%;
  background: rgba(0,0,0,0.55);
  color: #fff;
  font-size: 22px;
  cursor: pointer;
}
.kpv2-page-media-nav-prev { left: 20px; }
.kpv2-page-media-nav-next { right: 20px; }
.kpv2-page-media-fullcounter {
  position: absolute;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  padding: 6px 14px;
  border-radius: 2px;
  background: rgba(0,0,0,0.7);
  font-size: 12px;
  color: ${c.fg};
}
@media (max-width: 640px) {
  .kpv2-page-media-content {
    grid-template-columns: repeat(auto-fill, minmax(calc(140px * var(--kpv2-pm-image-scale, 1.5)), 1fr));
    padding: 10px;
    gap: calc(8px * var(--kpv2-pm-image-scale, 1.5));
  }
  .kpv2-page-media-size-grid {
    grid-template-columns: repeat(auto-fill, minmax(calc(110px * var(--kpv2-pm-image-scale, 1.5)), 1fr));
    gap: calc(8px * var(--kpv2-pm-image-scale, 1.5));
  }
  .kpv2-page-media-url-card-grid {
    grid-template-columns: repeat(auto-fill, minmax(calc(140px * var(--kpv2-pm-image-scale, 1.5)), 1fr));
    gap: calc(8px * var(--kpv2-pm-image-scale, 1.5));
  }
  .kpv2-page-media-header {
    flex-wrap: wrap;
  }
  .kpv2-page-media-scale-range,
  .kp-nct-scale-range {
    width: 72px;
  }
}
`;
    injectChromeStyles(root, {
      attr: "data-kp-page-media-styles",
      css
    });
  }

  // src/ui/media-library-overlay.js
  init_constants();
  var OVERLAY_ID2 = "kpv2-media-lib-overlay";
  var _overlay2 = null;
  var _onClose3 = null;
  var _keyHandler2 = null;
  var _notify2 = () => {
  };
  var _kind = "image";
  var _domain = "";
  var _navExpanded = { image: true, video: true, document: true, url: true };
  var _items2 = [];
  var _domains = [];
  var _counts = { image: 0, video: 0, document: 0, url: 0 };
  var _selected = /* @__PURE__ */ new Set();
  var _anchorId = null;
  var _fullViewId = null;
  var _fullObjectUrl = null;
  var _prevOverflow2 = null;
  var _loadGen = 0;
  var _remoteReloadTimer = 0;
  function emptyCounts() {
    return { image: 0, video: 0, document: 0, url: 0 };
  }
  function kindHasGallery(kind) {
    return kind === "image" || kind === "url" || kind === "video" || kind === "document";
  }
  function getOverlayRoot2() {
    return _overlay2?.shadowRoot || _overlay2;
  }
  function isMediaLibraryOverlayOpen() {
    return !!_overlay2 && !!document.getElementById(OVERLAY_ID2);
  }
  function requestCloseMediaLibraryOverlay() {
    if (!isMediaLibraryOverlayOpen()) return false;
    const modal = getOverlayRoot2()?.querySelector(".kpv2-media-lib-fullview");
    if (modal && modal.classList.contains("is-open")) {
      closeFullView2();
      return true;
    }
    closeMediaLibraryOverlay();
    return true;
  }
  function closeMediaLibraryOverlay() {
    if (_remoteReloadTimer) {
      try {
        clearTimeout(_remoteReloadTimer);
      } catch {
      }
      _remoteReloadTimer = 0;
    }
    if (_keyHandler2) {
      try {
        document.removeEventListener("keydown", _keyHandler2, true);
      } catch {
      }
      _keyHandler2 = null;
    }
    revokeFullObjectUrl();
    if (_overlay2) {
      try {
        _overlay2.remove();
      } catch {
      }
      _overlay2 = null;
    }
    if (_prevOverflow2 != null) {
      try {
        document.body.style.overflow = _prevOverflow2;
      } catch {
      }
      _prevOverflow2 = null;
    }
    _items2 = [];
    _domains = [];
    _selected = /* @__PURE__ */ new Set();
    _anchorId = null;
    _fullViewId = null;
    const cb = _onClose3;
    _onClose3 = null;
    if (typeof cb === "function") {
      try {
        cb();
      } catch {
      }
    }
  }
  function revokeFullObjectUrl() {
    if (_fullObjectUrl) {
      try {
        URL.revokeObjectURL(_fullObjectUrl);
      } catch {
      }
      _fullObjectUrl = null;
    }
  }
  async function openMediaLibraryOverlay(opts = {}) {
    closeMediaLibraryOverlay();
    _onClose3 = typeof opts.onClose === "function" ? opts.onClose : null;
    _notify2 = typeof opts.onNotify === "function" ? opts.onNotify : () => {
    };
    _kind = "image";
    _domain = "";
    _navExpanded = { image: true, video: true, url: true };
    _selected = /* @__PURE__ */ new Set();
    _anchorId = null;
    const overlay = document.createElement("div");
    overlay.id = OVERLAY_ID2;
    overlay.className = "kpv2-media-lib-overlay";
    overlay.setAttribute("role", "dialog");
    overlay.setAttribute("aria-label", "Media Library");
    overlay.style.cssText = `
    position: fixed;
    inset: 0;
    z-index: ${Z_INDEX.POPUP_PANEL_MAX + 20};
    pointer-events: auto;
  `;
    const shadowRoot = ensureOpenChromeShadow(overlay, { id: "media-lib" });
    const mount = shadowRoot || overlay;
    ensureStyles4(mount);
    const backdrop = document.createElement("div");
    backdrop.className = NCT_DARK_UI_BACKDROP_CLASS;
    backdrop.setAttribute("aria-hidden", "true");
    const shell = document.createElement("div");
    shell.className = "kpv2-media-lib-shell";
    const header = document.createElement("div");
    header.className = "kpv2-media-lib-header";
    const titleWrap = document.createElement("div");
    titleWrap.className = "kpv2-media-lib-title-wrap";
    const title = document.createElement("h2");
    title.className = "kpv2-media-lib-title";
    title.textContent = "Media Library";
    const subtitle = document.createElement("span");
    subtitle.className = "kpv2-media-lib-subtitle";
    subtitle.textContent = "Personal scrapbook";
    titleWrap.appendChild(title);
    titleWrap.appendChild(subtitle);
    const toolbar = document.createElement("div");
    toolbar.className = "kpv2-media-lib-toolbar";
    toolbar.setAttribute("role", "toolbar");
    toolbar.setAttribute("aria-label", "Library actions");
    const zipCatBtn = makeHeaderBtn("Download zip", "Download this category as a zip", () => zipCurrentView());
    zipCatBtn.dataset.role = "zip-category";
    const zipSelBtn = makeHeaderBtn("Download selected", "Download selected items as a zip", () => zipSelection());
    zipSelBtn.dataset.role = "zip-selected";
    zipSelBtn.disabled = true;
    const delBtn = makeHeaderBtn("Delete selected", "Remove selected items from the library", () => deleteSelection());
    delBtn.dataset.role = "delete-selected";
    delBtn.disabled = true;
    toolbar.appendChild(zipCatBtn);
    toolbar.appendChild(zipSelBtn);
    toolbar.appendChild(delBtn);
    const closeBtn = makeHeaderBtn("Close", "Close Media Library", () => closeMediaLibraryOverlay());
    closeBtn.classList.add("kpv2-media-lib-close");
    header.appendChild(titleWrap);
    header.appendChild(toolbar);
    header.appendChild(closeBtn);
    const body = document.createElement("div");
    body.className = "kpv2-media-lib-body";
    const sidebar = document.createElement("nav");
    sidebar.className = `kpv2-media-lib-sidebar ${NCT_DARK_UI_SCROLLBAR_CLASS}`;
    sidebar.setAttribute("aria-label", "Media types");
    const content = document.createElement("div");
    content.className = `kpv2-media-lib-content ${NCT_DARK_UI_SCROLLBAR_CLASS}`;
    content.id = "kpv2-media-lib-grid";
    const fullView = document.createElement("div");
    fullView.className = "kpv2-media-lib-fullview";
    fullView.setAttribute("aria-hidden", "true");
    const prevBtn = document.createElement("button");
    prevBtn.type = "button";
    prevBtn.className = "kpv2-media-lib-nav kpv2-media-lib-nav-prev";
    prevBtn.textContent = "\u2039";
    prevBtn.addEventListener("click", (e) => {
      e.preventDefault();
      e.stopPropagation();
      navigateFullView2(-1);
    }, true);
    const nextBtn = document.createElement("button");
    nextBtn.type = "button";
    nextBtn.className = "kpv2-media-lib-nav kpv2-media-lib-nav-next";
    nextBtn.textContent = "\u203A";
    nextBtn.addEventListener("click", (e) => {
      e.preventDefault();
      e.stopPropagation();
      navigateFullView2(1);
    }, true);
    const fullMediaHost = document.createElement("div");
    fullMediaHost.className = "kpv2-media-lib-fullmedia";
    fullMediaHost.addEventListener("click", (e) => e.stopPropagation(), true);
    const counter = document.createElement("div");
    counter.className = "kpv2-media-lib-fullcounter";
    fullView.appendChild(prevBtn);
    fullView.appendChild(fullMediaHost);
    fullView.appendChild(nextBtn);
    fullView.appendChild(counter);
    fullView.addEventListener("click", () => closeFullView2(), true);
    body.appendChild(sidebar);
    body.appendChild(content);
    shell.appendChild(header);
    shell.appendChild(body);
    shell.appendChild(fullView);
    mount.appendChild(backdrop);
    mount.appendChild(shell);
    overlay._sidebar = sidebar;
    overlay._content = content;
    overlay._fullView = fullView;
    overlay._fullMediaHost = fullMediaHost;
    overlay._fullCounter = counter;
    overlay._zipSelBtn = zipSelBtn;
    overlay._delBtn = delBtn;
    document.body.appendChild(overlay);
    _overlay2 = overlay;
    try {
      _prevOverflow2 = document.body.style.overflow || "";
      document.body.style.overflow = "hidden";
    } catch {
    }
    _keyHandler2 = (e) => {
      if (e.key === "Escape" || e.code === "Escape") {
        e.preventDefault();
        e.stopPropagation();
        try {
          e.stopImmediatePropagation();
        } catch {
        }
        requestCloseMediaLibraryOverlay();
        return;
      }
      if (e.key === "ArrowLeft" && _fullViewId) {
        e.preventDefault();
        navigateFullView2(-1);
      } else if (e.key === "ArrowRight" && _fullViewId) {
        e.preventDefault();
        navigateFullView2(1);
      }
    };
    document.addEventListener("keydown", _keyHandler2, true);
    await reload();
  }
  function makeHeaderBtn(label, title, handler) {
    const btn = document.createElement("button");
    btn.type = "button";
    btn.className = "kpv2-media-lib-btn";
    btn.textContent = label;
    btn.title = title;
    btn.addEventListener("click", async (e) => {
      e.preventDefault();
      e.stopPropagation();
      if (btn.disabled) return;
      try {
        await handler();
      } catch (err) {
        console.warn("[KeyPilot] Media Library action failed:", err);
        _notify2("Action failed", "error");
      }
    }, true);
    return btn;
  }
  function updateSelectionButtons() {
    const n = _selected.size;
    const zipSel = (
      /** @type {any} */
      _overlay2?._zipSelBtn
    );
    const delBtn = (
      /** @type {any} */
      _overlay2?._delBtn
    );
    if (zipSel) {
      zipSel.disabled = n === 0;
      zipSel.textContent = n > 0 ? `Download selected (${n})` : "Download selected";
    }
    if (delBtn) {
      delBtn.disabled = n === 0;
      delBtn.textContent = n > 0 ? `Delete selected (${n})` : "Delete selected";
    }
  }
  async function reload() {
    if (!isMediaLibraryOverlayOpen()) return;
    const gen = ++_loadGen;
    try {
      const result = await listMediaLibrary({
        kind: _kind,
        domain: kindHasGallery(_kind) ? _domain : "",
        includeThumbs: _kind === "image" || _kind === "video"
      });
      if (gen !== _loadGen || !isMediaLibraryOverlayOpen()) return;
      if (!result?.success) {
        _notify2(result?.error || "Could not load Media Library", "error");
        _items2 = [];
        _domains = [];
        _counts = emptyCounts();
      } else {
        _items2 = Array.isArray(result.items) ? result.items : [];
        _domains = Array.isArray(result.domains) ? result.domains : [];
        _counts = result.counts || emptyCounts();
      }
      const keep = new Set(_items2.map((it) => it.id));
      for (const id of Array.from(_selected)) {
        if (!keep.has(id)) _selected.delete(id);
      }
      if (_fullViewId && !keep.has(_fullViewId)) {
        closeFullView2();
      }
      renderSidebar();
      renderGrid2();
      updateSelectionButtons();
    } catch (err) {
      if (gen !== _loadGen) return;
      console.warn("[KeyPilot] Media Library list failed:", err);
      _notify2("Could not load Media Library", "error");
    }
  }
  function scheduleRemoteReload() {
    if (!isMediaLibraryOverlayOpen()) return;
    if (_remoteReloadTimer) {
      try {
        clearTimeout(_remoteReloadTimer);
      } catch {
      }
    }
    _remoteReloadTimer = setTimeout(() => {
      _remoteReloadTimer = 0;
      void reload();
    }, 50);
  }
  try {
    installContentRuntimeRouter();
    registerContentRuntimeHandler(MSG.MEDIA_LIBRARY_CHANGED, (msg) => {
      if (msg?.type !== MSG.MEDIA_LIBRARY_CHANGED) return;
      if (typeof window !== "undefined" && window !== window.top) return;
      scheduleRemoteReload();
    });
  } catch {
  }
  function renderSidebar() {
    const sidebar = (
      /** @type {HTMLElement|null} */
      /** @type {any} */
      _overlay2?._sidebar
    );
    if (!sidebar) return;
    sidebar.replaceChildren();
    sidebar.appendChild(buildKindTab({
      kind: "image",
      label: "Images",
      count: _counts.image,
      enabled: true,
      expandable: true,
      expanded: !!_navExpanded.image
    }));
    if (_navExpanded.image && _kind === "image") {
      for (const { domain, count } of _domains) {
        sidebar.appendChild(buildDomainRow(domain, count));
      }
    }
    sidebar.appendChild(buildKindTab({
      kind: "url",
      label: "URLs",
      count: _counts.url || 0,
      enabled: true,
      expandable: true,
      expanded: !!_navExpanded.url
    }));
    if (_navExpanded.url && _kind === "url") {
      for (const { domain, count } of _domains) {
        sidebar.appendChild(buildDomainRow(domain, count));
      }
    }
    sidebar.appendChild(buildKindTab({
      kind: "video",
      label: "Videos",
      count: _counts.video || 0,
      enabled: true,
      expandable: true,
      expanded: !!_navExpanded.video
    }));
    if (_navExpanded.video && _kind === "video") {
      for (const { domain, count } of _domains) {
        sidebar.appendChild(buildDomainRow(domain, count));
      }
    }
    sidebar.appendChild(buildKindTab({
      kind: "document",
      label: "Documents",
      count: _counts.document || 0,
      enabled: true,
      expandable: true,
      expanded: !!_navExpanded.document
    }));
    if (_navExpanded.document && _kind === "document") {
      for (const { domain, count } of _domains) {
        sidebar.appendChild(buildDomainRow(domain, count));
      }
    }
  }
  function buildKindTab(def) {
    const btn = document.createElement("button");
    btn.type = "button";
    btn.className = "kpv2-media-lib-tab";
    if (def.enabled && _kind === def.kind && !_domain) btn.classList.add("is-active");
    if (!def.enabled) {
      btn.classList.add("is-disabled");
      btn.disabled = true;
      btn.title = "Coming soon";
    }
    btn.setAttribute("role", "tab");
    btn.setAttribute("aria-selected", def.enabled && _kind === def.kind && !_domain ? "true" : "false");
    const label = document.createElement("span");
    label.className = "kpv2-media-lib-tab-label";
    if (def.expandable) {
      const chev = document.createElement("span");
      chev.className = "kpv2-media-lib-chev";
      chev.textContent = def.expanded ? "\u25BE" : "\u25B8";
      label.appendChild(chev);
    }
    const name = document.createElement("span");
    name.textContent = def.label;
    label.appendChild(name);
    const badge = document.createElement("span");
    badge.className = "kpv2-media-lib-tab-count";
    badge.textContent = def.enabled ? String(def.count) : "soon";
    btn.appendChild(label);
    btn.appendChild(badge);
    if (def.enabled) {
      btn.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        const clickingSame = _kind === def.kind && !_domain;
        if (def.expandable && clickingSame) {
          _navExpanded[def.kind] = !_navExpanded[def.kind];
          renderSidebar();
          return;
        }
        _kind = def.kind;
        _domain = "";
        if (def.expandable) _navExpanded[def.kind] = true;
        _selected = /* @__PURE__ */ new Set();
        void reload();
      }, true);
    }
    return btn;
  }
  function buildDomainRow(domain, count) {
    const btn = document.createElement("button");
    btn.type = "button";
    btn.className = "kpv2-media-lib-tab kpv2-media-lib-tab-domain";
    if (_domain === domain) btn.classList.add("is-active");
    btn.setAttribute("role", "tab");
    const label = document.createElement("span");
    label.className = "kpv2-media-lib-tab-label";
    label.textContent = domain;
    const badge = document.createElement("span");
    badge.className = "kpv2-media-lib-tab-count";
    badge.textContent = String(count);
    btn.appendChild(label);
    btn.appendChild(badge);
    btn.title = domain;
    btn.addEventListener("click", (e) => {
      e.preventDefault();
      e.stopPropagation();
      _domain = domain;
      _selected = /* @__PURE__ */ new Set();
      void reload();
    }, true);
    return btn;
  }
  function renderGrid2() {
    const content = (
      /** @type {HTMLElement|null} */
      /** @type {any} */
      _overlay2?._content
    );
    if (!content) return;
    content.replaceChildren();
    if (!kindHasGallery(_kind)) {
      const empty = document.createElement("div");
      empty.className = "kpv2-media-lib-empty";
      empty.textContent = "Coming soon";
      content.appendChild(empty);
      return;
    }
    if (!_items2.length) {
      const empty = document.createElement("div");
      empty.className = "kpv2-media-lib-empty";
      empty.textContent = _kind === "url" ? _domain ? `No URLs from ${_domain}` : "No URLs yet. Set Copy URL destination to Media Library or Both." : _kind === "video" ? _domain ? `No videos from ${_domain}` : "No videos yet. Set Copy Video destination to Media Library or Both." : _kind === "document" ? _domain ? `No documents from ${_domain}` : "No documents yet. Use Fetch URL for Media Library or send from Page Media." : _domain ? `No images from ${_domain}` : "No images yet. Send from Page Media (O) or set Copy Image destination to Media Library.";
      content.appendChild(empty);
      return;
    }
    if (!_domain) {
      const groups = /* @__PURE__ */ new Map();
      for (const item of _items2) {
        const d = item.sourceDomain || item.domain || "(unknown)";
        if (!groups.has(d)) groups.set(d, []);
        groups.get(d).push(item);
      }
      const keys = Array.from(groups.keys()).sort((a, b) => a.localeCompare(b));
      for (const domain of keys) {
        const section = document.createElement("section");
        section.className = "kpv2-media-lib-section";
        const heading = document.createElement("h3");
        heading.className = "kpv2-media-lib-section-title";
        heading.textContent = domain;
        const count = document.createElement("span");
        count.className = "kpv2-media-lib-section-count";
        count.textContent = String(groups.get(domain).length);
        heading.appendChild(count);
        const grid2 = document.createElement("div");
        grid2.className = "kpv2-media-lib-grid";
        for (const item of groups.get(domain)) {
          grid2.appendChild(buildCard(item));
        }
        section.appendChild(heading);
        section.appendChild(grid2);
        content.appendChild(section);
      }
      return;
    }
    const grid = document.createElement("div");
    grid.className = "kpv2-media-lib-grid";
    for (const item of _items2) {
      grid.appendChild(buildCard(item));
    }
    content.appendChild(grid);
  }
  function handleCardSelectClick(itemId, e) {
    e.preventDefault();
    e.stopPropagation();
    if (e.detail > 1) return;
    if (e.shiftKey && _anchorId) {
      selectRange(_anchorId, itemId);
      return;
    }
    toggleSelected(itemId);
  }
  function buildCard(item) {
    const selected = _selected.has(item.id);
    const card = document.createElement("article");
    card.className = "kpv2-media-lib-card";
    card.dataset.id = item.id;
    if (selected) card.classList.add("is-selected");
    const isUrl = item.kind === "url";
    const isVideo = item.kind === "video";
    const isDoc = item.kind === "document";
    const wrap = document.createElement("div");
    wrap.className = "kpv2-media-lib-thumb-wrap";
    if (isUrl) wrap.classList.add("is-url");
    if (isVideo) wrap.classList.add("is-video");
    if (isDoc) wrap.classList.add("is-document");
    const img = document.createElement("img");
    img.className = "kpv2-media-lib-thumb";
    img.alt = "";
    img.draggable = false;
    if (item.thumbDataUrl) {
      img.src = item.thumbDataUrl;
      wrap.appendChild(img);
    } else {
      const ph = document.createElement("span");
      ph.className = "kpv2-media-lib-placeholder";
      ph.textContent = isUrl ? "URL" : isVideo ? "VID" : String(item.ext || (isDoc ? "DOC" : "IMG"));
      wrap.appendChild(ph);
    }
    const sourceHost = String(item.sourceDomain || item.domain || "").trim();
    const selectLabel = `Select ${sourceHost || (isUrl ? "URL" : isVideo ? "video" : isDoc ? "document" : "image")}`;
    const selectBtn = document.createElement("button");
    selectBtn.type = "button";
    selectBtn.className = "kpv2-media-lib-select";
    selectBtn.setAttribute("aria-label", selectLabel);
    selectBtn.setAttribute("aria-pressed", selected ? "true" : "false");
    const check = document.createElement("span");
    check.className = "kpv2-media-lib-check";
    check.setAttribute("aria-hidden", "true");
    selectBtn.appendChild(check);
    wrap.appendChild(selectBtn);
    const openBtn = document.createElement("button");
    openBtn.type = "button";
    openBtn.className = "kpv2-media-lib-open";
    openBtn.setAttribute("aria-label", isUrl ? `Open ${sourceHost || "URL"}` : isVideo ? `Play ${sourceHost || "video"}` : isDoc ? `Open ${sourceHost || "document"}` : `Open ${sourceHost || "image"}`);
    wrap.appendChild(openBtn);
    wrap.appendChild(buildHoverActions2(item));
    const metaSelect = document.createElement("button");
    metaSelect.type = "button";
    metaSelect.className = "kpv2-media-lib-meta-select";
    metaSelect.setAttribute("aria-label", selectLabel);
    metaSelect.setAttribute("aria-pressed", selected ? "true" : "false");
    const metaRow = document.createElement("div");
    metaRow.className = "kpv2-media-lib-meta-row";
    const typeBadge = document.createElement("span");
    typeBadge.className = "kpv2-media-lib-badge";
    typeBadge.textContent = String(item.ext || (isUrl ? "URL" : isVideo ? "VID" : isDoc ? "DOC" : "IMG"));
    const dimBadge = document.createElement("span");
    dimBadge.className = "kpv2-media-lib-badge kpv2-media-lib-badge-dims";
    const w = Number(item.width) || 0;
    const h = Number(item.height) || 0;
    dimBadge.textContent = isUrl ? "LINK" : isDoc ? "FILE" : w > 0 && h > 0 ? `${w}\xD7${h}` : "\u2014";
    metaRow.appendChild(typeBadge);
    metaRow.appendChild(dimBadge);
    const info = document.createElement("div");
    info.className = "kpv2-media-lib-info";
    const pageHost = String(item.pageDomain || "").trim();
    const name = document.createElement("div");
    name.className = "kpv2-media-lib-name";
    name.textContent = isUrl ? urlCardTitle(item) : sourceHost || "(unknown)";
    name.title = item.sourceUrl || "";
    info.appendChild(name);
    if (pageHost && pageHost !== sourceHost) {
      const page = document.createElement("div");
      page.className = "kpv2-media-lib-page";
      page.textContent = pageHost;
      page.title = item.pageUrl || "";
      info.appendChild(page);
    }
    const sizeRow = document.createElement("div");
    sizeRow.className = "kpv2-media-lib-size-row";
    const size = document.createElement("span");
    size.className = "kpv2-media-lib-size";
    size.textContent = librarySizeLabel(item);
    const dpi = document.createElement("span");
    dpi.className = "kpv2-media-lib-dpi";
    const dpiVal = Number(item.dpi) || 0;
    dpi.textContent = isUrl || isVideo || isDoc ? "" : dpiVal > 0 ? `${Math.round(dpiVal)} dpi` : "\u2014";
    sizeRow.appendChild(size);
    sizeRow.appendChild(dpi);
    info.appendChild(sizeRow);
    if (isVideo) {
      void resolveVideoCardFileSize(item, size);
    }
    metaSelect.appendChild(metaRow);
    metaSelect.appendChild(info);
    card.appendChild(wrap);
    card.appendChild(metaSelect);
    const onSelect = (e) => handleCardSelectClick(item.id, e);
    const onOpen = (e) => {
      e.preventDefault();
      e.stopPropagation();
      if (item.kind === "url") openStoredUrl(item);
      else void openFullView2(item.id);
    };
    selectBtn.addEventListener("click", onSelect, true);
    metaSelect.addEventListener("click", onSelect, true);
    openBtn.addEventListener("click", onOpen, true);
    metaSelect.addEventListener("dblclick", onOpen, true);
    return card;
  }
  function buildHoverActions2(item) {
    const bar = document.createElement("div");
    bar.className = "kpv2-media-lib-actions";
    bar.setAttribute("role", "group");
    bar.setAttribute("aria-label", "Item actions");
    const mk = (label, title, handler) => {
      const btn = document.createElement("button");
      btn.type = "button";
      btn.className = "kpv2-media-lib-action";
      btn.textContent = label;
      btn.title = title;
      btn.addEventListener("click", async (e) => {
        e.preventDefault();
        e.stopPropagation();
        try {
          await handler();
        } catch (err) {
          console.warn("[KeyPilot] Media Library action failed:", err);
          _notify2("Action failed", "error");
        }
      }, true);
      return btn;
    };
    bar.appendChild(mk("Copy", isUrlItem(item) ? "Copy URL" : isVideoItem(item) ? item.thumbDataUrl ? "Copy frame" : "Copy video URL" : isDocumentItem(item) ? item.sourceUrl ? "Copy URL" : "Copy file name" : "Copy to pasteboard", () => copyLibraryItem(item)));
    if (isUrlItem(item) || isVideoItem(item) && isHttpUrl(item.sourceUrl) || isDocumentItem(item) && isHttpUrl(item.sourceUrl)) {
      bar.appendChild(mk("Open", "Open in a new tab", async () => openStoredUrl(item)));
    }
    bar.appendChild(mk("Download", isUrlItem(item) || isVideoShortcut(item) ? "Download shortcut" : "Download file", () => downloadLibraryItem(item)));
    return bar;
  }
  function isUrlItem(item) {
    return item?.kind === "url";
  }
  function isVideoItem(item) {
    return item?.kind === "video";
  }
  function isDocumentItem(item) {
    return item?.kind === "document";
  }
  function isVideoShortcut(item) {
    return isVideoItem(item) && (String(item?.ext || "") === "URL" || /^text\/uri-list/i.test(String(item?.mime || "")));
  }
  function librarySizeLabel(item) {
    if (item?.kind === "url" || isVideoShortcut(item)) return "Saved link";
    return formatFileSize(item?.byteSize);
  }
  async function resolveVideoCardFileSize(item, sizeEl) {
    if (!item || !sizeEl) return;
    const stored = Number(item.byteSize) || 0;
    if (!isVideoShortcut(item) && stored > 0) {
      sizeEl.textContent = formatFileSize(stored);
      return;
    }
    const href = String(item.sourceUrl || "").trim();
    if (!isHttpUrl(href)) return;
    try {
      const bytes = await probeRemoteFileSize(href);
      if (!(bytes > 0) || !sizeEl.isConnected) return;
      item.byteSize = bytes;
      sizeEl.textContent = formatFileSize(bytes);
    } catch {
    }
  }
  async function probeRemoteFileSize(url) {
    try {
      const head = await fetch(url, { method: "HEAD", credentials: "omit", cache: "force-cache" });
      if (head.ok) {
        const len = head.headers.get("content-length");
        if (len && /^\d+$/.test(len)) return Number(len);
      }
    } catch {
    }
    try {
      const res = await fetch(url, {
        method: "GET",
        credentials: "omit",
        cache: "force-cache",
        headers: { Range: "bytes=0-0" }
      });
      if (res.ok || res.status === 206) {
        const cr = res.headers.get("content-range");
        const m = cr && cr.match(/\/(\d+)\s*$/);
        if (m) return Number(m[1]);
        const len = res.headers.get("content-length");
        if (len && /^\d+$/.test(len) && res.status !== 206) return Number(len);
      }
    } catch {
    }
    return 0;
  }
  function isHttpUrl(href) {
    return /^https?:\/\//i.test(String(href || "").trim());
  }
  function urlCardTitle(item) {
    const href = String(item?.sourceUrl || "");
    try {
      const u = new URL(href);
      const path = `${u.pathname || "/"}${u.search || ""}`;
      if (path === "/" || path === "") return u.hostname || href;
      return path.length > 48 ? `${path.slice(0, 45)}\u2026` : path;
    } catch {
      return href || String(item?.domain || "URL");
    }
  }
  function openStoredUrl(item) {
    const href = String(item?.sourceUrl || "").trim();
    if (!href) {
      _notify2("No URL", "error");
      return;
    }
    if (!isHttpUrl(href)) {
      _notify2("This link cannot be opened in a new tab", "error");
      return;
    }
    try {
      window.open(href, "_blank", "noopener,noreferrer");
    } catch {
      _notify2("Could not open URL", "error");
    }
  }
  async function copyLibraryItem(item) {
    if (item?.kind === "url" && item.sourceUrl) {
      try {
        if (navigator.clipboard?.writeText) {
          await navigator.clipboard.writeText(item.sourceUrl);
          _notify2("URL copied", "success");
          return;
        }
      } catch {
      }
      _notify2("Could not copy URL", "error");
      return;
    }
    if (item?.kind === "video") {
      if (item.thumbDataUrl && /^data:image\//i.test(item.thumbDataUrl)) {
        try {
          const res = await fetch(item.thumbDataUrl);
          const blob2 = await res.blob();
          if (blob2 && blob2.size > 0) {
            const ok2 = await writeImageBlobToClipboard2(blob2, blob2.type || "image/png");
            if (ok2) {
              _notify2("Copied to pasteboard", "success");
              return;
            }
          }
        } catch {
        }
      }
      if (item.sourceUrl && navigator.clipboard?.writeText) {
        try {
          await navigator.clipboard.writeText(item.sourceUrl);
          _notify2("Video URL copied", "success");
          return;
        } catch {
        }
      }
      _notify2("Could not copy video", "error");
      return;
    }
    if (item?.kind === "document") {
      if (item.sourceUrl && navigator.clipboard?.writeText) {
        try {
          await navigator.clipboard.writeText(item.sourceUrl);
          _notify2("Document URL copied", "success");
          return;
        } catch {
        }
      }
      _notify2("Could not copy document", "error");
      return;
    }
    const result = await getMediaLibraryOriginal(item.id);
    const blob = result?.blob;
    if (!(blob instanceof Blob) || blob.size <= 0) {
      _notify2("Could not copy image", "error");
      return;
    }
    const mime = result?.item?.mime || blob.type || "image/png";
    const ok = await writeImageBlobToClipboard2(blob, mime);
    if (ok) {
      _notify2("Copied to pasteboard", "success");
      return;
    }
    try {
      if (item.sourceUrl && navigator.clipboard?.writeText) {
        await navigator.clipboard.writeText(item.sourceUrl);
        _notify2("Image URL copied", "success");
        return;
      }
    } catch {
    }
    _notify2("Could not copy to pasteboard", "error");
  }
  async function downloadLibraryItem(item) {
    if ((item?.kind === "url" || isVideoShortcut(item)) && item.sourceUrl) {
      const body = `[InternetShortcut]\r
URL=${item.sourceUrl}\r
`;
      downloadBlob(new Blob([body], { type: "application/internet-shortcut" }), libraryDownloadFilename(item));
      _notify2("Download started", "success");
      return;
    }
    const result = await getMediaLibraryOriginal(item.id);
    const blob = result?.blob;
    if (!(blob instanceof Blob) || blob.size <= 0) {
      _notify2("Could not download", "error");
      return;
    }
    downloadBlob(blob, libraryDownloadFilename(item));
    _notify2("Download started", "success");
  }
  function libraryDownloadFilename(item) {
    if (item?.kind === "url" || isVideoShortcut(item)) {
      let host = String(item.sourceDomain || item.domain || "").trim() || (item?.kind === "video" ? "video" : "link");
      host = host.replace(/[\\/:*?"<>|]+/g, "_").slice(0, 80);
      return `${host}.url`;
    }
    const ext = String(item.ext || (item?.kind === "video" ? "mp4" : item?.kind === "document" ? "bin" : "img")).toLowerCase();
    let base = "";
    try {
      const u = new URL(String(item.sourceUrl || ""));
      base = decodeURIComponent((u.pathname || "").split("/").pop() || "");
    } catch {
    }
    if (!base) {
      base = String(item.id || (item?.kind === "video" ? "video" : item?.kind === "document" ? "document" : "image")).replace(/^ml_/, "");
    }
    base = base.replace(/[\\/:*?"<>|]+/g, "_").slice(0, 120) || (item?.kind === "video" ? "video" : item?.kind === "document" ? "document" : "image");
    if (ext && !new RegExp(`\\.${ext}$`, "i").test(base)) base += `.${ext}`;
    return base;
  }
  async function writeImageBlobToClipboard2(blob, mimeType) {
    if (!navigator.clipboard || typeof navigator.clipboard.write !== "function") return false;
    const type2 = mimeType && String(mimeType).startsWith("image/") ? String(mimeType) : blob.type && blob.type.startsWith("image/") ? blob.type : "image/png";
    let outBlob = blob;
    let outType = type2;
    if (type2 !== "image/png" && typeof createImageBitmap === "function") {
      try {
        const bmp = await createImageBitmap(blob);
        const canvas = document.createElement("canvas");
        canvas.width = bmp.width;
        canvas.height = bmp.height;
        const ctx = canvas.getContext("2d");
        if (ctx) {
          ctx.drawImage(bmp, 0, 0);
          const png = await new Promise((resolve) => canvas.toBlob(resolve, "image/png"));
          if (png && png.size > 0) {
            outBlob = png;
            outType = "image/png";
          }
        }
        try {
          bmp.close();
        } catch {
        }
      } catch {
      }
    }
    try {
      await navigator.clipboard.write([new ClipboardItem({ [outType]: Promise.resolve(outBlob) })]);
      return true;
    } catch {
      try {
        await navigator.clipboard.write([new ClipboardItem({ [outType]: outBlob })]);
        return true;
      } catch {
        return false;
      }
    }
  }
  function toggleSelected(id) {
    if (_selected.has(id)) _selected.delete(id);
    else _selected.add(id);
    _anchorId = id;
    syncCardSelection();
    updateSelectionButtons();
  }
  function selectRange(fromId, toId) {
    const ids = _items2.map((it) => it.id);
    const a = ids.indexOf(fromId);
    const b = ids.indexOf(toId);
    if (a < 0 || b < 0) {
      toggleSelected(toId);
      return;
    }
    const lo = Math.min(a, b);
    const hi = Math.max(a, b);
    for (let i = lo; i <= hi; i++) _selected.add(ids[i]);
    syncCardSelection();
    updateSelectionButtons();
  }
  function syncCardSelection() {
    const root = getOverlayRoot2();
    if (!root) return;
    const cards = root.querySelectorAll(".kpv2-media-lib-card");
    cards.forEach((el2) => {
      const node = (
        /** @type {HTMLElement} */
        el2
      );
      const id = node.dataset.id;
      const on = !!(id && _selected.has(id));
      node.classList.toggle("is-selected", on);
      node.querySelectorAll(".kpv2-media-lib-select, .kpv2-media-lib-meta-select").forEach((btn) => {
        btn.setAttribute("aria-pressed", on ? "true" : "false");
      });
    });
  }
  async function zipCurrentView() {
    if (!kindHasGallery(_kind)) {
      _notify2("Nothing to download", "info");
      return;
    }
    const result = await zipMediaLibrary({
      kind: _kind,
      domain: _domain || ""
    });
    if (result?.empty || !result?.blob) {
      _notify2(result?.error || "Nothing to download", "info");
      return;
    }
    if (!result.success) {
      _notify2(result.error || "Could not build zip", "error");
      return;
    }
    downloadBlob(result.blob, result.filename || (_kind === "url" ? "URLs.zip" : _kind === "video" ? "Videos.zip" : _kind === "document" ? "Documents.zip" : "Images.zip"));
    _notify2("Download started", "success");
  }
  async function zipSelection() {
    const ids = Array.from(_selected);
    if (!ids.length) {
      _notify2("Nothing to download", "info");
      return;
    }
    const result = await zipMediaLibrary({ ids, kind: _kind });
    if (result?.empty || !result?.blob) {
      _notify2(result?.error || "Nothing to download", "info");
      return;
    }
    if (!result.success) {
      _notify2(result.error || "Could not build zip", "error");
      return;
    }
    downloadBlob(result.blob, result.filename || "Media-selection.zip");
    _notify2("Download started", "success");
  }
  async function deleteSelection() {
    const ids = Array.from(_selected);
    if (!ids.length) return;
    const result = await deleteMediaLibraryItems(ids);
    if (!result?.success) {
      _notify2(result?.error || "Could not delete", "error");
      return;
    }
    _selected = /* @__PURE__ */ new Set();
    _notify2(ids.length === 1 ? "Removed from Media Library" : `Removed ${ids.length} items`, "success");
    await reload();
  }
  async function openFullView2(id) {
    const overlay = _overlay2;
    if (!overlay) return;
    const fullView = (
      /** @type {HTMLElement} */
      /** @type {any} */
      overlay._fullView
    );
    const host = (
      /** @type {HTMLElement} */
      /** @type {any} */
      overlay._fullMediaHost
    );
    const counter = (
      /** @type {HTMLElement} */
      /** @type {any} */
      overlay._fullCounter
    );
    if (!fullView || !host) return;
    const listed = _items2.find((it) => it.id === id) || null;
    const isVideo = listed?.kind === "video" || _kind === "video";
    const isDoc = listed?.kind === "document" || _kind === "document";
    revokeFullObjectUrl();
    host.replaceChildren();
    _fullViewId = id;
    if (isVideo) {
      const ok = await mountFullVideo(host, id, listed);
      if (!ok) {
        _fullViewId = null;
        return;
      }
    } else if (isDoc) {
      const ok = await mountFullDocument(host, id, listed);
      if (!ok) {
        _fullViewId = null;
        return;
      }
    } else {
      const result = await getMediaLibraryOriginal(id);
      if (!result?.success || !result.blob) {
        _notify2(result?.error || "Could not open image", "error");
        _fullViewId = null;
        return;
      }
      _fullObjectUrl = URL.createObjectURL(result.blob);
      const img = document.createElement("img");
      img.className = "kpv2-media-lib-fullimage";
      img.alt = result.item?.domain || listed?.domain || "";
      img.src = _fullObjectUrl;
      host.appendChild(img);
    }
    const idx = _items2.findIndex((it) => it.id === id);
    if (counter) {
      counter.textContent = idx >= 0 ? `${idx + 1} / ${_items2.length}` : "";
    }
    fullView.classList.add("is-open");
    fullView.setAttribute("aria-hidden", "false");
  }
  async function mountFullVideo(host, id, listed) {
    const item = listed || { id, kind: "video" };
    let src = "";
    if (isVideoShortcut(item)) {
      const href2 = String(item.sourceUrl || "").trim();
      if (!isHttpUrl(href2)) {
        _notify2("This video link is not playable", "error");
        return false;
      }
      src = href2;
    } else {
      const result = await getMediaLibraryOriginal(id);
      const blob = result?.blob;
      const meta = result?.item || item;
      if (isVideoShortcut(meta)) {
        const href2 = String(meta.sourceUrl || item.sourceUrl || "").trim();
        if (!isHttpUrl(href2)) {
          _notify2("This video link is not playable", "error");
          return false;
        }
        src = href2;
      } else if (result?.success && blob instanceof Blob && blob.size > 0 && !/^text\/uri-list/i.test(blob.type || meta?.mime || "")) {
        _fullObjectUrl = URL.createObjectURL(blob);
        src = _fullObjectUrl;
      } else {
        const href2 = String(meta?.sourceUrl || item.sourceUrl || "").trim();
        if (isHttpUrl(href2)) {
          src = href2;
        } else {
          _notify2(result?.error || "Could not open video", "error");
          return false;
        }
      }
    }
    const video = document.createElement("video");
    video.className = "kpv2-media-lib-fullvideo";
    video.controls = true;
    video.autoplay = true;
    video.playsInline = true;
    video.src = src;
    host.appendChild(video);
    const href = String((listed || item).sourceUrl || "").trim();
    if (isHttpUrl(href)) {
      const openLink = document.createElement("a");
      openLink.className = "kpv2-media-lib-openlink";
      openLink.href = href;
      openLink.target = "_blank";
      openLink.rel = "noopener noreferrer";
      openLink.textContent = "Open in new tab";
      openLink.addEventListener("click", (e) => e.stopPropagation(), true);
      host.appendChild(openLink);
    }
    return true;
  }
  async function mountFullDocument(host, id, listed) {
    const result = await getMediaLibraryOriginal(id);
    const blob = result?.blob;
    const meta = result?.item || listed;
    if (!result?.success || !(blob instanceof Blob) || blob.size <= 0) {
      _notify2(result?.error || "Could not open document", "error");
      return false;
    }
    const mime = String(meta?.mime || blob.type || "").toLowerCase().split(";")[0].trim();
    const previewable = mime === "application/pdf" || mime.startsWith("text/");
    if (!previewable) {
      downloadBlob(blob, libraryDownloadFilename(meta || listed || { id, kind: "document" }));
      _notify2("Download started", "success");
      return false;
    }
    _fullObjectUrl = URL.createObjectURL(blob);
    const frame = document.createElement("iframe");
    frame.className = "kpv2-media-lib-fullframe";
    frame.title = meta?.ext || "Document";
    frame.src = _fullObjectUrl;
    host.appendChild(frame);
    return true;
  }
  function closeFullView2() {
    const overlay = _overlay2;
    if (!overlay) return;
    const fullView = (
      /** @type {HTMLElement} */
      /** @type {any} */
      overlay._fullView
    );
    const host = (
      /** @type {HTMLElement} */
      /** @type {any} */
      overlay._fullMediaHost
    );
    if (host) {
      try {
        const v = host.querySelector("video");
        if (v) v.pause();
      } catch {
      }
      host.replaceChildren();
    }
    if (fullView) {
      fullView.classList.remove("is-open");
      fullView.setAttribute("aria-hidden", "true");
    }
    revokeFullObjectUrl();
    _fullViewId = null;
  }
  async function navigateFullView2(delta) {
    if (!_fullViewId || !_items2.length) return;
    const idx = _items2.findIndex((it) => it.id === _fullViewId);
    if (idx < 0) return;
    const next = (idx + delta + _items2.length) % _items2.length;
    await openFullView2(_items2[next].id);
  }
  function ensureStyles4(root) {
    if (!root) return;
    const c = NCT_DARK_UI_COLORS;
    const css = `
.kpv2-media-lib-shell {
  position: absolute;
  inset: 10pt;
  z-index: 1;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: ${c.bg};
  border: ${NCT_DARK_UI_PANEL_BORDER};
  border-radius: ${NCT_DARK_UI_PANEL_RADIUS};
  box-shadow: ${NCT_DARK_UI_PANEL_BOX_SHADOW};
  font-family: ${NCT_DARK_UI_FONT};
  color: ${c.fg};
}
${getNctDarkUiBackdropCss()}
${getNctDarkUiScrollbarCss()}
.kpv2-media-lib-header {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 10px 14px;
  background: ${NCT_DARK_UI_TITLEBAR_GRADIENT};
  border-bottom: ${NCT_DARK_UI_TITLEBAR_BORDER_BOTTOM};
  flex-shrink: 0;
}
.kpv2-media-lib-title-wrap {
  display: flex;
  flex-direction: column;
  min-width: 140px;
  flex-shrink: 0;
}
.kpv2-media-lib-title {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  color: ${c.fg};
  line-height: 1.2;
}
.kpv2-media-lib-subtitle {
  font-size: 11px;
  color: ${c.fgMute};
  line-height: 1.2;
}
.kpv2-media-lib-toolbar {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1;
  justify-content: flex-end;
  min-width: 0;
}
.kpv2-media-lib-btn {
  padding: 5px 12px;
  border: ${NCT_DARK_UI_BTN_BORDER};
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  background: ${NCT_DARK_UI_BTN_GRADIENT};
  color: ${c.fg};
  font: inherit;
  font-size: 12px;
  cursor: pointer;
  white-space: nowrap;
}
.kpv2-media-lib-btn:hover:not(:disabled) {
  color: #fff;
}
.kpv2-media-lib-btn:disabled {
  opacity: 0.35;
  cursor: default;
}
.kpv2-media-lib-close {
  flex-shrink: 0;
}
.kpv2-media-lib-body {
  flex: 1;
  min-height: 0;
  display: flex;
}
.kpv2-media-lib-sidebar {
  width: 220px;
  flex-shrink: 0;
  background: #0f0f0f;
  border-right: 1px solid #333;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  padding: 8px 0;
}
.kpv2-media-lib-tab {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  margin: 2px 8px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 3px;
  color: ${c.fgDim};
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  text-align: left;
  width: calc(100% - 16px);
  box-sizing: border-box;
}
.kpv2-media-lib-tab:hover:not(:disabled) {
  background: #1f1f1f;
  border-color: #333;
}
.kpv2-media-lib-tab.is-active {
  background: #2a2a2a;
  border-color: #444;
  color: #fff;
}
.kpv2-media-lib-tab.is-disabled {
  opacity: 0.45;
  cursor: default;
}
.kpv2-media-lib-tab-domain {
  padding: 7px 16px 7px 32px;
  font-size: 12px;
  color: ${c.fgMute};
}
.kpv2-media-lib-tab-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.kpv2-media-lib-chev {
  width: 0.9em;
  flex-shrink: 0;
  color: ${c.fgMute};
  font-size: 10px;
}
.kpv2-media-lib-tab-count {
  margin-left: auto;
  flex-shrink: 0;
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: #888;
}
.kpv2-media-lib-content {
  flex: 1;
  min-width: 0;
  overflow: auto;
  padding: 16px 18px 24px;
}
.kpv2-media-lib-empty {
  padding: 64px 24px;
  text-align: center;
  color: ${c.fgMute};
  font-size: 13px;
  line-height: 1.5;
}
.kpv2-media-lib-section {
  margin-bottom: 22px;
}
.kpv2-media-lib-section-title {
  margin: 0 0 10px;
  font-size: 12px;
  font-weight: 600;
  color: ${c.fgDim};
  display: flex;
  align-items: center;
  gap: 8px;
}
.kpv2-media-lib-section-count {
  font-size: 11px;
  font-weight: 600;
  color: ${c.fgMute};
  font-variant-numeric: tabular-nums;
}
.kpv2-media-lib-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 12px;
  align-content: flex-start;
}
.kpv2-media-lib-card {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  padding: 0;
  margin: 0;
  border: 1px solid ${c.panelEdgeDark};
  border-radius: 3px;
  background: ${c.panel};
  box-shadow: 0 0 0 1px ${c.panelEdge} inset;
  overflow: hidden;
  text-align: left;
  color: inherit;
  font: inherit;
}
.kpv2-media-lib-card:hover:not(.is-selected) {
  border-color: ${c.accent};
}
.kpv2-media-lib-card.is-selected {
  border-color: #f0f0f0;
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.9) inset,
    0 0 14px 3px rgba(255, 255, 255, 0.45);
}
.kpv2-media-lib-card:hover .kpv2-media-lib-actions,
.kpv2-media-lib-card:focus-within .kpv2-media-lib-actions {
  opacity: 1;
  pointer-events: auto;
}
.kpv2-media-lib-thumb-wrap {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  background-color: #1a1a1a;
  background-image:
    linear-gradient(45deg, #2a2a2a 25%, transparent 25%),
    linear-gradient(-45deg, #2a2a2a 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #2a2a2a 75%),
    linear-gradient(-45deg, transparent 75%, #2a2a2a 75%);
  background-size: 16px 16px;
  background-position: 0 0, 0 8px, 8px -8px, -8px 0;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
.kpv2-media-lib-thumb {
  max-width: 100%;
  max-height: 100%;
  object-fit: cover;
  width: 100%;
  height: 100%;
}
.kpv2-media-lib-placeholder {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: ${c.fgMute};
}
.kpv2-media-lib-thumb-wrap.is-url,
.kpv2-media-lib-thumb-wrap.is-video,
.kpv2-media-lib-thumb-wrap.is-document {
  background: #161616;
}
.kpv2-media-lib-select {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  width: 100%;
  height: 28px;
  display: flex;
  align-items: center;
  padding: 0 8px;
  margin: 0;
  border: 0;
  box-sizing: border-box;
  background: rgba(0, 0, 0, 0.55);
  cursor: pointer;
  z-index: 2;
  appearance: none;
}
.kpv2-media-lib-select:hover {
  background: rgba(0, 0, 0, 0.72);
}
.kpv2-media-lib-open {
  position: absolute;
  top: 28px;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  padding: 0;
  margin: 0;
  border: 0;
  background: transparent;
  cursor: zoom-in;
  z-index: 1;
  appearance: none;
}
.kpv2-media-lib-meta-select {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  width: 100%;
  padding: 0;
  margin: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  text-align: left;
  color: inherit;
  font: inherit;
  appearance: none;
}
.kpv2-media-lib-check {
  position: relative;
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  border-radius: 2px;
  border: 1px solid rgba(255, 255, 255, 0.55);
  background: rgba(0, 0, 0, 0.35);
  pointer-events: none;
}
.kpv2-media-lib-card.is-selected .kpv2-media-lib-check {
  background: #f2f2f2;
  border-color: #fff;
}
.kpv2-media-lib-card.is-selected .kpv2-media-lib-check::after {
  content: '';
  position: absolute;
  left: 4px;
  top: 1px;
  width: 5px;
  height: 9px;
  border: solid #222;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.kpv2-media-lib-actions {
  position: absolute;
  left: 6px;
  right: 6px;
  bottom: 6px;
  display: flex;
  gap: 4px;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s ease;
  z-index: 3;
}
.kpv2-media-lib-action {
  flex: 1;
  min-width: 0;
  padding: 5px 4px;
  border: ${NCT_DARK_UI_BTN_BORDER};
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  background: ${NCT_DARK_UI_BTN_GRADIENT};
  color: ${c.fg};
  font: inherit;
  font-size: 10px;
  font-weight: 600;
  line-height: 1.2;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0,0,0,0.45);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.kpv2-media-lib-action:hover {
  background: ${NCT_DARK_UI_BTN_LIT_GRADIENT};
  border: ${NCT_DARK_UI_BTN_LIT_BORDER};
  color: #e8f0f8;
}
.kpv2-media-lib-meta-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 5px 8px;
  background: ${c.fieldBg};
  border-bottom: 1px solid ${c.panelEdgeDark};
  min-height: 22px;
  box-sizing: border-box;
}
.kpv2-media-lib-badge {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: ${c.fgDim};
  white-space: nowrap;
}
.kpv2-media-lib-info {
  padding: 8px 10px;
}
.kpv2-media-lib-name {
  font-size: 11px;
  color: ${c.fg};
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.kpv2-media-lib-page {
  margin-top: 2px;
  font-size: 10px;
  color: ${c.fgMute};
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.kpv2-media-lib-size-row {
  margin-top: 4px;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
.kpv2-media-lib-size {
  font-size: 10px;
  color: ${c.fgMute};
}
.kpv2-media-lib-dpi {
  font-size: 10px;
  font-weight: 600;
  color: ${c.fgDim};
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}
.kpv2-media-lib-fullview {
  display: none;
  position: absolute;
  inset: 0;
  z-index: 1;
  background: rgba(0, 0, 0, 0.92);
  align-items: center;
  justify-content: center;
  cursor: zoom-out;
}
.kpv2-media-lib-fullview.is-open {
  display: flex;
}
.kpv2-media-lib-fullmedia {
  max-width: 90vw;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  cursor: default;
}
.kpv2-media-lib-fullimage {
  max-width: 90vw;
  max-height: 85vh;
  object-fit: contain;
  border-radius: 3px;
}
.kpv2-media-lib-fullvideo {
  max-width: 90vw;
  max-height: 80vh;
  background: #000;
}
.kpv2-media-lib-fullframe {
  width: 90vw;
  height: 85vh;
  border: 0;
  background: #111;
  border-radius: 3px;
}
.kpv2-media-lib-openlink {
  color: ${c.accent};
  font-size: 12px;
}
.kpv2-media-lib-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 40px;
  height: 40px;
  border: ${NCT_DARK_UI_BTN_BORDER};
  border-radius: 50%;
  background: rgba(0,0,0,0.55);
  color: #fff;
  font-size: 22px;
  cursor: pointer;
}
.kpv2-media-lib-nav-prev { left: 20px; }
.kpv2-media-lib-nav-next { right: 20px; }
.kpv2-media-lib-fullcounter {
  position: absolute;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  padding: 6px 14px;
  border-radius: 2px;
  background: rgba(0,0,0,0.7);
  font-size: 12px;
  color: ${c.fg};
}
`;
    injectChromeStyles(root, {
      attr: "data-kp-media-lib-styles",
      css
    });
  }

  // src/utils/scroll-hold.js
  init_constants();
  function holdCfg() {
    const speed = Number(SCROLL.HOLD_PX_PER_SEC);
    const delay = Number(SCROLL.HOLD_RAF_START_MS);
    return {
      speedPxPerSec: Number.isFinite(speed) && speed > 0 ? speed : 1400,
      startDelayMs: Number.isFinite(delay) && delay >= 0 ? delay : 120
    };
  }
  var ScrollHoldController = class {
    /**
     * @param {{ apply: (ctx: ScrollHoldApplyContext) => void, speedPxPerSec?: number, startDelayMs?: number }} opts
     */
    constructor(opts) {
      this._apply = typeof opts?.apply === "function" ? opts.apply : () => {
      };
      this._speedOverride = Number(opts?.speedPxPerSec);
      this._delayOverride = Number(opts?.startDelayMs);
      this.key = null;
      this.sign = 0;
      this.target = null;
      this._raf = 0;
      this._lastTs = 0;
      this._armTimer = 0;
    }
    /** @returns {boolean} */
    get active() {
      return !!this.key;
    }
    /**
     * @param {string|null|undefined} key
     * @param {number} [sign]
     * @returns {boolean}
     */
    isHolding(key, sign) {
      if (!this.key) return false;
      if (key != null && this.key !== key) return false;
      if (sign != null && this.sign !== (sign < 0 ? -1 : 1)) return false;
      return true;
    }
    /**
     * Start (or redirect) a hold. Does not apply the tap step — caller does that.
     * Continuous rAF begins after a short delay so a quick tap stays a single step.
     *
     * @param {{ key?: string|null, sign: number, target?: any, speedPxPerSec?: number }} args
     */
    begin(args) {
      const sign = args.sign < 0 ? -1 : 1;
      const key = args.key == null ? null : String(args.key);
      const same = this.key != null && this.key === key && this.sign === sign;
      this.key = key;
      this.sign = sign;
      this.target = args.target ?? this.target;
      if (Number.isFinite(Number(args.speedPxPerSec)) && Number(args.speedPxPerSec) > 0) {
        this._speedOverride = Number(args.speedPxPerSec);
      }
      if (same && (this._raf || this._armTimer)) return;
      this._clearArmTimer();
      if (this._raf) return;
      const cfg = holdCfg();
      const delay = Number.isFinite(this._delayOverride) && this._delayOverride >= 0 ? this._delayOverride : cfg.startDelayMs;
      if (delay <= 0) {
        this._startLoop();
        return;
      }
      this._armTimer = setTimeout(() => {
        this._armTimer = 0;
        if (!this.key) return;
        this._startLoop();
      }, delay);
    }
    /**
     * OS key-repeat: keep the hold alive; do not scroll here (rAF owns motion).
     * @param {string|null|undefined} key
     * @param {number} [sign]
     * @returns {boolean} true if this repeat belongs to the active hold
     */
    noteRepeat(key, sign) {
      if (!this.isHolding(key, sign)) return false;
      if (!this._raf && !this._armTimer) {
        this._startLoop();
      } else if (!this._raf && this._armTimer) {
        this._clearArmTimer();
        this._startLoop();
      }
      return true;
    }
    /**
     * @param {string|null|undefined} [key]  if set, only stop when it matches
     */
    end(key) {
      if (key != null && this.key != null && this.key !== key) return;
      this.reset();
    }
    reset() {
      this._clearArmTimer();
      if (this._raf) {
        try {
          cancelAnimationFrame(this._raf);
        } catch {
        }
        this._raf = 0;
      }
      this._lastTs = 0;
      this.key = null;
      this.sign = 0;
      this.target = null;
    }
    _clearArmTimer() {
      if (this._armTimer) {
        try {
          clearTimeout(this._armTimer);
        } catch {
        }
        this._armTimer = 0;
      }
    }
    _speed() {
      if (Number.isFinite(this._speedOverride) && this._speedOverride > 0) {
        return this._speedOverride;
      }
      return holdCfg().speedPxPerSec;
    }
    _startLoop() {
      if (this._raf) return;
      this._lastTs = 0;
      const tick = (ts) => {
        this._raf = 0;
        if (!this.key) return;
        const last = this._lastTs;
        this._lastTs = ts;
        this._raf = requestAnimationFrame(tick);
        if (!last) return;
        const dt = Math.min(0.05, Math.max(0, (ts - last) / 1e3));
        if (!dt) return;
        const deltaPx = this.sign * this._speed() * dt;
        if (!deltaPx) return;
        try {
          this._apply({
            deltaPx,
            sign: this.sign,
            dtSec: dt,
            target: this.target
          });
        } catch {
        }
      };
      this._raf = requestAnimationFrame(tick);
    }
  };

  // src/utils/map-surface-drag.js
  var MAP_SURFACE_SEL = [
    ".leaflet-container",
    ".leaflet-grab",
    ".maplibregl-map",
    ".maplibregl-canvas",
    ".mapboxgl-map",
    ".mapboxgl-canvas",
    ".gm-style",
    "canvas.maplibregl-canvas",
    "canvas.mapboxgl-canvas",
    'canvas[aria-label="Map" i]',
    'canvas[aria-label*="Map" i]'
  ].join(",");
  var PAN_EVENT = "__kp_map_pan_v1";
  var SESSION_START = "__kp_map_session_start_v1";
  var SESSION_END = "__kp_map_session_end_v1";
  var MAP_PAN_POINTER_ID = 10088;
  var SCROLL_LINE_MAP_DRAG_ENABLED = false;
  var _bridgePromise = null;
  function hostLooksLikeMapsSite(hostname = "", pathname = "") {
    const h = String(hostname || "").toLowerCase();
    const p = String(pathname || "").toLowerCase();
    if (h.includes("openstreetmap.org")) return true;
    if (h.includes("openstreetmap.de")) return true;
    if (h.includes("bing.com") && p.includes("/maps")) return true;
    if (h === "maps.google.com" || h.startsWith("maps.google.")) return true;
    if (h.includes("google.") && (p === "/maps" || p.startsWith("/maps/") || p.startsWith("/maps?"))) {
      return true;
    }
    if (h.includes("brave.com") && p.includes("/maps")) return true;
    if (h.includes("mapbox.com")) return true;
    return false;
  }
  function isOnMapWebsite(hostname, pathname) {
    let h = hostname;
    let p = pathname;
    if (h == null || p == null) {
      try {
        h = h ?? location.hostname;
        p = p ?? location.pathname;
      } catch {
        return false;
      }
    }
    return hostLooksLikeMapsSite(h, p);
  }
  function elementLooksLikeMapSurface(el2) {
    if (!el2 || el2.nodeType !== 1) return false;
    let cls = "";
    try {
      cls = String(el2.className || "");
    } catch {
      cls = "";
    }
    const tag = String(el2.tagName || "").toUpperCase();
    if (/\bleaflet-container\b|\bleaflet-grab\b/.test(cls)) return true;
    if (/\bmaplibregl-(?:map|canvas)\b/.test(cls)) return true;
    if (/\bmapboxgl-(?:map|canvas)\b/.test(cls)) return true;
    if (/\bgm-style\b/.test(cls)) return true;
    if (tag === "CANVAS") {
      let pcls = "";
      try {
        pcls = String(el2.parentElement?.className || "");
      } catch {
        pcls = "";
      }
      if (/maplibregl|mapboxgl|gm-style|leaflet|widget-scene/.test(`${cls} ${pcls}`)) return true;
      let label = "";
      try {
        label = String(el2.getAttribute("aria-label") || "").toLowerCase();
      } catch {
        label = "";
      }
      if (label === "map" || /\bmap\b/.test(label)) return true;
    }
    let role = "";
    let id = "";
    try {
      role = String(el2.getAttribute("role") || "").toLowerCase();
    } catch {
      role = "";
    }
    try {
      id = String(el2.id || "");
    } catch {
      id = "";
    }
    if (role === "application" && /map/i.test(`${cls} ${id}`)) return true;
    return false;
  }
  function isMapUiChrome(el2) {
    if (!el2 || el2.nodeType !== 1) return false;
    try {
      if (typeof el2.closest === "function") {
        const chrome2 = el2.closest(
          'button, a[href], input, select, textarea, [role="button"], [role="search"], [role="dialog"]'
        );
        if (chrome2) return true;
      }
    } catch {
    }
    return false;
  }
  function findMapSurfaceAtPoint(clientX, clientY) {
    const x = Number(clientX);
    const y = Number(clientY);
    if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
    let start = null;
    try {
      start = document.elementFromPoint(x, y);
    } catch {
      start = null;
    }
    if (start && start.nodeType !== 1) {
      try {
        start = start.parentElement;
      } catch {
        start = null;
      }
    }
    let n = start;
    let depth = 0;
    while (n && n.nodeType === 1 && depth++ < 18) {
      if (elementLooksLikeMapSurface(n) && !isMapUiChrome(n)) return n;
      if (elementLooksLikeMapSurface(n)) {
        return n;
      }
      try {
        n = n.parentElement;
      } catch {
        n = null;
      }
    }
    if (!hostLooksLikeMapsSite(location.hostname, location.pathname)) return null;
    try {
      const stack = typeof document.elementsFromPoint === "function" ? document.elementsFromPoint(x, y) : [];
      for (let i = 0; i < stack.length; i++) {
        const el2 = stack[i];
        if (!el2 || el2.nodeType !== 1) continue;
        if (String(el2.tagName || "").toUpperCase() !== "CANVAS") continue;
        let pe = "";
        try {
          pe = String(window.getComputedStyle(el2).pointerEvents || "");
        } catch {
          pe = "";
        }
        if (pe === "none") continue;
        let r = null;
        try {
          r = el2.getBoundingClientRect();
        } catch {
          r = null;
        }
        if (r && r.width > 200 && r.height > 200) return el2;
      }
    } catch {
    }
    let nodes = [];
    try {
      nodes = Array.from(document.querySelectorAll(MAP_SURFACE_SEL));
    } catch {
      nodes = [];
    }
    if (!nodes.length) {
      try {
        const named = document.getElementById("map");
        if (named) nodes = [named];
      } catch {
      }
    }
    for (let i = 0; i < nodes.length; i++) {
      const el2 = nodes[i];
      if (!el2 || el2.nodeType !== 1) continue;
      let r = null;
      try {
        r = el2.getBoundingClientRect();
      } catch {
        r = null;
      }
      if (!r || r.width < 120 || r.height < 120) continue;
      if (x < r.left || x > r.right || y < r.top || y > r.bottom) continue;
      return el2;
    }
    return null;
  }
  function ensureMapPanBridge() {
    if (!SCROLL_LINE_MAP_DRAG_ENABLED) return Promise.resolve(false);
    if (_bridgePromise) return _bridgePromise;
    _bridgePromise = new Promise((resolve) => {
      try {
        if (typeof chrome === "undefined" || !chrome.runtime?.sendMessage) {
          resolve(false);
          return;
        }
        chrome.runtime.sendMessage(
          { type: MSG.ENSURE_MAP_PAN_BRIDGE },
          (response) => {
            const err = chrome.runtime.lastError;
            if (err) {
              _bridgePromise = null;
              resolve(false);
              return;
            }
            resolve(!!(response && response.ok));
          }
        );
      } catch {
        _bridgePromise = null;
        resolve(false);
      }
    });
    return _bridgePromise;
  }
  function dispatchBridgeEvent(type2, detail = {}) {
    try {
      document.documentElement.dispatchEvent(new CustomEvent(type2, {
        bubbles: true,
        detail
      }));
    } catch {
    }
  }
  function createMapPanSession(el2, originX, originY) {
    if (!SCROLL_LINE_MAP_DRAG_ENABLED) return null;
    ensureMapPanBridge();
    const x = Number(originX) || 0;
    const y = Number(originY) || 0;
    dispatchBridgeEvent(SESSION_START, { clientX: x, clientY: y });
    return { el: el2, x, y };
  }
  function endMapPanSession() {
    if (!SCROLL_LINE_MAP_DRAG_ENABLED) return;
    dispatchBridgeEvent(SESSION_END);
  }
  function mapPanBy(session, dx, dy) {
    if (!SCROLL_LINE_MAP_DRAG_ENABLED) return;
    if (!session) return;
    const ax = Number(dx) || 0;
    const ay = Number(dy) || 0;
    if (!ax && !ay) return;
    ensureMapPanBridge();
    dispatchBridgeEvent(PAN_EVENT, {
      dx: ax,
      dy: ay,
      clientX: session.x,
      clientY: session.y
    });
  }

  // src/utils/map-poi.js
  var MAPS_HOST_RE = /(?:^|\.)(?:google|gstatic|googleapis|googleusercontent|ggpht|gvt1|youtube|blogger|bing|microsoft|live|msn|brave|search\.brave|openstreetmap|osm)\./i;
  function unwrapMapsRedirect(href) {
    const raw = String(href || "").trim();
    if (!raw) return "";
    try {
      const u = new URL(raw, location.href);
      if (/google\./i.test(u.hostname) && (u.pathname === "/url" || u.pathname === "/url/")) {
        const q = u.searchParams.get("q") || u.searchParams.get("url");
        if (q) return unwrapMapsRedirect(q);
      }
      if (/bing\./i.test(u.hostname) && /\/ck\/a/i.test(u.pathname)) {
        const u2 = u.searchParams.get("u") || u.searchParams.get("url");
        if (u2) return unwrapMapsRedirect(u2);
      }
      return u.href;
    } catch {
      return raw;
    }
  }
  function isExternalPoiWebsite(href) {
    const unwrapped = unwrapMapsRedirect(href);
    if (!unwrapped) return false;
    let u = null;
    try {
      u = new URL(unwrapped, location.href);
    } catch {
      return false;
    }
    if (u.protocol !== "http:" && u.protocol !== "https:") return false;
    const host = String(u.hostname || "").toLowerCase();
    if (!host) return false;
    if (MAPS_HOST_RE.test(host + ".")) {
      return false;
    }
    try {
      if (host === String(location.hostname || "").toLowerCase()) return false;
    } catch {
    }
    return true;
  }
  function findPoiWebsiteInRoot(root) {
    if (!root) return null;
    const scope = (
      /** @type {any} */
      root
    );
    const prefer = [];
    try {
      prefer.push(
        ...scope.querySelectorAll(
          'a[data-item-id="authority"], a[aria-label^="Website" i], a[aria-label="Open website" i], a[data-tooltip="Open website" i]'
        )
      );
    } catch {
    }
    for (let i = 0; i < prefer.length; i++) {
      const a = prefer[i];
      const href = unwrapMapsRedirect(a && a.href);
      if (isExternalPoiWebsite(href)) return href;
    }
    let links = [];
    try {
      links = Array.from(scope.querySelectorAll("a[href]"));
    } catch {
      links = [];
    }
    for (let i = 0; i < links.length; i++) {
      const a = links[i];
      const href = unwrapMapsRedirect(a && a.href);
      if (!isExternalPoiWebsite(href)) continue;
      let aria = "";
      let tip = "";
      let text = "";
      try {
        aria = String(a.getAttribute("aria-label") || "");
      } catch {
      }
      try {
        tip = String(a.getAttribute("data-tooltip") || a.getAttribute("title") || "");
      } catch {
      }
      try {
        text = String(a.textContent || "").replace(/\s+/g, " ").trim();
      } catch {
      }
      const blob = `${aria} ${tip} ${text}`.toLowerCase();
      if (/\bwebsite\b|\bofficial site\b|\bopen website\b/.test(blob)) return href;
      if (/^[a-z0-9][a-z0-9.-]+\.[a-z]{2,}(?:\/.*)?$/i.test(text)) return href;
    }
    return null;
  }
  function findPoiWebsiteInDocument() {
    const roots = [];
    try {
      const main = document.querySelector('[role="main"]');
      if (main) roots.push(main);
    } catch {
    }
    try {
      const popup = document.querySelector(
        ".leaflet-popup-content, .leaflet-popup, .maplibregl-popup-content, .mapboxgl-popup-content"
      );
      if (popup) roots.push(popup);
    } catch {
    }
    try {
      const side = document.querySelector("#sidebar_content, #sidebar, .sidebar");
      if (side) roots.push(side);
    } catch {
    }
    for (let i = 0; i < roots.length; i++) {
      const url = findPoiWebsiteInRoot(roots[i]);
      if (url) return url;
    }
    try {
      return findPoiWebsiteInRoot(document);
    } catch {
      return null;
    }
  }
  function findPoiWebsiteAtPoint(clientX, clientY) {
    const x = Number(clientX);
    const y = Number(clientY);
    if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
    let el2 = null;
    try {
      el2 = document.elementFromPoint(x, y);
    } catch {
      el2 = null;
    }
    if (el2 && el2.nodeType !== 1) {
      try {
        el2 = el2.parentElement;
      } catch {
        el2 = null;
      }
    }
    if (!el2) return null;
    let n = el2;
    let depth = 0;
    while (n && n.nodeType === 1 && depth++ < 16) {
      let role = "";
      try {
        role = String(n.getAttribute("role") || "").toLowerCase();
      } catch {
        role = "";
      }
      let cls = "";
      try {
        cls = String(n.className || "");
      } catch {
        cls = "";
      }
      const tag = String(n.tagName || "").toUpperCase();
      const isCard = role === "main" || role === "article" || role === "dialog" || /leaflet-popup|maplibregl-popup|mapboxgl-popup|section-layout|widget-pane/.test(cls) || tag === "A" && /\/maps\/place\//i.test(String(n.href || ""));
      if (isCard) {
        const url = findPoiWebsiteInRoot(n);
        if (url) return url;
      }
      try {
        n = n.parentElement;
      } catch {
        n = null;
      }
    }
    return null;
  }
  function findMapsPlacePanelCloseButton(doc = document) {
    let nodes = [];
    try {
      nodes = Array.from(doc.querySelectorAll('button, [role="button"]'));
    } catch {
      return null;
    }
    for (let i = 0; i < nodes.length; i++) {
      const el2 = nodes[i];
      if (!el2 || el2.nodeType !== 1) continue;
      let jsaction = "";
      try {
        jsaction = String(el2.getAttribute("jsaction") || "");
      } catch {
        jsaction = "";
      }
      if (/omnibox\.clear/i.test(jsaction)) {
        let r = null;
        try {
          r = el2.getBoundingClientRect();
        } catch {
          r = null;
        }
        if (r && r.width > 4 && r.height > 4) return el2;
      }
    }
    for (let i = 0; i < nodes.length; i++) {
      const el2 = nodes[i];
      if (!el2 || el2.nodeType !== 1) continue;
      let aria = "";
      try {
        aria = String(el2.getAttribute("aria-label") || "").trim();
      } catch {
        aria = "";
      }
      if (!/^close$/i.test(aria)) continue;
      let inSearch = false;
      try {
        inSearch = !!(el2.closest && el2.closest('form, [role="search"]'));
      } catch {
        inSearch = false;
      }
      if (!inSearch) continue;
      let r = null;
      try {
        r = el2.getBoundingClientRect();
      } catch {
        r = null;
      }
      if (r && r.width > 4 && r.height > 4) return el2;
    }
    return null;
  }
  function waitForPoiWebsite(opts = {}) {
    const timeoutMs = Number(opts.timeoutMs) > 0 ? Number(opts.timeoutMs) : 2800;
    const intervalMs = Number(opts.intervalMs) > 0 ? Number(opts.intervalMs) : 120;
    const ignore = String(opts.ignoreUrl || "").trim();
    const start = Date.now();
    return new Promise((resolve) => {
      const tick = () => {
        const url = findPoiWebsiteInDocument() || findPoiWebsiteInRoot(document);
        if (url && url !== ignore) {
          resolve(url);
          return;
        }
        if (Date.now() - start >= timeoutMs) {
          resolve(url && url !== ignore ? url : null);
          return;
        }
        setTimeout(tick, intervalMs);
      };
      tick();
    });
  }
  function labeledValue(raw, prefix) {
    const s = String(raw || "").replace(/\s+/g, " ").trim();
    if (!s) return "";
    const re = new RegExp(`^${prefix}\\s*:\\s*(.+)$`, "i");
    const m = s.match(re);
    return m ? String(m[1] || "").trim() : "";
  }
  function findPoiPlaceDetails(root) {
    const out = { name: "", address: "", phone: "", website: "" };
    if (!root) return out;
    const scope = (
      /** @type {any} */
      root
    );
    try {
      const h1 = scope.querySelector?.("h1");
      if (h1) out.name = String(h1.textContent || "").replace(/\s+/g, " ").trim();
    } catch {
    }
    if (!out.name) {
      try {
        const main = scope.querySelector?.('[role="main"]') || (scope.getAttribute && String(scope.getAttribute("role") || "") === "main" ? scope : null);
        if (main && main.getAttribute) {
          out.name = String(main.getAttribute("aria-label") || "").trim();
        }
      } catch {
      }
    }
    let nodes = [];
    try {
      nodes = Array.from(scope.querySelectorAll("button, a, [aria-label], [data-item-id], [data-tooltip]"));
    } catch {
      nodes = [];
    }
    for (let i = 0; i < nodes.length; i++) {
      const el2 = nodes[i];
      if (!el2 || el2.nodeType !== 1) continue;
      let aria = "";
      let item = "";
      let tip = "";
      try {
        aria = String(el2.getAttribute("aria-label") || "");
      } catch {
        aria = "";
      }
      try {
        item = String(el2.getAttribute("data-item-id") || "");
      } catch {
        item = "";
      }
      try {
        tip = String(el2.getAttribute("data-tooltip") || "");
      } catch {
        tip = "";
      }
      if (!out.address) {
        const fromAria = labeledValue(aria, "Address");
        if (fromAria && !/^plus code/i.test(fromAria)) out.address = fromAria;
      }
      if (!out.address && (item === "address" || /copy address/i.test(`${aria} ${tip}`))) {
        const text = String(el2.textContent || "").replace(/\s+/g, " ").trim();
        const cleaned = labeledValue(text, "Address") || text;
        if (cleaned && cleaned.length > 6 && !/plus code/i.test(cleaned)) out.address = cleaned;
      }
      if (!out.phone) {
        const fromAria = labeledValue(aria, "Phone");
        if (fromAria) out.phone = fromAria;
      }
      if (!out.website) {
        const href = unwrapMapsRedirect(el2.href);
        if (isExternalPoiWebsite(href) && (item === "authority" || /^website/i.test(aria) || /open website/i.test(`${aria} ${tip}`))) {
          out.website = href;
        }
      }
    }
    if (!out.website) {
      try {
        out.website = findPoiWebsiteInRoot(scope) || "";
      } catch {
      }
    }
    return out;
  }
  function findPoiPlaceDetailsInDocument() {
    const roots = [];
    try {
      const main = document.querySelector('[role="main"]');
      if (main) roots.push(main);
    } catch {
    }
    try {
      const popup = document.querySelector(
        ".leaflet-popup-content, .leaflet-popup, .maplibregl-popup-content, .mapboxgl-popup-content"
      );
      if (popup) roots.push(popup);
    } catch {
    }
    try {
      const side = document.querySelector("#sidebar_content, #sidebar, .sidebar");
      if (side) roots.push(side);
    } catch {
    }
    for (let i = 0; i < roots.length; i++) {
      const info = findPoiPlaceDetails(roots[i]);
      if (info.address) return info;
    }
    try {
      return findPoiPlaceDetails(document);
    } catch {
      return { name: "", address: "", phone: "", website: "" };
    }
  }
  function findPoiPlaceDetailsAtPoint(clientX, clientY) {
    const x = Number(clientX);
    const y = Number(clientY);
    if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
    let el2 = null;
    try {
      el2 = document.elementFromPoint(x, y);
    } catch {
      el2 = null;
    }
    if (el2 && el2.nodeType !== 1) {
      try {
        el2 = el2.parentElement;
      } catch {
        el2 = null;
      }
    }
    if (!el2) return null;
    let n = el2;
    let depth = 0;
    while (n && n.nodeType === 1 && depth++ < 16) {
      let role = "";
      try {
        role = String(n.getAttribute("role") || "").toLowerCase();
      } catch {
        role = "";
      }
      let cls = "";
      try {
        cls = String(n.className || "");
      } catch {
        cls = "";
      }
      const tag = String(n.tagName || "").toUpperCase();
      const isCard = role === "main" || role === "article" || role === "dialog" || /leaflet-popup|maplibregl-popup|mapboxgl-popup|section-layout|widget-pane/.test(cls) || tag === "A" && /\/maps\/place\//i.test(String(n.href || ""));
      if (isCard) {
        const info = findPoiPlaceDetails(n);
        if (info.address) return info;
      }
      try {
        n = n.parentElement;
      } catch {
        n = null;
      }
    }
    return null;
  }
  function escapeVCard(value) {
    return String(value || "").replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/,/g, "\\,").replace(/;/g, "\\;");
  }
  function parsePostalAddress(address) {
    const raw = String(address || "").replace(/\s+/g, " ").trim();
    const empty = { street: raw, city: "", region: "", postal: "", country: "" };
    if (!raw) return { street: "", city: "", region: "", postal: "", country: "" };
    const parts = raw.split(",").map((p) => p.trim()).filter(Boolean);
    if (parts.length < 2) return empty;
    let country = "";
    let tail = parts[parts.length - 1];
    if (/united states|usa|u\.s\.a\.?/i.test(tail) && parts.length >= 3) {
      country = tail;
      parts.pop();
      tail = parts[parts.length - 1];
    }
    const stateZip = tail.match(/^([A-Z]{2})\s+(\d{5}(?:-\d{4})?)$/i);
    if (stateZip && parts.length >= 2) {
      const region = stateZip[1].toUpperCase();
      const postal = stateZip[2];
      const city = parts[parts.length - 2];
      const street = parts.slice(0, parts.length - 2).join(", ");
      return { street: street || "", city, region, postal, country };
    }
    if (parts.length >= 3) {
      return {
        street: parts.slice(0, parts.length - 2).join(", "),
        city: parts[parts.length - 2],
        region: "",
        postal: parts[parts.length - 1],
        country
      };
    }
    return { street: parts[0], city: parts[1] || "", region: "", postal: "", country };
  }
  function formatPoiAddressVCard(details) {
    const name = String(details?.name || "").trim();
    const address = String(details?.address || "").trim();
    const phone = String(details?.phone || "").trim();
    const website = String(details?.website || "").trim();
    const parsed = parsePostalAddress(address);
    const adr = [
      "",
      "",
      parsed.street,
      parsed.city,
      parsed.region,
      parsed.postal,
      parsed.country
    ].map(escapeVCard).join(";");
    const fn = name || address || "Place";
    const lines = [
      "BEGIN:VCARD",
      "VERSION:3.0",
      `FN:${escapeVCard(fn)}`
    ];
    if (name) lines.push(`ORG:${escapeVCard(name)}`);
    if (address) lines.push(`ADR;TYPE=WORK:${adr}`);
    if (phone) lines.push(`TEL;TYPE=WORK,VOICE:${escapeVCard(phone)}`);
    if (website) lines.push(`URL:${escapeVCard(website)}`);
    lines.push("END:VCARD");
    return lines.join("\r\n");
  }
  function mapsSearchUrlForAddress(address) {
    const q = String(address || "").trim();
    if (!q) return "";
    return `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(q)}`;
  }
  function waitForPoiAddress(opts = {}) {
    const timeoutMs = Number(opts.timeoutMs) > 0 ? Number(opts.timeoutMs) : 2800;
    const intervalMs = Number(opts.intervalMs) > 0 ? Number(opts.intervalMs) : 120;
    const ignore = String(opts.ignoreAddress || "").trim();
    const start = Date.now();
    return new Promise((resolve) => {
      const tick = () => {
        const info = findPoiPlaceDetailsInDocument();
        if (info.address && info.address !== ignore) {
          resolve(info);
          return;
        }
        if (Date.now() - start >= timeoutMs) {
          resolve(info.address && info.address !== ignore ? info : null);
          return;
        }
        setTimeout(tick, intervalMs);
      };
      tick();
    });
  }

  // src/modules/scroll-line-overlay.js
  init_constants();
  var STROKE = COLORS.ORANGE;
  var DOT_R = 6;
  var ScrollLineOverlay = class {
    constructor() {
      this.root = null;
      this.svg = null;
      this.line = null;
      this.dot = null;
      this.targetBox = null;
      this.origin = { x: 0, y: 0 };
      this._lastTargetBox = null;
      this._visible = false;
    }
    /**
     * @param {number} originX
     * @param {number} originY
     */
    show(originX, originY) {
      this.origin = { x: Number(originX) || 0, y: Number(originY) || 0 };
      this._ensure();
      if (!this.root) return;
      this._syncDot();
      this.updatePointer(this.origin.x, this.origin.y);
      try {
        if (!this.root.isConnected) {
          (document.documentElement || document.body).appendChild(this.root);
        }
      } catch {
      }
      this._visible = true;
      try {
        this.root.hidden = false;
        this.root.style.display = "block";
        this.root.style.visibility = "visible";
        this.root.style.opacity = "1";
      } catch {
      }
    }
    /**
     * @param {number} x
     * @param {number} y
     */
    updatePointer(x, y) {
      if (!this.line) return;
      const px = Number(x);
      const py = Number(y);
      if (!Number.isFinite(px) || !Number.isFinite(py)) return;
      try {
        this.line.setAttribute("x2", String(px));
        this.line.setAttribute("y2", String(py));
      } catch {
      }
    }
    /**
     * Viewport-fixed ring around a nested overflow container. Pass null to hide
     * (document / full-viewport scrollers).
     * @param {{ left: number, top: number, width: number, height: number, radius?: string }|null} box
     */
    setTargetBox(box) {
      this._ensure();
      if (!this.targetBox) return;
      if (!box || !(box.width > 4) || !(box.height > 4)) {
        this._lastTargetBox = null;
        try {
          this.targetBox.style.display = "none";
        } catch {
        }
        return;
      }
      const left = Number(box.left) || 0;
      const top = Number(box.top) || 0;
      const width = Number(box.width) || 0;
      const height = Number(box.height) || 0;
      const radius = String(box.radius || "0");
      const prev = this._lastTargetBox;
      if (prev && Math.abs(prev.left - left) < 0.5 && Math.abs(prev.top - top) < 0.5 && Math.abs(prev.width - width) < 0.5 && Math.abs(prev.height - height) < 0.5 && prev.radius === radius) {
        return;
      }
      this._lastTargetBox = { left, top, width, height, radius };
      const el2 = this.targetBox;
      try {
        el2.style.display = "block";
        el2.style.transform = `translate3d(${left}px, ${top}px, 0)`;
        el2.style.width = `${width}px`;
        el2.style.height = `${height}px`;
        el2.style.borderRadius = radius;
      } catch {
      }
    }
    hide() {
      if (!this.root) return;
      this._visible = false;
      this.setTargetBox(null);
      try {
        this.root.hidden = true;
        this.root.style.display = "none";
      } catch {
      }
    }
    destroy() {
      this.hide();
      try {
        this.root?.remove?.();
      } catch {
      }
      this.root = null;
      this.svg = null;
      this.line = null;
      this.dot = null;
      this.targetBox = null;
      this._lastTargetBox = null;
    }
    _syncDot() {
      if (!this.dot || !this.line) return;
      const x = String(this.origin.x);
      const y = String(this.origin.y);
      try {
        this.dot.setAttribute("cx", x);
        this.dot.setAttribute("cy", y);
        this.line.setAttribute("x1", x);
        this.line.setAttribute("y1", y);
      } catch {
      }
    }
    _ensure() {
      if (this.root) return;
      const doc = document;
      const root = doc.createElement("div");
      root.className = CSS_CLASSES.SCROLL_LINE_OVERLAY;
      root.id = CSS_CLASSES.SCROLL_LINE_OVERLAY;
      root.setAttribute("aria-hidden", "true");
      root.hidden = true;
      root.style.cssText = [
        "position:fixed",
        "inset:0",
        "width:100%",
        "height:100%",
        "max-width:none",
        "max-height:none",
        "margin:0",
        "padding:0",
        "border:none",
        "background:transparent",
        "overflow:hidden",
        "pointer-events:none",
        `z-index:${Z_INDEX.CURSOR}`,
        "box-sizing:border-box",
        "display:none"
      ].join(";");
      const targetBox = doc.createElement("div");
      targetBox.className = CSS_CLASSES.SCROLL_LINE_TARGET || "kpv2-scroll-line-target";
      targetBox.setAttribute("aria-hidden", "true");
      targetBox.style.cssText = [
        "display:none",
        "position:absolute",
        "left:0",
        "top:0",
        "box-sizing:border-box",
        "pointer-events:none",
        `border:3px solid ${STROKE}`,
        "background:transparent",
        `box-shadow:0 0 0 2px ${COLORS.ORANGE_SHADOW}, 0 0 10px 2px ${COLORS.ORANGE_SHADOW_LIGHT}`,
        "will-change:transform,width,height"
      ].join(";");
      const svg = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
      svg.setAttribute("width", "100%");
      svg.setAttribute("height", "100%");
      svg.style.cssText = "display:block;width:100%;height:100%;pointer-events:none;";
      const line = doc.createElementNS("http://www.w3.org/2000/svg", "line");
      line.setAttribute("stroke", STROKE);
      line.setAttribute("stroke-width", "2");
      line.setAttribute("stroke-linecap", "round");
      line.setAttribute("fill", "none");
      const dot = doc.createElementNS("http://www.w3.org/2000/svg", "circle");
      dot.setAttribute("r", String(DOT_R));
      dot.setAttribute("fill", STROKE);
      dot.setAttribute("stroke", COLORS.TEXT_WHITE_PRIMARY);
      dot.setAttribute("stroke-width", "2");
      svg.appendChild(line);
      svg.appendChild(dot);
      root.appendChild(targetBox);
      root.appendChild(svg);
      this.root = root;
      this.svg = svg;
      this.line = line;
      this.dot = dot;
      this.targetBox = targetBox;
    }
  };

  // src/modules/pointer-function-bindings.js
  function pointerButtonName(e) {
    if (!e || typeof e.button !== "number") return null;
    if (e.button === 0) return "left";
    if (e.button === 1) return "middle";
    if (e.button === 2) return "right";
    return null;
  }
  function readSettingsPath(settings, path) {
    const s = String(path || "");
    if (!s) return void 0;
    let cur = settings;
    for (const part of s.split(".")) {
      if (!cur || typeof cur !== "object") return void 0;
      cur = cur[part];
    }
    return cur;
  }
  function matchPointerFunctionDef(e, settings) {
    const button = pointerButtonName(e);
    if (!button) return null;
    for (const def of listPointerBoundFunctionDefs()) {
      const b = def?.pointerBinding;
      if (!b || b.button !== button) continue;
      if (b.enabledSetting && !readSettingsPath(settings, b.enabledSetting)) continue;
      return def;
    }
    return null;
  }

  // src/modules/navigation-handlers.js
  init_constants();
  init_settings_manager();
  function withNavigationHandlers(Base) {
    return class NavigationHandlers extends Base {
      handleScroll(e) {
        if (!this.enabled) {
          return;
        }
        return;
      }
      /**
       * C / V scroll distance (px), from Settings with SCROLL fallback.
       * @returns {number}
       */
      _getHalfPageScrollPx() {
        const n = Number(this._settings?.scroll?.halfPagePx);
        if (Number.isFinite(n) && n > 0) return n;
        return SCROLL.HALF_PAGE_PX;
      }
      /**
       * Legacy page-step scroll distance (px). Used by popover parent→iframe
       * PAGE_UP / PAGE_DOWN; Z / X are cursor-aware edge jumps instead.
       * @returns {number}
       */
      _getPageScrollPx() {
        return SCROLL.PAGE_PX;
      }
      /**
       * CSS scroll-behavior derived from Settings scroll.speed.
       * @returns {'smooth'|'auto'}
       */
      _getScrollBehavior() {
        return scrollBehaviorFromSpeed(this._settings?.scroll?.speed ?? DEFAULT_SETTINGS.scroll.speed);
      }
      /**
       * Parent-document popover iframe scroll (C/V while the popover is open).
       * @param {KeyboardEvent} e
       * @param {number} sign
       * @param {number} stepPx
       */
      _scrollPopoverByHeld(e, sign, stepPx) {
        const s = sign < 0 ? -1 : 1;
        if (e?.repeat) {
          this._scrollHold.noteRepeat(e.key, s);
          return;
        }
        const behavior = this._getScrollBehavior();
        this.overlayManager?.scrollPopoverBy?.(s * stepPx, behavior);
        this._scrollHold.begin({
          key: e?.key,
          sign: s,
          target: { kind: "popover" }
        });
      }
      handlePageUp(e) {
        if (!this._allowActionKey("handlePageUp", e)) return;
        this._scrollWindowByHeld(e, -1, this._getPageScrollPx());
        this.emitAction("scrollUp");
      }
      handlePageDown(e) {
        if (!this._allowActionKey("handlePageDown", e)) return;
        this._scrollWindowByHeld(e, 1, this._getPageScrollPx());
        this.emitAction("scrollDown");
      }
      handleInstantPageUp(e) {
        if (!this._allowActionKey("handleInstantPageUp", e)) return;
        this._scrollHalfPageAtCursor(-1, e);
        this.emitAction("scrollUp");
      }
      handleInstantPageDown(e) {
        if (!this._allowActionKey("handleInstantPageDown", e)) return;
        this._scrollHalfPageAtCursor(1, e);
        this.emitAction("scrollDown");
      }
      /**
       * @returns {Element}
       */
      _getWindowScrollEl() {
        try {
          return document.scrollingElement || document.documentElement || document.body;
        } catch {
          return document.documentElement;
        }
      }
      /**
       * Hold speed for continuous rAF (px/s). Scales lightly with configured step.
       * @param {number} [stepPx]
       * @returns {number}
       */
      _getScrollHoldSpeed(stepPx) {
        const base = Number(SCROLL.HOLD_PX_PER_SEC);
        const fallback = Number.isFinite(base) && base > 0 ? base : 1400;
        const step = Number(stepPx);
        if (!Number.isFinite(step) || step <= 0) return fallback;
        return Math.max(600, Math.min(2400, step * 2.8));
      }
      /**
       * Per-frame apply for continuous hold (always instant — never CSS smooth).
       * @param {{ deltaPx: number, sign: number, target?: any }} ctx
       */
      _applyScrollHoldFrame(ctx) {
        const target = ctx?.target;
        const delta = Number(ctx?.deltaPx) || 0;
        if (!delta || !target) return;
        if (target.kind === "popover") {
          this.overlayManager?.scrollPopoverBy?.(delta, "auto");
          return;
        }
        if (target.kind === "iframe") {
          const { x, y } = this._getScrollCursorPoint();
          this._tryScrollIframeUnderCursor(x, y, ctx.sign, "auto", {
            mode: "delta",
            deltaPx: Math.abs(delta)
          });
          return;
        }
        if (target.kind === "window" || target.kind === "element") {
          const el2 = target.el || this._getWindowScrollEl();
          const axis = target.axis === "x" ? "x" : "y";
          const dx = axis === "x" ? delta : 0;
          const dy = axis === "y" ? delta : 0;
          scrollElementBy(el2, dx, dy, "auto");
        }
      }
      /**
       * Legacy window PAGE_UP / PAGE_DOWN: tap step + continuous rAF while held.
       * @param {KeyboardEvent} e
       * @param {number} sign
       * @param {number} stepPx
       */
      _scrollWindowByHeld(e, sign, stepPx) {
        const s = sign < 0 ? -1 : 1;
        if (e?.repeat) {
          this._scrollHold.noteRepeat(e.key, s);
          return;
        }
        const behavior = this._getScrollBehavior();
        const el2 = this._getWindowScrollEl();
        window.scrollBy({ top: s * stepPx, behavior });
        this._scrollHold.begin({
          key: e?.key,
          sign: s,
          target: { kind: "window", el: el2, axis: "y" },
          speedPxPerSec: this._getScrollHoldSpeed(stepPx)
        });
      }
      /**
       * Last known cursor point, or viewport center when unknown.
       * @returns {{ x: number, y: number }}
       */
      _getScrollCursorPoint() {
        const st = this.state.getState();
        let x = Number(st?.lastMouse?.x);
        let y = Number(st?.lastMouse?.y);
        if (!Number.isFinite(x) || !Number.isFinite(y) || x < 0 || y < 0) {
          x = Math.floor(window.innerWidth / 2);
          y = Math.floor(window.innerHeight / 2);
        }
        return { x, y };
      }
      /**
       * C / V: scroll the overflow container under the cursor first (vertical, or
       * horizontal when that container only/can scroll on X). Falls back to the
       * page when nothing nested can move. Forwards into iframes via the light
       * frame-click-agent (FRAME_SCROLL).
       *
       * Tap: one configured step (smooth/instant from Settings).
       * Hold: continuous rAF instant deltas until keyup (ignores OS key-repeat).
       *
       * @param {number} sign  -1 = C (up/left), +1 = V (down/right)
       * @param {KeyboardEvent|null|undefined} [e]
       */
      _scrollHalfPageAtCursor(sign, e = null) {
        const stepPx = this._getHalfPageScrollPx();
        const behavior = this._getScrollBehavior();
        const s = sign < 0 ? -1 : 1;
        if (e?.repeat) {
          this._scrollHold.noteRepeat(e.key, s);
          return;
        }
        const { x, y } = this._getScrollCursorPoint();
        const speed = this._getScrollHoldSpeed(stepPx);
        if (this._tryScrollIframeUnderCursor(x, y, s, behavior, { mode: "delta", deltaPx: stepPx })) {
          this._scrollHold.begin({
            key: e?.key,
            sign: s,
            target: { kind: "iframe" },
            speedPxPerSec: speed
          });
          return;
        }
        const found = findScrollTargetAtPoint(x, y, s);
        const el2 = found?.el || this._getWindowScrollEl();
        const axis = found?.axis || "y";
        scrollAtPoint(x, y, s, stepPx, behavior);
        this._scrollHold.begin({
          key: e?.key,
          sign: s,
          target: { kind: "element", el: el2, axis },
          speedPxPerSec: speed
        });
      }
      /**
       * Jump-style for Scroll To Top / Bottom (`mode`: fade | smooth). Fade is the default.
       * @param {'PAGE_TOP'|'PAGE_BOTTOM'} functionId
       * @param {{ mode?: string }|null|undefined} [parameters]
       * @returns {'fade'|'smooth'}
       */
      _resolveEdgeJumpStyle(functionId, parameters) {
        const raw = parameters?.mode ?? getActionMode(this._getBuiltinFunctionActionParams(functionId), functionId);
        return raw === "smooth" ? "smooth" : "fade";
      }
      /**
       * Overflow box under the cursor (iframe shell, nested scroller, or document root).
       * @param {number} clientX
       * @param {number} clientY
       * @param {number} sign
       * @returns {Element|null}
       */
      _resolveEdgeJumpCoverEl(clientX, clientY, sign) {
        try {
          const under = this.detector?.deepElementFromPoint?.(clientX, clientY) || deepElementFromPoint(clientX, clientY);
          if (under && (under.tagName === "IFRAME" || under.tagName === "FRAME")) {
            if (!this._isKeyPilotUiElement?.(under)) return under;
          }
        } catch {
        }
        try {
          const target = findScrollTargetAtPoint(clientX, clientY, sign);
          if (target?.el) return target.el;
        } catch {
        }
        try {
          return document.scrollingElement || document.documentElement || document.body;
        } catch {
          return null;
        }
      }
      /**
       * Cover the overflow box, then run `fn` (instant jump).
       * @param {() => void} fn
       * @param {Element|null} coverEl
       * @param {'top'|'bottom'|null} [edge]
       */
      _runEdgeJump(fn, coverEl, edge = null) {
        const run = () => {
          try {
            fn();
          } catch {
          }
        };
        if (this.overlayManager?.runEdgeJumpFade) {
          void this.overlayManager.runEdgeJumpFade(run, {
            coverEl: coverEl || null,
            edge: edge === "bottom" ? "bottom" : "top"
          });
          return;
        }
        run();
      }
      /**
       * Z / X: same cursor targeting as C / V, but jump to the start/end edge.
       *
       * @param {number} sign  -1 = Z (top/left), +1 = X (bottom/right)
       * @param {{ jumpStyle?: 'fade'|'smooth' }} [opts]
       */
      _scrollToEdgeAtCursor(sign, opts = {}) {
        const s = sign < 0 ? -1 : 1;
        const { x, y } = this._getScrollCursorPoint();
        const fade = opts.jumpStyle !== "smooth";
        const behavior = fade ? "auto" : this._getScrollBehavior();
        const jump = () => {
          if (this._tryScrollIframeUnderCursor(x, y, s, behavior, { mode: "edge" })) {
            return;
          }
          scrollToEdgeAtPoint(x, y, s, behavior);
        };
        if (!fade) {
          jump();
          return;
        }
        this._runEdgeJump(jump, this._resolveEdgeJumpCoverEl(x, y, s), s < 0 ? "top" : "bottom");
      }
      /**
       * Popover iframe edge jump, using the same Fade / Scroll setting as the page keys.
       * @param {number} sign  -1 = top, +1 = bottom
       */
      _scrollPopoverToEdge(sign) {
        const functionId = sign < 0 ? "PAGE_TOP" : "PAGE_BOTTOM";
        const fade = this._resolveEdgeJumpStyle(functionId) === "fade";
        const behavior = fade ? "instant" : this._getScrollBehavior();
        const jump = () => {
          if (sign < 0) this.overlayManager?.scrollPopoverToTop?.(behavior);
          else this.overlayManager?.scrollPopoverToBottom?.(behavior);
        };
        if (!fade) {
          jump();
          return;
        }
        const coverEl = this.overlayManager?.popoverIframeElement || this.overlayManager?.popoverContainer || null;
        this._runEdgeJump(jump, coverEl, sign < 0 ? "top" : "bottom");
      }
      /**
       * When the pointer is over an iframe, scroll inside that frame at local
       * coordinates (same-origin directly; cross-origin via FRAME_SCROLL agent).
       *
       * @param {number} clientX
       * @param {number} clientY
       * @param {number} sign
       * @param {ScrollBehavior} behavior
       * @param {{ mode?: 'delta'|'edge'|'xy', deltaPx?: number, deltaX?: number, deltaY?: number, iframe?: HTMLIFrameElement, localX?: number, localY?: number }} [opts]
       * @returns {boolean} true if an iframe under the cursor was targeted
       */
      _tryScrollIframeUnderCursor(clientX, clientY, sign, behavior, opts = {}) {
        const lockedIframe = opts.iframe && (opts.iframe.tagName === "IFRAME" || opts.iframe.tagName === "FRAME") ? (
          /** @type {HTMLIFrameElement} */
          opts.iframe
        ) : null;
        let iframe = lockedIframe;
        if (!iframe) {
          if (!Number.isFinite(clientX) || !Number.isFinite(clientY)) return false;
          let under = null;
          try {
            under = this.detector?.deepElementFromPoint?.(clientX, clientY) || deepElementFromPoint(clientX, clientY);
          } catch {
            under = null;
          }
          if (!under || under.tagName !== "IFRAME" && under.tagName !== "FRAME") {
            return false;
          }
          try {
            if (this._isKeyPilotUiElement?.(under)) return false;
          } catch {
          }
          iframe = /** @type {HTMLIFrameElement} */
          under;
        }
        let rect;
        try {
          rect = iframe.getBoundingClientRect();
        } catch {
          return false;
        }
        if (!rect || rect.width <= 0 || rect.height <= 0) return false;
        const localX = lockedIframe && Number.isFinite(opts.localX) ? Number(opts.localX) : clientX - rect.left;
        const localY = lockedIframe && Number.isFinite(opts.localY) ? Number(opts.localY) : clientY - rect.top;
        if (!lockedIframe && (localX < 0 || localY < 0 || localX > rect.width || localY > rect.height)) {
          return false;
        }
        const mode = opts.mode === "edge" ? "edge" : opts.mode === "xy" ? "xy" : "delta";
        const payload = {
          type: MSG.FRAME_SCROLL,
          clientX: localX,
          clientY: localY,
          sign: sign < 0 ? -1 : 1,
          mode,
          deltaPx: mode === "edge" || mode === "xy" ? 0 : Math.abs(Number(opts.deltaPx)) || 0,
          deltaX: mode === "xy" ? Number(opts.deltaX) || 0 : 0,
          deltaY: mode === "xy" ? Number(opts.deltaY) || 0 : 0,
          behavior: mode === "xy" || behavior === "auto" || behavior === "instant" ? "auto" : "smooth",
          frameName: typeof iframe.name === "string" ? iframe.name : ""
        };
        try {
          const doc = iframe.contentDocument;
          const view = iframe.contentWindow;
          if (doc && view) {
            let el2 = null;
            try {
              el2 = deepElementFromPoint(localX, localY, doc);
            } catch {
              el2 = null;
            }
            if (el2 && (el2.tagName === "IFRAME" || el2.tagName === "FRAME")) {
              try {
                const nested = (
                  /** @type {HTMLIFrameElement} */
                  el2
                );
                const nr = nested.getBoundingClientRect();
                nested.contentWindow?.postMessage({
                  ...payload,
                  clientX: localX - nr.left,
                  clientY: localY - nr.top,
                  frameName: typeof nested.name === "string" ? nested.name : ""
                }, "*");
                return true;
              } catch {
              }
            }
            if (mode === "xy") {
              scrollByAtPoint(localX, localY, payload.deltaX, payload.deltaY, payload.behavior, {
                doc,
                win: view
              });
            } else if (mode === "edge") {
              scrollToEdgeAtPoint(localX, localY, payload.sign, payload.behavior, {
                doc,
                win: view
              });
            } else {
              scrollAtPoint(localX, localY, payload.sign, payload.deltaPx, payload.behavior, {
                doc,
                win: view
              });
            }
            return true;
          }
        } catch {
        }
        let posted = false;
        try {
          const win = iframe.contentWindow;
          if (win) {
            win.postMessage(payload, "*");
            posted = true;
          }
        } catch {
        }
        try {
          this._sendRuntimeMessage(payload, { silent: true });
          posted = true;
        } catch {
        }
        return posted;
      }
      handlePageTop(e, parameters) {
        if (!this._allowActionKey("handlePageTop", e)) return;
        this._scrollToEdgeAtCursor(-1, { jumpStyle: this._resolveEdgeJumpStyle("PAGE_TOP", parameters) });
        this.emitAction("scrollTop");
      }
      handlePageBottom(e, parameters) {
        if (!this._allowActionKey("handlePageBottom", e)) return;
        this._scrollToEdgeAtCursor(1, { jumpStyle: this._resolveEdgeJumpStyle("PAGE_BOTTOM", parameters) });
        this.emitAction("scrollBottom");
      }
      handleScrollLineKey(e) {
        if (e?.repeat) return;
        const fromPointer = !!e && typeof e.button === "number";
        if (!fromPointer && !this._allowActionKey("handleScrollLineKey", e)) return;
        const currentState = this.state.getState();
        if (currentState.mode === MODES.TEXT_FOCUS) return;
        if (currentState.mode === MODES.POPOVER || currentState.mode === MODES.OMNIBOX) return;
        if (this.state.isScrollLineMode()) {
          this._exitScrollLineMode();
          return;
        }
        this._enterScrollLineMode();
      }
      /**
       * Detach delegated pointerover hover targeting so Click Mode outlines do not
       * update while a selection/pick mode (Text Select, Element Select, Delete,
       * Cols, Scroll Line) owns the pointer. Restore with
       * {@link _restoreClickableHoverTracking}.
       */
      _suspendClickableHoverTracking() {
        try {
          this.state.setFocusElement(null);
        } catch {
        }
        try {
          this.overlayManager?.hideFocusOverlay?.();
        } catch {
        }
        if (!this._domHoverListenersEnabled) return;
        try {
          this.intersectionManager?.setDomHoverListenersEnabled?.(
            false,
            (el2) => this._handleDomHoverChange(el2)
          );
        } catch {
        }
      }
      /**
       * Re-attach pointerover hover targeting after a selection/pick mode ends.
       */
      _restoreClickableHoverTracking() {
        if (!this.enabled || !this._domHoverListenersEnabled) return;
        try {
          this.intersectionManager?.setDomHoverListenersEnabled?.(
            true,
            (el2) => this._handleDomHoverChange(el2)
          );
        } catch {
        }
        try {
          const { x, y } = this._getScrollCursorPoint();
          this.intersectionManager?.resyncDomHoverAtPoint?.(x, y);
        } catch {
        }
      }
      _enterScrollLineMode() {
        try {
          if (this.state.isHighlightMode()) this.cancelHighlightMode();
        } catch {
        }
        try {
          if (this.state.isInspectorMode()) this.inspector.exit();
        } catch {
        }
        this._suspendClickableHoverTracking();
        const { x, y } = this._getScrollCursorPoint();
        this._scrollLineOrigin = { x, y };
        this._scrollLineTarget = this._resolveScrollLineTarget(x, y);
        if (SCROLL_LINE_MAP_DRAG_ENABLED && this._scrollLineTarget?.kind === "drag") {
          try {
            ensureMapPanBridge();
          } catch {
          }
          try {
            this._scrollLineDragSession = createMapPanSession(this._scrollLineTarget.el, x, y);
          } catch {
            this._scrollLineDragSession = null;
          }
        }
        try {
          this._scrollLineOverlay?.show(x, y);
        } catch {
        }
        try {
          this._syncScrollLineTargetOverlay();
        } catch {
        }
        this.state.setMode(MODES.SCROLL_LINE);
        this._scrollLineLastTs = 0;
        if (this._scrollLineRaf) {
          try {
            cancelAnimationFrame(this._scrollLineRaf);
          } catch {
          }
          this._scrollLineRaf = 0;
        }
        const tick = (ts) => {
          this._scrollLineRaf = 0;
          if (!this.state.isScrollLineMode()) return;
          this._tickScrollLine(ts);
          this._scrollLineRaf = window.requestAnimationFrame(tick);
        };
        this._scrollLineRaf = window.requestAnimationFrame(tick);
        this.emitAction("scrollLine");
      }
      _exitScrollLineMode() {
        if (this._scrollLineRaf) {
          try {
            cancelAnimationFrame(this._scrollLineRaf);
          } catch {
          }
          this._scrollLineRaf = 0;
        }
        this._scrollLineLastTs = 0;
        this._scrollLineTarget = null;
        if (this._scrollLineDragSession) {
          try {
            endMapPanSession();
          } catch {
          }
        }
        this._scrollLineDragSession = null;
        try {
          this._scrollLineOverlay?.hide();
        } catch {
        }
        try {
          if (this.state.isScrollLineMode()) this.state.setMode(MODES.NONE);
        } catch {
        }
        this._restoreClickableHoverTracking();
      }
      /**
       * Lock the scroller (or iframe) under the origin. Same targeting as C/V.
       * @param {number} x
       * @param {number} y
       */
      _resolveScrollLineTarget(x, y) {
        let under = null;
        try {
          under = this.detector?.deepElementFromPoint?.(x, y) || deepElementFromPoint(x, y);
        } catch {
          under = null;
        }
        if (SCROLL_LINE_MAP_DRAG_ENABLED) {
          try {
            const mapEl = findMapSurfaceAtPoint(x, y);
            if (mapEl) return { kind: "drag", el: mapEl };
          } catch {
          }
        }
        const skipWide = this._settings?.scroll?.linePreferPortraitTargets !== false;
        if (under && (under.tagName === "IFRAME" || under.tagName === "FRAME")) {
          try {
            if (!this._isKeyPilotUiElement?.(under)) {
              const iframe = (
                /** @type {HTMLIFrameElement} */
                under
              );
              const rect = iframe.getBoundingClientRect();
              const vw = window.innerWidth || 0;
              const vh = window.innerHeight || 0;
              const fullPane = !!(rect && vw > 0 && vh > 0 && rect.width >= vw * 0.94 && rect.height >= vh * 0.94);
              const wideStrip = skipWide && rect && rect.width > rect.height + 1 && !fullPane;
              if (rect && rect.width > 0 && rect.height > 0 && !wideStrip) {
                return {
                  kind: "iframe",
                  iframe,
                  localX: x - rect.left,
                  localY: y - rect.top
                };
              }
            }
          } catch {
          }
        }
        const found = findScrollableAtPoint(x, y, { skipWideTargets: skipWide });
        if (found?.el) {
          return { kind: "element", el: found.el, canX: !!found.canX, canY: !!found.canY };
        }
        const se = document.scrollingElement || document.documentElement || document.body;
        if (se) {
          try {
            const cap = getScrollCapacity(se);
            if (cap.canX || cap.canY) {
              return { kind: "element", el: se, canX: !!cap.canX, canY: !!cap.canY };
            }
          } catch {
          }
        }
        return null;
      }
      /**
       * Outline the locked nested scroller (or iframe). Hidden for document /
       * near-full-viewport roots so page scroll does not draw a screen-sized box.
       */
      _syncScrollLineTargetOverlay() {
        const overlay = this._scrollLineOverlay;
        if (!overlay || typeof overlay.setTargetBox !== "function") return;
        const target = this._scrollLineTarget;
        let el2 = null;
        if (target?.kind === "iframe" && target.iframe) {
          el2 = target.iframe;
        } else if (target?.kind === "drag" && target.el) {
          el2 = target.el;
        } else if (target?.kind === "element" && target.el) {
          el2 = target.el;
        }
        if (!el2 || el2.nodeType !== 1) {
          overlay.setTargetBox(null);
          return;
        }
        try {
          if (isDocumentScrollRoot(el2, document)) {
            overlay.setTargetBox(null);
            return;
          }
        } catch {
        }
        let r = null;
        try {
          r = el2.getBoundingClientRect();
        } catch {
          r = null;
        }
        if (!r || !(r.width > 8) || !(r.height > 8)) {
          overlay.setTargetBox(null);
          return;
        }
        try {
          const vw = window.innerWidth || 0;
          const vh = window.innerHeight || 0;
          if (vw > 0 && vh > 0 && r.width >= vw * 0.94 && r.height >= vh * 0.94) {
            overlay.setTargetBox(null);
            return;
          }
        } catch {
        }
        let radius = "0";
        try {
          radius = String(window.getComputedStyle(el2).borderRadius || "0");
        } catch {
          radius = "0";
        }
        overlay.setTargetBox({
          left: r.left,
          top: r.top,
          width: r.width,
          height: r.height,
          radius
        });
      }
      /**
       * Ease-in curve: small moves stay slow; speed ramps toward the cap as the
       * pointer approaches LINE_CURVE_RANGE_PX beyond the dead zone.
       * @param {number} offset
       * @returns {number} px/s
       */
      _scrollLineAxisVelocity(offset) {
        const mag = Math.abs(Number(offset) || 0);
        const dead = Number(SCROLL.LINE_DEADZONE_PX) || 12;
        if (mag <= dead) return 0;
        const range = Math.max(1, Number(SCROLL.LINE_CURVE_RANGE_PX) || 360);
        const exp = Number(SCROLL.LINE_CURVE_EXPONENT);
        const power = Number.isFinite(exp) && exp > 0 ? exp : 2;
        const cap = Number(SCROLL.LINE_MAX_PX_PER_SEC) || 2400;
        const t = Math.min(1, (mag - dead) / range);
        const speed = cap * Math.pow(t, power);
        return (offset < 0 ? -1 : 1) * speed;
      }
      /**
       * @param {number} ts
       */
      _tickScrollLine(ts) {
        const last = this._scrollLineLastTs;
        this._scrollLineLastTs = ts;
        if (!last) return;
        const dt = Math.min(0.05, Math.max(0, (ts - last) / 1e3));
        if (!dt) return;
        const mouse = this.state.getState()?.lastMouse || this._scrollLineOrigin;
        const ox = this._scrollLineOrigin?.x || 0;
        const oy = this._scrollLineOrigin?.y || 0;
        const mx = Number(mouse?.x);
        const my = Number(mouse?.y);
        const px = Number.isFinite(mx) ? mx : ox;
        const py = Number.isFinite(my) ? my : oy;
        try {
          this._scrollLineOverlay?.updatePointer(px, py);
        } catch {
        }
        try {
          this._syncScrollLineTargetOverlay();
        } catch {
        }
        let vx = this._scrollLineAxisVelocity(px - ox);
        let vy = this._scrollLineAxisVelocity(py - oy);
        const dx = vx * dt;
        const dy = vy * dt;
        if (!dx && !dy) return;
        const target = this._scrollLineTarget;
        if (SCROLL_LINE_MAP_DRAG_ENABLED && target?.kind === "drag" && target.el) {
          if (!this._scrollLineDragSession || this._scrollLineDragSession.el !== target.el) {
            this._scrollLineDragSession = createMapPanSession(target.el, ox, oy);
          }
          mapPanBy(this._scrollLineDragSession, dx, dy);
          return;
        }
        if (target?.kind === "iframe" && target.iframe) {
          this._tryScrollIframeUnderCursor(ox, oy, 0, "auto", {
            mode: "xy",
            deltaX: dx,
            deltaY: dy,
            iframe: target.iframe,
            localX: target.localX,
            localY: target.localY
          });
          return;
        }
        if (target?.kind === "element" && target.el) {
          let applyX = target.canX ? dx : 0;
          let applyY = target.canY ? dy : 0;
          if (!applyX && !applyY) return;
          scrollElementBy(target.el, applyX, applyY, "auto");
          return;
        }
        try {
          window.scrollBy({ left: dx, top: dy, behavior: "auto" });
        } catch {
        }
      }
      /**
       * True when the event matches browser history back/forward keybindings.
       * @param {Record<string, any>} KB
       * @param {KeyboardEvent} e
       */
      _isHistoryNavigationKey(KB, e) {
        if (!KB || !e) return false;
        return !!(KB.BACK?.keys?.includes?.(e.key) || KB.BACK2?.keys?.includes?.(e.key) || KB.FORWARD?.keys?.includes?.(e.key));
      }
      /**
       * Run the history handler that matches this key (back vs forward).
       * @param {Record<string, any>} KB
       * @param {KeyboardEvent} e
       */
      _runHistoryNavigationKey(KB, e) {
        if (KB.FORWARD?.keys?.includes?.(e.key)) {
          this.handleForwardKey();
          return;
        }
        this.handleBackKey();
      }
      handleBackKey(e) {
        if (!this._allowActionKey("handleBackKey", e)) return;
        this.emitAction("back");
        this._navigateHistory(-1, { recordTransientAction: "back" });
      }
      handleForwardKey(e) {
        if (!this._allowActionKey("handleForwardKey", e)) return;
        this._navigateHistory(1);
      }
      /**
       * Navigate browser session history (back/forward).
       *
       * Uses `history.back/forward` as the primary action (immediate, same stack as the page).
       * Transient onboarding actions are recorded separately via the service worker.
       *
       * Silent hops (common SPA / in-page patterns that feel like "D did nothing"):
       * - Only cleared a `#hash` fragment
       * - URL is unchanged after a same-document `pushState` entry
       * - Only the query string changed on the same path (e.g. openrouter.ai/activity
       *   pushes `?from=&to=&date_preset=` after load — first back only strips filters)
       * In those cases, take one automatic extra step so one keypress matches user intent.
       *
       * @param {-1|1} direction
       * @param {{ recordTransientAction?: string }} [opts]
       */
      _navigateHistory(direction, opts = {}) {
        const dir = direction < 0 ? -1 : 1;
        const recordAction = typeof opts.recordTransientAction === "string" ? opts.recordTransientAction : "";
        if (recordAction) {
          this._sendRuntimeMessage({
            type: MSG.TRANSIENT_ACTION,
            action: recordAction,
            timestamp: Date.now()
          }, { silent: true });
        }
        const step = () => {
          try {
            if (dir < 0) history.back();
            else history.forward();
          } catch {
          }
        };
        if (window !== window.top) {
          step();
          return;
        }
        if (dir >= 0) {
          step();
          return;
        }
        const beforeHref = location.href;
        const beforeOrigin = location.origin;
        const beforePathname = location.pathname;
        const beforeSearch = location.search || "";
        const beforePath = `${beforePathname}${beforeSearch}`;
        const beforeHash = location.hash || "";
        try {
          if (this._historySkipCleanup) this._historySkipCleanup();
        } catch {
        }
        this._historySkipCleanup = null;
        let extraHopsUsed = 0;
        const maxExtraHops = 1;
        const cleanup = () => {
          if (popListener) {
            try {
              window.removeEventListener("popstate", popListener);
            } catch {
            }
            popListener = null;
          }
          if (timer) {
            try {
              clearTimeout(timer);
            } catch {
            }
            timer = 0;
          }
          if (this._historySkipCleanup === cleanup) this._historySkipCleanup = null;
        };
        let popListener = () => {
          try {
            if (extraHopsUsed >= maxExtraHops) {
              cleanup();
              return;
            }
            const path = `${location.pathname}${location.search}`;
            const hash = location.hash || "";
            const href = location.href;
            const search = location.search || "";
            const urlUnchanged = href === beforeHref;
            const onlyClearedHash = !!beforeHash && !hash && path === beforePath;
            const samePath = location.origin === beforeOrigin && location.pathname === beforePathname;
            const onlyQueryChanged = samePath && search !== beforeSearch;
            if (urlUnchanged || onlyClearedHash || onlyQueryChanged) {
              extraHopsUsed += 1;
              step();
              return;
            }
          } catch {
          }
          cleanup();
        };
        let timer = 0;
        try {
          window.addEventListener("popstate", popListener);
          timer = window.setTimeout(cleanup, 700);
          this._historySkipCleanup = cleanup;
        } catch {
          popListener = null;
        }
        step();
      }
      handleTabLeftKey(e) {
        if (!this._allowActionKey("handleTabLeftKey", e)) return;
        this.emitAction("tabLeft");
        this._sendRuntimeMessage({ type: MSG.TRANSIENT_ACTION, action: "tabLeft", timestamp: Date.now() }, { silent: true });
        if (!this._sendRuntimeMessage({ type: MSG.TAB_LEFT }, {
          onResponse: (response) => this._notifyTabSwitchFailure(response)
        })) {
        }
      }
      handleTabRightKey(e) {
        if (!this._allowActionKey("handleTabRightKey", e)) return;
        this.emitAction("tabRight");
        this._sendRuntimeMessage({ type: MSG.TRANSIENT_ACTION, action: "tabRight", timestamp: Date.now() }, { silent: true });
        if (!this._sendRuntimeMessage({ type: MSG.TAB_RIGHT }, {
          onResponse: (response) => this._notifyTabSwitchFailure(response)
        })) {
        }
      }
      /**
       * Show the top-center flash alert when left/right tab switch has nowhere to go
       * (or otherwise fails in the background).
       * @param {unknown} response
       */
      _notifyTabSwitchFailure(response) {
        if (!response || typeof response !== "object") return;
        if (
          /** @type {{ type?: string }} */
          response.type !== MSG.ERROR
        ) return;
        const error = String(
          /** @type {{ error?: unknown }} */
          response.error || ""
        );
        const message = /no valid tabs to switch to/i.test(error) ? "No other tabs to switch to" : error ? error.replace(/^Failed to switch tab:\s*/i, "") : "Failed to switch tab";
        this.showFlashNotification(message, COLORS.NOTIFICATION_INFO);
      }
      handleRootKey(e) {
        if (!this._allowActionKey("handleRootKey", e)) return;
        console.log("[KeyPilot] Root key pressed!");
        console.log("[KeyPilot] Current URL:", window.location.href);
        console.log("[KeyPilot] Origin:", window.location.origin);
        const rootUrl = window.location.origin;
        if (rootUrl && rootUrl !== window.location.href) {
          console.log("[KeyPilot] Navigating to root:", rootUrl);
          this.showFlashNotification("Navigating to Site Root...", COLORS.NOTIFICATION_INFO);
          window.location.href = rootUrl;
        } else {
          console.log("[KeyPilot] Already at root, no navigation needed");
        }
      }
      handleCloseTabKey(e) {
        if (!this._allowActionKey("handleCloseTabKey", e)) return;
        console.log("[KeyPilot] Close tab key pressed!");
        try {
          if (!this._sendRuntimeMessage({ type: MSG.CLOSE_TAB })) {
            if (!this._extensionContextInvalidatedHandled) {
              this.showFlashNotification("Failed to Close Tab", COLORS.NOTIFICATION_ERROR);
            }
            return;
          }
          this.showFlashNotification("Closing Tab...", COLORS.NOTIFICATION_INFO);
        } catch (error) {
          if (noteExtensionContextError(error)) {
            this._handleExtensionContextInvalidated();
            return;
          }
          console.error("[KeyPilot] Failed to close tab:", error);
          this.showFlashNotification("Failed to Close Tab", COLORS.NOTIFICATION_ERROR);
        }
      }
      handleNewTabKey(e) {
        if (!this._allowActionKey("handleNewTabKey", e)) return;
        console.log("[KeyPilot] New tab key pressed!");
        try {
          this.emitAction("newTab");
          this._sendRuntimeMessage({ type: MSG.TRANSIENT_ACTION, action: "newTab", timestamp: Date.now() }, { silent: true });
          if (!this._sendRuntimeMessage({ type: MSG.NEW_TAB })) {
            if (!this._extensionContextInvalidatedHandled) {
              this.showFlashNotification("Failed to Open New Tab", COLORS.NOTIFICATION_ERROR);
            }
            return;
          }
          this.showFlashNotification("Opening New Tab...", COLORS.NOTIFICATION_INFO);
        } catch (error) {
          if (noteExtensionContextError(error)) {
            this._handleExtensionContextInvalidated();
            return;
          }
          console.error("[KeyPilot] Failed to open new tab:", error);
          this.showFlashNotification("Failed to Open New Tab", COLORS.NOTIFICATION_ERROR);
        }
      }
    };
  }

  // src/modules/activation-handlers.js
  init_constants();
  function withActivationHandlers(Base) {
    return class ActivationHandlers extends Base {
      handleActivateKey() {
        const currentState = this.state.getState();
        const x = currentState.lastMouse.x;
        const y = currentState.lastMouse.y;
        if (this._tryActivateIframeUnderCursor(x, y, {})) {
          this.showRipple(x, y);
          this.emitAction("activate", { viaIframe: true });
          return;
        }
        const target = this._getValidatedActivationTarget(currentState);
        if (!target || target === document.documentElement || target === document.body) {
          return;
        }
        if (currentState.mode === MODES.POPOVER) {
          if (!this._isElementInPopover(target)) {
            return;
          }
        }
        console.log("[KeyPilot] Activating element:", {
          tagName: target.tagName,
          className: target.className,
          id: target.id,
          hasClickHandler: !!(target.onclick || target.getAttribute("onclick"))
        });
        const activationDetail = this._buildActivationDetail(target);
        try {
          const focus = currentState.focusEl;
          if (focus instanceof Element && target instanceof Element && (focus === target || containsComposed(focus, target) || containsComposed(target, focus))) {
            activationDetail.hadFocusOutline = true;
          }
        } catch {
        }
        if (target.tagName === "A" && target.href) {
          this.mouseCoordinateManager.handleLinkClick(x, y, target);
        }
        this._flashThenActivate(target, () => {
          if (!this.activator.handleSmartActivate(target, x, y)) {
            this.activator.smartClick(target, x, y);
          }
          this.postClickRefresh(target, x, y);
          this.emitAction("activate", activationDetail);
          if (activationDetail.isKeyboardHelpKey) {
            try {
              const keyEl = closestComposed(target, "[data-kp-action-id]");
              const actionId = keyEl?.dataset?.kpActionId;
              if (actionId) pinKeyPopover(actionId, { keyEl, keybindings: this.keybindings });
            } catch {
            }
          }
        });
      }
      handleActivateNewTabKey() {
        const currentState = this.state.getState();
        const x = currentState.lastMouse.x;
        const y = currentState.lastMouse.y;
        if (this._tryActivateIframeUnderCursor(x, y, { openInNewTab: true })) {
          this.showRipple(x, y);
          this.emitAction("activateNewTab", { viaIframe: true });
          return;
        }
        const target = this._getValidatedActivationTarget(currentState);
        if (!target || target === document.documentElement || target === document.body) {
          this._flashUrlActionUnavailable(target, x, y);
          return;
        }
        if (currentState.mode === MODES.POPOVER) {
          if (!this._isElementInPopover(target)) {
            this._flashUrlActionUnavailable(target, x, y);
            return;
          }
        }
        console.log("[KeyPilot] Activating element in new tab:", {
          tagName: target.tagName,
          className: target.className,
          id: target.id,
          hasClickHandler: !!(target.onclick || target.getAttribute("onclick"))
        });
        let link = target;
        let url = null;
        const resolvedNewTab = this._resolveHoveredLink(target);
        if (resolvedNewTab?.url) {
          url = resolvedNewTab.url;
          link = resolvedNewTab.link;
        }
        const mx = currentState.lastMouse.x;
        const my = currentState.lastMouse.y;
        if (!url) {
          this._flashUrlActionUnavailable(target, mx, my);
          return;
        }
        this.mouseCoordinateManager.handleLinkClick(mx, my, link);
        this._flashThenActivate(link, () => {
          try {
            if (this._sendRuntimeMessage({ type: MSG.OPEN_URL_FOREGROUND, url })) {
              this.postClickRefresh(link, mx, my);
              this.emitAction("activateNewTab", { isLink: true, href: url });
              return;
            }
          } catch (error) {
            if (noteExtensionContextError(error)) {
              this._handleExtensionContextInvalidated();
            } else {
              console.error("[KeyPilot] Failed to open link in foreground tab:", error);
            }
          }
          this._activateNewTabFallback(target, mx, my);
        });
      }
      /**
       * Visual "this action needs a URL" cue: dashed orange outline on the hover
       * box, or a small cursor square when nothing interactive is under the pointer.
       * Used by Click New Tab, Click New Tab Background, Preview Link, Open Popover.
       * @param {Element|null|undefined} target
       * @param {number} [x]
       * @param {number} [y]
       */
      _flashUrlActionUnavailable(target, x, y) {
        const el2 = target instanceof Element && target !== document.documentElement && target !== document.body ? target : null;
        try {
          this.overlayManager?.flashDeniedDashOutline?.(el2, { x, y });
        } catch {
        }
      }
      _activateNewTabFallback(target, x, y) {
        if (this.activator.handleSmartActivate(target, x, y, true)) {
          this.postClickRefresh(target, x, y);
          this.emitAction("activateNewTab", this._buildActivationDetail(target));
          return;
        }
        this.activator.smartClick(target, x, y, true);
        this.postClickRefresh(target, x, y);
        this.emitAction("activateNewTab", this._buildActivationDetail(target));
      }
      handleActivateNewTabBackgroundKey() {
        const currentState = this.state.getState();
        const x = currentState.lastMouse.x;
        const y = currentState.lastMouse.y;
        if (this._tryActivateIframeUnderCursor(x, y, { background: true, openInNewTab: true })) {
          this.showRipple(x, y);
          this.emitAction("activateNewTabBackground", { viaIframe: true });
          return;
        }
        const target = this._getValidatedActivationTarget(currentState);
        if (!target || target === document.documentElement || target === document.body) {
          this._flashUrlActionUnavailable(target, x, y);
          return;
        }
        if (currentState.mode === MODES.POPOVER) {
          if (!this._isElementInPopover(target)) {
            this._flashUrlActionUnavailable(target, x, y);
            return;
          }
        }
        let link = target;
        let url = null;
        const resolvedBg = this._resolveHoveredLink(target);
        if (resolvedBg?.url) {
          url = resolvedBg.url;
          link = resolvedBg.link;
        }
        if (!url) {
          console.log("[KeyPilot] Activate New Tab Background: not hovering over a hyperlink");
          this._flashUrlActionUnavailable(target, x, y);
          return;
        }
        console.log("[KeyPilot] Opening link in new tab (background):", url);
        this.mouseCoordinateManager.handleLinkClick(currentState.lastMouse.x, currentState.lastMouse.y, link);
        this._flashThenActivate(link, () => {
          try {
            if (this._sendRuntimeMessage({ type: MSG.OPEN_URL_BACKGROUND, url })) {
              this.postClickRefresh(link, currentState.lastMouse.x, currentState.lastMouse.y);
              this.emitAction("activateNewTabBackground", { isLink: true, href: url });
              return;
            }
            try {
              window.open(url, "_blank", "noopener,noreferrer");
            } catch {
            }
          } catch (error) {
            if (noteExtensionContextError(error)) {
              this._handleExtensionContextInvalidated();
            } else {
              console.error("[KeyPilot] Failed to open link in background tab:", error);
            }
            try {
              window.open(url, "_blank", "noopener,noreferrer");
            } catch {
            }
          }
        });
      }
      handleOpenPopover(e) {
        if (!this._allowActionKey("handleOpenPopover", e)) return;
        if (this.overlayManager.isPopoverOpen()) {
          this._closeMapSitePopover();
          return;
        }
        const currentState = this.state.getState();
        const { lastMouse } = currentState;
        const mx = Number(lastMouse?.x);
        const my = Number(lastMouse?.y);
        const px = Number.isFinite(mx) ? mx : (window.innerWidth || 0) / 2;
        const py = Number.isFinite(my) ? my : (window.innerHeight || 0) / 2;
        const onMap = this.isOnMapWebsite() || !!findMapSurfaceAtPoint(px, py);
        if (onMap) {
          void this._resolvePoiWebsiteAtPoint(px, py).then((poiUrl) => {
            if (!poiUrl) {
              this.showFlashNotification("No website for this place", COLORS.NOTIFICATION_INFO);
              return;
            }
            this._openFullPopover(poiUrl);
          }).catch(() => {
            this.showFlashNotification("No website for this place", COLORS.NOTIFICATION_INFO);
          });
          return;
        }
        let target = currentState.focusEl;
        if (!target) {
          const under = this.detector.deepElementFromPoint(lastMouse.x, lastMouse.y);
          target = this.detector.findClickable(under);
        }
        if (!target || !(target instanceof Element)) {
          console.log("[KeyPilot] Open popover: not hovering over a link");
          this._flashUrlActionUnavailable(target, lastMouse?.x, lastMouse?.y);
          return;
        }
        const resolvedPopover = this._resolveHoveredLink(target);
        const url = resolvedPopover?.url || null;
        if (!url) {
          console.log("[KeyPilot] Open popover: not hovering over a link");
          this._flashUrlActionUnavailable(target, lastMouse?.x, lastMouse?.y);
          return;
        }
        console.log("[KeyPilot] Opening popover for link:", url);
        this.overlayManager.showPopover(url);
        this.state.setPopoverOpen(true, url);
      }
    };
  }

  // src/keypilot.js
  var KeyPilot = class _KeyPilot extends withActivationHandlers(withNavigationHandlers(EventManager)) {
    constructor() {
      super();
      if (window.__KeyPilotV22) {
        console.warn("[KeyPilot] Already loaded.");
        return;
      }
      window.__KeyPilotV22 = true;
      try {
        window.isOnMapWebsite = isOnMapWebsite;
      } catch {
      }
      window.__KeyPilotInstance = this;
      this.enabled = true;
      this.initializationComplete = false;
      this.state = new StateManager();
      this.cursor = new CursorManager();
      this.detector = new ElementDetector();
      this.activator = new ActivationHandler(this.detector);
      this.mouseCoordinateManager = new MouseCoordinateManager();
      this.focusDetector = new FocusDetector(this.state, this.mouseCoordinateManager);
      this.overlayManager = new OverlayManager();
      this.unitSelection = new UnitSelectionManager();
      this.styleManager = new StyleManager();
      this.columnLayoutManager = new ColumnLayoutManager();
      this.inspector = new InspectorModeController({
        state: this.state,
        deepElementFromPoint: (x, y) => this.detector.deepElementFromPoint(x, y),
        onBeforeEnter: () => {
          try {
            if (this.state.isHighlightMode()) this.cancelHighlightMode();
          } catch {
          }
          try {
            this._exitScrollLineMode();
          } catch {
          }
        },
        onPicksChanged: (picks, unionRect) => {
          try {
            const kind = this.inspector.getKind();
            this.overlayManager?.updateInspectorPickedOverlays?.(picks, unionRect, kind);
          } catch {
          }
        }
      });
      this.shadowDOMManager = new ShadowDOMManager(this.styleManager);
      this.floatingKeyboardHelp = null;
      this.controlStrip = null;
      this._systemKeybindings = buildSystemKeybindings(DEFAULT_KEYBOARD_HANDEDNESS);
      this._layoutKeybindings = buildKeybindingsForLayout(DEFAULT_KEYBOARD_LAYOUT_ID);
      this.keybindings = {
        ...this._layoutKeybindings,
        ...this._systemKeybindings
      };
      this._keyboardLayoutId = DEFAULT_KEYBOARD_LAYOUT_ID;
      this._keyboardUiLayout = getKeyboardUiLayoutForLayout(DEFAULT_KEYBOARD_LAYOUT_ID);
      this._currentKeyboardLayoutId = "builtin";
      this._currentUserLayout = null;
      this._currentUserMacros = [];
      this._currentUserActions = [];
      this._currentKeySlotMap = null;
      this._builtinFunctionActionParams = /* @__PURE__ */ new Map();
      this.omniboxManager = new OmniboxManager({
        onClose: () => {
          try {
            if (window === window.top) this.state.setMode(MODES.NONE);
          } catch {
          }
        }
      });
      this.tabHistoryPopover = new TabHistoryPopover({
        popupManager: this.overlayManager?.popupManager,
        onStateChange: (open) => {
          this.state.setPopoverOpen(open, open ? "tab-history" : null);
        }
      });
      this.launcherPopover = new LauncherPopover(this);
      this.topSitesPopover = new TopSitesPopover({
        popupManager: this.overlayManager?.popupManager,
        onVisibilityChange: (visible, opts) => {
          this.applyTopSitesVisibility(visible, {
            persist: true,
            applyUi: opts?.applyUi !== false
          });
        }
      });
      this.KEYBOARD_HELP_STORAGE_KEY = "keypilot_keyboard_help_visible";
      this._keyboardHelpVisible = false;
      try {
        const cached = peekChromeLayoutCache();
        if (typeof cached?.keyboardHelpVisible === "boolean") {
          this._keyboardHelpVisible = cached.keyboardHelpVisible;
        }
      } catch {
      }
      this._keyboardHelpStorageListener = null;
      this.TOP_SITES_STORAGE_KEY = "keypilot_top_sites_visible";
      this._topSitesVisible = false;
      this._topSitesStorageListener = null;
      this._isPopoverOsWindow = false;
      this._scrollLineOverlay = new ScrollLineOverlay();
      this._scrollLineRaf = 0;
      this._scrollLineLastTs = 0;
      this._scrollLineTarget = null;
      this._scrollLineDragSession = null;
      this._scrollLineOrigin = { x: 0, y: 0 };
      this._scrollHold = new ScrollHoldController({
        apply: (ctx) => this._applyScrollHoldFrame(ctx)
      });
      this._pointerBindingClaimed = false;
      this._LINK_HOVER_HINT_DEBOUNCE_MS = 90;
      this._linkHoverHintTimer = null;
      this._linkHoverHintPendingState = null;
      this._linkHoverHintLastApplied = null;
      this.intersectionManager = new IntersectionObserverManager(this.detector);
      this.scrollManager = new OptimizedScrollManager(this.overlayManager, this.state, {
        // Live-refresh text/rectangle selection overlays every scroll frame (~60fps).
        onScrollFrame: () => {
          this.refreshHighlightDuringScroll();
          try {
            this.unitSelection?.refreshOverlays?.();
          } catch {
          }
        }
      });
      this._domHoverListenersEnabled = true;
      this._panelTrackingInterval = null;
      this._lastPanelCheck = 0;
      this.rectangleIntersectionObserver = null;
      this.edgeOnlyProcessingEnabled = FEATURE_FLAGS.ENABLE_EDGE_ONLY_PROCESSING;
      this.lastQueryPosition = { x: -1, y: -1 };
      this.MOUSE_QUERY_THRESHOLD = 1;
      this._lastHighlightUpdatePos = { x: -1, y: -1 };
      this._HIGHLIGHT_UPDATE_THRESHOLD = 2;
      this._completingHighlight = false;
      this._mouseMoveRAF = 0;
      this._pendingMouse = { x: -1, y: -1, underHint: null };
      this._boundFrameBridgeMessage = null;
      this._framePointerInside = false;
      this._framePointerIframe = null;
      this._postClickRefreshToken = 0;
      this._postClickRefreshTimer = null;
      this._postClickRefreshInnerTimer = null;
      this._textModeClickArmed = false;
      this._textModeClickArmedTarget = null;
      this._textModeClickInterval = null;
      this._settings = null;
      this._settingsStorageListener = null;
      this.performanceMetrics = {
        mouseQueries: 0,
        cacheHits: 0,
        lastMetricsLog: Date.now()
      };
      this._boundScrollEndHandler = null;
      this.init();
    }
    _handleDomHoverChange(el2) {
      try {
        if (!this.enabled) return;
        const currentState = this.state.getState();
        if (currentState.mode !== MODES.NONE) return;
        let next = el2 && el2.nodeType === 1 ? el2 : null;
        try {
          if (next && (next.tagName === "HTML" || next.tagName === "BODY")) next = null;
        } catch {
        }
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] DOM hover change:", {
            element: next?.tagName,
            href: next?.href,
            currentFocusEl: currentState.focusEl?.tagName
          });
        }
        if (next !== currentState.focusEl) {
          this.state.setFocusElement(next);
        }
      } catch {
      }
    }
    _getUnderElementHintFromMouseEvent(e) {
      try {
        let under = null;
        const path = e && typeof e.composedPath === "function" ? e.composedPath() : null;
        if (Array.isArray(path)) {
          for (const n of path) {
            if (n && n.nodeType === 1) {
              under = n;
              break;
            }
          }
        }
        if (!under && e && e.target && e.target.nodeType === 1) under = e.target;
        if (!under || under.nodeType !== 1) return null;
        try {
          if (under.tagName === "HTML" || under.tagName === "BODY") return null;
        } catch {
        }
        return under;
      } catch {
        return null;
      }
    }
    _isKeyPilotUiElement(el2) {
      try {
        if (!el2 || el2 === document.documentElement || el2 === document.body) return false;
        if (isInteractiveKeyPilotOverlayElement(el2)) return false;
        if (isKeyPilotChromeElement(el2)) return true;
        let n = el2;
        let guard = 0;
        while (n && n.nodeType === 1 && guard++ < 12) {
          if (n === document.documentElement || n === document.body) break;
          const id = typeof n.id === "string" ? n.id : "";
          if (id && id.startsWith("kpv2-") && id !== "kpv2-media-lib-overlay" && id !== "kpv2-page-media-overlay") {
            return true;
          }
          const cl = n.classList;
          if (cl && cl.length) {
            for (const c of cl) {
              if (typeof c !== "string" || !c.startsWith("kpv2-")) continue;
              if (isInteractiveKeyPilotOverlayClass(c) || c === "kpv2-cursor-hidden" || c === "kpv2-focus" || c === "kpv2-delete" || c === "kpv2-highlight" || c === "kpv2-hidden" || c === "kpv2-cols" || c === "kpv2-cols-active" || c === "kpv2-cols-page" || c === "kpv2-inspector" || c === "kpv2-inspector-picked" || c.startsWith("kpv2-text-") || c.startsWith("kpv2-focus-") && !c.includes("overlay")) {
                continue;
              }
              return true;
            }
          }
          n = n.parentElement;
        }
      } catch {
      }
      return false;
    }
    /**
     * Check if an element is inside a valid popover container (iframe or popupManager modal)
     * @param {Element} el
     * @returns {boolean}
     */
    _isElementInPopover(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      if (isInteractiveKeyPilotOverlayElement(el2)) return true;
      if (isClickableKeyPilotChromeElement(el2)) return true;
      const iframeContainer = this.overlayManager?.popoverContainer;
      if (iframeContainer instanceof Element && containsComposed(iframeContainer, el2)) {
        return true;
      }
      try {
        const iframe = this.overlayManager?.popoverIframeElement;
        const childDoc = iframe?.contentDocument;
        if (childDoc && el2.ownerDocument === childDoc) return true;
      } catch {
      }
      const modalPanel = this.overlayManager?.popupManager?.top()?.panel;
      if (modalPanel instanceof Element && containsComposed(modalPanel, el2)) {
        return true;
      }
      return false;
    }
    /**
     * Same-origin hit inside the modal popover iframe (Settings / Docs / Guide).
     * Parent hit-testing only sees the <iframe> shell; inner KeyPilot does not run
     * on chrome-extension pages.
     * @param {number} clientX
     * @param {number} clientY
     * @returns {{ el: Element, clickable: Element|null }|null}
     */
    _piercePopoverIframeAt(clientX, clientY) {
      const iframe = this.overlayManager?.popoverIframeElement;
      if (!iframe || !Number.isFinite(clientX) || !Number.isFinite(clientY)) return null;
      let rect;
      try {
        rect = iframe.getBoundingClientRect();
      } catch {
        return null;
      }
      if (!rect || rect.width <= 0 || rect.height <= 0) return null;
      const localX = clientX - rect.left;
      const localY = clientY - rect.top;
      if (localX < 0 || localY < 0 || localX > rect.width || localY > rect.height) {
        return null;
      }
      let doc = null;
      try {
        doc = iframe.contentDocument;
      } catch {
        doc = null;
      }
      if (!doc) return null;
      let el2 = null;
      try {
        el2 = deepElementFromPoint(localX, localY, doc);
      } catch {
        el2 = null;
      }
      if (!el2 || el2.nodeType !== 1) return null;
      let clickable = null;
      try {
        clickable = this.detector.findClickable(el2);
      } catch {
        clickable = null;
      }
      return { el: el2, clickable };
    }
    /**
     * When DOM-hover listener mode is enabled, `state.focusEl` is driven by delegated hover events.
     * However, if the DOM changes under a stationary pointer (menus, dialogs, overlays), hover events
     * may not fire and `focusEl` can become stale. This helper validates focusEl against the actual
     * element under the cursor at activation time.
     */
    _getValidatedActivationTarget(currentState) {
      const st = currentState || this.state.getState();
      const x = st?.lastMouse?.x;
      const y = st?.lastMouse?.y;
      const focus = st?.focusEl;
      let under = null;
      try {
        if (typeof x === "number" && typeof y === "number") {
          under = this.detector.deepElementFromPoint(x, y);
          const pierced = this._piercePopoverIframeAt(x, y);
          if (pierced) under = pierced.clickable || pierced.el || under;
        }
      } catch {
      }
      try {
        if (under && this._isKeyPilotUiElement(under)) {
          const allowUi = this._isElementInPopover(under) || isClickableKeyPilotChromeElement(under) || under instanceof Element && under.getAttribute("role") === "link" && !!under.dataset?.kpUrl;
          if (!allowUi) under = null;
        }
      } catch {
      }
      let target = focus || under;
      try {
        if (this._domHoverListenersEnabled && focus && under && focus instanceof Element && under instanceof Element) {
          const consistent = containsComposed(focus, under) || containsComposed(under, focus);
          if (!consistent) {
            const clickableUnder = this.detector.findClickable(under) || under;
            target = clickableUnder;
          }
        }
      } catch {
      }
      return target;
    }
    /**
     * Emit a semantic KeyPilot action event for consumers like onboarding.
     * Consumers should prefer this over raw keydown because it reflects what KeyPilot actually did.
     */
    emitAction(action, detail = {}) {
      try {
        const payload = {
          action: String(action || ""),
          timestamp: Date.now(),
          ...detail
        };
        if (!payload.action) return;
        document.dispatchEvent(new CustomEvent("keypilot:action", { detail: payload }));
      } catch {
      }
    }
    _buildActivationDetail(target) {
      const detail = {
        isLink: false,
        href: null,
        isKeyboardHelpKey: false,
        /** True when F activated the element that had the blue/green focus outline. */
        hadFocusOutline: false,
        category: CLICKABLE_CATEGORY.NONE
      };
      try {
        const el2 = target instanceof Element ? target : null;
        if (el2 && this.detector?.getClickableCategory) {
          detail.category = this.detector.getClickableCategory(el2);
        }
        if (detail.category === CLICKABLE_CATEGORY.LINK) {
          detail.isLink = true;
          if (el2 && el2.tagName === "A" && /** @type {any} */
          el2.href) {
            detail.href = /** @type {any} */
            el2.href;
          } else if (el2 && typeof el2.closest === "function") {
            const a = el2.closest("a[href]");
            if (a && /** @type {any} */
            a.href) detail.href = /** @type {any} */
            a.href;
            if (!detail.href) {
              const linkish = (
                /** @type {HTMLElement|null} */
                el2.closest('[role="link"]')
              );
              const kpUrl = linkish?.dataset?.kpUrl ? String(linkish.dataset.kpUrl) : "";
              if (kpUrl) detail.href = kpUrl;
            }
          }
        }
      } catch {
      }
      try {
        const el2 = target instanceof Element ? target : null;
        const keyEl = el2 ? closestComposed(el2, "[data-kp-action-id]") : null;
        const inKeyboardHelp = !!(keyEl && closestComposed(keyEl, ".kp-floating-keyboard-help"));
        const editing = !!(this.floatingKeyboardHelp && typeof this.floatingKeyboardHelp.isEditMode === "function" && this.floatingKeyboardHelp.isEditMode());
        if (keyEl && inKeyboardHelp && !editing) detail.isKeyboardHelpKey = true;
      } catch {
      }
      try {
        const el2 = target instanceof Element ? target : null;
        if (el2 && closestComposed(el2, ".kp-control-strip")) {
          detail.isControlStrip = true;
        }
      } catch {
      }
      return detail;
    }
    _clearTextModeClickTimers() {
      try {
        if (this._textModeClickInterval) {
          clearInterval(this._textModeClickInterval);
        }
      } catch {
      }
      this._textModeClickInterval = null;
    }
    _disarmTextModeClick() {
      this._clearTextModeClickTimers();
      this._textModeClickArmed = false;
      this._textModeClickArmedTarget = null;
      try {
        this.overlayManager?.setHoverClickLabelText?.("F clicks");
      } catch {
      }
      try {
        this.floatingKeyboardHelp?.setTextModeActivateArmed?.(false);
      } catch {
      }
    }
    _armTextModeClick(target) {
      this._clearTextModeClickTimers();
      this._textModeClickArmed = true;
      this._textModeClickArmedTarget = target;
      let remaining = 3;
      try {
        this.overlayManager?.setHoverClickLabelText?.(`F clicks ${remaining}`);
      } catch {
      }
      try {
        this.floatingKeyboardHelp?.setTextModeActivateArmed?.(true);
      } catch {
      }
      this._textModeClickInterval = setInterval(() => {
        remaining -= 1;
        if (remaining > 0) {
          try {
            this.overlayManager?.setHoverClickLabelText?.(`F clicks ${remaining}`);
          } catch {
          }
          return;
        }
        this._disarmTextModeClick();
        try {
          this.state.setFocusElement(null);
        } catch {
        }
      }, 1e3);
    }
    _handleActivateFromTextFocus(currentState) {
      const target = currentState?.focusEl;
      if (!target) return;
      const x = currentState.lastMouse.x;
      const y = currentState.lastMouse.y;
      this._flashThenActivate(target, () => {
        if (!this.activator.handleSmartActivate(target, x, y)) {
          this.activator.smartClick(target, x, y);
        }
        this.postClickRefresh(target, x, y);
        this._disarmTextModeClick();
        this.state.setFocusElement(null);
      });
    }
    shouldImmediateRefreshAfterClick(target) {
      try {
        if (!target || !(target instanceof Element)) return false;
        const a = target.tagName === "A" ? target : typeof target.closest === "function" ? target.closest("a") : null;
        if (!a || a.tagName !== "A") return false;
        const rawHref = a.getAttribute("href");
        if (!rawHref) return true;
        return rawHref.trim().startsWith("#");
      } catch {
        return false;
      }
    }
    postClickRefresh(target, x, y) {
      if (!this.enabled) return;
      if (typeof x !== "number" || typeof y !== "number") return;
      if (x < 0 || y < 0) return;
      if (this.shouldImmediateRefreshAfterClick(target)) {
        const doc = document;
        const token = ++this._postClickRefreshToken;
        try {
          if (this._postClickRefreshTimer) clearTimeout(this._postClickRefreshTimer);
        } catch {
        }
        try {
          if (this._postClickRefreshInnerTimer) clearTimeout(this._postClickRefreshInnerTimer);
        } catch {
        }
        try {
          this.overlayManager?.hideFocusOverlay?.();
        } catch {
        }
        this._postClickRefreshTimer = window.setTimeout(() => {
          if (!this.enabled) return;
          if (token !== this._postClickRefreshToken) return;
          if (document !== doc) return;
          if (!document?.body) return;
          this.updateElementsUnderCursor(x, y);
          this.state.setState({ _overlayUpdateTrigger: Date.now() });
        }, 250);
        return;
      }
      this.schedulePostClickRefresh(x, y);
    }
    schedulePostClickRefresh(x, y) {
      if (!this.enabled) return;
      if (typeof x !== "number" || typeof y !== "number") return;
      if (x < 0 || y < 0) return;
      const doc = document;
      const token = ++this._postClickRefreshToken;
      try {
        if (this._postClickRefreshTimer) clearTimeout(this._postClickRefreshTimer);
      } catch {
      }
      try {
        if (this._postClickRefreshInnerTimer) clearTimeout(this._postClickRefreshInnerTimer);
      } catch {
      }
      this._postClickRefreshTimer = window.setTimeout(() => {
        if (!this.enabled) return;
        if (token !== this._postClickRefreshToken) return;
        if (document !== doc) return;
        if (!document?.body) return;
        this.overlayManager?.fadeOutFocusOverlay?.(120);
        this._postClickRefreshInnerTimer = window.setTimeout(() => {
          if (!this.enabled) return;
          if (token !== this._postClickRefreshToken) return;
          if (document !== doc) return;
          if (!document?.body) return;
          this.updateElementsUnderCursor(x, y);
          this.state.setState({ _overlayUpdateTrigger: Date.now() });
        }, 130);
      }, 250);
    }
    async init() {
      await startKeyPilotDebugFromSettings();
      this.setupStyles();
      this.setupShadowDOMSupport();
      try {
        if (window === window.top) {
          const popWin = await queryPopoverWindowInfo();
          if (popWin?.isPopoverWindow) {
            this._isPopoverOsWindow = true;
            await installPopoverWindowChrome(popWin);
          }
        }
      } catch (e) {
        console.warn("[KeyPilot] Popover window chrome install failed:", e?.message || e);
      }
      await this.queryInitialState();
      await this.refreshSettingsFromStorage();
      this.installSettingsSync();
      try {
        this.styleManager?.setCursorOverridesEnabled?.(this._isCustomCursorModeEnabled());
      } catch {
      }
      if (this.enabled) {
        if (this._isCustomCursorModeEnabled()) {
          this.cursor.ensure();
          try {
            const st = this.state?.getState?.() || {};
            const mode = this._cursorModeForState(st);
            this.cursor.setMode(mode, this._buildCursorOptionsForState({ ...st, mode: st.mode }));
          } catch {
          }
        } else {
          try {
            this.cursor.cleanup();
          } catch {
          }
        }
        await this.initializeEnabledState();
      } else {
        this.initializeDisabledState();
      }
      this.state.subscribe((newState, prevState) => {
        this.handleStateChange(newState, prevState);
      });
      this.setupPopupCommunication();
      this.setupOptimizedEventListeners();
      this._signalEarlyInjectHandoff();
      await this.setupKeyboardHelpSync();
      await this.setupTopSitesSync();
      this.setupControlStrip();
      this.applyControlStripFromSettings();
      await this.initializeCursorPosition();
      this.initializationComplete = true;
      this.state.setState({ isInitialized: true });
    }
    async refreshSettingsFromStorage() {
      const prevCursorMode = this._settings?.cursorMode;
      try {
        this._settings = await getSettings();
      } catch {
        this._settings = { ...DEFAULT_SETTINGS };
      }
      try {
        applyDebugSetting(!!this._settings?.debugLogging);
      } catch {
      }
      try {
        await this._syncClickDefaultsForTheme();
      } catch {
      }
      try {
        this._applyThemeFromSettings();
      } catch {
      }
      try {
        this._applyKeyboardLayoutFromSettings();
      } catch {
      }
      try {
        await this._refreshCurrentKeyboardLayoutFromSettings();
      } catch {
      }
      try {
        void this._refreshBuiltinFunctionActionParams();
      } catch {
      }
      const cursorEnabled = this._isCustomCursorModeEnabled();
      try {
        this.styleManager?.setCursorOverridesEnabled?.(cursorEnabled);
      } catch {
      }
      try {
        this.overlayManager?.setModeSettings?.(this._settings);
      } catch {
      }
      try {
        const px = Number(this._settings?.textMode?.strokeThickness);
        if (Number.isFinite(px)) {
          document.documentElement.style.setProperty("--kpv2-text-stroke-width", `${px}px`);
        } else {
          document.documentElement.style.removeProperty("--kpv2-text-stroke-width");
        }
      } catch {
      }
      try {
        const edgePx = Number(this._settings?.textMode?.leftEdgeWidth);
        if (Number.isFinite(edgePx)) {
          document.documentElement.style.setProperty("--kpv2-text-left-edge-width", `${edgePx}px`);
        } else {
          document.documentElement.style.removeProperty("--kpv2-text-left-edge-width");
        }
      } catch {
      }
      if (cursorEnabled) {
        try {
          this.cursor.ensure();
        } catch {
        }
        try {
          this.cursor.currentMode = null;
          this.cursor.currentModeKey = null;
          const st = this.state?.getState?.();
          if (st && st.mode) {
            this.cursor.setMode(this._cursorModeForState(st), this._buildCursorOptionsForState(st));
          }
        } catch {
        }
      } else {
        try {
          if (prevCursorMode === CURSOR_MODE.CUSTOM_CURSORS) {
            this.cursor.cleanup();
          } else {
            this.cursor.cleanup();
          }
        } catch {
        }
      }
      try {
        this.state?.setState?.({ _overlayUpdateTrigger: Date.now() });
      } catch {
      }
      try {
        this._keyboardHelpVisible = await this.getKeyboardHelpVisibleFromStorage();
      } catch {
      }
      try {
        this.applyControlStripFromSettings();
      } catch {
      }
      try {
        this.floatingKeyboardHelp?.setCollapsed?.(
          !!this._settings?.keyboardReferenceCollapsed,
          { persist: false }
        );
      } catch {
      }
      try {
        this._linkHoverHintLastApplied = null;
        this._flushKeyboardLinkHoverHints();
      } catch {
      }
      try {
        void this.refreshTopSitesVisibilityFromStorage();
      } catch {
      }
    }
    /**
     * Create the control strip host (top frame only). Safe to call multiple times.
     * Kept alive while KeyPilot is disabled so On/Off can re-enable the extension.
     */
    setupControlStrip() {
      if (window !== window.top) return;
      if (this.controlStrip) {
        this._wireControlStripHandlers();
        this._syncControlStripEnabledFromRuntime();
        return;
      }
      try {
        const ControlStripClass = ControlStrip || window.ControlStrip;
        if (!ControlStripClass) return;
        this.controlStrip = new ControlStripClass();
        this._wireControlStripHandlers();
        this.controlStrip.setKeyboardHelpActive(!!this._keyboardHelpVisible);
        this._syncControlStripEnabledFromRuntime();
      } catch (e) {
        console.warn("[KeyPilot] Failed to create control strip:", e);
        this.controlStrip = null;
      }
    }
    /**
     * Prefer toggle-handler state (global source of truth), then KeyPilot.enabled.
     */
    _syncControlStripTextModeFromState() {
      if (!this.controlStrip || typeof this.controlStrip.setTextModeActive !== "function") return;
      try {
        const mode = this.state?.getState?.()?.mode;
        this.controlStrip.setTextModeActive(mode === MODES.TEXT_FOCUS);
      } catch {
      }
    }
    _syncControlStripEnabledFromRuntime() {
      if (!this.controlStrip) return;
      let on = !!this.enabled;
      try {
        const th = window.__KeyPilotToggleHandler;
        if (th && typeof th.isEnabled === "function") {
          on = !!th.isEnabled();
        } else if (th && typeof th.enabled === "boolean") {
          on = !!th.enabled;
        }
      } catch {
      }
      try {
        this.controlStrip.setEnabledState(on);
      } catch {
      }
      this._syncControlStripTextModeFromState();
    }
    _wireControlStripHandlers() {
      if (!this.controlStrip || typeof this.controlStrip.setHandlers !== "function") return;
      this.controlStrip.setHandlers({
        onToggleEnabled: () => {
          this._sendRuntimeMessage({ type: MSG.TOGGLE_STATE }, { silent: true });
        },
        onToggleKeyboard: () => {
          if (!this.enabled) return;
          const next = !this._isKeyboardHelpPaintedVisible();
          this.applyKeyboardHelpVisibility(next, { persist: true });
        },
        onOpenSettings: () => {
          if (!this.enabled) return;
          try {
            this.handleOpenSettingsPopover();
          } catch (e) {
            console.warn("[KeyPilot] Control strip open settings failed:", e);
          }
        },
        onCollapseChange: (collapsed) => {
          void setSettings({ controlStrip: { collapsed: !!collapsed } });
        },
        onClose: () => {
          void setSettings({ controlStrip: { visible: false } });
        }
      });
    }
    /**
     * Apply control strip visibility + collapsed from settings (or defaults).
     */
    applyControlStripFromSettings() {
      if (window !== window.top) return;
      this.setupControlStrip();
      if (!this.controlStrip) return;
      const cs = this._settings?.controlStrip || DEFAULT_SETTINGS.controlStrip || {};
      const visible = cs.visible !== false;
      const collapsed = !!cs.collapsed;
      this._syncControlStripEnabledFromRuntime();
      this.controlStrip.setKeyboardHelpActive(!!this._keyboardHelpVisible);
      this._syncControlStripTextModeFromState();
      this.controlStrip.setCollapsed(collapsed, { notify: false });
      this.controlStrip.setVisible(visible);
    }
    /**
     * Toggle the control strip (Alt+J).
     * When showing: persists visible=true and keeps the existing collapsed state.
     * When hiding: persists visible=false (same as the strip close button).
     */
    async toggleControlStripFromHotkey() {
      if (window !== window.top) return;
      this.setupControlStrip();
      let currentlyVisible = false;
      try {
        if (this.controlStrip && typeof this.controlStrip.isVisible === "function") {
          currentlyVisible = !!this.controlStrip.isVisible();
        } else {
          currentlyVisible = this._settings?.controlStrip?.visible !== false;
        }
      } catch {
        currentlyVisible = this._settings?.controlStrip?.visible !== false;
      }
      const patch = { visible: !currentlyVisible };
      try {
        await setSettings({ controlStrip: patch });
      } catch {
      }
      try {
        if (this._settings) {
          this._settings.controlStrip = {
            ...this._settings.controlStrip || DEFAULT_SETTINGS.controlStrip,
            ...patch
          };
        }
      } catch {
      }
      this.applyControlStripFromSettings();
    }
    /** @deprecated Use toggleControlStripFromHotkey — kept for older call sites. */
    async showControlStripFromHotkey() {
      return this.toggleControlStripFromHotkey();
    }
    _applyKeyboardLayoutFromSettings() {
      const handedness = normalizeKeyboardHandedness(this._settings?.keyboardHandedness);
      const layoutId = resolveKeyboardLayoutId({
        familyId: this._settings?.keyboardLayoutFamilyId,
        handedness
      });
      this._keyboardLayoutId = layoutId;
      this._layoutKeybindings = buildKeybindingsForLayout(layoutId);
      this._systemKeybindings = buildSystemKeybindings(handedness);
      this.keybindings = {
        ...this._layoutKeybindings,
        ...this._systemKeybindings
      };
      const showNumberRow = !!this._settings?.keyboardReferenceShowNumberRow;
      this._keyboardUiLayout = getKeyboardUiLayoutForLayout(layoutId, { includeNumberRow: showNumberRow });
      try {
        this._applyTextInputHintLabels();
      } catch {
      }
      try {
        if (this.floatingKeyboardHelp) {
          if (typeof this.floatingKeyboardHelp.setLayoutChrome === "function") {
            this.floatingKeyboardHelp.setLayoutChrome({
              keybindings: this.keybindings,
              keyboardLayout: this._keyboardUiLayout,
              layoutId: this._keyboardLayoutId
            });
          } else {
            this.floatingKeyboardHelp.setKeybindings(this.keybindings);
            if (typeof this.floatingKeyboardHelp.setKeyboardLayout === "function") {
              this.floatingKeyboardHelp.setKeyboardLayout({
                keyboardLayout: this._keyboardUiLayout,
                layoutId: this._keyboardLayoutId
              });
            }
          }
        }
      } catch {
      }
    }
    /**
     * Change handedness. For a current custom layout this moves each assignment
     * to its paired physical key and changes its built-in visual base, preserving
     * every customization instead of leaving a right-handed copy unchanged.
     * @param {'left'|'right'|string} handedness
     */
    async setKeyboardHandedness(handedness) {
      const next = normalizeKeyboardHandedness(handedness);
      const prev = normalizeKeyboardHandedness(this._settings?.keyboardHandedness);
      if (next === prev) return;
      let updatedUserLayout = null;
      if (String(this._currentKeyboardLayoutId || "").startsWith("user:") && this._currentUserLayout) {
        try {
          updatedUserLayout = await setUserKeyboardLayoutHandedness(this._currentUserLayout, next);
        } catch {
        }
      }
      try {
        await setSettings({ keyboardHandedness: next });
      } catch {
      }
      if (this._settings) this._settings.keyboardHandedness = next;
      this._applyKeyboardLayoutFromSettings();
      if (updatedUserLayout) {
        await this.applyLiveUserLayout(updatedUserLayout, {
          macros: this._currentUserMacros,
          actions: this._currentUserActions
        });
      }
    }
    /** Built-in Function ids read via {@link _getBuiltinFunctionActionParams} in hot key handlers. */
    static get BUILTIN_FUNCTION_ACTION_IDS() {
      return [...FIXED_KEY_FUNCTION_IDS, ...UNIT_SELECT_FUNCTION_IDS];
    }
    /**
     * Load/refresh the cached parameters for built-in Functions still bound to a fixed physical
     * key (see the `_builtinFunctionActionParams` field comment in the constructor). A plain cache
     * refill rather than a live subscription because chrome.storage reads are async and these
     * Functions' handlers (`handleRectangleHighlightKey`, `handleSendTextToAiKey`) run synchronously
     * relative to the keydown event — the cache is what lets them read a value without awaiting.
     */
    async _refreshBuiltinFunctionActionParams() {
      for (const functionId of _KeyPilot.BUILTIN_FUNCTION_ACTION_IDS) {
        try {
          const action = await getOrCreateBuiltinFunctionUserAction(functionId);
          this._builtinFunctionActionParams.set(functionId, action?.parameters || {});
        } catch {
          this._builtinFunctionActionParams.set(functionId, {});
        }
      }
    }
    /**
     * @param {string} functionId
     * @returns {Record<string, any>}
     */
    _getBuiltinFunctionActionParams(functionId) {
      return this._builtinFunctionActionParams.get(functionId) || {};
    }
    async _refreshCurrentKeyboardLayoutFromSettings() {
      let sel = String(this._settings?.currentKeyboardLayoutId || "builtin");
      this._currentKeyboardLayoutId = sel;
      this._currentUserLayout = null;
      this._currentUserMacros = [];
      this._currentUserActions = [];
      this._currentKeySlotMap = null;
      if (sel && sel.startsWith("user:")) {
        const id = sel.slice("user:".length);
        const layout = await getUserKeyboardLayoutById(id);
        if (layout) {
          this._currentUserLayout = layout;
          try {
            this._currentUserMacros = await listUserMacros();
          } catch {
            this._currentUserMacros = [];
          }
          try {
            this._currentUserActions = await listUserActions();
          } catch {
            this._currentUserActions = [];
          }
          this._currentKeySlotMap = layout.slots && typeof layout.slots === "object" ? layout.slots : {};
        } else {
          sel = "builtin";
          this._currentKeyboardLayoutId = "builtin";
          try {
            if (this._settings) this._settings.currentKeyboardLayoutId = "builtin";
            await setSettings({ currentKeyboardLayoutId: "builtin" });
          } catch {
          }
        }
      }
      try {
        if (this.floatingKeyboardHelp && typeof this.floatingKeyboardHelp.setActiveLayoutSelection === "function") {
          this.floatingKeyboardHelp.setActiveLayoutSelection({
            currentKeyboardLayoutId: this._currentKeyboardLayoutId,
            userLayout: this._currentUserLayout,
            userMacros: this._currentUserMacros,
            userActions: this._currentUserActions
          });
        }
      } catch {
      }
    }
    /**
     * Push a just-saved user layout into live dispatch + Keyboard Reference immediately
     * (no page refresh). Used after place / DnD / Config CRUD.
     * @param {any} layout
     * @param {{ setAsCurrent?: boolean, macros?: any[], actions?: any[] }} [opts]
     */
    async applyLiveUserLayout(layout, opts = {}) {
      if (!layout || !layout.id) return;
      const sel = `user:${layout.id}`;
      const isCurrent = !!opts.setAsCurrent || String(this._currentKeyboardLayoutId || "") === sel;
      if (opts.setAsCurrent) {
        this._currentKeyboardLayoutId = sel;
        if (this._settings) this._settings.currentKeyboardLayoutId = sel;
        try {
          await setSettings({ currentKeyboardLayoutId: sel });
        } catch {
        }
      }
      if (isCurrent) {
        this._currentUserLayout = layout;
        this._currentKeySlotMap = layout.slots && typeof layout.slots === "object" ? layout.slots : {};
        if (Array.isArray(opts.macros)) this._currentUserMacros = opts.macros;
        if (Array.isArray(opts.actions)) this._currentUserActions = opts.actions;
      }
      try {
        this.floatingKeyboardHelp?.setActiveLayoutSelection?.({
          currentKeyboardLayoutId: this._currentKeyboardLayoutId || (isCurrent ? sel : "builtin"),
          userLayout: this._currentUserLayout,
          userMacros: this._currentUserMacros,
          userActions: this._currentUserActions
        });
      } catch {
      }
    }
    _maybeHandleCurrentLayoutBinding(e) {
      try {
        const sel = String(this._currentKeyboardLayoutId || "");
        if (!sel || !sel.startsWith("user:")) return false;
        const slots = this._currentKeySlotMap;
        if (!slots || typeof slots !== "object") return false;
        const key = e && typeof e.key === "string" ? e.key : "";
        if (!key || key === " ") return false;
        const raw = String(key).trim();
        const slot = raw.length === 1 ? raw.toUpperCase() : raw;
        if (!slot) return false;
        const assigned = slots[slot] || slots[raw.toUpperCase()];
        if (!assigned || !assigned.type || !assigned.id) return false;
        if (assigned.type === "macro") {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          void this._runMacroById(String(assigned.id), e);
          return true;
        }
        if (assigned.type === "function") {
          return this._dispatchFunctionSlot(String(assigned.id), e);
        }
      } catch {
      }
      return false;
    }
    /**
     * @param {string} id Bare Function id or Action Instance id ("action:<uuid>").
     * @param {KeyboardEvent} e
     * @returns {boolean}
     */
    _dispatchFunctionSlot(id, e) {
      let functionId = id;
      let parameters;
      let instanceId;
      if (id.startsWith("action:")) {
        const instance = (this._currentUserActions || []).find((a) => a && a.id === id);
        if (!instance) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          void this._dispatchFunctionSlotAsync(id, e);
          return true;
        }
        functionId = instance.functionId;
        parameters = instance.parameters;
        instanceId = id;
      }
      const def = getFunctionDef(functionId);
      const handlerName = def && def.handler ? String(def.handler) : "";
      const fn = handlerName ? this[handlerName] : null;
      if (typeof fn !== "function") return false;
      e.preventDefault();
      e.stopPropagation();
      e.stopImmediatePropagation();
      try {
        const ret = fn.call(this, e, parameters, { functionId, instanceId });
        if (ret && typeof ret.then === "function") {
          void ret.catch((err) => {
            console.warn("[KeyPilot] Function handler failed:", handlerName, err);
          });
        }
      } catch (err) {
        console.warn("[KeyPilot] Function handler threw:", handlerName, err);
      }
      return true;
    }
    /**
     * Modifier-chord slot dispatch for Functions that must run while a text field is focused
     * (`worksWhileTyping`, e.g. TYPE_CHARACTERS, CLIPBOARD_*). Deliberately does NOT call
     * `_isUnsafeToRunActionKey` — running while typing is the entire point of these Functions.
     * Must be called *before* the blanket `hasModifierKeys(e)` early-return in `handleKeyDown`,
     * since that early-return would otherwise ignore every modifier combo unconditionally.
     * See KEY_ACTION_ARCHITECTURE.md "Text-active Functions & modifier-chord assignment".
     * @param {KeyboardEvent} e
     * @returns {boolean} true if this chord was claimed and dispatched.
     */
    _maybeHandleTextActiveFunctionSlot(e) {
      try {
        if (!this.hasModifierKeys(e)) return false;
        const sel = String(this._currentKeyboardLayoutId || "");
        if (!sel.startsWith("user:")) return false;
        const slots = this._currentKeySlotMap;
        if (!slots || typeof slots !== "object") return false;
        const chordKey = chordSlotKeyFromEvent(e);
        if (!chordKey) return false;
        const assigned = slots[chordKey];
        if (!assigned || assigned.type !== "function" || !assigned.id) return false;
        const id = String(assigned.id);
        if (id.startsWith("action:")) {
          const instance = (this._currentUserActions || []).find((a) => a && a.id === id);
          if (instance) {
            if (!functionWorksWhileTyping(instance.functionId)) return false;
            return this._dispatchFunctionSlot(id, e);
          }
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          void this._dispatchTextActiveFunctionSlotAsync(id, e);
          return true;
        }
        if (!functionWorksWhileTyping(id)) return false;
        return this._dispatchFunctionSlot(id, e);
      } catch {
        return false;
      }
    }
    /**
     * Async counterpart of {@link _maybeHandleTextActiveFunctionSlot} for not-yet-cached Action
     * Instances. Re-checks `worksWhileTyping` after resolving the instance so this never dispatches
     * (and therefore never bypasses the typing-safety gate for) a non-text-active Function.
     * @param {string} instanceId
     * @param {KeyboardEvent} e
     */
    async _dispatchTextActiveFunctionSlotAsync(instanceId, e) {
      try {
        const instance = await getUserActionById(instanceId);
        if (!instance || !functionWorksWhileTyping(instance.functionId)) return;
        const def = getFunctionDef(instance.functionId);
        const handlerName = def && def.handler ? String(def.handler) : "";
        const fn = handlerName ? this[handlerName] : null;
        if (typeof fn !== "function") return;
        fn.call(this, e, instance.parameters, { functionId: instance.functionId, instanceId });
      } catch {
      }
    }
    /**
     * @param {string} instanceId
     * @param {KeyboardEvent} e
     */
    async _dispatchFunctionSlotAsync(instanceId, e) {
      try {
        const instance = await getUserActionById(instanceId);
        if (!instance) return;
        const def = getFunctionDef(instance.functionId);
        const handlerName = def && def.handler ? String(def.handler) : "";
        const fn = handlerName ? this[handlerName] : null;
        if (typeof fn !== "function") return;
        fn.call(this, e, instance.parameters, { functionId: instance.functionId, instanceId });
      } catch {
      }
    }
    /**
     * Always-on system keybinding layer (Esc, KB Reference, Settings).
     * Independent of the selected layout family / user layout.
     * Alt+ chrome hotkeys (Alt+K, Alt+C, …) are handled earlier in handleKeyDown.
     * @param {KeyboardEvent} e
     * @returns {boolean}
     */
    _maybeHandleSystemLayerBinding(e) {
      try {
        const systemKb = this._systemKeybindings && typeof this._systemKeybindings === "object" ? this._systemKeybindings : buildSystemKeybindings(this._settings?.keyboardHandedness);
        for (const keybinding of Object.values(systemKb || {})) {
          if (!keybinding?.handler || !Array.isArray(keybinding.keys)) continue;
          const matchOn = Array.isArray(keybinding.matchOn) ? keybinding.matchOn : ["key"];
          const isMatch = matchOn.some((field) => keybinding.keys.includes(e[field]));
          if (!isMatch) continue;
          if (this._isUnsafeToRunActionKey(e)) return false;
          const handlerFn = this[keybinding.handler];
          if (typeof handlerFn !== "function") return false;
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          handlerFn.call(this, e);
          return true;
        }
      } catch {
      }
      return false;
    }
    /**
     * Run a Macro's Steps in order. Function steps resolve through the Function Library;
     * Logic steps (wait / gate / stop / runMacro) are handled here — see KEY_ACTION_ARCHITECTURE.md.
     * A Function Step's handler is called with the *originating* keydown event for every Step
     * (there is only one real keyboard event per macro run).
     * @param {string} macroId
     * @param {KeyboardEvent} [e]
     * @param {string[]} [_callStack] Nested-run cycle guard (internal).
     */
    async _runMacroById(macroId, e, _callStack = []) {
      try {
        const id = String(macroId || "");
        if (!id) return;
        if (Array.isArray(_callStack) && _callStack.includes(id)) {
          console.warn("[KeyPilot] Macro cycle detected:", [..._callStack, id].join(" \u2192 "));
          this.overlayManager?.showNotification?.("Macro cycle detected \u2014 stopped.", "error");
          return;
        }
        let macro = resolveMacroById(id, this._currentUserMacros);
        if (!macro) {
          try {
            const user = await getUserMacroById(id);
            if (user) {
              macro = { id: user.id, label: String(user.label || "Macro"), steps: user.steps || [], stock: false };
            } else {
              const stock = getStockMacroById(id);
              if (stock) {
                macro = { id: stock.id, label: stock.label, steps: stock.steps.map((s) => ({ ...s })), stock: true };
              }
            }
          } catch {
            macro = null;
          }
        }
        if (!macro) return;
        const steps = Array.isArray(macro.steps) ? macro.steps : [];
        if (steps.length === 0) {
          this.overlayManager?.showNotification?.(
            `Macro "${macro.label}" has no steps yet \u2014 add some via Keyboard Layout Editor.`,
            "info"
          );
          return;
        }
        const stack = [...Array.isArray(_callStack) ? _callStack : [], id];
        let priorResult = void 0;
        let i = 0;
        while (i < steps.length) {
          const step = steps[i];
          const kind = step?.kind || (step?.functionId ? "function" : "");
          if (kind === "wait") {
            const ms = Math.max(0, Number(step.ms) || 0);
            if (ms > 0) await new Promise((resolve) => setTimeout(resolve, ms));
            i += 1;
            continue;
          }
          if (kind === "stop") break;
          if (kind === "gate") {
            const ok = this._evalMacroGate(step, priorResult);
            if (!ok) {
              i += 1 + Math.max(0, Math.floor(Number(step.thenSkip) || 0));
              continue;
            }
            i += 1;
            continue;
          }
          if (kind === "runMacro") {
            const nestedId = String(step.macroId || "");
            if (nestedId) await this._runMacroById(nestedId, e, stack);
            i += 1;
            continue;
          }
          priorResult = await this._runMacroStep(step, e, id, priorResult);
          i += 1;
        }
      } catch (err) {
        console.warn("[KeyPilot] Macro execution failed:", err);
      }
    }
    /**
     * Evaluate a Gate Logic step against the prior Function step's return value.
     * @param {{ op?: string, left?: string, leftKey?: string, right?: any }} step
     * @param {any} priorResult
     * @returns {boolean}
     */
    _evalMacroGate(step, priorResult) {
      const op = String(step?.op || "truthy");
      let left = priorResult;
      const leftSrc = String(step?.left || "prior");
      if (leftSrc === "prior") {
        left = priorResult;
        if (step?.leftKey && priorResult && typeof priorResult === "object") {
          left = priorResult[String(step.leftKey)];
        }
      } else {
        left = leftSrc;
      }
      const right = step?.right;
      switch (op) {
        case "falsy":
          return !left;
        case "eq":
          return left == right;
        // eslint-disable-line eqeqeq
        case "neq":
          return left != right;
        // eslint-disable-line eqeqeq
        case "gt":
          return Number(left) > Number(right);
        case "lt":
          return Number(left) < Number(right);
        case "truthy":
        default:
          return !!left;
      }
    }
    /**
     * Run a single Function Macro Step. Failures are logged and swallowed so one bad Step
     * doesn't abort the rest of the Macro. Returns the handler result (for Gate steps).
     * @param {import('./modules/keyboard-layout-store.js').MacroStep} step
     * @param {KeyboardEvent} [e]
     * @param {string} [macroId]
     * @param {any} [priorResult]
     * @returns {Promise<any>}
     */
    async _runMacroStep(step, e, macroId, priorResult) {
      const functionId = step?.functionId;
      if (!functionId) return void 0;
      const def = getFunctionDef(functionId);
      const handlerName = def && def.handler ? String(def.handler) : "";
      const fn = handlerName ? this[handlerName] : null;
      if (typeof fn !== "function") {
        console.warn("[KeyPilot] Macro step references unknown Function handler:", functionId);
        return void 0;
      }
      const delay = Number(step?.delayMsBefore);
      if (Number.isFinite(delay) && delay > 0) {
        await new Promise((resolve) => setTimeout(resolve, delay));
      }
      try {
        return await fn.call(this, e, step.parameters, { functionId, macroId, priorResult });
      } catch (err) {
        console.warn("[KeyPilot] Macro step failed:", functionId, err);
        return void 0;
      }
    }
    /**
     * @param {number} direction -1 for prev, +1 for next
     */
    _cycleKeyboardLayoutFamily(direction) {
      try {
        const families = getInstalledKeyboardLayoutFamilyIds();
        if (!Array.isArray(families) || families.length <= 1) return;
        const currentSettings = this._settings || DEFAULT_SETTINGS;
        const currentFamily = normalizeKeyboardLayoutFamilyId(currentSettings?.keyboardLayoutFamilyId);
        const idx = families.indexOf(currentFamily);
        const start = idx >= 0 ? idx : 0;
        const nextIdx = (start + (direction < 0 ? -1 : 1) + families.length) % families.length;
        const nextFamily = families[nextIdx];
        this._settings = { ...currentSettings, keyboardLayoutFamilyId: nextFamily };
        this._applyKeyboardLayoutFromSettings();
        setSettings({ keyboardLayoutFamilyId: nextFamily }).catch(() => {
        });
      } catch {
      }
    }
    _toggleKeyboardLayoutConfigurator() {
      toggleKeyboardLayoutConfigurator(this);
    }
    /**
     * Paint layout-aware "F to select" / "Esc to exit" labels
     * (vertical sidecars left of text fields).
     */
    _applyTextInputHintLabels() {
      const KB = this.keybindings || {};
      const activate = KB.ACTIVATE?.keyLabel || KB.ACTIVATE?.displayKey || (Array.isArray(KB.ACTIVATE?.keys) ? KB.ACTIVATE.keys[0] : null) || "F";
      const cancel = KB.CANCEL?.keyLabel || KB.CANCEL?.displayKey || "Esc";
      this.styleManager?.setTextInputHintLabels?.({
        hover: `${activate} to select`,
        focus: `press ${cancel} to exit`
      });
      this.overlayManager?.setTextFocusEscKeyLabel?.(cancel);
      this.overlayManager?.setTextHoverActivateKeyLabel?.(activate);
    }
    _applyThemeFromSettings() {
      const theme = resolveThemeFromSettings(this._settings || DEFAULT_SETTINGS);
      applyThemeToRoots(theme, { roots: [document], hosts: [document.documentElement] });
      try {
        document.querySelectorAll(".kp-onboarding-panel, .kp-layout-config-panel").forEach((el2) => {
          applyOnboardingSurface(el2, theme);
        });
      } catch {
      }
    }
    /**
     * When the active theme has no matching clickMode snapshot, adopt that theme's
     * Click Mode / cursor defaults so Settings and hover chrome stay in sync.
     */
    async _syncClickDefaultsForTheme() {
      const s = this._settings || DEFAULT_SETTINGS;
      const themeId = s.themeId || DEFAULT_SETTINGS.themeId;
      if (s.clickModeThemeId === themeId) return;
      const theme = resolveThemeFromSettings(s);
      const patch = getThemeClickDefaults(theme);
      const next = await setSettings({
        cursorMode: patch.cursorMode,
        clickMode: patch.clickMode,
        clickModeThemeId: themeId
      });
      this._settings = next;
    }
    installSettingsSync() {
      if (this._settingsStorageListener) return;
      try {
        this._settingsStorageListener = (changes, area) => {
          if (area !== "sync" && area !== "local") return;
          const entry = changes?.[SETTINGS_STORAGE_KEY];
          if (!entry) return;
          const raw = entry.newValue;
          if (raw && typeof raw === "object") {
            try {
              this._settings = { ...this._settings || DEFAULT_SETTINGS, ...raw };
              this._applyThemeFromSettings();
            } catch {
            }
          }
          this.refreshSettingsFromStorage();
        };
        chrome.storage.onChanged.addListener(this._settingsStorageListener);
      } catch {
      }
      try {
        if (!this._actionSettingsDomListener) {
          this._actionSettingsDomListener = (ev) => {
            try {
              const functionId = ev?.detail?.actionId;
              const action = ev?.detail?.action;
              if (functionId && action && typeof action === "object") {
                this._builtinFunctionActionParams.set(functionId, action.parameters || {});
              }
            } catch {
            }
          };
          document.addEventListener("keypilot:action-settings-changed", this._actionSettingsDomListener);
        }
      } catch {
      }
    }
    /**
     * Resolve cursor glyph mode from state.
     * Inspector uses kind-specific cursors (delete / cols / …).
     * @param {any} state
     * @returns {string}
     */
    _cursorModeForState(state) {
      const mode = state?.mode;
      if (mode === MODES.INSPECTOR) {
        return getInspectorCursorMode(state?.inspectorKind);
      }
      if (mode === MODES.DELETE || mode === "delete") return "delete";
      if (mode === MODES.COLS || mode === "cols") return "cols";
      if (mode === MODES.SCROLL_LINE) return MODES.NONE;
      return mode || MODES.NONE;
    }
    _buildCursorOptionsForState(state) {
      const s = this._settings || DEFAULT_SETTINGS;
      const mode = state?.mode;
      const hasClickableElement = !!state?.focusEl;
      if (mode === MODES.TEXT_FOCUS) {
        const type2 = s?.textMode?.cursorType || DEFAULT_SETTINGS.textMode.cursorType;
        if (type2 === "crosshair") {
          return { hasClickableElement };
        }
        return { cursorType: "t_square", hasClickableElement };
      }
      if (mode === MODES.NONE || mode === MODES.POPOVER || mode === MODES.SCROLL_LINE) {
        const type2 = s?.clickMode?.cursor?.type || DEFAULT_SETTINGS.clickMode.cursor.type;
        if (type2 === "native_arrow") return { cursorType: "native_arrow" };
        if (type2 === "native_pointer") return { cursorType: "native_pointer" };
        const lineWidth = Number(s?.clickMode?.cursor?.lineWidth);
        const sizePixels = Number(s?.clickMode?.cursor?.sizePixels);
        const gap = Number(s?.clickMode?.cursor?.gap);
        return {
          strokeWidth: Number.isFinite(lineWidth) ? lineWidth : DEFAULT_SETTINGS.clickMode.cursor.lineWidth,
          crossHairQuadrantWidth: Number.isFinite(sizePixels) ? sizePixels : DEFAULT_SETTINGS.clickMode.cursor.sizePixels,
          gap: Number.isFinite(gap) ? gap : DEFAULT_SETTINGS.clickMode.cursor.gap
        };
      }
      return mode === MODES.HIGHLIGHT ? { strokeWidth: 4, hasClickableElement: false } : {};
    }
    async setupKeyboardHelpSync() {
      if (this._keyboardHelpStorageListener) return;
      if (!chrome?.storage) return;
      this._keyboardHelpStorageListener = (changes, areaName) => {
        if (!changes || areaName !== "sync" && areaName !== "local") return;
        if (!Object.prototype.hasOwnProperty.call(changes, this.KEYBOARD_HELP_STORAGE_KEY)) return;
        const raw = changes[this.KEYBOARD_HELP_STORAGE_KEY]?.newValue;
        if (typeof raw !== "boolean") return;
        this.applyKeyboardHelpVisibility(raw, { persist: false });
      };
      try {
        chrome.storage.onChanged.addListener(this._keyboardHelpStorageListener);
      } catch {
      }
      const initial = await this.getKeyboardHelpVisibleFromStorage();
      this.applyKeyboardHelpVisibility(initial, { persist: false });
    }
    async setupTopSitesSync() {
      if (this._topSitesStorageListener) return;
      if (!chrome?.storage) return;
      this._topSitesStorageListener = (changes, areaName) => {
        if (!changes || areaName !== "sync" && areaName !== "local") return;
        if (!Object.prototype.hasOwnProperty.call(changes, this.TOP_SITES_STORAGE_KEY)) return;
        const raw = changes[this.TOP_SITES_STORAGE_KEY]?.newValue;
        if (typeof raw !== "boolean") return;
        if (!this._settings?.topSitesPersistent && !this.topSitesPopover?.isPersistent?.()) return;
        this.applyTopSitesVisibility(raw, { persist: false });
      };
      try {
        chrome.storage.onChanged.addListener(this._topSitesStorageListener);
      } catch {
      }
      await this.refreshTopSitesVisibilityFromStorage();
    }
    async refreshTopSitesVisibilityFromStorage() {
      const persistent = !!(this._settings?.topSitesPersistent || this.topSitesPopover?.isPersistent?.());
      if (!persistent) return;
      const visible = await this.getTopSitesVisibleFromStorage();
      if (visible) this.applyTopSitesVisibility(true, { persist: false });
    }
    async getTopSitesVisibleFromStorage() {
      const value = await storageGetValue(this.TOP_SITES_STORAGE_KEY, false);
      return typeof value === "boolean" ? value : false;
    }
    async setTopSitesVisibleInStorage(visible) {
      if (!chrome?.storage) return;
      await storageSetValue(this.TOP_SITES_STORAGE_KEY, Boolean(visible), {
        includeTimestamp: true,
        dualWrite: true
      });
    }
    applyTopSitesVisibility(visible, { persist = false, applyUi = true } = {}) {
      if (this._isPopoverOsWindow || window.__KP_POPOVER_WINDOW) {
        this._topSitesVisible = false;
        return;
      }
      const next = Boolean(visible);
      this._topSitesVisible = next;
      if (persist) {
        void this.setTopSitesVisibleInStorage(next);
      }
      if (!applyUi) return;
      if (!this.enabled) {
        try {
          this.topSitesPopover?.hide?.({ persistClosed: false });
        } catch {
        }
        return;
      }
      if (next) {
        try {
          this.launcherPopover?.hide?.();
        } catch {
        }
        void this.topSitesPopover?.show?.();
      } else {
        try {
          this.topSitesPopover?.hide?.({ persistClosed: false });
        } catch {
        }
      }
    }
    async refreshKeyboardHelpVisibilityFromStorage() {
      const visible = await this.getKeyboardHelpVisibleFromStorage();
      this.applyKeyboardHelpVisibility(visible, { persist: false });
    }
    async getKeyboardHelpVisibleFromStorage() {
      const value = await storageGetValue(this.KEYBOARD_HELP_STORAGE_KEY, false);
      return typeof value === "boolean" ? value : false;
    }
    async setKeyboardHelpVisibleInStorage(visible) {
      if (!chrome?.storage) return;
      const next = Boolean(visible);
      try {
        cacheChromeLayout({ keyboardHelpVisible: next });
      } catch {
      }
      await storageSetValue(this.KEYBOARD_HELP_STORAGE_KEY, next, {
        includeTimestamp: true,
        dualWrite: true
      });
    }
    /**
     * Prefer painted DOM over the persisted flag so K / the control strip cannot
     * persist "hide" when the panel is already invisible (visibility leftover).
     * @returns {boolean}
     */
    _isKeyboardHelpPaintedVisible() {
      try {
        const help = this.floatingKeyboardHelp;
        if (help && typeof help.isVisible === "function") return !!help.isVisible();
      } catch {
      }
      return !!this._keyboardHelpVisible;
    }
    /**
     * Push a keydown/keyup into the floating keyboard reference so pressed keys
     * light up even when KeyPilot stops the event with stopImmediatePropagation.
     * @param {KeyboardEvent} e
     * @param {'down'|'up'} phase
     */
    _reflectKeyOnKeyboardHelp(e, phase = "down") {
      try {
        const help = this.floatingKeyboardHelp;
        if (!help || !this._keyboardHelpVisible) return;
        if (typeof help.isVisible === "function" && !help.isVisible()) return;
        if (phase === "up") {
          if (typeof help.reflectKeyUp === "function") help.reflectKeyUp(e);
          return;
        }
        if (typeof help.reflectKeyDown === "function") help.reflectKeyDown(e);
      } catch {
      }
    }
    applyKeyboardHelpVisibility(visible, { persist = false } = {}) {
      if (this._isPopoverOsWindow || window.__KP_POPOVER_WINDOW) {
        this._keyboardHelpVisible = false;
        if (this.floatingKeyboardHelp) {
          try {
            this.floatingKeyboardHelp.cleanup();
          } catch {
          }
          this.floatingKeyboardHelp = null;
        }
        return;
      }
      const next = Boolean(visible);
      this._keyboardHelpVisible = next;
      try {
        cacheChromeLayout({ keyboardHelpVisible: next });
      } catch {
      }
      if (!this.enabled) {
        if (this.floatingKeyboardHelp) {
          try {
            this.floatingKeyboardHelp.cleanup();
          } catch {
          }
          this.floatingKeyboardHelp = null;
        }
        if (persist) this.setKeyboardHelpVisibleInStorage(next);
        return;
      }
      try {
        const kb = this.keybindings || buildKeybindingsForLayout(DEFAULT_KEYBOARD_LAYOUT_ID);
        const settingsReady = !!this._settings;
        const knownPosition = settingsReady ? this._settings.panelPositions?.keyboardReference || DEFAULT_SETTINGS.panelPositions?.keyboardReference || null : null;
        if (!this.floatingKeyboardHelp) {
          const FloatingKeyboardHelpClass = FloatingKeyboardHelp || window.FloatingKeyboardHelp;
          this.floatingKeyboardHelp = new FloatingKeyboardHelpClass({
            keybindings: kb,
            keyboardLayout: this._keyboardUiLayout,
            layoutId: this._keyboardLayoutId,
            // Seed saved dock/free coords so the first paint is not bottom-left then jump.
            panelPosition: knownPosition || void 0,
            getKeyPilot: () => this
          });
        } else {
          try {
            this.floatingKeyboardHelp.setKeyPilotAccessor?.(() => this);
          } catch {
          }
          if (typeof this.floatingKeyboardHelp.setLayoutChrome === "function") {
            this.floatingKeyboardHelp.setLayoutChrome({
              keybindings: kb,
              keyboardLayout: this._keyboardUiLayout,
              layoutId: this._keyboardLayoutId
            });
          } else {
            this.floatingKeyboardHelp.setKeybindings(kb);
            if (typeof this.floatingKeyboardHelp.setKeyboardLayout === "function") {
              this.floatingKeyboardHelp.setKeyboardLayout({
                keyboardLayout: this._keyboardUiLayout,
                layoutId: this._keyboardLayoutId
              });
            }
          }
          if (settingsReady && typeof this.floatingKeyboardHelp.setPanelPositionFromSettings === "function") {
            this.floatingKeyboardHelp.setPanelPositionFromSettings(knownPosition);
          }
        }
        try {
          if (typeof this.floatingKeyboardHelp.setActiveLayoutSelection === "function") {
            const layoutSel = String(
              this._settings?.currentKeyboardLayoutId || this._currentKeyboardLayoutId || "builtin"
            );
            this.floatingKeyboardHelp.setActiveLayoutSelection({
              currentKeyboardLayoutId: layoutSel,
              userLayout: this._currentUserLayout,
              userMacros: this._currentUserMacros,
              userActions: this._currentUserActions
            });
          }
        } catch {
        }
        if (next) {
          this.floatingKeyboardHelp.show();
          this._syncKeyboardLinkHoverHints(void 0, { immediate: true });
        } else {
          this.floatingKeyboardHelp.hide();
          this._syncKeyboardLinkHoverHints(void 0, { immediate: true });
        }
        try {
          this.controlStrip?.setKeyboardHelpActive?.(!!next);
        } catch {
        }
        if (persist) this.setKeyboardHelpVisibleInStorage(next);
      } catch (e) {
        console.warn("[KeyPilot] Failed to apply keyboard reference visibility:", e);
      }
    }
    /**
     * Query service worker for initial enabled state
     */
    async queryInitialState() {
      try {
        if (!isExtensionContextValid()) {
          this.enabled = true;
          console.log("[KeyPilot] Extension context invalid; defaulting to enabled until reload");
          return;
        }
        const response = await chrome.runtime.sendMessage({ type: MSG.GET_STATE });
        if (response && typeof response.enabled === "boolean") {
          this.enabled = response.enabled;
          console.log("[KeyPilot] Initial state from service worker:", this.enabled ? "enabled" : "disabled");
        } else {
          this.enabled = true;
          console.log("[KeyPilot] No valid state from service worker, defaulting to enabled");
        }
      } catch (error) {
        if (noteExtensionContextError(error)) {
          this.enabled = true;
          return;
        }
        this.enabled = true;
        console.log("[KeyPilot] Service worker not available, defaulting to enabled:", error.message);
      }
    }
    /**
     * Initialize KeyPilot in enabled state
     */
    async initializeEnabledState() {
      this.overlayManager.setRenderingMode("dom");
      this.overlayManager.initDebugPanel();
      try {
        if (FEATURE_FLAGS.DEBUG_SHADOW_ROOT_HUD) {
          this.overlayManager.setShadowRootDebugHud(true);
        }
      } catch {
      }
      if (window.KEYPILOT_EARLY) {
        window.KEYPILOT_EARLY.setEnabled(true);
      }
      this.focusDetector.start();
      try {
        if (this.intersectionManager && typeof this.intersectionManager.setDomHoverListenersEnabled === "function") {
          this.intersectionManager.setDomHoverListenersEnabled(
            true,
            (el2) => this._handleDomHoverChange(el2)
          );
        }
      } catch {
      }
      await this.intersectionManager.init();
      try {
        if (this.overlayManager && typeof this.overlayManager.setDomHoverFocusColorsEnabled === "function") {
          this.overlayManager.setDomHoverFocusColorsEnabled(true);
        }
      } catch {
      }
      this.scrollManager.init();
      this.start();
      this.cursor.show();
      this._installFrameBridgeListener();
    }
    /**
     * Initialize KeyPilot in disabled state
     */
    initializeDisabledState() {
      if (window.KEYPILOT_EARLY) {
        window.KEYPILOT_EARLY.setEnabled(false);
      }
      this.cursor.hide();
      this.overlayManager.hideFocusOverlay();
      this.overlayManager.hideDeleteOverlay();
    }
    setupOptimizedEventListeners() {
      if (!this._boundScrollEndHandler) {
        this._boundScrollEndHandler = (event) => {
          const { mouseX, mouseY } = event.detail || {};
          if (typeof mouseX !== "number" || typeof mouseY !== "number") return;
          if (FEATURE_FLAGS.ENABLE_INTERACTIVE_DISCOVERY) {
            try {
              this.intersectionManager?.resetDiscoveryAndSchedule?.();
            } catch {
            }
          }
          this.updateElementsUnderCursor(mouseX, mouseY, false, this._pendingMouse?.underHint || null);
        };
      }
      document.addEventListener("keypilot:scroll-end", this._boundScrollEndHandler);
      if (window.KEYPILOT_DEBUG && !this._debugMetricsInterval) {
        this._debugMetricsInterval = setInterval(() => {
          if (window.KEYPILOT_DEBUG) this.logPerformanceMetrics();
        }, 1e4);
      }
    }
    /**
     * Always hand off from early-inject (stop its MO/mouse/key handlers), even when
     * custom cursors are off and CursorManager.ensure() never runs.
     */
    _signalEarlyInjectHandoff() {
      try {
        if (window.__KP_EARLY_HANDOFF_DONE) return;
        if (!window.KEYPILOT_EARLY) return;
        window.__KP_EARLY_HANDOFF_DONE = true;
        try {
          window.dispatchEvent(new CustomEvent("keypilot-main-loaded"));
        } catch {
        }
      } catch {
      }
    }
    /**
     * Initialize edge-only processing for rectangle selection optimization.
     * Lazy: call only when entering rectangle highlight mode (not on enable).
     * @param {{ force?: boolean }} [opts]
     */
    initializeEdgeOnlyProcessing(opts = {}) {
      if (this.rectangleIntersectionObserver) {
        return true;
      }
      const edgeOnlyEnabled = FEATURE_FLAGS.USE_EDGE_ONLY_SELECTION && FEATURE_FLAGS.ENABLE_EDGE_ONLY_PROCESSING && this.edgeOnlyProcessingEnabled;
      if (!edgeOnlyEnabled) {
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Edge-only processing disabled:", {
            USE_EDGE_ONLY_SELECTION: FEATURE_FLAGS.USE_EDGE_ONLY_SELECTION,
            ENABLE_EDGE_ONLY_PROCESSING: FEATURE_FLAGS.ENABLE_EDGE_ONLY_PROCESSING,
            edgeOnlyProcessingEnabled: this.edgeOnlyProcessingEnabled
          });
        }
        return false;
      }
      try {
        if (window.KEYPILOT_DEBUG || opts.force) {
          console.log("[KeyPilot] Initializing RectangleIntersectionObserver (lazy)...");
        }
        this.rectangleIntersectionObserver = new RectangleIntersectionObserver();
        if (this.rectangleIntersectionObserver) {
          console.log("[KeyPilot] RectangleIntersectionObserver initialized successfully");
        }
        this.rectangleIntersectionObserver.initialize((intersectionData) => {
          this.handleEdgeOnlyIntersectionChange(intersectionData);
        });
        if (this.overlayManager && this.overlayManager.highlightManager) {
          const notificationCallback = (message, type2) => {
            const colorMap = {
              "success": COLORS.NOTIFICATION_SUCCESS,
              "warning": COLORS.NOTIFICATION_WARNING || COLORS.NOTIFICATION_INFO,
              "error": COLORS.NOTIFICATION_ERROR,
              "info": COLORS.NOTIFICATION_INFO
            };
            const backgroundColor = colorMap[type2] || COLORS.NOTIFICATION_INFO;
            this.showFlashNotification(message, backgroundColor);
          };
          this.overlayManager.highlightManager.initializeEdgeOnlyProcessing(
            this.rectangleIntersectionObserver,
            notificationCallback
          );
        }
        if (window.KEYPILOT_DEBUG) {
          console.log("[KeyPilot Debug] Edge-only rectangle intersection observer initialized:", {
            caching: FEATURE_FLAGS.ENABLE_SELECTION_CACHING,
            batchProcessing: FEATURE_FLAGS.ENABLE_EDGE_BATCH_PROCESSING,
            predictiveCaching: FEATURE_FLAGS.ENABLE_PREDICTIVE_CACHING
          });
        }
        return true;
      } catch (error) {
        console.warn("[KeyPilot] Failed to initialize edge-only processing:", error);
        this.edgeOnlyProcessingEnabled = false;
        this.rectangleIntersectionObserver = null;
        return false;
      }
    }
    /**
     * Ensure rectangle edge-only stack exists (first rectangle session only).
     */
    ensureEdgeOnlyProcessingForRectangle() {
      if (this.rectangleIntersectionObserver) return true;
      return !!this.initializeEdgeOnlyProcessing();
    }
    /**
     * Handle intersection changes from edge-only processing
     * @param {Object} intersectionData - Data from edge-only intersection observer
     */
    handleEdgeOnlyIntersectionChange(intersectionData) {
      if (!intersectionData || !this.state.isHighlightMode()) {
        return;
      }
      try {
        const selection = this.rectangleIntersectionObserver.createSelectionFromIntersection();
        if (selection) {
          this.state.setCurrentSelection(selection);
          if (this.overlayManager && this.overlayManager.highlightManager) {
            this.overlayManager.highlightManager.updateHighlightSelectionOverlays(selection);
          }
        }
        if (window.KEYPILOT_DEBUG && FEATURE_FLAGS.DEBUG_EDGE_ONLY_PROCESSING) {
          console.log("[KeyPilot Debug] Edge-only intersection update:", {
            intersectingElements: intersectionData.intersectingElements?.length || 0,
            intersectingTextNodes: intersectionData.intersectingTextNodes?.length || 0,
            edgeProcessing: intersectionData.edgeProcessing,
            metrics: intersectionData.metrics
          });
        }
      } catch (error) {
        console.warn("[KeyPilot] Error handling edge-only intersection change:", error);
      }
    }
    /**
     * Trigger fallback from edge-only processing to spatial method
     * @param {string} reason - Reason for fallback
     */
    triggerEdgeOnlyFallback(reason) {
      if (!this.edgeOnlyProcessingEnabled) {
        return;
      }
      console.warn("[KeyPilot] Triggering edge-only processing fallback:", reason);
      this.edgeOnlyProcessingEnabled = false;
      if (FEATURE_FLAGS.SHOW_SELECTION_METHOD_IN_UI) {
        this.showFlashNotification(
          "Selection method switched to spatial for better performance",
          COLORS.NOTIFICATION_INFO
        );
      }
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot Debug] Edge-only processing disabled, using spatial fallback");
      }
    }
    /**
     * @deprecated Perpetual rAF cursor sync removed (perf audit).
     * Cursor position is updated from pointermove in handleMouseMove.
     * Kept as a no-op so any external callers do not throw.
     */
    setupContinuousCursorSync() {
    }
    setupStyles() {
      this.styleManager.injectSharedStyles();
    }
    setupShadowDOMSupport() {
      this.shadowDOMManager.setup();
      if (this.intersectionManager?.setShadowDOMManager) {
        this.intersectionManager.setShadowDOMManager(this.shadowDOMManager);
      }
    }
    setupPopupCommunication() {
      const handler = (msg, _sender, sendResponse) => {
        if (msg.type === MSG.GET_STATUS) {
          sendResponse({ mode: this.state.getState().mode });
        } else if (msg.type === MSG.TOGGLE_STATE || msg.type === MSG.UPDATE_STATE) {
          if (typeof msg.enabled === "boolean") {
            try {
              const th = window.__KeyPilotToggleHandler;
              if (th && typeof th.setEnabled === "function") {
                void th.setEnabled(msg.enabled, false);
                return;
              }
            } catch {
            }
            if (msg.enabled) {
              this.enable();
            } else {
              this.disable();
            }
          }
        } else if (msg.type === MSG.OPEN_SETTINGS_POPOVER) {
          try {
            if (window !== window.top) return;
            this.handleOpenSettingsPopover({
              panelId: typeof msg.panelId === "string" ? msg.panelId : void 0
            });
          } catch (e) {
            console.warn("[KeyPilot] Failed to open settings popover via message:", e);
          }
        } else if (msg.type === MSG.OPEN_GUIDE_POPOVER) {
          try {
            if (window !== window.top) return;
            this.handleOpenGuidePopover();
          } catch (e) {
            console.warn("[KeyPilot] Failed to open guide popover via message:", e);
          }
        } else if (msg.type === MSG.OPEN_DOCS_POPOVER) {
          try {
            if (window !== window.top) return;
            this.handleOpenDocsPopover({
              topicId: typeof msg.topicId === "string" ? msg.topicId : void 0,
              hash: typeof msg.hash === "string" ? msg.hash : void 0
            });
          } catch (e) {
            console.warn("[KeyPilot] Failed to open docs popover via message:", e);
          }
        } else if (msg.type === MSG.POPOVER_WINDOW_CLOSED) {
          try {
            if (window !== window.top) return;
            if (this.state?.isPopoverMode?.() || this.state?.getState?.()?.popoverOpen) {
              this.state.setPopoverOpen(false, null);
            }
            try {
              this.overlayManager?.clearPopoverWindowTracking?.();
            } catch {
            }
          } catch (e) {
            console.warn("[KeyPilot] Failed to handle POPOVER_WINDOW_CLOSED:", e);
          }
        } else if (msg.type === MSG.OPEN_ONBOARDING) {
          try {
            if (window !== window.top) return;
            try {
              const th = window.__KeyPilotToggleHandler;
              if (th && typeof th.enabled === "boolean" && th.enabled === false) return;
            } catch {
            }
            const ob = window.__KeyPilotOnboarding;
            if (ob && typeof ob.setActive === "function") {
              ob.setActive(true);
            }
          } catch (e) {
            console.warn("[KeyPilot] Failed to open onboarding via message:", e);
          }
        } else if (msg.type === MSG.LAUNCH_WALKTHROUGH) {
          try {
            if (window !== window.top) return;
            try {
              const th = window.__KeyPilotToggleHandler;
              if (th && typeof th.enabled === "boolean" && th.enabled === false) return;
            } catch {
            }
            try {
              const st = this.state?.getState?.();
              if (st?.mode === MODES.POPOVER) {
                this.handleClosePopover();
              }
            } catch {
            }
            const ob = window.__KeyPilotOnboarding;
            if (ob && typeof ob.resetTutorial === "function") {
              void ob.resetTutorial();
            } else if (ob && typeof ob.setActive === "function") {
              ob.setActive(true);
            }
          } catch (e) {
            console.warn("[KeyPilot] Failed to launch walkthrough via message:", e);
          }
        } else if (msg.type === MSG.KEYBOARD_REFERENCE_CONTEXT_ACTION) {
          try {
            if (window !== window.top) return;
            const value = typeof msg.value === "string" ? msg.value : "";
            if (value === "__toggle_keyboard_reference__") {
              const visible = this.floatingKeyboardHelp?.isVisible?.() === true;
              this.applyKeyboardHelpVisibility(!visible, { persist: true });
              return;
            }
            if (value === "__show_keyboard_reference__") {
              this.applyKeyboardHelpVisibility(true, { persist: true });
              return;
            }
            if (value === "__hide_keyboard_reference__") {
              this.applyKeyboardHelpVisibility(false, { persist: true });
              return;
            }
            this.floatingKeyboardHelp?.selectLayoutByValue?.(
              value
            );
          } catch (e) {
            console.warn("[KeyPilot] Failed to handle Keyboard Reference context action:", e);
          }
        }
      };
      this._runtimeMessageDisposers = [
        registerContentRuntimeHandler(MSG.GET_STATUS, handler),
        registerContentRuntimeHandler(MSG.TOGGLE_STATE, handler),
        registerContentRuntimeHandler(MSG.UPDATE_STATE, handler),
        registerContentRuntimeHandler(MSG.OPEN_SETTINGS_POPOVER, handler),
        registerContentRuntimeHandler(MSG.OPEN_GUIDE_POPOVER, handler),
        registerContentRuntimeHandler(MSG.OPEN_DOCS_POPOVER, handler),
        registerContentRuntimeHandler(MSG.POPOVER_WINDOW_CLOSED, handler),
        registerContentRuntimeHandler(MSG.OPEN_ONBOARDING, handler),
        registerContentRuntimeHandler(MSG.LAUNCH_WALKTHROUGH, handler),
        registerContentRuntimeHandler(MSG.KEYBOARD_REFERENCE_CONTEXT_ACTION, handler)
      ];
    }
    handleStateChange(newState, prevState) {
      if (prevState.mode === MODES.TEXT_FOCUS && newState.mode !== MODES.TEXT_FOCUS) {
        this._disarmTextModeClick();
      }
      if (newState.mode !== prevState.mode) {
        const inText = newState.mode === MODES.TEXT_FOCUS;
        try {
          this.controlStrip?.setTextModeActive?.(inText);
        } catch {
        }
        try {
          this.floatingKeyboardHelp?.setTextModeFilter?.(inText);
        } catch {
        }
        try {
          this.floatingKeyboardHelp?.syncEscExitButton?.();
        } catch {
        }
        const prevClickHover = modeShowsClickableHover(prevState.mode);
        const nextClickHover = modeShowsClickableHover(newState.mode);
        if (prevClickHover && !nextClickHover) {
          try {
            this._suspendClickableHoverTracking();
          } catch {
          }
        } else if (!prevClickHover && nextClickHover) {
          try {
            this._restoreClickableHoverTracking();
          } catch {
          }
        }
      }
      if (newState.mode !== prevState.mode || newState.inspectorKind !== prevState.inspectorKind || newState.mode === MODES.TEXT_FOCUS && newState.focusEl !== prevState.focusEl || newState.mode === MODES.NONE && newState.focusEl !== prevState.focusEl) {
        if (this._isCustomCursorModeEnabled()) {
          const options = this._buildCursorOptionsForState(newState);
          this.cursor.setMode(this._cursorModeForState(newState), options);
        }
        this.updatePopupStatus(newState.mode, newState.inspectorKind);
      }
      if (newState.focusedTextElement !== prevState.focusedTextElement || newState._overlayUpdateTrigger !== prevState._overlayUpdateTrigger || newState.focusEl !== prevState.focusEl || newState.inspectorEl !== prevState.inspectorEl || newState.inspectorKind !== prevState.inspectorKind || newState.mode !== prevState.mode) {
        const st = this.state.getState();
        this.updateOverlays(st.focusEl, st.inspectorEl, st.inspectorKind);
      }
      if (newState.focusEl !== prevState.focusEl || newState.mode !== prevState.mode) {
        this._syncKeyboardLinkHoverHints(newState);
      }
    }
    /**
     * Schedule (or immediately apply) link-hover keyboard key hints.
     * Debounces rapid focusEl churn (e.g. YouTube preview thumbnails).
     * @param {any} [state]
     * @param {{ immediate?: boolean }} [opts]
     */
    _syncKeyboardLinkHoverHints(state, opts = {}) {
      this._linkHoverHintPendingState = state;
      if (opts.immediate) {
        this._clearKeyboardLinkHoverHintTimer();
        this._flushKeyboardLinkHoverHints();
        return;
      }
      if (this._linkHoverHintTimer != null) {
        try {
          clearTimeout(this._linkHoverHintTimer);
        } catch {
        }
      }
      const delay = Number.isFinite(this._LINK_HOVER_HINT_DEBOUNCE_MS) ? this._LINK_HOVER_HINT_DEBOUNCE_MS : 90;
      this._linkHoverHintTimer = setTimeout(() => {
        this._linkHoverHintTimer = null;
        this._flushKeyboardLinkHoverHints();
      }, delay);
    }
    _clearKeyboardLinkHoverHintTimer() {
      if (this._linkHoverHintTimer == null) return;
      try {
        clearTimeout(this._linkHoverHintTimer);
      } catch {
      }
      this._linkHoverHintTimer = null;
    }
    /**
     * When the keyboard reference is open and the pointer is over a link,
     * highlight ACTIVATE / OPEN_POPOVER (and related) keys with a strong outline/glow.
     */
    _flushKeyboardLinkHoverHints() {
      try {
        const help = this.floatingKeyboardHelp;
        if (!help || typeof help.setLinkHoverHints !== "function") return;
        const hintsEnabled = this._settings?.clickMode?.keyboardLinkHoverHints === true || this._settings == null && DEFAULT_SETTINGS.clickMode.keyboardLinkHoverHints === true;
        let isLink = false;
        if (hintsEnabled && this._keyboardHelpVisible && this.enabled && help.isVisible?.()) {
          const st = this._linkHoverHintPendingState || this.state?.getState?.();
          if (!(st?.mode === MODES.POPOVER || st?.mode === MODES.INSPECTOR || st?.mode === MODES.DELETE || st?.mode === MODES.COLS || st?.mode === MODES.HIGHLIGHT || st?.mode === MODES.OMNIBOX)) {
            const focusEl = st?.focusEl;
            isLink = this.detector?.getClickableCategory?.(focusEl) === CLICKABLE_CATEGORY.LINK;
          }
        }
        if (this._linkHoverHintLastApplied === isLink) return;
        this._linkHoverHintLastApplied = isLink;
        help.setLinkHoverHints(isLink);
      } catch {
      }
    }
    _isCustomCursorModeEnabled() {
      const mode = this._settings?.cursorMode || DEFAULT_SETTINGS.cursorMode;
      return mode === CURSOR_MODE.CUSTOM_CURSORS;
    }
    /**
     * @param {string} mode
     * @param {string|null} [inspectorKind]
     */
    updatePopupStatus(mode, inspectorKind = null) {
      try {
        if (window !== window.top) return;
        const statusMode = mode === MODES.INSPECTOR ? getInspectorStatusMode(inspectorKind || this.state.getState()?.inspectorKind) : mode;
        this._sendRuntimeMessage({ type: MSG.STATUS, mode: statusMode }, { silent: true });
      } catch (error) {
      }
    }
    /**
     * Send a message to the service worker, handling "Extension context invalidated"
     * (content script orphaned after extension reload/update).
     * @param {object} message
     * @param {{ silent?: boolean, onResponse?: (response: unknown) => void }} [opts]
     * @returns {boolean}
     */
    _sendRuntimeMessage(message, opts = {}) {
      return safeRuntimeSendMessage(message, {
        onInvalidated: () => this._handleExtensionContextInvalidated(),
        onError: (error) => {
          if (opts.silent) return;
          if (window.KEYPILOT_DEBUG) {
            console.warn("[KeyPilot] runtime.sendMessage failed:", error);
          }
        },
        onResponse: typeof opts.onResponse === "function" ? opts.onResponse : void 0
      });
    }
    /**
     * One-time UX + quiet shutdown after the extension was reloaded/updated.
     */
    _handleExtensionContextInvalidated() {
      if (this._extensionContextInvalidatedHandled) return;
      this._extensionContextInvalidatedHandled = true;
      if (window.KEYPILOT_DEBUG) {
        console.warn("[KeyPilot] Extension context invalidated \u2014 reload the page to restore KeyPilot");
      }
      try {
        this.showFlashNotification(
          "KeyPilot updated \u2014 reload this page to restore shortcuts",
          COLORS.NOTIFICATION_WARNING
        );
      } catch {
      }
      try {
        if (typeof this.disable === "function") {
          this.disable();
        }
      } catch {
      }
    }
    handleKeyDown(e) {
      if ((e.altKey || e.code === "AltRight") && (e.key === "k" || e.key === "K" || e.code === "KeyK")) {
        try {
          e.__kpToggleHandled = true;
        } catch {
        }
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        this._sendRuntimeMessage({ type: MSG.TOGGLE_STATE }, { silent: true });
        return;
      }
      if ((e.altKey || e.code === "AltRight") && (e.key === "j" || e.key === "J" || e.code === "KeyJ")) {
        if (e.__kpControlStripHandled) return;
        try {
          e.__kpControlStripHandled = true;
        } catch {
        }
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        void this.toggleControlStripFromHotkey();
        return;
      }
      if ((e.altKey || e.code === "AltRight") && (e.key === "l" || e.key === "L" || e.code === "KeyL")) {
        if (window !== window.top) return;
        if (!this.enabled) return;
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        const currentState2 = this.state.getState();
        if (currentState2.mode === MODES.OMNIBOX) {
          this.handleCloseOmnibox();
        } else {
          this.handleOpenOmnibox();
        }
        return;
      }
      if ((e.altKey || e.code === "AltRight") && (e.key === ";" || e.key === ":" || e.code === "Semicolon" || e.key === "a" || e.key === "A" || e.code === "KeyA")) {
        if (window !== window.top) return;
        if (!this.enabled) return;
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        if (this.launcherPopover.isOpen()) {
          this.launcherPopover.hide();
        } else {
          this.launcherPopover.showWithSearchFocus();
        }
        return;
      }
      if ((e.altKey || e.code === "AltRight") && (e.key === "d" || e.key === "D" || e.code === "KeyD")) {
        if (window !== window.top) return;
        if (!this.enabled) return;
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        try {
          const on = !!this.overlayManager?.isShadowRootDebugHudEnabled?.();
          this.setShadowRootDebugHud(!on);
        } catch {
        }
        return;
      }
      if ((e.altKey || e.code === "AltRight") && (e.code === "BracketLeft" || e.key === "[")) {
        if (window !== window.top) return;
        if (!this.enabled) return;
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        this._cycleKeyboardLayoutFamily(-1);
        return;
      }
      if ((e.altKey || e.code === "AltRight") && (e.code === "BracketRight" || e.key === "]")) {
        if (window !== window.top) return;
        if (!this.enabled) return;
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        this._cycleKeyboardLayoutFamily(1);
        return;
      }
      if ((e.altKey || e.code === "AltRight") && (e.key === "c" || e.key === "C" || e.code === "KeyC")) {
        if (window !== window.top) return;
        if (!this.enabled) return;
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        this._toggleKeyboardLayoutConfigurator();
        return;
      }
      if ((e.altKey || e.code === "AltRight") && (e.key === "h" || e.key === "H" || e.code === "KeyH")) {
        if (window !== window.top) return;
        if (!this.enabled) return;
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        this.handleToggleDocsPopover();
        return;
      }
      if (!this.enabled) {
        return;
      }
      this._reflectKeyOnKeyboardHelp(e, "down");
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot] Key pressed:", e.key, "Code:", e.code);
      }
      if (this._maybeHandleTextActiveFunctionSlot(e)) return;
      if (this.hasModifierKeys(e)) {
        return;
      }
      try {
        const inPopoverIframe = window !== window.top && window.__KP_POPOVER_IFRAME;
        const inPopoverWindow = window === window.top && window.__KP_POPOVER_WINDOW;
        if (inPopoverIframe || inPopoverWindow) {
          const closeKeys = Array.isArray(window.__KP_POPOVER_CLOSE_KEYS) && window.__KP_POPOVER_CLOSE_KEYS.length ? window.__KP_POPOVER_CLOSE_KEYS : ["Escape", "e", "E", "p", "P"];
          const k = e.key;
          const isEsc = k === "Escape" || k === "Esc";
          if (isEsc || closeKeys.includes(k)) {
            if (!isEsc && this._isUnsafeToRunActionKey?.(e)) {
            } else {
              e.preventDefault();
              e.stopPropagation();
              e.stopImmediatePropagation();
              if (inPopoverWindow) {
                try {
                  void chrome.runtime.sendMessage({
                    type: MSG.CLOSE_POPOVER_WINDOW,
                    reason: "close_key"
                  });
                } catch {
                }
              } else {
                try {
                  window.parent.postMessage({ type: MSG.POPOVER_REQUEST_CLOSE, key: k }, "*");
                } catch {
                }
              }
              return;
            }
          }
        }
      } catch {
      }
      const currentState = this.state.getState();
      const KB = this.keybindings || {};
      const isCancelKey = KB.CANCEL?.keys?.includes?.(e.key) || e.key === "Escape" || e.code === "Escape";
      if (isCancelKey) {
        try {
          const help = this.floatingKeyboardHelp;
          if (help && typeof help.isPlaceTargetingActive === "function" && help.isPlaceTargetingActive()) {
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();
            try {
              help.cancelPlaceTargeting?.();
            } catch {
            }
            return;
          }
        } catch {
        }
        try {
          if (currentState.mode === MODES.OMNIBOX || this.omniboxManager?.isOpen?.()) {
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();
            this.handleCloseOmnibox();
            return;
          }
        } catch {
        }
        try {
          if (this.topSitesPopover?.isOpen?.()) {
            const handled = this.topSitesPopover.handleKeyDown(e);
            if (handled) return;
          }
        } catch {
        }
        try {
          if (this.launcherPopover?.isOpen?.()) {
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();
            this.launcherPopover.hide();
            return;
          }
        } catch {
        }
        try {
          if (isPageMediaOverlayOpen()) {
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();
            requestClosePageMediaOverlay();
            return;
          }
        } catch {
        }
        try {
          if (isMediaLibraryOverlayOpen()) {
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();
            requestCloseMediaLibraryOverlay();
            return;
          }
        } catch {
        }
        try {
          const pm = this.overlayManager?.popupManager;
          if (pm?.isOpen?.()) {
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();
            pm.requestCloseTop?.();
            return;
          }
        } catch {
        }
      }
      if (this.topSitesPopover?.isOpen?.()) {
        const handled = this.topSitesPopover.handleKeyDown(e);
        if (handled) {
          return;
        }
      }
      if (this.launcherPopover?.isOpen?.()) {
        const handled = this.launcherPopover.handleKeyDown(e);
        if (handled) {
          return;
        }
      }
      if (currentState.mode === MODES.OMNIBOX || this.omniboxManager?.isOpen?.()) {
        if (KB.CANCEL?.keys?.includes?.(e.key) || e.key === "Escape" || e.code === "Escape") {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          this.handleCloseOmnibox();
          return;
        }
        if (!this.omniboxManager?.eventTargetsOmnibox?.(e)) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          this.omniboxManager?.focusAndInsertKey?.(e);
        }
        return;
      }
      if (currentState.mode === MODES.POPOVER) {
        console.log("[KeyPilot] Popover mode active, key pressed:", e.key);
        if (KB.CANCEL?.keys?.includes?.(e.key)) {
          console.log("[KeyPilot] Escape key pressed while popover open");
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          this.handleClosePopover();
          return;
        }
        const popoverScrollKeys = collectPopoverScrollKeysFromKeyPilot(this);
        if (popoverScrollKeyMatches(popoverScrollKeys, "pageUp", e.key)) {
          e.preventDefault();
          this._scrollPopoverByHeld(e, -1, this._getPageScrollPx());
          return;
        }
        if (popoverScrollKeyMatches(popoverScrollKeys, "pageDown", e.key)) {
          e.preventDefault();
          this._scrollPopoverByHeld(e, 1, this._getPageScrollPx());
          return;
        }
        if (popoverScrollKeyMatches(popoverScrollKeys, "pageUpInstant", e.key)) {
          e.preventDefault();
          this._scrollPopoverByHeld(e, -1, this._getHalfPageScrollPx());
          return;
        }
        if (popoverScrollKeyMatches(popoverScrollKeys, "pageDownInstant", e.key)) {
          e.preventDefault();
          this._scrollPopoverByHeld(e, 1, this._getHalfPageScrollPx());
          return;
        }
        if (popoverScrollKeyMatches(popoverScrollKeys, "pageTop", e.key)) {
          e.preventDefault();
          this._scrollPopoverToEdge(-1);
          return;
        }
        if (popoverScrollKeyMatches(popoverScrollKeys, "pageBottom", e.key)) {
          e.preventDefault();
          this._scrollPopoverToEdge(1);
          return;
        }
        if (KB.ACTIVATE?.keys?.includes?.(e.key)) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          this.handleActivateKey();
          return;
        }
        if (KB.ACTIVATE_NEW_TAB?.keys?.includes?.(e.key)) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          this.handleActivateNewTabKey();
          return;
        }
        if (KB.ACTIVATE_NEW_TAB_BACKGROUND?.keys?.includes?.(e.key)) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          this.handleActivateNewTabBackgroundKey();
          return;
        }
        if (KB.PREVIEW_LINK_POPOVER?.keys?.includes?.(e.key)) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          this.handlePreviewLinkPopover();
          return;
        }
        if (KB.POI_WEBSITE?.keys?.includes?.(e.key)) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          this.handlePoiWebsiteKey(e);
          return;
        }
        if (KB.TAB_HISTORY?.keys?.includes?.(e.key)) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          this.handleToggleTabHistoryPopover();
          return;
        }
        if (KB.OPEN_POPOVER?.keys?.includes?.(e.key)) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          this.handleOpenPopover(e);
          return;
        }
        if (KB.LAUNCHER?.keys?.includes?.(e.key)) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          this.handleLauncherKey(e);
          return;
        }
        if (KB.TOP_SITES?.keys?.includes?.(e.key) || KB.TOP_SITES?.keys?.includes?.(e.code)) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          this.handleTopSitesKey(e);
          return;
        }
        if (KB.TOGGLE_KEYBOARD_HELP?.keys?.includes?.(e.key)) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          this.handleToggleKeyboardHelp();
          return;
        }
        if (KB.OPEN_SETTINGS_POPOVER?.keys?.includes?.(e.key)) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          this.handleToggleSettingsPopover();
          return;
        }
        if (KB.BACK?.keys?.includes?.(e.key) || KB.BACK2?.keys?.includes?.(e.key)) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          this.handleBackKey();
          return;
        }
        if (KB.FORWARD?.keys?.includes?.(e.key)) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          this.handleForwardKey();
          return;
        }
        console.log("[KeyPilot] Letting key pass through to popover/iframe");
        return;
      }
      const typingGate = this._evaluateTypingGate(e, currentState, KB);
      if (typingGate.blockActions) {
        if (typingGate.handled) return;
        return;
      }
      if (this.inspector?.isCumulative?.()) {
        if (e.key === "Enter" || e.key === "Return" || e.code === "Enter" || e.code === "NumpadEnter") {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          void this._finalizeCumulativeSelection().catch((err) => {
            console.error("[KeyPilot] Cumulative finalize failed:", err);
            try {
              this.inspector.exit();
            } catch {
            }
            try {
              this.overlayManager?.clearInspectorPickedOverlays?.();
            } catch {
            }
          });
          return;
        }
      }
      if (currentState.mode === MODES.HIGHLIGHT) {
        if (KB.CANCEL?.keys?.includes?.(e.key)) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          this.cancelHighlightMode();
          return;
        }
        if (KB.HIGHLIGHT?.keys?.includes?.(e.key) || KB.RECTANGLE_HIGHLIGHT?.keys?.includes?.(e.key)) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          void this.completeSelection().catch((err) => {
            console.error("[KeyPilot] completeSelection failed:", err);
            try {
              this.cancelHighlightMode();
            } catch {
            }
          });
          return;
        }
        this.cancelHighlightMode();
      }
      if (this._maybeHandleSystemLayerBinding(e)) return;
      const currentSel = String(this._currentKeyboardLayoutId || "");
      const isUserCurrent = currentSel.startsWith("user:");
      if (isUserCurrent) {
        if (this._maybeHandleCurrentLayoutBinding(e)) return;
        return;
      }
      const layoutKb = this._layoutKeybindings && typeof this._layoutKeybindings === "object" ? this._layoutKeybindings : KB;
      for (const keybinding of Object.values(layoutKb)) {
        if (!keybinding?.handler || !Array.isArray(keybinding.keys)) continue;
        if (keybinding.systemLayer) continue;
        const matchOn = Array.isArray(keybinding.matchOn) ? keybinding.matchOn : ["key"];
        const isMatch = matchOn.some((field) => keybinding.keys.includes(e[field]));
        if (!isMatch) continue;
        if (this._isUnsafeToRunActionKey(e)) {
          console.warn(
            "[KeyPilot] Suppressed action while typing:",
            keybinding.handler,
            "key=",
            e.key,
            "active=",
            document.activeElement && document.activeElement.tagName,
            document.activeElement && document.activeElement.id
          );
          return;
        }
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        const handlerFn = this[keybinding.handler];
        if (typeof handlerFn === "function") {
          try {
            const ret = handlerFn.call(this, e);
            if (ret && typeof ret.then === "function") {
              void ret.catch((err) => {
                console.warn("[KeyPilot] Action handler failed:", keybinding.handler, err);
              });
            }
          } catch (err) {
            console.warn("[KeyPilot] Action handler threw:", keybinding.handler, err);
          }
        } else {
          console.warn("[KeyPilot] Missing keybinding handler:", keybinding.handler, keybinding);
        }
        return;
      }
    }
    handleKeyUp(e) {
      this._reflectKeyOnKeyboardHelp(e, "up");
      try {
        this._scrollHold?.end?.(e?.key);
      } catch {
      }
    }
    /**
     * Dependency-free: is this element a place where letter keys should type?
     * Intentionally does NOT call isTypingContext/resolveTypingTarget (avoids any
     * bundle name-shadowing or swallowed-throw fail-open paths).
     * @param {any} el
     * @returns {boolean}
     */
    _isTextEntryElement(el2) {
      if (!el2) return false;
      try {
        if (el2.nodeType === 3) el2 = el2.parentElement;
      } catch {
      }
      if (!el2 || el2.nodeType !== 1) return false;
      try {
        if (el2.classList?.contains?.(CSS_CLASSES.OMNIBOX_INPUT)) return false;
        if (el2.closest?.(`.${CSS_CLASSES.OMNIBOX_BACKDROP}`) || el2.closest?.(`.${CSS_CLASSES.OMNIBOX_PANEL}`)) {
          return false;
        }
      } catch {
      }
      try {
        if (el2.isContentEditable) return true;
      } catch {
      }
      let tag = "";
      try {
        tag = String(el2.tagName || "").toUpperCase();
      } catch {
        tag = "";
      }
      if (tag === "TEXTAREA") {
        try {
          return !el2.disabled;
        } catch {
          return true;
        }
      }
      if (tag === "INPUT") {
        try {
          if (el2.disabled || el2.readOnly) return false;
        } catch {
        }
        let type2 = "text";
        try {
          type2 = String(el2.type || "text").toLowerCase();
        } catch {
        }
        if (type2 === "button" || type2 === "submit" || type2 === "reset" || type2 === "checkbox" || type2 === "radio" || type2 === "file" || type2 === "image" || type2 === "range" || type2 === "color" || type2 === "hidden") {
          return false;
        }
        return true;
      }
      try {
        const role = el2.getAttribute?.("role");
        if (role === "textbox" || role === "searchbox") return true;
        if (role === "combobox") {
          if (el2.isContentEditable) return true;
          if (el2.getAttribute?.("aria-multiline") === "true") return true;
          if ("value" in el2 && tag !== "SELECT") return true;
        }
      } catch {
      }
      try {
        let p = el2.parentElement;
        let depth = 0;
        while (p && depth++ < 6) {
          if (p.isContentEditable) return true;
          p = p.parentElement;
        }
      } catch {
      }
      return false;
    }
    /**
     * Find the live typing element from event + focus, without helper indirection.
     * @param {KeyboardEvent|null|undefined} e
     * @returns {Element|null}
     */
    _findLiveTypingElement(e) {
      const candidates = [];
      try {
        const path = e && typeof e.composedPath === "function" ? e.composedPath() : null;
        if (path) {
          for (const n of path) {
            if (n && n.nodeType === 1) {
              candidates.push(n);
              break;
            }
          }
        }
      } catch {
      }
      try {
        if (e?.target) candidates.push(e.target);
      } catch {
      }
      try {
        const active2 = kpGetDeepActiveElement();
        if (active2) candidates.push(active2);
      } catch {
      }
      try {
        const cur = this.focusDetector?.currentFocusedElement;
        if (cur) candidates.push(cur);
      } catch {
      }
      try {
        const st = this.state?.getState?.();
        if (st?.focusedTextElement) candidates.push(st.focusedTextElement);
      } catch {
      }
      for (const c of candidates) {
        if (this._isTextEntryElement(c)) {
          return (
            /** @type {Element} */
            c.nodeType === 1 ? c : c.parentElement
          );
        }
      }
      return null;
    }
    /**
     * True when KeyPilot action keys (close tab, new tab, etc.) must not run.
     * Fail-closed for any detected text entry surface.
     * @param {KeyboardEvent|null|undefined} [e]
     * @returns {boolean}
     */
    _isUnsafeToRunActionKey(e) {
      try {
        const st = this.state?.getState?.();
        if (st?.mode === MODES.TEXT_FOCUS) return true;
        if (st?.focusedTextElement) return true;
      } catch {
      }
      try {
        if (this.focusDetector?.isInTextFocus?.()) return true;
      } catch {
      }
      if (this._findLiveTypingElement(e)) return true;
      try {
        if (typeof this.resolveTypingTarget === "function" && this.resolveTypingTarget(e)) {
          return true;
        }
      } catch {
      }
      try {
        if (this.isTypingContext?.(e?.target)) return true;
      } catch {
      }
      return false;
    }
    /**
     * Call at the top of every tab/nav action handler.
     * @param {string} actionName
     * @param {KeyboardEvent|null|undefined} [e]
     * @returns {boolean} true if the action is allowed to proceed
     */
    _allowActionKey(actionName, e) {
      if (this._isUnsafeToRunActionKey(e)) {
        console.warn(
          `[KeyPilot] ${actionName} blocked \u2014 typing context`,
          "active=",
          document.activeElement && document.activeElement.tagName,
          document.activeElement && document.activeElement.id
        );
        return false;
      }
      return true;
    }
    /**
     * Early typing gate for handleKeyDown.
     * @param {KeyboardEvent} e
     * @param {any} currentState
     * @param {Record<string, any>} KB
     * @returns {{ blockActions: boolean, handled: boolean }}
     */
    _evaluateTypingGate(e, currentState, KB) {
      const inTextFocus = currentState?.mode === MODES.TEXT_FOCUS || !!currentState?.focusedTextElement;
      const typingTarget = this._findLiveTypingElement(e);
      if (!inTextFocus && !typingTarget) {
        return { blockActions: false, handled: false };
      }
      if (typingTarget && !inTextFocus) {
        try {
          if (this.focusDetector?.isTextInput?.(typingTarget)) {
            this.focusDetector.setTextFocus(typingTarget);
          } else if (this.focusDetector?.setTextFocus) {
            try {
              this.focusDetector.setTextFocus(typingTarget);
            } catch {
            }
          }
        } catch {
        }
      }
      if (KB.CANCEL?.keys?.includes?.(e.key) || e.key === "Escape" || e.code === "Escape") {
        console.debug("Escape key detected in text focus / typing context");
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        try {
          this.handleEscapeFromTextFocus(currentState || this.state.getState());
        } catch {
          try {
            this.focusDetector?.clearTextFocus?.();
          } catch {
          }
        }
        return { blockActions: true, handled: true };
      }
      if ((inTextFocus || !!typingTarget) && KB.ACTIVATE?.keys?.includes?.(e.key) && this._textModeClickArmed && currentState?.focusEl) {
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        this._handleActivateFromTextFocus(currentState);
        return { blockActions: true, handled: true };
      }
      return { blockActions: true, handled: false };
    }
    handlePointerDown(e) {
      if (!this.enabled) return;
      const pointerDef = matchPointerFunctionDef(e, this._settings || DEFAULT_SETTINGS);
      if (pointerDef) {
        if (this._pointerBindingShouldYield(e, pointerDef)) {
          try {
            const mode = pointerDef.mode;
            if (mode && this.state.getState()?.mode === mode) this.cancelModes();
          } catch {
          }
          return;
        }
        this._claimPointerBinding(e);
        this._dispatchPointerFunction(pointerDef, e);
        return;
      }
      if (!this._activeModeCancelsOnPointerDown()) return;
      try {
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
      } catch {
      }
      this.cancelModes();
    }
    handleAuxClick(e) {
      if (!this.enabled) return;
      if (!this._pointerBindingClaimed) return;
      this._pointerBindingClaimed = false;
      try {
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
      } catch {
      }
    }
    /**
     * @param {MouseEvent|PointerEvent} e
     */
    _claimPointerBinding(e) {
      this._pointerBindingClaimed = true;
      try {
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
      } catch {
      }
    }
    /**
     * Native middle-click (open-in-new-tab, caret, chrome) wins over a pointer-bound Function.
     * @param {MouseEvent|PointerEvent} e
     * @param {import('./config/function-library.js').FunctionDef} def
     * @returns {boolean}
     */
    _pointerBindingShouldYield(e, def) {
      const binding = def?.pointerBinding;
      if (!binding) return true;
      try {
        const mode = this.state.getState()?.mode;
        const yieldModes = binding.yieldToModes || [];
        if (mode && yieldModes.includes(mode)) return true;
      } catch {
      }
      let path = [];
      try {
        path = typeof e.composedPath === "function" ? e.composedPath() : [];
      } catch {
        path = [];
      }
      for (const n of path) {
        if (!n || n.nodeType !== 1) continue;
        try {
          if (this._isKeyPilotUiElement(n)) return true;
        } catch {
        }
        if (binding.yieldToTextEntry) {
          try {
            if (this._isTextEntryElement(n)) return true;
          } catch {
          }
        }
      }
      if (!binding.yieldToClickables) return false;
      let under = path.find((n) => n && n.nodeType === 1) || null;
      if (!under) {
        try {
          under = this.detector?.deepElementFromPoint?.(e.clientX, e.clientY) || deepElementFromPoint(e.clientX, e.clientY);
        } catch {
          under = null;
        }
      }
      if (!under) return false;
      try {
        if (this.detector?.findClickable?.(under)) return true;
      } catch {
      }
      try {
        const focus = this.state.getState()?.focusEl;
        if (focus instanceof Element && under instanceof Element) {
          if (containsComposed(focus, under) || containsComposed(under, focus)) return true;
        }
      } catch {
      }
      return false;
    }
    /**
     * @param {import('./config/function-library.js').FunctionDef} def
     * @param {MouseEvent|PointerEvent} e
     */
    _dispatchPointerFunction(def, e) {
      try {
        const x = Number(e.clientX);
        const y = Number(e.clientY);
        if (Number.isFinite(x) && Number.isFinite(y)) {
          this.state.setMousePosition(x, y);
          this.cursor?.updatePosition?.(x, y);
        }
      } catch {
      }
      const handlerName = def?.handler ? String(def.handler) : "";
      const fn = handlerName ? this[handlerName] : null;
      if (typeof fn !== "function") return;
      try {
        const ret = fn.call(this, e);
        if (ret && typeof ret.then === "function") {
          void ret.catch((err) => {
            console.warn("[KeyPilot] Pointer Function handler failed:", handlerName, err);
          });
        }
      } catch (err) {
        console.warn("[KeyPilot] Pointer Function handler threw:", handlerName, err);
      }
    }
    /**
     * Pointerdown dismiss is declared on the active mode resource
     * (FunctionDef, inspector kind, or highlight mode) — not hardcoded per key.
     * @returns {boolean}
     */
    _activeModeCancelsOnPointerDown() {
      let mode = null;
      try {
        mode = this.state.getState()?.mode;
      } catch {
      }
      if (functionCancelsOnPointerDown(mode)) return true;
      try {
        if (this.inspector?.cancelsOnPointerDown?.()) return true;
      } catch {
      }
      try {
        if (this.state.isHighlightMode() && this.overlayManager?.highlightManager?.cancelsOnPointerDown?.()) {
          return true;
        }
      } catch {
      }
      return false;
    }
    handleMouseMove(e) {
      if (!this.enabled) {
        return;
      }
      try {
        if (e && e.isTrusted === false) {
          if (Number(e.pointerId) === MAP_PAN_POINTER_ID) return;
          if (Number(e.detail) === 88) return;
        }
      } catch {
      }
      const x = e.clientX;
      const y = e.clientY;
      this.state.setMousePosition(x, y);
      this.cursor.updatePosition(x, y);
      this.mouseCoordinateManager.updateCurrentMousePosition(x, y);
      this._maybeReclaimFocusAfterParentPointerMove();
      try {
        if (this.state.isScrollLineMode()) {
          this._scrollLineOverlay?.updatePointer(x, y);
          return;
        }
      } catch {
      }
      this._pendingMouse.x = x;
      this._pendingMouse.y = y;
      this._pendingMouse.underHint = this._getUnderElementHintFromMouseEvent(e);
      try {
        const st = this.state.getState();
        if (this._domHoverListenersEnabled && st.mode === MODES.NONE) {
          return;
        }
      } catch {
      }
      try {
        if (this.scrollManager && this.scrollManager.isScrolling) {
          return;
        }
      } catch {
      }
      if (this._mouseMoveRAF) return;
      this._mouseMoveRAF = window.requestAnimationFrame(() => {
        this._mouseMoveRAF = 0;
        if (!this.enabled) return;
        this.updateElementsUnderCursorWithThreshold(
          this._pendingMouse.x,
          this._pendingMouse.y,
          true,
          this._pendingMouse.underHint
        );
      });
    }
    updateElementsUnderCursorWithThreshold(x, y, fromMouseMove = false, underHint = null) {
      const deltaX = Math.abs(x - this.lastQueryPosition.x);
      const deltaY = Math.abs(y - this.lastQueryPosition.y);
      if (deltaX < this.MOUSE_QUERY_THRESHOLD && deltaY < this.MOUSE_QUERY_THRESHOLD) {
        return;
      }
      this.lastQueryPosition.x = x;
      this.lastQueryPosition.y = y;
      this.updateElementsUnderCursor(x, y, fromMouseMove, underHint);
    }
    updateElementsUnderCursor(x, y, fromMouseMove = false, underHint = null) {
      const currentState = this.state.getState();
      this.performanceMetrics.mouseQueries++;
      const clickHoverActive = modeShowsClickableHover(currentState.mode);
      let under = underHint || null;
      if (!under) under = this.detector.deepElementFromPoint(x, y);
      let clickable = null;
      if (clickHoverActive) {
        try {
          clickable = this.detector.findClickable(under);
        } catch {
          clickable = null;
        }
      }
      if (currentState.mode === MODES.POPOVER) {
        const pierced = this._piercePopoverIframeAt(x, y);
        if (pierced) {
          under = pierced.el;
          clickable = pierced.clickable;
        } else {
          const isInsidePopover = (el2) => this._isElementInPopover(el2);
          if (!isInsidePopover(under)) clickable = null;
          else if (clickable && !isInsidePopover(clickable)) clickable = null;
          try {
            if (under && under.tagName === "IFRAME") clickable = null;
            if (clickable && clickable.tagName === "IFRAME") clickable = null;
          } catch {
          }
        }
      }
      if (currentState.mode === MODES.TEXT_FOCUS && currentState.focusedTextElement && clickable === currentState.focusedTextElement) {
        clickable = null;
      }
      if (currentState.mode === MODES.TEXT_FOCUS && !fromMouseMove) {
        if (this._textModeClickArmed || currentState.focusEl) {
          this._disarmTextModeClick();
          if (currentState.focusEl) this.state.setFocusElement(null);
        } else {
          try {
            this.overlayManager?.setHoverClickLabelText?.("F clicks");
          } catch {
          }
        }
        if (currentState.inspectorEl) this.state.setInspectorElement(null);
        return;
      }
      this.intersectionManager.trackElementAtPoint(x, y, under, clickable);
      if (window.KEYPILOT_DEBUG && clickable) {
        console.log("[KeyPilot Debug] Found clickable element:", {
          tagName: clickable.tagName,
          href: clickable.href,
          className: clickable.className,
          text: clickable.textContent?.substring(0, 50),
          mode: currentState.mode
        });
      }
      if (!clickHoverActive) {
        if (currentState.focusEl) this.state.setFocusElement(null);
      } else if (clickable !== currentState.focusEl) {
        this.state.setFocusElement(clickable);
      }
      if (currentState.mode === MODES.TEXT_FOCUS) {
        if (clickable) {
          if (!this._textModeClickArmed || this._textModeClickArmedTarget !== clickable) {
            this._armTextModeClick(clickable);
          }
        } else {
          this._disarmTextModeClick();
        }
      }
      this.inspector.updateHover(under);
      if (this.state.isHighlightMode()) {
        if (!isPageMediaOverlayOpen() && !isMediaLibraryOverlayOpen()) {
          this.updateSelection();
        }
      }
    }
    /**
     * Shared path: enter inspector kind, or confirm if already that kind.
     * @param {string} kind INSPECTOR_KIND
     * @param {(el: Element|null) => void} onConfirm
     * @param {{ ignoreTextFocus?: boolean, ignoreModal?: boolean }} [opts]
     * @returns {boolean} true if enter/confirm was handled (false if blocked)
     */
    _toggleInspectorKind(kind, onConfirm, opts = {}) {
      const currentState = this.state.getState();
      if (!opts.ignoreTextFocus && currentState.mode === MODES.TEXT_FOCUS) {
        return false;
      }
      if (!opts.ignoreModal && (currentState.mode === MODES.POPOVER || currentState.mode === MODES.OMNIBOX)) {
        return false;
      }
      if (this.inspector.isKind(kind)) {
        const target = this.inspector.confirmAndExit();
        try {
          onConfirm(target);
        } catch (err) {
          console.warn("[KeyPilot] Inspector confirm failed:", kind, err);
        }
        return true;
      }
      console.log("[KeyPilot] Entering inspector mode:", kind);
      this._prepareInspectorModeIndicator(kind);
      this.inspector.enter(kind);
      return true;
    }
    /**
     * Layout-aware confirm key label for inspector instruction chip.
     * @param {string} kind
     * @returns {string}
     */
    _confirmKeyLabelForInspectorKind(kind) {
      const def = getInspectorDef(kind);
      const actionId = def?.actionId;
      const binding = actionId ? this.keybindings?.[actionId] : null;
      if (binding?.displayKey) return String(binding.displayKey);
      if (Array.isArray(binding?.keys) && binding.keys.length) {
        const ch = binding.keys.find((k) => typeof k === "string" && k.length === 1);
        if (ch) return ch === " " ? "Space" : ch;
        const first = String(binding.keys[0] || "");
        if (first === "Backspace") return "Backspace";
        if (first === "Escape") return "Esc";
        return first || "?";
      }
      if (kind === INSPECTOR_KIND.COLS) return ".";
      if (kind === INSPECTOR_KIND.DELETE) return "Backspace";
      return "?";
    }
    /**
     * Seed top-right instruction chip (Text Select style) before/while pick is active.
     * @param {string} kind
     */
    _prepareInspectorModeIndicator(kind) {
      try {
        const confirmKey = this._confirmKeyLabelForInspectorKind(kind);
        this.overlayManager?.setInspectorModeIndicatorOpts?.({ kind, confirmKey });
        this.overlayManager?.showInspectorModeIndicator?.({ kind, confirmKey });
      } catch {
      }
    }
    handleDeleteKey(e) {
      if (!this._allowActionKey("handleDeleteKey", e)) return;
      this._toggleInspectorKind(INSPECTOR_KIND.DELETE, (victim) => {
        console.log("[KeyPilot] Deleting element:", victim);
        this.deleteElement(victim);
      }, { ignoreTextFocus: true, ignoreModal: true });
    }
    /**
     * Cols Toggle (period): shared inspector pick → apply multicol,
     * or clear sticky columns if already applied. Esc exits pick only.
     */
    handleColsToggleKey(e) {
      if (!this._allowActionKey("handleColsToggleKey", e)) return;
      const currentState = this.state.getState();
      if (currentState.mode === MODES.TEXT_FOCUS) {
        console.log("[KeyPilot] Cols toggle ignored \u2014 text focus mode");
        return;
      }
      if (currentState.mode === MODES.POPOVER || currentState.mode === MODES.OMNIBOX) {
        console.log("[KeyPilot] Cols toggle ignored \u2014 modal mode");
        return;
      }
      if (this.columnLayoutManager?.isActive?.()) {
        console.log("[KeyPilot] Clearing column layout");
        try {
          this.columnLayoutManager.clear();
        } catch (err) {
          console.warn("[KeyPilot] Failed to clear columns:", err);
        }
        if (this.inspector.isKind(INSPECTOR_KIND.COLS)) {
          this.inspector.exit();
        }
        try {
          this.showFlashNotification("Columns off", COLORS.NOTIFICATION_INFO);
        } catch {
        }
        return;
      }
      this._toggleInspectorKind(INSPECTOR_KIND.COLS, (target) => {
        if (!target) {
          try {
            this.showFlashNotification("Nothing to columnize", COLORS.NOTIFICATION_INFO);
          } catch {
          }
          return;
        }
        console.log("[KeyPilot] Applying columns to:", target?.tagName, target?.id || target?.className);
        const ok = this.columnLayoutManager.apply(target);
        if (ok) {
          try {
            this.showFlashNotification(
              this.columnLayoutManager.isPageMode?.() ? "Page columns on" : "Columns on",
              COLORS.NOTIFICATION_SUCCESS
            );
          } catch {
          }
        } else {
          try {
            this.showFlashNotification("Could not columnize element", COLORS.NOTIFICATION_ERROR);
          } catch {
          }
        }
      });
    }
    handleHighlightKey(e) {
      if (!this._allowActionKey("handleHighlightKey", e)) return;
      const currentState = this.state.getState();
      if (currentState.mode === MODES.TEXT_FOCUS) {
        console.log("[KeyPilot] H key ignored - currently in text focus mode");
        return;
      }
      if (!this.state.isHighlightMode()) {
        console.log("[KeyPilot] Entering highlight mode");
        try {
          this.unitSelection?.clear?.();
        } catch {
        }
        if (this.state.isInspectorMode()) {
          console.log("[KeyPilot] Canceling inspector mode to enter highlight mode");
          this.inspector.exit();
        }
        try {
          this._exitScrollLineMode();
        } catch {
        }
        this.state.setMode(MODES.HIGHLIGHT);
        this._lastHighlightUpdatePos = { x: -1, y: -1 };
        this.overlayManager.setSelectionMode("character");
        this.startHighlighting();
      } else {
        console.log("[KeyPilot] Completing highlight selection");
        void this.completeSelection().catch((err) => {
          console.error("[KeyPilot] completeSelection failed:", err);
          try {
            this.cancelHighlightMode();
          } catch {
          }
        });
      }
    }
    handleRectangleHighlightKey(e) {
      if (!this._allowActionKey("handleRectangleHighlightKey", e)) return;
      const currentState = this.state.getState();
      if (currentState.mode === MODES.TEXT_FOCUS) {
        console.log("[KeyPilot] Rectangle highlight ignored - currently in text focus mode");
        return;
      }
      try {
        this.unitSelection?.clear?.();
      } catch {
      }
      const yMode = getActionMode(this._getBuiltinFunctionActionParams("RECTANGLE_HIGHLIGHT"), "RECTANGLE_HIGHLIGHT");
      if (yMode === "cumulative") {
        if (this.inspector.isKind(INSPECTOR_KIND.RECTANGLE_PICK) && this.inspector.isCumulative()) {
          const added = this.inspector.confirmAdd();
          if (!added) {
            try {
              this.showFlashNotification("Hover an element to add", COLORS.NOTIFICATION_INFO);
            } catch {
            }
          }
          return;
        }
        if (this.state.isHighlightMode()) {
          this.cancelHighlightMode();
        }
        try {
          this._exitScrollLineMode();
        } catch {
        }
        if (this.state.isInspectorMode() && !this.inspector.isKind(INSPECTOR_KIND.RECTANGLE_PICK)) {
          this.inspector.exit();
          try {
            this.overlayManager?.clearInspectorPickedOverlays?.();
          } catch {
          }
        }
        console.log("[KeyPilot] Entering cumulative element pick mode");
        this._prepareInspectorModeIndicator(INSPECTOR_KIND.RECTANGLE_PICK);
        this.inspector.enter(INSPECTOR_KIND.RECTANGLE_PICK, { selectionMode: "cumulative" });
        return;
      }
      if (!this.state.isHighlightMode()) {
        console.log("[KeyPilot] Entering element rectangle highlight mode");
        if (this.state.isInspectorMode()) {
          console.log("[KeyPilot] Canceling inspector mode to enter rectangle highlight mode");
          this.inspector.exit();
          try {
            this.overlayManager?.clearInspectorPickedOverlays?.();
          } catch {
          }
        }
        try {
          this._exitScrollLineMode();
        } catch {
        }
        if (FEATURE_FLAGS.USE_EDGE_ONLY_SELECTION && FEATURE_FLAGS.ENABLE_EDGE_ONLY_PROCESSING) {
          try {
            this.ensureEdgeOnlyProcessingForRectangle();
          } catch {
          }
        }
        this.state.setMode(MODES.HIGHLIGHT);
        this._lastHighlightUpdatePos = { x: -1, y: -1 };
        this.overlayManager.setSelectionMode("element");
        this.startHighlighting();
      } else {
        console.log("[KeyPilot] Completing element rectangle highlight selection");
        void this.completeSelection().catch((err) => {
          console.error("[KeyPilot] completeSelection failed:", err);
          try {
            this.cancelHighlightMode();
          } catch {
          }
        });
      }
    }
    startHighlighting() {
      const currentState = this.state.getState();
      const selectionMode = this.overlayManager.getSelectionMode();
      try {
        const KB = this.keybindings || {};
        const binding = selectionMode === "rectangle" || selectionMode === "element" ? KB.RECTANGLE_HIGHLIGHT : KB.HIGHLIGHT;
        const finishKey = Array.isArray(binding?.keys) && binding.keys.length ? binding.keys.find((k) => k && k.length === 1) || binding.keys[0] : selectionMode === "rectangle" || selectionMode === "element" ? "Y" : "H";
        this.overlayManager.highlightManager?.showHighlightModeIndicator?.({ finishKey });
      } catch {
        try {
          this.overlayManager.highlightManager?.showHighlightModeIndicator?.();
        } catch {
        }
      }
      const scrollX = window.pageXOffset || document.documentElement.scrollLeft;
      const scrollY = window.pageYOffset || document.documentElement.scrollTop;
      const startPosition = {
        x: currentState.lastMouse.x + scrollX,
        // Document coordinate
        y: currentState.lastMouse.y + scrollY,
        // Document coordinate
        viewportX: currentState.lastMouse.x,
        // Keep viewport coordinate for reference
        viewportY: currentState.lastMouse.y,
        // Keep viewport coordinate for reference
        element: this.detector.deepElementFromPoint(currentState.lastMouse.x, currentState.lastMouse.y)
      };
      console.log("[KeyPilot] Starting text selection at:", startPosition, "Mode:", selectionMode);
      this.state.setHighlightStartPosition(startPosition);
      if (selectionMode === "character") {
        const success = this.overlayManager.startCharacterSelection(
          { x: currentState.lastMouse.x, y: currentState.lastMouse.y }
        );
        if (success) {
          console.log("[KeyPilot] Character selection started successfully");
        } else {
          console.log("[KeyPilot] No text at start point; character selection will seed when cursor hits text");
        }
        return;
      }
      if (selectionMode === "element") {
        try {
          const viewportStart = {
            x: currentState.lastMouse.x,
            y: currentState.lastMouse.y
          };
          const matched = this.overlayManager.updateElementRectangleSelection(
            viewportStart,
            viewportStart
          );
          console.log("[KeyPilot] Element rectangle selection seeded;", Array.isArray(matched) ? matched.length : 0, "hits");
        } catch (error) {
          console.warn("[KeyPilot] Error seeding element rectangle selection:", error);
        }
        return;
      }
      try {
        const viewportStart = {
          x: currentState.lastMouse.x,
          y: currentState.lastMouse.y
        };
        const seeded = this.overlayManager.updateRectangleSelectionFromCarets(
          viewportStart,
          viewportStart
        );
        if (seeded) {
          this.state.setCurrentSelection(seeded);
          console.log("[KeyPilot] Rectangle selection seeded at cursor");
        } else {
          console.log("[KeyPilot] No text at rectangle origin; selection will start when drag hits text");
        }
      } catch (error) {
        console.warn("[KeyPilot] Error seeding rectangle selection:", error);
      }
    }
    /**
     * Update live text/rectangle selection.
     * @param {{ force?: boolean }} [opts] - force=true bypasses mouse-move threshold (use on scroll)
     */
    updateSelection(opts = {}) {
      if (this._completingHighlight) return;
      const force = !!opts.force;
      const currentState = this.state.getState();
      const startPos = currentState.highlightStartPosition;
      const selectionMode = this.overlayManager.getSelectionMode();
      if (!startPos) {
        return;
      }
      const currentPos = {
        x: currentState.lastMouse.x,
        y: currentState.lastMouse.y
      };
      if (!force) {
        const last = this._lastHighlightUpdatePos || { x: -1, y: -1 };
        const thr = this._HIGHLIGHT_UPDATE_THRESHOLD ?? 2;
        if (Math.abs(currentPos.x - last.x) < thr && Math.abs(currentPos.y - last.y) < thr) {
          return;
        }
      }
      this._lastHighlightUpdatePos = { x: currentPos.x, y: currentPos.y };
      const scrollX = window.pageXOffset || document.documentElement.scrollLeft || 0;
      const scrollY = window.pageYOffset || document.documentElement.scrollTop || 0;
      const startPosForOverlay = {
        x: (typeof startPos.x === "number" ? startPos.x : (startPos.viewportX ?? 0) + scrollX) - scrollX,
        y: (typeof startPos.y === "number" ? startPos.y : (startPos.viewportY ?? 0) + scrollY) - scrollY
      };
      const hmOrigin = this.overlayManager?.highlightManager?.getOriginViewportPoint?.();
      const originVp = hmOrigin || startPosForOverlay;
      if (selectionMode === "character") {
        try {
          this.overlayManager.updateCharacterSelection(currentPos, originVp);
        } catch (error) {
          console.warn("[KeyPilot] Error updating character selection:", error);
        }
        return;
      }
      if (selectionMode === "element") {
        try {
          this.overlayManager.updateElementRectangleSelection(originVp, currentPos);
        } catch (error) {
          console.warn("[KeyPilot] Error updating element rectangle selection:", error);
        }
        return;
      }
      try {
        const caretSelection = this.overlayManager.updateRectangleSelectionFromCarets(
          originVp,
          currentPos
        );
        if (caretSelection) {
          this.state.setCurrentSelection(caretSelection);
        } else {
          try {
            this.overlayManager.updateHighlightRectangleOverlay(originVp, currentPos);
          } catch (overlayError) {
            console.warn("[KeyPilot] Error updating highlight rectangle overlay:", overlayError);
          }
        }
      } catch (error) {
        console.error("[KeyPilot] Unexpected error updating selection:", error);
      }
    }
    /**
     * Called every scroll frame while in highlight mode so the dashed rectangle
     * stays glued to page content (not only on scroll-end).
     */
    refreshHighlightDuringScroll() {
      if (!this.enabled || this._completingHighlight || !this.state?.isHighlightMode?.()) return;
      try {
        this.updateSelection({ force: true });
      } catch (e) {
        console.warn("[KeyPilot] refreshHighlightDuringScroll failed:", e);
      }
    }
    /**
     * Build clipboard payload from a list of DOM elements (plain + rich HTML).
     * Used by element-rectangle complete and cumulative pick finalize.
     * @param {Element[]} elements
     * @returns {{ plainText: string, htmlContent: string, hasRichContent: boolean }}
     */
    buildElementsClipboardContent(elements) {
      const list = Array.isArray(elements) ? elements : [];
      const plainParts = [];
      const htmlParts = [];
      for (const el2 of list) {
        if (!el2 || el2.nodeType !== 1 || !el2.isConnected) continue;
        const tag = String(el2.tagName || "").toUpperCase();
        try {
          if (tag === "IMG") {
            const alt = (el2.getAttribute?.("alt") || "").trim();
            const src = el2.currentSrc || el2.src || el2.getAttribute?.("src") || "";
            plainParts.push(alt || src || "");
            htmlParts.push(el2.outerHTML || "");
          } else if (tag === "VIDEO" || tag === "AUDIO" || tag === "PICTURE" || tag === "SVG") {
            plainParts.push((el2.getAttribute?.("aria-label") || el2.getAttribute?.("title") || tag).trim());
            htmlParts.push(el2.outerHTML || "");
          } else {
            const text = (el2.innerText || el2.textContent || "").replace(/\s+\n/g, "\n").trim();
            if (text) plainParts.push(text);
            htmlParts.push(el2.outerHTML || "");
          }
        } catch {
        }
      }
      let htmlContent = htmlParts.filter(Boolean).join("");
      try {
        if (htmlContent) htmlContent = this.sanitizeHtmlContent(htmlContent);
      } catch {
      }
      const plainText = plainParts.filter(Boolean).join("\n\n");
      return {
        plainText,
        htmlContent,
        hasRichContent: !!(FEATURE_FLAGS.ENABLE_RICH_TEXT_CLIPBOARD && htmlContent && htmlContent !== plainText)
      };
    }
    /**
     * Finalize cumulative inspector pick: copy picked elements and exit.
     */
    async _finalizeCumulativeSelection() {
      if (!this.inspector?.isCumulative?.()) return;
      const { elements } = this.inspector.finalizeAndExit();
      try {
        this.overlayManager?.hideInspectorModeIndicator?.();
      } catch {
      }
      try {
        this.overlayManager?.clearInspectorPickedOverlays?.();
      } catch {
      }
      const content = this.buildElementsClipboardContent(elements);
      if (!content.plainText || !String(content.plainText).trim()) {
        if (!(content.htmlContent && content.hasRichContent)) {
          this.showFlashNotification("No elements selected", COLORS.NOTIFICATION_INFO);
          return;
        }
      }
      const payload = content.plainText?.trim() ? content : { ...content, plainText: content.plainText || " " };
      let copySuccess = false;
      try {
        copySuccess = await this.copyToClipboard(payload);
      } catch (error) {
        console.error("[KeyPilot] Cumulative clipboard failed:", error);
        copySuccess = false;
      }
      if (copySuccess) {
        const notificationCopyType = content.hasRichContent ? "Rich text" : "Text";
        this.showFlashNotification(`${notificationCopyType} copied to clipboard`, COLORS.NOTIFICATION_SUCCESS);
        try {
          this.overlayManager.flashFocusOverlay();
        } catch {
        }
      } else {
        this.showFlashNotification("Failed to copy selection", COLORS.NOTIFICATION_ERROR);
      }
    }
    async completeSelection() {
      if (this._completingHighlight) {
        return;
      }
      const selectionMode = this.overlayManager.getSelectionMode();
      if (selectionMode === "character" || selectionMode === "rectangle") {
        try {
          this.updateSelection({ force: true });
        } catch (e) {
          console.warn("[KeyPilot] Error applying latest caret before complete:", e);
        }
      }
      this._completingHighlight = true;
      const currentState = this.state.getState();
      console.log("[KeyPilot] Completing text selection, mode:", selectionMode);
      const exitHighlight = () => {
        try {
          this.cancelHighlightMode();
        } catch (e) {
          console.warn("[KeyPilot] Error exiting highlight mode:", e);
          try {
            this.state.setMode(MODES.NONE);
          } catch {
          }
        }
      };
      try {
        let selectedText = "";
        let contentToClipboard = null;
        if (selectionMode === "element") {
          try {
            const matched = this.overlayManager.getMatchedElements?.() || [];
            contentToClipboard = this.buildElementsClipboardContent(matched);
            selectedText = contentToClipboard.plainText || "";
          } catch (extractError) {
            console.warn("[KeyPilot] Error extracting element selection content:", extractError);
            selectedText = "";
          }
        } else {
          try {
            const selection = this.getCurrentSelectionWithShadowSupport();
            if (selection && typeof selection.toString === "function" && selection.rangeCount > 0) {
              if (FEATURE_FLAGS.ENABLE_RICH_TEXT_CLIPBOARD) {
                try {
                  contentToClipboard = this.extractSelectionContent(selection);
                  selectedText = contentToClipboard.plainText || "";
                } catch (extractError) {
                  console.warn("[KeyPilot] Error extracting rich text content:", extractError);
                  selectedText = selection.toString() || "";
                }
              } else {
                selectedText = selection.toString() || "";
              }
            }
          } catch (selectionError) {
            console.warn("[KeyPilot] Error getting current selection:", selectionError);
          }
          if (!selectedText || !selectedText.trim()) {
            try {
              selectedText = this.overlayManager.peekCharacterSelectedText() || "";
            } catch {
            }
          }
          if (!selectedText || !selectedText.trim()) {
            try {
              const stateSelection = currentState.currentSelection;
              if (stateSelection && typeof stateSelection.toString === "function") {
                selectedText = stateSelection.toString() || "";
              }
            } catch {
            }
          }
          if (selectionMode === "character") {
            const copyAs = getActionMode(this._getBuiltinFunctionActionParams("HIGHLIGHT"), "HIGHLIGHT");
            if (copyAs === "plain" && contentToClipboard && typeof contentToClipboard === "object") {
              selectedText = contentToClipboard.plainText || selectedText;
              contentToClipboard = selectedText;
            }
          }
        }
        exitHighlight();
        if (!selectedText || !String(selectedText).trim()) {
          console.log("[KeyPilot] Empty selection \u2014 exited highlight mode");
          this.showFlashNotification("No text selected", COLORS.NOTIFICATION_INFO);
          return;
        }
        selectedText = String(selectedText);
        if (!contentToClipboard) {
          contentToClipboard = selectedText;
        }
        let copySuccess = false;
        let clipboardError = null;
        try {
          copySuccess = await this.copyToClipboard(contentToClipboard);
        } catch (error) {
          clipboardError = error;
          console.error("[KeyPilot] Clipboard operation threw error:", error);
          copySuccess = false;
        }
        if (copySuccess) {
          const copyType = contentToClipboard && contentToClipboard.hasRichContent ? "rich text" : "plain text";
          const textPreview = typeof contentToClipboard === "string" ? contentToClipboard.substring(0, 50) : (contentToClipboard.plainText || "").substring(0, 50);
          console.log(`[KeyPilot] Content copied (${copyType}):`, textPreview);
          const notificationCopyType = contentToClipboard && contentToClipboard.hasRichContent ? "Rich text" : "Text";
          this.showFlashNotification(`${notificationCopyType} copied to clipboard`, COLORS.NOTIFICATION_SUCCESS);
          try {
            this.overlayManager.flashFocusOverlay();
          } catch (flashError) {
            console.warn("[KeyPilot] Error flashing focus overlay:", flashError);
          }
        } else {
          console.warn("[KeyPilot] Failed to copy text to clipboard");
          let errorMessage = "Failed to copy text";
          if (clipboardError) {
            if (clipboardError.name === "NotAllowedError" || clipboardError.message?.includes("permission")) {
              errorMessage = "Clipboard access denied - check browser permissions";
            } else if (clipboardError.message?.includes("not supported")) {
              errorMessage = "Clipboard not supported in this context";
            } else if (clipboardError.message?.includes("secure context")) {
              errorMessage = "Clipboard requires secure connection (HTTPS)";
            }
          }
          this.showFlashNotification(errorMessage, COLORS.NOTIFICATION_ERROR);
        }
      } catch (error) {
        console.error("[KeyPilot] Unexpected error completing selection:", error);
        try {
          exitHighlight();
        } catch {
        }
        let errorMessage = "Error copying text";
        if (error?.message?.includes("Selection API")) {
          errorMessage = "Text selection not supported on this page";
        } else if (error?.message?.includes("shadow")) {
          errorMessage = "Cannot copy text from this element";
        }
        this.showFlashNotification(errorMessage, COLORS.NOTIFICATION_ERROR);
      } finally {
        this._completingHighlight = false;
      }
    }
    /**
     * Get current selection with comprehensive shadow DOM support and error handling
     * @returns {Selection|null} - Current selection or null
     */
    getCurrentSelectionWithShadowSupport() {
      try {
        let mainSelection = null;
        try {
          if (window && typeof window.getSelection === "function") {
            mainSelection = window.getSelection();
            if (mainSelection && typeof mainSelection.rangeCount === "number" && typeof mainSelection.toString === "function") {
              if (mainSelection.rangeCount > 0) {
                const selectionText = mainSelection.toString();
                if (selectionText && selectionText.trim()) {
                  console.log("[KeyPilot] Found valid main document selection");
                  return mainSelection;
                }
              }
            }
          }
        } catch (mainSelectionError) {
          console.warn("[KeyPilot] Error accessing main document selection:", mainSelectionError);
        }
        try {
          const currentState = this.state.getState();
          if (currentState && currentState.currentSelection) {
            const stateSelection = currentState.currentSelection;
            if (stateSelection && typeof stateSelection.toString === "function" && typeof stateSelection.rangeCount === "number") {
              const stateSelectionText = stateSelection.toString();
              if (stateSelectionText && stateSelectionText.trim()) {
                console.log("[KeyPilot] Found valid stored selection from state");
                return stateSelection;
              }
            }
          }
        } catch (stateSelectionError) {
          console.warn("[KeyPilot] Error accessing stored selection from state:", stateSelectionError);
        }
        try {
          const shadowSelection = this.findSelectionInShadowDOM();
          if (shadowSelection) {
            console.log("[KeyPilot] Found valid shadow DOM selection");
            return shadowSelection;
          }
        } catch (shadowSelectionError) {
          console.warn("[KeyPilot] Error finding selection in shadow DOM:", shadowSelectionError);
        }
        console.log("[KeyPilot] No valid selection found in any context");
        return null;
      } catch (error) {
        console.error("[KeyPilot] Unexpected error getting current selection:", error);
        return null;
      }
    }
    /**
     * Find selection in shadow DOM contexts with comprehensive error handling
     * @returns {Selection|null} - Selection found in shadow DOM or null
     */
    findSelectionInShadowDOM() {
      try {
        if (!this.shadowDOMManager) {
          console.log("[KeyPilot] Shadow DOM manager not available");
          return null;
        }
        const shadowRoots = this.shadowDOMManager.shadowRoots;
        if (!shadowRoots || !(shadowRoots instanceof Set) || shadowRoots.size === 0) {
          console.log("[KeyPilot] No shadow roots available for selection search");
          return null;
        }
        let i = 0;
        for (const shadowRoot of shadowRoots) {
          try {
            if (!shadowRoot) {
              console.warn(`[KeyPilot] Shadow root at index ${i} is null or undefined`);
              i++;
              continue;
            }
            if (typeof shadowRoot.getSelection !== "function") {
              i++;
              continue;
            }
            let shadowSelection = null;
            try {
              shadowSelection = shadowRoot.getSelection();
            } catch (getSelectionError) {
              console.warn(`[KeyPilot] Error calling getSelection on shadow root ${i}:`, getSelectionError);
              i++;
              continue;
            }
            if (!shadowSelection) {
              i++;
              continue;
            }
            if (typeof shadowSelection.rangeCount !== "number" || typeof shadowSelection.toString !== "function") {
              console.warn(`[KeyPilot] Shadow selection at index ${i} missing required methods`);
              i++;
              continue;
            }
            if (shadowSelection.rangeCount > 0) {
              let selectionText = "";
              try {
                selectionText = shadowSelection.toString();
              } catch (toStringError) {
                console.warn(`[KeyPilot] Error getting text from shadow selection ${i}:`, toStringError);
                i++;
                continue;
              }
              if (selectionText && selectionText.trim()) {
                console.log(`[KeyPilot] Found valid selection in shadow root ${i}`);
                return shadowSelection;
              }
            }
          } catch (shadowRootError) {
            console.warn(`[KeyPilot] Error processing shadow root ${i}:`, shadowRootError);
            i++;
            continue;
          }
          i++;
        }
        console.log("[KeyPilot] No valid selection found in any shadow DOM context");
        return null;
      } catch (error) {
        console.error("[KeyPilot] Unexpected error finding selection in shadow DOM:", error);
        return null;
      }
    }
    /**
     * Extract both plain text and HTML content from a selection
     * @param {Selection} selection - The selection object to extract content from
     * @returns {Object} - Object containing both plainText and htmlContent
     */
    extractSelectionContent(selection) {
      try {
        if (!selection || typeof selection.rangeCount !== "number" || selection.rangeCount === 0) {
          throw new Error("Invalid or empty selection");
        }
        const plainText = selection.toString();
        let htmlContent = "";
        if (FEATURE_FLAGS.ENABLE_RICH_TEXT_CLIPBOARD) {
          try {
            const container = document.createElement("div");
            for (let i = 0; i < selection.rangeCount; i++) {
              const range = selection.getRangeAt(i);
              const clonedContent = range.cloneContents();
              container.appendChild(clonedContent);
            }
            htmlContent = container.innerHTML;
            htmlContent = this.sanitizeHtmlContent(htmlContent);
          } catch (htmlError) {
            console.warn("[KeyPilot] Failed to extract HTML content:", htmlError);
            htmlContent = "";
          }
        }
        return {
          plainText,
          htmlContent,
          hasRichContent: htmlContent.length > 0 && htmlContent !== plainText
        };
      } catch (error) {
        console.error("[KeyPilot] Error extracting selection content:", error);
        return {
          plainText: "",
          htmlContent: "",
          hasRichContent: false
        };
      }
    }
    /**
     * Sanitize HTML content to remove potentially harmful elements
     * @param {string} html - Raw HTML content
     * @returns {string} - Sanitized HTML content
     */
    sanitizeHtmlContent(html) {
      if (!html || typeof html !== "string") {
        return "";
      }
      try {
        const temp = document.createElement("div");
        temp.innerHTML = html;
        const dangerousElements = temp.querySelectorAll("script, object, embed, iframe, form, input, button");
        dangerousElements.forEach((el2) => el2.remove());
        const allElements = temp.querySelectorAll("*");
        allElements.forEach((el2) => {
          const attributes = Array.from(el2.attributes);
          attributes.forEach((attr) => {
            if (attr.name.startsWith("on") || attr.value.includes("javascript:")) {
              el2.removeAttribute(attr.name);
            }
          });
        });
        return temp.innerHTML;
      } catch (error) {
        console.warn("[KeyPilot] Error sanitizing HTML content:", error);
        return html;
      }
    }
    /**
     * Copy text to clipboard using modern Clipboard API with comprehensive fallback methods and error handling
     * @param {string|Object} content - Text to copy, or object with plainText and htmlContent
     * @returns {Promise<boolean>} - True if copy was successful, false otherwise
     */
    async copyToClipboard(content) {
      let plainText, htmlContent, hasRichContent;
      if (typeof content === "string") {
        plainText = content;
        htmlContent = "";
        hasRichContent = false;
      } else if (content && typeof content === "object") {
        plainText = content.plainText || "";
        htmlContent = content.htmlContent || "";
        hasRichContent = content.hasRichContent || false;
      } else {
        console.warn("[KeyPilot] Invalid content provided to copyToClipboard:", typeof content);
        return false;
      }
      if (!plainText) {
        console.warn("[KeyPilot] No plain text content provided to copyToClipboard");
        return false;
      }
      if (plainText.length === 0) {
        console.warn("[KeyPilot] Empty plain text content provided to copyToClipboard");
        return false;
      }
      try {
        const encoded = encodeURIComponent(plainText);
        if (!encoded) {
          throw new Error("Text contains invalid characters");
        }
      } catch (encodingError) {
        console.warn("[KeyPilot] Text encoding validation failed:", encodingError);
        return false;
      }
      if (navigator.clipboard) {
        try {
          if (!window.isSecureContext) {
            console.warn("[KeyPilot] Not in secure context, modern Clipboard API may fail");
          }
          let clipboardPromise;
          if (hasRichContent && htmlContent && navigator.clipboard.write) {
            try {
              const clipboardItems = [];
              const clipboardItem = new ClipboardItem({
                "text/plain": new Blob([plainText], { type: "text/plain" }),
                "text/html": new Blob([htmlContent], { type: "text/html" })
              });
              clipboardItems.push(clipboardItem);
              clipboardPromise = navigator.clipboard.write(clipboardItems);
              console.log("[KeyPilot] Attempting to copy rich text (HTML + plain text)");
            } catch (richTextError) {
              console.warn("[KeyPilot] Rich text clipboard failed, falling back to plain text:", richTextError);
              if (typeof navigator.clipboard.writeText === "function") {
                clipboardPromise = navigator.clipboard.writeText(plainText);
              } else {
                throw new Error("Neither rich text nor plain text clipboard API available");
              }
            }
          } else {
            if (typeof navigator.clipboard.writeText === "function") {
              clipboardPromise = navigator.clipboard.writeText(plainText);
              console.log("[KeyPilot] Attempting to copy plain text");
            } else {
              throw new Error("Plain text clipboard API not available");
            }
          }
          const timeoutPromise = new Promise((_, reject) => {
            setTimeout(() => reject(new Error("Clipboard operation timed out")), 5e3);
          });
          await Promise.race([clipboardPromise, timeoutPromise]);
          const copyType = hasRichContent && htmlContent ? "rich text (HTML + plain text)" : "plain text";
          console.log(`[KeyPilot] Content copied using modern Clipboard API (${copyType})`);
          return true;
        } catch (error) {
          console.warn("[KeyPilot] Modern Clipboard API failed:", error.message);
          if (error.name === "NotAllowedError") {
            console.warn("[KeyPilot] Clipboard permission denied");
          } else if (error.name === "NotSupportedError") {
            console.warn("[KeyPilot] Clipboard API not supported");
          } else if (error.message.includes("secure context")) {
            console.warn("[KeyPilot] Clipboard requires secure context (HTTPS)");
          } else if (error.message.includes("timed out")) {
            console.warn("[KeyPilot] Clipboard operation timed out");
          } else if (error.message.includes("permission")) {
            console.warn("[KeyPilot] Clipboard permission issue");
          }
        }
      } else {
        console.log("[KeyPilot] Modern Clipboard API not available, using fallback method");
      }
      let textarea = null;
      try {
        if (!document || !document.body) {
          throw new Error("Document or document.body not available");
        }
        if (typeof document.execCommand !== "function") {
          throw new Error("execCommand not available");
        }
        let tempElement2;
        if (hasRichContent && htmlContent && FEATURE_FLAGS.ENABLE_RICH_TEXT_CLIPBOARD) {
          tempElement2 = document.createElement("div");
          if (!tempElement2) {
            throw new Error("Failed to create div element for rich text");
          }
          try {
            tempElement2.innerHTML = htmlContent;
            tempElement2.style.position = "fixed";
            tempElement2.style.left = "-9999px";
            tempElement2.style.top = "-9999px";
            tempElement2.style.width = "1px";
            tempElement2.style.height = "1px";
            tempElement2.style.opacity = "0";
            tempElement2.style.pointerEvents = "none";
            tempElement2.style.zIndex = "-1";
            tempElement2.setAttribute("tabindex", "-1");
            tempElement2.setAttribute("aria-hidden", "true");
            console.log("[KeyPilot] Using div element for rich text fallback");
          } catch (styleError) {
            throw new Error(`Failed to set div properties: ${styleError.message}`);
          }
        } else {
          tempElement2 = document.createElement("textarea");
          if (!tempElement2) {
            throw new Error("Failed to create textarea element");
          }
          try {
            tempElement2.value = plainText;
            tempElement2.style.position = "fixed";
            tempElement2.style.left = "-9999px";
            tempElement2.style.top = "-9999px";
            tempElement2.style.width = "1px";
            tempElement2.style.height = "1px";
            tempElement2.style.opacity = "0";
            tempElement2.style.pointerEvents = "none";
            tempElement2.style.zIndex = "-1";
            tempElement2.setAttribute("readonly", "");
            tempElement2.setAttribute("tabindex", "-1");
            tempElement2.setAttribute("aria-hidden", "true");
            console.log("[KeyPilot] Using textarea element for plain text fallback");
          } catch (styleError) {
            throw new Error(`Failed to set textarea properties: ${styleError.message}`);
          }
        }
        textarea = tempElement2;
        try {
          document.body.appendChild(tempElement2);
        } catch (appendError) {
          throw new Error(`Failed to append element to DOM: ${appendError.message}`);
        }
        try {
          tempElement2.focus();
          if (hasRichContent && htmlContent && tempElement2.tagName === "DIV") {
            const range = document.createRange();
            range.selectNodeContents(tempElement2);
            const selection = window.getSelection();
            selection.removeAllRanges();
            selection.addRange(range);
            console.log("[KeyPilot] Selected rich content in div element");
          } else {
            tempElement2.select();
            if (typeof tempElement2.setSelectionRange === "function") {
              tempElement2.setSelectionRange(0, plainText.length);
            }
            if (tempElement2.selectionStart !== 0 || tempElement2.selectionEnd !== plainText.length) {
              console.warn("[KeyPilot] Text selection in textarea may be incomplete");
            }
          }
        } catch (selectionError) {
          throw new Error(`Failed to select content: ${selectionError.message}`);
        }
        let success = false;
        try {
          success = document.execCommand("copy");
        } catch (execError) {
          throw new Error(`execCommand failed: ${execError.message}`);
        }
        try {
          if (tempElement2 && tempElement2.parentNode) {
            document.body.removeChild(tempElement2);
          }
        } catch (cleanupError) {
          console.warn("[KeyPilot] Error cleaning up temporary element:", cleanupError);
        }
        if (success) {
          const copyType = hasRichContent && htmlContent ? "rich text" : "plain text";
          console.log(`[KeyPilot] Content copied using fallback execCommand method (${copyType})`);
          return true;
        } else {
          throw new Error("execCommand returned false");
        }
      } catch (error) {
        console.error("[KeyPilot] Fallback clipboard method failed:", error);
        try {
          if (tempElement && tempElement.parentNode) {
            document.body.removeChild(tempElement);
          }
        } catch (cleanupError) {
          console.warn("[KeyPilot] Error cleaning up temporary element after failure:", cleanupError);
        }
        return false;
      }
    }
    /**
     * Copy image under the cursor (I on right-handed layout; E on left).
     * Destination is clipboard (default), Media Library, or both.
     * @param {KeyboardEvent} [_e]
     * @param {{ destination?: string }} [parameters]
     */
    async handleCopyHoveredImageKey(_e, parameters) {
      const flags = destinationFlags(this._resolveCopyDestination("COPY_HOVERED_IMAGE", parameters));
      const currentState = this.state.getState();
      const x = Number(currentState?.lastMouse?.x);
      const y = Number(currentState?.lastMouse?.y);
      if (!Number.isFinite(x) || !Number.isFinite(y)) {
        this.showFlashNotification("No image under cursor", COLORS.NOTIFICATION_INFO);
        return;
      }
      let result = null;
      try {
        result = await getHoveredImage(x, y);
      } catch (error) {
        console.warn("[KeyPilot] getHoveredImage failed:", error);
        this.showFlashNotification("Could not copy image", COLORS.NOTIFICATION_ERROR);
        return;
      }
      if (!result?.blob) {
        this.showFlashNotification("No image under cursor", COLORS.NOTIFICATION_INFO);
        return;
      }
      const label = result.kind === "background" ? "Background image" : result.kind === "svg" ? "SVG" : result.kind === "video" ? "Video thumbnail" : "Image";
      let copied = false;
      let copyError = "";
      if (flags.clipboard) {
        try {
          copied = await this.copyImageToClipboard(result.blob, result.mimeType);
          if (copied) {
            try {
              this.overlayManager?.flashImageCopyPulse?.(result.element);
            } catch {
            }
          } else {
            copyError = "Could not copy image";
          }
        } catch (error) {
          console.warn("[KeyPilot] copyImageToClipboard failed:", error);
          copyError = "Could not copy image";
          if (error?.name === "NotAllowedError" || /permission/i.test(error?.message || "")) {
            copyError = "Clipboard permission denied";
          } else if (/secure context/i.test(error?.message || "")) {
            copyError = "Clipboard requires HTTPS";
          }
        }
      }
      let saved = null;
      if (flags.mediaLibrary) {
        saved = await this._persistImageToMediaLibrary({
          blob: result.blob,
          mimeType: result.mimeType,
          url: result.url,
          kind: result.kind
        });
      }
      if (flags.clipboard && flags.mediaLibrary && copied && (saved?.success || saved?.duplicate)) {
        this.showFlashNotification(
          saved?.duplicate ? `${label} copied; already in Media Library` : `${label} copied and saved to Media Library`,
          saved?.duplicate ? COLORS.NOTIFICATION_INFO : COLORS.NOTIFICATION_SUCCESS,
          result.blob
        );
      } else {
        if (flags.clipboard) {
          this.showFlashNotification(
            copied ? `${label} copied to clipboard` : copyError || "Could not copy image",
            copied ? COLORS.NOTIFICATION_SUCCESS : COLORS.NOTIFICATION_ERROR,
            copied ? result.blob : void 0
          );
        }
        if (flags.mediaLibrary) {
          this._notifyMediaLibrarySave(saved, {
            blob: result.blob,
            kind: result.kind,
            url: result.url
          });
        }
      }
      if (copied) {
        this.emitAction("copy_hovered_image", {
          kind: result.kind,
          url: result.url ? String(result.url).slice(0, 200) : ""
        });
      }
      if (saved?.success && !saved?.duplicate) {
        try {
          this.emitAction("media_library_add", {
            kind: result.kind || "image",
            url: result.url ? String(result.url).slice(0, 200) : ""
          });
        } catch {
        }
      }
    }
    /**
     * Copy the hyperlink under the cursor (U on right-handed layout).
     * Destination is clipboard (default), Media Library, or both.
     * @param {KeyboardEvent} [_e]
     * @param {{ destination?: string }} [parameters]
     */
    async handleCopyHoveredUrlKey(_e, parameters) {
      const flags = destinationFlags(this._resolveCopyDestination("COPY_HOVERED_URL", parameters));
      const url = this._getHoveredHyperlinkUrl();
      if (!url) {
        this.showFlashNotification("No URL under cursor", COLORS.NOTIFICATION_INFO);
        return;
      }
      let copied = false;
      if (flags.clipboard) {
        copied = await this.copyToClipboard(url);
        if (copied) this.emitAction("copy_hovered_url", { url: String(url).slice(0, 200) });
      }
      let saved = null;
      if (flags.mediaLibrary) {
        saved = await this._persistUrlToMediaLibrary(url);
      }
      if (flags.clipboard && flags.mediaLibrary && copied && (saved?.success || saved?.duplicate)) {
        this.showFlashNotification(
          saved?.duplicate ? "URL copied; already in Media Library" : "URL copied and saved to Media Library",
          saved?.duplicate ? COLORS.NOTIFICATION_INFO : COLORS.NOTIFICATION_SUCCESS
        );
        return;
      }
      if (flags.clipboard) {
        this.showFlashNotification(
          copied ? "URL copied to clipboard" : "Could not copy URL",
          copied ? COLORS.NOTIFICATION_SUCCESS : COLORS.NOTIFICATION_ERROR
        );
      }
      if (flags.mediaLibrary) {
        this._notifyUrlLibrarySave(saved);
      }
    }
    /**
     * Copy the <video> under the cursor (Actions Library — not on built-in layouts).
     * Default destination is Media Library (file bytes when fetchable). Clipboard gets the URL.
     * @param {KeyboardEvent} [_e]
     * @param {{ destination?: string }} [parameters]
     */
    async handleCopyHoveredVideoKey(_e, parameters) {
      const flags = destinationFlags(this._resolveCopyDestination("COPY_HOVERED_VIDEO", parameters));
      const currentState = this.state.getState();
      const x = Number(currentState?.lastMouse?.x);
      const y = Number(currentState?.lastMouse?.y);
      if (!Number.isFinite(x) || !Number.isFinite(y)) {
        this.showFlashNotification("No video under cursor", COLORS.NOTIFICATION_INFO);
        return;
      }
      let result = null;
      try {
        result = await getHoveredVideo(x, y);
      } catch (error) {
        console.warn("[KeyPilot] getHoveredVideo failed:", error);
        this.showFlashNotification("Could not copy video", COLORS.NOTIFICATION_ERROR);
        return;
      }
      if (!result) {
        this.showFlashNotification("No video under cursor", COLORS.NOTIFICATION_INFO);
        return;
      }
      const videoUrl = String(result.currentSrc || result.fileUrl || "").trim();
      const thumb = result.thumbBlob instanceof Blob && result.thumbBlob.size > 0 ? result.thumbBlob : null;
      let copied = false;
      let copyError = "";
      if (flags.clipboard) {
        if (videoUrl) {
          copied = await this.copyToClipboard(videoUrl);
          if (!copied) copyError = "Could not copy video URL";
        } else {
          copyError = "No video URL under cursor";
        }
      }
      let saved = null;
      if (flags.mediaLibrary) {
        saved = await this._persistVideoToMediaLibrary(result);
      }
      if (flags.clipboard && flags.mediaLibrary && copied && (saved?.success || saved?.duplicate)) {
        this.showFlashNotification(
          saved?.duplicate ? "Video URL copied; already in Media Library" : "Video URL copied and saved to Media Library",
          saved?.duplicate ? COLORS.NOTIFICATION_INFO : COLORS.NOTIFICATION_SUCCESS,
          thumb || void 0
        );
      } else {
        if (flags.clipboard) {
          this.showFlashNotification(
            copied ? "Video URL copied to clipboard" : copyError || "Could not copy video URL",
            copied ? COLORS.NOTIFICATION_SUCCESS : COLORS.NOTIFICATION_ERROR
          );
        }
        if (flags.mediaLibrary) {
          this._notifyVideoLibrarySave(saved, thumb);
        }
      }
      if (copied) {
        this.emitAction("copy_hovered_video", {
          url: videoUrl ? videoUrl.slice(0, 200) : ""
        });
      }
      if (saved?.success && !saved?.duplicate) {
        try {
          this.emitAction("media_library_add", {
            kind: "video",
            url: videoUrl ? videoUrl.slice(0, 200) : ""
          });
        } catch {
        }
      }
    }
    /**
     * @param {string} functionId
     * @param {{ destination?: string }|null|undefined} parameters
     * @returns {import('./modules/action-result-delivery.js').ActionResultDestination}
     */
    _resolveCopyDestination(functionId, parameters) {
      const fallback = functionId === "COPY_HOVERED_VIDEO" ? ACTION_RESULT_DESTINATIONS.MEDIA_LIBRARY : ACTION_RESULT_DESTINATIONS.CLIPBOARD;
      return normalizeActionResultDestination(
        parameters?.destination ?? getActionParameter(this._getBuiltinFunctionActionParams(functionId), functionId, "destination"),
        fallback
      );
    }
    /**
     * @param {Element|null|undefined} target
     * @returns {{ url: string, link: Element }|null}
     */
    _resolveHoveredLink(target) {
      try {
        if (target instanceof Element && this.detector?.resolveHoveredLink) {
          return this.detector.resolveHoveredLink(target);
        }
      } catch {
      }
      return null;
    }
    /**
     * Absolute href of the link under the cursor (including shadow-host walk + data-kp-url rows).
     * @returns {string}
     */
    _getHoveredHyperlinkUrl() {
      const currentState = this.state.getState();
      const lastMouse = currentState?.lastMouse;
      let target = currentState?.focusEl;
      if (!target && lastMouse) {
        const under = this.detector.deepElementFromPoint(lastMouse.x, lastMouse.y);
        target = this.detector.findClickable(under) || under;
      }
      if (target instanceof Element) {
        const resolved = this._resolveHoveredLink(target);
        if (resolved?.url) return resolved.url;
      }
      const x = Number(lastMouse?.x);
      const y = Number(lastMouse?.y);
      if (Number.isFinite(x) && Number.isFinite(y)) {
        return String(getTextAtPoint(x, y, { granularity: "hyperlink" }).text || "").trim();
      }
      return "";
    }
    /**
     * Page Media (O) — scan the page for images, videos, documents, fonts, and URLs;
     * open a tabbed overlay gallery. Press O again (or Esc) to close.
     * @param {KeyboardEvent} [e]
     */
    handlePageMediaKey(e) {
      if (!this._allowActionKey("handlePageMediaKey", e)) return;
      if (isPageMediaOverlayOpen()) {
        closePageMediaOverlay();
        return;
      }
      let items = [];
      try {
        items = collectPageMedia(document);
      } catch (error) {
        console.warn("[KeyPilot] collectPageMedia failed:", error);
        this.showFlashNotification("Could not scan page media", COLORS.NOTIFICATION_ERROR);
        return;
      }
      if (!items.length) {
        this.showFlashNotification("No page media found", COLORS.NOTIFICATION_INFO);
        return;
      }
      try {
        void openPageMediaOverlay({
          items,
          onNotify: (message, type2) => {
            const color4 = type2 === "error" ? COLORS.NOTIFICATION_ERROR : type2 === "success" ? COLORS.NOTIFICATION_SUCCESS : COLORS.NOTIFICATION_INFO;
            this.showFlashNotification(String(message || ""), color4);
          },
          onSendToMediaLibrary: async (item) => {
            await this._sendPageMediaItemToLibrary(item);
          }
        }).catch((error) => {
          console.warn("[KeyPilot] openPageMediaOverlay failed:", error);
          this.showFlashNotification("Could not open Page Media", COLORS.NOTIFICATION_ERROR);
        });
        this.emitAction("page_media", { count: items.length });
      } catch (error) {
        console.warn("[KeyPilot] openPageMediaOverlay failed:", error);
        this.showFlashNotification("Could not open Page Media", COLORS.NOTIFICATION_ERROR);
      }
    }
    /**
     * Best-effort plain text from KeyPilot highlight selection or native selection.
     * @returns {string}
     */
    getSelectedPlainText() {
      try {
        const unitText = this.unitSelection?.getPlainText?.();
        if (unitText && String(unitText).trim()) return String(unitText);
      } catch {
      }
      try {
        const peeked = this.overlayManager?.peekCharacterSelectedText?.();
        if (peeked && String(peeked).trim()) return String(peeked);
      } catch {
      }
      try {
        const sel = typeof window.getSelection === "function" ? window.getSelection() : null;
        const t = sel ? String(sel.toString() || "") : "";
        if (t.trim()) return t;
      } catch {
      }
      try {
        const el2 = document.activeElement;
        if (el2 && (el2.tagName === "INPUT" || el2.tagName === "TEXTAREA") && typeof el2.selectionStart === "number") {
          const v = String(el2.value || "");
          const start = el2.selectionStart;
          const end = el2.selectionEnd;
          if (Number.isFinite(start) && Number.isFinite(end) && end > start) {
            return v.slice(start, end);
          }
        }
        if (el2 && el2.isContentEditable) {
          const sel = window.getSelection?.();
          const t = sel ? String(sel.toString() || "") : "";
          if (t.trim()) return t;
        }
      } catch {
      }
      try {
        const shadowSel = this.findSelectionInShadowDOM?.();
        const t = shadowSel && typeof shadowSel.toString === "function" ? String(shadowSel.toString() || "") : "";
        if (t.trim()) return t;
      } catch {
      }
      return "";
    }
    /** @returns {boolean} */
    _execDocumentCommand(command) {
      try {
        if (typeof document.execCommand !== "function") return false;
        return !!document.execCommand(command);
      } catch {
        return false;
      }
    }
    /**
     * Insert plain text into the focused editable (paste helper when execCommand fails).
     * @param {string} text
     * @returns {boolean}
     */
    _insertTextAtFocus(text) {
      const value = String(text ?? "");
      const el2 = document.activeElement;
      if (!el2) return false;
      try {
        if (el2.tagName === "INPUT" || el2.tagName === "TEXTAREA") {
          if (el2.disabled || el2.readOnly) return false;
          const start = Number.isFinite(el2.selectionStart) ? el2.selectionStart : String(el2.value || "").length;
          const end = Number.isFinite(el2.selectionEnd) ? el2.selectionEnd : start;
          const before = String(el2.value || "").slice(0, start);
          const after = String(el2.value || "").slice(end);
          el2.value = before + value + after;
          const caret = start + value.length;
          try {
            el2.setSelectionRange(caret, caret);
          } catch {
          }
          try {
            el2.dispatchEvent(new Event("input", { bubbles: true }));
          } catch {
          }
          return true;
        }
      } catch {
      }
      try {
        if (el2.isContentEditable) {
          if (typeof document.execCommand === "function") {
            const ok = document.execCommand("insertText", false, value);
            if (ok) return true;
          }
        }
      } catch {
      }
      return false;
    }
    async handleClipboardCopyKey() {
      if (this.unitSelection?.hasItems?.()) {
        return this._copyUnitSelection({ cut: false });
      }
      if (this._execDocumentCommand("copy")) {
        this.showFlashNotification("Copied", COLORS.NOTIFICATION_SUCCESS);
        this.emitAction("clipboard_copy", { via: "execCommand" });
        return true;
      }
      const text = this.getSelectedPlainText();
      if (!text.trim()) {
        this.showFlashNotification("Nothing selected to copy", COLORS.NOTIFICATION_INFO);
        return;
      }
      const ok = await this.copyToClipboard(text);
      this.showFlashNotification(
        ok ? "Copied" : "Could not copy",
        ok ? COLORS.NOTIFICATION_SUCCESS : COLORS.NOTIFICATION_ERROR
      );
      if (ok) {
        this.emitAction("clipboard_copy", { via: "clipboardApi", length: text.length });
        return text;
      }
    }
    async handleClipboardCutKey() {
      if (this.unitSelection?.hasItems?.()) {
        return this._copyUnitSelection({ cut: true });
      }
      if (this._execDocumentCommand("cut")) {
        this.showFlashNotification("Cut", COLORS.NOTIFICATION_SUCCESS);
        this.emitAction("clipboard_cut", { via: "execCommand" });
        return true;
      }
      const text = this.getSelectedPlainText();
      if (!text.trim()) {
        this.showFlashNotification("Nothing selected to cut", COLORS.NOTIFICATION_INFO);
        return;
      }
      const ok = await this.copyToClipboard(text);
      if (ok) {
        try {
          this._execDocumentCommand("delete");
        } catch {
        }
        this.showFlashNotification("Cut", COLORS.NOTIFICATION_SUCCESS);
        this.emitAction("clipboard_cut", { via: "clipboardApi", length: text.length });
        return text;
      }
      this.showFlashNotification("Could not cut", COLORS.NOTIFICATION_ERROR);
    }
    async handleClipboardPasteKey() {
      if (this._execDocumentCommand("paste")) {
        this.showFlashNotification("Pasted", COLORS.NOTIFICATION_SUCCESS);
        this.emitAction("clipboard_paste", { via: "execCommand" });
        return;
      }
      let text = "";
      try {
        if (navigator.clipboard && typeof navigator.clipboard.readText === "function") {
          text = await navigator.clipboard.readText();
        }
      } catch (e) {
        console.warn("[KeyPilot] clipboard.readText failed:", e);
      }
      if (!String(text || "").length) {
        this.showFlashNotification("Clipboard is empty or paste blocked", COLORS.NOTIFICATION_INFO);
        return;
      }
      const ok = this._insertTextAtFocus(text);
      this.showFlashNotification(
        ok ? "Pasted" : "Focus an editable field to paste",
        ok ? COLORS.NOTIFICATION_SUCCESS : COLORS.NOTIFICATION_INFO
      );
      if (ok) this.emitAction("clipboard_paste", { via: "clipboardApi", length: text.length });
    }
    handleClipboardSelectAllKey() {
      if (this._execDocumentCommand("selectAll")) {
        this.emitAction("clipboard_select_all", { via: "execCommand" });
        return;
      }
      try {
        const el2 = document.activeElement;
        if (el2 && (el2.tagName === "INPUT" || el2.tagName === "TEXTAREA") && typeof el2.select === "function") {
          el2.select();
          this.emitAction("clipboard_select_all", { via: "input.select" });
          return;
        }
      } catch {
      }
      this.showFlashNotification("Could not select all", COLORS.NOTIFICATION_INFO);
    }
    /**
     * @returns {{ x: number, y: number }|null}
     */
    _lastMousePoint() {
      const st = this.state.getState();
      const x = Number(st?.lastMouse?.x);
      const y = Number(st?.lastMouse?.y);
      if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
      return { x, y };
    }
    /**
     * Copy KeyPilot unit selection (Select Word / Sentence / Paragraph / Image).
     * Cut copies the bag but does not delete page content.
     * @param {{ cut?: boolean }} [opts]
     */
    async _copyUnitSelection(opts = {}) {
      const bag = this.unitSelection;
      if (!bag?.hasItems?.()) {
        this.showFlashNotification("Nothing selected to copy", COLORS.NOTIFICATION_INFO);
        return;
      }
      if (bag.isImagesOnly()) {
        const images = bag.getImageItems();
        if (images.length === 1 && images[0].element) {
          const el2 = images[0].element;
          let result = null;
          try {
            const rect = el2.getBoundingClientRect();
            result = await getHoveredImage(rect.left + rect.width / 2, rect.top + rect.height / 2);
          } catch (error) {
            console.warn("[KeyPilot] unit image copy failed:", error);
          }
          if (result?.blob) {
            const ok2 = await this.copyImageToClipboard(result.blob, result.mimeType);
            this.showFlashNotification(
              ok2 ? opts.cut ? "Copied (page not changed)" : "Copied" : "Could not copy",
              ok2 ? COLORS.NOTIFICATION_SUCCESS : COLORS.NOTIFICATION_ERROR
            );
            if (ok2) {
              this.emitAction(opts.cut ? "clipboard_cut" : "clipboard_copy", {
                via: "unitSelection",
                kind: "image"
              });
            }
            return ok2;
          }
        }
      }
      const content = bag.getClipboardContent();
      if (!String(content.plainText || "").trim()) {
        this.showFlashNotification("Nothing selected to copy", COLORS.NOTIFICATION_INFO);
        return;
      }
      const ok = await this.copyToClipboard(content);
      this.showFlashNotification(
        ok ? opts.cut ? "Copied (page not changed)" : "Copied" : "Could not copy",
        ok ? COLORS.NOTIFICATION_SUCCESS : COLORS.NOTIFICATION_ERROR
      );
      if (ok) {
        this.emitAction(opts.cut ? "clipboard_cut" : "clipboard_copy", {
          via: "unitSelection",
          length: content.plainText.length
        });
        return content.plainText;
      }
    }
    /**
     * @param {'word'|'sentence'|'paragraph'} kind
     * @param {Record<string, any>|undefined} [_parameters]
     * @param {string} functionId
     */
    _toggleTextUnit(kind, _parameters, functionId) {
      const pt = this._lastMousePoint();
      if (!pt) {
        this.showFlashNotification("No cursor position available", COLORS.NOTIFICATION_INFO);
        return;
      }
      const { text, range } = getTextAtPoint(pt.x, pt.y, { granularity: kind });
      const unit = unitFromTextRange(kind, range, text);
      if (!unit) {
        this.showFlashNotification(`No ${kind} under cursor`, COLORS.NOTIFICATION_INFO);
        return;
      }
      const params = this._getBuiltinFunctionActionParams(functionId);
      const mode = getActionMode(params, functionId) === "cumulative" ? "cumulative" : "exclusive";
      const result = this.unitSelection.toggle(unit, mode);
      this.emitAction("select_unit", { kind, mode, added: result.added, removed: result.removed, count: result.count });
    }
    handleSelectWordKey(_e, parameters) {
      this._toggleTextUnit("word", parameters, "SELECT_WORD");
    }
    handleSelectSentenceKey(_e, parameters) {
      this._toggleTextUnit("sentence", parameters, "SELECT_SENTENCE");
    }
    handleSelectParagraphKey(_e, parameters) {
      this._toggleTextUnit("paragraph", parameters, "SELECT_PARAGRAPH");
    }
    handleSelectImageKey(_e, _parameters) {
      const pt = this._lastMousePoint();
      if (!pt) {
        this.showFlashNotification("No cursor position available", COLORS.NOTIFICATION_INFO);
        return;
      }
      const source = findHoveredImageSource(pt.x, pt.y);
      const unit = source?.element ? unitFromImageElement(source.element, source.url) : null;
      if (!unit) {
        this.showFlashNotification("No image under cursor", COLORS.NOTIFICATION_INFO);
        return;
      }
      const params = this._getBuiltinFunctionActionParams("SELECT_IMAGE");
      const mode = getActionMode(params, "SELECT_IMAGE") === "cumulative" ? "cumulative" : "exclusive";
      const result = this.unitSelection.toggle(unit, mode);
      this.emitAction("select_unit", { kind: "image", mode, added: result.added, removed: result.removed, count: result.count });
    }
    /**
     * TYPE_CHARACTERS Function handler — types the Action Instance's configured `text` into
     * the focused editable each time the key is pressed. Multiple keys can each hold their own
     * Action Instance of this same Function with different `text`, since the value lives on the
     * instance (see keyboard-layout-store.js `UserAction`), not on the Function definition.
     * @param {KeyboardEvent} _e
     * @param {{ text?: string }} [parameters]
     */
    handleTypeCharactersKey(_e, parameters) {
      const text = String(parameters?.text ?? "");
      if (!text) {
        this.showFlashNotification("No text configured for this key", COLORS.NOTIFICATION_INFO);
        return;
      }
      const ok = this._insertTextAtFocus(text);
      this.showFlashNotification(
        ok ? "Typed" : "Focus an editable field first",
        ok ? COLORS.NOTIFICATION_SUCCESS : COLORS.NOTIFICATION_INFO
      );
      if (ok) this.emitAction("type_characters", { length: text.length });
    }
    /**
     * EXECUTE_JS Function handler — runs the Action Instance / Macro step `script` in the
     * content-script isolated world with hovered-element bindings and optional callbacks.
     * @param {KeyboardEvent} _e
     * @param {{
     *   script?: string,
     *   cbShowPopover?: boolean,
     *   cbCopyToClipboard?: boolean,
     *   cbNotify?: boolean
     * }} [parameters]
     * @param {{ priorResult?: any }} [meta]
     */
    async handleExecuteJsKey(_e, parameters, meta) {
      const script = String(parameters?.script ?? "").trim();
      if (!script) {
        this.showFlashNotification("No script configured for this key", COLORS.NOTIFICATION_INFO);
        return void 0;
      }
      const st = this.state?.getState?.() || {};
      let hovered = st.focusEl || null;
      try {
        if (!hovered) hovered = window.__KP_HOVERED_INTERACTIVE_EL || null;
      } catch {
      }
      let hoverLeaf = null;
      try {
        hoverLeaf = window.__KP_HOVER_LEAF || null;
      } catch {
      }
      const focusedText = st.focusedTextElement || (this._isTextEntryElement?.(document.activeElement) ? document.activeElement : null);
      const showPopover = parameters?.cbShowPopover ? async (content, title) => {
        await deliverActionResult(this, {
          text: stringifyExecuteJsValue(content),
          title: title == null || title === "" ? "Execute JS" : String(title),
          destination: ACTION_RESULT_DESTINATIONS.POPOVER
        });
      } : void 0;
      const copyToClipboard = parameters?.cbCopyToClipboard ? async (content) => {
        const text = stringifyExecuteJsValue(content);
        if (!text) return false;
        return await this.copyToClipboard(text);
      } : void 0;
      const notify2 = parameters?.cbNotify ? (message) => {
        this.showFlashNotification(stringifyExecuteJsValue(message) || "Execute JS", COLORS.NOTIFICATION_INFO);
      } : void 0;
      try {
        const result = await runUserExecuteJs(script, {
          kpHoveredClickable: hovered && hovered.nodeType === 1 ? hovered : null,
          kpHoverLeaf: hoverLeaf || null,
          kpFocusedTextField: focusedText || null,
          kpMode: st.mode || null,
          kpPageUrl: typeof location !== "undefined" ? String(location.href || "") : "",
          kpSelection: typeof window !== "undefined" && typeof window.getSelection === "function" ? window.getSelection() : null,
          kpPriorResult: meta && Object.prototype.hasOwnProperty.call(meta, "priorResult") ? meta.priorResult : void 0,
          showPopover,
          copyToClipboard,
          notify: notify2
        });
        return result;
      } catch (err) {
        const msg = err && err.message ? String(err.message) : "Execute JS failed";
        this.showFlashNotification(msg, COLORS.NOTIFICATION_ERROR);
        console.warn("[KeyPilot] Execute JS failed:", err);
        return void 0;
      }
    }
    /**
     * GET_TEXT_AT_CURSOR Function handler — reads text at the requested granularity from the
     * point last tracked in `state.lastMouse` and copies it to the clipboard. Low-level data
     * primitive (see KEY_ACTION_ARCHITECTURE.md "Data Acquisition & Result Destinations") that's
     * still directly useful/testable on its own, hence the fixed clipboard destination.
     * @param {KeyboardEvent} _e
     * @param {{ granularity?: 'word'|'sentence'|'paragraph'|'hyperlink' }} [parameters]
     */
    async handleGetTextAtCursorKey(_e, parameters) {
      const granularity = parameters?.granularity || "word";
      const st = this.state.getState();
      const x = Number(st?.lastMouse?.x);
      const y = Number(st?.lastMouse?.y);
      if (!Number.isFinite(x) || !Number.isFinite(y)) {
        this.showFlashNotification("No cursor position available", COLORS.NOTIFICATION_INFO);
        return;
      }
      const { text } = getTextAtPoint(x, y, { granularity });
      if (!text.trim()) {
        this.showFlashNotification(`No ${granularity} under cursor`, COLORS.NOTIFICATION_INFO);
        return;
      }
      const ok = await this.copyToClipboard(text);
      this.showFlashNotification(
        ok ? "Copied" : "Could not copy",
        ok ? COLORS.NOTIFICATION_SUCCESS : COLORS.NOTIFICATION_ERROR
      );
      if (ok) {
        this.emitAction("get_text_at_cursor", { granularity, length: text.length });
        return text;
      }
    }
    /**
     * GET_TEXT_RANGE — Macro Step that reads the current highlight/selection and yields it as
     * the next step's prior result. Clipboard is Copy's job; leftover key bindings still copy
     * so existing layouts keep working. Not a standalone key action.
     * @param {KeyboardEvent} _e
     * @param {Record<string, any>} [_parameters]
     * @param {{ macroId?: string }} [meta]
     */
    async handleGetTextRangeKey(_e, _parameters, meta = {}) {
      const text = this.getSelectedPlainText();
      if (!text.trim()) {
        this.showFlashNotification("Nothing selected \u2014 set up a text range first", COLORS.NOTIFICATION_INFO);
        return;
      }
      const inMacro = !!(meta && meta.macroId);
      if (!inMacro) {
        const ok = await this.copyToClipboard(text);
        this.showFlashNotification(
          ok ? "Copied" : "Could not copy",
          ok ? COLORS.NOTIFICATION_SUCCESS : COLORS.NOTIFICATION_ERROR
        );
        if (!ok) return;
      }
      this.emitAction("get_text_range", { length: text.length });
      return text;
    }
    /**
     * GET_MEDIA_AT_CURSOR Function handler — reads the image/video/audio under the cursor.
     * Image/video reuse the same hover-capture as `COPY_HOVERED_IMAGE`; audio has no visual
     * "hover" target on the page, so it copies the nearest `<audio>` element's source URL as text
     * instead of a blob.
     * @param {KeyboardEvent} _e
     * @param {{ kind?: 'image'|'video'|'audio' }} [parameters]
     */
    async handleGetMediaAtCursorKey(_e, parameters) {
      const kind = parameters?.kind || "image";
      const st = this.state.getState();
      const x = Number(st?.lastMouse?.x);
      const y = Number(st?.lastMouse?.y);
      if (!Number.isFinite(x) || !Number.isFinite(y)) {
        this.showFlashNotification("No cursor position available", COLORS.NOTIFICATION_INFO);
        return;
      }
      if (kind === "audio") {
        let src = "";
        try {
          const el2 = deepElementFromPoint(x, y);
          const audio = el2?.closest?.("audio") || el2?.querySelector?.("audio") || null;
          src = audio ? String(audio.currentSrc || audio.src || "") : "";
        } catch {
        }
        if (!src) {
          this.showFlashNotification("No audio under cursor", COLORS.NOTIFICATION_INFO);
          return;
        }
        const ok2 = await this.copyToClipboard(src);
        this.showFlashNotification(
          ok2 ? "Audio URL copied" : "Could not copy",
          ok2 ? COLORS.NOTIFICATION_SUCCESS : COLORS.NOTIFICATION_ERROR
        );
        if (ok2) this.emitAction("get_media_at_cursor", { kind, via: "url" });
        return;
      }
      let result = null;
      try {
        result = await getHoveredImage(x, y);
      } catch (error) {
        console.warn("[KeyPilot] getHoveredImage failed:", error);
        this.showFlashNotification("Could not copy media", COLORS.NOTIFICATION_ERROR);
        return;
      }
      if (!result?.blob) {
        this.showFlashNotification(`No ${kind} under cursor`, COLORS.NOTIFICATION_INFO);
        return;
      }
      const ok = await this.copyImageToClipboard(result.blob, result.mimeType);
      this.showFlashNotification(
        ok ? "Copied to clipboard" : "Could not copy",
        ok ? COLORS.NOTIFICATION_SUCCESS : COLORS.NOTIFICATION_ERROR,
        ok ? result.blob : void 0
      );
      if (ok) this.emitAction("get_media_at_cursor", { kind: result.kind });
    }
    /**
     * LOOKUP_WORD Function handler — shows a definition popover for the word under the cursor.
     * Default source is the Free Dictionary API (via the service worker). AI is reserved for
     * when {@link isWordLookupAiAvailable} is true and the Action Instance sets `source: 'ai'`.
     * @param {KeyboardEvent} [_e]
     * @param {{ source?: 'dictionary'|'ai' }} [parameters]
     */
    async handleLookupWordKey(_e, parameters) {
      const st = this.state.getState();
      const x = Number(st?.lastMouse?.x);
      const y = Number(st?.lastMouse?.y);
      if (!Number.isFinite(x) || !Number.isFinite(y)) {
        this.showFlashNotification("No cursor position available", COLORS.NOTIFICATION_INFO);
        return;
      }
      const { text: rawWord } = getTextAtPoint(x, y, { granularity: "word" });
      const word = normalizeWordForLookup(rawWord);
      if (!word) {
        this.showFlashNotification("No word under cursor", COLORS.NOTIFICATION_INFO);
        return;
      }
      const source = String(parameters?.source || "dictionary").trim() || "dictionary";
      const useAi = source === "ai" && isWordLookupAiAvailable();
      this.showFlashNotification("Looking up\u2026", COLORS.NOTIFICATION_INFO);
      let result;
      try {
        if (useAi) {
          result = await sendTextToAi({
            prompt: "Give a concise dictionary-style definition of this single word or short phrase: part of speech, a one-sentence definition, and one short example sentence. No preamble.",
            text: word
          });
        } else {
          if (!isExtensionContextValid()) {
            this.showFlashNotification("Lookup failed", COLORS.NOTIFICATION_ERROR);
            return;
          }
          const response = await chrome.runtime.sendMessage({
            type: MSG.DICTIONARY_LOOKUP,
            word
          });
          result = response && typeof response === "object" ? response : { ok: false, error: "No response from dictionary lookup" };
        }
      } catch (e) {
        console.warn("[KeyPilot] Lookup failed:", e);
        this.showFlashNotification("Lookup failed", COLORS.NOTIFICATION_ERROR);
        return;
      }
      if (!result?.ok) {
        this.showFlashNotification(result?.error || "No definition found", COLORS.NOTIFICATION_ERROR);
        return;
      }
      await deliverActionResult(this, {
        text: result.text,
        title: `Definition \u2014 ${word}`,
        destination: ACTION_RESULT_DESTINATIONS.POPOVER
      });
      this.emitAction("lookup_word", { word, source: useAi ? "ai" : "dictionary" });
    }
    /**
     * FONT_INFO — inspect the styled text run under the cursor.
     * @param {KeyboardEvent} [_e]
     */
    async handleFontInfoKey(_e) {
      const st = this.state.getState();
      const x = Number(st?.lastMouse?.x);
      const y = Number(st?.lastMouse?.y);
      if (!Number.isFinite(x) || !Number.isFinite(y)) {
        this.showFlashNotification("No cursor position available", COLORS.NOTIFICATION_INFO);
        return;
      }
      const info = inspectFontAtPoint(x, y);
      if (!info) {
        this.showFlashNotification("No text under cursor", COLORS.NOTIFICATION_INFO);
        return;
      }
      try {
        this.overlayManager?.showFontInfoOutline?.(info.range);
      } catch {
      }
      let anchor = null;
      try {
        const r = info.range.getBoundingClientRect();
        if (r && (r.width > 0 || r.height > 0)) {
          anchor = { left: r.right + 12, top: r.top };
        }
      } catch {
      }
      showFontInfoPopover({
        ...info,
        onClose: () => {
          try {
            this.overlayManager?.hideFontInfoOutline?.();
          } catch {
          }
        }
      }, anchor);
      this.emitAction("font_info", { family: info.usedFamily, size: info.size });
    }
    /**
     * TRANSLATE Function handler — translates the current highlight/selection, or (if nothing is
     * highlighted) the word/sentence/paragraph under the cursor, via the same on-device AI
     * provider `SEND_TEXT_TO_AI` uses. `modifyPage` writes the translation back over the acquired
     * range when one is available (a live `window.getSelection()` range, or the precise sub-range
     * `getTextAtPoint` returns for word/sentence) — `deliverActionResult` falls back to `popover`
     * when no range could be resolved (e.g. `paragraph` granularity, or an `<input>`/`<textarea>`
     * selection), so the translation is never silently dropped.
     * @param {KeyboardEvent} _e
     * @param {{ granularity?: 'word'|'sentence'|'paragraph', targetLanguage?: string, destination?: string }} [parameters]
     */
    async handleTranslateKey(_e, parameters) {
      const granularity = parameters?.granularity || "sentence";
      const targetLanguage = String(parameters?.targetLanguage || "English").trim() || "English";
      const destination = normalizeActionResultDestination(
        parameters?.destination,
        ACTION_RESULT_DESTINATIONS.POPOVER
      );
      let text = this.getSelectedPlainText();
      let modifyRange = null;
      if (text.trim()) {
        try {
          const sel = typeof window.getSelection === "function" ? window.getSelection() : null;
          if (sel && sel.rangeCount > 0 && !sel.isCollapsed) modifyRange = sel.getRangeAt(0).cloneRange();
        } catch {
        }
      } else {
        const st = this.state.getState();
        const x = Number(st?.lastMouse?.x);
        const y = Number(st?.lastMouse?.y);
        if (Number.isFinite(x) && Number.isFinite(y)) {
          const at = getTextAtPoint(x, y, { granularity });
          text = at.text;
          modifyRange = at.range;
        }
      }
      if (!text.trim()) {
        this.showFlashNotification("No text to translate", COLORS.NOTIFICATION_INFO);
        return;
      }
      this.showFlashNotification("Translating\u2026", COLORS.NOTIFICATION_INFO);
      let result;
      try {
        result = await sendTextToAi({
          prompt: `Translate the following text to ${targetLanguage}. Respond with only the translation itself \u2014 no notes, no quotation marks.`,
          text
        });
      } catch (e) {
        console.warn("[KeyPilot] Translate failed:", e);
        this.showFlashNotification("Translation failed", COLORS.NOTIFICATION_ERROR);
        return;
      }
      if (!result?.ok) {
        this.showFlashNotification(result?.error || "Translation failed", COLORS.NOTIFICATION_ERROR);
        return;
      }
      await deliverActionResult(this, {
        text: result.text,
        title: `Translation (${targetLanguage})`,
        destination,
        successMessage: "Translation copied",
        onModifyPage: modifyRange ? async (translated) => this._replaceRangeText(modifyRange, translated) : void 0
      });
      this.emitAction("translate", { granularity, targetLanguage, viaSelection: !!modifyRange });
    }
    /**
     * Best-effort in-place page-write for TRANSLATE's `modifyPage` destination. Only works for a
     * real DOM `Range` (regular text nodes / contentEditable) — `<input>`/`<textarea>` selections
     * have no `Range` representation, so `handleTranslateKey` never passes one of those here (see
     * its `onModifyPage` wiring above); `deliverActionResult` falls back to `popover` instead.
     * @param {Range} range
     * @param {string} text
     * @returns {boolean}
     */
    _replaceRangeText(range, text) {
      try {
        range.deleteContents();
        range.insertNode(document.createTextNode(String(text ?? "")));
        return true;
      } catch (e) {
        console.warn("[KeyPilot] Failed to write translation back into the page:", e);
        return false;
      }
    }
    /**
     * SHOW_POPOVER — Macro Step that renders the previous step's result (or configured fallback
     * `content`) via `deliverActionResult`'s popover destination. Not a standalone key action.
     * @param {KeyboardEvent} _e
     * @param {{ content?: string }} [parameters]
     * @param {{ priorResult?: any }} [meta]
     */
    async handleShowPopoverKey(_e, parameters, meta = {}) {
      const prior = meta?.priorResult;
      let fromPrior = "";
      if (typeof prior === "string") fromPrior = prior;
      else if (prior && typeof prior === "object" && prior.text != null) fromPrior = String(prior.text);
      const content = String(fromPrior || parameters?.content || "").trim();
      if (!content) {
        this.showFlashNotification("No content to show in popover", COLORS.NOTIFICATION_INFO);
        return;
      }
      await deliverActionResult(this, {
        text: content,
        title: "Popover",
        destination: ACTION_RESULT_DESTINATIONS.POPOVER
      });
      return content;
    }
    /**
     * Persist a hyperlink in the Media Library (no flash).
     * @param {string} url
     * @returns {Promise<{ success: boolean, duplicate: boolean, error?: string }>}
     */
    async _persistUrlToMediaLibrary(url) {
      const sourceUrl = String(url || "").trim();
      if (!sourceUrl) {
        return { success: false, duplicate: false, error: "No URL" };
      }
      try {
        const result = await addUrlToMediaLibrary({
          sourceUrl,
          pageUrl: typeof location !== "undefined" ? String(location.href || "") : ""
        });
        if (result?.success && !result?.duplicate) {
          try {
            this.emitAction("media_library_add", {
              kind: "url",
              url: sourceUrl.slice(0, 200)
            });
          } catch {
          }
        }
        return {
          success: !!result?.success,
          duplicate: !!result?.duplicate,
          error: result?.error || ""
        };
      } catch (error) {
        console.warn("[KeyPilot] Media Library URL save failed:", error);
        return { success: false, duplicate: false, error: "Could not save URL to Media Library" };
      }
    }
    /**
     * @param {{ success?: boolean, duplicate?: boolean, error?: string }|null} result
     */
    _notifyUrlLibrarySave(result) {
      if (result?.duplicate) {
        this.showFlashNotification("Already in Media Library", COLORS.NOTIFICATION_INFO);
        return;
      }
      if (result?.success) {
        this.showFlashNotification("URL saved to Media Library", COLORS.NOTIFICATION_SUCCESS);
        return;
      }
      this.showFlashNotification(
        result?.error || "Could not save URL to Media Library",
        COLORS.NOTIFICATION_ERROR
      );
    }
    /**
     * Persist a video record in the Media Library (no flash).
     * @param {{
     *   blob?: Blob|null,
     *   mimeType?: string,
     *   currentSrc?: string,
     *   thumbBlob?: Blob|null,
     *   videoWidth?: number,
     *   videoHeight?: number
     * }} result
     * @returns {Promise<{ success: boolean, duplicate: boolean, error?: string }>}
     */
    async _persistVideoToMediaLibrary(result) {
      const fileUrl = String(result?.fileUrl || "").trim();
      const linkUrl = String(result?.currentSrc || "").trim();
      const sourceUrl = (/^https?:\/\//i.test(fileUrl) ? fileUrl : "") || linkUrl || fileUrl;
      const fileBlob = result?.blob instanceof Blob && result.blob.size > 0 ? result.blob : null;
      const thumbBlob = result?.thumbBlob instanceof Blob && result.thumbBlob.size > 0 ? result.thumbBlob : null;
      if (!fileBlob && !sourceUrl) {
        return { success: false, duplicate: false, error: "Could not save video to Media Library" };
      }
      try {
        const saved = await addVideoToMediaLibrary({
          blob: fileBlob || void 0,
          mime: result?.mimeType || fileBlob?.type || "",
          sourceUrl,
          pageUrl: typeof location !== "undefined" ? String(location.href || "") : "",
          thumbBlob: thumbBlob || void 0,
          width: Number(result?.videoWidth) || 0,
          height: Number(result?.videoHeight) || 0
        });
        return {
          success: !!saved?.success,
          duplicate: !!saved?.duplicate,
          error: saved?.error || ""
        };
      } catch (error) {
        console.warn("[KeyPilot] Media Library video save failed:", error);
        return { success: false, duplicate: false, error: "Could not save video to Media Library" };
      }
    }
    /**
     * @param {{ success?: boolean, duplicate?: boolean, error?: string }|null} result
     * @param {Blob|null} [thumb]
     */
    _notifyVideoLibrarySave(result, thumb = null) {
      if (result?.duplicate) {
        this.showFlashNotification("Already in Media Library", COLORS.NOTIFICATION_INFO, thumb || void 0);
        return;
      }
      if (result?.success) {
        this.showFlashNotification("Video saved to Media Library", COLORS.NOTIFICATION_SUCCESS, thumb || void 0);
        return;
      }
      this.showFlashNotification(
        result?.error || "Could not save video to Media Library",
        COLORS.NOTIFICATION_ERROR
      );
    }
    /**
     * @param {{ success?: boolean, duplicate?: boolean, error?: string }|null} result
     */
    _notifyDocumentLibrarySave(result) {
      if (result?.duplicate) {
        this.showFlashNotification("Already in Media Library", COLORS.NOTIFICATION_INFO);
        return;
      }
      if (result?.success) {
        this.showFlashNotification("Document saved to Media Library", COLORS.NOTIFICATION_SUCCESS);
        return;
      }
      this.showFlashNotification(
        result?.error || "Could not save document to Media Library",
        COLORS.NOTIFICATION_ERROR
      );
    }
    /**
     * @param {{ success?: boolean, duplicate?: boolean, error?: string, item?: { kind?: string } }|null} result
     */
    _notifyFetchedLibrarySave(result) {
      const kind = String(result?.item?.kind || "");
      if (kind === "video") {
        this._notifyVideoLibrarySave(result);
        return;
      }
      if (kind === "image") {
        this._notifyMediaLibrarySave(result, { kind: "image" });
        return;
      }
      this._notifyDocumentLibrarySave(result);
    }
    /**
     * Persist an image blob in the Media Library (no flash).
     * @param {{ blob: Blob, mimeType?: string, url?: string|null, kind?: string }} input
     * @returns {Promise<{ success: boolean, duplicate: boolean, error?: string }>}
     */
    async _persistImageToMediaLibrary(input) {
      if (!(input?.blob instanceof Blob) || input.blob.size <= 0) {
        return { success: false, duplicate: false, error: "Could not save to Media Library" };
      }
      try {
        const result = await addImageToMediaLibrary({
          blob: input.blob,
          mime: input.mimeType || input.blob.type || "",
          sourceUrl: input.url || "",
          pageUrl: typeof location !== "undefined" ? String(location.href || "") : ""
        });
        return {
          success: !!result?.success,
          duplicate: !!result?.duplicate,
          error: result?.error || ""
        };
      } catch (error) {
        console.warn("[KeyPilot] Media Library save failed:", error);
        return { success: false, duplicate: false, error: "Could not save to Media Library" };
      }
    }
    /**
     * @param {{ success?: boolean, duplicate?: boolean, error?: string }|null} result
     * @param {{ blob?: Blob, kind?: string, url?: string|null }} input
     */
    _notifyMediaLibrarySave(result, input = {}) {
      if (result?.duplicate) {
        this.showFlashNotification("Already in Media Library", COLORS.NOTIFICATION_INFO, input.blob);
        return;
      }
      if (!result?.success) {
        this.showFlashNotification(
          result?.error || "Could not save to Media Library",
          COLORS.NOTIFICATION_ERROR
        );
        return;
      }
      const label = input.kind === "background" ? "Background image" : input.kind === "svg" ? "SVG" : input.kind === "video" ? "Video thumbnail" : "Image";
      this.showFlashNotification(
        `${label} saved to Media Library`,
        COLORS.NOTIFICATION_SUCCESS,
        input.blob
      );
    }
    /**
     * Persist an image blob in the Media Library. Flashes on duplicate / success / error.
     * @param {{ blob: Blob, mimeType?: string, url?: string|null, kind?: string }} input
     */
    async _saveImageToMediaLibrary(input) {
      const result = await this._persistImageToMediaLibrary(input);
      this._notifyMediaLibrarySave(result, input);
      if (result?.success && !result?.duplicate) {
        try {
          this.emitAction("media_library_add", {
            kind: input.kind || "image",
            url: input.url ? String(input.url).slice(0, 200) : ""
          });
        } catch {
        }
      }
    }
    /**
     * Open / close the Media Library overlay (M on right-handed layout).
     * @param {KeyboardEvent} [e]
     */
    handleOpenMediaLibraryKey(e) {
      if (!this._allowActionKey("handleOpenMediaLibraryKey", e)) return;
      if (isMediaLibraryOverlayOpen()) {
        closeMediaLibraryOverlay();
        return;
      }
      try {
        void openMediaLibraryOverlay({
          onNotify: (message, type2) => {
            const color4 = type2 === "error" ? COLORS.NOTIFICATION_ERROR : type2 === "success" ? COLORS.NOTIFICATION_SUCCESS : COLORS.NOTIFICATION_INFO;
            this.showFlashNotification(String(message || ""), color4);
          }
        }).catch((error) => {
          console.warn("[KeyPilot] openMediaLibraryOverlay failed:", error);
          this.showFlashNotification("Could not open Media Library", COLORS.NOTIFICATION_ERROR);
        });
        this.emitAction("open_media_library", {});
      } catch (error) {
        console.warn("[KeyPilot] openMediaLibraryOverlay failed:", error);
        this.showFlashNotification("Could not open Media Library", COLORS.NOTIFICATION_ERROR);
      }
    }
    /**
     * Persist a Page Media item into the Media Library (image / video / document / URL).
     * @param {import('./utils/page-media-utils.js').PageMediaItem} item
     */
    async _sendPageMediaItemToLibrary(item) {
      const pageUrl = typeof location !== "undefined" ? String(location.href || "") : "";
      const url = String(item?.url || "").trim();
      const category = String(item?.category || "");
      try {
        if (category === "url") {
          if (!url) {
            this.showFlashNotification("Could not send to Media Library", COLORS.NOTIFICATION_ERROR);
            return;
          }
          const saved = await this._persistUrlToMediaLibrary(url);
          this._notifyUrlLibrarySave(saved);
          if (saved?.success && !saved?.duplicate) {
            this.emitAction("page_media_send_to_library", { category, url: url.slice(0, 200) });
          }
          return;
        }
        if (category === "video") {
          if (!url) {
            this.showFlashNotification("Could not send to Media Library", COLORS.NOTIFICATION_ERROR);
            return;
          }
          const saved = await addVideoToMediaLibrary({
            mime: item?.mimeType || "",
            sourceUrl: url,
            pageUrl
          });
          this._notifyVideoLibrarySave(saved);
          if (saved?.success && !saved?.duplicate) {
            this.emitAction("page_media_send_to_library", { category, url: url.slice(0, 200) });
          }
          return;
        }
        if (category === "text") {
          if (!url) {
            this.showFlashNotification("Could not send to Media Library", COLORS.NOTIFICATION_ERROR);
            return;
          }
          const saved = await addDocumentToMediaLibrary({
            mime: item?.mimeType || "",
            sourceUrl: url,
            pageUrl
          });
          this._notifyDocumentLibrarySave(saved);
          if (saved?.success && !saved?.duplicate) {
            this.emitAction("page_media_send_to_library", { category, url: url.slice(0, 200) });
          }
          return;
        }
        let blob = null;
        if (url) blob = await fetchMediaBlob(url);
        if (!blob || blob.size <= 0) {
          this.showFlashNotification("Could not send to Media Library", COLORS.NOTIFICATION_ERROR);
          return;
        }
        const mime = blob.type && blob.type.startsWith("image/") ? blob.type : item?.mimeType && String(item.mimeType).startsWith("image/") ? String(item.mimeType) : "image/png";
        await this._saveImageToMediaLibrary({
          blob,
          mimeType: mime,
          url
        });
        try {
          this.emitAction("page_media_send_to_library", {
            category: category || "image",
            url: url ? url.slice(0, 200) : ""
          });
        } catch {
        }
      } catch (error) {
        console.warn("[KeyPilot] send to Media Library failed:", error);
        this.showFlashNotification("Could not send to Media Library", COLORS.NOTIFICATION_ERROR);
      }
    }
    /**
     * ADD_URL_TO_MEDIA_LIBRARY — store the hovered hyperlink (href only, no fetch).
     * @param {KeyboardEvent} [_e]
     */
    async handleAddUrlToMediaLibraryKey(_e) {
      const url = this._getHoveredHyperlinkUrl();
      if (!url) {
        this.showFlashNotification("No URL under cursor", COLORS.NOTIFICATION_INFO);
        return;
      }
      const saved = await this._persistUrlToMediaLibrary(url);
      this._notifyUrlLibrarySave(saved);
    }
    /**
     * FETCH_URL_FOR_MEDIA_LIBRARY — download the resource the hovered href points to.
     * @param {KeyboardEvent} [_e]
     */
    async handleFetchUrlForMediaLibraryKey(_e) {
      const url = this._getHoveredHyperlinkUrl();
      if (!url) {
        this.showFlashNotification("No URL under cursor", COLORS.NOTIFICATION_INFO);
        return;
      }
      this.showFlashNotification("Fetching file\u2026", COLORS.NOTIFICATION_INFO);
      let saved = null;
      try {
        saved = await fetchUrlIntoMediaLibrary({
          sourceUrl: url,
          pageUrl: typeof location !== "undefined" ? String(location.href || "") : ""
        });
      } catch (error) {
        console.warn("[KeyPilot] Fetch URL for Media Library failed:", error);
        this.showFlashNotification("Could not fetch file", COLORS.NOTIFICATION_ERROR);
        return;
      }
      this._notifyFetchedLibrarySave(saved);
      if (saved?.success && !saved?.duplicate) {
        try {
          this.emitAction("media_library_add", {
            kind: saved?.item?.kind || "file",
            url: url.slice(0, 200)
          });
        } catch {
        }
      }
    }
    /**
     * Dispatch bridge for the keystroke-primitive Functions generalized from the legacy
     * `MacroKeyKind` catalog (SEND_HOTKEY, SEND_BURST, CYCLE_ROUND_ROBIN, HOLD_CONTINUOUS,
     * CLICK_MOUSE_BUTTON, REMAP_KEY — see function-library.js). Delegates to the shared
     * execution switch in macro-key-runtime.js.
     * @param {KeyboardEvent} _e
     * @param {{ config?: any }} [parameters]
     * @param {{ functionId?: string, instanceId?: string }} [meta]
     */
    async handleLegacyMacroKeyFunction(_e, parameters, meta) {
      const functionId = meta?.functionId;
      if (!functionId) return;
      await runLegacyMacroKeyFunction(meta?.instanceId || functionId, functionId, parameters, {
        notify: (msg, type2) => {
          try {
            this.overlayManager?.showNotification?.(msg, type2 || "info");
          } catch {
          }
        }
      });
    }
    /**
     * Send selected text to AI with a configurable instruction (prompt), then route
     * the response through the shared destination helper (clipboard / popover / both).
     */
    async handleSendTextToAiKey() {
      const text = this.getSelectedPlainText();
      if (!String(text || "").trim()) {
        this.showFlashNotification("Select text first", COLORS.NOTIFICATION_INFO);
        return;
      }
      const sendToAiParams = this._getBuiltinFunctionActionParams("SEND_TEXT_TO_AI");
      const prompt = String(getActionParameter(sendToAiParams, "SEND_TEXT_TO_AI", "prompt") ?? "").trim();
      const destination = normalizeActionResultDestination(
        getActionParameter(sendToAiParams, "SEND_TEXT_TO_AI", "destination")
      );
      this.showFlashNotification("Sending to AI\u2026", COLORS.NOTIFICATION_INFO);
      let result;
      try {
        result = await sendTextToAi({ prompt, text });
      } catch (e) {
        console.warn("[KeyPilot] sendTextToAi failed:", e);
        this.showFlashNotification("AI request failed", COLORS.NOTIFICATION_ERROR);
        return;
      }
      if (!result?.ok) {
        this.showFlashNotification(result?.error || "AI request failed", COLORS.NOTIFICATION_ERROR);
        if (result?.request && /No AI provider/i.test(String(result.error || ""))) {
          await deliverActionResult(this, {
            text: result.request,
            title: "AI request (no provider)",
            destination,
            successMessage: "AI request copied \u2014 paste into your AI chat"
          });
        }
        return;
      }
      await deliverActionResult(this, {
        text: result.text,
        title: prompt ? `AI \u2014 ${prompt}` : "AI result",
        destination,
        successMessage: "AI result copied"
      });
      this.emitAction("send_text_to_ai", {
        prompt: prompt.slice(0, 80),
        destination,
        provider: result.provider || "",
        length: String(result.text || "").length
      });
    }
    /**
     * Write an image Blob to the system clipboard (PNG preferred).
     * Separate from text copyToClipboard to keep that API type-safe.
     *
     * @param {Blob} blob
     * @param {string} [mimeType='image/png']
     * @returns {Promise<boolean>}
     */
    async copyImageToClipboard(blob, mimeType = "image/png") {
      if (!blob || !(blob instanceof Blob) || blob.size === 0) {
        console.warn("[KeyPilot] Invalid blob provided to copyImageToClipboard");
        return false;
      }
      const type2 = mimeType && String(mimeType).startsWith("image/") ? String(mimeType) : blob.type && blob.type.startsWith("image/") ? blob.type : "image/png";
      if (!navigator.clipboard || typeof navigator.clipboard.write !== "function") {
        console.warn("[KeyPilot] Clipboard image write not available");
        return false;
      }
      try {
        const item = new ClipboardItem({
          [type2]: Promise.resolve(blob)
        });
        const writePromise = navigator.clipboard.write([item]);
        const timeoutPromise = new Promise((_, reject) => {
          setTimeout(() => reject(new Error("Clipboard operation timed out")), 5e3);
        });
        await Promise.race([writePromise, timeoutPromise]);
        console.log("[KeyPilot] Image copied to clipboard:", type2, blob.size);
        return true;
      } catch (error) {
        if (type2 !== "image/png") {
          try {
            const item = new ClipboardItem({
              "image/png": Promise.resolve(blob)
            });
            await navigator.clipboard.write([item]);
            console.log("[KeyPilot] Image copied to clipboard as image/png fallback");
            return true;
          } catch (retryErr) {
            console.warn("[KeyPilot] Image clipboard write failed:", retryErr);
            throw retryErr;
          }
        }
        console.warn("[KeyPilot] Image clipboard write failed:", error);
        throw error;
      }
    }
    /**
     * Cancel highlight mode and return to normal mode
     * Clears selection, visual indicators, and state with shadow DOM support
     */
    cancelHighlightMode() {
      console.log("[KeyPilot] Canceling highlight mode");
      try {
        this.overlayManager.clearCharacterSelection();
      } catch (error) {
        console.warn("[KeyPilot] Error clearing character selection:", error);
      }
      try {
        this.overlayManager.clearElementSelection?.();
      } catch (error) {
        console.warn("[KeyPilot] Error clearing element selection:", error);
      }
      try {
        this.overlayManager.removeHighlightRectangleOverlay();
      } catch (error) {
        console.warn("[KeyPilot] Error clearing highlight rectangle overlay:", error);
      }
      try {
        this.overlayManager.clearHighlightSelectionOverlays();
      } catch (error) {
        console.warn("[KeyPilot] Error clearing highlight overlays:", error);
      }
      try {
        this.overlayManager.highlightManager?.hideHighlightModeIndicator?.();
      } catch {
      }
      try {
        document.querySelectorAll(
          ".kpv2-highlight-selection-overlay, .kpv2-highlight-selection, .kpv2-highlight-mode-indicator"
        ).forEach((el2) => {
          try {
            el2.remove();
          } catch {
          }
        });
        try {
          if (this.overlayManager?.highlightManager) {
            this.overlayManager.highlightManager.highlightModeIndicator = null;
          }
        } catch {
        }
      } catch {
      }
      if (this.edgeOnlyProcessingEnabled && this.rectangleIntersectionObserver && FEATURE_FLAGS.ENABLE_EDGE_ONLY_PROCESSING) {
        try {
          this.rectangleIntersectionObserver.updateRectangle({
            left: 0,
            top: 0,
            width: 0,
            height: 0
          });
        } catch (error) {
          console.warn("[KeyPilot] Error cleaning up edge-only processing:", error);
        }
      }
      this.clearAllSelectionsWithShadowSupport();
      this.state.setHighlightStartPosition(null);
      this.state.setCurrentSelection(null);
      this.state.setHighlightElement(null);
      this._lastHighlightUpdatePos = { x: -1, y: -1 };
      this.state.setMode(MODES.NONE);
      console.log("[KeyPilot] Highlight mode canceled, returned to normal mode");
    }
    /**
     * Clear all selections including shadow DOM contexts with comprehensive error handling
     */
    clearAllSelectionsWithShadowSupport() {
      try {
        if (window && typeof window.getSelection === "function") {
          const mainSelection = window.getSelection();
          if (mainSelection && typeof mainSelection.rangeCount === "number" && typeof mainSelection.removeAllRanges === "function") {
            if (mainSelection.rangeCount > 0) {
              mainSelection.removeAllRanges();
              console.log("[KeyPilot] Cleared main document selection");
            }
          }
        }
      } catch (error) {
        console.warn("[KeyPilot] Error clearing main document selection:", error);
      }
      try {
        if (!this.shadowDOMManager) {
          console.log("[KeyPilot] Shadow DOM manager not available for selection clearing");
          return;
        }
        const shadowRoots = this.shadowDOMManager.shadowRoots;
        if (!shadowRoots || !(shadowRoots instanceof Set) || shadowRoots.size === 0) {
          console.log("[KeyPilot] No shadow roots available for selection clearing");
          return;
        }
        let i = 0;
        for (const shadowRoot of shadowRoots) {
          try {
            if (!shadowRoot) {
              console.warn(`[KeyPilot] Shadow root at index ${i} is null`);
              i++;
              continue;
            }
            if (typeof shadowRoot.getSelection !== "function") {
              i++;
              continue;
            }
            let shadowSelection = null;
            try {
              shadowSelection = shadowRoot.getSelection();
            } catch (getSelectionError) {
              console.warn(`[KeyPilot] Error getting shadow selection ${i}:`, getSelectionError);
              i++;
              continue;
            }
            if (shadowSelection && typeof shadowSelection.rangeCount === "number" && typeof shadowSelection.removeAllRanges === "function") {
              if (shadowSelection.rangeCount > 0) {
                try {
                  shadowSelection.removeAllRanges();
                  console.log(`[KeyPilot] Cleared shadow DOM selection ${i}`);
                } catch (removeRangesError) {
                  console.warn(`[KeyPilot] Error removing ranges from shadow selection ${i}:`, removeRangesError);
                }
              }
            }
          } catch (shadowRootError) {
            console.warn(`[KeyPilot] Error processing shadow root ${i} for selection clearing:`, shadowRootError);
            i++;
            continue;
          }
          i++;
        }
      } catch (error) {
        console.error("[KeyPilot] Unexpected error clearing shadow DOM selections:", error);
      }
    }
    handleLauncherKey(e) {
      if (!this._allowActionKey("handleLauncherKey", e)) return;
      console.log("[KeyPilot] Launcher key pressed!");
      if (this.launcherPopover.isOpen()) {
        this.launcherPopover.hide();
      } else {
        if (!this.topSitesPopover?.isPersistent?.()) {
          try {
            this.topSitesPopover?.hide?.();
          } catch {
          }
        }
        this.launcherPopover.show();
      }
    }
    handleTopSitesKey(e) {
      if (!this._allowActionKey("handleTopSitesKey", e)) return;
      const persistent = !!(this._settings?.topSitesPersistent || this.topSitesPopover?.isPersistent?.());
      if (persistent) {
        const open = !!this.topSitesPopover?.isOpen?.();
        this.applyTopSitesVisibility(!open, { persist: true });
        return;
      }
      if (this.topSitesPopover?.isOpen?.()) {
        this.topSitesPopover.hide();
      } else {
        try {
          this.launcherPopover?.hide?.();
        } catch {
        }
        void this.topSitesPopover?.show?.();
      }
    }
    /**
     * Listen for KP_FRAME_POINTER / KP_FRAME_FOCUS_RECLAIM / KP_FRAME_TYPING_*
     * from child frame agents. Top frame only.
     */
    _installFrameBridgeListener() {
      try {
        if (window !== window.top) return;
      } catch {
        return;
      }
      if (this._boundFrameBridgeMessage) return;
      this._boundFrameBridgeMessage = (event) => {
        try {
          this._onFrameBridgeMessage(event);
        } catch {
        }
      };
      try {
        window.addEventListener("message", this._boundFrameBridgeMessage, true);
      } catch {
      }
    }
    _uninstallFrameBridgeListener() {
      if (!this._boundFrameBridgeMessage) return;
      try {
        window.removeEventListener("message", this._boundFrameBridgeMessage, true);
      } catch {
      }
      this._boundFrameBridgeMessage = null;
      this._framePointerInside = false;
      this._framePointerIframe = null;
    }
    /**
     * @param {Window|null|undefined} win
     * @returns {HTMLIFrameElement|HTMLFrameElement|null}
     */
    _findIframeByContentWindow(win) {
      if (!win) return null;
      try {
        const popoverIframe = this.overlayManager?.popoverIframeElement;
        if (popoverIframe && popoverIframe.contentWindow === win) {
          return popoverIframe;
        }
      } catch {
      }
      try {
        const nodes = document.querySelectorAll("iframe, frame");
        for (let i = 0; i < nodes.length; i++) {
          const el2 = nodes[i];
          try {
            if (el2 && el2.contentWindow === win) {
              return (
                /** @type {HTMLIFrameElement|HTMLFrameElement} */
                el2
              );
            }
          } catch {
          }
        }
      } catch {
      }
      return null;
    }
    /**
     * True when this iframe is KeyPilot UI (popover) — hybrid focus owns it.
     * @param {Element|null|undefined} iframe
     * @returns {boolean}
     */
    _isKeyPilotManagedIframe(iframe) {
      if (!iframe || !(iframe instanceof Element)) return false;
      try {
        if (iframe.classList?.contains?.("modal-iframe")) return true;
      } catch {
      }
      try {
        if (this._isElementInPopover(iframe)) return true;
      } catch {
      }
      try {
        if (this.overlayManager?.popoverIframeElement === iframe) return true;
      } catch {
      }
      return false;
    }
    /**
     * True when this iframe is Google's account / profile switcher (or similar).
     * Those menus focus the iframe on open and dismiss on blur — never steal focus
     * from them except via explicit Esc handling inside the frame.
     * @param {Element|null|undefined} iframe
     * @returns {boolean}
     */
    _isGoogleAccountIframe(iframe) {
      if (!iframe || !(iframe instanceof Element)) return false;
      try {
        const name = typeof /** @type {any} */
        iframe.name === "string" ? (
          /** @type {any} */
          iframe.name
        ) : "";
        if (name === "account" || name === "oauth2" || /account/i.test(name)) return true;
      } catch {
      }
      try {
        const src = String(
          /** @type {any} */
          iframe.src || ""
        );
        if (/accounts\.google\.com|ogs\.google\.com|myaccount\.google\.com/i.test(src)) {
          return true;
        }
      } catch {
      }
      return false;
    }
    /**
     * Blur a focused page <iframe> so top-frame KeyPilot receives keydown again.
     * Skips KeyPilot popover iframes. Google account menus are skipped unless
     * `allowGoogleAccount` (explicit Esc) — blur on mouse leave dismisses them.
     * @param {{ allowGoogleAccount?: boolean }} [opts]
     */
    _reclaimKeyboardFocusFromPageIframes(opts = {}) {
      const allowGoogleAccount = opts.allowGoogleAccount === true;
      try {
        if (window !== window.top) return;
      } catch {
        return;
      }
      try {
        const active2 = document.activeElement;
        if (!active2 || active2.tagName !== "IFRAME" && active2.tagName !== "FRAME") {
          return;
        }
        if (this._isKeyPilotManagedIframe(active2)) return;
        if (!allowGoogleAccount && this._isGoogleAccountIframe(active2)) return;
        try {
          active2.blur();
        } catch {
        }
        try {
          window.focus();
        } catch {
        }
      } catch {
      }
    }
    /**
     * Parent document received a pointer move after we were tracking inside an
     * embed. Clear tracking only — do NOT blur the iframe (Google account menus
     * dismiss on blur; gaps between avatar and menu fire parent mousemove).
     */
    _maybeReclaimFocusAfterParentPointerMove() {
      try {
        if (window !== window.top) return;
        if (!this._framePointerInside) return;
        this._framePointerInside = false;
        this._framePointerIframe = null;
      } catch {
      }
    }
    /**
     * Same-origin typing node inside `iframe`, or null (cross-origin / not typing).
     * @param {HTMLIFrameElement|HTMLFrameElement|null|undefined} iframe
     * @returns {Element|null}
     */
    _peekSameOriginTypingElement(iframe) {
      if (!iframe) return null;
      try {
        const doc = iframe.contentDocument;
        if (!doc) return null;
        const active2 = kpGetDeepActiveElement(doc);
        if (!active2) return null;
        if (this.focusDetector?.isTextInput?.(active2)) return active2;
        if (this._isTextEntryElement(active2)) return active2;
      } catch {
      }
      return null;
    }
    /**
     * @param {Element|null|undefined} el
     * @param {HTMLIFrameElement|HTMLFrameElement} iframe
     * @returns {boolean}
     */
    _elementBelongsToIframe(el2, iframe) {
      if (!el2 || !iframe) return false;
      try {
        const doc = iframe.contentDocument;
        if (doc && el2.ownerDocument === doc) return true;
      } catch {
      }
      try {
        return iframe.contains(el2);
      } catch {
        return false;
      }
    }
    /**
     * Enter/exit text mode from a child frame-agent typing notice.
     * @param {Window|null|undefined} win
     * @param {{ allowClear?: boolean }} [opts]
     */
    _syncTextFocusFromFrameWindow(win, opts = {}) {
      if (!this.enabled || !this.focusDetector) return;
      const iframe = this._findIframeByContentWindow(win);
      if (!iframe) return;
      if (this._isKeyPilotManagedIframe(iframe)) return;
      const el2 = this._peekSameOriginTypingElement(iframe);
      if (el2) {
        try {
          this.focusDetector.setTextFocus(el2);
        } catch {
        }
        return;
      }
      if (!opts.allowClear) return;
      const cur = this.focusDetector.currentFocusedElement;
      if (cur && this._elementBelongsToIframe(cur, iframe)) {
        try {
          this.focusDetector.clearTextFocus();
        } catch {
        }
        return;
      }
      try {
        this.focusDetector.checkCurrentFocus();
      } catch {
      }
    }
    /**
     * @param {MessageEvent} event
     */
    _onFrameBridgeMessage(event) {
      if (!this.enabled) return;
      const data = event?.data;
      if (!data || typeof data !== "object") return;
      if (data.type === MSG.FRAME_FOCUS_RECLAIM) {
        this._framePointerInside = false;
        this._framePointerIframe = null;
        this._reclaimKeyboardFocusFromPageIframes({ allowGoogleAccount: true });
        return;
      }
      if (data.type === MSG.FRAME_TYPING_FOCUS || data.type === MSG.FRAME_TYPING_BLUR) {
        this._syncTextFocusFromFrameWindow(
          /** @type {Window} */
          event.source,
          { allowClear: data.type === MSG.FRAME_TYPING_BLUR }
        );
        return;
      }
      if (data.type !== MSG.FRAME_POINTER) return;
      try {
        if (event.source === window) return;
      } catch {
      }
      const iframe = this._findIframeByContentWindow(
        /** @type {Window} */
        event.source
      );
      if (!iframe) return;
      const managed = this._isKeyPilotManagedIframe(iframe);
      if (this._isGoogleAccountIframe(iframe)) {
        if (data.inside !== false) {
          let rect2;
          try {
            rect2 = iframe.getBoundingClientRect();
          } catch {
            return;
          }
          const x2 = rect2.left + Number(data.clientX);
          const y2 = rect2.top + Number(data.clientY);
          if (!Number.isFinite(x2) || !Number.isFinite(y2)) return;
          try {
            this.state.setMousePosition(x2, y2);
          } catch {
          }
          try {
            this.cursor.updatePosition(x2, y2);
          } catch {
          }
          try {
            this.mouseCoordinateManager.updateCurrentMousePosition(x2, y2);
          } catch {
          }
        }
        return;
      }
      if (data.inside === false) {
        this._framePointerInside = false;
        this._framePointerIframe = null;
        return;
      }
      let rect;
      try {
        rect = iframe.getBoundingClientRect();
      } catch {
        return;
      }
      const x = rect.left + Number(data.clientX);
      const y = rect.top + Number(data.clientY);
      if (!Number.isFinite(x) || !Number.isFinite(y)) return;
      this._framePointerInside = true;
      this._framePointerIframe = iframe;
      try {
        this.state.setMousePosition(x, y);
      } catch {
      }
      try {
        this.cursor.updatePosition(x, y);
      } catch {
      }
      try {
        this.mouseCoordinateManager.updateCurrentMousePosition(x, y);
      } catch {
      }
      if (managed) {
        try {
          this.updateElementsUnderCursor(x, y, true);
        } catch {
        }
      }
    }
    /**
     * When the pointer is over a cross-origin (or any) iframe, top-frame hit-testing
     * only sees the <iframe> shell. Forward activate to the child frame-click-agent
     * with coordinates local to the iframe viewport.
     *
     * @param {number} clientX
     * @param {number} clientY
     * @param {{ openInNewTab?: boolean, background?: boolean }} [opts]
     * @returns {boolean} true if a message was posted to an iframe under the cursor
     */
    _tryActivateIframeUnderCursor(clientX, clientY, opts = {}) {
      if (!Number.isFinite(clientX) || !Number.isFinite(clientY)) return false;
      let under = null;
      try {
        under = this.detector.deepElementFromPoint(clientX, clientY);
      } catch {
        under = null;
      }
      try {
        const popoverIframe = this.overlayManager?.popoverIframeElement;
        if (popoverIframe) {
          const r = popoverIframe.getBoundingClientRect();
          if (r && r.width > 0 && r.height > 0 && clientX >= r.left && clientX <= r.right && clientY >= r.top && clientY <= r.bottom) {
            const hit = under;
            const steal = !hit || hit === popoverIframe || hit.tagName === "IFRAME" || !this._isKeyPilotUiElement?.(hit) && !this._isElementInPopover?.(hit);
            if (steal) under = popoverIframe;
          }
        }
      } catch {
      }
      if (!under || under.tagName !== "IFRAME") return false;
      try {
        const mode = this.state.getState()?.mode;
        if (mode === MODES.POPOVER && !this._isElementInPopover(under)) {
          return false;
        }
      } catch {
      }
      try {
        if (this._isKeyPilotUiElement?.(under) && !this._isKeyPilotManagedIframe(under)) {
          return false;
        }
      } catch {
      }
      const iframe = (
        /** @type {HTMLIFrameElement} */
        under
      );
      let rect;
      try {
        rect = iframe.getBoundingClientRect();
      } catch {
        return false;
      }
      if (!rect || rect.width <= 0 || rect.height <= 0) return false;
      const localX = clientX - rect.left;
      const localY = clientY - rect.top;
      if (localX < 0 || localY < 0 || localX > rect.width || localY > rect.height) {
        return false;
      }
      const payload = {
        type: MSG.FRAME_ACTIVATE,
        clientX: localX,
        clientY: localY,
        openInNewTab: !!opts.openInNewTab,
        background: !!opts.background,
        // Child agent filters on window.name when set (e.g. Google name="account").
        frameName: typeof iframe.name === "string" ? iframe.name : "",
        // Parent origin for frame-agent link routing (no domain hardcoding in the child).
        topOrigin: (() => {
          try {
            return String(location.origin || "");
          } catch {
            return "";
          }
        })()
      };
      try {
        const doc = iframe.contentDocument;
        const view = iframe.contentWindow;
        if (doc && view) {
          let el2 = doc.elementFromPoint(localX, localY);
          let guard = 0;
          while (el2 && el2.shadowRoot && guard++ < 10) {
            const nested = el2.shadowRoot.elementFromPoint(localX, localY);
            if (!nested || nested === el2) break;
            el2 = nested;
          }
          if (el2) {
            if (el2.tagName === "IFRAME") {
              try {
                const nested = (
                  /** @type {HTMLIFrameElement} */
                  el2
                );
                const nr = nested.getBoundingClientRect();
                nested.contentWindow?.postMessage({
                  ...payload,
                  clientX: localX - nr.left,
                  clientY: localY - nr.top,
                  frameName: typeof nested.name === "string" ? nested.name : ""
                }, "*");
              } catch {
              }
            } else {
              const clickable = typeof el2.closest === "function" ? el2.closest(
                'a[href], button, [role="button"], [role="link"], [role="menuitem"], summary, input, select, textarea, label'
              ) : null;
              const target = clickable || el2;
              if (clickable && typeof /** @type {any} */
              clickable.click === "function") {
                try {
                  clickable.click();
                } catch {
                }
              } else {
                const common = {
                  bubbles: true,
                  cancelable: true,
                  composed: true,
                  view,
                  clientX: localX,
                  clientY: localY,
                  button: 0,
                  buttons: 1
                };
                const hasPointer = typeof view.PointerEvent === "function";
                if (hasPointer) {
                  const pCommon = { ...common, pointerId: 1, pointerType: "mouse", isPrimary: true };
                  try {
                    target.dispatchEvent(new view.PointerEvent("pointerdown", pCommon));
                  } catch {
                  }
                }
                try {
                  target.dispatchEvent(new view.MouseEvent("mousedown", common));
                } catch {
                }
                const commonUp = { ...common, buttons: 0 };
                if (hasPointer) {
                  const pUp = { ...commonUp, pointerId: 1, pointerType: "mouse", isPrimary: true };
                  try {
                    target.dispatchEvent(new view.PointerEvent("pointerup", pUp));
                  } catch {
                  }
                }
                try {
                  target.dispatchEvent(new view.MouseEvent("mouseup", commonUp));
                } catch {
                }
                try {
                  target.dispatchEvent(new view.MouseEvent("click", commonUp));
                } catch {
                }
              }
            }
            return true;
          }
        }
      } catch {
      }
      let posted = false;
      try {
        const win = iframe.contentWindow;
        if (win) {
          win.postMessage(payload, "*");
          posted = true;
        }
      } catch {
      }
      try {
        this._sendRuntimeMessage(payload, { silent: true });
        posted = true;
      } catch {
      }
      return posted;
    }
    /**
     * Mount F-click flash (and ripple) before HTMLElement.click() / navigation.
     * Same-tick click unloads or re-renders the target before the browser paints,
     * which is why the green strobe was almost never visible.
     * When a pulse actually mounted, wait two animation frames (after paint)
     * then run `activate`. Selects/file pickers skip that wait so they stay
     * inside the key-gesture (no flash for CONTROL category).
     * @param {Element|null|undefined} target
     * @param {() => void} activate
     */
    _flashThenActivate(target, activate) {
      this._activationFlashToken = (this._activationFlashToken || 0) + 1;
      const token = this._activationFlashToken;
      const state = this.state?.getState?.();
      const x = state?.lastMouse?.x;
      const y = state?.lastMouse?.y;
      try {
        if (typeof x === "number" && typeof y === "number") this.showRipple(x, y);
      } catch {
      }
      let flashed = false;
      try {
        flashed = !!this.overlayManager?.flashFocusOverlay?.(target);
      } catch {
      }
      const run = () => {
        if (token !== this._activationFlashToken) return;
        try {
          activate();
        } catch (e) {
          console.warn("[KeyPilot] Activation after flash failed:", e);
        }
      };
      if (!flashed) {
        run();
        return;
      }
      try {
        requestAnimationFrame(() => {
          requestAnimationFrame(run);
        });
      } catch {
        run();
      }
    }
    /**
     * True on Google / Bing / Brave / OSM / Mapbox map pages.
     * Also published as `window.isOnMapWebsite`.
     * @param {string} [hostname]
     * @param {string} [pathname]
     * @returns {boolean}
     */
    isOnMapWebsite(hostname, pathname) {
      return isOnMapWebsite(hostname, pathname);
    }
    /**
     * Click the map / place card under the cursor so the details pane fills in.
     * @param {number} x
     * @param {number} y
     * @returns {boolean}
     */
    _clickMapPoiAtPoint(x, y) {
      let under = null;
      try {
        under = this.detector?.deepElementFromPoint?.(x, y) || deepElementFromPoint(x, y);
      } catch {
        under = null;
      }
      if (!under || under.nodeType !== 1) return false;
      try {
        if (this._isKeyPilotUiElement?.(under)) return false;
      } catch {
      }
      try {
        this.activator?.smartClick?.(under, x, y);
        return true;
      } catch {
        return false;
      }
    }
    /**
     * Dismiss Google Maps' selected-place sliding pane via the search-box Close.
     * @returns {boolean}
     */
    _dismissMapPlacePanel() {
      if (!this.isOnMapWebsite()) return false;
      const btn = findMapsPlacePanelCloseButton();
      if (!btn) return false;
      let x = 0;
      let y = 0;
      try {
        const r = btn.getBoundingClientRect();
        x = r.left + r.width / 2;
        y = r.top + r.height / 2;
      } catch {
      }
      let clicked = false;
      try {
        btn.click();
        clicked = true;
      } catch {
        clicked = false;
      }
      if (!clicked) {
        try {
          this.activator?.smartClick?.(btn, x, y);
          clicked = true;
        } catch {
          clicked = false;
        }
      }
      if (!clicked) return false;
      const blurSearch = () => {
        try {
          const ae = document.activeElement;
          if (!ae || ae === document.body || ae === document.documentElement) return;
          const tag = String(ae.tagName || "").toUpperCase();
          let role = "";
          try {
            role = String(ae.getAttribute("role") || "").toLowerCase();
          } catch {
            role = "";
          }
          const typing = tag === "INPUT" || tag === "TEXTAREA" || role === "combobox" || ae.isContentEditable;
          if (typing) {
            try {
              ae.blur();
            } catch {
            }
          }
        } catch {
        }
        try {
          this.focusDetector?.clearTextFocus?.();
        } catch {
        }
      };
      blurSearch();
      try {
        setTimeout(blurSearch, 0);
      } catch {
      }
      try {
        setTimeout(blurSearch, 80);
      } catch {
      }
      return true;
    }
    /**
     * Close Link Preview / Open Popover and clear the Maps place pane so the
     * next E or P is not stuck on the previous POI's website.
     */
    _closeMapSitePopover() {
      this._dismissMapPlacePanel();
      this.handleClosePopover();
    }
    /**
     * Website of the map place under the cursor (or the selected place pane).
     * Clicks the POI when the pane does not already list a site.
     * @param {number} x
     * @param {number} y
     * @returns {Promise<string|null>}
     */
    async _resolvePoiWebsiteAtPoint(x, y) {
      let url = findPoiWebsiteAtPoint(x, y);
      if (url) return url;
      try {
        const state = this.state.getState();
        const focus = state?.focusEl;
        if (focus instanceof Element) {
          const hovered = this._resolveHoveredLink(focus);
          const href = unwrapMapsRedirect(hovered?.url || "");
          if (isExternalPoiWebsite(href)) return href;
        }
      } catch {
      }
      const overMap = !!findMapSurfaceAtPoint(x, y);
      const previous = findPoiWebsiteInDocument();
      if (previous && !overMap) return previous;
      if (this._clickMapPoiAtPoint(x, y)) {
        url = await waitForPoiWebsite({
          timeoutMs: 2800,
          ignoreUrl: overMap ? previous : null
        });
        if (url) return url;
      }
      return findPoiWebsiteInDocument();
    }
    /**
     * Address (and name/phone/website) of the map place under the cursor.
     * @param {number} x
     * @param {number} y
     * @returns {Promise<{ name: string, address: string, phone: string, website: string }|null>}
     */
    async _resolvePoiAddressAtPoint(x, y) {
      let info = findPoiPlaceDetailsAtPoint(x, y);
      if (info?.address) return info;
      const overMap = !!findMapSurfaceAtPoint(x, y);
      const previous = findPoiPlaceDetailsInDocument();
      if (previous?.address && !overMap) return previous;
      if (this._clickMapPoiAtPoint(x, y)) {
        info = await waitForPoiAddress({
          timeoutMs: 2800,
          ignoreAddress: overMap ? previous?.address : null
        });
        if (info?.address) return info;
      }
      const fallback = findPoiPlaceDetailsInDocument();
      return fallback?.address ? fallback : null;
    }
    /**
     * Copy the map place address under the cursor (txt or vCard).
     * @param {KeyboardEvent} [e]
     * @param {{ action?: string, format?: string, destination?: string }} [parameters]
     */
    async handlePoiAddressKey(e, parameters) {
      if (!this._allowActionKey("handlePoiAddressKey", e)) return;
      const currentState = this.state.getState();
      const { lastMouse } = currentState;
      const x = Number(lastMouse?.x);
      const y = Number(lastMouse?.y);
      const px = Number.isFinite(x) ? x : (window.innerWidth || 0) / 2;
      const py = Number.isFinite(y) ? y : (window.innerHeight || 0) / 2;
      const info = await this._resolvePoiAddressAtPoint(px, py);
      if (!info?.address) {
        this.showFlashNotification("No address for this place", COLORS.NOTIFICATION_INFO);
        return;
      }
      const formatRaw = parameters?.format ?? getActionParameter(this._getBuiltinFunctionActionParams("POI_ADDRESS"), "POI_ADDRESS", "format");
      const format = String(formatRaw || "txt").toLowerCase() === "vcard" ? "vcard" : "txt";
      const payload = format === "vcard" ? formatPoiAddressVCard(info) : String(info.address).trim();
      const dest = this._resolveCopyDestination("POI_ADDRESS", parameters);
      const flags = destinationFlags(dest);
      let copied = false;
      if (flags.clipboard) {
        copied = await this.copyToClipboard(payload);
      }
      let saved = null;
      if (flags.mediaLibrary) {
        const mapsUrl = mapsSearchUrlForAddress(info.address);
        saved = await this._persistUrlToMediaLibrary(mapsUrl || info.website || "");
      }
      if (flags.clipboard && flags.mediaLibrary) {
        if (copied && (saved?.success || saved?.duplicate)) {
          this.showFlashNotification(
            saved?.duplicate ? "Address copied; already in Media Library" : "Address copied and saved to Media Library",
            saved?.duplicate ? COLORS.NOTIFICATION_INFO : COLORS.NOTIFICATION_SUCCESS
          );
          return;
        }
      }
      if (flags.clipboard) {
        this.showFlashNotification(
          copied ? format === "vcard" ? "vCard copied to clipboard" : "Address copied to clipboard" : "Could not copy address",
          copied ? COLORS.NOTIFICATION_SUCCESS : COLORS.NOTIFICATION_ERROR
        );
      }
      if (flags.mediaLibrary && !flags.clipboard) {
        if (saved?.duplicate) {
          this.showFlashNotification("Already in Media Library", COLORS.NOTIFICATION_INFO);
        } else if (saved?.success) {
          this.showFlashNotification("Address saved to Media Library", COLORS.NOTIFICATION_SUCCESS);
        } else {
          this.showFlashNotification(saved?.error || "Could not save address", COLORS.NOTIFICATION_ERROR);
        }
      }
      if (copied) {
        try {
          this.emitAction("poi_address", { format, address: String(info.address).slice(0, 200) });
        } catch {
        }
      }
    }
    _openLinkPreviewPopover(url, mouseX, mouseY) {
      const href = String(url || "").trim();
      if (!href) return;
      this.overlayManager.showPreviewPopover(href, {
        title: "Link Preview",
        mouseX,
        mouseY
      });
      this.state.setPopoverOpen(true, href);
    }
    /**
     * Open a URL in the large Open Popover (P).
     * @param {string} url
     */
    _openFullPopover(url) {
      const href = String(url || "").trim();
      if (!href) return;
      this.overlayManager.showPopover(href);
      this.state.setPopoverOpen(true, href);
    }
    async handlePoiWebsiteKey(e) {
      if (!this._allowActionKey("handlePoiWebsiteKey", e)) return;
      if (this.overlayManager.isPopoverOpen()) {
        this._closeMapSitePopover();
        return;
      }
      const currentState = this.state.getState();
      const { lastMouse } = currentState;
      const x = Number(lastMouse?.x);
      const y = Number(lastMouse?.y);
      const px = Number.isFinite(x) ? x : (window.innerWidth || 0) / 2;
      const py = Number.isFinite(y) ? y : (window.innerHeight || 0) / 2;
      const url = await this._resolvePoiWebsiteAtPoint(px, py);
      if (!url) {
        this.showFlashNotification("No website for this place", COLORS.NOTIFICATION_INFO);
        return;
      }
      this._openLinkPreviewPopover(url, px, py);
    }
    handlePreviewLinkPopover(e) {
      if (!this._allowActionKey("handlePreviewLinkPopover", e)) return;
      if (this.overlayManager.isPopoverOpen()) {
        this._closeMapSitePopover();
        return;
      }
      const currentState = this.state.getState();
      const { lastMouse } = currentState;
      const mx = Number(lastMouse?.x);
      const my = Number(lastMouse?.y);
      const px = Number.isFinite(mx) ? mx : (window.innerWidth || 0) / 2;
      const py = Number.isFinite(my) ? my : (window.innerHeight || 0) / 2;
      const onMap = this.isOnMapWebsite() || !!findMapSurfaceAtPoint(px, py);
      if (onMap) {
        void this._resolvePoiWebsiteAtPoint(px, py).then((poiUrl) => {
          if (!poiUrl) {
            this.showFlashNotification("No website for this place", COLORS.NOTIFICATION_INFO);
            return;
          }
          this._openLinkPreviewPopover(poiUrl, px, py);
        }).catch(() => {
          this.showFlashNotification("No website for this place", COLORS.NOTIFICATION_INFO);
        });
        return;
      }
      let under = null;
      try {
        under = this.detector.deepElementFromPoint(lastMouse.x, lastMouse.y);
      } catch {
      }
      let target = currentState.focusEl;
      if (!target) {
        target = this.detector.findClickable(under) || under;
      }
      if (!target || !(target instanceof Element)) {
        console.log("[KeyPilot] Preview popover: not hovering over a link");
        this._flashUrlActionUnavailable(target, lastMouse?.x, lastMouse?.y);
        return;
      }
      let url = this._resolveHoveredLink(target)?.url || null;
      if (!url) {
        try {
          const start = (under instanceof Element ? under : null) || (target instanceof Element ? target : null);
          const outline = start?.closest?.("details.history-outline") || null;
          if (outline && !outline.open) {
            const rootUrl = String(outline.dataset?.kpRootUrl || "").trim();
            if (rootUrl) {
              url = rootUrl;
            }
          }
        } catch {
        }
      }
      if (!url) {
        console.log("[KeyPilot] Preview popover: not hovering over a link");
        this._flashUrlActionUnavailable(target, lastMouse?.x, lastMouse?.y);
        return;
      }
      console.log("[KeyPilot] Opening preview popover for link:", url);
      this.overlayManager.showPreviewPopover(url, {
        title: "Link Preview",
        mouseX: lastMouse.x,
        mouseY: lastMouse.y
      });
      this.state.setPopoverOpen(true, url);
    }
    getSettingsPopoverUrl() {
      try {
        return chrome.runtime.getURL("pages/settings.html");
      } catch {
        return null;
      }
    }
    getGuidePopoverUrl() {
      try {
        return chrome.runtime.getURL("pages/guide.html");
      } catch {
        return null;
      }
    }
    getDocsPopoverUrl() {
      try {
        return chrome.runtime.getURL("pages/docs.html");
      } catch {
        return null;
      }
    }
    handleOpenSettingsPopover(opts = {}) {
      const url = this.getSettingsPopoverUrl();
      if (!url) return;
      const panelId = normalizeSettingsPanelId(opts?.panelId) || null;
      const currentState = this.state.getState();
      if (currentState.mode === MODES.POPOVER && currentState.popoverUrl === url) {
        if (panelId) {
          try {
            if (this.overlayManager?.setSettingsPanel?.(panelId)) return;
          } catch {
          }
        } else {
          return;
        }
      }
      if (currentState.mode === MODES.POPOVER) {
        this.handleClosePopover();
      }
      const settingsContainerWidth = Math.min(980, window.innerWidth - 36) + 20;
      const settingsContainerHeight = Math.min(window.innerHeight * 0.82, window.innerHeight - 80) + 20;
      void this.overlayManager.showInPageSettingsPopover({
        title: "KeyPilot Settings",
        hintKeyLabel: "'",
        width: `${settingsContainerWidth}px`,
        height: `${settingsContainerHeight}px`,
        actions: this._createSettingsDocsTitlebarButton(),
        panelId: panelId || void 0
      });
      this.state.setPopoverOpen(true, url);
    }
    /**
     * Settings titlebar control: open Docs immediately left of ×.
     * @returns {HTMLButtonElement}
     */
    _createSettingsDocsTitlebarButton() {
      return createTitlebarActionButton({
        label: "Help/Documentation",
        title: "Help/Documentation",
        className: "kpv2-popover-titlebar-docs",
        iconPaths: [
          { attrs: { d: "M4 19.5A2.5 2.5 0 0 1 6.5 17H20" } },
          { attrs: { d: "M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z" } }
        ],
        onClick: () => this.handleOpenDocsPopover({ topicId: "settings" })
      });
    }
    /**
     * Documentation titlebar control for article text size.
     * Uses the shared NCT dark scale slider; docs iframe defaults to 1.25×.
     * @returns {HTMLElement}
     */
    _createDocsFontScaleControl() {
      const { root } = createNctDarkUiScaleSlider({
        label: "Text",
        title: "Documentation text size",
        ariaLabel: "Documentation text size",
        min: 0.8,
        max: 1.75,
        step: 0.05,
        value: 1.25,
        formatValue: (scale) => `${Number(scale).toFixed(2).replace(/0$/, "")}\xD7`,
        onInput: (scale) => {
          try {
            this.overlayManager?.setDocsFontScale?.(scale);
          } catch {
          }
        }
      });
      return root;
    }
    handleToggleSettingsPopover() {
      if (window !== window.top) return;
      const currentState = this.state.getState();
      const settingsUrl = this.getSettingsPopoverUrl();
      if (!settingsUrl) return;
      if (currentState.mode === MODES.POPOVER && currentState.popoverUrl === settingsUrl) {
        this.handleClosePopover();
        return;
      }
      this.handleOpenSettingsPopover();
    }
    handleOpenGuidePopover() {
      const url = this.getGuidePopoverUrl();
      if (!url) return;
      const currentState = this.state.getState();
      if (currentState.mode === MODES.POPOVER) {
        this.handleClosePopover();
      }
      const guideContainerWidth = Math.min(920, window.innerWidth - 36) + 20;
      const guideContainerHeight = Math.min(window.innerHeight * 0.8, window.innerHeight - 100) + 20;
      this.overlayManager.showPopover(url, {
        title: "KeyPilot Guide",
        hintKeyLabel: "Esc",
        closeKeys: ["Escape", "'", '"', "p", "P"],
        width: `${guideContainerWidth}px`,
        height: `${guideContainerHeight}px`
      });
      this.state.setPopoverOpen(true, url);
    }
    /**
     * Open Docs popover, optionally at a catalog topic / in-article hash.
     * @param {{ topicId?: string, hash?: string }} [opts]
     */
    handleOpenDocsPopover(opts = {}) {
      const url = this.getDocsPopoverUrl();
      if (!url) return;
      const topicId = String(opts?.topicId || "").trim() || null;
      const hash = String(opts?.hash || "").replace(/^#/, "").trim() || null;
      const currentState = this.state.getState();
      if (currentState.mode === MODES.POPOVER && currentState.popoverUrl === url) {
        if (topicId) {
          try {
            if (this.overlayManager?.setDocsTopic?.(topicId, hash || void 0)) return;
          } catch {
          }
        } else {
          return;
        }
      }
      if (currentState.mode === MODES.POPOVER) {
        this.handleClosePopover();
      }
      const docsContainerWidth = Math.min(980, window.innerWidth - 36) + 20;
      const docsContainerHeight = Math.min(window.innerHeight * 0.82, window.innerHeight - 80) + 20;
      void this.overlayManager.showInPageDocsPopover({
        title: "KeyPilot Docs",
        hintKeyLabel: "Alt + H",
        width: `${docsContainerWidth}px`,
        height: `${docsContainerHeight}px`,
        actions: this._createDocsFontScaleControl(),
        topicId: topicId || void 0,
        hash: hash || void 0,
        onNavigateDeepLink: (target) => this.navigateKpDeepLink(target)
      });
      this.state.setPopoverOpen(true, url);
    }
    /**
     * Route a parsed kp:// deep link to Settings or Docs.
     * @param {string|{ kind: string, id: string, hash?: string }|null|undefined} hrefOrTarget
     */
    navigateKpDeepLink(hrefOrTarget) {
      const target = hrefOrTarget && typeof hrefOrTarget === "object" && hrefOrTarget.kind ? hrefOrTarget : parseKpDeepLink(hrefOrTarget);
      if (!target) return;
      if (target.kind === "settings") {
        this.handleOpenSettingsPopover({ panelId: target.id });
        return;
      }
      if (target.kind === "docs") {
        this.handleOpenDocsPopover({ topicId: target.id, hash: target.hash });
      }
    }
    /**
     * Install click capture on a root so kp:// anchors open Settings/Docs.
     * @param {ParentNode|null|undefined} root
     * @returns {() => void} dispose
     */
    installKpDeepLinkClickHandler(root) {
      if (!root || typeof root.addEventListener !== "function") return () => {
      };
      const onClick = (e) => {
        if (!e || e.defaultPrevented) return;
        if (e.button != null && e.button !== 0) return;
        if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
        const path = typeof e.composedPath === "function" ? e.composedPath() : [];
        let el2 = null;
        for (const node of path) {
          if (node && node.nodeType === 1 && /** @type {Element} */
          node.tagName === "A") {
            el2 = /** @type {Element} */
            node;
            break;
          }
        }
        if (!el2) {
          const t = e.target;
          el2 = t && /** @type {Element} */
          t.closest ? (
            /** @type {Element} */
            t.closest("a[href]")
          ) : null;
        }
        if (!el2) return;
        const href = el2.getAttribute("href") || "";
        if (!isKpDeepLink(href)) return;
        const parsed = parseKpDeepLink(href);
        if (!parsed) return;
        e.preventDefault();
        e.stopPropagation();
        this.navigateKpDeepLink(parsed);
      };
      root.addEventListener("click", onClick, true);
      return () => {
        try {
          root.removeEventListener("click", onClick, true);
        } catch {
        }
      };
    }
    handleToggleDocsPopover() {
      if (window !== window.top) return;
      const currentState = this.state.getState();
      const docsUrl2 = this.getDocsPopoverUrl();
      if (!docsUrl2) return;
      if (currentState.mode === MODES.POPOVER && currentState.popoverUrl === docsUrl2) {
        this.handleClosePopover();
        return;
      }
      this.handleOpenDocsPopover();
    }
    handleClosePopover() {
      console.log("[KeyPilot] Closing popover");
      this.overlayManager.hidePopover();
      this.state.setPopoverOpen(false, null);
    }
    handleToggleTabHistoryPopover() {
      if (window !== window.top) return;
      try {
        this.tabHistoryPopover?.toggle?.();
      } catch (e) {
        console.warn("[KeyPilot] Failed to toggle tab history popover:", e);
      }
    }
    handleToggleKeyboardHelp() {
      if (this._isPopoverOsWindow || window.__KP_POPOVER_WINDOW) return;
      try {
        const next = !this._isKeyboardHelpPaintedVisible();
        this.applyKeyboardHelpVisibility(next, { persist: true });
        this.emitAction("toggleKeyboardHelp", { visible: next });
      } catch (e) {
        console.warn("[KeyPilot] Failed to toggle keyboard reference:", e);
      }
    }
    deleteElement(element) {
      if (!element || element === document.documentElement || element === document.body) {
        return;
      }
      try {
        element.remove();
      } catch (error) {
        try {
          element.classList.add("kpv2-hidden");
          element.setAttribute("aria-hidden", "true");
        } catch {
        }
      }
    }
    handleEscapeFromTextFocus(currentState) {
      console.debug("Escape pressed in text focus mode (exit only)");
      this._disarmTextModeClick();
      try {
        this.state.setFocusElement(null);
      } catch {
      }
      const focused = currentState?.focusedTextElement || this.focusDetector?.currentFocusedElement;
      let inChildDocument = false;
      try {
        inChildDocument = !!(focused && focused.ownerDocument && focused.ownerDocument !== document);
      } catch {
        inChildDocument = false;
      }
      if (inChildDocument) {
        try {
          focused.blur();
        } catch {
        }
      } else {
        if (document.activeElement) {
          document.activeElement.blur();
        }
        try {
          document.body.focus();
        } catch {
        }
      }
      this.focusDetector.clearTextFocus();
      console.debug("Text focus escape completed");
    }
    /**
     * Cancel all active modes and return to normal mode
     * Handles mode-specific cleanup logic
     */
    cancelModes() {
      const currentState = this.state.getState();
      console.log("[KeyPilot] Canceling modes, current mode:", currentState.mode);
      try {
        if (this.topSitesPopover?.isOpen?.()) {
          this.topSitesPopover.hide();
          return;
        }
      } catch {
      }
      try {
        if (this.launcherPopover?.isOpen?.()) {
          this.launcherPopover.hide();
          return;
        }
      } catch {
      }
      try {
        if (isFontInfoPopoverOpen()) {
          hideFontInfoPopover();
          return;
        }
      } catch {
      }
      try {
        this.unitSelection?.clear?.();
      } catch {
      }
      if (currentState.mode === MODES.HIGHLIGHT) {
        this.cancelHighlightMode();
        return;
      }
      if (currentState.mode === MODES.SCROLL_LINE) {
        this._exitScrollLineMode();
        return;
      }
      if (currentState.mode === MODES.POPOVER) {
        this.handleClosePopover();
        return;
      }
      if (currentState.mode === MODES.OMNIBOX) {
        this.handleCloseOmnibox();
        return;
      }
      if (currentState.mode === MODES.INSPECTOR || currentState.mode === MODES.DELETE || currentState.mode === MODES.COLS) {
        this.inspector.exit();
        try {
          this.overlayManager?.hideInspectorModeIndicator?.();
        } catch {
        }
        try {
          this.overlayManager?.clearInspectorPickedOverlays?.();
        } catch {
        }
        return;
      }
      if (currentState.mode !== MODES.TEXT_FOCUS) {
        this.state.reset();
      }
      try {
        if (this._framePointerInside) {
          this._framePointerInside = false;
          this._framePointerIframe = null;
          this._reclaimKeyboardFocusFromPageIframes();
        }
      } catch {
      }
    }
    handleOpenOmnibox() {
      try {
        if (window !== window.top) return;
        try {
          this._disarmTextModeClick?.();
        } catch {
        }
        try {
          this.focusDetector?.clearTextFocus?.();
        } catch {
        }
        this.state.setMode(MODES.OMNIBOX);
        this.omniboxManager?.show?.("");
      } catch (e) {
        console.warn("[KeyPilot] Failed to open omnibox:", e);
      }
    }
    handleCloseOmnibox() {
      try {
        if (window !== window.top) return;
        this.omniboxManager?.hide?.();
        this.state.setMode(MODES.NONE);
      } catch (e) {
        console.warn("[KeyPilot] Failed to close omnibox:", e);
      }
    }
    showRipple(x, y) {
      const ripple = document.createElement("div");
      ripple.className = CSS_CLASSES.RIPPLE;
      ripple.style.left = `${x}px`;
      ripple.style.top = `${y}px`;
      document.body.appendChild(ripple);
      ripple.addEventListener("animationend", () => ripple.remove(), { once: true });
    }
    /**
     * Top-center flash toast. Optional thumbnailBlob (e.g. after Copy Image)
     * renders a max 150×150 preview to the right of the message.
     *
     * @param {string} message
     * @param {string} [backgroundColor]
     * @param {Blob|null} [thumbnailBlob]
     */
    showFlashNotification(message, backgroundColor = COLORS.NOTIFICATION_SUCCESS, thumbnailBlob = null) {
      try {
        if (!message || typeof message !== "string") {
          console.warn("[KeyPilot] Invalid message provided to showFlashNotification:", message);
          return;
        }
        if (!backgroundColor || typeof backgroundColor !== "string") {
          console.warn("[KeyPilot] Invalid backgroundColor provided, using default");
          backgroundColor = COLORS.NOTIFICATION_SUCCESS;
        }
        if (!document || !document.body) {
          console.warn("[KeyPilot] Document or document.body not available for notification");
          return;
        }
        let notification = null;
        try {
          notification = document.createElement("div");
          if (!notification) {
            throw new Error("Failed to create notification element");
          }
        } catch (createError) {
          console.error("[KeyPilot] Error creating notification element:", createError);
          return;
        }
        notification.className = "kpv2-flash-notification";
        const notificationMount = ensureOpenChromeShadow(notification, { id: "flash-notification" }) || notification;
        const hasThumbnail = thumbnailBlob instanceof Blob && thumbnailBlob.size > 0;
        let objectUrl = null;
        const messageEl = document.createElement("span");
        messageEl.textContent = message;
        notificationMount.appendChild(messageEl);
        if (hasThumbnail) {
          try {
            objectUrl = URL.createObjectURL(thumbnailBlob);
            const thumbBox = document.createElement("div");
            applyFlashNotificationThumbnailStyle(thumbBox);
            const img = document.createElement("img");
            img.src = objectUrl;
            img.alt = "";
            Object.assign(img.style, {
              display: "block",
              maxWidth: "150px",
              maxHeight: "150px",
              width: "auto",
              height: "auto",
              objectFit: "contain"
            });
            thumbBox.appendChild(img);
            notificationMount.appendChild(thumbBox);
          } catch (thumbError) {
            console.warn("[KeyPilot] Failed to render flash thumbnail:", thumbError);
            if (objectUrl) {
              try {
                URL.revokeObjectURL(objectUrl);
              } catch {
              }
              objectUrl = null;
            }
          }
        }
        try {
          applyFlashNotificationStyle(notification, {
            backgroundColor,
            hasThumbnail: !!objectUrl,
            zIndex: Z_INDEX.NOTIFICATION
          });
        } catch (styleError) {
          console.error("[KeyPilot] Error styling notification:", styleError);
          notification.style.position = "fixed";
          notification.style.top = "16px";
          notification.style.left = "50%";
          notification.style.backgroundColor = backgroundColor;
          notification.style.color = "white";
          notification.style.padding = "8px 16px";
          notification.style.zIndex = String(Z_INDEX.NOTIFICATION);
          notification.style.boxShadow = "0 10px 28px rgba(0, 0, 0, 0.50)";
        }
        try {
          document.body.appendChild(notification);
        } catch (appendError) {
          console.error("[KeyPilot] Error appending notification to document:", appendError);
          if (objectUrl) {
            try {
              URL.revokeObjectURL(objectUrl);
            } catch {
            }
          }
          return;
        }
        notification.style.opacity = "1";
        const duration = backgroundColor === COLORS.NOTIFICATION_ERROR ? 4e3 : 2e3;
        setTimeout(() => {
          try {
            if (objectUrl) {
              try {
                URL.revokeObjectURL(objectUrl);
              } catch {
              }
            }
            if (notification && notification.parentNode) {
              notification.parentNode.removeChild(notification);
            }
          } catch (removeError) {
            console.warn("[KeyPilot] Error removing notification:", removeError);
          }
        }, duration);
      } catch (error) {
        console.error("[KeyPilot] Unexpected error showing flash notification:", error);
        try {
          if (window.alert && typeof window.alert === "function") {
            window.alert(`KeyPilot: ${message}`);
          }
        } catch (alertError) {
          console.error("[KeyPilot] Even alert fallback failed:", alertError);
        }
      }
    }
    updateOverlays(focusEl, inspectorEl = null, inspectorKind = null) {
      if (!this.enabled) {
        return;
      }
      const currentState = this.state.getState();
      const inspEl = inspectorEl !== void 0 && inspectorEl !== null ? inspectorEl : currentState.inspectorEl;
      const kind = inspectorKind != null ? inspectorKind : currentState.inspectorKind;
      let focusRectOverride = null;
      try {
        if (focusEl && this.intersectionManager && typeof this.intersectionManager.getDisplayRectForElement === "function") {
          focusRectOverride = this.intersectionManager.getDisplayRectForElement(focusEl, { tolerancePx: 6 });
        }
      } catch {
      }
      this.overlayManager.updateOverlays(
        focusEl,
        inspEl,
        currentState.mode,
        currentState.focusedTextElement,
        focusRectOverride,
        kind
      );
    }
    logPerformanceMetrics() {
      const now = Date.now();
      const timeSinceLastLog = now - this.performanceMetrics.lastMetricsLog;
      if (timeSinceLastLog < 1e4) return;
      const intersectionMetrics = this.intersectionManager.getMetrics();
      const scrollMetrics = this.scrollManager.getScrollMetrics();
      console.group("[KeyPilot] Performance Metrics");
      console.log("Mouse Queries:", this.performanceMetrics.mouseQueries);
      console.log("Intersection Observer Cache Hit Rate:", intersectionMetrics.cacheHitRate);
      console.log("Visible Interactive Elements:", intersectionMetrics.visibleElements);
      console.log("Scroll Throttle Ratio:", scrollMetrics.throttleRatio);
      console.log("Average Scroll Duration:", `${scrollMetrics.averageScrollDuration.toFixed(1)}ms`);
      console.groupEnd();
      this.performanceMetrics.lastMetricsLog = now;
    }
    /**
     * Enable KeyPilot functionality
     */
    async enable() {
      const wasEnabled = !!this.enabled;
      this.enabled = true;
      this._installFrameBridgeListener();
      if (wasEnabled) return;
      if (this.initializationComplete) {
        this.start();
        if (this.cursor) {
          this.cursor.show();
        }
        if (this.focusDetector) {
          this.focusDetector.start();
        }
        if (this.intersectionManager) {
          try {
            if (typeof this.intersectionManager.setDomHoverListenersEnabled === "function") {
              this.intersectionManager.setDomHoverListenersEnabled(
                true,
                (el2) => this._handleDomHoverChange(el2)
              );
            }
          } catch {
          }
          await this.intersectionManager.init();
        }
        try {
          if (this.overlayManager && typeof this.overlayManager.setDomHoverFocusColorsEnabled === "function") {
            this.overlayManager.setDomHoverFocusColorsEnabled(true);
          }
        } catch {
        }
        if (this.scrollManager) {
          this.scrollManager.init();
        }
        this.state.reset();
        try {
          const { lastMouse } = this.state.getState();
          const x = Number(lastMouse?.x);
          const y = Number(lastMouse?.y);
          if (Number.isFinite(x) && Number.isFinite(y) && (x !== 0 || y !== 0)) {
            this.updateElementsUnderCursor(x, y, false, null);
          } else {
            await this.initializeCursorPosition();
            const s2 = this.state.getState();
            const x2 = Number(s2.lastMouse?.x);
            const y2 = Number(s2.lastMouse?.y);
            if (Number.isFinite(x2) && Number.isFinite(y2) && (x2 !== 0 || y2 !== 0)) {
              this.updateElementsUnderCursor(x2, y2, false, null);
            }
          }
        } catch {
        }
      }
      try {
        this.refreshKeyboardHelpVisibilityFromStorage();
      } catch {
      }
      try {
        void this.refreshTopSitesVisibilityFromStorage();
      } catch {
      }
      try {
        this.controlStrip?.setEnabledState?.(true);
        this.controlStrip?.setKeyboardHelpActive?.(!!this._keyboardHelpVisible);
        this._syncControlStripTextModeFromState();
      } catch {
      }
      console.log("[KeyPilot] Extension enabled");
    }
    /**
     * Tear down every transient KeyPilot UI surface (popovers, launcher, omnibox,
     * keyboard help, highlight mode, onboarding panels, flash toasts, etc.).
     * Safe to call while already disabled / partially initialized.
     */
    dismissActiveUI() {
      try {
        if (this.state?.getState?.()?.mode === MODES.HIGHLIGHT) {
          this.cancelHighlightMode?.();
        }
      } catch {
      }
      try {
        this._exitScrollLineMode();
      } catch {
      }
      try {
        this.topSitesPopover?.hide?.({ persistClosed: false });
      } catch {
      }
      try {
        this.launcherPopover?.hide?.();
      } catch {
      }
      try {
        closePageMediaOverlay();
      } catch {
      }
      try {
        this.handleCloseOmnibox?.();
      } catch {
      }
      try {
        this.omniboxManager?.hide?.();
      } catch {
      }
      try {
        this.tabHistoryPopover?.hide?.();
      } catch {
      }
      try {
        this.handleClosePopover?.();
      } catch {
      }
      try {
        this.overlayManager?.hidePopover?.();
      } catch {
      }
      try {
        this.overlayManager?.popupManager?.closeAll?.();
      } catch {
      }
      if (this.floatingKeyboardHelp) {
        try {
          this.floatingKeyboardHelp.cleanup();
        } catch {
        }
        this.floatingKeyboardHelp = null;
      }
      try {
        this.overlayManager?.hideFocusOverlay?.();
        this.overlayManager?.hideDeleteOverlay?.();
        this.overlayManager?.hideEscExitLabel?.();
      } catch {
      }
      try {
        const ob = window.__KeyPilotOnboarding;
        try {
          ob?.panel?.hide?.();
        } catch {
        }
        try {
          ob?.practicePanel?.hide?.();
        } catch {
        }
      } catch {
      }
      try {
        document.querySelectorAll(
          ".kpv2-flash-notification, .kpv2-toggle-notification"
        ).forEach((el2) => {
          try {
            el2.remove();
          } catch {
          }
        });
      } catch {
      }
      try {
        this.state?.reset?.();
      } catch {
      }
    }
    /**
     * Disable KeyPilot functionality
     */
    disable() {
      if (!this.enabled) return;
      this.enabled = false;
      this._framePointerInside = false;
      this._framePointerIframe = null;
      this._uninstallFrameBridgeListener();
      this.dismissActiveUI();
      try {
        this.columnLayoutManager?.clear?.();
      } catch {
      }
      try {
        this.unitSelection?.clear?.();
      } catch {
      }
      try {
        this.controlStrip?.setEnabledState?.(false);
        this.controlStrip?.setKeyboardHelpActive?.(false);
      } catch {
      }
      if (this.initializationComplete) {
        this.stop();
        if (this.cursor) {
          this.cursor.hide();
        }
        if (this.focusDetector) {
          this.focusDetector.stop();
        }
        if (this.intersectionManager) {
          try {
            this.intersectionManager.setDomHoverListenersEnabled(false, null);
          } catch {
          }
          this.intersectionManager.cleanup();
        }
        if (this.scrollManager) {
          this.scrollManager.cleanup();
        }
        if (this.rectangleIntersectionObserver) {
          this.rectangleIntersectionObserver.cleanup();
          this.rectangleIntersectionObserver = null;
        }
        this.state.reset();
      }
      console.log("[KeyPilot] Extension disabled");
    }
    /**
     * Initialize cursor position using stored coordinates or fallback
     */
    async initializeCursorPosition() {
      const currentState = this.state.getState();
      if (currentState.lastMouse.x === 0 && currentState.lastMouse.y === 0) {
        const initialPosition = this.mouseCoordinateManager.getInitialCursorPosition();
        console.log("[KeyPilot] Initializing cursor position:", initialPosition);
        this.state.setMousePosition(initialPosition.x, initialPosition.y);
        this.cursor.updatePosition(initialPosition.x, initialPosition.y);
        this.mouseCoordinateManager.startInactiveMouseMonitoring();
      }
    }
    /**
     * Check if KeyPilot is currently enabled
     */
    isEnabled() {
      return this.enabled;
    }
    /**
     * Show/hide the shadow-root paint debug HUD (leaf, focusEl, paint target, Auto / B→C / A/B/C).
     * Console: `keyPilot.setShadowRootDebugHud(true)`
     * @param {boolean} enabled
     */
    setShadowRootDebugHud(enabled) {
      try {
        this.overlayManager?.setShadowRootDebugHud?.(!!enabled);
      } catch (e) {
        console.warn("[KeyPilot] setShadowRootDebugHud failed:", e);
      }
    }
    /**
     * Force hover paint strategy while the shadow debug HUD is open.
     * @param {'A'|'B'|'C'|'BC'|null|string} strategy
     *   null/'auto' restores full automatic choice (A→B→C).
     *   'BC' / 'B→C' = Auto B→C (skip A; try in-target then fixed).
     */
    setShadowDebugPaintStrategy(strategy) {
      try {
        this.overlayManager?.setShadowDebugPaintStrategy?.(strategy);
      } catch (e) {
        console.warn("[KeyPilot] setShadowDebugPaintStrategy failed:", e);
      }
    }
    cleanup() {
      try {
        this.dismissActiveUI();
      } catch {
      }
      try {
        this.columnLayoutManager?.clear?.();
      } catch {
      }
      try {
        if (this.controlStrip) {
          this.controlStrip.cleanup();
          this.controlStrip = null;
        }
      } catch {
      }
      try {
        this._scrollLineOverlay?.destroy?.();
      } catch {
      }
      try {
        this._scrollHold?.reset?.();
      } catch {
      }
      this.stop();
      if (this.intersectionManager) {
        this.intersectionManager.cleanup();
      }
      if (this.scrollManager) {
        this.scrollManager.cleanup();
      }
      if (this.focusDetector) {
        this.focusDetector.stop();
      }
      if (this.mouseCoordinateManager) {
        this.mouseCoordinateManager.cleanup();
      }
      if (this.cursor) {
        this.cursor.cleanup();
      }
      if (this.overlayManager) {
        this.overlayManager.cleanup();
      }
      try {
        this.unitSelection?.clear?.();
      } catch {
      }
      if (this.styleManager) {
        this.styleManager.cleanup();
      }
      if (this.shadowDOMManager) {
        this.shadowDOMManager.cleanup();
      }
      if (this._boundScrollEndHandler) {
        document.removeEventListener("keypilot:scroll-end", this._boundScrollEndHandler);
        this._boundScrollEndHandler = null;
      }
    }
  };

  // src/modules/keypilot-toggle-handler.js
  init_constants();
  var KeyPilotToggleHandler = class extends EventManager {
    constructor(keyPilotInstance) {
      super();
      this.keyPilot = keyPilotInstance;
      this.enabled = true;
      this.initialized = false;
      this.globalToggleKeyHandler = null;
      this.ENABLED_STORAGE_KEY = "keypilot_enabled";
      this._onRuntimeMessage = this._onRuntimeMessage.bind(this);
      this._onStorageChanged = this._onStorageChanged.bind(this);
      this.originalMethods = {
        handleKeyDown: null,
        handleMouseMove: null,
        handleScroll: null
      };
    }
    /**
     * Initialize the toggle handler
     * Queries service worker for current state and sets up message listener
     */
    async initialize() {
      try {
        const response = await chrome.runtime.sendMessage({ type: MSG.GET_STATE });
        if (response && typeof response.enabled === "boolean") {
          this.setEnabled(response.enabled, false);
        } else {
          this.setEnabled(true, false);
        }
      } catch (error) {
        console.warn("[KeyPilotToggleHandler] Failed to query service worker state:", error);
        this.setEnabled(true, false);
      }
      try {
        installContentRuntimeRouter();
        this._runtimeDisposers = [
          registerContentRuntimeHandler(MSG.TOGGLE_STATE, this._onRuntimeMessage),
          registerContentRuntimeHandler(MSG.UPDATE_STATE, this._onRuntimeMessage)
        ];
      } catch {
      }
      try {
        if (chrome?.storage?.onChanged?.addListener) {
          chrome.storage.onChanged.addListener(this._onStorageChanged);
        }
      } catch {
      }
      this.globalToggleKeyHandler = (e) => {
        try {
          if (e && e.__kpToggleHandled) return;
          if (e && e.altKey && (e.key === "k" || e.key === "K" || e.code === "KeyK")) {
            e.__kpToggleHandled = true;
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();
            if (typeof chrome !== "undefined" && chrome.runtime) {
              chrome.runtime.sendMessage({ type: MSG.TOGGLE_STATE }).catch(() => {
              });
            }
            return;
          }
          if (e && e.altKey && (e.key === "j" || e.key === "J" || e.code === "KeyJ")) {
            if (e.__kpControlStripHandled) return;
            e.__kpControlStripHandled = true;
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();
            try {
              const kp = this.keyPilot;
              if (typeof kp?.toggleControlStripFromHotkey === "function") {
                kp.toggleControlStripFromHotkey();
              } else {
                kp?.showControlStripFromHotkey?.();
              }
            } catch {
            }
          }
        } catch {
        }
      };
      document.addEventListener("keydown", this.globalToggleKeyHandler, { capture: true });
      this.initialized = true;
    }
    /**
     * @param {any} message
     * @param {chrome.runtime.MessageSender} _sender
     * @param {(response?: any) => void} sendResponse
     */
    _onRuntimeMessage(message, _sender, sendResponse) {
      try {
        if (message?.type === MSG.TOGGLE_STATE || message?.type === MSG.UPDATE_STATE) {
          if (typeof message.enabled === "boolean") {
            void this.setEnabled(message.enabled, true);
            try {
              sendResponse({ success: true });
            } catch {
            }
            return true;
          }
        }
      } catch {
      }
      return false;
    }
    /**
     * @param {Record<string, chrome.storage.StorageChange>} changes
     * @param {string} area
     */
    _onStorageChanged(changes, area) {
      try {
        if (area !== "sync" && area !== "local") return;
        if (!changes || !Object.prototype.hasOwnProperty.call(changes, this.ENABLED_STORAGE_KEY)) return;
        const entry = changes[this.ENABLED_STORAGE_KEY];
        const next = entry?.newValue;
        if (typeof next !== "boolean") return;
        void this.setEnabled(next, false);
      } catch {
      }
    }
    /**
     * Keep the control strip On/Off segment aligned with extension state.
     * Safe when the strip is not yet constructed (no-op).
     * @param {boolean} enabled
     */
    _syncControlStripEnabled(enabled) {
      try {
        const on = !!enabled;
        this.keyPilot?.controlStrip?.setEnabledState?.(on);
        if (!on) {
          this.keyPilot?.controlStrip?.setKeyboardHelpActive?.(false);
        } else {
          this.keyPilot?.controlStrip?.setKeyboardHelpActive?.(!!this.keyPilot?._keyboardHelpVisible);
        }
      } catch {
      }
    }
    /**
     * Enable or disable KeyPilot functionality
     * @param {boolean} enabled - Whether KeyPilot should be enabled
     * @param {boolean} showNotification - Whether to show toggle notification (default: true)
     */
    async setEnabled(enabled, showNotification = true) {
      const next = !!enabled;
      this._syncControlStripEnabled(next);
      if (this.enabled === next) {
        try {
          if (this.keyPilot && this.keyPilot.enabled !== next) {
            if (next) await this.enableKeyPilot();
            else this.disableKeyPilot();
          }
        } catch {
        }
        return;
      }
      if (window.KEYPILOT_EARLY) {
        window.KEYPILOT_EARLY.setEnabled(next);
      }
      this.enabled = next;
      try {
        document.dispatchEvent(new CustomEvent("keypilot:action", {
          detail: {
            action: "toggleExtension",
            enabled: next,
            timestamp: Date.now()
          }
        }));
      } catch {
      }
      if (next) {
        await this.enableKeyPilot();
      } else {
        this.disableKeyPilot();
      }
      this._syncControlStripEnabled(next);
      if (showNotification) {
        this.showToggleNotification(next);
      }
    }
    /**
     * Enable KeyPilot functionality
     * Restores event listeners, CSS styles, and visual elements
     */
    async enableKeyPilot() {
      if (!this.keyPilot) return;
      try {
        try {
          this.keyPilot.enabled = true;
        } catch {
        }
        if (this.keyPilot.styleManager) {
          try {
            this.keyPilot.styleManager.restoreAllStyles();
          } catch (error) {
            console.warn("[KeyPilotToggleHandler] Cannot restore styles on this page:", error.message);
          }
        }
        try {
          this.keyPilot.start();
        } catch (error) {
          console.warn("[KeyPilotToggleHandler] Cannot start event listeners on this page:", error.message);
        }
        if (this.keyPilot.cursor) {
          try {
            this.keyPilot.cursor.ensure();
            this.keyPilot.cursor.show();
          } catch (error) {
            console.warn("[KeyPilotToggleHandler] Cannot show cursor on this page:", error.message);
          }
        }
        if (this.keyPilot.focusDetector) {
          try {
            this.keyPilot.focusDetector.start();
          } catch (error) {
            console.warn("[KeyPilotToggleHandler] Cannot start focus detector on this page:", error.message);
          }
        }
        if (this.keyPilot.intersectionManager) {
          try {
            if (typeof this.keyPilot.intersectionManager.setDomHoverListenersEnabled === "function") {
              this.keyPilot.intersectionManager.setDomHoverListenersEnabled(
                true,
                (el2) => this.keyPilot._handleDomHoverChange?.(el2)
              );
            }
          } catch (error) {
            console.warn("[KeyPilotToggleHandler] Cannot restore DOM hover listeners:", error.message);
          }
          try {
            await this.keyPilot.intersectionManager.init();
          } catch (error) {
            console.warn("[KeyPilotToggleHandler] Cannot initialize intersection manager on this page:", error.message);
          }
        }
        if (this.keyPilot.scrollManager) {
          try {
            this.keyPilot.scrollManager.init();
          } catch (error) {
            console.warn("[KeyPilotToggleHandler] Cannot initialize scroll manager on this page:", error.message);
          }
        }
        if (this.keyPilot.overlayManager) {
          try {
            this.keyPilot.overlayManager.setupOverlayObserver();
            if (this.keyPilot.overlayManager.highlightManager) {
              this.keyPilot.overlayManager.highlightManager.initialize(this.keyPilot.overlayManager.overlayObserver);
            }
            try {
              this.keyPilot.overlayManager.setRenderingMode?.("dom");
            } catch {
            }
            if (typeof this.keyPilot.overlayManager.setDomHoverFocusColorsEnabled === "function") {
              this.keyPilot.overlayManager.setDomHoverFocusColorsEnabled(true);
            }
            this.keyPilot.overlayManager.initDebugPanel?.();
          } catch (error) {
            console.warn("[KeyPilotToggleHandler] Cannot restore overlays on this page:", error.message);
          }
        }
        try {
          const st = this.keyPilot.state?.getState?.();
          const x = Number(st?.lastMouse?.x);
          const y = Number(st?.lastMouse?.y);
          if (Number.isFinite(x) && Number.isFinite(y) && (x !== 0 || y !== 0)) {
            this.keyPilot.updateElementsUnderCursor?.(x, y, false, null);
          }
        } catch {
        }
        try {
          this.keyPilot._framePointerInside = false;
          this.keyPilot._framePointerIframe = null;
          this.keyPilot._installFrameBridgeListener?.();
        } catch {
        }
        try {
          this.keyPilot.refreshKeyboardHelpVisibilityFromStorage?.();
        } catch {
        }
        console.log("[KeyPilotToggleHandler] KeyPilot enabled");
      } catch (error) {
        console.error("[KeyPilotToggleHandler] Error enabling KeyPilot:", error);
      }
    }
    /**
     * Disable KeyPilot functionality
     * Removes event listeners, CSS styles, and all visual elements
     */
    disableKeyPilot() {
      if (!this.keyPilot) return;
      try {
        try {
          if (typeof this.keyPilot.dismissActiveUI === "function") {
            this.keyPilot.dismissActiveUI();
          } else if (typeof this.keyPilot.disable === "function" && this.keyPilot.enabled) {
            this.keyPilot.disable();
          }
        } catch (e) {
          console.warn("[KeyPilotToggleHandler] dismissActiveUI failed:", e);
        }
        try {
          this.keyPilot.enabled = false;
        } catch {
        }
        try {
          this.keyPilot._framePointerInside = false;
          this.keyPilot._framePointerIframe = null;
          this.keyPilot._uninstallFrameBridgeListener?.();
        } catch {
        }
        this.keyPilot.stop();
        if (this.keyPilot.floatingKeyboardHelp) {
          try {
            this.keyPilot.floatingKeyboardHelp.cleanup();
          } catch {
          }
          this.keyPilot.floatingKeyboardHelp = null;
        }
        if (this.keyPilot.cursor) {
          this.keyPilot.cursor.cleanup();
        }
        if (this.keyPilot.focusDetector) {
          this.keyPilot.focusDetector.stop();
        }
        if (this.keyPilot.overlayManager) {
          this.keyPilot.overlayManager.cleanup();
        }
        if (this.keyPilot.intersectionManager) {
          this.keyPilot.intersectionManager.cleanup();
        }
        if (this.keyPilot.scrollManager) {
          this.keyPilot.scrollManager.cleanup();
        }
        if (this.keyPilot.state) {
          this.keyPilot.state.reset();
        }
        if (this.keyPilot.styleManager) {
          this.keyPilot.styleManager.removeAllStyles();
        }
        console.log("[KeyPilotToggleHandler] KeyPilot disabled - all styles and elements removed");
      } catch (error) {
        console.error("[KeyPilotToggleHandler] Error disabling KeyPilot:", error);
      }
    }
    /**
     * Show toggle notification to user
     * @param {boolean} enabled - Whether KeyPilot was enabled or disabled
     */
    showToggleNotification(enabled) {
      const notification = document.createElement("div");
      notification.className = "kpv2-toggle-notification";
      const notificationMount = ensureOpenChromeShadow(notification, { id: "toggle-notification" }) || notification;
      const message = document.createElement("span");
      message.textContent = enabled ? "KeyPilot Enabled" : "KeyPilot Disabled";
      notificationMount.appendChild(message);
      applyFlashNotificationStyle(notification, {
        backgroundColor: enabled ? COLORS.NOTIFICATION_SUCCESS : COLORS.NOTIFICATION_ERROR,
        zIndex: Z_INDEX.NOTIFICATION
      });
      document.body.appendChild(notification);
      notification.style.opacity = "1";
      setTimeout(() => {
        if (notification.parentNode) {
          notification.parentNode.removeChild(notification);
        }
      }, 2e3);
    }
    /**
     * Get current enabled state
     * @returns {boolean} Whether KeyPilot is currently enabled
     */
    isEnabled() {
      return this.enabled;
    }
    /**
     * Clean up the toggle handler
     */
    cleanup() {
      try {
        for (const dispose of this._runtimeDisposers || []) {
          try {
            dispose();
          } catch {
          }
        }
        this._runtimeDisposers = [];
      } catch {
      }
      try {
        if (chrome?.storage?.onChanged?.removeListener) {
          chrome.storage.onChanged.removeListener(this._onStorageChanged);
        }
      } catch {
      }
      if (chrome.runtime && chrome.runtime.onMessage && this.handleMessage) {
        chrome.runtime.onMessage.removeListener(this.handleMessage);
      }
      if (this.globalToggleKeyHandler) {
        try {
          document.removeEventListener("keydown", this.globalToggleKeyHandler, { capture: true });
        } catch {
        }
        this.globalToggleKeyHandler = null;
      }
      if (!this.enabled && this.keyPilot) {
        this.keyPilot.cleanup();
      }
    }
  };

  // src/modules/onboarding-manager.js
  init_constants();

  // src/ui/onboarding-panel.js
  init_constants();
  var REENABLE_TIP_ARROW_TOP = "#9a9a9a";
  var REENABLE_TIP_ARROW_BOTTOM = "#707070";
  var TOGGLE_OFF_ARROW_STYLE_ID = "kp-onboarding-toggle-off-arrow-style-v3";
  var TOGGLE_OFF_ARROW_SCALE = 1.5;
  var TOGGLE_OFF_ARROW_LEFT_NUDGE_PX = 5;
  function getControlStripStatusAnchor() {
    try {
      const strip = document.querySelector('.kp-control-strip, [data-kp-control-strip="true"]');
      const status = strip?.shadowRoot?.querySelector?.('[data-kp-control-strip-status="true"]');
      return status || strip || null;
    } catch {
      return null;
    }
  }
  function stripListeners(btn) {
    try {
      if (!btn || !btn.parentNode) return btn;
      const clone = btn.cloneNode(true);
      btn.parentNode.replaceChild(clone, btn);
      return clone;
    } catch {
      return btn;
    }
  }
  function resolveClickFocusColor() {
    try {
      const kp = window.__KeyPilotInstance;
      const focusColor = kp?._settings?.clickMode?.focusColor;
      if (focusColor === "green") {
        return "rgba(0,180,0,0.95)";
      }
    } catch {
    }
    return COLORS.FOCUS_BLUE || "rgba(33,150,243,0.95)";
  }
  function resolveClickFocusGlow(accent) {
    const c = String(accent || "");
    if (c.includes("0,180,0") || c.includes("0, 180, 0") || /green/i.test(c)) {
      return "rgba(80,255,120,0.95)";
    }
    return "rgba(120,210,255,0.95)";
  }
  function ensureToggleOffArrowStyles(root) {
    try {
      if (!root) return;
      injectChromeStyles(root, {
        attr: TOGGLE_OFF_ARROW_STYLE_ID,
        css: `
@keyframes kp-onboarding-toggle-off-arrow-osc {
  0%, 100% { transform: translateX(0); }
  50% { transform: translateX(6px); }
}
@keyframes kp-onboarding-toggle-off-arrow-glow {
  0%, 100% {
    filter:
      drop-shadow(0 0 2px #fff)
      drop-shadow(0 0 6px var(--kp-arrow-accent, currentColor))
      drop-shadow(0 0 14px var(--kp-arrow-glow, currentColor))
      drop-shadow(0 0 22px var(--kp-arrow-glow, currentColor))
      drop-shadow(0 1px 2px rgba(0,0,0,0.45));
  }
  50% {
    filter:
      drop-shadow(0 0 3px #fff)
      drop-shadow(0 0 10px var(--kp-arrow-accent, currentColor))
      drop-shadow(0 0 20px var(--kp-arrow-glow, currentColor))
      drop-shadow(0 0 32px var(--kp-arrow-glow, currentColor))
      drop-shadow(0 1px 2px rgba(0,0,0,0.45));
  }
}
:host {
  animation:
    kp-onboarding-toggle-off-arrow-osc 1.15s ease-in-out infinite,
    kp-onboarding-toggle-off-arrow-glow 1.5s ease-in-out infinite;
  will-change: transform, filter;
}
:host svg {
  display: block;
  overflow: visible;
}
`
      });
    } catch {
    }
  }
  function applyToggleOffArrowColor(el2, accent) {
    if (!el2) return;
    const color4 = String(accent || resolveClickFocusColor());
    const glow = resolveClickFocusGlow(color4);
    try {
      el2.style.color = color4;
      el2.style.fill = color4;
      el2.style.setProperty("--kp-arrow-accent", color4);
      el2.style.setProperty("--kp-arrow-glow", glow);
    } catch {
    }
  }
  function nextIncompleteTaskId(tasks, completedTaskIds) {
    const done = completedTaskIds instanceof Set ? completedTaskIds : new Set(Array.isArray(completedTaskIds) ? completedTaskIds.map(String) : []);
    for (const task of tasks || []) {
      const id = String(task?.id || "");
      if (id && !done.has(id)) return id;
    }
    return "";
  }
  function ensureKeyboardReferenceOpenAndExpanded() {
    const apply = () => {
      try {
        const kp = window.__KeyPilotInstance;
        if (!kp) return false;
        if (typeof kp.applyKeyboardHelpVisibility === "function") {
          kp.applyKeyboardHelpVisibility(true, { persist: true });
        }
        try {
          kp.floatingKeyboardHelp?.setCollapsed?.(false, { persist: true });
        } catch {
        }
        return true;
      } catch {
        return false;
      }
    };
    apply();
    try {
      requestAnimationFrame(() => {
        apply();
        try {
          window.setTimeout(apply, 0);
        } catch {
        }
      });
    } catch {
      try {
        window.setTimeout(apply, 0);
      } catch {
      }
    }
  }
  var OnboardingPanel = class {
    /**
     * @param {Object} params
     * @param {() => void} params.onRequestClose
     * @param {() => void} [params.onRequestPrev]
     * @param {() => void} [params.onRequestNext]
     * @param {() => void} [params.onRequestReset]
     * @param {(taskId: string) => void} [params.onRequestUncheckTask]
     */
    constructor({ onRequestClose, onRequestPrev, onRequestNext, onRequestReset, onRequestUncheckTask } = {}) {
      this.root = null;
      this.body = null;
      this.slideSurface = null;
      this.titleEl = null;
      this.stepEl = null;
      this.closeBtn = null;
      this.prevBtn = null;
      this.nextBtn = null;
      this.resetBtn = null;
      this._overlayEl = null;
      this._overlayTitleEl = null;
      this._overlayMsgEl = null;
      this._overlayPrimaryBtn = null;
      this._overlaySecondaryBtn = null;
      this._overlayOnPrimary = null;
      this._overlayOnSecondary = null;
      this._overlayOpen = false;
      this._lastRenderedSlideId = null;
      this._lastRenderedSlideIndex = null;
      this._keyboardKeyInfoStepActive = false;
      this._onRequestClose = typeof onRequestClose === "function" ? onRequestClose : null;
      this._onRequestPrev = typeof onRequestPrev === "function" ? onRequestPrev : null;
      this._onRequestNext = typeof onRequestNext === "function" ? onRequestNext : null;
      this._onRequestReset = typeof onRequestReset === "function" ? onRequestReset : null;
      this._onRequestUncheckTask = typeof onRequestUncheckTask === "function" ? onRequestUncheckTask : null;
      this._onCloseClick = this._onCloseClick.bind(this);
      this._onPrevClick = this._onPrevClick.bind(this);
      this._onNextClick = this._onNextClick.bind(this);
      this._onResetClick = this._onResetClick.bind(this);
      this._onOverlayPrimary = this._onOverlayPrimary.bind(this);
      this._onOverlaySecondary = this._onOverlaySecondary.bind(this);
      this._onTaskRowClick = this._onTaskRowClick.bind(this);
      this._reEnableTipEl = null;
      this._reEnableTipArrow = null;
      this._reEnableTipMsg = null;
      this._onReEnableTipOutside = this._onReEnableTipOutside.bind(this);
      this._onReEnableTipResize = this._onReEnableTipResize.bind(this);
      this._reEnableTipAnchor = null;
      this._toggleOffArrowEl = null;
      this._toggleOffArrowAnchor = null;
      this._toggleOffArrowRaf = 0;
      this._onToggleOffArrowReposition = this._onToggleOffArrowReposition.bind(this);
    }
    /**
     * Open + expand Keyboard Reference for the hover-key onboarding task.
     */
    ensureKeyboardReferenceOpenAndExpanded() {
      this._keyboardKeyInfoStepActive = true;
      ensureKeyboardReferenceOpenAndExpanded();
    }
    isVisible() {
      return !!(this.root && this.root.isConnected && this.root.hidden === false);
    }
    /**
     * Whether the modal slide overlay is currently open (user has not accepted it yet).
     * While true, checklist tasks must not be checked off.
     * @returns {boolean}
     */
    isOverlayOpen() {
      if (this._overlayOpen) return true;
      try {
        if (this.root?.dataset?.kpOnboardingOverlayOpen === "true") return true;
        if (this._overlayEl && this._overlayEl.hidden === false && this._overlayEl.style?.display !== "none") {
          return true;
        }
      } catch {
      }
      return false;
    }
    show() {
      if (window !== window.top) return;
      this._ensure();
      setOnboardingPanelVisible(this.root, true);
      if (this._keyboardKeyInfoStepActive) {
        ensureKeyboardReferenceOpenAndExpanded();
      }
    }
    hide() {
      this.hideToggleOffArrow();
      setOnboardingPanelVisible(this.root, false);
    }
    /**
     * Small speech-bubble under the control strip status control.
     * Click outside dismisses it (interrupted); status click is ignored as outside.
     *
     * @param {Object} [opts]
     * @param {Element|null} [opts.anchorEl]
     * @param {string} [opts.message]
     */
    showReEnableTip({
      anchorEl = null,
      message = "Click the control strip again to turn KeyPilot back on."
    } = {}) {
      if (window !== window.top) return;
      this.hideReEnableTip();
      const anchor = anchorEl || getControlStripStatusAnchor();
      if (!anchor || !anchor.isConnected) return;
      const tip = document.createElement("div");
      tip.setAttribute("data-kp-onboarding-reenable-tip", "true");
      tip.setAttribute("role", "status");
      const tipMount = ensureOpenChromeShadow(tip, { id: "onboarding-reenable-tip" }) || tip;
      Object.assign(tip.style, {
        position: "fixed",
        zIndex: String((Z_INDEX.ONBOARDING_PANEL || ONBOARDING_PANEL_Z_FALLBACK) + 2),
        maxWidth: "260px",
        padding: "10px 12px",
        borderRadius: "3px",
        border: ONBOARDING_METAL.panelBorder,
        background: ONBOARDING_METAL.panelBg,
        color: ONBOARDING_METAL.fg,
        boxShadow: ONBOARDING_METAL.panelShadow,
        fontFamily: "Helvetica, Arial, sans-serif",
        fontSize: "13px",
        fontWeight: "600",
        lineHeight: "1.35",
        pointerEvents: "auto"
      });
      const arrow = document.createElement("div");
      arrow.setAttribute("data-kp-onboarding-reenable-tip-arrow", "true");
      Object.assign(arrow.style, {
        position: "absolute",
        width: "0",
        height: "0",
        borderLeft: "8px solid transparent",
        borderRight: "8px solid transparent",
        borderBottom: `8px solid ${REENABLE_TIP_ARROW_TOP}`,
        top: "-8px",
        left: "20px",
        filter: "drop-shadow(0 -1px 0 rgba(42,52,62,0.92))"
      });
      const msg = document.createElement("div");
      msg.textContent = String(message || "");
      tipMount.appendChild(arrow);
      tipMount.appendChild(msg);
      try {
        applyPopupThemeVars(tip);
      } catch {
      }
      (document.body || document.documentElement).appendChild(tip);
      this._reEnableTipEl = tip;
      this._reEnableTipArrow = arrow;
      this._reEnableTipMsg = msg;
      this._reEnableTipAnchor = anchor;
      this._positionReEnableTip();
      try {
        document.addEventListener("pointerdown", this._onReEnableTipOutside, true);
        window.addEventListener("resize", this._onReEnableTipResize, true);
        window.addEventListener("scroll", this._onReEnableTipResize, true);
      } catch {
      }
    }
    hideReEnableTip() {
      try {
        document.removeEventListener("pointerdown", this._onReEnableTipOutside, true);
        window.removeEventListener("resize", this._onReEnableTipResize, true);
        window.removeEventListener("scroll", this._onReEnableTipResize, true);
      } catch {
      }
      try {
        if (this._reEnableTipEl && this._reEnableTipEl.parentNode) {
          this._reEnableTipEl.parentNode.removeChild(this._reEnableTipEl);
        }
      } catch {
      }
      this._reEnableTipEl = null;
      this._reEnableTipArrow = null;
      this._reEnableTipMsg = null;
      this._reEnableTipAnchor = null;
    }
    isReEnableTipVisible() {
      return !!(this._reEnableTipEl && this._reEnableTipEl.isConnected);
    }
    _positionReEnableTip() {
      const tip = this._reEnableTipEl;
      const anchor = this._reEnableTipAnchor;
      if (!tip || !anchor || !anchor.isConnected) return;
      try {
        const rect = anchor.getBoundingClientRect();
        const tipRect = tip.getBoundingClientRect();
        const gap = 10;
        let left = Math.round(rect.left + rect.width / 2 - Math.min(tipRect.width, 260) / 2);
        left = Math.max(8, Math.min(left, window.innerWidth - tipRect.width - 8));
        let top = Math.round(rect.bottom + gap);
        if (top + tipRect.height > window.innerHeight - 8) {
          top = Math.max(8, Math.round(rect.top - tipRect.height - gap));
          if (this._reEnableTipArrow) {
            Object.assign(this._reEnableTipArrow.style, {
              top: "auto",
              bottom: "-8px",
              borderBottom: "none",
              borderTop: `8px solid ${REENABLE_TIP_ARROW_BOTTOM}`,
              filter: "drop-shadow(0 1px 0 rgba(42,52,62,0.92))"
            });
          }
        } else if (this._reEnableTipArrow) {
          Object.assign(this._reEnableTipArrow.style, {
            top: "-8px",
            bottom: "auto",
            borderTop: "none",
            borderBottom: `8px solid ${REENABLE_TIP_ARROW_TOP}`,
            filter: "drop-shadow(0 -1px 0 rgba(42,52,62,0.92))"
          });
        }
        tip.style.left = `${left}px`;
        tip.style.top = `${top}px`;
        if (this._reEnableTipArrow) {
          const arrowLeft = Math.round(rect.left + rect.width / 2 - left - 8);
          this._reEnableTipArrow.style.left = `${Math.max(12, Math.min(arrowLeft, tipRect.width - 24))}px`;
        }
      } catch {
      }
    }
    _onReEnableTipOutside(e) {
      try {
        const t = e?.target;
        if (!t || !this._reEnableTipEl) return;
        if (this._reEnableTipEl.contains(t)) return;
        if (typeof t.closest === "function") {
          if (t.closest('[data-kp-control-strip-status="true"]')) return;
          if (t.closest('[data-kp-onboarding-reenable-tip="true"]')) return;
        }
        this.hideReEnableTip();
      } catch {
      }
    }
    _onReEnableTipResize() {
      this._positionReEnableTip();
    }
    /**
     * Large arrow to the right of the control strip (points left at the strip’s
     * right edge). Only for the toggle-off onboarding step; call hide when the
     * step completes or changes.
     *
     * @param {Object} [opts]
     * @param {Element|null} [opts.anchorEl] ON/OFF segment (preferred) or strip root
     * @param {string} [opts.color] CSS color; defaults to click-focus accent
     */
    showToggleOffArrow({ anchorEl = null, color: color4 = "" } = {}) {
      if (window !== window.top) return;
      const anchor = anchorEl || getControlStripStatusAnchor();
      if (!anchor || !anchor.isConnected) {
        this.hideToggleOffArrow();
        return;
      }
      if (this._toggleOffArrowEl && this._toggleOffArrowEl.isConnected && this._toggleOffArrowAnchor === anchor) {
        applyToggleOffArrowColor(this._toggleOffArrowEl, color4 || resolveClickFocusColor());
        this._positionToggleOffArrow();
        return;
      }
      this.hideToggleOffArrow();
      const accent = String(color4 || resolveClickFocusColor());
      const el2 = document.createElement("div");
      el2.setAttribute("data-kp-onboarding-toggle-off-arrow", "true");
      el2.setAttribute("aria-hidden", "true");
      Object.assign(el2.style, {
        position: "fixed",
        zIndex: String((Z_INDEX.ONBOARDING_PANEL || ONBOARDING_PANEL_Z_FALLBACK) + 3),
        width: "28px",
        height: "28px",
        margin: "0",
        padding: "0",
        pointerEvents: "none",
        lineHeight: "0"
      });
      applyToggleOffArrowColor(el2, accent);
      const arrowMount = ensureOpenChromeShadow(el2, { id: "onboarding-toggle-off-arrow" }) || el2;
      ensureToggleOffArrowStyles(arrowMount);
      arrowMount.innerHTML = '<svg viewBox="0 0 32 28" width="100%" height="100%" focusable="false" aria-hidden="true"><path d="M14 2 L2 14 L14 26 L14 19 L30 19 L30 9 L14 9 Z" fill="currentColor"/></svg>';
      (document.body || document.documentElement).appendChild(el2);
      this._toggleOffArrowEl = el2;
      this._toggleOffArrowAnchor = anchor;
      this._positionToggleOffArrow();
      this._bindToggleOffArrowWatch();
    }
    hideToggleOffArrow() {
      this._unbindToggleOffArrowWatch();
      try {
        if (this._toggleOffArrowEl && this._toggleOffArrowEl.parentNode) {
          this._toggleOffArrowEl.parentNode.removeChild(this._toggleOffArrowEl);
        }
      } catch {
      }
      this._toggleOffArrowEl = null;
      this._toggleOffArrowAnchor = null;
    }
    isToggleOffArrowVisible() {
      return !!(this._toggleOffArrowEl && this._toggleOffArrowEl.isConnected);
    }
    _bindToggleOffArrowWatch() {
      this._unbindToggleOffArrowWatch();
      try {
        window.addEventListener("resize", this._onToggleOffArrowReposition, true);
        window.addEventListener("scroll", this._onToggleOffArrowReposition, true);
      } catch {
      }
      const tick = () => {
        this._positionToggleOffArrow();
        if (this._toggleOffArrowEl && this._toggleOffArrowEl.isConnected) {
          this._toggleOffArrowRaf = window.requestAnimationFrame(tick);
        } else {
          this._toggleOffArrowRaf = 0;
        }
      };
      try {
        this._toggleOffArrowRaf = window.requestAnimationFrame(tick);
      } catch {
        this._toggleOffArrowRaf = 0;
      }
    }
    _unbindToggleOffArrowWatch() {
      try {
        window.removeEventListener("resize", this._onToggleOffArrowReposition, true);
        window.removeEventListener("scroll", this._onToggleOffArrowReposition, true);
      } catch {
      }
      if (this._toggleOffArrowRaf) {
        try {
          window.cancelAnimationFrame(this._toggleOffArrowRaf);
        } catch {
        }
        this._toggleOffArrowRaf = 0;
      }
    }
    _onToggleOffArrowReposition() {
      this._positionToggleOffArrow();
    }
    _positionToggleOffArrow() {
      const el2 = this._toggleOffArrowEl;
      const anchor = this._toggleOffArrowAnchor;
      if (!el2 || !anchor || !anchor.isConnected) {
        if (el2 && (!anchor || !anchor.isConnected)) this.hideToggleOffArrow();
        return;
      }
      try {
        if (anchor.hidden || anchor.style && anchor.style.display === "none") {
          el2.style.visibility = "hidden";
          return;
        }
        const rect = anchor.getBoundingClientRect();
        if (!rect || rect.width < 2 || rect.height < 2) {
          el2.style.visibility = "hidden";
          return;
        }
        el2.style.visibility = "visible";
        const baseH = Math.max(24, Math.round(rect.height || 28));
        const h = Math.round(baseH * TOGGLE_OFF_ARROW_SCALE);
        const w = Math.round(baseH * 1.15 * TOGGLE_OFF_ARROW_SCALE);
        const gap = 4;
        el2.style.height = `${h}px`;
        el2.style.width = `${w}px`;
        el2.style.top = `${Math.round(rect.top + rect.height / 2 - h / 2)}px`;
        el2.style.left = `${Math.round(rect.right + gap - TOGGLE_OFF_ARROW_LEFT_NUDGE_PX)}px`;
      } catch {
      }
    }
    /**
     * @param {Object} params
     * @param {string} params.title
     * @param {string} [params.bodyText]
     * @param {string} params.slideId
     * @param {number} params.slideIndex
     * @param {number} params.slideCount
     * @param {Array} params.tasks
     * @param {Set<string>} params.completedTaskIds
     * @param {{type:'slide', dir:1|-1}|null} [params.transition]
     * @param {boolean} [params.forceRebuild]
     * @returns {Promise<void>}
     */
    async render({
      title,
      bodyText = "",
      slideId,
      slideIndex,
      slideCount,
      tasks,
      completedTaskIds,
      lastCompletedTaskId = null,
      showTip = false,
      showCloseButton = false,
      transition = null,
      forceRebuild = false
    }) {
      const keyInfoStep = nextIncompleteTaskId(tasks, completedTaskIds) === "keyboard_key_info";
      this._keyboardKeyInfoStepActive = keyInfoStep;
      if (keyInfoStep) ensureKeyboardReferenceOpenAndExpanded();
      this._ensure();
      if (!this.root || !this.root.isConnected) return;
      let panelHidden = false;
      try {
        panelHidden = this.root.hidden === true || String(this.root.style?.display || "") === "none";
      } catch {
      }
      try {
        const targetSurface = this.slideSurface || this.body;
        const refs = {
          root: this.root,
          titleEl: this.titleEl,
          stepEl: this.stepEl,
          prevBtn: this.prevBtn,
          nextBtn: this.nextBtn
        };
        const updateDom = () => {
          updateOnboardingChrome(refs, {
            title: title || ONBOARDING_DEFAULT_TITLE,
            slideId,
            slideIndex,
            slideCount
          });
          renderOnboardingSlideSurface(targetSurface, {
            tasks,
            completedTaskIds,
            lastCompletedTaskId,
            bodyText,
            forceRebuild,
            showTip: showTip === true,
            showCloseButton: showCloseButton === true,
            onCloseClick: this._onCloseClick,
            onTaskRowClick: this._onTaskRowClick
          });
        };
        const doSlide = !panelHidden && !!(transition && transition.type === "slide");
        const dir = doSlide && transition?.dir === -1 ? -1 : 1;
        if (!doSlide) {
          updateDom();
          this._lastRenderedSlideId = String(slideId || "");
          this._lastRenderedSlideIndex = Number(slideIndex) || 0;
          return;
        }
        try {
          if (document && typeof document.startViewTransition === "function" && targetSurface) {
            try {
              this.root?.style?.setProperty?.("--kp-onboarding-slide-dir", String(dir));
            } catch {
            }
            try {
              targetSurface.style.viewTransitionName = "kp-onboarding-slide-surface";
            } catch {
            }
            const vt = document.startViewTransition(() => {
              updateDom();
            });
            await Promise.resolve(vt?.finished).catch(() => {
            });
            try {
              targetSurface.style.viewTransitionName = "";
            } catch {
            }
            this._lastRenderedSlideId = String(slideId || "");
            this._lastRenderedSlideIndex = Number(slideIndex) || 0;
            return;
          }
        } catch {
        }
        try {
          targetSurface.style.transform = `translateX(${dir > 0 ? "100%" : "-100%"})`;
          targetSurface.style.opacity = "0.7";
        } catch {
        }
        updateDom();
        try {
          const anim = targetSurface.animate([
            { transform: `translateX(${dir > 0 ? "100%" : "-100%"})`, opacity: 0.7 },
            { transform: "translateX(0%)", opacity: 1 }
          ], { duration: 220, easing: "cubic-bezier(0.2, 0.8, 0.2, 1)", fill: "both" });
          await Promise.resolve(anim?.finished).catch(() => {
          });
        } catch {
        }
        try {
          targetSurface.style.transform = "";
          targetSurface.style.opacity = "";
        } catch {
        }
        this._lastRenderedSlideId = String(slideId || "");
        this._lastRenderedSlideIndex = Number(slideIndex) || 0;
      } catch (e) {
        try {
          const surface = this.slideSurface || this.body;
          while (surface && surface.firstChild) surface.removeChild(surface.firstChild);
          const msg = document.createElement("div");
          msg.textContent = "Unable to render onboarding panel on this page.";
          surface?.appendChild(msg);
        } catch {
        }
        console.warn("[KeyPilot] Failed to render onboarding panel:", e);
      }
    }
    showOverlay({ title, message, primaryText = "OK", secondaryText = "", onPrimary = null, onSecondary = null } = {}) {
      this._ensure();
      if (!this.root || !this._overlayEl) return;
      this._overlayOnPrimary = typeof onPrimary === "function" ? onPrimary : null;
      this._overlayOnSecondary = typeof onSecondary === "function" ? onSecondary : null;
      try {
        renderKeyboardKeysInto(this._overlayTitleEl, String(title || "Nice!"));
      } catch {
      }
      try {
        renderKeyboardKeysInto(this._overlayMsgEl, String(message || ""));
      } catch {
      }
      try {
        this._overlayPrimaryBtn.textContent = String(primaryText || "OK");
        this._overlayPrimaryBtn.hidden = false;
      } catch {
      }
      try {
        const showSecondary = !!(secondaryText && String(secondaryText).trim());
        this._overlaySecondaryBtn.textContent = String(secondaryText || "");
        this._overlaySecondaryBtn.hidden = !showSecondary;
      } catch {
      }
      this._overlayOpen = true;
      setOnboardingOverlayOpen(this._overlayEl, true, this.root);
    }
    hideOverlay() {
      this._overlayOpen = false;
      if (!this._overlayEl) return;
      setOnboardingOverlayOpen(this._overlayEl, false, this.root);
      this._overlayOnPrimary = null;
      this._overlayOnSecondary = null;
    }
    _ensure() {
      if (this.root && this.root.isConnected) {
        this._ensureOverlay(this.root);
        return;
      }
      try {
        const existing = document.querySelector(`.${ONBOARDING_PANEL_CLASS}[data-kp-early-onboarding="true"]`);
        if (existing && existing.isConnected) {
          const refs = queryOnboardingShellRefs(existing);
          if (refs) {
            try {
              existing.style.zIndex = String(Z_INDEX.ONBOARDING_PANEL || ONBOARDING_PANEL_Z_FALLBACK);
            } catch {
            }
            applyPopupThemeVars(existing);
            let { closeBtn, prevBtn, nextBtn, resetBtn } = refs;
            closeBtn = stripListeners(closeBtn);
            prevBtn = stripListeners(prevBtn);
            nextBtn = stripListeners(nextBtn);
            resetBtn = stripListeners(resetBtn);
            if (closeBtn) closeBtn.addEventListener("click", this._onCloseClick);
            if (prevBtn) prevBtn.addEventListener("click", this._onPrevClick);
            if (nextBtn) nextBtn.addEventListener("click", this._onNextClick);
            if (resetBtn) resetBtn.addEventListener("click", this._onResetClick);
            this._bindShell({
              root: existing,
              body: refs.body,
              slideSurface: refs.slideSurface,
              titleEl: refs.titleEl,
              stepEl: refs.stepEl,
              closeBtn,
              prevBtn,
              nextBtn,
              resetBtn
            });
            this._ensureOverlay(existing);
            return;
          }
        }
      } catch {
      }
      const shell = createOnboardingShell(document, {
        zIndex: Z_INDEX.ONBOARDING_PANEL || ONBOARDING_PANEL_Z_FALLBACK,
        early: false,
        initiallyHidden: true,
        includeViewTransitions: true,
        applyTheme: applyPopupThemeVars,
        navDisabled: false
      });
      shell.prevBtn.addEventListener("click", this._onPrevClick);
      shell.nextBtn.addEventListener("click", this._onNextClick);
      shell.resetBtn.addEventListener("click", this._onResetClick);
      shell.closeBtn.addEventListener("click", this._onCloseClick);
      (document.body || document.documentElement).appendChild(shell.root);
      this._bindShell(shell);
      this._ensureOverlay(shell.root);
    }
    _bindShell(shell) {
      this.root = shell.root;
      this.body = shell.body;
      this.slideSurface = shell.slideSurface;
      this.titleEl = shell.titleEl || null;
      this.stepEl = shell.stepEl || null;
      this.closeBtn = shell.closeBtn || null;
      this.prevBtn = shell.prevBtn || null;
      this.nextBtn = shell.nextBtn || null;
      this.resetBtn = shell.resetBtn || null;
    }
    _ensureOverlay(rootEl) {
      try {
        if (this._overlayEl && this._overlayEl.isConnected) return;
        const overlayRefs = ensureOnboardingOverlay(rootEl || this.root);
        if (!overlayRefs) return;
        this._overlayEl = overlayRefs.overlayEl;
        this._overlayTitleEl = overlayRefs.titleEl;
        this._overlayMsgEl = overlayRefs.msgEl;
        this._overlayPrimaryBtn = overlayRefs.primaryBtn;
        this._overlaySecondaryBtn = overlayRefs.secondaryBtn;
        try {
          this._overlayPrimaryBtn?.removeEventListener?.("click", this._onOverlayPrimary);
        } catch {
        }
        try {
          this._overlaySecondaryBtn?.removeEventListener?.("click", this._onOverlaySecondary);
        } catch {
        }
        try {
          this._overlayPrimaryBtn?.addEventListener?.("click", this._onOverlayPrimary);
        } catch {
        }
        try {
          this._overlaySecondaryBtn?.addEventListener?.("click", this._onOverlaySecondary);
        } catch {
        }
        try {
          const isHidden = this._overlayEl.hidden === true || this._overlayEl.hasAttribute("hidden");
          const displayNone = String(this._overlayEl.style?.display || "") === "none";
          const open = !isHidden && !displayNone;
          this._overlayOpen = open;
          setOnboardingOverlayOpen(this._overlayEl, open, this.root);
        } catch {
        }
      } catch {
      }
    }
    _onOverlayPrimary(e) {
      try {
        e.preventDefault();
        e.stopPropagation();
      } catch {
      }
      const cb = this._overlayOnPrimary;
      this.hideOverlay();
      try {
        cb?.();
      } catch {
      }
    }
    _onOverlaySecondary(e) {
      try {
        e.preventDefault();
        e.stopPropagation();
      } catch {
      }
      const cb = this._overlayOnSecondary;
      this.hideOverlay();
      try {
        cb?.();
      } catch {
      }
    }
    _onCloseClick(e) {
      try {
        e.preventDefault();
        e.stopPropagation();
      } catch {
      }
      try {
        if (this._onRequestClose) this._onRequestClose();
      } catch (err) {
        console.warn("[KeyPilot Onboarding] Error in onRequestClose:", err);
      }
    }
    _onPrevClick(e) {
      try {
        e.preventDefault();
        e.stopPropagation();
      } catch {
      }
      try {
        if (this._onRequestPrev) this._onRequestPrev();
      } catch {
      }
    }
    _onNextClick(e) {
      try {
        e.preventDefault();
        e.stopPropagation();
      } catch {
      }
      try {
        if (this._onRequestNext) this._onRequestNext();
      } catch {
      }
    }
    _onResetClick(e) {
      try {
        e.preventDefault();
        e.stopPropagation();
      } catch {
      }
      try {
        const result = this._onRequestReset ? this._onRequestReset() : null;
        if (result && typeof result.then === "function") {
          result.catch((err) => {
            console.warn("[KeyPilot Onboarding] reset failed:", err);
          });
        }
      } catch (err) {
        console.warn("[KeyPilot Onboarding] reset handler error:", err);
      }
    }
    _onTaskRowClick(e) {
      try {
        e.preventDefault();
        e.stopPropagation();
      } catch {
      }
      try {
        if (this.isOverlayOpen()) return;
        const row = e?.currentTarget || e?.target?.closest?.("[data-kp-onboarding-task-id]");
        if (!row) return;
        if (row.getAttribute("data-kp-onboarding-uncheckable") !== "true") return;
        const taskId = row.getAttribute("data-kp-onboarding-task-id");
        if (!taskId || !this._onRequestUncheckTask) return;
        this._onRequestUncheckTask(String(taskId));
      } catch {
      }
    }
    /**
     * Play the F-key click border effect around the walkthrough panel (marquee/flash/etc).
     * Uses the same CSS classes as OverlayManager so it matches user settings visuals.
     * @param {'marquee'|'flash'|'dash'|'scale'} [effect]
     */
    playBorderEffect(effect = "marquee") {
      try {
        this._ensure();
        const host = this.root;
        if (!host || !host.isConnected) return;
        const rect = host.getBoundingClientRect();
        if (!rect || rect.width < 2 || rect.height < 2) return;
        const map = {
          marquee: { className: CSS_CLASSES.FOCUS_MARQUEE || "kpv2-focus-marquee", ms: 1200 },
          flash: { className: CSS_CLASSES.FOCUS_FLASH || "kpv2-focus-flash", ms: 500 },
          scale: { className: CSS_CLASSES.FOCUS_PULSE || "kpv2-focus-pulse", ms: 800 },
          dash: { className: CSS_CLASSES.FOCUS_DASH || "kpv2-focus-dash", ms: 1100 }
        };
        const kind = map[String(effect || "marquee")] || map.marquee;
        const pulse = document.createElement("div");
        pulse.className = kind.className;
        pulse.setAttribute("aria-hidden", "true");
        pulse.setAttribute("data-kp-onboarding-border-effect", String(effect || "marquee"));
        Object.assign(pulse.style, {
          left: `${rect.left}px`,
          top: `${rect.top}px`,
          width: `${rect.width}px`,
          height: `${rect.height}px`,
          borderRadius: "3px",
          zIndex: String((Z_INDEX.ONBOARDING_PANEL || ONBOARDING_PANEL_Z_FALLBACK) + 5),
          pointerEvents: "none"
        });
        (document.body || document.documentElement).appendChild(pulse);
        window.setTimeout(() => {
          try {
            if (pulse.parentNode) pulse.parentNode.removeChild(pulse);
          } catch {
          }
        }, kind.ms + 80);
      } catch {
      }
    }
  };

  // src/ui/practice-popover-panel.js
  init_constants();
  var PRACTICE_PANEL_STYLE_ATTR = "data-kp-practice-popover-style";
  function ensurePracticePanelStyles(root) {
    injectChromeStyles(root, { attr: PRACTICE_PANEL_STYLE_ATTR, css: `
:host {
  position: fixed;
  left: 392px;
  top: 16px;
  width: 420px;
  max-width: calc(100vw - 24px);
  height: auto;
  max-height: calc(100vh - 24px);
  overflow: auto;
  z-index: ${String((Z_INDEX.ONBOARDING_PANEL || 2147483045) - 1)};
  background: ${ONBOARDING_METAL.panelBg};
  color: ${ONBOARDING_METAL.fg};
  border: ${ONBOARDING_METAL.panelBorder};
  border-radius: var(--kp-radius-panel, 3px);
  box-shadow: ${ONBOARDING_METAL.panelShadow};
  font-family: Helvetica, Arial, sans-serif;
  pointer-events: auto;
}
:host([hidden]) { display: none !important; }
.kp-practice-popover__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 12px;
  background: ${ONBOARDING_METAL.titlebarBg};
  border-bottom: ${ONBOARDING_METAL.titlebarBorder};
  box-shadow: ${ONBOARDING_METAL.titlebarShadow};
  letter-spacing: var(--kp-type-tracking-titlebar, 0.02em);
  text-transform: var(--kp-type-transform-titlebar, none);
}
.kp-practice-popover__title {
  font-size: 13px;
  font-weight: var(--kp-titlebar-title-weight, 800);
  letter-spacing: var(--kp-type-tracking-titlebar, 0.02em);
  text-transform: var(--kp-type-transform-titlebar, none);
  color: var(--kp-color-fg, ${ONBOARDING_METAL.fg});
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.kp-practice-popover__close {
  width: 28px;
  height: 28px;
  border-radius: 2px;
  border: ${ONBOARDING_METAL.btnBorder};
  background: ${ONBOARDING_METAL.btnBg};
  box-shadow: ${ONBOARDING_METAL.btnShadow};
  color: ${ONBOARDING_METAL.fg};
  cursor: pointer;
  font-size: 16px;
  font-weight: 700;
  line-height: 26px;
  padding: 0;
  flex: 0 0 auto;
}
.kp-practice-popover__body { padding: 12px; }
` });
  }
  function clearPracticePanelElement(el2) {
    while (el2 && el2.firstChild) el2.removeChild(el2.firstChild);
  }
  function clampPracticePanelValue(n, min, max) {
    return Math.max(min, Math.min(max, n));
  }
  var PracticePopoverPanel = class {
    /**
     * @param {Object} params
     * @param {() => void} [params.onRequestClose]
     */
    constructor({ onRequestClose } = {}) {
      this.root = null;
      this.shadowRoot = null;
      this.body = null;
      this._onRequestClose = typeof onRequestClose === "function" ? onRequestClose : null;
      this._onCloseClick = this._onCloseClick.bind(this);
    }
    isVisible() {
      return !!(this.root && this.root.isConnected && this.root.hidden === false);
    }
    show() {
      if (window !== window.top) return;
      this._ensure();
      this.root.hidden = false;
    }
    hide() {
      if (this.root) this.root.hidden = true;
    }
    /**
     * Position the practice panel directly to the right of the onboarding panel when possible.
     * Falls back to "below" when the viewport is too narrow.
     * @param {HTMLElement|null} onboardingRoot
     */
    positionNextToOnboarding(onboardingRoot) {
      try {
        if (!this.root || !this.root.isConnected) return;
        if (!onboardingRoot || !onboardingRoot.getBoundingClientRect) return;
        const gap = 12;
        const margin = 12;
        const ob = onboardingRoot.getBoundingClientRect();
        const w = window.innerWidth || document.documentElement?.clientWidth || 0;
        const h = window.innerHeight || document.documentElement?.clientHeight || 0;
        const desiredLeft = Math.round(ob.right + gap);
        const desiredTop = Math.round(ob.top);
        const rect = this.root.getBoundingClientRect();
        const panelW = rect.width || 420;
        const panelH = rect.height || 280;
        const canFitRight = desiredLeft + panelW <= w - margin;
        let left = desiredLeft;
        let top = desiredTop;
        if (!canFitRight) {
          left = Math.round(ob.left);
          top = Math.round(ob.bottom + gap);
        }
        left = clampPracticePanelValue(left, margin, Math.max(margin, w - panelW - margin));
        top = clampPracticePanelValue(top, margin, Math.max(margin, h - panelH - margin));
        this.root.style.left = `${left}px`;
        this.root.style.top = `${top}px`;
      } catch {
      }
    }
    render() {
      if (!this.root || this.root.hidden) return;
      if (!this.body) return;
      try {
        const existing = this.body.querySelector('[data-kp-practice-built="true"]');
        if (existing) return;
      } catch {
      }
      clearPracticePanelElement(this.body);
      const wrap = document.createElement("div");
      wrap.dataset.kpPracticeBuilt = "true";
      Object.assign(wrap.style, {
        display: "flex",
        flexDirection: "column",
        gap: "12px"
      });
      const card = (titleText) => {
        const c = document.createElement("div");
        Object.assign(c.style, {
          padding: "12px",
          borderRadius: "3px",
          border: ONBOARDING_METAL.rowBorder,
          background: ONBOARDING_METAL.rowBg,
          boxShadow: "0 1px 0 rgba(255,255,255,0.28) inset"
        });
        if (titleText) {
          const t = document.createElement("div");
          t.textContent = titleText;
          Object.assign(t.style, {
            fontSize: "13px",
            fontWeight: "800",
            marginBottom: "8px",
            color: ONBOARDING_METAL.fg
          });
          c.appendChild(t);
        }
        return c;
      };
      const p = (text) => {
        const el2 = document.createElement("div");
        el2.textContent = text;
        Object.assign(el2.style, {
          fontSize: "13px",
          lineHeight: "1.5",
          color: ONBOARDING_METAL.fgDim
        });
        return el2;
      };
      const kbd = (text) => {
        const el2 = document.createElement("span");
        el2.textContent = text;
        Object.assign(el2.style, {
          fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
          fontSize: "11px",
          fontWeight: "700",
          padding: "2px 7px",
          border: ONBOARDING_METAL.kbdBorder,
          borderRadius: "4px",
          background: ONBOARDING_METAL.kbdBg,
          color: ONBOARDING_METAL.kbdColor,
          boxShadow: ONBOARDING_METAL.kbdShadow
        });
        return el2;
      };
      const intro = card("Text Boxes");
      intro.appendChild(
        p("Click a text box with F to enter text box mode (typing works normally). Press `Esc` to exit text box mode and return to normal browsing.")
      );
      const tip = document.createElement("div");
      Object.assign(tip.style, {
        marginTop: "8px",
        fontSize: "12px",
        color: ONBOARDING_METAL.fgMute,
        lineHeight: "1.45"
      });
      tip.appendChild(document.createTextNode("Tip: while in text mode, you can still hover something and press "));
      tip.appendChild(kbd("F"));
      tip.appendChild(document.createTextNode(" during a short countdown window."));
      intro.appendChild(tip);
      const fields = card("Text Boxes");
      const grid = document.createElement("div");
      Object.assign(grid.style, {
        display: "grid",
        gridTemplateColumns: "1fr 1fr",
        gap: "10px"
      });
      const mkLabel = (forId, text) => {
        const l = document.createElement("label");
        l.setAttribute("for", forId);
        l.textContent = text;
        Object.assign(l.style, {
          display: "block",
          fontWeight: "800",
          fontSize: "12px",
          margin: "0 0 6px 0",
          color: ONBOARDING_METAL.fg
        });
        return l;
      };
      const fieldBorder = "1px solid rgba(0,0,0,0.35)";
      const fieldFocusBorder = "1px solid rgba(255, 140, 0, 0.75)";
      const mkInput = (id, placeholder) => {
        const i = document.createElement("input");
        i.id = id;
        i.type = "text";
        i.placeholder = placeholder;
        Object.assign(i.style, {
          width: "100%",
          boxSizing: "border-box",
          borderRadius: "2px",
          border: fieldBorder,
          background: "rgba(255,255,255,0.55)",
          color: ONBOARDING_METAL.fg,
          boxShadow: "inset 0 1px 0 rgba(255,255,255,0.55)",
          padding: "8px 10px",
          fontSize: "13px",
          fontFamily: "Helvetica, Arial, sans-serif",
          outline: "none"
        });
        i.addEventListener("focus", () => {
          try {
            i.style.border = fieldFocusBorder;
            i.style.boxShadow = "0 0 0 2px rgba(255, 140, 0, 0.22), inset 0 1px 0 rgba(255,255,255,0.55)";
          } catch {
          }
        });
        i.addEventListener("blur", () => {
          try {
            i.style.border = fieldBorder;
            i.style.boxShadow = "inset 0 1px 0 rgba(255,255,255,0.55)";
          } catch {
          }
        });
        return i;
      };
      const mkTextarea = (id, placeholder) => {
        const t = document.createElement("textarea");
        t.id = id;
        t.placeholder = placeholder;
        Object.assign(t.style, {
          width: "100%",
          boxSizing: "border-box",
          borderRadius: "2px",
          border: fieldBorder,
          background: "rgba(255,255,255,0.55)",
          color: ONBOARDING_METAL.fg,
          boxShadow: "inset 0 1px 0 rgba(255,255,255,0.55)",
          padding: "8px 10px",
          fontSize: "13px",
          fontFamily: "Helvetica, Arial, sans-serif",
          outline: "none",
          minHeight: "120px",
          resize: "vertical"
        });
        t.addEventListener("focus", () => {
          try {
            t.style.border = fieldFocusBorder;
            t.style.boxShadow = "0 0 0 2px rgba(255, 140, 0, 0.22), inset 0 1px 0 rgba(255,255,255,0.55)";
          } catch {
          }
        });
        t.addEventListener("blur", () => {
          try {
            t.style.border = fieldBorder;
            t.style.boxShadow = "inset 0 1px 0 rgba(255,255,255,0.55)";
          } catch {
          }
        });
        return t;
      };
      const col1 = document.createElement("div");
      col1.appendChild(mkLabel("kp-practice-input-1", "Practice input"));
      col1.appendChild(mkInput("kp-practice-input-1", "Click here with F, then type\u2026"));
      const col2 = document.createElement("div");
      col2.appendChild(mkLabel("kp-practice-input-2", "Second input (optional)"));
      col2.appendChild(mkInput("kp-practice-input-2", "Try switching between fields\u2026"));
      grid.appendChild(col1);
      grid.appendChild(col2);
      fields.appendChild(grid);
      const taWrap = document.createElement("div");
      Object.assign(taWrap.style, { marginTop: "10px" });
      taWrap.appendChild(mkLabel("kp-practice-textarea", "Practice textarea"));
      taWrap.appendChild(mkTextarea("kp-practice-textarea", "Type a few lines here\u2026"));
      fields.appendChild(taWrap);
      const note = document.createElement("div");
      Object.assign(note.style, {
        marginTop: "10px",
        fontSize: "12px",
        color: ONBOARDING_METAL.fgMute,
        lineHeight: "1.45"
      });
      note.appendChild(document.createTextNode("When you\u2019re done typing, press "));
      note.appendChild(kbd("Esc"));
      note.appendChild(document.createTextNode(" to exit text box mode."));
      fields.appendChild(note);
      wrap.appendChild(fields);
      const clickWin = card("Step 2: Practice the \u201Chover + F click\u201D window");
      const descr = document.createElement("div");
      Object.assign(descr.style, {
        fontSize: "13px",
        lineHeight: "1.5",
        color: ONBOARDING_METAL.fgDim
      });
      descr.appendChild(document.createTextNode("While still in text mode, hover this link or button and press "));
      descr.appendChild(kbd("F"));
      descr.appendChild(document.createTextNode(" during the short countdown window:"));
      clickWin.appendChild(descr);
      const link = document.createElement("a");
      link.href = "#kp-practice-anchor";
      link.textContent = "Practice link (jumps in this panel)";
      link.id = "kp-practice-link";
      Object.assign(link.style, {
        color: "#0b5f8a",
        textDecoration: "underline",
        display: "inline-block",
        marginTop: "10px",
        fontWeight: "700"
      });
      clickWin.appendChild(link);
      const btn = document.createElement("button");
      btn.type = "button";
      btn.textContent = "Practice button (no navigation)";
      Object.assign(btn.style, {
        display: "inline-flex",
        alignItems: "center",
        justifyContent: "center",
        padding: "6px 12px",
        borderRadius: "2px",
        border: ONBOARDING_METAL.btnBorder,
        background: ONBOARDING_METAL.btnBg,
        boxShadow: ONBOARDING_METAL.btnShadow,
        color: ONBOARDING_METAL.fg,
        cursor: "pointer",
        fontWeight: "700",
        fontFamily: "Helvetica, Arial, sans-serif",
        fontSize: "12px",
        marginTop: "10px"
      });
      btn.addEventListener("click", () => {
        try {
          btn.textContent = "Clicked!";
          setTimeout(() => {
            btn.textContent = "Practice button (no navigation)";
          }, 900);
        } catch {
        }
      });
      clickWin.appendChild(btn);
      const anchor = document.createElement("div");
      anchor.id = "kp-practice-anchor";
      anchor.textContent = "Anchor reached. Press Esc to exit text mode if needed.";
      Object.assign(anchor.style, {
        marginTop: "10px",
        fontSize: "12px",
        color: ONBOARDING_METAL.fgMute,
        lineHeight: "1.45"
      });
      clickWin.appendChild(anchor);
      this.body.appendChild(wrap);
    }
    _ensure() {
      if (this.root && this.root.isConnected) return;
      const root = document.createElement("div");
      root.className = "kp-practice-popover";
      root.dataset.kpPracticePopover = "true";
      root.hidden = true;
      root.setAttribute("role", "dialog");
      root.setAttribute("aria-label", "KeyPilot practice popover");
      applyPopupThemeVars(root);
      const shadowRoot = ensureOpenChromeShadow(root, { id: "practice-popover", chromeWindow: true });
      const panelRoot = shadowRoot || root;
      ensurePracticePanelStyles(panelRoot);
      const header = document.createElement("div");
      header.className = "kp-practice-popover__header";
      const title = document.createElement("div");
      title.textContent = "Entering Text";
      title.className = "kp-practice-popover__title";
      const closeBtn = document.createElement("button");
      closeBtn.type = "button";
      closeBtn.textContent = "\xD7";
      closeBtn.setAttribute("aria-label", "Hide practice popover");
      closeBtn.className = "kp-practice-popover__close";
      closeBtn.addEventListener("click", this._onCloseClick);
      header.appendChild(title);
      header.appendChild(closeBtn);
      const body = document.createElement("div");
      body.className = "kp-practice-popover__body";
      panelRoot.appendChild(header);
      panelRoot.appendChild(body);
      (document.body || document.documentElement).appendChild(root);
      this.root = root;
      this.shadowRoot = shadowRoot;
      this.body = body;
    }
    _onCloseClick(e) {
      try {
        e.preventDefault();
        e.stopPropagation();
      } catch {
      }
      try {
        this.hide();
        if (this._onRequestClose) this._onRequestClose();
      } catch {
      }
    }
  };

  // src/modules/onboarding-manager.js
  init_storage();
  var TRANSIENT_KEYS = {
    LAST_ACTION: "keypilot_transient_action"
  };
  function safeBool(v) {
    return typeof v === "boolean" ? v : null;
  }
  async function storageGet(keys) {
    const list = Array.isArray(keys) ? keys.filter((k) => typeof k === "string" && k) : [];
    if (!list.length) return {};
    let sync = {};
    let local = {};
    try {
      if (chrome?.storage?.sync?.get) {
        sync = await chrome.storage.sync.get(list) || {};
      }
    } catch {
      sync = {};
    }
    try {
      if (chrome?.storage?.local?.get) {
        local = await chrome.storage.local.get(list) || {};
      }
    } catch {
      local = {};
    }
    const out = {};
    for (const key of list) {
      const hasSync = Object.prototype.hasOwnProperty.call(sync, key) && sync[key] !== void 0;
      const hasLocal = Object.prototype.hasOwnProperty.call(local, key) && local[key] !== void 0;
      if (key === ONBOARDING_STORAGE_KEYS.PROGRESS && hasSync && hasLocal) {
        const s = sync[key];
        const l = local[key];
        const sTs = s && typeof s === "object" && typeof s.timestamp === "number" ? s.timestamp : 0;
        const lTs = l && typeof l === "object" && typeof l.timestamp === "number" ? l.timestamp : 0;
        out[key] = lTs > sTs ? l : s;
        continue;
      }
      if (hasSync) out[key] = sync[key];
      else if (hasLocal) out[key] = local[key];
    }
    return out;
  }
  async function storageGetTransient() {
    try {
      if (chrome?.storage?.session?.get) {
        const r = await chrome.storage.session.get([TRANSIENT_KEYS.LAST_ACTION]);
        return r || {};
      }
    } catch {
    }
    try {
      const r = await chrome.storage.local.get([TRANSIENT_KEYS.LAST_ACTION]);
      return r || {};
    } catch {
      return {};
    }
  }
  async function storageRemoveTransient() {
    try {
      if (chrome?.storage?.session?.remove) {
        await chrome.storage.session.remove([TRANSIENT_KEYS.LAST_ACTION]);
        return;
      }
    } catch {
    }
    try {
      await chrome.storage.local.remove([TRANSIENT_KEYS.LAST_ACTION]);
    } catch {
    }
  }
  async function storageSet(obj) {
    if (!obj || typeof obj !== "object") return false;
    let ok = false;
    try {
      if (chrome?.storage?.sync?.set) {
        await chrome.storage.sync.set(obj);
        ok = true;
      }
    } catch {
    }
    try {
      if (chrome?.storage?.local?.set) {
        await chrome.storage.local.set(obj);
        ok = true;
      }
    } catch {
    }
    if (!ok) {
      ok = await storageSetObject(obj);
    }
    return ok;
  }
  function withViewTransition(updateDomFn) {
    try {
      updateDomFn();
    } catch {
    }
  }
  function parseOnboardingXml(xmlText) {
    const parser = new DOMParser();
    const doc = parser.parseFromString(String(xmlText || ""), "application/xml");
    const parseError = doc.querySelector("parsererror");
    if (parseError) {
      throw new Error("Failed to parse onboarding.xml");
    }
    const slides = [];
    const slideEls = doc.querySelectorAll("onboarding > slide");
    for (const slideEl of slideEls) {
      const id = slideEl.getAttribute("id") || "";
      const title = slideEl.getAttribute("title") || "";
      if (!id) continue;
      const bodyEl = slideEl.querySelector(":scope > body");
      const bodyText = bodyEl ? String(bodyEl.textContent || "").trim() : "";
      const onEnter = [];
      const onEnterEls = slideEl.querySelectorAll(":scope > onEnter");
      for (const oe of onEnterEls) {
        const type2 = (oe.getAttribute("type") || "").trim();
        if (!type2) continue;
        const entry = { type: type2 };
        for (const attr of oe.attributes || []) {
          if (!attr || !attr.name) continue;
          if (attr.name === "type") continue;
          entry[attr.name] = attr.value;
        }
        onEnter.push(entry);
      }
      const tasks = [];
      const taskEls = slideEl.querySelectorAll(":scope > task");
      for (const taskEl of taskEls) {
        const taskId = taskEl.getAttribute("id") || "";
        const label = taskEl.getAttribute("label") || "";
        if (!taskId) continue;
        const whenEl = taskEl.querySelector(":scope > when");
        const when = whenEl ? {
          type: (whenEl.getAttribute("type") || "").trim(),
          action: (whenEl.getAttribute("action") || "").trim(),
          target: (whenEl.getAttribute("target") || "").trim(),
          mode: (whenEl.getAttribute("mode") || "").trim(),
          change: (whenEl.getAttribute("change") || "").trim()
        } : { type: "" };
        tasks.push({ id: taskId, label, when });
      }
      slides.push({ id, title, tasks, onEnter, bodyText });
    }
    return { slides };
  }
  var OnboardingManager = class {
    /**
     * @param {Object} [params]
     * @param {(type:string, detail:Object) => void} [params.onEvent] Optional callback hook for onboarding lifecycle events.
     */
    constructor({ onEvent } = {}) {
      this.panel = new OnboardingPanel({
        onRequestClose: () => this.setActive(false),
        onRequestPrev: () => this.goPrevSlide(),
        onRequestNext: () => this.goNextSlide(),
        onRequestReset: () => this.resetTutorial(),
        onRequestUncheckTask: (taskId) => this.uncheckLastTask(taskId)
      });
      this.practicePanel = new PracticePopoverPanel({
        onRequestClose: () => {
          this._practiceDismissed = true;
        }
      });
      this.model = { slides: [] };
      this.progress = createEmptyProgress(null);
      this.active = false;
      this._bound = false;
      this._keyPilotReady = false;
      this._unsubscribeState = null;
      this._prevMode = null;
      this._practiceDismissed = false;
      this._practiceLastSlideId = null;
      this._onActionEvent = this._onActionEvent.bind(this);
      this._onDocKeydownCapture = this._onDocKeydownCapture.bind(this);
      this._onPageShow = this._onPageShow.bind(this);
      this._pageShowBound = false;
      this._onEvent = typeof onEvent === "function" ? onEvent : null;
      this._listeners = /* @__PURE__ */ new Map();
      this._lastRenderedSlideId = null;
      this._lastRenderedSlideIndex = null;
      this._lastAction = null;
      this._isTransitioning = false;
      this._onEnterOverlayPendingSlideId = null;
      this._persistDirty = false;
      this._persistChain = null;
      this._persistEpoch = 0;
      this._applyingRemoteProgress = false;
      this._storageListenerBound = false;
      this._storageChangeTimer = null;
      this._onStorageChanged = this._onStorageChanged.bind(this);
      this._onVisibilityChange = this._onVisibilityChange.bind(this);
      this._visibilityBound = false;
      this._enabledCache = null;
      this._enabledCacheTs = 0;
    }
    /**
     * Subscribe to onboarding events.
     * Events are also dispatched globally as `document` CustomEvents: `keypilot:onboarding`.
     *
     * @param {string} type
     * @param {(detail:Object) => void} handler
     * @returns {() => void} unsubscribe
     */
    on(type2, handler) {
      if (!type2 || typeof handler !== "function") return () => {
      };
      const key = String(type2);
      const set = this._listeners.get(key) || /* @__PURE__ */ new Set();
      set.add(handler);
      this._listeners.set(key, set);
      return () => {
        try {
          set.delete(handler);
        } catch {
        }
      };
    }
    _emit(type2, detail = {}) {
      const t = String(type2 || "");
      if (!t) return;
      const payload = { type: t, ...detail && typeof detail === "object" ? detail : {} };
      try {
        this._onEvent?.(t, payload);
      } catch {
      }
      try {
        const set = this._listeners.get(t);
        if (set && set.size) {
          for (const fn of set) {
            try {
              fn(payload);
            } catch {
            }
          }
        }
      } catch {
      }
      try {
        document.dispatchEvent(new CustomEvent("keypilot:onboarding", { detail: payload }));
      } catch {
      }
    }
    /**
     * Clear all walkthrough progress and return to the first slide.
     * Safe to call while other task-completion persists are in flight.
     */
    /**
     * Undo only the most recently completed task (user clicked that checklist row).
     * @param {string} taskId
     */
    uncheckLastTask(taskId) {
      if (!this._isKeyPilotEnabled() || !this.active || this.progress.completed) return;
      const id = String(taskId || "");
      if (!id) return;
      const ids = Array.isArray(this.progress.completedTaskIds) ? this.progress.completedTaskIds.map(String) : [];
      if (!ids.length || ids[ids.length - 1] !== id) return;
      this.progress.completedTaskIds = ids.slice(0, -1);
      this._persist();
      this._render({ reason: "uncheck", forceRebuild: true });
    }
    async resetTutorial() {
      if (!this._isKeyPilotEnabled()) return;
      const firstSlideId = this.model.slides[0]?.id || null;
      this._persistEpoch += 1;
      this.progress = createEmptyProgress(firstSlideId);
      this.active = true;
      this._practiceDismissed = false;
      this._practiceLastSlideId = null;
      this._lastRenderedSlideId = null;
      this._lastRenderedSlideIndex = null;
      this._isTransitioning = false;
      this._lastAction = null;
      try {
        this.panel.hideOverlay();
      } catch {
      }
      try {
        this.practicePanel.hide();
      } catch {
      }
      await this._persist();
      this._isTransitioning = true;
      this._render({
        transition: { type: "slide", dir: -1 },
        reason: "reset",
        forceRebuild: true
      });
      this._emit("tutorialReset", {
        slideId: firstSlideId,
        timestamp: this.progress.timestamp
      });
    }
    async goPrevSlide() {
      if (!this._isKeyPilotEnabled() || this._isTransitioning) return;
      const idx = this._currentSlideIndex();
      if (idx <= 0) return;
      const prev = this.model.slides[idx - 1];
      if (!prev) return;
      this.progress.slideId = prev.id;
      await this._persist();
      this._isTransitioning = true;
      this._render({ transition: { type: "slide", dir: -1 }, reason: "manualPrev" });
    }
    async goNextSlide() {
      if (!this._isKeyPilotEnabled() || this._isTransitioning) return;
      const idx = this._currentSlideIndex();
      const next = this.model.slides[idx + 1];
      if (!next) return;
      this.progress.slideId = next.id;
      await this._persist();
      this._isTransitioning = true;
      this._render({ transition: { type: "slide", dir: 1 }, reason: "manualNext" });
    }
    _readEnabledFromGlobals() {
      try {
        const th = window.__KeyPilotToggleHandler;
        if (th && typeof th.enabled === "boolean") return th.enabled === true;
      } catch {
      }
      try {
        const kp = window.__KeyPilotInstance;
        if (kp && kp.initializationComplete === true && typeof kp.enabled === "boolean") {
          return kp.enabled === true;
        }
      } catch {
      }
      return null;
    }
    async _syncEnabledFromServiceWorker() {
      try {
        const resp = await chrome.runtime.sendMessage({ type: MSG.GET_STATE });
        if (resp && typeof resp.enabled === "boolean") {
          this._enabledCache = resp.enabled === true;
          this._enabledCacheTs = Date.now();
          return this._enabledCache;
        }
      } catch {
      }
      const g = this._readEnabledFromGlobals();
      if (typeof g === "boolean") {
        this._enabledCache = g === true;
        this._enabledCacheTs = Date.now();
        return this._enabledCache;
      }
      this._enabledCache = false;
      this._enabledCacheTs = Date.now();
      return this._enabledCache;
    }
    _isKeyPilotEnabled() {
      const g = this._readEnabledFromGlobals();
      if (typeof g === "boolean") return g === true;
      if (typeof this._enabledCache === "boolean") return this._enabledCache === true;
      return false;
    }
    _setAltSlashListenerEnabled(enabled) {
      const next = !!enabled;
      try {
        if (next) {
          if (this._altSlashListenerInstalled) return;
          document.addEventListener("keydown", this._onDocKeydownCapture, true);
          this._altSlashListenerInstalled = true;
        } else {
          if (!this._altSlashListenerInstalled) return;
          document.removeEventListener("keydown", this._onDocKeydownCapture, true);
          this._altSlashListenerInstalled = false;
        }
      } catch {
      }
    }
    async init() {
      if (window !== window.top) return;
      if (this._bound) return;
      this._bound = true;
      await this._loadModel();
      await this._loadProgress();
      if (!this.progress.slideId) {
        this.progress.slideId = this.model.slides[0]?.id || null;
      }
      await this._applyTransientActionHeuristicIfNeeded();
      await this._applyBackForwardHeuristicIfNeeded();
      if (!this._pageShowBound) {
        this._pageShowBound = true;
        try {
          window.addEventListener("pageshow", this._onPageShow, true);
        } catch {
        }
      }
      this._bindStorageSync();
      if (!this._visibilityBound) {
        this._visibilityBound = true;
        try {
          document.addEventListener("visibilitychange", this._onVisibilityChange, true);
        } catch {
        }
      }
      try {
        document.addEventListener("keypilot:action", this._onActionEvent, true);
      } catch {
      }
      await this._syncEnabledFromServiceWorker();
      this._setAltSlashListenerEnabled(this._isKeyPilotEnabled());
      this._render();
      this._attachToKeyPilotStateSoon();
    }
    _onPageShow(ev) {
      try {
        if (!ev || ev.persisted !== true) return;
        if (!this.active || this.progress.completed) return;
        this._applyTransientActionHeuristicIfNeeded().then(() => {
          withViewTransition(() => this._render());
        }).catch(() => {
        });
      } catch {
      }
    }
    async _applyTransientActionHeuristicIfNeeded() {
      try {
        if (!this.active || this.progress.completed) return;
        if (this._isOverlayBlockingTasks()) return;
        const data = await storageGetTransient();
        const rec = data && data[TRANSIENT_KEYS.LAST_ACTION];
        if (!rec || typeof rec !== "object") return;
        const action = typeof rec.action === "string" ? rec.action : "";
        const ts = typeof rec.timestamp === "number" ? rec.timestamp : 0;
        if (!action) return;
        const now = Date.now();
        if (!ts || now - ts > 15e3) {
          await storageRemoveTransient();
          return;
        }
        const handled = /* @__PURE__ */ new Set(["back", "newTab", "tabLeft", "tabRight"]);
        if (!handled.has(action)) {
          await storageRemoveTransient();
          return;
        }
        const slide = this._getCurrentSlide();
        if (!slide) return;
        const completed = new Set(this.progress.completedTaskIds);
        const changed = this._tryCompleteNextMatchingTask(slide, completed, {
          type: "action",
          action,
          detail: {}
        });
        await storageRemoveTransient();
        if (!changed) return;
        this.progress.completedTaskIds = Array.from(completed);
        await this._persist();
        const slideComplete = this._isSlideComplete(slide, completed);
        if (slideComplete) {
          this._handleSlideCompleted(slide, { cause: `transient:${action}`, completedTaskIds: Array.from(completed) });
          await this._advanceSlide({ cause: `transient:${action}` });
        }
      } catch {
      }
    }
    _bindStorageSync() {
      if (this._storageListenerBound) return;
      this._storageListenerBound = true;
      try {
        chrome.storage.onChanged.addListener(this._onStorageChanged);
      } catch {
      }
    }
    _onStorageChanged(changes, areaName) {
      try {
        if (!changes || typeof changes !== "object") return;
        if (!areaName || areaName !== "sync" && areaName !== "local" && areaName !== "session") return;
        if (this._applyingRemoteProgress) return;
        if (this._persistDirty || this._persistChain) return;
        const relevant = Object.prototype.hasOwnProperty.call(changes, ONBOARDING_STORAGE_KEYS.ACTIVE) || Object.prototype.hasOwnProperty.call(changes, ONBOARDING_STORAGE_KEYS.PROGRESS);
        if (!relevant) return;
        try {
          if (this._storageChangeTimer) clearTimeout(this._storageChangeTimer);
        } catch {
        }
        const epochAtSchedule = this._persistEpoch;
        this._storageChangeTimer = setTimeout(() => {
          if (epochAtSchedule !== this._persistEpoch) return;
          if (this._persistDirty || this._persistChain) return;
          this._loadProgress({ preferRemote: true }).then((changed) => {
            if (epochAtSchedule !== this._persistEpoch) return;
            if (changed === false) return;
            withViewTransition(() => this._render({ reason: "storageChanged" }));
          }).catch(() => {
          });
        }, 50);
      } catch {
      }
    }
    _onVisibilityChange() {
      try {
        if (document.visibilityState !== "visible") return;
        if (!this.active || this.progress.completed) return;
        this._applyTransientActionHeuristicIfNeeded().then(() => {
          withViewTransition(() => this._render({ reason: "visibility" }));
        }).catch(() => {
        });
      } catch {
      }
    }
    async _applyBackForwardHeuristicIfNeeded() {
      try {
        if (!this.active || this.progress.completed) return;
        if (this._isOverlayBlockingTasks()) return;
        const navEntries = typeof performance !== "undefined" && performance.getEntriesByType ? performance.getEntriesByType("navigation") : [];
        const nav = navEntries && navEntries[0];
        const navType = nav && typeof nav.type === "string" ? nav.type : "";
        if (navType !== "back_forward") return;
        const slide = this._getCurrentSlide();
        if (!slide) return;
        const completed = new Set(this.progress.completedTaskIds);
        const changed = this._tryCompleteNextMatchingTask(slide, completed, {
          type: "action",
          action: "back",
          detail: {}
        });
        if (!changed) return;
        this.progress.completedTaskIds = Array.from(completed);
        await this._persist();
        const slideComplete = this._isSlideComplete(slide, completed);
        if (slideComplete) {
          this._handleSlideCompleted(slide, { cause: "back_forward", completedTaskIds: Array.from(completed) });
          await this._advanceSlide({ cause: "back_forward" });
        }
      } catch {
      }
    }
    async _loadModel() {
      try {
        const url = chrome.runtime.getURL("pages/onboarding.xml");
        const res = await fetch(url);
        const text = await res.text();
        this.model = parseOnboardingXml(text);
        if (this.model?.slides?.length) return;
      } catch {
      }
      try {
        const early = window.KEYPILOT_EARLY?.getOnboardingModel?.();
        if (early && Array.isArray(early.slides) && early.slides.length) {
          this.model = early;
        }
      } catch {
      }
    }
    /**
     * Load active flag + progress from storage.
     * @param {{preferRemote?: boolean}} [opts]
     * @returns {Promise<boolean>} true when in-memory state was updated
     */
    async _loadProgress(opts = {}) {
      const preferRemote = opts.preferRemote === true;
      const data = await storageGet([ONBOARDING_STORAGE_KEYS.ACTIVE, ONBOARDING_STORAGE_KEYS.PROGRESS]);
      const active2 = safeBool(data[ONBOARDING_STORAGE_KEYS.ACTIVE]);
      let changed = false;
      if (active2 !== null && active2 !== this.active) {
        this.active = active2;
        changed = true;
      } else if (active2 === null && !preferRemote) {
        if (this.active !== false) {
          this.active = false;
          changed = true;
        }
      }
      const p = data[ONBOARDING_STORAGE_KEYS.PROGRESS];
      if (p && typeof p === "object") {
        const remote = cloneProgress(p);
        const localTs = typeof this.progress?.timestamp === "number" ? this.progress.timestamp : 0;
        const remoteTs = remote.timestamp || 0;
        if (preferRemote && remoteTs && localTs && remoteTs < localTs) {
          return changed;
        }
        if (!progressEqual(this.progress, remote)) {
          this.progress = remote;
          changed = true;
        }
      }
      return changed;
    }
    /**
     * Persist active + progress. Coalesces concurrent callers so the last
     * in-memory state always wins (critical for Reset vs task-complete races).
     * @returns {Promise<void>}
     */
    async _persist() {
      this._persistDirty = true;
      if (!this._persistChain) {
        this._persistChain = this._drainPersistQueue().finally(() => {
          this._persistChain = null;
        });
      }
      await this._persistChain;
    }
    async _drainPersistQueue() {
      while (this._persistDirty) {
        this._persistDirty = false;
        const payload = {
          [ONBOARDING_STORAGE_KEYS.ACTIVE]: this.active,
          [ONBOARDING_STORAGE_KEYS.PROGRESS]: cloneProgress({
            ...this.progress,
            timestamp: Date.now()
          })
        };
        this.progress.timestamp = payload[ONBOARDING_STORAGE_KEYS.PROGRESS].timestamp;
        this._applyingRemoteProgress = true;
        try {
          await storageSet(payload);
        } finally {
          try {
            setTimeout(() => {
              this._applyingRemoteProgress = false;
            }, 0);
          } catch {
            this._applyingRemoteProgress = false;
          }
        }
      }
    }
    async setActive(active2) {
      const next = !!active2;
      if (next && !this._isKeyPilotEnabled()) {
        this.panel.hide();
        this.practicePanel.hide();
        this._isTransitioning = false;
        return;
      }
      if (this.active === next) {
        if (next) this._render();
        else {
          this.hideReEnableTip();
          this.hideToggleOffArrow();
          this.panel.hide();
          this.practicePanel.hide();
          await this._persist();
        }
        return;
      }
      this.active = next;
      this._isTransitioning = false;
      if (!next) {
        this.hideReEnableTip();
        this.hideToggleOffArrow();
      }
      await this._persist();
      this._render();
    }
    _currentSlideIndex() {
      const idx = this.model.slides.findIndex((s) => s.id === this.progress.slideId);
      return idx >= 0 ? idx : 0;
    }
    _getCurrentSlide() {
      const idx = this._currentSlideIndex();
      return this.model.slides[idx] || null;
    }
    /**
     * @param {Object} [opts]
     * @param {{type:'slide', dir:1|-1}|null} [opts.transition]
     * @param {string} [opts.reason]
     * @param {boolean} [opts.forceRebuild] Force checklist DOM rebuild (e.g. after Reset)
     */
    _render(opts = {}) {
      const slide = this._getCurrentSlide();
      const total = this.model.slides.length;
      const index = this._currentSlideIndex();
      if (!this._isKeyPilotEnabled()) {
        this.hideToggleOffArrow();
        this.panel.hide();
        this.practicePanel.hide();
        return;
      }
      if (!this.active || this.progress.completed || !slide) {
        this.hideToggleOffArrow();
        this.panel.hide();
        this.practicePanel.hide();
        return;
      }
      try {
        this._autoCompleteToggleOffIfAlreadyDisabled();
      } catch {
      }
      const isSlideChange = this._lastRenderedSlideId !== null && String(this._lastRenderedSlideId) !== String(slide.id);
      const wantTransition = opts && opts.transition ? opts.transition : isSlideChange ? { type: "slide", dir: index > (this._lastRenderedSlideIndex || 0) ? 1 : -1 } : null;
      const transition = this.panel.isVisible() ? wantTransition : null;
      const reason = opts && opts.reason ? String(opts.reason) : "render";
      const forceRebuild = !!(opts && opts.forceRebuild) || reason === "reset";
      const fromSlideId = this._lastRenderedSlideId;
      if (reason === "reset" || transition && transition.type === "slide" && fromSlideId !== null && String(fromSlideId) !== String(slide.id)) {
        try {
          this.panel.hideOverlay();
        } catch {
        }
      }
      if (!this.progress.onEnterDoneSlideIds.includes(slide.id)) {
        this._runOnEnter(slide);
      }
      this._syncKeyboardReferenceForKeyInfoStep();
      if (transition && transition.type === "slide") {
        this._emit("slideTransitionStart", {
          fromSlideId,
          toSlideId: slide.id,
          dir: transition.dir,
          reason
        });
      }
      const tasksForUi = (slide.tasks || []).map((t) => ({
        ...t,
        label: this._resolveTaskLabel(t)
      }));
      const completedList = Array.isArray(this.progress.completedTaskIds) ? this.progress.completedTaskIds.map(String) : [];
      const lastCompletedTaskId = completedList.length ? completedList[completedList.length - 1] : null;
      const renderPromise = this.panel.render({
        title: slide.title || "Welcome to KeyPilot",
        bodyText: slide.bodyText || "",
        slideId: slide.id,
        slideIndex: index,
        slideCount: total,
        tasks: tasksForUi,
        completedTaskIds: new Set(completedList),
        lastCompletedTaskId,
        // Reopen tip removed from walkthrough slides (kept available via showTip API).
        showTip: false,
        showCloseButton: slide.id === "completion",
        transition,
        forceRebuild
      });
      this.panel.show();
      if (transition && transition.type === "slide") {
        Promise.resolve(renderPromise).then(() => {
          this._isTransitioning = false;
          this._emit("slideTransitionEnd", {
            fromSlideId,
            toSlideId: slide.id,
            dir: transition.dir,
            reason
          });
        }).catch(() => {
          this._isTransitioning = false;
        });
      } else {
        this._isTransitioning = false;
      }
      this._lastRenderedSlideId = slide.id;
      this._lastRenderedSlideIndex = index;
      if (this._practiceLastSlideId !== slide.id) {
        this._practiceLastSlideId = slide.id;
        this._practiceDismissed = false;
      }
      const shouldShowPractice = slide.id === "text_box_mode" && !this._practiceDismissed;
      if (shouldShowPractice) {
        const wasVisible = this.practicePanel.isVisible();
        this.practicePanel.show();
        this.practicePanel.render();
        this.practicePanel.positionNextToOnboarding(this.panel.root);
        if (!wasVisible) {
          try {
            const kp = window.__KeyPilotInstance;
            const st = kp?.state?.getState?.();
            if (st?.mode === MODES.TEXT_FOCUS && typeof kp.handleEscapeFromTextFocus === "function") {
              kp.handleEscapeFromTextFocus(st);
            }
          } catch {
          }
        }
      } else {
        this.practicePanel.hide();
      }
      this._syncToggleOffArrow();
      this._syncKeyboardReferenceForKeyInfoStep();
    }
    /**
     * Persist that onEnter for this slide has finished (so it won't re-fire).
     * For overlay onEnter, call only after the user accepts the modal (OK / primary) —
     * not merely when the overlay is shown, and not when they Close/dismiss without accepting.
     * @param {string|null|undefined} slideId
     */
    _markOnEnterDone(slideId) {
      const id = String(slideId || "");
      if (!id) return;
      if (this.progress.onEnterDoneSlideIds.includes(id)) return;
      this.progress.onEnterDoneSlideIds.push(id);
      this._persist();
    }
    _runOnEnter(slide) {
      const entries = Array.isArray(slide?.onEnter) ? slide.onEnter : [];
      const slideId = slide?.id || null;
      let hasOverlay = false;
      for (const entry of entries) {
        if (!entry || !entry.type) continue;
        if (entry.type === "overlay") {
          hasOverlay = true;
          const title = String(entry.title || "Nice!").trim();
          const message = String(entry.message || entry.text || "").trim();
          const primaryText = String(entry.primaryText || entry.primary || "OK").trim();
          const secondaryText = String(entry.secondaryText || entry.secondary || "").trim();
          const secondaryAction = String(entry.secondaryAction || "").trim().toLowerCase();
          const laterTitle = String(entry.laterTitle || "").trim();
          const laterMessage = String(entry.laterMessage || entry.laterText || "").trim();
          const laterPrimaryText = String(entry.laterPrimaryText || entry.laterPrimary || "OK, Close").trim();
          const effect = String(entry.effect || "").trim().toLowerCase();
          const shouldPlayEffect = effect === "marquee" || effect === "flash" || effect === "dash" || effect === "scale";
          const closeWalkthrough = () => {
            try {
              this.setActive(false);
            } catch {
            }
          };
          const acceptOverlay = () => {
            this._markOnEnterDone(slideId);
          };
          const showLaterReminder = () => {
            const reminderTitle = laterTitle || "Return to this Tutorial with `Alt`+`I`";
            try {
              this.panel.showOverlay({
                title: reminderTitle,
                message: laterMessage,
                primaryText: laterPrimaryText || "OK, Close",
                secondaryText: "",
                onPrimary: closeWalkthrough
              });
            } catch {
              closeWalkthrough();
            }
          };
          let onSecondary = null;
          if (secondaryText) {
            if (secondaryAction === "later" || secondaryAction === "defer" || secondaryAction === "remind") {
              onSecondary = () => {
                acceptOverlay();
                showLaterReminder();
              };
            } else if (secondaryAction === "close" || secondaryAction === "dismiss") {
              onSecondary = closeWalkthrough;
            }
          }
          const scheduleBorderEffect = () => {
            if (!shouldPlayEffect) return;
            const play = () => {
              try {
                if (!this.panel.isOverlayOpen?.()) return;
                this.panel.playBorderEffect?.(effect);
              } catch {
              }
            };
            try {
              requestAnimationFrame(() => {
                requestAnimationFrame(() => {
                  try {
                    setTimeout(play, 160);
                  } catch {
                    play();
                  }
                });
              });
            } catch {
              try {
                setTimeout(play, 200);
              } catch {
                play();
              }
            }
          };
          if (this._onEnterOverlayPendingSlideId === slideId) continue;
          this._onEnterOverlayPendingSlideId = slideId;
          try {
            if (this.panel.isOverlayOpen?.()) {
              this._onEnterOverlayPendingSlideId = null;
              try {
                this.panel.showOverlay({
                  title,
                  message,
                  primaryText,
                  secondaryText,
                  onPrimary: acceptOverlay,
                  onSecondary
                });
              } catch {
              }
              scheduleBorderEffect();
              continue;
            }
          } catch {
          }
          const tryShow = () => {
            try {
              this.panel.showOverlay({
                title,
                message,
                primaryText,
                secondaryText,
                onPrimary: acceptOverlay,
                onSecondary
              });
              this._emit("overlayShown", { slideId, title, message });
            } catch {
            }
            try {
              return !!this.panel.isOverlayOpen?.();
            } catch {
              return false;
            }
          };
          const finish = (shown) => {
            if (this._onEnterOverlayPendingSlideId === slideId) {
              this._onEnterOverlayPendingSlideId = null;
            }
            if (shown) {
              scheduleBorderEffect();
            }
          };
          if (tryShow()) {
            finish(true);
            continue;
          }
          try {
            requestAnimationFrame(() => {
              if (slideId && this.progress.onEnterDoneSlideIds.includes(String(slideId))) {
                this._onEnterOverlayPendingSlideId = null;
                return;
              }
              if (this.panel.isOverlayOpen?.()) {
                finish(true);
                return;
              }
              if (tryShow()) {
                finish(true);
                return;
              }
              requestAnimationFrame(() => {
                if (slideId && this.progress.onEnterDoneSlideIds.includes(String(slideId))) {
                  this._onEnterOverlayPendingSlideId = null;
                  return;
                }
                if (this.panel.isOverlayOpen?.()) {
                  finish(true);
                  return;
                }
                finish(tryShow());
              });
            });
          } catch {
            finish(tryShow());
          }
          continue;
        }
        if (entry.type === "openTab") {
          const relUrl = String(entry.url || "").trim();
          if (!relUrl) continue;
          const url = chrome.runtime.getURL(relUrl);
          try {
            chrome.runtime.sendMessage({ type: MSG.OPEN_URL_FOREGROUND, url }).catch(() => {
            });
            continue;
          } catch {
          }
          try {
            window.open(url, "_blank", "noopener,noreferrer");
          } catch {
          }
          continue;
        }
        if (entry.type === "openPopover") {
          const relUrl = String(entry.url || "").trim();
          if (!relUrl) continue;
          const title = String(entry.title || "KeyPilot Tutorial");
          try {
            const kp = window.__KeyPilotInstance;
            if (!kp || !kp.overlayManager || typeof kp.overlayManager.showPopover !== "function") continue;
            const url = chrome.runtime.getURL(relUrl);
            kp.overlayManager.showPopover(url, { title });
            try {
              kp.state?.setPopoverOpen?.(true, url);
            } catch {
            }
          } catch {
          }
        }
      }
      if (!hasOverlay) {
        this._markOnEnterDone(slideId);
      }
    }
    _onDocKeydownCapture(e) {
      try {
        if (!this._isKeyPilotEnabled()) return;
        try {
          const kp = window.__KeyPilotInstance;
          const st = kp?.state?.getState?.();
          if (st?.mode === MODES.TEXT_FOCUS) return;
        } catch {
        }
        const isAltOrAltGraph = !!e && (e.altKey === true || typeof e.getModifierState === "function" && e.getModifierState("AltGraph") === true);
        const isIKey = !!e && (e.code === "KeyI" || e.key === "i" || e.key === "I");
        const isAltI = isAltOrAltGraph && isIKey;
        if (!isAltI) return;
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        const shouldOpen = !(this.active === true && this.panel?.isVisible?.());
        this.setActive(shouldOpen);
      } catch {
      }
    }
    /**
     * True while the panel's modal overlay is up (welcome / slide intro).
     * Checklist tasks must not be checked off until the user accepts it.
     * @returns {boolean}
     */
    _isOverlayBlockingTasks() {
      try {
        return !!this.panel?.isOverlayOpen?.();
      } catch {
        return false;
      }
    }
    _onActionEvent(ev) {
      if (!this.active || this.progress.completed) return;
      const detail = ev?.detail || {};
      const action = typeof detail.action === "string" ? detail.action : "";
      if (!action) return;
      this._lastAction = action;
      if (action === "toggleExtension" && typeof detail.enabled === "boolean") {
        this._enabledCache = detail.enabled === true;
        this._enabledCacheTs = Date.now();
        this._setAltSlashListenerEnabled(detail.enabled);
      }
      const slide = this._getCurrentSlide();
      if (!slide) {
        if (action === "toggleExtension" && detail.enabled === false) {
          this.hideToggleOffArrow();
          this.panel.hide();
          this.practicePanel.hide();
        }
        if (action === "toggleExtension" && detail.enabled === true) {
          this.hideReEnableTip();
          this._render({ reason: "toggleOn" });
        }
        return;
      }
      if (this._isOverlayBlockingTasks()) {
        if (action === "toggleExtension" && detail.enabled === false) {
          this.hideToggleOffArrow();
          this.panel.hide();
          this.practicePanel.hide();
        }
        if (action === "toggleExtension" && detail.enabled === true) {
          this.hideReEnableTip();
          this._render({ reason: "toggleOn" });
        }
        return;
      }
      const completedBefore = new Set(this.progress.completedTaskIds);
      const nextBefore = this._nextIncompleteTask(slide, completedBefore);
      const offTaskWasOpen = this._isToggleExtensionOffTask(nextBefore);
      let changed = false;
      const completed = new Set(this.progress.completedTaskIds);
      if (action === "toggleExtension" && detail.enabled === true) {
        const next = this._nextIncompleteTask(slide, completed);
        if (this._isToggleExtensionOffTask(next)) {
          completed.add(next.id);
          changed = true;
        }
      }
      if (this._tryCompleteNextMatchingTask(slide, completed, { type: "action", action, detail })) {
        changed = true;
      }
      if (changed) {
        this.progress.completedTaskIds = Array.from(completed);
        this._persist();
      }
      if (action === "toggleExtension" && detail.enabled === false) {
        this.hideToggleOffArrow();
        this.panel.hide();
        this.practicePanel.hide();
        if (offTaskWasOpen) {
          this.showReEnableTip();
        }
        return;
      }
      if (action === "toggleExtension" && detail.enabled === true) {
        this.hideReEnableTip();
        this._render({ reason: "toggleOn" });
        const slideComplete = this._isSlideComplete(slide, new Set(this.progress.completedTaskIds));
        if (slideComplete) {
          this._handleSlideCompleted(slide, {
            cause: action,
            completedTaskIds: Array.from(this.progress.completedTaskIds)
          });
          this._advanceSlide({ cause: action });
        }
        return;
      }
      if (changed) {
        this._render({ reason: "taskUpdate" });
        const slideComplete = this._isSlideComplete(slide, completed);
        if (slideComplete) {
          this._handleSlideCompleted(slide, { cause: action, completedTaskIds: Array.from(completed) });
          this._advanceSlide({ cause: action });
        }
      }
    }
    showReEnableTip() {
      try {
        this.hideToggleOffArrow();
        const strip = document.querySelector('.kp-control-strip, [data-kp-control-strip="true"]');
        const anchor = strip?.shadowRoot?.querySelector?.('[data-kp-control-strip-status="true"]') || strip;
        this.panel?.showReEnableTip?.({
          anchorEl: anchor,
          message: "Click it again to turn KeyPilot back on."
        });
      } catch {
      }
    }
    hideReEnableTip() {
      try {
        this.panel?.hideReEnableTip?.();
      } catch {
      }
    }
    showToggleOffArrow() {
      try {
        const strip = document.querySelector('.kp-control-strip, [data-kp-control-strip="true"]');
        const anchor = strip?.shadowRoot?.querySelector?.('[data-kp-control-strip-status="true"]') || strip;
        this.panel?.showToggleOffArrow?.({ anchorEl: anchor });
      } catch {
      }
    }
    hideToggleOffArrow() {
      try {
        this.panel?.hideToggleOffArrow?.();
      } catch {
      }
    }
    /**
     * Keyboard Reference hover step: keep the window open and expanded, including
     * after the walkthrough is closed and reopened on this task.
     */
    _syncKeyboardReferenceForKeyInfoStep() {
      try {
        if (!this.active || this.progress.completed || !this._isKeyPilotEnabled()) return;
        const slide = this._getCurrentSlide();
        if (!slide) return;
        const completed = new Set(
          Array.isArray(this.progress.completedTaskIds) ? this.progress.completedTaskIds.map(String) : []
        );
        const nextTask = this._nextIncompleteTask(slide, completed);
        if (nextTask && String(nextTask.id) === "keyboard_key_info") {
          this.panel?.ensureKeyboardReferenceOpenAndExpanded?.();
        }
      } catch {
      }
    }
    /**
     * Arrow only while the next incomplete task is "click strip to turn off".
     * Hidden on click (task complete / KP off), step change, or panel hide.
     */
    _syncToggleOffArrow() {
      try {
        if (!this.active || this.progress.completed || !this._isKeyPilotEnabled()) {
          this.hideToggleOffArrow();
          return;
        }
        const slide = this._getCurrentSlide();
        if (!slide) {
          this.hideToggleOffArrow();
          return;
        }
        const completed = new Set(
          Array.isArray(this.progress.completedTaskIds) ? this.progress.completedTaskIds.map(String) : []
        );
        const nextTask = this._nextIncompleteTask(slide, completed);
        if (nextTask && String(nextTask.id) === "toggle_extension_off") {
          this.showToggleOffArrow();
        } else {
          this.hideToggleOffArrow();
        }
      } catch {
        try {
          this.hideToggleOffArrow();
        } catch {
        }
      }
    }
    _isSlideComplete(slide, completedTaskIdsSet) {
      return isSlideComplete(slide, completedTaskIdsSet);
    }
    /**
     * First incomplete task in slide order (the only step detection may check off).
     * @param {{tasks?: Array<{id?: string}>}|null|undefined} slide
     * @param {Set<string>|string[]|null|undefined} completedSet
     * @returns {{id: string, when?: Object, label?: string}|null}
     */
    _nextIncompleteTask(slide, completedSet) {
      const set = completedSet instanceof Set ? completedSet : new Set(Array.isArray(completedSet) ? completedSet.map(String) : []);
      for (const task of slide?.tasks || []) {
        if (!task?.id) continue;
        if (!set.has(String(task.id))) return task;
      }
      return null;
    }
    /**
     * @param {{when?: Object}|null|undefined} task
     * @returns {boolean}
     */
    _isToggleExtensionOffTask(task) {
      if (!task?.id) return false;
      const when = task.when || {};
      return String(when.type || "") === "action" && String(when.action || "") === "toggleExtension" && String(when.change || "") === "off";
    }
    /**
     * Complete at most the next incomplete task when it matches ctx.
     * Later tasks are ignored even if the same action would match them.
     * @param {{tasks?: Array}|null|undefined} slide
     * @param {Set<string>} completed
     * @param {Object} ctx
     * @returns {boolean}
     */
    _tryCompleteNextMatchingTask(slide, completed, ctx) {
      const next = this._nextIncompleteTask(slide, completed);
      if (!next?.id) return false;
      if (!this._taskMatches(next, ctx)) return false;
      completed.add(String(next.id));
      return true;
    }
    _handleSlideCompleted(slide, { cause = "", completedTaskIds = null } = {}) {
      try {
        const idx = this._currentSlideIndex();
        const next = this.model.slides[idx + 1] || null;
        const isLast = !next;
        this._emit("slideCompleted", {
          slideId: slide?.id || null,
          slideIndex: idx,
          slideCount: this.model.slides.length,
          nextSlideId: next?.id || null,
          cause: String(cause || ""),
          completedTaskIds: Array.isArray(completedTaskIds) ? completedTaskIds : Array.from(new Set(this.progress.completedTaskIds))
        });
        if (isLast) {
          this._emit("lastSlideCompleted", {
            slideId: slide?.id || null,
            slideIndex: idx,
            slideCount: this.model.slides.length,
            cause: String(cause || "")
          });
        }
      } catch {
      }
    }
    async _advanceSlide({ cause = "" } = {}) {
      if (this._isTransitioning) return;
      const idx = this._currentSlideIndex();
      const next = this.model.slides[idx + 1];
      if (!next) {
        this.progress.completed = true;
        this.active = false;
        await this._persist();
        this._emit("onboardingCompleted", {
          slideId: this.progress.slideId,
          slideIndex: idx,
          slideCount: this.model.slides.length,
          cause: String(cause || "")
        });
        this._render({ reason: "completed" });
        return;
      }
      this.progress.slideId = next.id;
      await this._persist();
      this._isTransitioning = true;
      this._render({ transition: { type: "slide", dir: 1 }, reason: "autoAdvance" });
    }
    _taskMatches(task, ctx) {
      const when = task?.when || {};
      const type2 = String(when.type || "").trim();
      if (!type2) return false;
      if (type2 === "action" && ctx.type === "action") {
        if (when.action && when.action !== ctx.action) return false;
        const change = String(when.change || "").trim();
        if (change === "off") {
          if (ctx.detail?.enabled !== false) return false;
        } else if (change === "on") {
          if (ctx.detail?.enabled !== true) return false;
        }
        const target = String(when.target || "").trim();
        if (!target) return true;
        if (target === "link") {
          return !!(ctx.detail?.isLink || ctx.detail?.hadFocusOutline);
        }
        if (target === "keyboardHelpKey") return !!ctx.detail?.isKeyboardHelpKey;
        return false;
      }
      if (type2 === "mode" && ctx.type === "mode") {
        const mode = String(when.mode || "").trim();
        const change = String(when.change || "").trim();
        if (!mode || !change) return false;
        if (change === "enter") return ctx.nextMode === mode && ctx.prevMode !== mode;
        if (change === "exit") return ctx.prevMode === mode && ctx.nextMode !== mode;
        return false;
      }
      return false;
    }
    /**
     * Labels that depend on whether KeyPilot is currently ON or OFF.
     * @param {{id?: string, label?: string}} task
     * @returns {string}
     */
    _resolveTaskLabel(task) {
      const base = String(task?.label || task?.id || "");
      const id = String(task?.id || "");
      const enabled = this._isKeyPilotEnabled();
      if (id === "toggle_extension_off") {
        if (!enabled) {
          return "KeyPilot is already off (control strip shows `OFF`). Click the strip to turn it back on \u2014 a tip points at the control if you need it.";
        }
        return base || "There is a control strip above that says `ON`. Click it to turn KeyPilot completely off.";
      }
      return base;
    }
    /**
     * If the "turn off" step is still open and KeyPilot is already disabled, complete it.
     * @returns {boolean} whether progress changed
     */
    _autoCompleteToggleOffIfAlreadyDisabled() {
      if (!this.active || this.progress.completed) return false;
      if (this._isKeyPilotEnabled()) return false;
      if (this._isOverlayBlockingTasks()) return false;
      const slide = this._getCurrentSlide();
      if (!slide) return false;
      const completed = new Set(this.progress.completedTaskIds);
      const next = this._nextIncompleteTask(slide, completed);
      if (!this._isToggleExtensionOffTask(next)) return false;
      completed.add(String(next.id));
      this.progress.completedTaskIds = Array.from(completed);
      this._persist();
      return true;
    }
    _attachToKeyPilotStateSoon() {
      if (this._keyPilotReady) return;
      const tryAttach = () => {
        try {
          const kp = window.__KeyPilotInstance;
          if (!kp || !kp.state || typeof kp.state.subscribe !== "function") return false;
          this._keyPilotReady = true;
          try {
            const s = kp.state.getState?.();
            this._prevMode = s?.mode || null;
          } catch {
            this._prevMode = null;
          }
          this._unsubscribeState = kp.state.subscribe((nextState, prevState) => {
            if (!this.active || this.progress.completed) return;
            const slide = this._getCurrentSlide();
            if (!slide) return;
            const prevMode = prevState?.mode || this._prevMode || null;
            const nextMode = nextState?.mode || null;
            this._prevMode = nextMode;
            if (this._isOverlayBlockingTasks()) return;
            let changed = false;
            const completed = new Set(this.progress.completedTaskIds);
            if (this._tryCompleteNextMatchingTask(slide, completed, {
              type: "mode",
              prevMode,
              nextMode
            })) {
              changed = true;
            }
            if (!changed) return;
            this.progress.completedTaskIds = Array.from(completed);
            this._persist();
            this._render({ reason: "modeTaskUpdate" });
            const slideComplete = this._isSlideComplete(slide, completed);
            if (slideComplete) {
              this._handleSlideCompleted(slide, { cause: "modeChange", completedTaskIds: Array.from(completed) });
              this._advanceSlide({ cause: "modeChange" });
            }
          });
          return true;
        } catch {
          return false;
        }
      };
      let attempts = 0;
      const timer = setInterval(() => {
        attempts += 1;
        if (tryAttach() || attempts > 80) {
          try {
            clearInterval(timer);
          } catch {
          }
        }
      }, 100);
    }
  };

  // src/content-script.js
  void startKeyPilotDebugFromSettings();
  installContentRuntimeRouter();
  async function initializeKeyPilot() {
    try {
      await startKeyPilotDebugFromSettings();
      if (window.__KeyPilotToggleHandler || window.keyPilot) {
        return;
      }
      const keyPilot = new KeyPilot();
      window.keyPilot = keyPilot;
      const toggleHandler = new KeyPilotToggleHandler(keyPilot);
      await toggleHandler.initialize();
      window.__KeyPilotToggleHandler = toggleHandler;
    } catch (error) {
      console.error("[KeyPilot] Failed to initialize with toggle functionality:", error);
      try {
        if (!window.keyPilot) {
          const keyPilot = new KeyPilot();
          window.keyPilot = keyPilot;
          console.warn("[KeyPilot] Initialized without toggle functionality as fallback");
        }
      } catch (fallbackError) {
        console.error("[KeyPilot] Complete initialization failure:", fallbackError);
      }
    }
  }
  var isTop = (() => {
    try {
      return window === window.top;
    } catch {
      return false;
    }
  })();
  var forceFullInFrame = (() => {
    try {
      return !!(window.__KP_POPOVER_IFRAME || window.__KP_FORCE_FULL_KEYPILOT);
    } catch {
      return false;
    }
  })();
  if (isTop) {
    (async () => {
      await initializeKeyPilot();
      try {
        const onboarding = new OnboardingManager();
        await onboarding.init();
        window.__KeyPilotOnboarding = onboarding;
      } catch (e) {
        console.warn("[KeyPilot] Failed to initialize onboarding:", e);
      }
    })();
  } else if (forceFullInFrame) {
    void initializeKeyPilot();
  }
})();
