diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx index b46e916..304df25 100644 --- a/src/app/about/page.tsx +++ b/src/app/about/page.tsx @@ -1,99 +1,125 @@ -"use client" +"use client"; -import { useRef } from "react" -import gsap from "gsap" -import { useGSAP } from "@gsap/react" -import { ScrollTrigger } from "gsap/ScrollTrigger" -import { Instagram, Linkedin, Github } from "lucide-react" -import Footer from "@/src/components/Footer" +import { useRef } from "react"; +import gsap from "gsap"; +import { useGSAP } from "@gsap/react"; +import { ScrollTrigger } from "gsap/ScrollTrigger"; +import { Instagram, Linkedin, Github } from "lucide-react"; +import Footer from "@/src/components/Footer"; -const singularityLogo = "https://res.cloudinary.com/djtemmctt/image/upload/v1771104005/singularity_new_logo_knedxr.png" +const singularityLogo = + "https://res.cloudinary.com/djtemmctt/image/upload/v1771104005/singularity_new_logo_knedxr.png"; export default function AboutPage() { - const containerRef = useRef(null) + const containerRef = useRef(null); - useGSAP(() => { - gsap.registerPlugin(ScrollTrigger) - - // 1. Page Load Hero Reveal - const tl = gsap.timeline() - - tl.fromTo(".hero-tag", - { opacity: 0, y: 20 }, - { opacity: 1, y: 0, duration: 0.8, ease: "power3.out", delay: 0.2 } - ) - .fromTo(".hero-title-word", - { opacity: 0, y: 60, rotateX: -45 }, - { opacity: 1, y: 0, rotateX: 0, duration: 1, stagger: 0.1, ease: "expo.out" }, - "-=0.6" - ) + useGSAP( + () => { + gsap.registerPlugin(ScrollTrigger); - // 2. Scroll Animations for Text Blocks - const textBlocks = gsap.utils.toArray(".reveal-block") - textBlocks.forEach((block) => { - gsap.fromTo(block, - { opacity: 0, y: 40 }, + // 1. Page Load Hero Reveal + const tl = gsap.timeline(); + + tl.fromTo( + ".hero-tag", + { opacity: 0, y: 20 }, + { opacity: 1, y: 0, duration: 0.8, ease: "power3.out", delay: 0.2 }, + ).fromTo( + ".hero-title-word", + { opacity: 0, y: 60, rotateX: -45 }, { opacity: 1, y: 0, - duration: 1.2, - ease: "power3.out", - clearProps: "all", - scrollTrigger: { - trigger: block, - start: "top 90%", - } - } - ) - }) + rotateX: 0, + duration: 1, + stagger: 0.1, + ease: "expo.out", + }, + "-=0.6", + ); - // 3. Staggered Grid for "Why Singularity" - gsap.fromTo(".why-card", - { opacity: 0, y: 40, scale: 0.95 }, - { - opacity: 1, - y: 0, - scale: 1, - duration: 0.8, - stagger: 0.15, - ease: "power2.out", - clearProps: "all", - scrollTrigger: { - trigger: ".why-grid", - start: "top 85%", - } - } - ) + // 2. Scroll Animations for Text Blocks + const textBlocks = gsap.utils.toArray(".reveal-block"); + textBlocks.forEach((block) => { + gsap.fromTo( + block, + { opacity: 0, y: 40 }, + { + opacity: 1, + y: 0, + duration: 1.2, + ease: "power3.out", + clearProps: "all", + scrollTrigger: { + trigger: block, + start: "top 90%", + }, + }, + ); + }); - }, { scope: containerRef }) + // 3. Staggered Grid for "Why Singularity" + gsap.fromTo( + ".why-card", + { opacity: 0, y: 40, scale: 0.95 }, + { + opacity: 1, + y: 0, + scale: 1, + duration: 0.8, + stagger: 0.15, + ease: "power2.out", + clearProps: "all", + scrollTrigger: { + trigger: ".why-grid", + start: "top 85%", + }, + }, + ); + }, + { scope: containerRef }, + ); return ( -
- -
+
+
{/* HERO SECTION */} -
+

Decode // Innovate // Transform

- About - Singularity - Student - Lab + + About + + + Singularity + + + Student + + + Lab +

{/* VISION & MISSION */} -
- +

Our Vision

- We aim to revolutionize the future of technology by driving groundbreaking research and fostering an environment where innovation knows no bounds. Through inclusive collaboration, we strive to create transformative solutions that bridge the gap between ideas and real-world impact. + We aim to revolutionize the future of technology by driving + groundbreaking research and fostering an environment where + innovation knows no bounds. Through inclusive collaboration, we + strive to create transformative solutions that bridge the gap + between ideas and real-world impact.

@@ -103,29 +129,30 @@ export default function AboutPage() {

- To push the limits of technological advancement with cutting-edge tools and research-driven innovation. + To push the limits of technological advancement with + cutting-edge tools and research-driven innovation.

- We are dedicated to knowledge sharing and empowering the next generation of tech leaders, ensuring a future where technology serves all. + We are dedicated to knowledge sharing and empowering the next + generation of tech leaders, ensuring a future where technology + serves all.

- {/* LEADERSHIP SECTION */} -
- +

Founding Student

-
+
- Jayanth Ramakrishnan
@@ -135,14 +162,29 @@ export default function AboutPage() {

Founder & Lead

-
- + @@ -155,12 +197,12 @@ export default function AboutPage() { Founding Faculty
-
+
- Prof. CV Tomy
@@ -172,12 +214,12 @@ export default function AboutPage() {

-
+
- Dr. Murali Krishna Enduri
@@ -196,12 +238,12 @@ export default function AboutPage() {

Faculty Advisor

-
+
- Dr. Priyanka
@@ -214,14 +256,17 @@ export default function AboutPage() {
-
{/* WHY SINGULARITY GRID */}
- Singularity Student Lab Logo -

+ Singularity Student Lab Logo +

Why Singularity?

@@ -230,25 +275,34 @@ export default function AboutPage() { {[ { title: "Learn by Doing", - desc: "We build, prototype, and explore real-world problems through research-driven projects, hackathons, and innovation sprints." + desc: "We build, prototype, and explore real-world problems through research-driven projects, hackathons, and innovation sprints.", }, { title: "Multi-Domain Labs", - desc: "Whether you're into AI, Quantum, Robotics, Cybersecurity, or AR/VR — there's a place for you. Cross-domain projects are encouraged and supported." + desc: "Whether you're into AI, Quantum, Robotics, Cybersecurity, or AR/VR — there's a place for you. Cross-domain projects are encouraged and supported.", }, { title: "Collaborate with the Best", - desc: "Work with passionate students, dedicated mentors, and industry leaders. We're supported by the Microsoft Student Community and growing partnerships every semester." + desc: "Work with passionate students, dedicated mentors, and industry leaders. We're supported by the Microsoft Student Community and growing partnerships every semester.", }, { title: "Research with Impact", - desc: "From publishing papers to building MVPs — we incubate ideas that can evolve into startups, research publications, or community-focused solutions." - } + desc: "From publishing papers to building MVPs — we incubate ideas that can evolve into startups, research publications, or community-focused solutions.", + }, ].map((feature, i) => ( -
-
0{i + 1} // CAPABILITY
-

{feature.title}

-

{feature.desc}

+
+
+ 0{i + 1} // CAPABILITY +
+

+ {feature.title} +

+

+ {feature.desc} +

))} @@ -261,41 +315,58 @@ export default function AboutPage() {

An Optimistic Approach

- +
-

Quantum Horizons

+

+ Quantum Horizons +

- Quantum computing is reshaping cybersecurity, AI, and complex problem-solving. We provide tools for students to explore quantum algorithms and real-world applications. + Quantum computing is reshaping cybersecurity, AI, and complex + problem-solving. We provide tools for students to explore + quantum algorithms and real-world applications.

-

Decentralized Trust

+

+ Decentralized Trust +

- Web3 and blockchain technologies are redefining data ownership, transparency, and digital trust. We build decentralized applications that empower users and secure the internet of tomorrow. + Web3 and blockchain technologies are redefining data + ownership, transparency, and digital trust. We build + decentralized applications that empower users and secure the + internet of tomorrow.

-

Immersive Realities

+

+ Immersive Realities +

- By bridging the physical and digital worlds through Mixed Reality (XR), we are creating intuitive, spatial experiences that transform how we learn, interact, and solve problems. + By bridging the physical and digital worlds through Mixed + Reality (XR), we are creating intuitive, spatial experiences + that transform how we learn, interact, and solve problems.

-

Inclusive Progress

+

+ Inclusive Progress +

- With low-code and no-code platforms, we enable students from all backgrounds to develop solutions without deep programming expertise. Empowering non-tech innovators is key to our mission. + With low-code and no-code platforms, we enable students from + all backgrounds to develop solutions without deep programming + expertise. Empowering non-tech innovators is key to our + mission.

-
- ) -} \ No newline at end of file + ); +} diff --git a/src/app/globals.css b/src/app/globals.css index c2ccc1a..3ea158c 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,13 +1,51 @@ @import "tailwindcss"; +/* ── GPU Compositor Layer Promotions ─────────────────────────────────────── */ +/* Elements that animate frequently get their own GPU layer from the start. */ +/* This eliminates the "compositing surprise" jank on first animation. */ +#smooth-content { + will-change: transform; + transform: translateZ(0); +} + +.marquee-track { + will-change: transform; + transform: translateZ(0); +} + +.marquee-logo { + will-change: transform; + transform: translateZ(0); +} + +.hero-heading { + will-change: transform, opacity; + transform: translateZ(0); +} + +.parallax-grid img { + will-change: transform, opacity; + transform: translateZ(0); +} + +.header__marq { + will-change: transform, opacity; + transform: translateZ(0); +} + + @theme inline { --color-background: #0a0a0a; --color-foreground: #ededed; --animate-marquee: marquee 25s linear infinite; @keyframes marquee { - 0% { transform: translateX(0%); } - 100% { transform: translateX(-100%); } + 0% { + transform: translateX(0%); + } + 100% { + transform: translateX(-100%); + } } } @@ -20,20 +58,29 @@ body { .card-interactive { transform-style: preserve-3d; transition: transform 0.25s ease; + /* GPU layer promotion — avoids repaint on every hover/scroll */ + will-change: transform; + backface-visibility: hidden; + /* Contain layout & paint to this card — prevents expensive full-page repaints */ + contain: layout style paint; } .card-bg { transform: translateZ(-40px); + will-change: transform, opacity, filter; + backface-visibility: hidden; } .lab-logo { transform: translateZ(40px); + will-change: transform, opacity; + backface-visibility: hidden; } .card-glow { background: radial-gradient( circle at center, - rgba(255,255,255,0.15), + rgba(255, 255, 255, 0.15), transparent 60% ); } @@ -41,12 +88,32 @@ body { .card-light { background: radial-gradient( circle at var(--x) var(--y), - rgba(255,255,255,0.25), + rgba(255, 255, 255, 0.25), transparent 40% ); transition: opacity 0.3s; } -.lab-logo{ +.lab-logo { transform: translateZ(40px); -} \ No newline at end of file +} + +/* Prevent horizontal overflow and disable heavy 3D transforms on mobile */ +@media (max-width: 767px) { + body { + overflow-x: hidden; + } + + .card-interactive { + transform: none !important; + transform-style: flat; + } + + .card-bg { + transform: none; + } + + .lab-logo { + transform: none; + } +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 9876b70..9385a73 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,33 +1,43 @@ "use client"; -import { useRef, useState } from 'react'; -import { Flip } from "gsap/Flip" -import { useRouter } from 'next/navigation'; -import gsap from 'gsap'; -import { ScrollTrigger } from 'gsap/ScrollTrigger'; -import { ScrollSmoother } from 'gsap/ScrollSmoother'; -import { DrawSVGPlugin } from 'gsap/DrawSVGPlugin'; -import { ScrollToPlugin } from 'gsap/ScrollToPlugin'; -import { useGSAP } from '@gsap/react'; - -import { labs } from '../data/labs'; -import { FallingPattern } from '../components/ui/falling-pattern'; -import { Particles } from '../components/ui/particles'; -import { ClipPathLinks } from '../components/ui/clip-path-links'; -import Footer from '../components/Footer'; - -gsap.registerPlugin(ScrollTrigger, ScrollSmoother, DrawSVGPlugin, Flip, ScrollToPlugin) - -const CLOUDINARY_BASE = "https://res.cloudinary.com/djtemmctt/image/upload/q_auto:eco,f_auto/"; -const CLOUDINARY_BASEVID = "https://res.cloudinary.com/djtemmctt/video/upload/q_auto:eco,f_auto/"; - -const singularityLogo = "https://res.cloudinary.com/djtemmctt/image/upload/v1771104005/singularity_new_logo_knedxr.png"; +import { useRef, useState } from "react"; +import { Flip } from "gsap/all"; +import { useRouter } from "next/navigation"; +import gsap from "gsap"; +import { ScrollTrigger } from "gsap/ScrollTrigger"; +import { ScrollSmoother } from "gsap/ScrollSmoother"; +import { DrawSVGPlugin } from "gsap/DrawSVGPlugin"; +import { ScrollToPlugin } from "gsap/ScrollToPlugin"; +import { useGSAP } from "@gsap/react"; + +import { labs } from "../data/labs"; +import { FallingPattern } from "../components/ui/falling-pattern"; +import { Particles } from "../components/ui/particles"; +import { ClipPathLinks } from "../components/ui/clip-path-links"; +import Footer from "../components/Footer"; +import TargetCursor from "../components/ui/TargetCursor"; + +gsap.registerPlugin( + ScrollTrigger, + ScrollSmoother, + DrawSVGPlugin, + Flip, + ScrollToPlugin, +); + +const CLOUDINARY_BASE = + "https://res.cloudinary.com/djtemmctt/image/upload/q_auto:eco,f_auto/"; +const CLOUDINARY_BASEVID = + "https://res.cloudinary.com/djtemmctt/video/upload/q_auto:eco,f_auto/"; + +const singularityLogo = + "https://res.cloudinary.com/djtemmctt/image/upload/v1771104005/singularity_new_logo_knedxr.png"; export default function Hub() { const container = useRef(null); const router = useRouter(); const [isNavigating, setIsNavigating] = useState(false); - + const videoRefs = useRef>({}); const playVideo = (index: number) => { @@ -40,129 +50,159 @@ export default function Hub() { if (vid && !vid.paused) vid.pause(); }; - useGSAP(() => { - const smoother = ScrollSmoother.create({ - wrapper: "#smooth-wrapper", - content: "#smooth-content", - smooth: 1.35, - effects: true, - normalizeScroll: true, - ignoreMobileResize: true - }); - - const gTl = gsap.timeline(); - - gTl.to(".hero-section", { - opacity: 1, - duration: 0.8, - ease: "power2.out" - }); - - gsap.to(".lab-page", { - opacity: 1, - duration: 1, - ease: "power2.out" - }) - - gsap.from(".hero-heading", { - scale: 0.92, - duration: 1.0, - ease: "expo.out" - }); - - gTl.from(".hero-heading > span:first-child", { - y: 120, - opacity: 0, - duration: 1.6, - ease: "expo.out" - }, "-=0.2"); - - gTl.fromTo(".hero-heading > span:last-child", - { opacity: 0, filter: "blur(10px)", letterSpacing: "1em" }, - { opacity: 0.5, filter: "blur(0px)", letterSpacing: "0.5em", duration: 1.2, ease: "power3.out" }, - "-=1" - ); - - gTl.fromTo(".parallax-grid img", - { opacity: 0, y: 80, scale: 0.96 }, - { opacity: 1, y: 0, scale: 1, stagger: 0.08, duration: 1.6, ease: "power3.out" }, - "-=1.2" - ); - - gTl.from(".header__marq", { - yPercent: 100, - opacity: 0, - duration: 0.8, - ease: "expo.out" - }, "-=0.8"); - - gTl.from(".draw-path", { - drawSVG: "0%", - duration: 1.0, - ease: "power3.out" - }, "-=0.6"); - - gsap.to(".marquee-track", { - xPercent: -50, - ease: "none", - scrollTrigger: { - trigger: "#smooth-content", - start: "top top", - end: "bottom bottom", - scrub: 1 - } - }); - - gsap.to(".marquee-logo", { - rotation: 3600, - transformOrigin: "center center", - ease: "none", - scrollTrigger: { - trigger: "#smooth-content", - start: "top top", - end: "bottom bottom", - scrub: 1 - } - }); - - const cards = gsap.utils.toArray(".card-wrapper"); - - cards.forEach((card, i) => { - ScrollTrigger.create({ - trigger: card, - start: "top 20%", - scrub: 0.5, - endTrigger: ".cards-container", - end: "bottom 90%", - pin: true, - pinSpacing: false, - anticipatePin: 1, - onEnter: () => { - playVideo(i); - if (i >= 2) pauseVideo(i - 2); + useGSAP( + () => { + const smoother = ScrollSmoother.create({ + wrapper: "#smooth-wrapper", + content: "#smooth-content", + smooth: 1.35, + effects: true, + normalizeScroll: true, + ignoreMobileResize: true, + }); + + const gTl = gsap.timeline(); + + gTl.to(".hero-section", { + opacity: 1, + duration: 0.8, + ease: "power2.out", + }); + + gsap.to(".lab-page", { + opacity: 1, + duration: 1, + ease: "power2.out", + }); + + gsap.from(".hero-heading", { + scale: 0.92, + duration: 1.0, + ease: "expo.out", + }); + + gTl.from( + ".hero-heading > span:first-child", + { + y: 120, + opacity: 0, + duration: 1.6, + ease: "expo.out", + }, + "-=0.2", + ); + + gTl.fromTo( + ".hero-heading > span:last-child", + { opacity: 0, filter: "blur(10px)", letterSpacing: "1em" }, + { + opacity: 0.5, + filter: "blur(0px)", + letterSpacing: "0.5em", + duration: 1.2, + ease: "power3.out", + }, + "-=1", + ); + + gTl.fromTo( + ".parallax-grid img", + { opacity: 0, y: 80, scale: 0.96 }, + { + opacity: 1, + y: 0, + scale: 1, + stagger: 0.08, + duration: 1.6, + ease: "power3.out", + }, + "-=1.2", + ); + + gTl.from( + ".header__marq", + { + yPercent: 100, + opacity: 0, + duration: 0.8, + ease: "expo.out", + }, + "-=0.8", + ); + + gTl.from( + ".draw-path", + { + drawSVG: "0%", + duration: 1.0, + ease: "power3.out", + }, + "-=0.6", + ); + + gsap.to(".marquee-track", { + xPercent: -50, + ease: "none", + scrollTrigger: { + trigger: "#smooth-content", + start: "top top", + end: "bottom bottom", + scrub: 1, }, - onLeaveBack: () => { - if (i >= 2) playVideo(i - 2); - } }); - if (i < cards.length - 1) { - gsap.to(card, { - scale: 0.94, - opacity: 0.3, - ease: "none", - scrollTrigger: { - trigger: cards[i + 1], - start: "top 80%", - end: "top 12%", - scrub: true - } + gsap.to(".marquee-logo", { + rotation: 3600, + transformOrigin: "center center", + ease: "none", + scrollTrigger: { + trigger: "#smooth-content", + start: "top top", + end: "bottom bottom", + scrub: 1, + }, + }); + + const cards = gsap.utils.toArray(".card-wrapper"); + + cards.forEach((card, i) => { + ScrollTrigger.create({ + trigger: card, + start: "top 20%", + scrub: 0.5, + endTrigger: ".cards-container", + end: "bottom 90%", + pin: true, + pinSpacing: false, + anticipatePin: 1, + onEnter: () => { + playVideo(i); + if (i >= 2) pauseVideo(i - 2); + }, + onLeaveBack: () => { + if (i >= 2) playVideo(i - 2); + }, }); - } - }); - return () => smoother.kill(); - }, { scope: container }); + if (i < cards.length - 1) { + gsap.to(card, { + scale: 0.94, + opacity: 0.3, + ease: "none", + scrollTrigger: { + trigger: cards[i + 1], + start: "top 80%", + end: "top 12%", + scrub: true, + }, + }); + } + }); + + return () => smoother.kill(); + }, + { scope: container }, + ); const handleMediaLoad = () => { requestAnimationFrame(() => ScrollTrigger.refresh()); @@ -170,56 +210,97 @@ export default function Hub() { return (
+
-
- +
{/* BACKGROUND LAYER */}
- + +
{/* HERO SECTION */} -
-
-

+
+
+

Singularity - - + +
- {""} + + {""} +

-
- {[ - "first_jqmtz8.jpg", - "WhatsApp_Image_2026-02-15_at_2.46.25_AM_ttclec.jpg", - "WhatsApp_Image_2026-02-15_at_2.46.25_AM_1_b5r7lz.jpg", - "WhatsApp_Image_2026-02-15_at_2.46.25_AM_2_f2qg5v.jpg" - ].map((img, idx) => ( - - ))} +
+ {[ + "first_jqmtz8.jpg", + "WhatsApp_Image_2026-02-15_at_2.46.25_AM_ttclec.jpg", + "WhatsApp_Image_2026-02-15_at_2.46.25_AM_1_b5r7lz.jpg", + "WhatsApp_Image_2026-02-15_at_2.46.25_AM_2_f2qg5v.jpg", + ].map((img, idx) => ( + + ))}
{[...Array(12)].map((_, i) => ( - + singularity student lab - + ))}
@@ -227,44 +308,154 @@ export default function Hub() {
{/* CARDS SECTION */} -
+
{labs.map((lab, i) => ( -
+
{ + if (isNavigating) return; + setIsNavigating(true); + + const card = document.getElementById( + `lab-card-${lab.id}`, + ); + if (!card) return; + + const rect = card.getBoundingClientRect(); + const clone = card.cloneNode(true) as HTMLElement; + + // Position using fixed coords, but we'll animate via transform + // (GPU-only: no layout recalc on every frame) + clone.style.cssText = ` + position: fixed; top: ${rect.top}px; left: ${rect.left}px; + width: ${rect.width}px; height: ${rect.height}px; margin: 0; + z-index: 9999; pointer-events: none; border-radius: 0; overflow: hidden; + will-change: transform; backface-visibility: hidden; + `; + document.body.appendChild(clone); + + const curtain = document.createElement("div"); + curtain.style.cssText = ` + position: fixed; inset: 0; background: black; + z-index: 9998; opacity: 0; pointer-events: none; + will-change: opacity; + `; + document.body.appendChild(curtain); + + const cloneVideo = clone.querySelector( + "video", + ) as HTMLVideoElement | null; + if (cloneVideo) { + cloneVideo.muted = true; + cloneVideo.play().catch(() => {}); + } + + // Calculate scale factors to fill viewport via transform + // Avoids animating top/left/width/height (layout-thrashing) + const scaleX = window.innerWidth / rect.width; + const scaleY = window.innerHeight / rect.height; + const targetX = -rect.left + (window.innerWidth - rect.width) / 2; + const targetY = -rect.top + (window.innerHeight - rect.height) / 2; + + const tl = gsap.timeline({ + onComplete: () => { + router.push(`/labs/${lab.id}`); + clone.remove(); + curtain.remove(); + }, + }); + + // GPU-only animation: translate + scale (compositor thread, no layout) + tl.to(clone, { + x: targetX, + y: targetY, + scaleX, + scaleY, + duration: 1, + ease: "power2.inOut", + force3D: true, + }); + if (cloneVideo) { + tl.to( + cloneVideo, + { + opacity: 0.9, + scale: 1.1, + filter: "grayscale(0)", + duration: 1, + ease: "power2.inOut", + force3D: true, + }, + "<", + ); + } + tl.to( + curtain, + { opacity: 1, duration: 0.5, ease: "power2.inOut" }, + "-=0.2", + ); + }} onMouseMove={(e) => { - const rect = e.currentTarget.getBoundingClientRect() - const x = (e.clientX - rect.left) / rect.width - const y = (e.clientY - rect.top) / rect.height - const rotateX = (y - 0.5) * 22 - const rotateY = (x - 0.5) * -22 - const xPos = e.clientX - rect.left - const yPos = e.clientY - rect.top - - e.currentTarget.style.setProperty("--x", `${xPos}px`) - e.currentTarget.style.setProperty("--y", `${yPos}px`) - - gsap.to(e.currentTarget, { rotateX, rotateY, scale: 1.02, transformPerspective: 1200, duration: 0.2, ease: "power2.out" }) + const isTouchDevice = + "ontouchstart" in window || navigator.maxTouchPoints > 0; + if (isTouchDevice) return; + const rect = e.currentTarget.getBoundingClientRect(); + const x = (e.clientX - rect.left) / rect.width; + const y = (e.clientY - rect.top) / rect.height; + const rotateX = (y - 0.5) * 22; + const rotateY = (x - 0.5) * -22; + const xPos = e.clientX - rect.left; + const yPos = e.clientY - rect.top; + + e.currentTarget.style.setProperty("--x", `${xPos}px`); + e.currentTarget.style.setProperty("--y", `${yPos}px`); + + gsap.to(e.currentTarget, { + rotateX, + rotateY, + scale: 1.02, + transformPerspective: 1200, + duration: 0.2, + ease: "power2.out", + }); }} onMouseLeave={(e) => { - gsap.to(e.currentTarget, { rotateX: 0, rotateY: 0, scale: 1, duration: 0.5, ease: "power3.out" }) + const isTouchDevice = + "ontouchstart" in window || navigator.maxTouchPoints > 0; + if (isTouchDevice) return; + gsap.to(e.currentTarget, { + rotateX: 0, + rotateY: 0, + scale: 1, + duration: 0.5, + ease: "power3.out", + }); }} >
{lab.video_id ? (
-
+
+
); -} \ No newline at end of file +} diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index b7bd766..d27ecf6 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -1,25 +1,30 @@ -"use client" +"use client"; -import Link from "next/link" -import { Instagram, Linkedin, Mail, MapPin } from "lucide-react" +import Link from "next/link"; +import { Instagram, Linkedin, Mail, MapPin } from "lucide-react"; -const singularityLogo = "https://res.cloudinary.com/djtemmctt/image/upload/v1771104005/singularity_new_logo_knedxr.png" +const singularityLogo = + "https://res.cloudinary.com/djtemmctt/image/upload/v1771104005/singularity_new_logo_knedxr.png"; export default function Footer() { return ( -