better designs
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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's proxy captures every request, file access, and command your AI agent executes. This is what you'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 />
|
||||
|
||||
Reference in New Issue
Block a user