ASSs MultiTools

удобный мультитул для AnimeSSS

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.

(У мене вже є менеджер скриптів, дайте мені встановити його!)

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         ASSs MultiTools
// @namespace    https://animesss.com/
// @version      1.3
// @description  удобный мультитул для AnimeSSS
// @author       SoulUA
// @license      MIT
// @match        *://animesss.tv/*
// @match        *://animesss.com/*
// @run-at       document-start
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_registerMenuCommand
// ==/UserScript==

(function () {
  'use strict';

  const SCRIPT_NAME = 'ASS TM Combo';
  const SETTING_PREFIX = 'ass_tm_combo_';

  const DEFAULT_PROFILE_BUTTONS = [
    { id: 'need', enabled: true, text: '', icon: 'fal fa-search', url: '/user/cards/need/?name={USERNAME}' },
    { id: 'in-list', enabled: true, text: '', icon: 'fal fa-heart', url: '/user/cards/?name={USERNAME}&in_list=1' },
    { id: 'unlocked', enabled: true, text: '', icon: 'fal fa-unlock', url: '/user/cards/?name={USERNAME}&locked=0' },
    { id: 'club', enabled: true, text: '', icon: 'fal fa-users', url: '/clubs/{CLUB_ID}/' },
    ...['s_plus', 's', 'a_plus', 'a', 'b_plus', 'b', 'c_plus', 'c', 'd_plus', 'd', 'e_plus', 'e', 'ass'].map((rank) => ({
      id: `rank-${rank}`,
      enabled: true,
      text: rank.replace('_plus', '+').toUpperCase(),
      icon: '',
      url: `/user/cards/?name={USERNAME}&locked=0&rank=${rank}`,
    })),
  ];

  const DEFAULT_QN_LINKS = [
    { id: 'qn-1', title: 'Лента карт', url: '/cards' },
    { id: 'qn-2', title: 'Паки карт', url: '/cards/pack/' },
    { id: 'qn-3', title: 'Трейды', url: '/trades/offers/' }
  ];

  const FONT_OPTIONS = [
    { value: 'system-ui, sans-serif', label: 'Системный (По умолчанию)' },
    { value: 'Georgia, serif', label: 'Georgia (Элегантный)' },
    { value: '"Palatino Linotype", "Book Antiqua", serif', label: 'Palatino (Книжный)' },
    { value: '"Trebuchet MS", sans-serif', label: 'Trebuchet MS (Компактный)' },
    { value: 'Impact, sans-serif', label: 'Impact (Массивный)' },
    { value: '"Arial Black", sans-serif', label: 'Arial Black (Жирный)' },
    { value: '"Comic Sans MS", cursive', label: 'Comic Sans (Неформальный)' },
    { value: '"Courier New", monospace', label: 'Courier New (Печатная машинка)' },
    { value: '"Lucida Console", monospace', label: 'Lucida Console (Терминал)' }
  ];

  function cloneValue(value) {
    try { return JSON.parse(JSON.stringify(value)); } catch (_) { return value; }
  }

  const DEFAULTS = {
    profileButtons: true,
    cardNeedButtons: true,
    modalStarButton: true,

    takeCinemaStone: false,
    takeSnowStone: false,
    takeHeavenlyStone: false,
    stoneBaseDelayMs: 900,
    stoneGrowthDelayMs: 250,

    quickNavEnabled: true,
    qnBgColor: '#121212',
    qnBgOpacity: 0.0,
    qnBlur: 0,
    qnBtnBgColor: '#212121',
    qnBtnTextColor: '#e0e0e0',
    qnFontFamily: 'system-ui, sans-serif',
    qnBtnFontSize: 13,
    qnBtnPadY: 10,
    qnBtnPadX: 16,

    profBtnBgColor: '#2b2b2b',
    profBtnHoverBgColor: '#9e294f',
    profBtnTextColor: '#e0e0e0',
    profFontFamily: 'system-ui, sans-serif',
    profBtnFontSize: 12,
    profBtnPadX: 8,
    profBtnHeight: 28,

    profileButtonsConfig: cloneValue(DEFAULT_PROFILE_BUTTONS),
    qnLinks: cloneValue(DEFAULT_QN_LINKS),
  };

  const state = {};
  const clickedCinemaCodes = new Set();
  let qnResizeObserver = null;
  let qnIsScrollListenerAttached = false;
  let stoneClickQueue = 0;

  // ---------------------------------------------------------------------------
  // Core Utilities & Toast UI
  // ---------------------------------------------------------------------------

  function showToast(msg) {
    let toast = document.getElementById('ass-tm-toast');
    if (!toast) {
      toast = document.createElement('div');
      toast.id = 'ass-tm-toast';
      toast.style.cssText = 'position:fixed; bottom:20px; left:50%; transform:translateX(-50%); background:rgba(0,0,0,0.85); color:#fff; padding:10px 20px; border-radius:12px; z-index:9999999; font-size:13px; font-family:sans-serif; pointer-events:none; opacity:0; transition:opacity 0.3s ease; box-shadow: 0 4px 12px rgba(0,0,0,0.3); font-weight: 600; text-align: center; white-space: nowrap;';
      document.body.appendChild(toast);
    }
    toast.textContent = msg;
    toast.style.opacity = '1';
    clearTimeout(toast.timer);
    toast.timer = setTimeout(() => { toast.style.opacity = '0'; }, 3000);
  }

  function gmGet(key, fallback) {
    try { if (typeof GM_getValue === 'function') return GM_getValue(SETTING_PREFIX + key, fallback); } catch (_) { }
    try { const raw = localStorage.getItem(SETTING_PREFIX + key); return raw == null ? fallback : JSON.parse(raw); } catch (_) { return fallback; }
  }

  function gmSet(key, value) {
    state[key] = value;
    try { if (typeof GM_setValue === 'function') { GM_setValue(SETTING_PREFIX + key, value); return; } } catch (_) { }
    try { localStorage.setItem(SETTING_PREFIX + key, JSON.stringify(value)); } catch (_) { }
  }

  function loadSettings() {
    for (const [key, value] of Object.entries(DEFAULTS)) {
      state[key] = gmGet(key, cloneValue(value));
      if ((key === 'profileButtonsConfig' || key === 'qnLinks') && !Array.isArray(state[key])) {
        state[key] = cloneValue(value);
      }
    }
  }

  function onBodyReady(fn) {
    if (document.body) return fn();
    const timer = setInterval(() => { if (!document.body) return; clearInterval(timer); fn(); }, 50);
  }

  // ---------------------------------------------------------------------------
  // Styles
  // ---------------------------------------------------------------------------

  function injectStyle() {
    const css = `
      .user-card-buttons { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-left: 0; width: 100%; justify-content: flex-start; }
      .user-card-buttons a {
        display: inline-flex; align-items: center; justify-content: center; min-width: 28px;
        height: var(--prof-btn-h, 28px); padding: 0 var(--prof-btn-padx, 8px);
        border-radius: 8px; background: var(--prof-btn-bg, rgba(255,255,255,.08));
        color: var(--prof-btn-text, inherit); font-family: var(--prof-btn-font, system-ui, sans-serif);
        text-decoration: none; font-size: var(--prof-btn-fs, 12px); font-weight: 600; line-height: 1; transition: background-color 0.2s ease;
      }
      .user-card-buttons a:hover { background: var(--prof-btn-hover, rgba(158,41,79,.85)); }
      .as-ext-star-meta-item { flex: 0 0 auto; }

      #ass-tm-settings-fab { display: inline-flex; align-items: center; justify-content: center; min-width: 38px; height: 38px; border: 0; border-radius: 12px; background: rgba(255,255,255,.10); color: #fff; font: 18px/1 Arial, sans-serif; cursor: pointer; text-decoration: none; box-sizing: border-box; transition: background 0.2s; }
      #ass-tm-settings-fab:hover { background: rgba(158,41,79,.92); }
      #ass-tm-settings-fab.ass-tm-settings-fallback { position: fixed; z-index: 999998; right: 12px; top: 88px; box-shadow: 0 8px 24px rgba(0,0,0,.28); }

      .ass-tm-settings-backdrop { position: fixed; inset: 0; z-index: 999999; display: none; align-items: center; justify-content: center; background: rgba(0,0,0,.55); padding: 14px; box-sizing: border-box; backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px); }
      .ass-tm-settings-backdrop.ass-tm-settings-open { display: flex; }
      .ass-tm-settings-panel { width: min(760px, 100%); max-height: min(86vh, 760px); overflow: auto; border-radius: 14px; background: #171719; color: #f4f4f4; box-shadow: 0 18px 56px rgba(0,0,0,.45); font: 13px/1.35 Arial, sans-serif; }
      .ass-tm-settings-head { position: sticky; top: 0; z-index: 10; display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 12px 14px; background: #202024; border-bottom: 1px solid rgba(255,255,255,.08); }
      .ass-tm-settings-title { font-weight: 700; font-size: 15px; }
      .ass-tm-settings-close { border: 0; border-radius: 8px; background: rgba(255,255,255,.08); color: #fff; min-width: 34px; height: 32px; cursor: pointer; transition: background 0.2s; }
      .ass-tm-settings-close:hover { background: rgba(255,255,255,.15); }
      .ass-tm-settings-body { padding: 12px 14px 16px; }

      .ass-tm-settings-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
      .ass-tm-settings-section { border: 1px solid rgba(255,255,255,.08); border-radius: 12px; padding: 10px; background: rgba(255,255,255,.035); }
      .ass-tm-settings-section h3 { margin: 0 0 8px; font-size: 13px; color: #fff; }
      .ass-tm-setting-row { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 8px; min-height: 34px; border-top: 1px solid rgba(255,255,255,.06); padding: 7px 0; }
      .ass-tm-setting-row:first-of-type { border-top: 0; }
      .ass-tm-setting-label { flex: 1 1 auto; min-width: 50%; }

      .ass-tm-setting-row input[type="number"], .ass-tm-setting-row input[type="text"], .ass-tm-editor-row input[type="text"] { border: 1px solid rgba(255,255,255,.14); border-radius: 8px; background: #0f0f11; color: #fff; padding: 6px 8px; box-sizing: border-box; }
      .ass-tm-setting-row select {
      border: 1px solid rgba(255,255,255,.14);
      border-radius: 8px;
      background-color: #0f0f11 !important;
      background-image: none !important;
      color: #fff;
      padding: 6px 36px 6px 8px;
      box-sizing: border-box;
      width: 140px;
      outline: none;
      font-size: 12px;
      cursor: pointer;
      appearance: none;
      -webkit-appearance: none;
      -moz-appearance: none;
     }
      .ass-tm-setting-row {
      position: relative;
     }
      .ass-tm-setting-row select {
      background-repeat: no-repeat !important;
      background-size: 0 0 !important;
     }
      .ass-tm-setting-row input[type="number"] { width: 104px; }
      .ass-tm-setting-row input[type="color"] { background: none; border: none; width: 32px; height: 32px; cursor: pointer; padding: 0; border-radius: 6px; }
      .ass-tm-setting-row input[type="checkbox"], .ass-tm-editor-row input[type="checkbox"] { width: 18px; height: 18px; accent-color: #9e294f; cursor: pointer; margin: 0; }
      .ass-tm-editor { margin-top: 12px; border: 1px solid rgba(255,255,255,.08); border-radius: 12px; padding: 10px; background: rgba(255,255,255,.035); }
      .ass-tm-editor h3 { margin: 0 0 8px; font-size: 13px; }
      .ass-tm-editor-list { display: flex; flex-direction: column; gap: 8px; }

      /* ========================================================= */
      /* --- ПУЛЕНЕПРОБИВАЕМЫЙ CSS (из скриншотов) --- */
      .ass-tm-editor-row {
        display: grid;
        gap: 8px;
        padding: 10px;
        border-radius: 8px;
        background: rgba(255,255,255,.04);
        border: 1px solid rgba(255,255,255,.05);
        align-items: center;

        grid-template-columns: 24px minmax(0, 1fr) minmax(0, 1fr) 34px;
        grid-template-rows: auto auto;
      }

      .ass-tm-editor-row > [data-f="en"] {
        grid-column: 1;
        grid-row: 1;
        justify-self: center;
        margin: 0;
      }

      .ass-tm-editor-row > [data-f="txt"] {
        grid-column: 2;
        grid-row: 1;
        width: 100%;
        min-width: 0;
        margin: 0;
      }

      .ass-tm-editor-row > [data-f="icn"] {
        grid-column: 3;
        grid-row: 1;
        width: 100%;
        min-width: 0;
        margin: 0;
      }

      .ass-tm-editor-row > .ass-tm-editor-remove {
        grid-column: 4;
        grid-row: 1;
        width: 100%;
        height: 34px;
        margin: 0;
        border: 0;
        border-radius: 8px;
        background: rgba(255,255,255,.08);
        color: #fff;
        cursor: pointer;
        transition: background 0.2s;
      }

      .ass-tm-editor-row > .ass-tm-editor-remove:hover {
        background: rgba(190,40,40,.9);
      }

      .ass-tm-editor-row > [data-f="url"] {
        grid-column: 1 / -1;
        grid-row: 2;
        width: 100%;
        min-width: 0;
        margin: 0;
      }

      /* Стили для ссылок Quick Nav (у них нет галочки и иконки) */
      .ass-tm-editor-row.qn-link {
        grid-template-columns: minmax(0, 1fr) 34px;
      }
      .ass-tm-editor-row.qn-link > [data-f="ttl"] {
        grid-column: 1;
        grid-row: 1;
        width: 100%;
        min-width: 0;
        margin: 0;
      }
      .ass-tm-editor-row.qn-link > .ass-tm-editor-remove {
        grid-column: 2;
        grid-row: 1;
      }

      @media (min-width: 681px) {
        .ass-tm-editor-row {
          grid-template-columns: 24px 1fr 1fr 2fr 34px;
          grid-template-rows: auto;
          padding: 6px;
          background: rgba(255,255,255,.02);
          border: none;
        }

        .ass-tm-editor-row > [data-f="en"] {
          grid-column: 1;
          grid-row: 1;
        }

        .ass-tm-editor-row > [data-f="txt"] {
          grid-column: 2;
          grid-row: 1;
        }

        .ass-tm-editor-row > [data-f="icn"] {
          grid-column: 3;
          grid-row: 1;
        }

        .ass-tm-editor-row > .ass-tm-editor-remove {
          grid-column: 5;
          grid-row: 1;
        }

        .ass-tm-editor-row > [data-f="url"] {
          grid-column: 4;
          grid-row: 1;
        }

        .ass-tm-editor-row.qn-link {
          grid-template-columns: 1fr 2fr 34px;
        }
        .ass-tm-editor-row.qn-link > [data-f="ttl"] {
          grid-column: 1;
          grid-row: 1;
        }
        .ass-tm-editor-row.qn-link > [data-f="url"] {
          grid-column: 2;
          grid-row: 1;
        }
        .ass-tm-editor-row.qn-link > .ass-tm-editor-remove {
          grid-column: 3;
          grid-row: 1;
        }
      }

      /* Mobile overrides for standard settings */
      @media (max-width: 680px) {
        .ass-tm-settings-grid { grid-template-columns: 1fr; }
        .ass-tm-settings-backdrop { padding: 8px; }
        .ass-tm-settings-panel { max-height: 95vh; }
        .ass-tm-settings-body { padding: 12px 10px; }

        .ass-tm-setting-row { flex-direction: column; align-items: flex-start; gap: 6px; }
        .ass-tm-setting-row input[type="number"], .ass-tm-setting-row input[type="text"], .ass-tm-setting-row select {
          flex: 0 0 100%; max-width: none; width: 100%; margin-top: 4px;
        }
      }
      /* ========================================================= */

      .ass-tm-editor-actions, .ass-tm-settings-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
      .ass-tm-settings-actions { margin-top: 12px; }
      .ass-tm-editor-actions button, .ass-tm-settings-actions button { border: 0; border-radius: 9px; background: rgba(255,255,255,.1); color: #fff; padding: 8px 10px; cursor: pointer; font-weight: 600; transition: background 0.2s;}
      .ass-tm-editor-actions button:hover, .ass-tm-settings-actions button:hover { background: rgba(158,41,79,.85); }
      .ass-tm-save-status { margin-top: 10px; min-height: 16px; color: rgba(255,255,255,.62); font-size: 11px; }

      /* Quick Nav UI Styles */
      #custom-quick-nav {
        display: flex; gap: 8px; padding: 12px 16px; overflow-x: auto; white-space: nowrap; scrollbar-width: none;
        -ms-overflow-style: none; border-bottom: none; box-shadow: none; position: fixed; box-sizing: border-box;
        z-index: 998; justify-content: safe center; pointer-events: none; transition: background-color 0.3s ease, backdrop-filter 0.3s ease;
      }
      #custom-quick-nav > * { pointer-events: auto; }
      #custom-quick-nav::-webkit-scrollbar { display: none; }
      #aqn-spacer { width: 100%; display: block; flex-shrink: 0; }

      .custom-nav-btn {
        background-color: var(--btn-bg, #212121); color: var(--btn-text, #e0e0e0); font-family: var(--btn-font, system-ui, sans-serif);
        font-size: var(--btn-fs, 13px); padding: var(--btn-pad, 10px 16px); text-decoration: none; border-radius: 6px;
        font-weight: 600; letter-spacing: 0.5px; transition: opacity 0.2s ease; flex-shrink: 0; border: none; cursor: pointer;
        display: flex; align-items: center; justify-content: center; text-align: center; box-shadow: 0 2px 5px rgba(0,0,0,0.5);
      }
      .custom-nav-btn:hover, .custom-nav-btn:active { opacity: 0.8; }
    `;
    const style = document.createElement('style');
    style.id = 'ass-tm-opt-combo-style';
    style.textContent = css;
    (document.head || document.documentElement).appendChild(style);
  }

  // ---------------------------------------------------------------------------
  // Profile & Quick Nav Visual Styling Engine
  // ---------------------------------------------------------------------------

  function hexToRgb(hex) {
    let c;
    if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) {
      c = hex.substring(1).split('');
      if (c.length === 3) c = [c[0], c[0], c[1], c[1], c[2], c[2]];
      c = '0x' + c.join('');
      return [(c >> 16) & 255, (c >> 8) & 255, c & 255].join(',');
    }
    return '18,18,18';
  }

  function applyQnVisualSettings() {
    const nav = document.getElementById('custom-quick-nav');
    if (!nav) return;
    const rgb = hexToRgb(state.qnBgColor || '#121212');
    nav.style.backgroundColor = `rgba(${rgb}, ${state.qnBgOpacity})`;
    nav.style.backdropFilter = `blur(${state.qnBlur}px)`;
    nav.style.webkitBackdropFilter = `blur(${state.qnBlur}px)`;
    nav.style.setProperty('--btn-bg', state.qnBtnBgColor || '#212121');
    nav.style.setProperty('--btn-text', state.qnBtnTextColor || '#e0e0e0');
    nav.style.setProperty('--btn-font', state.qnFontFamily || 'system-ui, sans-serif');
    nav.style.setProperty('--btn-fs', `${state.qnBtnFontSize || 13}px`);
    nav.style.setProperty('--btn-pad', `${state.qnBtnPadY || 10}px ${state.qnBtnPadX || 16}px`);
  }

  function applyProfVisualSettings() {
    const root = document.documentElement;
    root.style.setProperty('--prof-btn-bg', state.profBtnBgColor || 'rgba(255,255,255,.08)');
    root.style.setProperty('--prof-btn-hover', state.profBtnHoverBgColor || 'rgba(158,41,79,.85)');
    root.style.setProperty('--prof-btn-text', state.profBtnTextColor || 'inherit');
    root.style.setProperty('--prof-btn-font', state.profFontFamily || 'system-ui, sans-serif');
    root.style.setProperty('--prof-btn-fs', `${state.profBtnFontSize || 12}px`);
    root.style.setProperty('--prof-btn-padx', `${state.profBtnPadX || 8}px`);
    root.style.setProperty('--prof-btn-h', `${state.profBtnHeight || 28}px`);
  }

  function renderQnButtons() {
    const nav = document.getElementById('custom-quick-nav');
    if (!nav) return;
    nav.innerHTML = '';
    const fragment = document.createDocumentFragment();
    (state.qnLinks || []).forEach(link => {
      if (!link.url || !link.title) return;
      const btn = document.createElement('a');
      btn.href = link.url;
      btn.textContent = link.title;
      btn.className = 'custom-nav-btn';
      fragment.appendChild(btn);
    });
    nav.appendChild(fragment);
  }

  function setupQnPositioning(targetElement, navContainer, spacer) {
    if (qnResizeObserver) qnResizeObserver.disconnect();
    const updateStickyPosition = () => {
      if (!document.body.contains(targetElement) || !document.body.contains(navContainer)) return;
      const rect = targetElement.getBoundingClientRect();
      navContainer.style.top = `${rect.bottom}px`;
      navContainer.style.left = `${rect.left}px`;
      navContainer.style.width = `${rect.width}px`;
      if (spacer) spacer.style.height = `${navContainer.offsetHeight}px`;
    };
    updateStickyPosition();
    if (window.ResizeObserver) {
      qnResizeObserver = new ResizeObserver(() => requestAnimationFrame(updateStickyPosition));
      qnResizeObserver.observe(targetElement);
      qnResizeObserver.observe(navContainer);
    }
    if (!qnIsScrollListenerAttached) {
      let ticking = false;
      window.addEventListener('scroll', () => {
        if (!ticking) { window.requestAnimationFrame(() => { updateStickyPosition(); ticking = false; }); ticking = true; }
      }, { passive: true });
      qnIsScrollListenerAttached = true;
    }
  }

  function injectQuickNav() {
    const existingNav = document.getElementById('custom-quick-nav');
    const existingSpacer = document.getElementById('aqn-spacer');

    if (!state.quickNavEnabled) {
      if (existingNav) existingNav.remove();
      if (existingSpacer) existingSpacer.remove();
      if (qnResizeObserver) qnResizeObserver.disconnect();
      return;
    }

    if (existingNav) return;
    const targetElement = document.querySelector('header');
    if (!targetElement) return;

    const spacer = document.createElement('div');
    spacer.id = 'aqn-spacer';
    const navContainer = document.createElement('div');
    navContainer.id = 'custom-quick-nav';

    targetElement.insertAdjacentElement('afterend', spacer);
    targetElement.insertAdjacentElement('afterend', navContainer);

    renderQnButtons();
    applyQnVisualSettings();
    setupQnPositioning(targetElement, navContainer, spacer);
  }

  // ---------------------------------------------------------------------------
  // Settings Logic & UI Schema
  // ---------------------------------------------------------------------------

  const SETTINGS_SCHEMA = [
    {
      title: 'Панель быстрого доступа',
      items: [
        { key: 'quickNavEnabled', type: 'bool', label: 'Включить панель' },
        { key: 'qnBgColor', type: 'color', label: 'Цвет подложки' },
        { key: 'qnBgOpacity', type: 'number', step: '0.05', label: 'Непрозрачность (0-1)', min: 0, max: 1 },
        { key: 'qnBlur', type: 'number', label: 'Размытие фона (px)', min: 0, max: 20 },
        { key: 'qnBtnBgColor', type: 'color', label: 'Фон кнопок' },
        { key: 'qnBtnTextColor', type: 'color', label: 'Текст кнопок' },
        { key: 'qnFontFamily', type: 'select', label: 'Шрифт кнопок', options: FONT_OPTIONS },
        { key: 'qnBtnFontSize', type: 'number', label: 'Размер шрифта', min: 10, max: 24 },
        { key: 'qnBtnPadY', type: 'number', label: 'Отступ (вертикаль)', min: 4, max: 24 },
        { key: 'qnBtnPadX', type: 'number', label: 'Отступ (горизонталь)', min: 4, max: 32 },
      ],
    },
    {
      title: 'Кнопки профиля / рангов (Внешний вид)',
      items: [
        { key: 'profBtnBgColor', type: 'color', label: 'Фон кнопок' },
        { key: 'profBtnHoverBgColor', type: 'color', label: 'Фон при наведении' },
        { key: 'profBtnTextColor', type: 'color', label: 'Текст кнопок' },
        { key: 'profFontFamily', type: 'select', label: 'Шрифт кнопок', options: FONT_OPTIONS },
        { key: 'profBtnFontSize', type: 'number', label: 'Размер шрифта', min: 8, max: 24 },
        { key: 'profBtnPadX', type: 'number', label: 'Отступ (горизонталь)', min: 0, max: 32 },
        { key: 'profBtnHeight', type: 'number', label: 'Высота кнопки', min: 16, max: 48 },
      ],
    },
    {
      title: 'Прочее',
      items: [
        { key: 'profileButtons', type: 'bool', label: 'Включить кнопки в профиле' },
        { key: 'cardNeedButtons', type: 'bool', label: 'Кнопки need на карточках' },
        { key: 'modalStarButton', type: 'bool', label: 'Кнопка звезды в модалке' },
      ],
    },
    {
      title: 'Камни (Автосбор)',
      items: [
        { key: 'takeCinemaStone', type: 'bool', label: 'Автозабор cinema stone' },
        { key: 'takeSnowStone', type: 'bool', label: 'Автозабор snow stone' },
        { key: 'takeHeavenlyStone', type: 'bool', label: 'Автозабор heavenly stone' },
        { key: 'stoneBaseDelayMs', type: 'number', label: 'Базовая задержка клика, ms', min: 10, max: 5000 },
        { key: 'stoneGrowthDelayMs', type: 'number', label: 'Рост задержки, ms', min: 10, max: 3000 },
      ],
    }
  ];

  function getSettingDef(key) {
    for (const section of SETTINGS_SCHEMA) {
      const found = section.items.find((item) => item.key === key);
      if (found) return found;
    }
    return null;
  }

  function clampNumberSetting(key, rawValue, min = 10, max = 999999) {
    const fallback = Number(DEFAULTS[key]);
    const n = parseFloat(rawValue);
    const base = Number.isFinite(n) ? n : (Number.isFinite(fallback) ? fallback : min);
    return Math.min(max, Math.max(min, base));
  }

  function removeProfileButtons() { document.querySelectorAll('.user-card-buttons').forEach((el) => el.remove()); }
  function remountProfileButtons() { removeProfileButtons(); mountProfileButtons(); }

  function showSettingsStatus(text) {
    const el = document.querySelector('.ass-tm-save-status');
    if (!el) return;
    el.textContent = text;
    window.clearTimeout(showSettingsStatus._timer);
    showSettingsStatus._timer = window.setTimeout(() => { if (el.textContent === text) el.textContent = ''; }, 1800);
  }

  function applyRuntimeSettingChange(key) {
    if (key === 'profileButtons' || key === 'profileButtonsConfig') {
      remountProfileButtons();
    } else if (key === 'cardNeedButtons') {
      if (state.cardNeedButtons) scanCardNeedButtons();
      else document.querySelectorAll('.show-need_button').forEach((el) => el.classList.remove('show-need_button'));
    } else if (key === 'modalStarButton') {
      if (state.modalStarButton) processExistingDialogs();
      else document.querySelectorAll('.as-ext-star-meta-item').forEach((el) => el.remove());
    } else if (key.startsWith('qn') || key === 'quickNavEnabled') {
      if (key === 'quickNavEnabled') injectQuickNav();
      else if (key === 'qnLinks') renderQnButtons();
      else applyQnVisualSettings();
    } else if (key.startsWith('profBtn') || key === 'profFontFamily') {
      applyProfVisualSettings();
    }
  }

  function setSettingFromUi(key, rawValue) {
    const def = getSettingDef(key);
    if (!def) return;
    let next;
    if (def.type === 'bool') next = !!rawValue;
    else if (def.type === 'color' || def.type === 'text' || def.type === 'select') next = String(rawValue);
    else next = clampNumberSetting(key, rawValue, def.min ?? 10, def.max ?? 999999);
    gmSet(key, next);
    applyRuntimeSettingChange(key);
  }

  function findTelegramHeaderButton() {
    const candidates = Array.from(document.querySelectorAll('a, button')).filter((el) => el.id !== 'ass-tm-settings-fab');
    const topRight = candidates
      .map((el) => ({ el, rect: el.getBoundingClientRect?.() }))
      .filter(({ rect }) => rect && rect.width > 0 && rect.height > 0 && rect.top >= 0 && rect.top < 190 && rect.right > Math.max(260, window.innerWidth * 0.55));
    const isTelegram = (el) => {
      const text = [el.getAttribute('href'), el.getAttribute('title'), el.className, el.textContent].join(' ').toLowerCase();
      if (text.includes('telegram') || text.includes('t.me') || text.includes('tg://')) return true;
      const icon = el.querySelector?.('i, svg');
      return icon && (icon.className.includes('telegram') || icon.outerHTML.includes('paper-plane'));
    };
    const direct = topRight.find(({ el }) => isTelegram(el));
    if (direct) return direct.el;
    const smallButtons = topRight.filter(({ rect }) => rect.width <= 56 && rect.height <= 56).sort((a, b) => b.rect.right - a.rect.right || a.rect.top - b.rect.top);
    return smallButtons[0]?.el || null;
  }

  function makeSettingsButton(replaceTarget = null) {
    const btn = document.createElement('button');
    btn.id = 'ass-tm-settings-fab';
    btn.type = 'button';
    btn.title = 'ASS Tools настройки';
    btn.innerHTML = '<i class="fal fa-cog" aria-hidden="true"></i>';
    if (replaceTarget) {
      const cls = String(replaceTarget.getAttribute('class') || '').trim();
      if (cls) btn.className = cls;
      btn.classList.add('ass-tm-settings-header-btn');
    } else {
      btn.className = 'ass-tm-settings-fallback';
    }
    btn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); openSettingsPanel(); });
    return btn;
  }

  function ensureSettingsFab() {
    const existing = document.getElementById('ass-tm-settings-fab');
    if (existing && !existing.classList.contains('ass-tm-settings-fallback')) return;
    const target = findTelegramHeaderButton();
    if (target && target.id !== 'ass-tm-settings-fab') {
      const btn = makeSettingsButton(target);
      target.replaceWith(btn);
      if (existing && existing !== btn) existing.remove();
      return;
    }
    if (existing) return;
    document.body.appendChild(makeSettingsButton(null));
  }

  // --- Double-Click Confirm Utility ---
  function addDoubleConfirm(btn, normalText, onConfirm) {
    let clicks = 0;
    btn.addEventListener('click', (e) => {
      e.preventDefault();
      if (clicks === 0) {
        clicks++;
        const originalBg = btn.style.background;
        btn.textContent = 'Уверены? (Нажмите еще раз)';
        btn.style.background = '#d93838';
        setTimeout(() => {
          if (clicks > 0) {
            clicks = 0;
            btn.textContent = normalText;
            btn.style.background = originalBg;
          }
        }, 3000);
      } else {
        clicks = 0;
        btn.textContent = normalText;
        btn.style.background = '';
        onConfirm();
      }
    });
  }

  // --- List Editors (Profile + Quick Nav Links) ---
  function renderListEditor(panel, listClass, dataKey, defaults, rowBuilder, readRow) {
    const list = panel.querySelector(listClass);
    if (!list) return;
    list.innerHTML = '';
    const items = Array.isArray(state[dataKey]) && state[dataKey].length ? state[dataKey] : defaults;
    items.forEach((item, index) => list.appendChild(rowBuilder(item, index)));

    const saveFn = () => {
      const config = Array.from(list.children).map((row, i) => readRow(row, i)).filter(Boolean);
      gmSet(dataKey, config.length ? config : cloneValue(defaults));
      applyRuntimeSettingChange(dataKey);
      showSettingsStatus('Сохранено');
    };

    let timer = null;
    const schedule = () => { clearTimeout(timer); timer = setTimeout(saveFn, 250); };

    list.addEventListener('input', schedule);
    list.addEventListener('change', schedule);

    const wrapper = list.closest('.ass-tm-editor');
    wrapper.addEventListener('click', (e) => {
      if (e.target.closest('.ass-tm-editor-remove')) {
        e.target.closest('.ass-tm-editor-row').remove();
        saveFn();
      } else if (e.target.dataset.action === 'add') {
        list.appendChild(rowBuilder({}, Date.now()));
        saveFn();
      }
    });

    const resetBtn = wrapper.querySelector('[data-action="reset"]');
    if (resetBtn && !resetBtn.dataset.bound) {
      resetBtn.dataset.bound = '1';
      addDoubleConfirm(resetBtn, 'Сбросить', () => {
        gmSet(dataKey, cloneValue(defaults));
        applyRuntimeSettingChange(dataKey);
        renderListEditor(panel, listClass, dataKey, defaults, rowBuilder, readRow);
        showToast('Элементы сброшены');
      });
    }
  }

  function buildProfileRow(item, i) {
    const row = document.createElement('div'); row.className = 'ass-tm-editor-row'; row.dataset.id = item.id || `p-${i}`;
    const enabled = document.createElement('input'); enabled.type = 'checkbox'; enabled.checked = item.enabled !== false; enabled.dataset.f = 'en'; enabled.title = 'Включено';
    const text = document.createElement('input'); text.type = 'text'; text.value = item.text || ''; text.placeholder = 'Текст'; text.dataset.f = 'txt';
    const icon = document.createElement('input'); icon.type = 'text'; icon.value = item.icon || ''; icon.placeholder = 'Icon'; icon.dataset.f = 'icn';
    const rm = document.createElement('button'); rm.type = 'button'; rm.className = 'ass-tm-editor-remove'; rm.textContent = '×'; rm.title = 'Удалить';
    const url = document.createElement('input'); url.type = 'text'; url.value = item.url || ''; url.placeholder = 'URL'; url.dataset.f = 'url';

    // ВАЖНО: Добавление элементов ровно так, как указано на скриншоте пользователя
    row.append(enabled, text, icon, rm, url);

    return row;
  }

  function readProfileRow(row, i) {
    const q = (s) => row.querySelector(`[data-f="${s}"]`)?.value?.trim() || '';
    const url = q('url'), txt = q('txt'), icn = q('icn');
    if (!url && !txt && !icn) return null;
    return { id: row.dataset.id || `p-${i}`, enabled: !!row.querySelector('[data-f="en"]')?.checked, text: txt, icon: icn, url: url };
  }

  function buildQnRow(item, i) {
    const row = document.createElement('div'); row.className = 'ass-tm-editor-row qn-link'; row.dataset.id = item.id || `q-${i}`;
    const text = document.createElement('input'); text.type = 'text'; text.value = item.title || ''; text.placeholder = 'Название'; text.dataset.f = 'ttl';
    const rm = document.createElement('button'); rm.type = 'button'; rm.className = 'ass-tm-editor-remove'; rm.textContent = '×';
    const url = document.createElement('input'); url.type = 'text'; url.value = item.url || ''; url.placeholder = 'URL'; url.dataset.f = 'url';

    // По аналогии: текст, удаление, url
    row.append(text, rm, url);

    return row;
  }

  function readQnRow(row, i) {
    const url = row.querySelector('[data-f="url"]')?.value?.trim() || '';
    const ttl = row.querySelector('[data-f="ttl"]')?.value?.trim() || '';
    if (!url && !ttl) return null;
    return { id: row.dataset.id || `q-${i}`, title: ttl || 'Без названия', url: url || '#' };
  }

  function openSettingsPanel() {
    if (!document.body) return;
    let backdrop = document.querySelector('.ass-tm-settings-backdrop');
    if (!backdrop) {
      backdrop = document.createElement('div');
      backdrop.className = 'ass-tm-settings-backdrop';
      document.body.appendChild(backdrop);
    }
    backdrop.innerHTML = '';
    const panel = document.createElement('div');
    panel.className = 'ass-tm-settings-panel';
    panel.innerHTML = `
      <div class="ass-tm-settings-head">
        <div class="ass-tm-settings-title">ASS Tools — настройки</div>
        <button type="button" class="ass-tm-settings-close" title="Закрыть">×</button>
      </div>
      <div class="ass-tm-settings-body">
        <div class="ass-tm-settings-grid"></div>

        <div class="ass-tm-editor" id="qn-editor">
          <h3>Панель быстрого доступа (Ссылки)</h3>
          <div class="ass-tm-editor-list"></div>
          <div class="ass-tm-editor-actions">
            <button type="button" data-action="add">Добавить ссылку</button>
            <button type="button" data-action="reset">Сбросить</button>
          </div>
        </div>

        <div class="ass-tm-editor" id="prof-editor">
          <h3>Список кнопок профиля (включая ранги)</h3>
          <p style="margin:0 0 8px; color:#999; font-size:11px;">Плейсхолдеры: {USERNAME}, {CLUB_ID}.</p>
          <div class="ass-tm-editor-list"></div>
          <div class="ass-tm-editor-actions">
            <button type="button" data-action="add">Добавить кнопку</button>
            <button type="button" data-action="reset">Сбросить</button>
          </div>
        </div>

        <div class="ass-tm-save-status"></div>
        <div class="ass-tm-settings-actions">
          <button type="button" data-action="save">Сохранить всё</button>
          <button type="button" data-action="reset-all" style="background:rgba(255,255,255,.05); color:#999;">Полный сброс настроек</button>
        </div>
      </div>
    `;

    const grid = panel.querySelector('.ass-tm-settings-grid');
    SETTINGS_SCHEMA.forEach((section) => {
      const sectionEl = document.createElement('section'); sectionEl.className = 'ass-tm-settings-section';
      const h = document.createElement('h3'); h.textContent = section.title; sectionEl.appendChild(h);
      section.items.forEach((item) => {
        const row = document.createElement('label'); row.className = 'ass-tm-setting-row';
        const text = document.createElement('span'); text.className = 'ass-tm-setting-label'; text.textContent = item.label;
        let input;

        if (item.type === 'select') {
          input = document.createElement('select');
          item.options.forEach(opt => {
            const o = document.createElement('option');
            o.value = opt.value;
            o.textContent = opt.label;
            input.appendChild(o);
          });
          input.value = state[item.key] || item.options[0].value;
        } else {
          input = document.createElement('input');
          if (item.type === 'bool') {
            input.type = 'checkbox'; input.checked = !!state[item.key];
          } else if (item.type === 'color' || item.type === 'text') {
            input.type = item.type; input.value = state[item.key] || '';
          } else {
            input.type = 'number'; input.min = String(item.min ?? 10); input.max = String(item.max ?? 999999);
            if (item.step) input.step = item.step; else input.step = '1';
            input.value = String(state[item.key]);
          }
        }

        input.setAttribute('data-setting-key', item.key);
        row.appendChild(text); row.appendChild(input); sectionEl.appendChild(row);
      });
      grid.appendChild(sectionEl);
    });

    renderListEditor(panel.querySelector('#qn-editor'), '.ass-tm-editor-list', 'qnLinks', DEFAULT_QN_LINKS, buildQnRow, readQnRow);
    renderListEditor(panel.querySelector('#prof-editor'), '.ass-tm-editor-list', 'profileButtonsConfig', DEFAULT_PROFILE_BUTTONS, buildProfileRow, readProfileRow);

    let numTimer = null;
    panel.querySelectorAll('[data-setting-key]').forEach((input) => {
      const saveOne = () => { setSettingFromUi(input.getAttribute('data-setting-key'), input.type === 'checkbox' ? input.checked : input.value); showSettingsStatus('Сохранено'); };
      if (input.type === 'checkbox' || input.tagName === 'SELECT' || input.type === 'color') input.addEventListener('change', saveOne);
      else { input.addEventListener('input', () => { window.clearTimeout(numTimer); numTimer = window.setTimeout(saveOne, 300); }); input.addEventListener('change', saveOne); }
    });

    panel.querySelector('.ass-tm-settings-close')?.addEventListener('click', () => backdrop.classList.remove('ass-tm-settings-open'));
    panel.querySelector('[data-action="save"]')?.addEventListener('click', () => showSettingsStatus('Сохранено'));

    const resetAllBtn = panel.querySelector('[data-action="reset-all"]');
    if (resetAllBtn) {
      addDoubleConfirm(resetAllBtn, 'Полный сброс настроек', () => {
        Object.keys(DEFAULTS).forEach((k) => { gmSet(k, cloneValue(DEFAULTS[k])); applyRuntimeSettingChange(k); });
        backdrop.classList.remove('ass-tm-settings-open');
        showToast('Все настройки полностью сброшены');
        openSettingsPanel();
      });
    }

    backdrop.addEventListener('click', (e) => { if (e.target === backdrop) backdrop.classList.remove('ass-tm-settings-open'); });
    backdrop.appendChild(panel); backdrop.classList.add('ass-tm-settings-open');
  }

  // ---------------------------------------------------------------------------
  // Profile buttons
  // ---------------------------------------------------------------------------

  function getProfileUsername() { return document.querySelector('.usn__name > h1')?.textContent?.trim?.() || ''; }
  function getUserClubId() { const u = document.querySelector('.usn__club-item-top a')?.href; return u && u.includes('clubs/') ? u.replace(/\/$/, '').split('/').pop() : '1'; }
  function resolveIconClass(ic) { return ic ? (ic.startsWith('fas ') ? `fal ${ic.slice(4)}` : ic) : ''; }

  function mountProfileButtons() {
    if (!state.profileButtons) return;
    const username = getProfileUsername();
    if (!username) return;
    const header = document.querySelector('.usn-sect__header');
    if (!header || header.querySelector('.user-card-buttons')) return;

    const clubId = getUserClubId();
    const box = document.createElement('div'); box.className = 'user-card-buttons';

    (state.profileButtonsConfig || []).forEach((btn) => {
      if (!btn.enabled) return;
      const text = String(btn.text || '').trim(), iconClass = String(btn.icon || '').trim();
      let url = String(btn.url || '').replace(/\{USERNAME\}/g, username).replace(/\{USER\}/g, username).replace(/\{CLUB_ID\}/g, clubId);
      if (!url || (!text && !iconClass)) return;
      if (!/^https?:\/\//i.test(url) && !url.startsWith('/')) url = `/${url.replace(/^\/+/, '')}`;

      const link = document.createElement('a'); link.href = url; link.title = text || '';
      if (iconClass) {
        const i = document.createElement('i'); i.className = resolveIconClass(iconClass); link.appendChild(i);
        if (text && text.length <= 3) link.appendChild(document.createTextNode(text));
      } else link.textContent = text;
      box.appendChild(link);
    });

    if (box.childNodes.length) header.appendChild(box);
  }

  // ---------------------------------------------------------------------------
  // Card need buttons / class marker
  // ---------------------------------------------------------------------------

  const CARD_CONTAINER_SELECTOR = '.lootbox__card, .anime-cards__item, a.trade__main-item, a.history__body-item, .trade__inventory-item, div.trade__main-item, .remelt__inventory-item, .remelt__item, .anime-cards__placeholder, .stone__inventory-item';

  function applyNeedButtonMarker(elm) {
    if (!state.cardNeedButtons || !elm || elm.classList.contains('show-need_button')) return;
    if (elm.classList.contains('show-trade_button') || elm.dataset?.canTrade === '1') return;
    elm.classList.add('show-need_button');
  }
  function scanCardNeedButtons(root = document) { if (state.cardNeedButtons) root.querySelectorAll?.(CARD_CONTAINER_SELECTOR).forEach(applyNeedButtonMarker); }

  // ---------------------------------------------------------------------------
  // Modal star button
  // ---------------------------------------------------------------------------

  function addStarButton(modalContent) {
    if (!state.modalStarButton || !modalContent) return;
    const meta = modalContent.querySelector('.ncard__meta');
    if (!meta || meta.querySelector('.as-ext-star-meta-item')) return;
    const rankElement = modalContent.querySelector('.ncard__meta-item.ncard__rank');
    if (!rankElement) return;

    const rankClass = Array.from(rankElement.classList).find((c) => c.startsWith('rank-'));
    const rank = rankClass ? rankClass.split('-').slice(1).join('-') : null;
    const cardName = modalContent.querySelector('.anime-cards__name')?.textContent?.trim() || null;
    if (!rank || !cardName) return;

    const starLink = document.createElement('a');
    starLink.href = `/update_stars/?rank=${encodeURIComponent(rank)}&search=${encodeURIComponent(cardName)}`;
    starLink.className = 'ncard__meta-item as-ext-star-meta-item';

    // Идеальная, залитая по центру SVG звезда (гарантированно работает без внешних шрифтов)
    starLink.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" width="16" height="16" fill="gold"><path d="M316.9 18C311.6 7 300.4 0 288.1 0s-23.4 7-28.8 18L195 150.3 51.4 171.5c-12 1.8-22 10.2-25.7 21.7s-.7 24.2 7.9 32.7L137.8 329 113.2 474.7c-2 12 3 24.2 12.9 31.3s23 8 33.8 2.3l128.3-68.5 128.3 68.5c10.8 5.7 23.9 4.9 33.8-2.3s14.9-19.3 12.9-31.3L438.5 329 542.7 225.9c8.6-8.5 11.7-21.2 7.9-32.7s-13.7-19.9-25.7-21.7L381.2 150.3 316.9 18z"/></svg>';

    starLink.style.cssText = 'display:flex; align-items:center; justify-content:center; width:36px; min-width:36px; height:36px; border-radius:50%; text-decoration:none; padding:0; margin:0; box-sizing:border-box; background-color:transparent; border:1px solid #555; transition:all .2s ease;';

    starLink.onmouseenter = () => { starLink.style.backgroundColor = 'rgba(158,41,79,.9)'; starLink.style.borderColor = 'rgba(158,41,79,.9)'; };
    starLink.onmouseleave = () => { starLink.style.backgroundColor = 'transparent'; starLink.style.borderColor = '#555'; };

    meta.style.columnGap = '5px';
    meta.insertBefore(starLink, rankElement);
  }

  function processDialogElement(dialogElement) {
    if (state.modalStarButton) setTimeout(() => addStarButton(dialogElement.querySelector?.('#card-modal .modal__content')), 50);
  }
  function processExistingDialogs() { document.querySelectorAll('.ui-dialog').forEach(processDialogElement); }

  // ---------------------------------------------------------------------------
  // Stones (Event-Driven Auto-collect)
  // ---------------------------------------------------------------------------

  function clickStoneWithDelay(stoneElement) {
    const baseDelay = clampNumberSetting('stoneBaseDelayMs', state.stoneBaseDelayMs, 10, 5000);
    const growthDelay = clampNumberSetting('stoneGrowthDelayMs', state.stoneGrowthDelayMs, 10, 3000);
    const currentDelay = baseDelay + (growthDelay * stoneClickQueue);
    stoneClickQueue++;

    setTimeout(() => {
      if (stoneElement && document.body.contains(stoneElement)) {
        stoneElement.click();
      }
      stoneClickQueue--;
      if (stoneClickQueue < 0) stoneClickQueue = 0;
    }, currentDelay);
  }

  function scanExistingStones() {
    if (state.takeCinemaStone) {
      const diamonds = Array.from(document.querySelectorAll('#diamonds-chat[data-code]'));
      diamonds.reverse().forEach((diamond) => {
        if (diamond.dataset.assQueued === '1') return;
        const code = diamond.getAttribute('data-code');
        if (!code || clickedCinemaCodes.has(code)) return;
        clickedCinemaCodes.add(code);
        diamond.dataset.assQueued = '1';
        clickStoneWithDelay(diamond);
      });
    }
    if (state.takeSnowStone) {
      const snow = document.querySelector('#snow-stone-gift');
      if (snow && snow.dataset.assQueued !== '1') {
        snow.dataset.assQueued = '1';
        clickStoneWithDelay(snow);
      }
    }
    if (state.takeHeavenlyStone) {
      const heavenly = document.querySelector('#gift-icon');
      if (heavenly && heavenly.dataset.assQueued !== '1') {
        heavenly.dataset.assQueued = '1';
        clickStoneWithDelay(heavenly);
      }
    }
  }

  function startInitialStoneScanner() {
    let ticks = 0;
    const maxTicks = 60;

    scanExistingStones();
    const scanInterval = setInterval(() => {
      scanExistingStones();
      ticks++;
      if (ticks >= maxTicks) clearInterval(scanInterval);
    }, 3000);
  }

  function interactionWithChat() {
    if (!state.takeCinemaStone) return;
    const idle = document.querySelector('#animesssChatIdle');
    if (idle && idle.style.display !== 'none') {
      document.querySelector('#animesssChatIdleBack')?.click();
      document.activeElement?.focus?.();
    }
  }

  // ---------------------------------------------------------------------------
  // Main Observers & Init
  // ---------------------------------------------------------------------------

  function setupObservers() {
    new MutationObserver((mutations) => {
      for (const m of mutations) {
        m.addedNodes.forEach((node) => {
          if (node.nodeType !== Node.ELEMENT_NODE) return;

          if (node.matches?.(CARD_CONTAINER_SELECTOR)) applyNeedButtonMarker(node);
          scanCardNeedButtons(node);

          if (node.classList?.contains('ui-dialog')) processDialogElement(node);
          const nestedDialog = node.querySelector?.('.ui-dialog');
          if (nestedDialog) processDialogElement(nestedDialog);

          if (state.takeCinemaStone) {
            let diamonds = [];
            if (node.id === 'diamonds-chat' && node.hasAttribute('data-code')) {
              diamonds.push(node);
            } else if (node.querySelectorAll) {
              diamonds = Array.from(node.querySelectorAll('#diamonds-chat[data-code]'));
            }

            diamonds.reverse().forEach((diamond) => {
              if (diamond.dataset.assQueued === '1') return;
              const code = diamond.getAttribute('data-code');
              if (!code || clickedCinemaCodes.has(code)) return;
              clickedCinemaCodes.add(code);
              diamond.dataset.assQueued = '1';
              clickStoneWithDelay(diamond);
            });
          }

          if (state.takeSnowStone) {
            const snow = node.id === 'snow-stone-gift' ? node : node.querySelector?.('#snow-stone-gift');
            if (snow && snow.dataset.assQueued !== '1') {
              snow.dataset.assQueued = '1';
              clickStoneWithDelay(snow);
            }
          }

          if (state.takeHeavenlyStone) {
            const heavenly = node.id === 'gift-icon' ? node : node.querySelector?.('#gift-icon');
            if (heavenly && heavenly.dataset.assQueued !== '1') {
              heavenly.dataset.assQueued = '1';
              clickStoneWithDelay(heavenly);
            }
          }
        });
      }

      mountProfileButtons();
      ensureSettingsFab();
      if (state.quickNavEnabled && !document.getElementById('custom-quick-nav') && document.querySelector('header')) {
        injectQuickNav();
      }
    }).observe(document.body, { childList: true, subtree: true });
  }

  function main() {
    loadSettings();
    injectStyle();

    onBodyReady(() => {
      injectQuickNav();
      applyProfVisualSettings();
      ensureSettingsFab();

      setupObservers();
      mountProfileButtons();
      scanCardNeedButtons();
      processExistingDialogs();

      startInitialStoneScanner();
      setInterval(interactionWithChat, 10000);
    });
  }

  main();
})();