// DOM Content Loaded document.addEventListener('DOMContentLoaded', function() { // Initialize all components initNavigation(); initScrollAnimations(); initCounters(); initTestimonials(); initTabs(); initAccordion(); initFAQ(); initContactForm(); initSmoothScrolling(); }); // Navigation functionality function initNavigation() { const navToggle = document.getElementById('nav-toggle'); const navMenu = document.getElementById('nav-menu'); const navLinks = document.querySelectorAll('.nav-link'); const header = document.getElementById('header'); // Mobile menu toggle if (navToggle && navMenu) { // Added navMenu check navToggle.addEventListener('click', () => { navMenu.classList.toggle('active'); const icon = navToggle.querySelector('i'); icon.classList.toggle('fa-bars'); icon.classList.toggle('fa-times'); }); } // Close mobile menu when clicking on links navLinks.forEach(link => { link.addEventListener('click', () => { if (navMenu && navMenu.classList.contains('active')) { // Added navMenu check navMenu.classList.remove('active'); const icon = navToggle.querySelector('i'); icon.classList.add('fa-bars'); icon.classList.remove('fa-times'); } }); }); // Header scroll effect if (header) { // Added header check window.addEventListener('scroll', () => { if (window.scrollY > 80) { // Adjusted scroll threshold header.style.background = 'rgba(255, 255, 255, 0.98)'; header.style.boxShadow = '0 2px 15px rgba(0, 0, 0, 0.1)'; } else { header.style.background = 'rgba(255, 255, 255, 0.95)'; header.style.boxShadow = 'none'; } }); } } // Scroll animations function initScrollAnimations() { const animatedElements = document.querySelectorAll('section, .service-card, .stat-item, .step, .cert-item, .tech-item, .testimonial, .standard-card, .team-member, .case-study, .pricing-card, .differentiator-card, .insight-card, .client-logo'); // Added new card types const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const observer = new IntersectionObserver((entries, observer) => { // Added observer to unobserve entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('fade-in-up'); observer.unobserve(entry.target); // Unobserve after animation } }); }, observerOptions); animatedElements.forEach(el => { observer.observe(el); }); } // Counter animations function initCounters() { const counters = document.querySelectorAll('.stat-number'); if (counters.length === 0) return; const animateCounter = (counter) => { const target = parseInt(counter.getAttribute('data-target')); const duration = 2000; // Animation duration in ms const increment = target / (duration / 16); // 16ms per frame (approx 60fps) let current = 0; const updateCounter = () => { if (current < target) { current += increment; counter.textContent = Math.ceil(Math.min(current, target)); // Ensure it doesn't exceed target requestAnimationFrame(updateCounter); } else { counter.textContent = target; // Final set to target } }; updateCounter(); }; const counterObserver = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { animateCounter(entry.target); observer.unobserve(entry.target); } }); }, { threshold: 0.5 }); // Trigger when 50% visible counters.forEach(counter => { counterObserver.observe(counter); }); } // Testimonials slider function initTestimonials() { const testimonials = document.querySelectorAll('.testimonial'); const navBtns = document.querySelectorAll('.testimonial-nav .nav-btn'); // More specific selector if (testimonials.length === 0 || navBtns.length === 0) return; let currentSlide = 0; let slideInterval; function showSlide(index) { testimonials.forEach((testimonial, i) => { testimonial.classList.toggle('active', i === index); }); navBtns.forEach((btn, i) => { btn.classList.toggle('active', i === index); }); } function nextSlide() { currentSlide = (currentSlide + 1) % testimonials.length; showSlide(currentSlide); } navBtns.forEach((btn, index) => { btn.addEventListener('click', () => { currentSlide = index; showSlide(currentSlide); clearInterval(slideInterval); // Stop auto-rotate on manual navigation slideInterval = setInterval(nextSlide, 7000); // Restart auto-rotate }); }); showSlide(currentSlide); // Show initial slide slideInterval = setInterval(nextSlide, 5000); // Auto-rotate testimonials } // Tabs functionality function initTabs() { const tabContainers = document.querySelectorAll('.expertise-tabs'); // Target specific tab systems if (tabContainers.length === 0) return; tabContainers.forEach(container => { const tabBtns = container.querySelectorAll('.tab-btn'); const tabPanels = container.querySelectorAll('.tab-panel'); tabBtns.forEach(btn => { btn.addEventListener('click', () => { const targetTab = btn.getAttribute('data-tab'); tabBtns.forEach(b => b.classList.remove('active')); tabPanels.forEach(panel => panel.classList.remove('active')); btn.classList.add('active'); const targetPanel = container.querySelector(`#${targetTab}`); if (targetPanel) targetPanel.classList.add('active'); }); }); // Activate the first tab by default if (tabBtns.length > 0 && tabPanels.length > 0) { tabBtns[0].classList.add('active'); tabPanels[0].classList.add('active'); } }); } // Accordion functionality function initAccordion() { const accordionContainers = document.querySelectorAll('.assessment-accordion'); // Target specific accordions if (accordionContainers.length === 0) return; accordionContainers.forEach(container => { const accordionItems = container.querySelectorAll('.accordion-item'); accordionItems.forEach(item => { const header = item.querySelector('.accordion-header'); if (!header) return; header.addEventListener('click', () => { const isActive = item.classList.contains('active'); // Option: Close other items when one is opened // accordionItems.forEach(accItem => { // if (accItem !== item) accItem.classList.remove('active'); // }); item.classList.toggle('active'); // Toggle current item }); }); // Optionally open the first item by default // if(accordionItems.length > 0) accordionItems[0].classList.add('active'); }); } // FAQ functionality (similar to accordion, but can be separate if behavior differs) function initFAQ() { const faqLists = document.querySelectorAll('.faq-list'); if (faqLists.length === 0) return; faqLists.forEach(list => { const faqItems = list.querySelectorAll('.faq-item'); faqItems.forEach(item => { const question = item.querySelector('.faq-question'); if (!question) return; question.addEventListener('click', () => { const isActive = item.classList.contains('active'); // Option: Close other FAQ items when one is opened // faqItems.forEach(faqItem => { // if (faqItem !== item) faqItem.classList.remove('active'); // }); item.classList.toggle('active'); }); }); }); } // Contact form functionality function initContactForm() { const contactForm = document.getElementById('contact-form'); if (contactForm) { contactForm.addEventListener('submit', function(e) { e.preventDefault(); const formData = new FormData(contactForm); const data = Object.fromEntries(formData.entries()); // Use .entries() for wider compatibility if (validateForm(data, contactForm)) { // Pass form for targeted error display const submitBtn = contactForm.querySelector('button[type="submit"]'); const originalText = submitBtn.textContent; submitBtn.textContent = 'Sending...'; submitBtn.disabled = true; // Simulate form submission (replace with actual API call) setTimeout(() => { showNotification('Thank you! Your assessment request has been submitted. We will contact you within 24 hours.', 'success'); contactForm.reset(); // Clear any previous error messages clearFormErrors(contactForm); submitBtn.textContent = originalText; submitBtn.disabled = false; }, 2000); } }); } } // Form validation function validateForm(data, formElement) { clearFormErrors(formElement); // Clear previous errors const errors = []; if (!data.name || data.name.trim().length < 2) { errors.push({ field: 'name', message: 'Please enter a valid name (at least 2 characters).' }); } if (!data.email || !isValidEmail(data.email)) { errors.push({ field: 'email', message: 'Please enter a valid email address.' }); } if (!data.service) { errors.push({ field: 'service', message: 'Please select a service type.' }); } // Optional: phone number validation if (data.phone && !/^\+?[0-9\s-()]{7,20}$/.test(data.phone.trim())) { errors.push({ field: 'phone', message: 'Please enter a valid phone number.' }); } if (errors.length > 0) { const errorMessages = errors.map(err => err.message); showNotification(errorMessages.join('\n'), 'error'); // Display errors next to fields (optional) errors.forEach(err => displayFieldError(formElement, err.field, err.message)); return false; } return true; } function clearFormErrors(formElement) { const errorMessages = formElement.querySelectorAll('.form-error-message'); errorMessages.forEach(msg => msg.remove()); const errorInputs = formElement.querySelectorAll('.input-error'); errorInputs.forEach(input => input.classList.remove('input-error')); } function displayFieldError(formElement, fieldName, message) { const inputField = formElement.querySelector(`[name="${fieldName}"]`); if (inputField) { inputField.classList.add('input-error'); // const errorSpan = document.createElement('span'); // errorSpan.className = 'form-error-message'; // errorSpan.textContent = message; // errorSpan.style.color = '#e74c3c'; // errorSpan.style.fontSize = '0.85rem'; // inputField.parentNode.insertBefore(errorSpan, inputField.nextSibling); } } // Email validation function isValidEmail(email) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(String(email).toLowerCase()); } // Notification system function showNotification(message, type = 'info') { const existingNotification = document.querySelector('.notification-popup'); // Changed class if (existingNotification) { existingNotification.remove(); } const notification = document.createElement('div'); notification.className = `notification-popup notification-${type}`; // Changed class notification.innerHTML = `