🎨 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.

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

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

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

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

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

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

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

})();