| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>A.I. Engineer Portfolio | Cinematic Experience</title> |
| |
| |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script> |
| <script src="https://unpkg.com/lucide@latest"></script> |
|
|
| |
| <link rel="preconnect" href="https://fonts.googleapis.com"> |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
| <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@100;300;400;700&family=Space+Grotesk:wght@300;500;700&display=swap" rel="stylesheet"> |
|
|
| |
| <script> |
| tailwind.config = { |
| theme: { |
| extend: { |
| colors: { |
| void: '#050505', |
| obsidian: '#0a0a0a', |
| charcoal: '#1c1c1c', |
| platinum: '#e5e5e5', |
| accent: '#ff3333', |
| }, |
| fontFamily: { |
| mono: ['"JetBrains Mono"', 'monospace'], |
| sans: ['"Space Grotesk"', 'sans-serif'], |
| }, |
| backgroundImage: { |
| 'grid-pattern': "linear-gradient(to right, #1f1f1f 1px, transparent 1px), linear-gradient(to bottom, #1f1f1f 1px, transparent 1px)", |
| } |
| } |
| } |
| } |
| </script> |
|
|
| <style> |
| |
| body { |
| background-color: #050505; |
| color: #e5e5e5; |
| overflow-x: hidden; |
| cursor: none; |
| } |
| |
| ::selection { |
| background: #333; |
| color: #fff; |
| } |
| |
| |
| ::-webkit-scrollbar { |
| width: 8px; |
| } |
| ::-webkit-scrollbar-track { |
| background: #0a0a0a; |
| } |
| ::-webkit-scrollbar-thumb { |
| background: #333; |
| border-radius: 4px; |
| } |
| ::-webkit-scrollbar-thumb:hover { |
| background: #555; |
| } |
| |
| |
| #cursor { |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 20px; |
| height: 20px; |
| border: 1px solid rgba(255, 255, 255, 0.5); |
| border-radius: 50%; |
| pointer-events: none; |
| z-index: 9999; |
| transform: translate(-50%, -50%); |
| transition: width 0.3s, height 0.3s, background-color 0.3s; |
| mix-blend-mode: difference; |
| } |
| #cursor.hovered { |
| width: 50px; |
| height: 50px; |
| background-color: rgba(255, 255, 255, 0.1); |
| border-color: transparent; |
| } |
| |
| |
| .glass-panel { |
| background: rgba(10, 10, 10, 0.6); |
| backdrop-filter: blur(12px); |
| -webkit-backdrop-filter: blur(12px); |
| border: 1px solid rgba(255, 255, 255, 0.05); |
| } |
| |
| .text-outline { |
| -webkit-text-stroke: 1px rgba(255, 255, 255, 0.2); |
| color: transparent; |
| transition: all 0.5s ease; |
| } |
| .text-outline:hover { |
| color: #fff; |
| -webkit-text-stroke: 1px #fff; |
| } |
| |
| |
| .noise-overlay { |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| pointer-events: none; |
| z-index: 50; |
| opacity: 0.05; |
| background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E"); |
| } |
| |
| |
| #loader { |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| background: #000; |
| z-index: 10000; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| flex-direction: column; |
| } |
| .loader-bar { |
| width: 0%; |
| height: 2px; |
| background: #fff; |
| margin-top: 20px; |
| transition: width 0.1s; |
| } |
| |
| |
| #canvas-container { |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| z-index: -1; |
| opacity: 0; |
| transition: opacity 1.5s ease; |
| } |
| </style> |
| </head> |
| <body class="font-sans antialiased"> |
|
|
| |
| <div id="loader"> |
| <div class="font-mono text-xs tracking-[0.5em] text-gray-400">INITIALIZING SYSTEM</div> |
| <div class="loader-bar" id="loader-bar"></div> |
| </div> |
|
|
| |
| <div class="noise-overlay"></div> |
|
|
| |
| <div id="cursor"></div> |
|
|
| |
| <div id="canvas-container"></div> |
|
|
| |
| <nav class="fixed top-0 left-0 w-full p-6 flex justify-between items-center z-40 mix-blend-difference"> |
| <div class="font-mono text-sm font-bold tracking-widest hover-trigger">AI.ENGINEER</div> |
| <div class="hidden md:flex gap-8 font-mono text-xs tracking-wider"> |
| <a href="#work" class="hover:text-white transition-colors hover-trigger">WORK</a> |
| <a href="#research" class="hover:text-white transition-colors hover-trigger">RESEARCH</a> |
| <a href="#about" class="hover:text-white transition-colors hover-trigger">ABOUT</a> |
| </div> |
| <button class="border border-white/20 px-4 py-2 rounded-full font-mono text-xs hover:bg-white hover:text-black transition-all hover-trigger"> |
| CONTACT |
| </button> |
| </nav> |
|
|
| |
| <main class="relative z-10"> |
|
|
| |
| <section class="h-screen w-full flex flex-col justify-center items-center relative px-4"> |
| <div class="absolute inset-0 bg-gradient-to-b from-transparent via-transparent to-[#050505] pointer-events-none"></div> |
| |
| <div class="text-center z-10"> |
| <p class="font-mono text-xs md:text-sm text-gray-400 mb-4 tracking-[0.3em] hero-anim opacity-0 translate-y-10"> |
| ARTIFICIAL INTELLIGENCE & RESEARCH |
| </p> |
| <h1 class="text-5xl md:text-8xl lg:text-9xl font-bold tracking-tighter leading-none mb-6 hero-anim opacity-0 translate-y-10"> |
| <span class="block bg-clip-text text-transparent bg-gradient-to-r from-white to-gray-600">PRECISION</span> |
| <span class="block text-outline">ENGINEERING</span> |
| </h1> |
| <p class="max-w-xl mx-auto font-light text-gray-400 text-sm md:text-base leading-relaxed hero-anim opacity-0 translate-y-10"> |
| Architecting the future through neural networks and high-performance algorithms. |
| </p> |
| </div> |
|
|
| <div class="absolute bottom-10 left-1/2 -translate-x-1/2 flex flex-col items-center gap-2 hero-anim opacity-0"> |
| <span class="font-mono text-[10px] tracking-widest text-gray-500">SCROLL TO EXPLORE</span> |
| <div class="w-[1px] h-12 bg-gradient-to-b from-white to-transparent"></div> |
| </div> |
| </section> |
|
|
| |
| <section id="kpi-section" class="py-32 px-6 md:px-20 relative"> |
| <div class="absolute top-0 left-0 w-full h-[1px] bg-white/10"></div> |
| |
| <div class="flex flex-col md:flex-row justify-between items-end mb-20 border-b border-white/10 pb-8"> |
| <h2 class="text-4xl md:text-6xl font-bold">SYSTEM METRICS</h2> |
| <div class="font-mono text-xs text-gray-500 mt-4 md:mt-0"> |
| REAL-TIME DATA STREAM |
| </div> |
| </div> |
|
|
| |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-6"> |
| |
| <div class="kpi-card group p-8 border border-white/5 bg-[#0a0a0a] relative overflow-hidden hover-trigger"> |
| <div class="absolute top-0 right-0 p-4 opacity-50 group-hover:opacity-100 transition-opacity"> |
| <i data-lucide="cpu" class="w-6 h-6"></i> |
| </div> |
| <div class="font-mono text-xs text-gray-500 mb-2">MODEL ACCURACY</div> |
| <div class="text-5xl md:text-6xl font-bold font-mono mb-4 counter" data-target="99.8">0</div> |
| <div class="w-full h-[1px] bg-white/10 mb-4 relative overflow-hidden"> |
| <div class="absolute top-0 left-0 h-full w-1/2 bg-white group-hover:w-full transition-all duration-1000 ease-out"></div> |
| </div> |
| <p class="text-sm text-gray-400">Optimized for edge deployment and low-latency inference.</p> |
| </div> |
|
|
| |
| <div class="kpi-card group p-8 border border-white/5 bg-[#0a0a0a] relative overflow-hidden hover-trigger"> |
| <div class="absolute top-0 right-0 p-4 opacity-50 group-hover:opacity-100 transition-opacity"> |
| <i data-lucide="database" class="w-6 h-6"></i> |
| </div> |
| <div class="font-mono text-xs text-gray-500 mb-2">DATA PROCESSED</div> |
| <div class="text-5xl md:text-6xl font-bold font-mono mb-4 counter" data-target="850">0</div> |
| <div class="w-full h-[1px] bg-white/10 mb-4 relative overflow-hidden"> |
| <div class="absolute top-0 left-0 h-full w-0 group-hover:w-full transition-all duration-1000 ease-out delay-200"></div> |
| </div> |
| <p class="text-sm text-gray-400">Petabytes of structured and unstructured data analyzed.</p> |
| </div> |
|
|
| |
| <div class="kpi-card group p-8 border border-white/5 bg-[#0a0a0a] relative overflow-hidden hover-trigger"> |
| <div class="absolute top-0 right-0 p-4 opacity-50 group-hover:opacity-100 transition-opacity"> |
| <i data-lucide="zap" class="w-6 h-6"></i> |
| </div> |
| <div class="font-mono text-xs text-gray-500 mb-2">INFERENCE TIME</div> |
| <div class="text-5xl md:text-6xl font-bold font-mono mb-4 counter" data-target="12">0</div> |
| <div class="text-xs text-gray-500 font-mono mb-4">ms</div> |
| <div class="w-full h-[1px] bg-white/10 mb-4 relative overflow-hidden"> |
| <div class="absolute top-0 left-0 h-full w-1/3 bg-white group-hover:w-full transition-all duration-1000 ease-out delay-400"></div> |
| </div> |
| <p class="text-sm text-gray-400">Achieving sub-15ms response times on consumer hardware.</p> |
| </div> |
| </div> |
| </section> |
|
|
| |
| <section id="work" class="py-32 relative overflow-hidden"> |
| <div class="container mx-auto px-6"> |
| <div class="flex flex-col md:flex-row justify-between items-start mb-16"> |
| <h2 class="text-4xl md:text-6xl font-bold">PROJECTS</h2> |
| <div class="font-mono text-xs text-gray-500 mt-4 md:mt-0">ARCHIVE: 2023 - 2024</div> |
| </div> |
|
|
| |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-12 items-center"> |
| |
| |
| <div class="order-2 md:order-1 space-y-8"> |
| <div class="project-trigger group cursor-pointer"> |
| <div class="flex items-center gap-4 mb-2"> |
| <span class="font-mono text-xs text-accent">01</span> |
| <span class="font-mono text-xs text-gray-500">NEURAL INTERFACE</span> |
| </div> |
| <h3 class="text-3xl md:text-5xl font-bold group-hover:text-white transition-colors">Brain-Computer Interface</h3> |
| <p class="text-gray-400 mt-4 leading-relaxed"> |
| Decoding motor cortex signals in real-time to control robotic prosthetics with millimeter precision. |
| </p> |
| </div> |
|
|
| <div class="w-full h-[1px] bg-white/10"></div> |
|
|
| <div class="project-trigger group cursor-pointer"> |
| <div class="flex items-center gap-4 mb-2"> |
| <span class="font-mono text-xs text-accent">02</span> |
| <span class="font-mono text-xs text-gray-500">GENERATIVE MODEL</span> |
| </div> |
| <h3 class="text-3xl md:text-5xl font-bold group-hover:text-white transition-colors">Synthetic Data Fabric</h3> |
| <p class="text-gray-400 mt-4 leading-relaxed"> |
| Creating infinite, privacy-compliant datasets for training LLMs and Computer Vision models. |
| </p> |
| </div> |
| </div> |
|
|
| |
| <div class="order-1 md:order-2 relative h-[600px] w-full border border-white/10 bg-charcoal overflow-hidden group hover-trigger"> |
| |
| <div class="absolute inset-0 bg-grid-pattern opacity-20"></div> |
| <div class="absolute inset-0 flex items-center justify-center"> |
| <div class="w-64 h-64 border border-white/20 rounded-full animate-[spin_10s_linear_infinite]"></div> |
| <div class="absolute w-48 h-48 border border-white/10 rotate-45"></div> |
| <div class="absolute w-32 h-32 bg-white/5 blur-xl rounded-full"></div> |
| </div> |
| <div class="absolute bottom-0 left-0 p-8 bg-gradient-to-t from-black to-transparent w-full"> |
| <span class="font-mono text-xs">FIG 1.1: ARCHITECTURE</span> |
| </div> |
| </div> |
|
|
| </div> |
| </div> |
| </section> |
|
|
| |
| <section id="research" class="py-32 bg-[#0a0a0a] relative"> |
| <div class="container mx-auto px-6"> |
| <h2 class="text-4xl md:text-6xl font-bold mb-20 text-center">RESEARCH PAPERS</h2> |
| |
| <div class="max-w-4xl mx-auto space-y-4"> |
| |
| <div class="research-item group border-b border-white/10 pb-4 cursor-pointer hover:bg-white/5 transition-colors"> |
| <div class="flex justify-between items-center py-6"> |
| <h3 class="text-2xl font-bold">Attention Is All You Need (Redux)</h3> |
| <i data-lucide="chevron-down" class="w-6 h-6 transform transition-transform duration-300"></i> |
| </div> |
| <div class="max-h-0 overflow-hidden transition-all duration-500 ease-in-out"> |
| <div class="pb-8 grid grid-cols-1 md:grid-cols-3 gap-6"> |
| <div class="md:col-span-2 text-gray-400 text-sm leading-relaxed"> |
| <p class="mb-4">An exploration into self-attention mechanisms in transformer architectures, optimized for sparse computation.</p> |
| <ul class="list-disc list-inside text-gray-500 font-mono text-xs space-y-1"> |
| <li>ArXiv: 2023.10.24</li> |
| <li>Latex Source Available</li> |
| <li>Code: GitHub.com/AI-Eng/Transformer</li> |
| </ul> |
| </div> |
| <div class="bg-white/5 p-4 border border-white/10"> |
| <div class="text-xs font-mono text-gray-500 mb-2">CITATION SCORE</div> |
| <div class="text-3xl font-bold">142</div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="research-item group border-b border-white/10 pb-4 cursor-pointer hover:bg-white/5 transition-colors"> |
| <div class="flex justify-between items-center py-6"> |
| <h3 class="text-2xl font-bold">Quantum-Classical Hybrid Heuristics</h3> |
| <i data-lucide="chevron-down" class="w-6 h-6 transform transition-transform duration-300"></i> |
| </div> |
| <div class="max-h-0 overflow-hidden transition-all duration-500 ease-in-out"> |
| <div class="pb-8 grid grid-cols-1 md:grid-cols-3 gap-6"> |
| <div class="md:col-span-2 text-gray-400 text-sm leading-relaxed"> |
| <p class="mb-4">Leveraging variational quantum eigensolvers (VQE) to optimize reinforcement learning policies.</p> |
| </div> |
| <div class="bg-white/5 p-4 border border-white/10"> |
| <div class="text-xs font-mono text-gray-500 mb-2">CITATION SCORE</div> |
| <div class="text-3xl font-bold">89</div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </section> |
|
|
| |
| <footer class="py-20 border-t border-white/10 bg-void relative overflow-hidden"> |
| <div class="container mx-auto px-6 flex flex-col md:flex-row justify-between items-center relative z-10"> |
| <div class="mb-8 md:mb-0 text-center md:text-left"> |
| <h2 class="text-2xl font-bold">READY TO COLLABORATE?</h2> |
| <p class="text-gray-500 font-mono text-sm mt-2">Open for select research partnerships.</p> |
| </div> |
| <div class="flex gap-6"> |
| <a href="#" class="hover:text-white transition-colors hover-trigger"><i data-lucide="github" class="w-6 h-6"></i></a> |
| <a href="#" class="hover:text-white transition-colors hover-trigger"><i data-lucide="twitter" class="w-6 h-6"></i></a> |
| <a href="#" class="hover:text-white transition-colors hover-trigger"><i data-lucide="linkedin" class="w-6 h-6"></i></a> |
| </div> |
| </div> |
| <div class="absolute bottom-4 right-6 font-mono text-[10px] text-gray-700"> |
| Built with anycoder |
| </div> |
| </footer> |
|
|
| </main> |
|
|
| <script> |
| |
| lucide.createIcons(); |
| |
| |
| const loader = document.getElementById('loader'); |
| const loaderBar = document.getElementById('loader-bar'); |
| const canvasContainer = document.getElementById('canvas-container'); |
| |
| let progress = 0; |
| const interval = setInterval(() => { |
| progress += Math.random() * 10; |
| if (progress >= 100) { |
| progress = 100; |
| clearInterval(interval); |
| |
| gsap.to(loaderBar, { width: '100%', duration: 0.2, onComplete: () => { |
| gsap.to(loader, { |
| yPercent: -100, |
| duration: 1, |
| ease: "power4.inOut", |
| onComplete: () => { |
| canvasContainer.style.opacity = 1; |
| initAnimations(); |
| } |
| }); |
| }}); |
| } |
| loaderBar.style.width = `${progress}%`; |
| }, 100); |
| |
| |
| |
| const scene = new THREE.Scene(); |
| |
| scene.fog = new THREE.FogExp2(0x050505, 0.002); |
| |
| const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); |
| camera.position.z = 100; |
| |
| const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }); |
| renderer.setSize(window.innerWidth, window.innerHeight); |
| renderer.setPixelRatio(window.devicePixelRatio); |
| canvasContainer.appendChild(renderer.domElement); |
| |
| |
| const geometry = new THREE.BufferGeometry(); |
| const count = 3000; |
| const positions = new Float32Array(count * 3); |
| const colors = new Float32Array(count * 3); |
| const sizes = new Float32Array(count); |
| |
| const color1 = new THREE.Color(0xffffff); |
| const color2 = new THREE.Color(0x333333); |
| |
| for(let i = 0; i < count; i++) { |
| |
| const angle = Math.random() * Math.PI * 2; |
| const radius = 20 + Math.random() * 80; |
| const z = (Math.random() - 0.5) * 400; |
| |
| positions[i * 3] = Math.cos(angle) * radius; |
| positions[i * 3 + 1] = Math.sin(angle) * radius; |
| positions[i * 3 + 2] = z; |
| |
| |
| const mixedColor = Math.random() > 0.5 ? color1 : color2; |
| colors[i * 3] = mixedColor.r; |
| colors[i * 3 + 1] = mixedColor.g; |
| colors[i * 3 + 2] = mixedColor.b; |
| |
| sizes[i] = Math.random() * 2; |
| } |
| |
| geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); |
| geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)); |
| geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1)); |
| |
| |
| const material = new THREE.PointsMaterial({ |
| size: 0.5, |
| vertexColors: true, |
| transparent: true, |
| opacity: 0.8, |
| blending: THREE.AdditiveBlending |
| }); |
| |
| const particles = new THREE.Points(geometry, material); |
| scene.add(particles); |
| |
| |
| const gridHelper = new THREE.GridHelper(200, 50, 0x333333, 0x111111); |
| gridHelper.position.y = -50; |
| scene.add(gridHelper); |
| |
| |
| let mouseX = 0; |
| let mouseY = 0; |
| let targetX = 0; |
| let targetY = 0; |
| |
| document.addEventListener('mousemove', (event) => { |
| mouseX = event.clientX - window.innerWidth / 2; |
| mouseY = event.clientY - window.innerHeight / 2; |
| }); |
| |
| const clock = new THREE.Clock(); |
| |
| function animate() { |
| requestAnimationFrame(animate); |
| const elapsedTime = clock.getElapsedTime(); |
| |
| targetX = mouseX * 0.001; |
| targetY = mouseY * 0.001; |
| |
| |
| particles.rotation.y += 0.002; |
| particles.rotation.x += (targetY - particles.rotation.x) * 0.05; |
| particles.rotation.y += (targetX - particles.rotation.y) * 0.05; |
| |
| |
| const positions = particles.geometry.attributes.position.array; |
| for(let i = 0; i < count; i++) { |
| |
| positions[i * 3 + 2] += 0.5; |
| |
| |
| if(positions[i * 3 + 2] > 150) { |
| positions[i * 3 + 2] = -250; |
| } |
| } |
| particles.geometry.attributes.position.needsUpdate = true; |
| |
| |
| gridHelper.rotation.y = -mouseX * 0.0002; |
| |
| renderer.render(scene, camera); |
| } |
| |
| animate(); |
| |
| |
| window.addEventListener('resize', () => { |
| camera.aspect = window.innerWidth / window.innerHeight; |
| camera.updateProjectionMatrix(); |
| renderer.setSize(window.innerWidth, window.innerHeight); |
| }); |
| |
| |
| gsap.registerPlugin(ScrollTrigger); |
| |
| function initAnimations() { |
| |
| const heroTl = gsap.timeline(); |
| heroTl.to('.hero-anim', { |
| y: 0, |
| opacity: 1, |
| duration: 1, |
| stagger: 0.2, |
| ease: "power3.out" |
| }); |
| |
| |
| gsap.utils.toArray('.counter').forEach(counter => { |
| const target = parseFloat(counter.getAttribute('data-target')); |
| gsap.to(counter, { |
| innerText: target, |
| duration: 2, |
| snap: { innerText: 0.1 }, |
| scrollTrigger: { |
| trigger: counter, |
| start: "top 85%", |
| toggleActions: "play none none reverse" |
| } |
| }); |
| }); |
| |
| |
| gsap.from('.kpi-card', { |
| y: 50, |
| opacity: 0, |
| duration: 1, |
| stagger: 0.2, |
| scrollTrigger: { |
| trigger: '#kpi-section', |
| start: "top 70%" |
| } |
| }); |
| |
| |
| gsap.from('.project-trigger', { |
| x: -50, |
| opacity: 0, |
| duration: 1, |
| stagger: 0.2, |
| scrollTrigger: { |
| trigger: '#work', |
| start: "top 70%" |
| } |
| }); |
| |
| |
| const researchItems = document.querySelectorAll('.research-item'); |
| researchItems.forEach(item => { |
| const header = item.querySelector('div.flex'); |
| const content = item.querySelector('.max-h-0'); |
| const icon = item.querySelector('i'); |
| |
| header.addEventListener('click', () => { |
| const isOpen = content.style.maxHeight; |
| |
| |
| researchItems.forEach(otherItem => { |
| otherItem.querySelector('.max-h-0').style.maxHeight = null; |
| otherItem.querySelector('i').style.transform = 'rotate(0deg)'; |
| }); |
| |
| |
| if (!isOpen) { |
| content.style.maxHeight = content.scrollHeight + "px"; |
| icon.style.transform = 'rotate(180deg)'; |
| } |
| }); |
| }); |
| } |
| |
| |
| const cursor = document.getElementById('cursor'); |
| const triggers = document.querySelectorAll('.hover-trigger'); |
| |
| document.addEventListener('mousemove', (e) => { |
| cursor.style.left = e.clientX + 'px'; |
| cursor.style.top = e.clientY + 'px'; |
| }); |
| |
| triggers.forEach(trigger => { |
| trigger.addEventListener('mouseenter', () => { |
| cursor.classList.add('hovered'); |
| }); |
| trigger.addEventListener('mouseleave', () => { |
| cursor.classList.remove('hovered'); |
| }); |
| }); |
| |
| </script> |
| </body> |
| </html> |