735 lines
34 KiB
TypeScript
735 lines
34 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useEffect, useRef, Suspense, useCallback } from 'react'
|
|
import dynamic from 'next/dynamic'
|
|
import {
|
|
ChevronDown,
|
|
ArrowRight,
|
|
Users,
|
|
Star,
|
|
MapPin,
|
|
Eye,
|
|
Target,
|
|
AlertTriangle,
|
|
ShieldAlert,
|
|
MessageSquare,
|
|
Crown,
|
|
} from 'lucide-react'
|
|
import { Footer } from '@/components/footer'
|
|
import { LiveTerminal } from '@/components/hackathons/live-terminal'
|
|
import { StreamViz, SecureViz, RadarViz, ScanViz, ExtendViz } from '@/components/hackathons/track-visuals'
|
|
|
|
const ShieldScene = dynamic(
|
|
() => import('@/components/hackathons/shield-scene').then((m) => m.ShieldScene),
|
|
{ ssr: false }
|
|
)
|
|
|
|
/* ─── Hooks ─── */
|
|
|
|
function useInView(threshold = 0.15) {
|
|
const ref = useRef<HTMLDivElement>(null)
|
|
const [visible, setVisible] = useState(false)
|
|
useEffect(() => {
|
|
const el = ref.current
|
|
if (!el) return
|
|
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])
|
|
return { ref, visible }
|
|
}
|
|
|
|
|
|
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>
|
|
)
|
|
}
|
|
|
|
|
|
/* ─── Nav ─── */
|
|
|
|
function Nav() {
|
|
return (
|
|
<nav className="fixed top-0 left-0 right-0 z-50 border-b border-border/50 bg-background/80 backdrop-blur-md">
|
|
<div className="mx-auto max-w-5xl flex items-center justify-between px-6 h-14">
|
|
<a href="/" className="flex items-center gap-2.5">
|
|
<svg viewBox="0 0 32 32" fill="none" className="h-6 w-6" xmlns="http://www.w3.org/2000/svg">
|
|
<path d="M16 2L4 7V15C4 22.18 9.11 28.79 16 30C22.89 28.79 28 22.18 28 15V7L16 2Z" fill="#D95E2A" />
|
|
<path d="M16 6L8 9.5V15C8 20.05 11.42 24.68 16 26C20.58 24.68 24 20.05 24 15V9.5L16 6Z" fill="#161614" />
|
|
<circle cx="16" cy="12" r="2" fill="#D95E2A" /><circle cx="12" cy="17" r="1.5" fill="#D95E2A" />
|
|
<circle cx="20" cy="17" r="1.5" fill="#D95E2A" /><circle cx="16" cy="21" r="1.5" fill="#D95E2A" />
|
|
<path d="M16 14V19.5M14 16L12.5 17M18 16L19.5 17" stroke="#D95E2A" strokeWidth="1" strokeLinecap="round" />
|
|
</svg>
|
|
<span className="font-serif font-semibold text-lg tracking-tight">Greywall</span>
|
|
</a>
|
|
<div className="flex items-center gap-6">
|
|
<a href="#tracks" className="text-sm text-muted-foreground hover:text-foreground transition-colors hidden sm:block">Tracks</a>
|
|
<a href="#faq" className="text-sm text-muted-foreground hover:text-foreground transition-colors hidden sm:block">FAQ</a>
|
|
<a href="https://github.com/GreyhavenHQ/greywall" target="_blank" rel="noopener noreferrer" className="text-sm text-muted-foreground hover:text-foreground transition-colors">
|
|
<svg className="h-4 w-4" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" /></svg>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
)
|
|
}
|
|
|
|
/* ─── 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 background */}
|
|
<div className="absolute inset-0 z-0 opacity-60">
|
|
<Suspense fallback={null}>
|
|
<ShieldScene />
|
|
</Suspense>
|
|
</div>
|
|
|
|
{/* 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-4 text-shimmer">
|
|
Hack the Wall.
|
|
</h1>
|
|
<p className="text-xl sm:text-2xl text-muted-foreground font-serif mb-10 max-w-2xl mx-auto">
|
|
AI Safety & Data Sovereignty Hackathon 2026
|
|
</p>
|
|
<a
|
|
href="#tracks"
|
|
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 group-hover:translate-x-0.5 transition-transform" />
|
|
</a>
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|
|
|
|
|
|
/* ─── Notify Button ─── */
|
|
|
|
function NotifyButton({ className = '' }: { className?: string }) {
|
|
const [mode, setMode] = useState<'button' | 'form' | 'success'>('button')
|
|
const [email, setEmail] = useState('')
|
|
const [submitting, setSubmitting] = useState(false)
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
if (!email) return
|
|
setSubmitting(true)
|
|
try {
|
|
const formData = new FormData()
|
|
formData.append('access_key', '9239e4ed-eb6f-4fa2-afdd-f40b9dec25bf')
|
|
formData.append('email', email)
|
|
formData.append('subject', 'Hackathon Registration Interest')
|
|
const response = await fetch('https://api.web3forms.com/submit', {
|
|
method: 'POST',
|
|
body: formData,
|
|
})
|
|
const data = await response.json()
|
|
if (data.success) {
|
|
setMode('success')
|
|
}
|
|
} catch {
|
|
window.location.href = `mailto:hello@greyhaven.co?subject=Hackathon%20Notify&body=Please%20notify%20me%20at%20${encodeURIComponent(email)}`
|
|
setMode('success')
|
|
}
|
|
setSubmitting(false)
|
|
}
|
|
|
|
if (mode === 'success') {
|
|
return (
|
|
<div className={`inline-flex items-center gap-2 px-8 py-4 bg-primary/10 border border-primary/20 text-primary font-sans font-medium rounded-lg text-base ${className}`}>
|
|
You are on the list.
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (mode === 'form') {
|
|
return (
|
|
<form onSubmit={handleSubmit} className={`inline-flex items-center gap-2 ${className}`}>
|
|
<input
|
|
type="email"
|
|
required
|
|
placeholder="you@example.com"
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
autoFocus
|
|
className="px-4 py-3 bg-card/60 border border-border/40 rounded-lg text-sm font-sans text-foreground placeholder:text-muted-foreground/50 focus:outline-none focus:border-primary/50 w-64"
|
|
/>
|
|
<button
|
|
type="submit"
|
|
disabled={submitting}
|
|
className="px-6 py-3 bg-primary text-primary-foreground font-sans font-medium rounded-lg hover:bg-primary/90 transition-all text-sm cursor-pointer disabled:opacity-50"
|
|
>
|
|
{submitting ? 'Sending...' : 'Notify me'}
|
|
</button>
|
|
</form>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<button
|
|
onClick={() => setMode('form')}
|
|
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)] cursor-pointer ${className}`}
|
|
>
|
|
Get notified when registration opens
|
|
<ArrowRight className="h-4 w-4 group-hover:translate-x-0.5 transition-transform" />
|
|
</button>
|
|
)
|
|
}
|
|
|
|
/* ─── Info Section (Tabbed) ─── */
|
|
|
|
const infoTabs = ['Overview', 'Resources', 'Guidelines', 'Schedule'] as const
|
|
|
|
function OverviewTab() {
|
|
return (
|
|
<div className="space-y-12">
|
|
{/* Intro */}
|
|
<div>
|
|
<p className="font-serif text-lg text-muted-foreground leading-relaxed mb-6">
|
|
The Greywall Hackathon brings together engineers, security professionals, and AI enthusiasts to tackle one of the most urgent open problems: how do we keep AI agents safe when they operate autonomously on real systems?
|
|
</p>
|
|
<p className="font-serif text-lg text-muted-foreground leading-relaxed">
|
|
Over 48 hours, participants will build guardrails, filters, classifiers, and detection systems that sit on top of{' '}
|
|
<a href="https://github.com/GreyhavenHQ/greywall" target="_blank" rel="noopener noreferrer" className="text-primary hover:text-primary/80 transition-colors underline underline-offset-2">Greywall</a>,
|
|
an open-source sandboxing system for AI agents built by{' '}
|
|
<a href="https://greyhaven.co" target="_blank" rel="noopener noreferrer" className="text-primary hover:text-primary/80 transition-colors underline underline-offset-2">Greyhaven</a>.
|
|
</p>
|
|
</div>
|
|
|
|
{/* What is Sovereign AI? */}
|
|
<div>
|
|
<h3 className="font-serif text-2xl sm:text-3xl font-semibold tracking-tight mb-4">What is Sovereign AI?</h3>
|
|
<p className="font-serif text-lg text-muted-foreground leading-relaxed">
|
|
Sovereign AI is the principle that organizations should maintain full control over their AI systems: what they can access, what data they process, and what actions they take. No data leaks, no unauthorized actions, no black boxes. Your AI agents should work for you, within boundaries you define. That is what Greywall enforces, and that is what this hackathon is about extending.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Why this hackathon? */}
|
|
<div>
|
|
<h3 className="font-serif text-2xl sm:text-3xl font-semibold tracking-tight mb-4">Why this hackathon?</h3>
|
|
<p className="font-serif text-lg text-muted-foreground leading-relaxed">
|
|
AI agents are getting more powerful and more autonomous every month. But the security tooling has not kept up. There is a real gap between what agents can do and the guardrails available to keep them in check. This hackathon exists to close that gap, and to give talented people a chance to build the tools that the entire industry needs.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Prizes */}
|
|
<div>
|
|
<h3 className="font-serif text-2xl sm:text-3xl font-semibold tracking-tight mb-6">Top teams get</h3>
|
|
<div className="grid sm:grid-cols-3 gap-4">
|
|
{[
|
|
{ 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' },
|
|
{ icon: Users, title: 'CEO Dinner Invitation', sub: 'Attend an exclusive Greyhaven CEO dinner for free' },
|
|
].map((item) => (
|
|
<div key={item.title} className="text-center p-6 rounded-2xl border border-border/30 bg-card/20 backdrop-blur-sm">
|
|
<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>
|
|
<h4 className="font-serif text-base font-semibold mb-1">{item.title}</h4>
|
|
<p className="text-xs text-muted-foreground font-sans">{item.sub}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function ResourcesTab() {
|
|
const articles = [
|
|
{
|
|
title: 'The Greyhaven Sovereign AI Framework',
|
|
url: 'https://greyhaven.co/insights/greyhaven-sovereign-ai-framework',
|
|
description: 'Our framework for how organizations can maintain sovereignty over their AI systems.',
|
|
},
|
|
{
|
|
title: 'Why We Built Our Own Sandboxing System',
|
|
url: 'https://greyhaven.co/insights/why-we-built-our-own-sandboxing-system',
|
|
description: 'The story behind Greywall and why existing solutions were not enough.',
|
|
},
|
|
{
|
|
title: 'Greywall on GitHub',
|
|
url: 'https://github.com/GreyhavenHQ/greywall',
|
|
description: 'The open-source codebase you will be building on. Start here.',
|
|
},
|
|
{
|
|
title: 'Greywall Documentation',
|
|
url: 'https://docs.greywall.io/',
|
|
description: 'Setup guides, API reference, and architecture docs.',
|
|
},
|
|
]
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
{articles.map((article) => (
|
|
<a
|
|
key={article.title}
|
|
href={article.url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="group flex items-start gap-4 p-5 rounded-xl border border-border/30 bg-card/20 hover:bg-card/40 hover:border-primary/20 transition-all"
|
|
>
|
|
<div className="flex-1">
|
|
<h4 className="font-serif text-lg font-semibold mb-1 group-hover:text-primary transition-colors">{article.title}</h4>
|
|
<p className="text-sm text-muted-foreground font-sans">{article.description}</p>
|
|
</div>
|
|
<ArrowRight className="h-4 w-4 text-muted-foreground group-hover:text-primary group-hover:translate-x-0.5 transition-all mt-1.5 shrink-0" />
|
|
</a>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function GuidelinesTab() {
|
|
const dimensions = [
|
|
{
|
|
title: 'Impact Potential & Innovation',
|
|
question: 'How much would this matter for AI safety if it worked? How innovative is it?',
|
|
scores: [
|
|
{ score: 1, desc: 'Negligible. No clear problem addressed, or no meaningful novelty.' },
|
|
{ score: 2, desc: 'Limited. Addresses a real problem but with a generic or well-trodden approach.' },
|
|
{ score: 3, desc: 'Moderate. Clear problem with a reasonable approach; some novelty in framing or method.' },
|
|
{ score: 4, desc: 'Significant. Important problem with an original approach. A valuable contribution others could build on.' },
|
|
{ score: 5, desc: 'Exceptional. Tackles a critical AI safety problem with a genuinely novel approach. Opens a new direction.' },
|
|
],
|
|
},
|
|
{
|
|
title: 'Execution Quality',
|
|
question: 'How sound are methodology, implementation, and findings?',
|
|
scores: [
|
|
{ score: 1, desc: 'Seriously flawed. Methodology broken, results uninterpretable, or implementation does not work.' },
|
|
{ score: 2, desc: 'Weak. Significant gaps: missing validation, flawed experimental design, or incomplete implementation.' },
|
|
{ score: 3, desc: 'Competent. Technically solid given the short duration. Results are interpretable, limitations acknowledged.' },
|
|
{ score: 4, desc: 'Strong. Thorough methodology with convincing validation. Immediately useful for future work.' },
|
|
{ score: 5, desc: 'Exceptional. Ambitious scope executed rigorously. Surprising findings or unusually robust validation.' },
|
|
],
|
|
},
|
|
{
|
|
title: 'Presentation & Clarity',
|
|
question: 'How clearly are work, findings, and impact potential communicated?',
|
|
scores: [
|
|
{ score: 1, desc: 'Incomprehensible. Cannot determine what the project is actually claiming or doing.' },
|
|
{ score: 2, desc: 'Hard to follow. Key information buried or missing. Significant effort to extract main points.' },
|
|
{ score: 3, desc: 'Clear enough. Can understand the problem, approach, and results without undue effort.' },
|
|
{ score: 4, desc: 'Well presented. Easy to follow, well-structured. Target audience would get it quickly.' },
|
|
{ score: 5, desc: 'Exceptionally clear. A pleasure to read. Complex ideas made accessible.' },
|
|
],
|
|
},
|
|
]
|
|
|
|
return (
|
|
<div className="space-y-12">
|
|
{/* Judging Criteria */}
|
|
<div>
|
|
<h3 className="font-serif text-2xl sm:text-3xl font-semibold tracking-tight mb-6">Judging Criteria</h3>
|
|
<div className="space-y-8">
|
|
{dimensions.map((dim) => (
|
|
<div key={dim.title} className="rounded-xl border border-border/30 bg-card/20 overflow-hidden">
|
|
<div className="p-5 border-b border-border/20">
|
|
<h4 className="font-serif text-lg font-semibold mb-1">{dim.title}</h4>
|
|
<p className="text-sm text-muted-foreground font-sans">{dim.question}</p>
|
|
</div>
|
|
<div className="divide-y divide-border/15">
|
|
{dim.scores.map((s) => (
|
|
<div key={s.score} className="flex gap-4 px-5 py-3">
|
|
<span className="font-sans text-sm font-bold text-primary w-6 shrink-0">{s.score}</span>
|
|
<p className="text-sm text-muted-foreground font-sans">{s.desc}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Submission Requirements */}
|
|
<div>
|
|
<h3 className="font-serif text-2xl sm:text-3xl font-semibold tracking-tight mb-6">Submission Requirements</h3>
|
|
<div className="rounded-xl border border-border/30 bg-card/20 p-6 space-y-6">
|
|
<div>
|
|
<h4 className="font-serif text-base font-semibold mb-3">A complete submission includes:</h4>
|
|
<ul className="space-y-2 text-sm text-muted-foreground font-sans">
|
|
<li className="flex gap-2"><span className="text-primary">•</span>A research report in PDF format</li>
|
|
<li className="flex gap-2"><span className="text-primary">•</span>A project title and brief abstract (150 words max)</li>
|
|
<li className="flex gap-2"><span className="text-primary">•</span>Author names for all team members</li>
|
|
<li className="flex gap-2"><span className="text-primary">•</span>Which challenge track your project addresses</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 className="font-serif text-base font-semibold mb-3">Recommended report structure:</h4>
|
|
<p className="text-sm text-muted-foreground font-sans mb-3">There is no hard page limit. Most strong submissions are 4-8 pages.</p>
|
|
<ul className="space-y-2 text-sm text-muted-foreground font-sans">
|
|
<li className="flex gap-2"><span className="text-primary font-semibold">Introduction:</span>What problem did you address? Why does it matter?</li>
|
|
<li className="flex gap-2"><span className="text-primary font-semibold">Related Work:</span>What existing work does your project build on?</li>
|
|
<li className="flex gap-2"><span className="text-primary font-semibold">Methodology:</span>What did you build or test? Enough detail to replicate.</li>
|
|
<li className="flex gap-2"><span className="text-primary font-semibold">Results:</span>What did you find? Include quantitative results where possible.</li>
|
|
<li className="flex gap-2"><span className="text-primary font-semibold">Discussion:</span>Implications, limitations, what you would do with more time.</li>
|
|
<li className="flex gap-2"><span className="text-primary font-semibold">References:</span>Cite relevant prior work.</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 className="font-serif text-base font-semibold mb-3">Important notes:</h4>
|
|
<ul className="space-y-2 text-sm text-muted-foreground font-sans">
|
|
<li className="flex gap-2"><span className="text-primary">•</span>You can submit as an individual or as a team</li>
|
|
<li className="flex gap-2"><span className="text-primary">•</span>You can build on existing work, but you must clearly identify what is new work done during the hackathon</li>
|
|
<li className="flex gap-2"><span className="text-primary">•</span>Your PDF is not editable or replaceable once submitted</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function ScheduleTab() {
|
|
return (
|
|
<div className="text-center py-12 space-y-6">
|
|
<p className="font-serif text-lg text-muted-foreground">Schedule will be announced soon.</p>
|
|
<NotifyButton />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function InfoSection() {
|
|
const [activeTab, setActiveTab] = useState<string>('overview')
|
|
|
|
return (
|
|
<section className="py-16 sm:py-24 px-4 sm:px-6 border-t border-border/30">
|
|
<div className="mx-auto max-w-4xl">
|
|
{/* Tab navigation */}
|
|
<div className="flex gap-2 mb-12 justify-center flex-wrap">
|
|
{infoTabs.map((tab) => (
|
|
<button
|
|
key={tab}
|
|
onClick={() => setActiveTab(tab.toLowerCase())}
|
|
className={`px-5 py-2 rounded-full text-sm font-sans font-medium transition-all cursor-pointer ${
|
|
activeTab === tab.toLowerCase()
|
|
? 'bg-primary text-primary-foreground'
|
|
: 'bg-card/40 border border-border/40 text-muted-foreground hover:text-foreground hover:border-border/60'
|
|
}`}
|
|
>
|
|
{tab}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{/* Tab content */}
|
|
{activeTab === 'overview' && <OverviewTab />}
|
|
{activeTab === 'resources' && <ResourcesTab />}
|
|
{activeTab === 'guidelines' && <GuidelinesTab />}
|
|
{activeTab === 'schedule' && <ScheduleTab />}
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|
|
|
|
/* ─── 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 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>
|
|
<h2 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-4">
|
|
Every agent action, in real time.
|
|
</h2>
|
|
<p className="font-serif text-lg text-muted-foreground leading-relaxed">
|
|
<a href="https://github.com/GreyhavenHQ/greywall" target="_blank" rel="noopener noreferrer" className="text-primary hover:text-primary/80 transition-colors underline underline-offset-2">Greywall</a>'s proxy captures every request, file access, and command your AI agent executes. This is what you'll be building on.
|
|
</p>
|
|
</div>
|
|
<div className="animate-float [animation-duration:6s]">
|
|
<LiveTerminal />
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|
|
|
|
/* ─── Tracks ─── */
|
|
|
|
const tracks = [
|
|
{
|
|
id: 'pii-filtering',
|
|
icon: Eye,
|
|
title: 'PII Filtering',
|
|
hook: 'Strip sensitive data before it reaches the model, without breaking the task.',
|
|
color: 'from-orange-500/10 to-amber-500/5',
|
|
borderColor: 'hover:border-orange-500/30',
|
|
description: 'Build a Greywall layer that strips PII (names, credit cards, etc.) from data before it reaches the model while still letting the agent complete tasks correctly. You define your own test cases and demonstrate it works.',
|
|
scoring: 'You bring your own proof. Show it works on real-world data, not just toy examples.',
|
|
examples: ['Data masking', 'Pattern detection', 'Context-aware redaction', 'Format preservation'],
|
|
Visual: StreamViz,
|
|
},
|
|
{
|
|
id: 'intent-classifier',
|
|
icon: Target,
|
|
title: 'Intent vs. Action Classifier',
|
|
hook: 'Detect when an agent does something the user never asked for.',
|
|
color: 'from-emerald-500/10 to-teal-500/5',
|
|
borderColor: 'hover:border-emerald-500/30',
|
|
description: 'Build a classifier that sits in the proxy and blocks destructive actions that weren\'t asked for. Some tool calls match the user\'s intent ("delete files starting with 1" results in rm ./1*). Some don\'t ("refactor this module" results in rm -rf everything). You build the test suite that proves it.',
|
|
scoring: 'Design your own evaluation. Demonstrate it catches real mismatches, not just scripted ones.',
|
|
examples: ['Heuristics-based', 'ML classifiers', 'Semantic matching', 'Action risk scoring'],
|
|
Visual: SecureViz,
|
|
},
|
|
{
|
|
id: 'derail-detection',
|
|
icon: AlertTriangle,
|
|
title: 'Derail Detection',
|
|
hook: 'Catch agents that keep trying variations after being blocked.',
|
|
color: 'from-amber-500/10 to-yellow-500/5',
|
|
borderColor: 'hover:border-amber-500/30',
|
|
description: 'Detect when an agent persistently mutates blocked commands, like repeatedly trying SSH variations to find a path through. Build something that spots the pattern and stops or redirects it with an informative message.',
|
|
scoring: 'Create your own adversarial sessions and prove your detector catches them.',
|
|
examples: ['Pattern matching', 'Mutation detection', 'Session analysis', 'Auto-redirect'],
|
|
Visual: RadarViz,
|
|
},
|
|
{
|
|
id: 'antivirus',
|
|
icon: ShieldAlert,
|
|
title: 'Malicious Request Detection',
|
|
hook: 'Scan requests for supply chain attacks and flag them. Fast.',
|
|
color: 'from-cyan-500/10 to-sky-500/5',
|
|
borderColor: 'hover:border-cyan-500/30',
|
|
description: 'Build a guardrail that scans network requests and commands for malicious patterns (e.g. supply chain attacks via NPM packages). You define the threat scenarios and prove detection works without killing latency.',
|
|
scoring: 'Fastest accurate solution wins. You bring the benchmarks.',
|
|
examples: ['Request scanning', 'Dependency analysis', 'Threat signatures', 'Low-latency filtering'],
|
|
Visual: ScanViz,
|
|
},
|
|
{
|
|
id: 'response-rewriting',
|
|
icon: MessageSquare,
|
|
title: 'Response Rewriting',
|
|
hook: 'Turn cryptic block messages into helpful guidance.',
|
|
color: 'from-violet-500/10 to-purple-500/5',
|
|
borderColor: 'hover:border-violet-500/30',
|
|
description: 'When an agent gets blocked, intercept the error and rewrite it to explain why and suggest alternatives, so the agent self-corrects instead of retrying the same thing twenty times.',
|
|
scoring: 'Demonstrate with real agent sessions that your rewrites actually stop the retry loop.',
|
|
examples: ['Error interception', 'Context injection', 'Alternative suggestion', 'Loop prevention'],
|
|
Visual: ExtendViz,
|
|
},
|
|
]
|
|
|
|
function TrackCard({ track, index }: { track: typeof tracks[0]; index: number }) {
|
|
const { ref, visible } = useInView(0.1)
|
|
const { ref: spotlightRef, onMove } = useMouseSpotlight()
|
|
|
|
return (
|
|
<div
|
|
ref={ref}
|
|
className={`transition-all duration-700 ${visible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-12'}`}
|
|
style={{ transitionDelay: `${index * 120}ms` }}
|
|
>
|
|
<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">
|
|
<h3 className="font-serif text-3xl sm:text-4xl font-semibold tracking-tight mb-3">
|
|
{track.title}
|
|
</h3>
|
|
<p className="font-serif text-lg text-muted-foreground leading-relaxed mb-4 max-w-lg">
|
|
{track.hook}
|
|
</p>
|
|
<p className="text-sm text-muted-foreground/80 font-sans leading-relaxed mb-2 max-w-lg">
|
|
{track.description}
|
|
</p>
|
|
<p className="text-sm text-primary/80 font-sans font-medium mb-6 max-w-lg">
|
|
{track.scoring}
|
|
</p>
|
|
|
|
<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 hover:border-primary/20 hover:text-foreground transition-colors">
|
|
{ex}
|
|
</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Side visual */}
|
|
<div className="hidden md:block w-[220px] shrink-0 relative overflow-hidden">
|
|
<track.Visual />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function Tracks() {
|
|
return (
|
|
<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 track.
|
|
</h2>
|
|
<p className="font-serif text-lg text-muted-foreground max-w-2xl mx-auto">
|
|
Five open-ended tracks, all building on top of{' '}
|
|
<a href="https://github.com/GreyhavenHQ/greywall" target="_blank" rel="noopener noreferrer" className="text-primary hover:text-primary/80 transition-colors underline underline-offset-2">Greywall</a>.
|
|
Go deep on one or try a few.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="space-y-6">
|
|
{tracks.map((track, i) => (
|
|
<TrackCard key={track.id} track={track} index={i} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|
|
|
|
|
|
/* ─── Location ─── */
|
|
|
|
function Location() {
|
|
const { ref, visible } = useInView()
|
|
|
|
return (
|
|
<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-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-3xl sm:text-4xl font-semibold tracking-tight mb-2">
|
|
Montreal.
|
|
</h2>
|
|
<p className="font-serif text-lg text-muted-foreground">
|
|
Venue and dates announced soon.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|
|
|
|
/* ─── FAQ ─── */
|
|
|
|
const faqs = [
|
|
{ q: 'Can I work on more than one track?', a: 'Yes. You can tackle as many tracks as you want over 48 hours. Focus deep on one or spread across several.' },
|
|
{ q: 'Do I need security or ML experience?', a: 'No. The tracks are designed so you can approach them with heuristics, ML, or creative engineering. If you can write code, you can participate.' },
|
|
{ q: 'How are teams formed?', a: 'We split participants into teams of 2 to 3. You can request to be grouped with someone, or we will match you.' },
|
|
{ q: 'Do I need to know Greywall?', a: 'Nope. We provide setup support and Greywall maintainers are on hand throughout.' },
|
|
{ q: 'What happens to my code?', a: 'Your code is yours. Winners get featured on the permanent Hall of Fame page, added as contributors in the GitHub README, and invited to an exclusive Greyhaven CEO dinner.' },
|
|
{ q: 'What do I need to bring?', a: 'A laptop. We provide Greywall infrastructure, docs, food, and caffeine.' },
|
|
]
|
|
|
|
function FAQ() {
|
|
return (
|
|
<section id="faq" className="py-24 sm:py-32 px-4 sm:px-6 border-t border-border/30">
|
|
<div className="mx-auto max-w-3xl">
|
|
<h2 className="font-serif text-4xl sm:text-5xl font-semibold tracking-tight text-center mb-16">FAQ.</h2>
|
|
<div>
|
|
{faqs.map((faq) => <FAQItem key={faq.q} question={faq.q} answer={faq.a} />)}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|
|
|
|
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 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]'}`}>
|
|
<div className="overflow-hidden">
|
|
<p className="pb-5 text-muted-foreground font-serif text-base leading-relaxed">{answer}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
/* ─── Final CTA ─── */
|
|
|
|
function FinalCTA() {
|
|
const { ref, visible } = useInView()
|
|
|
|
return (
|
|
<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-3xl sm:text-4xl md:text-5xl font-semibold tracking-tight mb-6 text-shimmer">
|
|
Build something that matters.
|
|
</h2>
|
|
<NotifyButton />
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|
|
|
|
/* ─── Page ─── */
|
|
|
|
export default function HackathonsPage() {
|
|
return (
|
|
<main className="min-h-screen relative">
|
|
<NoiseOverlay />
|
|
<Nav />
|
|
<Hero />
|
|
<TerminalShowcase />
|
|
<InfoSection />
|
|
<Tracks />
|
|
<Location />
|
|
<FAQ />
|
|
<FinalCTA />
|
|
<Footer />
|
|
</main>
|
|
)
|
|
}
|