better designs

This commit is contained in:
Nik L
2026-03-31 09:31:52 -04:00
parent a4a6dd97c9
commit 1d814a74e3
2 changed files with 204 additions and 49 deletions

View File

@@ -196,3 +196,79 @@ section {
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
/* ─── Aurora gradient background (Stripe-inspired) ─── */
@keyframes aurora {
0%, 100% { background-position: 0% 50%, 100% 50%, 50% 100%; }
33% { background-position: 100% 0%, 0% 100%, 50% 50%; }
66% { background-position: 50% 100%, 50% 0%, 0% 50%; }
}
.aurora-bg {
background:
radial-gradient(ellipse at 20% 50%, rgba(217,94,42,0.12) 0%, transparent 50%),
radial-gradient(ellipse at 80% 20%, rgba(217,94,42,0.06) 0%, transparent 50%),
radial-gradient(ellipse at 50% 80%, rgba(249,249,247,0.03) 0%, transparent 50%);
background-size: 200% 200%, 200% 200%, 200% 200%;
animation: aurora 20s ease-in-out infinite;
}
/* ─── Gradient text shimmer ─── */
@keyframes gradient-shift {
0%, 100% { background-position: 0% center; }
50% { background-position: 200% center; }
}
.text-shimmer {
background: linear-gradient(90deg, rgb(var(--foreground)) 0%, rgb(var(--primary)) 40%, rgb(var(--foreground)) 80%);
background-size: 200% auto;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
animation: gradient-shift 6s ease infinite;
}
/* ─── Dot grid wave (Linear-inspired) ─── */
@keyframes dot-pulse {
0%, 100% { opacity: 0.08; transform: scale(1); }
50% { opacity: 0.5; transform: scale(1.3); }
}
/* ─── Floating animation ─── */
@keyframes float {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-8px); }
}
.animate-float {
animation: float 4s ease-in-out infinite;
}
/* ─── Card spotlight hover (mouse-tracking glow) ─── */
.card-spotlight {
position: relative;
overflow: hidden;
}
.card-spotlight::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(600px circle at var(--mouse-x, 50%) var(--mouse-y, 50%), rgba(217,94,42,0.06), transparent 40%);
opacity: 0;
transition: opacity 0.3s;
pointer-events: none;
z-index: 1;
}
.card-spotlight:hover::before {
opacity: 1;
}
/* ─── Marquee scroll ─── */
@keyframes marquee {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
.animate-marquee {
animation: marquee 30s linear infinite;
}

View File

@@ -1,6 +1,6 @@
'use client'
import { useState, useEffect, useRef, Suspense } from 'react'
import { useState, useEffect, useRef, Suspense, useCallback } from 'react'
import dynamic from 'next/dynamic'
import {
Activity,
@@ -36,7 +36,7 @@ function useInView(threshold = 0.15) {
useEffect(() => {
const el = ref.current
if (!el) return
const obs = new IntersectionObserver(([e]) => { if (e.isIntersecting) { setVisible(true); obs.unobserve(el) } }, { threshold })
const obs = new IntersectionObserver(([e]) => { if (e.isIntersecting) { setVisible(true); obs.unobserve(el) } }, { threshold, rootMargin: '0px 0px -40px 0px' })
obs.observe(el)
return () => obs.disconnect()
}, [threshold])
@@ -52,7 +52,8 @@ function Counter({ target, suffix = '' }: { target: number; suffix?: string }) {
const step = (ts: number) => {
if (!start) start = ts
const p = Math.min((ts - start) / 1200, 1)
setCount(Math.floor(p * target))
const eased = 1 - Math.pow(1 - p, 3) // ease-out cubic
setCount(Math.floor(eased * target))
if (p < 1) requestAnimationFrame(step)
}
requestAnimationFrame(step)
@@ -60,6 +61,50 @@ function Counter({ target, suffix = '' }: { target: number; suffix?: string }) {
return <span ref={ref as React.RefObject<HTMLSpanElement>}>{count}{suffix}</span>
}
function useMouseSpotlight() {
const ref = useRef<HTMLDivElement>(null)
const onMove = useCallback((e: React.MouseEvent) => {
const el = ref.current
if (!el) return
const rect = el.getBoundingClientRect()
el.style.setProperty('--mouse-x', `${e.clientX - rect.left}px`)
el.style.setProperty('--mouse-y', `${e.clientY - rect.top}px`)
}, [])
return { ref, onMove }
}
/* ─── Noise overlay ─── */
function NoiseOverlay() {
return (
<svg className="fixed inset-0 w-full h-full pointer-events-none z-[100] opacity-[0.025]" aria-hidden>
<filter id="grain">
<feTurbulence type="fractalNoise" baseFrequency="0.8" numOctaves="4" stitchTiles="stitch" />
</filter>
<rect width="100%" height="100%" filter="url(#grain)" />
</svg>
)
}
/* ─── 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 ─── */
function Nav() {
@@ -88,35 +133,38 @@ function Nav() {
)
}
/* ─── Hero with 3D Shield ─── */
/* ─── Hero ─── */
function Hero() {
return (
<section className="relative pt-28 sm:pt-40 pb-28 sm:pb-40 px-4 sm:px-6 overflow-hidden">
{/* 3D Shield as background */}
{/* 3D Shield background */}
<div className="absolute inset-0 z-0 opacity-60">
<Suspense fallback={null}>
<ShieldScene />
</Suspense>
</div>
{/* Gradient overlays to keep text readable */}
<div className="absolute inset-0 z-[1] bg-[radial-gradient(ellipse_at_center,rgba(22,22,20,0.4)_0%,rgba(22,22,20,0.8)_70%)]" />
<div className="absolute inset-0 z-[1] bg-gradient-to-b from-background/60 via-transparent to-background" />
{/* Aurora gradient */}
<div className="absolute inset-0 z-[1] aurora-bg" />
{/* Dark overlay for readability */}
<div className="absolute inset-0 z-[1] bg-[radial-gradient(ellipse_at_center,rgba(22,22,20,0.3)_0%,rgba(22,22,20,0.75)_70%)]" />
<div className="absolute inset-0 z-[1] bg-gradient-to-b from-background/50 via-transparent to-background" />
<div className="relative z-[2] mx-auto max-w-4xl text-center">
<h1 className="font-serif text-6xl sm:text-7xl md:text-8xl font-semibold tracking-tight leading-[1] mb-6">
Hack the <em className="italic text-primary">Wall.</em>
<h1 className="font-serif text-6xl sm:text-7xl md:text-8xl font-semibold tracking-tight leading-[1] mb-6 text-shimmer">
Hack the Wall.
</h1>
<p className="text-xl sm:text-2xl text-muted-foreground font-serif mb-8 max-w-xl mx-auto">
<p className="text-xl sm:text-2xl text-muted-foreground font-serif mb-10 max-w-xl mx-auto">
Build on the AI agent security stack. Your best hacks get merged into Greywall.
</p>
<a
href="#tracks"
className="inline-flex items-center gap-2 px-8 py-4 bg-primary text-primary-foreground font-sans font-medium rounded-lg hover:bg-primary/90 transition-all glow-orange text-base"
className="group inline-flex items-center gap-2 px-8 py-4 bg-primary text-primary-foreground font-sans font-medium rounded-lg hover:bg-primary/90 transition-all glow-orange text-base hover:shadow-[0_0_30px_rgba(217,94,42,0.3)]"
>
Explore tracks
<ArrowRight className="h-4 w-4" />
<ArrowRight className="h-4 w-4 group-hover:translate-x-0.5 transition-transform" />
</a>
</div>
</section>
@@ -127,8 +175,13 @@ function Hero() {
function Stats() {
return (
<section className="py-12 px-4 sm:px-6 border-t border-b border-border/30">
<div className="mx-auto max-w-3xl grid grid-cols-3 gap-8 text-center">
<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: 24, suffix: 'h', label: 'of hacking' },
{ value: 3, suffix: '', label: 'open-ended tracks' },
@@ -146,13 +199,16 @@ function Stats() {
)
}
/* ─── Live Terminal Showcase ─── */
/* ─── Terminal Showcase ─── */
function TerminalShowcase() {
const { ref, visible } = useInView(0.1)
return (
<section ref={ref} className="py-20 sm:py-28 px-4 sm:px-6">
<section ref={ref} className="py-20 sm:py-28 px-4 sm:px-6 relative overflow-hidden">
{/* Background glow behind terminal */}
<div className="absolute right-0 top-1/2 -translate-y-1/2 w-[500px] h-[400px] bg-[radial-gradient(ellipse,rgba(217,94,42,0.06),transparent_70%)] blur-2xl pointer-events-none" />
<div className={`mx-auto max-w-5xl grid md:grid-cols-2 gap-10 items-center transition-all duration-700 ${visible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-12'}`}>
<div>
<p className="text-xs font-sans uppercase tracking-wider text-primary font-medium mb-4">The data stream</p>
@@ -163,13 +219,15 @@ function TerminalShowcase() {
Greywall&apos;s proxy captures every request, file access, and command your AI agent executes. This is what you&apos;ll be building on.
</p>
</div>
<LiveTerminal />
<div className="animate-float [animation-duration:6s]">
<LiveTerminal />
</div>
</div>
</section>
)
}
/* ─── Tracks with visuals ─── */
/* ─── Tracks ─── */
const tracks = [
{
@@ -206,6 +264,7 @@ const tracks = [
function TrackCard({ track, index }: { track: typeof tracks[0]; index: number }) {
const { ref, visible } = useInView(0.1)
const { ref: spotlightRef, onMove } = useMouseSpotlight()
return (
<div
@@ -213,14 +272,18 @@ function TrackCard({ track, index }: { track: typeof tracks[0]; index: number })
className={`transition-all duration-700 ${visible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-12'}`}
style={{ transitionDelay: `${index * 120}ms` }}
>
<div className={`group relative rounded-2xl border border-border/40 ${track.borderColor} bg-gradient-to-br ${track.color} transition-all duration-300 overflow-hidden`}>
<div
ref={spotlightRef}
onMouseMove={onMove}
className={`card-spotlight group relative rounded-2xl border border-border/40 ${track.borderColor} bg-gradient-to-br ${track.color} transition-all duration-300 overflow-hidden hover:-translate-y-0.5 hover:shadow-[0_8px_40px_rgba(0,0,0,0.2)]`}
>
<div className="absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-primary/30 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
<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">
<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">
@@ -239,7 +302,7 @@ function TrackCard({ track, index }: { track: typeof tracks[0]; index: number })
<div className="flex flex-wrap gap-2">
{track.examples.map((ex) => (
<span key={ex} className="px-3 py-1.5 text-xs font-sans font-medium rounded-full bg-card/60 border border-border/30 text-muted-foreground backdrop-blur-sm">
<span key={ex} className="px-3 py-1.5 text-xs font-sans font-medium rounded-full bg-card/60 border border-border/30 text-muted-foreground backdrop-blur-sm hover:border-primary/20 hover:text-foreground transition-colors">
{ex}
</span>
))}
@@ -258,8 +321,11 @@ function TrackCard({ track, index }: { track: typeof tracks[0]; index: number })
function Tracks() {
return (
<section id="tracks" className="py-24 sm:py-32 px-4 sm:px-6">
<div className="mx-auto max-w-5xl">
<section id="tracks" className="py-24 sm:py-32 px-4 sm:px-6 relative">
{/* Subtle background glow */}
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[800px] h-[600px] bg-[radial-gradient(ellipse,rgba(217,94,42,0.04),transparent_70%)] pointer-events-none" />
<div className="relative mx-auto max-w-5xl">
<div className="text-center mb-16">
<h2 className="font-serif text-4xl sm:text-5xl font-semibold tracking-tight mb-4">
Pick your arena.
@@ -291,22 +357,26 @@ function HowItWorks() {
const { ref, visible } = useInView()
return (
<section id="how-it-works" ref={ref} className="py-24 sm:py-32 px-4 sm:px-6 border-t border-border/30">
<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-5xl">
<h2 className="font-serif text-4xl sm:text-5xl font-semibold tracking-tight text-center mb-16">
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight text-center mb-10">
How it works.
</h2>
<div className="relative grid grid-cols-2 sm:grid-cols-4 gap-8">
<div className="hidden sm:block absolute top-7 left-[12.5%] right-[12.5%] h-px border-t border-dashed border-border/50" />
{/* 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 border-t border-dashed border-primary/30 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' : 'opacity-0 translate-y-8'}`}
style={{ transitionDelay: `${i * 100}ms` }}
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-card/60 border border-border/40 mb-4 relative z-10 backdrop-blur-sm">
<step.icon className="h-6 w-6 text-primary" />
<div className="inline-flex items-center justify-center w-14 h-14 rounded-2xl bg-card/60 border border-border/40 mb-4 relative z-10 backdrop-blur-sm hover:border-primary/30 hover:bg-card/80 transition-all group">
<step.icon className="h-6 w-6 text-primary group-hover:scale-110 transition-transform" />
</div>
<h3 className="font-serif text-lg font-semibold mb-1">{step.title}</h3>
<p className="text-sm text-muted-foreground font-sans">{step.sub}</p>
@@ -331,20 +401,25 @@ function Prizes() {
]
return (
<section ref={ref} className="py-24 sm:py-32 px-4 sm:px-6 border-t border-border/30">
<div className="mx-auto max-w-5xl">
<h2 className="font-serif text-4xl sm:text-5xl font-semibold tracking-tight text-center mb-4">
<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 flex items-center justify-center opacity-30 pointer-events-none">
<DotGrid cols={24} rows={4} />
</div>
<div className="relative mx-auto max-w-5xl">
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight text-center mb-3">
More than a trophy.
</h2>
<p className="font-serif text-lg text-muted-foreground text-center mb-16">
<p className="font-serif text-lg text-muted-foreground text-center mb-10">
The best hacks ship.
</p>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-6">
{items.map((item, i) => (
<div
key={item.title}
className={`text-center p-6 rounded-2xl border border-border/30 bg-card/20 hover:bg-card/40 hover:border-primary/20 transition-all duration-500 ${visible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}
style={{ transitionDelay: `${i * 80}ms` }}
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" />
@@ -365,13 +440,13 @@ function Location() {
const { ref, visible } = useInView()
return (
<section ref={ref} className="py-24 sm:py-32 px-4 sm:px-6 border-t border-border/30">
<section ref={ref} className="py-14 sm:py-20 px-4 sm:px-6 border-t border-border/30">
<div className={`mx-auto max-w-5xl text-center transition-all duration-700 ${visible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
<div className="inline-flex items-center gap-2 mb-4">
<div className="inline-flex items-center gap-2 mb-3">
<MapPin className="h-4 w-4 text-primary" />
<span className="text-xs font-sans uppercase tracking-wider text-primary font-medium">Location</span>
</div>
<h2 className="font-serif text-4xl sm:text-5xl font-semibold tracking-tight mb-4">
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-2">
Montreal.
</h2>
<p className="font-serif text-lg text-muted-foreground">
@@ -409,8 +484,8 @@ function FAQItem({ question, answer }: { question: string; answer: string }) {
const [open, setOpen] = useState(false)
return (
<div className="border-b border-border/30">
<button onClick={() => setOpen(!open)} className="w-full flex items-center justify-between gap-4 py-5 text-left cursor-pointer">
<h3 className="font-serif text-base sm:text-lg font-semibold">{question}</h3>
<button onClick={() => setOpen(!open)} className="w-full flex items-center justify-between gap-4 py-5 text-left cursor-pointer group">
<h3 className="font-serif text-base sm:text-lg font-semibold group-hover:text-primary transition-colors">{question}</h3>
<ChevronDown className={`h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200 ${open ? 'rotate-180' : ''}`} />
</button>
<div className={`grid transition-[grid-template-rows] duration-200 ${open ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'}`}>
@@ -428,15 +503,18 @@ function FinalCTA() {
const { ref, visible } = useInView()
return (
<section ref={ref} className="py-28 sm:py-36 px-4 sm:px-6 border-t border-border/30 relative overflow-hidden">
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,rgba(217,94,42,0.08)_0%,transparent_60%)]" />
<section ref={ref} className="py-16 sm:py-24 px-4 sm:px-6 border-t border-border/30 relative overflow-hidden">
{/* Aurora background */}
<div className="absolute inset-0 aurora-bg opacity-60" />
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,rgba(217,94,42,0.1)_0%,transparent_60%)]" />
<div className={`relative mx-auto max-w-3xl text-center transition-all duration-700 ${visible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
<h2 className="font-serif text-4xl sm:text-5xl md:text-6xl font-semibold tracking-tight mb-8">
Build something that <em className="italic text-primary">ships.</em>
<h2 className="font-serif text-3xl sm:text-4xl md:text-5xl font-semibold tracking-tight mb-6 text-shimmer">
Build something that ships.
</h2>
<a href="mailto:hello@greyhaven.co" className="inline-flex items-center gap-2 px-8 py-4 bg-primary text-primary-foreground font-sans font-medium rounded-lg hover:bg-primary/90 transition-all glow-orange text-base">
<a href="mailto:hello@greyhaven.co" className="group inline-flex items-center gap-2 px-8 py-4 bg-primary text-primary-foreground font-sans font-medium rounded-lg hover:bg-primary/90 transition-all glow-orange text-base hover:shadow-[0_0_30px_rgba(217,94,42,0.3)]">
Get notified when registration opens
<ArrowRight className="h-4 w-4" />
<ArrowRight className="h-4 w-4 group-hover:translate-x-0.5 transition-transform" />
</a>
<p className="text-xs text-muted-foreground/50 font-sans mt-8">
Follow{' '}
@@ -452,7 +530,8 @@ function FinalCTA() {
export default function HackathonsPage() {
return (
<main className="min-h-screen">
<main className="min-h-screen relative">
<NoiseOverlay />
<Nav />
<Hero />
<Stats />