/** * SUSTAINABLE GLOBE PRIVATE LIMITED * Enterprise Multi-Page Router & Interactive Engine (Vanilla TS/JS) */ document.addEventListener('DOMContentLoaded', () => { initImageLoaders(); initScrollProgress(); initNavbarScroll(); initPageRouting(); initAboutTabs(); initAnimatedCounters(); initIntersectionObserver(); initProductsFilter(); initFaqAccordion(); initTestimonialSlider(); initModals(); initContactForm(); initBackToTop(); initTalentedTeamModal(); initTeamFilter(); initHeroMessageRotator(); }); /* Image Loader & Fallback Guard */ function initImageLoaders() { const images = document.querySelectorAll('img'); images.forEach(img => { // Check if image is already broken on initial load if (img.complete && img.naturalWidth === 0) { applyImageFallback(img); } img.addEventListener('error', () => { applyImageFallback(img); }); }); } function applyImageFallback(img: HTMLImageElement) { // If an image fails to load, gracefully fallback to hero background image if (img.dataset.fallbackApplied) return; img.dataset.fallbackApplied = 'true'; img.src = '/assets/images/hero_engineering_bg_1785319936150.jpg'; } /* 1. Scroll Progress Bar */ function initScrollProgress() { const progressBar = document.getElementById('scroll-progress'); if (!progressBar) return; window.addEventListener('scroll', () => { const windowHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight; const scrolled = (window.scrollY / windowHeight) * 100; progressBar.style.width = `${scrolled}%`; }); } /* 2. Navbar Scroll Transformation & Mobile Toggle */ function initNavbarScroll() { const navbar = document.querySelector('.navbar'); if (!navbar) return; window.addEventListener('scroll', () => { if (window.scrollY > 40) { navbar.classList.add('scrolled'); } else { navbar.classList.remove('scrolled'); } }); const mobileBtn = document.querySelector('.mobile-menu-btn'); const navMenu = document.querySelector('.nav-menu'); if (mobileBtn && navMenu) { mobileBtn.addEventListener('click', () => { const isExpanded = navMenu.classList.toggle('active'); mobileBtn.setAttribute('aria-expanded', String(isExpanded)); if (isExpanded) { navMenu.style.display = 'flex'; navMenu.style.flexDirection = 'column'; navMenu.style.position = 'absolute'; navMenu.style.top = '100%'; navMenu.style.left = '0'; navMenu.style.width = '100%'; navMenu.style.background = 'rgba(13, 27, 42, 0.98)'; navMenu.style.padding = '24px'; navMenu.style.borderBottom = '1px solid rgba(255,255,255,0.1)'; } else { navMenu.style.display = ''; } }); } } /* 3. Multi-Page SPA Router */ function initPageRouting() { const pageViews = document.querySelectorAll('.page-view'); const navLinks = document.querySelectorAll('.nav-link'); function navigateTo(targetPageId: string, scrollTargetId?: string) { const targetElId = `page-${targetPageId}`; let pageFound = false; pageViews.forEach(page => { if (page.id === targetElId) { page.classList.remove('hidden'); pageFound = true; } else { page.classList.add('hidden'); } }); if (!pageFound) { // Fallback to home page const homePage = document.getElementById('page-home'); if (homePage) homePage.classList.remove('hidden'); } // Update nav active states navLinks.forEach(link => { const pageAttr = link.getAttribute('data-page'); const scrollAttr = link.getAttribute('data-scroll'); if (scrollTargetId && scrollAttr === scrollTargetId) { link.classList.add('active'); } else if (!scrollTargetId && pageAttr === targetPageId) { link.classList.add('active'); } else { link.classList.remove('active'); } }); // Handle smooth scrolling to anchor if on home page if (scrollTargetId) { setTimeout(() => { const targetSection = document.getElementById(scrollTargetId); if (targetSection) { targetSection.scrollIntoView({ behavior: 'smooth' }); } }, 50); } else { window.scrollTo({ top: 0, behavior: 'smooth' }); } } // Handle all click triggers with data-page attribute document.addEventListener('click', (e) => { const target = (e.target as HTMLElement).closest('[data-page]') as HTMLElement | null; if (!target) return; const targetPage = target.getAttribute('data-page'); const targetScroll = target.getAttribute('data-scroll'); if (targetPage) { e.preventDefault(); navigateTo(targetPage, targetScroll || undefined); // Close mobile nav menu if open const navMenu = document.querySelector('.nav-menu'); if (navMenu && navMenu.classList.contains('active')) { navMenu.classList.remove('active'); navMenu.style.display = ''; } } }); // Handle initial route from hash if present function checkHashRoute() { const hash = window.location.hash.replace('#', ''); if (hash === 'industries') { navigateTo('industries'); } else if (hash === 'projects') { navigateTo('projects'); } else if (hash === 'contact') { navigateTo('contact'); } else if (hash === 'team') { navigateTo('team'); } else if (hash === 'privacy') { navigateTo('privacy'); } else if (['home', 'about', 'services', 'products'].includes(hash)) { navigateTo('home', hash === 'home' ? undefined : hash); } } window.addEventListener('hashchange', checkHashRoute); checkHashRoute(); } /* 4. About Section Tab Switcher (Optional Safety Check) */ function initAboutTabs() { const tabBtns = document.querySelectorAll('.about-tab-btn'); const tabContents = document.querySelectorAll('.about-tab-content'); if (!tabBtns.length) return; function switchTab(tabId: string) { tabBtns.forEach(btn => { if (btn.getAttribute('data-tab') === tabId) { btn.classList.add('active'); } else { btn.classList.remove('active'); } }); tabContents.forEach(content => { if (content.id === `tab-content-${tabId}`) { content.classList.add('active'); } else { content.classList.remove('active'); } }); } tabBtns.forEach(btn => { btn.addEventListener('click', () => { const tabId = btn.getAttribute('data-tab'); if (tabId) switchTab(tabId); }); }); } /* 5. Animated Statistics Counter with High-Tech Ease-Out Interpolation */ function initAnimatedCounters() { const counters = document.querySelectorAll('.stat-number, .dark-stat-number, [data-target]'); if (!counters.length) return; const observer = new IntersectionObserver((entries, obs) => { entries.forEach(entry => { if (entry.isIntersecting) { const targetEl = entry.target as HTMLElement; const targetAttr = targetEl.getAttribute('data-target'); if (!targetAttr) return; const targetVal = parseFloat(targetAttr); const decimals = parseInt(targetEl.getAttribute('data-decimals') || '0', 10); // Preserve any existing symbol element (e.g. MW+) const symbolSpan = targetEl.querySelector('.stat-symbol'); const symbolHTML = symbolSpan ? symbolSpan.outerHTML : ''; const startTime = performance.now(); const duration = 2000; // 2 seconds smooth ease-out function updateCounter(currentTime: number) { const elapsed = currentTime - startTime; const progress = Math.min(elapsed / duration, 1); // Ease-Out Cubic interpolation const easeProgress = 1 - Math.pow(1 - progress, 3); const currentVal = easeProgress * targetVal; let formatted = ''; if (decimals > 0) { formatted = currentVal.toFixed(decimals); } else { formatted = Math.floor(currentVal).toLocaleString(); } targetEl.innerHTML = `${formatted}${symbolHTML}`; if (progress < 1) { requestAnimationFrame(updateCounter); } else { const finalFormatted = decimals > 0 ? targetVal.toFixed(decimals) : targetVal.toLocaleString(); targetEl.innerHTML = `${finalFormatted}${symbolHTML}`; } } requestAnimationFrame(updateCounter); obs.unobserve(targetEl); } }); }, { threshold: 0.25 }); counters.forEach(counter => observer.observe(counter)); } /* 6. Intersection Observer for Fade-Up Animations */ function initIntersectionObserver() { const fadeElements = document.querySelectorAll('.fade-up'); if (!fadeElements.length) return; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('visible'); } }); }, { threshold: 0.15 }); fadeElements.forEach(el => observer.observe(el)); } /* 7. Products Section Filter */ function initProductsFilter() { const filterBtns = document.querySelectorAll('.filter-btn'); const productCards = document.querySelectorAll('.product-card'); if (!filterBtns.length || !productCards.length) return; filterBtns.forEach(btn => { btn.addEventListener('click', () => { filterBtns.forEach(b => b.classList.remove('active')); btn.classList.add('active'); const filterValue = btn.getAttribute('data-filter') || 'all'; productCards.forEach(card => { const category = card.getAttribute('data-category'); if (filterValue === 'all' || category === filterValue) { card.style.display = 'flex'; setTimeout(() => { card.style.opacity = '1'; card.style.transform = 'translateY(0)'; }, 50); } else { card.style.opacity = '0'; card.style.transform = 'translateY(10px)'; setTimeout(() => { card.style.display = 'none'; }, 200); } }); }); }); } /* 8. FAQ Accordion & Search */ function initFaqAccordion() { const accordionItems = document.querySelectorAll('.accordion-item'); const searchInput = document.getElementById('faq-search-input') as HTMLInputElement | null; accordionItems.forEach(item => { const header = item.querySelector('.accordion-header'); if (!header) return; header.addEventListener('click', () => { const isActive = item.classList.contains('active'); // Close all other items accordionItems.forEach(i => i.classList.remove('active')); if (!isActive) { item.classList.add('active'); } }); }); if (searchInput) { searchInput.addEventListener('input', (e) => { const query = (e.target as HTMLInputElement).value.toLowerCase().trim(); accordionItems.forEach(item => { const text = item.textContent?.toLowerCase() || ''; if (text.includes(query)) { item.style.display = 'block'; } else { item.style.display = 'none'; } }); }); } } /* 10. Testimonials Carousel */ function initTestimonialSlider() { const testimonials = [ { text: "Sustainable Globe Private Limited engineered our 12.5MW rooftop solar installation and 11kV high-voltage grid integration with absolute perfection. Their HSE standards and execution speed set an industry benchmark.", author: "Engr. Muhammad Hamza", role: "Chief Operating Officer, Crescent Textile Mills" }, { text: "Our zero liquid discharge (ZLD) water treatment plant delivered by Sustainable Globe has reduced operational costs by 45% and surpassed environmental regulations. Tabssum Khan's leadership is exemplary.", author: "Dr. Alistair Vance", role: "Chief Technical Officer, Al-Abbas Sugar & Ethanol Complex" }, { text: "From 132kV substation erection to industrial PLC SCADA automation, Sustainable Globe Private Limited proved why they are trusted by premier enterprise industrial accounts nationwide.", author: "Sarah Jenkins", role: "Director of Infrastructure, National Energy Utilities" } ]; let currentIndex = 0; const textEl = document.getElementById('t-text'); const authorEl = document.getElementById('t-author'); const roleEl = document.getElementById('t-role'); const dots = document.querySelectorAll('.t-dot'); if (!textEl || !authorEl || !roleEl || !dots.length) return; function renderTestimonial(index: number) { const t = testimonials[index]; textEl.textContent = `"${t.text}"`; authorEl.textContent = t.author; roleEl.textContent = t.role; dots.forEach((d, i) => { d.classList.toggle('active', i === index); }); } dots.forEach((dot, index) => { dot.addEventListener('click', () => { currentIndex = index; renderTestimonial(currentIndex); }); }); setInterval(() => { currentIndex = (currentIndex + 1) % testimonials.length; renderTestimonial(currentIndex); }, 6000); } /* 10b. Full Talented Team Modal Handler */ function initTalentedTeamModal() { const teamModal = document.getElementById('talented-team-modal'); const openBtns = document.querySelectorAll('[data-open-team-modal]'); const closeBtn = document.getElementById('close-team-modal-btn'); if (!teamModal) return; function openTeamModal() { teamModal?.classList.add('active'); document.body.style.overflow = 'hidden'; } function closeTeamModal() { teamModal?.classList.remove('active'); document.body.style.overflow = ''; } openBtns.forEach(btn => { btn.addEventListener('click', (e) => { e.preventDefault(); openTeamModal(); }); }); if (closeBtn) { closeBtn.addEventListener('click', closeTeamModal); } teamModal.addEventListener('click', (e) => { if (e.target === teamModal) { closeTeamModal(); } }); document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && teamModal.classList.contains('active')) { closeTeamModal(); } }); } /* 11. Custom Engineering Query & Proposal Action Handler */ function initModals() { const openBtns = document.querySelectorAll('[data-open-modal]'); openBtns.forEach(btn => { btn.addEventListener('click', (e) => { e.preventDefault(); const el = btn as HTMLElement; const productName = el.getAttribute('data-product') || el.textContent?.trim(); // Populate custom query message in contact form const msgField = document.getElementById('c-msg') as HTMLTextAreaElement | null; if (msgField && productName) { msgField.value = `Inquiry regarding: ${productName}. Please provide technical specifications, pricing estimate, and project timeline.`; } // Navigate to Contact Page view const contactPageLink = document.querySelector('[data-page="contact"]'); if (contactPageLink) { contactPageLink.click(); } // Scroll smoothly to the form card and focus setTimeout(() => { const formCard = document.getElementById('contact-form-wrapper') || document.getElementById('contact-page-form'); if (formCard) { formCard.scrollIntoView({ behavior: 'smooth', block: 'start' }); } const nameField = document.getElementById('c-name') as HTMLInputElement | null; if (nameField) { nameField.focus(); } }, 200); }); }); } /* 12. Form Submissions & Web3Forms API Integration */ function initContactForm() { const pageForm = document.getElementById('contact-page-form') as HTMLFormElement | null; const resultMsg = document.getElementById('form-result-message'); if (pageForm) { pageForm.addEventListener('submit', async (e) => { e.preventDefault(); const submitBtn = document.getElementById('c-submit-btn') as HTMLButtonElement | null; const originalText = submitBtn ? submitBtn.innerHTML : 'Submit Engineering Inquiry'; if (submitBtn) { submitBtn.disabled = true; submitBtn.innerHTML = `Submitting Inquiry...`; } if (resultMsg) { resultMsg.style.display = 'block'; resultMsg.style.background = '#eef2ff'; resultMsg.style.color = '#3730a3'; resultMsg.style.border = '1px solid #c7d2fe'; resultMsg.innerHTML = 'Sending your inquiry to Sustainable Globe engineering team...'; } try { const formData = new FormData(pageForm); const response = await fetch('https://api.web3forms.com/submit', { method: 'POST', body: formData }); const data = await response.json(); if (data.success) { if (resultMsg) { resultMsg.style.background = '#ecfdf5'; resultMsg.style.color = '#065f46'; resultMsg.style.border = '1px solid #a7f3d0'; resultMsg.innerHTML = '✔ Thank you! Your engineering inquiry has been received. Our technical team will reach out within 4 hours.'; } showToast('Inquiry sent successfully to Sustainable Globe!'); pageForm.reset(); } else { throw new Error(data.message || 'Form submission error'); } } catch (error) { console.error('Web3Forms Error:', error); if (resultMsg) { resultMsg.style.background = '#fef2f2'; resultMsg.style.color = '#991b1b'; resultMsg.style.border = '1px solid #fecaca'; resultMsg.innerHTML = '✖ Unable to submit form right now. Please try again or chat directly with us on WhatsApp (+92 321 1082811).'; } showToast('Submission error. Please try WhatsApp or try again.'); } finally { if (submitBtn) { submitBtn.disabled = false; submitBtn.innerHTML = originalText; } } }); } } export function showToast(msg: string) { const toast = document.getElementById('global-toast'); const msgEl = document.getElementById('toast-message'); if (toast && msgEl) { msgEl.textContent = msg; toast.classList.add('show'); setTimeout(() => { toast.classList.remove('show'); }, 4500); } } /* 13. Back to Top Button */ function initBackToTop() { const btn = document.getElementById('back-to-top-btn'); if (!btn) return; window.addEventListener('scroll', () => { if (window.scrollY > 400) { btn.classList.add('show'); } else { btn.classList.remove('show'); } }); btn.addEventListener('click', () => { window.scrollTo({ top: 0, behavior: 'smooth' }); }); } /* 14. Hero Auto-Rotating Carousel Engine */ function initHeroCarousel() { const slides = document.querySelectorAll('.hero-slide'); const dotBtns = document.querySelectorAll('.carousel-dot-btn'); const prevBtn = document.getElementById('hero-prev-btn'); const nextBtn = document.getElementById('hero-next-btn'); const progressBar = document.getElementById('hero-progress-bar'); const carouselContainer = document.getElementById('hero-carousel-container'); if (!slides.length) return; let currentSlide = 0; const slideCount = slides.length; const slideDuration = 5000; // 5 seconds per rotation let autoPlayTimer: any = null; function showSlide(index: number) { if (index < 0) index = slideCount - 1; if (index >= slideCount) index = 0; currentSlide = index; slides.forEach((slide, i) => { if (i === currentSlide) { slide.classList.add('active'); } else { slide.classList.remove('active'); } }); dotBtns.forEach((dot, i) => { if (i === currentSlide) { dot.classList.add('active'); } else { dot.classList.remove('active'); } }); // Re-trigger animated statistics counter for active slide const activeSlide = slides[currentSlide]; if (activeSlide) { const counters = activeSlide.querySelectorAll('.stat-number[data-target]'); counters.forEach(counter => { const target = parseInt(counter.getAttribute('data-target') || '0', 10); if (target > 0) { let count = 0; const step = Math.ceil(target / 40); const counterInterval = setInterval(() => { count += step; if (count >= target) { counter.textContent = target.toString(); clearInterval(counterInterval); } else { counter.textContent = count.toString(); } }, 30); } }); } resetProgressBar(); } function resetProgressBar() { if (progressBar) { progressBar.style.transition = 'none'; progressBar.style.width = '0%'; void progressBar.offsetWidth; // force reflow progressBar.style.transition = `width ${slideDuration}ms linear`; progressBar.style.width = '100%'; } } function nextSlide() { showSlide(currentSlide + 1); } function prevSlide() { showSlide(currentSlide - 1); } function startAutoPlay() { stopAutoPlay(); resetProgressBar(); autoPlayTimer = setInterval(() => { nextSlide(); }, slideDuration); } function stopAutoPlay() { if (autoPlayTimer) { clearInterval(autoPlayTimer); autoPlayTimer = null; } } // Bind dot buttons dotBtns.forEach((dot) => { dot.addEventListener('click', () => { const index = parseInt(dot.getAttribute('data-slide') || '0', 10); showSlide(index); startAutoPlay(); }); }); // Bind arrow buttons if (prevBtn) { prevBtn.addEventListener('click', () => { prevSlide(); startAutoPlay(); }); } if (nextBtn) { nextBtn.addEventListener('click', () => { nextSlide(); startAutoPlay(); }); } // Pause auto-rotation on hover if (carouselContainer) { carouselContainer.addEventListener('mouseenter', () => { stopAutoPlay(); if (progressBar) { progressBar.style.transition = 'none'; } }); carouselContainer.addEventListener('mouseleave', () => { startAutoPlay(); }); } // Initialize first slide showSlide(0); startAutoPlay(); } /* 15. Team Domain Filter on Talented Team Page */ function initTeamFilter() { const filterBtns = document.querySelectorAll('.team-filter-btn'); const domainBlocks = document.querySelectorAll('.team-domain-block'); if (!filterBtns.length || !domainBlocks.length) return; filterBtns.forEach(btn => { btn.addEventListener('click', () => { filterBtns.forEach(b => b.classList.remove('active')); btn.classList.add('active'); const filterVal = btn.getAttribute('data-team-filter') || 'all'; domainBlocks.forEach(block => { const domainGroup = block.getAttribute('data-domain-group'); if (filterVal === 'all' || domainGroup === filterVal) { block.style.display = 'block'; } else { block.style.display = 'none'; } }); }); }); } /* 16. Hero Headline & Subtext 3-Second Slide-Left Rotator with Synced Image Crossfade */ function initHeroMessageRotator() { const rotatorContainer = document.getElementById('hero-headline-rotator'); const headingEl = document.getElementById('hero-animated-heading'); const descEl = document.getElementById('hero-animated-desc'); const img1 = document.getElementById('hero-bg-img-1') as HTMLImageElement | null; const img2 = document.getElementById('hero-bg-img-2') as HTMLImageElement | null; if (!rotatorContainer || !headingEl || !descEl) return; const messages = [ { heading: 'Engineering Sustainable Solutions for Energy, Industry & Safety', desc: 'Delivering trusted solar energy systems, industrial electrical engineering, water treatment solutions, industrial chemicals and HSE consultancy with world-class quality and sustainable innovation.', image: 'https://iili.io/C8lx6Xf.png' }, { heading: 'Turnkey Solar & Microgrid EPC for Industrial Enterprises', desc: 'High-yield rooftop solar installations, battery energy storage systems (BESS), and DISCO net-metering grid synchronization designed for maximum operational output.', image: 'https://iili.io/CU2QJQS.png' }, { heading: 'Advanced Industrial Water Treatment & Effluent Solutions', desc: 'Custom Reverse Osmosis (RO) plants, Zero Liquid Discharge (ZLD) engineering, and eco-certified chemicals engineered for sustainable water recovery.', image: 'https://iili.io/C8lx6Xf.png' }, { heading: 'Certified HSE & Climate ESG Advisory Services', desc: 'Empowering enterprise industrial safety, ISO compliance auditing, carbon footprint reduction strategies, and climate sustainability leadership.', image: 'https://iili.io/CU2QJQS.png' } ]; let currentIndex = 0; let activeImgNum = 1; const cycleTimeMs = 3000; // 3 seconds per cycle setInterval(() => { // 1. Slide out left for text rotatorContainer.classList.add('slide-out-left'); rotatorContainer.classList.remove('slide-in-right'); setTimeout(() => { // 2. Change text content currentIndex = (currentIndex + 1) % messages.length; headingEl.innerHTML = messages[currentIndex].heading; descEl.textContent = messages[currentIndex].desc; // 3. Smooth background image crossfade const nextImage = messages[currentIndex].image; if (img1 && img2) { const activeImgEl = activeImgNum === 1 ? img1 : img2; const hiddenImgEl = activeImgNum === 1 ? img2 : img1; if (hiddenImgEl.src !== nextImage) { hiddenImgEl.src = nextImage; } activeImgEl.classList.remove('active'); hiddenImgEl.classList.add('active'); activeImgNum = activeImgNum === 1 ? 2 : 1; } // 4. Slide in text from right rotatorContainer.classList.remove('slide-out-left'); rotatorContainer.classList.add('slide-in-right'); // 5. Clean up text animation class after completion setTimeout(() => { rotatorContainer.classList.remove('slide-in-right'); }, 400); }, 350); }, cycleTimeMs); }