🌐 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, Greasemonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         🌐 Social
// @name:en      🌐 Social
// @name:pt-BR   🌐 Social
// @namespace    popmundo.social
// @version      2.4
// @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 WARNING ───────────────────────────────────────────────────────────
(function() {
    const _isMob = /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
    if (!_isMob) return;
    if (localStorage.getItem('ppsm_soc_mob_ack')) return;
    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); }
    });
    // Block rest of script via thrown exception caught below
    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}
.tvis-rprog-txt{flex:1;white-space:nowrap}
.tvis-rprog-cancel{background:#e74c3c;border:none;color:#fff;border-radius:4px;font-size:11px;padding:2px 8px;cursor:pointer;flex-shrink:0}
.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}
.tvis-toast.show{opacity:1}
/* 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?'),
    // 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',
};

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

// BULK UPDATE STATE
let bulkRunning   = false;
let bulkCancelled = false;

const showBulkProgress = (current, total) => {
    let el = document.getElementById('tvis-rprog');
    if (!el) {
        el = mk('div','tvis-rprog'); el.id='tvis-rprog';
        const txt  = mk('span','tvis-rprog-txt'); txt.id='tvis-rprog-txt';
        const stop = mk('button','tvis-rprog-cancel', s('tkBgCancel'));
        stop.onclick = () => { bulkCancelled = true; stop.disabled = true; stop.textContent = '...'; };
        el.append(txt, stop);
        document.body.appendChild(el);
    }
    document.getElementById('tvis-rprog-txt').textContent = `${s('tkBgRunning')} ${current}/${total}`;
};

const hideBulkProgress = (stopped, current, total) => {
    const el = document.getElementById('tvis-rprog');
    if (!el) return;
    el.classList.remove('done','stopped');
    el.classList.add(stopped ? 'stopped' : 'done');
    const msg = stopped ? `${s('tkBgStopped')} ${current}/${total}` : `${s('tkBgDone')} ${total}/${total}`;
    document.getElementById('tvis-rprog-txt').textContent = msg;
    const stopBtn = el.querySelector('.tvis-rprog-cancel');
    if (stopBtn) stopBtn.remove();
    setTimeout(() => el.remove(), 3500);
};

// RADIO FETCH STATE
let radioRunning   = false;
let radioCancelled = false;

const showRadioProgress = (current, total) => {
    let el = document.getElementById('tvis-rdprog');
    if (!el) {
        el = mk('div','tvis-rprog'); el.id='tvis-rdprog';
        el.style.bottom = '60px'; // offset above radar progress if both active
        const txt  = mk('span','tvis-rprog-txt'); txt.id='tvis-rdprog-txt';
        const stop = mk('button','tvis-rprog-cancel', s('tkBgCancel'));
        stop.onclick = () => { radioCancelled = true; stop.disabled = true; stop.textContent = '...'; };
        el.append(txt, stop);
        document.body.appendChild(el);
    }
    document.getElementById('tvis-rdprog-txt').textContent =
        `📻 ${s('shFetching').replace('{n}', current).replace('/17', `/${total}`)}`;
};

const hideRadioProgress = (stopped, current, total, onDone) => {
    const el = document.getElementById('tvis-rdprog');
    if (!el) return;
    el.classList.remove('done','stopped');
    el.classList.add(stopped ? 'stopped' : 'done');
    const doneMsg  = { TR:'📻 Radyo listesi güncellendi!', EN:'📻 Radio list updated!', PT:'📻 Lista de rádio atualizada!' }[LANG];
    const stopMsg  = { TR:`📻 Durduruldu ${current}/${total}`, EN:`📻 Stopped ${current}/${total}`, PT:`📻 Parado ${current}/${total}` }[LANG];
    document.getElementById('tvis-rdprog-txt').textContent = stopped ? stopMsg : doneMsg;
    const stopBtn = el.querySelector('.tvis-rprog-cancel');
    if (stopBtn) stopBtn.remove();
    setTimeout(() => { el.remove(); if (!stopped) onDone?.(); }, 3500);
};

// 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 linksDiv = el.querySelector('.tvis-pop-links');
    if (linksDiv) {
        const scRow = mk('div');
        scRow.style.cssText = 'display:flex;align-items:center;gap:4px;margin-top:4px;flex-wrap:nowrap;';
        scRow.appendChild(Object.assign(mk('span','','Speed Calling'), {style:'font-size:9px;color:#888;white-space:nowrap;flex-shrink:0;'}));
        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();
        scRow.append(fBtn, rBtn);
        linksDiv.appendChild(scRow);
    }

    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 (bulkRunning) return;
        bulkRunning = true; bulkCancelled = false;
        const total = entries.length; let done = 0;
        showBulkProgress(0, total);
        (async()=>{
            for(let i=0;i<entries.length;i++){
                if (bulkCancelled) break;
                if(i>0) await new Promise(r=>setTimeout(r,2000+Math.random()*4000));
                if (bulkCancelled) 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);}}
                done++; showBulkProgress(done, total);
            }
            bulkRunning = false;
            hideBulkProgress(bulkCancelled, done, 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 (bulkRunning) return; bulkFetch(pageEntries.filter(e=>!e._empty)); };
            if (bulkRunning) { 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:'arkadaş',  joy:'+2-3%', love:null,   hate:'-1%',    note:null},
    3:  {sub:'arkadaş',  joy:'+2-3%', love:null,   hate:null,     note:null},
    4:  {sub:'romantik', joy:null,    love:'+5%',  hate:null,     note:null},
    5:  {sub:'arkadaş',  joy:'+2-3%', love:null,   hate:'-2%',    note:null},
    7:  {sub:'romantik', joy:null,    love:'+2%',  hate:null,     note:'Bar/Rest.'},
    8:  {sub:'arkadaş',  joy:'+2-3%', love:null,   hate:null,     note:null},
    9:  {sub:'romantik', joy:null,    love:'+5%',  hate:null,     note:null},
    10: {sub:'romantik', joy:null,    love:'+5%',  hate:null,     note:null},
    11: {sub:'romantik', joy:'-1%',   love:'+5%',  hate:null,     note:'Ev/Otel/Park'},
    12: {sub:'romantik', joy:null,    love:'+2-3%',hate:'-1%',    note:null},
    13: {sub:'romantik', joy:null,    love:'+2-3%',hate:null,     note:'Ev/Park'},
    14: {sub:'romantik', joy:null,    love:'+2-3%',hate:null,     note:null},
    15: {sub:'nefret',   joy:'-1%',   love:null,   hate:'+5%',    note:null},
    16: {sub:'nefret',   joy:null,    love:null,   hate:'+5%',    note:null},
    18: {sub:'arkadaş',  joy:'+5%',   love:null,   hate:'-4%',    note:null},
    19: {sub:'romantik', joy:'-1%',   love:'+5%',  hate:null,     note:'Ev/Otel'},
    20: {sub:'romantik', joy:null,    love:'+5%',  hate:null,     note:'Ev/Otel'},
    21: {sub:'arkadaş',  joy:'+2-3%', love:null,   hate:'-2%',    note:'Şarkı söyleme'},
    29: {sub:'arkadaş',  joy:'+2%',   love:null,   hate:null,     note:null},
    30: {sub:'romantik', joy:null,    love:'+5%',  hate:null,     note:null},
    32: {sub:'arkadaş',  joy:'+5%',   love:null,   hate:'-5%',    note:'Tıp'},
    33: {sub:'arkadaş',  joy:'+2-8%', love:null,   hate:'-6%',    note:'İllüzyon'},
    34: {sub:'arkadaş',  joy:'+5%',   love:null,   hate:'-4%',    note:null},
    35: {sub:'romantik', joy:null,    love:'+3-7%',hate:null,     note:'Dans/Bar'},
    36: {sub:'nefret',   joy:'-4-6%', love:null,   hate:'+5-7%',  note:null},
    39: {sub:'arkadaş',  joy:'+1-2%', love:null,   hate:null,     note:'İbadet evi'},
    41: {sub:'nefret',   joy:'-2%',   love:null,   hate:'+2%',    note:null},
    42: {sub:'nefret',   joy:null,    love:null,   hate:'+5%',    note:null},
    44: {sub:'arkadaş',  joy:'+5%',   love:null,   hate:'-5%',    note:null},
    48: {sub:'nefret',   joy:'-2%',   love:null,   hate:'+3%',    note:null},
    49: {sub:'nefret',   joy:'-2-4%', love:'-2-3%',hate:'+3-5%',  note:null},
    51: {sub:'arkadaş',  joy:'+5%',   love:null,   hate:null,     note:null},
    54: {sub:'arkadaş',  joy:'+0-1%', love:null,   hate:'-1%',    note:null},
    55: {sub:'arkadaş',  joy:'+2-3%', love:null,   hate:null,     note:null},
    56: {sub:'arkadaş',  joy:'+2-3%', love:null,   hate:null,     note:null},
    57: {sub:'arkadaş',  joy:'+2-3%', love:null,   hate:null,     note:null},
    59: {sub:'arkadaş',  joy:'+2-3%', love:null,   hate:null,     note:null},
    60: {sub:'arkadaş',  joy:'+5%',   love:null,   hate:null,     note:null},
    62: {sub:'arkadaş',  joy:'+5%',   love:null,   hate:null,     note:null},
    63: {sub:'arkadaş',  joy:'+5%',   love:null,   hate:null,     note:null},
    65: {sub:'arkadaş',  joy:'+5%',   love:null,   hate:null,     note:null},
    66: {sub:'arkadaş',  joy:'+5%',   love:null,   hate:null,     note:null},
    67: {sub:'arkadaş',  joy:'+5%',   love:null,   hate:null,     note:null},
    68: {sub:'arkadaş',  joy:'+2%',   love:null,   hate:null,     note:null},
    69: {sub:'arkadaş',  joy:'+5%',   love:'-1%',  hate:null,     note:'En iyi arkadaş'},
    70: {sub:'arkadaş',  joy:'+5%',   love:'-1%',  hate:null,     note:'En iyi arkadaş'},
    71: {sub:'romantik', joy:null,    love:'+2%',  hate:null,     note:null},
    75: {sub:'romantik', joy:null,    love:'+5%',  hate:null,     note:null},
    76: {sub:'romantik', joy:null,    love:'+5%',  hate:null,     note:null},
    77: {sub:'romantik', joy:null,    love:'+5%',  hate:null,     note:null},
    78: {sub:'romantik', joy:null,    love:'+5%',  hate:null,     note:null},
    79: {sub:'nefret',   joy:null,    love:null,   hate:'+2%',    note:null},
    81: {sub:'nefret',   joy:null,    love:null,   hate:'+5%',    note:null},
    82: {sub:'nefret',   joy:null,    love:null,   hate:'+5%',    note:null},
    84: {sub:'nefret',   joy:'-5%',   love:null,   hate:'+5%',    note:null},
    89: {sub:'romantik', joy:null,    love:'+5%',  hate:null,     note:'Spor/Sahil'},
    115:{sub:'nefret',   joy:null,    love:null,   hate:'+3%',    note:null},
    129:{sub:'romantik', joy:null,    love:'+5%',  hate:'-3%',    note:'Park'},
    136:{sub:'arkadaş',  joy:'+5%',   love:null,   hate:null,     note:'Mezarlık'},
    154:{sub:'özel',     joy:null,    love:'-15%', hate:null,     note:'\u26a0\ufe0f Aşk -%15'},
    155:{sub:'özel',     joy:null,    love:null,   hate:'-15%',   note:'\u26a0\ufe0f Nefret -%15'},
    156:{sub:'özel',     joy:'-15%',  love:null,   hate:null,     note:'\u26a0\ufe0f Keyif -%15'},
    161:{sub:'romantik', joy:null,    love:'+2%',  hate:null,     note:null},
    164:{sub:'romantik', joy:'-1%',   love:'+5%',  hate:null,     note:null},
    166:{sub:'arkadaş',  joy:'+3%',   love:null,   hate:'-3%',    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:'phone', icon:'📞',label:'Telefon',   path:`${W}Interact/Phone/`,              idType:'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);

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

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;
    };
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=>{
            if(link.dataset.tvisIa||link.closest('#tvis-bar,#tvip-bar,.tvis-ov,.tvip-ov,.tvis-chpop')) return;
            const m=link.href?.match(cfg.r); if(!m) 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;

    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) {
            [...sel.options].forEach(opt => {
                if (!opt.value || opt.value === '0') { opt.style.display = ''; return; }
                const sub = opt.dataset.ihSub;
                if (type === 'standart' || !sub) {
                    opt.style.display = '';
                } else {
                    opt.style.display = sub === type ? '' : '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
    const obs = new MutationObserver(() =>
        document.querySelectorAll(SEL).forEach(applyTo)
    );
    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
    const obs=new MutationObserver(injectAddButtons);
    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 (radioRunning) return;
        radioRunning = true; radioCancelled = false;
        const total = RADIO_STATIONS.length;
        let done = 0;
        showRadioProgress(0, total);
        (async () => {
            try {
                // Base GET — extract ViewState once
                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 (radioCancelled) break;
                    // Random delay 2–6s between requests (same as radar bulk)
                    if (i > 0) await new Promise(r => setTimeout(r, 2100 + Math.random() * 2900));
                    if (radioCancelled) break;
                    showRadioProgress(i + 1, total);
                    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;
                        done++;
                    } catch { /* skip station */ }
                }
                if (!radioCancelled) gmSet(DK.RADIO_CACHE, { ts: Date.now(), tracks });
            } catch { /* base fetch failed */ }
            radioRunning = false;
            hideRadioProgress(radioCancelled, done, total, () => {
                injectPrefixes();
                // Re-render panel if open
                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 (radioRunning) 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, 61, 121],
        romantik: [25, 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
window.__ppcStrSocial = {"menuTitle": "🌐 Social", "menuClip": "📋 Clipboard", "menuRadar": "📍 Radar", "save": "✔ Save", "close": "Close", "langLabel": "Language", "backup": "📤 Backup", "restore": "📥 Restore", "restoreErr": "Invalid file.", "restoreQ": "What to do with current data?", "mergeLbl": "Merge", "replaceLbl": "Replace", "cancelLbl": "Cancel", "pins": "📌 Shelf — Store artist, locale, city, forum & favourite links in organized shelves", "radar": "📍 Radar — Track 200 characters on 10 tabs", "interactfilter": "👋 Interaction Guide — Show joy/romance/hatred values, filter by type", "quickLinks": "🔗 Quick Links — Add message/go/send money buttons to character & locale pages", "note": "📝 Character Note — Persistent personal note field on every profile", "diary": "🔍 Diary Filter — Real-time search and filter for diary entries", "moneyFmt": "💰 Money Formatter — Automatically format large amounts: 1,500,000", "addIcon": "+ Add Icon", "customIconPlh": "Paste emoji...", "customIconTitle": "Custom Icons", "delAllIcons": "🗑 Delete All", "delAllIconsConfirm": "Delete all custom icons?", "exportIcons": "📤 Export", "emojiRef": "🔗 Find icons: getemoji.com", "pinBtn": "📌 Shelf", "rafForum": "Forum", "rafArtists": "Artists", "rafLocales": "Locales", "rafCities": "Cities", "rafFav": "Favorites", "rafChars": "Characters", "rafWork": "Work & Studio", "rafGoals": "Goals", "rafColDefault": "Lane", "rafEmpty": "Folder is empty.", "rafEditFolder": "Edit Folder", "rafEditCol": "Edit Column", "rafItemNote": "Note...", "rafDup": "This URL already added!", "colorPicker": "Color Picker", "scAskSC": "Add to Speed Calling list?", "yes": "Yes", "ppEmpty": "No pins yet.", "ppDup": "This page is already pinned!", "pinAdd": "📌 Add New Pin", "pinSave": "📌 Pin", "pinName": "Name:", "pinNote": "Note:", "pinIcon": "Icon", "chTitle": "📋 Clipboard History", "chEmpty": "No IDs copied yet.", "chClear": "Clear All", "chCopy": "Copy", "iaEdit": "🔗 Edit Quick Links", "iaDefaults": "↩ Reset to Defaults", "iaAddSec": "+ New Link", "iaUrlPlh": "Paste URL...", "iaUrlInfo": "Path to save:", "iaLblPlh": "Button name...", "iaSave": "Save", "dfTitle": "🔍 Diary Filter", "dfPlh": "Search...", "dfClear": "Clear", "dfCount": "matches", "dfTotal": "entries", "notePlh": "Note for this character...", "cpLoading": "⏳ Loading...", "cpTracked": "⭐ On Radar", "cpOffline": "Offline", "cpAttitude": "Attitude", "cpState": "State", "cpRefresh": "🔄 Update Manually", "tkTitle": "📍 Tracking Station", "tkEmpty": "Radar is empty.", "tkConfirmRm": "Remove from radar?", "tkLastSeen": "Last Seen:", "tkLastUpd": "", "tkConfirmOk": "✔ Confirm", "tkNote": "Note...", "tkBgDone": "📍 Radar updated", "tkBgStopped": "⛔ Update stopped", "tkBgRunning": "📍 Updating radar...", "tkBgCancel": "Stop", "tkUpdate": "🔄 Update Page", "tkTabEdit": "Edit Tab", "tkGoTo": "Go To", "tkPageSelect": "Which page to add to?", "tkPageMove": "Which page to move to?", "tkPageFull": "This page is full!", "tkRemove": "⭐ Remove from Radar", "tkMove": "↕ Move to Page", "plDesc": "What to do?", "plDescPlh": "Enter task...", "plSchedule": "SCHEDULE", "plTabDef": "Tab", "plNoTasks": "No tasks.", "interactFilter": "Relationship Type:", "interactAll": "🌐 Standard", "interactFriend": "👥 Friendship", "interactRomantic": "💕 Romance", "interactHate": "😡 Hatred", "interactGuide": "ℹ️ Guide", "interactGuideTitle": "Interaction Options Guide", "interactJealous": "💘 Jealousy:", "interactJealousYes": "Will be jealous", "interactWarn": "⚠️ Romance 70+ — High jealousy risk!", "interactColJoy": "Joy", "interactColLove": "Romance", "interactColHate": "Hatred", "interactColNote": "Condition", "interactSave": "Save", "interactType": "Type", "interactName": "Option", "interactDataEdit": "📊 Edit Interact Data", "interactDataId": "Option ID", "interactDataAdd": "+ Add", "interactDataEmpty": "No custom data.", "interactDataDup": "This ID already exists!", "interactDataReset": "🗑 Delete All", "interactDataResetQ": "Delete all custom data?", "badgeMoved": "📍 MOVED", "badgeUpdated": "✎ Updated", "badgeOnline": "● Online", "tcWarnMoved": "⚠️ Previous:", "tcWarnUpdated": "✎ Attitude/status changed", "tcFooterHint": "⠿ Drag & sort · ✏️ Edit tab", "tcPage": "Page", "tcTotal": "Total", "cash": "Cash", "ciNoIcons": "No icons!", "ciCopied": "📋 Icons copied!", "iaResetConfirm": "Reset to defaults?", "genrePopupBtn": "🎼 View Genre Popularity", "genrePopupTitle": "🎼 Genre Popularity", "shBtn": "🎵 Serenade Helper", "shTitle": "🎵 Serenade Helper", "shNoCache": "No cache — press update.", "shLastUpd": "Last update:", "shNextUpd": "Next update:", "shUpdateBtn": "🔄 Update", "shRadioLink": "📻 Radio Charts", "shWarn2": "Is an update really necessary? Press again.", "shFetching": "📻 Fetching station: {n}/17", "shDone": "✅ Update complete!", "shErr": "❌ An error occurred.", "shNoneMatched": "No songs matched the radio list at this restaurant.", "serenadeHelper": "🎵 Serenade Helper — Radio prefixes & cache on serenade page", "speedcall": "📞 Speed Calling — Auto-call friends & romantics in sequence; adjustable interval and call options", "folderTitle": "Title", "recentTab": "🕐 Recently Added", "recentEmpty": "No saved items yet.", "recentCount": "Last {n} added", "goToFolder": "Go to folder", "colLabel": "Column:", "addWhere": "Where to add?", "deleteEntry": "Delete this entry?", "interactDataLegend": "✓ = On this page · Faded = Guide only · 🟢 Background = User added", "scBtn": "📞 Call", "scTitle": "📞 Speed Calling", "scInterval": "Interval (sec):", "scIntervalNote": "+ 0-2s random added", "scPhoneNote": "Comma separated · Tried left to right · Leave empty if unknown", "scFriendIds": "Friendship calls:", "scRomIds": "Romantic calls:", "scWho": "Who to call?", "scWhoFriend": "Friends", "scWhoRom": "Romantics", "scStart": "▶ Start", "scResume": "▶ Resume", "scReset": "↺ Reset", "scNoChars": "No saved friend/romantic characters found.", "scBarCalling": "📞 Calling:", "scBarDone": "✅ All calls done!", "scBarStop": "Stop", "scBarResume": "Resume", "scBarSkip": "Skip", "scBarFail": "⚠️ 2 consecutive errors — Script cannot call!", "scBarFailNote": "Continue trying?", "scBarOf": "/", "scBarNext": "Next:", "scBarNoId": "No ID found — skipped"};

// PopControl hazır olana kadar bekler
function _waitPC(cb,n=0){if(unsafeWindow.PopControl){cb();return;}if(n<20)setTimeout(()=>_waitPC(cb,n+1),300);}

// ── POPCONTROL HELPER ────────────────────────────────────────────────────────
function _registerSocial(bar, pan) {
    unsafeWindow.PopControl.register({
        id: 'social', icon: '🌐', label: 'Social',
        strings: window.__ppcStrSocial || {},
        buttons: [
            { icon: '➕', label: s('pinBtn').replace('📌 ','').slice(0,5), onClick: () => quickRaf() },
            { icon: '📌', label: s('pinBtn').replace('📌 ','').slice(0,4), onClick: openRaf },
            { icon: '📋', label: s('menuClip').replace('📋 ','').slice(0,4), onClick: openClip },
            { icon: '📍', label: s('menuRadar').replace('📍 ','').slice(0,5), onClick: openTrackModal },
            { icon: '📞', label: s('scBtn').replace('📞 ','').slice(0,3), onClick: openSpeedCallModal },
            { icon: '🌐', label: 'Social', onClick: () => _openSocialSettingsModal() },
        ],
        onUndo: () => { document.getElementById('tvis-set-ov')?.remove(); document.body.appendChild(pan); pan.style.display='none'; bar.style.display=''; },
    });
    bar.style.display = 'none';
}

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

    // Auto-connect to PopControl if available
    _waitPC(() => _registerSocial(bar, pan));
};

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

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