'use client' import { useState, useEffect, useRef, Suspense } from 'react' import dynamic from 'next/dynamic' import { Activity, Shield, Code2, ChevronDown, ArrowRight, Users, Trophy, GitMerge, Star, Terminal, Clock, MapPin, Cpu, Boxes, Sparkles, } from 'lucide-react' import { Footer } from '@/components/footer' import { LiveTerminal } from '@/components/hackathons/live-terminal' import { StreamViz, SecureViz, 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(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 }) obs.observe(el) return () => obs.disconnect() }, [threshold]) 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) setCount(Math.floor(p * target)) if (p < 1) requestAnimationFrame(step) } requestAnimationFrame(step) }, [visible, target]) return }>{count}{suffix} } /* ─── Nav ─── */ function Nav() { return ( ) } /* ─── Hero with 3D Shield ─── */ function Hero() { return (
{/* 3D Shield as background */}
{/* Gradient overlays to keep text readable */}

Hack the Wall.

Build on the AI agent security stack. Your best hacks get merged into Greywall.

Explore tracks
) } /* ─── Stats ─── */ function Stats() { return (
{[ { value: 24, suffix: 'h', label: 'of hacking' }, { value: 3, suffix: '', label: 'open-ended tracks' }, { value: 100, suffix: '%', label: 'open source' }, ].map((stat) => (
{stat.label}
))}
) } /* ─── Live Terminal Showcase ─── */ function TerminalShowcase() { const { ref, visible } = useInView(0.1) return (

The data stream

Every agent action. In real time.

Greywall's proxy captures every request, file access, and command your AI agent executes. This is what you'll be building on.

) } /* ─── Tracks with visuals ─── */ const tracks = [ { id: 'stream', icon: Activity, title: 'Build on the Stream', hook: 'One live data firehose. Make something cool with it.', color: 'from-orange-500/10 to-amber-500/5', borderColor: 'hover:border-orange-500/30', examples: ['Dashboards', 'Anomaly detection', 'Cost trackers', 'Behavior research', 'Bots', 'Art'], Visual: StreamViz, }, { id: 'secure', icon: Shield, title: 'Secure Your Stack', hook: 'Bring your own project. Lock it down. Demo the tightest sandbox.', color: 'from-emerald-500/10 to-teal-500/5', borderColor: 'hover:border-emerald-500/30', examples: ['Policy templates', 'Threat models', 'Security writeups', 'Monitoring configs'], Visual: SecureViz, }, { id: 'extend', icon: Code2, title: 'Extend Greywall', hook: 'Plugin, CLI tool, VS Code extension, web UI. If it\'s cool, it counts.', color: 'from-violet-500/10 to-purple-500/5', borderColor: 'hover:border-violet-500/30', examples: ['IDE plugins', 'NLP policies', 'Cost guardians', 'Grafana integrations', 'Wild ideas'], Visual: ExtendViz, }, ] function TrackCard({ track, index }: { track: typeof tracks[0]; index: number }) { const { ref, visible } = useInView(0.1) return (
{/* Content */}
24h Teams Montreal

{track.title}

{track.hook}

{track.examples.map((ex) => ( {ex} ))}
{/* Side visual */}
) } function Tracks() { return (

Pick your arena.

Three tracks. All open-ended. You bring the creativity.

{tracks.map((track, i) => ( ))}
) } /* ─── How It Works ─── */ const steps = [ { icon: Sparkles, title: 'Register', sub: 'All levels welcome' }, { icon: Terminal, title: 'Get set up', sub: 'We provide everything' }, { icon: Cpu, title: 'Hack for 24h', sub: 'Mentors on hand' }, { icon: Trophy, title: 'Demo & win', sub: 'Best hacks ship' }, ] function HowItWorks() { const { ref, visible } = useInView() return (

How it works.

{steps.map((step, i) => (

{step.title}

{step.sub}

))}
) } /* ─── Prizes ─── */ function Prizes() { const { ref, visible } = useInView() const items = [ { icon: GitMerge, title: 'Code gets merged', sub: 'Your hack becomes the product' }, { icon: Star, title: 'Contributor credit', sub: 'Name in the release notes' }, { icon: Boxes, title: 'Demo day invite', sub: 'Present to the community' }, { icon: Trophy, title: 'Community status', sub: 'Featured on greywall.io' }, ] return (

More than a trophy.

The best hacks ship.

{items.map((item, i) => (

{item.title}

{item.sub}

))}
) } /* ─── Location ─── */ function Location() { const { ref, visible } = useInView() return (
Location

Montreal.

Venue and dates announced soon.

) } /* ─── FAQ ─── */ const faqs = [ { q: 'Do I need security experience?', a: 'No. All experience levels welcome. If you can write code, you can participate.' }, { q: 'Do I need to know Greywall?', a: 'Nope. We provide setup support and Greywall maintainers are on hand throughout.' }, { q: 'Is it in-person only?', a: 'Yes. Hackathons are a social experience. Plus, you get direct access to maintainers all day.' }, { q: 'What happens to my code?', a: 'Your code is yours. Winning hacks get merged into Greywall with your full contributor credit, but only with your consent.' }, { q: 'What do I need to bring?', a: 'A laptop. We provide Greywall infrastructure, data streams, docs, food, and caffeine.' }, ] function FAQ() { return (

FAQ.

{faqs.map((faq) => )}
) } function FAQItem({ question, answer }: { question: string; answer: string }) { const [open, setOpen] = useState(false) return (

{answer}

) } /* ─── Final CTA ─── */ function FinalCTA() { const { ref, visible } = useInView() return (

Build something that ships.

Get notified when registration opens

Follow{' '} Greywall on GitHub{' '} for updates.

) } /* ─── Page ─── */ export default function HackathonsPage() { return (
) }