/**
 * KeyPilot Chrome Extension — esbuild bundle
 * Generated on 2026-08-14T04:06:17.821Z
 */

(() => {
  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 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)) {
      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 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 (source[id]) out[id] = source[id];
    }
    for (const [id, assignment] of Object.entries(source || {})) {
      if (allowed.has(id) && !out[id]) out[id] = assignment;
    }
    return Object.freeze(out);
  }
  function letterFromAssignment(assignment) {
    if (!assignment) return "";
    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 (allowed.has(cell.id)) return cell;
            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 });
          })
        )
      )
    );
  }
  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, 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, 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";
      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 Element",
          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 New Tab (Foreground)",
          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 New Tab (Background, like middle click)",
          keyboardClass: "key-activate-new-over",
          row: 2
        }),
        BACK: Object.freeze({
          handler: "handleBackKey",
          label: "Go Back",
          description: "Go Back (History)",
          keyboardClass: "key-back",
          row: 2
        }),
        BACK2: Object.freeze({
          handler: "handleBackKey",
          label: "Go Back",
          description: "Go Back (History)",
          keyboardClass: "key-back",
          row: 2
        }),
        FORWARD: Object.freeze({
          handler: "handleForwardKey",
          label: "Go Forward",
          description: "Go Forward (History)",
          keyboardClass: "key-forward",
          row: 1
        }),
        DELETE: Object.freeze({
          handler: "handleDeleteKey",
          label: "Delete Mode",
          description: "Delete Mode",
          keyboardClass: "key-delete",
          row: 2
        }),
        COLS_TOGGLE: Object.freeze({
          handler: "handleColsToggleKey",
          label: "Cols Toggle",
          description: "Columnize element under cursor (multi-column layout)",
          keyboardClass: "key-cols",
          row: 3
        }),
        TAB_LEFT: Object.freeze({
          handler: "handleTabLeftKey",
          label: "Tab Left",
          description: "Move To Previous Tab",
          keyboardClass: "key-gray",
          row: 1
        }),
        TAB_RIGHT: Object.freeze({
          handler: "handleTabRightKey",
          label: "Tab Right",
          description: "Move To Next Tab",
          keyboardClass: "key-gray",
          row: 1
        }),
        ROOT: Object.freeze({
          handler: "handleRootKey",
          label: "Go to Site Root",
          description: "Go to Site Root",
          keyboardClass: null,
          row: null
        }),
        LAUNCHER: Object.freeze({
          handler: "handleLauncherKey",
          label: "Launcher",
          description: "Open Launcher (Quick Access to Sites)",
          keyboardClass: "key-launcher-orange",
          row: 2
        }),
        CLOSE_TAB: Object.freeze({
          handler: "handleCloseTabKey",
          label: "Close Tab",
          description: "Close Tab",
          keyboardClass: "key-close-tab",
          row: 3
        }),
        CANCEL: Object.freeze({
          handler: "cancelModes",
          label: "Exit Focus",
          description: "Exit Focus",
          keyboardClass: null,
          row: null
        }),
        PAGE_UP_INSTANT: Object.freeze({
          handler: "handleInstantPageUp",
          label: "Page Up Fast",
          description: "Page Up (Instant)",
          keyboardClass: "key-scroll",
          row: 3
        }),
        PAGE_DOWN_INSTANT: Object.freeze({
          handler: "handleInstantPageDown",
          label: "Page Down Fast",
          description: "Page Down (Instant)",
          keyboardClass: "key-scroll",
          row: 3
        }),
        PAGE_TOP: Object.freeze({
          handler: "handlePageTop",
          label: "Scroll To Top",
          description: "Jump to the top of the scroll target (Fade hides the jump; Scroll animates)",
          keyboardClass: "key-scroll",
          row: 3
        }),
        PAGE_BOTTOM: Object.freeze({
          handler: "handlePageBottom",
          label: "Scroll To Bottom",
          description: "Jump to the bottom of the scroll target (Fade hides the jump; Scroll animates)",
          keyboardClass: "key-scroll",
          row: 3
        }),
        SCROLL_LINE: Object.freeze({
          handler: "handleScrollLineKey",
          label: "Scroll Line",
          description: "Scroll from a fixed origin: move the mouse away from the dot to scroll faster. Optional middle-click on empty page area (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 New Tab",
          keyboardClass: "key-gray",
          row: 1
        }),
        OPEN_POPOVER: Object.freeze({
          handler: "handleOpenPopover",
          label: "Open Popover",
          description: "Open Link in Popover",
          keyboardClass: "key-open-popover",
          row: 2
        }),
        PREVIEW_LINK_POPOVER: Object.freeze({
          handler: "handlePreviewLinkPopover",
          label: "Preview Link",
          description: "Open Link Preview in Popover",
          keyboardClass: "key-preview-popover",
          row: 2
        }),
        OPEN_SETTINGS_POPOVER: Object.freeze({
          handler: "handleToggleSettingsPopover",
          label: "Settings",
          description: "Open KeyPilot Settings",
          keyboardClass: "key-settings-dark",
          row: null
        }),
        OMNIBOX: Object.freeze({
          handler: "handleOpenOmnibox",
          label: "Omnibox",
          description: "Open Omnibox (Address Bar Overlay)",
          keyboardClass: "key-orange",
          row: 2
        }),
        TAB_HISTORY: Object.freeze({
          handler: "handleToggleTabHistoryPopover",
          label: "Tab History",
          description: "Open Tab History (Branch-Retaining)",
          keyboardClass: "key-gray",
          row: 2
        }),
        TOGGLE_KEYBOARD_HELP: Object.freeze({
          handler: "handleToggleKeyboardHelp",
          label: "KB Reference",
          description: "Show/Hide the floating KeyPilot keyboard reference",
          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 (character level) and copy as rich text by default",
          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: "Select intersecting HTML elements in a rectangle (or pick cumulative)",
          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 image under cursor (clipboard, or Media Library later)",
          // 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 URL under cursor (clipboard, or Media Library later)",
          keyboardClass: null,
          row: 1
        }),
        // 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 images, videos, and documents found on this page",
          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). Media Library itself isn't built yet;
        // the handler just shows a "coming soon" notification — see `handleMediaLibraryNotAvailableKey`
        // and the `ADD_URL_TO_MEDIA_LIBRARY`/`FETCH_URL_FOR_MEDIA_LIBRARY` Functions in
        // function-library.js.
        OPEN_MEDIA_LIBRARY: Object.freeze({
          handler: "handleMediaLibraryNotAvailableKey",
          label: "Media Library",
          description: "Open the Media Library (coming soon).",
          keyboardClass: null,
          row: 1
        }),
        // Clipboard commands (Functions palette — Clipboard category).
        CLIPBOARD_COPY: Object.freeze({
          handler: "handleClipboardCopyKey",
          label: "Copy",
          description: "Copy selected text to the clipboard",
          keyboardClass: null,
          row: null
        }),
        CLIPBOARD_CUT: Object.freeze({
          handler: "handleClipboardCutKey",
          label: "Cut",
          description: "Cut selected text to the clipboard",
          keyboardClass: null,
          row: null
        }),
        CLIPBOARD_PASTE: Object.freeze({
          handler: "handleClipboardPasteKey",
          label: "Paste",
          description: "Paste clipboard text into the focused field",
          keyboardClass: null,
          row: null
        }),
        CLIPBOARD_SELECT_ALL: Object.freeze({
          handler: "handleClipboardSelectAllKey",
          label: "Select All",
          description: "Select all text in the focused field or page",
          keyboardClass: null,
          row: null
        }),
        // AI (Functions palette — AI category).
        SEND_TEXT_TO_AI: Object.freeze({
          handler: "handleSendTextToAiKey",
          label: "Send Text To AI",
          description: "Send selected text to AI with a configurable instruction; route the result to clipboard and/or popover",
          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",
        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",
        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",
        SEND_TEXT_TO_AI: "AI",
        LAUNCHER: "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",
        "Scroll",
        "Select",
        "Media Library",
        "Clipboard",
        "AI",
        "KeyPilot",
        "Tools",
        "System",
        "Other"
      ]);
      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"] }),
        BACK2: Object.freeze({ keys: ["s", "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"] }),
        LAUNCHER: 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"] }),
        ROOT: Object.freeze({ keys: ["1", "!"], displayKey: "1", keyLabel: "1" }),
        DELETE: Object.freeze({ keys: ["Backspace"], displayKey: "Backspace", keyLabel: "Backspace" }),
        COLS_TOGGLE: Object.freeze({ keys: [".", ">"], displayKey: ".", keyLabel: "." })
      });
      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: ";" }),
        BACK2: Object.freeze({ keys: ["l", "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"] }),
        LAUNCHER: Object.freeze({ keys: ["a", "A", "`", "~", "Backquote"], matchOn: ["key", "code"], displayKey: "a/`", keyLabel: "a/`" }),
        // Bottom row cluster: Z X C V B  ->  / . , M N (mirrored)
        // Period reserved for COLS_TOGGLE (same muscle memory as right-handed).
        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"] }),
        COLS_TOGGLE: Object.freeze({ keys: [".", ">"], displayKey: ".", keyLabel: "." }),
        // I is OPEN_POPOVER on left-handed; E is free.
        COPY_HOVERED_IMAGE: Object.freeze({ keys: ["e", "E"] }),
        ROOT: Object.freeze({ keys: ["1", "!"], displayKey: "1", keyLabel: "1" }),
        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",
        "BACK2",
        "PAGE_TOP",
        "PAGE_BOTTOM",
        "PAGE_UP_INSTANT",
        "PAGE_DOWN_INSTANT"
      ]);
      CLICK_HISTORY_ACTION_IDS = Object.freeze([
        "ACTIVATE",
        "BACK",
        "BACK2",
        "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: "BACK2", fallbackText: "Go Back" },
          { 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: "LAUNCHER", fallbackText: "Launcher" },
          { 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 Fast" },
          { type: "action", id: "PAGE_DOWN_INSTANT", fallbackText: "Page Down Fast" },
          { 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: "action", id: "COLS_TOGGLE", fallbackText: "Cols Toggle" },
          { 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: "LAUNCHER", fallbackText: "Launcher" },
          // 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: "BACK2", fallbackText: "Go Back" },
          // 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 Fast" },
          // M
          { type: "action", id: "PAGE_UP_INSTANT", fallbackText: "Page Up Fast" },
          // ,
          { type: "action", id: "COLS_TOGGLE", fallbackText: "Cols Toggle" },
          // .
          { 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
  var KEYBINDINGS, SELECTORS, CLICKABLE_CATEGORY, CSS_CLASSES, ELEMENT_IDS, Z_INDEX, SCROLL, MODES, INSPECTOR_KIND, 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",
        /**
         * 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",
        /** 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",
        /** 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",
        VIEWPORT_MODAL_FRAME: "kpv2-viewport-modal-frame",
        ESC_EXIT_LABEL: "kpv2-esc-exit-label",
        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",
        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"
      };
      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,
        // Iframe-based popover modal (Open Popover)
        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,
        // 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,
        /** Default CSS scroll-behavior for keyboard scrolling */
        BEHAVIOR: "smooth",
        /** Fade-in / fade-out duration for Scroll To Top / Bottom "Fade" jump style */
        EDGE_JUMP_FADE_MS: 180,
        /** 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_TAGS = Object.freeze([
        "h1",
        "h2",
        "h3",
        "h4",
        "h5",
        "h6",
        "p",
        "li",
        "blockquote",
        "pre",
        "code",
        "article",
        "section",
        "aside",
        "header",
        "footer",
        "main",
        "nav",
        "a",
        "img",
        "figure",
        "figcaption",
        "picture",
        "video",
        "audio",
        "svg",
        "td",
        "th",
        "dt",
        "dd",
        "caption",
        "summary",
        "label"
      ]);
      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)",
        // 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)",
        // 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 extension/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
        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 A / B / C. Toggle: Alt+D (or keyPilot.setShadowRootDebugHud).
        DEBUG_SHADOW_ROOT_HUD: false
      };
    }
  });

  // 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
  async function storageGetValue(key, defaultValue = void 0) {
    if (!key || typeof key !== "string") return defaultValue;
    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) {
          return (
            /** @type {T} */
            syncResult[key]
          );
        }
      }
    } catch {
    }
    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) {
          return (
            /** @type {T} */
            localResult[key]
          );
        }
      }
    } catch {
    }
    return 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,
    normalizeSearchEngine: () => normalizeSearchEngine,
    normalizeTextFocusStyle: () => normalizeTextFocusStyle,
    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 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 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
      )
    };
  }
  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)
    };
  }
  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
      )
    };
  }
  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,
        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
        ),
        actionsLibraryTableExpanded: normalizeActionsLibraryTableExpanded(
          stored?.actionsLibraryTableExpanded
        ),
        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 }
        },
        actionSettings: normalizeActionSettings(null),
        clickMode: { ...DEFAULT_SETTINGS.clickMode, cursor: { ...DEFAULT_SETTINGS.clickMode.cursor } },
        textMode: { ...DEFAULT_SETTINGS.textMode },
        scroll: { ...DEFAULT_SETTINGS.scroll },
        actionsLibraryTableExpanded: [...DEFAULT_SETTINGS.actionsLibraryTableExpanded]
      };
    }
  }
  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 : {}
        }
      },
      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);
    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.actionsLibraryTableExpanded = normalizeActionsLibraryTableExpanded(
      next.actionsLibraryTableExpanded
    );
    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);
    await storageSetValue(SETTINGS_STORAGE_KEY, next);
    return next;
  }
  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_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({
        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,
        // 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"]),
        // 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" })
        }),
        // 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
        }),
        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()
        })
      });
      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;
    }
    while (active2) {
      let next = null;
      try {
        next = active2.shadowRoot?.activeElement || null;
      } catch {
        next = null;
      }
      if (!next) break;
      active2 = next;
    }
    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 type = String(input.type || "text").toLowerCase();
    return TEXT_ENTRY_TYPE_SET.has(type);
  }
  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.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;
      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`;
      console.log("setMode: cursorValue", cursorValue);
      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 color = 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="${color}" stroke-width="${strokeWidth}" />
        <line x1="${30 - tHalf}" y1="${tTopY}" x2="${30 + tHalf}" y2="${tTopY}" stroke="${color}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="30" y1="${tTopY}" x2="30" y2="${tBottomY}" stroke="${color}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
      </svg>`;
      } else if (mode === "text_focus") {
        const color = 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="${color}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="30" y1="${segmentStart2}" x2="30" y2="${segmentEnd2}" stroke="${color}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="${segmentStart}" y1="30" x2="${segmentEnd}" y2="30" stroke="${color}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="${segmentStart2}" y1="30" x2="${segmentEnd2}" y2="30" stroke="${color}" 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 color = 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="${color}" stroke-width="3"/>
        <rect x="34" y="12" width="16" height="36" rx="1" fill="none" stroke="${color}" stroke-width="3"/>
        <line x1="13" y1="20" x2="23" y2="20" stroke="${color}" stroke-width="2" stroke-linecap="round"/>
        <line x1="13" y1="28" x2="23" y2="28" stroke="${color}" stroke-width="2" stroke-linecap="round"/>
        <line x1="13" y1="36" x2="23" y2="36" stroke="${color}" stroke-width="2" stroke-linecap="round"/>
        <line x1="37" y1="20" x2="47" y2="20" stroke="${color}" stroke-width="2" stroke-linecap="round"/>
        <line x1="37" y1="28" x2="47" y2="28" stroke="${color}" stroke-width="2" stroke-linecap="round"/>
        <line x1="37" y1="36" x2="47" y2="36" stroke="${color}" stroke-width="2" stroke-linecap="round"/>
      </svg>`;
      } else if (mode === "highlight") {
        const color = 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="${color}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="30" y1="${segmentStart2}" x2="30" y2="${segmentEnd2}" stroke="${color}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="${segmentStart}" y1="30" x2="${segmentEnd}" y2="30" stroke="${color}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="${segmentStart2}" y1="30" x2="${segmentEnd2}" y2="30" stroke="${color}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
      </svg>`;
      } else {
        const color = 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="${color}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="30" y1="${segmentStart2}" x2="30" y2="${segmentEnd2}" stroke="${color}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="${segmentStart}" y1="30" x2="${segmentEnd}" y2="30" stroke="${color}" stroke-width="${strokeWidth}" stroke-linecap="${strokeLineCap}"/>
        <line x1="${segmentStart2}" y1="30" x2="${segmentEnd2}" y2="30" stroke="${color}" 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();
        const oldMode = this.currentMode;
        this.currentMode = null;
        this.currentModeKey = null;
        this.setMode(currentMode, {});
      }
    }
    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();
  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, audio, summary";
      this.FOCUSABLE_SEL = 'a[href], button, input, select, textarea, video, audio, summary, [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]';
      this.clickHandlerElements = /* @__PURE__ */ new WeakSet();
      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";
    }
    setupEventListenerTracking() {
      const originalAddEventListener = EventTarget.prototype.addEventListener;
      EventTarget.prototype.addEventListener = function(type, listener, options) {
        originalAddEventListener.call(this, type, listener, options);
        if (type === "click" && this instanceof Element) {
          try {
            elementDetectorInstance.clickHandlerElements.add(this);
          } catch {
          }
        }
      };
      window.elementDetectorInstance = this;
    }
    hasTrackedClickHandler(el2) {
      return this.clickHandlerElements.has(el2);
    }
    /**
     * 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) {
      let el2 = document.elementFromPoint(x, y);
      let guard = 0;
      while (el2 && el2.shadowRoot && guard++ < 10) {
        const nested = el2.shadowRoot.elementFromPoint(x, y);
        if (!nested || nested === el2) break;
        el2 = nested;
      }
      return 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;
      const matchesSelector = el2.matches(this.FOCUSABLE_SEL);
      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);
      let hasClickHandler = hasInlineClick || hasTrackedClick;
      if (hasClickHandler && !matchesSelector && !hasRole && !hasInlineClick && this.isCompositeClickContainer(el2, role)) {
        hasClickHandler = false;
      }
      let hasCursor = false;
      if (allowCursor && !matchesSelector && !hasRole && !hasClickHandler) {
        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;
      }
      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 this._withNativePageCursors(
          () => this._findClickableWalk(el2, { allowCursor: true })
        );
      }
      return this._findClickableWalk(el2, { allowCursor: true });
    }
    /**
     * True when `el` is nested chrome inside `host` (More menu, small sub-links,
     * icon buttons) rather than the primary hover target for the row/card/tab.
     * @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;
      }
      const tag = el2.tagName;
      const role = (el2.getAttribute && el2.getAttribute("role") || "").trim().toLowerCase();
      if (tag === "BUTTON" || role === "button") return true;
      if (el2.getAttribute && el2.getAttribute("aria-haspopup")) return true;
      if (tag === "A" || role === "link") {
        try {
          const er = el2.getBoundingClientRect();
          const hr = host.getBoundingClientRect();
          const eArea = Math.max(1, er.width * er.height);
          const hArea = Math.max(1, hr.width * hr.height);
          return eArea < hArea * 0.35;
        } catch {
          return true;
        }
      }
      return false;
    }
    /**
     * 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 leafInside = this.findClickable(under);
            if (leafInside) {
              try {
                const stretchTarget = this._resolveStretchedLinkHoverTarget(
                  leafInside,
                  clientX,
                  clientY
                );
                if (stretchTarget) return 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 host2;
            return 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 stretchTarget;
          } catch {
          }
        }
        if (!leafAtPoint || leafAtPoint === prev || this._isNestedHoverChrome(leafAtPoint, prev)) {
          return prev;
        }
        try {
          if (!this.composedContains(prev, leafAtPoint) && this.pointInElementUnionBox(leafAtPoint, clientX, clientY, 1)) {
            const host2 = this._findPreferableHoverHost(leafAtPoint);
            return 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 underlayHost || underlay;
      }
      try {
        const stretchTarget = this._resolveStretchedLinkHoverTarget(leaf, clientX, clientY);
        if (stretchTarget) return stretchTarget;
      } catch {
      }
      const host = this._findPreferableHoverHost(leaf);
      return 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, type) {
      return el2 && el2.tagName === "INPUT" && (el2.getAttribute("type") || "").toLowerCase() === type;
    }
    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;
    }
    /**
     * 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 activatorContainsPrimary = !!(activatorEl && primaryNode && typeof activatorEl.contains === "function" && activatorEl.contains(primaryNode));
        if (activator && activator !== primary && !activatorContainsPrimary) {
          this.dispatchClickSequence(activator, clientX, clientY);
        }
      } catch {
      }
      return true;
    }
    handleSmartActivate(target, x, y, openInNewTab = false) {
      if (!target) return false;
      target = this.resolveLabel(target);
      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) {
        target.checked = true;
        this.dispatchInputChange(target);
      }
      return true;
    }
    handleCheckbox(target) {
      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;
        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);
    }
    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.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);
      const leftText = this.isTextInput(composed) || this.isTextInput(e.target) || this.currentFocusedElement && e.target === this.currentFocusedElement;
      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;
      }
    }
    setTextFocus(element) {
      if (window.KEYPILOT_DEBUG) {
        console.log("[KeyPilot] Setting text focus for element:", element.tagName, element.type || "N/A");
      }
      this.currentFocusedElement = 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.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;
      }
      const target = root === document || typeof Document !== "undefined" && root instanceof Document ? document.documentElement : root || 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/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",
    // --- 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_ONBOARDING: "KP_OPEN_ONBOARDING",
    /** Reset walkthrough progress and open it (e.g. Guide "Launch Walkthrough"). */
    LAUNCH_WALKTHROUGH: "KP_LAUNCH_WALKTHROUGH",
    // --- History / bookmarks / favicon (SW APIs for content scripts) ---
    OMNIBOX_SUGGEST: "KP_OMNIBOX_SUGGEST",
    GET_BOOKMARKS: "KP_GET_BOOKMARKS",
    BROWSER_HISTORY_GET: "KP_BROWSER_HISTORY_GET",
    GET_TOP_SITES: "KP_GET_TOP_SITES",
    GET_HISTORY_FOR_DOMAINS: "KP_GET_HISTORY_FOR_DOMAINS",
    GET_RECENT_HISTORY: "KP_GET_RECENT_HISTORY",
    GET_FAVICON: "KP_GET_FAVICON",
    // --- Page preview screenshots for card backgrounds ---
    GET_PAGE_THUMB: "KP_GET_PAGE_THUMB",
    PAGE_THUMB_RESPONSE: "KP_PAGE_THUMB_RESPONSE",
    PAGE_THUMB_UPDATED: "KP_PAGE_THUMB_UPDATED",
    GET_VIDEO_THUMB: "KP_GET_VIDEO_THUMB",
    VIDEO_THUMB_RESPONSE: "KP_VIDEO_THUMB_RESPONSE",
    // --- Per-tab navigation graph ---
    NAVGRAPH_GET: "KP_NAVGRAPH_GET",
    NAVGRAPH_JUMP: "KP_NAVGRAPH_JUMP",
    NAVGRAPH_CLEAR: "KP_NAVGRAPH_CLEAR",
    // --- Generic ---
    SUCCESS: "KP_SUCCESS",
    ERROR: "KP_ERROR",
    // --- Link Preview mobile User-Agent (SW declarativeNetRequest session rules) ---
    SET_PREVIEW_MOBILE_UA: "KP_SET_PREVIEW_MOBILE_UA",
    // --- 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; C/V/Z/X 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 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"
  });
  var TAB_UI_FORWARD_TYPES = Object.freeze([
    MSG.OPEN_SETTINGS_POPOVER,
    MSG.OPEN_GUIDE_POPOVER,
    MSG.OPEN_ONBOARDING,
    MSG.LAUNCH_WALKTHROUGH
  ]);

  // 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.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 && node.childNodes?.length) {
              const child = node.childNodes[Math.min(caretRange.startOffset, node.childNodes.length - 1)];
              if (child?.nodeType === Node.TEXT_NODE) {
                return { textNode: child, offset: 0 };
              }
            }
          }
        }
        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 };
            }
          }
        }
      } 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.elementMatchedElements = [];
        this.clearHighlightSelectionOverlays();
        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.elementMatchedElements = [];
          this.clearHighlightSelectionOverlays();
          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.elementMatchedElements = [];
        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;
        let r;
        try {
          r = el2.getBoundingClientRect();
        } catch {
          continue;
        }
        if (!r || r.width <= 0 && r.height <= 0) continue;
        if (!this._rectsIntersect(rect, r)) continue;
        hits.push(el2);
      }
      return this._deepestWins(hits);
    }
    /**
     * Prefer deepest elements: drop any hit that contains another hit.
     * @param {Element[]} elements
     * @returns {Element[]}
     */
    _deepestWins(elements) {
      if (!elements || elements.length <= 1) return elements || [];
      const set = new Set(elements);
      return elements.filter((el2) => {
        for (const other of set) {
          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 {{ minWidth?: number, minHeight?: number, margin?: number }} [params.resizeOptions]
     */
    showModal({ id, panel, onRequestClose, resizable = true, resizeOptions } = {}) {
      if (!id || !panel) return;
      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;
        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
      };
      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();
    }
    _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",
          background: "rgba(0,0,0,0.35)",
          // Blur only the page behind the dimmer — never the panel (sibling above).
          backdropFilter: "blur(6px)",
          WebkitBackdropFilter: "blur(6px)",
          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();
      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 {
          }
        }
      }
    }
    _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 {
        e.preventDefault();
        e.stopPropagation();
        const top = this.top();
        if (top?.onRequestClose) top.onRequestClose();
        else this.hideModal();
      } catch {
      }
    }
  };

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

  // src/ui/preview-open-actions.js
  init_constants();

  // src/ui/nct-dark-ui.js
  var NCT_DARK_UI_FONT = "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 = NCT_DARK_UI_COLORS.panel;
  var NCT_DARK_UI_PANEL_BORDER = `1px solid ${NCT_DARK_UI_COLORS.panelEdgeDark}`;
  var NCT_DARK_UI_PANEL_BOX_SHADOW = `0 0 0 1px ${NCT_DARK_UI_COLORS.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)`;
  var NCT_DARK_UI_PANEL_RADIUS = "3px";
  var NCT_DARK_UI_TITLEBAR_GRADIENT = `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_BORDER_BOTTOM = `1px solid ${NCT_DARK_UI_COLORS.panelEdgeDark}`;
  var NCT_DARK_UI_TITLEBAR_BOX_SHADOW = `0 1px 0 ${NCT_DARK_UI_COLORS.panelEdge}`;
  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 = `linear-gradient(180deg, ${NCT_DARK_UI_COLORS.btnTop} 0%, ${NCT_DARK_UI_COLORS.btnMid} 50%, ${NCT_DARK_UI_COLORS.btnBot} 100%)`;
  var NCT_DARK_UI_BTN_BORDER = `1px solid ${NCT_DARK_UI_COLORS.panelEdgeDark}`;
  var NCT_DARK_UI_BTN_RADIUS = "2px";
  var NCT_DARK_UI_ICON_BUTTON_OUTLINE = `inset 0 0 0 1px ${NCT_DARK_UI_COLORS.panelEdge}`;
  var NCT_DARK_UI_BTN_LIT_GRADIENT = `linear-gradient(180deg, ${NCT_DARK_UI_COLORS.litTop} 0%, ${NCT_DARK_UI_COLORS.litBot} 100%)`;
  var NCT_DARK_UI_BTN_LIT_BORDER = `1px solid ${NCT_DARK_UI_COLORS.litEdge}`;
  var NCT_DARK_UI_FIELD_BACKGROUND = NCT_DARK_UI_COLORS.fieldBg;
  var NCT_DARK_UI_FIELD_BORDER = `1px solid ${NCT_DARK_UI_COLORS.fieldEdge}`;
  var NCT_DARK_UI_FIELD_BOX_SHADOW = `inset 0 1px 0 ${NCT_DARK_UI_COLORS.fieldInsetTop}`;
  var NCT_DARK_UI_FIELD_FOCUS_BORDER = NCT_DARK_UI_COLORS.accent;
  var NCT_DARK_UI_FIELD_FOCUS_BOX_SHADOW = `inset 0 0 0 1px rgba(74,144,200,0.35)`;
  var NCT_DARK_UI_FOCUS_RING = `inset 0 0 0 1px rgba(74,144,200,0.55)`;
  var NCT_DARK_UI_SELECTED_TINT = "rgba(74,144,200,0.22)";
  var NCT_DARK_UI_SELECTED_TEXT = "#e8f0f8";
  var NCT_DARK_UI_HOVER_TINT = "rgba(255,255,255,0.06)";
  function applyFlashNotificationStyle(el2, opts = {}) {
    if (!el2) return;
    const color = 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%), ${color}`,
        backgroundColor: color,
        color: "#fff",
        padding: hasThumbnail ? "8px 12px 8px 14px" : "8px 16px",
        borderRadius: "2px",
        border: "1px solid rgba(0,0,0,0.55)",
        fontSize: "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: "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
  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 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 };
  }

  // src/ui/popover-titlebar.js
  init_constants();
  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: ${KP_UI_FONT};
      font-size: 14px;
      font-weight: 400;
      font-style: normal;
      line-height: 1.3;
      letter-spacing: normal;
      text-transform: none;
      -webkit-font-smoothing: antialiased;
    `,
      title: `
      font-family: inherit;
      font-size: 14px;
      font-weight: 500;
      color: #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: ${KP_UI_FONT};
      font-size: 12px;
      font-weight: 400;
      font-style: normal;
      line-height: 1.3;
      letter-spacing: normal;
      text-transform: none;
      -webkit-font-smoothing: antialiased;
    `,
      title: `
      font-family: inherit;
      font-size: 12px;
      font-weight: 500;
      color: #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;
    `
    }
  };
  var KBD_STYLE = `
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
  font-size: 11px;
  padding: 1px 6px;
  border: ${NCT_DARK_UI_BTN_BORDER};
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  background: ${NCT_DARK_UI_BTN_GRADIENT};
  color: ${NCT_DARK_UI_COLORS.fg};
`;
  function createTitlebarKbd(doc = document, label = "") {
    const kbd = doc.createElement("kbd");
    kbd.style.cssText = KBD_STYLE;
    kbd.textContent = String(label || "");
    return kbd;
  }
  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" ? "preview" : "modal";
    const styles = VARIANT_STYLES[variant];
    const showClose = config.showClose !== false;
    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 (config.draggable) {
      titlebar.style.cursor = "grab";
    }
    if (config.titleAttr) {
      titlebar.title = config.titleAttr;
    }
    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: baseline;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1;
    min-width: 0;
    margin-right: 4px;
    ${config.draggable ? "pointer-events: none;" : ""}
  `;
    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 hintEl = doc.createElement("span");
    hintEl.className = "kpv2-popover-titlebar-hint";
    hintEl.style.cssText = styles.hint;
    titleContainer.appendChild(titleEl);
    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) : "";
    };
    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,
      hintEl,
      titleContainer,
      actionsSlot,
      setTitle,
      setHint,
      getInteractiveElements
    };
  }

  // 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 ensureOpenChromeShadow(host, opts = {}) {
    if (!host) return null;
    try {
      host.setAttribute("data-kp-ui-shadow", String(opts.id || "chrome"));
    } catch {
    }
    try {
      if (host.shadowRoot) return host.shadowRoot;
      return host.attachShadow({ mode: "open" });
    } catch {
      return host.shadowRoot || null;
    }
  }
  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 isKeyPilotChromeElement(element) {
    return !!closestComposed(
      element,
      [
        "[data-kp-ui-shadow]",
        ".kp-floating-keyboard-help",
        ".kp-control-strip",
        ".kp-onboarding-panel",
        ".kp-layout-config-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/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/modules/overlay-manager.js
  var PREVIEW_VIEWPORT_BY_HOST_KEY = "kp_link_preview_viewport_by_host";
  function previewHostFromUrl(url) {
    try {
      let host = new URL(String(url || "")).hostname.toLowerCase();
      if (host.startsWith("www.")) host = host.slice(4);
      return host;
    } catch {
      return "";
    }
  }
  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;
  }
  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.hoverClickLabelText = "F clicks";
      this.popoverContainer = null;
      this.popoverIframeElement = null;
      this.popoverIframeWindow = null;
      this.popoverMessageHandler = null;
      this.popoverInitTimer = null;
      this.popoverBridgeReady = false;
      this.popoverCloseButton = null;
      this._popoverLastMouse = { x: null, y: null };
      this._popoverMouseTrackerInstalled = false;
      this._isPreviewPopover = false;
      this._popoverArrowStyle = null;
      this._popoverClickOutsideHandler = null;
      this._previewPopoverDragCleanup = null;
      this._popoverResizeDispose = null;
      this._popoverHybridFocusCleanup = null;
      this._previewMobileUaActive = false;
      this._edgeJumpFadeEl = null;
      this._edgeJumpFadeToken = 0;
      this.popupManager = new PopupManager();
      this._popoverPopupId = "kpv2-iframe-popover";
      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 = null;
      this._shadowDebugLastInfo = null;
      this._useDomHoverFocusColors = false;
      this._focusPaintUsesFixedOverlay = false;
      this._focusPaintUsesInTargetRing = false;
      this._inTargetRing = null;
      this._inTargetHost = null;
      this._inTargetHostPosRestore = null;
      this._textFocusCurrentElement = null;
      this._textFocusStyledElements = /* @__PURE__ */ new Set();
      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._installFocusClipCacheInvalidation();
      this._activeEphemeralEffects = /* @__PURE__ */ new Set();
      this._ephemeralEffectLifecycleInstalled = false;
      this._ephemeralEffectLifecycleDispose = null;
      this.setupOverlayObserver();
      this.highlightManager.initialize(this.overlayObserver);
    }
    /**
     * Toggle alternate focus overlay colors (blue) for DOM-hover listener targeting mode.
     * @param {boolean} enabled
     */
    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;
    }
    /**
     * 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 color = focusColor === "green" || focusColor === "blue" ? focusColor : this._getClickModeSettings().focusColor || "blue";
      if (color === "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 rect = rectOverride && typeof rectOverride === "object" ? rectOverride : this.getBestRect(element);
      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 rect = rectOverride && typeof rectOverride === "object" ? rectOverride : this.getBestRect(element);
      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 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 {
      }
    }
    _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";
      return {
        rectangleThickness: thickness,
        overlayFillEnabled,
        overlayShadowEnabled,
        focusColor,
        clickEffect
      };
    }
    _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";
    }
    _clearTextFocusElementStyling() {
      if (!this._textFocusStyledElements || this._textFocusStyledElements.size === 0) {
        this._textFocusCurrentElement = null;
        this._textFocusAppliedStyle = null;
        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);
          } catch {
          }
        }
      } finally {
        this._textFocusStyledElements.clear();
        this._textFocusCurrentElement = null;
        this._textFocusAppliedStyle = null;
      }
    }
    _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;
    }
    /**
     * 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";
      if (this._textFocusCurrentElement === inputEl && this._textFocusStyledElements.size > 0 && this._textFocusAppliedStyle === focusStyle) {
        try {
          inputEl.classList.add(CSS_CLASSES.TEXT_FOCUS_INPUT);
          if (useLeftEdge) inputEl.classList.add(CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE);
          else inputEl.classList.remove(CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE);
        } catch {
        }
        return;
      }
      this._clearTextFocusElementStyling();
      this._textFocusCurrentElement = inputEl;
      this._textFocusAppliedStyle = focusStyle;
      try {
        this._ensureStylesForElement(inputEl);
        inputEl.classList.add(CSS_CLASSES.TEXT_FOCUS_INPUT);
        if (useLeftEdge) inputEl.classList.add(CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE);
        this._textFocusStyledElements.add(inputEl);
      } 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 {
          }
        }
      }
    }
    _clearTextHoverElementStyling() {
      if (!this._textHoverStyledElements || this._textHoverStyledElements.size === 0) {
        this._textHoverCurrentElement = null;
        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;
      }
    }
    _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 {
        }
        return;
      }
      this._clearTextHoverElementStyling();
      this._textHoverCurrentElement = inputEl;
      try {
        this._ensureStylesForElement(inputEl);
        inputEl.classList.add(CSS_CLASSES.TEXT_HOVER_INPUT);
        this._textHoverStyledElements.add(inputEl);
      } catch {
      }
    }
    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) {
      if (window.KEYPILOT_DEBUG && focusEl) {
        console.log("[KeyPilot Debug] Updating overlays:", {
          focusElement: focusEl.tagName,
          mode,
          willShowFocus: mode === "none" || mode === "text_focus" || mode === "highlight" || mode === "popover",
          focusedTextElement: focusedTextElement?.tagName
        });
      }
      if (mode === "none" || mode === "text_focus" || mode === "highlight" || mode === "popover") {
        this.updateFocusOverlay(focusEl, mode, focusRectOverride);
        if (mode === "text_focus") {
          if (focusedTextElement) this.updateTextModeLabels(focusedTextElement);
          else this.hideTextModeLabels();
        } else {
          this.hideTextModeLabels();
        }
      } else {
        this.hideFocusOverlay();
      }
      if (!focusEl) {
        this._clearTextHoverElementStyling();
      }
      if (mode === "text_focus" && focusedTextElement) {
        this._applyTextFocusElementStyling(focusedTextElement);
      } else {
        this._clearTextFocusElementStyling();
      }
      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.updateHighlightOverlay(focusEl);
        this.highlightManager.showHighlightModeIndicator();
      } else {
        this.highlightManager.hideHighlightOverlay();
        this.highlightManager.hideHighlightModeIndicator();
        this.highlightManager.hideHighlightRectangleOverlay();
      }
    }
    // Unified interface that switches between rendering modes
    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;
      }
      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 = element;
        try {
          paintEl = this._resolveElementForFocusStyling(element) || element;
        } catch {
          paintEl = element;
        }
        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 needsEscapeHatch = false;
        try {
          needsEscapeHatch = this._shouldUseFixedFocusOverlay(element);
        } catch {
          needsEscapeHatch = false;
        }
        let autoStrategy = "A";
        if (shadowPaint || needsEscapeHatch) {
          let canB = false;
          try {
            canB = !!(FEATURE_FLAGS && FEATURE_FLAGS.ENABLE_IN_TARGET_FOCUS_RING) && !!this._resolveInTargetHost(element);
          } catch {
            canB = false;
          }
          autoStrategy = canB ? "B" : "C";
        }
        const override = this._shadowDebugHudEnabled ? this._shadowDebugPaintOverride : null;
        const strategy = override === "A" || override === "B" || override === "C" ? override : autoStrategy;
        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;
          }
        }
        this._focusPaintUsesInTargetRing = false;
        try {
          this.hideInTargetFocusRing();
        } catch {
        }
        this._focusPaintUsesFixedOverlay = true;
        const paintForC = rectOverride ? element : paintEl;
        const cResult = this.updateFocusOverlayDOM(paintForC, mode, rectOverride);
        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 {
      }
    }
    /**
     * 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 (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"));
    }
    /**
     * 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;
      }
    }
    /**
     * Overflow or paint-containment clip (self / wrapper).
     * @param {CSSStyleDeclaration|null|undefined} cs
     * @returns {boolean}
     */
    _styleClipsSelf(cs) {
      return this._styleClipsOverflow(cs) || this._styleClipsPaintContain(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.
     * Matches the historical path-A default.
     */
    _preferredFocusOutlineOffsetPx() {
      return 2;
    }
    /**
     * 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 = [];
      }
      if (!clippers.length) {
        try {
          if (this._isProbablyClippedByAncestorOverflow(paintEl) && clippers.length === 0) {
          }
        } catch {
        }
        return Infinity;
      }
      let 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;
        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 **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 → still use **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 (strip z > OVERLAYS) and stay invisible.
     * - 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;
      }
      let er = null;
      try {
        er = paintEl.getBoundingClientRect();
      } catch {
        return false;
      }
      if (!er || !(er.width > 0) || !(er.height > 0)) return false;
      let selfClips = false;
      try {
        selfClips = this._styleClipsSelf(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;
    }
    /**
     * 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)) continue;
          if (wr.width >= box.width * 0.85 && wr.height >= box.height * 0.85) {
            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;
    }
    /**
     * 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);
          }
        }
        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);
        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;
    }
    /**
     * 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;
      let paintEl = element;
      try {
        paintEl = this._resolveElementForFocusStyling(element) || element;
      } catch {
        paintEl = element;
      }
      if (!paintEl || paintEl.nodeType !== 1) return null;
      try {
        if (!paintEl.isConnected) return null;
      } catch {
        return null;
      }
      let focusRect = null;
      try {
        focusRect = element.getBoundingClientRect();
      } catch {
        focusRect = null;
      }
      if (!focusRect || !(focusRect.width > 1) || !(focusRect.height > 1)) {
        try {
          focusRect = paintEl.getBoundingClientRect();
        } catch {
          focusRect = null;
        }
      }
      try {
        const root = typeof paintEl.getRootNode === "function" ? paintEl.getRootNode() : null;
        if (root && typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot) {
          if (this._canMountVisibleChildOnHost(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._canMountVisibleChildOnHost(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._canMountVisibleChildOnHost(paintEl)) {
        let pr = null;
        try {
          pr = paintEl.getBoundingClientRect();
        } catch {
          pr = null;
        }
        if (pr && pr.width >= 8 && pr.height >= 8) return paintEl;
      }
      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._canMountVisibleChildOnHost(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 element children (excluding ring).
     * @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)) max = Math.max(max, n);
      };
      try {
        for (const pseudo of [":before", ":after"]) {
          const ps = window.getComputedStyle(host, pseudo);
          if (ps) consider(ps.zIndex);
        }
      } catch {
      }
      try {
        const kids = host.children;
        if (!kids) return max;
        const ringClass = CSS_CLASSES.FOCUS_RING_INTARGET || "kpv2-focus-ring-intarget";
        for (let i = 0; i < kids.length; i++) {
          const child = kids[i];
          if (!child || child.nodeType !== 1) continue;
          if (child === this._inTargetRing) continue;
          try {
            if (child.classList && child.classList.contains(ringClass)) continue;
          } catch {
          }
          let cs = null;
          try {
            cs = window.getComputedStyle(child);
          } catch {
            cs = null;
          }
          if (!cs) continue;
          if (cs.zIndex !== "auto") consider(cs.zIndex);
        }
      } 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;
        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;
    }
    /**
     * 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) — fall through to strategy C for the union rect instead.
     * @param {Element|null|undefined} el
     * @returns {boolean}
     */
    _isFragmentedInlineFocusTarget(el2) {
      if (!el2 || el2.nodeType !== 1) return false;
      try {
        const rects = el2.getClientRects();
        if (rects && rects.length >= 2) return true;
      } catch {
      }
      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;
      if (this._isFragmentedInlineFocusTarget(element)) {
        return false;
      }
      const host = 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();
      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";
        }
      }
      let radius = "0";
      try {
        radius = this._resolveElementBorderRadius(host) || this._resolveElementBorderRadius(element) || "0";
      } catch {
        radius = "0";
      }
      const zLocal = this._maxLocalZIndex(host) + 1;
      const z = this._isInShadowTree(host) ? Math.max(zLocal, 2147483e3) : zLocal;
      try {
        ring.style.setProperty("position", "absolute", "important");
        ring.style.setProperty("inset", "0", "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");
        ring.style.setProperty("border-style", "solid", "important");
        ring.style.setProperty("border-color", borderColor, "important");
        ring.style.setProperty("border-radius", radius, "important");
        ring.style.setProperty("background", backgroundColor, "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;
        }
      } 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;
      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 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");
      if (this._isInShadowTree(stylingTarget)) {
        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;
    }
    /**
     * 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 elements that contain block children can be split into multiple
     *    inline fragments, causing outline/box-shadow to render as disjoint pieces.
     * 3) 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 `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;
      }
      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-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 {
      }
    }
    updateFocusOverlayDOM(element, mode = MODES.NONE, rectOverride = 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 isTextInput = element.matches && element.matches(SELECTORS.FOCUSABLE_TEXT);
      const suppressFill = this.shouldSuppressFocusFill(element);
      const rect = rectOverride && typeof rectOverride === "object" ? rectOverride : this.getBestRect(element);
      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";
      this.focusOverlay.style.border = `${rectangleThickness}px solid ${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();
      }
    }
    /**
     * Shared inspector outline for any pick tool kind.
     * @param {Element|null|undefined} element
     * @param {string|null|undefined} kind
     */
    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 {
          }
        }
      }
    }
    getBestRect(element) {
      if (!element) return { left: 0, top: 0, width: 0, height: 0 };
      let rect = 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 = 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;
    }
    /**
     * 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;
        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;
      }
    }
    /**
     * 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;
      const own = this._readNonZeroBorderRadius(element);
      if (own) return own;
      try {
        let parentArea = 0;
        try {
          const pr = element.getBoundingClientRect();
          parentArea = Math.max(0, (pr.width || 0) * (pr.height || 0));
        } catch {
          parentArea = 0;
        }
        const candidates = [];
        try {
          if (element.children && element.children.length) {
            for (const child of element.children) candidates.push(child);
          }
        } catch {
        }
        try {
          const media = element.querySelectorAll?.("img, svg, video, picture");
          if (media && media.length) {
            for (const m of media) candidates.push(m);
          }
        } catch {
        }
        let best = null;
        let bestArea = 0;
        for (const c of candidates) {
          const radius = this._readNonZeroBorderRadius(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;
        }
      } catch {
      }
      return null;
    }
    /**
     * Resolve the viewport rect of the current focus outline for activation feedback.
     * Works across DOM-hover element styling, DOM overlay, CSS-custom-props, and canvas modes.
     * @returns {{ left: number, top: number, width: number, height: number }|null}
     */
    _getFocusPulseRect() {
      try {
        if (this.focusOverlay && this.focusOverlay.style.display !== "none") {
          const r = this.focusOverlay.getBoundingClientRect();
          if (r && r.width > 0 && r.height > 0) {
            return { left: r.left, top: r.top, width: r.width, height: r.height };
          }
        }
      } catch {
      }
      try {
        if (this.cssCustomPropsOverlay && this.cssCustomPropsOverlay.style.display !== "none") {
          const r = this.cssCustomPropsOverlay.getBoundingClientRect();
          if (r && r.width > 0 && r.height > 0) {
            return { left: r.left, top: r.top, width: r.width, height: r.height };
          }
        }
      } catch {
      }
      try {
        const el2 = this._currentStyledElement || this._lastFocusElement;
        if (el2 && el2.nodeType === 1 && el2.isConnected) {
          const r = this.getBestRect(el2);
          if (r && r.width > 0 && r.height > 0) {
            return { left: r.left, top: r.top, width: r.width, height: r.height };
          }
        }
      } catch {
      }
      if (this._lastFocusRect && this._lastFocusRect.width > 0 && this._lastFocusRect.height > 0) {
        return { ...this._lastFocusRect };
      }
      return 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, type, handler, options) => {
        try {
          target.addEventListener(type, handler, options);
          bindings.push([target, type, 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, type, handler, options] of bindings) {
          try {
            target.removeEventListener(type, 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?.();
    }
    /**
     * Track a temporary fixed overlay so it is removed when the source target is
     * gone/hidden 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
     */
    _trackEphemeralEffect(pulse, sourceEl, originRect, cleanupMs) {
      if (!pulse) return;
      this._installEphemeralEffectLifecycle();
      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,
        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) => {
              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) {
          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 {
            }
          } 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);
    }
    /**
     * 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 3;
      const first = String(borderRadius).trim().split(/\s+/)[0] || "";
      if (!first) return 3;
      if (first.endsWith("%")) {
        const p = parseFloat(first);
        if (!Number.isFinite(p)) return 3;
        return Math.max(0, Math.min(width, height) * (p / 100));
      }
      const n = parseFloat(first);
      return Number.isFinite(n) ? Math.max(0, n) : 3;
    }
    /**
     * 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;
    }
    /**
     * F-key activation feedback for link-style targets.
     * Uses a temporary floating rectangle so it works in every render mode
     * (including DOM-hover element styling, where there is no focusOverlay div).
     *
     * Effect style comes from settings (clickMode.clickEffect):
     *   - flash (default): hard strobe on the outline
     *   - 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.
     */
    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;
      } catch {
      }
      const { clickEffect } = this._getClickModeSettings();
      if (clickEffect === "none") return;
      const presentation = this._clickEffectPresentation(clickEffect);
      if (!presentation) return;
      const rect = this._getFocusPulseRect();
      if (!rect) return;
      try {
        if (this.focusOverlay && this.focusOverlay.style.display !== "none") {
          const originalBorder = this.focusOverlay.style.border;
          const originalBoxShadow = this.focusOverlay.style.boxShadow;
          this.focusOverlay.style.border = `3px solid ${COLORS.FLASH_GREEN}`;
          this.focusOverlay.style.boxShadow = `0 0 0 2px ${COLORS.FLASH_GREEN_SHADOW}, 0 0 20px 4px ${COLORS.FLASH_GREEN_GLOW}`;
          this.focusOverlay.style.transition = "border 0.15s ease-out, box-shadow 0.15s ease-out";
          setTimeout(() => {
            if (!this.focusOverlay) return;
            this.focusOverlay.style.border = originalBorder;
            this.focusOverlay.style.boxShadow = originalBoxShadow;
            setTimeout(() => {
              if (this.focusOverlay) this.focusOverlay.style.transition = "";
            }, 150);
          }, 150);
        }
      } catch {
      }
      try {
        let liveRect = rect;
        if (el2 && el2.nodeType === 1) {
          try {
            const r = el2.getBoundingClientRect();
            if (r && r.width > 0 && r.height > 0) {
              liveRect = {
                left: r.left,
                top: r.top,
                width: r.width,
                height: r.height
              };
            }
          } catch {
          }
        }
        if (el2 && el2.nodeType === 1) {
          if (!el2.isConnected) return;
          try {
            const cs = window.getComputedStyle(el2);
            if (cs && (cs.display === "none" || cs.visibility === "hidden")) return;
          } catch {
          }
        }
        const borderRadius = this._resolveElementBorderRadius(el2);
        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`;
          if (borderRadius) {
            pulse.style.borderRadius = borderRadius;
          }
        }
        document.body.appendChild(pulse);
        this._trackEphemeralEffect(pulse, el2, liveRect, presentation.cleanupMs);
      } catch (e) {
        if (window.KEYPILOT_DEBUG) {
          console.warn("[KeyPilot] focus pulse failed:", e);
        }
      }
    }
    /**
     * 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._resolveElementBorderRadius(element);
        if (borderRadius) {
          pulse.style.borderRadius = borderRadius;
        }
        document.body.appendChild(pulse);
        this._trackEphemeralEffect(
          pulse,
          element,
          { left, top, width, height },
          900
        );
      } catch (e) {
        if (window.KEYPILOT_DEBUG) {
          console.warn("[KeyPilot] image-copy pulse failed:", e);
        }
      }
    }
    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;
      }
      this.cleanupDebugPanel();
      this.cleanupShadowRootDebugHud();
    }
    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);
      };
    }
    /**
     * Focus the popover iframe so in-frame KeyPilot receives keys without a user click.
     * @param {HTMLIFrameElement|null|undefined} iframe
     */
    _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 {
        }
      };
    }
    /**
     * Show popover with iframe containing the linked page.
     * Uses the shared {@link createPopoverTitlebar} chrome: single titlebar with
     * title, optional close-key hint, and uniform × close when enabled.
     *
     * @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 {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 default 80vw)
     * @param {string} [opts.height] - Optional fixed height (e.g., '600px', overrides default 80vh)
     */
    showPopover(url, opts = {}) {
      this.hidePopover();
      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 closeKeys = Array.isArray(opts?.closeKeys) && opts.closeKeys.length ? opts.closeKeys.map(String) : ["Escape", "p", "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 = previewChromeHost?.shadowRoot?.elementFromPoint?.(x, y) || 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",
        tabindex: "-1",
        role: "dialog",
        "aria-modal": "true",
        style: `
        position: fixed;
        inset: 0;                  /* top: 0; left: 0; bottom: 0; right: 0; */
        width: ${opts.width || "80vw"};
        height: ${opts.height || "80vh"};
        max-width: 100vw;          /* prevents overflow on very small screens */
        max-height: 100vh;
        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;
      `
      });
      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,
        variant: "modal",
        showClose,
        onClose: requestClosePopover,
        closeTitle: "Close (Esc)",
        hint: titlebarHint,
        className: "kpv2-popover-titlebar"
      });
      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 iframe = this.createElement("iframe", {
        src: url,
        tabindex: "0",
        style: `
        flex: 1;
        border: none;
        width: 100%;
        height: 100%;
      `
      });
      iframeRef = iframe;
      this.popoverIframeElement = iframe;
      this.popoverIframeWindow = iframe.contentWindow || null;
      const sendBridgeInit = () => {
        try {
          iframe.contentWindow?.postMessage({
            type: "KP_POPOVER_BRIDGE_INIT",
            closeKeys
          }, "*");
        } catch {
        }
      };
      iframe.onerror = () => {
        console.log("[KeyPilot] Iframe load error detected");
        iframe.style.display = "none";
        chromeHost.style.flex = "1 1 auto";
        errorContainer.style.display = "flex";
      };
      const loadTimeout = setTimeout(() => {
        console.log("[KeyPilot] Iframe load timeout - showing error as fallback");
        iframe.style.display = "none";
        chromeHost.style.flex = "1 1 auto";
        errorContainer.style.display = "flex";
      }, 3e4);
      iframe.onload = () => {
        clearTimeout(loadTimeout);
        console.log("[KeyPilot] Iframe loaded successfully");
        sendBridgeInit();
      };
      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
      });
      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 === "KP_POPOVER_BRIDGE_READY") {
          this.popoverBridgeReady = true;
          this._focusPopoverIframe(iframeRef);
          this._installPopoverHybridFocus({
            iframe: iframeRef,
            chromeEls: [header, closeButton].filter(Boolean),
            focusChromeEl: closeButton || header
          });
          return;
        }
        if (data.type === "KP_POPOVER_REQUEST_CLOSE") {
          if (closeKeys.includes(String(data.key))) requestClosePopover();
        }
        if (data.type === "KP_POPOVER_LAUNCH_WALKTHROUGH") {
          requestClosePopover();
          try {
            const ob = window.__KeyPilotOnboarding;
            if (ob && typeof ob.resetTutorial === "function") {
              void ob.resetTutorial();
            }
          } catch {
          }
          return;
        }
        if (data.type === "KP_POPOVER_BRIDGE_KEYDOWN") {
          const k = String(data.key || "");
          if (k === "f" || k === "F") {
            clickCloseIfHovered();
          }
        }
      };
      window.addEventListener("message", this.popoverMessageHandler, true);
      try {
        (closeButton || header)?.focus?.();
      } catch (_e) {
        try {
          this.popoverContainer.focus();
        } catch (_e2) {
        }
      }
    }
    /**
     * Hide the popover
     */
    /**
     * Remembered viewport mode for a host. Default is desktop; only mobile is stored.
     * @param {string} hostname
     * @returns {Promise<'mobile'|'desktop'>}
     */
    async _getPreviewViewportModeForHost(hostname) {
      const host = String(hostname || "").toLowerCase();
      if (!host) return "desktop";
      try {
        const map = await storageGetValue(PREVIEW_VIEWPORT_BY_HOST_KEY, {});
        if (map && typeof map === "object" && map[host] === "mobile") {
          return "mobile";
        }
      } catch (e) {
        console.warn("[KeyPilot] Failed to read preview viewport prefs:", e?.message || e);
      }
      return "desktop";
    }
    /**
     * Persist viewport mode for a host. Desktop clears the override (default).
     * @param {string} hostname
     * @param {'mobile'|'desktop'} mode
     * @returns {Promise<void>}
     */
    async _setPreviewViewportModeForHost(hostname, mode) {
      const host = String(hostname || "").toLowerCase();
      if (!host) return;
      try {
        const prev = await storageGetValue(PREVIEW_VIEWPORT_BY_HOST_KEY, {});
        const map = prev && typeof prev === "object" && !Array.isArray(prev) ? { ...prev } : {};
        if (mode === "mobile") {
          map[host] = "mobile";
        } else {
          delete map[host];
        }
        await storageSetValue(PREVIEW_VIEWPORT_BY_HOST_KEY, map);
      } catch (e) {
        console.warn("[KeyPilot] Failed to save preview viewport prefs:", e?.message || e);
      }
    }
    /**
     * Ask the service worker to enable/disable mobile User-Agent for this tab's
     * sub_frame requests (Link Preview Mobile mode).
     * @param {boolean} enabled
     * @returns {Promise<boolean>}
     */
    async _setPreviewMobileUa(enabled) {
      const next = !!enabled;
      try {
        if (typeof chrome === "undefined" || !chrome.runtime?.sendMessage) {
          this._previewMobileUaActive = false;
          return false;
        }
        const res = await chrome.runtime.sendMessage({
          type: MSG.SET_PREVIEW_MOBILE_UA,
          enabled: next
        });
        if (res?.type === MSG.ERROR) {
          console.warn("[KeyPilot] Preview mobile UA not applied:", res.error || res);
          this._previewMobileUaActive = false;
          return false;
        }
        this._previewMobileUaActive = next;
        return true;
      } catch (e) {
        console.warn("[KeyPilot] Preview mobile UA message failed:", e?.message || e);
        this._previewMobileUaActive = false;
        return false;
      }
    }
    /**
     * 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() {
      if (this._previewMobileUaActive) {
        this._previewMobileUaActive = false;
        try {
          void this._setPreviewMobileUa(false);
        } catch {
        }
      }
      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.popoverKeyHandler) {
        document.removeEventListener("keydown", this.popoverKeyHandler, true);
        if (this.popoverContainer) {
          this.popoverContainer.removeEventListener("keydown", this.popoverKeyHandler, true);
        }
        this.popoverKeyHandler = null;
      }
      if (this._popoverArrowStyle) {
        try {
          this._popoverArrowStyle.remove();
        } catch {
        }
        this._popoverArrowStyle = null;
      }
      if (this._popoverClickOutsideHandler) {
        try {
          document.removeEventListener("mousedown", this._popoverClickOutsideHandler, true);
        } catch {
        }
        this._popoverClickOutsideHandler = null;
      }
      if (this._previewPopoverDragCleanup) {
        try {
          this._previewPopoverDragCleanup();
        } catch {
        }
        this._previewPopoverDragCleanup = null;
      }
      if (this._popoverResizeDispose) {
        try {
          this._popoverResizeDispose();
        } catch {
        }
        this._popoverResizeDispose = null;
      }
      if (this._popoverHybridFocusCleanup) {
        try {
          this._popoverHybridFocusCleanup();
        } catch {
        }
        this._popoverHybridFocusCleanup = null;
      }
      if (this.popoverContainer) {
        if (this._isPreviewPopover) {
          try {
            this.popoverContainer.remove();
          } catch {
          }
        } else {
          try {
            this.popupManager?.hideModal?.(this._popoverPopupId);
          } catch {
            try {
              this.popoverContainer.remove();
            } catch {
            }
          }
        }
        this.popoverContainer = null;
      }
      this._isPreviewPopover = false;
      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;
      }
    }
    /**
     * Cover the overflow box that will jump, run `onCovered` (instant scroll), 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 }} [opts]
     * @returns {Promise<void>}
     */
    async runEdgeJumpFade(onCovered, opts = {}) {
      const durationMs = Number.isFinite(Number(opts.durationMs)) ? Math.max(80, Number(opts.durationMs)) : SCROLL.EDGE_JUMP_FADE_MS;
      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);
      el2.style.background = this._resolveEdgeJumpFadeColor(opts.coverEl || null);
      el2.style.transition = `opacity ${durationMs}ms ease`;
      await this._fadeEdgeJumpEl(el2, 1, durationMs);
      try {
        onCovered?.();
      } catch {
      }
      if (token !== this._edgeJumpFadeToken) return;
      await this._fadeEdgeJumpEl(el2, 0, durationMs);
      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 {HTMLElement} el
     * @param {number} opacity
     * @param {number} ms
     * @returns {Promise<void>}
     */
    _fadeEdgeJumpEl(el2, opacity, ms) {
      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 && e.propertyName !== "opacity") return;
          done();
        };
        el2.addEventListener("transitionend", onEnd);
        const apply = () => {
          el2.style.opacity = String(opacity);
        };
        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
     */
    scrollPopoverBy(deltaY, behavior = "smooth") {
      return this.postMessageToPopoverIframe({
        type: "KP_POPOVER_SCROLL",
        command: "scrollBy",
        delta: deltaY,
        behavior
      });
    }
    scrollPopoverToTop(behavior = "smooth") {
      return this.postMessageToPopoverIframe({
        type: "KP_POPOVER_SCROLL",
        command: "scrollToTop",
        behavior
      });
    }
    scrollPopoverToBottom(behavior = "smooth") {
      return this.postMessageToPopoverIframe({
        type: "KP_POPOVER_SCROLL",
        command: "scrollToBottom",
        behavior
      });
    }
    /**
     * Check if popover is currently open
     * @returns {boolean}
     */
    isPopoverOpen() {
      return this.popoverContainer !== null;
    }
    /**
     * Show a preview popover near the cursor (picture-in-picture style)
     * @param {string} url - URL to load in iframe
     * @param {Object} opts - Options including mouseX, mouseY for positioning
     * @param {'mobile'|'desktop'} [opts.viewportMode] - Override mode; otherwise host preference or desktop default
     */
    async showPreviewPopover(url, opts = {}) {
      this.hidePopover();
      url = preferHttpsForPreview(url);
      const mouseX = opts.mouseX ?? window.innerWidth / 2;
      const popoverWidth = 600;
      const arrowSize = 10;
      const margin = 20;
      const previewHost = previewHostFromUrl(url);
      const popoverHeight = Math.max(200, window.innerHeight - margin * 2);
      const top = margin;
      let left = mouseX - popoverWidth / 2;
      left = Math.max(margin, Math.min(left, window.innerWidth - popoverWidth - margin));
      const arrowLeft = Math.max(20, Math.min(mouseX - left, popoverWidth - 20));
      const titleText = "Link Preview";
      const closeKeys = Array.isArray(opts?.closeKeys) && opts.closeKeys.length ? opts.closeKeys.map(String) : ["Escape", "e", "E"];
      let viewportMode = "desktop";
      if (opts.viewportMode === "mobile" || opts.viewportMode === "desktop") {
        viewportMode = opts.viewportMode;
      } else {
        try {
          viewportMode = await this._getPreviewViewportModeForHost(previewHost);
        } catch {
          viewportMode = "desktop";
        }
      }
      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);
          if (!el2) return false;
          if (el2 === btn || btn.contains(el2)) {
            try {
              btn.click();
            } catch {
            }
            return true;
          }
          return false;
        } catch {
          return false;
        }
      };
      this.popoverContainer = this.createElement("div", {
        className: "kpv2-preview-popover-container",
        tabindex: "-1",
        role: "dialog",
        "aria-modal": "true",
        style: `
        position: fixed;
        top: ${top}px;
        left: ${left}px;
        width: ${popoverWidth}px;
        height: ${popoverHeight}px;
        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;
        --arrow-left: ${arrowLeft}px;
        z-index: ${Z_INDEX.POPUP_PANEL_BASE};
        font-family: ${KP_UI_FONT};
        font-size: 12px;
        line-height: 1.3;
        letter-spacing: normal;
      `
      });
      const previewChromeHost2 = this.createElement("div", {
        className: "kpv2-preview-popover-chrome-host",
        style: "display:flex; flex:0 0 auto; flex-direction:column; min-height:0;"
      });
      const previewChromeShadow = ensureOpenChromeShadow(previewChromeHost2, { id: "link-preview-chrome" });
      const previewChromeMount = previewChromeShadow || previewChromeHost2;
      const arrowStyle = this.createElement("style");
      arrowStyle.textContent = `
      .kpv2-preview-popover-container::before {
        content: "";
        position: absolute;
        width: 0;
        height: 0;
        left: var(--arrow-left, 50%);
        margin-left: -${arrowSize}px;
        border: ${arrowSize}px solid transparent;
        z-index: 1;
      }
      .kpv2-preview-popover-container::after {
        content: "";
        position: absolute;
        width: 0;
        height: 0;
        left: var(--arrow-left, 50%);
        margin-left: -${arrowSize - 1}px;
        border: ${arrowSize - 1}px solid transparent;
        z-index: 2;
      }
      .kpv2-preview-popover-container[data-placement="bottom"]::before {
        top: -${arrowSize * 2}px;
        border-bottom-color: rgb(43, 43, 43);
      }
      .kpv2-preview-popover-container[data-placement="bottom"]::after {
        top: -${arrowSize * 2 - 1}px;
        border-bottom-color: rgb(18, 18, 18);
      }
      .kpv2-preview-popover-container[data-placement="top"]::before {
        bottom: -${arrowSize * 2}px;
        border-top-color: rgb(43, 43, 43);
      }
      .kpv2-preview-popover-container[data-placement="top"]::after {
        bottom: -${arrowSize * 2 - 1}px;
        border-top-color: rgb(11, 11, 11);
      }
    `;
      document.head.appendChild(arrowStyle);
      this._popoverArrowStyle = arrowStyle;
      let iframeRef = null;
      this.popoverBridgeReady = false;
      const iframeViewport = this.createElement("div", {
        className: "kpv2-preview-popover-viewport",
        style: `
        flex: 1;
        min-height: 0;
        display: flex;
        align-items: stretch;
        justify-content: stretch;
        overflow: hidden;
        background: #0a0a0a;
      `
      });
      const { actions: previewOpenActions } = createPreviewOpenActionButtons({
        getUrl: () => url,
        afterOpen: () => requestClosePopover(),
        afterOpenNewTab: () => requestClosePopover()
      });
      const applyViewportMode = async (mode, opts2 = {}) => {
        const next = mode === "desktop" ? "desktop" : "mobile";
        const prev = viewportMode;
        const shouldReload = opts2.reload !== false && prev !== next && !!iframeRef;
        viewportMode = next;
        try {
          this.popoverContainer?.setAttribute("data-kp-preview-viewport", viewportMode);
        } catch {
        }
        if (iframeRef) {
          iframeRef.style.width = "100%";
          iframeRef.style.maxWidth = "none";
          iframeRef.style.flex = "1 1 auto";
          iframeRef.style.alignSelf = "stretch";
          iframeRef.style.height = "100%";
        }
        iframeViewport.style.justifyContent = "stretch";
        if (viewportMode === "mobile") {
          await this._setPreviewMobileUa(true);
        } else {
          await this._setPreviewMobileUa(false);
        }
        if (shouldReload && iframeRef) {
          try {
            iframeViewport.style.display = "";
            errorContainer.style.display = "none";
          } catch {
          }
          try {
            armLoadTimeout();
          } catch {
          }
          try {
            iframeRef.src = "about:blank";
            iframeRef.src = url;
            this.popoverIframeWindow = iframeRef.contentWindow || null;
          } catch {
            try {
              iframeRef.src = url;
              this.popoverIframeWindow = iframeRef.contentWindow || null;
            } catch {
            }
          }
        }
      };
      const viewportModeControl = createSegmentedControl({
        value: viewportMode,
        ariaLabel: "Preview viewport mode",
        className: "kpv2-preview-viewport-mode",
        options: [
          {
            value: "mobile",
            label: "Mobile",
            title: "Mobile site (mobile User-Agent). Remembered for this website.",
            ariaLabel: "Mobile preview"
          },
          {
            value: "desktop",
            label: "Desktop",
            title: "Desktop site (default). Remembered for this website.",
            ariaLabel: "Desktop preview"
          }
        ],
        onChange: (value) => {
          void (async () => {
            await applyViewportMode(value, { reload: true });
            try {
              await this._setPreviewViewportModeForHost(previewHost, value === "mobile" ? "mobile" : "desktop");
            } catch {
            }
          })();
        }
      });
      const titlebarApi = createPopoverTitlebar({
        title: titleText,
        variant: "preview",
        draggable: true,
        titleAttr: "Drag to move",
        showClose: true,
        onClose: requestClosePopover,
        closeTitle: "Close (Esc)",
        hint: "Press Esc / E to hide",
        actions: [viewportModeControl.root, previewOpenActions],
        className: "kpv2-preview-popover-titlebar"
      });
      const header = titlebarApi.titlebar;
      const closeButton = titlebarApi.closeButton;
      this.popoverCloseButton = closeButton;
      ensureTopMouseTracking();
      let dragState = null;
      const DRAG_MOVE_THRESHOLD_PX = 3;
      const clampPopoverPosition = (leftPx, topPx) => {
        const el2 = this.popoverContainer;
        if (!el2) return { left: leftPx, top: topPx };
        const w = el2.offsetWidth || popoverWidth;
        const h = el2.offsetHeight || popoverHeight;
        const maxLeft = Math.max(margin, window.innerWidth - w - margin);
        const maxTop = Math.max(margin, window.innerHeight - h - margin);
        return {
          left: Math.max(margin, Math.min(leftPx, maxLeft)),
          top: Math.max(margin, Math.min(topPx, maxTop))
        };
      };
      const hidePreviewArrow = () => {
        const el2 = this.popoverContainer;
        if (!el2 || el2.dataset.kpDragged === "1") return;
        el2.dataset.kpDragged = "1";
        el2.removeAttribute("data-placement");
      };
      const onDragPointerMove = (e) => {
        if (!dragState || !this.popoverContainer) return;
        const dx = e.clientX - dragState.startX;
        const dy = e.clientY - dragState.startY;
        if (!dragState.moved) {
          if (Math.abs(dx) < DRAG_MOVE_THRESHOLD_PX && Math.abs(dy) < DRAG_MOVE_THRESHOLD_PX) {
            return;
          }
          dragState.moved = true;
          hidePreviewArrow();
          header.style.cursor = "grabbing";
          if (this.popoverIframeElement) {
            this.popoverIframeElement.style.pointerEvents = "none";
          }
        }
        const next = clampPopoverPosition(dragState.originLeft + dx, dragState.originTop + dy);
        this.popoverContainer.style.left = `${next.left}px`;
        this.popoverContainer.style.top = `${next.top}px`;
      };
      const endDrag = (e) => {
        if (!dragState) return;
        const pointerId = dragState.pointerId;
        dragState = null;
        header.style.cursor = "grab";
        if (this.popoverIframeElement) {
          this.popoverIframeElement.style.pointerEvents = "";
        }
        try {
          if (e && typeof e.pointerId === "number") {
            header.releasePointerCapture(e.pointerId);
          } else if (typeof pointerId === "number") {
            header.releasePointerCapture(pointerId);
          }
        } catch {
        }
        document.removeEventListener("pointermove", onDragPointerMove, true);
        document.removeEventListener("pointerup", endDrag, true);
        document.removeEventListener("pointercancel", endDrag, true);
      };
      const onTitlebarPointerDown = (e) => {
        const interactive = titlebarApi.getInteractiveElements();
        for (const btn of interactive) {
          if (e.target === btn || btn.contains && btn.contains(e.target)) {
            return;
          }
        }
        if (e.pointerType === "mouse" && e.button !== 0) return;
        if (!this.popoverContainer) return;
        e.preventDefault();
        e.stopPropagation();
        const rect = this.popoverContainer.getBoundingClientRect();
        dragState = {
          startX: e.clientX,
          startY: e.clientY,
          originLeft: rect.left,
          originTop: rect.top,
          pointerId: e.pointerId,
          moved: false
        };
        try {
          header.setPointerCapture(e.pointerId);
        } catch {
        }
        document.addEventListener("pointermove", onDragPointerMove, true);
        document.addEventListener("pointerup", endDrag, true);
        document.addEventListener("pointercancel", endDrag, true);
      };
      header.addEventListener("pointerdown", onTitlebarPointerDown);
      this._previewPopoverDragCleanup = () => {
        endDrag();
        try {
          header.removeEventListener("pointerdown", onTitlebarPointerDown);
        } catch {
        }
      };
      try {
        this._popoverResizeDispose?.();
      } catch {
      }
      try {
        const resizeApi = makePopoverResizable(this.popoverContainer, {
          minWidth: 280,
          minHeight: 200,
          margin,
          onResizeStart: () => {
            hidePreviewArrow();
          }
        });
        this._popoverResizeDispose = resizeApi?.dispose || null;
      } catch (e) {
        console.warn("[KeyPilot] Failed to make preview popover resizable:", e?.message || e);
        this._popoverResizeDispose = null;
      }
      const errorContainer = this.createElement("div", {
        className: "kpv2-popover-error",
        style: `
        flex: 1;
        display: none;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        padding: 20px;
        text-align: center;
        background: #f9f9f9;
      `
      });
      const previewErrorHost = this.createElement("div", {
        className: "kpv2-preview-popover-error-host",
        style: "display:contents;"
      });
      const previewErrorShadow = ensureOpenChromeShadow(previewErrorHost, { id: "link-preview-error" });
      const previewErrorMount = previewErrorShadow || previewErrorHost;
      const errorIcon = this.createElement("div", {
        style: `
        font-size: 32px;
        margin-bottom: 12px;
        color: #999;
      `
      });
      errorIcon.textContent = "\u{1F6AB}";
      errorContainer.appendChild(errorIcon);
      const errorTitle = this.createElement("div", {
        style: `
        font-size: 14px;
        font-weight: 600;
        color: #333;
        margin-bottom: 6px;
      `
      });
      errorTitle.textContent = "Cannot Display Page";
      errorContainer.appendChild(errorTitle);
      const errorMessage = this.createElement("div", {
        style: `
        font-size: 12px;
        color: #666;
        margin-bottom: 16px;
        max-width: 300px;
      `
      });
      errorMessage.textContent = "This website prevents embedding in iframes.";
      errorContainer.appendChild(errorMessage);
      const openInTabButton = this.createElement("button", {
        style: `
        background: #4CAF50;
        color: white;
        border: none;
        padding: 8px 16px;
        border-radius: 4px;
        cursor: pointer;
        font-size: 12px;
        font-weight: 500;
      `
      });
      openInTabButton.textContent = "Open in New Tab";
      openInTabButton.onclick = () => {
        window.open(url, "_blank");
        requestClosePopover();
      };
      errorContainer.appendChild(openInTabButton);
      const iframe = this.createElement("iframe", {
        tabindex: "0",
        style: `
        border: none;
        width: 100%;
        max-width: none;
        height: 100%;
        flex: 1 1 auto;
        align-self: stretch;
        background: #fff;
      `
      });
      iframeRef = iframe;
      this.popoverIframeElement = iframe;
      this.popoverIframeWindow = iframe.contentWindow || null;
      const sendBridgeInit = () => {
        try {
          iframe.contentWindow?.postMessage({
            type: "KP_POPOVER_BRIDGE_INIT",
            closeKeys
          }, "*");
        } catch {
        }
      };
      let loadTimeout = null;
      const armLoadTimeout = () => {
        if (loadTimeout) {
          try {
            clearTimeout(loadTimeout);
          } catch {
          }
        }
        loadTimeout = setTimeout(() => {
          console.log("[KeyPilot] Iframe load timeout - showing error as fallback");
          iframeViewport.style.display = "none";
          errorContainer.style.display = "flex";
        }, 3e4);
      };
      const clearLoadTimeout = () => {
        if (!loadTimeout) return;
        try {
          clearTimeout(loadTimeout);
        } catch {
        }
        loadTimeout = null;
      };
      iframe.onerror = () => {
        console.log("[KeyPilot] Iframe load error detected");
        clearLoadTimeout();
        iframeViewport.style.display = "none";
        errorContainer.style.display = "flex";
      };
      iframe.onload = () => {
        try {
          const srcAttr = iframe.getAttribute("src") || "";
          if (srcAttr === "about:blank" || iframe.src === "about:blank") {
            return;
          }
        } catch {
        }
        clearLoadTimeout();
        console.log("[KeyPilot] Iframe loaded successfully");
        sendBridgeInit();
      };
      iframeViewport.appendChild(iframe);
      previewChromeMount.appendChild(header);
      previewErrorMount.appendChild(errorContainer);
      this.popoverContainer.appendChild(previewChromeHost2);
      this.popoverContainer.appendChild(iframeViewport);
      this.popoverContainer.appendChild(previewErrorHost);
      this._isPreviewPopover = true;
      try {
        document.body.appendChild(this.popoverContainer);
      } catch (e) {
        console.error("[KeyPilot] Failed to mount preview popover:", e);
        this._isPreviewPopover = false;
        return;
      }
      void (async () => {
        try {
          await applyViewportMode(viewportMode, { reload: false });
        } catch (e) {
          console.warn("[KeyPilot] Failed to prepare preview viewport mode:", e?.message || e);
        }
        try {
          armLoadTimeout();
          iframe.src = url;
          this.popoverIframeWindow = iframe.contentWindow || null;
        } catch (e) {
          console.error("[KeyPilot] Failed to load preview URL:", e?.message || e);
          clearLoadTimeout();
          iframeViewport.style.display = "none";
          errorContainer.style.display = "flex";
        }
      })();
      this._popoverClickOutsideHandler = (e) => {
        if (this.popoverContainer && !this.popoverContainer.contains(e.target)) {
          console.log("[KeyPilot] Click outside preview popover, closing");
          requestClosePopover();
        }
      };
      setTimeout(() => {
        if (this.popoverContainer) {
          document.addEventListener("mousedown", this._popoverClickOutsideHandler, true);
        }
      }, 100);
      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 {
      }
      const handlePopoverKeyDown = (e) => {
        console.log("[KeyPilot] Preview popover key event:", e.key);
        if (e.key === "Escape" || closeKeys.includes(String(e.key))) {
          const ae = document.activeElement;
          const focusInIframe = ae === iframeRef || ae === iframe;
          if (focusInIframe) return;
          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 === "KP_POPOVER_BRIDGE_READY") {
          this.popoverBridgeReady = true;
          this._focusPopoverIframe(iframeRef);
          this._installPopoverHybridFocus({
            iframe: iframeRef,
            chromeEls: [
              header,
              closeButton,
              viewportModeControl.root,
              previewOpenActions
            ].filter(Boolean),
            focusChromeEl: closeButton || header
          });
          return;
        }
        if (data.type === "KP_POPOVER_REQUEST_CLOSE") {
          if (closeKeys.includes(String(data.key))) requestClosePopover();
        }
        if (data.type === "KP_POPOVER_LAUNCH_WALKTHROUGH") {
          requestClosePopover();
          try {
            const ob = window.__KeyPilotOnboarding;
            if (ob && typeof ob.resetTutorial === "function") {
              void ob.resetTutorial();
            }
          } catch {
          }
          return;
        }
        if (data.type === "KP_POPOVER_BRIDGE_KEYDOWN") {
          const k = String(data.key || "");
          if (k === "f" || k === "F") {
            clickCloseIfHovered();
          }
        }
      };
      window.addEventListener("message", this.popoverMessageHandler, true);
      try {
        (closeButton || header)?.focus?.();
      } catch (_e) {
        try {
          this.popoverContainer.focus();
        } catch (_e2) {
        }
      }
    }
    // =============================================================================
    // 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 + force 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._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'|null|string} strategy - null / 'auto' clears override
     */
    setShadowDebugPaintStrategy(strategy) {
      const s = strategy == null ? null : String(strategy).toUpperCase();
      if (s === "A" || s === "B" || s === "C") {
        this._shadowDebugPaintOverride = s;
      } 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) {
        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="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;">
        A = element outline \xB7 B = absolute ring in host \xB7 C = body fixed overlay
      </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") {
          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";
      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}${override ? `  (forced ${override})` : "  (auto)"}`;
      this._refreshShadowDebugHudButtons();
    }
  };

  // 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.RIPPLE}`,
      `.${CSS_CLASSES.FOCUS_PULSE}`,
      `.${CSS_CLASSES.FOCUS_FLASH}`,
      `.${CSS_CLASSES.FOCUS_DASH}`,
      `.${CSS_CLASSES.FOCUS_MARQUEE}`,
      `.${CSS_CLASSES.IMAGE_COPY_PULSE}`,
      `.${CSS_CLASSES.EDGE_JUMP_FADE}`,
      `.${CSS_CLASSES.VIEWPORT_MODAL_FRAME}`,
      `.${CSS_CLASSES.ESC_EXIT_LABEL}`,
      // 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-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 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="rgba(255,255,255,0.55)" 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.isEnabled = true;
      this.cursorOverridesEnabled = false;
      this._textHoverHintLabel = "Press F to select text field";
      this._textFocusHintLabel = "press Esc to exit";
      this._textHoverHintUri = buildTextInputHintDataUri(this._textHoverHintLabel);
      this._textFocusHintUri = buildTextInputHintDataUri(this._textFocusHintLabel);
    }
    /**
     * 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);
      }
      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 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;
        will-change: opacity, left, top, width, height;
      }
      
      @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: 0;
          border-color: ${COLORS.FLASH_GREEN};
          box-shadow:
            0 0 0 0 transparent,
            0 0 0 0 transparent;
        }
        10% {
          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: 3px;
        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: 3px;
        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;
      }

      /*
       * 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: 3px;
        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 ${COLORS.FOCUS_GREEN}; 
        box-shadow: 0 0 0 2px ${COLORS.GREEN_SHADOW}, 0 0 10px 2px ${COLORS.GREEN_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}; 
      }

      /* 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.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 + SVG hint labels.
         - Hover: orange outline only + SVG "Press F to select\u2026" (no background wash)
         - Focus background_tint: full orange wash + SVG "press Esc to exit"
         - Focus left_edge (default): pulsating left inset bar + Esc SVG
         Hint copy is an SVG background-image (upper-left) \u2014 not a DOM overlay. */
      .${CSS_CLASSES.TEXT_FOCUS_INPUT},
      .${CSS_CLASSES.TEXT_FOCUS_INPUT_PARENT} {
        color: var(--kpv2-text-input-color, rgba(55, 55, 55, 0.96)) !important;
        text-shadow: var(--kpv2-text-input-text-shadow, 1px 1px 0 rgba(255, 255, 255, 0.95)) !important;
      }

      /* 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: SVG hint only \u2014 no background wash (outline is separate). */
      .${CSS_CLASSES.TEXT_HOVER_INPUT} {
        background-image: var(--kpv2-text-hover-hint-image, none) !important;
        background-repeat: no-repeat !important;
        background-position: left 6px top 3px !important;
        background-size: auto 12px !important;
      }

      /* Focus (background tint style): wash + Esc SVG. */
      .${CSS_CLASSES.TEXT_FOCUS_INPUT}:not(.${CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE}) {
        background-color: var(--kpv2-text-input-focus-bg, rgba(255, 140, 0, 0.42)) !important;
        background-image: var(--kpv2-text-focus-hint-image, none) !important;
        background-repeat: no-repeat !important;
        background-position: left 6px top 3px !important;
        background-size: auto 12px !important;
      }

      /* Focus (default left-edge style): inset orange bar that pulses + Esc SVG. */
      .${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: var(--kpv2-text-focus-hint-image, none) !important;
        background-repeat: no-repeat !important;
        background-position: left calc(var(--kpv2-text-left-edge-width, 5px) + 6px) top 3px !important;
        background-size: auto 12px !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} {
        caret-color: var(--kpv2-text-input-color, rgba(55, 55, 55, 0.96)) !important;
      }

      /* Keep placeholders readable when we force text color on focus wash. */
      .${CSS_CLASSES.TEXT_FOCUS_INPUT}::placeholder {
        color: var(--kpv2-text-input-placeholder-color, rgba(55, 55, 55, 0.62)) !important;
        text-shadow: var(--kpv2-text-input-text-shadow, 1px 1px 0 rgba(255, 255, 255, 0.85)) !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) 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) 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;
      }

      /* Keep left-edge pulse when DOM-hover also puts a ring on the focused field. */
      .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 + SVG hint treatment inside shadow DOM */
      .${CSS_CLASSES.TEXT_FOCUS_INPUT},
      .${CSS_CLASSES.TEXT_FOCUS_INPUT_PARENT} {
        color: var(--kpv2-text-input-color, rgba(55, 55, 55, 0.96)) !important;
        text-shadow: var(--kpv2-text-input-text-shadow, 1px 1px 0 rgba(255, 255, 255, 0.95)) !important;
      }

      .${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: var(--kpv2-text-hover-hint-image, none) !important;
        background-repeat: no-repeat !important;
        background-position: left 6px top 3px !important;
        background-size: auto 12px !important;
      }

      .${CSS_CLASSES.TEXT_FOCUS_INPUT}:not(.${CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE}) {
        background-color: var(--kpv2-text-input-focus-bg, rgba(255, 140, 0, 0.42)) !important;
        background-image: var(--kpv2-text-focus-hint-image, none) !important;
        background-repeat: no-repeat !important;
        background-position: left 6px top 3px !important;
        background-size: auto 12px !important;
      }

      .${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: var(--kpv2-text-focus-hint-image, none) !important;
        background-repeat: no-repeat !important;
        background-position: left calc(var(--kpv2-text-left-edge-width, 5px) + 6px) top 3px !important;
        background-size: auto 12px !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} {
        caret-color: var(--kpv2-text-input-color, rgba(55, 55, 55, 0.96)) !important;
      }

      .${CSS_CLASSES.TEXT_FOCUS_INPUT}::placeholder {
        color: var(--kpv2-text-input-placeholder-color, rgba(55, 55, 55, 0.62)) !important;
        text-shadow: var(--kpv2-text-input-text-shadow, 1px 1px 0 rgba(255, 255, 255, 0.85)) !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) 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) 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;
      }

      /* Keep left-edge pulse when DOM-hover also puts a ring on the focused field. */
      .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 || root instanceof Document) {
        return true;
      }
      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 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.HIGHLIGHT_OVERLAY,
        CSS_CLASSES.HIGHLIGHT_SELECTION,
        CSS_CLASSES.TEXT_FOCUS_INPUT,
        CSS_CLASSES.TEXT_FOCUS_INPUT_PARENT,
        CSS_CLASSES.TEXT_FOCUS_LEFT_EDGE,
        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
     * extension/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;
        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-")) return true;
          const cl = n.classList;
          if (cl && cl.length) {
            for (const c of cl) {
              if (typeof c !== "string" || !c.startsWith("kpv2-")) continue;
              if (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?.hideFocusOverlay?.();
          } catch {
          }
        }
      }
      this.throttledScrollHandler();
      if (this.scrollTimeout) {
        clearTimeout(this.scrollTimeout);
      }
      this.scrollTimeout = setTimeout(() => {
        this.handleScrollEnd();
      }, 100);
    }
    handleScrollThrottled() {
      this.scrollMetrics.throttledCalls++;
      let currentState;
      try {
        currentState = this.stateManager.getState();
      } catch {
        return;
      }
      if (!currentState) return;
      const mode = currentState.mode;
      let didOverlayWork = false;
      if (mode === "highlight" && 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);
          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;
      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 type = element.type ? element.type.toLowerCase() : "text";
        const textInputTypes = [
          "text",
          "search",
          "url",
          "email",
          "tel",
          "password",
          "number"
        ];
        return textInputTypes.includes(type);
      }
      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(type, data) {
      const behaviorAnalyzer = this.predictiveCaching.behaviorAnalyzer;
      const interaction = {
        type,
        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, type = "info") {
      if (this.notificationSystem.notificationCallback) {
        this.notificationSystem.notificationCallback(message, type);
      } else {
        const logMethod = type === "error" ? "error" : type === "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(type, data) {
      if (!this.performanceMonitoringEnabled) {
        return;
      }
      const alert = {
        type,
        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();
  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__",
    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
    "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.7C46.9 265.2 45.8 253.7 45.8 242s1.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-32.8c2.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",
    // 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 "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"
  });
  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",
    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",
    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",
    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",
    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",
    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"
  });
  function faIconDataUri(pathD, fill = "black") {
    const safeFill = String(fill || "black").replace(/"/g, "");
    const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" 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 ? FA_SOLID_PATHS[iconName] : null;
    if (!pathD) return "";
    return faIconDataUri(pathD, opts.fill || "white");
  }
  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] = 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 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: normal;
  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: "cubellan", href: fontUrls.cubellan, type: "font/ttf" },
      { id: "ezarion", href: fontUrls.ezarion, type: "font/ttf" }
    ];
    for (const { id, href, type } 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 = type;
        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 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,
      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: 7px;

  /*
   * Low-profile key: nearly flat face, thin rim, soft ground shadow.
   * Reads more like a real chiclet key than a heavy 3D bevel.
   */
  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:
    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;
}

/* 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;
}

/* 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: pin to the keycap's upper-right, above the FA glyph. */
.${KEYBINDINGS_UI_ROOT_CLASS} .key > .kp-key-delete {
  position: absolute !important;
  top: 1px !important;
  right: 1px !important;
  left: auto !important;
  bottom: auto !important;
  width: 14px !important;
  height: 14px !important;
  min-width: 14px !important;
  min-height: 14px !important;
  margin: 0 !important;
  padding: 0 !important;
  border: none !important;
  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);
  z-index: 8;
  pointer-events: auto;
}
.${KEYBINDINGS_UI_ROOT_CLASS} .key:hover > .kp-key-delete,
.${KEYBINDINGS_UI_ROOT_CLASS} .key:focus-within > .kp-key-delete {
  display: flex !important;
}
.${KEYBINDINGS_UI_ROOT_CLASS} .key > .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:
    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 {
  font-weight: 700;
  margin: 0 0 3px 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-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));
}

.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-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-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);
}

/* 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: "One key sends a modifier chord (e.g. Ctrl+C, Win+R, Ctrl+Shift+Esc).",
      keyboardClass: "key-orange"
    }),
    Object.freeze({
      id: (
        /** @type {const} */
        "burst"
      ),
      label: "Burst Keys",
      description: "One press types a short sequence (e.g. a\u2192b\u2192c or Ctrl+C \u2192 Ctrl+V \u2192 Enter).",
      keyboardClass: "key-purple"
    }),
    Object.freeze({
      id: (
        /** @type {const} */
        "roundRobin"
      ),
      label: "Round Robin",
      description: "Each press advances a cycle of strokes (A, then B, then C, then A\u2026).",
      keyboardClass: "key-scroll"
    }),
    Object.freeze({
      id: (
        /** @type {const} */
        "continuous"
      ),
      label: "Continue / Continuous",
      description: "Press to start sending a stroke repeatedly; press again to stop.",
      keyboardClass: "key-highlight"
    }),
    Object.freeze({
      id: (
        /** @type {const} */
        "mouse"
      ),
      label: "Synthetic Mouse",
      description: "Left, middle, or right click under the cursor.",
      keyboardClass: "key-activate"
    }),
    Object.freeze({
      id: (
        /** @type {const} */
        "key"
      ),
      label: "Normal Key",
      description: "Remap this slot to another key (e.g. F sends 1).",
      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_ATTR = "data-kp-procedure-result-style";
  var ROOT_CLASS = "kp-procedure-result";
  var _root = null;
  var _dragApi = null;
  function ensureStyles(root) {
    injectChromeStyles(root, { attr: STYLE_ATTR, 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;
}
.${ROOT_CLASS}__title {
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.02em;
  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" });
      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/modules/action-result-delivery.js
  var ACTION_RESULT_DESTINATIONS = Object.freeze({
    CLIPBOARD: "clipboard",
    POPOVER: "popover",
    BOTH: "both",
    MODIFY_PAGE: "modifyPage",
    MEDIA_LIBRARY: "mediaLibrary",
    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 (coming soon)",
    [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);
    return {
      clipboard: d === ACTION_RESULT_DESTINATIONS.CLIPBOARD || d === ACTION_RESULT_DESTINATIONS.BOTH,
      popover: d === ACTION_RESULT_DESTINATIONS.POPOVER || d === ACTION_RESULT_DESTINATIONS.BOTH,
      modifyPage: d === ACTION_RESULT_DESTINATIONS.MODIFY_PAGE
    };
  }
  async function deliverActionResult(kp, opts = {}) {
    const text = String(opts.text ?? "");
    const flags = destinationFlags(opts.destination);
    const out = { clipboard: false, popover: false, modifyPage: false };
    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 {
          kp.showFlashNotification(
            opts.successMessage || "Copied to clipboard",
            COLORS.NOTIFICATION_SUCCESS
          );
        } 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/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
  ]);
  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
    }),
    // 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" })
  });
  var FIXED_KEY_FUNCTION_IDS = Object.freeze([
    "SEND_TEXT_TO_AI",
    "RECTANGLE_HIGHLIGHT",
    "HIGHLIGHT",
    "COPY_HOVERED_IMAGE",
    "COPY_HOVERED_URL",
    "PAGE_TOP",
    "PAGE_BOTTOM"
  ]);
  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 BUILTIN_FUNCTION_PARAMETER_OVERRIDES = Object.freeze({
    PAGE_TOP: EDGE_SCROLL_PARAMETERS,
    PAGE_BOTTOM: EDGE_SCROLL_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)
    ])
  });
  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 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) continue;
      out[functionId] = Object.freeze({
        id: functionId,
        label: kindDef.label,
        description: kindDef.description,
        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: "Types configured text into the focused field each time the key is pressed. Assign this Function to multiple keys, each with its own text.",
    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"
      })
    ])
  });
  function buildBuiltinActionFunctionDefs() {
    const out = {};
    for (const [id, def] of Object.entries(KEYBINDING_ACTION_DEFS)) {
      out[id] = Object.freeze({
        id,
        label: def.label,
        description: def.description,
        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)
        // 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: "Reads the word, sentence, paragraph, or hyperlink under the cursor 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: "Reads the current highlight/selection (set up beforehand) and copies it to the clipboard.",
        handler: "handleGetTextRangeKey",
        category: DATA_FUNCTION_CATEGORY,
        dataSource: "textRange",
        dataKind: "text",
        destinations: Object.freeze([ACTION_RESULT_DESTINATIONS.CLIPBOARD])
      }),
      GET_MEDIA_AT_CURSOR: Object.freeze({
        id: "GET_MEDIA_AT_CURSOR",
        label: "Get Media At Cursor",
        description: "Reads the image, video, or audio under the cursor and copies it (or its URL) 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: "Shows a definition popover for the word under the cursor. No setup required.",
        handler: "handleLookupWordKey",
        category: LOOKUP_FUNCTION_CATEGORY,
        dataSource: "underCursor",
        dataKind: "text",
        destinations: Object.freeze([ACTION_RESULT_DESTINATIONS.POPOVER])
      }),
      TRANSLATE: Object.freeze({
        id: "TRANSLATE",
        label: "Translate",
        description: "Translates the highlighted text (or the word/sentence/paragraph under the cursor if nothing is highlighted).",
        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: "Shows the configured text in a popover \u2014 a display primitive for composing into future macro steps.",
        handler: "handleShowPopoverKey",
        category: DISPLAY_FUNCTION_CATEGORY,
        dataSource: "none",
        destinations: Object.freeze([ACTION_RESULT_DESTINATIONS.POPOVER]),
        parameters: Object.freeze([Object.freeze({
          id: "content",
          label: "Content",
          type: "string",
          multiline: true,
          defaultValue: "",
          placeholder: "Text to show in the popover\u2026"
        })])
      }),
      ADD_URL_TO_MEDIA_LIBRARY: Object.freeze({
        id: "ADD_URL_TO_MEDIA_LIBRARY",
        label: "Add URL to Media Library",
        description: "Stores the hyperlink under the cursor itself (its href) \u2014 does not download anything. Media Library is not built yet.",
        handler: "handleMediaLibraryNotAvailableKey",
        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: "Fetches the resource the hyperlink under the cursor points to (e.g. a .pdf/.mp3/.mp4) to store it. Media Library is not built yet.",
        handler: "handleMediaLibraryNotAvailableKey",
        category: MEDIA_LIBRARY_FUNCTION_CATEGORY,
        dataSource: "urlFetch",
        dataKind: "file",
        destinations: Object.freeze([ACTION_RESULT_DESTINATIONS.MEDIA_LIBRARY])
      })
    };
  }
  var FUNCTION_LIBRARY = Object.freeze({
    ...buildBuiltinActionFunctionDefs(),
    ...buildKeystrokeFunctionDefs(),
    [TYPE_CHARACTERS_FUNCTION_DEF.id]: TYPE_CHARACTERS_FUNCTION_DEF,
    ...buildDataAcquisitionFunctionDefs()
  });
  var FUNCTION_CATEGORY_ORDER = Object.freeze([
    "Navigation",
    "Tab Control",
    "Begin URL",
    "Get Page Data",
    "Scroll",
    "Select",
    "Clipboard",
    TEXT_FUNCTION_CATEGORY,
    KEYSTROKE_FUNCTION_CATEGORY,
    DATA_FUNCTION_CATEGORY,
    LOOKUP_FUNCTION_CATEGORY,
    TRANSLATE_FUNCTION_CATEGORY,
    DISPLAY_FUNCTION_CATEGORY,
    MEDIA_LIBRARY_FUNCTION_CATEGORY,
    "AI",
    "KeyPilot",
    "Tools",
    "System",
    "Other"
  ]);
  var FUNCTION_LIBRARY_ITEM_ORDER = Object.freeze({
    // Navigation
    ACTIVATE: 10,
    ACTIVATE_NEW_TAB: 20,
    ACTIVATE_NEW_TAB_BACKGROUND: 30,
    PREVIEW_LINK_POPOVER: 40,
    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
    LAUNCHER: 160,
    OMNIBOX: 170,
    // Get Page Data
    COPY_HOVERED_IMAGE: 200,
    COPY_HOVERED_URL: 202,
    PAGE_MEDIA: 205,
    RECTANGLE_HIGHLIGHT: 210,
    HIGHLIGHT: 220,
    // 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 isFunctionInstantiable(functionId) {
    const def = getFunctionDef(functionId);
    return !!(def?.parameters && def.parameters.length > 0);
  }
  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;
    }
    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.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;
  }
  function slotLabelFromBinding(binding) {
    const s = String(binding?.displayKey || binding?.keyLabel || "").trim();
    if (!s) return "";
    if (s.length === 1) return normalizeSlotLabel(s);
    if (s.includes("/")) {
      const first = s.split("/")[0];
      if (first && first.trim().length === 1) return normalizeSlotLabel(first.trim());
    }
    return "";
  }
  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 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 kb = buildKeybindingsForLayout(baseId);
    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/ui/key-action-settings.js
  var MODE_PARAMETER_ID = "mode";
  var DESTINATION_PARAMETER_ID = "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 && p.id !== DESTINATION_PARAMETER_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};
}
.kp-action-config-panel__title {
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.02em;
}
.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" });
        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;
      }
      for (const param of params) {
        const row = document.createElement("div");
        row.className = "kp-action-config-panel__row";
        const label = document.createElement("label");
        label.className = "kp-action-config-panel__label";
        label.textContent = param.label || param.id;
        row.appendChild(label);
        const currentVal = storedParams[param.id] !== void 0 ? storedParams[param.id] : param.defaultValue;
        let control;
        if (param.type === "boolean") {
          control = document.createElement("input");
          control.type = "checkbox";
          control.className = "kp-action-config-panel__control";
          control.checked = !!currentVal;
          control.addEventListener("change", async () => {
            const next = await setActionParameter(actionId, param.id, !!control.checked);
            try {
              this.onSettingsChanged?.(next);
            } catch {
            }
          });
        } else if (param.type === "enum" && Array.isArray(param.options)) {
          control = document.createElement("select");
          control.className = "kp-action-config-panel__control";
          for (const opt of param.options) {
            const o = document.createElement("option");
            o.value = opt.id;
            o.textContent = opt.label;
            if (opt.id === currentVal) o.selected = true;
            control.appendChild(o);
          }
          control.addEventListener("change", async () => {
            const next = await setActionParameter(actionId, param.id, control.value);
            try {
              this.onSettingsChanged?.(next);
            } catch {
            }
          });
        } else if (param.type === "number") {
          control = document.createElement("input");
          control.type = "number";
          control.className = "kp-action-config-panel__control";
          if (param.min != null) control.min = String(param.min);
          if (param.max != null) control.max = String(param.max);
          if (param.step != null) control.step = String(param.step);
          control.value = currentVal != null ? String(currentVal) : "";
          control.addEventListener("change", async () => {
            const n = Number(control.value);
            const next = await setActionParameter(actionId, param.id, Number.isFinite(n) ? n : param.defaultValue);
            try {
              this.onSettingsChanged?.(next);
            } catch {
            }
          });
        } else if (param.multiline) {
          control = document.createElement("textarea");
          control.className = "kp-action-config-panel__control";
          control.setAttribute("data-multiline", "true");
          control.rows = 3;
          if (param.placeholder) control.placeholder = String(param.placeholder);
          control.value = currentVal != null ? String(currentVal) : "";
          control.addEventListener("change", async () => {
            const next = await setActionParameter(actionId, param.id, control.value);
            try {
              this.onSettingsChanged?.(next);
            } catch {
            }
          });
        } else {
          control = document.createElement("input");
          control.type = "text";
          control.className = "kp-action-config-panel__control";
          if (param.placeholder) control.placeholder = String(param.placeholder);
          control.value = currentVal != null ? String(currentVal) : "";
          control.addEventListener("change", async () => {
            const next = await setActionParameter(actionId, param.id, control.value);
            try {
              this.onSettingsChanged?.(next);
            } catch {
            }
          });
        }
        row.appendChild(control);
        body.appendChild(row);
      }
    }
    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
  var _activePopoverContext = null;
  var _pinnedActionId = null;
  var _pinnedKeyEl = null;
  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"),
        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 ? true : 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 = `
      <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"></div>
          <div class="kp-popover-keys"></div>
        </div>
      </div>
      <p class="kp-popover-desc"></p>
      <div class="kp-popover-settings" hidden></div>
    `;
      doc.body.appendChild(pop);
    } 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 = `
          <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"></div>
              <div class="kp-popover-keys"></div>
            </div>
          </div>
          <p class="kp-popover-desc"></p>
          <div class="kp-popover-settings" hidden></div>
        `;
        } 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 {
        }
      }
    }
    try {
      if (supportsPopoverApi(pop)) {
        pop.popover = "manual";
      }
    } catch {
      try {
        pop.setAttribute("popover", "manual");
      } catch {
      }
    }
    try {
      pop.hidden = true;
    } catch {
    }
    try {
      pop.removeAttribute("data-kp-popover-open");
    } catch {
    }
    return pop;
  }
  function hidePopover(pop, opts = {}) {
    if (!pop) return;
    if (opts.clearPinned !== false) {
      _pinnedActionId = null;
      _pinnedKeyEl = null;
      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 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 (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 {
        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`);
  }
  async function renderPopoverSettings({ doc, pop, targetEl, binding, actionId }) {
    const host = pop.querySelector(".kp-popover-settings");
    if (!host) return;
    const hasModes = actionHasModes(actionId);
    const hasDestination = actionHasDestination(actionId);
    const hasParams = actionHasParameters(actionId);
    if (!hasModes && !hasDestination && !hasParams) {
      host.hidden = true;
      host.replaceChildren();
      return;
    }
    host.hidden = false;
    host.replaceChildren();
    let builtinAction = null;
    try {
      builtinAction = await getOrCreateBuiltinFunctionUserAction(actionId);
    } catch {
      builtinAction = null;
    }
    if (hasModes) {
      const def = getActionSettingsDef(actionId);
      const currentMode = getActionMode(builtinAction?.parameters, actionId);
      const modeWrap = doc.createElement("div");
      modeWrap.className = "kp-popover-mode-switch";
      modeWrap.setAttribute("role", "group");
      const modeParam = (def?.parameters || []).find((p) => p && p.id === "mode");
      modeWrap.setAttribute("aria-label", modeParam?.label || "Selection mode");
      for (const mode of def.modes) {
        const btn = doc.createElement("button");
        btn.type = "button";
        btn.className = "kp-popover-mode-btn";
        btn.dataset.modeId = mode.id;
        btn.textContent = mode.label;
        btn.setAttribute("aria-pressed", mode.id === currentMode ? "true" : "false");
        btn.addEventListener("click", async (e) => {
          e.preventDefault();
          e.stopPropagation();
          try {
            await setActionMode(actionId, mode.id);
            modeWrap.querySelectorAll(".kp-popover-mode-btn").forEach((el2) => {
              el2.setAttribute("aria-pressed", el2.dataset.modeId === mode.id ? "true" : "false");
            });
          } catch (err) {
            console.warn("[KeyPilot] Failed to set action mode:", err);
          }
        });
        modeWrap.appendChild(btn);
      }
      host.appendChild(modeWrap);
    }
    if (hasDestination) {
      const destDef = getActionDestinationDef(actionId);
      if (destDef) {
        const currentDest = getActionParameter(builtinAction?.parameters, actionId, "destination");
        const destWrap = doc.createElement("div");
        destWrap.className = "kp-popover-mode-switch";
        destWrap.setAttribute("role", "group");
        destWrap.setAttribute("aria-label", destDef.label || "Destination");
        for (const opt of destDef.options || []) {
          const btn = doc.createElement("button");
          btn.type = "button";
          btn.className = "kp-popover-mode-btn";
          btn.dataset.destinationId = opt.id;
          btn.textContent = opt.label;
          btn.setAttribute("aria-pressed", opt.id === currentDest ? "true" : "false");
          btn.addEventListener("click", async (e) => {
            e.preventDefault();
            e.stopPropagation();
            try {
              await setActionParameter(actionId, "destination", opt.id);
              destWrap.querySelectorAll(".kp-popover-mode-btn").forEach((el2) => {
                el2.setAttribute("aria-pressed", el2.dataset.destinationId === opt.id ? "true" : "false");
              });
            } catch (err) {
              console.warn("[KeyPilot] Failed to set action destination:", err);
            }
          });
          destWrap.appendChild(btn);
        }
        host.appendChild(destWrap);
      }
    }
    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);
    }
  }
  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 = keybindings[actionId];
    if (!binding) return false;
    _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 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 = keybindings && keybindings[actionId];
      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();
      showForKeyEl(e.currentTarget, { 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 (pop.contains(t)) 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/keyboard-layout-config-panel.js
  var KP_LAYOUT_ITEM_MIME = "application/x-kp-layout-item";
  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_INSPECTOR_WIDTH_PX = 280;
  var CONFIG_STYLE_ATTR = "data-kp-layout-config-panel-style";
  var CONFIG_STYLE_VERSION = "v18";
  var CONFIG_ICON_SPRITE_ATTR = "data-kp-layout-config-icons";
  var CONFIG_PLACE_ARROW_STYLE_ATTR = "data-kp-layout-place-arrow-style";
  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-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"],
    ["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-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",
    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",
    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",
    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",
    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: "If condition, else skip" }),
    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(["truthy", "falsy", "eq", "neq", "gt", "lt"]);
  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._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._legendEl = null;
      this._libViewSegEl = null;
      this._addStepSelect = null;
      this._addStepDelayInput = null;
      this._libTabsEl = null;
      this._fnCategorySelect = null;
      this._currentBadge = null;
      this._refToggleBtn = null;
      this._showNumRowToggle = null;
      this._dragDispose = null;
      this._resizeDispose = null;
      this._positionHydrated = false;
      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._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._syncKeyboardReferenceToggle();
      this._setVisible(true);
      this._emitChange();
    }
    hide() {
      this._cancelPlaceMode();
      this._stopChordCapture();
      this._setVisible(false);
    }
    /**
     * Persist the active user layout (if any), then close Config and exit edit mode.
     */
    async _saveAndClose() {
      try {
        await this._persistUserLayout();
      } catch {
      }
      this.hide();
      try {
        this._onClose?.();
      } catch {
      }
    }
    cleanup() {
      this._cancelPlaceMode();
      this._stopChordCapture();
      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._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();
    }
    _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"),
          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: ${NCT_DARK_UI_FONT};
  font-size: 12px;
  line-height: 1.35;
  color: ${c.fg};
  /* Lightened edit-mode panel chrome (Config only opens while editing). */
  background-color: #2e2e2e !important;
}
.kp-layout-config-panel .kp-cfg-ico {
  width: 12px;
  height: 12px;
  flex: 0 0 auto;
  fill: 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: ${NCT_DARK_UI_TITLEBAR_BORDER_BOTTOM};
  box-shadow: ${NCT_DARK_UI_TITLEBAR_BOX_SHADOW};
  /* Lightened edit-mode titlebar + same steel hatch as Keyboard Reference. */
  background-image:
    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, #646464 0%, #4a4a4a 45%, #383838 100%);
  flex: 0 0 auto;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}
.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: 700;
  letter-spacing: 0.02em;
  color: ${c.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-save-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 22px;
  min-height: 22px;
  padding: 0 8px;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  border: ${NCT_DARK_UI_BTN_LIT_BORDER};
  background: ${NCT_DARK_UI_BTN_LIT_GRADIENT};
  color: ${NCT_DARK_UI_SELECTED_TEXT};
  cursor: pointer;
  font-size: 11px;
  font-weight: 600;
  font-family: inherit;
  line-height: 20px;
  white-space: nowrap;
  flex: 0 0 auto;
  box-shadow: inset 0 1px 0 rgba(200,220,240,0.18);
}
.kp-layout-config-panel .kp-cfg-save-close:hover {
  filter: brightness(1.08);
}
.kp-layout-config-panel .kp-cfg-close {
  width: 22px;
  height: 22px;
  min-width: 22px;
  min-height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  border: none;
  background: transparent;
  color: ${c.fgDim};
  cursor: pointer;
  padding: 0;
  margin: 0;
  flex: 0 0 auto;
  box-shadow: ${NCT_DARK_UI_ICON_BUTTON_OUTLINE};
}
.kp-layout-config-panel .kp-cfg-close:hover {
  color: ${c.fg};
  background: ${NCT_DARK_UI_HOVER_TINT};
}
.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;
  /* Lightened edit-mode body + same steel hatch as Keyboard Reference plate. */
  background-color: #1a1c20;
  background-image:
    repeating-linear-gradient(
      -45deg,
      rgba(180, 200, 220, 0.08) 0px,
      rgba(180, 200, 220, 0.08) 1px,
      transparent 1px,
      transparent 7px
    );
}
.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: 1px solid #1a2430;
  border-radius: 0;
  background:
    linear-gradient(180deg, rgba(55, 85, 120, 0.18), rgba(30, 45, 70, 0.08)),
    #1a1e24;
  box-shadow:
    0 0 0 1px ${c.panelEdge} inset,
    inset 0 -1px 0 rgba(90, 130, 170, 0.12);
  flex: 0 0 auto;
  overflow: visible;
}
.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: #7a92a8;
  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-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: 1px solid ${c.panelEdgeDark};
  border-radius: ${NCT_DARK_UI_PANEL_RADIUS};
  background: #1a1a1a;
  overflow: hidden;
  box-shadow: 0 0 0 1px ${c.panelEdge} inset;
}
.kp-layout-config-panel .kp-cfg-pane-library {
  flex: 1 1 auto;
  background:
    linear-gradient(180deg, rgba(70, 95, 55, 0.14), rgba(40, 55, 35, 0.06)),
    linear-gradient(180deg, #161814, #1a1c18);
  border-color: #3a4a30;
  box-shadow:
    0 0 0 1px rgba(170, 210, 120, 0.28),
    0 0 14px rgba(120, 170, 80, 0.12),
    inset 0 1px 0 rgba(210, 240, 160, 0.18),
    inset 0 0 0 1px rgba(120, 150, 90, 0.16);
}
.kp-layout-config-panel .kp-cfg-pane-library .kp-cfg-pane-hdr {
  background: linear-gradient(180deg, #3a4a34, #2a3426);
  color: #c8d8b0;
  border-bottom-color: #141c12;
  box-shadow: 0 1px 0 rgba(150, 180, 110, 0.12);
}
.kp-layout-config-panel .kp-cfg-pane-library .kp-cfg-pane-title {
  color: #c8d8b0;
}
.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: 1px solid ${c.panelEdgeDark};
  background: linear-gradient(180deg, #383838, #2a2a2a);
  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: ${c.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-pane-library .kp-cfg-pane-scroll::-webkit-scrollbar-thumb {
  background: #4a5a40;
}
.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: #121410;
  border-color: #0a1008;
  color: #c8d8b0;
  box-shadow: inset 0 1px 0 #2a3024;
}
.kp-layout-config-panel .kp-cfg-fn-cat {
  width: auto;
  min-width: 120px;
  max-width: 180px;
  padding: 0 6px;
  height: 18px;
  font-size: 9px;
}
.kp-layout-config-panel .kp-cfg-lib-viewbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  padding: 2px 2px 8px;
  flex: 0 0 auto;
}
.kp-layout-config-panel .kp-cfg-lib-viewbar .kp-cfg-seg {
  flex: 0 0 auto;
}
.kp-layout-config-panel .kp-cfg-legend-mini {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 6px 10px;
  font-size: 8px;
  color: #6a7a58;
  opacity: 0.85;
}
.kp-layout-config-panel .kp-cfg-legend-mini span {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.kp-layout-config-panel .kp-cfg-legend-mini i {
  display: inline-block;
  width: 10px;
  height: 8px;
  border-radius: 1px;
  border: 1px solid #444;
  box-sizing: border-box;
}
.kp-layout-config-panel .kp-cfg-legend-mini .kp-cfg-sw-stock-fn {
  background: #2a2e32;
  border-color: #3a4550;
}
.kp-layout-config-panel .kp-cfg-legend-mini .kp-cfg-sw-user {
  background: #2a2e32;
  border-color: ${accentA(0.45)};
}
.kp-layout-config-panel .kp-cfg-legend-mini .kp-cfg-sw-stock-macro {
  background: #243430;
  border-left: 3px solid #5a9a8a;
}
.kp-layout-config-panel .kp-cfg-legend-mini .kp-cfg-sw-user-macro {
  background: #2c2434;
  border-left: 3px solid #9a7ab8;
}
.kp-layout-config-panel .kp-cfg-lib-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);
}
.kp-layout-config-panel .kp-cfg-lib-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 11px;
  table-layout: fixed;
}
.kp-layout-config-panel .kp-cfg-lib-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;
}
.kp-layout-config-panel .kp-cfg-lib-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;
}
.kp-layout-config-panel .kp-cfg-lib-table tr.kp-cfg-lib-row-group {
  background: rgba(255,255,255,0.03);
  cursor: pointer;
}
.kp-layout-config-panel .kp-cfg-lib-table tr.kp-cfg-lib-row-group td {
  font-weight: 700;
  font-size: 10px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #9a8aaa;
}
.kp-layout-config-panel .kp-cfg-lib-table tr.kp-cfg-lib-row-leaf:hover {
  background: rgba(74, 144, 200, 0.12);
}
.kp-layout-config-panel .kp-cfg-lib-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-cfg-lib-table tr.kp-cfg-lib-row-place {
  box-shadow: inset 2px 0 0 ${c.accent};
}
.kp-layout-config-panel .kp-cfg-lib-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;
}
.kp-layout-config-panel .kp-cfg-lib-label-cell {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.kp-layout-config-panel .kp-cfg-lib-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-cfg-lib-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: 6px !important;
  cursor: grab;
  padding: 0 !important;
  margin: 0;
  display: block;
  position: relative;
  overflow: hidden;
  box-sizing: border-box;
}
.kp-layout-config-panel [data-kp-layout-list].${KEYBINDINGS_UI_ROOT_CLASS} .kp-cfg-lib-table .key.kp-cfg-lib-key:active {
  cursor: grabbing;
}
.kp-layout-config-panel [data-kp-layout-list].${KEYBINDINGS_UI_ROOT_CLASS} .kp-cfg-lib-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-cfg-lib-table .key.kp-cfg-lib-key > .key-main,
.kp-layout-config-panel [data-kp-layout-list].${KEYBINDINGS_UI_ROOT_CLASS} .kp-cfg-lib-table .key.kp-cfg-lib-key > .key-label {
  display: none !important;
}
.kp-layout-config-panel [data-kp-layout-list].${KEYBINDINGS_UI_ROOT_CLASS} .kp-cfg-lib-table .key.kp-cfg-lib-key.kp-place-source {
  outline: 2px solid ${c.accent};
  outline-offset: 0;
}
.kp-layout-config-panel .kp-cfg-lib-label-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.kp-layout-config-panel .kp-cfg-lib-table .kp-cfg-card-actions {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 4px;
  justify-content: flex-end;
}
.kp-layout-config-panel .kp-cfg-lib-table col.kp-cfg-col-label { width: 34%; }
.kp-layout-config-panel .kp-cfg-lib-table col.kp-cfg-col-kind { width: 14%; }
.kp-layout-config-panel .kp-cfg-lib-table col.kp-cfg-col-summary { width: 22%; }
.kp-layout-config-panel .kp-cfg-lib-table col.kp-cfg-col-slots { width: 12%; }
.kp-layout-config-panel .kp-cfg-lib-table col.kp-cfg-col-actions { width: 18%; }
.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: 8px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #8a9a78;
  background: #121410;
}
.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: 1px solid #3a3a3a;
  background: linear-gradient(180deg, #2a2e32, #24282c);
  box-shadow: inset 0 1px 0 rgba(140,170,200,0.08);
  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 {
  background: linear-gradient(180deg, #2a2e32, #24282c);
  border-color: #3a4550;
}
.kp-layout-config-panel .kp-cfg-item.kp-cfg-variant-stock-macro {
  background: linear-gradient(180deg, #243430, #1c2824);
  border-color: #3a5a50;
  border-left: 3px solid #5a9a8a;
}
.kp-layout-config-panel .kp-cfg-item.kp-cfg-variant-user-macro {
  background: linear-gradient(180deg, #2c2434, #221c28);
  border-color: #4a3a5a;
  border-left: 3px solid #9a7ab8;
  box-shadow: inset 0 1px 0 rgba(154,122,184,0.15);
}
.kp-layout-config-panel .kp-cfg-item.kp-cfg-variant-macro-key {
  background: #282828;
  border-color: #3a3a3a;
}
.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: #d8c8e8;
  font-style: italic;
}
.kp-layout-config-panel .kp-cfg-item:hover {
  border-color: #4a5560;
  background: linear-gradient(180deg, #30363c, #282c32);
}
.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: ${NCT_DARK_UI_SELECTED_TINT};
}
.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: #d8e4f0;
  word-break: break-word;
}
.kp-layout-config-panel .kp-cfg-card-sub {
  font-size: 10px;
  opacity: 0.65;
  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 [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;
  border-radius: 7px !important;
}
.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-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;
}
.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;
  overflow: 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-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-split {
  position: relative;
  z-index: 2;
  flex: 0 0 8px;
  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: 2px;
  height: 36px;
}
.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: 36px;
  height: 2px;
}
.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;
  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-pane-create.kp-cfg-maximized {
  flex: 1 1 auto !important;
  min-height: 0;
}
.kp-layout-config-panel .kp-cfg-workspace.kp-cfg-create-maximized .kp-cfg-pane-library .kp-cfg-pane-scroll {
  display: none;
}
.kp-layout-config-panel .kp-cfg-workspace.kp-cfg-create-maximized .kp-cfg-pane-library {
  flex: 0 0 auto;
}
.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;
}
.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-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-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;
}
`.trim().replaceAll(".kp-layout-config-panel", ":host");
    }
    _applyProChrome(root) {
      if (!root || !root.style) return;
      const savedWidth = Number(this._panelPosition?.width);
      const savedHeight = Number(this._panelPosition?.height);
      Object.assign(root.style, {
        position: "fixed",
        width: `${Number.isFinite(savedWidth) && savedWidth > 0 ? savedWidth : CONFIG_PANEL_WIDTH_PX}px`,
        ...Number.isFinite(savedHeight) && savedHeight > 0 ? { height: `${savedHeight}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: NCT_DARK_UI_PANEL_BACKGROUND,
        color: NCT_DARK_UI_COLORS.fg,
        border: NCT_DARK_UI_PANEL_BORDER,
        borderRadius: NCT_DARK_UI_PANEL_RADIUS,
        boxShadow: NCT_DARK_UI_PANEL_BOX_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: 520
        });
        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 configuration");
      root.hidden = true;
      this._applyProChrome(root);
      const shadowRoot = ensureOpenChromeShadow(root, { id: "keyboard-layout-config" });
      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 Config";
      title.appendChild(titleText);
      const saveCloseBtn = doc.createElement("button");
      saveCloseBtn.type = "button";
      saveCloseBtn.className = "kp-cfg-save-close";
      saveCloseBtn.setAttribute("data-kp-cfg-save-close", "true");
      saveCloseBtn.setAttribute("aria-label", "Save and close layout config");
      saveCloseBtn.title = "Save layout changes and exit edit mode";
      saveCloseBtn.textContent = "Save and Close";
      saveCloseBtn.addEventListener("click", (e) => {
        try {
          e?.preventDefault?.();
          e?.stopPropagation?.();
        } catch {
        }
        void this._saveAndClose();
      }, true);
      const closeBtn = doc.createElement("button");
      closeBtn.type = "button";
      closeBtn.className = "kp-cfg-close";
      closeBtn.setAttribute("aria-label", "Close layout config");
      closeBtn.title = "Close";
      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(saveCloseBtn);
      header.appendChild(titleStart);
      header.appendChild(closeBtn);
      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 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 refToggleBtn = mkBtn("Keyboard Reference", "toggle-reference");
      refToggleBtn.title = "Show/hide the Keyboard Reference window (the place/drop target)";
      refToggleBtn.setAttribute("aria-pressed", "false");
      refToggleBtn.prepend(mkCfgIcon(doc, "kp-cfg-i-kb"));
      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 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(refToggleBtn);
      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);
      const hint = doc.createElement("div");
      hint.className = "kp-cfg-hint kp-cfg-strip-hint";
      hint.textContent = 'Click a keycap in the Actions Library, then click a Keyboard Reference key to place it. Functions marked "Needs modifier" must use "Bind modifier combo" instead \u2014 they run while a text field is focused.';
      strip.appendChild(layoutStrip);
      strip.appendChild(hint);
      strip.appendChild(importFile);
      const mainRow = doc.createElement("div");
      mainRow.className = "kp-cfg-main";
      const workspace = doc.createElement("div");
      workspace.className = "kp-cfg-workspace";
      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");
      fnCategorySelect.hidden = true;
      libraryHdr.appendChild(libraryTitle);
      libraryHdr.appendChild(searchWrap);
      libraryHdr.appendChild(libTabs);
      libraryHdr.appendChild(fnCategorySelect);
      const list = doc.createElement("div");
      list.setAttribute("data-kp-layout-list", "true");
      list.className = `${KEYBINDINGS_UI_ROOT_CLASS} kp-cfg-pane-scroll`;
      Object.assign(list.style, {
        display: "flex",
        flexDirection: "column",
        gap: "10px",
        flex: "1 1 auto",
        minHeight: "120px",
        overflow: "auto"
      });
      const viewBar = doc.createElement("div");
      viewBar.className = "kp-cfg-lib-viewbar";
      viewBar.setAttribute("aria-label", "Actions Library view");
      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 legendMini = doc.createElement("div");
      legendMini.className = "kp-cfg-legend-mini";
      legendMini.title = "Card / row color meaning";
      for (const [swClass, text] of [
        ["kp-cfg-sw-stock-fn", "Stock function"],
        ["kp-cfg-sw-user", "Configurable"],
        ["kp-cfg-sw-stock-macro", "Stock macro"],
        ["kp-cfg-sw-user-macro", "User macro"]
      ]) {
        const span = doc.createElement("span");
        const sw = doc.createElement("i");
        sw.className = swClass;
        span.appendChild(sw);
        span.appendChild(doc.createTextNode(text));
        legendMini.appendChild(span);
      }
      viewBar.appendChild(viewSeg);
      viewBar.appendChild(legendMini);
      libraryPane.appendChild(libraryHdr);
      libraryPane.appendChild(list);
      list.appendChild(viewBar);
      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");
      Object.assign(scriptPanel.style, { display: "flex", flexDirection: "column", gap: "8px", minHeight: "0" });
      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 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 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 = "Add Logic steps or Functions. Save commits the macro 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.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"));
      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._searchInput = search;
      this._currentBadge = currentBadge;
      this._refToggleBtn = refToggleBtn;
      this._showNumRowToggle = showNumRowToggle;
      this._mainRow = mainRow;
      this._libTabsEl = libTabs;
      this._fnCategorySelect = fnCategorySelect;
      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._legendEl = viewBar;
      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();
      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 config"], button[data-kp-cfg-save-close="true"], .kp-cfg-save-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: 460,
          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 {
          }
          this._applyTableExpandedFromSettings(settings?.actionsLibraryTableExpanded);
          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);
      refToggleBtn.addEventListener("click", () => {
        const next = !this._isKeyboardReferenceVisible();
        try {
          const kp = this._kp;
          if (typeof kp?.applyKeyboardHelpVisibility === "function") {
            kp.applyKeyboardHelpVisibility(next, { persist: true });
          } else if (next) {
            kp?.floatingKeyboardHelp?.show?.();
          } else {
            kp?.floatingKeyboardHelp?.hide?.();
          }
        } catch {
        }
        this._syncKeyboardReferenceToggle();
      }, 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._resetMacroDraft(`Macro ${(this._st.macros || []).length + 1}`);
        this._setCreateMode("script", { open: true });
        this._inspectorSelection = null;
        this._setInspectorOpen(true);
        this._renderInspector();
      }, 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);
    }
    _notify(message, type) {
      try {
        this._kp?.overlayManager?.showNotification?.(message, type);
      } 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";
    }
    async _persistUserLayout() {
      if (this._st.mode !== "user" || !this._st.userLayout) return;
      try {
        this._st.userLayout = await upsertUserKeyboardLayout(this._st.userLayout);
      } catch {
      }
    }
    _renderMacroKeyKindButtons() {
      const host = this._macroKeysActionsRow;
      if (!host) 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) {
      const frag = document.createDocumentFragment();
      const title = document.createElement("div");
      title.className = "kp-cfg-dock-title";
      title.textContent = String(text || "");
      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;
    }
    /** @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");
        tdKey.textContent = formatChordSlotKeyLabel(slot);
        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"));
      host.appendChild(this._dockRows([
        ["Id", functionId],
        ["Category", def ? getFunctionCategory(def.id) || "Other" : ""],
        ["About", String(def?.description || actionDef?.description || binding?.description || "")],
        [def?.worksWhileTyping ? "Note" : "", def?.worksWhileTyping ? "Requires a modifier combo" : ""]
      ]));
      host.appendChild(this._renderAssignmentTable({ type: "function", id: functionId }));
      const actions = [{
        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)) {
        actions.push({
          label: "Config\u2026",
          onClick: async () => {
            try {
              this._cancelPlaceMode();
            } catch {
            }
            try {
              const panel = getSharedKeyActionConfigPanel();
              await panel.open(def.id, { title: def.label, anchorRect: host.getBoundingClientRect() });
            } catch {
            }
          }
        });
      }
      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([
        ["Id", macro.id],
        ["Based on", macro.baseStockMacroId || ""],
        ["Steps", String(steps.length)]
      ]));
      host.appendChild(this._renderAssignmentTable({ type: "macro", id: macro.id }));
      host.appendChild(this._renderStepSummaryList(steps));
      host.appendChild(this._dockActions([
        {
          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);
          }
        },
        {
          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);
          }
        },
        stock ? null : {
          label: "Delete",
          onClick: () => {
            void this._deleteMacro(macro.id);
          }
        }
      ].filter(Boolean)));
    }
    /**
     * 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([
        ["Id", draft.id || "(unsaved)"],
        ["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 op = document.createElement("select");
        op.className = "kp-cfg-field";
        for (const name of MACRO_GATE_OPS) {
          const option = document.createElement("option");
          option.value = name;
          option.textContent = name;
          option.selected = String(step.op || "truthy") === name;
          op.appendChild(option);
        }
        op.addEventListener("change", () => patch({ op: op.value }), true);
        field("Operator", op);
        textInput("Prior-result key", step.leftKey, (leftKey) => patch({ leftKey }), "Optional object property");
        textInput("Compare to", step.right, (right) => patch({ right }), "Required for eq, neq, gt, lt");
        numberInput("On fail, skip steps", step.thenSkip, (thenSkip) => patch({ thenSkip }), { step: 1 });
      } 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);
      } 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);
        for (const param of def?.parameters || []) {
          const current = step.parameters?.[param.id] ?? param.defaultValue;
          let control;
          if (param.type === "boolean") {
            control = document.createElement("input");
            control.type = "checkbox";
            control.checked = !!current;
            control.addEventListener("change", () => patch({
              parameters: { ...step.parameters || {}, [param.id]: control.checked }
            }), true);
          } else if (param.type === "enum" && Array.isArray(param.options)) {
            control = document.createElement("select");
            control.className = "kp-cfg-field";
            for (const optionDef of param.options) {
              const option = document.createElement("option");
              option.value = optionDef.id;
              option.textContent = optionDef.label;
              option.selected = optionDef.id === current;
              control.appendChild(option);
            }
            control.addEventListener("change", () => patch({
              parameters: { ...step.parameters || {}, [param.id]: control.value }
            }), true);
          } else if (param.multiline) {
            control = document.createElement("textarea");
            control.className = "kp-cfg-field";
            control.rows = 3;
            control.value = current == null ? "" : String(current);
            control.addEventListener("change", () => patch({
              parameters: { ...step.parameters || {}, [param.id]: control.value }
            }), true);
          } else {
            control = document.createElement("input");
            control.type = param.type === "number" ? "number" : "text";
            control.className = "kp-cfg-field";
            if (param.type === "number") {
              if (param.min != null) control.min = String(param.min);
              if (param.max != null) control.max = String(param.max);
              if (param.step != null) control.step = String(param.step);
            }
            control.value = current == null ? "" : String(current);
            control.addEventListener("change", () => {
              const value = param.type === "number" ? Number(control.value) : control.value;
              patch({ parameters: { ...step.parameters || {}, [param.id]: value } });
            }, true);
          }
          if (param.placeholder) control.placeholder = String(param.placeholder);
          field(param.label || param.id, control);
        }
      }
      return wrap;
    }
    /**
     * @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 || {} } };
      host.appendChild(this._dockTitle(String(macroKey.label || macroKey.kind || "Macro Key"), "Macro Key"));
      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);
    }
    /**
     * 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"
      ));
      host.appendChild(this._renderAssignmentTable({ type: "function", id: instance.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));
      }
      for (const param of def.parameters || []) {
        const row = document.createElement("div");
        row.style.cssText = "display:flex;flex-direction:column;gap:4px;";
        const label = document.createElement("div");
        label.className = "kp-mk-field-label";
        label.textContent = param.label || param.id;
        row.appendChild(label);
        const currentVal = draft.parameters[param.id] !== void 0 ? draft.parameters[param.id] : param.defaultValue;
        let control;
        if (param.type === "boolean") {
          control = document.createElement("input");
          control.type = "checkbox";
          control.checked = !!currentVal;
          control.addEventListener("change", () => {
            draft.parameters[param.id] = !!control.checked;
          }, true);
        } else if (param.type === "enum" && Array.isArray(param.options)) {
          control = document.createElement("select");
          control.className = "kp-cfg-field";
          for (const opt of param.options) {
            const o = document.createElement("option");
            o.value = opt.id;
            o.textContent = opt.label;
            if (opt.id === currentVal) o.selected = true;
            control.appendChild(o);
          }
          control.addEventListener("change", () => {
            draft.parameters[param.id] = control.value;
          }, true);
        } else if (param.type === "number") {
          control = document.createElement("input");
          control.type = "number";
          control.className = "kp-cfg-field";
          if (param.min != null) control.min = String(param.min);
          if (param.max != null) control.max = String(param.max);
          if (param.step != null) control.step = String(param.step);
          control.value = currentVal != null ? String(currentVal) : "";
          control.addEventListener("change", () => {
            const n = Number(control.value);
            draft.parameters[param.id] = Number.isFinite(n) ? n : param.defaultValue;
          }, true);
        } else if (param.multiline) {
          control = document.createElement("textarea");
          control.className = "kp-cfg-field";
          control.rows = 3;
          if (param.placeholder) control.placeholder = String(param.placeholder);
          control.value = currentVal != null ? String(currentVal) : "";
          control.addEventListener("input", () => {
            draft.parameters[param.id] = control.value;
          }, true);
        } else {
          control = document.createElement("input");
          control.type = "text";
          control.className = "kp-cfg-field";
          if (param.placeholder) control.placeholder = String(param.placeholder);
          control.value = currentVal != null ? String(currentVal) : "";
          control.addEventListener("input", () => {
            draft.parameters[param.id] = control.value;
          }, true);
        }
        row.appendChild(control);
        wrap.appendChild(row);
      }
      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 (!macro || !macro.id) return;
      const id = String(macro.id);
      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 {
      }
    }
    /** @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 on the left, or select an Actions Library card 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 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;
      };
      const mkText = (labelText, value, onChange, placeholder) => {
        const wrap = document.createElement("label");
        const text = document.createElement("span");
        text.textContent = labelText;
        const input = document.createElement("input");
        input.type = "text";
        input.className = "kp-cfg-field";
        input.value = value == null ? "" : String(value);
        if (placeholder) input.placeholder = placeholder;
        input.addEventListener("change", () => onChange(input.value), 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 opWrap = document.createElement("label");
        const opText = document.createElement("span");
        opText.textContent = "if";
        const opSelect = document.createElement("select");
        opSelect.className = "kp-cfg-field";
        for (const op of MACRO_GATE_OPS) {
          const o = document.createElement("option");
          o.value = op;
          o.textContent = op;
          if (String(step.op || "truthy") === op) o.selected = true;
          opSelect.appendChild(o);
        }
        opSelect.addEventListener("change", () => patch({ op: opSelect.value }), true);
        opWrap.appendChild(opText);
        opWrap.appendChild(opSelect);
        fields.appendChild(opWrap);
        fields.appendChild(mkText("key", step.leftKey, (v) => patch({ leftKey: v }), "prior result key"));
        fields.appendChild(mkText("value", step.right, (v) => patch({ right: v }), "compare to"));
        fields.appendChild(mkNumber("skip", step.thenSkip, (v) => patch({ thenSkip: v }), "Steps skipped when the gate fails"));
      } 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);
      } 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);
      return row;
    }
    /**
     * 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 } : {} };
      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));
      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;
      if (!draft.id) {
        this._notify("Save the macro before running it.", "error");
        return;
      }
      if (draft.dirty) {
        const saved = await this._saveMacroDraft();
        if (!saved) return;
      }
      try {
        await this._kp?._runMacroById?.(this._macroDraft.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;
      }
      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 right = step.right === void 0 || step.right === "" ? "" : ` "${step.right}"`;
        return `${step.op || "truthy"}${right} \xB7 skip ${Math.max(0, Number(step.thenSkip) || 0)}`;
      }
      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;
      }
    }
    _syncKeyboardReferenceToggle() {
      const btn = this._refToggleBtn;
      if (!btn) return;
      const on = this._isKeyboardReferenceVisible();
      try {
        btn.setAttribute("aria-pressed", on ? "true" : "false");
      } catch {
      }
      btn.title = on ? "Hide the Keyboard Reference window" : "Show the Keyboard Reference window";
    }
    /** @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) {
      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" : "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 setCreateHeight = (height) => {
        const bounds = workspace.getBoundingClientRect();
        const next = Math.max(210, Math.min(Math.max(210, bounds.height - 160), 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 height = bounds.bottom - event.clientY;
        if (height <= 36) {
          this._setCreateOpen(false);
          return;
        }
        setCreateHeight(height);
      });
    }
    /**
     * @param {'script'|'macroKey'} mode
     * @param {{ open?: boolean }} [opts]
     */
    _setCreateMode(mode, { open = false } = {}) {
      this._createMode = mode === "macroKey" ? "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]") || []) {
          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" },
        { id: "macros", label: "Macros", icon: "kp-cfg-i-macro" },
        { id: "macroKeys", label: "Macro Keys", icon: "kp-cfg-i-keycap" }
      ];
      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;
      if (select) select.hidden = this._libPrimaryTab !== "functions";
    }
    _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 {
      }
    }
    /**
     * @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) 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 || "";
    }
    _buildBuiltinSlotMap() {
      const kb = this._kp?.keybindings || buildKeybindingsForLayout(this._st.builtinLayoutId);
      const map = {};
      for (const [actionId, binding] of Object.entries(kb || {})) {
        const label = String(binding?.displayKey || binding?.keyLabel || "").trim();
        if (!label || label.length !== 1) continue;
        map[label.toUpperCase()] = { 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);
    }
    _renderRightList() {
      const list = this._listEl;
      if (!list) return;
      const q = String(this._searchInput?.value || "").trim().toLowerCase();
      const viewBar = this._legendEl;
      list.replaceChildren();
      if (viewBar) list.appendChild(viewBar);
      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 = tab === "all" || tab === "macros";
      const showMacroKeys = 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,
        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 = type;
        item.dataset.kpItemId = id;
        item.draggable = true;
        if (this._inspectorSelection && this._inspectorSelection.type === type && 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 = type;
        keyEl.dataset.kpItemId = id;
        const iconFunctionId = type === "function" ? String(functionId || id || "") : "";
        if (type === "function" && iconFunctionId) {
          try {
            keyEl.setAttribute("data-kp-action-id", iconFunctionId);
          } catch {
          }
          ensureKeyBackgroundIcon(document, keyEl);
        } else if (type === "macro") {
          try {
            keyEl.setAttribute("data-kp-macro-id", String(id));
          } catch {
          }
          ensureKeyBackgroundIcon(document, keyEl);
        }
        if (this._placeItem && this._placeItem.type === type && 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, 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, id }, keyEl);
        }, true);
        item.addEventListener("click", (e) => {
          if (e.target?.closest?.("button, a, input, select, textarea")) return;
          this._inspectItem({ type, id });
          if (type === "function" && getFunctionDef(id)) {
            this._selectedLibraryFunctionId = String(id);
            if (this._addStepSelect) this._addStepSelect.value = String(id);
          }
        }, 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) 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 || ""} ${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 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)) {
            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();
              try {
                this._cancelPlaceMode();
              } catch {
              }
              try {
                const panel = getSharedKeyActionConfigPanel();
                await panel.open(def.id, {
                  title: def.label,
                  anchorRect: itemEl.getBoundingClientRect()
                });
              } catch {
              }
            }, 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._resetMacroDraft(`Macro ${(this._st.macros || []).length + 1}`);
          this._setCreateMode("script", { open: true });
          this._inspectorSelection = null;
          this._setInspectorOpen(true);
          this._renderInspector();
        }, true);
        macrosTitle.appendChild(newMacroBtn);
        macrosSection.appendChild(macrosTitle);
        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);
        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 = "Create one from User Macros \u2192 Macro Key, configure it, 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 wrap = document.createElement("div");
      wrap.className = "kp-cfg-lib-table-wrap";
      const table = document.createElement("table");
      table.className = "kp-cfg-lib-table";
      table.setAttribute("aria-label", "Actions Library table");
      const colgroup = document.createElement("colgroup");
      for (const cls of [
        "kp-cfg-col-label",
        "kp-cfg-col-kind",
        "kp-cfg-col-summary",
        "kp-cfg-col-slots",
        "kp-cfg-col-actions"
      ]) {
        const col = document.createElement("col");
        col.className = cls;
        colgroup.appendChild(col);
      }
      table.appendChild(colgroup);
      const thead = document.createElement("thead");
      const hr = document.createElement("tr");
      for (const h of ["Label", "Kind", "Summary", "Slots", "Actions"]) {
        const th = document.createElement("th");
        th.textContent = h;
        hr.appendChild(th);
      }
      thead.appendChild(hr);
      table.appendChild(thead);
      const tbody = document.createElement("tbody");
      const appendGroupRow = ({ groupKey, label, depth = 0, count = 0, kind = "", trailing = null }) => {
        const expanded = this._isTableGroupExpanded(groupKey);
        const tr = document.createElement("tr");
        tr.className = "kp-cfg-lib-row-group";
        tr.dataset.kpGroupKey = groupKey;
        const tdLabel = document.createElement("td");
        const cell = document.createElement("div");
        cell.className = "kp-cfg-lib-label-cell";
        cell.style.paddingLeft = `${depth * 14}px`;
        const twisty = document.createElement("button");
        twisty.type = "button";
        twisty.className = "kp-cfg-lib-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();
          this._toggleTableGroup(groupKey);
        }, true);
        const text = document.createElement("span");
        text.className = "kp-cfg-lib-label-text";
        text.textContent = count > 0 ? `${label} (${count})` : label;
        cell.appendChild(twisty);
        cell.appendChild(text);
        tdLabel.appendChild(cell);
        const tdKind = document.createElement("td");
        tdKind.textContent = kind || "";
        const tdSummary = document.createElement("td");
        tdSummary.textContent = "";
        const tdSlots = document.createElement("td");
        tdSlots.textContent = "";
        const tdActions = document.createElement("td");
        if (trailing) tdActions.appendChild(trailing);
        tr.appendChild(tdLabel);
        tr.appendChild(tdKind);
        tr.appendChild(tdSummary);
        tr.appendChild(tdSlots);
        tr.appendChild(tdActions);
        tr.addEventListener("click", (e) => {
          if (e.target?.closest?.("button, a, input, select, textarea")) return;
          this._toggleTableGroup(groupKey);
        }, true);
        tbody.appendChild(tr);
        return expanded;
      };
      const appendLeafRow = ({
        type,
        id,
        label,
        kind,
        summary,
        depth = 1,
        variant,
        functionId,
        iconKind,
        keyboardClass = "",
        actions = []
      }) => {
        const tr = document.createElement("tr");
        tr.className = "kp-cfg-lib-row-leaf";
        tr.dataset.kpItemType = type;
        tr.dataset.kpItemId = id;
        if (this._inspectorSelection && this._inspectorSelection.type === type && this._inspectorSelection.id === id) {
          tr.classList.add("kp-cfg-lib-row-inspecting");
        }
        if (this._placeItem && this._placeItem.type === type && this._placeItem.id === id) {
          tr.classList.add("kp-cfg-lib-row-place");
        }
        const tdLabel = document.createElement("td");
        const cell = document.createElement("div");
        cell.className = "kp-cfg-lib-label-cell";
        cell.style.paddingLeft = `${depth * 14}px`;
        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 = type;
        keyEl.dataset.kpItemId = id;
        const iconFunctionId = type === "function" ? String(functionId || id || "") : "";
        if (type === "function" && iconFunctionId) {
          try {
            keyEl.setAttribute("data-kp-action-id", iconFunctionId);
          } catch {
          }
          ensureKeyBackgroundIcon(document, keyEl);
        } else if (type === "macro") {
          try {
            keyEl.setAttribute("data-kp-macro-id", String(id));
          } catch {
          }
          ensureKeyBackgroundIcon(document, keyEl);
        }
        if (this._placeItem && this._placeItem.type === type && this._placeItem.id === id) {
          keyEl.classList.add("kp-place-source");
        }
        const setDragData = (e) => {
          try {
            e.dataTransfer?.setData?.(KP_LAYOUT_ITEM_MIME, JSON.stringify({ type, 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, id }, keyEl);
        }, true);
        cell.appendChild(keyEl);
        const text = document.createElement("span");
        text.className = "kp-cfg-lib-label-text";
        text.textContent = label;
        cell.appendChild(text);
        tdLabel.appendChild(cell);
        const tdKind = document.createElement("td");
        tdKind.textContent = kind || "";
        const tdSummary = document.createElement("td");
        tdSummary.textContent = summary || "";
        tdSummary.title = summary || "";
        const info = assignedInfoByItemKey.get(`${type}:${id}`) || null;
        const tdSlots = document.createElement("td");
        if (info && info.count > 0) {
          tdSlots.textContent = info.count > 1 ? `\xD7${info.count}` : String(info.first || "");
          tdSlots.title = info.count > 1 ? `Assigned to ${info.count} slots` : `Assigned to ${info.first}`;
        } else {
          tdSlots.textContent = "\u2014";
        }
        const tdActions = document.createElement("td");
        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, 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);
        }
        tdActions.appendChild(actionsRow);
        tr.addEventListener("click", (e) => {
          if (e.target?.closest?.("button, a, input, select, textarea")) return;
          this._inspectItem({ type, id });
          if (type === "function" && getFunctionDef(id)) {
            this._selectedLibraryFunctionId = String(id);
            if (this._addStepSelect) this._addStepSelect.value = String(id);
          }
        }, true);
        tr.appendChild(tdLabel);
        tr.appendChild(tdKind);
        tr.appendChild(tdSummary);
        tr.appendChild(tdSlots);
        tr.appendChild(tdActions);
        tbody.appendChild(tr);
      };
      const appendHintRow = (text, depth = 1) => {
        const tr = document.createElement("tr");
        tr.className = "kp-cfg-lib-row-leaf";
        const td = document.createElement("td");
        td.colSpan = 5;
        td.style.paddingLeft = `${8 + depth * 14}px`;
        td.style.color = "#6a7a58";
        td.style.whiteSpace = "normal";
        td.textContent = text;
        tr.appendChild(td);
        tbody.appendChild(tr);
      };
      const allDefs = showFunctions ? listFunctionDefs().filter((d) => {
        if (!d || d.legacyMacroKeyKind) 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 || ""} ${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)) {
                leafActions.push({
                  label: "Config",
                  title: `Configure ${def.label}`,
                  onClick: async () => {
                    try {
                      this._cancelPlaceMode();
                    } catch {
                    }
                    try {
                      const panel = getSharedKeyActionConfigPanel();
                      await panel.open(def.id, {
                        title: def.label,
                        anchorRect: list.getBoundingClientRect()
                      });
                    } catch {
                    }
                  }
                });
              }
              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._resetMacroDraft(`Macro ${(this._st.macros || []).length + 1}`);
          this._setCreateMode("script", { open: true });
          this._inspectorSelection = null;
          this._setInspectorOpen(true);
          this._renderInspector();
        }, 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(
              "Create one from User Macros \u2192 Macro Key, configure it, 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)
              }]
            });
          }
        }
      }
      table.appendChild(tbody);
      wrap.appendChild(table);
      list.appendChild(wrap);
    }
    /** @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 type = String(item?.type || "");
      const id = String(item?.id || "");
      const out = [];
      for (const [key, val] of Object.entries(slots)) {
        if (val && val.type === type && String(val.id) === id) 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;
      }
      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);
      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.");
    }
    /** @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("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("keydown", this._onPlaceKeyDown, true);
      } catch {
      }
      this._placePointerBound = 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 any click that is not a Keyboard Reference slot
     * (or a Config palette key, so users can switch/toggle the source).
     * @param {PointerEvent} e
     */
    _onPlacePointerDown(e) {
      if (!this._placeItem) return;
      if (typeof e.button === "number" && e.button !== 0) return;
      try {
        const slot = getComposedEventElement(e, "[data-kp-slot]");
        if (closestComposed(slot, ".kp-floating-keyboard-help")) return;
        const source = getComposedEventElement(e, "[data-kp-item-type][data-kp-item-id]");
        if (source?.getRootNode?.() === this.shadowRoot) return;
        e.preventDefault();
        e.stopPropagation();
        try {
          e.stopImmediatePropagation();
        } catch {
        }
        this._cancelPlaceMode();
      } 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);
      } 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 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._titlebar = null;
      this._saveFinishBtn = null;
      this._exitTextModeBtn = null;
      this._collapseBtn = null;
      this._collapsed = false;
      this._onCloseClick = this._onCloseClick.bind(this);
      this._onCollapseClick = this._onCollapseClick.bind(this);
      this._onSaveAndFinishClick = this._onSaveAndFinishClick.bind(this);
      this._onExitTextModeClick = this._onExitTextModeClick.bind(this);
      this._onLayoutSelectChange = this._onLayoutSelectChange.bind(this);
      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._positionApplyScheduled = false;
      this._onWinResizePosition = this._onWinResizePosition.bind(this);
      this._suppressPositionPersist = false;
      this._layoutSelectEl = 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();
      }
    }
    /**
     * @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._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.appendChild(document.createTextNode("Editing \u2014 Alt+C to exit"));
            this.hintEl.setAttribute("aria-label", "Editing layout \u2014 Alt+C to exit");
          } else {
            const b = this.keybindings && this.keybindings.TOGGLE_KEYBOARD_HELP;
            const key = b && (b.displayKey || b.keyLabel) ? String(b.displayKey || b.keyLabel) : "K";
            this._setToggleHint(this.hintEl, key);
          }
        }
      } catch {
      }
      try {
        if (this._layoutSelectEl) this._layoutSelectEl.disabled = false;
      } catch {
      }
      this._applyEditModeHatch(next);
      this._syncSaveAndFinishButton(next);
      this._syncEscExitButton();
      if (this.root && !this.root.hidden) this._render();
    }
    /**
     * Titlebar CTA shown only while layout edit mode is active.
     * Placed immediately to the right of the layout dropdown.
     * @param {boolean} visible
     */
    _syncSaveAndFinishButton(visible) {
      try {
        if (visible) {
          this._ensureSaveAndFinishButton();
          if (this._saveFinishBtn) this._saveFinishBtn.hidden = false;
        } else if (this._saveFinishBtn) {
          this._saveFinishBtn.hidden = true;
        }
      } catch {
      }
    }
    _ensureSaveAndFinishButton() {
      if (this._saveFinishBtn && this._saveFinishBtn.isConnected) return;
      const header = this._titlebar || this.shadowRoot?.querySelector?.('[data-kp-floating-keyboard-titlebar="true"]') || null;
      if (!header) return;
      let btn = header.querySelector('button[data-kp-floating-keyboard-save-finish="true"]');
      if (!btn) {
        btn = document.createElement("button");
        btn.type = "button";
        btn.textContent = "Save and Finish";
        btn.setAttribute("data-kp-floating-keyboard-save-finish", "true");
        btn.setAttribute("aria-label", "Save and finish editing keyboard layout");
        btn.title = "Save layout changes and exit edit mode";
        Object.assign(btn.style, {
          marginLeft: "6px",
          padding: "0 8px",
          height: "22px",
          minHeight: "22px",
          borderRadius: NCT_DARK_UI_BTN_RADIUS,
          border: NCT_DARK_UI_BTN_LIT_BORDER,
          background: NCT_DARK_UI_BTN_LIT_GRADIENT,
          color: NCT_DARK_UI_SELECTED_TEXT,
          outline: "none",
          fontSize: "11px",
          fontWeight: "600",
          fontFamily: NCT_DARK_UI_FONT,
          lineHeight: "20px",
          whiteSpace: "nowrap",
          cursor: "pointer",
          flex: "0 0 auto",
          boxShadow: "inset 0 1px 0 rgba(200,220,240,0.18)"
        });
        const layoutSelect = this._layoutSelectEl || header.querySelector('[data-kp-floating-keyboard-layout-select="true"]');
        const hintEl = this.hintEl || header.querySelector('[data-kp-floating-keyboard-hint="true"]');
        try {
          if (layoutSelect && layoutSelect.nextSibling) {
            header.insertBefore(btn, layoutSelect.nextSibling);
          } else if (hintEl) {
            header.insertBefore(btn, hintEl);
          } else {
            header.appendChild(btn);
          }
        } catch {
          try {
            header.appendChild(btn);
          } catch {
          }
        }
      }
      try {
        btn.removeEventListener("click", this._onSaveAndFinishClick);
      } catch {
      }
      btn.addEventListener("click", this._onSaveAndFinishClick);
      try {
        btn.addEventListener("pointerdown", (e) => {
          try {
            e.stopPropagation();
          } catch {
          }
        });
      } catch {
      }
      this._saveFinishBtn = btn;
    }
    _onSaveAndFinishClick(e) {
      try {
        e?.preventDefault?.();
        e?.stopPropagation?.();
      } catch {
      }
      try {
        const kp = typeof this._getKeyPilot === "function" ? this._getKeyPilot() : null;
        if (kp) {
          exitKeyboardLayoutEditMode(kp);
          return;
        }
      } catch {
      }
      try {
        const panel = typeof this._getConfigPanel === "function" ? this._getConfigPanel() : null;
        panel?.hide?.();
      } catch {
      }
      try {
        this.setEditMode(false);
      } catch {
      }
    }
    /**
     * 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 || mode === MODES.SCROLL_LINE;
    }
    _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() {
      if (this._exitTextModeBtn && this._exitTextModeBtn.isConnected) return;
      const header = this._titlebar || this.shadowRoot?.querySelector?.('[data-kp-floating-keyboard-titlebar="true"]') || null;
      if (!header) return;
      let btn = 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 titleEl = this._layoutTitleEl || header.querySelector('[data-kp-floating-keyboard-title="true"]');
        const layoutSelect = this._layoutSelectEl || header.querySelector('[data-kp-floating-keyboard-layout-select="true"]');
        try {
          if (titleEl && titleEl.nextSibling) {
            header.insertBefore(btn, titleEl.nextSibling);
          } else if (layoutSelect) {
            header.insertBefore(btn, layoutSelect);
          } else {
            header.appendChild(btn);
          }
        } catch {
          try {
            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 {
      }
    }
    /**
     * 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;
      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);
    }
    /**
     * 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 = () => {
        if (String(this._currentKeyboardLayoutId || "").startsWith("user:")) {
          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);
      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._saveFinishBtn) {
          this._saveFinishBtn.removeEventListener("click", this._onSaveAndFinishClick);
        }
      } catch {
      }
      try {
        if (this._exitTextModeBtn) {
          this._exitTextModeBtn.removeEventListener("click", this._onExitTextModeClick);
        }
      } catch {
      }
      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._saveFinishBtn = null;
      this._exitTextModeBtn = 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_COLORS.panel,
        color: NCT_DARK_UI_COLORS.fg,
        // 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, hintEl?: HTMLElement|null, closeBtn?: HTMLElement|null }} [parts]
     */
    _applyCompactTitlebar(header, parts = {}) {
      if (!header || !header.style) return;
      Object.assign(header.style, {
        display: "flex",
        alignItems: "center",
        justifyContent: "space-between",
        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: "600",
          letterSpacing: "0.01em",
          textTransform: "none",
          color: NCT_DARK_UI_COLORS.fg,
          lineHeight: "28px",
          whiteSpace: "nowrap",
          overflow: "hidden",
          textOverflow: "ellipsis",
          margin: "0",
          padding: "0"
        });
      }
      const hintEl = parts.hintEl || header.querySelector('[data-kp-floating-keyboard-hint="true"]');
      if (hintEl && hintEl.style) {
        Object.assign(hintEl.style, {
          display: "inline-flex",
          alignItems: "center",
          gap: "4px",
          marginLeft: "auto",
          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 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"], button[data-kp-floating-keyboard-save-finish="true"], .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" });
          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 layoutSelect2 = shell2.querySelector('[data-kp-floating-keyboard-layout-select="true"]');
          if (!layoutSelect2 && header2) {
            layoutSelect2 = document.createElement("select");
            layoutSelect2.setAttribute("aria-label", "Current keyboard layout");
            layoutSelect2.setAttribute("data-kp-floating-keyboard-layout-select", "true");
            Object.assign(layoutSelect2.style, {
              marginLeft: "6px",
              padding: "2px 6px",
              borderRadius: "2px",
              border: NCT_DARK_UI_FIELD_BORDER,
              background: NCT_DARK_UI_FIELD_BACKGROUND,
              color: NCT_DARK_UI_COLORS.fg,
              outline: "none",
              fontSize: "11px",
              width: "190px",
              height: "22px",
              cursor: "pointer"
            });
            this._wireLayoutSelect(layoutSelect2);
            try {
              const hintNode = hintEl || header2.querySelector('[data-kp-floating-keyboard-hint="true"]');
              if (hintNode) header2.insertBefore(layoutSelect2, hintNode);
              else header2.appendChild(layoutSelect2);
            } catch {
              try {
                header2.appendChild(layoutSelect2);
              } catch {
              }
            }
          } else if (layoutSelect2) {
            this._wireLayoutSelect(layoutSelect2);
          }
          if (!this._positionHydrated) {
            const fromDom = this._readPositionFromDom(existing);
            if (fromDom) this._seedPanelPosition(fromDom, { hydrated: true });
          }
          try {
            this._applyProPanelChrome(existing);
            this._applyCompactTitlebar(header2, { titleEl, 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._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._ensureCollapseButton();
            this._applyCollapsedLayout();
            this._bindWindowChrome();
            try {
              ensureStylesInjected(shadowRoot2 || existing);
            } catch {
            }
            this._bindHostGuard();
            this._signalEarlyKeyboardHelpAdopted();
            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" });
      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 layoutSelect = document.createElement("select");
      layoutSelect.setAttribute("aria-label", "Current keyboard layout");
      layoutSelect.setAttribute("data-kp-floating-keyboard-layout-select", "true");
      Object.assign(layoutSelect.style, {
        marginLeft: "6px",
        padding: "2px 6px",
        borderRadius: "2px",
        border: NCT_DARK_UI_FIELD_BORDER,
        background: NCT_DARK_UI_FIELD_BACKGROUND,
        color: NCT_DARK_UI_COLORS.fg,
        outline: "none",
        fontSize: "11px",
        width: "190px",
        height: "22px",
        cursor: "pointer"
      });
      this._wireLayoutSelect(layoutSelect);
      const hint = document.createElement("div");
      hint.setAttribute("data-kp-floating-keyboard-hint", "true");
      this._setToggleHint(hint, "K");
      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);
      header.appendChild(title);
      header.appendChild(layoutSelect);
      header.appendChild(hint);
      header.appendChild(closeBtn);
      this._applyCompactTitlebar(header, { titleEl: title, 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._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",
        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;
      if (this._collapsed === next) {
        this._applyCollapsedLayout();
        this._applyPanelPositionNow();
        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 settings = await getSettings();
        this.setCollapsed(!!settings?.keyboardReferenceCollapsed, { persist: false });
      } catch {
      }
    }
    /**
     * Titlebar hint: "Press <kbd>K</kbd> to toggle" (key label is layout-aware).
     * @param {HTMLElement|null} hintEl
     * @param {string} keyLabel
     */
    _setToggleHint(hintEl, keyLabel) {
      if (!hintEl) return;
      const key = String(keyLabel || "K").trim() || "K";
      try {
        const existing = hintEl.querySelector('[data-kp-floating-keyboard-hint-key="true"]');
        if (existing && existing.textContent === key && hintEl.getAttribute("aria-label") === `Press ${key} to toggle` && !this._editMode) {
          return;
        }
      } catch {
      }
      while (hintEl.firstChild) hintEl.removeChild(hintEl.firstChild);
      hintEl.appendChild(document.createTextNode("Press "));
      const kbd = document.createElement("kbd");
      kbd.setAttribute("data-kp-floating-keyboard-hint-key", "true");
      kbd.textContent = key;
      Object.assign(kbd.style, {
        display: "inline-block",
        fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
        fontSize: "10px",
        fontWeight: "600",
        lineHeight: "1.2",
        padding: "1px 5px",
        border: "1px solid rgba(255, 255, 255, 0.16)",
        borderBottomColor: "rgba(0, 0, 0, 0.55)",
        borderRadius: "4px",
        background: "linear-gradient(180deg, rgba(255,255,255,0.10) 0%, rgba(255,255,255,0.04) 100%)",
        color: "rgba(230, 232, 238, 0.95)",
        boxShadow: "0 1px 0 rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.06)",
        verticalAlign: "middle"
      });
      hintEl.appendChild(kbd);
      hintEl.appendChild(document.createTextNode(" to toggle"));
      try {
        hintEl.setAttribute("aria-label", `Press ${key} to toggle`);
      } 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._setToggleHint(this.hintEl, key);
        }
      } catch {
      }
      this._syncEscExitButton();
      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 = "repeating-linear-gradient(-45deg, rgba(180, 200, 220, 0.08) 0px, rgba(180, 200, 220, 0.08) 1px, transparent 1px, transparent 7px)";
      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}, 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 = "#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) {
          const b = this.keybindings && this.keybindings.TOGGLE_KEYBOARD_HELP;
          const key = b && (b.displayKey || b.keyLabel) ? String(b.displayKey || b.keyLabel) : "K";
          this._setToggleHint(this.hintEl, 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:
    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, #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: #1a1c20 !important;
  background-image:
    repeating-linear-gradient(
      -45deg,
      rgba(180, 200, 220, 0.08) 0px,
      rgba(180, 200, 220, 0.08) 1px,
      transparent 1px,
      transparent 7px
    ) !important;
}
/* 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;
}
:host([data-kp-edit-mode="true"]) .keyboard-visual.${KEYBINDINGS_UI_ROOT_CLASS} .key .key-main {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding-right: 16px;
}
/* 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 {Event} e
     */
    async _onLayoutSelectChange(e) {
      const sel = e?.currentTarget instanceof HTMLSelectElement ? e.currentTarget : this._layoutSelectEl;
      if (!sel) return;
      const v = String(sel.value || "builtin");
      if (v === LAYOUT_SELECT_EDIT_VALUE || v === LAYOUT_SELECT_NEW_VALUE || v === LAYOUT_SELECT_DUP_VALUE) {
        const prev = this._layoutSelectValueForCurrent();
        const known = [...sel.options].some((o) => o && o.value === prev);
        sel.value = known ? prev : builtinFamilySelectValue("browsing");
        try {
          const kp = this._getKeyPilot?.();
          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 {
              sel.value = this._layoutSelectValueForCurrent();
            } catch {
            }
            return;
          }
        } catch {
        }
      }
      try {
        const familyId = parseBuiltinFamilySelectValue(v);
        if (familyId) {
          await setSettings({
            currentKeyboardLayoutId: "builtin",
            keyboardLayoutFamilyId: familyId
          });
        } else {
          await setSettings({ currentKeyboardLayoutId: v });
        }
      } catch {
      }
    }
    /**
     * 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);
    }
    /**
     * Wire (or re-wire) the layout select change listener once.
     * @param {HTMLSelectElement|null} layoutSelect
     */
    _wireLayoutSelect(layoutSelect) {
      if (!layoutSelect) return;
      try {
        layoutSelect.removeEventListener("change", this._onLayoutSelectChange, true);
      } catch {
      }
      layoutSelect.addEventListener("change", this._onLayoutSelectChange, true);
      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 selEl = this._layoutSelectEl;
      if (!selEl) return;
      try {
        const layouts = await listUserKeyboardLayouts();
        const groups = listLayoutPickerGroups(layouts);
        selEl.innerHTML = "";
        const known = /* @__PURE__ */ new Set();
        const appendGroup = (heading, items) => {
          const hdr = document.createElement("option");
          hdr.disabled = true;
          hdr.textContent = heading;
          selEl.appendChild(hdr);
          for (const item of items || []) {
            if (!item?.value) continue;
            const opt = document.createElement("option");
            opt.value = item.value;
            opt.textContent = item.label;
            selEl.appendChild(opt);
            known.add(opt.value);
          }
        };
        appendGroup("Built-In", groups.builtin);
        known.add("builtin");
        try {
          selEl.appendChild(document.createElement("hr"));
        } catch {
        }
        appendGroup("Custom", groups.custom);
        if (!this._editMode) {
          try {
            selEl.appendChild(document.createElement("hr"));
          } catch {
          }
          const optEdit = document.createElement("option");
          optEdit.value = LAYOUT_SELECT_EDIT_VALUE;
          optEdit.textContent = "Edit Keyboard Layout\u2026";
          selEl.appendChild(optEdit);
          try {
            selEl.appendChild(document.createElement("hr"));
          } catch {
          }
          const optNew = document.createElement("option");
          optNew.value = LAYOUT_SELECT_NEW_VALUE;
          optNew.textContent = "New Blank Keyboard Layout";
          selEl.appendChild(optNew);
          const optDup = document.createElement("option");
          optDup.value = LAYOUT_SELECT_DUP_VALUE;
          optDup.textContent = "New Duplicate Keyboard Layout";
          selEl.appendChild(optDup);
        }
        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;
          }
        }
        selEl.value = v;
      } catch {
      }
    }
    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;
      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 uiLayout = getKeyboardUiLayoutForLayout(this.layoutId || "browsing-right", {
            includeNumberRow: showNumberRow
          });
          this.keyboardLayout = uiLayout;
          renderKeybindingsKeyboard({
            container: this.keyboardContainer,
            keybindings: this.keybindings,
            keyboardLayout: uiLayout,
            layoutId: this.layoutId || void 0,
            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 {
        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 uiLayout2 = getKeyboardUiLayoutForLayout(this.layoutId || "browsing-right", {
              includeNumberRow: showNumberRow2
            });
            this.keyboardLayout = uiLayout2;
            renderKeybindingsKeyboard({
              container: this.keyboardContainer,
              keybindings: this.keybindings,
              keyboardLayout: uiLayout2,
              layoutId: this.layoutId || void 0,
              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 });
        this._renderSlotKeyboard({
          container: this.keyboardContainer,
          uiLayout,
          slots: userLayout.slots || {},
          macros,
          actions,
          keybindings: this.keybindings,
          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 baseId = this.layoutId || "browsing-right";
      let userLayout = null;
      if (mode === "user" && st?.userLayout) {
        userLayout = st.userLayout;
        slots = userLayout.slots && typeof userLayout.slots === "object" ? userLayout.slots : {};
        baseId = String(userLayout.baseBuiltinLayoutId || baseId);
      } else {
        for (const [actionId, binding] of Object.entries(this.keybindings || {})) {
          const label = String(binding?.displayKey || binding?.keyLabel || "").trim();
          if (!label || label.length !== 1) continue;
          slots[label.toUpperCase()] = { type: "function", id: String(actionId) };
        }
        baseId = String(st?.builtinLayoutId || 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 });
      this._renderSlotKeyboard({
        container: this.keyboardContainer,
        uiLayout,
        slots,
        macros,
        actions,
        keybindings: this.keybindings,
        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];
        const label = String(binding?.displayKey || binding?.keyLabel || "").trim();
        return label && label.length === 1 ? label.toUpperCase() : "";
      };
      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) => {
        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";
        }
        btn.className = `key${keyboardClass ? " " + keyboardClass : ""}${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 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";
          Object.assign(del.style, {
            position: "absolute",
            top: "1px",
            right: "1px",
            left: "auto",
            bottom: "auto",
            width: "14px",
            height: "14px",
            zIndex: "8"
          });
          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);
          btn.appendChild(del);
        }
        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;
          }
          if (item.type === "action" && (item.id === "DELETE" || String(item.className || "").includes("key-backspace"))) {
            const sp = doc.createElement("div");
            sp.className = String(item.className || "key key-backspace");
            const text = doc.createElement("span");
            text.className = "key-text";
            text.textContent = "Backspace";
            sp.appendChild(text);
            ensureKeyPressOverlay(doc, sp);
            rowEl.appendChild(sp);
            continue;
          }
          let slotLabel = "";
          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));
        }
      }
      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();

  // 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 + T to re-open this walkthrough later.";
  var ONBOARDING_PANEL_Z_FALLBACK = 2147483026;
  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, #d0d0d0 0%, #a8a8a8 50%, #909090 100%)",
    kbdBorder: "1px solid #4a4a4a",
    kbdColor: "#1c1c1c"
  };
  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;
    let css = `.${ONBOARDING_PANEL_CLASS} kbd {
        font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
        font-size: 11px;
        padding: 1px 6px;
        border: ${ONBOARDING_METAL.kbdBorder};
        border-radius: 2px;
        background: ${ONBOARDING_METAL.kbdBg};
        color: ${ONBOARDING_METAL.kbdColor};
        box-shadow: 0 1px 0 rgba(255,255,255,0.45) inset, 0 -1px 0 rgba(0,0,0,0.18) inset;
      }
      .${ONBOARDING_PANEL_CLASS} [data-kp-onboarding-overlay-title="true"] {
        color: #ffffff;
      }
      /* 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);
        }
      }
      .${ONBOARDING_PANEL_CLASS} [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); }
      }
      .${ONBOARDING_PANEL_CLASS} .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;
      }
      .${ONBOARDING_PANEL_CLASS} .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) {
        .${ONBOARDING_PANEL_CLASS} [data-kp-onboarding-task-next="true"] {
          animation: none;
        }
        .${ONBOARDING_PANEL_CLASS} .kp-onboarding-check-flash,
        .${ONBOARDING_PANEL_CLASS} .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";
    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: ONBOARDING_METAL.panelBg,
      color: ONBOARDING_METAL.fg,
      border: ONBOARDING_METAL.panelBorder,
      borderRadius: "3px",
      boxShadow: ONBOARDING_METAL.panelShadow,
      fontFamily: "Helvetica, Arial, sans-serif",
      pointerEvents: initiallyHidden ? "none" : "auto"
    });
    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";
      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 {
    }
  }
  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 {
      }
      try {
        const titleEl2 = existing.querySelector('[data-kp-onboarding-overlay-title="true"]');
        if (titleEl2) titleEl2.style.color = "#ffffff";
      } catch {
      }
      return {
        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"]')
      };
    }
    const overlay = d.createElement("div");
    overlay.setAttribute("data-kp-onboarding-overlay", "true");
    overlay.hidden = true;
    assignStyle(overlay, {
      position: "absolute",
      inset: "0",
      padding: "14px",
      display: "none",
      alignItems: "center",
      justifyContent: "center",
      background: "rgba(0,0,0,0.42)",
      backdropFilter: "blur(10px)",
      WebkitBackdropFilter: "blur(10px)",
      zIndex: "20",
      pointerEvents: "none",
      boxSizing: "border-box"
    });
    const card = d.createElement("div");
    assignStyle(card, {
      width: "100%",
      maxWidth: "320px",
      borderRadius: "14px",
      border: "1px solid rgba(255,255,255,0.16)",
      background: "rgba(18, 18, 18, 0.78)",
      boxShadow: "0 16px 44px rgba(0,0,0,0.55)",
      padding: "14px 14px 12px 14px"
    });
    const titleEl = d.createElement("div");
    titleEl.setAttribute("data-kp-onboarding-overlay-title", "true");
    titleEl.textContent = "Nice!";
    assignStyle(titleEl, {
      fontSize: "14px",
      fontWeight: "900",
      letterSpacing: "0.2px",
      marginBottom: "8px",
      color: "#ffffff"
    });
    const msgEl = d.createElement("div");
    msgEl.setAttribute("data-kp-onboarding-overlay-message", "true");
    msgEl.textContent = "";
    assignStyle(msgEl, {
      fontSize: "13px",
      lineHeight: "1.35",
      color: "rgba(255,255,255,0.90)",
      whiteSpace: "pre-wrap"
    });
    const btnRow = d.createElement("div");
    assignStyle(btnRow, {
      display: "flex",
      flexWrap: "wrap",
      alignItems: "center",
      justifyContent: "flex-end",
      gap: "8px",
      marginTop: "12px"
    });
    const mkBtn = (variant) => {
      const b = d.createElement("button");
      b.type = "button";
      assignStyle(b, {
        minHeight: "30px",
        height: "auto",
        borderRadius: "999px",
        border: variant === "primary" ? "1px solid rgba(46, 204, 113, 0.55)" : "1px solid rgba(255,255,255,0.20)",
        background: variant === "primary" ? "rgba(46, 204, 113, 0.18)" : "rgba(255,255,255,0.06)",
        color: "rgba(255,255,255,0.92)",
        cursor: "pointer",
        fontSize: "12px",
        fontWeight: "800",
        padding: "4px 12px",
        lineHeight: "20px",
        whiteSpace: "normal",
        textAlign: "center"
      });
      return b;
    };
    const secondaryBtn = mkBtn("secondary");
    secondaryBtn.hidden = true;
    secondaryBtn.textContent = "";
    secondaryBtn.setAttribute("data-kp-onboarding-overlay-secondary", "true");
    const primaryBtn = mkBtn("primary");
    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);
    return { overlayEl: overlay, titleEl, msgEl, primaryBtn, secondaryBtn };
  }
  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/control-strip.js
  init_settings_manager();
  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 = false;
      this._keyboardActive = false;
      this._desiredVisible = true;
      this._panelPosition = { ...DEFAULT_SETTINGS.panelPositions.controlStrip };
      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;
      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();
    }
    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" });
          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;
            try {
              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();
            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" });
      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 {
      }
    }
    /**
     * @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();
      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, color = NCT_DARK_UI_COLORS.fg) {
      try {
        const cssUri = getActionIconDataUri(actionId, { fill: color });
        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/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 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/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 getGoogleS2FaviconUrl(pageUrl, size = 128) {
    const s = Math.max(16, Math.min(256, Number(size) || 128));
    try {
      const u = new URL(String(pageUrl || "").trim());
      const scheme = String(u.protocol || "").toLowerCase();
      if (scheme !== "http:" && scheme !== "https:") return "";
      const domain = (u.hostname || "").replace(/^www\./i, "");
      if (!domain) return "";
      return `https://www.google.com/s2/favicons?domain=${encodeURIComponent(domain)}&sz=${s}`;
    } catch {
      return "";
    }
  }
  async function requestFaviconFromServiceWorker(pageUrl, size = 32) {
    try {
      if (typeof chrome === "undefined" || !chrome.runtime?.sendMessage) {
        return null;
      }
      const response = await chrome.runtime.sendMessage({
        type: "KP_GET_FAVICON",
        pageUrl,
        size
      });
      if (response && response.type === "KP_FAVICON_RESPONSE" && response.success && response.dataUrl) {
        return response.dataUrl;
      }
    } catch (e) {
    }
    return null;
  }
  function attachFaviconWithUpgrade(img, pageUrl, opts = {}) {
    if (!img) return;
    const url = String(pageUrl || "").trim();
    const displaySize = Math.max(12, Number(opts.displaySize) || 32);
    const requestSize = Math.max(
      displaySize,
      Number(opts.requestSize) || Math.max(128, displaySize * 2)
    );
    const fallback = typeof opts.fallbackUrl === "string" && opts.fallbackUrl ? opts.fallbackUrl : GENERIC_FAVICON_DATA_URL;
    const highRes = opts.highRes !== false;
    try {
      img.alt = img.alt || "";
      img.referrerPolicy = "no-referrer";
      img.decoding = "async";
      img.loading = img.loading || "lazy";
    } catch {
    }
    let stage = "";
    const setSrc = (src, nextStage) => {
      try {
        stage = nextStage;
        img.src = src;
      } catch {
      }
    };
    const chromeUrl = getExtensionFaviconUrl(url, Math.min(128, Math.max(32, displaySize * 2)));
    const googleUrl = getGoogleS2FaviconUrl(url, requestSize);
    setSrc(chromeUrl, "chrome");
    img.addEventListener("error", () => {
      if (stage === "sw" || stage === "fallback") return;
      if (stage === "chrome" && googleUrl) {
        setSrc(googleUrl, "google");
        try {
          img.dataset.kpFaviconHires = "google";
        } catch {
        }
        return;
      }
      if (stage === "google" || stage === "chrome") {
        setSrc(fallback, "fallback");
        try {
          img.dataset.kpFaviconFallback = "true";
        } catch {
        }
      }
    });
    if (!highRes || !url) return;
    const tryGoogleUpgrade = () => {
      if (!googleUrl || !img.isConnected) return;
      if (stage === "sw" || stage === "fallback") return;
      if (stage !== "chrome") return;
      setSrc(googleUrl, "google");
      try {
        img.dataset.kpFaviconHires = "google";
      } catch {
      }
    };
    try {
      requestAnimationFrame(() => {
        setTimeout(tryGoogleUpgrade, 0);
      });
    } catch {
      tryGoogleUpgrade();
    }
    requestFaviconFromServiceWorker(url, requestSize).then((dataUrl) => {
      if (!dataUrl || !img.isConnected) return;
      try {
        img.src = dataUrl;
        img.dataset.kpFaviconHires = "sw";
        stage = "sw";
      } catch {
      }
    }).catch(() => {
    });
  }
  function createFaviconImg(doc, url, { size = 18, faviconUrl, fallbackUrl, 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,
            highRes
          });
        },
        { once: true }
      );
      if (highRes) {
        requestFaviconFromServiceWorker(url, Math.max(size, 128)).then((dataUrl) => {
          if (dataUrl && img.isConnected) {
            img.src = dataUrl;
            img.dataset.kpFaviconHires = "true";
          }
        }).catch(() => {
        });
      }
      return img;
    }
    attachFaviconWithUpgrade(img, url, {
      displaySize: size,
      fallbackUrl: fallback,
      highRes
    });
    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;
      }
    }
    _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: "KP_OMNIBOX_SUGGEST",
            query,
            maxResults: 12
          });
          if (resp && resp.type === "KP_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/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 requestPageThumb(pageUrl) {
    const url = String(pageUrl || "").trim();
    if (!url) return null;
    try {
      if (typeof chrome === "undefined" || !chrome.runtime?.sendMessage) {
        return null;
      }
      const response = await chrome.runtime.sendMessage({
        type: "KP_GET_PAGE_THUMB",
        pageUrl: url
      });
      if (response && response.type === "KP_PAGE_THUMB_RESPONSE" && response.success && typeof response.dataUrl === "string" && response.dataUrl) {
        return response.dataUrl;
      }
    } catch {
    }
    return null;
  }
  async function requestVideoThumb(pageUrl) {
    const url = String(pageUrl || "").trim();
    if (!url || !isVideoSiteUrl(url)) 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: "KP_GET_VIDEO_THUMB",
        pageUrl: url
      });
      if (response && response.type === "KP_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;
    }
    const dataUrl = await requestPageThumb(pageUrl);
    if (dataUrl) return { url: dataUrl, source: "capture" };
    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: "KP_NAVGRAPH_GET" });
      } catch {
        resp = null;
      }
      if (!this._open) return;
      if (!resp || resp.type !== "KP_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: "KP_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 !== "KP_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: "KP_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: "KP_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._pageResults = /* @__PURE__ */ Object.create(null);
      this._pageResultsIframe = null;
      this._pageResultsBridgeTimer = null;
      this._pageResultsLoadTimeout = null;
      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._previewError = null;
      this._errorTitle = null;
      this._errorMessage = null;
      this._currentPreviewUrl = null;
      this._previewBridgeTimer = 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();
      if (this._previewBridgeTimer) {
        clearInterval(this._previewBridgeTimer);
        this._previewBridgeTimer = null;
      }
      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._previewIframe = null;
      this._pageResultsIframe = null;
      this._teardownPageResultsBridge();
      this._currentSheet = 0;
      this._searchQuery = "";
      this._headerSearchDrafts = /* @__PURE__ */ Object.create(null);
      this._videosSearchSiteUrl = "https://youtube.com";
      this._pageResults = /* @__PURE__ */ Object.create(null);
      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: "KP_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: "KP_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: "KP_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: "KP_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" });
      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;
      background: #0f0f0f;
      border-left: 1px solid #333;
      display: flex;
      flex-direction: column;
      overflow: hidden;
      transition: width 0.3s ease;
    `;
      const previewHeader = doc.createElement("div");
      previewHeader.className = "kp-launcher-preview-header";
      previewHeader.style.cssText = `
      padding: 8px 12px;
      border-bottom: ${NCT_DARK_UI_TITLEBAR_BORDER_BOTTOM};
      background: ${NCT_DARK_UI_TITLEBAR_GRADIENT};
      display: flex;
      justify-content: flex-start;
      align-items: center;
      gap: 8px;
    `;
      const previewTitle = doc.createElement("div");
      previewTitle.className = "kp-launcher-preview-title";
      previewTitle.textContent = "Preview";
      previewTitle.style.cssText = `
      color: #fff;
      font-size: 14px;
      font-weight: 500;
      flex: 1;
      min-width: 0;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    `;
      const { actions: previewOpenActions } = createPreviewOpenActionButtons({
        doc,
        getUrl: () => this._currentPreviewUrl,
        afterOpen: () => {
          try {
            this.hide();
          } catch {
          }
        },
        afterOpenNewTab: () => {
        }
      });
      const previewCloseBtn = doc.createElement("button");
      previewCloseBtn.type = "button";
      previewCloseBtn.className = "kp-launcher-preview-close";
      previewCloseBtn.title = "Hide preview pane";
      previewCloseBtn.setAttribute("aria-label", "Hide preview pane");
      previewCloseBtn.style.cssText = `
      display: inline-flex;
      align-items: center;
      justify-content: center;
      margin: 0;
      appearance: none;
      -webkit-appearance: none;
      box-sizing: border-box;
      background: transparent;
      border: 1px solid transparent;
      box-shadow: none;
      color: ${NCT_DARK_UI_COLORS.fgMute};
      cursor: pointer;
      padding: 0;
      width: 28px;
      height: 28px;
      border-radius: ${NCT_DARK_UI_BTN_RADIUS};
      flex-shrink: 0;
      transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
    `;
      const collapseIcon = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
      collapseIcon.setAttribute("viewBox", "0 0 24 24");
      collapseIcon.setAttribute("width", "18");
      collapseIcon.setAttribute("height", "18");
      collapseIcon.setAttribute("aria-hidden", "true");
      collapseIcon.style.cssText = "display: block; pointer-events: none;";
      const stroke = {
        fill: "none",
        stroke: "currentColor",
        "stroke-width": "1.75",
        "stroke-linecap": "round",
        "stroke-linejoin": "round"
      };
      const addSvg = (name, attrs) => {
        const el2 = doc.createElementNS("http://www.w3.org/2000/svg", name);
        for (const [k, v] of Object.entries({ ...stroke, ...attrs })) {
          el2.setAttribute(k, v);
        }
        collapseIcon.appendChild(el2);
        return el2;
      };
      addSvg("rect", { x: "3", y: "4", width: "18", height: "16", rx: "2", "stroke-width": "1.75" });
      addSvg("path", { d: "M16 4v16" });
      addSvg("path", { d: "M7 12h6" });
      addSvg("path", { d: "M10.5 9L13.5 12 10.5 15" });
      previewCloseBtn.appendChild(collapseIcon);
      previewCloseBtn.addEventListener("click", () => {
        this._hidePreview();
      });
      previewCloseBtn.addEventListener("mouseenter", () => {
        previewCloseBtn.style.color = "#fff";
        previewCloseBtn.style.background = NCT_DARK_UI_HOVER_TINT;
        previewCloseBtn.style.borderColor = NCT_DARK_UI_COLORS.panelEdge;
      });
      previewCloseBtn.addEventListener("mouseleave", () => {
        previewCloseBtn.style.color = NCT_DARK_UI_COLORS.fgMute;
        previewCloseBtn.style.background = "transparent";
        previewCloseBtn.style.borderColor = "transparent";
      });
      previewHeader.appendChild(previewCloseBtn);
      previewHeader.appendChild(previewTitle);
      previewHeader.appendChild(previewOpenActions);
      this._previewIframe = doc.createElement("iframe");
      this._previewIframe.className = "kp-launcher-preview-iframe";
      this._previewIframe.style.cssText = `
      flex: 1;
      border: none;
      background: #fff;
    `;
      this._previewError = doc.createElement("div");
      this._previewError.className = "kp-launcher-preview-error";
      this._previewError.style.cssText = `
      flex: 1;
      display: none;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      padding: 20px;
      text-align: center;
      background: #f9f9f9;
      border-radius: 8px;
      margin: 8px;
    `;
      const errorIcon = doc.createElement("div");
      errorIcon.style.cssText = `
      font-size: 32px;
      margin-bottom: 12px;
      color: #999;
    `;
      errorIcon.textContent = "\u{1F6AB}";
      this._previewError.appendChild(errorIcon);
      this._errorTitle = doc.createElement("div");
      this._errorTitle.style.cssText = `
      font-size: 16px;
      font-weight: 600;
      color: #333;
      margin-bottom: 6px;
    `;
      this._errorTitle.textContent = "Cannot Display Page";
      this._previewError.appendChild(this._errorTitle);
      this._errorMessage = doc.createElement("div");
      this._errorMessage.style.cssText = `
      font-size: 13px;
      color: #666;
      margin-bottom: 16px;
      max-width: 300px;
    `;
      this._errorMessage.textContent = "This website prevents embedding in iframes for security reasons.";
      this._previewError.appendChild(this._errorMessage);
      const openInTabButton = doc.createElement("button");
      openInTabButton.style.cssText = `
      margin: 0;
      appearance: none;
      -webkit-appearance: none;
      box-sizing: border-box;
      background: #4CAF50;
      color: white;
      border: none;
      box-shadow: none;
      padding: 8px 16px;
      border-radius: 4px;
      cursor: pointer;
      font-size: 13px;
      font-weight: 500;
    `;
      openInTabButton.textContent = "Open in New Tab";
      openInTabButton.onclick = () => {
        if (this._currentPreviewUrl) {
          window.open(this._currentPreviewUrl, "_blank");
        }
      };
      this._previewError.appendChild(openInTabButton);
      previewArea.appendChild(previewHeader);
      previewArea.appendChild(this._previewIframe);
      previewArea.appendChild(this._previewError);
      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._clearCategoryPageResults(this._currentCategory, { render: 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(type, label) {
      const doc = document;
      const subTab = doc.createElement("button");
      subTab.className = "kp-launcher-subtab";
      subTab.dataset.type = type;
      const currentSubTab = this._categorySubTabs[this._currentCategory] || "history";
      const isActive = type === currentSubTab;
      const count = this._subTabItemCount(this._currentCategory, type);
      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._clearCategoryPageResults(this._currentCategory, { render: false });
        this._categorySubTabs[this._currentCategory] = type;
        this._currentSheet = 0;
        if (type !== "sites" && this._launchDeckEditMode) {
          this._launchDeckEditMode = false;
          this._closeAddSitePicker();
        }
        if (type === "favorites" || type === "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 (type !== active2) {
          subTab.style.color = "#fff";
        }
      });
      subTab.addEventListener("mouseleave", () => {
        const active2 = this._categorySubTabs[this._currentCategory] || "history";
        if (type !== 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 type = subTab.dataset.type;
          const countEl = subTab.querySelector('[data-role="count"]');
          if (countEl && type) {
            countEl.textContent = String(this._subTabItemCount(this._currentCategory, type));
          }
        });
      }
    }
    /**
     * 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, or in-pane search results.
     * Header page search is managed separately via `_updateHeaderPageSearch`.
     */
    _renderCategory(categoryKey) {
      if (!this._categories || !this._categories[categoryKey]) return;
      this._teardownPageResultsBridge();
      this._pageResultsIframe = null;
      this._gridContainer.innerHTML = "";
      const pageResults = this._pageResults[categoryKey];
      if (pageResults?.url) {
        this._gridContainer.appendChild(
          this._createCategoryResultsView(document, categoryKey, pageResults)
        );
        try {
          this._updateTabCounts?.();
        } catch {
        }
        return;
      }
      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 {
        }
      }
    }
    /**
     * Show search results in the category's card area (iframe).
     * @param {string} categoryKey
     * @param {string} url
     * @param {string} [title]
     */
    _showCategoryPageResults(categoryKey, url, title = "Search results") {
      if (!categoryKey || !url) return;
      this._pageResults[categoryKey] = { url, title };
      if (this._currentCategory === categoryKey && this._gridContainer) {
        this._renderCategory(categoryKey);
      }
    }
    /**
     * Clear in-pane results and optionally restore the card grid.
     * @param {string} categoryKey
     * @param {{ render?: boolean }} [opts]
     */
    _clearCategoryPageResults(categoryKey, opts = {}) {
      if (!categoryKey) return;
      const render = opts.render !== false;
      if (this._pageResults[categoryKey]) {
        delete this._pageResults[categoryKey];
      }
      this._teardownPageResultsBridge();
      this._pageResultsIframe = null;
      if (render && this._currentCategory === categoryKey && this._gridContainer) {
        this._renderCategory(categoryKey);
      }
    }
    _teardownPageResultsBridge() {
      if (this._pageResultsBridgeTimer) {
        clearInterval(this._pageResultsBridgeTimer);
        this._pageResultsBridgeTimer = null;
      }
      if (this._pageResultsLoadTimeout) {
        clearTimeout(this._pageResultsLoadTimeout);
        this._pageResultsLoadTimeout = null;
      }
    }
    /**
     * 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);
    }
    /**
     * In-pane results view that replaces the card grid after a header search.
     * Embeds via the same declarativeNetRequest strategy as Link Preview /
     * launcher preview (`rules.json` strips X-Frame-Options + CSP on sub_frame).
     * @param {Document} doc
     * @param {string} categoryKey
     * @param {{ url: string, title: string }} results
     * @returns {HTMLElement}
     */
    _createCategoryResultsView(doc, categoryKey, results) {
      const wrap = doc.createElement("div");
      wrap.className = "kp-launcher-page-results";
      wrap.style.cssText = `
      display: flex;
      flex-direction: column;
      height: 100%;
      min-height: 0;
      gap: 0;
      background: #0f0f0f;
      border: 1px solid #333;
      border-radius: 8px;
      overflow: hidden;
    `;
      const toolbar = doc.createElement("div");
      toolbar.className = "kp-launcher-page-results-toolbar";
      toolbar.style.cssText = `
      display: flex;
      align-items: center;
      gap: 10px;
      padding: 8px 12px;
      border-bottom: 1px solid #333;
      background: #141414;
      flex-shrink: 0;
    `;
      const backBtn = doc.createElement("button");
      backBtn.type = "button";
      backBtn.textContent = "\u2190 Launch Deck";
      backBtn.style.cssText = `
      margin: 0;
      appearance: none;
      -webkit-appearance: none;
      padding: 6px 10px;
      background: #2a2a2a;
      border: 1px solid #444;
      border-radius: 6px;
      color: #fff;
      font-size: 12px;
      cursor: pointer;
      white-space: nowrap;
    `;
      backBtn.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        this._clearCategoryPageResults(categoryKey);
      });
      const titleEl = doc.createElement("div");
      titleEl.textContent = results.title || "Search results";
      titleEl.title = results.url;
      titleEl.style.cssText = `
      flex: 1;
      min-width: 0;
      color: #ddd;
      font-size: 13px;
      font-weight: 500;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    `;
      const { actions: openActions } = createPreviewOpenActionButtons({
        doc,
        getUrl: () => this._pageResults[categoryKey]?.url || results.url,
        afterOpen: () => {
          try {
            this.hide();
          } catch {
          }
        },
        afterOpenNewTab: () => {
          try {
            this.hide();
          } catch {
          }
        }
      });
      toolbar.appendChild(backBtn);
      toolbar.appendChild(titleEl);
      toolbar.appendChild(openActions);
      const frameWrap = doc.createElement("div");
      frameWrap.style.cssText = `
      position: relative;
      flex: 1;
      min-height: 0;
      background: #fff;
    `;
      const iframe = doc.createElement("iframe");
      iframe.className = "kp-launcher-page-results-iframe";
      iframe.title = results.title || "Search results";
      iframe.tabIndex = 0;
      iframe.style.cssText = `
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      border: 0;
      background: #fff;
      display: block;
    `;
      const errorEl = doc.createElement("div");
      errorEl.className = "kp-launcher-page-results-error";
      errorEl.style.cssText = `
      display: none;
      position: absolute;
      inset: 0;
      align-items: center;
      justify-content: center;
      flex-direction: column;
      gap: 12px;
      padding: 24px;
      text-align: center;
      color: #666;
      background: #f9f9f9;
      z-index: 1;
    `;
      const errorIcon = doc.createElement("div");
      errorIcon.textContent = "\u{1F6AB}";
      errorIcon.style.cssText = "font-size: 32px; color: #999;";
      const errorTitle = doc.createElement("div");
      errorTitle.textContent = "Cannot Display Page";
      errorTitle.style.cssText = "color: #333; font-size: 16px; font-weight: 600;";
      const errorMsg = doc.createElement("div");
      errorMsg.textContent = "This website prevents embedding in iframes for security reasons.";
      errorMsg.style.cssText = "font-size: 13px; max-width: 360px;";
      const openInTabButton = doc.createElement("button");
      openInTabButton.type = "button";
      openInTabButton.textContent = "Open in New Tab";
      openInTabButton.style.cssText = `
      margin: 0;
      appearance: none;
      -webkit-appearance: none;
      background: #4CAF50;
      color: white;
      border: none;
      padding: 10px 20px;
      border-radius: 4px;
      cursor: pointer;
      font-size: 14px;
      font-weight: 500;
    `;
      openInTabButton.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        const url = this._pageResults[categoryKey]?.url || results.url;
        if (!url) return;
        try {
          const a = doc.createElement("a");
          a.href = url;
          a.target = "_blank";
          a.rel = "noopener noreferrer";
          a.style.display = "none";
          doc.body.appendChild(a);
          a.click();
          a.remove();
        } catch {
          try {
            window.open(url, "_blank", "noopener,noreferrer");
          } catch {
          }
        }
        try {
          this.hide();
        } catch {
        }
      });
      errorEl.appendChild(errorIcon);
      errorEl.appendChild(errorTitle);
      errorEl.appendChild(errorMsg);
      errorEl.appendChild(openInTabButton);
      const showResultsError = () => {
        iframe.style.display = "none";
        errorEl.style.display = "flex";
      };
      const sendBridgeInit = () => {
        try {
          iframe.contentWindow?.postMessage({ type: "KP_POPOVER_BRIDGE_INIT" }, "*");
        } catch {
        }
      };
      this._teardownPageResultsBridge();
      this._pageResultsIframe = iframe;
      iframe.onerror = () => {
        console.log("[LauncherPopover] Page-results iframe load error detected");
        showResultsError();
      };
      this._pageResultsLoadTimeout = setTimeout(() => {
        this._pageResultsLoadTimeout = null;
        console.log("[LauncherPopover] Page-results iframe load timeout \u2014 showing error fallback");
        showResultsError();
      }, 3e4);
      iframe.onload = () => {
        if (this._pageResultsLoadTimeout) {
          clearTimeout(this._pageResultsLoadTimeout);
          this._pageResultsLoadTimeout = null;
        }
        console.log("[LauncherPopover] Page-results iframe loaded successfully");
        iframe.style.display = "block";
        errorEl.style.display = "none";
        sendBridgeInit();
      };
      iframe.src = results.url;
      sendBridgeInit();
      try {
        let attemptsLeft = 6;
        this._pageResultsBridgeTimer = setInterval(() => {
          if (attemptsLeft <= 0 || !this._pageResultsIframe || !this._isOpen) {
            if (this._pageResultsBridgeTimer) {
              clearInterval(this._pageResultsBridgeTimer);
              this._pageResultsBridgeTimer = null;
            }
            return;
          }
          attemptsLeft -= 1;
          sendBridgeInit();
        }, 250);
      } catch {
      }
      const minH = Math.max(320, Math.floor((this._gridContainer?.clientHeight || 480) - 8));
      wrap.style.minHeight = `${minH}px`;
      wrap.style.height = `${minH}px`;
      frameWrap.appendChild(iframe);
      frameWrap.appendChild(errorEl);
      wrap.appendChild(toolbar);
      wrap.appendChild(frameWrap);
      return wrap;
    }
    /**
     * 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;
    }
    /**
     * Collapse the preview pane and clear the loaded URL.
     */
    _hidePreview() {
      if (this._previewBridgeTimer) {
        clearInterval(this._previewBridgeTimer);
        this._previewBridgeTimer = null;
      }
      if (this._previewArea) {
        this._previewArea.style.width = "0";
      }
      this._currentPreviewUrl = null;
      if (this._previewIframe) {
        this._previewIframe.src = "about:blank";
        this._previewIframe.onload = null;
        this._previewIframe.onerror = null;
      }
      if (this._previewError) {
        this._previewError.style.display = "none";
      }
    }
    /**
     * Show preview iframe with URL using advanced bridge system.
     * Clicking preview again for the same URL collapses the pane.
     */
    _showPreview(url) {
      if (!this._previewArea || !this._previewIframe) return;
      if (url && this._currentPreviewUrl === url && this._previewArea.style.width && this._previewArea.style.width !== "0" && this._previewArea.style.width !== "0px") {
        this._hidePreview();
        return;
      }
      if (url && url.startsWith("file://")) {
        console.warn("[LauncherPopover] Cannot preview file:// URLs due to CSP restrictions");
        this._showPreviewError("Cannot preview local files");
        return;
      }
      this._currentPreviewUrl = url;
      this._previewArea.style.width = "40%";
      this._previewIframe.style.display = "flex";
      if (this._previewError) {
        this._previewError.style.display = "none";
      }
      if (this._previewBridgeTimer) {
        clearInterval(this._previewBridgeTimer);
        this._previewBridgeTimer = null;
      }
      const sendBridgeInit = () => {
        try {
          this._previewIframe.contentWindow?.postMessage({ type: "KP_POPOVER_BRIDGE_INIT" }, "*");
        } catch {
        }
      };
      this._previewIframe.onerror = () => {
        console.log("[LauncherPopover] Preview iframe load error detected");
        this._showPreviewError();
      };
      this._previewIframe.onload = () => {
        console.log("[LauncherPopover] Preview iframe loaded successfully");
        sendBridgeInit();
      };
      this._previewIframe.src = url;
      sendBridgeInit();
      try {
        let attemptsLeft = 6;
        this._previewBridgeTimer = setInterval(() => {
          if (attemptsLeft <= 0 || !this._previewIframe || !this._isOpen) {
            if (this._previewBridgeTimer) {
              clearInterval(this._previewBridgeTimer);
              this._previewBridgeTimer = null;
            }
            return;
          }
          attemptsLeft -= 1;
          sendBridgeInit();
        }, 250);
      } catch {
      }
    }
    /**
     * Show preview error message
     */
    _showPreviewError(message = null) {
      if (!this._previewArea) return;
      this._previewArea.style.width = "40%";
      this._previewIframe.style.display = "none";
      this._previewError.style.display = "flex";
      if (message) {
        this._errorMessage.textContent = message;
      } else {
        this._errorMessage.textContent = "This website prevents embedding in iframes for security reasons.";
      }
    }
    /**
     * Scroll to previous sheet
     */
    _scrollUp() {
      if (this._pageResults[this._currentCategory]?.url) return;
      if (this._currentSheet > 0) {
        this._currentSheet--;
        this._renderCategory(this._currentCategory);
      }
    }
    /**
     * Scroll to next sheet
     */
    _scrollDown() {
      if (this._pageResults[this._currentCategory]?.url) return;
      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/keypilot.js
  init_settings_manager();

  // 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/utils/text-at-point.js
  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;
      for (const s of new Intl.Segmenter(void 0, { granularity: "sentence" }).segment(text)) {
        if (offset >= s.index && offset < s.index + s.segment.length) {
          return { segment: s.segment, index: s.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 paragraphAtPoint(x, y, doc) {
    try {
      const range = caretRangeAtPoint(x, y, doc);
      const node = range?.startContainer;
      const el2 = node?.nodeType === Node.TEXT_NODE ? node.parentElement : node;
      const block = el2?.closest?.("p, li, blockquote, td, th, dd, dt, figcaption, article, section, div") || el2;
      return String(block?.innerText ?? block?.textContent ?? "").trim();
    } catch {
      return "";
    }
  }
  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 { text: paragraphAtPoint(x, y, doc), range: null };
    }
    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 = granularity === "sentence" ? sentenceSegmentAt(full, offset) : 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/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 deepElementFromPoint(x, y) {
    let el2 = null;
    try {
      el2 = document.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) break;
      el2 = nested;
    }
    return el2;
  }
  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 findHoveredImageSource(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 type = raw.type && raw.type.startsWith("image/") ? raw.type : "image/png";
    return { blob: raw, mimeType: type };
  }
  async function getHoveredImage(clientX, clientY, options = {}) {
    const source = findHoveredImageSource(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/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 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 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 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((a, b) => String(a.url || "").localeCompare(String(b.url || "")));
      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 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 filenameFromUrl(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 (isKeyPilotChrome2(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);
      if (allPageUrls.has(resolved)) {
        const existing = allPageUrls.get(resolved);
        if (existing && linkTitle && (!existing.label || existing.label === resolved)) {
          existing.label = linkTitle;
          if (!existing.element && el2) existing.element = el2;
        }
        return;
      }
      allPageUrls.set(resolved, {
        category: "url",
        kind: kind || "url",
        url: resolved,
        element: el2,
        label: linkTitle || filenameFromUrl(resolved) || resolved,
        ext,
        urlGroup: urlTabGroupForUrl(resolved, ext)
      });
    };
    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)) return;
      const ext = raw.ext || extensionFromUrl(resolved) || "";
      const label = raw.label || filenameFromUrl(resolved) || resolved.slice(0, 48);
      const item = {
        category: raw.category,
        kind: raw.kind || raw.category,
        url: resolved,
        element: raw.element || null,
        label,
        ext
      };
      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 || filenameFromUrl(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: filenameFromUrl(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: filenameFromUrl(resolved),
            posterUrl: posterResolved || void 0
          });
        }
      }
      if (tag === "SOURCE") {
        const urls = harvestAttrUrls(el2);
        for (const u of urls) addByExtension(u, el2, "source");
      }
      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);
    return [...byUrl.values(), ...allPageUrls.values(), ...pageTextItems];
  }
  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 || isKeyPilotChrome2(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: [] };
    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);
    }
    groups.url.sort((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;
      return String(a.url || "").localeCompare(String(b.url || ""));
    });
    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 imageSizeGroupForItem(item) {
    const w = Number(item?.width) || 0;
    const h = Number(item?.height) || 0;
    const edge = Math.max(w, h);
    if (!(edge > 0)) {
      return IMAGE_SIZE_GROUP_DEFS[IMAGE_SIZE_GROUP_DEFS.length - 1];
    }
    for (const def of IMAGE_SIZE_GROUP_DEFS) {
      if (def.id === "unknown") continue;
      if (edge >= def.minEdge && (def.maxEdge == null || edge <= def.maxEdge)) return def;
    }
    return IMAGE_SIZE_GROUP_DEFS[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 groupImagesByDimensionSize(items) {
    const buckets = /* @__PURE__ */ new Map();
    for (const def of IMAGE_SIZE_GROUP_DEFS) buckets.set(def.id, []);
    for (const item of items || []) {
      if (!item || item.category !== "image") continue;
      if (isVideoPosterItem(item)) continue;
      const def = imageSizeGroupForItem(item);
      const list = buckets.get(def.id);
      if (list) list.push(item);
    }
    const out = [];
    for (const def of IMAGE_SIZE_GROUP_DEFS) {
      const list = buckets.get(def.id) || [];
      if (!list.length) continue;
      list.sort((a, b) => {
        const aa = (Number(a.width) || 0) * (Number(a.height) || 0);
        const bb = (Number(b.width) || 0) * (Number(b.height) || 0);
        if (bb !== aa) return bb - aa;
        return String(a.label || "").localeCompare(String(b.label || ""));
      });
      out.push({
        id: def.id,
        label: def.label,
        sortOrder: def.sortOrder,
        items: list
      });
    }
    return out;
  }
  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;
    }
  }
  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 type = String.fromCharCode(
          view.getUint8(offset2 + 4),
          view.getUint8(offset2 + 5),
          view.getUint8(offset2 + 6),
          view.getUint8(offset2 + 7)
        );
        const dataStart = offset2 + 8;
        if (type === "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 (type === "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 type = 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 && type === 5 && count === 1) {
              xRes = readRational(tiffStart + u32(valOff));
            } else if (tag === 283 && type === 5 && count === 1) {
              yRes = readRational(tiffStart + u32(valOff));
            } else if (tag === 296 && type === 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;
  }
  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 m = ct.match(/image\/([a-z0-9.+-]+)/i);
            if (m) {
              let sub = m[1].toLowerCase();
              if (sub === "jpeg") sub = "jpg";
              if (sub === "svg+xml") sub = "svg";
              out.ext = sub.split("+")[0];
            }
          }
        }
      } 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 (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();
  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 _overlay = null;
  var _onClose = 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;
  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 = _onClose;
    _onClose = null;
    _items = [];
    _imageFlatList = [];
    _cardByItem = /* @__PURE__ */ new WeakMap();
    _enrichGen += 1;
    _notify = () => {
    };
    _onSendToMediaLibrary = async () => {
    };
    _imageScaleSlider = IMAGE_SCALE_DEFAULT;
    _imageAspectMode = IMAGE_ASPECT_DEFAULT;
    _aspectControl = null;
    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 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 });
    }
    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 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);
    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 wrap = document.createElement("div");
    wrap.className = "kpv2-page-media-scale";
    wrap.title = "Overlay content scale (1\xD7\u20132.5\xD7, all tabs)";
    const label = document.createElement("span");
    label.className = "kpv2-page-media-scale-label";
    label.textContent = "Scale";
    const minTag = document.createElement("span");
    minTag.className = "kpv2-page-media-scale-edge";
    minTag.textContent = "1\xD7";
    const range = document.createElement("input");
    range.type = "range";
    range.className = "kpv2-page-media-scale-range";
    range.min = String(IMAGE_SCALE_SLIDER_MIN);
    range.max = String(IMAGE_SCALE_SLIDER_MAX);
    range.step = String(IMAGE_SCALE_SLIDER_STEP);
    range.value = String(_imageScaleSlider);
    range.setAttribute("aria-label", "Page Media content scale");
    const maxTag = document.createElement("span");
    maxTag.className = "kpv2-page-media-scale-edge";
    maxTag.textContent = "2.5\xD7";
    const value = document.createElement("span");
    value.className = "kpv2-page-media-scale-value";
    value.textContent = formatImageScaleReadout(_imageScaleSlider);
    const onSlide = () => {
      const next = normalizeImageScaleSlider(range.value);
      applyImageScale(next);
      persistImageScalePreference(next);
    };
    range.addEventListener("input", onSlide, true);
    range.addEventListener("change", onSlide, true);
    wrap.appendChild(label);
    wrap.appendChild(minTag);
    wrap.appendChild(range);
    wrap.appendChild(maxTag);
    wrap.appendChild(value);
    return wrap;
  }
  async function openPageMediaOverlay({ items, onClose, onNotify, onSendToMediaLibrary } = (
    /** @type {any} */
    {}
  )) {
    closePageMediaOverlay();
    _items = Array.isArray(items) ? items.slice() : [];
    _onClose = typeof onClose === "function" ? onClose : null;
    _notify = typeof onNotify === "function" ? onNotify : () => {
    };
    _onSendToMediaLibrary = typeof onSendToMediaLibrary === "function" ? onSendToMediaLibrary : async () => {
      _notify("Media Library is not built yet \u2014 coming soon", "info");
    };
    await loadImageScalePreference();
    await loadImageAspectPreference();
    const groups = groupPageMediaByCategory(_items);
    if (groups.image.length) _activeTab = "image";
    else if (groups.video.length) _activeTab = "video";
    else if (groups.pageText.length) _activeTab = "pageText";
    else if (groups.text.length) _activeTab = "text";
    else if (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;
    ensureStyles2(mount);
    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 + groups.pageText.length;
    total.textContent = String(mediaCount || groups.url.length);
    total.title = groups.url.length ? `${mediaCount} media \xB7 ${groups.url.length} unique URLs` : `${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: "url", label: "URLs" }
      ]
    );
    for (const { id, label } of tabDefs) {
      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";
    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());
    shell.appendChild(content);
    shell.appendChild(fullView);
    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();
    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();
  }
  function setActiveTab(tab) {
    _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();
  }
  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");
    const groups = groupPageMediaByCategory(_items);
    const list = groups[
      /** @type {'video'|'text'|'url'|'pageText'} */
      _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." : `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");
      const listEl = document.createElement("div");
      listEl.className = "kpv2-page-media-url-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" });
      });
      const flat = [];
      for (const domain of domains) {
        const items = byDomain.get(domain) || [];
        items.sort((a, b) => String(a.url || "").localeCompare(String(b.url || "")));
        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) {
          let host = rows;
          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);
            const subRows = document.createElement("div");
            subRows.className = "kpv2-page-media-url-subpath-rows";
            sub.appendChild(subRows);
            rows.appendChild(sub);
            host = subRows;
          }
          for (const item of pathItems) {
            const flatIndex = flat.length;
            flat.push(item);
            host.appendChild(buildUrlRow(item, () => onItemActivate(flat, flatIndex), {
              displayUrl: prefix ? urlPathRelativeToPrefix(item.url, prefix) : urlPathDisplay(item.url)
            }));
          }
        }
        section.appendChild(rows);
        listEl.appendChild(section);
      }
      content.appendChild(listEl);
      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 sizeGroups = groupImagesByDimensionSize(photos);
    const flat = [];
    const photoItems = sizeGroups.flatMap((g) => g.items);
    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 (posters.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(posters.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 posters) {
        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 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";
    meta.textContent = formatImageFooterMeta(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();
    };
    card.addEventListener("click", activate, true);
    card.addEventListener("keydown", (e) => {
      if (e.key === "Enter" || e.key === " ") activate(e);
    }, true);
    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";
    meta.textContent = item.ext ? `.${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();
    };
    card.addEventListener("click", activate, true);
    card.addEventListener("keydown", (e) => {
      if (e.key === "Enter" || e.key === " ") activate(e);
    }, true);
    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();
    };
    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 buildUrlRow(item, onActivate, opts = {}) {
    const row = document.createElement("div");
    row.className = "kpv2-page-media-url-row";
    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";
    const bits = [];
    if (item.ext) bits.push(`.${item.ext}`);
    if (item.kind && item.kind !== "url") bits.push(item.kind);
    meta.textContent = bits.join(" \xB7 ") || "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();
    };
    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";
    meta.textContent = item.ext ? `.${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();
    };
    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)));
    bar.appendChild(mk("Send", "Send to Media Library", () => sendItemToMediaLibrary(item)));
    bar.appendChild(mk("Download", "Download file", () => downloadItem(item)));
    return bar;
  }
  async function copyItemToPasteboard(item) {
    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 type = mimeType && String(mimeType).startsWith("image/") ? String(mimeType) : blob.type && blob.type.startsWith("image/") ? blob.type : "image/png";
    let outBlob = blob;
    let outType = type;
    if (type !== "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) meta.textContent = formatImageFooterMeta(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 formatImageFooterMeta(item) {
    return [formatImageFileType(item), formatImageDpi(item)].join(" \xB7 ");
  }
  function onItemActivate(list, index) {
    const item = list[index];
    if (!item) return;
    if (_activeTab === "pageText") {
      copyItemToPasteboard(item);
      return;
    }
    if (_activeTab === "text" || _activeTab === "url") {
      openUrl(item.url);
      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 || "";
      host.appendChild(img);
    }
    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 ensureStyles2(root) {
    if (!root) return;
    const c = NCT_DARK_UI_COLORS;
    const css = `
.kpv2-page-media-shell {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  background: rgba(15, 15, 16, 0.92);
  font-family: ${NCT_DARK_UI_FONT};
  color: ${c.fg};
}
.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-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 {
  font-size: 11px;
}
.kpv2-page-media-title-wrap {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 120px;
  flex-shrink: 0;
}
.kpv2-page-media-scale {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  padding: 3px 8px;
  border: 1px solid ${c.panelEdgeDark};
  border-radius: ${NCT_DARK_UI_BTN_RADIUS};
  background: ${c.fieldBg};
  box-shadow: 0 0 0 1px ${c.panelEdge} inset;
}
.kpv2-page-media-scale-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: ${c.fgMute};
  user-select: none;
}
.kpv2-page-media-scale-edge {
  font-size: 10px;
  color: ${c.fgDim};
  font-variant-numeric: tabular-nums;
  user-select: none;
}
.kpv2-page-media-scale-value {
  min-width: 2.6em;
  font-size: 11px;
  font-weight: 600;
  color: ${c.fg};
  font-variant-numeric: tabular-nums;
  text-align: right;
  user-select: none;
}
.kpv2-page-media-scale-range {
  -webkit-appearance: none;
  appearance: none;
  width: 88px;
  height: 4px;
  border-radius: 2px;
  background: linear-gradient(90deg, ${c.panelEdgeDark} 0%, ${c.accent} 100%);
  outline: none;
  cursor: pointer;
  margin: 0;
  vertical-align: middle;
}
.kpv2-page-media-scale-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 12px;
  height: 12px;
  border-radius: 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;
}
.kpv2-page-media-scale-range::-moz-range-thumb {
  width: 12px;
  height: 12px;
  border-radius: 2px;
  background: #3a5f7a;
  border: ${NCT_DARK_UI_BTN_LIT_BORDER};
  box-shadow: 0 1px 3px rgba(0,0,0,0.55);
  cursor: pointer;
}
.kpv2-page-media-scale-range::-moz-range-track {
  height: 4px;
  border-radius: 2px;
  background: linear-gradient(90deg, ${c.panelEdgeDark} 0%, ${c.accent} 100%);
}
.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-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(8px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-url-subpath {
  gap: calc(6px * var(--kpv2-pm-image-scale, 1.5));
  padding: calc(8px * var(--kpv2-pm-image-scale, 1.5));
  flex-basis: calc(280px * 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-subpath-rows {
  gap: calc(6px * var(--kpv2-pm-image-scale, 1.5));
}
.kpv2-page-media-content .kpv2-page-media-url-row {
  flex-basis: calc(180px * var(--kpv2-pm-image-scale, 1.5));
  max-width: calc(320px * var(--kpv2-pm-image-scale, 1.5));
  min-width: calc(140px * var(--kpv2-pm-image-scale, 1.5));
  gap: calc(6px * var(--kpv2-pm-image-scale, 1.5));
  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-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-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: row;
  flex-wrap: wrap;
  gap: 8px;
  align-items: stretch;
}
.kpv2-page-media-url-subpath {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px;
  border: 1px solid ${c.panelEdgeDark};
  border-radius: 2px;
  background: rgba(0, 0, 0, 0.18);
  min-width: min(100%, 220px);
  flex: 1 1 280px;
  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-subpath-rows {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 6px;
  align-items: stretch;
}
.kpv2-page-media-url-group .kpv2-page-media-url-row {
  margin-bottom: 0;
  flex: 1 1 180px;
  max-width: 320px;
  min-width: 140px;
}
.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;
  gap: 6px;
  padding: 8px 10px;
  border: 1px solid ${c.panelEdgeDark};
  border-radius: 3px;
  background: ${c.fieldBg};
  box-shadow: none;
  cursor: pointer;
  color: inherit;
}
.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;
}
.kpv2-page-media-url-text {
  font-size: 11px;
  color: ${c.fgDim};
  word-break: break-all;
  line-height: 1.35;
}
.kpv2-page-media-url-meta {
  margin-top: 2px;
  font-size: 10px;
  color: ${c.fgMute};
  text-transform: lowercase;
}
.kpv2-page-media-url-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;
}
.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;
}
.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;
}
.kpv2-page-media-fullview {
  display: none;
  position: fixed;
  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;
}
.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-header {
    flex-wrap: wrap;
  }
  .kpv2-page-media-scale-range {
    width: 72px;
  }
}
`;
    injectChromeStyles(root, {
      attr: "data-kp-page-media-styles",
      css
    });
  }

  // src/utils/scroll-at-point.js
  var EDGE_EPS = 1;
  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 elementFromPointDeep(x, y, doc = document) {
    try {
      let el2 = doc.elementFromPoint(x, y);
      let guard = 0;
      while (el2 && el2.shadowRoot && guard++ < 10) {
        const nested = el2.shadowRoot.elementFromPoint(x, y);
        if (!nested || nested === el2) break;
        el2 = nested;
      }
      return el2 || 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;
    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 = elementFromPointDeep(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} */
      composedParent(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 = composedParent(n);
          continue;
        }
        if (n.classList) {
          let skip = false;
          n.classList.forEach((c) => {
            if (typeof c === "string" && c.startsWith("kpv2-")) skip = true;
          });
          if (skip) {
            n = composedParent(n);
            continue;
          }
        }
      } catch {
      }
      const cap = getScrollCapacity(n, doc);
      if (cap.canY || cap.canX) {
        if (isDocumentScrollRoot(n, doc)) {
          seenDocRoot = n;
          n = composedParent(n);
          continue;
        }
        const axis = pickAxis(cap, n, sign);
        if (axis) return { el: n, axis };
      }
      n = composedParent(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 findScrollableAtPoint(clientX, clientY, 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 = elementFromPointDeep(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} */
      composedParent(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 = composedParent(n);
        continue;
      }
      const cap = getScrollCapacity(n, doc);
      if (cap.canY || cap.canX) {
        if (isDocumentScrollRoot(n, doc)) {
          seenDocRoot = n;
          n = composedParent(n);
          continue;
        }
        return { el: n, canX: cap.canX, canY: cap.canY };
      }
      n = composedParent(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 {
        if (win && typeof win.scrollBy === "function") {
          win.scrollBy({ left: dx, top: dy, behavior });
          return { scrolled: true, el: doc.scrollingElement || doc.documentElement || 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 {
        if (win && typeof win.scrollBy === "function") {
          win.scrollBy({ top: s * amount, left: 0, behavior });
          return { scrolled: true, axis: "y", el: doc.scrollingElement || doc.documentElement || 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;
    }
    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));
          win.scrollTo({ top, left: win.pageXOffset || 0, 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/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.origin = { x: 0, y: 0 };
      this._usingPopover = 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.body || document.documentElement).appendChild(this.root);
        }
      } catch {
      }
      if (this._usingPopover) {
        try {
          if (typeof this.root.showPopover === "function" && !this.root.matches(":popover-open")) {
            this.root.showPopover();
          }
        } catch {
        }
      }
      this.root.hidden = false;
    }
    /**
     * @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 {
      }
    }
    hide() {
      if (!this.root) return;
      if (this._usingPopover) {
        try {
          if (typeof this.root.hidePopover === "function" && this.root.matches(":popover-open")) {
            this.root.hidePopover();
          }
        } catch {
        }
      }
      try {
        this.root.hidden = true;
      } catch {
      }
    }
    destroy() {
      this.hide();
      try {
        this.root?.remove?.();
      } catch {
      }
      this.root = null;
      this.svg = null;
      this.line = null;
      this.dot = 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;
      const popoverOk = typeof HTMLElement !== "undefined" && "popover" in HTMLElement.prototype && typeof root.showPopover === "function";
      this._usingPopover = popoverOk;
      if (popoverOk) {
        try {
          root.popover = "manual";
        } catch {
          try {
            root.setAttribute("popover", "manual");
          } catch {
          }
        }
      }
      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"
      ].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(svg);
      this.root = root;
      this.svg = svg;
      this.line = line;
      this.dot = dot;
    }
  };

  // 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/keypilot.js
  var KeyPilot = class _KeyPilot extends EventManager {
    constructor() {
      super();
      if (window.__KeyPilotV22) {
        console.warn("[KeyPilot] Already loaded.");
        return;
      }
      window.__KeyPilotV22 = true;
      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.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.KEYBOARD_HELP_STORAGE_KEY = "keypilot_keyboard_help_visible";
      this._keyboardHelpVisible = false;
      this._keyboardHelpStorageListener = null;
      this._scrollLineOverlay = new ScrollLineOverlay();
      this._scrollLineRaf = 0;
      this._scrollLineLastTs = 0;
      this._scrollLineTarget = null;
      this._scrollLineOrigin = { x: 0, y: 0 };
      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()
      });
      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 (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-")) return true;
          const cl = n.classList;
          if (cl && cl.length) {
            for (const c of cl) {
              if (typeof c !== "string" || !c.startsWith("kpv2-")) continue;
              if (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 instanceof Element)) return false;
      const iframeContainer = this.overlayManager?.popoverContainer;
      if (iframeContainer instanceof Element && containsComposed(iframeContainer, el2)) {
        return true;
      }
      const modalPanel = this.overlayManager?.popupManager?.top()?.panel;
      if (modalPanel instanceof Element && containsComposed(modalPanel, el2)) {
        return true;
      }
      return false;
    }
    /**
     * 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);
        }
      } catch {
      }
      try {
        if (under && this._isKeyPilotUiElement(under)) {
          const allowUi = this._isElementInPopover(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;
      if (this.activator.handleSmartActivate(target, currentState.lastMouse.x, currentState.lastMouse.y)) {
        this.showRipple(currentState.lastMouse.x, currentState.lastMouse.y);
        this.overlayManager.flashFocusOverlay(target);
        this.postClickRefresh(target, currentState.lastMouse.x, currentState.lastMouse.y);
      } else {
        this.activator.smartClick(target, currentState.lastMouse.x, currentState.lastMouse.y);
        this.showRipple(currentState.lastMouse.x, currentState.lastMouse.y);
        this.overlayManager.flashFocusOverlay(target);
        this.postClickRefresh(target, currentState.lastMouse.x, currentState.lastMouse.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() {
      this.setupStyles();
      this.setupShadowDOMSupport();
      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();
      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 {
        this._applyKeyboardLayoutFromSettings();
      } catch {
      }
      try {
        void 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 {
      }
    }
    /**
     * 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) {
          this.floatingKeyboardHelp.setKeybindings(this.keybindings);
          if (typeof this.floatingKeyboardHelp.setKeyboardLayout === "function") {
            this.floatingKeyboardHelp.setKeyboardLayout({
              keyboardLayout: this._keyboardUiLayout,
              layoutId: this._keyboardLayoutId
            });
          }
        }
      } catch {
      }
    }
    /** Built-in Function ids read via {@link _getBuiltinFunctionActionParams} in hot key handlers. */
    static get BUILTIN_FUNCTION_ACTION_IDS() {
      return FIXED_KEY_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 slot = String(key).trim().toUpperCase();
        if (!slot) return false;
        const assigned = slots[slot];
        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 Config.`,
            "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);
          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]
     * @returns {Promise<any>}
     */
    async _runMacroStep(step, e, macroId) {
      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 });
      } 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 "Press F to select text field" / "press Esc to exit" SVG
     * background-images onto hovered/focused text inputs (via StyleManager CSS vars).
     */
    _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: `Press ${activate} to select text field`,
        focus: `press ${cancel} to exit`
      });
    }
    installSettingsSync() {
      if (this._settingsStorageListener) return;
      try {
        this._settingsStorageListener = (changes, area) => {
          if (area !== "sync" && area !== "local") return;
          if (!changes || !changes[SETTINGS_STORAGE_KEY]) return;
          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 type = s?.textMode?.cursorType || DEFAULT_SETTINGS.textMode.cursorType;
        if (type === "crosshair") {
          return { hasClickableElement };
        }
        return { cursorType: "t_square", hasClickableElement };
      }
      if (mode === MODES.NONE || mode === MODES.POPOVER || mode === MODES.SCROLL_LINE) {
        const type = s?.clickMode?.cursor?.type || DEFAULT_SETTINGS.clickMode.cursor.type;
        if (type === "native_arrow") return { cursorType: "native_arrow" };
        if (type === "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 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;
      await storageSetValue(this.KEYBOARD_HELP_STORAGE_KEY, Boolean(visible), {
        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 } = {}) {
      const next = Boolean(visible);
      this._keyboardHelpVisible = next;
      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 {
          }
          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") {
            this.floatingKeyboardHelp.setActiveLayoutSelection({
              currentKeyboardLayoutId: this._currentKeyboardLayoutId || "builtin",
              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, type) => {
            const colorMap = {
              "success": COLORS.NOTIFICATION_SUCCESS,
              "warning": COLORS.NOTIFICATION_WARNING || COLORS.NOTIFICATION_INFO,
              "error": COLORS.NOTIFICATION_ERROR,
              "info": COLORS.NOTIFICATION_INFO
            };
            const backgroundColor = colorMap[type] || 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() {
      chrome.runtime.onMessage.addListener((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();
          } 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_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);
          }
        }
      });
    }
    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 {
        }
      }
      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) {
        this.updateOverlays(newState.focusEl, newState.inspectorEl, newState.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 (!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 {
        if (window !== window.top && window.__KP_POPOVER_IFRAME) {
          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();
              try {
                window.parent.postMessage({ type: "KP_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.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 {
          const pm = this.overlayManager?.popupManager;
          if (pm?.isOpen?.()) {
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();
            pm.requestCloseTop?.();
            return;
          }
        } catch {
        }
      }
      if (this.launcherPopover?.isOpen?.()) {
        const handled = this.launcherPopover.handleKeyDown(e);
        if (handled) {
          return;
        }
      }
      if (currentState.mode === MODES.OMNIBOX) {
        if (KB.CANCEL?.keys?.includes?.(e.key)) {
          e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          this.handleCloseOmnibox();
        }
        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;
        }
        if (KB.PAGE_UP?.keys?.includes?.(e.key)) {
          e.preventDefault();
          this.overlayManager?.scrollPopoverBy?.(-this._getPageScrollPx(), this._getScrollBehavior());
          return;
        }
        if (KB.PAGE_DOWN?.keys?.includes?.(e.key)) {
          e.preventDefault();
          this.overlayManager?.scrollPopoverBy?.(this._getPageScrollPx(), this._getScrollBehavior());
          return;
        }
        if (KB.PAGE_UP_INSTANT?.keys?.includes?.(e.key)) {
          e.preventDefault();
          this.overlayManager?.scrollPopoverBy?.(-this._getHalfPageScrollPx(), this._getScrollBehavior());
          return;
        }
        if (KB.PAGE_DOWN_INSTANT?.keys?.includes?.(e.key)) {
          e.preventDefault();
          this.overlayManager?.scrollPopoverBy?.(this._getHalfPageScrollPx(), this._getScrollBehavior());
          return;
        }
        if (KB.PAGE_TOP?.keys?.includes?.(e.key)) {
          e.preventDefault();
          this._scrollPopoverToEdge(-1);
          return;
        }
        if (KB.PAGE_BOTTOM?.keys?.includes?.(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.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.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");
    }
    /**
     * 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 type = "text";
        try {
          type = String(el2.type || "text").toLowerCase();
        } catch {
        }
        if (type === "button" || type === "submit" || type === "reset" || type === "checkbox" || type === "radio" || type === "file" || type === "image" || type === "range" || type === "color" || type === "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 {
        let active2 = document.activeElement;
        let guard = 0;
        while (active2 && active2.shadowRoot && active2.shadowRoot.activeElement && guard++ < 10) {
          active2 = active2.shadowRoot.activeElement;
        }
        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) || elementFromPointDeep(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;
      }
      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
        );
      });
    }
    handleScroll(e) {
      if (!this.enabled) {
        return;
      }
      return;
    }
    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++;
      let under = underHint || null;
      if (!under) under = this.detector.deepElementFromPoint(x, y);
      let clickable = null;
      try {
        clickable = this.detector.findClickable(under);
      } catch {
        clickable = null;
      }
      if (currentState.mode === MODES.POPOVER) {
        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 (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()) {
        this.updateSelection();
      }
    }
    /**
     * 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);
    }
    handlePageUp(e) {
      if (!this._allowActionKey("handlePageUp", e)) return;
      window.scrollBy({
        top: -this._getPageScrollPx(),
        behavior: this._getScrollBehavior()
      });
      this.emitAction("scrollUp");
    }
    handlePageDown(e) {
      if (!this._allowActionKey("handlePageDown", e)) return;
      window.scrollBy({
        top: this._getPageScrollPx(),
        behavior: this._getScrollBehavior()
      });
      this.emitAction("scrollDown");
    }
    handleInstantPageUp(e) {
      if (!this._allowActionKey("handleInstantPageUp", e)) return;
      this._scrollHalfPageAtCursor(-1);
      this.emitAction("scrollUp");
    }
    handleInstantPageDown(e) {
      if (!this._allowActionKey("handleInstantPageDown", e)) return;
      this._scrollHalfPageAtCursor(1);
      this.emitAction("scrollDown");
    }
    /**
     * 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).
     *
     * @param {number} sign  -1 = C (up/left), +1 = V (down/right)
     */
    _scrollHalfPageAtCursor(sign) {
      const delta = this._getHalfPageScrollPx();
      const behavior = this._getScrollBehavior();
      const s = sign < 0 ? -1 : 1;
      const { x, y } = this._getScrollCursorPoint();
      if (this._tryScrollIframeUnderCursor(x, y, s, behavior, { mode: "delta", deltaPx: delta })) {
        return;
      }
      scrollAtPoint(x, y, s, delta, behavior);
    }
    /**
     * 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) || elementFromPointDeep(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
     */
    _runEdgeJump(fn, coverEl) {
      const run = () => {
        try {
          fn();
        } catch {
        }
      };
      if (this.overlayManager?.runEdgeJumpFade) {
        void this.overlayManager.runEdgeJumpFade(run, { coverEl: coverEl || null });
        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));
    }
    /**
     * 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 ? "auto" : 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);
    }
    /**
     * 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) || elementFromPointDeep(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 = elementFromPointDeep(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 clickable outlines do not
     * update while 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 Scroll Line 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);
      this.state.setMode(MODES.SCROLL_LINE);
      try {
        this._scrollLineOverlay?.show(x, y);
      } catch {
      }
      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;
      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) || elementFromPointDeep(x, y);
      } catch {
        under = null;
      }
      if (under && (under.tagName === "IFRAME" || under.tagName === "FRAME")) {
        try {
          if (!this._isKeyPilotUiElement?.(under)) {
            const iframe = (
              /** @type {HTMLIFrameElement} */
              under
            );
            const rect = iframe.getBoundingClientRect();
            if (rect && rect.width > 0 && rect.height > 0) {
              return {
                kind: "iframe",
                iframe,
                localX: x - rect.left,
                localY: y - rect.top
              };
            }
          }
        } catch {
        }
      }
      const found = findScrollableAtPoint(x, y);
      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) return { kind: "element", el: se, canX: true, canY: true };
      return null;
    }
    /**
     * 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 {
      }
      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 (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 !== "KP_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);
    }
    /**
     * 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");
        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;
      }
      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;
      }
      this._completingHighlight = true;
      const currentState = this.state.getState();
      const selectionMode = this.overlayManager.getSelectionMode();
      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) or Media Library (coming soon).
     * @param {KeyboardEvent} [_e]
     * @param {{ destination?: string }} [parameters]
     */
    async handleCopyHoveredImageKey(_e, parameters) {
      const destination = 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;
      }
      if (destination === ACTION_RESULT_DESTINATIONS.MEDIA_LIBRARY) {
        this.handleMediaLibraryNotAvailableKey();
        return;
      }
      try {
        const ok = await this.copyImageToClipboard(result.blob, result.mimeType);
        if (ok) {
          try {
            this.overlayManager?.flashImageCopyPulse?.(result.element);
          } catch {
          }
          const label = result.kind === "background" ? "Background image" : result.kind === "svg" ? "SVG" : result.kind === "video" ? "Video thumbnail" : "Image";
          this.showFlashNotification(
            `${label} copied to clipboard`,
            COLORS.NOTIFICATION_SUCCESS,
            result.blob
          );
          this.emitAction("copy_hovered_image", {
            kind: result.kind,
            url: result.url ? String(result.url).slice(0, 200) : ""
          });
        } else {
          this.showFlashNotification("Could not copy image", COLORS.NOTIFICATION_ERROR);
        }
      } catch (error) {
        console.warn("[KeyPilot] copyImageToClipboard failed:", error);
        let message = "Could not copy image";
        if (error?.name === "NotAllowedError" || /permission/i.test(error?.message || "")) {
          message = "Clipboard permission denied";
        } else if (/secure context/i.test(error?.message || "")) {
          message = "Clipboard requires HTTPS";
        }
        this.showFlashNotification(message, COLORS.NOTIFICATION_ERROR);
      }
    }
    /**
     * Copy the hyperlink under the cursor (U on right-handed layout).
     * Destination is clipboard (default) or Media Library (coming soon).
     * @param {KeyboardEvent} [_e]
     * @param {{ destination?: string }} [parameters]
     */
    async handleCopyHoveredUrlKey(_e, parameters) {
      const destination = this._resolveCopyDestination("COPY_HOVERED_URL", parameters);
      const url = this._getHoveredHyperlinkUrl();
      if (!url) {
        this.showFlashNotification("No URL under cursor", COLORS.NOTIFICATION_INFO);
        return;
      }
      if (destination === ACTION_RESULT_DESTINATIONS.MEDIA_LIBRARY) {
        this.handleMediaLibraryNotAvailableKey();
        return;
      }
      const ok = await this.copyToClipboard(url);
      this.showFlashNotification(
        ok ? "URL copied to clipboard" : "Could not copy URL",
        ok ? COLORS.NOTIFICATION_SUCCESS : COLORS.NOTIFICATION_ERROR
      );
      if (ok) this.emitAction("copy_hovered_url", { url: String(url).slice(0, 200) });
    }
    /**
     * @param {string} functionId
     * @param {{ destination?: string }|null|undefined} parameters
     * @returns {import('./modules/action-result-delivery.js').ActionResultDestination}
     */
    _resolveCopyDestination(functionId, parameters) {
      return normalizeActionResultDestination(
        parameters?.destination ?? getActionParameter(this._getBuiltinFunctionActionParams(functionId), functionId, "destination"),
        ACTION_RESULT_DESTINATIONS.CLIPBOARD
      );
    }
    /**
     * 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) {
        let probe = target;
        let guard = 0;
        while (probe && guard++ < 12) {
          const a = probe.tagName === "A" ? probe : probe.closest?.("a[href]");
          if (a && /** @type {HTMLAnchorElement} */
          a.href) {
            return String(
              /** @type {HTMLAnchorElement} */
              a.href
            );
          }
          const roleLink = probe.getAttribute?.("role") === "link" ? probe : probe.closest?.('[role="link"]');
          if (roleLink?.dataset?.kpUrl) return String(roleLink.dataset.kpUrl);
          const root = probe.getRootNode?.();
          if (!(root instanceof ShadowRoot) || !(root.host instanceof Element)) break;
          probe = root.host;
        }
      }
      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, and document links;
     * 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, type) => {
            const color = type === "error" ? COLORS.NOTIFICATION_ERROR : type === "success" ? COLORS.NOTIFICATION_SUCCESS : COLORS.NOTIFICATION_INFO;
            this.showFlashNotification(String(message || ""), color);
          },
          onSendToMediaLibrary: (item) => {
            this.showFlashNotification(
              "Media Library is not built yet \u2014 coming soon",
              COLORS.NOTIFICATION_INFO
            );
            try {
              this.emitAction("page_media_send_to_library", {
                category: item?.category || "",
                url: item?.url ? String(item.url).slice(0, 200) : ""
              });
            } catch {
            }
          }
        }).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 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._execDocumentCommand("copy")) {
        this.showFlashNotification("Copied", COLORS.NOTIFICATION_SUCCESS);
        this.emitAction("clipboard_copy", { via: "execCommand" });
        return;
      }
      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 });
    }
    async handleClipboardCutKey() {
      if (this._execDocumentCommand("cut")) {
        this.showFlashNotification("Cut", COLORS.NOTIFICATION_SUCCESS);
        this.emitAction("clipboard_cut", { via: "execCommand" });
        return;
      }
      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 });
      } else {
        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);
    }
    /**
     * 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 });
    }
    /**
     * 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 });
    }
    /**
     * GET_TEXT_RANGE Function handler — reads the current highlight/selection (set up beforehand)
     * and copies it to the clipboard. Same acquisition as `handleClipboardCopyKey`, but as an
     * explicit, addressable Function for future macro composition rather than a fixed key.
     */
    async handleGetTextRangeKey() {
      const text = this.getSelectedPlainText();
      if (!text.trim()) {
        this.showFlashNotification("Nothing selected \u2014 set up a text range first", 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_range", { length: text.length });
    }
    /**
     * 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 = elementFromPointDeep(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,
     * via the same on-device AI provider `SEND_TEXT_TO_AI` uses (`ai-text-service.js`). No setup
     * required — this is the "stock, zero-configuration" example from KEY_ACTION_ARCHITECTURE.md.
     */
    async handleLookupWordKey() {
      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: word } = getTextAtPoint(x, y, { granularity: "word" });
      if (!word.trim()) {
        this.showFlashNotification("No word under cursor", COLORS.NOTIFICATION_INFO);
        return;
      }
      this.showFlashNotification("Looking up\u2026", COLORS.NOTIFICATION_INFO);
      let result;
      try {
        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
        });
      } catch (e) {
        console.warn("[KeyPilot] Lookup failed:", e);
        this.showFlashNotification("Lookup failed", COLORS.NOTIFICATION_ERROR);
        return;
      }
      if (!result?.ok) {
        this.showFlashNotification(result?.error || "Lookup failed", COLORS.NOTIFICATION_ERROR);
        return;
      }
      await deliverActionResult(this, {
        text: result.text,
        title: `Definition \u2014 ${word}`,
        destination: ACTION_RESULT_DESTINATIONS.POPOVER
      });
      this.emitAction("lookup_word", { word });
    }
    /**
     * 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 Function handler — shows the Action Instance's configured `content` in a
     * popover. A display primitive: takes a static configured value today, but formalizes the
     * same "render a result" behavior `deliverActionResult`'s `popover` branch already has baked
     * in, so it can become an explicit macro Step once the macro builder passes a previous Step's
     * output forward — see KEY_ACTION_ARCHITECTURE.md "SHOW_POPOVER as a composable primitive".
     * @param {KeyboardEvent} _e
     * @param {{ content?: string }} [parameters]
     */
    async handleShowPopoverKey(_e, parameters) {
      const content = String(parameters?.content ?? "").trim();
      if (!content) {
        this.showFlashNotification("No content configured for this key", COLORS.NOTIFICATION_INFO);
        return;
      }
      await deliverActionResult(this, {
        text: content,
        title: "Popover",
        destination: ACTION_RESULT_DESTINATIONS.POPOVER
      });
    }
    /**
     * Shared handler for `ADD_URL_TO_MEDIA_LIBRARY` / `FETCH_URL_FOR_MEDIA_LIBRARY` — both are
     * catalog entries only until a real Media Library sink exists (see `action-result-delivery.js`
     * and KEY_ACTION_ARCHITECTURE.md, "Fetching vs. linking a URL").
     */
    handleMediaLibraryNotAvailableKey() {
      this.showFlashNotification("Media Library is not built yet \u2014 coming soon", COLORS.NOTIFICATION_INFO);
    }
    /**
     * 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, type) => {
          try {
            this.overlayManager?.showNotification?.(msg, type || "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 type = 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({
          [type]: 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:", type, blob.size);
        return true;
      } catch (error) {
        if (type !== "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);
      }
    }
    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");
      }
    }
    handleLauncherKey(e) {
      if (!this._allowActionKey("handleLauncherKey", e)) return;
      console.log("[KeyPilot] Launcher key pressed!");
      if (this.launcherPopover.isOpen()) {
        this.launcherPopover.hide();
      } else {
        this.launcherPopover.show();
      }
    }
    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);
      }
    }
    /**
     * Listen for KP_FRAME_POINTER / KP_FRAME_FOCUS_RECLAIM from child frame agents.
     * Top frame only — keeps lastMouse fresh over embeds and reclaims keyboard focus.
     */
    _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 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 {
      }
    }
    /**
     * @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_POINTER) return;
      try {
        if (event.source === window) return;
      } catch {
      }
      const iframe = this._findIframeByContentWindow(
        /** @type {Window} */
        event.source
      );
      if (!iframe) return;
      if (this._isKeyPilotManagedIframe(iframe)) return;
      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 {
      }
    }
    /**
     * 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;
      }
      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)) 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;
    }
    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(currentState.lastMouse.x, currentState.lastMouse.y, target);
      }
      if (this.activator.handleSmartActivate(target, currentState.lastMouse.x, currentState.lastMouse.y)) {
        this.showRipple(currentState.lastMouse.x, currentState.lastMouse.y);
        this.overlayManager.flashFocusOverlay(target);
        this.postClickRefresh(target, currentState.lastMouse.x, currentState.lastMouse.y);
        this.emitAction("activate", activationDetail);
        if (activationDetail.isKeyboardHelpKey) {
          try {
            const actionId = target.closest?.("[data-kp-action-id]")?.dataset?.kpActionId;
            if (actionId) pinKeyPopover(actionId, { keybindings: this.keybindings });
          } catch {
          }
        }
        return;
      }
      this.activator.smartClick(target, currentState.lastMouse.x, currentState.lastMouse.y);
      this.showRipple(currentState.lastMouse.x, currentState.lastMouse.y);
      this.overlayManager.flashFocusOverlay(target);
      this.postClickRefresh(target, currentState.lastMouse.x, currentState.lastMouse.y);
      this.emitAction("activate", activationDetail);
      if (activationDetail.isKeyboardHelpKey) {
        try {
          const actionId = target.closest?.("[data-kp-action-id]")?.dataset?.kpActionId;
          if (actionId) pinKeyPopover(actionId, { 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) {
        return;
      }
      if (currentState.mode === MODES.POPOVER) {
        if (!this._isElementInPopover(target)) {
          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;
      try {
        if (link && link.tagName !== "A" && typeof link.closest === "function") {
          link = link.closest("a[href]");
        }
      } catch {
      }
      if (link && link.tagName === "A" && link.href) {
        url = link.href;
      } else {
        try {
          let roleLink = target;
          if (roleLink instanceof Element) {
            if (roleLink.getAttribute("role") !== "link") {
              roleLink = roleLink.closest?.('[role="link"][data-kp-url]') || null;
            }
            if (roleLink && roleLink.getAttribute("role") === "link" && roleLink.dataset?.kpUrl) {
              url = String(roleLink.dataset.kpUrl || "").trim() || null;
              link = roleLink;
            }
          }
        } catch {
        }
      }
      if (url) {
        this.mouseCoordinateManager.handleLinkClick(currentState.lastMouse.x, currentState.lastMouse.y, link);
        try {
          if (this._sendRuntimeMessage({ type: MSG.OPEN_URL_FOREGROUND, url })) {
            this.showRipple(currentState.lastMouse.x, currentState.lastMouse.y);
            this.overlayManager.flashFocusOverlay(link);
            this.postClickRefresh(link, currentState.lastMouse.x, currentState.lastMouse.y);
            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);
          }
        }
      }
      if (this.activator.handleSmartActivate(target, currentState.lastMouse.x, currentState.lastMouse.y, true)) {
        this.showRipple(currentState.lastMouse.x, currentState.lastMouse.y);
        this.overlayManager.flashFocusOverlay(target);
        this.postClickRefresh(target, currentState.lastMouse.x, currentState.lastMouse.y);
        this.emitAction("activateNewTab", this._buildActivationDetail(target));
        return;
      }
      this.activator.smartClick(target, currentState.lastMouse.x, currentState.lastMouse.y, true);
      this.showRipple(currentState.lastMouse.x, currentState.lastMouse.y);
      this.overlayManager.flashFocusOverlay(target);
      this.postClickRefresh(target, currentState.lastMouse.x, currentState.lastMouse.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) {
        return;
      }
      if (currentState.mode === MODES.POPOVER) {
        if (!this._isElementInPopover(target)) {
          return;
        }
      }
      let link = target;
      let url = null;
      try {
        if (link && link.tagName !== "A") {
          link = link.closest?.("a[href]") || link;
        }
      } catch {
      }
      if (link && link.tagName === "A" && link.href) {
        url = link.href;
      } else {
        try {
          let roleLink = target;
          if (roleLink instanceof Element) {
            if (roleLink.getAttribute("role") !== "link") {
              roleLink = roleLink.closest?.('[role="link"][data-kp-url]') || null;
            }
            if (roleLink && roleLink.getAttribute("role") === "link" && roleLink.dataset?.kpUrl) {
              url = String(roleLink.dataset.kpUrl || "").trim() || null;
              link = roleLink;
            }
          }
        } catch {
        }
      }
      if (!url) {
        console.log("[KeyPilot] Activate New Tab Background: not hovering over a hyperlink");
        return;
      }
      console.log("[KeyPilot] Opening link in new tab (background):", url);
      this.mouseCoordinateManager.handleLinkClick(currentState.lastMouse.x, currentState.lastMouse.y, link);
      try {
        if (this._sendRuntimeMessage({ type: MSG.OPEN_URL_BACKGROUND, url })) {
          this.showRipple(currentState.lastMouse.x, currentState.lastMouse.y);
          this.overlayManager.flashFocusOverlay(link);
          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.handleClosePopover();
        return;
      }
      const currentState = this.state.getState();
      const { lastMouse } = currentState;
      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");
        return;
      }
      let probe = target;
      let link = probe;
      if (link.tagName !== "A") {
        link = link.closest("a[href]");
      }
      let url = null;
      if (link && link.tagName === "A" && link.href) {
        url = link.href;
      } else {
        let roleLink = probe;
        if (roleLink.getAttribute("role") !== "link") {
          roleLink = roleLink.closest('[role="link"]');
        }
        if (roleLink && roleLink.getAttribute("role") === "link" && roleLink.dataset.kpUrl) {
          url = roleLink.dataset.kpUrl;
          link = roleLink;
        }
      }
      let guard = 0;
      while (!url && guard++ < 10) {
        const root = probe.getRootNode?.();
        if (!(root instanceof ShadowRoot) || !(root.host instanceof Element)) break;
        probe = root.host;
        let hostLink = probe;
        if (hostLink.tagName !== "A") {
          hostLink = hostLink.closest("a[href]");
        }
        if (hostLink && hostLink.tagName === "A" && hostLink.href) {
          url = hostLink.href;
          link = hostLink;
          break;
        }
        let hostRoleLink = probe;
        if (hostRoleLink.getAttribute("role") !== "link") {
          hostRoleLink = hostRoleLink.closest('[role="link"]');
        }
        if (hostRoleLink && hostRoleLink.getAttribute("role") === "link" && hostRoleLink.dataset.kpUrl) {
          url = hostRoleLink.dataset.kpUrl;
          link = hostRoleLink;
          break;
        }
      }
      if (!url) {
        console.log("[KeyPilot] Open popover: not hovering over a link");
        return;
      }
      console.log("[KeyPilot] Opening popover for link:", url);
      this.overlayManager.showPopover(url);
      this.state.setPopoverOpen(true, url);
    }
    handlePreviewLinkPopover(e) {
      if (!this._allowActionKey("handlePreviewLinkPopover", e)) return;
      if (this.overlayManager.isPopoverOpen()) {
        this.handleClosePopover();
        return;
      }
      const currentState = this.state.getState();
      const { lastMouse } = currentState;
      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");
        return;
      }
      let probe = target;
      let link = probe;
      if (link.tagName !== "A") {
        link = link.closest("a[href]");
      }
      let url = null;
      if (link && link.tagName === "A" && link.href) {
        url = link.href;
      } else {
        let roleLink = probe;
        if (roleLink.getAttribute("role") !== "link") {
          roleLink = roleLink.closest('[role="link"]');
        }
        if (roleLink && roleLink.getAttribute("role") === "link" && roleLink.dataset.kpUrl) {
          url = roleLink.dataset.kpUrl;
          link = roleLink;
        }
      }
      let guard = 0;
      while (!url && guard++ < 10) {
        const root = probe.getRootNode?.();
        if (!(root instanceof ShadowRoot) || !(root.host instanceof Element)) break;
        probe = root.host;
        let hostLink = probe;
        if (hostLink.tagName !== "A") {
          hostLink = hostLink.closest("a[href]");
        }
        if (hostLink && hostLink.tagName === "A" && hostLink.href) {
          url = hostLink.href;
          link = hostLink;
          break;
        }
        let hostRoleLink = probe;
        if (hostRoleLink.getAttribute("role") !== "link") {
          hostRoleLink = hostRoleLink.closest('[role="link"]');
        }
        if (hostRoleLink && hostRoleLink.getAttribute("role") === "link" && hostRoleLink.dataset.kpUrl) {
          url = hostRoleLink.dataset.kpUrl;
          link = hostRoleLink;
          break;
        }
      }
      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;
              link = outline;
            }
          }
        } catch {
        }
      }
      if (!url) {
        console.log("[KeyPilot] Preview popover: not hovering over a link");
        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;
      }
    }
    handleOpenSettingsPopover() {
      const url = this.getSettingsPopoverUrl();
      if (!url) return;
      const currentState = this.state.getState();
      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;
      this.overlayManager.showPopover(url, {
        title: "KeyPilot Settings",
        hintKeyLabel: "'",
        closeKeys: ["Escape", "'", '"'],
        width: `${settingsContainerWidth}px`,
        height: `${settingsContainerHeight}px`
      });
      this.state.setPopoverOpen(true, url);
    }
    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);
    }
    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() {
      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 {
      }
      if (document.activeElement) {
        document.activeElement.blur();
      }
      document.body.focus();
      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.launcherPopover?.isOpen?.()) {
          this.launcherPopover.hide();
          return;
        }
      } 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 {
        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.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.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, 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'|null|string} strategy - null/'auto' restores automatic choice
     */
    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 {
      }
      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();
      }
      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: "KP_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 {
        chrome.runtime.onMessage.addListener(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: "KP_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 === "KP_TOGGLE_STATE" || message?.type === "KP_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 {
        if (chrome?.runtime?.onMessage?.removeListener) {
          chrome.runtime.onMessage.removeListener(this._onRuntimeMessage);
        }
      } 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;
}
@media (prefers-reduced-motion: reduce) {
  :host {
    animation: none;
    filter:
      drop-shadow(0 0 2px #fff)
      drop-shadow(0 0 8px var(--kp-arrow-accent, currentColor))
      drop-shadow(0 0 16px var(--kp-arrow-glow, currentColor))
      drop-shadow(0 1px 2px rgba(0,0,0,0.45));
  }
}
`
      });
    } catch {
    }
  }
  function applyToggleOffArrowColor(el2, accent) {
    if (!el2) return;
    const color = String(accent || resolveClickFocusColor());
    const glow = resolveClickFocusGlow(color);
    try {
      el2.style.color = color;
      el2.style.fill = color;
      el2.style.setProperty("--kp-arrow-accent", color);
      el2.style.setProperty("--kp-arrow-glow", glow);
    } 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._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);
    }
    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);
    }
    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 = "" } = {}) {
      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, color || resolveClickFocusColor());
        this._positionToggleOffArrow();
        return;
      }
      this.hideToggleOffArrow();
      const accent = String(color || 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
    }) {
      if (!this.root || this.root.hidden) return;
      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 = !!(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: ${NCT_DARK_UI_COLORS.panel};
  color: ${NCT_DARK_UI_COLORS.fg};
  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};
  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: ${NCT_DARK_UI_TITLEBAR_GRADIENT};
  border-bottom: ${NCT_DARK_UI_TITLEBAR_BORDER_BOTTOM};
}
.kp-practice-popover__title {
  font-size: 13px;
  font-weight: 900;
  letter-spacing: 0.2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.kp-practice-popover__close {
  width: 30px;
  height: 30px;
  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};
  cursor: pointer;
  font-size: 18px;
  line-height: 28px;
  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: "14px",
          border: "1px solid rgba(255,255,255,0.12)",
          background: "rgba(10, 12, 18, 0.58)",
          boxShadow: "0 10px 26px rgba(0, 0, 0, 0.35)"
        });
        if (titleText) {
          const t = document.createElement("div");
          t.textContent = titleText;
          Object.assign(t.style, { fontSize: "13px", fontWeight: "800", marginBottom: "8px" });
          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: "rgba(255,255,255,0.88)"
        });
        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: "12px",
          padding: "2px 6px",
          border: "1px solid rgba(255, 255, 255, 0.18)",
          borderBottomColor: "rgba(0, 0, 0, 0.6)",
          borderRadius: "6px",
          background: "rgba(255, 255, 255, 0.08)",
          color: "rgba(255, 255, 255, 0.95)"
        });
        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", opacity: "0.82", 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" });
        return l;
      };
      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: "12px",
          border: "1px solid rgba(255,255,255,0.16)",
          background: "rgba(255,255,255,0.06)",
          color: "rgba(255,255,255,0.95)",
          padding: "10px 12px",
          fontSize: "14px",
          outline: "none"
        });
        i.addEventListener("focus", () => {
          try {
            i.style.borderColor = "rgba(255, 140, 0, 0.55)";
            i.style.boxShadow = "0 0 0 3px rgba(255, 140, 0, 0.18)";
          } catch {
          }
        });
        i.addEventListener("blur", () => {
          try {
            i.style.borderColor = "rgba(255,255,255,0.16)";
            i.style.boxShadow = "none";
          } 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: "12px",
          border: "1px solid rgba(255,255,255,0.16)",
          background: "rgba(255,255,255,0.06)",
          color: "rgba(255,255,255,0.95)",
          padding: "10px 12px",
          fontSize: "14px",
          outline: "none",
          minHeight: "120px",
          resize: "vertical"
        });
        t.addEventListener("focus", () => {
          try {
            t.style.borderColor = "rgba(255, 140, 0, 0.55)";
            t.style.boxShadow = "0 0 0 3px rgba(255, 140, 0, 0.18)";
          } catch {
          }
        });
        t.addEventListener("blur", () => {
          try {
            t.style.borderColor = "rgba(255,255,255,0.16)";
            t.style.boxShadow = "none";
          } 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", opacity: "0.84", 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: "rgba(255,255,255,0.88)" });
      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: "rgba(91, 226, 241, 0.95)", textDecoration: "underline", display: "inline-block", marginTop: "10px" });
      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: "10px 12px",
        borderRadius: "12px",
        border: "1px solid rgba(255, 255, 255, 0.16)",
        background: "rgba(255, 255, 255, 0.08)",
        color: "rgba(255, 255, 255, 0.92)",
        cursor: "pointer",
        fontWeight: "800",
        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", opacity: "0.84", 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" });
      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 type = (oe.getAttribute("type") || "").trim();
        if (!type) continue;
        const entry = { type };
        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(type, handler) {
      if (!type || typeof handler !== "function") return () => {
      };
      const key = String(type);
      const set = this._listeners.get(key) || /* @__PURE__ */ new Set();
      set.add(handler);
      this._listeners.set(key, set);
      return () => {
        try {
          set.delete(handler);
        } catch {
        }
      };
    }
    _emit(type, detail = {}) {
      const t = String(type || "");
      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: "KP_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 {
      }
      this.panel.show();
      const isSlideChange = this._lastRenderedSlideId !== null && String(this._lastRenderedSlideId) !== String(slide.id);
      const transition = opts && opts.transition ? opts.transition : isSlideChange ? { type: "slide", dir: index > (this._lastRenderedSlideIndex || 0) ? 1 : -1 } : 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 (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
      });
      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();
      if (!this.progress.onEnterDoneSlideIds.includes(slide.id)) {
        this._runOnEnter(slide);
      }
    }
    /**
     * Persist that onEnter for this slide has finished (so it won't re-fire).
     * @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 showLaterReminder = () => {
            const reminderTitle = laterTitle || "Return to this Tutorial with `Alt`+`T`";
            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 = 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;
              this._markOnEnterDone(slideId);
              scheduleBorderEffect();
              continue;
            }
          } catch {
          }
          const tryShow = () => {
            try {
              this.panel.showOverlay({
                title,
                message,
                primaryText,
                secondaryText,
                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) {
              this._markOnEnterDone(slideId);
              scheduleBorderEffect();
            }
          };
          if (tryShow()) {
            finish(true);
            continue;
          }
          try {
            requestAnimationFrame(() => {
              if (slideId && this.progress.onEnterDoneSlideIds.includes(String(slideId))) {
                this._onEnterOverlayPendingSlideId = null;
                return;
              }
              if (tryShow()) {
                finish(true);
                return;
              }
              requestAnimationFrame(() => {
                if (slideId && this.progress.onEnterDoneSlideIds.includes(String(slideId))) {
                  this._onEnterOverlayPendingSlideId = null;
                  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: "KP_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 isTKey = !!e && (e.code === "KeyT" || e.key === "t" || e.key === "T");
        const isAltT = isAltOrAltGraph && isTKey;
        if (!isAltT) 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 {
      }
    }
    /**
     * 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 type = String(when.type || "").trim();
      if (!type) return false;
      if (type === "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 (type === "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
  async function initializeKeyPilot() {
    try {
      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();
  }
})();
