QOLBox

Fullscreen hitbox.io, reserve spots, away-tab alerts, audio controls, mobile Grab, readable chat, lobby commands, map import/export, and first-start setup for hitbox.io.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

Advertisement:

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

Advertisement:

// ==UserScript==
// @name         QOLBox
// @namespace    Violentmonkey Scripts
// @author       gpt-5.4 and gpt-5.5
// @version      2.1.3
// @description  Fullscreen hitbox.io, reserve spots, away-tab alerts, audio controls, mobile Grab, readable chat, lobby commands, map import/export, and first-start setup for hitbox.io.
// @license      ISC
// @match        https://hitbox.io/
// @match        https://www.hitbox.io/
// @match        https://hitbox.io/game2.html*
// @match        https://www.hitbox.io/game2.html*
// @run-at       document-start
// @inject-into  content
// @grant        GM_xmlhttpRequest
// @grant        GM.xmlHttpRequest
// @connect      api.github.com
// @connect      greasyfork.org
// ==/UserScript==
(() => {
  'use strict';

  const QOLBOX_BRIDGE_READY_SCRIPT = 'window.__qolboxReleaseHistoryBridgeReady = true;';
  const REQUEST_SOURCE = 'qolbox-release-history';
  const RESPONSE_SOURCE = 'qolbox-release-history-bridge';
  const REQUEST_TYPE = 'fetch';
  const RESPONSE_TYPE = 'fetch-result';
  const REQUEST_TIMEOUT_MS = 7000;
  const ALLOWED_HOSTS = new Set(['api.github.com', 'greasyfork.org']);

  function injectPageScript(source, sourceName) {
    const script = document.createElement('script');
    script.textContent = source + '\n//# sourceURL=' + sourceName;
    const host = document.documentElement || document.head || document.body;
    if (!host) {
      window.setTimeout(() => injectPageScript(source, sourceName), 0);
      return;
    }
    host.appendChild(script);
    script.remove();
  }

  function injectPageFunction(fn, sourceName) {
    injectPageScript('(' + fn.toString() + ')();', sourceName);
  }

  function getUserscriptRequest() {
    if (typeof GM_xmlhttpRequest === 'function') {
      return GM_xmlhttpRequest;
    }
    if (typeof GM === 'object' && GM && typeof GM.xmlHttpRequest === 'function') {
      return GM.xmlHttpRequest.bind(GM);
    }
    return null;
  }

  function isAllowedUrl(url) {
    try {
      const parsed = new URL(String(url));
      return parsed.protocol === 'https:' && ALLOWED_HOSTS.has(parsed.hostname);
    } catch {
      return false;
    }
  }

  function sanitizeHeaders(headers) {
    const sanitized = {};
    if (!headers || typeof headers !== 'object') {
      return sanitized;
    }
    for (const [name, value] of Object.entries(headers)) {
      if (typeof value === 'string' && /^accept$/i.test(name)) {
        sanitized[name] = value;
      }
    }
    return sanitized;
  }

  function postBridgeResponse(id, payload) {
    window.postMessage({
      source: RESPONSE_SOURCE,
      type: RESPONSE_TYPE,
      id,
      ...payload,
    }, window.location.origin);
  }

  function installReleaseHistoryBridge() {
    const request = getUserscriptRequest();
    if (!request) {
      return;
    }

    injectPageScript(QOLBOX_BRIDGE_READY_SCRIPT, 'QOLBox.bridge-ready.js');

    window.addEventListener('message', event => {
      if (event.source !== window || event.origin !== window.location.origin) {
        return;
      }

      const data = event.data;
      if (!data || typeof data !== 'object' || data.source !== REQUEST_SOURCE || data.type !== REQUEST_TYPE) {
        return;
      }

      const id = typeof data.id === 'string' ? data.id : '';
      const url = typeof data.url === 'string' ? data.url : '';
      if (!id || !isAllowedUrl(url)) {
        postBridgeResponse(id, { ok: false, error: 'Release-history request was not allowed.' });
        return;
      }

      let settled = false;
      const details = {
        method: 'GET',
        url,
        headers: sanitizeHeaders(data.headers),
        timeout: REQUEST_TIMEOUT_MS,
        onload(response) {
          if (settled) {
            return;
          }
          settled = true;
          const status = typeof response?.status === 'number' ? response.status : 0;
          if (status < 200 || status >= 300) {
            postBridgeResponse(id, {
              ok: false,
              status,
              error: 'HTTP ' + String(status || 0),
            });
            return;
          }
          postBridgeResponse(id, {
            ok: true,
            status,
            text: typeof response?.responseText === 'string' ? response.responseText : '',
          });
        },
        onerror(error) {
          if (settled) {
            return;
          }
          settled = true;
          postBridgeResponse(id, {
            ok: false,
            error: error instanceof Error ? error.message : 'GM_xmlhttpRequest failed.',
          });
        },
        ontimeout() {
          if (settled) {
            return;
          }
          settled = true;
          postBridgeResponse(id, {
            ok: false,
            error: 'GM_xmlhttpRequest timed out.',
          });
        },
      };

      try {
        const maybePromise = request(details);
        if (maybePromise && typeof maybePromise.then === 'function') {
          maybePromise.then(details.onload, details.onerror);
        }
      } catch (error) {
        details.onerror(error);
      }
    }, false);
  }

  function runQolboxPageApp() {
    "use strict";
    (() => {
      // src/config/qolbox-constants.ts
      var DESKTOP_LOBBY_CHAT_PROMPT = "Press Enter to send a message";
      var TOUCH_LOBBY_CHAT_PROMPT = "Tap here to send a message";
      var MENU_KEY_LABEL = "F8";
      var MENU_KEY = "F8";
      var QOLBOX_MENU_ID = "qolboxMenu";
      var QOLBOX_MENU_ROOT_CLASS = "qolbox-menu-open";
      var FALLBACK_BASE_WIDTH = 800;
      var FALLBACK_BASE_HEIGHT = 500;
      var SCORE_ROW_FALLBACK_RGB = { red: 225, green: 21, blue: 0, alpha: 1 };
      var TEAM_SCORE_COLORS = /* @__PURE__ */ new Map([
        [2, { red: 225, green: 21, blue: 0, alpha: 1 }],
        [3, { red: 0, green: 117, blue: 225, alpha: 1 }]
      ]);
      var FULLSCREEN_GAMEPLAY_LAYER_SELECTOR = "#pixiContainer, #singlePlayer, .singlePlayer";
      var FULLSCREEN_EDITOR_LAYER_SELECTOR = "#editorContainer";
      var FULLSCREEN_MENU_LAYER_SELECTOR = ".replayViewer";
      var CHAT_INPUT_SELECTOR = ".inGameChat .input, .lobbyContainer .chatBox .input";
      var FULLSCREEN_PLAY_LAYER_SELECTOR = [
        FULLSCREEN_GAMEPLAY_LAYER_SELECTOR,
        FULLSCREEN_EDITOR_LAYER_SELECTOR
      ].join(", ");
      var FULLSCREEN_RENDER_LAYER_SELECTOR = [
        FULLSCREEN_PLAY_LAYER_SELECTOR,
        FULLSCREEN_MENU_LAYER_SELECTOR
      ].join(", ");
      var FULLSCREEN_RENDER_CANVAS_SELECTORS = [
        "#pixiContainer canvas",
        "#singlePlayer canvas",
        ".singlePlayer canvas",
        "#editorContainer > canvas",
        ".replayViewer canvas"
      ];
      var FULLSCREEN_RENDER_CANVAS_SELECTOR = FULLSCREEN_RENDER_CANVAS_SELECTORS.join(", ");
      var FULLSCREEN_RENDER_CANVAS_FOCUS_SELECTOR = FULLSCREEN_RENDER_CANVAS_SELECTORS.flatMap((selector) => [`${selector}:focus`, `${selector}:focus-visible`]).join(", ");
      var FULLSCREEN_LAYOUT_TARGET_SELECTOR = [
        "#appContainer",
        "#relativeContainer",
        "#backgroundImage",
        FULLSCREEN_RENDER_LAYER_SELECTOR,
        FULLSCREEN_RENDER_CANVAS_SELECTOR,
        ".inGameCSS",
        ".scores",
        ".spectateControls"
      ].join(", ");
      var GAMEPLAY_FOCUS_EXCLUSION_SELECTOR = [
        CHAT_INPUT_SELECTOR,
        ".inGameChat",
        ".lobbyContainer",
        ".cornerButton",
        ".cornerButton .items",
        ".jukebox",
        ".scores",
        ".spectateControls",
        ".qolboxMenuOverlay",
        ".qolboxReserveWindowContainer",
        ".connectingWindowContainer",
        ".passwordWindowContainer",
        ".buttonArea",
        "button",
        "input",
        "select",
        "textarea",
        "a",
        ".button",
        ".bottomButton",
        ".item"
      ].join(", ");
      var FEATURE_PATCH_TARGET_SELECTOR = [
        CHAT_INPUT_SELECTOR,
        ".items.left",
        ".items.left .item",
        ".jukebox",
        ".jukebox .knob.volumeContainer",
        ".buttonArea",
        ".cornerButton .items",
        ".cornerButton .items .item",
        "#ytContainer",
        "#ytContainer iframe",
        ".roomListContainer",
        ".roomListContainer .scrollBox tr",
        ".roomListContainer .bottomButton.right",
        ".passwordWindowContainer",
        ".connectingWindowContainer",
        ".lobbyContainer",
        ".lobbyContainer .teamsButtonContainer",
        ".scores",
        ".scores .entryContainer",
        "#editorContainer",
        ".fileMenu",
        ".fileMenu .item"
      ].join(", ");
      var FULLSCREEN_SETTLE_PASSES = 4;
      var FULLSCREEN_NATIVE_LAYOUT_WAIT_MS = 2500;
      var RESIZE_SETTLE_PASSES = 2;
      var JUKEBOX_WHEEL_STEP = 5;
      var JUKEBOX_DRAG_SENSITIVITY = 1;
      var YOUTUBE_HOOK_RETRY_DELAY_MS = 250;
      var YOUTUBE_HOOK_MAX_RETRIES = 120;
      var RESERVE_BUTTON_TEXT = "RESERVE";
      var JOIN_BUTTON_TEXT = "JOIN";
      var RESERVE_WAIT_TITLE_TEXT = "Waiting for a Spot";
      var RESERVE_WAIT_TEXT = "Waiting for someone to leave...";
      var RESERVE_STATUS_FALLBACK_TEXT = "Connecting...";
      var RESERVE_UNAVAILABLE_TITLE_TEXT = "Lobby Not Available";
      var RESERVE_ONE_PERSON_TEXT = "This lobby only allows one person, so there is no spot to reserve.";
      var RESERVE_RETRY_DELAY_MS = 2500;
      var RESERVE_COUNTDOWN_UPDATE_MS = 100;
      var RESERVE_RETRY_AUDIO_SUPPRESS_MS = 900;
      var RESERVE_JOINED_ROOM_FULL_SUPPRESS_MS = 12e3;
      var RESERVE_ROOM_FULL_PATTERN = /room[_ ]?full|room is full/i;
      var RESERVE_ROOM_CLOSED_PATTERN = /room[_ ]?not[_ ]?found|room has just closed/i;
      var RESERVE_WRONG_PASSWORD_PATTERN = /wrong[_ ]?password|password incorrect|incorrect password/i;
      var GAME_START_INDICATOR_DELAY_MS = 1200;
      var GAME_START_WATCH_INTERVAL_MS = 750;
      var GAME_START_FLASH_INTERVAL_MS = 700;
      var GAME_START_END_WATCH_INTERVAL_MS = 1e3;
      var GAME_START_LOCAL_TRANSITION_TIMEOUT_MS = 5e3;
      var GAME_START_SESSION_ENTRY_GRACE_MS = 2e3;
      var TYPING_INDICATOR_TIMEOUT_MS = 1600;
      var IS_QOLBOX_GAME_PAGE = /\/game2\.html$/i.test(window.location.pathname);

      // src/utils/object-properties.ts
      function isReflectableObject(value) {
        return typeof value === "object" && value !== null || typeof value === "function";
      }
      function readObjectProperty(source, property) {
        return isReflectableObject(source) ? Reflect.get(source, property) : void 0;
      }
      function setObjectProperty(source, property, value) {
        if (!isReflectableObject(source)) {
          return false;
        }
        try {
          return Reflect.set(source, property, value);
        } catch {
          return false;
        }
      }

      // src/features/game-start-shared.ts
      var GAME_START_TITLE_PREFIX = "[GAME STARTED] ";
      var GAME_PULLED_TITLE_PREFIX = "[PULLED INTO GAME] ";
      var GAME_START_TITLE_PREFIXES = [GAME_START_TITLE_PREFIX, GAME_PULLED_TITLE_PREFIX];
      var GAME_START_FAVICON_HREF = "data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 64 64%22%3E%3Crect width=%2264%22 height=%2264%22 rx=%2212%22 fill=%22%23f5c542%22/%3E%3Cpath d=%22M32 10 56 54H8Z%22 fill=%22%23111111%22/%3E%3Crect x=%2229%22 y=%2223%22 width=%226%22 height=%2217%22 rx=%223%22 fill=%22%23f5c542%22/%3E%3Ccircle cx=%2232%22 cy=%2247%22 r=%223%22 fill=%22%23f5c542%22/%3E%3C/svg%3E";
      function stripGameStartTitlePrefix(title) {
        const value = String(title);
        for (const prefix of GAME_START_TITLE_PREFIXES) {
          if (value.startsWith(prefix)) {
            return value.slice(prefix.length);
          }
        }
        return value;
      }

      // src/features/top-level-page.ts
      var TOP_LEVEL_INPUT_STYLE_ID = "qolbox-top-level-input-style";
      var HITBOX_ORIGIN_PATTERN = /^https:\/\/(www\.)?hitbox\.io$/i;
      function installTopLevelGameInputPassthrough() {
        const applyPassthroughStyle = () => {
          if (document.getElementById(TOP_LEVEL_INPUT_STYLE_ID)) {
            return true;
          }
          const root = document.head || document.documentElement;
          if (!root) {
            return false;
          }
          const style = document.createElement("style");
          style.id = TOP_LEVEL_INPUT_STYLE_ID;
          style.textContent = `
            #adboxverticalleft,
            #adboxverticalright {
              display: none !important;
              visibility: hidden !important;
              pointer-events: none !important;
            }
          `;
          root.appendChild(style);
          return true;
        };
        if (!applyPassthroughStyle()) {
          document.addEventListener("DOMContentLoaded", applyPassthroughStyle, { once: true });
        }
      }
      function installTopLevelGameStartRelay() {
        if (window.top !== window || window.__qolboxGameStartRelayInstalled) {
          return;
        }
        window.__qolboxGameStartRelayInstalled = true;
        let relayActive = false;
        let relayOriginalTitle = "";
        let relayOriginalFavicon = null;
        let relayFaviconLink = null;
        function saveRelayState() {
          if (relayActive) {
            return;
          }
          const link = document.querySelector('link[rel~="icon"]');
          relayOriginalTitle = stripGameStartTitlePrefix(document.title || "");
          relayOriginalFavicon = link ? {
            href: link.getAttribute("href"),
            link,
            type: link.getAttribute("type")
          } : { href: null, link: null, type: null };
          relayFaviconLink = link || document.createElement("link");
          if (!link) {
            relayFaviconLink.rel = "icon";
            (document.head || document.documentElement).appendChild(relayFaviconLink);
          }
          relayActive = true;
        }
        function setRelayFavicon(active) {
          saveRelayState();
          if (!relayFaviconLink) {
            return;
          }
          if (active) {
            relayFaviconLink.setAttribute("href", GAME_START_FAVICON_HREF);
            relayFaviconLink.setAttribute("type", "image/svg+xml");
            return;
          }
          if (relayOriginalFavicon && relayOriginalFavicon.href) {
            relayFaviconLink.setAttribute("href", relayOriginalFavicon.href);
          } else {
            relayFaviconLink.removeAttribute("href");
          }
          if (relayOriginalFavicon && relayOriginalFavicon.type) {
            relayFaviconLink.setAttribute("type", relayOriginalFavicon.type);
          } else {
            relayFaviconLink.removeAttribute("type");
          }
        }
        function clearRelayState() {
          if (!relayActive) {
            return;
          }
          document.title = relayOriginalTitle;
          if (relayOriginalFavicon && relayFaviconLink) {
            if (!relayOriginalFavicon.link) {
              relayFaviconLink.remove();
            } else {
              setRelayFavicon(false);
            }
          }
          relayActive = false;
          relayOriginalTitle = "";
          relayOriginalFavicon = null;
          relayFaviconLink = null;
        }
        window.addEventListener(
          "message",
          (event) => {
            if (!HITBOX_ORIGIN_PATTERN.test(event.origin)) {
              return;
            }
            const data = event.data;
            if (readObjectProperty(data, "source") !== "QOLBox" || readObjectProperty(data, "feature") !== "gameStartIndicator") {
              return;
            }
            const action = readObjectProperty(data, "action");
            if (action === "title") {
              saveRelayState();
              document.title = String(readObjectProperty(data, "title") || relayOriginalTitle);
            } else if (action === "favicon") {
              setRelayFavicon(Boolean(readObjectProperty(data, "active")));
            } else if (action === "clear") {
              clearRelayState();
            }
          },
          true
        );
      }

      // src/boot/page-entry.ts
      function shouldRunGamePageBootstrap() {
        if (IS_QOLBOX_GAME_PAGE) {
          if (window.__qolboxGamePageBootstrapInstalled) {
            return false;
          }
          window.__qolboxGamePageBootstrapInstalled = true;
          return true;
        }
        installTopLevelGameInputPassthrough();
        installTopLevelGameStartRelay();
        return false;
      }

      // src/boot/startup-sequence.ts
      function runQolboxStartupSequence(options) {
        const scheduleInitialSettle = () => {
          options.scheduleUiWork({
            force: true,
            features: true,
            passes: FULLSCREEN_SETTLE_PASSES
          });
        };
        options.applyFeatureRootClasses();
        options.ensureGlobalStyle();
        options.installQolboxMenuHooks();
        options.installPopupKeyboardHooks();
        if (options.isReserveEnabled()) {
          options.installReserveSocketCaptureHook();
        }
        if (options.isAudioEnabled()) {
          options.installYouTubeReadyCallbackHook();
        }
        options.installFullscreenHooks();
        options.scheduleFirstBootOnboarding();
        scheduleInitialSettle();
        if (document.readyState === "loading") {
          document.addEventListener("DOMContentLoaded", scheduleInitialSettle, { once: true });
        } else {
          scheduleInitialSettle();
        }
      }

      // src/settings/advanced-settings.ts
      var ADVANCED_RESERVE_RETRY_INTERVAL_MS = "reserveRetryIntervalMs";
      var ADVANCED_COMMAND_ALIASES = "commandAliases";
      var ADVANCED_BLACKLIST_ENFORCEMENT = "blacklistEnforcement";
      var ADVANCED_ALERT_DELAY_MS = "gameStartAlertDelayMs";
      var ADVANCED_ALERT_FLASH_INTERVAL_MS = "gameStartAlertFlashIntervalMs";
      var ADVANCED_TYPING_DURATION_MS = "typingIndicatorDurationMs";
      var ADVANCED_SETTINGS_KEY = "vm.hitbox.qolboxAdvancedSettings";
      var ADVANCED_SETTING_DEFINITIONS = [
        {
          key: ADVANCED_RESERVE_RETRY_INTERVAL_MS,
          kind: "number",
          title: "Reserve retry interval",
          description: "Milliseconds between reserve join attempts.",
          defaultValue: RESERVE_RETRY_DELAY_MS,
          min: 500,
          max: 1e4,
          step: 100,
          unit: "ms"
        },
        {
          key: ADVANCED_COMMAND_ALIASES,
          kind: "boolean",
          title: "Command aliases",
          description: "Enable shorthand commands such as /rec and /r.",
          defaultValue: true
        },
        {
          key: ADVANCED_BLACKLIST_ENFORCEMENT,
          kind: "boolean",
          title: "Automatic blacklist",
          description: "Ban exact-name blacklist matches while you are host.",
          defaultValue: true
        },
        {
          key: ADVANCED_ALERT_DELAY_MS,
          kind: "number",
          title: "Tab alert delay",
          description: "Delay before the away-tab title changes.",
          defaultValue: GAME_START_INDICATOR_DELAY_MS,
          min: 200,
          max: 5e3,
          step: 100,
          unit: "ms"
        },
        {
          key: ADVANCED_ALERT_FLASH_INTERVAL_MS,
          kind: "number",
          title: "Tab flash speed",
          description: "Milliseconds between title/favicon flashes.",
          defaultValue: GAME_START_FLASH_INTERVAL_MS,
          min: 250,
          max: 2e3,
          step: 50,
          unit: "ms"
        },
        {
          key: ADVANCED_TYPING_DURATION_MS,
          kind: "number",
          title: "Typing indicator duration",
          description: "How long typing indicators stay visible.",
          defaultValue: TYPING_INDICATOR_TIMEOUT_MS,
          min: 500,
          max: 5e3,
          step: 100,
          unit: "ms"
        }
      ];
      function isRecord(value) {
        return typeof value === "object" && value !== null;
      }
      function clampNumber(value, definition) {
        const stepped = Math.round(value / definition.step) * definition.step;
        return Math.min(definition.max, Math.max(definition.min, stepped));
      }
      function getDefaultAdvancedSettings() {
        const settings = {};
        for (const definition of ADVANCED_SETTING_DEFINITIONS) {
          settings[definition.key] = definition.defaultValue;
        }
        return settings;
      }
      function sanitizeAdvancedSetting(definition, value) {
        switch (definition.kind) {
          case "number": {
            const numericValue = Number(value);
            return Number.isFinite(numericValue) ? clampNumber(numericValue, definition) : definition.defaultValue;
          }
          case "boolean":
            return value === true || value === "true";
        }
      }
      function loadAdvancedSettings() {
        const settings = getDefaultAdvancedSettings();
        try {
          const rawSettings = localStorage.getItem(ADVANCED_SETTINGS_KEY);
          if (!rawSettings) {
            return settings;
          }
          const parsedSettings = JSON.parse(rawSettings);
          if (!isRecord(parsedSettings)) {
            return settings;
          }
          for (const definition of ADVANCED_SETTING_DEFINITIONS) {
            if (Object.prototype.hasOwnProperty.call(parsedSettings, definition.key)) {
              settings[definition.key] = sanitizeAdvancedSetting(definition, parsedSettings[definition.key]);
            }
          }
        } catch {
        }
        return settings;
      }
      function saveAdvancedSettings(settings) {
        try {
          localStorage.setItem(ADVANCED_SETTINGS_KEY, JSON.stringify(settings));
        } catch {
        }
      }
      function getAdvancedSettingDefinition(key) {
        return ADVANCED_SETTING_DEFINITIONS.find((definition) => definition.key === key) || null;
      }
      function getAdvancedReserveRetryIntervalMs(settings = loadAdvancedSettings()) {
        return settings[ADVANCED_RESERVE_RETRY_INTERVAL_MS];
      }
      function getAdvancedGameStartAlertDelayMs(settings = loadAdvancedSettings()) {
        return settings[ADVANCED_ALERT_DELAY_MS];
      }
      function getAdvancedGameStartFlashIntervalMs(settings = loadAdvancedSettings()) {
        return settings[ADVANCED_ALERT_FLASH_INTERVAL_MS];
      }
      function getAdvancedTypingIndicatorDurationMs(settings = loadAdvancedSettings()) {
        return settings[ADVANCED_TYPING_DURATION_MS];
      }
      function areAdvancedCommandAliasesEnabled(settings = loadAdvancedSettings()) {
        return settings[ADVANCED_COMMAND_ALIASES];
      }
      function isAdvancedBlacklistEnforcementEnabled(settings = loadAdvancedSettings()) {
        return settings[ADVANCED_BLACKLIST_ENFORCEMENT];
      }

      // src/settings/advanced-settings-controller.ts
      function createAdvancedSettingsController(options) {
        const settings = loadAdvancedSettings();
        function getAdvancedSettings() {
          return { ...settings };
        }
        function getAdvancedSetting(key) {
          return settings[key];
        }
        function setAdvancedSetting(key, value) {
          const definition = getAdvancedSettingDefinition(key);
          if (!definition) {
            return;
          }
          settings[definition.key] = sanitizeAdvancedSetting(definition, value);
          applyAdvancedSettingsChange();
        }
        function setAdvancedSettings(nextSettings) {
          for (const definition of ADVANCED_SETTING_DEFINITIONS) {
            settings[definition.key] = sanitizeAdvancedSetting(definition, nextSettings[definition.key]);
          }
          applyAdvancedSettingsChange();
        }
        function resetAdvancedSetting(key) {
          const definition = getAdvancedSettingDefinition(key);
          if (!definition) {
            return;
          }
          settings[definition.key] = definition.defaultValue;
          applyAdvancedSettingsChange();
        }
        function resetAdvancedSettings() {
          const defaults = getDefaultAdvancedSettings();
          for (const definition of ADVANCED_SETTING_DEFINITIONS) {
            settings[definition.key] = defaults[definition.key];
          }
          applyAdvancedSettingsChange();
        }
        function applyAdvancedSettingsChange() {
          saveAdvancedSettings(settings);
          options.onApplyPersistentFeatures();
          options.onScheduleLayoutRefresh();
          options.onRenderMenu();
        }
        return {
          getAdvancedSetting,
          getAdvancedSettings,
          resetAdvancedSetting,
          resetAdvancedSettings,
          setAdvancedSettings,
          setAdvancedSetting
        };
      }

      // src/settings/feature-settings.ts
      var FEATURE_FULLSCREEN = "fullscreen";
      var FEATURE_AUDIO = "audio";
      var FEATURE_RESERVE = "reserve";
      var FEATURE_CHAT = "chat";
      var FEATURE_GAME_START_ALERT = "gameStartAlert";
      var FEATURE_MOBILE_GRAB = "mobileGrab";
      var FEATURE_LOBBY_COMMANDS = "lobbyCommands";
      var FEATURE_EDITOR_MAP_TRANSFER = "editorMapTransfer";
      var FEATURE_SETTINGS_KEY = "vm.hitbox.qolboxFeatures";
      var FEATURE_DEFINITIONS = [
        {
          key: FEATURE_FULLSCREEN,
          title: "Fullscreen Layout",
          shortTitle: "Fullscreen",
          summary: "Center and scale hitbox.io so the play area uses the browser window cleanly."
        },
        {
          key: FEATURE_AUDIO,
          title: "Audio Controls",
          shortTitle: "Audio",
          summary: "Remember volume choices, make the sliders easier to adjust, and keep jukebox mute behavior stable."
        },
        {
          key: FEATURE_RESERVE,
          title: "Reserve Spots",
          shortTitle: "Reserve",
          summary: "Wait for a spot in full custom lobbies instead of stopping at the full-room message."
        },
        {
          key: FEATURE_CHAT,
          title: "Chat Improvements",
          shortTitle: "Chat",
          summary: "Press Esc to discard chat drafts, keep game chat readable, and show typing indicators."
        },
        {
          key: FEATURE_GAME_START_ALERT,
          title: "Away Game Alert",
          shortTitle: "Game Alert",
          summary: "Flash the tab title and favicon when you need to play while away from the tab."
        },
        {
          key: FEATURE_MOBILE_GRAB,
          title: "Mobile Grab Button",
          shortTitle: "Mobile Grab",
          summary: "Add the missing Grab control to the game's mobile ability buttons."
        },
        {
          key: FEATURE_LOBBY_COMMANDS,
          title: "Lobby Commands",
          shortTitle: "Commands",
          summary: "Add lobby controls, special player targets, and access to normal and hidden host settings.",
          onboardingText: "Use /spec, /join, /red, /blue, /switch, /lock, /unlock, /host, /start, /end, /restart, /settings all, and /blacklist. Special targets: /spec all|playing, /join all|spectators, and /red or /blue all|playing|spectators. Named targets for /spec, /join, /red, /blue, /host, /kick, and /ban accept exact or unique partial player names. /blacklist stores exact names only."
        },
        {
          key: FEATURE_EDITOR_MAP_TRANSFER,
          title: "Map Import and Export",
          shortTitle: "Map Files",
          summary: "Add Import and Export to the editor File menu for saving map files on your computer."
        }
      ];
      var DEFAULT_FEATURE_SETTINGS = {
        [FEATURE_FULLSCREEN]: true,
        [FEATURE_AUDIO]: true,
        [FEATURE_RESERVE]: true,
        [FEATURE_CHAT]: true,
        [FEATURE_GAME_START_ALERT]: true,
        [FEATURE_MOBILE_GRAB]: true,
        [FEATURE_LOBBY_COMMANDS]: true,
        [FEATURE_EDITOR_MAP_TRANSFER]: true
      };
      function isRecord2(value) {
        return typeof value === "object" && value !== null;
      }
      function getDefaultFeatureSettings() {
        return { ...DEFAULT_FEATURE_SETTINGS };
      }
      function loadFeatureSettings() {
        const defaults = getDefaultFeatureSettings();
        try {
          const rawSettings = localStorage.getItem(FEATURE_SETTINGS_KEY);
          if (!rawSettings) {
            return defaults;
          }
          const parsedSettings = JSON.parse(rawSettings);
          if (!isRecord2(parsedSettings)) {
            return defaults;
          }
          for (const feature of FEATURE_DEFINITIONS) {
            if (Object.prototype.hasOwnProperty.call(parsedSettings, feature.key)) {
              defaults[feature.key] = parsedSettings[feature.key] !== false;
            }
          }
        } catch {
        }
        return defaults;
      }
      function saveFeatureSettings(settings) {
        try {
          localStorage.setItem(FEATURE_SETTINGS_KEY, JSON.stringify(settings));
        } catch {
        }
      }
      function isKnownFeature(featureKey) {
        return FEATURE_DEFINITIONS.some((feature) => feature.key === featureKey);
      }

      // src/settings/feature-gates.ts
      function createFeatureGateSet(shouldRunFeature) {
        return {
          isAudioEnabled: () => shouldRunFeature(FEATURE_AUDIO),
          isChatEnabled: () => shouldRunFeature(FEATURE_CHAT),
          isEditorMapTransferEnabled: () => shouldRunFeature(FEATURE_EDITOR_MAP_TRANSFER),
          isFullscreenEnabled: () => shouldRunFeature(FEATURE_FULLSCREEN),
          isGameStartAlertEnabled: () => shouldRunFeature(FEATURE_GAME_START_ALERT),
          isLobbyCommandsEnabled: () => shouldRunFeature(FEATURE_LOBBY_COMMANDS),
          isMobileGrabEnabled: () => shouldRunFeature(FEATURE_MOBILE_GRAB),
          isReserveEnabled: () => shouldRunFeature(FEATURE_RESERVE),
          shouldRunFeature
        };
      }

      // src/settings/feature-settings-controller.ts
      function createFeatureSettingsController(options) {
        const featureSettings = loadFeatureSettings();
        function isFeatureEnabled(featureKey) {
          return isKnownFeature(featureKey) && featureSettings[featureKey] !== false;
        }
        function shouldRunFeature(featureKey) {
          return options.isOnboardingComplete() && isFeatureEnabled(featureKey);
        }
        function setFeatureEnabled(featureKey, enabled) {
          if (!isKnownFeature(featureKey)) {
            return;
          }
          featureSettings[featureKey] = Boolean(enabled);
          applySettingsChange([featureKey]);
        }
        function applySettingsChange(featuresToRefresh = []) {
          saveFeatureSettings(featureSettings);
          options.onApplyFeatureRootClasses();
          for (const featureKey of featuresToRefresh) {
            if (!shouldRunFeature(featureKey)) {
              options.onDisableFeatureSideEffects(featureKey);
            }
          }
          if (options.isOnboardingComplete()) {
            options.onApplyPersistentFeatures();
            options.onScheduleUiWork({ force: true, features: true, passes: options.resizeSettlePasses });
          }
          options.onRenderMenu();
        }
        function setAllFeatureSettings(nextSettings) {
          const changedFeatures = [];
          for (const { key } of FEATURE_DEFINITIONS) {
            if (featureSettings[key] !== nextSettings[key]) {
              changedFeatures.push(key);
            }
            featureSettings[key] = nextSettings[key];
          }
          applySettingsChange(changedFeatures);
        }
        function resetFeatureSettingsToDefaults() {
          setAllFeatureSettings(getDefaultFeatureSettings());
        }
        return {
          isFeatureEnabled,
          resetFeatureSettingsToDefaults,
          setAllFeatureSettings,
          setFeatureEnabled,
          shouldRunFeature
        };
      }

      // src/dom/settings-menu-dom.ts
      function findSettingsContainer() {
        return document.querySelector(".items.left");
      }
      function findChangeControlsItem(container) {
        if (!container) {
          return null;
        }
        for (const item of container.querySelectorAll(".item")) {
          if ((item.textContent || "").trim() === "Change Controls") {
            return item;
          }
        }
        return null;
      }

      // src/settings/audio-storage.ts
      var STEP_PERCENT = 5;
      var DEFAULT_GAME_PERCENT = 100;
      var DEFAULT_JUKEBOX_PERCENT = 50;
      var GAME_VOLUME_KEY = "vm.hitbox.volumePercent";
      var JUKEBOX_STATE_KEY = "vm.hitbox.jukeboxState";
      function isRecord3(value) {
        return typeof value === "object" && value !== null;
      }
      function clampPercent(value, fallback = 0) {
        if (value === null || value === void 0 || typeof value === "string" && value.trim() === "") {
          return fallback;
        }
        const numericValue = Number(value);
        if (!Number.isFinite(numericValue)) {
          return fallback;
        }
        return Math.max(0, Math.min(100, Math.round(numericValue / STEP_PERCENT) * STEP_PERCENT));
      }
      function clampJukeboxPercent(value) {
        if (value === null || value === void 0 || typeof value === "string" && value.trim() === "") {
          return DEFAULT_JUKEBOX_PERCENT;
        }
        const numericValue = Number(value);
        if (!Number.isFinite(numericValue)) {
          return DEFAULT_JUKEBOX_PERCENT;
        }
        return Math.max(0, Math.min(100, Math.round(numericValue)));
      }
      function loadGamePercent() {
        try {
          return clampPercent(localStorage.getItem(GAME_VOLUME_KEY), DEFAULT_GAME_PERCENT);
        } catch {
          return DEFAULT_GAME_PERCENT;
        }
      }
      function saveGamePercent(percent) {
        try {
          localStorage.setItem(GAME_VOLUME_KEY, String(percent));
        } catch {
        }
      }
      function loadJukeboxState() {
        const fallback = { percent: null, muted: false };
        try {
          const rawState = localStorage.getItem(JUKEBOX_STATE_KEY);
          if (!rawState) {
            return fallback;
          }
          const parsed = JSON.parse(rawState);
          if (!isRecord3(parsed)) {
            return fallback;
          }
          return {
            percent: parsed.percent !== null && parsed.percent !== void 0 ? clampJukeboxPercent(parsed.percent) : null,
            muted: Boolean(parsed.muted)
          };
        } catch {
          return fallback;
        }
      }
      function saveJukeboxState(state) {
        try {
          localStorage.setItem(JUKEBOX_STATE_KEY, JSON.stringify(state));
        } catch {
        }
      }

      // src/hitbox/native-access.ts
      function isNativeObject(value) {
        return typeof value === "object" && value !== null;
      }
      function isNativeReflectTarget(value) {
        return isNativeObject(value) || typeof value === "function";
      }
      function readNativeProperty(source, property) {
        return isNativeObject(source) ? Reflect.get(source, property) : void 0;
      }
      function readNativeReflectProperty(source, property) {
        return isNativeReflectTarget(source) ? Reflect.get(source, property) : void 0;
      }
      function setNativeReflectProperty(source, property, value) {
        return isNativeReflectTarget(source) && Reflect.set(source, property, value);
      }
      function readNativePath(source, path) {
        let current = source;
        for (const property of path) {
          current = readNativeProperty(current, property);
          if (current === void 0 || current === null) {
            return current;
          }
        }
        return current;
      }
      function hasNativeMethod(source, methodName) {
        return typeof readNativeProperty(source, methodName) === "function";
      }
      function callNativeMethod(source, methodName, args = []) {
        const method = readNativeProperty(source, methodName);
        if (!isNativeObject(source) || typeof method !== "function") {
          return { called: false, result: void 0 };
        }
        return { called: true, result: Reflect.apply(method, source, [...args]) };
      }

      // src/hitbox/howler-audio-adapter.ts
      function isNativeCallable(value) {
        return typeof value === "function";
      }
      function createHowlerGameAudioAdapter(options) {
        let originalHowlVolume = null;
        let settingGameVolumeInternally = false;
        function applyGameVolumeToHowls() {
          const howler = readNativeReflectProperty(window, "Howler");
          const howls = readNativeReflectProperty(howler, "_howls");
          if (!Array.isArray(howls) || !originalHowlVolume) {
            return;
          }
          settingGameVolumeInternally = true;
          try {
            for (const howl of howls) {
              if (!isNativeReflectTarget(howl)) {
                continue;
              }
              const storedBaseVolume = readNativeReflectProperty(howl, "__qolboxBaseVolume");
              let baseVolume = typeof storedBaseVolume === "number" ? storedBaseVolume : null;
              if (baseVolume === null) {
                const initialVolume = Number(readNativeReflectProperty(howl, "_volume"));
                baseVolume = Number.isFinite(initialVolume) ? initialVolume : 1;
                setNativeReflectProperty(howl, "__qolboxBaseVolume", baseVolume);
              }
              Reflect.apply(originalHowlVolume, howl, [baseVolume * options.getGameVolumeScalar()]);
            }
          } finally {
            settingGameVolumeInternally = false;
          }
        }
        function hookHowlPrototype() {
          if (!options.isAudioEnabled() && !originalHowlVolume) {
            return false;
          }
          const howlConstructor = readNativeReflectProperty(window, "Howl");
          const howlPrototype = readNativeReflectProperty(howlConstructor, "prototype");
          if (!isNativeReflectTarget(howlPrototype)) {
            return false;
          }
          const currentVolumeMethod = readNativeReflectProperty(howlPrototype, "volume");
          let volumePatched = Boolean(
            isNativeCallable(currentVolumeMethod) && readNativeReflectProperty(currentVolumeMethod, "__qolboxWrapped") === true
          );
          if (!volumePatched && isNativeCallable(currentVolumeMethod)) {
            let wrappedVolume2 = function(value, ...rest) {
              if (arguments.length === 0) {
                const baseVolume = readNativeReflectProperty(this, "__qolboxBaseVolume");
                if (typeof baseVolume === "number") {
                  return baseVolume;
                }
                return Reflect.apply(baseVolumeMethod, this, []);
              }
              if (typeof value === "number" && !settingGameVolumeInternally) {
                setNativeReflectProperty(this, "__qolboxBaseVolume", value);
                return Reflect.apply(baseVolumeMethod, this, [value * options.getGameVolumeScalar(), ...rest]);
              }
              return Reflect.apply(baseVolumeMethod, this, [value, ...rest]);
            };
            var wrappedVolume = wrappedVolume2;
            const baseVolumeMethod = currentVolumeMethod;
            originalHowlVolume = baseVolumeMethod;
            setNativeReflectProperty(wrappedVolume2, "__qolboxWrapped", true);
            setNativeReflectProperty(howlPrototype, "volume", wrappedVolume2);
            volumePatched = true;
          }
          const currentPlayMethod = readNativeReflectProperty(howlPrototype, "play");
          const playPatched = isNativeCallable(currentPlayMethod) && readNativeReflectProperty(currentPlayMethod, "__qolboxReserveAudioWrapped");
          if (isNativeCallable(currentPlayMethod) && !playPatched) {
            let wrappedPlay2 = function(...args) {
              if (options.shouldSuppressReserveRetryAudio()) {
                return void 0;
              }
              return Reflect.apply(basePlayMethod, this, args);
            };
            var wrappedPlay = wrappedPlay2;
            const basePlayMethod = currentPlayMethod;
            setNativeReflectProperty(wrappedPlay2, "__qolboxReserveAudioWrapped", true);
            setNativeReflectProperty(howlPrototype, "play", wrappedPlay2);
          }
          return volumePatched;
        }
        return {
          applyGameVolumeToHowls,
          hookHowlPrototype
        };
      }

      // src/features/audio-levels.ts
      var KEYBOARD_PAGE_STEP_MULTIPLIER = 4;
      var GAME_CURVE_EXPONENT = 2;
      var JUKEBOX_CURVE_EXPONENT = 2;
      var JUKEBOX_MIN_ANGLE = -40;
      var JUKEBOX_MAX_ANGLE = 220;
      var JUKEBOX_ARC_CENTER = 14;
      var JUKEBOX_ARC_RADIUS = 12;
      var JUKEBOX_ANGLE_EPSILON = 1e-6;
      function readBooleanProperty(source, property) {
        return readObjectProperty(source, property) === true;
      }
      function readStringProperty(source, property) {
        const value = readObjectProperty(source, property);
        return typeof value === "string" ? value : "";
      }
      function percentToGameScalar(percent) {
        return Math.pow(clampPercent(percent, DEFAULT_GAME_PERCENT) / 100, GAME_CURVE_EXPONENT);
      }
      function percentToJukeboxVolume(percent) {
        const clampedPercent = clampJukeboxPercent(percent);
        if (clampedPercent <= 0) {
          return 0;
        }
        return Math.max(1, Math.round(Math.pow(clampedPercent / 100, JUKEBOX_CURVE_EXPONENT) * 100));
      }
      function percentToJukeboxAngle(percent) {
        const normalized = clampJukeboxPercent(percent) / 100;
        return JUKEBOX_MIN_ANGLE + (JUKEBOX_MAX_ANGLE - JUKEBOX_MIN_ANGLE) * normalized;
      }
      function getKeyboardPercentTarget(event, currentPercent, stepPercent) {
        if (!event || readBooleanProperty(event, "altKey") || readBooleanProperty(event, "ctrlKey") || readBooleanProperty(event, "metaKey")) {
          return null;
        }
        const current = Number.isFinite(Number(currentPercent)) ? Number(currentPercent) : 0;
        const step = Math.max(1, Number(stepPercent) || 1);
        switch (readStringProperty(event, "key")) {
          case "ArrowUp":
          case "ArrowRight":
            return current + step;
          case "ArrowDown":
          case "ArrowLeft":
            return current - step;
          case "PageUp":
            return current + step * KEYBOARD_PAGE_STEP_MULTIPLIER;
          case "PageDown":
            return current - step * KEYBOARD_PAGE_STEP_MULTIPLIER;
          case "Home":
            return 0;
          case "End":
            return 100;
          default:
            return null;
        }
      }
      function angleToJukeboxPercent(angle) {
        const numericAngle = Number(angle);
        if (!Number.isFinite(numericAngle)) {
          return DEFAULT_JUKEBOX_PERCENT;
        }
        const normalizedAngle = normalizeJukeboxAngle(numericAngle);
        const normalized = (Math.min(JUKEBOX_MAX_ANGLE, Math.max(JUKEBOX_MIN_ANGLE, normalizedAngle)) - JUKEBOX_MIN_ANGLE) / (JUKEBOX_MAX_ANGLE - JUKEBOX_MIN_ANGLE);
        return clampJukeboxPercent(normalized * 100);
      }
      function normalizeJukeboxAngle(angle) {
        const numericAngle = Number(angle);
        if (!Number.isFinite(numericAngle)) {
          return percentToJukeboxAngle(DEFAULT_JUKEBOX_PERCENT);
        }
        const candidates = [numericAngle, numericAngle + 360, numericAngle - 360];
        for (const candidate of candidates) {
          if (candidate >= JUKEBOX_MIN_ANGLE - JUKEBOX_ANGLE_EPSILON && candidate <= JUKEBOX_MAX_ANGLE + JUKEBOX_ANGLE_EPSILON) {
            return Math.max(JUKEBOX_MIN_ANGLE, Math.min(JUKEBOX_MAX_ANGLE, candidate));
          }
        }
        return Math.max(JUKEBOX_MIN_ANGLE, Math.min(JUKEBOX_MAX_ANGLE, numericAngle));
      }
      function parseJukeboxAngleFromTransform(transform) {
        if (typeof transform !== "string" || transform === "" || transform === "none") {
          return null;
        }
        const rotateMatch = transform.match(/rotate\(\s*(-?\d+(?:\.\d+)?)deg\s*\)/i);
        if (rotateMatch) {
          return normalizeJukeboxAngle(Number(rotateMatch[1]));
        }
        const matrixMatch = transform.match(/^matrix\(([^)]+)\)$/i);
        if (matrixMatch) {
          const values = matrixMatch[1].split(",").map((value) => Number(value.trim()));
          if (values.length >= 4 && values.every(Number.isFinite)) {
            return normalizeJukeboxAngle(Math.atan2(values[1], values[0]) * 180 / Math.PI);
          }
        }
        const matrix3dMatch = transform.match(/^matrix3d\(([^)]+)\)$/i);
        if (matrix3dMatch) {
          const values = matrix3dMatch[1].split(",").map((value) => Number(value.trim()));
          if (values.length >= 16 && values.every(Number.isFinite)) {
            return normalizeJukeboxAngle(Math.atan2(values[1], values[0]) * 180 / Math.PI);
          }
        }
        return null;
      }
      function polarToArcPoint(angle) {
        const radians = (angle + 180) * Math.PI / 180;
        return {
          x: JUKEBOX_ARC_CENTER + JUKEBOX_ARC_RADIUS * Math.cos(radians),
          y: JUKEBOX_ARC_CENTER + JUKEBOX_ARC_RADIUS * Math.sin(radians)
        };
      }

      // src/dom/element-guards.ts
      function isObjectLike(value) {
        return typeof value === "object" && value !== null;
      }
      function hasDataset(value) {
        return value instanceof Element && "dataset" in value && isObjectLike(value.dataset);
      }
      function isFocusableElement(value) {
        return value instanceof Element && "focus" in value && typeof value.focus === "function";
      }
      function isStyleDeclaration(value) {
        return isObjectLike(value) && "getPropertyPriority" in value && typeof value.getPropertyPriority === "function" && "getPropertyValue" in value && typeof value.getPropertyValue === "function" && "removeProperty" in value && typeof value.removeProperty === "function" && "setProperty" in value && typeof value.setProperty === "function";
      }
      function isStyledElement(value) {
        return value instanceof Element && "style" in value && isStyleDeclaration(value.style);
      }
      function isTabbableElement(value) {
        return value instanceof Element && "tabIndex" in value && typeof value.tabIndex === "number";
      }
      function getCanvasBackingSize(value) {
        if (typeof value !== "object" || value === null || !("width" in value) || !("height" in value) || typeof value.width !== "number" || typeof value.height !== "number") {
          return null;
        }
        return {
          width: value.width,
          height: value.height
        };
      }

      // src/dom/dom-helpers.ts
      function isFocusableValue(value) {
        return isFocusableElement(value);
      }
      function isElementVisible(element) {
        if (!element || !element.isConnected) {
          return false;
        }
        const style = window.getComputedStyle(element);
        if (style.display === "none" || style.visibility === "hidden") {
          return false;
        }
        const rect = element.getBoundingClientRect();
        return rect.width > 0 && rect.height > 0;
      }
      function hasVisibleLayer(selector) {
        for (const layer of document.querySelectorAll(selector)) {
          if (isElementVisible(layer)) {
            return true;
          }
        }
        return false;
      }
      function escapeMenuText(value) {
        return String(value).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
      }
      function focusElementWithoutScroll(element) {
        if (!isFocusableValue(element)) {
          return;
        }
        try {
          element.focus({ preventScroll: true });
        } catch {
          element.focus();
        }
      }
      function keepOutOfBrowserTabOrder(element) {
        if (isTabbableElement(element)) {
          element.tabIndex = -1;
        }
      }
      function keepInBrowserTabOrder(element) {
        if (isTabbableElement(element)) {
          element.tabIndex = 0;
        }
      }
      function matchesElementOrDescendant(node, selector) {
        if (!(node instanceof Element)) {
          return false;
        }
        return node.matches(selector) || Boolean(node.closest(selector)) || Boolean(node.querySelector(selector));
      }
      function mutationTouchesSelector(record, selector) {
        const targetElement = record.target instanceof Element ? record.target : record.target.parentElement instanceof Element ? record.target.parentElement : null;
        if (matchesElementOrDescendant(targetElement, selector)) {
          return true;
        }
        for (const node of record.addedNodes) {
          if (matchesElementOrDescendant(node, selector)) {
            return true;
          }
        }
        for (const node of record.removedNodes) {
          if (matchesElementOrDescendant(node, selector)) {
            return true;
          }
        }
        return false;
      }

      // src/features/game-volume-menu-item.ts
      function isGameVolumeMenuItemElement(value) {
        return value instanceof Element && hasDataset(value) && isStyledElement(value) && "cursor" in value.style && "userSelect" in value.style;
      }
      function findGameVolumeItem() {
        const candidates = document.querySelectorAll(".items.left .item, .item");
        for (const candidate of candidates) {
          if (/^Volume:\s*\d+%$/.test(candidate.textContent?.trim() || "") && isGameVolumeMenuItemElement(candidate)) {
            return candidate;
          }
        }
        return null;
      }
      function updateGameVolumeItemView(item, gamePercent) {
        item.textContent = `Volume: ${gamePercent}%`;
        item.setAttribute("title", "Scroll or use arrow keys to adjust by 5%, left-click up, right-click down");
        item.style.cursor = "ns-resize";
        item.style.userSelect = "none";
        keepOutOfBrowserTabOrder(item);
        item.setAttribute("role", "slider");
        item.setAttribute("aria-label", "Game volume");
        item.setAttribute("aria-valuemin", "0");
        item.setAttribute("aria-valuemax", "100");
        item.setAttribute("aria-valuenow", String(gamePercent));
        item.setAttribute("aria-valuetext", `${gamePercent}%`);
      }

      // src/features/game-volume-menu-control.ts
      function readNumberProperty(source, property) {
        const value = readObjectProperty(source, property);
        return typeof value === "number" ? value : Number(value);
      }
      function createGameVolumeMenuController(options) {
        let currentGameMenuItem = null;
        function updateGameVolumeText() {
          if (!options.isAudioEnabled()) {
            return;
          }
          if (!currentGameMenuItem || !currentGameMenuItem.isConnected) {
            currentGameMenuItem = findGameVolumeItem();
          }
          if (!currentGameMenuItem) {
            return;
          }
          const gamePercent = options.getGamePercent();
          updateGameVolumeItemView(currentGameMenuItem, gamePercent);
        }
        function patchGameVolumeMenu() {
          if (!options.isAudioEnabled()) {
            return false;
          }
          const item = findGameVolumeItem();
          if (!item) {
            return false;
          }
          currentGameMenuItem = item;
          if (!item.dataset.qolboxGameVolumePatched) {
            item.dataset.qolboxGameVolumePatched = "true";
            item.addEventListener(
              "click",
              (event) => {
                if (!options.isAudioEnabled()) {
                  return;
                }
                event.preventDefault();
                event.stopImmediatePropagation();
                focusElementWithoutScroll(item);
                options.setGamePercent(options.getGamePercent() + options.stepPercent);
              },
              true
            );
            item.addEventListener(
              "contextmenu",
              (event) => {
                if (!options.isAudioEnabled()) {
                  return;
                }
                event.preventDefault();
                event.stopImmediatePropagation();
                focusElementWithoutScroll(item);
                options.setGamePercent(options.getGamePercent() - options.stepPercent);
              },
              true
            );
            item.addEventListener(
              "wheel",
              (event) => {
                if (!options.isAudioEnabled()) {
                  return;
                }
                event.preventDefault();
                event.stopImmediatePropagation();
                focusElementWithoutScroll(item);
                options.setGamePercent(
                  options.getGamePercent() + (readNumberProperty(event, "deltaY") < 0 ? options.stepPercent : -options.stepPercent)
                );
              },
              { passive: false, capture: true }
            );
            item.addEventListener(
              "keydown",
              (event) => {
                if (!options.isAudioEnabled()) {
                  return;
                }
                const nextPercent = getKeyboardPercentTarget(
                  event,
                  options.getGamePercent(),
                  options.stepPercent
                );
                if (nextPercent === null) {
                  return;
                }
                event.preventDefault();
                event.stopImmediatePropagation();
                options.setGamePercent(nextPercent);
              },
              true
            );
          }
          updateGameVolumeText();
          return true;
        }
        return {
          patchGameVolumeMenu,
          updateGameVolumeText
        };
      }

      // src/features/game-volume-control.ts
      function createGameVolumeController(options) {
        let gamePercent = loadGamePercent();
        const howlerAudio = createHowlerGameAudioAdapter({
          getGameVolumeScalar: () => options.isAudioEnabled() ? percentToGameScalar(gamePercent) : 1,
          isAudioEnabled: options.isAudioEnabled,
          shouldSuppressReserveRetryAudio: options.isReserveRetryAudioSuppressed
        });
        const menuController = createGameVolumeMenuController({
          stepPercent: STEP_PERCENT,
          getGamePercent: () => gamePercent,
          isAudioEnabled: options.isAudioEnabled,
          setGamePercent
        });
        function updateGameVolumeText() {
          menuController.updateGameVolumeText();
        }
        function applyGameVolume() {
          updateGameVolumeText();
          howlerAudio.applyGameVolumeToHowls();
        }
        function setGamePercent(nextPercent) {
          gamePercent = clampPercent(nextPercent, DEFAULT_GAME_PERCENT);
          saveGamePercent(gamePercent);
          applyGameVolume();
        }
        function patchGameVolumeMenu() {
          return menuController.patchGameVolumeMenu();
        }
        function shouldSuppressReserveRetryAudio() {
          return options.isReserveRetryAudioSuppressed();
        }
        function hookHowlPrototype() {
          const volumePatched = howlerAudio.hookHowlPrototype();
          if (volumePatched) {
            applyGameVolume();
          }
          return volumePatched;
        }
        return {
          applyGameVolume,
          hookHowlPrototype,
          patchGameVolumeMenu,
          setGamePercent,
          shouldSuppressReserveRetryAudio
        };
      }

      // src/hitbox/youtube-player-native.ts
      function isRecord4(value) {
        return typeof value === "object" && value !== null;
      }
      function isNativeCallable2(value) {
        return typeof value === "function";
      }
      function isConstructableCallable(value) {
        return typeof value === "function";
      }
      function readBooleanProperty2(source, property) {
        return readObjectProperty(source, property) === true;
      }

      // src/hitbox/youtube-player-options-wrapper.ts
      function wrapYouTubePlayerOptions(args, options) {
        const wrappedArgs = Array.from(args);
        const optionsArg = wrappedArgs[1];
        if (!isRecord4(optionsArg)) {
          return wrappedArgs;
        }
        const events = isRecord4(optionsArg.events) ? optionsArg.events : {};
        const originalOnReady = events.onReady;
        if (readBooleanProperty2(originalOnReady, "__qolboxWrapped")) {
          return wrappedArgs;
        }
        const wrappedEvents = {
          ...events,
          onReady(event, ...readyArgs) {
            const player = readObjectProperty(event, "target") || options.getPlayer();
            options.onPlayerReady(player);
            options.onPlayerStateNeeded(player || options.getPlayer());
            try {
              return isNativeCallable2(originalOnReady) ? Reflect.apply(originalOnReady, this, [event, ...readyArgs]) : void 0;
            } finally {
              window.setTimeout(() => {
                options.onPlayerStateNeeded(player || options.getPlayer());
              }, 0);
            }
          }
        };
        setObjectProperty(wrappedEvents.onReady, "__qolboxWrapped", true);
        setObjectProperty(wrappedEvents.onReady, "__qolboxOriginal", originalOnReady);
        wrappedArgs[1] = {
          ...optionsArg,
          events: wrappedEvents
        };
        return wrappedArgs;
      }

      // src/hitbox/youtube-player-adapter.ts
      function createYouTubeJukeboxAdapter(options) {
        let trackedPlayers = /* @__PURE__ */ new Set();
        let hookInstalled = false;
        let playerStateApplied = false;
        let retryTimer = 0;
        let retryCount = 0;
        let readyCallbackHookInstalled = false;
        function trackPlayer(player) {
          if (!player || !isNativeCallable2(readObjectProperty(player, "setVolume"))) {
            return;
          }
          trackedPlayers.add(player);
        }
        function discoverPlayers() {
          const yt = readObjectProperty(window, "YT");
          const getPlayer = readObjectProperty(yt, "get");
          if (!isNativeCallable2(getPlayer)) {
            return;
          }
          for (const candidate of document.querySelectorAll("#ytContainer [id], #ytContainer iframe[id]")) {
            if (!candidate.id) {
              continue;
            }
            try {
              const player = Reflect.apply(getPlayer, yt, [candidate.id]);
              trackPlayer(player);
            } catch {
            }
          }
        }
        function applyPlayerState(player) {
          if (!options.isEnabled()) {
            return;
          }
          const setVolume = readObjectProperty(player, "setVolume");
          if (!player || !isNativeCallable2(setVolume)) {
            trackedPlayers.delete(player);
            return;
          }
          try {
            const setPlaybackRate = readObjectProperty(player, "setPlaybackRate");
            const getPlaybackRate = readObjectProperty(player, "getPlaybackRate");
            const playbackRate = isNativeCallable2(getPlaybackRate) ? Reflect.apply(getPlaybackRate, player, []) : null;
            if (isNativeCallable2(setPlaybackRate) && playbackRate !== 1) {
              Reflect.apply(setPlaybackRate, player, [1]);
            }
            const getVolume = readObjectProperty(player, "getVolume");
            const getMuted = readObjectProperty(player, "isMuted");
            const currentVolume = isNativeCallable2(getVolume) ? Reflect.apply(getVolume, player, []) : null;
            const currentlyMuted = isNativeCallable2(getMuted) ? Reflect.apply(getMuted, player, []) : null;
            if (options.isMuted()) {
              if (currentVolume !== 0) {
                Reflect.apply(setVolume, player, [0]);
              }
              const mute = readObjectProperty(player, "mute");
              if (isNativeCallable2(mute) && currentlyMuted !== true) {
                Reflect.apply(mute, player, []);
              }
            } else {
              const targetVolume = options.getVolume();
              if (currentVolume !== targetVolume) {
                Reflect.apply(setVolume, player, [targetVolume]);
              }
              const unMute = readObjectProperty(player, "unMute");
              if (isNativeCallable2(unMute) && currentlyMuted === true) {
                Reflect.apply(unMute, player, []);
              }
            }
            playerStateApplied = true;
          } catch {
            trackedPlayers.delete(player);
          }
        }
        function applyToTrackedPlayers() {
          if (!options.isEnabled()) {
            return;
          }
          discoverPlayers();
          for (const player of Array.from(trackedPlayers)) {
            applyPlayerState(player);
          }
        }
        function restoreTrackedPlayers(volume) {
          if (!playerStateApplied) {
            return;
          }
          for (const player of Array.from(trackedPlayers)) {
            const setVolume = readObjectProperty(player, "setVolume");
            if (!player || !isNativeCallable2(setVolume)) {
              trackedPlayers.delete(player);
              continue;
            }
            try {
              Reflect.apply(setVolume, player, [volume]);
              const unMute = readObjectProperty(player, "unMute");
              if (isNativeCallable2(unMute)) {
                Reflect.apply(unMute, player, []);
              }
            } catch {
              trackedPlayers.delete(player);
            }
          }
          playerStateApplied = false;
        }
        function scheduleRetry() {
          if (!options.isEnabled() || hookInstalled || retryTimer || retryCount >= options.maxRetries) {
            return;
          }
          retryCount += 1;
          retryTimer = window.setTimeout(() => {
            retryTimer = 0;
            hookPlayerConstructor();
            options.onPlayerStateNeeded();
          }, options.retryDelayMs);
        }
        function wrapReadyCallback(callback) {
          if (!isNativeCallable2(callback) || readBooleanProperty2(callback, "__qolboxWrapped")) {
            return callback;
          }
          const nativeCallback = callback;
          function wrappedYouTubeReadyCallback(...args) {
            if (options.isEnabled()) {
              hookPlayerConstructor();
              options.onPlayerStateNeeded();
            }
            try {
              return Reflect.apply(nativeCallback, this, args);
            } finally {
              if (options.isEnabled()) {
                hookPlayerConstructor();
                window.setTimeout(options.onPlayerStateNeeded, 0);
              }
            }
          }
          setObjectProperty(wrappedYouTubeReadyCallback, "__qolboxWrapped", true);
          setObjectProperty(wrappedYouTubeReadyCallback, "__qolboxOriginal", callback);
          return wrappedYouTubeReadyCallback;
        }
        function installReadyCallbackHook() {
          if (!options.isEnabled() || readyCallbackHookInstalled) {
            return;
          }
          const descriptor = Object.getOwnPropertyDescriptor(window, "onYouTubeIframeAPIReady");
          if (descriptor && (!descriptor.configurable || descriptor.get || descriptor.set)) {
            return;
          }
          readyCallbackHookInstalled = true;
          let readyCallback = wrapReadyCallback(
            descriptor ? descriptor.value : readObjectProperty(window, "onYouTubeIframeAPIReady")
          );
          try {
            Object.defineProperty(window, "onYouTubeIframeAPIReady", {
              configurable: true,
              enumerable: true,
              get() {
                return readyCallback;
              },
              set(value) {
                readyCallback = wrapReadyCallback(value);
              }
            });
          } catch {
            readyCallbackHookInstalled = false;
          }
        }
        function hookPlayerConstructor() {
          if (!options.isEnabled()) {
            return false;
          }
          installReadyCallbackHook();
          const yt = readObjectProperty(window, "YT");
          const playerConstructor = readObjectProperty(yt, "Player");
          if (!isConstructableCallable(playerConstructor)) {
            scheduleRetry();
            return false;
          }
          if (retryTimer) {
            window.clearTimeout(retryTimer);
            retryTimer = 0;
          }
          retryCount = 0;
          if (hookInstalled || readBooleanProperty2(playerConstructor, "__qolboxWrapped")) {
            hookInstalled = true;
            discoverPlayers();
            return true;
          }
          const OriginalPlayer = playerConstructor;
          function WrappedPlayer(...args) {
            let instance = null;
            const wrappedArgs = wrapYouTubePlayerOptions(args, {
              getPlayer: () => instance,
              onPlayerReady: trackPlayer,
              onPlayerStateNeeded: applyPlayerState
            });
            instance = new OriginalPlayer(...wrappedArgs);
            trackPlayer(instance);
            window.setTimeout(() => {
              applyPlayerState(instance);
            }, 0);
            return instance;
          }
          Object.setPrototypeOf(WrappedPlayer, OriginalPlayer);
          setObjectProperty(WrappedPlayer, "prototype", readObjectProperty(OriginalPlayer, "prototype"));
          setObjectProperty(WrappedPlayer, "__qolboxWrapped", true);
          setObjectProperty(yt, "Player", WrappedPlayer);
          hookInstalled = true;
          discoverPlayers();
          return true;
        }
        return {
          applyToTrackedPlayers,
          hookPlayerConstructor,
          installReadyCallbackHook,
          restoreTrackedPlayers
        };
      }

      // src/features/jukebox-dom-helpers.ts
      function readJukeboxProperty(source, property) {
        return readObjectProperty(source, property);
      }
      function setJukeboxProperty(source, property, value) {
        return setObjectProperty(source, property, value);
      }
      function readJukeboxNumberProperty(source, property) {
        const value = readJukeboxProperty(source, property);
        return typeof value === "number" ? value : Number(value);
      }
      function readJukeboxBooleanProperty(source, property) {
        return readJukeboxProperty(source, property) === true;
      }
      function isNativeCallable3(value) {
        return typeof value === "function";
      }
      function isJukeboxStyleDatasetElement(value) {
        return value instanceof Element && typeof readJukeboxProperty(value, "dataset") === "object" && typeof readJukeboxProperty(value, "style") === "object";
      }
      function requestJukeboxPointerCapture(knob, event) {
        const setPointerCapture = readJukeboxProperty(knob, "setPointerCapture");
        const pointerId = readJukeboxProperty(event, "pointerId");
        if (!isNativeCallable3(setPointerCapture) || pointerId === void 0) {
          return;
        }
        try {
          Reflect.apply(setPointerCapture, knob, [pointerId]);
        } catch {
        }
      }

      // src/features/chat-input-elements.ts
      function hasEditableChatValue(value) {
        return value instanceof Element && "value" in value && typeof value.value === "string";
      }
      function canBlur(value) {
        return typeof value === "object" && value !== null && "blur" in value && typeof value.blur === "function";
      }
      function isChatInputElement(element, selector) {
        return element instanceof Element && element.matches(selector);
      }
      function isLobbyChatInputElement(element, selector) {
        return element instanceof Element && element.matches(selector);
      }
      function getActiveChatInputElement(target, selector) {
        if (isChatInputElement(target, selector)) {
          return target;
        }
        if (target instanceof Element) {
          const closestChatInput = target.closest(selector);
          if (isChatInputElement(closestChatInput, selector)) {
            return closestChatInput;
          }
        }
        return document.querySelector(".inGameChat .input:focus, .lobbyContainer .chatBox .input:focus");
      }

      // src/features/chat-keyboard-events.ts
      function readTextProperty(source, property) {
        const value = readObjectProperty(source, property);
        return typeof value === "string" ? value : void 0;
      }
      function isEscapeKey(event) {
        const key = readTextProperty(event, "key");
        const code = readTextProperty(event, "code");
        return key === "Escape" || key === "Esc" || code === "Escape";
      }
      function isTabKey(event) {
        const key = readTextProperty(event, "key");
        const code = readTextProperty(event, "code");
        return key === "Tab" || code === "Tab";
      }
      function isEnterKey(event) {
        return readTextProperty(event, "key") === "Enter";
      }
      function isArrowLeftKey(event) {
        return readTextProperty(event, "key") === "ArrowLeft" || readTextProperty(event, "code") === "ArrowLeft";
      }
      function isArrowRightKey(event) {
        return readTextProperty(event, "key") === "ArrowRight" || readTextProperty(event, "code") === "ArrowRight";
      }

      // src/features/chat-input-controls.ts
      function createChatInputController(options) {
        let escapeHooksInstalled = false;
        let commandAliasHooksInstalled = false;
        let suppressEscapeKeyUntil = 0;
        const originalTabIndexByInput = /* @__PURE__ */ new Map();
        function isChatInput(element) {
          return isChatInputElement(element, options.chatInputSelector);
        }
        function isLobbyChatInput(element) {
          return isLobbyChatInputElement(element, options.lobbyChatInputSelector);
        }
        function getActiveChatInput(target = document.activeElement) {
          return getActiveChatInputElement(target, options.chatInputSelector);
        }
        function restoreLobbyChatPrompt(input) {
          if (!isLobbyChatInput(input)) {
            return;
          }
          const chatBox = input.closest(".lobbyContainer .chatBox");
          const instruction = chatBox ? chatBox.querySelector(".lowerInstruction") : null;
          if (instruction) {
            instruction.style.visibility = "inherit";
            if (!(instruction.textContent || "").trim()) {
              instruction.textContent = options.isTouchLobbyChatPrompt() ? options.touchLobbyChatPrompt : options.desktopLobbyChatPrompt;
            }
          }
          if (!options.isTouchLobbyChatPrompt() && isStyledElement(input)) {
            input.style.pointerEvents = "none";
          }
        }
        function closeChatInput(input) {
          if (!options.isChatFeatureEnabled() || !isChatInput(input) || !hasEditableChatValue(input) || !canBlur(input)) {
            return false;
          }
          const closingLobbyChat = isLobbyChatInput(input);
          input.value = "";
          input.dispatchEvent(new Event("input", { bubbles: true }));
          input.blur();
          input.classList.remove("bgActive");
          if (closingLobbyChat) {
            restoreLobbyChatPrompt(input);
          } else {
            options.focusActiveRenderCanvas();
          }
          return true;
        }
        function handleChatEscape(event) {
          if (!options.isChatFeatureEnabled() || !isEscapeKey(event)) {
            return;
          }
          const input = getActiveChatInput(event.target);
          const suppressingKeyup = event.type === "keyup" && Date.now() < suppressEscapeKeyUntil;
          if (!input && !suppressingKeyup) {
            return;
          }
          event.preventDefault();
          event.stopPropagation();
          event.stopImmediatePropagation();
          if (event.type === "keydown" && input) {
            suppressEscapeKeyUntil = Date.now() + 500;
            closeChatInput(input);
          }
        }
        function installChatEscapeHooks() {
          if (escapeHooksInstalled) {
            return;
          }
          escapeHooksInstalled = true;
          window.addEventListener("keydown", handleChatEscape, true);
          window.addEventListener("keyup", handleChatEscape, true);
          document.addEventListener("keydown", handleChatEscape, true);
          document.addEventListener("keyup", handleChatEscape, true);
        }
        function handleChatCommandAliasKeydown(event) {
          if (!options.areLobbyCommandsEnabled() || !isEnterKey(event)) {
            return;
          }
          const input = event.target;
          if (!isChatInput(input)) {
            return;
          }
          if (hasEditableChatValue(input)) {
            input.value = options.expandNativeChatAlias(input.value);
          }
        }
        function installChatCommandAliasHooks() {
          if (commandAliasHooksInstalled) {
            return;
          }
          commandAliasHooksInstalled = true;
          document.addEventListener("keydown", handleChatCommandAliasKeydown, true);
        }
        function patchChatTabOrder() {
          if (!options.isChatFeatureEnabled()) {
            return;
          }
          if (!document.querySelector(".inGameChat, .lobbyContainer")) {
            return;
          }
          for (const input of document.querySelectorAll(options.chatInputSelector)) {
            if (!originalTabIndexByInput.has(input)) {
              originalTabIndexByInput.set(input, input.getAttribute("tabindex"));
            }
            keepOutOfBrowserTabOrder(input);
          }
        }
        function restoreChatTabOrder() {
          for (const [input, originalTabIndex] of originalTabIndexByInput) {
            if (originalTabIndex === null) {
              input.removeAttribute("tabindex");
            } else {
              input.setAttribute("tabindex", originalTabIndex);
            }
          }
          originalTabIndexByInput.clear();
        }
        return {
          closeChatInput,
          getActiveChatInput,
          installChatCommandAliasHooks,
          installChatEscapeHooks,
          isChatInput,
          patchChatTabOrder,
          restoreChatTabOrder,
          restoreLobbyChatPrompt
        };
      }

      // src/features/jukebox-keyboard-focus.ts
      function readProperty(source, property) {
        return readObjectProperty(source, property);
      }
      function isNativeCallable4(value) {
        return typeof value === "function";
      }
      function isStyleElement(value) {
        return value instanceof Element && typeof readProperty(value, "style") === "object";
      }
      function isStyleDatasetElement(value) {
        return value instanceof Element && typeof readProperty(value, "dataset") === "object" && typeof readProperty(value, "style") === "object";
      }
      function createJukeboxKeyboardFocusController(options) {
        let tabFocusHooksInstalled = false;
        function setJukeboxBottom(jukebox, bottom) {
          if (isStyleElement(jukebox)) {
            jukebox.style.bottom = bottom;
          }
        }
        function openJukeboxFromKeyboardFocus(jukebox) {
          if (!options.isAudioEnabled() || !jukebox) {
            return;
          }
          options.resetBrowserScroll();
          setJukeboxBottom(jukebox, "0px");
          const onMouseEnter = readProperty(jukebox, "onmouseenter");
          if (isNativeCallable4(onMouseEnter)) {
            Reflect.apply(onMouseEnter, jukebox, []);
          } else {
            setJukeboxBottom(jukebox, "0px");
          }
          options.scheduleUiWork({ force: true, passes: options.resizeSettlePasses });
        }
        function closeJukeboxFromKeyboardFocus(jukebox, nextFocusTarget) {
          if (!options.isAudioEnabled() || !jukebox || nextFocusTarget instanceof Element && jukebox.contains(nextFocusTarget) || jukebox.matches(":hover")) {
            return;
          }
          const onMouseLeave = readProperty(jukebox, "onmouseleave");
          if (isNativeCallable4(onMouseLeave)) {
            Reflect.apply(onMouseLeave, jukebox, []);
          } else {
            setJukeboxBottom(jukebox, "-50px");
          }
        }
        function focusJukeboxKnobFromTab(knob) {
          if (!options.isAudioEnabled()) {
            return false;
          }
          const jukebox = knob?.closest(".jukebox") || null;
          if (!jukebox) {
            return false;
          }
          openJukeboxFromKeyboardFocus(jukebox);
          focusElementWithoutScroll(knob);
          options.resetBrowserScroll();
          return true;
        }
        function isGameplayTabFocusContext(target, knob) {
          const activeCanvas = options.getActiveRenderCanvas();
          return target === window || target === document || target === document.body || target === document.documentElement || target === activeCanvas || target === knob;
        }
        function handleGameplayTabFocus(event) {
          if (!options.isAudioEnabled() || !isTabKey(event) || event.altKey || event.ctrlKey || event.metaKey || options.isChatInput(event.target) || options.getActiveRenderMode() !== "gameplay") {
            return;
          }
          const knob = options.findJukeboxKnob();
          const jukebox = knob?.closest(".jukebox") || null;
          if (!knob || !jukebox || !isElementVisible(jukebox) || !isGameplayTabFocusContext(event.target, knob)) {
            return;
          }
          event.preventDefault();
          if (document.activeElement === knob) {
            options.focusActiveRenderCanvas();
            closeJukeboxFromKeyboardFocus(jukebox, document.activeElement);
            return;
          }
          focusJukeboxKnobFromTab(knob);
        }
        function installTabFocusHooks() {
          if (tabFocusHooksInstalled) {
            return;
          }
          tabFocusHooksInstalled = true;
          window.addEventListener("keydown", handleGameplayTabFocus, true);
        }
        function patchJukeboxKeyboardFocus(knob) {
          if (!options.isAudioEnabled()) {
            return;
          }
          const jukebox = knob?.closest(".jukebox") || null;
          if (!isStyleDatasetElement(jukebox) || jukebox.dataset.qolboxKeyboardFocusPatched) {
            return;
          }
          jukebox.dataset.qolboxKeyboardFocusPatched = "true";
          jukebox.addEventListener("focusin", () => openJukeboxFromKeyboardFocus(jukebox), true);
          jukebox.addEventListener(
            "focusout",
            (event) => closeJukeboxFromKeyboardFocus(jukebox, readProperty(event, "relatedTarget")),
            true
          );
        }
        return {
          handleGameplayTabFocus,
          installTabFocusHooks,
          patchJukeboxKeyboardFocus
        };
      }

      // src/features/jukebox-knob-interaction.ts
      function createJukeboxKnobInteractionController(options) {
        let activeKnobDrag = null;
        function isKnobDragActive() {
          return Boolean(activeKnobDrag);
        }
        function getKnobPercentFromPointer(event) {
          if (!activeKnobDrag) {
            return DEFAULT_JUKEBOX_PERCENT;
          }
          const deltaY = activeKnobDrag.startY - readJukeboxNumberProperty(event, "clientY");
          return clampJukeboxPercent(activeKnobDrag.startPercent + deltaY * options.dragSensitivity);
        }
        function onKnobPointerMove(event) {
          if (!options.isAudioEnabled() || !activeKnobDrag) {
            return;
          }
          event.preventDefault();
          options.setJukeboxPercent(getKnobPercentFromPointer(event));
        }
        function endKnobDrag() {
          activeKnobDrag = null;
        }
        function patchGlobalKnobListeners() {
          if (readJukeboxBooleanProperty(window, "__qolboxJukeboxGlobalsPatched")) {
            return;
          }
          setJukeboxProperty(window, "__qolboxJukeboxGlobalsPatched", true);
          window.addEventListener("pointermove", onKnobPointerMove, true);
          window.addEventListener("mousemove", onKnobPointerMove, true);
          window.addEventListener("pointerup", endKnobDrag, true);
          window.addEventListener("mouseup", endKnobDrag, true);
          window.addEventListener("blur", endKnobDrag, true);
        }
        function patchJukeboxKnobInteraction(knob) {
          patchGlobalKnobListeners();
          if (knob.dataset.qolboxJukeboxPatched) {
            return;
          }
          knob.dataset.qolboxJukeboxPatched = "true";
          knob.setAttribute("title", "Scroll, drag, or use arrow keys to adjust the jukebox volume");
          knob.style.touchAction = "none";
          knob.addEventListener(
            "pointerdown",
            (event) => {
              if (!options.isAudioEnabled()) {
                return;
              }
              event.preventDefault();
              event.stopPropagation();
              focusElementWithoutScroll(knob);
              requestJukeboxPointerCapture(knob, event);
              if (options.unmuteJukeboxIfMuted()) {
                options.updateJukeboxMenuItem();
                options.applyJukeboxState();
              }
              activeKnobDrag = {
                startY: readJukeboxNumberProperty(event, "clientY"),
                startPercent: options.getJukeboxPercent() ?? DEFAULT_JUKEBOX_PERCENT
              };
              onKnobPointerMove(event);
            },
            true
          );
          knob.addEventListener(
            "wheel",
            (event) => {
              if (!options.isAudioEnabled()) {
                return;
              }
              event.preventDefault();
              event.stopPropagation();
              focusElementWithoutScroll(knob);
              options.ensureJukeboxPercent(knob);
              const currentPercent = options.isJukeboxMuted() ? 0 : options.getJukeboxPercent();
              options.setJukeboxPercent(
                (currentPercent ?? DEFAULT_JUKEBOX_PERCENT) + (readJukeboxNumberProperty(event, "deltaY") < 0 ? options.wheelStep : -options.wheelStep)
              );
            },
            { passive: false }
          );
          knob.addEventListener(
            "keydown",
            (event) => {
              if (!options.isAudioEnabled()) {
                return;
              }
              const currentPercent = options.isJukeboxMuted() ? 0 : options.getEffectiveJukeboxPercent();
              const nextPercent = getKeyboardPercentTarget(event, currentPercent, options.wheelStep);
              if (nextPercent === null) {
                return;
              }
              event.preventDefault();
              event.stopPropagation();
              options.ensureJukeboxPercent(knob);
              options.setJukeboxPercent(nextPercent);
            },
            true
          );
        }
        return {
          isKnobDragActive,
          patchJukeboxKnobInteraction
        };
      }

      // src/features/jukebox-menu-control.ts
      function createJukeboxMenuController(options) {
        let currentJukeboxMenuItem = null;
        function updateJukeboxMenuItem() {
          if (!currentJukeboxMenuItem || !currentJukeboxMenuItem.isConnected) {
            return;
          }
          currentJukeboxMenuItem.textContent = options.getLabel();
          currentJukeboxMenuItem.setAttribute("title", "Remember the lobby radio mute state");
        }
        function patchJukeboxMenu() {
          if (!options.isAudioEnabled()) {
            return false;
          }
          const container = options.findSettingsContainer();
          if (!container) {
            return false;
          }
          let item = container.querySelector('.item[data-qolbox-jukebox-menu="true"]');
          if (!item) {
            const createdItem = document.createElement("div");
            createdItem.className = "item";
            createdItem.dataset.qolboxJukeboxMenu = "true";
            createdItem.addEventListener(
              "click",
              (event) => {
                event.preventDefault();
                event.stopImmediatePropagation();
                options.onToggleMute();
              },
              true
            );
            const beforeItem = options.findChangeControlsItem(container);
            if (beforeItem) {
              container.insertBefore(createdItem, beforeItem);
            } else {
              container.appendChild(createdItem);
            }
            item = createdItem;
          }
          currentJukeboxMenuItem = item;
          updateJukeboxMenuItem();
          return true;
        }
        function removeJukeboxMenuItem() {
          if (currentJukeboxMenuItem && currentJukeboxMenuItem.isConnected) {
            currentJukeboxMenuItem.remove();
          }
          currentJukeboxMenuItem = null;
        }
        return {
          patchJukeboxMenu,
          removeJukeboxMenuItem,
          updateJukeboxMenuItem
        };
      }

      // src/features/jukebox-knob-view.ts
      function isStyleElement2(value) {
        return value instanceof Element && typeof readObjectProperty(value, "style") === "object";
      }
      function findJukeboxKnob() {
        return document.querySelector(".jukebox .knob.volumeContainer");
      }
      function readJukeboxPercentFromKnob(knob) {
        const bar = knob ? knob.querySelector(".barSVG") : null;
        if (!isStyleElement2(bar)) {
          return null;
        }
        const inlineAngle = parseJukeboxAngleFromTransform(bar.style.transform);
        if (inlineAngle !== null) {
          return angleToJukeboxPercent(inlineAngle);
        }
        const computedAngle = parseJukeboxAngleFromTransform(window.getComputedStyle(bar).transform);
        if (computedAngle !== null) {
          return angleToJukeboxPercent(computedAngle);
        }
        return null;
      }
      function updateJukeboxKnobAccessibility(knob, visualPercent, state) {
        if (!knob) {
          return;
        }
        const effectivePercent = state.muted ? 0 : clampJukeboxPercent(visualPercent ?? state.percent ?? DEFAULT_JUKEBOX_PERCENT);
        knob.setAttribute("aria-label", "Jukebox volume");
        knob.setAttribute("aria-orientation", "vertical");
        knob.setAttribute("aria-valuemin", "0");
        knob.setAttribute("aria-valuemax", "100");
        knob.setAttribute("aria-valuenow", String(effectivePercent));
        knob.setAttribute("aria-valuetext", state.muted ? `Muted (${effectivePercent}%)` : `${effectivePercent}%`);
        knob.setAttribute("role", "slider");
        keepInBrowserTabOrder(knob);
      }
      function setJukeboxKnobVisual(knob, visualPercent, state) {
        if (!knob) {
          return;
        }
        const angle = percentToJukeboxAngle(visualPercent ?? DEFAULT_JUKEBOX_PERCENT);
        const bar = knob.querySelector(".barSVG");
        const arcPath = knob.querySelector(".arcSVG path");
        if (isStyleElement2(bar)) {
          bar.style.transform = `rotate(${angle}deg)`;
        }
        if (arcPath) {
          const startPoint = polarToArcPoint(JUKEBOX_MIN_ANGLE);
          const endPoint = polarToArcPoint(angle);
          const sweepDegrees = Math.max(0, angle - JUKEBOX_MIN_ANGLE);
          const largeArcFlag = sweepDegrees > 180 ? 1 : 0;
          arcPath.setAttribute(
            "d",
            `M ${startPoint.x} ${startPoint.y} A ${JUKEBOX_ARC_RADIUS} ${JUKEBOX_ARC_RADIUS} 0 ${largeArcFlag} 1 ${endPoint.x} ${endPoint.y}`
          );
        }
        updateJukeboxKnobAccessibility(knob, visualPercent, state);
      }
      function clearJukeboxKnobAccessibility(knob) {
        if (!knob) {
          return;
        }
        knob.removeAttribute("aria-label");
        knob.removeAttribute("aria-orientation");
        knob.removeAttribute("aria-valuemin");
        knob.removeAttribute("aria-valuemax");
        knob.removeAttribute("aria-valuenow");
        knob.removeAttribute("aria-valuetext");
        knob.removeAttribute("role");
        if (knob.getAttribute("tabindex") === "0") {
          knob.removeAttribute("tabindex");
        }
      }

      // src/features/jukebox-state.ts
      function createJukeboxStateController() {
        let state = loadJukeboxState();
        function persistState() {
          saveJukeboxState(state);
        }
        function getEffectivePercent() {
          return clampJukeboxPercent(state.percent ?? DEFAULT_JUKEBOX_PERCENT);
        }
        function ensurePercent(readPercent) {
          if (state.percent !== null) {
            return;
          }
          state.percent = readPercent() ?? DEFAULT_JUKEBOX_PERCENT;
          persistState();
        }
        function setPercent(nextPercent) {
          state.percent = clampJukeboxPercent(nextPercent);
          state.muted = false;
          persistState();
        }
        function toggleMuted() {
          state.muted = !state.muted;
          persistState();
        }
        function unmuteIfMuted() {
          if (!state.muted) {
            return false;
          }
          state.muted = false;
          persistState();
          return true;
        }
        function setState(nextState) {
          state = {
            muted: Boolean(nextState.muted),
            percent: nextState.percent ?? null
          };
        }
        function getState() {
          return state;
        }
        function getPercent() {
          return state.percent;
        }
        function isMuted() {
          return state.muted;
        }
        function getMenuLabel() {
          return state.muted ? "Unmute Jukebox" : "Mute Jukebox";
        }
        return {
          ensurePercent,
          getEffectivePercent,
          getMenuLabel,
          getPercent,
          getState,
          isMuted,
          setPercent,
          setState,
          toggleMuted,
          unmuteIfMuted
        };
      }

      // src/features/jukebox-control.ts
      function createJukeboxController(options) {
        const jukeboxState = createJukeboxStateController();
        const youTubeAdapter = createYouTubeJukeboxAdapter({
          getVolume: () => percentToJukeboxVolume(getEffectiveJukeboxPercent()),
          isEnabled: options.isAudioEnabled,
          isMuted: () => jukeboxState.isMuted(),
          maxRetries: options.youTubeHookMaxRetries,
          onPlayerStateNeeded: () => applyJukeboxState(),
          retryDelayMs: options.youTubeHookRetryDelayMs
        });
        const keyboardFocus = createJukeboxKeyboardFocusController({
          resizeSettlePasses: options.resizeSettlePasses,
          findJukeboxKnob,
          focusActiveRenderCanvas: options.focusActiveRenderCanvas,
          getActiveRenderCanvas: options.getActiveRenderCanvas,
          getActiveRenderMode: options.getActiveRenderMode,
          isAudioEnabled: options.isAudioEnabled,
          isChatInput: options.isChatInput,
          resetBrowserScroll: options.resetBrowserScroll,
          scheduleUiWork: options.scheduleUiWork
        });
        const menuController = createJukeboxMenuController({
          findChangeControlsItem: options.findChangeControlsItem,
          findSettingsContainer: options.findSettingsContainer,
          getLabel: getJukeboxMenuLabel,
          isAudioEnabled: options.isAudioEnabled,
          onToggleMute: toggleJukeboxMute
        });
        const knobInteraction = createJukeboxKnobInteractionController({
          dragSensitivity: options.jukeboxDragSensitivity,
          wheelStep: options.jukeboxWheelStep,
          applyJukeboxState,
          ensureJukeboxPercent,
          getEffectiveJukeboxPercent,
          getJukeboxPercent: () => jukeboxState.getPercent(),
          isAudioEnabled: options.isAudioEnabled,
          isJukeboxMuted: () => jukeboxState.isMuted(),
          setJukeboxPercent,
          unmuteJukeboxIfMuted: () => jukeboxState.unmuteIfMuted(),
          updateJukeboxMenuItem
        });
        function installTabFocusHooks() {
          keyboardFocus.installTabFocusHooks();
        }
        function patchJukeboxKeyboardFocus(knob) {
          keyboardFocus.patchJukeboxKeyboardFocus(knob);
        }
        function getJukeboxMenuLabel() {
          return jukeboxState.getMenuLabel();
        }
        function updateJukeboxMenuItem() {
          menuController.updateJukeboxMenuItem();
        }
        function patchJukeboxMenu() {
          return menuController.patchJukeboxMenu();
        }
        function getEffectiveJukeboxPercent() {
          return jukeboxState.getEffectivePercent();
        }
        function ensureJukeboxPercent(knob) {
          if (!knob) {
            return;
          }
          jukeboxState.ensurePercent(() => readJukeboxPercentFromKnob(knob));
        }
        function applyJukeboxStateToKnob(knob) {
          if (!options.isAudioEnabled() || !knob || knobInteraction.isKnobDragActive()) {
            return;
          }
          ensureJukeboxPercent(knob);
          setJukeboxKnobVisual(knob, jukeboxState.isMuted() ? 0 : jukeboxState.getPercent(), jukeboxState.getState());
        }
        function applyJukeboxState() {
          if (!options.isAudioEnabled()) {
            return;
          }
          const knob = findJukeboxKnob();
          applyJukeboxStateToKnob(knob);
          ensureJukeboxPercent(knob);
          youTubeAdapter.applyToTrackedPlayers();
        }
        function installYouTubeReadyCallbackHook() {
          youTubeAdapter.installReadyCallbackHook();
        }
        function hookYouTubePlayer() {
          return youTubeAdapter.hookPlayerConstructor();
        }
        function setJukeboxPercent(nextPercent) {
          if (!options.isAudioEnabled()) {
            return;
          }
          jukeboxState.setPercent(nextPercent);
          updateJukeboxMenuItem();
          setJukeboxKnobVisual(findJukeboxKnob(), jukeboxState.getPercent(), jukeboxState.getState());
          applyJukeboxState();
        }
        function toggleJukeboxMute() {
          if (!options.isAudioEnabled()) {
            return;
          }
          ensureJukeboxPercent(findJukeboxKnob());
          jukeboxState.toggleMuted();
          updateJukeboxMenuItem();
          applyJukeboxState();
        }
        function patchJukeboxKnob() {
          if (!options.isAudioEnabled()) {
            return false;
          }
          const knob = findJukeboxKnob();
          if (!isJukeboxStyleDatasetElement(knob)) {
            return false;
          }
          ensureJukeboxPercent(knob);
          applyJukeboxStateToKnob(knob);
          patchJukeboxKeyboardFocus(knob);
          knobInteraction.patchJukeboxKnobInteraction(knob);
          return true;
        }
        function removeJukeboxMenuItem() {
          menuController.removeJukeboxMenuItem();
        }
        function restoreJukeboxState() {
          const knob = findJukeboxKnob();
          const nativePercent = readJukeboxPercentFromKnob(knob);
          const restorePercent = jukeboxState.isMuted() && nativePercent === 0 ? getEffectiveJukeboxPercent() : nativePercent ?? getEffectiveJukeboxPercent();
          clearJukeboxKnobAccessibility(knob);
          youTubeAdapter.restoreTrackedPlayers(percentToJukeboxVolume(restorePercent));
        }
        function setJukeboxState(nextState) {
          jukeboxState.setState(nextState);
        }
        return {
          applyJukeboxState,
          findJukeboxKnob,
          getEffectiveJukeboxPercent,
          handleGameplayTabFocus: keyboardFocus.handleGameplayTabFocus,
          hookYouTubePlayer,
          installTabFocusHooks,
          installYouTubeReadyCallbackHook,
          patchJukeboxKeyboardFocus,
          patchJukeboxKnob,
          patchJukeboxMenu,
          removeJukeboxMenuItem,
          restoreJukeboxState,
          setJukeboxState
        };
      }

      // src/hitbox/lobby-music-adapter.ts
      function isNativeCallable5(value) {
        return typeof value === "function";
      }
      function getNativeLobbyMusicController() {
        const game = readNativeReflectProperty(window, "a8");
        const controller = readNativeReflectProperty(game, "cR");
        return isNativeReflectTarget(controller) ? controller : null;
      }
      function stopNativeLobbyMusic(controller = getNativeLobbyMusicController()) {
        const stop = readNativeReflectProperty(controller, "stop");
        if (!isNativeCallable5(stop)) {
          return false;
        }
        try {
          Reflect.apply(stop, controller, []);
          return true;
        } catch {
          return false;
        }
      }
      function patchNativeLobbyMusicStart(shouldAllowStart, forcePatch = false) {
        const controller = getNativeLobbyMusicController();
        const start = readNativeReflectProperty(controller, "start");
        if (!isNativeCallable5(start)) {
          return false;
        }
        if (!forcePatch && readNativeReflectProperty(start, "__qolboxWrapped") === true) {
          return true;
        }
        const originalStart = start;
        const wrappedStart = function wrappedLobbyMusicStart(...args) {
          if (shouldAllowStart()) {
            return Reflect.apply(originalStart, this, args);
          }
          stopNativeLobbyMusic(this);
          return void 0;
        };
        setNativeReflectProperty(wrappedStart, "__qolboxWrapped", true);
        setNativeReflectProperty(wrappedStart, "__qolboxOriginal", originalStart);
        return setNativeReflectProperty(controller, "start", wrappedStart);
      }

      // src/features/lobby-music-control.ts
      function createLobbyMusicController(options) {
        let lobbyMusicPatchInstalled = false;
        function isLobbyMusicAllowed() {
          return !options.isAudioEnabled() || !hasVisibleLayer(options.playLayerSelector);
        }
        function stopLobbyMusicIfNeeded() {
          if (!options.isAudioEnabled() || isLobbyMusicAllowed()) {
            return;
          }
          stopNativeLobbyMusic();
        }
        function patchLobbyMusicController() {
          if (!options.isAudioEnabled() && !lobbyMusicPatchInstalled) {
            return false;
          }
          if (!patchNativeLobbyMusicStart(isLobbyMusicAllowed, !lobbyMusicPatchInstalled)) {
            return false;
          }
          lobbyMusicPatchInstalled = true;
          stopLobbyMusicIfNeeded();
          return true;
        }
        return {
          patchLobbyMusicController,
          stopLobbyMusicIfNeeded
        };
      }

      // src/features/audio-feature-bundle.ts
      function createAudioFeatureBundle(options) {
        const gameVolume = createGameVolumeController({
          isAudioEnabled: options.isAudioEnabled,
          isReserveRetryAudioSuppressed: options.isReserveRetryAudioSuppressed
        });
        const jukebox = createJukeboxController({
          jukeboxDragSensitivity: JUKEBOX_DRAG_SENSITIVITY,
          jukeboxWheelStep: JUKEBOX_WHEEL_STEP,
          resizeSettlePasses: RESIZE_SETTLE_PASSES,
          youTubeHookMaxRetries: YOUTUBE_HOOK_MAX_RETRIES,
          youTubeHookRetryDelayMs: YOUTUBE_HOOK_RETRY_DELAY_MS,
          findChangeControlsItem,
          findSettingsContainer,
          focusActiveRenderCanvas: options.focusActiveRenderCanvas,
          getActiveRenderCanvas: options.getActiveRenderCanvas,
          getActiveRenderMode: options.getActiveRenderMode,
          isAudioEnabled: options.isAudioEnabled,
          isChatInput: options.isChatInput,
          resetBrowserScroll: options.resetBrowserScroll,
          scheduleUiWork: options.scheduleUiWork
        });
        const lobbyMusic = createLobbyMusicController({
          playLayerSelector: FULLSCREEN_PLAY_LAYER_SELECTOR,
          isAudioEnabled: options.isAudioEnabled
        });
        return {
          ...gameVolume,
          ...jukebox,
          ...lobbyMusic
        };
      }

      // src/hitbox/editor-map-adapter.ts
      var EDITOR_MAP_STATE_PATH = ["multiplayerSession", "TJ", "JD", "tP"];
      var EDITOR_FILE_MENU_SELECTOR = "#editorContainer .fileMenu";
      var EDITOR_MENU_ITEM_SELECTOR = ".item";
      function getEditorMapState() {
        const maps = readNativePath(window, EDITOR_MAP_STATE_PATH);
        if (!Array.isArray(maps)) {
          return null;
        }
        return readNativeProperty(maps[0], "state") || null;
      }
      function isNativeFunction(value) {
        return typeof value === "function";
      }
      function callMapExport(mapState) {
        try {
          const { called, result } = callNativeMethod(mapState, "rc");
          if (!called || typeof result !== "string") {
            return null;
          }
          const mapData = result.trim();
          return mapData ? mapData : null;
        } catch {
          return null;
        }
      }
      function getNativeEditorFileItem(label) {
        const fileMenu = document.querySelector(EDITOR_FILE_MENU_SELECTOR);
        if (!(fileMenu instanceof HTMLElement)) {
          return null;
        }
        return Array.from(fileMenu.querySelectorAll(EDITOR_MENU_ITEM_SELECTOR)).find((item) => item instanceof HTMLElement && item.textContent?.trim() === label) || null;
      }
      function replaceNativeMethod(target, methodName, replacement) {
        if (!isNativeReflectTarget(target)) {
          return null;
        }
        const original = readNativeProperty(target, methodName);
        if (!isNativeFunction(original) || !setNativeReflectProperty(target, methodName, replacement)) {
          return null;
        }
        return () => {
          setNativeReflectProperty(target, methodName, original);
        };
      }
      function getCapturedMapState(candidate) {
        if (!isNativeObject(candidate)) {
          return null;
        }
        const state = readNativeProperty(candidate, "state");
        return isNativeObject(state) ? state : null;
      }
      function exportCurrentEditorMapViaNativePlayClone() {
        const playItem = getNativeEditorFileItem("Play");
        const session = readNativeProperty(window, "multiplayerSession");
        const lobbyState = readNativePath(window, ["multiplayerSession", "JD"]);
        if (!(playItem instanceof HTMLElement) || !isNativeReflectTarget(session) || !isNativeReflectTarget(lobbyState)) {
          return null;
        }
        let capturedMapState = null;
        const captureMap = (candidate) => {
          capturedMapState = getCapturedMapState(candidate) || capturedMapState;
        };
        const restores = [
          replaceNativeMethod(lobbyState, "tU", (maps) => {
            if (Array.isArray(maps)) {
              captureMap(maps[0]);
            }
          }),
          replaceNativeMethod(lobbyState, "sU", (map) => {
            captureMap(map);
          }),
          replaceNativeMethod(session, "_J", () => void 0)
        ].filter((restore) => typeof restore === "function");
        if (!restores.length) {
          return null;
        }
        try {
          playItem.click();
          return callMapExport(capturedMapState);
        } catch {
          return null;
        } finally {
          for (const restore of restores.reverse()) {
            try {
              restore();
            } catch {
            }
          }
        }
      }
      function refreshEditorAfterMapImport() {
        try {
          const editorController = readNativePath(window, ["multiplayerSession", "TJ"]);
          callNativeMethod(editorController, "gW");
        } catch {
        }
        try {
          window.dispatchEvent(new Event("resize"));
        } catch {
        }
      }
      function exportEditorMapData() {
        return exportCurrentEditorMapViaNativePlayClone() || callMapExport(getEditorMapState());
      }
      function importEditorMapData(mapData) {
        const trimmedMapData = mapData.trim();
        if (!trimmedMapData) {
          return false;
        }
        try {
          const { called } = callNativeMethod(getEditorMapState(), "ac", [trimmedMapData]);
          if (!called) {
            return false;
          }
          refreshEditorAfterMapImport();
          return true;
        } catch {
          return false;
        }
      }

      // src/features/editor-map-file-transfer.ts
      var EDITOR_FILE_MENU_SELECTOR2 = ".fileMenu";
      var EDITOR_MENU_ITEM_SELECTOR2 = ".item";
      var EDITOR_TRANSFER_ITEM_SELECTOR = "[data-qolbox-editor-map-transfer]";
      var EDITOR_MAP_FILE_INPUT_ID = "qolboxEditorMapFileInput";
      var EDITOR_MAP_STATUS_ID = "qolboxEditorMapStatus";
      var EDITOR_MAP_FILE_EXTENSION = "hitboxmap";
      var STATUS_HIDE_DELAY_MS = 2400;
      function getMenuItems(fileMenu) {
        return Array.from(fileMenu.querySelectorAll(EDITOR_MENU_ITEM_SELECTOR2)).filter(
          (child) => child instanceof HTMLElement
        );
      }
      function findMenuItem(fileMenu, label) {
        return getMenuItems(fileMenu).find((item) => item.textContent?.trim() === label) || null;
      }
      function getDownloadTimestamp() {
        const now = /* @__PURE__ */ new Date();
        const pad = (value) => String(value).padStart(2, "0");
        return [
          now.getFullYear(),
          pad(now.getMonth() + 1),
          pad(now.getDate()),
          "-",
          pad(now.getHours()),
          pad(now.getMinutes()),
          pad(now.getSeconds())
        ].join("");
      }
      function createEditorMapMenuItem(label, action, handler) {
        const item = document.createElement("div");
        item.className = "item";
        item.textContent = label;
        item.setAttribute("data-qolbox-editor-map-transfer", action);
        item.addEventListener(
          "click",
          (event) => {
            event.preventDefault();
            event.stopPropagation();
            handler();
          },
          true
        );
        return item;
      }
      function isStringRecord(value) {
        return typeof value === "object" && value !== null;
      }
      function extractMapDataFromParsedJson(value) {
        if (typeof value === "string") {
          return value.trim() || null;
        }
        if (!isStringRecord(value)) {
          return null;
        }
        for (const key of ["leveldata", "levelData", "map", "mapData", "data"]) {
          const mapData = value[key];
          if (typeof mapData === "string" && mapData.trim()) {
            return mapData.trim();
          }
        }
        return null;
      }
      function extractMapDataFromFileText(fileText) {
        const trimmedText = fileText.trim();
        if (!trimmedText) {
          return null;
        }
        try {
          const jsonMapData = extractMapDataFromParsedJson(JSON.parse(trimmedText));
          if (jsonMapData) {
            return jsonMapData;
          }
        } catch {
        }
        return trimmedText;
      }
      function createEditorMapFileTransferController(options) {
        let statusHideTimer = 0;
        let documentHooksInstalled = false;
        function getStatusElement() {
          let status = document.getElementById(EDITOR_MAP_STATUS_ID);
          if (status instanceof HTMLElement) {
            return status;
          }
          const host = document.body || document.documentElement;
          if (!host) {
            return null;
          }
          status = document.createElement("div");
          status.id = EDITOR_MAP_STATUS_ID;
          status.className = "qolboxEditorMapStatus";
          host.appendChild(status);
          return status;
        }
        function showStatus(message, kind = "success") {
          const status = getStatusElement();
          if (!status) {
            return;
          }
          window.clearTimeout(statusHideTimer);
          status.textContent = message;
          status.classList.toggle("error", kind === "error");
          status.classList.add("visible");
          statusHideTimer = window.setTimeout(() => {
            status.classList.remove("visible");
          }, STATUS_HIDE_DELAY_MS);
        }
        function getFileInput() {
          const existingInput = document.getElementById(EDITOR_MAP_FILE_INPUT_ID);
          if (existingInput instanceof HTMLInputElement) {
            return existingInput;
          }
          const host = document.body || document.documentElement;
          if (!host) {
            return null;
          }
          const input = document.createElement("input");
          input.id = EDITOR_MAP_FILE_INPUT_ID;
          input.type = "file";
          input.accept = `.${EDITOR_MAP_FILE_EXTENSION},.txt,.json,application/json,text/plain`;
          input.style.display = "none";
          input.addEventListener("change", () => {
            const file = input.files?.[0] || null;
            input.value = "";
            if (file) {
              void importMapFile(file);
            }
          });
          host.appendChild(input);
          return input;
        }
        function exportCurrentEditorMap() {
          const mapData = exportEditorMapData();
          if (!mapData) {
            showStatus("No editor map is available to export.", "error");
            return;
          }
          try {
            const blob = new Blob([mapData], { type: "text/plain;charset=utf-8" });
            const objectUrl = URL.createObjectURL(blob);
            const anchor = document.createElement("a");
            anchor.href = objectUrl;
            anchor.download = `hitbox-map-${getDownloadTimestamp()}.${EDITOR_MAP_FILE_EXTENSION}`;
            anchor.style.display = "none";
            (document.body || document.documentElement).appendChild(anchor);
            anchor.click();
            anchor.remove();
            window.setTimeout(() => URL.revokeObjectURL(objectUrl), 0);
            showStatus("Map export started.");
          } catch {
            showStatus("Could not export this map.", "error");
          }
        }
        function requestMapImport() {
          const input = getFileInput();
          if (!input) {
            showStatus("Could not open the file picker.", "error");
            return;
          }
          input.click();
        }
        async function importMapFile(file) {
          try {
            const mapData = extractMapDataFromFileText(await file.text());
            if (!mapData || !importEditorMapData(mapData)) {
              showStatus("Could not import this map file.", "error");
              return;
            }
            showStatus("Map imported.");
          } catch {
            showStatus("Could not import this map file.", "error");
          }
        }
        function removeTransferItems(fileMenu = document.documentElement) {
          fileMenu.querySelectorAll(EDITOR_TRANSFER_ITEM_SELECTOR).forEach((item) => item.remove());
        }
        function syncOpenFileMenu(fileMenu) {
          if (!options.isEditorMapTransferEnabled()) {
            removeTransferItems(fileMenu);
            return false;
          }
          const loadItem = findMenuItem(fileMenu, "Load");
          const dropdownContainer = loadItem?.parentElement || null;
          if (!loadItem || !dropdownContainer) {
            return false;
          }
          if (dropdownContainer.querySelector(EDITOR_TRANSFER_ITEM_SELECTOR)) {
            return false;
          }
          const exportItem = createEditorMapMenuItem("Export", "export", exportCurrentEditorMap);
          const importItem = createEditorMapMenuItem("Import", "import", requestMapImport);
          dropdownContainer.insertBefore(exportItem, loadItem);
          dropdownContainer.insertBefore(importItem, loadItem);
          return true;
        }
        function getEventFileMenu(event) {
          return event.target instanceof Element ? event.target.closest(EDITOR_FILE_MENU_SELECTOR2) : null;
        }
        function installDocumentHooks() {
          if (documentHooksInstalled) {
            return false;
          }
          documentHooksInstalled = true;
          document.addEventListener(
            "click",
            (event) => {
              const clickedFileMenu = getEventFileMenu(event);
              const hadTransferItems = Boolean(clickedFileMenu?.querySelector(EDITOR_TRANSFER_ITEM_SELECTOR));
              window.setTimeout(() => {
                if (!clickedFileMenu) {
                  removeTransferItems();
                  return;
                }
                if (hadTransferItems) {
                  removeTransferItems(clickedFileMenu);
                  return;
                }
                syncOpenFileMenu(clickedFileMenu);
              }, 0);
            },
            true
          );
          document.addEventListener("keydown", (event) => {
            if (event.key === "Escape") {
              removeTransferItems();
            }
          }, true);
          return true;
        }
        function removeEditorMapFileTransfer() {
          window.clearTimeout(statusHideTimer);
          removeTransferItems();
          document.getElementById(EDITOR_MAP_FILE_INPUT_ID)?.remove();
          document.getElementById(EDITOR_MAP_STATUS_ID)?.remove();
        }
        function patchEditorMapFileTransfer() {
          if (!options.isEditorMapTransferEnabled()) {
            removeEditorMapFileTransfer();
            return false;
          }
          return installDocumentHooks();
        }
        return {
          patchEditorMapFileTransfer,
          removeEditorMapFileTransfer
        };
      }

      // src/features/feature-side-effects.ts
      function createFeatureSideEffectsController(options) {
        function disableFeatureSideEffects(featureKey) {
          switch (featureKey) {
            case FEATURE_RESERVE:
              options.stopReserveSpot({ hideNative: false });
              options.clearReservePasswordPromptPending();
              options.syncReserveJoinButtonLabel();
              break;
            case FEATURE_GAME_START_ALERT:
              options.disableGameStartAlerts();
              break;
            case FEATURE_AUDIO:
              options.removeJukeboxMenuItem();
              options.restoreJukeboxState();
              options.applyGameVolume();
              break;
            case FEATURE_FULLSCREEN:
              options.clearFullscreenLayoutStyles();
              if (options.featureGates.isChatEnabled()) {
                options.syncScoreRows();
                options.syncTypingIndicators();
              }
              break;
            case FEATURE_EDITOR_MAP_TRANSFER:
              options.removeEditorMapFileTransfer();
              break;
            case FEATURE_MOBILE_GRAB:
              options.removeMobileGrabButton();
              break;
            case FEATURE_CHAT:
              options.cleanupInGameChatScroll();
              options.clearTypingIndicators();
              options.restoreChatTabOrder();
              break;
            case FEATURE_LOBBY_COMMANDS:
              options.removeSwitchTeamsButton();
              break;
            default:
              break;
          }
        }
        function applyPersistentFeatures() {
          options.applyFeatureRootClasses();
          options.installPlayerPopupDismissal();
          options.patchSlashCommands();
          options.patchLobbyBlacklist();
          options.patchSwitchTeamsButton();
          options.patchMobileQolboxHamburgerEntry();
          if (options.featureGates.isReserveEnabled()) {
            options.patchReserveSpotFeature();
          } else {
            options.syncReserveJoinButtonLabel();
          }
          if (options.featureGates.isGameStartAlertEnabled()) {
            options.installGameStartIndicatorHooks();
            options.updateGameStartIndicator();
          } else {
            disableFeatureSideEffects(FEATURE_GAME_START_ALERT);
          }
          if (options.featureGates.isChatEnabled()) {
            options.patchChatTabOrder();
            options.patchInGameChatScroll();
            options.patchTypingIndicatorHooks();
            options.syncScoreRows();
            options.syncTypingIndicators();
          } else {
            disableFeatureSideEffects(FEATURE_CHAT);
          }
          if (options.featureGates.isMobileGrabEnabled()) {
            options.patchMobileGrabButton();
          } else {
            disableFeatureSideEffects(FEATURE_MOBILE_GRAB);
          }
          if (options.featureGates.isEditorMapTransferEnabled()) {
            options.patchEditorMapFileTransfer();
          } else {
            disableFeatureSideEffects(FEATURE_EDITOR_MAP_TRANSFER);
          }
          if (options.featureGates.isAudioEnabled()) {
            options.installTabFocusHooks();
            options.hookHowlPrototype();
            options.patchLobbyMusicController();
            options.patchGameVolumeMenu();
            options.installYouTubeReadyCallbackHook();
            options.hookYouTubePlayer();
            options.patchJukeboxMenu();
            options.patchJukeboxKnob();
            options.applyJukeboxState();
          } else {
            disableFeatureSideEffects(FEATURE_AUDIO);
          }
        }
        return {
          applyPersistentFeatures,
          disableFeatureSideEffects
        };
      }

      // src/hitbox/fullscreen-metric-overrides.ts
      var METRIC_NAMES = ["_P", "Qp", "lg", "ug"];
      function isFullscreenDimensions(value) {
        return isNativeObject(value) && typeof readNativeProperty(value, "scale") === "number" && typeof readNativeProperty(value, "width") === "number" && typeof readNativeProperty(value, "height") === "number";
      }
      function createMetricOriginals(game) {
        return {
          _P: { descriptor: Object.getOwnPropertyDescriptor(game, "_P") || null },
          Qp: { descriptor: Object.getOwnPropertyDescriptor(game, "Qp") || null },
          lg: { descriptor: Object.getOwnPropertyDescriptor(game, "lg") || null },
          ug: { descriptor: Object.getOwnPropertyDescriptor(game, "ug") || null }
        };
      }
      function makeMetricAccessor(getter) {
        return {
          configurable: true,
          enumerable: true,
          get: getter,
          set: () => {
          }
        };
      }
      function createFullscreenMetricOverrideController(options) {
        function getPinnedFullscreenDimensions(game) {
          const pinned = readNativeProperty(game, "__qolboxPinnedDimensions");
          return isFullscreenDimensions(pinned) ? pinned : options.getFallbackDimensions();
        }
        function installNativeMetricOverride(game) {
          if (!isNativeObject(game)) {
            return false;
          }
          if (readNativeProperty(game, "__qolboxMetricOverrideInstalled")) {
            return true;
          }
          setNativeReflectProperty(game, "__qolboxMetricOriginals", createMetricOriginals(game));
          try {
            Object.defineProperty(game, "_P", makeMetricAccessor(() => getPinnedFullscreenDimensions(game).scale));
            Object.defineProperty(
              game,
              "Qp",
              makeMetricAccessor(() => options.getNativeUiZoom(getPinnedFullscreenDimensions(game)))
            );
            Object.defineProperty(game, "lg", makeMetricAccessor(() => getPinnedFullscreenDimensions(game).width));
            Object.defineProperty(game, "ug", makeMetricAccessor(() => getPinnedFullscreenDimensions(game).height));
            setNativeReflectProperty(game, "__qolboxMetricOverrideInstalled", true);
            return true;
          } catch {
            return false;
          }
        }
        function restoreNativeMetricOverride(game) {
          if (!isNativeObject(game) || !readNativeProperty(game, "__qolboxMetricOverrideInstalled")) {
            return false;
          }
          const originals = readNativeProperty(game, "__qolboxMetricOriginals");
          for (const metricName of METRIC_NAMES) {
            const original = isNativeObject(originals) ? readNativeProperty(originals, metricName) : void 0;
            const descriptor = isNativeObject(original) ? readNativeProperty(original, "descriptor") : void 0;
            try {
              if (descriptor && typeof descriptor === "object") {
                Object.defineProperty(game, metricName, descriptor);
              } else {
                Reflect.deleteProperty(game, metricName);
              }
            } catch {
            }
          }
          Reflect.deleteProperty(game, "__qolboxPinnedDimensions");
          Reflect.deleteProperty(game, "__qolboxMetricOriginals");
          Reflect.deleteProperty(game, "__qolboxMetricOverrideInstalled");
          return true;
        }
        return {
          getPinnedFullscreenDimensions,
          installNativeMetricOverride,
          restoreNativeMetricOverride
        };
      }

      // src/hitbox/fullscreen-metrics-adapter.ts
      function createFullscreenMetricsAdapter(options) {
        const getWindowObject = () => options.windowObject || window;
        const getGame = () => readNativeProperty(getWindowObject(), "a8");
        const metricOverrides = createFullscreenMetricOverrideController({
          getFallbackDimensions: options.getFullscreenDimensions,
          getNativeUiZoom: options.getNativeUiZoom
        });
        function installNativeMetricOverride(game = getGame()) {
          return metricOverrides.installNativeMetricOverride(game);
        }
        function restoreNativeMetricOverride(game = getGame()) {
          return metricOverrides.restoreNativeMetricOverride(game);
        }
        function setNativeFullscreenSize(dimensions = options.getFullscreenDimensions()) {
          const game = getGame();
          if (!isNativeObject(game)) {
            return false;
          }
          setNativeReflectProperty(game, "__qolboxPinnedDimensions", dimensions);
          installNativeMetricOverride(game);
          setNativeReflectProperty(game, "_P", dimensions.scale);
          setNativeReflectProperty(game, "lg", dimensions.width);
          setNativeReflectProperty(game, "ug", dimensions.height);
          if ("Qp" in game) {
            setNativeReflectProperty(game, "Qp", options.getNativeUiZoom(dimensions));
          }
          const layoutMethod = readNativeProperty(game, "PP");
          if (typeof layoutMethod === "function") {
            try {
              Reflect.apply(layoutMethod, game, []);
            } catch {
            }
          }
          return true;
        }
        function restoreNativeFullscreenPatch(game = getGame()) {
          if (!isNativeObject(game)) {
            return false;
          }
          const resizeMethod = readNativeProperty(game, "ag");
          const originalResize = readNativeProperty(resizeMethod, "__qolboxOriginal");
          if (typeof resizeMethod === "function" && readNativeProperty(resizeMethod, "__qolboxWrapped") && typeof originalResize === "function") {
            setNativeReflectProperty(game, "ag", originalResize);
          }
          restoreNativeMetricOverride(game);
          const restoredResize = readNativeProperty(game, "ag");
          if (typeof restoredResize === "function") {
            try {
              Reflect.apply(restoredResize, game, []);
            } catch {
            }
          }
          return true;
        }
        function installNativeFullscreenPatch() {
          const game = getGame();
          if (!isNativeObject(game)) {
            return false;
          }
          installNativeMetricOverride(game);
          const originalResize = readNativeProperty(game, "ag");
          if (typeof originalResize !== "function" || readNativeProperty(originalResize, "__qolboxWrapped")) {
            return true;
          }
          const wrappedResize = function wrappedResize2(...args) {
            setNativeFullscreenSize(options.getFullscreenDimensions());
            if (readNativeProperty(game, "__qolboxRunningNativeResize")) {
              return Reflect.apply(originalResize, this, args);
            }
            setNativeReflectProperty(game, "__qolboxRunningNativeResize", true);
            try {
              const result = Reflect.apply(originalResize, this, args);
              setNativeFullscreenSize(options.getFullscreenDimensions());
              return result;
            } finally {
              setNativeReflectProperty(game, "__qolboxRunningNativeResize", false);
            }
          };
          setNativeReflectProperty(wrappedResize, "__qolboxWrapped", true);
          setNativeReflectProperty(wrappedResize, "__qolboxOriginal", originalResize);
          setNativeReflectProperty(game, "ag", wrappedResize);
          return true;
        }
        function runNativeResize(dimensions = options.getFullscreenDimensions()) {
          const game = getGame();
          const resizeMethod = readNativeProperty(game, "ag");
          if (!isNativeObject(game) || typeof resizeMethod !== "function") {
            return false;
          }
          setNativeFullscreenSize(dimensions);
          try {
            Reflect.apply(resizeMethod, game, [dimensions]);
            return true;
          } catch {
            return false;
          }
        }
        return {
          installNativeFullscreenPatch,
          restoreNativeFullscreenPatch,
          runNativeResize,
          setNativeFullscreenSize
        };
      }

      // src/hitbox/native-game-adapter.ts
      function getNativeGameObject() {
        const game = readNativeProperty(window, "a8");
        return isNativeObject(game) ? game : null;
      }
      function getNativeGameSlot() {
        return readNativeProperty(window, "a8");
      }
      function readFinitePositiveNumber(source, property) {
        const value = Number(readNativeProperty(source, property));
        return Number.isFinite(value) && value > 0 ? value : null;
      }
      function hasNativeGameObject() {
        return getNativeGameObject() !== null;
      }
      function installNativeGameReadyHook(onReady) {
        if (getNativeGameSlot()) {
          onReady();
          return;
        }
        try {
          let pendingGame = null;
          Object.defineProperty(window, "a8", {
            configurable: true,
            enumerable: true,
            get() {
              return pendingGame;
            },
            set(value) {
              pendingGame = value;
              Object.defineProperty(window, "a8", {
                configurable: true,
                enumerable: true,
                writable: true,
                value
              });
              onReady();
            }
          });
        } catch {
        }
      }
      function getNativeBaseGameSize(fallback) {
        const game = getNativeGameObject();
        return {
          width: readFinitePositiveNumber(game, "Xg") ?? fallback.width,
          height: readFinitePositiveNumber(game, "Zg") ?? fallback.height
        };
      }
      function getNativeFullscreenLayoutSize() {
        const game = getNativeGameObject();
        return {
          width: Number(readNativeProperty(game, "lg")) || 0,
          height: Number(readNativeProperty(game, "ug")) || 0
        };
      }

      // src/features/fullscreen-probe-alignment.ts
      function isFullscreenRenderProbeAligned(probe, dimensions) {
        if (probe.renderWidth <= 0 || probe.renderHeight <= 0) {
          return false;
        }
        return Math.abs(probe.renderWidth - dimensions.width) <= 2 && Math.abs(probe.renderHeight - dimensions.height) <= 2 && Math.abs(probe.renderLeft - dimensions.left) <= 2 && Math.abs(probe.renderTop - dimensions.top) <= 2 && probe.backingWidth > 0 && probe.backingHeight > 0;
      }
      function isFullscreenNativeProbeAligned(probe, dimensions) {
        if (probe.nativeWidth <= 0 || probe.nativeHeight <= 0) {
          return false;
        }
        return Math.abs(probe.nativeWidth - dimensions.width) <= 2 && Math.abs(probe.nativeHeight - dimensions.height) <= 2;
      }
      function buildFullscreenProbeSignature(dimensions, probe, hasNativeGame) {
        return [
          dimensions.mode,
          dimensions.viewportWidth,
          dimensions.viewportHeight,
          dimensions.width,
          dimensions.height,
          dimensions.left,
          dimensions.top,
          probe.appWidth,
          probe.appHeight,
          probe.relativeWidth,
          probe.relativeHeight,
          probe.renderWidth,
          probe.renderHeight,
          probe.renderLeft,
          probe.renderTop,
          probe.backingWidth,
          probe.backingHeight,
          probe.rendererCount,
          probe.nativeWidth,
          probe.nativeHeight,
          hasNativeGame
        ].join(":");
      }

      // src/features/fullscreen-geometry.ts
      var MENU_FRAME_PADDING_PX = 0;
      var GAMEPLAY_SAFE_TOP_PX = 0;
      var GAMEPLAY_SAFE_BOTTOM_PX = 0;
      var GAMEPLAY_SAFE_SIDE_PX = 0;
      function createFullscreenGeometry(options) {
        function getModeInsets(mode) {
          if (mode === "gameplay" || mode === "editor") {
            return {
              left: GAMEPLAY_SAFE_SIDE_PX,
              right: GAMEPLAY_SAFE_SIDE_PX,
              top: GAMEPLAY_SAFE_TOP_PX,
              bottom: GAMEPLAY_SAFE_BOTTOM_PX
            };
          }
          return {
            left: MENU_FRAME_PADDING_PX,
            right: MENU_FRAME_PADDING_PX,
            top: MENU_FRAME_PADDING_PX,
            bottom: MENU_FRAME_PADDING_PX
          };
        }
        function getFullscreenDimensions(viewport = options.getViewportSize(), mode = options.getActiveRenderMode()) {
          const base = options.getBaseGameSize();
          const insets = getModeInsets(mode);
          const availableWidth = Math.max(1, viewport.width - insets.left - insets.right);
          const availableHeight = Math.max(1, viewport.height - insets.top - insets.bottom);
          const scale = Math.max(0.01, Math.min(availableWidth / base.width, availableHeight / base.height));
          const width = Math.max(1, Math.round(base.width * scale));
          const height = Math.max(1, Math.round(base.height * scale));
          const left = insets.left + Math.max(0, Math.floor((availableWidth - width) / 2));
          const top = insets.top + Math.max(0, Math.floor((availableHeight - height) / 2));
          return {
            viewportWidth: viewport.width,
            viewportHeight: viewport.height,
            baseWidth: base.width,
            baseHeight: base.height,
            width,
            height,
            scale,
            left,
            top,
            shellLeft: 0,
            shellTop: 0,
            shellWidth: viewport.width,
            shellHeight: viewport.height,
            insets,
            mode
          };
        }
        function getNativeUiZoom(dimensions = getFullscreenDimensions()) {
          return Math.min(1, dimensions.width / 1400);
        }
        function getRelativeContainerBounds(dimensions = getFullscreenDimensions()) {
          return {
            left: dimensions.left,
            top: dimensions.top,
            width: dimensions.width,
            height: dimensions.height
          };
        }
        function isRenderProbeAligned(probe, dimensions) {
          return isFullscreenRenderProbeAligned(probe, dimensions);
        }
        function isNativeProbeAligned(probe, dimensions) {
          return isFullscreenNativeProbeAligned(probe, dimensions);
        }
        function buildFullscreenSignature(dimensions, probe) {
          return buildFullscreenProbeSignature(dimensions, probe, options.hasNativeGame());
        }
        return {
          buildFullscreenSignature,
          getFullscreenDimensions,
          getModeInsets,
          getNativeUiZoom,
          getRelativeContainerBounds,
          isNativeProbeAligned,
          isRenderProbeAligned
        };
      }

      // src/features/fullscreen-native-layout-fallback.ts
      function getStyleDeclaration(element) {
        if (isStyledElement(element)) {
          return element.style;
        }
        return null;
      }
      function hasStyleSize(element) {
        const style = getStyleDeclaration(element);
        return Boolean(style?.width && style.height);
      }
      function setStyleSizeIfEmpty(element, width, height) {
        const style = getStyleDeclaration(element);
        if (!style || style.width || style.height) {
          return;
        }
        style.width = width;
        style.height = height;
      }
      function createFullscreenNativeLayoutFallback(options) {
        let waitStartedAt = 0;
        function hasNativeLayoutSeed() {
          const appContainer = document.getElementById("appContainer");
          const relativeContainer = document.getElementById("relativeContainer");
          return Boolean(appContainer && relativeContainer && hasStyleSize(appContainer) && hasStyleSize(relativeContainer));
        }
        function shouldWaitForNativeLayoutSeed() {
          if (hasNativeLayoutSeed()) {
            waitStartedAt = 0;
            return false;
          }
          if (!document.getElementById("appContainer") || !document.getElementById("relativeContainer")) {
            return false;
          }
          if (!waitStartedAt) {
            waitStartedAt = Date.now();
          }
          return Date.now() - waitStartedAt < options.waitMs;
        }
        function restoreNativeLayoutSizeFallback() {
          const canvas = options.getActiveRenderCanvas();
          const canvasSize = getCanvasBackingSize(canvas);
          const backingWidth = canvasSize?.width ?? Number.NaN;
          const backingHeight = canvasSize?.height ?? Number.NaN;
          const pixelRatio = Math.max(1, Number(window.devicePixelRatio) || 1);
          const width = backingWidth / pixelRatio;
          const height = backingHeight / pixelRatio;
          if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0 || width >= window.innerWidth * 0.98 || height >= window.innerHeight * 0.98) {
            return;
          }
          const containerWidthPx = `${Math.floor(width)}px`;
          const containerHeightPx = `${Math.floor(height)}px`;
          const canvasWidthPx = `${Math.round(width * 10) / 10}px`;
          const canvasHeightPx = `${Math.round(height * 10) / 10}px`;
          for (const element of [
            document.getElementById("appContainer"),
            document.getElementById("relativeContainer")
          ]) {
            setStyleSizeIfEmpty(element, containerWidthPx, containerHeightPx);
          }
          setStyleSizeIfEmpty(canvas, canvasWidthPx, canvasHeightPx);
        }
        return {
          restoreNativeLayoutSizeFallback,
          shouldWaitForNativeLayoutSeed
        };
      }

      // src/hitbox/renderer-discovery.ts
      function isRendererCandidate(value) {
        return isNativeObject(value) && isNativeObject(readNativeProperty(value, "Bc")) && (isNativeObject(readNativeProperty(value, "Ag")) || typeof readNativeProperty(value, "cg") === "function");
      }
      function getRendererView(renderer) {
        const view = readNativePath(renderer, ["Ag", "view"]);
        return view instanceof Element ? view : null;
      }
      function getRendererHost(renderer) {
        const directHost = readNativeProperty(renderer, "Tg") || readNativeProperty(renderer, "dg");
        if (directHost instanceof Element) {
          return directHost;
        }
        return getRendererView(renderer)?.parentElement || null;
      }
      function getKnownFullscreenRenderers(windowObject = window) {
        const renderers = [];
        const seen = /* @__PURE__ */ new Set();
        function addRenderer(candidate) {
          if (!isRendererCandidate(candidate) || seen.has(candidate)) {
            return;
          }
          seen.add(candidate);
          renderers.push(candidate);
        }
        function collect(candidate) {
          if (!candidate) {
            return;
          }
          if (Array.isArray(candidate)) {
            candidate.forEach(collect);
            return;
          }
          addRenderer(candidate);
          const nested = readNativeProperty(candidate, "hb");
          addRenderer(nested);
          if (Array.isArray(nested)) {
            nested.forEach(addRenderer);
          }
        }
        const multiplayerSession = readNativeProperty(windowObject, "multiplayerSession");
        collect(multiplayerSession);
        collect(readNativeProperty(windowObject, "A4"));
        collect(readNativePath(windowObject, ["a8", "II"]));
        return renderers;
      }

      // src/hitbox/renderer-adapter.ts
      function resizeKnownRenderer(renderer, width, height) {
        const backing = readNativeProperty(renderer, "Bc");
        if (isNativeObject(backing)) {
          setNativeReflectProperty(backing, "wc", width);
          setNativeReflectProperty(backing, "mc", height);
        }
        const pixelRatio = Math.max(1, Number(window.devicePixelRatio) || 1);
        const pixiRenderer = readNativeProperty(renderer, "Ag");
        if (isNativeObject(pixiRenderer)) {
          if ("autoDensity" in pixiRenderer) {
            setNativeReflectProperty(pixiRenderer, "autoDensity", true);
          }
          if (typeof readNativeProperty(pixiRenderer, "resolution") === "number") {
            setNativeReflectProperty(pixiRenderer, "resolution", pixelRatio);
          }
          const options = readNativeProperty(pixiRenderer, "options");
          if (isNativeObject(options)) {
            setNativeReflectProperty(options, "autoDensity", true);
            setNativeReflectProperty(options, "resolution", pixelRatio);
          }
        }
        try {
          const nativeResize = readNativeProperty(renderer, "cg");
          if (typeof nativeResize === "function") {
            Reflect.apply(nativeResize, renderer, [width, height]);
          } else {
            const pixiResize = readNativeProperty(pixiRenderer, "resize");
            if (typeof pixiResize === "function") {
              Reflect.apply(pixiResize, pixiRenderer, [width, height]);
            }
          }
        } catch {
        }
      }
      function resizeKnownFullscreenRenderers(options) {
        const { dimensions, fitElementToFrame, setImportantStyle, windowObject = window } = options;
        const frameWidth = Math.max(1, Math.round(dimensions.width));
        const frameHeight = Math.max(1, Math.round(dimensions.height));
        for (const renderer of getKnownFullscreenRenderers(windowObject)) {
          resizeKnownRenderer(renderer, frameWidth, frameHeight);
          fitElementToFrame(getRendererHost(renderer), dimensions, dimensions.left, dimensions.top);
          const view = getRendererView(renderer);
          if (!view) {
            continue;
          }
          setImportantStyle(view, "position", "absolute");
          setImportantStyle(view, "left", "0");
          setImportantStyle(view, "top", "0");
          setImportantStyle(view, "right", "auto");
          setImportantStyle(view, "bottom", "auto");
          setImportantStyle(view, "width", `${frameWidth}px`);
          setImportantStyle(view, "height", `${frameHeight}px`);
          setImportantStyle(view, "max-width", "none");
          setImportantStyle(view, "max-height", "none");
          setImportantStyle(view, "transform", "none");
          fitElementToFrame(view.parentElement, dimensions, dimensions.left, dimensions.top);
        }
      }

      // src/features/fullscreen-render-state.ts
      function createFullscreenRenderState(options) {
        function getViewportSize() {
          return {
            width: Math.max(window.innerWidth, document.documentElement.clientWidth || 0),
            height: Math.max(window.innerHeight, document.documentElement.clientHeight || 0)
          };
        }
        function getBaseGameSize() {
          return getNativeBaseGameSize({
            width: options.fallbackBaseWidth,
            height: options.fallbackBaseHeight
          });
        }
        function isEditorLayer(element) {
          return element instanceof Element && element.id === "editorContainer";
        }
        function isCanvasElement(element) {
          return element instanceof Element && element.tagName === "CANVAS";
        }
        function isEditorCanvas(element) {
          return isCanvasElement(element) && element.parentElement instanceof Element && element.parentElement.id === "editorContainer";
        }
        function getActiveRenderMode() {
          if (options.hasVisibleLayer(options.menuLayerSelector)) {
            return "menu";
          }
          if (options.hasVisibleLayer(options.editorLayerSelector)) {
            return "editor";
          }
          if (options.hasVisibleLayer(options.gameplayLayerSelector)) {
            return "gameplay";
          }
          return "menu";
        }
        function getActiveRenderCanvas(mode = getActiveRenderMode()) {
          const selector = mode === "gameplay" ? options.gameplayLayerSelector : mode === "editor" ? options.editorLayerSelector : options.menuLayerSelector;
          for (const layer of document.querySelectorAll(selector)) {
            if (!options.isElementVisible(layer)) {
              continue;
            }
            const canvas = layer.querySelector("canvas");
            if (isCanvasElement(canvas)) {
              return canvas;
            }
          }
          const fallback = document.querySelector(options.renderCanvasSelector);
          return isCanvasElement(fallback) ? fallback : null;
        }
        function getLayoutProbe() {
          const appContainer = document.getElementById("appContainer");
          const relativeContainer = document.getElementById("relativeContainer");
          const renderLayer = getActiveRenderCanvas();
          const appRect = appContainer ? appContainer.getBoundingClientRect() : null;
          const relativeRect = relativeContainer ? relativeContainer.getBoundingClientRect() : null;
          const renderRect = renderLayer ? renderLayer.getBoundingClientRect() : null;
          const renderers = getKnownFullscreenRenderers();
          const backingSize = getCanvasBackingSize(renderLayer);
          const nativeLayoutSize = getNativeFullscreenLayoutSize();
          return {
            appWidth: appRect ? Math.round(appRect.width) : 0,
            appHeight: appRect ? Math.round(appRect.height) : 0,
            relativeWidth: relativeRect ? Math.round(relativeRect.width) : 0,
            relativeHeight: relativeRect ? Math.round(relativeRect.height) : 0,
            renderWidth: renderRect ? Math.round(renderRect.width) : 0,
            renderHeight: renderRect ? Math.round(renderRect.height) : 0,
            renderLeft: renderRect ? Math.round(renderRect.left) : 0,
            renderTop: renderRect ? Math.round(renderRect.top) : 0,
            backingWidth: backingSize ? Math.round(backingSize.width) : 0,
            backingHeight: backingSize ? Math.round(backingSize.height) : 0,
            rendererCount: renderers.length,
            nativeWidth: nativeLayoutSize.width,
            nativeHeight: nativeLayoutSize.height
          };
        }
        return {
          getActiveRenderCanvas,
          getActiveRenderMode,
          getBaseGameSize,
          getLayoutProbe,
          getViewportSize,
          isEditorCanvas,
          isEditorLayer
        };
      }

      // src/features/fullscreen-style-manager.ts
      function createFullscreenStyleManager() {
        let fullscreenStyleSnapshots = /* @__PURE__ */ new WeakMap();
        function rememberFullscreenStyle(element, property) {
          if (!isStyledElement(element)) {
            return;
          }
          let snapshot = fullscreenStyleSnapshots.get(element);
          if (!snapshot) {
            snapshot = /* @__PURE__ */ new Map();
            fullscreenStyleSnapshots.set(element, snapshot);
          }
          if (snapshot.has(property)) {
            return;
          }
          const value = element.style.getPropertyValue(property);
          const priority = element.style.getPropertyPriority(property);
          snapshot.set(property, {
            priority,
            value,
            hadValue: value !== "" || priority !== ""
          });
        }
        function setImportantStyle(element, property, value) {
          if (!isStyledElement(element)) {
            return;
          }
          rememberFullscreenStyle(element, property);
          element.style.setProperty(property, value, "important");
        }
        function restoreFullscreenStyles(element, properties) {
          if (!isStyledElement(element)) {
            return;
          }
          const snapshot = fullscreenStyleSnapshots.get(element);
          for (const property of properties) {
            const original = snapshot?.get(property);
            if (original?.hadValue) {
              element.style.setProperty(property, original.value, original.priority);
            } else {
              element.style.removeProperty(property);
            }
          }
        }
        function clearFullscreenStyleSnapshots() {
          fullscreenStyleSnapshots = /* @__PURE__ */ new WeakMap();
        }
        return {
          clearFullscreenStyleSnapshots,
          restoreFullscreenStyles,
          setImportantStyle
        };
      }

      // src/features/fullscreen-foundation-bundle.ts
      function createFullscreenFoundationBundle() {
        const renderState = createFullscreenRenderState({
          editorLayerSelector: FULLSCREEN_EDITOR_LAYER_SELECTOR,
          fallbackBaseHeight: FALLBACK_BASE_HEIGHT,
          fallbackBaseWidth: FALLBACK_BASE_WIDTH,
          gameplayLayerSelector: FULLSCREEN_GAMEPLAY_LAYER_SELECTOR,
          hasVisibleLayer,
          isElementVisible,
          menuLayerSelector: FULLSCREEN_MENU_LAYER_SELECTOR,
          renderCanvasSelector: FULLSCREEN_RENDER_CANVAS_SELECTOR
        });
        const nativeLayoutFallback = createFullscreenNativeLayoutFallback({
          getActiveRenderCanvas: renderState.getActiveRenderCanvas,
          waitMs: FULLSCREEN_NATIVE_LAYOUT_WAIT_MS
        });
        const styleManager = createFullscreenStyleManager();
        const geometry = createFullscreenGeometry({
          getActiveRenderMode: renderState.getActiveRenderMode,
          getBaseGameSize: renderState.getBaseGameSize,
          getViewportSize: renderState.getViewportSize,
          hasNativeGame: hasNativeGameObject
        });
        const metricsAdapter = createFullscreenMetricsAdapter({
          getFullscreenDimensions: geometry.getFullscreenDimensions,
          getNativeUiZoom: geometry.getNativeUiZoom
        });
        return {
          ...renderState,
          ...nativeLayoutFallback,
          ...styleManager,
          ...geometry,
          ...metricsAdapter
        };
      }

      // src/hitbox/session-adapter.ts
      function readNativeCollectionValue(collection, key) {
        if (!isNativeObject(collection) || key === null || key === void 0) {
          return null;
        }
        const propertyValue = readNativeProperty(collection, String(key));
        if (propertyValue) {
          return propertyValue;
        }
        const getter = readNativeProperty(collection, "get");
        if (typeof getter === "function") {
          const value = Reflect.apply(getter, collection, [key]);
          return value ?? null;
        }
        return propertyValue ?? null;
      }
      function getMultiplayerSession() {
        return isNativeObject(window.multiplayerSession) ? window.multiplayerSession : null;
      }
      function getNativeLobbyState(session) {
        return readNativeProperty(session, "JD");
      }
      function getSessionPlayer(session = getMultiplayerSession()) {
        const lobbyState = getNativeLobbyState(session);
        return readNativeCollectionValue(readNativeProperty(lobbyState, "Pi"), readNativeProperty(lobbyState, "vL"));
      }
      function getSessionPlayers(session = getMultiplayerSession()) {
        const players = readNativeProperty(getNativeLobbyState(session), "Pi");
        if (!isNativeObject(players)) {
          return [];
        }
        if (Array.isArray(players)) {
          return players.map((player, id) => ({ id, player })).filter((entry) => Boolean(entry.player));
        }
        const forEach = readNativeProperty(players, "forEach");
        if (typeof forEach === "function") {
          const entries = [];
          Reflect.apply(forEach, players, [
            (player, id) => {
              if (player) {
                entries.push({ id, player });
              }
            }
          ]);
          return entries;
        }
        return Object.keys(players).map((id) => ({ id, player: readNativeProperty(players, id) })).filter((entry) => Boolean(entry.player));
      }
      function getSessionPlayerById(session, playerId) {
        const players = readNativeProperty(getNativeLobbyState(session), "Pi");
        return readNativeCollectionValue(players, playerId);
      }
      function getLocalPlayerId(session = getMultiplayerSession()) {
        const playerId = readNativeProperty(getNativeLobbyState(session), "vL");
        return playerId === null || playerId === void 0 ? null : playerId;
      }
      function hasLobbyPlayerState(session = getMultiplayerSession()) {
        return isNativeObject(getNativeLobbyState(session));
      }
      function getPlayerTeamState(player) {
        return Number(player ? readNativeProperty(player, "N") : player);
      }
      function getPlayerName(player) {
        return readNativeProperty(player, "name");
      }
      function isSamePlayerId(left, right) {
        return left !== null && left !== void 0 && right !== null && right !== void 0 && String(left) === String(right);
      }
      function isNativeTeamMode(session = getMultiplayerSession()) {
        const nativeTeamMode = readNativeProperty(getNativeLobbyState(session), "Qn");
        return nativeTeamMode === true || nativeTeamMode === 1;
      }
      function isTeamsLocked(session = getMultiplayerSession()) {
        const locked = readNativeProperty(getNativeLobbyState(session), "VL");
        return locked === true || locked === 1;
      }
      function isHostSession(session = getMultiplayerSession()) {
        const lobbyState = getNativeLobbyState(session);
        const hostCheck = readNativeProperty(lobbyState, "XD");
        return typeof hostCheck === "function" && Boolean(Reflect.apply(hostCheck, lobbyState, []));
      }
      function isSessionLobbyActive(session = getMultiplayerSession()) {
        const lobbyUi = readNativeProperty(session, "TJ");
        const match = readNativeProperty(session, "KR");
        return Boolean(readNativeProperty(lobbyUi, "NS") && !readNativeProperty(match, "SL"));
      }
      function isSessionMatchActive(session = getMultiplayerSession()) {
        return Boolean(readNativeProperty(readNativeProperty(session, "KR"), "SL"));
      }

      // src/features/fullscreen-cleanup.ts
      var APP_CONTAINER_PROPERTIES = [
        "position",
        "left",
        "top",
        "right",
        "bottom",
        "margin",
        "width",
        "height",
        "max-width",
        "max-height",
        "border",
        "overflow"
      ];
      var BACKGROUND_IMAGE_PROPERTIES = ["position", "left", "top", "right", "bottom", "width", "height"];
      var FRAME_PROPERTIES = [
        "position",
        "left",
        "top",
        "right",
        "bottom",
        "margin",
        "width",
        "height",
        "max-width",
        "max-height",
        "overflow",
        "transform",
        "transform-origin",
        "zoom"
      ];
      var RELATIVE_CONTAINER_PROPERTIES = [
        "position",
        "left",
        "top",
        "right",
        "bottom",
        "margin",
        "width",
        "height",
        "overflow"
      ];
      function deleteDatasetValue(element, key) {
        if (hasDataset(element)) {
          delete element.dataset[key];
        }
      }
      function shouldDispatchNativeResizeAfterCleanup() {
        const appContainer = document.getElementById("appContainer");
        const relativeContainer = document.getElementById("relativeContainer");
        return appContainer?.style.position === "fixed" || relativeContainer?.style.position === "fixed" || document.documentElement.style.overflow === "hidden";
      }
      function dispatchNativeResizeAfterCleanup() {
        try {
          window.dispatchEvent(new Event("resize"));
        } catch {
        }
      }
      function createFullscreenCleanup(options) {
        function clearFullscreenLayoutStyles() {
          const needsNativeResize = shouldDispatchNativeResizeAfterCleanup();
          options.clearFullscreenSignature();
          options.restoreFullscreenStyles(document.documentElement, ["overflow"]);
          options.restoreFullscreenStyles(document.body, ["overflow", "margin", "background-color"]);
          options.restoreFullscreenStyles(document.getElementById("appContainer"), APP_CONTAINER_PROPERTIES);
          options.restoreFullscreenStyles(document.getElementById("relativeContainer"), RELATIVE_CONTAINER_PROPERTIES);
          options.restoreFullscreenStyles(document.getElementById("backgroundImage"), BACKGROUND_IMAGE_PROPERTIES);
          for (const element of document.querySelectorAll(options.renderLayerSelector)) {
            options.restoreFullscreenStyles(element, FRAME_PROPERTIES);
            deleteDatasetValue(element, "qolboxEditorNativeWidth");
            deleteDatasetValue(element, "qolboxEditorNativeHeight");
            deleteDatasetValue(element, "qolboxEditorScale");
          }
          for (const canvas of document.querySelectorAll(options.renderCanvasSelector)) {
            options.restoreFullscreenStyles(canvas, FRAME_PROPERTIES);
          }
          for (const overlay of document.querySelectorAll(".inGameCSS")) {
            options.restoreFullscreenStyles(overlay, ["zoom", "transform-origin"]);
          }
          for (const scorePanel of document.querySelectorAll(".scores")) {
            options.resetScorePanelLayout(scorePanel);
            options.restoreFullscreenStyles(scorePanel, ["display"]);
          }
          for (const scoreRow of document.querySelectorAll(".scores .entryContainer")) {
            options.restoreFullscreenStyles(scoreRow, ["background-color"]);
          }
          for (const spectateControls of document.querySelectorAll(".spectateControls")) {
            options.resetSpectateControlsLayout(spectateControls);
          }
          for (const editorStatusWindow of document.querySelectorAll(".physicsCountWindow")) {
            options.restoreFullscreenStyles(editorStatusWindow, [
              "position",
              "left",
              "top",
              "right",
              "bottom",
              "margin",
              "transform",
              "z-index"
            ]);
          }
          options.restoreNativeFullscreenPatch();
          options.restoreNativeLayoutSizeFallback();
          options.clearFullscreenStyleSnapshots();
          if (needsNativeResize) {
            dispatchNativeResizeAfterCleanup();
          }
        }
        return {
          clearFullscreenLayoutStyles
        };
      }

      // src/features/fullscreen-container-layout.ts
      function applyFullscreenContainerLayout(options, dimensions, activeDimensions, relativeBounds) {
        options.setImportantStyle(document.documentElement, "overflow", "hidden");
        options.setImportantStyle(document.body, "overflow", "hidden");
        options.setImportantStyle(document.body, "margin", "0");
        options.setImportantStyle(document.body, "background-color", "#0a0a0a");
        const appContainer = document.getElementById("appContainer");
        if (appContainer) {
          options.setImportantStyle(appContainer, "position", "fixed");
          options.setImportantStyle(appContainer, "left", `${activeDimensions.shellLeft}px`);
          options.setImportantStyle(appContainer, "top", `${activeDimensions.shellTop}px`);
          options.setImportantStyle(appContainer, "right", "auto");
          options.setImportantStyle(appContainer, "bottom", "auto");
          options.setImportantStyle(appContainer, "margin", "0");
          options.setImportantStyle(appContainer, "width", `${activeDimensions.shellWidth}px`);
          options.setImportantStyle(appContainer, "height", `${activeDimensions.shellHeight}px`);
          options.setImportantStyle(appContainer, "max-width", "none");
          options.setImportantStyle(appContainer, "max-height", "none");
          options.setImportantStyle(appContainer, "border", "0");
          options.setImportantStyle(appContainer, "overflow", "hidden");
        }
        const relativeContainer = document.getElementById("relativeContainer");
        if (relativeContainer) {
          options.setImportantStyle(relativeContainer, "position", "fixed");
          options.setImportantStyle(relativeContainer, "left", `${relativeBounds.left}px`);
          options.setImportantStyle(relativeContainer, "top", `${relativeBounds.top}px`);
          options.setImportantStyle(relativeContainer, "right", "auto");
          options.setImportantStyle(relativeContainer, "bottom", "auto");
          options.setImportantStyle(relativeContainer, "margin", "0");
          options.setImportantStyle(relativeContainer, "width", `${relativeBounds.width}px`);
          options.setImportantStyle(relativeContainer, "height", `${relativeBounds.height}px`);
          options.setImportantStyle(relativeContainer, "overflow", "visible");
        }
        const backgroundImage = document.getElementById("backgroundImage");
        if (backgroundImage) {
          options.setImportantStyle(backgroundImage, "position", "fixed");
          options.setImportantStyle(backgroundImage, "left", "0");
          options.setImportantStyle(backgroundImage, "top", "0");
          options.setImportantStyle(backgroundImage, "right", "auto");
          options.setImportantStyle(backgroundImage, "bottom", "auto");
          options.setImportantStyle(backgroundImage, "width", `${dimensions.viewportWidth}px`);
          options.setImportantStyle(backgroundImage, "height", `${dimensions.viewportHeight}px`);
        }
      }

      // src/features/fullscreen-editor-frame-layout.ts
      var EDITOR_STATUS_BLOCKER_MARGIN_PX = 8;
      var EDITOR_STATUS_VIEWPORT_MARGIN_PX = 5;
      var EDITOR_STATUS_RADIO_TRACKING_FRAMES = 60;
      function createFullscreenEditorFrameLayout(options) {
        let latestEditorFrame = null;
        let observedJukebox = null;
        let jukeboxStatusObserver = null;
        let statusLayoutFrame = 0;
        let statusLayoutFramesRemaining = 0;
        function getEditorNativeSize(editorLayer, dimensions) {
          const canvas = editorLayer instanceof Element ? editorLayer.querySelector("canvas") : null;
          const canvasSize = getCanvasBackingSize(canvas);
          const canvasWidth = Number(canvasSize?.width);
          const canvasHeight = Number(canvasSize?.height);
          return {
            width: Number.isFinite(canvasWidth) && canvasWidth > 0 ? canvasWidth : dimensions.baseWidth,
            height: Number.isFinite(canvasHeight) && canvasHeight > 0 ? canvasHeight : dimensions.baseHeight
          };
        }
        function getScaledEditorFrame(editorLayer, dimensions) {
          const nativeSize = getEditorNativeSize(editorLayer, dimensions);
          const scale = Math.max(
            0.01,
            Math.min(dimensions.width / nativeSize.width, dimensions.height / nativeSize.height)
          );
          const visualWidth = Math.max(1, Math.round(nativeSize.width * scale));
          const visualHeight = Math.max(1, Math.round(nativeSize.height * scale));
          return {
            left: dimensions.left + Math.max(0, Math.floor((dimensions.width - visualWidth) / 2)),
            top: dimensions.top + Math.max(0, Math.floor((dimensions.height - visualHeight) / 2)),
            width: nativeSize.width,
            height: nativeSize.height,
            scale,
            visualWidth,
            visualHeight
          };
        }
        function getVisibleRect(element) {
          const rect = element.getBoundingClientRect();
          const style = window.getComputedStyle(element);
          if (style.display === "none" || style.visibility === "hidden" || rect.width <= 0 || rect.height <= 0) {
            return null;
          }
          return rect;
        }
        function intersectsHorizontally(rect, left, right) {
          return rect.right > left && rect.left < right;
        }
        function getJukeboxTop(left, right) {
          const jukebox = document.querySelector(".jukebox");
          if (!(jukebox instanceof Element)) {
            return null;
          }
          const candidates = [jukebox, ...Array.from(jukebox.querySelectorAll("*"))];
          let top = null;
          for (const candidate of candidates) {
            const rect = getVisibleRect(candidate);
            if (!rect || !intersectsHorizontally(rect, left, right)) {
              continue;
            }
            if (rect.top > window.innerHeight + EDITOR_STATUS_BLOCKER_MARGIN_PX || rect.bottom < 0) {
              continue;
            }
            top = top === null ? rect.top : Math.min(top, rect.top);
          }
          return top;
        }
        function getSpectatorControlsTop(left, right) {
          let top = null;
          for (const controls of document.querySelectorAll(".spectateControls")) {
            const rect = getVisibleRect(controls);
            if (!rect || !intersectsHorizontally(rect, left, right)) {
              continue;
            }
            top = top === null ? rect.top : Math.min(top, rect.top);
          }
          return top;
        }
        function getEditorStatusMaxTop(left, right, height) {
          const viewportMaxTop = window.innerHeight - Math.ceil(height) - EDITOR_STATUS_VIEWPORT_MARGIN_PX;
          const blockerTops = [getJukeboxTop(left, right), getSpectatorControlsTop(left, right)].filter(
            (top) => top !== null
          );
          if (!blockerTops.length) {
            return viewportMaxTop;
          }
          const blockerMaxTop = Math.min(...blockerTops) - Math.ceil(height) - EDITOR_STATUS_BLOCKER_MARGIN_PX;
          return Math.min(viewportMaxTop, blockerMaxTop);
        }
        function hasActiveFullscreenEditorFrame() {
          const editorLayer = document.querySelector("#editorContainer");
          return hasDataset(editorLayer) && Boolean(editorLayer.dataset.qolboxEditorScale);
        }
        function runScheduledEditorStatusLayout() {
          statusLayoutFrame = 0;
          if (!latestEditorFrame || !hasActiveFullscreenEditorFrame()) {
            statusLayoutFramesRemaining = 0;
            return;
          }
          layoutEditorStatusWindow(latestEditorFrame);
          statusLayoutFramesRemaining -= 1;
          if (statusLayoutFramesRemaining > 0) {
            statusLayoutFrame = window.requestAnimationFrame(runScheduledEditorStatusLayout);
          }
        }
        function scheduleEditorStatusLayout(frames = 1) {
          statusLayoutFramesRemaining = Math.max(statusLayoutFramesRemaining, frames);
          if (!statusLayoutFrame) {
            statusLayoutFrame = window.requestAnimationFrame(runScheduledEditorStatusLayout);
          }
        }
        function observeJukeboxForEditorStatusLayout() {
          const jukebox = document.querySelector(".jukebox");
          if (jukebox === observedJukebox) {
            return;
          }
          jukeboxStatusObserver?.disconnect();
          observedJukebox = jukebox instanceof Element ? jukebox : null;
          if (!observedJukebox) {
            jukeboxStatusObserver = null;
            return;
          }
          jukeboxStatusObserver = new MutationObserver(() => {
            scheduleEditorStatusLayout(EDITOR_STATUS_RADIO_TRACKING_FRAMES);
          });
          jukeboxStatusObserver.observe(observedJukebox, {
            attributes: true,
            attributeFilter: ["class", "style"],
            subtree: true
          });
        }
        function fitEditorCanvasToNative(canvas, frame) {
          if (!canvas || !frame) {
            return;
          }
          options.setImportantStyle(canvas, "position", "absolute");
          options.setImportantStyle(canvas, "left", "0");
          options.setImportantStyle(canvas, "top", "0");
          options.setImportantStyle(canvas, "right", "auto");
          options.setImportantStyle(canvas, "bottom", "auto");
          options.setImportantStyle(canvas, "width", `${frame.width}px`);
          options.setImportantStyle(canvas, "height", `${frame.height}px`);
          options.setImportantStyle(canvas, "max-width", "none");
          options.setImportantStyle(canvas, "max-height", "none");
          options.setImportantStyle(canvas, "transform", "none");
        }
        function layoutEditorStatusWindow(frame) {
          const statusWindow = document.querySelector(".physicsCountWindow");
          if (!(statusWindow instanceof Element)) {
            return;
          }
          const rect = statusWindow.getBoundingClientRect();
          const width = rect.width || 186;
          const height = rect.height || 22;
          const left = Math.max(0, Math.round(frame.left + (frame.visualWidth - width) / 2));
          const preferredTop = frame.top + frame.visualHeight + 12;
          const maxTop = getEditorStatusMaxTop(left, left + width, height);
          const top = Math.max(0, Math.min(Math.round(preferredTop), Math.round(maxTop)));
          options.setImportantStyle(statusWindow, "position", "fixed");
          options.setImportantStyle(statusWindow, "left", `${left}px`);
          options.setImportantStyle(statusWindow, "top", `${top}px`);
          options.setImportantStyle(statusWindow, "right", "auto");
          options.setImportantStyle(statusWindow, "bottom", "auto");
          options.setImportantStyle(statusWindow, "margin", "0");
          options.setImportantStyle(statusWindow, "transform", "none");
          options.setImportantStyle(statusWindow, "z-index", "2147483001");
        }
        function fitEditorLayerToFrame(layer, dimensions) {
          if (!(layer instanceof Element)) {
            return null;
          }
          const frame = getScaledEditorFrame(layer, dimensions);
          latestEditorFrame = frame;
          options.setImportantStyle(layer, "position", "absolute");
          options.setImportantStyle(layer, "left", `${frame.left}px`);
          options.setImportantStyle(layer, "top", `${frame.top}px`);
          options.setImportantStyle(layer, "right", "auto");
          options.setImportantStyle(layer, "bottom", "auto");
          options.setImportantStyle(layer, "width", `${frame.width}px`);
          options.setImportantStyle(layer, "height", `${frame.height}px`);
          options.setImportantStyle(layer, "max-width", "none");
          options.setImportantStyle(layer, "max-height", "none");
          options.setImportantStyle(layer, "overflow", "visible");
          options.setImportantStyle(layer, "transform", `scale(${frame.scale})`);
          options.setImportantStyle(layer, "transform-origin", "top left");
          options.setImportantStyle(layer, "zoom", "1");
          if (hasDataset(layer)) {
            layer.dataset.qolboxEditorNativeWidth = String(frame.width);
            layer.dataset.qolboxEditorNativeHeight = String(frame.height);
            layer.dataset.qolboxEditorScale = String(frame.scale);
          }
          const canvas = layer.querySelector("canvas");
          if (canvas) {
            fitEditorCanvasToNative(canvas, frame);
          }
          observeJukeboxForEditorStatusLayout();
          layoutEditorStatusWindow(frame);
          return frame;
        }
        return {
          fitEditorCanvasToNative,
          fitEditorLayerToFrame,
          getScaledEditorFrame
        };
      }

      // src/features/fullscreen-render-frame-layout.ts
      function createFullscreenRenderFrameLayout(options) {
        function fitElementToFrame(element, dimensions, left = 0, top = 0) {
          if (!(element instanceof Element)) {
            return;
          }
          if (options.isEditorLayer(element)) {
            options.fitEditorLayerToFrame(element, dimensions);
            return;
          }
          options.setImportantStyle(element, "position", "absolute");
          options.setImportantStyle(element, "left", `${left}px`);
          options.setImportantStyle(element, "top", `${top}px`);
          options.setImportantStyle(element, "right", "auto");
          options.setImportantStyle(element, "bottom", "auto");
          options.setImportantStyle(element, "margin", "0");
          options.setImportantStyle(element, "width", `${dimensions.width}px`);
          options.setImportantStyle(element, "height", `${dimensions.height}px`);
          options.setImportantStyle(element, "max-width", "none");
          options.setImportantStyle(element, "max-height", "none");
          options.setImportantStyle(element, "overflow", "hidden");
          options.setImportantStyle(element, "transform", "none");
        }
        function fitRenderLayersToFrame(dimensions) {
          for (const layer of document.querySelectorAll(options.renderLayerSelector)) {
            if (options.isEditorLayer(layer)) {
              options.fitEditorLayerToFrame(layer, dimensions);
              continue;
            }
            options.setImportantStyle(layer, "position", "absolute");
            options.setImportantStyle(layer, "left", `${dimensions.left}px`);
            options.setImportantStyle(layer, "top", `${dimensions.top}px`);
            options.setImportantStyle(layer, "right", "auto");
            options.setImportantStyle(layer, "bottom", "auto");
            options.setImportantStyle(layer, "width", `${dimensions.width}px`);
            options.setImportantStyle(layer, "height", `${dimensions.height}px`);
            options.setImportantStyle(layer, "max-width", "none");
            options.setImportantStyle(layer, "max-height", "none");
            options.setImportantStyle(layer, "overflow", "hidden");
            options.setImportantStyle(layer, "transform", "none");
            options.setImportantStyle(layer, "zoom", "1");
          }
        }
        function fitRenderCanvasesToFrame(dimensions) {
          for (const canvas of document.querySelectorAll(options.renderCanvasSelector)) {
            if (options.isEditorCanvas(canvas)) {
              const editorLayer = canvas.parentElement;
              const frame = options.getScaledEditorFrame(editorLayer, dimensions);
              options.fitEditorCanvasToNative(canvas, frame);
              continue;
            }
            options.setImportantStyle(canvas, "position", "absolute");
            options.setImportantStyle(canvas, "left", "0");
            options.setImportantStyle(canvas, "top", "0");
            options.setImportantStyle(canvas, "right", "auto");
            options.setImportantStyle(canvas, "bottom", "auto");
            options.setImportantStyle(canvas, "width", `${dimensions.width}px`);
            options.setImportantStyle(canvas, "height", `${dimensions.height}px`);
            options.setImportantStyle(canvas, "max-width", "none");
            options.setImportantStyle(canvas, "max-height", "none");
            options.setImportantStyle(canvas, "transform", "none");
          }
        }
        return {
          fitElementToFrame,
          fitRenderCanvasesToFrame,
          fitRenderLayersToFrame
        };
      }

      // src/features/fullscreen-frame-layout.ts
      function createFullscreenFrameLayout(options) {
        const editorFrameLayout = createFullscreenEditorFrameLayout({
          setImportantStyle: options.setImportantStyle
        });
        function getScaledEditorFrame(editorLayer, dimensions = options.getFullscreenDimensions(void 0, "editor")) {
          return editorFrameLayout.getScaledEditorFrame(editorLayer, dimensions);
        }
        function fitEditorCanvasToNative(canvas, frame) {
          editorFrameLayout.fitEditorCanvasToNative(canvas, frame);
        }
        function fitEditorLayerToFrame(layer, dimensions = options.getFullscreenDimensions(void 0, "editor")) {
          return editorFrameLayout.fitEditorLayerToFrame(layer, dimensions);
        }
        const renderFrameLayout = createFullscreenRenderFrameLayout({
          fitEditorCanvasToNative,
          fitEditorLayerToFrame,
          getScaledEditorFrame,
          isEditorCanvas: options.isEditorCanvas,
          isEditorLayer: options.isEditorLayer,
          renderCanvasSelector: options.renderCanvasSelector,
          renderLayerSelector: options.renderLayerSelector,
          setImportantStyle: options.setImportantStyle
        });
        function fitElementToFrame(element, dimensions = options.getFullscreenDimensions(), left = 0, top = 0) {
          renderFrameLayout.fitElementToFrame(element, dimensions, left, top);
        }
        function enforceFullscreenLayout(dimensions = options.getFullscreenDimensions()) {
          options.ensureGlobalStyle();
          const menuDimensions = dimensions.mode === "menu" ? dimensions : options.getFullscreenDimensions(void 0, "menu");
          const playDimensions = dimensions.mode === "gameplay" || dimensions.mode === "editor" ? dimensions : options.getFullscreenDimensions(void 0, "gameplay");
          const activeDimensions = dimensions.mode === "gameplay" || dimensions.mode === "editor" ? playDimensions : menuDimensions;
          const relativeBounds = options.getRelativeContainerBounds(activeDimensions);
          applyFullscreenContainerLayout(options, dimensions, activeDimensions, relativeBounds);
          renderFrameLayout.fitRenderLayersToFrame(activeDimensions);
          renderFrameLayout.fitRenderCanvasesToFrame(activeDimensions);
          const uiZoom = String(options.getNativeUiZoom(activeDimensions));
          for (const overlay of document.querySelectorAll(".inGameCSS")) {
            options.setImportantStyle(overlay, "zoom", uiZoom);
            options.setImportantStyle(overlay, "transform-origin", "top left");
          }
          options.layoutRelativeHud(relativeBounds, activeDimensions);
          return true;
        }
        return {
          enforceFullscreenLayout,
          fitEditorCanvasToNative,
          fitEditorLayerToFrame,
          fitElementToFrame,
          getScaledEditorFrame
        };
      }

      // src/features/fullscreen-game-ready-hook.ts
      function createFullscreenGameReadyHook(options) {
        let installed = false;
        function scheduleFullscreenSettle() {
          options.scheduleUiWork({ force: true, passes: options.settlePasses });
        }
        function installGameReadyHook() {
          if (installed) {
            return;
          }
          installed = true;
          installNativeGameReadyHook(scheduleFullscreenSettle);
        }
        return {
          installGameReadyHook
        };
      }

      // src/features/fullscreen-inline-style.ts
      function getFullscreenInlineStyle(element) {
        if (isStyledElement(element)) {
          return element.style;
        }
        return null;
      }
      function removeFullscreenInlineProperties(element, properties) {
        const style = getFullscreenInlineStyle(element);
        if (!style) {
          return;
        }
        for (const property of properties) {
          style.removeProperty(property);
        }
      }
      function getFullscreenInlineStyleProperty(element, property) {
        return getFullscreenInlineStyle(element)?.getPropertyValue(property) ?? "";
      }

      // src/features/fullscreen-spectate-controls-layout.ts
      var CLOSED_CONTROLS_BOTTOM_OFFSET_PX = 12;
      var HELD_OPEN_CONTROLS_MARGIN_PX = 3;
      var RADIO_NEAR_OPEN_BOTTOM_PX = -2;
      var RADIO_CLOSING_REENTER_DELTA_PX = 0.5;
      var isPointerTrackingInstalled = false;
      var lastPointerPosition = null;
      function rememberPointerPosition(event) {
        lastPointerPosition = {
          x: event.clientX,
          y: event.clientY
        };
      }
      function clearPointerPosition() {
        lastPointerPosition = null;
      }
      function ensurePointerTracking() {
        if (isPointerTrackingInstalled) {
          return;
        }
        isPointerTrackingInstalled = true;
        window.addEventListener("pointermove", rememberPointerPosition, true);
        window.addEventListener("pointerdown", rememberPointerPosition, true);
        window.addEventListener("blur", clearPointerPosition, true);
        document.addEventListener(
          "pointerleave",
          (event) => {
            if (!event.relatedTarget) {
              clearPointerPosition();
            }
          },
          true
        );
      }
      function isPointerOverElement(element) {
        if (!lastPointerPosition) {
          return false;
        }
        const { x, y } = lastPointerPosition;
        if (x < 0 || y < 0 || x > window.innerWidth || y > window.innerHeight) {
          return false;
        }
        const hitElement = document.elementFromPoint(x, y);
        if (hitElement && (hitElement === element || element.contains(hitElement))) {
          return true;
        }
        const rect = element.getBoundingClientRect();
        return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
      }
      function isPointerInControlsTravelCorridor(controls, jukebox) {
        if (!lastPointerPosition) {
          return false;
        }
        const { x, y } = lastPointerPosition;
        const controlsRect = controls.getBoundingClientRect();
        const jukeboxRect = jukebox.getBoundingClientRect();
        if (controlsRect.width <= 0 || controlsRect.height <= 0 || jukeboxRect.width <= 0 || jukeboxRect.height <= 0) {
          return false;
        }
        const corridorLeft = controlsRect.left;
        const corridorRight = controlsRect.right;
        const corridorTop = Math.min(controlsRect.bottom, jukeboxRect.top);
        const corridorBottom = Math.max(controlsRect.bottom, jukeboxRect.top);
        return x >= corridorLeft && x <= corridorRight && y >= corridorTop && y <= corridorBottom;
      }
      function parseBottomPx(value) {
        const parsed = Number.parseFloat(typeof value === "string" ? value : "");
        return Number.isFinite(parsed) ? parsed : null;
      }
      function hasFocusedDescendant(element) {
        return document.activeElement instanceof Element && element.contains(document.activeElement);
      }
      function getElementBottomPx(element) {
        return parseBottomPx(getFullscreenInlineStyleProperty(element, "bottom")) ?? parseBottomPx(window.getComputedStyle(element).bottom);
      }
      function wantsToHoldSpectateControls(controls, jukebox) {
        return controls.matches(":hover") || isPointerOverElement(controls) || isPointerInControlsTravelCorridor(controls, jukebox) || hasFocusedDescendant(controls);
      }
      function isSpectateControlsAlreadyExpanded(controls, openOffset) {
        const bottom = getElementBottomPx(controls);
        return bottom !== null && bottom >= openOffset - HELD_OPEN_CONTROLS_MARGIN_PX;
      }
      function isNativeCallable6(value) {
        return typeof value === "function";
      }
      function callNativeJukeboxHandler(jukebox, handlerName, fallbackBottom) {
        const handler = readObjectProperty(jukebox, handlerName);
        if (isNativeCallable6(handler)) {
          Reflect.apply(handler, jukebox, []);
          return;
        }
        const style = readObjectProperty(jukebox, "style");
        if (style instanceof CSSStyleDeclaration) {
          style.bottom = fallbackBottom;
        }
      }
      function getSpectatorRadioLayoutState(options, spectatorRadioHoldActive, keepControlsOpenUntilRadioCloses) {
        const baseOffset = CLOSED_CONTROLS_BOTTOM_OFFSET_PX;
        const jukebox = document.querySelector(".jukebox");
        if (!(jukebox instanceof Element) || !options.isElementVisible(jukebox)) {
          return {
            controlsBottomOffset: baseOffset,
            jukebox: null,
            jukeboxDirectlyActive: false,
            jukeboxBottom: null,
            openOffset: baseOffset,
            shouldHoldRadioOpen: false
          };
        }
        const style = window.getComputedStyle(jukebox);
        const inlineBottom = getFullscreenInlineStyleProperty(jukebox, "bottom");
        const bottom = Number.parseFloat(typeof inlineBottom === "string" ? inlineBottom : style.bottom);
        const rect = jukebox.getBoundingClientRect();
        const height = rect.height || Number.parseFloat(style.height) || 35;
        const openOffset = Math.ceil(height + 36);
        const openProgress = Number.isFinite(bottom) ? Math.max(0, Math.min(1, (bottom + 50) / 50)) : 0;
        const jukeboxDirectlyActive = jukebox.matches(":hover") || hasFocusedDescendant(jukebox);
        const jukeboxIsActive = jukeboxDirectlyActive || openProgress > 0.05 || spectatorRadioHoldActive;
        const spectateControls = Array.from(document.querySelectorAll(options.spectateControlsSelector));
        const controlsAlreadyExpanded = spectateControls.some(
          (controls) => isSpectateControlsAlreadyExpanded(controls, openOffset)
        );
        const controlsWantHold = spectateControls.some((controls) => wantsToHoldSpectateControls(controls, jukebox));
        const holdControlsOpen = controlsAlreadyExpanded && controlsWantHold && jukeboxIsActive;
        const holdReleasedNearOpen = keepControlsOpenUntilRadioCloses && !controlsWantHold && !jukeboxDirectlyActive && Number.isFinite(bottom) && bottom > RADIO_NEAR_OPEN_BOTTOM_PX;
        const shouldHoldRadioOpen = holdControlsOpen && !jukeboxDirectlyActive;
        if (!Number.isFinite(bottom)) {
          const open = holdControlsOpen || holdReleasedNearOpen || jukeboxDirectlyActive;
          return {
            controlsBottomOffset: open ? openOffset : baseOffset,
            jukebox,
            jukeboxDirectlyActive,
            jukeboxBottom: null,
            openOffset,
            shouldHoldRadioOpen
          };
        }
        const liveOffset = Math.round(baseOffset + (openOffset - baseOffset) * openProgress);
        return {
          controlsBottomOffset: holdControlsOpen || holdReleasedNearOpen ? openOffset : liveOffset,
          jukebox,
          jukeboxDirectlyActive,
          jukeboxBottom: bottom,
          openOffset,
          shouldHoldRadioOpen
        };
      }
      function createFullscreenSpectateControlsLayout(options) {
        ensurePointerTracking();
        let pointerSyncFrame = 0;
        let keepControlsOpenUntilRadioCloses = false;
        let lastHeldJukeboxBottom = null;
        let spectatorRadioHoldActive = false;
        function schedulePointerSync() {
          if (pointerSyncFrame) {
            return;
          }
          pointerSyncFrame = window.requestAnimationFrame(() => {
            pointerSyncFrame = 0;
            syncSpectateControlsBottomWithJukebox();
          });
        }
        window.addEventListener("pointermove", schedulePointerSync, true);
        window.addEventListener("pointerdown", schedulePointerSync, true);
        function setSpectateControlsBottom(spectateControls, bottom) {
          if (getFullscreenInlineStyleProperty(spectateControls, "bottom") === bottom) {
            return false;
          }
          options.setImportantStyle(spectateControls, "bottom", bottom);
          return true;
        }
        function releaseSpectatorRadioHold() {
          keepControlsOpenUntilRadioCloses = false;
          lastHeldJukeboxBottom = null;
          if (!spectatorRadioHoldActive) {
            return;
          }
          spectatorRadioHoldActive = false;
          const jukebox = document.querySelector(".jukebox");
          if (jukebox instanceof Element && !jukebox.matches(":hover") && !hasFocusedDescendant(jukebox)) {
            callNativeJukeboxHandler(jukebox, "onmouseleave", "-50px");
          }
        }
        function syncJukeboxSpectatorHold(state) {
          if (!state.jukebox) {
            releaseSpectatorRadioHold();
            return false;
          }
          if (state.shouldHoldRadioOpen) {
            keepControlsOpenUntilRadioCloses = false;
            const radioAppearsToBeClosing = lastHeldJukeboxBottom !== null && state.jukeboxBottom !== null && state.jukeboxBottom < lastHeldJukeboxBottom - RADIO_CLOSING_REENTER_DELTA_PX;
            if (!spectatorRadioHoldActive || radioAppearsToBeClosing) {
              callNativeJukeboxHandler(state.jukebox, "onmouseenter", "0px");
            }
            spectatorRadioHoldActive = true;
            if (state.jukeboxBottom !== null) {
              lastHeldJukeboxBottom = state.jukeboxBottom;
            }
            return false;
          }
          if (keepControlsOpenUntilRadioCloses && (state.jukeboxDirectlyActive || state.jukeboxBottom === null || state.jukeboxBottom <= RADIO_NEAR_OPEN_BOTTOM_PX)) {
            keepControlsOpenUntilRadioCloses = false;
          }
          if (spectatorRadioHoldActive) {
            spectatorRadioHoldActive = false;
            lastHeldJukeboxBottom = null;
            if (!state.jukeboxDirectlyActive && state.jukeboxBottom !== null && state.jukeboxBottom > RADIO_NEAR_OPEN_BOTTOM_PX) {
              keepControlsOpenUntilRadioCloses = true;
            }
            if (!state.jukeboxDirectlyActive) {
              callNativeJukeboxHandler(state.jukebox, "onmouseleave", "-50px");
            }
            return keepControlsOpenUntilRadioCloses;
          }
          return false;
        }
        function resetSpectateControlsLayout(spectateControls) {
          releaseSpectatorRadioHold();
          removeFullscreenInlineProperties(spectateControls, [
            "position",
            "left",
            "right",
            "top",
            "bottom",
            "transform",
            "transition",
            "margin",
            "z-index"
          ]);
        }
        function syncSpectateControlsBottomWithJukebox() {
          if (!options.isFullscreenEnabled() || !options.isSessionMatchActive()) {
            releaseSpectatorRadioHold();
            return false;
          }
          const state = getSpectatorRadioLayoutState(
            options,
            spectatorRadioHoldActive,
            keepControlsOpenUntilRadioCloses
          );
          const forceOpenControls = syncJukeboxSpectatorHold(state);
          const bottom = `${forceOpenControls ? state.openOffset : state.controlsBottomOffset}px`;
          let changed = false;
          for (const controls of document.querySelectorAll(options.spectateControlsSelector)) {
            changed = setSpectateControlsBottom(controls, bottom) || changed;
          }
          return changed;
        }
        function layoutSpectateControls(useGameplayHudLayout) {
          if (!useGameplayHudLayout) {
            releaseSpectatorRadioHold();
          } else {
            const state = getSpectatorRadioLayoutState(
              options,
              spectatorRadioHoldActive,
              keepControlsOpenUntilRadioCloses
            );
            const forceOpenControls = syncJukeboxSpectatorHold(state);
            const controlsBottomOffset = forceOpenControls ? state.openOffset : state.controlsBottomOffset;
            for (const spectateControls of document.querySelectorAll(options.spectateControlsSelector)) {
              options.setImportantStyle(spectateControls, "position", "absolute");
              options.setImportantStyle(spectateControls, "left", "50%");
              options.setImportantStyle(spectateControls, "right", "auto");
              options.setImportantStyle(spectateControls, "top", "auto");
              setSpectateControlsBottom(spectateControls, `${controlsBottomOffset}px`);
              options.setImportantStyle(spectateControls, "transform", "translateX(-50%)");
              options.setImportantStyle(spectateControls, "margin", "0");
              options.setImportantStyle(spectateControls, "z-index", "2147483002");
            }
            return;
          }
          for (const spectateControls of document.querySelectorAll(options.spectateControlsSelector)) {
            resetSpectateControlsLayout(spectateControls);
          }
        }
        return {
          layoutSpectateControls,
          resetSpectateControlsLayout,
          syncSpectateControlsBottomWithJukebox
        };
      }

      // src/features/fullscreen-hud-layout.ts
      function isLoadingScreenVisible() {
        const loading = document.getElementById("ccLoading");
        if (!loading || !loading.isConnected) {
          return false;
        }
        const style = window.getComputedStyle(loading);
        return style.display !== "none" && style.visibility !== "hidden" && style.opacity !== "0";
      }
      function createFullscreenHudLayout(options) {
        const spectateControlsLayout = createFullscreenSpectateControlsLayout({
          isElementVisible: options.isElementVisible,
          isFullscreenEnabled: options.isFullscreenEnabled,
          isSessionMatchActive: options.isSessionMatchActive,
          setImportantStyle: options.setImportantStyle,
          spectateControlsSelector: options.spectateControlsSelector
        });
        function resetScorePanelLayout(scorePanel) {
          removeFullscreenInlineProperties(scorePanel, [
            "position",
            "left",
            "top",
            "right",
            "bottom",
            "transform",
            "text-align",
            "margin-top",
            "z-index"
          ]);
        }
        function resetSpectateControlsLayout(spectateControls) {
          spectateControlsLayout.resetSpectateControlsLayout(spectateControls);
        }
        function syncSpectateControlsBottomWithJukebox() {
          return spectateControlsLayout.syncSpectateControlsBottomWithJukebox();
        }
        function layoutRelativeHud(_relativeBounds, dimensions) {
          const isLoading = isLoadingScreenVisible();
          const useGameplayHudLayout = !isLoading && (dimensions.mode === "gameplay" || options.isSessionMatchActive() && options.hasVisibleLayer(options.spectateControlsSelector));
          for (const scorePanel of document.querySelectorAll(options.scoresSelector)) {
            if (!useGameplayHudLayout) {
              resetScorePanelLayout(scorePanel);
              options.setImportantStyle(scorePanel, "display", "none");
              continue;
            }
            options.syncScoreRowsFromPlayers(scorePanel);
            options.makeScoreRowsOpaque(scorePanel);
            options.syncTypingIndicators(scorePanel);
            options.setImportantStyle(scorePanel, "display", "block");
            options.setImportantStyle(scorePanel, "position", "absolute");
            options.setImportantStyle(scorePanel, "left", "50%");
            options.setImportantStyle(scorePanel, "top", "12px");
            options.setImportantStyle(scorePanel, "right", "auto");
            options.setImportantStyle(scorePanel, "bottom", "auto");
            options.setImportantStyle(scorePanel, "transform", "translateX(-50%)");
            options.setImportantStyle(scorePanel, "text-align", "center");
            options.setImportantStyle(scorePanel, "margin-top", "0");
            options.setImportantStyle(scorePanel, "z-index", "10");
          }
          spectateControlsLayout.layoutSpectateControls(useGameplayHudLayout);
        }
        return {
          layoutRelativeHud,
          resetScorePanelLayout,
          resetSpectateControlsLayout,
          syncSpectateControlsBottomWithJukebox
        };
      }

      // src/features/fullscreen-resize-target-observer.ts
      function createFullscreenResizeTargetObserver(options) {
        let resizeObserver = null;
        let observedResizeTargets = /* @__PURE__ */ new WeakSet();
        function setFullscreenResizeObserver(observer) {
          resizeObserver = observer;
          if (!observer) {
            observedResizeTargets = /* @__PURE__ */ new WeakSet();
          }
        }
        function observeResizeTarget(element) {
          if (!resizeObserver || !(element instanceof Element) || observedResizeTargets.has(element)) {
            return;
          }
          observedResizeTargets.add(element);
          resizeObserver.observe(element);
        }
        function refreshObservedResizeTargets() {
          observeResizeTarget(document.documentElement);
          observeResizeTarget(document.body);
          observeResizeTarget(document.getElementById("appContainer"));
          observeResizeTarget(document.getElementById("relativeContainer"));
          observeResizeTarget(document.getElementById("backgroundImage"));
          for (const element of document.querySelectorAll(options.renderLayerSelector)) {
            observeResizeTarget(element);
          }
          for (const element of document.querySelectorAll(options.renderCanvasSelector)) {
            observeResizeTarget(element);
          }
        }
        return {
          refreshObservedResizeTargets,
          setFullscreenResizeObserver
        };
      }

      // src/features/fullscreen-layout-feature-bundle.ts
      function createFullscreenLayoutFeatureBundle(options) {
        const hudLayout = createFullscreenHudLayout({
          scoresSelector: ".scores",
          spectateControlsSelector: ".spectateControls",
          hasVisibleLayer,
          isElementVisible,
          isFullscreenEnabled: options.isFullscreenEnabled,
          isSessionMatchActive,
          makeScoreRowsOpaque: options.makeScoreRowsOpaque,
          setImportantStyle: options.setImportantStyle,
          syncScoreRowsFromPlayers: options.syncScoreRowsFromPlayers,
          syncTypingIndicators: options.syncTypingIndicators
        });
        const frameLayout = createFullscreenFrameLayout({
          renderCanvasSelector: FULLSCREEN_RENDER_CANVAS_SELECTOR,
          renderLayerSelector: FULLSCREEN_RENDER_LAYER_SELECTOR,
          ensureGlobalStyle: options.ensureGlobalStyle,
          getFullscreenDimensions: options.getFullscreenDimensions,
          getNativeUiZoom: options.getNativeUiZoom,
          getRelativeContainerBounds: options.getRelativeContainerBounds,
          isEditorCanvas: options.isEditorCanvas,
          isEditorLayer: options.isEditorLayer,
          layoutRelativeHud: hudLayout.layoutRelativeHud,
          setImportantStyle: options.setImportantStyle
        });
        const cleanup = createFullscreenCleanup({
          renderCanvasSelector: FULLSCREEN_RENDER_CANVAS_SELECTOR,
          renderLayerSelector: FULLSCREEN_RENDER_LAYER_SELECTOR,
          clearFullscreenSignature: options.clearFullscreenSignature,
          clearFullscreenStyleSnapshots: options.clearFullscreenStyleSnapshots,
          resetScorePanelLayout: hudLayout.resetScorePanelLayout,
          resetSpectateControlsLayout: hudLayout.resetSpectateControlsLayout,
          restoreFullscreenStyles: options.restoreFullscreenStyles,
          restoreNativeFullscreenPatch: options.restoreNativeFullscreenPatch,
          restoreNativeLayoutSizeFallback: options.restoreNativeLayoutSizeFallback
        });
        const gameReadyHook = createFullscreenGameReadyHook({
          scheduleUiWork: options.scheduleUiWork,
          settlePasses: FULLSCREEN_SETTLE_PASSES
        });
        const resizeTargets = createFullscreenResizeTargetObserver({
          renderCanvasSelector: FULLSCREEN_RENDER_CANVAS_SELECTOR,
          renderLayerSelector: FULLSCREEN_RENDER_LAYER_SELECTOR
        });
        function resizeFullscreenRenderers(dimensions) {
          resizeKnownFullscreenRenderers({
            dimensions,
            fitElementToFrame: frameLayout.fitElementToFrame,
            setImportantStyle: options.setImportantStyle
          });
        }
        return {
          ...hudLayout,
          ...frameLayout,
          ...cleanup,
          ...gameReadyHook,
          ...resizeTargets,
          resizeKnownFullscreenRenderers: resizeFullscreenRenderers
        };
      }

      // src/features/fullscreen-hook-installer.ts
      function createFullscreenHookInstaller(options) {
        let installed = false;
        function scheduleResizeSettle() {
          options.scheduleUiWork({ force: true, passes: options.resizeSettlePasses });
        }
        function installFullscreenHooks() {
          if (installed) {
            return;
          }
          if (!document.documentElement) {
            options.scheduleUiWork({ force: true, features: true, passes: options.fullscreenSettlePasses });
            return;
          }
          installed = true;
          options.installGameReadyHook();
          options.installQolboxMenuHooks();
          options.installChatEscapeHooks();
          options.installChatCommandAliasHooks();
          options.installGameplayBackgroundFocusHooks();
          if (options.isAudioEnabled()) {
            options.installTabFocusHooks();
          }
          if (options.isGameStartAlertEnabled()) {
            options.installGameStartIndicatorHooks();
          }
          if (options.isReserveEnabled()) {
            options.installReserveSocketCaptureHook();
          }
          window.addEventListener("resize", scheduleResizeSettle, true);
          window.addEventListener("orientationchange", scheduleResizeSettle, true);
          window.addEventListener(
            "load",
            () => options.scheduleUiWork({ force: true, features: true, passes: options.fullscreenSettlePasses }),
            true
          );
          window.addEventListener(
            "pageshow",
            () => options.scheduleUiWork({ force: true, features: true, passes: options.resizeSettlePasses }),
            true
          );
          document.addEventListener(
            "visibilitychange",
            () => {
              if (!document.hidden) {
                options.scheduleUiWork({ force: true, features: true, passes: options.resizeSettlePasses });
              }
            },
            true
          );
          document.addEventListener("fullscreenchange", scheduleResizeSettle, true);
          options.installFullscreenMutationObserver(document.documentElement);
          const ResizeObserverConstructor = window.ResizeObserver;
          if (typeof ResizeObserverConstructor === "function") {
            options.setFullscreenResizeObserver(
              new ResizeObserverConstructor(() => {
                options.scheduleUiWork({ force: true, passes: 1 });
              })
            );
            options.refreshObservedResizeTargets();
          }
        }
        return {
          installFullscreenHooks
        };
      }

      // src/features/fullscreen-mutation-observer.ts
      var FULLSCREEN_OBSERVER_OPTIONS = {
        subtree: true,
        childList: true,
        characterData: true,
        attributes: true,
        attributeFilter: ["class", "style", "id"]
      };
      function createFullscreenMutationObserver(options) {
        let fullscreenMutationObserver = null;
        function handleMutationRecords(records) {
          let needsLayout = false;
          let needsFeatures = false;
          let needsSpectateSync = false;
          for (const record of records) {
            if (!needsLayout && mutationTouchesSelector(record, options.layoutTargetSelector)) {
              needsLayout = true;
            }
            if (!needsFeatures && mutationTouchesSelector(record, options.featurePatchTargetSelector)) {
              needsFeatures = true;
            }
            if (!needsSpectateSync && mutationTouchesSelector(record, ".jukebox")) {
              needsSpectateSync = true;
            }
            if (needsLayout && needsFeatures && needsSpectateSync) {
              break;
            }
          }
          if (needsSpectateSync) {
            options.syncSpectateControlsBottomWithJukebox();
          }
          if (needsLayout || needsFeatures) {
            options.updateGameStartIndicator();
            options.scheduleUiWork({
              force: needsLayout,
              features: needsFeatures,
              passes: needsLayout ? options.settlePasses : 1
            });
          }
        }
        function installFullscreenMutationObserver(target = document.documentElement) {
          if (!target) {
            return;
          }
          fullscreenMutationObserver = new MutationObserver(handleMutationRecords);
          fullscreenMutationObserver.observe(target, FULLSCREEN_OBSERVER_OPTIONS);
        }
        return {
          installFullscreenMutationObserver
        };
      }

      // src/features/fullscreen-refresh-controller.ts
      function createFullscreenRefreshController(options) {
        let lastFullscreenSignature = "";
        function clearFullscreenSignature() {
          lastFullscreenSignature = "";
        }
        function refreshFullscreen(force = false) {
          if (!options.isFullscreenEnabled()) {
            options.clearFullscreenLayoutStyles();
            options.syncNonFullscreenHud();
            return false;
          }
          if (options.shouldWaitForNativeLayoutSeed()) {
            window.setTimeout(() => options.scheduleUiWork({ force: true, passes: 1 }), 100);
            return false;
          }
          const dimensions = options.getFullscreenDimensions();
          const probe = options.getLayoutProbe();
          const signature = options.buildFullscreenSignature(dimensions, probe);
          const transitionOverlap = options.isMenuGameplayOverlap();
          options.patchLobbyMusicController();
          options.stopLobbyMusicIfNeeded();
          options.updateGameStartIndicator();
          options.enforceFullscreenLayout(dimensions);
          options.installNativeFullscreenPatch();
          options.setNativeFullscreenSize(dimensions);
          if (!force && signature === lastFullscreenSignature && options.isRenderProbeAligned(probe, dimensions) && options.isNativeProbeAligned(probe, dimensions)) {
            return false;
          }
          lastFullscreenSignature = signature;
          const resizedNatively = options.runNativeResize(dimensions);
          const postNativeProbe = options.getLayoutProbe();
          if (!transitionOverlap && (!resizedNatively || !options.isRenderProbeAligned(postNativeProbe, dimensions))) {
            options.resizeKnownFullscreenRenderers(dimensions);
          }
          options.enforceFullscreenLayout(dimensions);
          lastFullscreenSignature = options.buildFullscreenSignature(dimensions, options.getLayoutProbe());
          return true;
        }
        return {
          clearFullscreenSignature,
          refreshFullscreen
        };
      }

      // src/features/fullscreen-work-scheduler.ts
      function createFullscreenWorkScheduler(options) {
        let scheduledWorkRaf = 0;
        let scheduledWorkForce = false;
        let scheduledWorkFeatures = false;
        let scheduledWorkPasses = 0;
        function scheduleUiWork({ force = false, features = false, passes = 1 } = {}) {
          scheduledWorkForce = scheduledWorkForce || force;
          scheduledWorkFeatures = scheduledWorkFeatures || features;
          scheduledWorkPasses = Math.max(scheduledWorkPasses, Math.max(1, passes));
          if (scheduledWorkRaf) {
            return;
          }
          const runScheduledWork = () => {
            scheduledWorkRaf = 0;
            const shouldForce = scheduledWorkForce;
            const shouldPatchFeatures = scheduledWorkFeatures;
            const remainingPasses = scheduledWorkPasses;
            scheduledWorkForce = false;
            scheduledWorkFeatures = false;
            scheduledWorkPasses = 0;
            options.ensureGlobalStyle();
            options.applyFeatureRootClasses();
            options.installFullscreenHooks();
            if (shouldPatchFeatures) {
              options.applyPersistentFeatures();
            }
            options.refreshFullscreen(shouldForce);
            options.refreshObservedResizeTargets();
            if (remainingPasses > 1) {
              scheduleUiWork({ force: true, passes: remainingPasses - 1 });
            }
          };
          scheduledWorkRaf = document.hidden ? window.setTimeout(runScheduledWork, 0) : window.requestAnimationFrame(runScheduledWork);
        }
        return {
          scheduleUiWork
        };
      }

      // src/features/fullscreen-orchestration-bundle.ts
      function createFullscreenOrchestrationBundle(options) {
        const { clearFullscreenSignature, refreshFullscreen } = createFullscreenRefreshController({
          buildFullscreenSignature: options.buildFullscreenSignature,
          clearFullscreenLayoutStyles: options.clearFullscreenLayoutStyles,
          enforceFullscreenLayout: options.enforceFullscreenLayout,
          getFullscreenDimensions: options.getFullscreenDimensions,
          getLayoutProbe: options.getLayoutProbe,
          installNativeFullscreenPatch: options.installNativeFullscreenPatch,
          isFullscreenEnabled: options.isFullscreenEnabled,
          isMenuGameplayOverlap: options.isMenuGameplayOverlap,
          isNativeProbeAligned: options.isNativeProbeAligned,
          isRenderProbeAligned: options.isRenderProbeAligned,
          patchLobbyMusicController: options.patchLobbyMusicController,
          resizeKnownFullscreenRenderers: options.resizeKnownFullscreenRenderers,
          runNativeResize: options.runNativeResize,
          scheduleUiWork: (request) => scheduleUiWork(request),
          setNativeFullscreenSize: options.setNativeFullscreenSize,
          shouldWaitForNativeLayoutSeed: options.shouldWaitForNativeLayoutSeed,
          stopLobbyMusicIfNeeded: options.stopLobbyMusicIfNeeded,
          syncNonFullscreenHud: options.syncNonFullscreenHud,
          updateGameStartIndicator: options.updateGameStartIndicator
        });
        const { scheduleUiWork } = createFullscreenWorkScheduler({
          applyFeatureRootClasses: options.applyFeatureRootClasses,
          applyPersistentFeatures: options.applyPersistentFeatures,
          ensureGlobalStyle: options.ensureGlobalStyle,
          installFullscreenHooks: () => installFullscreenHooks(),
          refreshFullscreen,
          refreshObservedResizeTargets: options.refreshObservedResizeTargets
        });
        const { installFullscreenMutationObserver } = createFullscreenMutationObserver({
          featurePatchTargetSelector: FEATURE_PATCH_TARGET_SELECTOR,
          layoutTargetSelector: FULLSCREEN_LAYOUT_TARGET_SELECTOR,
          scheduleUiWork,
          settlePasses: FULLSCREEN_SETTLE_PASSES,
          syncSpectateControlsBottomWithJukebox: options.syncSpectateControlsBottomWithJukebox,
          updateGameStartIndicator: options.updateGameStartIndicator
        });
        const { installFullscreenHooks } = createFullscreenHookInstaller({
          fullscreenSettlePasses: FULLSCREEN_SETTLE_PASSES,
          installChatCommandAliasHooks: options.installChatCommandAliasHooks,
          installChatEscapeHooks: options.installChatEscapeHooks,
          installFullscreenMutationObserver,
          installGameReadyHook: options.installGameReadyHook,
          installGameStartIndicatorHooks: options.installGameStartIndicatorHooks,
          installGameplayBackgroundFocusHooks: options.installGameplayBackgroundFocusHooks,
          installQolboxMenuHooks: options.installQolboxMenuHooks,
          installReserveSocketCaptureHook: options.installReserveSocketCaptureHook,
          installTabFocusHooks: options.installTabFocusHooks,
          isAudioEnabled: options.isAudioEnabled,
          isGameStartAlertEnabled: options.isGameStartAlertEnabled,
          isReserveEnabled: options.isReserveEnabled,
          refreshObservedResizeTargets: options.refreshObservedResizeTargets,
          resizeSettlePasses: RESIZE_SETTLE_PASSES,
          scheduleUiWork,
          setFullscreenResizeObserver: options.setFullscreenResizeObserver
        });
        return {
          clearFullscreenSignature,
          installFullscreenHooks,
          installFullscreenMutationObserver,
          refreshFullscreen,
          scheduleUiWork: (request) => scheduleUiWork(request)
        };
      }

      // src/hitbox/game-start-hooks.ts
      var REMOTE_START_METHODS = ["KJ", "ZJ"];
      var LOCAL_START_METHOD = "_J";
      function isNativeCallable7(value) {
        return typeof value === "function";
      }
      function isWrappedGameStartMethod(method) {
        return isNativeCallable7(method) && readNativeReflectProperty(method, "__qolboxWrapped") === true;
      }
      function markWrappedGameStartMethod(method, originalMethod) {
        setNativeReflectProperty(method, "__qolboxWrapped", true);
        setNativeReflectProperty(method, "__qolboxOriginal", originalMethod);
      }
      function areGameStartSessionHooksInstalled(session) {
        return [...REMOTE_START_METHODS, LOCAL_START_METHOD].every((methodName) => {
          const method = readNativeProperty(session, methodName);
          return !isNativeCallable7(method) || isWrappedGameStartMethod(method);
        });
      }
      function installGameStartSessionHooks(session, callbacks) {
        if (!isNativeObject(session)) {
          return false;
        }
        let foundRemoteStartHandler = false;
        for (const methodName of REMOTE_START_METHODS) {
          const originalMethod = readNativeProperty(session, methodName);
          if (!isNativeCallable7(originalMethod)) {
            continue;
          }
          foundRemoteStartHandler = true;
          if (isWrappedGameStartMethod(originalMethod)) {
            continue;
          }
          const wrappedMethod = function wrappedGameStartSessionMethod(...args) {
            const snapshot = callbacks.captureStartState();
            let result;
            try {
              result = Reflect.apply(originalMethod, this, args);
            } finally {
              callbacks.handleStartAfterNativeEvent(snapshot, this);
            }
            return result;
          };
          markWrappedGameStartMethod(wrappedMethod, originalMethod);
          setNativeReflectProperty(session, methodName, wrappedMethod);
        }
        const originalStartRequest = readNativeProperty(session, LOCAL_START_METHOD);
        if (isNativeCallable7(originalStartRequest) && !isWrappedGameStartMethod(originalStartRequest)) {
          const wrappedStartRequest = function wrappedLocalGameStartRequest(...args) {
            callbacks.noteLocalStartRequest(this);
            return Reflect.apply(originalStartRequest, this, args);
          };
          markWrappedGameStartMethod(wrappedStartRequest, originalStartRequest);
          setNativeReflectProperty(session, LOCAL_START_METHOD, wrappedStartRequest);
        }
        const startRequest = readNativeProperty(session, LOCAL_START_METHOD);
        return foundRemoteStartHandler || isNativeCallable7(startRequest) && isWrappedGameStartMethod(startRequest);
      }

      // src/features/game-start-display.ts
      function createGameStartDisplayController() {
        let faviconLink = null;
        let originalFavicon = null;
        function getIndicatorDocument() {
          try {
            const targetWindow = window.top;
            if (targetWindow && targetWindow.document) {
              return targetWindow.document;
            }
          } catch {
          }
          return document;
        }
        function getFaviconLink() {
          return getIndicatorDocument().querySelector('link[rel~="icon"]');
        }
        function shouldPostToTop() {
          const targetWindow = window.top;
          if (!targetWindow || targetWindow === window) {
            return false;
          }
          try {
            return !targetWindow.document;
          } catch {
            return true;
          }
        }
        function postToTop(payload) {
          if (!shouldPostToTop()) {
            return;
          }
          try {
            window.top?.postMessage(
              {
                ...payload,
                feature: "gameStartIndicator",
                source: "QOLBox"
              },
              "*"
            );
          } catch {
          }
        }
        function saveFavicon() {
          if (originalFavicon) {
            return;
          }
          const targetDocument = getIndicatorDocument();
          const link = getFaviconLink();
          originalFavicon = link ? {
            href: link.getAttribute("href"),
            link,
            type: link.getAttribute("type")
          } : { href: null, link: null, type: null };
          faviconLink = link || targetDocument.createElement("link");
          if (!link) {
            faviconLink.rel = "icon";
            (targetDocument.head || targetDocument.documentElement).appendChild(faviconLink);
          }
        }
        function setFavicon(active) {
          saveFavicon();
          if (!faviconLink) {
            return;
          }
          if (active) {
            faviconLink.setAttribute("href", GAME_START_FAVICON_HREF);
            faviconLink.setAttribute("type", "image/svg+xml");
            postToTop({ action: "favicon", active: true });
            return;
          }
          if (originalFavicon?.href) {
            faviconLink.setAttribute("href", originalFavicon.href);
          } else {
            faviconLink.removeAttribute("href");
          }
          if (originalFavicon?.type) {
            faviconLink.setAttribute("type", originalFavicon.type);
          } else {
            faviconLink.removeAttribute("type");
          }
          postToTop({ action: "favicon", active: false });
        }
        function restoreFavicon() {
          if (!originalFavicon || !faviconLink) {
            return;
          }
          if (!originalFavicon.link) {
            faviconLink.remove();
          } else {
            setFavicon(false);
          }
          faviconLink = null;
          originalFavicon = null;
        }
        function getTitle() {
          return getIndicatorDocument().title || "";
        }
        function setTitle(title) {
          getIndicatorDocument().title = title;
          postToTop({ action: "title", title });
        }
        function postClear() {
          postToTop({ action: "clear" });
        }
        return {
          getTitle,
          postClear,
          restoreFavicon,
          setFavicon,
          setTitle
        };
      }

      // src/features/game-start-focus-hooks.ts
      function createGameStartFocusHookInstaller({
        handleAway,
        handleInteractionFocus,
        handleReturn,
        handleVisibilityChange,
        initializeFocusState
      }) {
        let hooksInstalled = false;
        function installGameStartIndicatorHooks() {
          if (hooksInstalled) {
            return;
          }
          hooksInstalled = true;
          initializeFocusState();
          document.addEventListener("pointerdown", handleInteractionFocus, true);
          document.addEventListener("mousedown", handleInteractionFocus, true);
          document.addEventListener("click", handleInteractionFocus, true);
          document.addEventListener("keydown", handleInteractionFocus, true);
          window.addEventListener("focus", handleReturn, true);
          window.addEventListener("blur", handleAway, true);
          document.addEventListener("visibilitychange", handleVisibilityChange, true);
        }
        return {
          installGameStartIndicatorHooks
        };
      }

      // src/features/game-start-local-transition.ts
      function createLocalPlayTransitionTracker(options) {
        let localTransitionSession = null;
        let localTransitionUntil = 0;
        function clear() {
          localTransitionSession = null;
          localTransitionUntil = 0;
        }
        function note(session = options.getSession()) {
          if (!session) {
            return false;
          }
          localTransitionSession = session;
          localTransitionUntil = Date.now() + options.timeoutMs;
          return true;
        }
        function has(session = options.getSession()) {
          if (!localTransitionSession || Date.now() > localTransitionUntil) {
            clear();
            return false;
          }
          return localTransitionSession === session;
        }
        function consume(session = options.getSession()) {
          if (!has(session)) {
            return false;
          }
          clear();
          return true;
        }
        return {
          clear,
          consume,
          has,
          note
        };
      }

      // src/features/game-start-timers.ts
      function createGameStartTimerController() {
        let indicatorTimer = 0;
        let watchTimer = 0;
        let endWatchTimer = 0;
        let flashTimer = 0;
        function clearIndicatorTimer() {
          if (indicatorTimer) {
            window.clearTimeout(indicatorTimer);
            indicatorTimer = 0;
          }
        }
        function clearWatchTimer() {
          if (watchTimer) {
            window.clearTimeout(watchTimer);
            watchTimer = 0;
          }
        }
        function clearEndWatchTimer() {
          if (endWatchTimer) {
            window.clearTimeout(endWatchTimer);
            endWatchTimer = 0;
          }
        }
        function clearFlashTimer() {
          if (flashTimer) {
            window.clearTimeout(flashTimer);
            flashTimer = 0;
          }
        }
        function setIndicatorTimer(callback, delayMs) {
          indicatorTimer = window.setTimeout(() => {
            indicatorTimer = 0;
            callback();
          }, delayMs);
        }
        function setWatchTimer(callback, delayMs) {
          watchTimer = window.setTimeout(() => {
            watchTimer = 0;
            callback();
          }, delayMs);
        }
        function setEndWatchTimer(callback, delayMs) {
          endWatchTimer = window.setTimeout(() => {
            endWatchTimer = 0;
            callback();
          }, delayMs);
        }
        function setFlashTimer(callback, delayMs) {
          flashTimer = window.setTimeout(callback, delayMs);
        }
        function hasIndicatorTimer() {
          return Boolean(indicatorTimer);
        }
        function hasWatchTimer() {
          return Boolean(watchTimer);
        }
        function hasEndWatchTimer() {
          return Boolean(endWatchTimer);
        }
        return {
          clearEndWatchTimer,
          clearFlashTimer,
          clearIndicatorTimer,
          clearWatchTimer,
          hasEndWatchTimer,
          hasIndicatorTimer,
          hasWatchTimer,
          setEndWatchTimer,
          setFlashTimer,
          setIndicatorTimer,
          setWatchTimer
        };
      }

      // src/features/game-start-indicator.ts
      function getTitlePrefix(reason) {
        return reason === "pulled" ? GAME_PULLED_TITLE_PREFIX : GAME_START_TITLE_PREFIX;
      }
      function createGameStartIndicatorController(options) {
        const display = createGameStartDisplayController();
        const localPlayTransition = createLocalPlayTransitionTracker({
          getSession: options.getSession,
          timeoutMs: options.localTransitionTimeoutMs
        });
        let sessionHookTarget = null;
        let indicatorActive = false;
        const timers = createGameStartTimerController();
        let flashOn = false;
        let originalTitle = "";
        let wasPlayingWhenUnfocused = false;
        let wasInLobbyWhenUnfocused = false;
        let observedSession = null;
        let wasSessionActive = false;
        let sessionEntryGraceSession = null;
        let sessionEntryGraceUntil = 0;
        let indicatorReason = "started";
        let pageFocused = true;
        const focusHooks = createGameStartFocusHookInstaller({
          handleAway,
          handleInteractionFocus,
          handleReturn,
          handleVisibilityChange,
          initializeFocusState
        });
        function isIndicatorPageFocused() {
          return pageFocused && options.isPageFocused();
        }
        function getPolledReason() {
          return wasInLobbyWhenUnfocused ? "started" : "pulled";
        }
        function clearIndicatorTimer() {
          timers.clearIndicatorTimer();
        }
        function clearWatchTimer() {
          timers.clearWatchTimer();
        }
        function clearEndWatchTimer() {
          timers.clearEndWatchTimer();
        }
        function clearFlashTimer() {
          timers.clearFlashTimer();
        }
        function flashIndicator() {
          if (!indicatorActive) {
            return;
          }
          flashOn = !flashOn;
          display.setTitle(`${getTitlePrefix(indicatorReason)}${originalTitle}`);
          display.setFavicon(flashOn);
          timers.setFlashTimer(flashIndicator, options.getFlashIntervalMs());
        }
        function scheduleEndWatch() {
          if (!indicatorActive || timers.hasEndWatchTimer()) {
            return;
          }
          timers.setEndWatchTimer(() => {
            if (!indicatorActive) {
              return;
            }
            if (!options.isPlayingMatch()) {
              wasPlayingWhenUnfocused = false;
              wasInLobbyWhenUnfocused = options.isPlayableLobby();
              clearIndicator();
              if (!isIndicatorPageFocused()) {
                scheduleWatch();
              }
              return;
            }
            scheduleEndWatch();
          }, options.endWatchIntervalMs);
        }
        function showIndicator(reason = "started") {
          if (!options.isEnabled()) {
            return;
          }
          if (indicatorActive) {
            scheduleEndWatch();
            return;
          }
          indicatorReason = reason;
          originalTitle = stripGameStartTitlePrefix(display.getTitle());
          indicatorActive = true;
          flashOn = false;
          clearFlashTimer();
          flashIndicator();
          scheduleEndWatch();
        }
        function clearIndicator() {
          clearIndicatorTimer();
          clearEndWatchTimer();
          clearFlashTimer();
          if (!indicatorActive) {
            return;
          }
          display.setTitle(originalTitle);
          display.restoreFavicon();
          display.postClear();
          originalTitle = "";
          flashOn = false;
          indicatorReason = "started";
          indicatorActive = false;
        }
        function noteLocallyInitiatedPlayTransition(session = options.getSession()) {
          if (!options.isEnabled()) {
            return;
          }
          if (localPlayTransition.note(session)) {
            clearIndicatorTimer();
          }
        }
        function hasPendingLocalPlayTransition(session = options.getSession()) {
          return localPlayTransition.has(session);
        }
        function consumePendingLocalPlayTransition(session = options.getSession()) {
          return localPlayTransition.consume(session);
        }
        function clearSessionEntryGrace() {
          sessionEntryGraceSession = null;
          sessionEntryGraceUntil = 0;
        }
        function noteSessionEntryGrace(session) {
          if (!session) {
            clearSessionEntryGrace();
            return;
          }
          sessionEntryGraceSession = session;
          sessionEntryGraceUntil = Date.now() + options.sessionEntryGraceMs;
        }
        function consumeSessionEntryGrace(session = options.getSession()) {
          if (!sessionEntryGraceSession || sessionEntryGraceSession !== session || Date.now() > sessionEntryGraceUntil) {
            clearSessionEntryGrace();
            return false;
          }
          clearSessionEntryGrace();
          return true;
        }
        function observeSessionEntry(session) {
          if (!session) {
            return;
          }
          if (session !== observedSession) {
            observedSession = session;
            wasSessionActive = false;
          }
          const sessionActive = options.isSessionActive();
          if (!sessionActive) {
            wasSessionActive = false;
            clearSessionEntryGrace();
            return;
          }
          if (!wasSessionActive) {
            if (options.isMatchActive()) {
              noteSessionEntryGrace(session);
            } else {
              clearSessionEntryGrace();
            }
          }
          wasSessionActive = true;
        }
        function scheduleIndicator(reason = "pulled") {
          if (!options.isEnabled() || timers.hasIndicatorTimer() || isIndicatorPageFocused()) {
            return;
          }
          clearWatchTimer();
          indicatorReason = reason;
          timers.setIndicatorTimer(() => {
            if (!isIndicatorPageFocused() && !wasPlayingWhenUnfocused && !hasPendingLocalPlayTransition() && options.isPlayingMatch() && !options.isPlayableLobby()) {
              showIndicator(indicatorReason);
            }
          }, options.getIndicatorDelayMs());
        }
        function scheduleWatch() {
          if (!options.isEnabled() || timers.hasWatchTimer() || isIndicatorPageFocused() || indicatorActive) {
            return;
          }
          timers.setWatchTimer(() => {
            updateGameStartIndicator();
            if (!indicatorActive && !isIndicatorPageFocused()) {
              scheduleWatch();
            }
          }, options.watchIntervalMs);
        }
        function handleStartAfterNativeEvent(wasPlayingMatch, wasPlayableLobby, session = options.getSession()) {
          const startedPlaying = !wasPlayingMatch && options.isPlayingMatch();
          if (startedPlaying && consumePendingLocalPlayTransition(session)) {
            wasPlayingWhenUnfocused = true;
            wasInLobbyWhenUnfocused = false;
            clearWatchTimer();
            clearIndicatorTimer();
            return;
          }
          if (startedPlaying && wasPlayableLobby && !isIndicatorPageFocused()) {
            clearWatchTimer();
            clearIndicatorTimer();
            showIndicator("started");
            return;
          }
          updateGameStartIndicator();
        }
        function patchMultiplayerSessionGameStartHooks(session = options.getSession()) {
          if (!options.isEnabled() || !session) {
            return;
          }
          observeSessionEntry(session);
          if (session === sessionHookTarget && areGameStartSessionHooksInstalled(session)) {
            return;
          }
          if (session !== sessionHookTarget && !isIndicatorPageFocused() && options.isPlayingMatch()) {
            wasPlayingWhenUnfocused = true;
            wasInLobbyWhenUnfocused = false;
            clearIndicatorTimer();
          }
          if (installGameStartSessionHooks(session, {
            captureStartState: () => ({
              wasPlayingMatch: options.isPlayingMatch(),
              wasPlayableLobby: options.isPlayableLobby()
            }),
            handleStartAfterNativeEvent: ({ wasPlayingMatch, wasPlayableLobby }, eventSession) => {
              handleStartAfterNativeEvent(wasPlayingMatch, wasPlayableLobby, eventSession);
            },
            noteLocalStartRequest: noteLocallyInitiatedPlayTransition
          })) {
            sessionHookTarget = session;
          }
        }
        function updateGameStartIndicator() {
          if (!options.isEnabled()) {
            wasPlayingWhenUnfocused = false;
            clearWatchTimer();
            clearIndicator();
            return;
          }
          const playingMatch = options.isPlayingMatch();
          const playableLobby = options.isPlayableLobby();
          patchMultiplayerSessionGameStartHooks();
          if (isIndicatorPageFocused()) {
            wasPlayingWhenUnfocused = playingMatch;
            wasInLobbyWhenUnfocused = false;
            return;
          }
          if (playableLobby) {
            wasPlayingWhenUnfocused = false;
            wasInLobbyWhenUnfocused = true;
            scheduleWatch();
            return;
          }
          if (!wasPlayingWhenUnfocused && playingMatch) {
            if (consumeSessionEntryGrace()) {
              wasPlayingWhenUnfocused = true;
              wasInLobbyWhenUnfocused = false;
              clearWatchTimer();
              clearIndicatorTimer();
              return;
            }
            scheduleIndicator(getPolledReason());
            return;
          }
          if (!playingMatch) {
            wasPlayingWhenUnfocused = false;
            wasInLobbyWhenUnfocused = false;
            clearSessionEntryGrace();
            clearWatchTimer();
            clearIndicator();
            scheduleWatch();
          }
        }
        function handleReturn() {
          if (!options.isEnabled()) {
            clearIndicator();
            return;
          }
          pageFocused = true;
          clearWatchTimer();
          clearIndicator();
          wasPlayingWhenUnfocused = options.isPlayingMatch();
          wasInLobbyWhenUnfocused = false;
        }
        function handleInteractionFocus() {
          if (options.isEnabled() && !document.hidden) {
            pageFocused = true;
            wasPlayingWhenUnfocused = options.isPlayingMatch();
            wasInLobbyWhenUnfocused = false;
          }
        }
        function setGameStartPageFocused(value) {
          pageFocused = Boolean(value);
        }
        function setGameStartWasPlayingWhenUnfocused(value) {
          wasPlayingWhenUnfocused = Boolean(value);
        }
        function setGameStartWasInLobbyWhenUnfocused(value) {
          wasInLobbyWhenUnfocused = Boolean(value);
        }
        function handleAway() {
          if (!options.isEnabled()) {
            return;
          }
          pageFocused = false;
          patchMultiplayerSessionGameStartHooks();
          wasPlayingWhenUnfocused = options.isPlayingMatch();
          wasInLobbyWhenUnfocused = !wasPlayingWhenUnfocused && options.isPlayableLobby();
          scheduleWatch();
        }
        function handleVisibilityChange() {
          if (!options.isEnabled()) {
            return;
          }
          if (document.hidden) {
            handleAway();
          } else {
            handleReturn();
          }
        }
        function initializeFocusState() {
          pageFocused = options.isPageFocused();
          if (!pageFocused) {
            wasPlayingWhenUnfocused = options.isPlayingMatch();
            wasInLobbyWhenUnfocused = !wasPlayingWhenUnfocused && options.isPlayableLobby();
          }
        }
        function installGameStartIndicatorHooks() {
          focusHooks.installGameStartIndicatorHooks();
        }
        function disableGameStartAlerts() {
          wasPlayingWhenUnfocused = false;
          wasInLobbyWhenUnfocused = false;
          observedSession = null;
          wasSessionActive = false;
          clearSessionEntryGrace();
          localPlayTransition.clear();
          clearWatchTimer();
          clearIndicator();
        }
        return {
          clearGameStartIndicator: clearIndicator,
          disableGameStartAlerts,
          handleGameStartInteractionFocus: handleInteractionFocus,
          hasPendingLocalPlayTransition,
          installGameStartIndicatorHooks,
          noteLocallyInitiatedPlayTransition,
          patchMultiplayerSessionGameStartHooks,
          setGameStartPageFocused,
          setGameStartWasInLobbyWhenUnfocused,
          setGameStartWasPlayingWhenUnfocused,
          updateGameStartIndicator
        };
      }

      // src/features/gameplay-state.ts
      function createGameplayStateController(options) {
        function hasReserveSuccessfulJoinLayer() {
          return options.hasVisibleLayer(options.lobbyLayerSelector) || options.hasVisibleLayer(options.gameplayLayerSelector);
        }
        function isMenuGameplayOverlap() {
          return options.hasVisibleLayer(options.menuLayerSelector) && options.hasVisibleLayer(options.playLayerSelector);
        }
        function isPageFocused() {
          return !document.hidden && (!document.hasFocus || document.hasFocus());
        }
        function isCurrentPlayerSpectating(session = options.getSession()) {
          const player = options.getSessionPlayer(session);
          const team = options.getPlayerTeamState(player);
          if (Number.isFinite(team)) {
            return team === 0;
          }
          return options.hasVisibleLayer(options.spectateControlsSelector);
        }
        function isPlayableLobby() {
          const session = options.getSession();
          if (options.isSessionMatchActive(session)) {
            return false;
          }
          if (options.isSessionLobbyActive(session)) {
            return !isCurrentPlayerSpectating(session);
          }
          return options.hasVisibleLayer(options.lobbyLayerSelector) && !options.hasVisibleLayer(options.spectateControlsSelector);
        }
        function isPlayingMatch() {
          const session = options.getSession();
          if (options.isSessionMatchActive(session)) {
            return !isCurrentPlayerSpectating(session);
          }
          return options.hasVisibleLayer(options.gameplayLayerSelector) && !options.hasVisibleLayer(options.spectateControlsSelector);
        }
        return {
          hasReserveSuccessfulJoinLayer,
          isCurrentPlayerSpectating,
          isMenuGameplayOverlap,
          isPageFocused,
          isPlayableLobby,
          isPlayingMatch
        };
      }

      // src/features/gameplay-alert-feature-bundle.ts
      function createGameplayAlertFeatureBundle(options) {
        const gameplayState = createGameplayStateController({
          gameplayLayerSelector: FULLSCREEN_GAMEPLAY_LAYER_SELECTOR,
          lobbyLayerSelector: ".lobbyContainer",
          menuLayerSelector: FULLSCREEN_MENU_LAYER_SELECTOR,
          playLayerSelector: FULLSCREEN_PLAY_LAYER_SELECTOR,
          spectateControlsSelector: ".spectateControls",
          getPlayerTeamState,
          getSession: getMultiplayerSession,
          getSessionPlayer,
          hasVisibleLayer,
          isSessionLobbyActive,
          isSessionMatchActive
        });
        const gameStartIndicator = createGameStartIndicatorController({
          endWatchIntervalMs: GAME_START_END_WATCH_INTERVAL_MS,
          getFlashIntervalMs: getAdvancedGameStartFlashIntervalMs,
          getIndicatorDelayMs: getAdvancedGameStartAlertDelayMs,
          localTransitionTimeoutMs: GAME_START_LOCAL_TRANSITION_TIMEOUT_MS,
          sessionEntryGraceMs: GAME_START_SESSION_ENTRY_GRACE_MS,
          watchIntervalMs: GAME_START_WATCH_INTERVAL_MS,
          getSession: getMultiplayerSession,
          isEnabled: options.isGameStartAlertEnabled,
          isMatchActive: () => isSessionMatchActive(getMultiplayerSession()),
          isPageFocused: gameplayState.isPageFocused,
          isPlayableLobby: gameplayState.isPlayableLobby,
          isPlayingMatch: gameplayState.isPlayingMatch,
          isSessionActive: () => {
            const session = getMultiplayerSession();
            return isSessionLobbyActive(session) || isSessionMatchActive(session);
          }
        });
        return {
          ...gameplayState,
          ...gameStartIndicator
        };
      }

      // src/features/in-game-chat-scroll.ts
      var CHAT_READING_CLASS = "qolboxChatReading";
      var CHAT_INTERACTIVE_CLASS = "qolboxChatInteractive";
      var RESTORED_CHAT_MESSAGE_ATTR = "data-qolbox-restored-chat-message";
      var MAX_RETAINED_MESSAGES = 1e3;
      var RESTORED_HISTORY_DISPLAY_MS = 6500;
      function getChatContent(chat) {
        return chat.querySelector(".content");
      }
      function hasVisibleGameplayCanvas() {
        const canvas = document.querySelector("#pixiContainer canvas");
        if (!canvas || typeof canvas.getBoundingClientRect !== "function") {
          return false;
        }
        const rect = canvas.getBoundingClientRect();
        const style = typeof window.getComputedStyle === "function" ? getComputedStyle(canvas) : null;
        if (!style) {
          return false;
        }
        return style.display !== "none" && style.visibility !== "hidden" && rect.width > 0 && rect.height > 0;
      }
      function getChatMessageViewportHeight(chat) {
        const input = chat.querySelector(".input");
        if (!input) {
          return chat.clientHeight;
        }
        const chatRect = chat.getBoundingClientRect();
        const inputRect = input.getBoundingClientRect();
        return Math.max(20, inputRect.top - chatRect.top);
      }
      function getMaxChatOffset(chat, content) {
        return Math.max(0, content.scrollHeight - getChatMessageViewportHeight(chat));
      }
      function getChatOpacity(chat) {
        const opacity = Number(getComputedStyle(chat).opacity);
        return Number.isFinite(opacity) ? opacity : 1;
      }
      function isChatShellVisible(chat) {
        const rect = chat.getBoundingClientRect();
        const style = getComputedStyle(chat);
        return style.display !== "none" && style.visibility !== "hidden" && rect.width > 0 && rect.height > 0 && getChatOpacity(chat) > 0.04;
      }
      function isChatVisible(chat) {
        const hasFocusedInput = Boolean(chat.querySelector(".input:focus"));
        const hasMessageText = Boolean((getChatContent(chat)?.textContent || "").trim());
        return isChatShellVisible(chat) && (hasFocusedInput || hasMessageText);
      }
      function hasLostRetainedHistory(content, state) {
        const messages = getContentMessages(content);
        return messages.signatures.length > 0 && messages.signatures.length < state.historySignatures.length;
      }
      function shouldRestoreRetainedHistory(chat, content, state) {
        return state.historyInteractionActive || state.offsetPx > 0 || chat.classList.contains(CHAT_READING_CLASS) || chat.matches(":hover") || Boolean(chat.querySelector(".input:focus")) || hasLostRetainedHistory(content, state);
      }
      function hasFocusedChatInput(chat) {
        return Boolean(chat.querySelector(".input:focus"));
      }
      function getMessageNodes(content) {
        const nodes = Array.from(content.childNodes).filter((node) => (node.textContent || "").trim());
        return nodes;
      }
      function getMessageHtml(node) {
        if (node instanceof Element) {
          return node.outerHTML;
        }
        const container = document.createElement("span");
        container.textContent = node.textContent || "";
        return container.outerHTML;
      }
      function getContentMessages(content) {
        const nodes = getMessageNodes(content);
        const html = nodes.map(getMessageHtml);
        return {
          html,
          nodes,
          signatures: html.map((value) => `${value.length}:${value}`)
        };
      }
      function getMessageSignatures(html) {
        return html.map((value) => `${value.length}:${value}`);
      }
      function getOverlapLength(left, right) {
        const maxOverlap = Math.min(left.length, right.length);
        for (let overlap = maxOverlap; overlap > 0; overlap -= 1) {
          let matches = true;
          for (let index = 0; index < overlap; index += 1) {
            if (left[left.length - overlap + index] !== right[index]) {
              matches = false;
              break;
            }
          }
          if (matches) {
            return overlap;
          }
        }
        return 0;
      }
      function rememberMessageRecords(messages, state) {
        if (state.restoring) {
          return false;
        }
        const html = messages.html;
        const signatures = getMessageSignatures(html);
        if (!signatures.length) {
          return false;
        }
        const overlap = getOverlapLength(state.historySignatures, signatures);
        const newHtml = html.slice(overlap);
        const newNodes = messages.nodes?.slice(overlap) || [];
        const newSignatures = signatures.slice(overlap);
        state.historyHtml.push(...newHtml);
        state.historyNodes.push(...newHtml.map((_, index) => newNodes[index] || null));
        state.historySignatures.push(...newSignatures);
        if (state.historyHtml.length > MAX_RETAINED_MESSAGES) {
          const excess = state.historyHtml.length - MAX_RETAINED_MESSAGES;
          state.historyHtml.splice(0, excess);
          state.historyNodes.splice(0, excess);
          state.historySignatures.splice(0, excess);
        }
        return newHtml.length > 0;
      }
      function rememberChatMessages(content, state) {
        if (state.restoredDomActive) {
          return;
        }
        rememberMessageRecords(getContentMessages(content), state);
      }
      function rememberAddedChatNodes(nodes, state) {
        const retainedNodes = nodes.filter((node) => !isRestoredChatMessageNode(node, state) && (node.textContent || "").trim());
        const html = retainedNodes.map(getMessageHtml);
        if (rememberMessageRecords({ html, nodes: retainedNodes }, state)) {
          state.historyVisibleUntil = performance.now() + RESTORED_HISTORY_DISPLAY_MS;
        }
      }
      function appendRetainedHtmlFallback(content, html) {
        const template = document.createElement("template");
        template.innerHTML = html;
        if (template.content) {
          content.appendChild(template.content.cloneNode(true));
          return;
        }
        const fallback = document.createElement("span");
        fallback.innerHTML = html;
        content.appendChild(fallback);
      }
      function getNodePath(root, target) {
        const path = [];
        let current = target;
        while (current && current !== root) {
          const parent = current.parentNode;
          if (!parent) {
            return null;
          }
          path.unshift(Array.prototype.indexOf.call(parent.childNodes, current));
          current = parent;
        }
        return current === root ? path : null;
      }
      function getNodeByPath(root, path) {
        let current = root;
        for (const index of path) {
          current = current.childNodes[index] || null;
          if (!current) {
            return null;
          }
        }
        return current;
      }
      function markRestoredChatMessageNode(node, state) {
        state.restoredNodes.add(node);
        if (node instanceof Element) {
          node.setAttribute(RESTORED_CHAT_MESSAGE_ATTR, "true");
        }
      }
      function cloneRetainedMessageNode(retainedNode, state) {
        const restoredNode = retainedNode.cloneNode(true);
        markRestoredChatMessageNode(restoredNode, state);
        if (!(retainedNode instanceof Element) || !(restoredNode instanceof Element)) {
          return restoredNode;
        }
        restoredNode.addEventListener(
          "click",
          (event) => {
            const target = event.target instanceof Node ? event.target : restoredNode;
            const path = getNodePath(restoredNode, target);
            const retainedTarget = path ? getNodeByPath(retainedNode, path) : retainedNode;
            const clickTarget = retainedTarget instanceof HTMLElement ? retainedTarget : retainedNode instanceof HTMLElement ? retainedNode : null;
            if (!clickTarget) {
              return;
            }
            event.preventDefault();
            event.stopPropagation();
            clickTarget.click();
          },
          true
        );
        return restoredNode;
      }
      function isRestoredChatMessageNode(node, state) {
        return state.restoredNodes.has(node) || node instanceof Element && (node.hasAttribute(RESTORED_CHAT_MESSAGE_ATTR) || Boolean(node.closest(`[${RESTORED_CHAT_MESSAGE_ATTR}]`)));
      }
      function restoreRetainedChatMessages(content, state) {
        if (!state.historyHtml.length) {
          return;
        }
        const messages = getContentMessages(content);
        if (messages.signatures.length >= state.historySignatures.length) {
          return;
        }
        state.restoring = true;
        content.innerHTML = "";
        for (let index = 0; index < state.historyHtml.length; index += 1) {
          const retainedNode = state.historyNodes[index];
          if (retainedNode) {
            content.appendChild(cloneRetainedMessageNode(retainedNode, state));
          } else {
            const childCountBeforeAppend = content.childNodes.length;
            appendRetainedHtmlFallback(content, state.historyHtml[index]);
            for (const node of Array.from(content.childNodes).slice(childCountBeforeAppend)) {
              markRestoredChatMessageNode(node, state);
            }
          }
        }
        state.restoring = false;
        state.restoredDomActive = true;
      }
      function clearRestoredChatDom(content, state, force = false) {
        if (!force && !state.historyInteractionActive) {
          return;
        }
        if (state.restoredDomActive || state.historyInteractionActive) {
          state.restoring = true;
          content.innerHTML = "";
          state.restoring = false;
        }
        state.restoredDomActive = false;
        state.historyInteractionActive = false;
        state.historyVisibleUntil = 0;
        if (state.fadeSyncTimerId) {
          window.clearTimeout(state.fadeSyncTimerId);
          state.fadeSyncTimerId = 0;
        }
        state.offsetPx = 0;
        content.style.transform = "";
        content.style.willChange = "";
      }
      function applyChatOffset(chat, content, state) {
        const maxOffset = getMaxChatOffset(chat, content);
        state.offsetPx = Math.max(0, Math.min(maxOffset, state.offsetPx));
        if (state.offsetPx > 0) {
          content.style.transform = `translateY(${Math.round(state.offsetPx)}px)`;
          content.style.willChange = "transform";
          chat.classList.add(CHAT_READING_CLASS);
          chat.dataset.qolboxChatOffset = String(Math.round(state.offsetPx));
        } else {
          content.style.transform = "";
          content.style.willChange = "";
          delete chat.dataset.qolboxChatOffset;
          if (!chat.matches(":hover")) {
            chat.classList.remove(CHAT_READING_CLASS);
          }
        }
      }
      function createInGameChatScrollController(options) {
        const patchedChats = /* @__PURE__ */ new Set();
        const chatStates = /* @__PURE__ */ new WeakMap();
        const chatObservers = /* @__PURE__ */ new WeakMap();
        let keyHooksInstalled = false;
        let patchScheduled = false;
        function removeContentWheelListener(state) {
          if (!state.wheelListenerTarget || !state.wheelListener) {
            state.wheelListenerTarget = null;
            return;
          }
          state.wheelListenerTarget.removeEventListener("wheel", state.wheelListener, true);
          state.wheelListenerTarget = null;
        }
        function handleChatWheel(chat, state, event) {
          if (!options.isChatFeatureEnabled()) {
            cleanupInGameChatScroll();
            return;
          }
          const target = event.target instanceof Element ? event.target : null;
          if (target?.closest("input, textarea, select, button, .qolboxMenuOverlay")) {
            return;
          }
          const content = getChatContent(chat);
          if (!content || getMaxChatOffset(chat, content) <= 0) {
            removeContentWheelListener(state);
            return;
          }
          state.offsetPx -= event.deltaY;
          applyChatOffset(chat, content, state);
          syncContentWheelListener(chat, content, state);
          event.preventDefault();
          event.stopPropagation();
        }
        function syncContentWheelListener(chat, content, state) {
          const shouldListen = options.isChatFeatureEnabled() && isChatShellVisible(chat) && getMaxChatOffset(chat, content) > 0;
          if (!shouldListen) {
            removeContentWheelListener(state);
            return;
          }
          if (!state.wheelListener) {
            state.wheelListener = (event) => handleChatWheel(chat, state, event);
          }
          if (state.wheelListenerTarget === content) {
            return;
          }
          removeContentWheelListener(state);
          content.addEventListener("wheel", state.wheelListener, { capture: true, passive: false });
          state.wheelListenerTarget = content;
        }
        function cleanupChatScroll(chat) {
          if (!(chat instanceof HTMLElement)) {
            return;
          }
          const content = getChatContent(chat);
          const state = chatStates.get(chat);
          if (state) {
            removeContentWheelListener(state);
            if (state.fadeSyncTimerId) {
              window.clearTimeout(state.fadeSyncTimerId);
              state.fadeSyncTimerId = 0;
            }
            if (content) {
              clearRestoredChatDom(content, state, true);
              content.style.transform = "";
              content.style.willChange = "";
            }
            state.offsetPx = 0;
            state.historyVisibleUntil = 0;
            state.syncScheduled = false;
          }
          chat.classList.remove(CHAT_INTERACTIVE_CLASS);
          chat.classList.remove(CHAT_READING_CLASS);
          delete chat.dataset.qolboxChatOffset;
        }
        function clearRetainedChatState(state) {
          state.historyHtml = [];
          state.historyNodes = [];
          state.historySignatures = [];
          state.historyVisibleUntil = 0;
          state.restoredNodes = /* @__PURE__ */ new WeakSet();
          state.restoredDomActive = false;
          state.historyInteractionActive = false;
          state.restoring = false;
          state.syncScheduled = false;
        }
        function unpatchChatScroll(chat) {
          cleanupChatScroll(chat);
          const state = chatStates.get(chat);
          if (state) {
            chat.removeEventListener("pointerenter", state.pointerEnterListener);
            chat.removeEventListener("pointerleave", state.pointerLeaveListener);
            chat.removeEventListener("focusin", state.focusInListener, true);
            chat.removeEventListener("focusout", state.focusOutListener, true);
            clearRetainedChatState(state);
          }
          chatObservers.get(chat)?.disconnect();
          chatObservers.delete(chat);
          chatStates.delete(chat);
          patchedChats.delete(chat);
          if (chat instanceof HTMLElement) {
            delete chat.dataset.qolboxChatScrollPatched;
          }
        }
        function isUserReadingChat(chat, state) {
          return hasFocusedChatInput(chat) || state.offsetPx > 0 || chat.matches(":hover") || chat.classList.contains(CHAT_READING_CLASS);
        }
        function scheduleFadeSync(chat, state, delayMs) {
          if (state.fadeSyncTimerId) {
            return;
          }
          state.fadeSyncTimerId = window.setTimeout(() => {
            state.fadeSyncTimerId = 0;
            syncChat(chat);
          }, Math.max(50, delayMs));
        }
        function syncChat(chat) {
          if (!(chat instanceof HTMLElement)) {
            return;
          }
          const content = getChatContent(chat);
          const state = chatStates.get(chat);
          if (!content || !state) {
            return;
          }
          if (!options.isChatFeatureEnabled()) {
            cleanupInGameChatScroll();
            return;
          }
          rememberChatMessages(content, state);
          const visible = isChatVisible(chat) || isChatShellVisible(chat) && state.historyInteractionActive && state.historyHtml.length > 0;
          if (visible) {
            const now = performance.now();
            const userReading = isUserReadingChat(chat, state);
            if (userReading) {
              state.historyVisibleUntil = 0;
              if (state.fadeSyncTimerId) {
                window.clearTimeout(state.fadeSyncTimerId);
                state.fadeSyncTimerId = 0;
              }
            } else if (state.historyInteractionActive && state.historyVisibleUntil <= 0) {
              state.historyVisibleUntil = now + RESTORED_HISTORY_DISPLAY_MS;
            }
            if (!userReading && state.historyVisibleUntil > 0 && now >= state.historyVisibleUntil) {
              clearRestoredChatDom(content, state);
              chat.classList.remove(CHAT_INTERACTIVE_CLASS);
              chat.classList.remove(CHAT_READING_CLASS);
              syncContentWheelListener(chat, content, state);
              return;
            }
            chat.classList.add(CHAT_INTERACTIVE_CLASS);
            if (shouldRestoreRetainedHistory(chat, content, state)) {
              if (hasFocusedChatInput(chat) || state.offsetPx > 0) {
                state.historyInteractionActive = true;
              }
              restoreRetainedChatMessages(content, state);
            } else {
              clearRestoredChatDom(content, state);
            }
            applyChatOffset(chat, content, state);
            syncContentWheelListener(chat, content, state);
            if (!userReading && state.historyVisibleUntil > 0) {
              scheduleFadeSync(chat, state, state.historyVisibleUntil - now + 50);
            }
            return;
          }
          clearRestoredChatDom(content, state, true);
          chat.classList.remove(CHAT_INTERACTIVE_CLASS);
          if (state.offsetPx <= 0) {
            chat.classList.remove(CHAT_READING_CLASS);
          }
          syncContentWheelListener(chat, content, state);
        }
        function scheduleChatSync(chat) {
          const state = chatStates.get(chat);
          if (state?.syncScheduled) {
            return;
          }
          if (state) {
            state.syncScheduled = true;
          }
          window.setTimeout(() => {
            if (state) {
              state.syncScheduled = false;
            }
            syncChat(chat);
            window.requestAnimationFrame(() => syncChat(chat));
          }, 0);
        }
        function schedulePatchInGameChatScroll(delayMs = 100) {
          if (!options.isChatFeatureEnabled()) {
            cleanupInGameChatScroll();
            return;
          }
          if (patchScheduled) {
            return;
          }
          patchScheduled = true;
          window.setTimeout(() => {
            patchScheduled = false;
            patchInGameChatScroll();
          }, delayMs);
        }
        function patchChat(chat) {
          if (patchedChats.has(chat)) {
            return;
          }
          patchedChats.add(chat);
          if (chat instanceof HTMLElement) {
            chat.dataset.qolboxChatScrollPatched = "true";
          }
          let state;
          const focusInListener = () => scheduleChatSync(chat);
          const focusOutListener = () => scheduleChatSync(chat);
          const pointerEnterListener = () => {
            if (!options.isChatFeatureEnabled()) {
              return;
            }
            if (chat instanceof HTMLElement && isChatVisible(chat)) {
              chat.classList.add(CHAT_READING_CLASS);
            }
          };
          const pointerLeaveListener = () => {
            if (!options.isChatFeatureEnabled()) {
              cleanupInGameChatScroll();
              return;
            }
            if (state.offsetPx <= 0) {
              chat.classList.remove(CHAT_READING_CLASS);
            }
            scheduleChatSync(chat);
          };
          state = {
            historyInteractionActive: false,
            historyHtml: [],
            historyNodes: [],
            historySignatures: [],
            historyVisibleUntil: 0,
            fadeSyncTimerId: 0,
            focusInListener,
            focusOutListener,
            offsetPx: 0,
            pointerEnterListener,
            pointerLeaveListener,
            restoredDomActive: false,
            restoredNodes: /* @__PURE__ */ new WeakSet(),
            restoring: false,
            syncScheduled: false,
            wheelListener: null,
            wheelListenerTarget: null
          };
          chatStates.set(chat, state);
          chat.addEventListener("pointerenter", state.pointerEnterListener);
          chat.addEventListener("pointerleave", state.pointerLeaveListener);
          chat.addEventListener("focusin", state.focusInListener, true);
          chat.addEventListener("focusout", state.focusOutListener, true);
          const chatObserver = new MutationObserver((records) => {
            if (!options.isChatFeatureEnabled()) {
              cleanupInGameChatScroll();
              return;
            }
            const content2 = getChatContent(chat);
            const currentState = chatStates.get(chat);
            if (content2 && currentState) {
              for (const record of records) {
                if (record.type === "childList" && record.target === content2 && record.addedNodes.length) {
                  rememberAddedChatNodes(Array.from(record.addedNodes), currentState);
                }
              }
            }
            scheduleChatSync(chat);
          });
          chatObserver.observe(chat, {
            attributes: true,
            attributeFilter: ["class", "style"]
          });
          const content = getChatContent(chat);
          if (content) {
            chatObserver.observe(content, { childList: true });
          }
          chatObservers.set(chat, chatObserver);
          syncChat(chat);
        }
        function installKeyHooks() {
          if (keyHooksInstalled) {
            return;
          }
          keyHooksInstalled = true;
          document.addEventListener("keydown", () => schedulePatchInGameChatScroll(0), true);
          document.addEventListener("keyup", () => schedulePatchInGameChatScroll(0), true);
        }
        function patchInGameChatScroll() {
          installKeyHooks();
          if (!options.isChatFeatureEnabled()) {
            cleanupInGameChatScroll();
            return;
          }
          if (!hasVisibleGameplayCanvas()) {
            return;
          }
          for (const chat of document.querySelectorAll(".inGameChat")) {
            patchChat(chat);
            syncChat(chat);
          }
        }
        function cleanupInGameChatScroll() {
          for (const chat of Array.from(patchedChats)) {
            unpatchChatScroll(chat);
          }
        }
        return {
          cleanupInGameChatScroll,
          patchInGameChatScroll
        };
      }

      // src/hitbox/mobile-controls-adapter.ts
      function isNativeMobileMode() {
        const game = window.a8;
        return Boolean(readNativeProperty(game, "xm") || readNativeProperty(game, "PD"));
      }
      function isNativeTouchLobbyChatPrompt() {
        return Boolean(readNativeProperty(window.a8, "xm"));
      }
      function getNativeMobileControls() {
        return readNativeProperty(window.a8, "PD") ?? null;
      }
      function getControlSlot(controls, key) {
        return readNativeProperty(controls, key);
      }
      function getControlInputState(control) {
        const inputState = readNativeProperty(control, "hg");
        return isNativeObject(inputState) ? inputState : null;
      }
      function getNativeMobileControlInputState(controls = getNativeMobileControls()) {
        for (const key of ["oz", "rz", "az", "nz"]) {
          const inputState = getControlInputState(getControlSlot(controls, key));
          if (inputState) {
            return inputState;
          }
        }
        return null;
      }
      function getLiveMultiplayerInputState() {
        const inputState = readNativePath(window.multiplayerSession, ["KR", "hg"]);
        return isNativeObject(inputState) ? inputState : null;
      }
      function getNativeMobileAbilityButtonElements() {
        const controls = getNativeMobileControls();
        if (!controls) {
          return [];
        }
        const buttons = [];
        for (const key of ["oz", "rz", "az"]) {
          const element = readNativeProperty(getControlSlot(controls, key), "hf");
          if (element instanceof Element) {
            buttons.push(element);
          }
        }
        return buttons;
      }
      function setGrabInputPressed(inputState, pressed) {
        if (!isNativeObject(inputState)) {
          return false;
        }
        setNativeReflectProperty(inputState, "Fn", pressed);
        return true;
      }
      function installNativeMobileControlHooks(hooks) {
        const controls = getNativeMobileControls();
        if (!isNativeObject(controls)) {
          return false;
        }
        if (readNativeProperty(controls, "__qolboxMobileGrabPatched")) {
          return true;
        }
        setNativeReflectProperty(controls, "__qolboxMobileGrabPatched", true);
        const setInputState = readNativeProperty(controls, "ED");
        if (typeof setInputState === "function") {
          const originalSetInputState = setInputState;
          setNativeReflectProperty(
            controls,
            "ED",
            function wrappedMobileControlInputState(inputState, ...rest) {
              hooks.onInputStateObserved(inputState);
              const result = Reflect.apply(originalSetInputState, this, [inputState, ...rest]);
              hooks.afterInputStateSet(inputState);
              hooks.onControlsShown();
              return result;
            }
          );
        }
        const showControls = readNativeProperty(controls, "NL");
        if (typeof showControls === "function") {
          const originalShowControls = showControls;
          setNativeReflectProperty(controls, "NL", function wrappedMobileControlsShow(...args) {
            const result = Reflect.apply(originalShowControls, this, args);
            hooks.onControlsShown();
            return result;
          });
        }
        const hideControls = readNativeProperty(controls, "_L");
        if (typeof hideControls === "function") {
          const originalHideControls = hideControls;
          setNativeReflectProperty(controls, "_L", function wrappedMobileControlsHide(...args) {
            const result = Reflect.apply(originalHideControls, this, args);
            hooks.onControlsHidden();
            return result;
          });
        }
        return true;
      }

      // src/features/gameplay-background-focus-events.ts
      function readGameplayFocusProperty(source, property) {
        return readObjectProperty(source, property);
      }
      function readStringProperty2(source, property) {
        const value = readGameplayFocusProperty(source, property);
        return typeof value === "string" ? value : "";
      }
      function readNumberProperty2(source, property) {
        const value = readGameplayFocusProperty(source, property);
        return typeof value === "number" ? value : Number(value);
      }
      function readGameplayFocusBooleanProperty(source, property) {
        return readGameplayFocusProperty(source, property) === true;
      }
      function canPreventGameplayDefault(event) {
        return typeof event === "object" && event !== null && typeof readGameplayFocusProperty(event, "preventDefault") === "function";
      }
      function canDispatchEvents(element) {
        return element instanceof Element && typeof readGameplayFocusProperty(element, "dispatchEvent") === "function";
      }
      function canBlurGameplayFocusTarget(element) {
        return typeof element === "object" && element !== null && typeof readGameplayFocusProperty(element, "blur") === "function";
      }
      function hasTabIndexApi(element) {
        return element instanceof Element && typeof readGameplayFocusProperty(element, "hasAttribute") === "function" && typeof readGameplayFocusProperty(element, "tabIndex") === "number";
      }
      function ensureGameplayFocusTargetFocusable(element) {
        if (hasTabIndexApi(element) && !element.hasAttribute("tabindex")) {
          element.tabIndex = -1;
        }
      }
      function getPointerEventType(event) {
        return readStringProperty2(event, "type");
      }
      function isPrimaryGameplayMouseButton(event) {
        const button = readGameplayFocusProperty(event, "button");
        return button === void 0 || button === 0;
      }
      function clampPointerToRect(value, min, max) {
        if (!Number.isFinite(value)) {
          return (min + max) / 2;
        }
        return Math.max(min, Math.min(max, value));
      }
      function createForwardedPointerEvent(event, clientX, clientY) {
        const eventType = getPointerEventType(event);
        const commonInit = {
          bubbles: true,
          cancelable: true,
          button: 0,
          buttons: eventType === "click" ? 0 : 1,
          clientX,
          clientY,
          ctrlKey: readGameplayFocusBooleanProperty(event, "ctrlKey"),
          shiftKey: readGameplayFocusBooleanProperty(event, "shiftKey"),
          altKey: readGameplayFocusBooleanProperty(event, "altKey"),
          metaKey: readGameplayFocusBooleanProperty(event, "metaKey")
        };
        if (/^pointer/i.test(eventType) && typeof PointerEvent === "function") {
          return new PointerEvent(eventType, {
            ...commonInit,
            pointerId: readNumberProperty2(event, "pointerId") || 1,
            pointerType: readStringProperty2(event, "pointerType") || "mouse",
            isPrimary: readGameplayFocusProperty(event, "isPrimary") !== false
          });
        }
        return new MouseEvent(eventType, commonInit);
      }
      function forwardGameplayPointerToCanvas(event, canvas) {
        const eventType = getPointerEventType(event);
        if (!canDispatchEvents(canvas) || !/^(?:pointerdown|mousedown|click)$/i.test(eventType)) {
          return false;
        }
        const rect = canvas.getBoundingClientRect();
        if (!rect.width || !rect.height) {
          return false;
        }
        const rectRight = Number.isFinite(rect.right) ? rect.right : rect.left + rect.width;
        const rectBottom = Number.isFinite(rect.bottom) ? rect.bottom : rect.top + rect.height;
        const clientX = clampPointerToRect(readNumberProperty2(event, "clientX"), rect.left + 1, rectRight - 1);
        const clientY = clampPointerToRect(readNumberProperty2(event, "clientY"), rect.top + 1, rectBottom - 1);
        const forwardedEvent = createForwardedPointerEvent(event, clientX, clientY);
        setObjectProperty(forwardedEvent, "__qolboxForwardedGameplayPointer", true);
        canvas.dispatchEvent(forwardedEvent);
        return true;
      }

      // src/features/gameplay-background-focus.ts
      function createGameplayBackgroundFocusController(options) {
        let hooksInstalled = false;
        function focusActiveRenderCanvas() {
          const canvas = options.getActiveRenderCanvas();
          if (!canvas) {
            return;
          }
          ensureGameplayFocusTargetFocusable(canvas);
          focusElementWithoutScroll(canvas);
        }
        function captureGameplayInputFocus() {
          if (typeof window.focus === "function") {
            try {
              window.focus();
            } catch {
            }
          }
          focusActiveRenderCanvas();
          const activeElement = document.activeElement;
          if (options.getActiveChatInput() === activeElement && canBlurGameplayFocusTarget(activeElement)) {
            activeElement.blur();
          }
          const canvasConstructor = typeof HTMLCanvasElement === "function" ? HTMLCanvasElement : null;
          if (!(canvasConstructor && document.activeElement instanceof canvasConstructor) && document.body) {
            if (typeof document.body.hasAttribute !== "function" || !document.body.hasAttribute("tabindex")) {
              document.body.tabIndex = -1;
            }
            focusElementWithoutScroll(document.body);
            focusActiveRenderCanvas();
          }
        }
        function forwardGameplayBackgroundPointer(event) {
          return forwardGameplayPointerToCanvas(event, options.getActiveRenderCanvas());
        }
        function isGameplayRenderTarget(target) {
          return Boolean(
            target instanceof Element && (target.matches(options.renderCanvasSelector) || target.closest(options.renderLayerSelector))
          );
        }
        function shouldCaptureGameplayBackgroundFocus(event) {
          if (!options.isPlayingMatch() || !options.isQolboxMenuClosed() || options.getActiveChatInput() || readGameplayFocusBooleanProperty(event, "__qolboxForwardedGameplayPointer") || readGameplayFocusBooleanProperty(event, "defaultPrevented")) {
            return false;
          }
          if (!isPrimaryGameplayMouseButton(event)) {
            return false;
          }
          const target = readGameplayFocusProperty(event, "target");
          if (!(target instanceof Element)) {
            return false;
          }
          if (target.closest(options.exclusionSelector)) {
            return false;
          }
          return !isGameplayRenderTarget(target);
        }
        function handleGameplayBackgroundFocus(event) {
          if (!shouldCaptureGameplayBackgroundFocus(event)) {
            return;
          }
          if (readGameplayFocusBooleanProperty(event, "cancelable") && canPreventGameplayDefault(event)) {
            event.preventDefault();
          }
          captureGameplayInputFocus();
          forwardGameplayBackgroundPointer(event);
          window.setTimeout(() => {
            if (shouldCaptureGameplayBackgroundFocus(event)) {
              captureGameplayInputFocus();
            }
          }, 0);
        }
        function installGameplayBackgroundFocusHooks() {
          if (hooksInstalled) {
            return;
          }
          hooksInstalled = true;
          document.addEventListener("pointerdown", handleGameplayBackgroundFocus, true);
          document.addEventListener("mousedown", handleGameplayBackgroundFocus, true);
          document.addEventListener("click", handleGameplayBackgroundFocus, true);
        }
        return {
          captureGameplayInputFocus,
          forwardGameplayBackgroundPointer,
          handleGameplayBackgroundFocus,
          installGameplayBackgroundFocusHooks,
          shouldCaptureGameplayBackgroundFocus
        };
      }

      // src/features/render-canvas-focus.ts
      function createRenderCanvasFocusController(options) {
        function resetBrowserScroll() {
          try {
            window.scrollTo(0, 0);
          } catch {
          }
          document.documentElement.scrollTop = 0;
          document.body.scrollTop = 0;
        }
        function focusActiveRenderCanvas() {
          const canvas = options.getActiveRenderCanvas();
          if (!canvas) {
            return;
          }
          if (isTabbableElement(canvas) && !canvas.hasAttribute("tabindex")) {
            canvas.tabIndex = -1;
          }
          options.focusElementWithoutScroll(canvas);
        }
        return {
          focusActiveRenderCanvas,
          resetBrowserScroll
        };
      }

      // src/hitbox/chat-send-adapter.ts
      function isNativeCallable8(value) {
        return typeof value === "function";
      }
      function getAccurateNativeHelpText(text) {
        return text === "/settings -- view all gameplay commands" ? "/settings -- view normal gameplay settings" : text;
      }
      function callNativeChatSend(nativeSendChat, session, message, rest) {
        return Reflect.apply(nativeSendChat, session, [message, ...rest]);
      }
      function callNativeChatSendWithSettingsHelpCorrection(nativeSendChat, session, message, rest) {
        const nativeShowStatus = readNativeProperty(session, "vG");
        if (!isNativeObject(session) || !isNativeCallable8(nativeShowStatus)) {
          return callNativeChatSend(nativeSendChat, session, message, rest);
        }
        const accurateShowStatus = function showAccurateNativeSettingsHelp(text, ...statusRest) {
          return Reflect.apply(nativeShowStatus, this, [getAccurateNativeHelpText(text), ...statusRest]);
        };
        setNativeReflectProperty(session, "vG", accurateShowStatus);
        try {
          return callNativeChatSend(nativeSendChat, session, message, rest);
        } finally {
          setNativeReflectProperty(session, "vG", nativeShowStatus);
        }
      }
      function installNativeChatSendInterceptor(session, options) {
        if (!isNativeObject(session)) {
          return false;
        }
        const nativeSendChat = readNativeProperty(session, "CJ");
        if (!isNativeCallable8(nativeSendChat) || readNativeProperty(session, "__qolboxSlashCommandsPatched")) {
          return false;
        }
        const wrappedSendChat = function wrappedQolboxSlashCommand(message, ...rest) {
          return options.handleSend({
            message,
            rest,
            session: this,
            sendNativeChat: (nextMessage) => callNativeChatSend(nativeSendChat, this, nextMessage, rest),
            sendNativeChatWithSettingsHelpCorrection: (nextMessage) => callNativeChatSendWithSettingsHelpCorrection(nativeSendChat, this, nextMessage, rest)
          });
        };
        setNativeReflectProperty(session, "CJ", wrappedSendChat);
        setNativeReflectProperty(session, "__qolboxSlashCommandsPatched", true);
        setNativeReflectProperty(session, "__qolboxSlashCommandsOriginalCJ", nativeSendChat);
        return true;
      }

      // src/features/slash-command-interceptor.ts
      function expandNativeChatAlias(message) {
        if (typeof message !== "string" || !areAdvancedCommandAliasesEnabled()) {
          return message;
        }
        return message.replace(/^(\s*)\/rec(?=\s|$)/i, "$1/record");
      }
      function installSlashCommandInterceptor(session, dependencies) {
        return installNativeChatSendInterceptor(session, {
          handleSend(nativeChat) {
            if (dependencies.areCommandsEnabled() && dependencies.handleCommand(nativeChat.message)) {
              return void 0;
            }
            const commandsEnabled = dependencies.areCommandsEnabled();
            let nextMessage = commandsEnabled ? expandNativeChatAlias(nativeChat.message) : nativeChat.message;
            if (commandsEnabled && dependencies.prepareNativeCommand) {
              const preparedMessage = dependencies.prepareNativeCommand(nextMessage);
              if (preparedMessage === null) {
                return void 0;
              }
              nextMessage = preparedMessage;
            }
            const isNativeHelp = commandsEnabled && /^\/help\s*$/.test(String(nextMessage || "").trim());
            const result = isNativeHelp ? nativeChat.sendNativeChatWithSettingsHelpCorrection(nextMessage) : nativeChat.sendNativeChat(nextMessage);
            if (isNativeHelp) {
              dependencies.showHelp(nativeChat.session);
            }
            return result;
          }
        });
      }

      // src/features/input-focus-feature-bundle.ts
      function createInputFocusFeatureBundle(options) {
        const { focusActiveRenderCanvas, resetBrowserScroll } = createRenderCanvasFocusController({
          focusElementWithoutScroll,
          getActiveRenderCanvas: options.getActiveRenderCanvas
        });
        const chatInput = createChatInputController({
          chatInputSelector: CHAT_INPUT_SELECTOR,
          lobbyChatInputSelector: ".lobbyContainer .chatBox .input",
          desktopLobbyChatPrompt: DESKTOP_LOBBY_CHAT_PROMPT,
          touchLobbyChatPrompt: TOUCH_LOBBY_CHAT_PROMPT,
          isChatFeatureEnabled: options.isChatFeatureEnabled,
          areLobbyCommandsEnabled: options.areLobbyCommandsEnabled,
          isTouchLobbyChatPrompt: isNativeTouchLobbyChatPrompt,
          focusActiveRenderCanvas,
          expandNativeChatAlias
        });
        const gameplayBackgroundFocus = createGameplayBackgroundFocusController({
          exclusionSelector: GAMEPLAY_FOCUS_EXCLUSION_SELECTOR,
          renderCanvasSelector: FULLSCREEN_RENDER_CANVAS_SELECTOR,
          renderLayerSelector: FULLSCREEN_RENDER_LAYER_SELECTOR,
          getActiveChatInput: chatInput.getActiveChatInput,
          getActiveRenderCanvas: options.getActiveRenderCanvas,
          isPlayingMatch: options.isPlayingMatch,
          isQolboxMenuClosed: options.isQolboxMenuClosed
        });
        return {
          ...chatInput,
          ...gameplayBackgroundFocus,
          focusActiveRenderCanvas,
          resetBrowserScroll
        };
      }

      // src/hitbox/player-appearance-adapter.ts
      var PLAYER_NAME_FIELDS = ["name", "Nm", "username", "playerName"];
      function getPlayerDisplayName(player) {
        if (!isNativeObject(player)) {
          return "";
        }
        for (const key of PLAYER_NAME_FIELDS) {
          const value = readNativeProperty(player, key);
          if (typeof value === "string" && value.trim()) {
            return value.trim();
          }
        }
        return "";
      }
      function getPlayerColorCandidates(player) {
        if (!isNativeObject(player)) {
          return [];
        }
        return Object.entries(player).filter(([key]) => /(colou?r|color|fill|tint)/i.test(key)).map(([, value]) => value);
      }

      // src/features/lobby-command-player-targets.ts
      var GROUP_TARGETS = /* @__PURE__ */ new Set(["all", "playing", "spectators"]);
      function normalizePlayerName(name) {
        return String(name || "").replace(/\s+/g, " ").trim().toLowerCase();
      }
      function formatCommandPlayerName(player) {
        const name = getPlayerName(player);
        return name ? String(name) : "Player";
      }
      function findPlayerByName(name, session = getMultiplayerSession()) {
        const query = normalizePlayerName(name);
        if (!query) {
          return { status: "missing", matches: [] };
        }
        const players = getSessionPlayers(session);
        const tiers = [
          players.filter(({ player }) => normalizePlayerName(getPlayerName(player)) === query),
          players.filter(({ player }) => normalizePlayerName(getPlayerName(player)).startsWith(query)),
          players.filter(({ player }) => normalizePlayerName(getPlayerName(player)).includes(query))
        ];
        for (const matches of tiers) {
          const uniqueMatches = [];
          const seenIds = /* @__PURE__ */ new Set();
          for (const match of matches) {
            const id = String(match.id);
            if (!seenIds.has(id)) {
              seenIds.add(id);
              uniqueMatches.push(match);
            }
          }
          if (uniqueMatches.length === 1) {
            return { status: "found", match: uniqueMatches[0], matches: uniqueMatches };
          }
          if (uniqueMatches.length > 1) {
            return { status: "ambiguous", matches: uniqueMatches };
          }
        }
        return { status: "missing", matches: [] };
      }
      function parseCommandTarget(argument) {
        const value = String(argument || "").trim();
        const quotedMatch = value.match(/^(["'])(.*)\1$/);
        const normalizedValue = normalizePlayerName(value);
        if (!quotedMatch && GROUP_TARGETS.has(normalizedValue)) {
          return { group: normalizedValue, type: "group", value };
        }
        return { type: "player", value: quotedMatch ? quotedMatch[2] : value };
      }

      // src/hitbox/team-state.ts
      var TEAM_STATE_SPECTATE = 0;
      var TEAM_STATE_FFA = 1;
      var TEAM_STATE_RED = 2;
      var TEAM_STATE_BLUE = 3;

      // src/hitbox/lobby-actions.ts
      function getLobbySocket(session) {
        return readNativePath(session, ["JD", "ZD"]);
      }
      function getCommandEventId() {
        const eventId = readNativeProperty(window.a8, "VP");
        return eventId === void 0 ? 1 : eventId;
      }
      function getCommandId(property, fallback) {
        const commandId = readNativeProperty(window.a8, property);
        return commandId === void 0 ? fallback : commandId;
      }
      function emitLobbyCommand(session, payload) {
        const socket = getLobbySocket(session);
        const emit = readNativeProperty(socket, "emit");
        if (!isNativeObject(socket) || typeof emit !== "function") {
          return false;
        }
        Reflect.apply(emit, socket, [getCommandEventId(), [...payload]]);
        return true;
      }
      function requestOwnTeamChange(session, team) {
        return emitLobbyCommand(session, [getCommandId("gE", 24), team]);
      }
      function movePlayerToTeam(session, playerId, team) {
        return emitLobbyCommand(session, [getCommandId("jE", 47), { i: playerId, t: team }]);
      }
      function setTeamsLocked(session, locked) {
        return emitLobbyCommand(session, [getCommandId("HE", 52), Boolean(locked)]);
      }
      function giveHostToPlayer(session, playerId) {
        return emitLobbyCommand(session, [getCommandId("qolboxGiveHost", 44), playerId]);
      }
      function banPlayer(session, playerId) {
        return emitLobbyCommand(session, [getCommandId("CE", 32), { id: playerId, ban: 1 }]);
      }

      // src/features/lobby-command-team-targets.ts
      function getBulkTeamTargets(team, session = getMultiplayerSession(), targetGroup = "all") {
        return getSessionPlayers(session).filter(({ player }) => {
          const currentTeam = getPlayerTeamState(player);
          if (currentTeam === Number(team)) {
            return false;
          }
          if (targetGroup === "playing" && currentTeam === TEAM_STATE_SPECTATE) {
            return false;
          }
          if (targetGroup === "spectators" && currentTeam !== TEAM_STATE_SPECTATE) {
            return false;
          }
          if (team === TEAM_STATE_SPECTATE) {
            return currentTeam !== TEAM_STATE_SPECTATE;
          }
          if (team === TEAM_STATE_FFA) {
            return currentTeam === TEAM_STATE_SPECTATE;
          }
          return currentTeam === TEAM_STATE_SPECTATE || currentTeam === TEAM_STATE_RED || currentTeam === TEAM_STATE_BLUE;
        });
      }
      function getSwitchableTeamPlayers(session = getMultiplayerSession()) {
        return getSessionPlayers(session).filter(({ player }) => {
          const team = getPlayerTeamState(player);
          return team === TEAM_STATE_RED || team === TEAM_STATE_BLUE;
        });
      }

      // src/features/lobby-command-team-state-text.ts
      function getTeamStateName(team) {
        switch (Number(team)) {
          case TEAM_STATE_SPECTATE:
            return "spectator";
          case TEAM_STATE_RED:
            return "red";
          case TEAM_STATE_BLUE:
            return "blue";
          case TEAM_STATE_FFA:
          default:
            return "playing";
        }
      }
      function getBulkTeamActionName(team) {
        if (team === TEAM_STATE_SPECTATE) {
          return "spectate";
        }
        if (team === TEAM_STATE_FFA) {
          return "join";
        }
        return `move to ${getTeamStateName(team)}`;
      }
      function formatBulkTeamMoveMessage(moved, team) {
        if (team === TEAM_STATE_FFA) {
          return `Moving ${moved} eligible player${moved === 1 ? "" : "s"} into play.`;
        }
        return `Moving ${moved} eligible player${moved === 1 ? "" : "s"} to ${getTeamStateName(team)}.`;
      }

      // src/features/lobby-command-team-state-request.ts
      function requestPlayerTeamState(session, playerId, team, localPlayerId = getLocalPlayerId(session)) {
        return isSamePlayerId(playerId, localPlayerId) ? requestOwnTeamChange(session, team) : movePlayerToTeam(session, playerId, team);
      }

      // src/features/lobby-command-team-actions.ts
      var SWITCH_SETTLE_MS = 900;
      function createLobbyCommandTeamActions(dependencies) {
        let switchLockedUntil = 0;
        let switchUnlockTimer = 0;
        function isSwitchingTeams() {
          return Date.now() < switchLockedUntil;
        }
        function lockSwitchOperation() {
          switchLockedUntil = Date.now() + SWITCH_SETTLE_MS;
          if (switchUnlockTimer) {
            window.clearTimeout(switchUnlockTimer);
          }
          switchUnlockTimer = window.setTimeout(() => {
            switchUnlockTimer = 0;
            if (Date.now() >= switchLockedUntil) {
              switchLockedUntil = 0;
            }
          }, SWITCH_SETTLE_MS + 50);
        }
        function requestTeamState(playerId, team, { requireTeamMode = false } = {}) {
          const session = getMultiplayerSession();
          if (!hasLobbyPlayerState(session)) {
            dependencies.showStatus("No active lobby or game session.");
            return false;
          }
          if (requireTeamMode && !dependencies.isTeamMode(session)) {
            dependencies.showStatus(`${getTeamStateName(team)} is only available in team modes.`);
            return false;
          }
          const player = getSessionPlayerById(session, playerId);
          if (!player) {
            dependencies.showStatus("Could not find that player.");
            return false;
          }
          if (getPlayerTeamState(player) === team) {
            dependencies.showStatus(`${formatCommandPlayerName(player)} is already ${getTeamStateName(team)}.`);
            return true;
          }
          const localPlayerId = getLocalPlayerId(session);
          if (isSamePlayerId(playerId, localPlayerId)) {
            if (team !== TEAM_STATE_SPECTATE && isSessionMatchActive(session) && dependencies.isCurrentPlayerSpectating(session)) {
              dependencies.noteLocallyInitiatedPlayTransition(session);
            }
            if (!requestPlayerTeamState(session, playerId, team, localPlayerId)) {
              dependencies.showStatus("Could not send the team change command.");
              return false;
            }
            return true;
          }
          if (!isHostSession(session)) {
            dependencies.showStatus("Only the host can move other players between teams.");
            return false;
          }
          if (!requestPlayerTeamState(session, playerId, team, localPlayerId)) {
            dependencies.showStatus("Could not send the team move command.");
            return false;
          }
          return true;
        }
        function requestBulkTeamState(team, { requireTeamMode = false, targetGroup = "all" } = {}) {
          const session = getMultiplayerSession();
          if (!hasLobbyPlayerState(session)) {
            dependencies.showStatus("No active lobby or game session.");
            return false;
          }
          if (requireTeamMode && !dependencies.isTeamMode(session)) {
            dependencies.showStatus(`${getTeamStateName(team)} is only available in team modes.`);
            return false;
          }
          const players = getBulkTeamTargets(team, session, targetGroup);
          if (!players.length) {
            dependencies.showStatus(`No eligible players need to ${getBulkTeamActionName(team)}.`);
            return true;
          }
          const localPlayerId = getLocalPlayerId(session);
          if (!isHostSession(session) && players.some(({ id }) => !isSamePlayerId(id, localPlayerId))) {
            dependencies.showStatus("Only the host can move other players.");
            return false;
          }
          let moved = 0;
          for (const { id } of players) {
            if (isSamePlayerId(id, localPlayerId) && team !== TEAM_STATE_SPECTATE && isSessionMatchActive(session) && dependencies.isCurrentPlayerSpectating(session)) {
              dependencies.noteLocallyInitiatedPlayTransition(session);
            }
            if (requestPlayerTeamState(session, id, team, localPlayerId)) {
              moved += 1;
            }
          }
          if (moved !== players.length) {
            dependencies.showStatus("Could not move every eligible player.");
            return false;
          }
          dependencies.showStatus(formatBulkTeamMoveMessage(moved, team));
          return true;
        }
        function switchTeamPlayers() {
          const session = getMultiplayerSession();
          if (!hasLobbyPlayerState(session)) {
            dependencies.showStatus("No active lobby or game session.");
            return false;
          }
          if (!dependencies.isTeamMode(session)) {
            dependencies.showStatus("SWITCH is only available in team modes.");
            return false;
          }
          if (!isHostSession(session)) {
            dependencies.showStatus("Only the host can switch teams.");
            return false;
          }
          if (isSwitchingTeams()) {
            dependencies.showStatus("Team switch is still settling.");
            return false;
          }
          const localPlayerId = getLocalPlayerId(session);
          const players = getSwitchableTeamPlayers(session);
          if (!players.length) {
            dependencies.showStatus("There are no red or blue players to switch.");
            return false;
          }
          let moved = 0;
          let failed = 0;
          const switchTargets = players.map(({ id, player }) => ({
            id,
            nextTeam: getPlayerTeamState(player) === TEAM_STATE_RED ? TEAM_STATE_BLUE : TEAM_STATE_RED
          }));
          lockSwitchOperation();
          for (const { id, nextTeam } of switchTargets) {
            if (requestPlayerTeamState(session, id, nextTeam, localPlayerId)) {
              moved += 1;
            } else {
              failed += 1;
            }
          }
          if (failed) {
            dependencies.showStatus("Could not switch every player.");
            return false;
          }
          dependencies.showStatus(`Switching ${moved} player${moved === 1 ? "" : "s"} between red and blue.`);
          return true;
        }
        function setTeamsLocked2(locked) {
          const session = getMultiplayerSession();
          if (!hasLobbyPlayerState(session)) {
            dependencies.showStatus("No active lobby or game session.");
            return false;
          }
          if (!isHostSession(session)) {
            dependencies.showStatus(`Only the host can ${locked ? "lock" : "unlock"} teams.`);
            return false;
          }
          if (isTeamsLocked(session) === locked) {
            dependencies.showStatus(`Teams are already ${locked ? "locked" : "unlocked"}.`);
            return true;
          }
          if (!setTeamsLocked(session, locked)) {
            dependencies.showStatus("Could not send the team lock/unlock command.");
            return false;
          }
          return true;
        }
        return {
          isSwitchingTeams,
          requestBulkTeamState,
          requestTeamState,
          setTeamsLocked: setTeamsLocked2,
          switchTeamPlayers
        };
      }

      // src/features/lobby-command-player-resolver.ts
      function formatPlayerNameMatches(matches, getPlayerDisplayName2) {
        return matches.map(({ player }) => getPlayerDisplayName2(player) || "Unnamed Player").filter(Boolean).slice(0, 4).join(", ");
      }
      function createCommandPlayerResolver(options) {
        function resolveNamedCommandPlayer(argument, session = getMultiplayerSession()) {
          const result = findPlayerByName(argument, session);
          if (result.status === "missing") {
            options.showStatus(`Couldn't find player '${argument}'.`);
            return null;
          }
          if (result.status === "ambiguous") {
            const matches = formatPlayerNameMatches(result.matches, options.getPlayerDisplayName);
            options.showStatus(`Player name '${argument}' is ambiguous${matches ? `: ${matches}` : ""}.`);
            return null;
          }
          return result.match;
        }
        return {
          resolveNamedCommandPlayer
        };
      }

      // src/features/lobby-command-host-actions.ts
      function createLobbyCommandHostActions(dependencies) {
        function handleHostSlashCommand(argument) {
          const session = getMultiplayerSession();
          if (!hasLobbyPlayerState(session)) {
            dependencies.showStatus("No active lobby or game session.");
            return false;
          }
          if (!argument) {
            dependencies.showStatus("Usage: /host playername");
            return false;
          }
          if (!isHostSession(session)) {
            dependencies.showStatus("Only the host can transfer host to another player.");
            return false;
          }
          const target = dependencies.resolveNamedCommandPlayer(argument, session);
          if (!target) {
            return false;
          }
          if (isSamePlayerId(target.id, getLocalPlayerId(session))) {
            dependencies.showStatus("You are already host.");
            return true;
          }
          if (!giveHostToPlayer(session, target.id)) {
            dependencies.showStatus("Could not send the host transfer command.");
            return false;
          }
          dependencies.showStatus(`Giving host to ${formatCommandPlayerName(target.player)}.`);
          return true;
        }
        return {
          handleHostSlashCommand
        };
      }

      // src/hitbox/chat-adapter.ts
      function canWriteChatLine(session) {
        return hasNativeMethod(session, "vG");
      }
      function writeChatLine(session, line) {
        return callNativeMethod(session, "vG", [line]).called;
      }

      // src/hitbox/host-settings-adapter.ts
      var EXTRA_HOST_SETTINGS = [
        ["bbPower", "it"],
        ["bbRange", "st"],
        ["bbAngleVariance", "ht"],
        ["bbFireOn", "nt"],
        ["bbFireFramesLength", "at"],
        ["bbHideAfterFireFrames", "lt"],
        ["bbResetOn", "ut"],
        ["bbInitAmmoCost", "ot"],
        ["bbHoldAmmoCost", "rt"],
        ["egEnabled", "Ot"],
        ["egSize", "Rt"],
        ["egAge", "Dt"],
        ["egGravityScale", "Lt"],
        ["egRestitution", "Ut"],
        ["egExplodeRadius", "jt"],
        ["egStartSpin", "Wt"],
        ["egMaxThrowPower", "Jt"],
        ["egAmmoNeeded", "Gt"],
        ["egDelay1", "Ht"],
        ["egDelay2", "zt"],
        ["egDelayBeforeAmmoUse", "Yt"],
        ["egAimRate", "qt"],
        ["egShape", "Vt"]
      ];
      function getHostSettingsObject(session) {
        return readNativePath(session, ["JD", "$L"]) || readNativePath(session, ["KR", "uL", "settings", 0]) || readNativePath(session, ["TJ", "JD", "tP", 0, "state", "settings", 0]) || readNativePath(session, ["JD", "tP", 0, "state", "settings", 0]) || null;
      }
      function readAllHostSettingLines(session) {
        const settings = getHostSettingsObject(session);
        if (!isNativeObject(settings)) {
          return null;
        }
        const nativeResult = callNativeMethod(settings, "pi");
        if (!nativeResult.called || !Array.isArray(nativeResult.result) || !nativeResult.result.every((line) => typeof line === "string")) {
          return null;
        }
        const lines = nativeResult.result.slice();
        if (lines[lines.length - 1] === "===") {
          lines.pop();
        }
        for (const [name, field] of EXTRA_HOST_SETTINGS) {
          const value = readNativeProperty(settings, field);
          if (value !== void 0) {
            lines.push(`${name}: ${String(value)}`);
          }
        }
        lines.push("===");
        return lines;
      }

      // src/features/lobby-command-help.ts
      function getQolboxCommandHelpLines() {
        return [
          "QOLBox commands:",
          "/spec -- move yourself to spectators",
          "/spec playername -- move a player to spectators",
          "/spec all|playing -- move active players to spectators",
          "/join -- move yourself into play (non-team modes)",
          "/join playername -- move a player into play (non-team modes)",
          "/join all|spectators -- move spectators into play (non-team modes)",
          "/red -- move yourself to red (team modes)",
          "/red playername -- move a player to red (team modes)",
          "/red all|playing|spectators -- move players to red (team modes)",
          "/blue -- move yourself to blue (team modes)",
          "/blue playername -- move a player to blue (team modes)",
          "/blue all|playing|spectators -- move players to blue (team modes)",
          "/switch -- swap red and blue teams",
          "/lock -- lock team switching",
          "/unlock -- unlock team switching",
          "/host playername -- give host to a player",
          "/blacklist playername -- add an exact name to automatic host bans",
          "/blacklist -- show blacklisted names",
          "/blacklist remove playername -- remove a blacklisted name",
          "/blacklist clear|on|off -- manage the blacklist",
          "/start -- start the game",
          "/end -- end the current game",
          "/restart -- end and start a new game",
          ...areAdvancedCommandAliasesEnabled() ? ["/r -- same as /restart"] : [],
          "/record -- record the current replay",
          ...areAdvancedCommandAliasesEnabled() ? ["/rec -- same as /record"] : [],
          "/settings -- view normal gameplay settings",
          "/settings all -- view normal and hidden gameplay settings",
          "Named targets for /spec, /join, /red, /blue, /host, /kick, and /ban accept exact or unique partial player names.",
          'Tip: all, playing, and spectators are special targets where shown above. Quote them to use them as player names: /spec "all".',
          'Tip: quote blacklist names like "clear", "on", or "off" to add those exact names.'
        ];
      }
      function writeQolboxCommandHelp(session) {
        for (const line of getQolboxCommandHelpLines()) {
          writeChatLine(session, line);
        }
      }

      // src/features/lobby-command-output-actions.ts
      function createLobbyCommandOutputActions(dependencies) {
        function showAllHostSettings() {
          const session = getMultiplayerSession();
          const lines = readAllHostSettingLines(session);
          if (!lines || !canWriteChatLine(session)) {
            dependencies.showStatus("Could not read the current host settings.", session);
            return false;
          }
          lines.forEach((line) => writeChatLine(session, line));
          return true;
        }
        function showQolboxCommandHelp(session = getMultiplayerSession()) {
          writeQolboxCommandHelp(session);
        }
        return { showAllHostSettings, showQolboxCommandHelp };
      }

      // src/features/lobby-command-play-actions.ts
      function createLobbyCommandPlayActions(dependencies) {
        function handleJoinSlashCommand(argument) {
          const session = getMultiplayerSession();
          if (!hasLobbyPlayerState(session)) {
            dependencies.showStatus("No active lobby or game session.");
            return false;
          }
          if (dependencies.isTeamMode(session)) {
            dependencies.showStatus("Use /red or /blue to join in team modes.");
            return false;
          }
          const targetArgument = parseCommandTarget(argument);
          if (targetArgument.type === "group") {
            return dependencies.requestBulkTeamState(TEAM_STATE_FFA, { targetGroup: targetArgument.group });
          }
          const target = argument ? dependencies.resolveNamedCommandPlayer(targetArgument.value, session) : { id: getLocalPlayerId(session), player: null };
          if (!target) {
            return false;
          }
          const player = getSessionPlayerById(session, target.id);
          if (player && getPlayerTeamState(player) === TEAM_STATE_FFA) {
            dependencies.showStatus(`${formatCommandPlayerName(player)} is already playing.`);
            return true;
          }
          return dependencies.requestTeamState(target.id, TEAM_STATE_FFA);
        }
        function handleSpecSlashCommand(argument) {
          const session = getMultiplayerSession();
          const targetArgument = parseCommandTarget(argument);
          if (targetArgument.type === "group") {
            return dependencies.requestBulkTeamState(TEAM_STATE_SPECTATE, { targetGroup: targetArgument.group });
          }
          const target = argument ? dependencies.resolveNamedCommandPlayer(targetArgument.value, session) : { id: getLocalPlayerId(session), player: null };
          return target ? dependencies.requestTeamState(target.id, TEAM_STATE_SPECTATE) : false;
        }
        return {
          handleJoinSlashCommand,
          handleSpecSlashCommand
        };
      }

      // src/features/lobby-command-actions.ts
      function createLobbyCommandActions(dependencies) {
        const teamActions = createLobbyCommandTeamActions({
          isCurrentPlayerSpectating: dependencies.isCurrentPlayerSpectating,
          isTeamMode: dependencies.isTeamMode,
          noteLocallyInitiatedPlayTransition: dependencies.noteLocallyInitiatedPlayTransition,
          showStatus: dependencies.showStatus
        });
        const playerResolver = createCommandPlayerResolver({
          getPlayerDisplayName: dependencies.getPlayerDisplayName,
          showStatus: dependencies.showStatus
        });
        const outputActions = createLobbyCommandOutputActions({
          showStatus: dependencies.showStatus
        });
        const hostActions = createLobbyCommandHostActions({
          resolveNamedCommandPlayer: playerResolver.resolveNamedCommandPlayer,
          showStatus: dependencies.showStatus
        });
        const playActions = createLobbyCommandPlayActions({
          isTeamMode: dependencies.isTeamMode,
          requestBulkTeamState,
          requestTeamState,
          resolveNamedCommandPlayer: playerResolver.resolveNamedCommandPlayer,
          showStatus: dependencies.showStatus
        });
        function requestTeamState(playerId, team, options = {}) {
          return teamActions.requestTeamState(playerId, team, options);
        }
        function requestBulkTeamState(team, options = {}) {
          return teamActions.requestBulkTeamState(team, options);
        }
        function switchTeamPlayers() {
          return teamActions.switchTeamPlayers();
        }
        function setTeamsLocked2(locked) {
          return teamActions.setTeamsLocked(locked);
        }
        function isSwitchingTeams() {
          return teamActions.isSwitchingTeams();
        }
        function handleTeamSlashCommand(commandName, argument) {
          const session = getMultiplayerSession();
          const targetTeam = commandName === "/blue" ? TEAM_STATE_BLUE : TEAM_STATE_RED;
          if (!argument) {
            return requestTeamState(getLocalPlayerId(session), targetTeam, { requireTeamMode: true });
          }
          const targetArgument = parseCommandTarget(argument);
          if (targetArgument.type === "group") {
            return requestBulkTeamState(targetTeam, { requireTeamMode: true, targetGroup: targetArgument.group });
          }
          if (!dependencies.isTeamMode(session)) {
            dependencies.showStatus(`${getTeamStateName(targetTeam)} is only available in team modes.`);
            return false;
          }
          const target = playerResolver.resolveNamedCommandPlayer(targetArgument.value, session);
          return target ? requestTeamState(target.id, targetTeam, { requireTeamMode: true }) : false;
        }
        function handleJoinSlashCommand(argument) {
          return playActions.handleJoinSlashCommand(argument);
        }
        function handleSpecSlashCommand(argument) {
          return playActions.handleSpecSlashCommand(argument);
        }
        return {
          findPlayerByName,
          handleHostSlashCommand: hostActions.handleHostSlashCommand,
          handleJoinSlashCommand,
          handleSpecSlashCommand,
          handleTeamSlashCommand,
          normalizePlayerName,
          requestBulkTeamState,
          requestTeamState,
          setTeamsLocked: setTeamsLocked2,
          showAllHostSettings: outputActions.showAllHostSettings,
          showQolboxCommandHelp: outputActions.showQolboxCommandHelp,
          isSwitchingTeams,
          switchTeamPlayers
        };
      }

      // src/hitbox/match-actions.ts
      function canEndMatch(session) {
        return hasNativeMethod(session, "PJ");
      }
      function endMatch(session) {
        return callNativeMethod(session, "PJ").called;
      }
      function canStartMatch(session) {
        return hasNativeMethod(session, "_J");
      }
      function startMatch(session) {
        return callNativeMethod(session, "_J").called;
      }

      // src/features/lobby-command-dispatcher.ts
      function hasTextValue(value) {
        return typeof value === "object" && value !== null && "value" in value && typeof value.value === "string";
      }
      function clearHandledChatDraft() {
        for (const input of document.querySelectorAll(".inGameChat .input, .lobbyContainer .chatBox .input")) {
          if (hasTextValue(input)) {
            input.value = "";
          }
        }
      }
      function createLobbyCommandDispatcher(dependencies) {
        function endCurrentGame() {
          const session = getMultiplayerSession();
          if (!isSessionMatchActive(session)) {
            dependencies.showStatus("There is no active game to end.");
            return false;
          }
          if (!isHostSession(session)) {
            dependencies.showStatus("Only the host can end the current game.");
            return false;
          }
          if (!canEndMatch(session)) {
            dependencies.showStatus("The game's end-game action is unavailable.");
            return false;
          }
          clearHandledChatDraft();
          endMatch(session);
          return true;
        }
        function restartCurrentGame() {
          const session = getMultiplayerSession();
          if (!isSessionMatchActive(session)) {
            dependencies.showStatus("There is no active game to restart.");
            return false;
          }
          if (!isHostSession(session)) {
            dependencies.showStatus("Only the host can restart the current game.");
            return false;
          }
          if (!canEndMatch(session) || !canStartMatch(session)) {
            dependencies.showStatus("The game's restart actions are unavailable.");
            return false;
          }
          if (dependencies.areGameStartAlertsEnabled()) {
            dependencies.installStartAlertHooks(session);
          }
          clearHandledChatDraft();
          endMatch(session);
          dependencies.noteLocallyInitiatedPlayTransition(session);
          startMatch(session);
          return true;
        }
        function startCurrentGame() {
          const session = getMultiplayerSession();
          if (isSessionMatchActive(session)) {
            dependencies.showStatus("There is already an active game.");
            return false;
          }
          if (!isHostSession(session)) {
            dependencies.showStatus("Only the host can start the game.");
            return false;
          }
          if (!canStartMatch(session)) {
            dependencies.showStatus("The game's start-game action is unavailable.");
            return false;
          }
          if (dependencies.areGameStartAlertsEnabled()) {
            dependencies.installStartAlertHooks(session);
          }
          clearHandledChatDraft();
          dependencies.noteLocallyInitiatedPlayTransition(session);
          startMatch(session);
          return true;
        }
        function handleQolboxSlashCommand(message) {
          const text = String(message || "").trim();
          const match = text.match(/^\/(switch|lock|unlock|spec|red|blue|join|host|start|end|restart|r|settings|blacklist)(?:\s+(.+))?$/i);
          if (!match) {
            return false;
          }
          const commandName = `/${match[1].toLowerCase()}`;
          const argument = (match[2] || "").trim();
          if (commandName === "/r" && !areAdvancedCommandAliasesEnabled()) {
            return false;
          }
          if (commandName === "/switch") {
            if (argument) {
              dependencies.showStatus("/switch does not take a player name.");
              return true;
            }
            dependencies.actions.switchTeamPlayers();
            return true;
          }
          if (commandName === "/lock" || commandName === "/unlock") {
            if (argument) {
              dependencies.showStatus(`${commandName} does not take an argument.`);
              return true;
            }
            dependencies.actions.setTeamsLocked(commandName === "/lock");
            return true;
          }
          if (commandName === "/spec") {
            dependencies.actions.handleSpecSlashCommand(argument);
            return true;
          }
          if (commandName === "/join") {
            dependencies.actions.handleJoinSlashCommand(argument);
            return true;
          }
          if (commandName === "/host") {
            dependencies.actions.handleHostSlashCommand(argument);
            return true;
          }
          if (commandName === "/blacklist") {
            dependencies.handleBlacklistSlashCommand(argument);
            return true;
          }
          if (commandName === "/end") {
            if (argument) {
              dependencies.showStatus("/end does not take an argument.");
              return true;
            }
            endCurrentGame();
            return true;
          }
          if (commandName === "/start") {
            if (argument) {
              dependencies.showStatus("/start does not take an argument.");
              return true;
            }
            startCurrentGame();
            return true;
          }
          if (commandName === "/restart" || commandName === "/r") {
            if (argument) {
              dependencies.showStatus(`${commandName} does not take an argument.`);
              return true;
            }
            restartCurrentGame();
            return true;
          }
          if (commandName === "/settings") {
            if (dependencies.actions.normalizePlayerName(argument) !== "all") {
              return false;
            }
            dependencies.actions.showAllHostSettings();
            return true;
          }
          dependencies.actions.handleTeamSlashCommand(commandName, argument);
          return true;
        }
        return { endCurrentGame, handleQolboxSlashCommand, restartCurrentGame, startCurrentGame };
      }

      // src/features/qolbox-chat-status.ts
      function createQolboxChatStatusWriter(options) {
        function showQolboxChatStatus(message, session = options.getSession()) {
          writeChatLine(session, `* ${message}`);
        }
        return {
          showQolboxChatStatus
        };
      }

      // src/hitbox/player-join-hooks.ts
      var PLAYER_JOIN_HOOK_MARKER = "__qolboxPlayerJoinHookInstalled";
      function installPlayerJoinHook(session, onPlayerJoined) {
        if (!isNativeObject(session) || readNativeProperty(session, PLAYER_JOIN_HOOK_MARKER)) {
          return false;
        }
        const nativePlayerJoined = readNativeProperty(session, "VW");
        if (typeof nativePlayerJoined !== "function") {
          return false;
        }
        const wrappedPlayerJoined = function wrappedQolboxPlayerJoined(...args) {
          const result = Reflect.apply(nativePlayerJoined, this, args);
          window.setTimeout(() => onPlayerJoined(this), 0);
          return result;
        };
        setNativeReflectProperty(session, "VW", wrappedPlayerJoined);
        setNativeReflectProperty(session, PLAYER_JOIN_HOOK_MARKER, true);
        return true;
      }

      // src/settings/blacklist-storage.ts
      var BLACKLIST_STORAGE_KEY = "vm.hitbox.qolboxBlacklist.v1";
      var MAX_BLACKLIST_ENTRIES = 200;
      function normalizeStoredName(name) {
        return String(name || "").replace(/\s+/g, " ").trim().toLowerCase();
      }
      function sanitizeBlacklistNames(value) {
        if (!Array.isArray(value)) {
          return [];
        }
        const names = [];
        const seen = /* @__PURE__ */ new Set();
        for (const rawName of value) {
          const name = String(rawName || "").replace(/\s+/g, " ").trim();
          const normalizedName = normalizeStoredName(name);
          if (!normalizedName || seen.has(normalizedName)) {
            continue;
          }
          seen.add(normalizedName);
          names.push(name);
          if (names.length >= MAX_BLACKLIST_ENTRIES) {
            break;
          }
        }
        return names;
      }
      function loadBlacklistNames() {
        try {
          return sanitizeBlacklistNames(JSON.parse(localStorage.getItem(BLACKLIST_STORAGE_KEY) || "[]"));
        } catch {
          return [];
        }
      }
      function saveBlacklistNames(names) {
        const sanitizedNames = sanitizeBlacklistNames(names);
        try {
          localStorage.setItem(BLACKLIST_STORAGE_KEY, JSON.stringify(sanitizedNames));
        } catch {
        }
        return sanitizedNames;
      }

      // src/features/lobby-blacklist.ts
      function parseQuotedName(value) {
        const trimmed = value.trim();
        const match = trimmed.match(/^(["'])(.*)\1$/);
        return {
          quoted: Boolean(match),
          value: (match ? match[2] : trimmed).replace(/\s+/g, " ").trim()
        };
      }
      function findStoredName(names, query) {
        const normalizedQuery = normalizePlayerName(query);
        if (!normalizedQuery) {
          return { matches: [], status: "missing" };
        }
        const tiers = [
          names.filter((name) => normalizePlayerName(name) === normalizedQuery),
          names.filter((name) => normalizePlayerName(name).startsWith(normalizedQuery)),
          names.filter((name) => normalizePlayerName(name).includes(normalizedQuery))
        ];
        for (const matches of tiers) {
          if (matches.length === 1) {
            return { matches, status: "found" };
          }
          if (matches.length > 1) {
            return { matches, status: "ambiguous" };
          }
        }
        return { matches: [], status: "missing" };
      }
      function getUniqueCurrentPlayerNames() {
        const names = [];
        const seen = /* @__PURE__ */ new Set();
        for (const { player } of getSessionPlayers()) {
          const name = String(getPlayerName(player) || "").replace(/\s+/g, " ").trim();
          const normalizedName = normalizePlayerName(name);
          if (!normalizedName || seen.has(normalizedName)) {
            continue;
          }
          seen.add(normalizedName);
          names.push(name);
        }
        return names;
      }
      function findCurrentPlayerName(requestedName) {
        const normalizedRequest = normalizePlayerName(requestedName);
        if (!normalizedRequest) {
          return { match: null, partialMatches: [] };
        }
        const names = getUniqueCurrentPlayerNames();
        const exactMatch = names.find((name) => normalizePlayerName(name) === normalizedRequest) || null;
        if (exactMatch) {
          return { match: exactMatch, partialMatches: [] };
        }
        const partialMatches = names.filter((name) => normalizePlayerName(name).includes(normalizedRequest)).slice(0, 4);
        return { match: null, partialMatches };
      }
      function getQuotedCommandExample(name) {
        return name.includes('"') ? `/blacklist '${name}'` : `/blacklist "${name}"`;
      }
      function getPartialCurrentPlayerMessage(requestedName, matches) {
        const matchText = matches.join(", ");
        return `Blacklist uses exact names. '${requestedName}' partially matches ${matchText}. Type the full player name or use ${getQuotedCommandExample(requestedName)} to add exactly '${requestedName}'.`;
      }
      function createLobbyBlacklistController(options) {
        let blacklistNames = loadBlacklistNames();
        let hookTarget = null;
        let attemptedSession = null;
        let attemptedPlayers = /* @__PURE__ */ new Set();
        function saveNames(nextNames) {
          blacklistNames = saveBlacklistNames(nextNames);
        }
        function getBlacklistNameMap() {
          return new Map(blacklistNames.map((name) => [normalizePlayerName(name), name]));
        }
        function resetAttemptsForSession(session) {
          if (attemptedSession === session) {
            return;
          }
          attemptedSession = session;
          attemptedPlayers = /* @__PURE__ */ new Set();
        }
        function enforceBlacklist(session = getMultiplayerSession()) {
          if (!options.areLobbyCommandsEnabled() || !options.isEnforcementEnabled() || !hasLobbyPlayerState(session) || !isHostSession(session)) {
            return 0;
          }
          resetAttemptsForSession(session);
          const namesByNormalizedName = getBlacklistNameMap();
          if (!namesByNormalizedName.size) {
            return 0;
          }
          const localPlayerId = getLocalPlayerId(session);
          let banned = 0;
          for (const { id, player } of getSessionPlayers(session)) {
            if (isSamePlayerId(id, localPlayerId)) {
              continue;
            }
            const playerName = String(getPlayerName(player) || "").trim();
            const normalizedName = normalizePlayerName(playerName);
            const attemptKey = `${String(id)}\0${normalizedName}`;
            if (!namesByNormalizedName.has(normalizedName) || attemptedPlayers.has(attemptKey)) {
              continue;
            }
            attemptedPlayers.add(attemptKey);
            if (banPlayer(session, id)) {
              banned += 1;
              options.showStatus(`Banned blacklisted player ${playerName || "Player"}.`, session);
            } else {
              attemptedPlayers.delete(attemptKey);
              options.showStatus(`Could not ban blacklisted player ${playerName || "Player"}.`, session);
            }
          }
          return banned;
        }
        function installBlacklistHook(session = getMultiplayerSession()) {
          if (!session || session === hookTarget) {
            return false;
          }
          if (installPlayerJoinHook(session, (joinedSession) => enforceBlacklist(joinedSession))) {
            hookTarget = session;
            return true;
          }
          return false;
        }
        function patchLobbyBlacklist() {
          const session = getMultiplayerSession();
          installBlacklistHook(session);
          enforceBlacklist(session);
        }
        function showBlacklist() {
          if (!blacklistNames.length) {
            options.showStatus("The blacklist is empty. Usage: /blacklist playername");
            return true;
          }
          options.showStatus(`Blacklisted names (${blacklistNames.length}):`);
          blacklistNames.forEach((name) => options.showStatus(`- ${name}`));
          return true;
        }
        function addBlacklistName(rawName) {
          const parsedName = parseQuotedName(rawName);
          const requestedName = parsedName.value;
          if (!requestedName) {
            options.showStatus("Usage: /blacklist playername");
            return false;
          }
          const currentPlayerName = parsedName.quoted ? { match: null, partialMatches: [] } : findCurrentPlayerName(requestedName);
          if (currentPlayerName.partialMatches.length) {
            options.showStatus(getPartialCurrentPlayerMessage(requestedName, currentPlayerName.partialMatches));
            return false;
          }
          const exactName = currentPlayerName.match || requestedName;
          if (blacklistNames.some((name) => normalizePlayerName(name) === normalizePlayerName(exactName))) {
            options.showStatus(`${exactName} is already blacklisted.`);
            return true;
          }
          if (blacklistNames.length >= MAX_BLACKLIST_ENTRIES) {
            options.showStatus(`The blacklist is full (${MAX_BLACKLIST_ENTRIES} names). Remove a name before adding another.`);
            return false;
          }
          saveNames([...blacklistNames, exactName]);
          options.showStatus(`Added ${exactName} to the blacklist.`);
          if (!options.isEnforcementEnabled()) {
            options.showStatus("Automatic blacklist is off.");
          } else if (!isHostSession()) {
            options.showStatus("Automatic bans will apply when you are host.");
          }
          patchLobbyBlacklist();
          return true;
        }
        function removeBlacklistName(rawName) {
          const requestedName = parseQuotedName(rawName).value;
          if (!requestedName) {
            options.showStatus("Usage: /blacklist remove playername");
            return false;
          }
          const result = findStoredName(blacklistNames, requestedName);
          if (result.status === "missing") {
            options.showStatus(`Couldn't find '${requestedName}' in the blacklist.`);
            return false;
          }
          if (result.status === "ambiguous") {
            options.showStatus(`Blacklist name '${requestedName}' is ambiguous: ${result.matches.slice(0, 4).join(", ")}.`);
            return false;
          }
          const removedName = result.matches[0];
          saveNames(blacklistNames.filter((name) => normalizePlayerName(name) !== normalizePlayerName(removedName)));
          options.showStatus(`Removed ${removedName} from the blacklist.`);
          return true;
        }
        function clearBlacklist() {
          const removedCount = blacklistNames.length;
          saveNames([]);
          options.showStatus(
            removedCount ? `Cleared ${removedCount} ${removedCount === 1 ? "name" : "names"} from the blacklist.` : "The blacklist is already empty."
          );
          return true;
        }
        function setBlacklistEnforcement(enabled) {
          if (options.isEnforcementEnabled() === enabled) {
            options.showStatus(`Automatic blacklist is already ${enabled ? "on" : "off"}.`);
            return true;
          }
          options.setEnforcementEnabled(enabled);
          options.showStatus(`Automatic blacklist is now ${enabled ? "on" : "off"}.`);
          if (enabled) {
            patchLobbyBlacklist();
          }
          return true;
        }
        function handleBlacklistSlashCommand(argument) {
          const trimmed = argument.trim();
          if (!trimmed) {
            return showBlacklist();
          }
          const parsedName = parseQuotedName(trimmed);
          if (parsedName.quoted) {
            return addBlacklistName(trimmed);
          }
          const commandMatch = trimmed.match(/^(clear|on|off)$/i);
          if (commandMatch?.[1].toLowerCase() === "clear") {
            return clearBlacklist();
          }
          if (commandMatch?.[1].toLowerCase() === "on") {
            return setBlacklistEnforcement(true);
          }
          if (commandMatch?.[1].toLowerCase() === "off") {
            return setBlacklistEnforcement(false);
          }
          const removeMatch = trimmed.match(/^(?:remove|delete|rm)(?:\s+(.+))?$/i);
          if (removeMatch) {
            return removeBlacklistName(removeMatch[1] || "");
          }
          return addBlacklistName(trimmed);
        }
        return {
          enforceBlacklist,
          handleBlacklistSlashCommand,
          patchLobbyBlacklist
        };
      }

      // src/features/player-popup-dismissal.ts
      var dismissalListenersInstalled = false;
      function getRightClickMenus() {
        return Array.from(document.querySelectorAll(".rightClickMenu"));
      }
      function removePlayerPopups() {
        const menus = getRightClickMenus();
        for (const menu of menus) {
          menu.remove();
        }
        return menus.length > 0;
      }
      function isInsidePopupActionList(target) {
        return target instanceof Element && Boolean(target.closest(".rightClickMenu .container"));
      }
      function handlePointerOutsidePlayerPopup(event) {
        if (!getRightClickMenus().length || isInsidePopupActionList(event.target)) {
          return;
        }
        removePlayerPopups();
      }
      function installPlayerPopupDismissal() {
        if (dismissalListenersInstalled) {
          return;
        }
        dismissalListenersInstalled = true;
        document.addEventListener("pointerdown", handlePointerOutsidePlayerPopup, true);
        document.addEventListener("mousedown", handlePointerOutsidePlayerPopup, true);
      }

      // src/features/switch-teams-button.ts
      function createSwitchTeamsButtonController(dependencies) {
        function removeSwitchTeamsButton() {
          for (const button of document.querySelectorAll(".qolboxSwitchTeamsButton")) {
            button.remove();
          }
        }
        function handleSwitchTeamsButtonClick(event) {
          event.preventDefault();
          event.stopPropagation();
          if (dependencies.isSwitching()) {
            return;
          }
          dependencies.switchTeams();
        }
        function patchSwitchTeamsButton() {
          const session = getMultiplayerSession();
          const container = document.querySelector(".lobbyContainer .playerBox .teamsButtonContainer");
          if (!dependencies.isEnabled() || !container || !isHostSession(session) || !dependencies.isTeamMode(session) || !isElementVisible(container)) {
            removeSwitchTeamsButton();
            return false;
          }
          let button = container.querySelector(".qolboxSwitchTeamsButton");
          if (!button) {
            button = document.createElement("div");
            button.className = "teamButton qolboxSwitchTeamsButton";
            button.dataset.qolboxSwitchTeams = "true";
          }
          const switching = dependencies.isSwitching();
          button.onclick = handleSwitchTeamsButtonClick;
          button.classList.toggle("qolboxSwitchTeamsButtonBusy", switching);
          button.setAttribute("aria-disabled", switching ? "true" : "false");
          button.setAttribute("title", switching ? "Switching teams..." : "Switch red and blue teams");
          const label = switching ? "SWITCHING" : "SWITCH";
          if (button.textContent !== label) {
            button.textContent = label;
          }
          const blueButton = Array.from(container.querySelectorAll(".teamButton")).find(
            (element) => /^\s*JOIN\s+BLUE\s*$/i.test(element.textContent || "")
          );
          if (blueButton && blueButton !== button && button.nextElementSibling !== blueButton) {
            container.insertBefore(button, blueButton);
          } else if (button.parentElement !== container) {
            container.appendChild(button);
          }
          return true;
        }
        return { patchSwitchTeamsButton, removeSwitchTeamsButton };
      }

      // src/features/team-mode-detector.ts
      var TEAM_MODE_VALUES = /* @__PURE__ */ new Set([3, 4, 5]);
      function getSelectedLobbyModeValue() {
        const modeSelect = document.querySelector("select.modeDropdown.left, select.modeDropdown");
        if (!modeSelect) {
          return null;
        }
        const mode = Number(modeSelect.value);
        return Number.isFinite(mode) ? mode : null;
      }
      function hasVisibleTeamModeControls() {
        for (const element of document.querySelectorAll("button, .button, .bottomButton, .item, div")) {
          if (!isElementVisible(element)) {
            continue;
          }
          if (/^\s*JOIN\s+(RED|BLUE)\s*$/i.test(element.textContent || "")) {
            return true;
          }
        }
        return false;
      }
      function isTeamMode(session = getMultiplayerSession()) {
        if (isNativeTeamMode(session)) {
          return true;
        }
        const selectedMode = getSelectedLobbyModeValue();
        if (TEAM_MODE_VALUES.has(selectedMode ?? Number.NaN)) {
          return true;
        }
        if (hasVisibleTeamModeControls()) {
          return true;
        }
        return getSessionPlayers(session).some(({ player }) => {
          const team = getPlayerTeamState(player);
          return team === TEAM_STATE_RED || team === TEAM_STATE_BLUE;
        });
      }

      // src/features/lobby-commands-feature-bundle.ts
      function createLobbyCommandsFeatureBundle(options) {
        const { showQolboxChatStatus } = createQolboxChatStatusWriter({
          getSession: getMultiplayerSession
        });
        const lobbyCommandActions = createLobbyCommandActions({
          getPlayerDisplayName,
          isCurrentPlayerSpectating: options.isCurrentPlayerSpectating,
          isTeamMode,
          noteLocallyInitiatedPlayTransition: options.noteLocallyInitiatedPlayTransition,
          showStatus: showQolboxChatStatus
        });
        const blacklist = createLobbyBlacklistController({
          areLobbyCommandsEnabled: options.areLobbyCommandsEnabled,
          isEnforcementEnabled: options.isBlacklistEnforcementEnabled,
          setEnforcementEnabled: options.setBlacklistEnforcementEnabled,
          showStatus: showQolboxChatStatus
        });
        const dispatcher = createLobbyCommandDispatcher({
          actions: lobbyCommandActions,
          areGameStartAlertsEnabled: options.areGameStartAlertsEnabled,
          handleBlacklistSlashCommand: blacklist.handleBlacklistSlashCommand,
          installStartAlertHooks: options.installStartAlertHooks,
          noteLocallyInitiatedPlayTransition: options.noteLocallyInitiatedPlayTransition,
          showStatus: showQolboxChatStatus
        });
        const switchTeamsButton = createSwitchTeamsButtonController({
          isEnabled: options.areLobbyCommandsEnabled,
          isSwitching: lobbyCommandActions.isSwitchingTeams,
          isTeamMode,
          switchTeams: lobbyCommandActions.switchTeamPlayers
        });
        function prepareNativePlayerCommand(message) {
          if (typeof message !== "string") {
            return message;
          }
          const match = message.match(/^(\s*)\/(kick|ban)\s+(.+?)\s*$/i);
          if (!match) {
            return message;
          }
          const [, leadingSpace, commandName, rawTarget] = match;
          const quotedTarget = rawTarget.match(/^(["'])(.*)\1$/);
          const targetName = quotedTarget ? quotedTarget[2] : rawTarget;
          const result = lobbyCommandActions.findPlayerByName(targetName);
          if (result.status === "found") {
            return `${leadingSpace}/${commandName.toLowerCase()} ${formatCommandPlayerName(result.match.player)}`;
          }
          if (result.status === "ambiguous") {
            const matches = result.matches.map(({ player }) => getPlayerDisplayName(player) || "Unnamed Player").slice(0, 4).join(", ");
            showQolboxChatStatus(`Player name '${targetName}' is ambiguous${matches ? `: ${matches}` : ""}.`);
            return null;
          }
          showQolboxChatStatus(`Couldn't find player '${targetName}'.`);
          return null;
        }
        function patchSlashCommands() {
          return installSlashCommandInterceptor(getMultiplayerSession(), {
            areCommandsEnabled: options.areLobbyCommandsEnabled,
            handleCommand: dispatcher.handleQolboxSlashCommand,
            prepareNativeCommand: prepareNativePlayerCommand,
            showHelp: lobbyCommandActions.showQolboxCommandHelp
          });
        }
        return {
          ...lobbyCommandActions,
          ...dispatcher,
          ...blacklist,
          ...switchTeamsButton,
          installPlayerPopupDismissal,
          patchSlashCommands,
          showQolboxChatStatus
        };
      }

      // src/features/mobile-grab-context.ts
      function getMobileAbilityButtons() {
        const nativeButtons = getNativeMobileAbilityButtonElements();
        if (nativeButtons.length) {
          return nativeButtons;
        }
        return Array.from(document.querySelectorAll(".buttonArea.bat, .buttonArea.push, .buttonArea.rocket"));
      }
      function areNativeMobileAbilityButtonsVisible() {
        const buttons = getMobileAbilityButtons();
        return buttons.length > 0 && buttons.every(isElementVisible);
      }
      function isMobileGameModeContext() {
        return isNativeMobileMode() || areNativeMobileAbilityButtonsVisible();
      }
      function isMobileQolboxMenuContextValue() {
        if (isNativeMobileMode()) {
          return true;
        }
        const nav = window.navigator || (typeof navigator !== "undefined" ? navigator : null);
        const touchPoints = Number(
          nav && (readObjectProperty(nav, "maxTouchPoints") || readObjectProperty(nav, "msMaxTouchPoints") || 0)
        );
        if (!touchPoints || typeof window.matchMedia !== "function") {
          return false;
        }
        try {
          return window.matchMedia("(hover: none) and (pointer: coarse)").matches;
        } catch {
          return false;
        }
      }

      // src/features/mobile-grab-button-element.ts
      function createMobileGrabButtonElement(container, handlers) {
        const button = document.createElement("div");
        button.className = "buttonArea qolboxMobileGrabButton";
        button.setAttribute("aria-label", "Grab");
        button.setAttribute("role", "button");
        button.dataset.qolboxMobileGrab = "true";
        button.addEventListener("touchstart", handlers.onTouchStart, {
          passive: false,
          capture: true
        });
        button.addEventListener("pointerdown", handlers.onPointerStart, true);
        container.appendChild(button);
        return button;
      }
      function hideMobileGrabButtonElement(button) {
        if (button && button.style) {
          button.style.display = "none";
        }
      }
      function removeMobileGrabButtonElement(button) {
        hideMobileGrabButtonElement(button);
        if (button && button.isConnected) {
          button.remove();
        }
        return null;
      }

      // src/features/mobile-grab-input.ts
      var MOBILE_GRAB_FALLBACK_KEY = "v";
      var MOBILE_GRAB_FALLBACK_CODE = "KeyV";
      var MOBILE_GRAB_FALLBACK_KEY_CODE = 86;
      function createMobileGrabInputController() {
        let mobileGrabPointerDown = false;
        let mobileGrabInputState = null;
        let mobileGrabControlledInputState = null;
        let mobileGrabKeyboardFallbackActive = false;
        function getMobileGrabInputState() {
          const controlInputState = getNativeMobileControlInputState(getNativeMobileControls());
          if (controlInputState) {
            return controlInputState;
          }
          const sessionInputState = getLiveMultiplayerInputState();
          if (sessionInputState) {
            return sessionInputState;
          }
          return mobileGrabInputState;
        }
        function dispatchMobileGrabKeyboardFallback(pressed) {
          if (pressed === mobileGrabKeyboardFallbackActive) {
            return;
          }
          mobileGrabKeyboardFallbackActive = pressed;
          const event = new KeyboardEvent(pressed ? "keydown" : "keyup", {
            bubbles: true,
            cancelable: true,
            code: MOBILE_GRAB_FALLBACK_CODE,
            composed: true,
            key: MOBILE_GRAB_FALLBACK_KEY
          });
          const legacyKeyProperties = [
            ["keyCode", MOBILE_GRAB_FALLBACK_KEY_CODE],
            ["which", MOBILE_GRAB_FALLBACK_KEY_CODE]
          ];
          for (const [property, value] of legacyKeyProperties) {
            try {
              Object.defineProperty(event, property, { get: () => value });
            } catch {
            }
          }
          window.dispatchEvent(event);
        }
        function setMobileGrabPressed(pressed) {
          const nextPressed = Boolean(pressed);
          if (!nextPressed && !mobileGrabPointerDown && !mobileGrabControlledInputState && !mobileGrabKeyboardFallbackActive) {
            return;
          }
          mobileGrabPointerDown = nextPressed;
          if (!mobileGrabPointerDown) {
            if (mobileGrabControlledInputState) {
              setGrabInputPressed(mobileGrabControlledInputState, false);
              mobileGrabControlledInputState = null;
            }
            if (mobileGrabKeyboardFallbackActive) {
              dispatchMobileGrabKeyboardFallback(false);
            }
            return;
          }
          const inputState = getMobileGrabInputState();
          if (inputState && setGrabInputPressed(inputState, true)) {
            mobileGrabInputState = inputState;
            mobileGrabControlledInputState = inputState;
            if (mobileGrabKeyboardFallbackActive) {
              dispatchMobileGrabKeyboardFallback(false);
            }
            return;
          }
          dispatchMobileGrabKeyboardFallback(true);
        }
        function observeMobileGrabInputState(inputState) {
          mobileGrabInputState = inputState;
        }
        function restoreMobileGrabPressedOnInputState(inputState) {
          if (mobileGrabPointerDown && setGrabInputPressed(inputState, true)) {
            mobileGrabControlledInputState = inputState;
          }
        }
        function isMobileGrabPressed() {
          return mobileGrabPointerDown;
        }
        return {
          isMobileGrabPressed,
          observeMobileGrabInputState,
          restoreMobileGrabPressedOnInputState,
          setMobileGrabPressed
        };
      }

      // src/features/mobile-grab-layout.ts
      function getCssScale(element, options) {
        const rect = element.getBoundingClientRect();
        const cssWidth = element.clientWidth || Number.parseFloat(window.getComputedStyle(element).width) || rect.width;
        const cssHeight = element.clientHeight || Number.parseFloat(window.getComputedStyle(element).height) || rect.height;
        return {
          x: cssWidth > 0 && rect.width > 0 ? rect.width / cssWidth : 1,
          y: cssHeight > 0 && rect.height > 0 ? rect.height / cssHeight : 1,
          width: cssWidth || options.fallbackBaseWidth,
          height: cssHeight || options.fallbackBaseHeight
        };
      }
      function getMobileAbilityGapCss(buttons, scaleY) {
        const rects = buttons.map((button) => button.getBoundingClientRect()).filter((rect) => rect.width > 0 && rect.height > 0).sort((left, right) => left.top - right.top);
        let gap = Infinity;
        for (let index = 1; index < rects.length; index += 1) {
          const currentGap = rects[index].top - rects[index - 1].bottom;
          if (currentGap > 0) {
            gap = Math.min(gap, currentGap);
          }
        }
        return Number.isFinite(gap) ? Math.round(gap / Math.max(0.01, scaleY)) : 10;
      }
      function positionMobileGrabButton(button, options) {
        const container = document.getElementById("relativeContainer");
        const abilityButtons = options.getAbilityButtons();
        const referenceButton = document.querySelector(".buttonArea.bat") || abilityButtons[0];
        if (!container || !referenceButton || !isElementVisible(referenceButton)) {
          button.style.left = "auto";
          button.style.top = "auto";
          button.style.right = "40px";
          button.style.bottom = "0px";
          button.style.width = "90px";
          button.style.height = "90px";
          return;
        }
        const containerRect = container.getBoundingClientRect();
        const referenceRect = referenceButton.getBoundingClientRect();
        const scale = getCssScale(container, options);
        const gap = getMobileAbilityGapCss(abilityButtons, scale.y);
        const width = Math.round(referenceRect.width / Math.max(0.01, scale.x)) || 90;
        const height = Math.round(referenceRect.height / Math.max(0.01, scale.y)) || 90;
        const desiredLeft = (referenceRect.left - containerRect.left) / Math.max(0.01, scale.x) - width - gap;
        const desiredTop = (referenceRect.top - containerRect.top) / Math.max(0.01, scale.y);
        const containerRight = Number.isFinite(containerRect.right) ? containerRect.right : containerRect.left + containerRect.width;
        const containerBottom = Number.isFinite(containerRect.bottom) ? containerRect.bottom : containerRect.top + containerRect.height;
        const viewportWidth = window.innerWidth || containerRight;
        const viewportHeight = window.innerHeight || containerBottom;
        const visibleLeft = Math.max(0, containerRect.left);
        const visibleTop = Math.max(0, containerRect.top);
        const visibleRight = Math.min(viewportWidth, containerRight);
        const visibleBottom = Math.min(viewportHeight, containerBottom);
        const minLeft = Math.max(0, Math.round((visibleLeft - containerRect.left) / Math.max(0.01, scale.x)));
        const minTop = Math.max(0, Math.round((visibleTop - containerRect.top) / Math.max(0.01, scale.y)));
        const maxLeft = Math.max(
          minLeft,
          Math.min(scale.width - width, Math.round((visibleRight - containerRect.left) / Math.max(0.01, scale.x) - width))
        );
        const maxTop = Math.max(
          minTop,
          Math.min(scale.height - height, Math.round((visibleBottom - containerRect.top) / Math.max(0.01, scale.y) - height))
        );
        const left = Math.max(minLeft, Math.min(maxLeft, Math.round(desiredLeft)));
        const top = Math.max(minTop, Math.min(maxTop, Math.round(desiredTop)));
        button.style.width = `${width}px`;
        button.style.height = `${height}px`;
        button.style.left = `${left}px`;
        button.style.top = `${top}px`;
        button.style.right = "auto";
        button.style.bottom = "auto";
      }

      // src/features/mobile-grab-events.ts
      function getChangedTouches(event) {
        const changedTouches = readObjectProperty(event, "changedTouches");
        const length = Number(readObjectProperty(changedTouches, "length"));
        if (!Number.isFinite(length) || length <= 0) {
          return [];
        }
        const touches = [];
        for (let index = 0; index < length; index += 1) {
          const touch = readObjectProperty(changedTouches, index);
          if (touch) {
            touches.push(touch);
          }
        }
        return touches;
      }
      function getTouchIdentifier(touch) {
        return readObjectProperty(touch, "identifier");
      }
      function getPointerIdentifier(event) {
        return readObjectProperty(event, "pointerId");
      }
      function isPrimaryPointerStart(event) {
        const button = readObjectProperty(event, "button");
        return button === void 0 || button === 0;
      }
      function callEventMethod(event, methodName) {
        const method = readObjectProperty(event, methodName);
        if (isReflectableObject(event) && typeof method === "function") {
          Reflect.apply(method, event, []);
        }
      }
      function stopMobileGrabEvent(event) {
        if (readObjectProperty(event, "cancelable") !== false) {
          callEventMethod(event, "preventDefault");
        }
        callEventMethod(event, "stopImmediatePropagation");
      }

      // src/features/mobile-grab-press.ts
      var UNKNOWN_POINTER_ID = /* @__PURE__ */ Symbol("qolbox-unknown-pointer");
      function createMobileGrabPressController(options) {
        let activeTouchId = null;
        let activePointerId = null;
        let releaseHooksInstalled = false;
        function resetMobileGrabPress() {
          activeTouchId = null;
          activePointerId = null;
          options.setPressed(false);
        }
        function getPointerKey(event) {
          const pointerId = getPointerIdentifier(event);
          return pointerId === void 0 || pointerId === null ? UNKNOWN_POINTER_ID : pointerId;
        }
        function handleMobileGrabTouchStart(event) {
          if (!options.getButton() || !options.shouldShow()) {
            return;
          }
          const touch = getChangedTouches(event)[0];
          if (!touch) {
            return;
          }
          stopMobileGrabEvent(event);
          activeTouchId = getTouchIdentifier(touch);
          options.setPressed(true);
        }
        function handleMobileGrabTouchEnd(event) {
          if (activeTouchId === null) {
            return;
          }
          for (const touch of getChangedTouches(event)) {
            if (getTouchIdentifier(touch) === activeTouchId) {
              activeTouchId = null;
              options.setPressed(false);
              return;
            }
          }
        }
        function handleMobileGrabPointerStart(event) {
          if (!options.getButton() || !options.shouldShow()) {
            return;
          }
          if (!isPrimaryPointerStart(event)) {
            return;
          }
          stopMobileGrabEvent(event);
          activePointerId = getPointerKey(event);
          options.setPressed(true);
        }
        function handleMobileGrabPointerEnd(event) {
          if (activePointerId === null) {
            return;
          }
          if (getPointerKey(event) !== activePointerId) {
            return;
          }
          stopMobileGrabEvent(event);
          activePointerId = null;
          options.setPressed(false);
        }
        function installMobileGrabReleaseHooks() {
          if (releaseHooksInstalled) {
            return;
          }
          releaseHooksInstalled = true;
          document.addEventListener("touchend", handleMobileGrabTouchEnd, true);
          document.addEventListener("touchcancel", handleMobileGrabTouchEnd, true);
          document.addEventListener("pointerup", handleMobileGrabPointerEnd, true);
          document.addEventListener("pointercancel", handleMobileGrabPointerEnd, true);
          window.addEventListener("touchend", handleMobileGrabTouchEnd, true);
          window.addEventListener("touchcancel", handleMobileGrabTouchEnd, true);
          window.addEventListener("pointerup", handleMobileGrabPointerEnd, true);
          window.addEventListener("pointercancel", handleMobileGrabPointerEnd, true);
          window.addEventListener("blur", resetMobileGrabPress, true);
        }
        return {
          handleMobileGrabPointerStart,
          handleMobileGrabTouchStart,
          installMobileGrabReleaseHooks,
          resetMobileGrabPress
        };
      }

      // src/features/mobile-grab-button.ts
      var MOBILE_GRAB_ICON_HREF = "data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 64 64%22 fill=%22none%22%3E%3Cpath d=%22M22 36V13a5 5 0 0 1 10 0v20V9a5 5 0 0 1 10 0v25V13a5 5 0 0 1 10 0v23V22a4 4 0 0 1 8 0v18c0 13-9 21-23 21h-7c-7 0-11-4-15-10l-6-9a5 5 0 0 1 8-6l8 9%22 stroke=%22%23f4f4f4%22 stroke-width=%226%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22/%3E%3C/svg%3E";
      function createMobileGrabController(dependencies) {
        let mobileGrabButton = null;
        const mobileGrabInput = createMobileGrabInputController();
        const mobileGrabPress = createMobileGrabPressController({
          getButton: () => mobileGrabButton,
          isPressed: () => mobileGrabInput.isMobileGrabPressed(),
          setPressed: (pressed) => setMobileGrabPressed(pressed),
          shouldShow: () => shouldShowMobileGrabButton()
        });
        function isMobileGameMode() {
          return isMobileGameModeContext();
        }
        function isMobileQolboxMenuContext() {
          return isMobileQolboxMenuContextValue();
        }
        function setMobileGrabPressed(pressed) {
          mobileGrabInput.setMobileGrabPressed(pressed);
        }
        function hideMobileGrabButton() {
          mobileGrabPress.resetMobileGrabPress();
          hideMobileGrabButtonElement(mobileGrabButton);
        }
        function removeMobileGrabButton() {
          hideMobileGrabButton();
          mobileGrabButton = removeMobileGrabButtonElement(mobileGrabButton);
        }
        function handleMobileGrabPointerStart(event) {
          mobileGrabPress.handleMobileGrabPointerStart(event);
        }
        function ensureMobileGrabButton() {
          if (mobileGrabButton && mobileGrabButton.isConnected) {
            return mobileGrabButton;
          }
          const container = document.getElementById("relativeContainer");
          if (!container) {
            return null;
          }
          const button = createMobileGrabButtonElement(container, {
            onPointerStart: handleMobileGrabPointerStart,
            onTouchStart: mobileGrabPress.handleMobileGrabTouchStart
          });
          mobileGrabButton = button;
          mobileGrabPress.installMobileGrabReleaseHooks();
          return button;
        }
        function layoutMobileGrabButton(button) {
          positionMobileGrabButton(button, {
            fallbackBaseHeight: dependencies.fallbackBaseHeight,
            fallbackBaseWidth: dependencies.fallbackBaseWidth,
            getAbilityButtons: getMobileAbilityButtons
          });
        }
        function shouldShowMobileGrabButton() {
          return Boolean(dependencies.isEnabled() && isMobileGameMode() && areNativeMobileAbilityButtonsVisible());
        }
        function syncMobileGrabButton() {
          if (!dependencies.isEnabled() || !isMobileGameMode()) {
            removeMobileGrabButton();
            return false;
          }
          const button = ensureMobileGrabButton();
          if (!button) {
            return false;
          }
          if (!shouldShowMobileGrabButton()) {
            hideMobileGrabButton();
            return false;
          }
          layoutMobileGrabButton(button);
          button.style.display = "block";
          return true;
        }
        function installMobileGrabControlHooks() {
          return installNativeMobileControlHooks({
            onInputStateObserved(inputState) {
              mobileGrabInput.observeMobileGrabInputState(inputState);
            },
            afterInputStateSet(inputState) {
              mobileGrabInput.restoreMobileGrabPressedOnInputState(inputState);
            },
            onControlsShown() {
              syncMobileGrabButton();
            },
            onControlsHidden() {
              hideMobileGrabButton();
            }
          });
        }
        function patchMobileGrabButton() {
          if (!dependencies.isEnabled()) {
            removeMobileGrabButton();
            return false;
          }
          installMobileGrabControlHooks();
          return syncMobileGrabButton();
        }
        return {
          handleMobileGrabPointerStart,
          hideMobileGrabButton,
          isMobileGameMode,
          isMobileQolboxMenuContext,
          layoutMobileGrabButton,
          patchMobileGrabButton,
          removeMobileGrabButton,
          setMobileGrabPressed,
          shouldShowMobileGrabButton,
          syncMobileGrabButton
        };
      }

      // src/features/mobile-qolbox-menu-entry.ts
      function createMobileQolboxMenuEntryController({
        findChangeControlsItem: findChangeControlsItem2,
        getSettingsContainer,
        isMobileQolboxMenuContext,
        openMenu
      }) {
        function removeMobileQolboxHamburgerEntry() {
          for (const item of document.querySelectorAll('.item[data-qolbox-mobile-menu="true"]')) {
            item.remove();
          }
        }
        function patchMobileQolboxHamburgerEntry() {
          const container = getSettingsContainer();
          if (!container) {
            return false;
          }
          if (!isMobileQolboxMenuContext()) {
            removeMobileQolboxHamburgerEntry();
            return false;
          }
          let item = container.querySelector('.item[data-qolbox-mobile-menu="true"]');
          if (!item) {
            item = document.createElement("div");
            item.className = "item";
            item.dataset.qolboxMobileMenu = "true";
            item.addEventListener(
              "click",
              (event) => {
                event.preventDefault();
                event.stopImmediatePropagation();
                openMenu();
              },
              true
            );
          }
          const beforeItem = findChangeControlsItem2(container);
          if (beforeItem && beforeItem !== item) {
            container.insertBefore(item, beforeItem);
          } else if (item.parentElement !== container) {
            container.appendChild(item);
          }
          item.textContent = "QOLBox";
          return true;
        }
        return { patchMobileQolboxHamburgerEntry, removeMobileQolboxHamburgerEntry };
      }

      // src/features/mobile-feature-bundle.ts
      function createMobileFeatureBundle(options) {
        const mobileGrabController = createMobileGrabController({
          fallbackBaseHeight: FALLBACK_BASE_HEIGHT,
          fallbackBaseWidth: FALLBACK_BASE_WIDTH,
          isEnabled: options.isMobileGrabEnabled
        });
        const { patchMobileQolboxHamburgerEntry } = createMobileQolboxMenuEntryController({
          findChangeControlsItem,
          getSettingsContainer: findSettingsContainer,
          isMobileQolboxMenuContext: mobileGrabController.isMobileQolboxMenuContext,
          openMenu: options.openMenu
        });
        return {
          ...mobileGrabController,
          patchMobileQolboxHamburgerEntry
        };
      }

      // src/features/popup-keyboard-controls.ts
      var NATIVE_POPUP_SELECTOR = [
        ".mouseBlockContainer > :not(.behindBlocker)",
        ".createWindowContainer .createWindow",
        ".passwordWindowContainer .passwordWindow",
        ".connectingWindowContainer .connectingWindow",
        ".autoLoginWindowContainer .autoLoginWindow",
        ".mapListContainer .enterNameWindow",
        ".oneButtonWindow",
        ".twoButtonWindow",
        ".updateNews",
        ".settingsWindow",
        ".recordsWindow",
        ".cosmeticWindow",
        ".rightClickMenu"
      ].join(", ");
      function isVisibleElement(element) {
        if (!(element instanceof HTMLElement)) {
          return false;
        }
        const style = window.getComputedStyle(element);
        const rect = element.getBoundingClientRect();
        return style.display !== "none" && style.visibility !== "hidden" && Number(style.opacity || 1) !== 0 && rect.width > 0 && rect.height > 0;
      }
      function getVisibleNativePopup() {
        const popups = Array.from(document.querySelectorAll(NATIVE_POPUP_SELECTOR)).filter(isVisibleElement).filter((popup) => !popup.closest(".qolboxMenuOverlay"));
        return popups[popups.length - 1] || null;
      }
      function isDisabledAction(element) {
        return element.classList.contains("disabled") || element.hasAttribute("disabled") || element.getAttribute("aria-disabled") === "true" || window.getComputedStyle(element).pointerEvents === "none";
      }
      function findEnabledAction(popup, selectors) {
        for (const selector of selectors) {
          const action = popup.querySelector(selector);
          if (action && isVisibleElement(action) && !isDisabledAction(action)) {
            return action;
          }
        }
        return null;
      }
      function isNativeKeyBindingActive(popup) {
        return popup.matches(".settingsWindow") && Array.from(popup.querySelectorAll(".clickable")).some((element) => element.textContent?.trim() === "...");
      }
      function isMultilineEditor(target) {
        return target instanceof HTMLTextAreaElement || target instanceof HTMLElement && target.isContentEditable;
      }
      function dismissRightClickMenu(popup) {
        if (!popup.matches(".rightClickMenu")) {
          return false;
        }
        popup.remove();
        return true;
      }
      function getEscapeAction(popup) {
        return findEnabledAction(popup, [
          ".crossButton",
          ".cancelButton",
          ".backButton",
          ".oneButtonWindow .button",
          ".button"
        ]);
      }
      function getEnterAction(popup) {
        const activeElement = document.activeElement;
        if (activeElement instanceof HTMLElement && popup.contains(activeElement) && activeElement.matches('button, [role="button"], .button, .bottomButton, .item') && !isDisabledAction(activeElement)) {
          return activeElement;
        }
        const primaryAction = findEnabledAction(popup, [
          ".okButton",
          ".joinButton",
          ".createButton",
          ".saveButton",
          ".playButton",
          ".oneButtonWindow .button",
          ".button:not(.cancelButton):not(.leftButton):not(.rightButton)"
        ]);
        if (primaryAction) {
          return primaryAction;
        }
        return popup.matches(".updateNews") ? findEnabledAction(popup, [".crossButton"]) : null;
      }
      function getArrowAction(popup, direction) {
        const hasPageNavigation = popup.matches(".updateNews") || Boolean(popup.querySelector(".dateLabel"));
        if (!hasPageNavigation) {
          return null;
        }
        return findEnabledAction(popup, [direction === "left" ? ".leftButton" : ".rightButton"]);
      }
      function createPopupKeyboardController() {
        let hooksInstalled = false;
        function handlePopupKeyboard(event) {
          if (event.defaultPrevented || event.repeat || event.isComposing || event.altKey || event.ctrlKey || event.metaKey || document.querySelector(".qolboxMenuOverlay")) {
            return;
          }
          const popup = getVisibleNativePopup();
          if (!popup || isNativeKeyBindingActive(popup)) {
            return;
          }
          let handled = false;
          if (isEscapeKey(event)) {
            handled = dismissRightClickMenu(popup);
            const action = handled ? null : getEscapeAction(popup);
            if (action) {
              action.click();
              handled = true;
            }
          } else if (isEnterKey(event) && !isMultilineEditor(event.target)) {
            const action = getEnterAction(popup);
            if (action) {
              action.click();
              handled = true;
            }
          } else if (isArrowLeftKey(event) || isArrowRightKey(event)) {
            const action = getArrowAction(popup, isArrowLeftKey(event) ? "left" : "right");
            if (action) {
              action.click();
              handled = true;
            }
          }
          if (!handled) {
            return;
          }
          event.preventDefault();
          event.stopPropagation();
          event.stopImmediatePropagation();
        }
        function installPopupKeyboardHooks() {
          if (hooksInstalled) {
            return;
          }
          hooksInstalled = true;
          window.addEventListener("keydown", handlePopupKeyboard, true);
        }
        return {
          handlePopupKeyboard,
          installPopupKeyboardHooks
        };
      }

      // src/settings/onboarding-storage.ts
      var ONBOARDING_COMPLETE_KEY = "vm.hitbox.qolboxOnboardingComplete";
      function loadOnboardingComplete() {
        try {
          return localStorage.getItem(ONBOARDING_COMPLETE_KEY) === "true";
        } catch {
          return false;
        }
      }
      function saveOnboardingComplete() {
        try {
          localStorage.setItem(ONBOARDING_COMPLETE_KEY, "true");
        } catch {
        }
      }

      // src/config/qolbox-version.ts
      var QOLBOX_VERSION = "2.1.3";
      var QOLBOX_VERSION_LABEL = `v${QOLBOX_VERSION}`;
      var QOLBOX_GREASYFORK_URL = "https://greasyfork.org/en/scripts/568667-qolbox";
      var QOLBOX_GITHUB_URL = "https://github.com/AggressiveCombo/QOLBox";

      // src/config/qolbox-release-notes.ts
      var GREASYFORK_HISTORY_URL = "https://greasyfork.org/en/scripts/568667-qolbox/versions?show_all_versions=1";
      var GITHUB_RELEASES_URL = "https://api.github.com/repos/AggressiveCombo/QOLBox/releases?per_page=100";
      var RELEASE_HISTORY_CACHE_KEY = "vm.hitbox.qolboxReleaseHistory.v2";
      var RELEASE_HISTORY_CACHE_TTL_MS = 12 * 60 * 60 * 1e3;
      var RELEASE_HISTORY_FETCH_TIMEOUT_MS = 7e3;
      var RELEASE_HISTORY_BRIDGE_REQUEST_SOURCE = "qolbox-release-history";
      var RELEASE_HISTORY_BRIDGE_RESPONSE_SOURCE = "qolbox-release-history-bridge";
      var RELEASE_HISTORY_BRIDGE_REQUEST_TYPE = "fetch";
      var RELEASE_HISTORY_BRIDGE_RESPONSE_TYPE = "fetch-result";
      var LOCAL_CURRENT_RELEASE_FALLBACK_NOTES = [
        "No public update notes were found for this version."
      ];
      var GREASYFORK_EMPTY_HISTORY_NOTES = [
        "No public update notes were posted for this version."
      ];
      var INITIAL_RELEASE_NOTES = [
        "Initial release.",
        "Persisted Hitbox game and jukebox volume, with wheel controls and jukebox mute."
      ];
      var LOCAL_CURRENT_RELEASE_FALLBACK = [
        {
          version: QOLBOX_VERSION,
          source: "local-fallback",
          notes: LOCAL_CURRENT_RELEASE_FALLBACK_NOTES
        }
      ];
      function isRecord5(value) {
        return typeof value === "object" && value !== null;
      }
      function normalizeVersionKey(version) {
        return String(version || "").trim().replace(/^v/i, "").toLowerCase();
      }
      function parseVersionPoint(version) {
        const normalized = normalizeVersionKey(version);
        if (!normalized) {
          return null;
        }
        const [main, prerelease = ""] = normalized.split("-", 2);
        const rawParts = main.split(".");
        const parts = [];
        let wildcardIndex = null;
        for (let index = 0; index < 3; index += 1) {
          const rawPart = rawParts[index] ?? "0";
          if (/^(x|\*)$/i.test(rawPart)) {
            if (wildcardIndex === null) {
              wildcardIndex = index;
            }
            parts.push(0);
            continue;
          }
          if (!/^\d+$/.test(rawPart)) {
            return null;
          }
          parts.push(Number(rawPart));
        }
        return {
          parts,
          prereleaseWeight: prerelease ? -1 : 0,
          wildcardIndex
        };
      }
      function compareVersionPoints(left, right) {
        for (let index = 0; index < 3; index += 1) {
          const delta = left.parts[index] - right.parts[index];
          if (delta) {
            return delta;
          }
        }
        return left.prereleaseWeight - right.prereleaseWeight;
      }
      function getWildcardUpperBound(point) {
        if (point.wildcardIndex === null) {
          return point;
        }
        const parts = [...point.parts];
        for (let index = point.wildcardIndex; index < 3; index += 1) {
          parts[index] = Number.MAX_SAFE_INTEGER;
        }
        return {
          parts,
          prereleaseWeight: 0,
          wildcardIndex: null
        };
      }
      function isVersionInUpgradeRange(version, previousVersion, currentVersion) {
        const versionPoint = parseVersionPoint(version);
        const currentPoint = parseVersionPoint(currentVersion);
        if (!versionPoint) {
          return false;
        }
        if (currentPoint && compareVersionPoints(versionPoint, currentPoint) > 0) {
          return false;
        }
        if (!previousVersion) {
          return true;
        }
        const previousPoint = parseVersionPoint(previousVersion);
        if (!previousPoint) {
          return true;
        }
        const previousUpperBound = getWildcardUpperBound(previousPoint);
        return compareVersionPoints(versionPoint, previousUpperBound) > 0;
      }
      function compareReleaseVersionsNewestFirst(left, right) {
        const leftPoint = parseVersionPoint(left.version);
        const rightPoint = parseVersionPoint(right.version);
        if (leftPoint && rightPoint) {
          const versionDelta = compareVersionPoints(rightPoint, leftPoint);
          if (versionDelta) {
            return versionDelta;
          }
        }
        return getReleaseTimestamp(right) - getReleaseTimestamp(left);
      }
      function getReleaseTimestamp(entry) {
        const timestamp = entry.publishedAt ? Date.parse(entry.publishedAt) : 0;
        return Number.isFinite(timestamp) ? timestamp : 0;
      }
      function getSourcePriority(source) {
        switch (source) {
          case "github":
            return 3;
          case "greasyfork":
            return 2;
          case "local-fallback":
          default:
            return 1;
        }
      }
      function hasReleaseHistoryText(entry) {
        return !entry.notes.every((note) => GREASYFORK_EMPTY_HISTORY_NOTES.includes(note));
      }
      function shouldReplaceReleaseEntry(next, current) {
        const sourcePriorityDelta = getSourcePriority(next.source) - getSourcePriority(current.source);
        if (sourcePriorityDelta) {
          return sourcePriorityDelta > 0;
        }
        const noteQualityDelta = Number(hasReleaseHistoryText(next)) - Number(hasReleaseHistoryText(current));
        if (noteQualityDelta) {
          return noteQualityDelta > 0;
        }
        const timestampDelta = getReleaseTimestamp(next) - getReleaseTimestamp(current);
        if (timestampDelta) {
          return timestampDelta > 0;
        }
        return false;
      }
      function dedupeLatestReleaseEntries(entries) {
        const byVersion = /* @__PURE__ */ new Map();
        for (const entry of entries) {
          const versionKey = normalizeVersionKey(entry.version);
          if (!versionKey || !entry.notes.length) {
            continue;
          }
          const current = byVersion.get(versionKey);
          if (!current || shouldReplaceReleaseEntry(entry, current)) {
            byVersion.set(versionKey, entry);
          }
        }
        return Array.from(byVersion.values()).sort(compareReleaseVersionsNewestFirst);
      }
      function cleanReleaseText(text) {
        return text.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/[`*_>#]+/g, "").replace(/\s+/g, " ").trim();
      }
      function extractMarkdownNotes(markdown) {
        if (typeof markdown !== "string") {
          return [];
        }
        const notes = [];
        for (const rawLine of markdown.split(/\r?\n/)) {
          const line = rawLine.trim();
          if (!line || /^#{1,6}\s+/.test(line)) {
            continue;
          }
          const bulletMatch = line.match(/^[-*+]\s+(.+)$/) || line.match(/^\d+[.)]\s+(.+)$/);
          if (bulletMatch) {
            const note = cleanReleaseText(bulletMatch[1]);
            if (note) {
              notes.push(note);
            }
          }
        }
        if (notes.length) {
          return notes;
        }
        const fallback = markdown.split(/\r?\n/).map(cleanReleaseText).find((line) => line && !/^#{1,6}\s+/.test(line));
        return fallback ? [fallback] : [];
      }
      function parseGitHubReleaseEntries(rawValue) {
        if (!Array.isArray(rawValue)) {
          return [];
        }
        return rawValue.filter((record) => isRecord5(record)).filter((record) => record.draft !== true).map((record) => {
          const version = normalizeVersionKey(record.tag_name);
          const notes = extractMarkdownNotes(record.body);
          return {
            version,
            source: "github",
            publishedAt: typeof record.published_at === "string" ? record.published_at : void 0,
            url: typeof record.html_url === "string" ? record.html_url : void 0,
            notes: notes.length ? notes : [cleanReleaseText(String(record.name || `QOLBox ${version}`))]
          };
        }).filter((entry) => entry.version && entry.notes.length);
      }
      async function fetchTextWithPageFetch(url, headers) {
        const controller = new AbortController();
        const timer = window.setTimeout(() => controller.abort(), RELEASE_HISTORY_FETCH_TIMEOUT_MS);
        try {
          const response = await fetch(url, {
            headers: {
              Accept: "application/json",
              ...headers
            },
            signal: controller.signal
          });
          if (!response.ok) {
            throw new Error(`HTTP ${response.status}`);
          }
          return await response.text();
        } finally {
          window.clearTimeout(timer);
        }
      }
      function isReleaseHistoryBridgeResponse(value, id) {
        return isRecord5(value) && value.source === RELEASE_HISTORY_BRIDGE_RESPONSE_SOURCE && value.type === RELEASE_HISTORY_BRIDGE_RESPONSE_TYPE && value.id === id;
      }
      function makeBridgeRequestId() {
        return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
      }
      function fetchTextWithUserscriptBridge(url, headers) {
        if (!window.__qolboxReleaseHistoryBridgeReady) {
          return Promise.reject(new Error("Release-history bridge is unavailable."));
        }
        return new Promise((resolve, reject) => {
          const id = makeBridgeRequestId();
          const timer = window.setTimeout(() => {
            cleanup();
            reject(new Error("Release-history bridge timed out."));
          }, RELEASE_HISTORY_FETCH_TIMEOUT_MS);
          const cleanup = () => {
            window.clearTimeout(timer);
            window.removeEventListener("message", handleBridgeMessage);
          };
          const handleBridgeMessage = (event) => {
            if (event.source !== window || !isReleaseHistoryBridgeResponse(event.data, id)) {
              return;
            }
            cleanup();
            if (event.data.ok === true && typeof event.data.text === "string") {
              resolve(event.data.text);
              return;
            }
            reject(new Error(typeof event.data.error === "string" ? event.data.error : "Release-history bridge failed."));
          };
          window.addEventListener("message", handleBridgeMessage);
          window.postMessage(
            {
              source: RELEASE_HISTORY_BRIDGE_REQUEST_SOURCE,
              type: RELEASE_HISTORY_BRIDGE_REQUEST_TYPE,
              id,
              url,
              headers
            },
            window.location.origin
          );
        });
      }
      async function fetchText(url, headers = {}) {
        const requestHeaders = {
          Accept: "text/html",
          ...headers
        };
        try {
          return await fetchTextWithPageFetch(url, requestHeaders);
        } catch {
          return fetchTextWithUserscriptBridge(url, requestHeaders);
        }
      }
      async function fetchJson(url, headers = {}) {
        return JSON.parse(await fetchText(url, {
          Accept: "application/json",
          ...headers
        }));
      }
      async function fetchGitHubReleaseEntries() {
        return parseGitHubReleaseEntries(await fetchJson(GITHUB_RELEASES_URL));
      }
      function getGreasyForkHistoryNotes(version, changelogElement) {
        if (!changelogElement) {
          return version === "1.0.0" ? INITIAL_RELEASE_NOTES : GREASYFORK_EMPTY_HISTORY_NOTES;
        }
        const notes = Array.from(changelogElement.querySelectorAll("li, p")).map((element) => cleanReleaseText(element.textContent || "")).filter(Boolean);
        return notes.length ? notes : GREASYFORK_EMPTY_HISTORY_NOTES;
      }
      function parseGreasyForkHistoryEntries(html) {
        const document2 = new DOMParser().parseFromString(html, "text/html");
        return Array.from(document2.querySelectorAll(".history_versions > li")).map((item) => {
          const versionLink = item.querySelector(".version-number a");
          const version = normalizeVersionKey(versionLink?.textContent);
          if (!version) {
            return null;
          }
          const href = versionLink?.getAttribute("href") || "";
          return {
            version,
            source: "greasyfork",
            publishedAt: item.querySelector("relative-time")?.getAttribute("datetime") || void 0,
            url: href ? new URL(href, GREASYFORK_HISTORY_URL).href : void 0,
            notes: getGreasyForkHistoryNotes(version, item.querySelector(".version-changelog"))
          };
        }).filter((entry) => Boolean(entry));
      }
      async function fetchGreasyForkReleaseEntries() {
        return parseGreasyForkHistoryEntries(await fetchText(GREASYFORK_HISTORY_URL));
      }
      function safeGetLocalStorage(key) {
        try {
          return localStorage.getItem(key);
        } catch {
          return null;
        }
      }
      function safeSetLocalStorage(key, value) {
        try {
          localStorage.setItem(key, value);
        } catch {
        }
      }
      function parseCachedReleaseHistory(rawValue) {
        if (!rawValue) {
          return null;
        }
        try {
          const parsed = JSON.parse(rawValue);
          if (!isRecord5(parsed) || typeof parsed.fetchedAt !== "number" || !Array.isArray(parsed.entries)) {
            return null;
          }
          const entries = parsed.entries.filter((entry) => isRecord5(entry) && typeof entry.version === "string" && Array.isArray(entry.notes)).map((entry) => ({
            version: entry.version,
            source: entry.source === "github" || entry.source === "greasyfork" || entry.source === "local-fallback" ? entry.source : "local-fallback",
            publishedAt: typeof entry.publishedAt === "string" ? entry.publishedAt : void 0,
            url: typeof entry.url === "string" ? entry.url : void 0,
            notes: entry.notes.map((note) => String(note)).filter(Boolean)
          }));
          return { fetchedAt: parsed.fetchedAt, entries };
        } catch {
          return null;
        }
      }
      function getCachedReleaseHistoryEntries(allowStale = false) {
        const cached = parseCachedReleaseHistory(safeGetLocalStorage(RELEASE_HISTORY_CACHE_KEY));
        if (!cached) {
          return null;
        }
        if (!allowStale && Date.now() - cached.fetchedAt > RELEASE_HISTORY_CACHE_TTL_MS) {
          return null;
        }
        return dedupeLatestReleaseEntries([...LOCAL_CURRENT_RELEASE_FALLBACK, ...cached.entries]);
      }
      function saveReleaseHistoryCache(entries) {
        safeSetLocalStorage(RELEASE_HISTORY_CACHE_KEY, JSON.stringify({
          fetchedAt: Date.now(),
          entries
        }));
      }
      async function fetchExternalReleaseHistoryEntries() {
        const [githubResult, greasyForkResult] = await Promise.allSettled([
          fetchGitHubReleaseEntries(),
          fetchGreasyForkReleaseEntries()
        ]);
        const externalEntries = [
          ...githubResult.status === "fulfilled" ? githubResult.value : [],
          ...greasyForkResult.status === "fulfilled" ? greasyForkResult.value : []
        ];
        if (!externalEntries.length) {
          throw new Error("No public release history entries loaded.");
        }
        const entries = dedupeLatestReleaseEntries([...LOCAL_CURRENT_RELEASE_FALLBACK, ...externalEntries]);
        saveReleaseHistoryCache(entries);
        return entries;
      }
      function getReleaseNotesBetween(previousVersion, currentVersion = QOLBOX_VERSION, releaseHistory = LOCAL_CURRENT_RELEASE_FALLBACK) {
        const entries = dedupeLatestReleaseEntries([...LOCAL_CURRENT_RELEASE_FALLBACK, ...releaseHistory]);
        return entries.filter((entry) => isVersionInUpgradeRange(entry.version, null, currentVersion));
      }
      function createInitialReleaseHistoryState(previousVersion, currentVersion = QOLBOX_VERSION) {
        const cachedEntries = getCachedReleaseHistoryEntries();
        if (cachedEntries) {
          const notes2 = getReleaseNotesBetween(previousVersion, currentVersion, cachedEntries);
          return {
            status: "ready",
            notes: notes2
          };
        }
        const notes = getReleaseNotesBetween(previousVersion, currentVersion);
        return {
          status: "loading",
          notes
        };
      }
      async function loadReleaseHistoryState(previousVersion, currentVersion = QOLBOX_VERSION) {
        try {
          const entries = await fetchExternalReleaseHistoryEntries();
          const notes = getReleaseNotesBetween(previousVersion, currentVersion, entries);
          return {
            status: "ready",
            notes
          };
        } catch {
          const cachedEntries = getCachedReleaseHistoryEntries(true);
          if (cachedEntries) {
            const notes2 = getReleaseNotesBetween(previousVersion, currentVersion, cachedEntries);
            return {
              status: "fallback",
              notes: notes2
            };
          }
          const notes = getReleaseNotesBetween(previousVersion, currentVersion);
          return {
            status: "fallback",
            notes
          };
        }
      }

      // src/settings/update-notice-storage.ts
      var LAST_VERSION_KEY = "vm.hitbox.qolboxLastVersion";
      var ACK_VERSION_KEY = "vm.hitbox.qolboxAcknowledgedVersion";
      function safeGetLocalStorage2(key) {
        try {
          return localStorage.getItem(key);
        } catch {
          return null;
        }
      }
      function safeSetLocalStorage2(key, value) {
        try {
          localStorage.setItem(key, value);
        } catch {
        }
      }
      function loadPendingUpdateNotice(currentVersion = QOLBOX_VERSION, existingInstallWithoutVersion = false) {
        const previousVersion = safeGetLocalStorage2(LAST_VERSION_KEY);
        const acknowledgedVersion = safeGetLocalStorage2(ACK_VERSION_KEY);
        if (!previousVersion) {
          if (existingInstallWithoutVersion) {
            return { previousVersion: "a pre-version-tracking build", currentVersion };
          }
          safeSetLocalStorage2(LAST_VERSION_KEY, currentVersion);
          safeSetLocalStorage2(ACK_VERSION_KEY, currentVersion);
          return null;
        }
        if (previousVersion === currentVersion || acknowledgedVersion === currentVersion) {
          if (previousVersion !== currentVersion) {
            safeSetLocalStorage2(LAST_VERSION_KEY, currentVersion);
          }
          return null;
        }
        return { previousVersion, currentVersion };
      }
      function acknowledgeUpdateNotice(currentVersion = QOLBOX_VERSION) {
        safeSetLocalStorage2(LAST_VERSION_KEY, currentVersion);
        safeSetLocalStorage2(ACK_VERSION_KEY, currentVersion);
      }

      // src/features/first-boot-onboarding.ts
      function createFirstBootOnboardingScheduler(options) {
        function scheduleFirstBootOnboarding() {
          if (options.isOnboardingComplete()) {
            return;
          }
          const show = () => {
            window.setTimeout(options.showFirstBootOnboarding, 0);
          };
          if (document.readyState === "loading") {
            document.addEventListener("DOMContentLoaded", show, { once: true });
          } else {
            show();
          }
        }
        return {
          scheduleFirstBootOnboarding
        };
      }

      // src/features/qolbox-menu-keyboard.ts
      function isModifiedQolboxMenuShortcut(event) {
        return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
      }
      function isQolboxMenuShortcut(event, menuKey) {
        return !isModifiedQolboxMenuShortcut(event) && (event.key === menuKey || event.code === menuKey);
      }

      // src/features/qolbox-menu-view.ts
      function findQolboxMenuPanel(menuId) {
        const menu = document.getElementById(menuId);
        return menu ? menu.querySelector(".qolboxMenuPanel") : null;
      }
      function focusFirstQolboxMenuControl(panel) {
        window.setTimeout(() => {
          const focusTarget = panel.querySelector(".qolboxMenuButton.primary, .qolboxMenuChoice.primary") || panel.querySelector(".qolboxMenuToggle.active") || panel.querySelector(".qolboxMenuButton");
          focusElementWithoutScroll(focusTarget);
        }, 0);
      }
      function renderQolboxMenuPanel(menuId, markup) {
        const panel = findQolboxMenuPanel(menuId);
        if (!panel) {
          return;
        }
        panel.innerHTML = markup;
        focusFirstQolboxMenuControl(panel);
      }
      function ensureQolboxMenuOverlay(options) {
        let menu = document.getElementById(options.menuId);
        if (menu) {
          return menu;
        }
        const host = document.body || document.documentElement;
        if (!host) {
          return null;
        }
        menu = document.createElement("div");
        menu.id = options.menuId;
        menu.className = "qolboxMenuOverlay";
        menu.setAttribute("role", "dialog");
        menu.setAttribute("aria-modal", "true");
        menu.innerHTML = '<div class="qolboxMenuPanel"></div>';
        menu.addEventListener("pointerdown", options.onPointerEvent, true);
        menu.addEventListener("mousedown", options.onPointerEvent, true);
        menu.addEventListener("mouseup", options.onPointerEvent, true);
        menu.addEventListener("wheel", options.onPointerEvent, { capture: true, passive: true });
        menu.addEventListener("click", options.onClick, true);
        menu.addEventListener("change", options.onInput, true);
        menu.addEventListener("input", options.onInput, true);
        host.appendChild(menu);
        return menu;
      }

      // src/features/qolbox-menu-controller.ts
      var FEATURE_PAGE_KEYS = [
        FEATURE_FULLSCREEN,
        FEATURE_RESERVE,
        FEATURE_CHAT,
        FEATURE_GAME_START_ALERT,
        FEATURE_EDITOR_MAP_TRANSFER,
        FEATURE_MOBILE_GRAB
      ];
      var ADVANCED_TIMING_KEYS = [
        ADVANCED_RESERVE_RETRY_INTERVAL_MS,
        ADVANCED_ALERT_DELAY_MS,
        ADVANCED_ALERT_FLASH_INTERVAL_MS,
        ADVANCED_TYPING_DURATION_MS
      ];
      function createQolboxMenuController(options) {
        let onboardingComplete = options.initialOnboardingComplete;
        let onboardingStepIndex = 0;
        let settingsDraft = null;
        let settingsErrors = {};
        let settingsPage = "features";
        let updateNoticePageIndex = 0;
        let mode = "closed";
        let hooksInstalled = false;
        function isOnboardingComplete() {
          return onboardingComplete;
        }
        function getMode() {
          return mode;
        }
        function isClosed() {
          return mode === "closed";
        }
        function renderQolboxMenu() {
          if (mode === "settings" && !settingsDraft) {
            settingsDraft = options.createSettingsDraft();
          }
          if (mode === "update") {
            updateNoticePageIndex = Math.max(
              0,
              Math.min(updateNoticePageIndex, Math.max(1, options.getUpdateNoticePageCount()) - 1)
            );
          }
          const markup = mode === "settings" ? options.getSettingsMenuMarkup(settingsDraft, settingsPage, settingsErrors) : mode === "update" ? options.getUpdateNoticeMarkup(updateNoticePageIndex) : options.getOnboardingStepMarkup(onboardingStepIndex);
          renderQolboxMenuPanel(options.menuId, markup);
        }
        function stopQolboxMenuPointerEvent(event) {
          if (mode !== "closed") {
            event.stopPropagation();
          }
        }
        function closeQolboxMenu() {
          mode = "closed";
          settingsDraft = null;
          settingsErrors = {};
          options.onMenuModeChanged();
          const menu = document.getElementById(options.menuId);
          if (menu) {
            menu.remove();
          }
        }
        function completeOnboarding() {
          onboardingComplete = true;
          closeQolboxMenu();
          options.onCompleteOnboarding();
        }
        function openQolboxMenu(nextMode = "settings") {
          options.onBeforeOpen();
          if (!ensureQolboxMenu()) {
            return;
          }
          mode = nextMode;
          if (nextMode === "onboarding") {
            settingsDraft = null;
            settingsErrors = {};
            onboardingStepIndex = 0;
          } else if (nextMode === "settings") {
            settingsDraft = options.createSettingsDraft();
            settingsErrors = {};
            settingsPage = "features";
          } else if (nextMode === "update") {
            updateNoticePageIndex = 0;
          }
          options.onMenuModeChanged();
          renderQolboxMenu();
        }
        function getAdvancedDefinition(key) {
          return ADVANCED_SETTING_DEFINITIONS.find((definition) => definition.key === key) || null;
        }
        function getDraftAdvancedValue(key) {
          const panel = document.getElementById(options.menuId);
          const input = panel ? Array.from(panel.querySelectorAll("[data-qolbox-advanced-input]")).find((element) => element.dataset.qolboxAdvancedInput === key) : null;
          return input ? input.value : settingsDraft?.advanced[key];
        }
        function updateDraftAdvancedValue(key, value) {
          const definition = getAdvancedDefinition(key);
          if (!definition || !settingsDraft) {
            return;
          }
          settingsDraft.advanced[definition.key] = value;
          if (settingsErrors[definition.key]) {
            delete settingsErrors[definition.key];
          }
        }
        function validateAdvancedValue(definition, value) {
          if (definition.kind === "number") {
            const numericValue = Number(value);
            if (!Number.isFinite(numericValue)) {
              return "Enter a number.";
            }
            if (numericValue < definition.min || numericValue > definition.max) {
              return `Use ${definition.min}-${definition.max}${definition.unit ? ` ${definition.unit}` : ""}.`;
            }
            return null;
          }
          return value === true || value === false || value === "true" || value === "false" ? null : "Choose Enabled or Off.";
        }
        function getErrorPage(key) {
          if (key === ADVANCED_COMMAND_ALIASES || key === ADVANCED_BLACKLIST_ENFORCEMENT) {
            return "commands";
          }
          return "advanced";
        }
        function validateSettingsDraft() {
          if (!settingsDraft) {
            return null;
          }
          const errors = {};
          const sanitized = {};
          for (const definition of ADVANCED_SETTING_DEFINITIONS) {
            const value = getDraftAdvancedValue(definition.key);
            settingsDraft.advanced[definition.key] = value;
            const error = validateAdvancedValue(definition, value);
            if (error) {
              errors[definition.key] = error;
            } else {
              sanitized[definition.key] = sanitizeAdvancedSetting(definition, value);
            }
          }
          settingsErrors = errors;
          const firstError = ADVANCED_SETTING_DEFINITIONS.find((definition) => errors[definition.key]);
          if (firstError) {
            settingsPage = getErrorPage(firstError.key);
            return null;
          }
          return sanitized;
        }
        function resetFeatureDraft(keys) {
          if (!settingsDraft) {
            return;
          }
          const defaults = getDefaultFeatureSettings();
          for (const key of keys) {
            settingsDraft.features[key] = defaults[key];
          }
        }
        function resetAdvancedDraft(keys) {
          if (!settingsDraft) {
            return;
          }
          const defaults = getDefaultAdvancedSettings();
          for (const key of keys) {
            settingsDraft.advanced[key] = defaults[key];
            delete settingsErrors[key];
          }
        }
        function resetSettingsPageDraft() {
          switch (settingsPage) {
            case "commands":
              resetFeatureDraft([FEATURE_LOBBY_COMMANDS]);
              resetAdvancedDraft([ADVANCED_COMMAND_ALIASES, ADVANCED_BLACKLIST_ENFORCEMENT]);
              break;
            case "audio":
              resetFeatureDraft([FEATURE_AUDIO]);
              break;
            case "advanced":
              resetAdvancedDraft(ADVANCED_TIMING_KEYS);
              break;
            case "features":
              resetFeatureDraft(FEATURE_PAGE_KEYS);
              break;
            case "about":
            default:
              break;
          }
          renderQolboxMenu();
        }
        function saveSettingsDraft() {
          const sanitized = validateSettingsDraft();
          if (!settingsDraft || !sanitized) {
            renderQolboxMenu();
            return;
          }
          const featureDraft = { ...settingsDraft.features };
          options.onCommitSettingsDraft(featureDraft, sanitized);
          closeQolboxMenu();
        }
        function handleQolboxMenuClick(event) {
          if (mode !== "closed") {
            event.stopPropagation();
          }
          const actionElement = event.target instanceof Element ? event.target.closest("[data-qolbox-action]") : null;
          if (!actionElement) {
            return;
          }
          const action = actionElement.dataset.qolboxAction;
          event.preventDefault();
          event.stopImmediatePropagation();
          switch (action) {
            case "set-feature":
              options.onSetFeatureEnabled(actionElement.dataset.feature, actionElement.dataset.enabled === "true");
              break;
            case "draft-feature":
              if (settingsDraft && isKnownFeature(actionElement.dataset.feature || "")) {
                settingsDraft.features[actionElement.dataset.feature] = actionElement.dataset.enabled === "true";
                renderQolboxMenu();
              }
              break;
            case "draft-advanced":
              updateDraftAdvancedValue(actionElement.dataset.advanced, actionElement.dataset.value);
              renderQolboxMenu();
              break;
            case "settings-page":
              if (isSettingsPage(actionElement.dataset.page)) {
                settingsPage = actionElement.dataset.page;
                renderQolboxMenu();
              }
              break;
            case "reset-page":
              resetSettingsPageDraft();
              break;
            case "save-settings":
              saveSettingsDraft();
              break;
            case "cancel-settings":
              closeQolboxMenu();
              break;
            case "choose-express":
              options.onChooseExpressSetup();
              onboardingStepIndex = options.getOnboardingStepCount() - 1;
              renderQolboxMenu();
              break;
            case "choose-custom":
              onboardingStepIndex = Math.min(1, options.getOnboardingStepCount() - 1);
              renderQolboxMenu();
              break;
            case "next":
              onboardingStepIndex = Math.min(onboardingStepIndex + 1, options.getOnboardingStepCount() - 1);
              renderQolboxMenu();
              break;
            case "back":
              onboardingStepIndex = Math.max(0, onboardingStepIndex - 1);
              renderQolboxMenu();
              break;
            case "skip-onboarding":
            case "finish-onboarding":
              completeOnboarding();
              break;
            case "acknowledge-update":
              options.onAcknowledgeUpdateNotice();
              closeQolboxMenu();
              break;
            case "update-newer":
              updateNoticePageIndex = Math.max(0, updateNoticePageIndex - 1);
              renderQolboxMenu();
              break;
            case "update-older":
              updateNoticePageIndex = Math.min(
                Math.max(1, options.getUpdateNoticePageCount()) - 1,
                updateNoticePageIndex + 1
              );
              renderQolboxMenu();
              break;
            case "redo-onboarding":
              openQolboxMenu("onboarding");
              break;
            default:
              break;
          }
        }
        function ensureQolboxMenu() {
          return ensureQolboxMenuOverlay({
            menuId: options.menuId,
            onClick: handleQolboxMenuClick,
            onInput: handleQolboxMenuInput,
            onPointerEvent: stopQolboxMenuPointerEvent
          });
        }
        function isSettingsPage(value) {
          return value === "features" || value === "commands" || value === "audio" || value === "advanced" || value === "about";
        }
        function handleQolboxMenuInput(event) {
          if (mode !== "settings" || !settingsDraft || !(event.target instanceof HTMLInputElement || event.target instanceof HTMLSelectElement)) {
            return;
          }
          const key = event.target.dataset.qolboxAdvancedInput;
          if (!key) {
            return;
          }
          updateDraftAdvancedValue(key, event.target.value);
        }
        function handleQolboxMenuKey(event) {
          if (mode !== "closed" && isEscapeKey(event)) {
            event.preventDefault();
            event.stopImmediatePropagation();
            closeQolboxMenu();
            return;
          }
          if (mode === "update" && (isArrowLeftKey(event) || isArrowRightKey(event))) {
            const action = isArrowLeftKey(event) ? "update-older" : "update-newer";
            const actionElement = document.querySelector(
              `#${options.menuId} [data-qolbox-action="${action}"]:not([disabled])`
            );
            if (actionElement) {
              event.preventDefault();
              event.stopImmediatePropagation();
              actionElement.click();
            }
            return;
          }
          if (mode !== "closed" && isEnterKey(event)) {
            const activeElement = document.activeElement;
            const actionElement = activeElement instanceof HTMLElement && activeElement.closest(`#${options.menuId}`) && activeElement.matches("[data-qolbox-action]:not([disabled])") ? activeElement : document.querySelector(
              `#${options.menuId} .qolboxMenuButton.primary:not([disabled]), #${options.menuId} .qolboxMenuChoice.primary:not([disabled])`
            );
            if (actionElement) {
              event.preventDefault();
              event.stopImmediatePropagation();
              actionElement.click();
            }
            return;
          }
          if (!isQolboxMenuShortcut(event, options.menuKey)) {
            return;
          }
          event.preventDefault();
          event.stopImmediatePropagation();
          if (mode === "settings") {
            closeQolboxMenu();
            return;
          }
          if (mode === "onboarding") {
            return;
          }
          openQolboxMenu(onboardingComplete ? "settings" : "onboarding");
        }
        function installQolboxMenuHooks() {
          if (hooksInstalled) {
            return;
          }
          hooksInstalled = true;
          window.addEventListener("keydown", handleQolboxMenuKey, true);
          document.addEventListener("keydown", handleQolboxMenuKey, true);
        }
        function showFirstBootOnboarding() {
          if (onboardingComplete || mode !== "closed") {
            return;
          }
          openQolboxMenu("onboarding");
        }
        function showUpdateNotice() {
          if (!onboardingComplete || mode !== "closed") {
            return;
          }
          openQolboxMenu("update");
        }
        return {
          closeQolboxMenu,
          getMode,
          installQolboxMenuHooks,
          isClosed,
          isOnboardingComplete,
          openQolboxMenu,
          renderQolboxMenu,
          showFirstBootOnboarding,
          showUpdateNotice
        };
      }

      // src/features/qolbox-menu-markup.ts
      var SETTINGS_PAGES = [
        { key: "features", title: "Features" },
        { key: "commands", title: "Commands" },
        { key: "audio", title: "Audio" },
        { key: "advanced", title: "Advanced" },
        { key: "about", title: "About" }
      ];
      var SETTINGS_PAGE_TITLES = {
        features: "Feature Settings",
        commands: "Command Settings",
        audio: "Audio Settings",
        advanced: "Advanced Settings",
        about: "About QOLBox"
      };
      var FEATURE_PAGE_KEYS2 = [
        FEATURE_FULLSCREEN,
        FEATURE_RESERVE,
        FEATURE_CHAT,
        FEATURE_GAME_START_ALERT,
        FEATURE_EDITOR_MAP_TRANSFER,
        FEATURE_MOBILE_GRAB
      ];
      var ADVANCED_TIMING_KEYS2 = [
        ADVANCED_RESERVE_RETRY_INTERVAL_MS,
        ADVANCED_ALERT_DELAY_MS,
        ADVANCED_ALERT_FLASH_INTERVAL_MS,
        ADVANCED_TYPING_DURATION_MS
      ];
      var GREASYFORK_ICON_DATA_URI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3ggEBCQHM3fXsAAAAVdJREFUOMudkz2qwkAUhc/goBaGJBgUtBCZyj0ILkpwAW7Bws4yO3AHLiCtEFD8KVREkoiFxZzX5A2KGfN4F04zMN+ce+5c4LMUgDmANYBnrnV+plBSi+FwyHq9TgA2LQpvCiEiABwMBtzv95RSfoNEHy8DYBzHrNVqVEr9BWKcqNFoxF6vx3a7zc1mYyC73a4MogBg7vs+z+czO50OW60Wt9stK5UKp9Mpj8cjq9WqDTBHnjAdxzGQZrPJw+HA31oulzbAWgLoA0CWZVBKIY5jzGYzdLtdE9DlcrFNrY98zobqOA6TJKHW2jg4nU5sNBpFDp6mhVe5rsvVasUwDHm9Xqm15u12o+/7Hy0gD8KatOd5vN/v1FozTVN6nkchxFuI6hsAAIMg4OPxMJCXdtTbR7JJCMEgCJhlGUlyPB4XfumozInrupxMJpRSRtZlKoNYl+m/6/wDuWAjtPfsQuwAAAAASUVORK5CYII=";
      function getAdvancedSettingDefinition2(key) {
        return ADVANCED_SETTING_DEFINITIONS.find((definition) => definition.key === key);
      }
      function getFeatureDefinition(featureDefinitions, featureKey) {
        return featureDefinitions.find((feature) => feature.key === featureKey);
      }
      function createQolboxMenuMarkup(options) {
        function getOnboardingSteps() {
          const featureSteps = options.featureDefinitions.map((feature) => ({
            type: "feature",
            featureKey: feature.key,
            title: feature.title,
            text: feature.onboardingText || feature.summary
          }));
          return [
            {
              type: "intro",
              title: "Welcome to QOLBox",
              text: "QOLBox is a hitbox.io userscript with fullscreen layout, reserve spots in full lobbies, audio controls, away-tab alerts, mobile Grab, readable chat, lobby commands, and map import/export."
            },
            ...featureSteps,
            {
              type: "finish",
              title: "QOLBox is ready",
              text: `On desktop, press ${options.menuKeyLabel} to open QOLBox later. On mobile, open the site's hamburger dropdown and choose QOLBox. You can change features and advanced settings there any time.`
            }
          ];
        }
        function getToggleMarkup({
          action,
          active,
          ariaLabel,
          dataName,
          dataValue
        }) {
          return `
          <div class="qolboxMenuToggleGroup" role="group" aria-label="${escapeMenuText(ariaLabel)}">
            <button class="qolboxMenuToggle${active ? " active" : ""}" data-qolbox-action="${action}" ${dataName}="${escapeMenuText(dataValue)}" data-enabled="true" data-value="true" aria-pressed="${active ? "true" : "false"}">Enabled</button>
            <button class="qolboxMenuToggle${active ? "" : " active"}" data-qolbox-action="${action}" ${dataName}="${escapeMenuText(dataValue)}" data-enabled="false" data-value="false" aria-pressed="${active ? "false" : "true"}">Off</button>
          </div>
        `;
        }
        function getOnboardingToggleMarkup(featureKey) {
          return getToggleMarkup({
            action: "set-feature",
            active: options.isFeatureEnabled(featureKey),
            ariaLabel: `${featureKey} setting`,
            dataName: "data-feature",
            dataValue: featureKey
          });
        }
        function getDraftFeatureToggleMarkup(featureKey, draft) {
          return getToggleMarkup({
            action: "draft-feature",
            active: draft.features[featureKey] !== false,
            ariaLabel: `${featureKey} setting`,
            dataName: "data-feature",
            dataValue: featureKey
          });
        }
        function getOnboardingSummaryMarkup() {
          const enabledFeatures = options.featureDefinitions.filter((feature) => options.isFeatureEnabled(feature.key)).map((feature) => feature.shortTitle).join(", ");
          return `
          <div class="qolboxMenuInfoBox">
            <div class="qolboxMenuFeatureName">Enabled features</div>
            <div class="qolboxMenuFeatureSummary">${escapeMenuText(enabledFeatures || "No optional features enabled")}</div>
          </div>
        `;
        }
        function getOnboardingStepMarkup(onboardingStepIndex) {
          const steps = getOnboardingSteps();
          const step = steps[Math.max(0, Math.min(onboardingStepIndex, steps.length - 1))];
          const isFeatureStep = step.type === "feature";
          const isFirstStep = onboardingStepIndex === 0;
          const isFinalStep = onboardingStepIndex === steps.length - 1;
          const progress = steps.map((_, index) => `<span class="qolboxMenuDot${index === onboardingStepIndex ? " active" : ""}"></span>`).join("");
          if (isFirstStep) {
            return `
            <div class="qolboxMenuBody">
              <div class="qolboxMenuHeaderLine">
                <h1 class="qolboxMenuTitle">${escapeMenuText(step.title)}</h1>
              </div>
              <p class="qolboxMenuText">${escapeMenuText(step.text)}</p>
              <div class="qolboxMenuChoiceGrid">
                <button class="qolboxMenuChoice primary" data-qolbox-action="choose-express">
                  <span>Express</span>
                  <small>Recommended defaults. You can change everything later.</small>
                </button>
                <button class="qolboxMenuChoice" data-qolbox-action="choose-custom">
                  <span>Custom</span>
                  <small>Review each feature during setup.</small>
                </button>
              </div>
              <div class="qolboxMenuActions">
                <button class="qolboxMenuButton" data-qolbox-action="skip-onboarding">Skip</button>
              </div>
            </div>
          `;
          }
          return `
          <div class="qolboxMenuBody">
            <div class="qolboxMenuHeaderLine">
              <h1 class="qolboxMenuTitle">${escapeMenuText(step.title)}</h1>
            </div>
            <p class="qolboxMenuText">${escapeMenuText(step.text)}</p>
            ${isFeatureStep && step.featureKey ? getOnboardingToggleMarkup(step.featureKey) : getOnboardingSummaryMarkup()}
            <div class="qolboxMenuProgress" aria-hidden="true">${progress}</div>
            <div class="qolboxMenuActions">
              <button class="qolboxMenuButton" data-qolbox-action="back">Back</button>
              <button class="qolboxMenuButton primary" data-qolbox-action="${isFinalStep ? "finish-onboarding" : "next"}">${isFinalStep ? "Finish" : "Next"}</button>
            </div>
          </div>
        `;
        }
        function getSettingsTabsMarkup(activePage) {
          return `
          <div class="qolboxMenuTabs" role="tablist" aria-label="QOLBox settings sections">
            ${SETTINGS_PAGES.map((page) => `
              <button class="qolboxMenuTab${page.key === activePage ? " active" : ""}" role="tab" aria-selected="${page.key === activePage ? "true" : "false"}" data-qolbox-action="settings-page" data-page="${page.key}">${escapeMenuText(page.title)}</button>
            `).join("")}
          </div>
        `;
        }
        function getFeatureRowMarkup(featureKey, draft) {
          const feature = getFeatureDefinition(options.featureDefinitions, featureKey);
          return `
          <div class="qolboxMenuFeatureRow">
            <div>
              <div class="qolboxMenuFeatureName">${escapeMenuText(feature.title)}</div>
              <div class="qolboxMenuFeatureSummary">${escapeMenuText(feature.summary)}</div>
            </div>
            ${getDraftFeatureToggleMarkup(feature.key, draft)}
          </div>
        `;
        }
        function getAdvancedInputMarkup(definition, draft, errors) {
          const value = draft.advanced[definition.key];
          const error = errors[definition.key];
          const invalidClass = error ? " invalid" : "";
          if (definition.kind === "boolean") {
            const enabled = value === true || value === "true";
            return getToggleMarkup({
              action: "draft-advanced",
              active: enabled,
              ariaLabel: `${definition.title} setting`,
              dataName: "data-advanced",
              dataValue: definition.key
            });
          }
          return `
          <input class="qolboxMenuInput${invalidClass}" type="number" value="${escapeMenuText(String(value))}" min="${definition.min}" max="${definition.max}" step="${definition.step}" data-qolbox-advanced-input="${escapeMenuText(definition.key)}">
          ${error ? `<div class="qolboxMenuFieldError">${escapeMenuText(error)}</div>` : ""}
        `;
        }
        function getAdvancedRowMarkup(key, draft, errors) {
          const definition = getAdvancedSettingDefinition2(key);
          const rowKindClass = definition.kind === "boolean" ? " boolean" : " numeric";
          return `
          <div class="qolboxMenuFeatureRow compact${rowKindClass}">
            <div>
              <div class="qolboxMenuFeatureName">${escapeMenuText(definition.title)}</div>
              <div class="qolboxMenuFeatureSummary">${escapeMenuText(definition.description)}</div>
            </div>
            <div class="qolboxMenuFieldControl">
              ${getAdvancedInputMarkup(definition, draft, errors)}
            </div>
          </div>
        `;
        }
        function getFeaturePageMarkup(draft) {
          return `
          <div class="qolboxMenuSettingsList">
            ${FEATURE_PAGE_KEYS2.map((featureKey) => getFeatureRowMarkup(featureKey, draft)).join("")}
          </div>
          <div class="qolboxMenuActions slim">
            <button class="qolboxMenuButton" data-qolbox-action="reset-page">Reset Features</button>
          </div>
        `;
        }
        function getCommandsPageMarkup(draft, errors) {
          return `
          <div class="qolboxMenuSettingsList">
            ${getFeatureRowMarkup(FEATURE_LOBBY_COMMANDS, draft)}
            ${getAdvancedRowMarkup(ADVANCED_COMMAND_ALIASES, draft, errors)}
            ${getAdvancedRowMarkup(ADVANCED_BLACKLIST_ENFORCEMENT, draft, errors)}
          </div>
          <div class="qolboxMenuInfoBox">Special targets: /spec all|playing, /join all|spectators, and /red or /blue all|playing|spectators. Quote those words to use them as player names. Named targets for /spec, /join, /red, /blue, /host, /kick, and /ban accept exact or unique partial names. /blacklist stores exact names for host bans.</div>
          <div class="qolboxMenuActions slim">
            <button class="qolboxMenuButton" data-qolbox-action="reset-page">Reset Commands</button>
          </div>
        `;
        }
        function getAudioPageMarkup(draft) {
          return `
          <div class="qolboxMenuSettingsList">
            ${getFeatureRowMarkup(FEATURE_AUDIO, draft)}
          </div>
          <div class="qolboxMenuInfoBox">Adjust game and jukebox volume from Hitbox's hamburger menu.</div>
          <div class="qolboxMenuActions slim">
            <button class="qolboxMenuButton" data-qolbox-action="reset-page">Reset Audio</button>
          </div>
        `;
        }
        function getAdvancedPageMarkup(draft, errors) {
          return `
          <div class="qolboxMenuSettingsList">
            ${ADVANCED_TIMING_KEYS2.map((key) => getAdvancedRowMarkup(key, draft, errors)).join("")}
          </div>
          <div class="qolboxMenuActions slim">
            <button class="qolboxMenuButton" data-qolbox-action="reset-page">Reset Advanced</button>
          </div>
        `;
        }
        function getCreditsMarkup() {
          return `
          <div class="qolboxMenuAboutLinks">
            <a class="qolboxMenuCredit" href="${escapeMenuText(options.greaseForkUrl)}" target="_blank" rel="noreferrer">
              <img class="qolboxMenuCreditIcon" src="${GREASYFORK_ICON_DATA_URI}" alt="" aria-hidden="true">
              <span>GreasyFork</span>
            </a>
            <a class="qolboxMenuCredit" href="${escapeMenuText(options.githubUrl)}" target="_blank" rel="noreferrer">
              <svg class="qolboxMenuCreditSvg" viewBox="0 0 16 16" aria-hidden="true"><path fill="currentColor" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38v-1.49c-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82A7.65 7.65 0 0 1 8 3.86c.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48v2.2c0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z"/></svg>
              <span>GitHub</span>
            </a>
          </div>
        `;
        }
        function getAboutPageMarkup() {
          return `
          <div class="qolboxMenuInfoBox">
            <div class="qolboxMenuFeatureName">QOLBox ${escapeMenuText(options.versionLabel)}</div>
            <div class="qolboxMenuFeatureSummary">Fullscreen layout, reserve spots, audio controls, away-tab alerts, mobile Grab, readable chat, lobby commands, and map import/export for hitbox.io.</div>
          </div>
          ${getCreditsMarkup()}
        `;
        }
        function getSettingsPageMarkup(draft, page, errors) {
          switch (page) {
            case "commands":
              return getCommandsPageMarkup(draft, errors);
            case "audio":
              return getAudioPageMarkup(draft);
            case "advanced":
              return getAdvancedPageMarkup(draft, errors);
            case "about":
              return getAboutPageMarkup();
            case "features":
            default:
              return getFeaturePageMarkup(draft);
          }
        }
        function getSettingsMenuMarkup(draft, page, errors) {
          const pageTitle = SETTINGS_PAGES.find((candidate) => candidate.key === page)?.title || "Features";
          const settingsTitle = SETTINGS_PAGE_TITLES[page];
          return `
          <div class="qolboxMenuBody">
            <div class="qolboxMenuHeaderLine">
              <h1 class="qolboxMenuTitle">${escapeMenuText(settingsTitle)}</h1>
            </div>
            ${getSettingsTabsMarkup(page)}
            <div class="qolboxMenuPage" aria-label="${escapeMenuText(pageTitle)} settings">
              ${getSettingsPageMarkup(draft, page, errors)}
            </div>
            <div class="qolboxMenuActions">
              <button class="qolboxMenuButton" data-qolbox-action="redo-onboarding">Redo Setup</button>
              <button class="qolboxMenuButton" data-qolbox-action="cancel-settings">Cancel</button>
              <button class="qolboxMenuButton primary" data-qolbox-action="save-settings">OK</button>
            </div>
          </div>
        `;
        }
        function getReleaseSourceText(release) {
          switch (release.source) {
            case "github":
              return "GitHub release";
            case "greasyfork":
              return "GreasyFork history";
            case "local-fallback":
            default:
              return "";
          }
        }
        function getReleaseDateText(release) {
          if (!release.publishedAt) {
            return "";
          }
          const timestamp = Date.parse(release.publishedAt);
          return Number.isFinite(timestamp) ? ` - ${new Date(timestamp).toLocaleDateString()}` : "";
        }
        function getUpdateRangeMarkup(notice) {
          return `
          <div class="qolboxMenuUpdateRange" aria-label="Updated from ${escapeMenuText(notice.previousVersion)} to ${escapeMenuText(notice.currentVersion)}">
            <span class="qolboxMenuUpdateLabel">Updated</span>
            <span class="qolboxMenuVersionPill old">${escapeMenuText(notice.previousVersion)}</span>
            <span class="qolboxMenuVersionArrow" aria-hidden="true">&rarr;</span>
            <span class="qolboxMenuVersionPill current">${escapeMenuText(notice.currentVersion)}</span>
          </div>
        `;
        }
        function getUpdateNoticeMarkup(notice, releaseHistory, pageIndex) {
          if (releaseHistory.status === "loading") {
            return `
            <div class="qolboxMenuBody">
              <div class="qolboxMenuHeaderLine">
                <h1 class="qolboxMenuTitle">QOLBox Updated</h1>
              </div>
              ${getUpdateRangeMarkup(notice)}
              <div class="qolboxMenuLoading" role="status" aria-live="polite">
                <span class="qolboxMenuSpinner" aria-hidden="true"></span>
                <span>Loading update notes from GitHub and GreasyFork...</span>
              </div>
            </div>
          `;
          }
          const releaseNotes = releaseHistory.notes;
          const safePageIndex = Math.max(0, Math.min(pageIndex, Math.max(0, releaseNotes.length - 1)));
          const release = releaseNotes[safePageIndex] || null;
          const releaseSourceText = release ? `${getReleaseSourceText(release)}${getReleaseDateText(release)}`.trim() : "";
          const notes = release ? `
              <div class="qolboxMenuInfoBox">
                <div class="qolboxMenuFeatureName">${escapeMenuText(release.version)}</div>
                ${releaseSourceText ? `<div class="qolboxMenuFeatureSummary">${escapeMenuText(releaseSourceText)}</div>` : ""}
                <ul class="qolboxMenuNoteList">
                  ${release.notes.map((note) => `<li>${escapeMenuText(note)}</li>`).join("")}
                </ul>
              </div>
            ` : '<p class="qolboxMenuText">No update notes are available for this version range.</p>';
          const pageCount = Math.max(1, releaseNotes.length);
          const chronologicalPageNumber = releaseNotes.length ? pageCount - safePageIndex : 0;
          return `
          <div class="qolboxMenuBody">
            <div class="qolboxMenuHeaderLine">
              <h1 class="qolboxMenuTitle">QOLBox Updated</h1>
            </div>
            ${getUpdateRangeMarkup(notice)}
            ${notes}
            <div class="qolboxMenuHeaderLine">
              <button class="qolboxMenuButton" data-qolbox-action="update-older" ${safePageIndex >= releaseNotes.length - 1 ? "disabled" : ""}>Older</button>
              <span class="qolboxMenuFeatureSummary">Version ${chronologicalPageNumber} of ${pageCount}</span>
              <button class="qolboxMenuButton" data-qolbox-action="update-newer" ${safePageIndex <= 0 ? "disabled" : ""}>Newer</button>
            </div>
            <div class="qolboxMenuActions">
              <button class="qolboxMenuButton primary" data-qolbox-action="acknowledge-update">OK</button>
            </div>
          </div>
        `;
        }
        return {
          getOnboardingStepMarkup,
          getOnboardingSteps,
          getSettingsMenuMarkup,
          getUpdateNoticeMarkup
        };
      }

      // src/features/qolbox-menu-feature-bundle.ts
      function createQolboxMenuFeatureBundle(options) {
        const initialOnboardingComplete = loadOnboardingComplete();
        let pendingUpdateNotice = loadPendingUpdateNotice(void 0, initialOnboardingComplete);
        let updateReleaseHistory = pendingUpdateNotice ? createInitialReleaseHistoryState(pendingUpdateNotice.previousVersion, pendingUpdateNotice.currentVersion) : null;
        let updateReleaseHistoryRefreshStarted = false;
        const { getOnboardingStepMarkup, getOnboardingSteps, getSettingsMenuMarkup, getUpdateNoticeMarkup } = createQolboxMenuMarkup({
          featureDefinitions: FEATURE_DEFINITIONS,
          greaseForkUrl: QOLBOX_GREASYFORK_URL,
          githubUrl: QOLBOX_GITHUB_URL,
          isFeatureEnabled: options.isFeatureEnabled,
          menuKeyLabel: MENU_KEY_LABEL,
          versionLabel: QOLBOX_VERSION_LABEL
        });
        function createSettingsDraft() {
          const features = {};
          for (const definition of FEATURE_DEFINITIONS) {
            features[definition.key] = options.isFeatureEnabled(definition.key);
          }
          return {
            advanced: { ...options.getAdvancedSettings() },
            features
          };
        }
        const menuController = createQolboxMenuController({
          createSettingsDraft,
          getOnboardingStepMarkup,
          getOnboardingStepCount: () => getOnboardingSteps().length,
          getSettingsMenuMarkup,
          getUpdateNoticeMarkup: (pageIndex) => pendingUpdateNotice ? getUpdateNoticeMarkup(
            pendingUpdateNotice,
            updateReleaseHistory || createInitialReleaseHistoryState(
              pendingUpdateNotice.previousVersion,
              pendingUpdateNotice.currentVersion
            ),
            pageIndex
          ) : getSettingsMenuMarkup(createSettingsDraft(), "features", {}),
          getUpdateNoticePageCount: () => Math.max(1, updateReleaseHistory?.notes.length || 1),
          initialOnboardingComplete,
          menuId: QOLBOX_MENU_ID,
          menuKey: MENU_KEY,
          onAcknowledgeUpdateNotice: () => {
            acknowledgeUpdateNotice();
            pendingUpdateNotice = null;
            updateReleaseHistory = null;
          },
          onBeforeOpen: options.ensureGlobalStyle,
          onChooseExpressSetup: () => {
            options.setAllFeatureSettings(getDefaultFeatureSettings());
          },
          onCompleteOnboarding: () => {
            saveOnboardingComplete();
            options.applyFeatureRootClasses();
            options.applyPersistentFeatures();
            options.scheduleUiWork({ force: true, features: true, passes: FULLSCREEN_SETTLE_PASSES });
          },
          onCommitSettingsDraft: (features, advanced) => {
            options.setAllFeatureSettings(features);
            options.setAdvancedSettings(advanced);
          },
          onMenuModeChanged: options.applyFeatureRootClasses,
          onSetFeatureEnabled: options.setFeatureEnabled
        });
        const { scheduleFirstBootOnboarding } = createFirstBootOnboardingScheduler({
          isOnboardingComplete: menuController.isOnboardingComplete,
          showFirstBootOnboarding: menuController.showFirstBootOnboarding
        });
        function scheduleStartupQolboxNotice() {
          if (!menuController.isOnboardingComplete()) {
            scheduleFirstBootOnboarding();
            return;
          }
          if (!pendingUpdateNotice) {
            return;
          }
          refreshUpdateReleaseHistory();
          const show = () => {
            window.setTimeout(menuController.showUpdateNotice, 0);
          };
          if (document.readyState === "loading") {
            document.addEventListener("DOMContentLoaded", show, { once: true });
          } else {
            show();
          }
        }
        function refreshUpdateReleaseHistory() {
          if (!pendingUpdateNotice || updateReleaseHistoryRefreshStarted) {
            return;
          }
          updateReleaseHistoryRefreshStarted = true;
          loadReleaseHistoryState(pendingUpdateNotice.previousVersion, pendingUpdateNotice.currentVersion).then((nextHistory) => {
            updateReleaseHistory = nextHistory;
            if (menuController.getMode() === "update") {
              menuController.renderQolboxMenu();
            }
          }).catch(() => {
          });
        }
        return {
          ...menuController,
          getOnboardingSteps,
          scheduleFirstBootOnboarding: scheduleStartupQolboxNotice
        };
      }

      // src/features/feature-root-classes.ts
      function getFeatureRootClass(featureKey) {
        return `qolbox-feature-${featureKey}`;
      }
      function createFeatureRootClassController(options) {
        function applyFeatureRootClasses() {
          const root = document.documentElement;
          if (!root || !root.classList) {
            return;
          }
          for (const feature of options.featureDefinitions) {
            root.classList.toggle(getFeatureRootClass(feature.key), options.isFeatureActive(feature.key));
          }
          root.classList.toggle(options.menuRootClass, !options.isMenuClosed());
        }
        return {
          applyFeatureRootClasses
        };
      }

      // src/features/global-style-fullscreen.ts
      function prefixSelectorList(prefix, selectorList) {
        return selectorList.split(",").map((selector) => `${prefix} ${selector.trim()}`).join(",\n      ");
      }
      function getFullscreenGlobalStyleText(options) {
        return `
          html.qolbox-feature-fullscreen,
          html.qolbox-feature-fullscreen body {
            width: 100vw !important;
            height: 100vh !important;
            margin: 0 !important;
            overflow: hidden !important;
            background: #0a0a0a !important;
          }

          html.qolbox-feature-fullscreen #appContainer,
          html.qolbox-feature-fullscreen #relativeContainer {
            margin: 0 !important;
            max-width: none !important;
            max-height: none !important;
            border: 0 !important;
          }

          html.qolbox-feature-fullscreen #backgroundImage,
          html.qolbox-feature-fullscreen .mainMenuFancy {
            position: fixed !important;
            left: 0 !important;
            top: 0 !important;
            right: auto !important;
            bottom: auto !important;
            width: 100vw !important;
            height: 100vh !important;
            max-width: none !important;
            max-height: none !important;
          }

          ${prefixSelectorList("html.qolbox-feature-fullscreen", options.fullscreenRenderLayerSelector)} {
            position: absolute !important;
            margin: 0 !important;
            max-width: none !important;
            max-height: none !important;
            overflow: hidden !important;
            transform: none !important;
          }

          html.qolbox-feature-fullscreen #editorContainer {
            overflow: visible !important;
            transform-origin: top left !important;
          }

          ${prefixSelectorList("html.qolbox-feature-fullscreen", options.fullscreenRenderCanvasSelector)} {
            display: block !important;
            max-width: none !important;
            max-height: none !important;
            transform: none !important;
          }

          /* Keep game keyboard focus after chat closes without drawing a browser focus ring over the playfield. */
          ${prefixSelectorList("html.qolbox-feature-chat", options.fullscreenRenderCanvasFocusSelector)} {
            outline: 0 !important;
            outline-color: transparent !important;
            outline-style: none !important;
            outline-width: 0 !important;
          }

          html.qolbox-feature-fullscreen .scores {
            display: none !important;
          }

          html.qolbox-feature-fullscreen .spectateControls {
            bottom: 12px !important;
          }

          html.qolbox-feature-fullscreen .scores .title {
            background-color: rgb(56, 56, 56) !important;
          }

          html.qolbox-feature-fullscreen .scores .title,
          html.qolbox-feature-fullscreen .scores .entryContainer,
          html.qolbox-feature-fullscreen .scores .entryContainer .number,
          html.qolbox-feature-fullscreen .scores .entryContainer .name {
            vertical-align: middle !important;
          }
        `;
      }

      // src/features/global-style-reserve.ts
      function getReserveGlobalStyleText() {
        return `
          html.qolbox-feature-reserve body.qolbox-reserve-active .connectingWindowContainer:not(.qolboxReserveWindowContainer) {
            display: none !important;
          }

          .qolboxReserveWindowContainer {
            display: none;
            z-index: 10000;
          }

          html.qolbox-feature-reserve body.qolbox-reserve-active .qolboxReserveWindowContainer {
            display: block !important;
          }

          html.qolbox-feature-reserve .roomListContainer .bottomButton.right.qolboxReserveUnavailable {
            cursor: not-allowed !important;
            filter: grayscale(1) saturate(0.35) !important;
            opacity: 0.48 !important;
          }

          .qolboxReserveWindowContainer .qolboxReserveContent {
            align-items: center;
            bottom: 48px;
            display: flex;
            flex-direction: column;
            gap: 4px;
            justify-content: center;
            left: 16px;
            pointer-events: none;
            position: absolute;
            right: 16px;
            text-align: center;
            top: 50px;
          }

          .qolboxReserveWindowContainer .connectingWindow .spinner {
            bottom: auto !important;
            flex: 0 0 auto;
            left: auto !important;
            margin: 0 auto;
            order: 2;
            position: static !important;
            right: auto !important;
            top: auto !important;
          }

          .qolboxReserveWindowContainer .qolboxReserveStatus,
          .qolboxReserveWindowContainer .qolboxReserveCountdown,
          .qolboxReserveWindowContainer .qolboxReserveMessage {
            width: 100%;
          }

          .qolboxReserveWindowContainer .qolboxReserveStatus {
            color: rgb(205, 210, 218);
            font-size: 11px;
            line-height: 14px;
            min-height: 14px;
            order: 1;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
          }

          .qolboxReserveWindowContainer .qolboxReserveCountdown {
            color: rgb(242, 242, 242);
            font-size: 13px;
            line-height: 16px;
            min-height: 16px;
            order: 3;
            white-space: nowrap;
          }

          .qolboxReserveWindowContainer .qolboxReserveMessage {
            color: rgb(242, 242, 242);
            font-size: 13px;
            line-height: 16px;
            order: 1;
            white-space: normal;
          }
    `;
      }

      // src/features/global-style-chat.ts
      function getChatGlobalStyleText() {
        return `
          html.qolbox-feature-chat .inGameChat {
            pointer-events: none;
          }

          html.qolbox-feature-chat .inGameChat.qolboxChatInteractive {
            pointer-events: auto;
          }

          html.qolbox-feature-chat .inGameChat .input {
            pointer-events: none;
          }

          html.qolbox-feature-chat .inGameChat .input:focus,
          html.qolbox-feature-chat .inGameChat .input.bgActive {
            pointer-events: auto;
          }

          html.qolbox-feature-chat .inGameChat:hover,
          html.qolbox-feature-chat .inGameChat.qolboxChatReading {
            opacity: 1 !important;
          }

          html.qolbox-feature-chat .inGameChat.qolboxChatReading {
            overflow: hidden !important;
            overscroll-behavior: contain;
          }
        `;
      }

      // src/features/global-style-menu.ts
      function getQolboxMenuGlobalStyleText() {
        return `
          .qolboxMenuOverlay {
            align-items: center;
            background: rgba(0, 0, 0, 0.72);
            box-sizing: border-box;
            display: flex;
            font-family: inherit;
            inset: 0;
            justify-content: center;
            opacity: 0;
            padding: 10px;
            pointer-events: none;
            position: fixed;
            z-index: 2147483647;
          }

          html.qolbox-menu-open .qolboxMenuOverlay {
            opacity: 1;
            pointer-events: auto;
          }

          .qolboxMenuPanel {
            background: rgba(22, 24, 28, 0.98);
            border: 2px solid rgb(69, 75, 86);
            border-radius: 4px;
            box-shadow: 0 8px 28px rgba(0, 0, 0, 0.55);
            box-sizing: border-box;
            color: #f4f4f4;
            display: flex;
            flex-direction: column;
            max-height: calc(100vh - 20px);
            max-width: min(430px, calc(100vw - 20px));
            overflow: hidden;
            width: 430px;
          }

          .qolboxMenuBody {
            box-sizing: border-box;
            display: flex;
            flex: 1 1 auto;
            flex-direction: column;
            gap: 8px;
            min-height: 0;
            overflow: auto;
            padding: 12px;
          }

          .qolboxMenuTitle {
            color: #ffffff;
            font-size: 18px;
            font-weight: 700;
            letter-spacing: 0;
            line-height: 22px;
            margin: 0;
          }

          .qolboxMenuHeaderLine {
            align-items: center;
            display: flex;
            gap: 8px;
            justify-content: space-between;
          }

          .qolboxMenuText {
            color: #d7dbe1;
            font-size: 12px;
            line-height: 16px;
            margin: 0;
          }

          .qolboxMenuUpdateRange {
            align-items: center;
            display: flex;
            flex-wrap: wrap;
            gap: 6px;
          }

          .qolboxMenuUpdateLabel {
            color: #c4c9d1;
            font-size: 10px;
            font-weight: 700;
            line-height: 13px;
            text-transform: uppercase;
          }

          .qolboxMenuVersionPill {
            background: rgb(31, 34, 39);
            border: 1px solid rgb(72, 78, 89);
            border-radius: 3px;
            color: #f4f4f4;
            font-size: 12px;
            font-weight: 700;
            line-height: 15px;
            padding: 4px 7px;
          }

          .qolboxMenuVersionPill.current {
            border-color: rgba(245, 197, 66, 0.8);
            color: #f5c542;
          }

          .qolboxMenuVersionArrow {
            color: #c4c9d1;
            font-size: 13px;
            font-weight: 700;
            line-height: 15px;
          }

          .qolboxMenuProgress {
            align-items: center;
            display: flex;
            gap: 4px;
            margin-top: 2px;
          }

          .qolboxMenuDot {
            background: rgba(255, 255, 255, 0.25);
            border-radius: 999px;
            height: 5px;
            width: 12px;
          }

          .qolboxMenuDot.active {
            background: #f5c542;
          }

          .qolboxMenuToggleGroup {
            background: rgb(31, 34, 39);
            border: 1px solid rgb(72, 78, 89);
            border-radius: 3px;
            display: grid;
            grid-template-columns: 1fr 1fr;
            overflow: hidden;
          }

          .qolboxMenuButton,
          .qolboxMenuTab,
          .qolboxMenuToggle {
            align-items: center;
            appearance: none;
            border: 0;
            box-sizing: border-box;
            cursor: pointer;
            display: inline-flex;
            font-family: inherit;
            font-size: 12px;
            font-weight: 700;
            justify-content: center;
            letter-spacing: 0;
            line-height: 14px;
            min-height: 30px;
          }

          .qolboxMenuToggle {
            background: transparent;
            color: #cfd3da;
            padding: 0 8px;
          }

          .qolboxMenuToggle + .qolboxMenuToggle {
            border-left: 1px solid rgba(255, 255, 255, 0.14);
          }

          .qolboxMenuToggle.active {
            background: #f5c542;
            color: #111111;
          }

          .qolboxMenuActions {
            display: flex;
            gap: 6px;
            justify-content: flex-end;
            margin-top: 4px;
          }

          .qolboxMenuActions.slim {
            margin-top: 0;
          }

          .qolboxMenuButton {
            background: rgb(47, 51, 58);
            border: 1px solid rgb(92, 98, 108);
            border-radius: 3px;
            color: #f4f4f4;
            min-width: 72px;
            padding: 0 12px;
          }

          .qolboxMenuButton.primary {
            background: #f5c542;
            color: #111111;
          }

          .qolboxMenuButton:disabled {
            cursor: default;
            opacity: 0.45;
          }

          .qolboxMenuSettingsList {
            display: grid;
            gap: 6px;
          }

          .qolboxMenuTabs {
            border-bottom: 1px solid rgba(255, 255, 255, 0.12);
            display: flex;
            flex-wrap: wrap;
            gap: 4px;
            justify-content: center;
            padding-bottom: 8px;
          }

          .qolboxMenuTab {
            background: rgb(31, 34, 39);
            border: 1px solid rgb(72, 78, 89);
            border-radius: 3px;
            color: #cfd3da;
            flex: 0 1 calc((100% - 8px) / 3);
            font-size: 11px;
            line-height: 13px;
            min-width: 0;
            padding: 0 6px;
          }

          .qolboxMenuTab.active {
            background: #f5c542;
            border-color: #f5c542;
            color: #111111;
          }

          .qolboxMenuPage {
            align-content: start;
            display: grid;
            gap: 8px;
            min-height: 172px;
          }

          .qolboxMenuChoiceGrid {
            display: grid;
            gap: 6px;
            grid-template-columns: 1fr 1fr;
          }

          .qolboxMenuChoice {
            appearance: none;
            background: rgb(47, 51, 58);
            border: 1px solid rgb(92, 98, 108);
            border-radius: 3px;
            color: #f4f4f4;
            cursor: pointer;
            display: grid;
            font-family: inherit;
            gap: 3px;
            min-height: 62px;
            padding: 9px;
            text-align: left;
          }

          .qolboxMenuChoice.primary {
            border-color: #f5c542;
          }

          .qolboxMenuChoice span {
            color: #ffffff;
            font-size: 13px;
            font-weight: 700;
            line-height: 15px;
          }

          .qolboxMenuChoice small {
            color: #c4c9d1;
            font-size: 10px;
            line-height: 13px;
          }

          .qolboxMenuFeatureRow {
            align-items: center;
            border-bottom: 1px solid rgba(255, 255, 255, 0.09);
            display: grid;
            gap: 8px;
            grid-template-columns: minmax(0, 1fr) 108px;
            padding: 0 0 6px;
          }

          .qolboxMenuFeatureRow.compact {
            grid-template-columns: minmax(0, 1fr) 150px;
          }

          .qolboxMenuFeatureRow.compact.boolean {
            grid-template-columns: minmax(0, 1fr) 108px;
          }

          .qolboxMenuFeatureName {
            color: #ffffff;
            font-size: 12px;
            font-weight: 700;
            line-height: 15px;
          }

          .qolboxMenuFeatureSummary {
            color: #c4c9d1;
            font-size: 10px;
            line-height: 13px;
            margin-top: 1px;
          }

          .qolboxMenuFieldControl {
            display: grid;
            gap: 3px;
          }

          .qolboxMenuInput {
            appearance: none;
            background: rgb(31, 34, 39);
            border: 1px solid rgb(72, 78, 89);
            border-radius: 3px;
            box-sizing: border-box;
            color: #f4f4f4;
            font-family: inherit;
            font-size: 12px;
            height: 30px;
            line-height: 16px;
            min-height: 30px;
            min-width: 0;
            padding: 0 6px;
            width: 100%;
          }

          .qolboxMenuInput.invalid {
            border-color: #f05f57;
          }

          .qolboxMenuFieldError {
            color: #ffaaa4;
            font-size: 10px;
            line-height: 12px;
          }

          .qolboxMenuWarning,
          .qolboxMenuInfoBox {
            background: rgba(255, 255, 255, 0.06);
            border: 1px solid rgba(255, 255, 255, 0.12);
            border-radius: 3px;
            color: #d7dbe1;
            font-size: 10px;
            line-height: 13px;
            padding: 6px;
          }

          .qolboxMenuWarning {
            border-color: rgba(245, 197, 66, 0.45);
          }

          .qolboxMenuNoteList {
            color: #d7dbe1;
            font-size: 11px;
            line-height: 15px;
            margin: 5px 0 0;
            padding-left: 16px;
          }

          .qolboxMenuLoading {
            align-items: center;
            background: rgba(255, 255, 255, 0.06);
            border: 1px solid rgba(255, 255, 255, 0.12);
            border-radius: 3px;
            color: #d7dbe1;
            display: flex;
            font-size: 11px;
            gap: 9px;
            line-height: 15px;
            min-height: 54px;
            padding: 8px;
          }

          .qolboxMenuSpinner {
            animation: qolboxMenuSpin 0.8s linear infinite;
            border: 2px solid rgba(255, 255, 255, 0.28);
            border-radius: 50%;
            border-top-color: #f5c542;
            box-sizing: border-box;
            flex: 0 0 auto;
            height: 18px;
            width: 18px;
          }

          @keyframes qolboxMenuSpin {
            to {
              transform: rotate(360deg);
            }
          }

          .qolboxMenuAboutLinks {
            display: grid;
            gap: 6px;
          }

          .qolboxMenuCredit {
            align-items: center;
            background: rgb(31, 34, 39);
            border: 1px solid rgb(72, 78, 89);
            border-radius: 3px;
            color: #e6e9ee;
            display: flex;
            gap: 8px;
            font-size: 10px;
            font-weight: 700;
            line-height: 14px;
            min-height: 30px;
            padding: 0 8px;
            text-decoration: none;
          }

          .qolboxMenuCreditIcon {
            background: rgba(255, 255, 255, 0.12);
            border-radius: 2px;
            display: block;
            flex: 0 0 auto;
            height: 18px;
            object-fit: contain;
            padding: 1px;
            width: 18px;
          }

          .qolboxMenuCreditSvg {
            fill: currentColor;
            height: 16px;
            width: 16px;
          }

          @media (max-height: 620px) {
            .qolboxMenuBody {
              gap: 6px;
              padding: 9px;
            }
          }

          @media (max-width: 420px) {
            .qolboxMenuTab {
              flex-basis: calc((100% - 4px) / 2);
            }

            .qolboxMenuChoiceGrid,
            .qolboxMenuFeatureRow,
            .qolboxMenuFeatureRow.compact {
              grid-template-columns: 1fr;
            }
          }

          @media (prefers-reduced-motion: reduce) {
            .qolboxMenuOverlay,
            .qolboxMenuSpinner {
              transition: none !important;
            }

            .qolboxMenuSpinner {
              animation-duration: 1.6s;
            }
          }
        `;
      }

      // src/features/global-style-mobile-grab.ts
      function getMobileGrabGlobalStyleText(options) {
        return `
          .buttonArea.qolboxMobileGrabButton {
            background-image: url("${options.mobileGrabIconHref}") !important;
            background-position: center center !important;
            background-repeat: no-repeat !important;
            background-size: 68% !important;
            box-sizing: border-box !important;
            display: none;
            transform: none !important;
            z-index: 12;
          }
        `;
      }

      // src/features/global-style-typing.ts
      function getTypingGlobalStyleText() {
        return `
          .scores .entryContainer .qolboxTypingIndicator {
            background-image: url("graphics/ui/typing.svg");
            background-position: center center;
            background-repeat: no-repeat;
            background-size: contain;
            display: inline-block;
            height: 14px;
            margin-left: 5px;
            pointer-events: none;
            vertical-align: -2px;
            width: 14px;
          }

          @supports ((-webkit-mask-image: url("graphics/ui/typing.svg")) or (mask-image: url("graphics/ui/typing.svg"))) {
            .scores .entryContainer .qolboxTypingIndicator {
              background-color: currentColor;
              background-image: none;
              -webkit-mask-image: url("graphics/ui/typing.svg");
              mask-image: url("graphics/ui/typing.svg");
              -webkit-mask-position: center center;
              mask-position: center center;
              -webkit-mask-repeat: no-repeat;
              mask-repeat: no-repeat;
              -webkit-mask-size: contain;
              mask-size: contain;
            }
          }

          .qolboxWorldTypingLayer {
            left: 0;
            pointer-events: none;
            position: fixed;
            top: 0;
            z-index: 12;
          }

          .qolboxWorldTypingIndicator {
            background-color: rgba(37, 38, 42, 0.82);
            background-image: url("graphics/ui/typing.svg");
            background-position: center center;
            background-repeat: no-repeat;
            background-size: 14px 14px;
            border-radius: 3px;
            box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
            height: 18px;
            pointer-events: none;
            position: fixed;
            transform: translate(-50%, -100%);
            width: 22px;
          }
        `;
      }

      // src/features/global-style-editor-map.ts
      function getEditorMapGlobalStyleText() {
        return `
          .qolboxEditorMapStatus {
            background: rgba(22, 24, 28, 0.96);
            border: 1px solid rgb(92, 98, 108);
            border-radius: 3px;
            box-shadow: 0 4px 14px rgba(0, 0, 0, 0.42);
            box-sizing: border-box;
            color: #f4f4f4;
            font-family: inherit;
            font-size: 12px;
            font-weight: 700;
            left: 50%;
            line-height: 15px;
            max-width: calc(100vw - 20px);
            opacity: 0;
            padding: 6px 10px;
            pointer-events: none;
            position: fixed;
            top: 36px;
            transform: translateX(-50%);
            transition: opacity 120ms ease;
            z-index: 2147483646;
          }

          .qolboxEditorMapStatus.visible {
            opacity: 1;
          }

          .qolboxEditorMapStatus.error {
            border-color: rgba(240, 95, 87, 0.8);
            color: #ffaaa4;
          }
        `;
      }

      // src/features/global-style.ts
      function getGlobalStyleText(options) {
        return `
          ${getFullscreenGlobalStyleText(options)}

          ${getTypingGlobalStyleText()}

          ${getChatGlobalStyleText()}

          .qolboxSwitchTeamsButton.qolboxSwitchTeamsButtonBusy {
            cursor: not-allowed !important;
            opacity: 0.62;
          }

          html.qolbox-feature-fullscreen #email,
          html.qolbox-feature-fullscreen #songcredit,
          html.qolbox-feature-fullscreen #betaLink {
            display: none !important;
          }

          ${getReserveGlobalStyleText()}

          ${getQolboxMenuGlobalStyleText()}

          ${getMobileGrabGlobalStyleText(options)}

          ${getEditorMapGlobalStyleText()}
        `;
      }
      function createGlobalStyleController(options) {
        function ensureGlobalStyle() {
          if (document.getElementById(options.styleId)) {
            return true;
          }
          const styleHost = document.head || document.documentElement;
          if (!styleHost) {
            return false;
          }
          const style = document.createElement("style");
          style.id = options.styleId;
          style.textContent = getGlobalStyleText(options);
          styleHost.appendChild(style);
          return true;
        }
        return {
          ensureGlobalStyle
        };
      }

      // src/features/qolbox-shell-feature-bundle.ts
      function createQolboxShellFeatureBundle(options) {
        const { ensureGlobalStyle } = createGlobalStyleController({
          styleId: "qolbox-style",
          fullscreenRenderLayerSelector: FULLSCREEN_RENDER_LAYER_SELECTOR,
          fullscreenRenderCanvasSelector: FULLSCREEN_RENDER_CANVAS_SELECTOR,
          fullscreenRenderCanvasFocusSelector: FULLSCREEN_RENDER_CANVAS_FOCUS_SELECTOR,
          mobileGrabIconHref: MOBILE_GRAB_ICON_HREF
        });
        const { applyFeatureRootClasses } = createFeatureRootClassController({
          featureDefinitions: FEATURE_DEFINITIONS,
          isMenuClosed: options.isMenuClosed,
          isFeatureActive: options.isFeatureActive,
          menuRootClass: QOLBOX_MENU_ROOT_CLASS
        });
        return {
          applyFeatureRootClasses,
          ensureGlobalStyle
        };
      }

      // src/hitbox/auto-join-adapter.ts
      function getNativeAutoJoin() {
        return readNativeProperty(window, "autoJoin");
      }
      function isNativeAutoJoinMatch(joinId, password) {
        const autoJoin = getNativeAutoJoin();
        if (!isNativeObject(autoJoin)) {
          return false;
        }
        return joinId === readNativeProperty(autoJoin, "address") && password === readNativeProperty(autoJoin, "passbypass");
      }
      function isNativeAutoJoinOnePersonRoom() {
        const autoJoin = getNativeAutoJoin();
        if (!isNativeObject(autoJoin)) {
          return false;
        }
        const maxPlayers = Number(
          readNativeProperty(autoJoin, "maxPlayers") || readNativeProperty(autoJoin, "maxplayers") || readNativeProperty(autoJoin, "max")
        );
        return Number.isFinite(maxPlayers) && maxPlayers === 1;
      }

      // src/hitbox/reserve-socket-emit-patcher.ts
      function isNativeReserveCallable(value) {
        return typeof value === "function";
      }
      function patchReserveSocketEmitTarget(target, options) {
        if (!target) {
          return false;
        }
        const nativeEmit = readNativeReflectProperty(target, "emit");
        if (readNativeReflectProperty(target, "__qolboxReservePatched") || !isNativeReserveCallable(nativeEmit)) {
          return false;
        }
        const baseEmit = nativeEmit;
        function wrappedReserveEmit(eventName, ...args) {
          if (options.shouldCaptureJoin(args)) {
            options.onJoin(this, eventName, args);
          }
          return Reflect.apply(baseEmit, this, [eventName, ...args]);
        }
        setNativeReflectProperty(target, "emit", wrappedReserveEmit);
        setNativeReflectProperty(target, "__qolboxReservePatched", true);
        setNativeReflectProperty(target, options.originalEmitKey, baseEmit);
        return true;
      }

      // src/hitbox/reserve-socket-adapter.ts
      function emitReserveSocketJoinAttempt(attempt, options) {
        const emit = readNativeProperty(attempt?.socket, "emit");
        if (!attempt || !isNativeReserveCallable(emit)) {
          return false;
        }
        const connect = readNativeProperty(attempt.socket, "connect");
        if (!readNativeProperty(attempt.socket, "connected") && isNativeReserveCallable(connect)) {
          try {
            Reflect.apply(connect, attempt.socket, []);
          } catch {
            return false;
          }
        }
        try {
          options.beforeEmit();
          Reflect.apply(emit, attempt.socket, [attempt.eventName, ...attempt.args.map(options.cloneValue)]);
          return true;
        } catch {
          return false;
        }
      }
      function createReserveSocketCaptureHook(options) {
        let socketHookInstalled = false;
        function patchSocket(socket) {
          patchReserveSocketEmitTarget(socket, {
            onJoin: options.onJoin,
            originalEmitKey: "__qolboxReserveOriginalEmit",
            shouldCaptureJoin: options.shouldCaptureJoin
          });
          return socket;
        }
        function patchSocketPrototype(ioFactory) {
          const prototype = readNativeReflectProperty(readNativeReflectProperty(ioFactory, "Socket"), "prototype");
          if (!prototype) {
            return;
          }
          patchReserveSocketEmitTarget(prototype, {
            onJoin: options.onJoin,
            originalEmitKey: "__qolboxReserveOriginalEmit",
            shouldCaptureJoin: options.shouldCaptureJoin
          });
        }
        function patchIo(ioFactory) {
          if (!isNativeReserveCallable(ioFactory) || readNativeReflectProperty(ioFactory, "__qolboxReservePatched")) {
            patchSocketPrototype(ioFactory);
            return ioFactory;
          }
          const baseIoFactory = ioFactory;
          function wrappedReserveIo(...args) {
            return patchSocket(Reflect.apply(baseIoFactory, this, args));
          }
          try {
            Object.setPrototypeOf(wrappedReserveIo, Object.getPrototypeOf(baseIoFactory));
          } catch {
          }
          for (const key of Reflect.ownKeys(baseIoFactory)) {
            try {
              setNativeReflectProperty(wrappedReserveIo, key, readNativeReflectProperty(baseIoFactory, key));
            } catch {
            }
          }
          setNativeReflectProperty(wrappedReserveIo, "__qolboxReservePatched", true);
          setNativeReflectProperty(wrappedReserveIo, "__qolboxReserveOriginal", baseIoFactory);
          patchSocketPrototype(wrappedReserveIo);
          return wrappedReserveIo;
        }
        function installReserveSocketCaptureHook() {
          if (socketHookInstalled) {
            return;
          }
          socketHookInstalled = true;
          try {
            let ioValue = readNativeReflectProperty(window, "io");
            Object.defineProperty(window, "io", {
              configurable: true,
              enumerable: true,
              get() {
                return ioValue;
              },
              set(value) {
                ioValue = patchIo(value);
              }
            });
            if (ioValue) {
              setNativeReflectProperty(window, "io", ioValue);
            }
          } catch {
            const ioValue = readNativeReflectProperty(window, "io");
            if (ioValue) {
              setNativeReflectProperty(window, "io", patchIo(ioValue));
            }
          }
        }
        return {
          installReserveSocketCaptureHook
        };
      }

      // src/features/reserve-action-controls.ts
      var SELECTED_RESERVE_ROW_SELECTOR = ".roomListContainer .scrollBox tr.SELECTED";
      function getText(element) {
        return (element.textContent || "").trim();
      }
      function setDatasetValue(element, key, value) {
        if (hasDataset(element)) {
          element.dataset[key] = value;
        }
      }
      function createReserveActionControls(options) {
        let passwordPromptPending = false;
        function clearReservePasswordPromptPending() {
          passwordPromptPending = false;
        }
        function isReservePasswordPromptPending() {
          return passwordPromptPending;
        }
        function setReservePasswordPromptPending(pending) {
          passwordPromptPending = Boolean(pending);
        }
        function syncReserveJoinButtonLabel() {
          const button = options.getReserveJoinButton();
          if (!(button instanceof Element)) {
            return;
          }
          if (!options.isEnabled()) {
            setDatasetValue(button, "qolboxReserveFull", "false");
            setDatasetValue(button, "qolboxReserveUnavailable", "false");
            button.classList.remove("qolboxReserveUnavailable");
            button.removeAttribute("aria-disabled");
            if (getText(button) === options.reserveButtonText) {
              button.textContent = options.joinButtonText;
            }
            return;
          }
          const selectedState = options.getReserveSelectedRoomState();
          const shouldReserve = selectedState.full || selectedState.unavailable;
          const isUnavailable = selectedState.unavailable;
          const nextText = shouldReserve ? options.reserveButtonText : options.joinButtonText;
          if (getText(button) !== nextText) {
            button.textContent = nextText;
          }
          setDatasetValue(button, "qolboxReserveFull", shouldReserve ? "true" : "false");
          setDatasetValue(button, "qolboxReserveUnavailable", isUnavailable ? "true" : "false");
          button.classList.toggle("qolboxReserveUnavailable", isUnavailable);
          button.setAttribute("aria-disabled", isUnavailable ? "true" : "false");
        }
        function syncReservePasswordPrompt() {
          if (!options.isEnabled()) {
            clearReservePasswordPromptPending();
            return;
          }
          const container = document.querySelector(".passwordWindowContainer");
          const joinButton = container?.querySelector(".joinButton") || null;
          if (!options.isElementVisible(container) || !joinButton) {
            clearReservePasswordPromptPending();
            return;
          }
          if (passwordPromptPending && getText(joinButton) !== options.reserveButtonText) {
            joinButton.textContent = options.reserveButtonText;
          }
        }
        function clearReserveVisibleRoomSelection() {
          for (const row of document.querySelectorAll(SELECTED_RESERVE_ROW_SELECTOR)) {
            row.classList.remove("SELECTED");
          }
          syncReserveJoinButtonLabel();
        }
        return {
          clearReservePasswordPromptPending,
          clearReserveVisibleRoomSelection,
          isReservePasswordPromptPending,
          setReservePasswordPromptPending,
          syncReserveJoinButtonLabel,
          syncReservePasswordPrompt
        };
      }

      // src/features/reserve-join-payload.ts
      function cloneReserveJoinValue(value) {
        try {
          const cloned = JSON.parse(JSON.stringify(value));
          return cloned;
        } catch {
          return value;
        }
      }
      function isReserveJoinPayload(value) {
        return Boolean(
          isReflectableObject(value) && (typeof getReserveJoinPayloadJoinId(value) === "string" || Object.prototype.hasOwnProperty.call(value, "playerName") && Object.prototype.hasOwnProperty.call(value, "peerID") && Object.prototype.hasOwnProperty.call(value, "password"))
        );
      }
      function getReserveJoinPayload(args) {
        return args.find(isReserveJoinPayload) || null;
      }
      function getReserveJoinPayloadJoinId(payload) {
        return readObjectProperty(payload, "joinID");
      }
      function getReserveJoinPayloadPassword(payload) {
        return readObjectProperty(payload, "password");
      }

      // src/features/reserve-captured-join.ts
      function isAutoReserveJoin(payload, options) {
        if (!payload) {
          return false;
        }
        return options.isAutoJoinMatch(getReserveJoinPayloadJoinId(payload), getReserveJoinPayloadPassword(payload));
      }
      function createReserveCapturedJoinController(options) {
        let capturedJoin = null;
        function clearReserveCapturedJoin() {
          capturedJoin = null;
        }
        function getReserveCapturedJoin() {
          return capturedJoin;
        }
        function getRetryCapturedJoin() {
          return options.getState()?.capturedJoin || capturedJoin;
        }
        function captureReserveJoin(socket, eventName, args) {
          if (!options.isEnabled()) {
            return;
          }
          const payload = getReserveJoinPayload(args);
          if (!payload) {
            return;
          }
          capturedJoin = {
            socket,
            eventName,
            args: args.map(cloneReserveJoinValue),
            autoReserve: isAutoReserveJoin(payload, options),
            time: Date.now()
          };
          const state = options.getState();
          if (state?.active) {
            state.capturedJoin = capturedJoin;
          }
          options.onCaptured();
        }
        function shouldWatchRecentReserveCapture() {
          return Boolean(
            capturedJoin && Date.now() - capturedJoin.time < options.capturedJoinFreshMs && !options.hasSuccessfulJoinLayer()
          );
        }
        function canAutoReserveCapturedJoin() {
          return Boolean(options.getState()?.active || capturedJoin?.autoReserve);
        }
        function emitReserveJoinAttempt() {
          return emitReserveSocketJoinAttempt(getRetryCapturedJoin(), {
            beforeEmit: options.suppressRetryAudio,
            cloneValue: cloneReserveJoinValue
          });
        }
        return {
          canAutoReserveCapturedJoin,
          captureReserveJoin,
          clearReserveCapturedJoin,
          emitReserveJoinAttempt,
          getReserveCapturedJoin,
          shouldWatchRecentReserveCapture
        };
      }

      // src/features/reserve-connecting-state.ts
      function createReserveConnectingStateController(options) {
        function handleReserveConnectingState() {
          if (!options.isEnabled()) {
            if (options.getState()) {
              options.stopReserveSpot();
            }
            return;
          }
          const nativeText = options.getNativeConnectingText();
          if (options.isRoomFullSuppressed() && options.hasSuccessfulJoinLayer() && options.roomFullPattern.test(nativeText)) {
            options.hideNativeConnectingWindows();
            return;
          }
          if (options.getState()?.active && options.hasSuccessfulJoinLayer()) {
            options.stopAfterSuccessfulJoin();
            return;
          }
          if (options.getState()?.active && options.roomClosedPattern.test(nativeText)) {
            options.stopReserveSpot();
            return;
          }
          if (options.getState()?.active && options.wrongPasswordPattern.test(nativeText)) {
            options.showTerminalMessage("wrong-password", options.getReserveNativeMessage(options.wrongPasswordPattern));
            return;
          }
          const canAutoReserve = options.canAutoReserveCapturedJoin();
          if (options.roomFullPattern.test(nativeText) && canAutoReserve) {
            if (options.isAutoJoinOnePersonRoom()) {
              options.showOnePersonUnavailable();
              options.hideNativeConnectingWindows();
              return;
            }
            options.startReserveSpot("room-full");
            options.scheduleReserveRetry();
          }
        }
        return {
          handleReserveConnectingState
        };
      }

      // src/features/reserve-countdown-timer.ts
      function createReserveCountdownTimer(options) {
        let countdownTimer = 0;
        function clearReserveCountdownTimer() {
          if (countdownTimer) {
            window.clearTimeout(countdownTimer);
            countdownTimer = 0;
          }
        }
        function scheduleReserveCountdownUpdate() {
          if (countdownTimer || !options.getState()?.active) {
            return;
          }
          countdownTimer = window.setTimeout(() => {
            countdownTimer = 0;
            if (!options.getState()?.active) {
              return;
            }
            options.onTick();
            scheduleReserveCountdownUpdate();
          }, options.intervalMs);
        }
        return {
          clearReserveCountdownTimer,
          scheduleReserveCountdownUpdate
        };
      }

      // src/features/reserve-dom-event-hooks.ts
      function createReserveDomEventHooks(options) {
        let domEventsInstalled = false;
        function installReserveDomEventHooks() {
          if (domEventsInstalled) {
            return;
          }
          domEventsInstalled = true;
          document.addEventListener("click", options.onRoomListClick, true);
          document.addEventListener("dblclick", options.onRoomListDoubleClick, true);
          document.addEventListener("click", options.onPasswordSubmit, true);
          window.addEventListener("keyup", options.onPasswordKey, true);
        }
        return {
          installReserveDomEventHooks
        };
      }

      // src/features/reserve-feature-patch.ts
      function createReserveFeaturePatchController(options) {
        function shouldContinueReserveStatusWatch() {
          if (options.getState()?.active) {
            return true;
          }
          if (options.isRoomFullSuppressed()) {
            return true;
          }
          return options.shouldWatchRecentCapture();
        }
        function patchReserveSpotFeature() {
          if (!options.isEnabled()) {
            options.syncJoinButtonLabel();
            return;
          }
          options.installSocketCaptureHook();
          options.syncJoinButtonLabel();
          options.syncPasswordPrompt();
          options.handleConnectingState();
          options.installDomEventHooks();
        }
        return {
          patchReserveSpotFeature,
          shouldContinueReserveStatusWatch
        };
      }

      // src/features/reserve-interaction-events.ts
      function getClosestReserveJoinButton(target) {
        return target instanceof Element ? target.closest(".roomListContainer .bottomButton.right") : null;
      }
      function getReservePasswordSubmitButton(target) {
        return target instanceof Element ? target.closest(".passwordWindowContainer .joinButton") : null;
      }
      function getReserveEventKey(event) {
        const key = readObjectProperty(event, "key");
        return typeof key === "string" ? key : "";
      }
      function stopReserveNativeEvent(event) {
        event.preventDefault();
        event.stopImmediatePropagation();
      }
      function clickReserveElement(element) {
        const click = readObjectProperty(element, "click");
        if (typeof click === "function") {
          Reflect.apply(click, element, []);
        }
      }

      // src/features/reserve-interaction-handlers.ts
      function createReserveInteractionHandlers(options) {
        function scheduleJoinButtonSync() {
          window.setTimeout(options.syncJoinButtonLabel, 0);
        }
        function schedulePasswordPromptSync() {
          window.setTimeout(options.syncPasswordPrompt, 0);
        }
        function markPasswordPromptPending() {
          options.setPasswordPromptPending(true);
          schedulePasswordPromptSync();
        }
        function showSelectedUnavailable(event) {
          stopReserveNativeEvent(event);
          options.clearPasswordPromptPending();
          options.showOnePersonUnavailable(options.getSelectedRoomRow());
        }
        function cancelReserveSpot() {
          if (options.getState()?.unavailable) {
            options.stopReserveSpot({ clearSelection: true });
            return;
          }
          const cancelButton = options.getNativeConnectingWindows().map((windowElement) => windowElement.querySelector(".cancelButton")).find(Boolean);
          if (cancelButton) {
            clickReserveElement(cancelButton);
          }
          options.stopReserveSpot();
        }
        function handleReserveRoomListClick(event) {
          if (!options.isEnabled()) {
            return;
          }
          const row = options.getRowFromTarget(event.target);
          const joinButton = getClosestReserveJoinButton(event.target);
          if (row) {
            options.rememberSelectedRoom(row);
            scheduleJoinButtonSync();
            if (options.isUnavailableRoom(row)) {
              options.showOnePersonUnavailable(row);
              if (joinButton) {
                stopReserveNativeEvent(event);
              }
            }
          }
          if (!joinButton) {
            return;
          }
          const selectedState = options.getSelectedRoomState();
          const selectedRow = selectedState.row;
          if (selectedState.unavailable) {
            stopReserveNativeEvent(event);
            options.showOnePersonUnavailable(selectedRow);
            return;
          }
          if (!selectedState.full) {
            options.clearPasswordPromptPending();
            return;
          }
          if (options.isPasswordRoom(selectedRow)) {
            markPasswordPromptPending();
            return;
          }
          options.startReserveSpot("room-list");
        }
        function handleReserveRoomListDoubleClick(event) {
          if (!options.isEnabled()) {
            return;
          }
          const row = options.getRowFromTarget(event.target);
          if (!options.isRoomFull(row)) {
            return;
          }
          options.rememberSelectedRoom(row);
          if (options.isUnavailableRoom(row)) {
            stopReserveNativeEvent(event);
            options.showOnePersonUnavailable(row);
            return;
          }
          if (options.isPasswordRoom(row)) {
            markPasswordPromptPending();
            return;
          }
          options.startReserveSpot("room-list");
        }
        function handleReservePasswordSubmit(event) {
          if (!options.isEnabled()) {
            return;
          }
          const submitButton = getReservePasswordSubmitButton(event.target);
          if (!submitButton || !options.isPasswordPromptPending()) {
            return;
          }
          if (options.isUnavailableRoom(options.getSelectedRoomRow())) {
            showSelectedUnavailable(event);
            return;
          }
          options.clearPasswordPromptPending();
          options.startReserveSpot("password-room");
        }
        function handleReservePasswordKey(event) {
          if (!options.isEnabled()) {
            return;
          }
          const passwordWindow = document.querySelector(".passwordWindowContainer");
          if (getReserveEventKey(event) !== "Enter" || !options.isPasswordPromptPending() || !options.isElementVisible(passwordWindow)) {
            return;
          }
          if (options.isUnavailableRoom(options.getSelectedRoomRow())) {
            showSelectedUnavailable(event);
            return;
          }
          options.clearPasswordPromptPending();
          options.startReserveSpot("password-room");
        }
        return {
          cancelReserveSpot,
          handleReservePasswordKey,
          handleReservePasswordSubmit,
          handleReserveRoomListClick,
          handleReserveRoomListDoubleClick
        };
      }

      // src/features/reserve-lifecycle.ts
      function createReserveLifecycleController(options) {
        let reserveState = null;
        function getReserveState() {
          return reserveState;
        }
        function startReserveSpot(reason) {
          if (!options.isEnabled()) {
            return;
          }
          if (!reserveState?.active) {
            reserveState = {
              active: true,
              unavailable: false,
              reason,
              retryTimer: 0,
              nextRetryAt: Date.now() + options.getRetryDelayMs(),
              retries: 0,
              capturedJoin: options.getCapturedJoin(),
              lastStatusText: ""
            };
          } else {
            reserveState.reason = reserveState.reason || reason;
            reserveState.capturedJoin = reserveState.capturedJoin || options.getCapturedJoin();
            reserveState.nextRetryAt = reserveState.nextRetryAt || Date.now() + options.getRetryDelayMs();
            reserveState.unavailable = false;
          }
          options.updateWaitingWindow();
          options.setWaitingVisible(true);
          options.scheduleStatusWatch();
          options.scheduleCountdownUpdate();
        }
        function stopReserveSpot({ hideNative = false, clearCaptured = true, clearSelection = false } = {}) {
          options.clearRetryTimer(reserveState);
          options.clearStatusWatchTimer();
          options.clearCountdownTimer();
          if (clearCaptured) {
            options.clearCapturedJoin();
          }
          reserveState = null;
          options.clearPasswordPromptPending();
          options.setWaitingVisible(false);
          if (hideNative) {
            options.hideNativeConnectingWindows();
          }
          if (clearSelection) {
            options.clearVisibleRoomSelection();
          } else {
            options.syncJoinButtonLabel();
          }
        }
        function showReserveOnePersonUnavailable(row = null) {
          if (!options.isEnabled()) {
            return;
          }
          if (row) {
            options.rememberSelectedRoom(row);
          }
          options.clearRetryTimer(reserveState);
          options.clearStatusWatchTimer();
          options.clearCountdownTimer();
          options.clearCapturedJoin();
          options.clearPasswordPromptPending();
          reserveState = {
            active: false,
            unavailable: true,
            reason: "one-person-room",
            message: options.onePersonText
          };
          options.updateWaitingWindow();
          options.setWaitingVisible(true);
          options.syncJoinButtonLabel();
        }
        function showReserveTerminalMessage(reason, message) {
          if (!options.isEnabled()) {
            return;
          }
          options.clearRetryTimer(reserveState);
          options.clearStatusWatchTimer();
          options.clearCountdownTimer();
          options.clearCapturedJoin();
          options.clearPasswordPromptPending();
          reserveState = {
            active: false,
            unavailable: false,
            terminal: true,
            reason,
            message: message || options.statusFallbackText
          };
          options.updateWaitingWindow();
          options.setWaitingVisible(true);
          options.hideNativeConnectingWindows();
          options.syncJoinButtonLabel();
        }
        function stopReserveAfterSuccessfulJoin() {
          options.suppressRoomFullAfterJoin();
          stopReserveSpot({ hideNative: true });
          options.scheduleStatusWatch();
        }
        return {
          getReserveState,
          showReserveOnePersonUnavailable,
          showReserveTerminalMessage,
          startReserveSpot,
          stopReserveAfterSuccessfulJoin,
          stopReserveSpot
        };
      }

      // src/features/reserve-room-list.ts
      function getCell(row, index) {
        const cells = readObjectProperty(row, "cells");
        return readObjectProperty(cells, index);
      }
      function getCellText(row, index) {
        const text = readObjectProperty(getCell(row, index), "textContent");
        return typeof text === "string" ? text.trim() : "";
      }
      function hasAtLeastTwoCells(row) {
        const cells = readObjectProperty(row, "cells");
        const length = Number(readObjectProperty(cells, "length"));
        return Number.isFinite(length) && length >= 2;
      }
      function getReserveRowFromTarget(target) {
        return target instanceof Element ? target.closest(".roomListContainer .scrollBox tr") : null;
      }
      function getReserveRoomSignature(row) {
        if (!row || !getCell(row, 0)) {
          return "";
        }
        const roomName = getCellText(row, 0);
        const lockState = isReservePasswordRoom(row) ? "locked" : "open";
        return `${roomName}
    ${lockState}`;
      }
      function findReserveRoomBySignature(signature) {
        if (!signature) {
          return null;
        }
        return [...document.querySelectorAll(".roomListContainer .scrollBox tr")].find((row) => {
          return row.isConnected && getReserveRoomSignature(row) === signature;
        }) || null;
      }
      function parseReserveRoomPlayers(row) {
        if (!hasAtLeastTwoCells(row)) {
          return null;
        }
        const match = getCellText(row, 1).match(/^(\d+)\s*\/\s*(\d+)$/);
        if (!match) {
          return null;
        }
        return {
          current: Number(match[1]),
          max: Number(match[2])
        };
      }
      function isReserveRoomFull(row) {
        const players = parseReserveRoomPlayers(row);
        return Boolean(players && players.max > 0 && players.current >= players.max);
      }
      function isReserveOnePersonRoom(row) {
        const players = parseReserveRoomPlayers(row);
        return Boolean(players && players.max === 1);
      }
      function isReserveUnavailableRoom(row) {
        return Boolean(isReserveRoomFull(row) && isReserveOnePersonRoom(row));
      }
      function isReservePasswordRoom(row) {
        return Boolean(row instanceof Element && row.querySelector('img[src*="lock"]'));
      }
      function createReserveRoomList(options) {
        function getReserveJoinButton() {
          const button = document.querySelector(".roomListContainer .bottomButton.right");
          return options.isElementVisible(button) ? button : null;
        }
        return {
          getReserveJoinButton
        };
      }

      // src/features/reserve-room-full-suppression.ts
      function createReserveRoomFullSuppression(options) {
        let suppressUntil = 0;
        function isReserveJoinedRoomFullSuppressed() {
          return Date.now() < suppressUntil;
        }
        function suppressReserveRoomFullAfterJoin() {
          suppressUntil = Date.now() + options.suppressMs;
        }
        return {
          isReserveJoinedRoomFullSuppressed,
          suppressReserveRoomFullAfterJoin
        };
      }

      // src/features/reserve-retry-audio-suppression.ts
      function createReserveRetryAudioSuppression(options) {
        let suppressUntil = 0;
        function isReserveRetryAudioSuppressed() {
          return Date.now() < suppressUntil;
        }
        function suppressReserveRetryAudio() {
          suppressUntil = Date.now() + options.suppressMs;
        }
        return {
          isReserveRetryAudioSuppressed,
          suppressReserveRetryAudio
        };
      }

      // src/features/reserve-retry-scheduler.ts
      function createReserveRetryScheduler(options) {
        function clearReserveRetryTimer(state = options.getState()) {
          if (state?.retryTimer) {
            window.clearTimeout(state.retryTimer);
            state.retryTimer = 0;
          }
        }
        function scheduleReserveRetry() {
          const state = options.getState();
          if (!options.isEnabled() || !state?.active || state.retryTimer) {
            return;
          }
          const retryDelayMs = options.getRetryDelayMs();
          state.nextRetryAt = Date.now() + retryDelayMs;
          options.updateWaitingWindow();
          options.scheduleCountdownUpdate();
          state.retryTimer = window.setTimeout(() => {
            const currentState = options.getState();
            if (!currentState?.active) {
              return;
            }
            currentState.retryTimer = 0;
            currentState.nextRetryAt = 0;
            options.updateWaitingWindow();
            if (options.hasSuccessfulJoinLayer()) {
              options.onSuccessfulJoin();
              return;
            }
            if (options.emitJoinAttempt()) {
              currentState.retries = (currentState.retries || 0) + 1;
            }
            scheduleReserveRetry();
          }, retryDelayMs);
        }
        return {
          clearReserveRetryTimer,
          scheduleReserveRetry
        };
      }

      // src/features/reserve-native-status.ts
      function getWindowLines(windowElement) {
        const textElement = windowElement.querySelector(".textBox") || windowElement;
        return (textElement.textContent || "").split(/\r?\n/);
      }
      function normalizeLine(line) {
        return line.replace(/\s+/g, " ").trim();
      }
      function setDisplayNone(element) {
        if (isStyledElement(element)) {
          element.style.display = "none";
        }
      }
      function createReserveNativeStatus(options) {
        function getNativeConnectingWindows() {
          return [...document.querySelectorAll(".connectingWindowContainer:not(.qolboxReserveWindowContainer)")];
        }
        function getNativeConnectingText() {
          return getNativeConnectingWindows().map((windowElement) => windowElement.textContent || "").join("\n");
        }
        function hideNativeConnectingWindows() {
          for (const windowElement of getNativeConnectingWindows()) {
            setDisplayNone(windowElement);
          }
        }
        function getReserveStatusLines() {
          return getNativeConnectingWindows().flatMap(getWindowLines).map(normalizeLine).filter((line) => {
            return line && !options.roomFullPattern.test(line) && !options.roomClosedPattern.test(line) && !options.wrongPasswordPattern.test(line) && !/^cancel$/i.test(line) && line !== options.reserveWaitText;
          });
        }
        function getReserveNativeMessage(pattern) {
          return getNativeConnectingWindows().flatMap(getWindowLines).map(normalizeLine).find((line) => line && pattern.test(line)) || "";
        }
        return {
          getNativeConnectingText,
          getNativeConnectingWindows,
          getReserveNativeMessage,
          getReserveStatusLines,
          hideNativeConnectingWindows
        };
      }

      // src/features/reserve-selection-state.ts
      var SELECTED_RESERVE_ROW_SELECTOR2 = ".roomListContainer .scrollBox tr.SELECTED";
      function createReserveSelectionState() {
        let selectedRow = null;
        let selectedSignature = "";
        let selectedWasFull = false;
        let selectedWasUnavailable = false;
        function rememberReserveSelectedRoom(row) {
          if (!(row instanceof Element) || !row.isConnected) {
            return null;
          }
          selectedRow = row;
          selectedSignature = getReserveRoomSignature(row);
          selectedWasFull = isReserveRoomFull(row);
          selectedWasUnavailable = isReserveUnavailableRoom(row);
          return row;
        }
        function getReserveSelectedRoomRow() {
          const selected = document.querySelector(SELECTED_RESERVE_ROW_SELECTOR2);
          if (selected?.isConnected) {
            return rememberReserveSelectedRoom(selected);
          }
          if (selectedRow?.isConnected) {
            return rememberReserveSelectedRoom(selectedRow);
          }
          const matchingRow = findReserveRoomBySignature(selectedSignature);
          if (matchingRow) {
            return rememberReserveSelectedRoom(matchingRow);
          }
          return null;
        }
        function getReserveSelectedRoomState() {
          const row = getReserveSelectedRoomRow();
          if (row) {
            return {
              row,
              full: isReserveRoomFull(row),
              unavailable: isReserveUnavailableRoom(row)
            };
          }
          return {
            row: null,
            full: selectedWasFull,
            unavailable: selectedWasUnavailable
          };
        }
        return {
          getReserveSelectedRoomRow,
          getReserveSelectedRoomState,
          rememberReserveSelectedRoom
        };
      }

      // src/features/reserve-status-watch-timer.ts
      function createReserveStatusWatchTimer(options) {
        let statusWatchTimer = 0;
        function clearReserveStatusWatchTimer() {
          if (statusWatchTimer) {
            window.clearTimeout(statusWatchTimer);
            statusWatchTimer = 0;
          }
        }
        function scheduleReserveStatusWatch(delay = options.defaultDelayMs) {
          if (statusWatchTimer) {
            return;
          }
          statusWatchTimer = window.setTimeout(() => {
            statusWatchTimer = 0;
            options.onTick();
            if (options.shouldContinue()) {
              scheduleReserveStatusWatch(delay);
            }
          }, delay);
        }
        return {
          clearReserveStatusWatchTimer,
          scheduleReserveStatusWatch
        };
      }

      // src/features/reserve-waiting-window.ts
      var RESERVE_WINDOW_ID = "qolboxReserveWindow";
      function getReserveWindowHost() {
        return document.getElementById("appContainer") || document.body || document.documentElement;
      }
      function createReserveWaitingWindow(options) {
        function ensureReserveWaitingWindow() {
          const existing = document.getElementById(RESERVE_WINDOW_ID);
          if (existing) {
            return existing;
          }
          const container = document.createElement("div");
          container.id = RESERVE_WINDOW_ID;
          container.className = "connectingWindowContainer qolboxReserveWindowContainer";
          container.innerHTML = `
          <div class="behindBlocker"></div>
          <div class="connectingWindow">
            <div class="topBar"></div>
            <div class="qolboxReserveContent">
              <div class="spinner" aria-hidden="true"></div>
              <div class="qolboxReserveStatus"></div>
              <div class="qolboxReserveCountdown"></div>
              <div class="qolboxReserveMessage"></div>
            </div>
            <div class="cancelButton">CANCEL</div>
          </div>
        `;
          container.querySelector(".cancelButton")?.addEventListener("click", () => {
            options.onCancel();
          });
          getReserveWindowHost().appendChild(container);
          return container;
        }
        function getReserveStatusText() {
          const statusText = options.getReserveStatusLines().slice(-2).join(" - ");
          const state = options.getState();
          if (statusText) {
            if (state) {
              state.lastStatusText = statusText;
            }
            return statusText;
          }
          return state?.lastStatusText || options.statusFallbackText;
        }
        function getReserveCountdownText() {
          const nextRetryAt = options.getState()?.nextRetryAt;
          const remainingMs = nextRetryAt ? Math.max(0, nextRetryAt - Date.now()) : options.getRetryDelayMs();
          return `Retrying in ${(remainingMs / 1e3).toFixed(1)} seconds...`;
        }
        function updateReserveWaitingWindow() {
          const container = ensureReserveWaitingWindow();
          const state = options.getState();
          const title = container.querySelector(".topBar");
          const spinner = container.querySelector(".spinner");
          const status = container.querySelector(".qolboxReserveStatus");
          const countdown = container.querySelector(".qolboxReserveCountdown");
          const message = container.querySelector(".qolboxReserveMessage");
          const isTerminalMessage = Boolean(state && (state.unavailable || state.terminal));
          const isUnavailable = Boolean(state?.unavailable);
          if (title) {
            title.textContent = isUnavailable ? options.unavailableTitleText : options.waitTitleText;
          }
          if (spinner) {
            spinner.hidden = isTerminalMessage;
          }
          if (status) {
            status.hidden = isTerminalMessage;
            status.textContent = isTerminalMessage ? "" : getReserveStatusText();
          }
          if (countdown) {
            countdown.hidden = isTerminalMessage;
            countdown.textContent = isTerminalMessage ? "" : getReserveCountdownText();
          }
          if (message) {
            message.hidden = !isTerminalMessage;
            message.textContent = isTerminalMessage ? state?.message || options.onePersonText : "";
          }
        }
        function setReserveWaitingVisible(visible) {
          document.body?.classList.toggle("qolbox-reserve-active", visible);
          ensureReserveWaitingWindow().style.display = visible ? "block" : "none";
        }
        return {
          ensureReserveWaitingWindow,
          getReserveCountdownText,
          getReserveStatusText,
          setReserveWaitingVisible,
          updateReserveWaitingWindow
        };
      }

      // src/features/reserve-feature-bundle.ts
      function createReserveFeatureBundle(options) {
        const {
          getReserveSelectedRoomRow,
          getReserveSelectedRoomState,
          rememberReserveSelectedRoom
        } = createReserveSelectionState();
        const {
          getReserveState,
          showReserveOnePersonUnavailable,
          showReserveTerminalMessage,
          startReserveSpot,
          stopReserveAfterSuccessfulJoin,
          stopReserveSpot
        } = createReserveLifecycleController({
          clearCapturedJoin: () => clearReserveCapturedJoin(),
          clearCountdownTimer: () => clearReserveCountdownTimer(),
          clearPasswordPromptPending: () => clearReservePasswordPromptPending(),
          clearRetryTimer: (state) => clearReserveRetryTimer(state),
          clearStatusWatchTimer: () => clearReserveStatusWatchTimer(),
          clearVisibleRoomSelection: () => clearReserveVisibleRoomSelection(),
          getCapturedJoin: () => getReserveCapturedJoin(),
          hideNativeConnectingWindows: () => hideNativeConnectingWindows(),
          isEnabled: options.isReserveEnabled,
          onePersonText: RESERVE_ONE_PERSON_TEXT,
          rememberSelectedRoom: (row) => rememberReserveSelectedRoom(row),
          getRetryDelayMs: getAdvancedReserveRetryIntervalMs,
          scheduleCountdownUpdate: () => scheduleReserveCountdownUpdate(),
          scheduleStatusWatch: () => scheduleReserveStatusWatch(),
          setWaitingVisible: (visible) => setReserveWaitingVisible(visible),
          statusFallbackText: RESERVE_STATUS_FALLBACK_TEXT,
          suppressRoomFullAfterJoin: () => suppressReserveRoomFullAfterJoin(),
          syncJoinButtonLabel: () => syncReserveJoinButtonLabel(),
          updateWaitingWindow: () => updateReserveWaitingWindow()
        });
        const { getReserveJoinButton } = createReserveRoomList({
          isElementVisible
        });
        const {
          clearReservePasswordPromptPending,
          clearReserveVisibleRoomSelection,
          isReservePasswordPromptPending,
          setReservePasswordPromptPending,
          syncReserveJoinButtonLabel,
          syncReservePasswordPrompt
        } = createReserveActionControls({
          getReserveJoinButton,
          getReserveSelectedRoomState,
          isElementVisible,
          isEnabled: options.isReserveEnabled,
          joinButtonText: JOIN_BUTTON_TEXT,
          reserveButtonText: RESERVE_BUTTON_TEXT
        });
        const {
          getNativeConnectingWindows,
          getNativeConnectingText,
          getReserveNativeMessage,
          getReserveStatusLines,
          hideNativeConnectingWindows
        } = createReserveNativeStatus({
          reserveWaitText: RESERVE_WAIT_TEXT,
          roomClosedPattern: RESERVE_ROOM_CLOSED_PATTERN,
          roomFullPattern: RESERVE_ROOM_FULL_PATTERN,
          wrongPasswordPattern: RESERVE_WRONG_PASSWORD_PATTERN
        });
        const {
          isReserveJoinedRoomFullSuppressed,
          suppressReserveRoomFullAfterJoin
        } = createReserveRoomFullSuppression({
          suppressMs: RESERVE_JOINED_ROOM_FULL_SUPPRESS_MS
        });
        const {
          clearReserveStatusWatchTimer,
          scheduleReserveStatusWatch
        } = createReserveStatusWatchTimer({
          defaultDelayMs: 250,
          onTick: () => handleReserveConnectingState(),
          shouldContinue: () => shouldContinueReserveStatusWatch()
        });
        const {
          isReserveRetryAudioSuppressed,
          suppressReserveRetryAudio
        } = createReserveRetryAudioSuppression({
          suppressMs: RESERVE_RETRY_AUDIO_SUPPRESS_MS
        });
        const {
          canAutoReserveCapturedJoin,
          captureReserveJoin,
          clearReserveCapturedJoin,
          emitReserveJoinAttempt,
          getReserveCapturedJoin,
          shouldWatchRecentReserveCapture
        } = createReserveCapturedJoinController({
          capturedJoinFreshMs: 3e4,
          getState: () => getReserveState(),
          hasSuccessfulJoinLayer: options.hasSuccessfulJoinLayer,
          isEnabled: options.isReserveEnabled,
          isAutoJoinMatch: isNativeAutoJoinMatch,
          onCaptured: () => scheduleReserveStatusWatch(),
          suppressRetryAudio: suppressReserveRetryAudio
        });
        const { installReserveSocketCaptureHook } = createReserveSocketCaptureHook({
          onJoin: captureReserveJoin,
          shouldCaptureJoin: (args) => Boolean(getReserveJoinPayload(args))
        });
        const {
          setReserveWaitingVisible,
          updateReserveWaitingWindow
        } = createReserveWaitingWindow({
          getReserveStatusLines,
          getState: () => getReserveState(),
          getRetryDelayMs: getAdvancedReserveRetryIntervalMs,
          onCancel: () => cancelReserveSpot(),
          onePersonText: RESERVE_ONE_PERSON_TEXT,
          statusFallbackText: RESERVE_STATUS_FALLBACK_TEXT,
          unavailableTitleText: RESERVE_UNAVAILABLE_TITLE_TEXT,
          waitTitleText: RESERVE_WAIT_TITLE_TEXT
        });
        const {
          clearReserveCountdownTimer,
          scheduleReserveCountdownUpdate
        } = createReserveCountdownTimer({
          getState: () => getReserveState(),
          intervalMs: RESERVE_COUNTDOWN_UPDATE_MS,
          onTick: () => updateReserveWaitingWindow()
        });
        const { clearReserveRetryTimer, scheduleReserveRetry } = createReserveRetryScheduler({
          emitJoinAttempt: emitReserveJoinAttempt,
          getState: () => getReserveState(),
          hasSuccessfulJoinLayer: options.hasSuccessfulJoinLayer,
          isEnabled: options.isReserveEnabled,
          onSuccessfulJoin: () => stopReserveAfterSuccessfulJoin(),
          getRetryDelayMs: getAdvancedReserveRetryIntervalMs,
          scheduleCountdownUpdate: () => scheduleReserveCountdownUpdate(),
          updateWaitingWindow: () => updateReserveWaitingWindow()
        });
        const { handleReserveConnectingState } = createReserveConnectingStateController({
          canAutoReserveCapturedJoin,
          getNativeConnectingText,
          getReserveNativeMessage,
          getState: () => getReserveState(),
          hasSuccessfulJoinLayer: options.hasSuccessfulJoinLayer,
          hideNativeConnectingWindows,
          isAutoJoinOnePersonRoom: isNativeAutoJoinOnePersonRoom,
          isEnabled: options.isReserveEnabled,
          isRoomFullSuppressed: isReserveJoinedRoomFullSuppressed,
          roomClosedPattern: RESERVE_ROOM_CLOSED_PATTERN,
          roomFullPattern: RESERVE_ROOM_FULL_PATTERN,
          scheduleReserveRetry,
          showOnePersonUnavailable: () => showReserveOnePersonUnavailable(),
          showTerminalMessage: showReserveTerminalMessage,
          startReserveSpot,
          stopAfterSuccessfulJoin: stopReserveAfterSuccessfulJoin,
          stopReserveSpot,
          wrongPasswordPattern: RESERVE_WRONG_PASSWORD_PATTERN
        });
        const {
          cancelReserveSpot,
          handleReservePasswordKey,
          handleReservePasswordSubmit,
          handleReserveRoomListClick,
          handleReserveRoomListDoubleClick
        } = createReserveInteractionHandlers({
          clearPasswordPromptPending: clearReservePasswordPromptPending,
          getNativeConnectingWindows,
          getRowFromTarget: getReserveRowFromTarget,
          getSelectedRoomRow: getReserveSelectedRoomRow,
          getSelectedRoomState: getReserveSelectedRoomState,
          getState: () => getReserveState(),
          isElementVisible,
          isEnabled: options.isReserveEnabled,
          isPasswordPromptPending: isReservePasswordPromptPending,
          isPasswordRoom: isReservePasswordRoom,
          isRoomFull: isReserveRoomFull,
          isUnavailableRoom: isReserveUnavailableRoom,
          rememberSelectedRoom: rememberReserveSelectedRoom,
          setPasswordPromptPending: setReservePasswordPromptPending,
          showOnePersonUnavailable: showReserveOnePersonUnavailable,
          startReserveSpot,
          stopReserveSpot,
          syncJoinButtonLabel: syncReserveJoinButtonLabel,
          syncPasswordPrompt: syncReservePasswordPrompt
        });
        const { installReserveDomEventHooks } = createReserveDomEventHooks({
          onPasswordKey: handleReservePasswordKey,
          onPasswordSubmit: handleReservePasswordSubmit,
          onRoomListClick: handleReserveRoomListClick,
          onRoomListDoubleClick: handleReserveRoomListDoubleClick
        });
        const {
          patchReserveSpotFeature,
          shouldContinueReserveStatusWatch
        } = createReserveFeaturePatchController({
          getState: () => getReserveState(),
          handleConnectingState: handleReserveConnectingState,
          installDomEventHooks: installReserveDomEventHooks,
          installSocketCaptureHook: installReserveSocketCaptureHook,
          isEnabled: options.isReserveEnabled,
          isRoomFullSuppressed: isReserveJoinedRoomFullSuppressed,
          shouldWatchRecentCapture: shouldWatchRecentReserveCapture,
          syncJoinButtonLabel: syncReserveJoinButtonLabel,
          syncPasswordPrompt: syncReservePasswordPrompt
        });
        return {
          clearReservePasswordPromptPending,
          getReserveState,
          installReserveSocketCaptureHook,
          isReserveRetryAudioSuppressed,
          patchReserveSpotFeature,
          stopReserveSpot,
          syncReserveJoinButtonLabel
        };
      }

      // src/hitbox/scoreboard-adapter.ts
      function getScorePlayers(session) {
        const players = readNativePath(session, ["KR", "uL", "Ho"]);
        return Array.isArray(players) ? players.filter(Boolean) : [];
      }

      // src/features/score-row-color-values.ts
      function parseCssRgbColor(value) {
        if (typeof value !== "string") {
          return null;
        }
        const match = value.match(
          /^rgba?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)(?:\s*,\s*(\d+(?:\.\d+)?))?\s*\)$/i
        );
        if (!match) {
          return null;
        }
        return {
          red: Math.max(0, Math.min(255, Math.round(Number(match[1])))),
          green: Math.max(0, Math.min(255, Math.round(Number(match[2])))),
          blue: Math.max(0, Math.min(255, Math.round(Number(match[3])))),
          alpha: match[4] === void 0 ? 1 : Math.max(0, Math.min(1, Number(match[4])))
        };
      }
      function parseNumericRgbColor(value) {
        const color = Number(value);
        if (!Number.isFinite(color) || color < 0 || color > 16777215) {
          return null;
        }
        return {
          red: color >> 16 & 255,
          green: color >> 8 & 255,
          blue: color & 255,
          alpha: 1
        };
      }
      function parseHexRgbColor(value) {
        if (typeof value !== "string") {
          return null;
        }
        const normalized = value.trim().replace(/^#|^0x/i, "");
        if (!/^[0-9a-f]{6}$/i.test(normalized)) {
          return null;
        }
        return parseNumericRgbColor(Number.parseInt(normalized, 16));
      }
      function parsePlayerRgbColor(value) {
        return typeof value === "number" ? parseNumericRgbColor(value) : parseCssRgbColor(value) || parseHexRgbColor(value);
      }
      function colorsMatch(left, right) {
        return Boolean(
          left && right && left.red === right.red && left.green === right.green && left.blue === right.blue
        );
      }
      function getElementBackgroundColor(element) {
        return isStyledElement(element) && typeof element.style.backgroundColor === "string" ? element.style.backgroundColor : "";
      }
      function blendRgbColors(foreground, background) {
        const alpha = foreground.alpha;
        return {
          red: Math.round(foreground.red * alpha + background.red * (1 - alpha)),
          green: Math.round(foreground.green * alpha + background.green * (1 - alpha)),
          blue: Math.round(foreground.blue * alpha + background.blue * (1 - alpha)),
          alpha: 1
        };
      }
      function getRelativeLuminance(color) {
        const channels = [color.red, color.green, color.blue].map((value) => {
          const normalized = value / 255;
          return normalized <= 0.03928 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
        });
        return channels[0] * 0.2126 + channels[1] * 0.7152 + channels[2] * 0.0722;
      }
      function getContrastRatio(left, right) {
        const leftLuminance = getRelativeLuminance(left);
        const rightLuminance = getRelativeLuminance(right);
        const lighter = Math.max(leftLuminance, rightLuminance);
        const darker = Math.min(leftLuminance, rightLuminance);
        return (lighter + 0.05) / (darker + 0.05);
      }
      function getEffectiveBackgroundColor(element) {
        let current = element;
        let color = { red: 10, green: 10, blue: 10, alpha: 1 };
        const layers = [];
        while (current) {
          const background = parseCssRgbColor(window.getComputedStyle(current).backgroundColor);
          if (background && background.alpha > 0) {
            layers.unshift(background);
            if (background.alpha >= 1) {
              break;
            }
          }
          current = current.parentElement;
        }
        for (const layer of layers) {
          color = layer.alpha >= 1 ? { ...layer, alpha: 1 } : blendRgbColors(layer, color);
        }
        return color;
      }
      function getReadableTextColor(background) {
        const dark = { red: 0, green: 0, blue: 0, alpha: 1 };
        const light = { red: 255, green: 255, blue: 255, alpha: 1 };
        return getContrastRatio(dark, background) >= getContrastRatio(light, background) ? dark : light;
      }
      function toCssRgb(color) {
        return `rgb(${color.red}, ${color.green}, ${color.blue})`;
      }

      // src/features/score-row-colors.ts
      var MIN_SCORE_TEXT_CONTRAST = 4.5;
      function normalizeScoreName(value) {
        return String(value || "").replace(/\s+/g, " ").trim().toLowerCase();
      }
      function getScoreRowName(row) {
        const nameElement = row && row.querySelector ? row.querySelector(".name") : null;
        return normalizeScoreName(nameElement ? nameElement.textContent : row && row.textContent);
      }
      function createScoreRowColorController(options) {
        const scoreRowColorsByKey = /* @__PURE__ */ new Map();
        function isFallbackScoreRowColor(color) {
          return colorsMatch(color, options.fallbackRgb);
        }
        function getPlayerDirectScoreColor(player) {
          for (const value of getPlayerColorCandidates(player)) {
            const parsed = parsePlayerRgbColor(value);
            if (parsed) {
              return parsed;
            }
          }
          return null;
        }
        function getScoreRowColorKeys(row, player) {
          const keys = /* @__PURE__ */ new Set();
          const rowName = getScoreRowName(row);
          const playerName = normalizeScoreName(getPlayerDisplayName(player));
          const teamState = options.getPlayerTeamState(player);
          if (rowName) {
            keys.add(`row:${rowName}`);
          }
          if (playerName) {
            keys.add(`player:${playerName}`);
          }
          if (Number.isFinite(teamState)) {
            keys.add(`team:${teamState}`);
          }
          return Array.from(keys);
        }
        function rememberScoreRowColor(keys, color) {
          if (!color || isFallbackScoreRowColor(color)) {
            return;
          }
          for (const key of keys) {
            scoreRowColorsByKey.set(key, { ...color, alpha: 1 });
          }
        }
        function getRememberedScoreRowColor(keys) {
          for (const key of keys) {
            const color = scoreRowColorsByKey.get(key);
            if (color) {
              return color;
            }
          }
          return null;
        }
        function getTeamScoreColor(player) {
          return options.teamScoreColors.get(options.getPlayerTeamState(player)) || null;
        }
        function getPlayerScoreColor(row, player) {
          const keys = getScoreRowColorKeys(row, player);
          return getPlayerDirectScoreColor(player) || getRememberedScoreRowColor(keys) || getTeamScoreColor(player);
        }
        function syncScoreRowTextContrast(row) {
          const background = getEffectiveBackgroundColor(row);
          const readableColor = toCssRgb(getReadableTextColor(background));
          let changed = false;
          const textElements = [row, ...Array.from(row.querySelectorAll(".number, .name"))];
          for (const element of textElements) {
            if (!(element.textContent || "").trim()) {
              continue;
            }
            const currentColor = parseCssRgbColor(window.getComputedStyle(element).color);
            if (currentColor && getContrastRatio(currentColor, background) >= MIN_SCORE_TEXT_CONTRAST) {
              continue;
            }
            options.setImportantStyle(element, "color", readableColor);
            changed = true;
          }
          return changed;
        }
        function getUniquePlayersByName(players) {
          const playersByName = /* @__PURE__ */ new Map();
          for (const player of players) {
            const name = normalizeScoreName(getPlayerDisplayName(player));
            if (!name) {
              continue;
            }
            playersByName.set(name, playersByName.has(name) ? null : player);
          }
          return playersByName;
        }
        function syncScoreRowsFromPlayers(scorePanel) {
          const rows = Array.from(scorePanel.querySelectorAll(".entryContainer"));
          const players = options.getScorePlayers();
          if (!rows.length || !players.length) {
            return false;
          }
          const playersByName = getUniquePlayersByName(players);
          let changed = false;
          rows.forEach((row, index) => {
            const inlineColor = parseCssRgbColor(getElementBackgroundColor(row));
            const computedColor = parseCssRgbColor(window.getComputedStyle(row).backgroundColor);
            const currentColor = inlineColor || computedColor;
            const namedPlayer = playersByName.get(getScoreRowName(row));
            const player = namedPlayer || players[index];
            const colorKeys = getScoreRowColorKeys(row, player);
            if (currentColor && !isFallbackScoreRowColor(currentColor)) {
              rememberScoreRowColor(colorKeys, currentColor);
            }
            const playerColor = getPlayerScoreColor(row, player);
            if (!playerColor) {
              changed = syncScoreRowTextContrast(row) || changed;
              return;
            }
            if (currentColor && colorsMatch(currentColor, playerColor)) {
              changed = syncScoreRowTextContrast(row) || changed;
              return;
            }
            options.setImportantStyle(row, "background-color", `rgb(${playerColor.red}, ${playerColor.green}, ${playerColor.blue})`);
            syncScoreRowTextContrast(row);
            changed = true;
          });
          return changed;
        }
        function syncAllScoreRowsFromPlayers() {
          let changed = false;
          for (const scorePanel of document.querySelectorAll(".scores")) {
            changed = syncScoreRowsFromPlayers(scorePanel) || changed;
          }
          return changed;
        }
        function makeScoreRowsOpaque(scorePanel) {
          const rows = Array.from(scorePanel.querySelectorAll(".entryContainer"));
          const players = options.getScorePlayers();
          rows.forEach((row, index) => {
            const inlineColor = parseCssRgbColor(getElementBackgroundColor(row));
            const computedColor = parseCssRgbColor(window.getComputedStyle(row).backgroundColor);
            const parsedColor = inlineColor || computedColor;
            const player = players[index];
            const colorKeys = getScoreRowColorKeys(row, player);
            if (parsedColor && !isFallbackScoreRowColor(parsedColor)) {
              rememberScoreRowColor(colorKeys, parsedColor);
            }
            if (parsedColor && parsedColor.alpha < 1 && (inlineColor || !isFallbackScoreRowColor(parsedColor))) {
              options.setImportantStyle(
                row,
                "background-color",
                `rgb(${parsedColor.red}, ${parsedColor.green}, ${parsedColor.blue})`
              );
            }
            syncScoreRowTextContrast(row);
          });
        }
        return {
          makeScoreRowsOpaque,
          syncAllScoreRowsFromPlayers,
          syncScoreRowsFromPlayers
        };
      }

      // src/hitbox/typing-pulse-adapter.ts
      function isNativeCallable9(value) {
        return typeof value === "function";
      }
      function getNativeLobbyUi(session) {
        const lobbyUi = readNativeProperty(session, "TJ");
        return isNativeObject(lobbyUi) ? lobbyUi : null;
      }
      function isNativeTypingPulseHookInstalled(session) {
        return Boolean(readNativeProperty(getNativeLobbyUi(session), "__qolboxTypingIndicatorPatched"));
      }
      function installNativeTypingPulseHook(session, onTypingPulse) {
        const lobbyUi = getNativeLobbyUi(session);
        if (!lobbyUi || isNativeTypingPulseHookInstalled(session)) {
          return Boolean(lobbyUi);
        }
        const nativeTypingPulse = readNativeProperty(lobbyUi, "$W");
        if (!isNativeCallable9(nativeTypingPulse)) {
          return false;
        }
        const wrappedTypingPulse = function wrappedTypingPulse2(playerId, ...rest) {
          onTypingPulse(playerId);
          return Reflect.apply(nativeTypingPulse, this, [playerId, ...rest]);
        };
        setNativeReflectProperty(lobbyUi, "$W", wrappedTypingPulse);
        setNativeReflectProperty(lobbyUi, "__qolboxTypingIndicatorOriginal", nativeTypingPulse);
        setNativeReflectProperty(lobbyUi, "__qolboxTypingIndicatorPatched", true);
        return true;
      }

      // src/features/typing-expiration-tracker.ts
      function createTypingExpirationTracker(options) {
        const timers = /* @__PURE__ */ new Map();
        const expirations = /* @__PURE__ */ new Map();
        function clear() {
          for (const timer of timers.values()) {
            window.clearTimeout(timer);
          }
          timers.clear();
          expirations.clear();
        }
        function isTyping(playerId) {
          const id = String(playerId);
          const expiresAt = expirations.get(id);
          if (!expiresAt) {
            return false;
          }
          if (expiresAt <= Date.now()) {
            expirations.delete(id);
            return false;
          }
          return true;
        }
        function note(playerId) {
          if (playerId === null || playerId === void 0) {
            return false;
          }
          const id = String(playerId);
          const timeoutMs = options.getTimeoutMs();
          const expiresAt = Date.now() + timeoutMs;
          const existingTimer = timers.get(id);
          if (existingTimer) {
            window.clearTimeout(existingTimer);
          }
          expirations.set(id, expiresAt);
          timers.set(
            id,
            window.setTimeout(() => {
              if ((expirations.get(id) || 0) <= Date.now()) {
                expirations.delete(id);
                timers.delete(id);
                options.onExpire();
              }
            }, timeoutMs + 50)
          );
          return true;
        }
        return {
          clear,
          isTyping,
          note
        };
      }

      // src/features/typing-score-indicators.ts
      function clearScoreTypingIndicators() {
        for (const indicator of document.querySelectorAll(".qolboxTypingIndicator")) {
          indicator.remove();
        }
      }
      function syncScoreTypingIndicators(scorePanel, typingPlayers) {
        const panels = scorePanel ? [scorePanel] : Array.from(document.querySelectorAll(".scores"));
        let changed = false;
        for (const panel of panels) {
          for (const row of panel.querySelectorAll(".entryContainer")) {
            const nameElement = row.querySelector(".name") || row;
            const rowName = getScoreRowName(row);
            const rowText = normalizeScoreName(row.textContent);
            const isTyping = typingPlayers.some(
              (entry) => entry.name && (entry.name === rowName || rowText.includes(entry.name))
            );
            const indicator = nameElement.querySelector(".qolboxTypingIndicator");
            if (isTyping && !indicator) {
              const newIndicator = document.createElement("span");
              newIndicator.className = "qolboxTypingIndicator";
              newIndicator.setAttribute("aria-label", "typing");
              nameElement.appendChild(newIndicator);
              changed = true;
            } else if (!isTyping && indicator) {
              indicator.remove();
              changed = true;
            }
          }
        }
        return changed;
      }

      // src/features/typing-world-indicators.ts
      function ensureWorldTypingLayer() {
        if (!document.body) {
          return null;
        }
        const existingLayer = document.querySelector(".qolboxWorldTypingLayer");
        if (existingLayer) {
          return existingLayer;
        }
        const layer = document.createElement("div");
        layer.className = "qolboxWorldTypingLayer";
        layer.setAttribute("aria-hidden", "true");
        document.body.appendChild(layer);
        return layer;
      }
      function createWorldTypingIndicatorController(options) {
        let typingIndicatorPositionRaf = 0;
        function stopTypingIndicatorPositionLoop() {
          if (!typingIndicatorPositionRaf) {
            return;
          }
          if (typeof window.cancelAnimationFrame === "function") {
            window.cancelAnimationFrame(typingIndicatorPositionRaf);
          } else {
            window.clearTimeout(typingIndicatorPositionRaf);
          }
          typingIndicatorPositionRaf = 0;
        }
        function syncWorldTypingIndicators(typingPlayers, session = options.getSession()) {
          const shouldShowWorldIndicators = options.isChatFeatureEnabled() && options.isSessionMatchActive(session) && typingPlayers.length > 0;
          const existingLayer = document.querySelector(".qolboxWorldTypingLayer");
          if (!shouldShowWorldIndicators) {
            if (existingLayer) {
              existingLayer.remove();
              return true;
            }
            return false;
          }
          const layer = ensureWorldTypingLayer();
          if (!layer) {
            return false;
          }
          const activeIds = /* @__PURE__ */ new Set();
          let changed = false;
          for (const player of typingPlayers) {
            const position = options.getWorldTypingPosition(player.id, session);
            if (!position) {
              continue;
            }
            const id = String(player.id);
            activeIds.add(id);
            let indicator = Array.from(layer.querySelectorAll(".qolboxWorldTypingIndicator")).find(
              (element) => element.dataset.playerId === id
            );
            if (!indicator) {
              indicator = document.createElement("span");
              indicator.className = "qolboxWorldTypingIndicator";
              indicator.dataset.playerId = id;
              indicator.setAttribute("aria-label", "typing");
              layer.appendChild(indicator);
              changed = true;
            }
            const left = `${Math.round(position.left)}px`;
            const top = `${Math.round(position.top)}px`;
            if (indicator.style.left !== left) {
              indicator.style.left = left;
            }
            if (indicator.style.top !== top) {
              indicator.style.top = top;
            }
          }
          for (const indicator of Array.from(layer.querySelectorAll(".qolboxWorldTypingIndicator"))) {
            if (!activeIds.has(indicator.dataset.playerId || "")) {
              indicator.remove();
              changed = true;
            }
          }
          if (!activeIds.size && layer.children.length === 0) {
            layer.remove();
            changed = true;
          }
          return changed;
        }
        function scheduleTypingIndicatorPositionLoop(session = options.getSession()) {
          if (typingIndicatorPositionRaf || !options.isSessionMatchActive(session)) {
            return;
          }
          const updateTypingIndicatorPositions = () => {
            typingIndicatorPositionRaf = 0;
            if (!options.isChatFeatureEnabled() || !options.isSessionMatchActive()) {
              syncWorldTypingIndicators([], options.getSession());
              return;
            }
            const typingPlayers = options.getTypingPlayers();
            syncWorldTypingIndicators(typingPlayers);
            if (typingPlayers.length > 0) {
              scheduleTypingIndicatorPositionLoop();
            }
          };
          typingIndicatorPositionRaf = typeof window.requestAnimationFrame === "function" ? window.requestAnimationFrame(updateTypingIndicatorPositions) : window.setTimeout(updateTypingIndicatorPositions, options.fallbackUpdateDelayMs);
        }
        function clearWorldTypingIndicators() {
          stopTypingIndicatorPositionLoop();
          const worldLayer = document.querySelector(".qolboxWorldTypingLayer");
          if (worldLayer) {
            worldLayer.remove();
          }
        }
        return {
          clearWorldTypingIndicators,
          scheduleTypingIndicatorPositionLoop,
          stopTypingIndicatorPositionLoop,
          syncWorldTypingIndicators
        };
      }

      // src/features/typing-indicators.ts
      function createTypingIndicatorController(options) {
        let typingIndicatorSession = null;
        const typingExpirations = createTypingExpirationTracker({
          getTimeoutMs: options.getTimeoutMs,
          onExpire: () => syncTypingIndicators()
        });
        const worldTypingIndicators = createWorldTypingIndicatorController({
          fallbackUpdateDelayMs: 100,
          getSession: options.getSession,
          getTypingPlayers,
          getWorldTypingPosition: options.getWorldTypingPosition,
          isChatFeatureEnabled: options.isChatFeatureEnabled,
          isSessionMatchActive: options.isSessionMatchActive
        });
        function clearTypingIndicators() {
          worldTypingIndicators.clearWorldTypingIndicators();
          typingExpirations.clear();
          clearScoreTypingIndicators();
        }
        function isPlayerTypingNow(playerId) {
          return typingExpirations.isTyping(playerId);
        }
        function getTypingPlayers(session = options.getSession()) {
          const localPlayerId = options.getLocalPlayerId(session);
          return options.getSessionPlayers(session).filter(({ id }) => !options.isSamePlayerId(id, localPlayerId) && isPlayerTypingNow(id)).map(({ id, player }) => ({
            id,
            name: normalizeScoreName(getPlayerDisplayName(player))
          }));
        }
        function syncWorldTypingIndicators(typingPlayers, session = options.getSession()) {
          return worldTypingIndicators.syncWorldTypingIndicators(typingPlayers, session);
        }
        function scheduleTypingIndicatorPositionLoop(session = options.getSession()) {
          worldTypingIndicators.scheduleTypingIndicatorPositionLoop(session);
        }
        function syncTypingIndicators(scorePanel = null) {
          if (!options.isChatFeatureEnabled()) {
            clearTypingIndicators();
            return false;
          }
          const session = options.getSession();
          if (!session) {
            worldTypingIndicators.stopTypingIndicatorPositionLoop();
            return false;
          }
          const typingPlayers = getTypingPlayers(session);
          let changed = syncScoreTypingIndicators(scorePanel, typingPlayers);
          changed = syncWorldTypingIndicators(typingPlayers, session) || changed;
          if (typingPlayers.length > 0 && options.isSessionMatchActive(session)) {
            scheduleTypingIndicatorPositionLoop(session);
          } else {
            worldTypingIndicators.stopTypingIndicatorPositionLoop();
          }
          return changed;
        }
        function notePlayerTyping(playerId) {
          if (!options.isChatFeatureEnabled()) {
            clearTypingIndicators();
            return false;
          }
          if (playerId === null || playerId === void 0) {
            return false;
          }
          if (options.isSamePlayerId(playerId, options.getLocalPlayerId())) {
            return false;
          }
          typingExpirations.note(playerId);
          syncTypingIndicators();
          return true;
        }
        function patchTypingIndicatorHooks() {
          if (!options.isChatFeatureEnabled()) {
            clearTypingIndicators();
            return false;
          }
          const session = options.getSession();
          if (isNativeTypingPulseHookInstalled(session)) {
            return true;
          }
          if (typingIndicatorSession && typingIndicatorSession !== session) {
            clearTypingIndicators();
          }
          const installed = installNativeTypingPulseHook(session, notePlayerTyping);
          if (installed) {
            typingIndicatorSession = session;
          }
          return installed;
        }
        return {
          clearTypingIndicators,
          notePlayerTyping,
          patchTypingIndicatorHooks,
          syncTypingIndicators,
          syncWorldTypingIndicators
        };
      }

      // src/hitbox/world-state-adapter.ts
      function hasForEach(value) {
        return isNativeObject(value) && typeof readNativeProperty(value, "forEach") === "function";
      }
      function getCollectionEntries(collection) {
        if (!collection) {
          return [];
        }
        if (Array.isArray(collection)) {
          return collection.map((value, key) => ({ key, value })).filter((entry) => entry.value);
        }
        if (collection instanceof Map) {
          const entries = [];
          collection.forEach((value, key) => {
            if (value) {
              entries.push({ key, value });
            }
          });
          return entries;
        }
        if (hasForEach(collection)) {
          const entries = [];
          collection.forEach((value, key) => {
            if (value) {
              entries.push({ key, value });
            }
          });
          return entries;
        }
        if (!isNativeObject(collection)) {
          return [];
        }
        return Object.keys(collection).map((key) => ({ key, value: readNativeProperty(collection, key) })).filter((entry) => entry.value);
      }
      function readFiniteNumber(source, property) {
        const value = Number(readNativeProperty(source, property));
        return Number.isFinite(value) ? value : null;
      }
      function getPlayerWorldEntityPosition(playerId, session) {
        const sources = [
          // Live match entities observed during gameplay.
          readNativePath(session, ["KR", "uL", "Ho"]),
          // Alternate player collection observed around match/lobby transitions.
          readNativePath(session, ["KR", "mL", "Pi"])
        ];
        for (const source of sources) {
          for (const { key, value } of getCollectionEntries(source)) {
            const id = readNativeProperty(value, "id") !== void 0 ? readNativeProperty(value, "id") : key;
            const x = readFiniteNumber(value, "x");
            const y = readFiniteNumber(value, "y");
            if (isSamePlayerId(id, playerId) && x !== null && y !== null) {
              return { x, y };
            }
          }
        }
        return null;
      }
      function getWorldCameraState(session) {
        const camera = readNativePath(session, ["KR", "ed"]) || readNativePath(session, ["KR", "hb", "Bc"]);
        return {
          width: Number(readNativeProperty(camera, "fc")),
          height: Number(readNativeProperty(camera, "gc")),
          left: Number(readNativeProperty(camera, "yc")),
          top: Number(readNativeProperty(camera, "vc"))
        };
      }

      // src/features/world-typing-position.ts
      function createWorldTypingPositioner(options) {
        function getPlayerWorldEntity(playerId, session = options.getSession()) {
          return getPlayerWorldEntityPosition(playerId, session);
        }
        function getWorldTypingViewport(session = options.getSession()) {
          const canvas = options.getActiveGameplayCanvas();
          if (!canvas || !isElementVisible(canvas)) {
            return null;
          }
          const rect = canvas.getBoundingClientRect();
          const camera = getWorldCameraState(session);
          const baseGameSize = options.getBaseGameSize();
          return {
            rect,
            worldLeft: Number.isFinite(camera.left) ? camera.left : 0,
            worldTop: Number.isFinite(camera.top) && camera.top >= 0 ? camera.top : 0,
            worldWidth: Number.isFinite(camera.width) && camera.width > 0 ? camera.width : baseGameSize.width,
            worldHeight: Number.isFinite(camera.height) && camera.height > 0 ? camera.height : (Number.isFinite(camera.width) && camera.width > 0 ? camera.width : baseGameSize.width) * (rect.height / rect.width)
          };
        }
        function getWorldTypingPosition(playerId, session = options.getSession()) {
          const entity = getPlayerWorldEntity(playerId, session);
          const viewport = getWorldTypingViewport(session);
          if (!entity || !viewport) {
            return null;
          }
          const { rect, worldLeft, worldTop, worldWidth, worldHeight } = viewport;
          const rectRight = Number.isFinite(rect.right) ? rect.right : rect.left + rect.width;
          const rectBottom = Number.isFinite(rect.bottom) ? rect.bottom : rect.top + rect.height;
          const x = rect.left + (entity.x - worldLeft) / worldWidth * rect.width;
          const y = rect.top + (entity.y - worldTop) / worldHeight * rect.height - 42;
          return {
            left: Math.max(rect.left + 11, Math.min(rectRight - 11, x)),
            top: Math.max(rect.top + 18, Math.min(rectBottom - 6, y))
          };
        }
        return {
          getPlayerWorldEntity,
          getWorldTypingPosition,
          getWorldTypingViewport
        };
      }

      // src/features/typing-feature-bundle.ts
      function createTypingFeatureBundle(options) {
        const scoreRows = createScoreRowColorController({
          fallbackRgb: SCORE_ROW_FALLBACK_RGB,
          teamScoreColors: TEAM_SCORE_COLORS,
          getPlayerTeamState,
          getScorePlayers: () => getScorePlayers(getMultiplayerSession()),
          setImportantStyle: options.setImportantStyle
        });
        const { getWorldTypingPosition } = createWorldTypingPositioner({
          getActiveGameplayCanvas: () => options.getActiveRenderCanvas("gameplay"),
          getBaseGameSize: options.getBaseGameSize,
          getSession: getMultiplayerSession
        });
        const typingIndicators = createTypingIndicatorController({
          getTimeoutMs: getAdvancedTypingIndicatorDurationMs,
          getLocalPlayerId,
          getSession: getMultiplayerSession,
          getSessionPlayers,
          getWorldTypingPosition,
          isChatFeatureEnabled: options.isChatFeatureEnabled,
          isSamePlayerId,
          isSessionMatchActive
        });
        return {
          ...scoreRows,
          ...typingIndicators,
          getWorldTypingPosition
        };
      }

      // src/app/qolbox-app.ts
      (function() {
        "use strict";
        if (!shouldRunGamePageBootstrap()) {
          return;
        }
        function scheduleAppUiWork(request) {
          scheduleUiWork(request);
        }
        const { isFeatureEnabled, setAllFeatureSettings, setFeatureEnabled, shouldRunFeature } = createFeatureSettingsController({
          isOnboardingComplete: () => qolboxMenuController.isOnboardingComplete(),
          onApplyFeatureRootClasses: () => applyFeatureRootClasses(),
          onApplyPersistentFeatures: () => applyPersistentFeatures(),
          onDisableFeatureSideEffects: (featureKey) => disableFeatureSideEffects(featureKey),
          onRenderMenu: () => renderQolboxMenu(),
          onScheduleUiWork: scheduleAppUiWork,
          resizeSettlePasses: RESIZE_SETTLE_PASSES
        });
        const featureGates = createFeatureGateSet(shouldRunFeature);
        const { patchEditorMapFileTransfer, removeEditorMapFileTransfer } = createEditorMapFileTransferController({
          isEditorMapTransferEnabled: featureGates.isEditorMapTransferEnabled
        });
        const { cleanupInGameChatScroll, patchInGameChatScroll } = createInGameChatScrollController({
          isChatFeatureEnabled: featureGates.isChatEnabled
        });
        const {
          getAdvancedSettings,
          setAdvancedSetting,
          setAdvancedSettings
        } = createAdvancedSettingsController({
          onApplyPersistentFeatures: () => applyPersistentFeatures(),
          onRenderMenu: () => renderQolboxMenu(),
          onScheduleLayoutRefresh: () => scheduleAppUiWork({ force: true, features: true, passes: FULLSCREEN_SETTLE_PASSES })
        });
        const {
          applyPersistentFeatures,
          disableFeatureSideEffects
        } = createFeatureSideEffectsController({
          applyFeatureRootClasses: () => applyFeatureRootClasses(),
          applyGameVolume: () => applyGameVolume(),
          applyJukeboxState: () => applyJukeboxState(),
          clearFullscreenLayoutStyles: () => clearFullscreenLayoutStyles(),
          clearReservePasswordPromptPending: () => clearReservePasswordPromptPending(),
          clearTypingIndicators: () => clearTypingIndicators(),
          cleanupInGameChatScroll: () => cleanupInGameChatScroll(),
          disableGameStartAlerts: () => disableGameStartAlerts(),
          hookHowlPrototype: () => hookHowlPrototype(),
          hookYouTubePlayer: () => hookYouTubePlayer(),
          installGameStartIndicatorHooks: () => installGameStartIndicatorHooks(),
          installPlayerPopupDismissal: () => installPlayerPopupDismissal2(),
          installTabFocusHooks: () => installTabFocusHooks(),
          installYouTubeReadyCallbackHook: () => installYouTubeReadyCallbackHook(),
          patchChatTabOrder: () => patchChatTabOrder(),
          patchEditorMapFileTransfer: () => patchEditorMapFileTransfer(),
          patchInGameChatScroll,
          patchGameVolumeMenu: () => patchGameVolumeMenu(),
          patchJukeboxKnob: () => patchJukeboxKnob(),
          patchJukeboxMenu: () => patchJukeboxMenu(),
          patchLobbyMusicController: () => patchLobbyMusicController(),
          patchLobbyBlacklist: () => patchLobbyBlacklist(),
          patchMobileGrabButton: () => patchMobileGrabButton(),
          patchMobileQolboxHamburgerEntry: () => patchMobileQolboxHamburgerEntry(),
          patchReserveSpotFeature: () => patchReserveSpotFeature(),
          patchSlashCommands: () => patchSlashCommands(),
          patchSwitchTeamsButton: () => patchSwitchTeamsButton(),
          patchTypingIndicatorHooks: () => patchTypingIndicatorHooks(),
          removeEditorMapFileTransfer: () => removeEditorMapFileTransfer(),
          removeJukeboxMenuItem: () => removeJukeboxMenuItem(),
          removeMobileGrabButton: () => removeMobileGrabButton(),
          removeSwitchTeamsButton: () => removeSwitchTeamsButton(),
          restoreChatTabOrder: () => restoreChatTabOrder(),
          restoreJukeboxState: () => restoreJukeboxState(),
          featureGates,
          stopReserveSpot: (options) => stopReserveSpot(options),
          syncScoreRows: () => syncAllScoreRowsFromPlayers(),
          syncReserveJoinButtonLabel: () => syncReserveJoinButtonLabel(),
          syncTypingIndicators: () => syncTypingIndicators(),
          updateGameStartIndicator: () => updateGameStartIndicator()
        });
        const {
          clearReservePasswordPromptPending,
          getReserveState,
          installReserveSocketCaptureHook,
          isReserveRetryAudioSuppressed,
          patchReserveSpotFeature,
          stopReserveSpot,
          syncReserveJoinButtonLabel
        } = createReserveFeatureBundle({
          hasSuccessfulJoinLayer: () => hasReserveSuccessfulJoinLayer(),
          isReserveEnabled: featureGates.isReserveEnabled
        });
        const {
          handleMobileGrabPointerStart,
          hideMobileGrabButton,
          isMobileGameMode,
          isMobileQolboxMenuContext,
          layoutMobileGrabButton,
          patchMobileGrabButton,
          removeMobileGrabButton,
          setMobileGrabPressed,
          shouldShowMobileGrabButton,
          syncMobileGrabButton,
          patchMobileQolboxHamburgerEntry
        } = createMobileFeatureBundle({
          isMobileGrabEnabled: featureGates.isMobileGrabEnabled,
          openMenu: () => openQolboxMenu(qolboxMenuController.isOnboardingComplete() ? "settings" : "onboarding")
        });
        const {
          clearGameStartIndicator,
          disableGameStartAlerts,
          handleGameStartInteractionFocus,
          hasPendingLocalPlayTransition,
          hasReserveSuccessfulJoinLayer,
          installGameStartIndicatorHooks,
          isCurrentPlayerSpectating,
          isMenuGameplayOverlap,
          isPageFocused,
          isPlayableLobby,
          isPlayingMatch,
          noteLocallyInitiatedPlayTransition,
          patchMultiplayerSessionGameStartHooks,
          setGameStartPageFocused,
          setGameStartWasInLobbyWhenUnfocused,
          setGameStartWasPlayingWhenUnfocused,
          updateGameStartIndicator
        } = createGameplayAlertFeatureBundle({
          isGameStartAlertEnabled: featureGates.isGameStartAlertEnabled
        });
        const { applyFeatureRootClasses, ensureGlobalStyle } = createQolboxShellFeatureBundle({
          isMenuClosed: () => qolboxMenuController.isClosed(),
          isFeatureActive: featureGates.shouldRunFeature
        });
        const {
          buildFullscreenSignature,
          clearFullscreenStyleSnapshots,
          getActiveRenderCanvas,
          getActiveRenderMode,
          getBaseGameSize,
          getFullscreenDimensions,
          getLayoutProbe,
          getNativeUiZoom,
          getRelativeContainerBounds,
          installNativeFullscreenPatch,
          isEditorCanvas,
          isEditorLayer,
          isNativeProbeAligned,
          isRenderProbeAligned,
          restoreFullscreenStyles,
          restoreNativeFullscreenPatch,
          restoreNativeLayoutSizeFallback,
          runNativeResize,
          setImportantStyle,
          setNativeFullscreenSize,
          shouldWaitForNativeLayoutSeed
        } = createFullscreenFoundationBundle();
        const {
          captureGameplayInputFocus,
          focusActiveRenderCanvas,
          handleGameplayBackgroundFocus,
          installChatCommandAliasHooks,
          installChatEscapeHooks,
          installGameplayBackgroundFocusHooks,
          isChatInput,
          patchChatTabOrder,
          resetBrowserScroll,
          restoreChatTabOrder,
          restoreLobbyChatPrompt,
          shouldCaptureGameplayBackgroundFocus
        } = createInputFocusFeatureBundle({
          getActiveRenderCanvas,
          isChatFeatureEnabled: featureGates.isChatEnabled,
          areLobbyCommandsEnabled: featureGates.isLobbyCommandsEnabled,
          isPlayingMatch,
          isQolboxMenuClosed: () => qolboxMenuController.isClosed()
        });
        const {
          clearTypingIndicators,
          getWorldTypingPosition,
          makeScoreRowsOpaque,
          notePlayerTyping,
          patchTypingIndicatorHooks,
          syncAllScoreRowsFromPlayers,
          syncScoreRowsFromPlayers,
          syncTypingIndicators,
          syncWorldTypingIndicators
        } = createTypingFeatureBundle({
          getActiveRenderCanvas,
          getBaseGameSize,
          isChatFeatureEnabled: featureGates.isChatEnabled,
          setImportantStyle
        });
        const {
          clearFullscreenLayoutStyles,
          enforceFullscreenLayout,
          fitEditorCanvasToNative,
          fitEditorLayerToFrame,
          getScaledEditorFrame,
          installGameReadyHook,
          layoutRelativeHud,
          refreshObservedResizeTargets,
          resizeKnownFullscreenRenderers: resizeKnownFullscreenRenderers2,
          setFullscreenResizeObserver,
          syncSpectateControlsBottomWithJukebox
        } = createFullscreenLayoutFeatureBundle({
          clearFullscreenSignature: () => clearFullscreenSignature(),
          clearFullscreenStyleSnapshots,
          ensureGlobalStyle,
          getFullscreenDimensions,
          getNativeUiZoom,
          getRelativeContainerBounds,
          isEditorCanvas,
          isEditorLayer,
          isFullscreenEnabled: featureGates.isFullscreenEnabled,
          makeScoreRowsOpaque,
          restoreFullscreenStyles,
          restoreNativeFullscreenPatch,
          restoreNativeLayoutSizeFallback,
          scheduleUiWork: scheduleAppUiWork,
          setImportantStyle,
          syncScoreRowsFromPlayers,
          syncTypingIndicators
        });
        const qolboxMenuController = createQolboxMenuFeatureBundle({
          applyFeatureRootClasses,
          applyPersistentFeatures,
          ensureGlobalStyle,
          getAdvancedSettings,
          isFeatureEnabled,
          scheduleUiWork: scheduleAppUiWork,
          setAdvancedSettings,
          setAllFeatureSettings,
          setFeatureEnabled
        });
        const { getOnboardingSteps, installQolboxMenuHooks, openQolboxMenu, renderQolboxMenu, scheduleFirstBootOnboarding } = qolboxMenuController;
        const { handlePopupKeyboard, installPopupKeyboardHooks } = createPopupKeyboardController();
        const {
          applyGameVolume,
          applyJukeboxState,
          getEffectiveJukeboxPercent,
          hookHowlPrototype,
          hookYouTubePlayer,
          installTabFocusHooks,
          installYouTubeReadyCallbackHook,
          patchGameVolumeMenu,
          patchJukeboxKnob,
          patchJukeboxMenu,
          patchLobbyMusicController,
          removeJukeboxMenuItem,
          restoreJukeboxState,
          setJukeboxState,
          stopLobbyMusicIfNeeded
        } = createAudioFeatureBundle({
          focusActiveRenderCanvas,
          getActiveRenderCanvas,
          getActiveRenderMode,
          isAudioEnabled: featureGates.isAudioEnabled,
          isChatInput,
          isReserveRetryAudioSuppressed: () => Boolean(
            featureGates.isReserveEnabled() && getReserveState()?.active && isReserveRetryAudioSuppressed()
          ),
          resetBrowserScroll,
          scheduleUiWork: scheduleAppUiWork
        });
        const {
          endCurrentGame,
          findPlayerByName: findPlayerByName2,
          installPlayerPopupDismissal: installPlayerPopupDismissal2,
          handleJoinSlashCommand,
          handleQolboxSlashCommand,
          handleBlacklistSlashCommand,
          handleSpecSlashCommand,
          patchSlashCommands,
          patchLobbyBlacklist,
          enforceBlacklist,
          patchSwitchTeamsButton,
          requestBulkTeamState,
          requestTeamState,
          restartCurrentGame,
          removeSwitchTeamsButton,
          showAllHostSettings,
          switchTeamPlayers
        } = createLobbyCommandsFeatureBundle({
          areGameStartAlertsEnabled: featureGates.isGameStartAlertEnabled,
          areLobbyCommandsEnabled: featureGates.isLobbyCommandsEnabled,
          isBlacklistEnforcementEnabled: () => isAdvancedBlacklistEnforcementEnabled(getAdvancedSettings()),
          installStartAlertHooks: (session) => patchMultiplayerSessionGameStartHooks(session),
          isCurrentPlayerSpectating,
          noteLocallyInitiatedPlayTransition,
          setBlacklistEnforcementEnabled: (enabled) => setAdvancedSetting(ADVANCED_BLACKLIST_ENFORCEMENT, enabled)
        });
        const {
          clearFullscreenSignature,
          installFullscreenHooks,
          scheduleUiWork
        } = createFullscreenOrchestrationBundle({
          applyFeatureRootClasses,
          applyPersistentFeatures,
          buildFullscreenSignature,
          clearFullscreenLayoutStyles,
          enforceFullscreenLayout,
          ensureGlobalStyle,
          getFullscreenDimensions,
          getLayoutProbe,
          installChatCommandAliasHooks,
          installChatEscapeHooks,
          installGameReadyHook,
          installGameStartIndicatorHooks,
          installGameplayBackgroundFocusHooks,
          installQolboxMenuHooks,
          installReserveSocketCaptureHook,
          installTabFocusHooks,
          installNativeFullscreenPatch,
          isAudioEnabled: featureGates.isAudioEnabled,
          isFullscreenEnabled: featureGates.isFullscreenEnabled,
          isGameStartAlertEnabled: featureGates.isGameStartAlertEnabled,
          isMenuGameplayOverlap,
          isNativeProbeAligned,
          isRenderProbeAligned,
          isReserveEnabled: featureGates.isReserveEnabled,
          patchLobbyMusicController,
          refreshObservedResizeTargets,
          resizeKnownFullscreenRenderers: resizeKnownFullscreenRenderers2,
          runNativeResize,
          setFullscreenResizeObserver,
          setNativeFullscreenSize,
          shouldWaitForNativeLayoutSeed,
          stopLobbyMusicIfNeeded,
          syncSpectateControlsBottomWithJukebox,
          syncNonFullscreenHud: () => {
            if (featureGates.isChatEnabled()) {
              syncAllScoreRowsFromPlayers();
              syncTypingIndicators();
            }
          },
          updateGameStartIndicator
        });
        void captureGameplayInputFocus;
        void clearGameStartIndicator;
        void endCurrentGame;
        void enforceBlacklist;
        void findPlayerByName2;
        void fitEditorCanvasToNative;
        void fitEditorLayerToFrame;
        void getEffectiveJukeboxPercent;
        void getOnboardingSteps;
        void getScaledEditorFrame;
        void getWorldTypingPosition;
        void handleBlacklistSlashCommand;
        void handleGameStartInteractionFocus;
        void handleGameplayBackgroundFocus;
        void handleJoinSlashCommand;
        void handleMobileGrabPointerStart;
        void hideMobileGrabButton;
        void handlePopupKeyboard;
        void handleQolboxSlashCommand;
        void handleSpecSlashCommand;
        void hasPendingLocalPlayTransition;
        void isMobileGameMode;
        void isMobileQolboxMenuContext;
        void isPageFocused;
        void isPlayableLobby;
        void layoutMobileGrabButton;
        void layoutRelativeHud;
        void notePlayerTyping;
        void requestBulkTeamState;
        void requestTeamState;
        void restartCurrentGame;
        void restoreLobbyChatPrompt;
        void setGameStartPageFocused;
        void setGameStartWasInLobbyWhenUnfocused;
        void setGameStartWasPlayingWhenUnfocused;
        void setJukeboxState;
        void setMobileGrabPressed;
        void shouldCaptureGameplayBackgroundFocus;
        void shouldShowMobileGrabButton;
        void showAllHostSettings;
        void switchTeamPlayers;
        void syncAllScoreRowsFromPlayers;
        void syncMobileGrabButton;
        void syncWorldTypingIndicators;
        runQolboxStartupSequence({
          applyFeatureRootClasses,
          ensureGlobalStyle,
          installFullscreenHooks,
          installPopupKeyboardHooks,
          installQolboxMenuHooks,
          installReserveSocketCaptureHook,
          installYouTubeReadyCallbackHook,
          isAudioEnabled: featureGates.isAudioEnabled,
          isReserveEnabled: featureGates.isReserveEnabled,
          scheduleFirstBootOnboarding,
          scheduleUiWork
        });
      })();
    })();

  }

  installReleaseHistoryBridge();
  injectPageFunction(runQolboxPageApp, 'QOLBox.page.js');
})();