Hide Web Elements Pro

Stealth element hider, editable hidden rules, domain & global rule editor, soft snow freeze mode, anti-detection engine, redirect blocker, smart auto-closing menus with mobile keyboard safety.

Versione datata 02/08/2026. Vedi la nuova versione l'ultima versione.

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

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

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         Hide Web Elements Pro
// @version      4.3
// @description  Stealth element hider, editable hidden rules, domain & global rule editor, soft snow freeze mode, anti-detection engine, redirect blocker, smart auto-closing menus with mobile keyboard safety.
// @author       KTZ
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        unsafeWindow
// @run-at       document-start
// @license      MIT
// @namespace    https://greasyfork.org/users/1620673
// ==/UserScript==

(function() {
    'use strict';

    const doc = document, win = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window, gv = GM_getValue, sv = GM_setValue;
    const UI_HOST_ID = 'hider-ui-root', STEPPER_BAR_ID = 'hider-stepper-bar', isTop = (window.self === window.top);
    const by = id => doc.getElementById(id);

    let initialMem = gv('hider_freeze_memory', null) || (gv('hider_autono_global', false) ? 'block_all' : 'ask');
    const CACHE = { 
        blockedDomainsList: [], 
        blockedDomainsSet: new Set(), 
        customRules: [], // [{ id, selector, target }]
        isFrozen: false, 
        freezeMemory: initialMem, 
        logs: null 
    };

    let isSelecting = false, isFrozen = CACHE.isFrozen, currentScope = 'site', previewElement = null;
    let stepperStack = [], userApprovedNavigation = false, cachedCssString = null, lastUrl = location.href;
    let stepperPos = { x: null, y: null }, isDraggingStepper = false, isDraggingDock = false, logSaveTimer = null;
    let collapseTimer = null, editingRuleId = null;

    const cleanUrl = () => location.origin + location.pathname;
    const cleanDomain = url => {
        try { 
            if (!url) return '';
            if (url === '*') return '*';
            return new URL(url.includes('://') ? url : 'http://' + url).hostname.replace(/^www\./, ''); 
        } catch { 
            return url.trim().toLowerCase(); 
        }
    };

    function syncCache() {
        CACHE.blockedDomainsList = gv('hider_blocked_domains', []);
        CACHE.blockedDomainsSet = new Set(CACHE.blockedDomainsList.map(cleanDomain).filter(Boolean));
        CACHE.customRules = gv('hider_custom_rules_v4', []);
        CACHE.isFrozen = gv('hider_freeze_global', false);
        CACHE.freezeMemory = gv('hider_freeze_memory', CACHE.freezeMemory);
        if (gv('hider_last_log_clear_day', '') !== new Date().toDateString()) {
            CACHE.logs = []; sv('hider_global_logs', []); sv('hider_last_log_clear_day', new Date().toDateString());
        }
    }
    syncCache();

    // Stealth Engine: Anti-Anti-Adblock & Undetectable Element Concealment
    try {
        const origGetComputedStyle = win.getComputedStyle;
        win.getComputedStyle = function(el, pseudo) {
            const style = origGetComputedStyle.apply(this, arguments);
            if (el && el.nodeType === 1 && el.classList.contains('hider-stealth-target')) {
                return new Proxy(style, {
                    get(target, prop) {
                        if (prop === 'display') return 'block';
                        if (prop === 'visibility') return 'visible';
                        if (prop === 'opacity') return '1';
                        if (prop === 'pointerEvents') return 'auto';
                        const val = target[prop];
                        return typeof val === 'function' ? val.bind(target) : val;
                    }
                });
            }
            return style;
        };
    } catch {}

    const isSameDomain = u => cleanDomain(u) && cleanDomain(u) === cleanDomain(location.href);
    const isDomainBlocked = u => {
        if (!u || !CACHE.blockedDomainsSet.size) return false;
        const d = cleanDomain(u), lower = u.toLowerCase();
        return d && (CACHE.blockedDomainsSet.has(d) || [...CACHE.blockedDomainsSet].some(b => b !== '*' && (d.endsWith('.' + b) || lower.includes(b))));
    };

    function convertToWildcardSelector(sel) {
        return (sel || '').replace(/(#|\.)([a-zA-Z0-9_-]+)/g, (m, p, name) => {
            if (name.startsWith('hider-')) return '';
            let base = (name.match(/^([a-zA-Z_-]+?)[0-9a-fA-F_-]{3,}$/)?.[1] || name.replace(/[0-9a-fA-F]{4,}$/, '')).replace(/[-_]+$/, '');
            return p === '#' ? `[id*="${base || name}"]` : `[class*="${base || name}"]`;
        }).replace(/\s*>\s*/g, ' ');
    }

    function logBlockedAttempt(url, triggerType) {
        if (!CACHE.logs) CACHE.logs = gv('hider_global_logs', []);
        CACHE.logs.unshift({ url: url || 'about:blank', time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }), type: triggerType || 'Popup' });
        if (CACHE.logs.length > 50) CACHE.logs.pop();
        if (!logSaveTimer) logSaveTimer = setTimeout(() => { logSaveTimer = null; sv('hider_global_logs', CACHE.logs); }, 1000);
    }

    // Soft Frosted Snow Animation
    function triggerFreezeFx() {
        const fx = doc.createElement('div');
        fx.className = 'hider-freeze-snow-overlay';
        for (let i = 0; i < 14; i++) {
            const flake = doc.createElement('div');
            flake.className = 'hider-snow-flake';
            flake.style.left = `${Math.random() * 100}vw`;
            flake.style.top = `${Math.random() * 70}vh`;
            flake.style.animationDelay = `${Math.random() * 0.3}s`;
            flake.style.transform = `scale(${0.4 + Math.random() * 0.8})`;
            fx.appendChild(flake);
        }
        (doc.body || doc.documentElement).appendChild(fx);
        setTimeout(() => fx.remove(), 1300);
    }

    function forceExposeAndPlayVideos() {
        doc.querySelectorAll('video').forEach(v => {
            ['display', 'visibility', 'opacity', 'pointer-events'].forEach(p => v.style.setProperty(p, p === 'display' ? 'block' : p === 'pointer-events' ? 'auto' : '1', 'important'));
            v.play()?.catch?.(() => {});
        });
    }

    function triggerVideoResumeChain() {
        forceExposeAndPlayVideos(); [150, 400, 800].forEach(t => setTimeout(forceExposeAndPlayVideos, t));
        broadcastToFrames(window, { type: 'HIDER_RESUME_VIDEOS' });
    }

    function simulateAdWindowSuccess() {
        try {
            win.dispatchEvent(new Event('blur')); doc.dispatchEvent(new Event('visibilitychange'));
            win.onblur?.();
            setTimeout(() => { win.dispatchEvent(new Event('focus')); win.onfocus?.(); }, 60);
            triggerVideoResumeChain();
        } catch {}
    }

    function createDummyWindow() {
        const dummy = {
            closed: false, focus(){}, blur(){}, close(){ this.closed = true; }, postMessage(){},
            location: { href: 'about:blank', replace(u){ if(isDomainBlocked(u)) logBlockedAttempt(u, 'Blocked Dummy Replace'); }, assign(u){ if(isDomainBlocked(u)) logBlockedAttempt(u, 'Blocked Dummy Assign'); } },
            document: { write(){}, close(){}, body:{} }, opener: win
        };
        return new Proxy(dummy, {
            get: (t, p) => p in t ? t[p] : () => {},
            set: (t, p, v) => { if (p === 'location' && isDomainBlocked(v)) logBlockedAttempt(v, 'Blocked Dummy Location Set'); else t[p] = v; return true; }
        });
    }

    function handleFreezeNavigation(url, triggerType, onConfirm, onDeny) {
        const mem = CACHE.freezeMemory || 'ask';
        if (mem === 'block_all') { logBlockedAttempt(url, triggerType + ' (Auto-Block)'); onDeny?.(); simulateAdWindowSuccess(); return; }
        if (mem === 'allow_all' || (mem === 'allow_same' && isSameDomain(url))) {
            userApprovedNavigation = true; onConfirm?.(); setTimeout(() => userApprovedNavigation = false, 300); return;
        }
        showFreezePrompt(url, triggerType, onConfirm, onDeny);
    }

    // Interceptors
    try {
        const lp = win.Location ? win.Location.prototype : Object.getPrototypeOf(win.location);
        if (lp) {
            const origA = lp.assign, origR = lp.replace, hrefDesc = Object.getOwnPropertyDescriptor(lp, 'href');
            lp.assign = function(u) { if (isDomainBlocked(u)) { logBlockedAttempt(u, 'Blocked Redirect'); showToast('⛔ Blocked redirect'); simulateAdWindowSuccess(); return; } return origA.apply(this, arguments); };
            lp.replace = function(u) { if (isDomainBlocked(u)) { logBlockedAttempt(u, 'Blocked Redirect'); showToast('⛔ Blocked redirect'); simulateAdWindowSuccess(); return; } return origR.apply(this, arguments); };
            if (hrefDesc?.set) {
                Object.defineProperty(lp, 'href', { set(u) { if (isDomainBlocked(u)) { logBlockedAttempt(u, 'Blocked Redirect'); showToast('⛔ Blocked redirect'); simulateAdWindowSuccess(); return; } hrefDesc.set.call(this, u); } });
            }
        }
        HTMLAnchorElement.prototype.click = function() { if (isDomainBlocked(this.href)) { logBlockedAttempt(this.href, 'Blocked Click'); showToast('⛔ Blocked link click'); simulateAdWindowSuccess(); return; } return HTMLElement.prototype.click.apply(this, arguments); };
    } catch {}

    try {
        const origOpen = win.open;
        win.open = function(url, target, features) {
            if (isDomainBlocked(url)) { logBlockedAttempt(url || 'about:blank', 'Blocked Popup'); simulateAdWindowSuccess(); showToast('⛔ Blocked Popup'); return createDummyWindow(); }
            if (!url || url === 'about:blank') {
                const realWin = origOpen.apply(win, arguments);
                return realWin ? new Proxy(realWin, {
                    get: (t, p) => p === 'location' ? new Proxy(t.location, { set: (l, lp, lv) => (lp==='href'||lp==='assign') && isDomainBlocked(lv) ? (logBlockedAttempt(lv, 'Blocked Popup Nav'), showToast('⛔ Blocked popup nav'), t.close(), true) : (l[lp]=lv, true) }) : (typeof t[p]==='function'?t[p].bind(t):t[p]),
                    set: (t, p, v) => p === 'location' && isDomainBlocked(v) ? (logBlockedAttempt(v, 'Blocked Popup Location'), showToast('⛔ Blocked popup nav'), t.close(), true) : (t[p]=v, true)
                }) : createDummyWindow();
            }
            if (isFrozen && !userApprovedNavigation) { handleFreezeNavigation(url, 'win.open()', () => origOpen.call(win, url, target, features), () => {}); return createDummyWindow(); }
            return origOpen.apply(win, arguments);
        };
    } catch {}

    function broadcastToFrames(w, msg) { try { for (let i = 0; i < w.frames.length; i++) { w.frames[i].postMessage(msg, '*'); broadcastToFrames(w.frames[i], msg); } } catch {} }
    function broadcastState() { if (isTop) broadcastToFrames(window, { type: 'HIDER_SYNC_STATE', isSelecting, currentScope, isFrozen }); }

    window.addEventListener('message', e => {
        if (!e.data) return;
        if (e.data.type === 'HIDER_REQUEST_STATE' && isTop) broadcastState();
        if (e.data.type === 'HIDER_SYNC_STATE') {
            ({ isSelecting, currentScope, isFrozen } = e.data);
            by('btn-select')?.classList.toggle('active', isSelecting);
            by('btn-scope')?.classList.toggle('active', currentScope === 'link');
            by('btn-freeze')?.classList.toggle('active', isFrozen);
            if (!isSelecting) clearSelectionState();
        }
        if (e.data.type === 'HIDER_RESUME_VIDEOS') triggerVideoResumeChain();
    });
    if (!isTop) try { window.top.postMessage({ type: 'HIDER_REQUEST_STATE' }, '*'); } catch {}

    function updateStyles() {
        let el = by('hider-dynamic-styles') || Object.assign(doc.createElement('style'), { id: 'hider-dynamic-styles' });
        if (!el.parentNode) (doc.head || doc.documentElement).appendChild(el);
        
        const host = location.hostname;
        const siteRules = gv('hider_site_' + host, []);
        const linkRules = gv('hider_link_' + cleanUrl(), []);
        
        const customActiveRules = (CACHE.customRules || []).filter(r => {
            if (!r || !r.selector) return false;
            const t = cleanDomain(r.target);
            return t === '*' || t === host || (t && host.endsWith('.' + t));
        }).map(r => r.selector);

        const combined = [...new Set([...siteRules, ...linkRules, ...customActiveRules])].filter(Boolean);
        
        // Stealth CSS: Layout collapsing off-screen
        const css = combined.map(s => `${s}{opacity:0!important;pointer-events:none!important;position:absolute!important;top:-99999px!important;left:-99999px!important;width:0!important;height:0!important;max-width:0!important;max-height:0!important;overflow:hidden!important;visibility:hidden!important;clip:rect(0,0,0,0)!important}`).join('\n');
        
        if (cachedCssString !== css) { 
            cachedCssString = css; 
            el.textContent = css; 
            
            combined.forEach(s => {
                try {
                    doc.querySelectorAll(s).forEach(targetEl => targetEl.classList.add('hider-stealth-target'));
                } catch {}
            });
        }
    }
    updateStyles();

    function getExactSelector(el) {
        if (!el || el.nodeType !== 1) return '';
        const path = []; let curr = el;
        while (curr && curr.nodeType === 1 && curr.tagName.toLowerCase() !== 'html') {
            let tag = curr.tagName.toLowerCase();
            if (tag === 'body') { path.unshift('body'); break; }
            if (curr.id && !curr.id.startsWith('hider-') && !/^\d+$/.test(curr.id) && curr.id.length < 15) { path.unshift(`#${CSS.escape(curr.id)}`); break; }
            let rawC = typeof curr.className === 'string' ? curr.className : curr.getAttribute('class') || '';
            let classes = rawC.trim().split(/\s+/).filter(c => c.length > 1 && !c.startsWith('hider-') && !/^[a-zA-Z0-9]{10,}$/.test(c) && !/^hover:/i.test(c));
            if (classes.length) { tag += `.${CSS.escape(classes[0])}` + (classes[1] ? `.${CSS.escape(classes[1])}` : ''); }
            else {
                let idx = 1, sib = curr.previousElementSibling;
                while (sib) { if (sib.tagName === curr.tagName) idx++; sib = sib.previousElementSibling; }
                tag += `:nth-of-type(${idx})`;
            }
            path.unshift(tag); curr = curr.parentElement;
        }
        return path.join(' > ');
    }

    function attachDragListeners(barEl, handleEl) {
        let startX, startY, initialX, initialY;
        const onStart = e => {
            if (e.target.tagName === 'BUTTON') return;
            isDraggingStepper = true;
            const p = e.touches ? e.touches[0] : e, r = barEl.getBoundingClientRect();
            startX = p.clientX; startY = p.clientY; initialX = r.left; initialY = r.top;
            barEl.style.transition = 'none';
        };
        const onMove = e => {
            if (!isDraggingStepper) return; e.cancelable && e.preventDefault();
            const p = e.touches ? e.touches[0] : e;
            stepperPos = { x: initialX + (p.clientX - startX), y: initialY + (p.clientY - startY) };
            Object.assign(barEl.style, { left: `${stepperPos.x}px`, top: `${stepperPos.y}px`, bottom: 'auto', transform: 'none' });
        };
        const onEnd = () => { if (isDraggingStepper) { isDraggingStepper = false; barEl.style.transition = 'all 0.2s cubic-bezier(0.16, 1, 0.3, 1)'; } };
        handleEl.addEventListener('mousedown', onStart); window.addEventListener('mousemove', onMove); window.addEventListener('mouseup', onEnd);
        handleEl.addEventListener('touchstart', onStart, { passive: false }); window.addEventListener('touchmove', onMove, { passive: false }); window.addEventListener('touchend', onEnd);
    }

    function clearSelectionState() {
        previewElement?.classList.remove('hider-preview-highlight'); previewElement = null; stepperStack = [];
        by(STEPPER_BAR_ID)?.remove();
    }

    function turnOffHideMode() {
        if (isSelecting) {
            isSelecting = false;
            by('btn-select')?.classList.remove('active');
            clearSelectionState();
            broadcastState();
        }
    }

    // Smart Auto-Close menus function with Mobile Keyboard & Editing Safety Guards (v4.3)
    function closeAllMenus(e) {
        if (isSelecting) return; // Do not auto-close if Hide Mode is active
        if (isDraggingDock || isDraggingStepper) return; // Ignore while dragging UI

        // Safety 1: Mobile Soft Keyboard / Input Focus Protection
        const active = doc.activeElement;
        if (active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' || active.tagName === 'SELECT')) {
            if (active.closest('#' + UI_HOST_ID + ', #' + STEPPER_BAR_ID)) return;
        }

        // Safety 2: Scrolling or clicking inside the script UI elements
        if (e && e.target && e.target.closest) {
            if (e.target.closest('#' + UI_HOST_ID + ', #' + STEPPER_BAR_ID)) return;
        }

        // Safety 3: Inline Rule Editing Safety Guard
        if (by('hider-panel')?.querySelector('.is-editing')) return;

        const panel = by('hider-panel');
        const menu = by('hider-dock-menu');
        const mainBtn = by('btn-toggle-dock');
        const dockEl = by('hider-main-dock');

        if (panel && panel.style.display === 'flex') {
            panel.style.display = 'none';
            by('btn-manage')?.classList.remove('active');
        }

        if (menu && menu.style.display === 'flex') {
            menu.style.display = 'none';
            mainBtn?.classList.remove('expanded');
            dockEl?.classList.remove('expanded');
            dockEl?.classList.add('manual-hidden');
            if (collapseTimer) clearTimeout(collapseTimer);
            collapseTimer = setTimeout(() => {
                dockEl?.classList.add('is-collapsed');
                collapseTimer = null;
            }, 1000);
        }
    }

    function renderTouchStepperUI() {
        let stepper = by(STEPPER_BAR_ID);
        if (!previewElement) { stepper?.remove(); return; }
        if (!stepper) { stepper = doc.createElement('div'); stepper.id = STEPPER_BAR_ID; (doc.body || doc.documentElement).appendChild(stepper); }

        stepper.className = 'h-glass h-stepper-pill';
        if (stepperPos.x !== null) Object.assign(stepper.style, { left: `${stepperPos.x}px`, top: `${stepperPos.y}px`, bottom: 'auto', transform: 'none' });

        const tag = previewElement.tagName.toLowerCase();
        const c = typeof previewElement.className === 'string' ? previewElement.className.trim().split(/\s+/)[0] : '';
        const classStr = c && !c.startsWith('hider-') ? `.${c}` : '';
        const idStr = previewElement.id && !previewElement.id.startsWith('hider-') ? `#${previewElement.id}` : '';

        stepper.innerHTML = `<div id="hider-drag-handle" class="h-drag-dots" title="Drag">⋮⋮</div><button id="hider-step-up" class="h-btn-icon" title="Parent Level">▲</button><button id="hider-step-down" class="h-btn-icon" title="Child Level">▼</button><div class="h-tag-badge" title="${tag}${idStr}${classStr}">${tag}${idStr}${classStr}</div><button id="hider-step-confirm" class="h-btn-pill h-btn-save">Hide</button><button id="hider-step-cancel" class="h-btn-icon h-btn-close">✖</button>`;

        attachDragListeners(stepper, stepper.querySelector('#hider-drag-handle'));

        stepper.querySelector('#hider-step-up').onclick = e => {
            e.stopPropagation(); const p = previewElement.parentElement;
            if (p && p !== doc.body && p !== doc.documentElement && p.id !== UI_HOST_ID) {
                previewElement.classList.remove('hider-preview-highlight'); stepperStack.push(previewElement);
                previewElement = p; previewElement.classList.add('hider-preview-highlight'); renderTouchStepperUI();
            } else showToast('Top parent reached');
        };

        stepper.querySelector('#hider-step-down').onclick = e => {
            e.stopPropagation();
            if (stepperStack.length || previewElement.firstElementChild) {
                previewElement.classList.remove('hider-preview-highlight');
                previewElement = stepperStack.length ? stepperStack.pop() : previewElement.firstElementChild;
                previewElement.classList.add('hider-preview-highlight'); renderTouchStepperUI();
            }
        };

        stepper.querySelector('#hider-step-confirm').onclick = e => { e.stopPropagation(); confirmHideSelectedElement(); };
        stepper.querySelector('#hider-step-cancel').onclick = e => { e.stopPropagation(); clearSelectionState(); };
    }

    function confirmHideSelectedElement() {
        if (!previewElement) return;
        const el = previewElement;
        el.classList.remove('hider-preview-highlight'); el.classList.add('hider-stealth-target');
        const sel = getExactSelector(el);
        if (sel) {
            const key = currentScope === 'site' ? 'hider_site_' + location.hostname : 'hider_link_' + cleanUrl();
            const s = gv(key, []); if (!s.includes(sel)) { s.push(sel); sv(key, s); }
        }
        updateStyles(); showToast('🙈 Element Hidden!'); clearSelectionState();
    }

    function showToast(msg) {
        by('hider-toast')?.remove();
        const toast = Object.assign(doc.createElement('div'), { id: 'hider-toast', className: 'h-glass h-toast', textContent: msg });
        (doc.body || doc.documentElement).appendChild(toast);
        setTimeout(() => { toast.style.opacity = '0'; toast.style.transform = 'translateX(-50%) translateY(8px)'; setTimeout(() => toast.remove(), 250); }, 1800);
    }

    function showFreezePrompt(url, triggerType, onConfirm, onDeny) {
        by('hider-freeze-prompt')?.remove();
        const promptEl = Object.assign(doc.createElement('div'), { id: 'hider-freeze-prompt', className: 'h-glass h-prompt' });
        const displayUrl = url ? (url.length > 38 ? url.substring(0, 35) + '...' : url) : 'another page';

        promptEl.innerHTML = `<div style="font-weight:700;color:#38bdf8;font-size:11px;text-transform:uppercase">❄️ Soft Freeze Triggered</div><div style="font-size:12px;color:#f8fafc">Proceed to page?</div><div class="h-url-box">${displayUrl}</div><select id="hider-freeze-modal-memory" class="h-select"><option value="ask" ${CACHE.freezeMemory==='ask'?'selected':''}>Ask Every Time</option><option value="block_all" ${CACHE.freezeMemory==='block_all'?'selected':''}>Auto-Block All</option><option value="allow_same" ${CACHE.freezeMemory==='allow_same'?'selected':''}>Allow Same Domain</option><option value="allow_all" ${CACHE.freezeMemory==='allow_all'?'selected':''}>Allow All Navigations</option></select><div style="display:flex;gap:6px;width:100%"><button id="hider-freeze-yes" class="h-btn-pill h-btn-save" style="flex:1">Yes</button><button id="hider-freeze-no" class="h-btn-pill h-btn-close" style="flex:1">No</button></div>`;

        (doc.body || doc.documentElement).appendChild(promptEl);
        const saveMem = () => { const m = by('hider-freeze-modal-memory'); if (m && m.value !== CACHE.freezeMemory) { CACHE.freezeMemory = m.value; sv('hider_freeze_memory', m.value); } };
        promptEl.querySelector('#hider-freeze-yes').onclick = () => { saveMem(); promptEl.remove(); onConfirm?.(); };
        promptEl.querySelector('#hider-freeze-no').onclick = () => { saveMem(); logBlockedAttempt(url, triggerType + ' (User)'); promptEl.remove(); onDeny?.(); simulateAdWindowSuccess(); };
    }

    function injectStylesAndUI() {
        if (by('hider-core-styles')) return;
        const s = doc.createElement('style'); s.id = 'hider-core-styles';
        s.textContent = `
        .h-glass{background:rgba(15,23,42,.92);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid rgba(255,255,255,.12);color:#f8fafc;font-family:system-ui,-apple-system,sans-serif;box-sizing:border-box;border-radius:12px}
        .h-dock{position:fixed;right:0;top:35%;z-index:2147483646;display:flex;flex-direction:column;align-items:flex-end;gap:5px;padding:4px;box-shadow:0 10px 25px rgba(0,0,0,.5);pointer-events:auto;transition:transform .25s ease,opacity .25s ease;will-change:transform,opacity}
        .h-dock.is-collapsed{transform:translateX(50%);opacity:.4}
        .h-dock.manual-hidden{transform:translateX(50%!important);opacity:.4!important}
        .h-dock.is-collapsed:not(.manual-hidden):hover,.h-dock.expanded{transform:translateX(0);opacity:1}
        .h-dock-main{width:36px;height:36px;font-size:16px!important;background:rgba(59,130,246,.3)!important;border-color:rgba(96,165,250,.5)!important;cursor:grab!important}
        .h-dock-main:active{cursor:grabbing!important}
        .h-dock-main.expanded{background:#3b82f6!important;color:#fff!important}
        .h-dock-menu{display:none;flex-direction:column;gap:5px;margin-top:2px}
        .h-dock-btn{width:36px;height:36px;border-radius:9px;border:1px solid rgba(255,255,255,.08);background:rgba(255,255,255,.05);color:#cbd5e1;font-size:10px;font-weight:700;display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:pointer;transition:all .15s ease}
        .h-dock-btn:hover{background:rgba(255,255,255,.15);color:#fff}
        .h-dock-btn.active{background:#3b82f6!important;color:#fff!important;border-color:#60a5fa!important}
        .h-dock-btn.scope-link{background:#10b981!important;color:#fff!important}
        .h-dock-btn.is-frozen{background:#8b5cf6!important;color:#fff!important}
        .h-stepper-pill{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);z-index:2147483647;padding:5px 8px;display:flex;align-items:center;gap:6px;box-shadow:0 10px 30px rgba(0,0,0,.6);pointer-events:auto}
        .h-drag-dots{cursor:grab;color:#64748b;font-size:12px;padding:0 3px;user-select:none}
        .h-btn-icon{width:28px;height:28px;border-radius:7px;border:1px solid rgba(255,255,255,.1);background:rgba(255,255,255,.06);color:#e2e8f0;font-size:11px;font-weight:700;cursor:pointer;display:flex;align-items:center;justify-content:center}
        .h-btn-icon:hover{background:rgba(255,255,255,.18)}
        .h-tag-badge{max-width:130px;height:26px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background:rgba(0,0,0,.4);border:1px solid rgba(255,255,255,.08);border-radius:6px;padding:0 6px;color:#38bdf8;font-size:10px;font-family:monospace;line-height:24px}
        .h-btn-pill{height:28px;padding:0 10px;border-radius:7px;font-size:11px;font-weight:700;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center}
        .h-btn-save{background:#10b981;color:#fff}
        .h-btn-close{background:rgba(239,68,68,.2);border:1px solid rgba(239,68,68,.4);color:#f87171}
        .h-toast{position:fixed;bottom:70px;left:50%;transform:translateX(-50%);padding:6px 16px;font-size:11px;font-weight:600;z-index:2147483647;pointer-events:none;transition:all .25s ease}
        .h-prompt{position:fixed;top:30px;left:50%;transform:translateX(-50%);padding:14px 16px;z-index:2147483647;display:flex;flex-direction:column;align-items:center;gap:8px;box-shadow:0 15px 35px rgba(0,0,0,.6);pointer-events:auto;width:300px;max-width:90vw}
        .h-url-box{font-size:10px;color:#9ca3af;word-break:break-all;max-height:32px;overflow:hidden;background:rgba(0,0,0,.3);padding:4px 6px;border-radius:6px;width:100%;border:1px solid rgba(255,255,255,.05)}
        .h-select{width:100%;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.15);border-radius:6px;color:#fff;padding:5px 8px;font-size:10px;outline:0;box-sizing:border-box}
        #hider-panel{position:fixed;right:50px;top:75px;z-index:2147483647;width:330px;max-height:80vh;border-radius:14px;padding:12px;display:none;flex-direction:column;overflow-y:auto;pointer-events:auto;box-shadow:0 15px 35px rgba(0,0,0,.6);gap:8px}
        .panel-header{font-weight:700;font-size:12px;border-bottom:1px solid rgba(255,255,255,.1);padding-bottom:6px;display:flex;justify-content:space-between;align-items:center}
        .section-title{font-size:9px;color:#94a3b8;font-weight:700;text-transform:uppercase;letter-spacing:.5px;display:flex;justify-content:space-between;align-items:center;margin-top:6px}
        .list-item{font-size:10px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.06);border-radius:6px;padding:5px 7px;display:flex;justify-content:space-between;align-items:center;gap:6px}
        .list-item span.rule-text{word-break:break-all;padding-right:4px;color:#cbd5e1;font-family:monospace;font-size:9px;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
        .hider-btn-small{cursor:pointer;border:none;border-radius:4px;padding:2px 6px;color:#fff;font-weight:600;background:#ef4444;font-size:9px}
        @keyframes hider-pulse{0%,100%{outline:2px stroke #ef4444}50%{outline:2px dashed #ef4444;box-shadow:inset 0 0 10px rgba(239,68,68,.3)}}
        .hider-preview-highlight{animation:hider-pulse .8s infinite!important;cursor:pointer!important}
        
        /* Soft Frosted Snow Animation */
        @keyframes hider-snow-freeze {
            0% { opacity: 0; backdrop-filter: blur(0px); background: rgba(224, 242, 254, 0); }
            35% { opacity: 1; backdrop-filter: blur(8px) saturate(130%); background: radial-gradient(circle at center, rgba(186, 230, 253, 0.22) 0%, rgba(56, 189, 248, 0.12) 60%, rgba(15, 23, 42, 0.35) 100%); box-shadow: inset 0 0 120px rgba(186, 230, 253, 0.35); }
            100% { opacity: 0; backdrop-filter: blur(0px); background: rgba(224, 242, 254, 0); }
        }
        @keyframes hider-flake-drift {
            0% { transform: translateY(0) scale(0.6); opacity: 0; }
            40% { opacity: 0.95; }
            100% { transform: translateY(40px) scale(1.1); opacity: 0; }
        }
        .hider-freeze-snow-overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 2147483647; pointer-events: none; animation: hider-snow-freeze 1.3s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
        .hider-snow-flake { position: absolute; width: 6px; height: 6px; background: #e0f2fe; border-radius: 50%; box-shadow: 0 0 8px #38bdf8, 0 0 12px #ffffff; animation: hider-flake-drift 1.2s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
        `;
        (doc.head || doc.documentElement).appendChild(s);

        if (!isTop || by(UI_HOST_ID)) return;
        const host = doc.createElement('div'); host.id = UI_HOST_ID;
        host.style.cssText = 'position:fixed;top:0;right:0;bottom:0;width:0;z-index:2147483647;pointer-events:none;';
        (doc.body || doc.documentElement).appendChild(host);

        host.innerHTML = `
        <div class="h-glass h-dock is-collapsed manual-hidden" id="hider-main-dock">
            <button class="h-dock-btn h-dock-main" id="btn-toggle-dock" title="Toggle Hider Menu">🛡️</button>
            <div class="h-dock-menu" id="hider-dock-menu">
                <button class="h-dock-btn" id="btn-select" title="Target Element to Hide">🎯<span>Hide</span></button>
                <button class="h-dock-btn" id="btn-scope" title="Toggle Scope (Site/Page)">🌐<span>SITE</span></button>
                <button class="h-dock-btn ${isFrozen?'is-frozen':''}" id="btn-freeze" title="Toggle Navigation Freeze">❄️<span>Freeze</span></button>
                <button class="h-dock-btn" id="btn-manage" title="Open Control Panel">⚙️<span>Menu</span></button>
            </div>
        </div>
        <div id="hider-panel" class="h-glass">
            <div class="panel-header"><span>🛡️ Hide Web Elements Pro <span style="font-size:9px;color:#60a5fa">v4.3</span></span><button class="hider-btn-small" id="close-p" style="background:rgba(255,255,255,0.1)">✖</button></div>
            
            <!-- Manual Rule Builder -->
            <div class="section-title">Manual Rule Builder</div>
            <div style="display:flex;flex-direction:column;gap:4px">
                <input type="text" id="manual-rule-input" placeholder="Rule CSS Selector (e.g. .ad-banner)" class="h-select">
                <div style="display:flex;gap:4px">
                    <input type="text" id="manual-target-input" placeholder="Target Domain (* for Global)" class="h-select" style="flex:1">
                    <button class="hider-btn-small" id="save-rule-btn" style="background:#3b82f6;padding:4px 10px">Save</button>
                </div>
            </div>

            <!-- Custom Editable Rules -->
            <div class="section-title">Custom Rules</div>
            <div id="list-custom-rules"></div>

            <!-- Domain Blacklist -->
            <div class="section-title">Block Domain</div>
            <div style="display:flex;gap:4px">
                <input type="text" id="manual-domain-input" placeholder="e.g. badsite.com" class="h-select" style="flex:1">
                <button class="hider-btn-small" id="add-domain-btn" style="background:#3b82f6">Add</button>
            </div>
            <div class="section-title">Blocked Domains</div>
            <div id="list-blocked-domains"></div>

            <div class="section-title">Freeze Mode Behavior</div>
            <select id="freeze-memory-select" class="h-select">
                <option value="ask">Ask Every Time</option>
                <option value="block_all">Auto-Block All</option>
                <option value="allow_same">Allow Same Domain</option>
                <option value="allow_all">Allow All Navigations</option>
            </select>

            <div class="section-title"><span>Blocked Log</span><button class="hider-btn-small" id="clear-log-btn" style="background:#475569">Clear</button></div>
            <div id="list-blocked-log" style="max-height:90px;overflow-y:auto;background:rgba(0,0,0,.25);border-radius:6px;padding:4px"></div>

            <div class="section-title">Hidden Site-Wide (${location.hostname})</div>
            <div id="list-site"></div>
            <div class="section-title">Hidden This Page Only</div>
            <div id="list-link"></div>
        </div>`;

        const dockEl = by('hider-main-dock'), mainBtn = by('btn-toggle-dock');
        
        // Restore saved FAB dock Y position
        const savedY = gv('hider_dock_y', null);
        if (savedY) dockEl.style.top = savedY;

        dockEl.addEventListener('mouseleave', () => dockEl.classList.remove('manual-hidden'));

        // Draggable FAB Dock Logic
        let dockStartY, dockInitY, dockMoved = false;

        const onDockStart = e => {
            const p = e.touches ? e.touches[0] : e;
            dockStartY = p.clientY; dockInitY = dockEl.getBoundingClientRect().top;
            isDraggingDock = true; dockMoved = false;
            dockEl.style.transition = 'none';
        };
        const onDockMove = e => {
            if (!isDraggingDock) return;
            const p = e.touches ? e.touches[0] : e;
            const dy = p.clientY - dockStartY;
            if (Math.abs(dy) > 4) dockMoved = true;
            if (dockMoved) {
                e.cancelable && e.preventDefault();
                dockEl.style.top = `${dockInitY + dy}px`;
            }
        };
        const onDockEnd = () => {
            if (isDraggingDock) {
                isDraggingDock = false;
                dockEl.style.transition = '';
                if (dockMoved) sv('hider_dock_y', dockEl.style.top);
            }
        };

        mainBtn.addEventListener('mousedown', onDockStart);
        window.addEventListener('mousemove', onDockMove, { passive: true });
        window.addEventListener('mouseup', onDockEnd);
        mainBtn.addEventListener('touchstart', onDockStart, { passive: false });
        window.addEventListener('touchmove', onDockMove, { passive: false });
        window.addEventListener('touchend', onDockEnd);

        mainBtn.onclick = e => {
            e.stopPropagation();
            if (dockMoved) return;
            
            if (collapseTimer) { clearTimeout(collapseTimer); collapseTimer = null; }

            const menu = by('hider-dock-menu');
            const isOpening = menu.style.display !== 'flex';
            menu.style.display = isOpening ? 'flex' : 'none';
            mainBtn.classList.toggle('expanded', isOpening);
            dockEl.classList.toggle('expanded', isOpening);
            dockEl.classList.toggle('is-collapsed', !isOpening);

            if (!isOpening) {
                turnOffHideMode();
                by('hider-panel').style.display = 'none';
                by('btn-manage')?.classList.remove('active');

                dockEl.classList.add('manual-hidden');
                collapseTimer = setTimeout(() => {
                    dockEl.classList.add('is-collapsed');
                    collapseTimer = null;
                }, 1000);
            } else {
                dockEl.classList.remove('manual-hidden');
            }
        };

        // Manual Rule Builder Handler
        by('save-rule-btn').onclick = () => {
            const ruleInput = by('manual-rule-input');
            const targetInput = by('manual-target-input');
            const sel = (ruleInput.value || '').trim();
            let target = cleanDomain(targetInput.value) || '*';

            if (!sel) { showToast('⚠️ Enter a selector'); return; }

            if (editingRuleId) {
                const idx = CACHE.customRules.findIndex(r => r.id === editingRuleId);
                if (idx !== -1) {
                    CACHE.customRules[idx] = { id: editingRuleId, selector: sel, target };
                    showToast('✏️ Rule updated');
                }
                editingRuleId = null;
                by('save-rule-btn').textContent = 'Save';
            } else {
                const newRule = { id: 'rule_' + Date.now(), selector: sel, target };
                CACHE.customRules.push(newRule);
                showToast('✨ Custom Rule Added!');
            }

            sv('hider_custom_rules_v4', CACHE.customRules);
            ruleInput.value = ''; targetInput.value = '';
            updateStyles(); renderList();
        };

        const addDomain = () => {
            const input = by('manual-domain-input'), d = cleanDomain(input.value);
            if (d && !CACHE.blockedDomainsSet.has(d)) {
                CACHE.blockedDomainsList.push(d); CACHE.blockedDomainsSet.add(d);
                sv('hider_blocked_domains', CACHE.blockedDomainsList); showToast(`🚫 Blocked: ${d}`);
            }
            input.value = ''; renderList();
        };

        by('add-domain-btn').onclick = addDomain;
        by('manual-domain-input').onkeypress = e => e.key === 'Enter' && addDomain();

        by('btn-select').onclick = e => {
            isSelecting = !isSelecting;
            e.currentTarget.classList.toggle('active', isSelecting);
            if (isSelecting) { by('hider-panel').style.display = 'none'; by('btn-manage').classList.remove('active'); }
            else clearSelectionState();
            broadcastState();
        };

        const btnScope = by('btn-scope');
        btnScope.onclick = () => {
            currentScope = currentScope === 'site' ? 'link' : 'site';
            btnScope.querySelector('span').textContent = currentScope.toUpperCase();
            btnScope.classList.toggle('scope-link', currentScope === 'link');
            showToast(`Scope: ${currentScope.toUpperCase()}`); broadcastState();
        };

        const btnFreeze = by('btn-freeze');
        btnFreeze.onclick = () => {
            isFrozen = !isFrozen; CACHE.isFrozen = isFrozen; sv('hider_freeze_global', isFrozen);
            btnFreeze.classList.toggle('is-frozen', isFrozen);
            if (isFrozen) triggerFreezeFx();
            showToast(isFrozen ? '❄️ Freeze ON' : '🔥 Freeze OFF'); broadcastState();
        };

        by('btn-manage').onclick = e => {
            const p = by('hider-panel'), show = p.style.display !== 'flex';
            p.style.display = show ? 'flex' : 'none';
            e.currentTarget.classList.toggle('active', show);
            if (show) { turnOffHideMode(); renderList(); }
        };

        const closePanelAndTriggerCollapse = e => {
            turnOffHideMode();
            closeAllMenus(e);
        };

        by('close-p').onclick = closePanelAndTriggerCollapse;
        by('clear-log-btn').onclick = () => { CACHE.logs = []; sv('hider_global_logs', []); renderList(); };
    }

    function renderList() {
        if (!CACHE.logs) CACHE.logs = gv('hider_global_logs', []);
        const siteKey = 'hider_site_' + location.hostname, linkKey = 'hider_link_' + cleanUrl();
        const siteData = gv(siteKey, []), linkData = gv(linkKey, []);

        const cCustom = by('list-custom-rules'), cDomains = by('list-blocked-domains'), cSite = by('list-site'), cLink = by('list-link'), cLog = by('list-blocked-log'), memSelect = by('freeze-memory-select');
        if (!cSite || !cLink) return;

        if (memSelect) {
            memSelect.value = CACHE.freezeMemory || 'ask';
            memSelect.onchange = e => { CACHE.freezeMemory = e.target.value; sv('hider_freeze_memory', e.target.value); showToast(`Memory updated`); };
        }

        // Render Custom Rules
        if (cCustom) {
            cCustom.innerHTML = CACHE.customRules.length ? '' : `<div style="font-size:9px;color:#64748b">No custom rules</div>`;
            CACHE.customRules.forEach(rule => {
                const div = doc.createElement('div'); div.className = 'list-item';
                const targetTag = rule.target === '*' ? '<span style="color:#38bdf8">[Global]</span>' : `<span style="color:#10b981">[${rule.target}]</span>`;
                div.innerHTML = `<span class="rule-text" title="${rule.selector}">${rule.selector} ${targetTag}</span><div style="display:flex;gap:2px"><button class="hider-btn-small btn-edit" style="background:#3b82f6">✏️</button><button class="hider-btn-small btn-del">✖</button></div>`;
                
                div.querySelector('.btn-edit').onclick = () => {
                    editingRuleId = rule.id;
                    by('manual-rule-input').value = rule.selector;
                    by('manual-target-input').value = rule.target;
                    by('save-rule-btn').textContent = 'Update';
                };
                div.querySelector('.btn-del').onclick = () => {
                    CACHE.customRules = CACHE.customRules.filter(r => r.id !== rule.id);
                    sv('hider_custom_rules_v4', CACHE.customRules); updateStyles(); renderList();
                };
                cCustom.appendChild(div);
            });
        }

        if (cDomains) {
            cDomains.innerHTML = CACHE.blockedDomainsList.length ? '' : `<div style="font-size:9px;color:#64748b">None</div>`;
            CACHE.blockedDomainsList.forEach((d, i) => {
                const div = doc.createElement('div'); div.className = 'list-item';
                div.innerHTML = `<span class="rule-text" title="${d}">${d}</span><button class="hider-btn-small">✖</button>`;
                div.querySelector('button').onclick = () => { CACHE.blockedDomainsList.splice(i, 1); CACHE.blockedDomainsSet.delete(cleanDomain(d)); sv('hider_blocked_domains', CACHE.blockedDomainsList); renderList(); };
                cDomains.appendChild(div);
            });
        }

        if (cLog) {
            cLog.innerHTML = CACHE.logs.length ? '' : `<div style="font-size:9px;color:#64748b;text-align:center">No logs today</div>`;
            CACHE.logs.forEach(item => {
                const row = doc.createElement('div'); row.style.cssText = 'font-size:9px;border-bottom:1px solid rgba(255,255,255,0.06);padding:3px 0;';
                row.innerHTML = `<div style="display:flex;justify-content:space-between;color:#a7f3d0;font-weight:bold"><span>${item.type}</span><span style="color:#64748b">${item.time}</span></div><div style="color:#94a3b8;word-break:break-all;font-family:monospace">${item.url}</div>`;
                cLog.appendChild(row);
            });
        }

        // Render & Editable Hidden Elements Rules
        const renderEditableHiddenItems = (data, key, container) => {
            container.innerHTML = data.length ? '' : `<div style="font-size:9px;color:#64748b">None</div>`;
            data.forEach((sel, i) => {
                const div = doc.createElement('div'); div.className = 'list-item';
                div.innerHTML = `
                    <span class="rule-text" title="${sel}">${sel}</span>
                    <div style="display:flex;gap:2px;align-items:center">
                        <button class="hider-btn-small btn-edit" style="background:#3b82f6" title="Edit Rule Inline">✏️</button>
                        <button class="hider-btn-small btn-wild" style="background:#8b5cf6" title="Convert Wildcard">🪄</button>
                        <button class="hider-btn-small btn-del" style="background:#ef4444" title="Delete Rule">✖</button>
                    </div>`;

                const textSpan = div.querySelector('.rule-text');
                const editBtn = div.querySelector('.btn-edit');

                editBtn.onclick = () => {
                    if (div.classList.contains('is-editing')) return;
                    div.classList.add('is-editing');
                    
                    const input = doc.createElement('input');
                    input.type = 'text';
                    input.value = data[i];
                    input.className = 'h-select';
                    input.style.cssText = 'font-size:9px;padding:2px 4px;height:22px;flex:1;margin-right:4px;';
                    
                    const saveBtn = doc.createElement('button');
                    saveBtn.className = 'hider-btn-small';
                    saveBtn.style.cssText = 'background:#10b981;padding:2px 6px;';
                    saveBtn.textContent = '✓';
                    
                    textSpan.replaceWith(input);
                    editBtn.replaceWith(saveBtn);
                    input.focus();

                    const saveHandler = () => {
                        const newVal = input.value.trim();
                        if (newVal) {
                            data[i] = newVal;
                            sv(key, data);
                            updateStyles();
                            showToast('✏️ Hidden rule updated');
                        }
                        renderList();
                    };

                    saveBtn.onclick = saveHandler;
                    input.onkeypress = e => e.key === 'Enter' && saveHandler();
                };

                div.querySelector('.btn-wild').onclick = () => { 
                    data[i] = convertToWildcardSelector(data[i]); 
                    sv(key, data); updateStyles(); renderList(); showToast(`🪄 Wildcard applied`); 
                };

                div.querySelector('.btn-del').onclick = () => {
                    const removed = data.splice(i, 1)[0]; sv(key, data);
                    try { doc.querySelectorAll(removed).forEach(el => el.classList.remove('hider-stealth-target')); } catch {}
                    updateStyles(); renderList();
                };

                container.appendChild(div);
            });
        };

        renderEditableHiddenItems(siteData, siteKey, cSite); 
        renderEditableHiddenItems(linkData, linkKey, cLink);
    }

    function handleNavigationClick(e) {
        if (isSelecting || e.target.closest('#' + UI_HOST_ID + ', #hider-freeze-prompt, #' + STEPPER_BAR_ID)) return;
        const link = e.target.closest('a[href], area[href]');
        if (link?.href && !link.getAttribute('href').startsWith('#') && !link.getAttribute('href').startsWith('javascript:')) {
            const targetUrl = link.href;
            if (isDomainBlocked(targetUrl)) {
                e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
                logBlockedAttempt(targetUrl, 'Blocked Link Click'); simulateAdWindowSuccess(); showToast('⛔ Force Blocked'); return;
            }
            if (!isFrozen) return;
            e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
            const isNewTab = link.target === '_blank' || e.ctrlKey || e.metaKey || e.button === 1;
            handleFreezeNavigation(targetUrl, 'Link Click', () => {
                userApprovedNavigation = true;
                if (isNewTab) win.open(targetUrl, '_blank'); else win.location.href = targetUrl;
                setTimeout(() => userApprovedNavigation = false, 300);
            }, simulateAdWindowSuccess);
        }
    }

    // Auto-close safety check on scroll & tap (v4.3)
    window.addEventListener('scroll', closeAllMenus, { passive: true });

    window.addEventListener('click', e => {
        const isUI = e.target.closest('#' + UI_HOST_ID + ', #hider-freeze-prompt, #' + STEPPER_BAR_ID);
        
        if (!isUI) {
            closeAllMenus(e); // Close menus when user taps/clicks main website
        }

        if (isUI) return;
        
        handleNavigationClick(e); 
        if (!isSelecting) return;
        
        e.preventDefault(); e.stopPropagation();
        if (previewElement === e.target) confirmHideSelectedElement();
        else {
            previewElement?.classList.remove('hider-preview-highlight');
            stepperStack = []; previewElement = e.target; previewElement.classList.add('hider-preview-highlight'); renderTouchStepperUI();
        }
    }, true);

    window.addEventListener('auxclick', handleNavigationClick, true);

    window.addEventListener('submit', e => {
        if (e.target.closest('#' + UI_HOST_ID + ', #hider-freeze-prompt, #' + STEPPER_BAR_ID)) return;
        const form = e.target, action = form.action || location.href;
        if (isDomainBlocked(action)) {
            e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
            logBlockedAttempt(action, 'Blocked Form Submit'); simulateAdWindowSuccess(); showToast('⛔ Blocked Form'); return;
        }
        if (!isFrozen) return;
        e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
        handleFreezeNavigation(action, 'Form Submit', () => { userApprovedNavigation = true; form.submit(); setTimeout(() => userApprovedNavigation = false, 300); }, simulateAdWindowSuccess);
    }, true);

    win.addEventListener('beforeunload', e => {
        if (isFrozen && !userApprovedNavigation && (CACHE.freezeMemory === 'ask' || CACHE.freezeMemory === 'block_all')) {
            e.preventDefault(); return (e.returnValue = 'Page navigation is currently frozen site-wide.');
        }
    }, true);

    const checkUrlChange = () => {
        if (location.href !== lastUrl) {
            lastUrl = location.href; updateStyles();
            if (by('hider-panel')?.style.display === 'flex') renderList();
        }
    };

    ['pushState', 'replaceState'].forEach(fn => {
        const orig = history[fn];
        if (orig) history[fn] = function() { orig.apply(this, arguments); checkUrlChange(); };
    });
    window.addEventListener('popstate', checkUrlChange, { passive: true });
    window.addEventListener('hashchange', checkUrlChange, { passive: true });

    function init() {
        ['contextmenu', 'selectstart', 'copy', 'paste', 'dragstart'].forEach(evt => doc.addEventListener(evt, e => e.stopPropagation(), true));
        injectStylesAndUI(); updateStyles();
        let timer = null;
        new MutationObserver(() => {
            if (timer) return;
            timer = setTimeout(() => {
                timer = null;
                if (isTop && !by(UI_HOST_ID)) injectStylesAndUI();
                if (!by('hider-dynamic-styles')) updateStyles();
            }, 1000);
        }).observe(doc.documentElement || doc.body, { childList: true, subtree: true });
    }

    if (doc.readyState === 'loading') doc.addEventListener('DOMContentLoaded', init); else init();
})();