feat: better design

This commit is contained in:
Nik L
2026-04-07 15:54:20 -04:00
parent f4c794790c
commit 4964162e5d

View File

@@ -3,18 +3,12 @@
import { useState, useEffect, useRef, Suspense, useCallback } from 'react'
import dynamic from 'next/dynamic'
import {
Shield,
ChevronDown,
ArrowRight,
Users,
Trophy,
Star,
Terminal,
Clock,
MapPin,
Cpu,
Sparkles,
Eye,
Target,
AlertTriangle,
@@ -46,23 +40,6 @@ function useInView(threshold = 0.15) {
return { ref, visible }
}
function Counter({ target, suffix = '' }: { target: number; suffix?: string }) {
const [count, setCount] = useState(0)
const { ref, visible } = useInView(0.3)
useEffect(() => {
if (!visible) return
let start = 0
const step = (ts: number) => {
if (!start) start = ts
const p = Math.min((ts - start) / 1200, 1)
const eased = 1 - Math.pow(1 - p, 3) // ease-out cubic
setCount(Math.floor(eased * target))
if (p < 1) requestAnimationFrame(step)
}
requestAnimationFrame(step)
}, [visible, target])
return <span ref={ref as React.RefObject<HTMLSpanElement>}>{count}{suffix}</span>
}
function useMouseSpotlight() {
const ref = useRef<HTMLDivElement>(null)
@@ -89,24 +66,6 @@ function NoiseOverlay() {
)
}
/* ─── Dot grid (Linear-inspired) ─── */
function DotGrid({ cols = 16, rows = 5 }: { cols?: number; rows?: number }) {
return (
<div className="grid gap-6" style={{ gridTemplateColumns: `repeat(${cols}, 1fr)` }}>
{Array.from({ length: cols * rows }, (_, i) => (
<div
key={i}
className="w-1 h-1 rounded-full bg-primary"
style={{
animation: 'dot-pulse 3.5s ease-in-out infinite',
animationDelay: `${(i % cols) * 80 + Math.floor(i / cols) * 120}ms`,
}}
/>
))}
</div>
)
}
/* ─── Nav ─── */
@@ -174,33 +133,6 @@ function Hero() {
)
}
/* ─── Stats ─── */
function Stats() {
return (
<section className="py-14 px-4 sm:px-6 border-t border-b border-border/30 relative overflow-hidden">
{/* Dot grid background */}
<div className="absolute inset-0 flex items-center justify-center opacity-50 pointer-events-none">
<DotGrid cols={20} rows={3} />
</div>
<div className="relative mx-auto max-w-3xl grid grid-cols-3 gap-8 text-center">
{[
{ value: 48, suffix: 'h', label: 'of hacking' },
{ value: 5, suffix: '', label: 'tracks' },
{ value: 100, suffix: '%', label: 'open source' },
].map((stat) => (
<div key={stat.label}>
<div className="font-serif text-4xl sm:text-5xl font-semibold text-primary mb-1">
<Counter target={stat.value} suffix={stat.suffix} />
</div>
<div className="text-sm text-muted-foreground font-sans">{stat.label}</div>
</div>
))}
</div>
</section>
)
}
/* ─── Notify Button ─── */
@@ -637,17 +569,6 @@ function TrackCard({ track, index }: { track: typeof tracks[0]; index: number })
<div className="flex">
{/* Content */}
<div className="flex-1 p-8 sm:p-10 relative z-10">
<div className="flex items-center gap-3 mb-6">
<div className="flex items-center justify-center w-12 h-12 rounded-2xl bg-card/60 border border-border/30 backdrop-blur-sm group-hover:border-primary/20 transition-colors">
<track.icon className="h-6 w-6 text-primary" />
</div>
<div className="flex items-center gap-2 text-xs text-muted-foreground font-sans">
<Clock className="h-3 w-3" />48h
<Users className="h-3 w-3 ml-1" />Teams of 23
<MapPin className="h-3 w-3 ml-1" />Montreal
</div>
</div>
<h3 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-3">
{track.title}
</h3>
@@ -708,95 +629,6 @@ function Tracks() {
)
}
/* ─── How It Works ─── */
const steps = [
{ icon: Sparkles, title: 'Form a team', sub: 'Groups of 23' },
{ icon: Terminal, title: 'Get set up', sub: 'Docs & infra provided' },
{ icon: Cpu, title: 'Hack for 48h', sub: 'Collect achievements' },
{ icon: Trophy, title: 'Present & win', sub: 'Top teams demo' },
]
function HowItWorks() {
const { ref, visible } = useInView()
return (
<section id="how-it-works" ref={ref} className="py-14 sm:py-20 px-4 sm:px-6 border-t border-border/30">
<div className="mx-auto max-w-3xl">
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight text-center mb-12">
How it works.
</h2>
<div className="relative grid grid-cols-4 gap-6">
{/* Animated connector line */}
<div className="hidden sm:block absolute top-7 left-[12.5%] right-[12.5%] h-px overflow-hidden">
<div className={`h-full bg-gradient-to-r from-primary/40 via-primary/20 to-primary/40 transition-all duration-1000 ${visible ? 'w-full' : 'w-0'}`} style={{ transitionDelay: '300ms' }} />
</div>
{steps.map((step, i) => (
<div
key={step.title}
className={`relative text-center transition-all duration-700 ${visible ? 'opacity-100 translate-y-0 scale-100' : 'opacity-0 translate-y-8 scale-95'}`}
style={{ transitionDelay: `${i * 150}ms` }}
>
<div className="inline-flex items-center justify-center w-14 h-14 rounded-2xl bg-gradient-to-br from-primary/15 to-primary/5 border border-primary/20 mb-4 relative z-10 hover:border-primary/40 transition-all group">
<step.icon className="h-6 w-6 text-primary group-hover:scale-110 transition-transform" />
</div>
<h3 className="font-serif text-base font-semibold mb-1">{step.title}</h3>
<p className="text-xs text-muted-foreground font-sans">{step.sub}</p>
</div>
))}
</div>
</div>
</section>
)
}
/* ─── Prizes ─── */
function Prizes() {
const { ref, visible } = useInView()
const items = [
{ icon: Crown, title: 'Hall of Fame', sub: 'Featured on greywall.io permanently' },
{ icon: Star, title: 'Contributor credit', sub: 'Added as a contributor in the GitHub README' },
]
return (
<section ref={ref} className="py-14 sm:py-20 px-4 sm:px-6 border-t border-border/30 relative overflow-hidden">
{/* Background dot grid */}
<div
className="absolute inset-0 pointer-events-none opacity-40"
style={{
backgroundImage: 'radial-gradient(circle, rgba(217,94,42,0.5) 1px, transparent 1px)',
backgroundSize: '32px 32px',
}}
/>
<div className="relative mx-auto max-w-3xl">
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight text-center mb-10">
What you win.
</h2>
<div className="flex justify-center gap-6">
{items.map((item, i) => (
<div
key={item.title}
className={`text-center p-6 rounded-2xl border border-border/30 bg-card/20 backdrop-blur-sm hover:bg-card/50 hover:border-primary/20 hover:-translate-y-1 hover:shadow-[0_8px_30px_rgba(217,94,42,0.08)] transition-all duration-500 ${visible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}
style={{ transitionDelay: `${i * 100}ms` }}
>
<div className="inline-flex items-center justify-center w-12 h-12 rounded-xl bg-primary/10 border border-primary/15 mb-4">
<item.icon className="h-5 w-5 text-primary" />
</div>
<h3 className="font-serif text-base font-semibold mb-1">{item.title}</h3>
<p className="text-xs text-muted-foreground font-sans">{item.sub}</p>
</div>
))}
</div>
</div>
</section>
)
}
/* ─── Location ─── */