Spaces:
Running
Running
| // Mobile menu toggle | |
| const hamburger = document.querySelector('.hamburger'); | |
| const navMenu = document.querySelector('.nav-menu'); | |
| hamburger.addEventListener('click', () => { | |
| hamburger.classList.toggle('active'); | |
| navMenu.classList.toggle('active'); | |
| }); | |
| document.querySelectorAll('.nav-link').forEach(n => n.addEventListener('click', () => { | |
| hamburger.classList.remove('active'); | |
| navMenu.classList.remove('active'); | |
| })); | |
| // FAQ Accordion | |
| const faqItems = document.querySelectorAll('.faq-item'); | |
| faqItems.forEach(item => { | |
| const question = item.querySelector('.faq-question'); | |
| question.addEventListener('click', () => { | |
| // Close all other items | |
| faqItems.forEach(otherItem => { | |
| if (otherItem !== item) { | |
| otherItem.classList.remove('active'); | |
| } | |
| }); | |
| // Toggle current item | |
| item.classList.toggle('active'); | |
| }); | |
| }); | |
| // Smooth scroll for navigation links | |
| document.querySelectorAll('a[href^="#"]').forEach(anchor => { | |
| anchor.addEventListener('click', function (e) { | |
| e.preventDefault(); | |
| const target = document.querySelector(this.getAttribute('href')); | |
| if (target) { | |
| target.scrollIntoView({ | |
| behavior: 'smooth', | |
| block: 'start' | |
| }); | |
| } | |
| }); | |
| }); | |
| // Scroll animations | |
| const observerOptions = { | |
| threshold: 0.1, | |
| rootMargin: '0px 0px -50px 0px' | |
| }; | |
| const observer = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| entry.target.classList.add('fade-in'); | |
| } | |
| }); | |
| }, observerOptions); | |
| // Observe all sections | |
| document.querySelectorAll('section').forEach(section => { | |
| observer.observe(section); | |
| }); | |
| // Parallax effect for hero section | |
| window.addEventListener('scroll', () => { | |
| const scrolled = window.pageYOffset; | |
| const hero = document.querySelector('.hero'); | |
| if (hero) { | |
| hero.style.transform = `translateY(${scrolled * 0.5}px)`; | |
| } | |
| }); | |
| // Counter animation for stats | |
| const animateCounter = (element, target, duration = 2000) => { | |
| let start = 0; | |
| const increment = target / (duration / 16); | |
| const timer = setInterval(() => { | |
| start += increment; | |
| if (start >= target) { | |
| element.textContent = element.textContent.includes('.') ? | |
| target.toFixed(1) : target + (element.textContent.includes('+') ? '+' : ''); | |
| clearInterval(timer); | |
| } else { | |
| element.textContent = element.textContent.includes('.') ? | |
| start.toFixed(1) : Math.floor(start) + (element.textContent.includes('+') ? '+' : ''); | |
| } | |
| }, 16); | |
| }; | |
| // Initialize counters when visible | |
| const statNumbers = document.querySelectorAll('.stat-number'); | |
| const statObserver = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting && !entry.target.classList.contains('animated')) { | |
| entry.target.classList.add('animated'); | |
| const text = entry.target.textContent; | |
| const number = parseFloat(text.replace(/[^0-9.]/g, '')); | |
| animateCounter(entry.target, number); | |
| } | |
| }); | |
| }, { threshold: 0.5 }); | |
| statNumbers.forEach(stat => statObserver.observe(stat)); | |
| // Button ripple effect | |
| document.querySelectorAll('.btn').forEach(button => { | |
| button.addEventListener('click', function(e) { | |
| const ripple = document.createElement('span'); | |
| const rect = this.getBoundingClientRect(); | |
| const size = Math.max(rect.width, rect.height); | |
| const x = e.clientX - rect.left - size / 2; | |
| const y = e.clientY - rect.top - size / 2; | |
| ripple.style.width = ripple.style.height = size + 'px'; | |
| ripple.style.left = x + 'px'; | |
| ripple.style.top = y + 'px'; | |
| ripple.classList.add('ripple'); | |
| this.appendChild(ripple); | |
| setTimeout(() => { | |
| ripple.remove(); | |
| }, 600); | |
| }); | |
| }); | |
| // Add ripple styles dynamically | |
| const style = document.createElement('style'); | |
| style.textContent = ` | |
| .btn { | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .ripple { | |
| position: absolute; | |
| border-radius: 50%; | |
| background: rgba(255, 255, 255, 0.5); | |
| transform: scale(0); | |
| animation: ripple-animation 0.6s ease-out; | |
| pointer-events: none; | |
| } | |
| @keyframes ripple-animation { | |
| to { | |
| transform: scale(4); | |
| opacity: 0; | |
| } | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| // Form submission handling (if any forms are added) | |
| document.addEventListener('submit', (e) => { | |
| if (e.target.tagName === 'FORM') { | |
| e.preventDefault(); | |
| // Add your form handling logic here | |
| console.log('Form submitted'); | |
| } | |
| }); | |
| // Loading animation for page | |
| window.addEventListener('load', () => { | |
| document.body.classList.add('loaded'); | |
| }); | |
| // Add loading styles | |
| const loadingStyle = document.createElement('style'); | |
| loadingStyle.textContent = ` | |
| body { | |
| opacity: 0; | |
| transition: opacity 0.5s ease; | |
| } | |
| body.loaded { | |
| opacity: 1; | |
| } | |
| `; | |
| document.head.appendChild(loadingStyle); |