/** * OTS Signs — Main JavaScript */ ( function () { 'use strict'; // ── Sticky header ───────────────────────────────────────────────────────── const header = document.getElementById( 'site-header' ); if ( header ) { const onScroll = () => { header.classList.toggle( 'scrolled', window.scrollY > 40 ); }; window.addEventListener( 'scroll', onScroll, { passive: true } ); onScroll(); // run on init } // ── Mobile nav toggle ────────────────────────────────────────────────────── const navToggle = document.getElementById( 'nav-toggle' ); const siteNav = document.getElementById( 'site-nav' ); if ( navToggle && siteNav ) { navToggle.addEventListener( 'click', () => { const isOpen = siteNav.classList.toggle( 'open' ); navToggle.classList.toggle( 'open', isOpen ); navToggle.setAttribute( 'aria-expanded', isOpen ); document.body.style.overflow = isOpen ? 'hidden' : ''; } ); // Close on nav link click siteNav.querySelectorAll( 'a' ).forEach( link => { link.addEventListener( 'click', () => { siteNav.classList.remove( 'open' ); navToggle.classList.remove( 'open' ); navToggle.setAttribute( 'aria-expanded', 'false' ); document.body.style.overflow = ''; } ); } ); } // ── Scroll-to-top button ────────────────────────────────────────────────── const scrollTopBtn = document.querySelector( '.scroll-top' ); if ( scrollTopBtn ) { window.addEventListener( 'scroll', () => { scrollTopBtn.classList.toggle( 'visible', window.scrollY > 400 ); }, { passive: true } ); scrollTopBtn.addEventListener( 'click', () => { window.scrollTo( { top: 0, behavior: 'smooth' } ); } ); } // ── Contact form AJAX ───────────────────────────────────────────────────── const contactForm = document.getElementById( 'contact-form' ); if ( contactForm ) { const submitBtn = document.getElementById( 'contact-submit' ); const btnText = submitBtn ? submitBtn.querySelector( '.btn-text' ) : null; const btnLoading = submitBtn ? submitBtn.querySelector( '.btn-loading' ) : null; const notice = document.getElementById( 'form-notice' ); contactForm.addEventListener( 'submit', async ( e ) => { e.preventDefault(); // Basic client-side validation const name = contactForm.querySelector( '[name="name"]' ); const email = contactForm.querySelector( '[name="email"]' ); const message = contactForm.querySelector( '[name="message"]' ); if ( ! name.value.trim() || ! email.value.trim() || ! message.value.trim() ) { showNotice( 'error', 'Please fill in all required fields.' ); return; } // Set loading state if ( submitBtn ) submitBtn.disabled = true; if ( btnText ) btnText.style.display = 'none'; if ( btnLoading ) btnLoading.style.display = 'inline'; if ( notice ) notice.className = 'form-notice'; try { const formData = new FormData( contactForm ); formData.append( 'action', 'ots_contact' ); formData.append( 'nonce', otsAjax.nonce ); const response = await fetch( otsAjax.url, { method: 'POST', body: formData, credentials: 'same-origin', } ); const data = await response.json(); if ( data.success ) { showNotice( 'success', data.data.message || 'Message sent successfully!' ); contactForm.reset(); } else { showNotice( 'error', data.data.message || 'Something went wrong. Please try again.' ); } } catch ( err ) { showNotice( 'error', 'Network error. Please check your connection and try again.' ); } finally { if ( submitBtn ) submitBtn.disabled = false; if ( btnText ) btnText.style.display = 'inline'; if ( btnLoading ) btnLoading.style.display = 'none'; } } ); function showNotice( type, message ) { if ( ! notice ) return; notice.textContent = message; notice.className = 'form-notice ' + type; notice.style.display = 'block'; notice.scrollIntoView( { behavior: 'smooth', block: 'nearest' } ); } } // ── Animate cards on scroll (intersection observer) ────────────────────── if ( 'IntersectionObserver' in window ) { const cards = document.querySelectorAll( '.feature-card, .industry-card, .value-card, .service-industry-card, .pricing-card' ); const style = document.createElement( 'style' ); style.textContent = ` .animate-card { opacity: 0; transform: translateY(24px); transition: opacity 0.5s ease, transform 0.5s ease; } .animate-card.visible { opacity: 1; transform: translateY(0); } `; document.head.appendChild( style ); cards.forEach( ( card, i ) => { card.classList.add( 'animate-card' ); card.style.transitionDelay = ( ( i % 4 ) * 80 ) + 'ms'; } ); const observer = new IntersectionObserver( ( entries ) => { entries.forEach( entry => { if ( entry.isIntersecting ) { entry.target.classList.add( 'visible' ); observer.unobserve( entry.target ); } } ); }, { threshold: 0.1 } ); cards.forEach( card => observer.observe( card ) ); } } )();