document.addEventListener('DOMContentLoaded', () => { // ── 1. LENIS SMOOTH SCROLL ──────────────────────────────────────── const lenis = new Lenis({ duration: 1.2, easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), smooth: true, mouseMultiplier: 1, smoothTouch: false, touchMultiplier: 2, }); function raf(time) { lenis.raf(time); requestAnimationFrame(raf); } requestAnimationFrame(raf); // ── 2. GSAP PLUGINS ─────────────────────────────────────────────── gsap.registerPlugin(ScrollTrigger); // ── 3. PRELOADER ────────────────────────────────────────────────── gsap.set('.hero-title, .hero-subtitle', { opacity: 0 }); gsap.timeline() .to('#loader-text', { y: 0, duration: 1, ease: 'power4.out', delay: 0.2 }) .to('#loader-text', { y: '-100%', duration: 1, ease: 'power4.in', delay: 0.5 }) .to('#preloader', { y: '-100%', duration: 1, ease: 'power4.inOut' }, '-=0.5') .to('.hero-title', { y: 0, opacity: 1, duration: 1.5, stagger: 0.15, ease: 'power4.out' }, '-=0.4') .to('.hero-subtitle', { y: 0, opacity: 1, duration: 1.2, stagger: 0.12, ease: 'power3.out' }, '-=1.2'); // ── 4. CUSTOM CURSOR ────────────────────────────────────────────── if (window.innerWidth >= 768) { const cursor = document.getElementById('cursor'); const cursorDot = document.getElementById('cursor-dot'); let mouseX = 0, mouseY = 0, cursorX = 0, cursorY = 0; document.addEventListener('mousemove', (e) => { mouseX = e.clientX; mouseY = e.clientY; gsap.to(cursorDot, { x: mouseX, y: mouseY, duration: 0 }); }); gsap.ticker.add(() => { cursorX += (mouseX - cursorX) * 0.2; cursorY += (mouseY - cursorY) * 0.2; gsap.set(cursor, { x: cursorX, y: cursorY }); }); document.querySelectorAll('.hover-target').forEach(target => { target.addEventListener('mouseenter', () => { gsap.to(cursor, { scale: 2.5, backgroundColor: 'rgba(255,255,255,1)', duration: 0.3 }); gsap.to(cursorDot, { opacity: 0, duration: 0.3 }); }); target.addEventListener('mouseleave', () => { gsap.to(cursor, { scale: 1, backgroundColor: 'transparent', duration: 0.3 }); gsap.to(cursorDot, { opacity: 1, duration: 0.3 }); }); }); } // ── 5. SCROLL PROGRESS BAR ──────────────────────────────────────── const progressBar = document.getElementById('progress-bar'); lenis.on('scroll', ({ progress }) => { if (progressBar) progressBar.style.width = `${progress * 100}%`; }); // ── 6. ACTIVE NAV LINK ──────────────────────────────────────────── const navLinks = document.querySelectorAll('.nav-link'); const sections = document.querySelectorAll('section[id]'); const sectionObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const id = entry.target.getAttribute('id'); navLinks.forEach(link => { link.classList.toggle('active', link.dataset.section === id); }); } }); }, { rootMargin: '-40% 0px -40% 0px', threshold: 0 }); sections.forEach(s => sectionObserver.observe(s)); // ── 7. MOBILE MENU ──────────────────────────────────────────────── const hamburger = document.getElementById('hamburger'); const mobileOverlay = document.getElementById('mobile-overlay'); if (hamburger && mobileOverlay) { hamburger.addEventListener('click', () => { const isOpen = hamburger.classList.contains('open'); hamburger.classList.toggle('open'); mobileOverlay.classList.toggle('open'); isOpen ? lenis.start() : lenis.stop(); }); document.querySelectorAll('.mobile-nav-link').forEach(link => { link.addEventListener('click', () => { hamburger.classList.remove('open'); mobileOverlay.classList.remove('open'); lenis.start(); }); }); } // ── 8. REVEAL TEXT ANIMATIONS ───────────────────────────────────── gsap.utils.toArray('.reveal-text').forEach(el => { gsap.from(el, { scrollTrigger: { trigger: el, start: 'top 85%' }, y: 60, opacity: 0, duration: 1.2, ease: 'power3.out', }); }); // ── 9. CV STAGGERED ANIMATIONS ──────────────────────────────────── gsap.utils.toArray('.cv-column').forEach(col => { const items = col.querySelectorAll('.group'); if (!items.length) return; gsap.from(items, { scrollTrigger: { trigger: col, start: 'top 82%' }, y: 40, opacity: 0, duration: 0.85, stagger: 0.18, ease: 'power3.out', }); }); // ── 10. PARALLAX IMAGES ─────────────────────────────────────────── gsap.utils.toArray('.parallax-img').forEach(img => { gsap.to(img, { scrollTrigger: { trigger: img.parentElement, start: 'top bottom', end: 'bottom top', scrub: true, }, y: 50, ease: 'none', }); }); // ── 11. DRAGGABLE GALLERY ───────────────────────────────────────── const gallery = document.getElementById('gallery'); if (gallery) { let isDown = false, startX, scrollLeft; gallery.addEventListener('mousedown', (e) => { isDown = true; startX = e.pageX - gallery.offsetLeft; scrollLeft = gallery.scrollLeft; }); gallery.addEventListener('mouseleave', () => { isDown = false; }); gallery.addEventListener('mouseup', () => { isDown = false; }); gallery.addEventListener('mousemove', (e) => { if (!isDown) return; e.preventDefault(); gallery.scrollLeft = scrollLeft - (e.pageX - gallery.offsetLeft - startX) * 2; }); } });