🎨 Helper

Popmundo için kapsamlı oyun yardımcısı — görsel iyileştirmeler, envanter & ticaret araçları, müzik ve şehir kısayolları. Masaüstü, Android ve iPhone ile tam uyumlu.

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

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

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

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

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

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name            🎨 Helper
// @name:en         🎨 Helper
// @name:pt-BR      🎨 Helper
// @namespace       popmundo.helper
// @version         6.0
// @description     Popmundo için kapsamlı oyun yardımcısı — görsel iyileştirmeler, envanter & ticaret araçları, müzik ve şehir kısayolları. Masaüstü, Android ve iPhone ile tam uyumlu.
// @description:en  Comprehensive Popmundo game assistant — UI enhancements, inventory & trading tools, music and city shortcuts. Fully compatible with desktop, Android and iPhone.
// @description:pt-BR Assistente completo para Popmundo — melhorias visuais, ferramentas de inventário e comércio, música e atalhos de cidade. Compatível com desktop, Android e iPhone.
// @author          luke-james-gibson
// @license         MIT
// @id              9g1a6x
// @match           https://*.popmundo.com/*
// @grant           GM_setValue
// @grant           GM_getValue
// @grant           GM_deleteValue
// @run-at          document-end
// @grant        unsafeWindow
// ==/UserScript==

(function () {
'use strict';

// MOBILE DETECTION
const isMobile = window.innerWidth < 768 || 'ontouchstart' in window;

// CSS
document.head.appendChild(Object.assign(document.createElement('style'), { textContent: `
.tvip-bar{position:fixed;top:0;right:0;z-index:9990;background:#fff;border:1px solid #ddd;border-radius:0 0 0 6px;padding:3px 10px;font-size:11px;display:flex;gap:10px;align-items:center;box-shadow:0 1px 6px rgba(0,0,0,.15)}
.tvip-bar a{font-weight:bold;text-decoration:none;color:inherit;cursor:pointer}
.tvip-hpanel{display:none;position:fixed;top:26px;right:0;z-index:9989;background:#fff;border:1px solid #ddd;border-radius:0 0 0 6px;padding:12px;min-width:240px;max-width:290px;box-shadow:0 4px 12px rgba(0,0,0,.15);max-height:82vh;overflow-y:auto}
.tvip-ov{position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:99999;display:flex;align-items:center;justify-content:center}
.tvip-box{background:#fff;border-radius:8px;padding:18px;min-width:300px;max-width:500px;width:90%;box-shadow:0 4px 24px rgba(0,0,0,.35);max-height:80vh;overflow-y:auto}
.tvip-title{font-weight:bold;font-size:14px;margin-bottom:12px}
.tvip-chk{display:flex;align-items:flex-start;gap:6px;margin-bottom:8px;cursor:pointer;font-size:12px;line-height:1.4}
.tvip-chk input{margin-top:2px;flex-shrink:0;cursor:pointer}
.tvip-hr{border:none;border-top:1px solid #e0e0e0;margin:6px 0}
.tvip-sec{font-size:10px;font-weight:bold;color:#888;margin:8px 0 3px;text-transform:uppercase;letter-spacing:.5px}
.tvip-pct{float:left;font-size:9px;pointer-events:none}
.tvip-badge{padding:0 5px;border-radius:10px;font-weight:bold;margin-left:4px;font-size:inherit;display:inline-block}
.tvip-search-wrap{margin-bottom:6px;display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.tvip-search-done{display:none}
.tvip-ticket-price{margin-left:8px;color:#d6021e;font-weight:bold}
.tvip-item-id,.tvip-item-page-id{margin-left:6px;color:#777;font-size:11px}
.tvip-tbl-avg,.tvip-heist-avg{font-weight:bold;background:#f5f5f5}
.tvip-lang-row{display:flex;gap:4px;margin:6px 0}
.tvip-lang-btn{flex:1;padding:3px 4px;border:1px solid #ccc;border-radius:4px;cursor:pointer;font-size:11px;background:#f8f9fa;text-align:center}
.tvip-lang-btn.active{background:#17a2b8;color:#fff;border-color:#17a2b8;font-weight:bold}
.tvip-bulk-panel{background:#f0f0f0;border:1px solid #dcdcdc;border-radius:6px;padding:12px;margin-bottom:16px;font-size:13px}
.tvip-bulk-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:8px;margin-bottom:10px}
.tvip-bulk-field label{display:block;font-weight:bold;font-size:11px;color:#555;margin-bottom:3px}
.tvip-bulk-field input,.tvip-bulk-field select{width:100%;padding:5px 6px;border:1px solid #ccc;border-radius:4px;box-sizing:border-box;font-size:12px}
.tvip-bulk-actions{display:flex;gap:6px;margin-bottom:8px}
.tvip-bulk-actions button{flex:1;padding:5px 10px;border-radius:4px;font-weight:bold;font-size:12px;cursor:pointer;border:1px solid #555}
.tvip-bulk-actions button:disabled{opacity:.5;cursor:not-allowed}
.tvip-status{font-size:12px;text-align:center;background:#e9ecef;padding:5px;border-radius:4px;margin-bottom:4px;color:#495057;font-weight:500}
.tvip-spent{font-size:12px;text-align:center;background:#d1fae5;padding:5px;border-radius:4px;color:#065f46;font-weight:500}
.tvip-btn-start{background:linear-gradient(to bottom,#d4edda,#c3e6cb);border-color:#28a745!important}
.tvip-btn-stop{background:linear-gradient(to bottom,#f8d7da,#f5c6cb);border-color:#dc3545!important}
.tvip-send-wrap{margin-top:10px;padding:10px;border-radius:5px}
.tvip-send-running{background:#d4edda;border:2px solid #28a745}
.tvip-send-setup{background:#fff3cd;border:2px solid #ffc107}
.btn-g{padding:5px 14px;background:#218838;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:12px}
.btn-b{padding:5px 14px;background:#17a2b8;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:12px}
.btn-r{padding:5px 14px;background:#e74c3c;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:12px}
.btn-grey{padding:5px 14px;background:#6c757d;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:12px}
.btn-sm{padding:2px 8px!important;font-size:11px!important}
/* Remove spinners from number inputs */
input[type=number]::-webkit-outer-spin-button,input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
input[type=number]{-moz-appearance:textfield}
` }));



if (isMobile) document.head.appendChild(Object.assign(document.createElement('style'), { textContent: `
/* ── Touch targets ── */
.tvip-chk{padding:12px 6px;font-size:15px;gap:12px}
.tvip-chk input{width:22px;height:22px;flex-shrink:0}
.tvip-lang-btn{padding:12px 6px;font-size:14px;min-height:44px}
.btn-g,.btn-b,.btn-r,.btn-grey{padding:12px 20px;font-size:15px;min-height:44px}
.btn-sm{padding:10px 14px!important;font-size:14px!important;min-height:40px}
/* ── Sections ── */
.tvip-sec{font-size:13px;margin:14px 0 8px}
.tvip-hr{margin:12px 0}
/* ── Modal ── */
.tvip-box{width:98%!important;max-width:98%!important;padding:18px;max-height:90vh;min-height:auto}
.tvip-title{font-size:17px;margin-bottom:16px}
/* ── Bulk panels ── */
.tvip-bulk-grid{grid-template-columns:1fr!important}
.tvip-bulk-field input,.tvip-bulk-field select{font-size:15px;padding:10px 12px;min-height:44px}
.tvip-bulk-field label{font-size:14px;margin-bottom:6px}
.tvip-bulk-actions button{padding:12px;font-size:15px;min-height:44px}
.tvip-status,.tvip-spent{font-size:14px;padding:10px}
/* ── Table search ── */
.tvip-search-wrap input{font-size:15px;padding:10px 12px;width:100%;box-sizing:border-box;min-height:44px}
.tvip-search-wrap{flex-wrap:wrap;gap:8px}
/* ── Send wrap ── */
.tvip-send-wrap{padding:16px}
.tvip-send-wrap input[type=number]{font-size:15px;padding:8px 12px;min-height:44px}
/* ── City manager ── */
#tvip-bulk-offer-ui h3,#tvip-bulk-accept-ui h3,#tvip-bulk-cancel-ui h3{font-size:16px}
/* ── Mobile Header Fix ── */
.tvip-bar{top:50px!important;z-index:999999!important;font-size:13px;padding:4px 12px}
.tvip-bar a{padding:4px 8px}
/* ── Modal improvements ── */
.tvip-ov{padding:10px;box-sizing:border-box}
.tvip-hpanel{max-height:85vh;overflow-y:scroll}
/* ── Touch improvements ── */
.tvip-chk:hover,.tvip-lang-btn:hover,.btn-g:hover,.btn-b:hover,.btn-r:hover,.btn-grey:hover{background-color:rgba(0,0,0,0.05)}
/* ── Input focus for mobile ── */
input:focus,select:focus,textarea:focus{outline:2px solid #17a2b8;outline-offset:2px}
` }));
// 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 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) !== '0';
const guard = (key, ...urls) => isOn(key) && (!urls.length || urls.some(u => location.href.includes(u)));
const showGlobalStop = (onStop) => {
    if (document.getElementById('tvip-global-stop')) return;
    const bar = mk('div'); bar.id = 'tvip-global-stop';
    bar.style.cssText = 'position:fixed;top:0;left:0;width:100%;background:#dc3545;color:#fff;text-align:center;padding:12px;z-index:999999;font-weight:bold;cursor:pointer;box-shadow:0 4px 12px rgba(0,0,0,0.3);font-size:14px;';
    bar.textContent = '⏹ ' + s('globalStop');
    bar.onclick = () => { bar.remove(); if(onStop) onStop(); };
    document.body.appendChild(bar);
};
const mkModal = (title, renderFn) => {
    document.getElementById('tvip-modal')?.remove();
    const ov  = mk('div', 'tvip-ov'); ov.id = 'tvip-modal';
    const box = mk('div', 'tvip-box');
    box.appendChild(mk('div', 'tvip-title', title));
    const cont = mk('div'); box.appendChild(cont);
    const close = () => ov.remove();
    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);
};

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

const STR = {
    menuTitle:      _D('🎨 Helper',                    '🎨 Helper',                      '🎨 Helper'),
    save:           _D('✔ Tamam',                      '✔ Save',                         '✔ Salvar'),
    readMe:         _D('📖 Beni Oku',                  '📖 Read Me',                     '📖 Leia-me'),
    langLabel:      _D('Dil',                          'Language',                       'Idioma'),
    backup:         _D('📤 Yedekle',                   '📤 Backup',                      '📤 Exportar'),
    restore:        _D('📥 Geri Yükle',                '📥 Restore',                     '📥 Importar'),
    backupOk:       _D('Yedek alındı.',                'Backup created.',                'Backup criado.'),
    restoreOk:      _D('Geri yüklendi.',               'Restored.',                      'Restaurado.'),
    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'),
    close:          _D('Kapat',                        'Close',                 'Fechar'),
    // sections
    secGeneral:     _D('GENEL GÖRÜNÜM',                'GENERAL DISPLAY',       'APARÊNCIA GERAL'),
    secItems:       _D('EŞYA',                         'ITEMS',                 'ITENS'),
    // features
    pb:             _D('📊 Bar yüzdeleri gösterilir',  '📊 Show % on bars',     '📊 Mostrar % nas barras'),
    alignLeft:      _D('⬅️ Sayfa sola yaslanır','⬅️ Page aligned to left','⬅️ Página alinhada à esquerda'),
    tableTools:     _D('🔍 Tablolarda Arama & Sıralama & Ortalama','🔍 Table Search & Sort & Average','🔍 Busca & Ordenação & Média em tabelas'),
    itemId:         _D('🏷️ Eşya adlarına ID numarası ekle','🏷️ Add ID number next to item names','🏷️ Adiciona número de ID ao lado dos nomes'),
    colorScoring:   _D('🌈 Renkli Puanlama — Şöhret linklerine 0–26 rozet','🌈 Colour Scoring — 0–26 badge on fame links','🌈 Pontuação Colorida — emblema 0–26 nos links de fama'),
    autoDelivery:   _D('✅ Teslim onay kutularını otomatik işaretler','✅ Auto-checks delivery confirmation boxes','✅ Marca automaticamente caixas de entrega'),
    ticketPricer:   _D('🎟️ Sanatçı davet sayfasında önerilen bilet fiyatını göster','🎟️ Show suggested ticket price on invite page','🎟️ Mostra preço sugerido na página de convite'),
    imageCtrl:      _D('🖼️ Yavaş/Yüklenmeyen Resim — 5 sn, tıkla yeniden dene','🖼️ Slow/Broken Images — 5s, click to retry','🖼️ Imagens Lentas/Quebradas — 5s, clique para tentar novamente'),
    itemFilters:    _D('🎒 Sadece alınabilir eşyalar gösterilir', '🎒 Only takeable items shown','🎒 Exibir apenas itens disponíveis'),
    repertoireF:    _D('🎵 Repertuarı kategoriye göre filtreler','🎵 Filters repertoire by category','🎵 Filtra repertório por categoria'),
    bulkOffer:      _D('🏷️ TradeHub - Toplu teklif','🏷️ TradeHub - Bulk offer','🏷️ TradeHub - Oferta em massa'),
    bulkAccept:     _D('🛒 Gelen teklifleri otomatik kabul','🛒 Auto-accept incoming offers','🛒 Aceitar ofertas recebidas automaticamente'),
    cityShortcuts:  _D('🏙️ Şehirde duş evi ve patikalar görünür', '🏙️ Show shower house & paths in city',  '🏙️ Mostrar casa de banho e trilhas na cidade'),

    bbMinimize:     _D('—',                                       '—',                                      '—'),
    bbRestore:      _D('🎮',                                      '🎮',                                     '🎮'),
    // table search
    psPlh:          _D('Tabloda ara...',               'Search table...',                'Buscar na tabela...'),
    psCount:        _D('sonuç',                        'results',                        'resultados'),
    tdConfirm:      _D('Bu tablonun arama kutusunu gizlemek istiyor musun?', 'Hide the search box for this table?', 'Ocultar a caixa de busca desta tabela?'),
    tdHide:         _D('Aramayı gizle',                'Hide search',                    'Ocultar busca'),
    tdClear:        _D('Gizli Tabloları Sıfırla',      'Reset Hidden Tables',            'Redefinir Tabelas Ocultas'),
    // item filters
    btnShowAll:     _D('👁 Tüm Eşyaları Göster',       '👁 Show All Items',               '👁 Mostrar Todos os Itens'),
    btnOnlyTake:    _D('🔒 Sadece Alınabilecekler',    '🔒 Only Takeable',               '🔒 Só os Pegáveis'),
    resetOffered:   _D('🗑️ Teklif Listesini Sıfırla', '🗑️ Reset Offer List',            '🗑️ Redefinir Lista'),
    confirmReset:   _D('Teklif listesi sıfırlansın mı?', 'Reset offered items list?',   'Redefinir lista de itens ofertados?'),
    resetDone:      _D('Sıfırlandı.',                  'Done.',                          'Redefinido.'),
    hideOfferedChk: _D('🙈 Teklif edilenleri gizle',   '🙈 Hide offered items',          '🙈 Ocultar itens ofertados'),
    deleteConfirmChk: _D('🗑️ Onay penceresine eşya adlarını ekler','🗑️ Add item names to confirm dialog','🗑️ Adiciona nomes dos itens à confirmação'),
    // repertoire
    repAll:         _D('Tüm Şarkılar',                 'All Songs',                      'Todas as Músicas'),
    repMarket:      _D('Pazar Şarkıları',              'Market Songs',                   'Músicas do Mercado'),
    repNoMarket:    _D('Pazar Dışı',                   'Non-Market',                     'Fora do Mercado'),
    repJam:         _D('Jam Şarkıları',                'Jam Songs',                      'Músicas de Jam'),
    repSetlist:     _D('Setlist Şarkıları',            'Setlist Songs',                  'Músicas do Setlist'),
    repSecret:      _D('Gizli Şarkılar',               'Secret Songs',                   'Músicas Secretas'),
    repTitle:       _D('🎵 Repertuar Filtresi',        '🎵 Repertoire Filter',           '🎵 Filtro de Repertório'),
    // bulk offer
    boTitle:        _D('Toplu Teklif',                 'Bulk Offer',                     'Oferta em Massa'),
    globalStop:     _D('TÜM İŞLEMLERİ DURDUR',         'STOP ALL PROCESSES',             'PARAR TODOS OS PROCESSOS'),
    boNamePlh:      _D('Örn: Ağrı kesici...',           'e.g., Painkiller...',            'Ex: Analgésico...'),
    boItemName:     _D('Eşya adı (başlangıç):',        'Item name (prefix):',            'Nome do item (prefixo):'),
    boQty:          _D('Adet:',                        'Quantity:',                      'Quantidade:'),
    boPrice:        _D('Fiyat (M$):',                  'Price (M$):',                    'Preço (M$):'),
    boStart:        _D('▶ Teklif Et',                  '▶ Offer',                        '▶ Ofertar'),
    boStop:         _D('■ Durdur',                     '■ Stop',                         '■ Parar'),
    boFavorites:    _D('⭐ Favoriler',                 '⭐ Favorites',                   '⭐ Favoritos'),
    boCustomers:    _D('👥 Müşteriler',                '👥 Customers',                   '👥 Clientes'),
    boReady:        _D('Hazır.',                       'Ready.',                         'Pronto.'),
    boDone:         _D('Tüm teklifler tamamlandı!',    'All offers completed!',          'Todas as ofertas concluídas!'),
    boStopped:      _D('Kullanıcı tarafından durduruldu.', 'Stopped by user.',           'Parado pelo usuário.'),
    boResumed:      _D('Yeniden yüklendi, devam ediliyor...', 'Reloaded, resuming...',   'Recarregado, retomando...'),
    boCritErr:      _D('Kritik hata: Sayfa öğeleri kayboldu.', 'Critical error: Page elements gone.', 'Erro crítico: Elementos da página desapareceram.'),
    boErrName:      _D('Hata: Eşya adı girin.',        'Error: Enter item name.',        'Erro: Digite o nome do item.'),
    boErrQty:       _D('Hata: Geçersiz adet.',         'Error: Invalid quantity.',       'Erro: Quantidade inválida.'),
    boErrPrice:     _D('Hata: Geçersiz fiyat.',        'Error: Invalid price.',          'Erro: Preço inválido.'),
    // favorites modal
    favTitle:       _D('⭐ Favoriler',                 '⭐ Favorites',                   '⭐ Favoritos'),
    favEmpty:       _D('Henüz favori yok.',            'No favorites yet.',              'Nenhum favorito ainda.'),
    favNameLbl:     _D('Favori Adı:',                  'Favorite Name:',                 'Nome do Favorito:'),
    favItemLbl:     _D('Eşya Adı (prefix):',           'Item Name (prefix):',            'Nome do Item (prefixo):'),
    favQtyLbl:      _D('Adet:',                        'Qty:',                           'Qtd:'),
    favPriceLbl:    _D('Fiyat (M$):',                  'Price (M$):',                    'Preço (M$):'),
    favAdd:         _D('Ekle',                         'Add',                            'Adicionar'),
    favUse:         _D('Kullan',                       'Use',                            'Usar'),
    favDel:         _D('Sil',                          'Delete',                         'Excluir'),
    favDelQ:        _D('Bu favori silinsin mi?',       'Delete this favorite?',          'Excluir este favorito?'),
    favFillErr:     _D('Tüm alanları doldurun.',       'Fill in all fields.',            'Preencha todos os campos.'),
    // customers modal
    custTitle:      _D('👥 Müşteri Kartları',          '👥 Customer Cards',              '👥 Cartões de Clientes'),
    custEmpty:      _D('Henüz müşteri yok.',           'No customers yet.',              'Nenhum cliente ainda.'),
    custSearch:     _D('Müşteri ara...',               'Search customer...',             'Buscar cliente...'),
    custAdd:        _D('+ Yeni Müşteri',               '+ New Customer',                 '+ Novo Cliente'),
    custNameLbl:    _D('Ad:',                          'Name:',                          'Nome:'),
    custUrlLbl:     _D('Karakter URL:',                'Character URL:',                 'URL do Personagem:'),
    custNoteLbl:    _D('Not:',                         'Note:',                          'Nota:'),
    custSave:       _D('Kaydet',                       'Save',                           'Salvar'),
    custEdit:       _D('Düzenle',                      'Edit',                           'Editar'),
    custDel:        _D('Sil',                          'Delete',                         'Excluir'),
    custDelQ:       _D('Bu müşteri silinsin mi?',      'Delete this customer?',          'Excluir este cliente?'),
    custOffers:     _D('Son Gönderimler',              'Recent Offers',                  'Ofertas Recentes'),
    custCharLink:   _D('👤 Karakter',                  '👤 Character',                   '👤 Personagem'),
    custNoNote:     _D('Not yok.',                     'No note.',                       'Sem nota.'),
    custNoOffers:   _D('Henüz gönderim yok.',          'No offers yet.',                 'Nenhuma oferta ainda.'),
    custSaved:      _D('Müşteri kaydedildi.',          'Customer saved.',                'Cliente salvo.'),
    // log modal
    boLogTitle:     _D('📋 Toplu Teklif Logu',         '📋 Bulk Offer Log',              '📋 Log de Ofertas em Massa'),
    boLogEmpty:     _D('Henüz gönderim kaydı yok.',   'No offer records yet.',          'Nenhum registro ainda.'),
    boLogClear:     _D('Temizle',                      'Clear',                          'Limpar'),
    boLogClearQ:    _D('Tüm log kayıtları silinsin mi?', 'Clear all log records?',      'Limpar todos os registros?'),
    // bulk accept
    baTitle:        _D('Toplu Kabul',                  'Bulk Accept',                    'Aceitar em Massa'),
    baItemName:     _D('Eşya Adı (Opsiyonel):',        'Item Name (Optional):',          'Nome do Item (Opcional):'),
    baItemPlh:      _D('Tümü için boş bırakın',        'Leave blank for all',            'Deixe em branco para todos'),
    baMaxPrice:     _D('Maks. Fiyat (M$):',            'Max Price (M$):',                'Preço Máximo (M$):'),
    baStart:        _D('▶ Kabul Et',                   '▶ Accept',                       '▶ Aceitar'),
    baStop:         _D('■ Durdur',                     '■ Stop',                         '■ Parar'),
    baReady:        _D('Hazır.',                       'Ready.',                         'Pronto.'),
    baNoSection:    _D('Teklif bölümü bulunamadı.',    'Offer section not found.',       'Seção de ofertas não encontrada.'),
    baResumed:      _D('Yeniden yüklendi, devam ediliyor...', 'Reloaded, resuming...',   'Recarregado, retomando...'),
    baErrPrice:     _D('Hata: Geçersiz fiyat.',        'Error: Invalid price.',          'Erro: Preço inválido.'),
    baFree:         _D('Bedava',                       'Free',                           'Grátis'),
    // bulk cancel
    bcTitle:        _D('Toplu İptal',                  'Bulk Cancel',                    'Cancelar em Massa'),
    bcItemName:     _D('Eşya Adı (Opsiyonel):',        'Item Name (Optional):',          'Nome do Item (Opcional):'),
    bcFilter:       _D('Filtre:',                      'Filter:',                        'Filtro:'),
    bcFilterAll:    _D('Tümü',                         'All',                            'Todos'),
    bcFilterFree:   _D('Sadece Bedava',                'Free Only',                      'Só Grátis'),
    bcFilterPaid:   _D('Sadece Ücretli',               'Paid Only',                      'Só Pagos'),
    bcStart:        _D('▶ İptal Et',                   '▶ Cancel',                       '▶ Cancelar'),
    bcStop:         _D('■ Durdur',                     '■ Stop',                         '■ Parar'),
    bcReady:        _D('Hazır.',                       'Ready.',                         'Pronto.'),
    bcNoSection:    _D('Teklif bölümü bulunamadı.',    'Offer section not found.',       'Seção não encontrada.'),
    // city shortcuts
    csShower:       _D('Duş Evi',                      'Shower House',                   'Casa de Banho'),
    csPath:         _D('Patika',                       'Path',                           'Trilha'),
    csGoShower:     _D('Duş evine git',                'Go to shower house',             'Ir para casa de banho'),
    csGoPath:       _D('Patikaya git',                 'Go to path',                     'Ir para trilha'),
    csMin:          _D('Dakika',                       'Minutes',                        'Minutos'),
    csOther:        _D('Diğer Mekan',          'Other Location',      'Outro Local'),
    cityMgr:        _D('Şehir Kısayolları Yönet','Manage City Shortcuts','Gerenciar Atalhos'),
    cityMgrReset:   _D('↩ Sıfırla',            '↩ Reset',             '↩ Redefinir'),
    cityMgrResetQ:  _D('Bu şehir varsayılana dönsün mü?','Reset this city to default?','Redefinir esta cidade?'),
    cityMgrAdd:     _D('Mekan Ekle',           'Add Location',        'Adicionar Local'),
    cityMgrAddBtn:  _D('Ekle',                 'Add',                 'Adicionar'),
    cityMgrName:    _D('Mekan Adı',            'Location Name',       'Nome do Local'),
    cityMgrCustom:  _D('Özelleştirilmiş',      'Customized',          'Personalizado'),
    cityMgrDelQ:    _D('Bu mekan silinsin mi?','Delete this location?','Excluir este local?'),
    cityMgrEmpty:   _D('Mekan yok.',           'No locations.',       'Sem locais.'),
    // table avg
    taAvg:          _D('🌍 Ortalama',                  '🌍 Average',                     '🌍 Média'),
    taDiscAvg:      _D('🎯 Keşif Ortalaması:',         '🎯 Heist Avg:',                  '🎯 Média de Descoberta:'),
};

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

// Dynamic string helpers
const sf = {
    boNoItem:    n    => ({ TR:`"${n}" ile başlayan eşya bulunamadı.`,          EN:`No items starting with "${n}" found.`,                       PT:`Nenhum item começando com "${n}" encontrado.`                    }[LANG]),
    boFound:     (t,q,p) => ({ TR:`${t} eşya bulundu. ${q} adet ${p}M$ tekliflenecek...`,  EN:`Found ${t}. Offering ${q} at ${p}M$...`,           PT:`${t} itens. Ofertando ${q} por ${p}M$...`                        }[LANG]),
    boOfferring: (c,t,n) => ({ TR:`Teklif ${c}/${t}: '${n}'...`,                           EN:`Offering ${c}/${t}: '${n}'...`,                    PT:`Ofertando ${c}/${t}: '${n}'...`                                  }[LANG]),
    boSkip:      n    => ({ TR:`Hata: '${n}' seçilemedi, atlandı.`,              EN:`Error: Could not select '${n}', skipping.`,                  PT:`Erro: Não foi possível selecionar '${n}', pulando.`               }[LANG]),
    baSpent:     n    => ({ TR:`Toplam Harcama: ${n} M$`,                        EN:`Total Spent: ${n} M$`,                                       PT:`Total Gasto: ${n} M$`                                            }[LANG]),
    baInit:      (n,p) => ({ TR:`Başlatılıyor... "${n||'tümü'}" maks. ${p}M$`,   EN:`Starting... "${n||'all'}" up to ${p}M$`,                     PT:`Iniciando... "${n||'todos'}" até ${p}M$`                          }[LANG]),
    baAccepting: (i,n,p) => ({ TR:`Kabul ediliyor #${i}: '${n}' (${p})...`,     EN:`Accepting #${i}: '${n}' (${p})...`,                          PT:`Aceitando #${i}: '${n}' (${p})...`                               }[LANG]),
    baDone:      (c,sp) => ({ TR:`Tamamlandı! Kabul: ${c}. Harcama: ${sp}M$.`,  EN:`Done! Accepted: ${c}. Spent: ${sp}M$.`,                      PT:`Concluído! Aceitos: ${c}. Gasto: ${sp}M$.`                        }[LANG]),
    baStopped:   (c,sp) => ({ TR:`Durduruldu. Kabul: ${c}. Harcama: ${sp}M$.`,  EN:`Stopped. Accepted: ${c}. Spent: ${sp}M$.`,                   PT:`Parado. Aceitos: ${c}. Gasto: ${sp}M$.`                          }[LANG]),
    baNoMatch:   (c,sp) => ({ TR:`Eşleşen teklif yok. Kabul: ${c}. Harcama: ${sp}M$.`, EN:`No more matches. Accepted: ${c}. Spent: ${sp}M$.`,    PT:`Sem correspondências. Aceitos: ${c}. Gasto: ${sp}M$.`            }[LANG]),
    bcCancelling:(i,n)  => ({ TR:`İptal ediliyor #${i}: '${n}'...`,                    EN:`Cancelling #${i}: '${n}'...`,                          PT:`Cancelando #${i}: '${n}'...`                                     }[LANG]),
    bcDone:      c      => ({ TR:`Tamamlandı! İptal edilen: ${c}.`,                    EN:`Done! Cancelled: ${c}.`,                               PT:`Concluído! Cancelados: ${c}.`                                    }[LANG]),
    bcStopped:   c      => ({ TR:`Durduruldu. İptal edilen: ${c}.`,                    EN:`Stopped. Cancelled: ${c}.`,                            PT:`Parado. Cancelados: ${c}.`                                       }[LANG]),
    bcNoMatch:   c      => ({ TR:`Eşleşen teklif yok. İptal edilen: ${c}.`,            EN:`No more matches. Cancelled: ${c}.`,                    PT:`Sem correspondências. Cancelados: ${c}.`                         }[LANG]),
    deleteConfirmItems: names => ({ TR:`Silinecek eşyalar:\n${names.map(n=>'• '+n).join('\n')}\n\nDevam edilsin mi?`, EN:`Items to delete:\n${names.map(n=>'• '+n).join('\n')}\n\nProceed?`, PT:`Itens a excluir:\n${names.map(n=>'• '+n).join('\n')}\n\nContinuar?` }[LANG]),
};

// SETTINGS KEYS
const K = {
    pb:           'tvip_feat_pb',
    alignLeft:    'tvip_align_left',
    imageCtrl:    'tvip_feat_imgctrl',
    tableTools:   'tvip_feat_ttls',
    itemId:       'tvip_feat_itemid',
    colorScoring: 'tvip_feat_colorsc',
    autoDelivery: 'tvip_feat_autodlv',
    ticketPricer: 'tvip_feat_ticket',
    itemFilters:  'tvip_feat_itmf',
    repertoireF:  'tvip_feat_repf',
    bulkOffer:    'tvip_feat_bkoffer',
    bulkAccept:   'tvip_feat_bkaccept',
    cityShortcuts:'tvip_feat_citysc',
    onlyYoursSt:  'tvip_oy_state',
    hideOffBox:   'tvip_hideoff_chk',
    deleteConfirm:'tvip_delete_confirm',
};

// DATA KEYS (GM_setValue)
const DK = {
    TD:            'tvip_table_deny',
    OFFERED:       'tvip_offered_list',
    BO_ITEMS:      'tvip_bo_items',
    BO_RUNNING:    'tvip_bo_running',
    BO_PRICE:      'tvip_bo_price',
    BO_COUNT:      'tvip_bo_count',
    BO_TOTAL:      'tvip_bo_total',
    BO_LOG:        'tvip_bo_log',
    BO_CUSTOMERS:  'tvip_bo_customers',
    BO_FAVORITES:  'tvip_bo_favorites',
    BA_RUNNING:    'tvip_ba_running',
    BA_MAX_PRICE:  'tvip_ba_max_price',
    BA_ITEM_NAME:  'tvip_ba_item_name',
    BA_COUNT:      'tvip_ba_count',
    BA_SPENT:      'tvip_ba_spent',
    CITY_CUSTOM:   'tvip_city_custom',
    BC_RUNNING:    'tvip_bc_running',
    BC_ITEM_NAME:  'tvip_bc_item_name',
    BC_FILTER:     'tvip_bc_filter',
    BC_COUNT:      'tvip_bc_count',
    BO_BATCH_ID:   'tvip_bo_batch_id',
    BO_CUST_LOGS:  'tvip_bo_customer_logs',
};

// RAINBOW (scoring badges)
const RAINBOW = [
    ['#ff0000','#fff'],['#ff0036','#fff'],['#ff006c','#fff'],['#ff00a2','#fff'],
    ['#ff00d8','#fff'],['#f000ff','#fff'],['#ba00ff','#fff'],['#8400ff','#fff'],
    ['#4e00ff','#fff'],['#1900ff','#fff'],['#001dff','#fff'],['#0053ff','#fff'],
    ['#0089ff','#fff'],['#00bfff','#fff'],['#00f5ff','#000'],['#00ffd3','#000'],
    ['#00ff9d','#000'],['#00ff67','#000'],['#00ff31','#000'],['#05ff00','#000'],
    ['#3bff00','#000'],['#71ff00','#000'],['#a7ff00','#000'],['#ddff00','#000'],
    ['#ffeb00','#000'],['#ffb500','#000'],['#ff8000','#000']
];

// TABLE DENY
const getTableDeny  = ()         => { try { return JSON.parse(GM_getValue(DK.TD, '[]')); } catch { return []; } };
const addTableDeny  = (path, i)  => { const l = getTableDeny(), k = `${path}::${i}`; if (!l.includes(k)) { l.push(k); GM_setValue(DK.TD, JSON.stringify(l)); } };
const clearTableDeny = ()        => GM_setValue(DK.TD, '[]');

// BACKUP & RESTORE
const DB_KEYS_GM  = [DK.TD, DK.OFFERED, DK.CITY_CUSTOM,
                     DK.BA_RUNNING, DK.BA_MAX_PRICE, DK.BA_ITEM_NAME, DK.BA_COUNT, DK.BA_SPENT,
                     DK.BC_RUNNING, DK.BC_ITEM_NAME, DK.BC_FILTER, DK.BC_COUNT,
                     DK.BO_LOG, DK.BO_CUSTOMERS, DK.BO_FAVORITES, DK.BO_CUST_LOGS];
const DB_KEYS_CK  = Object.values(K);

const dbExport = () => {
    const data = { v: 1, script: 'helper', cookies: {}, gm: {} };
    DB_KEYS_CK.forEach(k => { const v = CK.get(k); if (v !== null) data.cookies[k] = v; });
    DB_KEYS_GM.forEach(k => { const v = GM_getValue(k, null); if (v !== null) data.gm[k] = v; });
    data.cookies['ppm_lang'] = CK.get('ppm_lang') || 'TR';
    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-helper-${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 applyMerge = () => {
                    close();
                    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 (GM_getValue(k, null) === null) GM_setValue(k, v); });
                    location.reload();
                };
                const applyReplace = () => {
                    close();
                    DB_KEYS_CK.forEach(k => CK.set(k, ''));
                    DB_KEYS_GM.forEach(k => GM_deleteValue(k));
                    if (data.cookies) Object.entries(data.cookies).forEach(([k, v]) => CK.set(k, v));
                    if (data.gm)      Object.entries(data.gm).forEach(([k, v])      => GM_setValue(k, v));
                    location.reload();
                };
                const row = mk('div'); row.style.cssText = 'display:flex;gap:8px';
                row.append(mkB(s('mergeLbl'), 'btn-b', applyMerge), mkB(s('replaceLbl'), 'btn-r', applyReplace), mkB(s('cancelLbl'), 'btn-grey', close));
                cont.appendChild(row);
            });
        };
        reader.readAsText(f);
    };
    inp.click();
};

// ALIGN LEFT
const applyAlignLeft = () => {
    if (!isOn(K.alignLeft)) return;
    document.head.appendChild(Object.assign(document.createElement('style'), {
        textContent: '#aspnetForm>div{margin-left:20px!important}body{justify-content:flex-start!important}'
    }));
};

// PROGRESS BAR
const applyProgressBar = () => {
    if (!isOn(K.pb)) return;
    document.querySelectorAll('.progressBar,.greenProgressBar,.blueProgressBar').forEach(e => {
        let pct = e.getAttribute('title') || ''; if (pct.includes(' ')) pct = pct.split(' ').pop();
        if (pct && !e.querySelector('.tvip-pct')) e.prepend(mk('div', 'tvip-pct', pct));
    });
    document.querySelectorAll('.plusMinusBar').forEach(e => {
        let pct = e.getAttribute('title') || ''; if (pct.includes(' ')) pct = pct.split(' ').pop();
        const tgt = e.querySelector('.negholder');
        if (tgt && pct && !tgt.querySelector('.tvip-pct')) tgt.prepend(mk('div', 'tvip-pct', pct));
    });
};

// ITEM ID
const applyItemId = () => {
    if (!isOn(K.itemId)) return;
    document.querySelectorAll("a[href*='/ItemDetails/']").forEach(link => {
        if (link.parentElement.querySelector('.tvip-item-id')) return;
        const m = link.href.match(/\/ItemDetails\/(\d+)/); if (!m) return;
        link.parentElement.appendChild(mk('span', 'tvip-item-id', `(#${m[1]})`));
    });
    const pm = location.href.match(/\/ItemDetails\/(\d+)/);
    if (pm && !document.querySelector('.tvip-item-page-id')) {
        const h2 = document.querySelector('h2');
        if (h2) h2.appendChild(mk('span', 'tvip-item-page-id', `ID: ${pm[1]}`));
    }
};

// COLOUR SCORING
const applyColorScoring = () => {
    if (!isOn(K.colorScoring)) return;
    document.querySelectorAll('a[href*="Help/Scoring/"]:not(.tvip-ng)').forEach(a => {
        const m = a.href.match(/\/Help\/Scoring\/(\d+)/i); if (!m) return;
        const idx = parseInt(m[1]) - 1; if (idx < 0 || idx > 26) return;
        const [bg, fg] = RAINBOW[idx];
        const badge = mk('span', 'tvip-badge', String(idx));
        badge.style.cssText = `background:${bg};color:${fg}`;
        a.classList.add('tvip-ng'); a.parentNode.insertBefore(badge, a.nextSibling);
    });
};

// AUTO DELIVERY
// FIX: Popmundo'nun disabled yaptığı kutucuğu zorla etkinleştirip işaretliyoruz.
// MutationObserver ile UpdatePanel yenilendiğinde de otomatik tekrar uygulanır.
const applyAutoDelivery = () => {
    if (!isOn(K.autoDelivery)) return;
    const tryCheck = () => {
        document.querySelectorAll("input[type=checkbox][id$='chkDelivery']").forEach(cb => {
            cb.removeAttribute('disabled');
            cb.checked = true;
        });
    };
    tryCheck();
    if (location.href.includes('/Character/OfferItem/')) {
        // MutationObserver: UpdatePanel DOM değiştiğinde re-check (en güvenilir yöntem)
        const observeTarget = document.getElementById('ctl00_cphLeftColumn_ctl00_updMain')
            || document.querySelector('form')
            || document.body;
        const mo = new MutationObserver(() => tryCheck());
        mo.observe(observeTarget, { childList: true, subtree: true });
        // Ek güvence: dropdown change olayında da çalıştır
        const dd = document.getElementById('ctl00_cphLeftColumn_ctl00_ddlItem');
        if (dd) dd.addEventListener('change', () => setTimeout(tryCheck, 300));
    }
};

// TICKET PRICER
const applyTicketPricer = () => {
    if (!isOn(K.ticketPricer)) return;
    if (!location.href.includes('/InviteArtist/')) return;
    const pm = {0:'5$',1:'5$',2:'5$',3:'7$',4:'9$',5:'12$',6:'15$',7:'18$',8:'20$',9:'25$',
                10:'30$',11:'35$',12:'40$',13:'45$',14:'50$',15:'65$',16:'70$',17:'75$',18:'80$',19:'85$',20:'90$'};
    document.querySelectorAll("a[href^='/World/Popmundo.aspx/Help/Scoring/']").forEach(link => {
        const raw = link.getAttribute('title'); if (!raw) return;
        const score = parseInt(raw.replace('/26','').trim()); if (isNaN(score)) return;
        if (pm[score] && !link.parentElement.querySelector('.tvip-ticket-price'))
            link.parentElement.appendChild(mk('span', 'tvip-ticket-price', pm[score]));
    });
};

// IMAGE CONTROL
const applyImageCtrl = () => {
    if (!isOn(K.imageCtrl)) return;
    const TIMEOUT_MS = 5000;
    const POPMUNDO_RE = /^https?:\/\/([^/]*\.)?popmundo\.com\//i;
    const placeholder = (img) => {
        if (img.dataset.tvipImgHandled) return;
        img.dataset.tvipImgHandled = '1';
        const src = img.src;
        const wrap = img.parentElement;
        const ph = mk('span', 'tvip-img-ph');
        ph.style.cssText = 'display:inline-flex;align-items:center;justify-content:center;width:'+(img.width||48)+'px;height:'+(img.height||48)+'px;min-width:24px;min-height:24px;background:#eee;border:1px dashed #ccc;border-radius:4px;font-size:13px;cursor:pointer;color:#aaa;box-sizing:border-box;';
        ph.textContent = '🖼️';
        ph.title = 'Yüklenemedi — yeniden denemek için tıkla';
        ph.onclick = () => {
            ph.remove();
            const ni = mk('img');
            ni.src = src + (src.includes('?') ? '&' : '?') + '_r=' + Date.now();
            ni.className = img.className;
            ni.style.cssText = img.style.cssText;
            if (wrap) wrap.insertBefore(ni, img.nextSibling);
        };
        img.style.display = 'none';
        if (wrap) wrap.insertBefore(ph, img.nextSibling);
    };

    const observe = (img) => {
        if (img.dataset.tvipImgHandled) return;
        if (!img.src || POPMUNDO_RE.test(img.src)) return;
        if (img.complete && img.naturalWidth > 0) return;
        let timer = setTimeout(() => placeholder(img), TIMEOUT_MS);
        img.addEventListener('load', () => clearTimeout(timer), { once: true });
        img.addEventListener('error', () => { clearTimeout(timer); placeholder(img); }, { once: true });
    };

    document.querySelectorAll('img').forEach(observe);
    const mo = new MutationObserver(muts => {
        muts.forEach(m => m.addedNodes.forEach(n => {
            if (n.tagName === 'IMG') observe(n);
            else if (n.querySelectorAll) n.querySelectorAll('img').forEach(observe);
        }));
    });
    mo.observe(document.body, { childList: true, subtree: true });
};

// ITEM FILTERS
const applyOnlyYours = () => {
    if (!isOn(K.itemFilters)) return;
    const list = document.getElementById('checkedlist'); if (!list) return;
    const filter = on => list.querySelectorAll('tr:not(:first-child)').forEach(row => {
        const inputs = row.querySelector('td:first-child')?.querySelectorAll('input') || [];
        row.style.display = (on && inputs.length < 2 && row.className !== 'group') ? 'none' : '';
    });
    const active = CK.get(K.onlyYoursSt) === '1';
    filter(active);
    const btn = mk('a', '', active ? s('btnShowAll') : s('btnOnlyTake'));
    btn.href = '#';
    btn.style.cssText = 'display:inline-block;margin-bottom:12px;padding:5px 10px;background:#17a2b8;color:#fff;text-decoration:none;border-radius:4px;font-size:12px';
    btn.onclick = e => {
        e.preventDefault();
        const cur = CK.get(K.onlyYoursSt) === '1';
        CK.set(K.onlyYoursSt, cur ? '0' : '1');
        btn.textContent = cur ? s('btnOnlyTake') : s('btnShowAll');
        filter(!cur);
    };
    list.before(btn);
};

const getOfferedList  = ()  => { try { return JSON.parse(GM_getValue(DK.OFFERED, '[]')); } catch { return []; } };
const saveOfferedList = (l) => GM_setValue(DK.OFFERED, JSON.stringify(l));

// ── Ortak Fiyat Geçmişi okuma/yazma (pop_shared_prices — Depot ile paylaşılır) ──
const _spDateH = () => { const d=new Date(); return `${String(d.getDate()).padStart(2,'0')}.${String(d.getMonth()+1).padStart(2,'0')}.${String(d.getFullYear()).slice(2)}`; };
// Depot ile aynı key formatı: Sözlükten EN adını çöz (pop_cat_data), yoksa raw adı kullan
// ── Dropdown option text → {name, variant} ayırıcı ──
// "Fıkra (hoş) (3 kullanımlık)" → { name:"Fıkra (hoş)", variant:"3 kullanımlık" }
// "Painkiller (5 uses left.)"   → { name:"Painkiller", variant:"5 uses left." }
// "Snowball"                    → { name:"Snowball", variant:"" }
const _splitItemVariant = (text) => {
    if (!text) return { name: '', variant: '' };
    // Son parantezdeki içerik: "N kullanımlık", "N kullanım", "N uses left.", "N uses"
    const usageRe = /\s*\((\d+\s*(?:kullanımlık|kullanım|uses?\s*(?:left\.?)?|кратного|раза|uso[s]?))\)\s*$/i;
    const m = text.match(usageRe);
    if (m) {
        return { name: text.slice(0, text.length - m[0].length).trim(), variant: m[1].trim() };
    }
    return { name: text.trim(), variant: '' };
};

let _catDictCache = null;
const _getCatDict  = () => {
    if (_catDictCache) return _catDictCache;
    try {
        const raw = GM_getValue('pop_cat_data', null);
        if (!raw) return null;
        _catDictCache = JSON.parse(raw).items || [];
        return _catDictCache;
    } catch { return null; }
};
const _spResolveKey = (name, variant) => {
    const cleanV = (variant||'').replace(/\.\s*$/,'').trim();
    try {
        const dict = _getCatDict();
        if (dict) {
            const normN = _normItemName(name);
            for (const d of dict) {
                const langs = [d.en, d.tr, d.pt_br, d.it, d.es].filter(Boolean);
                if (langs.some(l => _normItemName(l) === normN)) {
                    const enName = d.en || d.tr || name;
                    return `${enName}|||${cleanV}`;
                }
            }
        }
    } catch {}
    return `${name}|||${cleanV}`;
};
const addSharedPriceH = (rawText, variantHint, priceVal) => {
    if (!priceVal || priceVal < 10000) return;
    try {
        // Dropdown option text'ten isim ve varyantı ayır
        const split    = _splitItemVariant(rawText);
        const itemName = split.name || rawText;
        const variant  = split.variant || variantHint || '';

        const k    = _spResolveKey(itemName, variant);
        const dStr = _spDateH();

        // pop_shared_prices'a yaz
        const spRaw = GM_getValue('pop_shared_prices', null);
        const sp    = spRaw ? JSON.parse(spRaw) : {};
        if (!sp[k]) sp[k] = { prices: [], last: priceVal, lastD: dStr };
        if (!sp[k].prices.some(e => e.p === priceVal)) {
            sp[k].prices.push({ p: priceVal, d: dStr });
            if (sp[k].prices.length > 20) sp[k].prices.splice(0, sp[k].prices.length - 20);
        }
        sp[k].last = priceVal; sp[k].lastD = dStr;
        GM_setValue('pop_shared_prices', JSON.stringify(sp));

        // pop_price_data'ya da yaz (Depot uyumluluğu: aynı key ile orada da görünsün)
        const pdRaw   = GM_getValue('pop_price_data', null);
        const pd      = pdRaw ? JSON.parse(pdRaw) : {};
        const priceStr = priceVal >= 1000000 ? (priceVal/1000000)+'m'
                       : priceVal >= 1000    ? (priceVal/1000)+'k'
                       : String(priceVal);
        pd[k] = priceStr;
        GM_setValue('pop_price_data', JSON.stringify(pd));
    } catch {}
};

const getSharedPriceH = (rawText, variantHint) => {
    try {
        // isim + varyant ayır
        const split    = _splitItemVariant(rawText);
        const itemName = split.name || rawText;
        const variant  = split.variant || variantHint || '';

        // Denenecek key kombinasyonları
        const keys = [ _spResolveKey(itemName, variant) ];
        if (variant) keys.push(_spResolveKey(itemName, ''));
        // raw text fallback
        const rawK = rawText.trim() + '|||';
        if (!keys.includes(rawK)) keys.push(rawK);

        // pop_shared_prices
        const spRaw = GM_getValue('pop_shared_prices', null);
        if (spRaw) {
            const sp = JSON.parse(spRaw);
            for (const k of keys) { if (sp[k]) return sp[k]; }
        }

        // Fallback: pop_price_data (Depot'un eski fiyatları + Helper'ın pop_price_data'ya yazdıkları)
        const pdRaw = GM_getValue('pop_price_data', null);
        if (pdRaw) {
            const pd = JSON.parse(pdRaw);
            for (const k of keys) {
                const rawPrice = pd[k];
                if (rawPrice) {
                    const v = String(rawPrice).trim().toLowerCase().replace(',','.');
                    let n = parseFloat(v);
                    if (isNaN(n)) continue;
                    if (v.endsWith('m')) n = Math.round(n * 1_000_000);
                    else if (v.endsWith('k')) n = Math.round(n * 1_000);
                    if (n >= 10000)
                        return { prices: [{ p: n, d: '—' }], last: n, lastD: '—' };
                }
            }
        }
        return null;
    } catch { return null; }
};


const applyHideOffered = () => {
    if (!isOn(K.itemFilters)) return;
    if (isOn(K.bulkOffer)) return; // cleanHideOfferedCheckbox handles it
    const sel = document.querySelector('#ctl00_cphLeftColumn_ctl00_ddlItem'); if (!sel) return;
    getOfferedList().forEach(id => sel.querySelector(`option[value="${id}"]`)?.remove());
    document.querySelector('#ctl00_cphLeftColumn_ctl00_btnGive')?.addEventListener('click', () => {
        const id = sel.value; if (!id || id === '-1') return;
        const l = getOfferedList(); if (!l.includes(id)) { l.push(id); saveOfferedList(l); }
    });
};

// TABLE SORT
const applyTableSort = () => {
    if (!guard(K.tableTools)) return;
    const deny = getTableDeny();
    document.querySelectorAll('table').forEach((table, ti) => {
        if (deny.includes(`${location.pathname}::${ti}`)) return;
        const ths = table.querySelectorAll('tr:first-child th'); if (!ths.length) return;
        ths.forEach((th, ci) => {
            th.style.cursor = 'pointer'; let asc = true;
            th.addEventListener('click', () => {
                const body = table.querySelector('tbody') || table;
                const rows = [...body.querySelectorAll('tr')].filter(r => r.cells.length && !r.classList.contains('tvip-tbl-avg') && !r.classList.contains('tvip-heist-avg'));
                rows.sort((a, b) => {
                    const av = a.cells[ci]?.textContent.trim() || '', bv = b.cells[ci]?.textContent.trim() || '';
                    const an = parseFloat(av.replace(/[^\d.-]/g, '')), bn = parseFloat(bv.replace(/[^\d.-]/g, ''));
                    if (!isNaN(an) && !isNaN(bn)) return asc ? an - bn : bn - an;
                    return asc ? av.localeCompare(bv, LANG === 'PT' ? 'pt' : LANG === 'EN' ? 'en' : 'tr') : bv.localeCompare(av, LANG === 'PT' ? 'pt' : LANG === 'EN' ? 'en' : 'tr');
                });
                rows.forEach(r => body.appendChild(r)); asc = !asc;
                body.querySelectorAll('.tvip-tbl-avg,.tvip-heist-avg').forEach(r => body.appendChild(r));
            });
        });
    });
};

// TABLE SEARCH
const applyPageSearch = () => {
    if (!guard(K.tableTools)) return;
    const deny = getTableDeny();
    document.querySelectorAll('table').forEach((table, ti) => {
        if (table.closest('#tvip-bar,.tvip-ov') || table.querySelector('.tvip-search-done')) return;
        if (deny.includes(`${location.pathname}::${ti}`)) return;
        const rows = table.querySelectorAll('tbody tr'); if (rows.length < 8) return;
        const wrap = mk('div', 'tvip-search-wrap');
        const inp  = mk('input');
        inp.style.cssText = 'padding:4px 8px;border:1px solid #ccc;border-radius:4px;font-size:12px;width:180px';
        inp.placeholder = s('psPlh');
        const info = mk('span'); info.style.cssText = 'font-size:11px;color:#666';
        inp.addEventListener('input', () => {
            const q = inp.value.trim().toLowerCase(); let count = 0;
            rows.forEach(r => { const m = !q || r.textContent.toLowerCase().includes(q); r.style.display = m ? '' : 'none'; if (m) count++; });
            info.textContent = q ? `${count} ${s('psCount')}` : '';
        });
        const hideBtn = mkB('🚫', 'btn-sm btn-grey', () => {
            if (!confirm(s('tdConfirm'))) return;
            addTableDeny(location.pathname, ti); wrap.remove();
        });
        hideBtn.title = s('tdHide');
        wrap.append(inp, info, hideBtn); table.parentNode.insertBefore(wrap, table);
        table.appendChild(mk('span', 'tvip-search-done'));
    });
};

// TABLE AVG
const applyTableAvg = () => {
    if (!isOn(K.tableTools)) return;
    const table = document.querySelector('#tablefame');
    if (table && !table.querySelector('.tvip-tbl-avg')) {
        const rows = table.querySelectorAll('tbody tr');
        let fameSum = 0, mcSum = 0, count = 0;
        rows.forEach(row => {
            const sl  = row.querySelector("a[href*='/Help/Scoring/']");
            if (sl)  { const v = parseInt((sl.getAttribute('title')||'').replace('/26','').trim()); if (!isNaN(v)) fameSum += v; }
            const bar = row.querySelector("div[class$='ProgressBar']");
            if (bar) { const mv = bar.getAttribute('title')?.match(/(\d+)%/); if (mv) mcSum += parseInt(mv[1]); }
            count++;
        });
        if (count) {
            const row = mk('tr', 'even tvip-tbl-avg');
            row.innerHTML = `<td>${s('taAvg')}</td><td>💫 ${(fameSum/count).toFixed(2)}</td><td>🔛 ${(mcSum/count).toFixed(2)}%</td>`;
            table.querySelector('tbody').prepend(row);
        }
    }
    if (!location.href.includes('CrewReconnaissance')) return;
    const rows = document.querySelectorAll("tr[id*='repReconnaissance'][id*='trCrewMember']"); if (!rows.length) return;
    let sum = 0, cnt = 0;
    rows.forEach(r => { const sk = r.querySelector('span.sortkey'); if (sk) { const v = parseInt(sk.textContent); if (!isNaN(v)) { sum += v; cnt++; } } });
    if (!cnt) return;
    const tbody = rows[0].parentElement;
    if (!tbody.querySelector('.tvip-heist-avg')) {
        const row = mk('tr', 'tvip-heist-avg');
        row.innerHTML = `<td colspan="2">${s('taDiscAvg')}</td><td class="width20">${(sum/cnt).toFixed(0)}%</td><td class="width10"></td>`;
        tbody.prepend(row);
    }
};

// REPERTOIRE FILTER
const applyRepertoireFilter = () => {
    if (!guard(K.repertoireF, '/Artist/Repertoire/') || document.querySelector('#tvip-rep-filter')) return;
    const div = mk('div'); div.id = 'tvip-rep-filter';
    const labels = [s('repAll'), s('repMarket'), s('repNoMarket'), s('repJam'), s('repSetlist'), s('repSecret')];
    div.innerHTML = `<h3 style="margin-top:10px">${s('repTitle')}</h3>` +
        labels.map((l, i) => `<label><input type="radio" name="tvipRepF" value="${i}"> ${l}</label><br>`).join('');
    document.querySelector('div.box')?.insertAdjacentElement('beforebegin', div);
    const checks  = ['_', 'imgSongMarket', 'imgSongMarket', '_Rep_PracOn', '_imgDefaultSetlist', '_imgSecret'];
    const invert  = [true, true, false, true, true, true];
    const rowSel  = "tr[id^='ctl00_cphLeftColumn_ctl01_repArtistRepertoire_ct']";
    const update  = id => document.querySelectorAll(rowSel).forEach(tr => {
        tr.style.display = invert[id]
            ? (tr.innerHTML.includes(checks[id]) ? '' : 'none')
            : (tr.innerHTML.includes(checks[id]) ? 'none' : '');
    });
    div.querySelectorAll('input[name=tvipRepF]').forEach(r => r.addEventListener('change', () => update(parseInt(r.value))));
};

// TRADEHUB CORE
const SEL_DD    = '#ctl00_cphLeftColumn_ctl00_ddlItem';
const SEL_PRICE = '#ctl00_cphLeftColumn_ctl00_txtPriceTag';
const SEL_BTN   = '#ctl00_cphLeftColumn_ctl00_btnGive';
const SEL_FORM  = '#ctl00_cphLeftColumn_ctl00_updMain';

const removeWarningBox = () => {
    const h2 = [...document.querySelectorAll('.box h2')].find(h =>
        h.textContent.includes('Quem avisa') || h.textContent.includes('Uyarı') || h.textContent.includes('Warning'));
    h2?.closest('.box')?.remove();
};

const cleanHideOfferedCheckbox = () => {
    const hide = CK.get(K.hideOffBox) === '1';
    const deliveryParent = document.querySelector('#ctl00_cphLeftColumn_ctl00_chkDelivery')?.parentElement;
    if (!deliveryParent) return;
    const p   = document.createElement('p');
    const chk = document.createElement('input'); chk.type = 'checkbox'; chk.id = 'tvip-hideoff-chk'; chk.checked = hide;
    const lbl = document.createElement('label'); lbl.htmlFor = 'tvip-hideoff-chk';
    lbl.style.cssText = 'font-weight:normal;color:#333;font-size:12px;margin-left:4px';
    lbl.textContent   = s('hideOfferedChk');
    chk.addEventListener('change', () => { CK.set(K.hideOffBox, chk.checked ? '1' : '0'); location.reload(); });
    p.append(chk, lbl); deliveryParent.before(p);
    const sel = document.querySelector(SEL_DD);
    if (hide && sel) getOfferedList().forEach(id => sel.querySelector(`option[value="${id}"]`)?.remove());

    // Manuel teklif: btnGive click — log kaydı + delivery checkbox + offered list
    document.querySelector(SEL_BTN)?.addEventListener('click', () => {
        if (!sel) return;
        const id = sel.value; if (!id || id === '-1') return;

        // Offered list güncelle
        if (chk.checked) {
            const l = getOfferedList(); if (!l.includes(id)) { l.push(id); saveOfferedList(l); }
        }

        // Delivery checkbox — eğer AutoDelivery kapalıysa bile burada zorla uygula
        setTimeout(() => {
            document.querySelectorAll("input[type=checkbox][id$='chkDelivery']").forEach(cb => {
                cb.removeAttribute('disabled');
                cb.checked = true;
            });
        }, 100);

        // Log kaydı — BulkOffer çalışıyorsa log yazma (processNext zaten yazıyor)
        if (GM_getValue(DK.BO_RUNNING, false)) return;
        const itemText = sel.options[sel.selectedIndex]?.textContent?.trim() || `ID:${id}`;
        const priceEl  = document.querySelector(SEL_PRICE);
        // parsePriceStr kullan: "25.000" → 25000, parseInt ile 25 olarak yanlış parse ederdi
        const price    = parsePriceStr(priceEl?.value || '0');
        // Ortak fiyat geçmişine yaz (10.000 M$ altı yoksay)
        if (price >= 10000) addSharedPriceH(itemText, '', price);
        const ci       = getPageCharacterInfo();
        const entry = {
            timestamp:     new Date().toISOString(),
            itemName:      itemText,
            price,
            qty:           1,
            source:        'manual',
            characterName: ci?.characterName || '',
            characterId:   ci?.characterId   || '',
            characterUrl:  ci?.characterUrl  || '',
        };
        const log = getLog();
        log.push(entry);
        if (log.length > 500) log.splice(0, log.length - 500);
        saveLog(log);
        // Müşteri logu — kayıtlı müşteriyse ekle
        const customers = getCustomers();
        const existingCustomer = customers.find(c =>
            (ci?.characterId && c.characterId === ci.characterId) ||
            (ci?.characterName && c.name.toLowerCase() === ci.characterName.toLowerCase())
        );
        if (existingCustomer) {
            const custLogs = getCustomerLogs();
            custLogs.push({
                customerId:   existingCustomer.id,
                customerName: existingCustomer.name,
                timestamp:    entry.timestamp,
                itemName:     entry.itemName,
                qty:          1, price, source: 'manual'
            });
            if (custLogs.length > 1000) custLogs.splice(0, custLogs.length - 1000);
            saveCustomerLogs(custLogs);
        }
    });
};

const cleanOfferedOnItemsPage = () => {
    const offered = getOfferedList(); if (!offered.length) return;
    const box = [...document.querySelectorAll('.box h2')]
        .find(h => h.textContent.includes('Itens que você está ofertando') || h.textContent.includes('Items you are offering') || h.textContent.includes('Teklif ettiğiniz'))
        ?.closest('.box');
    if (!box) { saveOfferedList([]); return; }
    const currentIDs = [...box.querySelectorAll('a[href*="/Character/ItemDetails/"]')]
        .map(a => { const m = a.href.match(/\/ItemDetails\/(\d+)/); return m ? m[1] : null; }).filter(Boolean);
    if (!currentIDs.length) { saveOfferedList([]); return; }
    saveOfferedList(offered.filter(id => currentIDs.includes(id)));
};


// ── Fiyat parse yardımcıları ──
// "20k" → 20000, "1.5m" → 1500000, "2.000.000" → 2000000
const parsePriceStr = (v) => {
    const norm2 = v.toString().trim().toLowerCase().replace(/\s/g, '').replace(/,/g, '.');
    // Türkçe binlik nokta → kaldır (ama "1.5m" gibi ondalık noktayı koru)
    // Strateji: k/m yoksa tüm noktaları kaldır; varsa sadece bin ayraçlarını kaldır
    const hasSuffix = /[km]$/.test(norm2);
    let clean;
    if (hasSuffix) {
        // "1.500k" → "1500k", "1.5m" → "1.5m" (ondalık olabilir)
        const core = norm2.slice(0, -1);
        const dots = (core.match(/\./g) || []).length;
        // Birden fazla nokta → hepsi binlik ayraç → kaldır
        // Tek nokta ve rakamlar → ondalık
        const numeric = dots > 1 ? core.replace(/\./g, '') : core;
        clean = numeric + norm2.slice(-1);
    } else {
        clean = norm2.replace(/\./g, '');
    }
    const m = clean.match(/^(\d+(?:\.\d+)?)([km]?)$/);
    if (!m) return parseInt(clean.replace(/[^0-9]/g, '')) || 0;
    const n = parseFloat(m[1]);
    if (m[2] === 'k') return Math.round(n * 1_000);
    if (m[2] === 'm') return Math.round(n * 1_000_000);
    return Math.round(n);
};
const setupPriceInput = (inp) => {
    inp.addEventListener('blur', () => {
        const raw = parsePriceStr(inp.value);
        inp.dataset.raw = String(raw);
        inp.value = raw > 0 ? raw.toLocaleString('tr-TR') : '0';
    });
    inp.addEventListener('focus', () => {
        const raw = parseInt(inp.dataset.raw || '0') || parsePriceStr(inp.value);
        inp.value = raw > 0 ? String(raw) : '';
        inp.select();
    });
};
const getPriceVal = (inp) => {
    return parseInt(inp.dataset.raw || '0') || parsePriceStr(inp.value);
};

// ── Sayfa karakter bilgisini çek ──
const getPageCharacterInfo = () => {
    const urlM = location.href.match(/\/Character\/OfferItem\/(\d+)/);
    if (!urlM) return null;
    const characterId = urlM[1];
    // PreviewReport linkini içeren h2 → ismi al
    let characterName = '';
    const reportLink = document.querySelector('a[href*="/PreviewReport/50/"]');
    if (reportLink) {
        const h2 = reportLink.closest('h2');
        if (h2) characterName = h2.firstChild?.textContent?.trim() || '';
    }
    if (!characterName) {
        const h2 = document.querySelector('.charPresBox h2, .ofauto h2');
        if (h2) characterName = h2.firstChild?.textContent?.trim() || '';
    }
    const characterUrl = `/World/Popmundo.aspx/Character/OfferItem/${characterId}`;
    return { characterName, characterId, characterUrl };
};

// TRADEHUB — MÜŞTERİ SİSTEMİ
// ── Müşteri sistemi ──
const getCustomers  = () => { try { return JSON.parse(GM_getValue(DK.BO_CUSTOMERS, '[]')); } catch { return []; } };
const saveCustomers = cs => GM_setValue(DK.BO_CUSTOMERS, JSON.stringify(cs));

// Customer-specific logs (separate from main logs)
const getCustomerLogs = () => { try { return JSON.parse(GM_getValue(DK.BO_CUST_LOGS, '[]')); } catch { return []; } };
const saveCustomerLogs = logs => GM_setValue(DK.BO_CUST_LOGS, JSON.stringify(logs));

const saveCustomer = (name, characterId, characterUrl, note, logEntry = null) => {
    const cs = getCustomers();
    const existing = cs.find(c => c.name.toLowerCase() === name.toLowerCase() || (characterId && c.characterId === characterId));

    const _appendCustLog = (custId, custName) => {
        if (!logEntry) return;
        const custLogs = getCustomerLogs();
        custLogs.push({
            customerId:    custId,
            customerName:  custName,
            timestamp:     logEntry.timestamp,
            itemName:      logEntry.itemName,
            qty:           logEntry.qty   || 1,
            price:         logEntry.price || 0,
            source:        logEntry.source || 'bulk',
            batchNumber:   logEntry.batchNumber,
            totalInBatch:  logEntry.totalInBatch,
            batchId:       logEntry.batchId
        });
        if (custLogs.length > 1000) custLogs.splice(0, custLogs.length - 1000);
        saveCustomerLogs(custLogs);
    };

    if (existing) {
        if (name)              existing.name         = name;
        if (characterId)       existing.characterId  = characterId;
        if (characterUrl)      existing.characterUrl = characterUrl;
        if (note !== undefined) existing.note        = note;
        existing.updatedAt = new Date().toISOString();
        _appendCustLog(existing.id, existing.name);
    } else {
        const newCust = {
            id: Date.now().toString(), name: name || '', characterId: characterId || '',
            characterUrl: characterUrl || '', note: note || '',
            createdAt: new Date().toISOString(), updatedAt: new Date().toISOString()
        };
        cs.push(newCust);
        _appendCustLog(newCust.id, newCust.name);
    }
    if (cs.length > 200) cs.splice(200);
    saveCustomers(cs);
};

// ── Log yardımcıları ──
const getLog   = () => { try { return JSON.parse(GM_getValue(DK.BO_LOG, '[]')); } catch { return []; } };
const saveLog  = lg => GM_setValue(DK.BO_LOG, JSON.stringify(lg));

// Log'dan o müşteriye ait teklifleri bul (characterId veya isim eşleşmesi)
const getOffersForCustomer = (customer) => {
    const log = getLog();
    return log.filter(e =>
        (customer.characterId && e.characterId === customer.characterId) ||
        (customer.name && e.characterName && e.characterName.toLowerCase() === customer.name.toLowerCase())
    );
};

// ── Müşteri arama: isim + ID + not + teklif edilen eşya ──
const searchCustomers = (query) => {
    const cs = getCustomers();
    if (!query) return cs;
    const q = query.toLowerCase();
    const log = getLog();
    // Hangi characterId'lerde bu eşya geçiyor?
    const matchingIds = new Set(
        log.filter(e => (e.itemName || '').toLowerCase().includes(q)).map(e => e.characterId).filter(Boolean)
    );
    return cs.filter(c =>
        c.name.toLowerCase().includes(q) ||
        (c.characterId || '').includes(q) ||
        (c.note || '').toLowerCase().includes(q) ||
        (c.characterId && matchingIds.has(c.characterId))
    );
};

// TRADEHUB — FAVORİLER
// ── Favori sistemi ──
const getFavorites  = () => { try { return JSON.parse(GM_getValue(DK.BO_FAVORITES, '[]')); } catch { return []; } };
const saveFavorites = fs => GM_setValue(DK.BO_FAVORITES, JSON.stringify(fs));
const saveFavorite = (name, itemName, qty, price) => {
    const fs = getFavorites();
    const existing = fs.find(f => f.name.toLowerCase() === name.toLowerCase());
    if (existing) {
        Object.assign(existing, { itemName, qty, price, updatedAt: new Date().toISOString() });
    } else {
        fs.push({ id: Date.now().toString(), name, itemName, qty, price,
                  createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() });
    }
    if (fs.length > 100) fs.splice(100);
    saveFavorites(fs);
};

// Favoriler boşsa 2 varsayılan şablonu yükle
const seedDefaultFavorites = () => {
    if (getFavorites().length > 0) return;
    const defaults = [
        { name: ({ TR:'💊 Ağrı Kesici ×10', EN:'💊 Painkiller ×10', PT:'💊 Analgésico ×10' }[LANG]),
          itemName: ({ TR:'Ağrı kesici', EN:'Painkiller', PT:'Analgésico' }[LANG]),
          qty: 10, price: 50000 },
        { name: ({ TR:'⚗️ Zehir & Afrodizyak ×10', EN:'⚗️ Poison & Aphrodisiac ×10', PT:'⚗️ Veneno & Afrodisíaco ×10' }[LANG]),
          itemName: ({ TR:'Zehir, Afrodizyak', EN:'Poison, Aphrodisiac', PT:'Veneno, Afrodisíaco' }[LANG]),
          qty: 10, price: 30000 },
    ];
    defaults.forEach((d, i) => {
        const fs = getFavorites();
        fs.push({ id: (Date.now() + i).toString(), name: d.name, itemName: d.itemName,
                  qty: d.qty, price: d.price, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() });
        saveFavorites(fs);
    });
};

// ── Paylaşımlı form satırı yardımcısı ──
const _mkFormRow = (container, lbl, val, ph) => {
    const w = mk('div'); w.style.cssText = 'margin-bottom:7px';
    const l = mk('label', '', lbl); l.style.cssText = 'display:block;font-size:10px;font-weight:bold;color:#555;margin-bottom:2px';
    const i = mk('input'); i.type = 'text'; i.value = val || ''; if (ph) i.placeholder = ph;
    i.style.cssText = 'width:100%;padding:5px 8px;border:1px solid #ccc;border-radius:4px;font-size:12px;box-sizing:border-box';
    w.append(l, i); container.appendChild(w); return i;
};

// ── Müşteri Ekleme Formu (log satırından) ──
const openAddFromLogModal = (logEntry, onSaved) => {
    const modal   = mk('div'); modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.85);z-index:999999;display:flex;align-items:center;justify-content:center;padding:12px;box-sizing:border-box';
    const content = mk('div'); content.style.cssText = 'background:#fff;border-radius:8px;padding:18px;width:95vw;max-width:420px;box-sizing:border-box';

    const title = mk('h3', '', ({ TR:'👤 Müşteri Olarak Ekle', EN:'👤 Add as Customer', PT:'👤 Adicionar como Cliente' }[LANG]));
    title.style.cssText = 'margin:0 0 14px;font-size:14px;color:#333';

    const nameInp  = _mkFormRow(content, ({ TR:'Ad:', EN:'Name:', PT:'Nome:' }[LANG]), logEntry.characterName || '');
    const urlInp   = _mkFormRow(content, ({ TR:'Karakter Linki:', EN:'Character Link:', PT:'Link do Personagem:' }[LANG]), logEntry.characterUrl || '');
    const offerInp = _mkFormRow(content, ({ TR:'Eşya & Fiyat:', EN:'Item & Price:', PT:'Item & Preço:' }[LANG]),
                              `${logEntry.itemName || ''} × ${logEntry.qty || 1} @ ${(logEntry.price || 0).toLocaleString('tr-TR')} M$`);
    offerInp.readOnly = true; offerInp.style.background = '#f8f9fa';
    const noteInp  = _mkFormRow(content, ({ TR:'Not:', EN:'Note:', PT:'Nota:' }[LANG]), '');

    const btns = mk('div'); btns.style.cssText = 'display:flex;gap:8px;margin-top:4px';
    btns.append(
        mkB(({ TR:'Kaydet', EN:'Save', PT:'Salvar' }[LANG]), 'btn-g', () => {
            const name = nameInp.value.trim();
            if (!name) { nameInp.style.borderColor = '#dc3545'; return; }
            saveCustomer(name, logEntry.characterId || '', urlInp.value.trim(), noteInp.value.trim(), logEntry);
            modal.remove();
            if (onSaved) onSaved();
        }),
        mkB(({ TR:'İptal', EN:'Cancel', PT:'Cancelar' }[LANG]), 'btn-grey', () => modal.remove())
    );
    content.append(title, btns);
    modal.appendChild(content);
    document.body.appendChild(modal);
    modal.addEventListener('click', e => { if (e.target === modal) modal.remove(); });
};

// ── Customer History Modal ──
const openCustomerHistoryModal = (customer) => {
    document.getElementById('tvip-cust-hist-modal')?.remove(); // Varsa kapat, yeniden aç
    const modal = mk('div'); modal.id = 'tvip-cust-hist-modal'; modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.8);z-index:99999;display:flex;align-items:center;justify-content:center;padding:10px;box-sizing:border-box';
    const content = mk('div'); content.style.cssText = 'background:#fff;border-radius:8px;padding:16px;width:clamp(340px,80vw,700px);max-height:88vh;display:flex;flex-direction:column;box-sizing:border-box';

    const title = mk('h3', '', ({ TR:`📦 ${customer.name} — Tüm Teklifler`, EN:`📦 ${customer.name} — All Offers`, PT:`📦 ${customer.name} — Todas as Ofertas` }[LANG]));
    title.style.cssText = 'margin:0 0 12px;font-size:14px;color:#333;flex-shrink:0';

    const custLogs = getCustomerLogs()
        .filter(log => log.customerId === customer.id)
        .slice().reverse(); // En yeni önce

    if (!custLogs.length) {
        const msg = mk('p', '', ({ TR:'Bu müşteriye ait teklif kaydı bulunamadı.', EN:'No offer records found for this customer.', PT:'Nenhum registro encontrado.' }[LANG]));
        msg.style.cssText = 'text-align:center;color:#aaa;font-style:italic;font-size:12px;margin:20px 0';
        content.appendChild(msg);
    } else {
        // Özet satırı
        const totalQty   = custLogs.reduce((s, l) => s + (l.qty || 1), 0);
        const totalPrice = custLogs.reduce((s, l) => s + (l.price || 0) * (l.qty || 1), 0);
        const summary = mk('div');
        summary.style.cssText = 'display:flex;gap:12px;background:#f0f8ff;border:1px solid #bee3f8;border-radius:5px;padding:8px 10px;margin-bottom:10px;font-size:11px;flex-shrink:0';
        summary.innerHTML = `<span>📦 <b>${custLogs.length}</b> ${({ TR:'teklif', EN:'offers', PT:'ofertas' }[LANG])}</span><span>🔢 <b>${totalQty}</b> ${({ TR:'adet', EN:'qty', PT:'qtd' }[LANG])}</span><span>💰 <b>${totalPrice.toLocaleString('tr-TR')} M$</b> ${({ TR:'toplam', EN:'total', PT:'total' }[LANG])}</span>`;
        content.appendChild(summary);

        const tblWrap = mk('div');
        tblWrap.style.cssText = 'overflow-y:auto;flex:1;min-height:0';
        const tbl = document.createElement('table');
        tbl.style.cssText = 'width:100%;border-collapse:collapse;font-size:11px;table-layout:fixed;word-break:break-word;';
        const thead = document.createElement('thead');
        thead.innerHTML = `<tr style="background:#f8f9fa;border-bottom:2px solid #dee2e6;position:sticky;top:0">
            <th style="padding:5px 6px;text-align:left;color:#555;width:110px">${({ TR:'Tarih/Saat', EN:'Date/Time', PT:'Data/Hora' }[LANG])}</th>
            <th style="padding:5px 6px;text-align:left;color:#555">${({ TR:'Eşya', EN:'Item', PT:'Item' }[LANG])}</th>
            <th style="padding:5px 6px;text-align:right;color:#555;width:36px">${({ TR:'Adet', EN:'Qty', PT:'Qtd' }[LANG])}</th>
            <th style="padding:5px 6px;text-align:right;color:#555;width:80px">${({ TR:'Fiyat', EN:'Price', PT:'Preço' }[LANG])}</th>
            <th style="padding:5px 6px;text-align:center;color:#555;width:54px">${({ TR:'Kaynak', EN:'Src', PT:'Origem' }[LANG])}</th>
        </tr>`;
        tbl.appendChild(thead);
        const tbody = document.createElement('tbody');

        custLogs.forEach((log, i) => {
            const d = new Date(log.timestamp);
            const dateStr = `${d.getDate().toString().padStart(2,'0')}.${(d.getMonth()+1).toString().padStart(2,'0')}.${d.getFullYear().toString().slice(2)} ${d.getHours().toString().padStart(2,'0')}:${d.getMinutes().toString().padStart(2,'0')}:${d.getSeconds().toString().padStart(2,'0')}`;
            const tr = document.createElement('tr');
            tr.style.cssText = `border-bottom:1px solid #f0f0f0;${i%2===1?'background:#fafafa':''}`;

            const tdDate  = document.createElement('td'); tdDate.style.cssText  = 'padding:4px 6px;color:#888;font-size:10px;white-space:nowrap'; tdDate.textContent  = dateStr;
            const tdItem  = document.createElement('td'); tdItem.style.cssText  = 'padding:4px 6px;color:#333;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:0'; tdItem.textContent = log.itemName || '-';
            const tdQty   = document.createElement('td'); tdQty.style.cssText   = 'padding:4px 6px;text-align:right;color:#555;white-space:nowrap'; tdQty.textContent   = log.qty || 1;
            const tdPrice = document.createElement('td'); tdPrice.style.cssText = 'padding:4px 6px;text-align:right;color:#218838;white-space:nowrap;font-weight:500';
            tdPrice.textContent = ((log.price || 0) * (log.qty || 1)).toLocaleString('tr-TR') + ' M$';
            const tdSrc   = document.createElement('td'); tdSrc.style.cssText   = 'padding:4px 6px;text-align:center';
            tdSrc.innerHTML = log.source === 'manual'
                ? `<span style="background:#fff3cd;color:#856404;padding:1px 5px;border-radius:3px;font-size:9px">M</span>`
                : `<span style="background:#d4edda;color:#155724;padding:1px 5px;border-radius:3px;font-size:9px">T</span>`;

            tr.append(tdDate, tdItem, tdQty, tdPrice, tdSrc);
            tbody.appendChild(tr);
        });
        tbl.appendChild(tbody);
        tblWrap.appendChild(tbl);
        content.appendChild(tblWrap);
    }

    const closeBtn = mkB('✕ ' + ({ TR:'Kapat', EN:'Close', PT:'Fechar' }[LANG]), 'btn-grey', () => modal.remove());
    closeBtn.style.cssText = 'width:100%;margin-top:12px;flex-shrink:0';
    content.appendChild(closeBtn);

    modal.appendChild(content);
    document.body.appendChild(modal);
    modal.addEventListener('click', e => { if (e.target === modal) modal.remove(); });
};

// ── Müşteriler Modalı ──
const openCustomersModal = () => {
    const modal   = mk('div'); modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.8);z-index:99999;display:flex;align-items:center;justify-content:center;padding:10px;box-sizing:border-box';
    const content = mk('div'); content.style.cssText = 'background:#fff;border-radius:8px;padding:12px;width:min(640px,96vw);max-height:90vh;overflow-y:auto;overflow-x:hidden;box-sizing:border-box;position:relative;';

    const titleRow = mk('div'); titleRow.style.cssText = 'display:flex;align-items:center;gap:10px;margin-bottom:12px';
    const titleEl  = mk('h3', '', ({ TR:'👥 Müşteriler', EN:'👥 Customers', PT:'👥 Clientes' }[LANG]));
    titleEl.style.cssText = 'margin:0;font-size:15px;color:#333;flex:1';
    const searchInp = mk('input'); searchInp.type = 'text';
    searchInp.placeholder = ({ TR:'İsim, ID, eşya, not...', EN:'Name, ID, item, note...', PT:'Nome, ID, item, nota...' }[LANG]);
    searchInp.style.cssText = 'flex:2;padding:6px 8px;border:1px solid #ccc;border-radius:4px;font-size:12px';
    titleRow.append(titleEl, searchInp);
    content.appendChild(titleRow);

    // ── BÖLÜM 1: Kayıtlı Müşteriler ──
    const sec1Hdr = mk('div', '', ({ TR:'📋 Kayıtlı Müşteriler', EN:'📋 Registered Customers', PT:'📋 Clientes Registrados' }[LANG]));
    sec1Hdr.style.cssText = 'font-size:11px;font-weight:bold;text-transform:uppercase;color:#888;letter-spacing:.5px;margin-bottom:6px';
    content.appendChild(sec1Hdr);

    const custListEl = mk('div');
    custListEl.style.cssText = 'margin-bottom:16px';
    content.appendChild(custListEl);

    const formArea = mk('div'); formArea.style.cssText = 'margin-bottom:8px';
    content.appendChild(formArea);

    const addNewBtn = mkB(({ TR:'+ Yeni Müşteri', EN:'+ New Customer', PT:'+ Novo Cliente' }[LANG]), 'btn-g btn-sm', () => openAddCustForm());
    addNewBtn.style.marginBottom = '12px';
    content.appendChild(addNewBtn);

    // ── BÖLÜM 2: Son 500 Teklif ──
    const sec2Hdr = mk('div', '', ({ TR:'📦 Teklif Geçmişi (son 500)', EN:'📦 Offer History (last 500)', PT:'📦 Histórico (últimas 500)' }[LANG]));
    sec2Hdr.style.cssText = 'font-size:11px;font-weight:bold;text-transform:uppercase;color:#888;letter-spacing:.5px;margin-bottom:6px;border-top:1px solid #e9ecef;padding-top:12px';
    content.appendChild(sec2Hdr);

    const logListEl = mk('div'); content.appendChild(logListEl);

    const closeBtn = mkB('✕ ' + ({ TR:'Kapat', EN:'Close', PT:'Fechar' }[LANG]), 'btn-grey', () => modal.remove());
    closeBtn.style.cssText = 'width:100%;margin-top:14px';
    content.appendChild(closeBtn);

    // Müşteri listesi renderer
    const renderCustList = (query = '') => {
        custListEl.innerHTML = '';
        const list = searchCustomers(query);
        if (!list.length) {
            const msg = mk('p', '', ({ TR:'Müşteri bulunamadı.', EN:'No customers found.', PT:'Nenhum cliente encontrado.' }[LANG]));
            msg.style.cssText = 'text-align:center;color:#aaa;font-style:italic;font-size:12px;margin:6px 0';
            custListEl.appendChild(msg); return;
        }
        list.forEach(c => {
            const card = mk('div'); card.style.cssText = 'display:flex;align-items:center;gap:8px;padding:7px 10px;border:1px solid #e9ecef;border-radius:5px;margin-bottom:5px;background:#fafafa';
            const nameSpan = mk('span', '', c.name); nameSpan.style.cssText = 'font-weight:bold;font-size:12px;color:#333;flex:1;min-width:80px';
            const idSpan = mk('span', '', c.characterId ? `#${c.characterId}` : '');
            idSpan.style.cssText = 'font-size:10px;color:#aaa;min-width:60px';
            const noteSpan = mk('span', '', c.note || '');
            noteSpan.style.cssText = 'font-size:10px;color:#888;font-style:italic;flex:2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap';
            const btnRow = mk('div'); btnRow.style.cssText = 'display:flex;gap:3px;flex-shrink:0';

            if (c.characterUrl) {
                const lnk = mk('a', 'btn-sm btn-grey', '👤');
                lnk.href = c.characterUrl; lnk.target = '_blank'; lnk.style.textDecoration = 'none';
                lnk.title = c.characterUrl;
                btnRow.appendChild(lnk);
                
                // Bir Eşya Teklif Et butonu
                const offerBtn = mk('a', 'btn-sm btn-g', ({ TR:'Bir Eşya Teklif Et', EN:'Offer an Item', PT:'Oferecer um Item' }[LANG]));
                offerBtn.href = c.characterUrl.replace(/\/Character\/\d+/, (match) => {
                    const charId = match.match(/\d+/)[0];
                    return `/Character/OfferItem/${charId}`;
                });
                offerBtn.target = '_blank';
                offerBtn.style.textDecoration = 'none';
                offerBtn.style.marginLeft = '3px';
                offerBtn.style.fontSize = '9px';
                btnRow.appendChild(offerBtn);
            }
            const histBtn = mkB(({ TR:'Geçmiş Teklifler', EN:'Offer History', PT:'Histórico de Ofertas' }[LANG]), 'btn-sm btn-b', () => {
                openCustomerHistoryModal(c);
            });
            histBtn.style.fontSize = '10px';
            btnRow.append(
                histBtn,
                mkB(({ TR:'Düzenle', EN:'Edit', PT:'Editar' }[LANG]), 'btn-sm btn-b', () => openEditCustForm(c)),
                mkB(({ TR:'Sil', EN:'Del', PT:'Exc' }[LANG]), 'btn-sm btn-r', () => {
                    if (!confirm(({ TR:`"${c.name}" silinsin mi?`, EN:`Delete "${c.name}"?`, PT:`Excluir "${c.name}"?` }[LANG]))) return;
                    saveCustomers(getCustomers().filter(x => x.id !== c.id));
                    renderCustList(searchInp.value.trim());
                })
            );
            card.append(nameSpan, idSpan, noteSpan, btnRow);
            custListEl.appendChild(card);
        });
    };

    // Yeni müşteri formu
    const openAddCustForm = () => {
        formArea.innerHTML = '';
        formArea.style.cssText = 'background:#f0f8ff;border:1px solid #bee3f8;border-radius:6px;padding:12px;margin-bottom:8px';
        const ci = getPageCharacterInfo();
        const nI = _mkFormRow(formArea, ({ TR:'Ad:', EN:'Name:', PT:'Nome:' }[LANG]), ci?.characterName || '');
        const uI = _mkFormRow(formArea, ({ TR:'Eşya Teklif Linki:', EN:'Offer Item Link:', PT:'Link de Oferta:' }[LANG]), ci?.characterUrl || '');
        const tI = _mkFormRow(formArea, ({ TR:'Not:', EN:'Note:', PT:'Nota:' }[LANG]), '');
        const btns = mk('div'); btns.style.cssText = 'display:flex;gap:6px';
        btns.append(
            mkB(({ TR:'Kaydet', EN:'Save', PT:'Salvar' }[LANG]), 'btn-g btn-sm', () => {
                const name = nI.value.trim();
                if (!name) { nI.style.borderColor = '#dc3545'; return; }
                saveCustomer(name, ci?.characterId || '', uI.value.trim(), tI.value.trim());
                formArea.innerHTML = ''; formArea.style.cssText = 'margin-bottom:8px';
                renderCustList(searchInp.value.trim());
            }),
            mkB(({ TR:'İptal', EN:'Cancel', PT:'Cancelar' }[LANG]), 'btn-grey btn-sm', () => { formArea.innerHTML = ''; formArea.style.cssText = 'margin-bottom:8px'; })
        );
        formArea.appendChild(btns);
    };
    const openEditCustForm = (c) => {
        formArea.innerHTML = '';
        formArea.style.cssText = 'background:#fffbe6;border:1px solid #f0c040;border-radius:6px;padding:12px;margin-bottom:8px';
        const nI = _mkFormRow(formArea, ({ TR:'Ad:', EN:'Name:', PT:'Nome:' }[LANG]), c.name);
        const uI = _mkFormRow(formArea, ({ TR:'Eşya Teklif Linki:', EN:'Offer Item Link:', PT:'Link de Oferta:' }[LANG]), c.characterUrl);
        const tI = _mkFormRow(formArea, ({ TR:'Not:', EN:'Note:', PT:'Nota:' }[LANG]), c.note);
        const btns = mk('div'); btns.style.cssText = 'display:flex;gap:6px';
        btns.append(
            mkB(({ TR:'Kaydet', EN:'Save', PT:'Salvar' }[LANG]), 'btn-g btn-sm', () => {
                const cs = getCustomers(); const found = cs.find(x => x.id === c.id);
                if (found) { found.name = nI.value.trim() || found.name; found.characterUrl = uI.value.trim(); found.note = tI.value.trim(); found.updatedAt = new Date().toISOString(); saveCustomers(cs); }
                formArea.innerHTML = ''; formArea.style.cssText = 'margin-bottom:8px';
                renderCustList(searchInp.value.trim());
            }),
            mkB(({ TR:'İptal', EN:'Cancel', PT:'Cancelar' }[LANG]), 'btn-grey btn-sm', () => { formArea.innerHTML = ''; formArea.style.cssText = 'margin-bottom:8px'; })
        );
        formArea.appendChild(btns);
    };

    // Log listesi renderer
    const renderLogList = (query = '') => {
        logListEl.innerHTML = '';
        const log = getLog().slice().reverse(); // En yeni önde
        const q = query.toLowerCase();
        const filtered = q ? log.filter(e =>
            (e.itemName || '').toLowerCase().includes(q) ||
            (e.characterName || '').toLowerCase().includes(q) ||
            (e.characterId || '').includes(q)
        ) : log;
        const shown = filtered.slice(0, 500);

        if (!shown.length) {
            logListEl.appendChild(mk('p', '', ({ TR:'Teklif kaydı yok.', EN:'No offer records.', PT:'Sem registros.' }[LANG]))).style.cssText = 'text-align:center;color:#aaa;font-style:italic;font-size:12px;margin:6px 0';
            return;
        }

        // Group by customer
        const grouped = {};
        shown.forEach(e => {
            const key = e.characterName || 'Bilinmeyen';
            if (!grouped[key]) {
                grouped[key] = {
                    characterName: e.characterName,
                    characterUrl: e.characterUrl,
                    characterId: e.characterId,
                    items: [],
                    totalQty: 0,
                    totalPrice: 0
                };
            }
            grouped[key].items.push(e);
            grouped[key].totalQty += e.qty || 1;
            grouped[key].totalPrice += (e.price || 0) * (e.qty || 1);
        });

        // Add buttons at the top
        const btnRow = mk('div'); btnRow.style.cssText = 'display:flex;gap:8px;margin-bottom:10px;justify-content:flex-end';
        const addCustBtn = mkB(({ TR:'+ Müşteri Ekle', EN:'+ Add Customer', PT:'+ Add Cliente' }[LANG]), 'btn-sm btn-g', () => openAddCustForm());
        const clearLogBtn = mkB(({ TR:'Logları Temizle', EN:'Clear Logs', PT:'Limpar Logs' }[LANG]), 'btn-sm btn-r', () => {
            if (!confirm(({ TR:'Tüm ana log kayıtları silinsin mi? (Müşteri kayıtları silinmez)', EN:'Clear all main log records? (Customer records will not be deleted)', PT:'Limpar todos os registros principais? (Registros de clientes não serão excluídos)' }[LANG]))) return;
            saveLog([]); renderLogList(query);
        });
        btnRow.append(addCustBtn, clearLogBtn);
        logListEl.appendChild(btnRow);

        // Tablo
        const tblWrap = document.createElement('div');
        tblWrap.style.cssText = 'overflow-x:hidden;width:100%';
        const tbl = document.createElement('table');
        tbl.style.cssText = 'width:100%;border-collapse:collapse;font-size:11px;table-layout:fixed;word-break:break-word;';
        const thead = document.createElement('thead');
        thead.innerHTML = `<tr style="background:#f8f9fa;border-bottom:2px solid #dee2e6">
            <th style="padding:5px 6px;text-align:left;color:#555;white-space:nowrap">${({ TR:'Tarih', EN:'Date', PT:'Data' }[LANG])}</th>
            <th style="padding:5px 6px;text-align:left;color:#555">${({ TR:'Eşya', EN:'Item', PT:'Item' }[LANG])}</th>
            <th style="padding:5px 6px;text-align:right;color:#555;white-space:nowrap">${({ TR:'Adet', EN:'Qty', PT:'Qtd' }[LANG])}</th>
            <th style="padding:5px 6px;text-align:right;color:#555;white-space:nowrap">${({ TR:'Fiyat', EN:'Price', PT:'Preço' }[LANG])}</th>
            <th style="padding:5px 6px;text-align:left;color:#555">${({ TR:'Karakter', EN:'Character', PT:'Personagem' }[LANG])}</th>
            <th style="padding:5px 6px"></th>
        </tr>`;
        tbl.appendChild(thead);
        const tbody = document.createElement('tbody');

        Object.values(grouped).forEach((group, i) => {
            const firstItem = group.items[0];
            const d = new Date(firstItem.timestamp);
            const dateStr = `${d.getDate().toString().padStart(2,'0')}.${(d.getMonth()+1).toString().padStart(2,'0')}.${d.getFullYear().toString().slice(2)}`;
            const tr = document.createElement('tr');
            tr.style.cssText = `border-bottom:1px solid #f0f0f0;${i%2===1?'background:#fafafa':''}`;

            const tdDate = document.createElement('td'); tdDate.style.cssText = 'padding:4px 6px;white-space:nowrap;color:#888'; tdDate.textContent = dateStr;
            const tdItem = document.createElement('td'); tdItem.style.cssText = 'padding:4px 6px;color:#333;max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap'; tdItem.textContent = firstItem.itemName || '-';
            const tdQty  = document.createElement('td'); tdQty.style.cssText = 'padding:4px 6px;text-align:right;color:#555;white-space:nowrap'; tdQty.textContent = group.totalQty;
            const tdPrice= document.createElement('td'); tdPrice.style.cssText = 'padding:4px 6px;text-align:right;color:#218838;white-space:nowrap;font-weight:500';
            tdPrice.textContent = group.totalPrice.toLocaleString('tr-TR') + ' M$';
            const tdChar = document.createElement('td'); tdChar.style.cssText = 'padding:4px 6px;max-width:140px';
            if (group.characterName && group.characterUrl) {
                const a = mk('a', '', group.characterName); a.href = group.characterUrl; a.target = '_blank';
                a.style.cssText = 'color:#17a2b8;text-decoration:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block;max-width:130px';
                tdChar.appendChild(a);
            } else {
                tdChar.textContent = group.characterName || '-';
                tdChar.style.color = '#aaa';
            }
            const tdAct = document.createElement('td'); tdAct.style.cssText = 'padding:4px 6px;text-align:right;white-space:nowrap';
            const addB = mkB(({ TR:'+ Müşteri', EN:'+ Customer', PT:'+ Cliente' }[LANG]), 'btn-sm btn-g', () => {
                openAddFromLogModal(firstItem, () => renderCustList(searchInp.value.trim()));
            });
            addB.style.fontSize = '10px';
            tdAct.appendChild(addB);
            tr.append(tdDate, tdItem, tdQty, tdPrice, tdChar, tdAct);
            tbody.appendChild(tr);
        });
        tbl.appendChild(tbody);
        tblWrap.appendChild(tbl);
        logListEl.appendChild(tblWrap);
    };

    searchInp.addEventListener('input', () => {
        const q = searchInp.value.trim();
        renderCustList(q);
        renderLogList(q);
    });

    modal.appendChild(content);
    document.body.appendChild(modal);
    modal.addEventListener('click', e => { if (e.target === modal) modal.remove(); });

    renderCustList();
    renderLogList();
};

// ── Favoriler Modalı ──
const openFavoritesModal = (fillForm) => {
    const modal   = mk('div'); modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.8);z-index:99999;display:flex;align-items:center;justify-content:center;padding:10px;box-sizing:border-box';
    const content = mk('div'); content.style.cssText = 'background:#fff;border-radius:8px;padding:16px;width:95vw;max-width:500px;max-height:85vh;overflow-y:auto;box-sizing:border-box';

    const title = mk('h3', '', ({ TR:'⭐ Favoriler', EN:'⭐ Favorites', PT:'⭐ Favoritos' }[LANG])); title.style.cssText = 'margin:0 0 12px;font-size:15px;color:#333';

    const addForm = mk('div'); addForm.style.cssText = 'background:#f8f9fa;border:1px solid #e9ecef;border-radius:6px;padding:12px;margin-bottom:12px';
    const mkFld = (lbl, type) => {
        const d = mk('div'); d.style.cssText = 'display:flex;flex-direction:column';
        const l = mk('label', '', lbl); l.style.cssText = 'font-size:10px;font-weight:bold;color:#666;margin-bottom:2px';
        const i = mk('input'); i.type = type;
        i.style.cssText = 'padding:5px 6px;border:1px solid #ccc;border-radius:4px;font-size:12px;box-sizing:border-box';
        d.append(l, i); return { wrap: d, inp: i };
    };
    const row1 = mk('div'); row1.style.cssText = 'display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:8px';
    const {wrap: wn, inp: nameInp} = mkFld(({ TR:'Favori Adı:', EN:'Favorite Name:', PT:'Nome do Favorito:' }[LANG]), 'text');
    const {wrap: wi, inp: itemInp} = mkFld(({ TR:'Eşya Adı (prefix):', EN:'Item Name (prefix):', PT:'Nome do Item:' }[LANG]), 'text');
    row1.append(wn, wi);
    const row2 = mk('div'); row2.style.cssText = 'display:grid;grid-template-columns:80px 1fr auto;gap:8px;align-items:end;margin-bottom:8px';
    const {wrap: wq, inp: qtyInp}   = mkFld(({ TR:'Adet:', EN:'Qty:', PT:'Qtd:' }[LANG]), 'number');
    const {wrap: wp, inp: priceInp} = mkFld(({ TR:'Fiyat:', EN:'Price:', PT:'Preço:' }[LANG]), 'text');
    qtyInp.min = '1'; qtyInp.max = '100'; qtyInp.value = '1';
    priceInp.placeholder = '50k / 2m...';
    setupPriceInput(priceInp);
    const addBtn = mkB(({ TR:'Ekle', EN:'Add', PT:'Adicionar' }[LANG]), 'btn-g btn-sm', () => {
        const nm = nameInp.value.trim(), itm = itemInp.value.trim();
        const qty = parseInt(qtyInp.value), price = getPriceVal(priceInp);
        if (!nm || !itm || isNaN(qty) || qty < 1 || price < 0) { alert(({ TR:'Tüm alanları doldurun.', EN:'Fill in all fields.', PT:'Preencha todos.' }[LANG])); return; }
        if (editingId) {
            const fs = getFavorites(); const f = fs.find(x => x.id === editingId);
            if (f) { f.name = nm; f.itemName = itm; f.qty = qty; f.price = price; f.updatedAt = new Date().toISOString(); saveFavorites(fs); }
            clearEditMode();
        } else {
            saveFavorite(nm, itm, qty, price);
            nameInp.value = ''; itemInp.value = ''; qtyInp.value = '1'; priceInp.value = '0'; priceInp.dataset.raw = '0';
        }
        renderFavList();
    });
    addBtn.style.alignSelf = 'flex-end';
    row2.append(wq, wp, addBtn);
    addForm.append(row1, row2);

    const listEl = mk('div'); listEl.style.cssText = 'max-height:350px;overflow-y:auto';
    let editingId = null;
    const clearEditMode = () => {
        editingId = null;
        nameInp.value = ''; itemInp.value = ''; qtyInp.value = '1'; priceInp.value = '0'; priceInp.dataset.raw = '0';
        addBtn.textContent = ({ TR:'Ekle', EN:'Add', PT:'Adicionar' }[LANG]);
        addForm.style.borderColor = '';
    };
    const setEditMode = (fav) => {
        editingId = fav.id;
        nameInp.value = fav.name; itemInp.value = fav.itemName; qtyInp.value = fav.qty;
        priceInp.dataset.raw = String(fav.price);
        priceInp.value = fav.price > 0 ? fav.price.toLocaleString('tr-TR') : '0';
        addBtn.textContent = ({ TR:'💾 Güncelle', EN:'💾 Update', PT:'💾 Atualizar' }[LANG]);
        addForm.style.borderColor = '#f0c040';
        addForm.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
    };
    const renderFavList = () => {
        listEl.innerHTML = '';
        const fs = getFavorites();
        if (!fs.length) { listEl.appendChild(mk('p', '', ({ TR:'Henüz favori yok.', EN:'No favorites yet.', PT:'Nenhum favorito.' }[LANG]))).style.cssText = 'text-align:center;color:#999;font-style:italic;font-size:12px'; return; }
        fs.forEach((fav, idx) => {
            const row = mk('div'); row.style.cssText = 'display:flex;justify-content:space-between;align-items:center;background:#fff;border:1px solid #e9ecef;border-radius:5px;padding:8px 10px;margin-bottom:6px';
            const info = mk('div');
            info.innerHTML = `<strong style="font-size:12px;color:#333">${fav.name}</strong><div style="font-size:11px;color:#888">${fav.itemName || ({ TR:'(eşya adı girilmedi)', EN:'(no item name)', PT:'(sem nome)' }[LANG])} &nbsp;×${fav.qty}&nbsp; @ ${fav.price > 0 ? fav.price.toLocaleString('tr-TR') + ' M$' : ({ TR:'Ücretsiz', EN:'Free', PT:'Grátis' }[LANG])}</div>`;
            const btns = mk('div'); btns.style.cssText = 'display:flex;gap:4px';
            btns.append(
                mkB(({ TR:'Kullan', EN:'Use', PT:'Usar' }[LANG]), 'btn-sm btn-b', () => {                    if (fav.itemName === '__SEND_ALL__') {
                        // Tüm dropdown eşyalarını sepete ücretsiz ekle
                        modal.remove();
                        const allItems = filterItems('', 9999);
                        if (!allItems.length) {
                            alert(({ TR:'Teklif edilecek eşya bulunamadı.', EN:'No items available to offer.', PT:'Nenhum item disponível.' }[LANG]));
                            return;
                        }
                        const existingCart = getCart();
                        allItems.forEach(it => {
                            if (it.value) existingCart.push({ name: it.text, qty: 1, price: 0 });
                        });
                        saveCart(existingCart);
                        renderCart();
                        document.getElementById('tvip-bo-status').textContent =
                            ({ TR:`${allItems.length} eşya sepete eklendi (0 M$).`, EN:`${allItems.length} items added to cart (free).`, PT:`${allItems.length} itens adicionados (grátis).` }[LANG]);
                    } else {
                        fillForm(fav.itemName, fav.qty, fav.price); modal.remove();
                    }
                }),
                mkB(({ TR:'Düzenle', EN:'Edit', PT:'Editar' }[LANG]), 'btn-sm btn-b', () => setEditMode(fav)),
                mkB(({ TR:'Sil', EN:'Del', PT:'Exc' }[LANG]), 'btn-sm btn-r', () => {
                    if (!confirm(({ TR:'Bu favori silinsin mi?', EN:'Delete this favorite?', PT:'Excluir?' }[LANG]))) return;
                    const fs2 = getFavorites(); fs2.splice(idx, 1); saveFavorites(fs2);
                    if (editingId === fav.id) clearEditMode();
                    renderFavList();
                })
            );
            row.append(info, btns); listEl.appendChild(row);
        });
    };

    const closeBtn = mkB('✕ ' + ({ TR:'Kapat', EN:'Close', PT:'Fechar' }[LANG]), 'btn-grey', () => modal.remove());
    closeBtn.style.cssText = 'width:100%;margin-top:12px';
    content.append(title, addForm, listEl, closeBtn);
    modal.appendChild(content);
    document.body.appendChild(modal);
    modal.addEventListener('click', e => { if (e.target === modal) modal.remove(); });
    renderFavList();
};

// TRADEHUB — LOG MODALı (Bulk Offer Log)
const openLogModal = () => {
    const modal   = mk('div'); modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.8);z-index:99999;display:flex;align-items:center;justify-content:center;padding:10px;box-sizing:border-box';
    const content = mk('div'); content.style.cssText = 'background:#fff;border-radius:8px;padding:12px;width:min(640px,96vw);max-height:88vh;display:flex;flex-direction:column;box-sizing:border-box;overflow-x:hidden;';

    const titleRow = mk('div'); titleRow.style.cssText = 'display:flex;align-items:center;gap:10px;margin-bottom:12px;flex-shrink:0';
    const titleEl = mk('h3', '', s('boLogTitle'));
    titleEl.style.cssText = 'margin:0;font-size:14px;color:#333;flex:1';
    const clearBtn = mkB(({ TR:'Temizle', EN:'Clear', PT:'Limpar' }[LANG]), 'btn-sm btn-r', () => {
        if (!confirm(({ TR:'Tüm log silinsin mi?', EN:'Clear all logs?', PT:'Limpar tudo?' }[LANG]))) return;
        saveLog([]); modal.remove();
    });
    titleRow.append(titleEl, clearBtn);
    content.appendChild(titleRow);

    const log = getLog().slice().reverse();

    if (!log.length) {
        content.appendChild(mk('p', '', ({ TR:'Henüz gönderim kaydı yok.', EN:'No offer records yet.', PT:'Nenhum registro.' }[LANG]))).style.cssText = 'color:#aaa;font-style:italic;font-size:12px';
    } else {
        const tblWrap = mk('div'); tblWrap.style.cssText = 'overflow-y:auto;overflow-x:hidden;flex:1;min-height:0';
        const tbl = document.createElement('table');
        tbl.style.cssText = 'width:100%;border-collapse:collapse;font-size:11px;table-layout:fixed';
        const thead = document.createElement('thead');
        thead.innerHTML = `<tr style="background:#f0f0f0;border-bottom:2px solid #dee2e6;position:sticky;top:0">
            <th style="padding:5px 8px;text-align:left;color:#555">#</th>
            <th style="padding:5px 8px;text-align:left;color:#555">${({ TR:'Tarih/Saat', EN:'Date/Time', PT:'Data/Hora' }[LANG])}</th>
            <th style="padding:5px 8px;text-align:left;color:#555">${({ TR:'Eşya', EN:'Item', PT:'Item' }[LANG])}</th>
            <th style="padding:5px 8px;text-align:right;color:#555">${({ TR:'Adet', EN:'Qty', PT:'Qtd' }[LANG])}</th>
            <th style="padding:5px 8px;text-align:right;color:#555">${({ TR:'Fiyat (M$)', EN:'Price (M$)', PT:'Preço (M$)' }[LANG])}</th>
            <th style="padding:5px 8px;text-align:center;color:#555">${({ TR:'Kaynak', EN:'Source', PT:'Origem' }[LANG])}</th>
            <th style="padding:5px 8px;text-align:left;color:#555">${({ TR:'Karakter', EN:'Character', PT:'Personagem' }[LANG])}</th>
        </tr>`;
        tbl.appendChild(thead);
        // colgroup ile sütun genişlikleri sabit → yatay scroll yok
        tbl.insertAdjacentHTML('afterbegin', `<colgroup>
            <col style="width:30px">
            <col style="width:120px">
            <col style="width:auto">
            <col style="width:36px">
            <col style="width:80px">
            <col style="width:58px">
            <col style="width:110px">
        </colgroup>`);
        const tbody = document.createElement('tbody');
        log.forEach((e, i) => {
            const d = new Date(e.timestamp);
            const dateStr = `${d.getDate().toString().padStart(2,'0')}.${(d.getMonth()+1).toString().padStart(2,'0')}.${d.getFullYear().toString().slice(2)} ${d.getHours().toString().padStart(2,'0')}:${d.getMinutes().toString().padStart(2,'0')}:${d.getSeconds().toString().padStart(2,'0')}`;
            const tr = document.createElement('tr');
            tr.style.cssText = `border-bottom:1px solid #f0f0f0;${i%2===1?'background:#fafafa':''}`;

            const tdN     = document.createElement('td'); tdN.style.cssText = 'padding:4px 6px;color:#aaa;font-size:10px'; tdN.textContent = i+1;
            const tdDate  = document.createElement('td'); tdDate.style.cssText = 'padding:4px 6px;color:#888;font-size:10px;white-space:nowrap'; tdDate.textContent = dateStr;
            const tdItem  = document.createElement('td'); tdItem.style.cssText = 'padding:4px 6px;color:#333;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:0'; tdItem.textContent = e.itemName || '-';
            const tdQty   = document.createElement('td'); tdQty.style.cssText = 'padding:4px 6px;text-align:right;color:#555;white-space:nowrap'; tdQty.textContent = e.qty || 1;
            const tdPrice = document.createElement('td'); tdPrice.style.cssText = 'padding:4px 6px;text-align:right;color:#218838;font-weight:500;white-space:nowrap';
            tdPrice.textContent = e.price ? e.price.toLocaleString('tr-TR') : '-';
            const tdSrc   = document.createElement('td'); tdSrc.style.cssText = 'padding:4px 6px;text-align:center';
            tdSrc.innerHTML = e.source === 'manual'
                ? `<span style="background:#fff3cd;color:#856404;padding:1px 5px;border-radius:3px;font-size:9px">M</span>`
                : `<span style="background:#d4edda;color:#155724;padding:1px 5px;border-radius:3px;font-size:9px">T</span>`;
            const tdChar  = document.createElement('td'); tdChar.style.cssText = 'padding:4px 6px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:0';
            if (e.characterName && e.characterUrl) {
                const a = mk('a', '', e.characterName); a.href = e.characterUrl; a.target = '_blank';
                a.style.cssText = 'color:#17a2b8;text-decoration:none';
                tdChar.appendChild(a);
            } else {
                tdChar.textContent = e.characterName || '-'; tdChar.style.color = '#aaa';
            }
            tr.append(tdN, tdDate, tdItem, tdQty, tdPrice, tdSrc, tdChar);
            tbody.appendChild(tr);
        });
        tbl.appendChild(tbody);
        tblWrap.appendChild(tbl);
        content.appendChild(tblWrap);
    }

    const closeBtn = mkB('✕ ' + ({ TR:'Kapat', EN:'Close', PT:'Fechar' }[LANG]), 'btn-grey', () => modal.remove());
    closeBtn.style.cssText = 'margin-top:12px;width:100%;flex-shrink:0';
    content.appendChild(closeBtn);
    modal.appendChild(content);
    document.body.appendChild(modal);
    modal.addEventListener('click', e => { if (e.target === modal) modal.remove(); });
};

// TRADEHUB — EŞYA FİLTRELEME
// ── Dil-normalize helper ──
const _normItemName = str => (str||'').toLowerCase()
    .replace(/[çÇ]/g,'c').replace(/[şŞ]/g,'s').replace(/[ğĞ]/g,'g')
    .replace(/[ıİ]/g,'i').replace(/[öÖ]/g,'o').replace(/[üÜ]/g,'u')
    .replace(/[^a-z0-9 ]/g,' ').replace(/\s+/g,' ').trim();

// ── Item filtering helper ──
const filterItems = (searchTerm, limit = 9999) => {
    const dropdown = document.querySelector(SEL_DD);
    const terms = searchTerm.split(',').map(t => t.trim().toLowerCase());
    const filtered = [];
    const seenValues = new Set();

    // --- Katman 1: Sayfa dropdown'u ---
    if (dropdown) {
        const options = [...dropdown.querySelectorAll('option')].filter(opt => opt.value && opt.value !== '-1');
        for (const option of options) {
            const text = option.textContent.toLowerCase();
            for (const term of terms) {
                let match = false;
                if (term.startsWith('*') && term.endsWith('*')) {
                    match = text.includes(term.slice(1,-1));
                } else if (term.startsWith('*')) {
                    match = text.endsWith(term.slice(1));
                } else if (term.endsWith('*')) {
                    match = text.startsWith(term.slice(0,-1));
                } else {
                    match = text.startsWith(term);
                }
                if (match) {
                    filtered.push({ value: option.value, text: option.textContent.trim(), source: 'page' });
                    seenValues.add(option.value);
                    break;
                }
            }
            if (filtered.length >= limit) break;
        }
    }

    return filtered;
};

// TRADEHUB — TOPLU TEKLİF UI
const setupBulkOfferUI = () => {
    if (!guard(K.bulkOffer, '/Character/OfferItem/')) return;
    const formBox = document.querySelector(SEL_FORM)?.closest('.box');
    if (!formBox || document.getElementById('tvip-bulk-offer-ui')) return;

    seedDefaultFavorites(); // varsayılan favorileri yükle (sadece boşsa)
    removeWarningBox();
    cleanHideOfferedCheckbox();

    const charInfo = getPageCharacterInfo();

    const ui = mk('div'); ui.id = 'tvip-bulk-offer-ui';
    const charInfoHtml = charInfo
        ? `<div id="tvip-bo-char-info" style="font-size:11px;color:#495057;padding:5px 8px;background:#f0f8ff;border:1px solid #bee3f8;border-radius:4px;margin-bottom:8px">
               👤 <a href="${charInfo.characterUrl}" target="_blank" style="color:#17a2b8;text-decoration:none;font-weight:bold">${charInfo.characterName}</a>
               <span style="color:#aaa;margin-left:6px">#${charInfo.characterId}</span>
           </div>`
        : '';

    ui.innerHTML = `
        <h3 style="margin:0 0 8px;font-size:13px">${({ TR:'Toplu Teklif', EN:'Bulk Offer', PT:'Oferta em Massa' }[LANG])}</h3>
        <div class="tvip-bulk-panel">
            ${charInfoHtml}
            <div class="tvip-bulk-grid">
                <div class="tvip-bulk-field" style="grid-column:1/-1"><label>${({ TR:'Eşya adı (virgülle ayırarak birden fazla girilebilir):', EN:'Item name (comma-separated for multiple):', PT:'Nome do item (vários separados por vírgula):' }[LANG])}</label><input type="text" id="tvip-bo-name" placeholder="${({ TR:'Örn: Ağrı kesici   veya   Zehir, Afrodizyak', EN:'e.g. Painkiller   or   Poison, Aphrodisiac', PT:'Ex: Analgésico   ou   Veneno, Afrodisíaco' }[LANG])}" style="font-size:14px;padding:8px"></div>
                <div class="tvip-bulk-field"><label>${({ TR:'Adet:', EN:'Quantity:', PT:'Quantidade:' }[LANG])}</label><input type="number" id="tvip-bo-qty" min="1" max="100" value="1"></div>
                <div class="tvip-bulk-field">
                    <label>${({ TR:'Fiyat (M$):', EN:'Price (M$):', PT:'Preço (M$):' }[LANG])}</label>
                    <div style="display:flex;gap:4px;align-items:center">
                        <input type="text" id="tvip-bo-price" placeholder="50k / 1.5m / 0" style="flex:1">
                        <button id="tvip-bo-lastprice" type="button" title="${({ TR:'Son teklif fiyatını gir', EN:'Use last offer price', PT:'Usar último preço' }[LANG])}" style="padding:4px 8px;border:1px solid #e67e22;border-radius:4px;background:#fef9e7;color:#7d4f00;cursor:pointer;font-size:11px;white-space:nowrap">💰</button>
                    </div>
                </div>
                <div class="tvip-bulk-field" style="grid-column:1/-1">
                    <div style="display:flex;align-items:center;gap:8px">
                        <label style="font-size:11px;color:#555;flex-shrink:0">${({ TR:'Aralık (sn):', EN:'Interval (s):', PT:'Intervalo (s):' }[LANG])}</label>
                        <input type="number" id="tvip-bo-delay" min="1" max="10" value="1" style="width:56px;padding:4px 6px;border:1px solid #ccc;border-radius:4px;font-size:12px">
                        <span style="font-size:10px;color:#aaa">± 1 sn random</span>
                    </div>
                </div>
            </div>
            <div class="tvip-bulk-actions">
                <button id="tvip-bo-preview" class="btn-b">${({ TR:'👁 Önizle', EN:'👁 Preview', PT:'👁 Prévia' }[LANG])}</button>
                <button id="tvip-bo-start"   class="tvip-btn-start">${({ TR:'▶ Teklif Et', EN:'▶ Offer', PT:'▶ Ofertar' }[LANG])}</button>
                <button id="tvip-bo-stop"    class="tvip-btn-stop">${({ TR:'■ Durdur', EN:'■ Stop', PT:'■ Parar' }[LANG])}</button>
            </div>
            <div class="tvip-bulk-actions" style="justify-content:center">
                <button id="tvip-bo-favorites" class="btn-b btn-sm">${({ TR:'⭐ Favoriler', EN:'⭐ Favorites', PT:'⭐ Favoritos' }[LANG])}</button>
                <button id="tvip-bo-customers" class="btn-g btn-sm">${({ TR:'👥 Müşteriler', EN:'👥 Customers', PT:'👥 Clientes' }[LANG])}</button>
                <button id="tvip-bo-log"       class="btn-grey btn-sm">📋 Log</button>
            </div>
            <div id="tvip-bo-status" class="tvip-status">${({ TR:'Hazır.', EN:'Ready.', PT:'Pronto.' }[LANG])}</div>
        </div>`;
    formBox.insertBefore(ui, formBox.firstChild);

    const priceInp = document.getElementById('tvip-bo-price');
    setupPriceInput(priceInp);

    // ── Auto-Suggest: isim girilirken öneri listesi göster ──
    const nameInp = document.getElementById('tvip-bo-name');
    const suggBox = mk('div'); suggBox.id = 'tvip-bo-sugg';
    suggBox.style.cssText = 'display:none;position:fixed;background:#fff;border:1px solid #ccc;border-radius:4px;box-shadow:0 4px 12px rgba(0,0,0,.2);z-index:999999;max-height:200px;overflow-y:auto;font-size:12px;box-sizing:border-box;';
    document.body.appendChild(suggBox);
    const _positionSugg = () => {
        const r = nameInp.getBoundingClientRect();
        suggBox.style.left  = r.left + 'px';
        suggBox.style.top   = (r.bottom + 2) + 'px';
        suggBox.style.width = r.width + 'px';
    };

    const _fillSugg = (items) => {
        if (!items.length) { suggBox.style.display='none'; return; }
        suggBox.innerHTML = '';
        items.slice(0,12).forEach(item => {
            const row = mk('div'); row.style.cssText = 'padding:6px 10px;cursor:pointer;border-bottom:1px solid #f0f0f0;display:flex;justify-content:space-between;align-items:center;gap:8px;';
            const nameSpan = mk('span','',item.text);
            nameSpan.style.cssText = 'flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;';
            const metaSpan = mk('span');
            const spData = getSharedPriceH(item.text, '');
            const metaParts = [];
            if (spData) metaParts.push('<span style="color:#e67e22;font-weight:bold;cursor:pointer;" class="tvip-price-badge" data-name="'+encodeURIComponent(item.text)+'" data-variant="">'+spData.last.toLocaleString('tr-TR')+' M$</span>');
            metaSpan.innerHTML = metaParts.join(' ');
            metaSpan.style.cssText = 'font-size:11px;color:#888;white-space:nowrap;flex-shrink:0;';
            row.append(nameSpan, metaSpan);
            row.addEventListener('mouseenter', () => row.style.background='#f0f8ff');
            row.addEventListener('mouseleave', () => row.style.background='');
            row.addEventListener('mousedown', (e) => {
                e.preventDefault();
                nameInp.value = item.text.replace(/\s*\(.*\)\s*$/, ''); // Varyant parantezini çıkar
                suggBox.style.display = 'none';
                // Fiyat badge varsa price input'unu doldur
                if (spData) {
                    priceInp.dataset.raw = String(spData.last);
                    priceInp.value = spData.last.toLocaleString('tr-TR');
                }
                nameInp.focus();
            });
            suggBox.appendChild(row);
        });
        // Fiyat badge tıklama: geçmiş popup
        suggBox.querySelectorAll('.tvip-price-badge').forEach(badge => {
            badge.addEventListener('mousedown', (e) => {
                e.stopPropagation();
                _showPriceHistoryPopup(decodeURIComponent(badge.dataset.name), decodeURIComponent(badge.dataset.variant), badge);
            });
        });
        _positionSugg(); suggBox.style.display = 'block';
    };

    let _suggTimer = null;
    nameInp.addEventListener('input', () => {
        clearTimeout(_suggTimer);
        const val = nameInp.value.trim();
        if (!val) { suggBox.style.display='none'; return; }
        _suggTimer = setTimeout(() => {
            // Sadece ilk terimi suggest için kullan (virgülle ayrılmış çoklu girişte son terim)
            const terms = val.split(',');
            const lastTerm = terms[terms.length-1].trim();
            if (!lastTerm) return;
            const results = filterItems(lastTerm, 12);
            _fillSugg(results);
        }, 250);
    });
    nameInp.addEventListener('blur', () => setTimeout(() => { suggBox.style.display='none'; }, 200));

    // ── Fiyat Badge & Geçmiş Popup ──
    const _showPriceHistoryPopup = (name, variant, anchorEl) => {
        document.getElementById('tvip-price-hist-popup')?.remove();
        const sp = getSharedPriceH(name, variant);
        const popup = mk('div'); popup.id = 'tvip-price-hist-popup';
        popup.style.cssText = 'position:fixed;z-index:999999;background:#fff;border:1px solid #f0ad4e;border-radius:6px;padding:10px;min-width:180px;box-shadow:0 4px 16px rgba(0,0,0,.25);font-size:11px;';
        const rect = anchorEl.getBoundingClientRect();
        popup.style.left = Math.min(rect.left, window.innerWidth-200)+'px';
        popup.style.top  = (rect.bottom+4)+'px';
        const title = mk('div','',({ TR:'📋 Fiyat Geçmişi', EN:'📋 Price History', PT:'📋 Histórico de Preços' }[LANG]));
        title.style.cssText = 'font-weight:bold;color:#e67e22;margin-bottom:6px;';
        popup.appendChild(title);
        if (!sp || !sp.prices.length) {
            popup.appendChild(mk('p','',({ TR:'Fiyat geçmişi yok.', EN:'No price history.', PT:'Sem histórico.' }[LANG]))).style.cssText='color:#aaa;font-style:italic;font-size:11px;';
        } else {
            [...sp.prices].reverse().forEach(e => {
                const row = mk('div'); row.style.cssText = 'display:flex;justify-content:space-between;gap:10px;padding:3px 0;border-bottom:1px solid #f0f0f0;cursor:pointer;';
                const pSpan = mk('span','',e.p.toLocaleString('tr-TR')+' M$'); pSpan.style.cssText='color:#e67e22;font-weight:bold;';
                const dSpan = mk('span','',e.d); dSpan.style.cssText='color:#aaa;';
                row.append(pSpan, dSpan);
                row.title = ({ TR:'Tıkla: Bu fiyatı gir', EN:'Click: Use this price', PT:'Clique: Usar este preço' }[LANG]);
                row.addEventListener('click', () => {
                    priceInp.dataset.raw = String(e.p);
                    priceInp.value = e.p.toLocaleString('tr-TR');
                    popup.remove();
                    // Fiyat input'una odaklan
                    priceInp.dispatchEvent(new Event('blur'));
                });
                popup.appendChild(row);
            });
        }
        const closeB = mkB('✕', 'btn-grey btn-sm', () => popup.remove());
        closeB.style.cssText = 'margin-top:6px;width:100%;';
        popup.appendChild(closeB);
        document.body.appendChild(popup);
        setTimeout(() => {
            const handler = (e) => { if (!popup.contains(e.target)) { popup.remove(); document.removeEventListener('click', handler); } };
            document.addEventListener('click', handler);
        }, 50);
    };

    // ── Fiyat Input badge: son fiyatı yanında göster ──
    const _updatePriceBadge = () => {
        document.getElementById('tvip-bo-price-hint')?.remove();
        const rawName = nameInp.value.split(',')[0].trim();
        if (!rawName) return;
        const sp = getSharedPriceH(rawName, '');
        if (!sp) return;
        const hint = mk('span'); hint.id = 'tvip-bo-price-hint';
        hint.style.cssText = 'font-size:10px;color:#e67e22;cursor:pointer;margin-left:4px;white-space:nowrap;';
        hint.textContent = ({ TR:'Son:', EN:'Last:', PT:'Último:' }[LANG])+' '+sp.last.toLocaleString('tr-TR')+' M$';
        hint.title = ({ TR:'Geçmişi gör', EN:'View history', PT:'Ver histórico' }[LANG]);
        hint.addEventListener('click', () => _showPriceHistoryPopup(rawName, '', hint));
        // Fiyat alanının hemen altına ekle
        const priceField = priceInp.closest('.tvip-bulk-field') || priceInp.parentElement.parentElement || priceInp.parentElement;
        priceField.appendChild(hint);
    };
    nameInp.addEventListener('change', _updatePriceBadge);
    nameInp.addEventListener('blur',   _updatePriceBadge);

    // ── Sepet Sistemi ──
    const CART_KEY = 'tvip_bo_cart';
    const getCart  = () => { try { return JSON.parse(GM_getValue(CART_KEY,'[]')); } catch { return []; } };
    const saveCart = c  => GM_setValue(CART_KEY, JSON.stringify(c));
    const clearCart = () => GM_setValue(CART_KEY, '[]');

    const renderCart = () => {
        const cart = getCart();
        const cartEl = document.getElementById('tvip-bo-cart-list');
        if (!cartEl) return;
        cartEl.innerHTML = '';
        if (!cart.length) {
            cartEl.appendChild(mk('p','',({ TR:'Sepet boş.', EN:'Cart empty.', PT:'Carrinho vazio.' }[LANG]))).style.cssText='color:#aaa;font-size:11px;text-align:center;margin:4px 0;';
            document.getElementById('tvip-bo-cart-offer').disabled = true;
            return;
        }
        cart.forEach((item, idx) => {
            const row = mk('div'); row.style.cssText='display:flex;justify-content:space-between;align-items:center;padding:3px 0;border-bottom:1px solid #e9ecef;font-size:11px;gap:6px;';
            const info = mk('span','',`${item.name} ×${item.qty} — ${item.price > 0 ? item.price.toLocaleString('tr-TR')+' M$' : ({ TR:'Ücretsiz', EN:'Free', PT:'Grátis' }[LANG])}`);
            info.style.cssText = 'flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#333;';
            const delBtn = mkB('✕','btn-sm btn-r', () => {
                const c = getCart(); c.splice(idx,1); saveCart(c); renderCart();
            }); delBtn.style.cssText='padding:1px 5px!important;font-size:10px!important;';
            row.append(info, delBtn);
            cartEl.appendChild(row);
        });
        document.getElementById('tvip-bo-cart-offer').disabled = false;
    };

    // Sepet UI — status div'inin altına ekle
    const cartSection = mk('div'); cartSection.id = 'tvip-bo-cart-section';
    cartSection.style.cssText = 'margin-top:8px;padding:8px;background:#f8f9fa;border:1px solid #dee2e6;border-radius:5px;';
    const cartTitle = mk('div','',({ TR:'🛒 Sepet', EN:'🛒 Cart', PT:'🛒 Carrinho' }[LANG]));
    cartTitle.style.cssText = 'font-size:11px;font-weight:bold;color:#495057;margin-bottom:5px;display:flex;justify-content:space-between;align-items:center;';
    const clearCartBtn = mkB(({ TR:'Temizle', EN:'Clear', PT:'Limpar' }[LANG]),'btn-sm btn-r',() => { if(confirm(({ TR:'Sepet temizlensin mi?', EN:'Clear cart?', PT:'Limpar carrinho?' }[LANG]))){ clearCart(); renderCart(); } });
    clearCartBtn.style.cssText = 'padding:1px 6px!important;font-size:10px!important;';
    cartTitle.appendChild(clearCartBtn);
    const cartList = mk('div'); cartList.id = 'tvip-bo-cart-list';
    cartList.style.cssText = 'max-height:150px;overflow-y:auto;';
    const cartActions = mk('div'); cartActions.style.cssText = 'display:flex;gap:6px;margin-top:6px;';
    const addToCartBtn = mkB(({ TR:'+ Sepete Ekle', EN:'+ Add to Cart', PT:'+ Adicionar' }[LANG]),'btn-b btn-sm',() => {
        const name  = nameInp.value.trim();
        const qty   = parseInt(document.getElementById('tvip-bo-qty')?.value||'1',10)||1;
        const price = getPriceVal(priceInp);
        if (!name) { document.getElementById('tvip-bo-status').textContent = s('boErrName'); return; }
        const cart = getCart();
        cart.push({ name, qty, price });
        saveCart(cart);
        renderCart();
        document.getElementById('tvip-bo-status').textContent = ({ TR:`"${name}" sepete eklendi.`, EN:`"${name}" added to cart.`, PT:`"${name}" adicionado.` }[LANG]);
    });
    const offerCartBtn = mkB(({ TR:'▶ Tümünü Teklif Et', EN:'▶ Offer All', PT:'▶ Ofertar Todos' }[LANG]),'btn-g btn-sm',() => {
        const cart = getCart(); if(!cart.length) return;
        // İlk elemanı forma yükle, kalanları cart'ta bırak (processNext her seferinde cart'ı azaltır)
        const first = cart[0]; cart.splice(0,1); saveCart(cart);
        nameInp.value = first.name;
        document.getElementById('tvip-bo-qty').value = String(first.qty);
        priceInp.dataset.raw = String(first.price);
        priceInp.value = first.price > 0 ? first.price.toLocaleString('tr-TR') : '0';
        document.getElementById('tvip-bo-start').click();
    });
    offerCartBtn.id = 'tvip-bo-cart-offer';
    cartActions.append(addToCartBtn, offerCartBtn);
    cartSection.append(cartTitle, cartList, cartActions);
    // Status div'den sonra ekle
    document.getElementById('tvip-bo-status')?.after(cartSection);
    renderCart();

    // 💰 Son fiyatı gir
    document.getElementById('tvip-bo-lastprice').onclick = () => {
        const rawName = nameInp.value.split(',')[0].trim();
        if (!rawName) return;
        const sp = getSharedPriceH(rawName, '');
        if (sp && sp.last) {
            priceInp.dataset.raw = String(sp.last);
            priceInp.value = sp.last.toLocaleString('tr-TR');
            priceInp.dispatchEvent(new Event('blur'));
        } else {
            // Geçmiş yoksa fiyat geçmişi popup'ı aç
            _showPriceHistoryPopup(rawName, '', document.getElementById('tvip-bo-lastprice'));
        }
    };

    const setDisabled = on => {
        document.getElementById('tvip-bo-start').disabled    =  on;
        document.getElementById('tvip-bo-stop').disabled     = !on;
        document.getElementById('tvip-bo-name').disabled     =  on;
        document.getElementById('tvip-bo-qty').disabled      =  on;
        document.getElementById('tvip-bo-price').disabled    =  on;
        document.getElementById('tvip-bo-delay').disabled    =  on;
        document.getElementById('tvip-bo-preview').disabled  =  on;
        document.getElementById('tvip-bo-favorites').disabled=  on;
        const lpBtn = document.getElementById('tvip-bo-lastprice'); if(lpBtn) lpBtn.disabled = on;
        const cartOfferBtn = document.getElementById('tvip-bo-cart-offer');
        if (cartOfferBtn) cartOfferBtn.disabled = on || !getCart().length;
    };

    const stopOffer = () => {
        GM_deleteValue(DK.BO_ITEMS); GM_deleteValue(DK.BO_RUNNING);
        GM_deleteValue(DK.BO_PRICE); GM_deleteValue(DK.BO_TOTAL);
        document.getElementById('tvip-global-stop')?.remove();
        setDisabled(false);
        // Sepette daha ürün varsa otomatik devam et
        const nextCart = getCart();
        if (nextCart.length) {
            const next = nextCart[0]; nextCart.splice(0,1); saveCart(nextCart);
            setTimeout(() => {
                nameInp.value = next.name;
                document.getElementById('tvip-bo-qty').value = String(next.qty);
                priceInp.dataset.raw = String(next.price);
                priceInp.value = next.price > 0 ? next.price.toLocaleString('tr-TR') : '0';
                renderCart();
                document.getElementById('tvip-bo-start').click();
            }, 1500);
        } else {
            renderCart();
        }
    };

    const processNext = () => {
        showGlobalStop(() => document.getElementById('tvip-bo-stop')?.click());
        if (!GM_getValue(DK.BO_RUNNING, false)) { setDisabled(false); return; }
        let items = [];
        try { items = JSON.parse(GM_getValue(DK.BO_ITEMS, '[]')); } catch {}
        const status = document.getElementById('tvip-bo-status');

        if (!items.length) {
            status.textContent = ({ TR:'Tüm teklifler tamamlandı!', EN:'All offers completed!', PT:'Todas as ofertas concluídas!' }[LANG]);
            document.getElementById('tvip-global-stop')?.remove();
            stopOffer(); return;
        }

        const dropdown = document.querySelector(SEL_DD);
        const offerBtn = document.querySelector(SEL_BTN);
        const priceEl  = document.querySelector(SEL_PRICE);
        if (!dropdown || !offerBtn) {
            status.textContent = ({ TR:'Kritik hata: Sayfa öğeleri kayboldu.', EN:'Critical error: Page elements gone.', PT:'Erro crítico: Elementos desapareceram.' }[LANG]);
            stopOffer(); return;
        }

        const item  = items.shift();
        const price = parseInt(GM_getValue(DK.BO_PRICE, '0')) || 0;
        const total = parseInt(GM_getValue(DK.BO_TOTAL, '0'));
        const count = total - items.length;
        if (priceEl) priceEl.value = String(price);
        GM_setValue(DK.BO_ITEMS, JSON.stringify(items));
        status.textContent = `${({ TR:'Teklif', EN:'Offering', PT:'Ofertando' }[LANG])} ${count}/${total}: '${item.text}'...`;
        dropdown.value = item.value;
        dropdown.dispatchEvent(new Event('change', { bubbles: true }));
        if (dropdown.value !== item.value) {
            status.textContent = `Atlandı: '${item.text}'`;
            setTimeout(processNext, 1000); return;
        }
        const l = getOfferedList(); if (!l.includes(item.value)) { l.push(item.value); saveOfferedList(l); }

        // Log kaydını click'ten ÖNCE al (sayfa yenilenmeden)
        const ci = getPageCharacterInfo();
        const entry = {
            timestamp: new Date().toISOString(),
            itemName: item.text, price, qty: 1,
            source: 'bulk',
            batchNumber: count, totalInBatch: total,
            batchId: GM_getValue(DK.BO_BATCH_ID, ''),
            characterName: ci?.characterName || '',
            characterId:   ci?.characterId   || '',
            characterUrl:  ci?.characterUrl  || '',
        };
        // Ana log
        const log = getLog();
        log.push(entry);
        if (log.length > 500) log.splice(0, log.length - 500);
        saveLog(log);
        // Ortak fiyat geçmişine yaz
        if (price >= 10000) addSharedPriceH(item.text, '', price);
        // Müşteri logu — müşteri kaydı varsa ekle
        const customers = getCustomers();
        const existingCustomer = customers.find(c =>
            (ci?.characterId && c.characterId === ci.characterId) ||
            (ci?.characterName && c.name.toLowerCase() === ci.characterName.toLowerCase())
        );
        if (existingCustomer) {
            const custLogs = getCustomerLogs();
            custLogs.push({
                customerId: existingCustomer.id,
                customerName: existingCustomer.name,
                timestamp: entry.timestamp,
                itemName: entry.itemName,
                qty: 1, price,
                source: 'bulk',
                batchNumber: count, totalInBatch: total,
                batchId: entry.batchId
            });
            if (custLogs.length > 1000) custLogs.splice(0, custLogs.length - 1000);
            saveCustomerLogs(custLogs);
        }

        // Süre: delay input değeri ± 1 sn random
        const delaySec = Math.max(1, Math.min(10, parseInt(document.getElementById('tvip-bo-delay')?.value || '1')));
        setTimeout(() => {
            if (!GM_getValue(DK.BO_RUNNING, false)) { setDisabled(false); return; }
            // Teslimat checkbox'ı zorla işaretle — Otomatik Teslimat açık olsun olmasın, her toplu teklifte uygula
            document.querySelectorAll("input[type=checkbox][id$='chkDelivery']").forEach(cb => {
                cb.removeAttribute('disabled');
                cb.checked = true;
            });
            offerBtn.click();
        }, Math.max(0, delaySec * 1000 + (Math.random() * 2000 - 1000)));
    };

    // ── Button handlers ──
    document.getElementById('tvip-bo-preview').onclick = (e) => {
        e.preventDefault(); e.stopPropagation();
        const nameVal = document.getElementById('tvip-bo-name').value.trim();
        const qtyVal  = Math.max(1, Math.min(100, parseInt(document.getElementById('tvip-bo-qty').value, 10) || 1));
        const price   = getPriceVal(document.getElementById('tvip-bo-price'));
        const status  = document.getElementById('tvip-bo-status');
        if (!nameVal) { status.textContent = ({ TR:'Hata: Eşya adı girin.', EN:'Error: Enter item name.', PT:'Erro: Digite o nome.' }[LANG]); return; }
        const found = filterItems(nameVal, 9999); // tümünü bul
        const toOffer = found.slice(0, qtyVal);

        // Önizleme modalı
        const modal   = mk('div'); modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.8);z-index:99999;display:flex;align-items:center;justify-content:center;padding:10px;box-sizing:border-box';
        const content = mk('div'); content.style.cssText = 'background:#fff;border-radius:8px;padding:16px;width:95vw;max-width:460px;max-height:85vh;overflow-y:auto;box-sizing:border-box';
        const title = mk('h3', '', ({ TR:'👁 Önizleme', EN:'👁 Preview', PT:'👁 Prévia' }[LANG])); title.style.cssText = 'margin:0 0 10px;font-size:14px;color:#333';

        const summary = mk('div'); summary.style.cssText = 'background:#f8f9fa;border-radius:5px;padding:10px;margin-bottom:10px;font-size:12px';
        if (!found.length) {
            summary.innerHTML = `<span style="color:#dc3545">⚠️ ${({ TR:`"${nameVal}" ile başlayan eşya bulunamadı.`, EN:`No items starting with "${nameVal}".`, PT:`Nenhum item com "${nameVal}".` }[LANG])}</span>`;
        } else {
            const total = toOffer.length * price;
            summary.innerHTML = `
                <div><b>${({ TR:'Envanterde bulunan:', EN:'Found in inventory:', PT:'Encontrado:' }[LANG])}</b> ${found.length}</div>
                <div><b>${({ TR:'Gönderilecek:', EN:'Will offer:', PT:'Será ofertado:' }[LANG])}</b> ${toOffer.length}</div>
                <div><b>${({ TR:'Birim fiyat:', EN:'Unit price:', PT:'Preço unitário:' }[LANG])}</b> ${price.toLocaleString('tr-TR')} M$</div>
                <div style="border-top:1px solid #dee2e6;margin-top:6px;padding-top:6px;font-weight:bold;color:#218838">
                    ${({ TR:'Toplam:', EN:'Total:', PT:'Total:' }[LANG])} ${total.toLocaleString('tr-TR')} M$
                </div>`;
        }

        const listEl = mk('div'); listEl.style.cssText = 'max-height:200px;overflow-y:auto;font-size:11px';
        toOffer.forEach((item, i) => {
            const row = mk('div', '', `${i+1}. ${item.text}`);
            row.style.cssText = 'padding:2px 4px;border-bottom:1px solid #f0f0f0;color:#555';
            listEl.appendChild(row);
        });

        const closeBtn = mkB('✕ ' + ({ TR:'Kapat', EN:'Close', PT:'Fechar' }[LANG]), 'btn-grey', () => modal.remove());
        closeBtn.style.cssText = 'width:100%;margin-top:12px';
        content.append(title, summary, listEl, closeBtn);
        modal.appendChild(content);
        document.body.appendChild(modal);
        modal.addEventListener('click', ev => { if (ev.target === modal) modal.remove(); });
    };

    document.getElementById('tvip-bo-start').onclick = () => {
        const nameVal  = document.getElementById('tvip-bo-name').value.trim();
        const qtyVal   = Math.max(1, Math.min(100, parseInt(document.getElementById('tvip-bo-qty').value, 10) || 1));
        const priceVal = getPriceVal(document.getElementById('tvip-bo-price'));
        const status   = document.getElementById('tvip-bo-status');
        if (!nameVal) {
            const allItems = filterItems('', 9999).filter(it => it.value);
            if (!allItems.length) { status.textContent = s('boErrName'); return; }
            const D = (tr,en,pt) => ({TR:tr,EN:en,PT:pt}[LANG]);
            if (!confirm(D(
                `${allItems.length} eşya 0 M$ (ücretsiz) olarak gönderilecek. Devam edilsin mi?`,
                `${allItems.length} items will be offered for free (0 M$). Proceed?`,
                `${allItems.length} itens serão ofertados por 0 M$. Continuar?`
            ))) return;
            GM_setValue(DK.BO_BATCH_ID, Date.now().toString());
            GM_setValue(DK.BO_PRICE, 0);
            GM_setValue(DK.BO_ITEMS, JSON.stringify(allItems));
            GM_setValue(DK.BO_TOTAL, String(allItems.length));
            GM_setValue(DK.BO_RUNNING, 'true');
            setDisabled(true);
            setTimeout(processNext, 300);
            return;
        }
        if (priceVal < 0)      { status.textContent = ({ TR:'Hata: Geçersiz fiyat.', EN:'Error: Invalid price.', PT:'Erro: Preço inválido.' }[LANG]); return; }
        const toOffer = filterItems(nameVal, qtyVal);
        if (!toOffer.length)   { status.textContent = `"${nameVal}" — ${({ TR:'bulunamadı.', EN:'not found.', PT:'não encontrado.' }[LANG])}`; return; }
        status.textContent = `${toOffer.length} ${({ TR:'eşya bulundu, başlıyor...', EN:'items found, starting...', PT:'itens encontrados...' }[LANG])}`;
        GM_setValue(DK.BO_BATCH_ID, Date.now().toString());
        GM_setValue(DK.BO_PRICE,   priceVal);
        GM_setValue(DK.BO_ITEMS,   JSON.stringify(toOffer));
        GM_setValue(DK.BO_TOTAL,   String(toOffer.length));
        GM_setValue(DK.BO_RUNNING, 'true');
        setDisabled(true);
        setTimeout(processNext, 300);
    };

    document.getElementById('tvip-bo-stop').onclick = () => {
        document.getElementById('tvip-bo-status').textContent = ({ TR:'Kullanıcı tarafından durduruldu.', EN:'Stopped by user.', PT:'Parado pelo usuário.' }[LANG]);
        document.getElementById('tvip-global-stop')?.remove();
        stopOffer();
    };

    document.getElementById('tvip-bo-favorites').onclick = (e) => {
        e.preventDefault(); e.stopPropagation();
        openFavoritesModal((itemName, qty, price) => {
            document.getElementById('tvip-bo-name').value  = itemName;
            document.getElementById('tvip-bo-qty').value   = qty;
            const pi = document.getElementById('tvip-bo-price');
            pi.dataset.raw = String(price);
            pi.value = price > 0 ? price.toLocaleString('tr-TR') : '0';
        });
    };

    document.getElementById('tvip-bo-customers').onclick = (e) => {
        e.preventDefault(); e.stopPropagation();
        openCustomersModal();
    };

    document.getElementById('tvip-bo-log').onclick = (e) => {
        e.preventDefault(); e.stopPropagation();
        openLogModal();
    };

    // Resume on page reload
    if (GM_getValue(DK.BO_RUNNING, false)) {
        setDisabled(true);
        document.getElementById('tvip-bo-status').textContent = ({ TR:'Yeniden yüklendi, devam ediliyor...', EN:'Reloaded, resuming...', PT:'Recarregado, retomando...' }[LANG]);
        setTimeout(processNext, 500);
    } else { setDisabled(false); }
};

// TRADEHUB — TOPLU KABUL + TOPLU İPTAL
const setupBulkAcceptUI = () => {
    if (!guard(K.bulkAccept, '/Character/ItemsOffered')) return;
    if (document.getElementById('tvip-bulk-accept-ui')) return;

    cleanOfferedOnItemsPage();

    const findBox = txt => [...document.querySelectorAll('.box')]
        .find(b => b.querySelector('h2')?.textContent.includes(txt));

    const incomingBox = findBox('Teklif edilen eşyalar') || findBox('Items you are being offered') || findBox('Itens sendo ofertados');
    const outgoingBox = findBox('Teklif ettiğiniz')      || findBox('Items you are offering') || findBox('Itens que você está ofertando');

    const parseCurrency = str => parseInt((str || '').replace(/\./g, '').split(',')[0].replace(/[^\d]/g, ''), 10) || 0;
    const getPrice = p => {
        const m = p.textContent.match(/Tutar:\s*([\d.,]+)\s*M\$|cost:\s*([\d.,]+)\s*M\$|custo:\s*([\d.,]+)\s*M\$/i);
        return m ? parseCurrency(m[1] || m[2] || m[3]) : 0;
    };
    const isFree = p => /Tutar:\s*Bedava|cost:\s*Free|custo:\s*Grátis/i.test(p.textContent);

    // ── ACCEPT UI ──
    if (incomingBox) {
        const ui = mk('div'); ui.id = 'tvip-bulk-accept-ui';
        ui.innerHTML = `
            <h3 style="margin:0 0 8px;font-size:13px">${s('baTitle')}</h3>
            <div class="tvip-bulk-panel">
                <div class="tvip-bulk-grid">
                    <div class="tvip-bulk-field"><label>${s('baItemName')}</label><input type="text" id="tvip-ba-name" placeholder="${s('baItemPlh')}"></div>
                    <div class="tvip-bulk-field"><label>${s('baMaxPrice')}</label><input type="number" id="tvip-ba-price" min="0" step="1" value="55000"></div>
                </div>
                <div class="tvip-bulk-actions">
                    <button id="tvip-ba-start" class="tvip-btn-start">${s('baStart')}</button>
                    <button id="tvip-ba-stop"  class="tvip-btn-stop">${s('baStop')}</button>
                </div>
                <div id="tvip-ba-status" class="tvip-status">${s('baReady')}</div>
                <div id="tvip-ba-spent"  class="tvip-spent">${sf.baSpent(0)}</div>
            </div>`;
        incomingBox.insertBefore(ui, incomingBox.firstChild);

        const setDisabledA = on => {
            document.getElementById('tvip-ba-start').disabled =  on;
            document.getElementById('tvip-ba-stop').disabled  = !on;
            document.getElementById('tvip-ba-name').disabled  =  on;
            document.getElementById('tvip-ba-price').disabled =  on;
        };

        const processNextAccept = () => {
            showGlobalStop(() => document.getElementById('tvip-ba-stop')?.click());
            if (!GM_getValue(DK.BA_RUNNING, false)) { setDisabledA(false); return; }
            const maxP    = parseInt(GM_getValue(DK.BA_MAX_PRICE, '0'));
            const tgtName = GM_getValue(DK.BA_ITEM_NAME, '');
            const count   = parseInt(GM_getValue(DK.BA_COUNT, '0'));
            const spent   = parseInt(GM_getValue(DK.BA_SPENT, '0'));
            const status  = document.getElementById('tvip-ba-status');
            const spentEl = document.getElementById('tvip-ba-spent');
            const box     = findBox('Teklif edilen eşyalar') || findBox('Items you are being offered') || findBox('Itens sendo ofertados');
            if (!box) { status.textContent = s('baNoSection'); stopAccept(); return; }
            const paragraphs = [...box.querySelectorAll('p.nobmargin')].filter(p => p.querySelector('a[id*="lnkItem"]'));
            if (!paragraphs.length) { status.textContent = sf.baDone(count, spent); stopAccept(); return; }

            let found = false;
            for (const p of paragraphs) {
                const itemName = p.querySelector('a[id*="lnkItem"]')?.textContent.trim() || ''; if (!itemName) continue;
                const price = isFree(p) ? 0 : getPrice(p);
                const priceLabel = price ? `${price} M$` : s('baFree');
                if ((tgtName === '' || itemName.toLowerCase().includes(tgtName)) && price <= maxP) {
                    const newCount = count + 1, newSpent = spent + price;
                    GM_setValue(DK.BA_COUNT, String(newCount)); GM_setValue(DK.BA_SPENT, String(newSpent));
                    status.textContent = sf.baAccepting(newCount, itemName, priceLabel);
                    if (spentEl) spentEl.textContent = sf.baSpent(newSpent);
                    found = true;
                    // Repeater indeksini item linkinden al, box içinde butonu bul
                    const itemLink = p.querySelector('a[id*="lnkItem"]');
                    const idxM = itemLink?.id.match(/_(ctl\d+)_lnkItem/i);
                    const repIdx = idxM ? idxM[1] : null;
                    const btn = (repIdx && box.querySelector(`input[id*="${repIdx}_btnAccept"]`))
                        || p.querySelector('input[id*="btnAccept"]')
                        || box.querySelector('input[id*="btnAccept"]');
                    if (!btn) continue;
                    setTimeout(() => {
                        if (!GM_getValue(DK.BA_RUNNING, false)) { setDisabledA(false); return; }
                        btn.click();
                    }, 2000);
                    break;
                }
            }
            if (!found) { status.textContent = sf.baNoMatch(count, spent); stopAccept(); }
        };

        const stopAccept = () => {
            GM_deleteValue(DK.BA_RUNNING); GM_deleteValue(DK.BA_MAX_PRICE);
            GM_deleteValue(DK.BA_ITEM_NAME); GM_deleteValue(DK.BA_COUNT); GM_deleteValue(DK.BA_SPENT);
            document.getElementById('tvip-global-stop')?.remove();
            setDisabledA(false);
        };

        document.getElementById('tvip-ba-start').onclick = () => {
            const price = parseInt(document.getElementById('tvip-ba-price').value.replace(/[.,]/g,''), 10);
            const name  = document.getElementById('tvip-ba-name').value.trim().toLowerCase();
            if (isNaN(price) || price < 0) { document.getElementById('tvip-ba-status').textContent = s('baErrPrice'); return; }
            document.getElementById('tvip-ba-status').textContent = sf.baInit(name, price);
            document.getElementById('tvip-ba-spent').textContent  = sf.baSpent(0);
            GM_setValue(DK.BA_RUNNING, 'true'); GM_setValue(DK.BA_MAX_PRICE, price);
            GM_setValue(DK.BA_ITEM_NAME, name); GM_setValue(DK.BA_COUNT, '0'); GM_setValue(DK.BA_SPENT, '0');
            setDisabledA(true); setTimeout(processNextAccept, 300);
        };

        document.getElementById('tvip-ba-stop').onclick = () => {
            const c = parseInt(GM_getValue(DK.BA_COUNT, '0')), sp = parseInt(GM_getValue(DK.BA_SPENT, '0'));
            document.getElementById('tvip-ba-status').textContent = sf.baStopped(c, sp);
            document.getElementById('tvip-global-stop')?.remove();
            stopAccept();
        };

        if (GM_getValue(DK.BA_RUNNING, false)) {
            setDisabledA(true);
            document.getElementById('tvip-ba-status').textContent = s('baResumed');
            document.getElementById('tvip-ba-spent').textContent  = sf.baSpent(parseInt(GM_getValue(DK.BA_SPENT, '0')));
            setTimeout(processNextAccept, 500);
        } else { setDisabledA(false); }
    }

    // ── CANCEL UI ──
    if (outgoingBox) {
        const uic = mk('div'); uic.id = 'tvip-bulk-cancel-ui';
        uic.innerHTML = `
            <h3 style="margin:0 0 8px;font-size:13px">${s('bcTitle')}</h3>
            <div class="tvip-bulk-panel">
                <div class="tvip-bulk-grid">
                    <div class="tvip-bulk-field"><label>${s('bcItemName')}</label><input type="text" id="tvip-bc-name" placeholder="${s('baItemPlh')}"></div>
                    <div class="tvip-bulk-field">
                        <label>${s('bcFilter')}</label>
                        <select id="tvip-bc-filter" style="width:100%;padding:5px 6px;border:1px solid #ccc;border-radius:4px;font-size:12px">
                            <option value="all">${s('bcFilterAll')}</option>
                            <option value="free">${s('bcFilterFree')}</option>
                            <option value="paid">${s('bcFilterPaid')}</option>
                        </select>
                    </div>
                </div>
                <div class="tvip-bulk-actions">
                    <button id="tvip-bc-start" class="tvip-btn-start">${s('bcStart')}</button>
                    <button id="tvip-bc-stop"  class="tvip-btn-stop">${s('bcStop')}</button>
                </div>
                <div id="tvip-bc-status" class="tvip-status">${s('bcReady')}</div>
            </div>`;
        outgoingBox.insertBefore(uic, outgoingBox.firstChild);

        const setDisabledC = on => {
            document.getElementById('tvip-bc-start').disabled  =  on;
            document.getElementById('tvip-bc-stop').disabled   = !on;
            document.getElementById('tvip-bc-name').disabled   =  on;
            document.getElementById('tvip-bc-filter').disabled =  on;
        };

        const processNextCancel = () => {
            showGlobalStop(() => document.getElementById('tvip-bc-stop')?.click());
            if (!GM_getValue(DK.BC_RUNNING, false)) { setDisabledC(false); return; }
            const tgtName = GM_getValue(DK.BC_ITEM_NAME, '');
            const filter  = GM_getValue(DK.BC_FILTER, 'all');
            const count   = parseInt(GM_getValue(DK.BC_COUNT, '0'));
            const status  = document.getElementById('tvip-bc-status');
            const box     = findBox('Teklif ettiğiniz') || findBox('Items you are offering') || findBox('Itens que você está ofertando');
            if (!box) { status.textContent = s('bcNoSection'); stopCancel(); return; }
            const paragraphs = [...box.querySelectorAll('p.nobmargin')].filter(p => p.querySelector('a[id*="lnkItem"]'));
            if (!paragraphs.length) { status.textContent = sf.bcDone(count); stopCancel(); return; }

            let found = false;
            for (const p of paragraphs) {
                const itemName = p.querySelector('a[id*="lnkItem"]')?.textContent.trim() || ''; if (!itemName) continue;
                const free = isFree(p);
                const matchesFilter = filter === 'all' || (filter === 'free' && free) || (filter === 'paid' && !free);
                const matchesName   = tgtName === '' || itemName.toLowerCase().includes(tgtName);
                if (matchesFilter && matchesName) {
                    const newCount = count + 1;
                    GM_setValue(DK.BC_COUNT, String(newCount));
                    status.textContent = sf.bcCancelling(newCount, itemName);
                    found = true;
                    // Repeater indeksini item linkinden al, box içinde butonu bul
                    const itemLinkC = p.querySelector('a[id*="lnkItem"]');
                    const idxMC = itemLinkC?.id.match(/_(ctl\d+)_lnkItem/i);
                    const repIdxC = idxMC ? idxMC[1] : null;
                    const btn = (repIdxC && box.querySelector(`input[id*="${repIdxC}_btnStop"]`))
                        || p.querySelector('input[type="submit"][id*="btnStop"]')
                        || box.querySelector('input[type="submit"][id*="btnStop"]');
                    if (!btn) continue;
                    setTimeout(() => {
                        if (!GM_getValue(DK.BC_RUNNING, false)) { setDisabledC(false); return; }
                        btn.click();
                    }, 2000);
                    break;
                }
            }
            if (!found) { status.textContent = sf.bcNoMatch(count); stopCancel(); }
        };

        const stopCancel = () => {
            GM_deleteValue(DK.BC_RUNNING); GM_deleteValue(DK.BC_ITEM_NAME);
            GM_deleteValue(DK.BC_FILTER);  GM_deleteValue(DK.BC_COUNT);
            document.getElementById('tvip-global-stop')?.remove();
            setDisabledC(false);
        };

        document.getElementById('tvip-bc-start').onclick = () => {
            const name   = document.getElementById('tvip-bc-name').value.trim().toLowerCase();
            const filter = document.getElementById('tvip-bc-filter').value;
            document.getElementById('tvip-bc-status').textContent = s('bcReady');
            GM_setValue(DK.BC_RUNNING, 'true'); GM_setValue(DK.BC_ITEM_NAME, name);
            GM_setValue(DK.BC_FILTER, filter);  GM_setValue(DK.BC_COUNT, '0');
            setDisabledC(true); setTimeout(processNextCancel, 300);
        };

        document.getElementById('tvip-bc-stop').onclick = () => {
            const c = parseInt(GM_getValue(DK.BC_COUNT, '0'));
            document.getElementById('tvip-bc-status').textContent = sf.bcStopped(c);
            document.getElementById('tvip-global-stop')?.remove();
            stopCancel();
        };

        if (GM_getValue(DK.BC_RUNNING, false)) {
            setDisabledC(true);
            setTimeout(processNextCancel, 500);
        } else { setDisabledC(false); }
    }
};

// CITY SHORTCUTS
const CITY_NAMES = {
  "1":"Stockholm","5":"Londra","6":"New York","7":"Berlin","8":"Amsterdam",
  "9":"Barselona","10":"Melbourne","11":"Nashville","14":"Los Angeles",
  "16":"Toronto","17":"Buenos Aires","18":"Moskova","19":"Helsinki",
  "20":"Paris","21":"Sao Paulo","22":"Kopenhag","23":"Roma","24":"Madrid",
  "25":"Rio de Janeiro","26":"Tromsø","27":"Glasgow","28":"Vilnius",
  "29":"Dubrovnik","30":"İstanbul","31":"Porto","32":"Mexico City",
  "33":"Brüksel","34":"Tallinn","35":"Ankara","36":"Belgrad","38":"Montreal",
  "39":"Singapur","42":"Budapeşte","45":"Şangay","46":"Bükreş","47":"İzmir",
  "48":"Varşova","49":"Saraybosna","50":"Seattle","51":"Johannesburg",
  "52":"Milano","53":"Sofya","54":"Manila","55":"Cakarta","56":"Kyiv",
  "58":"Bakü","60":"Şikago","61":"Antalya","62":"Tokyo"
};
const CITY_DATA = {
    "1":  { locations: [ {id:"s",type:"shower",placeId:3160405,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:49044, name:"Årsta Havsbad",   dur:90 } ]},
    "5":  { locations: [ {id:"s",type:"shower",placeId:3161774,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:234234,name:"Herman's Palace",  dur:5  } ]},
    "6":  { locations: [ {id:"s",type:"shower",placeId:2986433,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:49087, name:"Cape Cod",         dur:95 } ]},
    "7":  { locations: [ {id:"s",type:"shower",placeId:3231072,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:233224,name:"Schliemann's Zimmer",dur:10} ]},
    "8":  { locations: [ {id:"s",type:"shower",placeId:3161145,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:49089, name:"Breskens",         dur:90 } ]},
    "9":  { locations: [ {id:"s",type:"shower",placeId:3159414,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:49090, name:"Costa Brava",      dur:20 } ]},
    "10": { locations: [ {id:"s",type:"shower",placeId:3198312,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:49091, name:"Niney Mile Beach",  dur:50 } ]},
    "11": { locations: [ {id:"s",type:"shower",placeId:3245177,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:76469, name:"Little house on the Prairie",dur:10} ]},
    "14": { locations: [ {id:"s",type:"shower",placeId:3196672,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:49198, name:"Santa Monica Beach",dur:20 } ]},
    "16": { locations: [ {id:"s",type:"shower",placeId:3268245,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:49094, name:"Sunnyside",        dur:15 } ]},
    "17": { locations: [ {id:"s",type:"shower",placeId:3161537,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:49095, name:"La Pampa",         dur:90 } ]},
    "18": { locations: [ {id:"s",type:"shower",placeId:3204377,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:49096, name:"Волга",            dur:120} ]},
    "19": { locations: [ {id:"s",type:"shower",placeId:3237480,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:49097, name:"Pyhäjärvi",        dur:95 } ]},
    "20": { locations: [ {id:"s",type:"shower",placeId:3162065,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:49098, name:"Charente",         dur:65 } ]},
    "21": { locations: [ {id:"s",type:"shower",placeId:3262551,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:53596, name:"Guarujá",          dur:90 } ]},
    "22": { locations: [ {id:"s",type:"shower",placeId:3204935,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:67582, name:"Gilleleje",        dur:95 } ]},
    "23": { locations: [ {id:"s",type:"shower",placeId:3181531,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:72404, name:"Ostia Lido",       dur:50 } ]},
    "24": { locations: [ {id:"s",type:"shower",placeId:3162492,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:103128,name:"La Rioja",         dur:50 } ]},
    "25": { locations: [ {id:"s",type:"shower",placeId:3199641,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:104742,name:"Ipanema",          dur:20 } ]},
    "26": { locations: [ {id:"s",type:"shower",placeId:3203190,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:106202,name:"Telegrafbukta",    dur:15 } ]},
    "27": { locations: [ {id:"s",type:"shower",placeId:3205233,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:182793,name:"Öğretmenin evi",   dur:5  } ]},
    "28": { locations: [ {id:"s",type:"shower",placeId:3220498,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:122919,name:"Merkys",           dur:90 } ]},
    "29": { locations: [ {id:"s",type:"shower",placeId:3220722,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:131991,name:"Korcula",          dur:95 } ]},
    "30": { locations: [ {id:"s",type:"shower",placeId:3160535,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:137942,name:"Gala Gölü",        dur:90 } ]},
    "31": { locations: [ {id:"s",type:"shower",placeId:2986566,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:140964,name:"Costa Verde",      dur:20 } ]},
    "32": { locations: [ {id:"s",type:"shower",placeId:3218344,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:170268,name:"Acapulco",         dur:90 } ]},
    "33": { locations: [ {id:"s",type:"shower",placeId:2965425,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:188643,name:"Blankenberge",     dur:95 } ]},
    "34": { locations: [ {id:"s",type:"shower",placeId:3222559,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:195084,name:"Pirita",           dur:15 } ]},
    "35": { locations: [ {id:"s",type:"shower",placeId:3198434,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:249590,name:"Hatay",            dur:65 } ]},
    "36": { locations: [ {id:"s",type:"shower",placeId:3218479,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:282985,name:"Srebrno",          dur:95 } ]},
    "38": { locations: [ {id:"s",type:"shower",placeId:3198981,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:358359,name:"St Lawrence River",dur:95 } ]},
    "39": { locations: [ {id:"s",type:"shower",placeId:3222154,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:473018,name:"Sentosa",          dur:36 } ]},
    "42": { locations: [ {id:"s",type:"shower",placeId:3231282,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:653963,name:"Tisza",            dur:90 } ]},
    "45": { locations: [ {id:"s",type:"shower",placeId:3255714,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:670043,name:"Putuo Shan",       dur:90 } ]},
    "46": { locations: [ {id:"s",type:"shower",placeId:3198948,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:773546,name:"Constanţa",        dur:95 } ]},
    "47": { locations: [ {id:"s",type:"shower",placeId:3204448,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:782567,name:"Urla",             dur:90 } ]},
    "48": { locations: [ {id:"s",type:"shower",placeId:3231449,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:847919,name:"Wielkopolskie",    dur:100} ]},
    "49": { locations: [ {id:"s",type:"shower",placeId:3177850,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:1174002,name:"Pliva",           dur:95 } ]},
    "50": { locations: [ {id:"s",type:"shower",placeId:3023079,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:1349118,name:"Elliott Bay Park", dur:20 } ]},
    "51": { locations: [ {id:"s",type:"shower",placeId:3202857,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:1845324,name:"St Lucia",        dur:90 } ]},
    "52": { locations: [ {id:"s",type:"shower",placeId:3218697,name:"Duş Evi",dur:0}, {id:"p",type:"path",placeId:1886305,name:"Lago di Garda",   dur:95 } ]},
    "53": { locations: [ {id:"s",type:"shower",placeId:3201807,name:"Duş Evi",dur:0} ]},
    "54": { locations: [ {id:"s",type:"shower",placeId:3188092,name:"Duş Evi",dur:0} ]},
    "55": { locations: [ {id:"s",type:"shower",placeId:3222289,name:"Duş Evi",dur:0} ]},
    "56": { locations: [ {id:"s",type:"shower",placeId:3187003,name:"Duş Evi",dur:0} ]},
    "58": { locations: [ {id:"s",type:"shower",placeId:3230603,name:"Duş Evi",dur:0} ]},
    "60": { locations: [ {id:"s",type:"shower",placeId:3200260,name:"Duş Evi",dur:0} ]},
    "61": { locations: [ {id:"s",type:"shower",placeId:3263617,name:"Duş Evi",dur:0} ]},
    "62": { locations: [ {id:"s",type:"shower",placeId:3227018,name:"Duş Evi",dur:0} ]},
};

const getCityData = () => {
    const custom = (() => { try { return JSON.parse(GM_getValue('tvip_city_custom','null')); } catch { return null; } })();
    if (!custom) return CITY_DATA;
    const merged = {};
    Object.keys({...CITY_DATA,...custom}).forEach(id => { merged[id] = custom[id] ?? CITY_DATA[id]; });
    return merged;
};

const typeIcon  = t => t==='shower'?'🚿':t==='path'?'🌿':'📍';
const typeLabel = t => t==='shower'?s('csShower'):t==='path'?s('csPath'):s('csOther');
const PM = '/World/Popmundo.aspx';

const setupDeleteConfirm = () => {
    if (!guard(K.deleteConfirm, '/Character/Items')) return;
    const btn = document.querySelector('#ctl00_cphLeftColumn_ctl00_btnThrowAwaySelItems');
    const chk = document.querySelector('#ctl00_cphLeftColumn_ctl00_chkThrowAwaySelItems');
    if (!btn || !chk) return;
    btn.addEventListener('click', e => {
        if (btn.disabled) return;
        e.preventDefault(); e.stopImmediatePropagation();
        const names = [...document.querySelectorAll('input[type=checkbox][id*="_chkItem"]:checked')]
            .map(cb => cb.closest('tr')?.querySelector('a[id*="_lnkItem"]')?.textContent.trim())
            .filter(Boolean);
        if (!names.length) return;
        if (confirm(sf.deleteConfirmItems(names))) {
            chk.checked = true;
            if (typeof unsafeWindow !== 'undefined' && typeof unsafeWindow.__doPostBack === 'function') {
                unsafeWindow.__doPostBack('ctl00$cphLeftColumn$ctl00$btnThrowAwaySelItems', '');
            } else if (typeof __doPostBack === 'function') {
                __doPostBack('ctl00$cphLeftColumn$ctl00$btnThrowAwaySelItems', '');
            }
        }
    }, true);
};

const applyCityShortcuts = () => {
    if (!guard(K.cityShortcuts)) return;
    const airport = document.getElementById('ctl00_cphLeftColumn_ctl00_lnkAirport'); if (!airport) return;
    const cityDd  = document.getElementById('ctl00_cphRightColumn_ctl01_ddlCities'); if (!cityDd) return;
    const props   = getCityData()[cityDd.value]; if (!props) return;
    const tbody   = airport.closest('tr')?.parentElement; if (!tbody) return;
    props.locations.forEach(loc => {
        const tr = document.createElement('tr');
        const label  = `${typeIcon(loc.type)} ${loc.name}`;
        const durStr = loc.type==='path' && loc.dur ? ` — ${loc.dur} ${s('csMin')}` : '';
        tr.innerHTML = `
            <td>${typeLabel(loc.type)}:</td>
            <td><a href="${PM}/Locale/${loc.placeId}">${label}</a>${durStr}</td>
            <td class="right"><a class="icon" href="${PM}/Locale/MoveToLocale/${loc.placeId}"><img src="../../../Static/Icons/movetolocale.png" alt="" style="border:0"></a></td>`;
        tbody.appendChild(tr);
    });
};

const openCityManager = () => {
    const cityDd = document.getElementById('ctl00_cphRightColumn_ctl01_ddlCities');
    const activeCityId = cityDd?.value || Object.keys(CITY_DATA)[0];
    mkModal('🏙️ ' + s('cityMgr'), (cont) => {
        const topRow = mk('div'); topRow.style.cssText='display:flex;gap:6px;align-items:center;margin-bottom:10px';
        const citySelect = mk('select'); citySelect.style.cssText='flex:1;padding:4px 6px;border:1px solid #ccc;border-radius:4px;font-size:12px';
        const allCityIds = [...new Set([...Object.keys(CITY_DATA), ...Object.keys((() => { try{return JSON.parse(GM_getValue('tvip_city_custom','{}'));}catch{return {};} })())])].sort((a,b)=>+a-+b);
        allCityIds.forEach(id => {
            const o = mk('option','', CITY_NAMES[id] ? `${CITY_NAMES[id]} (ID ${id})` : `ID ${id}`); o.value=id;
            if (id===activeCityId) o.selected=true;
            citySelect.appendChild(o);
        });
        const resetBtn = mkB(s('cityMgrReset'),'btn-sm btn-r',() => {
            if (!confirm(s('cityMgrResetQ'))) return;
            const custom = (() => { try{return JSON.parse(GM_getValue('tvip_city_custom','{}'));}catch{return {};} })();
            delete custom[citySelect.value];
            GM_setValue('tvip_city_custom', JSON.stringify(custom));
            renderList();
        });
        topRow.append(citySelect, resetBtn);
        cont.appendChild(topRow);
        const listDiv = mk('div'); cont.appendChild(listDiv);
        const renderList = () => {
            listDiv.innerHTML = '';
            const data = getCityData()[citySelect.value];
            const locs = data ? data.locations : [];
            if (!locs.length) { listDiv.appendChild(mk('p','',s('cityMgrEmpty'))).style.cssText='font-size:12px;color:#888'; return; }
            locs.forEach((loc,i) => {
                const row = mk('div'); row.style.cssText='display:flex;align-items:center;gap:6px;margin-bottom:4px;font-size:12px;padding:4px 6px;background:#f8f9fa;border-radius:4px';
                const icon = mk('span','',typeIcon(loc.type)); icon.style.fontSize='14px';
                const lbl  = mk('a','',loc.name); lbl.href=`${PM}/Locale/${loc.placeId}`; lbl.target='_blank'; lbl.style.cssText='flex:1;color:#17a2b8;text-decoration:none';
                const delB = mkB('✕','btn-sm btn-r',() => {
                    if (!confirm(s('cityMgrDelQ'))) return;
                    const custom = (() => { try{return JSON.parse(GM_getValue('tvip_city_custom','{}'));}catch{return {};} })();
                    const cityId = citySelect.value;
                    if (!custom[cityId]) custom[cityId] = JSON.parse(JSON.stringify(getCityData()[cityId] || {locations:[]}));
                    custom[cityId].locations = custom[cityId].locations.filter((_,j)=>j!==i);
                    GM_setValue('tvip_city_custom', JSON.stringify(custom));
                    renderList();
                });
                row.append(icon, lbl);
                if (loc.dur) { const dur=mk('span','',`${loc.dur} ${s('csMin')}`); dur.style.cssText='color:#888;font-size:11px'; row.appendChild(dur); }
                row.appendChild(delB);
                listDiv.appendChild(row);
            });
            const custom = (() => { try{return JSON.parse(GM_getValue('tvip_city_custom','{}'));}catch{return {};} })();
            if (custom[citySelect.value]) {
                const tag = mk('span','',`✏️ ${s('cityMgrCustom')}`); tag.style.cssText='font-size:10px;color:#888;display:block;margin-top:2px';
                listDiv.appendChild(tag);
            }
        };
        citySelect.addEventListener('change', renderList);
        renderList();
        const addSection = mk('div'); addSection.style.cssText='margin-top:10px;padding:8px;background:#f0f8ff;border-radius:6px';
        const addTitle = mk('div','',s('cityMgrAdd')); addTitle.style.cssText='font-size:11px;font-weight:bold;color:#555;margin-bottom:6px';
        const addGrid  = mk('div'); addGrid.style.cssText='display:grid;grid-template-columns:1fr 80px 60px auto;gap:4px;align-items:center';
        const namInp = mk('input'); namInp.placeholder=s('cityMgrName'); namInp.style.cssText='padding:4px 6px;border:1px solid #ccc;border-radius:3px;font-size:11px';
        const idInp  = mk('input'); idInp.type='number'; idInp.placeholder='Place ID'; idInp.style.cssText='padding:4px 6px;border:1px solid #ccc;border-radius:3px;font-size:11px';
        const durInp = mk('input'); durInp.type='number'; durInp.placeholder='Min'; durInp.style.cssText='padding:4px 6px;border:1px solid #ccc;border-radius:3px;font-size:11px';
        const addBtn = mkB(s('cityMgrAddBtn'),'btn-sm btn-g',() => {
            const nm=namInp.value.trim(), pid=parseInt(idInp.value), dur=parseInt(durInp.value)||0;
            if (!nm||isNaN(pid)) return;
            const custom = (() => { try{return JSON.parse(GM_getValue('tvip_city_custom','{}'));}catch{return {};} })();
            const cityId = citySelect.value;
            if (!custom[cityId]) custom[cityId] = JSON.parse(JSON.stringify(getCityData()[cityId] || {locations:[]}));
            custom[cityId].locations.push({id:`c${Date.now()}`,type:'path',placeId:pid,name:nm,dur});
            GM_setValue('tvip_city_custom', JSON.stringify(custom));
            namInp.value=''; idInp.value=''; durInp.value='';
            renderList();
        });
        addGrid.append(namInp,idInp,durInp,addBtn);
        addSection.append(addTitle,addGrid);
        cont.appendChild(addSection);
    });
};

// MENU
const injectMenu = () => {
    if (document.getElementById('tvip-bar')) return;

    const hpanel = mk('div', 'tvip-hpanel');
    hpanel.id = 'tvip-hpanel'; hpanel.style.display = 'none';

    const checks = {};
    const mkCheck = (ck, lk) => {
        const lbl = mk('label', 'tvip-chk');
        const chk = Object.assign(mk('input'), { type: 'checkbox', checked: isOn(ck) });
        checks[ck] = chk; lbl.append(chk, mk('span', '', s(lk))); hpanel.appendChild(lbl);
    };
    const mkHr  = ()  => hpanel.appendChild(mk('hr', 'tvip-hr'));
    const mkSec = txt => hpanel.appendChild(mk('div', 'tvip-sec', txt));

    // ── GENEL GÖRÜNÜM ──
    mkSec(s('secGeneral'));
    mkCheck(K.pb,           'pb');
    mkCheck(K.alignLeft,    'alignLeft');
    mkCheck(K.imageCtrl,    'imageCtrl');
    mkCheck(K.colorScoring, 'colorScoring');
    mkCheck(K.cityShortcuts,'cityShortcuts');
    mkCheck(K.tableTools,   'tableTools');
    mkCheck(K.repertoireF,  'repertoireF');
    mkHr();

    // ── EŞYA ──
    mkSec(s('secItems'));
    mkCheck(K.itemId,       'itemId');
    mkCheck(K.itemFilters,  'itemFilters');
    mkCheck(K.hideOffBox,   'hideOfferedChk');
    mkCheck(K.deleteConfirm,'deleteConfirmChk');
    mkCheck(K.autoDelivery, 'autoDelivery');
    mkCheck(K.ticketPricer, 'ticketPricer');
    mkCheck(K.bulkOffer,    'bulkOffer');
    mkCheck(K.bulkAccept,   'bulkAccept');
    mkHr();

    mkSec(s('langLabel'));
    const langRow = mk('div', 'tvip-lang-row');
    [['TR','🇹🇷 Türkçe'],['EN','🇬🇧 English'],['PT','🇧🇷 Português']].forEach(([code, label]) => {
        const b = mk('button', 'tvip-lang-btn' + (LANG === code ? ' active' : ''), label);
        b.onclick = () => { CK.set('ppm_lang', code); location.reload(); };
        langRow.appendChild(b);
    });
    hpanel.appendChild(langRow);

    const ecosystemRow = mk('div');
    ecosystemRow.style.cssText = 'background:#f0f8ff;border-radius:8px;padding:16px;margin-bottom:12px';
    ecosystemRow.innerHTML = `
        <div style="display:flex;gap:20px;justify-content:center;align-items:center">
            <div style="display:flex;align-items:center;gap:10px">
                <span style="font-size:16px">🇹🇷</span>
                <a href="https://rentry.org/PopControlEkosistemi" target="_blank" style="color:#1a5276;font-weight:500;text-decoration:none;font-size:13px">Beni Oku</a>
            </div>
            <div style="display:flex;align-items:center;gap:10px">
                <span style="font-size:16px">🇬🇧</span>
                <a href="https://rentry.org/PopControlEcosystem" target="_blank" style="color:#1a5276;font-weight:500;text-decoration:none;font-size:13px">Read Me</a>
            </div>
            <div style="display:flex;align-items:center;gap:10px">
                <span style="font-size:16px">🇧🇷</span>
                <a href="https://rentry.org/EcossistemaPopControl" target="_blank" style="color:#1a5276;font-weight:500;text-decoration:none;font-size:13px">Leia-me</a>
            </div>
        </div>
    `;
    hpanel.appendChild(ecosystemRow);
    mkHr();

    const row1 = mk('div'); row1.style.cssText = 'display:flex;flex-wrap:wrap;gap:4px;margin-top:4px';
    const row2 = mk('div'); row2.style.cssText = 'display:flex;flex-wrap:wrap;gap:4px;margin-top:4px';

    const saveBtn = mkB(s('save'), 'btn-g', () => {
        Object.entries(checks).forEach(([k, c]) => CK.set(k, c.checked ? '1' : '0'));
        location.reload();
    });
    const readBtn = mk('a', 'btn-grey btn-sm', s('readMe'));
    readBtn.href = 'https://rentry.org/HelperOku';
    readBtn.target = '_blank';
    readBtn.style.cssText = 'text-decoration:none;display:inline-flex;align-items:center;justify-content:center';
    row1.append(
        saveBtn, readBtn,
        mkB(s('backup'),  'btn-b btn-sm',    () => dbExport()),
        mkB(s('restore'), 'btn-grey btn-sm', () => dbImport())
    );
    const tdClearBtn = mkB(s('tdClear'), 'btn-sm btn-grey', () => { clearTableDeny(); alert(s('resetDone')); });
    const resetOfferBtn = mkB(s('resetOffered'), 'btn-sm', () => {
        if (confirm(s('confirmReset'))) { saveOfferedList([]); alert(s('resetDone')); }
    });
    resetOfferBtn.style.cssText = 'padding:2px 8px;background:none;color:#c0392b;border:1px solid #c0392b;border-radius:3px;cursor:pointer;font-size:11px';
    const cityMgrBtn = mkB('🏙️ ' + s('cityMgr'), 'btn-sm btn-grey', () => openCityManager());
    row2.append(tdClearBtn, resetOfferBtn, cityMgrBtn);
    hpanel.append(row1, row2);

    const _isModalOpen = () => !!document.getElementById('tvip-modal-ov');
    const closeModal = () => {
        const ov = document.getElementById('tvip-modal-ov');
        if (!ov) return;
        document.body.appendChild(hpanel);
        hpanel.style.cssText = 'display:none';
        ov.remove();
    };
    const openModal = () => {
        if (_isModalOpen()) { closeModal(); return; }
        const novEl = document.createElement('div'); novEl.id = 'tvip-modal-ov';
        novEl.style.cssText = [
            'position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:99997',
            'display:flex;align-items:center;justify-content:center',
            'padding:20px;box-sizing:border-box'
        ].join(';');
        hpanel.removeAttribute('style');
        hpanel.style.cssText = [
            'display:block!important;position:relative!important',
            'top:auto!important;right:auto!important;left:auto!important',
            'max-height:90vh;overflow-y:auto;border-radius:10px',
            'min-width:280px;max-width:560px;width:96%;box-sizing:border-box'
        ].join(';');
        novEl.appendChild(hpanel);
        novEl.addEventListener('click', function(e) { if (e.target === novEl) closeModal(); });
        document.body.appendChild(novEl);
    };
    const togglePanelModal = () => _isModalOpen() ? closeModal() : openModal();

    // ── TOP VIP "Helper Ayarlar" butonu için erişim noktasını güncelle ──
    window.__tvip_api.openSettings = togglePanelModal;

    const bar = mk('div', 'tvip-bar'); bar.id = 'tvip-bar';
    const lnk = (txt, fn) => { const a = mk('a', '', txt); a.href = '#'; a.onclick = e => { e.preventDefault(); fn(); }; return a; };
    bar.appendChild(lnk(s('menuTitle'), togglePanelModal));
    document.body.append(bar, hpanel);
    document.addEventListener('click', e => {
        if (_isModalOpen()) return;
        if (!bar.contains(e.target) && !hpanel.contains(e.target)) hpanel.style.display = 'none';
    });

// POPCONTROL BAĞLANTISI
    function connectToPopControl() {
        if (window.PPC_Helper_Done) return;
        const pc = (typeof unsafeWindow !== 'undefined' && unsafeWindow.PopControl);
        if (pc && typeof pc.register === 'function') {
            try {
                pc.register({
                    id: 'helper',
                    icon: '🎨',
                    label: 'Helper',
                    strings: window.__ppcStrHelper || {},
                    buttons: [{
                        icon: '🎨',
                        label: 'Helper',
                        onClick: function(e) { e.preventDefault(); togglePanelModal(); }
                    }],
                    onUndo: function() {
                        closeModal();
                        bar.style.display = '';
                        window.PPC_Helper_Done = false;
                    },
                });
                bar.style.display = 'none';
                window.PPC_Helper_Done = true;
                injectCharMenuItems();
                console.log('[Helper] Successfully registered with PopControl');
            } catch (error) {
                console.error('[Helper] PopControl connection error:', error);
            }
        }
    }

    document.addEventListener('PopControlReady', () => setTimeout(connectToPopControl, 50), { once: true });

    function checkPopControl(tries = 0) {
        if (window.PPC_Helper_Done) return;
        const pc = (typeof unsafeWindow !== 'undefined' && unsafeWindow.PopControl);
        if (pc && typeof pc.register === 'function') {
            connectToPopControl();
        } else if (tries < 20) {
            setTimeout(() => checkPopControl(tries + 1), 150);
        }
    }
    checkPopControl(0);


    if (isMobile) {
        const mobileTrigger = () => {
            if (!window.PPC_Helper_Done && !window.PPC_Helper_Mobile_Triggered) {
                window.PPC_Helper_Mobile_Triggered = true;
                setTimeout(() => checkPopControl(0), 2000);
            }
        };
        document.addEventListener('touchstart', mobileTrigger, { once: true, passive: true });
        setTimeout(() => {
            if (!window.PPC_Helper_Done && !window.PPC_Helper_Mobile_Triggered) mobileTrigger();
        }, 3000);
    }
};

// INIT
applyAlignLeft();
applyProgressBar();
applyItemId();
applyColorScoring();
applyImageCtrl();
applyTableSort();
applyPageSearch();
applyAutoDelivery();
applyTicketPricer();
applyTableAvg();
applyRepertoireFilter();
setupBulkOfferUI();
setupBulkAcceptUI();
setupDeleteConfirm();

// ── PUBLIC API (diğer scriptler ve menü injection için) ──
window.__tvip_api = {
    openCustomers:  () => typeof openCustomersModal  === 'function' && openCustomersModal(),
    openFavorites:  () => typeof openFavoritesModal  === 'function' && openFavoritesModal(() => {}),
    openLog:        () => typeof openLogModal        === 'function' && openLogModal(),
    openSettings:   () => typeof togglePanelModal    === 'function' && togglePanelModal(),
};

// ── TOP VIP MENÜSÜ ENJEKSİYONU ──────────────────────────────────────────────
const injectCharMenuItems = () => {
    if (!location.href.includes('/World/Popmundo.aspx/Character')) return;
    const D = (tr, en, pt) => ({ TR: tr, EN: en, PT: pt }[LANG] || tr);
    const helperButtons = [
        { id: 'mnu-tvip-customers', label: D('👥 Müşteriler',        '👥 Customers',    '👥 Clientes'),     fn: () => window.__tvip_api?.openCustomers() },
        { id: 'mnu-tvip-log',       label: D('📋 Toplu Teklif Logu', '📋 Offer Log',    '📋 Log de Oferta'), fn: () => window.__tvip_api?.openLog()       },
        { id: 'mnu-tvip-settings',  label: D('🎨 Helper Ayarlar',   '🎨 Helper Settings','🎨 Config Helper'), fn: () => window.__tvip_api?.openSettings()  },
    ];

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

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

injectMenu();

if (!window.PPC_Helper_Done) {
    setTimeout(() => {
        if (!window.PPC_Helper_Done) {
            console.log('[Helper] Final connection attempt...');
            connectToPopControl();
        }
    }, 500);
}

if (location.href.includes('/Locale/ItemsEquipment/')) applyOnlyYours();
if (location.href.includes('/Character/OfferItem/'))   applyHideOffered();
if (document.getElementById('ctl00_cphLeftColumn_ctl00_lnkAirport')) applyCityShortcuts();

})();