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} */ 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} */ 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 = `

`; 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} */ 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 = ' Unblock This Site'; blockBtn.classList.add('blocked'); } else { blockBtn.innerHTML = ' Block This Site'; blockBtn.classList.remove('blocked'); } if (hasTimeLimit) { limitBtn.innerHTML = ' Edit Time Limit'; } else { limitBtn.innerHTML = ' 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} */ 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;');