Better Search ⭐

Clean up your search results! Highlights preferred websites and fades or completely hides sites you dislike on Google, Bing, DuckDuckGo, Brave, and Yandex.

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

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

Advertisement:

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

Advertisement:

// ==UserScript==
// @name         Better Search ⭐
// @namespace    https://github.com/quantavil/userscript/better-search
// @version      3.4
// @author       quantavil
// @description  Clean up your search results! Highlights preferred websites and fades or completely hides sites you dislike on Google, Bing, DuckDuckGo, Brave, and Yandex.
// @license      MIT
// @homepage     https://github.com/quantavil/userscript
// @homepageURL  https://github.com/quantavil/userscript
// @include      /^https?:\/\/(?:www\.)?google\.[a-z]{2,6}(?:\.[a-z]{2,3})?\/search/
// @include      *://www.bing.com/search*
// @include      *://*.bing.com/search*
// @include      *://duckduckgo.com/*
// @include      *://*.duckduckgo.com/*
// @include      *://search.brave.com/search*
// @include      /^https?:\/\/(?:[a-z]+\.)?yandex\.[a-z]{2,}\/search/
// @include      /^https?:\/\/(?:[a-z]+\.)?ya\.ru\/search/
// @connect      gist.githubusercontent.com
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_setClipboard
// @grant        GM_setValue
// @grant        GM_xmlhttpRequest
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';

  const SVF_CONFIG = {
    /** Prefix for all localStorage / GM storage keys */
    STORAGE_PREFIX: "svf_",
    /** data-attribute written to result items for identification */
    ITEM_ATTR: "data-svf-id",
    /** How long (ms) to debounce the scanner after DOM mutations */
    SCAN_DEBOUNCE_MS: 120,
    /** How long (ms) to debounce settings saves */
    SAVE_DEBOUNCE_MS: 800,
    /** Default liked domains prepopulated on first install */
    DEFAULT_LIKED: [],
    /** Default disliked domains prepopulated on first install */
    DEFAULT_DISLIKED: []
  };
  function gmGet(key, fallback) {
    try {
      if (typeof GM_getValue === "function") {
        const v2 = GM_getValue(SVF_CONFIG.STORAGE_PREFIX + key, void 0);
        return v2 !== void 0 && v2 !== null ? v2 : fallback;
      }
      const raw = localStorage.getItem(SVF_CONFIG.STORAGE_PREFIX + key);
      return raw ? JSON.parse(raw) : fallback;
    } catch (err) {
      console.error("[SVF] Failed to read from storage:", err);
      return fallback;
    }
  }
  function gmSet(key, value) {
    try {
      if (typeof GM_setValue === "function") {
        GM_setValue(SVF_CONFIG.STORAGE_PREFIX + key, value);
        return;
      }
      localStorage.setItem(SVF_CONFIG.STORAGE_PREFIX + key, JSON.stringify(value));
    } catch (err) {
      console.error("[SVF] Failed to write to storage:", err);
    }
  }
  class Store {
    constructor() {
      this._saveTimers = {};
      this._listeners = /* @__PURE__ */ new Set();
      this._settings = {
        liked: gmGet("liked", SVF_CONFIG.DEFAULT_LIKED),
        disliked: gmGet("disliked", SVF_CONFIG.DEFAULT_DISLIKED),
        dislikeMode: gmGet("dislikeMode", "fade"),
        showTrigger: gmGet("showTrigger", true),
        accent: gmGet("accent", "emerald")
      };
      this._pagehideHandler = () => {
        for (const key of Object.keys(this._saveTimers)) {
          const timer = this._saveTimers[key];
          if (timer) {
            clearTimeout(timer);
            gmSet(key, this._settings[key]);
          }
        }
      };
      window.addEventListener("pagehide", this._pagehideHandler);
    }
    // ── Accessors ─────────────────────────────────────────────────────────────
    get liked() {
      return this._settings.liked;
    }
    get disliked() {
      return this._settings.disliked;
    }
    get dislikeMode() {
      return this._settings.dislikeMode;
    }
    get showTrigger() {
      return this._settings.showTrigger;
    }
    get accent() {
      return this._settings.accent;
    }
    /** Returns 'liked' | 'disliked' | 'normal' for a given bare domain. Supports wildcards. */
    matchDomain(domain) {
      const d2 = domain.toLowerCase();
      const hit = (list) => list.some((rule) => {
        if (rule.startsWith("*.")) {
          const r2 = rule.slice(2);
          return d2 === r2 || d2.endsWith("." + r2);
        }
        return d2 === rule;
      });
      if (hit(this._settings.liked)) return "liked";
      if (hit(this._settings.disliked)) return "disliked";
      return "normal";
    }
    // ── Mutations ─────────────────────────────────────────────────────────────
    addDomain(list, domain) {
      const d2 = normalizeDomain(domain);
      const otherList = list === "liked" ? "disliked" : "liked";
      if (!d2 || this._settings[list].includes(d2)) return;
      this._settings[otherList] = this._settings[otherList].filter((x2) => x2 !== d2);
      this._settings[list] = [...this._settings[list], d2].sort();
      this._persist("liked");
      this._persist("disliked");
      this._notify();
    }
    removeDomain(list, domain) {
      const d2 = normalizeDomain(domain);
      this._settings[list] = this._settings[list].filter((x2) => x2 !== d2);
      this._persist(list);
      this._notify();
    }
    setDomains(list, domains) {
      const normalized = domains.map((x2) => normalizeDomain(x2)).filter(Boolean);
      const otherList = list === "liked" ? "disliked" : "liked";
      this._settings[list] = [...new Set(normalized)];
      this._settings[otherList] = this._settings[otherList].filter((x2) => !this._settings[list].includes(x2));
      this._persist("liked");
      this._persist("disliked");
      this._notify();
    }
    setDislikeMode(mode) {
      this._settings.dislikeMode = mode;
      this._persist("dislikeMode");
      this._notify();
    }
    setShowTrigger(show) {
      this._settings.showTrigger = show;
      this._persist("showTrigger");
      this._notify();
    }
    setAccent(accent) {
      this._settings.accent = accent;
      this._persist("accent");
      this._notify();
    }
    /** Export current domain lists as a plain object. */
    exportDomains() {
      return {
        liked: [...this._settings.liked],
        disliked: [...this._settings.disliked]
      };
    }
    /** Merge imported domains into current lists (additive, deduplicated). */
    importDomains(data) {
      let added = 0;
      if (Array.isArray(data.liked)) {
        for (const raw of data.liked) {
          const d2 = normalizeDomain(raw);
          if (d2 && !this._settings.liked.includes(d2)) {
            this._settings.disliked = this._settings.disliked.filter((x2) => x2 !== d2);
            this._settings.liked = [...this._settings.liked, d2];
            added++;
          }
        }
      }
      if (Array.isArray(data.disliked)) {
        for (const raw of data.disliked) {
          const d2 = normalizeDomain(raw);
          if (d2 && !this._settings.disliked.includes(d2)) {
            this._settings.liked = this._settings.liked.filter((x2) => x2 !== d2);
            this._settings.disliked = [...this._settings.disliked, d2];
            added++;
          }
        }
      }
      if (added > 0) {
        this._persist("liked");
        this._persist("disliked");
        this._notify();
      }
      return added;
    }
    // ── Subscriptions ─────────────────────────────────────────────────────────
    /** Subscribe to any settings change. Returns an unsubscribe fn. */
    subscribe(fn) {
      this._listeners.add(fn);
      return () => this._listeners.delete(fn);
    }
    destroy() {
      window.removeEventListener("pagehide", this._pagehideHandler);
      for (const key of Object.keys(this._saveTimers)) {
        const timer = this._saveTimers[key];
        if (timer) {
          clearTimeout(timer);
          gmSet(key, this._settings[key]);
        }
      }
      this._saveTimers = {};
      this._listeners.clear();
    }
    // ── Internal ──────────────────────────────────────────────────────────────
    _notify() {
      this._listeners.forEach((fn) => {
        try {
          fn();
        } catch (err) {
          console.error("[SVF] Subscriber error during notify:", err);
        }
      });
    }
    _persist(key) {
      clearTimeout(this._saveTimers[key]);
      this._saveTimers[key] = setTimeout(() => {
        gmSet(key, this._settings[key]);
      }, SVF_CONFIG.SAVE_DEBOUNCE_MS);
    }
  }
  function normalizeDomain(input) {
    let s2 = input.trim().toLowerCase();
    if (!s2) return "";
    const hasWildcard = s2.startsWith("*.");
    if (hasWildcard) {
      s2 = s2.slice(2);
    }
    try {
      if (!s2.includes("://")) {
        s2 = "http://" + s2;
      }
      s2 = new URL(s2).hostname;
    } catch {
      return "";
    }
    s2 = s2.startsWith("www.") ? s2.slice(4) : s2;
    return hasWildcard ? "*." + s2 : s2;
  }
  const ACCENT_RAW = {
    emerald: { hex: "#10b981", rgb: "16, 185, 129" },
    indigo: { hex: "#6366f1", rgb: "99, 102, 241" },
    blue: { hex: "#3b82f6", rgb: "59, 130, 246" },
    teal: { hex: "#06b6d4", rgb: "6, 182, 212" },
    rose: { hex: "#f43f5e", rgb: "244, 63, 94" },
    amber: { hex: "#f59e0b", rgb: "245, 158, 11" }
  };
  const ACCENT_COLORS = Object.keys(ACCENT_RAW).reduce((acc, key) => {
    const { hex, rgb } = ACCENT_RAW[key];
    acc[key] = {
      primary: hex,
      glow: `rgba(${rgb}, 0.15)`,
      bg: `rgba(${rgb}, 0.04)`,
      bgHover: `rgba(${rgb}, 0.08)`,
      border: `rgba(${rgb}, 0.25)`,
      statusBg: `rgba(${rgb}, 0.1)`,
      dotGlow: `rgba(${rgb}, 0.6)`
    };
    return acc;
  }, {});
  const ACCENT_VARS = [
    "--svf-primary",
    "--svf-primary-glow",
    "--svf-primary-bg",
    "--svf-primary-bg-hover",
    "--svf-primary-border",
    "--svf-primary-status-bg"
  ];
  function applyAccentVariables(el, accent) {
    const current = ACCENT_COLORS[accent] || ACCENT_COLORS.emerald;
    el.style.setProperty("--svf-primary", current.primary);
    el.style.setProperty("--svf-primary-glow", current.glow);
    el.style.setProperty("--svf-primary-bg", current.bg);
    el.style.setProperty("--svf-primary-bg-hover", current.bgHover);
    el.style.setProperty("--svf-primary-border", current.border);
    el.style.setProperty("--svf-primary-status-bg", current.statusBg);
  }
  function removeAccentVariables(el) {
    for (const key of ACCENT_VARS) {
      el.style.removeProperty(key);
    }
  }
  const BASE_CSS = '@layer svf-filters{[data-svf-id].svf-liked{position:relative!important;border-left:4px solid var(--svf-primary, #10b981)!important;border-radius:0 6px 6px 0!important;padding-left:12px!important;background:var(--svf-primary-bg, rgba(16, 185, 129, .04))!important;transition:border-color .2s,background .2s!important}[data-svf-id].svf-liked:after{content:"✦";position:absolute;top:6px;right:10px;font-size:10px;font-weight:700;letter-spacing:.4px;color:var(--svf-primary, #10b981);background:var(--svf-primary-glow, rgba(16, 185, 129, .12));padding:2px 8px;border-radius:10px;pointer-events:none;z-index:1;font-family:system-ui,sans-serif}[data-svf-id].svf-fade{opacity:.14!important;filter:grayscale(.8)!important;transition:opacity .25s,filter .25s!important}[data-svf-id].svf-fade:hover{opacity:.9!important;filter:none!important}[data-svf-id].svf-hide{position:relative!important;max-height:36px!important;overflow:hidden!important;opacity:1!important;cursor:pointer!important;border:1px dashed rgba(239,68,68,.25)!important;border-radius:6px!important;background:#ef444408!important;transition:max-height .3s ease,background .2s!important;margin:4px 0!important}[data-svf-id].svf-hide:before{content:"⊘  " attr(data-svf-id-domain) " — blocked (click to reveal)";display:block!important;padding:9px 14px!important;font-size:11.5px!important;font-weight:500!important;color:#ef4444b3!important;font-family:system-ui,sans-serif!important;white-space:nowrap!important;overflow:hidden!important;text-overflow:ellipsis!important;pointer-events:none!important}[data-svf-id].svf-hide>*{display:none!important}[data-svf-id].svf-hide:hover{background:#ef44440f!important}[data-svf-id].svf-revealed{max-height:none!important;cursor:auto!important;border:none!important;background:transparent!important}[data-svf-id].svf-revealed:before{display:none!important}[data-svf-id].svf-revealed>*{pointer-events:auto!important;user-select:auto!important}}';
  class FilterStyleSheet {
    constructor(store) {
      this._store = store;
      this._el = document.createElement("style");
      this._el.id = "svf-styles";
      this._el.textContent = BASE_CSS;
      document.head.appendChild(this._el);
      this._updateVariables();
      this._unsub = this._store.subscribe(() => this._updateVariables());
    }
    _updateVariables() {
      const accent = this._store.accent;
      applyAccentVariables(document.documentElement, accent);
    }
    destroy() {
      this._unsub();
      this._el.remove();
      removeAccentVariables(document.documentElement);
    }
  }
  const ATTR$1 = SVF_CONFIG.ITEM_ATTR;
  const DOMAIN_ATTR$1 = `${ATTR$1}-domain`;
  let _counter = 0;
  const nextId = () => `${++_counter}`;
  class Scanner {
    constructor(store, engine, abortSignal) {
      this._container = null;
      this._observer = null;
      this._debounce = null;
      this._revealedElements = /* @__PURE__ */ new WeakSet();
      this._store = store;
      this._engine = engine;
      this._abortSignal = abortSignal;
    }
    /** Find the container and start observing. Returns false if not found yet. */
    attach() {
      const container = document.querySelector(this._engine.containerSelector);
      if (!container) return false;
      if (this._container === container) return true;
      this._disconnect();
      this._container = container;
      this._observer = new MutationObserver((mutations) => {
        let shouldScan = false;
        for (const m2 of mutations) {
          if (m2.addedNodes.length > 0) {
            shouldScan = true;
            break;
          }
          if (m2.type === "attributes" && m2.attributeName === "href") {
            shouldScan = true;
            break;
          }
        }
        if (!shouldScan) return;
        this._scheduleScan();
      });
      this._observer.observe(container, {
        childList: true,
        subtree: true,
        attributes: true,
        attributeFilter: ["href"]
      });
      this.scanAll();
      return true;
    }
    /** Full re-scan of all items. Called on init and on settings change. */
    scanAll() {
      if (!this._container) return;
      const items = this._container.querySelectorAll(this._engine.itemSelector);
      items.forEach((item) => this._processItem(item));
    }
    /** Re-apply classes to all already-scanned items (e.g. after settings change). */
    reapply() {
      if (!this._container) return;
      const items = this._container.querySelectorAll(`[${ATTR$1}]`);
      items.forEach((item) => {
        const domain = item.getAttribute(DOMAIN_ATTR$1) ?? "";
        this._applyClass(item, domain);
      });
    }
    destroy() {
      this._disconnect();
      if (this._debounce) clearTimeout(this._debounce);
    }
    // ── Private ───────────────────────────────────────────────────────────────
    _scheduleScan() {
      if (this._debounce) clearTimeout(this._debounce);
      this._debounce = setTimeout(() => this.scanAll(), SVF_CONFIG.SCAN_DEBOUNCE_MS);
    }
    _processItem(item) {
      if (!(item instanceof HTMLElement)) return;
      if (item.parentElement && item.parentElement.closest(this._engine.itemSelector)) {
        return;
      }
      if (item.hasAttribute(ATTR$1)) {
        const domain2 = item.getAttribute(DOMAIN_ATTR$1) ?? "";
        this._applyClass(item, domain2);
        return;
      }
      const href = this._engine.extractUrl(item);
      if (!href) return;
      const domain = normalizeDomain(href);
      if (!domain) return;
      const id = nextId();
      item.setAttribute(ATTR$1, id);
      item.setAttribute(DOMAIN_ATTR$1, domain);
      this._applyClass(item, domain);
      this._bindReveal(item);
    }
    _applyClass(item, domain) {
      item.getAttribute(ATTR$1) ?? "";
      const match = this._store.matchDomain(domain);
      const mode = this._store.dislikeMode;
      item.classList.remove(
        `svf-liked`,
        `svf-fade`,
        `svf-hide`,
        `svf-revealed`
      );
      if (match === "liked") {
        item.classList.add(`svf-liked`);
      } else if (match === "disliked") {
        if (mode === "hide") {
          if (this._revealedElements.has(item)) {
            item.classList.add(`svf-revealed`);
          } else {
            item.classList.add(`svf-hide`);
          }
        } else {
          item.classList.add(`svf-fade`);
        }
      }
    }
    _bindReveal(item) {
      item.addEventListener("click", (e2) => {
        if (!item.classList.contains(`svf-hide`)) return;
        e2.preventDefault();
        e2.stopPropagation();
        this._revealedElements.add(item);
        item.classList.remove(`svf-hide`);
        item.classList.add(`svf-revealed`);
      }, { capture: true, signal: this._abortSignal });
    }
    _disconnect() {
      this._observer?.disconnect();
      this._observer = null;
      this._container = null;
    }
  }
  const t$3 = globalThis, e$6 = t$3.ShadowRoot && (void 0 === t$3.ShadyCSS || t$3.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype, s$3 = /* @__PURE__ */ Symbol(), o$7 = /* @__PURE__ */ new WeakMap();
  let n$5 = class n {
    constructor(t2, e2, o2) {
      if (this._$cssResult$ = true, o2 !== s$3) throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");
      this.cssText = t2, this.t = e2;
    }
    get styleSheet() {
      let t2 = this.o;
      const s2 = this.t;
      if (e$6 && void 0 === t2) {
        const e2 = void 0 !== s2 && 1 === s2.length;
        e2 && (t2 = o$7.get(s2)), void 0 === t2 && ((this.o = t2 = new CSSStyleSheet()).replaceSync(this.cssText), e2 && o$7.set(s2, t2));
      }
      return t2;
    }
    toString() {
      return this.cssText;
    }
  };
  const r$6 = (t2) => new n$5("string" == typeof t2 ? t2 : t2 + "", void 0, s$3), S$1 = (s2, o2) => {
    if (e$6) s2.adoptedStyleSheets = o2.map((t2) => t2 instanceof CSSStyleSheet ? t2 : t2.styleSheet);
    else for (const e2 of o2) {
      const o3 = document.createElement("style"), n3 = t$3.litNonce;
      void 0 !== n3 && o3.setAttribute("nonce", n3), o3.textContent = e2.cssText, s2.appendChild(o3);
    }
  }, c$3 = e$6 ? (t2) => t2 : (t2) => t2 instanceof CSSStyleSheet ? ((t3) => {
    let e2 = "";
    for (const s2 of t3.cssRules) e2 += s2.cssText;
    return r$6(e2);
  })(t2) : t2;
  const { is: i$3, defineProperty: e$5, getOwnPropertyDescriptor: h$2, getOwnPropertyNames: r$5, getOwnPropertySymbols: o$6, getPrototypeOf: n$4 } = Object, a$1 = globalThis, c$2 = a$1.trustedTypes, l$1 = c$2 ? c$2.emptyScript : "", p$1 = a$1.reactiveElementPolyfillSupport, d$1 = (t2, s2) => t2, u$1 = { toAttribute(t2, s2) {
    switch (s2) {
      case Boolean:
        t2 = t2 ? l$1 : null;
        break;
      case Object:
      case Array:
        t2 = null == t2 ? t2 : JSON.stringify(t2);
    }
    return t2;
  }, fromAttribute(t2, s2) {
    let i3 = t2;
    switch (s2) {
      case Boolean:
        i3 = null !== t2;
        break;
      case Number:
        i3 = null === t2 ? null : Number(t2);
        break;
      case Object:
      case Array:
        try {
          i3 = JSON.parse(t2);
        } catch (t3) {
          i3 = null;
        }
    }
    return i3;
  } }, f$2 = (t2, s2) => !i$3(t2, s2), b$1 = { attribute: true, type: String, converter: u$1, reflect: false, useDefault: false, hasChanged: f$2 };
  Symbol.metadata ??= /* @__PURE__ */ Symbol("metadata"), a$1.litPropertyMetadata ??= /* @__PURE__ */ new WeakMap();
  let y$1 = class y extends HTMLElement {
    static addInitializer(t2) {
      this._$Ei(), (this.l ??= []).push(t2);
    }
    static get observedAttributes() {
      return this.finalize(), this._$Eh && [...this._$Eh.keys()];
    }
    static createProperty(t2, s2 = b$1) {
      if (s2.state && (s2.attribute = false), this._$Ei(), this.prototype.hasOwnProperty(t2) && ((s2 = Object.create(s2)).wrapped = true), this.elementProperties.set(t2, s2), !s2.noAccessor) {
        const i3 = /* @__PURE__ */ Symbol(), h2 = this.getPropertyDescriptor(t2, i3, s2);
        void 0 !== h2 && e$5(this.prototype, t2, h2);
      }
    }
    static getPropertyDescriptor(t2, s2, i3) {
      const { get: e2, set: r2 } = h$2(this.prototype, t2) ?? { get() {
        return this[s2];
      }, set(t3) {
        this[s2] = t3;
      } };
      return { get: e2, set(s3) {
        const h2 = e2?.call(this);
        r2?.call(this, s3), this.requestUpdate(t2, h2, i3);
      }, configurable: true, enumerable: true };
    }
    static getPropertyOptions(t2) {
      return this.elementProperties.get(t2) ?? b$1;
    }
    static _$Ei() {
      if (this.hasOwnProperty(d$1("elementProperties"))) return;
      const t2 = n$4(this);
      t2.finalize(), void 0 !== t2.l && (this.l = [...t2.l]), this.elementProperties = new Map(t2.elementProperties);
    }
    static finalize() {
      if (this.hasOwnProperty(d$1("finalized"))) return;
      if (this.finalized = true, this._$Ei(), this.hasOwnProperty(d$1("properties"))) {
        const t3 = this.properties, s2 = [...r$5(t3), ...o$6(t3)];
        for (const i3 of s2) this.createProperty(i3, t3[i3]);
      }
      const t2 = this[Symbol.metadata];
      if (null !== t2) {
        const s2 = litPropertyMetadata.get(t2);
        if (void 0 !== s2) for (const [t3, i3] of s2) this.elementProperties.set(t3, i3);
      }
      this._$Eh = /* @__PURE__ */ new Map();
      for (const [t3, s2] of this.elementProperties) {
        const i3 = this._$Eu(t3, s2);
        void 0 !== i3 && this._$Eh.set(i3, t3);
      }
      this.elementStyles = this.finalizeStyles(this.styles);
    }
    static finalizeStyles(s2) {
      const i3 = [];
      if (Array.isArray(s2)) {
        const e2 = new Set(s2.flat(1 / 0).reverse());
        for (const s3 of e2) i3.unshift(c$3(s3));
      } else void 0 !== s2 && i3.push(c$3(s2));
      return i3;
    }
    static _$Eu(t2, s2) {
      const i3 = s2.attribute;
      return false === i3 ? void 0 : "string" == typeof i3 ? i3 : "string" == typeof t2 ? t2.toLowerCase() : void 0;
    }
    constructor() {
      super(), this._$Ep = void 0, this.isUpdatePending = false, this.hasUpdated = false, this._$Em = null, this._$Ev();
    }
    _$Ev() {
      this._$ES = new Promise((t2) => this.enableUpdating = t2), this._$AL = /* @__PURE__ */ new Map(), this._$E_(), this.requestUpdate(), this.constructor.l?.forEach((t2) => t2(this));
    }
    addController(t2) {
      (this._$EO ??= /* @__PURE__ */ new Set()).add(t2), void 0 !== this.renderRoot && this.isConnected && t2.hostConnected?.();
    }
    removeController(t2) {
      this._$EO?.delete(t2);
    }
    _$E_() {
      const t2 = /* @__PURE__ */ new Map(), s2 = this.constructor.elementProperties;
      for (const i3 of s2.keys()) this.hasOwnProperty(i3) && (t2.set(i3, this[i3]), delete this[i3]);
      t2.size > 0 && (this._$Ep = t2);
    }
    createRenderRoot() {
      const t2 = this.shadowRoot ?? this.attachShadow(this.constructor.shadowRootOptions);
      return S$1(t2, this.constructor.elementStyles), t2;
    }
    connectedCallback() {
      this.renderRoot ??= this.createRenderRoot(), this.enableUpdating(true), this._$EO?.forEach((t2) => t2.hostConnected?.());
    }
    enableUpdating(t2) {
    }
    disconnectedCallback() {
      this._$EO?.forEach((t2) => t2.hostDisconnected?.());
    }
    attributeChangedCallback(t2, s2, i3) {
      this._$AK(t2, i3);
    }
    _$ET(t2, s2) {
      const i3 = this.constructor.elementProperties.get(t2), e2 = this.constructor._$Eu(t2, i3);
      if (void 0 !== e2 && true === i3.reflect) {
        const h2 = (void 0 !== i3.converter?.toAttribute ? i3.converter : u$1).toAttribute(s2, i3.type);
        this._$Em = t2, null == h2 ? this.removeAttribute(e2) : this.setAttribute(e2, h2), this._$Em = null;
      }
    }
    _$AK(t2, s2) {
      const i3 = this.constructor, e2 = i3._$Eh.get(t2);
      if (void 0 !== e2 && this._$Em !== e2) {
        const t3 = i3.getPropertyOptions(e2), h2 = "function" == typeof t3.converter ? { fromAttribute: t3.converter } : void 0 !== t3.converter?.fromAttribute ? t3.converter : u$1;
        this._$Em = e2;
        const r2 = h2.fromAttribute(s2, t3.type);
        this[e2] = r2 ?? this._$Ej?.get(e2) ?? r2, this._$Em = null;
      }
    }
    requestUpdate(t2, s2, i3, e2 = false, h2) {
      if (void 0 !== t2) {
        const r2 = this.constructor;
        if (false === e2 && (h2 = this[t2]), i3 ??= r2.getPropertyOptions(t2), !((i3.hasChanged ?? f$2)(h2, s2) || i3.useDefault && i3.reflect && h2 === this._$Ej?.get(t2) && !this.hasAttribute(r2._$Eu(t2, i3)))) return;
        this.C(t2, s2, i3);
      }
      false === this.isUpdatePending && (this._$ES = this._$EP());
    }
    C(t2, s2, { useDefault: i3, reflect: e2, wrapped: h2 }, r2) {
      i3 && !(this._$Ej ??= /* @__PURE__ */ new Map()).has(t2) && (this._$Ej.set(t2, r2 ?? s2 ?? this[t2]), true !== h2 || void 0 !== r2) || (this._$AL.has(t2) || (this.hasUpdated || i3 || (s2 = void 0), this._$AL.set(t2, s2)), true === e2 && this._$Em !== t2 && (this._$Eq ??= /* @__PURE__ */ new Set()).add(t2));
    }
    async _$EP() {
      this.isUpdatePending = true;
      try {
        await this._$ES;
      } catch (t3) {
        Promise.reject(t3);
      }
      const t2 = this.scheduleUpdate();
      return null != t2 && await t2, !this.isUpdatePending;
    }
    scheduleUpdate() {
      return this.performUpdate();
    }
    performUpdate() {
      if (!this.isUpdatePending) return;
      if (!this.hasUpdated) {
        if (this.renderRoot ??= this.createRenderRoot(), this._$Ep) {
          for (const [t4, s3] of this._$Ep) this[t4] = s3;
          this._$Ep = void 0;
        }
        const t3 = this.constructor.elementProperties;
        if (t3.size > 0) for (const [s3, i3] of t3) {
          const { wrapped: t4 } = i3, e2 = this[s3];
          true !== t4 || this._$AL.has(s3) || void 0 === e2 || this.C(s3, void 0, i3, e2);
        }
      }
      let t2 = false;
      const s2 = this._$AL;
      try {
        t2 = this.shouldUpdate(s2), t2 ? (this.willUpdate(s2), this._$EO?.forEach((t3) => t3.hostUpdate?.()), this.update(s2)) : this._$EM();
      } catch (s3) {
        throw t2 = false, this._$EM(), s3;
      }
      t2 && this._$AE(s2);
    }
    willUpdate(t2) {
    }
    _$AE(t2) {
      this._$EO?.forEach((t3) => t3.hostUpdated?.()), this.hasUpdated || (this.hasUpdated = true, this.firstUpdated(t2)), this.updated(t2);
    }
    _$EM() {
      this._$AL = /* @__PURE__ */ new Map(), this.isUpdatePending = false;
    }
    get updateComplete() {
      return this.getUpdateComplete();
    }
    getUpdateComplete() {
      return this._$ES;
    }
    shouldUpdate(t2) {
      return true;
    }
    update(t2) {
      this._$Eq &&= this._$Eq.forEach((t3) => this._$ET(t3, this[t3])), this._$EM();
    }
    updated(t2) {
    }
    firstUpdated(t2) {
    }
  };
  y$1.elementStyles = [], y$1.shadowRootOptions = { mode: "open" }, y$1[d$1("elementProperties")] = /* @__PURE__ */ new Map(), y$1[d$1("finalized")] = /* @__PURE__ */ new Map(), p$1?.({ ReactiveElement: y$1 }), (a$1.reactiveElementVersions ??= []).push("2.1.2");
  const t$2 = globalThis, i$2 = (t2) => t2, s$2 = t$2.trustedTypes, e$4 = s$2 ? s$2.createPolicy("lit-html", { createHTML: (t2) => t2 }) : void 0, h$1 = "$lit$", o$5 = `lit$${Math.random().toFixed(9).slice(2)}$`, n$3 = "?" + o$5, r$4 = `<${n$3}>`, l = document, c$1 = () => l.createComment(""), a = (t2) => null === t2 || "object" != typeof t2 && "function" != typeof t2, u = Array.isArray, d = (t2) => u(t2) || "function" == typeof t2?.[Symbol.iterator], f$1 = "[ 	\n\f\r]", v = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g, _ = /-->/g, m = />/g, p = RegExp(`>|${f$1}(?:([^\\s"'>=/]+)(${f$1}*=${f$1}*(?:[^ 	
\f\r"'\`<>=]|("|')|))|$)`, "g"), g = /'/g, $ = /"/g, y2 = /^(?:script|style|textarea|title)$/i, x = (t2) => (i3, ...s2) => ({ _$litType$: t2, strings: i3, values: s2 }), b = x(1), E = /* @__PURE__ */ Symbol.for("lit-noChange"), A = /* @__PURE__ */ Symbol.for("lit-nothing"), C = /* @__PURE__ */ new WeakMap(), P = l.createTreeWalker(l, 129);
  function V(t2, i3) {
    if (!u(t2) || !t2.hasOwnProperty("raw")) throw Error("invalid template strings array");
    return void 0 !== e$4 ? e$4.createHTML(i3) : i3;
  }
  const N = (t2, i3) => {
    const s2 = t2.length - 1, e2 = [];
    let n3, l2 = 2 === i3 ? "<svg>" : 3 === i3 ? "<math>" : "", c2 = v;
    for (let i4 = 0; i4 < s2; i4++) {
      const s3 = t2[i4];
      let a2, u2, d2 = -1, f2 = 0;
      for (; f2 < s3.length && (c2.lastIndex = f2, u2 = c2.exec(s3), null !== u2); ) f2 = c2.lastIndex, c2 === v ? "!--" === u2[1] ? c2 = _ : void 0 !== u2[1] ? c2 = m : void 0 !== u2[2] ? (y2.test(u2[2]) && (n3 = RegExp("</" + u2[2], "g")), c2 = p) : void 0 !== u2[3] && (c2 = p) : c2 === p ? ">" === u2[0] ? (c2 = n3 ?? v, d2 = -1) : void 0 === u2[1] ? d2 = -2 : (d2 = c2.lastIndex - u2[2].length, a2 = u2[1], c2 = void 0 === u2[3] ? p : '"' === u2[3] ? $ : g) : c2 === $ || c2 === g ? c2 = p : c2 === _ || c2 === m ? c2 = v : (c2 = p, n3 = void 0);
      const x2 = c2 === p && t2[i4 + 1].startsWith("/>") ? " " : "";
      l2 += c2 === v ? s3 + r$4 : d2 >= 0 ? (e2.push(a2), s3.slice(0, d2) + h$1 + s3.slice(d2) + o$5 + x2) : s3 + o$5 + (-2 === d2 ? i4 : x2);
    }
    return [V(t2, l2 + (t2[s2] || "<?>") + (2 === i3 ? "</svg>" : 3 === i3 ? "</math>" : "")), e2];
  };
  class S {
    constructor({ strings: t2, _$litType$: i3 }, e2) {
      let r2;
      this.parts = [];
      let l2 = 0, a2 = 0;
      const u2 = t2.length - 1, d2 = this.parts, [f2, v2] = N(t2, i3);
      if (this.el = S.createElement(f2, e2), P.currentNode = this.el.content, 2 === i3 || 3 === i3) {
        const t3 = this.el.content.firstChild;
        t3.replaceWith(...t3.childNodes);
      }
      for (; null !== (r2 = P.nextNode()) && d2.length < u2; ) {
        if (1 === r2.nodeType) {
          if (r2.hasAttributes()) for (const t3 of r2.getAttributeNames()) if (t3.endsWith(h$1)) {
            const i4 = v2[a2++], s2 = r2.getAttribute(t3).split(o$5), e3 = /([.?@])?(.*)/.exec(i4);
            d2.push({ type: 1, index: l2, name: e3[2], strings: s2, ctor: "." === e3[1] ? I : "?" === e3[1] ? L : "@" === e3[1] ? z : H }), r2.removeAttribute(t3);
          } else t3.startsWith(o$5) && (d2.push({ type: 6, index: l2 }), r2.removeAttribute(t3));
          if (y2.test(r2.tagName)) {
            const t3 = r2.textContent.split(o$5), i4 = t3.length - 1;
            if (i4 > 0) {
              r2.textContent = s$2 ? s$2.emptyScript : "";
              for (let s2 = 0; s2 < i4; s2++) r2.append(t3[s2], c$1()), P.nextNode(), d2.push({ type: 2, index: ++l2 });
              r2.append(t3[i4], c$1());
            }
          }
        } else if (8 === r2.nodeType) if (r2.data === n$3) d2.push({ type: 2, index: l2 });
        else {
          let t3 = -1;
          for (; -1 !== (t3 = r2.data.indexOf(o$5, t3 + 1)); ) d2.push({ type: 7, index: l2 }), t3 += o$5.length - 1;
        }
        l2++;
      }
    }
    static createElement(t2, i3) {
      const s2 = l.createElement("template");
      return s2.innerHTML = t2, s2;
    }
  }
  function M(t2, i3, s2 = t2, e2) {
    if (i3 === E) return i3;
    let h2 = void 0 !== e2 ? s2._$Co?.[e2] : s2._$Cl;
    const o2 = a(i3) ? void 0 : i3._$litDirective$;
    return h2?.constructor !== o2 && (h2?._$AO?.(false), void 0 === o2 ? h2 = void 0 : (h2 = new o2(t2), h2._$AT(t2, s2, e2)), void 0 !== e2 ? (s2._$Co ??= [])[e2] = h2 : s2._$Cl = h2), void 0 !== h2 && (i3 = M(t2, h2._$AS(t2, i3.values), h2, e2)), i3;
  }
  class R {
    constructor(t2, i3) {
      this._$AV = [], this._$AN = void 0, this._$AD = t2, this._$AM = i3;
    }
    get parentNode() {
      return this._$AM.parentNode;
    }
    get _$AU() {
      return this._$AM._$AU;
    }
    u(t2) {
      const { el: { content: i3 }, parts: s2 } = this._$AD, e2 = (t2?.creationScope ?? l).importNode(i3, true);
      P.currentNode = e2;
      let h2 = P.nextNode(), o2 = 0, n3 = 0, r2 = s2[0];
      for (; void 0 !== r2; ) {
        if (o2 === r2.index) {
          let i4;
          2 === r2.type ? i4 = new k(h2, h2.nextSibling, this, t2) : 1 === r2.type ? i4 = new r2.ctor(h2, r2.name, r2.strings, this, t2) : 6 === r2.type && (i4 = new Z(h2, this, t2)), this._$AV.push(i4), r2 = s2[++n3];
        }
        o2 !== r2?.index && (h2 = P.nextNode(), o2++);
      }
      return P.currentNode = l, e2;
    }
    p(t2) {
      let i3 = 0;
      for (const s2 of this._$AV) void 0 !== s2 && (void 0 !== s2.strings ? (s2._$AI(t2, s2, i3), i3 += s2.strings.length - 2) : s2._$AI(t2[i3])), i3++;
    }
  }
  class k {
    get _$AU() {
      return this._$AM?._$AU ?? this._$Cv;
    }
    constructor(t2, i3, s2, e2) {
      this.type = 2, this._$AH = A, this._$AN = void 0, this._$AA = t2, this._$AB = i3, this._$AM = s2, this.options = e2, this._$Cv = e2?.isConnected ?? true;
    }
    get parentNode() {
      let t2 = this._$AA.parentNode;
      const i3 = this._$AM;
      return void 0 !== i3 && 11 === t2?.nodeType && (t2 = i3.parentNode), t2;
    }
    get startNode() {
      return this._$AA;
    }
    get endNode() {
      return this._$AB;
    }
    _$AI(t2, i3 = this) {
      t2 = M(this, t2, i3), a(t2) ? t2 === A || null == t2 || "" === t2 ? (this._$AH !== A && this._$AR(), this._$AH = A) : t2 !== this._$AH && t2 !== E && this._(t2) : void 0 !== t2._$litType$ ? this.$(t2) : void 0 !== t2.nodeType ? this.T(t2) : d(t2) ? this.k(t2) : this._(t2);
    }
    O(t2) {
      return this._$AA.parentNode.insertBefore(t2, this._$AB);
    }
    T(t2) {
      this._$AH !== t2 && (this._$AR(), this._$AH = this.O(t2));
    }
    _(t2) {
      this._$AH !== A && a(this._$AH) ? this._$AA.nextSibling.data = t2 : this.T(l.createTextNode(t2)), this._$AH = t2;
    }
    $(t2) {
      const { values: i3, _$litType$: s2 } = t2, e2 = "number" == typeof s2 ? this._$AC(t2) : (void 0 === s2.el && (s2.el = S.createElement(V(s2.h, s2.h[0]), this.options)), s2);
      if (this._$AH?._$AD === e2) this._$AH.p(i3);
      else {
        const t3 = new R(e2, this), s3 = t3.u(this.options);
        t3.p(i3), this.T(s3), this._$AH = t3;
      }
    }
    _$AC(t2) {
      let i3 = C.get(t2.strings);
      return void 0 === i3 && C.set(t2.strings, i3 = new S(t2)), i3;
    }
    k(t2) {
      u(this._$AH) || (this._$AH = [], this._$AR());
      const i3 = this._$AH;
      let s2, e2 = 0;
      for (const h2 of t2) e2 === i3.length ? i3.push(s2 = new k(this.O(c$1()), this.O(c$1()), this, this.options)) : s2 = i3[e2], s2._$AI(h2), e2++;
      e2 < i3.length && (this._$AR(s2 && s2._$AB.nextSibling, e2), i3.length = e2);
    }
    _$AR(t2 = this._$AA.nextSibling, s2) {
      for (this._$AP?.(false, true, s2); t2 !== this._$AB; ) {
        const s3 = i$2(t2).nextSibling;
        i$2(t2).remove(), t2 = s3;
      }
    }
    setConnected(t2) {
      void 0 === this._$AM && (this._$Cv = t2, this._$AP?.(t2));
    }
  }
  class H {
    get tagName() {
      return this.element.tagName;
    }
    get _$AU() {
      return this._$AM._$AU;
    }
    constructor(t2, i3, s2, e2, h2) {
      this.type = 1, this._$AH = A, this._$AN = void 0, this.element = t2, this.name = i3, this._$AM = e2, this.options = h2, s2.length > 2 || "" !== s2[0] || "" !== s2[1] ? (this._$AH = Array(s2.length - 1).fill(new String()), this.strings = s2) : this._$AH = A;
    }
    _$AI(t2, i3 = this, s2, e2) {
      const h2 = this.strings;
      let o2 = false;
      if (void 0 === h2) t2 = M(this, t2, i3, 0), o2 = !a(t2) || t2 !== this._$AH && t2 !== E, o2 && (this._$AH = t2);
      else {
        const e3 = t2;
        let n3, r2;
        for (t2 = h2[0], n3 = 0; n3 < h2.length - 1; n3++) r2 = M(this, e3[s2 + n3], i3, n3), r2 === E && (r2 = this._$AH[n3]), o2 ||= !a(r2) || r2 !== this._$AH[n3], r2 === A ? t2 = A : t2 !== A && (t2 += (r2 ?? "") + h2[n3 + 1]), this._$AH[n3] = r2;
      }
      o2 && !e2 && this.j(t2);
    }
    j(t2) {
      t2 === A ? this.element.removeAttribute(this.name) : this.element.setAttribute(this.name, t2 ?? "");
    }
  }
  class I extends H {
    constructor() {
      super(...arguments), this.type = 3;
    }
    j(t2) {
      this.element[this.name] = t2 === A ? void 0 : t2;
    }
  }
  class L extends H {
    constructor() {
      super(...arguments), this.type = 4;
    }
    j(t2) {
      this.element.toggleAttribute(this.name, !!t2 && t2 !== A);
    }
  }
  class z extends H {
    constructor(t2, i3, s2, e2, h2) {
      super(t2, i3, s2, e2, h2), this.type = 5;
    }
    _$AI(t2, i3 = this) {
      if ((t2 = M(this, t2, i3, 0) ?? A) === E) return;
      const s2 = this._$AH, e2 = t2 === A && s2 !== A || t2.capture !== s2.capture || t2.once !== s2.once || t2.passive !== s2.passive, h2 = t2 !== A && (s2 === A || e2);
      e2 && this.element.removeEventListener(this.name, this, s2), h2 && this.element.addEventListener(this.name, this, t2), this._$AH = t2;
    }
    handleEvent(t2) {
      "function" == typeof this._$AH ? this._$AH.call(this.options?.host ?? this.element, t2) : this._$AH.handleEvent(t2);
    }
  }
  class Z {
    constructor(t2, i3, s2) {
      this.element = t2, this.type = 6, this._$AN = void 0, this._$AM = i3, this.options = s2;
    }
    get _$AU() {
      return this._$AM._$AU;
    }
    _$AI(t2) {
      M(this, t2);
    }
  }
  const B = t$2.litHtmlPolyfillSupport;
  B?.(S, k), (t$2.litHtmlVersions ??= []).push("3.3.3");
  const D = (t2, i3, s2) => {
    const e2 = s2?.renderBefore ?? i3;
    let h2 = e2._$litPart$;
    if (void 0 === h2) {
      const t3 = s2?.renderBefore ?? null;
      e2._$litPart$ = h2 = new k(i3.insertBefore(c$1(), t3), t3, void 0, s2 ?? {});
    }
    return h2._$AI(t2), h2;
  };
  const s$1 = globalThis;
  let i$1 = class i extends y$1 {
    constructor() {
      super(...arguments), this.renderOptions = { host: this }, this._$Do = void 0;
    }
    createRenderRoot() {
      const t2 = super.createRenderRoot();
      return this.renderOptions.renderBefore ??= t2.firstChild, t2;
    }
    update(t2) {
      const r2 = this.render();
      this.hasUpdated || (this.renderOptions.isConnected = this.isConnected), super.update(t2), this._$Do = D(r2, this.renderRoot, this.renderOptions);
    }
    connectedCallback() {
      super.connectedCallback(), this._$Do?.setConnected(true);
    }
    disconnectedCallback() {
      super.disconnectedCallback(), this._$Do?.setConnected(false);
    }
    render() {
      return E;
    }
  };
  i$1._$litElement$ = true, i$1["finalized"] = true, s$1.litElementHydrateSupport?.({ LitElement: i$1 });
  const o$4 = s$1.litElementPolyfillSupport;
  o$4?.({ LitElement: i$1 });
  (s$1.litElementVersions ??= []).push("4.2.2");
  const t$1 = (t2) => (e2, o2) => {
    void 0 !== o2 ? o2.addInitializer(() => {
      customElements.define(t2, e2);
    }) : customElements.define(t2, e2);
  };
  const o$3 = { attribute: true, type: String, converter: u$1, reflect: false, hasChanged: f$2 }, r$3 = (t2 = o$3, e2, r2) => {
    const { kind: n3, metadata: i3 } = r2;
    let s2 = globalThis.litPropertyMetadata.get(i3);
    if (void 0 === s2 && globalThis.litPropertyMetadata.set(i3, s2 = /* @__PURE__ */ new Map()), "setter" === n3 && ((t2 = Object.create(t2)).wrapped = true), s2.set(r2.name, t2), "accessor" === n3) {
      const { name: o2 } = r2;
      return { set(r3) {
        const n4 = e2.get.call(this);
        e2.set.call(this, r3), this.requestUpdate(o2, n4, t2, true, r3);
      }, init(e3) {
        return void 0 !== e3 && this.C(o2, void 0, t2, e3), e3;
      } };
    }
    if ("setter" === n3) {
      const { name: o2 } = r2;
      return function(r3) {
        const n4 = this[o2];
        e2.call(this, r3), this.requestUpdate(o2, n4, t2, true, r3);
      };
    }
    throw Error("Unsupported decorator location: " + n3);
  };
  function n$2(t2) {
    return (e2, o2) => "object" == typeof o2 ? r$3(t2, e2, o2) : ((t3, e3, o3) => {
      const r2 = e3.hasOwnProperty(o3);
      return e3.constructor.createProperty(o3, t3), r2 ? Object.getOwnPropertyDescriptor(e3, o3) : void 0;
    })(t2, e2, o2);
  }
  function r$2(r2) {
    return n$2({ ...r2, state: true, attribute: false });
  }
  const e$3 = (e2, t2, c2) => (c2.configurable = true, c2.enumerable = true, Reflect.decorate && "object" != typeof t2 && Object.defineProperty(e2, t2, c2), c2);
  function e$2(e2, r2) {
    return (n3, s2, i3) => {
      const o2 = (t2) => t2.renderRoot?.querySelector(e2) ?? null;
      return e$3(n3, s2, { get() {
        return o2(this);
      } });
    };
  }
  const t = { CHILD: 2 }, e$1 = (t2) => (...e2) => ({ _$litDirective$: t2, values: e2 });
  class i2 {
    constructor(t2) {
    }
    get _$AU() {
      return this._$AM._$AU;
    }
    _$AT(t2, e2, i3) {
      this._$Ct = t2, this._$AM = e2, this._$Ci = i3;
    }
    _$AS(t2, e2) {
      return this.update(t2, e2);
    }
    update(t2, e2) {
      return this.render(...e2);
    }
  }
  class e extends i2 {
    constructor(i3) {
      if (super(i3), this.it = A, i3.type !== t.CHILD) throw Error(this.constructor.directiveName + "() can only be used in child bindings");
    }
    render(r2) {
      if (r2 === A || null == r2) return this._t = void 0, this.it = r2;
      if (r2 === E) return r2;
      if ("string" != typeof r2) throw Error(this.constructor.directiveName + "() called with a non-string value");
      if (r2 === this.it) return this._t;
      this.it = r2;
      const s2 = [r2];
      return s2.raw = s2, this._t = { _$litType$: this.constructor.resultType, strings: s2, values: [] };
    }
  }
  e.directiveName = "unsafeHTML", e.resultType = 1;
  const o$2 = e$1(e);
  const HOVER_CSS = ":host{position:fixed;z-index:2147483640;display:flex;gap:3px;align-items:center;background:#16161ae6;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:1px solid rgba(255,255,255,.1);border-radius:8px;padding:4px 6px;box-shadow:0 4px 16px #0000004d;opacity:0;pointer-events:none;transition:opacity .15s ease,visibility .15s ease;font-family:system-ui,sans-serif;visibility:hidden;user-select:none;-webkit-user-select:none}:host(.visible){opacity:1;visibility:visible;pointer-events:none}.svf-hb-btn{background:transparent;border:none;cursor:pointer;width:24px;height:24px;border-radius:5px;display:grid;place-items:center;color:#fff9;transition:background .15s,color .15s,transform .1s;padding:0;pointer-events:auto}.svf-hb-btn:hover{background:#ffffff1a;color:#fff;transform:scale(1.1)}.svf-hb-btn.like-active{color:var(--svf-primary, #10b981)}.svf-hb-btn.dislike-active{color:#ef4444}.svf-hb-sep{width:1px;height:16px;background:#ffffff1a;margin:0 2px}.svf-hb-label{font-size:10px;color:#fff6;max-width:120px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;padding:0 4px}";
  function svgIcon(path, size = 14, viewBox = "0 0 24 24", strokeWidth = "2.5") {
    return `<svg width="${size}" height="${size}" viewBox="${viewBox}" fill="none" stroke="currentColor" stroke-width="${strokeWidth}" stroke-linecap="round" stroke-linejoin="round">${path}</svg>`;
  }
  const ICON_CLOSE = svgIcon('<line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line>', 16, "0 0 24 24", "2.5");
  const ICON_SEARCH = svgIcon('<circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line>', 14, "0 0 24 24", "2.5");
  const ICON_CLEAR = svgIcon('<line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line>', 12, "0 0 24 24", "2.5");
  const ICON_COPY = svgIcon('<rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>', 14, "0 0 24 24", "2");
  const ICON_DOWNLOAD = svgIcon('<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>', 14, "0 0 24 24", "2");
  const ICON_IMPORT = svgIcon('<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/>', 14, "0 0 24 24", "2");
  const ICON_EDIT = svgIcon('<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 1 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>');
  const ICON_DELETE = svgIcon('<polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>');
  const ICON_TRIGGER = svgIcon('<path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7z"></path><circle cx="12" cy="12" r="3"></circle>', 20, "0 0 24 24", "2.2");
  const STAR_FILLED = svgIcon('<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" fill="currentColor" stroke="none"/>', 14, "0 0 24 24", "2.2");
  const STAR_EMPTY = svgIcon('<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>', 14, "0 0 24 24", "2.2");
  const BAN_ICON = svgIcon('<circle cx="12" cy="12" r="10"/><line x1="4.93" y1="4.93" x2="19.07" y2="19.07"/>', 14, "0 0 24 24", "2.2");
  var __defProp$2 = Object.defineProperty;
  var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
  var __decorateClass$2 = (decorators, target, key, kind) => {
    var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
    for (var i3 = decorators.length - 1, decorator; i3 >= 0; i3--)
      if (decorator = decorators[i3])
        result = (kind ? decorator(target, key, result) : decorator(result)) || result;
    if (kind && result) __defProp$2(target, key, result);
    return result;
  };
  const ATTR = SVF_CONFIG.ITEM_ATTR;
  const DOMAIN_ATTR = `${ATTR}-domain`;
  let HoverOverlayElement = class extends i$1 {
    constructor() {
      super(...arguments);
      this._activeDomain = "";
      this._matchState = "normal";
      this._activeItem = null;
      this._hideTimer = null;
      this._HIDE_DELAY = 350;
      this._rafId = null;
      this._lastEvent = null;
      this._longPressTimer = null;
      this._LONGPRESS_MS = 500;
    }
    connectedCallback() {
      super.connectedCallback();
      this._unsubscribe?.();
      if (this.store) {
        this._unsubscribe = this.store.subscribe(() => {
          this._updateMatchState();
        });
      }
      this._connectedAbortController?.abort();
      this._connectedAbortController = new AbortController();
      const signal = this._connectedAbortController.signal;
      const docOpts = { signal, passive: true, capture: true };
      document.addEventListener("pointermove", (e2) => {
        this._lastEvent = e2;
        if (this._rafId === null) {
          this._rafId = requestAnimationFrame(() => {
            if (this._lastEvent) {
              this._onPointerMove(this._lastEvent);
            }
            this._rafId = null;
          });
        }
      }, docOpts);
      document.addEventListener("pointerleave", () => {
        this._scheduleHide();
      }, docOpts);
      this.addEventListener("pointerenter", () => this._cancelHide(), { signal });
      this.addEventListener("pointerleave", () => this._scheduleHide(), { signal });
      const cancelLongPress = () => {
        if (this._longPressTimer) {
          clearTimeout(this._longPressTimer);
          this._longPressTimer = null;
        }
      };
      document.addEventListener("pointerdown", (e2) => {
        if (e2.pointerType !== "touch") return;
        cancelLongPress();
        if (!e2.composedPath().includes(this)) this.classList.remove("visible");
        const item = e2.target?.closest(`[${ATTR}]`);
        if (!item) return;
        this._longPressTimer = setTimeout(() => {
          this._activeItem = item;
          this._activeDomain = item.getAttribute(DOMAIN_ATTR) ?? "";
          this._updateMatchState();
          this._positionBar(item);
        }, this._LONGPRESS_MS);
      }, docOpts);
      document.addEventListener("pointerup", cancelLongPress, docOpts);
      document.addEventListener("pointercancel", cancelLongPress, docOpts);
      document.addEventListener("pointermove", (e2) => {
        if (e2.pointerType === "touch") cancelLongPress();
      }, docOpts);
      document.addEventListener("contextmenu", (e2) => {
        if (this.classList.contains("visible") && this._activeItem) {
          e2.preventDefault();
        }
      }, { signal });
    }
    disconnectedCallback() {
      super.disconnectedCallback();
      this._unsubscribe?.();
      if (this._connectedAbortController) {
        this._connectedAbortController.abort();
        this._connectedAbortController = void 0;
      }
      if (this._rafId !== null) {
        cancelAnimationFrame(this._rafId);
        this._rafId = null;
      }
      if (this._hideTimer) {
        clearTimeout(this._hideTimer);
        this._hideTimer = null;
      }
      if (this._longPressTimer) {
        clearTimeout(this._longPressTimer);
        this._longPressTimer = null;
      }
    }
    updated(changedProperties) {
      super.updated(changedProperties);
      if (this.store) {
        applyAccentVariables(this, this.store.accent);
      }
    }
    _onPointerMove(e2) {
      if (e2.pointerType === "touch") return;
      const shadowHost = document.getElementById("svf-shadow-host");
      const panel = (shadowHost?.shadowRoot || shadowHost)?.querySelector("svf-panel");
      if (panel && typeof panel.isOpen === "function" && panel.isOpen()) {
        this._scheduleHide();
        return;
      }
      let target = document.elementFromPoint(e2.clientX, e2.clientY);
      if (!target) {
        this._scheduleHide();
        return;
      }
      while (target && target.shadowRoot) {
        const inner = target.shadowRoot.elementFromPoint(e2.clientX, e2.clientY);
        if (!inner || inner === target) break;
        target = inner;
      }
      const item = target.closest(`[${ATTR}]`);
      if (!item) {
        if (target === shadowHost || this.contains(target)) {
          this._cancelHide();
          return;
        }
        this._scheduleHide();
        return;
      }
      this._cancelHide();
      if (item === this._activeItem) return;
      this._activeItem = item;
      this._activeDomain = item.getAttribute(DOMAIN_ATTR) ?? "";
      this._updateMatchState();
      this._positionBar(item);
    }
    _positionBar(item) {
      const rect = item.getBoundingClientRect();
      const barW = this.offsetWidth || 160;
      const barH = this.offsetHeight || 32;
      let top = rect.top + 4;
      if (rect.top < 0) {
        top = Math.max(4, rect.bottom - barH - 4);
      }
      let left = rect.right - barW - 4;
      if (left < 8) left = rect.left + 8;
      if (left + barW > window.innerWidth - 8) left = window.innerWidth - barW - 8;
      this.style.top = `${top}px`;
      this.style.left = `${left}px`;
      this.classList.add("visible");
    }
    _updateMatchState() {
      if (!this._activeDomain || !this.store) return;
      this._matchState = this.store.matchDomain(this._activeDomain);
    }
    _scheduleHide() {
      if (this._hideTimer) return;
      this._hideTimer = setTimeout(() => {
        this.classList.remove("visible");
        this._activeItem = null;
        this._activeDomain = "";
        this._hideTimer = null;
      }, this._HIDE_DELAY);
    }
    _cancelHide() {
      if (this._hideTimer) {
        clearTimeout(this._hideTimer);
        this._hideTimer = null;
      }
    }
    _toggleLike(e2) {
      e2.stopPropagation();
      if (!this._activeDomain) return;
      if (this._matchState === "liked") {
        this.store.removeDomain("liked", this._activeDomain);
      } else {
        this.store.addDomain("liked", this._activeDomain);
      }
      this.scanner.reapply();
    }
    _toggleDislike(e2) {
      e2.stopPropagation();
      if (!this._activeDomain) return;
      if (this._matchState === "disliked") {
        this.store.removeDomain("disliked", this._activeDomain);
      } else {
        this.store.addDomain("disliked", this._activeDomain);
      }
      this.scanner.reapply();
    }
    render() {
      return b`
            <button
                class="svf-hb-btn ${this._matchState === "liked" ? "like-active" : ""}"
                title=${this._matchState === "liked" ? "Remove from Liked" : "Add to Liked"}
                @click=${this._toggleLike}
            >
                ${o$2(this._matchState === "liked" ? STAR_FILLED : STAR_EMPTY)}
            </button>
            
            <button
                class="svf-hb-btn ${this._matchState === "disliked" ? "dislike-active" : ""}"
                title=${this._matchState === "disliked" ? "Remove from Disliked" : "Add to Disliked"}
                @click=${this._toggleDislike}
            >
                ${o$2(BAN_ICON)}
            </button>
            
            <div class="svf-hb-sep"></div>
            
            <span class="svf-hb-label" title=${this._activeDomain}>${this._activeDomain}</span>
        `;
    }
  };
  HoverOverlayElement.styles = r$6(HOVER_CSS);
  __decorateClass$2([
    n$2({ attribute: false })
  ], HoverOverlayElement.prototype, "store", 2);
  __decorateClass$2([
    n$2({ attribute: false })
  ], HoverOverlayElement.prototype, "scanner", 2);
  __decorateClass$2([
    n$2({ attribute: false })
  ], HoverOverlayElement.prototype, "signal", 2);
  __decorateClass$2([
    r$2()
  ], HoverOverlayElement.prototype, "_activeDomain", 2);
  __decorateClass$2([
    r$2()
  ], HoverOverlayElement.prototype, "_matchState", 2);
  HoverOverlayElement = __decorateClass$2([
    t$1("svf-hover-overlay")
  ], HoverOverlayElement);
  const r$1 = (o2) => void 0 === o2.strings;
  const s = (i3, t2) => {
    const e2 = i3._$AN;
    if (void 0 === e2) return false;
    for (const i4 of e2) i4._$AO?.(t2, false), s(i4, t2);
    return true;
  }, o$1 = (i3) => {
    let t2, e2;
    do {
      if (void 0 === (t2 = i3._$AM)) break;
      e2 = t2._$AN, e2.delete(i3), i3 = t2;
    } while (0 === e2?.size);
  }, r = (i3) => {
    for (let t2; t2 = i3._$AM; i3 = t2) {
      let e2 = t2._$AN;
      if (void 0 === e2) t2._$AN = e2 = /* @__PURE__ */ new Set();
      else if (e2.has(i3)) break;
      e2.add(i3), c(t2);
    }
  };
  function h(i3) {
    void 0 !== this._$AN ? (o$1(this), this._$AM = i3, r(this)) : this._$AM = i3;
  }
  function n$1(i3, t2 = false, e2 = 0) {
    const r2 = this._$AH, h2 = this._$AN;
    if (void 0 !== h2 && 0 !== h2.size) if (t2) if (Array.isArray(r2)) for (let i4 = e2; i4 < r2.length; i4++) s(r2[i4], false), o$1(r2[i4]);
    else null != r2 && (s(r2, false), o$1(r2));
    else s(this, i3);
  }
  const c = (i3) => {
    i3.type == t.CHILD && (i3._$AP ??= n$1, i3._$AQ ??= h);
  };
  class f extends i2 {
    constructor() {
      super(...arguments), this._$AN = void 0;
    }
    _$AT(i3, t2, e2) {
      super._$AT(i3, t2, e2), r(this), this.isConnected = i3._$AU;
    }
    _$AO(i3, t2 = true) {
      i3 !== this.isConnected && (this.isConnected = i3, i3 ? this.reconnected?.() : this.disconnected?.()), t2 && (s(this, i3), o$1(this));
    }
    setValue(t2) {
      if (r$1(this._$Ct)) this._$Ct._$AI(t2, this);
      else {
        const i3 = [...this._$Ct._$AH];
        i3[this._$Ci] = t2, this._$Ct._$AI(i3, this, 0);
      }
    }
    disconnected() {
    }
    reconnected() {
    }
  }
  const o = /* @__PURE__ */ new WeakMap(), n2 = e$1(class extends f {
    render(i3) {
      return A;
    }
    update(i3, [s2]) {
      const e2 = s2 !== this.G;
      return e2 && this.rt(void 0), (e2 || this.lt !== this.ct) && (this.G = s2, this.ht = i3.options?.host, this.rt(this.ct = i3.element)), A;
    }
    rt(t2) {
      if (void 0 !== this.G) if (this.isConnected || (t2 = void 0), "function" == typeof this.G) {
        const i3 = this.ht ?? globalThis;
        let s2 = o.get(i3);
        void 0 === s2 && (s2 = /* @__PURE__ */ new WeakMap(), o.set(i3, s2)), void 0 !== s2.get(this.G) && this.G.call(this.ht, void 0), s2.set(this.G, t2), void 0 !== t2 && this.G.call(this.ht, t2);
      } else this.G.value = t2;
    }
    get lt() {
      return "function" == typeof this.G ? o.get(this.ht ?? globalThis)?.get(this.G) : this.G?.value;
    }
    disconnected() {
      this.lt === this.ct && this.rt(void 0);
    }
    reconnected() {
      this.rt(this.ct);
    }
  });
  function parseJsonRobust(text) {
    if (text.charCodeAt(0) === 65279) {
      text = text.slice(1);
    }
    let inString = false;
    let escape = false;
    let out = "";
    let lastCommaIdx = -1;
    for (let i3 = 0; i3 < text.length; i3++) {
      const char = text[i3];
      if (inString) {
        out += char;
        if (escape) {
          escape = false;
        } else if (char === "\\") {
          escape = true;
        } else if (char === '"') {
          inString = false;
        }
      } else {
        if (char === "/" && text[i3 + 1] === "/") {
          while (i3 < text.length && text[i3] !== "\n" && text[i3] !== "\r") {
            i3++;
          }
          out += "\n";
        } else if (char === "/" && text[i3 + 1] === "*") {
          i3 += 2;
          while (i3 < text.length && !(text[i3] === "*" && text[i3 + 1] === "/")) {
            i3++;
          }
          i3++;
        } else {
          if (char === '"') {
            inString = true;
          }
          if (char === ",") {
            lastCommaIdx = out.length;
            out += char;
          } else if ((char === "}" || char === "]") && lastCommaIdx !== -1) {
            let onlyWhitespace = true;
            for (let j = lastCommaIdx + 1; j < out.length; j++) {
              if (!/\s/.test(out[j])) {
                onlyWhitespace = false;
                break;
              }
            }
            if (onlyWhitespace) {
              out = out.slice(0, lastCommaIdx) + out.slice(lastCommaIdx + 1);
            }
            lastCommaIdx = -1;
            out += char;
          } else {
            if (!/\s/.test(char)) {
              lastCommaIdx = -1;
            }
            out += char;
          }
        }
      }
    }
    return JSON.parse(out);
  }
  function getRegisterableDomain(domain) {
    let clean = domain.toLowerCase().trim();
    if (clean.startsWith("*.")) {
      clean = clean.slice(2);
    }
    if (/^\d+\.\d+\.\d+\.\d+$/.test(clean)) {
      return clean;
    }
    const parts = clean.split(".");
    if (parts.length <= 2) return clean;
    const tld = parts[parts.length - 1];
    const sld = parts[parts.length - 2];
    const isCcTld = tld.length === 2;
    const isCommonSld = /^(co|com|org|net|gov|edu|ac|or|ne|ltd|plc|sch|asn)$/.test(sld);
    if (isCcTld && isCommonSld && parts.length >= 3) {
      return parts.slice(-3).join(".");
    }
    return parts.slice(-2).join(".");
  }
  const PANEL_CSS = ':host{--svf-bg: rgba(18, 18, 22, .85);--svf-border: rgba(255, 255, 255, .08);--svf-border-hover: rgba(255, 255, 255, .16);--svf-primary: #10b981;--svf-primary-glow: rgba(16, 185, 129, .15);--svf-primary-bg-hover: rgba(16, 185, 129, .08);--svf-primary-border: rgba(16, 185, 129, .25);--svf-primary-status-bg: rgba(16, 185, 129, .1);--svf-danger: #ef4444;--svf-text: #f3f4f6;--svf-text-muted: #9ca3af;--svf-font: "Outfit", "Inter", system-ui, -apple-system, sans-serif;--svf-glow: 0 24px 50px rgba(0, 0, 0, .6);user-select:none;-webkit-user-select:none}*,*:before,*:after{box-sizing:border-box}.svf-color-picker-row{display:flex;gap:10px;margin-bottom:6px;padding:4px 0}.svf-color-dot{width:24px;height:24px;border-radius:50%;cursor:pointer;border:2px solid transparent;transition:all .2s cubic-bezier(.4,0,.2,1)}.svf-color-dot:hover{transform:scale(1.15)}.svf-color-dot.active{border-color:#fff;box-shadow:0 0 10px var(--dot-glow)}.svf-backdrop{position:fixed;inset:0;background:#0009;backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);opacity:0;visibility:hidden;pointer-events:none;transition:opacity .3s ease,visibility .3s ease;will-change:opacity;z-index:2147483645}.svf-backdrop.open{opacity:1;visibility:visible;pointer-events:auto}.svf-card{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) scale(.9);width:90%;max-width:480px;max-height:85vh;background:var(--svf-bg);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);border:1px solid var(--svf-border);border-radius:20px;box-shadow:var(--svf-glow);color:var(--svf-text);display:flex;flex-direction:column;overflow:hidden;opacity:0;visibility:hidden;pointer-events:none;transition:transform .35s cubic-bezier(.34,1.56,.64,1),opacity .3s ease,visibility .3s ease;will-change:transform,opacity;z-index:2147483646;font-family:var(--svf-font)}.svf-card.open{transform:translate(-50%,-50%) scale(1);opacity:1;visibility:visible;pointer-events:auto}.svf-header{display:flex;justify-content:space-between;align-items:center;padding:20px 24px;border-bottom:1px solid var(--svf-border)}.svf-title-group{display:flex;flex-direction:column}.svf-title{font-size:20px;font-weight:800;letter-spacing:-.4px;background:linear-gradient(135deg,#ffffff 15%,var(--svf-primary) 100%);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}.svf-close-btn{background:transparent;border:none;color:#ef4444b3;cursor:pointer;width:32px;height:32px;border-radius:50%;display:grid;place-items:center;transition:background .2s ease,color .2s ease,transform .2s ease;padding:0}.svf-close-btn:hover{background:#ef44441f;color:var(--svf-danger);transform:rotate(90deg)}.svf-body{padding:24px;overflow-y:auto;overflow-x:hidden;display:flex;flex-direction:column;gap:24px;max-height:calc(85vh - 140px)}.svf-control-group{display:flex;flex-direction:column;gap:8px}.svf-label{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--svf-text-muted)}.svf-segmented{display:flex;background:#0000004d;border:1px solid var(--svf-border);padding:4px;border-radius:12px}.svf-seg-btn{flex:1;background:transparent;border:none;color:var(--svf-text-muted);padding:9px 16px;font-size:13px;font-weight:600;border-radius:9px;cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);font-family:var(--svf-font)}.svf-seg-btn.active{background:var(--svf-primary);color:#0c0d10;box-shadow:0 4px 12px var(--svf-primary-glow);font-weight:700}.svf-section{display:flex;flex-direction:column;gap:12px}.svf-section-header{display:flex;justify-content:space-between;align-items:center}.svf-badge{font-size:11px;font-weight:700;padding:2px 8px;border-radius:10px;background:#ffffff14}.svf-section.liked .svf-badge{background:var(--svf-primary-glow);color:var(--svf-primary)}.svf-section.disliked .svf-badge{background:#ef444426;color:var(--svf-danger)}.svf-textarea{width:100%;height:120px;background:#00000059;border:1px solid var(--svf-border);border-radius:12px;padding:10px 12px;color:var(--svf-text);font-family:var(--svf-mono, monospace);font-size:12.5px;line-height:1.5;resize:none;outline:none;transition:border-color .2s ease,box-shadow .2s ease;overflow-x:hidden;white-space:pre-wrap;word-break:break-all}.svf-textarea:focus{border-color:var(--svf-primary);box-shadow:0 0 0 2px var(--svf-primary-glow)}.svf-textarea-status-row{display:flex;justify-content:space-between;align-items:center;margin-top:6px}.svf-textarea-msg-info{font-size:11px;color:var(--svf-text-muted)}.svf-apply-btn{background:var(--svf-primary-bg-hover);border:1px solid var(--svf-primary-border);color:var(--svf-primary);padding:6px 14px;border-radius:8px;font-family:var(--svf-font);font-size:12px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1)}.svf-apply-btn:hover{background:var(--svf-primary);border-color:var(--svf-primary);color:#0c0d10;box-shadow:0 0 12px var(--svf-primary-glow)}.svf-merger-card{background:#f59e0b14;border:1px solid rgba(245,158,11,.25);border-radius:12px;padding:10px 14px;display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;box-shadow:0 4px 12px #f59e0b0d;animation:svf-fade-in .25s cubic-bezier(.4,0,.2,1)}@keyframes svf-fade-in{0%{opacity:0;transform:translateY(-3px)}to{opacity:1;transform:translateY(0)}}.svf-merger-info{display:flex;flex-direction:column;gap:2px}.svf-merger-title{font-size:12px;font-weight:700;color:#f59e0b}.svf-merger-desc{font-size:11px;color:var(--svf-text-muted)}.svf-merger-btn{background:#f59e0b;border:none;color:#0f1015;padding:6px 12px;border-radius:8px;font-family:var(--svf-font);font-size:11.5px;font-weight:700;cursor:pointer;transition:all .2s;box-shadow:0 2px 6px #f59e0b4d;flex-shrink:0}.svf-merger-btn:hover{opacity:.95;transform:translateY(-1px);box-shadow:0 4px 10px #f59e0b80}.svf-io-section{display:flex;flex-direction:column;gap:12px;border-top:1px solid var(--svf-border);padding-top:20px}.svf-io-row{display:flex;gap:8px}.svf-io-btn{flex:1;border:1px solid var(--svf-border);background:#ffffff08;color:var(--svf-text);border-radius:12px;padding:10px 14px;font-size:13px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);font-family:var(--svf-font);display:flex;align-items:center;justify-content:center;gap:8px}.svf-io-btn:hover{background:#ffffff14;border-color:var(--svf-border-hover);box-shadow:0 4px 12px #00000026}.svf-io-btn:active{transform:scale(.97)}.svf-io-btn.primary{background:var(--svf-primary-bg-hover);border-color:var(--svf-primary-border);color:var(--svf-primary)}.svf-io-btn.primary:hover{background:var(--svf-primary);border-color:var(--svf-primary);color:#0c0d10;box-shadow:0 4px 12px var(--svf-primary-glow)}.svf-io-url-form{display:flex;gap:8px}.svf-io-url-input{flex:1;background:#00000040;border:1px solid var(--svf-border);color:var(--svf-text);border-radius:12px;padding:10px 14px;font-size:12.5px;outline:none;transition:all .2s;font-family:var(--svf-font)}.svf-io-url-input:focus{border-color:#fff3;box-shadow:0 0 0 2px #ffffff0d}.svf-io-status{font-size:11.5px;padding:6px 10px;border-radius:8px;transition:opacity .3s}.svf-io-status.success{background:var(--svf-primary-status-bg);color:var(--svf-primary)}.svf-io-status.error{background:#ef44441a;color:var(--svf-danger)}.svf-io-file{display:none}.svf-body::-webkit-scrollbar,.svf-textarea::-webkit-scrollbar,.svf-filtered-list::-webkit-scrollbar{width:6px;height:6px}.svf-body::-webkit-scrollbar-track,.svf-textarea::-webkit-scrollbar-track,.svf-filtered-list::-webkit-scrollbar-track{background:transparent}.svf-body::-webkit-scrollbar-thumb,.svf-textarea::-webkit-scrollbar-thumb,.svf-filtered-list::-webkit-scrollbar-thumb{background:#ffffff26;border-radius:3px}.svf-body::-webkit-scrollbar-thumb:hover,.svf-textarea::-webkit-scrollbar-thumb:hover,.svf-filtered-list::-webkit-scrollbar-thumb:hover{background:#ffffff4d}.svf-body,.svf-textarea,.svf-filtered-list{scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.15) transparent}.svf-switch-row{display:flex;justify-content:space-between;align-items:center;padding:6px 0}.svf-switch-label-group{display:flex;flex-direction:column}.svf-switch-title{font-size:13px;font-weight:600;color:var(--svf-text)}.svf-switch-desc{font-size:11px;color:var(--svf-text-muted);margin-top:1px}.svf-switch{position:relative;display:inline-block;width:40px;height:22px;flex-shrink:0}.svf-switch input{opacity:0;width:0;height:0}.svf-slider{position:absolute;cursor:pointer;inset:0;background-color:#ffffff14;border:1px solid var(--svf-border);transition:.3s cubic-bezier(.4,0,.2,1);border-radius:34px}.svf-slider:before{position:absolute;content:"";height:14px;width:14px;left:3px;bottom:3px;background-color:var(--svf-text-muted);transition:.3s cubic-bezier(.4,0,.2,1);border-radius:50%}input:checked+.svf-slider{background-color:var(--svf-primary);border-color:var(--svf-primary)}input:checked+.svf-slider:before{transform:translate(18px);background-color:#0c0d10}@keyframes svf-shake{0%,to{transform:translate(0)}25%{transform:translate(-4px)}75%{transform:translate(4px)}}.svf-io-url-input.invalid{border-color:var(--svf-danger)!important;animation:svf-shake .2s ease-in-out 2}.svf-tab-nav{display:flex;background:#00000026;border-bottom:1px solid var(--svf-border);padding:0 12px}.svf-tab-btn{background:transparent;border:none;color:var(--svf-text-muted);padding:14px 16px;font-family:var(--svf-font);font-weight:600;font-size:13px;cursor:pointer;border-bottom:2px solid transparent;transition:all .2s ease}.svf-tab-btn:hover{color:var(--svf-text)}.svf-tab-btn.active{color:var(--svf-primary);border-bottom-color:var(--svf-primary)}.svf-tab-content{display:flex;flex-direction:column;gap:20px;width:100%}.svf-search-wrapper{position:relative;display:flex;align-items:center;margin-bottom:2px}.svf-search-input{width:100%;background:#00000040;border:1px solid var(--svf-border);border-radius:12px;padding:10px 14px 10px 36px;color:var(--svf-text);font-family:var(--svf-font);font-size:13px;outline:none;transition:all .2s cubic-bezier(.4,0,.2,1)}.svf-search-input:focus{border-color:var(--svf-primary);box-shadow:0 0 0 2px var(--svf-primary-glow)}.svf-search-icon{position:absolute;left:14px;color:var(--svf-text-muted);pointer-events:none;display:flex;align-items:center;justify-content:center}.svf-clear-btn{position:absolute;right:12px;background:transparent;border:none;color:var(--svf-text-muted);cursor:pointer;width:20px;height:20px;border-radius:50%;display:grid;place-items:center;transition:background .2s,color .2s;padding:0}.svf-clear-btn:hover{background:#ffffff14;color:#fff}.svf-filtered-list{display:flex;flex-direction:column;gap:8px;background:#00000026;border:1px solid var(--svf-border);border-radius:12px;padding:10px;max-height:180px;overflow-y:auto;scrollbar-width:thin}.svf-filtered-item{background:#ffffff05;border:1px solid rgba(255,255,255,.05);border-radius:8px;padding:8px 12px;display:flex;align-items:center;justify-content:space-between;font-family:var(--svf-mono, monospace);font-size:12px;transition:all .2s cubic-bezier(.4,0,.2,1)}.svf-filtered-item:hover{background:#ffffff0a;border-color:var(--svf-border-hover)}.svf-filtered-text{color:var(--svf-text);word-break:break-all}.svf-filtered-actions{display:flex;gap:6px;flex-shrink:0}.svf-filtered-btn{background:transparent;border:none;color:var(--svf-text-muted);cursor:pointer;width:28px;height:28px;border-radius:6px;display:grid;place-items:center;transition:all .15s ease;padding:0}.svf-filtered-btn:hover{color:#fff;background:#ffffff14}.svf-filtered-btn.delete:hover{color:var(--svf-danger);background:#ef44441a}.svf-filtered-input{width:100%;background:#00000080;border:1px solid var(--svf-primary);border-radius:6px;padding:4px 8px;color:#fff;font-family:var(--svf-mono, monospace);font-size:12px;outline:none}.svf-filtered-empty{padding:12px;font-size:12px;color:var(--svf-text-muted);text-align:center}.svf-textarea,.svf-search-input,.svf-filtered-input,.svf-io-url-input{user-select:text;-webkit-user-select:text}';
  var __defProp$1 = Object.defineProperty;
  var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
  var __decorateClass$1 = (decorators, target, key, kind) => {
    var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
    for (var i3 = decorators.length - 1, decorator; i3 >= 0; i3--)
      if (decorator = decorators[i3])
        result = (kind ? decorator(target, key, result) : decorator(result)) || result;
    if (kind && result) __defProp$1(target, key, result);
    return result;
  };
  let PanelElement = class extends i$1 {
    constructor() {
      super(...arguments);
      this._isOpen = false;
      this._activeTab = "filters";
      this._searchQuery = "";
      this._ioStatus = null;
      this._isFetchingUrl = false;
      this._likedText = "";
      this._dislikedText = "";
      this._editingMatches = {};
      this._lastActiveElement = null;
      this._isTextareaFocused = { liked: false, disliked: false };
      this._handleKeydown = (e2) => {
        if (!this._isOpen) return;
        if (e2.key === "Escape") {
          this.close();
        } else if (e2.key === "Tab") {
          this._handleTab(e2);
        }
      };
    }
    connectedCallback() {
      super.connectedCallback();
      if (!document.querySelector('link[href*="fonts.googleapis.com/css2?family=Outfit"]')) {
        const fontLink = document.createElement("link");
        fontLink.rel = "stylesheet";
        fontLink.href = "https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700&display=swap";
        document.head.appendChild(fontLink);
      }
      this.style.display = "none";
      this._unsubscribe?.();
      if (this.store) {
        this._syncTextFromStore();
        this._unsubscribe = this.store.subscribe(() => {
          this.requestUpdate();
          this._syncTextFromStore();
        });
      }
      window.removeEventListener("keydown", this._handleKeydown);
      window.addEventListener("keydown", this._handleKeydown);
    }
    disconnectedCallback() {
      super.disconnectedCallback();
      this._unsubscribe?.();
      window.removeEventListener("keydown", this._handleKeydown);
    }
    _syncTextFromStore() {
      if (!this._isTextareaFocused.liked) {
        this._likedText = this.store.liked.join("\n");
      }
      if (!this._isTextareaFocused.disliked) {
        this._dislikedText = this.store.disliked.join("\n");
      }
    }
    updated(changedProperties) {
      super.updated(changedProperties);
      if (this.store) {
        applyAccentVariables(this, this.store.accent);
      }
      const textChanged = changedProperties.has("_likedText") || changedProperties.has("_dislikedText");
      const tabChangedToFilters = changedProperties.has("_activeTab") && this._activeTab === "filters";
      const panelOpened = changedProperties.has("_isOpen") && this._isOpen;
      if (textChanged || tabChangedToFilters || panelOpened) {
        if (this._activeTab === "filters") {
          const delay = panelOpened ? 200 : 0;
          if (this._likedTextarea) this._autoResize(this._likedTextarea, delay);
          if (this._dislikedTextarea) this._autoResize(this._dislikedTextarea, delay);
        }
      }
    }
    open() {
      this.style.display = "block";
      this.offsetHeight;
      this._lastActiveElement = document.activeElement;
      this._isOpen = true;
      this._syncTextFromStore();
      setTimeout(() => {
        this._closeBtn?.focus();
      }, 50);
    }
    close() {
      this._isOpen = false;
      if (this._lastActiveElement) {
        this._lastActiveElement.focus();
        this._lastActiveElement = null;
      }
      const card = this.shadowRoot?.querySelector(".svf-card");
      if (card) {
        card.addEventListener("transitionend", (e2) => {
          if (!this._isOpen && e2.propertyName === "opacity") {
            this.style.display = "none";
          }
        }, { once: true });
      } else {
        this.style.display = "none";
      }
    }
    isOpen() {
      return this._isOpen;
    }
    _handleTab(e2) {
      const focusables = Array.from(
        this.shadowRoot.querySelectorAll(
          'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
        )
      ).filter((el) => {
        if (el.disabled) return false;
        const rect = el.getBoundingClientRect();
        if (rect.width === 0 || rect.height === 0) return false;
        const style = window.getComputedStyle(el);
        return style.visibility !== "hidden" && style.opacity !== "0";
      });
      if (focusables.length === 0) return;
      const first = focusables[0];
      const last = focusables[focusables.length - 1];
      const active = this.shadowRoot.activeElement;
      if (e2.shiftKey) {
        if (active === first || !focusables.includes(active)) {
          last.focus();
          e2.preventDefault();
        }
      } else {
        if (active === last || !focusables.includes(active)) {
          first.focus();
          e2.preventDefault();
        }
      }
    }
    render() {
      return b`
            <div class="svf-backdrop ${this._isOpen ? "open" : ""}" @click=${this.close}></div>
            <div class="svf-card ${this._isOpen ? "open" : ""}">
                <div class="svf-header">
                    <div class="svf-title-group">
                        <div class="svf-title">Better Search ⭐</div>
                    </div>
                    <button class="svf-close-btn" @click=${this.close}>${o$2(ICON_CLOSE)}</button>
                </div>
                
                <div class="svf-tab-nav">
                    <button class="svf-tab-btn ${this._activeTab === "filters" ? "active" : ""}" @click=${() => this._activeTab = "filters"}>Domain Filters</button>
                    <button class="svf-tab-btn ${this._activeTab === "settings" ? "active" : ""}" @click=${() => this._activeTab = "settings"}>Settings</button>
                </div>
                
                <div class="svf-body">
                    <div class="svf-tab-content ${this._activeTab === "filters" ? "active" : ""}" style="display: ${this._activeTab === "filters" ? "flex" : "none"}">
                        ${this._renderFiltersTab()}
                    </div>
                    <div class="svf-tab-content ${this._activeTab === "settings" ? "active" : ""}" style="display: ${this._activeTab === "settings" ? "flex" : "none"}">
                        ${this._renderSettingsTab()}
                    </div>
                </div>
            </div>
        `;
    }
    _renderFiltersTab() {
      return b`
            <div class="svf-search-wrapper">
                <span class="svf-search-icon">${o$2(ICON_SEARCH)}</span>
                <input type="text" id="svf-unified-search" class="svf-search-input" placeholder="Filter all domains (e.g. github)..." .value=${this._searchQuery} @input=${(e2) => this._searchQuery = e2.target.value.trim().toLowerCase()}>
                <button type="button" class="svf-clear-btn" style="display: ${this._searchQuery ? "block" : "none"}" @click=${() => {
      this._searchQuery = "";
      this.shadowRoot?.querySelector("#svf-unified-search")?.focus();
    }}>
                    ${o$2(ICON_CLEAR)}
                </button>
            </div>
            ${this._renderSection("liked", "Preferred Domains")}
            ${this._renderSection("disliked", "Disliked Domains")}
        `;
    }
    _renderSection(type, titleText) {
      const domains = type === "liked" ? this.store.liked : this.store.disliked;
      const textValue = type === "liked" ? this._likedText : this._dislikedText;
      const lines = this._getLines(textValue);
      const uniques = new Set(lines);
      const duplicateCount = lines.length - uniques.size;
      let proposalFound = false;
      let activeProposal = null;
      if (!this._searchQuery) {
        const tree = {};
        lines.forEach((dom) => {
          const root = getRegisterableDomain(dom);
          if (!tree[root]) tree[root] = [];
          tree[root].push(dom);
        });
        for (const root of Object.keys(tree)) {
          const subCount = tree[root].length;
          const isWildcardRule = tree[root].includes(`*.${root}`);
          if (subCount >= 3 && !isWildcardRule) {
            activeProposal = root;
            proposalFound = true;
            break;
          }
        }
      }
      const matches = this._searchQuery ? domains.filter((d2) => d2.toLowerCase().includes(this._searchQuery)) : [];
      const badgeText = this._searchQuery ? `${matches.length} match${matches.length !== 1 ? "es" : ""}` : String(domains.length);
      return b`
            <div class="svf-section ${type}">
                <div class="svf-section-header">
                    <div class="svf-label">${titleText}</div>
                    <span class="svf-badge" id="svf-${type}-count">${badgeText}</span>
                </div>
                
                ${!this._searchQuery && proposalFound && activeProposal ? b`
                    <div class="svf-merger-card" id="svf-${type}-proposal-card" style="display: flex">
                        <div class="svf-merger-info">
                            <div class="svf-merger-title">💡 Wildcard Suggestion</div>
                            <div class="svf-merger-desc">${lines.filter((l2) => getRegisterableDomain(l2) === activeProposal).length} subdomains of '${activeProposal}' found. Merge to *.${activeProposal}?</div>
                        </div>
                        <button class="svf-merger-btn" type="button" @click=${() => {
      const rule = `*.${activeProposal}`;
      const filtered = lines.filter((line) => getRegisterableDomain(line) !== activeProposal);
      if (!filtered.includes(rule)) filtered.push(rule);
      this._saveAndReapply(type, filtered);
      if (type === "liked") this._likedText = filtered.join("\n");
      else this._dislikedText = filtered.join("\n");
    }}>Consolidate</button>
                    </div>
                ` : A}
                
                ${this._searchQuery ? b`
                    <div class="svf-filtered-list" id="svf-${type}-filtered-list" style="display: flex">
                        ${matches.length === 0 ? b`
                            <div class="svf-filtered-empty">No matching domains</div>
                        ` : matches.map((match) => {
      const isEditing = this._editingMatches[`${type}:${match}`] !== void 0;
      return b`
                                <div class="svf-filtered-item">
                                    ${isEditing ? b`
                                        <input type="text" class="svf-filtered-input" .value=${this._editingMatches[`${type}:${match}`]}
                                            @blur=${(e2) => this._saveEdit(type, match, e2.target.value)}
                                            @keydown=${(e2) => {
        if (e2.key === "Enter") this._saveEdit(type, match, e2.target.value);
        if (e2.key === "Escape") this._cancelEdit(type, match);
      }}
                                            ${n2((el) => {
        if (el) setTimeout(() => el.focus(), 0);
      })}
                                        >
                                    ` : b`
                                        <span class="svf-filtered-text">${match}</span>
                                        <div class="svf-filtered-actions">
                                            <button type="button" class="svf-filtered-btn" title="Edit" @click=${() => this._startEdit(type, match)}>
                                                ${o$2(ICON_EDIT)}
                                            </button>
                                            <button type="button" class="svf-filtered-btn delete" title="Remove" @click=${() => this._deleteMatch(type, match)}>
                                                ${o$2(ICON_DELETE)}
                                            </button>
                                        </div>
                                    `}
                                </div>
                            `;
    })}
                    </div>
                ` : b`
                        <textarea class="svf-textarea" id="svf-${type}-textarea" placeholder="e.g.\nstackoverflow.com\ngithub.com"
                        style="display: block"
                        .value=${textValue}
                        @input=${(e2) => {
      if (type === "liked") this._likedText = e2.target.value;
      else this._dislikedText = e2.target.value;
    }}
                        @focus=${() => this._isTextareaFocused[type] = true}
                        @blur=${() => {
      this._isTextareaFocused[type] = false;
    }}
                    ></textarea>
                    
                    <div class="svf-textarea-status-row" style="display: flex">
                        <div class="svf-textarea-msg-info" id="svf-${type}-msg-info">
                            ${duplicateCount > 0 ? b`<span style="color: var(--svf-danger)">⚠️ ${duplicateCount} duplicates found (will merge on apply)</span>` : b`${lines.length} domains configured`}
                        </div>
                        <button class="svf-apply-btn" id="svf-${type}-apply" type="button" @click=${() => this._saveAndReapply(type, lines)}>
                            Apply Changes
                        </button>
                    </div>
                `}
            </div>
        `;
    }
    _renderSettingsTab() {
      const accent = this.store.accent;
      const mode = this.store.dislikeMode;
      return b`
            <div class="svf-control-group">
                <div class="svf-label">Dislike Filter Mode</div>
                <div class="svf-segmented">
                    <button class="svf-seg-btn ${mode === "fade" ? "active" : ""}" @click=${() => {
      this.store.setDislikeMode("fade");
      this.scanner.reapply();
    }}>Fade Results</button>
                    <button class="svf-seg-btn ${mode === "hide" ? "active" : ""}" @click=${() => {
      this.store.setDislikeMode("hide");
      this.scanner.reapply();
    }}>Hide (Reveal-on-click)</button>
                </div>
            </div>

            <div class="svf-control-group">
                <div class="svf-label">Theme Accent Color</div>
                <div class="svf-color-picker-row">
                    ${Object.keys(ACCENT_COLORS).map((name) => {
      const acc = ACCENT_COLORS[name];
      return b`
                            <div class="svf-color-dot ${name === accent ? "active" : ""}" data-accent="${name}" style="background-color: ${acc.primary}; --dot-glow: ${acc.dotGlow}" @click=${() => this.store.setAccent(name)}></div>
                        `;
    })}
                </div>
            </div>

            <div class="svf-control-group">
                <div class="svf-label">Preferences</div>
                <div class="svf-switch-row">
                    <div class="svf-switch-label-group">
                        <div class="svf-switch-title">Show floating button</div>
                        <div class="svf-switch-desc">Toggle settings icon visibility in the bottom right</div>
                    </div>
                    <label class="svf-switch">
                        <input type="checkbox" id="svf-pref-show-trigger" .checked=${this.store.showTrigger} @change=${(e2) => this.store.setShowTrigger(e2.target.checked)}>
                        <span class="svf-slider"></span>
                    </label>
                </div>
            </div>

            <div class="svf-io-section">
                <div class="svf-label">Import / Export</div>
                
                <div class="svf-io-row">
                    <button class="svf-io-btn" @click=${this._copyJson}>${o$2(ICON_COPY)} Copy JSON</button>
                    <button class="svf-io-btn" @click=${this._downloadJson}>${o$2(ICON_DOWNLOAD)} Download</button>
                </div>
                
                <div class="svf-io-row">
                    <input type="file" accept=".json,application/json" class="svf-io-file" id="svf-io-file" @change=${this._importFile}>
                    <button class="svf-io-btn primary" @click=${() => this.shadowRoot?.querySelector("#svf-io-file")?.click()}>
                        ${o$2(ICON_IMPORT)} Import File
                    </button>
                </div>
                
                <div class="svf-label">Import from URL</div>
                <form class="svf-io-url-form" @submit=${this._importUrl}>
                    <input type="url" class="svf-io-url-input" id="svf-io-url-input" value="https://gist.githubusercontent.com/quantavil/12880b87fd1ebd497469455d1898088b/raw/63daeaec36bc08e68f94a2db9dc5ec08af6e3ff9/domains.json" placeholder="https://gist.githubusercontent.com/…/domains.json" ?disabled=${this._isFetchingUrl} @input=${(e2) => e2.target.classList.remove("invalid")}>
                    <button type="submit" class="svf-io-btn primary" id="svf-url-fetch-btn" style="flex: 0 0 auto" ?disabled=${this._isFetchingUrl}>${this._isFetchingUrl ? "Fetching..." : "Fetch"}</button>
                </form>
                
                ${this._ioStatus ? b`<div class="svf-io-status ${this._ioStatus.type}" style="display: block">${this._ioStatus.msg}</div>` : b`<div class="svf-io-status" style="display: none"></div>`}
            </div>
        `;
    }
    _showStatus(msg, type) {
      this._ioStatus = { msg, type };
      clearTimeout(this._ioTimeout);
      this._ioTimeout = window.setTimeout(() => {
        this._ioStatus = null;
      }, 3500);
    }
    _copyJson() {
      const json = JSON.stringify(this.store.exportDomains(), null, 2);
      if (typeof GM_setClipboard === "function") {
        GM_setClipboard(json, "text");
        this._showStatus("Copied to clipboard!", "success");
      } else {
        navigator.clipboard.writeText(json).then(() => {
          this._showStatus("Copied to clipboard!", "success");
        }).catch(() => {
          this._showStatus("Failed to copy", "error");
        });
      }
    }
    _downloadJson() {
      const json = JSON.stringify(this.store.exportDomains(), null, 2);
      const blob = new Blob([json], { type: "application/json" });
      const url = URL.createObjectURL(blob);
      const a2 = document.createElement("a");
      a2.href = url;
      a2.download = "svf-domains.json";
      a2.click();
      URL.revokeObjectURL(url);
      this._showStatus("Downloaded svf-domains.json", "success");
    }
    _importFile(e2) {
      const fileInput = e2.target;
      const file = fileInput.files?.[0];
      if (!file) return;
      const reader = new FileReader();
      reader.onload = () => {
        this._applyImport(reader.result, "file");
        fileInput.value = "";
      };
      reader.readAsText(file);
    }
    _importUrl(e2) {
      e2.preventDefault();
      const urlInput = this.shadowRoot?.querySelector("#svf-io-url-input");
      if (!urlInput) return;
      const url = urlInput.value.trim();
      if (!url) return;
      try {
        new URL(url);
        this._isFetchingUrl = true;
        this._fetchAndImport(url, () => {
          this._isFetchingUrl = false;
        });
      } catch {
        urlInput.classList.add("invalid");
      }
    }
    _applyImport(text, source) {
      try {
        const data = parseJsonRobust(text);
        const n3 = this.store.importDomains(data);
        this.scanner.reapply();
        const suffix = source === "url" ? " from URL" : "";
        this._showStatus(`Imported ${n3} new domain${n3 !== 1 ? "s" : ""}${suffix}`, "success");
      } catch {
        const errorMsg = source === "url" ? "Invalid JSON at URL" : "Invalid JSON file";
        this._showStatus(errorMsg, "error");
      }
    }
    _fetchAndImport(url, onDone) {
      const TIMEOUT_MS = 1e4;
      if (typeof GM_xmlhttpRequest === "function") {
        let timedOut = false;
        const timeoutId = setTimeout(() => {
          timedOut = true;
          this._showStatus("Request timed out", "error");
          onDone();
        }, TIMEOUT_MS);
        GM_xmlhttpRequest({
          method: "GET",
          url,
          timeout: TIMEOUT_MS,
          onload: (resp) => {
            if (timedOut) return;
            clearTimeout(timeoutId);
            if (resp.status >= 200 && resp.status < 300) {
              this._applyImport(resp.responseText, "url");
            } else {
              this._showStatus(`Fetch failed: ${resp.status} ${resp.statusText}`, "error");
            }
            onDone();
          },
          onerror: () => {
            if (timedOut) return;
            clearTimeout(timeoutId);
            this._showStatus("Network error fetching URL", "error");
            onDone();
          },
          ontimeout: () => {
            if (timedOut) return;
            clearTimeout(timeoutId);
            this._showStatus("Request timed out", "error");
            onDone();
          }
        });
      } else {
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
        fetch(url, { signal: controller.signal }).then((r2) => {
          if (!r2.ok) throw new Error(String(r2.status));
          return r2.text();
        }).then((text) => {
          this._applyImport(text, "url");
        }).catch((err) => {
          if (err?.name === "AbortError") {
            this._showStatus("Request timed out", "error");
          } else {
            this._showStatus("Failed to fetch or parse URL", "error");
          }
        }).finally(() => {
          clearTimeout(timeoutId);
          onDone();
        });
      }
    }
    _startEdit(type, match) {
      this._editingMatches = { ...this._editingMatches, [`${type}:${match}`]: match };
    }
    _cancelEdit(type, match) {
      if (this._editingMatches[`${type}:${match}`] === void 0) return;
      const next = { ...this._editingMatches };
      delete next[`${type}:${match}`];
      this._editingMatches = next;
    }
    _saveEdit(type, oldMatch, newValRaw) {
      if (this._editingMatches[`${type}:${oldMatch}`] === void 0) return;
      const newVal = newValRaw.trim().toLowerCase();
      if (newVal && newVal !== oldMatch) {
        const domains = type === "liked" ? this.store.liked : this.store.disliked;
        const updated = domains.map((d2) => d2 === oldMatch ? newVal : d2);
        this._saveAndReapply(type, updated);
      }
      this._cancelEdit(type, oldMatch);
    }
    _deleteMatch(type, match) {
      const domains = type === "liked" ? this.store.liked : this.store.disliked;
      const updated = domains.filter((d2) => d2 !== match);
      this._saveAndReapply(type, updated);
    }
    _getLines(text) {
      return text.split("\n").map((l2) => l2.trim().toLowerCase()).filter(Boolean);
    }
    _saveAndReapply(type, domains) {
      const deduped = [...new Set(domains)];
      deduped.sort();
      this.store.setDomains(type, deduped);
      this.scanner.reapply();
    }
    _autoResize(textarea, delay = 0) {
      const resize = () => {
        if (!textarea) return;
        textarea.style.height = "auto";
        textarea.style.height = `${Math.min(Math.max(textarea.scrollHeight + 2, 60), 150)}px`;
      };
      if (delay > 0) setTimeout(resize, delay);
      else resize();
    }
  };
  PanelElement.styles = r$6(PANEL_CSS);
  __decorateClass$1([
    n$2({ attribute: false })
  ], PanelElement.prototype, "store", 2);
  __decorateClass$1([
    n$2({ attribute: false })
  ], PanelElement.prototype, "scanner", 2);
  __decorateClass$1([
    r$2()
  ], PanelElement.prototype, "_isOpen", 2);
  __decorateClass$1([
    r$2()
  ], PanelElement.prototype, "_activeTab", 2);
  __decorateClass$1([
    r$2()
  ], PanelElement.prototype, "_searchQuery", 2);
  __decorateClass$1([
    r$2()
  ], PanelElement.prototype, "_ioStatus", 2);
  __decorateClass$1([
    r$2()
  ], PanelElement.prototype, "_isFetchingUrl", 2);
  __decorateClass$1([
    r$2()
  ], PanelElement.prototype, "_likedText", 2);
  __decorateClass$1([
    r$2()
  ], PanelElement.prototype, "_dislikedText", 2);
  __decorateClass$1([
    r$2()
  ], PanelElement.prototype, "_editingMatches", 2);
  __decorateClass$1([
    e$2(".svf-close-btn")
  ], PanelElement.prototype, "_closeBtn", 2);
  __decorateClass$1([
    e$2("#svf-liked-textarea")
  ], PanelElement.prototype, "_likedTextarea", 2);
  __decorateClass$1([
    e$2("#svf-disliked-textarea")
  ], PanelElement.prototype, "_dislikedTextarea", 2);
  PanelElement = __decorateClass$1([
    t$1("svf-panel")
  ], PanelElement);
  const TRIGGER_CSS = ":host{user-select:none;-webkit-user-select:none}.svf-trigger{position:fixed;bottom:24px;right:24px;width:48px;height:48px;border-radius:50%;background:#121216d9;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,.08);box-shadow:0 8px 32px #0000004d,0 0 0 1px #ffffff05;color:#ffffffb3;cursor:pointer;display:grid;place-items:center;z-index:2147483640;transition:transform .25s cubic-bezier(.25,.8,.25,1),border-color .2s ease,box-shadow .2s ease,color .2s ease;will-change:transform;pointer-events:auto;padding:0;outline:none}.svf-trigger:hover{transform:translateY(-3px) scale(1.05);border-color:var(--svf-primary);box-shadow:0 12px 36px var(--svf-primary-glow),0 0 0 1px var(--svf-primary-glow);color:var(--svf-primary)}.svf-trigger:active{transform:translateY(0) scale(.95)}.svf-trigger svg{transition:transform .4s ease}.svf-trigger:hover svg{transform:rotate(30deg) scale(1.05)}";
  var __defProp = Object.defineProperty;
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  var __decorateClass = (decorators, target, key, kind) => {
    var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
    for (var i3 = decorators.length - 1, decorator; i3 >= 0; i3--)
      if (decorator = decorators[i3])
        result = (kind ? decorator(target, key, result) : decorator(result)) || result;
    if (kind && result) __defProp(target, key, result);
    return result;
  };
  let TriggerElement = class extends i$1 {
    constructor() {
      super(...arguments);
      this._show = false;
    }
    updated(changedProperties) {
      super.updated(changedProperties);
      if (this.store) {
        applyAccentVariables(this, this.store.accent);
      }
    }
    connectedCallback() {
      super.connectedCallback();
      this._unsubscribe?.();
      if (this.store) {
        this._show = this.store.showTrigger;
        this._unsubscribe = this.store.subscribe(() => {
          this._show = this.store.showTrigger;
        });
      }
    }
    disconnectedCallback() {
      super.disconnectedCallback();
      this._unsubscribe?.();
    }
    render() {
      return b`
            <button
                class="svf-trigger"
                aria-label="Open Better Search Settings"
                title="Better Search Settings"
                style="display: ${this._show ? "grid" : "none"}"
                @click=${this._handleClick}
            >
                ${o$2(ICON_TRIGGER)}
            </button>
        `;
    }
    _handleClick(e2) {
      e2.stopPropagation();
      if (this.panel) {
        if (this.panel.isOpen()) {
          this.panel.close();
        } else {
          this.panel.open();
        }
      }
    }
  };
  TriggerElement.styles = r$6(TRIGGER_CSS);
  __decorateClass([
    n$2({ attribute: false })
  ], TriggerElement.prototype, "store", 2);
  __decorateClass([
    n$2({ attribute: false })
  ], TriggerElement.prototype, "panel", 2);
  __decorateClass([
    r$2()
  ], TriggerElement.prototype, "_show", 2);
  TriggerElement = __decorateClass([
    t$1("svf-trigger")
  ], TriggerElement);
  const GOOGLE_INTERNAL_HOSTS = /* @__PURE__ */ new Set([
    "www.google.com",
    "accounts.google.com",
    "maps.google.com",
    "webcache.googleusercontent.com"
  ]);
  function extractUrl$4(item) {
    const candidates = item.querySelectorAll("a[href]");
    for (const a2 of candidates) {
      const href = a2.href;
      if (!href || href.startsWith("javascript") || href.startsWith("#")) continue;
      try {
        const u2 = new URL(href);
        if (GOOGLE_INTERNAL_HOSTS.has(u2.hostname) || u2.pathname.startsWith("/search") || u2.hostname.includes("gstatic.")) {
          continue;
        }
        return href;
      } catch (err) {
        console.debug("[SVF] Skipped malformed Google result URL:", href, err);
      }
    }
    return null;
  }
  const googleEngine = {
    name: "google",
    // The element that contains all organic .g cards — stable across layout changes
    containerSelector: '#search, #rso, [data-async-context] [id="search"]',
    // Each organic result card
    itemSelector: ".g, .tF2Cxc, .MjjYud",
    extractUrl: extractUrl$4
  };
  function decodeBingUrl(href) {
    try {
      if (!href.includes("bing.com/ck/a")) return href;
      const uObj = new URL(href);
      let u2 = uObj.searchParams.get("u");
      if (u2) {
        u2 = u2.replace(/^a[0-9]/, "");
        let normalized = u2.replace(/-/g, "+").replace(/_/g, "/");
        normalized = normalized.padEnd(normalized.length + (4 - normalized.length % 4) % 4, "=");
        const decoded = decodeURIComponent(
          atob(normalized).split("").map((c2) => "%" + ("00" + c2.charCodeAt(0).toString(16)).slice(-2)).join("")
        );
        if (decoded && decoded.startsWith("http")) {
          return decoded;
        }
      }
    } catch (err) {
      console.debug("[SVF] Failed to decode Bing URL:", href, err);
    }
    return null;
  }
  function extractUrl$3(item) {
    const h2a = item.querySelector("h2 a[href]");
    if (h2a?.href) {
      const decoded = decodeBingUrl(h2a.href);
      if (decoded) return decoded;
    }
    const cite = item.querySelector("cite");
    if (cite?.textContent) {
      let t2 = cite.textContent.trim();
      if (t2.includes("›")) {
        t2 = t2.split("›")[0].trim();
      }
      if (t2.startsWith("http")) return t2;
      if (!t2.includes(".")) return null;
      return "https://" + t2;
    }
    return null;
  }
  const bingEngine = {
    name: "bing",
    containerSelector: "#b_results",
    itemSelector: ".b_algo",
    extractUrl: extractUrl$3
  };
  function extractUrl$2(item) {
    const titleA = item.querySelector(
      'a[data-testid="result-title-a"], h2 a[href], a.result__a[href]'
    );
    let url = null;
    if (titleA?.href) {
      url = titleA.href;
    } else {
      for (const a2 of item.querySelectorAll("a[href]")) {
        const href = a2.href;
        if (!href || href.startsWith("#") || href.startsWith("javascript")) continue;
        if (!href.includes("duckduckgo.com") || href.includes("/l/")) {
          url = href;
          break;
        }
      }
    }
    if (url && url.includes("/l/")) {
      try {
        const u2 = new URL(url);
        const uddg = u2.searchParams.get("uddg");
        if (uddg) {
          return decodeURIComponent(uddg);
        }
      } catch (err) {
        console.debug("[SVF] Failed to parse DDG redirect URL:", url, err);
      }
    }
    return url;
  }
  const ddgEngine = {
    name: "ddg",
    containerSelector: ".react-results--main, #links, .serp__results",
    itemSelector: 'article[data-testid="result"], .result.results_links_deep, .nrn-react-div[data-nr]',
    extractUrl: extractUrl$2
  };
  function extractUrl$1(item) {
    const a2 = item.querySelector(
      "a.result-header[href], a.svelte-1dh4yef[href], .snippet-title a[href]"
    );
    if (a2?.href) return a2.href;
    for (const link of item.querySelectorAll("a[href]")) {
      const h2 = link.href;
      if (h2 && !h2.includes("search.brave.com") && !h2.startsWith("#")) return h2;
    }
    return null;
  }
  const braveEngine = {
    name: "brave",
    containerSelector: "#results, .results",
    itemSelector: ".snippet[data-pos], .result[data-pos]",
    extractUrl: extractUrl$1
  };
  function extractUrl(item) {
    const a2 = item.querySelector(
      "a.link.organic__url[href], h2 a.link[href], .OrganicTitle a[href]"
    );
    if (a2?.href) return a2.href;
    for (const link of item.querySelectorAll("a[href]")) {
      const h2 = link.href;
      if (!h2 || h2.startsWith("#") || h2.startsWith("javascript")) continue;
      if (!h2.includes("yandex.") && !h2.includes("ya.ru")) return h2;
    }
    return null;
  }
  const yandexEngine = {
    name: "yandex",
    containerSelector: '.content__left, #search-result, [data-layout-part="cl"], ul[id^="search-"]',
    itemSelector: ".serp-item.organic, li.serp-item[data-cid], li:has(.Organic), .Organic",
    extractUrl,
    shouldActivate: (url) => url.pathname.startsWith("/search")
  };
  const ENGINES = [
    { pattern: /(?:^|\.)google\.[a-z]{2,6}(?:\.[a-z]{2,3})?$/i, config: googleEngine },
    { pattern: /(?:^|\.)bing\.com$/i, config: bingEngine },
    { pattern: /(?:^|\.)duckduckgo\.com$/i, config: ddgEngine },
    { pattern: /(?:^|\.)search\.brave\.com$/i, config: braveEngine },
    { pattern: /(?:^|\.)(?:yandex\.[a-z]{2,}|ya\.ru)$/i, config: yandexEngine }
  ];
  function detectEngine() {
    const host = window.location.hostname;
    for (const { pattern, config } of ENGINES) {
      if (pattern.test(host)) {
        if (config.shouldActivate && !config.shouldActivate(new URL(window.location.href))) {
          continue;
        }
        return config;
      }
    }
    return null;
  }
  let historyHooked = false;
  function hookHistoryEvents() {
    if (historyHooked) return;
    historyHooked = true;
    const patch = (type) => {
      const orig = history[type];
      history[type] = function(...args) {
        const res = orig.apply(this, args);
        window.dispatchEvent(new Event("svf-location-change"));
        return res;
      };
    };
    patch("pushState");
    patch("replaceState");
  }
  class Controller {
    constructor() {
      this._store = null;
      this._styleSheet = null;
      this._scanner = null;
      this._hoverOverlay = null;
      this._panel = null;
      this._trigger = null;
      this._shadowHost = null;
      this._abortController = null;
      this._engine = null;
      this._attachInterval = null;
      this._locationTimeout = null;
      this._onLocationChange = this._onLocationChange.bind(this);
    }
    init() {
      const globalKey = "__svf_controller";
      const prevController = window[globalKey];
      if (prevController && prevController !== this) {
        try {
          prevController.destroy();
        } catch (err) {
          console.error("[SVF] Error destroying previous controller instance:", err);
        }
      }
      this.destroy();
      window[globalKey] = this;
      try {
        this._abortController = new AbortController();
        const signal = this._abortController.signal;
        this._engine = detectEngine();
        if (!this._engine) {
          return;
        }
        this._store = new Store();
        this._styleSheet = new FilterStyleSheet(this._store);
        this._scanner = new Scanner(this._store, this._engine, signal);
        this._shadowHost = document.createElement("div");
        this._shadowHost.id = "svf-shadow-host";
        this._shadowHost.style.position = "fixed";
        this._shadowHost.style.zIndex = "2147483644";
        this._shadowHost.style.pointerEvents = "none";
        this._shadowHost.style.userSelect = "none";
        this._shadowHost.style.webkitUserSelect = "none";
        const shadow = this._shadowHost.attachShadow({ mode: "open" });
        (document.body || document.documentElement).appendChild(this._shadowHost);
        this._panel = document.createElement("svf-panel");
        this._panel.store = this._store;
        this._panel.scanner = this._scanner;
        shadow.appendChild(this._panel);
        this._trigger = document.createElement("svf-trigger");
        this._trigger.store = this._store;
        this._trigger.panel = this._panel;
        shadow.appendChild(this._trigger);
        this._hoverOverlay = document.createElement("svf-hover-overlay");
        this._hoverOverlay.store = this._store;
        this._hoverOverlay.scanner = this._scanner;
        this._hoverOverlay.signal = signal;
        shadow.appendChild(this._hoverOverlay);
        if (typeof GM_registerMenuCommand === "function") {
          GM_registerMenuCommand("Better Search Settings", () => {
            this._panel?.open();
          });
        }
        if (!this._scanner?.attach()) {
          this._startAttachInterval();
        }
        window.addEventListener("popstate", this._onLocationChange, { signal });
        window.addEventListener("svf-location-change", this._onLocationChange, { signal });
        hookHistoryEvents();
      } catch (err) {
        this.destroy();
        throw err;
      }
    }
    destroy() {
      if (this._locationTimeout) {
        clearTimeout(this._locationTimeout);
        this._locationTimeout = null;
      }
      if (this._attachInterval) {
        clearInterval(this._attachInterval);
        this._attachInterval = null;
      }
      if (this._abortController) {
        this._abortController.abort();
        this._abortController = null;
      }
      if (this._scanner) {
        this._scanner.destroy();
        this._scanner = null;
      }
      if (this._hoverOverlay) {
        this._hoverOverlay.remove();
        this._hoverOverlay = null;
      }
      if (this._styleSheet) {
        this._styleSheet.destroy();
        this._styleSheet = null;
      }
      if (this._shadowHost) {
        this._shadowHost.remove();
        this._shadowHost = null;
      }
      if (this._store) {
        this._store.destroy();
        this._store = null;
      }
      this._panel = null;
      this._trigger = null;
      this._engine = null;
      const globalKey = "__svf_controller";
      if (window[globalKey] === this) {
        window[globalKey] = null;
      }
    }
    _onLocationChange() {
      if (this._locationTimeout) {
        clearTimeout(this._locationTimeout);
      }
      this._locationTimeout = setTimeout(() => {
        this._locationTimeout = null;
        const nextEngine = detectEngine();
        if (!nextEngine) {
          this.destroy();
          return;
        }
        if (!this._engine || this._engine.name !== nextEngine.name) {
          this.init();
        } else {
          if (!this._scanner?.attach()) {
            this._startAttachInterval();
          }
        }
      }, 150);
    }
    _startAttachInterval() {
      this._clearAttachInterval();
      let attempts = 0;
      this._attachInterval = setInterval(() => {
        if (this._scanner?.attach()) {
          this._clearAttachInterval();
        } else if (++attempts > 40) {
          this._clearAttachInterval();
        }
      }, 500);
    }
    _clearAttachInterval() {
      if (this._attachInterval) {
        clearInterval(this._attachInterval);
        this._attachInterval = null;
      }
    }
  }
  try {
    const controller = new Controller();
    controller.init();
  } catch (err) {
    console.error("[SVF] Init error:", err);
  }

})();