Map-Making Folders

Advanced tag organization and folder system for map-making.app

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Map-Making Folders
// @icon         https://img.icons8.com/?size=200&id=3509&format=png&color=ffffff
// @namespace    https://map-making.app/
// @version      7.6.2
// @description  Advanced tag organization and folder system for map-making.app
// @author       Youri Auski
// @match        https://map-making.app/*
// @grant        none
// @require      https://cdnjs.cloudflare.com/ajax/libs/lz-string/1.5.0/lz-string.min.js
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  const getMapId = () => { const m = location.pathname.match(/\/maps\/([^/?#]+)/); return m ? m[1] : null; };
  const SK = () => `mmapp_v6_${getMapId() || 'global'}`;

  let S = { folders: [], lastUpdate: 0 };
  let isDirty = false;

  const setDirty = (state) => {
    isDirty = state;
    const btn = document.getElementById('mm-folder-save-btn');
    if (btn) {
      if (state) {
        btn.classList.add('mm-btn-dirty');
        btn.innerHTML = '<span class="button__label">Save Folders</span>';
      } else {
        btn.classList.remove('mm-btn-dirty');
        btn.innerHTML = '<span class="button__label">Folders Saved</span>';
      }
    }
  };

  const loadS = () => {
    try {
      const r = localStorage.getItem(SK());
      if (r) S = { folders: [], lastUpdate: 0, ...JSON.parse(r) };
      setDirty(false);
    } catch (_) {}
  };

  const saveS = (markDirty = true) => {
    try {
      invalidateFlatCache();
      S.lastUpdate = Date.now();
      localStorage.setItem(SK(), JSON.stringify(S));
      if (markDirty) {
        setDirty(true);
      }
    } catch (_) {}
  };

  const waitDOM = (selector, root = document, timeout = 3000) => {
    return new Promise(resolve => {
      const existing = root.querySelector(selector);
      if (existing) return resolve(existing);
      const obs = new MutationObserver(() => {
        const el = root.querySelector(selector);
        if (el) { obs.disconnect(); resolve(el); }
      });
      obs.observe(root, { childList: true, subtree: true });
      setTimeout(() => { obs.disconnect(); resolve(null); }, timeout);
    });
  };

  const uid = () => Math.random().toString(36).slice(2, 8) + Math.random().toString(36).slice(2, 8);

  const hexToRgb = h => {
    const m = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(h);
    return m ? [parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16)] : [100, 100, 200];
  };

  const lerp = (h1, h2, t) => {
    const a = hexToRgb(h1), b = hexToRgb(h2);
    return `rgb(${Math.round(a[0] + (b[0] - a[0]) * t)},${Math.round(a[1] + (b[1] - a[1]) * t)},${Math.round(a[2] + (b[2] - a[2]) * t)})`;
  };

  const lum = c => {
    const v = (c.startsWith('#') ? hexToRgb(c) : c.match(/\d+/g)?.map(Number)) || [128, 128, 128];
    return (0.299 * v[0] + 0.587 * v[1] + 0.114 * v[2]) / 255;
  };

  const textOn = bg => lum(bg) > 0.45 ? '#111' : '#fff';

  const rgbToHex = rgb => {
    if (rgb.startsWith('#')) return rgb;
    const m = rgb.match(/\d+/g);
    if (!m || m.length < 3) return '#ffffff';
    return '#' + m.slice(0, 3).map(n => parseInt(n).toString(16).padStart(2, '0')).join('');
  };

  let _flatCache = null;
  let _flatCacheDirty = true;
  let _allTagsCache = null;

  const invalidateFlatCache = () => {
    _flatCacheDirty = true;
    _allTagsCache = null;
  };

  const _flatRaw = (l = S.folders) => l.flatMap(f => [f, ..._flatRaw(f.children || [])]);

  const flat = () => {
    if (_flatCacheDirty || !_flatCache) {
      _flatCache = _flatRaw(S.folders);
      _flatCacheDirty = false;
    }
    return _flatCache;
  };

  const getAssignedTags = () => {
    if (!_allTagsCache) _allTagsCache = new Set(flat().flatMap(f => f.tags || []));
    return _allTagsCache;
  };

  let recentPresets = [];
  try { recentPresets = JSON.parse(localStorage.getItem('mmapp_recent_presets') || '[]'); } catch (_) {}

  const saveRecentPreset = p => {
    recentPresets = recentPresets.filter(r => r.name !== p.name);
    recentPresets.unshift(p);
    if (recentPresets.length > 4) recentPresets.length = 4;
    localStorage.setItem('mmapp_recent_presets', JSON.stringify(recentPresets));
  };

  const GRADIENT_PRESETS = [
    // =====================================================================
    // REDS & FLAMES / ROUGES & FLAMMES (25)
    // =====================================================================
    { name: 'Volcanic Rouge Red',           colors: ['#1a0000', '#8b1a00', '#ff4500', '#ffaa00'] },
    { name: 'Ember Rouge Red',              colors: ['#2d0000', '#c0392b', '#e74c3c', '#f39c12'] },
    { name: 'Magma Flow Rouge Red',         colors: ['#0d0000', '#3d0000', '#9b2335', '#ff6b35', '#ffd700'] },
    { name: 'Inferno Rouge Red',            colors: ['#120000', '#4a0000', '#aa1100', '#ff4400', '#ffaa00', '#ffff00'] },
    { name: 'Phoenix Rouge Red',            colors: ['#1a0000', '#7d0000', '#c0392b', '#e67e22', '#f1c40f'] },
    { name: 'Campfire Rouge Red',           colors: ['#1a0a00', '#7a2800', '#e85d04', '#f4a261', '#ffcb77'] },
    { name: 'Red Giant Rouge Red',          colors: ['#100000', '#400000', '#a00000', '#ff4400', '#ffaa00'] },
    { name: 'Sunset Blaze Rouge Red',       colors: ['#1a0028', '#8b0050', '#cc2200', '#ff7700', '#ffdd00'] },
    { name: 'Hot Coals Rouge Red',          colors: ['#1c0a00', '#5c1a00', '#cc3300', '#ff7700'] },
    { name: 'Cinnabar Glow Rouge Red',      colors: ['#6a0010', '#e03020', '#f89060'] },
    { name: 'Dark Rust Rouge Red',          colors: ['#1a0800', '#600800', '#c02800'] },
    { name: 'Kiln Rouge Red',               colors: ['#200000', '#602000', '#d45500', '#f08020'] },
    { name: 'Crimson Tide Rouge Red',       colors: ['#180008', '#660020', '#cc0030', '#ff4060'] },
    { name: 'Forge Rouge Red',              colors: ['#0d0000', '#3a0a00', '#aa4400', '#dd7722', '#ffaa44'] },
    { name: 'Lava Lamp Rouge Red',          colors: ['#1a0028', '#ff0066', '#ff6600', '#ffcc00'] },
    { name: 'Ruby Depths Rouge Red',        colors: ['#0a0005', '#400010', '#900030', '#d00050', '#ff6080'] },
    { name: 'Pomegranate Rouge Red',        colors: ['#3a0010', '#880030', '#cc2040', '#e87880'] },
    { name: 'Blood Moon Rouge Red',         colors: ['#0a0000', '#3a0800', '#8a1a10', '#c84030', '#e88060'] },
    { name: 'Chili Rouge Red',              colors: ['#2a0000', '#7a1000', '#c03000', '#e06020', '#f0a060'] },
    { name: 'Molten Rouge Red',             colors: ['#0a0000', '#4a0800', '#c03000', '#f06000', '#ffb030'] },
    { name: 'Garnet Rouge Red',             colors: ['#300010', '#800030', '#c00050', '#e04080', '#f890b0'] },
    { name: 'Carnelian Rouge Red',          colors: ['#3a0808', '#9a2808', '#d05020', '#e88050', '#f8c0a0'] },
    { name: 'Rage Rouge Red',               colors: ['#1a0000', '#6a0000', '#cc0000', '#ff4000'] },
    { name: 'Scarlet Smoke Rouge Red',      colors: ['#2a0808', '#802020', '#c05040', '#e09080'] },
    { name: 'Fire & Ice Rouge Red Bleu Blue', colors: ['#cc0000', '#ff6600', '#ffffff', '#0066ff', '#0000cc'] },

    // =====================================================================
    // GREENS / VERTS (15)
    // =====================================================================
    { name: 'Jungle Canopy Vert Green',     colors: ['#0a1a00', '#1a4000', '#2d7020', '#60a840', '#a0d060'] },
    { name: 'Emerald Night Vert Green',     colors: ['#020a04', '#0a5828', '#30c878'] },
    { name: 'Spring Meadow Vert Green',     colors: ['#102800', '#2a6010', '#50a030', '#90d060', '#d0f890'] },
    { name: 'Fern Hollow Vert Green',       colors: ['#0a2010', '#1a5028', '#30a050', '#70d080', '#c0f0a0'] },
    { name: 'Pine Resin Vert Green',        colors: ['#0a1a08', '#1a3808', '#307020', '#60b040', '#a0d880'] },
    { name: 'Malachite Vert Green',         colors: ['#001a08', '#006030', '#00c860', '#40e888', '#a0ffc0'] },
    { name: 'Radioactive Vert Green',       colors: ['#001000', '#004000', '#00a000', '#80ff00', '#ccff80'] },
    { name: 'Acid Rain Vert Green',         colors: ['#001008', '#004020', '#00c060', '#80ff40', '#ccff00'] },
    { name: 'Cyber Green Vert Green',       colors: ['#001008', '#002a18', '#007040', '#00cc80', '#00ff88'] },
    { name: 'Circuit Vert Green',           colors: ['#001808', '#003818', '#006030', '#00a050', '#00e880'] },
    { name: 'Absinthe Vert Green',          colors: ['#0a1800', '#2a5000', '#60a000', '#a0d020', '#d0f060'] },
    { name: 'Sage Meadow Vert Green',       colors: ['#1a2808', '#3a5820', '#6a9040', '#9ab860', '#c8e090'] },
    { name: 'Dark Fern Vert Green',         colors: ['#0a180a', '#207020', '#50b050'] },
    { name: 'Forest Amber Vert Green Jaune Yellow', colors: ['#1a2008', '#385820', '#a89020'] },

    // =====================================================================
    // BLUES / BLEUS (10)
    // =====================================================================
    { name: 'Deep Dive Bleu Blue',          colors: ['#000510', '#001a40', '#003070', '#0060aa', '#00aacc'] },
    { name: 'Pacific Bleu Blue',            colors: ['#001020', '#003050', '#006688', '#00aabb', '#00ddee'] },
    { name: 'Wave Break Bleu Blue',         colors: ['#002040', '#004080', '#0080c0', '#40c0e0', '#c0f0ff'] },
    { name: 'Electric Blue Bleu Blue',      colors: ['#000520', '#000880', '#0040ff', '#00ccff', '#80ffff'] },
    { name: 'Cobalt Night Bleu Blue',       colors: ['#030510', '#081888', '#4070e8'] },
    { name: 'Lapis Bleu Blue',              colors: ['#000830', '#001870', '#0040c0', '#3070e0', '#7090ff'] },
    { name: 'Nordic Bleu Blue',             colors: ['#0a1820', '#183060', '#306098', '#5898d8', '#90c8f0'] },

    // =====================================================================
    // YELLOWS & GOLDS / JAUNES & ORS (15)
    // =====================================================================
    { name: 'Gold Jaune Yellow',            colors: ['#2a1800', '#8a5800', '#d0a000', '#f0d040', '#fff8c0'] },
    { name: 'Saffron Jaune Yellow',         colors: ['#3a1000', '#aa4000', '#e08000', '#f8c000', '#fff080'] },
    { name: 'Honey Jaune Yellow',           colors: ['#3a1800', '#9a5000', '#e09020', '#f8c840', '#fff8a0'] },
    { name: 'Summer Noon Jaune Yellow',     colors: ['#fff9c4', '#fff176', '#ffee58', '#fdd835', '#f9a825'] },
    { name: 'Sulfur Jaune Yellow',          colors: ['#1a1800', '#5a5000', '#c0a800', '#f0d800', '#ffffa0'] },
    { name: 'Pyrite Jaune Yellow',          colors: ['#2a2000', '#6a5010', '#c0a020', '#e8c840', '#f8f080'] },
    { name: 'Butterscotch Jaune Yellow',    colors: ['#3a1808', '#a05818', '#d09038', '#f0c860', '#fff8c0'] },
    { name: 'Caramel Jaune Yellow Marron Brown', colors: ['#2a1000', '#8a4010', '#d08030', '#e8c070', '#f8f0c0'] },
    { name: 'Sunbaked Jaune Yellow',        colors: ['#4a2800', '#9a5010', '#d08030', '#f0b060', '#f8e0a0'] },
    { name: 'Deco Gold Jaune Yellow',       colors: ['#1a1200', '#5a4200', '#c09000', '#e8c840', '#f8f0c0'] },
    { name: 'Harvest Moon Jaune Yellow',    colors: ['#1a0800', '#7a3000', '#c07020', '#e0a840', '#f8d880'] },
    { name: 'Topaz Jaune Yellow',           colors: ['#201000', '#703000', '#d08000', '#f0c040', '#fff0a0'] },
    { name: 'Smoked Gold Jaune Yellow',     colors: ['#3a3020', '#9a8830', '#e8d060'] },
    { name: 'Brass Jaune Yellow Marron Brown', colors: ['#a08020', '#c8a840', '#e8c870'] },
    { name: 'Solar Flare Jaune Yellow Rouge Red', colors: ['#ff0000', '#ff6600', '#ffcc00'] },

    // =====================================================================
    // PURPLES & VIOLETS / VIOLETS (15)
    // =====================================================================
    { name: 'Amethyst Violet Purple',       colors: ['#1a0028', '#4a0080', '#8800cc', '#cc66ff', '#f0ccff'] },
    { name: 'Nebula Violet Purple',         colors: ['#0a0020', '#300060', '#6000c0', '#c040e0', '#ff80ff'] },
    { name: 'Vaporwave Violet Purple',      colors: ['#1a0040', '#6600cc', '#cc00aa', '#ff0066', '#ff6600'] },
    { name: 'Retrowave Violet Purple',      colors: ['#080018', '#200050', '#800080', '#ff00ff', '#ff8800'] },
    { name: 'Quasar Violet Purple',         colors: ['#080010', '#280040', '#7800c0', '#c040ff', '#ff80dd'] },
    { name: 'Andromeda Violet Purple',      colors: ['#080018', '#180040', '#400088', '#8800cc', '#dd66ff'] },
    { name: 'Synthwave Violet Purple',      colors: ['#0a0020', '#2a0060', '#cc00ff', '#ff0099', '#ff6600'] },
    { name: 'Plasma Violet Purple',         colors: ['#080018', '#4000a0', '#c000ff', '#ff00cc', '#ffaa00'] },
    { name: 'Orchid Dusk Violet Purple',    colors: ['#1a1028', '#8838b8', '#e888e0'] },
    { name: 'Deep Wisteria Violet Purple',  colors: ['#1a0838', '#7850b0', '#d0b0e8'] },
    { name: 'Night Bloom Violet Purple',    colors: ['#0a0818', '#380858', '#a840c8', '#f090f0'] },
    { name: 'Berry Smoke Violet Purple',    colors: ['#280830', '#8a2870', '#e060c0'] },
    { name: 'Dusk Periwinkle Violet Purple', colors: ['#2a2848', '#8088d0', '#d0d8f8'] },
    { name: 'Plum Amber Violet Purple Jaune Yellow', colors: ['#2a0830', '#7a2860', '#e09030'] },
    { name: 'Ink Bloom Violet Purple',      colors: ['#0a0818', '#4a2060', '#c870d0'] },

    // =====================================================================
    // PINKS & ROSES / ROSES (10)
    // =====================================================================
    { name: 'Cherry Blossom Rose Pink',     colors: ['#fce4ec', '#f8bbd0', '#f48fb1', '#f06292', '#ec407a'] },
    { name: 'Cotton Candy Rose Pink',       colors: ['#ffb3c6', '#ffc6e0', '#e8c0f8', '#c0d0ff', '#b8f0ff'] },
    { name: 'Sakura Rose Pink',             colors: ['#fae0e8', '#f8c8d8', '#f0a8c0', '#e888a8', '#e07090'] },
    { name: 'Hot Pink Rose Pink',           colors: ['#200010', '#800050', '#ff0080', '#ff88cc', '#ffc0ff'] },
    { name: 'Raspberry Rose Pink',          colors: ['#2a0018', '#880040', '#d80068', '#f840a0', '#ff90cc'] },
    { name: 'Bubblegum Rose Pink',          colors: ['#ff99bb', '#ff99ee', '#cc99ff', '#99bbff', '#99ffee'] },
    { name: 'Hibiscus Rose Pink',           colors: ['#3a0018', '#a01840', '#e84880', '#f890b8', '#ffd0e0'] },
    { name: 'Rose Quartz Rose Pink',        colors: ['#3a1428', '#a04870', '#e080a8', '#f8c0d0', '#fff0f5'] },
    { name: 'Dreamsicle Rose Pink Orange',  colors: ['#ffe0c0', '#ffcc99', '#ffaa88', '#ff8899', '#ff66bb'] },
    { name: 'Blush Marble Rose Pink',       colors: ['#e8c8c8', '#f8e8e0', '#fff8f5'] },

    // =====================================================================
    // ORANGES (10)
    // =====================================================================
    { name: 'Terracotta Orange',            colors: ['#7a2810', '#c05030', '#e08050', '#f0b090', '#f8d8c0'] },
    { name: 'Mesa Orange',                  colors: ['#2a1000', '#7a3810', '#c07030', '#e0b060', '#f0d8a0'] },
    { name: 'Indian Summer Orange',         colors: ['#2a1000', '#8a4000', '#e07818', '#f8b840', '#fff8c0'] },
    { name: 'Jasper Orange',                colors: ['#8a3818', '#c86830', '#f0b888'] },
    { name: 'Tangerine Smoke Orange',       colors: ['#2a1a10', '#e08820'] },
    { name: 'Mango Orange',                 colors: ['#3a1800', '#c06010', '#f09030', '#f8c860', '#fff0b0'] },
    { name: 'Rust Belt Orange',             colors: ['#2a1200', '#8b3a00', '#c46a1a', '#e8a030'] },
    { name: 'Bronze Orange Marron Brown',   colors: ['#1a0800', '#6a3000', '#b07020', '#e0b050', '#f8e090'] },

    // =====================================================================
    // TEALS & CYANS / CYANS (12)
    // =====================================================================
    { name: 'Aurora Borealis Cyan',         colors: ['#000a20', '#003030', '#00aa88', '#00ff80', '#88ffcc'] },
    { name: 'Bioluminescent Cyan',          colors: ['#000820', '#001540', '#003366', '#00aaff', '#00ffcc'] },
    { name: 'Tide Pool Cyan',               colors: ['#0a1a2a', '#1a4060', '#2a8080', '#40c0b0', '#a0f0e0'] },
    { name: 'Blue Lagoon Cyan',             colors: ['#001a30', '#007070', '#00c0a0', '#80f0e0'] },
    { name: 'Sea Glass Cyan',               colors: ['#2a5050', '#50a090', '#80d0c0', '#c0f0e8'] },
    { name: 'Tidal Surge Cyan',             colors: ['#000810', '#002050', '#0060b0', '#20b0f0', '#a0ffff'] },
    { name: 'Peacock Ore Cyan',             colors: ['#001010', '#003838', '#006868', '#40a8c0', '#80e8ff'] },
    { name: 'Ion Storm Cyan',               colors: ['#000820', '#002060', '#0080ff', '#00ffcc', '#80ffff'] },
    { name: 'Tidal Pool Cyan',              colors: ['#0a2020', '#2a7878', '#80d8d0', '#d0f8f8'] },
    { name: 'Tourmaline Cyan Vert Green',   colors: ['#001820', '#005840', '#00a860', '#60e880', '#ccffcc'] },
    { name: 'Laser Grid Cyan',              colors: ['#000810', '#001840', '#00a0ff', '#00ffaa'] },

    // =====================================================================
    // BROWNS & EARTH TONES / MARRONS & TERRES (13)
    // =====================================================================
    { name: 'Espresso Marron Brown',        colors: ['#0a0500', '#2a1008', '#5a2810', '#8a5020', '#c09040'] },
    { name: 'Mocha Marron Brown',           colors: ['#1a0808', '#4a2010', '#8a4820', '#c08050', '#e8b890'] },
    { name: 'Sepia Marron Brown',           colors: ['#1a1000', '#5a4010', '#a08030', '#d0b870', '#f0e8c0'] },
    { name: 'Canyon Marron Brown',          colors: ['#3a1000', '#8a3010', '#c06020', '#e0a060', '#f0d0a0'] },
    { name: 'Redwood Marron Brown',         colors: ['#1a0800', '#5a1800', '#a04020', '#c07848', '#d8b090'] },
    { name: 'Ochre Marron Brown Jaune Yellow', colors: ['#3a2800', '#8a6010', '#d0a020', '#f0d050', '#f8f0c0'] },
    { name: 'Adobe Marron Brown',           colors: ['#6a3018', '#b07050', '#d0a880', '#e8d0b8'] },
    { name: 'Walnut Sage Marron Brown Vert Green', colors: ['#3a2010', '#6a4a20', '#90a870'] },
    { name: 'Cognac Marron Brown',          colors: ['#3a1000', '#8a3a10', '#d07030', '#f0c080'] },
    { name: 'Mahogany Marron Brown',        colors: ['#2a0808', '#6a1818', '#c06848', '#e8c0a0'] },
    { name: 'Tiger Eye Marron Brown Jaune Yellow', colors: ['#3a1800', '#8a4800', '#d08020', '#e8b860', '#f8e0a0'] },
    { name: 'Smoked Amber Marron Brown Jaune Yellow', colors: ['#3a3020', '#b09030', '#e8d060'] },
    { name: 'Western Marron Brown',         colors: ['#3a1a08', '#8a5020', '#c08840', '#e0b870', '#f0ddb0'] },

    // =====================================================================
    // MIXED DUOS / MÉLANGES INÉDITS
    // =====================================================================
    { name: 'Copper Teal Marron Brown Cyan', colors: ['#5a2800', '#a06030', '#408080', '#40c0c0'] },
    { name: 'Mocha Blue Marron Brown Bleu Blue', colors: ['#3a1808', '#8a5030', '#2050a0', '#4090e0'] },
    { name: 'Earth Sky Marron Brown Bleu Blue', colors: ['#6a3810', '#c09040', '#408888', '#80c8e0'] },
    { name: 'Bark Violet Marron Brown Violet Purple', colors: ['#5a3020', '#a07050', '#7040a0', '#c080e0'] },
    { name: 'Rust Cyan Orange Cyan',        colors: ['#9a2800', '#e06020', '#20a0a0', '#60f0e0'] },
    { name: 'Coffee Mint Marron Brown Vert Green', colors: ['#3a1808', '#8a5020', '#20a060', '#80e0b0'] },
    { name: 'Umber Teal Marron Brown Cyan', colors: ['#4a2808', '#9a6030', '#1a7878', '#40c0b8'] },
    { name: 'Bronze Cerulean Marron Brown Bleu Blue', colors: ['#6a3000', '#b07020', '#2060c0', '#60a0ff'] },
    { name: 'Caramel Azure Jaune Yellow Bleu Blue', colors: ['#c07820', '#e0b040', '#2080c0', '#80c8ff'] },
    { name: 'Clay Indigo Marron Brown Violet Purple', colors: ['#8a4020', '#d08050', '#2020a0', '#6060e0'] },
    { name: 'Coral Turquoise Rose Pink Cyan', colors: ['#e05050', '#f09080', '#20b0b0', '#60f0e0'] },
    { name: 'Gold Violet Jaune Yellow Violet Purple', colors: ['#c09000', '#e8c030', '#6020a0', '#c060e0'] },
    { name: 'Jade Amber Vert Green Jaune Yellow', colors: ['#1a7850', '#40c888', '#c89020', '#f0d040'] },
    { name: 'Navy Saffron Bleu Blue Jaune Yellow', colors: ['#0a1848', '#2858c8', '#e0a000', '#f8d840'] },
    { name: 'Magenta Lime Rose Pink Vert Green', colors: ['#c00060', '#f040a0', '#60c000', '#b0f000'] },
    { name: 'Aqua Peach Cyan Orange',       colors: ['#0a9090', '#40e0d0', '#e08050', '#f8c090'] },
    { name: 'Midnight Gold Noir Black Jaune Yellow', colors: ['#0a0808', '#1a1018', '#c08000', '#f0d040'] },
    { name: 'Green Burgundy Vert Green Rouge Red', colors: ['#1a4820', '#40a850', '#7a0820', '#c04060'] },
    { name: 'Arctic Ember Bleu Blue Rouge Red', colors: ['#c8e8f8', '#80c0e8', '#e85020', '#ff8040'] },
    { name: 'Forest Crimson Vert Green Rouge Red', colors: ['#0a2808', '#308030', '#880020', '#d04060'] },
    { name: 'Slate Lime Gris Gray Vert Green', colors: ['#3a4050', '#606880', '#a0c820', '#d0f050'] },
    { name: 'Charcoal Teal Gris Gray Cyan', colors: ['#1a2020', '#404848', '#10a090', '#40e0d0'] },
    { name: 'Plum Gold Violet Purple Jaune Yellow', colors: ['#2a0840', '#7820a0', '#c09000', '#f0c830'] },
    { name: 'Smoke Coral Gris Gray Orange', colors: ['#504850', '#909090', '#e07050', '#f8b090'] },
    { name: 'Ice Magma Bleu Blue Rouge Red', colors: ['#a0d0f8', '#4090e8', '#0020a0', '#d04000', '#ff8000'] },

    // =====================================================================
    // GALAXIES & COSMOS (10)
    // =====================================================================
    { name: 'Pulsar Violet Purple Bleu Blue', colors: ['#000820', '#001840', '#0040a0', '#00aaff', '#80ffff'] },
    { name: 'Stardust Violet Purple',       colors: ['#080010', '#200030', '#580060', '#9040a0', '#e0a0e0'] },
    { name: 'Binary Star Bleu Blue Jaune Yellow', colors: ['#000820', '#00204a', '#0080c0', '#ff8800', '#ffdd00'] },
    { name: 'Event Horizon Violet Purple',  colors: ['#000000', '#100020', '#300060', '#700090', '#aa00cc'] },
    { name: 'Dark Matter Violet Purple',    colors: ['#050005', '#100020', '#200040', '#400080'] },
    { name: 'Solar Wind Violet Purple',     colors: ['#0a0818', '#2a1040', '#6040c0', '#c0a0ff', '#ffd0ff'] },
    { name: 'Cosmic Dust Violet Purple',    colors: ['#0a0820', '#180840', '#380868', '#680890', '#a838b8'] },
    { name: 'Zodiac Violet Purple Rouge Red', colors: ['#0a0010', '#200040', '#4000a0', '#9000d0', '#f000f0', '#ff6600'] },
    { name: 'Supernova Bleu Blue',          colors: ['#000820', '#002040', '#0060a0', '#40b0ff', '#ffffff'] },
    { name: 'Wormhole Violet Purple',       colors: ['#000000', '#1a0030', '#5000a0', '#b040ff', '#ffffff'] },

    // =====================================================================
    // NEON & CYBERPUNK (8)
    // =====================================================================
    { name: 'Neon City Violet Purple Rouge Red', colors: ['#080010', '#2000a0', '#8000ff', '#ff00aa', '#ff6600'] },
    { name: 'Hologram Bleu Blue Violet Purple', colors: ['#001840', '#00a0ff', '#00ffee', '#ff00ee', '#ff8800'] },
    { name: 'Glitch Vert Green Violet Purple', colors: ['#000808', '#003000', '#00ff00', '#ff00ff', '#00ffff'] },
    { name: 'UV Violet Purple',             colors: ['#0a0020', '#3000a0', '#8000ff', '#cc80ff', '#ffffff'] },
    { name: 'Laser Cut Bleu Blue Cyan',     colors: ['#000010', '#0000a0', '#0080ff', '#00ffcc', '#ffffff'] },
    { name: 'Power Grid Bleu Blue Cyan',    colors: ['#000008', '#000040', '#001080', '#00a0ff', '#ccffff'] },
    { name: 'Night Market Violet Purple Rose Pink', colors: ['#080818', '#201040', '#602080', '#c060c0', '#f0a0e0'] },
    { name: 'Phase Shift Violet Purple Cyan', colors: ['#0a0028', '#4000c0', '#cc00ff', '#00ccff', '#ccffff'] },

    // =====================================================================
    // PASTELS & SOFT / PASTELS & DOUX (7)
    // =====================================================================
    { name: 'Macaron Rose Pink Vert Green', colors: ['#f9d5e5', '#fce4c5', '#d4f5e9', '#cfe4f7', '#e9d5f0'] },
    { name: 'Baby Blues Bleu Blue',         colors: ['#e0eeff', '#c8d8ff', '#a8c0ff', '#88a8ff', '#6888ff'] },
    { name: 'Sherbet Jaune Yellow Rose Pink', colors: ['#fff0c0', '#ffe0a0', '#ffc0a0', '#ffa0c0', '#e0a0e0'] },
    { name: 'Cloud Drift Bleu Blue',        colors: ['#f0f8ff', '#e0f0ff', '#d0e8ff', '#c0d8f8', '#b0c8f0'] },
    { name: 'Spearmint Cream Vert Green',   colors: ['#e0fff0', '#d0f8e8', '#c0f0e0', '#b0e8d8', '#a0d8d0'] },
    { name: 'Lilac Mist Violet Purple',     colors: ['#ece0ff', '#f0d8ff', '#f8d0ff', '#ffd0f0', '#ffe0e8'] },
    { name: 'Morning Mist Vert Green Bleu Blue', colors: ['#f8f0ff', '#e8e8ff', '#d8f0ff', '#c8f8f0', '#d0ffd8'] },

    // =====================================================================
    // EARTH & DESERTS / TERRES & DÉSERTS (7)
    // =====================================================================
    { name: 'Sahara Marron Brown Jaune Yellow', colors: ['#2a1800', '#8a5010', '#d09030', '#f0d070', '#f8f0d0'] },
    { name: 'Red Rock Rouge Red Marron Brown', colors: ['#2a0a00', '#6a2000', '#b05020', '#d09060', '#e8c8a0'] },
    { name: 'Dust Storm Marron Brown',      colors: ['#4a3828', '#9a8060', '#d0b888', '#f0e0c8'] },
    { name: 'Gobi Jaune Yellow Marron Brown', colors: ['#3a3020', '#7a7040', '#b8b070', '#e0d8a0', '#f8f5e0'] },
    { name: 'Flint Gris Gray',              colors: ['#2a2a28', '#585850', '#888880', '#b8b8b0', '#e0e0d8'] },
    { name: 'Petrified Marron Brown',       colors: ['#3a3028', '#7a6850', '#b0a080', '#d8c8a8', '#f0e8d0'] },
    { name: 'Outback Orange Marron Brown',  colors: ['#3a1800', '#9a5010', '#d09030', '#e8c070', '#f8e8c0'] },

    // =====================================================================
    // JEWELS & MINERALS / JOYAUX & MINÉRAUX (19)
    // =====================================================================
    { name: 'Sapphire Bleu Blue',           colors: ['#000820', '#001068', '#0040c0', '#4080ff', '#80b8ff'] },
    { name: 'Emerald Vert Green',           colors: ['#000e08', '#003020', '#007040', '#00c070', '#60e8a0'] },
    { name: 'Ruby Rouge Red',               colors: ['#180000', '#600010', '#cc0030', '#ff4060', '#ff99aa'] },
    { name: 'Opal Multicolore Multicolor',  colors: ['#f0f8ff', '#d0e8ff', '#e0d0ff', '#ffd0e8', '#d0ffe8'] },
    { name: 'Onyx Noir Black',              colors: ['#000000', '#0a0808', '#151010', '#201818', '#2a2020'] },
    { name: 'Alexandrite Violet Purple Vert Green', colors: ['#1a0028', '#6000a0', '#c040e0', '#40d0a0', '#00f0a0'] },
    { name: 'Rhodonite Rose Pink',          colors: ['#2a1020', '#7a3050', '#c05880', '#e898b8', '#f8d0e0'] },
    { name: 'Jade Vert Green',              colors: ['#0a1a10', '#1a5030', '#40a060', '#80cc90', '#c0f0c0'] },
    { name: 'Labradorite Bleu Blue Gris Gray', colors: ['#181820', '#303848', '#506888', '#7098c0', '#a0c8e8'] },
    { name: 'Selenite Blanc White',         colors: ['#d8d0e8', '#e0d8f0', '#e8e0f8', '#f0e8ff', '#f8f4ff'] },
    { name: 'Azurite Bleu Blue',            colors: ['#000a20', '#001848', '#003090', '#2060d0', '#60a0ff'] },
    { name: 'Sodalite Bleu Blue Violet Purple', colors: ['#050820', '#101840', '#201870', '#3030a8', '#5858e0'] },
    { name: 'Agate Rose Pink Gris Gray',    colors: ['#3a1828', '#8a3858', '#c07090', '#e8a8c0', '#f8d8e8'] },
    { name: 'Carnelian Orange Rouge Red',   colors: ['#3a0808', '#9a2808', '#d05020', '#e88050', '#f8c0a0'] },
    { name: 'Cinnabar Rouge Red',           colors: ['#2a0800', '#8a1800', '#cc3000', '#f06040', '#f8b0a0'] },

    // =====================================================================
    // SEASONS & NATURE / SAISONS & NATURE (10)
    // =====================================================================
    { name: 'Autumn Canopy Orange',         colors: ['#1a0800', '#6a2000', '#c06010', '#e0a020', '#f0d060'] },
    { name: 'Winter Frost Bleu Blue',       colors: ['#e8f4ff', '#d0e8ff', '#b8d8ff', '#a0c8ff', '#88b8ff'] },
    { name: 'Spring Thaw Vert Green',       colors: ['#d0e8d0', '#b0d8b0', '#80c880', '#60b880', '#40a870'] },
    { name: 'Late Summer Jaune Yellow Rouge Red', colors: ['#f8e070', '#f0b040', '#e88020', '#e06040', '#c84060'] },
    { name: 'Golden Hour Jaune Yellow Orange', colors: ['#1a0800', '#8a3000', '#e07010', '#f8a030', '#fff0a0'] },
    { name: 'Blue Hour Bleu Blue Violet Purple', colors: ['#0a0820', '#181840', '#302878', '#5050a8', '#8080d0'] },
    { name: 'May Day Vert Green Jaune Yellow', colors: ['#a0e860', '#c8f880', '#f0ff80', '#f8e860', '#f8c840'] },
    { name: 'Deep Winter Bleu Blue',        colors: ['#0a1020', '#182040', '#283060', '#404880', '#6068a0'] },
    { name: 'Birch Grove Marron Brown Vert Green', colors: ['#c8c0a0', '#e0d8b8', '#f0f0e0', '#d0e8c8', '#a0c888'] },
    { name: 'Rainforest Mist Vert Green',   colors: ['#0a1808', '#206030', '#50a060', '#a0d898', '#d0f8c8'] },

    // =====================================================================
    // METALLICS / MÉTALLIQUES (13)
    // =====================================================================
    { name: 'Rose Gold Rose Pink Marron Brown', colors: ['#2a0810', '#8a2840', '#d07070', '#e8a898', '#f8d8d8'] },
    { name: 'Gunmetal Gris Gray Bleu Blue', colors: ['#101518', '#202830', '#304050', '#406070', '#507888'] },
    { name: 'Iridescent Multicolore Multicolor', colors: ['#0040a0', '#00a080', '#80ff80', '#ffd040', '#ff0080'] },
    { name: 'Patina Vert Green Marron Brown', colors: ['#2a4028', '#508060', '#80b890', '#b0d8b8', '#d8f0d8'] },
    { name: 'Burnished Jaune Yellow Marron Brown', colors: ['#2a1800', '#8a5000', '#d09820', '#f0cc60', '#fff8c0'] },
    { name: 'Bismuth Violet Purple Gris Gray', colors: ['#3a3038', '#8070a0', '#c0a8e0', '#f0d8ff', '#ffffff'] },
    { name: 'Pewter Gris Gray',             colors: ['#303838', '#506068', '#708888', '#98b0b0', '#c0d0d0'] },
    { name: 'Chrome Gris Gray',             colors: ['#202020', '#505050', '#909090', '#c8c8c8', '#ffffff'] },
    { name: 'Anodized Bleu Blue',           colors: ['#000838', '#0010a0', '#0090ff', '#80d0ff', '#c0f0ff'] },
    { name: 'Gilded Jaune Yellow Noir Black', colors: ['#080400', '#3a2000', '#9a6000', '#e0a820', '#f8f080'] },
    { name: 'Platinum Gris Gray',           colors: ['#484848', '#787878', '#a8a8a8', '#d0d0d0', '#f0f0f0'] },
    { name: 'Silver Gris Gray',             colors: ['#383838', '#686868', '#989898', '#c8c8c8', '#f0f0f0'] },
    { name: 'Copper Orange Marron Brown',   colors: ['#2a0a00', '#8a3800', '#d07030', '#e8a860', '#f8d0a0'] },

    // =====================================================================
    // MOOD & EMOTION / HUMEUR & ÉMOTION (10)
    // =====================================================================
    { name: 'Serenity Bleu Blue',           colors: ['#e0f0ff', '#c0d8f8', '#a0c0f0', '#80a8e0', '#6090d0'] },
    { name: 'Nostalgia Marron Brown',       colors: ['#a08060', '#c0a878', '#d8c898', '#f0e8c0', '#f8f0d8'] },
    { name: 'Melancholy Bleu Blue Gris Gray', colors: ['#101828', '#283848', '#405870', '#608898', '#90b8c0'] },
    { name: 'Euphoria Violet Purple',       colors: ['#1a0040', '#6000c0', '#cc00ff', '#ff66ff', '#ffccff'] },
    { name: 'Wonder Bleu Blue',             colors: ['#000828', '#002060', '#0060d0', '#40a8ff', '#c0e8ff'] },
    { name: 'Triumph Rouge Red Jaune Yellow', colors: ['#200000', '#800000', '#ff0000', '#ff8800', '#ffff00'] },
    { name: 'Passion Rouge Red',            colors: ['#1a0000', '#6a0010', '#cc0030', '#ff3060', '#ff80a0'] },
    { name: 'Mystery Violet Purple Noir Black', colors: ['#050010', '#150030', '#350060', '#650090', '#9500c0'] },
    { name: 'Joy Multicolore Multicolor',   colors: ['#ff8800', '#ffcc00', '#88ff00', '#00ffcc', '#0088ff'] },
    { name: 'Hope Vert Green Bleu Blue',    colors: ['#c0d8f8', '#a0c0f0', '#80e8c8', '#a0f8a0', '#d0f8d0'] },

    // =====================================================================
    // VINTAGE & RETRO (9)
    // =====================================================================
    { name: '70s Harvest Orange',           colors: ['#5a2800', '#c86810', '#e0a030', '#f8d870', '#fff8e0'] },
    { name: 'Kodachrome Rouge Red Vert Green', colors: ['#9a3020', '#e06830', '#f8a840', '#f8e870', '#a0c8a0'] },
    { name: 'Disco Violet Purple Rouge Red', colors: ['#1a0030', '#6600cc', '#cc0066', '#ff6600', '#ffcc00'] },
    { name: 'Paisley Violet Purple',        colors: ['#2a0840', '#7820a0', '#d050c0', '#f098c0', '#f8d8e8'] },
    { name: 'Daguerreotype Gris Gray',      colors: ['#181818', '#484840', '#787860', '#a8a888', '#d8d8c8'] },
    { name: 'Psychedelic Multicolore Multicolor', colors: ['#1a0040', '#8800ff', '#ff0088', '#ff8800', '#88ff00'] },
    { name: 'Faded Marron Brown Gris Gray', colors: ['#a09888', '#b8b0a0', '#d0c8b8', '#e8e0d0', '#f5f0e8'] },
    { name: 'Art Deco Jaune Yellow Noir Black', colors: ['#0a0800', '#4a3800', '#c8a800', '#f8e840', '#fff8e0'] },

    // =====================================================================
    // INDUSTRIAL & URBAN / INDUSTRIEL & URBAIN (7)
    // =====================================================================
    { name: 'Concrete Gris Gray',           colors: ['#404040', '#606060', '#808080', '#a0a0a0', '#c0c0c0'] },
    { name: 'Asphalt Gris Gray Noir Black', colors: ['#101010', '#202020', '#383838', '#585858', '#787878'] },
    { name: 'Neon Sign Violet Purple Rouge Red', colors: ['#0a0010', '#3000a0', '#aa00ff', '#ff00aa', '#ff8800'] },
    { name: 'Subway Bleu Blue Gris Gray',   colors: ['#101820', '#203040', '#305060', '#408090', '#70a8c0'] },
    { name: 'Oxide Orange Marron Brown',    colors: ['#2a1008', '#7a3810', '#c07030', '#e0b060', '#f0d8b0'] },
    { name: 'Titanium Gris Gray',           colors: ['#383838', '#585858', '#808080', '#a8a8a8', '#d0d0d0'] },
    { name: 'Foundry Orange Noir Black',    colors: ['#100800', '#402000', '#aa5000', '#e09030', '#f8d080'] },

    // =====================================================================
    // WEATHER & ATMOSPHERE / MÉTÉO & ATMOSPHÈRE (9)
    // =====================================================================
    { name: 'Thunderhead Gris Gray Bleu Blue', colors: ['#101820', '#283848', '#406080', '#6088a8', '#a0c0d8'] },
    { name: 'Lightning Bleu Blue Blanc White', colors: ['#050510', '#101030', '#2020a0', '#6060ff', '#ffffff'] },
    { name: 'Rainbow Multicolore Multicolor', colors: ['#ff0000', '#ff8800', '#ffff00', '#00ff00', '#0088ff', '#8800ff'] },
    { name: 'Fog Bank Gris Gray',           colors: ['#202020', '#484848', '#787878', '#a8a8a8', '#d8d8d8'] },
    { name: 'Clear Sky Bleu Blue',          colors: ['#e8f4ff', '#d0e8ff', '#b0d0ff', '#80b0ff', '#5090ff'] },
    { name: 'El Nino Bleu Blue Jaune Yellow', colors: ['#0a2030', '#2060a0', '#50a0d0', '#90d0f0', '#f0f890'] },
    { name: 'Trade Wind Bleu Blue Cyan',    colors: ['#003058', '#006090', '#30a0c8', '#70d0e8', '#c0f0ff'] },
    { name: 'Permafrost Bleu Blue Blanc White', colors: ['#b0d0f0', '#c8e0f8', '#d8ecff', '#e8f4ff', '#f4faff'] },
    { name: 'Sirocco Jaune Yellow Marron Brown', colors: ['#2a2010', '#6a5818', '#b09830', '#d8c060', '#f0e8b0'] },

    // =====================================================================
    // CELESTIAL & SPIRITUAL / CÉLESTE & SPIRITUEL (8)
    // =====================================================================
    { name: 'Mandala Multicolore Multicolor', colors: ['#c00020', '#d06000', '#f0b800', '#60c020', '#0080c0', '#6000c0'] },
    { name: 'Chakra Multicolore Multicolor', colors: ['#cc0000', '#ff8800', '#ffcc00', '#00cc44', '#0044cc', '#6600cc'] },
    { name: 'Solstice Jaune Yellow Rouge Red', colors: ['#1a0800', '#8a4000', '#f0a000', '#f8e040', '#ffffff'] },
    { name: 'Equinox Vert Green',           colors: ['#002010', '#008040', '#00e870', '#80ffb0', '#ffffff'] },
    { name: 'Ether Violet Purple Blanc White', colors: ['#f8f0ff', '#f0e8ff', '#e8e0ff', '#e0d8ff', '#d8d0f8'] },
    { name: 'Astral Violet Purple',         colors: ['#080018', '#2000a0', '#8040ff', '#d080ff', '#ffffff'] },
    { name: 'Transcend Violet Purple Blanc White', colors: ['#1a0028', '#5000aa', '#d000ff', '#ff80ff', '#ffffff'] },

    // =====================================================================
    // CULTURAL & GEOGRAPHIC / CULTUREL & GÉOGRAPHIQUE (7)
    // =====================================================================
    { name: 'Kyoto Orange Marron Brown',    colors: ['#1a0800', '#8a3020', '#d08050', '#f0c090', '#f8e8d0'] },
    { name: 'Marrakech Rouge Red Jaune Yellow', colors: ['#3a0808', '#b84020', '#e89040', '#f8c870', '#f8f0c0'] },
    { name: 'Santorini Bleu Blue Blanc White', colors: ['#0a1848', '#2050a8', '#80b0e8', '#c0d8f8', '#ffffff'] },
    { name: 'Patagonia Bleu Blue Gris Gray', colors: ['#1a2830', '#305870', '#5090b0', '#90c0d8', '#d0e8f0'] },
    { name: 'Amazon Vert Green',            colors: ['#0a1800', '#1a5010', '#30a030', '#70d070', '#b0f8b0'] },
    { name: 'Fjord Bleu Blue',              colors: ['#0a1828', '#1a4060', '#305090', '#4888c8', '#90c0e8'] },
    { name: 'Bengal Rouge Red Orange',      colors: ['#1a0808', '#8a2820', '#d06038', '#f0a870', '#f8d8c0'] },

    // =====================================================================
    // CREATIVE EXTRAS / CRÉATIFS SUPPLÉMENTAIRES
    // =====================================================================
    { name: 'Glasswork Multicolore Multicolor', colors: ['#e0f0ff', '#f0e0ff', '#ffe0f0', '#fff0e0', '#e0ffe0'] },
    { name: 'Entropy Gris Gray Rose Pink',  colors: ['#f8f8f8', '#c0a0a0', '#a0a0c0', '#a0c0a0', '#808080'] },
    { name: 'Umbra Noir Black Gris Gray',   colors: ['#000000', '#080808', '#202020', '#484848', '#808080'] },
    { name: 'Prismatic Multicolore Multicolor', colors: ['#ff0044', '#ff8800', '#ffff00', '#00ff88', '#0088ff', '#8800ff'] },
    { name: 'Anti-Gravity Violet Purple Cyan', colors: ['#0a0018', '#180060', '#4800c0', '#c040ff', '#60ffff'] },
    { name: 'Drift Bleu Blue Blanc White',  colors: ['#c0d8f0', '#d0e8f8', '#e0f4ff', '#f0faff', '#f8fdff'] },
    { name: 'Tectonic Marron Brown Gris Gray', colors: ['#1a1010', '#504030', '#908060', '#c8c0a0', '#e8e8d8'] },
    { name: 'Black Hole Noir Black Violet Purple', colors: ['#000000', '#0a0005', '#200015', '#500030', '#aa0050'] },
    { name: 'Fracture Bleu Blue Gris Gray', colors: ['#101010', '#404060', '#6060a0', '#9090e0', '#e0e0ff'] },
    { name: 'Soldering Iron Jaune Yellow Orange', colors: ['#1a0800', '#6a2000', '#d06010', '#f0b040', '#ffe880'] },
    { name: 'Ink Wash Noir Black Gris Gray', colors: ['#101010', '#303030', '#606060', '#909090', '#c0c0c0'] },
    { name: 'Fresco Marron Brown Bleu Blue', colors: ['#8a7060', '#b8a888', '#d8c8a8', '#e8d8c0', '#f5eedd'] },
    { name: 'Watercolor Bleu Blue Rose Pink', colors: ['#a0c0e8', '#c0d8f0', '#d0e8f8', '#e8d0f0', '#f8c0d8'] },
    { name: 'Stained Glass Multicolore Multicolor', colors: ['#1a0028', '#4400aa', '#0066cc', '#00aa66', '#ccaa00', '#cc2200'] },
    { name: 'Pop Art Rose Pink Bleu Blue',  colors: ['#ff0088', '#ff8800', '#ffff00', '#00ff88', '#0088ff'] },
    { name: 'Minimalist Gris Gray',         colors: ['#f0f0f0', '#e0e0e0', '#c0c0c0', '#a0a0a0'] },
    { name: 'Cobalt Coral Bleu Blue Orange', colors: ['#0a1848', '#2050c0', '#f09080'] },
    { name: 'Coral Reef Bleu Blue Orange',  colors: ['#001830', '#00405a', '#007080', '#50b090', '#ff8844'] },
    { name: 'Highland Moor Marron Brown Gris Gray', colors: ['#201a10', '#504030', '#887858', '#b8a880', '#d8c8a8'] },
    { name: 'Savanna Jaune Yellow Marron Brown', colors: ['#2a2000', '#7a5a10', '#c09030', '#e0c060', '#f0e8a0'] },
    { name: 'Moss Stone Vert Green Gris Gray', colors: ['#1a1a10', '#3a4020', '#606840', '#90a060', '#c0c890'] },
    { name: 'Old Growth Vert Green',        colors: ['#0a1000', '#1a3000', '#304010', '#506020', '#788040'] },
    { name: 'Undergrowth Vert Green',       colors: ['#080e00', '#182808', '#305020', '#507840', '#80a860'] },
    { name: 'Wetland Vert Green Marron Brown', colors: ['#1a2808', '#385820', '#607040', '#90a068', '#c0c898'] },
    { name: 'Tundra Gris Gray Bleu Blue',   colors: ['#c8d8e8', '#a8c0d8', '#88a8c8', '#6888a8', '#486880'] },
    { name: 'Sahel Jaune Yellow Marron Brown', colors: ['#3a2800', '#8a7010', '#d0b040', '#e8d880', '#f8f5d0'] },
    { name: 'Medina Marron Brown Jaune Yellow', colors: ['#2a1808', '#8a5828', '#c0a060', '#e0d0a0', '#f8f0d8'] },
    { name: 'Orinoco Vert Green',           colors: ['#0a1208', '#205030', '#408860', '#70b890', '#b0e8c8'] },
    { name: 'Steppes Vert Green Jaune Yellow', colors: ['#3a3818', '#6a6828', '#a0a040', '#d0d070', '#f0f0a8'] },
    { name: 'Sanctuary Jaune Yellow Marron Brown', colors: ['#1a1810', '#4a4028', '#8a8040', '#c0b870', '#e8e0b0'] },
    { name: 'Sacred Flame Rouge Red Jaune Yellow', colors: ['#1a0000', '#aa2200', '#ff6600', '#ffcc00', '#ffffff'] },
    { name: 'Zenith Bleu Blue Blanc White', colors: ['#000810', '#002060', '#0080c0', '#40c0ff', '#ffffff'] },
    { name: 'Meridian Cyan Blanc White',    colors: ['#002030', '#008888', '#00e8e8', '#80ffff', '#ffffff'] },
    { name: 'Nirvana Bleu Blue Blanc White', colors: ['#d8f0ff', '#e0f0ff', '#e8f4ff', '#f0f8ff', '#f8fcff'] },
    { name: 'Rothko Rouge Red Orange',      colors: ['#3a0808', '#c02020', '#e86020', '#f0a040'] },
    { name: 'Bauhaus Multicolore Multicolor', colors: ['#cc0000', '#0000cc', '#ffcc00', '#2a2a2a'] },
    { name: 'Expressionist Rouge Red Jaune Yellow', colors: ['#1a0808', '#8a2020', '#e06030', '#f8b060', '#f8e0a0'] },
    { name: 'Impressionist Multicolore Multicolor', colors: ['#d0c890', '#a8c8b8', '#88a8d8', '#b888a8', '#d8a888'] },
    { name: 'Polaroid Gris Gray Blanc White', colors: ['#c0c0b8', '#d8d8d0', '#e8e8e0', '#f0f0e8', '#f8f8f8'] },
    { name: 'Victorian Violet Purple Gris Gray', colors: ['#1a0818', '#5a2850', '#9a6890', '#c8a0c0', '#e8d0e0'] },
    { name: 'Nouveau Vert Green Jaune Yellow', colors: ['#182808', '#4a6020', '#80a050', '#c0c888', '#e8e8c0'] },
    { name: 'Rose Gold Jade Rose Pink Vert Green', colors: ['#d07070', '#e8a898', '#408860', '#80c890'] },
    { name: 'Navy Amber Bleu Blue Jaune Yellow', colors: ['#0a1848', '#1a3a80', '#c89030', '#f8d060'] },
    { name: 'Indigo Coral Violet Purple Orange', colors: ['#1a0840', '#5030a0', '#e07060', '#f8b090'] },
    { name: 'Teal Wine Cyan Rouge Red',     colors: ['#1a4040', '#20a090', '#800830', '#c04060'] },
    { name: 'Mint Flame Vert Green Rouge Red', colors: ['#208868', '#60d0a8', '#d04010', '#f07040'] },
    { name: 'Slate Peach Gris Gray Orange', colors: ['#3a4858', '#6888a8', '#e8a870', '#f8d0a8'] },
    { name: 'Lilac Gold Violet Purple Jaune Yellow', colors: ['#8850b8', '#c090e0', '#c09020', '#f0d860'] },
    { name: 'Forest Sunset Vert Green Rouge Red', colors: ['#1a4010', '#407030', '#e07020', '#f8c060'] },

    // =====================================================================
    // NEW / NOUVEAUX — ROUGES & FEUX (20)
    // =====================================================================
    { name: 'Dragon Scale Rouge Red',       colors: ['#1a0000', '#5a0000', '#c00020', '#ff2020', '#ff9040'] },
    { name: 'Wildfire Rouge Red Orange',    colors: ['#200000', '#881000', '#e04000', '#ff8000', '#ffdd00'] },
    { name: 'Lava Crust Rouge Red',         colors: ['#0a0000', '#300000', '#800000', '#cc3300', '#ff6620'] },
    { name: 'Burning Rose Rouge Red Rose Pink', colors: ['#400010', '#a00030', '#e02060', '#f87090', '#ffb8cc'] },
    { name: 'Cherry Coal Rouge Red Noir Black', colors: ['#100000', '#400000', '#8a1020', '#cc3040'] },
    { name: 'Flamethrower Rouge Red Jaune Yellow', colors: ['#1a0000', '#7a1000', '#dd4000', '#ff8800', '#ffe000'] },
    { name: 'Coral Sunset Rouge Red Orange', colors: ['#3a0010', '#aa2820', '#e06040', '#f0a060', '#f8e0b0'] },
    { name: 'Magenta Blaze Rouge Red Violet Purple', colors: ['#200010', '#800040', '#e00080', '#ff40b0', '#ffaae0'] },
    { name: 'War Paint Rouge Red',          colors: ['#120000', '#5a0808', '#a82020', '#d85030'] },
    { name: 'Red Velvet Rouge Red',         colors: ['#1a0008', '#6a0018', '#b81030', '#e06070', '#f8b8c0'] },
    { name: 'Iron Heat Rouge Red Orange',   colors: ['#100000', '#3a0800', '#8a2800', '#d06000', '#f8c000'] },
    { name: 'Habanero Rouge Red Orange',    colors: ['#2a0000', '#8a1000', '#d03000', '#f07000', '#f8a030'] },
    { name: 'Caldera Rouge Red',            colors: ['#080000', '#280000', '#700000', '#c82000', '#ff6000'] },
    { name: 'Rose Flame Rouge Red Rose Pink', colors: ['#2a0018', '#8a0040', '#d03060', '#f06090', '#ffb0c0'] },
    { name: 'Lychee Rouge Red Rose Pink',   colors: ['#3a0010', '#880040', '#d06070', '#f0a0a8', '#f8d8d8'] },
    { name: 'Ember Cave Rouge Red Marron Brown', colors: ['#100400', '#402010', '#8a4020', '#c07040', '#e0a870'] },
    { name: 'Cerise Smoke Rouge Red',       colors: ['#1a0008', '#700028', '#c03050', '#e07080'] },
    { name: 'Torch Rouge Red Jaune Yellow', colors: ['#1a0000', '#600000', '#cc2000', '#f06000', '#ffb800'] },
    { name: 'Sunset Embers Rouge Red Orange Jaune Yellow', colors: ['#0a0000', '#4a0800', '#b04000', '#e08000', '#f8cc00', '#fffff0'] },
    { name: 'Red Shift Rouge Red Violet Purple', colors: ['#050010', '#280040', '#7a0088', '#cc0044', '#ff4000'] },

    // =====================================================================
    // NEW / NOUVEAUX — VERTS (20)
    // =====================================================================
    { name: 'Swamp Vert Green Marron Brown', colors: ['#081008', '#203818', '#406030', '#608850', '#90b870'] },
    { name: 'Chlorophyll Vert Green',       colors: ['#002800', '#007000', '#30c030', '#90f060', '#e0ffa0'] },
    { name: 'Lime Sherbet Vert Green Jaune Yellow', colors: ['#e0ff90', '#c8f870', '#a8f050', '#80e030', '#60c010'] },
    { name: 'Neon Moss Vert Green',         colors: ['#001000', '#003000', '#008800', '#40dd00', '#aaff40'] },
    { name: 'Seagrass Vert Green Cyan',     colors: ['#001808', '#005030', '#00a860', '#50d890', '#b0f8c0'] },
    { name: 'Canopy Glow Vert Green Jaune Yellow', colors: ['#0a1800', '#2a5010', '#50a020', '#90d040', '#d0f890'] },
    { name: 'Poison Ivy Vert Green',        colors: ['#001800', '#005020', '#20a040', '#70e070', '#c8ffc0'] },
    { name: 'Tea Garden Vert Green',        colors: ['#283818', '#4a6828', '#70a040', '#a8c878', '#d8eeb0'] },
    { name: 'Glacier Mint Vert Green Cyan', colors: ['#b0f0d8', '#90e0c8', '#70d0b8', '#50c0a8', '#30b098'] },
    { name: 'Pesto Vert Green Jaune Yellow', colors: ['#283010', '#506028', '#809040', '#b0c068', '#d8e0a0'] },
    { name: 'Green Flash Vert Green',       colors: ['#002000', '#006020', '#00c850', '#60ff90', '#ccffe0'] },
    { name: 'Phytoplankton Vert Green Cyan', colors: ['#002020', '#006050', '#00c888', '#60ffc0', '#ccffee'] },
    { name: 'Cactus Vert Green',            colors: ['#1a2810', '#3a5820', '#609038', '#90c060', '#c8e8a0'] },
    { name: 'Irish Mist Vert Green',        colors: ['#102008', '#306030', '#60a058', '#98c880', '#d0f0b8'] },
    { name: 'Verdigris Vert Green Cyan',    colors: ['#102820', '#308060', '#50c098', '#90e8c8', '#d0fff0'] },
    { name: 'Mangrove Vert Green Marron Brown', colors: ['#101808', '#304028', '#507840', '#80a860', '#b8d8a0'] },
    { name: 'Mojito Vert Green Jaune Yellow', colors: ['#203810', '#508028', '#80c040', '#b8e868', '#e8ffb0'] },
    { name: 'Velvet Moss Vert Green',       colors: ['#0a1200', '#203808', '#407020', '#68a840', '#a0d870'] },

    // =====================================================================
    // NEW / NOUVEAUX — BLEUS (20)
    // =====================================================================
    { name: 'Midnight Ocean Bleu Blue',     colors: ['#000308', '#000818', '#001838', '#003060', '#0060a0'] },
    { name: 'Steel Blue Bleu Blue Gris Gray', colors: ['#101828', '#203858', '#406898', '#6090c0', '#a0c0e8'] },
    { name: 'Arctic Deep Bleu Blue',        colors: ['#000820', '#002050', '#003880', '#006bbf', '#40a0e0'] },
    { name: 'Blue Morpho Bleu Blue',        colors: ['#000520', '#001060', '#0040c0', '#2090f8', '#80d0ff'] },
    { name: 'Ocean Trench Bleu Blue',       colors: ['#000005', '#000015', '#000838', '#001868', '#0038a0'] },
    { name: 'Cerulean Bleu Blue',           colors: ['#001838', '#004888', '#0098e0', '#50c8f8', '#b0eaff'] },
    { name: 'Denim Bleu Blue',              colors: ['#0a1828', '#204060', '#306898', '#5090c0', '#88b8e0'] },
    { name: 'Hydrothermal Bleu Blue Cyan',  colors: ['#000818', '#003060', '#0068b0', '#00b8d0', '#80f0ff'] },
    { name: 'Blueprint Bleu Blue',          colors: ['#000820', '#001848', '#002870', '#00409a', '#3080d0'] },
    { name: 'Ink River Bleu Blue Violet Purple', colors: ['#050010', '#100838', '#280870', '#4820a8', '#8060e0'] },
    { name: 'Frostbite Bleu Blue Blanc White', colors: ['#c8e8ff', '#d8f0ff', '#e8f8ff', '#f0fcff', '#ffffff'] },
    { name: 'Periwinkle Bleu Blue Violet Purple', colors: ['#181848', '#404898', '#6870c8', '#98a8e0', '#d0d8f8'] },
    { name: 'Twilight Sea Bleu Blue Violet Purple', colors: ['#080828', '#181858', '#302898', '#5050c8', '#8890e8'] },
    { name: 'Selene Bleu Blue',             colors: ['#0a0a20', '#182050', '#304898', '#6080d0', '#b0c8f0'] },
    { name: 'Icefield Bleu Blue Blanc White', colors: ['#b8d8f8', '#c8e4ff', '#d8eeff', '#e8f4ff', '#f4f9ff'] },
    { name: 'Neptunian Bleu Blue',          colors: ['#000818', '#001048', '#002890', '#1060c8', '#50a0f0'] },
    { name: 'Blue Flame Bleu Blue Cyan',    colors: ['#000020', '#000880', '#0040ff', '#00c8ff', '#80ffff'] },
    { name: 'Bay Fog Bleu Blue Gris Gray',  colors: ['#a8b8c8', '#c0d0e0', '#d8e8f0', '#e8f0f8', '#f4f8fc'] },
    { name: 'Tidewater Bleu Blue',          colors: ['#001828', '#004060', '#007898', '#30b0d0', '#80d8f0'] },
    { name: 'Starlight Bleu Blue Blanc White', colors: ['#080818', '#182040', '#3840a0', '#7080e0', '#d0d8ff'] },

    // =====================================================================
    // NEW / NOUVEAUX — JAUNES & ORS (20)
    // =====================================================================
    { name: 'Sunflower Jaune Yellow',       colors: ['#3a2000', '#9a6010', '#e0b020', '#f8e040', '#fffbc0'] },
    { name: 'Champagne Jaune Yellow Blanc White', colors: ['#e8d8a0', '#f0e4b8', '#f8f0d8', '#fcf8f0', '#fffff8'] },
    { name: 'Lemon Drop Jaune Yellow',      colors: ['#fffce0', '#fff8c0', '#fff090', '#ffe060', '#ffc820'] },
    { name: 'Wheat Field Jaune Yellow Marron Brown', colors: ['#3a2808', '#8a6818', '#d0a030', '#f0c860', '#f8f0d0'] },
    { name: 'Neon Lemon Jaune Yellow Vert Green', colors: ['#e0ff00', '#c8f000', '#a8e000', '#80c800', '#58a000'] },
    { name: 'Antiqued Gold Jaune Yellow Marron Brown', colors: ['#2a1800', '#6a4808', '#b08830', '#d8c068', '#f0e8b8'] },
    { name: 'Electric Lime Jaune Yellow Vert Green', colors: ['#003000', '#409000', '#a0f000', '#d8ff40', '#f8ffa0'] },
    { name: 'Old Gold Jaune Yellow',        colors: ['#2a2000', '#7a6000', '#c8a820', '#e8d060', '#f8f0c0'] },
    { name: 'Canary Jaune Yellow',          colors: ['#f8f080', '#f8e850', '#f8d820', '#e8c000', '#c8a000'] },
    { name: 'Chartreuse Jaune Yellow Vert Green', colors: ['#303800', '#608000', '#a0c000', '#d0f020', '#e8ff80'] },
    { name: 'Coin Jaune Yellow',            colors: ['#3a2800', '#8a6010', '#d0a820', '#f0d840', '#f8f8d0'] },
    { name: 'Midas Touch Jaune Yellow',     colors: ['#1a0800', '#7a4800', '#d08000', '#f0c020', '#fff8a0'] },
    { name: 'Amber Wave Jaune Yellow Orange', colors: ['#2a1000', '#8a4000', '#d08020', '#f0b840', '#f8e8c0'] },
    { name: 'Vanilla Jaune Yellow Blanc White', colors: ['#f8f0d0', '#f8ecc0', '#f8e8b0', '#f4e098', '#ecd880'] },
    { name: 'Solar Noon Jaune Yellow',      colors: ['#fffff0', '#ffff80', '#ffe820', '#ffc000', '#ff9800'] },
    { name: 'Pollen Jaune Yellow Vert Green', colors: ['#d0e880', '#e0f060', '#f0f840', '#f8f020', '#f8e000'] },
    { name: 'Gilded Rose Jaune Yellow Rose Pink', colors: ['#4a2018', '#a05828', '#e0b860', '#f8d888', '#f8f0c8'] },
    { name: 'Dandelion Jaune Yellow',       colors: ['#f8e870', '#f8d040', '#f8b810', '#e8a000', '#c07000'] },
    { name: 'Vintage Ivory Jaune Yellow Blanc White', colors: ['#f8f4e8', '#f8f0d8', '#f0e8c0', '#e8d8a0', '#d8c880'] },
    { name: 'Harvest Gold Jaune Yellow Orange', colors: ['#2a1400', '#7a4000', '#c08020', '#e8b040', '#f8e090'] },

    // =====================================================================
    // NEW / NOUVEAUX — VIOLETS (20)
    // =====================================================================
    { name: 'Ultraviolet Violet Purple',    colors: ['#050018', '#180060', '#5000c8', '#b000ff', '#e860ff'] },
    { name: 'Mauve Violet Purple Rose Pink', colors: ['#281030', '#703860', '#b07898', '#d8a8c0', '#f0d8e8'] },
    { name: 'Grape Crush Violet Purple',    colors: ['#1a0028', '#600080', '#a040c0', '#d080e0', '#f0c0f8'] },
    { name: 'Twilight Violet Purple Bleu Blue', colors: ['#080028', '#201060', '#502098', '#8048d0', '#c090f0'] },
    { name: 'Elderberry Violet Purple',     colors: ['#150018', '#480048', '#800060', '#c03080', '#e888b8'] },
    { name: 'Dark Orchid Violet Purple',    colors: ['#0a0018', '#300058', '#7800aa', '#c040e0', '#f090ff'] },
    { name: 'Pastel Lavender Violet Purple Blanc White', colors: ['#e0d8f8', '#e8e0ff', '#f0e8ff', '#f8f0ff', '#ffffff'] },
    { name: 'Velvet Violet Purple',         colors: ['#0a0010', '#2a0048', '#6800a8', '#b040e0', '#e880ff'] },
    { name: 'Heliotrope Violet Purple Rose Pink', colors: ['#280030', '#800080', '#d040d0', '#f880f0', '#ffc0ff'] },
    { name: 'Byzantine Violet Purple',      colors: ['#180028', '#500068', '#9000c0', '#d040e8', '#f890ff'] },
    { name: 'Iris Violet Purple Bleu Blue', colors: ['#180030', '#480080', '#7030c0', '#9868e0', '#c0a0f8'] },
    { name: 'Neon Grape Violet Purple',     colors: ['#100020', '#400080', '#9000ff', '#cc60ff', '#ee80ff'] },
    { name: 'Prune Violet Purple',          colors: ['#180018', '#500038', '#880050', '#c06880', '#e8a8b8'] },
    { name: 'Purple Dusk Violet Purple Bleu Blue', colors: ['#080020', '#200858', '#500898', '#8840c8', '#c090e8'] },
    { name: 'Morpho Violet Purple Bleu Blue', colors: ['#080018', '#200058', '#4818a8', '#8060d8', '#c0b0ff'] },
    { name: 'Aubergine Violet Purple',      colors: ['#0a0008', '#300018', '#600038', '#9a4060', '#c88090'] },
    { name: 'Lilac Dream Violet Purple Rose Pink', colors: ['#281838', '#785898', '#c0a0d8', '#e8d0f0', '#f8f0ff'] },
    { name: 'Frozen Violet Violet Purple Bleu Blue', colors: ['#d0d0f8', '#c0c8ff', '#b0c0ff', '#a0b8ff', '#90a8f8'] },

    // =====================================================================
    // NEW / NOUVEAUX — ROSES & PÊCHES (20)
    // =====================================================================
    { name: 'Neon Pink Rose Pink',          colors: ['#200010', '#900050', '#ff00a0', '#ff70d0', '#ffb0e8'] },
    { name: 'Flamingo Rose Pink',           colors: ['#e87878', '#f0a0a0', '#f8c8c0', '#f8e0d8', '#fff0f0'] },
    { name: 'Strawberry Cream Rose Pink',   colors: ['#400020', '#c03060', '#e87898', '#f8b8c0', '#fff0f5'] },
    { name: 'Deep Magenta Rose Pink Violet Purple', colors: ['#1a0010', '#700050', '#c00090', '#e850c0', '#f8a0e0'] },
    { name: 'Blush Peach Rose Pink Orange', colors: ['#f0c8b8', '#f8d8c8', '#f8e4d8', '#f8eee8', '#fff8f5'] },
    { name: 'Peony Rose Pink',              colors: ['#2a0018', '#8a2848', '#d05878', '#f090a8', '#f8d0d8'] },
    { name: 'Dusty Rose Rose Pink',         colors: ['#3a1828', '#8a5060', '#c08898', '#e0b8c0', '#f8e0e8'] },
    { name: 'Nectarine Rose Pink Orange',   colors: ['#e08060', '#f0a070', '#f8c888', '#f8e0a0', '#fff8d0'] },
    { name: 'Electric Rose Rose Pink',      colors: ['#1a0018', '#700060', '#d000a0', '#ff50d0', '#ffa8f0'] },
    { name: 'Cosmo Rose Pink Violet Purple', colors: ['#200020', '#800060', '#d040a0', '#f080d0', '#f8c8f0'] },
    { name: 'Guava Rose Pink Orange',       colors: ['#3a0010', '#a83040', '#e07060', '#f8a888', '#f8d8c8'] },
    { name: 'Ballet Rose Pink',             colors: ['#e8c8d0', '#f0d8e0', '#f8e8f0', '#f8f0f8', '#ffffff'] },
    { name: 'Sunset Rose Rose Pink Orange', colors: ['#2a0010', '#901040', '#e06060', '#f0a080', '#f8e0c0'] },
    { name: 'Powder Rose Pink Blanc White', colors: ['#f0d8e8', '#f8e4f0', '#f8ecf4', '#fef4f8', '#ffffff'] },
    { name: 'Crimson Rose Rouge Red Rose Pink', colors: ['#1a0010', '#700030', '#c02050', '#f06080', '#f8b0c0'] },
    { name: 'Sorbet Rose Pink',             colors: ['#ff99bb', '#ffaacc', '#ffbbdd', '#ffccee', '#ffeeff'] },
    { name: 'Tutti Frutti Rose Pink Multicolore', colors: ['#ff6699', '#ff9966', '#ffcc66', '#ccff66', '#66ccff'] },
    { name: 'Rose Gold Shimmer Rose Pink Jaune Yellow', colors: ['#8a3848', '#c07888', '#e0c0a8', '#f8e0c0', '#f8f4e8'] },
    { name: 'Petal Rose Pink',              colors: ['#f8e8f0', '#f0d0e4', '#e8b8d8', '#d898c0', '#c070a0'] },
    { name: 'Taffy Rose Pink Bleu Blue',    colors: ['#ff99cc', '#ee88ff', '#9988ff', '#88ccff', '#99ffee'] },

    // =====================================================================
    // NEW / NOUVEAUX — ORANGES & AMBERS (20)
    // =====================================================================
    { name: 'Papaya Orange',                colors: ['#3a1400', '#a04800', '#e08020', '#f8b840', '#f8e8a0'] },
    { name: 'Pumpkin Orange',               colors: ['#2a1000', '#8a3800', '#d06818', '#f0a040', '#f8d898'] },
    { name: 'Amber Resin Orange Marron Brown', colors: ['#2a1000', '#8a5020', '#d09030', '#f0c870', '#f8f0d8'] },
    { name: 'Saffron Dusk Orange Jaune Yellow', colors: ['#3a1800', '#a04800', '#e08800', '#f8c820', '#fff880'] },
    { name: 'Apricot Orange Rose Pink',     colors: ['#3a1800', '#a05030', '#e09060', '#f8c090', '#f8e0c8'] },
    { name: 'Sienna Orange Marron Brown',   colors: ['#2a0a00', '#8a3010', '#c06030', '#e09060', '#f8c8a8'] },
    { name: 'Peach Fuzz Orange Rose Pink',  colors: ['#f8d0a0', '#f8e0b8', '#f8e8d0', '#f8f0e8', '#fff8f4'] },
    { name: 'Clementine Orange',            colors: ['#3a0000', '#c03000', '#f07000', '#f8a820', '#f8e080'] },
    { name: 'Sherbet Orange Rose Pink',     colors: ['#f08040', '#f8a868', '#f8c898', '#f8d8b8', '#f8e8d8'] },
    { name: 'Hot Sauce Orange Rouge Red',   colors: ['#2a0000', '#880800', '#d04000', '#f07820', '#f8b850'] },
    { name: 'Marmalade Orange Jaune Yellow', colors: ['#3a0800', '#b04010', '#e08030', '#f8b840', '#f8e880'] },
    { name: 'Amber Alert Orange',           colors: ['#2a0800', '#8a3800', '#e08000', '#f8b020', '#f8f860'] },
    { name: 'Cider Orange Marron Brown',    colors: ['#2a1000', '#7a3808', '#c07828', '#e0a850', '#f0d898'] },
    { name: 'Marigold Orange Jaune Yellow', colors: ['#3a1800', '#a85808', '#e09818', '#f8c840', '#f8f0a0'] },
    { name: 'Tiger Lily Orange',            colors: ['#2a0800', '#9a2800', '#e06020', '#f89050', '#f8c8a8'] },
    { name: 'Neon Orange Orange',           colors: ['#1a0800', '#700000', '#dd4000', '#ff8000', '#ffcc00'] },
    { name: 'Persimmon Orange Rouge Red',   colors: ['#3a0800', '#a02810', '#d85828', '#f09060', '#f8c8a8'] },
    { name: 'Caramel Macchiato Orange Marron Brown', colors: ['#2a1000', '#7a4020', '#c08040', '#e8b870', '#f8e4c0'] },

    // =====================================================================
    // NEW / NOUVEAUX — CYANS & TEALS (20)
    // =====================================================================
    { name: 'Deep Teal Cyan',               colors: ['#001818', '#004848', '#008888', '#30c0b8', '#a0f0e8'] },
    { name: 'Electric Cyan Cyan',           colors: ['#001018', '#003050', '#0080c0', '#00e8ff', '#a0ffff'] },
    { name: 'Mermaid Cyan Vert Green',      colors: ['#001818', '#006050', '#00b888', '#50e8b8', '#b0fff0'] },
    { name: 'Neon Aqua Cyan',               colors: ['#000808', '#002828', '#009090', '#00f0e8', '#80fff8'] },
    { name: 'Marine Phosphor Cyan Bleu Blue', colors: ['#000818', '#002850', '#0060a0', '#20c0e0', '#a0fff8'] },
    { name: 'Submarine Cyan Bleu Blue',     colors: ['#001018', '#003038', '#006070', '#20a0b0', '#70d8e8'] },
    { name: 'Abalone Cyan Multicolore',     colors: ['#b0d8d0', '#b8d0e0', '#c8c8f0', '#d8c0f0', '#e8d0f0'] },
    { name: 'Patina Teal Cyan Vert Green',  colors: ['#102820', '#305040', '#508868', '#80b898', '#b8e0c8'] },
    { name: 'Hummingbird Cyan Vert Green',  colors: ['#002818', '#008060', '#20c0a0', '#80e8d0', '#d0fff8'] },
    { name: 'Caspian Cyan Bleu Blue',       colors: ['#001828', '#005070', '#0090b0', '#40c0d8', '#a0eef8'] },
    { name: 'Spearmint Cyan Vert Green',    colors: ['#003820', '#008050', '#30c888', '#90f8c0', '#d0fff0'] },
    { name: 'Glaucous Cyan Bleu Blue',      colors: ['#183848', '#307088', '#50a8c0', '#80d0e0', '#c0f0f8'] },
    { name: 'Night Aqua Cyan Noir Black',   colors: ['#001010', '#003030', '#006868', '#00c8c8', '#80ffff'] },
    { name: 'Celeste Cyan Blanc White',     colors: ['#b0e8f0', '#c8f0f8', '#d8f8ff', '#eafeff', '#f8ffff'] },
    { name: 'Lagoon Cyan Vert Green',       colors: ['#001820', '#006070', '#20a8a0', '#70d8d0', '#c0fff8'] },
    { name: 'Arcadia Cyan Vert Green',      colors: ['#002010', '#007858', '#30d0a8', '#90f0d8', '#d8fff8'] },
    { name: 'Brine Cyan',                   colors: ['#001018', '#003040', '#006070', '#40a8b8', '#80d8e8'] },
    { name: 'Aquamarine Cyan Vert Green',   colors: ['#002820', '#008070', '#30c8b0', '#80f0e0', '#d0fffc'] },

    // =====================================================================
    // NEW / NOUVEAUX — GRAYS & BLACKS (15)
    // =====================================================================
    { name: 'Graphite Gris Gray',           colors: ['#101010', '#282828', '#484848', '#707070', '#a0a0a0'] },
    { name: 'Storm Cloud Gris Gray Bleu Blue', colors: ['#181828', '#303848', '#506070', '#7898b0', '#a8c8d8'] },
    { name: 'Ash Gris Gray',                colors: ['#202018', '#484838', '#707058', '#989870', '#b8b898'] },
    { name: 'Volcanic Ash Gris Gray',       colors: ['#101010', '#282820', '#484840', '#686858', '#888870'] },
    { name: 'Slate Gris Gray Bleu Blue',    colors: ['#1a2028', '#303848', '#507078', '#7090a0', '#a0b8c0'] },
    { name: 'Moonlight Gris Gray',          colors: ['#282838', '#484858', '#686880', '#9898b0', '#c8c8d8'] },
    { name: 'Flint Spark Gris Gray Jaune Yellow', colors: ['#202020', '#484840', '#909080', '#d0d0c0', '#f8f8d8'] },
    { name: 'Matte Black Noir Black',       colors: ['#0a0a0a', '#141414', '#1e1e1e', '#282828', '#323232'] },
    { name: 'Steel Gris Gray',              colors: ['#202028', '#383840', '#585868', '#808090', '#b0b0c0'] },

    // =====================================================================
    // NEW / NOUVEAUX — WHITES & CREAMS (10)
    // =====================================================================
    { name: 'Pearl Blanc White',            colors: ['#f0f0f8', '#f4f4fc', '#f8f8ff', '#fcfcff', '#ffffff'] },
    { name: 'Cream Blanc White Jaune Yellow', colors: ['#f8f4e0', '#f8f0d8', '#f8ecc8', '#f0e8b8', '#e8e0a8'] },
    { name: 'Parchment Blanc White Marron Brown', colors: ['#f0e8d0', '#e8e0c8', '#e0d8b8', '#d8d0a8', '#d0c898'] },
    { name: 'Ivory Blanc White',            colors: ['#f8f4e8', '#f4f0e0', '#f0ecd8', '#ece8d0', '#e4e0c8'] },
    { name: 'Cloud Blanc White Bleu Blue',  colors: ['#f4f8ff', '#e8f0ff', '#dceaff', '#d0e4ff', '#c4deff'] },
    { name: 'Egret Blanc White Gris Gray',  colors: ['#f8f8f4', '#f0f0ec', '#e8e8e0', '#e0e0d8', '#d8d8d0'] },
    { name: 'Magnolia Blanc White Rose Pink', colors: ['#f8f4f8', '#f4ecf4', '#f0e4f0', '#e8dce8', '#e0d4e0'] },
    { name: 'Frost Blanc White Cyan',       colors: ['#f0f8ff', '#e4f4ff', '#d8f0ff', '#ccecff', '#c0e8ff'] },

    // =====================================================================
    // NEW / NOUVEAUX — COSMIC & SCI-FI (20)
    // =====================================================================
    { name: 'Nebula Core Violet Purple Rose Pink', colors: ['#080018', '#300060', '#8000d0', '#e040e0', '#ff90f0'] },
    { name: 'Cryogenic Bleu Blue Cyan',     colors: ['#000820', '#002060', '#008888', '#40e0e0', '#c0ffff'] },
    { name: 'Xenon Cyan Violet Purple',     colors: ['#000020', '#001060', '#2000c0', '#6000ff', '#00ffff'] },
    { name: 'Plasma Core Violet Purple Jaune Yellow', colors: ['#050010', '#2000a0', '#8800ff', '#ff8800', '#ffff00'] },
    { name: 'Quantum Foam Bleu Blue Violet Purple', colors: ['#000818', '#082070', '#3050b8', '#8090e0', '#e0e0ff'] },
    { name: 'Antimatter Violet Purple Noir Black', colors: ['#000000', '#100020', '#400080', '#9000d0', '#e040ff'] },
    { name: 'Neutron Star Blanc White Bleu Blue', colors: ['#ffffff', '#d0e8ff', '#a0c0ff', '#6088ff', '#2040ff'] },
    { name: 'Parsec Bleu Blue Violet Purple', colors: ['#000010', '#000840', '#0018a0', '#4030e0', '#c080ff'] },
    { name: 'Stellar Nursery Rose Pink Violet Purple', colors: ['#180038', '#600080', '#c040c0', '#f070d0', '#ffc0f0'] },
    { name: 'Hawking Radiation Jaune Yellow Blanc White', colors: ['#1a0800', '#884800', '#f0c000', '#f8f060', '#ffffff'] },
    { name: 'Singularity Violet Purple Noir Black', colors: ['#000000', '#050005', '#150015', '#350035', '#700070'] },
    { name: 'Chromosphere Rouge Red Jaune Yellow', colors: ['#100000', '#4a0000', '#b02000', '#f06000', '#ffe000'] },
    { name: 'Interstellar Bleu Blue',       colors: ['#000008', '#000020', '#001060', '#0040c0', '#20a0ff'] },
    { name: 'Pulsar Wind Cyan Violet Purple', colors: ['#000010', '#001050', '#0080c0', '#c000ff', '#ff80ff'] },
    { name: 'Red Dwarf Rouge Red Marron Brown', colors: ['#0a0000', '#380a00', '#882000', '#c06020', '#e0a070'] },
    { name: 'Cosmic Ray Cyan Blanc White',  colors: ['#001020', '#0040a0', '#00c0f0', '#80f0ff', '#ffffff'] },
    { name: 'Magnetar Violet Purple Cyan',  colors: ['#050015', '#180058', '#5010b0', '#0080ff', '#00ffcc'] },
    { name: 'Space Station Gris Gray Bleu Blue', colors: ['#101828', '#283848', '#406080', '#6090b0', '#90c0d8'] },
    { name: 'Ion Drive Bleu Blue Cyan',     colors: ['#000020', '#002080', '#00a0ff', '#80f0ff', '#ffffff'] },
    { name: 'Dark Nebula Violet Purple',    colors: ['#030005', '#100018', '#280038', '#580068', '#9808a8'] },

    // =====================================================================
    // NEW / NOUVEAUX — NATURE & TERROIR (20)
    // =====================================================================
    { name: 'Volcanic Soil Marron Brown Noir Black', colors: ['#0a0800', '#281808', '#503020', '#885040', '#b08870'] },
    { name: 'Basalt Gris Gray Noir Black',  colors: ['#080808', '#181810', '#282818', '#404030', '#585848'] },
    { name: 'Prairie Wind Vert Green Jaune Yellow', colors: ['#283818', '#508038', '#88b850', '#c0d878', '#e8f0b0'] },
    { name: 'Glacier Bleu Blue Blanc White', colors: ['#c0d8f0', '#d0e8f8', '#e0f0ff', '#f0f8ff', '#f8fcff'] },
    { name: 'Riverbed Marron Brown Gris Gray', colors: ['#1a1808', '#485028', '#788858', '#a8b880', '#d0d8b0'] },
    { name: 'Loam Marron Brown',            colors: ['#201008', '#604028', '#a07050', '#c8a880', '#e8d0b8'] },
    { name: 'Hot Spring Cyan Vert Green',   colors: ['#001818', '#007070', '#20c0b0', '#80f8e0', '#d0fff8'] },
    { name: 'Lichen Vert Green Gris Gray',  colors: ['#202818', '#486040', '#709868', '#a0c098', '#d0e8c8'] },
    { name: 'Tide Flat Gris Gray Cyan',     colors: ['#182028', '#305060', '#508890', '#80b8c8', '#b0d8e8'] },
    { name: 'Kelp Forest Vert Green',       colors: ['#001808', '#005030', '#208060', '#50b890', '#a0e8c8'] },
    { name: 'Bark Marron Brown',            colors: ['#1a0800', '#502010', '#8a4020', '#b06840', '#d09878'] },
    { name: 'Moss Rock Vert Green Gris Gray', colors: ['#181810', '#384830', '#607050', '#90a070', '#c0c8a0'] },
    { name: 'Fossil Gris Gray Marron Brown', colors: ['#2a2820', '#585040', '#888068', '#b0a890', '#d8d0b8'] },
    { name: 'Soil Layer Marron Brown',      colors: ['#100800', '#402810', '#804830', '#b08060', '#d8b898'] },
    { name: 'Creek Bed Bleu Blue Marron Brown', colors: ['#1a2028', '#405060', '#607880', '#88a8b0', '#c0d0d8'] },
    { name: 'Mushroom Marron Brown Gris Gray', colors: ['#2a2018', '#605848', '#988870', '#c0b098', '#e0d8c8'] },
    { name: 'Quartzite Gris Gray Blanc White', colors: ['#c0c0c8', '#d0d0d8', '#e0e0e8', '#f0f0f8', '#f8f8ff'] },
    { name: 'Mineral Spring Cyan Vert Green', colors: ['#001818', '#006858', '#30b8a0', '#80e8d8', '#d0fff8'] },
    { name: 'Dew Drop Vert Green Blanc White', colors: ['#d0f8e0', '#e0f8f0', '#f0fef8', '#f8fffd', '#ffffff'] },
    { name: 'Peat Marron Brown Noir Black',  colors: ['#0a0800', '#201808', '#402818', '#604030', '#885848'] },

    // =====================================================================
    // NEW / NOUVEAUX — FOOD & DRINK (15)
    // =====================================================================
    { name: 'Matcha Vert Green',            colors: ['#283818', '#508040', '#88b868', '#c0d898', '#e8f8c8'] },
    { name: 'Blueberry Violet Purple Bleu Blue', colors: ['#0a0818', '#281840', '#502880', '#7850a8', '#b0a0d0'] },
    { name: 'Raspberry Jam Rouge Red Rose Pink', colors: ['#1a0008', '#700030', '#c84060', '#f08098', '#f8c0d0'] },
    { name: 'Dark Chocolate Marron Brown Noir Black', colors: ['#0a0400', '#200c00', '#401808', '#682010', '#902818'] },
    { name: 'Mint Chocolate Vert Green Marron Brown', colors: ['#101808', '#305828', '#60a848', '#90d870', '#c0f8a0'] },
    { name: 'Lavender Honey Violet Purple Jaune Yellow', colors: ['#302848', '#708098', '#c0b8d8', '#e8d890', '#f8f0b8'] },
    { name: 'Espresso Cream Marron Brown Blanc White', colors: ['#180800', '#603818', '#c09858', '#e8d0a0', '#f8f0e0'] },
    { name: 'Mango Lassi Orange Jaune Yellow', colors: ['#3a1400', '#b05818', '#e8a840', '#f8d880', '#fff8d0'] },
    { name: 'Grenadine Rouge Red Orange',   colors: ['#2a0000', '#900000', '#e04000', '#f88020', '#ffc860'] },
    { name: 'Pistachio Vert Green Jaune Yellow', colors: ['#2a3818', '#607048', '#98a870', '#c8d0a0', '#e8f0d0'] },
    { name: 'Blue Cheese Bleu Blue Gris Gray', colors: ['#3a3848', '#6a7080', '#a0a8c0', '#c8d0e0', '#e8ecf8'] },
    { name: 'Sour Apple Vert Green Jaune Yellow', colors: ['#183800', '#409810', '#80d820', '#c0f840', '#e8ff90'] },
    { name: 'Black Sesame Noir Black Gris Gray', colors: ['#101010', '#201818', '#382820', '#504030', '#686050'] },
    { name: 'Pomelo Rose Pink Jaune Yellow', colors: ['#f8e8c0', '#f8d8a8', '#f8c890', '#f8a878', '#f8806a'] },
    { name: 'Tahini Jaune Yellow Marron Brown', colors: ['#3a2808', '#8a6820', '#c0a848', '#d8c890', '#f0e8d0'] },

    // =====================================================================
    // NEW / NOUVEAUX — ABSTRACT & ARTISTIC (20)
    // =====================================================================
    { name: 'Chiaroscuro Noir Black Blanc White', colors: ['#000000', '#181818', '#606060', '#b8b8b8', '#ffffff'] },
    { name: 'Negative Space Noir Black Blanc White', colors: ['#080808', '#303030', '#787878', '#d0d0d0', '#f8f8f8'] },
    { name: 'Oxidized Copper Orange Vert Green', colors: ['#1a0800', '#7a3818', '#b06020', '#408060', '#60a888'] },
    { name: 'Neon Void Violet Purple Cyan', colors: ['#000008', '#000030', '#0000c0', '#8000ff', '#00ffff'] },
    { name: 'Thermal Scan Bleu Blue Rouge Red', colors: ['#000040', '#0030a0', '#008888', '#a04000', '#ff2000'] },
    { name: 'Pixel Burn Violet Purple Orange', colors: ['#050010', '#280060', '#9800ff', '#ff8000', '#ffff00'] },
    { name: 'Glitch Art Rose Pink Bleu Blue', colors: ['#ff0088', '#ff0000', '#0000ff', '#00ffff', '#ffffff'] },
    { name: 'Infrared Rouge Red Jaune Yellow', colors: ['#000000', '#400000', '#c80000', '#ff8000', '#ffff00'] },
    { name: 'UV Scan Violet Purple Cyan',   colors: ['#000010', '#080040', '#4000c0', '#a000ff', '#00ffff'] },
    { name: 'Heat Map Rouge Red Bleu Blue', colors: ['#0000aa', '#0088ff', '#00ffaa', '#ffff00', '#ff0000'] },
    { name: 'Audio Wave Vert Green Bleu Blue', colors: ['#001818', '#00808a', '#00d0d0', '#80f8f8', '#ffffff'] },
    { name: 'Chroma Key Vert Green',        colors: ['#00a800', '#00c800', '#00e800', '#00ff00', '#80ff80'] },
    { name: 'Retroreflect Jaune Yellow Gris Gray', colors: ['#202020', '#808080', '#e8e880', '#f8f840', '#f8f800'] },
    { name: 'Overexposed Blanc White Jaune Yellow', colors: ['#f8f8d0', '#f8f8e0', '#f8f8f0', '#f8f8f8', '#ffffff'] },
    { name: 'Solarize Jaune Yellow Violet Purple', colors: ['#000000', '#400040', '#c000c0', '#f8a000', '#ffffff'] },
    { name: 'Double Exposure Bleu Blue Rose Pink', colors: ['#0a0028', '#280060', '#8040a8', '#d080c8', '#f8c0e0'] },
    { name: 'Color Bleed Rouge Red Bleu Blue', colors: ['#aa0000', '#cc4040', '#8040a0', '#4040cc', '#0000aa'] },
    { name: 'Film Grain Gris Gray Marron Brown', colors: ['#2a2018', '#484030', '#787060', '#a8a090', '#d0c8c0'] },
    { name: 'Burned Paper Marron Brown Noir Black', colors: ['#080400', '#281408', '#503020', '#807050', '#b0a080'] },
    { name: 'Lumen Jaune Yellow Blanc White', colors: ['#2a2000', '#888000', '#e8e000', '#f8f880', '#ffffff'] },

    // =====================================================================
    // NEW / NOUVEAUX — CULTURAL EXTRA (15)
    // =====================================================================
    { name: 'Oaxaca Orange Marron Brown',   colors: ['#3a0800', '#a03010', '#e07830', '#f0b870', '#f8e8c8'] },
    { name: 'Havana Marron Brown Jaune Yellow', colors: ['#2a1000', '#785018', '#c09030', '#e8c870', '#f8f0d0'] },
    { name: 'Casablanca Blanc White Bleu Blue', colors: ['#1a2848', '#2058a0', '#60a0d8', '#b0d8f0', '#f0f8ff'] },
    { name: 'Bali Vert Green Orange',       colors: ['#1a2000', '#508030', '#c08820', '#f0a840', '#f8e8c0'] },
    { name: 'Nairobi Vert Green Marron Brown', colors: ['#1a1808', '#486020', '#80a848', '#b8c880', '#d8e0b0'] },
    { name: 'Petra Marron Brown Rouge Red', colors: ['#2a0808', '#8a2818', '#c06030', '#e09060', '#f0c8a0'] },
    { name: 'Reykjavik Bleu Blue Gris Gray', colors: ['#0a1828', '#203858', '#406090', '#7098c0', '#a8c8e0'] },
    { name: 'Hanoi Vert Green Jaune Yellow', colors: ['#182808', '#408830', '#78c050', '#c0e888', '#e8f8d0'] },
    { name: 'Tulum Bleu Blue Vert Green',   colors: ['#001828', '#007878', '#30c0b0', '#80f0e0', '#d0fff8'] },
    { name: 'Isfahan Bleu Blue Jaune Yellow', colors: ['#0a1840', '#1a5080', '#4090d0', '#c0b040', '#f8e870'] },
    { name: 'Kathmandu Orange Marron Brown', colors: ['#2a1000', '#8a4018', '#d09030', '#f0c870', '#f8f0d0'] },
    { name: 'Lagos Rouge Red Orange',       colors: ['#1a0000', '#700000', '#c84010', '#f07830', '#f8c880'] },
    { name: 'Lima Vert Green Jaune Yellow', colors: ['#182008', '#508030', '#98c040', '#d0e880', '#f0f8c0'] },
    { name: 'Tbilisi Violet Purple Marron Brown', colors: ['#2a0828', '#783050', '#c07080', '#e0a8a0', '#f8d8d0'] },
    { name: 'Muscat Jaune Yellow Orange',   colors: ['#3a1808', '#a06028', '#e0a848', '#f8d880', '#f8f8e0'] },
];

  const gradColors = f => {
    if (f.noGrad) return f.tags.map(() => null);
    const n = f.tags.length;
    if (!n) return [];
    const g = f.gradient || [f.colorStart || '#c0f0f8', f.colorEnd || '#183848'];
    if (n === 1) return [g[0]];
    return f.tags.map((_, i) => {
      const p = (i / (n - 1)) * (g.length - 1);
      const idx = Math.floor(p);
      if (idx >= g.length - 1) return g[g.length - 1];
      return lerp(g[idx], g[idx + 1], p - idx);
    });
  };

  const gradChildColors = f => {
    if (!f.gradFolders || f.noGrad) return (f.children || []).map(() => null);
    const children = f.children || [];
    const n = children.length;
    if (!n) return [];
    const g = f.gradient || [f.colorStart || '#c0f0f8', f.colorEnd || '#183848'];
    if (n === 1) return [g[0]];
    return children.map((_, i) => {
      const p = (i / (n - 1)) * (g.length - 1);
      const idx = Math.floor(p);
      if (idx >= g.length - 1) return g[g.length - 1];
      return lerp(g[idx], g[idx + 1], p - idx);
    });
  };

  const findF = (id, l = S.folders) => {
    for (const f of l) {
      if (f.id === id) return f;
      const r = findF(id, f.children || []);
      if (r) return r;
    }
    return null;
  };

  const folderOf = tag => flat().find(f => (f.tags || []).includes(tag)) || null;
  const allTagsOf = f => [...(f.tags || []), ...(f.children || []).flatMap(allTagsOf)];

  const delF = (id, l = S.folders) => {
    const i = l.findIndex(f => f.id === id);
    if (i !== -1) { l.splice(i, 1); return true; }
    return l.some(f => delF(id, f.children || []));
  };

  function extractFolder(id, l = S.folders) {
    const i = l.findIndex(f => f.id === id);
    if (i !== -1) return l.splice(i, 1)[0];
    for (const f of l) {
      const found = extractFolder(id, f.children || []);
      if (found) return found;
    }
    return null;
  }

  function isDescendant(sourceId, targetId) {
    const source = findF(sourceId);
    if (!source) return false;
    const check = children => children.some(c => c.id === targetId || check(c.children || []));
    return check(source.children || []);
  }

  function getFolderPath(tagName) {
    const path = [];
    const search = (folders, ancestors) => {
      for (const f of folders) {
        const chain = [...ancestors, f];
        if ((f.tags || []).includes(tagName)) { path.push(...chain); return true; }
        if (search(f.children || [], chain)) return true;
      }
      return false;
    };
    search(S.folders, []);
    return path;
  }

  const getNativeLis = () => Array.from(document.querySelectorAll('li.tag.has-button:not([data-mm])'));

  const getMainUl = () => {
    const candidates = Array.from(document.querySelectorAll('ul.tag-list:not([role="listbox"])'));
    const withTags = candidates.find(ul => ul.querySelector('li.tag.has-button'));
    if (withTags) return withTags;
    const fromLi = document.querySelector('li.tag.has-button:not([data-mm])')?.closest('ul');
    if (fromLi) return fromLi;
    return candidates[0] || null;
  };

  const getLocOl = () => document.querySelector('section.location-preview ol.tag-list[role="listbox"]') || null;

  function nameFromLi(li) {
    const label = li.querySelector('label.tag_text, label');
    if (!label) return '';
    let n = '';
    label.childNodes.forEach(node => { if (node.nodeType === Node.TEXT_NODE) n += node.textContent; });
    return n.trim() || (label.textContent || '').replace(/\s*\d+\s*$/, '').trim();
  }

  const countFromLi = li => li?.querySelector('small')?.textContent?.trim() || '';
 let _nativeTagMapCache = null;
  let _nativeTagMapTimer = null;
  const liOfTag = name => {
    if (!_nativeTagMapCache) {
      _nativeTagMapCache = new Map();
      document.querySelectorAll('li.tag.has-button:not([data-mm])').forEach(li => {
        _nativeTagMapCache.set(nameFromLi(li), li);
      });
      if (!_nativeTagMapTimer) {
        _nativeTagMapTimer = setTimeout(() => {
          _nativeTagMapCache = null;
          _nativeTagMapTimer = null;
        }, 0);
      }
    }
    return _nativeTagMapCache.get(name) || null;
  };

  function getTagColor(li) {
    if (!li) return null;
    if (li.dataset.mmColor) return li.dataset.mmColor;
    const wasHidden = li.style.display === 'none';
    if (wasHidden) li.style.removeProperty('display');
    const bg = window.getComputedStyle(li).backgroundColor;
    if (wasHidden) li.style.setProperty('display', 'none', 'important');
    if (bg && bg !== 'rgba(0, 0, 0, 0)' && bg !== 'transparent') {
      li.dataset.mmColor = bg;
      return bg;
    }
    return null;
  }

  const STORAGE_PREFIX = '[⚠️_DO_NOT_DELETE_MM_CLOUD_SAVE]';
  const OLD_STORAGE_PREFIX = '[⚠️_NE_PAS_SUPPRIMER_MM_CLOUD_SAVE]';

  function getStorageTagLi() {
    return getNativeLis().find(li => {
      const n = nameFromLi(li);
      return n.startsWith(STORAGE_PREFIX) || n.startsWith(OLD_STORAGE_PREFIX);
    });
  }

  function showCloudSetupWizard() {
    return new Promise(res => {
      const ov = mk('div', '', 'position:fixed;inset:0;background:rgba(10,10,10,.7);backdrop-filter:blur(5px);-webkit-backdrop-filter:blur(5px);z-index:999999;display:flex;align-items:center;justify-content:center;');
      const box = mk('div', '', 'background:#1c1c1a;border:1px solid rgba(255,255,255,.08);border-radius:16px;padding:32px;width:420px;color:#e8e8e8;font-family:"Open Sans",sans-serif;box-shadow:0 24px 64px rgba(0,0,0,.6), 0 0 0 1px rgba(255,255,255,.02) inset;text-align:left;position:relative;display:flex;flex-direction:column;gap:20px;');
      const header = mk('div', '', 'display:flex;align-items:center;gap:12px;');
      const iconWrap = mk('div', '', 'display:flex;align-items:center;justify-content:center;width:42px;height:42px;background:rgba(59,130,246,.15);border-radius:10px;color:#3b82f6;');
      iconWrap.innerHTML = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z"></path></svg>`;
      const title = mk('h2', '', 'font-size:18px;font-weight:700;margin:0;color:#fff;letter-spacing:0.3px;');
      title.textContent = 'Cloud Sync Setup';
      header.append(iconWrap, title);
      box.appendChild(header);
      const p1 = mk('p', '', 'font-size:13.5px;color:rgba(255,255,255,.65);line-height:1.5;margin:0;');
      p1.innerHTML = `To securely save your folders to the cloud, the script needs a native tag to store its data. <b>You only need to do this once per map.</b>`;
      box.appendChild(p1);
      const stepsWrap = mk('div', '', 'display:flex;flex-direction:column;gap:12px;');
      const step1 = mk('div', '', 'display:flex;align-items:center;gap:14px;background:rgba(0,0,0,.25);padding:14px 16px;border-radius:12px;border:1px solid rgba(255,255,255,.04);');
      step1.innerHTML = `<div style="background:#3b82f6;color:#fff;width:22px;height:22px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11.5px;font-weight:bold;flex-shrink:0;">1</div><div style="font-size:13.5px;line-height:1.4;color:#ddd;">Add a location anywhere on your map.</div>`;
      const step2 = mk('div', '', 'display:flex;align-items:flex-start;gap:14px;background:rgba(0,0,0,.25);padding:14px 16px;border-radius:12px;border:1px solid rgba(255,255,255,.04);');
      step2.innerHTML = `<div style="background:#3b82f6;color:#fff;width:22px;height:22px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11.5px;font-weight:bold;flex-shrink:0;margin-top:2px;">2</div><div style="font-size:13.5px;line-height:1.4;color:#ddd;width:100%;">Give it the exact tag name below:
        <div style="display:flex;gap:8px;margin-top:12px;">
          <input type="text" value="${STORAGE_PREFIX}" readonly style="flex:1;background:rgba(0,0,0,.4);border:1px solid rgba(255,255,255,.1);border-radius:8px;color:#60a5fa;padding:10px 12px;font-family:monospace;font-size:11px;outline:none;" />
          <button id="mm-copy-btn" style="background:#2d2d2a;border:1px solid rgba(255,255,255,.1);border-radius:8px;color:#fff;padding:0 16px;cursor:pointer;font-weight:600;font-size:12px;transition:all .2s;">Copy</button>
        </div>
      </div>`;
      const step3 = mk('div', '', 'display:flex;align-items:center;gap:14px;background:rgba(0,0,0,.25);padding:14px 16px;border-radius:12px;border:1px solid rgba(255,255,255,.04);');
      step3.innerHTML = `<div style="background:#3b82f6;color:#fff;width:22px;height:22px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11.5px;font-weight:bold;flex-shrink:0;">3</div><div style="font-size:13.5px;line-height:1.4;color:#ddd;">Click <b>Save</b> at the bottom left of the panel.</div>`;
      stepsWrap.append(step1, step2, step3);
      box.appendChild(stepsWrap);
      setTimeout(() => {
        const copyBtn = box.querySelector('#mm-copy-btn');
        copyBtn.onclick = () => {
          navigator.clipboard.writeText(STORAGE_PREFIX);
          copyBtn.textContent = 'Copied!';
          copyBtn.style.background = '#22c55e';
          copyBtn.style.color = '#fff';
          copyBtn.style.borderColor = '#16a34a';
          setTimeout(() => {
            copyBtn.textContent = 'Copy';
            copyBtn.style.background = '#2d2d2a';
            copyBtn.style.color = '#fff';
            copyBtn.style.borderColor = 'rgba(255,255,255,.1)';
          }, 2000);
        };
        copyBtn.onmouseenter = () => { if(copyBtn.textContent === 'Copy') copyBtn.style.background = '#3f3f3a'; };
        copyBtn.onmouseleave = () => { if(copyBtn.textContent === 'Copy') copyBtn.style.background = '#2d2d2a'; };
      }, 0);
      const btnRow = mk('div', '', 'display:flex;gap:12px;justify-content:center;margin-top:8px;');
      const cancelBtn = mk('button', '', 'background:transparent;border:1px solid rgba(255,255,255,.15);border-radius:8px;color:rgba(255,255,255,.6);padding:10px 18px;cursor:pointer;font-size:13px;font-weight:600;transition:all 0.2s;');
      cancelBtn.textContent = 'Cancel';
      cancelBtn.onmouseenter = () => { cancelBtn.style.borderColor = 'rgba(255,255,255,.3)'; cancelBtn.style.color = '#fff'; };
      cancelBtn.onmouseleave = () => { cancelBtn.style.borderColor = 'rgba(255,255,255,.15)'; cancelBtn.style.color = 'rgba(255,255,255,.6)'; };
      const doneBtn = mk('button', '', 'background:#3b82f6;border:none;border-radius:8px;color:#fff;padding:10px 24px;cursor:pointer;font-size:13px;font-weight:700;transition:background 0.2s;');
      doneBtn.textContent = 'I created the tag';
      doneBtn.onmouseenter = () => doneBtn.style.background = '#2563eb';
      doneBtn.onmouseleave = () => doneBtn.style.background = '#3b82f6';
      cancelBtn.onclick = () => { ov.remove(); res(false); };
      doneBtn.onclick = () => { ov.remove(); res(true); };
      btnRow.append(cancelBtn, doneBtn);
      box.appendChild(btnRow);
      ov.appendChild(box);
      document.body.appendChild(ov);
    });
  }

  function showSaveWarningModal() {
    return new Promise(res => {
      const ov = mk('div', '', 'position:fixed;inset:0;background:rgba(0,0,0,.8);z-index:99999;display:flex;align-items:center;justify-content:center;');
      const box = mk('div', '', 'background:rgb(37,37,33);border:1px solid rgba(255,255,255,.1);border-radius:12px;padding:24px;width:400px;color:#e8e8e8;font-family:"Open Sans",sans-serif;box-shadow:0 16px 48px rgba(0,0,0,.8);');
      const ttl = mk('h2', '', 'margin:0 0 12px 0;font-size:16px;font-weight:700;color:#fff;');
      ttl.textContent = 'Save Map & Folders';
      const p = mk('p', '', 'margin:0 0 20px 0;font-size:13px;color:rgba(255,255,255,.7);line-height:1.5;');
      p.textContent = 'Saving your folders requires saving the map. This will also save any recent locations or tags you added. Proceed?';
      box.append(ttl, p);
      const btnRow = mk('div', '', 'display:flex;gap:8px;justify-content:flex-end;');
      const bCancel = mk('button', '', 'background:transparent;border:1px solid rgba(255,255,255,.15);border-radius:8px;color:rgba(255,255,255,.6);padding:8px 12px;cursor:pointer;font-size:12px;font-weight:600;transition:all 0.2s;');
      bCancel.textContent = 'Cancel';
      bCancel.onmouseenter = () => bCancel.style.borderColor = 'rgba(255,255,255,.3)';
      bCancel.onmouseleave = () => bCancel.style.borderColor = 'rgba(255,255,255,.15)';
      const bSkip = mk('button', '', 'background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.1);border-radius:8px;color:rgba(255,255,255,.8);padding:8px 12px;cursor:pointer;font-size:12px;font-weight:600;transition:all 0.2s;');
      bSkip.textContent = "Understood, don't show again";
      bSkip.onmouseenter = () => bSkip.style.background = 'rgba(255,255,255,.15)';
      bSkip.onmouseleave = () => bSkip.style.background = 'rgba(255,255,255,.07)';
      const bSave = mk('button', '', 'background:#3b82f6;border:none;border-radius:8px;color:#fff;padding:8px 16px;cursor:pointer;font-size:12px;font-weight:700;transition:background 0.2s;');
      bSave.textContent = 'Save';
      bSave.onmouseenter = () => bSave.style.background = '#2563eb';
      bSave.onmouseleave = () => bSave.style.background = '#3b82f6';
      bCancel.onclick = () => { ov.remove(); res('cancel'); };
      bSkip.onclick = () => { ov.remove(); res('save_and_skip'); };
      bSave.onclick = () => { ov.remove(); res('save'); };
      btnRow.append(bCancel, bSkip, bSave);
      box.appendChild(btnRow);
      ov.appendChild(box);
      document.body.appendChild(ov);
    });
  }

  function injectFooterButtons() {
    const footerContainer = document.querySelector('.map-meta__actions');
    if (!footerContainer || document.getElementById('mm-footer-actions-wrapper')) return;
    const wrapper = mk('span', '', 'display:inline-flex; align-items:center; margin-left:12px;');
    wrapper.id = 'mm-footer-actions-wrapper';
    const saveBtn = mk('button', 'button button--primary' + (isDirty ? ' mm-btn-dirty' : ''));
    saveBtn.id = 'mm-folder-save-btn';
    saveBtn.type = 'button';
    saveBtn.title = 'Save folder tree to the Cloud';
    saveBtn.innerHTML = `<span class="button__label">${isDirty ? 'Save Folders' : 'Folders Saved'}</span>`;
    saveBtn.onclick = () => saveToCloud();
    const loadBtn = mk('button', 'button button--primary');
    loadBtn.id = 'mm-folder-load-btn';
    loadBtn.type = 'button';
    loadBtn.title = 'Restore folders from the Cloud (Undo local changes)';
    loadBtn.innerHTML = '<span class="button__label">Load</span>';
    loadBtn.onclick = () => loadFromCloud(true);
    wrapper.append(saveBtn, loadBtn);
    footerContainer.appendChild(wrapper);
  }

  async function saveToCloud() {
    if (typeof LZString === 'undefined') { alert('Error: LZString library failed to load.'); return; }
    let li = getStorageTagLi();
    if (!li) {
      const userDidSetup = await showCloudSetupWizard();
      if (!userDidSetup) return;
      await new Promise(r => setTimeout(r, 500));
      li = getStorageTagLi();
      if (!li) { alert('Storage tag not found. Make sure you created it with the exact name and clicked Save.'); return; }
    }
    const skipWarning = localStorage.getItem('mmapp_skip_save_warning') === 'true';
    if (!skipWarning) {
      const action = await showSaveWarningModal();
      if (action === 'cancel') return;
      if (action === 'save_and_skip') localStorage.setItem('mmapp_skip_save_warning', 'true');
    }
    const editBtn = li.querySelector('button.tag_button--edit, button[class*="edit"]');
    if (!editBtn) return;
    S.lastUpdate = Date.now();
    const compressed = LZString.compressToEncodedURIComponent(JSON.stringify(S));
    const currentName = nameFromLi(li);
    const prefixToUse = currentName.startsWith(OLD_STORAGE_PREFIX) ? OLD_STORAGE_PREFIX : STORAGE_PREFIX;
    const payload = `${prefixToUse}:::${compressed}`;
    editBtn.click();
    const nameInput = await waitDOM('div[role="dialog"] input[type="text"].input');
    if (!nameInput) { console.warn("Save dialog didn't open in time."); return; }
    const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;
    if (nativeSetter) nativeSetter.call(nameInput, payload);
    else nameInput.value = payload;
    nameInput.dispatchEvent(new Event('input', { bubbles: true }));
    nameInput.dispatchEvent(new Event('change', { bubbles: true }));
    await new Promise(r => setTimeout(r, 100));
    const dialog = nameInput.closest('div[role="dialog"]');
    const btns = dialog?.querySelectorAll('.edit-tag-modal__actions button, button.save');
    btns?.[btns.length - 1]?.click();
    setDirty(false);
    updateHideStyle();
    await new Promise(r => setTimeout(r, 550));
    const nativeSaveBtn = document.querySelector('button[data-qa="map-save"]');
    if (nativeSaveBtn && !nativeSaveBtn.disabled) {
      nativeSaveBtn.click();
    } else {
      console.warn("[mm-folders] Could not find the native save button using data-qa!");
    }
    const notif = mk('div', '', 'position:fixed;top:20px;right:20px;background:#3b82f6;color:#fff;padding:10px 20px;border-radius:8px;font-family:sans-serif;font-size:13px;z-index:999999;box-shadow:0 4px 12px rgba(0,0,0,.5);');
    notif.textContent = "☁️ Folders and map saved!";
    document.body.appendChild(notif);
    setTimeout(() => notif.remove(), 3000);
  }

  function loadFromCloud(force = false) {
    if (typeof LZString === 'undefined') { if (force) alert('Error: LZString library failed to load.'); return false; }
    const li = getStorageTagLi();
    if (!li) { if (force) alert(`No save found on this map.\n(No tag starting with ${STORAGE_PREFIX.substring(0, 10)}...)`); return false; }
    const name = nameFromLi(li);
    if (!name.includes(':::')) { if (force) alert('Storage tag exists but seems empty. Save your folders first.'); return false; }
    try {
      const dataPart = name.substring(name.indexOf(':::') + 3);
      const decoded = LZString.decompressFromEncodedURIComponent(dataPart);
      const parsed = JSON.parse(decoded || dataPart);
      if (parsed?.folders) {
        if (force || S.folders.length === 0 || (parsed.lastUpdate || 0) > S.lastUpdate) {
          if (force && !confirm('⚠️ Warning: Forcing Load will overwrite your current local folders with the Cloud save. Continue?')) return false;
          S = { folders: parsed.folders, lastUpdate: parsed.lastUpdate || Date.now() };
          invalidateFlatCache();
          saveS(false);
          setDirty(false);
          render();
          return true;
        }
      }
    } catch (e) { console.error('Cloud Load Error:', e); }
    return false;
  }

  function injectCSS() {
    if (document.getElementById('mm-css')) return;
    const s = document.createElement('style');
    s.id = 'mm-css';
    s.textContent = `
      li[data-mm-folder] { list-style:none; margin:0; padding:2px 0; width:100%; box-sizing:border-box; }
      .mm-hd {
        display:flex; align-items:center; gap:5px;
        width:100%; height:32px; box-sizing:border-box;
        padding:0 10px 0 0; border-radius:999px;
        font-family:"Open Sans",sans-serif; font-size:16px; font-weight:400;
        cursor:grab; user-select:none;
        transition:filter .15s, box-shadow .15s;
      }
      .mm-hd:has(.mm-left-actions:hover, .mm-right-actions:hover) { cursor:default; }
      .mm-hd:active:not(:has(.mm-left-actions:hover, .mm-right-actions:hover)) { cursor:grabbing; }
      .mm-hd:hover { filter:brightness(1.08); }
      .mm-hd--on      { box-shadow:0 0 0 2px #fff!important; }
      .mm-hd--off     { opacity:.55; }
      .mm-hd--partial { box-shadow:0 0 0 2px rgba(255,255,255,.4)!important; }
      .mm-left-actions { display:flex; align-items:center; gap:2px; cursor:default; height:100%; padding-left:2px; }
      .mm-right-actions { display:flex; align-items:center; gap:4px; cursor:default; height:100%; }
      li[data-mm-folder].mm-drop-above { border-top:3px solid rgba(255,255,255,.8) !important; }
      li[data-mm-folder].mm-drop-after  { border-bottom:3px solid rgba(255,255,255,.8) !important; }
      li[data-mm-folder].mm-drop-inside > .mm-hd { box-shadow:0 0 0 2px rgba(255,255,255,.9), 0 0 12px rgba(255,255,255,.3) !important; filter:brightness(1.15); }
      .mm-arrow-zone {
        display:flex; align-items:center; justify-content:center;
        width:28px; height:28px; flex-shrink:0;
        border-radius:50%; cursor:pointer; transition:background .12s;
      }
      .mm-arrow-zone:hover { background:rgba(0,0,0,.22); }
      .mm-pencil {
        display:flex; align-items:center; justify-content:center;
        width:22px; height:22px; flex-shrink:0; cursor:pointer;
        border-radius:50%; opacity:.55; transition:opacity .1s,background .1s;
      }
      .mm-pencil:hover { opacity:1; background:rgba(0,0,0,.18); }
      .mm-body { display:flex; flex-wrap:wrap; gap:5px; padding:6px 8px 8px 16px; }
      .mm-dropzone {
        width:100%; padding:5px 12px; text-align:center;
        border:1.5px dashed rgba(255,255,255,.2); border-radius:8px;
        font-size:11px; font-family:"Open Sans",sans-serif;
        color:rgba(255,255,255,.3); font-style:italic;
      }
      .mm-dh { outline:2px dashed rgba(255,255,255,.5)!important; outline-offset:2px!important; }
      .mm-dropzone.mm-dh { border-color:rgba(255,255,255,.5)!important; color:rgba(255,255,255,.6)!important; outline:none!important; }
      li.mm-sep { list-style:none; height:1px; background:rgba(120,120,220,.2); margin:6px 4px; }
      li.mm-addli { list-style:none; margin:4px 0 5px; }
      .mm-addbtn {
        display:flex; align-items:center; justify-content:center; width:100%; height:32px;
        padding:0 14px; border-radius:999px; border:none; cursor:pointer;
        background:#E0E4E5; color:#121210;
        font-size:16px; font-family:"Open Sans",sans-serif; font-weight:400;
        box-sizing:border-box; transition:background .15s;
      }
      .mm-addbtn:hover { background:#d0d4d6; }
      .mm-tag-btn {
        display:inline-flex; align-items:center; gap:5px;
        height:32px; padding:0 10px 0 8px; border-radius:999px; border:none;
        font-family:"Open Sans",sans-serif; font-size:16px; font-weight:400;
        cursor:pointer;
        transition:filter .12s, box-shadow .12s, transform .12s;
        white-space:nowrap;
      }
      .mm-tag-btn:hover { filter:brightness(1.12); transform:translateY(-1px); box-shadow:0 3px 8px rgba(0,0,0,.25); }
      .mm-tag-btn--on  { box-shadow:0 0 0 2px #fff!important; }
      .mm-tag-drop-indicator { border-left: 3px solid #fff !important; }
      .mm-subaddbtn {
        background:rgba(255,255,255,.07); border:1.5px dashed rgba(255,255,255,.2);
        border-radius:999px; color:rgba(255,255,255,.4);
        font-size:11px; padding:2px 14px; height:24px;
        cursor:pointer; font-family:"Open Sans",sans-serif; font-weight:600;
        transition:color .12s,border-color .12s,background .12s;
        display:flex; align-items:center; width:fit-content;
      }
      .mm-subaddbtn:hover { color:rgba(255,255,255,.8); border-color:rgba(255,255,255,.5); background:rgba(255,255,255,.12); }
      .mm-sub-wrap { display:flex; flex-direction:column; gap:4px; padding:5px 0 3px 14px; }
      .mm-grad { width:24px; height:7px; border-radius:4px; flex-shrink:0; opacity:.85; }
      .mm-cnt { background:rgba(0,0,0,.2); border-radius:999px; padding:0 7px; font-size:11px; font-weight:700; flex-shrink:0; line-height:18px; }
      .mm-actbtn { background:rgba(0,0,0,.18); border:none; cursor:pointer; border-radius:999px; padding:1px 7px; font-size:11px; font-weight:600; flex-shrink:0; opacity:.75; transition:opacity .1s; font-family:inherit; line-height:18px; display:inline-flex; align-items:center; justify-content:center; }
      .mm-actbtn:hover { opacity:1; background:rgba(0,0,0,.32); }
      #mm-folder-save-btn { background-color: #1e3a8a !important; border-color: #1e3a8a !important; color: #cbd5e1 !important; transition: all 0.2s; }
      #mm-folder-save-btn:hover { background-color: #1e40af !important; border-color: #1e40af !important; color: #fff !important; }
      #mm-folder-save-btn.mm-btn-dirty { background-color: #3b82f6 !important; border-color: #3b82f6 !important; color: #fff !important; }
      #mm-folder-save-btn.mm-btn-dirty:hover { background-color: #2563eb !important; border-color: #2563eb !important; }
      #mm-folder-load-btn { background-color: #7B68EE !important; border-color: #7B68EE !important; color: #fff !important; margin-left: 6px; }
      #mm-folder-load-btn:hover { background-color: #6A5ACD !important; border-color: #6A5ACD !important; }

      body:not(.mm-search-mode).mm-loading li.tag.has-button:not([data-mm]) { display: none !important; }

      /* Cacher proprement les dossiers sans casser le menu ou les popups */
      body.mm-search-mode ul.tag-list > li[data-mm="1"],
      body.mm-search-mode li.mm-sep,
      body.mm-search-mode li.mm-addli { display: none !important; }

      body.mm-loc-open ul.tag-list > li[data-mm="1"],
      body.mm-loc-open li.mm-sep,
      body.mm-loc-open li.mm-addli { display: none !important; }

      #mm-path-tooltip {
        position: fixed; z-index: 999999; pointer-events: none; display: flex;
        background: rgba(22, 22, 19, 0.78); border: 1px solid rgba(255,255,255,.08);
        box-shadow: 0 4px 18px rgba(0,0,0,.4); backdrop-filter: blur(12px);
        -webkit-backdrop-filter: blur(12px); font-family: "Open Sans", sans-serif;
        font-size: 12.5px; opacity: 0; transition: opacity .18s ease, transform .18s ease; transform: translateY(3px);
      }
      #mm-path-tooltip.mm-tt-visible { opacity: 1; transform: translateY(0); }
      #mm-path-tooltip:not(.mm-tt-col) { flex-direction: row; align-items: center; padding: 5px 13px; gap: 5px; border-radius: 999px; }
      #mm-path-tooltip.mm-tt-col { flex-direction: column; align-items: flex-start; padding: 7px 12px 8px; gap: 3px; border-radius: 10px; }
      #mm-path-tooltip .mm-tt-row { display: flex; align-items: center; gap: 5px; white-space: nowrap; line-height: 1; }
      #mm-path-tooltip .mm-tt-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
      #mm-path-tooltip .mm-tt-name { font-weight: 600; color: #dcdcdc; letter-spacing: .01em; }
      #mm-path-tooltip .mm-tt-arrow { display: flex; align-items: center; justify-content: center; flex-shrink: 0; width: 12px; height: 12px; opacity: .4; }
      #mm-path-tooltip.mm-tt-col .mm-tt-connector { font-size: 10px; opacity: .28; line-height: 1; display: flex; align-items: center; user-select: none; }
    `;
    document.head.appendChild(s);
  }

  function updateHideStyle() {
    const isSearch = document.body.classList.contains('mm-search-mode');
    const assigned = getAssignedTags();
    document.querySelectorAll('li:not([data-mm])').forEach(li => {
      let hide = false;
      const t = li.textContent || '';
      if (t.includes(STORAGE_PREFIX) || t.includes(OLD_STORAGE_PREFIX)) {
        hide = true;
      } else if (!isSearch) {
        const name = nameFromLi(li);
        if (name && assigned.has(name)) hide = true;
      }
      if (hide) li.style.setProperty('display', 'none', 'important');
      else li.style.removeProperty('display');
    });
    document.querySelectorAll('.mm-tag-btn').forEach(btn => {
      const spans = btn.querySelectorAll('span');
      let tagName = '';
      spans.forEach(s => { if (!s.querySelector('svg')) tagName = s.textContent.trim(); });
      if (!tagName) return;
      const folder = folderOf(tagName);
      if (folder && (folder.gradTags === false || folder.noGrad)) {
        const li = liOfTag(tagName);
        if (li) {
          const bg = getTagColor(li) || '#5a5a8a';
          btn.style.backgroundColor = bg;
          btn.style.color = textOn(bg);
        }
      }
    });
    document.body.classList.remove('mm-loading');
    document.body.classList.remove('mm-search-exiting');
  }

  function updateAllFolderVisuals() {
    document.querySelectorAll('[data-mm-folder]').forEach(folderEl => {
      const f = findF(folderEl.getAttribute('data-mm-folder'));
      if (!f) return;
      const hd = Array.from(folderEl.children).find(c => c.classList.contains('mm-hd'));
      if (!hd) return;
      const fullyOn = isFolderFullyOn(f);
      const partialOn = !fullyOn && isFolderPartiallyOn(f);
      hd.classList.remove('mm-hd--on', 'mm-hd--off', 'mm-hd--partial');
      if (fullyOn) hd.classList.add('mm-hd--on');
      else if (partialOn) hd.classList.add('mm-hd--partial');
      else hd.classList.add('mm-hd--off');
    });
    document.querySelectorAll('.mm-tag-btn').forEach(btn => {
      const spans = btn.querySelectorAll('span');
      let t = '';
      spans.forEach(s => { if (!s.querySelector('svg')) t = s.textContent.trim(); });
      if (!t) return;
      const nat = liOfTag(t);
      if (!nat) return;
      if (nat.classList.contains('is-selected')) btn.classList.add('mm-tag-btn--on');
      else btn.classList.remove('mm-tag-btn--on');
    });
  }

  function enterSearchMode() {
    document.body.classList.add('mm-search-mode');
    document.querySelectorAll('li:not([data-mm])').forEach(li => {
      const t = li.textContent || '';
      if (!t.includes(STORAGE_PREFIX) && !t.includes(OLD_STORAGE_PREFIX)) {
        li.style.removeProperty('display');
      }
    });
  }

  function exitSearchMode() {
  hidePathTooltip(true);
  document.body.classList.remove('mm-search-mode');
  updateHideStyle();
}

  function watchSearchInput() {
    const getInput = () => document.querySelector(
      'body > div > div > div.tool-block.tag-manager > header > input'
    );
    let _boundInput = null;
    let lastValue = '';
    const attach = input => {
      if (_boundInput === input) return;
      _boundInput = input;
      const onChange = () => {
        const val = input.value || '';
        if (val === lastValue) return;
        lastValue = val;
        if (val.length > 0) {
          document.body.classList.remove('mm-search-exiting');
          enterSearchMode();
        } else {
          document.body.classList.add('mm-search-exiting');
          exitSearchMode();
        }
      };
      input.addEventListener('input', onChange);
      const obs = new MutationObserver(onChange);
      obs.observe(input, { attributes: true, attributeFilter: ['value'] });
      if (input.value) onChange();
    };
    setInterval(() => {
      const input = getInput();
      if (input && input !== _boundInput) attach(input);
    }, 800);
  }

  let _tooltip = null;
  let _tooltipTimeout = null;
  let _tooltipLi = null;

  function showPathTooltip(tagName, x, y) {
    const path = getFolderPath(tagName);
    if (!path.length) { hidePathTooltip(true); return; }
    if (_tooltip && _tooltip.dataset.tag === tagName) return;
    _tooltip?.remove();
    _tooltip = null;
    const tt = mk('div', '', '');
    tt.id = 'mm-path-tooltip';
    tt.dataset.tag = tagName;
    const totalChars = path.reduce((sum, f) => sum + f.name.length, 0);
    const isCol = totalChars > 35 || path.length > 3;
    if (isCol) tt.classList.add('mm-tt-col');
    path.forEach((folder, i) => {
      const isLast = i === path.length - 1;
      const row = mk('div', 'mm-tt-row');
      if (isCol && i > 0) {
        row.style.paddingLeft = (i * 9 + 2) + 'px';
        row.style.borderLeft = `1.5px solid rgba(255,255,255,${Math.min(0.08 + i * 0.06, 0.24)})`;
        row.style.marginLeft = '4px';
      }
      const bg = folder.color || '#c0f0f8';
      const dot = mk('div', 'mm-tt-dot');
      dot.style.background = bg;
      dot.style.boxShadow = `0 0 5px ${bg}55`;
      const name = mk('span', 'mm-tt-name');
      name.textContent = folder.name;
      row.appendChild(dot);
      row.appendChild(name);
      if (!isCol) {
        tt.appendChild(row);
        if (!isLast) {
          const arr = mk('span', 'mm-tt-arrow');
          arr.innerHTML = '<svg viewBox="0 0 24 24" width="10" height="10" fill="currentColor" style="color:rgba(255,255,255,.55)"><path d="M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z"/></svg>';
          tt.appendChild(arr);
        }
      } else {
        tt.appendChild(row);
        if (!isLast) {
          const connector = mk('div', 'mm-tt-connector');
          connector.style.paddingLeft = (i * 9 + 6) + 'px';
          connector.style.marginTop = '-1px';
          connector.style.marginBottom = '-1px';
          connector.innerHTML = '↳';
          tt.appendChild(connector);
        }
      }
    });
    document.body.appendChild(tt);
    _tooltip = tt;
    const r = tt.getBoundingClientRect();
    let left = x + 14;
    let top = y - r.height / 2;
    if (left + r.width > window.innerWidth - 8) left = x - r.width - 14;
    if (top < 8) top = 8;
    if (top + r.height > window.innerHeight - 8) top = window.innerHeight - r.height - 8;
    tt.style.left = left + 'px';
    tt.style.top = top + 'px';
    requestAnimationFrame(() => { tt.classList.add('mm-tt-visible'); });
  }

  function hidePathTooltip(instant = false) {
    clearTimeout(_tooltipTimeout);
    _tooltipLi = null;
    if (!_tooltip) return;
    if (instant) { _tooltip.remove(); _tooltip = null; return; }
    const el = _tooltip; _tooltip = null;
    el.classList.remove('mm-tt-visible');
    setTimeout(() => el.remove(), 220);
  }

  function bindTooltipListeners() {
    document.addEventListener('mouseover', e => {
      if (!document.body.classList.contains('mm-search-mode')) return;
      const li = e.target.closest('li.tag.has-button:not([data-mm])');
      if (!li) return;
      if (li === _tooltipLi) return;
      _tooltipLi = li;
      clearTimeout(_tooltipTimeout);
      const name = nameFromLi(li);
      if (!name) return;
      _tooltipTimeout = setTimeout(() => {
        if (_tooltipLi !== li) return;
        showPathTooltip(name, e.clientX, e.clientY);
      }, 100);
    }, true);
    document.addEventListener('mouseout', e => {
      const li = e.target.closest('li.tag.has-button:not([data-mm])');
      if (!li) return;
      if (li.contains(e.relatedTarget)) return;
      if (li !== _tooltipLi) return;
      _tooltipLi = null;
      clearTimeout(_tooltipTimeout);
      hidePathTooltip();
    }, true);
  }

  function showInfoModal() {
    const ov = mk('div', '', 'position:fixed;inset:0;background:rgba(0,0,0,.8);z-index:999999;display:flex;align-items:center;justify-content:center;');
    const box = mk('div', '', 'background:rgb(37,37,33);border:1px solid rgba(255,255,255,.1);border-radius:12px;padding:24px;width:420px;color:#e8e8e8;font-family:"Open Sans",sans-serif;box-shadow:0 16px 48px rgba(0,0,0,.8);line-height:1.5;font-size:13px;');
    box.innerHTML = `
      <h2 style="margin:0 0 15px 0;font-size:17px;color:#fff;">Help & Warnings</h2>
      <ul style="padding-left:20px;margin-bottom:20px;color:rgba(255,255,255,.85);display:flex;flex-direction:column;gap:12px;">
        <li><b>Security:</b> This script NEVER deletes your original tags. Deleting a folder simply releases the tags inside it back to the main list.</li>
        <li><b>Renaming:</b> If you rename a tag via the site's native menu, it will drop out of its folder. Just drag it back in.</li>
        <li><b>Cloud Save:</b> Remember to click the <b>Save Folders</b> button. The script creates a special tag [⚠️_DO_NOT_DELETE...] on your map to hide the data. Do not delete it!</li>
        <li><b>Search:</b> When you type in the search bar, folders hide and all matching tags appear directly. Hover a tag to see which folder it belongs to.</li>
        <li><b>Updates:</b> If the official website undergoes a major redesign, folder display might temporarily disappear (your tags will remain intact).</li>
      </ul>
      <div style="font-size:11px;opacity:.5;margin-bottom:20px;font-style:italic;">Note: This script is provided as is. The author is not responsible for the loss of your folder organization due to mishandling or unsaved cache clearing.</div>
      <div style="text-align:right;"><button id="mm-close-info" style="background:#fff;border:none;border-radius:8px;color:#121210;padding:8px 18px;cursor:pointer;font-weight:700;font-size:12px;">Got it!</button></div>
    `;
    ov.appendChild(box);
    document.body.appendChild(ov);
    const closeBtn = box.querySelector('#mm-close-info');
    closeBtn.onmouseenter = () => closeBtn.style.background = '#e0e0e0';
    closeBtn.onmouseleave = () => closeBtn.style.background = '#fff';
    closeBtn.onclick = () => ov.remove();
    ov.onclick = e => { if (e.target === ov) ov.remove(); };
  }

  const SVG_PEN = `<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor" style="pointer-events:none;flex-shrink:0"><path d="M20.71,7.04C21.1,6.65 21.1,6 20.71,5.63L18.37,3.29C18,2.9 17.35,2.9 16.96,3.29L15.12,5.12L18.87,8.87M3,17.25V21H6.75L17.81,9.93L14.06,6.18L3,17.25Z"/></svg>`;
  const SVG_ARR = deg => `<svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor" style="display:block;transition:transform .2s;transform:rotate(${deg}deg);pointer-events:none"><path d="M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z"/></svg>`;

  const mk = (tag, cls = '', css = '', attrs = {}) => {
    const e = document.createElement(tag);
    if (cls) e.className = cls;
    if (css) e.style.cssText = css;
    Object.entries(attrs).forEach(([k, v]) => e.setAttribute(k, v));
    return e;
  };

  let dragTag = null;
  let dragFolderId = null;

  function initDnD() {
    document.addEventListener('dragstart', e => {
      const li = e.target.closest('li.tag.has-button:not([data-mm])');
      if (!li) return;
      dragTag = nameFromLi(li);
      try { e.dataTransfer.setData('text/plain', dragTag); } catch (_) {}
      li.style.opacity = '.45';
    }, true);
    document.addEventListener('dragend', e => {
      const li = e.target.closest('li.tag.has-button:not([data-mm])');
      if (li) li.style.opacity = '';
      if (li) dragTag = null;
    }, true);
  }

  function dndOn(el, folderId) {
    el.addEventListener('dragover', e => {
      if (dragFolderId || !dragTag) return;
      e.preventDefault(); e.stopPropagation(); el.classList.add('mm-dh');
    });
    el.addEventListener('dragleave', () => el.classList.remove('mm-dh'));
    el.addEventListener('drop', e => {
      if (dragFolderId) return;
      e.preventDefault(); e.stopPropagation(); el.classList.remove('mm-dh');
      const tag = dragTag || e.dataTransfer.getData('text/plain');
      if (tag) { dragTag = null; moveTag(tag, folderId); }
    });
  }

  function reRenderFolders() {
    const ul = getMainUl();
    if (ul) renderInto(ul);
    updateHideStyle();
  }

  // --- REECRITURE EN PROFONDEUR : PURGE DU CACHE POUR moveTag ET removeTag ---
  function moveTag(tag, folderId) {
    const cleanTag = tag.trim();
    let changed = false;

    // Destruction DOM instantanée pour forcer la disparition visuelle sans attendre
    document.querySelectorAll('.mm-tag-btn').forEach(btn => {
      let btnTag = '';
      btn.querySelectorAll('span').forEach(s => {
        if (!s.querySelector('svg')) btnTag = s.textContent.trim();
      });
      if (btnTag === cleanTag) {
        btn.remove();
        changed = true;
      }
    });

    const walkRemove = (list) => {
      for (const f of list) {
        if (f.tags) {
          const originalLen = f.tags.length;
          f.tags = f.tags.filter(t => t.trim() !== cleanTag);
          if (f.tags.length !== originalLen) changed = true;
        }
        if (f.children) walkRemove(f.children);
      }
    };
    walkRemove(S.folders);

    const f = findF(folderId);
    if (f && !f.tags.some(t => t.trim() === cleanTag)) {
      f.tags.push(cleanTag);
      changed = true;
    }

    if (changed) {
      invalidateFlatCache();
      saveS();
      reRenderFolders();
    }
  }

  function removeTag(tag) {
    const cleanTag = tag.trim();
    let changed = false;

    // Destruction DOM instantanée pour forcer la disparition visuelle sans attendre
    document.querySelectorAll('.mm-tag-btn').forEach(btn => {
      let btnTag = '';
      btn.querySelectorAll('span').forEach(s => {
        if (!s.querySelector('svg')) btnTag = s.textContent.trim();
      });
      if (btnTag === cleanTag) {
        btn.remove();
        changed = true;
      }
    });

    const walk = (list) => {
      for (const f of list) {
        if (f.tags) {
          const originalLen = f.tags.length;
          f.tags = f.tags.filter(t => t.trim() !== cleanTag);
          if (f.tags.length !== originalLen) changed = true;
        }
        if (f.children) walk(f.children);
      }
    };
    walk(S.folders);

    if (changed) {
      invalidateFlatCache();
      saveS();
      reRenderFolders();
    }
  }

  function reorderFolder(dragId, targetId) {
    if (dragId === targetId) return;
    const dragged = extractFolder(dragId);
    if (!dragged) return;
    const insert = list => {
      const i = list.findIndex(f => f.id === targetId);
      if (i !== -1) { list.splice(i, 0, dragged); return true; }
      return list.some(f => insert(f.children || []));
    };
    if (!insert(S.folders)) S.folders.push(dragged);
    saveS(); reRenderFolders();
  }

  function moveFolderInto(dragId, targetId) {
    if (dragId === targetId) return;
    if (isDescendant(dragId, targetId)) return;
    const dragged = extractFolder(dragId);
    if (!dragged) return;
    const target = findF(targetId);
    if (!target) { S.folders.push(dragged); saveS(); reRenderFolders(); return; }
    if (!target.children) target.children = [];
    target.children.push(dragged);
    target.expanded = true;
    saveS(); reRenderFolders();
  }

  function reorderTag(draggedName, targetName) {
    if (draggedName === targetName) return;
    const targetFolder = folderOf(targetName);
    if (!targetFolder) return;
    const sourceFolder = folderOf(draggedName);
    if (!sourceFolder || sourceFolder.id !== targetFolder.id) return;
    targetFolder.tags = targetFolder.tags.filter(t => t !== draggedName);
    const newTargetIndex = targetFolder.tags.indexOf(targetName);
    targetFolder.tags.splice(newTargetIndex, 0, draggedName);
    saveS(); reRenderFolders();
  }

  function modal(cfg) {
    return new Promise(res => {
      const ov = mk('div', '', 'position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:99999;display:flex;align-items:center;justify-content:center;', { 'data-mm': '1' });
      const box = mk('div', '', 'background:rgb(37,37,33);border:1px solid rgba(255,255,255,.08);border-radius:12px;padding:20px 18px;width:300px;color:#e8e8e8;font-family:"Open Sans",sans-serif;font-size:13px;box-shadow:0 16px 48px rgba(0,0,0,.7);');
      const ttl = mk('div', '', 'font-size:15px;font-weight:700;margin-bottom:16px;color:#fff;'); ttl.textContent = cfg.title; box.appendChild(ttl);
      const fels = {};
      const collect = () => {
        const r = {};
        Object.entries(fels).forEach(([k, e]) => {
          if (e.type === 'checkbox') r[k] = e.checked;
          else if (e.type === 'custom') r[k] = e.value;
          else r[k] = e.value;
        });
        return r;
      };
      const confirm_ = () => { cleanup(); ov.remove(); res(collect()); };
      const cancel_ = () => { cleanup(); ov.remove(); res(null); };
      const onKeyDown = e => {
        if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); cancel_(); }
        if (e.key === 'Enter' && e.target.tagName !== 'BUTTON') { e.preventDefault(); confirm_(); }
      };
      const cleanup = () => document.removeEventListener('keydown', onKeyDown, true);
      document.addEventListener('keydown', onKeyDown, true);
      (cfg.fields || []).forEach(f => {
        const lbl = mk('label', '', 'display:block;font-size:10px;font-weight:700;letter-spacing:.8px;text-transform:uppercase;color:rgba(255,255,255,.4);margin-bottom:6px;'); lbl.textContent = f.label; box.appendChild(lbl);
        const inp = mk('input');
        inp.type = 'text'; inp.placeholder = f.placeholder || ''; inp.value = f.value || '';
        inp.style.cssText = 'background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.1);border-radius:8px;color:#fff;padding:9px 12px;font-size:13px;margin-bottom:14px;box-sizing:border-box;outline:none;font-family:"Open Sans",sans-serif;width:100%;display:block;';
        inp.onfocus = () => inp.style.borderColor = 'rgba(255,255,255,.3)';
        inp.onblur = () => inp.style.borderColor = 'rgba(255,255,255,.1)';
        fels[f.key] = inp; box.appendChild(inp);
      });
      if (cfg.showFolderColor) {
        const lbl = mk('label', '', 'display:block;font-size:10px;font-weight:700;letter-spacing:.8px;text-transform:uppercase;color:rgba(255,255,255,.4);margin-bottom:6px;'); lbl.textContent = 'Folder color'; box.appendChild(lbl);
        const fcRow = mk('div', '', 'display:flex;gap:8px;align-items:center;margin-bottom:6px;');
        const inp = mk('input'); inp.type = 'color'; inp.value = cfg.folderColor || '#c0f0f8';
        inp.style.cssText = 'width:48px;height:36px;border:1px solid rgba(255,255,255,.1);border-radius:8px;background:rgba(255,255,255,.07);cursor:pointer;padding:3px;flex-shrink:0;';
        const fcHex = mk('input'); fcHex.type = 'text'; fcHex.value = inp.value; fcHex.maxLength = 7;
        fcHex.style.cssText = 'flex:1;background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.1);border-radius:8px;color:#fff;padding:8px 10px;font-size:12px;font-family:monospace;outline:none;min-width:0;cursor:text;';
        fcHex.onfocus = () => fcHex.style.borderColor = 'rgba(255,255,255,.3)';
        fcHex.onblur = () => fcHex.style.borderColor = 'rgba(255,255,255,.1)';
        inp.oninput = () => { fcHex.value = inp.value; };
        fcHex.oninput = () => { const v = fcHex.value.startsWith('#') ? fcHex.value : '#' + fcHex.value; if (/^#[0-9a-fA-F]{6}$/.test(v)) inp.value = v; };
        fcRow.append(inp, fcHex);
        fels.folderColor = inp; box.appendChild(fcRow);
        let useParentColorState = !!cfg.useParentColor;
        if (cfg.inheritedColor) {
          const upcRow = mk('div', '', 'display:flex;align-items:center;gap:8px;margin-bottom:14px;');
          const preview = mk('div', '', `width:18px;height:18px;border-radius:4px;background:${cfg.inheritedColor};flex-shrink:0;border:1px solid rgba(255,255,255,.2);`);
          const upcBtn = mk('button', '', `display:flex;align-items:center;gap:6px;flex:1;background:${useParentColorState ? 'rgba(255,255,255,.1)' : 'transparent'};border:1px solid rgba(255,255,255,.12);border-radius:8px;color:${useParentColorState ? '#fff' : 'rgba(255,255,255,.4)'};padding:6px 10px;cursor:pointer;font-size:11px;font-family:"Open Sans",sans-serif;transition:all .15s;`);
          const upcChk = mk('span', '', 'width:13px;height:13px;border:1.5px solid currentColor;border-radius:3px;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;font-size:9px;');
          upcChk.textContent = useParentColorState ? '✓' : '';
          const upcTxt = mk('span'); upcTxt.textContent = 'Use parent color';
          upcBtn.append(upcChk, upcTxt);
          upcBtn.onclick = e => {
            e.preventDefault(); e.stopPropagation();
            useParentColorState = !useParentColorState;
            upcChk.textContent = useParentColorState ? '✓' : '';
            upcBtn.style.background = useParentColorState ? 'rgba(255,255,255,.1)' : 'transparent';
            upcBtn.style.color = useParentColorState ? '#fff' : 'rgba(255,255,255,.4)';
            fcRow.style.opacity = useParentColorState ? '.35' : '1';
            fcRow.style.pointerEvents = useParentColorState ? 'none' : '';
          };
          if (useParentColorState) { fcRow.style.opacity = '.35'; fcRow.style.pointerEvents = 'none'; }
          upcRow.append(preview, upcBtn);
          box.appendChild(upcRow);
        } else {
          fcRow.style.marginBottom = '14px';
        }
        fels.useParentColor = { get checked() { return useParentColorState; }, type: 'checkbox' };
      }
      if (cfg.showColors) {
        const lbl = mk('label', '', 'display:block;font-size:10px;font-weight:700;letter-spacing:.8px;text-transform:uppercase;color:rgba(255,255,255,.4);margin-bottom:6px;');
        lbl.textContent = 'Gradient Colors (2 to 6)';
        box.appendChild(lbl);
        const colorRow = mk('div', '', 'display:flex;gap:5px;margin-bottom:10px;align-items:center;');
        let currentColors = [...(cfg.colors || ['#c0f0f8', '#183848'])];
        if (currentColors.length < 2) currentColors.push('#183848');
        const prev = mk('div', '', 'height:10px;border-radius:5px;margin-bottom:10px;opacity:.8;');
        const upd = () => { prev.style.background = `linear-gradient(to right, ${currentColors.join(', ')})`; };
        const allPresets = [
          { name: 'Volcanic Rouge Red', colors: ['#1a0000', '#8b1a00', '#ff4500', '#ffaa00'] },
          ...GRADIENT_PRESETS,
        ];
        try {
          const stored = JSON.parse(localStorage.getItem('mmapp_custom_presets') || '[]');
          stored.forEach(p => { if (p.name && p.colors) allPresets.push(p); });
        } catch (_) {}
        const showAllGradientsModal = (presets, applyFn) => {
          const ov2 = mk('div', '', 'position:fixed;inset:0;background:rgba(0,0,0,.75);z-index:999999;display:flex;align-items:center;justify-content:center;');
          const box2 = mk('div', '', 'background:rgb(37,37,33);border:1px solid rgba(255,255,255,.1);border-radius:12px;padding:20px;width:400px;height:500px;max-height:85vh;display:flex;flex-direction:column;box-shadow:0 16px 48px rgba(0,0,0,.8);');
          const topRow = mk('div', '', 'display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;flex-shrink:0;');
          const title = mk('div', '', 'color:#fff;font-weight:700;font-size:15px;font-family:"Open Sans",sans-serif;'); title.textContent = 'All Presets';
          const closeBtn = mk('button', '', 'background:transparent;border:none;color:rgba(255,255,255,.5);cursor:pointer;font-size:16px;line-height:1;padding:0;transition:color .15s;'); closeBtn.innerHTML = '✕';
          closeBtn.onmouseenter = () => closeBtn.style.color = '#fff';
          closeBtn.onmouseleave = () => closeBtn.style.color = 'rgba(255,255,255,.5)';
          closeBtn.onclick = () => ov2.remove();
          topRow.append(title, closeBtn);
          const searchInput = mk('input', '', 'background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.1);border-radius:6px;color:#fff;padding:9px 12px;font-size:12px;margin-bottom:15px;outline:none;font-family:"Open Sans",sans-serif;');
          searchInput.placeholder = 'Search "blue", "rose", "dark"...';
          searchInput.onfocus = () => searchInput.style.borderColor = 'rgba(255,255,255,.3)';
          searchInput.onblur = () => searchInput.style.borderColor = 'rgba(255,255,255,.1)';
          const grid = mk('div', '', 'display:grid;grid-template-columns:repeat(5, 1fr);gap:6px;overflow-y:auto;padding:2px 4px;margin:0 -4px;flex:1;min-height:0;align-content:start;');
          grid.style.scrollbarWidth = 'thin';
          grid.style.scrollbarColor = 'rgba(255,255,255,.2) transparent';
          const renderGrid = filterText => {
            grid.innerHTML = '';
            let term = filterText.toLowerCase();
            const map = { 'bleu': 'blue', 'rouge': 'red', 'vert': 'green', 'jaune': 'yellow', 'noir': 'dark', 'blanc': 'white' };
            Object.entries(map).forEach(([fr, en]) => { term = term.replace(fr, en); });
            const filtered = presets.filter(p => p.name.toLowerCase().includes(term));
            if (!filtered.length) {
              const noRes = mk('div', '', 'grid-column:1/-1;color:rgba(255,255,255,.4);font-size:12px;text-align:center;padding:10px;');
              noRes.textContent = 'No preset found.';
              grid.appendChild(noRes);
              return;
            }
            filtered.forEach(p => {
              const chip = mk('div', '', `height:28px;border-radius:6px;cursor:pointer;background:linear-gradient(to right,${p.colors.join(',')});outline:1.5px solid transparent;outline-offset:1px;transition:outline-color .12s;`);
              chip.title = p.name;
              chip.onmouseenter = () => chip.style.outlineColor = 'rgba(255,255,255,.75)';
              chip.onmouseleave = () => chip.style.outlineColor = 'transparent';
              chip.onclick = () => { applyFn(p.colors, p); ov2.remove(); };
              grid.appendChild(chip);
            });
          };
          searchInput.oninput = e => renderGrid(e.target.value);
          renderGrid('');
          box2.append(topRow, searchInput, grid);
          ov2.appendChild(box2);
          ov2.onclick = e => { if (e.target === ov2) ov2.remove(); };
          document.body.appendChild(ov2);
          setTimeout(() => searchInput.focus(), 30);
        };
        let renderRecents = () => {};
        const buildChip = (p, applyFn) => {
          const chip = mk('div', '', `height:22px;border-radius:5px;cursor:pointer;background:linear-gradient(to right,${p.colors.join(',')});outline:1.5px solid transparent;outline-offset:1px;transition:outline-color .12s;box-sizing:border-box;`);
          chip.title = p.name;
          chip.onmouseenter = () => chip.style.outlineColor = 'rgba(255,255,255,.75)';
          chip.onmouseleave = () => chip.style.outlineColor = 'transparent';
          chip.onclick = e => {
            e.preventDefault(); e.stopPropagation();
            saveRecentPreset(p);
            applyFn(p.colors);
            renderRecents();
          };
          return chip;
        };
        const recentsWrap = mk('div', '', 'margin-bottom:10px;');
        const recentsHeaderRow = mk('div', '', 'display:flex;align-items:center;justify-content:space-between;margin-bottom:5px;');
        const recentsLbl = mk('div', '', 'font-size:9px;font-weight:700;letter-spacing:.7px;text-transform:uppercase;color:rgba(255,255,255,.3);');
        recentsLbl.textContent = 'Recently Used';
        recentsHeaderRow.appendChild(recentsLbl);
        recentsWrap.appendChild(recentsHeaderRow);
        const recentsGrid = mk('div', '', 'display:grid;grid-template-columns:repeat(4,1fr);gap:4px;');
        recentsWrap.appendChild(recentsGrid);
        renderRecents = () => {
          if (!recentPresets.length) { recentsWrap.style.display = 'none'; return; }
          recentsWrap.style.display = 'block';
          recentsGrid.innerHTML = '';
          recentPresets.slice(0, 4).forEach(p => recentsGrid.appendChild(buildChip(p, colors => {
            currentColors = [...colors]; renderInputs(); upd();
          })));
        };
        box.appendChild(recentsWrap);
        renderRecents();
        const presetsWrap = mk('div', '', 'margin-bottom:10px;');
        const presetsHeaderRow = mk('div', '', 'display:flex;align-items:center;justify-content:space-between;margin-bottom:5px;');
        const presetsLbl = mk('div', '', 'font-size:9px;font-weight:700;letter-spacing:.7px;text-transform:uppercase;color:rgba(255,255,255,.3);');
        presetsLbl.textContent = 'Presets';
        const expandBtn = mk('button', '', 'background:transparent;border:none;color:rgba(255,255,255,.4);font-size:9px;cursor:pointer;padding:2px 6px;font-family:"Open Sans",sans-serif;letter-spacing:.5px;border-radius:4px;border:1px solid rgba(255,255,255,.15);transition:background .12s,color .12s,border-color .12s;');
        expandBtn.textContent = `See all (${allPresets.length}) ↗`;
        expandBtn.onmouseenter = () => { expandBtn.style.background = 'rgba(255,255,255,.08)'; expandBtn.style.color = '#fff'; expandBtn.style.borderColor = 'rgba(255,255,255,.3)'; };
        expandBtn.onmouseleave = () => { expandBtn.style.background = 'transparent'; expandBtn.style.color = 'rgba(255,255,255,.4)'; expandBtn.style.borderColor = 'rgba(255,255,255,.15)'; };
        presetsHeaderRow.append(presetsLbl, expandBtn);
        presetsWrap.appendChild(presetsHeaderRow);
        const presetsGrid = mk('div', '', 'display:grid;grid-template-columns:repeat(4,1fr);gap:4px;');
        const PRESETS_VISIBLE = 16;
        const renderPresets = () => {
          presetsGrid.innerHTML = '';
          allPresets.slice(0, PRESETS_VISIBLE).forEach(p => presetsGrid.appendChild(buildChip(p, colors => {
            currentColors = [...colors]; renderInputs(); upd();
          })));
          expandBtn.textContent = `See all (${allPresets.length}) ↗`;
        };
        expandBtn.onclick = e => {
          e.preventDefault(); e.stopPropagation();
          showAllGradientsModal(allPresets, (colors, preset) => {
            currentColors = [...colors];
            if (preset) { saveRecentPreset(preset); renderRecents(); }
            renderInputs(); upd();
          });
        };
        presetsWrap.appendChild(presetsGrid);
        const savePresetRow = mk('div', '', 'display:flex;gap:5px;align-items:center;margin-top:5px;');
        const savePresetBtn = mk('button', '', 'background:rgba(255,255,255,.07);border:1px dashed rgba(255,255,255,.2);border-radius:5px;color:rgba(255,255,255,.4);font-size:10px;cursor:pointer;padding:3px 8px;font-family:"Open Sans",sans-serif;transition:all .12s;flex:1;');
        savePresetBtn.textContent = '+ Save current as preset';
        savePresetBtn.onmouseenter = () => { savePresetBtn.style.color = '#fff'; savePresetBtn.style.borderColor = 'rgba(255,255,255,.5)'; };
        savePresetBtn.onmouseleave = () => { savePresetBtn.style.color = 'rgba(255,255,255,.4)'; savePresetBtn.style.borderColor = 'rgba(255,255,255,.2)'; };
        savePresetBtn.onclick = e => {
          e.preventDefault(); e.stopPropagation();
          const name = prompt('Preset name?', 'My gradient');
          if (!name) return;
          const custom = { name, colors: [...currentColors], custom: true };
          allPresets.push(custom);
          try {
            const stored = JSON.parse(localStorage.getItem('mmapp_custom_presets') || '[]');
            stored.push(custom);
            localStorage.setItem('mmapp_custom_presets', JSON.stringify(stored));
          } catch (_) {}
          saveRecentPreset(custom);
          renderRecents();
          renderPresets();
        };
        savePresetRow.appendChild(savePresetBtn);
        presetsWrap.appendChild(savePresetRow);
        renderPresets();
        box.append(presetsWrap);
        const renderInputs = () => {
          colorRow.innerHTML = '';
          currentColors.forEach((c, idx) => {
            const colWrap = mk('div', '', 'display:flex;flex-direction:column;gap:3px;flex:1;min-width:0;');
            const inp = mk('input'); inp.type = 'color'; inp.value = c;
            inp.style.cssText = 'width:100%;height:28px;border:1px solid rgba(255,255,255,.1);border-radius:6px;background:rgba(255,255,255,.07);cursor:pointer;padding:1px;';
            const hexEl = mk('input'); hexEl.type = 'text'; hexEl.value = c; hexEl.maxLength = 7;
            hexEl.style.cssText = 'width:100%;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.08);border-radius:5px;color:rgba(255,255,255,.7);padding:3px 5px;font-size:10px;font-family:monospace;outline:none;box-sizing:border-box;';
            hexEl.onfocus = () => hexEl.style.borderColor = 'rgba(255,255,255,.3)';
            hexEl.onblur = () => hexEl.style.borderColor = 'rgba(255,255,255,.08)';
            inp.oninput = () => { currentColors[idx] = inp.value; hexEl.value = inp.value; upd(); };
            hexEl.oninput = () => { const v = hexEl.value.startsWith('#') ? hexEl.value : '#' + hexEl.value; if (/^#[0-9a-fA-F]{6}$/.test(v)) { inp.value = v; currentColors[idx] = v; upd(); } };
            colWrap.append(inp, hexEl);
            colorRow.appendChild(colWrap);
          });
          const controls = mk('div', '', 'display:flex;flex-direction:column;gap:2px;margin-left:5px;');
          const btnAdd = mk('button', '', 'background:rgba(255,255,255,.1);border:none;border-radius:4px;color:#fff;width:20px;height:15px;font-size:10px;cursor:pointer;display:flex;align-items:center;justify-content:center;');
          btnAdd.textContent = '+';
          btnAdd.onclick = e => { e.preventDefault(); if (currentColors.length < 6) { currentColors.push(currentColors[currentColors.length - 1]); renderInputs(); upd(); } };
          const btnRem = mk('button', '', 'background:rgba(255,255,255,.1);border:none;border-radius:4px;color:#fff;width:20px;height:15px;font-size:10px;cursor:pointer;display:flex;align-items:center;justify-content:center;');
          btnRem.textContent = '-';
          btnRem.onclick = e => { e.preventDefault(); if (currentColors.length > 2) { currentColors.pop(); renderInputs(); upd(); } };
          const btnInv = mk('button', '', 'background:rgba(255,255,255,.1);border:none;border-radius:4px;color:#fff;width:20px;height:15px;font-size:11px;cursor:pointer;display:flex;align-items:center;justify-content:center;margin-top:2px;');
          btnInv.innerHTML = '⇄'; btnInv.title = 'Invert gradient';
          btnInv.onclick = e => { e.preventDefault(); currentColors.reverse(); renderInputs(); upd(); };
          controls.append(btnAdd, btnRem, btnInv);
          colorRow.appendChild(controls);
        };
        renderInputs();
        upd();
        box.append(colorRow, prev);
        let gradTagsState = cfg.gradTags !== false && !cfg.noGrad;
        let gradFoldersState = !!cfg.gradFolders && !cfg.noGrad;
        const mkToggleBtn = (label, active) => {
          const btn = mk('button', '', `display:flex;align-items:center;gap:8px;width:100%;background:${active ? 'rgba(255,255,255,.1)' : 'transparent'};border:1px solid rgba(255,255,255,.12);border-radius:8px;color:${active ? '#fff' : 'rgba(255,255,255,.4)'};padding:7px 12px;cursor:pointer;font-size:11px;font-family:"Open Sans",sans-serif;margin-bottom:6px;transition:all .15s;text-align:left;`);
          const chk = mk('span', '', 'width:14px;height:14px;border:1.5px solid currentColor;border-radius:3px;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;font-size:10px;');
          chk.textContent = active ? '✓' : '';
          const txt = mk('span'); txt.textContent = label;
          btn.append(chk, txt);
          btn._isActive = active;
          btn._chk = chk;
          btn._toggle = () => {
            btn._isActive = !btn._isActive;
            chk.textContent = btn._isActive ? '✓' : '';
            btn.style.background = btn._isActive ? 'rgba(255,255,255,.1)' : 'transparent';
            btn.style.color = btn._isActive ? '#fff' : 'rgba(255,255,255,.4)';
          };
          return btn;
        };
        const btnGradTags = mkToggleBtn('Apply gradient to tags', gradTagsState);
        const btnGradFolders = mkToggleBtn('Apply gradient to sub-folders', gradFoldersState);
        const syncStates = () => {
          const noGrad = !btnGradTags._isActive && !btnGradFolders._isActive;
          colorRow.style.opacity = noGrad ? '.35' : '1';
          colorRow.style.pointerEvents = noGrad ? 'none' : '';
          prev.style.opacity = noGrad ? '.35' : '.8';
        };
        btnGradTags.onclick = e => { e.preventDefault(); e.stopPropagation(); btnGradTags._toggle(); gradTagsState = btnGradTags._isActive; syncStates(); };
        btnGradFolders.onclick = e => { e.preventDefault(); e.stopPropagation(); btnGradFolders._toggle(); gradFoldersState = btnGradFolders._isActive; syncStates(); };
        syncStates();
        box.append(btnGradTags, btnGradFolders);
        fels.noGrad = { get checked() { return !btnGradTags._isActive && !btnGradFolders._isActive; }, type: 'checkbox' };
        fels.gradTags = { get checked() { return gradTagsState; }, type: 'checkbox' };
        fels.gradFolders = { get checked() { return gradFoldersState; }, type: 'checkbox' };
        fels.gradientColors = { get value() { return currentColors; }, type: 'custom' };
      }
      const acts = mk('div', '', 'display:flex;gap:8px;justify-content:flex-end;margin-top:4px;');
      const bC = mk('button', '', 'background:transparent;border:1px solid rgba(255,255,255,.15);border-radius:8px;color:rgba(255,255,255,.5);padding:8px 18px;cursor:pointer;font-size:12px;font-family:"Open Sans",sans-serif;'); bC.textContent = 'Cancel';
      const bO = mk('button', '', 'background:#fff;border:none;border-radius:8px;color:#252521;padding:8px 18px;cursor:pointer;font-size:12px;font-weight:700;font-family:"Open Sans",sans-serif;'); bO.textContent = cfg.okText || 'OK';
      bC.onmouseenter = () => bC.style.borderColor = 'rgba(255,255,255,.35)'; bC.onmouseleave = () => bC.style.borderColor = 'rgba(255,255,255,.15)';
      bO.onmouseenter = () => bO.style.background = 'rgba(255,255,255,.85)'; bO.onmouseleave = () => bO.style.background = '#fff';
      acts.append(bC, bO); box.appendChild(acts); ov.appendChild(box); document.body.appendChild(ov);
      bO.onclick = confirm_;
      bC.onclick = cancel_;
      ov.onclick = e => { if (e.target === ov) cancel_(); };
      setTimeout(() => box.querySelector('input[type=text]')?.focus(), 30);
    });
  }

  let _ctx = null;
  const closeCtx = () => { _ctx?.remove(); _ctx = null; };

  function showCtx(x, y, items) {
    closeCtx();
    const m = mk('div', '', `position:fixed;left:${x}px;top:${y}px;background:rgba(22,22,19,0.85);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,0.08);border-radius:10px;padding:6px;z-index:100000;min-width:240px;max-height:45vh;overflow-y:auto;box-shadow:0 8px 32px rgba(0,0,0,0.5);font-family:"Open Sans",sans-serif;font-size:13px;color:#e8e8e8;`, { 'data-mm': '1' });
    m.style.scrollbarWidth = 'thin';
    m.style.scrollbarColor = 'rgba(255,255,255,.2) transparent';
    _ctx = m;
    items.forEach(item => {
      if (item === '---') { const s = mk('div', '', 'height:1px;background:rgba(255,255,255,.08);margin:4px 0;'); m.appendChild(s); return; }

      const d = mk('div', '', 'padding:6px 10px;cursor:pointer;border-radius:6px;display:flex;align-items:center;gap:8px;position:relative;transition:background 0.1s;');
      d.onmouseenter = () => d.style.background = 'rgba(255,255,255,.08)';
      d.onmouseleave = () => d.style.background = 'transparent';

      if (item.html) {
        d.innerHTML = item.html;
      } else if (item.sub?.length) {
        d.innerHTML = `<span style="min-width:16px;opacity:.6">${item.icon || ''}</span><span style="flex:1">${item.label}</span><span style="opacity:.3;font-size:9px">▶</span>`;
        const sub = mk('div', '', `display:none;position:absolute;left:calc(100% + 4px);top:-4px;background:rgba(22,22,19,0.85);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,0.08);border-radius:10px;padding:6px;min-width:180px;max-height:300px;overflow-y:auto;box-shadow:0 8px 32px rgba(0,0,0,0.5);z-index:100001;font-family:"Open Sans",sans-serif;font-size:13px;`);
        item.sub.forEach(s => {
          const si = mk('div', '', 'padding:6px 10px;cursor:pointer;border-radius:6px;display:flex;align-items:center;gap:8px;white-space:nowrap;color:#e8e8e8;');
          si.onmouseenter = () => si.style.background = 'rgba(255,255,255,.08)';
          si.onmouseleave = () => si.style.background = 'transparent';
          si.innerHTML = `<span style="min-width:16px;opacity:.6">${s.icon || ''}</span><span>${s.label}</span>`;
          si.onclick = e => { e.stopPropagation(); closeCtx(); s.action?.(); };
          sub.appendChild(si);
        });
        d.appendChild(sub);
        d.onmouseenter = () => { d.style.background = 'rgba(255,255,255,.08)'; sub.style.display = 'block'; };
        d.onmouseleave = () => { d.style.background = 'transparent'; sub.style.display = 'none'; };
      } else {
        d.innerHTML = `<span style="min-width:16px;opacity:.6">${item.icon || ''}</span><span>${item.label}</span>`;
      }

      d.onclick = e => { e.stopPropagation(); closeCtx(); item.action?.(); };
      m.appendChild(d);
    });
    document.body.appendChild(m);
    requestAnimationFrame(() => {
      const r = m.getBoundingClientRect();
      if (r.right > window.innerWidth) m.style.left = (x - r.width - 8) + 'px';
      if (r.bottom > window.innerHeight) m.style.top = (y - r.height - 8) + 'px';
    });
    setTimeout(() => document.addEventListener('click', closeCtx, { once: true }), 0);
  }

  function clickNative(tagName) {
    const li = liOfTag(tagName);
    if (!li) return;
    (li.querySelector('label.tag_text, label') || li).click();
  }

  async function setNativeTagColor(tagName, hexColor, finalDelay = 100) {
    const li = liOfTag(tagName);
    if (!li) return false;
    const editBtn = li.querySelector('button.tag_button--edit, button[class*="edit"]');
    if (!editBtn) return false;
    editBtn.click();
    const hexInput = await waitDOM('div[role="dialog"] input.input.hex-color, div[role="dialog"] input[class*="hex-color"]');
    if (!hexInput) {
      console.warn(`[mm-folders] setNativeTagColor: hex input not found for tag "${tagName}"`);
      const dialog = document.querySelector('div[role="dialog"]');
      dialog?.querySelectorAll('button')[0]?.click();
      return false;
    }
    const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;
    const hex = hexColor.replace('#', '');
    if (nativeSetter) nativeSetter.call(hexInput, hex);
    else hexInput.value = hex;
    hexInput.dispatchEvent(new Event('input', { bubbles: true }));
    hexInput.dispatchEvent(new Event('change', { bubbles: true }));
    await new Promise(r => setTimeout(r, 80));
    const dialog = hexInput.closest('div[role="dialog"]');
    const btns = dialog?.querySelectorAll('.edit-tag-modal__actions button, button.save');
    btns?.[btns.length - 1]?.click();
    await new Promise(r => setTimeout(r, finalDelay));
    return true;
  }

  async function applyFolderColorsToNative(folder) {
    let failed = 0;
    if (folder.gradTags !== false && !folder.noGrad) {
      const colors = gradColors(folder);
      for (let i = 0; i < folder.tags.length; i++) {
        const ok = await setNativeTagColor(folder.tags[i], rgbToHex(colors[i] || folder.colorStart || '#c0f0f8'), 120);
        if (!ok) failed++;
      }
    }
    for (const child of folder.children || []) await applyFolderColorsToNative(child);
    return failed;
  }

  async function applyTagToLoc(tagName) {
    const searchInput = document.querySelector(
      'section.location-preview input[type="text"], .location-preview-tags input, input[placeholder*="tag" i], input[placeholder*="Add"]'
    );
    if (!searchInput) return false;
    const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;
    const setVal = v => {
      if (nativeSetter) nativeSetter.call(searchInput, v);
      else searchInput.value = v;
      searchInput.dispatchEvent(new Event('input', { bubbles: true }));
      searchInput.dispatchEvent(new Event('change', { bubbles: true }));
    };
    searchInput.focus();
    setVal(tagName);
    return new Promise(resolve => {
      let tries = 0;
      const interval = setInterval(() => {
        tries++;
        const ol = getLocOl();
        if (ol) {
          for (const li of ol.querySelectorAll('li:not([data-mm])')) {
            const span = li.querySelector('span.tag_text, span[class*="tag_text"]');
            const name = (span?.textContent || '').trim();
            if (name !== tagName) continue;
            const btn = li.querySelector('button[class*="add"]') || li.querySelector('button');
            (btn || li).click();
            clearInterval(interval);
            setTimeout(() => { setVal(''); searchInput.blur(); }, 80);
            resolve(true);
            return;
          }
        }
        if (tries > 20) { clearInterval(interval); resolve(false); }
      }, 60);
    });
  }

  async function newFolder(parentId = null) {
    const parent = parentId ? findF(parentId) : null;
    const r = await modal({
      title: parentId ? 'New subfolder' : 'New folder',
      fields: [{ key: 'name', label: 'Name', type: 'text', placeholder: 'e.g. Guardrails' }],
      showFolderColor: true, folderColor: parent?.color || '#c0f0f8',
      showColors: true, colors: parent?.gradient || ['#c0f0f8', '#183848'],
      noGrad: parent?.noGrad || false,
      gradTags: parent?.gradTags !== false,
      gradFolders: !!parent?.gradFolders,
      okText: 'Create'
    });
    if (!r?.name?.trim()) return;
    const gradientColors = r.gradientColors || ['#c0f0f8', '#183848'];
    const f = {
      id: uid(), name: r.name.trim(),
      color: r.folderColor || '#c0f0f8',
      gradient: gradientColors, colorStart: gradientColors[0], colorEnd: gradientColors[gradientColors.length - 1],
      noGrad: !!r.noGrad, gradTags: r.gradTags !== false, gradFolders: !!r.gradFolders,
      expanded: true, visible: true, tags: [], children: []
    };
    if (parentId) { const p = findF(parentId); if (p) { p.children.push(f); saveS(); reRenderFolders(); return; } }
    S.folders.push(f); saveS(); reRenderFolders();
  }

  async function editFolder(id) {
    const f = findF(id); if (!f) return;
    const r = await modal({
      title: 'Edit folder',
      fields: [{ key: 'name', label: 'Name', type: 'text', value: f.name }],
      showFolderColor: true, folderColor: f.color || '#c0f0f8',
      inheritedColor: f._inheritedColor || null,
      useParentColor: !!f.useParentColor,
      showColors: true, colors: f.gradient || ['#c0f0f8', '#183848'],
      noGrad: f.noGrad || false, gradTags: f.gradTags !== false, gradFolders: !!f.gradFolders,
      okText: 'Save'
    });
    if (!r?.name?.trim()) return;
    const gradientColors = r.gradientColors || ['#c0f0f8', '#183848'];
    f.name = r.name.trim(); f.color = r.folderColor || f.color;
    f.gradient = gradientColors; f.colorStart = gradientColors[0]; f.colorEnd = gradientColors[gradientColors.length - 1];
    f.noGrad = !!r.noGrad; f.gradTags = r.gradTags !== false; f.gradFolders = !!r.gradFolders;
    f.useParentColor = !!r.useParentColor;
    saveS(); reRenderFolders();
  }

  function isFolderFullyOn(folder) {
    const tags = allTagsOf(folder).filter(tag => !!liOfTag(tag));
    if (!tags.length) return false;
    return tags.every(tag => liOfTag(tag)?.classList.contains('is-selected'));
  }

  function isFolderPartiallyOn(folder) {
    return allTagsOf(folder).some(tag => liOfTag(tag)?.classList.contains('is-selected'));
  }

  function toggleVis(folder) {
  const targetState = !isFolderFullyOn(folder);
  const tagsToToggle = allTagsOf(folder).filter(tag => {
    const li = liOfTag(tag);
    if (!li) return false;
    return targetState !== li.classList.contains('is-selected');
  });

  if (!tagsToToggle.length) return;

  // 1. Mise à jour visuelle immédiate du folder header
  const folderEl = document.querySelector(`[data-mm-folder="${folder.id}"]`);
  const hd = folderEl ? Array.from(folderEl.children).find(c => c.classList.contains('mm-hd')) : null;
  if (hd) {
    hd.classList.remove('mm-hd--on', 'mm-hd--off', 'mm-hd--partial');
    hd.classList.add(targetState ? 'mm-hd--on' : 'mm-hd--off');
  }

  // 2. Mise à jour visuelle immédiate des tag buttons
  tagsToToggle.forEach(tag => {
    const btn = Array.from(document.querySelectorAll('.mm-tag-btn')).find(b => {
      let t = '';
      b.querySelectorAll('span').forEach(s => { if (!s.querySelector('svg')) t = s.textContent.trim(); });
      return t === tag;
    });
    if (btn) {
      if (targetState) btn.classList.add('mm-tag-btn--on');
      else btn.classList.remove('mm-tag-btn--on');
    }
  });

  // 3. Clics natifs en batch asynchrone par paquets de 5
  folder.visible = targetState;
  saveS(false);

  const BATCH_SIZE = 25;
  const BATCH_DELAY = 1; // ms entre chaque batch

  const runBatch = (index) => {
    const batch = tagsToToggle.slice(index, index + BATCH_SIZE);
    batch.forEach(tag => clickNative(tag));
    if (index + BATCH_SIZE < tagsToToggle.length) {
      setTimeout(() => runBatch(index + BATCH_SIZE), BATCH_DELAY);
    } else {
      // Sync final après tous les clics
      setTimeout(updateAllFolderVisuals, 60);
    }
  };

  runBatch(0);
}

  // ─── TAG BUTTON ──────────────────────────────────────────────────────────────

  function buildTagBtn(tag, color) {
    const li = liOfTag(tag);
    const count = countFromLi(li);
    const bg = color || getTagColor(li) || '#5a5a8a';
    const fg = textOn(bg);
    const isOn = li?.classList.contains('is-selected') ?? false;

    const btn = mk('button', 'mm-tag-btn' + (isOn ? ' mm-tag-btn--on' : ''), '', { 'data-mm': '1', 'draggable': 'true' });
    btn.style.backgroundColor = bg;
    btn.style.color = fg;

    btn.addEventListener('dragover', e => {
      if (dragTag && dragTag !== tag) { e.preventDefault(); e.stopPropagation(); btn.classList.add('mm-tag-drop-indicator'); }
    });
    btn.addEventListener('dragleave', () => btn.classList.remove('mm-tag-drop-indicator'));
    btn.addEventListener('drop', e => {
      if (dragTag && dragTag !== tag) {
        e.preventDefault(); e.stopPropagation();
        btn.classList.remove('mm-tag-drop-indicator');
        reorderTag(dragTag, tag); dragTag = null;
      }
    });

    const pen = mk('span', '', 'opacity:.7;flex-shrink:0;display:inline-flex;align-items:center;cursor:pointer;');
    pen.innerHTML = SVG_PEN;
    pen.addEventListener('click', e => {
      e.stopPropagation(); e.preventDefault();
      const nat = liOfTag(tag);
      if (!nat) return;
      nat.querySelector('button.tag_button--edit, button[class*="edit"]')?.click();
    });
    btn.appendChild(pen);

    const nameSp = mk('span', '', 'pointer-events:none;');
    nameSp.textContent = tag;
    btn.appendChild(nameSp);

    if (count) {
      const cSp = mk('small', '', 'margin-left:0.375rem;font-weight:600;vertical-align:middle;pointer-events:none;');
      cSp.textContent = count;
      btn.appendChild(cSp);
    }

    btn.onclick = async e => {
      if (e.target === pen || pen.contains(e.target)) return;
      e.stopPropagation();
      if (getLocOl()) {
        await applyTagToLoc(tag);
      } else {
        clickNative(tag);
        setTimeout(updateAllFolderVisuals, 60);
      }
    };

    btn.addEventListener('dragstart', e => {
      if (e.target === pen || pen.contains(e.target)) { e.preventDefault(); return; }
      dragTag = tag;
      try { e.dataTransfer.setData('text/plain', tag); } catch (_) {}
      btn.style.opacity = '.45';
    });
    btn.addEventListener('dragend', () => { btn.style.opacity = ''; dragTag = null; });

    btn.addEventListener('contextmenu', e => {
      e.preventDefault();
      e.stopPropagation();

      const natLi = liOfTag(tag);
      if (!natLi) return;

      const observer = new MutationObserver((mutations, obs) => {
        const nativeMenu = document.querySelector('[data-side="right"][role="menu"].context-menu, [role="menu"].context-menu');
        if (nativeMenu) {
          if (!nativeMenu.querySelector('[data-mm-injected]')) {
            injectIntoNativeMenu(tag, nativeMenu);
          }
          obs.disconnect();

          natLi.style.removeProperty('position');
          natLi.style.removeProperty('left');
          natLi.style.removeProperty('top');
          natLi.style.removeProperty('opacity');
          natLi.style.removeProperty('pointer-events');
          natLi.style.removeProperty('z-index');
          natLi.style.setProperty('display', 'none', 'important');
        }
      });
      observer.observe(document.body, { childList: true, subtree: true });
      setTimeout(() => observer.disconnect(), 500);

      natLi.style.removeProperty('display');
      natLi.style.setProperty('position', 'fixed', 'important');
      natLi.style.setProperty('left', e.clientX + 'px', 'important');
      natLi.style.setProperty('top', e.clientY + 'px', 'important');
      natLi.style.setProperty('opacity', '0', 'important');
      natLi.style.setProperty('pointer-events', 'auto', 'important');
      natLi.style.setProperty('z-index', '-1', 'important');

      natLi.dispatchEvent(new MouseEvent('contextmenu', {
        bubbles: true, cancelable: true,
        clientX: e.clientX, clientY: e.clientY, button: 2
      }));
    });

    return btn;
  }

  const escapeHTML = (str) => {
    return str.replace(/[&<>'"]/g,
      tag => ({
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        "'": '&#39;',
        '"': '&quot;'
      }[tag] || tag)
    );
  };

  // ─── INJECTION DANS LE MENU NATIF ────────────────────────────────────────────

  function injectIntoNativeMenu(tag, nativeMenu) {
    const cur = folderOf(tag);
    const allF = flat();

    // Ligne séparatrice
    const sep = document.createElement('div');
    sep.setAttribute('data-mm-injected', '1');
    sep.style.cssText = 'height:1px;background:rgba(255,255,255,.1);margin:3px 0;';
    nativeMenu.appendChild(sep);

    if (allF.length) {
      const moveItem = document.createElement('div');
      moveItem.setAttribute('data-mm-injected', '1');
      moveItem.setAttribute('role', 'menuitem');
      moveItem.setAttribute('tabindex', '-1');
      moveItem.className = 'context-menu__item';
      moveItem.textContent = ' Move to folder...';

      moveItem.onclick = e => {
        e.stopPropagation();
        const rect = moveItem.getBoundingClientRect();
        document.body.click();

        const getDepth = (id, list = S.folders, currentDepth = 0) => {
          for (const f of list) {
            if (f.id === id) return currentDepth;
            const d = getDepth(id, f.children || [], currentDepth + 1);
            if (d !== -1) return d;
          }
          return -1;
        };

        const cur2 = folderOf(tag);

        // Construction du menu façon tooltip
        setTimeout(() => {
          showCtx(rect.right + 4, rect.top, flat().map(f => {
            const depth = getDepth(f.id);
            const bg = f.color || '#c0f0f8';
            const isCurrent = f.id === cur2?.id;

            const pad = depth * 14;
            // Création du connecteur "L" pour les sous dossiers
            let connector = '';
            if (depth > 0) {
              connector = `
                <div style="position:absolute;left:${pad - 8}px;top:0;height:50%;border-left:1.5px solid rgba(255,255,255,0.15);border-bottom:1.5px solid rgba(255,255,255,0.15);width:6px;border-bottom-left-radius:3px;"></div>
              `;
            }

            const html = `
              <div style="display:flex;align-items:center;gap:6px;padding-left:${pad}px;position:relative;width:100%;">
                ${connector}
                <div style="width:8px;height:8px;border-radius:50%;background:${bg};box-shadow:0 0 5px ${bg}55;flex-shrink:0;"></div>
                <span style="font-weight:${isCurrent ? '700' : '600'};color:${isCurrent ? '#fff' : '#dcdcdc'};flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;letter-spacing:0.01em;">${escapeHTML(f.name)}</span>
                ${isCurrent ? '<span style="color:rgba(255,255,255,0.5);font-size:11px;font-weight:400;margin-left:4px;">✓</span>' : ''}
              </div>
            `;

            return {
              html,
              action: () => moveTag(tag, f.id)
            };
          }));
        }, 50);
      };

      nativeMenu.appendChild(moveItem);
    }

    if (cur) {
      const removeItem = document.createElement('div');
      removeItem.setAttribute('data-mm-injected', '1');
      removeItem.setAttribute('role', 'menuitem');
      removeItem.setAttribute('tabindex', '-1');
      removeItem.className = 'context-menu__item';
      removeItem.textContent = `Remove from "${cur.name}"`;

      removeItem.onclick = (e) => {
        e.preventDefault();
        e.stopPropagation();
        document.body.click();
        setTimeout(() => removeTag(tag), 150);
      };
      nativeMenu.appendChild(removeItem);
    }

    const nativeDelete = Array.from(nativeMenu.querySelectorAll('.context-menu__item')).find(el => el.textContent.toLowerCase().includes('delete') || el.querySelector('.icon-trash') || el.textContent.includes('Supprimer'));
    if (nativeDelete) {
      nativeDelete.addEventListener('click', () => {
        setTimeout(() => removeTag(tag), 150);
      });
    }
  }

  // ─── CONTEXT MENU SUR TAGS NATIFS (mode recherche) ───────────────────────────

  function bindCtxMenu() {
    document.addEventListener('contextmenu', e => {
      if (e.target.closest('[data-mm]')) return;
      const li = e.target.closest('li.tag.has-button');
      if (!li) return;
      const tag = nameFromLi(li);
      if (!tag) return;

      const observer = new MutationObserver((mutations, obs) => {
        const nativeMenu = document.querySelector('[role="menu"].context-menu');
        if (nativeMenu && !nativeMenu.querySelector('[data-mm-injected]')) {
          injectIntoNativeMenu(tag, nativeMenu);
          obs.disconnect();
        }
      });
      observer.observe(document.body, { childList: true, subtree: true });
      setTimeout(() => observer.disconnect(), 400);
    }, true);
  }

  function needsRender() {
    const ol = getLocOl();
    const ul = getMainUl();
    const footer = document.querySelector('.map-meta__actions');
    if (footer && !document.getElementById('mm-footer-actions-wrapper')) return true;
    if (ul) return !ul.querySelector('[data-mm="1"]');
    return false;
  }

    async function deleteFolder(id) {
    const f = findF(id);
    if (!f) return;
    if (!confirm(`Delete folder "${f.name}"?\n\n(The tags inside will be moved back to the main list, they won't be deleted from your map)`)) return;

    delF(id);
    saveS();
    reRenderFolders();
  }

  function buildFolder(folder, depth = 0, isLast = false, parentGradColor = null) {
    const li = mk('li', '', '', { 'data-mm': '1', 'data-mm-folder': folder.id });
    folder._inheritedColor = parentGradColor || null;
    const bg = (parentGradColor && folder.useParentColor !== false) ? parentGradColor : folder.color || '#c0f0f8';
    const fg = textOn(bg);
    const gradArr = folder.gradient || [folder.colorStart || '#3b82f6', folder.colorEnd || '#06b6d4'];
    const grad = `linear-gradient(90deg, ${gradArr.join(', ')})`;
    const colors = gradColors(folder);
    const fullyOn = isFolderFullyOn(folder);
    const partialOn = !fullyOn && isFolderPartiallyOn(folder);
    const hdClass = fullyOn ? ' mm-hd--on' : (partialOn ? ' mm-hd--partial' : ' mm-hd--off');
    const hd = mk('div', 'mm-hd' + hdClass);
    hd.style.backgroundColor = bg;
    hd.style.color = fg;
    dndOn(hd, folder.id);
    hd.setAttribute('draggable', 'true');
    hd.addEventListener('dragstart', e => {
      if (dragTag) return;
      if (e.target.closest('.mm-left-actions, .mm-right-actions')) { e.preventDefault(); return; }
      e.stopPropagation();
      dragFolderId = folder.id;
      setTimeout(() => { if (dragFolderId) li.style.opacity = '.5'; }, 0);
      try { e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/plain', ''); } catch (_) {}
    });
    hd.addEventListener('dragend', () => { li.style.opacity = ''; dragFolderId = null; });
    li.addEventListener('dragover', e => {
      if (!dragFolderId || dragFolderId === folder.id || dragTag) return;
      if (isDescendant(dragFolderId, folder.id)) return;
      e.preventDefault(); e.stopPropagation();
      const rect = hd.getBoundingClientRect();
      const isTopHalf = e.clientY < rect.top + rect.height / 2;
      if (isTopHalf) {
        li.classList.add('mm-drop-above'); li.classList.remove('mm-drop-inside', 'mm-drop-after');
      } else if (isLast && e.clientY > rect.bottom - 6) {
        li.classList.add('mm-drop-after'); li.classList.remove('mm-drop-above', 'mm-drop-inside');
      } else {
        li.classList.add('mm-drop-inside'); li.classList.remove('mm-drop-above', 'mm-drop-after');
      }
    });
    li.addEventListener('dragleave', e => {
      if (!li.contains(e.relatedTarget)) li.classList.remove('mm-drop-above', 'mm-drop-inside', 'mm-drop-after');
    });
    li.addEventListener('drop', e => {
      const wasAbove = li.classList.contains('mm-drop-above');
      const wasAfter = li.classList.contains('mm-drop-after');
      li.classList.remove('mm-drop-above', 'mm-drop-inside', 'mm-drop-after');
      if (!dragFolderId || dragFolderId === folder.id || dragTag) return;
      if (isDescendant(dragFolderId, folder.id)) return;
      e.preventDefault(); e.stopPropagation();
      const id = dragFolderId; dragFolderId = null;
      if (wasAbove) reorderFolder(id, folder.id);
      else if (wasAfter) { const dragged = extractFolder(id); if (dragged) { S.folders.push(dragged); saveS(); reRenderFolders(); } }
      else moveFolderInto(id, folder.id);
    });
    const leftActions = mk('div', 'mm-left-actions');
    const az = mk('div', 'mm-arrow-zone');
    az.innerHTML = SVG_ARR(folder.expanded ? 90 : 0);
  az.onclick = e => {
  e.stopPropagation();
  folder.expanded = !folder.expanded;
  saveS(false);
  // Rotation de la flèche
  const svg = az.querySelector('svg');
  if (svg) svg.style.transform = `rotate(${folder.expanded ? 90 : 0}deg)`;
  if (!folder.expanded) {
    // Collapse : retire juste le contenu de ce dossier
    li.querySelectorAll(':scope > .mm-sub-wrap, :scope > .mm-body').forEach(el => el.remove());
  } else {
    // Expand : construit uniquement le contenu de ce dossier
    if (folder.children?.length) {
      const sw = mk('div', 'mm-sub-wrap', '', { 'data-mm': '1' });
      const childColors = gradChildColors(folder);
      folder.children.forEach((c, ci) => sw.appendChild(buildFolder(c, depth + 1, false, childColors[ci] || null)));
      li.appendChild(sw);
    }
    const body = mk('div', 'mm-body', '', { 'data-mm': '1' });
    dndOn(body, folder.id);
    const tagColors = gradColors(folder);
    if (!folder.tags.length) {
      const dz = mk('div', 'mm-dropzone');
      dz.textContent = '↓ Drop tags here';
      dndOn(dz, folder.id);
      body.appendChild(dz);
    } else {
      folder.tags.forEach((tag, i) => body.appendChild(buildTagBtn(tag, folder.gradTags !== false ? (tagColors[i] || null) : null)));
    }
    const addSub = mk('button', 'mm-subaddbtn', '', { 'data-mm': '1' });
    addSub.textContent = '+ New subfolder';
    addSub.onclick = ev => { ev.stopPropagation(); newFolder(folder.id); };
    body.appendChild(addSub);
    li.appendChild(body);
  }
};
    const pen = mk('span', 'mm-pencil'); pen.innerHTML = SVG_PEN; pen.title = 'Edit'; pen.style.color = fg;
    pen.onclick = e => { e.stopPropagation(); editFolder(folder.id); };
    leftActions.append(az, pen);
    const nameSp = mk('span', '', 'flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;');
    nameSp.textContent = folder.name;
    const gd = mk('div', 'mm-grad'); gd.style.background = grad;
    const cnt = mk('span', 'mm-cnt'); cnt.style.color = fg;
    const tagCount = allTagsOf(folder).length;
    const locCount = allTagsOf(folder).reduce((sum, tag) => sum + (parseInt(countFromLi(liOfTag(tag))) || 0), 0);
    cnt.textContent = locCount > 0 ? `${tagCount} · ${locCount} locs` : tagCount;
    const rightActions = mk('div', 'mm-right-actions');
    const colBtn = mk('button', 'mm-actbtn');
    colBtn.textContent = 'Apply colors';
    colBtn.title = 'Apply gradient colors to native tags';
    colBtn.style.cssText += ';font-size:10px;padding:1px 8px;color:' + fg;
    colBtn.onclick = async e => {
      e.stopPropagation();
      const n = allTagsOf(folder).length;
      if (!n) return;
      const secs = Math.ceil(n * 0.5);
      const timeStr = secs < 60 ? `~${secs} seconds` : `~${Math.ceil(secs / 60)} minutes`;
      if (confirm(`Apply gradient to the ${n} tags of "${folder.name}"?\n\n⚠️ This will modify the actual tag colors in the app and take ${timeStr}.\n\nPlease DO NOT click anything until the button says 'Done!'.`)) {
        colBtn.textContent = 'Applying...'; colBtn.style.opacity = '0.5'; colBtn.style.pointerEvents = 'none';
        const failed = await applyFolderColorsToNative(folder);
        colBtn.textContent = failed > 0 ? `Done (${failed} failed)` : 'Done!';
        if (failed > 0) console.warn(`[mm-folders] ${failed} tag(s) could not be recolored.`);
        setTimeout(() => { colBtn.textContent = 'Apply colors'; colBtn.style.opacity = '1'; colBtn.style.pointerEvents = 'auto'; }, 2500);
      }
    };
    const delBtn = mk('button', 'mm-actbtn'); delBtn.textContent = '✕'; delBtn.title = 'Delete'; delBtn.style.color = fg;
    delBtn.onclick = e => { e.stopPropagation(); deleteFolder(folder.id); };
    rightActions.append(colBtn, delBtn);
    hd.append(leftActions, nameSp, gd, cnt, rightActions);
    hd.onclick = e => {
      if (e.target.closest('.mm-arrow-zone,.mm-pencil,.mm-actbtn')) return;
      e.stopPropagation(); e.preventDefault();
      toggleVis(folder);
    };
    li.appendChild(hd);
    if (folder.expanded) {
      if (folder.children?.length) {
        const sw = mk('div', 'mm-sub-wrap', '', { 'data-mm': '1' });
        const childColors = gradChildColors(folder);
        folder.children.forEach((c, ci) => sw.appendChild(buildFolder(c, depth + 1, false, childColors[ci] || null)));
        li.appendChild(sw);
      }
      const body = mk('div', 'mm-body', '', { 'data-mm': '1' });
      dndOn(body, folder.id);
      if (!folder.tags.length) {
        const dz = mk('div', 'mm-dropzone'); dz.textContent = '↓ Drop tags here'; dndOn(dz, folder.id); body.appendChild(dz);
      } else {
        folder.tags.forEach((tag, i) => body.appendChild(buildTagBtn(tag, folder.gradTags !== false ? (colors[i] || null) : null)));
      }
      const addSub = mk('button', 'mm-subaddbtn', '', { 'data-mm': '1' });
      addSub.textContent = '+ New subfolder';
      addSub.onclick = e => { e.stopPropagation(); newFolder(folder.id); };
      body.appendChild(addSub);
      li.appendChild(body);
    }
    return li;
  }

  function renderInto(container) {
    if (!container) return;
    container.querySelectorAll('[data-mm="1"]').forEach(e => e.remove());
    container.querySelectorAll('.mm-sep,.mm-addli').forEach(e => e.remove());
    invalidateFlatCache();
    const addLi = mk('li', 'mm-addli', '', { 'data-mm': '1' });
    const addBtn = mk('button', 'mm-addbtn'); addBtn.innerHTML = '<span>+ New folder</span>';
    addBtn.onclick = () => newFolder(null);
    addBtn.addEventListener('dragover', e => { if (dragTag) { e.preventDefault(); addBtn.style.outline = '2px dashed rgba(0,0,0,.3)'; } });
    addBtn.addEventListener('dragleave', () => addBtn.style.outline = '');
    addBtn.addEventListener('drop', async e => {
      e.preventDefault(); addBtn.style.outline = '';
      const tag = dragTag || e.dataTransfer.getData('text/plain'); if (!tag) return;
      const r = await modal({
        title: `New folder for "${tag}"`,
        fields: [{ key: 'name', label: 'Name', type: 'text', placeholder: 'Folder name' }],
        showFolderColor: true, folderColor: '#c0f0f8',
        showColors: true, colors: ['#c0f0f8', '#183848'],
        okText: 'Create'
      });
      if (!r?.name?.trim()) return;
      const gradientColors = r.gradientColors || ['#c0f0f8', '#183848'];
      const f = {
        id: uid(), name: r.name.trim(),
        color: r.folderColor || '#c0f0f8',
        gradient: gradientColors, colorStart: gradientColors[0], colorEnd: gradientColors[gradientColors.length - 1],
        noGrad: !!r.noGrad, gradTags: r.gradTags !== false, gradFolders: !!r.gradFolders,
        expanded: true, visible: true, tags: [tag], children: []
      };
      S.folders.push(f); dragTag = null; saveS(); reRenderFolders();
    });
    const exportBtn = mk('button', 'mm-actbtn', 'margin-left:6px;color:#a0a0a0;font-size:10px;background:transparent;');
    exportBtn.textContent = 'Export'; exportBtn.title = 'Export folders (local JSON)';
    exportBtn.onclick = () => {
      const blob = new Blob([JSON.stringify(S, null, 2)], { type: 'application/json' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url; a.download = `mm-folders-${getMapId() || 'map'}.json`;
      a.click(); URL.revokeObjectURL(url);
    };
    const importBtn = mk('button', 'mm-actbtn', 'margin-left:4px;color:#a0a0a0;font-size:10px;background:transparent;');
    importBtn.textContent = 'Import'; importBtn.title = 'Import folders (local JSON)';
    importBtn.onclick = () => {
      const input = document.createElement('input');
      input.type = 'file'; input.accept = '.json';
      input.onchange = e => {
        const file = e.target.files[0]; if (!file) return;
        const reader = new FileReader();
        reader.onload = ev => {
          try {
            const parsed = JSON.parse(ev.target.result);
            if (!parsed.folders) { alert('Invalid file — no folders found.'); return; }
            if (!confirm(`Import ${parsed.folders.length} folder(s)?\n(Current folders will be replaced.)`)) return;
            S = { folders: parsed.folders };
            saveS(); reRenderFolders();
          } catch (_) { alert('JSON file read error.'); }
        };
        reader.readAsText(file);
      };
      input.click();
    };
    const infoBtn = mk('button', 'mm-actbtn', 'margin-left:4px;background:transparent;padding:2px;cursor:pointer;display:flex;align-items:center;');
    infoBtn.title = 'Help & Warnings';
    infoBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" style="fill:#a0a0a0;"><path d="M12 2C6.489 2 2 6.489 2 12s4.489 10 10 10 10-4.489 10-10S17.511 2 12 2zm0 2c4.43 0 8 3.57 8 8s-3.57 8-8 8-8-3.57-8-8 3.57-8 8-8zm-1 5v2h2V9h-2zm0 4v6h2v-6h-2z"/></svg>`;
    infoBtn.onclick = showInfoModal;
    const row = mk('div', '', 'display:flex;align-items:center;margin-bottom:2px;');
    row.append(addBtn, exportBtn, importBtn, infoBtn);
    addLi.appendChild(row);
    container.insertBefore(addLi, container.firstChild);
    let last = addLi;
    S.folders.forEach((f, i) => {
      const li = buildFolder(f, 0, i === S.folders.length - 1);
      last.insertAdjacentElement('afterend', li);
      last = li;
    });
    if (S.folders.length) {
      const sep = mk('li', 'mm-sep', '', { 'data-mm': '1' });
      last.insertAdjacentElement('afterend', sep);
      const dropOutLi = mk('li', '', 'list-style:none;', { 'data-mm': '1' });
      const dropOut = mk('div', '', 'padding:4px 8px;border:1.5px dashed rgba(255,255,255,.15);border-radius:8px;font-size:11px;font-family:"Open Sans",sans-serif;color:rgba(255,255,255,.25);text-align:center;font-style:italic;transition:border-color .15s,color .15s;margin-bottom:4px;');
      dropOut.textContent = '↓ Drop here to remove from folder';
      dropOut.addEventListener('dragover', e => { if (!dragTag) return; e.preventDefault(); dropOut.style.borderColor = 'rgba(255,100,100,.6)'; dropOut.style.color = 'rgba(255,150,150,.8)'; });
      dropOut.addEventListener('dragleave', () => { dropOut.style.borderColor = ''; dropOut.style.color = ''; });
      dropOut.addEventListener('drop', e => {
        e.preventDefault(); dropOut.style.borderColor = ''; dropOut.style.color = '';
        const tag = dragTag || e.dataTransfer.getData('text/plain');
        if (tag) { dragTag = null; removeTag(tag); }
      });
      dropOutLi.appendChild(dropOut);
      sep.insertAdjacentElement('afterend', dropOutLi);
    }
  }

  function render() {
    if (!getMapId()) return;
    injectFooterButtons();
    const ol = getLocOl();
    const ul = getMainUl();
    if (ol) {
      document.body.classList.add('mm-loc-open');
      if (ul && !ul.querySelector('[data-mm="1"]')) renderInto(ul);
    } else {
      document.body.classList.remove('mm-loc-open');
      if (ul) renderInto(ul);
    }
    updateHideStyle();
  }

  function watchDOM() {
    let _t = null;
    let colorSyncInterval = null;
    new MutationObserver(mutations => {
      let needsColorSync = false;
      let syncRender = false;

      for (const m of mutations) {
        if (m.removedNodes.length) {
          m.removedNodes.forEach(n => {
            if (n.nodeType !== 1) return;
            if (n.getAttribute?.('role') === 'dialog' || n.classList?.contains('edit-tag-modal')) {
              needsColorSync = true;
            }
            // Détection suppression native :
            if (n.tagName === 'LI' && n.classList?.contains('tag') && n.classList?.contains('has-button') && !n.hasAttribute('data-mm')) {
              const tagName = nameFromLi(n);
              // Si le tag était dans nos dossiers
              if (tagName && !tagName.startsWith(STORAGE_PREFIX) && !tagName.startsWith(OLD_STORAGE_PREFIX) && getAssignedTags().has(tagName)) {
                // On attend plus longtemps pour s'assurer que c'est une vraie suppression et non un re-render dû à la recherche
                setTimeout(() => {
                  const currentlySearching = document.body.classList.contains('mm-search-mode') || document.body.classList.contains('mm-search-exiting');
                  const mainUl = getMainUl();
                  // Vérifie qu'on n'est pas en train de chercher, que la liste est bien chargée, et que le tag a vraiment disparu du DOM natif
                  if (!currentlySearching && mainUl && !liOfTag(tagName)) {
                    removeTag(tagName);
                  }
                }, 1000); // Délai de 1s pour laisser le DOM natif se stabiliser
              }
            }
          });
        }
        if (m.addedNodes.length) {
          m.addedNodes.forEach(n => {
            if (n.nodeType !== 1) return;
            if (n.tagName === 'UL' && n.classList.contains('tag-list') && !n.hasAttribute('role')) syncRender = true;
            const processNode = el => {
              if (el.tagName === 'LI' && !el.hasAttribute('data-mm')) {
                const ul = el.closest('ul.tag-list:not([role="listbox"])');
                if (ul && !ul.querySelector('[data-mm="1"]')) syncRender = true;
                const txt = el.textContent || '';
                if (txt.includes(STORAGE_PREFIX) || txt.includes(OLD_STORAGE_PREFIX)) {
                  el.style.setProperty('display', 'none', 'important');
                } else if (!document.body.classList.contains('mm-search-mode')) {
                  const name = nameFromLi(el);
                  if (name && getAssignedTags().has(name)) el.style.setProperty('display', 'none', 'important');
                }
              }
            };
            processNode(n);
            if (n.querySelectorAll) n.querySelectorAll('li:not([data-mm])').forEach(processNode);
          });
        }
      }

      if (syncRender && needsRender()) render();

      if (needsColorSync) {
        clearInterval(colorSyncInterval);
        let ticks = 0;
        colorSyncInterval = setInterval(() => {
          document.querySelectorAll('li:not([data-mm])').forEach(li => delete li.dataset.mmColor);
          updateHideStyle();
          ticks++;
          if (ticks > 6) clearInterval(colorSyncInterval);
        }, 250);
      }

      clearTimeout(_t);
      _t = setTimeout(() => {
        if (needsRender()) render();
        else updateHideStyle();
      }, 50);
    }).observe(document.body, { childList: true, subtree: true });

    setInterval(() => { if (needsRender()) render(); }, 5000);
  }

  function watchNav() {
    let last = location.href;
    const check = () => {
      if (location.href === last) return;
      last = location.href;
      document.getElementById('mm-hide-css')?.remove();
      invalidateFlatCache();
      loadS();
      setTimeout(() => render(), 700);
    };
    window.addEventListener('popstate', check);
    const orig = history.pushState.bind(history);
    history.pushState = (...a) => { orig(...a); check(); };
    setInterval(check, 1500);
  }

  function boot() {
    document.body.classList.add('mm-loading');
    loadS();
    injectCSS();
    initDnD();
    bindCtxMenu();
    bindTooltipListeners();
    watchSearchInput();
    watchNav();
    watchDOM();
    window.addEventListener('beforeunload', e => {
      if (!isDirty) return;
      e.preventDefault();
      e.returnValue = '';
    });
    setTimeout(() => document.body.classList.remove('mm-loading'), 3000);
    let tries = 0;
    function tryRender() {
      tries++;
      if (document.querySelectorAll('li.tag.has-button').length > 0 || document.querySelector('.map-meta__actions')) {
        loadFromCloud(false);
        render();
      } else if (tries < 25) {
        setTimeout(tryRender, 500);
      }
    }
    const obs = new MutationObserver(() => {
      if (document.querySelectorAll('li.tag.has-button').length > 0 || document.querySelector('.map-meta__actions')) {
        obs.disconnect();
        setTimeout(tryRender, 80);
      }
    });
    obs.observe(document.body, { childList: true, subtree: true });
    setTimeout(tryRender, 800);
    setTimeout(tryRender, 2200);
  }

  boot();
})();