| |
| lucide.createIcons(); |
|
|
| |
| gsap.registerPlugin(ScrollTrigger); |
|
|
| |
| |
| |
| const cursorDot = document.querySelector('.cursor-dot'); |
| const cursorOutline = document.querySelector('.cursor-outline'); |
|
|
| if (window.matchMedia("(min-width: 768px)").matches) { |
| window.addEventListener('mousemove', (e) => { |
| const posX = e.clientX; |
| const posY = e.clientY; |
|
|
| |
| cursorDot.style.left = `${posX}px`; |
| cursorDot.style.top = `${posY}px`; |
|
|
| |
| gsap.to(cursorOutline, { |
| x: posX, |
| y: posY, |
| duration: 0.15, |
| ease: "power2.out" |
| }); |
| }); |
|
|
| |
| const interactiveElements = document.querySelectorAll('a, button, .tilt-card, input, textarea'); |
| |
| interactiveElements.forEach(el => { |
| el.addEventListener('mouseenter', () => { |
| document.body.classList.add('hovering'); |
| gsap.to(cursorOutline, { |
| scale: 1.5, |
| duration: 0.3 |
| }); |
| }); |
| |
| el.addEventListener('mouseleave', () => { |
| document.body.classList.remove('hovering'); |
| gsap.to(cursorOutline, { |
| scale: 1, |
| duration: 0.3 |
| }); |
| }); |
| }); |
| } |
|
|
| |
| |
| |
| |
| |
|
|
| const compassNeedle = document.getElementById('compassNeedle'); |
| let lastScrollY = window.scrollY; |
| let scrollVelocity = 0; |
|
|
| function updateCompassScroll() { |
| const scrollHeight = document.documentElement.scrollHeight - window.innerHeight; |
| const scrollTop = window.scrollY; |
| const scrollPercent = scrollTop / scrollHeight; |
| |
| |
| const containerHeight = 200; |
| const needlePosition = containerHeight - (scrollPercent * containerHeight); |
| |
| if (compassNeedle) { |
| compassNeedle.style.top = `${needlePosition}px`; |
| |
| |
| scrollVelocity = scrollTop - lastScrollY; |
| const rotation = scrollVelocity * 2; |
| |
| gsap.to(compassNeedle, { |
| rotation: rotation, |
| duration: 0.1 |
| }); |
| } |
| |
| lastScrollY = scrollTop; |
| } |
|
|
| |
| let ticking = false; |
| window.addEventListener('scroll', () => { |
| if (!ticking) { |
| window.requestAnimationFrame(() => { |
| updateCompassScroll(); |
| ticking = false; |
| }); |
| ticking = true; |
| } |
| }); |
|
|
| |
| |
| |
| const initD3Compass = () => { |
| const width = window.innerWidth; |
| const height = window.innerHeight; |
| |
| const svg = d3.select("#compass-viz") |
| .append("svg") |
| .attr("width", width) |
| .attr("height", height) |
| .attr("viewBox", `0 0 ${width} ${height}`); |
|
|
| const centerX = width / 2; |
| const centerY = height / 2; |
| |
| |
| const circles = [100, 200, 300, 400]; |
| circles.forEach(radius => { |
| svg.append("circle") |
| .attr("cx", centerX) |
| .attr("cy", centerY) |
| .attr("r", radius) |
| .attr("class", "compass-circle"); |
| }); |
| |
| |
| for (let i = 0; i < 360; i += 30) { |
| const angle = (i * Math.PI) / 180; |
| const x2 = centerX + Math.cos(angle) * 400; |
| const y2 = centerY + Math.sin(angle) * 400; |
| |
| svg.append("line") |
| .attr("x1", centerX) |
| .attr("y1", centerY) |
| .attr("x2", x2) |
| .attr("y2", y2) |
| .attr("class", "compass-rose-line"); |
| } |
| |
| |
| gsap.to(".compass-circle", { |
| scrollTrigger: { |
| trigger: "body", |
| start: "top top", |
| end: "bottom bottom", |
| scrub: 1 |
| }, |
| attr: { r: (i, target) => 100 + (i * 100) + 50 }, |
| opacity: 0 |
| }); |
| |
| gsap.to(".compass-rose-line", { |
| scrollTrigger: { |
| trigger: "body", |
| start: "top top", |
| end: "bottom bottom", |
| scrub: 1 |
| }, |
| rotation: 45, |
| transformOrigin: "center" |
| }); |
| }; |
|
|
| if (window.matchMedia("(min-width: 1024px)").matches) { |
| initD3Compass(); |
| } |
|
|
| |
| |
| |
| const lineInners = document.querySelectorAll('.line-inner'); |
| lineInners.forEach((line, index) => { |
| gsap.to(line, { |
| y: 0, |
| duration: 1, |
| ease: "power4.out", |
| delay: index * 0.1, |
| scrollTrigger: { |
| trigger: line, |
| start: "top 85%", |
| toggleActions: "play none none none" |
| } |
| }); |
| }); |
|
|
| |
| gsap.to("#heroSubtext", { |
| opacity: 1, |
| y: 0, |
| duration: 1, |
| delay: 0.5, |
| ease: "power2.out" |
| }); |
|
|
| |
| |
| |
| const gridBg = document.getElementById('gridBg'); |
| if (gridBg) { |
| gsap.to(gridBg, { |
| scrollTrigger: { |
| trigger: "body", |
| start: "top top", |
| end: "bottom bottom", |
| scrub: 0.5 |
| }, |
| backgroundPosition: "50% 100%" |
| }); |
| } |
|
|
| |
| |
| |
| const tiltCards = document.querySelectorAll('.tilt-card'); |
|
|
| tiltCards.forEach(card => { |
| card.addEventListener('mousemove', (e) => { |
| const rect = card.getBoundingClientRect(); |
| const x = e.clientX - rect.left; |
| const y = e.clientY - rect.top; |
| |
| const centerX = rect.width / 2; |
| const centerY = rect.height / 2; |
| |
| const rotateX = (y - centerY) / centerY * -10; |
| const rotateY = (x - centerX) / centerX * 10; |
| |
| gsap.to(card, { |
| rotationX: rotateX, |
| rotationY: rotateY, |
| transformPerspective: 1000, |
| duration: 0.3, |
| ease: "power2.out" |
| }); |
| }); |
| |
| card.addEventListener('mouseleave', () => { |
| gsap.to(card, { |
| rotationX: 0, |
| rotationY: 0, |
| duration: 0.5, |
| ease: "power2.out" |
| }); |
| }); |
| }); |
|
|
| |
| |
| |
| const nav = document.getElementById('mainNav'); |
| let lastScroll = 0; |
|
|
| window.addEventListener('scroll', () => { |
| const currentScroll = window.pageYOffset; |
| |
| if (currentScroll > 100) { |
| if (currentScroll > lastScroll) { |
| |
| nav.classList.remove('nav-visible'); |
| nav.classList.add('nav-hidden'); |
| } else { |
| |
| nav.classList.remove('nav-hidden'); |
| nav.classList.add('nav-visible'); |
| } |
| } else { |
| nav.classList.add('nav-hidden'); |
| nav.classList.remove('nav-visible'); |
| } |
| |
| lastScroll = currentScroll; |
| }); |
|
|
| |
| |
| |
| function toggleFaq(element) { |
| const content = element.querySelector('.faq-content'); |
| const isActive = element.classList.contains('active'); |
| |
| |
| document.querySelectorAll('.faq-item').forEach(item => { |
| item.classList.remove('active'); |
| item.querySelector('.faq-content').classList.remove('active'); |
| }); |
| |
| |
| if (!isActive) { |
| element.classList.add('active'); |
| content.classList.add('active'); |
| } |
| } |
|
|
| |
| |
| |
| document.querySelectorAll('a[href^="#"]').forEach(anchor => { |
| anchor.addEventListener('click', function (e) { |
| e.preventDefault(); |
| const target = document.querySelector(this.getAttribute('href')); |
| if (target) { |
| target.scrollIntoView({ |
| behavior: 'smooth', |
| block: 'start' |
| }); |
| } |
| }); |
| }); |
|
|
| |
| |
| |
| const magneticBtns = document.querySelectorAll('.magnetic-btn'); |
|
|
| magneticBtns.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; |
| |
| gsap.to(btn, { |
| x: x * 0.3, |
| y: y * 0.3, |
| duration: 0.3, |
| ease: "power2.out" |
| }); |
| }); |
| |
| btn.addEventListener('mouseleave', () => { |
| gsap.to(btn, { |
| x: 0, |
| y: 0, |
| duration: 0.5, |
| ease: "elastic.out(1, 0.3)" |
| }); |
| }); |
| }); |
|
|
| |
| |
| |
| const parallaxImages = document.querySelectorAll('.parallax-img'); |
| parallaxImages.forEach(img => { |
| const speed = img.getAttribute('data-speed') || 0.5; |
| |
| gsap.to(img, { |
| scrollTrigger: { |
| trigger: img.parentElement, |
| start: "top bottom", |
| end: "bottom top", |
| scrub: true |
| }, |
| y: -50 * speed, |
| scale: 1.1 |
| }); |
| }); |