MWI Customizer

Customize Milky Way Idle

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         MWI Customizer
// @namespace    https://github.com/collaring
// @version      1.2.2
// @description  Customize Milky Way Idle
// @author       collaring <https://github.com/collaring>
// @license      MIT
// @match        https://www.milkywayidle.com/*
// @match        https://test.milkywayidle.com/*
// @match        https://www.milkywayidlecn.com/*
// @match        https://test.milkywayidlecn.com/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

// MIT License
//
// Copyright (c) 2026 ave
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

(function () {
  'use strict';

  // Configuration (user-tweakable)
  const cfg = {
    debug: false,
    // A list of DOM selectors that might represent inventory item elements.
    inventorySelectors: [
      '.inventory .slot',
      '.inv-slot',
      '.item-slot',
      '[data-item-id]',
      '.inventory-slot'
    ],
    // exposed quick-tunable options (defaults; can be overridden at runtime)
    outlineWidth: 2, // px
    glowAlpha: 0.08, // alpha for outer glow
    // smaller default spread so the outer cloud is less dominant
    glowSpread: 6, // px distance of the colored ring from the icon
    // blur amount for the outer glow (softens hard edge)
    glowBlur: 6, // px blur radius for outer glow
    innerBorderWidth: 2, // px width of the inner curved border
    // Toggle to show the inner curved border (inset) around icons
    showInnerBorder: true,
    // reduce background tint so icons aren't cloudy
    bgAlpha: 0.12, // background tint alpha
    // allow user override of quantity tiers via cfg.quantityTiers (null = disabled)
    quantityTiers: null,
    // Color mode: 'Quantity'|'Category'|'None' 窶・controls which coloring strategy is applied
    colorMode: 'Quantity',
    // 'All' mode: single color applied to all inventory/targets
    allColor: '#1d8ce0',
    allAlpha: 0.2,
    // Category-to-color mapping used when colorMode === 'Category'. Keys are category names.
    // Collection-style quantity tiers (min inclusive). These attempt to match Collection colors.
    // Edit these thresholds/colors to match your Collections UI precisely.
    collectionQuantityTiers: [
      { min: 1000000, color: '#ff44cc', alpha: 0.2 },
      { min: 100000, color: '#e3931b', alpha: 0.2 },
      { min: 10000, color: '#d0333d', alpha: 0.2 },
      { min: 1000, color: '#9368cf', alpha: 0.2 },
      { min: 100, color: '#1d8ce0', alpha: 0.2 },
      { min: 1, color: '#d0d0d0', alpha: 0.2 }
    ]
    ,
    // Sitewide colors (adjust site appearance). Empty = no override.
    siteColors: {
      header: '',
      headerAlpha: 0.2,
      panelBg: '',
      panelBgAlpha: 0.2,
      sidePanel: '',
      sidePanelAlpha: 0.2,
      navLabel: '',
      navLabelAlpha: 1,
      interactables: '',
      interactablesAlpha: 1,
      subPanel: '',
      subPanelAlpha: 0.2,
      chatBg: '',
      chatBgAlpha: 0.2,
        timestamp: '',
        timestampAlpha: 1,
        chatText: '',
        chatTextAlpha: 1,
        systemMessage: '',
        systemMessageAlpha: 1,
        inventoryLabels: '',
        inventoryLabelsAlpha: 1,
        buttonBg: '',
        buttonBgAlpha: 1,
        tabsBg: '',
        tabsBgAlpha: 1,
      skillXPBar: '',
      skillXPBarAlpha: 1,
      level: '',
      levelAlpha: 1,
      skillActions: '',
      skillActionsAlpha: 1,
      combat: '',
      combatAlpha: 1,
      progressBar: '',
      progressBarAlpha: 1,
      hitDmg: '',
      hitDmgAlpha: 1,
      hitMiss: '',
      hitMissAlpha: 1,
      barBg: '',
      barBgAlpha: 1,
      // Selected skill (active navigation link)
      selectedSkill: '',
      selectedSkillAlpha: 1,
      accent: '',
      accentAlpha: 1,
      text: ''
    }
    ,
    // Custom background image (URL). Enable to overlay a full-page background.
    backgroundEnabled: false,
    backgroundUrl: '',
    // background overlay appearance
    backgroundOpacity: 1,
    // active theme key (empty = custom/default)
    themeKey: '',
    // Hide/Organize UI state: per-element hidden flags and ordering
    hiddenElements: {},
    organizeOrder: [],
    // UI style: 'frosted' (default) or 'solid'
    uiStyle: 'frosted',
    // Settings button style: 'frosted' (default) or 'solid'
    btnStyle: 'frosted',
    // Animations
    combatAnim: true,
    usageAnim: true,
    swapPanels: false,
    chatTop: false,
    headerBottom: false,
    // Theme presets removed 窶・use siteColors and manual controls in the UI
  };

  // Reset-on-refresh flag: when true restores defaults except Dev keys
  cfg.resetOnRefresh = false;

  // Keep an immutable copy of defaults for reset
  const DEFAULT_CFG = JSON.parse(JSON.stringify(cfg));

  // Preset themes (map to `cfg.siteColors`)
  const PRESET_THEMES = [
    { key: '', label: 'Custom / Default', siteColors: {} },
    { key: 'dark', label: 'Dark', siteColors: {
        header: '#000000', headerAlpha: 1,
        panelBg: '#000000', panelBgAlpha: 1,
        sidePanel: '#000000', sidePanelAlpha: 1,
        navLabel: '', navLabelAlpha: 1,
        interactables: '#3c3c3c', interactablesAlpha: 1,
        subPanel: '#000000', subPanelAlpha: 1,
        chatBg: '#000000', chatBgAlpha: 1,
        timestamp: '#868686', timestampAlpha: 1,
        chatText: '', chatTextAlpha: 1,
        systemMessage: '', systemMessageAlpha: 1,
        inventoryLabels: '#b0b0b0', inventoryLabelsAlpha: 1,
        buttonBg: '#000000', buttonBgAlpha: 1,
        skillXPBar: '#999999', skillXPBarAlpha: 1,
        level: '', levelAlpha: 1,
        skillActions: '#1b1b1b', skillActionsAlpha: 1,
        combat: '#252525', combatAlpha: 1,
        progressBar: '', progressBarAlpha: 1,
        accent: '', accentAlpha: 1,
        text: ''
      } },
    { key: 'pink', label: 'Pink', siteColors: {
        header: '#ff80ff', headerAlpha: 0.78,
        panelBg: '#ff80ff', panelBgAlpha: 0.64,
        subPanel: '#ff80ff', subPanelAlpha: 0.47,
        sidePanel: '#ff80ff', sidePanelAlpha: 0.65,
        navLabel: '', navLabelAlpha: 1,
        interactables: '', interactablesAlpha: 1,
        chatBg: '#414141', chatBgAlpha: 0.41,
        timestamp: '', timestampAlpha: 1,
        chatText: '#ff80ff', chatTextAlpha: 1,
        systemMessage: '', systemMessageAlpha: 1,
        inventoryLabels: '#ffffff', inventoryLabelsAlpha: 1,
        buttonBg: '#000000', buttonBgAlpha: 1,
        skillXPBar: '#ff80ff', skillXPBarAlpha: 1,
        level: '', levelAlpha: 1,
        skillActions: '#460046', skillActionsAlpha: 0.79,
        combat: '#9b009b', combatAlpha: 1,
        progressBar: '#ff80ff', progressBarAlpha: 1,
        accent: '', accentAlpha: 1,
        text: ''
      } }
  ];

  // Items available for Hide/Organize (id, label, selectors)
  const ORGANIZE_ITEMS = [
    { id: 'marketplace', label: 'Marketplace', icon: { sprite: 'misc', id: 'marketplace' }, selectors: ['[class*="Marketplace"]','[id*="marketplace"]'] },
    { id: 'tasks', label: 'Tasks', icon: { sprite: 'misc', id: 'tasks' }, selectors: ['[class*="Tasks"]','[id*="tasks"]'] },
    { id: 'labyrinth', label: 'Labyrinth', icon: { sprite: 'misc', id: 'labyrinth' }, selectors: ['[class*="Labyrinth"]','[id*="labyrinth"]'] },
    { type: 'separator' },
    { id: 'milking', label: 'Milking', icon: { sprite: 'skills', id: 'milking' }, selectors: ['[class*="Milking"]','[id*="milking"]'] },
    { id: 'foraging', label: 'Foraging', icon: { sprite: 'skills', id: 'foraging' }, selectors: ['[class*="Foraging"]','[id*="foraging"]'] },
    { id: 'woodcutting', label: 'Woodcutting', icon: { sprite: 'skills', id: 'woodcutting' }, selectors: ['[class*="Woodcutting"]','[id*="woodcutting"]'] },
    { id: 'cheesesmithing', label: 'Cheesesmithing', icon: { sprite: 'skills', id: 'cheesesmithing' }, selectors: ['[class*="Cheese"]','[id*="cheese"]'] },
    { id: 'crafting', label: 'Crafting', icon: { sprite: 'skills', id: 'crafting' }, selectors: ['[class*="Craft"]','[id*="craft"]'] },
    { id: 'tailoring', label: 'Tailoring', icon: { sprite: 'skills', id: 'tailoring' }, selectors: ['[class*="Tailor"]','[id*="tailor"]'] },
    { id: 'cooking', label: 'Cooking', icon: { sprite: 'skills', id: 'cooking' }, selectors: ['[class*="Cook"]','[id*="cooking"]'] },
    { id: 'brewing', label: 'Brewing', icon: { sprite: 'skills', id: 'brewing' }, selectors: ['[class*="Brew"]','[id*="brew"]'] },
    { id: 'alchemy', label: 'Alchemy', icon: { sprite: 'skills', id: 'alchemy' }, selectors: ['[class*="Alchemy"]','[id*="alchemy"]'] },
    { id: 'enhancing', label: 'Enhancing', icon: { sprite: 'skills', id: 'enhancing' }, selectors: ['[class*="Enhance"]','[id*="enhance"]'] },
    { type: 'separator' },
    { id: 'combat', label: 'Combat', icon: { sprite: 'misc', id: 'combat' }, selectors: ['[class*="Combat"]','[id*="combat"]'] },
    { type: 'separator' },
    { id: 'shop', label: 'Shop', icon: { sprite: 'misc', id: 'shop' }, selectors: ['[class*="Shop"]','[id*="shop"]'] },
    { id: 'cowbell', label: 'Cowbell Store', icon: { sprite: 'misc', id: 'cowbell_store' }, selectors: ['[class*="Cowbell"]','[id*="cowbell"]'] },
    { id: 'loottracker', label: 'Loot Tracker', icon: { sprite: 'misc', id: 'loot_tracker' }, selectors: ['[class*="LootTracker"]','[id*="loottracker"]'] },
    { id: 'achievements', label: 'Achievements', icon: { sprite: 'misc', id: 'achievements' }, selectors: ['[class*="Achievement"]','[id*="achieve"]'] },
    { id: 'social', label: 'Social', icon: { sprite: 'misc', id: 'social' }, selectors: ['[class*="Social"]','[id*="social"]'] },
    { id: 'guild', label: 'Guild', icon: { sprite: 'misc', id: 'guild' }, selectors: ['[class*="Guild"]','[id*="guild"]'] },
    { id: 'leaderboard', label: 'Leaderboard', icon: { sprite: 'misc', id: 'leaderboard' }, selectors: ['[class*="Leader"]','[id*="leaderboard"]'] },
    { type: 'separator' },
    { id: 'settings', label: 'Settings', icon: { sprite: 'misc', id: 'settings' }, selectors: [], noHide: true },
    { id: 'links', label: 'Links', icon: { sprite: 'misc', id: 'patch_notes' }, selectors: ['[class*="minorNavigationLinks"]'] },
  ];

  const DEV_KEYS = ['debug']; // keys in cfg considered part of Dev category and preserved during auto-reset

  // Settings persistence
  const STORAGE_KEY = 'mwiCustomizerSettings_v1';

  function loadSettings() {
    try {
      const raw = localStorage.getItem(STORAGE_KEY);
      if (!raw) return;
      const obj = JSON.parse(raw);
      for (const k of Object.keys(obj)) {
        // only restore known cfg keys
        if (k in cfg) cfg[k] = obj[k];
      }
    } catch (e) { log('loadSettings error', e); }
  }

  function saveSettings() {
    try {
      // Persist all cfg keys except dev-only keys
      const keys = Object.keys(cfg).filter(k => !DEV_KEYS.includes(k));
      const out = {};
      for (const k of keys) if (cfg[k] !== undefined) out[k] = cfg[k];
      localStorage.setItem(STORAGE_KEY, JSON.stringify(out));
    } catch (e) { log('saveSettings error', e); }
  }

  function clearSettings() { try { localStorage.removeItem(STORAGE_KEY); } catch (e) { log('clearSettings error', e); } }

  // Apply stored sitewide colors
  function applySiteColors() {
    try {
      const sc = cfg.siteColors || {};
      const root = document.documentElement;
      if (sc.header) {
        const headVal = (sc.headerAlpha !== undefined && sc.headerAlpha < 1) ? colorToRGBA(sc.header, sc.headerAlpha) : sc.header;
        root.style.setProperty('--mwi-header-bg', headVal);
      } else root.style.removeProperty('--mwi-header-bg');
      if (sc.headerGrad) {
        const hgVal = (sc.headerGradAlpha !== undefined && sc.headerGradAlpha < 1) ? colorToRGBA(sc.headerGrad, sc.headerGradAlpha) : sc.headerGrad;
        root.style.setProperty('--mwi-header-grad', hgVal);
        root.classList.add('mwi-header-grad-active');
      } else { root.style.removeProperty('--mwi-header-grad'); root.classList.remove('mwi-header-grad-active'); }
      // panel bg variable still available but we also explicitly override default-colored panels
      // support alpha for panel and accent by converting to rgba when alpha < 1
      if (sc.panelBg) {
        const panelVal = (sc.panelBgAlpha !== undefined && sc.panelBgAlpha < 1) ? colorToRGBA(sc.panelBg, sc.panelBgAlpha) : sc.panelBg;
        root.style.setProperty('--mwi-panel-bg', panelVal);
      } else root.style.removeProperty('--mwi-panel-bg');
      if (sc.sidePanel) {
        const sideVal = (sc.sidePanelAlpha !== undefined && sc.sidePanelAlpha < 1) ? colorToRGBA(sc.sidePanel, sc.sidePanelAlpha) : sc.sidePanel;
        root.style.setProperty('--mwi-side-panel-bg', sideVal);
      } else root.style.removeProperty('--mwi-side-panel-bg');
      if (sc.subPanel) {
        const subVal = (sc.subPanelAlpha !== undefined && sc.subPanelAlpha < 1) ? colorToRGBA(sc.subPanel, sc.subPanelAlpha) : sc.subPanel;
        root.style.setProperty('--mwi-subpanel-bg', subVal);
      } else root.style.removeProperty('--mwi-subpanel-bg');
      if (sc.chatBg) {
        const chatVal = (sc.chatBgAlpha !== undefined && sc.chatBgAlpha < 1) ? colorToRGBA(sc.chatBg, sc.chatBgAlpha) : sc.chatBg;
        root.style.setProperty('--mwi-chat-bg', chatVal);
      } else root.style.removeProperty('--mwi-chat-bg');
      if (sc.skillActions) {
        const saVal = (sc.skillActionsAlpha !== undefined && sc.skillActionsAlpha < 1) ? colorToRGBA(sc.skillActions, sc.skillActionsAlpha) : sc.skillActions;
        root.style.setProperty('--mwi-skill-actions', saVal);
        root.classList.add('mwi-skill-actions-active');
      } else {
        root.style.removeProperty('--mwi-skill-actions');
        root.classList.remove('mwi-skill-actions-active');
      }
      if (sc.skillXPBar) {
        const xpVal = (sc.skillXPBarAlpha !== undefined && sc.skillXPBarAlpha < 1) ? colorToRGBA(sc.skillXPBar, sc.skillXPBarAlpha) : sc.skillXPBar;
        root.style.setProperty('--mwi-skill-xp', xpVal);
        root.classList.add('mwi-skill-xp-active');
      } else {
        root.style.removeProperty('--mwi-skill-xp');
        root.classList.remove('mwi-skill-xp-active');
      }
      if (sc.navLabel) {
        const nlVal = (sc.navLabelAlpha !== undefined && sc.navLabelAlpha < 1) ? colorToRGBA(sc.navLabel, sc.navLabelAlpha) : sc.navLabel;
        root.style.setProperty('--mwi-nav-label', nlVal);
        root.classList.add('mwi-nav-label-active');
      } else {
        root.style.removeProperty('--mwi-nav-label');
        root.classList.remove('mwi-nav-label-active');
      }
      // Selected skill (active nav link)
      if (sc.selectedSkill) {
        const ssVal = (sc.selectedSkillAlpha !== undefined && sc.selectedSkillAlpha < 1) ? colorToRGBA(sc.selectedSkill, sc.selectedSkillAlpha) : sc.selectedSkill;
        root.style.setProperty('--mwi-selected-skill', ssVal);
        root.classList.add('mwi-nav-selected-active');
      } else {
        root.style.removeProperty('--mwi-selected-skill');
        root.classList.remove('mwi-nav-selected-active');
      }
      if (sc.inventoryLabels) {
        const ilVal = (sc.inventoryLabelsAlpha !== undefined && sc.inventoryLabelsAlpha < 1) ? colorToRGBA(sc.inventoryLabels, sc.inventoryLabelsAlpha) : sc.inventoryLabels;
        root.style.setProperty('--mwi-inventory-labels', ilVal);
        root.classList.add('mwi-inventory-labels-active');
      } else {
        root.style.removeProperty('--mwi-inventory-labels');
        root.classList.remove('mwi-inventory-labels-active');
      }
      if (sc.level) {
        const lv = (sc.levelAlpha !== undefined && sc.levelAlpha < 1) ? colorToRGBA(sc.level, sc.levelAlpha) : sc.level;
        root.style.setProperty('--mwi-level', lv);
        root.classList.add('mwi-level-active');
      } else {
        root.style.removeProperty('--mwi-level');
        root.classList.remove('mwi-level-active');
      }
      if (sc.interactables) {
        const iaVal = (sc.interactablesAlpha !== undefined && sc.interactablesAlpha < 1) ? colorToRGBA(sc.interactables, sc.interactablesAlpha) : sc.interactables;
        root.style.setProperty('--mwi-interactables', iaVal);
        root.classList.add('mwi-interactables-active');
      } else {
        root.style.removeProperty('--mwi-interactables');
        root.classList.remove('mwi-interactables-active');
      }
      if (sc.timestamp) {
        const tVal = (sc.timestampAlpha !== undefined && sc.timestampAlpha < 1) ? colorToRGBA(sc.timestamp, sc.timestampAlpha) : sc.timestamp;
        root.style.setProperty('--mwi-timestamp', tVal);
        root.classList.add('mwi-timestamp-active');
      } else {
        root.style.removeProperty('--mwi-timestamp');
        root.classList.remove('mwi-timestamp-active');
      }
      if (sc.chatText) {
        const ctVal = (sc.chatTextAlpha !== undefined && sc.chatTextAlpha < 1) ? colorToRGBA(sc.chatText, sc.chatTextAlpha) : sc.chatText;
        root.style.setProperty('--mwi-chat-text', ctVal);
        root.classList.add('mwi-chat-text-active');
        try {
          applyChatTextToAll(); ensureChatTextObserver();
          // Chat messages may not be in the DOM yet on page load — retry a few times
          // to catch the initial batch that renders after React mounts.
          setTimeout(() => { try { applyChatTextToAll(); } catch (e) {} }, 800);
          setTimeout(() => { try { applyChatTextToAll(); } catch (e) {} }, 2500);
          setTimeout(() => { try { applyChatTextToAll(); } catch (e) {} }, 5000);
        } catch (e) { log('apply chatText error', e); }
      } else {
        root.style.removeProperty('--mwi-chat-text');
        root.classList.remove('mwi-chat-text-active');
        try { clearChatTextFromAll(); if (chatTextObserver) { chatTextObserver.disconnect(); chatTextObserver = null; } } catch (e) { log('clear chatText error', e); }
      }
      if (sc.systemMessage) {
        const sVal = (sc.systemMessageAlpha !== undefined && sc.systemMessageAlpha < 1) ? colorToRGBA(sc.systemMessage, sc.systemMessageAlpha) : sc.systemMessage;
        root.style.setProperty('--mwi-system-message', sVal);
        root.classList.add('mwi-system-message-active');
      } else {
        root.style.removeProperty('--mwi-system-message');
        root.classList.remove('mwi-system-message-active');
      }
      if (sc.progressBar) {
        const pVal = (sc.progressBarAlpha !== undefined && sc.progressBarAlpha < 1) ? colorToRGBA(sc.progressBar, sc.progressBarAlpha) : sc.progressBar;
        root.style.setProperty('--mwi-progress-bar', pVal);
        root.classList.add('mwi-progress-bar-active');
      } else {
        root.style.removeProperty('--mwi-progress-bar');
        root.classList.remove('mwi-progress-bar-active');
      }
      if (sc.combat) {
        const cVal = (sc.combatAlpha !== undefined && sc.combatAlpha < 1) ? colorToRGBA(sc.combat, sc.combatAlpha) : sc.combat;
        root.style.setProperty('--mwi-combat', cVal);
        root.classList.add('mwi-combat-active');
      } else {
        root.style.removeProperty('--mwi-combat');
        root.classList.remove('mwi-combat-active');
      }
      // HP / MP bar colors
      if (sc.hp) {
        const hpVal = (sc.hpAlpha !== undefined && sc.hpAlpha < 1) ? colorToRGBA(sc.hp, sc.hpAlpha) : sc.hp;
        root.style.setProperty('--mwi-hp', hpVal);
        root.classList.add('mwi-hp-active');
      } else {
        root.style.removeProperty('--mwi-hp');
        root.classList.remove('mwi-hp-active');
      }
      if (sc.mp) {
        const mpVal = (sc.mpAlpha !== undefined && sc.mpAlpha < 1) ? colorToRGBA(sc.mp, sc.mpAlpha) : sc.mp;
        root.style.setProperty('--mwi-mp', mpVal);
        root.classList.add('mwi-mp-active');
      } else {
        root.style.removeProperty('--mwi-mp');
        root.classList.remove('mwi-mp-active');
      }
      // Hitsplat Damage color
      if (sc.hitDmg) {
        const hitDmgVal = (sc.hitDmgAlpha !== undefined && sc.hitDmgAlpha < 1) ? colorToRGBA(sc.hitDmg, sc.hitDmgAlpha) : sc.hitDmg;
        root.style.setProperty('--mwi-hit-dmg', hitDmgVal);
        root.classList.add('mwi-hit-dmg-active');
      } else {
        root.style.removeProperty('--mwi-hit-dmg');
        root.classList.remove('mwi-hit-dmg-active');
      }
      // Hitsplat Miss color
      if (sc.hitMiss) {
        const hitMissVal = (sc.hitMissAlpha !== undefined && sc.hitMissAlpha < 1) ? colorToRGBA(sc.hitMiss, sc.hitMissAlpha) : sc.hitMiss;
        root.style.setProperty('--mwi-hit-miss', hitMissVal);
        root.classList.add('mwi-hit-miss-active');
      } else {
        root.style.removeProperty('--mwi-hit-miss');
        root.classList.remove('mwi-hit-miss-active');
      }
      // Attack bar color
      if (sc.attack) {
        const attackVal = (sc.attackAlpha !== undefined && sc.attackAlpha < 1) ? colorToRGBA(sc.attack, sc.attackAlpha) : sc.attack;
        root.style.setProperty('--mwi-attack', attackVal);
        root.classList.add('mwi-attack-active');
      } else {
        root.style.removeProperty('--mwi-attack');
        root.classList.remove('mwi-attack-active');
      }
      // Bar background color
      if (sc.barBg) {
        const barBgVal = (sc.barBgAlpha !== undefined && sc.barBgAlpha < 1) ? colorToRGBA(sc.barBg, sc.barBgAlpha) : sc.barBg;
        root.style.setProperty('--mwi-bar-bg', barBgVal);
        root.classList.add('mwi-bar-bg-active');
      } else {
        root.style.removeProperty('--mwi-bar-bg');
        root.classList.remove('mwi-bar-bg-active');
      }
      // Consumables / Abilities color
      if (sc.consumables) {
        const consVal = (sc.consumablesAlpha !== undefined && sc.consumablesAlpha < 1) ? colorToRGBA(sc.consumables, sc.consumablesAlpha) : sc.consumables;
        root.style.setProperty('--mwi-consumables', consVal);
        root.classList.add('mwi-consumables-active');
      } else {
        root.style.removeProperty('--mwi-consumables');
        root.classList.remove('mwi-consumables-active');
      }
      if (sc.buttonBg) {
        const btnVal = (sc.buttonBgAlpha !== undefined && sc.buttonBgAlpha < 1) ? colorToRGBA(sc.buttonBg, sc.buttonBgAlpha) : sc.buttonBg;
        root.style.setProperty('--mwi-button-bg', btnVal);
        root.classList.add('mwi-button-bg-active');
      } else {
        root.style.removeProperty('--mwi-button-bg');
        root.classList.remove('mwi-button-bg-active');
      }
      if (sc.tabsBg) {
        const tabsBgVal = (sc.tabsBgAlpha !== undefined && sc.tabsBgAlpha < 1) ? colorToRGBA(sc.tabsBg, sc.tabsBgAlpha) : sc.tabsBg;
        root.style.setProperty('--mwi-tabs-bg', tabsBgVal);
        root.classList.add('mwi-tabs-bg-active');
      } else {
        root.style.removeProperty('--mwi-tabs-bg');
        root.classList.remove('mwi-tabs-bg-active');
      }
      if (sc.accent) {
        const accVal = (sc.accentAlpha !== undefined && sc.accentAlpha < 1) ? colorToRGBA(sc.accent, sc.accentAlpha) : sc.accent;
        root.style.setProperty('--mwi-accent', accVal);
        root.classList.add('mwi-accent-active');
      } else {
        root.style.removeProperty('--mwi-accent');
        root.classList.remove('mwi-accent-active');
      }
      // text color falls back to accent when text is not explicitly set
      if (sc.text) root.style.setProperty('--mwi-text', sc.text);
      else if (sc.accent) root.style.setProperty('--mwi-text', sc.accent);
      else root.style.removeProperty('--mwi-text');
      // Custom background image (opt-in)
      try {
        if (cfg.backgroundUrl && String(cfg.backgroundUrl).trim()) {
          const urlVal = String(cfg.backgroundUrl).trim();
          root.style.setProperty('--mwi-bg-image', `url("${urlVal}")`);
          // set appearance vars (opacity/size/position)
          root.style.setProperty('--mwi-bg-opacity', (cfg.backgroundOpacity !== undefined ? cfg.backgroundOpacity : 1));
          // background size/position are fixed to cover/center for now
          if (cfg.backgroundEnabled) root.classList.add('mwi-bg-active'); else root.classList.remove('mwi-bg-active');
        } else {
          root.style.removeProperty('--mwi-bg-image');
          root.style.removeProperty('--mwi-bg-opacity');
          root.style.removeProperty('--mwi-bg-size');
          root.style.removeProperty('--mwi-bg-position');
          root.classList.remove('mwi-bg-active');
        }
      } catch (e) { log('apply background error', e); }
      // Use root-level CSS variables so dynamic elements inherit colors.
    try { applyHideOrganize(); } catch (e) {}
    // Apply panel swap from saved config
    try { document.documentElement.classList.toggle('mwi-swap-panels', cfg.swapPanels === true); } catch (e) {}
    try { document.documentElement.classList.toggle('mwi-chat-top', cfg.chatTop === true); } catch (e) {}
    try { document.documentElement.classList.toggle('mwi-header-bottom', cfg.headerBottom === true); } catch (e) {}
    } catch (e) { log('applySiteColors error', e); }
  }

  // Apply hide/organize settings
  function applyHideOrganize() {
    try {
      const order = Array.isArray(cfg.organizeOrder) && cfg.organizeOrder.length ? cfg.organizeOrder : ORGANIZE_ITEMS.filter(i=>i.id).map(i=>i.id);
      // ensure cfg.organizeOrder initialized
      if (!Array.isArray(cfg.organizeOrder) || !cfg.organizeOrder.length) cfg.organizeOrder = order.slice();
      // append any newly-added items not yet in the saved order (e.g. after importing an old theme)
      for (const item of ORGANIZE_ITEMS) { if (item.id && item.type !== 'separator' && !cfg.organizeOrder.includes(item.id)) cfg.organizeOrder.push(item.id); }
      for (const item of ORGANIZE_ITEMS) {
        if (!item || item.type === 'separator' || !item.id) continue;
        const hidden = cfg.hiddenElements && cfg.hiddenElements[item.id];
        const sels = Array.isArray(item.selectors) ? item.selectors : [];
        for (const s of sels) {
          try {
            const els = Array.from(document.querySelectorAll(s));
            for (const el of els) {
              try {
                if (hidden) {
                  el.style.setProperty('display','none','important');
                  try { el.dataset.mwiHidden = '1'; } catch (e) {}
                } else {
                  try {
                    if (el.dataset && el.dataset.mwiHidden) {
                      el.style.removeProperty('display');
                      delete el.dataset.mwiHidden;
                    }
                  } catch (e) {}
                }
              } catch (e) {}
            }
          } catch (e) {}
        }
        // Also support hiding navigation entries by label text
        try {
          const navs = Array.from(document.querySelectorAll('.NavigationBar_nav__3uuUl'));
          if (navs.length && item.label) {
            const want = (String(item.label||'')||'').trim().toLowerCase();
            for (const n of navs) {
              try {
                const labelSpan = n.querySelector('.NavigationBar_label__1uH-y');
                const txt = labelSpan && labelSpan.textContent ? labelSpan.textContent.trim().toLowerCase() : '';
                if (!txt) continue;
                if (txt === want) {
                  // hide the whole navigationLink wrapper if present
                  const link = n.closest('.NavigationBar_navigationLink__3eAHA') || n;
                  try {
                    if (hidden) {
                      link.style.setProperty('display','none','important');
                      try { link.dataset.mwiHidden = '1'; } catch (e) {}
                    } else {
                      try {
                        if (link.dataset && link.dataset.mwiHidden) {
                          link.style.removeProperty('display');
                          delete link.dataset.mwiHidden;
                        }
                      } catch (e) {}
                    }
                  } catch (e) {}
                }
              } catch (e) {}
            }
          }
        } catch (e) {}
      }
      // Also reorder nav elements in the DOM
      try { applyNavOrder(); } catch(e) {}
    } catch (e) { log('applyHideOrganize error', e); }
  }

  // Reorder navigation items in the game DOM to match cfg.organizeOrder
  function applyNavOrder() {
    try {
      const order = Array.isArray(cfg.organizeOrder) && cfg.organizeOrder.length ? cfg.organizeOrder : ORGANIZE_ITEMS.filter(i=>i.id).map(i=>i.id);
      // Build label 竊・id map
      const labelToId = {};
      for (const it of ORGANIZE_ITEMS) { if (it.id && it.label) labelToId[it.label.trim().toLowerCase()] = it.id; }
      // Find all nav link elements (hashed class names)
      const navLinks = Array.from(document.querySelectorAll('[class*="NavigationBar_navigationLink"]'));
      if (!navLinks.length) return;
      // Match each nav link to an id by its label text
      const idToEl = {};
      for (const link of navLinks) {
        const labelEl = link.querySelector('[class*="NavigationBar_label"]');
        const txt = labelEl ? labelEl.textContent.trim().toLowerCase() : '';
        if (txt && labelToId[txt]) idToEl[labelToId[txt]] = link;
      }
      // Also map 'links' to the minorNavigationLinks element (different element type, same container)
      const minorLinks = document.querySelector('[class*="NavigationBar_minorNavigationLinks"]');
      if (minorLinks) idToEl['links'] = minorLinks;
      // Find container (parent of first matched element)
      const firstMatched = Object.values(idToEl)[0];
      if (!firstMatched) return;
      const container = firstMatched.parentElement;
      if (!container) return;
      // Append each ordered element to end of container in sequence
      for (const id of order) {
        const el = idToEl[id];
        if (el && el.parentElement === container) container.appendChild(el);
      }
    } catch (e) { log('applyNavOrder error', e); }
  }

  // Open organize modal (hide toggles only)
  function openOrganizeModal() {
    try {
      // remove existing
      const existing = document.getElementById('mwi-organize-overlay'); if (existing) existing.remove();
      const over = document.createElement('div'); over.id = 'mwi-organize-overlay'; over.style.position = 'fixed'; over.style.inset = '0'; over.style.background = 'rgba(0,0,0,0.6)'; over.style.display = 'flex'; over.style.alignItems = 'center'; over.style.justifyContent = 'center'; over.style.zIndex = '10000030';
      const dialog = document.createElement('div'); dialog.id = 'mwi-organize-dialog'; dialog.style.position = 'relative'; dialog.style.background = '#0f1720'; dialog.style.color = '#e6eef8'; dialog.style.padding = '12px'; dialog.style.borderRadius = '8px'; dialog.style.width = '420px'; dialog.style.maxWidth = '86%'; dialog.style.maxHeight = '80vh'; dialog.style.overflow = 'hidden'; dialog.style.boxShadow = '0 8px 24px rgba(0,0,0,0.6)'; dialog.style.display = 'flex'; dialog.style.flexDirection = 'column';
      const title = document.createElement('h3'); title.textContent = 'Customize left side panel'; title.style.margin = '0 0 8px 0'; title.style.background = 'linear-gradient(90deg, #44aaff, #ff44cc)'; title.style.webkitBackgroundClip = 'text'; title.style.webkitTextFillColor = 'transparent'; title.style.backgroundClip = 'text';
      // top-right close button (matches main modal close style)
      const orgClose = document.createElement('button'); orgClose.id = 'mwi-organize-close'; orgClose.textContent = '\u2715';
      orgClose.style.position = 'absolute'; orgClose.style.top = '8px'; orgClose.style.right = '8px'; orgClose.style.background = 'transparent'; orgClose.style.border = '0'; orgClose.style.color = '#e6eef8'; orgClose.style.fontSize = '16px'; orgClose.style.cursor = 'pointer'; orgClose.style.padding = '4px';
      dialog.appendChild(orgClose);
      dialog.appendChild(title);

      const list = document.createElement('div'); list.className = 'mwi-org-list';

      // Resolve sprite URL from live DOM by matching a partial filename
      function getSpriteUrl(spriteKey) {
        try {
          const uses = document.querySelectorAll('svg use[href*="' + spriteKey + '_sprite"]');
          if (uses.length) { const href = uses[0].getAttribute('href'); return href ? href.split('#')[0] : null; }
        } catch (e) {}
        return null;
      }
      const skillsSpriteUrl = getSpriteUrl('skills');
      const miscSpriteUrl = getSpriteUrl('misc');

      let dragSrcId = null;

      function renderList() {
        list.innerHTML = '';
        const order = Array.isArray(cfg.organizeOrder) && cfg.organizeOrder.length ? cfg.organizeOrder.slice() : ORGANIZE_ITEMS.filter(i=>i.id).map(i=>i.id);
        // append newly-added items not yet in saved order (e.g. old imported theme)
        for (const it of ORGANIZE_ITEMS) { if (it.id && it.type !== 'separator' && !order.includes(it.id)) order.push(it.id); }
        const map = {};
        for (const it of ORGANIZE_ITEMS) if (it && it.id) map[it.id] = it;

        function makeRow(it) {
          const row = document.createElement('div'); row.className = 'mwi-org-row'; row.dataset.id = it.id;
          row.draggable = true;

          // Drag handle 窶・fixed width col
          const handleWrap = document.createElement('div'); handleWrap.className = 'mwi-org-row-handle';
          const handle = document.createElement('span'); handle.className = 'mwi-drag-handle'; handle.textContent = '⣿';
          handle.title = 'Drag to reorder';
          handleWrap.appendChild(handle); row.appendChild(handleWrap);

          // Icon 窶・fixed width col
          const iconWrap = document.createElement('div'); iconWrap.className = 'mwi-org-row-icon';
          if (it.icon) {
            try {
              const spriteUrl = it.icon.sprite === 'skills' ? skillsSpriteUrl : miscSpriteUrl;
              if (spriteUrl) {
                const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
                svg.setAttribute('role', 'img'); svg.setAttribute('width', '20'); svg.setAttribute('height', '20');
                svg.style.flexShrink = '0';
                const use = document.createElementNS('http://www.w3.org/2000/svg', 'use');
                use.setAttribute('href', spriteUrl + '#' + it.icon.id);
                svg.appendChild(use); iconWrap.appendChild(svg);
              }
            } catch (e) {}
          }
          row.appendChild(iconWrap);

          // Label 窶・fills remaining space
          const lbl = document.createElement('div'); lbl.className = 'mwi-org-row-label'; lbl.textContent = it.label;
          row.appendChild(lbl);

          // Right side
          const right = document.createElement('div'); right.className = 'mwi-org-row-right';
          if (it.noHide) {
            const badge = document.createElement('span'); badge.textContent = 'drag only'; badge.style.fontSize = '11px'; badge.style.opacity = '0.4'; badge.style.fontStyle = 'italic';
            right.appendChild(badge);
          } else {
            const chk = document.createElement('input'); chk.type = 'checkbox'; chk.checked = !!(cfg.hiddenElements && cfg.hiddenElements[it.id]);
            chk.addEventListener('change', () => { try { cfg.hiddenElements = cfg.hiddenElements || {}; cfg.hiddenElements[it.id] = chk.checked; saveSettings(); applyHideOrganize(); } catch (e) { log('hide toggle error', e); } });
            const chkLabel = document.createElement('label'); chkLabel.textContent = 'Hide'; chkLabel.style.marginLeft = '8px';
            right.appendChild(chk); right.appendChild(chkLabel);
            // Clicking anywhere in the row (except handle) also toggles the checkbox
            row.style.cursor = 'pointer';
            row.addEventListener('click', (ev) => { try { if (ev.target === chk || ev.target === handle) return; chk.checked = !chk.checked; chk.dispatchEvent(new Event('change')); } catch (e) {} });
          }
          row.appendChild(right);

          // Drag events
          row.addEventListener('dragstart', (ev) => {
            dragSrcId = it.id;
            row.classList.add('dragging');
            ev.dataTransfer.effectAllowed = 'move';
            ev.dataTransfer.setData('text/plain', it.id);
          });
          row.addEventListener('dragend', () => {
            row.classList.remove('dragging');
            list.querySelectorAll('.mwi-org-row').forEach(r => r.classList.remove('drag-over-top','drag-over-bottom'));
          });
          row.addEventListener('dragover', (ev) => {
            ev.preventDefault(); ev.dataTransfer.dropEffect = 'move';
            if (!dragSrcId || dragSrcId === it.id) return;
            const rect = row.getBoundingClientRect();
            const half = rect.top + rect.height / 2;
            list.querySelectorAll('.mwi-org-row').forEach(r => r.classList.remove('drag-over-top','drag-over-bottom'));
            row.classList.add(ev.clientY < half ? 'drag-over-top' : 'drag-over-bottom');
          });
          row.addEventListener('dragleave', () => row.classList.remove('drag-over-top','drag-over-bottom'));
          row.addEventListener('drop', (ev) => {
            ev.preventDefault();
            row.classList.remove('drag-over-top','drag-over-bottom');
            if (!dragSrcId || dragSrcId === it.id) return;
            const currentOrder = Array.isArray(cfg.organizeOrder) && cfg.organizeOrder.length ? cfg.organizeOrder.slice() : ORGANIZE_ITEMS.filter(i=>i.id).map(i=>i.id);
            const fromIdx = currentOrder.indexOf(dragSrcId);
            let toIdx = currentOrder.indexOf(it.id);
            if (fromIdx === -1 || toIdx === -1) return;
            const rect = row.getBoundingClientRect();
            const insertBefore = ev.clientY < rect.top + rect.height / 2;
            currentOrder.splice(fromIdx, 1);
            toIdx = currentOrder.indexOf(it.id);
            currentOrder.splice(insertBefore ? toIdx : toIdx + 1, 0, dragSrcId);
            cfg.organizeOrder = currentOrder;
            saveSettings();
            try { applyNavOrder(); } catch(e) {}
            renderList();
            dragSrcId = null;
          });

          return row;
        }

        // Render all items in current order (flat, no grouping by separator)
        const container = document.createElement('div'); container.style.display = 'flex'; container.style.flexDirection = 'column';
        for (const id of order) {
          const it = map[id]; if (!it) continue;
          container.appendChild(makeRow(it));
        }
        list.appendChild(container);
      }

      renderList(); dialog.appendChild(list);
      const row = document.createElement('div'); row.style.display = 'flex'; row.style.justifyContent = 'center'; row.style.marginTop = '8px';
      const closeBtn = document.createElement('button'); closeBtn.type = 'button'; closeBtn.textContent = 'Close';
      // style like main modal action buttons
      closeBtn.style.background = '#1f7d3d'; closeBtn.style.color = '#fff'; closeBtn.style.border = '0'; closeBtn.style.padding = '8px 12px'; closeBtn.style.borderRadius = '6px'; closeBtn.classList.add('mwi-push-btn');
      const orgResetBtn = document.createElement('button'); orgResetBtn.type = 'button'; orgResetBtn.textContent = 'Reset to defaults';
      orgResetBtn.style.background = '#7f1d1d'; orgResetBtn.style.color = '#fff'; orgResetBtn.style.border = '0'; orgResetBtn.style.padding = '8px 12px'; orgResetBtn.style.borderRadius = '6px'; orgResetBtn.style.marginLeft = '8px'; orgResetBtn.classList.add('mwi-push-btn');
      orgResetBtn.addEventListener('click', () => {
        try {
          cfg.organizeOrder = DEFAULT_CFG.organizeOrder.slice ? DEFAULT_CFG.organizeOrder.slice() : [];
          cfg.hiddenElements = {};
          saveSettings();
          try { applyHideOrganize(); } catch (e) {}
          try { applyNavOrder(); } catch (e) {}
          renderList();
        } catch (e) { log('organize reset error', e); }
      });
      row.appendChild(closeBtn); row.appendChild(orgResetBtn); dialog.appendChild(row);
      function closeOrganizeModal() {
        over.classList.remove('mwi-organize-open');
        over.classList.add('mwi-organize-closing');
        setTimeout(() => { try { over.remove(); } catch (e) {} }, 200);
      }
      orgClose.addEventListener('click', () => closeOrganizeModal());
      closeBtn.addEventListener('click', () => closeOrganizeModal());
      over.appendChild(dialog);
      // clicking outside closes organize modal only
      over.addEventListener('click', (ev) => { try { if (ev.target === over) closeOrganizeModal(); } catch (e) {} });
      document.body.appendChild(over);
      requestAnimationFrame(() => requestAnimationFrame(() => over.classList.add('mwi-organize-open')));
    } catch (e) { log('openOrganizeModal error', e); }
  }

  // Chat-text helpers: recolor message text that is white
  function applyChatTextToNode(node) {
    try {
      if (!node || !(node instanceof Element)) return;
      // If this is a chat message container, mark only its text elements (exclude username span)
      const isMsgContainer = (node.matches && (node.matches('.ChatMessage_chatMessage__2wev4') || node.matches('[class*="ChatMessage_chatMessage__"]')));
      if (isMsgContainer) {
        const descendants = Array.from(node.querySelectorAll('*'));
        const parentText = (node.textContent || '').trim();
        for (const d of descendants) {
          try {
            // Skip if this element is within a username element
            if (d.closest && d.closest('[class*="CharacterName_name__"]')) continue;
            // Only consider leaf elements (no element children) to avoid marking containers
            if (d.children && d.children.length) continue;
            const txt = (d.textContent || '').trim();
            if (!txt) continue;
            // Heuristic: if the parent message text begins with this descendant followed by ':'
            // it's very likely the username; skip it.
            if (parentText.startsWith(txt + ':')) continue;
            const cs = getComputedStyle(d);
            if (cs && cs.color && cs.color.trim() === 'rgb(231, 231, 231)') d.setAttribute('data-mwi-chat-text-applied', '1');
          } catch (e) { /* ignore per-node errors */ }
        }
        return;
      }
      // Otherwise, mark the node itself if it's text-colored white and not a username
      // Skip nodes that are the username or contained within a username element
      if (node.closest && node.closest('[class*="CharacterName_name__"]')) return;
      const cs = getComputedStyle(node);
      if (!cs) return;
      if (cs.color && cs.color.trim() === 'rgb(231, 231, 231)') {
        node.setAttribute('data-mwi-chat-text-applied', '1');
      }
    } catch (e) { /* ignore */ }
  }

  function applyChatTextToAll() {
    try {
      const sel = '.ChatMessage_chatMessage__2wev4, [class*="ChatMessage_chatMessage__"]';
      const nodes = Array.from(document.querySelectorAll(sel));
      for (const n of nodes) applyChatTextToNode(n);
    } catch (e) { /* ignore */ }
  }

  function clearChatTextFromAll() {
    try {
      const nodes = Array.from(document.querySelectorAll('[data-mwi-chat-text-applied]'));
      for (const n of nodes) n.removeAttribute('data-mwi-chat-text-applied');
    } catch (e) { /* ignore */ }
  }

  function ensureChatTextObserver() {
    try {
      if (chatTextObserver) return;
      chatTextObserver = new MutationObserver((muts) => {
        try {
          for (const m of muts) {
            for (const n of Array.from(m.addedNodes || [])) {
              if (!(n instanceof Element)) continue;
              if (n.matches && (n.matches('.ChatMessage_chatMessage__2wev4') || n.querySelector('.ChatMessage_chatMessage__2wev4'))) applyChatTextToAll();
            }
          }
        } catch (e) { }
      });
      chatTextObserver.observe(document.body, { childList: true, subtree: true });
    } catch (e) { /* ignore */ }
  }

  // load persisted settings (if any)
  loadSettings();
  // If reset-on-refresh is enabled, restore defaults on startup except Dev keys
  try {
    if (cfg.resetOnRefresh) {
      try {
        const preserved = {};
        // preserve Dev keys and the reset flag itself
        for (const k of DEV_KEYS) if (k in cfg) preserved[k] = cfg[k];
        preserved.resetOnRefresh = cfg.resetOnRefresh;
        // reset all keys to DEFAULT_CFG
        for (const k of Object.keys(DEFAULT_CFG)) cfg[k] = JSON.parse(JSON.stringify(DEFAULT_CFG[k]));
        // restore preserved
        for (const k of Object.keys(preserved)) cfg[k] = preserved[k];
        // persist the reset defaults (keep resetOnRefresh enabled)
        saveSettings();
      } catch (e) { log('resetOnRefresh handling error', e); }
    }
  } catch (e) {}
  // immediately apply persisted/site colors
  try { applySiteColors(); } catch (e) {}

  // Ensure hide/organize is applied after dynamic page content loads.
  try {
    // small delayed reapply in case elements mount after initial run
    setTimeout(() => { try { applyHideOrganize(); } catch (e) {} }, 600);
    // also wait for the navigation container and reapply + observe for changes
    waitFor(() => document.querySelector('.NavigationBar_navigationLinks__1XSSb') || document.querySelector('.NavigationBar_navigationLinks'), 10000, 200)
      .then((nav) => {
        try {
          if (nav) applyHideOrganize();
          // debounce helper
          let to = null;
          const obs = new MutationObserver((muts) => {
            try {
              if (to) clearTimeout(to);
              to = setTimeout(() => { try { applyHideOrganize(); } catch (e) {} }, 120);
            } catch (e) {}
          });
          try { obs.observe(document.body, { childList: true, subtree: true }); } catch (e) {}
        } catch (e) {}
      }).catch(() => {});
  } catch (e) {}

  function log(...args) { if (cfg.debug) console.log('[MWI-HL]', ...args); }

  function waitFor(conditionFn, timeout = 10000, interval = 200) {
    return new Promise((resolve, reject) => {
      const start = Date.now();
      (function check() {
        try {
          const v = conditionFn();
          if (v) return resolve(v);
        } catch (e) {}
        if (Date.now() - start >= timeout) return resolve(null);
        setTimeout(check, interval);
      })();
    });
  }

  // Attempt to discover a mapping of item id/name -> color by scanning globals.
  function discoverCollectionColors() {
    const hridMap = new Map();
    const nameMap = new Map();
    const hridNameMap = new Map();
    const nameToHrid = new Map();
    // Use init payload if available 窶・handle Map-like or plain object
    try {
      const util = window.localStorageUtil;
      if (util && typeof util.getInitClientData === 'function') {
        const init = util.getInitClientData();
        const idm = init && init.itemDetailMap;
        if (idm) {
          const entries = (typeof idm.forEach === 'function' && typeof idm.entries === 'function') ? Array.from(idm.entries()) : Object.entries(idm);
          for (const [k, v] of entries) {
            try {
              const key = String(k);
              if (v && v.name) {
                const name = String(v.name).toLowerCase();
                nameToHrid.set(name, key);
                hridNameMap.set(key, String(v.name));
              }
              const color = v && (v.collectionColor || v.color || v.hex);
              if (color) {
                hridMap.set(key, color);
                if (v && v.name) nameMap.set(String(v.name).toLowerCase(), color);
              }
            } catch (e) {}
          }
        }
      }
    } catch (e) { log('discover error', e); }
    return { hridMap, nameMap, hridNameMap, nameToHrid };
  }

  // Build a best-effort key extractor for inventory elements
  function elementItemKey(el) {
    if (!el) return null;
    // common attributes
    const attrs = ['data-item-id', 'data-id', 'data-item', 'data-itemid'];
    for (const a of attrs) {
      const v = el.getAttribute && el.getAttribute(a);
      if (v) return String(v);
    }
    // dataset
    for (const k of Object.keys(el.dataset || {})) {
      if (k.toLowerCase().includes('item')) return String(el.dataset[k]);
    }
    // image filename
    const img = el.querySelector && el.querySelector('img');
    if (img) {
      if (img.alt) return String(img.alt).trim();
      if (img.src) {
        const parts = img.src.split('/');
        const last = parts[parts.length - 1];
        return last.split('.')[0];
      }
    }
    // svg <use href="#sprite"> pattern (collection icons)
    try {
      const use = el.querySelector && (el.querySelector('use') || el.querySelector('svg use'));
      if (use) {
        // use.href may be an SVGAnimatedString
        const href = use.getAttribute('href') || use.getAttribute('xlink:href') || (use.href && use.href.baseVal);
        if (href) {
          const frag = String(href).split('#').slice(-1)[0];
          if (frag) return String(frag);
        }
      }
      // ancestor svg
      const an = el.closest && el.closest('svg');
      if (an) {
        const use2 = an.querySelector('use');
        if (use2) {
          const href2 = use2.getAttribute('href') || use2.getAttribute('xlink:href') || (use2.href && use2.href.baseVal);
          if (href2) {
            const frag2 = String(href2).split('#').slice(-1)[0];
            if (frag2) return String(frag2);
          }
        }
      }
    } catch (e) {}
    // title or text fallback
    if (el.title) return String(el.title).trim();
    const txt = el.textContent && el.textContent.trim();
    if (txt) return txt.split('\n')[0].trim();
    return null;
  }

  function highlightElement(el, color, itemAlpha) {
    if (!el || !color) return;
    // stronger visual: outline (higher specificity) plus inset box-shadow
    try {
      // Only style small, item-like elements to avoid tinting the whole inventory
      if (!isSmallNode(el)) return;
      // use setProperty with important to override game styles
      // thinner border like collection log, with a soft outer glow
      const outlineWidth = (cfg.outlineWidth !== undefined) ? cfg.outlineWidth : 2;
      const glowAlpha = (itemAlpha !== undefined && !isNaN(Number(itemAlpha))) ? Number(itemAlpha) : ((cfg.glowAlpha !== undefined) ? cfg.glowAlpha : 0.08);
      // inner curved border (inset) + outer glow; and subtle background tint
      const innerW = (cfg.innerBorderWidth !== undefined) ? cfg.innerBorderWidth : outlineWidth;
      const bg = colorToRGBA(color, (cfg.bgAlpha !== undefined) ? cfg.bgAlpha : 0.12);
      if (bg) el.style.setProperty('background-color', bg, 'important');
      const glow = colorToRGBA(color, glowAlpha) || 'rgba(0,0,0,0)';
      const spread = (cfg.glowSpread !== undefined) ? cfg.glowSpread : Math.max(6, outlineWidth*2);
      const blur = (cfg.glowBlur !== undefined) ? cfg.glowBlur : Math.max(4, Math.floor(spread/2));
      // compose inset inner border and a soft outer halo (blur + spread)
      const innerPart = (cfg.showInnerBorder === false) ? '' : `inset 0 0 0 ${innerW}px ${color}, `;
      const boxShadow = `${innerPart}0 0 ${blur}px ${spread}px ${glow}`;
      el.style.setProperty('box-shadow', boxShadow, 'important');
      if (!el.style.borderRadius) el.style.borderRadius = '8px';
    } catch (e) {
      // style assignment may fail on SVG or read-only nodes
    }
  }

  // Parse quantity strings like '28M', '3.2k', '410', etc.
  function parseQuantity(str) {
    if (!str) return 0;
    const s = String(str).trim().toUpperCase().replace(/,/g,'');
    const m = s.match(/^([0-9]*\.?[0-9]+)\s*([KMBT])?$/i);
    if (!m) return parseInt(s) || 0;
    let v = parseFloat(m[1]);
    const suf = m[2] || '';
    if (suf === 'K') v *= 1e3;
    if (suf === 'M') v *= 1e6;
    if (suf === 'B') v *= 1e9;
    if (suf === 'T') v *= 1e12;
    return Math.floor(v);
  }

  // Default quantity tiers (min inclusive). You can edit these in cfg if desired.
  const defaultQuantityTiers = [
    { min: 100000, color: '#9b59b6' },
    { min: 10000, color: '#d0333d' },
    { min: 1000, color: '#a272e4' },
    { min: 100, color: '#1d8ce0' },
    { min: 10, color: '#259c85' },
    { min: 1, color: '#7f8c8d' },
    { min: 0, color: '#7f8c8d' }
  ];

  function quantityToColor(q) {
    // Prefer collection-style tiers if provided, then cfg.quantityTiers, then defaults
    const tiers = cfg.collectionQuantityTiers || cfg.quantityTiers || defaultQuantityTiers;
    for (const t of tiers) {
      if (q >= t.min) {
        return { color: t.color, alpha: (t.alpha !== undefined ? t.alpha : 1) };
      }
    }
    return null;
  }

  // Convert color string (#rgb, #rrggbb, rgb(...), rgba(...)) to rgba(...) with given alpha
  function colorToRGBA(c, alpha) {
    try {
      if (!c) return null;
      c = String(c).trim();
      if (c.startsWith('rgba')) {
        // replace alpha
        const parts = c.replace(/rgba\(|\)/g, '').split(',').map(s=>s.trim());
        return `rgba(${parts[0]}, ${parts[1]}, ${parts[2]}, ${alpha})`;
      }
      if (c.startsWith('rgb')) {
        const parts = c.replace(/rgb\(|\)/g, '').split(',').map(s=>s.trim());
        return `rgba(${parts[0]}, ${parts[1]}, ${parts[2]}, ${alpha})`;
      }
      // hex
      const hex = c.replace('#','');
      if (hex.length === 3) {
        const r = parseInt(hex[0]+hex[0],16);
        const g = parseInt(hex[1]+hex[1],16);
        const b = parseInt(hex[2]+hex[2],16);
        return `rgba(${r}, ${g}, ${b}, ${alpha})`;
      }
      if (hex.length === 6) {
        const r = parseInt(hex.slice(0,2),16);
        const g = parseInt(hex.slice(2,4),16);
        const b = parseInt(hex.slice(4,6),16);
        return `rgba(${r}, ${g}, ${b}, ${alpha})`;
      }
      return null;
    } catch (e) { return null; }
  }

  // Reusable small-node check (used in multiple places)
  function isSmallNode(node) {
    try {
      if (!node || !node.getBoundingClientRect) return false;
      const r = node.getBoundingClientRect();
      if (!r || !r.width || !r.height) return false;
      return (r.width <= 240 && r.height <= 240);
    } catch (e) { return false; }
  }

  // find a sensible container to apply highlight to (avoid tiny count elements)
  function findHighlightTarget(el) {
    if (!el) return null;
    // Prefer the Item wrapper when available (e.g. Item_item__* / Item_clickable__*)
    try {
      const itemAncestor = el.closest && (el.closest('[class*="Item_item"]') || el.closest('[class*="Item_clickable"]') || el.closest('[class*="Item_"]'));
      if (itemAncestor && isSmallNode(itemAncestor)) return itemAncestor;
    } catch (e) {}
    // Prefer an explicit icon/container around the svg/img. Try several heuristics and prefer
    // small wrappers so we style only the tile/icon, not entire rows.
    try {
      // If the element itself is an icon wrapper (contains svg or img) and is small, use it
      if (el.querySelector && (el.querySelector('use') || el.querySelector('svg') || el.querySelector('img'))) {
        if (isSmallNode(el)) return el;
      }

      // look for nearest ancestor that directly wraps an svg/img
      const iconAncestor = el.closest && (el.closest('[class*="icon"]') || el.closest('[class*="Icon"]') || el.closest('[class*="IconContainer"]') || el.closest('[class*="Collection_"]'));
      if (iconAncestor && isSmallNode(iconAncestor)) return iconAncestor;

      // nearest svg ancestor
      const svgan = el.closest && el.closest('svg');
      if (svgan) {
        const parent = svgan.parentElement;
        if (parent && isSmallNode(parent)) return parent;
        if (isSmallNode(svgan)) return svgan;
      }

      // if element has a direct sibling or child img/svg, prefer that small sibling
      try {
        const p = el.parentElement;
        if (p) {
          const imgs = p.querySelectorAll && p.querySelectorAll('img, svg, use');
          for (const candidate of imgs || []) {
            const wrap = candidate.closest && candidate.closest('*') || candidate.parentElement;
            if (wrap && isSmallNode(wrap)) return wrap;
          }
        }
      } catch (e) {}

      // fallback: parent that is small enough
      if (el.parentElement && isSmallNode(el.parentElement)) return el.parentElement;
      if (isSmallNode(el)) return el;
    } catch (e) {}
    // nothing appropriate 窶・avoid styling large containers
    return null;
  }

  // Look for a nearby SVG/icon element associated with this element (useful when the visible node is just the count)
  function findAssociatedIcon(el) {
    if (!el) return null;
    try {
      // check siblings first
      const p = el.parentElement;
      if (p) {
        // look for svg/use inside parent
        const u = p.querySelector('use') || p.querySelector('svg');
        if (u) {
          // return the element wrapping the svg (small target)
          const wrap = u.closest && u.closest('.Collection_iconContainer__2cD7o') || u.closest('svg') || u.closest('*');
          if (wrap && isSmallNode(wrap)) return wrap;
        }
        // look at previous/next element siblings
        const prev = el.previousElementSibling;
        const next = el.nextElementSibling;
        for (const s of [prev, next]) {
          if (!s) continue;
          if (s.querySelector && s.querySelector('use')) return s;
          if (s.tagName && s.tagName.toLowerCase() === 'svg') return s;
        }
      }
      // search within element for use
      if (el.querySelector && el.querySelector('use')) return el;
    } catch (e) {}
    return null;
  }

  // Find the count element near an item (target the class the user reported)
  function findCountElement(el) {
    if (!el) return null;
    const cls = '.Item_count__1HVvv';
    try {
      // if the element itself is the count
      if (el.classList && el.classList.contains && el.classList.contains('Item_count__1HVvv')) return el;
      // descendant
      if (el.querySelector) {
        const d = el.querySelector(cls);
        if (d) return d;
      }
      // check up to 3 ancestor levels for a count
      let p = el.parentElement;
      for (let i = 0; i < 3 && p; i++, p = p.parentElement) {
        try {
          const found = p.querySelector && p.querySelector(cls);
          if (found) return found;
        } catch (e) {}
      }
      // check slot-like container
      const slot = el.closest && (el.closest('.slot') || el.closest('.inventory-slot') || el.closest('[class*="Item_"]') || el.closest('[class*="Inventory_"]'));
      if (slot) {
        const s = slot.querySelector && slot.querySelector(cls);
        if (s) return s;
      }
      // check siblings via parent
      const parent = el.parentElement;
      if (parent) {
        const sib = parent.querySelector && parent.querySelector(cls);
        if (sib) return sib;
      }
    } catch (e) {}
    return null;
  }

  function highlightInventory(maps) {
    const hridMap = maps && maps.hridMap ? maps.hridMap : new Map();
    const nameMap = maps && maps.nameMap ? maps.nameMap : new Map();
    const hridNameMap = maps && maps.hridNameMap ? maps.hridNameMap : new Map();
    const nameToHrid = maps && maps.nameToHrid ? maps.nameToHrid : new Map();
    // prepare name keys for substring matching (longer names first)
    const nameKeys = Array.from(nameMap.keys()).sort((a,b)=>b.length - a.length);

    // Debug: report map sizes and a small sample
    log('highlightInventory 窶・hridMap size:', hridMap.size, 'nameMap size:', nameMap.size, 'hridNameMap size:', hridNameMap.size, 'nameToHrid size:', nameToHrid.size);
    if (cfg.debug && nameMap.size) {
      const sample = Array.from(nameMap.entries()).slice(0,5).map(e => e[0] + '->' + e[1]);
      log('nameMap sample:', sample);
    }

    // Evaluate which coloring strategy to use
    if (cfg.colorMode === 'None') {
      log('highlightInventory skipped 窶・colorMode: None');
      return;
    }
    const useQuantity = cfg.colorMode === 'Quantity';
    const useAll = cfg.colorMode === 'All';

    // Collect likely item candidates. Keep set small and focused to avoid scanning the whole DOM.
    const els = new Set();
    // Restrict to inventory panels matching the game's Inventory wrapper to avoid site-wide effects
    const inventoryRoots = Array.from(document.querySelectorAll('.Inventory_inventory__17CH2'));
    if (inventoryRoots.length) {
      for (const root of inventoryRoots) {
        try {
          const selList = ['[data-item-id], [data-id], [data-item], [data-itemid]'].concat(cfg.inventorySelectors || []);
          for (const sel of selList) {
            const nl = root.querySelectorAll(sel);
            for (const n of nl) els.add(n);
          }
          const icons = root.querySelectorAll('.Collection_iconContainer__2cD7o, [class*="icon"], [class*="Icon"], [class*="Collection_"]');
          for (const n of icons) if (isSmallNode(n)) els.add(n);
          const imgs = root.querySelectorAll('img, svg');
          for (const im of imgs) {
            if (isSmallNode(im)) { els.add(im); continue; }
            const p = im.closest && im.closest('*');
            if (p && isSmallNode(p)) els.add(p);
          }
        } catch (e) { /* ignore individual root errors */ }
      }
    } else {
      const globalSel = ['[data-item-id], [data-id], [data-item], [data-itemid]'].concat(cfg.inventorySelectors || []);
      try {
        for (const sel of globalSel) {
          const nl = document.querySelectorAll(sel);
          for (const n of nl) els.add(n);
        }
        const icons = document.querySelectorAll('.Collection_iconContainer__2cD7o, [class*="icon"], [class*="Icon"], [class*="Collection_"]');
        for (const n of icons) if (isSmallNode(n)) els.add(n);
        const imgs = document.querySelectorAll('img, svg');
        for (const im of imgs) {
          if (isSmallNode(im)) { els.add(im); continue; }
          const p = im.closest && im.closest('*');
          if (p && isSmallNode(p)) els.add(p);
        }
      } catch (e) { /* ignore fallback selection errors */ }
    }

    // Cap number of elements processed to avoid long synchronous loops
    const elArray = Array.from(els).slice(0, 1200);
    if (cfg.debug) log('highlightInventory candidates:', elArray.length);
    let applied = 0;
    for (const el of elArray) {
      let colorInfo = null; // { color: '#rrggbb' , alpha: 0-1 }
      // Category mode: try to infer a category and map to configured colors
      // All mode: force single color for everything
      if (!colorInfo && useAll) {
        try {
          const col = cfg.allColor || '#ffffff';
          const a = (cfg.allAlpha !== undefined ? cfg.allAlpha : 1);
          colorInfo = { color: col, alpha: a };
        } catch (e) {}
      }

      // Quantity (or fallback) mode: existing quantity/name matching logic
      if (!colorInfo && useQuantity) {
        try {
          const countEl = findCountElement(el);
          if (countEl && countEl.textContent) {
            const q = parseQuantity(countEl.textContent);
            const qc = quantityToColor(q);
            if (qc) {
              colorInfo = { color: qc.color, alpha: (qc.alpha !== undefined ? qc.alpha : 1) };
              if (cfg.debug) log('Found count', countEl.textContent.trim(), '->', q, 'colorInfo', colorInfo);
            }
          }
        } catch (e) {}

        if (!colorInfo) {
          const key = elementItemKey(el);
          let resolvedHrid = null;
          if (key) {
            const tmp = hridMap.get(String(key)) || hridMap.get(String(parseInt(key) || '')) || nameMap.get(String(key).toLowerCase());
            if (tmp) colorInfo = { color: tmp, alpha: 1 };
            if (!colorInfo) {
              const lk = String(key).toLowerCase();
              if (nameToHrid.has(lk)) resolvedHrid = nameToHrid.get(lk);
            }
          }
          if (!colorInfo) {
            const txt = (el.textContent || '').toLowerCase();
            if (txt) {
              for (const n of nameKeys) {
                if (txt.indexOf(n) !== -1) { const tmp = nameMap.get(n); if (tmp) colorInfo = { color: tmp, alpha: 1 }; break; }
              }
            }
          }
          if (!colorInfo && !resolvedHrid) {
            const txt = (el.textContent || '').toLowerCase();
            if (txt) {
              for (const n of nameKeys) {
                if (txt.indexOf(n) !== -1) { const tmp = nameMap.get(n); if (tmp) colorInfo = { color: tmp, alpha: 1 }; resolvedHrid = nameToHrid.get(n); break; }
              }
            }
          }
          if (!colorInfo && resolvedHrid && hridNameMap.has(resolvedHrid)) {
            const nm = String(hridNameMap.get(resolvedHrid)).toLowerCase();
            const tmp = nameMap.get(nm) || null;
            if (tmp) colorInfo = { color: tmp, alpha: 1 };
          }
        }
      }
      if (colorInfo) {
        let target = null;
        try { target = findHighlightTarget(el) || findAssociatedIcon(el); } catch (e) { target = null; }
        if (target) { highlightElement(target, colorInfo.color, colorInfo.alpha); applied++; }
        }
      }

    // If nothing was highlighted, try a broader text-search fallback inside likely inventory containers
    if (!applied && nameKeys.length) {
      const roots = inventoryRoots.length ? inventoryRoots.slice() : [];
      if (!roots.length) {
        document.querySelectorAll('[id^="mwi-inventory-"]').forEach(e => roots.push(e));
        document.querySelectorAll('.inventory, .panel-content, .list, .items-list').forEach(e => roots.push(e));
      }
      // dedupe
      const uniqRoots = Array.from(new Set(roots));
      for (const root of uniqRoots) {
        if (!root) continue;
        const children = Array.from(root.querySelectorAll('*')).slice(0,1000);
        for (const el of children) {
          try {
            if (!el.textContent) continue;
            const txt = el.textContent.toLowerCase();
            for (const n of nameKeys) {
              if (txt.indexOf(n) !== -1) {
                const color = nameMap.get(n);
                if (color) { const target = findHighlightTarget(el) || el; highlightElement(target, color, 1); applied++; }
                break;
              }
            }
          } catch (e) {}
        }
      }
    }
    log('highlightInventory applied highlights:', applied);
  }

    // --- Settings UI (button + modal) ---
    function injectStyles() {
      const css = `
          #mwi-settings-btn { display:inline-flex; align-items:center; justify-content:center; min-width:36px; height:36px; padding:6px 10px; border-radius:6px; background:#222; color:#fff; border:1px solid rgba(255,255,255,0.06); cursor:pointer; margin-left:8px; transition: filter 120ms ease; }
          #mwi-settings-btn:hover { filter: brightness(1.25); }
          #mwi-settings-btn:active { transform: translateY(1px) scale(0.96); filter: brightness(0.85); }
          /* Frosted button style (default) */
          #mwi-settings-btn.mwi-btn-frosted { background: linear-gradient(90deg, rgba(255,68,204,0.18), rgba(68,170,255,0.18)); border: 1px solid rgba(255,255,255,0.15); color: transparent; background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparent; position: relative; }
          #mwi-settings-btn.mwi-btn-frosted::before { content:''; position:absolute; inset:0; border-radius:inherit; background: linear-gradient(90deg, rgba(255,68,204,0.18), rgba(68,170,255,0.18)); z-index:0; }
          #mwi-settings-btn.mwi-btn-frosted span { position:relative; z-index:1; background: linear-gradient(90deg, #ff44cc, #44aaff); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
          .mwi-push-btn { transition: filter 120ms ease !important; cursor: pointer; }
          .mwi-push-btn:hover { filter: brightness(1.25) !important; }
          .mwi-push-btn:active { transform: translateY(1px) scale(0.96) !important; filter: brightness(0.85) !important; }
          #mwi-settings-overlay { position:fixed; inset:0; background:rgba(0,0,0,0.45); display:flex; align-items:center; justify-content:center; z-index:9999999; }
          #mwi-settings-dialog { background: rgba(15,23,32,0.25); backdrop-filter: blur(18px) saturate(1.4); -webkit-backdrop-filter: blur(18px) saturate(1.4); border: 1px solid rgba(255,255,255,0.08); color:#e6eef8; padding:16px; border-radius:12px; width:520px; max-width:92%; box-shadow:0 8px 32px rgba(0,0,0,0.7); display:flex; flex-direction:column; position:relative; overflow:hidden; }
          #mwi-settings-dialog::before { content:''; position:absolute; inset:0; border-radius:inherit; pointer-events:none; background: radial-gradient(400px circle at var(--glow-x,50%) var(--glow-y,50%), rgba(255,68,204,0.13) 0%, transparent 70%); transition: background 0.1s ease; z-index:0; }
          #mwi-settings-dialog > * { position:relative; z-index:1; }
          /* Solid UI style overrides */
          #mwi-settings-dialog.mwi-ui-solid { background: #0f1720; backdrop-filter: none; -webkit-backdrop-filter: none; border: 1px solid rgba(255,255,255,0.12); }
          #mwi-settings-dialog.mwi-ui-solid::before { display: none; }
          #mwi-search-wrap { background: transparent; }
          #mwi-settings-dialog.mwi-ui-solid #mwi-search-wrap { background: #0f1720; }
          /* Dialog pop animations - more noticeable pop with overshoot */
          @keyframes mwi-pop-in {
            0% { transform: translateY(-20px) scale(0.9); opacity: 0; }
            100% { transform: translateY(0) scale(1); opacity: 1; }
          }
          @keyframes mwi-pop-out {
            0% { transform: translateY(0) scale(1); opacity: 1; }
            100% { transform: translateY(-20px) scale(0.9); opacity: 0; }
          }
          #mwi-settings-dialog { transform-origin: center top; /* start slightly up and smaller; transition to visible */ transform: translateY(-20px) scale(0.9); opacity: 0; transition: transform 200ms ease, opacity 200ms ease; }
          .mwi-dialog-open #mwi-settings-dialog { transform: translateY(0) scale(1); opacity: 1; }
          .mwi-dialog-closing #mwi-settings-dialog { animation: mwi-pop-out 200ms ease both; }
          /* Ensure the settings modal is not affected by site button/accent overrides */
          #mwi-settings-dialog, #mwi-settings-dialog * { --mwi-button-bg: unset !important; --mwi-accent: unset !important; --mwi-progress-bar: unset !important; --mwi-skill-actions: unset !important; --mwi-combat: unset !important; }
          #mwi-settings-dialog button, #mwi-settings-dialog .btn, #mwi-settings-dialog [class*="MuiButton"], #mwi-settings-dialog [class*="Button_"] {
            /* Don't override explicit inline backgrounds so modal buttons keep their intended colors */
            background-image: none !important; border-color: initial !important; box-shadow: initial !important; color: inherit !important;
          }
          #mwi-settings-dialog button::before, #mwi-settings-dialog button::after, #mwi-settings-dialog [class*="MuiButton"]::before, #mwi-settings-dialog [class*="MuiButton"]::after { background-image: none !important; }
          #mwi-settings-dialog h3 { margin:0 0 8px 0; font-size:16px; }
          #mwi-settings-close { position:absolute; top:10px; right:10px; cursor:pointer; background:transparent; border:0; color:#fff; font-size:16px; z-index:10000002; }
          /* Share modal (Import/Export) */
          #mwi-share-modal-overlay { position: fixed; inset: 0; display:flex; align-items:center; justify-content:center; background: rgba(0,0,0,0.6); z-index:10000020; }
          .mwi-share-modal { background:#0f1720; color:#e6eef8; padding:12px; border-radius:8px; width:560px; max-width:92%; box-shadow:0 8px 24px rgba(0,0,0,0.6); transform-origin: center top; transform: translateY(-20px) scale(0.9); opacity: 0; transition: transform 200ms ease, opacity 200ms ease; }
          .mwi-share-open .mwi-share-modal { transform: translateY(0) scale(1); opacity: 1; }
          .mwi-share-closing .mwi-share-modal { animation: mwi-pop-out 200ms ease both; }
          .mwi-share-modal textarea { width:100%; height:140px; resize:vertical; margin-bottom:8px; background:#071018; color:#e6eef8; border:1px solid rgba(255,255,255,0.06); border-radius:6px; padding:8px; outline:none; }
          .mwi-share-modal button { margin-left:6px; padding:6px 10px; border-radius:6px; background:#222; color:#fff; border:1px solid rgba(255,255,255,0.06); cursor:pointer; }
          #mwi-settings-content { overflow-y:auto; max-height: calc(80vh - 160px); padding-right:16px; scrollbar-gutter: stable; }
          .mwi-settings-notice { font-size:12px; color:#9fb7d7; margin:6px 0 8px 0; }
          .mwi-settings-row { margin:8px 0; display:flex; align-items:center; justify-content:space-between; }
          .mwi-settings-section { margin-top:12px; padding-top:8px; border-top:0; }
          .mwi-settings-section h4 { margin:0 0 8px 0; font-size:15px; color:#bcd3ea; }
          #mwi-settings-dialog h4 { text-decoration: underline; }
          /* sitewide customizable colors (set via JS variables) */
              body { color: var(--mwi-text, inherit) !important; }
              .GamePage_headerPanel__1T_cA { background-color: var(--mwi-header-bg, unset) !important; }
          :root.mwi-header-grad-active [class*="Header_header__"] { background: var(--mwi-header-grad) !important; }
          .panel, .panel-content, .Inventory_inventory__17CH2, .EquipmentPanel_equipmentPanel__29pDG, .AbilitiesPanel_abilitiesPanel__2kLc9, .HousePanel_housePanel__lpphK, .LoadoutsPanel_loadoutsPanel__Gc5VA, [class*="Inventory_inventory__"], [class*="EquipmentPanel_equipmentPanel__"], [class*="AbilitiesPanel_abilitiesPanel__"], [class*="HousePanel_housePanel__"], [class*="LoadoutsPanel_loadoutsPanel__"] { background-color: var(--mwi-panel-bg, unset) !important; }
          :root.mwi-tabs-bg-active body > :not(#mwi-settings-overlay) .MuiTabs-root { background-color: var(--mwi-tabs-bg) !important; }
          .GamePage_navPanel__3wbAU { background-color: var(--mwi-side-panel-bg, unset) !important; }
          /* Swap left/right panels */
          :root.mwi-swap-panels [class*="GamePage_gamePanel__"] { flex-direction: row-reverse !important; }
          :root.mwi-swap-panels [class*="GamePage_contentPanel__"] { flex-direction: row-reverse !important; }
          /* Move chat to top */
          :root.mwi-chat-top [class*="GamePage_middlePanel__"] { flex-direction: column-reverse !important; }
          /* Move header to bottom */
          :root.mwi-header-bottom [class*="GamePage_gamePage__"] { flex-direction: column-reverse !important; }
          .MainPanel_subPanelContainer__1i-H9 { background-color: var(--mwi-subpanel-bg, unset) !important; }
           .Chat_chat__3DQkj { background-color: var(--mwi-chat-bg, unset) !important; }
           /* Apply button background/color only when user explicitly sets them. These
             rules use helper classes on :root toggled by applySiteColors() so the
             site's native styles are preserved until the user overrides them. */
          /* Apply background, border, and remove shadows/gradients so custom button
             color appears even if the site uses gradients or pseudo-elements. Include
             Material-UI (Mui*) classes used on the site. */
          /* Apply button backgrounds only to specific MUI Tab buttons (page content only) */
          :root.mwi-button-bg-active body > :not(#mwi-settings-overlay) .MuiButtonBase-root.MuiTab-root[class*="MuiTab-textColorPrimary"] {
            background: var(--mwi-button-bg) !important;
            background-color: var(--mwi-button-bg) !important;
            background-image: none !important;
            border-color: var(--mwi-button-bg) !important;
            box-shadow: none !important;
            color: inherit !important;
          }
          /* Clear pseudo-element overlays only for those specific tab buttons */
          :root.mwi-button-bg-active body > :not(#mwi-settings-overlay) .MuiButtonBase-root.MuiTab-root[class*="MuiTab-textColorPrimary"]::before,
          :root.mwi-button-bg-active body > :not(#mwi-settings-overlay) .MuiButtonBase-root.MuiTab-root[class*="MuiTab-textColorPrimary"]::after {
            background-image: none !important;
            background: transparent !important;
            box-shadow: none !important;
            border: 0 !important;
          }
          /* Ensure MUI inner label elements inherit accent color when set */
          /* Accent color applied only to page content (exclude settings overlay).
             Restrict button/text accent to the specific Badge class so general
             buttons and tabs are not recolored by the 'Text Color' setting. */
          :root.mwi-accent-active body > :not(#mwi-settings-overlay) a:not([class*="Inventory_label"]), :root.mwi-accent-active body > :not(#mwi-settings-overlay) button:not(.Inventory_categoryButton__35s1x):not([class*="Inventory_label"]),
          :root.mwi-accent-active body > :not(#mwi-settings-overlay) .MuiBadge-root.TabsComponent_badge__1Du26.css-1rzb3uu {
            color: var(--mwi-accent) !important;
          }
          /* inactive inputs: visual hint when a color setting is not set */
          .mwi-inactive { filter: grayscale(80%) opacity(.55); }
          .mwi-range-disabled { opacity: .55; }
          /* Colors editor subsections */
          .mwi-colors-subsection { margin-top:8px; padding-top:6px; border-top:1px solid rgba(255,255,255,0.10); }
          .mwi-colors-subsection h5 { margin:6px 0; font-size:12px; color:#e6f4ff; }
          /* Large separator specifically above Site Colors */
          #mwi-section-site-colors { border-top:2px solid rgba(255,255,255,0.12); }
          #mwi-section-animations { border-top:2px solid rgba(255,255,255,0.12); }
          .mwi-anim-toggle-row { display:flex; align-items:center; gap:8px; margin:8px 0 4px; }
          .mwi-anim-toggle-row label { color:#c8dff5; font-size:12px; cursor:pointer; user-select:none; }
          .mwi-anim-toggle-row input[type=checkbox] { width:14px; height:14px; cursor:pointer; accent-color:#44aaff; }
          .mwi-anim-sub { margin-top:8px; padding-top:6px; border-top:none; }
          /* Large separator specifically above Inventory */
          #mwi-section-inventory { border-top:2px solid rgba(255,255,255,0.12); }
          /* Large separator above Customizer section */
          #mwi-section-customizer { border-top:none; }
          /* sub-sections inside Customizer */
          .mwi-customizer-sub { margin-top:8px; padding-top:6px; border-top:1px solid rgba(255,255,255,0.10); }
          .mwi-customizer-sub h5 { margin:4px 0 6px 0; font-size:12px; background:linear-gradient(90deg,#44aaff,#ff44cc); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
          /* Large separator specifically above Dev section */
          #mwi-section-dev { border-top:2px solid rgba(255,255,255,0.12); }
          /* Organize modal */
          #mwi-organize-dialog { transform-origin: center top; transform: translateY(-20px) scale(0.9); opacity: 0; transition: transform 200ms ease, opacity 200ms ease; }
          .mwi-organize-open #mwi-organize-dialog { transform: translateY(0) scale(1); opacity: 1; }
          .mwi-organize-closing #mwi-organize-dialog { animation: mwi-pop-out 200ms ease both; }
          /* Organize modal rows */
          .mwi-org-row { display:flex; align-items:center; padding:6px 8px; border:1px solid rgba(255,255,255,0.04); border-radius:6px; margin-bottom:4px; background:transparent; gap:0; }
          .mwi-org-row-handle { flex:0 0 24px; display:flex; align-items:center; justify-content:center; }
          .mwi-org-row-icon { flex:0 0 28px; display:flex; align-items:center; justify-content:center; }
          .mwi-org-row-label { flex:1 1 0; font-size:14px; padding-left:4px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
          .mwi-org-row-right { flex:0 0 auto; display:flex; align-items:center; gap:6px; margin-left:8px; }
          .mwi-org-separator { height:8px; margin:8px 0; border-top:1px solid rgba(255,255,255,0.06); }
          .mwi-org-list { max-height: 56vh; overflow:auto; padding:6px; }
          .mwi-org-row.dragging { opacity:0.4; }
          .mwi-org-row.drag-over-top { border-top: 2px solid #4caaff !important; }
          .mwi-org-row.drag-over-bottom { border-bottom: 2px solid #4caaff !important; }
          .mwi-drag-handle { display:flex; align-items:center; color:rgba(255,255,255,0.35); font-size:18px; line-height:1; margin-right:8px; cursor:grab; flex-shrink:0; user-select:none; padding:0 2px; }
          .mwi-drag-handle:hover { color:rgba(255,255,255,0.7); }
          /* Skill Actions: applied only when user enables it via settings */
          /* Skill Actions: applied only when user enables it via settings (page content only) */
          :root.mwi-skill-actions-active body > :not(#mwi-settings-overlay) .SkillAction_skillAction__1esCp,
          :root.mwi-skill-actions-active body > :not(#mwi-settings-overlay) [class*="SkillAction_skillAction__"] {
            background-color: var(--mwi-skill-actions) !important;
            background-image: none !important;
            box-shadow: none !important;
            color: inherit !important;
          }
          :root.mwi-skill-actions-active .SkillAction_skillAction__1esCp::before,
          :root.mwi-skill-actions-active .SkillAction_skillAction__1esCp::after,
          :root.mwi-skill-actions-active [class*="SkillAction_skillAction__"]::before,
          :root.mwi-skill-actions-active [class*="SkillAction_skillAction__"]::after {
            background-image: none !important; background: transparent !important; box-shadow: none !important; border:0 !important;
          }
          /* Skill XP Bar: left-side navigation experience bar coloring */
          :root.mwi-skill-xp-active body > :not(#mwi-settings-overlay) .NavigationBar_currentExperience__3GDeX,
          :root.mwi-skill-xp-active body > :not(#mwi-settings-overlay) [class*="NavigationBar_currentExperience__"] {
            background-color: var(--mwi-skill-xp) !important;
            background-image: none !important;
            box-shadow: none !important;
            color: inherit !important;
          }
          :root.mwi-skill-xp-active .NavigationBar_currentExperience__3GDeX::before,
          :root.mwi-skill-xp-active .NavigationBar_currentExperience__3GDeX::after,
          :root.mwi-skill-xp-active [class*="NavigationBar_currentExperience__"]::before,
          :root.mwi-skill-xp-active [class*="NavigationBar_currentExperience__"]::after {
            background-image: none !important; background: transparent !important; box-shadow: none !important; border:0 !important;
          }
          /* Left-side navigation label text color (NavigationBar_label) */
          :root.mwi-nav-label-active body > :not(#mwi-settings-overlay) .NavigationBar_label__1uH-y,
          :root.mwi-nav-label-active body > :not(#mwi-settings-overlay) [class*="NavigationBar_label__"] {
            color: var(--mwi-nav-label) !important;
          }
          /* Selected skill (active navigation link) */
          :root.mwi-nav-selected-active body > :not(#mwi-settings-overlay) .NavigationBar_navigationLink__3eAHA.NavigationBar_active__3R-QS,
          :root.mwi-nav-selected-active body > :not(#mwi-settings-overlay) [class*="NavigationBar_navigationLink__"][class*="NavigationBar_active__"] {
            background-color: var(--mwi-selected-skill) !important;
            background-image: none !important;
            box-shadow: none !important;
            color: inherit !important;
          }
          /* Inventory label text color (right-side panel labels) */
          :root.mwi-inventory-labels-active body > :not(#mwi-settings-overlay) .Inventory_label__XEOAx,
          :root.mwi-inventory-labels-active body > :not(#mwi-settings-overlay) [class*="Inventory_label__"] {
            color: var(--mwi-inventory-labels) !important;
          }
          /* Left-side navigation level text color */
          :root.mwi-level-active body > :not(#mwi-settings-overlay) .NavigationBar_level__3C7eR,
          :root.mwi-level-active body > :not(#mwi-settings-overlay) [class*="NavigationBar_level__"] {
            color: var(--mwi-level) !important;
          }
          /* Chat timestamp text color */
          :root.mwi-timestamp-active body > :not(#mwi-settings-overlay) .ChatMessage_timestamp__1iRZO,
          :root.mwi-timestamp-active body > :not(#mwi-settings-overlay) [class*="ChatMessage_timestamp__"] {
            color: var(--mwi-timestamp) !important;
          }
          /* Chat text override applied only to nodes we've explicitly marked
             (script checks computed color and sets data-mwi-chat-text-applied) */
          :root.mwi-chat-text-active body > :not(#mwi-settings-overlay) [data-mwi-chat-text-applied] {
            color: var(--mwi-chat-text) !important;
          }
          /* Chat system message text color */
          :root.mwi-system-message-active body > :not(#mwi-settings-overlay) .ChatMessage_systemMessage__3Jz9e,
          :root.mwi-system-message-active body > :not(#mwi-settings-overlay) [class*="ChatMessage_systemMessage__"] {
            color: var(--mwi-system-message) !important;
          }
          /* HP / MP bars (user-configurable) */
          :root.mwi-hp-active body > :not(#mwi-settings-overlay) .HitpointsBar_currentHp__5exLr {
            background-color: var(--mwi-hp) !important;
            background-image: none !important;
            box-shadow: none !important;
          }
          :root.mwi-hp-active body > :not(#mwi-settings-overlay) [class*="CombatUnit_heal__"] {
            background-color: var(--mwi-hp) !important;
            border-color: var(--mwi-hp) !important;
          }
          :root.mwi-mp-active body > :not(#mwi-settings-overlay) .ManapointsBar_currentMp__3xpqC {
            background-color: var(--mwi-mp) !important;
            background-image: none !important;
            box-shadow: none !important;
          }
          :root.mwi-mp-active body > :not(#mwi-settings-overlay) [class*="CombatUnit_mana__"] {
            background-color: var(--mwi-mp) !important;
            border-color: var(--mwi-mp) !important;
          }
          /* Hitsplat Damage / Miss (user-configurable) */
          :root.mwi-hit-dmg-active body > :not(#mwi-settings-overlay) [class*="CombatUnit_damage__"] {
            background-color: var(--mwi-hit-dmg) !important;
            border-color: var(--mwi-hit-dmg) !important;
          }
          :root.mwi-hit-miss-active body > :not(#mwi-settings-overlay) [class*="CombatUnit_miss__"] {
            background-color: var(--mwi-hit-miss) !important;
            border-color: var(--mwi-hit-miss) !important;
          }
          /* Attack bar (user-configurable) */
          :root.mwi-attack-active body > :not(#mwi-settings-overlay) .ProgressBar_innerBar__3Z_sf.ProgressBar_active__Do7AF {
            background-color: var(--mwi-attack) !important;
            background-image: none !important;
            box-shadow: none !important;
          }
          /* Bar background (user-configurable) */
          :root.mwi-bar-bg-active body > :not(#mwi-settings-overlay) [class*="HitpointsBar_hitpointsBar__"],
          :root.mwi-bar-bg-active body > :not(#mwi-settings-overlay) [class*="ManapointsBar_manapointsBar__"],
          :root.mwi-bar-bg-active body > :not(#mwi-settings-overlay) [class*="ProgressBar_progressBar__"] {
            background-color: var(--mwi-bar-bg) !important;
            background-image: none !important;
          }
          /* Consumables / Abilities */
          :root.mwi-consumables-active body > :not(#mwi-settings-overlay) .Item_item__2De2O.Item_small__1HxwE,
          :root.mwi-consumables-active body > :not(#mwi-settings-overlay) .Ability_ability__1njrh.Ability_small__1GKAt {
            background-color: var(--mwi-consumables) !important;
            background-image: none !important;
            box-shadow: none !important;
          }
          /* Custom full-page background image (scoped, opt-in). Uses --mwi-bg-image on :root when enabled. */
          :root.mwi-bg-active::before, :root.mwi-bg-preview::before {
            content: "";
            position: fixed;
            inset: 0;
            background-image: var(--mwi-bg-image, none);
              background-size: cover;
              background-position: center;
            background-repeat: no-repeat;
            opacity: var(--mwi-bg-opacity, 1);
            pointer-events: none;
            /* place the overlay above the original background but beneath typical UI elements */
            z-index: 0;
          }
          /* Force a 50% transparent overlay inside the header panel specifically.
             This creates a header-local pseudo-element that uses the same image
             but at 50% opacity and sits above the page-level overlay. */
          :root.mwi-bg-active body > :not(#mwi-settings-overlay) .GamePage_headerPanel__1T_cA,
          :root.mwi-bg-preview body > :not(#mwi-settings-overlay) .GamePage_headerPanel__1T_cA {
            position: relative; z-index: 2;
          }
          :root.mwi-bg-active body > :not(#mwi-settings-overlay) .GamePage_headerPanel__1T_cA::before,
          :root.mwi-bg-preview body > :not(#mwi-settings-overlay) .GamePage_headerPanel__1T_cA::before {
            content: "";
            position: absolute;
            inset: 0;
            background-image: var(--mwi-bg-image, none);
            background-size: cover;
            background-position: center;
            background-repeat: no-repeat;
            opacity: 0.1; /* forced 10% transparency */
            pointer-events: none;
            z-index: 1; /* sits above the page overlay (z-index:0) but beneath header content (z-index:2)
                        so it only affects the header region */
          }
          /* Ensure the settings dialog remains above the custom background */
          #mwi-settings-dialog { z-index: 10000002; position: relative; }
          /* Interactables: clickable items, abilities, and house rooms on right panel */
          :root.mwi-interactables-active body > :not(#mwi-settings-overlay) .Item_item__2De2O.Item_clickable__3viV6,
          :root.mwi-interactables-active body > :not(#mwi-settings-overlay) .Ability_ability__1njrh.Ability_clickable__w9HcM,
          :root.mwi-interactables-active body > :not(#mwi-settings-overlay) .HousePanel_houseRoom__nOmpF,
          :root.mwi-interactables-active body > :not(#mwi-settings-overlay) [class*="Item_item__2De2O"][class*="Item_clickable__"],
          :root.mwi-interactables-active body > :not(#mwi-settings-overlay) [class*="Ability_ability__"][class*="Ability_clickable__"],
          :root.mwi-interactables-active body > :not(#mwi-settings-overlay) [class*="HousePanel_houseRoom__"] {
            background-color: var(--mwi-interactables) !important;
            background-image: none !important;
            box-shadow: none !important;
            color: inherit !important;
          }
          :root.mwi-interactables-active .Item_item__2De2O.Item_clickable__3viV6::before,
          :root.mwi-interactables-active .Item_item__2De2O.Item_clickable__3viV6::after,
          :root.mwi-interactables-active .Ability_ability__1njrh.Ability_clickable__w9HcM::before,
          :root.mwi-interactables-active .Ability_ability__1njrh.Ability_clickable__w9HcM::after,
          :root.mwi-interactables-active .HousePanel_houseRoom__nOmpF::before,
          :root.mwi-interactables-active .HousePanel_houseRoom__nOmpF::after,
          :root.mwi-interactables-active [class*="Item_item__2De2O"]::before,
          :root.mwi-interactables-active [class*="Item_item__2De2O"]::after,
          :root.mwi-interactables-active [class*="Ability_ability__"]::before,
          :root.mwi-interactables-active [class*="Ability_ability__"]::after,
          :root.mwi-interactables-active [class*="HousePanel_houseRoom__"]::before,
          :root.mwi-interactables-active [class*="HousePanel_houseRoom__"]::after {
            background-image: none !important; background: transparent !important; box-shadow: none !important; border:0 !important;
          }
          /* Combat: applied only when user enables it via settings */
          /* Combat: applied only when user enables it via settings (page content only) */
          :root.mwi-combat-active body > :not(#mwi-settings-overlay) .CombatUnit_combatUnit__1m3XT,
          :root.mwi-combat-active body > :not(#mwi-settings-overlay) [class*="CombatUnit_combatUnit__"] {
            background-color: var(--mwi-combat) !important;
            background-image: none !important;
            box-shadow: none !important;
            color: inherit !important;
          }
          :root.mwi-combat-active .CombatUnit_combatUnit__1m3XT::before,
          :root.mwi-combat-active .CombatUnit_combatUnit__1m3XT::after,
          :root.mwi-combat-active [class*="CombatUnit_combatUnit__"]::before,
          :root.mwi-combat-active [class*="CombatUnit_combatUnit__"]::after {
            background-image: none !important; background: transparent !important; box-shadow: none !important; border:0 !important;
          }
          /* Progress Bar: applied only when user enables it via settings (inner active bar) */
          /* Progress Bar: applied only when user enables it via settings (page content only) */
          :root.mwi-progress-bar-active body > :not(#mwi-settings-overlay) .ProgressBar_innerBar__3Z_sf.ProgressBar_active__Do7AF,
          :root.mwi-progress-bar-active body > :not(#mwi-settings-overlay) [class*="ProgressBar_innerBar__"],
          :root.mwi-progress-bar-active body > :not(#mwi-settings-overlay) [class*="ProgressBar_active__"] {
            background-color: var(--mwi-progress-bar) !important;
            background-image: none !important;
            box-shadow: none !important;
            color: inherit !important;
          }
          :root.mwi-progress-bar-active .ProgressBar_innerBar__3Z_sf.ProgressBar_active__Do7AF::before,
          :root.mwi-progress-bar-active .ProgressBar_innerBar__3Z_sf.ProgressBar_active__Do7AF::after,
          :root.mwi-progress-bar-active [class*="ProgressBar_innerBar__"]::before,
          :root.mwi-progress-bar-active [class*="ProgressBar_innerBar__"]::after,
          :root.mwi-progress-bar-active [class*="ProgressBar_active__"]::before,
          :root.mwi-progress-bar-active [class*="ProgressBar_active__"]::after {
            background-image: none !important; background: transparent !important; box-shadow: none !important; border:0 !important;
          }
          /* Strong overrides to ensure the settings modal is never restyled by
             site-wide active classes. These selectors use the ID to increase
             specificity so they win against other author !important rules. */
          :root.mwi-button-bg-active #mwi-settings-dialog button,
          :root.mwi-button-bg-active #mwi-settings-dialog [class*="Button_"],
          :root.mwi-button-bg-active #mwi-settings-dialog .btn,
          :root.mwi-button-bg-active #mwi-settings-dialog a.button,
          :root.mwi-button-bg-active #mwi-settings-dialog [role="button"],
          :root.mwi-button-bg-active #mwi-settings-dialog .MuiButton-root,
          :root.mwi-button-bg-active #mwi-settings-dialog [class*="MuiButton"] {
            /* Do not override explicit inline backgrounds; only remove gradients/overlays */
            background-image: none !important; border-color: initial !important; box-shadow: none !important; color: inherit !important;
          }
          :root.mwi-button-bg-active #mwi-settings-dialog button::before,
          :root.mwi-button-bg-active #mwi-settings-dialog button::after,
          :root.mwi-button-bg-active #mwi-settings-dialog [class*="Button_"]::before,
          :root.mwi-button-bg-active #mwi-settings-dialog [class*="Button_"]::after,
          :root.mwi-button-bg-active #mwi-settings-dialog .btn::before,
          :root.mwi-button-bg-active #mwi-settings-dialog .btn::after,
          :root.mwi-button-bg-active #mwi-settings-dialog [class*="MuiButton"]::before,
          :root.mwi-button-bg-active #mwi-settings-dialog [class*="MuiButton"]::after {
            background-image: none !important; background: transparent !important; box-shadow: none !important; border: 0 !important;
          }
          :root.mwi-accent-active #mwi-settings-dialog a, :root.mwi-accent-active #mwi-settings-dialog button,
          :root.mwi-accent-active #mwi-settings-dialog .MuiButton-root, :root.mwi-accent-active #mwi-settings-dialog .MuiTab-root,
          :root.mwi-accent-active #mwi-settings-dialog .MuiButton-label, :root.mwi-accent-active #mwi-settings-dialog .MuiTab-label {
            color: inherit !important;
          }
          :root.mwi-skill-actions-active #mwi-settings-dialog .SkillAction_skillAction__1esCp,
          :root.mwi-skill-actions-active #mwi-settings-dialog [class*="SkillAction_skillAction__"] {
            background-image: none !important; box-shadow: none !important;
          }
          :root.mwi-combat-active #mwi-settings-dialog .CombatUnit_combatUnit__1m3XT,
          :root.mwi-combat-active #mwi-settings-dialog #mwi-settings-dialog [class*="CombatUnit_combatUnit__"] {
            background-image: none !important; background: transparent !important; box-shadow: none !important; border:0 !important;
          }
          :root.mwi-progress-bar-active #mwi-settings-dialog .ProgressBar_innerBar__3Z_sf.ProgressBar_active__Do7AF,
          :root.mwi-progress-bar-active #mwi-settings-dialog [class*="ProgressBar_innerBar__"],
          :root.mwi-progress-bar-active #mwi-settings-dialog [class*="ProgressBar_active__"] {
            background-image: none !important; box-shadow: none !important; color: inherit !important;
          }
        `;
      const s = document.createElement('style'); s.textContent = css; document.head.appendChild(s);
    }

    function applyUIStyle() {
      try {
        const dialog = document.getElementById('mwi-settings-dialog');
        if (dialog) {
          if ((cfg.uiStyle || 'frosted') === 'solid') {
            dialog.classList.add('mwi-ui-solid');
          } else {
            dialog.classList.remove('mwi-ui-solid');
          }
        }
      } catch (e) {}
      try {
        const btn = document.getElementById('mwi-settings-btn');
        if (btn) {
          if ((cfg.btnStyle || 'frosted') === 'frosted') {
            btn.classList.add('mwi-btn-frosted');
            // wrap text in span for gradient text rendering
            if (!btn.querySelector('span')) {
              const sp = document.createElement('span'); sp.textContent = btn.textContent; btn.textContent = ''; btn.appendChild(sp);
            }
          } else {
            btn.classList.remove('mwi-btn-frosted');
            // unwrap span back to plain text
            const sp = btn.querySelector('span');
            if (sp) { btn.textContent = sp.textContent; }
          }
        }
      } catch (e) {}
    }

    // ── Combat animations ────────────────────────────────────────────────
    // Squash-and-stretch "kinetic impact" + colour-burst glow on hit.
    // Hooks the WS message stream to detect battle_updated damage events.

    function tryHitAnim(type, index, dmg) {
      try {
        let unitEl;
        if (type === 'monster') {
          const area = document.querySelector('.BattlePanel_monstersArea__2dzrY');
          if (!area || !area.children[0]) return;
          unitEl = area.children[0].children[index];
        } else {
          const area = document.querySelector('.BattlePanel_playersArea__vvwlB');
          if (!area || !area.children[0]) return;
          unitEl = area.children[0].children[index];
        }
        if (!unitEl) return;

        // Intensity scalar 0–1 (capped at 800 dmg for max punch)
        const t = Math.min(1, Math.max(0, dmg / 800));

        // Cancel any in-flight animations so back-to-back hits always start clean.
        if (unitEl._mwiHitAnim)  { try { unitEl._mwiHitAnim.cancel();  } catch (_) {} }
        if (unitEl._mwiVibeAnim) { try { unitEl._mwiVibeAnim.cancel(); } catch (_) {} }

        // ── Squash & stretch ───────────────────────────────────────────
        // More dramatic values — visibly rubber-like on heavier hits.
        const sqX = (1 + 0.52 * t).toFixed(3);   // wide squash
        const sqY = (1 - 0.40 * t).toFixed(3);   // flat squash
        const stX = (1 - 0.18 * t).toFixed(3);   // tall stretch narrow
        const stY = (1 + 0.30 * t).toFixed(3);   // tall stretch
        unitEl._mwiHitAnim = unitEl.animate(
          [
            { transform: 'scale(1, 1)',            offset: 0.00 },
            { transform: `scale(${sqX}, ${sqY})`, offset: 0.09 },
            { transform: `scale(${stX}, ${stY})`, offset: 0.26 },
            { transform: 'scale(1.04, 0.97)',      offset: 0.48 },
            { transform: 'scale(0.98, 1.03)',      offset: 0.65 },
            { transform: 'scale(1.01, 0.99)',      offset: 0.80 },
            { transform: 'scale(1, 1)',            offset: 1.00 }
          ],
          {
            duration: 460 + t * 200,
            easing: 'cubic-bezier(0.22, 0.61, 0.36, 1)',
            fill: 'none'
          }
        );
        unitEl._mwiHitAnim.onfinish = () => { unitEl._mwiHitAnim = null; };

        // ── Lateral vibration ──────────────────────────────────────────
        // Fast left-right rattle overlaid on the squash. Amplitude and rattle
        // count both scale with hit intensity so small hits get a tiny nudge
        // and big hits get a proper multi-cycle shudder.
        const vx = (3 + t * 9).toFixed(1);   // peak lateral offset px
        const vy = (1 + t * 3).toFixed(1);   // slight vertical component
        const vibeDur = 60 + t * 30;          // ms per half-cycle (faster = more frantic)
        const cycles  = Math.round(2 + t * 3); // number of left-right pairs
        const vibeFrames = [{ transform: 'translate(0,0)', offset: 0 }];
        for (let c = 0; c < cycles; c++) {
          const pct = (c + 0.5) / cycles;
          const decay = (1 - pct * 0.7).toFixed(3); // amplitude decays toward end
          const sign  = c % 2 === 0 ? 1 : -1;
          vibeFrames.push({
            transform: `translate(${(sign * vx * decay).toFixed(2)}px, ${(-vy * decay).toFixed(2)}px)`,
            offset: parseFloat(((c + 0.5) / cycles * 0.55).toFixed(3))
          });
        }
        vibeFrames.push({ transform: 'translate(0,0)', offset: 1.0 });
        unitEl._mwiVibeAnim = unitEl.animate(vibeFrames, {
          duration: vibeDur * cycles * 2 + 80,
          easing: 'ease-in-out',
          fill: 'none'
        });
        unitEl._mwiVibeAnim.onfinish = () => { unitEl._mwiVibeAnim = null; };

        // ── Colour-burst glow ──────────────────────────────────────────
        const iconEl = unitEl.querySelector(
          '.CombatUnit_monsterIcon__2g3AZ, .FullAvatar_fullAvatar__3RB2h'
        ) || unitEl;
        if (iconEl._mwiGlowAnim) { try { iconEl._mwiGlowAnim.cancel(); } catch (_) {} }
        const glowPx  = (7 + t * 18).toFixed(1);
        const glowClr = dmg > 0
          ? `rgba(255,${Math.round(180 - t * 150)},${Math.round(40 - t * 40)},0.92)`
          : 'rgba(80,255,120,0.80)';
        iconEl._mwiGlowAnim = iconEl.animate(
          [
            { filter: `drop-shadow(0 0 ${glowPx}px ${glowClr}) brightness(${(1.35 + t * 0.55).toFixed(2)})`,        offset: 0.00 },
            { filter: `drop-shadow(0 0 ${(glowPx * 0.65).toFixed(1)}px ${glowClr}) brightness(1.12)`,                offset: 0.30 },
            { filter: `drop-shadow(0 0 ${(glowPx * 0.2).toFixed(1)}px ${glowClr}) brightness(1.02)`,                 offset: 0.65 },
            { filter: 'none',                                                                                         offset: 1.00 }
          ],
          {
            duration: 520 + t * 200,
            easing: 'ease-out',
            fill: 'none'
          }
        );
        iconEl._mwiGlowAnim.onfinish = () => { iconEl._mwiGlowAnim = null; };
      } catch (e) {}
    }

    // ── Consumable / Ability usage animations ──────────────────────────
    // Debug helper — call mwiDebugUsage() in the browser console, then use a
    // consumable or ability. It will log every DOM mutation on those elements
    // so we can see exactly what signal fires.
    window.mwiDebugUsage = function() {
      console.log('[mwi-debug] Starting usage DOM observer. Use a consumable or ability now.');
      const obs = new MutationObserver((muts) => {
        for (const m of muts) {
          const target = m.target;
          const cls = (typeof target.className === 'string') ? target.className : (target.data !== undefined ? '#text' : '?');
          if (m.type === 'childList') {
            m.addedNodes.forEach(n => console.log('[mwi-debug] childList ADD', n.nodeName, typeof n.className === 'string' ? n.className : n.data, '| parent:', cls));
            m.removedNodes.forEach(n => console.log('[mwi-debug] childList REM', n.nodeName, typeof n.className === 'string' ? n.className : n.data, '| parent:', cls));
          } else if (m.type === 'attributes') {
            console.log('[mwi-debug] attr change [' + m.attributeName + ']', '\n  old:', m.oldValue, '\n  new:', target.getAttribute(m.attributeName), '\n  el:', cls);
          } else if (m.type === 'characterData') {
            console.log('[mwi-debug] text change old:"' + m.oldValue + '" → new:"' + m.target.data + '" | parent:', target.parentElement && (target.parentElement.className || target.parentElement.nodeName));
          }
        }
      });
      obs.observe(document.body, {
        childList: true, subtree: true,
        characterData: true, characterDataOldValue: true,
        attributes: true, attributeOldValue: true,
      });
      console.log('[mwi-debug] Observer active. Call mwiDebugUsageStop() to stop.');
      window.mwiDebugUsageStop = function() { obs.disconnect(); console.log('[mwi-debug] Stopped.'); };
    };

    function hookUsageAnim() {
      if (window._mwiUsageAnimHooked) return;
      window._mwiUsageAnimHooked = true;

      // Consumable: crunchy pop — quick squash (bite/crunch) then springy bounce back
      function playConsumeAnim(el) {
        if (!el || el._mwiConsumeAnim) return;
        el._mwiConsumeAnim = true;
        const a = el.animate([
          { transform: 'scale(1)',           filter: 'brightness(1)',    offset: 0.00 },
          { transform: 'scale(0.80, 0.78)', filter: 'brightness(1.9)',  offset: 0.10 }, // crunch
          { transform: 'scale(1.20, 1.18)', filter: 'brightness(1.5)',  offset: 0.28 }, // pop
          { transform: 'scale(0.93, 0.95)', filter: 'brightness(1.15)', offset: 0.46 },
          { transform: 'scale(1.04, 1.03)', filter: 'brightness(1.06)', offset: 0.63 },
          { transform: 'scale(1)',           filter: 'brightness(1)',    offset: 1.00 }
        ], { duration: 2000, easing: 'cubic-bezier(0.22, 0.61, 0.36, 1)', fill: 'none' });
        a.onfinish = () => { el._mwiConsumeAnim = false; };
      }

      // Ability: impactful surge — charge-up burst then crash-down, with fiery glow
      function playAbilityAnim(el) {
        if (!el || el._mwiAbilityAnim) return;
        el._mwiAbilityAnim = true;
        const a = el.animate([
          { transform: 'scale(1)',    filter: 'brightness(1)',                                              offset: 0.00 },
          { transform: 'scale(1.32)', filter: 'brightness(2.6) drop-shadow(0 0 14px rgba(255,180,40,1))', offset: 0.14 },
          { transform: 'scale(0.85)', filter: 'brightness(1.6) drop-shadow(0 0 9px rgba(255,90,20,0.8))', offset: 0.33 },
          { transform: 'scale(1.10)', filter: 'brightness(1.25) drop-shadow(0 0 5px rgba(255,70,0,0.4))', offset: 0.52 },
          { transform: 'scale(0.97)', filter: 'brightness(1.06)',                                          offset: 0.73 },
          { transform: 'scale(1)',    filter: 'brightness(1)',                                             offset: 1.00 }
        ], { duration: 2000, easing: 'cubic-bezier(0.22, 0.61, 0.36, 1)', fill: 'none' });
        a.onfinish = () => { el._mwiAbilityAnim = false; };
      }

      // ── Unified observer — triggered by CountdownOverlay add/remove.
      // Abilities:   CountdownOverlay ADDED to CombatAbility_combatAbility__ (new cooldown starts = ability fired)
      // Consumables: CountdownOverlay ADDED to CombatConsumable_combatConsumable__
      const obs = new MutationObserver((mutations) => {
        if (cfg.usageAnim === false) return;
        for (const mut of mutations) {
          if (mut.type !== 'childList') continue;
          const parentCls = typeof mut.target.className === 'string' ? mut.target.className : '';

          if (parentCls.includes('CombatAbility_combatAbility__')) {
            // Fire on addition of the countdown overlay (ability just activated)
            for (const node of mut.addedNodes) {
              if (node.nodeType !== 1) continue;
              const cls = typeof node.className === 'string' ? node.className : '';
              if (cls.includes('CountdownOverlay_countdownOverlay__')) {
                playAbilityAnim(mut.target);
                break;
              }
            }
          } else if (parentCls.includes('CombatConsumable_combatConsumable__')) {
            // Fire on addition of the countdown overlay (consumable just used)
            for (const node of mut.addedNodes) {
              if (node.nodeType !== 1) continue;
              const cls = typeof node.className === 'string' ? node.className : '';
              if (cls.includes('CountdownOverlay_countdownOverlay__')) {
                playConsumeAnim(mut.target);
                break;
              }
            }
          }
        }
      });
      obs.observe(document.body, { childList: true, subtree: true });
    }

    function hookCombatWS() {
      if (window._mwiCombatWSHooked) return;
      window._mwiCombatWSHooked = true;
      try {
        const dp = Object.getOwnPropertyDescriptor(MessageEvent.prototype, 'data');
        if (!dp) return;
        const oriGet = dp.get;
        let monsHP = [], monsCtr = [], plrsHP = [], plrsCtr = [];
        dp.get = function () {
          const socket = this.currentTarget;
          if (!(socket instanceof WebSocket)) return oriGet.call(this);
          if (!socket.url.includes('milkywayidle.com/ws')) return oriGet.call(this);
          const msg = oriGet.call(this);
          // Anti-loop: cache on instance so the prototype getter isn't called again
          Object.defineProperty(this, 'data', { value: msg });
          try {
            if (cfg.combatAnim !== false) {
              const obj = JSON.parse(msg);
              if (obj && obj.type === 'new_battle') {
                monsHP  = obj.monsters.map(m => m.currentHitpoints);
                monsCtr = obj.monsters.map(m => m.damageSplatCounter);
                plrsHP  = obj.players.map(p => p.currentHitpoints);
                plrsCtr = obj.players.map(p => p.damageSplatCounter);
              } else if (obj && obj.type === 'battle_updated' && monsHP.length) {
                const { mMap, pMap } = obj;
                Object.keys(mMap || {}).forEach(i => {
                  const m = mMap[i]; if (!m) return;
                  if ((monsCtr[i] ?? -1) < m.dmgCounter) {
                    tryHitAnim('monster', +i, Math.max(0, monsHP[i] - m.cHP));
                  }
                  monsHP[i] = m.cHP; monsCtr[i] = m.dmgCounter;
                });
                Object.keys(pMap || {}).forEach(i => {
                  const p = pMap[i]; if (!p) return;
                  if ((plrsCtr[i] ?? -1) < p.dmgCounter) {
                    tryHitAnim('player', +i, Math.max(0, plrsHP[i] - p.cHP));
                  }
                  plrsHP[i] = p.cHP; plrsCtr[i] = p.dmgCounter;
                });
              }
            }
          } catch (e) {}
          return msg;
        };
        Object.defineProperty(MessageEvent.prototype, 'data', dp);
      } catch (e) { log('hookCombatWS error', e); }
    }

    function createSettingsModal() {
      // If an older overlay exists (from a previous script version), remove it
      // so we recreate the modal with current event handlers.
      const existingOverlay = document.getElementById('mwi-settings-overlay');
      if (existingOverlay) existingOverlay.remove();
      const overlay = document.createElement('div'); overlay.id = 'mwi-settings-overlay';
      overlay.style.display = 'none';
      // helper to animate-close the modal (keeps consistent timing everywhere)
      function animateClose() {
        try {
          if (!overlay) return;
          overlay.classList.remove('mwi-dialog-open');
          overlay.classList.add('mwi-dialog-closing');
          setTimeout(() => {
            try { overlay.style.display = 'none'; overlay.classList.remove('mwi-dialog-closing'); } catch (e) {}
          }, 200);
        } catch (e) {}
      }
      // close modal when clicking outside the dialog
      overlay.addEventListener('click', (ev) => { try { if (ev.target === overlay) animateClose(); } catch (e) {} });
      // mouse-tracked glow on dialog (frosted mode only)
      overlay.addEventListener('mousemove', (ev) => {
        try {
          if ((cfg.uiStyle || 'frosted') !== 'frosted') return;
          const rect = dialog.getBoundingClientRect();
          const x = ((ev.clientX - rect.left) / rect.width * 100).toFixed(1) + '%';
          const y = ((ev.clientY - rect.top) / rect.height * 100).toFixed(1) + '%';
          dialog.style.setProperty('--glow-x', x);
          dialog.style.setProperty('--glow-y', y);
        } catch (e) {}
      });

      const dialog = document.createElement('div'); dialog.id = 'mwi-settings-dialog';
      const closeBtn = document.createElement('button'); closeBtn.id = 'mwi-settings-close'; closeBtn.textContent = '\u2715';
      closeBtn.addEventListener('click', () => animateClose());

      const title = document.createElement('h3'); title.textContent = 'MWI Customizer Settings - v1.2.2'; title.style.background = 'linear-gradient(90deg, #ff44cc, #44aaff)'; title.style.webkitBackgroundClip = 'text'; title.style.webkitTextFillColor = 'transparent'; title.style.backgroundClip = 'text';
      const notice = document.createElement('div'); notice.className = 'mwi-settings-notice'; notice.textContent = 'Refresh the page for changes to apply.';

      // Search bar (sticky, below notice)
      const searchWrap = document.createElement('div');
      searchWrap.id = 'mwi-search-wrap';
      searchWrap.style.cssText = 'position:sticky;top:0;z-index:10;padding:8px 16px 6px;margin:0 -16px;display:flex;align-items:center;gap:8px;border-bottom:1px solid rgba(255,255,255,0.08);margin-bottom:4px;';
      const searchInput = document.createElement('input'); searchInput.type = 'text'; searchInput.id = 'mwi-settings-search'; searchInput.placeholder = 'Search settings…';
      searchInput.style.cssText = 'flex:1;background:rgba(255,255,255,0.07);border:1px solid rgba(255,255,255,0.15);border-radius:6px;padding:5px 10px;color:#e6eef8;font-size:13px;outline:none;font-family:inherit;transition:border-color 0.2s;min-width:0;';
      searchInput.addEventListener('focus', () => { searchInput.style.borderColor = '#44aaff'; });
      searchInput.addEventListener('blur', () => { searchInput.style.borderColor = 'rgba(255,255,255,0.15)'; });
      const clearBtn = document.createElement('button'); clearBtn.textContent = '✕'; clearBtn.title = 'Clear search';
      clearBtn.style.cssText = 'background:none;border:none;color:#99b7d9;font-size:13px;cursor:pointer;padding:0 4px;flex-shrink:0;opacity:0;transition:opacity 0.2s;';
      function runSearch() {
        const q = searchInput.value.trim().toLowerCase();
        clearBtn.style.opacity = q ? '1' : '0';
        const content = document.getElementById('mwi-settings-content');
        if (!content) return;
        if (!q) {
          content.querySelectorAll('.mwi-settings-section,.mwi-colors-subsection,.mwi-customizer-sub').forEach(el => { el.style.display = ''; });
          return;
        }
        // Search mwi-customizer-sub blocks (Themes, Background, Manage Elements, etc.)
        content.querySelectorAll('.mwi-customizer-sub').forEach(sub => {
          const txt = sub.textContent.toLowerCase();
          sub.style.display = txt.includes(q) ? '' : 'none';
        });
        content.querySelectorAll('.mwi-settings-section').forEach(section => {
          let sectionMatch = false;
          const h4 = section.querySelector('h4');
          if (h4 && h4.textContent.toLowerCase().includes(q)) { sectionMatch = true; }
          section.querySelectorAll('.mwi-colors-subsection').forEach(sub => {
            let subMatch = false;
            const h5 = sub.querySelector('h5');
            if (h5 && h5.textContent.toLowerCase().includes(q)) { subMatch = true; }
            sub.querySelectorAll('.mwi-settings-row').forEach(row => {
              const txt = row.textContent.toLowerCase();
              if (txt.includes(q)) subMatch = true;
            });
            sub.style.display = subMatch ? '' : 'none';
            if (subMatch) sectionMatch = true;
          });
          section.querySelectorAll(':scope > .mwi-settings-row').forEach(row => {
            const txt = row.textContent.toLowerCase();
            if (txt.includes(q)) sectionMatch = true;
          });
          // Also check mwi-customizer-sub children inside this section
          section.querySelectorAll(':scope > .mwi-customizer-sub').forEach(sub => {
            if (sub.style.display !== 'none') sectionMatch = true;
          });
          section.style.display = sectionMatch ? '' : 'none';
        });
      }
      searchInput.addEventListener('input', runSearch);
      clearBtn.addEventListener('click', () => { searchInput.value = ''; runSearch(); searchInput.focus(); });
      searchWrap.appendChild(searchInput); searchWrap.appendChild(clearBtn);

      const content = document.createElement('div'); content.id = 'mwi-settings-content'; content.style.marginTop = '8px';

      // Small helper to ensure the 'Borders' row appears before 'Glow'
      function reorderBordersGlow(root) {
        try {
          const inv = Array.from((root || document).querySelectorAll('.mwi-settings-section')).find(s => { try { const h = s.querySelector('h4'); return h && h.textContent && h.textContent.trim() === 'Inventory'; } catch (e) { return false; } });
          if (!inv) return;
          const rows = Array.from(inv.querySelectorAll('.mwi-settings-row'));
          const glow = rows.find(r => { const l = r.querySelector('label') || r.querySelector('div'); return l && l.textContent && l.textContent.trim() === 'Glow'; });
          const borders = rows.find(r => { const l = r.querySelector('label') || r.querySelector('div'); return l && l.textContent && l.textContent.trim() === 'Borders'; });
          if (borders && glow && glow.previousElementSibling !== borders) {
            try { glow.parentElement.insertBefore(borders, glow); } catch (e) { try { inv.insertBefore(borders, glow); } catch (e2) {} }
          }
        } catch (e) {}
      }

      // Colors section (sitewide)
      const colorsSection = document.createElement('div'); colorsSection.className = 'mwi-settings-section'; colorsSection.id = 'mwi-section-site-colors';
      const colorsTitle = document.createElement('h4'); colorsTitle.textContent = 'Site Colors'; colorsTitle.style.background = 'linear-gradient(90deg, #ff44cc, #44aaff)'; colorsTitle.style.webkitBackgroundClip = 'text'; colorsTitle.style.webkitTextFillColor = 'transparent'; colorsTitle.style.backgroundClip = 'text';
      colorsSection.appendChild(colorsTitle);
      // Remove the large separator that appears below Inventory (beneath Glow)
      // by clearing this section's top border.
      try { colorsSection.style.borderTop = '2px solid rgba(255,255,255,0.12)'; } catch (e) {}

      const colorsList = document.createElement('div'); colorsList.style.marginTop = '6px';

      // Background section (custom image overlay)
      const bgSection = document.createElement('div'); bgSection.className = 'mwi-customizer-sub'; bgSection.id = 'mwi-section-background';
      const bgTitle = document.createElement('h5'); bgTitle.textContent = 'Background'; bgSection.appendChild(bgTitle);
      const bgList = document.createElement('div'); bgList.style.marginTop = '6px';

      // MWI Customizer sub-section (UI style + button style)
      const mwiCfgSection = document.createElement('div'); mwiCfgSection.className = 'mwi-customizer-sub'; mwiCfgSection.id = 'mwi-section-mwi-cfg';
      const mwiCfgTitle = document.createElement('h5'); mwiCfgTitle.textContent = 'MWI Customizer'; mwiCfgSection.appendChild(mwiCfgTitle);
      try {
        // Settings UI dropdown
        const uiRow = document.createElement('div'); uiRow.className = 'mwi-settings-row';
        const uiLabel = document.createElement('label'); uiLabel.textContent = 'Settings UI';
        const uiSelect = document.createElement('select');
        [['frosted','Frosted'],['solid','Solid']].forEach(([v,t]) => { const o = document.createElement('option'); o.value=v; o.textContent=t; if ((cfg.uiStyle||'frosted')===v) o.selected=true; uiSelect.appendChild(o); });
        uiSelect.addEventListener('change', () => { cfg.uiStyle = uiSelect.value; saveSettings(); applyUIStyle(); });
        uiRow.appendChild(uiLabel); uiRow.appendChild(uiSelect); mwiCfgSection.appendChild(uiRow);
        // Settings Button dropdown
        const btnRow = document.createElement('div'); btnRow.className = 'mwi-settings-row';
        const btnLabel = document.createElement('label'); btnLabel.textContent = 'Settings Button';
        const btnSelect = document.createElement('select');
        [['frosted','Frosted'],['solid','Solid']].forEach(([v,t]) => { const o = document.createElement('option'); o.value=v; o.textContent=t; if ((cfg.btnStyle||'frosted')===v) o.selected=true; btnSelect.appendChild(o); });
        btnSelect.addEventListener('change', () => { cfg.btnStyle = btnSelect.value; saveSettings(); applyUIStyle(); });
        btnRow.appendChild(btnLabel); btnRow.appendChild(btnSelect); mwiCfgSection.appendChild(btnRow);
      } catch (e) {}

      // Themes selector
      const themesSection = document.createElement('div'); themesSection.className = 'mwi-customizer-sub'; themesSection.style.borderTop = 'none'; themesSection.style.paddingTop = '2px'; themesSection.id = 'mwi-section-themes';
      const themesTitle = document.createElement('h5'); themesTitle.textContent = 'Themes'; themesSection.appendChild(themesTitle);
      const themesList = document.createElement('div'); themesList.style.marginTop = '6px';
      try {
        const themeRow = document.createElement('div'); themeRow.className = 'mwi-settings-row';
        const themeLabel = document.createElement('label'); themeLabel.textContent = 'Preset theme';
        const themeSelect = document.createElement('select');
        for (const t of PRESET_THEMES) {
          const o = document.createElement('option'); o.value = t.key; o.textContent = t.label; if ((cfg.themeKey||'') === t.key) o.selected = true; themeSelect.appendChild(o);
        }
        themeSelect.addEventListener('change', () => {
          try {
            const key = themeSelect.value || '';
            cfg.themeKey = key;
            // find preset
            const t = PRESET_THEMES.find(x => x.key === key);
            if (t && t.siteColors) {
              // replace siteColors with theme values
              cfg.siteColors = cfg.siteColors || {};
              // clear existing
              for (const k of Object.keys(cfg.siteColors)) delete cfg.siteColors[k];
              // copy theme siteColors
              for (const kk of Object.keys(t.siteColors || {})) cfg.siteColors[kk] = t.siteColors[kk];
            } else {
              // restore defaults if no theme
              cfg.siteColors = JSON.parse(JSON.stringify(DEFAULT_CFG.siteColors || {}));
            }
            applySiteColors(); saveSettings();
            try { if (typeof renderColorsEditor === 'function') renderColorsEditor(colorsList); } catch (e) {}
          } catch (e) { log('theme select error', e); }
        });
        themeRow.appendChild(themeLabel); themeRow.appendChild(themeSelect); themesList.appendChild(themeRow);
        // Import / Export controls for sharing settings
        const shareRow = document.createElement('div'); shareRow.className = 'mwi-settings-row';
        shareRow.style.alignItems = 'center';
        const exportBtn = document.createElement('button'); exportBtn.type = 'button'; exportBtn.textContent = 'Export';
        // Export: primary green
        exportBtn.style.background = '#1f7d3d'; exportBtn.style.color = '#fff'; exportBtn.style.border = '0'; exportBtn.style.padding = '6px 10px'; exportBtn.style.borderRadius = '6px'; exportBtn.style.marginRight = '8px'; exportBtn.classList.add('mwi-push-btn');
        const importBtn = document.createElement('button'); importBtn.type = 'button'; importBtn.textContent = 'Import';
        // Import: primary blue
        importBtn.style.background = '#1f4a8a'; importBtn.style.color = '#fff'; importBtn.style.border = '0'; importBtn.style.padding = '6px 10px'; importBtn.style.borderRadius = '6px'; importBtn.style.marginRight = '8px'; importBtn.classList.add('mwi-push-btn');
        // hidden textarea to show the exported string or accept import
        // Build a reusable modal for Import/Export strings
        function openShareModal(mode, text) {
          try {
            // remove existing if present
            const existing = document.getElementById('mwi-share-modal-overlay');
            if (existing) existing.remove();
            const over = document.createElement('div'); over.id = 'mwi-share-modal-overlay'; over.style.position = 'fixed'; over.style.inset = '0'; over.style.background = 'rgba(0,0,0,0.6)'; over.style.display = 'flex'; over.style.alignItems = 'center'; over.style.justifyContent = 'center'; over.style.zIndex = '10000020';
            const modal = document.createElement('div'); modal.className = 'mwi-share-modal'; modal.style.background = '#0f1720'; modal.style.color = '#e6eef8'; modal.style.padding = '12px'; modal.style.borderRadius = '8px'; modal.style.width = '560px'; modal.style.maxWidth = '92%'; modal.style.boxShadow = '0 8px 24px rgba(0,0,0,0.6)'; modal.style.display = 'flex'; modal.style.flexDirection = 'column';
            const h = document.createElement('h4'); h.textContent = (mode === 'export') ? 'Export Settings' : 'Import Settings'; h.style.margin = '0 0 8px 0'; modal.appendChild(h);
            const ta = document.createElement('textarea'); ta.style.width = '100%'; ta.style.height = '140px'; ta.style.marginBottom = '8px'; ta.value = text || '';
            if (mode === 'export') { ta.readOnly = true; }
            modal.appendChild(ta);
            const row = document.createElement('div'); row.style.display = 'flex'; row.style.justifyContent = 'flex-end'; row.style.gap = '8px';
            if (mode === 'export') {
              const copyBtn = document.createElement('button'); copyBtn.type = 'button'; copyBtn.textContent = 'Copy'; copyBtn.classList.add('mwi-push-btn');
              copyBtn.addEventListener('click', async () => { try { await navigator.clipboard.writeText(ta.value); copyBtn.textContent = 'Copied'; setTimeout(() => copyBtn.textContent = 'Copy', 1200); } catch (e) { alert('Copy failed'); } });
              row.appendChild(copyBtn);
            } else {
              const applyBtn = document.createElement('button'); applyBtn.type = 'button'; applyBtn.textContent = 'Apply'; applyBtn.classList.add('mwi-push-btn');
              applyBtn.addEventListener('click', () => {
                try {
                  const val = (ta.value || '').trim(); if (!val) { alert('Paste an import string into the box then click Apply.'); return; }
                  let json = null;
                  try { json = decodeURIComponent(escape(atob(val))); } catch (e) { try { json = atob(val); } catch (e2) { throw new Error('Unable to decode string'); } }
                  let obj = null; try { obj = JSON.parse(json); } catch (e) { throw new Error('Invalid JSON payload'); }
                  if (!obj || typeof obj !== 'object') throw new Error('Invalid import data');
                  // allow applying any cfg key except dev-only keys
                  const allowed = Object.keys(cfg).filter(k => !DEV_KEYS.includes(k));
                  for (const k of Object.keys(obj)) if (allowed.includes(k)) cfg[k] = obj[k];
                  saveSettings(); applySiteColors();
                  try { applyHideOrganize(); } catch (e) {}
                  try { if (typeof renderColorsEditor === 'function') renderColorsEditor(colorsList); } catch (e) {}
                  // Show confirmation prompting the user to refresh (like Reset flow)
                  try {
                    if (document.getElementById('mwi-import-confirm')) return;
                    // remove the share modal so the confirmation appears above the settings overlay
                    try { over.remove(); } catch (e) {}
                    const conf = document.createElement('div'); conf.id = 'mwi-import-confirm';
                    conf.style.position = 'absolute'; conf.style.left = '50%'; conf.style.top = '50%';
                    conf.style.transform = 'translate(-50%, -50%)'; conf.style.zIndex = 10000003;
                    conf.style.background = '#071019'; conf.style.color = '#e6eef8'; conf.style.padding = '14px';
                    conf.style.borderRadius = '8px'; conf.style.boxShadow = '0 8px 24px rgba(0,0,0,0.6)'; conf.style.width = '420px';

                    const msg = document.createElement('div'); msg.style.marginBottom = '12px'; msg.style.fontSize = '13px';
                    msg.textContent = 'Import applied. Refresh the page to apply changes?';
                    const btnRow = document.createElement('div'); btnRow.style.textAlign = 'right';
                    const cancel = document.createElement('button'); cancel.textContent = 'Cancel'; cancel.style.marginRight = '8px';
                    cancel.addEventListener('click', () => { try { conf.remove(); } catch (e) {} });
                    const confirmNow = document.createElement('button'); confirmNow.textContent = 'Refresh Now';
                    confirmNow.style.background = '#1f7d3d'; confirmNow.style.color = '#fff'; confirmNow.style.border = '0'; confirmNow.style.padding = '8px 12px'; confirmNow.style.borderRadius = '6px';
                    confirmNow.addEventListener('click', () => {
                      try {
                        // Remove confirmation box and share modal, then close settings overlay and reload
                        try { conf.remove(); } catch (e) {}
                        try { over.remove(); } catch (e) {}
                        try { if (typeof animateClose === 'function') animateClose(); else if (overlay && overlay.style) overlay.style.display = 'none'; } catch (e) {}
                        setTimeout(() => { try { location.reload(); } catch (e) { log('reload after import failed', e); } }, 50);
                      } catch (e) { log('confirm import error', e); }
                    });

                    btnRow.appendChild(cancel); btnRow.appendChild(confirmNow);
                    conf.appendChild(msg); conf.appendChild(btnRow);
                    try { if (overlay) overlay.appendChild(conf); else document.body.appendChild(conf); } catch (e) { document.body.appendChild(conf); }
                  } catch (e) { log('import applied confirmation error', e); }
                } catch (e) { log('import error', e); alert('Import failed: ' + (e && e.message)); }
              });
              row.appendChild(applyBtn);
            }
            function closeShareModal() {
              over.classList.remove('mwi-share-open');
              over.classList.add('mwi-share-closing');
              setTimeout(() => { try { over.remove(); } catch (e) {} }, 200);
            }
            const closeBtn = document.createElement('button'); closeBtn.type = 'button'; closeBtn.textContent = 'Close'; closeBtn.classList.add('mwi-push-btn'); closeBtn.addEventListener('click', () => closeShareModal()); row.appendChild(closeBtn);
            modal.appendChild(row);
            over.appendChild(modal);
            // clicking outside the share modal closes it (but won't affect main modal)
            over.addEventListener('click', (ev) => { try { if (ev.target === over) closeShareModal(); } catch (e) {} });
            document.body.appendChild(over);
            requestAnimationFrame(() => requestAnimationFrame(() => over.classList.add('mwi-share-open')));
            ta.focus(); if (mode === 'export') ta.select();
          } catch (e) { log('openShareModal error', e); }
        }
        // Export implementation: produce compact base64 JSON and open modal
        exportBtn.addEventListener('click', async () => {
          try {
            // export all non-dev cfg keys
            const keys = Object.keys(cfg).filter(k => !DEV_KEYS.includes(k));
            const out = {};
            for (const k of keys) if (cfg[k] !== undefined) out[k] = cfg[k];
            const json = JSON.stringify(out);
            const encoded = btoa(unescape(encodeURIComponent(json)));
            openShareModal('export', encoded);
          } catch (e) { log('export error', e); alert('Export failed: ' + (e && e.message)); }
        });
        // Import implementation: open modal with empty textarea for paste
        importBtn.addEventListener('click', () => { try { openShareModal('import', ''); } catch (e) { log('import open error', e); } });
        const customLabel = document.createElement('label'); customLabel.textContent = 'Custom theme'; customLabel.style.flex = '1'; customLabel.style.marginRight = '8px';
        shareRow.appendChild(customLabel);
        shareRow.appendChild(importBtn); shareRow.appendChild(exportBtn); themesList.appendChild(shareRow);
      } catch (e) {}
      themesSection.appendChild(themesList);

      // Enable toggle
      try {
        const beRow = document.createElement('div'); beRow.className = 'mwi-settings-row';
        const beLabel = document.createElement('label'); beLabel.textContent = 'Enable background'; beLabel.htmlFor = 'mwi-bg-enabled-chk'; beLabel.style.cursor = 'pointer'; beLabel.style.flex = '1';
        const beChk = document.createElement('input'); beChk.type = 'checkbox'; beChk.id = 'mwi-bg-enabled-chk'; beChk.checked = !!cfg.backgroundEnabled;
        beChk.addEventListener('change', () => { cfg.backgroundEnabled = beChk.checked; applySiteColors(); saveSettings(); });
        beRow.appendChild(beLabel); beRow.appendChild(beChk); bgList.appendChild(beRow);
      } catch (e) {}

      // URL input + reset
      try {
        const urlRow = document.createElement('div'); urlRow.className = 'mwi-settings-row';
        const urlLabel = document.createElement('label'); urlLabel.textContent = 'Image URL';
        const urlInput = document.createElement('input'); urlInput.type = 'text'; urlInput.placeholder = 'https://...'; urlInput.value = cfg.backgroundUrl || '';
        urlInput.style.flex = '1'; urlInput.style.marginLeft = '8px';
        urlInput.addEventListener('change', () => {
          try {
            let v = (urlInput.value || '').trim();
            if (v) {
              // If no scheme present but looks like a domain/path, prefix https://
              // allow protocol-relative URLs (//cdn.example.com/img.png)
              if (!/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(v) && !v.startsWith('//')) {
                // naive domain detection (has a dot and no spaces)
                if (/^[^\s]+\.[^\s]+/.test(v)) v = 'https://' + v;
              }
            }
            cfg.backgroundUrl = v;
            applySiteColors(); saveSettings();
          } catch (e) { log('background url change error', e); }
        });
        // URL input (preview control removed)
        urlRow.appendChild(urlLabel); urlRow.appendChild(urlInput);
        bgList.appendChild(urlRow);

        // Opacity
        const opRow = document.createElement('div'); opRow.className = 'mwi-settings-row';
        const opLabel = document.createElement('label'); opLabel.textContent = 'Opacity';
        const opacityInput = document.createElement('input'); opacityInput.type = 'range'; opacityInput.min = 0; opacityInput.max = 100; opacityInput.value = String(Math.round((cfg.backgroundOpacity !== undefined ? cfg.backgroundOpacity : 1) * 100)); opacityInput.style.marginLeft = '8px';
        opacityInput.addEventListener('input', () => { cfg.backgroundOpacity = Number(opacityInput.value)/100; applySiteColors(); saveSettings(); });
        opRow.appendChild(opLabel); opRow.appendChild(opacityInput); bgList.appendChild(opRow);

        // size/position controls removed; overlay uses fixed cover/center
      } catch (e) {}

      bgSection.appendChild(bgList);

      // Hide/Organize Elements section (button opens organize modal)
      const hideSection = document.createElement('div'); hideSection.className = 'mwi-customizer-sub'; hideSection.id = 'mwi-section-hide-organize';
      const hideTitle = document.createElement('h5'); hideTitle.textContent = 'Manage Elements'; hideSection.appendChild(hideTitle);
      const hideList = document.createElement('div'); hideList.style.marginTop = '6px';
      try {
        const openBtnRow = document.createElement('div'); openBtnRow.className = 'mwi-settings-row';
        const openLabel = document.createElement('label'); openLabel.textContent = 'Customize left side panel'; openLabel.style.flex = '1';
        const openBtn = document.createElement('button'); openBtn.type = 'button'; openBtn.textContent = 'Manage';
        // Manage: neutral gray
        openBtn.style.background = '#6b7280'; openBtn.style.color = '#fff'; openBtn.style.border = '0'; openBtn.style.padding = '6px 10px'; openBtn.style.borderRadius = '6px'; openBtn.style.marginRight = '8px'; openBtn.classList.add('mwi-push-btn');
        openBtn.addEventListener('click', () => { try { openOrganizeModal(); } catch (e) { log('open organize error', e); } });
        openBtnRow.appendChild(openLabel); openBtnRow.appendChild(openBtn); hideList.appendChild(openBtnRow);
        // Swap panels toggle
        const swapRow = document.createElement('div'); swapRow.className = 'mwi-settings-row'; swapRow.style.marginTop = '8px';
        const swapChk = document.createElement('input'); swapChk.type = 'checkbox'; swapChk.id = 'mwi-swap-panels-chk'; swapChk.checked = (cfg.swapPanels === true);
        const swapLbl = document.createElement('label'); swapLbl.htmlFor = 'mwi-swap-panels-chk'; swapLbl.textContent = 'Swap left and right panels';
        swapChk.addEventListener('change', () => {
          cfg.swapPanels = swapChk.checked;
          saveSettings();
          document.documentElement.classList.toggle('mwi-swap-panels', cfg.swapPanels);
        });
        swapRow.appendChild(swapLbl); swapRow.appendChild(swapChk); hideList.appendChild(swapRow);
        // Chat to top toggle
        const chatTopRow = document.createElement('div'); chatTopRow.className = 'mwi-settings-row'; chatTopRow.style.marginTop = '8px';
        const chatTopChk = document.createElement('input'); chatTopChk.type = 'checkbox'; chatTopChk.id = 'mwi-chat-top-chk'; chatTopChk.checked = (cfg.chatTop === true);
        const chatTopLbl = document.createElement('label'); chatTopLbl.htmlFor = 'mwi-chat-top-chk'; chatTopLbl.textContent = 'Move chat to top'; chatTopLbl.style.cursor = 'pointer'; chatTopLbl.style.flex = '1';
        chatTopChk.addEventListener('change', () => {
          cfg.chatTop = chatTopChk.checked;
          saveSettings();
          document.documentElement.classList.toggle('mwi-chat-top', cfg.chatTop);
        });
        chatTopRow.appendChild(chatTopLbl); chatTopRow.appendChild(chatTopChk); hideList.appendChild(chatTopRow);
        // Header to bottom toggle
        const hdrRow = document.createElement('div'); hdrRow.className = 'mwi-settings-row'; hdrRow.style.marginTop = '8px';
        const hdrChk = document.createElement('input'); hdrChk.type = 'checkbox'; hdrChk.id = 'mwi-header-bottom-chk'; hdrChk.checked = (cfg.headerBottom === true);
        const hdrLbl = document.createElement('label'); hdrLbl.htmlFor = 'mwi-header-bottom-chk'; hdrLbl.textContent = 'Move header to bottom'; hdrLbl.style.cursor = 'pointer'; hdrLbl.style.flex = '1';
        hdrChk.addEventListener('change', () => {
          cfg.headerBottom = hdrChk.checked;
          saveSettings();
          document.documentElement.classList.toggle('mwi-header-bottom', cfg.headerBottom);
        });
        hdrRow.appendChild(hdrLbl); hdrRow.appendChild(hdrChk); hideList.appendChild(hdrRow);
      } catch (e) {}
      hideSection.appendChild(hideList);


      

      // Inventory section
      const invSection = document.createElement('div'); invSection.className = 'mwi-settings-section'; invSection.id = 'mwi-section-inventory';
      const invTitle = document.createElement('h4'); invTitle.textContent = 'Inventory Color Coding'; invTitle.style.background = 'linear-gradient(90deg, #ff44cc, #44aaff)'; invTitle.style.webkitBackgroundClip = 'text'; invTitle.style.webkitTextFillColor = 'transparent'; invTitle.style.backgroundClip = 'text';
      invSection.appendChild(invTitle);

      // Inventory setting: color mode dropdown
      const modeRow = document.createElement('div'); modeRow.className = 'mwi-settings-row';
      const modeLabel = document.createElement('label'); modeLabel.textContent = 'Color mode';
      const modeSelect = document.createElement('select');
      ['Quantity','All','None'].forEach(opt => {
        const o = document.createElement('option'); o.value = opt; o.textContent = opt; if (cfg.colorMode === opt) o.selected = true; modeSelect.appendChild(o);
      });
      modeSelect.addEventListener('change', () => {
        cfg.colorMode = modeSelect.value;
        saveSettings();
        try { if (window.MWI_InventoryHighlighter && window.MWI_InventoryHighlighter.reScan) window.MWI_InventoryHighlighter.reScan(); } catch (e) {}
        try { if (typeof updateInventoryEditorsVisibility === 'function') updateInventoryEditorsVisibility(); } catch (e) {}
      });
      modeRow.appendChild(modeLabel); modeRow.appendChild(modeSelect);
      invSection.appendChild(modeRow);

      // All-mode color picker row (hidden unless mode === 'All')
      const allRow = document.createElement('div'); allRow.className = 'mwi-settings-row';
      const allLabel = document.createElement('div'); allLabel.textContent = 'All Color'; allLabel.style.flex = '1'; allLabel.style.marginRight = '8px';
      const allInput = document.createElement('input'); allInput.type = 'color';
      try { allInput.value = (cfg.allColor && String(cfg.allColor).trim()) || '#1d8ce0'; } catch (e) { allInput.value = '#1d8ce0'; }
      allInput.addEventListener('input', () => { try { cfg.allColor = allInput.value; saveSettings(); if (window.MWI_InventoryHighlighter && window.MWI_InventoryHighlighter.reScan) window.MWI_InventoryHighlighter.reScan(); } catch (e) {} });
      allRow.appendChild(allLabel); allRow.appendChild(allInput);
      // alpha slider
      const allAlpha = document.createElement('input'); allAlpha.type = 'range'; allAlpha.min = 0; allAlpha.max = 100; allAlpha.style.marginLeft = '8px';
      try { allAlpha.value = String(Math.round((cfg.allAlpha !== undefined ? cfg.allAlpha : 1) * 100)); } catch (e) { allAlpha.value = '100'; }
      allAlpha.addEventListener('input', () => { try { cfg.allAlpha = Number(allAlpha.value)/100; saveSettings(); if (window.MWI_InventoryHighlighter && window.MWI_InventoryHighlighter.reScan) window.MWI_InventoryHighlighter.reScan(); } catch (e) {} });
      allRow.appendChild(allAlpha);
      // single clipboard shared across all color rows (site colors, all color, quantity tiers)
      let sharedColorClipboard = null;
      // copy button
      const allCopy = document.createElement('button'); allCopy.type = 'button'; allCopy.title = 'Copy color & opacity'; allCopy.textContent = '\u2398'; allCopy.classList.add('mwi-push-btn');
      allCopy.style.marginLeft = '8px'; allCopy.style.width = '26px'; allCopy.style.height = '22px'; allCopy.style.borderRadius = '4px'; allCopy.style.border = '1px solid rgba(255,255,255,0.06)'; allCopy.style.background = 'transparent'; allCopy.style.color = '#aaa'; allCopy.style.fontSize = '14px';
      allCopy.addEventListener('click', () => {
        try {
          sharedColorClipboard = { color: allInput.value, alpha: Number(allAlpha.value) };
          document.querySelectorAll('#mwi-settings-dialog .mwi-paste-btn').forEach(b => { b.style.color = '#ff44cc'; setTimeout(() => { b.style.color = '#aaa'; }, 600); });
          allCopy.style.color = '#ff44cc'; setTimeout(() => { allCopy.style.color = '#aaa'; }, 600);
        } catch(e) {}
      });
      // paste button
      const allPaste = document.createElement('button'); allPaste.type = 'button'; allPaste.title = 'Paste color & opacity'; allPaste.textContent = '⏶'; allPaste.classList.add('mwi-push-btn', 'mwi-paste-btn');
      allPaste.style.marginLeft = '2px'; allPaste.style.width = '26px'; allPaste.style.height = '22px'; allPaste.style.borderRadius = '4px'; allPaste.style.border = '1px solid rgba(255,255,255,0.06)'; allPaste.style.background = 'transparent'; allPaste.style.color = '#aaa'; allPaste.style.fontSize = '12px';
      allPaste.addEventListener('click', () => {
        try {
          if (!sharedColorClipboard) return;
          cfg.allColor = sharedColorClipboard.color; cfg.allAlpha = sharedColorClipboard.alpha / 100;
          allInput.value = sharedColorClipboard.color; allAlpha.value = String(sharedColorClipboard.alpha);
          saveSettings(); if (window.MWI_InventoryHighlighter && window.MWI_InventoryHighlighter.reScan) window.MWI_InventoryHighlighter.reScan();
          allPaste.style.color = '#44aaff'; setTimeout(() => { allPaste.style.color = '#aaa'; }, 600);
        } catch(e) {}
      });
      // reset button
      const allReset = document.createElement('button'); allReset.type = 'button'; allReset.title = 'Reset All color to default'; allReset.textContent = '↺'; allReset.classList.add('mwi-push-btn');
      allReset.style.marginLeft = '2px'; allReset.style.width = '26px'; allReset.style.height = '22px'; allReset.style.borderRadius = '4px'; allReset.style.border = '1px solid rgba(255,255,255,0.06)'; allReset.style.background = 'transparent'; allReset.style.color = '#fff';
      allReset.addEventListener('click', () => {
        try {
          if (DEFAULT_CFG && DEFAULT_CFG.allColor) cfg.allColor = DEFAULT_CFG.allColor; else delete cfg.allColor;
          cfg.allAlpha = (DEFAULT_CFG && DEFAULT_CFG.allAlpha !== undefined) ? DEFAULT_CFG.allAlpha : 1;
          try { allInput.value = cfg.allColor || '#1d8ce0'; } catch (e) { allInput.value = '#1d8ce0'; }
          allAlpha.value = String(Math.round((cfg.allAlpha !== undefined ? cfg.allAlpha : 1) * 100));
          saveSettings(); if (window.MWI_InventoryHighlighter && window.MWI_InventoryHighlighter.reScan) window.MWI_InventoryHighlighter.reScan();
        } catch (e) { log('reset all color error', e); }
      });
      allRow.appendChild(allCopy); allRow.appendChild(allPaste); allRow.appendChild(allReset);
      // do not append directly to invSection 窶・will be inserted into the category wrapper
      // (so All replaces Quantity in the same area)

      // Inventory setting: toggle inner curved border (disable the bright inset border)
      const innerRow = document.createElement('div'); innerRow.className = 'mwi-settings-row';
      const innerLabel = document.createElement('label'); innerLabel.textContent = 'Borders'; innerLabel.htmlFor = 'mwi-borders-chk'; innerLabel.style.cursor = 'pointer'; innerLabel.style.flex = '1';
      const innerChk = document.createElement('input'); innerChk.type = 'checkbox'; innerChk.id = 'mwi-borders-chk'; innerChk.checked = !!cfg.showInnerBorder;
      innerChk.addEventListener('change', () => { cfg.showInnerBorder = innerChk.checked; saveSettings(); try { if (window.MWI_InventoryHighlighter && window.MWI_InventoryHighlighter.reScan) window.MWI_InventoryHighlighter.reScan(); } catch (e) {} });
      innerRow.appendChild(innerLabel); innerRow.appendChild(innerChk);
      invSection.appendChild(innerRow);

      // Inventory setting: glow spread
      const spreadRow = document.createElement('div'); spreadRow.className = 'mwi-settings-row';
      const spreadLabel = document.createElement('label'); spreadLabel.textContent = 'Glow'; spreadLabel.style.flex = '1';
      const spreadInput = document.createElement('input'); spreadInput.type = 'number'; spreadInput.value = cfg.glowSpread || 6; spreadInput.min = 0;
      spreadInput.addEventListener('change', () => { cfg.glowSpread = Number(spreadInput.value) || cfg.glowSpread; window.MWI_InventoryHighlighter.setGlowSpread(cfg.glowSpread); saveSettings(); });
      const glowResetBtn = document.createElement('button'); glowResetBtn.type = 'button'; glowResetBtn.title = 'Reset to default'; glowResetBtn.textContent = '↺'; glowResetBtn.classList.add('mwi-push-btn');
      glowResetBtn.style.marginLeft = '2px'; glowResetBtn.style.width = '26px'; glowResetBtn.style.height = '22px'; glowResetBtn.style.borderRadius = '4px'; glowResetBtn.style.border = '1px solid rgba(255,255,255,0.06)'; glowResetBtn.style.background = 'transparent'; glowResetBtn.style.color = '#aaa'; glowResetBtn.style.fontSize = '14px'; glowResetBtn.style.cursor = 'pointer';
      glowResetBtn.addEventListener('click', () => { cfg.glowSpread = DEFAULT_CFG.glowSpread; spreadInput.value = cfg.glowSpread; try { window.MWI_InventoryHighlighter.setGlowSpread(cfg.glowSpread); } catch (e) {} saveSettings(); });
      spreadRow.appendChild(spreadLabel); spreadRow.appendChild(spreadInput); spreadRow.appendChild(glowResetBtn);
      invSection.appendChild(spreadRow);
      try { invSection.insertBefore(innerRow, spreadRow); } catch (e) {}

      // Category editor removed

      // Quantity tiers editor (editable colors for quantity thresholds) as a subcategory of Inventory
      const qtySection = document.createElement('div'); qtySection.className = 'mwi-colors-subsection';
      const qtyTitle = document.createElement('h5'); qtyTitle.textContent = 'Quantity Colors';
      qtySection.appendChild(qtyTitle);
      const qtyList = document.createElement('div'); qtyList.style.marginTop = '6px';

      function renderQuantityEditor(container) {
        container.innerHTML = '';
        const tiers = cfg.collectionQuantityTiers || [];
        if (!tiers.length) {
          const empty = document.createElement('div'); empty.style.fontSize = '12px'; empty.style.color = '#9fb7d7'; empty.textContent = 'No quantity tiers configured.'; container.appendChild(empty); return;
        }
        for (let i = 0; i < tiers.length; i++) {
          try {
            const t = tiers[i];
            const row = document.createElement('div'); row.className = 'mwi-settings-row';
            const lbl = document.createElement('div'); lbl.textContent = '≥ ' + (t.min || 0); lbl.style.flex = '1'; lbl.style.marginRight = '8px';
            const colorInput = document.createElement('input'); colorInput.type = 'color';
            try { colorInput.value = (t.color && String(t.color).trim()) || '#ffffff'; } catch (e) { colorInput.value = '#ffffff'; }
            colorInput.addEventListener('input', () => {
              try { cfg.collectionQuantityTiers[i].color = colorInput.value; saveSettings(); if (window.MWI_InventoryHighlighter && window.MWI_InventoryHighlighter.reScan) window.MWI_InventoryHighlighter.reScan(); } catch (e) { log('quantity color set error', e); }
            });
            row.appendChild(lbl); row.appendChild(colorInput);
            // alpha slider for quantity tier
            const aVal = (t.alpha !== undefined) ? t.alpha : 0.2;
            const alpha = document.createElement('input'); alpha.type = 'range'; alpha.min = 0; alpha.max = 100; alpha.value = String(Math.round(aVal*100)); alpha.style.marginLeft = '8px'; alpha.title = 'Opacity';
            alpha.addEventListener('input', () => {
              try { cfg.collectionQuantityTiers[i].alpha = Number(alpha.value)/100; saveSettings(); if (window.MWI_InventoryHighlighter && window.MWI_InventoryHighlighter.reScan) window.MWI_InventoryHighlighter.reScan(); } catch (e) { log('quantity alpha set error', e); }
            });
            // copy button
            const copyBtn = document.createElement('button'); copyBtn.type = 'button'; copyBtn.title = 'Copy color & opacity'; copyBtn.textContent = '\u2398'; copyBtn.classList.add('mwi-push-btn');
            copyBtn.style.marginLeft = '8px'; copyBtn.style.width = '26px'; copyBtn.style.height = '22px'; copyBtn.style.borderRadius = '4px'; copyBtn.style.border = '1px solid rgba(255,255,255,0.06)'; copyBtn.style.background = 'transparent'; copyBtn.style.color = '#aaa'; copyBtn.style.fontSize = '14px';
            copyBtn.addEventListener('click', () => {
              try {
                sharedColorClipboard = { color: colorInput.value, alpha: Number(alpha.value) };
                document.querySelectorAll('#mwi-settings-dialog .mwi-paste-btn').forEach(b => { b.style.color = '#ff44cc'; setTimeout(() => { b.style.color = '#aaa'; }, 600); });
                copyBtn.style.color = '#ff44cc'; setTimeout(() => { copyBtn.style.color = '#aaa'; }, 600);
              } catch(e) {}
            });
            // paste button
            const pasteBtn = document.createElement('button'); pasteBtn.type = 'button'; pasteBtn.title = 'Paste color & opacity'; pasteBtn.textContent = '⏶'; pasteBtn.classList.add('mwi-push-btn', 'mwi-paste-btn');
            pasteBtn.style.marginLeft = '2px'; pasteBtn.style.width = '26px'; pasteBtn.style.height = '22px'; pasteBtn.style.borderRadius = '4px'; pasteBtn.style.border = '1px solid rgba(255,255,255,0.06)'; pasteBtn.style.background = 'transparent'; pasteBtn.style.color = '#aaa'; pasteBtn.style.fontSize = '12px';
            pasteBtn.addEventListener('click', () => {
              try {
                if (!sharedColorClipboard) return;
                cfg.collectionQuantityTiers[i].color = sharedColorClipboard.color;
                cfg.collectionQuantityTiers[i].alpha = sharedColorClipboard.alpha / 100;
                colorInput.value = sharedColorClipboard.color; alpha.value = String(sharedColorClipboard.alpha);
                saveSettings(); if (window.MWI_InventoryHighlighter && window.MWI_InventoryHighlighter.reScan) window.MWI_InventoryHighlighter.reScan();
                pasteBtn.style.color = '#44aaff'; setTimeout(() => { pasteBtn.style.color = '#aaa'; }, 600);
              } catch(e) {}
            });
            // reset button
            const resetBtn = document.createElement('button'); resetBtn.type = 'button'; resetBtn.title = 'Reset this tier to default'; resetBtn.textContent = '↺'; resetBtn.classList.add('mwi-push-btn');
            resetBtn.style.marginLeft = '2px'; resetBtn.style.width = '26px'; resetBtn.style.height = '22px'; resetBtn.style.borderRadius = '4px'; resetBtn.style.border = '1px solid rgba(255,255,255,0.06)'; resetBtn.style.background = 'transparent'; resetBtn.style.color = '#fff';
            resetBtn.addEventListener('click', () => {
              try {
                if (DEFAULT_CFG && DEFAULT_CFG.collectionQuantityTiers && DEFAULT_CFG.collectionQuantityTiers[i]) {
                  cfg.collectionQuantityTiers[i].color = DEFAULT_CFG.collectionQuantityTiers[i].color;
                  cfg.collectionQuantityTiers[i].alpha = DEFAULT_CFG.collectionQuantityTiers[i].alpha !== undefined ? DEFAULT_CFG.collectionQuantityTiers[i].alpha : aVal;
                } else {
                  cfg.collectionQuantityTiers[i].alpha = aVal;
                }
                try { colorInput.value = (cfg.collectionQuantityTiers[i].color && String(cfg.collectionQuantityTiers[i].color).trim()) || '#ffffff'; } catch (e) {}
                alpha.value = String(Math.round((cfg.collectionQuantityTiers[i].alpha !== undefined ? cfg.collectionQuantityTiers[i].alpha : aVal)*100));
                saveSettings(); if (window.MWI_InventoryHighlighter && window.MWI_InventoryHighlighter.reScan) window.MWI_InventoryHighlighter.reScan();
              } catch (e) { log('reset quantity tier error', e); }
            });
            row.appendChild(alpha); row.appendChild(copyBtn); row.appendChild(pasteBtn); row.appendChild(resetBtn);
            container.appendChild(row);
          } catch (e) {}
        }
      }

      qtySection.appendChild(qtyList);
      renderQuantityEditor(qtyList);

      // Show/hide category vs quantity editors based on selected color mode
      function updateInventoryEditorsVisibility() {
        try {
          const mode = (cfg && cfg.colorMode) || (modeSelect && modeSelect.value) || 'None';
          if (mode === 'Quantity') {
            qtySection.style.display = '';
            allRow.style.display = 'none';
          } else if (mode === 'All') {
            qtySection.style.display = 'none';
            allRow.style.display = '';
          } else {
            qtySection.style.display = 'none';
            allRow.style.display = 'none';
          }
        } catch (e) {}
      }

      // initial visibility
      try { updateInventoryEditorsVisibility(); } catch (e) {}

      // Dev section (preserved keys / debug helpers)
      const devSection = document.createElement('div'); devSection.className = 'mwi-settings-section'; devSection.id = 'mwi-section-dev';
      const devTitle = document.createElement('h4'); devTitle.textContent = 'Dev'; devTitle.style.background = 'linear-gradient(90deg, #ff44cc, #44aaff)'; devTitle.style.webkitBackgroundClip = 'text'; devTitle.style.webkitTextFillColor = 'transparent'; devTitle.style.backgroundClip = 'text';
      devSection.appendChild(devTitle);
      const devList = document.createElement('div'); devList.style.marginTop = '6px';

      // Reset-on-refresh toggle
      try {
        const rrRow = document.createElement('div'); rrRow.className = 'mwi-settings-row';
        const rrLabel = document.createElement('label'); rrLabel.textContent = 'Reset to defaults on refresh';
        const rrChk = document.createElement('input'); rrChk.type = 'checkbox'; rrChk.checked = !!cfg.resetOnRefresh;
        rrChk.addEventListener('change', () => { cfg.resetOnRefresh = rrChk.checked; saveSettings(); });
        rrRow.appendChild(rrLabel); rrRow.appendChild(rrChk);
        devList.appendChild(rrRow);
      } catch (e) {}

      // Debug toggle (simple logger enable)
      try {
        const dRow = document.createElement('div'); dRow.className = 'mwi-settings-row';
        const dLabel = document.createElement('label'); dLabel.textContent = 'Enable debug logging';
        const dChk = document.createElement('input'); dChk.type = 'checkbox'; dChk.checked = !!cfg.debug;
        dChk.addEventListener('change', () => { cfg.debug = dChk.checked; saveSettings(); });
        dRow.appendChild(dLabel); dRow.appendChild(dChk);
        devList.appendChild(dRow);
      } catch (e) {}

      devSection.appendChild(devList);

      // Group Category and Quantity editors under an "Inventory Category" subsection
      const inventoryCategoryWrapper = document.createElement('div');
      inventoryCategoryWrapper.className = 'mwi-settings-section';
      // title removed per request: do not display "Inventory Category" text
      // Category editor removed; include quantity editor and the 'All' picker so
      // they occupy the same subsection and can be toggled (All replaces Quantity)
      inventoryCategoryWrapper.appendChild(qtySection);
      // append allRow (created earlier) into this wrapper so it shares the same area
      try { if (typeof allRow !== 'undefined' && allRow) inventoryCategoryWrapper.appendChild(allRow); } catch (e) {}
      invSection.appendChild(inventoryCategoryWrapper);

      // ensure local ordering now that rows exist
      try { reorderBordersGlow(invSection); } catch (e) {}

      // show/hide editors based on selected mode
      function renderColorsEditor(container) {
        container.innerHTML = '';
        const sc = cfg.siteColors || {};
        const groups = [
          { title: 'Header', fields: [
            { key: 'header', label: 'Header' , hasAlpha: true, alphaKey: 'headerAlpha', defaultAlpha: 0.2},
            { key: 'headerGrad', label: 'Header Gradient', hasAlpha: true, alphaKey: 'headerGradAlpha', defaultAlpha: 1},
            { key: 'progressBar', label: 'Progress Bar', hasAlpha: true, alphaKey: 'progressBarAlpha', defaultAlpha: 1}
          ]},
          { title: 'Combat', fields: [
            { key: 'combat', label: 'Combat', hasAlpha: true, alphaKey: 'combatAlpha', defaultAlpha: 1},
            { key: 'hp', label: 'HP', hasAlpha: true, alphaKey: 'hpAlpha', defaultAlpha: 1},
            { key: 'mp', label: 'MP', hasAlpha: true, alphaKey: 'mpAlpha', defaultAlpha: 1},
            { key: 'hitDmg', label: 'Hitsplat Damage', hasAlpha: true, alphaKey: 'hitDmgAlpha', defaultAlpha: 1},
            { key: 'hitMiss', label: 'Hitsplat Miss', hasAlpha: true, alphaKey: 'hitMissAlpha', defaultAlpha: 1},
            { key: 'attack', label: 'Attack', hasAlpha: true, alphaKey: 'attackAlpha', defaultAlpha: 1},
            { key: 'barBg', label: 'Bar Background', hasAlpha: true, alphaKey: 'barBgAlpha', defaultAlpha: 1}
          ,
            { key: 'consumables', label: 'Consumables / Abilities', hasAlpha: true, alphaKey: 'consumablesAlpha', defaultAlpha: 1}
          ]},
          { title: 'Main Panel', fields: [
            { key: 'subPanel', label: 'Panel' , hasAlpha: true, alphaKey: 'subPanelAlpha', defaultAlpha: 0.2},
            { key: 'skillActions', label: 'Skill Actions', hasAlpha: true, alphaKey: 'skillActionsAlpha', defaultAlpha: 1}
          ]},
          { title: 'Left Side Panel', fields: [
            { key: 'sidePanel', label: 'Panel' , hasAlpha: true, alphaKey: 'sidePanelAlpha', defaultAlpha: 0.2},
            { key: 'selectedSkill', label: 'Selected Skill', hasAlpha: true, alphaKey: 'selectedSkillAlpha', defaultAlpha: 1},
            { key: 'skillXPBar', label: 'Skill XP Bar', hasAlpha: true, alphaKey: 'skillXPBarAlpha', defaultAlpha: 1},
            { key: 'navLabel', label: 'Text Color', hasAlpha: true, alphaKey: 'navLabelAlpha', defaultAlpha: 1},
            { key: 'level', label: 'Level Text Color', hasAlpha: true, alphaKey: 'levelAlpha', defaultAlpha: 1}
          ]},
          { title: 'Right Side Panel', fields: [
            { key: 'panelBg', label: 'Panel' , hasAlpha: true, alphaKey: 'panelBgAlpha', defaultAlpha: 0.2},
            { key: 'inventoryLabels', label: 'Inventory Labels', hasAlpha: true, alphaKey: 'inventoryLabelsAlpha', defaultAlpha: 1},
            { key: 'interactables', label: 'Interactables', hasAlpha: true, alphaKey: 'interactablesAlpha', defaultAlpha: 1}
          ]},
          { title: 'Chat', fields: [
            { key: 'chatBg', label: 'Panel' , hasAlpha: true, alphaKey: 'chatBgAlpha', defaultAlpha: 0.2},
            { key: 'timestamp', label: 'Timestamp Color', hasAlpha: true, alphaKey: 'timestampAlpha', defaultAlpha: 1 },
            { key: 'chatText', label: 'Text Color', hasAlpha: true, alphaKey: 'chatTextAlpha', defaultAlpha: 1 },
            { key: 'systemMessage', label: 'System Message', hasAlpha: true, alphaKey: 'systemMessageAlpha', defaultAlpha: 1 }
          ]},
          { title: 'Tabs', fields: [
            { key: 'buttonBg', label: 'Tabs' , hasAlpha: true, alphaKey: 'buttonBgAlpha', defaultAlpha: 1},
            { key: 'tabsBg', label: 'Tabs Background', hasAlpha: true, alphaKey: 'tabsBgAlpha', defaultAlpha: 1},
            { key: 'accent', label: 'Text Color' , hasAlpha: true, alphaKey: 'accentAlpha', defaultAlpha: 1}
          ]}
        ];

        // clipboard for copy/paste between color rows
        // uses sharedColorClipboard from outer scope

        function buildRow(f) {
          try {
            const row = document.createElement('div'); row.className = 'mwi-settings-row';
            const lbl = document.createElement('div'); lbl.textContent = f.label; lbl.style.flex = '1'; lbl.style.marginRight = '8px';
            const colorInput = document.createElement('input'); colorInput.type = 'color';
            try { colorInput.value = (sc[f.key] && String(sc[f.key]).trim()) || '#ffffff'; } catch (e) { colorInput.value = '#ffffff'; }
            colorInput.addEventListener('input', () => {
              try {
                cfg.siteColors = cfg.siteColors || {};
                cfg.siteColors[f.key] = colorInput.value;
                if (f.hasAlpha && f.alphaKey && cfg.siteColors[f.alphaKey] === undefined) cfg.siteColors[f.alphaKey] = (f.defaultAlpha !== undefined ? f.defaultAlpha : 1);
                if (f.key === 'accent') cfg.siteColors.text = colorInput.value;
                try { const rng = row.querySelector('input[type="range"]'); if (rng) { rng.disabled = false; rng.classList.remove('mwi-range-disabled'); } colorInput.classList.remove('mwi-inactive'); } catch(e){}
                applySiteColors(); saveSettings();
              } catch (e) { log('site color set error', e); }
            });
            row.appendChild(lbl); row.appendChild(colorInput);
            if (f.hasAlpha) {
              const alpha = document.createElement('input'); alpha.type = 'range'; alpha.min = 0; alpha.max = 100; alpha.value = String(Math.round((sc[f.alphaKey] !== undefined ? sc[f.alphaKey] : (f.defaultAlpha !== undefined ? f.defaultAlpha : 1)) * 100)); alpha.style.marginLeft = '8px'; alpha.title = 'Opacity';
              alpha.addEventListener('input', () => {
                try { cfg.siteColors = cfg.siteColors || {}; cfg.siteColors[f.alphaKey] = Number(alpha.value)/100; applySiteColors(); saveSettings(); } catch (e) { log('site alpha set error', e); }
              });
              const isActive = sc[f.key] && String(sc[f.key]).trim() !== '';
              if (!isActive) { alpha.disabled = true; alpha.classList.add('mwi-range-disabled'); colorInput.classList.add('mwi-inactive'); }

              // Copy button
              const copyBtn = document.createElement('button'); copyBtn.type = 'button'; copyBtn.title = 'Copy color & opacity'; copyBtn.textContent = '\u2398'; copyBtn.classList.add('mwi-push-btn');
              copyBtn.style.marginLeft = '8px'; copyBtn.style.width = '26px'; copyBtn.style.height = '22px'; copyBtn.style.borderRadius = '4px'; copyBtn.style.border = '1px solid rgba(255,255,255,0.06)'; copyBtn.style.background = 'transparent'; copyBtn.style.color = '#aaa'; copyBtn.style.fontSize = '14px';
              copyBtn.addEventListener('click', () => {
                try {
                  sharedColorClipboard = { color: colorInput.value, alpha: Number(alpha.value) };
                  // Flash all paste buttons to indicate clipboard is ready
                  document.querySelectorAll('#mwi-settings-dialog .mwi-paste-btn').forEach(b => { b.style.color = '#ff44cc'; setTimeout(() => { b.style.color = '#aaa'; }, 600); });
                  copyBtn.style.color = '#ff44cc'; setTimeout(() => { copyBtn.style.color = '#aaa'; }, 600);
                } catch(e) {}
              });

              // Paste button
              const pasteBtn = document.createElement('button'); pasteBtn.type = 'button'; pasteBtn.title = 'Paste color & opacity'; pasteBtn.textContent = '⏶'; pasteBtn.className = 'mwi-paste-btn mwi-push-btn';
              pasteBtn.style.marginLeft = '2px'; pasteBtn.style.width = '26px'; pasteBtn.style.height = '22px'; pasteBtn.style.borderRadius = '4px'; pasteBtn.style.border = '1px solid rgba(255,255,255,0.06)'; pasteBtn.style.background = 'transparent'; pasteBtn.style.color = '#aaa'; pasteBtn.style.fontSize = '12px';
              pasteBtn.addEventListener('click', () => {
                try {
                  if (!sharedColorClipboard) return;
                  cfg.siteColors = cfg.siteColors || {};
                  cfg.siteColors[f.key] = sharedColorClipboard.color;
                  cfg.siteColors[f.alphaKey] = sharedColorClipboard.alpha / 100;
                  if (f.key === 'accent') cfg.siteColors.text = sharedColorClipboard.color;
                  colorInput.value = sharedColorClipboard.color;
                  alpha.value = String(sharedColorClipboard.alpha);
                  colorInput.classList.remove('mwi-inactive'); alpha.disabled = false; alpha.classList.remove('mwi-range-disabled');
                  applySiteColors(); saveSettings();
                  pasteBtn.style.color = '#44aaff'; setTimeout(() => { pasteBtn.style.color = '#aaa'; }, 600);
                } catch(e) {}
              });

              const resetBtn = document.createElement('button'); resetBtn.type = 'button'; resetBtn.title = 'Reset this setting to default'; resetBtn.textContent = '↺'; resetBtn.classList.add('mwi-push-btn');
              resetBtn.style.marginLeft = '2px'; resetBtn.style.width = '26px'; resetBtn.style.height = '22px'; resetBtn.style.borderRadius = '4px'; resetBtn.style.border = '1px solid rgba(255,255,255,0.06)'; resetBtn.style.background = 'transparent'; resetBtn.style.color = '#fff';
              resetBtn.addEventListener('click', () => {
                try {
                  cfg.siteColors = cfg.siteColors || {};
                  const defColor = (DEFAULT_CFG && DEFAULT_CFG.siteColors && DEFAULT_CFG.siteColors[f.key]) ? DEFAULT_CFG.siteColors[f.key] : '';
                  if (defColor) cfg.siteColors[f.key] = defColor; else delete cfg.siteColors[f.key];
                  const defAlpha = (DEFAULT_CFG && DEFAULT_CFG.siteColors && DEFAULT_CFG.siteColors[f.alphaKey] !== undefined) ? DEFAULT_CFG.siteColors[f.alphaKey] : (f.defaultAlpha !== undefined ? f.defaultAlpha : 1);
                  cfg.siteColors[f.alphaKey] = defAlpha;
                  try { if (defColor) colorInput.value = defColor; else colorInput.value = '#ffffff'; } catch(e){}
                  alpha.value = String(Math.round(cfg.siteColors[f.alphaKey]*100));
                  if (defColor) { colorInput.classList.remove('mwi-inactive'); alpha.disabled = false; alpha.classList.remove('mwi-range-disabled'); } else { colorInput.classList.add('mwi-inactive'); alpha.disabled = true; alpha.classList.add('mwi-range-disabled'); }
                  applySiteColors(); saveSettings();
                } catch (e) { log('reset single site color error', e); }
              });
              row.appendChild(alpha); row.appendChild(copyBtn); row.appendChild(pasteBtn); row.appendChild(resetBtn);
            }
            return row;
          } catch (e) { return null; }
        }

        for (let i = 0; i < groups.length; i++) {
          const g = groups[i];
          try {
            const sub = document.createElement('div'); sub.className = 'mwi-colors-subsection';
            const h = document.createElement('h5'); h.textContent = g.title; h.style.background = 'linear-gradient(90deg, #44aaff, #ff44cc)'; h.style.webkitBackgroundClip = 'text'; h.style.webkitTextFillColor = 'transparent'; h.style.backgroundClip = 'text'; sub.appendChild(h);
            // Remove the small separator for the first subsection (Header) so
            // it doesn't display a thin line directly beneath the 'Site Colors' title.
            if (i === 0) {
              try { sub.style.borderTop = 'none'; sub.style.marginTop = '0'; sub.style.paddingTop = '0'; h.style.marginTop = '0'; } catch (e) {}
            }
            for (const f of g.fields) {
              const r = buildRow(f);
              if (r) sub.appendChild(r);
            }
            container.appendChild(sub);
          } catch (e) {}
        }
      }
      const footer = document.createElement('div'); footer.style.marginTop = '12px'; footer.style.textAlign = 'right';

      // Append main sections into the content container so they're visible
      try {
        // ensure the colors list is part of the Colors section and render it
        try { colorsSection.appendChild(colorsList); renderColorsEditor(colorsList); } catch (e) {}
        // Customizer section wraps Themes, Background, Hide/Organize
        try {
          const customizerSection = document.createElement('div'); customizerSection.className = 'mwi-settings-section'; customizerSection.id = 'mwi-section-customizer';
          const customizerTitle = document.createElement('h4'); customizerTitle.textContent = 'Customizer'; customizerTitle.style.background = 'linear-gradient(90deg, #ff44cc, #44aaff)'; customizerTitle.style.webkitBackgroundClip = 'text'; customizerTitle.style.webkitTextFillColor = 'transparent'; customizerTitle.style.backgroundClip = 'text';
          customizerSection.appendChild(customizerTitle);
          customizerSection.appendChild(themesSection);
          customizerSection.appendChild(mwiCfgSection);
          customizerSection.appendChild(bgSection);
          customizerSection.appendChild(hideSection);
          content.appendChild(customizerSection);
        } catch (e) {}
        // Animations section
        try {
          const animSection = document.createElement('div'); animSection.className = 'mwi-settings-section'; animSection.id = 'mwi-section-animations';
          const animTitle = document.createElement('h4'); animTitle.textContent = 'Animations'; animTitle.style.background = 'linear-gradient(90deg, #ff44cc, #44aaff)'; animTitle.style.webkitBackgroundClip = 'text'; animTitle.style.webkitTextFillColor = 'transparent'; animTitle.style.backgroundClip = 'text';
          animSection.appendChild(animTitle);
          // Sitewide animations sub-section
          try {
            const sitewideSub = document.createElement('div'); sitewideSub.className = 'mwi-anim-sub';
            const sitewideSubTitle = document.createElement('h5'); sitewideSubTitle.textContent = 'Sitewide'; sitewideSubTitle.style.cssText = 'font-size:12px;margin:0 0 6px;background:linear-gradient(90deg,#44aaff,#ff44cc);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;';
            sitewideSub.appendChild(sitewideSubTitle);
            animSection.appendChild(sitewideSub);
          } catch (e) {}
          // Combat animations sub-section
          try {
            const combatSub = document.createElement('div'); combatSub.className = 'mwi-anim-sub';
            const combatSubTitle = document.createElement('h5'); combatSubTitle.textContent = 'Combat'; combatSubTitle.style.cssText = 'font-size:12px;margin:0 0 6px;background:linear-gradient(90deg,#44aaff,#ff44cc);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;';
            combatSub.appendChild(combatSubTitle);
            const combatRow = document.createElement('div'); combatRow.className = 'mwi-anim-toggle-row';
            const combatChk = document.createElement('input'); combatChk.type = 'checkbox'; combatChk.id = 'mwi-anim-combat-chk'; combatChk.checked = (cfg.combatAnim !== false);
            const combatLbl = document.createElement('label'); combatLbl.htmlFor = 'mwi-anim-combat-chk'; combatLbl.textContent = 'Hit impact - Inspired by MWI-Hit-Tracker';
            combatChk.addEventListener('change', () => { cfg.combatAnim = combatChk.checked; saveSettings(); });
            combatRow.appendChild(combatChk); combatRow.appendChild(combatLbl); combatSub.appendChild(combatRow);
            // Consumables / Abilities usage animation toggle
            const usageRow = document.createElement('div'); usageRow.className = 'mwi-anim-toggle-row';
            const usageChk = document.createElement('input'); usageChk.type = 'checkbox'; usageChk.id = 'mwi-anim-usage-chk'; usageChk.checked = (cfg.usageAnim !== false);
            const usageLbl = document.createElement('label'); usageLbl.htmlFor = 'mwi-anim-usage-chk'; usageLbl.textContent = 'Consumables / Abilities usage';
            usageChk.addEventListener('change', () => { cfg.usageAnim = usageChk.checked; saveSettings(); });
            usageRow.appendChild(usageChk); usageRow.appendChild(usageLbl); combatSub.appendChild(usageRow);
            animSection.appendChild(combatSub);
          } catch (e) {}
          content.appendChild(animSection);
        } catch (e) {}
        content.appendChild(invSection);
        content.appendChild(colorsSection);
        // include Dev in the scrolling content so it's not frozen
        try { content.appendChild(devSection); } catch (e) {}
      } catch (e) {}

      // Reset button area (centered at very bottom)
      const resetArea = document.createElement('div');
      resetArea.style.marginTop = '12px';
      resetArea.style.textAlign = 'center';
      // Refresh (left) + Reset (right) buttons
      const refreshBtn = document.createElement('button'); refreshBtn.id = 'mwi-settings-refresh'; refreshBtn.textContent = 'Refresh Page';
      refreshBtn.style.background = '#1f7d3d'; refreshBtn.style.color = '#fff'; refreshBtn.style.border = '0'; refreshBtn.style.padding = '8px 12px'; refreshBtn.style.borderRadius = '6px'; refreshBtn.style.marginRight = '8px'; refreshBtn.classList.add('mwi-push-btn');
      refreshBtn.addEventListener('click', () => { try { location.reload(); } catch (e) { log('refresh click error', e); } });

      const resetBtn = document.createElement('button'); resetBtn.id = 'mwi-settings-reset'; resetBtn.textContent = 'Reset to defaults';
      resetBtn.style.background = '#7f1d1d'; resetBtn.style.color = '#fff'; resetBtn.style.border = '0'; resetBtn.style.padding = '8px 12px'; resetBtn.style.borderRadius = '6px'; resetBtn.classList.add('mwi-push-btn');
      resetBtn.addEventListener('click', () => {
        try {
          if (document.getElementById('mwi-reset-confirm')) return;
          const conf = document.createElement('div'); conf.id = 'mwi-reset-confirm';
          conf.style.position = 'absolute'; conf.style.left = '50%'; conf.style.top = '50%';
          conf.style.transform = 'translate(-50%, -50%)'; conf.style.zIndex = 10000003;
          conf.style.background = '#071019'; conf.style.color = '#e6eef8'; conf.style.padding = '14px';
          conf.style.borderRadius = '8px'; conf.style.boxShadow = '0 8px 24px rgba(0,0,0,0.6)'; conf.style.width = '420px';

          const msg = document.createElement('div'); msg.style.marginBottom = '12px'; msg.style.fontSize = '13px';
          msg.textContent = 'Reset to defaults? This will clear your saved settings and then reload the page.';
          const btnRow = document.createElement('div'); btnRow.style.textAlign = 'right';
          const cancel = document.createElement('button'); cancel.textContent = 'Cancel'; cancel.style.marginRight = '8px';
          cancel.addEventListener('click', () => { try { conf.remove(); } catch (e) {} });
          const confirmNow = document.createElement('button'); confirmNow.textContent = 'Reset & Refresh';
          confirmNow.style.background = '#7f1d1d'; confirmNow.style.color = '#fff'; confirmNow.style.border = '0'; confirmNow.style.padding = '8px 12px'; confirmNow.style.borderRadius = '6px';
          confirmNow.addEventListener('click', () => {
            try {
              // Clear persisted settings, restore defaults in memory, persist, and reload.
              clearSettings();
              for (const k of Object.keys(DEFAULT_CFG)) {
                try { cfg[k] = JSON.parse(JSON.stringify(DEFAULT_CFG[k])); } catch (e) { cfg[k] = DEFAULT_CFG[k]; }
              }
              saveSettings();
              // Remove confirmation box and hide overlay before reload to avoid UI races
              try { conf.remove(); } catch (e) {}
              try { if (typeof animateClose === 'function') animateClose(); else if (overlay && overlay.style) overlay.style.display = 'none'; } catch (e) {}
              // Use a short timeout to allow the storage to flush in some browsers
              setTimeout(() => { try { location.reload(); } catch (e) { log('reload after reset failed', e); } }, 50);
            } catch (e) { log('confirm reset error', e); }
          });

          btnRow.appendChild(cancel); btnRow.appendChild(confirmNow);
          conf.appendChild(msg); conf.appendChild(btnRow);
          overlay.appendChild(conf);
        } catch (e) { log('reset modal error', e); }
      });
      resetArea.appendChild(refreshBtn);
      resetArea.appendChild(resetBtn);

      dialog.appendChild(closeBtn); dialog.appendChild(title); dialog.appendChild(notice); dialog.appendChild(searchWrap); dialog.appendChild(content); dialog.appendChild(footer);
      dialog.appendChild(resetArea);
      // signature footer (frozen, not part of scrollable content)
      try {
        const sig = document.createElement('div');
        sig.style.marginTop = '8px'; sig.style.fontSize = '12px'; sig.style.color = '#99b7d9'; sig.style.textAlign = 'center';
        sig.textContent = 'Maintained by ave (MWI username: collar)';
        // Links row: GitHub | Greasy Fork
        const links = document.createElement('div');
        links.style.marginTop = '6px'; links.style.fontSize = '12px'; links.style.color = '#99b7d9'; links.style.textAlign = 'center';
        const gh = document.createElement('a'); gh.href = 'https://github.com/collaring/mwi-customizer'; gh.textContent = 'GitHub'; gh.target = '_blank'; gh.style.cssText = 'color:#9ecbff;margin:0 8px;text-decoration:none;transition:color 200ms ease;background-clip:text;-webkit-background-clip:text;';
        const gfHoverStyle = 'background:linear-gradient(90deg,#44aaff,#ff44cc);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;color:transparent;';
        const ghHoverStyle = gfHoverStyle;
        gh.addEventListener('mouseenter', () => { gh.style.cssText += ghHoverStyle; });
        gh.addEventListener('mouseleave', () => { gh.style.cssText = 'color:#9ecbff;margin:0 8px;text-decoration:none;transition:color 200ms ease;'; });
        const sep = document.createElement('span'); sep.textContent = '|'; sep.style.color = '#6f8fa3'; sep.style.margin = '0 4px';
        const gf = document.createElement('a'); gf.href = 'https://greasyfork.org/en/scripts/570632-mwi-customizer'; gf.textContent = 'Greasy Fork'; gf.target = '_blank'; gf.style.cssText = 'color:#9ecbff;margin:0 8px;text-decoration:none;transition:color 200ms ease;';
        gf.addEventListener('mouseenter', () => { gf.style.cssText += gfHoverStyle; });
        gf.addEventListener('mouseleave', () => { gf.style.cssText = 'color:#9ecbff;margin:0 8px;text-decoration:none;transition:color 200ms ease;'; });
        const sep2 = document.createElement('span'); sep2.textContent = '|'; sep2.style.color = '#6f8fa3'; sep2.style.margin = '0 4px';
        const dc = document.createElement('a'); dc.href = 'https://discord.com/channels/891160051459436574/1485148839743721513'; dc.textContent = 'Discord'; dc.target = '_blank'; dc.style.cssText = 'color:#9ecbff;margin:0 8px;text-decoration:none;transition:color 200ms ease;';
        dc.addEventListener('mouseenter', () => { dc.style.cssText += gfHoverStyle; });
        dc.addEventListener('mouseleave', () => { dc.style.cssText = 'color:#9ecbff;margin:0 8px;text-decoration:none;transition:color 200ms ease;'; });
        const sep3 = document.createElement('span'); sep3.textContent = '|'; sep3.style.color = '#6f8fa3'; sep3.style.margin = '0 4px';
        const ws = document.createElement('a'); ws.href = 'https://ave.sh/'; ws.textContent = 'Website'; ws.target = '_blank'; ws.style.cssText = 'color:#9ecbff;margin:0 8px;text-decoration:none;transition:color 200ms ease;';
        ws.addEventListener('mouseenter', () => { ws.style.cssText += gfHoverStyle; });
        ws.addEventListener('mouseleave', () => { ws.style.cssText = 'color:#9ecbff;margin:0 8px;text-decoration:none;transition:color 200ms ease;'; });
        links.appendChild(gh); links.appendChild(sep); links.appendChild(gf); links.appendChild(sep2); links.appendChild(dc); links.appendChild(sep3); links.appendChild(ws);
        dialog.appendChild(sig); dialog.appendChild(links);
      } catch (e) {}
      overlay.appendChild(dialog); document.body.appendChild(overlay);
      // post-append safety: ensure ordering once dialog rows are inserted
      try {
        setTimeout(() => reorderBordersGlow(document.getElementById('mwi-settings-dialog')), 0);
        // compact observer: watch for row insertions and run reorder once
        const dlg = document.getElementById('mwi-settings-dialog');
        if (dlg) {
          const mo = new MutationObserver(() => { try { reorderBordersGlow(dlg); mo.disconnect(); } catch (e) {} });
          mo.observe(dlg, { childList: true, subtree: true });
        }
      } catch (e) {}
      
    }

    // close modal on Escape key when it's open; prefer closing organize/share modals first
    document.addEventListener('keydown', (ev) => {
      try {
        if (ev.key === 'Escape' || ev.key === 'Esc') {
            // close organize modal first (if open)
            const org = document.getElementById('mwi-organize-overlay');
            if (org) { try { org.classList.remove('mwi-organize-open'); org.classList.add('mwi-organize-closing'); setTimeout(() => { try { org.remove(); } catch (e) {} }, 200); } catch (e) {} return; }
            // then share/import modal
            const share = document.getElementById('mwi-share-modal-overlay');
            if (share) { try { share.classList.remove('mwi-share-open'); share.classList.add('mwi-share-closing'); setTimeout(() => { try { share.remove(); } catch (e) {} }, 200); } catch (e) {} return; }
            // finally close the main settings overlay (animate)
            const ov = document.getElementById('mwi-settings-overlay');
            if (ov && ov.style && ov.style.display === 'flex') {
              try { ov.classList.remove('mwi-dialog-open'); ov.classList.add('mwi-dialog-closing'); } catch (e) {}
              setTimeout(() => { try { ov.style.display = 'none'; ov.classList.remove('mwi-dialog-closing'); } catch (e) {} }, 220);
            }
        }
      } catch (e) {}
    });

    function openSettings() {
      try {
        // Always remove existing overlay and recreate modal so event handlers
        // (like the custom reset confirmation) are the current ones.
        const existing = document.getElementById('mwi-settings-overlay');
        if (existing) existing.remove();
        createSettingsModal();
        const ov = document.getElementById('mwi-settings-overlay');
        if (ov) {
          ov.style.display = 'flex';
          applyUIStyle();
          // trigger pop-in animation
            setTimeout(() => { try { ov.classList.add('mwi-dialog-open'); } catch (e) {} }, 10);
        }
      } catch (e) { log('openSettings error', e); }
    }


    function findFilterContainer() {
      const selectors = ['.item-filter', '.filter', '.ItemFilter', '[data-filter]', '.filters', '.controls'];
      for (const s of selectors) {
        const el = document.querySelector(s);
        if (el) return el;
      }
      // fallback: try inventory container header or rightmost toolbar
      const alt = document.querySelector('.Inventory_inventory__17CH2') || document.body;
      return alt;
    }

    function insertSettingsButton() {
      try {
        injectStyles(); createSettingsModal();
      // Prefer the item grid placement if available
      const grid = document.querySelector('.Inventory_itemGrid__20YAH');
      if (grid) {
        // ensure parent is positioned so absolute placement works
        const parent = grid.parentElement || grid;
        try {
          const cs = window.getComputedStyle(parent);
          if (cs.position === 'static' || !cs.position) parent.style.position = 'relative';
        } catch (e) {}
        const btn = document.createElement('button'); btn.id = 'mwi-settings-btn'; btn.title = 'MWI Customizer Settings'; btn.textContent = '\u2699 MWI Customizer';
        btn.setAttribute('aria-label', 'MWI Customizer Settings');
        btn.style.position = 'absolute'; btn.style.right = '8px'; btn.style.top = '8px'; btn.style.zIndex = 9999999;
        btn.addEventListener('click', openSettings);
        // append to parent so it sits over the grid on the far right
        parent.appendChild(btn);
        applyUIStyle();
        return;
      }

      const container = findFilterContainer();
      if (!container) return;
        // prefer adding to a toolbar-like area; if the found container is large, attach to its parent
        let target = container;
        if (container.tagName && container.tagName.toLowerCase() === 'body') {
          // place fixed near right edge if no filter container found
          const btn = document.createElement('button'); btn.id = 'mwi-settings-btn'; btn.title = 'MWI Customizer Settings'; btn.textContent = '\u2699 MWI Customizer';
          btn.style.position = 'fixed'; btn.style.right = '12px'; btn.style.bottom = '12px'; btn.style.zIndex = 9999999;
          btn.addEventListener('click', openSettings);
          document.body.appendChild(btn); applyUIStyle(); return;
        }
        // try to append next to container
        const btn = document.createElement('button'); btn.id = 'mwi-settings-btn'; btn.title = 'MWI Customizer Settings'; btn.textContent = '\u2699 MWI Customizer';
        btn.addEventListener('click', openSettings);
        // if container is inline/toolbar, append as child; otherwise append to its parent
        try { container.appendChild(btn); } catch (e) { container.parentElement && container.parentElement.appendChild(btn); }
      } catch (e) { log('insertSettingsButton error', e); }
    }

  // Main init
  (async function init() {
    log('Starting highlighter');
    // Wait a bit for game globals to initialize
    await waitFor(() => document.readyState === 'complete', 10000).catch(() => null);

    let colors = discoverCollectionColors();
    if (!(colors && (colors.hridMap && colors.hridMap.size || colors.nameMap && colors.nameMap.size))) {
      log('No collection-color map auto-detected. You may need to provide the game URL or DOM details.');
    }

    // initial highlight
    highlightInventory(colors);

    // observe DOM changes to re-run lightweight highlighting with debounce
    let pendingHighlightTimer = null;
    const observer = new MutationObserver(() => {
      try {
        if (pendingHighlightTimer) clearTimeout(pendingHighlightTimer);
        pendingHighlightTimer = setTimeout(() => {
          try { colors = discoverCollectionColors(); highlightInventory(colors); } catch (e) { log('debounced highlight error', e); }
          pendingHighlightTimer = null;
        }, 250);
      } catch (e) {}
    });
    observer.observe(document.body, { childList: true, subtree: true });

    // Try to insert the settings button once the inventory grid exists (or fallback immediately)
    // Also re-scan now that the grid is rendered so colors apply on first load
    try {
      await waitFor(() => document.querySelector('.Inventory_itemGrid__20YAH'), 10000, 250);
      colors = discoverCollectionColors();
      highlightInventory(colors);
    } catch (e) {}
    insertSettingsButton();
    hookCombatWS();
    try { hookUsageAnim(); } catch (e) {}

    // Re-insert the settings button if React ever removes it during a re-render.
    // Uses the existing body MutationObserver (observer) — piggyback a debounced check.
    let btnWatchTimer = null;
    const btnObserver = new MutationObserver(() => {
      try {
        if (btnWatchTimer) return; // already scheduled
        btnWatchTimer = setTimeout(() => {
          btnWatchTimer = null;
          try {
            if (!document.getElementById('mwi-settings-btn')) {
              log('Settings button missing — reinserting');
              insertSettingsButton();
            }
          } catch (e) {}
        }, 300);
      } catch (e) {}
    });
    btnObserver.observe(document.body, { childList: true, subtree: true });

    // expose for debugging / manual refresh
    window.MWI_InventoryHighlighter = {
      reScan: () => { try { if (pendingHighlightTimer) { clearTimeout(pendingHighlightTimer); pendingHighlightTimer = null; } colors = discoverCollectionColors(); highlightInventory(colors); } catch (e) { log('reScan error', e); } return colors; },
      highlightInventory: () => highlightInventory(colors),
      // runtime setter for glow spread (px) and re-run
      setGlowSpread: function(px) { try { cfg.glowSpread = Number(px) || cfg.glowSpread; highlightInventory(colors); saveSettings(); return cfg.glowSpread; } catch (e) { return null; } },
      setGlowBlur: function(px) { try { cfg.glowBlur = Number(px) || cfg.glowBlur; highlightInventory(colors); saveSettings(); return cfg.glowBlur; } catch (e) { return null; } },
      setGlowAlpha: function(a) { try { cfg.glowAlpha = Number(a); if (isNaN(cfg.glowAlpha)) cfg.glowAlpha = 0.08; highlightInventory(colors); saveSettings(); return cfg.glowAlpha; } catch (e) { return null; } },
      setBgAlpha: function(a) { try { cfg.bgAlpha = Number(a); if (isNaN(cfg.bgAlpha)) cfg.bgAlpha = 0.06; highlightInventory(colors); saveSettings(); return cfg.bgAlpha; } catch (e) { return null; } },
      // persistence helpers
      saveSettings: saveSettings,
      loadSettings: loadSettings,
      clearSettings: clearSettings,
      colors
    };

    log('MWI Inventory Highlighter ready. Use window.MWI_InventoryHighlighter.reScan() to refresh.');
  })();

})();