☰

πŸ› οΈ PopControl

TR β€” Popmundo scriptlerini tek panelden yΓΆnetmenizi sağlayan merkezi modΓΌl

As of 10.03.2026. See апошняя вСрсія.

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        πŸ› οΈ PopControl
// @namespace   popmundo.popcontrol
// @version     1.2
// @description TR β€” Popmundo scriptlerini tek panelden yΓΆnetmenizi sağlayan merkezi modΓΌl
// @description:en EN β€” Central hub to manage all Popmundo scripts from one panel
// @description:pt-BR PT-BR β€” MΓ³dulo central para gerenciar todos os scripts do Popmundo
// @author      luke-james-gibson
// @license     MIT
// @match       https://*.popmundo.com/*
// @run-at      document-end
// @grant       unsafeWindow
// ==/UserScript==

(function () {
'use strict';

// ─── UTILS ───────────────────────────────────────────────────────────────────
const CK = {
    get: k => { const m = document.cookie.match(new RegExp('(?:^|; )' + k + '=([^;]*)')); return m ? decodeURIComponent(m[1]) : null; },
    set: (k, v) => { document.cookie = `${k}=${encodeURIComponent(v)};domain=.popmundo.com;path=/;max-age=31536000`; }
};
const LS = {
    get: (k, d) => { try { const v = localStorage.getItem(k); return v !== null ? JSON.parse(v) : d; } catch { return d; } },
    set: (k, v) => { try { localStorage.setItem(k, JSON.stringify(v)); } catch {} },
};
const mk  = (tag, cls, txt) => { const e = document.createElement(tag); if (cls) e.className = cls; if (txt != null) e.textContent = txt; return e; };
const mkB = (txt, cls, fn)  => Object.assign(mk('button', cls, txt), { onclick: fn, type: 'button' });

// ─── LANG ────────────────────────────────────────────────────────────────────
const LANG = CK.get('ppm_lang') || 'TR';
const _D   = (tr, en, pt) => ({ TR: tr, EN: en, PT: pt }[LANG] || tr);
const STR  = {
    posLabel:    _D('Konum',            'Position',          'PosiΓ§Γ£o'),
    posBottom:   _D('⬇ Alt',           '⬇ Bottom',         '⬇ Baixo'),
    posTop:      _D('⬆ Üst',           '⬆ Top',            '⬆ Cima'),
    posLeft:     _D('β—€ Sol',           'β—€ Left',           'β—€ Esquerda'),
    posRight:    _D('β–Ά Sağ',           'β–Ά Right',          'β–Ά Direita'),
    langLabel:   _D('Dil',              'Language',          'Idioma'),
    scripts:     _D('Scriptler',        'Scripts',           'Scripts'),
    order:       _D('SΔ±ra',             'Order',             'Ordem'),
    orderHint:   _D('Barda butonlarΔ± sΓΌrΓΌkleyip bΔ±rakarak sΔ±ralayabilirsiniz.',
                    'Drag & drop buttons on the bar to reorder.',
                    'Arraste os botΓ΅es na barra para reordenar.'),
    colorBar:    _D('Bar Rengi',        'Bar Color',         'Cor da Barra'),
    colorText:   _D('YazΔ± Rengi',       'Text Color',        'Cor do Texto'),
    colorHint:   _D('Γ–zel renk',        'Custom color',      'Cor personalizada'),
    guide:       _D('πŸ“– KullanΔ±m Rehberi', 'πŸ“– Usage Guide', 'πŸ“– Guia de Uso'),
    close:       _D('Kapat',            'Close',             'Fechar'),
};

// ─── STATE ───────────────────────────────────────────────────────────────────
// ppc_enabled + ppc_order stored in cookies (cross-subdomain via .popmundo.com)
const _getEnabledObj = () => { try { return JSON.parse(decodeURIComponent(CK.get('ppc_enabled') || '%7B%7D')); } catch { return {}; } };
const _saveEnabledObj= o  => CK.set('ppc_enabled', JSON.stringify(o));
const _isEnabled  = id     => _getEnabledObj()[id] !== false;
const _setEnabled = (id,v) => { const e = _getEnabledObj(); e[id] = v; _saveEnabledObj(e); };
const _getPos     = ()     => CK.get('ppc_pos') || 'bottom';
const _setPos     = p      => CK.set('ppc_pos', p);
const _getOrder   = ()     => { try { return JSON.parse(decodeURIComponent(CK.get('ppc_order') || '%5B%5D')); } catch { return []; } };
const _setOrder   = o      => CK.set('ppc_order', JSON.stringify(o));
const _getColBg   = ()     => CK.get('ppc_bg')  || '#f5f0ff';
const _getColFg   = ()     => CK.get('ppc_fg')  || '#5a30a0';
const _setColBg   = v      => CK.set('ppc_bg', v);
const _setColFg   = v      => CK.set('ppc_fg', v);

const _registry = [];
let   _collapsed = false;

function _sortedRegistry() {
    const order = _getOrder();
    if (!order.length) return [..._registry];
    const res = [];
    order.forEach(id => { const e = _registry.find(r => r.id === id); if (e) res.push(e); });
    _registry.forEach(e => { if (!res.includes(e)) res.push(e); });
    return res;
}

// ─── COLORS ──────────────────────────────────────────────────────────────────
const PRESET_BG = [
    '#f5f0ff','#fff','#1a1035','#2c3e50','#0d1117',
    '#fff0f5','#f0fff4','#f0f8ff','#fffde7','#f3e5f5',
];
const PRESET_FG = [
    '#5a30a0','#6f42c1','#fff','#ecf0f1','#ccc',
    '#e83e8c','#28a745','#007bff','#fd7e14','#ffc107',
];

// ─── GEOMETRY ────────────────────────────────────────────────────────────────
function _mainGeom() {
    const m = document.getElementById('ppm-main') || document.getElementById('ppm-footer');
    if (!m) return { left: 0, width: window.innerWidth, right: window.innerWidth };
    const r = m.getBoundingClientRect();
    return { left: Math.round(r.left), width: m.offsetWidth, right: Math.round(r.right) };
}

// ─── PUSH STYLE ──────────────────────────────────────────────────────────────
function _applyPush(size) {
    document.getElementById('ppc-push')?.remove();
    if (!size) return;
    const s = document.createElement('style'); s.id = 'ppc-push';
    s.textContent = `#ppm-main{margin-top:${65 + size}px!important}#character-tools,#header-logo{margin-top:${size}px!important}`;
    document.head.appendChild(s);
}

// ─── BAR ─────────────────────────────────────────────────────────────────────
let _dragId   = null;
let _touchDrg = null;

function _rebuild() {
    document.getElementById('ppc-bar')?.remove();
    document.getElementById('ppc-fab')?.remove();
    document.getElementById('ppc-push')?.remove();

    const pos  = _getPos();
    const g    = _mainGeom();
    const mob  = window.innerWidth < 768;
    const vert = pos === 'left' || pos === 'right';
    const BG   = _getColBg();
    const BDR  = '1px solid ' + _shadeHex(BG, -25);
    const FG   = _getColFg();
    const SZ   = 46;  // fixed size for all modes

    // ── FAB (collapsed) ──────────────────────────────────────────────────────
    const fab = mk('button'); fab.id = 'ppc-fab'; fab.type = 'button';
    fab.textContent = 'βš™οΈ';
    fab.style.cssText = [
        'position:fixed;bottom:16px;right:16px;z-index:9996',
        `background:${BG};border:${BDR};border-radius:50%`,
        `width:${SZ}px;height:${SZ}px;font-size:20px;cursor:pointer`,
        'box-shadow:0 2px 10px rgba(0,0,0,.25);display:none;align-items:center;justify-content:center',
        'font-family:inherit'
    ].join(';');
    fab.onclick = () => {
        _collapsed = false;
        fab.style.display = 'none';
        document.getElementById('ppc-bar').style.display = '';
    };
    document.body.appendChild(fab);

    // ── BAR ──────────────────────────────────────────────────────────────────
    const bar = mk('div'); bar.id = 'ppc-bar';
    bar.style.display = _collapsed ? 'none' : '';

    if (vert) {
        const side = pos === 'left' ? `left:${mob ? 0 : Math.max(0, g.left - SZ)}px` : `right:${mob ? 0 : Math.max(0, window.innerWidth - g.right - SZ)}px`;
        const shadow = pos === 'left' ? '2px 0 8px rgba(0,0,0,.12)' : '-2px 0 8px rgba(0,0,0,.12)';
        bar.style.cssText = [
            `position:fixed;${side};top:65px;bottom:0;z-index:9990`,
            `background:${BG};border:${BDR};box-shadow:${shadow}`,
            `width:${SZ}px;overflow:hidden;overflow-y:auto;scrollbar-width:none`,
            `display:flex;flex-direction:column;align-items:center;padding:4px 0;gap:0`,
        ].join(';');
    } else {
        const side   = pos === 'bottom' ? 'bottom:0' : 'top:0';
        const xPos   = mob ? 'left:0;right:0' : `left:${g.left}px;width:${g.width}px`;
        const zIdx   = pos === 'top' ? 99990 : 9990;
        const shadow = pos === 'bottom' ? '0 -2px 8px rgba(0,0,0,.12)' : '0 2px 8px rgba(0,0,0,.12)';
        bar.style.cssText = [
            `position:fixed;${xPos};${side};z-index:${zIdx}`,
            `background:${BG};border:${BDR};box-shadow:${shadow}`,
            `display:flex;flex-direction:row;flex-wrap:wrap;align-items:stretch`,
            `overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch`,
        ].join(';');
    }

    // ── BUTTON FACTORY ────────────────────────────────────────────────────────
    const BTN_W = vert ? SZ : 52;
    const mkBtn = (icon, label, onClick, draggable) => {
        const b = mk('button'); b.type = 'button';
        if (draggable) b.draggable = true;
        b.style.cssText = [
            `display:flex;flex-direction:column;align-items:center;justify-content:center`,
            `width:${BTN_W}px;min-height:${SZ}px;padding:4px 2px;gap:2px`,
            `background:none;border:none;cursor:${draggable ? 'grab' : 'pointer'};font-family:inherit;flex-shrink:0`
        ].join(';');
        const ico = mk('span'); ico.textContent = icon;
        ico.style.cssText = 'font-size:18px;line-height:1;pointer-events:none;flex-shrink:0';
        const lbl = mk('span'); lbl.textContent = label;
        lbl.style.cssText = `font-size:9px;color:${FG};font-weight:700;white-space:nowrap;pointer-events:none;max-width:${BTN_W - 4}px;overflow:hidden;text-overflow:ellipsis`;
        b.append(ico, lbl);
        b.addEventListener('mouseenter', () => b.style.background = 'rgba(0,0,0,.07)');
        b.addEventListener('mouseleave', () => b.style.background = 'none');
        b.onclick = onClick;
        return b;
    };

    const mkSep = () => {
        const d = mk('div');
        d.style.cssText = vert
            ? `height:1px;background:${_shadeHex(BG,-18)};margin:2px 6px;width:${SZ-12}px;flex-shrink:0`
            : `width:1px;background:${_shadeHex(BG,-18)};margin:5px 0;flex-shrink:0;align-self:stretch`;
        return d;
    };

    // ── DESKTOP DRAG & DROP ───────────────────────────────────────────────────
    function _attachDrag(btn, groupId) {
        btn.addEventListener('dragstart', e => {
            _dragId = groupId;
            e.dataTransfer.effectAllowed = 'move';
            setTimeout(() => btn.style.opacity = '.4', 0);
        });
        btn.addEventListener('dragend', () => { _dragId = null; btn.style.opacity = '1'; });
        btn.addEventListener('dragover', e => { e.preventDefault(); btn.style.outline = '2px dashed ' + FG; });
        btn.addEventListener('dragleave', () => btn.style.outline = '');
        btn.addEventListener('drop', e => {
            e.preventDefault(); btn.style.outline = '';
            if (!_dragId || _dragId === groupId) return;
            const order = _sortedRegistry().map(r => r.id);
            const from = order.indexOf(_dragId), to = order.indexOf(groupId);
            if (from < 0 || to < 0) return;
            order.splice(to, 0, order.splice(from, 1)[0]);
            _setOrder(order); _rebuild();
        });
    }

    // ── TOUCH DRAG (mobile) ───────────────────────────────────────────────────
    function _attachTouchDrag(container) {
        let dragEl = null, ghost = null, lastOver = null, startX = 0, startY = 0;
        const btnSel = '[data-ppc-drag]';
        container.addEventListener('touchstart', e => {
            const h = e.target.closest('[data-ppc-drag]');
            if (!h) return;
            dragEl = h; startX = e.touches[0].clientX; startY = e.touches[0].clientY;
            const r = dragEl.getBoundingClientRect();
            ghost = dragEl.cloneNode(true);
            ghost.style.cssText = `position:fixed;left:${r.left}px;top:${r.top}px;width:${r.width}px;height:${r.height}px;opacity:.65;pointer-events:none;z-index:99999;border:2px dashed ${FG};border-radius:6px;background:${BG}`;
            document.body.appendChild(ghost);
            dragEl.style.opacity = '.3';
        }, { passive: true });
        container.addEventListener('touchmove', e => {
            if (!dragEl) return;
            e.preventDefault();
            const t = e.touches[0];
            ghost.style.transform = `translate(${t.clientX - startX}px,${t.clientY - startY}px)`;
            const over = document.elementFromPoint(t.clientX, t.clientY)?.closest('[data-ppc-drag]');
            if (lastOver && lastOver !== dragEl) lastOver.style.outline = '';
            if (over && over !== dragEl) { over.style.outline = '2px dashed ' + FG; lastOver = over; }
        }, { passive: false });
        container.addEventListener('touchend', e => {
            if (!dragEl) return;
            ghost?.remove(); ghost = null;
            dragEl.style.opacity = '1';
            if (lastOver && lastOver !== dragEl) lastOver.style.outline = '';
            const t = e.changedTouches[0];
            const over = document.elementFromPoint(t.clientX, t.clientY)?.closest('[data-ppc-drag]');
            if (over && over !== dragEl) {
                const fromId = dragEl.dataset.ppcDrag, toId = over.dataset.ppcDrag;
                const order = _sortedRegistry().map(r => r.id);
                const fi = order.indexOf(fromId), ti = order.indexOf(toId);
                if (fi >= 0 && ti >= 0) { order.splice(ti, 0, order.splice(fi, 1)[0]); _setOrder(order); _rebuild(); }
            }
            dragEl = null; lastOver = null;
        }, { passive: true });
    }

    // ── COLLAPSE BUTTON ───────────────────────────────────────────────────────
    const colBtn = mk('button'); colBtn.type = 'button';
    colBtn.textContent = 'β–Ό';
    colBtn.style.cssText = [
        `font-size:9px;color:${FG};background:none;border:none;cursor:pointer`,
        `padding:2px 4px;font-family:inherit;flex-shrink:0`,
        vert ? `width:${SZ}px;text-align:center` : `min-width:20px;align-self:center`
    ].join(';');
    colBtn.title = _D('KΓΌΓ§ΓΌlt', 'Collapse', 'Recolher');
    colBtn.onclick = () => {
        _collapsed = true;
        bar.style.display = 'none';
        fab.style.display = 'flex';
    };

    // ── RENDER ENTRIES ────────────────────────────────────────────────────────
    let hasAny = false;
    _sortedRegistry().forEach(entry => {
        if (!_isEnabled(entry.id)) return;
        if (hasAny) bar.appendChild(mkSep());
        entry.buttons.forEach((btn, i) => {
            const b = mkBtn(btn.icon, btn.label, btn.onClick, i === 0);
            if (i === 0) {
                b.dataset.ppcDrag = entry.id;
                _attachDrag(b, entry.id);
            }
            bar.appendChild(b);
        });
        hasAny = true;
    });

    // ── SETTINGS + COLLAPSE ───────────────────────────────────────────────────
    if (hasAny) bar.appendChild(mkSep());
    const sBtn = mkBtn('βš™οΈ', 'PopControl', _openSettings, false);
    if (vert) { sBtn.style.marginTop = 'auto'; bar.appendChild(sBtn); bar.appendChild(mkSep()); bar.appendChild(colBtn); }
    else { sBtn.style.marginLeft = 'auto'; bar.appendChild(sBtn); bar.appendChild(colBtn); }

    document.body.appendChild(bar);

    // Attach touch drag to bar
    _attachTouchDrag(bar);

    // Push layout for top
    if (pos === 'top') _applyPush(bar.offsetHeight || 34);

    // Apply collapsed state
    if (_collapsed) { bar.style.display = 'none'; fab.style.display = 'flex'; }
}

// ─── COLOR UTILS ─────────────────────────────────────────────────────────────
function _shadeHex(hex, amt) {
    try {
        let h = hex.replace('#','');
        if (h.length === 3) h = h[0]+h[0]+h[1]+h[1]+h[2]+h[2];
        const r = Math.max(0,Math.min(255,parseInt(h.slice(0,2),16)+amt));
        const g = Math.max(0,Math.min(255,parseInt(h.slice(2,4),16)+amt));
        const b = Math.max(0,Math.min(255,parseInt(h.slice(4,6),16)+amt));
        return '#'+[r,g,b].map(x=>x.toString(16).padStart(2,'0')).join('');
    } catch { return hex; }
}

// ─── SETTINGS PANEL ──────────────────────────────────────────────────────────
function _openSettings() {
    document.getElementById('ppc-ov')?.remove();
    const pos = _getPos();

    const ov = mk('div'); ov.id = 'ppc-ov';
    ov.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:99998;display:flex;align-items:flex-end;justify-content:center';

    const box = mk('div');
    box.style.cssText = 'background:#fff;border-radius:16px 16px 0 0;padding:20px;width:100%;max-width:520px;max-height:80vh;overflow-y:auto;box-sizing:border-box';

    const mkH  = t  => { const d = mk('div'); d.textContent = t; d.style.cssText = 'font-size:10px;font-weight:700;color:#888;text-transform:uppercase;letter-spacing:.5px;margin:14px 0 6px'; return d; };
    const mkHr = () => { const hr = mk('hr'); hr.style.cssText = 'border:none;border-top:1px solid #eee;margin:10px 0'; return hr; };
    const mkPillRow = (items) => {
        const row = mk('div'); row.style.cssText = 'display:flex;gap:6px;flex-wrap:wrap';
        items.forEach(([val, lbl, active, fn]) => {
            const b = mkB(lbl, '', fn);
            b.style.cssText = `flex:1;min-width:70px;padding:7px 4px;border-radius:6px;border:1px solid ${active?'#6f42c1':'#ddd'};background:${active?'#6f42c1':'#f8f9fa'};color:${active?'#fff':'#333'};font-size:11px;cursor:pointer;font-family:inherit`;
            row.appendChild(b);
        });
        return row;
    };

    // Title row
    const titleRow = mk('div'); titleRow.style.cssText = 'display:flex;justify-content:space-between;align-items:center;margin-bottom:16px';
    titleRow.appendChild(Object.assign(mk('span'), { textContent: 'πŸ› οΈ PopControl v1.2', style: { fontWeight: 'bold', fontSize: '16px' } }));
    const xBtn = mkB('βœ•', '', () => ov.remove()); xBtn.style.cssText = 'background:none;border:none;font-size:20px;cursor:pointer;color:#aaa;padding:0';
    titleRow.appendChild(xBtn); box.appendChild(titleRow);

    // Position
    box.appendChild(mkH(STR.posLabel));
    box.appendChild(mkPillRow([
        ['bottom', STR.posBottom, pos==='bottom', () => { _setPos('bottom'); ov.remove(); location.reload(); }],
        ['top',    STR.posTop,    pos==='top',    () => { _setPos('top');    ov.remove(); location.reload(); }],
        ['left',   STR.posLeft,   pos==='left',   () => { _setPos('left');   ov.remove(); location.reload(); }],
        ['right',  STR.posRight,  pos==='right',  () => { _setPos('right');  ov.remove(); location.reload(); }],
    ]));
    box.appendChild(mkHr());

    // Language
    box.appendChild(mkH(STR.langLabel));
    box.appendChild(mkPillRow([
        ['TR','πŸ‡ΉπŸ‡· TΓΌrkΓ§e', LANG==='TR', () => { CK.set('ppm_lang','TR'); ov.remove(); location.reload(); }],
        ['EN','πŸ‡¬πŸ‡§ English', LANG==='EN', () => { CK.set('ppm_lang','EN'); ov.remove(); location.reload(); }],
        ['PT','πŸ‡§πŸ‡· PortuguΓͺs', LANG==='PT', () => { CK.set('ppm_lang','PT'); ov.remove(); location.reload(); }],
    ]));
    box.appendChild(mkHr());

    // Color pickers
    const mkSwatches = (presets, current, onPick) => {
        const wrap = mk('div'); wrap.style.cssText = 'display:flex;flex-wrap:wrap;gap:5px;align-items:center;margin:6px 0 4px';
        let sel = current;
        const updateHexInput = () => { if (hexInp) hexInp.value = sel; };
        presets.forEach(c => {
            const sw = mk('button'); sw.type = 'button';
            sw.style.cssText = `width:22px;height:22px;border-radius:4px;border:2px solid ${c===current?'#333':'transparent'};cursor:pointer;padding:0;background:${c};flex-shrink:0;box-shadow:0 1px 3px rgba(0,0,0,.2)`;
            sw.title = c;
            sw.onclick = () => {
                sel = c;
                wrap.querySelectorAll('button[data-sw]').forEach(b => b.style.borderColor = 'transparent');
                sw.style.borderColor = '#333';
                ci.value = c; updateHexInput(); onPick(c);
            };
            sw.dataset.sw = '1';
            wrap.appendChild(sw);
        });
        const ci = mk('input'); ci.type = 'color'; ci.value = current;
        ci.style.cssText = 'width:26px;height:22px;padding:1px;border:1px solid #ccc;border-radius:4px;cursor:pointer;margin-left:2px;flex-shrink:0';
        ci.title = STR.colorHint;
        ci.oninput = () => {
            sel = ci.value;
            wrap.querySelectorAll('button[data-sw]').forEach(b => b.style.borderColor = 'transparent');
            updateHexInput(); onPick(ci.value);
        };
        wrap.appendChild(ci);
        // Hex text input
        const hexWrap = mk('div'); hexWrap.style.cssText = 'display:flex;align-items:center;gap:6px;margin-bottom:10px';
        const hexLbl = mk('span'); hexLbl.textContent = '#'; hexLbl.style.cssText = 'font-size:12px;color:#666;font-weight:600';
        const hexInp = mk('input'); hexInp.type = 'text';
        hexInp.value = current.replace('#','');
        hexInp.maxLength = 6;
        hexInp.placeholder = 'hex';
        hexInp.style.cssText = 'width:70px;padding:5px 8px;border:1px solid #ddd;border-radius:6px;font-size:12px;font-family:monospace;letter-spacing:1px';
        hexInp.oninput = () => {
            const v = '#' + hexInp.value.replace(/[^0-9a-fA-F]/g,'').slice(0,6);
            if (v.length === 7) {
                sel = v; ci.value = v;
                wrap.querySelectorAll('button[data-sw]').forEach(b => b.style.borderColor = 'transparent');
                onPick(v);
            }
        };
        hexWrap.append(hexLbl, hexInp);
        const outerWrap = mk('div');
        outerWrap.append(wrap, hexWrap);
        return outerWrap;
    };

    box.appendChild(mkH(STR.colorBar));
    box.appendChild(mkSwatches(PRESET_BG, _getColBg(), v => { _setColBg(v); _applyColors(); }));

    box.appendChild(mkH(STR.colorText));
    box.appendChild(mkSwatches(PRESET_FG, _getColFg(), v => { _setColFg(v); _applyColors(); }));
    box.appendChild(mkHr());

    // Script toggles
    if (_registry.length) {
        box.appendChild(mkH(STR.scripts));
        _sortedRegistry().forEach(entry => {
            const row = mk('div'); row.style.cssText = 'display:flex;align-items:center;justify-content:space-between;padding:10px 0;border-bottom:1px solid #f0f0f0';
            const name = mk('span'); name.textContent = entry.icon + ' ' + entry.label; name.style.cssText = 'font-size:13px;font-weight:500';
            const swLbl = mk('label'); swLbl.style.cssText = 'position:relative;display:inline-block;width:46px;height:26px;cursor:pointer;flex-shrink:0';
            const inp   = mk('input'); inp.type = 'checkbox'; inp.checked = _isEnabled(entry.id); inp.style.cssText = 'opacity:0;width:0;height:0;position:absolute';
            const trk   = mk('span'); trk.style.cssText = `position:absolute;inset:0;border-radius:26px;background:${inp.checked?'#6f42c1':'#ccc'};transition:.25s`;
            const knob  = mk('span'); knob.style.cssText = `position:absolute;height:20px;width:20px;left:${inp.checked?'23px':'3px'};bottom:3px;background:#fff;border-radius:50%;transition:.25s;box-shadow:0 1px 3px rgba(0,0,0,.3)`;
            trk.appendChild(knob);
            inp.onchange = () => {
                const v = inp.checked;
                trk.style.background = v ? '#6f42c1' : '#ccc';
                knob.style.left = v ? '23px' : '3px';
                _setEnabled(entry.id, v);
                if (!v) entry.onUndo?.();
                _rebuild();
            };
            swLbl.append(inp, trk); row.append(name, swLbl); box.appendChild(row);
        });

        box.appendChild(mkHr());
        const hint = mk('div'); hint.textContent = STR.orderHint;
        hint.style.cssText = 'font-size:11px;color:#999;padding:4px 0 6px';
        box.appendChild(hint);
    }

    // Guide button
    box.appendChild(mkHr());
    const guideBtn = mkB(STR.guide, '', () => window.open('https://rentry.org/PopControl', '_blank'));
    guideBtn.style.cssText = 'width:100%;padding:10px;border-radius:8px;background:#6f42c1;color:#fff;border:none;cursor:pointer;font-size:13px;font-weight:600;font-family:inherit';
    box.appendChild(guideBtn);

    ov.onclick = e => { if (e.target === ov) ov.remove(); };
    ov.appendChild(box); document.body.appendChild(ov);
}

// ─── APPLY COLORS (live) ─────────────────────────────────────────────────────
function _applyColors() {
    const bar = document.getElementById('ppc-bar');
    const fab = document.getElementById('ppc-fab');
    const BG  = _getColBg();
    const FG  = _getColFg();
    const BDR = '1px solid ' + _shadeHex(BG, -25);
    if (bar) {
        bar.style.background = BG;
        bar.style.border = BDR;
        bar.querySelectorAll('span:last-child').forEach(s => { if (s.style.fontSize === '9px') s.style.color = FG; });
    }
    if (fab) { fab.style.background = BG; fab.style.border = BDR; }
    // Full rebuild to repaint properly
    _rebuild();
}

// ─── PUBLIC API ───────────────────────────────────────────────────────────────
unsafeWindow.PopControl = {
    register({ id, icon, label, buttons, onUndo }) {
        if (_registry.some(r => r.id === id)) return;
        _registry.push({ id, icon: icon || 'πŸ”Œ', label: label || id, buttons: buttons || [], onUndo });
        _rebuild();
    },
    unregister(id) {
        const i = _registry.findIndex(r => r.id === id);
        if (i < 0) return;
        const [entry] = _registry.splice(i, 1);
        entry.onUndo?.();
        _rebuild();
    },
    isEnabled: _isEnabled,
    getLang:   () => LANG,
};

// ─── INIT ────────────────────────────────────────────────────────────────────
(function _init() {
    const go = () => _rebuild();
    if (document.getElementById('ppm-main') || document.getElementById('ppm-footer')) { go(); return; }
    const obs = new MutationObserver(() => {
        if (document.getElementById('ppm-main') || document.getElementById('ppm-footer')) { obs.disconnect(); go(); }
    });
    obs.observe(document.body, { childList: true, subtree: true });
    setTimeout(() => { obs.disconnect(); go(); }, 4000);
})();

})();