-
+ {/* Light sticker chip behind the mark — the "lyts" wordmark is dark
+ navy, so it vanishes on the dark hero without a light backing. */}
+
@@ -323,7 +333,7 @@ export default function Header() {
@@ -281,40 +283,60 @@ export function TheWhy() {
}
/* 03 — THE PROBLEM
- GSAP: the three cards deal in like cards flipped onto a table — a 3D
- rotationY/lift entrance scrubbed to scroll, with each number badge
- popping as its card lands. */
+ GSAP: 3D Horizontal Flip Card Reveal */
export function TheProblem() {
useGSAP(() => {
syncLenisScrollTrigger();
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (reduce) return;
- /* Play-once on enter: the cards deal in with a gentle 3D lift, then settle
- perfectly flat and aligned (transforms cleared) so nothing lingers
- skewed or overlaps the note below. */
+ /* Set 3D perspective container and initial 180-degree horizontal flip state */
+ gsap.set("#the-problem .prob-card-grid", { perspective: 1400 });
+
+ gsap.set("#the-problem .prob-card", {
+ transformStyle: "preserve-3d",
+ transformOrigin: "50% 50%",
+ rotationY: -180,
+ y: 45,
+ scale: 0.85,
+ autoAlpha: 0,
+ boxShadow: "0 30px 60px rgba(0,0,0,0.5)",
+ });
+
+ gsap.set("#the-problem .prob-num", {
+ scale: 0,
+ rotation: -30,
+ transformOrigin: "50% 50%",
+ });
+
const tl = gsap.timeline({
scrollTrigger: { trigger: "#the-problem", start: "top 72%", once: true },
- defaults: { ease: "power3.out" },
- onComplete: () => gsap.set("#the-problem .prob-card", { clearProps: "transform" }),
+ defaults: { ease: "back.out(1.5)" },
+ onComplete: () => gsap.set("#the-problem .prob-card", { clearProps: "transform,transformStyle" }),
});
- tl.from("#the-problem .prob-card", {
- y: 60,
- rotationY: -18,
- rotationX: 8,
- opacity: 0,
- transformOrigin: "50% 100%",
- stagger: 0.14,
- duration: 0.9,
+
+ /* Horizontal 3D flip animation sequence */
+ tl.to("#the-problem .prob-card", {
+ rotationY: 0,
+ y: 0,
+ scale: 1,
+ autoAlpha: 1,
+ boxShadow: "6px 6px 0 0 #0a0a0a",
+ duration: 0.85,
+ stagger: 0.2,
+ ease: "back.out(1.5)",
})
- .from("#the-problem .prob-num", {
- scale: 0,
- rotation: -20,
- transformOrigin: "50% 50%",
- stagger: 0.14,
- duration: 0.5,
- ease: "back.out(2.2)",
- }, 0.2);
+ .to(
+ "#the-problem .prob-num",
+ {
+ scale: 1,
+ rotation: 0,
+ duration: 0.45,
+ stagger: 0.2,
+ ease: "back.out(2.2)",
+ },
+ "-=0.7"
+ );
}, []);
const cards = [
@@ -324,24 +346,24 @@ export function TheProblem() {
];
return (
- ⚡ the problem
+ the problem
traditional loyalty is broken. here's why.
-
+
{cards.map((c) => (
{c.n}
{c.t}
{c.items.map((i) => (
-
✗ {i}
+
{i}
))}
{c.foot}
@@ -349,7 +371,7 @@ export function TheProblem() {
))}
- ❗ customers create value before spending. then traditional loyalty assigns zero credit.
+ customers create value before spending. then traditional loyalty assigns zero credit.
))}
@@ -391,13 +413,37 @@ export function TheShift() {
- 🔥 biggest miss in consumer tech right now: offline commerce still rewards only spending. that changes with LYTs.
+ biggest miss in consumer tech right now: offline commerce still rewards only spending. that changes with LYTs.
);
}
+/* Solid "slab" icons — bare black glyphs sitting inline with the chip label.
+ Each one loops a motion matching its meaning (see MeetLyts' GSAP hook). */
+const SLAB_PATHS: Record = {
+ walk: "M13.5 5.5c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zM9.8 8.9 7 23h2.1l1.8-8 2.1 2v6h2v-7.5l-2.1-2 .6-3C14.8 12 16.8 13 19 13v-2c-1.9 0-3.5-1-4.3-2.4l-1-1.6c-.4-.6-1-1-1.7-1-.3 0-.5.1-.8.1L6 8.3V13h2V9.6l1.8-.7z",
+ visit: "M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z",
+ friend: "M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z",
+ shop: "M18 6h-2c0-2.21-1.79-4-4-4S8 3.79 8 6H6c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-6-2c1.1 0 2 .9 2 2h-4c0-1.1.9-2 2-2z",
+ event: "M12 .6l3.1 7.5 8.1.7-6.1 5.3 1.8 7.9L12 17.8 5.1 22l1.8-7.9L.8 8.8l8.1-.7z",
+};
+
+function SlabIcon({ name }: { name: keyof typeof SLAB_PATHS | string }) {
+ return (
+
+
+
+ );
+}
+
/* 05 — MEET LYTs
GSAP: the wordmark "LYTs." lands letter-by-letter in 3D as the section
enters, then the value flow pops in beat by beat. The giant word also
@@ -433,12 +479,27 @@ export function MeetLyts() {
ease: "none",
scrollTrigger: { trigger: "#lyts", start: "top bottom", end: "bottom top", scrub: 0.6 },
});
+
+ /* ── living slab icons — each glyph loops a motion that matches its meaning ── */
+ const glyph = (name: string) => `#lyts .slab-icon[data-icon="${name}"] svg`;
+
+ // walk → steps up and down
+ gsap.to(glyph("walk"), { y: -2.5, duration: 0.42, yoyo: true, repeat: -1, ease: "sine.inOut" });
+ // visit → the pin drops and settles
+ gsap.to(glyph("visit"), { y: -3.5, duration: 0.75, yoyo: true, repeat: -1, ease: "power2.inOut" });
+ // friend → the pair rocks side to side
+ gsap.to(glyph("friend"), { rotation: 9, transformOrigin: "50% 80%", duration: 0.8, yoyo: true, repeat: -1, ease: "sine.inOut" });
+ // shop → the bag swings from its handle
+ gsap.to(glyph("shop"), { rotation: -11, transformOrigin: "50% 0%", duration: 0.9, yoyo: true, repeat: -1, ease: "sine.inOut" });
+ // event → the star spins and twinkles
+ gsap.to(glyph("event"), { rotation: 360, transformOrigin: "50% 50%", duration: 6, ease: "none", repeat: -1 });
+ gsap.to(glyph("event"), { scale: 0.78, duration: 0.7, yoyo: true, repeat: -1, ease: "sine.inOut" });
}, []);
return (
- ✦ introducing
+ introducing
-
- ))}
-
-
- );
-}
+import { Ico, withIcons } from "./Ico";
/* 12 — WHY NOW */
export function WhyNow() {
@@ -105,24 +24,95 @@ export function WhyNow() {
["🎮", "GAMIFIED LIFE", "Systems make it real.", "↑", C.orange, "#fff"],
["📉", "OLD LOYALTY FADING", "No real bond anymore.", "↓↓", C.pink, "#fff"],
] as const;
+
+ useGSAP(() => {
+ syncLenisScrollTrigger();
+ const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
+ if (reduce) return;
+
+ const cardsEl = gsap.utils.toArray("#why-now .game-card");
+ if (!cardsEl.length) return;
+
+ /* 3D perspective wrapper */
+ gsap.set("#why-now .game-card-grid", { perspective: 1200 });
+
+ /* Set cards in a fanned-out HAND at bottom center */
+ cardsEl.forEach((card, i) => {
+ gsap.set(card, {
+ transformStyle: "preserve-3d",
+ transformOrigin: "50% 120%",
+ autoAlpha: 0,
+ xPercent: (2 - i) * 110, // Gather cards at bottom center hand position
+ y: 140, // Lowered down in hand
+ scale: 0.6,
+ rotationZ: (i - 2) * 12, // Fan out angle in hand (-24deg to +24deg)
+ rotationY: -45, // Slightly tilted in hand
+ boxShadow: "0 25px 50px rgba(0,0,0,0.6)",
+ });
+ });
+
+ /* Card deal animation timeline on ScrollTrigger */
+ const tl = gsap.timeline({
+ scrollTrigger: {
+ trigger: "#why-now",
+ start: "top 72%",
+ once: true,
+ },
+ });
+
+ /* Step 1: Hand appears fanned open */
+ tl.to(cardsEl, {
+ autoAlpha: 1,
+ duration: 0.3,
+ stagger: 0.05,
+ ease: "power2.out",
+ });
+
+ /* Step 2: Deal cards from hand onto grid one by one */
+ tl.to(
+ cardsEl,
+ {
+ xPercent: 0,
+ y: 0,
+ scale: 1,
+ rotationZ: 0,
+ rotationY: 0,
+ boxShadow: "6px 6px 0 0 #0a0a0a",
+ duration: 0.65,
+ stagger: 0.16,
+ ease: "back.out(1.6)",
+ onComplete: () => {
+ gsap.set(cardsEl, { clearProps: "transform,transformStyle,perspective" });
+ },
+ },
+ "-=0.1"
+ );
+ });
+
return (
- ⏰ why this matters
+ why this matters
5 currents
are converging right now.
-
+
{cards.map(([e, t, s, a, bg, tc]) => (
-
-
{e}
-
{t}
-
{s}
-
{a}
-
+
+
+
+
{t}
+
{s}
+
+
{a}
+
))}
-
+
);
}
@@ -145,14 +135,14 @@ export function HowItWorks() {
const bottom = cards.slice(5);
return (
- ⚡ how it works
+ how it works
every action earns points{" "}
LYTs.
no app-opening rituals. no complicated loyalty rules.{" "}
- no waiting until you spend. just live your life, engage the world, and watch your LYTs grow. 🌱
+ no waiting until you spend. just live your life, engage the world, and watch your LYTs grow.
{/* Two counter-scrolling tracks. The opposing directions read as a
@@ -196,7 +186,7 @@ function ActionTile({
+LYTs
-
{emoji}
+
{title}
{sub}
@@ -233,7 +223,7 @@ export function OldVsNew() {
return (
- 💞 a new kind of loyalty
+ a new kind of loyalty
traditional loyalty said one thing.
@@ -241,25 +231,25 @@ export function OldVsNew() {
- 💻 TRADITIONAL LOYALTY
+ TRADITIONAL LOYALTY
the old rulespend first.earn later.
-
value is created only when you buy. participation = invisible. 🫥
+
value is created only when you buy. participation = invisible.
VS
- ✨ THE LYTS WAY
+ THE LYTS WAY
the new rulelive first.earn everywhere.
-
value is created when you participate. everyday life is the unlock. 🎉
+
value is created when you participate. everyday life is the unlock.
@@ -274,7 +264,7 @@ export function OldVsNew() {
/* 15 — THE VISION
GSAP: the headline reveals word by word with "social" punching in on an
overshoot, while the value list stacks up — each row slides in from the
- right and its ✓ ticks in with a springy pop, like a checklist completing. */
+ right and its ticks in with a springy pop, like a checklist completing. */
export function TheVision() {
const items = [
["every discovery has value", "#fff", "#0a0a0a"],
@@ -308,7 +298,7 @@ export function TheVision() {