Every animation is interactive — click, hover, and play. GSAP + CSS, copy-ready for Lumnix.
Showstopper animations for the first impression
card.addEventListener("mousemove", (e) => {\n const x = (e.clientX - rect.left) / rect.width - 0.5;\n gsap.to(card, { rotateY: x * 35, rotateX: -y * 35,\n transformPerspective: 800, duration: 0.3 });\n});\ncard.addEventListener("mouseleave", () => {\n gsap.to(card, { rotateY: 0, rotateX: 0, ease: "elastic.out(1,.4)" });\n});
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ@#$%";\nlet iteration = 0;\nconst interval = setInterval(() => {\n el.textContent = finalText.split("").map((char, i) =>\n i < iteration ? finalText[i]\n : chars[Math.floor(Math.random() * chars.length)]\n ).join("");\n iteration += 0.5;\n}, 30);
@property --angle { syntax: ""; initial-value: 0deg; }\n\n.card::before {\n background: conic-gradient(from var(--angle), #ff2d78, #7b2ff7, #00d4ff);\n animation: spin 3s linear infinite;\n}\n@keyframes spin { to { --angle: 360deg } }
const phrases = ["Find winning products.", "Ship faster."];\nlet pi=0, ci=0, deleting=false;\nfunction type() {\n const current = phrases[pi];\n el.textContent = current.substring(0, ci);\n if (!deleting) { ci++; if (ci > current.length) { deleting=true; setTimeout(type, 2000); return; } }\n else { ci--; if (ci < 0) { deleting=false; pi=(pi+1)%phrases.length; } }\n setTimeout(type, deleting ? 25 : 55);\n}
.gradient-text {\n background: linear-gradient(270deg, #00d4ff, #7b2ff7, #ff2d78, #4ade80);\n background-size: 300% 300%;\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n animation: shift 5s ease infinite;\n}\n@keyframes shift { 50% { background-position: 100% 50%; } }
@keyframes neon {\n 0%, 100% { box-shadow: 0 0 5px #00d4ff; }\n 50% { box-shadow: 0 0 15px #00d4ff, 0 0 45px #00d4ff44, 0 0 90px #00d4ff22; }\n}\n.neon-btn { animation: neon 2s ease-in-out infinite; }
\n \n\n
document.querySelectorAll(".floater").forEach((el, i) => {\n gsap.to(el, {\n y: -20, duration: 1.5 + i * 0.3,\n ease: "sine.inOut", repeat: -1, yoyo: true,\n delay: i * 0.5\n });\n});
element.addEventListener("mousemove", (e) => {\n const p = document.createElement("div");\n p.style.background = `hsl(${Math.random()*360}, 80%, 60%)`;\n gsap.to(p, { opacity: 0, scale: 0,\n y: (Math.random()-.5)*60, duration: 0.8,\n onComplete: () => p.remove() });\n});
section.addEventListener("mousemove", (e) => {\n const rect = section.getBoundingClientRect();\n section.style.background = `radial-gradient(\n circle 200px at ${e.clientX-rect.left}px ${e.clientY-rect.top}px,\n rgba(0,212,255,0.08), transparent)`;\n});
const circ = 2 * Math.PI * 70;\nconst offset = circ - (percent/100) * circ;\ngsap.to("#ring", { strokeDashoffset: offset,\n duration: 1.5, ease: "power2.out" });
document.querySelectorAll(".digit").forEach(digit => {\n const target = parseInt(digit.dataset.target);\n const inner = digit.querySelector("span");\n inner.textContent = "01234567890123456789" + target;\n gsap.to(inner, { y: -(20+target)*60, duration: 1.5, ease: "power2.out" });\n});
function countUp(el, target) {\n const obj = { val: 0 };\n gsap.to(obj, { val: target, duration: 1.8,\n snap: { val: 1 },\n onUpdate: () => el.textContent = Math.floor(obj.val).toLocaleString()\n });\n}
function toggle() {\n if (body.style.height === "0px") {\n gsap.to(body, { height: "auto", duration: 0.4 });\n gsap.to(arrow, { rotation: 180, duration: 0.3 });\n } else {\n gsap.to(body, { height: 0, duration: 0.3 });\n gsap.to(arrow, { rotation: 0, duration: 0.3 });\n }\n}
function ripple(e, el) {\n const span = document.createElement("span");\n const size = Math.max(rect.width, rect.height) * 2;\n span.style.background = "rgba(255,255,255,.35)";\n gsap.to(span, { scale: 1, opacity: 0, duration: 0.6,\n onComplete: () => span.remove() });\n}
card.addEventListener("mouseenter", () => {\n gsap.to(card, { y: -10, duration: 0.3,\n boxShadow: "0 16px 50px rgba(0,212,255,.15)" });\n});
gsap.fromTo(".list-item",\n { x: -30, opacity: 0 },\n { x: 0, opacity: 1, stagger: 0.12,\n duration: 0.5, ease: "power2.out" }\n);
@keyframes breathe {\n 0%, 100% { transform: scale(1); opacity: 1; }\n 50% { transform: scale(1.5); opacity: 0.5; }\n}\n.live-dot { animation: breathe 2.5s ease-in-out infinite; }
gsap.from(".reveal", {\n scale: 0.75, filter: "blur(12px)", opacity: 0,\n duration: 1, ease: "power2.out",\n scrollTrigger: { trigger: ".reveal", start: "top 85%" }\n});
for (let i = 0; i < 60; i++) {\n const p = document.createElement("div");\n p.style.background = `hsl(${Math.random()*360}, 85%, 60%)`;\n gsap.to(p, {\n x: (Math.random()-.5)*600, y: (Math.random()-.5)*500,\n rotation: Math.random()*720, opacity: 0, duration: 1\n });\n}