UI performance and Mobile responsiveness

This commit is contained in:
root 2026-02-15 12:08:28 +05:00
parent c15b7d8ab7
commit 9d02c62292
13 changed files with 341 additions and 42 deletions

26
package-lock.json generated
View File

@ -24,6 +24,7 @@
"date-fns": "^4.1.0",
"dotenv": "^17.2.4",
"framer-motion": "^12.33.0",
"lenis": "^1.3.17",
"lucide-react": "^0.563.0",
"mammoth": "^1.11.0",
"next": "16.1.6",
@ -7328,6 +7329,31 @@
"node": ">=0.10"
}
},
"node_modules/lenis": {
"version": "1.3.17",
"resolved": "https://registry.npmjs.org/lenis/-/lenis-1.3.17.tgz",
"integrity": "sha512-k9T9rgcxne49ggJOvXCraWn5dt7u2mO+BNkhyu6yxuEnm9c092kAW5Bus5SO211zUvx7aCCEtzy9UWr0RB+oJw==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/darkroomengineering"
},
"peerDependencies": {
"@nuxt/kit": ">=3.0.0",
"react": ">=17.0.0",
"vue": ">=3.0.0"
},
"peerDependenciesMeta": {
"@nuxt/kit": {
"optional": true
},
"react": {
"optional": true
},
"vue": {
"optional": true
}
}
},
"node_modules/levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",

View File

@ -26,6 +26,7 @@
"date-fns": "^4.1.0",
"dotenv": "^17.2.4",
"framer-motion": "^12.33.0",
"lenis": "^1.3.17",
"lucide-react": "^0.563.0",
"mammoth": "^1.11.0",
"next": "16.1.6",

View File

@ -67,7 +67,7 @@ export default function AboutPage() {
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.2 }}
className="max-w-3xl mx-auto text-xl text-slate-400 font-medium leading-relaxed"
className="max-w-3xl mx-auto text-xl text-slate-300 font-medium leading-relaxed"
>
We started JUNOHUB with a simple belief: the global supply chain is broken, and
technology is the only way to fix it. We're building the infrastructure for a
@ -87,7 +87,7 @@ export default function AboutPage() {
transition={{ duration: 0.8 }}
>
<h2 className="text-4xl font-black text-white mb-8 tracking-tighter uppercase">Our Vision</h2>
<div className="space-y-6 text-slate-400 font-medium text-lg leading-relaxed">
<div className="space-y-6 text-slate-300 font-medium text-lg leading-relaxed">
<p>
Every day, millions of dollars are lost due to inefficient communication,
lost inventory data, and slow synchronization.
@ -115,7 +115,7 @@ export default function AboutPage() {
<div key={item.label} className={`p-8 rounded-3xl bg-white/[0.03] border border-white/10 text-center ${i % 2 !== 0 ? 'mt-8' : ''}`}>
<item.icon className="w-8 h-8 text-indigo-400 mx-auto mb-4" />
<div className="text-2xl font-black text-white">{item.value}</div>
<div className="text-[10px] font-bold text-slate-500 uppercase tracking-widest">{item.label}</div>
<div className="text-[10px] font-bold text-slate-400 uppercase tracking-widest">{item.label}</div>
</div>
))}
</motion.div>
@ -141,7 +141,7 @@ export default function AboutPage() {
<v.icon className="w-7 h-7 text-white" />
</div>
<h3 className="text-xl font-black text-white mb-4 uppercase tracking-tight">{v.title}</h3>
<p className="text-slate-400 text-sm font-medium leading-relaxed">{v.description}</p>
<p className="text-slate-300 text-sm font-medium leading-relaxed">{v.description}</p>
</motion.div>
))}
</div>
@ -156,7 +156,7 @@ export default function AboutPage() {
viewport={{ once: true }}
>
<h2 className="text-5xl md:text-7xl font-black text-white mb-8 tracking-tighter uppercase">Join the Movement</h2>
<p className="text-slate-400 font-medium mb-12 max-w-2xl mx-auto text-lg leading-relaxed">
<p className="text-slate-300 font-medium mb-12 max-w-2xl mx-auto text-lg leading-relaxed">
We&apos;re always looking for ambitious vendors and suppliers to join our
expanding ecosystem. Let&apos;s build the future together.
</p>

View File

@ -24,6 +24,7 @@ export const viewport = {
import { Providers } from "@/components/providers";
import { ErrorBoundary } from "@/components/ErrorBoundary";
import { SmoothScroll } from "@/components/SmoothScroll";
export default function RootLayout({
children,
@ -33,10 +34,14 @@ export default function RootLayout({
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
className={`${geistSans.variable} ${geistMono.variable} antialiased selection:bg-indigo-500/30`}
>
<ErrorBoundary>
<Providers>{children}</Providers>
<Providers>
<SmoothScroll>
{children}
</SmoothScroll>
</Providers>
</ErrorBoundary>
</body>
</html>

View File

@ -1,21 +1,34 @@
import { auth } from "@/auth"
"use client"
import { useMediaQuery } from "@/hooks/useMediaQuery"
import { LandingNavbar } from "@/components/landing/LandingNavbar"
import { LandingHero } from "@/components/landing/LandingHero"
import { LandingFeatures } from "@/components/landing/LandingFeatures"
import { LandingSolutions } from "@/components/landing/LandingSolutions"
import { LandingFooter } from "@/components/landing/LandingFooter"
import { MobileHero } from "@/components/landing/mobile/MobileHero"
import { MobileFeatures } from "@/components/landing/mobile/MobileFeatures"
export default async function HomePage() {
const session = await auth()
const isLoggedIn = !!session
export default function HomePage() {
const isMobile = useMediaQuery("(max-width: 768px)")
return (
<div className="min-h-screen bg-[#0a0e1a] selection:bg-indigo-500/30">
<LandingNavbar isLoggedIn={isLoggedIn} />
<LandingNavbar isLoggedIn={false} />
<main>
{isMobile ? (
<>
<MobileHero />
<MobileFeatures />
<LandingSolutions />
</>
) : (
<>
<LandingHero />
<LandingFeatures />
<LandingSolutions />
</>
)}
</main>
<LandingFooter />
</div>

View File

@ -0,0 +1,32 @@
"use client"
import { useEffect } from 'react'
import Lenis from 'lenis'
export function SmoothScroll({ children }: { children: React.ReactNode }) {
useEffect(() => {
const lenis = new Lenis({
duration: 1.2,
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
orientation: 'vertical',
gestureOrientation: 'vertical',
smoothWheel: true,
wheelMultiplier: 1,
touchMultiplier: 2,
infinite: false,
})
function raf(time: number) {
lenis.raf(time)
requestAnimationFrame(raf)
}
requestAnimationFrame(raf)
return () => {
lenis.destroy()
}
}, [])
return <>{children}</>
}

View File

@ -8,7 +8,7 @@ export function LandingFooter() {
return (
<footer className="py-20 border-t border-white/5 relative overflow-hidden">
{/* Background Glow */}
<div className="absolute bottom-0 left-1/2 -translate-x-1/2 w-[800px] h-[300px] bg-indigo-600/10 blur-[120px] rounded-full -z-10" />
<div className="absolute bottom-0 left-1/2 -translate-x-1/2 w-[800px] h-[300px] bg-indigo-600/10 blur-[120px] rounded-full -z-10 will-change-transform" />
<div className="container mx-auto px-6">
<div className="grid grid-cols-1 md:grid-cols-4 gap-12 mb-16">
@ -19,17 +19,17 @@ export function LandingFooter() {
</div>
<span className="text-xl font-black tracking-tighter text-white">JUNOHUB</span>
</Link>
<p className="text-slate-500 font-medium mb-8">
<p className="text-slate-300 font-medium mb-8">
The next generation of supply chain connectivity. Secure, encrypted, and real-time.
</p>
<div className="flex items-center gap-4">
<a href="#" className="p-2 rounded-lg bg-white/5 border border-white/10 text-slate-400 hover:text-white hover:bg-white/10 transition-all">
<a href="#" aria-label="Follow us on Twitter" className="p-2 rounded-lg bg-white/5 border border-white/10 text-slate-300 hover:text-white hover:bg-white/10 transition-all">
<Twitter className="w-5 h-5" />
</a>
<a href="#" className="p-2 rounded-lg bg-white/5 border border-white/10 text-slate-400 hover:text-white hover:bg-white/10 transition-all">
<a href="#" aria-label="Connect on LinkedIn" className="p-2 rounded-lg bg-white/5 border border-white/10 text-slate-300 hover:text-white hover:bg-white/10 transition-all">
<Linkedin className="w-5 h-5" />
</a>
<a href="#" className="p-2 rounded-lg bg-white/5 border border-white/10 text-slate-400 hover:text-white hover:bg-white/10 transition-all">
<a href="#" aria-label="View our GitHub" className="p-2 rounded-lg bg-white/5 border border-white/10 text-slate-300 hover:text-white hover:bg-white/10 transition-all">
<Github className="w-5 h-5" />
</a>
</div>
@ -38,25 +38,25 @@ export function LandingFooter() {
<div>
<h4 className="text-white font-black uppercase tracking-widest text-sm mb-6">Platform</h4>
<ul className="space-y-4">
<li><Link href="/platform" className="text-slate-500 hover:text-indigo-400 transition-colors font-medium">Features</Link></li>
<li><Link href="/platform" className="text-slate-500 hover:text-indigo-400 transition-colors font-medium">Shopify Sync</Link></li>
<li><Link href="/platform" className="text-slate-500 hover:text-indigo-400 transition-colors font-medium">Security</Link></li>
<li><Link href="/platform" className="text-slate-500 hover:text-indigo-400 transition-colors font-medium">API</Link></li>
<li><Link href="/platform" className="text-slate-300 hover:text-indigo-400 transition-colors font-medium">Features</Link></li>
<li><Link href="/platform" className="text-slate-300 hover:text-indigo-400 transition-colors font-medium">Shopify Sync</Link></li>
<li><Link href="/platform" className="text-slate-300 hover:text-indigo-400 transition-colors font-medium">Security</Link></li>
<li><Link href="/platform" className="text-slate-300 hover:text-indigo-400 transition-colors font-medium">API</Link></li>
</ul>
</div>
<div>
<h4 className="text-white font-black uppercase tracking-widest text-sm mb-6">Company</h4>
<ul className="space-y-4">
<li><Link href="/about" className="text-slate-500 hover:text-indigo-400 transition-colors font-medium">About Us</Link></li>
<li><Link href="/privacy-policy" className="text-slate-500 hover:text-indigo-400 transition-colors font-medium">Privacy Policy</Link></li>
<li><Link href="/terms-of-service" className="text-slate-500 hover:text-indigo-400 transition-colors font-medium">Terms of Service</Link></li>
<li><Link href="/about" className="text-slate-300 hover:text-indigo-400 transition-colors font-medium">About Us</Link></li>
<li><Link href="/privacy-policy" className="text-slate-300 hover:text-indigo-400 transition-colors font-medium">Privacy Policy</Link></li>
<li><Link href="/terms-of-service" className="text-slate-300 hover:text-indigo-400 transition-colors font-medium">Terms of Service</Link></li>
</ul>
</div>
<div>
<h4 className="text-white font-black uppercase tracking-widest text-sm mb-6">Newsletter</h4>
<p className="text-slate-500 font-medium mb-6">Stay updated with the latest in supply chain tech.</p>
<p className="text-slate-300 font-medium mb-6">Stay updated with the latest in supply chain tech.</p>
{/* Status Message */}
<form
@ -113,6 +113,7 @@ export function LandingFooter() {
/>
<button
type="submit"
aria-label="Subscribe to newsletter"
className="bg-indigo-600 hover:bg-indigo-500 disabled:opacity-50 text-white p-3 rounded-xl transition-all active:scale-95 shadow-lg shadow-indigo-600/20"
>
<Mail className="w-5 h-5" />
@ -124,13 +125,13 @@ export function LandingFooter() {
</div>
<div className="pt-8 border-t border-white/5 flex flex-col md:flex-row items-center justify-between gap-4">
<p className="text-sm text-slate-600 font-medium">
<p className="text-sm text-slate-400 font-medium">
© 2026 JUNOHUB. All rights reserved.
</p>
<div className="flex items-center gap-6">
<Link href="/terms-of-service" className="text-xs text-slate-600 hover:text-slate-400 font-bold uppercase tracking-widest">Terms</Link>
<Link href="/privacy-policy" className="text-xs text-slate-600 hover:text-slate-400 font-bold uppercase tracking-widest">Privacy</Link>
<Link href="/cookies" className="text-xs text-slate-600 hover:text-slate-400 font-bold uppercase tracking-widest">Cookies</Link>
<Link href="/terms-of-service" className="text-xs text-slate-400 hover:text-indigo-300 font-bold uppercase tracking-widest">Terms</Link>
<Link href="/privacy-policy" className="text-xs text-slate-400 hover:text-indigo-300 font-bold uppercase tracking-widest">Privacy</Link>
<Link href="/cookies" className="text-xs text-slate-400 hover:text-indigo-300 font-bold uppercase tracking-widest">Cookies</Link>
</div>
</div>
</div>

View File

@ -22,9 +22,9 @@ export function LandingHero() {
className="relative min-h-screen flex items-center justify-center pt-20 overflow-hidden"
>
{/* Background Decorative Elements */}
<div className="absolute inset-0 z-0">
<div className="absolute top-1/4 left-1/4 w-96 h-96 bg-indigo-600/20 blur-[120px] rounded-full animate-pulse" />
<div className="absolute bottom-1/4 right-1/4 w-96 h-96 bg-purple-600/20 blur-[120px] rounded-full animate-pulse delay-700" />
<div className="absolute inset-0 z-0 pointer-events-none">
<div className="absolute top-1/4 left-1/4 w-96 h-96 bg-indigo-600/20 blur-[120px] rounded-full animate-pulse will-change-[transform,opacity]" />
<div className="absolute bottom-1/4 right-1/4 w-96 h-96 bg-purple-600/20 blur-[120px] rounded-full animate-pulse delay-700 will-change-[transform,opacity]" />
</div>
{/* Grid Pattern */}
@ -54,9 +54,9 @@ export function LandingHero() {
{/* Main Heading */}
<motion.h1
initial={{ opacity: 0, y: 30 }}
initial={{ opacity: 1, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.2 }}
transition={{ duration: 0.8 }}
className="text-5xl md:text-8xl font-black tracking-tighter text-white mb-8 leading-[0.9]"
>
THE PULSE OF <br />
@ -67,8 +67,8 @@ export function LandingHero() {
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1, delay: 0.4 }}
className="max-w-2xl mx-auto text-lg md:text-xl text-slate-400 font-medium mb-12 leading-relaxed"
transition={{ duration: 1, delay: 0.2 }}
className="max-w-2xl mx-auto text-lg md:text-xl text-slate-300 font-medium mb-12 leading-relaxed"
>
Bridge the gap between vendors and suppliers with the world&apos;s most advanced
encrypted ecosystem. Sync Shopify, chat securely, and scale effortlessly.
@ -135,7 +135,7 @@ export function LandingHero() {
className="absolute top-0 left-0 w-full h-1/3 bg-indigo-500"
/>
</div>
<span className="text-[10px] font-black uppercase tracking-[0.3em] text-slate-500">Scroll</span>
<span className="text-[10px] font-black uppercase tracking-[0.3em] text-slate-400">Scroll</span>
</motion.div>
</section>
)

View File

@ -57,7 +57,7 @@ export function LandingNavbar({ isLoggedIn }: LandingNavbarProps) {
<Link
key={link.name}
href={link.href}
className="text-sm font-bold text-slate-400 hover:text-white transition-colors"
className="text-sm font-bold text-slate-300 hover:text-indigo-400 transition-colors"
>
{link.name}
</Link>
@ -84,6 +84,8 @@ export function LandingNavbar({ isLoggedIn }: LandingNavbarProps) {
<button
className="md:hidden text-white p-1"
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
aria-label={mobileMenuOpen ? "Close menu" : "Open menu"}
aria-expanded={mobileMenuOpen}
>
{mobileMenuOpen ? <X /> : <Menu />}
</button>

View File

@ -57,7 +57,7 @@ export function LandingSolutions() {
{sol.title}
</h3>
<p className="text-slate-400 font-medium mb-8 text-lg">
<p className="text-slate-300 font-medium mb-8 text-lg">
{sol.description}
</p>

View File

@ -0,0 +1,109 @@
"use client"
import { motion } from "framer-motion"
import {
Zap,
MessageSquare,
BarChart3,
ShieldCheck,
LayoutDashboard,
RefreshCcw,
ArrowRight
} from "lucide-react"
import Link from "next/link"
const features = [
{
title: "Shopify Sync",
icon: RefreshCcw,
color: "text-emerald-400",
bg: "bg-emerald-500/10",
size: "col-span-1"
},
{
title: "E2E Chat",
icon: MessageSquare,
color: "text-blue-400",
bg: "bg-blue-500/10",
size: "col-span-1"
},
{
title: "High Octane Analytics",
icon: BarChart3,
color: "text-purple-400",
bg: "bg-purple-500/10",
size: "col-span-2"
},
{
title: "Dashboard",
icon: LayoutDashboard,
color: "text-orange-400",
bg: "bg-orange-500/10",
size: "col-span-1"
},
{
title: "Security",
icon: ShieldCheck,
color: "text-red-400",
bg: "bg-red-500/10",
size: "col-span-1"
},
{
title: "Global Scalability",
icon: Zap,
color: "text-yellow-400",
bg: "bg-yellow-500/10",
size: "col-span-2"
}
]
export function MobileFeatures() {
return (
<section className="py-24 px-6 bg-[#0a0e1a] relative overflow-hidden">
{/* Decorative lines */}
<div className="absolute top-0 left-0 w-full h-[1px] bg-gradient-to-r from-transparent via-white/10 to-transparent" />
<div className="mb-16 text-center">
<motion.div
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
className="text-[10px] font-black text-indigo-400 uppercase tracking-[0.4em] mb-4"
>
Infrastructure
</motion.div>
<h2 className="text-4xl font-black text-white mb-6 tracking-tighter uppercase italic">Dominance.</h2>
<div className="w-12 h-1 bg-indigo-600 mx-auto rounded-full" />
</div>
<div className="grid grid-cols-2 gap-4">
{features.map((f, i) => (
<motion.div
key={f.title}
initial={{ opacity: 1, scale: 0.95 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
transition={{ delay: i * 0.05 }}
className={`${f.size} p-8 rounded-[2rem] border border-white/5 bg-white/[0.02] flex flex-col items-center justify-center text-center relative overflow-hidden group active:bg-white/5 transition-colors`}
>
{/* Glow */}
<div className={`absolute -top-4 -right-4 w-16 h-16 ${f.bg} blur-[30px] opacity-20`} />
<div className={`w-14 h-14 rounded-[1.25rem] ${f.bg} flex items-center justify-center mb-4 border border-white/5 shadow-inner`}>
<f.icon className={`w-7 h-7 ${f.color}`} />
</div>
<h3 className="text-xs font-black text-white uppercase tracking-widest">{f.title}</h3>
</motion.div>
))}
</div>
<div className="mt-20 p-8 rounded-[2.5rem] bg-gradient-to-br from-indigo-600 to-purple-600 text-center relative overflow-hidden">
<div className="absolute top-0 right-0 w-32 h-32 bg-white/10 blur-[40px] rounded-full" />
<h3 className="text-2xl font-black text-white mb-4 uppercase italic">Ready to sync?</h3>
<p className="text-indigo-100 text-sm font-medium mb-8 opacity-90">Join the thousands of vendors scaling with JUNOHUB.</p>
<Link href="/signup" className="inline-flex items-center gap-2 px-8 py-4 bg-white text-indigo-600 rounded-2xl font-black text-sm uppercase tracking-wider shadow-lg">
Get Started <ArrowRight className="w-4 h-4" />
</Link>
</div>
</section>
)
}

View File

@ -0,0 +1,91 @@
"use client"
import { motion } from "framer-motion"
import { ArrowRight, Play, ShieldCheck, Zap, Globe, MessageSquare } from "lucide-react"
import Link from "next/link"
const stats = [
{ label: "AES-250 READY", icon: ShieldCheck },
{ label: "SHOPIFY SYNC", icon: Zap },
{ label: "120+ COUNTRIES", icon: Globe },
{ label: "E2E CHAT", icon: MessageSquare },
]
export function MobileHero() {
return (
<section className="relative min-h-[95vh] flex flex-col items-center justify-between px-6 pb-12 pt-16 overflow-hidden bg-[#0a0e1a]">
{/* The Pulse Core - Central Visual */}
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full h-full z-0 pointer-events-none flex items-center justify-center">
<div className="w-64 h-64 bg-indigo-600/30 blur-[80px] rounded-full animate-pulse will-change-[transform,opacity]" />
<div className="absolute w-40 h-40 bg-purple-600/20 blur-[60px] rounded-full animate-pulse delay-700" />
<div className="absolute w-[300px] h-[1px] bg-gradient-to-r from-transparent via-indigo-500/20 to-transparent rotate-45" />
<div className="absolute w-[300px] h-[1px] bg-gradient-to-r from-transparent via-purple-500/20 to-transparent -rotate-45" />
</div>
<div className="relative z-10 text-center flex flex-col items-center w-full mt-12">
{/* Mobile-First Badge */}
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-white/5 border border-white/10 backdrop-blur-md mb-8"
>
<span className="flex h-1.5 w-1.5 rounded-full bg-indigo-500 animate-ping" />
<span className="text-[10px] font-black uppercase tracking-[0.2em] text-indigo-400">Next-Gen Engine</span>
</motion.div>
{/* Big, Punchy Mobile Header */}
<motion.h1
initial={{ opacity: 1, y: 0 }}
className="text-6xl font-black tracking-tighter text-white mb-6 leading-[0.85] uppercase italic"
>
THE <br />
<span className="text-transparent bg-clip-text bg-gradient-to-br from-indigo-400 via-white to-purple-400">PULSE.</span>
</motion.h1>
<motion.p
initial={{ opacity: 1 }}
className="text-slate-300 text-sm font-medium mb-12 max-w-[260px] leading-relaxed mx-auto"
>
Bridge the gap between vendors and suppliers with the world&apos;s most advanced
encrypted ecosystem.
</motion.p>
{/* Thumb-Zone CTAs */}
<div className="flex flex-col w-full gap-4 max-w-[300px]">
<Link
href="/signup"
className="w-full py-5 bg-indigo-600 rounded-2xl font-black text-white shadow-xl shadow-indigo-600/20 flex items-center justify-center gap-2 active:scale-95 transition-all text-sm uppercase tracking-wider"
>
GET STARTED NOW <ArrowRight className="w-4 h-4" />
</Link>
<button className="w-full py-5 rounded-2xl font-black text-white bg-white/5 border border-white/10 flex items-center justify-center gap-3 active:scale-95 transition-all text-sm uppercase tracking-wider backdrop-blur-sm">
<div className="w-8 h-8 rounded-full bg-white/10 flex items-center justify-center">
<Play className="w-3 h-3 fill-white ml-0.5" />
</div>
WATCH DEMO
</button>
</div>
</div>
{/* Infinite Marquee for Stats - Prevents Overlap */}
<div className="relative w-full z-10 mt-auto pt-12 overflow-hidden mb-4">
<div className="absolute inset-y-0 left-0 w-12 bg-gradient-to-r from-[#0a0e1a] to-transparent z-20" />
<div className="absolute inset-y-0 right-0 w-12 bg-gradient-to-l from-[#0a0e1a] to-transparent z-20" />
<motion.div
animate={{ x: [0, -1000] }}
transition={{ duration: 30, repeat: Infinity, ease: "linear" }}
className="flex whitespace-nowrap gap-8"
>
{[...stats, ...stats, ...stats, ...stats].map((s, i) => (
<div key={i} className="flex items-center gap-2 px-4 py-2 bg-white/5 rounded-full border border-white/10">
<s.icon className="w-3 h-3 text-indigo-400" />
<span className="text-[10px] font-black tracking-[0.1em] text-white uppercase whitespace-nowrap">{s.label}</span>
</div>
))}
</motion.div>
</div>
</section>
)
}

View File

@ -0,0 +1,19 @@
"use client"
import { useState, useEffect } from "react"
export function useMediaQuery(query: string) {
const [matches, setMatches] = useState(false)
useEffect(() => {
const media = window.matchMedia(query)
if (media.matches !== matches) {
setMatches(media.matches)
}
const listener = () => setMatches(media.matches)
window.addEventListener("resize", listener)
return () => window.removeEventListener("resize", listener)
}, [matches, query])
return matches
}