diff --git a/src/app/globals.css b/src/app/globals.css index 3b4ff5d..a144974 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -32,30 +32,52 @@ :root { --font-sora: 'Sora', sans-serif; --font-inter: 'Inter', sans-serif; + + /* Section gutters (ported from the loyaly website). + --sec-gutter = left/right side margin; --sec-gap = vertical gap between + stacked sections; --hdr-gutter = header side padding. */ + --sec-gutter: 20px; + --sec-gap: 20px; + --hdr-gutter: 48px; } -/* Custom Scrollbar */ +/* Phones — full-bleed sections, tighter header padding */ +@media (max-width: 600px) { + :root { + --sec-gutter: 12px; + --sec-gap: 12px; + --hdr-gutter: 20px; + } +} + +/* Custom Scrollbar — neobrutalist, on-theme (no track border) */ ::-webkit-scrollbar { - width: 8px; + width: 14px; } ::-webkit-scrollbar-track { - background: #090909; + background: #FBF3DD; } ::-webkit-scrollbar-thumb { - background: #333333; - border-radius: 4px; + background: linear-gradient(180deg, #FFC72C 0%, #FF2E88 60%, #8B3DFF 100%); + border: 2px solid #0a0a0a; + border-radius: 10px; + background-clip: padding-box; } ::-webkit-scrollbar-thumb:hover { - background: #FFC72C; + background: linear-gradient(180deg, #FFD95A 0%, #FF2E88 55%, #8B3DFF 100%); + background-clip: padding-box; } /* Base Body Styles */ html, body { - background-color: #090909; + background-color: #FBF3DD; color: #FFFFFF; font-family: var(--font-inter), sans-serif; overflow-x: hidden; scroll-behavior: smooth; + color-scheme: light; + scrollbar-width: auto; + scrollbar-color: #FF2E88 #FBF3DD; } h1, h2, h3, h4, h5, h6 { diff --git a/src/app/layout.tsx b/src/app/layout.tsx index f2fecd6..7eb4ba1 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -50,7 +50,7 @@ export default function RootLayout({ className={`${sora.variable} ${inter.variable} h-full antialiased`} suppressHydrationWarning > - + {children} diff --git a/src/app/page.tsx b/src/app/page.tsx index 92c24c7..0190045 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,4 +1,6 @@ -import SideNav, { NavSection } from "@/components/layout/SideNav"; +import Header from "@/components/layout/Header"; +import Footer from "@/components/layout/Footer"; +import ScrollProgress from "@/components/layout/ScrollProgress"; import { Intro, TheWhy, @@ -21,30 +23,11 @@ import { ThePromise, } from "@/components/story/SectionsB"; -const sections: NavSection[] = [ - { id: "intro", label: "Intro" }, - { id: "the-why", label: "The Why" }, - { id: "the-problem", label: "The Problem" }, - { id: "the-shift", label: "The Shift" }, - { id: "lyts", label: "Meet LYTs" }, - { id: "one-day", label: "One Day" }, - { id: "feelings", label: "Feelings" }, - { id: "difference", label: "Difference" }, - { id: "the-loop", label: "The Loop" }, - { id: "for-you", label: "For You" }, - { id: "for-business", label: "For Business" }, - { id: "why-now", label: "Why Now" }, - { id: "how-it-works", label: "How It Works" }, - { id: "old-vs-new", label: "Old vs New" }, - { id: "the-vision", label: "The Vision" }, - { id: "our-belief", label: "Our Belief" }, - { id: "the-promise", label: "The Promise" }, -]; - export default function Home() { return (
- + +
@@ -62,6 +45,7 @@ export default function Home() { +
); } diff --git a/src/components/common/Providers.tsx b/src/components/common/Providers.tsx index 83ac9ce..337b453 100644 --- a/src/components/common/Providers.tsx +++ b/src/components/common/Providers.tsx @@ -35,7 +35,7 @@ export default function Providers({ children }: { children: React.ReactNode }) { }, []); return ( - + {children} diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx index 15eb256..fb56163 100644 --- a/src/components/layout/Footer.tsx +++ b/src/components/layout/Footer.tsx @@ -1,152 +1,246 @@ -"use client"; +'use client'; -import { useState } from "react"; -import { Send, Instagram, Twitter, MessageSquare, Heart } from "lucide-react"; -import Link from "next/link"; +import { useState, useEffect } from 'react'; +import Link from 'next/link'; +import Image from 'next/image'; + +const INK = '#0a0a0a'; + +const NAV = [ + { + title: 'Earn', + color: '#C8FF37', // lime + links: [ + { label: 'Walk to Earn', href: '#for-you' }, + { label: 'Play & Win', href: '#for-you' }, + { label: 'Refer Friends', href: '#for-you' }, + ], + }, + { + title: 'Platform', + color: '#FF2E88', // pink + links: [ + { label: 'For You', href: '#for-you' }, + { label: 'For Business', href: '#for-business' }, + { label: 'How It Works', href: '#how-it-works' }, + ], + }, + { + title: 'Company', + color: '#8B3DFF', // purple + links: [ + { label: 'The Vision', href: '#the-vision' }, + { label: 'The Why', href: '#the-why' }, + { label: 'Contact Us', href: '#for-business' }, + ], + }, +]; + +const SOCIALS = [ + { + label: 'Instagram', href: 'https://instagram.com/lytsup', bg: '#FF2E88', + icon: , + }, + { + label: 'X / Twitter', href: 'https://twitter.com/lytsup', bg: '#8B3DFF', + icon: , + }, + { + label: 'YouTube', href: 'https://www.youtube.com/@lytsup', bg: '#FF6B35', + icon: , + }, +]; export default function Footer() { - const [email, setEmail] = useState(""); - const [subscribed, setSubscribed] = useState(false); + const [showScroll, setShowScroll] = useState(false); - const handleSubscribe = (e: React.FormEvent) => { - e.preventDefault(); - if (email) { - setSubscribed(true); - setEmail(""); - setTimeout(() => setSubscribed(false), 5000); - } - }; + useEffect(() => { + const onScroll = () => setShowScroll(window.scrollY > 400); + window.addEventListener('scroll', onScroll, { passive: true }); + return () => window.removeEventListener('scroll', onScroll); + }, []); return ( -
- {/* Ambient background light glows */} -
-
+ <> +
+ {/* ── BIG CTA STICKER CARD ── */} +
+
+ {/* corner sticker */} + join the loop -
-
- {/* Brand Info */} -
- - - LytsUp +
+ + Earn. Spend. Live More. +

+ Turn everyday moments
+ + into real value. + +

+
+ + + Get Started + -

- The premier social currency app for Gen Z. Earn rewards for walking, playing, and living your best life. -

-

- Earn. Spend. Live More. -

-
- - {/* Site Navigation */} -
-

Navigate

- -
- - {/* Corporate Links */} -
-

Partnerships

-
    -
  • - Become a Partner -
  • -
  • - Brands Portal -
  • -
  • - API Integration -
  • -
  • - Support Hub -
  • -
-
- - {/* Newsletter subscription */} -
-

Get Secret Drops

-

- Subscribe to get notified about ultra-rare brand voucher drops. -

-
- setEmail(e.target.value)} - required - className="w-full bg-white/5 border border-white/10 rounded-xl px-4 py-2.5 text-sm text-white focus:outline-none focus:border-primary/50 transition-colors pr-10" - /> - -
- {subscribed && ( -

- You're in! Check your inbox for secret codes. -

- )}
-
- - {/* Socials & Copyright */} -
-
- © {new Date().getFullYear()} LytsUp.com. All rights reserved. - - - Made with for Gen Z - + {/* ── COLUMNS ── */} +
+ {/* Brand */} +
+ + LytsUp + +

+ Social currency for real life. Earn LYTs for the things you already do — then spend them on stuff you actually want. +

+
+ {SOCIALS.map(({ label, href, icon, bg }) => ( + {icon} + ))} +
- {/* Social Icons */} -
- - - - - - - - - + {/* Nav columns */} + {NAV.map(col => ( + + ))} +
+ + {/* ── BOTTOM BAR ── */} +
+ + © {new Date().getFullYear()} LytsUp. Made with + for Gen Z. + +
+ {[['Terms', '#'], ['Privacy', '#'], ['Cookies', '#']].map(([l, h]) => ( + ((e.currentTarget as HTMLElement).style.color = '#FFC72C')} + onMouseLeave={e => ((e.currentTarget as HTMLElement).style.color = 'rgba(255,255,255,0.5)')} + >{l} + ))}
-
-
+ + +
+ + {/* Scroll-to-top — neobrutalist */} + + + ); } diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx new file mode 100644 index 0000000..ae56b53 --- /dev/null +++ b/src/components/layout/Header.tsx @@ -0,0 +1,383 @@ +'use client'; + +import { useState, useEffect, useRef } from 'react'; +import Link from 'next/link'; +import Image from 'next/image'; + +const INK = '#0a0a0a'; + +const NAV_LINKS = [ + { label: 'Earn', href: '#for-you', color: '#C8FF37' }, // lime + { label: 'Spend', href: '#difference', color: '#FF2E88' }, // pink + { label: 'Live', href: '#the-vision', color: '#8B3DFF' }, // purple +]; + +const MOBILE_QUERY = '(max-width: 1023px)'; + +export default function Header() { + const [mobileOpen, setMobileOpen] = useState(false); + const [scrolled, setScrolled] = useState(false); + const [hidden, setHidden] = useState(false); + const mobileMenuRef = useRef(null); + const lastScrollYRef = useRef(0); + + // Hide on scroll-down, reveal on scroll-up; deepen the shadow once scrolled. + useEffect(() => { + lastScrollYRef.current = window.scrollY; + const onScroll = () => { + const y = window.scrollY; + setScrolled(y > 40); + const lastY = lastScrollYRef.current; + if (y <= 80) setHidden(false); + else if (y > lastY + 4) setHidden(true); + else if (y < lastY - 4) setHidden(false); + lastScrollYRef.current = y; + }; + window.addEventListener('scroll', onScroll, { passive: true }); + return () => window.removeEventListener('scroll', onScroll); + }, []); + + useEffect(() => { + if (mobileOpen) setHidden(false); + }, [mobileOpen]); + + useEffect(() => { + if (!mobileOpen) return; + const handler = (e: MouseEvent) => { + if (mobileMenuRef.current && !mobileMenuRef.current.contains(e.target as Node)) { + setMobileOpen(false); + } + }; + document.addEventListener('mousedown', handler); + return () => document.removeEventListener('mousedown', handler); + }, [mobileOpen]); + + useEffect(() => { + document.body.style.overflow = mobileOpen ? 'hidden' : ''; + return () => { document.body.style.overflow = ''; }; + }, [mobileOpen]); + + const [isMobileViewport, setIsMobileViewport] = useState(false); + useEffect(() => { + const mql = window.matchMedia(MOBILE_QUERY); + setIsMobileViewport(mql.matches); + const update = () => setIsMobileViewport(mql.matches); + mql.addEventListener('change', update); + return () => mql.removeEventListener('change', update); + }, []); + + return ( +
+ {/* Mobile menu overlay */} + {mobileOpen && ( +
setMobileOpen(false)} + aria-hidden="true" + style={{ position: 'fixed', inset: 0, zIndex: 99999998, background: 'rgba(10,4,20,0.6)' }} + /> + )} + + {/* Mobile slide-in panel — night dot-grid, neobrutalist links */} +
+
+ + LytsUp + + +
+ + + +
+

+ Follow us +

+
+ {SOCIALS.map(({ href, label, icon }) => ( + + {icon} + + ))} +
+
+ + setMobileOpen(false)} + className="font-sora" + style={{ + display: 'block', + marginTop: 'auto', + padding: '16px 24px', + background: '#FFC72C', + color: INK, + border: `3px solid ${INK}`, + borderRadius: 14, + fontWeight: 900, + fontSize: 16, + textTransform: 'uppercase', + textDecoration: 'none', + textAlign: 'center', + boxShadow: `5px 5px 0 0 ${INK}`, + }} + > + Get Started → + +
+ + {/* ——— Funky neobrutalist floating bar ——— */} + + + +
+ ); +} + +/* ——— Inline SVG social icons ——— */ +const SOCIALS = [ + { + label: 'Instagram', href: 'https://instagram.com/lytsup', + icon: , + }, + { + label: 'X / Twitter', href: 'https://twitter.com/lytsup', + icon: , + }, + { + label: 'YouTube', href: 'https://www.youtube.com/@lytsup', + icon: , + }, +]; diff --git a/src/components/layout/ScrollProgress.tsx b/src/components/layout/ScrollProgress.tsx new file mode 100644 index 0000000..644ef5b --- /dev/null +++ b/src/components/layout/ScrollProgress.tsx @@ -0,0 +1,38 @@ +"use client"; + +import { useEffect, useRef } from "react"; + +/* Site-wide reading-progress bar — sits at the very top edge and fills with the + brand gradient as you scroll. Driven by a rAF lerp so it glides smoothly and + works with Lenis smooth-scroll (reads native window.scrollY). */ +export default function ScrollProgress() { + const barRef = useRef(null); + const target = useRef(0); + const current = useRef(0); + + useEffect(() => { + let raf = 0; + const tick = () => { + // Read scroll position every frame so it tracks regardless of how the + // smooth-scroll library emits events. + const max = document.documentElement.scrollHeight - window.innerHeight; + target.current = max > 0 ? Math.min(1, Math.max(0, window.scrollY / max)) : 0; + current.current += (target.current - current.current) * 0.12; + if (barRef.current) { + barRef.current.style.transform = `scaleX(${current.current.toFixed(4)})`; + } + raf = requestAnimationFrame(tick); + }; + raf = requestAnimationFrame(tick); + return () => cancelAnimationFrame(raf); + }, []); + + return ( +
+ ); +} diff --git a/src/components/story/SectionsA.tsx b/src/components/story/SectionsA.tsx index c6dbfde..97fab30 100644 --- a/src/components/story/SectionsA.tsx +++ b/src/components/story/SectionsA.tsx @@ -1,45 +1,109 @@ -import { Section, Pill, Card, Chip, Heading, Logo, Group, Reveal } from "./ui"; +import { Section, Pill, Card, Chip, Heading, Group, Reveal } from "./ui"; import { C } from "./palette"; /* 01 — INTRO / HERO */ export function Intro() { return (
- {/* giant watermark */} - + {/* animated ambient glows — soft washes, heavily blurred so no hard edge shows */} + + + + + {/* giant drifting watermark */} + LYTS -
- - - -
-

we built this for you.

-

- social currency is value earned for participating in life. -

-

a LYTIAN believes every meaningful interaction has value.

-
-

- social -
- currency -
- for real life. -

-
- - "earn. spend. live more." - - a quiet belief that everyday moments matter. + +
+ {/* eyebrow badge */} + + + + + - -
+ social currency, reimagined +
+ + + {/* eyebrow copy */} + +
+

we built this for you.

+

+ social currency is value earned for participating in life. +

+

a LYTIAN believes every meaningful interaction has value.

+
+
+ + {/* headline — line-by-line reveal, shimmering last line */} +

+ social + currency + + + for{" "} + + real life. + + + +

+ + {/* CTA buttons */} + + + + + {/* tagline chip */} + +
+ + "earn. spend. live more." + + a quiet belief that everyday moments matter. + + +
+
-
-
-
psst
- make everyday count. + + {/* floating reward stickers (desktop) */} +
+ + +50 LYTs + + + 🔥 7-day streak + + + 🎁 rewards unlocked + +
+ + {/* rotating "make everyday count" badge — pinned to the right, vertically centered */} +
+
+
+
psst
+ make everyday count. +
@@ -78,7 +142,7 @@ export function TheWhy() {
-
🎬 DISCOVER ON TIKTOK
+
🎬 DISCOVER ON SOCIAL MEDIA
brand relationships start before purchase
diff --git a/src/components/story/SectionsB.tsx b/src/components/story/SectionsB.tsx index 88f467a..22d7e24 100644 --- a/src/components/story/SectionsB.tsx +++ b/src/components/story/SectionsB.tsx @@ -231,22 +231,20 @@ export function OurBelief() { return (
- - 🌱 our belief - + 🌱 our belief
-
-
- 1 -

- traditional loyalty begins when you SPEND. +
+
+ 1 +

+ traditional loyalty begins when you SPEND.

-
- 2 -
-

- LYTs begins when you ENGAGE. +
+ 2 +
+

+ LYTs begins when you ENGAGE.

diff --git a/src/components/story/ui.tsx b/src/components/story/ui.tsx index 0cbd512..0f52a81 100644 --- a/src/components/story/ui.tsx +++ b/src/components/story/ui.tsx @@ -7,20 +7,23 @@ import { C } from "./palette"; export { C }; -const viewport = { once: true, amount: 0.25 } as const; +const viewport = { once: true, amount: 0.2 } as const; + +/* Shared smooth easing — a soft, premium ease-out curve reused across reveals. */ +const smoothEase = [0.22, 1, 0.36, 1] as const; /* stagger container variants */ export const stagger: Variants = { hidden: {}, - show: { transition: { staggerChildren: 0.09, delayChildren: 0.05 } }, + show: { transition: { staggerChildren: 0.11, delayChildren: 0.06 } }, }; export const riseItem: Variants = { - hidden: { opacity: 0, y: 30, scale: 0.96 }, + hidden: { opacity: 0, y: 34, scale: 0.96 }, show: { opacity: 1, y: 0, scale: 1, - transition: { type: "spring", stiffness: 130, damping: 16 }, + transition: { duration: 0.7, ease: smoothEase }, }, }; @@ -44,7 +47,7 @@ export function Section({ id, children, variant = "deep", className = "" }: Sect return (
{children}
@@ -69,7 +72,7 @@ export function Reveal({ initial={{ opacity: 0, y }} whileInView={{ opacity: 1, y: 0 }} viewport={viewport} - transition={{ type: "spring", stiffness: 120, damping: 18, delay }} + transition={{ duration: 0.7, ease: smoothEase, delay }} > {children} @@ -109,12 +112,13 @@ export function Pill({ }) { return ( {children} @@ -146,7 +150,7 @@ export function Card({ initial: { opacity: 0, y: 30, scale: 0.96 }, whileInView: { opacity: 1, y: 0, scale: 1 }, viewport, - transition: { type: "spring" as const, stiffness: 130, damping: 16, delay }, + transition: { duration: 0.7, ease: smoothEase, delay }, }; return ( {children}