☰

WOW.XXX Wicked Tab

Multi-select videos and open them in a beautiful instant Fullscreen Grid Viewer!

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu budete musieť nainštalovať rozšírenie, ako je napríklad Tampermonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         WOW.XXX Wicked Tab
// @namespace    http://tampermonkey.net/
// @version      1.2.1
// @description  Multi-select videos and open them in a beautiful instant Fullscreen Grid Viewer!
// @author       6969RandomGuy6969
// @license MIT
// @match        https://www.wow.xxx/*
// @match        https://wow.xxx/*
// @icon         https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=https://wow.xxx/&size=256
// @grant        GM_xmlhttpRequest
// @grant        GM_setClipboard
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @connect      wow.xxx
// ==/UserScript==

(function () {
    'use strict';

    const WICKED_KEY        = 'wp_wicked_videos';
    const WICKED_SEL_KEY    = 'wp_wicked_selections';

    let autoPlayAll = GM_getValue('wickedAutoPlayAll', false);

    GM_registerMenuCommand(
        `${autoPlayAll ? '✅' : '⬜'} Wicked Tab: Auto-play all thumbnails`,
        () => {
            autoPlayAll = !autoPlayAll;
            GM_setValue('wickedAutoPlayAll', autoPlayAll);
            alert(`Wicked Tab thumbnail autoplay ${autoPlayAll ? 'ENABLED' : 'DISABLED'}.\\nReopen Wicked Tab to apply.`);
        }
    );

    function fixUrl(src, type) {
        if (!src) return '';
        let url = src;
        if (url.startsWith('//')) url = window.location.protocol + url;
        else if (url.startsWith('/')) url = window.location.origin + url;

        if (url.includes('www.wow.xxx')) {
            if (type === 'preview' || url.includes('/preview/')) {
                url = url.replace('www.wow.xxx', 'cast.wow.xxx');
            } else if (type === 'thumb' || url.match(/www\.wow\.xxx\/\d+\//)) {
                url = url.replace('www.wow.xxx', 'img.wow.xxx');
            }
        }
        return url;
    }

    function extractId(videoUrl) {
        if (!videoUrl) return null;
        // Match anything after /video/ until the next slash or end of string
        const m = videoUrl.match(/\/videos?\/([^\/]+)\/?/);
        return m ? m[1] : null;
    }

    function fetchPreviewUrl(videoUrl, callback) {
        GM_xmlhttpRequest({
            method: 'GET', url: videoUrl, timeout: 10000,
            onload: r => {
                try {
                    const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
                    const img = doc.querySelector('img[data-preview]');
                    callback(fixUrl(img?.getAttribute('data-preview') || '', 'preview'));
                } catch(e) { callback(''); }
            },
            onerror:   () => callback(''),
            ontimeout: () => callback('')
        });
    }

    function getMainImg(card) {
        // Skip the f3.svg loader image and get the actual thumbnail
        return card.querySelector('img.thumb-img') || card.querySelector('img[itemprop="thumbnail"]') || card.querySelector('img:not([src*="f3.svg"])') || card.querySelector('img');
    }

    function extractThumb(card) {
        const img = getMainImg(card);
        const src = img?.getAttribute('data-webp') || img?.getAttribute('data-src') || img?.getAttribute('data-original') || img?.src || '';
        return fixUrl(src, 'thumb');
    }

    function extractPreviewMp4(card) {
        const img = getMainImg(card);
        const previewEl = card.querySelector('[data-preview]');
        const p = previewEl ? previewEl.getAttribute('data-preview') : (img?.getAttribute('data-preview') || '');
        return fixUrl(p, 'preview');
    }

    function extractDuration(card) {
        return card.querySelector('.thumb__info-item, .duration')?.textContent?.trim() || '';
    }

    function extractRating(card) {
        const m = (card.querySelector('.thumb__info, .rating')?.textContent || '').match(/(\d+)%/);
        return m ? m[1] + '%' : '';
    }

    function extractViews(card) {
        for (const sp of [...(card.querySelectorAll('.thumb__info span, .views') || [])]) {
            const t = sp.textContent.trim();
            if (/^\d[\d.,]*[KkMm]?$/.test(t) && !t.includes(':') && !t.includes('%')) return t;
        }
        return '';
    }

    function getPath(urlStr) {
        if (!urlStr) return '';
        try { return new URL(urlStr, window.location.origin).pathname; } catch(e) { return urlStr; }
    }

    function generateShareLink() {
        if (!wickedState.sel.length) return alert('Select videos first!');
        const compactData = wickedState.sel.map(v => ({
            i: v.id || extractId(v.url),
            t: getPath(v.thumb),
            p: getPath(v.preview),
            n: v.title || 'Video'
        }));
        // Use proper UTF-8 Base64 encoding to prevent 3x size inflation
        const b64 = btoa(unescape(encodeURIComponent(JSON.stringify(compactData))));
        const link = `https://www.wow.xxx/#wicked_share=${b64}`;
        GM_setClipboard(link);
        alert('✅ Share Link Copied to Clipboard!\\n\\nAnyone with this script installed can paste this link in their browser to instantly open your exact Grid Viewer!');
    }

    function checkShareLink() {
        let shareData = '';
        if (window.location.hash.startsWith('#wicked_share=')) {
            shareData = window.location.hash.substring(14);
        } else {
            shareData = new URLSearchParams(window.location.search).get('wicked_share') || '';
        }

        if (shareData) {
            try {
                const raw = atob(shareData);
                const jsonStr = raw.startsWith('%') ? decodeURIComponent(raw) : decodeURIComponent(escape(raw));
                const arr = JSON.parse(jsonStr);
                if (Array.isArray(arr) && arr.length > 0) {
                    const importedSel = arr.map(v => ({
                        url: v.i ? `https://www.wow.xxx/videos/${v.i}/` : '',
                        id: v.i || '',
                        thumb: v.t ? fixUrl(v.t, 'thumb') : '',
                        preview: v.p ? fixUrl(v.p, 'preview') : '',
                        title: v.n || 'Shared Video',
                        duration: '', rating: '', views: ''
                    })).filter(v => v.url);

                    if (importedSel.length > 0) {
                        wickedState.sel = importedSel;
                        try { localStorage[WICKED_SEL_KEY] = JSON.stringify(wickedState.sel); } catch(e){}
                        window.history.replaceState(null, null, window.location.pathname);
                        setTimeout(() => {
                            updateWickedUI();
                            openFullscreenGrid();
                        }, 500);
                    }
                }
            } catch(e) {
                console.error("Invalid wicked_share link", e);
            }
        }
    }

    const wickedStore = {
        getSel: () => { try { return JSON.parse(localStorage[WICKED_SEL_KEY] || '[]') } catch { return [] } },
        setSel: d  => { try { localStorage[WICKED_SEL_KEY] = JSON.stringify(d); return 1 } catch { return 0 } },
        clrSel: () => delete localStorage[WICKED_SEL_KEY]
    };

    const wickedState = { sel: [], vis: false };

    // --- FULL PREMIUM CSS ---
    const css = document.createElement('style');
    css.textContent = `
        /* iOS-inspired Sleek Minimalist Theme */
        .wicked-select-overlay{position:absolute;top:8px;left:8px;width:24px;height:24px;background:rgba(10,132,255,.9);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:none;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:14px;color:#fff;opacity:0;transition:all .3s cubic-bezier(.16,1,.3,1);z-index:10;pointer-events:none;box-shadow:0 4px 12px rgba(10,132,255,.4);transform:scale(0.8)}
        body.wicked-active .item:hover .wicked-select-overlay{opacity:0.5;transform:scale(1)}
        body.wicked-active .item.wicked-selected .wicked-select-overlay{opacity:1;transform:scale(1)}
        .item.wicked-selected::before{content:"";position:absolute;inset:0;border:2px solid #0A84FF;border-radius:inherit;pointer-events:none;z-index:5;box-shadow:inset 0 0 0 1px rgba(0,0,0,.2)}
        .item{position:relative}

        .wp-wicked-sort-btn{display:inline-flex;align-items:center;gap:6px;background:rgba(28,28,30,.75);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);color:#fff;border:1px solid rgba(255,255,255,.1);border-radius:20px;padding:6px 12px;cursor:pointer;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;font-size:13px;font-weight:500;transition:all .3s;outline:none;margin-left:12px;vertical-align:middle;box-shadow:0 4px 12px rgba(0,0,0,.15)}
        .wp-wicked-sort-btn:hover{background:rgba(44,44,46,.9)}
        body.wicked-active .wp-wicked-sort-btn[style*="fixed"]{opacity:0;pointer-events:none;transform:translateY(10px) scale(0.9)}

        .wicked-icon{font-weight:600;letter-spacing:-0.3px}
        .wicked-badge{background:#0A84FF;color:#fff;padding:2px 6px;border-radius:10px;font-size:11px;font-weight:700;min-width:16px;text-align:center;display:none;transition:transform .2s}
        .wicked-badge.visible{display:inline-block}

        .wicked-logo-badge{position:absolute;top:-4px;right:-8px;background:#0A84FF;color:#fff;padding:2px 6px;border-radius:10px;font-size:11px;font-weight:700;min-width:16px;text-align:center;box-shadow:0 2px 8px rgba(10,132,255,.4);cursor:pointer;z-index:1000;display:none}

        /* iOS Pill Control Panel */
        #wicked-control-panel{position:fixed;bottom:24px;left:50%;transform:translateX(-50%) translateY(20px);background:rgba(28,28,30,.85);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);border:1px solid rgba(255,255,255,.1);border-radius:40px;padding:8px 12px;box-shadow:0 12px 40px rgba(0,0,0,.4);z-index:10000;display:none!important;flex-direction:row;align-items:center;gap:6px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;min-width:auto;white-space:nowrap}
        #wicked-control-panel.visible{display:flex!important;animation:slideUpPill .4s cubic-bezier(.16,1,.3,1) forwards}
        @keyframes slideUpPill{from{opacity:0;transform:translate(-50%,20px) scale(.95)}to{opacity:1;transform:translate(-50%,0) scale(1)}}

        .wicked-counter{font-size:14px;font-weight:500;color:#fff;display:flex;align-items:center;gap:6px;padding-left:4px}
        #wicked-count{background:#0A84FF;color:#fff;padding:2px 8px;border-radius:12px;font-size:13px;display:inline-flex;align-items:center;justify-content:center;font-weight:600;min-width:24px;line-height:1.2;box-shadow:0 2px 6px rgba(10,132,255,.3)}
        .wicked-counter .mob-hide{color:#EBEBF5}
        .wicked-divider{width:1px;height:20px;background:rgba(255,255,255,.15);margin:0 4px}

        .wicked-btn{background:transparent;color:#fff;border:none;padding:8px 12px;border-radius:20px;cursor:pointer;font-weight:500;font-size:14px;transition:all .2s ease;display:flex;align-items:center;gap:6px;font-family:inherit}
        .wicked-btn:hover{background:rgba(255,255,255,.1)}
        .wicked-btn.primary{color:#0A84FF;font-weight:600}
        .wicked-btn.primary:hover{background:rgba(10,132,255,.15)}
        .wicked-btn.danger{color:#FF453A}
        .wicked-btn.danger:hover{background:rgba(255,69,58,.15)}
        .wicked-btn svg{width:18px;height:18px;stroke-width:2.5px;display:block}

        /* Fullscreen Overlay Styles */
        #wicked-fullscreen-overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background:#000;z-index:9999999;overflow-y:auto;display:none;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}
        #wicked-fullscreen-overlay::-webkit-scrollbar{width:0px;display:none}

        #grid-header{background:rgba(28,28,30,.8);backdrop-filter:blur(30px);-webkit-backdrop-filter:blur(30px);border-bottom:1px solid rgba(255,255,255,.1);padding:12px 20px;display:flex;align-items:center;position:sticky;top:0;z-index:100}
        .grid-logo{font-size:18px;font-weight:700;letter-spacing:-0.5px;color:#fff}
        .grid-logo-text{color:#fff}
        .grid-version{font-size:11px;background:rgba(255,255,255,.1);color:rgba(255,255,255,.8);padding:2px 6px;border-radius:8px;margin-left:6px;vertical-align:middle;font-weight:500;display:inline-block}
        #grid-header h2{font-size:15px;font-weight:500;display:flex;align-items:center;gap:8px;margin:0 0 0 16px;color:#fff}
        .video-count{color:rgba(255,255,255,.5)}
        .grid-header-actions{margin-left:auto;display:flex;gap:8px}
        .grid-header-btn{background:rgba(255,255,255,.1);color:#fff;border:none;padding:6px 14px;border-radius:16px;cursor:pointer;font-size:14px;font-weight:500;transition:all .2s;font-family:inherit}
        .grid-header-btn:hover{background:rgba(255,255,255,.2)}
        .grid-header-btn.close{color:#0A84FF;font-weight:600;background:transparent}
        .grid-header-btn.close:hover{background:rgba(10,132,255,.15)}

        .autoplay-badge{display:inline-flex;align-items:center;gap:4px;background:rgba(48,209,88,.15);color:#30D158;font-size:12px;font-weight:500;padding:4px 10px;border-radius:12px;margin-left:12px}

        #grid-viewer-container{display:grid;grid-template-columns:1fr;gap:16px;padding:16px;max-width:1920px;margin:0 auto}

        .video-grid-item{background:transparent;border:none;border-radius:16px;overflow:visible;position:relative;animation:fadeInUp .4s cubic-bezier(.16,1,.3,1) backwards}
        @keyframes fadeInUp{from{opacity:0;transform:translateY(20px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}

        .vid-thumb-wrap{position:relative;width:100%;aspect-ratio:16/9;background:#1C1C1E;overflow:hidden;cursor:pointer;border-radius:16px;box-shadow:0 4px 20px rgba(0,0,0,.3);border:1px solid rgba(255,255,255,.05);transition:transform .3s cubic-bezier(.16,1,.3,1),box-shadow .3s}
        .vid-thumb{width:100%;height:100%;object-fit:cover;display:block;transition:transform .3s ease, opacity .3s;position:absolute;inset:0}

        .vid-thumb-wrap:active{transform:scale(0.97)}
        @media(hover:hover){
            .vid-thumb-wrap:hover{transform:scale(1.02);box-shadow:0 8px 30px rgba(0,0,0,.5)}
            .vid-thumb-wrap:hover .vid-thumb{transform:scale(1.03)}
            .vid-thumb-wrap:has(.vid-preview):hover .vid-thumb{opacity:0}
            .vid-thumb-wrap:has(.vid-preview):hover .vid-preview{opacity:1}
            .vid-thumb-wrap:hover .vid-action-btns{opacity:1}
            .vid-thumb-wrap:hover .vid-play-btn{opacity:1;transform:scale(1)}
            .vid-thumb-wrap:has(.vid-preview):hover .vid-play-btn{opacity:0}
        }

        .vid-thumb-wrap.autoplay-active:has(.vid-preview) .vid-thumb{opacity:0;transform:scale(1.03)}
        .vid-preview{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:0;transition:opacity .4s;pointer-events:none}
        .vid-thumb-wrap.autoplay-active .vid-preview{opacity:1}

        .vid-play-btn{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;opacity:0;transition:all .3s cubic-bezier(.16,1,.3,1);pointer-events:none;z-index:2;transform:scale(0.9)}
        .vid-play-btn svg{width:48px;height:48px;filter:drop-shadow(0 4px 12px rgba(0,0,0,.4))}
        .vid-play-btn circle{fill:rgba(255,255,255,.25);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}
        .vid-thumb-wrap.autoplay-active .vid-play-btn{opacity:0}

        .vid-duration{position:absolute;bottom:10px;right:10px;background:rgba(0,0,0,.6);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);color:#fff;font-size:12px;font-weight:600;padding:4px 8px;border-radius:8px;pointer-events:none;z-index:3;letter-spacing:0.5px}

        .vid-rating-bar{position:absolute;bottom:0;left:0;right:0;height:4px;background:rgba(255,255,255,.1);z-index:3}
        .vid-rating-fill{height:100%;background:#30D158;border-radius:0 2px 2px 0}

        .vid-action-btns{position:absolute;top:10px;right:10px;display:flex;gap:8px;opacity:0;transition:opacity .2s;z-index:4}
        .vid-action-btn{width:32px;height:32px;border-radius:16px;padding:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.5);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);color:#fff;border:none;cursor:pointer;font-size:16px;font-weight:500;transition:all .2s;box-shadow:0 2px 8px rgba(0,0,0,.2)}
        .vid-action-btn.remove:hover{background:#FF453A}
        .vid-action-btn.reload:hover{background:#0A84FF}

        .vid-player{width:100%;height:100%;object-fit:cover;background:#000;display:block;position:absolute;inset:0;z-index:5}
        .vid-player-loading{position:absolute;inset:0;z-index:6;background:rgba(0,0,0,.5);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px;color:#fff;font-size:13px;font-weight:500}
        .vid-player-loading::before{content:"";width:24px;height:24px;border:3px solid rgba(255,255,255,.2);border-top-color:#fff;border-radius:50%;animation:spin 1s linear infinite}
        @keyframes spin{to{transform:rotate(360deg)}}

        .vid-info{padding:12px 4px}
        .vid-title{font-size:15px;font-weight:500;color:#fff;text-decoration:none;line-height:1.3;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;transition:color .2s;letter-spacing:-0.2px}
        .vid-title:hover{color:#0A84FF}
        .vid-meta{display:flex;align-items:center;gap:12px;margin-top:6px;font-size:13px;color:#8E8E93;flex-wrap:wrap;font-weight:400}
        .vid-meta-rating{color:#30D158;font-weight:500}

        .vid-error{position:absolute;inset:0;background:#1C1C1E;z-index:5;display:flex;flex-direction:column;align-items:center;justify-content:center;color:#FF453A;gap:8px;font-size:13px;font-weight:500}
        .vid-error::before{content:"⚠";font-size:24px}

        .vid-thumb-wrap.fetching-preview .vid-thumb{animation:previewFetch 1.5s ease-in-out infinite}
        @keyframes previewFetch{0%,100%{opacity:1}50%{opacity:.6}}

        @media(min-width:600px){
            #wicked-control-panel{bottom:32px;padding:10px 20px;border-radius:40px;gap:8px}
            .wicked-btn{font-size:15px;padding:10px 16px}
            #grid-header{padding:16px 32px}
            .grid-header-actions{gap:12px}
            #grid-viewer-container{grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:24px;padding:24px 32px}
            .vid-action-btns { opacity: 0; }
        }
        @media(max-width:599px){
            .mob-hide { display: none !important; }
            #grid-header { padding: 10px 12px; }
            .grid-logo { font-size: 16px; }
            #grid-header h2 { margin: 0 0 0 8px; font-size: 14px; }
            .grid-header-actions { gap: 4px; }
            .grid-header-btn { padding: 6px 12px; font-size: 13px; }
            .autoplay-badge { padding: 4px 6px; margin-left: 8px; border-radius: 8px; }
            .vid-action-btns { opacity: 1; transform: scale(0.9); transform-origin: top right; }
            .vid-play-btn { opacity: 1; background: transparent; }
            .vid-thumb-wrap:has(.vid-preview) .vid-play-btn { opacity: 0; }
            #wicked-control-panel { bottom: 16px; padding: 6px 10px; border-radius: 30px; gap: 4px; width: auto; max-width: 95%; background: rgba(28,28,30,0.95); box-shadow: 0 10px 30px rgba(0,0,0,0.5); }
            .wicked-counter { font-size: 13px; padding: 0 2px; }
            #wicked-count { font-size: 12px; padding: 2px 6px; min-width: 20px; }
            .wicked-btn { padding: 6px; width: 36px; height: 36px; border-radius: 18px; justify-content: center; }
            .wicked-btn svg { margin: 0; width: 18px; height: 18px; }
        }
        @media(min-width:1200px){
            #grid-header{padding:16px 48px}
            #grid-viewer-container{grid-template-columns:repeat(auto-fill,minmax(400px,1fr));gap:32px;padding:32px 48px}
        }
        @media(min-width:1800px){
            #grid-viewer-container{grid-template-columns:repeat(auto-fill,minmax(480px,1fr))}
        }
    `;
    document.head.appendChild(css);

    wickedState.sel = wickedStore.getSel();

    function createWickedButton() {
        if (document.getElementById('wicked-toggle-btn')) return;
        const sortEl = document.querySelector('.grid__headline .sort, .headline .sort, .headline-wrap');
        const btn = document.createElement('button');
        btn.id        = 'wicked-toggle-btn';
        btn.className = 'wp-wicked-sort-btn';
        btn.innerHTML = `<strong class="wicked-icon">Wicked</strong><span class="wicked-badge" id="wicked-badge">0</span>`;
        btn.onclick   = toggleWickedPanel;
        if (sortEl) {
            sortEl.after(btn);
        } else {
            btn.style.position = 'fixed';
            btn.style.bottom = '20px';
            btn.style.left = '20px';
            btn.style.zIndex = '9999';
            document.body.appendChild(btn);
        }
        updateWickedUI();
    }

    function createLogoBadge() {
        if (document.querySelector('.wicked-logo-badge')) return;
        const logoLink = document.querySelector('a.header__logo.js-logo');
        if (!logoLink) return;
        logoLink.style.position = 'relative';
        const badge = document.createElement('div');
        badge.className   = 'wicked-logo-badge';
        badge.textContent = wickedState.sel.length;
        badge.style.display = wickedState.sel.length > 0 ? 'block' : 'none';
        badge.onclick = e => { e.preventDefault(); e.stopPropagation(); toggleWickedPanel(); };
        logoLink.appendChild(badge);
    }

    function createWickedPanel() {
        if (document.getElementById('wicked-control-panel')) return;
        const p = document.createElement('div');
        p.id = 'wicked-control-panel';
        p.innerHTML = `
            <div class="wicked-counter"><span id="wicked-count">0</span> <span class="mob-hide">Selected</span></div>
            <div class="wicked-divider"></div>
            <button class="wicked-btn primary" id="open-wicked-btn" title="Open Grid Viewer">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="3" y1="9" x2="21" y2="9"></line><line x1="9" y1="21" x2="9" y2="9"></line></svg>
                <span class="mob-hide">Grid</span>
            </button>
            <button class="wicked-btn primary" id="share-wicked-btn" title="Share Selection">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"></path><polyline points="16 6 12 2 8 6"></polyline><line x1="12" y1="2" x2="12" y2="15"></line></svg>
                <span class="mob-hide">Share</span>
            </button>
            <button class="wicked-btn danger" id="clear-wicked-btn" title="Clear Selection">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>
                <span class="mob-hide">Clear</span>
            </button>
            <div class="wicked-divider"></div>
            <button class="wicked-btn" id="close-panel-btn" title="Close Panel" style="color:#8E8E93">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
            </button>`;
        document.body.appendChild(p);
        p.querySelector('#open-wicked-btn').onclick  = openFullscreenGrid;
        p.querySelector('#share-wicked-btn').onclick = generateShareLink;
        p.querySelector('#clear-wicked-btn').onclick = clearWickedSelection;
        p.querySelector('#close-panel-btn').onclick  = toggleWickedPanel;
        updateWickedUI();
    }

    function updateWickedUI() {
        const n = wickedState.sel.length;
        const badge1 = document.getElementById('wicked-badge');
        if (badge1) { badge1.textContent = n; badge1.classList.toggle('visible', n > 0); }

        const badge2 = document.querySelector('.wicked-logo-badge');
        if (badge2) { badge2.textContent = n; badge2.style.display = n > 0 ? 'block' : 'none'; }

        const c = document.getElementById('wicked-count');
        if (c) c.textContent = n;
    }

    function toggleWickedPanel() {
        const p = document.getElementById('wicked-control-panel');
        if (!p) return;
        wickedState.vis = !wickedState.vis;
        p.classList.toggle('visible', wickedState.vis);
        document.body.classList.toggle('wicked-active', wickedState.vis);
    }

    function clearWickedSelection() {
        wickedState.sel = []; wickedStore.clrSel(); updateWickedUI();
        document.querySelectorAll('.item.wicked-selected').forEach(el => el.classList.remove('wicked-selected'));
    }

    function processVideoThumbnails() {
        document.querySelectorAll('.item:not([data-wicked-processed])').forEach(card => {
            card.dataset.wickedProcessed = '1';
            const link = card.querySelector('a[href*="/videos/"]');
            if (!link) return;
            const url = link.href;

            const ov = document.createElement('div');
            ov.className = 'wicked-select-overlay';
            ov.innerHTML = '✓';
            const imgWrap = card.querySelector('.box-img') || card.querySelector('.thumb__img') || card.querySelector('.img') || card;
            imgWrap.appendChild(ov);

            if (wickedState.sel.some(v => v.url === url)) card.classList.add('wicked-selected');

            const toggle = (e) => {
                if (!wickedState.vis) return;
                if (!(e.ctrlKey || e.metaKey) && e.type !== 'contextmenu') return;
                e.preventDefault(); e.stopPropagation();

                const i = wickedState.sel.findIndex(v => v.url === url);
                if (i > -1) {
                    wickedState.sel.splice(i, 1);
                    card.classList.remove('wicked-selected');
                } else {
                    const title = link.getAttribute('title') || card.querySelector('.name, .title, strong.title, .thumb__title')?.textContent.trim() || 'Video';

                    const newObj = {
                        url, title,
                        thumb:    extractThumb(card),
                        preview:  extractPreviewMp4(card),
                        duration: extractDuration(card),
                        rating:   extractRating(card),
                        views:    extractViews(card),
                        id:       extractId(url)
                    };
                    wickedState.sel.push(newObj);
                    card.classList.add('wicked-selected');
                }

                wickedStore.setSel(wickedState.sel);
                updateWickedUI();
            };

            card.addEventListener('click', toggle, true);
            card.addEventListener('contextmenu', toggle, true);
        });
    }

    // --- FULLSCREEN GRID VIEWER ---
    function openFullscreenGrid() {
        if (!wickedState.sel.length) return alert('Select videos first (Right-Click or Ctrl+Click on thumbnails)!');

        let overlay = document.getElementById('wicked-fullscreen-overlay');
        if (!overlay) {
            overlay = document.createElement('div');
            overlay.id = 'wicked-fullscreen-overlay';
            document.body.appendChild(overlay);
        }

        document.body.style.overflow = 'hidden'; // Stop background scrolling
        overlay.style.display = 'block';
        overlay.innerHTML = `
            <div id="grid-header">
                <div class="grid-logo"><span class="grid-logo-text">Wicked</span><span class="grid-version mob-hide">v1.2</span></div>
                <h2><span class="video-count"><span id="grid-video-count-num">${wickedState.sel.length}</span> <span class="mob-hide" style="font-weight:normal">videos</span></span></h2>
                ${autoPlayAll ? `<span class="autoplay-badge" title="Auto-preview Enabled"><svg viewBox="0 0 24 24" fill="currentColor" width="10" height="10" style="margin-top:1px"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg><span class="mob-hide" style="margin-left:4px">Auto-preview</span></span>` : ''}
                <div class="grid-header-actions">
                    <button class="grid-header-btn" id="share-grid-btn">Share</button>
                    <button class="grid-header-btn close" id="close-grid-btn">Done</button>
                </div>
            </div>
            <div id="grid-viewer-container"></div>
        `;

        document.getElementById('close-grid-btn').onclick = () => {
            overlay.style.display = 'none';
            overlay.innerHTML = ''; // Kill players instantly
            document.body.style.overflow = ''; // Restore scroll position
        };
        document.getElementById('share-grid-btn').onclick = generateShareLink;

        const container = overlay.querySelector('#grid-viewer-container');

        const autoPlayObserver = autoPlayAll ? new IntersectionObserver(entries => {
            entries.forEach(entry => {
                const tw   = entry.target;
                const prev = tw.querySelector('.vid-preview');
                if (!prev) return;
                if (entry.isIntersecting) {
                    tw.classList.add('autoplay-active');
                    prev.play().catch(() => {});
                } else {
                    tw.classList.remove('autoplay-active');
                    prev.pause(); prev.currentTime = 0;
                }
            });
        }, { rootMargin: '200px', threshold: 0.1 }) : null;

        function backgroundFetchPreviews(vids, items) {
            const missing = vids.map((v, i) => ({ v, i })).filter(x => !x.v.preview);
            if (!missing.length) return;

            let delay = 0;
            missing.forEach(({ v, i }) => {
                setTimeout(() => {
                    const item      = items[i];
                    const thumbWrap = item?.querySelector('.vid-thumb-wrap');
                    if (thumbWrap) thumbWrap.classList.add('fetching-preview');

                    fetchPreviewUrl(v.url, previewUrl => {
                        if (thumbWrap) thumbWrap.classList.remove('fetching-preview');
                        if (!previewUrl) return;

                        previewUrl = fixUrl(previewUrl, 'preview');

                        const stored = wickedStore.getSel();
                        const idx    = stored.findIndex(s => s.url === v.url);
                        if (idx > -1) {
                            stored[idx].preview = previewUrl;
                            wickedStore.setSel(stored);
                        }

                        if (!thumbWrap || thumbWrap.querySelector('.vid-preview')) return;
                        const videoEl = document.createElement('video');
                        videoEl.className   = 'vid-preview';
                        videoEl.src         = previewUrl;
                        videoEl.muted       = true;
                        videoEl.loop        = true;
                        videoEl.playsInline = true;
                        videoEl.preload     = 'auto';
                        const thumbImg = thumbWrap.querySelector('.vid-thumb');
                        thumbImg ? thumbImg.after(videoEl) : thumbWrap.prepend(videoEl);

                        if (autoPlayAll && autoPlayObserver) {
                            autoPlayObserver.observe(thumbWrap);
                        } else {
                            thumbWrap.addEventListener('mouseenter', () => videoEl.play().catch(() => {}));
                            thumbWrap.addEventListener('mouseleave', () => { videoEl.pause(); videoEl.currentTime = 0; });
                        }
                    });
                }, delay);
                delay += 800;
            });
        }

        const itemEls = [];

        wickedState.sel.forEach((vid, idx) => {
            const item = document.createElement('div');
            item.className = 'video-grid-item';
            item.style.animationDelay = `${Math.min(idx * 0.04, 0.4)}s`;
            itemEls.push(item);

            const thumb     = vid.thumb || '';
            const preview   = vid.preview  || '';
            const dur       = vid.duration || '';
            const rating    = vid.rating   || '';
            const views     = vid.views    || '';
            const ratingNum = parseInt(rating) || 0;

            item.innerHTML = `
                <div class="vid-thumb-wrap">
                    <img class="vid-thumb" src="${thumb}" onerror="this.style.opacity='0.2'">
                    ${preview ? `<video class="vid-preview" src="${preview}" muted loop playsinline></video>` : ''}
                    <div class="vid-play-btn">
                        <svg viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
                            <circle cx="28" cy="28" r="28" fill="rgba(0,0,0,0.5)"/>
                            <polygon points="22,16 42,28 22,40" fill="white"/>
                        </svg>
                    </div>
                    ${dur ? `<span class="vid-duration">${dur}</span>` : ''}
                    ${ratingNum > 0 ? `<div class="vid-rating-bar"><div class="vid-rating-fill" style="width:${ratingNum}%"></div></div>` : ''}
                    <div class="vid-action-btns">
                        <button class="vid-action-btn reload" title="Reset">↻</button>
                        <button class="vid-action-btn remove" title="Remove">✕</button>
                    </div>
                </div>
                <div class="vid-info">
                    <a href="${vid.url}" target="_blank" class="vid-title">${vid.title}</a>
                    <div class="vid-meta">
                        ${dur    ? `<span>${dur}</span>`                           : ''}
                        ${views  ? `<span>${views}</span>`                         : ''}
                        ${rating ? `<span class="vid-meta-rating">★ ${rating}</span>` : ''}
                    </div>
                </div>
            `;
            container.appendChild(item);

            const thumbWrap = item.querySelector('.vid-thumb-wrap');
            const previewEl = item.querySelector('.vid-preview');

            if (previewEl) {
                if (autoPlayAll && autoPlayObserver) {
                    autoPlayObserver.observe(thumbWrap);
                } else {
                    thumbWrap.addEventListener('mouseenter', () => previewEl.play().catch(() => {}));
                    thumbWrap.addEventListener('mouseleave', () => { previewEl.pause(); previewEl.currentTime = 0; });
                }
            }

            thumbWrap.onclick = e => {
                if (e.target.classList.contains('vid-action-btn')) return;
                if (previewEl) { previewEl.pause(); previewEl.currentTime = 0; }
                loadRealPlayer(vid, item);
            };

            item.querySelector('.vid-action-btn.reload').onclick = e => {
                e.stopPropagation();
                const tw = item.querySelector('.vid-thumb-wrap');
                tw.querySelectorAll('.vid-player,.vid-error,.vid-player-loading,iframe').forEach(el => el.remove());
                tw.querySelectorAll('.vid-thumb,.vid-preview,.vid-play-btn,.vid-duration,.vid-rating-bar,.vid-action-btns').forEach(el => el.style.display = '');
                tw.style.cursor = 'pointer';
                tw.classList.remove('autoplay-active');
                const prev = tw.querySelector('.vid-preview');
                if (prev && autoPlayAll && autoPlayObserver) autoPlayObserver.observe(tw);
            };

            item.querySelector('.vid-action-btn.remove').onclick = e => {
                e.stopPropagation();
                if (confirm('Remove this video from selection?')) {
                    const currentIdx = wickedState.sel.findIndex(v => v.url === vid.url);
                    if (currentIdx > -1) wickedState.sel.splice(currentIdx, 1);

                    wickedStore.setSel(wickedState.sel);
                    updateWickedUI();

                    // remove from Grid UI instantly without reloading
                    item.remove();
                    const countNum = document.getElementById('grid-video-count-num');
                    if (countNum) countNum.textContent = wickedState.sel.length;

                    // remove visual selection on main page too
                    const matchingCard = document.querySelector(`.item a[href="${vid.url}"]`)?.closest('.item');
                    if (matchingCard) matchingCard.classList.remove('wicked-selected');
                }
            };
        });

        setTimeout(() => backgroundFetchPreviews(wickedState.sel, itemEls), 1500);
    }

    function loadRealPlayer(vid, item) {
        const thumbWrap = item.querySelector('.vid-thumb-wrap');
        if (thumbWrap.querySelector('.vid-player, iframe')) return;

        const loader = document.createElement('div');
        loader.className = 'vid-player-loading';
        loader.innerHTML = '<span>Loading Secure Player…</span>';
        thumbWrap.appendChild(loader);

        const iframe = document.createElement('iframe');
        iframe.src = vid.url; // Load the actual video page
        // Sandbox prevents frame-busters from redirecting our parent page!
        iframe.sandbox = "allow-scripts allow-same-origin allow-forms allow-popups";
        iframe.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;border:none;background:#000;z-index:5;opacity:0;transition:opacity 0.3s;';
        iframe.allowFullscreen = true;

        iframe.onload = () => {
            try {
                const doc = iframe.contentDocument || iframe.contentWindow.document;

                // 1. Hide ALL top-level elements first (Destroys related videos, headers, comments)
                Array.from(doc.body.children).forEach(c => c.style.setProperty('display', 'none', 'important'));

                // 2. Find the exact native player container using specific IDs
                const player = doc.getElementById('kt_player') || doc.querySelector('.video-holder') || doc.querySelector('.main-player') || doc.querySelector('.player-holder') || doc.querySelector('.video-wrap') || doc.querySelector('video')?.parentElement;

                if (player) {
                    // 3. Make the player absolutely massive and fixed on top
                    player.style.cssText = 'display:block!important;position:fixed!important;top:0!important;left:0!important;width:100vw!important;height:100vh!important;max-width:100vw!important;max-height:100vh!important;z-index:2147483647!important;background:#000!important;margin:0!important;padding:0!important;visibility:visible!important;opacity:1!important;';

                    // 4. Walk back up the DOM tree and unhide ONLY the wrappers needed to reach the body
                    let p = player.parentElement;
                    while (p && p !== doc.body) {
                        p.style.cssText += ';display:block!important;position:static!important;transform:none!important;filter:none!important;opacity:1!important;visibility:visible!important;width:100%!important;height:100%!important;padding:0!important;margin:0!important;';
                        p = p.parentElement;
                    }

                    // 5. Try to auto-play the video
                    setTimeout(() => {
                        const v = player.querySelector('video');
                        if (v) v.play().catch(()=>{});
                    }, 500);
                }

                // 6. Extra CSS backup to prevent scrollbars
                const style = doc.createElement('style');
                style.textContent = 'html,body{overflow:hidden!important;background:#000!important;margin:0!important;padding:0!important}';
                doc.head.appendChild(style);

            } catch(e) {}

            loader.remove();
            iframe.style.opacity = '1';
        };

        // Fallback to remove loader if iframe is slow
        setTimeout(() => { if(loader.parentNode) loader.remove(); iframe.style.opacity = '1'; }, 5000);

        thumbWrap.querySelectorAll('.vid-thumb,.vid-preview,.vid-play-btn').forEach(el => el.style.display = 'none');
        thumbWrap.classList.remove('autoplay-active');
        thumbWrap.style.cursor = 'default';
        thumbWrap.appendChild(iframe);
    }

    document.addEventListener('keydown', e => {
        if (e.ctrlKey || e.metaKey) return;
        const tag = document.activeElement?.tagName;
        if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;
        if (document.activeElement?.isContentEditable) return;
        if ((e.key === 'g' || e.key === 'G') && wickedState.sel.length) { e.preventDefault(); openFullscreenGrid(); }
        if (e.key === 'w' || e.key === 'W') { e.preventDefault(); toggleWickedPanel(); }
        if (e.key === 'Escape') {
            const overlay = document.getElementById('wicked-fullscreen-overlay');
            if (overlay && overlay.style.display === 'block') {
                document.getElementById('close-grid-btn')?.click();
            } else if (wickedState.vis) {
                toggleWickedPanel();
            }
        }
    });

    createLogoBadge();
    createWickedButton();
    createWickedPanel();
    processVideoThumbnails();
    checkShareLink();

    new MutationObserver(() => {
        processVideoThumbnails();
        if (!document.getElementById('wicked-toggle-btn')) createWickedButton();
        if (!document.querySelector('.wicked-logo-badge')) createLogoBadge();
    }).observe(document.body, { childList: true, subtree: true });

})();