/* ===== Growing Now — animated canvas background (perf-minded) ===== */ (function () { const PALETTE = ['#3FAE4A', '#1F66B0', '#F7941E', '#2E8BD6', '#7FCB87', '#F6B65C']; function AnimatedBackground({ variant = 'blobs' }) { const ref = React.useRef(null); React.useEffect(() => { const canvas = ref.current; const ctx = canvas.getContext('2d'); let raf, t = 0, running = true; const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches; let W, H, DPR; let blobs = [], parts = []; function resize() { DPR = Math.min(window.devicePixelRatio || 1, 1.6); W = canvas.clientWidth; H = canvas.clientHeight; canvas.width = W * DPR; canvas.height = H * DPR; ctx.setTransform(DPR, 0, 0, DPR, 0, 0); build(); } function build() { const big = variant === 'aurora'; const n = variant === 'blobs' ? 6 : variant === 'aurora' ? 5 : 3; blobs = Array.from({ length: n }, (_, i) => ({ bx: Math.random() * W, by: Math.random() * H, r: (big ? 0.55 : 0.34) * Math.min(W, H) * (0.7 + Math.random() * 0.7), color: PALETTE[i % PALETTE.length], ph: Math.random() * 6.28, sp: 0.12 + Math.random() * 0.18, ax: (big ? 0.28 : 0.18) * W, ay: (big ? 0.22 : 0.16) * H, stretch: big ? 1.7 : 1, })); if (variant === 'particles') { const count = Math.min(70, Math.round(W * H / 22000)); parts = Array.from({ length: count }, () => ({ x: Math.random() * W, y: Math.random() * H, vx: (Math.random() - 0.5) * 0.25, vy: -0.15 - Math.random() * 0.35, r: 1.4 + Math.random() * 2.8, color: PALETTE[Math.floor(Math.random() * PALETTE.length)], a: 0.25 + Math.random() * 0.45, })); } } function draw() { ctx.clearRect(0, 0, W, H); ctx.globalCompositeOperation = 'source-over'; for (const b of blobs) { const x = b.bx + Math.sin(t * b.sp + b.ph) * b.ax; const y = b.by + Math.cos(t * b.sp * 0.8 + b.ph) * b.ay; ctx.save(); ctx.translate(x, y); if (b.stretch !== 1) ctx.scale(b.stretch, 1 / b.stretch); const g = ctx.createRadialGradient(0, 0, 0, 0, 0, b.r); g.addColorStop(0, b.color + (variant === 'aurora' ? '4a' : '55')); g.addColorStop(0.6, b.color + '20'); g.addColorStop(1, b.color + '00'); ctx.fillStyle = g; ctx.beginPath(); ctx.arc(0, 0, b.r, 0, 6.2832); ctx.fill(); ctx.restore(); } if (variant === 'particles') { for (const p of parts) { p.x += p.vx; p.y += p.vy; if (p.y < -10) { p.y = H + 10; p.x = Math.random() * W; } if (p.x < -10) p.x = W + 10; if (p.x > W + 10) p.x = -10; ctx.beginPath(); ctx.fillStyle = p.color; ctx.globalAlpha = p.a; ctx.arc(p.x, p.y, p.r, 0, 6.2832); ctx.fill(); } ctx.globalAlpha = 1; } } function loop() { if (!running) return; t += reduce ? 0 : 0.016; draw(); if (reduce) return; // draw once, stop raf = requestAnimationFrame(loop); } const onVis = () => { running = !document.hidden; if (running) { running = true; loop(); } }; resize(); loop(); window.addEventListener('resize', resize); document.addEventListener('visibilitychange', onVis); return () => { running = false; cancelAnimationFrame(raf); window.removeEventListener('resize', resize); document.removeEventListener('visibilitychange', onVis); }; }, [variant]); return React.createElement('canvas', { ref, id: 'bg-canvas' }); } window.AnimatedBackground = AnimatedBackground; })();