🌐 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와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 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; }
})();