// Programmatic spark generation for homepage logo (function() { const config = { numSparks: 9, minSize: 1.5, maxSize: 3.5, minRadius: 180, maxRadius: 350, minDuration: 23, maxDuration: 49, pathComplexity: 17, behindLogoFraction: 0.75, minOpacity: 0.5, maxOpacity: 0.8, staggerAnimations: true }; function random(min, max) { return Math.random() * (max - min) + min; } function generatePath(radius, complexity) { const points = []; for (let i = 0; i <= complexity; i++) { const angle = (i / complexity) * Math.PI * 2; const r = random(radius * 0.8, radius * 1.2); const x = Math.cos(angle) * r + random(-50, 50); const y = Math.sin(angle) * r + random(-50, 50); points.push({ x, y, scale: random(0.7, 1.3) }); } return points; } function createKeyframes(id, path, isBehind) { const steps = path.length; let keyframes = '@keyframes spark' + id + ' {\n'; path.forEach((point, i) => { const percent = (i / (steps - 1)) * 100; const opacity = (i > 1 && i < steps - 2) ? random(config.minOpacity, config.maxOpacity) : 0; let opacityValue = opacity; if (isBehind) { const hiddenStart = random(30, 45); const hiddenEnd = random(55, 70); if (percent > hiddenStart && percent < hiddenEnd) { opacityValue = 0; } } keyframes += ` ${percent.toFixed(1)}% {\n`; keyframes += ` transform: translate(-50%, -50%) translateX(${point.x.toFixed(1)}px) translateY(${point.y.toFixed(1)}px) scale(${point.scale.toFixed(2)});\n`; keyframes += ` opacity: ${opacityValue.toFixed(2)};\n`; keyframes += ` }\n`; }); keyframes += '}\n'; return keyframes; } function initSparks() { const logoFigure = document.querySelector('body:has(.background-container) article.glass figure:first-of-type'); if (!logoFigure) return; let allKeyframes = ''; const sparks = []; for (let i = 0; i < config.numSparks; i++) { const spark = document.createElement('span'); spark.className = 'logo-spark'; spark.dataset.sparkId = i; const size = random(config.minSize, config.maxSize); const duration = random(config.minDuration, config.maxDuration); const radius = random(config.minRadius, config.maxRadius); const isBehind = Math.random() < config.behindLogoFraction; const delay = config.staggerAnimations ? random(0, config.maxDuration) : 0; spark.style.cssText = ` position: absolute; width: ${size}px; height: ${size}px; border-radius: 50%; background: radial-gradient(circle, rgba(255, 255, 255, 0.9), rgba(74, 158, 255, 0.6), transparent); box-shadow: 0 0 8px rgba(74, 158, 255, 0.8), 0 0 4px rgba(255, 255, 255, 0.6); top: 50%; left: 50%; pointer-events: none; z-index: ${isBehind ? -1 : 1}; animation: spark${i} ${duration}s linear infinite; animation-delay: -${delay.toFixed(2)}s; `; const path = generatePath(radius, config.pathComplexity); allKeyframes += createKeyframes(i, path, isBehind); sparks.push(spark); } // Inject keyframes const style = document.createElement('style'); style.textContent = allKeyframes; document.head.appendChild(style); // Inject sparks sparks.forEach(spark => logoFigure.appendChild(spark)); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initSparks); } else { initSparks(); } })();