closing tag - Paste the entire script below METHOD 2: Appearance → Theme File Editor → functions.php Add: wp_enqueue_script('mrd-custom', get_template_directory_uri() . '/js/custom.js', array(), '1.0', true); Then create the js/custom.js file in your theme METHOD 3: Use a plugin like "WPCode" or "Insert Headers and Footers" ═══════════════════════════════════════════════════════════ */ (function() { 'use strict'; /* ───────────────────────────────────────────────────────────── 1. SCROLL REVEAL ANIMATION Uses IntersectionObserver to animate elements into view Elements with class 'mrd-reveal' will fade up on scroll ───────────────────────────────────────────────────────────── */ function initScrollReveal() { const revealElements = document.querySelectorAll( '.mrd-reveal, .mrd-service-card, .mrd-portfolio-card, .mrd-course-card, ' + '.mrd-reason-card, .mrd-testimonial-card, .mrd-blog-featured, .mrd-blog-side, ' + '.mrd-featured-project, .mrd-role-card' ); if (!revealElements.length) return; // Add initial hidden state revealElements.forEach(el => { if (!el.classList.contains('mrd-reveal-initialized')) { el.style.opacity = '0'; el.style.transform = 'translateY(40px)'; el.style.transition = 'opacity 0.6s ease, transform 0.6s ease'; el.classList.add('mrd-reveal-initialized'); } }); const observer = new IntersectionObserver((entries) => { entries.forEach((entry, index) => { if (entry.isIntersecting) { // Stagger animation for grid items const delay = entry.target.dataset.mrdDelay || 0; setTimeout(() => { entry.target.style.opacity = '1'; entry.target.style.transform = 'translateY(0)'; entry.target.classList.add('mrd-revealed'); }, delay); observer.unobserve(entry.target); } }); }, { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }); // Add stagger delays for grid children document.querySelectorAll('.mrd-services-grid, .mrd-portfolio-grid, .mrd-courses-grid, .mrd-reasons-grid, .mrd-testimonials-grid').forEach(grid => { const children = grid.querySelectorAll(':scope > .e-con, :scope > div'); children.forEach((child, i) => { child.dataset.mrdDelay = (i * 80) + 'ms'; }); }); revealElements.forEach(el => observer.observe(el)); } /* ───────────────────────────────────────────────────────────── 2. SKILL BAR ANIMATION Animates skill progress bars when they scroll into view Targets: .mrd-skill-fill elements ───────────────────────────────────────────────────────────── */ function initSkillBars() { const skillBars = document.querySelectorAll('.mrd-skill-fill'); if (!skillBars.length) return; const observer = new IntersectionObserver((entries) => { entries.forEach((entry, index) => { if (entry.isIntersecting) { const bar = entry.target; const targetWidth = bar.style.width || bar.getAttribute('data-width') || '0%'; // Reset and animate bar.style.width = '0%'; bar.style.transition = 'width 1s ease-out'; setTimeout(() => { bar.style.width = targetWidth; bar.classList.add('animated'); }, index * 100 + 200); observer.unobserve(bar); } }); }, { threshold: 0.5 }); skillBars.forEach(bar => observer.observe(bar)); } /* ───────────────────────────────────────────────────────────── 3. NAVIGATION SCROLL EFFECT Adds 'scrolled' class to nav on scroll for glassmorphism effect Also handles smooth scroll for anchor links ───────────────────────────────────────────────────────────── */ function initNavigation() { const nav = document.querySelector('.mrd-nav'); if (!nav) return; // Scroll effect function handleScroll() { if (window.scrollY > 50) { nav.classList.add('scrolled'); } else { nav.classList.remove('scrolled'); } } window.addEventListener('scroll', handleScroll, { passive: true }); handleScroll(); // Initial check // Smooth scroll for anchor links document.querySelectorAll('a[href^="#"]').forEach(link => { link.addEventListener('click', function(e) { const targetId = this.getAttribute('href'); if (targetId === '#') return; const target = document.querySelector(targetId); if (target) { e.preventDefault(); const navHeight = nav.offsetHeight || 64; const targetPosition = target.getBoundingClientRect().top + window.pageYOffset - navHeight; window.scrollTo({ top: targetPosition, behavior: 'smooth' }); } }); }); // Active nav highlighting const sections = document.querySelectorAll('[id]'); function highlightNav() { const scrollPos = window.scrollY + 100; sections.forEach(section => { const sectionTop = section.offsetTop; const sectionHeight = section.offsetHeight; const sectionId = section.getAttribute('id'); if (scrollPos >= sectionTop && scrollPos < sectionTop + sectionHeight) { document.querySelectorAll('.mrd-nav-links a').forEach(link => { link.style.color = ''; if (link.getAttribute('href') === '#' + sectionId) { link.style.color = '#f0fdfa'; } }); } }); } window.addEventListener('scroll', highlightNav, { passive: true }); } /* ───────────────────────────────────────────────────────────── 4. MOBILE MENU TOGGLE Hamburger menu for mobile view Creates a hamburger button and full-screen overlay ───────────────────────────────────────────────────────────── */ function initMobileMenu() { const nav = document.querySelector('.mrd-nav'); if (!nav) return; // Only create if doesn't exist if (document.querySelector('.mrd-hamburger')) return; // Create hamburger button const hamburger = document.createElement('button'); hamburger.className = 'mrd-hamburger'; hamburger.innerHTML = ''; hamburger.setAttribute('aria-label', 'Toggle menu'); nav.appendChild(hamburger); // Create mobile overlay const overlay = document.createElement('div'); overlay.className = 'mrd-mobile-overlay'; // Clone nav links for mobile const navLinks = nav.querySelector('.mrd-nav-links'); if (navLinks) { const mobileLinks = navLinks.cloneNode(true); mobileLinks.className = 'mrd-mobile-links'; overlay.appendChild(mobileLinks); } // Add CTA button to mobile const ctaBtn = nav.querySelector('.mrd-btn-primary'); if (ctaBtn) { const mobileCTA = ctaBtn.cloneNode(true); mobileCTA.className = 'mrd-mobile-cta'; overlay.appendChild(mobileCTA); } document.body.appendChild(overlay); // Toggle hamburger.addEventListener('click', () => { hamburger.classList.toggle('active'); overlay.classList.toggle('active'); document.body.style.overflow = overlay.classList.contains('active') ? 'hidden' : ''; }); // Close on link click overlay.querySelectorAll('a').forEach(link => { link.addEventListener('click', () => { hamburger.classList.remove('active'); overlay.classList.remove('active'); document.body.style.overflow = ''; }); }); } /* ───────────────────────────────────────────────────────────── 5. FAQ ACCORDION ENHANCEMENT Smooth open/close animation for FAQ accordion (Elementor's native accordion is functional but basic) ───────────────────────────────────────────────────────────── */ function initFAQAccordion() { const accordionItems = document.querySelectorAll('.mrd-faq-accordion .elementor-accordion-item'); accordionItems.forEach(item => { const content = item.querySelector('.elementor-accordion-content'); if (content) { content.style.transition = 'max-height 0.3s ease, padding 0.3s ease'; } }); } /* ───────────────────────────────────────────────────────────── 6. COUNTER ANIMATION Animates stat numbers when they come into view Targets: Numbers in hero stats, metrics bar, etc. ───────────────────────────────────────────────────────────── */ function initCounters() { const counterElements = document.querySelectorAll('.mrd-hero-stats h3, .mrd-metrics-bar h3'); if (!counterElements.length) return; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const el = entry.target; const text = el.textContent.trim(); const match = text.match(/(\d+\.?\d*)/); if (match) { const target = parseFloat(match[1]); const suffix = text.replace(match[1], ''); const isDecimal = text.includes('.'); let current = 0; const duration = 1500; const startTime = performance.now(); function animate(currentTime) { const elapsed = currentTime - startTime; const progress = Math.min(elapsed / duration, 1); // Easing const eased = 1 - Math.pow(1 - progress, 3); current = target * eased; el.textContent = (isDecimal ? current.toFixed(1) : Math.floor(current)) + suffix; if (progress < 1) { requestAnimationFrame(animate); } else { el.textContent = text; // Ensure final value is exact } } requestAnimationFrame(animate); } observer.unobserve(el); } }); }, { threshold: 0.5 }); counterElements.forEach(el => observer.observe(el)); } /* ───────────────────────────────────────────────────────────── 7. PARALLAX EFFECT (Subtle) Gentle parallax on hero gradient orbs ───────────────────────────────────────────────────────────── */ function initParallax() { const hero = document.querySelector('.mrd-hero'); if (!hero) return; window.addEventListener('mousemove', (e) => { const x = (e.clientX / window.innerWidth - 0.5) * 20; const y = (e.clientY / window.innerHeight - 0.5) * 20; // Move pseudo-elements via CSS custom properties hero.style.setProperty('--mrd-parallax-x', x + 'px'); hero.style.setProperty('--mrd-parallax-y', y + 'px'); }, { passive: true }); } /* ───────────────────────────────────────────────────────────── 8. MAGNETIC BUTTON EFFECT Subtle magnetic pull on primary buttons when cursor is near ───────────────────────────────────────────────────────────── */ function initMagneticButtons() { const buttons = document.querySelectorAll('.mrd-btn-primary'); buttons.forEach(btn => { btn.addEventListener('mousemove', (e) => { const rect = btn.getBoundingClientRect(); const x = e.clientX - rect.left - rect.width / 2; const y = e.clientY - rect.top - rect.height / 2; btn.style.transform = `translate(${x * 0.15}px, ${y * 0.15}px)`; }); btn.addEventListener('mouseleave', () => { btn.style.transform = ''; }); }); } /* ───────────────────────────────────────────────────────────── 9. INIT ALL ON DOM READY ───────────────────────────────────────────────────────────── */ function init() { initNavigation(); initMobileMenu(); initScrollReveal(); initSkillBars(); initFAQAccordion(); initCounters(); initParallax(); initMagneticButtons(); } // Run when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } // Also run when Elementor finishes preview rendering if (window.elementorFrontend) { window.elementorFrontend.hooks.addAction('frontend/element_ready/global', function() { setTimeout(init, 100); }); } })();