☰

🌟 Route49

Popmundo tour planner β€” geographic optimization, template system and booking interface helper.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         🌟 Route49
// @name:tr      🌟 Route49
// @name:en      🌟 Route49
// @name:pt-BR   🌟 Route49
// @namespace    pop.route49
// @version      1.7
// @description     Popmundo turne planlayΔ±cΔ±sΔ± β€” coğrafi optimizasyon, şablon sistemi ve konser arayΓΌzΓΌ yardΔ±mcΔ±sΔ±.
// @description:tr  Popmundo turne planlayΔ±cΔ±sΔ± β€” coğrafi optimizasyon, şablon sistemi ve konser arayΓΌzΓΌ yardΔ±mcΔ±sΔ±.
// @description:en  Popmundo tour planner β€” geographic optimization, template system and booking interface helper.
// @description:pt-BR Planejador de turnΓͺ Popmundo β€” otimizaΓ§Γ£o geogrΓ‘fica, sistema de modelos e assistente de interface de shows.
// @author       luke-james-gibson
// @license      MIT
// @match        https://*.popmundo.com/*
// @run-at       document-end
// @grant        unsafeWindow
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_info
// ==/UserScript==

(function () {
    'use strict';

    try { const p = JSON.parse(localStorage.getItem('ppc_enabled') || '{}'); if (p['route49'] === false) return; } catch {}

    // ─── UTILS ───────────────────────────────────────────────────────────────────
    const DEBUG   = localStorage.getItem('r49_debug') === 'true';
    const log     = (...a) => DEBUG && console.log('[Route49]', ...a);
    const delay   = ms => new Promise(r => setTimeout(r, ms));
    const rdelay  = () => { try { const f = JSON.parse(localStorage.getItem('GreasyMonkey_r49_settings')||GM_getValue('r49_settings','{"fastMode":false}')).fastMode; return delay(f ? 500+Math.random()*1000 : 2000+Math.random()*4000); } catch { return delay(2000+Math.random()*4000); } };
    const mk      = (tag, cls, txt) => { const e = document.createElement(tag); if (cls) e.className = cls; if (txt != null) e.textContent = txt; return e; };
    const mkB     = (txt, cls, fn) => Object.assign(mk('button', cls, txt), { onclick: fn, type: 'button' });
    const $       = s => document.querySelector(s);
    const norm    = s => { try { return String(s||'').normalize('NFD').replace(/\p{M}/gu,'').toLowerCase().trim(); } catch { return String(s||'').toLowerCase().replace(/[\u0300-\u036f]/g,'').trim(); } };
    const fmtISO  = d => `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
    const fmtDisp = d => `${String(d.getDate()).padStart(2,'0')}.${String(d.getMonth()+1).padStart(2,'0')}.${d.getFullYear()}`;
    const addDays = (d, n) => { const r = new Date(d); r.setDate(r.getDate() + n); return r; };

    // ─── LANGUAGE ─────────────────────────────────────────────────────────────────
    const LANG = (() => { try { const m = document.cookie.match(/ppm_lang=([^;]+)/); return m ? m[1] : 'TR'; } catch { return 'TR'; } })();
    const _D   = (tr, en, pt) => ({ TR: tr, EN: en, PT: pt }[LANG] || tr);
    const _LC  = (() => { try { return JSON.parse(localStorage.getItem('ppc_lc_route49') || '{}'); } catch { return {}; } })();
    const T    = k => _LC[k] || S[k] || k;

    function setLang(lang) {
        document.cookie = `ppm_lang=${lang};path=/;max-age=31536000`;
        location.reload();
    }

    // ─── PM DATE ENGINE ───────────────────────────────────────────────────────────
    const PM_ANCHOR   = new Date('2026-02-25T00:00:00');
    const MS_DAY      = 86400000;
    const PM_YEAR_LEN = 56;
    const TODAY       = new Date();

    const realToPM  = d => { const days = Math.floor((d - PM_ANCHOR) / MS_DAY); return { year: 152 + Math.floor(days / PM_YEAR_LEN), day: ((days % PM_YEAR_LEN) + PM_YEAR_LEN) % PM_YEAR_LEN + 1 }; };
    const pmToReal  = (year, day) => new Date(PM_ANCHOR.getTime() + ((year - 152) * PM_YEAR_LEN + (day - 1)) * MS_DAY);
    const dateLabel = d => { const p = realToPM(d); return `${fmtDisp(d)} (${_D('YΔ±l','Year','Ano')} ${p.year} ${_D('GΓΌn','Day','Dia')} ${p.day})`; };

    // ─── SPECIAL DAYS ─────────────────────────────────────────────────────────────
    const SPECIAL_DAYS = {
        1:  { icon:'🎊', label:_D('Yeni Yılın İlk Günü','New Year\'s Day','Ano Novo'),                anchor:false },
        9:  { icon:'πŸ’₯', label:_D('Big Bang','Big Bang','Big Bang'),                                   anchor:true  },
        13: { icon:'πŸŽ‰', label:_D('Popopalooza GΓΌnΓΌ','Popopalooza Day','Dia Popopalooza'),              anchor:false },
        22: { icon:'πŸ¦‘', label:_D('Kraken\'in DΓΆnüşü','Return of the Kraken','Retorno do Kraken'),     anchor:false },
        28: { icon:'πŸ’€', label:_D('Γ–lΓΌler GΓΌnΓΌ','Day of the Dead','Dia dos Mortos'),                   anchor:true  },
        40: { icon:'πŸ”οΈ', label:_D('Aziz Kobe GΓΌnΓΌ','Saint Kobe\'s Day','Dia de SΓ£o Kobe'),            anchor:true  },
        48: { icon:'πŸŽƒ', label:_D('CadΔ±lar BayramΔ±','Halloween','Halloween'),                           anchor:false },
        52: { icon:'πŸŽ„', label:_D('Noel','Christmas','Natal'),                                          anchor:false },
        54: { icon:'πŸ•ŠοΈ', label:_D('Kurtuluş GΓΌnΓΌ','Liberation Day','Dia da LibertaΓ§Γ£o'),              anchor:false },
        56: { icon:'πŸ₯‚', label:_D('Yeni YΔ±l Arifesi','New Year\'s Eve','VΓ©spera de Ano Novo'),         anchor:false },
    };
    const ANCHOR_DAYS = Object.entries(SPECIAL_DAYS).filter(([,v]) => v.anchor);

    // ─── CITY CATALOG ─────────────────────────────────────────────────────────────
    const CITIES =[
        {id:8,  en:'Amsterdam',      tz:1  },
        {id:35, en:'Ankara',         tz:3  },
        {id:61, en:'Antalya',        tz:3  },
        {id:58, en:'Baku',           tr:'BakΓΌ',           tz:4  },
        {id:9,  en:'Barcelona',      tr:'Barselona',      tz:1  },
        {id:36, en:'Belgrade',       tr:'Belgrad',        tz:1  },
        {id:7,  en:'Berlin',         tz:1  },
        {id:33, en:'Brussels',       tr:'BrΓΌksel',        tz:1  },
        {id:42, en:'Budapest',       tr:'Budapeşte',      tz:1  },
        {id:17, en:'Buenos Aires',   tz:-3 },
        {id:46, en:'Bucharest',      tr:'Bükreş',         tz:2  },
        {id:55, en:'Jakarta',        tr:'Cakarta',        tz:7  },
        {id:29, en:'Dubrovnik',      tz:1  },
        {id:27, en:'Glasgow',        tz:0  },
        {id:19, en:'Helsinki',       tz:2  },
        {id:30, en:'Istanbul',       tr:'Δ°stanbul',       tz:3  },
        {id:47, en:'Izmir',          tr:'Δ°zmir',          tz:3  },
        {id:51, en:'Johannesburg',   tz:2  },
        {id:22, en:'Copenhagen',     tr:'Kopenhag',       tz:1  },
        {id:56, en:'Kyiv',           tz:2  },
        {id:5,  en:'London',         tr:'Londra',         tz:0  },
        {id:14, en:'Los Angeles',    tz:-8 },
        {id:24, en:'Madrid',         tz:1  },
        {id:54, en:'Manila',         tz:8  },
        {id:10, en:'Melbourne',      tz:11 },
        {id:32, en:'Mexico City',    tz:-6 },
        {id:52, en:'Milan',          tr:'Milano',         tz:1  },
        {id:38, en:'Montreal',       tz:-5 },
        {id:18, en:'Moscow',         tr:'Moskova',        tz:3  },
        {id:11, en:'Nashville',      tz:-6 },
        {id:6,  en:'New York',       tz:-5 },
        {id:20, en:'Paris',          tz:1  },
        {id:31, en:'Porto',          tz:0  },
        {id:25, en:'Rio de Janeiro', tz:-3 },
        {id:23, en:'Rome',           tr:'Roma',           tz:1  },
        {id:45, en:'Shanghai',       tr:'Şangay',         tz:8  },
        {id:21, en:'Sao Paulo',      tz:-3 },
        {id:49, en:'Sarajevo',       tr:'Saraybosna',     tz:1  },
        {id:50, en:'Seattle',        tz:-8 },
        {id:60, en:'Chicago',        tr:'Şikago',         tz:-6 },
        {id:39, en:'Singapore',      tr:'Singapur',       tz:8  },
        {id:53, en:'Sofia',          tr:'Sofya',          tz:2  },
        {id:1,  en:'Stockholm',      tr:'Stokholm',       tz:1  },
        {id:34, en:'Tallinn',        tz:2  },
        {id:62, en:'Tokyo',          tz:9  },
        {id:16, en:'Toronto',        tz:-5 },
        {id:26, en:'TromsΓΈ',         tz:1  },
        {id:48, en:'Warsaw',         tr:'Varşova',        tz:1  },
        {id:28, en:'Vilnius',        tz:2  },
    ];
    const cityById  = id  => CITIES.find(c => c.id === id);
    const cityByAny = str => CITIES.find(c => norm(c.en)===norm(str) || norm(c.tr)===norm(str));
    const cityName  = c   => c ? (LANG==='TR' ? (c.tr||c.en) : c.en) : '?';

    // ─── TOUR TYPES & PLANS (v0.5 Architecture) ───────────────────────────────────
    const _TT =[30,47,61,35,58,18,56,45,54,55,39,62,10,17,25,21,32,14,50,60,11,16,38,6,5,27,31,24,9,20,33,8,7,22,26,1,19,34,28,48,42,46,53,36,49,29,23,52,51];
    const _LI =[5,27,6,38,16,11,60,50,14,32,21,25,17,10,55,39,54,45,62,51,31,24,9,20,33,8,7,22,26,1,19,34,28,48,42,46,53,36,49,29,23,52,18,56,58,35,61,47,30];

    const TOUR_PLANS = {
        'tt': { name:'β˜€οΈ GΓΌneş Δ°zinde (Istanbul β†’ Johannesburg)', legs:_TT },
        'li': { name:'πŸ™οΈ Prime Line (London β†’ Istanbul)', legs:_LI },
        'custom': { name:_D('✏️ Γ–zel Rota','✏️ Custom Route','✏️ Rota Personalizada'), legs:[] },
    };

    const TOUR_TYPES = {
        'relax':    { name:'😌 Relax (56 Gün)', dpc:2, doubleDay:true, durationDays:56 },
        'blitz':    { name:'⚑ Blitz (25 Gün)', dpc:1, doubleDay:true,  durationDays:25, blitzMode:true },
        'hardcore': { name:'🀘  Hardcore (51 Gün)', dpc:2, doubleDay:true,  durationDays:51 },
        'custom':   { name:'βš™οΈ Γ–zel Tarihler',  dpc:1, doubleDay:false, durationDays:56 },
    };

    // ─── STORAGE ──────────────────────────────────────────────────────────────────
    const GMK = {
        status:  'r49_status',   // 'IDLE'|'RUNNING'|'PAUSED'
        tour:    'r49_tour',
        idx:     'r49_idx',
        sets:    'r49_settings',
        restore: 'r49_restore',
        stadU:   'r49_stad_used',
        stadY:   'r49_stad_year',
        schemas: 'r49_schemas',  // Dynamic array of saved schemas
    };

    const gmGet = (k,d) => { try { const v=GM_getValue(k,null); return v!==null?JSON.parse(v):d; } catch { return d; } };
    const gmSet = (k,v) => { try { GM_setValue(k,JSON.stringify(v)); } catch {} };
    const gmDel = k     => { try { GM_deleteValue(k); } catch {} };

    // ─── STRINGS ──────────────────────────────────────────────────────────────────
    const S = {
        title:          _D('🌟 Route49',                    '🌟 Route49',                      '🌟 Route49'),
        tabPlan:        _D('πŸ“‹ Plan',                       'πŸ“‹ Plan',                         'πŸ“‹ Plano'),
        tabSaved:       _D('πŸ’Ύ Şemalar',                    'πŸ’Ύ Schemas',                      'πŸ’Ύ Esquemas'),
        tabSettings:    _D('βš™οΈ Ayarlar',                   'βš™οΈ Settings',                     'βš™οΈ Config'),
        tourType:       _D('Turne TΓΌrΓΌ',                    'Tour Type',                       'Tipo de TurnΓͺ'),
        tourPlan:       _D('Turne PlanΔ±',                   'Tour Plan',                       'Plano de TurnΓͺ'),
        startCity:      _D('Başlangıç Şehri',               'Start City',                      'Cidade Inicial'),
        customHint:     _D('Şehir adlarΔ± (Δ°ngilizce), her satΔ±ra bir tane:','City names (English), one per line:','Nomes das cidades (inglΓͺs), um por linha:'),
        artistId:       _D('SanatΓ§Δ± ID',                    'Artist ID',                       'ID do Artista'),
        startDate:      _D('Başlangıç',                     'Start',                           'Início'),
        endDate:        _D('Bitiş',                         'Fim',                             'Fim'),
        showsPerCity:   _D('Şehir başına 2',                '2 per city',                      '2 por cidade'),
        doubleDay:      _D('GΓΌnde 2 konser',                '2 shows per day',                 '2 shows por dia'),
        showsDDNote:    _D('Günde 2 konser seçili değilse şehir başına 2 konser yapılamaz.','Requires 2 shows/day to enable 2 per city.','Requer 2 shows/dia para ativar 2 por cidade.'),
        showTimes:      _D('Konser Saatleri (Ctrl+tΔ±k)',    'Show Times (Ctrl+click)',          'HorΓ‘rios (Ctrl+clique)'),
        fameScore:      _D('Ortalama Şâhret',               'Avg. Fame Score',                 'Fama Média'),
        priceRange:     _D('Bilet FiyatΔ± (M$)',             'Ticket Price (M$)',               'Ingresso (M$)'),
        minStars:       _D('Minimum YΔ±ldΔ±z',                'Min. Stars',                      'Estrelas MΓ­nimas'),
        sortMode:       _D('KulΓΌp SΔ±ralamasΔ±',              'Club Sort',                       'OrdenaΓ§Γ£o'),
        sortHigh:       _D('En YΓΌksek Fiyat',               'Highest Price',                   'Maior PreΓ§o'),
        sortLow:        _D('En Düşük Fiyat',                'Lowest Price',                    'Menor Preço'),
        sortMid:        _D('Orta Fiyat',                    'Mid-Range',                       'PreΓ§o MΓ©dio'),
        sdTitle:        _D('Γ–zel GΓΌn AyarlarΔ±',             'Special Day Settings',            'Config. de Dias Especiais'),
        sdAra:          _D('Ara',                           'Rest',                            'Descanso'),
        stadCities:     _D('Stadyum Şehirleri',             'Stadium Cities',                  'Cidades EstÑdio'),
        citiesLabel:    _D('Şehirler',                      'Cities',                          'Cidades'),
        selAll:         _D('TΓΌmΓΌ',                          'All',                             'Todos'),
        selNone:        _D('HiΓ§biri',                       'None',                            'Nenhum'),
        btnPreview:     _D('πŸ‘ Γ–nizle',                      'πŸ‘ Preview',                       'πŸ‘ PrΓ©-ver'),
        btnStart:       _D('β–Ά Başlat',                      'β–Ά Start',                         'β–Ά Iniciar'),
        btnStop:        _D('⏹ Durdur',                      '⏹ Stop',                          '⏹ Parar'),
        btnPause:       _D('⏸ Duraklat',                   '⏸ Pause',                         '⏸ Pausar'),
        btnResume:      _D('β–Ά Devam',                       'β–Ά Resume',                        'β–Ά Retomar'),
        btnExportCSV:   _D('⬇ CSV Δ°ndir',                   '⬇ Export CSV',                    '⬇ Exportar'),
        btnImportCSV:   _D('⬆ CSV YΓΌkle',                   '⬆ Import CSV',                    '⬆ Importar'),
        btnClose:       _D('βœ• Kapat',                       'βœ• Close',                         'βœ• Fechar'),
        btnReadme:      _D('πŸ“– Beni Oku',                   'πŸ“– README',                        'πŸ“– Leia-me'),
        colDay:         _D('Pop GΓΌnΓΌ',                      'Pop Day',                         'Dia Pop'),
        colDate:        _D('Tarih',                         'Date',                            'Data'),
        colTime:        _D('Saat',                          'Time',                            'Hora'),
        colCity:        _D('Şehir',                         'City',                            'Cidade'),
        colVenue:       _D('Mekan',                         'Venue',                           'Local'),
        colPrice:       _D('Fiyat',                         'Price',                           'PreΓ§o'),
        venueBar:       _D('Bar',                           'Bar',                             'Bar'),
        venueStad:      _D('🏟 Stadyum',                   '🏟 Stadium',                      '🏟 EstÑdio'),
        warnTZ:         _D('⏰ Saat Farkı',                 '⏰ TZ Gap',                        '⏰ Fuso'),
        warnFuture:     _D('⏳ Henüz Erken',                '⏳ Too Early',                     '⏳ Cedo'),
        statusIdle:     _D('HazΔ±r.',                        'Ready.',                          'Pronto.'),
        statusDone:     _D('βœ… Tur tamamlandΔ±!',            'βœ… Tour complete!',                'βœ… TurnΓͺ completa!'),
        statusFuture:   _D('⏳ Devam için bekle:',          '⏳ Wait to continue:',             '⏳ Aguardar:'),
        limit57:        _D('57 gΓΌn sΔ±nΔ±rΔ± β€” plan kaydedildi.','57-day limit β€” plan saved.','Limite 57 dias β€” plano salvo.'),
        errorPaused:    _D('Hata tespit edildi, duraklatΔ±ldΔ±.','Error detected, paused.','Erro detectado, pausado.'),
        noTour:         _D('Tur boş. Şehir veya tarih ayarlarΔ±nΔ± kontrol et.','Tour is empty.','TurnΓͺ vazia.'),
        confirmStop:    _D('Durdur ve verileri temizle?','Stop and clear data?','Parar e limpar dados?'),
        savedPlanHdr:   _D('Son Kaydedilen',                'Last Saved',                      'Último Salvo'),
        btnEdit:        _D('DΓΌzenle',                       'Edit',                            'Editar'),
        btnStartPlan:   _D('β–Ά Başlat',                      'β–Ά Start',                         'β–Ά Iniciar'),
        planName:       _D('Şema Adı',                      'Schema Name',                     'Nome do Esquema'),
        btnSave:        _D('πŸ’Ύ Kaydet',                      'πŸ’Ύ Save',                          'πŸ’Ύ Salvar'),
        summaryShows:   _D('konser',                        'shows',                           'shows'),

        // Artist quick-links (TOP VIP menΓΌsΓΌ)
        mnuSchedule:    _D('Program',                       'Schedule',                        'Agenda'),
        mnuSetlist:     _D('Performans PlanΔ± DΓΌzenleyici',  'Setlist Editor',                  'Editor de Setlist'),
        mnuPopularity:  _D('PopΓΌlerlik',                    'Popularity',                      'Popularidade'),
        mnuRepertoire:  _D('Repertuar',                     'Repertoire',                      'RepertΓ³rio'),
        mnuUpcoming:    _D('Gelecek Konserler',             'Upcoming Concerts',               'PrΓ³ximos Shows'),
        mnuEquipment:   _D('Sahne EkipmanΔ±',               'Stage Equipment',                 'Equipamento de Palco'),
        mnuVehicle:     _D('Tur AracΔ±',                     'Tour Vehicle',                    'VeΓ­culo de TurnΓͺ'),
        mnuVehicleItems:_D('Şahsi Eşyalar',                 'Personal Items',                  'Itens Pessoais'),
        mnuCrew:        _D('Teknik Ekip',                   'Technical Crew',                  'Equipe TΓ©cnica'),
    };

    // ─── PRICE GUIDE ──────────────────────────────────────────────────────────────
    const PRICE_GUIDE =[
        {score:0,  min:1,  max:5  },
        {score:1,  min:1,  max:6  },
        {score:2,  min:2,  max:7  },
        {score:3,  min:4,  max:8  },
        {score:4,  min:6,  max:9  },
        {score:5,  min:8,  max:11 },
        {score:6,  min:10, max:13 },
        {score:7,  min:12, max:15 },
        {score:8,  min:14, max:20 },
        {score:9,  min:16, max:25 },
        {score:10, min:18, max:30 },
        {score:11, min:20, max:35 },
        {score:12, min:25, max:45 },
        {score:13, min:30, max:50 },
        {score:14, min:35, max:55 },
        {score:15, min:40, max:60 },
        {score:16, min:45, max:75 },
        {score:17, min:50, max:80 },
        {score:18, min:55, max:85 },
        {score:19, min:60, max:90 },
        {score:20, min:65, max:95 },
        {score:21, min:70, max:100},
        {score:22, min:75, max:105},
        {score:23, min:80, max:110},
        {score:24, min:85, max:115},
        {score:25, min:90, max:120},
        {score:26, min:95, max:125},
    ];

    // ─── DEFAULTS ─────────────────────────────────────────────────────────────────
    const MIN_DATE_STR = fmtISO(addDays(TODAY, 3));
    const DEF = {
        tourType:'relax', tourPlan:'tt', startCityId:30,
        doubleDay:true, dpc:2, fastMode:false,
        showTimes:['14:00:00','22:00:00'],
        priceMin:30, priceMax:50, minStars:50, sortMode:'price_desc',
        startDate:MIN_DATE_STR, endDate:fmtISO(addDays(new Date(MIN_DATE_STR+'T00:00:00'), 56)),
        artistId:'', enabledCities:CITIES.map(c=>c.id), stadiumCities:[], anchors:{},
    };

    // ─── BOOKSHOW SELECTORS ───────────────────────────────────────────────────────
    const SEL = {
        venueType:'#ctl00_cphLeftColumn_ctl01_ddlVenueTypes',
        city:     '#ctl00_cphLeftColumn_ctl01_ddlCities',
        day:      '#ctl00_cphLeftColumn_ctl01_ddlDays',
        hour:     '#ctl00_cphLeftColumn_ctl01_ddlHours',
        findBtn:  '#ctl00_cphLeftColumn_ctl01_btnFindClubs',
        clubTable:'#tableclubs',
        bookBtn:  '#ctl00_cphLeftColumn_ctl01_btnBookShow',
    };
    const IS_BOOKSHOW = /\/Artist\/BookShow/i.test(window.location.pathname);
    // ─── TOUR BUILDER (v0.5 Blitz & Anchor Logic) ─────────────────────────────────
    function normLeg(l) { return typeof l==='number' ? {id:l,gapAfter:0} : {id:l.id,gapAfter:l.gapAfter||0}; }

    function buildTour(settings) {
        const typeKey = settings.tourType || 'relax';
        const planKey = settings.tourPlan || 'tt';
        const typeCfg = TOUR_TYPES[typeKey] || TOUR_TYPES['relax'];

        let rawLegs;
        if (planKey==='custom') rawLegs = parseCustomRoute(settings.customRoute||'').map(id=>({id,gapAfter:0}));
        else rawLegs = (TOUR_PLANS[planKey]?.legs||[]).map(normLeg);

        const enabled  = new Set(settings.enabledCities||DEF.enabledCities);
        const stadSet  = new Set(settings.stadiumCities||[]);
        const anchors  = settings.anchors||{};
        const minDateLimit = new Date(MIN_DATE_STR+'T00:00:00');

        let stadUsed = (() => {
            const cy=realToPM(new Date()).year, ly=gmGet(GMK.stadY,cy);
            return ly===cy ? gmGet(GMK.stadU,0) : 0;
        })();

        let sDateStr = settings.startDate; if(new Date(sDateStr+'T00:00:00') < minDateLimit) sDateStr = MIN_DATE_STR;
        const startDate = new Date(sDateStr+'T00:00:00');
        const endDate   = new Date(settings.endDate  +'T00:00:00');

// Resolve anchor dates
        const anchorDates = {}; // isoDate -> {cityId, restDays}
        const anchorCities = new Set();
        for (const[dayStr,anch] of Object.entries(anchors)) {
            if (!anch.cityId && !anch.restDays) continue;
            const pmDay = parseInt(dayStr);
            const startPM = realToPM(startDate);
            for (let yo=0; yo<=2; yo++) {
                const cand = pmToReal(startPM.year+yo, pmDay);
                if (cand>=startDate && cand<=endDate) {
                    anchorDates[fmtISO(cand)] = { cityId: anch.cityId, restDays: anch.restDays };
                    if (anch.cityId) anchorCities.add(anch.cityId);
                    break;
                }
            }
        }

        let pendingAnchors = Object.entries(anchorDates).map(([d, a]) => ({ date: new Date(d+'T00:00:00'), cityId: a.cityId, restDays: a.restDays })).sort((a,b)=>a.date-b.date);

        // Remove anchored cities from normal flow, except start/end
        const legs = rawLegs.filter((l, i) => {
            if (i === 0 || i === rawLegs.length - 1) return true;
            return !anchorCities.has(l.id);
        });

        let startIdx=0;
        if (settings.startCityId && planKey!=='custom') {
            const fi = legs.findIndex(l=>l.id===settings.startCityId);
            if (fi>0) startIdx=fi;
        }

        const orderedLegs = legs.slice(startIdx).concat(legs.slice(0, startIdx));

        let cursor = new Date(startDate);
        const tour =[];
        let blitzToggle = 0; // 0 = 14:00, 1 = 22:00

        const time22 = settings.showTimes?.includes('22:00:00') ? '22:00:00' : (settings.showTimes?.slice(-1)[0]||'22:00:00');
        const time14 = settings.showTimes?.includes('14:00:00') ? '14:00:00' : (settings.showTimes?.[0]||'14:00:00');
        const singleTime = settings.showTimes?.[0]||'22:00:00';

        for (let i=0; i<orderedLegs.length; i++) {
            const leg    = orderedLegs[i];
            const cityId = leg.id;

            if (!enabled.has(cityId)) { if (leg.gapAfter) cursor=addDays(cursor,leg.gapAfter); continue; }
            if (cursor > endDate) break;

// Dinlenme günlerini işleyen yardımcı fonksiyon
            const processAnchors = () => {
                while (pendingAnchors.length > 0 && pendingAnchors[0].date <= cursor) {
                    const anc = pendingAnchors.shift();
                    if (anc.cityId) {
                        let aTime = time22;
                        if (tour.length > 0) {
                            const lc = cityById(tour[tour.length-1].cityId), tc = cityById(anc.cityId);
                            if (lc && tc && Math.abs(lc.tz - tc.tz) > 10) aTime = '22:00:00';
                        }
                        tour.push(mkShow(anc.cityId, anc.date, aTime, stadSet.has(anc.cityId)));
                    }
                    const nextAvail = addDays(anc.date, (anc.cityId ? 1 : 0) + (anc.restDays || 0));
                    if (cursor < nextAvail) cursor = nextAvail;
                }
            };

            processAnchors();
            if (cursor > endDate) break;

            let baseT1 = time14, baseT2 = time22;
            if (tour.length > 0) {
                const lc = cityById(tour[tour.length-1].cityId), tc = cityById(cityId);
                if (lc && tc && Math.abs(lc.tz - tc.tz) > 10) baseT1 = '22:00:00';
            }

            const isStad = stadSet.has(cityId) && stadUsed<10;
            if (isStad) stadUsed++;

            const isDouble = typeCfg.blitzMode ? true : settings.doubleDay;
            const dpcVal   = typeCfg.blitzMode ? 1 : settings.dpc;

if (typeCfg.blitzMode) {
                tour.push(mkShow(cityId, cursor, blitzToggle === 0 ? baseT1 : baseT2, isStad));
                if (blitzToggle === 1) cursor = addDays(cursor, 1);
                blitzToggle = blitzToggle === 0 ? 1 : 0;
            } else if (isDouble && dpcVal >= 2) {
                tour.push(mkShow(cityId, cursor, baseT1, isStad));
                tour.push(mkShow(cityId, cursor, baseT2, false));
                cursor = addDays(cursor, 1);
            } else {
                tour.push(mkShow(cityId, cursor, isDouble ? baseT2 : singleTime, isStad));
                cursor = addDays(cursor, 1);
            }

            if (leg.gapAfter) cursor=addDays(cursor,leg.gapAfter);
        }

// Final sweep for remaining anchors
        while(pendingAnchors.length > 0 && pendingAnchors[0].date <= endDate) {
            const anc = pendingAnchors.shift();
            if (anc.cityId) tour.push(mkShow(anc.cityId, anc.date, time22, stadSet.has(anc.cityId)));
        }

        // Sort naturally by date & time
        tour.sort((a,b) => new Date(a.date+'T'+a.time) - new Date(b.date+'T'+b.time));
        recalcWarnings(tour);
        return tour;
    }

    function mkShow(cityId, dateObj, time, wantStad) {
        return {cityId, date:fmtISO(dateObj), time, venueType:wantStad?'stadium':'bar', booked:false, skipped:false};
    }

    function parseCustomRoute(text) {
        return text.split('\n').map(l=>l.trim()).filter(Boolean).map(l=>cityByAny(l)).filter(Boolean).map(c=>c.id);
    }

    function tourSummary(tour) {
        if (!tour.length) return '';
        const cities  = new Set(tour.map(t=>t.cityId)).size;
        const stads   = tour.filter(t=>t.venueType==='stadium').length;
        const booked  = tour.filter(t=>t.booked).length;
        const future  = tour.filter(t=>(t.warnings||[]).includes('future')).length;
        let s=`${tour.length} ${T('summaryShows')} · ${cities} ${_D('şehir','cities','cidades')} · 🏟${stads}/10`;
        if (booked)  s+=` Β· βœ…${booked}`;
        if (future)  s+=` · ⏳${future}`;
        return s;
    }

    // ─── CSV ──────────────────────────────────────────────────────────────────────
    function tourToCSV(tour, settings) {
        const BOM='\uFEFF';
        const hdr=['Pop Day','Date','Time','City','Venue','Price Min','Price Max','Status'].join(',');
        const rows=tour.map(t=>{
            const c=cityById(t.cityId), pm=realToPM(new Date(t.date+'T00:00:00'));
            const sd=t.specialDay?SPECIAL_DAYS[t.specialDay]?.icon:'';
            return[`${pm.year}-${pm.day}${sd?' '+sd:''}`, t.date, t.time.slice(0,5),
                `"${c?.en||t.cityId}"`, t.venueType==='stadium'?'Stadium':'Bar',
                settings?.priceMin??'', settings?.priceMax??'',
                t.booked?'booked':t.skipped?'skipped':''].join(',');
        });
        return BOM+[hdr,...rows].join('\r\n');
    }
    function csvToTour(csvText) {
        const lines=csvText.replace(/^\uFEFF/,'').trim().split(/\r?\n/);
        const tour = lines.slice(1).map(line=>{
            const cols=line.split(',').map(c=>c.replace(/^"|"$/g,'').trim());
            if (cols.length<4) return null;
            const city=cityByAny(cols[3]);
            if (!city) return null;
            let date=cols[1];
            if(date.includes('.')) { const p=date.split('.'); date=`${p[2]}-${p[1].padStart(2,'0')}-${p[0].padStart(2,'0')}`; }
            if(!date.match(/^\d{4}-\d{2}-\d{2}$/)) return null;
            const time=cols[2].length===5?cols[2]+':00':cols[2];
            return {cityId:city.id,date:cols[1],time,venueType:/stadium/i.test(cols[4]||'')?'stadium':'bar',booked:cols[7]==='booked',skipped:cols[7]==='skipped'};
        }).filter(Boolean);

        recalcWarnings(tour);
        return tour;
    }

    function dlCSV(text, fname) {
        const blob=new Blob([text],{type:'text/csv;charset=utf-8;'});
        const url=URL.createObjectURL(blob);
        const a=document.createElement('a'); a.href=url; a.download=fname; a.style.display='none';
        document.body.appendChild(a); a.click(); a.remove();
        setTimeout(()=>URL.revokeObjectURL(url),1000);
    }

    // ─── BOOKING ENGINE ───────────────────────────────────────────────────────────
    function isStadRow(row) { return !!(row.cells[1]?.querySelector('img[title]')); }

    function parseClubRow(row) {
        const name  = row.cells[0]?.querySelector('a')?.textContent?.trim()||'';
        const radio = row.cells[0]?.querySelector('input[type="radio"]');
        const avTxt = row.cells[1]?.textContent||'';
        const avM   = avTxt.match(/(\d+)\s*\/\s*(\d+)/);
        const stars = parseInt(row.querySelector('.sortkey')?.textContent||'0');
        const price = parseFloat((row.cells[row.cells.length-1]?.textContent||'').replace(/\s/g,'').replace(/M\$/,'').replace(',','.'))||0;
        return {name,radio,remaining:avM?(parseInt(avM[2])-parseInt(avM[1])):99,stars,price,stadium:isStadRow(row)};
    }

    function detectError() { return !!document.querySelector('.notification-real.notification-error,.notification-error'); }

    async function attemptBook(radio) {
        if (!radio) return false;
        radio.click();
        await delay(400);
        const btn=$(SEL.bookBtn); if (!btn) return false;
        btn.click();
        await delay(900);
        const ok=Array.from(document.querySelectorAll('.ui-dialog-buttonpane button,.ui-dialog button'))
            .find(b=>/yes|ok|onayla|evet|sim|confirm/i.test(b.textContent));
        if (ok) { ok.click(); await delay(1200); }
        if (detectError()) return false;
        return true;
    }

    async function findAndBook(settings, show) {
        const tbl=$(SEL.clubTable); if (!tbl) return false;

        const dayEl = $(SEL.day);
        if (dayEl && dayEl.value !== show.date) {
            log(`[Strict Match] Table loaded but date (${dayEl.value}) differs from target (${show.date}).`);
            return false;
        }

        const rows    = Array.from(tbl.querySelectorAll('tbody tr'));
        const minStar = settings.minStars ?? 50;
        const pMin    = settings.priceMin ?? DEF.priceMin;
        const pMax    = settings.priceMax ?? DEF.priceMax;
        const wantSt  = show.venueType==='stadium';
        const sort    = settings.sortMode||'price_desc';

        let cands = rows.map(parseClubRow).filter(c=>{
            if (!c.radio||c.remaining<=0) return false;
            if (c.stars < minStar) return false;
            if (c.price<pMin||c.price>pMax) return false;
            return true;
        });

        // Fallback: drop stars
        if (!cands.length) cands = rows.map(parseClubRow).filter(c=>c.radio&&c.remaining>0&&c.price>=pMin&&c.price<=pMax);

        if (wantSt) { const st=cands.filter(c=>c.stadium); cands=st.length?st:cands.filter(c=>!c.stadium); }
        else { const bars=cands.filter(c=>!c.stadium); if (bars.length) cands=bars; }

        if (sort==='price_asc')  cands.sort((a,b)=>a.price-b.price);
        else if (sort==='price_mid') { const mid=(pMin+pMax)/2; cands.sort((a,b)=>Math.abs(a.price-mid)-Math.abs(b.price-mid)); }
        else cands.sort((a,b)=>b.price-a.price);

        for (const c of cands) {
            const ok=await attemptBook(c.radio);
            if (ok) {
                if (c.stadium) {
                    const cy=realToPM(new Date()).year, ly=gmGet(GMK.stadY,cy);
                    gmSet(GMK.stadU,(ly===cy?gmGet(GMK.stadU,0):0)+1); gmSet(GMK.stadY,cy);
                }
                log(`Booked: ${c.name} (${c.price} M$) ${show.date} ${show.time}`);
                return true;
            }
            if (detectError()) { log('Error detected, pausing.'); gmSet(GMK.status,'PAUSED'); updateFloatBar(); return false; }
            await delay(400);
        }
        return false;
    }

    function waitForTable(cb, ms=20000) {
        const t0=Date.now();
        const iv=setInterval(()=>{
            if ($(SEL.clubTable)) { clearInterval(iv); cb(); }
            else if (Date.now()-t0>ms) { clearInterval(iv); log('Table timeout β€” pausing'); gmSet(GMK.status,'PAUSED'); updateFloatBar(); }
        },300);
    }

    // ─── PROCESS NEXT SHOW ────────────────────────────────────────────────────────
    async function processNextShow(settings) {
        if (gmGet(GMK.status,'IDLE')!=='RUNNING') return;

        const tour = gmGet(GMK.tour,[]);
        let   idx  = gmGet(GMK.idx,0);

  // Skip already-handled (booked, failed max retries, or user unchecked)
        while (idx<tour.length && (tour[idx].booked || tour[idx].skipped || tour[idx].willBook === false)) idx++;

        if (idx>=tour.length) {
            gmSet(GMK.status,'IDLE'); updateFloatBar();
            showPanelStatus(T('statusDone'),'#28a745');
            return;
        }

        const show = tour[idx];

        // 57-day limit
        const showDate=new Date(show.date+'T00:00:00');
        const maxBook =addDays(TODAY,57);
        if (showDate>maxBook) {
            const wait=Math.ceil((showDate-maxBook)/MS_DAY);
            gmSet(GMK.idx,idx); gmSet(GMK.status,'PAUSED');
            updateFloatBar(`${T('statusFuture')} ${wait}d`);
            showPanelStatus(`${T('limit57')} (+${wait}d)`,'#e67e00');
            return;
        }

        const city=cityById(show.cityId);
        updateFloatBar();
        showPanelStatus(`β–Ά ${idx+1}/${tour.length}: ${cityName(city)} ${show.date} ${show.time}`,'#28a745');

const handleInvalidDate = async () => {
            log(`[Strict Match] Date ${show.date} not available in dropdown. Skipping show.`);
            tour[idx]={...show, booked:false, skipped:true};
            gmSet(GMK.tour,tour); gmSet(GMK.idx,idx+1);
            await rdelay();
            window.location.href=`https://${window.location.hostname}/World/Popmundo.aspx/Artist/BookShow/${settings.artistId}`;
        };

        // Restore mode (after city postback)
        const restore=gmGet(GMK.restore,null);
        if (restore) {
            gmDel(GMK.restore);
            const dayEl=$(SEL.day), hourEl=$(SEL.hour);
            if (dayEl) { dayEl.value=restore.date; if (dayEl.value !== restore.date) return handleInvalidDate(); }
            if (hourEl) { const o=Array.from(hourEl.options).find(o=>o.value.startsWith(restore.time.slice(0,5))); if(o) hourEl.value=o.value; }
            const venEl=$(SEL.venueType); if(venEl) venEl.value='0';
            await delay(250);
            $(SEL.findBtn)?.click();
            waitForTable(()=>bookAndAdvance(settings,show,idx,tour));
            return;
        }

        // Set city
        const cityEl=$(SEL.city); if(!cityEl) return;
        if (parseInt(cityEl.value)!==show.cityId) {
            gmSet(GMK.restore,{date:show.date,time:show.time});
            cityEl.value=String(show.cityId);
            cityEl.dispatchEvent(new Event('change',{bubbles:true}));
            return; // postback
        }

        if (!$(SEL.clubTable)) {
            const dayEl=$(SEL.day), hourEl=$(SEL.hour);
            if (dayEl) { dayEl.value=show.date; if (dayEl.value !== show.date) return handleInvalidDate(); }
            if (hourEl) { const o=Array.from(hourEl.options).find(o=>o.value.startsWith(show.time.slice(0,5))); if(o) hourEl.value=o.value; }
            const venEl=$(SEL.venueType); if(venEl) venEl.value='0';
            await delay(250);
            $(SEL.findBtn)?.click();
            waitForTable(()=>bookAndAdvance(settings,show,idx,tour));
        } else {
            await bookAndAdvance(settings,show,idx,tour);
        }
    }

    async function bookAndAdvance(settings, show, idx, tour) {
        if (gmGet(GMK.status,'IDLE')!=='RUNNING') return;

        const booked = await findAndBook(settings, show);
        if (gmGet(GMK.status,'IDLE')!=='RUNNING') return;

        if (booked) {
            tour[idx] = { ...show, booked: true, skipped: false };
            delete tour[idx]._retry;
            gmSet(GMK.tour, tour);
            gmSet(GMK.idx, idx + 1);
        } else {
            const retries = (show._retry || 0) + 1;
            if (retries >= 2) {
                log(`Skipped (max retries): ${show.date} ${show.time}`);
                tour[idx] = { ...show, booked: false, skipped: true };
                delete tour[idx]._retry;
                gmSet(GMK.tour, tour);
                gmSet(GMK.idx, idx + 1);
            } else {
                log(`Retry ${retries} for: ${show.date} ${show.time}`);
                tour[idx] = { ...show, _retry: retries };
                gmSet(GMK.tour, tour);
                // Do not increment idx, so it will retry
            }
        }

        await rdelay();
        const aid=settings.artistId;
        window.location.href=`https://${window.location.hostname}/World/Popmundo.aspx/Artist/BookShow/${aid}`;
    }

    // ─── FLOATING STATUS BAR ──────────────────────────────────────────────────────

    function injectFloatBar() {
        if (document.getElementById('r49-float')) return;
        const bar=mk('div'); bar.id='r49-float';
        bar.style.cssText='position:fixed;top:0;left:0;right:0;z-index:2147483647;background:#2d1b5e;color:#fff;font-family:sans-serif;font-size:12px;display:none;align-items:center;gap:8px;padding:4px 12px;box-shadow:0 2px 8px rgba(0,0,0,.5);';
        const txt=mk('span'); txt.id='r49-float-txt'; txt.style.flex='1';
        const logBtn=mkB('πŸ“œ','',()=>openLogViewer());
        logBtn.style.cssText='padding:2px 8px;border:1px solid #fcd34d;border-radius:4px;background:transparent;color:#fcd34d;cursor:pointer;font-size:11px;margin-right:4px;';
        
        const saveBtn=mkB('πŸ’Ύ','',()=>{
            const t=gmGet(GMK.tour,[]); const s=gmGet(GMK.sets,null);
            if(!t.length) return;
            const name = prompt(_D('Şema Adı:','Schema Name:','Nome do Esquema:'), `Saved_${fmtISO(new Date())}`);
            if (name) {
                const sc = gmGet(GMK.schemas,[]);
                sc.push({ id: Date.now(), name, tour: t, settings: s, templateDays: 56 });
                gmSet(GMK.schemas, sc);
                stopBooking();
            }
        });
        saveBtn.style.cssText='padding:2px 8px;border:1px solid #4ade80;border-radius:4px;background:transparent;color:#4ade80;cursor:pointer;font-size:11px;';
        saveBtn.title=_D('Daha Sonra Devam Et','Save & Continue Later','Salvar e Continuar Mais Tarde');

        const pbtn=mk('button'); pbtn.id='r49-float-pbtn';
        pbtn.style.cssText='padding:2px 10px;border:1px solid #fff;border-radius:4px;background:transparent;color:#fff;cursor:pointer;font-size:11px;';
        bar.append(txt,logBtn,saveBtn,pbtn);
        document.body.prepend(bar);
        setInterval(updateFloatBar,1500);
        updateFloatBar();
    }

    function updateFloatBar(customMsg) {
        const bar=document.getElementById('r49-float');
        const txt=document.getElementById('r49-float-txt');
        const pbtn=document.getElementById('r49-float-pbtn');
        if (!bar||!txt) return;

        const status=gmGet(GMK.status,'IDLE');
        const tour  =gmGet(GMK.tour,[]);
        const idx   =gmGet(GMK.idx,0);
        const sets  =gmGet(GMK.sets,null);

        if (status==='IDLE'&&!tour.length) { bar.style.display='none'; return; }
        bar.style.display='flex';

if (customMsg) { txt.textContent=customMsg; }
        else if (status==='PAUSED' || status==='RUNNING') {
            const s=tour[Math.min(idx, tour.length-1)];
            if (s) {
                const c = cityName(cityById(s.cityId)) || '?';
                const vIcon = s.venueType==='stadium'?'🏟':'🎡';
                const vName = s.venueType==='stadium'?T('venueStad'):T('venueBar');
                const p = sets ? `${sets.priceMin}-${sets.priceMax}` : '';
                const stars = sets ? Math.floor(sets.minStars/10) : '5';
                const dStr = fmtDisp(new Date(s.date+'T00:00:00'));
                const tStr = (s.time||'').slice(0,5);
                const prefix = status==='PAUSED' ? '⏸' : 'β–Ά';

                txt.textContent=`${prefix} ${vIcon} ${c} | ${dStr} | ${tStr} | ${vName} | ${p} M$ | ${stars}β˜… | ${idx+1}/${tour.length}`;
            } else {
                txt.textContent=`${status==='PAUSED'?'⏸':'β–Ά'} Route49 β€” ${idx}/${tour.length}`;
            }
        } else {
            txt.textContent=`🌟 Route49 β€” ${idx}/${tour.length}`;
        }

        // Auto update active summary in schemas tab if present
        const activeSumm = document.getElementById('r49-active-summary');
        if (activeSumm) activeSumm.textContent = tourSummary(tour);

        if (pbtn) {
            pbtn.textContent=status==='PAUSED'?T('btnResume'):T('btnPause');
            pbtn.onclick=()=>{
                if (status==='PAUSED') {
                    gmSet(GMK.status,'RUNNING');
                    const s=gmGet(GMK.sets,null);
                    if (s?.artistId) window.location.href=`https://${window.location.hostname}/World/Popmundo.aspx/Artist/BookShow/${s.artistId}`;
                } else {
                    gmSet(GMK.status,'PAUSED');
                }
                updateFloatBar();
            };
        }
    }

    // ─── UI HELPERS ───────────────────────────────────────────────────────────────
    const inpSt = 'padding:4px;border:1px solid #c9b8f0;border-radius:4px;box-sizing:border-box;font-family:inherit;font-size:12px;';
    const btnSt = 'padding:4px 10px;border:1px solid #6f42c1;border-radius:4px;cursor:pointer;font-size:12px;background:#e8e0f9;color:#6f42c1;font-family:inherit;';
    const fieldRow=(lbl,el,note)=>{ const w=mk('div'); w.style.cssText='margin-bottom:8px;'; const l=mk('label','',lbl); l.style.cssText='display:block;font-weight:600;font-size:11px;margin-bottom:3px;color:#444;'; w.append(l,el); if(note){const n=mk('div','',note);n.style.cssText='font-size:10px;color:#888;margin-top:2px;';w.appendChild(n);}  return w; };

    function showPanelStatus(txt,color) { const el=document.getElementById('r49-status'); if(!el) return; el.textContent=txt; if(color) el.style.color=color; }

    // ─── GATHER SETTINGS ──────────────────────────────────────────────────────────
    function gatherSettings() {
        const saved = gmGet(GMK.sets, null);
        const g=id=>document.getElementById(id);
        if (!g('r49-panel') && saved) return saved; // Panel kapalΔ±yken eski ayarlarΔ± koru (Fiyat NaN hatasΔ± Γ§ΓΆzΓΌmΓΌ)
        const type=g('r49-ttype')?.value||'relax';
        const plan=g('r49-tplan')?.value||'tt';
        const times=Array.from(document.querySelectorAll('#r49-times option:checked')).map(o=>o.value);
        const doubleDay=g('r49-doubleday')?.checked===true;
        const dpc=doubleDay && g('r49-dpc')?.checked ? 2 : 1;
        const enabledCities=CITIES.filter(c=>{const el=g(`r49-city-${c.id}`);return el?el.checked:true;}).map(c=>c.id);
        const stadiumCities=CITIES.filter(c=>g(`r49-stad-${c.id}`)?.checked).map(c=>c.id);
        const anchors={};
        for (const [day] of ANCHOR_DAYS) {
            const cityId=parseInt(g(`r49-sd-city-${day}`)?.value||'0')||null;
            const rest  =parseInt(g(`r49-sd-rest-${day}`)?.value ||'0')||0;
            if (cityId || rest > 0) anchors[day]={cityId,restDays:rest};
        }
        return {
            artistId:    g('r49-artist')?.value?.trim()||'',
            tourType:    type,
            tourPlan:    plan,
            startCityId: parseInt(g('r49-startcity')?.value||'0')||DEF.startCityId,
            customRoute: g('r49-custom')?.value||'',
            startDate:   g('r49-start')?.value||DEF.startDate,
            endDate:     g('r49-end')?.value  ||DEF.endDate,
            doubleDay, dpc,
            fastMode:    g('r49-fastmode')?.checked===true,
            showTimes:   times.length?times:DEF.showTimes,
            priceMin:    parseFloat(g('r49-pmin')?.value||String(DEF.priceMin)),
            priceMax:    parseFloat(g('r49-pmax')?.value||String(DEF.priceMax)),
            minStars:    parseInt(g('r49-minstars')?.value||'50'),
            sortMode:    g('r49-sort')?.value||'price_desc',
            enabledCities, stadiumCities, anchors,
        };
    }

    function applySettingsToUI(cfg) {
        if (!cfg) return;
        const g=id=>document.getElementById(id);
        if (cfg.fastMode !== undefined && g('r49-fastmode')) g('r49-fastmode').checked = !!cfg.fastMode;
        if (cfg.artistId    && g('r49-artist'))    g('r49-artist').value    = cfg.artistId;
        if (cfg.tourType    && g('r49-ttype'))     g('r49-ttype').value = cfg.tourType;
        if (cfg.tourPlan    && g('r49-tplan'))     { g('r49-tplan').value = cfg.tourPlan; toggleCustom(); }
        if (cfg.startCityId && g('r49-startcity')) g('r49-startcity').value = String(cfg.startCityId);
        if (cfg.startDate   && g('r49-start'))     { g('r49-start').value=cfg.startDate;  updateDL('r49-start','r49-start-lbl'); }
        if (cfg.endDate     && g('r49-end'))       { g('r49-end').value=cfg.endDate;      updateDL('r49-end',  'r49-end-lbl');   }
        if (cfg.doubleDay   !== undefined && g('r49-doubleday')) { g('r49-doubleday').checked=!!cfg.doubleDay; syncDoubleDayUI(); }
        if (cfg.dpc         !== undefined && g('r49-dpc'))       g('r49-dpc').checked = cfg.dpc>=2;
        if (cfg.priceMin    !== undefined && g('r49-pmin'))  g('r49-pmin').value=cfg.priceMin;
        if (cfg.priceMax    !== undefined && g('r49-pmax'))  g('r49-pmax').value=cfg.priceMax;
        if (cfg.minStars    !== undefined && g('r49-minstars')) g('r49-minstars').value=String(cfg.minStars);
        if (cfg.sortMode    && g('r49-sort'))       g('r49-sort').value=cfg.sortMode;
        if (Array.isArray(cfg.showTimes) && g('r49-times')) Array.from(g('r49-times').options).forEach(o=>o.selected=cfg.showTimes.includes(o.value));
        if (Array.isArray(cfg.enabledCities)) CITIES.forEach(c=>{const el=g(`r49-city-${c.id}`);if(el)el.checked=cfg.enabledCities.includes(c.id);});
        if (Array.isArray(cfg.stadiumCities)) CITIES.forEach(c=>{const el=g(`r49-stad-${c.id}`);if(el)el.checked=cfg.stadiumCities.includes(c.id);});
        if (cfg.anchors) for (const [d,a] of Object.entries(cfg.anchors)) {
            const ce=g(`r49-sd-city-${d}`); if(ce&&a.cityId) ce.value=String(a.cityId);
            const re=g(`r49-sd-rest-${d}`); if(re) re.value=String(a.restDays||0);
        }
        updateSummary();
    }

function toggleCustom() {
        const p=document.getElementById('r49-tplan')?.value;
        const t=document.getElementById('r49-ttype')?.value;
        const elC=document.getElementById('r49-custom-wrap');
        const elS=document.getElementById('r49-startcity-wrap');
        const elEnd=document.getElementById('r49-end-wrap');
        const elG2=elEnd?.parentElement;

        if (elC) elC.style.display=p==='custom'?'block':'none';
        if (elS) elS.style.display=p!=='custom'?'flex':'none';
        if (elEnd) elEnd.style.display=t==='custom'?'block':'none';
        if (elG2) elG2.style.gridTemplateColumns = t==='custom'?'repeat(auto-fit, minmax(130px, 1fr))':'1fr';
    }

    function syncDoubleDayUI() {
        const t=document.getElementById('r49-ttype')?.value;
        const dd=document.getElementById('r49-doubleday')?.checked;
        const dpcEl=document.getElementById('r49-dpc');
        const dpcRow=document.getElementById('r49-dpc-row');
        if (dpcEl) { if (!dd || t==='blitz') { dpcEl.checked=false; dpcEl.disabled=true; } else dpcEl.disabled=false; }
        if (dpcRow) dpcRow.style.opacity=(dd && t!=='blitz')?'1':'0.4';
    }

    function updateDL(inputId,labelId) {
        const inp=document.getElementById(inputId), lbl=document.getElementById(labelId);
        if(!inp||!lbl||!inp.value) return;
        lbl.textContent=dateLabel(new Date(inp.value+'T00:00:00'));
    }

    function updateSummary() {
        const el=document.getElementById('r49-summary'); if(!el) return;
        try { const s=gatherSettings(); el.textContent=tourSummary(buildTour(s)); } catch { el.textContent=''; }
    }

    function updateStadBadge() {
        const el=document.getElementById('r49-stad-badge'); if(!el) return;
        const cy=realToPM(new Date()).year, ly=gmGet(GMK.stadY,cy);
        const used=ly===cy?gmGet(GMK.stadU,0):0;
        el.textContent=`🏟 ${used}/10`; el.style.color=used>=10?'#dc3545':'#28a745';
    }

    // ─── GLOBAL OPTIMIZATION ENGINE ───────────────────────────────────────────────
    function recalcWarnings(tour) {
        const maxBook = addDays(TODAY, 57);
        let stadCountByYear = {};
        for (let i = 0; i < tour.length; i++) {
            const d = new Date(tour[i].date + 'T00:00:00');
            tour[i].warnings = [];
            if (d > maxBook) tour[i].warnings.push('future');

            if (tour[i].venueType === 'stadium' && tour[i].willBook !== false) {
                const pmYear = realToPM(d).year;
                stadCountByYear[pmYear] = (stadCountByYear[pmYear] || 0) + 1;
                if (stadCountByYear[pmYear] > 10) tour[i].warnings.push('stadium_limit');
            }

            if (i > 0) {
                const p = cityById(tour[i-1].cityId), c = cityById(tour[i].cityId);

                if (tour[i-1].date === tour[i].date && tour[i-1].cityId !== tour[i].cityId && tour[i].willBook !== false && tour[i-1].willBook !== false) {
                    tour[i].warnings.push('impossible_flight');
                }
                else if (p && c && Math.abs(p.tz - c.tz) > 8) {
                    tour[i].warnings.push('timezone');
                }
            }
        }
    }

// ─── PREVIEW MODAL ────────────────────────────────────────────────────────────
    function openPreview(extTour = null, extSettings = null, schemaId = null) {
        try {
            const settings = extSettings || gatherSettings();
            let tour = extTour ? JSON.parse(JSON.stringify(extTour)) : buildTour(settings);
            let isModified = false;

            const ov = mk('div');
            ov.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:99999;display:flex;align-items:flex-start;justify-content:center;padding:40px 12px;overflow-y:auto;';
            const box = mk('div');
            box.style.cssText = 'background:#fff;border-radius:10px;padding:18px;width:100%;max-width:860px;box-shadow:0 6px 32px rgba(0,0,0,.35);font-family:inherit;font-size:13px;';

            const hdr = mk('div');
            hdr.style.cssText = 'display:flex;align-items:center;gap:10px;margin-bottom:8px;flex-wrap:wrap;';
            const htl = mk('span','',`🌟 Route49 β€” ${tour.length} ${T('summaryShows')}`);
            htl.style.cssText = 'font-weight:700;font-size:15px;flex:1;color:#6f42c1;';

            const saveBtn = mkB('πŸ’Ύ ' + T('btnSave'), '', () => {
                if (schemaId === 'active') { gmSet(GMK.tour, tour); }
                else if (schemaId) {
                    const sc = gmGet(GMK.schemas,[]); const idx = sc.findIndex(x=>x.id===schemaId);
                    if(idx!==-1){ sc[idx].tour = tour; gmSet(GMK.schemas, sc); }
                } else {
                    gmSet(GMK.tour, tour); gmSet(GMK.idx, 0);
                }
                isModified = false;
                const tab = document.getElementById('r49-tab-saved');
                if (tab && tab.style.display === 'block') renderSchemas(tab);
                const sumEl = document.getElementById('r49-summary');
                if (sumEl && !schemaId) sumEl.textContent = tourSummary(tour);
                alert(_D('Değişiklikler kaydedildi.','Changes saved.','Alteraçáes salvas.'));
            });

            const closeBtn = mkB(T('btnClose'), '', () => {
                if (isModified) {
                    if (confirm(_D('Değişiklikler kaydedilmeden çıkılsın mı?', 'Exit without saving?', 'Sair sem salvar?'))) {
                        ov.remove();
                    }
                } else {
                    ov.remove();
                }
            });
            saveBtn.style.cssText = btnSt;
            closeBtn.style.cssText = 'padding:5px 10px;border:1px solid #dc3545;border-radius:5px;cursor:pointer;font-size:12px;background:#fff0f0;color:#dc3545;font-family:inherit;';

            const copyBtn = mkB('πŸ“‹ Kopyala', '', () => {
                const txt = tour.map(s => `${s.date} ${s.time.slice(0,5)} ${cityName(cityById(s.cityId))}`).join('\n');
                navigator.clipboard.writeText(txt); alert('Panoya kopyalandΔ±!');
            });
            const csvBtn = mkB('⬇ CSV', '', () => dlCSV(tourToCSV(tour, settings), 'tour.csv'));
            copyBtn.style.cssText = 'padding:4px 12px;border:none;border-radius:4px;cursor:pointer;font-size:12px;font-weight:700;background:#6f42c1;color:#fff;box-shadow:0 2px 4px rgba(0,0,0,0.1);';
            csvBtn.style.cssText = btnSt;
            hdr.append(htl, copyBtn, csvBtn, saveBtn, closeBtn);
            box.appendChild(hdr);
const sumBar = mk('div','',tourSummary(tour));
            sumBar.style.cssText = 'font-size:11px;color:#6f42c1;font-weight:600;margin-bottom:10px;';
            box.appendChild(sumBar);

            // Bilgi Metni
            const intro = mk('div', '', _D('Turnede ziyaret edilecek şehirlerin sıralamasını sürükle bırak ile değiştirebilirsiniz. Aynı şehirdeki konserler bâlünmeden blok halinde taşınır. Sürükleme sırasında oluşan saat dezavantajları tabloda uyarı olarak belirir. 4 saatten fazla dezavantajı olan konserleri sağdaki kutucuktan devredışı bırakmanız ânerilir. Sonuçtan memnun kalırsanız kaydedin.', 'Drag and drop rows to reorder. Shows in the same city move together. Time disadvantages appear as warnings. It is recommended to disable shows with >4h disadvantage. Save when satisfied.', 'Arraste e solte para reordenar. Shows na mesma cidade movem-se juntos. Desvantagens de tempo aparecem como avisos. Recomenda-se desativar shows com desvantagem >4h. Salve quando satisfeito.'));
            intro.style.cssText = 'font-size:11px;color:#555;margin-bottom:12px;padding:8px;background:#f8f9fa;border-left:4px solid #6f42c1;border-radius:4px;';
            box.appendChild(intro);

            // Ayarla - Hepsi / HiΓ§biri ButonlarΔ±
            const chkBtnRow = mk('div'); chkBtnRow.style.cssText = 'display:flex;gap:8px;margin-bottom:8px;align-items:center;';
            const chkLbl = mk('span', '', _D('Tablo KontrolΓΌ:', 'Table Control:', 'Controle da Tabela:')); chkLbl.style.cssText = 'font-size:12px;font-weight:bold;color:#6f42c1;';
            const chkAll = mkB(_D('TΓΌmΓΌnΓΌ Ayarla', 'Set All', 'Definir Todos'), '', () => { tour.forEach(s => s.willBook = true); isModified = true; renderTable(); });
            const chkNone = mkB(_D('HiΓ§birini Ayarlama', 'Set None', 'Definir Nenhum'), '', () => { tour.forEach(s => s.willBook = false); isModified = true; renderTable(); });
            [chkAll, chkNone].forEach(b => b.style.cssText = btnSt);
            chkBtnRow.append(chkLbl, chkAll, chkNone);
            box.appendChild(chkBtnRow);

            const tbl = mk('table');
            tbl.style.cssText = 'width:100%;border-collapse:collapse;font-size:12px;';

            const thead = mk('thead');
            const headRow = mk('tr');
            [T('colDay'), T('colDate'), T('colTime'), T('colCity'), T('colVenue'), T('colPrice'), '', _D('Ayarla', 'Set', 'Def.'), _D('Sil', 'Del', 'Exc.')].forEach(h => {
                const th = mk('th','',h);
                th.style.cssText = 'padding:5px 6px;text-align:left;border-bottom:2px solid #6f42c1;font-size:11px;color:#6f42c1;white-space:nowrap;';
                headRow.appendChild(th);
            });
            thead.appendChild(headRow);
            tbl.appendChild(thead);

            const tbody = mk('tbody');
            let draggedIdx = null;

            const renderTable = () => {
                tbody.innerHTML = '';
                tour.forEach((show, i) => {
                    const c = cityById(show.cityId);
                    const pm = realToPM(new Date(show.date + 'T00:00:00'));
                    const sd = show.specialDay ? SPECIAL_DAYS[show.specialDay] : null;
                    const warns = show.warnings ||[];
                    const tr = mk('tr');

                    tr.draggable = true;
                    tr.style.cursor = 'grab';
                    tr.ondragstart = e => { draggedIdx = i; e.dataTransfer.effectAllowed = 'move'; tr.style.opacity = '0.5'; };
                    tr.ondragend = e => { tr.style.opacity = '1'; };
                    tr.ondragover = e => { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; tr.style.borderTop = '2px solid #6f42c1'; };
                    tr.ondragleave = e => { tr.style.borderTop = ''; };
tr.ondrop = e => {
                        e.preventDefault(); tr.style.borderTop = '';
                        if (draggedIdx === null || draggedIdx === i) return;

                        const draggedItem = tour[draggedIdx];
                        const targetItem = tour[i];

                        // Diziden çıkar ve hedefe yerleştir
                        tour.splice(draggedIdx, 1);
                        tour.splice(i, 0, draggedItem);

                        // Tarih ve zaman dizilimini temizle
                        const chronoSlots = tour.map(s => ({ date: s.date, time: s.time, specialDay: s.specialDay }));
                        chronoSlots.sort((a,b) => new Date(a.date+'T'+a.time) - new Date(b.date+'T'+b.time));

                        for (let j = 0; j < tour.length; j++) {
                            tour[j].date = chronoSlots[j].date;
                            tour[j].time = chronoSlots[j].time;
                            tour[j].specialDay = chronoSlots[j].specialDay;
                        }

                        isModified = true;
                        recalcWarnings(tour);
                        renderTable();
                    };

                    let bg = i % 2 === 0 ? '#fff' : '#faf7ff';
                    let rowFw = 'normal';
                    let rowColor = 'inherit';

                    if (sd) {
                        const cm = {'πŸ’€':'#fff0e0','πŸ”οΈ':'#e8f5e9','πŸ’₯':'#fff3e0','πŸŽƒ':'#fff8e1','πŸŽ„':'#e8f5e9','πŸŽ‰':'#f3e5f5','πŸ¦‘':'#e0f7fa'};
                        bg = cm[sd.icon] || '#f3e5f5';
                        if (['πŸ’₯','πŸ’€','πŸ”οΈ'].includes(sd.icon)) {
                            bg = '#fed7aa'; rowFw = 'bold'; rowColor = '#9a3412';
                        }
                    }
                    if (warns.includes('future') && rowFw === 'normal') bg = '#fff9e6';
                    tr.style.background = bg;

                    let wt = ''; let wtColor = rowColor; let wtFw = rowFw; let wtTitle = '';

                    if (warns.includes('timezone') && i > 0) {
                        const pC = cityById(tour[i-1].cityId);
                        if (pC && c) {
                            const diff = c.tz - pC.tz;
                            const h = Math.abs(diff);
                            if (diff < 0) {
                                wt = '⏰ ' + _D(`+${h}s Avantaj`,`+${h}h Adv.`,`+${h}h Vant.`);
                                wtTitle = _D(`${h} Saat Avantaj`,`${h} Hours Advantage`,`${h} Horas Vantagem`);
                                wtColor = '#16a34a'; wtFw = '700';
                            } else {
                                wt = '⏰ ' + _D(`-${h}s Dezavantaj`,`-${h}h Disadv.`,`-${h}h Desv.`);
                                wtTitle = _D(`${h} Saat Dezavantaj`,`${h} Hours Disadvantage`,`${h} Horas Desvantagem`);
                                wtColor = '#dc2626'; wtFw = '700';
                            }
                        }
                    }
                    if (warns.includes('future')) {
                        wt += (wt ? ' | ' : '') + '⏳';
                        wtTitle += (wtTitle ? ' | ' : '') + T('warnFuture');
                    }

                    if (warns.includes('impossible_flight')) {
                        wt += (wt ? ' | ' : '') + '🚨 ' + _D('İmkansız!','Impossible!','Impossível!');
                        wtTitle += (wtTitle ? ' | ' : '') + _D('Aynı gün farklı şehirde konser verilemez!','Cannot play in different cities on the same day!','Não pode tocar em cidades diferentes no mesmo dia!');
                        wtColor = '#dc2626'; wtFw = '900';
                    }
                    if (warns.includes('stadium_limit')) {
                        wt += (wt ? ' | ' : '') + '❌ ' + _D('Kota Dolu','Limit Reached','Limite Atingido');
                        wtTitle += (wtTitle ? ' | ' : '') + _D('Stadyum KotasΔ± Dolu! (Maks 10)','Stadium Limit Reached! (Max 10)','Limite de EstΓ‘dio Atingido! (Max 10)');
                        wtColor = '#dc2626'; wtFw = '900';
                    }

                    if (i < tour.length - 1) {
                        const nC = cityById(tour[i+1].cityId);
                        if (c && nC) {
                            const diff = nC.tz - c.tz;
                            if (diff > 4 && show.time.startsWith('22') && i>0 && tour[i-1].date === show.date && tour[i-1].cityId === show.cityId) {
                                wt += (wt?' | ':'') + `⚠️ -${diff}s ` + _D('Riskli','Risky','Risco');
                                wtTitle += (wtTitle?' | ':'') + _D(`+${diff} Saat Dezavantaj - Bu konseri iptal etmeniz ΓΆnerilir.`,`+${diff}h Disadvantage - Recommended to disable this show.`,`+${diff}h Desvantagem - Recomendado desativar este show.`);
                                wtColor = '#dc2626'; wtFw = '700';
                            }
                        }
                    }

                    let dayStr = `${sd ? sd.icon + ' ' : ''}${pm.year}-${pm.day}`;
                    let dayFw = rowFw, dayColor = rowColor;
                    if (sd && ['πŸ’₯','πŸ’€','πŸ”οΈ'].includes(sd.icon)) { dayFw = '900'; dayColor = '#b45309'; }

                    const setChk = mk('input'); setChk.type = 'checkbox';
                    setChk.checked = show.willBook !== false; // default true
                    if (show.booked) { setChk.disabled = true; setChk.style.opacity = '0.5'; }
                    setChk.style.cursor = 'pointer';
                    setChk.onchange = () => { show.willBook = setChk.checked; isModified = true; renderTable(); };

                    const delBtn = mkB('πŸ—‘οΈ', '', () => { tour.splice(i, 1); isModified = true; renderTable(); });
                    delBtn.style.cssText = 'background:transparent;border:none;cursor:pointer;font-size:14px;';
                    delBtn.title = _D('Konseri Sil', 'Delete Show', 'Excluir Show');

                    const rowData =[
                        { txt: dayStr, fw: dayFw, col: dayColor },
                        { txt: fmtDisp(new Date(show.date + 'T00:00:00')) },
                        { txt: show.time.slice(0, 5) },
                        { txt: cityName(c) || show.cityId },
                        { txt: show.venueType === 'stadium' ? T('venueStad') : T('venueBar') },
                        { txt: `${settings.priceMin}–${settings.priceMax} M$` },
                        { txt: wt, fw: wtFw, col: wtColor, title: wtTitle },
                        { el: setChk },
                        { el: delBtn }
                    ];

                    rowData.forEach(d => {
                        const td = mk('td');
                        if (d.txt !== undefined) td.textContent = d.txt;
                        if (d.el) td.appendChild(d.el);
                        if (d.title) td.title = d.title;
                        td.style.cssText = `padding:4px 6px;border-bottom:1px solid #f0ecff;white-space:nowrap;font-weight:${d.fw||rowFw};color:${d.col||rowColor};`;
                        tr.appendChild(td);
                    });
                    tbody.appendChild(tr);
                });
            };


            renderTable();
            tbl.appendChild(tbody);

            const tblWrap = mk('div');
            tblWrap.style.cssText = 'width:100%;overflow-x:auto;margin-bottom:10px;';
            tblWrap.appendChild(tbl);
            box.appendChild(tblWrap);
            const legend = mk('div');

            legend.style.cssText = 'margin-top:10px;font-size:11px;color:#666;display:flex;gap:12px;flex-wrap:wrap;';
            [[T('warnTZ'), '⏰'],[T('warnFuture'), '⏳']].forEach(([l, i]) => legend.appendChild(mk('span','',`${i} = ${l}`)));
            Object.values(SPECIAL_DAYS).forEach(sd => legend.appendChild(mk('span','',`${sd.icon} = ${sd.label}`)));
            box.appendChild(legend);

            ov.append(box);
            ov.onclick = e => {
                if (e.target === ov) {
                    if (isModified && !confirm(_D('Değişiklikler kaydedilmeden çıkılsın mı?', 'Exit without saving?', 'Sair sem salvar?'))) return;
                    ov.remove();
                }
            };
            document.body.appendChild(ov);
        } catch (err) {
            console.error("[Route49] Γ–nizleme HatasΔ±:", err);
            alert("Γ–nizleme oluşturulurken bir hata oluştu. LΓΌtfen konsolu (F12) kontrol edin.");
        }
    }
    // ─── INJECT MAIN UI ───────────────────────────────────────────────────────────
    function injectUI() {
        if (document.getElementById('r49-panel')) return;
        const container=document.querySelector('#ppm-content')||document.querySelector('#content')||document.body;
        const panel=mk('div'); panel.id='r49-panel';
        panel.style.cssText='border:2px solid #6f42c1;border-radius:8px;background:#f5f0ff;padding:12px;margin-bottom:14px;font-family:inherit;font-size:13px;max-width:100%;box-sizing:border-box;';

        // Header (Title + Artist ID)
        const hdr=mk('div'); hdr.style.cssText='display:flex;align-items:center;gap:10px;margin-bottom:10px;';
        const ttl=mk('span','',T('title')); ttl.style.cssText='font-weight:700;font-size:15px;color:#6f42c1;';
        const artInp=mk('input'); artInp.id='r49-artist'; artInp.type='text'; artInp.placeholder=T('artistId');
        artInp.style.cssText=inpSt+'width:100px;font-weight:600;color:#333;';
        const urlAid=window.location.pathname.match(/\/BookShow\/(\d+)/)?.[1]; if(urlAid) artInp.value=urlAid;
        const badge=mk('span'); badge.id='r49-stad-badge'; badge.style.cssText='font-size:12px;font-weight:700;padding:2px 8px;border-radius:10px;background:#e8f5e9;margin-left:auto;';
        hdr.append(ttl,artInp,badge); panel.appendChild(hdr);

        // Tabs
        const tabBar=mk('div'); tabBar.style.cssText='display:flex;gap:4px;margin-bottom:10px;';
        const TABS=[['plan',T('tabPlan')],['saved',T('tabSaved')],['settings',T('tabSettings')]];
        const panes={};
        TABS.forEach(([id,lbl])=>{
            const btn=mkB(lbl,'',()=>{
                TABS.forEach(([tid])=>{
                    document.getElementById(`r49-tab-${tid}`)?.style.setProperty('display','none');
                    const tb=document.getElementById(`r49-tbtn-${tid}`); if(tb){tb.style.background='#e8e0f9';tb.style.color='#6f42c1';}
                });
                document.getElementById(`r49-tab-${id}`)?.style.setProperty('display','block');
                btn.style.background='#6f42c1'; btn.style.color='#fff';
                if (id==='saved') renderSchemas(panes['saved']); // Render on click
            });
            btn.id=`r49-tbtn-${id}`;
            btn.style.cssText='padding:4px 12px;border:1px solid #6f42c1;border-radius:4px;cursor:pointer;font-size:12px;background:#e8e0f9;color:#6f42c1;font-family:inherit;';
            tabBar.appendChild(btn);
            const pane=mk('div'); pane.id=`r49-tab-${id}`; pane.style.display='none';
            panes[id]=pane;
        });
        panel.appendChild(tabBar);

        // TAB: PLAN
        const plan=panes['plan'];
        const tpRow=mk('div'); tpRow.style.cssText='display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px;';
        // ── Turne TΓΌrΓΌ
        const tySel=mk('select'); tySel.id='r49-ttype'; tySel.style.cssText=inpSt+'width:100%;';
        Object.entries(TOUR_TYPES).forEach(([k,t])=>{ const o=mk('option','',t.name); o.value=k; tySel.appendChild(o); });
tySel.onchange=()=>{
            syncDoubleDayUI();
            toggleCustom();
            const cfg = TOUR_TYPES[tySel.value];
            if (cfg.durationDays) {
                const sd=document.getElementById('r49-start')?.value||MIN_DATE_STR;
                const ed=fmtISO(addDays(new Date(sd+'T00:00:00'), cfg.durationDays));
                const endEl=document.getElementById('r49-end'); if(endEl){ endEl.value=ed; updateDL('r49-end','r49-end-lbl'); }
            }
            updateSummary();
        };
        const tyWrap=fieldRow(T('tourType'),tySel); tyWrap.style.flex='1';

        // ── Turne PlanΔ±
        const plSel=mk('select'); plSel.id='r49-tplan'; plSel.style.cssText=inpSt+'width:100%;';
        Object.entries(TOUR_PLANS).forEach(([k,t])=>{ const o=mk('option','',t.name); o.value=k; plSel.appendChild(o); });
        plSel.onchange=()=>{ toggleCustom(); updateSummary(); };
        const plWrap=fieldRow(T('tourPlan'),plSel); plWrap.style.flex='1';

        tpRow.append(tyWrap, plWrap); plan.appendChild(tpRow);

        // ── BaşlangΔ±Γ§ Şehri
        const scWrap=mk('div'); scWrap.id='r49-startcity-wrap'; scWrap.style.cssText='display:flex;align-items:center;gap:8px;margin-bottom:8px;';
        const scSel=mk('select'); scSel.id='r49-startcity'; scSel.style.cssText=inpSt;
        CITIES.forEach(c=>{ const o=mk('option','',cityName(c)); o.value=String(c.id); if(c.id===DEF.startCityId)o.selected=true; scSel.appendChild(o); });
        scSel.onchange=updateSummary;
        scWrap.append(mk('label','',T('startCity')+':'),scSel);
        plan.appendChild(scWrap);

        // ── Custom route
        const customWrap=mk('div'); customWrap.id='r49-custom-wrap'; customWrap.style.display='none';
        const customTA=mk('textarea'); customTA.id='r49-custom';
        customTA.style.cssText='width:100%;height:80px;font-family:monospace;font-size:11px;border:1px solid #c9b8f0;border-radius:4px;box-sizing:border-box;padding:4px;';
        customTA.placeholder=T('customHint'); customTA.onchange=updateSummary;
        customWrap.append(customTA);
        plan.appendChild(customWrap);

        // ── Dates (2-col)
        const g2=mk('div'); g2.style.cssText='display:grid;grid-template-columns:repeat(auto-fit, minmax(130px, 1fr));gap:8px;margin-bottom:8px;';
        const mkDateW=(id,lbl,def)=>{
            const w=mk('div'); w.id=id+'-wrap';
            const l=mk('label','',lbl+':'); l.style.cssText='display:block;font-weight:600;font-size:11px;margin-bottom:3px;color:#444;';
            const inp=mk('input'); inp.type='date'; inp.id=id; inp.value=def; inp.min=MIN_DATE_STR; inp.style.cssText=inpSt+'width:100%;';
            const dl=mk('div'); dl.id=id+'-lbl'; dl.style.cssText='font-size:10px;color:#777;margin-top:1px;';
            inp.onchange=()=>{
                updateDL(id,id+'-lbl');
                if (id === 'r49-start') {
                    const tySel = document.getElementById('r49-ttype');
                    if (tySel && tySel.value !== 'custom') {
                        const cfg = TOUR_TYPES[tySel.value];
                        if (cfg && cfg.durationDays) {
                            const ed = fmtISO(addDays(new Date(inp.value+'T00:00:00'), cfg.durationDays));
                            const endEl = document.getElementById('r49-end');
                            if(endEl) { endEl.value=ed; updateDL('r49-end','r49-end-lbl'); }
                        }
                    }
                }
                updateSummary();
            };
            w.append(l,inp,dl); return w;
        };

        g2.appendChild(mkDateW('r49-start',T('startDate'),DEF.startDate));
        g2.appendChild(mkDateW('r49-end',T('endDate'),DEF.endDate));
        plan.appendChild(g2);

        // ── Times & Options (Flex row)
        const toRow=mk('div'); toRow.style.cssText='display:flex;flex-wrap:wrap;gap:12px;margin-bottom:8px;align-items:stretch;';

        const cbWrap=mk('div'); cbWrap.style.cssText='flex:1;border:1px solid #e0d8ff;border-radius:6px;padding:8px;';
        const ddRow=mk('label'); ddRow.style.cssText='display:flex;align-items:center;gap:6px;font-size:12px;cursor:pointer;margin-bottom:4px;';
        const ddChk=mk('input'); ddChk.type='checkbox'; ddChk.id='r49-doubleday'; ddChk.checked=true;
        ddChk.onchange=()=>{ syncDoubleDayUI(); updateSummary(); };
        ddRow.append(ddChk, mk('span','',T('doubleDay')));

        const dpcRow=mk('div'); dpcRow.id='r49-dpc-row';
        const dpcLbl=mk('label'); dpcLbl.style.cssText='display:flex;align-items:center;gap:6px;font-size:12px;cursor:pointer;';
        const dpcChk=mk('input'); dpcChk.type='checkbox'; dpcChk.id='r49-dpc'; dpcChk.checked=true;
        dpcChk.onchange=updateSummary;
        dpcLbl.append(dpcChk, mk('span','',T('showsPerCity')));
        dpcRow.appendChild(dpcLbl);
        const ddNote=mk('div','',T('showsDDNote')); ddNote.style.cssText='font-size:10px;color:#888;margin-top:4px;';
        cbWrap.append(ddRow,dpcRow,ddNote);

        const timeSel=mk('select'); timeSel.id='r49-times'; timeSel.multiple=true; timeSel.size=5;
        timeSel.style.cssText='width:120px;height:auto;overflow-y:hidden;padding:4px;border:1px solid #c9b8f0;border-radius:4px;font-size:12px;';['14:00:00','16:00:00','18:00:00','20:00:00','22:00:00'].forEach(t=>{
            const o=mk('option','',t.slice(0,5)); o.value=t; if(t==='14:00:00'||t==='22:00:00') o.selected=true; timeSel.appendChild(o);
        });
        const timeWrap=mk('div');
        const timeLbl=mk('label','',T('showTimes')); timeLbl.style.cssText='display:block;font-weight:600;font-size:11px;margin-bottom:3px;color:#444;';
        timeWrap.append(timeLbl,timeSel);

        toRow.append(cbWrap, timeWrap); plan.appendChild(toRow);

        // ── Fame score β†’ price
        const fameSel=mk('select'); fameSel.id='r49-fame'; fameSel.style.cssText=inpSt+'width:100%;margin-bottom:5px;';
        PRICE_GUIDE.forEach(g=>{ const o=mk('option','',`${g.score}: ${g.min}–${g.max} M$`); o.value=String(g.score); fameSel.appendChild(o); });
        fameSel.value='13';
        fameSel.onchange=()=>{ const g=PRICE_GUIDE[parseInt(fameSel.value)]; if(g){document.getElementById('r49-pmin').value=g.min;document.getElementById('r49-pmax').value=g.max;} };
        const pRow=mk('div'); pRow.style.cssText='display:flex;flex-wrap:wrap;align-items:center;gap:6px;';
        const pMin=mk('input'); pMin.id='r49-pmin'; pMin.type='number'; pMin.min='0'; pMin.value=String(PRICE_GUIDE[13].min); pMin.style.cssText='width:70px;'+inpSt;
        const pMax=mk('input'); pMax.id='r49-pmax'; pMax.type='number'; pMax.min='0'; pMax.value=String(PRICE_GUIDE[13].max); pMax.style.cssText='width:70px;'+inpSt;
        pRow.append(mk('span','','Min:'),pMin,mk('span','','–Max:'),pMax);
        const priceW=mk('div');
        const prL=mk('label','',T('fameScore')+':'); prL.style.cssText='display:block;font-weight:600;font-size:11px;margin-bottom:3px;color:#444;';
        const prL2=mk('label','',T('priceRange')+':'); prL2.style.cssText='display:block;font-weight:600;font-size:11px;margin-bottom:3px;margin-top:5px;color:#444;';
        priceW.append(prL,fameSel,prL2,pRow); priceW.style.marginBottom='8px';
        plan.appendChild(priceW);

        // ── Min stars + sort (2-col)
        const g2b=mk('div'); g2b.style.cssText='display:grid;grid-template-columns:repeat(auto-fit, minmax(130px, 1fr));gap:8px;margin-bottom:8px;';
        const starSel=mk('select'); starSel.id='r49-minstars'; starSel.style.cssText=inpSt+'width:100%;';
        [[10,'1 β˜…'],[20,'2 β˜…β˜…'],[30,'3 β˜…β˜…β˜…'],[40,'4 β˜…β˜…β˜…β˜…'],[50,'5 β˜…β˜…β˜…β˜…β˜…']].forEach(([v,l])=>{ const o=mk('option','',l); o.value=String(v); if(v===50)o.selected=true; starSel.appendChild(o); });
        const starW=fieldRow(T('minStars'),starSel);
        const sortSel=mk('select'); sortSel.id='r49-sort'; sortSel.style.cssText=inpSt+'width:100%;';
        [['price_desc',T('sortHigh')],['price_asc',T('sortLow')],['price_mid',T('sortMid')]].forEach(([v,l])=>{ const o=mk('option','',l); o.value=v; sortSel.appendChild(o); });
        const sortW=fieldRow(T('sortMode'),sortSel);
        g2b.append(starW,sortW); plan.appendChild(g2b);

        // ── Γ–zel GΓΌn AyarlarΔ±
        const sdSec=mk('div'); sdSec.style.cssText='margin-bottom:8px;border:1px solid #e0d8ff;border-radius:6px;padding:8px;';
        const sdTtl=mk('div','',T('sdTitle')+':'); sdTtl.style.cssText='font-weight:600;font-size:12px;margin-bottom:6px;';
        sdSec.appendChild(sdTtl);
        for (const [day,sd] of ANCHOR_DAYS) {
            const row=mk('div'); row.style.cssText='display:flex;align-items:center;gap:6px;margin-bottom:5px;flex-wrap:wrap;';
            const lbl=mk('span','',`${sd.icon} ${sd.label}`); lbl.style.cssText='font-size:11px;min-width:140px;';
            const cSel=mk('select'); cSel.id=`r49-sd-city-${day}`; cSel.style.cssText=inpSt+'flex:1;min-width:100px;';
            const noOpt=mk('option','',_D('β€” SeΓ§ β€”','β€” Pick β€”','β€” Escolha β€”')); noOpt.value='0'; cSel.appendChild(noOpt);
            CITIES.forEach(c=>{ const o=mk('option','',cityName(c)); o.value=String(c.id); cSel.appendChild(o); });
            cSel.onchange=updateSummary;
            const araSel=mk('select'); araSel.id=`r49-sd-rest-${day}`; araSel.style.cssText=inpSt+'width:110px;';
            [0,1,2,3,4,5].forEach(v=>{ const o=mk('option','',`${v} `+_D('GΓΌn Dinlen','Days Rest','Dias Resto')); o.value=String(v); araSel.appendChild(o); });
            araSel.onchange=updateSummary;
            row.append(lbl,cSel,araSel);
            sdSec.appendChild(row);
        }
        plan.appendChild(sdSec);

        // ── Stadyum Şehirleri
        const stadSec=mk('div'); stadSec.style.cssText='margin-bottom:8px;';
        const stadTtlRow=mk('div'); stadTtlRow.style.cssText='display:flex;align-items:center;gap:8px;margin-bottom:4px;';
        const stadTtl=mk('span','',T('stadCities')+':'); stadTtl.style.cssText='font-weight:600;font-size:12px;flex:1;';
        const stNone=mkB(T('selNone'),'',()=>{ CITIES.forEach(c=>{const e=g(`r49-stad-${c.id}`);if(e)e.checked=false;}); updateSummary(); updateStadBadge(); });
        stNone.style.cssText=btnSt; stadTtlRow.append(stadTtl, stNone);
        const stadGrid=mk('div'); stadGrid.style.cssText='display:flex;flex-wrap:wrap;gap:4px;max-height:90px;overflow-y:auto;border:1px solid #e0d8ff;border-radius:4px;padding:4px;';
        CITIES.forEach(c=>{
            const lbl=mk('label'); lbl.style.cssText='display:flex;align-items:center;gap:3px;font-size:11px;background:#fff;border:1px solid #e0d8ff;border-radius:4px;padding:2px 6px;cursor:pointer;';
            const chk=mk('input'); chk.type='checkbox'; chk.id=`r49-stad-${c.id}`;
            chk.onchange=()=>{ updateSummary(); updateStadBadge(); };
            lbl.append(chk,mk('span','',cityName(c))); stadGrid.appendChild(lbl);
        });
        stadSec.append(stadTtlRow,stadGrid); plan.appendChild(stadSec);

        // ── Şehirler
        const cityTitle=mk('div'); cityTitle.style.cssText='display:flex;align-items:center;gap:8px;margin-bottom:4px;';
        const cityLbl=mk('span','',T('citiesLabel')+':'); cityLbl.style.cssText='font-weight:600;font-size:12px;flex:1;';
        const allBtn=mkB(T('selAll'),'',()=>{ CITIES.forEach(c=>{const e=document.getElementById(`r49-city-${c.id}`);if(e)e.checked=true;}); updateSummary(); });
        const noneBtn=mkB(T('selNone'),'',()=>{ CITIES.forEach(c=>{const e=document.getElementById(`r49-city-${c.id}`);if(e)e.checked=false;}); updateSummary(); });[allBtn,noneBtn].forEach(b=>b.style.cssText=btnSt);
        cityTitle.append(cityLbl,allBtn,noneBtn);
        const cityGrid=mk('div'); cityGrid.style.cssText='display:flex;flex-wrap:wrap;gap:4px;max-height:160px;overflow-y:auto;border:1px solid #e0d8ff;border-radius:4px;padding:4px;margin-bottom:8px;';
        CITIES.forEach(c=>{
            const lbl=mk('label'); lbl.style.cssText='display:flex;align-items:center;gap:3px;font-size:11px;background:#fff;border:1px solid #e0d8ff;border-radius:4px;padding:2px 6px;cursor:pointer;min-width:90px;';
            const chk=mk('input'); chk.type='checkbox'; chk.id=`r49-city-${c.id}`; chk.checked=true; chk.onchange=updateSummary;
            lbl.append(chk,mk('span','',cityName(c))); cityGrid.appendChild(lbl);
        });
        plan.append(cityTitle,cityGrid);

        // ════════════════════════════════════════════════════════════
        // TAB: ŞEMALAR (SCHEMAS)
        // ════════════════════════════════════════════════════════════
        const saved=panes['saved'];
        // Initial render deferred to tab click for performance

        // ════════════════════════════════════════════════════════════
        // TAB: SETTINGS
        // ════════════════════════════════════════════════════════════
        const sets=panes['settings'];
        sets.style.paddingTop='4px';

        // Links
        const mkLink=(lbl,url)=>{ const a=document.createElement('a'); a.href=url; a.textContent=lbl; a.target='_blank'; a.style.cssText='padding:6px 14px;border:1px solid #6f42c1;border-radius:5px;font-size:12px;color:#6f42c1;text-decoration:none;background:#fff;display:inline-block;'; return a; };
        const linkRow=mk('div'); linkRow.style.cssText='display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px;';
        linkRow.append(mkLink(T('btnReadme'),'https://rentry.org/Route49oku'), mkLink(T('btnTemplates'),'https://rentry.org/Route49sablon'));
        sets.appendChild(linkRow);

        // Language buttons
        const langLbl=mk('div','',_D('Dil / Language / Idioma')); langLbl.style.cssText='font-weight:600;font-size:12px;margin-bottom:6px;';
        const langRow=mk('div'); langRow.style.cssText='display:flex;gap:6px;';
        [['TR','πŸ‡ΉπŸ‡· TΓΌrkΓ§e'],['EN','πŸ‡¬πŸ‡§ English'],['PT','πŸ‡§πŸ‡· PortuguΓͺs']].forEach(([code,lbl])=>{
            const b=mkB(lbl,'',()=>setLang(code));
            b.style.cssText=btnSt+(LANG===code?'background:#6f42c1;color:#fff;font-weight:700;':'');
            langRow.appendChild(b);
        });
        sets.append(langLbl,langRow);

        // Advanced Settings
        const advLbl = mk('div','',_D('Gelişmiş Ayarlar','Advanced Settings','Configuraçáes Avançadas'));
        advLbl.style.cssText='font-weight:600;font-size:12px;margin:12px 0 6px;';

        const fastLbl = mk('label'); fastLbl.style.cssText='display:flex;align-items:center;gap:6px;font-size:12px;cursor:pointer;margin-bottom:8px;';
        const fastChk = mk('input'); fastChk.type='checkbox'; fastChk.id='r49-fastmode';
        fastChk.onchange = updateSummary;
        fastLbl.append(fastChk, mk('span','','⚑ ' + _D('Hızlı Mod (Riskli)','Fast Mode (Risky)','Modo RÑpido (Risco)')));

        const resetBtn = mkB(_D('πŸ”„ VarsayΔ±lanlara DΓΆn','Reset Defaults','Redefinir PadrΓ΅es'),'',()=>{
            if(confirm(_D('TΓΌm ayarlar sΔ±fΔ±rlansΔ±n mΔ±?','Reset all settings?','Redefinir tudo?'))) {
                gmDel(GMK.sets); location.reload();
            }
        });
        resetBtn.style.cssText = btnSt + 'background:#fff0f0;color:#dc3545;border-color:#dc3545;';

        sets.append(advLbl, fastLbl, resetBtn);

        // ── Append panes
        Object.values(panes).forEach(p=>panel.appendChild(p));

// (openPreview moved to global scope)

        // ── Summary + controls
        const sumEl=mk('div'); sumEl.id='r49-summary'; sumEl.style.cssText='font-size:11px;color:#555;min-height:1.2em;margin:8px 0 4px;';
        panel.appendChild(sumEl);

        const ctrlRow=mk('div'); ctrlRow.style.cssText='display:flex;gap:6px;flex-wrap:wrap;margin-bottom:8px;';
        const prevBtn=mkB(T('btnPreview'),'',()=>{
            // If there's an active tour, preview the active tour's state instead of generating a new one
            const t = gmGet(GMK.tour,[]);
            if (t.length > 0) openPreview(t, gmGet(GMK.sets,{}), 'active');
            else openPreview(); // Otherwise generate a fresh one from UI
        });
        const startBtn=mkB(T('btnStart'),'',()=>startBooking());
        const stopBtn=mkB(T('btnStop'),'',()=>{ if(confirm(T('confirmStop'))) stopBooking(); });
        prevBtn.style.cssText='padding:6px 14px;border:none;border-radius:5px;cursor:pointer;font-size:12px;font-weight:600;color:#fff;background:#6f42c1;';
        startBtn.style.cssText='padding:6px 14px;border:none;border-radius:5px;cursor:pointer;font-size:12px;font-weight:600;color:#fff;background:#28a745;';
        stopBtn.style.cssText='padding:6px 14px;border:none;border-radius:5px;cursor:pointer;font-size:12px;font-weight:600;color:#fff;background:#dc3545;';
        ctrlRow.append(prevBtn,startBtn,stopBtn);
        panel.appendChild(ctrlRow);

        const statusEl=mk('div'); statusEl.id='r49-status'; statusEl.style.cssText='font-size:12px;font-weight:600;color:#555;min-height:1.2em;';
        statusEl.textContent=T('statusIdle');
        panel.appendChild(statusEl);

        // Inject into page
        const ref=container.querySelector('.entityLogo')||container.querySelector('h1')||container.firstElementChild;
        if (ref) container.insertBefore(panel,ref); else container.prepend(panel);

        // Activate plan tab
        document.getElementById('r49-tbtn-plan').click();

        // Post-inject
        updateStadBadge();
        updateDL('r49-start','r49-start-lbl');
        updateDL('r49-end','r49-end-lbl');
        toggleCustom();
        syncDoubleDayUI();
        updateSummary();
    }

    // ─── ŞEMALAR (SCHEMAS) ────────────────────────────────────────────────────────
    function renderSchemas(container) {
        container.innerHTML='';

      // Yalnızca Global CSV Import Butonu (Bilgisayardan şema yüklemek için)
        const hr = mk('div'); hr.style.cssText = 'display:flex; gap:8px; margin-bottom:12px; padding-bottom:10px; border-bottom:1px solid #e0d8ff;';
        const iBtn = mkB(T('btnImportCSV'), '', () => {
            const inp = mk('input'); inp.type = 'file'; inp.accept = '.csv,text/csv';
            inp.onchange = e => {
                const f = e.target.files?.[0]; if(!f) return;
                const r = new FileReader();
                r.onload = ev => {
                    try {
                        const imp = csvToTour(String(ev.target.result));
                        if(!imp.length){ alert('No valid rows'); return; }
                        const sc = gmGet(GMK.schemas,[]);
                        sc.push({ id: Date.now(), name: `Imported_${fmtISO(new Date())}`, tour: imp, settings: DEF, templateDays: 56 });
                        gmSet(GMK.schemas, sc);
                        renderSchemas(container);
                        alert(`${imp.length} ${T('summaryShows')} OK`);
                    } catch { alert('CSV error'); }
                };
                r.readAsText(f);
            }; inp.click();
        });
        iBtn.style.cssText = btnSt;
        hr.appendChild(iBtn); container.appendChild(hr);

        // "Son Kaydedilen" = active GMK.tour
        const activeTour=gmGet(GMK.tour,[]);
        const activeSets=gmGet(GMK.sets,null);
        if (activeTour.length) {
            const hdrRow = mk('div'); hdrRow.style.cssText = 'display:flex; justify-content:space-between; align-items:center; margin-bottom:6px;';
            const hdr=mk('div','',T('savedPlanHdr')+':'); hdr.style.cssText='font-weight:700;font-size:12px;color:#6f42c1;';
            const saveBtn=mkB(T('btnSave'), '', () => {
                const sc = gmGet(GMK.schemas,[]);
                sc.push({ id: Date.now(), name: `Schema ${fmtISO(new Date())}`, tour: activeTour, settings: activeSets, templateDays: 56 });
                gmSet(GMK.schemas, sc);
                renderSchemas(container);
            });
            saveBtn.style.cssText = 'padding:2px 8px; font-size:11px; background:#6f42c1; color:#fff; border:none; border-radius:4px; cursor:pointer;';
            hdrRow.append(hdr, saveBtn); container.appendChild(hdrRow);

            const card = buildPlanCard({id:'active', name:T('savedPlanHdr'), tour:activeTour, settings:activeSets, templateDays:56}, container);
            // Dynamic summary
            const as = mk('div', '', tourSummary(activeTour)); as.id = 'r49-active-summary'; as.style.cssText = 'font-size:11px;color:#555;margin-bottom:6px;';
            card.insertBefore(as, card.firstChild);
            container.appendChild(card);

            const sep=mk('hr'); sep.style.cssText='border:none;border-top:1px solid #e0d8ff;margin:10px 0;'; container.appendChild(sep);
        }

        // Render dynamic schemas
        const schemas = gmGet(GMK.schemas,[]);
        if (!activeTour.length && !schemas.length) {
            const emptyState = mk('div', '', _D('HenΓΌz kaydedilmiş bir turne şemasΔ± bulunmuyor.', 'No saved tour schemas found.', 'Nenhum esquema de turnΓͺ salvo encontrado.'));
            emptyState.style.cssText = 'color:#888; font-style:italic; text-align:center; padding:20px 0; font-size:12px;';
            container.appendChild(emptyState);
        } else {
            schemas.forEach(sc => {
                const slotHdr=mk('div', '', sc.name);
                slotHdr.style.cssText='font-weight:700;font-size:12px;color:#888;margin-bottom:6px;';
                container.appendChild(slotHdr);
                container.appendChild(buildPlanCard(sc, container));
            });
        }
    }
    function buildPlanCard(planObj, tabContainer) {
        const {id, name, tour, settings, templateDays} = planObj;
        const card=mk('div'); card.style.cssText='background:#fff;border:1px solid #e0d8ff;border-radius:6px;padding:8px;margin-bottom:12px;';

const actRow = mk('div'); actRow.style.cssText='display:flex;gap:4px;margin-bottom:6px;flex-wrap:wrap;';

        const selAllBtn = mkB(T('selAll'), '', () => {
            document.querySelectorAll(`[id^="r49-plan-chk-${id}-"]:not(:disabled)`).forEach(cb=>cb.checked=true);
        });
        const selNoneBtn = mkB(T('selNone'), '', () => {
            document.querySelectorAll(`[id^="r49-plan-chk-${id}-"]:not(:disabled)`).forEach(cb=>cb.checked=false);
        });
        [selAllBtn, selNoneBtn].forEach(b => b.style.cssText='padding:2px 8px;font-size:11px;border:1px solid #c9b8f0;border-radius:4px;cursor:pointer;background:#fff;color:#6f42c1;');

        actRow.append(selAllBtn, selNoneBtn);
        card.appendChild(actRow);

        // Show list with checkboxes
        const listDiv=mk('div'); listDiv.style.cssText='max-height:160px;overflow-y:auto;border:1px solid #e0d8ff;border-radius:4px;padding:4px;margin-bottom:6px;font-size:11px;';

        (tour||[]).forEach((show,si)=>{
            const row=mk('div'); row.style.cssText='display:flex;align-items:center;gap:5px;padding:2px 4px;border-radius:3px;'+(si%2===0?'background:#faf7ff;':'');
            const chk=mk('input'); chk.type='checkbox'; chk.id=`r49-plan-chk-${id}-${si}`;
            chk.checked=!show.booked;
            if (show.booked) { chk.style.opacity='0.5'; chk.disabled=true; }

            const c=cityById(show.cityId);
            const sd=show.specialDay?SPECIAL_DAYS[show.specialDay]?.icon||'🎡':'🎡';
            const pm=realToPM(new Date(show.date+'T00:00:00'));
            const dateParts = show.date.split('-');
            const mmdd = `${dateParts[1]}-${dateParts[2]}`; // MM-DD
            const bIcon=show.booked?'βœ…':show.skipped?'⏭':'';
            const pMin=settings?.priceMin||0, pMax=settings?.priceMax||0;
            const stars=settings?Math.floor(settings.minStars/10):5;
            const vStr = show.venueType==='stadium' ? T('venueStad') : T('venueBar');

            // Format: 🎡 152-16 03-12 22:00 Δ°stanbul | Bar | 10-20 M$ | 5β˜…
            const txt=mk('span','',`${sd} ${pm.year}-${pm.day} ${mmdd} ${show.time.slice(0,5)} ${cityName(c)} | ${vStr} | ${pMin}-${pMax} M$ | ${stars}β˜… ${bIcon}`);
            txt.style.flex='1';
            row.append(chk,txt); listDiv.appendChild(row);
        });
        card.appendChild(listDiv);

        // Action buttons
        const btnRow=mk('div'); btnRow.style.cssText='display:flex;gap:6px;flex-wrap:wrap;';
        const editBtn=mkB(T('btnEdit'),'',()=>openSchemaEditor(planObj, tabContainer)); editBtn.style.cssText=btnSt;

        const startBtn=mkB(T('btnStartPlan'),'',()=>{

            const selectedTour=(tour||[]).filter((_,si)=>document.getElementById(`r49-plan-chk-${id}-${si}`)?.checked);
            if (!selectedTour.length) { alert(_D('Konser seΓ§ilmedi.','No shows selected.','Nenhum show selecionado.')); return; }
            const aid=settings?.artistId||document.getElementById('r49-artist')?.value?.trim()||'';
            if (!aid) { alert(T('artistId')+'?'); return; }
            const s={...settings, artistId:aid};
            selectedTour.forEach(show => { if (show.willBook === undefined) show.willBook = true; });
            gmSet(GMK.tour,selectedTour); gmSet(GMK.idx,0); gmSet(GMK.sets,s); gmSet(GMK.status,'CHECK_UPCOMING');
            updateFloatBar();
            window.location.href=`https://${window.location.hostname}/World/Popmundo.aspx/Artist/UpcomingPerformances/${aid}`;
        });
        startBtn.style.cssText='padding:4px 10px;border:none;border-radius:4px;cursor:pointer;font-size:12px;font-weight:600;color:#fff;background:#28a745;';

        const expBtn=mkB(T('btnExportCSV'),'',()=>dlCSV(tourToCSV(tour, settings), `${name.replace(/[^a-z0-9]/gi, '_').toLowerCase()}.csv`));
        expBtn.style.cssText=btnSt;

        const schemaPrevBtn=mkB('πŸ‘ '+_D('Γ–nizle','Preview','PrΓ©-ver'),'',()=>openPreview(tour, settings, id));
        schemaPrevBtn.style.cssText=btnSt;

        const delBtn = id!=='active' ? mkB(_D('πŸ—‘ Sil','πŸ—‘ Del','πŸ—‘ Exc'),'',()=>{
            if(confirm(_D('Sil?','Delete?','Excluir?'))){ const sc=gmGet(GMK.schemas,[]);gmSet(GMK.schemas,sc.filter(x=>x.id!==id));renderSchemas(tabContainer); }
        }) : null;
        if(delBtn){delBtn.style.cssText='padding:4px 10px;border:1px solid #dc3545;border-radius:4px;cursor:pointer;font-size:12px;background:#fff0f0;color:#dc3545;font-family:inherit;';}

        btnRow.append(editBtn,startBtn,schemaPrevBtn,expBtn,...(delBtn?[delBtn]:[])); card.appendChild(btnRow);
        return card;
    }

    function openSchemaEditor(planObj, tabContainer) {
        const name = prompt(T('planName'), planObj.name||'');
        if (name === null) return;
        const updated = {...planObj, name: name.trim()||planObj.name};
        if (planObj.id === 'active') {
            gmSet(GMK.sets, {...(planObj.settings||{}), name});
        } else {
            const sc = gmGet(GMK.schemas,[]);
            const idx = sc.findIndex(x => x.id === planObj.id);
            if (idx !== -1) { sc[idx] = updated; gmSet(GMK.schemas, sc); }
        }
        renderSchemas(tabContainer);
    }

    // ─── START / STOP ─────────────────────────────────────────────────────────────
    async function startBooking() {
        const settings=gatherSettings();
        if (!settings.artistId) { alert(T('artistId')+'?'); return; }
        const tour=buildTour(settings);
        if (!tour.length) { alert(T('noTour')); return; }
        // VarsayΔ±lan olarak hepsini ayarla (willBook) yap
        tour.forEach(s => { if (s.willBook === undefined) s.willBook = true; });
        gmSet(GMK.tour,tour); gmSet(GMK.idx,0); gmSet(GMK.sets,settings); gmSet(GMK.status,'CHECK_UPCOMING');
        updateFloatBar();
        showPanelStatus(_D('Yaklaşan konserler kontrol ediliyor...','Checking upcoming shows...','Verificando próximos shows...'),'#6f42c1');
        const path=`/World/Popmundo.aspx/Artist/UpcomingPerformances/${settings.artistId}`;
        window.location.href=`https://${window.location.hostname}${path}`;
    }

    function stopBooking() {[GMK.status,GMK.tour,GMK.idx,GMK.sets,GMK.restore].forEach(gmDel);
        updateFloatBar(); location.reload();
    }

    function checkUpcomingAndProceed() {
        const tour = gmGet(GMK.tour, []);
        const settings = gmGet(GMK.sets, null);
        if (!tour.length || !settings) { gmSet(GMK.status, 'IDLE'); return; }

        const cityLinks = document.querySelectorAll('a[href*="/World/Popmundo.aspx/City/"]');
        const existingCityIds = new Set();
        cityLinks.forEach(a => {
            const m = a.href.match(/\/City\/(\d+)/);
            if (m) existingCityIds.add(parseInt(m[1]));
        });

        const conflictCities = new Set();
        tour.forEach(show => {
            if (existingCityIds.has(show.cityId) && !show.booked && show.willBook !== false) {
                conflictCities.add(show.cityId);
            }
        });

        if (conflictCities.size > 0) {
            const cityNames = Array.from(conflictCities).map(id => cityName(cityById(id)) || id).join(', ');
            const msg = _D(
                `Dikkat: ${cityNames} şehirlerinde halihazırda konserleriniz var!\n\nBu şehirlerdeki yeni konser planları otomatik olarak devredışı bırakılsın mı?`,
                `Warning: You already have shows in ${cityNames}!\n\nDisable new planned shows in these cities?`,
                `Aviso: VocΓͺ jΓ‘ tem shows em ${cityNames}!\n\nDesativar novos shows planejados nestas cidades?`
            );

            if (confirm(msg)) {
                tour.forEach(show => {
                    if (conflictCities.has(show.cityId)) show.willBook = false;
                });
                gmSet(GMK.tour, tour);
            }

            if (!confirm(_D('Turne planΔ±na devam etmek istiyor musunuz?', 'Continue with tour plan?', 'Continuar com o plano?'))) {
                gmSet(GMK.status, 'IDLE');
                updateFloatBar();
                return;
            }
        }

        gmSet(GMK.status, 'RUNNING');
        window.location.href = `https://${window.location.hostname}/World/Popmundo.aspx/Artist/BookShow/${settings.artistId}`;
    }

// ─── POPCONTROL BAĞLANTISI ─────────────────────────────────────────────────────
    function registerWithPopControl() {
        if (window.PPC_Route49_Done) return;
        const pc = (typeof unsafeWindow !== 'undefined' && unsafeWindow.PopControl) || window.PopControl;
        if (!pc?.register) return;
        try {
            pc.register({
                id:'route49', icon:'🌟', label:'Route49',
                buttons:[{
                    icon:'🌟', label:'Route49',
                    onClick:()=>{ window.location.href='/World/Popmundo.aspx/Artist/BookShow/'; }
                }],
                onUndo:()=>{ window.PPC_Route49_Done = false; document.getElementById('r49-panel')?.remove(); },
            });
            window.PPC_Route49_Done = true;
            _injectR49MenuItems();
            log('PopControl bağlantısı başarılı');
        } catch(e) {
            console.error('[Route49] PopControl bağlantı hatası:', e);
        }
    }

    // ─── INIT ─────────────────────────────────────────────────────────────────────
    async function init() {
        // Floating bar on every page
        if (document.body) injectFloatBar(); else window.addEventListener('load',injectFloatBar);

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

        // BookShow UI
        if (IS_BOOKSHOW) {
            const tryInject=()=>{ if(!document.getElementById('r49-panel')) { injectUI(); if(settings) applySettingsToUI(settings); } };
            if (document.readyState==='complete') tryInject(); else window.addEventListener('load',tryInject);
            const iv=setInterval(()=>{ if(!document.getElementById('r49-panel')) tryInject(); },3000);
            window.addEventListener('beforeunload',()=>clearInterval(iv));
        }

        // Check running state
        const status=gmGet(GMK.status,'IDLE');

        if (status === 'CHECK_UPCOMING') {
            if (window.location.pathname.includes('/UpcomingPerformances/')) {
                checkUpcomingAndProceed();
            }
            return;
        }

        if (status!=='RUNNING') return;
        if (!settings) { gmSet(GMK.status,'IDLE'); return; }

        // If running but NOT on BookShow β†’ redirect there
        if (!IS_BOOKSHOW) {
            const aid=settings.artistId;
            if (aid) { await delay(500); window.location.href=`https://${window.location.hostname}/World/Popmundo.aspx/Artist/BookShow/${aid}`; }
            return;
        }

        // On BookShow + RUNNING β†’ continue
        await delay(700);
        const p=document.getElementById('r49-panel'); if(p) p.style.opacity='0.6';
        processNextShow(settings);
    }

    // ─── LOGGING SYSTEM ──────────────────────────────────────────────────────────
    function addLog(msg) {
        const logs = gmGet('r49_logs', []);
        logs.push(`[${new Date().toLocaleTimeString()}] ${msg}`);
        if(logs.length > 100) logs.shift();
        gmSet('r49_logs', logs);
    }

    function openLogViewer() {
        const ov = mk('div'); ov.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:99999;display:flex;align-items:center;justify-content:center;';
        const box = mk('div'); box.style.cssText = 'background:#fff;padding:20px;width:400px;max-height:80vh;overflow-y:auto;border-radius:8px;';
        const logs = gmGet('r49_logs', []);
        box.innerHTML = `<div style="font-weight:bold;margin-bottom:10px;">Logs</div>` + logs.map(l => `<div style="font-size:11px;border-bottom:1px solid #eee;">${l}</div>`).join('');
        box.appendChild(mkB('βœ• Kapat', '', () => ov.remove()));
        ov.onclick = e => { if(e.target === ov) ov.remove(); };
        document.body.appendChild(ov);
    }

    // ── PUBLIC API ──────────────────────────────────────────────────────────────
    window.__r49_api = {
        goBookShow: () => { window.location.href = '/World/Popmundo.aspx/Artist/BookShow/'; },
        openPreview:() => typeof openPreview    === 'function' && openPreview(),
        openLog:    () => typeof openLogViewer  === 'function' && openLogViewer(),
    };

    // ── TOP VIP MENÜSÜ ENJEKSΔ°YONU ──────────────────────────────────────────────
    const _injectR49MenuItems = () => {
        if (!location.href.includes('/World/Popmundo.aspx/Character')) return;

        // Artist ID – stored settings
        const aid = gmGet(GMK.sets, null)?.artistId || '';

        // ── Artist quick-links ──────────────────────────────────────────────
        const _artistLinks = [
            { id: 'mnu-r49-schedule',     icon: 'πŸ“…', key: 'mnuSchedule',     url: `/World/Popmundo.aspx/Artist/Schedule/${aid}`            },
            { id: 'mnu-r49-setlist',      icon: '🎡', key: 'mnuSetlist',      url: `/World/Popmundo.aspx/Setlist/`                          },
            { id: 'mnu-r49-popularity',   icon: 'πŸ“ˆ', key: 'mnuPopularity',   url: `/World/Popmundo.aspx/Artist/Popularity/${aid}`          },
            { id: 'mnu-r49-repertoire',   icon: 'πŸ“', key: 'mnuRepertoire',   url: `/World/Popmundo.aspx/Artist/Repertoire/${aid}`          },
            { id: 'mnu-r49-upcoming',     icon: '🎀', key: 'mnuUpcoming',     url: `/World/Popmundo.aspx/Artist/UpcomingPerformances/${aid}` },
            { id: 'mnu-r49-equipment',    icon: '🎸', key: 'mnuEquipment',    url: `/World/Popmundo.aspx/Artist/Equipment/${aid}`           },
            { id: 'mnu-r49-vehicle',      icon: '🚌', key: 'mnuVehicle',      url: `/World/Popmundo.aspx/Artist/Vehicle/${aid}`             },
            { id: 'mnu-r49-vehicleitems', icon: 'πŸŽ’', key: 'mnuVehicleItems', url: `/World/Popmundo.aspx/Artist/VehicleItems/${aid}`        },
            { id: 'mnu-r49-crew',         icon: 'πŸ‘₯', key: 'mnuCrew',         url: `/World/Popmundo.aspx/Artist/Crew/${aid}`                },
        ];

        const r49Buttons = [
            { id: 'mnu-r49-bookshow', label: '🌟 Route49', fn: () => window.__r49_api?.goBookShow() },
            ..._artistLinks.map(l => ({ id: l.id, label: `${l.icon} ${T(l.key)}`, fn: () => { window.location.href = l.url; } })),
        ];

        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: r49Buttons, collapsible: true });
            return;
        }

        // ── Standalone fallback (PopControl yoksa) ──────────────────────────
        if (document.getElementById('mnu-r49-bookshow')) 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' }));
        }
        r49Buttons.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);
        });
    };
    _injectR49MenuItems();

    init().catch(e=>console.error('[Route49] init error:',e));


})();