🛠️ PopControl

Popmundo scriptlerini tek panelden yönetmenizi sağlayan merkezi modül

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey, Greasemonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

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

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

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

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्क्रिप्ट व्यवस्थापक एक्स्टेंशन इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्क्रिप्ट व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्टाईल व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

// ==UserScript==
// @name        🛠️ PopControl
// @name:tr     🛠️ PopControl
// @name:en     🛠️ PopControl
// @name:pt-BR  🛠️ PopControl
// @namespace   popmundo.popcontrol
// @version     1.7
// @description Popmundo scriptlerini tek panelden yönetmenizi sağlayan merkezi modül
// @description:tr Popmundo scriptlerini tek panelden yönetmenizi sağlayan merkezi modül
// @description:en Central hub to manage all Popmundo scripts from one panel
// @description: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
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_deleteValue
// ==/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('🎨 Renk Seçici — İstediğin rengi buradan seçebilirsin', '🎨 Color Picker — Pick any custom color here', '🎨 Seletor de Cor — Escolha qualquer cor aqui'),
    guide:       _D('📖 Beni Oku', '📖 Read Me', '📖 Leia-me'),
    integrate:   _D('🔗 Entegrasyon', '🔗 Integration', '🔗 Integração'),
    close:       _D('Kapat',            'Close',             'Fechar'),
};

// ─── STATE ───────────────────────────────────────────────────────────────────
// ppc settings in GM_setValue (cross-subdomain, stable)
// One-time migration from cookies
(function _migrateCookiesToGM() {
    const keys = ['ppc_enabled','ppc_order','ppc_pos','ppc_bg','ppc_fg'];
    keys.forEach(k => {
        if (GM_getValue(k, null) !== null) return;
        const m = document.cookie.match(new RegExp('(?:^|; )' + k + '=([^;]*)'));
        if (m) { GM_setValue(k, decodeURIComponent(m[1])); document.cookie = k + '=;domain=.popmundo.com;path=/;max-age=0'; }
    });
})();

const _getEnabledObj = () => { try { return JSON.parse(GM_getValue('ppc_enabled', '{}')); } catch { return {}; } };
const _saveEnabledObj= o  => { const v = JSON.stringify(o); GM_setValue('ppc_enabled', v); try { localStorage.setItem('ppc_enabled', v); } catch {} };
const _isEnabled  = id     => _getEnabledObj()[id] !== false;
const _setEnabled = (id,v) => { const e = _getEnabledObj(); e[id] = v; _saveEnabledObj(e); };
const _getPos     = ()     => GM_getValue('ppc_pos', 'bottom');
const _setPos     = p      => GM_setValue('ppc_pos', p);
const _getOrder   = ()     => { try { return JSON.parse(GM_getValue('ppc_order', '[]')); } catch { return []; } };
const _setOrder   = o      => GM_setValue('ppc_order', JSON.stringify(o));
const _getColBg   = ()     => GM_getValue('ppc_bg', '#f5f0ff');
const _getColFg   = ()     => GM_getValue('ppc_fg', '#5a30a0');
const _setColBg   = v      => GM_setValue('ppc_bg', v);
const _setColFg   = v      => GM_setValue('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;
        _rebuild();
    };
    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}`,
            `border-radius:${pos==='left'?'0 8px 8px 0':'8px 0 0 8px'}`,
            `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}`,
            `border-radius:${pos==='bottom'?'8px 8px 0 0':'0 0 8px 8px'}`,
            `display:flex;flex-direction:row;flex-wrap:nowrap;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`,
            vert ? `width:${BTN_W}px;min-height:${SZ}px;padding:4px 2px;gap:2px;flex-shrink:0` : `min-width:48px;flex:1;min-height:${SZ}px;padding:4px 6px;gap:2px`,
            `background:none;border:none;cursor:${draggable ? 'grab' : 'pointer'};font-family:inherit`
        ].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:normal;word-break:break-word;text-align:center;line-height:1.15;pointer-events:none;max-width:${BTN_W}px`;
        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;
        _rebuild();
    };

    // ── 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 v' + GM_info.script.version, 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);

    // Beni Oku button — top
    const btnRow0 = mk('div'); btnRow0.style.cssText = 'display:flex;gap:6px;margin-bottom:10px';
    const guideBtn = mkB(STR.guide, '', () => window.open('https://rentry.org/PopControl', '_blank'));
    guideBtn.style.cssText = 'flex:1;padding:9px;border-radius:8px;background:#6f42c1;color:#fff;border:none;cursor:pointer;font-size:13px;font-weight:600;font-family:inherit';
    const intBtn = mkB(STR.integrate, '', () => window.open('https://rentry.org/PopControlEntegre', '_blank'));
    intBtn.style.cssText = 'flex:1;padding:9px;border-radius:8px;background:#5a30a0;color:#fff;border:none;cursor:pointer;font-size:13px;font-weight:600;font-family:inherit';
    btnRow0.append(guideBtn, intBtn);
    box.appendChild(btnRow0);
    box.appendChild(mkHr());

    // 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 + Customize
    box.appendChild(mkH(STR.langLabel));
    const _hasCustom = ['ppc_lc_helper','ppc_lc_social','ppc_lc_social_mobile','ppc_lc_depot','ppc_lc_missionaid'].some(k => localStorage.getItem(k));
    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(); }],
        ['CU','🌍 Customize', _hasCustom, () => _openCustomize(ov)],
    ]));
    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;
        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; 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');
            onPick(ci.value);
        };
        const ciLbl = mk('span'); ciLbl.textContent = '🎨';
        ciLbl.style.cssText = 'font-size:14px;cursor:default;margin-left:2px;flex-shrink:0';
        ciLbl.title = STR.colorHint;
        wrap.append(ci, ciLbl);
        const outerWrap = mk('div');
        outerWrap.append(wrap);
        return outerWrap;
    };

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

    box.appendChild(mkH(STR.colorText));
    box.appendChild(mkSwatches(PRESET_FG, _getColFg(), v => { _setColFg(v); _applyColorsD(); }));
    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);
    }





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


// ─── CUSTOMIZE (LANGUAGE IMPORT/EXPORT) ──────────────────────────────────────
function _openCustomize(parentOv) {
    const LC_KEYS = {
        helper:        'ppc_lc_helper',
        social:        'ppc_lc_social',
        social_mobile: 'ppc_lc_social_mobile',
        depot:         'ppc_lc_depot',
        missionaid:    'ppc_lc_missionaid',
    };
    const hasCustom = Object.values(LC_KEYS).some(k => localStorage.getItem(k));

    const ov = document.createElement('div');
    ov.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.75);z-index:100001;display:flex;align-items:center;justify-content:center;padding:16px;box-sizing:border-box';
    const box = document.createElement('div');
    box.style.cssText = 'background:#fff;border-radius:14px;padding:20px;width:100%;max-width:480px;max-height:90vh;overflow-y:auto;box-sizing:border-box;position:relative;font-family:inherit';

    // Header
    const hdr = document.createElement('div');
    hdr.style.cssText = 'display:flex;justify-content:space-between;align-items:center;margin-bottom:16px';
    const title = document.createElement('span');
    title.textContent = '🌍 Customize Language';
    title.style.cssText = 'font-weight:700;font-size:16px';
    const xBtn = document.createElement('button');
    xBtn.textContent = '✕'; xBtn.type = 'button';
    xBtn.style.cssText = 'background:none;border:none;font-size:20px;cursor:pointer;color:#aaa;padding:0';
    xBtn.onclick = () => ov.remove();
    hdr.append(title, xBtn);
    box.appendChild(hdr);

    // Active status
    if (hasCustom) {
        const badge = document.createElement('div');
        badge.textContent = '✅ Custom language active';
        badge.style.cssText = 'background:#d4edda;color:#155724;border-radius:6px;padding:7px 12px;font-size:12px;font-weight:600;margin-bottom:12px';
        box.appendChild(badge);
    }

    // Gemini tip
    const tip = document.createElement('div');
    tip.style.cssText = 'background:#e8f4fd;border-radius:8px;padding:10px 12px;font-size:12px;color:#1a5276;margin-bottom:14px;line-height:1.5';
    tip.innerHTML = '💡 <strong>Best results:</strong> Use <a href="https://gemini.google.com" target="_blank" style="color:#1a5276;font-weight:700">Google Gemini</a> for translation — it handles JSON format and emoji preservation reliably.';
    box.appendChild(tip);

    const mkHr = () => { const hr = document.createElement('hr'); hr.style.cssText = 'border:none;border-top:1px solid #eee;margin:14px 0'; return hr; };

    // ── EXPORT ──────────────────────────────────────────────────────────────
    box.appendChild(Object.assign(document.createElement('div'), {
        textContent: '📤 Export',
        style: 'font-weight:700;font-size:13px;margin-bottom:6px'
    }));
    const expHint = document.createElement('div');
    expHint.textContent = 'Copies the EN strings from all connected scripts. Paste into Gemini to translate.';
    expHint.style.cssText = 'font-size:11px;color:#666;margin-bottom:8px';
    box.appendChild(expHint);

    const expBtn = document.createElement('button');
    expBtn.textContent = '📋 Copy Export JSON + Prompt';
    expBtn.type = 'button';
    expBtn.style.cssText = 'width:100%;padding:10px;background:#0d6efd;color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:600;font-family:inherit';
    expBtn.onclick = () => {
        const collected = {};
        _registry.forEach(entry => {
            if (entry.strings && Object.keys(entry.strings).length) {
                collected[entry.id] = entry.strings;
            }
        });
        if (!Object.keys(collected).length) {
            expBtn.textContent = '⚠️ No scripts registered yet — navigate first';
            setTimeout(() => expBtn.textContent = '📋 Copy Export JSON + Prompt', 2500);
            return;
        }
        const promptText =
            'Translate the JSON values below to the target language.\n' +
            'Rules: keep all emojis, {n}, %s and \\n exactly as-is. Return ONLY valid JSON, no markdown, no explanation.\n\n' +
            'Target language: [ENTER TARGET LANGUAGE HERE]\n\n' +
            JSON.stringify(collected, null, 2);
        const ta = document.createElement('textarea');
        ta.value = promptText;
        document.body.appendChild(ta); ta.select(); document.execCommand('copy');
        document.body.removeChild(ta);
        navigator.clipboard?.writeText(promptText).catch(() => {});
        expBtn.textContent = '✅ Copied!';
        setTimeout(() => expBtn.textContent = '📋 Copy Export JSON + Prompt', 2500);
    };
    box.appendChild(expBtn);

    box.appendChild(mkHr());

    // ── IMPORT ──────────────────────────────────────────────────────────────
    box.appendChild(Object.assign(document.createElement('div'), {
        textContent: '📥 Import',
        style: 'font-weight:700;font-size:13px;margin-bottom:6px'
    }));
    const impHint = document.createElement('div');
    impHint.textContent = 'Paste the translated JSON returned by Gemini (or any AI). Accepts full object or partial.';
    impHint.style.cssText = 'font-size:11px;color:#666;margin-bottom:8px';
    box.appendChild(impHint);

    const impTa = document.createElement('textarea');
    impTa.placeholder = '{ "helper": { "save": "Speichern", ... }, "social": { ... }, ... }';
    impTa.style.cssText = 'width:100%;height:160px;font-size:11px;font-family:monospace;padding:8px;border:1px solid #ddd;border-radius:6px;box-sizing:border-box;resize:vertical;margin-bottom:8px';
    box.appendChild(impTa);

    const errDiv = document.createElement('div');
    errDiv.style.cssText = 'font-size:12px;min-height:18px;margin-bottom:8px';
    box.appendChild(errDiv);

    const applyBtn = document.createElement('button');
    applyBtn.textContent = '✅ Apply & Reload';
    applyBtn.type = 'button';
    applyBtn.style.cssText = 'width:100%;padding:10px;background:#198754;color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:600;font-family:inherit;margin-bottom:8px';
    applyBtn.onclick = () => {
        try {
            const raw = impTa.value.trim();
            if (!raw) { errDiv.style.color='#dc3545'; errDiv.textContent='Paste translated JSON first.'; return; }
            const data = JSON.parse(raw);
            let applied = 0;
            Object.entries(LC_KEYS).forEach(([id, lsKey]) => {
                if (data[id] && typeof data[id] === 'object') {
                    localStorage.setItem(lsKey, JSON.stringify(data[id]));
                    applied++;
                }
            });
            if (!applied) { errDiv.style.color='#dc3545'; errDiv.textContent='No matching script keys found (helper, social, social_mobile, depot, missionaid).'; return; }
            errDiv.style.color = '#198754';
            errDiv.textContent = '✅ Applied to ' + applied + ' script(s) — reloading...';
            setTimeout(() => { ov.remove(); if (parentOv) parentOv.remove(); location.reload(); }, 1200);
        } catch (err) {
            errDiv.style.color = '#dc3545';
            errDiv.textContent = '❌ Invalid JSON: ' + err.message;
        }
    };
    box.appendChild(applyBtn);

    box.appendChild(mkHr());

    // ── RESET ───────────────────────────────────────────────────────────────
    const resetBtn = document.createElement('button');
    resetBtn.textContent = '🗑️ Reset to Default Language';
    resetBtn.type = 'button';
    resetBtn.style.cssText = 'width:100%;padding:8px;background:none;color:#dc3545;border:1px solid #dc3545;border-radius:8px;cursor:pointer;font-size:12px;font-family:inherit';
    resetBtn.onclick = () => {
        if (!confirm('Remove all custom language data and restore defaults?')) return;
        Object.values(LC_KEYS).forEach(k => localStorage.removeItem(k));
        ov.remove(); if (parentOv) parentOv.remove(); location.reload();
    };
    if (hasCustom) box.appendChild(resetBtn);

    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();
}
const _applyColorsD = (() => { let _t; return () => { clearTimeout(_t); _t = setTimeout(_applyColors, 300); }; })();

// ─── PUBLIC API ───────────────────────────────────────────────────────────────
unsafeWindow.PopControl = {
    register({ id, icon, label, buttons, strings, onUndo }) {
        if (_registry.some(r => r.id === id)) return;
        _registry.push({ id, icon: icon || '🔌', label: label || id, buttons: buttons || [], strings: strings || {}, 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,
};

// ─── ALT+P KISAYOL ──────────────────────────────────────────────────────────
document.addEventListener('keydown', function(e) {
    if (e.altKey && (e.key === 'p' || e.key === 'P')) {
        e.preventDefault();
        const bar = document.getElementById('ppc-bar');
        const fab = document.getElementById('ppc-fab');
        if (!bar) return;
        if (_collapsed) {
            // Göster
            _collapsed = false;
            bar.style.display = '';
            if (fab) fab.style.display = 'none';
        } else {
            // Gizle — FAB gösterme, tamamen gizle
            _collapsed = true;
            bar.style.display = 'none';
            if (fab) fab.style.display = 'none';
        }
    }
});

// ─── 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);
})();

})();