(function() { 'use strict'; /* 1. Scroll-reveal */ if ('IntersectionObserver' in window) { const revealObserver = new IntersectionObserver(function(entries) { entries.forEach(function(entry) { if (entry.isIntersecting) { entry.target.classList.add('is-visible'); revealObserver.unobserve(entry.target); } }); }, { threshold: 0.1, rootMargin: '0px 0px -40px 0px' }); document.querySelectorAll('.reveal').forEach(function(el) { revealObserver.observe(el); }); } else { document.querySelectorAll('.reveal').forEach(function(el) { el.classList.add('is-visible'); }); } /* 2. Counter animation */ function animateCounter(el) { const target = parseInt(el.getAttribute('data-target'), 10); const suffix = el.getAttribute('data-suffix') || ''; const prefix = el.getAttribute('data-prefix') || ''; const duration = 1600; const start = performance.now(); function update(now) { const p = Math.min((now - start) / duration, 1); const eased = 1 - Math.pow(1 - p, 3); el.textContent = prefix + Math.round(eased * target).toLocaleString() + suffix; if (p < 1) requestAnimationFrame(update); } requestAnimationFrame(update); } if ('IntersectionObserver' in window) { const counterObserver = new IntersectionObserver(function(entries) { entries.forEach(function(entry) { if (entry.isIntersecting && !entry.target.classList.contains('counted')) { entry.target.classList.add('counted'); animateCounter(entry.target); } }); }, { threshold: 0.5 }); document.querySelectorAll('[data-counter]').forEach(function(el) { counterObserver.observe(el); }); } /* 3. Animated hero canvas background */ const heroCanvas = document.getElementById('hero-canvas'); if (heroCanvas) { const ctx = heroCanvas.getContext('2d'); let W, H, shapes = []; function resize() { W = heroCanvas.width = heroCanvas.offsetWidth; H = heroCanvas.height = heroCanvas.offsetHeight; } function init() { shapes = []; const count = Math.max(6, Math.floor(W / 140)); for (let i = 0; i < count; i++) { shapes.push({ x: Math.random() * W, y: Math.random() * H, r: 24 + Math.random() * 70, vx: (Math.random() - 0.5) * 0.25, vy: (Math.random() - 0.5) * 0.18, alpha: 0.025 + Math.random() * 0.045, sides: Math.random() > 0.5 ? 6 : 0 }); } } function drawShape(s) { ctx.save(); ctx.globalAlpha = s.alpha; ctx.strokeStyle = '#E8174B'; ctx.lineWidth = 0.8; ctx.beginPath(); if (s.sides === 0) { ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2); } else { for (let i = 0; i < s.sides; i++) { const a = (Math.PI * 2 / s.sides) * i - Math.PI / 6; const px = s.x + s.r * Math.cos(a); const py = s.y + s.r * Math.sin(a); i === 0 ? ctx.moveTo(px, py) : ctx.lineTo(px, py); } ctx.closePath(); } ctx.stroke(); ctx.restore(); } let running = true; function draw() { if (!running) return; ctx.clearRect(0, 0, W, H); shapes.forEach(function(s) { s.x += s.vx; s.y += s.vy; if (s.x < -s.r) s.x = W + s.r; if (s.x > W + s.r) s.x = -s.r; if (s.y < -s.r) s.y = H + s.r; if (s.y > H + s.r) s.y = -s.r; drawShape(s); }); requestAnimationFrame(draw); } // Respect prefers-reduced-motion if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) { resize(); init(); draw(); window.addEventListener('resize', function() { resize(); init(); }, { passive: true }); } } /* 4. Typed text */ const typedEl = document.getElementById('typed-text'); if (typedEl && !window.matchMedia('(prefers-reduced-motion: reduce)').matches) { const words = typedEl.getAttribute('data-words').split('|'); let wi = 0, ci = 0, del = false; function type() { const w = words[wi]; if (!del) { typedEl.textContent = w.slice(0, ++ci); if (ci === w.length) { setTimeout(function() { del = true; type(); }, 2400); return; } setTimeout(type, 80); } else { typedEl.textContent = w.slice(0, --ci); if (ci === 0) { del = false; wi = (wi + 1) % words.length; } setTimeout(type, ci === 0 ? 500 : 50); } } setTimeout(type, 1000); } /* 5. Nav compact on scroll */ const nav = document.querySelector('.nav'); if (nav) { window.addEventListener('scroll', function() { nav.classList.toggle('nav--scrolled', window.scrollY > 50); }, { passive: true }); } /* 6. Ripple on CTA buttons */ document.querySelectorAll('.btn--primary, .btn--teal, .nav__cta').forEach(function(btn) { btn.style.position = 'relative'; btn.style.overflow = 'hidden'; btn.addEventListener('click', function(e) { const rect = btn.getBoundingClientRect(); const d = Math.max(rect.width, rect.height) * 2; const ripple = document.createElement('span'); Object.assign(ripple.style, { position: 'absolute', width: d + 'px', height: d + 'px', borderRadius: '50%', background: 'rgba(255,255,255,0.25)', transform: 'scale(0)', pointerEvents: 'none', left: (e.clientX - rect.left - d/2) + 'px', top: (e.clientY - rect.top - d/2) + 'px', animation: 'ripple-anim 0.55s ease-out forwards' }); btn.appendChild(ripple); setTimeout(function() { if (ripple.parentNode) ripple.parentNode.removeChild(ripple); }, 600); }); }); /* 7. Stagger children of .stagger-parent on reveal */ if ('IntersectionObserver' in window) { const staggerObserver = new IntersectionObserver(function(entries) { entries.forEach(function(entry) { if (entry.isIntersecting) { const children = entry.target.querySelectorAll('.stagger-item'); children.forEach(function(child, i) { setTimeout(function() { child.classList.add('is-visible'); }, i * 110); }); staggerObserver.unobserve(entry.target); } }); }, { threshold: 0.1 }); document.querySelectorAll('.stagger-parent').forEach(function(el) { staggerObserver.observe(el); }); } })();