v3 · 37 files

ProjectElly/popup/popup.js 33 KB Raw
import '../lib/presets.js';

// ELLY PREMIUM THEME - ENHANCED INTERACTIONS WITH FUNCTIONALITY
const APP_SHORTNAME = 'Elly';

/**
 * Redirects to Access PIN setup/unlock and closes this window.
 * @param {boolean} configured
 * @returns {Promise<void>}
 */
async function redirectToAccessPin(configured) {
    const path = configured ? 'pin/pin.html' : 'pin/pin.html?mode=setup';
    await chrome.windows.create({
        url: chrome.runtime.getURL(path),
        type: 'popup',
        width: 560,
        height: 900,
        focused: true
    });
    window.close();
}

/**
 * Requires configured + verified Access PIN before showing popup controls.
 * @returns {Promise<boolean>}
 */
async function requireAccessGate() {
    try {
        const status = await chrome.runtime.sendMessage({ action: 'getAppStatus' });
        const configured = !!(status?.configured ?? status?.pinSet);
        const verified = !!status?.verified;
        if (!configured || !verified) {
            await redirectToAccessPin(configured);
            return false;
        }
        return true;
    } catch (err) {
        console.error('Elly popup access gate:', err);
        await redirectToAccessPin(false);
        return false;
    }
}

/**
 * Creates the Tabler-style prompt dialog once (minimal popup HTML may omit it).
 * @returns {HTMLDialogElement}
 */
function ensureEllyPopupPromptDialog() {
    let dlg = document.getElementById('ellyPopupPromptModal');
    if (dlg) {
        return /** @type {HTMLDialogElement} */ (dlg);
    }
    dlg = document.createElement('dialog');
    dlg.id = 'ellyPopupPromptModal';
    dlg.className = 'ellyp-modal';
    dlg.setAttribute('aria-labelledby', 'ellyPopupPromptTitle');
    const inner = document.createElement('div');
    inner.className = 'ellyp-modal-inner';
    inner.innerHTML = `
        <h3 id="ellyPopupPromptTitle" class="ellyp-modal-title"></h3>
        <p id="ellyPopupPromptMessage" class="ellyp-modal-message"></p>
        <input type="number" id="ellyPopupPromptInput" class="ellyp-modal-input" min="1" step="1" />
        <div class="ellyp-modal-actions">
            <button type="button" id="ellyPopupPromptCancel" class="ellyp-btn ellyp-btn-secondary">Cancel</button>
            <button type="button" id="ellyPopupPromptOk" class="ellyp-btn ellyp-btn-primary"><i class="ti ti-check"></i> OK</button>
        </div>
    `;
    dlg.appendChild(inner);
    document.body.appendChild(dlg);
    return /** @type {HTMLDialogElement} */ (dlg);
}

/**
 * Tabler-style prompt (replaces window.prompt).
 * @param {{ title: string, message: string, defaultValue?: string, min?: number }} opts
 * @returns {Promise<string|null>}
 */
function openEllyPopupPromptModal(opts) {
    const dlg = ensureEllyPopupPromptDialog();
    const titleEl = document.getElementById('ellyPopupPromptTitle');
    const msgEl = document.getElementById('ellyPopupPromptMessage');
    const input = document.getElementById('ellyPopupPromptInput');
    const cancelBtn = document.getElementById('ellyPopupPromptCancel');
    const okBtn = document.getElementById('ellyPopupPromptOk');
    if (!titleEl || !msgEl || !input || !cancelBtn || !okBtn) {
        return Promise.resolve(null);
    }
    titleEl.replaceChildren();
    const icon = document.createElement('i');
    icon.className = 'ti ti-clock';
    titleEl.appendChild(icon);
    titleEl.appendChild(document.createTextNode(` ${opts.title}`));
    msgEl.textContent = opts.message;
    input.value = opts.defaultValue ?? '60';
    input.min = String(opts.min ?? 1);

    return new Promise((resolve) => {
        let done = false;
        const cleanup = () => {
            okBtn.removeEventListener('click', onOk);
            cancelBtn.removeEventListener('click', onCancel);
            dlg.removeEventListener('cancel', onEsc);
            input.removeEventListener('keydown', onKey);
        };
        const finish = (/** @type {string|null} */ v) => {
            if (done) {
                return;
            }
            done = true;
            dlg.close();
            cleanup();
            resolve(v);
        };
        const onOk = () => finish(input.value.trim());
        const onCancel = () => finish(null);
        const onEsc = (e) => {
            e.preventDefault();
            finish(null);
        };
        const onKey = (e) => {
            if (e.key === 'Enter') {
                e.preventDefault();
                onOk();
            }
        };
        okBtn.addEventListener('click', onOk);
        cancelBtn.addEventListener('click', onCancel);
        dlg.addEventListener('cancel', onEsc);
        input.addEventListener('keydown', onKey);
        dlg.showModal();
        input.select();
        input.focus();
    });
}

// Global state
let currentTab = null;
let isExtensionEnabled = true;

/**
 * Boot UI when the popup document is ready. ES modules + import() can run after DOMContentLoaded
 * in edge cases; always run if the document is already interactive.
 */
async function bootPopupUi() {
    const ok = await requireAccessGate();
    if (!ok) {
        return;
    }
    addRequiredStyles();
    initializePopup();
    initializePremiumEffects();
    initializeButtonEffects();
    initializeCardAnimations();
    initializeParticleSystem();
}

if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => {
        bootPopupUi().catch((err) => console.error(err));
    });
} else {
    bootPopupUi().catch((err) => console.error(err));
}

// Initialize popup functionality
async function initializePopup() {
    console.log('Initializing popup...');

    try {
        // Check if extension context is valid
        if (!chrome.runtime?.id) {
            showError('Extension context invalid. Please refresh the page.');
            return;
        }

        // Get current tab
        const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
        currentTab = tabs[0];

        const currentUrlEl = document.getElementById('currentUrl');
        if (currentTab && currentTab.url) {
            try {
                const url = new URL(currentTab.url);
                if (currentUrlEl) {
                    currentUrlEl.textContent = url.hostname || currentTab.url;
                }
                await checkSiteStatus(url.hostname);
            } catch (parseErr) {
                if (currentUrlEl) {
                    currentUrlEl.textContent = currentTab.url;
                }
                console.warn('Elly popup: could not parse tab URL', parseErr);
            }
        } else if (currentUrlEl) {
            currentUrlEl.textContent = 'No active tab';
        }

        await loadStatistics();
        await checkActiveTimeLimits();
        await loadExtensionState();
    } catch (error) {
        console.error('Error initializing popup:', error);
        const msg = error && error.message ? String(error.message) : '';
        if (msg.includes('Extension context invalidated')) {
            showError('Extension reloaded. Please refresh the page.');
        } else {
            showError('Error loading popup. Please try again.');
        }
    } finally {
        setupEventListeners();
    }
}

// Check if current site is blocked or limited
async function checkSiteStatus(domain) {
    try {
        const data = await chrome.storage.sync.get(['blockedSites', 'limitedSites']);
        const blockedSites = data.blockedSites || [];
        const limitedSites = data.limitedSites || {};

        // Check if domain is in blocked list
        const isBlocked = blockedSites.some(site =>
            domain.includes(site.replace('*.', '')) || site.includes(domain)
        );

        // Check if domain has time limit
        const hasTimeLimit = limitedSites[domain] ||
            Object.keys(limitedSites).some(key => domain.includes(key));

        const blockBtn = document.getElementById('blockCurrent');
        const limitBtn = document.getElementById('limitCurrent');
        if (!blockBtn || !limitBtn) {
            return;
        }

        if (isBlocked) {
            blockBtn.innerHTML = '<i class="ti ti-ban premium"></i> Unblock This Site';
            blockBtn.classList.add('blocked');
        } else {
            blockBtn.innerHTML = '<i class="ti ti-ban premium"></i> Block This Site';
            blockBtn.classList.remove('blocked');
        }

        if (hasTimeLimit) {
            limitBtn.innerHTML = '<i class="ti ti-clock premium"></i> Edit Time Limit';
        } else {
            limitBtn.innerHTML = '<i class="ti ti-clock premium"></i> Set Time Limit';
        }

    } catch (error) {
        console.error('Error checking site status:', error);
    }
}

// Load statistics from storage
async function loadStatistics() {
    try {
        const data = await chrome.storage.sync.get(['blockedSites', 'statistics', 'limitedSites']);

        const blockedCount = data.blockedSites?.length || 0;
        const blockedCountEl = document.getElementById('blockedCount');
        if (blockedCountEl) {
            blockedCountEl.textContent = blockedCount;
        }

        const stats = data.statistics || {};
        const totalTimeSaved = Object.values(stats).reduce((sum, day) => sum + (day.timeSaved || 0), 0);
        const timeSavedEl = document.getElementById('timeSaved');
        if (timeSavedEl) {
            timeSavedEl.textContent = `${Math.floor(totalTimeSaved / 3600)}h`;
        }

        const activeLimits = Object.keys(data.limitedSites || {}).length;
        const activeLimitsEl = document.getElementById('activeLimits');
        if (activeLimitsEl) {
            activeLimitsEl.textContent = activeLimits;
        }

    } catch (error) {
        console.error('Error loading statistics:', error);
    }
}

// Check for active time limits
async function checkActiveTimeLimits() {
    try {
        const data = await chrome.storage.sync.get(['limitedSites']);
        const limitedSites = data.limitedSites || {};

        if (currentTab && currentTab.url) {
            let domain = '';
            try {
                domain = new URL(currentTab.url).hostname;
            } catch (e) {
                return;
            }
            const limit = limitedSites[domain];

            if (limit) {
                const widget = document.getElementById('timeLimitWidget');
                const timerSite = document.getElementById('timerSite');
                if (widget) {
                    widget.style.display = 'block';
                }
                if (timerSite) {
                    timerSite.textContent = domain;
                }
            }
        }
    } catch (error) {
        console.error('Error checking time limits:', error);
    }
}

// Load extension enabled state
async function loadExtensionState() {
    try {
        const data = await chrome.storage.sync.get(['settings']);
        isExtensionEnabled = data.settings?.extensionEnabled !== false;

        const toggle = document.getElementById('toggleEnabled');
        if (toggle) {
            toggle.checked = isExtensionEnabled;
        }
    } catch (error) {
        console.error('Error loading extension state:', error);
    }
}

let popupListenersBound = false;

// Setup event listeners for all buttons
function setupEventListeners() {
    if (popupListenersBound) {
        return;
    }
    popupListenersBound = true;

    document.getElementById('blockCurrent')?.addEventListener('click', () => {
        toggleBlockSite();
    });

    document.getElementById('limitCurrent')?.addEventListener('click', () => {
        setTimeLimit();
    });

    document.getElementById('btnDashboard')?.addEventListener('click', () => {
        chrome.tabs.create({ url: chrome.runtime.getURL('options/options.html#stats') });
    });

    document.getElementById('btnSettings')?.addEventListener('click', () => {
        chrome.tabs.create({ url: chrome.runtime.getURL('options/options.html') });
    });

    document.getElementById('btnFocus')?.addEventListener('click', () => {
        startFocusSession();
    });

    document.getElementById('btnQuickAdd')?.addEventListener('click', () => {
        quickAddUrl();
    });

    document.getElementById('quickUrl')?.addEventListener('keypress', (e) => {
        if (e.key === 'Enter') quickAddUrl();
    });

    // Preset buttons
    document.querySelectorAll('.preset-btn').forEach(btn => {
        btn.addEventListener('click', (e) => {
            const preset = e.currentTarget?.dataset?.preset;
            if (preset) {
                addPreset(preset);
            }
        });
    });

    document.getElementById('toggleEnabled')?.addEventListener('change', (e) => {
        toggleExtension(e.target.checked);
    });
}

// Toggle block/unblock current site
async function toggleBlockSite() {
    if (!currentTab || !currentTab.url) {
        showError('No active tab found');
        return;
    }

    try {
        const url = new URL(currentTab.url);
        const domain = url.hostname;

        const data = await chrome.storage.sync.get(['blockedSites', 'settings']);
        let blockedSites = data.blockedSites || [];
        const settings = data.settings || {};
        const pinLock = !!(settings.blocklistPinLock || settings.strictMode);

        // Check if already blocked
        const isBlocked = blockedSites.some(site =>
            domain.includes(site.replace('*.', '')) || site.includes(domain)
        );

        if (isBlocked) {
            let pin = null;
            if (pinLock) {
                pin = await promptParentPinForPopup(
                    'Enter your Parent PIN to unblock this site.'
                );
                if (pin == null) {
                    return;
                }
            }

            const sitesToRemove = blockedSites.filter(site =>
                domain.includes(site.replace('*.', '')) || site.includes(domain)
            );
            const response = await chrome.runtime.sendMessage({
                action: 'removeFromBlocklist',
                sites: sitesToRemove,
                pin
            });
            if (!response?.success) {
                showError(response?.error || 'Failed to unblock site');
                return;
            }
            blockedSites = response.blockedSites || [];
            showSuccessNotification('Site unblocked!');
        } else {
            // Add to blocklist
            if (!blockedSites.includes(domain)) {
                blockedSites.push(domain);
                await chrome.storage.sync.set({ blockedSites });
                showSuccessNotification('Site blocked!');
            }
        }

        await checkSiteStatus(domain);
        await loadStatistics();

        // Notify background script to update blocking
        chrome.runtime.sendMessage({
            action: 'updateBlocklist',
            blockedSites: blockedSites
        });

    } catch (error) {
        console.error('Error toggling block:', error);
        showError('Failed to update blocklist');
    }
}

/**
 * Minimal Parent PIN prompt for the popup (8–12 digits; verified by background).
 * @param {string} message
 * @returns {Promise<string|null>}
 */
async function promptParentPinForPopup(message) {
    const entered = window.prompt(`${message}\n(8–12 digits)`, '');
    if (entered == null) {
        return null;
    }
    const trimmed = String(entered).trim();
    if (!/^\d{8,12}$/.test(trimmed)) {
        showError('PIN must be 8–12 digits');
        return null;
    }
    return trimmed;
}

// Set time limit for current site
async function setTimeLimit() {
    if (!currentTab || !currentTab.url) {
        showError('No active tab found');
        return;
    }

    try {
        const url = new URL(currentTab.url);
        const domain = url.hostname;

        const rawMinutes = await openEllyPopupPromptModal({
            title: `${APP_SHORTNAME}: Time limit`,
            message: `Enter daily time limit for ${domain} (in minutes).`,
            defaultValue: '60',
            min: 1
        });
        if (rawMinutes === null) {
            return;
        }
        const minutes = parseInt(rawMinutes, 10);
        if (!rawMinutes || isNaN(minutes) || minutes <= 0) {
            return;
        }

        const data = await chrome.storage.sync.get(['limitedSites', 'settings']);
        const settings = data.settings || {};
        const pinLock = !!(settings.blocklistPinLock || settings.strictMode);
        if (pinLock) {
            const pin = await promptParentPinForPopup(
                'Enter your Parent PIN to set a time limit.'
            );
            if (pin == null) {
                return;
            }
            const verified = await chrome.runtime.sendMessage({
                action: 'verifyParentPinForBlocklist',
                pin
            });
            if (!verified?.success) {
                showError(verified?.error || 'Incorrect Parent PIN');
                return;
            }
        }
        const limitedSites = data.limitedSites || {};

        limitedSites[domain] = {
            limitMinutes: parseInt(minutes),
            added: new Date().toISOString()
        };

        await chrome.storage.sync.set({ limitedSites });
        showSuccessNotification(`${APP_SHORTNAME}: Time limit set: ${minutes} minutes/day`);
        await checkSiteStatus(domain);
        await loadStatistics();

    } catch (error) {
        console.error('Error setting time limit:', error);
        showError(`${APP_SHORTNAME}: Failed to set time limit`);
    }
}

// Quick add URL
async function quickAddUrl() {
    const input = document.getElementById('quickUrl');
    const url = input.value.trim();

    if (!url) {
        showError('Please enter a URL');
        return;
    }

    try {
        const data = await chrome.storage.sync.get(['blockedSites']);
        const blockedSites = data.blockedSites || [];

        if (!blockedSites.includes(url)) {
            blockedSites.push(url);
            await chrome.storage.sync.set({ blockedSites });
            showSuccessNotification('Site added to blocklist!');
            input.value = '';
            await loadStatistics();

            // Notify background script
            chrome.runtime.sendMessage({
                action: 'updateBlocklist',
                blockedSites: blockedSites
            });
        } else {
            showError('Site already in blocklist');
        }

    } catch (error) {
        console.error('Error adding site:', error);
        showError('Failed to add site');
    }
}

// Add preset sites
async function addPreset(preset) {
    const presets = typeof window !== 'undefined' && window.ELLY_PRESETS
        ? window.ELLY_PRESETS
        : {
            social: [
                '*.facebook.com', '*.twitter.com', '*.x.com', '*.instagram.com', '*.tiktok.com', '*.reddit.com',
                '*.linkedin.com', '*.discord.com', 'discord.com', 'vrilchat.com', '*.vrilchat.com', 'vrilone.com', '*.vrilone.com',
            ],
            entertainment: ['*.youtube.com', '*.netflix.com', '*.twitch.tv', '*.hulu.com', '*.disneyplus.com'],
            shopping: ['*.amazon.com', '*.ebay.com', '*.aliexpress.com', '*.etsy.com', '*.bestbuy.com'],
            prohibited: [],
        };

    const presetUrls = presets[preset];
    if (!presetUrls) return;

    try {
        const data = await chrome.storage.sync.get(['blockedSites']);
        const blockedSites = data.blockedSites || [];
        const newUrls = presetUrls.filter(url => !blockedSites.includes(url));

        if (newUrls.length > 0) {
            blockedSites.push(...newUrls);
            await chrome.storage.sync.set({ blockedSites });
            showSuccessNotification(`Added ${newUrls.length} ${preset} sites!`);
            await loadStatistics();

            // Notify background script
            chrome.runtime.sendMessage({
                action: 'updateBlocklist',
                blockedSites: blockedSites
            });
        } else {
            showError(`All ${preset} sites are already blocked`);
        }

    } catch (error) {
        console.error('Error adding preset:', error);
        showError('Failed to add preset');
    }
}

// Start focus session
async function startFocusSession() {
    try {
        const rawDuration = await openEllyPopupPromptModal({
            title: 'Focus session',
            message: 'Enter focus session duration (minutes).',
            defaultValue: '25',
            min: 1
        });
        if (rawDuration === null) {
            return;
        }
        const duration = parseInt(rawDuration, 10);
        if (!rawDuration || isNaN(duration) || duration <= 0) {
            return;
        }

        await chrome.runtime.sendMessage({
            action: 'startFocusSession',
            duration
        });

        showSuccessNotification(`Focus session started for ${duration} minutes!`);

    } catch (error) {
        console.error('Error starting focus session:', error);
        showError('Failed to start focus session');
    }
}

// Toggle extension on/off — Parent PIN always required to disable
async function toggleExtension(enabled) {
    try {
        if (!enabled) {
            const pin = await promptParentPinForPopup(
                'Enter your Parent PIN to disable Elly.'
            );
            if (pin == null) {
                const toggle = document.getElementById('toggleEnabled');
                if (toggle) {
                    toggle.checked = true;
                }
                return;
            }
            const verified = await chrome.runtime.sendMessage({
                action: 'setExtensionEnabled',
                enabled: false,
                pin
            });
            if (!verified?.success) {
                showError(verified?.error || 'Incorrect Parent PIN');
                const toggle = document.getElementById('toggleEnabled');
                if (toggle) {
                    toggle.checked = true;
                }
                return;
            }
            isExtensionEnabled = false;
            chrome.action.setBadgeText({ text: 'OFF' });
            chrome.action.setBadgeBackgroundColor({ color: '#ef4444' });
            showSuccessNotification('Extension disabled!');
            return;
        }

        const enabledResp = await chrome.runtime.sendMessage({
            action: 'setExtensionEnabled',
            enabled: true
        });
        if (!enabledResp?.success) {
            showError(enabledResp?.error || 'Failed to enable extension');
            const toggle = document.getElementById('toggleEnabled');
            if (toggle) {
                toggle.checked = false;
            }
            return;
        }

        isExtensionEnabled = true;

        chrome.action.setBadgeText({
            text: ''
        });

        chrome.action.setBadgeBackgroundColor({
            color: '#10b981'
        });

        showSuccessNotification('Extension enabled!');

    } catch (error) {
        console.error('Error toggling extension:', error);
        showError('Failed to update extension state');
    }
}

// Helper functions
function showSuccessNotification(message) {
    // Add "Elly says:" prefix if not already included
    const prefixedMessage = message.startsWith('Elly:') || message.startsWith('Elly says:')
        ? message
        : `Elly says: ${message}`;

    const notification = document.createElement('div');
    notification.textContent = prefixedMessage;
    notification.style.cssText = `
        position: fixed;
        top: 20px;
        left: 50%;
        transform: translateX(-50%) translateY(-100px);
        background: linear-gradient(135deg, #10b981, #059669);
        color: white;
        padding: 12px 24px;
        border-radius: 12px;
        font-weight: 600;
        font-size: 13px;
        z-index: 10000;
        box-shadow: 0 10px 40px rgba(16, 185, 129, 0.4);
        transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    `;

    document.body.appendChild(notification);

    setTimeout(() => {
        notification.style.transform = 'translateX(-50%) translateY(0)';
    }, 100);

    setTimeout(() => {
        notification.style.transform = 'translateX(-50%) translateY(-100px)';
        setTimeout(() => notification.remove(), 300);
    }, 2000);
}

function showError(message) {
    // Add "Elly says:" prefix if not already included
    const prefixedMessage = message.startsWith('Elly:') || message.startsWith('Elly says:')
        ? message
        : `Elly says: ${message}`;

    const notification = document.createElement('div');
    notification.textContent = prefixedMessage;
    notification.style.cssText = `
        position: fixed;
        top: 20px;
        left: 50%;
        transform: translateX(-50%) translateY(-100px);
        background: linear-gradient(135deg, #ef4444, #dc2626);
        color: white;
        padding: 12px 24px;
        border-radius: 12px;
        font-weight: 600;
        font-size: 13px;
        z-index: 10000;
        box-shadow: 0 10px 40px rgba(239, 68, 68, 0.4);
        transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    `;

    document.body.appendChild(notification);

    setTimeout(() => {
        notification.style.transform = 'translateX(-50%) translateY(0)';
    }, 100);

    setTimeout(() => {
        notification.style.transform = 'translateX(-50%) translateY(-100px)';
        setTimeout(() => notification.remove(), 300);
    }, 2000);
}

// Initialize all premium effects
function initializePremiumEffects() {
    addStaggeredAnimations();
    initializeSmoothScrolling();
    addMicroInteractions();
}

// Staggered entrance animations
function addStaggeredAnimations() {
    const elements = document.querySelectorAll('.container.premium > *');
    elements.forEach((el, index) => {
        el.style.opacity = '0';
        el.style.transform = 'translateY(20px)';

        setTimeout(() => {
            el.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
            el.style.opacity = '1';
            el.style.transform = 'translateY(0)';
        }, index * 50);
    });
}

// Enhanced button interactions
function initializeButtonEffects() {
    const buttons = document.querySelectorAll('button.premium');

    buttons.forEach(button => {
        // Ripple effect on click
        button.addEventListener('click', (e) => {
            createRipple(e, button);
        });

        // Magnetic effect on hover
        button.addEventListener('mousemove', (e) => {
            const rect = button.getBoundingClientRect();
            const x = e.clientX - rect.left - rect.width / 2;
            const y = e.clientY - rect.top - rect.height / 2;

            button.style.transform = `translate(${x * 0.1}px, ${y * 0.1}px) scale(1.02)`;
        });

        button.addEventListener('mouseleave', () => {
            button.style.transform = 'translate(0, 0) scale(1)';
        });
    });
}

// Create ripple effect
function createRipple(e, button) {
    const ripple = document.createElement('span');
    const rect = button.getBoundingClientRect();
    const size = Math.max(rect.width, rect.height) * 2;
    const x = e.clientX - rect.left - size / 2;
    const y = e.clientY - rect.top - size / 2;

    ripple.style.cssText = `
        position: absolute;
        width: ${size}px;
        height: ${size}px;
        border-radius: 50%;
        background: rgba(255, 255, 255, 0.4);
        top: ${y}px;
        left: ${x}px;
        pointer-events: none;
        transform: scale(0);
        opacity: 1;
        animation: rippleEffect 0.6s ease-out;
    `;

    button.style.position = 'relative';
    button.style.overflow = 'hidden';
    button.appendChild(ripple);

    setTimeout(() => ripple.remove(), 600);
}

// Card hover animations
function initializeCardAnimations() {
    const stats = document.querySelectorAll('.stat.premium.holographic');

    stats.forEach(stat => {
        stat.addEventListener('mouseenter', () => {
            stat.style.transform = 'translateY(-4px) scale(1.02)';
        });

        stat.addEventListener('mouseleave', () => {
            stat.style.transform = 'translateY(0) scale(1)';
        });
    });

    // Current site card animation
    const currentSite = document.querySelector('.current-site.premium');
    if (currentSite) {
        currentSite.addEventListener('mouseenter', () => {
            currentSite.style.transform = 'translateY(-2px)';
            currentSite.style.borderColor = 'rgba(139, 92, 246, 0.4)';
        });

        currentSite.addEventListener('mouseleave', () => {
            currentSite.style.transform = 'translateY(0)';
            currentSite.style.borderColor = 'rgba(255, 255, 255, 0.1)';
        });
    }
}

// Particle system
function initializeParticleSystem() {
    const particlesContainer = document.querySelector('.particles');
    if (!particlesContainer) return;

    createFloatingParticles(particlesContainer);
}

function createFloatingParticles(container) {
    const colors = ['rgba(99, 102, 241, 0.3)', 'rgba(139, 92, 246, 0.3)', 'rgba(217, 70, 239, 0.3)'];
    const particleCount = 15;

    for (let i = 0; i < particleCount; i++) {
        const particle = document.createElement('div');
        const size = Math.random() * 3 + 1;
        const color = colors[Math.floor(Math.random() * colors.length)];
        const startX = Math.random() * 100;
        const startY = Math.random() * 100;
        const duration = Math.random() * 15 + 10;
        const delay = Math.random() * 5;

        particle.style.cssText = `
            position: absolute;
            width: ${size}px;
            height: ${size}px;
            background: ${color};
            border-radius: 50%;
            left: ${startX}%;
            top: ${startY}%;
            filter: blur(1px);
            box-shadow: 0 0 ${size * 4}px ${color};
            animation: floatParticle ${duration}s ease-in-out ${delay}s infinite;
            pointer-events: none;
        `;

        container.appendChild(particle);
    }
}

// Smooth scrolling
function initializeSmoothScrolling() {
    const container = document.querySelector('.container.premium');
    if (!container) return;

    container.style.scrollBehavior = 'smooth';
}

// Micro-interactions
function addMicroInteractions() {
    // Input focus effects
    const inputs = document.querySelectorAll('input[type="text"].premium');
    inputs.forEach(input => {
        input.addEventListener('focus', () => {
            input.style.transform = 'scale(1.02)';
        });

        input.addEventListener('blur', () => {
            input.style.transform = 'scale(1)';
        });
    });

    // Toggle switch interaction
    const toggleSlider = document.querySelector('.toggle-slider.premium');
    if (toggleSlider) {
        toggleSlider.addEventListener('click', () => {
            toggleSlider.style.transform = 'scale(0.95)';
            setTimeout(() => {
                toggleSlider.style.transform = 'scale(1)';
            }, 100);
        });
    }

    // Preset button interactions
    const presetBtns = document.querySelectorAll('.preset-btn.premium');
    presetBtns.forEach(btn => {
        btn.addEventListener('click', () => {
            btn.style.transform = 'scale(0.95)';
            setTimeout(() => {
                btn.style.transform = 'scale(1)';
            }, 100);
        });
    });
}

// Add required CSS animations
function addRequiredStyles() {
    // Ripple animation
    const rippleStyle = document.createElement('style');
    rippleStyle.textContent = `
        @keyframes rippleEffect {
            to {
                transform: scale(1);
                opacity: 0;
            }
        }
        @keyframes floatParticle {
            0%, 100% {
                transform: translate(0, 0);
                opacity: 0;
            }
            25% {
                opacity: 1;
            }
            50% {
                transform: translate(${Math.random() * 100 - 50}px, ${Math.random() * 100 - 50}px);
                opacity: 0.8;
            }
            75% {
                opacity: 1;
            }
        }
        @keyframes gradientShift {
            0% { background-position: 0% 50%; }
            50% { background-position: 100% 50%; }
            100% { background-position: 0% 50%; }
        }
    `;
    document.head.appendChild(rippleStyle);
}

console.log('%c✨ ELLY PREMIUM POPUP LOADED ✨',
    'background: linear-gradient(135deg, #6366f1, #8b5cf6, #d946ef); color: white; padding: 12px 24px; border-radius: 8px; font-size: 14px; font-weight: bold;');