Rome

Unified Erome script: content filtering, photo/video toggles, multi-video playback, watched tracking, disclaimer bypass. Enhanced with IntersectionObserver, resilient storage, and debug utilities. Auto-removes repost/flag buttons.

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         Rome
// @namespace    n2ch
// @version      1.7.203
// @description  Unified Erome script: content filtering, photo/video toggles, multi-video playback, watched tracking, disclaimer bypass. Enhanced with IntersectionObserver, resilient storage, and debug utilities. Auto-removes repost/flag buttons.
// @icon         https://www.google.com/s2/favicons?sz=64&domain=erome.com
// @match        *://*.erome.com/*
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/billy-herrington-utils.umd.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/jabroni-outfit.umd.js
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_info
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        unsafeWindow
// @inject-into  page
// @run-at       document-idle
// @noframes
// ==/UserScript==

(function () {
  'use strict';

  var __defProp = Object.defineProperty;
  var __typeError = (msg) => {
    throw TypeError(msg);
  };
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
  var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
  var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
  var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
  var __privateWrapper = (obj, member, setter, getter) => ({
    set _(value) {
      __privateSet(obj, member, value);
    },
    get _() {
      return __privateGet(obj, member, getter);
    }
  });
  var _VALID_POSITIONS, _deps, _initialized, _overlayEl, _watchStarted, _textObserver, _totalPages, _positionSide, _keydownHandler, _resizeHandler, _bodyObserver, _rewriteFailCount, _rafPending, _pendingDesired, _pendingTextSpan, _pageFromEvent, _lastSearch, _pollId, _settleTimers, _waitTimeoutId, _childWatcherTTL, _childWatcher, _docObserver, _listeners, _InfyScrollOverlay_instances, loadPosition_fn, isOverlayRendering_fn, findTextSpan_fn, findIconEl_fn, styleOverlay_fn, getEromeTotalPages_fn, getCurrentPage_fn, updateCounter_fn, watchPageText_fn, initOverlay_fn, measureLayout_fn, applyDynamicPosition_fn, setupResizeHandler_fn, setupKeyboardShortcut_fn, waitForOverlay_fn, reanchorBodyObserver_fn, onNavigation_fn, onAppend_fn;
  var _GM_addStyle = (() => typeof GM_addStyle != "undefined" ? GM_addStyle : void 0)();
  var _GM_getValue = (() => typeof GM_getValue != "undefined" ? GM_getValue : void 0)();
  var _GM_registerMenuCommand = (() => typeof GM_registerMenuCommand != "undefined" ? GM_registerMenuCommand : void 0)();
  var _GM_setValue = (() => typeof GM_setValue != "undefined" ? GM_setValue : void 0)();
  var _unsafeWindow = (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)();
  function getVideoId(url) {
    const match = url.match(/\/a\/([^/?]+)/);
    return match ? match[1] : null;
  }
  function derivePosterFromVideoUrl(videoUrl) {
    return videoUrl.replace(/^(https?:\/\/)v(\d+)\.erome\.com\//, "$1s$2.erome.com/").replace(/_\d{3,4}p\.mp4$/, ".jpg");
  }
  const WARM_BUFFER_COUNT = 2;
  const _romePreloadIntersecting = new Map();
  let _romePreloadObserver = null;
  function getRomePreloadObserver() {
    if (_romePreloadObserver) return _romePreloadObserver;
    if (typeof IntersectionObserver === "undefined") return null;
    _romePreloadObserver = new IntersectionObserver(
      (entries) => {
        for (const entry of entries) {
          const v = entry.target;
          if (entry.isIntersecting) {
            _romePreloadIntersecting.set(v, Math.abs(entry.boundingClientRect.top));
          } else {
            _romePreloadIntersecting.delete(v);
            if (v.preload !== "metadata") v.preload = "metadata";
          }
        }
        const ranked = [..._romePreloadIntersecting.entries()].sort(
          (a, b) => a[1] - b[1]
        );
        ranked.forEach(([v], i) => {
          const want = i < WARM_BUFFER_COUNT ? "auto" : "metadata";
          if (v.preload !== want) v.preload = want;
        });
      },
      { rootMargin: "800px 0px", threshold: 0.01 }
    );
    return _romePreloadObserver;
  }
  let _romePlaybackObserver = null;
  function getRomePlaybackObserver() {
    if (_romePlaybackObserver) return _romePlaybackObserver;
    if (typeof IntersectionObserver === "undefined") return null;
    _romePlaybackObserver = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) return;
          const v = entry.target;
          if (!v.paused) v.pause();
        });
      },
      { rootMargin: "0px", threshold: 0 }
    );
    return _romePlaybackObserver;
  }
  function prioritizeVideoForPlayback(v) {
    if (v.preload !== "auto") v.preload = "auto";
    v.fetchPriority = "high";
  }
  function createAlbumMutationObserver({
    target,
    itemSelector,
    onItemsAdded
  }) {
    if (!target) return null;
    if (typeof MutationObserver === "undefined") return null;
    const observer = new MutationObserver((records) => {
      var _a;
      const matched = [];
      for (const rec of records) {
        for (const node of rec.addedNodes) {
          if (node.nodeType !== 1) continue;
          const el = node;
          if ((_a = el.matches) == null ? void 0 : _a.call(el, itemSelector)) {
            matched.push(el);
          } else if (el.querySelectorAll) {
            for (const descendant of el.querySelectorAll(itemSelector)) {
              matched.push(descendant);
            }
          }
        }
      }
      if (matched.length > 0) onItemsAdded(matched);
    });
    observer.observe(target, { childList: true, subtree: true });
    return observer;
  }
  const INFY_PAGE_IFRAME_SELECTOR = 'iframe[id^="infy-scroll-page"]';
  function removeDisclaimersIn(doc, selector) {
    if (!doc) return 0;
    const found = doc.querySelectorAll(selector);
    if (found.length === 0) return 0;
    found.forEach((el) => el.remove());
    if (doc.body) doc.body.style.overflow = "visible";
    return found.length;
  }
  function createDisclaimerWatcher({
    selector,
    doc,
    root,
    dismiss,
    onLog = () => {
    },
    onError = (...args) => console.error("[Rome] DisclaimerBypass", ...args)
  }) {
    let dismissState = "idle";
    const handledIframes = new WeakSet();
    function onDisclaimersRemoved(count, where) {
      onLog("DisclaimerBypass", `removed ${count} disclaimer element(s) from ${where}`);
      if (dismissState !== "idle") return;
      dismissState = "pending";
      try {
        Promise.resolve(dismiss()).then(
          () => {
            dismissState = "done";
            onLog("DisclaimerBypass", "server-side dismissal acknowledged");
          },
          (err) => {
            dismissState = "idle";
            onError("server-side dismissal failed", err);
          }
        );
      } catch (err) {
        dismissState = "idle";
        onError("server-side dismissal threw", err);
      }
    }
    function sweep(target, where) {
      const removed = removeDisclaimersIn(target, selector);
      if (removed > 0) onDisclaimersRemoved(removed, where);
    }
    function sweepIframe(iframe) {
      try {
        sweep(iframe.contentDocument, iframe.id || "infy iframe");
      } catch (err) {
        if (err instanceof DOMException && err.name === "SecurityError") return;
        onError("iframe sweep failed", err);
      }
    }
    function watchIframe(iframe) {
      if (handledIframes.has(iframe)) return;
      handledIframes.add(iframe);
      const frame = iframe;
      sweepIframe(frame);
      frame.addEventListener("load", () => sweepIframe(frame));
    }
    sweep(doc, "document");
    doc.querySelectorAll(INFY_PAGE_IFRAME_SELECTOR).forEach(watchIframe);
    if (!root) return null;
    if (typeof MutationObserver === "undefined") return null;
    const observer = new MutationObserver((records) => {
      var _a, _b, _c, _d;
      try {
        let needsDocumentSweep = false;
        const iframes = [];
        for (const rec of records) {
          for (const node of rec.addedNodes) {
            if (node.nodeType !== 1) continue;
            const el = node;
            if (((_a = el.matches) == null ? void 0 : _a.call(el, selector)) || ((_b = el.querySelector) == null ? void 0 : _b.call(el, selector))) {
              needsDocumentSweep = true;
            }
            if ((_c = el.matches) == null ? void 0 : _c.call(el, INFY_PAGE_IFRAME_SELECTOR)) iframes.push(el);
            (_d = el.querySelectorAll) == null ? void 0 : _d.call(el, INFY_PAGE_IFRAME_SELECTOR).forEach((f) => iframes.push(f));
          }
        }
        if (needsDocumentSweep) sweep(doc, "document");
        iframes.forEach(watchIframe);
      } catch (err) {
        onError("observer error", err);
      }
    });
    observer.observe(root, { childList: true, subtree: true });
    return {
      disconnect() {
        observer.disconnect();
      }
    };
  }
  function computeOverlayCss({
    layout,
    viewportWidth,
    config,
    positionSide
  }) {
    if (layout === null) {
      return {
        top: "auto",
        bottom: `${config.SIDE_GAP}px`,
        left: "auto",
        right: `${config.SIDE_GAP}px`
      };
    }
    const top = `${Math.max(layout.tabsTop + config.TOP_GAP, config.SIDE_GAP)}px`;
    if (positionSide === "left") {
      return {
        top,
        right: "auto",
        bottom: "auto",
        left: `${layout.tabsRight + config.SIDE_GAP}px`
      };
    }
    if (positionSide === "bottom-right") {
      return {
        top: "auto",
        bottom: `${config.SIDE_GAP}px`,
        left: `${layout.rightX}px`,
        right: "auto"
      };
    }
    if (positionSide === "bottom-left") {
      return {
        top: "auto",
        bottom: `${config.SIDE_GAP}px`,
        left: "auto",
        right: `${Math.max(viewportWidth - layout.leftX, 0)}px`
      };
    }
    if (positionSide !== "right") {
      console.warn("[InfyOverlay] computeOverlayCss: unrecognized positionSide:", positionSide, "-- falling back to right");
    }
    return {
      top,
      right: `${Math.max(viewportWidth - layout.rightX, 0)}px`,
      bottom: "auto",
      left: "auto"
    };
  }
  function notifyStorageFailure(context) {
    const TOAST_ID = "rome-storage-toast";
    let toast = document.getElementById(TOAST_ID);
    if (!toast) {
      toast = document.createElement("div");
      toast.id = TOAST_ID;
      Object.assign(toast.style, {
        position: "fixed",
        bottom: "20px",
        right: "20px",
        zIndex: "99999",
        padding: "10px 16px",
        background: "rgba(30, 30, 30, 0.9)",
        color: "#fff",
        fontSize: "13px",
        borderRadius: "6px",
        fontFamily: "system-ui, sans-serif",
        pointerEvents: "none",
        opacity: "1",
        transition: "opacity 0.4s ease"
      });
      document.body.appendChild(toast);
    }
    toast.textContent = `Rome: Failed to save ${context}`;
    toast.style.opacity = "1";
    clearTimeout(toast._romeTimer);
    toast._romeTimer = setTimeout(() => {
      toast.style.opacity = "0";
      setTimeout(() => toast.remove(), 400);
    }, 4e3);
  }
  function maxPageFromTexts(texts) {
    let max = 0;
    for (const t of texts) {
      const n = parseInt(t.trim(), 10);
      if (!isNaN(n) && n > max) max = n;
    }
    return max > 0 ? max : null;
  }
  function parsePageParam(search) {
    const raw = new URLSearchParams(search).get("page");
    const n = parseInt(raw ?? "", 10);
    return !isNaN(n) && n > 0 ? n : null;
  }
  function parsePageFromUrl(url) {
    if (!url) return null;
    try {
      return parsePageParam(new URL(url, "https://invalid.local").search);
    } catch {
      return null;
    }
  }
  function computeCounter(currentPage, liveTotal, prevTotal) {
    const knownTotal = Math.max(prevTotal ?? 0, liveTotal ?? 0);
    const total = knownTotal > 0 ? knownTotal : null;
    let desired = null;
    if (currentPage !== null && total !== null) desired = currentPage + "/" + total;
    else if (currentPage !== null) desired = currentPage + "/?";
    else if (total !== null) desired = "?/" + total;
    return { knownTotal, desired };
  }
  const _InfyScrollOverlay = class _InfyScrollOverlay {
    constructor(deps) {
      __privateAdd(this, _InfyScrollOverlay_instances);
      __privateAdd(this, _deps);
      __privateAdd(this, _initialized, false);
      __privateAdd(this, _overlayEl, null);
      __privateAdd(this, _watchStarted, false);
      __privateAdd(this, _textObserver, null);
      __privateAdd(this, _totalPages, null);
__privateAdd(this, _positionSide);
      __privateAdd(this, _keydownHandler, null);
      __privateAdd(this, _resizeHandler, null);
      __privateAdd(this, _bodyObserver, null);
      __privateAdd(this, _rewriteFailCount, 0);
      __privateAdd(this, _rafPending, false);
      __privateAdd(this, _pendingDesired, null);
      __privateAdd(this, _pendingTextSpan, null);
__privateAdd(this, _pageFromEvent, null);
__privateAdd(this, _lastSearch, null);
__privateAdd(this, _pollId, null);
__privateAdd(this, _settleTimers, new Set());
      __privateAdd(this, _waitTimeoutId, null);
      __privateAdd(this, _childWatcherTTL, null);
__privateAdd(this, _childWatcher, null);
      __privateAdd(this, _docObserver, null);
      __privateAdd(this, _listeners, []);
      __privateSet(this, _deps, deps);
      __privateSet(this, _positionSide, __privateMethod(this, _InfyScrollOverlay_instances, loadPosition_fn).call(this));
    }
    start() {
      __privateMethod(this, _InfyScrollOverlay_instances, waitForOverlay_fn).call(this);
      const on = (target, type, fn) => {
        target.addEventListener(type, fn);
        __privateGet(this, _listeners).push({ target, type, fn });
      };
      on(document, "GM_AutoPagerizeLoaded", () => __privateMethod(this, _InfyScrollOverlay_instances, onNavigation_fn).call(this, "GM_AutoPagerizeLoaded"));
      on(document, "AutoPagerize_DOMNodeInserted", (e) => {
        const detail = e.detail;
        __privateMethod(this, _InfyScrollOverlay_instances, onAppend_fn).call(this, "AutoPagerize_DOMNodeInserted", detail == null ? void 0 : detail.url);
      });
      on(document, "GM_AutoPagerizeNextPageLoaded", () => {
        const timerId = setTimeout(() => {
          __privateGet(this, _settleTimers).delete(timerId);
          const barPage = parsePageParam(window.location.search) ?? 1;
          const barIsLagging = __privateGet(this, _pageFromEvent) !== null && barPage === __privateGet(this, _pageFromEvent) - 1;
          if (!barIsLagging) __privateSet(this, _pageFromEvent, null);
          __privateMethod(this, _InfyScrollOverlay_instances, onAppend_fn).call(this, "GM_AutoPagerizeNextPageLoaded");
        }, _InfyScrollOverlay.APPEND_SETTLE_MS);
        __privateGet(this, _settleTimers).add(timerId);
      });
      const orig = {
        pushState: history.pushState.bind(history),
        replaceState: history.replaceState.bind(history)
      };
      history.pushState = (...args) => {
        orig.pushState(...args);
        __privateMethod(this, _InfyScrollOverlay_instances, onNavigation_fn).call(this, "pushState");
      };
      history.replaceState = (...args) => {
        orig.replaceState(...args);
        __privateMethod(this, _InfyScrollOverlay_instances, onNavigation_fn).call(this, "replaceState");
      };
      on(window, "popstate", () => __privateMethod(this, _InfyScrollOverlay_instances, onNavigation_fn).call(this, "popstate"));
      __privateSet(this, _docObserver, new MutationObserver((mutations) => {
        for (const m of mutations) {
          for (const node of m.addedNodes) {
            if (node === document.body) {
              __privateGet(this, _deps).logger.log(
                "[InfyOverlay]",
                "document.body replaced -- re-anchoring observer"
              );
              __privateMethod(this, _InfyScrollOverlay_instances, reanchorBodyObserver_fn).call(this);
            }
          }
        }
      }));
      __privateGet(this, _docObserver).observe(document.documentElement, { childList: true });
      __privateSet(this, _pollId, setInterval(() => {
        const el = document.getElementById(_InfyScrollOverlay.OVERLAY_ID);
        if (!el) return;
        if (!__privateGet(this, _initialized)) {
          __privateGet(this, _deps).logger.log(
            "[InfyOverlay]",
            "poll: uninitialized -- forcing _initOverlay",
            { pathname: window.location.pathname }
          );
          __privateMethod(this, _InfyScrollOverlay_instances, initOverlay_fn).call(this, el);
          return;
        }
        if (!__privateMethod(this, _InfyScrollOverlay_instances, isOverlayRendering_fn).call(this, el)) {
          __privateGet(this, _deps).logger.log(
            "[InfyOverlay]",
            "poll: initialized but hidden -- re-applying styles",
            { pathname: window.location.pathname }
          );
          __privateMethod(this, _InfyScrollOverlay_instances, styleOverlay_fn).call(this, el);
          return;
        }
        const search = window.location.search;
        if (search !== __privateGet(this, _lastSearch)) {
          __privateSet(this, _lastSearch, search);
          const fromUrl = parsePageParam(search);
          const barIsLagging = fromUrl !== null && __privateGet(this, _pageFromEvent) !== null && fromUrl === __privateGet(this, _pageFromEvent) - 1;
          if (fromUrl !== null && !barIsLagging) __privateSet(this, _pageFromEvent, null);
          __privateGet(this, _deps).logger.log("[InfyOverlay]", "poll: url changed", {
            search,
            fromUrl,
            barIsLagging
          });
          __privateMethod(this, _InfyScrollOverlay_instances, updateCounter_fn).call(this, el);
        }
      }, 1e3));
    }
stop() {
      if (__privateGet(this, _pollId) !== null) {
        clearInterval(__privateGet(this, _pollId));
        __privateSet(this, _pollId, null);
      }
      for (const id of __privateGet(this, _settleTimers)) clearTimeout(id);
      __privateGet(this, _settleTimers).clear();
      for (const id of [__privateGet(this, _waitTimeoutId), __privateGet(this, _childWatcherTTL)]) {
        if (id !== null) clearTimeout(id);
      }
      __privateSet(this, _waitTimeoutId, null);
      __privateSet(this, _childWatcherTTL, null);
      for (const { target, type, fn } of __privateGet(this, _listeners)) {
        target.removeEventListener(type, fn);
      }
      __privateSet(this, _listeners, []);
      for (const obs of [
        __privateGet(this, _docObserver),
        __privateGet(this, _bodyObserver),
        __privateGet(this, _textObserver),
        __privateGet(this, _childWatcher)
      ]) {
        obs == null ? void 0 : obs.disconnect();
      }
      __privateSet(this, _docObserver, null);
      __privateSet(this, _bodyObserver, null);
      __privateSet(this, _textObserver, null);
      __privateSet(this, _childWatcher, null);
      if (__privateGet(this, _keydownHandler)) {
        document.removeEventListener("keydown", __privateGet(this, _keydownHandler));
        __privateSet(this, _keydownHandler, null);
      }
      if (__privateGet(this, _resizeHandler)) {
        window.removeEventListener("resize", __privateGet(this, _resizeHandler));
        __privateSet(this, _resizeHandler, null);
      }
      __privateSet(this, _watchStarted, false);
      __privateSet(this, _initialized, false);
      __privateSet(this, _overlayEl, null);
      __privateSet(this, _pageFromEvent, null);
      __privateSet(this, _lastSearch, null);
      __privateSet(this, _totalPages, null);
      __privateSet(this, _pendingDesired, null);
      __privateSet(this, _pendingTextSpan, null);
      __privateSet(this, _rewriteFailCount, 0);
    }
  };
  _VALID_POSITIONS = new WeakMap();
  _deps = new WeakMap();
  _initialized = new WeakMap();
  _overlayEl = new WeakMap();
  _watchStarted = new WeakMap();
  _textObserver = new WeakMap();
  _totalPages = new WeakMap();
  _positionSide = new WeakMap();
  _keydownHandler = new WeakMap();
  _resizeHandler = new WeakMap();
  _bodyObserver = new WeakMap();
  _rewriteFailCount = new WeakMap();
  _rafPending = new WeakMap();
  _pendingDesired = new WeakMap();
  _pendingTextSpan = new WeakMap();
  _pageFromEvent = new WeakMap();
  _lastSearch = new WeakMap();
  _pollId = new WeakMap();
  _settleTimers = new WeakMap();
  _waitTimeoutId = new WeakMap();
  _childWatcherTTL = new WeakMap();
  _childWatcher = new WeakMap();
  _docObserver = new WeakMap();
  _listeners = new WeakMap();
  _InfyScrollOverlay_instances = new WeakSet();
  loadPosition_fn = function() {
    try {
      const stored = __privateGet(this, _deps).getValue(
        __privateGet(this, _deps).config.STORAGE_KEYS.OVERLAY_POSITION,
        null
      );
      if (stored && __privateGet(_InfyScrollOverlay, _VALID_POSITIONS).includes(stored)) {
        return stored;
      }
      if (stored) console.warn("[InfyOverlay] discarding invalid persisted position:", stored);
    } catch (e) {
      console.warn("[InfyOverlay] failed to load persisted position:", e);
    }
    return __privateGet(this, _deps).config.INFY_OVERLAY.DEFAULT_SIDE;
  };
  isOverlayRendering_fn = function(el) {
    if (!el) return false;
    const s = getComputedStyle(el);
    if (s.display === "none" || s.visibility === "hidden" || s.opacity === "0")
      return false;
    const rect = el.getBoundingClientRect();
    return rect.width > 0 && rect.height > 0;
  };
  findTextSpan_fn = function(overlay) {
    const numberSpan = overlay.querySelector(`#${_InfyScrollOverlay.NUMBER_ID}`);
    if (numberSpan) {
      numberSpan.setAttribute("data-infy-text", "number");
      return numberSpan;
    }
    const tagged = overlay.querySelector("[data-infy-text]");
    if (tagged && tagged.getAttribute("data-infy-text") !== "fallback")
      return tagged;
    for (const child of overlay.children) {
      if (_InfyScrollOverlay.PAGE_PATTERN.test(child.textContent ?? "")) {
        child.setAttribute("data-infy-text", "");
        return child;
      }
    }
    const fallback = overlay.children[1];
    if (fallback && fallback.tagName === "SPAN") {
      fallback.setAttribute("data-infy-text", "fallback");
      __privateGet(this, _deps).logger.log(
        "[InfyOverlay]",
        "Using fallback text span (children[1]); PAGE_PATTERN did not match"
      );
      return fallback;
    }
    __privateGet(this, _deps).logger.log("[InfyOverlay]", "_findTextSpan: no valid text span found");
    return null;
  };
  findIconEl_fn = function(overlay) {
    return overlay.querySelector("#infy-scroll-overlay-icon") || overlay.querySelector("svg") || null;
  };
  styleOverlay_fn = function(overlay) {
    const s = overlay.style;
    const c = __privateGet(this, _deps).config.INFY_OVERLAY;
    s.setProperty("display", "block", "important");
    s.setProperty("position", "fixed", "important");
    s.setProperty("background", c.BACKGROUND, "important");
    s.setProperty("width", "fit-content", "important");
    s.setProperty("max-width", "none", "important");
    __privateMethod(this, _InfyScrollOverlay_instances, applyDynamicPosition_fn).call(this, overlay);
    s.setProperty("padding", c.PADDING, "important");
    s.setProperty("border-radius", c.BORDER_RADIUS, "important");
    s.setProperty("box-shadow", "none", "important");
    s.setProperty("border", "none", "important");
    const textSpan = __privateMethod(this, _InfyScrollOverlay_instances, findTextSpan_fn).call(this, overlay);
    if (textSpan) {
      textSpan.style.setProperty("background", "transparent", "important");
      textSpan.style.setProperty("color", c.TEXT_COLOR, "important");
      textSpan.style.setProperty("font-size", c.FONT_SIZE, "important");
      textSpan.style.setProperty("font-family", c.FONT_FAMILY, "important");
      textSpan.style.setProperty("font-weight", "normal", "important");
      textSpan.style.setProperty("white-space", "nowrap", "important");
      textSpan.style.setProperty("display", "block", "important");
      textSpan.style.setProperty("width", "fit-content", "important");
      __privateMethod(this, _InfyScrollOverlay_instances, updateCounter_fn).call(this, overlay);
    }
    const iconEl = __privateMethod(this, _InfyScrollOverlay_instances, findIconEl_fn).call(this, overlay);
    if (iconEl) {
      iconEl.style.setProperty("display", "none", "important");
    }
    const closeEl = overlay.querySelector("#infy-scroll-overlay-close");
    const moveEl = overlay.querySelector("#infy-scroll-overlay-move");
    if (closeEl) closeEl.style.setProperty("display", "none", "important");
    if (moveEl) moveEl.style.setProperty("display", "none", "important");
  };
  getEromeTotalPages_fn = function() {
    const items = document.querySelectorAll("ul.pagination li a, ul.pagination li span");
    const texts = [];
    for (const el of items) texts.push(el.textContent ?? "");
    return maxPageFromTexts(texts);
  };
  getCurrentPage_fn = function() {
    if (__privateGet(this, _pageFromEvent) !== null) return __privateGet(this, _pageFromEvent);
    const urlPage = parsePageParam(window.location.search);
    if (urlPage !== null) return urlPage;
    const activeSpan = document.querySelector("ul.pagination li.active span");
    if (activeSpan) {
      const n = parseInt((activeSpan.textContent ?? "").trim(), 10);
      if (!isNaN(n)) return n;
    }
    return null;
  };
  updateCounter_fn = function(overlay) {
    const textSpan = __privateMethod(this, _InfyScrollOverlay_instances, findTextSpan_fn).call(this, overlay);
    if (!textSpan) {
      __privateGet(this, _deps).logger.log("[InfyOverlay]", "_updateCounter: textSpan not found; skipping");
      return;
    }
    const currentPage = __privateMethod(this, _InfyScrollOverlay_instances, getCurrentPage_fn).call(this);
    const liveTotal = __privateMethod(this, _InfyScrollOverlay_instances, getEromeTotalPages_fn).call(this);
    __privateGet(this, _deps).logger.log(
      "[InfyOverlay]",
      `_updateCounter: page=${currentPage} total=${liveTotal} url=${window.location.search} spanInDoc=${document.contains(textSpan)}`
    );
    const { knownTotal, desired } = computeCounter(currentPage, liveTotal, __privateGet(this, _totalPages));
    if (knownTotal > 0) __privateSet(this, _totalPages, knownTotal);
    if (desired === null) return;
    if (textSpan.textContent === desired) return;
    __privateSet(this, _pendingDesired, desired);
    __privateSet(this, _pendingTextSpan, textSpan);
    if (__privateGet(this, _rafPending)) return;
    __privateSet(this, _rafPending, true);
    requestAnimationFrame(() => {
      __privateSet(this, _rafPending, false);
      const span = __privateGet(this, _pendingTextSpan);
      const value = __privateGet(this, _pendingDesired);
      try {
        if (span && document.contains(span) && span.textContent !== value) {
          span.textContent = value;
        }
      } catch (e) {
        __privateSet(this, _rewriteFailCount, __privateGet(this, _rewriteFailCount) + 1);
        console.error("[InfyOverlay] rAF counter update failed:", e);
      }
    });
  };
  watchPageText_fn = function(overlay) {
    if (__privateGet(this, _textObserver)) {
      __privateGet(this, _textObserver).disconnect();
    }
    __privateSet(this, _textObserver, new MutationObserver(() => {
      const liveOverlay = document.getElementById(_InfyScrollOverlay.OVERLAY_ID);
      if (!liveOverlay) return;
      const currentSpan = __privateMethod(this, _InfyScrollOverlay_instances, findTextSpan_fn).call(this, liveOverlay);
      if (!currentSpan) return;
      try {
        __privateGet(this, _textObserver).disconnect();
        __privateMethod(this, _InfyScrollOverlay_instances, updateCounter_fn).call(this, liveOverlay);
        __privateMethod(this, _InfyScrollOverlay_instances, applyDynamicPosition_fn).call(this, liveOverlay);
        __privateSet(this, _rewriteFailCount, 0);
      } catch (e) {
        __privateSet(this, _rewriteFailCount, __privateGet(this, _rewriteFailCount) + 1);
        if (__privateGet(this, _rewriteFailCount) >= 5) {
          if (__privateGet(this, _rewriteFailCount) >= 10) {
            console.error(
              "[InfyOverlay] _updateCounter failed",
              __privateGet(this, _rewriteFailCount),
              "times -- giving up on recovery:",
              e
            );
            return;
          }
          console.warn(
            "[InfyOverlay] _updateCounter failed",
            __privateGet(this, _rewriteFailCount),
            "times consecutively -- triggering re-init:",
            e
          );
          const recoveryOverlay = document.getElementById(_InfyScrollOverlay.OVERLAY_ID);
          if (recoveryOverlay) {
            __privateSet(this, _initialized, false);
            try {
              __privateMethod(this, _InfyScrollOverlay_instances, initOverlay_fn).call(this, recoveryOverlay);
              __privateSet(this, _rewriteFailCount, 0);
            } catch (e2) {
              __privateWrapper(this, _rewriteFailCount)._++;
              console.error("[InfyOverlay] Recovery _initOverlay failed:", e2);
            }
          }
          return;
        }
        __privateGet(this, _deps).logger.log("[InfyOverlay]", "Observer error:", e);
      } finally {
        if (__privateGet(this, _rewriteFailCount) < 5) {
          try {
            __privateGet(this, _textObserver).observe(currentSpan, {
              characterData: true,
              childList: true,
              subtree: true
            });
          } catch (observeErr) {
            __privateGet(this, _deps).logger.log(
              "[InfyOverlay]",
              "Failed to re-observe text span:",
              observeErr
            );
          }
        }
      }
    }));
    const initialSpan = __privateMethod(this, _InfyScrollOverlay_instances, findTextSpan_fn).call(this, overlay);
    let spanObserveStarted = false;
    if (initialSpan) {
      try {
        __privateGet(this, _textObserver).observe(initialSpan, {
          characterData: true,
          childList: true,
          subtree: true
        });
        spanObserveStarted = true;
      } catch (e) {
        console.error("[InfyOverlay] _textObserver.observe failed on initialSpan:", e);
      }
    }
    if (!spanObserveStarted) {
      __privateSet(this, _childWatcherTTL, setTimeout(() => {
        __privateSet(this, _childWatcherTTL, null);
        childWatcher.disconnect();
        if (__privateGet(this, _childWatcher) === childWatcher) __privateSet(this, _childWatcher, null);
        __privateGet(this, _deps).logger.log(
          "[InfyOverlay]",
          "childWatcher timed out waiting for text span"
        );
      }, __privateGet(this, _deps).config.INFY_OVERLAY.CHILD_WAIT_TIMEOUT));
      const childWatcher = new MutationObserver((_, obs) => {
        const liveOverlay = document.getElementById(_InfyScrollOverlay.OVERLAY_ID);
        const span = liveOverlay && __privateMethod(this, _InfyScrollOverlay_instances, findTextSpan_fn).call(this, liveOverlay);
        if (span) {
          if (__privateGet(this, _childWatcherTTL) !== null) {
            clearTimeout(__privateGet(this, _childWatcherTTL));
            __privateSet(this, _childWatcherTTL, null);
          }
          obs.disconnect();
          if (__privateGet(this, _childWatcher) === obs) __privateSet(this, _childWatcher, null);
          try {
            __privateGet(this, _textObserver).observe(span, {
              characterData: true,
              childList: true,
              subtree: true
            });
          } catch (e) {
            console.error(
              "[InfyOverlay] childWatcher _textObserver.observe failed:",
              e
            );
            return;
          }
          __privateMethod(this, _InfyScrollOverlay_instances, updateCounter_fn).call(this, overlay);
          return;
        }
      });
      __privateSet(this, _childWatcher, childWatcher);
      childWatcher.observe(overlay, { childList: true });
    }
  };
  initOverlay_fn = function(overlay) {
    __privateGet(this, _deps).logger.log("[InfyOverlay]", "_initOverlay called", {
      id: overlay == null ? void 0 : overlay.id,
      initialized: __privateGet(this, _initialized),
      pathname: window.location.pathname
    });
    if (__privateGet(this, _initialized)) {
      if (__privateGet(this, _overlayEl) === overlay) {
        __privateGet(this, _deps).logger.log(
          "[InfyOverlay]",
          "Overlay re-inserted but already initialized; skipping re-init"
        );
        return;
      }
      console.warn(
        "[InfyOverlay] Overlay element replaced -- re-initializing for new element"
      );
      __privateSet(this, _initialized, false);
      __privateSet(this, _totalPages, null);
      if (__privateGet(this, _textObserver)) {
        __privateGet(this, _textObserver).disconnect();
        __privateSet(this, _textObserver, null);
      }
      if (__privateGet(this, _keydownHandler)) {
        document.removeEventListener("keydown", __privateGet(this, _keydownHandler));
        __privateSet(this, _keydownHandler, null);
      }
      if (__privateGet(this, _resizeHandler)) {
        window.removeEventListener("resize", __privateGet(this, _resizeHandler));
        __privateSet(this, _resizeHandler, null);
      }
      __privateSet(this, _rewriteFailCount, 0);
      __privateSet(this, _positionSide, __privateMethod(this, _InfyScrollOverlay_instances, loadPosition_fn).call(this));
    }
    __privateSet(this, _initialized, true);
    __privateSet(this, _overlayEl, overlay);
    __privateSet(this, _totalPages, __privateMethod(this, _InfyScrollOverlay_instances, getEromeTotalPages_fn).call(this));
    try {
      __privateMethod(this, _InfyScrollOverlay_instances, styleOverlay_fn).call(this, overlay);
    } catch (e) {
      console.error("[InfyOverlay] _styleOverlay failed:", e);
      __privateSet(this, _initialized, false);
    }
    try {
      __privateMethod(this, _InfyScrollOverlay_instances, watchPageText_fn).call(this, overlay);
    } catch (e) {
      console.error("[InfyOverlay] _watchPageText failed:", e);
      __privateSet(this, _initialized, false);
    }
    try {
      __privateMethod(this, _InfyScrollOverlay_instances, setupResizeHandler_fn).call(this);
    } catch (e) {
      console.error("[InfyOverlay] _setupResizeHandler failed:", e);
    }
    try {
      __privateMethod(this, _InfyScrollOverlay_instances, setupKeyboardShortcut_fn).call(this);
    } catch (e) {
      console.error("[InfyOverlay] _setupKeyboardShortcut failed:", e);
    }
  };
  measureLayout_fn = function() {
    const tabs = document.querySelector("#tabs");
    const tabsRect = tabs ? tabs.getBoundingClientRect() : null;
    const tabsTop = tabsRect ? tabsRect.top : 150;
    let tabsRight = tabsRect ? tabsRect.right : 0;
    if (tabs && tabs.children.length > 0) {
      const childRights = [];
      for (const child of tabs.children) {
        const cs = getComputedStyle(child);
        if (cs.display === "none") continue;
        const r = child.getBoundingClientRect();
        if (r.width === 0) continue;
        childRights.push(r.right);
      }
      if (childRights.length > 0) {
        tabsRight = Math.max(...childRights);
      }
    }
    const albumsGrid = document.querySelector("#albums");
    if (!albumsGrid) {
      __privateGet(this, _deps).logger.log(
        "[InfyOverlay]",
        "_measureLayout: #albums not found, using fallback position"
      );
      return null;
    }
    const gridRect = albumsGrid.getBoundingClientRect();
    if (gridRect.width === 0) {
      __privateGet(this, _deps).logger.log(
        "[InfyOverlay]",
        "_measureLayout: #albums has zero width, using fallback position"
      );
      return null;
    }
    return {
      tabsTop,
      tabsRight,
      leftX: gridRect.left,
      rightX: gridRect.right
    };
  };
  applyDynamicPosition_fn = function(overlay) {
    const layout = __privateMethod(this, _InfyScrollOverlay_instances, measureLayout_fn).call(this);
    const css = computeOverlayCss({
      layout,
      viewportWidth: window.innerWidth,
      config: __privateGet(this, _deps).config.INFY_OVERLAY,
      positionSide: __privateGet(this, _positionSide)
    });
    const s = overlay.style;
    s.setProperty("top", css.top, "important");
    s.setProperty("right", css.right, "important");
    s.setProperty("bottom", css.bottom, "important");
    s.setProperty("left", css.left, "important");
  };
  setupResizeHandler_fn = function() {
    if (__privateGet(this, _resizeHandler)) {
      window.removeEventListener("resize", __privateGet(this, _resizeHandler));
    }
    __privateSet(this, _resizeHandler, () => {
      const liveOverlay = document.getElementById(_InfyScrollOverlay.OVERLAY_ID);
      if (!liveOverlay) return;
      try {
        __privateMethod(this, _InfyScrollOverlay_instances, applyDynamicPosition_fn).call(this, liveOverlay);
      } catch (err) {
        console.warn("[InfyOverlay] resize handler error:", err);
      }
    });
    const _capturedResizeHandler = __privateGet(this, _resizeHandler);
    window.addEventListener("resize", __privateGet(this, _resizeHandler));
    window.addEventListener(
      "pagehide",
      () => {
        if (_capturedResizeHandler) {
          window.removeEventListener("resize", _capturedResizeHandler);
        }
      },
      { once: true }
    );
  };
  setupKeyboardShortcut_fn = function() {
    if (__privateGet(this, _keydownHandler)) {
      document.removeEventListener("keydown", __privateGet(this, _keydownHandler));
    }
    __privateSet(this, _keydownHandler, (e) => {
      var _a, _b;
      if (e.key !== "f" && e.key !== "F") return;
      const tag = (_a = document.activeElement) == null ? void 0 : _a.tagName;
      if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
      if ((_b = document.activeElement) == null ? void 0 : _b.isContentEditable) return;
      if (e.metaKey || e.ctrlKey || e.altKey) return;
      try {
        __privateGet(this, _deps).logger.performance("infy-overlay-flip", () => {
          const liveOverlay = document.getElementById(_InfyScrollOverlay.OVERLAY_ID);
          if (!liveOverlay || !__privateMethod(this, _InfyScrollOverlay_instances, isOverlayRendering_fn).call(this, liveOverlay)) {
            __privateGet(this, _deps).logger.log(
              "[InfyOverlay]",
              "overlay not rendering -- flip ignored (key not consumed)",
              {
                inDOM: !!liveOverlay,
                rendering: liveOverlay ? __privateMethod(this, _InfyScrollOverlay_instances, isOverlayRendering_fn).call(this, liveOverlay) : false
              }
            );
            return;
          }
          e.preventDefault();
          const _cycle = __privateGet(_InfyScrollOverlay, _VALID_POSITIONS);
          const _idx = _cycle.indexOf(__privateGet(this, _positionSide));
          __privateSet(this, _positionSide, _cycle[(_idx + 1) % _cycle.length]);
          __privateGet(this, _deps).logger.log("[InfyOverlay]", `flip to ${__privateGet(this, _positionSide)}`);
          try {
            __privateGet(this, _deps).setValue(
              __privateGet(this, _deps).config.STORAGE_KEYS.OVERLAY_POSITION,
              __privateGet(this, _positionSide)
            );
          } catch (saveErr) {
            console.warn("[InfyOverlay] failed to persist position:", saveErr);
            notifyStorageFailure("overlay position");
          }
          __privateMethod(this, _InfyScrollOverlay_instances, applyDynamicPosition_fn).call(this, liveOverlay);
        });
      } catch (err) {
        console.warn(
          "[InfyOverlay] keydown handler error (overlay may have been removed):",
          err
        );
      }
    });
    document.addEventListener("keydown", __privateGet(this, _keydownHandler));
    const _capturedKeydownHandler = __privateGet(this, _keydownHandler);
    window.addEventListener(
      "pagehide",
      () => {
        document.removeEventListener("keydown", _capturedKeydownHandler);
      },
      { once: true }
    );
  };
  waitForOverlay_fn = function() {
    if (__privateGet(this, _watchStarted)) return;
    __privateSet(this, _watchStarted, true);
    __privateSet(this, _bodyObserver, new MutationObserver((mutations) => {
      var _a;
      for (const mutation of mutations) {
        for (const node of mutation.removedNodes) {
          if (node.nodeType === Node.ELEMENT_NODE && node.id === _InfyScrollOverlay.OVERLAY_ID) {
            __privateGet(this, _deps).logger.log(
              "[InfyOverlay]",
              "overlay removed from DOM -- marking uninitialized"
            );
            __privateSet(this, _initialized, false);
            __privateSet(this, _overlayEl, null);
          }
        }
        for (const node of mutation.addedNodes) {
          if (node.nodeType === Node.ELEMENT_NODE && node.id === _InfyScrollOverlay.OVERLAY_ID) {
            __privateGet(this, _deps).logger.log(
              "[InfyOverlay]",
              "overlay detected via bodyObserver addedNodes -- calling _initOverlay"
            );
            __privateMethod(this, _InfyScrollOverlay_instances, initOverlay_fn).call(this, node);
            return;
          }
          if (node.nodeType === Node.ELEMENT_NODE) {
            const nested = (_a = node.querySelector) == null ? void 0 : _a.call(node, `#${_InfyScrollOverlay.OVERLAY_ID}`);
            if (nested) {
              __privateGet(this, _deps).logger.log(
                "[InfyOverlay]",
                "overlay detected as nested child in addedNodes -- calling _initOverlay",
                node.tagName,
                node.id || node.className
              );
              __privateMethod(this, _InfyScrollOverlay_instances, initOverlay_fn).call(this, nested);
              return;
            }
          }
        }
      }
    }));
    try {
      __privateGet(this, _bodyObserver).observe(document.body, { childList: true, subtree: true });
    } catch (err) {
      console.error("[InfyOverlay] Failed to observe document.body:", err);
      __privateSet(this, _watchStarted, false);
      setTimeout(() => __privateMethod(this, _InfyScrollOverlay_instances, waitForOverlay_fn).call(this), 2e3);
      return;
    }
    const existing = document.getElementById(_InfyScrollOverlay.OVERLAY_ID);
    if (existing) {
      __privateMethod(this, _InfyScrollOverlay_instances, initOverlay_fn).call(this, existing);
    } else {
      __privateSet(this, _waitTimeoutId, setTimeout(() => {
        __privateSet(this, _waitTimeoutId, null);
        __privateGet(this, _deps).logger.performance("infy-overlay-wait-timeout", () => {
          const atTimeout = document.getElementById(_InfyScrollOverlay.OVERLAY_ID);
          console.warn(
            "[InfyOverlay] Timed out waiting for overlay element -- will continue watching",
            {
              topLevelFound: !!atTimeout,
              initialized: __privateGet(this, _initialized),
              pathname: window.location.pathname
            }
          );
        });
      }, __privateGet(this, _deps).config.INFY_OVERLAY.WAIT_TIMEOUT));
    }
  };
  reanchorBodyObserver_fn = function() {
    if (!__privateGet(this, _bodyObserver)) return;
    try {
      __privateGet(this, _bodyObserver).disconnect();
      __privateGet(this, _bodyObserver).observe(document.body, { childList: true, subtree: true });
      __privateGet(this, _deps).logger.log(
        "[InfyOverlay]",
        "re-anchored bodyObserver to new document.body"
      );
    } catch (err) {
      console.warn("[InfyOverlay] _reanchorBodyObserver failed:", err);
    }
    const existing = document.getElementById(_InfyScrollOverlay.OVERLAY_ID);
    if (existing && !__privateGet(this, _initialized)) {
      __privateGet(this, _deps).logger.log(
        "[InfyOverlay]",
        "overlay found in new body after re-anchor -- initializing"
      );
      __privateMethod(this, _InfyScrollOverlay_instances, initOverlay_fn).call(this, existing);
    }
  };
  onNavigation_fn = function(source) {
    __privateGet(this, _deps).logger.log(
      "[InfyOverlay]",
      `navigation signal: ${source}`,
      window.location.pathname
    );
    __privateSet(this, _initialized, false);
    __privateSet(this, _overlayEl, null);
    __privateSet(this, _pageFromEvent, null);
    __privateSet(this, _lastSearch, null);
    __privateMethod(this, _InfyScrollOverlay_instances, reanchorBodyObserver_fn).call(this);
  };
onAppend_fn = function(source, url) {
    const parsed = parsePageFromUrl(url);
    if (parsed !== null) __privateSet(this, _pageFromEvent, parsed);
    __privateGet(this, _deps).logger.log("[InfyOverlay]", `append signal: ${source}`, { url, parsed });
    const el = document.getElementById(_InfyScrollOverlay.OVERLAY_ID);
    if (!el) return;
    if (!__privateGet(this, _initialized)) {
      __privateMethod(this, _InfyScrollOverlay_instances, initOverlay_fn).call(this, el);
      return;
    }
    __privateMethod(this, _InfyScrollOverlay_instances, updateCounter_fn).call(this, el);
    try {
      __privateMethod(this, _InfyScrollOverlay_instances, applyDynamicPosition_fn).call(this, el);
    } catch (e) {
      __privateGet(this, _deps).logger.log("[InfyOverlay]", "append: reposition failed", e);
    }
  };
  __publicField(_InfyScrollOverlay, "OVERLAY_ID", "infy-scroll-overlay");
  __publicField(_InfyScrollOverlay, "NUMBER_ID", "infy-scroll-overlay-number");
  __publicField(_InfyScrollOverlay, "PAGE_PATTERN", /Page\s+(\d+)\s*\/\s*(\d+)/i);
__publicField(_InfyScrollOverlay, "APPEND_SETTLE_MS", 250);
  __privateAdd(_InfyScrollOverlay, _VALID_POSITIONS, [
    "right",
    "left",
    "bottom-left",
    "bottom-right"
  ]);
  let InfyScrollOverlay = _InfyScrollOverlay;
  function getVidData(vidDiv) {
    const sourceEl = vidDiv.querySelector(".video video source") || vidDiv.querySelector("video source") || vidDiv.querySelector("source");
    const urL = sourceEl ? sourceEl.src : "";
    var bi = vidDiv.getAttribute("data-poster") || "";
    var vjsPoster = vidDiv.querySelector(".vjs-poster");
    if (!bi && vjsPoster && vjsPoster.style && vjsPoster.style.backgroundImage) {
      bi = vjsPoster.style.backgroundImage.slice(4, -1).replace(/"/g, "");
    }
    if (!bi) {
      var nativeVideo = vidDiv.querySelector("video");
      if (nativeVideo) {
        bi = nativeVideo.getAttribute("poster") || "";
      }
    }
    if (!bi && vjsPoster) {
      var computed = getComputedStyle(vjsPoster).backgroundImage;
      if (computed && computed !== "none") {
        bi = computed.slice(4, -1).replace(/"/g, "");
      }
    }
    if (!bi && urL) {
      bi = derivePosterFromVideoUrl(urL);
    }
    return { urL, bi };
  }
  const ROME_CONFIG = {
    DEBUG: false,
    TIMEOUTS: {
      DOM_RETRY: 250,
      DOM_MAX_DELAY: 2e3,
      WATCHED_CHECK: 5e3,


FILTER_DEBOUNCE: 150,
      INITIAL_DELAY: 100,
      CHUNK_PROCESSING: 16,
STORAGE_RETRY_BASE: 1e3,
      STORAGE_MAX_RETRIES: 3
    },
    SELECTORS: {
      USER_NAME: "#user_name",
      ALBUMS_CONTAINER: "#albums",




INFY_APPEND_ROOT: "#page",
      DISCLAIMER: "#disclaimer",
      ALBUM_ITEMS: "#albums > div",
      ALBUM_TITLE: ".album-title",
      ALBUM_USER: ".album-user",
      THUMBNAIL_CONTAINER: ".album-thumbnail-container"
    },
    STORAGE_KEYS: {
      BANNED_ALBUMS: "bannedAlbums",
      WATCHED_VIDEOS: "watchedVideos",
      SESSION_STATE: "eromeSessionState",
      OVERLAY_POSITION: "infyOverlayPosition"
    },
    CACHE: {
      MAX_SIZE: 1e3,
      MAX_AGE: 3 * 60 * 1e3,
CHUNK_SIZE: 100,
      MEMORY_CHECK_INTERVAL: 30 * 1e3
    },
    FALLBACK_SELECTORS: [".username", ".user-info", ".profile-name", "body"],
    INFY_OVERLAY: {
      BACKGROUND: "rgba(0, 0, 0, 0.75)",
      TEXT_COLOR: "rgba(255, 255, 255, 0.9)",
      TOP_GAP: 8,
SIDE_GAP: 8,
DEFAULT_SIDE: "right",
PADDING: "clamp(4px, 0.4vw, 9px) clamp(8px, 0.7vw, 14px)",
      BORDER_RADIUS: "7px",
      FONT_SIZE: "clamp(14px, 1.2vw, 22px)",
      FONT_FAMILY: "monospace",
      WAIT_TIMEOUT: 15e3,
      CHILD_WAIT_TIMEOUT: 5e3
    }
  };
  function wrap(fn, context = "Rome") {
    return function(...args) {
      try {
        return fn.apply(this, args);
      } catch (error) {
        console.error(`${context} Error:`, error);
        return null;
      }
    };
  }
  function createLogger(config) {
    function log(context, message, data = {}) {
      if (config.DEBUG) {
        console.group(`Rome ${context}`);
        console.log(message, data);
        console.groupEnd();
      }
    }
    function measurePerformance(label, fn) {
      if (config.DEBUG) {
        performance.mark(`rome-${label}-start`);
      }
      const result = fn();
      if (config.DEBUG) {
        performance.mark(`rome-${label}-end`);
        performance.measure(`rome-${label}`, `rome-${label}-start`, `rome-${label}-end`);
        console.log(
          `Rome Performance: ${label}`,
          performance.getEntriesByName(`rome-${label}`)[0]
        );
      }
      return result;
    }
    function setDebug(enabled) {
      config.DEBUG = enabled;
      console.log(`Rome Debug Mode: ${enabled ? "Enabled" : "Disabled"}`);
    }
    return { log, performance: measurePerformance, setDebug };
  }
  function createStorage({ config, logger, getValue, setValue }) {
    async function setWithRetry(key, value, maxRetries = config.TIMEOUTS.STORAGE_MAX_RETRIES) {
      for (let i = 0; i < maxRetries; i++) {
        try {
          setValue(key, value);
          logger.log("Storage", `Successfully saved ${key}`, {
            attempt: i + 1
          });
          return true;
        } catch (error) {
          logger.log("Storage", `Failed to save ${key}`, {
            attempt: i + 1,
            error
          });
          if (i === maxRetries - 1) {
            console.error(
              `Rome: Failed to save ${key} after ${maxRetries} attempts:`,
              error
            );
            throw error;
          }
          const delay = config.TIMEOUTS.STORAGE_RETRY_BASE * Math.pow(2, i);
          await new Promise((resolve) => setTimeout(resolve, delay));
        }
      }
      throw new Error(`Rome: setWithRetry(${key}) made no attempts (maxRetries=${maxRetries})`);
    }
    function getWithFallback(key, defaultValue = null) {
      try {
        const value = getValue(key);
        logger.log("Storage", `Retrieved ${key}`, { value });
        return value !== void 0 ? value : defaultValue;
      } catch (error) {
        console.warn(`Rome: Failed to retrieve ${key}, using default:`, error);
        return defaultValue;
      }
    }
    return { setWithRetry, getWithFallback };
  }
  function checkMemoryUsage(logger) {
    const memory = performance.memory;
    if (memory) {
      const used = memory.usedJSHeapSize;
      const limit = memory.jsHeapSizeLimit;
      const usagePercent = used / limit * 100;
      logger.log("Memory", "Memory usage check", {
        used: `${Math.round(used / 1024 / 1024)}MB`,
        limit: `${Math.round(limit / 1024 / 1024)}MB`,
        percentage: `${usagePercent.toFixed(1)}%`
      });
      if (used > limit * 0.8) {
        console.warn(
          "Rome: High memory usage detected:",
          `${usagePercent.toFixed(1)}%`
        );
        return true;
      }
    }
    return false;
  }
  const ALBUM_PAGE = /^\/a\/[A-Za-z0-9]+$/;
  const EXPLORE_NEW = /^\/explore\/new/;
  function detectPage(location, document2, logger) {
    const pathname = location.pathname;
    const hasPageParam = new URLSearchParams(location.search).has("page");
    const hasPagination = document2.querySelector(".pagination") !== null;
    if (ALBUM_PAGE.test(pathname)) return "ALBUM";
    if (EXPLORE_NEW.test(pathname) && (hasPageParam || hasPagination)) return "EXPLORE";
    if (hasPagination && (pathname.includes("/explore/") || pathname.includes("/search") || pathname.includes("/user/"))) {
      logger.log("URLDetection", "Detected paginated page", {
        pathname,
        hasPagination
      });
      return "EXPLORE";
    }
    if (/^\/[^\/\s]+$/.test(pathname) && !pathname.match(/\.(html|php|aspx?)$/i)) {
      logger.log("URLDetection", "Detected user profile page", {
        pathname
      });
      return "EXPLORE";
    }
    return "OTHER";
  }
  class JabronioStoreFallback {
    constructor() {
      __publicField(this, "state");
      __publicField(this, "stateSubject");
      __publicField(this, "eventSubject");
      this.state = {
        filterExclude: false,
        filterExcludeWords: "",
        filterInclude: false,
        filterIncludeWords: "",
        showPhotos: true,
        filterPrivate: false,
        filterPublic: false
      };
      const subscribers = [];
      this.stateSubject = {
        subscribe(fn) {
          if (typeof fn === "function") {
            subscribers.push(fn);
            setTimeout(fn, 100);
          }
        },
        next(value) {
          subscribers.forEach((fn) => fn(value));
        }
      };
      this.eventSubject = { subscribe() {
      } };
      console.warn("Rome: Using JabronioStore fallback");
    }
    add(key, value) {
      if (!(key in this.state)) {
        this.state[key] = value;
      }
    }
  }
  function safeLibraryAccess() {
    try {
      const w = window;
      const jabronioutfit = w.jabronioutfit || {};
      return {
        JabronioStore: jabronioutfit.JabronioStore || JabronioStoreFallback
      };
    } catch (error) {
      console.error("Rome: Failed to access external libraries:", error);
      return null;
    }
  }
  function createVjsObserverCallback({
    countFn,
    ensureFn,
    getObserver
  }) {
    let noOpCount = 0;
    let errorCount = 0;
    let rafPending = false;
    const callback = () => {
      if (rafPending) return;
      rafPending = true;
      requestAnimationFrame(() => {
        var _a, _b;
        rafPending = false;
        if (!getObserver()) return;
        try {
          const before = countFn();
          ensureFn();
          const after = countFn();
          errorCount = 0;
          if (after === before) {
            noOpCount++;
            if (noOpCount >= 10) (_a = getObserver()) == null ? void 0 : _a.disconnect();
          } else {
            noOpCount = 0;
          }
        } catch (e) {
          errorCount++;
          console.warn(
            "[AlbumPageManager] vjs observer callback error (",
            errorCount,
            "):",
            e
          );
          if (errorCount >= 3) {
            console.warn(
              "[AlbumPageManager] Disconnecting vjsObserver after repeated errors"
            );
            (_b = getObserver()) == null ? void 0 : _b.disconnect();
          }
        }
      });
    };
    return {
      callback,
      getNoOpCount: () => noOpCount,
      isRafPending: () => rafPending
    };
  }
  class EromeUnifiedState {
    constructor(deps) {
      __publicField(this, "deps");
      __publicField(this, "persistentStore");
      __publicField(this, "sessionState");
      this.deps = deps;
      this.persistentStore = null;
      this.sessionState = this.loadSessionState();
      this.initializePersistentStore();
    }
    initializePersistentStore() {
      if (this.deps.currentPage !== "EXPLORE") return;
      try {
        this.persistentStore = new this.deps.libs.JabronioStore();
      } catch (error) {
        console.error("Rome: Failed to initialize JabronioStore:", error);
        this.persistentStore = null;
      }
    }
    loadSessionState() {
      const defaults = {
        showPhotosSession: true,
        showSBSSession: false,
        videoRotation: 0,
        hidePhotoOnlyAlbums: false
      };
      return defaults;
    }
    saveSessionState() {
      try {
        localStorage.setItem(
          this.deps.config.STORAGE_KEYS.SESSION_STATE,
          JSON.stringify(this.sessionState)
        );
      } catch (error) {
        console.warn("Rome: Failed to save session state:", error);
      }
    }
  }
  function createMultiVideoPlayback({
    config,
    logger,
    perf
  }) {
    function addProperID() {
      perf.mark("addProperID-start");
      let count = 0;
      const containerMapping = [];
      logger.log("MultiVideo", "addProperID - starting ID assignment");
      document.querySelectorAll(".media-group img.img-front, .media-group video").forEach((item) => {
        count++;
        const mediaGroup = item.closest(".media-group");
        const oldId = mediaGroup.id;
        mediaGroup.removeAttribute("id");
        mediaGroup.id = String(count);
        containerMapping.push(count);
        logger.log("MultiVideo", `Assigned ID ${count} to media group`, {
          oldId,
          mediaGroup
        });
      });
      logger.log("MultiVideo", "All media groups after ID assignment");
      document.querySelectorAll(".media-group").forEach((group, index) => {
        logger.log("MultiVideo", `Media group ${index + 1}`, { id: group.id, group });
      });
      window.romeContainerMapping = containerMapping;
      logger.log("MultiVideo", "Container mapping for videos", { containerMapping });
      perf.mark("addProperID-end");
      perf.measure("addProperID", "addProperID-start", "addProperID-end");
      return containerMapping;
    }
    function playerVersion(data, videoIndex = 1, targetContainer = null) {
      try {
        logger.log("Video", "playerVersion called", { data, videoIndex });
        const mediaDiv = document.createElement("div");
        const videoDiv = document.createElement("div");
        mediaDiv.classList.add("media-group");
        videoDiv.classList.add("video");
        const mainMedia = targetContainer || document.querySelector(".media-group");
        logger.log("Video", "Looking for main media container", { found: mainMedia });
        logger.log("Video", "Found main media element", { mainMedia });
        if (!mainMedia || !mainMedia.parentElement) {
          console.error("Rome: No main media element found for video placement");
          return;
        }
        mainMedia.parentElement.insertBefore(mediaDiv, mainMedia.nextSibling);
        mediaDiv.appendChild(videoDiv);
        logger.log("Video", "Created video container structure");
        logger.log("Video", "Container hierarchy", {
          mainMediaParent: mainMedia.parentElement.tagName,
          mediaDiv,
          videoDiv,
          mediaParentChildren: Array.from(mainMedia.parentElement.children).map(
            (c) => c.className
          )
        });
        const video = document.createElement("video");
        video.controls = true;
        video.preload = "metadata";
        video.classList.add("rome-video");
        logger.log("Video", "Created video element with controls");
        const preloadObs = getRomePreloadObserver();
        if (preloadObs) preloadObs.observe(video);
        const playbackObs = getRomePlaybackObserver();
        if (playbackObs) playbackObs.observe(video);
        if (data.bi) {
          video.poster = data.bi;
          logger.log("Video", "Set video poster", { poster: data.bi });
        }
        const src = document.createElement("source");
        src.setAttribute("src", data.urL);
        src.setAttribute("type", "video/mp4");
        video.appendChild(src);
        videoDiv.appendChild(video);
        logger.log("Video", "Added source and appended video to DOM");
        video.addEventListener("loadedmetadata", () => {
          const aspectRatio = video.videoWidth / video.videoHeight;
          const isHorizontal = aspectRatio >= 1;
          if (isHorizontal) {
            video.style.cssText += "width: 100% !important; max-width: 800px !important; height: auto !important; display: block !important; position: relative !important; object-fit: contain !important; margin: 0 auto !important; background-color: #000000 !important;";
          } else {
            video.style.cssText += "width: 100% !important; max-width: 600px !important; max-height: 500px !important; height: auto !important; display: block !important; position: relative !important; object-fit: contain !important; margin: 0 auto !important; background-color: #000000 !important;";
          }
          logger.log(
            "Video",
            `Applied ${isHorizontal ? "horizontal" : "vertical"} video sizing`,
            { aspectRatio: aspectRatio.toFixed(2) }
          );
        });
        video.style.cssText += "width: 100% !important; max-width: 800px !important; max-height: 500px !important; height: auto !important; display: block !important; position: relative !important; object-fit: contain !important; margin: 0 auto !important; background-color: #000000 !important;";
      } catch (error) {
        console.error("Rome: Error creating player version:", error);
      }
    }
    function removeWithData(vidDiv) {
      const data = getVidData(vidDiv);
      if (data.urL) {
        logger.log("MultiVideo", "Removing original video div", { vidDiv });
        vidDiv.remove();
        return data;
      } else {
        console.warn("Rome: Failed to extract video data, skipping removal:", vidDiv);
        return null;
      }
    }
    function videoCleanseReplace(_vidDivs) {
      perf.mark("videoCleanseReplace-start");
      const vidDivs = document.getElementsByClassName("video");
      logger.log("MultiVideo", "Started video cleansing for multi-playback");
      addProperID();
      logger.log("MultiVideo", "Assigned proper IDs to media groups");
      Array.from(vidDivs).forEach((vidDiv, index) => {
        logger.log("MultiVideo", `Processing video ${index + 1}`, { vidDiv });
        const parentMediaGroup = vidDiv.closest(".media-group");
        const data = removeWithData(vidDiv);
        logger.log("MultiVideo", `Video ${index + 1} data`, { data });
        if (data && data.urL) {
          logger.log("MultiVideo", `Creating player for video ${index + 1}`, {
            url: data.urL
          });
          playerVersion(data, index + 1, parentMediaGroup);
          logger.log("MultiVideo", `Player created for video ${index + 1}`);
        } else {
          console.warn(`Rome: Skipping video ${index + 1} - no valid URL found`);
        }
      });
      logger.log("MultiVideo", "Cleaning up original video containers");
      Array.from(document.querySelectorAll(".video-lg")).forEach((lgVid) => {
        logger.log("MultiVideo", "Removing large video container", { lgVid });
        lgVid.parentElement.remove();
      });
      Array.from(document.querySelectorAll(".media-group video:not(.rome-video)")).forEach(
        (video, index) => {
          logger.log("MultiVideo", `Removing original video ${index + 1}`, { video });
          video.remove();
        }
      );
      Array.from(document.querySelectorAll(".media-group .video:empty")).forEach(
        (emptyVideo) => {
          logger.log("MultiVideo", "Removing empty video container", { emptyVideo });
          emptyVideo.remove();
        }
      );
      const messageDiv = document.querySelector("#user_message");
      if (messageDiv && !messageDiv.textContent.trim()) {
        messageDiv.textContent = "Multi-video playback enabled";
        messageDiv.style.display = "block";
        setTimeout(() => {
          if (messageDiv.textContent === "Multi-video playback enabled") {
            messageDiv.style.display = "none";
            messageDiv.textContent = "";
          }
        }, 1e3);
      }
      perf.mark("videoCleanseReplace-end");
      perf.measure(
        "videoCleanseReplace",
        "videoCleanseReplace-start",
        "videoCleanseReplace-end"
      );
      perf.mark("bootstrap-end");
      perf.measure("bootstrap", "script-start", "bootstrap-end");
      logger.log("MultiVideo", "Multi-video setup cleanup completed");
      if (config.DEBUG) {
        setTimeout(() => {
          logger.log("MultiVideo", "Checking video health 2 seconds after setup");
          const videos = document.querySelectorAll("video");
          videos.forEach((video, index) => {
            logger.log("MultiVideo", `Video ${index + 1} health check`, {
              src: video.currentSrc,
              readyState: video.readyState,
              networkState: video.networkState,
              paused: video.paused,
              ended: video.ended,
              error: video.error,
              canPlay: !video.error && video.readyState >= 3
            });
          });
        }, 2e3);
      }
    }
    return { videoCleanseReplace };
  }
  class HybridContentFilter {
    constructor(jabroniStore, deps) {
      __publicField(this, "deps");
      __publicField(this, "jabroniStore");
      __publicField(this, "regexPatterns");
      __publicField(this, "censoredIMG");
      __publicField(this, "filterTimeout");
      __publicField(this, "albumCache");
      __publicField(this, "cacheTimestamps");
      __publicField(this, "lastAlbumCount");
      __publicField(this, "isProcessing");
      __publicField(this, "processingQueue");
      __publicField(this, "unicodeToAsciiMap");
      __publicField(this, "_patternVersion");
      __publicField(this, "_memoryMonitorId", null);
      __publicField(this, "_albumMutationObserver", null);
      __publicField(this, "_photoToggle");
      __publicField(this, "_compiledPatterns");
      __publicField(this, "_lastPatternUpdate");
      this.deps = deps;
      this.jabroniStore = jabroniStore;
      this.regexPatterns = this.deps.storage.getWithFallback(
        this.deps.config.STORAGE_KEYS.BANNED_ALBUMS,
        []
      );
      this.censoredIMG = "https://www.onlygfx.com/wp-content/uploads/2018/04/censored-stamp-2.png";
      this.filterTimeout = null;
      this.albumCache = new Map();
      this.cacheTimestamps = new Map();
      this.lastAlbumCount = 0;
      this.isProcessing = false;
      this.processingQueue = [];
      this._patternVersion = 0;
      this.unicodeToAsciiMap = this.buildUnicodeMap();
      this.startMemoryMonitoring();
      window.addEventListener("beforeunload", () => {
        this.stopMemoryMonitoring();
      });
    }
buildUnicodeMap() {
      const map = new Map();
      const asciiLower = "abcdefghijklmnopqrstuvwxyz";
      const asciiUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
      const digits = "0123456789";
      const ranges = [
{ upper: 119808, lower: 119834 },
{ upper: 119860, lower: 119886 },
{ upper: 119912, lower: 119938 },
{ upper: 119964, lower: 119990 },
{ upper: 120016, lower: 120042 },
{ upper: 120068, lower: 120094 },
{ upper: 120120, lower: 120146 },
{ upper: 120172, lower: 120198 },
{ upper: 120224, lower: 120250 },
{ upper: 120276, lower: 120302 },
{ upper: 120328, lower: 120354 },
{ upper: 120380, lower: 120406 },
{ upper: 120432, lower: 120458 }
      ];
      ranges.forEach(({ upper, lower }) => {
        for (let i = 0; i < 26; i++) {
          map.set(String.fromCodePoint(upper + i), asciiUpper[i]);
          map.set(String.fromCodePoint(lower + i), asciiLower[i]);
        }
      });
      for (let i = 0; i < 26; i++) {
        map.set(String.fromCodePoint(65313 + i), asciiUpper[i]);
        map.set(String.fromCodePoint(65345 + i), asciiLower[i]);
      }
      for (let i = 0; i < 10; i++) {
        map.set(String.fromCodePoint(65296 + i), digits[i]);
      }
      for (let i = 0; i < 26; i++) {
        map.set(String.fromCodePoint(9398 + i), asciiUpper[i]);
        map.set(String.fromCodePoint(9424 + i), asciiLower[i]);
      }
      map.set("𝑕", "h");
      return map;
    }
normalizeUnicode(text) {
      let result = "";
      for (const char of text) {
        result += this.unicodeToAsciiMap.get(char) || char;
      }
      return result;
    }
    startMemoryMonitoring() {
      this._memoryMonitorId = setInterval(() => {
        if (this.deps.checkMemoryUsage(this.deps.logger)) {
          this.clearCache();
        }
        this.cleanExpiredCache();
      }, this.deps.config.CACHE.MEMORY_CHECK_INTERVAL);
    }
    stopMemoryMonitoring() {
      if (this._memoryMonitorId) {
        clearInterval(this._memoryMonitorId);
        this._memoryMonitorId = null;
      }
    }
    cleanup() {
      this.stopMemoryMonitoring();
      if (this._albumMutationObserver) {
        this._albumMutationObserver.disconnect();
        this._albumMutationObserver = null;
      }
      this.deps.logger.log("HybridContentFilter", "Cleaned up");
    }
    clearCache() {
      this.albumCache.clear();
      this.cacheTimestamps.clear();
      console.log("Rome: Cache cleared due to memory pressure");
    }
    cleanExpiredCache() {
      const now = Date.now();
      for (const [key, timestamp] of this.cacheTimestamps) {
        if (now - timestamp > this.deps.config.CACHE.MAX_AGE) {
          this.albumCache.delete(key);
          this.cacheTimestamps.delete(key);
        }
      }
    }
    initialize() {
      if (!this.jabroniStore) {
        console.error("Rome: JabroniOutfit not available, using regex filtering only");
        this.addRegexUI();
        this.applyAllFilters();
        this.setupAlbumMutationObserver();
        return;
      }
      try {
        this.jabroniStore.stateSubject.subscribe(() => this.debouncedApplyFilters());
      } catch (error) {
        console.error("Rome: JabroniOutfit state subscription failed:", error);
        this.jabroniStore = null;
      }
      this.addRegexUI();
      this.applyAllFilters();
      this.setupAlbumMutationObserver();
    }




setupAlbumMutationObserver() {
      if (this._albumMutationObserver) {
        this._albumMutationObserver.disconnect();
        this._albumMutationObserver = null;
      }
      const target = document.querySelector(this.deps.config.SELECTORS.INFY_APPEND_ROOT) || document.querySelector(this.deps.config.SELECTORS.ALBUMS_CONTAINER);
      if (!target) {
        console.warn("[HybridContentFilter] Append root missing at init -- mutation observer not attached");
        return;
      }
      this._albumMutationObserver = this.deps.createAlbumMutationObserver({
        target,
        itemSelector: this.deps.config.SELECTORS.ALBUM_ITEMS,
        onItemsAdded: this.deps.wrap(
          () => this.debouncedApplyFilters(),
          "HybridContentFilter.onItemsAdded"
        )
      });
    }
    debouncedApplyFilters() {
      clearTimeout(this.filterTimeout ?? void 0);
      this.filterTimeout = setTimeout(() => {
        this.applyAllFiltersInChunks();
      }, this.deps.config.TIMEOUTS.FILTER_DEBOUNCE);
    }
    setPhotoToggle(photoToggle) {
      this._photoToggle = photoToggle;
    }
    addRegexUI() {
      this.createSettingsModal();
      this.createNavbarButton();
      this.createQuickAddInput();
    }
    createSettingsModal() {
      const modalHTML = `
      <div id="romeModal" class="rome-modal" style="display: none;">
        <div class="rome-modal-content">
          <div class="rome-modal-header">
            <span class="rome-close-modal">&times;</span>
            <span class="rome-modal-title">Rome Settings - Content Filtering</span>
          </div>
          <div class="rome-modal-body">
            <div class="rome-settings-section">
              <h4>Debug Mode</h4>
              <label style="display: flex; align-items: center; gap: 10px; cursor: pointer; color: #ccc; font-size: 14px;">
                <input type="checkbox" id="romeDebugToggle" style="width: 18px; height: 18px; accent-color: #eb6395; cursor: pointer;">
                Enable debug logging to console
              </label>
            </div>
            <div class="rome-settings-section">
              <h4>Custom Blocklist (Regex Support)</h4>
              <p style="color: #ccc; font-size: 12px; margin-bottom: 10px;">
                Add words or patterns to block. Supports wildcards (*) and regex patterns.
              </p>
              <div class="blocklist-display" id="regexBlocklistDisplay"></div>
              <div class="add-word-container">
                <input type="text" id="regexBlockWord" placeholder="Add word/pattern to block...">
                <button id="addRegexBlock">Add</button>
              </div>
            </div>
          </div>
        </div>
      </div>
    `;
      document.body.insertAdjacentHTML("beforeend", modalHTML);
      this.setupModalEvents();
      this.setupRegexEvents();
      this.updateRegexDisplay();
    }
    createNavbarButton() {
      const scriptMenuBtn = document.createElement("div");
      scriptMenuBtn.classList.add("sp", "no-select", "rome-menu-btn");
      scriptMenuBtn.setAttribute("data-type", "RomeMenu");
      scriptMenuBtn.title = "Rome Settings";
      scriptMenuBtn.innerHTML = `⚙️ Rome`;
      scriptMenuBtn.style.cssText = `
      cursor: pointer;
      background: rgba(235, 99, 149, 0.8);
      color: white;
      border-radius: 4px;
      font-weight: bold;
      margin: 0 5px;
      user-select: none;
      transition: background 0.3s;
    `;
      const xChild = document.querySelector(".container > div > div:nth-child(6)");
      if (xChild) xChild.parentNode.insertBefore(scriptMenuBtn, xChild);
      scriptMenuBtn.addEventListener("click", () => {
        const modal = document.getElementById("romeModal");
        if (modal) {
          modal.style.display = "block";
          this.updateRegexDisplay();
          const debugToggle = document.getElementById("romeDebugToggle");
          if (debugToggle) debugToggle.checked = this.deps.config.DEBUG;
        }
      });
      scriptMenuBtn.addEventListener("mouseenter", () => {
        scriptMenuBtn.style.background = "rgba(235, 99, 149, 1)";
      });
      scriptMenuBtn.addEventListener("mouseleave", () => {
        scriptMenuBtn.style.background = "rgba(235, 99, 149, 0.8)";
      });
    }
    createQuickAddInput() {
      const quickAddHTML = `
      <div id="romeQuickAdd" class="rome-quick-add">
        <h3>Quick Add Blocklist</h3>
        <textarea id="quickAddTextarea" placeholder="Enter words to block (one per line)..."></textarea>
        <div class="instructions">
          <kbd>Q</kbd> to open • <kbd>Cmd+Enter</kbd> to add all • <kbd>Escape</kbd> to cancel
        </div>
      </div>
    `;
      document.body.insertAdjacentHTML("beforeend", quickAddHTML);
      this.setupQuickAddEvents();
    }
    setupQuickAddEvents() {
      const quickAdd = document.getElementById("romeQuickAdd");
      const textarea = document.getElementById("quickAddTextarea");
      if (!quickAdd || !textarea) return;
      document.addEventListener("keydown", (e) => {
        const t = e.target;
        if (t.tagName === "INPUT" || t.tagName === "TEXTAREA") return;
        const modal = document.getElementById("romeModal");
        if (modal && modal.style.display === "block") return;
        if (e.key === "q" || e.key === "Q") {
          e.preventDefault();
          this.showQuickAdd();
        }
      });
      textarea.addEventListener("keydown", (e) => {
        if (e.key === "Enter" && e.metaKey) {
          e.preventDefault();
          this.processQuickAddWords();
        } else if (e.key === "Escape") {
          e.preventDefault();
          this.hideQuickAdd();
        }
      });
      quickAdd.addEventListener("click", (e) => {
        if (e.target === quickAdd) {
          this.hideQuickAdd();
        }
      });
    }
    showQuickAdd() {
      const quickAdd = document.getElementById("romeQuickAdd");
      const textarea = document.getElementById("quickAddTextarea");
      if (quickAdd && textarea) {
        quickAdd.style.display = "block";
        textarea.focus();
        textarea.value = "";
      }
    }
    hideQuickAdd() {
      const quickAdd = document.getElementById("romeQuickAdd");
      if (quickAdd) {
        quickAdd.style.display = "none";
      }
    }
    processQuickAddWords() {
      const textarea = document.getElementById("quickAddTextarea");
      if (!textarea) return;
      const words = textarea.value.split("\n").map((word) => word.trim().toLowerCase()).filter((word) => word && !this.regexPatterns.includes(word));
      if (words.length === 0) {
        this.hideQuickAdd();
        return;
      }
      this.regexPatterns.push(...words);
      this._patternVersion++;
      this.deps.storage.setWithRetry(
        this.deps.config.STORAGE_KEYS.BANNED_ALBUMS,
        this.regexPatterns
      ).catch((err) => {
        console.error("Rome: storage write failed", err);
        this.deps.notifyStorageFailure("filter patterns");
      });
      this.updateRegexDisplay();
      this.debouncedApplyFilters();
      this.showQuickAddFeedback(
        `Added ${words.length} word${words.length === 1 ? "" : "s"} to blocklist`
      );
      this.hideQuickAdd();
    }
    showQuickAddFeedback(message) {
      const feedback = document.createElement("div");
      feedback.textContent = message;
      feedback.style.cssText = `
      position: fixed;
      top: 20px;
      right: 20px;
      background: linear-gradient(135deg, #26de81 0%, #20bf6b 100%);
      color: white;
      padding: 12px 20px;
      border-radius: 8px;
      font-weight: bold;
      z-index: 100000;
      box-shadow: 0 4px 12px rgba(38, 222, 129, 0.3);
      animation: slideIn 0.3s ease-out;
    `;
      document.body.appendChild(feedback);
      setTimeout(() => {
        if (feedback.parentNode) {
          feedback.parentNode.removeChild(feedback);
        }
      }, 3e3);
    }
    setupModalEvents() {
      const modal = document.getElementById("romeModal");
      const closeBtn = document.querySelector(".rome-close-modal");
      if (closeBtn) {
        closeBtn.addEventListener("click", () => {
          modal.style.display = "none";
        });
      }
      window.addEventListener("click", (event) => {
        if (event.target === modal) {
          modal.style.display = "none";
        }
      });
      document.addEventListener("keydown", (e) => {
        if (e.key === "Escape" && modal.style.display === "block") {
          modal.style.display = "none";
        }
      });
      const debugToggle = document.getElementById("romeDebugToggle");
      if (debugToggle) {
        debugToggle.addEventListener("change", (e) => {
          this.deps.logger.setDebug(e.target.checked);
        });
      }
    }
    setupRegexEvents() {
      const addButton = document.getElementById("addRegexBlock");
      const input = document.getElementById("regexBlockWord");
      if (addButton && input) {
        addButton.addEventListener("click", () => {
          const word = input.value.trim().toLowerCase();
          if (word && !this.regexPatterns.includes(word)) {
            this.regexPatterns.push(word);
            this._patternVersion++;
            this.deps.storage.setWithRetry(
              this.deps.config.STORAGE_KEYS.BANNED_ALBUMS,
              this.regexPatterns
            ).catch((err) => {
              console.error("Rome: storage write failed", err);
              this.deps.notifyStorageFailure("filter patterns");
            });
            this.updateRegexDisplay();
            this.debouncedApplyFilters();
            input.value = "";
          }
        });
        input.addEventListener("keypress", (e) => {
          if (e.key === "Enter") {
            addButton.click();
          }
        });
      }
    }
    updateRegexDisplay() {
      const display = document.getElementById("regexBlocklistDisplay");
      if (!display) return;
      display.innerHTML = "";
      if (this.regexPatterns.length === 0) {
        const emptyMsg = document.createElement("div");
        emptyMsg.style.cssText = "color: #999; font-style: italic;";
        emptyMsg.textContent = "No blocked words yet";
        display.appendChild(emptyMsg);
        return;
      }
      this.regexPatterns.forEach((pattern, index) => {
        const item = document.createElement("div");
        item.className = "blocklist-item";
        const span = document.createElement("span");
        span.textContent = pattern;
        const button = document.createElement("button");
        button.textContent = "Remove";
        button.addEventListener("click", () => this.removePattern(index));
        item.appendChild(span);
        item.appendChild(button);
        display.appendChild(item);
      });
    }
    removePattern(index) {
      this.regexPatterns.splice(index, 1);
      this._patternVersion++;
      this.deps.storage.setWithRetry(
        this.deps.config.STORAGE_KEYS.BANNED_ALBUMS,
        this.regexPatterns
      ).catch((err) => {
        console.error("Rome: storage write failed", err);
        this.deps.notifyStorageFailure("filter patterns");
      });
      this.updateRegexDisplay();
      this.debouncedApplyFilters();
    }
    applyAllFilters() {
      setTimeout(() => this.applyRegexFiltering(), 100);
    }
    applyAllFiltersInChunks() {
      const albums = this.getAlbums();
      this.processAlbumsInChunks(albums);
    }
    processAlbumsInChunks(albums) {
      if (this.isProcessing) {
        this.processingQueue = albums;
        return;
      }
      this.isProcessing = true;
      let index = 0;
      const processChunk = () => {
        const chunk = albums.slice(index, index + this.deps.config.CACHE.CHUNK_SIZE);
        chunk.forEach((albumData) => {
          this.applyCensoringToAlbum(albumData);
        });
        index += this.deps.config.CACHE.CHUNK_SIZE;
        if (index < albums.length) {
          requestAnimationFrame(processChunk);
        } else {
          this.isProcessing = false;
          if (this._photoToggle) this._photoToggle.applyPhotoOnlyFilter();
          if (this.processingQueue.length > 0) {
            const queuedAlbums = this.processingQueue;
            this.processingQueue = [];
            this.processAlbumsInChunks(queuedAlbums);
          }
        }
      };
      processChunk();
    }
    applyCensoringToAlbum(albumData) {
      const { albumTitle, albumUser, albumUrl, element } = albumData;
      const normalizedTitle = this.normalizeUnicode(albumTitle);
      const normalizedUser = this.normalizeUnicode(albumUser);
      const normalizedUrl = this.normalizeUnicode(albumUrl || "");
      const patterns = this.getCompiledPatterns();
      const isBlocked = patterns.some(
        (re) => (
re.test(albumTitle) || re.test(albumUser) || re.test(normalizedTitle) || re.test(normalizedUser) ||
re.test(albumUrl) || re.test(normalizedUrl) || normalizedTitle.split(/[ _.:;?!~,`"&|()<>{}\[\]\r\n/\\]+/g).some((word) => re.test(word)) || normalizedUser.split(/[ _.:;?!~,`"&|()<>{}\[\]\r\n/\\]+/g).some((word) => re.test(word)) ||
normalizedUrl.split(/[/_\-?&=]+/g).some((word) => re.test(word))
        )
      );
      if (isBlocked) {
        this.censorAlbum(element || albumTitle);
      }
    }
    getCompiledPatterns() {
      if (!this._compiledPatterns || this._lastPatternUpdate !== this._patternVersion) {
        this._compiledPatterns = this.regexPatterns.map((pattern) => {
          try {
            let pat = pattern.trim();
            if (pat.length > 200) {
              console.warn(`Rome: Skipping blocklist pattern exceeding 200 chars`);
              return /(?!.*)/;
            }
            if (pat.includes("*")) {
              pat = pat.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, "[^\\s]*");
              if (pat.includes(" ")) {
                return new RegExp(pat, "i");
              } else {
                return new RegExp("\\b" + pat, "i");
              }
            } else if (pat.includes(" ")) {
              const escaped = pat.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
              return new RegExp(escaped, "i");
            } else {
              const escaped = pat.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
              return new RegExp("\\b" + escaped + "\\b", "i");
            }
          } catch (error) {
            console.error(`Rome: Failed to compile pattern "${pattern}":`, error);
            return /(?!.*)/;
          }
        });
        this._lastPatternUpdate = this._patternVersion;
      }
      return this._compiledPatterns;
    }
    applyRegexFiltering() {
      this.applyAllFiltersInChunks();
    }
    getAlbums() {
      const currentCount = document.querySelectorAll(
        this.deps.config.SELECTORS.ALBUM_ITEMS
      ).length;
      if (currentCount === this.lastAlbumCount && this.albumCache.size > 0) {
        return Array.from(this.albumCache.values());
      }
      this.albumCache.clear();
      this.cacheTimestamps.clear();
      this.lastAlbumCount = currentCount;
      const now = Date.now();
      const albums = Array.from(
        document.querySelectorAll(this.deps.config.SELECTORS.ALBUM_ITEMS)
      ).map((albumEl) => {
        const titleEl = albumEl.querySelector(
          `div > ${this.deps.config.SELECTORS.ALBUM_TITLE}`
        );
        const userEl = albumEl.querySelector(
          `div > ${this.deps.config.SELECTORS.ALBUM_USER}`
        );
        const linkEl = albumEl.querySelector("a[href]");
        let albumUrl = "";
        if (linkEl) {
          try {
            albumUrl = decodeURIComponent(linkEl.href);
          } catch (e) {
            albumUrl = linkEl.href;
          }
        }
        return {
          albumTitle: (titleEl == null ? void 0 : titleEl.innerText) || "",
          albumUser: (userEl == null ? void 0 : userEl.innerText) || "",
          albumUrl,
          element: albumEl
        };
      }).filter((item) => item.albumTitle !== "");
      albums.forEach((album, index) => {
        this.albumCache.set(index, album);
        this.cacheTimestamps.set(index, now);
      });
      return albums;
    }
    censorAlbum(albumOrTitle) {
      const applyOverlay = (album) => {
        const container = album.querySelector(this.deps.config.SELECTORS.THUMBNAIL_CONTAINER);
        if (container) {
          const overlay = document.createElement("a");
          overlay.className = "album-link";
          overlay.style.cssText = `
            position: absolute; content: '';
            background: URL('${this.censoredIMG}') center no-repeat;
            background-size: contain; resize: both; pointer-events: none;
            width: 100%; height: 100%;
          `;
          container.innerHTML = "";
          container.appendChild(overlay);
        }
      };
      if (albumOrTitle instanceof HTMLElement) {
        applyOverlay(albumOrTitle);
      } else {
        document.querySelectorAll(this.deps.config.SELECTORS.ALBUM_ITEMS).forEach((album) => {
          const titleElement = album.querySelector(
            `div > ${this.deps.config.SELECTORS.ALBUM_TITLE}`
          );
          if (titleElement && titleElement.innerText === albumOrTitle) {
            applyOverlay(album);
          }
        });
      }
    }
  }
  class PhotoOnlyAlbumToggle {
    constructor(unifiedState, deps) {
      __publicField(this, "deps");
      __publicField(this, "state");
      __publicField(this, "button");
      this.deps = deps;
      this.state = unifiedState;
      this.initialize();
    }
    initialize() {
      if (this.deps.currentPage !== "EXPLORE") return;
      this.createToggleButton();
      this.applyPhotoOnlyFilter();
      this.updateButtonText();
      this.removeUnwantedButtons();
    }
    createToggleButton() {
      const toggleBtn = document.createElement("div");
      toggleBtn.classList.add("sp", "no-select", "rome-menu-btn");
      toggleBtn.setAttribute("data-type", "PhotoToggle");
      toggleBtn.title = "Toggle Photo-Only Albums";
      toggleBtn.innerHTML = `📸 Photos`;
      toggleBtn.style.cssText = `
      cursor: pointer;
      background: rgba(235, 99, 149, 0.8);
      color: white;
      border-radius: 4px;
      font-weight: bold;
      margin: 0 5px;
      user-select: none;
      transition: background 0.3s;
    `;
      const romeBtn = document.querySelector('.rome-menu-btn[data-type="RomeMenu"]');
      if (romeBtn && romeBtn.parentNode) {
        romeBtn.parentNode.insertBefore(toggleBtn, romeBtn.nextSibling);
      } else {
        const navChild = document.querySelector(".container > div > div:nth-child(6)");
        if (navChild && navChild.parentNode) {
          navChild.parentNode.insertBefore(toggleBtn, navChild);
        }
      }
      toggleBtn.addEventListener("click", () => {
        this.togglePhotoOnlyAlbums();
      });
      toggleBtn.addEventListener("mouseenter", () => {
        toggleBtn.style.background = "rgba(235, 99, 149, 1)";
      });
      toggleBtn.addEventListener("mouseleave", () => {
        toggleBtn.style.background = "rgba(235, 99, 149, 0.8)";
      });
      document.addEventListener("keydown", (e) => {
        const t = e.target;
        if (t.tagName === "INPUT" || t.tagName === "TEXTAREA") return;
        const modal = document.getElementById("romeModal");
        if (modal && modal.style.display === "block") return;
        if (e.key === "a" || e.key === "A") {
          if (this.deps.currentPage === "EXPLORE") {
            e.preventDefault();
            this.togglePhotoOnlyAlbums();
          }
        }
      });
      this.button = toggleBtn;
    }
    togglePhotoOnlyAlbums() {
      this.state.sessionState.hidePhotoOnlyAlbums = !this.state.sessionState.hidePhotoOnlyAlbums;
      this.applyPhotoOnlyFilter();
      this.state.saveSessionState();
      this.updateButtonText();
    }
    updateButtonText() {
      if (this.button) {
        const isHidden = this.state.sessionState.hidePhotoOnlyAlbums;
        this.button.innerHTML = isHidden ? `📸 Show Photos` : `📸 Hide Photos`;
        this.button.style.background = isHidden ? "rgba(160, 159, 157, 0.8)" : "rgba(235, 99, 149, 0.8)";
      }
    }
    applyPhotoOnlyFilter() {
      const hidePhotos = this.state.sessionState.hidePhotoOnlyAlbums;
      const albums = document.querySelectorAll(
        this.deps.config.SELECTORS.ALBUM_ITEMS
      );
      albums.forEach((album) => {
        const hasVideos = album.querySelector(".album-videos") !== null;
        if (hidePhotos && !hasVideos) {
          album.style.display = "none";
        } else {
          album.style.display = "";
        }
      });
      this.deps.logger.log("PhotoToggle", `Applied photo filter: hidePhotos=${hidePhotos}`, {
        totalAlbums: albums.length,
        hiddenAlbums: hidePhotos ? Array.from(albums).filter((a) => a.style.display === "none").length : 0
      });
    }
    removeUnwantedButtons() {
      const removeButtons = () => {
        let removed = 0;
        document.querySelectorAll("BUTTON.btn.btn-grey.album-repost").forEach((btn) => {
          btn.remove();
          removed++;
          this.deps.logger.log("PhotoToggle", "Removed album-repost button");
        });
        document.querySelectorAll("BUTTON#album-flag").forEach((btn) => {
          btn.remove();
          removed++;
          this.deps.logger.log("PhotoToggle", "Removed album-flag button");
        });
        return removed;
      };
      removeButtons();
      let noOpCount = 0;
      const observer = new MutationObserver(() => {
        const removed = removeButtons();
        if (removed === 0) {
          noOpCount++;
          if (noOpCount >= 10) observer.disconnect();
        } else {
          noOpCount = 0;
        }
      });
      const albumsContainer = document.querySelector(
        this.deps.config.SELECTORS.ALBUMS_CONTAINER
      );
      if (albumsContainer) {
        observer.observe(albumsContainer, { childList: true, subtree: true });
      }
    }
  }
  class AlbumPageManager {
    constructor(unifiedState, deps) {
      __publicField(this, "deps");
      __publicField(this, "state");
      __publicField(this, "_vjsObserver", null);
      this.deps = deps;
      this.state = unifiedState;
      this.initialize();
    }
    initialize() {
      const vidDivs = document.getElementsByClassName("video");
      if (vidDivs.length > 1) {
        this.deps.logger.log("MultiVideo", "Enabling multi-video", { count: vidDivs.length });
        let setupOk = false;
        try {
          this.deps.multiVideo.videoCleanseReplace(vidDivs);
          setupOk = true;
        } catch (error) {
          console.error("Rome: Multi-video setup failed:", error);
        }
        document.dispatchEvent(
          new CustomEvent("rome:videos-ready", { detail: { success: setupOk } })
        );
      }
      const initWrappers = () => {
        this.addSessionButtons();
        this.ensureVideoWrappers();
        this.ensureVjsCustomControls();
        this.applySessionPhotoToggle();
        this.applySessionSBSToggle();
        this.applyVideoRotation();
        const vjsObserveTarget = document.querySelector(".album-videos") || document.body;
        this._vjsObserver = new MutationObserver(
          this.deps.createVjsObserverCallback({
            countFn: () => document.querySelectorAll(".rome-vjs-controls").length,
            ensureFn: () => {
              this.ensureVjsCustomControls();
              this.applySessionPhotoToggle();
            },
            getObserver: () => this._vjsObserver
          }).callback
        );
        this._vjsObserver.observe(vjsObserveTarget, { childList: true, subtree: true });
      };
      if (vidDivs.length > 1) {
        initWrappers();
      } else {
        setTimeout(initWrappers, this.deps.config.TIMEOUTS.INITIAL_DELAY);
      }
    }
    addSessionButtons() {
      const userNameParent = $("#user_name").parent();
      if (userNameParent.length === 0) {
        console.warn("Rome: Could not find #user_name parent for buttons");
        return;
      }
      userNameParent.append(
        '<button id="togglePhotos" class="btn btn-pink">show photos</button>'
      );
      userNameParent.append(document.createTextNode("                                 "));
      userNameParent.append('<button id="sbsBtn" class="btn btn-pink">SBS: Off</button>');
      userNameParent.append(document.createTextNode("                                 "));
      userNameParent.append(
        '<button id="rotateBtn" class="btn btn-pink">Rotate: 0°</button>'
      );
      this.setupEventHandlers();
      this.setupKeyboardShortcuts();
      this.applySessionPhotoToggle();
      this.applySessionSBSToggle();
    }
    togglePhotos() {
      this.state.sessionState.showPhotosSession = !this.state.sessionState.showPhotosSession;
      this.applySessionPhotoToggle();
      this.applySessionSBSToggle();
      this.state.saveSessionState();
    }
    toggleSBS() {
      this.state.sessionState.showSBSSession = !this.state.sessionState.showSBSSession;
      this.applySessionSBSToggle();
      this.applySessionPhotoToggle();
    }
    setupEventHandlers() {
      $("#togglePhotos").on("click", () => this.togglePhotos());
      $("#sbsBtn").on("click", () => this.toggleSBS());
      $("#rotateBtn").on("click", () => this.rotateVideos());
    }
    setupKeyboardShortcuts() {
      document.addEventListener("keydown", (e) => {
        const t = e.target;
        if (t.tagName === "INPUT" || t.tagName === "TEXTAREA") return;
        if (e.metaKey || e.ctrlKey || e.altKey) return;
        if (e.key === "a") {
          this.togglePhotos();
          e.preventDefault();
        } else if (e.key === "s") {
          this.toggleSBS();
          e.preventDefault();
        } else if (e.key === "r") {
          this.rotateVideos();
          e.preventDefault();
        }
      });
    }
    _isPhotoOnlyGroup(group) {
      return !group.querySelector(
        "video, .video, .video-js, .rome-video, .rome-video-wrapper"
      );
    }
    applySessionPhotoToggle() {
      const show = this.state.sessionState.showPhotosSession;
      document.querySelectorAll(".media-group").forEach((group) => {
        if (this._isPhotoOnlyGroup(group)) {
          group.style.display = show ? "" : "none";
          group.dataset.romePhotoHidden = show ? "" : "1";
          if (!show) {
            group.classList.remove("col-sm-6");
          } else if (this.state.sessionState.showSBSSession) {
            group.classList.add("col-sm-6");
          }
        }
      });
      $("#togglePhotos").css("backgroundColor", show ? "#eb6395" : "#a09f9d").text(show ? "hide photos" : "show photos");
    }
    applySessionSBSToggle() {
      const show = this.state.sessionState.showSBSSession;
      document.querySelectorAll(".media-group").forEach((group) => {
        if (group.dataset.romePhotoHidden === "1") return;
        if (show) {
          group.classList.add("col-sm-6");
        } else {
          group.classList.remove("col-sm-6");
        }
      });
      $("#sbsBtn").css("backgroundColor", show ? "#a09f9d" : "#eb6395").text(show ? "SBS: On" : "SBS: Off");
    }
    rotateVideos() {
      this.state.sessionState.videoRotation += 90;
      if (this.state.sessionState.videoRotation >= 360) {
        this.state.sessionState.videoRotation = 0;
      }
      this.applyVideoRotation();
      this.state.saveSessionState();
    }
_computeVideoFitStyles(rotation) {
      let normalized = rotation % 360;
      if (normalized < 0) normalized += 360;
      if (normalized === 0) {
        return {
          position: "",
          top: "",
          left: "",
          transformOrigin: "",
          objectFit: "",
          maxWidth: "",
          maxHeight: "",
          width: "",
          height: "",
          transform: ""
        };
      }
      const sideways = normalized === 90 || normalized === 270;
      return {
        position: "absolute",
        top: "50%",
        left: "50%",
        transformOrigin: "center center",
        objectFit: "contain",
        maxWidth: "none",
        maxHeight: "none",
        width: sideways ? "100vh" : "100%",
        height: sideways ? "100vw" : "100%",
        transform: `translate(-50%, -50%) rotate(${normalized}deg)`
      };
    }
    applyVideoRotation() {
      const rotation = this.state.sessionState.videoRotation;
      document.querySelectorAll(".media-group video").forEach((video) => {
        const vjsContainer = video.closest(".video-js");
        if (vjsContainer) {
          const tech = vjsContainer.querySelector(".vjs-tech") || video;
          vjsContainer.style.overflow = rotation === 0 ? "" : "hidden";
          Object.assign(tech.style, this._computeVideoFitStyles(rotation));
        } else {
          const wrapper = video.closest(".rome-video-wrapper");
          const customControls = wrapper ? wrapper.querySelector(".rome-custom-controls") : null;
          video.controls = false;
          if (customControls) customControls.classList.add("active");
          Object.assign(video.style, this._computeVideoFitStyles(rotation));
        }
      });
      $("#rotateBtn").text(`Rotate: ${rotation}°`);
    }
    ensureVjsCustomControls() {
      document.querySelectorAll(".video-js").forEach((vjsEl) => {
        if (vjsEl.querySelector(".rome-vjs-controls") || vjsEl.dataset.romeVjsPending)
          return;
        const player = this.deps.getVjsPlayer(vjsEl);
        if (!player) return;
        vjsEl.dataset.romeVjsPending = "1";
        player.ready(function() {
          var _a, _b, _c, _d;
          if (((_a = player.isDisposed) == null ? void 0 : _a.call(player)) || player.isDisposed_) {
            delete vjsEl.dataset.romeVjsPending;
            return;
          }
          delete vjsEl.dataset.romeVjsPending;
          if (vjsEl.querySelector(".rome-vjs-controls")) return;
          const ac = new AbortController();
          const sig = ac.signal;
          (_b = player.controlBar) == null ? void 0 : _b.hide();
          const controls = document.createElement("div");
          controls.className = "rome-vjs-controls";
          const playBtn = document.createElement("button");
          playBtn.className = "rome-vjs-play-btn";
          playBtn.title = "Play/Pause";
          playBtn.textContent = "▶";
          const progress = document.createElement("div");
          progress.className = "rome-vjs-progress";
          const fill = document.createElement("div");
          fill.className = "rome-vjs-fill";
          progress.appendChild(fill);
          const time = document.createElement("span");
          time.className = "rome-vjs-time";
          time.textContent = "0:00 / 0:00";
          controls.append(playBtn, progress, time);
          player.el().appendChild(controls);
          const stopBubble = (e) => {
            e.stopPropagation();
          };
          for (const evt of [
            "mousedown",
            "mouseup",
            "mousemove",
            "mouseover",
            "mouseout",
            "click",
            "pointerdown",
            "pointerup",
            "pointermove",
            "pointerover",
            "pointerout"
          ]) {
            controls.addEventListener(evt, stopBubble, { signal: sig });
          }
          const fmt = (s) => {
            if (!isFinite(s)) return "0:00";
            const m = Math.floor(s / 60);
            const sec = String(Math.floor(s % 60)).padStart(2, "0");
            return `${m}:${sec}`;
          };
          const syncPlayBtn = () => {
            playBtn.textContent = player.paused() ? "▶" : "⏸";
          };
          player.on("play", syncPlayBtn);
          player.on("pause", syncPlayBtn);
          const onTimeUpdate = () => {
            const cur = player.currentTime();
            const dur = player.duration();
            if (isFinite(dur) && dur > 0) {
              fill.style.width = cur / dur * 100 + "%";
              time.textContent = `${fmt(cur)} / ${fmt(dur)}`;
            }
          };
          player.on("timeupdate", onTimeUpdate);
          playBtn.addEventListener(
            "click",
            (e) => {
              e.stopPropagation();
              const p = player.paused() ? player.play() : player.pause();
              if (p && typeof p.catch === "function") {
                p.catch((err) => {
                  if (err.name !== "AbortError" && err.name !== "NotAllowedError")
                    console.error("Rome: play/pause failed:", err);
                });
              }
            },
            { signal: sig }
          );
          const scrub = (e) => {
            const dur = player.duration();
            if (!isFinite(dur) || dur <= 0) return;
            const rect = progress.getBoundingClientRect();
            if (!rect.width) return;
            const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
            player.currentTime(ratio * dur);
            fill.style.width = ratio * 100 + "%";
          };
          progress.addEventListener(
            "click",
            (e) => {
              e.stopPropagation();
              scrub(e);
            },
            { signal: sig }
          );
          progress.addEventListener(
            "pointerdown",
            (e) => {
              e.stopPropagation();
              progress.setPointerCapture(e.pointerId);
              const onMove = (ev) => {
                ev.stopPropagation();
                scrub(ev);
              };
              const cleanup = (ev) => {
                ev.stopPropagation();
                progress.removeEventListener("pointermove", onMove);
                progress.removeEventListener("pointerup", cleanup);
                progress.removeEventListener("pointercancel", cleanup);
              };
              progress.addEventListener("pointermove", onMove, { signal: sig });
              progress.addEventListener("pointerup", cleanup, { signal: sig });
              progress.addEventListener("pointercancel", cleanup, { signal: sig });
              scrub(e);
            },
            { signal: sig }
          );
          if (((_d = (_c = player.options_) == null ? void 0 : _c.userActions) == null ? void 0 : _d.click) === false) {
            vjsEl.addEventListener(
              "click",
              (e) => {
                if (e.target.closest(".rome-vjs-controls")) return;
                const p = player.paused() ? player.play() : player.pause();
                if (p && typeof p.catch === "function") {
                  p.catch((err) => {
                    if (err.name !== "AbortError" && err.name !== "NotAllowedError")
                      console.error("Rome: play/pause failed:", err);
                  });
                }
              },
              { signal: sig }
            );
          }
          player.on("dispose", () => {
            ac.abort();
            player.off("play", syncPlayBtn);
            player.off("pause", syncPlayBtn);
            player.off("timeupdate", onTimeUpdate);
            controls.remove();
          });
        });
      });
    }
    ensureVideoWrappers() {
      document.querySelectorAll(".media-group video").forEach((video, idx) => {
        if (video.closest(".rome-video-wrapper")) return;
        if (video.closest(".video-js")) return;
        if (!video.parentNode) return;
        try {
          const wrapper = document.createElement("div");
          wrapper.className = "rome-video-wrapper";
          video.parentNode.insertBefore(wrapper, video);
          wrapper.appendChild(video);
          this.createCustomControls(video);
        } catch (err) {
          console.error(`Rome: ensureVideoWrappers failed for video ${idx}:`, err);
        }
      });
    }
    createCustomControls(video) {
      const wrapper = video.closest(".rome-video-wrapper");
      if (!wrapper || wrapper.querySelector(".rome-custom-controls")) return;
      const ac = new AbortController();
      const { signal } = ac;
      wrapper._romeControlsAbort = ac;
      const playBtn = document.createElement("button");
      playBtn.className = "rome-play-btn";
      playBtn.title = "Play/Pause";
      playBtn.textContent = "▶";
      const progressBar = document.createElement("div");
      progressBar.className = "rome-progress-bar";
      const progressFill = document.createElement("div");
      progressFill.className = "rome-progress-fill";
      progressBar.appendChild(progressFill);
      const timeDisplay = document.createElement("span");
      timeDisplay.className = "rome-time-display";
      timeDisplay.textContent = "0:00 / 0:00";
      const controls = document.createElement("div");
      controls.className = "rome-custom-controls";
      controls.append(playBtn, progressBar, timeDisplay);
      wrapper.appendChild(controls);
      controls.classList.add("active");
      wrapper.addEventListener(
        "mouseenter",
        () => wrapper.classList.add("rome-bar-expanded"),
        { signal }
      );
      wrapper.addEventListener(
        "mouseleave",
        () => wrapper.classList.remove("rome-bar-expanded"),
        { signal }
      );
      playBtn.addEventListener(
        "click",
        () => {
          if (video.paused) {
            prioritizeVideoForPlayback(video);
            video.play().catch((err) => {
              if (err.name !== "AbortError")
                console.warn("Rome: play() rejected:", err.message);
            });
          } else {
            video.pause();
          }
        },
        { signal }
      );
      video.addEventListener(
        "click",
        (e) => {
          const wrapperEl = video.closest(".rome-video-wrapper");
          if (!wrapperEl) {
            console.warn(
              "Rome: video click handler - video not inside .rome-video-wrapper, skipping play toggle"
            );
            return;
          }
          const ctrl = wrapperEl.querySelector(".rome-custom-controls");
          if (ctrl && ctrl.classList.contains("active")) {
            e.preventDefault();
            if (video.paused) {
              prioritizeVideoForPlayback(video);
              video.play().catch((err) => {
                if (err.name !== "AbortError")
                  console.warn("Rome: play() rejected:", err.message);
              });
            } else {
              video.pause();
            }
          }
        },
        { signal }
      );
      video.addEventListener(
        "play",
        () => {
          playBtn.textContent = "▮▮";
        },
        { signal }
      );
      video.addEventListener(
        "pause",
        () => {
          playBtn.textContent = "▶";
        },
        { signal }
      );
      video.addEventListener(
        "timeupdate",
        () => {
          const dur = video.duration;
          const cur = video.currentTime;
          const validDuration = Number.isFinite(dur) && dur > 0;
          if (validDuration) {
            progressFill.style.width = cur / dur * 100 + "%";
            timeDisplay.textContent = this.formatTime(cur) + " / " + this.formatTime(dur);
          } else {
            progressFill.style.width = "0%";
            timeDisplay.textContent = this.formatTime(cur) + " / --:--";
          }
        },
        { signal }
      );
      let isDragging = false;
      let wasPaused = false;
      let pendingSeekRatio = null;
      let seekRafId = null;
      const commitSeek = () => {
        seekRafId = null;
        if (pendingSeekRatio !== null && video.duration && isFinite(video.duration)) {
          video.currentTime = pendingSeekRatio * video.duration;
          pendingSeekRatio = null;
        }
      };
      const seekToPosition = (clientX, immediate) => {
        const rect = progressBar.getBoundingClientRect();
        if (rect.width <= 0) return;
        const ratio = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
        if (!Number.isFinite(video.duration) || video.duration <= 0) return;
        progressFill.style.width = ratio * 100 + "%";
        timeDisplay.textContent = this.formatTime(ratio * video.duration) + " / " + this.formatTime(video.duration);
        if (immediate) {
          video.currentTime = ratio * video.duration;
        } else {
          pendingSeekRatio = ratio;
          if (!seekRafId) {
            seekRafId = requestAnimationFrame(commitSeek);
          }
        }
      };
      progressBar.addEventListener(
        "pointerdown",
        (e) => {
          if (!Number.isFinite(video.duration) || video.duration <= 0) return;
          isDragging = true;
          wasPaused = video.paused;
          video.pause();
          progressBar.setPointerCapture(e.pointerId);
          seekToPosition(e.clientX, true);
          e.preventDefault();
        },
        { signal }
      );
      progressBar.addEventListener(
        "pointermove",
        (e) => {
          if (isDragging) {
            seekToPosition(e.clientX, false);
          }
        },
        { signal }
      );
      const endDrag = () => {
        if (isDragging) {
          if (seekRafId) {
            cancelAnimationFrame(seekRafId);
            seekRafId = null;
          }
          if (pendingSeekRatio !== null && video.duration && isFinite(video.duration)) {
            video.currentTime = pendingSeekRatio * video.duration;
            pendingSeekRatio = null;
          }
          if (!wasPaused)
            video.play().catch((err) => {
              if (err.name !== "AbortError")
                console.warn("Rome: play() rejected on seek resume:", err.message);
            });
        }
        isDragging = false;
      };
      progressBar.addEventListener("pointerup", endDrag, { signal });
      progressBar.addEventListener("pointercancel", endDrag, { signal });
      video.addEventListener(
        "emptied",
        () => {
          isDragging = false;
          if (seekRafId) {
            cancelAnimationFrame(seekRafId);
            seekRafId = null;
          }
          pendingSeekRatio = null;
          ac.abort();
        },
        { signal }
      );
    }
    formatTime(seconds) {
      if (!Number.isFinite(seconds) || seconds < 0) return "0:00";
      const mins = Math.floor(seconds / 60);
      const secs = Math.floor(seconds % 60);
      return mins + ":" + (secs < 10 ? "0" : "") + secs;
    }
  }
  class WatchedTracker {
    constructor(deps) {
      __publicField(this, "deps");
      __publicField(this, "watchedVideos");
      __publicField(this, "currentVideoId");
      __publicField(this, "intervalId");
      __publicField(this, "observer");
      __publicField(this, "observedAlbums");
      __publicField(this, "_containerObserver", null);
      this.deps = deps;
      this.watchedVideos = this.deps.storage.getWithFallback(
        this.deps.config.STORAGE_KEYS.WATCHED_VIDEOS,
        []
      );
      this.currentVideoId = this.deps.getVideoId(window.location.href);
      this.intervalId = null;
      this.observer = null;
      this.observedAlbums = new Set();
      this.initialize();
    }
    initialize() {
      this.saveCurrentVideo();
      this.setupIntersectionObserver();
      this.setupMiddleClickTracking();
      this.startPeriodicCheck();
      window.addEventListener("beforeunload", () => {
        this.cleanup();
      });
      document.addEventListener("visibilitychange", () => {
        if (document.hidden) {
          this.pause();
        } else {
          this.resume();
        }
      });
    }
    setupIntersectionObserver() {
      this.deps.logger.log("WatchedTracker", "Setting up IntersectionObserver");
      this.observer = new IntersectionObserver(
        (entries) => {
          this.deps.logger.performance("watched-intersection", () => {
            entries.forEach((entry) => {
              if (entry.isIntersecting) {
                this.markAlbumIfWatched(entry.target);
              }
            });
          });
        },
        {
          root: null,
          rootMargin: "50px",
          threshold: 0.1
        }
      );
      this.observeExistingAlbums();
      const appendRoot = document.querySelector(this.deps.config.SELECTORS.INFY_APPEND_ROOT) || document.querySelector(this.deps.config.SELECTORS.ALBUMS_CONTAINER);
      this._containerObserver = this.deps.createAlbumMutationObserver({
        target: appendRoot,
        itemSelector: this.deps.config.SELECTORS.ALBUM_ITEMS,
        onItemsAdded: (albums) => {
          const handleAlbum = this.deps.wrap((album) => {
            this.observeAlbum(album);
            this.markAlbumIfWatched(album);
          }, "WatchedTracker.onItemsAdded");
          albums.forEach(handleAlbum);
        }
      });
    }
    setupMiddleClickTracking() {
      this.deps.logger.log("WatchedTracker", "Setting up middle-click tracking");
      const albumsContainer = document.querySelector(
        this.deps.config.SELECTORS.ALBUMS_CONTAINER
      );
      if (!albumsContainer) {
        this.deps.logger.log(
          "WatchedTracker",
          "No albums container found for middle-click tracking"
        );
        return;
      }
      albumsContainer.addEventListener("auxclick", (event) => {
        if (event.button !== 1) return;
        this.deps.wrap(() => {
          const albumItem = event.target.closest('div[id^="album-"]');
          if (!albumItem) return;
          const albumLink = albumItem.querySelector("a[href]");
          if (!albumLink) return;
          const videoId = this.deps.getVideoId(albumLink.href);
          if (!videoId) return;
          this.markAlbumAsWatchedInstantly(albumItem, videoId);
          this.deps.logger.log(
            "WatchedTracker",
            `Middle-clicked album marked as watched: ${videoId}`
          );
        }, "MiddleClickTracking")();
      });
      this.deps.logger.log("WatchedTracker", "Middle-click tracking initialized");
    }
    observeExistingAlbums() {
      const albums = document.querySelectorAll(this.deps.config.SELECTORS.ALBUM_ITEMS);
      this.deps.logger.log("WatchedTracker", `Observing ${albums.length} existing albums`);
      albums.forEach((album) => this.observeAlbum(album));
    }
    observeAlbum(album) {
      if (!this.observedAlbums.has(album)) {
        this.observer.observe(album);
        this.observedAlbums.add(album);
      }
    }
    markAlbumIfWatched(album) {
      const link = album.querySelector("a[href]");
      if (link) {
        const videoId = this.deps.getVideoId(link.href);
        if (videoId && this.watchedVideos.includes(videoId)) {
          const container = album.querySelector(
            this.deps.config.SELECTORS.THUMBNAIL_CONTAINER
          );
          if (container && !container.style.borderBottom) {
            container.style.borderBottom = "5px solid red";
            this.deps.logger.log("WatchedTracker", `Marked album as watched: ${videoId}`);
          }
        }
      }
    }
    markAlbumAsWatchedInstantly(album, videoId) {
      if (!this.watchedVideos.includes(videoId)) {
        this.watchedVideos.push(videoId);
        if (this.watchedVideos.length > 5e3) {
          this.watchedVideos = this.watchedVideos.slice(-5e3);
        }
        this.deps.storage.setWithRetry(
          this.deps.config.STORAGE_KEYS.WATCHED_VIDEOS,
          this.watchedVideos
        ).catch((err) => {
          console.error("Rome: storage write failed", err);
          this.deps.notifyStorageFailure("watched videos");
        });
      }
      const container = album.querySelector(
        this.deps.config.SELECTORS.THUMBNAIL_CONTAINER
      );
      if (container) {
        container.style.borderBottom = "5px solid red";
        this.deps.logger.log(
          "WatchedTracker",
          `Instantly marked album as watched: ${videoId}`
        );
      } else {
        const altContainer = album.querySelector(".album-thumbnail-container") || album.querySelector("img") || album.querySelector("a");
        if (altContainer) {
          altContainer.style.borderBottom = "5px solid red";
          this.deps.logger.log(
            "WatchedTracker",
            `Instantly marked album as watched (alt): ${videoId}`
          );
        }
      }
    }
    startPeriodicCheck() {
      this.intervalId = setInterval(() => {
        this.deps.wrap(() => {
          const unobservedAlbums = Array.from(
            document.querySelectorAll(this.deps.config.SELECTORS.ALBUM_ITEMS)
          ).filter((album) => !this.observedAlbums.has(album));
          if (unobservedAlbums.length > 0) {
            this.deps.logger.log(
              "WatchedTracker",
              `Processing ${unobservedAlbums.length} unobserved albums`
            );
            unobservedAlbums.forEach((album) => {
              this.observeAlbum(album);
              this.markAlbumIfWatched(album);
            });
          }
        }, "WatchedTracker")();
      }, this.deps.config.TIMEOUTS.WATCHED_CHECK);
    }
    pause() {
      if (this.intervalId) {
        clearInterval(this.intervalId);
        this.intervalId = null;
      }
      if (this.observer) {
        this.observer.disconnect();
      }
      this.deps.logger.log("WatchedTracker", "Paused");
    }
    resume() {
      if (!this.intervalId) {
        this.startPeriodicCheck();
      }
      if (this.observer) {
        this.observeExistingAlbums();
      }
      this.deps.logger.log("WatchedTracker", "Resumed");
    }
    cleanup() {
      if (this.intervalId) {
        clearInterval(this.intervalId);
        this.intervalId = null;
      }
      if (this.observer) {
        this.observer.disconnect();
        this.observer = null;
      }
      if (this._containerObserver) {
        this._containerObserver.disconnect();
        this._containerObserver = null;
      }
      this.observedAlbums.clear();
      this.deps.logger.log("WatchedTracker", "Cleaned up");
    }
    saveCurrentVideo() {
      if (this.currentVideoId && !this.watchedVideos.includes(this.currentVideoId)) {
        this.watchedVideos.push(this.currentVideoId);
        if (this.watchedVideos.length > 5e3) {
          this.watchedVideos = this.watchedVideos.slice(-5e3);
        }
        this.deps.storage.setWithRetry(
          this.deps.config.STORAGE_KEYS.WATCHED_VIDEOS,
          this.watchedVideos
        ).catch((err) => {
          console.error("Rome: storage write failed", err);
          this.deps.notifyStorageFailure("watched videos");
        });
        this.deps.logger.log("WatchedTracker", `Saved current video: ${this.currentVideoId}`);
      }
    }
markWatchedAlbums() {
      document.querySelectorAll(this.deps.config.SELECTORS.ALBUM_ITEMS).forEach((album) => {
        this.markAlbumIfWatched(album);
      });
    }
  }
  async function main() {
    try {
      performance.mark("rome-perf-script-start");
    } catch (_e) {
    }
    const logger = createLogger(ROME_CONFIG);
    const storage = createStorage({
      config: ROME_CONFIG,
      logger,
      getValue: _GM_getValue,
      setValue: _GM_setValue
    });
    const currentPage = detectPage(window.location, document, logger);
    let _romePerfCachedEnabled = null;
    function _romePerfFreshBuffer() {
      return {
        meta: null,
        probe1: null,
        probe2: {
          rome: {},
          observers: { created: 0 },
          videojsCalls: { count: 0, totalMs: 0 }
        },
        probe3: null,
        probe4: null,
        probe5: null
      };
    }
    let _romePerfBuffer = _romePerfFreshBuffer();
    let _romePerfInitialized = false;
    let _romePerfLongtaskTotalMs = 0;
    let _romePerfLcpTimestamp = null;
    let _romePerfMemoryAtDcl = null;
    let _romePerfVideojsCallTimes = [];
    let _romePerfObserverCreatedCount = 0;
    let _romePerfTtff = { loadedmetadataMs: null, playingMs: null, src: null };
    let _romePerfClickProbeRan = false;
    const _romePerfWarnedKeys = new Set();
    function _romePerfWarnOnce(key, msg) {
      if (_romePerfWarnedKeys.has(key)) return;
      _romePerfWarnedKeys.add(key);
      console.warn(msg);
    }
    function _romePerfReadScriptVersion() {
      return typeof GM_info !== "undefined" && GM_info.script ? GM_info.script.version : null;
    }
    function _romePerfSafeDisconnect(obs) {
      if (!obs) return;
      try {
        obs.disconnect();
      } catch (_e) {
      }
    }
    function _romePerfComputeEnabled() {
      try {
        const params = new URLSearchParams(window.location.search);
        if (params.get("perf") === "1") return true;
      } catch (_e) {
      }
      if (typeof window !== "undefined" && window.__romePerfEnabled === true) {
        return true;
      }
      try {
        if (localStorage.getItem("romePerfEnabled") === "1") return true;
      } catch (_e) {
      }
      return false;
    }
    const RomePerf = {
      get enabled() {
        if (_romePerfCachedEnabled === null) {
          _romePerfCachedEnabled = _romePerfComputeEnabled();
        }
        return _romePerfCachedEnabled;
      },
      mark(name) {
        if (!this.enabled) return;
        try {
          performance.mark(`rome-perf-${name}`);
        } catch (_e) {
          _romePerfWarnOnce(
            "mark",
            "RomePerf: performance.mark unavailable; subsequent marks will silently no-op"
          );
        }
      },
      measure(name, startMark, endMark) {
        if (!this.enabled) return;
        try {
          performance.measure(
            `rome-perf-${name}`,
            `rome-perf-${startMark}`,
            `rome-perf-${endMark}`
          );
        } catch (_e) {
          _romePerfWarnOnce(
            "measure",
            "RomePerf: performance.measure unavailable; subsequent measures will silently no-op"
          );
        }
      },
      _frameStats(durations) {
        if (durations.length === 0) {
          return { frames: 0, min: null, median: null, p95: null };
        }
        const sorted = [...durations].sort((a, b) => a - b);
        const pick = (q) => sorted[Math.min(sorted.length - 1, Math.floor(q * sorted.length))];
        return {
          frames: sorted.length,
          min: sorted[0],
          median: pick(0.5),
          p95: pick(0.95)
        };
      },
      _markStart(name) {
        const e = performance.getEntriesByName(`rome-perf-${name}`)[0];
        return e ? e.startTime : null;
      },
      _measureDuration(name) {
        const e = performance.getEntriesByName(`rome-perf-${name}`)[0];
        return e && typeof e.duration === "number" ? e.duration : null;
      },
      flush() {
        if (!this.enabled) return;
        const meta = {
          url: window.location.href,
          ts: ( new Date()).toISOString(),
          romeVersion: _romePerfReadScriptVersion()
        };
        const out = { ..._romePerfBuffer, meta };
        console.log(`[RomePerf] ${JSON.stringify(out)}`);
        _romePerfBuffer = _romePerfFreshBuffer();
      },
      async scrollProbe(durationMs = 5e3) {
        if (!this.enabled) {
          console.warn("RomePerf: scrollProbe called but harness disabled");
          return null;
        }
        const startY = window.scrollY;
        const targetY = Math.max(
          document.documentElement.scrollHeight - window.innerHeight,
          startY
        );
        const startTs = performance.now();
        const endTs = startTs + durationMs;
        const frameTimes = [];
        let lastTs = startTs;
        return new Promise((resolve) => {
          const tick = (ts) => {
            const dt = ts - lastTs;
            if (lastTs !== startTs && !document.hidden) frameTimes.push(dt);
            lastTs = ts;
            const progress = Math.min(1, (ts - startTs) / durationMs);
            window.scrollTo({
              top: startY + (targetY - startY) * progress,
              behavior: "auto"
            });
            if (ts < endTs) {
              requestAnimationFrame(tick);
            } else {
              const stats = this._frameStats(frameTimes);
              const probe4 = {
                durationMs,
                scrolledFromY: startY,
                scrolledToY: window.scrollY,
                ...stats
              };
              _romePerfBuffer.probe4 = probe4;
              this.flush();
              resolve(probe4);
            }
          };
          requestAnimationFrame(tick);
        });
      },
      async clickProbe(timeoutMs = 3e4) {
        if (!this.enabled) {
          console.warn("RomePerf: clickProbe called but harness disabled");
          return null;
        }
        if (_romePerfClickProbeRan) {
          console.warn("RomePerf: clickProbe already ran this session");
          return null;
        }
        _romePerfClickProbeRan = true;
        const candidates = [...document.querySelectorAll("video")].map((v) => ({ v, top: v.getBoundingClientRect().top })).filter((x) => x.top > 0).sort((a, b) => a.top - b.top);
        if (candidates.length === 0) {
          const probe5 = { status: "no_video", src: null };
          _romePerfBuffer.probe5 = probe5;
          this.flush();
          return probe5;
        }
        const topmost = candidates[0].v;
        const src = topmost.currentSrc || topmost.src;
        const readyStateAtClick = topmost.readyState;
        if (!topmost.paused && topmost.currentTime > 0) {
          const probe5 = { status: "already_playing", src, readyStateAtClick };
          _romePerfBuffer.probe5 = probe5;
          this.flush();
          return probe5;
        }
        return new Promise((resolve) => {
          let resolved = false;
          const finish = (probe5) => {
            if (resolved) return;
            resolved = true;
            topmost.removeEventListener("playing", onPlaying);
            _romePerfBuffer.probe5 = probe5;
            this.flush();
            resolve(probe5);
          };
          const clickStartMs = performance.now();
          const onPlaying = () => {
            const playingMs = performance.now();
            finish({
              status: "ok",
              src,
              clickStartMs,
              playingMs,
              ttfp: playingMs - clickStartMs,
              readyStateAtClick,
              readyStateAtPlaying: topmost.readyState
            });
          };
          topmost.addEventListener("playing", onPlaying);
          try {
            topmost.dispatchEvent(
              new MouseEvent("click", { bubbles: true, cancelable: true, view: window })
            );
          } catch (_e) {
            try {
              topmost.play();
            } catch (_e2) {
            }
          }
          setTimeout(() => {
            finish({
              status: "timeout",
              src,
              clickStartMs,
              playingMs: null,
              ttfp: null,
              readyStateAtClick,
              readyStateAtPlaying: topmost.readyState
            });
          }, timeoutMs);
        });
      },
      markVideojsCall(durationMs) {
        if (!this.enabled) return;
        _romePerfVideojsCallTimes.push(durationMs);
      },
      markObserverCreated() {
        if (!this.enabled) return;
        _romePerfObserverCreatedCount += 1;
      },
      _captureProbe2() {
        _romePerfBuffer.probe2 = {
          rome: {
            scriptStart: this._markStart("script-start"),
            bootstrapEnd: this._markStart("bootstrap-end"),
            bootstrap: this._measureDuration("bootstrap"),
            addProperID: this._measureDuration("addProperID"),
            videoCleanseReplace: this._measureDuration("videoCleanseReplace")
          },
          observers: { created: _romePerfObserverCreatedCount },
          videojsCalls: {
            count: _romePerfVideojsCallTimes.length,
            totalMs: _romePerfVideojsCallTimes.reduce((a, b) => a + b, 0),
            stats: this._frameStats(_romePerfVideojsCallTimes)
          }
        };
      },
      _captureProbe1() {
        let domContentLoaded = null;
        let loadEnd = null;
        let timingError = null;
        try {
          const navEntries = performance.getEntriesByType("navigation");
          if (navEntries.length > 0) {
            domContentLoaded = navEntries[0].domContentLoadedEventEnd;
            loadEnd = navEntries[0].loadEventEnd;
          } else if (performance.timing) {
            const t = performance.timing;
            domContentLoaded = t.domContentLoadedEventEnd - t.navigationStart;
            loadEnd = t.loadEventEnd - t.navigationStart;
          }
        } catch (_e) {
          console.warn("RomePerf probe1: navigation timing read failed", _e);
          timingError = String(_e);
        }
        let memoryAtFlush = null;
        if (typeof performance !== "undefined" && performance.memory && typeof performance.memory.usedJSHeapSize === "number") {
          memoryAtFlush = performance.memory.usedJSHeapSize;
        }
        _romePerfBuffer.probe1 = {
          domContentLoaded,
          load: loadEnd,
          largestContentfulPaint: _romePerfLcpTimestamp,
          longtaskTotalMs: _romePerfLongtaskTotalMs,
          memoryAtDcl: _romePerfMemoryAtDcl,
          memoryAtFlush
        };
        if (timingError !== null) {
          _romePerfBuffer.probe1.timingError = timingError;
        }
      },
      _initTtff() {
        if (!this.enabled) return;
        setTimeout(() => {
          let topmost = null;
          try {
            const candidates = [...document.querySelectorAll("video")].map((v) => ({ v, top: v.getBoundingClientRect().top })).filter((x) => x.top > 0).sort((a, b) => a.top - b.top);
            if (candidates.length === 0) return;
            topmost = candidates[0].v;
            _romePerfTtff.src = topmost.currentSrc || topmost.src;
          } catch (_e) {
            console.warn("RomePerf TTFF: candidate selection failed", _e);
            return;
          }
          const stamp = () => performance.now();
          const safeUnbind = (event, handler) => {
            try {
              topmost.removeEventListener(event, handler);
            } catch (_e) {
              console.warn(`RomePerf TTFF: removeEventListener failed for ${event}`, _e);
            }
          };
          const onLoadedMeta = () => {
            if (_romePerfTtff.loadedmetadataMs === null) {
              _romePerfTtff.loadedmetadataMs = stamp();
            }
            safeUnbind("loadedmetadata", onLoadedMeta);
          };
          const onPlaying = () => {
            if (_romePerfTtff.playingMs === null) {
              _romePerfTtff.playingMs = stamp();
            }
            safeUnbind("playing", onPlaying);
          };
          try {
            topmost.addEventListener("loadedmetadata", onLoadedMeta);
            topmost.addEventListener("playing", onPlaying);
            if (topmost.readyState >= 1) onLoadedMeta();
            if (!topmost.paused && topmost.currentTime > 0) onPlaying();
          } catch (_e) {
            console.warn("RomePerf TTFF: listener wiring or synchronous fire failed", _e);
          }
        }, 1500);
      },
      _captureProbe3() {
        _romePerfBuffer.probe3 = {
          src: _romePerfTtff.src,
          loadedmetadataMs: _romePerfTtff.loadedmetadataMs,
          playingMs: _romePerfTtff.playingMs
        };
      },
      _init() {
        if (!this.enabled || _romePerfInitialized) return;
        _romePerfInitialized = true;
        let longtaskObs = null;
        try {
          longtaskObs = new PerformanceObserver((list) => {
            for (const entry of list.getEntries()) {
              _romePerfLongtaskTotalMs += entry.duration;
            }
          });
          longtaskObs.observe({ type: "longtask", buffered: true });
        } catch (_e) {
        }
        let lcpObs = null;
        try {
          lcpObs = new PerformanceObserver((list) => {
            const entries = list.getEntries();
            if (entries.length > 0) {
              _romePerfLcpTimestamp = entries[entries.length - 1].startTime;
            }
          });
          lcpObs.observe({ type: "largest-contentful-paint", buffered: true });
        } catch (_e) {
        }
        this._initTtff();
        const recordMemAtDcl = () => {
          if (typeof performance !== "undefined" && performance.memory && typeof performance.memory.usedJSHeapSize === "number") {
            _romePerfMemoryAtDcl = performance.memory.usedJSHeapSize;
          }
        };
        if (document.readyState === "loading") {
          document.addEventListener("DOMContentLoaded", recordMemAtDcl, { once: true });
        } else {
          recordMemAtDcl();
        }
        setTimeout(() => {
          _romePerfSafeDisconnect(longtaskObs);
          _romePerfSafeDisconnect(lcpObs);
          this._captureProbe1();
          this._captureProbe2();
          this._captureProbe3();
          this.flush();
        }, 5e3);
        try {
          const params = new URLSearchParams(window.location.search);
          if (params.get("autoclick") === "1") {
            setTimeout(() => {
              this.clickProbe(3e4);
            }, 6e3);
          }
        } catch (_e) {
        }
      }
    };
    RomePerf._init();
    if (RomePerf.enabled) {
      globalThis.RomePerf = RomePerf;
    }
    const multiVideo = createMultiVideoPlayback({ config: ROME_CONFIG, logger, perf: RomePerf });
    function getVjsPlayer(vjsEl) {
      var _a;
      if (vjsEl.player) return vjsEl.player;
      try {
        const vjs = typeof videojs !== "undefined" ? videojs : (window == null ? void 0 : window.videojs) ?? (typeof _unsafeWindow !== "undefined" ? _unsafeWindow == null ? void 0 : _unsafeWindow.videojs : null);
        const all = ((_a = vjs == null ? void 0 : vjs.getAllPlayers) == null ? void 0 : _a.call(vjs)) ?? [];
        return all.find((p) => p && p.el() === vjsEl) ?? null;
      } catch (e) {
        return null;
      }
    }
    const libs = safeLibraryAccess();
    if (!libs) {
      console.error("Rome: Critical library access failure - using minimal functionality");
      return;
    }
    _GM_addStyle(`
/* VJS Custom Controls */
.rome-vjs-controls {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px 12px 12px;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
  pointer-events: none;
}

.rome-vjs-controls > * {
  pointer-events: auto;
}

.rome-vjs-play-btn {
  background: none;
  border: none;
  color: #fff;
  cursor: pointer;
  font-size: 16px;
  padding: 0;
}

.rome-vjs-progress {
  flex: 1;
  box-sizing: content-box;
  height: 4px;
  background: rgba(255, 255, 255, 0.3);
  border-radius: 2px;
  cursor: pointer;
  position: relative;
  transition: height 0.15s ease-in-out, padding 0.15s ease-in-out;
  align-self: center;
  padding: 8px 0;
  background-clip: content-box;
}

.video-js:hover .rome-vjs-progress {
  height: 28px;
  padding: 0;
}

.rome-vjs-fill {
  height: 100%;
  background: #eb6395;
  border-radius: 2px;
  width: 0%;
  pointer-events: none;
}

.rome-vjs-time {
  color: #fff;
  font-size: 12px;
  font-family: monospace;
  white-space: nowrap;
}

/* Rome Core Styles */
.inactive-gm { background: #a09f9d; }
.active-gm { background: #eb6395 !important; }

/* #togglePhotos, #sbsBtn: no explicit margin-right; natural spacing is sufficient. */

/* Side-by-side video layout */
.media-group.col-sm-6 {
  width: 50% !important;
  display: inline-block !important;
  vertical-align: top !important;
  padding: 5px !important;
  box-sizing: border-box !important;
}

@media (max-width: 768px) {
  .media-group.col-sm-6 {
    width: 100% !important;
  }
}


/* Rome Modal Styles */
.rome-modal {
  display: none;
  position: fixed;
  z-index: 99999;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(3px);
}

.rome-modal-content {
  background: linear-gradient(135deg, #2c2c2c 0%, #1a1a1a 100%);
  margin: 5% auto;
  padding: 0;
  border: none;
  border-radius: 12px;
  width: 90%;
  max-width: 600px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
  color: #fff;
  animation: modalSlideIn 0.3s ease-out;
}

@keyframes modalSlideIn {
  from { opacity: 0; transform: translateY(-50px); }
  to { opacity: 1; transform: translateY(0); }
}

.rome-modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20px 25px;
  background: linear-gradient(135deg, #eb6395 0%, #d14d7a 100%);
  border-radius: 12px 12px 0 0;
  color: white;
}

.rome-modal-title {
  font-size: 18px;
  font-weight: bold;
  margin: 0;
}

.rome-close-modal {
  font-size: 28px;
  font-weight: bold;
  cursor: pointer;
  line-height: 1;
  padding: 0 5px;
  border-radius: 3px;
  transition: background 0.3s;
}

.rome-close-modal:hover {
  background: rgba(255, 255, 255, 0.2);
}

.rome-modal-body {
  padding: 25px;
}

.rome-settings-section {
  margin-bottom: 20px;
}

.rome-settings-section h4 {
  color: #eb6395;
  margin: 0 0 15px 0;
  font-size: 16px;
  border-bottom: 1px solid rgba(235, 99, 149, 0.3);
  padding-bottom: 8px;
}

/* Blocklist Styles */
.blocklist-display {
  background: rgba(0, 0, 0, 0.4);
  padding: 15px;
  border-radius: 8px;
  max-height: 300px;
  overflow-y: auto;
  margin-bottom: 15px;
  border: 1px solid rgba(235, 99, 149, 0.2);
}

.blocklist-display::-webkit-scrollbar {
  width: 8px;
}

.blocklist-display::-webkit-scrollbar-track {
  background: rgba(0, 0, 0, 0.2);
  border-radius: 4px;
}

.blocklist-display::-webkit-scrollbar-thumb {
  background: rgba(235, 99, 149, 0.6);
  border-radius: 4px;
}

.blocklist-display::-webkit-scrollbar-thumb:hover {
  background: rgba(235, 99, 149, 0.8);
}

.blocklist-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  color: #fff;
  font-family: monospace;
  font-size: 13px;
}

.blocklist-item:last-child {
  border-bottom: none;
}

.blocklist-item span {
  flex: 1;
  word-break: break-word;
  margin-right: 10px;
}

.blocklist-item button {
  background: linear-gradient(135deg, #ff4757 0%, #ff3742 100%);
  color: white;
  border: none;
  padding: 4px 8px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 11px;
  font-weight: bold;
  transition: all 0.3s;
  min-width: 60px;
}

.blocklist-item button:hover {
  background: linear-gradient(135deg, #ff6b7a 0%, #ff5252 100%);
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(255, 71, 87, 0.3);
}

/* Add Word Container */
.add-word-container {
  display: flex;
  gap: 10px;
  margin-top: 15px;
}

.add-word-container input {
  flex: 1;
  padding: 12px 15px;
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid rgba(235, 99, 149, 0.3);
  border-radius: 6px;
  color: #fff;
  font-size: 14px;
  transition: border-color 0.3s;
}

.add-word-container input:focus {
  outline: none;
  border-color: rgba(235, 99, 149, 0.8);
  box-shadow: 0 0 0 2px rgba(235, 99, 149, 0.2);
}

.add-word-container input::placeholder {
  color: rgba(255, 255, 255, 0.5);
}

.add-word-container button {
  background: linear-gradient(135deg, #26de81 0%, #20bf6b 100%);
  color: white;
  border: none;
  padding: 12px 20px;
  border-radius: 6px;
  cursor: pointer;
  font-weight: bold;
  font-size: 14px;
  transition: all 0.3s;
  min-width: 80px;
}

.add-word-container button:hover {
  background: linear-gradient(135deg, #2ed573 0%, #26de81 100%);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(38, 222, 129, 0.3);
}

.add-word-container button:disabled {
  background: #666;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

/* Navbar Button Styles */
.sp.no-select.rome-menu-btn {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  white-space: nowrap;
}

/* Utility Classes */
.sp {
  -webkit-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

.no-select {
  -webkit-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

.no-select::selection, .no-select *::selection {
  background-color: transparent;
}

/* Quick Add Blocklist Styles */
.rome-quick-add {
  display: none;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 99999;
  background: linear-gradient(135deg, #2c2c2c 0%, #1a1a1a 100%);
  border: 2px solid rgba(235, 99, 149, 0.6);
  border-radius: 12px;
  padding: 20px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
  min-width: 400px;
  max-width: 600px;
  backdrop-filter: blur(3px);
}

.rome-quick-add h3 {
  color: #eb6395;
  margin: 0 0 15px 0;
  font-size: 18px;
  font-weight: bold;
  text-align: center;
}

.rome-quick-add textarea {
  width: 100%;
  height: 150px;
  background: rgba(0, 0, 0, 0.4);
  border: 1px solid rgba(235, 99, 149, 0.3);
  border-radius: 8px;
  color: #fff;
  font-size: 14px;
  font-family: monospace;
  padding: 12px;
  resize: vertical;
  outline: none;
  transition: border-color 0.3s;
}

.rome-quick-add textarea:focus {
  border-color: rgba(235, 99, 149, 0.8);
  box-shadow: 0 0 0 2px rgba(235, 99, 149, 0.2);
}

.rome-quick-add textarea::placeholder {
  color: rgba(255, 255, 255, 0.5);
}

.rome-quick-add .instructions {
  color: #ccc;
  font-size: 12px;
  margin-top: 10px;
  text-align: center;
}

.rome-quick-add .instructions kbd {
  background: rgba(235, 99, 149, 0.2);
  border: 1px solid rgba(235, 99, 149, 0.4);
  border-radius: 3px;
  padding: 2px 6px;
  font-size: 11px;
  color: #eb6395;
}


/* Video rotation wrapper and custom controls */
.rome-video-wrapper {
  position: relative;
  overflow: hidden;
  background: #000;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100px;
}

.rome-video-wrapper video {
  transform-origin: center center;
}

.rome-custom-controls {
  display: none;
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 10;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
  padding: 8px 12px 12px 12px;
  align-items: center;
  gap: 8px;
}

.rome-custom-controls.active {
  display: flex;
}

.rome-custom-controls button {
  background: none;
  border: none;
  color: #fff;
  cursor: pointer;
  font-size: 16px;
}

.rome-custom-controls .rome-progress-bar {
  flex: 1;
  box-sizing: content-box;
  height: 4px;
  background: rgba(255, 255, 255, 0.3);
  border-radius: 2px;
  cursor: pointer;
  position: relative;
  transition: height 0.15s ease-in-out, padding 0.15s ease-in-out;
  align-self: center;
  padding: 8px 0;
  background-clip: content-box;
}

.rome-video-wrapper.rome-bar-expanded .rome-progress-bar {
  height: 28px;
  padding: 0;
}

.rome-custom-controls .rome-progress-fill {
  height: 100%;
  background: #eb6395;
  border-radius: 2px;
  width: 0%;
  pointer-events: none;
}

.rome-custom-controls .rome-time-display {
  color: #fff;
  font-size: 12px;
  font-family: monospace;
  white-space: nowrap;
}
`);
    function watchAppendedPagesForDisclaimer() {
      return createDisclaimerWatcher({
        selector: ROME_CONFIG.SELECTORS.DISCLAIMER,
        doc: document,
        root: document.body,
        dismiss: () => fetch("/user/disclaimer", { method: "POST" }),
        onLog: (...args) => logger.log(...args),
        onError: (...args) => console.error("[Rome] DisclaimerBypass", ...args)
      });
    }
    function blockCollectionsModal() {
      const BLOCK_PATTERN = /\/collections\/modal\/save\//;
      const SAVE_BTN_RE = /^save-collection-/;
      function suppressDialogModal(saveBtn) {
        const dialogModal = document.querySelector("#dialogModal");
        if (!dialogModal) return;
        let suppressed = false;
        function dismiss(modalObs2) {
          if (suppressed) return;
          suppressed = true;
          modalObs2.disconnect();
          dialogModal.style.setProperty("display", "none", "important");
          dialogModal.classList.remove("show", "in");
          document.body.classList.remove("modal-open");
          document.body.style.removeProperty("padding-right");
          document.querySelectorAll(".modal-backdrop").forEach((el) => el.remove());
          setTimeout(() => {
            dialogModal.style.removeProperty("display");
          }, 500);
          if (saveBtn) {
            saveBtn.classList.add("album-unsave");
            const svg = saveBtn.querySelector("svg");
            if (svg) {
              svg.classList.remove("svg-far-fa-bookmark");
              svg.classList.add("svg-fas-fa-bookmark", "pink");
              const use = svg.querySelector("use");
              if (use) {
                use.setAttribute("xlink:href", "#fas-fa-bookmark");
                use.setAttribute("href", "#fas-fa-bookmark");
              }
            }
          }
        }
        const modalObs = new MutationObserver(() => dismiss(modalObs));
        modalObs.observe(dialogModal, {
          attributes: true,
          attributeFilter: ["style", "class"]
        });
        const backdropObs = new MutationObserver((mutations) => {
          for (const m of mutations) {
            for (const node of m.addedNodes) {
              if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains("modal-backdrop")) {
                node.remove();
              }
            }
          }
        });
        backdropObs.observe(document.body, { childList: true });
        setTimeout(() => {
          modalObs.disconnect();
          backdropObs.disconnect();
        }, 1e3);
      }
      document.addEventListener(
        "click",
        (e) => {
          const btn = e.target.closest('[data-toggle="modal"], a, button');
          if (!btn) return;
          const url = btn.dataset.remote || btn.getAttribute("href") || "";
          if (BLOCK_PATTERN.test(url) && !btn.classList.contains("album-unsave")) {
            e.preventDefault();
            e.stopPropagation();
            const method = (btn.dataset.method || "GET").toUpperCase();
            _unsafeWindow.fetch(url, { method }).then((r) => {
              if (!r.ok)
                console.error("[Rome] collection save/unsave HTTP error:", r.status, url);
            }).catch((err) => {
              console.warn("[Rome] collection save/unsave network error:", err);
            });
            return;
          }
          if (SAVE_BTN_RE.test(btn.id || "") && !btn.classList.contains("album-unsave")) {
            suppressDialogModal(btn);
          }
        },
        true
      );
      function markPreSavedButtons() {
        document.querySelectorAll('[id^="save-collection-"]').forEach((btn) => {
          const svg = btn.querySelector("svg");
          if (svg && svg.classList.contains("svg-fas-fa-bookmark")) {
            btn.classList.add("album-unsave");
          }
        });
      }
      markPreSavedButtons();
    }
    function registerAlbumPageShortcuts() {
      document.addEventListener("keydown", (e) => {
        var _a, _b;
        const tag = (_a = e.target) == null ? void 0 : _a.tagName;
        if (tag === "INPUT" || tag === "TEXTAREA" || ((_b = e.target) == null ? void 0 : _b.isContentEditable)) return;
        if (e.ctrlKey || e.metaKey || e.altKey) return;
        const romeModal = document.getElementById("romeModal");
        if (romeModal && romeModal.style.display === "block") return;
        const key = e.key.toLowerCase();
        if (key === "w") {
          const likeBtn = document.querySelector("button.album-like, button.album-unlike");
          if (!likeBtn) return;
          e.preventDefault();
          likeBtn.click();
          logger.log("AlbumShortcuts", "Like toggled (w)");
        } else if (key === "e") {
          const saveBtn = document.querySelector('[id^="save-collection-"]');
          if (!saveBtn) return;
          if (saveBtn.classList.contains("album-unsave")) {
            e.preventDefault();
            logger.log("AlbumShortcuts", "Save ignored (already saved)");
            return;
          }
          const albumId = saveBtn.id.slice("save-collection-".length);
          if (!albumId) return;
          e.preventDefault();
          try {
            const $modalContent = $("#dialogModal .modal-content");
            if ($modalContent.length === 0) {
              console.warn(
                "[AlbumShortcuts] dialogModal content not found; save aborted (albumId:",
                albumId,
                ")"
              );
              return;
            }
            $modalContent.empty();
            $modalContent.load(
              "/collections/modal/save/" + albumId,
              function(_resp, status) {
                if (status === "error") {
                  console.warn("[AlbumShortcuts] Save load error (albumId:", albumId, ")");
                  return;
                }
                saveBtn.classList.add("album-unsave");
                const svg = saveBtn.querySelector("svg");
                if (svg) {
                  svg.classList.remove("svg-far-fa-bookmark");
                  svg.classList.add("svg-fas-fa-bookmark", "pink");
                  const use = svg.querySelector("use");
                  if (use) {
                    use.setAttribute("xlink:href", "#fas-fa-bookmark");
                    use.setAttribute("href", "#fas-fa-bookmark");
                  }
                }
                logger.log("AlbumShortcuts", "Save loaded (e)", { albumId });
              }
            );
          } catch (err) {
            console.warn("[AlbumShortcuts] Save failed:", err == null ? void 0 : err.message);
          }
        }
      });
    }
    function initializeErome() {
      console.warn(
        `Rome v${GM_info.script.version} initializing for page type: ${currentPage}`
      );
      const unifiedState = new EromeUnifiedState({ config: ROME_CONFIG, currentPage, libs });
      let hybridFilter = null;
      switch (currentPage) {
        case "ALBUM": {
          try {
            const albumManager = new AlbumPageManager(unifiedState, {
              config: ROME_CONFIG,
              logger,
              multiVideo,
              createVjsObserverCallback,
              getVjsPlayer
            });
            albumManager.ensureVjsCustomControls();
          } catch (err) {
            console.error("Rome: AlbumPageManager init failed:", err);
          }
          try {
            registerAlbumPageShortcuts();
          } catch (err) {
            console.error("Rome: registerAlbumPageShortcuts failed:", err);
          }
          break;
        }
        case "EXPLORE":
          try {
            hybridFilter = new HybridContentFilter(unifiedState.persistentStore, {
              config: ROME_CONFIG,
              logger,
              storage,
              checkMemoryUsage,
              createAlbumMutationObserver,
              wrap,
              notifyStorageFailure
            });
            hybridFilter.initialize();
          } catch (error) {
            console.error("Rome: Failed to initialize HybridContentFilter:", error);
          }
          try {
            const photoToggle = new PhotoOnlyAlbumToggle(unifiedState, {
              config: ROME_CONFIG,
              logger,
              currentPage
            });
            if (hybridFilter) hybridFilter.setPhotoToggle(photoToggle);
          } catch (error) {
            console.error("Rome: Failed to initialize PhotoOnlyAlbumToggle:", error);
          }
          if (hybridFilter) {
            window.addEventListener("beforeunload", () => hybridFilter.cleanup(), { once: true });
          }
          break;
        default:
          console.warn("Rome: No specific features for this page type");
      }
      new WatchedTracker({
        config: ROME_CONFIG,
        logger,
        storage,
        wrap,
        createAlbumMutationObserver,
        getVideoId,
        notifyStorageFailure
      });
      const infyOverlayInstance = currentPage === "ALBUM" ? null : new InfyScrollOverlay({ config: ROME_CONFIG, logger, getValue: _GM_getValue, setValue: _GM_setValue });
      if (!window.Rome) {
        window.Rome = {
          removeRegexPattern: hybridFilter ? (index) => hybridFilter.removePattern(index) : () => {
          },
          setDebug: logger.setDebug,
          version: GM_info.script.version,
          performance: performance.memory ? {
            used: `${Math.round(performance.memory.usedJSHeapSize / 1024 / 1024)}MB`,
            limit: `${Math.round(performance.memory.jsHeapSizeLimit / 1024 / 1024)}MB`
          } : null
        };
        if (window.Rome.performance) {
          Object.freeze(window.Rome.performance);
        }
        Object.freeze(window.Rome);
      }
      if (infyOverlayInstance) infyOverlayInstance.start();
    }
    if (document.readyState === "loading") {
      document.addEventListener("DOMContentLoaded", watchAppendedPagesForDisclaimer);
    } else {
      watchAppendedPagesForDisclaimer();
    }
    blockCollectionsModal();
    if (document.readyState === "loading") {
      document.addEventListener("DOMContentLoaded", initializeErome);
    } else {
      setTimeout(initializeErome, 100);
    }
    _GM_registerMenuCommand("Rome Settings", () => {
      const modal = document.getElementById("romeModal");
      if (modal) {
        modal.style.display = "block";
        const debugToggle = document.getElementById("romeDebugToggle");
        if (debugToggle) debugToggle.checked = ROME_CONFIG.DEBUG;
      }
    });
  }
  main().catch((e) => {
    console.error("Rome: initialization failed", e);
  });

})();