// Background canvas animation (moved out of index.html so CSP can forbid inline scripts) const canvas = document.getElementById('bgCanvas'); const ctx = canvas.getContext('2d'); function resize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } resize(); window.addEventListener('resize', resize); const particles = []; for (let i = 0; i < 70; i++) { particles.push({ x: Math.random() * window.innerWidth, y: Math.random() * window.innerHeight, vx: (Math.random() - 0.5) * 0.3, vy: (Math.random() - 0.5) * 0.3, r: Math.random() * 1.5 + 0.4, opacity: Math.random() * 0.5 + 0.1, color: Math.random() > 0.5 ? '#d94f00' : '#ff6a00' }); } function drawGrid() { ctx.strokeStyle = '#0f0505'; ctx.lineWidth = 0.5; const gs = 60; for (let x = 0; x < canvas.width; x += gs) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, canvas.height); ctx.stroke(); } for (let y = 0; y < canvas.height; y += gs) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(canvas.width, y); ctx.stroke(); } } let frame = 0; function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#060606'; ctx.fillRect(0, 0, canvas.width, canvas.height); drawGrid(); const t = frame * 0.002; const W = canvas.width, H = canvas.height; const glows = [ { cx: 0.15 * W + Math.sin(t * 0.7) * 60, cy: 0.85 * H + Math.cos(t * 0.5) * 40, r: 260, color: 'rgba(139,20,0,0.13)' }, { cx: 0.85 * W + Math.sin(t * 0.4) * 50, cy: 0.15 * H + Math.cos(t * 0.6) * 40, r: 220, color: 'rgba(200,70,0,0.09)' }, { cx: 0.5 * W + Math.sin(t * 0.3) * 30, cy: 0.6 * H + Math.cos(t * 0.4) * 20, r: 300, color: 'rgba(100,15,0,0.06)' }, ]; glows.forEach(g => { const grad = ctx.createRadialGradient(g.cx, g.cy, 0, g.cx, g.cy, g.r); grad.addColorStop(0, g.color); grad.addColorStop(1, 'rgba(0,0,0,0)'); ctx.fillStyle = grad; ctx.fillRect(0, 0, W, H); }); for (let i = 0; i < particles.length; i++) { const p = particles[i]; p.x += p.vx; p.y += p.vy; if (p.x < 0) p.x = W; if (p.x > W) p.x = 0; if (p.y < 0) p.y = H; if (p.y > H) p.y = 0; ctx.beginPath(); ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2); ctx.fillStyle = p.color; ctx.globalAlpha = p.opacity; ctx.fill(); ctx.globalAlpha = 1; for (let j = i + 1; j < particles.length; j++) { const p2 = particles[j]; const dx = p.x - p2.x, dy = p.y - p2.y; const dist = Math.sqrt(dx * dx + dy * dy); if (dist < 110) { ctx.beginPath(); ctx.moveTo(p.x, p.y); ctx.lineTo(p2.x, p2.y); ctx.strokeStyle = '#d94f00'; ctx.globalAlpha = (1 - dist / 110) * 0.07; ctx.lineWidth = 0.5; ctx.stroke(); ctx.globalAlpha = 1; } } } frame++; requestAnimationFrame(animate); } animate();