LiveReload

Randomized auto-refresh

2026/02/14のページです。最新版はこちら

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

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

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         LiveReload
// @namespace    https://github.com/RustwuIf
// @version      2.2.9
// @description  Randomized auto-refresh
// @author       Rustwulf
// @match        <all_urls>
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';
 
    function LiveReload() {
        // ==================== STORAGE KEYS ====================
        // Keys for persisting user preferences and statistics to localStorage
        const storageKey = 'autoRefreshState';
        const themeKey = 'autoRefreshTheme';
        const opacityKey = 'autoRefreshOpacity';
        const statsKey = 'autoRefreshStats';
        const defaults = { min: 5, max: 15, opacity: 1 };
 
        // ==================== NEW STORAGE KEYS FOR ANALYTICS ====================
        // Keys for reload history and uptime tracking
        const reloadHistoryKey = 'reloadHistory';
        const uptimeDataKey = 'uptimeData';
 
        // ==================== STATE VARIABLES ====================
        // Core runtime state for the auto-reload functionality
        let isRunning = false;
        let isPausedBySafety = false;
        let isPausedByInactivity = false;
        let remainingTime = 0;
        let maxRemainingTime = 0;
        let timerId = null;
        let sessionTimerId = null;
        let inactivityTimeout = null;
 
        // ==================== CONFIGURATION ====================
        // Thresholds and defaults for inactivity detection
        const INACTIVITY_THRESHOLD = 60000; // 60 seconds
        const originalFavicon = document.querySelector("link[rel~='icon']")?.href || '/favicon.ico';
 
        // ==================== STATISTICS OBJECT ====================
        // Tracks usage metrics and session data
        let stats = {
            totalReloads: 0,
            totalRunningTime: 0,
            waitTimes: [],
            maxReloadsLimit: 0,
            jitterMode: false,
            sessionStartTime: null
        };
 
        // ==================== NEW UPTIME OBJECT ====================
        // Tracks cumulative uptime across sessions
        let uptimeData = {
            totalUptimeSeconds: 0,
            sessionStartTime: null,
            lastUpdated: null
        };
 
        // ==================== STORAGE FUNCTIONS ====================
        // Load and save statistics from/to localStorage with error handling
        const loadStats = () => {
            try {
                const stored = localStorage.getItem(statsKey);
                if (stored) stats = { ...stats, ...JSON.parse(stored) };
            } catch (e) {
                console.error('Stats load error:', e);
            }
        };
 
        const saveStats = () => {
            try {
                localStorage.setItem(statsKey, JSON.stringify(stats));
            } catch (e) {
                console.error('Stats save error:', e);
            }
        };
 
        // ==================== NEW RELOAD HISTORY FUNCTIONS ====================
        // Load, save, and manage reload history from localStorage
        const loadReloadHistory = () => {
            try {
                const stored = localStorage.getItem(reloadHistoryKey);
                return stored ? JSON.parse(stored) : [];
            } catch (e) {
                console.error('Reload history load error:', e);
                return [];
            }
        };
 
        const saveReloadHistory = (history) => {
            try {
                localStorage.setItem(reloadHistoryKey, JSON.stringify(history));
            } catch (e) {
                console.error('Reload history save error:', e);
            }
        };
 
        const addReloadToHistory = (waitTime) => {
            const history = loadReloadHistory();
            const now = new Date();
            const timestamp = now.toLocaleTimeString('en-US', { hour12: false });
            const date = now.toLocaleDateString('en-US');
            
            history.push({
                timestamp: timestamp,
                date: date,
                waitTime: waitTime,
                fullTime: now.getTime()
            });
            
            // Keep only last 10 reloads
            if (history.length > 10) {
                history.shift();
            }
            
            saveReloadHistory(history);
        };
 
        // ==================== NEW RELOAD FREQUENCY CALCULATION ====================
        // Calculate reloads per minute based on total uptime and reload count
        const calculateReloadFrequency = () => {
            const totalReloads = stats.totalReloads || 0;
            let totalUptimeSeconds = (uptimeData.totalUptimeSeconds || 0);
            
            // Include current session if running
            if (isRunning && uptimeData.sessionStartTime) {
                totalUptimeSeconds += Math.floor((Date.now() - uptimeData.sessionStartTime) / 1000);
            }
            
            if (totalUptimeSeconds === 0 || totalReloads === 0) {
                return { reloadsPerMinute: 0, formattedFrequency: '0.0 /min' };
            }
            
            const minutes = totalUptimeSeconds / 60;
            const reloadsPerMinute = (totalReloads / minutes).toFixed(1);
            
            return {
                reloadsPerMinute: parseFloat(reloadsPerMinute),
                formattedFrequency: `${reloadsPerMinute} /min`
            };
        };
 
        // ==================== NEW RELOAD GRAPH DRAWING ====================
        // Draw a visual chart of reload times over the session
        const drawReloadGraph = (containerId) => {
            try {
                const container = shadow.querySelector(`#${containerId}`);
                if (!container) return;
                
                // Clear previous content
                container.innerHTML = '';
                
                // Get reload times
                const waitTimes = stats.waitTimes || [];
                if (waitTimes.length === 0) {
                    container.innerHTML = '<div style="text-align: center; opacity: 0.6; padding: 20px; font-size: 11px;">No reload data yet</div>';
                    return;
                }
                
                // Create canvas
                const canvas = document.createElement('canvas');
                canvas.width = 320;
                canvas.height = 120;
                canvas.style.width = '100%';
                canvas.style.height = '120px';
                container.appendChild(canvas);
                
                const ctx = canvas.getContext('2d');
                const padding = 20;
                const graphWidth = canvas.width - (padding * 2);
                const graphHeight = canvas.height - (padding * 2);
                
                // Find min and max wait times
                const maxWaitTime = Math.max(...waitTimes);
                const minWaitTime = Math.min(...waitTimes);
                const range = maxWaitTime - minWaitTime || 1;
                
                // Draw background
                ctx.fillStyle = 'rgba(17, 153, 142, 0.05)';
                ctx.fillRect(padding, padding, graphWidth, graphHeight);
                
                // Draw border
                ctx.strokeStyle = 'rgba(17, 153, 142, 0.2)';
                ctx.lineWidth = 1;
                ctx.strokeRect(padding, padding, graphWidth, graphHeight);
                
                // Draw grid lines
                ctx.strokeStyle = 'rgba(17, 153, 142, 0.1)';
                ctx.lineWidth = 0.5;
                for (let i = 0; i <= 4; i++) {
                    const y = padding + (graphHeight / 4) * i;
                    ctx.beginPath();
                    ctx.moveTo(padding, y);
                    ctx.lineTo(canvas.width - padding, y);
                    ctx.stroke();
                }
                
                // Draw line chart
                ctx.strokeStyle = 'rgba(17, 153, 142, 0.8)';
                ctx.lineWidth = 2;
                ctx.beginPath();
                
                waitTimes.forEach((time, index) => {
                    const x = padding + (graphWidth / (waitTimes.length - 1 || 1)) * index;
                    const normalizedTime = (time - minWaitTime) / range;
                    const y = canvas.height - padding - (normalizedTime * graphHeight);
                    
                    if (index === 0) {
                        ctx.moveTo(x, y);
                    } else {
                        ctx.lineTo(x, y);
                    }
                });
                
                ctx.stroke();
                
                // Draw data points
                ctx.fillStyle = '#11998e';
                waitTimes.forEach((time, index) => {
                    const x = padding + (graphWidth / (waitTimes.length - 1 || 1)) * index;
                    const normalizedTime = (time - minWaitTime) / range;
                    const y = canvas.height - padding - (normalizedTime * graphHeight);
                    
                    ctx.beginPath();
                    ctx.arc(x, y, 3, 0, 2 * Math.PI);
                    ctx.fill();
                });
                
                // Draw labels
                ctx.fillStyle = 'rgba(255, 255, 255, 0.6)';
                ctx.font = '10px Arial';
                ctx.textAlign = 'left';
                
                // Y-axis labels (wait times)
                ctx.fillText(maxWaitTime + 's', 3, padding - 5);
                ctx.fillText(minWaitTime + 's', 3, canvas.height - padding + 5);
                
                // X-axis label
                ctx.textAlign = 'center';
                ctx.fillText('Reload Timeline', canvas.width / 2, canvas.height - 3);
            } catch (e) {
                console.error('Graph drawing error:', e);
            }
        };
        // Load, save, and manage cumulative uptime data
        const loadUptimeData = () => {
            try {
                const stored = localStorage.getItem(uptimeDataKey);
                if (stored) {
                    return { ...uptimeData, ...JSON.parse(stored) };
                }
            } catch (e) {
                console.error('Uptime data load error:', e);
            }
            return uptimeData;
        };
 
        const saveUptimeData = () => {
            try {
                localStorage.setItem(uptimeDataKey, JSON.stringify(uptimeData));
            } catch (e) {
                console.error('Uptime data save error:', e);
            }
        };
 
        const updateUptimeData = () => {
            if (!uptimeData.sessionStartTime) {
                uptimeData.sessionStartTime = Date.now();
            }
            uptimeData.lastUpdated = Date.now();
            saveUptimeData();
        };
 
        // ==================== SETTINGS FUNCTIONS ====================
        // Retrieve user settings from localStorage with fallback to defaults
        const getSettings = () => ({
            min: parseInt(localStorage.getItem('minDelay'), 10) || defaults.min,
            max: parseInt(localStorage.getItem('maxDelay'), 10) || defaults.max,
            opacity: localStorage.getItem(opacityKey) || defaults.opacity
        });
 
        // ==================== FAVICON UPDATE ====================
        // Updates favicon with countdown timer and status indicator
        // Shows red circle when time is critical (≤3s), green when waiting, blue when paused
        const updateFavicon = (number) => {
            const canvas = document.createElement('canvas');
            canvas.width = canvas.height = 32;
            const ctx = canvas.getContext('2d');
            const img = new Image();
            img.crossOrigin = "Anonymous";
            img.src = originalFavicon;
            
            img.onload = () => {
                ctx.drawImage(img, 0, 0, 32, 32);
                ctx.beginPath();
                ctx.arc(20, 20, 12, 0, 2 * Math.PI);
                ctx.fillStyle = isPausedBySafety ? '#3498db' : (number <= 3 ? '#ff5f6d' : '#2ecc71');
                ctx.fill();
                ctx.fillStyle = "white";
                ctx.font = "bold 16px Arial";
                ctx.textAlign = "center";
                ctx.fillText(isPausedBySafety ? "!!" : number, 20, 26);
                
                let link = document.querySelector("link[rel~='icon']") || (() => {
                    const l = document.createElement('link');
                    l.rel = 'icon';
                    document.head.appendChild(l);
                    return l;
                })();
                link.href = canvas.toDataURL('image/png');
            };
        };
 
        // ==================== NOTIFICATION SYSTEM ====================
        // Displays temporary toast notifications for user feedback (jitter applied, limits reached, saved settings)
        const showToast = (message) => {
            const toast = document.createElement('div');
            toast.style.cssText = 'position: fixed; bottom: 100px; left: 20px; background: rgba(52, 152, 219, 0.95); color: white; padding: 16px 24px; border-radius: 12px; font-weight: 600; z-index: 2147483646; box-shadow: 0 8px 25px rgba(0,0,0,0.3); backdrop-filter: blur(10px); animation: slideInUp 0.3s ease-out;';
            toast.textContent = message;
            document.body.appendChild(toast);
            setTimeout(() => toast.remove(), 2500);
        };
 
        // ==================== RELOAD COUNTER FUNCTIONS ====================
        // Manages reload count in localStorage and stats, prevents exceeding max reload limit
        const getReloadCount = () => parseInt(localStorage.getItem('reloadCounter')) || 0;
        const incrementReloadCount = () => {
            const newCount = getReloadCount() + 1;
            localStorage.setItem('reloadCounter', newCount.toString());
            stats.totalReloads = newCount;
            saveStats();
            return newCount;
        };
        const resetReloadCount = () => {
            localStorage.setItem('reloadCounter', '0');
            stats.totalReloads = 0;
            saveStats();
        };
 
        // ==================== JITTER MODE ====================
        // Adds ±10% random delay to prevent predictable reload patterns
        // Returns object with newValue, jitterAmount, and applied flag
        const applyJitter = (value) => {
            const jitterEnabled = localStorage.getItem('jitterMode') === 'true';
            if (!jitterEnabled) {
                return { newValue: value, jitterAmount: 0, applied: false };
            }
            
            // Calculate 10% of the value
            const jitterRange = Math.floor(value * 0.1);
            
            // Generate random adjustment between -jitterRange and +jitterRange
            const adjustment = Math.floor(Math.random() * (jitterRange * 2 + 1)) - jitterRange;
            
            // Ensure the final value is at least 1
            const newValue = Math.max(1, value + adjustment);
            
            return { newValue: newValue, jitterAmount: adjustment, applied: true };
        };
 
        // ==================== ACTIVITY TRACKING ====================
        // Monitors user activity and resumes from inactivity pause
        const recordActivity = () => {
            if (isPausedByInactivity) {
                isPausedByInactivity = false;
                updateUIState();
            }
            resetInactivityTimeout();
        };
 
        // ==================== INACTIVITY DETECTION ====================
        // Automatically pauses reload when user hasn't interacted with page for 60 seconds
        const resetInactivityTimeout = () => {
            if (inactivityTimeout) clearTimeout(inactivityTimeout);
            if (!isRunning || localStorage.getItem('inactivityPauseEnabled') !== 'true') return;
            
            inactivityTimeout = setTimeout(() => {
                if (isRunning && !isPausedBySafety) {
                    isPausedByInactivity = true;
                    updateUIState();
                }
            }, INACTIVITY_THRESHOLD);
        };
 
        // ==================== UI ELEMENTS ====================
        // References to Shadow DOM elements (populated during createShadowUI)
        let host, shadow, mainBtn, settingsBtn, themeBtn, settingsModal, opacitySlider;
 
        // ==================== SHADOW DOM CREATION ====================
        // Creates isolated UI with shadow DOM for styling isolation and positioning
        function createShadowUI() {
            host = document.createElement('div');
            host.id = 'live-reload-host';
            host.style.cssText = 'position: fixed; bottom: 20px; left: 20px; z-index: 2147483647; font-family: "Inter", sans-serif;';
            
            // ========== DRAG & DROP HANDLER ==========
            // Allows user to drag widget to any position on screen and persist location
            let isDragging = false, dragOffsetX = 0, dragOffsetY = 0;
            
            const startDrag = (clientX, clientY) => {
                isDragging = true;
                dragOffsetX = clientX - host.offsetLeft;
                dragOffsetY = clientY - host.offsetTop;
                host.style.cursor = 'grabbing';
            };
            
            const moveDrag = (clientX, clientY) => {
                if (!isDragging) return;
                host.style.left = (clientX - dragOffsetX) + 'px';
                host.style.top = (clientY - dragOffsetY) + 'px';
                host.style.bottom = 'auto';
                localStorage.setItem('liveReloadPos', JSON.stringify({ x: clientX - dragOffsetX, y: clientY - dragOffsetY }));
            };
            
            const endDrag = () => {
                isDragging = false;
                host.style.cursor = 'grab';
            };
            
            const checkIfDraggable = (path) => !path.some(el => 
                el.tagName === 'INPUT' || (el.className && (el.className.includes('modal') || el.className.includes('stats-section')))
            );
            
            // Mouse and touch event listeners for dragging
            host.addEventListener('mousedown', (e) => {
                if (e.button !== 0 || !checkIfDraggable(e.composedPath())) return;
                startDrag(e.clientX, e.clientY);
            });
            
            host.addEventListener('touchstart', (e) => {
                if (!checkIfDraggable(e.composedPath())) return;
                startDrag(e.touches[0].clientX, e.touches[0].clientY);
            });
            
            document.addEventListener('mousemove', (e) => moveDrag(e.clientX, e.clientY));
            document.addEventListener('touchmove', (e) => moveDrag(e.touches[0].clientX, e.touches[0].clientY));
            document.addEventListener('mouseup', endDrag);
            document.addEventListener('touchend', endDrag);
            
            // Restore saved position on page load
            try {
                const pos = JSON.parse(localStorage.getItem('liveReloadPos'));
                if (pos) {
                    host.style.left = pos.x + 'px';
                    host.style.top = pos.y + 'px';
                    host.style.bottom = 'auto';
                }
            } catch (e) {}
            
            document.body.appendChild(host);
            shadow = host.attachShadow({ mode: 'open' });
 
            // ========== SHADOW DOM STYLES ==========
            // Complete styling for all UI elements, themes, animations, and responsive design
            const style = document.createElement('style');
            style.textContent = `
                :host { --accent-red: linear-gradient(135deg, #ff5f6d, #ffc371); --accent-green: linear-gradient(135deg, #11998e, #38ef7d); --accent-blue: #3498db; }
                :host([theme="dark"]) { --bg: rgba(20,20,25,0.95); --modal-bg: rgba(15,15,20,0.98); --text: #fff; --border: rgba(255,255,255,0.08); --input-bg: rgba(255,255,255,0.05); }
                :host([theme="light"]) { --bg: rgba(240,242,245,0.95); --modal-bg: rgba(255,255,255,0.98); --text: #1a1a1b; --border: rgba(0,0,0,0.08); --input-bg: rgba(0,0,0,0.03); }
                
                .btn { cursor: pointer; color: var(--text); border-radius: 12px; padding: 11px 20px; font-weight: 600; border: 1px solid var(--border); display: flex; align-items: center; gap: 8px; backdrop-filter: blur(20px); background: var(--bg); transition: all 0.25s ease; font-size: 14px; }
                .btn:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(0,0,0,0.15); border-color: rgba(255,255,255,0.15); }
                .btn:active { transform: translateY(0px); }
                .btn:hover .icon { animation: iconRotate 0.6s ease-in-out forwards; }
                .icon { display: inline-block; font-size: 16px; }
                
                .btn-main { background: var(--accent-red); border: none; color: white; min-width: 130px; justify-content: center; box-shadow: 0 6px 20px rgba(255, 95, 109, 0.3); position: relative; font-size: 15px; font-weight: 700; }
                .btn-main::before { content: ''; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 160px; height: 160px; border: 3px solid rgba(255, 255, 255, 0.2); border-radius: 50%; animation: rotatePulse 3s linear infinite; pointer-events: none; z-index: -1; }
                .btn-main.active::before { border-color: rgba(255, 255, 255, 0.7); animation: rotatePulse 2s linear infinite; }
                .btn-main.safety::before { border-color: rgba(52, 152, 219, 0.7); animation: rotatePulse 1.5s linear infinite; }
                @keyframes rotatePulse { 0% { transform: translate(-50%, -50%) rotate(0deg); } 100% { transform: translate(-50%, -50%) rotate(360deg); } }
                .btn-main.active { background: linear-gradient(135deg, #f1c40f, #f39c12); color: #000; box-shadow: 0 6px 20px rgba(241, 196, 15, 0.4); }
                .btn-main.safety { background: var(--accent-blue); color: white; box-shadow: 0 6px 20px rgba(52, 152, 219, 0.3); animation: softPulse 2s infinite; }
                
                .modal { position: absolute; bottom: 75px; left: 0; background: var(--modal-bg); padding: 0; border-radius: 18px; border: 1px solid var(--border); box-shadow: 0 25px 60px rgba(0,0,0,0.2); color: var(--text); display: none; flex-direction: row; gap: 0; width: 370px; backdrop-filter: blur(30px); max-height: 85vh; overflow: hidden; align-items: stretch; }
                .modal-wrapper { display: flex; flex-direction: column; flex: 1; height: 320px; }
                .modal-content { flex: 1; display: flex; flex-direction: column; gap: 5px; overflow-y: auto; padding: 12px; padding-right: 8px; padding-bottom: 20px; scrollbar-width: none; }
                .modal-content::-webkit-scrollbar { width: 0px; height: 0px; }
                .modal-content::-webkit-scrollbar-track { background: transparent; }
                .modal-content::-webkit-scrollbar-thumb { background: transparent; border-radius: 2px; display: none; }
                .modal-content::-webkit-scrollbar-thumb:hover { background: transparent; }
                
                .modal-footer { display: flex; flex-direction: column; gap: 8px; padding: 16px 12px 12px 12px; border-top: 1px solid var(--border); flex-shrink: 0; align-items: center; }
                
                .modal-tabs { display: flex; flex-direction: column; gap: 4px; padding: 8px; border-left: 1px solid var(--border); flex-shrink: 0; align-items: center; justify-content: flex-start; position: sticky; top: 0; min-width: 54px; background: var(--modal-bg); z-index: 1; height: 320px; }
                .tab-btn { background: transparent; border: none; color: var(--text); width: 38px; height: 38px; border-radius: 8px; cursor: pointer; font-size: 18px; display: flex; align-items: center; justify-content: center; transition: all 0.35s cubic-bezier(0.34, 1.56, 0.64, 1); opacity: 0.5; flex-shrink: 0; }
                .tab-btn:hover { opacity: 0.75; }
                .tab-btn:active { opacity: 0.9; }
                .tab-btn.active { background: rgba(17, 153, 142, 0.3); color: #11998e; opacity: 1; box-shadow: inset 0 0 0 1px rgba(17, 153, 142, 0.4), 0 0 12px rgba(17, 153, 142, 0.3); animation: tabGlow 0.4s ease-out; }
                @keyframes tabGlow { 0% { box-shadow: inset 0 0 0 1px rgba(17, 153, 142, 0.4), 0 0 6px rgba(17, 153, 142, 0.1); } 50% { box-shadow: inset 0 0 0 1px rgba(17, 153, 142, 0.4), 0 0 18px rgba(17, 153, 142, 0.4); } 100% { box-shadow: inset 0 0 0 1px rgba(17, 153, 142, 0.4), 0 0 12px rgba(17, 153, 142, 0.3); } }
                
                .tab-content { display: none; opacity: 0; transform: translateY(-8px); transition: opacity 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1); pointer-events: none; }
                .tab-content.active { display: flex; flex-direction: column; gap: 5px; opacity: 1; transform: translateY(0); pointer-events: auto; animation: slideInContent 0.35s cubic-bezier(0.34, 1.56, 0.64, 1); }
                @keyframes slideInContent { 0% { opacity: 0; transform: translateY(-8px); } 100% { opacity: 1; transform: translateY(0); } }
                
                .modal::-webkit-scrollbar { width: 6px; }
                .modal::-webkit-scrollbar-track { background: transparent; }
                .modal::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); border-radius: 3px; }
                .modal::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.3); }
                
                .modal, .modal * { cursor: auto !important; }
                .modal button { cursor: pointer !important; }
                .modal input { cursor: pointer !important; }
                
                input[type="range"] { width: 100%; height: 6px; border-radius: 5px; background: var(--input-bg); outline: none; -webkit-appearance: none; appearance: none; accent-color: #11998e; }
                input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 18px; height: 18px; border-radius: 50%; background: linear-gradient(135deg, #11998e, #38ef7d); cursor: pointer; box-shadow: 0 2px 8px rgba(0,0,0,0.2); transition: all 0.2s ease; }
                input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.15); box-shadow: 0 4px 12px rgba(17,153,142,0.4); }
                input[type="range"]::-moz-range-thumb { width: 18px; height: 18px; border-radius: 50%; background: linear-gradient(135deg, #11998e, #38ef7d); cursor: pointer; box-shadow: 0 2px 8px rgba(0,0,0,0.2); border: none; transition: all 0.2s ease; }
                input[type="range"]::-moz-range-thumb:hover { transform: scale(1.15); box-shadow: 0 4px 12px rgba(17,153,142,0.4); }
                
                input[type="number"] { background: var(--input-bg); border: 1px solid var(--border); color: var(--text); border-radius: 8px; padding: 8px 12px; font-size: 13px; transition: all 0.2s ease; font-family: inherit; }
                input[type="number"]:focus { border-color: #11998e; background: var(--input-bg); box-shadow: 0 0 0 2px rgba(17,153,142,0.1); }
                
                input[type="checkbox"] { width: 18px; height: 18px; cursor: pointer; accent-color: #11998e; }
                
                .preset-btn { background: rgba(17, 153, 142, 0.15); border: 1px solid rgba(17, 153, 142, 0.3); color: var(--text); padding: 7px 10px; border-radius: 8px; font-weight: 600; font-size: 11px; cursor: pointer; transition: all 0.2s ease; font-family: inherit; }
                .preset-btn:hover { background: rgba(17, 153, 142, 0.25); border-color: rgba(17, 153, 142, 0.5); transform: translateY(-1px); }
                .preset-btn:active { transform: translateY(0); }
                
                .btn-save { background: var(--accent-green); border: none; color: white; padding: 10px 40px; border-radius: 10px; font-weight: 700; margin: 8px auto 0 auto; display: block; cursor: pointer; font-size: 13px; transition: all 0.2s ease; box-shadow: 0 4px 15px rgba(17,153,142,0.3); font-family: inherit; flex-shrink: 0; width: fit-content; }
                .btn-save:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(17,153,142,0.4); }
                .btn-save:active { transform: translateY(0); }
                
                .section-title { font-weight: 700; font-size: 11px; text-transform: uppercase; letter-spacing: 0.4px; opacity: 0.7; margin-top: 2px; margin-bottom: 4px; }
                .input-group { display: flex; flex-direction: column; gap: 4px; }
                .input-row { display: flex; justify-content: space-between; align-items: center; gap: 12px; }
                .input-row label { font-size: 13px; font-weight: 500; }
                .input-row strong { color: #11998e; font-weight: 700; }
                
                @keyframes softPulse { 0% { opacity: 1; } 50% { opacity: 0.75; } 100% { opacity: 1; } }
                @keyframes iconRotate { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
                
                .mode-badge { display: inline-block; background: rgba(17, 153, 142, 0.2); border: 1px solid rgba(17, 153, 142, 0.4); color: #11998e; padding: 2px 6px; border-radius: 4px; font-size: 9px; font-weight: 600; }
                .mode-badge.active { background: rgba(17, 153, 142, 0.35); border-color: rgba(17, 153, 142, 0.6); animation: pulse-badge 1.8s infinite; }
                @keyframes pulse-badge { 0%, 100% { opacity: 1; } 50% { opacity: 0.6; } }
                
                .mode-card { padding: 8px; background: var(--input-bg); border-radius: 8px; border: 1px solid var(--border); display: flex; align-items: center; gap: 10px; transition: all 0.2s; }
                .mode-card.active { background: rgba(17, 153, 142, 0.1); border-color: rgba(17, 153, 142, 0.3); box-shadow: inset 0 0 0 1px rgba(17, 153, 142, 0.2); }
                .mode-label { display: flex; align-items: center; gap: 8px; flex: 1; margin: 0; }
                .mode-label label { font-size: 12px; font-weight: 500; margin: 0; cursor: pointer; }
                .mode-info { font-size: 9px; opacity: 0.6; margin-top: 2px; font-style: italic; line-height: 1.2; }
                
                /* ========== NEW STYLES FOR ANALYTICS ==========  */
                .history-item { padding: 6px 8px; background: var(--input-bg); border-radius: 6px; border: 1px solid var(--border); font-size: 11px; margin-bottom: 4px; display: flex; justify-content: space-between; align-items: center; }
                .history-time { color: #11998e; font-weight: 600; }
                .history-wait { opacity: 0.7; }
                .history-empty { text-align: center; opacity: 0.6; padding: 20px 8px; font-size: 11px; font-style: italic; }
                
                #reload-history-container { scrollbar-width: none; }
                #reload-history-container::-webkit-scrollbar { display: none; width: 0; }
                #reload-history-container::-webkit-scrollbar-track { background: transparent; }
                #reload-history-container::-webkit-scrollbar-thumb { background: transparent; }
                
                /* ========== NEW STYLES FOR GRAPH & FREQUENCY ==========  */
                #reload-graph-container { margin-top: 8px; }
                .frequency-stat { padding: 6px 8px; background: var(--input-bg); border-radius: 6px; border: 1px solid var(--border); font-size: 11px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; }
                .frequency-label { opacity: 0.8; }
                .frequency-value { color: #11998e; font-weight: 600; }
                
                @media (max-width: 480px) {
                    .modal { width: 90vw; max-width: 340px; bottom: 60px; left: 50%; transform: translateX(-50%); }
                    .btn { padding: 9px 14px; font-size: 13px; min-height: 40px; }
                    .btn-main { min-width: 100px; }
                    .preset-btn { min-height: 36px; font-size: 10px; }
                    .mode-card { flex-wrap: wrap; }
                    .mode-label label { font-size: 13px; }
                    .tab-btn { width: 34px; height: 34px; font-size: 16px; }
                }
            `;
 
            // ========== DOM STRUCTURE ==========
            // Build UI elements: wrapper, buttons, and settings modal
            const wrapper = document.createElement('div');
            wrapper.style.opacity = getSettings().opacity;
            const container = document.createElement('div');
            container.style.cssText = 'display: flex; gap: 10px;';
 
            // Main play/pause button
            mainBtn = document.createElement('button');
            mainBtn.className = 'btn btn-main';
            mainBtn.style.position = 'relative';
            mainBtn.innerHTML = '<span class="icon">▶</span><span>Start</span>';
            
            // Settings button
            settingsBtn = document.createElement('button');
            settingsBtn.className = 'btn';
            settingsBtn.innerHTML = '<span class="icon">⚙</span>';
            
            // Theme toggle button
            themeBtn = document.createElement('button');
            themeBtn.className = 'btn';
 
            // ========== SETTINGS MODAL STRUCTURE ==========
            // Three-tab modal: Timing/Presets, Controls, and Statistics
            settingsModal = document.createElement('div');
            settingsModal.className = 'modal';
            settingsModal.innerHTML = `
                <div class="modal-wrapper">
                    <div class="modal-content">
                        <!-- Tab 1: Timing & Presets -->
                        <div class="tab-content active" id="tab-timing">
                            <div style="font-weight: 700; font-size: 11px; margin-bottom: 4px;">⚡ Presets</div>
                            <div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 6px; margin-bottom: 4px;">
                                <button class="preset-btn" id="preset-1-5">1-5s</button>
                                <button class="preset-btn" id="preset-5-15">5-15s</button>
                                <button class="preset-btn" id="preset-5-10">5-10s</button>
                                <button class="preset-btn" id="preset-10-15">10-15s</button>
                                <button class="preset-btn" id="preset-15-20">15-20s</button>
                                <button class="preset-btn" id="preset-20-30">20-30s</button>
                                <button class="preset-btn" id="preset-1-5m" style="grid-column: 1 / -1;">1-5m</button>
                            </div>
                            <div class="section-title">⏱ Timing</div>
                            <div class="input-group">
                                <div class="input-row">
                                    <label>Min Wait</label>
                                    <strong id="min-display">5</strong><span style="opacity: 0.6;">s</span>
                                </div>
                                <input type="range" id="min-input" min="1" max="300" value="5">
                            </div>
                            <div class="input-group">
                                <div class="input-row">
                                    <label>Max Wait</label>
                                    <strong id="max-display">15</strong><span style="opacity: 0.6;">s</span>
                                </div>
                                <input type="range" id="max-input" min="1" max="300" value="15">
                            </div>
                        </div>
                        
                        <!-- Tab 2: Controls -->
                        <div class="tab-content" id="tab-controls">
                            <div class="section-title">🎛 Controls</div>
                            <div class="input-group">
                                <div class="input-row">
                                    <label>Max Reloads</label>
                                    <strong id="max-reloads-display">0</strong><span style="opacity: 0.6;">(0=∞)</span>
                                </div>
                                <input type="range" id="max-reloads-input" min="0" max="100" value="0">
                            </div>
                            <div class="input-group">
                                <div class="input-row">
                                    <label>Opacity</label>
                                    <strong style="color: #11998e;" id="opacity-display">100</strong><span style="opacity: 0.6;">%</span>
                                </div>
                                <input type="range" id="opacity-slider" min="0.2" max="1" step="0.1" value="1">
                            </div>
                            <div class="mode-card" id="inactivity-card">
                                <div class="mode-label">
                                    <input type="checkbox" id="inactivity-toggle">
                                    <label for="inactivity-toggle">Inactivity Pause</label>
                                </div>
                                <span class="mode-badge" id="inactivity-badge">OFF</span>
                            </div>
                            <div class="mode-info">Pauses auto-reload when you stop using the page (60s timeout)</div>
                            <div class="mode-card" id="jitter-card">
                                <div class="mode-label">
                                    <input type="checkbox" id="jitter-toggle">
                                    <label for="jitter-toggle">Jitter Mode</label>
                                </div>
                                <span class="mode-badge" id="jitter-badge">OFF</span>
                            </div>
                            <div class="mode-info">Adds ±10% random delay to prevent predictable reload patterns</div>
                        </div>
                        
                        <!-- Tab 3: Statistics -->
                        <div class="tab-content" id="tab-stats">
                            <div class="section-title">📊 Statistics</div>
                            <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 6px; font-size: 11px; margin-bottom: 8px;">
                                <div style="padding: 6px; background: var(--input-bg); border-radius: 8px; border: 1px solid var(--border);">
                                    <div style="opacity: 0.7; margin-bottom: 2px; font-size: 10px;">Reloads</div>
                                    <div style="font-size: 14px; font-weight: 700; color: #11998e;" id="stat-reloads">0</div>
                                </div>
                                <div style="padding: 6px; background: var(--input-bg); border-radius: 8px; border: 1px solid var(--border);">
                                    <div style="opacity: 0.7; margin-bottom: 2px; font-size: 10px;">Limit</div>
                                    <div style="font-size: 14px; font-weight: 700; color: #11998e;" id="stat-max-limit">0</div>
                                </div>
                                <div style="padding: 6px; background: var(--input-bg); border-radius: 8px; border: 1px solid var(--border);">
                                    <div style="opacity: 0.7; margin-bottom: 2px; font-size: 10px;">Session</div>
                                    <div style="font-size: 12px; font-weight: 700; color: #11998e;" id="stat-session">0m 0s</div>
                                </div>
                                <div style="padding: 6px; background: var(--input-bg); border-radius: 8px; border: 1px solid var(--border);">
                                    <div style="opacity: 0.7; margin-bottom: 2px; font-size: 10px;">Avg Wait</div>
                                    <div style="font-size: 12px; font-weight: 700; color: #11998e;" id="stat-avg">0s</div>
                                </div>
                                <div style="padding: 6px; background: var(--input-bg); border-radius: 8px; border: 1px solid var(--border);">
                                    <div style="opacity: 0.7; margin-bottom: 2px; font-size: 10px;">Total Uptime</div>
                                    <div style="font-size: 12px; font-weight: 700; color: #11998e;" id="stat-total-uptime">0s</div>
                                </div>
                                <div style="padding: 6px; background: var(--input-bg); border-radius: 8px; border: 1px solid var(--border);">
                                    <div style="opacity: 0.7; margin-bottom: 2px; font-size: 10px;">Frequency</div>
                                    <div style="font-size: 12px; font-weight: 700; color: #11998e;" id="stat-frequency">0.0 /min</div>
                                </div>
                            </div>
                            <div class="section-title">📈 Reload Graph</div>
                            <div id="reload-graph-container"></div>
                            <div class="section-title">📋 Recent Reloads</div>
                            <div id="reload-history-container" style="max-height: 120px; overflow-y: auto;">
                                <div class="history-empty">No reloads yet</div>
                            </div>
                        </div>
                    </div>
                    
                    <div class="modal-footer">
                        <button class="btn-save" id="save-btn">SAVE</button>
                        <button class="preset-btn" id="reset-stats-btn" style="padding: 8px; font-size: 11px;">Reset Stats</button>
                    </div>
                </div>
                
                <div class="modal-tabs">
                    <button class="tab-btn active" data-tab="timing" title="Timing / Presets">⏱</button>
                    <button class="tab-btn" data-tab="controls" title="Controls">🎛</button>
                    <button class="tab-btn" data-tab="stats" title="Stats">📊</button>
                </div>
            `;
 
            container.append(mainBtn, themeBtn, settingsBtn);
            wrapper.append(container, settingsModal);
            shadow.append(style, wrapper);
 
            // ========== INPUT ELEMENTS CACHE ==========
            // Cache frequently accessed input elements
            opacitySlider = settingsModal.querySelector('#opacity-slider');
            const minInput = settingsModal.querySelector('#min-input');
            const maxInput = settingsModal.querySelector('#max-input');
            const minDisplay = settingsModal.querySelector('#min-display');
            const maxDisplay = settingsModal.querySelector('#max-display');
            const maxReloadsInput = settingsModal.querySelector('#max-reloads-input');
            const maxReloadsDisplay = settingsModal.querySelector('#max-reloads-display');
            const opacityDisplay = settingsModal.querySelector('#opacity-display');
            
            // ========== INPUT VALIDATION & SYNC ==========
            // Validate min/max inputs and update displays in real-time
            minInput.oninput = (e) => {
                const minVal = parseInt(e.target.value), maxVal = parseInt(maxInput.value);
                if (minVal > maxVal) { minInput.value = maxVal; minDisplay.textContent = maxVal; }
                else minDisplay.textContent = minVal;
            };
            
            maxInput.oninput = (e) => {
                const minVal = parseInt(minInput.value), maxVal = parseInt(e.target.value);
                if (maxVal < minVal) { maxInput.value = minVal; maxDisplay.textContent = minVal; }
                else maxDisplay.textContent = maxVal;
            };
            
            maxReloadsInput.oninput = (e) => { maxReloadsDisplay.textContent = e.target.value; };
            opacitySlider.oninput = (e) => { 
                wrapper.style.opacity = e.target.value;
                opacityDisplay.textContent = Math.round(e.target.value * 100);
            };
            
            // ========== TAB SWITCHING ==========
            // Handle tab navigation in settings modal
            const tabButtons = settingsModal.querySelectorAll('.tab-btn');
            const tabContents = settingsModal.querySelectorAll('.tab-content');
            
            tabButtons.forEach(btn => {
                btn.addEventListener('click', (e) => {
                    const tabName = btn.dataset.tab;
                    tabButtons.forEach(b => b.classList.remove('active'));
                    tabContents.forEach(content => content.classList.remove('active'));
                    btn.classList.add('active');
                    settingsModal.querySelector(`#tab-${tabName}`).classList.add('active');
                });
            });
        }
 
        // ==================== UI STATE MANAGEMENT ====================
        // Updates button display and favicon based on current running state
        function updateUIState() {
            if (isPausedByInactivity) {
                mainBtn.className = 'btn btn-main safety';
                mainBtn.innerHTML = '<span class="icon">💤</span><span>NO ACTIVITY</span>';
            } else if (isPausedBySafety) {
                mainBtn.className = 'btn btn-main safety';
                mainBtn.innerHTML = '<span class="icon">🛡</span><span>PAUSED</span>';
            } else if (isRunning) {
                mainBtn.className = 'btn btn-main active';
                mainBtn.innerHTML = `<span class="icon">⏸</span><span>${remainingTime}s</span>`;
            } else {
                mainBtn.className = 'btn btn-main';
                mainBtn.innerHTML = '<span class="icon">▶</span><span>Start</span>';
            }
            updateFavicon(remainingTime);
            document.title = (isRunning && !isPausedBySafety && !isPausedByInactivity) 
                ? `[${remainingTime}s] ` + document.title.replace(/\[\d+s\]\s/, '')
                : document.title.replace(/\[\d+s\]\s/, '');
        }
 
        // ==================== RELOAD CYCLE ====================
        // Main loop: calculate wait time, start timers, handle reloads
        function startCycle() {
            const { min, max } = getSettings();
            let waitTime = Math.max(1, Math.floor(Math.random() * (max - min) + min));
            const jitterResult = applyJitter(waitTime);
            waitTime = jitterResult.newValue;
            
            // FIXED: ALWAYS show jitter notification when jitter mode is enabled
            if (jitterResult.applied) {
                const sign = jitterResult.jitterAmount > 0 ? '+' : '';
                const jitterDisplay = jitterResult.jitterAmount === 0 ? '±0' : sign + jitterResult.jitterAmount;
                showToast(`⚡ Jitter: ${jitterDisplay}s (${waitTime}s total)`);
            }
            
            remainingTime = maxRemainingTime = waitTime;
            stats.waitTimes.push(waitTime);
            
            if (!sessionTimerId) {
                sessionTimerId = setInterval(() => {
                    stats.totalRunningTime += 1;
                    saveStats();
                }, 1000);
            }
            
            isPausedByInactivity = false;
            resetInactivityTimeout();
            updateUIState();
            
            // ========== COUNTDOWN & RELOAD TIMER ==========
            // Decrements every second and triggers reload when reaching zero
            timerId = setInterval(() => {
                if (!isPausedBySafety && !isPausedByInactivity) {
                    remainingTime--;
                    updateUIState();
                    if (remainingTime <= 0) {
                        const maxReloadsLimit = parseInt(localStorage.getItem('maxReloadsLimit')) || 0;
                        const currentCount = getReloadCount();
                        
                        if (maxReloadsLimit > 0 && currentCount >= maxReloadsLimit) {
                            showToast('🛑 Max reached');
                            isRunning = false;
                            clearInterval(timerId);
                            updateUIState();
                            return;
                        }
                        
                        incrementReloadCount();
                        addReloadToHistory(maxRemainingTime);
                        window.location.reload();
                    }
                }
            }, 1000);
        }
 
        // ==================== SAFETY CHECK ====================
        // Prevents reload while user is typing in input fields (form protection)
        const checkSafety = (e) => {
            const active = document.activeElement;
            const isTyping = active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' || active.isContentEditable);
            isPausedBySafety = isRunning && isTyping;
            updateUIState();
        };
 
        // ==================== INITIALIZATION ====================
        // Creates UI, loads settings, restores state
        createShadowUI();
        host.setAttribute('theme', localStorage.getItem(themeKey) || 'dark');
        themeBtn.innerHTML = host.getAttribute('theme') === 'dark' ? '<span class="icon">☀️</span>' : '<span class="icon">🌙</span>';
 
        // ==================== BUTTON EVENT LISTENERS ====================
        // Main button: toggle start/stop
        mainBtn.onclick = () => {
            isRunning = !isRunning;
            isPausedBySafety = isPausedByInactivity = false;
            localStorage.setItem(storageKey, isRunning);
            updateUIState();
            if (isRunning) {
                uptimeData = loadUptimeData();
                updateUptimeData();
                startCycle();
            } else {
                clearInterval(timerId);
                clearTimeout(inactivityTimeout);
                if (sessionTimerId) clearInterval(sessionTimerId), sessionTimerId = null;
                // Accumulate session uptime to total
                if (uptimeData.sessionStartTime) {
                    const sessionElapsed = Math.floor((Date.now() - uptimeData.sessionStartTime) / 1000);
                    uptimeData.totalUptimeSeconds = (uptimeData.totalUptimeSeconds || 0) + sessionElapsed;
                    uptimeData.sessionStartTime = null;
                    saveUptimeData();
                }
            }
        };
 
        // ==================== DOCUMENT EVENT LISTENERS ====================
        // Safety: detect typing, activity tracking, keyboard shortcuts
        document.addEventListener('focusin', checkSafety);
        document.addEventListener('focusout', () => setTimeout(checkSafety, 100));
        document.addEventListener('mousemove', recordActivity);
        document.addEventListener('keypress', recordActivity);
        document.addEventListener('click', recordActivity);
        document.addEventListener('keydown', (e) => {
            if (e.shiftKey && e.key.toUpperCase() === 'T') {
                e.preventDefault();
                mainBtn.click();
            }
        });
        
        // ==================== LOAD PERSISTENT DATA ====================
        // Restore stats and reload count on page load
        loadStats();
        stats.totalReloads = getReloadCount();
        uptimeData = loadUptimeData();
 
        // ==================== THEME TOGGLE ==========
        // Switch between dark and light themes
        themeBtn.onclick = () => {
            const next = host.getAttribute('theme') === 'dark' ? 'light' : 'dark';
            host.setAttribute('theme', next);
            localStorage.setItem(themeKey, next);
            themeBtn.innerHTML = next === 'dark' ? '<span class="icon">☀️</span>' : '<span class="icon">🌙</span>';
        };
 
        // ==================== SETTINGS MODAL HANDLERS ====================
        // Open/close settings and sync UI with stored values
        settingsBtn.onclick = () => {
            settingsModal.style.display = settingsModal.style.display === 'flex' ? 'none' : 'flex';
            const s = getSettings();
            
            shadow.querySelector('#min-input').value = s.min;
            shadow.querySelector('#max-input').value = s.max;
            shadow.querySelector('#min-display').textContent = s.min;
            shadow.querySelector('#max-display').textContent = s.max;
            
            const inactivityToggle = shadow.querySelector('#inactivity-toggle');
            const jitterToggle = shadow.querySelector('#jitter-toggle');
            const inactivityBadge = shadow.querySelector('#inactivity-badge');
            const jitterBadge = shadow.querySelector('#jitter-badge');
            const inactivityCard = shadow.querySelector('#inactivity-card');
            const jitterCard = shadow.querySelector('#jitter-card');
            
            inactivityToggle.checked = localStorage.getItem('inactivityPauseEnabled') === 'true';
            jitterToggle.checked = localStorage.getItem('jitterMode') === 'true';
            
            const updateBadges = () => {
                const updateBadge = (toggle, badge, card) => {
                    if (toggle.checked) {
                        badge.textContent = 'ON';
                        badge.classList.add('active');
                        card.classList.add('active');
                    } else {
                        badge.textContent = 'OFF';
                        badge.classList.remove('active');
                        card.classList.remove('active');
                    }
                };
                updateBadge(inactivityToggle, inactivityBadge, inactivityCard);
                updateBadge(jitterToggle, jitterBadge, jitterCard);
            };
            
            updateBadges();
            inactivityToggle.oninput = jitterToggle.oninput = updateBadges;
            
            const maxReloadsValue = localStorage.getItem('maxReloadsLimit') || '0';
            shadow.querySelector('#max-reloads-input').value = maxReloadsValue;
            shadow.querySelector('#max-reloads-display').textContent = maxReloadsValue;
            
            opacitySlider.value = s.opacity;
            shadow.querySelector('#opacity-display').textContent = Math.round(s.opacity * 100);
            
            // ========== NEW: Calculate and display session statistics with uptime ==========
            const sessionTime = (stats.totalRunningTime || 0) * 1000;
            const minutes = Math.floor(sessionTime / 60000);
            const seconds = Math.floor((sessionTime % 60000) / 1000);
            
            // Calculate total uptime (persistent across sessions)
            let currentSessionElapsed = 0;
            if (isRunning && uptimeData.sessionStartTime) {
                currentSessionElapsed = Math.floor((Date.now() - uptimeData.sessionStartTime) / 1000);
            }
            const totalUptime = (uptimeData.totalUptimeSeconds || 0) + currentSessionElapsed;
            const uptimeMinutes = Math.floor(totalUptime / 60);
            const uptimeSeconds = totalUptime % 60;
            
            const avgWait = stats.waitTimes.length > 0 
                ? Math.round(stats.waitTimes.reduce((a, b) => a + b) / stats.waitTimes.length) 
                : 0;
            
            // Calculate reload frequency
            const frequencyData = calculateReloadFrequency();
            
            shadow.querySelector('#stat-reloads').textContent = stats.totalReloads;
            shadow.querySelector('#stat-max-limit').textContent = maxReloadsValue;
            shadow.querySelector('#stat-session').textContent = `${minutes}m ${seconds}s`;
            shadow.querySelector('#stat-avg').textContent = avgWait + 's';
            shadow.querySelector('#stat-total-uptime').textContent = `${uptimeMinutes}m ${uptimeSeconds}s`;
            shadow.querySelector('#stat-frequency').textContent = frequencyData.formattedFrequency;
            
            // Draw reload graph
            drawReloadGraph('reload-graph-container');
            
            // ========== NEW: Populate reload history ==========
            const history = loadReloadHistory();
            const historyContainer = shadow.querySelector('#reload-history-container');
            
            if (history.length === 0) {
                historyContainer.innerHTML = '<div class="history-empty">No reloads yet</div>';
            } else {
                historyContainer.innerHTML = history.map((item, index) => `
                    <div class="history-item">
                        <span class="history-time">${item.timestamp}</span>
                        <span class="history-wait">waited ${item.waitTime}s</span>
                    </div>
                `).reverse().join('');
            }
        };
 
        // ==================== PRESET BUTTONS ====================
        // Quick settings presets for common timing scenarios
        const updateSliderDisplay = (minVal, maxVal) => {
            shadow.querySelector('#min-input').value = minVal;
            shadow.querySelector('#max-input').value = maxVal;
            shadow.querySelector('#min-display').textContent = minVal;
            shadow.querySelector('#max-display').textContent = maxVal;
        };
 
        shadow.querySelector('#preset-1-5').onclick = () => updateSliderDisplay(1, 5);
        shadow.querySelector('#preset-5-15').onclick = () => updateSliderDisplay(5, 15);
        shadow.querySelector('#preset-5-10').onclick = () => updateSliderDisplay(5, 10);
        shadow.querySelector('#preset-10-15').onclick = () => updateSliderDisplay(10, 15);
        shadow.querySelector('#preset-15-20').onclick = () => updateSliderDisplay(15, 20);
        shadow.querySelector('#preset-20-30').onclick = () => updateSliderDisplay(20, 30);
        shadow.querySelector('#preset-1-5m').onclick = () => updateSliderDisplay(60, 300);
 
        // ==================== SAVE SETTINGS ====================
        // Persist all user settings to localStorage
        shadow.querySelector('#save-btn').onclick = () => {
            localStorage.setItem('minDelay', shadow.querySelector('#min-input').value);
            localStorage.setItem('maxDelay', shadow.querySelector('#max-input').value);
            localStorage.setItem(opacityKey, opacitySlider.value);
            localStorage.setItem('inactivityPauseEnabled', shadow.querySelector('#inactivity-toggle').checked);
            localStorage.setItem('jitterMode', shadow.querySelector('#jitter-toggle').checked);
            
            const maxReloadsValue = parseInt(shadow.querySelector('#max-reloads-input').value) || 0;
            localStorage.setItem('maxReloadsLimit', maxReloadsValue.toString());
            
            stats.maxReloadsLimit = maxReloadsValue;
            stats.jitterMode = shadow.querySelector('#jitter-toggle').checked;
            saveStats();
            showToast('✅ Settings saved!');
            settingsModal.style.display = 'none';
        };
 
        // ==================== RESET STATISTICS ====================
        // Clear all reload counts and timing history
        shadow.querySelector('#reset-stats-btn').onclick = () => {
            if (confirm('Reset all statistics?')) {
                resetReloadCount();
                stats = {
                    totalReloads: 0,
                    totalRunningTime: 0,
                    waitTimes: [],
                    maxReloadsLimit: 0,
                    jitterMode: false,
                    sessionStartTime: null
                };
                saveStats();
                saveReloadHistory([]);
                uptimeData = {
                    totalUptimeSeconds: 0,
                    sessionStartTime: null,
                    lastUpdated: null
                };
                saveUptimeData();
                shadow.querySelector('#stat-reloads').textContent = '0';
                shadow.querySelector('#stat-max-limit').textContent = '0';
                shadow.querySelector('#stat-session').textContent = '0m 0s';
                shadow.querySelector('#stat-avg').textContent = '0s';
                shadow.querySelector('#stat-total-uptime').textContent = '0s';
                shadow.querySelector('#stat-frequency').textContent = '0.0 /min';
                shadow.querySelector('#reload-graph-container').innerHTML = '<div style="text-align: center; opacity: 0.6; padding: 20px; font-size: 11px;">No reload data yet</div>';
                shadow.querySelector('#reload-history-container').innerHTML = '<div class="history-empty">No reloads yet</div>';
                showToast('🔄 Stats reset!');
            }
        };
 
        // ==================== RESTORE SESSION STATE ====================
        // Resume auto-reload if it was running on previous page load
        if (localStorage.getItem(storageKey) === 'true') { 
            isRunning = true;
            uptimeData = loadUptimeData();
            startCycle(); 
        }
    }
 
    LiveReload();
})();