🌐 Social

Popmundo'nun en kapsamlı sosyal scripti — Efsane Speed Calling ve yalnızca burada bulabileceğin Radar Takibi, Raf, Karakter Kartı, İlgilenme Rehberi ve daha fazlası.

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

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

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         🌐 Social
// @name:en      🌐 Social
// @name:pt-BR   🌐 Social
// @namespace    popmundo.social
// @version      3.1
// @description  Popmundo'nun en kapsamlı sosyal scripti — Efsane Speed Calling ve yalnızca burada bulabileceğin Radar Takibi, Raf, Karakter Kartı, İlgilenme Rehberi ve daha fazlası.
// @description:en  The most comprehensive social script for Popmundo — Legendary Speed Calling and features you won't find anywhere else: Radar Tracking, Shelf, Character Card, Interaction Guide and more.
// @description:pt-BR O script social mais completo do Popmundo — O lendário Speed Calling e recursos exclusivos: Radar, Prateleira, Cartão de Personagem, Guia de Interação e muito mais.
// @author       luke-james-gibson
// @license      MIT
// @id           9g1a6x
// @match        https://*.popmundo.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        unsafeWindow
// @run-at       document-end
// ==/UserScript==

(function () {
try {
'use strict';

// POPCONTROL DISABLE CHECK
try { const _ppc = JSON.parse(localStorage.getItem('ppc_enabled')||'{}'); if (_ppc['social'] === false) return; } catch {}

// DEVICE BLOCK — Mobilde çalışmaz; override için ppsm_soc_mob_ack=1 set et
(function() {
    const _isMob = /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile/i.test(navigator.userAgent);
    if (!_isMob) return; // Desktop — devam
    if (localStorage.getItem('ppsm_soc_mob_ack')) return; // Kullanıcı bypass seçti
    const ov = document.createElement('div');
    ov.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.7);z-index:99999;display:flex;align-items:center;justify-content:center;font-family:sans-serif;padding:16px';
    ov.innerHTML = '<div style="background:#fff;border-radius:12px;padding:24px;max-width:340px;width:100%;text-align:center">'
        + '<div style="font-size:36px;margin-bottom:8px">📱</div>'
        + '<div style="font-weight:bold;font-size:15px;margin-bottom:8px">Mobil Cihaz Tespit Edildi</div>'
        + '<div style="font-size:13px;color:#555;margin-bottom:16px">Bu script masaüstü cihazlar için tasarlanmıştır. Mobil kullanım için <b>Social Mobile</b> scriptini kullanmanız önerilir.</div>'
        + '<a href="https://greasyfork.org/tr/scripts/568918-social-mobile" target="_blank" style="display:inline-block;margin-bottom:16px;color:#6f42c1;font-weight:bold;font-size:13px;text-decoration:none">📥 Social Mobile — İndir / Install</a>'
        + '<div style="display:flex;gap:8px;justify-content:center">'
        + '<button id="_ppsm_ack" style="background:#6f42c1;color:#fff;border:none;border-radius:6px;padding:8px 18px;cursor:pointer;font-size:13px;font-family:inherit">Yine de Kullan</button>'
        + '<button id="_ppsm_close" style="background:#eee;border:none;border-radius:6px;padding:8px 18px;cursor:pointer;font-size:13px;font-family:inherit">Kapat</button>'
        + '</div></div>';
    document.body ? document.body.appendChild(ov) : document.addEventListener('DOMContentLoaded', () => document.body.appendChild(ov));
    document.addEventListener('click', function _h(e) {
        if (e.target.id === '_ppsm_ack') { localStorage.setItem('ppsm_soc_mob_ack','1'); location.reload(); }
        if (e.target.id === '_ppsm_ack' || e.target.id === '_ppsm_close') { ov.remove(); document.removeEventListener('click', _h); }
    });
    throw new Error('__ppsm_device_block__');
})();

// CSS
document.head.appendChild(Object.assign(document.createElement('style'), { textContent: `
.tvis-bar{position:fixed;top:0;z-index:9986;background:#f5f0ff;border:1px solid #c9b8f0;border-radius:0 0 0 6px;padding:3px 10px;font-size:11px;display:flex;gap:8px;align-items:center;box-shadow:0 1px 6px rgba(0,0,0,.15)}
.tvis-bar a,.tvis-bar button.tvis-lnk{font-weight:bold;text-decoration:none;color:#6f42c1;cursor:pointer;background:none;border:none;font-size:11px;padding:0}
.tvis-hpanel{display:none;position:fixed;top:26px;z-index:9985;background:#fff;border:1px solid #c9b8f0;border-radius:0 0 0 6px;padding:12px;min-width:240px;max-width:300px;box-shadow:0 4px 12px rgba(0,0,0,.15);max-height:82vh;overflow-y:auto}
.tvis-ov{position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:99999;display:flex;align-items:flex-start;padding:40px 10px 10px;justify-content:center;overflow-y:auto}
.tvis-box{background:#fff;border-radius:8px;padding:18px;min-width:320px;max-width:min(95vw,1500px);width:95%;box-shadow:0 4px 24px rgba(0,0,0,.35)}
.tvis-title{font-weight:bold;font-size:14px;margin-bottom:12px}
.tvis-chk{display:flex;align-items:flex-start;gap:6px;margin-bottom:8px;cursor:pointer;font-size:12px;line-height:1.4}
.tvis-chk input{margin-top:2px;flex-shrink:0;cursor:pointer}
.tvis-hr{border:none;border-top:1px solid #e0e0e0;margin:6px 0}
.tvis-sec{font-size:10px;font-weight:bold;color:#888;margin:8px 0 3px;text-transform:uppercase;letter-spacing:.5px}
.tvis-lang-row{display:flex;gap:4px;margin:6px 0}
.tvis-lang-btn{flex:1;padding:3px 4px;border:1px solid #ccc;border-radius:4px;cursor:pointer;font-size:11px;background:#f8f9fa;text-align:center}
.tvis-lang-btn.active{background:#6f42c1;color:#fff;border-color:#6f42c1;font-weight:bold}
.tvis-prow{display:flex;align-items:center;gap:4px;padding:4px 6px;background:#f8f9fa;border-radius:4px;margin-bottom:3px;border:1px solid #e8e8e8;font-size:12px}
.tvis-prow[draggable]{cursor:grab}
.tvis-prow[draggable]:active{opacity:.7}
.tvis-prow.drag-over{border-top:2px solid #6f42c1}
.tvis-plink{flex:1;text-decoration:none;color:#6f42c1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tvis-pnote{font-size:10px;color:#aaa;font-style:italic;max-width:70px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-shrink:0}
.tvis-pedit{background:#f5f0ff;border:1px solid #c9b8f0;border-radius:4px;padding:6px;margin-bottom:3px;font-size:11px}
.tvis-pin-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;margin-bottom:8px}
.tvis-ia{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;font-size:12px;text-decoration:none!important;cursor:pointer;background:transparent;border:none;padding:0;margin-left:2px;opacity:.55;vertical-align:middle;line-height:1}
.tvis-ia:hover{opacity:1}
.tvis-ia-wrap{display:inline;white-space:nowrap}
.tvis-iarow{display:flex;align-items:center;gap:4px;padding:4px 6px;background:#f8f9fa;border-radius:4px;margin-bottom:3px;border:1px solid #e8e8e8;font-size:12px}
.tvis-iarow[draggable]{cursor:grab}
.tvis-iarow[draggable]:active{opacity:.7}
.tvis-iarow.drag-over{border-top:2px solid #6f42c1}
.tvis-icon-pick{display:flex;flex-wrap:wrap;gap:3px;margin:4px 0 6px;max-height:120px;overflow-y:auto}
.tvis-icon-pick button{width:26px;height:26px;font-size:13px;cursor:pointer;border-radius:3px;border:1px solid #ddd;background:#fff;padding:0;line-height:1}
.tvis-icon-pick button.sel{border:2px solid #6f42c1;background:#f0ebff}
.tvis-custom-icons{display:flex;flex-wrap:wrap;gap:3px;margin-bottom:4px;min-height:8px}
.tvis-custom-chip{background:#f0ebff;border:1px solid #c9b8f0;border-radius:3px;padding:1px 5px;font-size:13px;display:inline-flex;align-items:center;gap:3px}
.tvis-custom-chip button{background:none;border:none;color:#e74c3c;cursor:pointer;font-size:11px;padding:0;line-height:1}
.tvis-notebar{background:#fff9e6;border:1px solid #f0c040;border-radius:4px;padding:5px 8px;margin-top:6px;display:flex;align-items:center;gap:6px}
.tvis-notebar textarea{flex:1;font-size:11px;padding:3px;border:1px solid #ddd;border-radius:3px;resize:none;height:26px;font-family:inherit}
/* CHAR HOVER POPUP */
.tvis-chpop{position:fixed;z-index:99997;background:#fff;border:1px solid #bbb;border-radius:8px;width:380px;display:none;padding:0;overflow:hidden;box-shadow:0 4px 18px rgba(0,0,0,.22);pointer-events:auto}
.tvis-pop-img{width:114px;min-width:114px;height:156px;flex-shrink:0;background:linear-gradient(160deg,#ddd5ff,#9b72f0);display:flex;align-items:center;justify-content:center;font-size:44px}
.tvis-pop-img img{width:114px;height:156px;object-fit:cover}
.tvis-pop-right{flex:1;display:flex;flex-direction:column;border-left:1px solid #ede9ff;min-width:0}
.tvis-pop-head{background:#f5f0ff;padding:7px 9px 5px;border-bottom:1px solid #ede9ff}
.tvis-pop-nameline{display:flex;align-items:center;gap:5px}
.tvis-pop-name{font-weight:700;font-size:12px;color:#2d1e6b;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tvis-pop-copyid{background:#ede9ff;border:1px solid #c9b8f0;color:#6f42c1;border-radius:3px;font-size:9px;padding:1px 5px;cursor:pointer}
.tvis-pop-body{flex:1;padding:5px 9px 4px;display:flex;flex-direction:column;gap:3px;overflow:hidden}
.tvis-pop-links{padding:5px 9px;border-top:1px solid #f0f0f0;display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.tvis-pop-link{font-size:17px;opacity:.6;text-decoration:none}
.tvis-pop-link:hover{opacity:1}
.tvis-trackbtn{background:none;border:1px solid #c9b8f0;color:#6f42c1;border-radius:3px;font-size:9px;padding:1px 6px;cursor:pointer;flex-shrink:0}
.tvis-trackbtn.tracked{background:#fff8e6;border-color:#e0a800;color:#c8900a}
.tvis-updatebtn{background:#ede9ff;border:1px solid #c9b8f0;color:#6f42c1;border-radius:3px;font-size:9px;padding:1px 5px;cursor:pointer;display:inline-flex;align-items:center;gap:2px}
.tvis-updatebtn:hover{background:#d4c5f9;border-color:#9b72f0}
/* RADAR BULK PROGRESS */
.tvis-rprog{position:fixed;bottom:20px;left:20px;z-index:999998;background:#2d1e6b;color:#fff;font-size:12px;padding:9px 14px;border-radius:7px;box-shadow:0 4px 16px rgba(0,0,0,.35);display:flex;align-items:center;gap:10px;min-width:220px;max-width:calc(100vw - 40px)}
.tvis-rprog-txt{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tvis-rprog-cancel{background:#e74c3c;border:none;color:#fff;border-radius:4px;font-size:11px;padding:2px 8px;cursor:pointer;flex-shrink:0;min-width:60px}
.tvis-rprog-cancel:hover{background:#c0392b}
.tvis-rprog.done{background:#218838}
.tvis-rprog.stopped{background:#6c757d}
.tvis-toast{position:fixed;bottom:20px;right:20px;background:#2d1e6b;color:#fff;font-size:12px;padding:10px 16px;border-radius:6px;box-shadow:0 4px 16px rgba(0,0,0,.35);z-index:999999;opacity:0;transition:opacity .3s;pointer-events:none;max-width:calc(100vw - 40px)}
.tvis-toast.show{opacity:1}

/* Mobile-specific improvements */
@media (max-width: 768px) {
    .tvis-rprog {
        bottom: 10px;
        left: 10px;
        right: 10px;
        min-width: auto;
        padding: 8px 12px;
        font-size: 11px;
    }
    
    .tvis-rprog-cancel {
        padding: 4px 6px;
        font-size: 10px;
        min-width: 50px;
    }
    
    .tvis-toast {
        bottom: 70px;
        left: 10px;
        right: 10px;
        font-size: 11px;
        padding: 8px 12px;
        text-align: center;
    }
    
    #tvis-global-stop {
        padding: 8px;
        font-size: 12px;
    }
}
/* RADAR GRID — no inner scrollbar; outer modal box scrolls */
.tvis-tgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(280px,100%),1fr));gap:6px;overflow-x:hidden}
.tvis-tcard{width:100%;height:156px;min-width:0;border:1px solid #e0e0e0;border-radius:7px;overflow:hidden;background:#fafafa;display:flex;cursor:grab;position:relative;transition:box-shadow .15s,border-color .15s}
.tvis-tcard:hover{box-shadow:0 3px 12px rgba(111,66,193,.15);border-color:#c9b8f0}
.tvis-tcard.tc-online{border-left:3px solid #28a745}
.tvis-tcard.tc-moved{border:2px solid #e74c3c;background:#fff8f8;animation:tvis-pulse 2s infinite}
.tvis-tcard.tc-updated{border-left:3px solid #f0c040;background:#fffef5}
@keyframes tvis-pulse{0%,100%{border-color:#e74c3c}50%{border-color:#ff8080}}
.tvis-tc-img{width:114px;min-width:114px;height:156px;flex-shrink:0;position:relative;overflow:hidden}
.tvis-tc-imgbg{width:114px;height:156px;background:linear-gradient(160deg,#ddd5ff,#9b72f0);display:flex;align-items:center;justify-content:center;font-size:44px}
.tvis-tc-imgbg img{width:114px;height:156px;object-fit:cover;display:block}
.tvis-tc-drag{position:absolute;top:3px;left:3px;color:rgba(255,255,255,.9);font-size:10px;background:rgba(0,0,0,.3);border-radius:2px;padding:0 3px;line-height:1.6;cursor:grab}
.tvis-tc-badge{position:absolute;bottom:4px;left:0;right:0;display:flex;justify-content:center}
.tvis-tc-badge span{font-size:8px;padding:1px 7px;border-radius:10px;color:#fff}
.badge-on{background:rgba(40,167,69,.85)}
.badge-off{background:rgba(100,100,100,.75)}
.badge-mv{background:rgba(231,76,60,.9);font-weight:700}
.badge-upd{background:rgba(240,192,64,.9);color:#333!important}
.tvis-tc-body{flex:1;min-width:0;padding:7px 7px 5px;display:flex;flex-direction:column;gap:2px;border-left:1px solid #ede9ff;overflow:hidden}
.tvis-tc-name{font-weight:700;font-size:10px;color:#6f42c1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-decoration:none;display:block}
.tvis-tc-row{font-size:9px;color:#666;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.35}
.tvis-tc-row a{color:#17a2b8;text-decoration:none}
.tvis-tc-warn{font-size:9px;color:#e74c3c;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tvis-tc-warn.upd{color:#c8900a}
/* Note textarea — 2 lines tall */
.tvis-tc-note{font-size:9px;border:1px solid #e0e0e0;border-radius:3px;padding:2px 4px;width:100%;box-sizing:border-box;resize:none;font-family:inherit;color:#555;height:34px;margin-top:1px}
.tvis-tc-actions{display:flex;gap:3px;align-items:center;margin-top:auto}
.tvis-tc-time{font-size:9px;color:#bbb;white-space:nowrap}
.tvis-tc-ref{background:#ede9ff;border:1px solid #c9b8f0;color:#6f42c1;border-radius:3px;font-size:8px;padding:1px 4px;cursor:pointer}
.tvis-tc-ref:hover{background:#d4c5f9}
.tvis-tc-ok{background:#218838;color:#fff;border:none;border-radius:3px;font-size:8px;padding:1px 4px;cursor:pointer}
.tvis-tc-x{background:#e74c3c;color:#fff;border:none;border-radius:3px;font-size:8px;padding:1px 4px;cursor:pointer}
.tvis-tc-empty{width:100%;height:156px;border:1px dashed #e8e8e8;border-radius:7px;background:#fafafa}
.tvis-page-tab{background:#f8f9fa;border:1px solid #e0e0e0;color:#555;border-radius:4px;font-size:10px;padding:3px 8px;cursor:pointer;white-space:nowrap;transition:background .15s,color .15s}
.tvis-page-tab.active{color:#fff;font-weight:700}
/* RENK SEÇİCİ */
.tvis-cpick{display:flex;flex-wrap:wrap;gap:3px;margin:4px 0 6px;align-items:center}
.tvis-cpick-sw{width:20px;height:20px;border-radius:3px;border:2px solid transparent;cursor:pointer;padding:0;flex-shrink:0}
.tvis-cpick-sw.sel{border-color:#333;transform:scale(1.2)}
.tvis-cpick-sw:hover{transform:scale(1.15)}
.tvis-cpick-custom{width:26px;height:22px;padding:1px;border:1px solid #ccc;border-radius:3px;cursor:pointer;margin-left:2px}
/* RAF */
.tvis-raf-tabs{display:flex;gap:4px;flex-wrap:wrap;margin-bottom:10px}
.tvis-raf-tab{padding:4px 10px;border-radius:4px;border:2px solid #ccc;font-size:11px;cursor:pointer;font-weight:600;background:#fff;white-space:nowrap;transition:.15s;user-select:none}
.tvis-raf-tab.active{color:#fff}
.tvis-raf-tab.drag-over-tab{outline:2px dashed #6f42c1;outline-offset:2px}
.tvis-raf-tab-badge{display:inline-block;background:rgba(0,0,0,.18);color:inherit;font-size:9px;font-weight:700;border-radius:8px;padding:0 4px;margin-left:4px;vertical-align:middle;min-width:14px;text-align:center}
.tvis-raf-tab.active .tvis-raf-tab-badge{background:rgba(255,255,255,.3)}
.tvis-raf-tab-edit{background:none;border:none;font-size:10px;cursor:pointer;opacity:.5;padding:0 2px}
.tvis-raf-tab-edit:hover{opacity:1}
.tvis-raf-recent-tab{padding:4px 10px;border-radius:4px;border:2px dashed #c9b8f0;font-size:11px;cursor:pointer;font-weight:600;background:#fdf8ff;color:#6f42c1;white-space:nowrap;transition:.15s}
.tvis-raf-recent-tab.active{background:#6f42c1;color:#fff;border-color:#6f42c1}
.tvis-raf-colcount{display:flex;align-items:center;gap:4px;margin-bottom:6px;font-size:10px;color:#aaa}
.tvis-raf-colcount button{padding:1px 6px;border:1px solid #ccc;border-radius:3px;background:#fff;font-size:10px;cursor:pointer;color:#666}
.tvis-raf-colcount button.sel{background:#6f42c1;color:#fff;border-color:#6f42c1}
.tvis-raf-grid{display:grid;gap:6px;min-width:600px}
.tvis-raf-col{background:#f8f9fa;border:1px solid #e0e0e0;border-radius:6px;padding:5px;min-height:60px}
.tvis-raf-col.drag-target{outline:2px dashed #6f42c1;background:#f5f0ff}
.tvis-raf-col-head{display:flex;gap:3px;align-items:center;margin-bottom:5px}
.tvis-raf-col-name{flex:1;padding:2px 4px;border:1px solid #ddd;border-radius:3px;font-size:11px;font-weight:600;background:#fff}
.tvis-raf-card{display:flex;align-items:center;gap:3px;padding:3px 5px;background:#fff;border:1px solid #e8e8e8;border-radius:3px;margin-bottom:2px;font-size:11px;cursor:grab}
.tvis-raf-card:active{opacity:.6}
.tvis-raf-card.drag-over{border-top:2px solid #6f42c1}
.tvis-raf-card.colored{border-color:transparent}
.tvis-raf-card-color{width:4px;min-width:4px;border-radius:2px;flex-shrink:0;align-self:stretch}
.tvis-raf-card-ico{flex-shrink:0;font-size:13px}
.tvis-raf-card-lbl{flex:1;text-decoration:none;color:#333;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tvis-raf-card-lbl:hover{color:#6f42c1}
.tvis-raf-card.colored .tvis-raf-card-lbl{color:inherit}
.tvis-raf-card.colored .tvis-raf-card-lbl:hover{opacity:.85;color:inherit}
.tvis-raf-card-note{font-size:9px;color:#888;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:80px;flex-shrink:0;font-style:italic}
.tvis-raf-card.colored .tvis-raf-card-note{color:inherit;opacity:.75}
.tvis-raf-edit{background:#f8f9fa;border:1px solid #ddd;border-radius:4px;padding:6px;margin-bottom:4px;font-size:11px}
/* WATCH INDICATOR */

.tvis-tab-edit-btn{background:none;border:none;font-size:10px;cursor:pointer;opacity:.5;padding:0 2px;vertical-align:middle}
.tvis-tab-edit-btn:hover{opacity:1}
.tvis-tab-edit-pop{position:fixed;z-index:999999;background:#fff;border:1px solid #c9b8f0;border-radius:6px;padding:8px;box-shadow:0 4px 14px rgba(0,0,0,.2);min-width:220px}
/* Page picker popup */
.tvis-page-picker{position:fixed;z-index:999999;background:#fff;border:1px solid #c9b8f0;border-radius:6px;padding:8px;box-shadow:0 4px 14px rgba(0,0,0,.2);min-width:190px}
/* INTERACT HELPER */
.tvis-ih-bar{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-bottom:6px;padding:5px 8px;background:#f5f0ff;border:1px solid #c9b8f0;border-radius:5px}
.tvis-ih-typebtn{padding:2px 8px;border-radius:4px;border:1px solid #c9b8f0;background:#fff;color:#6f42c1;font-size:11px;cursor:pointer;font-weight:400;transition:all .12s}
.tvis-ih-typebtn.active{background:#6f42c1;color:#fff;font-weight:700}
.tvis-ih-typebtn.t-arkadaş{border-color:#2196f3;color:#2196f3}
.tvis-ih-typebtn.t-arkadaş.active{background:#2196f3;color:#fff}
.tvis-ih-typebtn.t-romantik{border-color:#e91e63;color:#e91e63}
.tvis-ih-typebtn.t-romantik.active{background:#e91e63;color:#fff}
.tvis-ih-typebtn.t-nefret{border-color:#f44336;color:#f44336}
.tvis-ih-typebtn.t-nefret.active{background:#f44336;color:#fff}
.tvis-ih-warn{background:#fff3cd;border:1px solid #ffc107;border-radius:4px;padding:4px 8px;font-size:11px;color:#856404;margin-bottom:4px}
.tvis-ih-guide-tbl{border-collapse:collapse;font-size:11px;width:100%;min-width:580px}
.tvis-ih-guide-tbl th{background:#f5f0ff;color:#6f42c1;font-size:10px;padding:2px 4px;text-align:left;border-bottom:1px solid #c9b8f0;white-space:nowrap}
.tvis-ih-guide-tbl td{padding:2px 4px;border-bottom:1px solid #f0f0f0;vertical-align:middle}
.tvis-ih-guide-tbl td:first-child{white-space:nowrap}
.tvis-ih-guide-tbl td:nth-child(2){max-width:160px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tvis-ih-guide-tbl tr:hover td{background:#fdf8ff}.tvis-ih-joy{color:#28a745;font-size:10px;font-weight:600}
.tvis-ih-love{color:#e91e63;font-size:10px;font-weight:600}
.tvis-ih-hate{color:#dc3545;font-size:10px;font-weight:600}
.tvis-ih-note{color:#999;font-size:9px;font-style:italic}
.tvis-ih-badge{display:inline-block;font-size:9px;padding:1px 4px;border-radius:3px;margin-left:3px;font-weight:700}
.tvis-ih-badge-a{background:#e3f2fd;color:#1565c0}
.tvis-ih-badge-r{background:#fce4ec;color:#b71c1c}
.tvis-ih-badge-n{background:#ffebee;color:#b71c1c}
.tvis-ih-jlabel{display:inline-flex;align-items:center;gap:3px;font-size:11px;cursor:pointer;padding:2px 6px;border-radius:3px;border:1px solid #ddd;background:#fff}
.tvis-ih-jlabel.active-no{border-color:#28a745;color:#28a745;background:#f0fff4}
.tvis-ih-jlabel.active-yes{border-color:#e91e63;color:#e91e63;background:#fff0f5}
.frl-addbtn{display:inline-block;margin-left:5px;padding:1px 6px;border-radius:3px;border:1px solid #c9b8f0;background:#f5f0ff;color:#6f42c1;font-size:12px;cursor:pointer;vertical-align:middle;transition:all .15s;line-height:1.5}
.frl-addbtn:hover{background:#e0d0ff;border-color:#9b72f0}
.frl-addbtn.on{background:#6f42c1;color:#fff;border-color:#6f42c1}
.frl-addbtn.on:hover{background:#5a32a3;border-color:#5a32a3}
/* GENRE POPUP */
.tvis-gpop-iframe{width:100%;height:68vh;min-height:480px;border:none;border-radius:4px;display:block}
/* SERENADE HELPER */
.tvis-sh-row{display:flex;gap:6px;align-items:baseline;padding:4px 4px;border-bottom:1px solid #f0f0f0;font-size:11px}
.tvis-sh-row:last-child{border-bottom:none}
.tvis-sh-prefix{font-weight:700;color:#6f42c1;min-width:54px;flex-shrink:0;font-size:10px}
.tvis-sh-track{flex:1;color:#444;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tvis-sh-empty{font-size:11px;color:#999;padding:10px 4px;font-style:italic}
.tvis-sh-list{max-height:52vh;overflow-y:auto;margin-bottom:2px;border:1px solid #ede9ff;border-radius:4px;padding:2px 0}
.tvis-sh-footer{font-size:10px;color:#888;padding:6px 0 2px;border-top:1px solid #e0e0e0;margin-top:6px;display:flex;flex-direction:column;gap:4px}
.tvis-sh-prog{font-size:11px;color:#6f42c1;padding:3px 0;font-weight:600;min-height:16px}
.tvis-sh-warn{font-size:11px;color:#c8900a;padding:2px 0;min-height:14px}
/* SPEED CALLING BAR */
.tvis-sc-bar{position:fixed;top:0;left:0;right:0;z-index:999999;background:#1a1035;color:#fff;font-size:12px;padding:5px 12px;display:flex;align-items:center;gap:10px;box-shadow:0 2px 8px rgba(0,0,0,.4);font-family:inherit}
.tvis-sc-bar a,.tvis-sc-bar button{font-family:inherit}
.tvis-sc-stat{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tvis-sc-name{color:#c9b8f0;font-weight:600}
.tvis-sc-timer{color:#ffd700;font-weight:700;min-width:28px;text-align:center}
.tvis-sc-btn{background:#6f42c1;border:none;color:#fff;border-radius:4px;font-size:11px;padding:3px 10px;cursor:pointer;flex-shrink:0}
.tvis-sc-btn:hover{background:#5a32a3}
.tvis-sc-btn.stop{background:#e74c3c}
.tvis-sc-btn.stop:hover{background:#c0392b}
.tvis-sc-btn.resume{background:#218838}
.tvis-sc-btn.resume:hover{background:#1a6e2e}
.tvis-sc-warn{background:#fff3cd;color:#856404;border:1px solid #ffc107;border-radius:4px;padding:2px 8px;font-size:11px;flex-shrink:0}
/* SPEED CALLING MODAL */
.tvis-sc-phonerow{display:flex;align-items:center;gap:6px;margin-bottom:5px;font-size:12px}
.tvis-sc-phoneinp{flex:1;padding:3px 6px;border:1px solid #c9b8f0;border-radius:4px;font-size:11px;font-family:monospace}
.tvis-sc-charlist{max-height:180px;overflow-y:auto;border:1px solid #e0e0e0;border-radius:4px;margin:4px 0 8px;font-size:11px}
.tvis-sc-charrow{display:flex;align-items:center;gap:6px;padding:3px 7px;border-bottom:1px solid #f0f0f0}
.tvis-sc-charrow:last-child{border-bottom:none}
.tvis-sc-charrow input[type=checkbox]{flex-shrink:0;cursor:pointer}
.tvis-sc-badge{font-size:9px;padding:1px 5px;border-radius:8px;font-weight:700}
.tvis-sc-badge.r{background:#fce4ec;color:#b71c1c}
.tvis-sc-badge.a{background:#e3f2fd;color:#1565c0}
` }));

// 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 gmGet    = (k, def = null) => { try { const v = GM_getValue(k, null); return v !== null ? (typeof v === 'string' && (v[0] === '[' || v[0] === '{') ? JSON.parse(v) : v) : def; } catch { return def; } };
const gmSet    = (k, v)          => GM_setValue(k, typeof v === 'object' ? JSON.stringify(v) : v);
const gmDel    = k               => GM_deleteValue(k);
const mk       = (tag, cls, txt) => { const e = document.createElement(tag); if (cls) e.className = cls; if (txt !== undefined) e.textContent = txt; return e; };
const mkB      = (txt, cls, fn)  => Object.assign(mk('button', cls, txt), { onclick: fn, type: 'button' });
const isOn     = k               => CK.get(k) === '1';
const isOnDef  = (k, def=false)  => { const v = CK.get(k); return v !== null ? v === '1' : def; };
const guard    = (key, ...urls)  => isOnDef(key, true) && (!urls.length || urls.some(u => location.href.includes(u)));
const PM       = '/World/Popmundo.aspx';
const normUrl  = href => { try { const u = new URL(href, location.href); return u.pathname + u.search; } catch { return href; } };

// LANG
const LANG       = CK.get('ppm_lang') || 'TR';
const _D         = (tr, en, pt) => ({ TR: tr, EN: en, PT: pt });
const dateLocale = { TR: 'tr-TR', EN: 'en-GB', PT: 'pt-BR' }[LANG];

const STR = {
    menuTitle:        _D('🌐 Social',                      '🌐 Social',                        '🌐 Social'),
    menuClip:         _D('📋 Pano',                        '📋 Clipboard',                     '📋 Painel'),
    menuRadar:        _D('📍 Radar',                       '📍 Radar',                         '📍 Radar'),
    save:             _D('✔ Tamam',                        '✔ Save',                           '✔ Salvar'),
    close:            _D('Kapat',                          'Close',                            'Fechar'),
    langLabel:        _D('Dil',                            'Language',                         'Idioma'),
    backup:           _D('📤 Yedekle',                     '📤 Backup',                        '📤 Exportar'),
    restore:          _D('📥 Geri Yükle',                  '📥 Restore',                       '📥 Importar'),
    restoreErr:       _D('Geçersiz dosya.',                'Invalid file.',                    'Arquivo inválido.'),
    restoreQ:         _D('Mevcut veriler ne olsun?',       'What to do with current data?',    'O que fazer com os dados atuais?'),
    mergeLbl:         _D('Birleştir',                      'Merge',                            'Mesclar'),
    replaceLbl:       _D('Üzerine Yaz',                    'Replace',                          'Substituir'),
    cancelLbl:        _D('İptal',                          'Cancel',                           'Cancelar'),
    // features — SOSYAL
    pins:             _D('📌 Rafım — Sanatçı, mekan, şehir, forum ve favori bağlantıları klasörlü raflarda sakla',
                         '📌 Shelf — Store artist, locale, city, forum & favourite links in organized shelves',
                         '📌 Prateleira — Salve artistas, locais, cidades, fórum e favoritos em prateleiras'),
    charCard:         _D('⭐ Karakter Kartı — İsme hover\'da anlık bilgi: konum, tavır, online durumu',
                         '⭐ Character Card — Hover for instant info: location, attitude, online status',
                         '⭐ Cartão de Personagem — Hover para info: localização, atitude, status online'),
    radar:            _D('📍 Radar — 10 sekmede 200 karakter takibi',
                         '📍 Radar — Track 200 characters on 10 tabs',
                         '📍 Radar — Rastreie 200 personagens em 10 abas'),
    interactfilter:   _D('👋 İlgilenme Rehberi — Seçeneklere keyif/romantizm/nefret değerlerini göster, filtrele',
                         '👋 Interaction Guide — Show joy/romance/hatred values, filter by type',
                         '👋 Guia de Interação — Mostre valores de alegria/romance/ódio, filtre'),
    quickLinks:       _D('🔗 Hızlı Bağlantılar — Karakter ve mekan sayfalarına mesaj/git/para ver butonları ekle',
                         '🔗 Quick Links — Add message/go/send money buttons to character & locale pages',
                         '🔗 Links Rápidos — Botões de mensagem/ir/dinheiro em personagens e locais'),
    note:             _D('📝 Karakter Notu — Her profilde kalıcı kişisel not alanı',
                         '📝 Character Note — Persistent personal note field on every profile',
                         '📝 Nota de Personagem — Campo de nota pessoal em cada perfil'),
    // features — ARAÇLAR
    diary:            _D('🔍 Günlük Filtresi — Günlük girişlerini gerçek zamanlı ara ve filtrele',
                         '🔍 Diary Filter — Real-time search and filter for diary entries',
                         '🔍 Filtro de Diário — Busca em tempo real nas entradas do diário'),
    moneyFmt:         _D('💰 Para Biçimlendirici — Büyük sayıları otomatik noktalı yaz: 1.500.000',
                         '💰 Money Formatter — Automatically format large amounts: 1,500,000',
                         '💰 Formatador de Dinheiro — Formate valores grandes: 1.500.000'),
    addIcon:          _D('+ Simge Ekle',                   '+ Add Icon',                       '+ Adicionar Ícone'),
    customIconPlh:    _D('Emoji yapıştır...',              'Paste emoji...',                   'Cole emoji...'),
    customIconTitle:  _D('Özel Simgeler',                  'Custom Icons',                     'Ícones Personalizados'),
    delAllIcons:      _D('🗑 Hepsini Sil',                 '🗑 Delete All',                    '🗑 Excluir Todos'),
    delAllIconsConfirm:_D('Tüm özel simgeler silinsin mi?','Delete all custom icons?',         'Excluir todos os ícones?'),
    exportIcons:      _D('📤 Dışa Aktar',                  '📤 Export',                        '📤 Exportar'),
    emojiRef:         _D('🔗 Simge bul: getemoji.com',     '🔗 Find icons: getemoji.com',      '🔗 Encontrar ícones: getemoji.com'),
    // pins
    pinBtn:           _D('📌 Rafım',                         '📌 Shelf',                        '📌 Prateleira'),
    rafForum:         _D('Forum',              'Forum',              'Fórum'),
    rafArtists:       _D('Sanatçılar',         'Artists',            'Artistas'),
    rafLocales:       _D('Mekanlar',           'Locales',            'Locais'),
    rafCities:        _D('Şehirler',           'Cities',             'Cidades'),
    rafFav:           _D('Favoriler',          'Favorites',          'Favoritos'),
    rafChars:         _D('Karakterler',        'Characters',         'Personagens'),
    rafWork:          _D('İş & Stüdyo',        'Work & Studio',      'Trabalho & Estúdio'),
    rafGoals:         _D('Hedefler',           'Goals',              'Objetivos'),
    rafColDefault:    _D('Sıra',                              'Lane',                            'Faixa'),
    rafEmpty:         _D('Klasör boş.',                       'Folder is empty.',                'Pasta vazia.'),
    rafEditFolder:    _D('Klasörü Düzenle',                   'Edit Folder',                     'Editar Pasta'),
    rafEditCol:       _D('Sütunu Düzenle',                   'Edit Column',                     'Editar Coluna'),
    rafItemNote:      _D('Not...',                            'Note...',                         'Nota...'),
    rafDup:           _D('Bu URL zaten eklendi!',             'This URL already added!',         'Este URL já foi adicionado!'),
    colorPicker:      _D('Renk Seçici',                       'Color Picker',                    'Seletor de Cor'),
    scAskSC:          _D('Speed Calling listesine de eklensin mi?','Add to Speed Calling list?','Adicionar à lista Speed Calling?'),
    yes:              _D('Evet',                              'Yes',                             'Sim'),
    ppEmpty:          _D('Henüz pin yok.',                 'No pins yet.',                     'Nenhum pin ainda.'),
    ppDup:            _D('Bu sayfa zaten pinli!',          'This page is already pinned!',     'Esta página já está fixada!'),
    pinAdd:           _D('📌 Yeni Pin Ekle',               '📌 Add New Pin',                   '📌 Adicionar Novo Pin'),
    pinSave:          _D('📌 Pinle',                       '📌 Pin',                           '📌 Fixar'),
    pinName:          _D('Ad:',                            'Name:',                            'Nome:'),
    pinNote:          _D('Not:',                           'Note:',                            'Nota:'),
    pinIcon:          _D('Simge',                          'Icon',                             'Ícone'),
    // clipboard
    chTitle:          _D('📋 Pano Geçmişi',                '📋 Clipboard History',             '📋 Histórico de Área de Transferência'),
    chEmpty:          _D('Henüz kopyalanan ID yok.',       'No IDs copied yet.',               'Nenhum ID copiado ainda.'),
    chClear:          _D('Tümünü Temizle',                 'Clear All',                        'Limpar Tudo'),
    chCopy:           _D('Kopyala',                        'Copy',                             'Copiar'),
    // quick links (inline actions)
    iaEdit:           _D('🔗 Hızlı Bağlantıları Düzenle', '🔗 Edit Quick Links',              '🔗 Editar Links Rápidos'),
    iaDefaults:       _D('↩ Varsayılana Dön',              '↩ Reset to Defaults',              '↩ Restaurar Padrões'),
    iaAddSec:         _D('+ Yeni Link',                    '+ New Link',                       '+ Novo Link'),
    iaUrlPlh:         _D('URL yapıştır...',                'Paste URL...',                     'Cole a URL...'),
    iaUrlInfo:        _D('Kaydedilecek yol:',              'Path to save:',                    'Caminho a salvar:'),
    iaLblPlh:         _D('Buton adı...',                   'Button name...',                   'Nome do botão...'),
    iaSave:           _D('Kaydet',                         'Save',                             'Salvar'),
    // diary
    dfTitle:          _D('🔍 Günlük Filtresi',             '🔍 Diary Filter',                  '🔍 Filtro de Diário'),
    dfPlh:            _D('Ara...',                         'Search...',                        'Buscar...'),
    dfClear:          _D('Temizle',                        'Clear',                            'Limpar'),
    dfCount:          _D('eşleşme',                        'matches',                          'correspondências'),
    dfTotal:          _D('kayıt',                          'entries',                          'entradas'),
    // note
    notePlh:          _D('Bu karakter için not...',        'Note for this character...',       'Nota para este personagem...'),
    // char card popup
    cpLoading:        _D('⏳ Yükleniyor...',               '⏳ Loading...',                    '⏳ Carregando...'),
    cpTrack:          _D('☆ Radar\'a Ekle',                '☆ Add to Radar',                  '☆ Adicionar ao Radar'),
    cpTracked:        _D('⭐ Radarda',                     '⭐ On Radar',                      '⭐ No Radar'),
    cpOffline:        _D('Çevrimdışı',                     'Offline',                          'Desconectado'),
    cpAttitude:       _D('Tavır',                          'Attitude',                         'Atitude'),
    cpState:          _D('Durum',                          'State',                            'Estado'),
    cpRefresh:        _D('🔄 Manuel Güncelle',             '🔄 Update Manually',               '🔄 Atualizar Manualmente'),
    // radar
    tkTitle:          _D('📍 Takip İstasyonu',             '📍 Tracking Station',              '📍 Estação de Rastreamento'),
    tkEmpty:          _D('Radar boş.',                     'Radar is empty.',                  'Radar está vazio.'),
    tkConfirmRm:      _D('Radardan çıkarılsın mı?',        'Remove from radar?',               'Remover do radar?'),
    tkLastSeen:       _D('Son Giriş:',                     'Last Seen:',                       'Último Acesso:'),
    tkLastUpd:        _D('','',''),
    tkConfirmOk:      _D('✔ Onayla',                       '✔ Confirm',                        '✔ Confirmar'),
    tkNote:           _D('Not...','Note...','Nota...'),
    tkBgDone:         _D('📍 Radar güncellendi',           '📍 Radar updated',                 '📍 Radar atualizado'),
    tkBgStopped:      _D('⛔ Güncelleme durduruldu',       '⛔ Update stopped',                 '⛔ Atualização interrompida'),
    tkBgRunning:      _D('📍 Radar güncelleniyor...',      '📍 Updating radar...',              '📍 Atualizando radar...'),
    tkBgCancel:       _D('Durdur',                         'Stop',                              'Parar'),
    tkUpdate:         _D('🔄 Sayfayı Güncelle',            '🔄 Update Page',                   '🔄 Atualizar Página'),
    tkTabEdit:        _D('Sekmeyi Düzenle',                'Edit Tab',                         'Editar Aba'),
    tkGoTo:           _D('Yanına Git',                     'Go To',                            'Ir Para'),
    tkPageSelect:     _D('Hangi sayfaya eklensin?',        'Which page to add to?',            'Em qual página adicionar?'),
    tkPageMove:       _D('Hangi sayfaya taşınsın?',        'Which page to move to?',           'Para qual página mover?'),
    tkPageFull:       _D('Bu sayfa dolu!',                 'This page is full!',               'Esta página está cheia!'),
    tkRemove:         _D('⭐ Radardan Çıkar',              '⭐ Remove from Radar',              '⭐ Remover do Radar'),
    tkMove:           _D('↕ Sayfayı Değiştir',            '↕ Move to Page',                   '↕ Mover para Página'),
    // planner
    plDesc:           _D('Ne yapılacak?',                  'What to do?',                      'O que fazer?'),
    plDescPlh:        _D('Görev gir...',                   'Enter task...',                    'Digite a tarefa...'),
    plSchedule:       _D('PROGRAM',                        'SCHEDULE',                         'CRONOGRAMA'),
    plTabDef:         _D('Sekme',                          'Tab',                              'Aba'),
    plNoTasks:        _D('Görev yok.',                     'No tasks.',                        'Sem tarefas.'),

    // custom icons
    interactFilter:     _D('İlişki Tipi:',                    'Relationship Type:',              'Tipo de Relação:'),
    interactAll:        _D('🌐 Standart',                     '🌐 Standard',                     '🌐 Padrão'),
    interactFriend:     _D('👥 Arkadaşlık',                   '👥 Friendship',                   '👥 Amizade'),
    interactRomantic:   _D('💕 Romantizm',                    '💕 Romance',                      '💕 Romance'),
    interactHate:       _D('😡 Nefret',                       '😡 Hatred',                       '😡 Ódio'),
    interactGuide:      _D('ℹ️ Rehber',                       'ℹ️ Guide',                        'ℹ️ Guia'),
    interactGuideTitle: _D('İlgilen Seçenekleri Rehberi',     'Interaction Options Guide',       'Guia de Opções de Interação'),
    interactJealous:    _D('💘 Kıskançlık:',                  '💘 Jealousy:',                    '💘 Ciúme:'),
    interactJealousNo:  _D('Kıskanmam',                       "Won't be jealous",                'Não vou ciúmar'),
    interactJealousYes: _D('Kıskanırım',                      'Will be jealous',                 'Vou ciúmar'),
    interactWarn:       _D('⚠️ Romantizm 70+ — Kıskançlık riski yüksek!', '⚠️ Romance 70+ — High jealousy risk!', '⚠️ Romance 70+ — Alto risco de ciúme!'),
    interactColJoy:     _D('Keyif',                           'Joy',                             'Alegria'),
    interactColLove:    _D('Romantizm',                       'Romance',                         'Romance'),
    interactColHate:    _D('Nefret',                          'Hatred',                          'Ódio'),
    interactColNote:    _D('Koşul',                           'Condition',                       'Condição'),
    interactSave:       _D('Kaydet',                          'Save',                            'Salvar'),
    interactType:       _D('Tip',                             'Type',                            'Tipo'),
    interactName:       _D('Seçenek',                         'Option',                          'Opção'),
    interactDataEdit:   _D('📊 İlgilen Verilerini Düzenle',   '📊 Edit Interact Data',           '📊 Editar Dados de Interação'),
    interactDataId:     _D('Seçenek ID',                      'Option ID',                       'ID da Opção'),
    interactDataAdd:    _D('+ Ekle',                          '+ Add',                           '+ Adicionar'),
    interactDataEmpty:  _D('Özel veri yok.',                  'No custom data.',                 'Nenhum dado personalizado.'),
    interactDataDup:    _D('Bu ID zaten var!',                'This ID already exists!',         'Este ID já existe!'),
    interactDataReset:  _D('🗑 Tümünü Sil',                  '🗑 Delete All',                   '🗑 Excluir Todos'),
    interactDataResetQ: _D('Tüm özel veriler silinsin mi?',   'Delete all custom data?',         'Excluir todos os dados personalizados?'),
    // radar badges
    badgeMoved:         _D('📍 TAŞINDI',                      '📍 MOVED',                        '📍 MUDOU'),
    badgeUpdated:       _D('✎ Güncellendi',                   '✎ Updated',                       '✎ Atualizado'),
    badgeOnline:        _D('● Çevrimiçi',                     '● Online',                        '● Online'),
    // radar card warnings
    tcWarnMoved:        _D('⚠️ Önceki:',                      '⚠️ Previous:',                    '⚠️ Anterior:'),
    tcWarnUpdated:      _D('✎ Tavır/durum değişti',           '✎ Attitude/status changed',       '✎ Atitude/estado mudou'),
    // radar footer
    tcFooterHint:       _D('⠿ Sürükle & sırala · ✏️ Sekme düzenle', '⠿ Drag & sort · ✏️ Edit tab', '⠿ Arrastar & ordenar · ✏️ Editar aba'),
    tcPage:             _D('Sayfa',                           'Page',                            'Página'),
    tcTotal:            _D('Toplam',                          'Total',                           'Total'),
    // misc
    cash:               _D('Nakit',                           'Cash',                            'Dinheiro'),
    // custom icons
    ciNoIcons:          _D('Simge yok!',                      'No icons!',                       'Nenhum ícone!'),
    ciCopied:           _D('📋 Simgeler kopyalandı!',         '📋 Icons copied!',                '📋 Ícones copiados!'),
    // ia reset confirm
    iaResetConfirm:     _D('Varsayılana dön?',                'Reset to defaults?',              'Restaurar padrões?'),
    // ia exclusion list
    iaExcl:             _D('🚫 Dışlama Listesi',              '🚫 Exclusion List',               '🚫 Lista de Exclusão'),
    iaExclTitle:        _D('🚫 Hızlı Bağlantı Dışlama Listesi', '🚫 Quick Links Exclusion List', '🚫 Lista de Exclusão de Links Rápidos'),
    iaExclEmpty:        _D('Dışlama listesi boş.',             'Exclusion list is empty.',        'Lista de exclusão vazia.'),
    iaExclAddMe:        _D('➕ Kendimi Ekle',                  '➕ Add Myself',                   '➕ Adicionar-me'),
    iaExclAdd:          _D('+ Ekle',                           '+ Add',                           '+ Adicionar'),
    iaExclIdPlh:        _D('Karakter ID...',                   'Character ID...',                 'ID do Personagem...'),
    iaExclNamePlh:      _D('İsim (opsiyonel)...',              'Name (optional)...',              'Nome (opcional)...'),
    iaExclType:         _D('Tür:',                             'Type:',                           'Tipo:'),
    iaExclDel:          _D('Sil',                              'Delete',                          'Excluir'),
    iaExclNoId:         _D('ID bulunamadı — karakter sayfasında dene.', 'ID not found — try on a character page.', 'ID não encontrado — tente em uma página de personagem.'),
    // genre popup
    genrePopupBtn:      _D('🎼 Tür Popülerliğini Gör',       '🎼 View Genre Popularity',        '🎼 Ver Popularidade do Gênero'),
    genrePopupTitle:    _D('🎼 Müzik Türü Popülerliği',      '🎼 Genre Popularity',             '🎼 Popularidade do Gênero'),
    // serenade helper
    shBtn:              _D('🎵 Serenat Helper',               '🎵 Serenade Helper',              '🎵 Ajudante de Serenata'),
    shTitle:            _D('🎵 Serenat Helper',               '🎵 Serenade Helper',              '🎵 Ajudante de Serenata'),
    shNoCache:          _D('Cache yok — güncelle butonuna bas.',  'No cache — press update.',    'Sem cache — pressione atualizar.'),
    shLastUpd:          _D('Son güncelleme:',                 'Last update:',                    'Última atualização:'),
    shNextUpd:          _D('Sonraki güncelleme:',             'Next update:',                    'Próxima atualização:'),
    shUpdateBtn:        _D('🔄 Güncelle',                    '🔄 Update',                       '🔄 Atualizar'),
    shRadioLink:        _D('📻 Radyo Sıralamaları',          '📻 Radio Charts',                 '📻 Paradas de Rádio'),
    shWarn1:            _D('Son güncelleme: {date} — Çarşamba 10:00 CET\'e kadar güncellemeye gerek yok.',
                            'Last update: {date} — No update needed until Wednesday 10:00 CET.',
                            'Última atualização: {date} — Sem necessidade até quarta 10:00 CET.'),
    shWarn2:            _D('Güncelleme gerçekten gerekli mi? Tekrar bas.',
                            'Is an update really necessary? Press again.',
                            'A atualização é realmente necessária? Pressione novamente.'),
    shFetching:         _D('📻 İstasyon yükleniyor: {n}/17', '📻 Fetching station: {n}/17',     '📻 Buscando estação: {n}/17'),
    shDone:             _D('✅ Güncelleme tamamlandı!',      '✅ Update complete!',              '✅ Atualização concluída!'),
    shErr:              _D('❌ Bir hata oluştu.',            '❌ An error occurred.',            '❌ Erro ao atualizar.'),
    shNoneMatched:      _D('Bu restoranda radyo listesiyle eşleşen şarkı bulunamadı.',
                            'No songs matched the radio list at this restaurant.',
                            'Nenhuma música correspondeu à lista de rádio neste restaurante.'),
    // feature toggles
    genrePopup:         _D('🎼 Tür Popülerliği — Şarkı eklerken tür popülerliği popup\'ı',
                            '🎼 Genre Popularity — Popup when adding songs to repertoire',
                            '🎼 Popularidade do Gênero — Popup ao adicionar músicas'),
    serenadeHelper:     _D('🎵 Serenat Helper — Serenat sayfasında radyo önekleri ve güncelleme',
                            '🎵 Serenade Helper — Radio prefixes & cache on serenade page',
                            '🎵 Ajudante de Serenata — Prefixos de rádio na serenata'),
    speedcall:      _D('📞 Hızlı Arama — Arkadaş ve romantik karakterleri sırayla otomatik ara; aralık ve telefon seçenekleri ayarlanabilir',
                        '📞 Speed Calling — Auto-call friends & romantics in sequence; adjustable interval and call options',
                        '📞 Ligação Rápida — Ligue automaticamente para amigos e românticos em sequência; intervalo e opções ajustáveis'),
    // hardcoded strings
    folderTitle:        _D('Başlık',                          'Title',                           'Título'),
    recentTab:          _D('🕐 Son Eklenenler',               '🕐 Recently Added',               '🕐 Adicionados Recentemente'),
    recentEmpty:        _D('Henüz kayıtlı öğe yok.',         'No saved items yet.',             'Nenhum item salvo ainda.'),
    recentCount:        _D('Son {n} eklenen',                 'Last {n} added',                  'Últimos {n} adicionados'),
    goToFolder:         _D('Klasöre git',                     'Go to folder',                    'Ir para pasta'),
    colLabel:           _D('Sütun:',                          'Column:',                         'Coluna:'),
    addWhere:           _D('Nereye ekleyelim?',               'Where to add?',                   'Onde adicionar?'),
    deleteEntry:        _D('Bu kaydı sil?',                   'Delete this entry?',              'Excluir este registro?'),
    interactDataWip:    _D('⚠️ Bu bölüm çalışma aşamasındadır — eklenen veriler rehbere ve dropdown\'a anında yansır.',
                            '⚠️ This section is a work in progress — added data reflects immediately.',
                            '⚠️ Esta seção está em desenvolvimento — os dados adicionados refletem imediatamente.'),
    interactDataLegend: _D('✓ = Bu sayfada mevcut · Soluk = Sadece rehberde · 🟢 Arka plan = Kullanıcı ekledi',
                            '✓ = On this page · Faded = Guide only · 🟢 Background = User added',
                            '✓ = Nesta página · Esmaecido = Apenas guia · 🟢 Fundo = Adicionado pelo usuário'),
    // speed calling
    scBtn:          _D('📞 Ara',                        '📞 Call',                          '📞 Ligar'),
    scTitle:        _D('📞 Speed Calling',              '📞 Speed Calling',                 '📞 Speed Calling'),
    scInterval:     _D('Arama arası (sn):',             'Interval (sec):',                  'Intervalo (seg):'),
    scIntervalNote: _D('+ 0-2sn rastgele eklenir',      '+ 0-2s random added',              '+ 0-2s aleatório'),
    scPhoneIds:     _D('Telefon Seçenek ID\'leri',      'Phone Option IDs',                 'IDs de Opção de Telefone'),
    scPhoneNote:    _D('Virgülle ayır · Soldan denenecek · Güncel ID bilinmiyorsa boş bırak',
                        'Comma separated · Tried left to right · Leave empty if unknown',
                        'Separado por vírgula · Da esquerda para direita · Deixe vazio se desconhecido'),
    scFriendIds:    _D('Arkadaş aramaları:',            'Friendship calls:',                'Ligações de amizade:'),
    scRomIds:       _D('Romantik aramalar:',            'Romantic calls:',                  'Ligações românticas:'),
    scWho:          _D('Kimler aransın?',               'Who to call?',                     'Quem ligar?'),
    scWhoFriend:    _D('Arkadaşlar',                    'Friends',                          'Amigos'),
    scWhoRom:       _D('Romantikler',                   'Romantics',                        'Românticos'),
    scStart:        _D('▶ Başlat',                      '▶ Start',                          '▶ Iniciar'),
    scResume:       _D('▶ Kaldığı Yerden Devam',        '▶ Resume',                         '▶ Retomar'),
    scReset:        _D('↺ Sıfırla',                     '↺ Reset',                          '↺ Reiniciar'),
    scNoChars:      _D('Aramak için kayıtlı arkadaş/romantik karakter bulunamadı.',
                        'No saved friend/romantic characters found.',
                        'Nenhum personagem amigo/romântico encontrado.'),
    scBarCalling:   _D('📞 Aranıyor:',                  '📞 Calling:',                      '📞 Ligando:'),
    scBarDone:      _D('✅ Tüm aramalar tamamlandı!',   '✅ All calls done!',               '✅ Todas as ligações concluídas!'),
    scBarStop:      _D('Durdur',                        'Stop',                             'Parar'),
    scBarResume:    _D('Devam Et',                      'Resume',                           'Continuar'),
    scBarSkip:      _D('Geç',                           'Skip',                             'Pular'),
    scBarFail:      _D('⚠️ 2 ardışık hata — Script arama yapamıyor!', '⚠️ 2 consecutive errors — Script cannot call!', '⚠️ 2 erros consecutivos — Script não consegue ligar!'),
    scBarFailNote:  _D('Denemeye devam et?',            'Continue trying?',                 'Continuar tentando?'),
    scBarOf:        _D('/',                             '/',                                '/'),
    scBarNext:      _D('Sonraki:',                      'Next:',                            'Próximo:'),
    scBarNoId:      _D('ID bulunamadı — atlandı',       'No ID found — skipped',            'ID não encontrado — pulado'),
};

const _clSocial = (() => { try { const v = localStorage.getItem('ppc_lc_social'); return v ? JSON.parse(v) : null; } catch { return null; } })();
const s  = k => { if (_clSocial && _clSocial[k]) return _clSocial[k]; const v = STR[k]; if (!v) return k; return v[LANG] ?? v['TR'] ?? k; };

    // SETTINGS KEYS (cookies)
const K = {
    pins:          'tvis_feat_pins',
    charPopup:     'tvis_feat_chpopup',
    tracking:      'tvis_feat_tracking',
    ia:            'tvis_feat_ia',
    note:          'tvis_feat_cnote',
    diary:         'tvis_feat_dfl',
};

// DATA KEYS (GM_setValue)
const DK = {
    PINS:        'tvis_pins',
    CLIP:        'tvis_clip',
    IA:          'tvis_ia_',
    CACHE:       'tvis_char_cache',
    TRACK:       'tvis_track',      // legacy — kept for backup compat
    TRACK_V2:    'tvis_track_v2_',  // prefix; append page index 0-9
    TRACK_PAGE:  'tvis_track_page',
    NOTES:       'tvis_notes',
    LAST_NOTIF:  'ayu_last_notif_date',
    RADAR_TABS:  'tvis_radar_tabs',
    RAF:         'tvis_raf',
    WATCH:       'tvis_watch_state',
    WATCH_NOTIF: 'tvis_watch_notified',
    CUSTOM_ICONS:'tvis_custom_icons',
    INTERACT_TYPE: 'tvis_interact_type_',
    INTERACT_CUSTOM: 'tvis_interact_custom',
    RADIO_CACHE: 'tvis_radio_cache',
    SPEEDCALL_Q:     'tvis_sc_queue',
    SPEEDCALL_STATE: 'tvis_sc_state',
    SPEEDCALL_CFG:   'tvis_sc_cfg',
    SC_CHARS:        'tvis_sc_chars',
    IA_EXCL:         'tvis_ia_excl',
};

// RADAR TABS
const RADAR_PAGE_SIZE = 20;
const RADAR_PAGES     = 10;

// Radar V2 helpers (per-page independent arrays, no sentinels)
const getRadarPage  = idx => gmGet(DK.TRACK_V2 + idx, []) || [];
const setRadarPage  = (idx, arr) => gmSet(DK.TRACK_V2 + idx, arr);
const getTrackedPage = charId => {
    for (let i = 0; i < RADAR_PAGES; i++) {
        if (getRadarPage(i).some(e => String(e.charId) === String(charId))) return i;
    }
    return -1;
};
const isTrackedV2 = charId => getTrackedPage(charId) >= 0;
const removeFromRadar = charId => {
    for (let i = 0; i < RADAR_PAGES; i++) {
        const p = getRadarPage(i);
        const idx = p.findIndex(e => String(e.charId) === String(charId));
        if (idx >= 0) { p.splice(idx, 1); setRadarPage(i, p); return true; }
    }
    return false;
};
const addToRadar = (pageIdx, entry) => {
    if (getRadarPage(pageIdx).length >= RADAR_PAGE_SIZE) return false;
    if (isTrackedV2(entry.charId)) return false;
    const p = getRadarPage(pageIdx);
    p.push(entry);
    setRadarPage(pageIdx, p);
    return true;
};
const moveInRadar = (charId, newPageIdx) => {
    const oldPage = getTrackedPage(charId);
    if (oldPage < 0) return false;
    const oldArr = getRadarPage(oldPage);
    const idx = oldArr.findIndex(e => String(e.charId) === String(charId));
    if (idx < 0) return false;
    const [entry] = oldArr.splice(idx, 1);
    setRadarPage(oldPage, oldArr);
    const newArr = getRadarPage(newPageIdx);
    newArr.push(entry);
    setRadarPage(newPageIdx, newArr);
    return true;
};
const updateInRadar = (charId, data) => {
    const pageIdx = getTrackedPage(charId);
    if (pageIdx < 0) return;
    const p = getRadarPage(pageIdx);
    const idx = p.findIndex(e => String(e.charId) === String(charId));
    if (idx >= 0) { p[idx] = { ...p[idx], ...data, savedAt: Date.now() }; setRadarPage(pageIdx, p); }
};
const getAllTracked = () => {
    const all = [];
    for (let i = 0; i < RADAR_PAGES; i++) getRadarPage(i).forEach(e => all.push(e));
    return all;
};

const DEFAULT_RADAR_TABS = [
    { icon:'⭐', name:'YAKIN TAKİP', color:'#ffd700' },
    { icon:'👪', name:'AİLE',        color:'#28a745' },
    { icon:'🤝', name:'ARKADAŞ',     color:'#007bff' },
    { icon:'🎯', name:'TAKİP',       color:'#fd7e14' },
    { icon:'⚔️', name:'RAKİP',       color:'#dc3545' },
    { icon:'🎸', name:'BAND',        color:'#6f42c1' },
    { icon:'💼', name:'İŞ',          color:'#17a2b8' },
    { icon:'🌐', name:'DÜNYA',       color:'#20c997' },
    { icon:'📌', name:'ÖZEL',        color:'#e83e8c' },
    { icon:'📡', name:'DİĞER',       color:'#6c757d' },
];

const getRadarTabs = () => {
    const saved = gmGet(DK.RADAR_TABS, null);
    if (saved && Array.isArray(saved) && saved.length >= RADAR_PAGES) return saved;
    // Migrate if shorter
    if (saved && Array.isArray(saved) && saved.length > 0) {
        const merged = DEFAULT_RADAR_TABS.map((def, i) =>
            saved[i] ? { ...def, ...saved[i] } : def
        );
        return merged;
    }
    return DEFAULT_RADAR_TABS.map(t => ({ ...t }));
};

// CUSTOM ICONS
const getCustomIcons = () => gmGet(DK.CUSTOM_ICONS, []) || [];

// BACKUP & RESTORE
const dbExport = () => {
    const data = { v: 2, script: 'social', cookies: {}, gm: {} };
    Object.values(K).forEach(k => { const v = CK.get(k); if (v !== null) data.cookies[k] = v; });
    data.cookies['ppm_lang'] = CK.get('ppm_lang') || 'TR';
    [DK.PINS, DK.RAF, DK.CLIP, DK.CACHE, DK.NOTES, DK.RADAR_TABS, DK.CUSTOM_ICONS, DK.INTERACT_CUSTOM, DK.SPEEDCALL_CFG].forEach(k => {
        const v = gmGet(k, null); if (v !== null) data.gm[k] = v;
    });
    // Radar V2 pages
    for (let i = 0; i < RADAR_PAGES; i++) {
        const k = DK.TRACK_V2 + i; const v = gmGet(k, null); if (v !== null) data.gm[k] = v;
    }
   ['character','locale','artist','city'].forEach(sec => {
        const k = DK.IA + sec, v = gmGet(k, null); if (v !== null) data.gm[k] = v;
    });
    const d = new Date(), p = n => String(n).padStart(2,'0');
    const a = document.createElement('a');
    a.href = URL.createObjectURL(new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }));
    a.download = `ppm-social-${d.getFullYear()}-${p(d.getMonth()+1)}-${p(d.getDate())}.json`;
    a.click();
};

const dbImport = () => {
    const inp = Object.assign(document.createElement('input'), { type: 'file', accept: '.json' });
    inp.onchange = () => {
        const f = inp.files[0]; if (!f) return;
        const reader = new FileReader();
        reader.onload = ev => {
            let data; try { data = JSON.parse(ev.target.result); } catch { alert(s('restoreErr')); return; }
            mkModal(s('restore'), (cont, close) => {
                cont.appendChild(mk('div','',s('restoreQ'))).style.cssText='font-size:13px;margin-bottom:12px';
                const apply = (mode) => {
                    close();
                    if (mode === 'merge') {
                        if (data.cookies) Object.entries(data.cookies).forEach(([k,v]) => { if (CK.get(k) === null) CK.set(k,v); });
                        if (data.gm)      Object.entries(data.gm).forEach(([k,v])      => { if (gmGet(k, null) === null) gmSet(k,v); });
                    } else {
                        if (data.cookies) Object.entries(data.cookies).forEach(([k,v]) => CK.set(k,v));
                        if (data.gm)      Object.entries(data.gm).forEach(([k,v])      => gmSet(k,v));
                    }
                    location.reload();
                };
                const row = mk('div'); row.style.cssText='display:flex;gap:8px';
                row.append(mkB(s('mergeLbl'),'btn-b',()=>apply('merge')), mkB(s('replaceLbl'),'btn-r',()=>apply('replace')), mkB(s('cancelLbl'),'btn-grey',close));
                cont.appendChild(row);
            });
        };
        reader.readAsText(f);
    };
    inp.click();
};

// DRAG & DROP
const mkDraggable = (container, onReorder) => {
    let drag = null;
    container.addEventListener('dragstart', e => { drag = e.target.closest('[draggable]'); drag?.classList.add('dragging'); });
    container.addEventListener('dragend', () => {
        drag?.classList.remove('dragging');
        container.querySelectorAll('.drag-over').forEach(el => el.classList.remove('drag-over'));
        if (drag) onReorder([...container.querySelectorAll('[draggable]')].map(el => el.dataset.did));
        drag = null;
    });
    container.addEventListener('dragover', e => {
        e.preventDefault();
        if (!drag) return; // Obje tutulmadan tetiklenmeyi engelle
        const tgt = e.target.closest('[draggable]'); if (!tgt || tgt === drag) return;
        container.querySelectorAll('.drag-over').forEach(el => el.classList.remove('drag-over'));
        tgt.classList.add('drag-over');
        const r = tgt.getBoundingClientRect();
        container.insertBefore(drag, e.clientY > r.top + r.height / 2 ? tgt.nextSibling : tgt);
    });
    container.addEventListener('drop', e => e.preventDefault());
};

// ICON PICKER (includes custom icons at top)
const ICONS = [
    '🧑','👤','👑','🎭','🕵️','🧙','🦸','💃','🕺','🎤','🧛','🧟','🧝','🧜','🤴','👸','🤵','👩‍🎤','👨‍🎤','🤩','😎',
    '😀','😃','😄','😁','😆','😅','😂','🙂','🙃','😉','😊','😇','🥰','😍','🤗','🤭','🤫','🤔','🫡','🤠','🥳',
    '😺','😸','😹','😻','😼','😽','🙀','😿','😾','👋','🤚','✋','🖐️','🖖','👌','🤌','🤏','✌️','🤞','🤟','🤘','🤙',
    '👈','👉','👆','👇','☝️','👍','👎','✊','👊','🤛','🤜','👏','🙌','👐','🤲','🤝','🎵','🎶','🎸','🎹','🎺','🎻',
    '🥁','🎷','🎼','🎙️','🎚️','🎛️','🔊','📢','📣','🎧','🪕','🏠','🏡','🏢','🏤','🏥','🏦','🏨','🏩','🏪','🏫','🏬',
    '🏭','🏯','🏰','🗼','🗽','⛪','🕌','🎪','🎠','🎡','🏟️','🚪','🛋️','🛏️','🌆','🌇','🌃','🌉','🌁','🌍','🗺️','📍',
    '⛰️','🏕️','🏝️','🏖️','🚀','🚒','✈️','🍳','🌾','🏫','⚖️','🔧','🏭','🎓','🎬','✈️','🛩️','🚀','🛸','🛰️','🚁','🛶',
    '⛵','🚤','🚢','🚗','🚕','🚙','🚌','🏎️','🚓','🚑','🧭','🧳','☀️','🌤️','🌥️','🌦️','🌩️','🌨️','❄️','⛄','🌬️','💨',
    '🌪️','🌫️','🌈','☂️','☔','🌙','⚡','🌊','🌱','🌿','🍀','🌵','🌴','🌳','🌲','🪵','🌾','🌺','🌸','🌼','🌻','🌹','🥀',
    '🌷','🍁','🍂','🍃','🦁','🐯','🦊','🐺','🦝','🐻','🐗','🐴','🐐','🐑','🐄','🦒','🐘','🦛','🐁','🐀','🦄','🐲','🐉',
    '🐊','🐢','🦎','🐍','🦅','🦉','🦋','🦆','🦢','🦩','🦚','🦜','🦇','🐦','🐧','🐟','🐠','🐬','🐳','🦈','🦂','🕷️','🕸️',
    '🐞','🐜','🐝','🐾','🍺','🍻','🥂','🍷','🍸','🍹','🥃','🍾','☕','🧃','🍵','🧋','🥤','🍼','🥛','🍕','🍔','🌮','🍜','🍣',
    '🍱','🎂','🍰','🍫','🍬','🌭','🥗','🥘','🍲','🍛','🍝','🥟','🍤','🍚','🍢','🍡','🍧','🍨','🍦','🥧','🧁','🍮','🧊',
    '🍏','🍎','🍊','🍋','🍌','🍉','🍇','🍓','🍒','🍑','🍍','🥥','🥝','🍅','🥑','🥕','🌽','🌶️','🥒','🥦','🧄','🍄','🥔',
    '🍞','🥐','🥖','🧀','🥚','🍳','🥞','🧇','🥓','🍗','🍖','⭐','🌟','💫','✨','🔥','❤️','🧡','💛','💚','💙','💜','🖤',
    '🤍','🤎','💎','💰','🏆','🥇','🥈','🥉','🎖️','🏅','💼','📋','📊','📈','📉','📂','🗃️','📌','🔖','🏷️','✂️','🖊️','🖋️',
    '✒️','📝','⌚','⏰','⌛','⏳','🎉','🎊','🧸','🪅','🧨','🪓','🔔','⏱️','⏲️','🕰️','⌨️','🖱️','📀','📸','🕯️','🪔','📃',
    '📜','📄','🖇️','🧷','🧾','🗂️','🗄️','🗑️','🪣','⛓️','🛌','🚿','🛁','🚽','🧻','🅰️','🅱️','🆒','🆓','🆔','🆕','🆗','⤴️',
    '⤵️','🔙','🔚','🔛','🔜','🔝','🥎','🏉','🥏','🏒','🏑','🥍','🏏','🛹','🛷','⛸️','🥌','🎮','🕹️','🎲','🎯','🎬','🎞️',
    '📽️','🎥','📺','🎽','✅','❌','⚠️','🚨','❔','❕','❓','❗','🛑','⛔','🚫','💯','🔴','🟠','🟡','🟢','🔵','🟣','🟤','⚪',
    '⚫','🟥','🟧','🟨','🟩','🟦','🟪','🟫','⬜','⬛','🚸','🚦','🚧','🔶','🔸','🔷','🔹','🔺','🔻','🔮','💀','👁️','🗡️','🛡️',
];

const mkIconPicker = (container, initial) => {
    let sel = initial || ICONS[0];
    const pick = mk('div', 'tvis-icon-pick');
    const custom = getCustomIcons();
    const all = custom.length ? [...custom, '|', ...ICONS] : ICONS;
    all.forEach(ico => {
        if (ico === '|') {
            const sep = mk('div'); sep.style.cssText='width:100%;height:1px;background:#eee;margin:2px 0';
            pick.appendChild(sep); return;
        }
        const b = mk('button', ico === sel ? 'sel' : '', ico); b.type = 'button';
        b.onclick = () => { sel = ico; pick.querySelectorAll('button').forEach(x => x.className = ''); b.className = 'sel'; };
        pick.appendChild(b);
    });
    container.appendChild(pick);
    return () => sel;
};

// MODAL
const mkModal = (title, renderFn) => {
    document.getElementById('tvis-modal')?.remove();
    const ov  = mk('div', 'tvis-ov'); ov.id = 'tvis-modal';
    const box = mk('div', 'tvis-box');
    box.appendChild(mk('div', 'tvis-title', title));
    const cont = mk('div'); box.appendChild(cont);
    const close = () => {
        ov.remove();
        window.removeEventListener('resize', onResize);
    };
    // Close if box goes off screen on resize
    const onResize = () => {
        const r = box.getBoundingClientRect();
        if (r.left < 0 || r.right > innerWidth + 2) close();
    };
    window.addEventListener('resize', onResize);
    renderFn(cont, close);
    const cb = mkB('✕ ' + s('close'), 'btn-grey', close); cb.style.marginTop = '12px';
    box.appendChild(cb);
    ov.onclick = e => { if (e.target === ov) close(); };
    ov.appendChild(box); document.body.appendChild(ov);
    return cont;
};

// TOAST
const showToast = msg => {
    document.getElementById('tvis-toast')?.remove();
    const t = mk('div','tvis-toast',msg); t.id='tvis-toast';
    document.body.appendChild(t);
    requestAnimationFrame(()=>t.classList.add('show'));
    setTimeout(()=>{ t.classList.remove('show'); setTimeout(()=>t.remove(),400); },3500);
};

// RENK SEÇİCİ
// Returns true if hex color is dark enough to warrant white text
const isColorDark = hex => {
    const h = hex.replace('#','');
    if (h.length < 6) return false;
    const r=parseInt(h.slice(0,2),16), g=parseInt(h.slice(2,4),16), b=parseInt(h.slice(4,6),16);
    return (0.299*r + 0.587*g + 0.114*b) < 155;
};

const PRESET_COLORS = [
    // Temel mor / mavi ailesi
    '#6f42c1','#9b59b6','#8e44ad','#5a32a3','#4a235a',
    // Maviler
    '#007bff','#2980b9','#17a2b8','#0d6efd','#1abc9c',
    // Yeşiller
    '#28a745','#27ae60','#20c997','#2ecc71','#16a085',
    // Kırmızı / turuncu / sarı
    '#dc3545','#c0392b','#e74c3c','#fd7e14','#f39c12',
    '#ffc107','#ffd700','#f1c40f',
    // Pembe / mor tonları
    '#e83e8c','#e91e63','#c2185b',
    // Koyu / nötr
    '#343a40','#6c757d','#495057','#2c3e50','#34495e',
    // Açık ve özel
    '#3498db','#00b894','#e17055','#a29bfe','#74b9ff',
];

const mkColorPicker = (container, initial) => {
    let sel = initial || '#6f42c1';
    const wrap = mk('div','tvis-cpick');
    const label = mk('div','tvis-sec',s('colorPicker')); container.appendChild(label);
    PRESET_COLORS.forEach(c => {
        const sw = mk('button','tvis-cpick-sw'+(c===sel?' sel':'')); sw.type='button';
        sw.style.background=c; sw.title=c;
        sw.onclick=()=>{ sel=c; wrap.querySelectorAll('.tvis-cpick-sw').forEach(x=>x.classList.remove('sel')); sw.classList.add('sel'); };
        wrap.appendChild(sw);
    });
    const ci = mk('input'); ci.type='color'; ci.value=sel; ci.className='tvis-cpick-custom'; ci.title='Özel renk';
    ci.oninput=()=>{ sel=ci.value; wrap.querySelectorAll('.tvis-cpick-sw').forEach(x=>x.classList.remove('sel')); };
    wrap.appendChild(ci);
    container.appendChild(wrap);
    return () => sel;
};

// RAF SİSTEMİ
const RAF_FOLDER_DEFS = [
    { id:'chars',   icon:'👤', nameKey:'rafChars',   color:'#e83e8c', type:'character' },
    { id:'cities',  icon:'🏙️', nameKey:'rafCities',  color:'#fd7e14', type:'city'      },
    { id:'locales', icon:'📍', nameKey:'rafLocales', color:'#28a745', type:'locale'    },
    { id:'artists', icon:'🎸', nameKey:'rafArtists', color:'#6f42c1', type:'artist'    },
    { id:'forum',   icon:'📋', nameKey:'rafForum',   color:'#17a2b8', type:'forum'     },
    { id:'fav',     icon:'⭐', nameKey:'rafFav',     color:'#ffc107', type:'any'       },
    { id:'work',    icon:'💼', nameKey:'rafWork',    color:'#343a40', type:'any'       },
    { id:'goals',   icon:'🎯', nameKey:'rafGoals',   color:'#dc3545', type:'any'       },
];
const SHELF_COLS = 5;

const getShelf = () => {
    const saved = gmGet(DK.RAF, null);
    // Existing v1 save: patch missing folders + fields
    if (saved?.v && saved?.folders) {
        let dirty = false;
        // Add missing folders
        RAF_FOLDER_DEFS.forEach(f => {
            if (!saved.folders[f.id]) {
                saved.folders[f.id] = {
                    icon:f.icon, name:s(f.nameKey), color:f.color, colCount:5,
                    columns: Array.from({length:5},(_,i)=>({name:`${s('rafColDefault')} ${i+1}`,items:[]}))
                };
                dirty = true;
            }
            // Patch missing colCount
            if (!saved.folders[f.id].colCount) { saved.folders[f.id].colCount=5; dirty=true; }
        });
        // Add folderOrder if missing
        if (!saved.folderOrder) {
            saved.folderOrder = RAF_FOLDER_DEFS.map(f=>f.id);
            dirty = true;
        }
        // Pre-populate cities if empty
        if (!saved.folders.cities.columns.some(c=>c.items.length)) {
            saved.folders.cities.columns = buildCityCols();
            dirty = true;
        }
        if (dirty) gmSet(DK.RAF, saved);
        return saved;
    }
    // Fresh init
    const folders = {};
    RAF_FOLDER_DEFS.forEach(f => {
        const existing = saved?.[f.id];
        const cols = f.id==='cities' ? buildCityCols() :
            (existing?.columns || Array.from({length:5},(_,i)=>({name:`${s('rafColDefault')} ${i+1}`,items:[]})));
        folders[f.id] = { icon:f.icon, name:s(f.nameKey), color:f.color, colCount:5, columns:cols };
    });
    // Migrate old PIN data
    const oldPins = gmGet(DK.PINS,[]);
    if (oldPins?.length && !saved?.v) {
        oldPins.forEach(p=>{
            const fid=p.type==='artist'?'artists':p.type==='locale'?'locales':p.type==='city'?'cities':'fav';
            folders[fid]?.columns[0].items.push({id:'p'+Date.now()+Math.random(),label:p.label,url:p.url,icon:p.icon||'📌',note:p.note||'',color:null,type:p.type||'page',savedAt:Date.now()});
        });
    }
    // Migrate old forum list data
    const oldFl = gmGet('tvis_forum_list',null);
    if (oldFl?.lanes) {
        oldFl.lanes.forEach((lane,li)=>{
            if(li>=5)return;
            folders['forum'].columns[li].name=lane.name;
            lane.threads?.forEach(t=>folders['forum'].columns[li].items.push({id:'f'+Date.now()+Math.random(),label:t.name,url:t.url,icon:'📋',note:'',color:null,type:'forum',savedAt:Date.now()}));
        });
    }
    return {v:1, folderOrder:RAF_FOLDER_DEFS.map(f=>f.id), folders};
};
const saveShelf = d => gmSet(DK.RAF, d);

// Build pre-sorted city columns (alphabetical, 5 cols)
const buildCityCols = () => {
    const PM_BASE = '/World/Popmundo.aspx/City/';
    const cities = [
        {n:'Amsterdam',id:8},{n:'Ankara',id:35},{n:'Antalya',id:61},
        {n:'Bakü',id:58},{n:'Barselona',id:9},{n:'Belgrad',id:36},
        {n:'Berlin',id:7},{n:'Brüksel',id:33},{n:'Budapeşte',id:42},
        {n:'Buenos Aires',id:17},{n:'Bükreş',id:46},{n:'Cakarta',id:55},
        {n:'Dubrovnik',id:29},{n:'Glasgow',id:27},{n:'Helsinki',id:19},
        {n:'İstanbul',id:30},{n:'İzmir',id:47},{n:'Johannesburg',id:51},
        {n:'Kopenhag',id:22},{n:'Kyiv',id:56},{n:'Londra',id:5},
        {n:'Los Angeles',id:14},{n:'Madrid',id:24},{n:'Manila',id:54},
        {n:'Melbourne',id:10},{n:'Mexico City',id:32},{n:'Milano',id:52},
        {n:'Montreal',id:38},{n:'Moskova',id:18},{n:'Nashville',id:11},
        {n:'New York',id:6},{n:'Paris',id:20},{n:'Porto',id:31},
        {n:'Rio de Janeiro',id:25},{n:'Roma',id:23},{n:'Sao Paulo',id:21},
        {n:'Saraybosna',id:49},{n:'Seattle',id:50},{n:'Singapur',id:39},
        {n:'Sofya',id:53},{n:'Stokholm',id:1},{n:'Şangay',id:45},
        {n:'Şikago',id:60},{n:'Tallinn',id:34},{n:'Tokyo',id:62},
        {n:'Toronto',id:16},{n:'Tromsø',id:26},{n:'Varşova',id:48},
        {n:'Vilnius',id:28},
    ];
    // Already alphabetically sorted (TR locale). Distribute into 5 cols.
    const colCount=5, perCol=Math.ceil(cities.length/colCount);
    const colRanges=[{s:'A–B',e:9},{s:'B–C',e:19},{s:'L–N',e:29},{s:'N–S',e:39},{s:'S–V',e:49}];
    return Array.from({length:colCount},(_,ci)=>{
        const slice=cities.slice(ci*perCol,ci*perCol+perCol);
        return {
            name:colRanges[ci].s,
            items:slice.map(c=>({
                id:'city'+c.id, label:c.n, url:PM_BASE+c.id,
                icon:'🏙️', note:'', color:null, type:'city', savedAt:0
            }))
        };
    });
};

const detectPage = () => {
    const url = location.href; let m;
    if ((m = url.match(/\/Character\/(\d+)(?:[/?#]|$)/)))
        return { type:'character', id:m[1], icon:'🧑', label: document.querySelector('.charPresBox h2,.charPresBox h3')?.textContent.trim() || `Karakter #${m[1]}` };
    if ((m = url.match(/\/Locale\/(\d+)(?:[/?#]|$)/)))
        return { type:'locale', id:m[1], icon:'📍', label: document.querySelector('h1,h2')?.textContent.trim() || `Mekan #${m[1]}` };
    if ((m = url.match(/\/Artist\/(\d+)(?:[/?#]|$)/)))
        return { type:'artist', id:m[1], icon:'🎸', label: document.querySelector('h1,h2')?.textContent.trim() || `Sanatçı #${m[1]}` };
    if ((m = url.match(/\/City\/(\d+)(?:[/?#]|$)/)))
        return { type:'city', id:m[1], icon:'🏙️', label: document.querySelector('h1,h2')?.textContent.trim() || `Şehir #${m[1]}` };
    if (url.includes('/Forum/')||url.includes('/Thread/'))
        return { type:'forum', id:'', icon:'📋', label:(document.title||'').replace(/^Popmundo\s*[-–]\s*/i,'').trim()||location.pathname };
    return { type:'page', id:'', icon:'📌', label:(document.title||'').replace(/^\(\d+\)\s*/,'').replace(/^Popmundo\s*[-–]\s*/i,'').trim()||location.pathname };
};

const openMetaEdit = (anchorEl, current, onSave) => {
    document.getElementById('tvis-meta-edit-pop')?.remove();
    const pop=mk('div','tvis-tab-edit-pop'); pop.id='tvis-meta-edit-pop';
    const rect=anchorEl.getBoundingClientRect();
    pop.style.left=Math.min(rect.left,innerWidth-260)+'px'; pop.style.top=(rect.bottom+4)+'px'; pop.style.minWidth='240px';
    const nameI=mk('input'); nameI.value=current.name||''; nameI.maxLength=20;
    nameI.style.cssText='width:100%;box-sizing:border-box;padding:3px 6px;border:1px solid #ccc;border-radius:3px;font-size:11px;margin:4px 0';
    pop.appendChild(mk('div','tvis-sec',s('folderTitle'))); pop.appendChild(nameI);
    let selIcon=current.icon||'📌';
    const iconPick=mk('div','tvis-icon-pick'); iconPick.style.maxHeight='80px';
    const allIcos=[...getCustomIcons(),'|',...ICONS];
    allIcos.forEach(ico=>{
        if(ico==='|'){const sep=mk('div');sep.style.cssText='width:100%;height:1px;background:#eee;margin:2px 0';iconPick.appendChild(sep);return;}
        const b=mk('button',ico===selIcon?'sel':'',ico);b.type='button';
        b.onclick=()=>{selIcon=ico;iconPick.querySelectorAll('button').forEach(x=>x.className='');b.className='sel';};
        iconPick.appendChild(b);
    });
    pop.appendChild(mk('div','tvis-sec',s('pinIcon'))); pop.appendChild(iconPick);
    const getColor=mkColorPicker(pop,current.color||'#6f42c1');
    pop.appendChild(mkB('✔ '+s('iaSave'),'btn-sm btn-g',()=>{pop.remove();onSave({name:nameI.value.trim()||current.name,icon:selIcon,color:getColor()});}));
    const cb=mkB(s('cancelLbl'),'btn-sm btn-grey',()=>pop.remove()); cb.style.marginLeft='4px'; pop.lastChild.insertAdjacentElement('afterend',cb);
    document.body.appendChild(pop);
    setTimeout(()=>{const close=e=>{if(!pop.contains(e.target)){pop.remove();document.removeEventListener('mousedown',close);}};document.addEventListener('mousedown',close);},10);
};

const openRaf = () => mkModal(s('pinBtn'), cont => {
    cont.style.minWidth='720px';
    const shelf0=getShelf();
    const order0=shelf0.folderOrder||RAF_FOLDER_DEFS.map(f=>f.id);
    // Start on first folder that has items, else first folder
    let activeFolder=(()=>{ for(const fid of order0){ if(shelf0.folders[fid]?.columns.some(c=>c.items.length)) return fid; } return order0[0]||RAF_FOLDER_DEFS[0].id; })();
    let editItemKey=null;
    let drag=null;
    let tabDrag=null;
    let rafSearchQ='';

    // Shared card renderer
    const renderRafCard=(item,itemKey,onEdit,fid,ci,ii,grid,getDrag,setDrag)=>{
        const card=mk('div','tvis-raf-card');
        if(item.color){
            card.classList.add('colored');
            card.style.background=item.color;
            card.style.borderColor=item.color;
            card.style.color=isColorDark(item.color)?'#fff':'#222';
        }
        const cbar=mk('div','tvis-raf-card-color');
        if(item.color) cbar.style.background=isColorDark(item.color)?'rgba(255,255,255,.25)':'rgba(0,0,0,.15)';
        const ico=mk('span','tvis-raf-card-ico',item.icon||'📌');
        const lbl=mk('a','tvis-raf-card-lbl',item.label); lbl.href=item.url; lbl.target='_blank'; lbl.title=item.label+(item.note?'\n'+item.note:'');
        card.append(mk('span','frl-drag','⠿'),cbar,ico,lbl);
        if(item.note){ const noteEl=mk('span','tvis-raf-card-note',item.note); if(item.color) noteEl.style.color=isColorDark(item.color)?'rgba(255,255,255,.75)':'rgba(0,0,0,.55)'; card.appendChild(noteEl); }
        if(itemKey!=null && onEdit){
            const eB=mkB('✏','btn-sm btn-grey',()=>onEdit(itemKey));
            const dB=mkB('✕','btn-sm btn-r',()=>{const sh=getShelf();sh.folders[fid].columns[ci].items.splice(ii,1);saveShelf(sh);render();});
            eB.style.cssText=dB.style.cssText='font-size:9px;padding:1px 3px;flex-shrink:0';
            card.append(eB,dB);
            card.draggable=true; card.dataset.itemkey=`${fid}-${ci}`;
            card.addEventListener('dragstart',()=>setDrag({type:'card',fid,ci,ii}));
            card.addEventListener('dragover',e=>{
                e.preventDefault();
                if(getDrag()?.type==='card'){ grid?.querySelectorAll('.drag-over').forEach(x=>x.classList.remove('drag-over')); card.classList.add('drag-over'); }
            });
            card.addEventListener('drop',e=>{
                e.preventDefault();e.stopPropagation();
                const d=getDrag(); if(!d||d.type!=='card') return;
                const sh=getShelf();
                const [itm]=sh.folders[d.fid].columns[d.ci].items.splice(d.ii,1);
                const to=e.clientY>card.getBoundingClientRect().top+card.offsetHeight/2?ii+1:ii;
                sh.folders[fid].columns[ci].items.splice(to,0,itm);
                setDrag(null);saveShelf(sh);render();
            });
        }
        return card;
    };

    // Inline item editor
    const buildItemEditor=(item,ci,ii)=>{
        const ed=mk('div','tvis-raf-edit');
        const r1=mk('div');r1.style.cssText='display:flex;gap:4px;flex-wrap:wrap;margin-bottom:4px';
        const nI=mk('input');nI.value=item.label;nI.style.cssText='flex:1;min-width:100px;padding:3px 5px;border:1px solid #ccc;border-radius:3px;font-size:11px';
        const ntI=mk('input');ntI.value=item.note||'';ntI.placeholder=s('rafItemNote');ntI.style.cssText='flex:1;min-width:80px;padding:3px 5px;border:1px solid #ccc;border-radius:3px;font-size:11px';
        r1.append(nI,ntI); ed.appendChild(r1);
        ed.appendChild(mk('div','tvis-sec',s('pinIcon')));
        let edIcon=item.icon||'📌';
        const edIp=mk('div','tvis-icon-pick');edIp.style.maxHeight='60px';
        [...getCustomIcons(),'|',...ICONS].forEach(ico2=>{
            if(ico2==='|'){const sep=mk('div');sep.style.cssText='width:100%;height:1px;background:#eee;margin:2px 0';edIp.appendChild(sep);return;}
            const b=mk('button',ico2===edIcon?'sel':'',ico2);b.type='button';
            b.onclick=()=>{edIcon=ico2;edIp.querySelectorAll('button').forEach(x=>x.className='');b.className='sel';};
            edIp.appendChild(b);
        });
        ed.appendChild(edIp);
        const getEdColor=mkColorPicker(ed,item.color||null);
        const sv=mkB('✔ '+s('iaSave'),'btn-sm btn-g',()=>{
            const sh=getShelf();const it=sh.folders[activeFolder].columns[ci].items[ii];
            it.label=nI.value.trim()||it.label;it.note=ntI.value.trim();it.icon=edIcon;it.color=getEdColor()||null;
            saveShelf(sh);editItemKey=null;render();
        });
        sv.style.marginTop='4px';ed.appendChild(sv);
        return ed;
    };

    const render=()=>{
        cont.innerHTML='';
        const shelf=getShelf();
        const order=shelf.folderOrder||RAF_FOLDER_DEFS.map(f=>f.id);

        // SEARCH BAR
        const searchRow=mk('div'); searchRow.style.cssText='display:flex;gap:6px;align-items:center;margin-bottom:10px';
        const searchI=mk('input'); searchI.id='tvis-raf-search';
        searchI.placeholder='🔍 Raflarda ara... (isim, not)';
        searchI.style.cssText='flex:1;padding:4px 8px;border:1px solid #c9b8f0;border-radius:4px;font-size:12px';
        searchI.value=rafSearchQ;
        const clrBtn=mkB('✕','btn-sm btn-grey',()=>{ rafSearchQ=''; render(); });
        clrBtn.style.display=rafSearchQ?'':'none';
        searchI.addEventListener('input',()=>{ rafSearchQ=searchI.value; clrBtn.style.display=rafSearchQ?'':'none'; renderContent(); });
        searchRow.append(searchI,clrBtn);
        cont.appendChild(searchRow);

        // FOLDER TABS (draggable)
        const tabRow=mk('div','tvis-raf-tabs');
        if(rafSearchQ) tabRow.style.opacity='0.5';

        order.forEach(fid=>{
            const def=RAF_FOLDER_DEFS.find(d=>d.id===fid); if(!def) return;
            const fd=shelf.folders[fid]; if(!fd) return;
            const totalItems=fd.columns.flatMap(c=>c.items).length;
            const isActive=activeFolder===fid&&!rafSearchQ;
            const tw=mk('span'); tw.style.cssText='display:inline-flex;align-items:center;gap:1px';
            tw.draggable=true; tw.dataset.fid=fid;
            tw.addEventListener('dragstart',e=>{ tabDrag=fid; tw.style.opacity='.4'; e.dataTransfer.effectAllowed='move'; });
            tw.addEventListener('dragend',()=>{ tw.style.opacity=''; tabRow.querySelectorAll('.drag-over-tab').forEach(x=>x.classList.remove('drag-over-tab')); });
            tw.addEventListener('dragover',e=>{ if(tabDrag&&tabDrag!==fid){e.preventDefault();tw.querySelector('button')?.classList.add('drag-over-tab');} });
            tw.addEventListener('dragleave',()=>tw.querySelector('button')?.classList.remove('drag-over-tab'));
            tw.addEventListener('drop',e=>{
                e.preventDefault();e.stopPropagation();
                if(!tabDrag||tabDrag===fid) return;
                const sh=getShelf(); const ord=sh.folderOrder||RAF_FOLDER_DEFS.map(f=>f.id);
                const from=ord.indexOf(tabDrag),to=ord.indexOf(fid);
                if(from>=0&&to>=0){ord.splice(from,1);ord.splice(to,0,tabDrag);sh.folderOrder=ord;saveShelf(sh);}
                tabDrag=null;render();
            });
            const tab=mk('button','tvis-raf-tab'+(isActive?' active':''), `${fd.icon} ${fd.name}`);
            tab.style.borderColor=fd.color;
            if(isActive){tab.style.background=fd.color;tab.style.color='#fff';}
            if(totalItems>0){ const badge=mk('span','tvis-raf-tab-badge',String(totalItems)); tab.appendChild(badge); }
            // Trailing icon
            const trailIco=mk('span',''); trailIco.textContent=' '+fd.icon; trailIco.style.cssText='font-size:11px;opacity:.7';
            tab.appendChild(trailIco);
            tab.onclick=()=>{rafSearchQ='';activeFolder=fid;editItemKey=null;render();};
            const eb=mk('button','tvis-raf-tab-edit','✏️');
            eb.onclick=e=>{e.stopPropagation();openMetaEdit(eb,fd,(meta)=>{const sh=getShelf();sh.folders[fid]={...sh.folders[fid],...meta};saveShelf(sh);render();});};
            tw.append(tab,eb); tabRow.appendChild(tw);
        });

        // Son Eklenenler pseudo-tab
        const recTab=mk('button','tvis-raf-recent-tab'+(activeFolder==='__recent__'&&!rafSearchQ?' active':''),s('recentTab'));
        recTab.onclick=()=>{rafSearchQ='';activeFolder='__recent__';editItemKey=null;render();};
        tabRow.appendChild(recTab);
        cont.appendChild(tabRow);

        const contentArea=mk('div'); cont.appendChild(contentArea);

        const renderContent=()=>{
            contentArea.innerHTML='';
            const shelf2=getShelf();
            const order2=shelf2.folderOrder||RAF_FOLDER_DEFS.map(f=>f.id);

            if(rafSearchQ.trim()){
                // SEARCH
                const q=rafSearchQ.trim().toLowerCase();
                const results=[];
                order2.forEach(fid=>{ const fd2=shelf2.folders[fid]; if(!fd2) return;
                    fd2.columns.forEach((col,ci)=>{ col.items.forEach((item,ii)=>{
                        if((item.label||'').toLowerCase().includes(q)||(item.note||'').toLowerCase().includes(q))
                            results.push({item,fid,ficon:fd2.icon,fname:fd2.name,ci,ii,colname:col.name});
                    });});
                });
                if(!results.length){
                    const em=mk('div',''); em.style.cssText='color:#999;font-size:12px;padding:16px 0;text-align:center';
                    em.textContent=`"${rafSearchQ}" için sonuç bulunamadı.`; contentArea.appendChild(em);
                } else {
                    const info=mk('div',''); info.style.cssText='font-size:10px;color:#aaa;margin-bottom:8px';
                    info.textContent=`${results.length} sonuç`; contentArea.appendChild(info);
                    const list=mk('div'); list.style.cssText='display:flex;flex-direction:column;gap:3px';
                    results.forEach(({item,fid,ficon,fname,ci,colname})=>{
                        const row=renderRafCard(item,null,null,fid,ci,0,null,()=>null,()=>{});
                        row.style.cursor='default';
                        const bc=mk('span',''); bc.style.cssText='font-size:9px;white-space:nowrap;flex-shrink:0';
                        bc.style.color=item.color?(isColorDark(item.color)?'rgba(255,255,255,.6)':'rgba(0,0,0,.45)'):'#aaa';
                        bc.textContent=`${ficon} ${fname} › ${colname}`;
                        const goBtn=mkB('→','btn-sm btn-grey',()=>{rafSearchQ='';activeFolder=fid;render();});
                        goBtn.title='Bu klasöre git';goBtn.style.flexShrink='0';
                        row.append(bc,goBtn); list.appendChild(row);
                    });
                    contentArea.appendChild(list);
                }
                setTimeout(()=>{ const si=document.getElementById('tvis-raf-search'); if(si){si.focus();si.setSelectionRange(si.value.length,si.value.length);} },10);

            } else if(activeFolder==='__recent__'){
                // SON EKLENENLER
                const allItems=[];
                order2.forEach(fid=>{ const fd2=shelf2.folders[fid]; if(!fd2) return;
                    fd2.columns.forEach((col,ci)=>{ col.items.forEach((item,ii)=>{
                        const ts=item.savedAt||parseInt(String(item.id).replace(/\D/g,'').slice(0,13))||0;
                        if(ts>0) allItems.push({item,fid,ficon:fd2.icon,fname:fd2.name,ci,ii,colname:col.name,ts});
                    });});
                });
                allItems.sort((a,b)=>b.ts-a.ts);
                const recent=allItems.slice(0,20);
                if(!recent.length){
                    const em=mk('div',''); em.style.cssText='color:#999;font-size:12px;padding:16px 0;text-align:center';
                    em.textContent=s('recentEmpty'); contentArea.appendChild(em);
                } else {
                    const info=mk('div',''); info.style.cssText='font-size:10px;color:#aaa;margin-bottom:8px';
                    info.textContent=s('recentCount').replace('{n}',recent.length); contentArea.appendChild(info);
                    const list=mk('div'); list.style.cssText='display:flex;flex-direction:column;gap:3px';
                    recent.forEach(({item,fid,ficon,fname,ci,colname,ts})=>{
                        const row=renderRafCard(item,null,null,fid,ci,0,null,()=>null,()=>{});
                        row.style.cursor='default';
                        const dateStr=ts>0?new Date(ts).toLocaleDateString('tr-TR',{day:'2-digit',month:'2-digit',year:'2-digit'}):'';
                        const meta=mk('span',''); meta.style.cssText='font-size:9px;color:#aaa;white-space:nowrap;flex-shrink:0';
                        if(item.color) meta.style.color=isColorDark(item.color)?'rgba(255,255,255,.6)':'rgba(0,0,0,.45)';
                        meta.textContent=`${ficon} ${fname} › ${colname}${dateStr?' · '+dateStr:''}`;
                        const goBtn=mkB('→','btn-sm btn-grey',()=>{activeFolder=fid;render();});
                        goBtn.title=s('goToFolder');goBtn.style.flexShrink='0';
                        row.append(meta,goBtn); list.appendChild(row);
                    });
                    contentArea.appendChild(list);
                }

            } else {
                // NORMAL GRID
                const fd=shelf2.folders[activeFolder]; if(!fd) return;
                const colCount=fd.colCount||5;

                // Column count selector
                const ccRow=mk('div','tvis-raf-colcount');
                ccRow.appendChild(mk('span','',s('colLabel')));
                [3,4,5,6].forEach(n=>{
                    const b=mkB(String(n),'',()=>{
                        if(n===colCount) return;
                        const sh=getShelf(); const fdr=sh.folders[activeFolder];
                        if(n>colCount){ for(let i=colCount;i<n;i++) fdr.columns.push({name:`${s('rafColDefault')} ${i+1}`,items:[]}); }
                        else { const overflow=fdr.columns.splice(n).flatMap(c=>c.items); fdr.columns[n-1].items.push(...overflow); }
                        fdr.colCount=n; saveShelf(sh); render();
                    });
                    b.style.cssText=n===colCount
                        ? 'padding:1px 7px;border:1px solid #6f42c1;border-radius:3px;background:#6f42c1;font-size:10px;cursor:pointer;color:#fff'
                        : 'padding:1px 7px;border:1px solid #ccc;border-radius:3px;background:#fff;font-size:10px;cursor:pointer;color:#666';
                    ccRow.appendChild(b);
                });
                contentArea.appendChild(ccRow);

                const grid=mk('div','tvis-raf-grid');
                grid.style.gridTemplateColumns=`repeat(${colCount},1fr)`;
                contentArea.appendChild(grid);

                fd.columns.forEach((col,ci)=>{
                    const colEl=mk('div','tvis-raf-col');
                    const head=mk('div','tvis-raf-col-head');
                    const nameI=mk('input','tvis-raf-col-name'); nameI.value=col.name; nameI.maxLength=20;
                    nameI.onchange=()=>{const sh=getShelf();sh.folders[activeFolder].columns[ci].name=nameI.value.trim()||col.name;saveShelf(sh);};
                    const cnt=mk('span',''); cnt.style.cssText='font-size:9px;color:#aaa;flex-shrink:0;padding:0 2px';
                    cnt.textContent=col.items.length?`(${col.items.length})`:'';
                    head.append(nameI,cnt); colEl.appendChild(head);
                    col.items.forEach((item,ii)=>{
                        const itemKey=`${ci}-${ii}`;
                        const card=renderRafCard(item,itemKey,(key)=>{editItemKey=editItemKey===key?null:key;render();},
                            activeFolder,ci,ii,grid,()=>drag,(d)=>{drag=d;});
                        colEl.appendChild(card);
                        if(editItemKey===itemKey) colEl.appendChild(buildItemEditor(item,ci,ii));
                    });
                    colEl.addEventListener('dragover',e=>e.preventDefault());
                    colEl.addEventListener('dragenter',()=>colEl.classList.add('drag-target'));
                    colEl.addEventListener('dragleave',e=>{if(!colEl.contains(e.relatedTarget))colEl.classList.remove('drag-target');});
                    colEl.addEventListener('drop',e=>{
                        e.preventDefault();colEl.classList.remove('drag-target');
                        if(!drag||drag.type!=='card') return;
                        const sh=getShelf();
                        const [itm]=sh.folders[drag.fid].columns[drag.ci].items.splice(drag.ii,1);
                        sh.folders[activeFolder].columns[ci].items.push(itm);
                        drag=null;saveShelf(sh);render();
                    });
                    if(!col.items.length){const em=mk('div','',s('rafEmpty'));em.style.cssText='color:#bbb;font-size:10px;text-align:center;padding:8px 0';colEl.appendChild(em);}
                    grid.appendChild(colEl);
                });
            }
        };
        renderContent();
    };
    render();
})

// "Serbest" klasörler — sayfa tipine göre otomatik yönlendirilemeyen klasörler
const FREE_FOLDER_IDS = ['fav','work','goals'];

const quickRaf = () => {
    const pg = detectPage();
    // Bilinen tipe sahip sayfalar → direkt klasör
    const autoFid = pg.type==='artist'?'artists' : pg.type==='locale'?'locales' : pg.type==='city'?'cities'
                  : pg.type==='forum'?'forum' : pg.type==='character'?'chars' : null;

    const shelf = getShelf();

    // Zaten eklenmiş mi kontrol et (tüm klasörlerde)
    const allItems = Object.values(shelf.folders).flatMap(fd=>fd.columns.flatMap(c=>c.items));
    if(allItems.find(it=>normUrl(it.url)===normUrl(location.href))){ showToast(s('rafDup')); return; }

    const openForm = (targetFid) => {
        const fd = shelf.folders[targetFid];
        mkModal(s('pinAdd'),(cont,close)=>{
            const fld=(lbl,val,plh)=>{
                const l=mk('div','',lbl); l.style.cssText='font-size:11px;color:#666;margin-bottom:2px';
                const i=mk('input'); i.style.cssText='width:100%;box-sizing:border-box;padding:4px 6px;border:1px solid #ccc;border-radius:3px;font-size:12px;margin-bottom:6px';
                if(val!==undefined) i.value=val; if(plh) i.placeholder=plh;
                cont.append(l,i); return i;
            };
            const nI=fld(s('pinName'),pg.label);
            const ntI=fld(s('pinNote'),'','...');
            cont.appendChild(mk('div','tvis-sec',s('pinIcon')));
            let selIcon=pg.icon;
            const ip=mk('div','tvis-icon-pick'); ip.style.maxHeight='80px';
            [...getCustomIcons(),'|',...ICONS].forEach(ico=>{
                if(ico==='|'){const sep=mk('div');sep.style.cssText='width:100%;height:1px;background:#eee;margin:2px 0';ip.appendChild(sep);return;}
                const b=mk('button',ico===selIcon?'sel':'',ico); b.type='button';
                b.onclick=()=>{selIcon=ico;ip.querySelectorAll('button').forEach(x=>x.className='');b.className='sel';};
                ip.appendChild(b);
            });
            cont.appendChild(ip);
            const getColor=mkColorPicker(cont,null);
            cont.appendChild(mk('div','tvis-sec',s('rafColDefault')));
            const colSel=mk('select'); colSel.style.cssText='width:100%;padding:3px 5px;border:1px solid #ccc;border-radius:3px;font-size:11px;margin-bottom:6px';
            fd.columns.forEach((c,i)=>{const o=mk('option','',`${c.name} (${c.items.length})`);o.value=i;colSel.appendChild(o);});
            cont.appendChild(colSel);
            cont.appendChild(mkB(s('pinSave'),'btn-v',()=>{
                const sh=getShelf();
                sh.folders[targetFid].columns[parseInt(colSel.value)].items.push({
                    id:'i'+Date.now(), label:nI.value.trim()||pg.label, url:normUrl(location.href),
                    icon:selIcon, note:ntI.value.trim(), color:getColor()||null, type:pg.type, savedAt:Date.now()
                });
                saveShelf(sh); close(); showToast('📌 '+s('pinSave'));
            }));
        });
    };

    if(autoFid){
        // Bilinen tip → direkt forma git
        openForm(autoFid);
    } else {
        // Serbest tip → Hangi klasöre? seçici
        const freeFolders = FREE_FOLDER_IDS.map(fid=>({ fid, fd:shelf.folders[fid] })).filter(x=>x.fd);
        const order = shelf.folderOrder || RAF_FOLDER_DEFS.map(f=>f.id);
        freeFolders.sort((a,b)=>order.indexOf(a.fid)-order.indexOf(b.fid));

        // Mini popup — add button'un yakınında göster
        const addBtn = document.querySelector('#tvis-bar .tvis-bar a') || document.body;
        const pop = mk('div');
        pop.style.cssText='position:fixed;z-index:999999;background:#fff;border:1px solid #c9b8f0;border-radius:8px;padding:8px;box-shadow:0 4px 16px rgba(0,0,0,.2);min-width:180px';
        pop.style.top='36px'; pop.style.right='4px';

        const title=mk('div','tvis-sec',s('addWhere')); title.style.marginBottom='6px';
        pop.appendChild(title);

        freeFolders.forEach(({fid,fd})=>{
            const btn=mk('button'); btn.type='button';
            btn.style.cssText='display:flex;align-items:center;gap:8px;width:100%;padding:6px 10px;margin-bottom:4px;border:2px solid;border-radius:5px;cursor:pointer;font-size:12px;font-weight:600;background:#fff;text-align:left';
            btn.style.borderColor=fd.color;
            btn.style.color=fd.color;
            const totalItems=fd.columns.flatMap(c=>c.items).length;
            btn.innerHTML=`<span style="font-size:16px">${fd.icon}</span><span>${fd.name}</span><span style="margin-left:auto;font-size:10px;opacity:.6">${totalItems}</span>`;
            btn.onmouseover=()=>{ btn.style.background=fd.color; btn.style.color=isColorDark(fd.color)?'#fff':'#222'; };
            btn.onmouseout=()=>{ btn.style.background='#fff'; btn.style.color=fd.color; };
            btn.onclick=()=>{ pop.remove(); openForm(fid); };
            pop.appendChild(btn);
        });

        document.body.appendChild(pop);
        setTimeout(()=>{
            const close=e=>{ if(!pop.contains(e.target)){pop.remove();document.removeEventListener('mousedown',close);} };
            document.addEventListener('mousedown',close);
        },50);
    }
};

const addForumToRaf=(id,name,url,targetCol=-1)=>{
    const sh=getShelf(); const fd=sh.folders['forum'];
    const nUrl=normUrl(url);
    if(fd.columns.flatMap(c=>c.items).find(it=>normUrl(it.url)===nUrl))return false;
    let tc = targetCol >= 0 && targetCol < fd.columns.length ? targetCol
           : fd.columns.findIndex(c=>c.items.length<20);
    if(tc<0)tc=SHELF_COLS-1;
    fd.columns[tc].items.push({id:'f'+Date.now(),label:name,url:nUrl,icon:'📋',note:'',color:null,type:'forum',savedAt:Date.now()});
    saveShelf(sh); return true;
};
const isForumInRaf=url=>getShelf().folders['forum'].columns.flatMap(c=>c.items).some(it=>normUrl(it.url)===normUrl(url));

// CLIPBOARD HISTORY
const addClip = (id, name, type) => {
    const log = gmGet(DK.CLIP, []);
    log.unshift({ id, name, type, ts: Date.now() });
    if (log.length > 25) log.length = 25;
    gmSet(DK.CLIP, log);
};

const openClip = () => mkModal(s('chTitle'), cont => {
    const render = () => {
        cont.innerHTML = '';
        const log = gmGet(DK.CLIP, []);
        if (!log.length) { const d=mk('div','',s('chEmpty')); d.style.cssText='color:#999;font-size:12px'; cont.appendChild(d); return; }
        const clr = mkB(s('chClear'), 'btn-sm btn-r', () => { gmSet(DK.CLIP,[]); render(); });
        clr.style.marginBottom = '8px'; cont.appendChild(clr);
        log.forEach(e => {
            const row = mk('div'); row.style.cssText='display:flex;align-items:center;gap:6px;margin-bottom:4px;padding:4px 6px;background:#f8f9fa;border-radius:3px';
            const ico = e.type==='character'?'🧑':e.type==='locale'?'📍':'🎸';
            const lbl = mk('span','',`${ico} ${e.name||'?'} — ID: ${e.id}`); lbl.style.cssText='flex:1;font-size:12px';
            const ts  = mk('span','',new Date(e.ts).toLocaleTimeString(dateLocale,{hour:'2-digit',minute:'2-digit'})); ts.style.cssText='font-size:10px;color:#999';
            const cp  = mkB(s('chCopy'), 'btn-sm btn-b', () => { navigator.clipboard?.writeText(e.id); });
            row.append(lbl,ts,cp); cont.appendChild(row);
        });
    };
    render();
});

// CHARACTER CACHE
const CACHE_TTL = 86400000, CACHE_MAX = 200;
const CC = {
    _s: () => gmGet(DK.CACHE, {}),
    get(id) {
        const s = this._s(), e = s[String(id)]; if (!e) return null;
        if (Date.now() - e.t > CACHE_TTL) { delete s[id]; gmSet(DK.CACHE,s); return null; }
        return e;
    },
    set(id, data) {
        const s = this._s();
        const tracked = new Set(getAllTracked().map(t => String(t.charId)));
        const keys = Object.keys(s);
        if (keys.length >= CACHE_MAX) {
            const evict = keys.filter(k=>!tracked.has(k)).sort((a,b)=>s[a].t-s[b].t)[0] || keys.sort((a,b)=>s[a].t-s[b].t)[0];
            if (evict) delete s[evict];
        }
        s[String(id)] = { ...data, t: Date.now() };
        gmSet(DK.CACHE,s);
    },
    del(id) { const s=this._s(); delete s[String(id)]; gmSet(DK.CACHE,s); }
};

let lastFetch = 0;
const FETCH_GAP = 3000;
const waitGap = () => { const g = FETCH_GAP-(Date.now()-lastFetch); return g>0?new Promise(r=>setTimeout(r,g)):Promise.resolve(); };

// GLOBAL STOP FUNCTIONALITY
const showGlobalStop = (onStop) => {
    if (document.getElementById('tvis-global-stop')) return;
    const bar = mk('div'); bar.id = 'tvis-global-stop';
    bar.style.cssText = 'position:fixed;top:0;left:0;width:100%;background:#dc3545;color:#fff;text-align:center;padding:12px;z-index:999999;font-weight:bold;cursor:pointer;box-shadow:0 4px 12px rgba(0,0,0,0.3);font-size:14px;';
    bar.textContent = '⏹ ' + s('tkBgCancel');
    bar.onclick = () => { bar.remove(); if(onStop) onStop(); };
    document.body.appendChild(bar);
};

// NOTIFICATION SYSTEM
const showNotification = (message, type = 'info') => {
    const notification = mk('div', 'tvis-toast');
    notification.textContent = message;
    
    // Set color based on type
    const colors = {
        success: '#218838',
        error: '#dc3545',
        warning: '#ffc107',
        info: '#17a2b8'
    };
    notification.style.background = colors[type] || colors.info;
    
    document.body.appendChild(notification);
    
    // Show animation
    requestAnimationFrame(() => notification.classList.add('show'));
    
    // Auto-hide after 4 seconds
    setTimeout(() => {
        notification.classList.remove('show');
        setTimeout(() => notification.remove(), 300);
    }, 4000);
};

// Enhanced hideProgress with notifications
const hideProgressWithNotification = (taskType, stopped, task) => {
    const progressId = `tvis-${taskType}-prog`;
    const el = document.getElementById(progressId);
    if (!el) return;
    
    el.classList.remove('done','stopped');
    el.classList.add(stopped ? 'stopped' : 'done');
    
    let msg, notificationMsg, notificationType;
    
    if (stopped) {
        msg = `${s('tkBgStopped')} ${task.current}/${task.total}`;
        notificationMsg = `⛔ ${taskType} işlemi durduruldu: ${task.current}/${task.total}`;
        notificationType = 'warning';
    } else {
        switch(taskType) {
            case 'radar':
                msg = `${s('tkBgDone')} ${task.total}/${task.total}`;
                notificationMsg = `✅ Radar güncellendi: ${task.total} karakter`;
                notificationType = 'success';
                break;
            case 'radio':
                msg = '📻 Radyo listesi güncellendi!';
                notificationMsg = '📻 Radyo sıralamaları güncellendi';
                notificationType = 'success';
                break;
            case 'speedcall':
                msg = '✅ Tüm aramalar tamamlandı!';
                notificationMsg = `📞 Speed Calling tamamlandı: ${task.total} arama`;
                notificationType = 'success';
                break;
            default:
                msg = `✅ ${taskType} tamamlandı`;
                notificationMsg = `✅ ${taskType} işlemi tamamlandı`;
                notificationType = 'success';
        }
    }
    
    document.getElementById(`${progressId}-txt`).textContent = msg;
    const stopBtn = el.querySelector('.tvis-rprog-cancel');
    if (stopBtn) stopBtn.remove();
    
    // Show notification
    showNotification(notificationMsg, notificationType);
    
    setTimeout(() => el.remove(), 3500);
};

// BACKGROUND TASK MANAGER
const BGTASKS = {
    radar: { running: false, cancelled: false, current: 0, total: 0 },
    radio: { running: false, cancelled: false, current: 0, total: 0 },
    speedcall: { running: false, cancelled: false, current: 0, total: 0 }
};

// Page-independent task persistence
const BG_TASK_KEY = 'tvis_bg_tasks';
const saveBgTasks = () => localStorage.setItem(BG_TASK_KEY, JSON.stringify(BGTASKS));
const loadBgTasks = () => {
    try {
        const saved = localStorage.getItem(BG_TASK_KEY);
        if (saved) {
            const parsed = JSON.parse(saved);
            Object.assign(BGTASKS, parsed);
        }
    } catch (e) {
        console.warn('Failed to load background tasks:', e);
    }
};

// Initialize on page load
loadBgTasks();

// Auto-save when tasks change
const saveTaskState = (taskType) => {
    setTimeout(() => saveBgTasks(), 100);
};

const startBackgroundTask = (taskType, total, processFn) => {
    if (BGTASKS[taskType].running) return;
    
    BGTASKS[taskType] = { running: true, cancelled: false, current: 0, total };
    saveTaskState(taskType);
    
    const showProgress = () => {
        const task = BGTASKS[taskType];
        const progressId = `tvis-${taskType}-prog`;
        let el = document.getElementById(progressId);
        
        if (!el) {
            el = mk('div', 'tvis-rprog'); el.id = progressId;
            const txt = mk('span', 'tvis-rprog-txt'); txt.id = `${progressId}-txt`;
            const stop = mk('button', 'tvis-rprog-cancel', s('tkBgCancel'));
            stop.onclick = () => { 
                BGTASKS[taskType].cancelled = true; 
                saveTaskState(taskType);
                stop.disabled = true; 
                stop.textContent = '...'; 
            };
            el.append(txt, stop);
            document.body.appendChild(el);
        }
        
        document.getElementById(`${progressId}-txt`).textContent = 
            taskType === 'radar' ? `${s('tkBgRunning')} ${task.current}/${task.total}` :
            taskType === 'radio' ? `📻 ${s('shFetching').replace('{n}', task.current).replace('/17', `/${task.total}`)}` :
            `📞 ${s('scBarCalling')} ${task.current}/${task.total}`;
    };
    
    const hideProgress = (stopped) => {
        const task = BGTASKS[taskType];
        const progressId = `tvis-${taskType}-prog`;
        const el = document.getElementById(progressId);
        if (!el) return;
        
        el.classList.remove('done','stopped');
        el.classList.add(stopped ? 'stopped' : 'done');
        
        const msg = stopped ? 
            `${s('tkBgStopped')} ${task.current}/${task.total}` : 
            taskType === 'radar' ? `${s('tkBgDone')} ${task.total}/${task.total}` :
            taskType === 'radio' ? '📻 Radyo listesi güncellendi!' :
            '✅ Tüm aramalar tamamlandı!';
            
        document.getElementById(`${progressId}-txt`).textContent = msg;
        const stopBtn = el.querySelector('.tvis-rprog-cancel');
        if (stopBtn) stopBtn.remove();
        setTimeout(() => el.remove(), 3500);
    };
    
    // Global stop integration
    showGlobalStop(() => {
        BGTASKS[taskType].cancelled = true;
        saveTaskState(taskType);
        const stopBtn = document.querySelector(`#tvis-${taskType}-prog .tvis-rprog-cancel`);
        if (stopBtn) {
            stopBtn.disabled = true;
            stopBtn.textContent = '...';
        }
    });
    
    const runTask = async () => {
        const task = BGTASKS[taskType];
        showProgress();
        
        try {
            await processFn(task);
        } catch (error) {
            console.error(`Background task ${taskType} error:`, error);
        }
        
        task.running = false;
        saveTaskState(taskType);
        hideProgressWithNotification(taskType, task.cancelled, task);
    };
    
    runTask();
};

// PARSE CHAR PAGE
const parseCharPage = html => {
    const doc  = new DOMParser().parseFromString(html,'text/html');
    const name = doc.querySelector('.charPresBox h2')?.textContent.trim(); if (!name) return null;
    const pres = doc.querySelector('.characterPresentation');
    const bandLink   = pres?.querySelector('a[href*="/Artist/"]');
    const cityLink   = pres?.querySelector('a[href*="/City/"]');
    const localeLink = [...(pres?.querySelectorAll('a[href*="/Locale/"]')||[])].find(a=>!a.href.includes('CharactersPresent')&&!a.href.includes('MoveToLocale'));
    const onlineTxt  = doc.getElementById('ctl00_cphLeftColumn_ctl00_lnkOnlineStatus')?.textContent.trim()
        || doc.getElementById('ctl00_cphLeftColumn_ctl00_trOnlineStatus')?.querySelector('td')?.textContent.replace(/Durum[u]?\s*:/,'').trim() || '';
    const attitude   = doc.getElementById('ctl00_cphLeftColumn_ctl00_lnkAttitude')?.textContent.trim() || '';
    const stateImg   = doc.getElementById('ctl00_cphLeftColumn_ctl00_imgState');
    const state      = stateImg ? [...stateImg.closest('tr').querySelectorAll('td')].pop()?.textContent.trim()||'' : '';
    const avatarDiv  = doc.querySelector('.avatar.idTrigger');
    const avatarUrl  = avatarDiv?.style.backgroundImage?.match(/url\(['"]?([^'"]+)['"]?\)/)?.[1] || '';
    const cashRow    = doc.getElementById('ctl00_cphLeftColumn_ctl00_imgCash');
    const cash       = cashRow ? [...cashRow.closest('tr').querySelectorAll('td')].pop()?.textContent.trim()||'' : '';
    return {
        name, band: bandLink?.textContent.trim()||'', bandHref: bandLink?.getAttribute('href')||'',
        city: cityLink?.textContent.trim()||'', cityHref: cityLink?.getAttribute('href')||'',
        locale: localeLink?.textContent.trim()||'', localeHref: localeLink?.getAttribute('href')||'',
        localeId: localeLink?.href.match(/\/Locale\/(\d+)/)?.[1]||'',
        online: onlineTxt, attitude, state, avatarUrl, cash
    };
};

// ONLINE DOT — extracts just the date part to avoid prefix duplication
const onlineDot = (online, small) => {
    const dateMatch = (online||'').match(/(\d{1,2}\.\d{2}\.\d{4})/);
    const isDate    = !!dateMatch;
    const dateStr   = dateMatch?.[1] || '';
    const off       = !online || online === s('cpOffline') || online === 'Offline' || online === 'Çevrimdışı' || online === 'Desconectado' || isDate;
    const sz        = small ? '6px' : '7px';
    const dot       = `<span style="background:${off?'#aaa':'#28a745'};width:${sz};height:${sz};border-radius:50%;display:inline-block;margin-right:3px;vertical-align:middle;flex-shrink:0"></span>`;
    if (off) return dot + (isDate ? `${s('cpOffline')} · ${s('tkLastSeen')} ${dateStr}` : s('cpOffline'));
    return dot + online;
};

// SC CHARS (Speed Calling listesi)
const getSCChars = () => gmGet(DK.SC_CHARS, []) || [];
const setSCChars = a => gmSet(DK.SC_CHARS, a);
const addSCChar = (charId, name, type) => { const a=getSCChars(); if(a.some(c=>String(c.charId)===String(charId)))return false; a.push({charId:String(charId),name,type}); setSCChars(a); return true; };
const removeSCChar = charId => setSCChars(getSCChars().filter(c=>String(c.charId)!==String(charId)));

// CHARACTER HOVER POPUP (Karakter Kartı)
let popEl, hideT, fetchT, fetchSeq = 0;

const getPopEl = () => {
    if (!popEl) {
        popEl = mk('div','tvis-chpop');
        popEl.addEventListener('mouseenter', () => clearTimeout(hideT));
        popEl.addEventListener('mouseleave', () => { hideT = setTimeout(()=>{ popEl.style.display='none'; },1000); });
        document.body.appendChild(popEl);
    }
    return popEl;
};

const posPopup = (x,y) => {
    const el=getPopEl(), w=388, h=180;
    let l=x+14, t=y-10;
    if (l+w>innerWidth-8) l=x-w-14;
    if (t+h>innerHeight-8) t=innerHeight-h-8;
    el.style.left=Math.max(4,l)+'px'; el.style.top=Math.max(4,t)+'px';
};

const renderPopup = (id, data) => {
    const el      = getPopEl();
    const timeStr = new Date(data.t||Date.now()).toLocaleTimeString(dateLocale,{hour:'2-digit',minute:'2-digit'});
    const radarOn = isOnDef(K.tracking, true);
    const tracked = radarOn && isTrackedV2(id);
    const imgHtml = data.avatarUrl
        ? `<img src="${data.avatarUrl}" onerror="this.parentElement.innerHTML='🧑'" alt="">`
        : '🧑';
    el.innerHTML = `
<div class="tvis-pop-img">${imgHtml}</div>
<div class="tvis-pop-right">
  <div class="tvis-pop-head">
    <div class="tvis-pop-nameline">
      ${radarOn ? `<button class="tvis-trackbtn${tracked?' tracked':''}">${tracked?s('cpTracked'):s('cpTrack')}</button>` : ''}
      <span class="tvis-pop-name">${data.name}</span>
      <button class="tvis-pop-copyid">📋 ID</button>
    </div>
    <div style="font-size:10px;margin-top:2px">${onlineDot(data.online)}</div>
  </div>
  <div class="tvis-pop-body">
    ${data.band?`<div style="font-size:10px"><a href="${data.bandHref}" target="_blank" style="color:#6f42c1;text-decoration:none">🎸 ${data.band}</a></div>`:''}
    ${(data.city||data.locale)?`<div style="font-size:10px">${data.city?`<a href="${data.cityHref}" target="_blank" style="color:#17a2b8;text-decoration:none">🏙️ ${data.city}</a>`:''}${data.locale?` <a href="${data.localeHref}" target="_blank" style="color:#17a2b8;text-decoration:none">📍 ${data.locale}</a>`:''}</div>`:''}
    ${data.attitude?`<div style="font-size:10px;color:#555"><span style="color:#aaa;min-width:38px;display:inline-block">${s('cpAttitude')}</span>${data.attitude}</div>`:''}
    ${data.state?`<div style="font-size:10px;color:#555"><span style="color:#aaa;min-width:38px;display:inline-block">${s('cpState')}</span>${data.state}</div>`:''}
    ${data.cash?`<div style="font-size:10px;color:#555"><span style="color:#aaa;min-width:38px;display:inline-block">${s('cash')}</span><span style="color:#218838;font-weight:600">${data.cash}</span></div>`:''}
  </div>
  <div class="tvis-pop-links">
    <a href="${PM}/Interact/Phone/${id}" class="tvis-pop-link" target="_blank" title="Telefon">📞</a>
    <a href="${PM}/Conversations/Conversation/${id}" class="tvis-pop-link" target="_blank" title="Mesaj">✉️</a>
    <a href="${PM}/Interact/${id}" class="tvis-pop-link" target="_blank" title="İlgilen">👋</a>
    <a href="${PM}/Character/OfferItem/${id}" class="tvis-pop-link" target="_blank" title="Eşya Ver">🎁</a>
    <a href="${PM}/Character/GiveMoney/${id}" class="tvis-pop-link" target="_blank" title="Para Ver">💰</a>
    <a href="${PM}/Character/Blog/${id}" class="tvis-pop-link" target="_blank" title="Blog">📖</a>
    <div style="margin-left:auto;display:flex;align-items:center;gap:4px;flex-shrink:0">
      <button class="tvis-updatebtn" title="${s('cpRefresh')}">🔄 ${s('cpRefresh').replace('🔄 ','')}</button>
      <span style="font-size:10px;color:#bbb">${timeStr}</span>
    </div>
  </div>
</div>`;

    if (radarOn) {
        el.querySelector('.tvis-trackbtn')?.addEventListener('click', () => {
            const trackBtn = el.querySelector('.tvis-trackbtn');
            const alreadyTracked = isTrackedV2(id);
            if (alreadyTracked) {
                // Show mini menu: Remove or Move
                document.getElementById('tvis-track-menu')?.remove();
                const menu = mk('div','tvis-tab-edit-pop'); menu.id='tvis-track-menu';
                const rect = trackBtn.getBoundingClientRect();
                menu.style.left = Math.min(rect.left, innerWidth-200)+'px';
                menu.style.top  = (rect.bottom+4)+'px';
                const rmBtn = mkB(s('tkRemove'),'btn-sm btn-r',()=>{
                    removeFromRadar(id);
                    menu.remove(); renderPopup(id,data);
                });
                const mvBtn = mkB(s('tkMove'),'btn-sm btn-v',()=>{
                    menu.remove();
                    showPagePicker(trackBtn, pageIdx => {
                        moveInRadar(id, pageIdx);
                        renderPopup(id,data);
                    }, true);
                });
                rmBtn.style.cssText='width:100%;margin-bottom:3px;display:block;text-align:left';
                mvBtn.style.cssText='width:100%;display:block;text-align:left';
                menu.append(rmBtn,mvBtn);
                document.body.appendChild(menu);
                setTimeout(()=>{
                    const close=e=>{if(!menu.contains(e.target)){menu.remove();document.removeEventListener('click',close);}};
                    document.addEventListener('click',close);
                },50);
            } else {
                showPagePicker(trackBtn, pageIdx => {
                    const newEntry = { charId:String(id), charName:data.name, band:data.band, city:data.city, cityHref:data.cityHref, locale:data.locale, localeHref:data.localeHref, localeId:data.localeId, online:data.online, attitude:data.attitude, state:data.state, cash:data.cash||'', avatarUrl:data.avatarUrl||'', savedAt:Date.now() };
                    addToRadar(pageIdx, newEntry);
                    renderPopup(id,data);
                });
            }
        });
    }
    el.querySelector('.tvis-pop-copyid').onclick = () => {
        navigator.clipboard?.writeText(id);
        addClip(id,data.name,'character');
    };
    el.querySelector('.tvis-updatebtn').onclick = ev => {
        ev.stopPropagation(); CC.del(id);
        const rect=el.getBoundingClientRect(); loadAndShow(id,rect.left+10,rect.top+10);
    };

    // Speed Calling — Friend / Romantic toggle (no confirm)
    const updateWrap = el.querySelector('.tvis-pop-links > div:last-child');
    if (updateWrap) {
        let fBtn, rBtn;
        const refreshFR = () => {
            const cur = getSCChars().find(c=>String(c.charId)===String(id));
            fBtn.textContent = cur?.type==='arkadaş' ? '👥 ✓' : '👥';
            rBtn.textContent = cur?.type==='romantik' ? '💕 ✓' : '💕';
            fBtn.className = 'tvis-trackbtn' + (cur?.type==='arkadaş'?' tracked':'');
            rBtn.className = 'tvis-trackbtn' + (cur?.type==='romantik'?' tracked':'');
        };
        fBtn = mk('button'); rBtn = mk('button');
        fBtn.title = 'Arkadaş'; rBtn.title = 'Romantik';
        fBtn.onclick = () => {
            const cur = getSCChars().find(c=>String(c.charId)===String(id));
            if (cur?.type==='arkadaş') removeSCChar(id);
            else { if (cur) removeSCChar(id); addSCChar(id, data.name||id, 'arkadaş'); }
            refreshFR();
        };
        rBtn.onclick = () => {
            const cur = getSCChars().find(c=>String(c.charId)===String(id));
            if (cur?.type==='romantik') removeSCChar(id);
            else { if (cur) removeSCChar(id); addSCChar(id, data.name||id, 'romantik'); }
            refreshFR();
        };
        refreshFR();

        // Butonları sağdaki div'in içerisine (Güncelleme butonunun yanına) yerleştiriyoruz
        updateWrap.insertBefore(rBtn, updateWrap.firstChild);
        updateWrap.insertBefore(fBtn, updateWrap.firstChild);
        const scLbl = Object.assign(mk('span','','SC:'), {style:'font-size:9px;color:#888;white-space:nowrap;margin-right:2px'});
        updateWrap.insertBefore(scLbl, updateWrap.firstChild);
    }

    el.style.display='flex';
};

// PAGE PICKER — choose radar page when adding a character
const showPagePicker = (anchorEl, onSelect, moveMode=false) => {
    document.getElementById('tvis-page-picker')?.remove();
    const tabs = getRadarTabs();
    const pop = mk('div','tvis-page-picker'); pop.id='tvis-page-picker';
    const rect = anchorEl ? anchorEl.getBoundingClientRect() : { left: innerWidth/2-100, bottom: innerHeight/2 };
    pop.style.left = Math.min(Math.max(10, rect.left), innerWidth-210)+'px';
    pop.style.top  = (rect.bottom+6)+'px';
    pop.appendChild(mk('div','tvis-sec', moveMode ? s('tkPageMove') : s('tkPageSelect')));
    tabs.forEach((tab, i) => {
        const pageArr = getRadarPage(i);
        const count   = pageArr.length;
        const full    = count >= RADAR_PAGE_SIZE;
        const label   = `${tab.icon} ${tab.name}`;
        const btn = mkB(`${label}  (${count}/${RADAR_PAGE_SIZE})`, 'btn-sm '+(full?'btn-grey':'btn-v'), () => {
            if (full) { showToast(s('tkPageFull')); return; }
            pop.remove(); onSelect(i);
        });
        btn.style.cssText='width:100%;margin-bottom:3px;text-align:left;display:block';
        pop.appendChild(btn);
    });
    document.body.appendChild(pop);
    setTimeout(() => {
        const close = e => { if (!pop.contains(e.target)) { pop.remove(); document.removeEventListener('click', close); } };
        document.addEventListener('click', close);
    }, 50);
};

const loadAndShow = async (id,x,y) => {
    const seq = ++fetchSeq;
    const cached = CC.get(id);
    if (cached) { if (fetchSeq===seq) { renderPopup(id,cached); posPopup(x,y); } return; }
    const el = getPopEl();
    el.innerHTML = `<div style="color:#999;font-size:11px;padding:12px">${s('cpLoading')}</div>`;
    el.style.display='flex'; posPopup(x,y);
    await waitGap(); if (fetchSeq!==seq) return;
    lastFetch = Date.now();
    const html = await fetch(`${PM}/Character/${id}`).then(r=>r.ok?r.text():null).catch(()=>null);
    if (fetchSeq!==seq) return;
    if (!html) { el.style.display='none'; return; }
    const data = parseCharPage(html);
    if (!data) { el.style.display='none'; return; }
    CC.set(id,data); renderPopup(id,data); posPopup(x,y);
};

const applyCharPopup = () => {
    if (!isOnDef(K.charPopup, true)) return;
    let activeLink = null;
    document.addEventListener('mouseover', e => {
        const link = e.target.closest('a[href*="/Character/"]');
        if (link===activeLink) return;
        if (!link||link.closest('#tvis-bar,#tvip-bar,.tvis-ov,.tvis-chpop')) return;
        const m = link.href?.match(/\/Character\/(\d+)(?:[/?#]|$)/); if (!m) return;
        activeLink = link; const id=m[1];
        clearTimeout(fetchT); clearTimeout(hideT);
        const onMove = ev => posPopup(ev.clientX,ev.clientY);
        link.addEventListener('mousemove',onMove);
        fetchT = setTimeout(()=>loadAndShow(id,e.clientX,e.clientY),300);
        link.addEventListener('mouseleave',()=>{ activeLink=null; clearTimeout(fetchT); link.removeEventListener('mousemove',onMove); hideT=setTimeout(()=>{ if(popEl)popEl.style.display='none'; },1000); },{once:true});
    });
};

// RADAR (Takip İstasyonu)
const openTrackModal = () => mkModal(s('tkTitle'), box => {
    let trackPage = Math.max(0, Math.min(RADAR_PAGES-1, parseInt(gmGet(DK.TRACK_PAGE, 0)) || 0));
    let radarSearchQ = '';

    const getBadge = (cls, txt) => `<div class="tvis-tc-badge"><span class="${cls}">${txt}</span></div>`;

    const renderCard = entry => {
        const cached  = CC.get(entry.charId);
        const d       = cached || entry;
        const moved   = cached?.localeId && entry.localeId && cached.localeId !== entry.localeId;
        const isOff   = !d.online || /Çevrimdışı|Offline|Desconectado/.test(d.online) || /\d{1,2}\.\d{2}\.\d{4}/.test(d.online);
        const updated = !moved && cached && (cached.online !== entry.online || cached.attitude !== entry.attitude || cached.state !== entry.state);
        const online  = !isOff;

        const card = mk('div','tvis-tcard');
        card.dataset.did = entry.charId;
        card.draggable   = true; // FIX: enable drag on radar cards
        if (moved)        card.classList.add('tc-moved');
        else if (updated) card.classList.add('tc-updated');
        else if (online)  card.classList.add('tc-online');

        const imgWrap = mk('div','tvis-tc-img');
        const imgBg   = mk('div','tvis-tc-imgbg');
        if (d.avatarUrl||entry.avatarUrl) {
            const img=mk('img'); img.src=d.avatarUrl||entry.avatarUrl;
            img.onerror=()=>{imgBg.innerHTML='🧑';}; imgBg.appendChild(img);
        } else { imgBg.textContent='🧑'; }
        const dragH = mk('span','tvis-tc-drag','⠿');
        let badgeHtml = '';
        if (moved)        badgeHtml = getBadge('badge-mv', s('badgeMoved'));
        else if (updated) badgeHtml = getBadge('badge-upd', s('badgeUpdated'));
        else if (online)  badgeHtml = getBadge('badge-on', s('badgeOnline'));
        else              badgeHtml = getBadge('badge-off', s('cpOffline'));
        imgBg.insertAdjacentHTML('beforeend', badgeHtml);
        imgWrap.append(imgBg, dragH);

        const body      = mk('div','tvis-tc-body');
        const nameA     = mk('a','tvis-tc-name', entry.charName);
        nameA.href      = `${PM}/Character/${entry.charId}`;
        const onlineRow = mk('div','tvis-tc-row');
        onlineRow.innerHTML = onlineDot(d.online||'', true);
        const locRow = mk('div','tvis-tc-row');
        locRow.innerHTML = [
            d.city  ? `<a href="${d.cityHref||'#'}">${d.city}</a>` : '',
            d.locale? `<a href="${d.localeHref||'#'}">📍 ${d.locale}</a>` : ''
        ].filter(Boolean).join(' › ');

        const allNotes = gmGet(DK.NOTES,{})||{};
        const noteTA   = mk('textarea','tvis-tc-note'); noteTA.placeholder=s('tkNote'); noteTA.value=allNotes[entry.charId]||'';
        const saveBtn  = mk('button','tvis-tc-ok','💾'); saveBtn.style.cssText='font-size:8px;padding:1px 3px;flex-shrink:0';
        saveBtn.onclick = () => { const n=gmGet(DK.NOTES,{})||{}; n[entry.charId]=noteTA.value; gmSet(DK.NOTES,n); };

        const updTime  = entry.savedAt ? new Date(entry.savedAt).toLocaleTimeString(dateLocale,{hour:'2-digit',minute:'2-digit'}) : '--:--';
        const actions  = mk('div','tvis-tc-actions');

        // Mesaj butonu
        const msgA = mk('a','','✉️');
        msgA.style.cssText='font-size:13px;opacity:.6;text-decoration:none;flex-shrink:0';
        msgA.href  = `${PM}/Conversations/Conversation/${entry.charId}`;
        msgA.title = 'Mesaj';
        msgA.target= '_blank';
        // Para ver butonu
        const moneyA = mk('a','','💰');
        moneyA.style.cssText='font-size:13px;opacity:.6;text-decoration:none;flex-shrink:0';
        moneyA.href  = `${PM}/Character/GiveMoney/${entry.charId}`;
        moneyA.title = 'Para Ver';
        moneyA.target= '_blank';
        // Yanına Git butonu
        const locId = d.localeId || entry.localeId;
        const gotoA = mk('a','','🗺️');
        gotoA.style.cssText='font-size:13px;opacity:.6;text-decoration:none;flex-shrink:0';
        gotoA.title = s('tkGoTo');
        gotoA.target= '_blank';
        if (locId) {
            gotoA.href = `${PM}/Locale/MoveToLocale/${locId}/${entry.charId}`;
        } else {
            gotoA.style.opacity = '0.2';
            gotoA.style.cursor  = 'default';
            gotoA.href = '#';
            gotoA.onclick = e => e.preventDefault();
        }

        const timeSpan = mk('span','tvis-tc-time',updTime);
        const refBtn   = mk('button','tvis-tc-ref','🔄'); refBtn.title=s('cpRefresh');
        refBtn.style.marginLeft = 'auto'; // push right group to end
        const xBtn     = mk('button','tvis-tc-x','✕');

        refBtn.onclick = async () => {
            refBtn.disabled=true; refBtn.textContent='⏳';
            await waitGap(); lastFetch=Date.now();
            const html=await fetch(`${PM}/Character/${entry.charId}`).then(r=>r.ok?r.text():null).catch(()=>null);
            if (html){const data=parseCharPage(html);if(data){CC.set(entry.charId,data);updateInRadar(entry.charId,data);}}
            render();
        };
        const mvBtn = mk('button','tvis-tc-x','↕'); mvBtn.title=s('tkMove');
        mvBtn.onclick = () => {
            showPagePicker(mvBtn, pageIdx => { moveInRadar(entry.charId, pageIdx); render(); }, true);
        };
        xBtn.onclick = () => {
            if(!confirm(s('tkConfirmRm')))return;
            removeFromRadar(entry.charId);
            render();
        };

        // Build body
        body.appendChild(nameA);
        body.appendChild(onlineRow);
        body.appendChild(locRow);

        if (moved) {
            const warnRow=mk('div','tvis-tc-warn',`${s('tcWarnMoved')} ${entry.locale}`);
            body.appendChild(warnRow);
        } else if (updated) {
            const warnRow=mk('div','tvis-tc-warn upd', s('tcWarnUpdated'));
            body.appendChild(warnRow);
        } else {
            // Show state + cash compactly
            const infoParts = [];
            if (d.state) infoParts.push(d.state);
            if (d.cash)  infoParts.push(`<span style="color:#218838;font-weight:600">💰 ${d.cash}</span>`);
            if (infoParts.length) {
                const infoRow = mk('div','tvis-tc-row');
                infoRow.innerHTML = infoParts.join(' · ');
                body.appendChild(infoRow);
            } else if (d.band) {
                body.appendChild(mk('div','tvis-tc-row','🎸 '+d.band));
            }
        }

        const noteRow = mk('div'); noteRow.style.cssText='display:flex;gap:2px;margin-top:auto';
        noteRow.append(noteTA, saveBtn);
        body.appendChild(noteRow);

        if (moved) {
            const okBtn=mk('button','tvis-tc-ok','✔'); okBtn.title=s('tkConfirmOk'); okBtn.style.flexShrink='0';
            okBtn.onclick=()=>{ updateInRadar(entry.charId,{locale:cached.locale,localeId:cached.localeId,city:cached.city,cityHref:cached.cityHref}); render(); };
            actions.append(msgA, moneyA, gotoA, okBtn, refBtn, mvBtn, timeSpan, xBtn);
        } else {
            actions.append(msgA, moneyA, gotoA, refBtn, mvBtn, timeSpan, xBtn);
        }
        body.appendChild(actions);

        card.append(imgWrap,body);
        return card;
    };

    const bulkFetch = entries => {
        if (BGTASKS.radar.running) return;

        startBackgroundTask('radar', entries.length, async (task) => {
            for(let i = 0; i < entries.length; i++){
                if (task.cancelled) break;
                if(i > 0) await new Promise(r => setTimeout(r, 2000 + Math.random() * 4000));
                if (task.cancelled) break;
                await waitGap(); lastFetch = Date.now();

                const html = await fetch(`${PM}/Character/${entries[i].charId}`)
                    .then(r => r.ok ? r.text() : null)
                    .catch(() => null);

                if(html) {
                    const data = parseCharPage(html);
                    if(data) {
                        CC.set(entries[i].charId, data);
                        updateInRadar(entries[i].charId, data);
                    }
                }

                task.current = i + 1;
                saveTaskState('radar');

                const txtEl = document.getElementById('tvis-radar-prog-txt');
                if (txtEl) txtEl.textContent = `${s('tkBgRunning')} ${task.current}/${task.total}`;
            }
            if(document.getElementById('tvis-modal')) render();
        });
    };

    // TAB EDIT POPUP — opens via ✏️ button next to each tab
    const openTabEdit = (tabIdx, anchorEl) => {
        document.getElementById('tvis-tab-edit-pop')?.remove();
        const pop = mk('div','tvis-tab-edit-pop'); pop.id='tvis-tab-edit-pop';
        const rect = anchorEl.getBoundingClientRect();
        pop.style.left = Math.min(rect.left, innerWidth-240)+'px';
        pop.style.top  = (rect.bottom+4)+'px';

        const tabs = getRadarTabs();
        const cur  = tabs[tabIdx];
        let selIcon = cur.icon;

        pop.appendChild(mk('div','tvis-sec',s('tkTabEdit')));
        const iconPick = mk('div','tvis-icon-pick'); iconPick.style.maxHeight='80px';
        const custom = getCustomIcons();
        const allIcos = custom.length ? [...custom, '|', ...ICONS] : ICONS;
        allIcos.forEach(ico => {
            if (ico === '|') {
                const sep = mk('div'); sep.style.cssText='width:100%;height:1px;background:#eee;margin:2px 0';
                iconPick.appendChild(sep); return;
            }
            const b = mk('button', ico===selIcon?'sel':'', ico); b.type='button';
            b.onclick = () => { selIcon=ico; iconPick.querySelectorAll('button').forEach(x=>x.className=''); b.className='sel'; };
            iconPick.appendChild(b);
        });
        pop.appendChild(iconPick);

        const nameI = mk('input'); nameI.value=cur.name; nameI.maxLength=12;
        nameI.style.cssText='width:100%;box-sizing:border-box;padding:3px 6px;border:1px solid #ccc;border-radius:3px;font-size:11px;margin:4px 0';
        pop.appendChild(nameI);

        const getTabColor = mkColorPicker(pop, cur.color||'#6f42c1');

        pop.appendChild(mkB('✔ ' + s('iaSave'), 'btn-sm btn-g', () => {
            const t = getRadarTabs();
            t[tabIdx] = { icon: selIcon, name: (nameI.value.trim().toUpperCase().substring(0,12)) || String(tabIdx+1), color: getTabColor() };
            gmSet(DK.RADAR_TABS, t);
            pop.remove();
            render();
        }));

        const cancelBtn = mkB(s('cancelLbl'), 'btn-sm btn-grey', () => pop.remove());
        cancelBtn.style.marginLeft='4px';
        pop.lastChild.insertAdjacentElement('afterend', cancelBtn);

        document.body.appendChild(pop);
        setTimeout(() => {
            const close = e => { if (!pop.contains(e.target)) { pop.remove(); document.removeEventListener('mousedown', close); } };
            document.addEventListener('mousedown', close);
        }, 10);
    };

    const render = () => {
        box.querySelectorAll('.tvis-tl').forEach(e=>e.remove());
        const pageEntries = getRadarPage(trackPage);
        const tabs        = getRadarTabs();
        const realEntries = pageEntries;

        if (!pageEntries.length && trackPage === 0) {
            const anyTracked = getAllTracked().length > 0;
            if (!anyTracked) {
                const w=mk('div','tvis-tl'); const d=mk('div','',s('tkEmpty')); d.style.cssText='color:#999;font-size:12px;margin-bottom:10px';
                w.appendChild(d); box.appendChild(w); return;
            }
        }

        const wrap = mk('div','tvis-tl');

        // SEARCH BAR
        const searchRow = mk('div'); searchRow.style.cssText='display:flex;gap:6px;align-items:center;margin-bottom:8px';
        const searchI = mk('input'); searchI.id='tvis-radar-search';
        searchI.placeholder='🔍 Karakterleri ara...';
        searchI.style.cssText='flex:1;padding:4px 8px;border:1px solid #c9b8f0;border-radius:4px;font-size:12px';
        searchI.value = radarSearchQ;
        const clearBtn = mkB('✕','btn-sm btn-grey',()=>{ radarSearchQ=''; render(); });
        clearBtn.style.display = radarSearchQ ? '' : 'none';
        searchI.addEventListener('input',()=>{ radarSearchQ=searchI.value; clearBtn.style.display=radarSearchQ?'':'none'; renderSearchOrNormal(); });
        searchRow.append(searchI, clearBtn);
        wrap.appendChild(searchRow);

        const renderSearchOrNormal = () => {
            // Remove previous content below search bar
            wrap.querySelectorAll('.tvis-tl-body').forEach(e=>e.remove());
            const body = mk('div','tvis-tl-body');
            wrap.appendChild(body);

            if (radarSearchQ.trim()) {
                // Search mode
                const q = radarSearchQ.trim().toLowerCase();
                const results = [];
                for (let i=0; i<RADAR_PAGES; i++) {
                    getRadarPage(i).forEach(entry => {
                        if ((entry.charName||'').toLowerCase().includes(q)) results.push({entry,pageIdx:i});
                    });
                }
                if (!results.length) {
                    const em=mk('div','',`"${radarSearchQ}" için sonuç bulunamadı.`); em.style.cssText='color:#999;font-size:12px;padding:16px 0;text-align:center';
                    body.appendChild(em);
                } else {
                    const infoRow=mk('div',''); infoRow.style.cssText='font-size:10px;color:#aaa;margin-bottom:6px';
                    infoRow.textContent=`${results.length} sonuç`;
                    body.appendChild(infoRow);
                    const grid=mk('div','tvis-tgrid');
                    results.forEach(({entry,pageIdx})=>{
                        const card=renderCard(entry);
                        // Tab badge overlay
                        const tab=tabs[pageIdx];
                        const badge=mk('div',''); badge.style.cssText='position:absolute;top:3px;right:3px;font-size:8px;padding:1px 5px;border-radius:8px;color:#fff;font-weight:700;z-index:2;pointer-events:none';
                        badge.style.background=tab.color||'#6f42c1';
                        badge.textContent=`${tab.icon} ${tab.name}`;
                        card.style.position='relative';
                        card.appendChild(badge);
                        grid.appendChild(card);
                    });
                    body.appendChild(grid);
                }
                // Tab row — dimmed in search mode
                const tabRow2 = buildTabRow(tabs, true);
                body.insertAdjacentElement('afterbegin', tabRow2);
            } else {
                // Normal mode
                const tabRow2 = buildTabRow(tabs, false);
                body.appendChild(tabRow2);
                buildNormalContent(body, tabs, realEntries);
            }
        };

        const buildTabRow = (tabs, dimmed) => {
            const tabRow = mk('div'); tabRow.style.cssText='display:flex;gap:4px;align-items:center;margin-bottom:10px;flex-wrap:wrap';
            if(dimmed) tabRow.style.opacity='0.55';
            tabs.forEach((tab, i) => {
                const tabWrap = mk('span'); tabWrap.style.cssText='display:inline-flex;align-items:center;gap:1px';
                const isWT = false;
                const lbl  = `${tab.icon} ${tab.name}`;
                const btn  = mk('button', 'tvis-page-tab'+(trackPage===i&&!dimmed?' active':''), lbl);
                const tc = tab.color||'#6f42c1';
                btn.style.borderColor = tc;
                if(trackPage===i&&!dimmed){ btn.style.background=tc; btn.style.color='#fff'; }
                else { btn.style.color=tc; }
                btn.onclick = () => { radarSearchQ=''; trackPage=i; gmSet(DK.TRACK_PAGE,i); render(); };
                btn.title   = tab.icon+' '+tab.name;
                const editBtn = mk('button','tvis-tab-edit-btn','✏️');
                editBtn.title = s('tkTabEdit');
                editBtn.onclick = e => { e.stopPropagation(); openTabEdit(i, editBtn); };
                tabWrap.append(btn, editBtn);
                tabRow.appendChild(tabWrap);
            });
            const bulkBtn = mk('button','btn-b',s('tkUpdate')); bulkBtn.style.cssText='font-size:10px;padding:3px 10px;margin-left:auto;white-space:nowrap;flex-shrink:0';
            bulkBtn.onclick = () => { if (BGTASKS.radar.running) return; bulkFetch(pageEntries.filter(e=>!e._empty)); };
            if (BGTASKS.radar.running) { bulkBtn.disabled=true; bulkBtn.style.opacity='.5'; }
            tabRow.appendChild(bulkBtn);
            return tabRow;
        };

        const buildNormalContent = (body, tabs, realEntries) => {
            const grid = mk('div','tvis-tgrid');
            mkDraggable(grid, ids => {
                const pageArr = getRadarPage(trackPage);
                const map = {}; pageArr.forEach(e => { map[e.charId] = e; });
                const reordered = ids.map(id => map[id]).filter(Boolean);
                setRadarPage(trackPage, reordered); render();
            });
            pageEntries.forEach(entry => { grid.appendChild(renderCard(entry)); });
            const totalTracked = getAllTracked().length;
            body.appendChild(grid);
            const footer = mk('div'); footer.style.cssText='margin-top:8px;font-size:10px;color:#bbb;display:flex;justify-content:space-between;flex-wrap:wrap;gap:4px';
            footer.innerHTML=`<span>${s('tcFooterHint')}</span><span>${s('tcPage')} ${trackPage+1}: ${realEntries.length}/${RADAR_PAGE_SIZE} · ${s('tcTotal')}: ${totalTracked}</span>`;
            body.appendChild(footer);
        };

        renderSearchOrNormal();
        box.appendChild(wrap);
        // Restore focus to search box after re-render
        if (radarSearchQ) setTimeout(()=>{ const si=document.getElementById('tvis-radar-search'); if(si){si.focus();si.setSelectionRange(si.value.length,si.value.length);} },10);
    };
    render();
});

// INTERACT HELPER DATA
const INTERACT_DATA = {
    1:{sub:'temel',joy:'+2-3%',love:null,hate:'-1%',note:null},
    3:{sub:'sözlü',joy:'+2-3%',love:null,hate:'-2%',note:null},
    4:{sub:'özel',joy:'+1%',love:null,hate:'-1%',note:'🎭 ⭐ Komedi'},
    5:{sub:'sözlü',joy:'+2-3%',love:null,hate:'-2%',note:null},
    6:{sub:'fiziksel',joy:'+5%',love:null,hate:null,note:'👶 Bebeklere'},
    7:{sub:'fiziksel',joy:null,love:'+2%',hate:null,note:'🍻 Bar/Rest'},
    8:{sub:'fiziksel',joy:'+2-3%',love:null,hate:null,note:null},
    9:{sub:'yakın temas',joy:null,love:'+5%',hate:null,note:null},
    10:{sub:'yakın temas',joy:null,love:'+5%',hate:null,note:null},
    11:{sub:'cinsel',joy:'-1%',love:'+5%',hate:null,note:'🔞 ⭐ Seks | 🏠 Ev/Otel/Park'},
    12:{sub:'fiziksel',joy:null,love:'+2-3%',hate:'-1%',note:null},
    13:{sub:'cinsel',joy:null,love:'+2-3%',hate:null,note:'🔞 ⭐ Seks | 🏠 Ev/Otel/Park'},
    14:{sub:'sözlü',joy:null,love:'+2-3%',hate:null,note:null},
    15:{sub:'temel',joy:'-1%',love:null,hate:'+5%',note:null},
    16:{sub:'fiziksel',joy:null,love:null,hate:'+5%',note:null},
    17:{sub:'yakın temas',joy:null,love:null,hate:'+10%',note:'👊 Karşılıklı vuruş: Kalıcı sağlık kaybı 🩹'},
    18:{sub:'fiziksel',joy:'+5%',love:null,hate:'-4%',note:null},
    19:{sub:'cinsel',joy:'-1%',love:'+5%',hate:null,note:'🔞 ⭐ Tantrik | 🏠 Ev/Otel'},
    20:{sub:'cinsel',joy:null,love:'+5%',hate:null,note:'🏠 Ev/Otel'},
    21:{sub:'özel',joy:'+2-3%',love:null,hate:'-2%',note:'🎤 ⭐ Şarkı'},
    22:{sub:'özel',joy:null,love:null,hate:null,note:'📔 Sadece günlük yazısı'},
    24:{sub:'telefon',joy:'+2-3%',love:null,hate:'-2%',note:null},
    25:{sub:'telefon',joy:null,love:'+5%',hate:null,note:null},
    26:{sub:'telefon',joy:'+2-3%',love:null,hate:'-2%',note:null},
    29:{sub:'özel',joy:'+2%',love:null,hate:null,note:null},
    30:{sub:'fiziksel',joy:null,love:'+5%',hate:null,note:null},
    32:{sub:'tıbbi',joy:'+5%',love:null,hate:'-5%',note:'🩺 ⭐ Tıp'},
    33:{sub:'özel',joy:'+2-8%',love:null,hate:'-6%',note:'🪄 ⭐ İllüzyon | Düşük Yetenek arkadaşlığı düşürür'},
    34:{sub:'sözlü',joy:'+5%',love:null,hate:'-4%',note:null},
    35:{sub:'fiziksel',joy:null,love:'+3-7%',hate:null,note:'💃 ⭐ Dans | 🍻 Bar/Rest/Stüdyo/Lahit'},
    36:{sub:'fiziksel',joy:'-4-6%',love:null,hate:'+5-7%',note:null},
    37:{sub:'ölümcül',joy:'-10%',love:null,hate:'+10%',note:null},
    38:{sub:'ölümcül',joy:'-5%',love:null,hate:'+10%',note:null},
    39:{sub:'ruhani',joy:'+1-2%',love:null,hate:null,note:'⛪ ⭐ Din | İbadet evi'},
    41:{sub:'yakın temas',joy:'-2%',love:null,hate:'+2%',note:null},
    42:{sub:'fiziksel',joy:null,love:null,hate:'+5%',note:null},
    44:{sub:'tıbbi',joy:'+5%',love:null,hate:'-5%',note:null},
    45:{sub:'ölümcül',joy:null,love:null,hate:'+10%',note:'🔫 ⭐ Silah'},
    46:{sub:'telefon',joy:'-5%',love:'-5%',hate:'+10%',note:null},
    47:{sub:'sözlü',joy:'+5%',love:null,hate:'-4%',note:null},
    48:{sub:'yakın temas',joy:'-2%',love:null,hate:'+3%',note:null},
    49:{sub:'fiziksel',joy:'-2-4%',love:'-2-3%',hate:'+3-5%',note:null},
    51:{sub:'sözlü',joy:'+5%',love:null,hate:'-5%',note:null},
    52:{sub:'sözlü',joy:'+2%',love:null,hate:null,note:'🤫 👶 Bebeklere'},
    54:{sub:'temel',joy:'+0-1%',love:null,hate:'-1%',note:null},
    55:{sub:'fiziksel',joy:'+2-3%',love:null,hate:'-2%',note:null},
    56:{sub:'yakın temas',joy:'+2-3%',love:null,hate:'-2%',note:null},
    57:{sub:'sözlü',joy:'+2-3%',love:null,hate:'-2-3%',note:null},
    58:{sub:'telefon',joy:'+2-3%',love:'+2%',hate:'-1%',note:null},
    59:{sub:'fiziksel',joy:'+2-3%',love:null,hate:'-2%',note:null},
    60:{sub:'yakın temas',joy:'+5%',love:null,hate:'-4%',note:null},
    61:{sub:'telefon',joy:'+2-3%',love:null,hate:'-2%',note:null},
    62:{sub:'sözlü',joy:'+5%',love:null,hate:'-4%',note:null},
    63:{sub:'fiziksel',joy:'+5%',love:null,hate:'-4%',note:null},
    64:{sub:'yakın temas',joy:null,love:'+5%',hate:null,note:null},
    65:{sub:'sözlü',joy:'+5%',love:null,hate:'-4%',note:null},
    66:{sub:'fiziksel',joy:'+5%',love:null,hate:'-4%',note:null},
    67:{sub:'yakın temas',joy:'+5%',love:null,hate:'-4%',note:null},
    68:{sub:'sözlü',joy:'+2%',love:null,hate:null,note:null},
    69:{sub:'en iyi arkadaş',joy:'+5%',love:'-1%',hate:null,note:null},
    70:{sub:'en iyi arkadaş',joy:'+5%',love:'-1%',hate:null,note:null},
    71:{sub:'sözlü',joy:null,love:'+2%',hate:null,note:null},
    72:{sub:'fiziksel',joy:null,love:null,hate:'+5%',note:null},
    73:{sub:'telefon',joy:null,love:'+2-3%',hate:'-2%',note:null},
    74:{sub:'telefon',joy:null,love:'+2-3%',hate:null,note:null},
    75:{sub:'sözlü',joy:null,love:'+5%',hate:null,note:null},
    76:{sub:'sözlü',joy:null,love:'+5%',hate:null,note:null},
    77:{sub:'sözlü',joy:null,love:'+5%',hate:null,note:null},
    78:{sub:'özel',joy:null,love:'+5%',hate:'-2%',note:'🎤 ⭐ Şarkı'},
    79:{sub:'sözlü',joy:null,love:null,hate:'+2%',note:null},
    80:{sub:'telefon',joy:'-2-3%',love:null,hate:'+2-3%',note:null},
    81:{sub:'sözlü',joy:null,love:null,hate:'+5%',note:null},
    82:{sub:'yakın temas',joy:null,love:null,hate:'+5%',note:null},
    83:{sub:'telefon',joy:null,love:null,hate:'+5%',note:null},
    84:{sub:'sözlü',joy:'-5%',love:null,hate:'+5%',note:null},
    85:{sub:'fiziksel',joy:null,love:null,hate:'+5%',note:null},
    86:{sub:'telefon',joy:'-3%',love:'+3%',hate:'+2-3%',note:null},
    87:{sub:'sözlü',joy:null,love:null,hate:'+5%',note:null},
    88:{sub:'yakın temas',joy:null,love:null,hate:'+5%',note:null},
    89:{sub:'fiziksel',joy:null,love:'+5%',hate:null,note:'🏋️ Spor/Sahil'},
    93:{sub:'yakın temas',joy:'+5%',love:null,hate:null,note:'👶 🧒 Çocuk/Bebek'},
    94:{sub:'sözlü',joy:'+2%',love:null,hate:null,note:'👶 Bebeklere'},
    96:{sub:'özel',joy:'+3-5%',love:null,hate:null,note:'🎤 ⭐ Şarkı | 👶 Bebeklere'},
    101:{sub:'sözlü',joy:'+5%',love:null,hate:null,note:'🧒 Çocuklara'},
    102:{sub:'yakın temas',joy:'+2%',love:null,hate:null,note:'🧒 Çocuklara'},
    103:{sub:'yakın temas',joy:'+5%',love:null,hate:null,note:'🧒 Çocuklara'},
    104:{sub:'sözlü',joy:'+5%',love:null,hate:'-4%',note:'🧒 Çocuklara'},
    105:{sub:'sözlü',joy:'+2%',love:null,hate:null,note:'🧒 Çocuklara'},
    115:{sub:'sözlü',joy:null,love:null,hate:'+3%',note:null},
    121:{sub:'telefon',joy:'+2-3%',love:null,hate:'+2-3%',note:null},
    129:{sub:'fiziksel',joy:null,love:'+5%',hate:'-3%',note:'🌲 Park/Kırsal'},
    133:{sub:'fiziksel',joy:null,love:null,hate:'+5%',note:'🎓 Üniversite'},
    136:{sub:'sözlü',joy:'+5%',love:null,hate:null,note:'🪦 Mezarlık'},
    137:{sub:'fiziksel',joy:null,love:null,hate:'+5%',note:null},
    138:{sub:'ölümcül',joy:null,love:'-10%',hate:'+10%',note:'🚗 Yol/Üni'},
    139:{sub:'sözlü',joy:'+5%',love:null,hate:null,note:'📚 Okul/Üni'},
    154:{sub:'sözlü',joy:null,love:'-15%',hate:null,note:'💔 Aşk 📉'},
    155:{sub:'sözlü',joy:null,love:null,hate:'-15%',note:'🕊️ Nefret 📉'},
    156:{sub:'sözlü',joy:'-15%',love:null,hate:null,note:'☹️ Keyif 📉'},
    157:{sub:'telefon',joy:'-3%',love:'-15%',hate:null,note:null},
    158:{sub:'fiziksel',joy:'+5%',love:null,hate:null,note:'🐟 Alabalık Şart'},
    161:{sub:'temel',joy:null,love:'+2%',hate:null,note:null},
    162:{sub:'telefon',joy:'+1%',love:null,hate:null,note:null},
    164:{sub:'cinsel',joy:'-1%',love:'+5%',hate:null,note:null},
    166:{sub:'sözlü',joy:'+0-3%',love:null,hate:'-3%',note:null},
    171:{sub:'telefon',joy:'+2%',love:null,hate:null,note:null}
};
// Merge hardcoded data with user's custom entries (custom overrides on conflict)
const getInteractData = () => {
    const custom = gmGet(DK.INTERACT_CUSTOM, {}) || {};
    return Object.assign({}, INTERACT_DATA, custom);
};

const INTERACT_DEFAULTS = {
    arkadaş:  [34, 62, 1],
    romantik: [35, 78, 10],
    nefret:   [15, 16, 84],
    özel:     [154, 155, 156],
};

const INTERACT_TYPES = ['standart','arkadaş','romantik','nefret','özel'];

// INLINE ACTIONS (Hızlı Bağlantılar)
const W = PM + '/';
const IA_DEF = {
    character: [
        {id:'msg',   icon:'✉️',label:'Mesaj',     path:`${W}Conversations/Conversation/`,  idType:'character'},
        {id:'iact',  icon:'👋',label:'İlgilen',   path:`${W}Interact/`,                    idType:'character'},
        {id:'copyid',icon:'📋',label:'ID Kopyala',path:'',                                 idType:'copyid'},
    ],
    locale: [
        {id:'goto',  icon:'🗺️',label:'Git',           path:`${W}Locale/MoveToLocale/`,      idType:'locale'},
        {id:'chars', icon:'👥',label:'Mekandakiler',   path:`${W}Locale/CharactersPresent/`, idType:'locale'},
        {id:'copyid',icon:'📋',label:'ID Kopyala',     path:'',                              idType:'copyid'},
    ],
    artist: [
        {id:'upcoming',icon:'🎵',label:'Gelecek Konserler',path:`${W}Artist/UpcomingPerformances/`,idType:'artist'},
        {id:'past',    icon:'🎶',label:'Son Konserler',    path:`${W}Artist/PastPerformances/`,    idType:'artist'},
        {id:'copyid',  icon:'📋',label:'ID Kopyala',       path:'',                               idType:'copyid'},
    ],
    city: [
        {id:'flight',icon:'✈️',label:'Uçuş',    path:`${W}City/BookFlight/`, idType:'city'},
        {id:'jet',   icon:'🛩️',label:'VIP Jet', path:`${W}City/PrivateJet/`, idType:'city'},
        {id:'road',  icon:'🚗',label:'Kara',     path:`${W}City/RoadTrip/`,   idType:'city'},
    ],
};
// copyid removed from type dropdown — copyid buttons in IA_DEF still work
const IA_TYPES  = ['character','locale','artist','city'];
const IA_LABELS = {
    character:   '🧑 Karakter',
    locale:      '📍 Mekan',
    artist:      '🎸 Sanatçı',
    city:        '🏙️ Şehir',
};

const getIA  = sec => gmGet(DK.IA+sec, null) || IA_DEF[sec] || [];
const saveIA = (sec,v) => gmSet(DK.IA+sec,v);
const resetIA= sec => gmDel(DK.IA+sec);

// IA Exclusion list helpers
const getIAExcl  = () => gmGet(DK.IA_EXCL, []) || [];
const saveIAExcl = v  => gmSet(DK.IA_EXCL, v);

// Detect own character ID/name from the top nav shortcuts
const getOwnCharInfo = () => {
    const a = document.querySelector('#character-tools-shortcuts a[href*="/Character/"]');
    if (!a) return null;
    const m = a.href.match(/\/Character\/(\d+)/);
    if (!m) return null;
    return { id: m[1], name: (a.textContent || '').trim() };
};

const parseURL = url => { try { return new URL(url.includes('://')?url:'https://p.com'+url).pathname.replace(/\/\d+([?#].*)?$/,'').replace(/\/?$/,'/'); } catch { return url; } };

const openIAEdit = () => mkModal(s('iaEdit'), cont => {
    let active='character';
    const tabs=mk('div'); tabs.style.cssText='display:flex;gap:4px;margin-bottom:10px;flex-wrap:wrap';
    const tBtns={}; cont.appendChild(tabs);
    const secCont=mk('div'); cont.appendChild(secCont);
    const renderSec=()=>{
        secCont.innerHTML='';
        const cfg=getIA(active).map(a=>({...a}));
        const list=mk('div');
        mkDraggable(list,ids=>{ const cur=getIA(active),map={}; cur.forEach(a=>{map[a.id]=a;}); saveIA(active,ids.map(id=>map[id]).filter(Boolean)); renderSec(); });
        cfg.forEach(a=>{
            const row=mk('div','tvis-iarow'); row.draggable=true; row.dataset.did=a.id;
            const dr=mk('span','','⠿'); dr.style.cssText='color:#ccc;cursor:grab;flex-shrink:0';
            const ic=mk('span','',a.icon+' ');
            const lb=mk('span','',a.label); lb.style.flex='1';
            const tp=mk('span','',a.idType); tp.style.cssText='font-size:10px;color:#aaa;flex-shrink:0';
            const rm=mkB('🗑','btn-sm btn-r',()=>{saveIA(active,getIA(active).filter(x=>x.id!==a.id));renderSec();});
            row.append(dr,ic,lb,tp,rm); list.appendChild(row);
        });
        secCont.appendChild(list);
        const rst=mkB(s('iaDefaults'),'btn-sm btn-grey',()=>{if(confirm(s('iaResetConfirm'))){resetIA(active);renderSec();}});
        rst.style.margin='6px 0'; secCont.appendChild(rst);
        const form=mk('div'); form.style.cssText='border-top:1px solid #eee;padding-top:8px;margin-top:2px';
        form.appendChild(mk('div','tvis-sec',s('iaAddSec')));
        const urlI=mk('input'); urlI.placeholder=s('iaUrlPlh'); urlI.style.cssText='width:100%;box-sizing:border-box;padding:4px 6px;border:1px solid #ccc;border-radius:3px;font-size:11px;margin-bottom:3px';
        const nfo=mk('div',''); nfo.style.cssText='font-size:10px;color:#17a2b8;margin-bottom:5px;min-height:13px';
        urlI.addEventListener('input',()=>{const v=urlI.value.trim();nfo.textContent=v?s('iaUrlInfo')+' '+parseURL(v):''});
        const lblI=mk('input'); lblI.placeholder=s('iaLblPlh'); lblI.style.cssText='width:100%;box-sizing:border-box;padding:4px 6px;border:1px solid #ccc;border-radius:3px;font-size:11px;margin-bottom:6px';
        const tRow=mk('div'); tRow.style.cssText='display:flex;align-items:center;gap:6px;margin-bottom:6px';
        const tSel=mk('select'); tSel.style.cssText='flex:1;padding:3px;border:1px solid #ccc;border-radius:3px;font-size:11px';
        IA_TYPES.forEach(tp=>{const o=mk('option','',tp);o.value=tp;tSel.appendChild(o);}); tSel.value=active;
        tRow.append(mk('span','','ID Tipi:'),tSel);
        form.append(urlI,nfo,lblI,tRow);
        form.appendChild(mk('div','tvis-sec','Simge'));
        const getSel=mkIconPicker(form,'🔗');
        form.appendChild(mkB(s('iaSave'),'btn-sm btn-g',()=>{
            const url=urlI.value.trim(),lbl=lblI.value.trim(); if(!url||!lbl) return;
            const c=getIA(active); c.push({id:'c'+Date.now(),icon:getSel(),label:lbl,path:parseURL(url),idType:tSel.value});
            saveIA(active,c); urlI.value=''; lblI.value=''; renderSec();
        }));
        secCont.appendChild(form);
    };
    Object.entries(IA_LABELS).forEach(([sec,lbl])=>{
        const b=mkB(lbl,sec===active?'btn-sm btn-v':'btn-sm btn-grey',()=>{active=sec;Object.entries(tBtns).forEach(([k,b])=>b.className=k===sec?'btn-sm btn-v':'btn-sm btn-grey');renderSec();});
        tBtns[sec]=b; tabs.appendChild(b);
    });
    renderSec();
});

// IA EXCLUSION LIST MODAL
const openIAExclModal = () => mkModal(s('iaExclTitle'), cont => {
    const render = () => {
        cont.innerHTML = '';
        const list = getIAExcl();

        if (!list.length) {
            const em = mk('div', '', s('iaExclEmpty'));
            em.style.cssText = 'color:#999;font-size:12px;margin-bottom:10px';
            cont.appendChild(em);
        } else {
            list.forEach((entry, idx) => {
                const row = mk('div', 'tvis-iarow');
                const idSp   = mk('span', '', entry.id);  idSp.style.cssText = 'font-weight:bold;flex-shrink:0;min-width:50px';
                const nameSp = mk('span', '', entry.name || '—'); nameSp.style.cssText = 'flex:1;color:#555';
                const typeSp = mk('span', '', entry.type || 'character'); typeSp.style.cssText = 'font-size:10px;color:#aaa;flex-shrink:0';
                const delBtn = mkB(s('iaExclDel'), 'btn-sm btn-r', () => {
                    const cur = getIAExcl();
                    cur.splice(idx, 1);
                    saveIAExcl(cur);
                    render();
                });
                row.append(idSp, nameSp, typeSp, delBtn);
                cont.appendChild(row);
            });
        }

        // "Add Myself" button
        const own = getOwnCharInfo();
        const addMeBtn = mkB(s('iaExclAddMe'), 'btn-sm btn-g', () => {
            if (!own) { alert(s('iaExclNoId')); return; }
            const cur = getIAExcl();
            if (cur.some(e => String(e.id) === String(own.id))) return;
            cur.push({ id: own.id, name: own.name, type: 'character' });
            saveIAExcl(cur);
            render();
        });
        if (!own) { addMeBtn.disabled = true; addMeBtn.title = s('iaExclNoId'); }
        addMeBtn.style.marginBottom = '10px';
        cont.appendChild(addMeBtn);

        // Add form
        const form = mk('div'); form.style.cssText = 'border-top:1px solid #eee;padding-top:8px;margin-top:4px';
        form.appendChild(mk('div', 'tvis-sec', s('iaExclAdd')));

        const row = mk('div'); row.style.cssText = 'display:flex;gap:5px;flex-wrap:wrap;align-items:center;margin-bottom:6px';
        const idI   = mk('input'); idI.placeholder = s('iaExclIdPlh');
        idI.style.cssText = 'width:80px;padding:3px 6px;border:1px solid #ccc;border-radius:3px;font-size:11px';
        idI.type = 'number'; idI.min = '1';
        const nameI = mk('input'); nameI.placeholder = s('iaExclNamePlh');
        nameI.style.cssText = 'flex:1;min-width:90px;padding:3px 6px;border:1px solid #ccc;border-radius:3px;font-size:11px';
        const typeSel = mk('select'); typeSel.style.cssText = 'padding:3px 5px;border:1px solid #ccc;border-radius:3px;font-size:11px';
        ['character','locale','artist','city'].forEach(t => {
            const o = mk('option', '', t); o.value = t; typeSel.appendChild(o);
        });
        row.append(idI, nameI, typeSel);
        form.appendChild(row);
        form.appendChild(mkB(s('iaExclAdd'), 'btn-sm btn-g', () => {
            const id = idI.value.trim(); if (!id) return;
            const cur = getIAExcl();
            if (cur.some(e => String(e.id) === id)) return;
            cur.push({ id, name: nameI.value.trim(), type: typeSel.value });
            saveIAExcl(cur);
            idI.value = ''; nameI.value = '';
            render();
        }));
        cont.appendChild(form);
    };
    render();
});

const applyInlineActions = () => {
    if (!isOnDef(K.ia, true)) return;
    const clip=(id,name,type)=>{ navigator.clipboard?.writeText(id); addClip(id,name,type); };
    const mkBtn=(a,id,name,sec)=>{
        if (a.idType==='copyid'){const b=mk('button','tvis-ia',a.icon);b.title=a.label;b.onclick=e=>{e.preventDefault();e.stopPropagation();clip(id,name,sec);};return b;}
        const el=mk('a','tvis-ia',a.icon); el.title=a.label; el.href=a.path+id; return el;
    };

    // Build exclusion set once for this pass
    const _excl = getIAExcl();
    const _exclIds = new Set(_excl.map(e => String(e.id)));

const SELS = {
        character:   {sel:'a[href*="/Character/"]',               r:/\/Character\/(\d+)(?:[/?#]|$)/},
        locale:      {sel:'a[href*="/Locale/"]',                  r:/\/Locale\/(\d+)(?:[/?#]|$)/},
        artist:      {sel:'a[href*="/Artist/"]',                   r:/\/Artist\/(\d+)(?:[/?#]|$)/},
        city:        {sel:'a[href*="/City/"]',                     r:/\/City\/(\d+)(?:[/?#]|$)/},
    };

    Object.entries(SELS).forEach(([sec,cfg])=>{
        const acts=getIA(sec);
        document.querySelectorAll(cfg.sel).forEach(link=>{
            //Quick Links Exclusion - Buralarda Quick links çalışmaz
            if(link.dataset.tvisIa||link.closest('#tvis-bar,#tvip-bar,.tvis-ov,.tvip-ov,.tvis-chpop,#character-tools-shortcuts')) return;
            const m=link.href?.match(cfg.r); if(!m) return;
            // ID-based exclusion list check
            if (_exclIds.has(m[1])) return;
            link.dataset.tvisIa='1';
            const span=mk('span','tvis-ia-wrap');
            acts.forEach(a=>span.appendChild(mkBtn(a,m[1],link.textContent.trim(),sec)));
            link.insertAdjacentElement('afterend',span);
        });
    });
};
// INTERACT CUSTOM DATA EDITOR
const openInteractDataEdit = () => mkModal(s('interactDataEdit'), cont => {
    const getCustom  = () => gmGet(DK.INTERACT_CUSTOM, {}) || {};
    const saveCustom = d  => gmSet(DK.INTERACT_CUSTOM, d);

    const render = () => {
        cont.innerHTML = '';
        const custom = getCustom();
        const keys   = Object.keys(custom);

        if (!keys.length) {
            const em = mk('div','',s('interactDataEmpty'));
            em.style.cssText = 'color:#999;font-size:12px;margin-bottom:10px';
            cont.appendChild(em);
        } else {
            const tbl = mk('table','tvis-ih-guide-tbl'); tbl.style.marginBottom='10px';
            const thead = mk('thead');
            const hr = mk('tr');
            ['ID', s('interactType'), s('interactColJoy'), s('interactColLove'), s('interactColHate'), s('interactColNote'), ''].forEach(h => {
                const th = mk('th','',h); hr.appendChild(th);
            });
            thead.appendChild(hr); tbl.appendChild(thead);
            const tbody = mk('tbody');
            keys.sort((a,b)=>Number(a)-Number(b)).forEach(k => {
                const d  = custom[k];
                const tr = mk('tr');
                const subBadge = d.sub ? `<span class="tvis-ih-badge tvis-ih-badge-${d.sub?.[0]}">${d.sub}</span>` : '—';
                [
                    `<b>${k}</b>`,
                    subBadge,
                    d.joy  ? `<span class="tvis-ih-joy">${d.joy}</span>`  : '—',
                    d.love ? `<span class="tvis-ih-love">${d.love}</span>`: '—',
                    d.hate ? `<span class="tvis-ih-hate">${d.hate}</span>`: '—',
                    d.note ? `<span class="tvis-ih-note">${d.note}</span>`: '—',
                ].forEach(html => { const td=mk('td'); td.innerHTML=html; tr.appendChild(td); });
                const delTd = mk('td');
                delTd.appendChild(mkB('🗑','btn-sm btn-r',()=>{
                    const c=getCustom(); delete c[k]; saveCustom(c); render();
                }));
                tr.appendChild(delTd);
                tbody.appendChild(tr);
            });
            tbl.appendChild(tbody);
            cont.appendChild(tbl);
        }

        // ADD FORM
        const form = mk('div'); form.style.cssText='border-top:1px solid #eee;padding-top:8px';
        const row1 = mk('div'); row1.style.cssText='display:flex;gap:5px;flex-wrap:wrap;align-items:center;margin-bottom:6px';
        const inp = (ph, w) => { const i=mk('input'); i.placeholder=ph; i.style.cssText=`width:${w};padding:3px 6px;border:1px solid #ccc;border-radius:3px;font-size:11px`; return i; };

        const idI   = inp(s('interactDataId'), '64px');  idI.type='number'; idI.min='1';
        const subS  = mk('select'); subS.style.cssText='padding:3px 5px;border:1px solid #ccc;border-radius:3px;font-size:11px';
        ['arkadaş','romantik','nefret'].forEach(v=>{ const o=mk('option','',v); o.value=v; subS.appendChild(o); });
        const joyI  = inp('😊 Joy',  '72px');
        const lovI  = inp('❤️ Love', '72px');
        const hatI  = inp('😡 Hate', '72px');
        const notI  = inp(s('interactColNote'), '120px');
        const dupW  = mk('span',''); dupW.style.cssText='color:#e67e22;font-size:11px;display:none'; dupW.textContent=s('interactDataDup');

        row1.append(idI, subS, joyI, lovI, hatI, notI);
        const addBtn = mkB(s('interactDataAdd'), 'btn-sm btn-g', () => {
            const id = idI.value.trim(); if (!id) return;
            const c  = getCustom();
            if (c[id]) { dupW.style.display=''; return; }
            dupW.style.display='none';
            c[id] = {
                sub:  subS.value,
                joy:  joyI.value.trim() || null,
                love: lovI.value.trim() || null,
                hate: hatI.value.trim() || null,
                note: notI.value.trim() || null,
            };
            saveCustom(c);
            idI.value=''; joyI.value=''; lovI.value=''; hatI.value=''; notI.value='';
            render();
        });
        const delAll = mkB(s('interactDataReset'), 'btn-sm btn-r', () => {
            if (!confirm(s('interactDataResetQ'))) return;
            saveCustom({}); render();
        });
        delAll.style.marginLeft='6px';

        form.append(row1, dupW, addBtn, delAll);
        cont.appendChild(form);
    };
    render();
});

// INTERACT HELPER
const applyInteractHelper = () => {
    if (!isOnDef(K.interact, true)) return;
    if (!location.href.includes('/Interact/')) return;
    if (location.hash.includes('ppmCall')) return;

    const sel = document.getElementById('ctl00_cphTopColumn_ctl00_ddlInteractionTypes');

    // Karakter ID'sini URL'den al
    const charIdMatch = location.href.match(/\/Interact\/(?:Details\/)?(\d+)/);
    const charId = charIdMatch ? charIdMatch[1] : '0';

    // Mevcut tipi yükle
    let curType = gmGet(DK.INTERACT_TYPE + charId, 'standart');

    // Seçeneğe ikon/değer yaz
    const decorateOptions = () => {
        if (!sel) return;
        [...sel.options].forEach(opt => {
            if (!opt.value || opt.value === '0') return;
            const id   = parseInt(opt.value);
            const data = getInteractData()[id];
            if (!data) return;
            const parts = [];
            if (data.joy)  parts.push(`😊${data.joy}`);
            if (data.love) parts.push(`❤️${data.love}`);
            if (data.hate) parts.push(`😡${data.hate}`);
            const baseName = opt.dataset.ihBase || opt.textContent;
            opt.dataset.ihBase = baseName;
            opt.textContent = parts.length ? `${baseName}  (${parts.join('  ')})` : baseName;
            opt.dataset.ihSub = data.sub;
        });
    };
// Filtreye göre seçenekleri gizle/göster
    const applyFilter = (type) => {
        curType = type;
        gmSet(DK.INTERACT_TYPE + charId, type);

        if (sel) {
            const interactData = getInteractData();
            [...sel.options].forEach(opt => {
                if (!opt.value || opt.value === '0') { opt.style.display = ''; return; }

                if (type === 'standart') {
                    opt.style.display = '';
                    return;
                }

                const id = parseInt(opt.value);
                const data = interactData[id];

                if (!data) {
                    opt.style.display = 'none';
                    return;
                }

                const j = data.joy || '';
                const l = data.love || '';
                const h = data.hate || '';
                const s = data.sub || '';

                let isMatch = false;

                if (s === type) {
                    // Kullanıcının "arkadaş", "romantik", "nefret" olarak eklediklerini korur
                    isMatch = true;
                } else if (type === 'arkadaş') {
                    if (j.includes('+') && !l.includes('+') && !h.includes('+')) isMatch = true;
                } else if (type === 'romantik') {
                    if (l.includes('+') || s === 'cinsel') isMatch = true;
                } else if (type === 'nefret') {
                    if (h.includes('+') || (j.includes('-') && !j.includes('+') && !l.includes('+'))) isMatch = true;
                }

                opt.style.display = isMatch ? '' : 'none';
            });

            // Varsayılan seçenekleri sırayla dene
            const defIds = INTERACT_DEFAULTS[type] || [];
            let selected = false;
            for (const defId of defIds) {
                const defOpt = [...sel.options].find(o => o.value === String(defId) && o.style.display !== 'none');
                if (defOpt) { sel.value = defOpt.value; selected = true; break; }
            }
            if (!selected) {
                const first = [...sel.options].find(o => o.value !== '0' && o.style.display !== 'none');
                if (first) sel.value = first.value;
            }
        }

        // Buton stillerini güncelle
        document.querySelectorAll('.tvis-ih-typebtn').forEach(b => {
            b.classList.toggle('active', b.dataset.type === type);
        });
    };
    // Kıskançlık dropdown'unu bul ve varsayılanı ayarla
    const jealousSel = document.getElementById('ctl00_cphTopColumn_ctl00_ddlSexCausesJealousy');
    if (jealousSel) jealousSel.value = '0'; // Kıskanmam = default

    // Romantizm seviyesini sayfadan oku
    const getLove = () => {
        const rows = document.querySelectorAll('table.width100 tr');
        for (const row of rows) {
            const label = row.querySelector('td.nowrap');
            if (!label) continue;
            const txt = label.textContent.trim().toLowerCase();
            if (txt.includes('aşk') || txt.includes('love') || txt.includes('amor')) {
                const sk = row.querySelector('.sortkey');
                if (sk) return parseInt(sk.textContent.trim()) || 0;
            }
        }
        return -1;
    };

// UI oluştur
    const buildUI = () => {
        if (document.getElementById('tvis-ih-wrap')) return;
        const wrap = mk('div',''); wrap.id = 'tvis-ih-wrap';

        // ROW 1: Arkadaş / Romantik / Nefret
        const barTop = mk('div','tvis-ih-bar'); barTop.style.marginBottom='3px';
        [
            { key:'arkadaş',  label: s('interactFriend')   },
            { key:'romantik', label: s('interactRomantic') },
            { key:'nefret',   label: s('interactHate')     },
        ].forEach(({ key, label }) => {
            const cls = `tvis-ih-typebtn t-${key}${key === curType ? ' active' : ''}`;
            const btn = mk('button', cls, label);
            btn.dataset.type = key; btn.type = 'button';
            btn.onclick = () => applyFilter(key);
            barTop.appendChild(btn);
        });
        wrap.appendChild(barTop);

        // ROW 2: Standart · 📋 Listele/Düzenle
        const barBot = mk('div','tvis-ih-bar');
        const stdBtn = mk('button', `tvis-ih-typebtn${curType === 'standart' ? ' active' : ''}`, s('interactAll'));
        stdBtn.dataset.type = 'standart'; stdBtn.type = 'button';
        stdBtn.onclick = () => applyFilter('standart');
        barBot.appendChild(stdBtn);
        const guideBtn = mk('button','btn-sm btn-grey','📋 Listele / Düzenle');
        guideBtn.style.marginLeft='auto'; guideBtn.type='button';
        guideBtn.onclick = () => openInteractGuide();
        barBot.appendChild(guideBtn);
        wrap.appendChild(barBot);

        // Kıskançlık uyarısı
        if (jealousSel && jealousSel.value === '1') {
            const loveVal = getLove();
            if (loveVal >= 70) wrap.appendChild(mk('div','tvis-ih-warn', s('interactWarn')));
        }

        // Dropdown mevcut ise onun ALTINA ekle (dropdown üstte, butonlar altta)
        if (sel) {
            sel.insertAdjacentElement('afterend', wrap);
        } else {
            // Seçenek yoksa (ilgilenme hakkı bitti) sayfada uygun bir yere yerleştir
            const anchor = document.querySelector(
                '#ctl00_cphTopColumn_ctl00_pnlInteract, .interaction-form, form[action*="Interact"], .box, #ppm-content'
            );
            if (anchor) anchor.insertAdjacentElement('afterbegin', wrap);
            else document.body.appendChild(wrap);
        }
    };

    // Rehber modal (İlgilen Verilerini Listele / Düzenle)
    const openInteractGuide = () => {
        mkModal('İlgilen Verilerini Listele / Düzenle', cont => {
            cont.style.cssText = 'font-size:13px;padding-right:20px';
            cont.parentElement.style.overflowX = 'auto';
            cont.parentElement.style.width = 'auto';
            cont.parentElement.style.maxWidth = 'min(98vw,960px)';

            const pageMap = {};
            if (sel) {
                [...sel.options].forEach(opt => {
                    if (!opt.value || opt.value === '0') return;
                    pageMap[parseInt(opt.value)] = opt.dataset.ihBase || opt.textContent.split('  (')[0];
                });
            }

            const renderGuide = () => {
                cont.innerHTML = '';
                const allData = getInteractData();
                const custom  = gmGet(DK.INTERACT_CUSTOM, {}) || {};
                const allIds  = [...new Set([...Object.keys(allData).map(Number), ...Object.keys(pageMap).map(Number)])].sort((a,b)=>a-b);

                const tbl = mk('table','tvis-ih-guide-tbl'); tbl.style.cssText='width:100%;table-layout:auto';
                const thead = mk('thead'); const hrow = mk('tr');
                ['ID', s('interactName'), s('interactType'), '😊', '❤️', '😡', s('interactColNote'), '✓', ''].forEach(h => {
                    thead.appendChild(hrow); hrow.appendChild(mk('th','',h));
                });
                thead.appendChild(hrow); tbl.appendChild(thead);

                const tbody = mk('tbody');
                allIds.forEach(id => {
                    const data     = allData[id] || {};
                    const isCustom = !!custom[id];
                    const onPage   = !!pageMap[id];
                    const name     = pageMap[id] || `ID ${id}`;
                    const tr       = mk('tr');
                    if (!onPage) tr.style.opacity = '0.45';
                    if (isCustom) tr.style.background = '#f0fff4';
                    const subBadge = data.sub
                        ? `<span class="tvis-ih-badge tvis-ih-badge-${data.sub?.[0]}">${data.sub}</span>`
                        : '<span class="tvis-ih-note">—</span>';

                    [
                        `<span style="color:#aaa;font-size:10px">${id}</span>`,
                        `<span>${name}</span>`,
                        subBadge,
                        data.joy  ? `<span class="tvis-ih-joy">${data.joy}</span>`   : '—',
                        data.love ? `<span class="tvis-ih-love">${data.love}</span>` : '—',
                        data.hate ? `<span class="tvis-ih-hate">${data.hate}</span>` : '—',
                        data.note ? `<span class="tvis-ih-note">${data.note}</span>` : '—',
                        onPage ? `<span style="color:#28a745;font-weight:700">✓</span>` : '',
                    ].forEach(html => { const td=mk('td'); td.innerHTML=html; tr.appendChild(td); });

                    // Action cell — edit/delete for custom entries
                    const actTd = mk('td'); actTd.style.whiteSpace='nowrap';
                    if (isCustom) {
                        const eBtn = mkB('✏','btn-sm btn-grey', () => {
                            if (document.getElementById('tvis-ih-editrow')) document.getElementById('tvis-ih-editrow').remove();
                            const editRow = mk('tr'); editRow.id='tvis-ih-editrow'; editRow.style.background='#fffde7';
                            const eTd = mk('td'); eTd.colSpan=9; eTd.style.padding='4px 6px';
                            const ef = mk('div'); ef.style.cssText='display:flex;gap:4px;align-items:center;flex-wrap:wrap';
                            const idL = mk('span','',`ID: ${id}`); idL.style.cssText='font-size:10px;color:#aaa;font-weight:600;min-width:40px';
                            const subS=mk('select'); subS.style.cssText='padding:2px 4px;border:1px solid #ccc;border-radius:3px;font-size:11px';
                            ['arkadaş','romantik','nefret'].forEach(v=>{const o=mk('option','',v);o.value=v;if(data.sub===v)o.selected=true;subS.appendChild(o);});
                            const fi=(ph,val,w)=>{const i=mk('input');i.placeholder=ph;i.value=val||'';i.style.cssText=`width:${w};padding:2px 5px;border:1px solid #ccc;border-radius:3px;font-size:11px`;return i;};
                            const jI=fi('😊',data.joy,'64px'), lI=fi('❤️',data.love,'64px'), hI=fi('😡',data.hate,'64px'), nI=fi('Not',data.note,'110px');
                            const sv=mkB('✔','btn-sm btn-g',()=>{
                                const c=gmGet(DK.INTERACT_CUSTOM,{})||{};
                                c[id]={sub:subS.value,joy:jI.value.trim()||null,love:lI.value.trim()||null,hate:hI.value.trim()||null,note:nI.value.trim()||null};
                                gmSet(DK.INTERACT_CUSTOM,c); if(sel)decorateOptions(); renderGuide();
                            });
                            const cn=mkB('✕','btn-sm btn-grey',()=>editRow.remove());
                            ef.append(idL,subS,jI,lI,hI,nI,sv,cn);
                            eTd.appendChild(ef); editRow.appendChild(eTd);
                            tr.insertAdjacentElement('afterend',editRow);
                        });
                        const dBtn=mkB('🗑','btn-sm btn-r',()=>{
                        if(!confirm(s('deleteEntry')))return;
                            const c=gmGet(DK.INTERACT_CUSTOM,{})||{}; delete c[id]; gmSet(DK.INTERACT_CUSTOM,c);
                            if(sel)decorateOptions(); renderGuide();
                        });
                        actTd.append(eBtn, dBtn);
                    }
                    tr.appendChild(actTd);
                    tbody.appendChild(tr);
                });

                // INLINE ADD ROW
                const addRow = mk('tr'); addRow.style.background='#f8f0ff';
                const addTd = mk('td'); addTd.colSpan=9;
                addTd.style.cssText='padding:8px 6px;border-top:2px solid #c9b8f0';

                const wip = mk('div'); wip.style.cssText='font-size:9px;color:#e67e22;margin-bottom:5px';
                wip.textContent=s('interactDataWip');

                const af = mk('div'); af.style.cssText='display:flex;gap:4px;align-items:center;flex-wrap:wrap';
                const fi2=(ph,w,tp='text')=>{const i=mk('input');i.placeholder=ph;i.type=tp;if(tp==='number'){i.min='1';i.max='9999';}i.style.cssText=`width:${w};padding:2px 5px;border:1px solid #c9b8f0;border-radius:3px;font-size:11px`;return i;};
                const nId=fi2('ID','52px','number');
                const nSub=mk('select'); nSub.style.cssText='padding:2px 4px;border:1px solid #c9b8f0;border-radius:3px;font-size:11px';
                ['arkadaş','romantik','nefret'].forEach(v=>{const o=mk('option','',v);o.value=v;nSub.appendChild(o);});
                const nJoy=fi2('😊 Joy','64px'), nLov=fi2('❤️ Love','64px'), nHat=fi2('😡 Hate','64px'), nNot=fi2('Koşul/Not','110px');
                const dupW=mk('span',''); dupW.style.cssText='color:#e67e22;font-size:10px;display:none'; dupW.textContent=s('interactDataDup');

                const aBtn=mkB('+ Ekle','btn-sm btn-v',()=>{
                    const idv=nId.value.trim(); if(!idv)return;
                    const c=gmGet(DK.INTERACT_CUSTOM,{})||{};
                    if(c[idv]){dupW.style.display='';return;}
                    dupW.style.display='none';
                    c[idv]={sub:nSub.value,joy:nJoy.value.trim()||null,love:nLov.value.trim()||null,hate:nHat.value.trim()||null,note:nNot.value.trim()||null};
                    gmSet(DK.INTERACT_CUSTOM,c);
                    if(sel)decorateOptions();
                    nId.value=''; nJoy.value=''; nLov.value=''; nHat.value=''; nNot.value='';
                    renderGuide();
                    setTimeout(()=>cont.scrollTo({top:cont.scrollHeight,behavior:'smooth'}),50);
                });
                af.append(nId,nSub,nJoy,nLov,nHat,nNot,aBtn,dupW);
                addTd.append(wip,af); addRow.appendChild(addTd);
                tbody.appendChild(addRow);
                tbl.appendChild(tbody);
                cont.appendChild(tbl);

                const leg=mk('div'); leg.style.cssText='font-size:10px;color:#aaa;margin-top:6px';
                leg.textContent=s('interactDataLegend');
                cont.appendChild(leg);
            };
            renderGuide();
        });
    };

    // Başlat
    decorateOptions();
    buildUI();
    applyFilter(curType);
};

// CHARACTER NOTE
const applyCharNote = () => {
    if (!isOnDef(K.note, true)) return;
    const m=location.href.match(/\/Character\/(\d+)/); if (!m) return;
    const charBox=document.querySelector('.charPresBox'); if(!charBox||document.getElementById('tvis-notebar')) return;
    const allNotes=gmGet(DK.NOTES,{})||{};
    const bar=mk('div','tvis-notebar'); bar.id='tvis-notebar';
    const ta=mk('textarea'); ta.placeholder=s('notePlh'); ta.value=allNotes[m[1]]||'';
    const btn=mkB('💾','btn-g btn-sm',()=>{
        const notes=gmGet(DK.NOTES,{})||{}; notes[m[1]]=ta.value; gmSet(DK.NOTES,notes);
        btn.textContent='✅'; setTimeout(()=>btn.textContent='💾',1500);
    });
    bar.append(mk('span','','📝 '),ta,btn);
    charBox.insertAdjacentElement('afterend',bar);
};

// DIARY FILTER — searches all entries across all days
const applyDiaryFilter = () => {
    if (!isOnDef(K.diary, true)||!location.href.includes('/Character/Diary')) return;
    const nav=document.querySelector('[id$="_ddlNavigate"]')?.closest('div.box'); if(!nav||document.getElementById('tvis-df')) return;
    const wrap=mk('div'); wrap.id='tvis-df'; wrap.style.marginBottom='10px';
    wrap.innerHTML=`<h2>${s('dfTitle')}</h2><div style="display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin-top:6px"><input id="tvis-df-i" placeholder="${s('dfPlh')}" style="padding:5px 8px;border:1px solid #ccc;border-radius:4px;font-size:12px;width:220px"><button id="tvis-df-c" class="btn-grey btn-sm">${s('dfClear')}</button><span id="tvis-df-n" style="font-size:11px;color:#666"></span></div>`;
    nav.insertAdjacentElement('beforebegin',wrap);
    const inp=document.getElementById('tvis-df-i'), info=document.getElementById('tvis-df-n');

    const allDays = () => document.querySelectorAll('#ppm-content .diaryExtraspace > li');
    const allEntries = () => document.querySelectorAll('#ppm-content .diaryExtraspace > li > ul > li');

    const filter=()=>{
        const q=inp.value.trim().toLowerCase();
        let cnt=0, total=0;
        allDays().forEach(day=>{
            let vis=false;
            day.querySelectorAll(':scope > ul > li').forEach(e=>{
                total++;
                const ok=!q||e.textContent.toLowerCase().includes(q);
                e.style.display=ok?'':'none';
                if(ok){vis=true;cnt++;}
            });
            day.style.display=(!q||vis)?'':'none';
            // show/hide day header (first non-ul element)
            [...day.children].forEach(ch=>{ if(ch.tagName!=='UL') ch.style.display=(!q||vis)?'':'none'; });
        });
        if(q){
            info.textContent=`${cnt} / ${total} ${s('dfCount')}`;
        } else {
            info.textContent=`${total} ${s('dfTotal')}`;
        }
    };

    document.getElementById('tvis-df-c').onclick=()=>{
        inp.value='';
        allDays().forEach(day=>{
            day.style.display='';
            [...day.children].forEach(ch=>ch.style.display='');
        });
        allEntries().forEach(e=>e.style.display='');
        filter(); // recalculate total
    };
    inp.addEventListener('input',filter);
    inp.addEventListener('keyup',e=>{if(e.key==='Enter')filter();});
    filter(); // show total on load
};
// ICON MENU
const openCustomIconsModal = () => mkModal(s('customIconTitle'), cont => {
    const renderCustom = () => {
        chipArea.innerHTML='';
        getCustomIcons().forEach((ico,i) => {
            const chip=mk('span','tvis-custom-chip',ico);
            const del=mk('button','','×'); del.type='button'; del.title='Sil';
            del.onclick=()=>{ const c=getCustomIcons(); c.splice(i,1); gmSet(DK.CUSTOM_ICONS,c); renderCustom(); };
            chip.appendChild(del); chipArea.appendChild(chip);
        });
    };
    const chipArea = mk('div','tvis-custom-icons'); cont.appendChild(chipArea);
    renderCustom();

    const icoInput=mk('input'); icoInput.placeholder=s('customIconPlh');
    icoInput.style.cssText='width:100%;box-sizing:border-box;padding:3px 6px;border:1px solid #ccc;border-radius:3px;font-size:11px;margin:6px 0 3px';
    cont.appendChild(icoInput);

    const btnRow = mk('div'); btnRow.style.cssText='display:flex;flex-wrap:wrap;gap:4px;margin-bottom:8px';
    btnRow.append(
        mkB(s('addIcon'),'btn-sm btn-v',()=>{
            const val=icoInput.value.trim(); if(!val) return;
            const c=getCustomIcons();
            [...val].forEach(ico=>{ if(ico.trim()&&!c.includes(ico)) c.push(ico); });
            gmSet(DK.CUSTOM_ICONS,c); icoInput.value=''; renderCustom();
        }),
        mkB(s('delAllIcons'),'btn-sm btn-r',()=>{
            if(!confirm(s('delAllIconsConfirm'))) return;
            gmSet(DK.CUSTOM_ICONS,[]); renderCustom();
        }),
        mkB(s('exportIcons'),'btn-sm btn-b',()=>{
            const icons=getCustomIcons();
            if(!icons.length){ showToast(s('ciNoIcons')); return; }
            navigator.clipboard?.writeText(icons.join('')).then(()=>showToast(s('ciCopied')));
        })
    );
    cont.appendChild(btnRow);

    const geteEl=mk('a','',s('emojiRef'));
    geteEl.href='https://getemoji.com/'; geteEl.target='_blank';
    geteEl.style.cssText='font-size:10px;color:#17a2b8;display:block;text-decoration:none';
    cont.appendChild(geteEl);
//ShareMD-Emoji deposu
        const archiveEl=mk('a','','🗂️ Emoji Archive');
    archiveEl.href='https://share-md.com/view?id=3dc5b188-c715-4829-9270-59be829d2de3';
    archiveEl.target='_blank';
    archiveEl.style.cssText='font-size:10px;color:#17a2b8;display:block;text-decoration:none;margin-top:2px';
    cont.appendChild(archiveEl);
});
// MODÜL: Para Giriş Formatlayıcı
// Etiket: applyMoneyFormatter
// Sayfalara uygula: her sayfada çalışır, input yoksa sessizce geçer
const applyMoneyFormatter = () => {
    if (!isOnDef(K.moneyFmt, true)) return;
    // Para input'larını tanımlayan seçiciler — genişletilebilir
    const SEL = [
        'input[id*="txtPriceTag"]',
        'input[id*="txtAmount"]',
        'input[id*="txtPrice"]',
        'input[id*="txtMoney"]',
        'input[id*="txtCash"]',
        'input[id*="txtGive"]',
        'input[id*="txtPT"]',
        'input[id*="txtWithdrawAmount"]',
        'input[id*="txtDepositAmount"]',
    ].join(',');

    const fmt = v => v.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
    const raw = v => v.replace(/\./g, '');

    const applyTo = inp => {
        if (inp.dataset.mfmt) return;
        inp.dataset.mfmt = '1';

        // Cursor pozisyonunu koruyarak formatla
        inp.addEventListener('input', () => {
            const start   = inp.selectionStart;
            const oldLen  = inp.value.length;
            const digits  = raw(inp.value).replace(/\D/g, '');
            const newVal  = fmt(digits);
            inp.value     = newVal;
            const shift   = newVal.length - oldLen;
            inp.setSelectionRange(start + shift, start + shift);
        });

        // Form gönderilmeden önce noktaları temizle
        const strip = () => { inp.value = raw(inp.value); };
        inp.closest('form')?.addEventListener('submit', strip);

        // ASP.NET postback butonları için de temizle
        inp.closest('form')?.querySelectorAll(
            'input[type=submit], input[type=image], button[type=submit]'
        ).forEach(btn => btn.addEventListener('click', strip));

        // Mevcut değeri formatla
        const init = raw(inp.value || '').replace(/\D/g, '');
        if (init) inp.value = fmt(init);
    };

    document.querySelectorAll(SEL).forEach(applyTo);

    // ASP.NET UpdatePanel sonrası yeniden tara (Debounce eklendi)
    let moTimer;
    const obs = new MutationObserver(() => {
        clearTimeout(moTimer);
        moTimer = setTimeout(() => document.querySelectorAll(SEL).forEach(applyTo), 300);
    });
    obs.observe(document.body, { childList: true, subtree: true });
};
// MODÜL: Forum Ekleme Butonları (Raf entegreli)
const applyForumList = () => {
    if (!isOnDef(K.pins, true)) return;
    if (!location.href.includes('/Forum/') && !location.href.includes('/Thread/')) return;

    const parseId = url => url.match(/\/Thread\/(\d+)/)?.[1];

    const injectAddButtons = () => {
        document.querySelectorAll('table.data td a[href*="/Thread/"]').forEach(link => {
            if (link.dataset.frlIa) return;
            link.dataset.frlIa='1';
            const id=parseId(link.href); if(!id) return;
            const tracked=()=>isForumInRaf(link.href);
            const btn=mk('button','frl-addbtn'+(tracked()?' on':''),tracked()?'📋':'⊕');
            btn.type='button';
            btn.onclick=()=>{
                if(tracked()){openRaf();return;}
                // Show lane picker popup
                const shelf=getShelf(); const fd=shelf.folders['forum'];
                const pop=mk('div');
                pop.style.cssText='position:fixed;z-index:999999;background:#fff;border:1px solid #c9b8f0;border-radius:6px;padding:6px;box-shadow:0 4px 12px rgba(0,0,0,.2);display:flex;flex-direction:column;gap:3px;font-size:11px';
                const rect=btn.getBoundingClientRect();
                pop.style.top=(rect.bottom+4)+'px'; pop.style.left=rect.left+'px';
                fd.columns.forEach((col,ci)=>{
                    const row=mkB(`${col.name} (${col.items.length})`,'btn-sm btn-grey',()=>{
                        addForumToRaf(id,link.textContent.trim(),link.href,ci);
                        btn.textContent='📋'; btn.className='frl-addbtn on'; pop.remove();
                    });
                    row.style.cssText='width:100%;text-align:left'; pop.appendChild(row);
                });
                document.body.appendChild(pop);
                setTimeout(()=>{const close=e=>{if(!pop.contains(e.target)){pop.remove();document.removeEventListener('click',close);}};document.addEventListener('click',close);},50);
            };
            link.insertAdjacentElement('afterend',btn);
        });
    };

    injectAddButtons();
    // MutationObserver for dynamic content (Debounce eklendi)
    let moTimer;
    const obs=new MutationObserver(() => {
        clearTimeout(moTimer);
        moTimer = setTimeout(injectAddButtons, 300);
    });
    obs.observe(document.body,{childList:true,subtree:true});
    setTimeout(()=>obs.disconnect(),5000);
    return {};
};

// ────────────────────────────────────────────────────────────────────────────

// GENRE POPULARITY POPUP
const applyGenrePopup = () => {
    if (!guard(K.genrePopup)) return;
    if (!location.href.includes('/Character/SongToArtist/')) return;
    const content = document.getElementById('ppm-content') || document.querySelector('.content');
    if (!content) return;
    const h1 = content.querySelector('h1');
    if (!h1) return;
    const link = mk('a', '', s('genrePopupBtn'));
    link.href = '#';
    link.style.cssText = 'display:inline-block;margin:6px 0 10px;font-size:12px;color:#6f42c1;font-weight:bold;text-decoration:none;padding:3px 10px;background:#f5f0ff;border:1px solid #c9b8f0;border-radius:4px';
    link.onclick = e => {
        e.preventDefault();
        mkModal(s('genrePopupTitle'), cont => {
            cont.style.cssText = 'padding:4px 0 0';
            const iframe = document.createElement('iframe');
            iframe.className = 'tvis-gpop-iframe';
            iframe.src = `${PM}/Charts/GenrePopularity`;
            cont.appendChild(iframe);
            // widen box after paint
            requestAnimationFrame(() => {
                const box = cont.closest('.tvis-box');
                if (box) box.style.maxWidth = 'min(95vw, 1100px)';
            });
        });
    };
    h1.insertAdjacentElement('afterend', link);
};

// SERENADE HELPER
const applySerenadeHelper = () => {
    if (!guard(K.serenadeHelper)) return;
    if (!location.href.includes('/Locale/RestaurantOrderSerenade/')) return;

    const RADIO_STATIONS = [
        { id: 3,  prefix: '[ROCK]' }, { id: 4,  prefix: '[MR]'   },
        { id: 5,  prefix: '[HM]'   }, { id: 6,  prefix: '[PUNK]' },
        { id: 7,  prefix: '[ELEC]' }, { id: 8,  prefix: '[POP]'  },
        { id: 9,  prefix: '[HH]'   }, { id: 10, prefix: '[R&B]'  },
        { id: 11, prefix: '[REG]'  }, { id: 12, prefix: '[WM]'   },
        { id: 13, prefix: '[C&W]'  }, { id: 14, prefix: '[JAZZ]' },
        { id: 15, prefix: '[BLU]'  }, { id: 16, prefix: '[CLAS]' },
        { id: 17, prefix: '[LAT]'  }, { id: 18, prefix: '[AM]'   },
        { id: 19, prefix: '[FLA]'  },
    ];

    // Next Wednesday 10:00 CET (= 09:00 UTC)
    const nextWedCET = fromMs => {
        const d = new Date(fromMs);
        const dow = d.getUTCDay(); // 0=Sun, 3=Wed
        let days = (3 - dow + 7) % 7;
        if (days === 0 && d.getUTCHours() >= 9) days = 7;
        const nw = new Date(d);
        nw.setUTCDate(d.getUTCDate() + days);
        nw.setUTCHours(9, 0, 0, 0);
        return nw.getTime();
    };
    const isCacheValid = ts => !!ts && Date.now() < nextWedCET(ts);
    const fmtDt = ms => new Date(ms).toLocaleString(dateLocale, {
        weekday:'short', month:'short', day:'numeric', hour:'2-digit', minute:'2-digit'
    });

    // Inject prefixes into serenade dropdown
    const injectPrefixes = () => {
        const ddl = document.getElementById('ctl00_cphLeftColumn_ctl00_ddlSerenade');
        if (!ddl) return;
        const cache = gmGet(DK.RADIO_CACHE, null);
        if (!cache?.tracks) return;
        [...ddl.options].forEach(opt => {
            if (!opt.value || opt.value === '0') return;
            const prefix = cache.tracks[String(opt.value)];
            if (prefix && !opt.text.startsWith('[')) opt.text = `${prefix} ${opt.text}`;
        });
    };

    // Fetch all 17 stations and build cache — sequential, radar-style
    const doFetch = (onDone) => {
        if (BGTASKS.radio.running) return;
        const total = RADIO_STATIONS.length;

        startBackgroundTask('radio', total, async (task) => {
            try {
                await waitGap(); lastFetch = Date.now();
                const baseResp = await fetch(`${PM}/Charts/Radio/`);
                const baseDoc  = new DOMParser().parseFromString(await baseResp.text(), 'text/html');
                const gf = name => baseDoc.querySelector(`[name="${name}"]`)?.value || '';
                const vs = gf('__VIEWSTATE'), vsg = gf('__VIEWSTATEGENERATOR'), ev = gf('__EVENTVALIDATION');

                const tracks = {};
                for (let i = 0; i < RADIO_STATIONS.length; i++) {
                    if (task.cancelled) break;
                    if (i > 0) await new Promise(r => setTimeout(r, 2100 + Math.random() * 2900));
                    if (task.cancelled) break;
                    task.current = i + 1;
                    saveTaskState('radio');
                    await waitGap(); lastFetch = Date.now();
                    try {
                        const { id, prefix } = RADIO_STATIONS[i];
                        const body = new URLSearchParams({
                            '__EVENTTARGET': 'ctl00$cphLeftColumn$ctl00$ddlRadioStations',
                            '__EVENTARGUMENT': '',
                            '__VIEWSTATE': vs, '__VIEWSTATEGENERATOR': vsg, '__EVENTVALIDATION': ev,
                            'ctl00$cphLeftColumn$ctl00$ddlRadioStations': String(id),
                            'ctl00$cphLeftColumn$ctl00$ddlWeeksIntoThePast': '0',
                            'ctl00$cphLeftColumn$ctl00$ddlCountries': '2',
                        });
                        const resp = await fetch(`${PM}/Charts/Radio/`, {
                            method: 'POST',
                            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                            body: body.toString(),
                        });
                        const doc = new DOMParser().parseFromString(await resp.text(), 'text/html');
                        const tid = doc.querySelector('#tablechart tbody tr:first-child td:nth-child(2) a')
                            ?.href?.match(/Track\/(\d+)/)?.[1];
                        if (tid) tracks[String(tid)] = prefix;
                    } catch { /* skip station */ }
                }
                if (!task.cancelled) gmSet(DK.RADIO_CACHE, { ts: Date.now(), tracks });
            } catch { /* base fetch failed */ }

            if (!task.cancelled) {
                injectPrefixes();
                const modal = document.getElementById('tvis-modal');
                if (modal) onDone?.();
            }
        });
    };

    // Open helper modal
    let warnState = 0;
    const openHelper = () => {
        mkModal(s('shTitle'), cont => {
            cont.style.minWidth = '320px';
            warnState = 0;

            const render = () => {
                cont.innerHTML = '';
                const cache  = gmGet(DK.RADIO_CACHE, null);
                const ddl    = document.getElementById('ctl00_cphLeftColumn_ctl00_ddlSerenade');
                const tracks = cache?.tracks || {};

                // Build matched list from serenade dropdown
                const matched = [];
                if (ddl) {
                    [...ddl.options].forEach(opt => {
                        if (!opt.value || opt.value === '0') return;
                        const prefix = tracks[String(opt.value)];
                        if (prefix) {
                            const txt = opt.text.replace(/^\[.*?\]\s*/, '');
                            matched.push({ prefix, txt });
                        }
                    });
                } else {
                    // Fallback: show all cached prefixes without labels
                    RADIO_STATIONS.forEach(({ prefix }) => {
                        const tid = Object.keys(tracks).find(k => tracks[k] === prefix);
                        if (tid) matched.push({ prefix, txt: tid });
                    });
                }

                // List
                const list = mk('div', 'tvis-sh-list');
                if (!matched.length) {
                    list.appendChild(mk('div', 'tvis-sh-empty',
                        Object.keys(tracks).length ? s('shNoneMatched') : s('shNoCache')));
                } else {
                    matched.forEach(({ prefix, txt }) => {
                        const row = mk('div', 'tvis-sh-row');
                        row.append(mk('span', 'tvis-sh-prefix', prefix), mk('span', 'tvis-sh-track', txt));
                        list.appendChild(row);
                    });
                }
                cont.appendChild(list);

                // Footer
                const footer = mk('div', 'tvis-sh-footer');
                if (cache?.ts) {
                    footer.appendChild(mk('div', '', `${s('shLastUpd')} ${fmtDt(cache.ts)}`));
                    footer.appendChild(mk('div', '', `${s('shNextUpd')} ${fmtDt(nextWedCET(cache.ts))}`));
                }

                const warnEl = mk('div', 'tvis-sh-warn');
                footer.append(warnEl);

                // Buttons
                const btnRow = mk('div'); btnRow.style.cssText = 'display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:2px';
                const updBtn = mkB(s('shUpdateBtn'), 'btn-sm btn-v', async () => {
                    if (BGTASKS.radio.running) return;
                    if (isCacheValid(cache?.ts)) {
                        if (warnState === 0) {
                            warnState = 1;
                            warnEl.textContent = s('shWarn1').replace('{date}', fmtDt(cache.ts));
                            return;
                        }
                        if (warnState === 1) {
                            warnState = 2;
                            warnEl.textContent = s('shWarn2');
                            return;
                        }
                    }
                    warnEl.textContent = '';
                    doFetch(render);
                });

                const radioLink = mk('a', '', s('shRadioLink'));
                radioLink.href = `${PM}/Charts/Radio/`;
                radioLink.style.cssText = 'font-size:11px;color:#6f42c1;text-decoration:none';
                radioLink.target = '_blank';

                btnRow.append(updBtn, radioLink);
                footer.appendChild(btnRow);
                cont.appendChild(footer);
            };

            render();
        });
    };

    // Insert button before the .box
    const box = document.querySelector('.box');
    if (box) {
        const btn = mkB(s('shBtn'), 'btn-sm btn-v', openHelper);
        btn.style.cssText = 'display:block;margin:6px 0 8px';
        box.insertAdjacentElement('beforebegin', btn);
    }

    injectPrefixes();
};

// //
// SPEED CALLING
const SC_DEFAULT_CFG = {
    interval: 3,
    phoneIds: {
        arkadaş:  [24, 171],
        romantik: [73, 74],
    }
};

const getScCfg   = ()      => Object.assign({}, SC_DEFAULT_CFG, gmGet(DK.SPEEDCALL_CFG, {}));
const getScQueue = ()      => gmGet(DK.SPEEDCALL_Q, []) || [];
const setScQueue = arr     => gmSet(DK.SPEEDCALL_Q, arr);
const getScState = ()      => gmGet(DK.SPEEDCALL_STATE, {idx:0, running:false, failStreak:0}) || {idx:0,running:false,failStreak:0};
const setScState = st      => gmSet(DK.SPEEDCALL_STATE, st);

// Build queue from saved interact types (arkadaş + romantik)
const buildScQueue = (includeTypes) => {
    const queue = [];
    const seen  = new Set();
    // Scan all interact type keys stored in GM
    // We iterate known radar chars + try to find any saved interact type
    // Build from: all tracked chars + any charId key in GM storage
    const allTracked = getAllTracked().map(e => String(e.charId));
    // Also read interact custom list if present
    const custom = gmGet(DK.INTERACT_CUSTOM, {}) || {};
    // Collect unique charIds from radar
    const candidates = [...new Set(allTracked)];
    candidates.forEach(cid => {
        const t = gmGet(DK.INTERACT_TYPE + cid, null);
        if (t && includeTypes.includes(t) && !seen.has(cid)) {
            seen.add(cid);
            // Try to get name from char cache
            const cache = gmGet(DK.CACHE, {}) || {};
            const name = cache[cid]?.name || cid;
            queue.push({ charId: cid, type: t, name });
        }
    });
    return queue;
};

// Modal: configure & start
const openSpeedCallModal = () => {
    mkModal(s('scTitle'), (cont, close) => {
        const cfg   = getScCfg();
        const queue = getScQueue();
        const state = getScState();

        // WHO TO CALL
        cont.appendChild(mk('div','tvis-sec',s('scWho')));
        const chkFriend = Object.assign(mk('input'),{type:'checkbox',checked:true,id:'sc-chk-friend'});
        const chkRom    = Object.assign(mk('input'),{type:'checkbox',checked:true,id:'sc-chk-rom'});
        const lF = mk('label','tvis-chk'); lF.append(chkFriend, mk('span','',s('scWhoFriend'))); cont.appendChild(lF);
        const lR = mk('label','tvis-chk'); lR.append(chkRom,    mk('span','',s('scWhoRom')));    cont.appendChild(lR);

        // Preview list
        const listWrap = mk('div','tvis-sc-charlist');
        const refreshList = () => {
            listWrap.innerHTML = '';
            const inc = [];
            if (chkFriend.checked) inc.push('arkadaş');
            if (chkRom.checked)    inc.push('romantik');
            const chars = buildScQueue(inc);
            if (!chars.length) {
                listWrap.appendChild(mk('div','',s('scNoChars'))).style.cssText='font-size:11px;color:#999;padding:8px';
                return;
            }
            chars.forEach(c => {
                const row = mk('div','tvis-sc-charrow');
                const chk = Object.assign(mk('input'),{type:'checkbox',checked:true});
                chk.dataset.cid  = c.charId;
                chk.dataset.type = c.type;
                chk.dataset.name = c.name;
                const badge = mk('span','tvis-sc-badge '+(c.type==='romantik'?'r':'a'), c.type==='romantik'?'❤️':'👥');
                const nm    = mk('span','',c.name);
                row.append(chk, badge, nm);
                listWrap.appendChild(row);
            });
        };
        chkFriend.onchange = refreshList;
        chkRom.onchange    = refreshList;
        refreshList();
        cont.appendChild(listWrap);

        cont.appendChild(mk('hr','tvis-hr'));

        // INTERVAL
        cont.appendChild(mk('div','tvis-sec',s('scInterval')));
        const intRow = mk('div'); intRow.style.cssText='display:flex;align-items:center;gap:8px;margin-bottom:4px';
        const slider = Object.assign(mk('input'),{type:'range',min:1,max:10,value:cfg.interval});
        slider.style.cssText='flex:1';
        const intLbl = mk('span','',cfg.interval+'s');
        intLbl.style.cssText='min-width:24px;font-weight:700;color:#6f42c1';
        slider.oninput = () => { intLbl.textContent = slider.value+'s'; };
        intRow.append(slider, intLbl);
        cont.appendChild(intRow);
        cont.appendChild(mk('div','',s('scIntervalNote'))).style.cssText='font-size:10px;color:#aaa;margin-bottom:8px';

        cont.appendChild(mk('hr','tvis-hr'));

        // PHONE IDs
        cont.appendChild(mk('div','tvis-sec',s('scPhoneIds')));
        cont.appendChild(mk('div','',s('scPhoneNote'))).style.cssText='font-size:10px;color:#aaa;margin-bottom:6px';

        const makePhoneRow = (labelKey, typeKey) => {
            const row = mk('div','tvis-sc-phonerow');
            row.appendChild(mk('span','',s(labelKey))).style.cssText='min-width:110px;flex-shrink:0;font-size:11px';
            const inp = mk('input','tvis-sc-phoneinp');
            inp.value = (cfg.phoneIds[typeKey] || []).join(', ');
            inp.dataset.sctype = typeKey;
            row.appendChild(inp);
            cont.appendChild(row);
            return inp;
        };
        const inpFriend = makePhoneRow('scFriendIds','arkadaş');
        const inpRom    = makePhoneRow('scRomIds','romantik');

        cont.appendChild(mk('hr','tvis-hr'));

        // BUTTONS
        const btnRow = mk('div'); btnRow.style.cssText='display:flex;gap:6px;flex-wrap:wrap;margin-top:4px';

        const doStart = (resume) => {
            // Save config
            const newCfg = {
                interval: parseInt(slider.value)||3,
                phoneIds: {
                    arkadaş:  inpFriend.value.split(',').map(x=>parseInt(x.trim())).filter(n=>!isNaN(n)),
                    romantik: inpRom.value.split(',').map(x=>parseInt(x.trim())).filter(n=>!isNaN(n)),
                }
            };
            gmSet(DK.SPEEDCALL_CFG, newCfg);

            if (!resume) {
                // Build queue from checked chars
                const checked = [...listWrap.querySelectorAll('input[type=checkbox]:checked')];
                if (!checked.length) return;
                const q = checked.map(c=>({charId:c.dataset.cid, type:c.dataset.type, name:c.dataset.name}));
                setScQueue(q);
                setScState({idx:0, running:true, failStreak:0});
            } else {
                const st = getScState();
                setScState({...st, running:true, failStreak:0});
            }
            close();
            const q = getScQueue();
            const st = getScState();
            const first = q[st.idx];
            if (!first) return;
            window.open(`${PM}/Interact/Phone/${first.charId}#ppmCall`, '_blank');
        };

        btnRow.appendChild(mkB(s('scStart'),'btn-g',()=>doStart(false)));
        if (queue.length && state.idx > 0 && state.idx < queue.length) {
            const resumeBtn = mkB(`${s('scResume')} (${state.idx}/${queue.length})`,'btn-v',()=>doStart(true));
            btnRow.appendChild(resumeBtn);
        }
        btnRow.appendChild(mkB(s('scReset'),'btn-grey btn-sm',()=>{
            if (!confirm(s('scReset')+'?')) return;
            gmDel(DK.SPEEDCALL_Q); gmDel(DK.SPEEDCALL_STATE);
            close();
        }));
        cont.appendChild(btnRow);
    });
};

// Injected into every Interact/Phone page when #ppmCall is in URL
const applySpeedCall = () => {
    if (!location.href.includes('/Interact/Phone/') && !location.href.includes('/Interact/Details/')) return;
    if (!location.hash.includes('ppmCall')) return;

    const state = getScState();
    if (!state.running) return;

    const queue = getScQueue();
    const cfg   = getScCfg();
    const idx   = state.idx;
    const total = queue.length;
    const current = queue[idx];
    if (!current) {
        // Done
        setScState({...state, running:false});
        injectScBar(null, idx, total, cfg, false, true);
        return;
    }

    // BAR
    let timerHandle   = null;
    let paused        = false;
    let timerLeft     = 0;

    const bar = mk('div','tvis-sc-bar');
    bar.id = 'tvis-sc-bar';

    const statEl  = mk('span','tvis-sc-stat');
    const timerEl = mk('span','tvis-sc-timer','');
    const stopBtn = mkB(s('scBarStop'),'tvis-sc-btn stop',()=>{
        paused = true;
        clearTimeout(timerHandle);
        setScState({...getScState(), running:false});
        stopBtn.style.display='none';
        resumeBtn.style.display='';
        warnEl.textContent='';
        statEl.innerHTML=`<span style="color:#ffd700">⏸ Duraklatıldı</span>`;
        timerEl.textContent='';
    });
    const resumeBtn = mkB(s('scBarResume'),'tvis-sc-btn resume',()=>{
        setScState({...getScState(), running:true, failStreak:0});
        resumeBtn.style.display='none';
        stopBtn.style.display='';
        warnEl.textContent='';
        proceedToNext();
    });
    resumeBtn.style.display='none';
    const warnEl  = mk('span','tvis-sc-warn','');
    warnEl.style.display='none';

    bar.append(statEl, timerEl, warnEl, stopBtn, resumeBtn);
    document.body.prepend(bar);
    // Push page content down
    document.body.style.paddingTop = '34px';

    const updateStat = (name, nextName) => {
        statEl.innerHTML = `📞 <b>${idx+1}${s('scBarOf')}${total}</b> &nbsp;`
            + `<span class="tvis-sc-name">${name||current.charId}</span>`
            + (nextName ? ` &nbsp;›&nbsp; ${s('scBarNext')} <span class="tvis-sc-name">${nextName}</span>` : '');
    };

    const showWarn = (msg) => {
        warnEl.textContent = msg;
        warnEl.style.display='';
    };
    const hideWarn = () => { warnEl.style.display='none'; warnEl.textContent=''; };

    const playAlarm = () => {
        try {
            const ctx = new (window.AudioContext||window.webkitAudioContext)();
            [0,300,600].forEach(delay => {
                const osc = ctx.createOscillator();
                const gain = ctx.createGain();
                osc.connect(gain); gain.connect(ctx.destination);
                osc.frequency.value = 880;
                gain.gain.setValueAtTime(0.4, ctx.currentTime + delay/1000);
                gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + delay/1000 + 0.4);
                osc.start(ctx.currentTime + delay/1000);
                osc.stop(ctx.currentTime + delay/1000 + 0.4);
            });
        } catch(e) {}
    };

    const runCountdown = (secs, onDone) => {
        timerLeft = secs;
        timerEl.textContent = timerLeft + 's';
        const tick = () => {
            timerLeft--;
            timerEl.textContent = timerLeft + 's';
            if (timerLeft <= 0) { timerEl.textContent=''; onDone(); }
            else timerHandle = setTimeout(tick, 1000);
        };
        timerHandle = setTimeout(tick, 1000);
    };

    const proceedToNext = () => {
        const nextIdx = getScState().idx + 1;
        if (nextIdx >= total) {
            setScState({...getScState(), running:false, idx:nextIdx});
            statEl.innerHTML = `✅ <b>${s('scBarDone')}</b>`;
            timerEl.textContent='';
            stopBtn.style.display='none';
            return;
        }
        setScState({...getScState(), idx:nextIdx});
        const next = queue[nextIdx];
        const interval = cfg.interval + Math.random()*2;
        const nextAfter = queue[nextIdx+1];
        updateStat(next.name, nextAfter?.name);
        runCountdown(Math.round(interval), () => {
            location.assign(`${PM}/Interact/Phone/${next.charId}#ppmCall`);
        });
    };

    const doFailStreak = () => {
        const st = getScState();
        const streak = (st.failStreak||0) + 1;
        setScState({...st, failStreak:streak});
        if (streak >= 2) {
            clearTimeout(timerHandle);
            paused = true;
            playAlarm();
            stopBtn.style.display='none';
            resumeBtn.style.display='';
            showWarn(`${s('scBarFail')} — ${s('scBarFailNote')}`);
            timerEl.textContent='';
        } else {
            proceedToNext();
        }
    };

    // ATTEMPT CALL
    const sel = document.querySelector('#ctl00_cphTopColumn_ctl00_ddlInteractionTypes')
             || document.querySelector('select[id*="ddlInteraction"]');

    const phoneIds = (cfg.phoneIds[current.type] || []).map(String);
    const btn      = document.querySelector('#ctl00_cphTopColumn_ctl00_btnInteract')
                  || document.querySelector('input[id*="btnInteract"],button[id*="btnInteract"]');

    const next1 = queue[idx+1];
    updateStat(current.name, next1?.name);

    let called = false;
    if (sel && phoneIds.length) {
        for (const pid of phoneIds) {
            const opt = [...sel.options].find(o=>o.value===pid&&o.style.display!=='none');
            if (opt) {
                sel.value = pid;
                sel.dispatchEvent(new Event('change',{bubbles:true}));
                if (btn) {
                    setTimeout(()=>{
                        btn.click();
                        called = true;
                        setScState({...getScState(), failStreak:0});
                        const interval = cfg.interval + Math.random()*2;
                        runCountdown(Math.round(interval), proceedToNext);
                    }, 400);
                }
                break;
            }
        }
    }

    if (!called) {
        if (!sel) {
            // No interact dropdown found — might be wrong page
            showWarn(s('scBarNoId'));
        } else if (!phoneIds.length) {
            showWarn(s('scBarNoId'));
        } else {
            showWarn(s('scBarNoId'));
        }
        setTimeout(() => doFailStreak(), 600);
    }
};

// SETTINGS MODAL (used by bar link and PopControl button)
function _openSocialModal(pan) {
    const existing = document.getElementById('tvis-set-ov');
    if (existing) { existing.remove(); return; }
    const ov = document.createElement('div'); ov.id = 'tvis-set-ov';
    ov.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:99997;display:flex;align-items:center;justify-content:center;padding:20px;box-sizing:border-box';
    const origStyle = pan.getAttribute('data-orig-style') || '';
    pan.style.cssText = 'display:block;position:relative;top:auto;right:auto;left:auto;max-height:85vh;overflow-y:auto;border-radius:10px;min-width:260px;max-width:500px;width:92%;';
    ov.appendChild(pan);
    ov.onclick = function(e) {
        if (e.target === ov) {
            document.body.appendChild(pan);
            pan.style.display = 'none';
            ov.remove();
        }
    };
    document.body.appendChild(ov);
}

function _openSocialSettingsModal() {
    const pan = document.getElementById('tvis-hpanel');
    if (!pan) return;
    const ov = document.getElementById('tvis-set-ov');
    if (ov) { document.body.appendChild(pan); pan.style.display = 'none'; ov.remove(); return; }
    _openSocialModal(pan);
}

// EN strings for PopControl export
// EN strings for PopControl — auto-derived from STR
window.__ppcStrSocial = Object.fromEntries(
    Object.entries(STR).map(([k, v]) => [k, v['EN'] ?? v['TR'] ?? k])
);

// ─── POPCONTROL BAĞLANTISI ────────────────────────────────────────────────────
function _connectSocial(bar, pan) {
    if (window.PPC_Social_Done) return;
    const pc = (typeof unsafeWindow !== 'undefined' && unsafeWindow.PopControl) || window.PopControl;
    if (!pc?.register) return;
    try {
        pc.register({
            id: 'social', icon: '🌐', label: 'Social',
            strings: window.__ppcStrSocial || {},
            buttons: [
                { icon: '➕', label: ({ TR: 'Ekle', EN: 'Add', PT: 'Adicionar' }[LANG] || 'Ekle'), onClick: () => quickRaf() },
                { icon: '📌', label: s('pinBtn').replace('📌 ',''), onClick: openRaf },
                { icon: '📋', label: s('menuClip').replace('📋 ',''), onClick: openClip },
                { icon: '📍', label: s('menuRadar').replace('📍 ','').slice(0,5), onClick: openTrackModal },
                { icon: '📞', label: s('scBtn').replace('📞 ','').slice(0,3), onClick: openSpeedCallModal },
                { icon: '🌐', label: ({ TR: 'Social', EN: 'Social', PT: 'Social' }[LANG] || 'Social Ayarlar'), onClick: () => _openSocialSettingsModal() },
            ],
            onUndo: () => { window.PPC_Social_Done = false; document.getElementById('tvis-set-ov')?.remove(); document.body.appendChild(pan); pan.style.display='none'; bar.style.display=''; },
        });
        bar.style.display = 'none';
        window.PPC_Social_Done = true;
        _injectSocialMenuItems();
        console.log('[Social] PopControl bağlantısı başarılı');
    } catch(e) {
        console.error('[Social] PopControl bağlantı hatası:', e);
    }
}

// MENU
const injectMenu = () => {
    if (document.getElementById('tvis-bar')) return;
    const bar = mk('div','tvis-bar'); bar.id='tvis-bar';
    const pan = mk('div','tvis-hpanel'); pan.id='tvis-hpanel';

    const posBar = () => {
        let offset = 4;
        const helperBar = document.getElementById('tvip-bar');
        if (helperBar) offset += helperBar.offsetWidth + 4;
        bar.style.right = offset + 'px';
        pan.style.right = offset + 'px';
    };

    const lnk = (txt,fn)=>{ const a=mk('a','',txt); a.href='#'; a.onclick=e=>{e.preventDefault();fn();}; return a; };

    if (isOnDef(K.pins, true)) {
        const pinW=mk('span'); pinW.style.cssText='display:inline-flex;align-items:center;gap:3px';
        const addBtn=mk('button','','+ ');
        addBtn.style.cssText='background:#6f42c1;color:#fff;border:none;border-radius:3px;cursor:pointer;font-size:10px;padding:1px 5px;font-weight:bold;line-height:1.4';
        addBtn.onclick=e=>{e.preventDefault();quickRaf();};
        pinW.append(addBtn, lnk(s('pinBtn'),openRaf));
        bar.appendChild(pinW);
    }
    bar.appendChild(lnk(s('menuClip'), openClip));
    if (isOnDef(K.tracking, true)) bar.appendChild(lnk(s('menuRadar'), openTrackModal));
    if (isOnDef(K.speedcall, true)) bar.appendChild(lnk(s('scBtn'), openSpeedCallModal));
    bar.appendChild(lnk(s('menuTitle'), _openSocialSettingsModal));

    const checks = {};
    const mkChk  = (ck,lbl,defVal=true) => { const l=mk('label','tvis-chk'); const c=Object.assign(mk('input'),{type:'checkbox',checked:isOnDef(ck,defVal)}); checks[ck]=c; l.append(c,mk('span','',lbl)); pan.appendChild(l); };
    const mkHr   = () => pan.appendChild(mk('hr','tvis-hr'));
    const mkSec  = txt => pan.appendChild(mk('div','tvis-sec',txt));

    mkHr();

    mkSec('SOSYAL');
    mkChk(K.pins,      s('pins'),           true);
    mkChk(K.charPopup, s('charCard'),       true);
    mkChk(K.tracking,  s('radar'),          true);
    mkChk(K.interact,  s('interactfilter'), true);
    mkChk(K.ia,        s('quickLinks'),     true);
    mkChk(K.note,      s('note'),           true);
    mkChk(K.speedcall, s('speedcall'),      true);
    mkHr();
    mkSec('ARAÇLAR');
    mkChk(K.diary,          s('diary'),          true);
    mkChk(K.moneyFmt,       s('moneyFmt'),       true);
    mkChk(K.genrePopup,     s('genrePopup'),     true);
    mkChk(K.serenadeHelper, s('serenadeHelper'), true);
    mkHr();

    mkSec(s('langLabel'));
    const langRow=mk('div','tvis-lang-row');
    [['TR','🇹🇷 Türkçe'],['EN','🇬🇧 English'],['PT','🇧🇷 Português']].forEach(([code,label])=>{
        const b=mk('button','tvis-lang-btn'+(LANG===code?' active':''),label);
        b.onclick=()=>{CK.set('ppm_lang',code);location.reload();};
        langRow.appendChild(b);
    });
    pan.appendChild(langRow);
    
    // Ecosystem links
    const ecosystemRow = mk('div');
    ecosystemRow.style.cssText = 'background:#f0f8ff;border-radius:8px;padding:16px;margin-bottom:12px';
    ecosystemRow.innerHTML = `
        <div style="display:flex;gap:20px;justify-content:center;align-items:center">
            <div style="display:flex;align-items:center;gap:10px">
                <span style="font-size:16px">🇹🇷</span>
                <a href="https://rentry.org/PopControlEkosistemi" target="_blank" style="color:#1a5276;font-weight:500;text-decoration:none;font-size:13px">Beni Oku</a>
            </div>
            <div style="display:flex;align-items:center;gap:10px">
                <span style="font-size:16px">🇬🇧</span>
                <a href="https://rentry.org/PopControlEcosystem" target="_blank" style="color:#1a5276;font-weight:500;text-decoration:none;font-size:13px">Read Me</a>
            </div>
            <div style="display:flex;align-items:center;gap:10px">
                <span style="font-size:16px">🇧🇷</span>
                <a href="https://rentry.org/EcossistemaPopControl" target="_blank" style="color:#1a5276;font-weight:500;text-decoration:none;font-size:13px">Leia-me</a>
            </div>
        </div>
    `;
    pan.appendChild(ecosystemRow);
    
    mkHr();

    const row1=mk('div'); row1.style.cssText='display:flex;flex-wrap:wrap;gap:4px;margin-top:4px';
    const row2=mk('div'); row2.style.cssText='display:flex;flex-wrap:wrap;gap:4px;margin-top:4px';
    const readMeBtn = mk('a','btn-b btn-sm','📖 Beni Oku');
    readMeBtn.href='https://rentry.org/SocialOku'; readMeBtn.target='_blank';
    readMeBtn.style.textDecoration='none';
    row1.append(
        mkB(s('save'),'btn-g',()=>{ Object.entries(checks).forEach(([k,c])=>CK.set(k,c.checked?'1':'0')); location.reload(); }),
        readMeBtn,
        mkB(s('backup'),'btn-b btn-sm',()=>dbExport()),
        mkB(s('restore'),'btn-grey btn-sm',()=>dbImport())
    );
    row2.append(
        mkB(s('iaEdit'),'btn-sm btn-v',()=>openIAEdit()),
        mkB(s('iaExcl'),'btn-sm btn-grey',()=>openIAExclModal()),
        mkB(s('customIconTitle'),'btn-sm btn-grey',()=>openCustomIconsModal())
    );
    pan.append(row1,row2);
    mkHr();

    document.body.append(bar, pan);

    posBar();
    const _posObs = new MutationObserver(posBar);
    _posObs.observe(document.body, { childList: true });
    setTimeout(() => _posObs.disconnect(), 3000);

    document.addEventListener('click', e => {
        if (!bar.contains(e.target) && !pan.contains(e.target)) {
            if (!document.getElementById('tvis-set-ov')) pan.style.display = 'none';
        }
    });

    // PopControl bağlantısı
    const _tryConnect = () => _connectSocial(bar, pan);
    document.addEventListener('PopControlReady', () => setTimeout(_tryConnect, 50), { once: true });
    (function _checkPC(n) {
        if (window.PPC_Social_Done) return;
        const pc = (typeof unsafeWindow !== 'undefined' && unsafeWindow.PopControl) || window.PopControl;
        if (pc?.register) _tryConnect();
        else if (n < 20) setTimeout(() => _checkPC(n + 1), 150);
    })(0);
};

// INIT
applyCharPopup();
applyForumList();
applyMoneyFormatter();
applyCharNote();
applyDiaryFilter();
applyInlineActions();
applyInteractHelper();
applyGenrePopup();
applySerenadeHelper();
applySpeedCall();
injectMenu();

// ── PUBLIC API ──────────────────────────────────────────────────────────────
window.__social_api = {
    openShelf:    () => typeof openRaf                    === 'function' && openRaf(),
    openClip:     () => typeof openClip                   === 'function' && openClip(),
    openRadar:    () => typeof openTrackModal             === 'function' && openTrackModal(),
    openSC:       () => typeof openSpeedCallModal         === 'function' && openSpeedCallModal(),
    openSettings: () => typeof _openSocialSettingsModal   === 'function' && _openSocialSettingsModal(),
    quickAdd:     () => typeof quickRaf                   === 'function' && quickRaf(),
};

// ── TOP VIP MENÜSÜ ENJEKSİYONU ──────────────────────────────────────────────
function _injectSocialMenuItems() {
    if (!location.href.includes('/World/Popmundo.aspx/Character')) return;
    const _D2 = (tr, en, pt) => ({ TR: tr, EN: en, PT: pt }[LANG] || tr);
    const socialButtons = [
        { id: 'mnu-social-add',      label: _D2('➕ Ekle',               '➕ Add',              '➕ Adicionar'),      fn: () => window.__social_api?.quickAdd()     },
        { id: 'mnu-social-shelf',    label: _D2('📌 Rafım',              '📌 Shelf',            '📌 Prateleira'),     fn: () => window.__social_api?.openShelf()    },
        { id: 'mnu-social-clip',     label: _D2('📋 Pano',               '📋 Clipboard',        '📋 Painel'),         fn: () => window.__social_api?.openClip()     },
        { id: 'mnu-social-radar',    label: _D2('📍 Radar',              '📍 Radar',            '📍 Radar'),          fn: () => window.__social_api?.openRadar()    },
        { id: 'mnu-social-sc',       label: _D2('📞 Speed Calling',      '📞 Speed Calling',    '📞 Speed Calling'),  fn: () => window.__social_api?.openSC()       },
        { id: 'mnu-social-settings', label: _D2('🌐 Social Ayarlar',    '🌐 Social Settings',  '🌐 Config Social'),  fn: () => window.__social_api?.openSettings() },
    ];

const _pc = (typeof unsafeWindow !== 'undefined' && unsafeWindow.PopControl) || window.PopControl;
if (_pc?.MenuManager) {
    _pc.MenuManager.registerMenu({id: 'top-vip', title: '⭐ TOP VIP ⭐', position: 'above-career', items: socialButtons, collapsible: true });
        return;
    }

    // ── Standalone fallback (PopControl yoksa) ──────────────────────────
    if (document.getElementById('mnu-social-shelf')) return;
    let ul = document.querySelector('#top-vip-menu ul');
    if (!ul) {
        const ref = [...document.querySelectorAll('.menu h3')]
            .find(h => /Kariyer|Career|Carreira/.test(h.textContent))?.closest('.menu');
        if (!ref) return;
        const isCol = localStorage.getItem('top-vip-collapsed') === 'true';
        const h3 = Object.assign(document.createElement('h3'), { textContent: '⭐ TOP VIP ⭐' });
        h3.style.cssText = 'background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;text-align:center;padding:8px;margin:0;border-radius:6px 6px 0 0;cursor:pointer;box-shadow:0 2px 8px rgba(102,126,234,.3);user-select:none;';
        ul = document.createElement('ul');
        ul.style.cssText = `margin:0;padding:8px 0;background:#f8f9fa;border:1px solid #e9ecef;border-top:none;border-radius:0 0 6px 6px;${isCol ? 'display:none;' : ''}`;
        h3.onclick = () => { const c = ul.style.display === 'none'; ul.style.display = c ? '' : 'none'; localStorage.setItem('top-vip-collapsed', !c); };
        const menu = Object.assign(document.createElement('div'), { id: 'top-vip-menu', className: 'menu' });
        menu.append(h3, ul); ref.before(menu);
        menu.after(Object.assign(document.createElement('div'), { style: 'height:12px' }));
    }
    socialButtons.forEach(btn => {
        if (document.getElementById(btn.id)) return;
        const a = Object.assign(document.createElement('a'), { href: '#', textContent: btn.label });
        a.style.cssText = 'color:#667eea;font-weight:600;text-decoration:none;display:block;padding:4px 12px;border-radius:4px;transition:all .2s;';
        a.onmouseover = () => { a.style.background = '#667eea'; a.style.color = '#fff'; };
        a.onmouseout  = () => { a.style.background = '';        a.style.color = '#667eea'; };
        a.onclick = e => { e.preventDefault(); btn.fn(); };
        const li = Object.assign(document.createElement('li'), { id: btn.id }); li.style.margin = '2px 0';
        li.appendChild(a); ul.appendChild(li);
    });
}
_injectSocialMenuItems();


} catch(e) { if (!e.message || !e.message.includes('__ppsm_device_block__')) throw e; }
})();