diff --git a/landing/.gitignore b/landing/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/landing/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/landing/app/globals.css b/landing/app/globals.css new file mode 100644 index 0000000..e6b390b --- /dev/null +++ b/landing/app/globals.css @@ -0,0 +1,412 @@ +:root { + --bg: #f8f9fc; + --surface: #ffffff; + --border: #e2e8f0; + --text: #0f172a; + --text-secondary: #475569; + --muted: #94a3b8; + --accent: #2563eb; + --accent-hover: #1d4ed8; + --accent-light: #eff6ff; + --emerald: #059669; + --emerald-light: #ecfdf5; + --emerald-bright: #10b981; + --violet: #7c3aed; + --violet-light: #f5f3ff; + --rose: #e11d48; + --rose-light: #fff1f2; + --amber: #d97706; + --amber-light: #fffbeb; + --radius: 10px; + --radius-lg: 16px; + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04); + --shadow: 0 1px 3px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04); + --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.06), 0 2px 4px -2px rgba(0, 0, 0, 0.04); + --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.06), 0 4px 6px -4px rgba(0, 0, 0, 0.04); + --transition: 0.2s ease; +} + +* { margin: 0; padding: 0; box-sizing: border-box; } + +html { scroll-behavior: smooth; } + +body { + background: var(--bg); + color: var(--text); + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + -webkit-font-smoothing: antialiased; +} + +::selection { background: var(--accent-light); color: var(--accent); } + +/* ── Card ── */ +.card { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-sm); + transition: box-shadow var(--transition), border-color var(--transition); +} +.card:hover { + box-shadow: var(--shadow-md); + border-color: #cbd5e1; +} + +.card-spotlight { + position: relative; + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + overflow: hidden; + transition: box-shadow var(--transition), border-color var(--transition); +} +.card-spotlight::before { + content: ''; + position: absolute; + inset: 0; + background: radial-gradient( + 600px circle at var(--mouse-x, 50%) var(--mouse-y, 50%), + rgba(37, 99, 235, 0.04), + transparent 40% + ); + opacity: 0; + transition: opacity 0.3s; + pointer-events: none; +} +.card-spotlight:hover::before { opacity: 1; } +.card-spotlight:hover { + box-shadow: var(--shadow-md); + border-color: #cbd5e1; +} + +/* ── Button ── */ +.btn-primary { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 12px 28px; + background: var(--accent); + color: #fff; + font-weight: 600; + font-size: 15px; + border-radius: var(--radius); + border: none; + cursor: pointer; + transition: background 0.2s, box-shadow 0.2s; + text-decoration: none; +} +.btn-primary:hover { + background: var(--accent-hover); + box-shadow: var(--shadow-md); +} + +.btn-secondary { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 12px 28px; + background: var(--surface); + color: var(--text); + font-weight: 600; + font-size: 15px; + border-radius: var(--radius); + border: 1px solid var(--border); + cursor: pointer; + transition: border-color 0.2s, box-shadow 0.2s; + text-decoration: none; +} +.btn-secondary:hover { + border-color: var(--accent); + box-shadow: var(--shadow-sm); +} + +.btn-ghost { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 16px; + background: transparent; + color: var(--text-secondary); + font-size: 13px; + font-weight: 500; + border-radius: 8px; + border: 1px solid transparent; + cursor: pointer; + transition: color 0.2s, border-color 0.2s; + text-decoration: none; +} +.btn-ghost:hover { + color: var(--text); + border-color: var(--border); +} + +/* ── Badge ── */ +.badge { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 6px 14px; + border-radius: 100px; + background: var(--accent-light); + color: var(--accent); + font-size: 12px; + font-weight: 600; + letter-spacing: 0.2px; + border: 1px solid rgba(37, 99, 235, 0.12); +} +.badge-dot { + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--accent); + animation: badge-pulse 2s infinite; +} +@keyframes badge-pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.3; } +} + +/* ── Tag / chip ── */ +.tag { + display: inline-flex; + align-items: center; + padding: 3px 10px; + border-radius: 100px; + font-size: 10px; + font-weight: 700; + letter-spacing: 0.5px; + text-transform: uppercase; + border: 1px solid transparent; +} +.tag-emerald { background: var(--emerald-light); color: var(--emerald); border-color: rgba(5, 150, 105, 0.12); } +.tag-blue { background: var(--accent-light); color: var(--accent); border-color: rgba(37, 99, 235, 0.12); } +.tag-violet { background: var(--violet-light); color: var(--violet); border-color: rgba(124, 58, 237, 0.12); } +.tag-amber { background: var(--amber-light); color: var(--amber); border-color: rgba(217, 119, 6, 0.12); } +.tag-rose { background: var(--rose-light); color: var(--rose); border-color: rgba(225, 29, 72, 0.12); } + +/* ── Code block ── */ +.code-block { + background: #f1f5f9; + border: 1px solid var(--border); + border-radius: var(--radius); + overflow: hidden; +} +.code-header { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 16px; + background: #e2e8f0; + font-size: 12px; + color: var(--text-secondary); + font-family: 'JetBrains Mono', 'Fira Code', monospace; +} +.code-dot { width: 9px; height: 9px; border-radius: 50%; } +.code-body { + padding: 16px; + font-family: 'JetBrains Mono', 'Fira Code', monospace; + font-size: 13px; + line-height: 1.6; + overflow-x: auto; + color: var(--text-secondary); +} + +/* ── Modern Centered Floating Nav ── */ +.nav-wrapper { + position: fixed; + top: 20px; + left: 0; + right: 0; + z-index: 100; + display: flex; + justify-content: center; + padding: 0 16px; + pointer-events: none; +} +.nav { + pointer-events: auto; + width: 100%; + max-width: 900px; + background: rgba(255, 255, 255, 0.78); + backdrop-filter: blur(20px) saturate(190%); + -webkit-backdrop-filter: blur(20px) saturate(190%); + border: 1px solid rgba(226, 232, 240, 0.85); + border-radius: 100px; + padding: 8px 12px 8px 22px; + box-shadow: 0 12px 35px -10px rgba(15, 23, 42, 0.08), 0 4px 12px rgba(0, 0, 0, 0.02); + transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1); +} +.nav-inner { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; +} +.logo { + display: flex; + align-items: center; + gap: 10px; + font-weight: 800; + font-size: 17px; + letter-spacing: -0.03em; + color: var(--text); + text-decoration: none; +} +.logo-mark { + width: 26px; + height: 26px; + border-radius: 7px; + background: linear-gradient(135deg, var(--accent), var(--emerald)); + display: flex; + align-items: center; + justify-content: center; + color: #fff; + font-weight: 900; + font-size: 13px; + box-shadow: 0 2px 8px rgba(37, 99, 235, 0.25); +} +.nav-links { + display: flex; + gap: 20px; + align-items: center; +} +.nav-links a { + color: var(--text-secondary); + text-decoration: none; + font-size: 13.5px; + font-weight: 600; + padding: 6px 14px; + border-radius: 100px; + transition: all 0.2s ease; +} +.nav-links a:hover { + color: var(--text); + background: rgba(15, 23, 42, 0.04); +} +.nav-action { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 18px; + background: var(--text); + color: #fff; + font-size: 13px; + font-weight: 600; + border-radius: 100px; + text-decoration: none; + transition: all 0.25s ease; + box-shadow: 0 4px 12px rgba(15, 23, 42, 0.15); +} +.nav-action:hover { + background: var(--accent); + transform: translateY(-1px); + box-shadow: 0 6px 16px rgba(37, 99, 235, 0.3); +} +@media (max-width: 768px) { + .nav-links { display: none; } + .nav-wrapper { top: 12px; } + .nav { padding: 8px 12px 8px 16px; } +} + +/* ── Section ── */ +.section { padding: 100px 24px; max-width: 1200px; margin: 0 auto; } +.section-label { + color: var(--accent); + font-size: 12px; + font-weight: 700; + letter-spacing: 1.5px; + text-transform: uppercase; + margin-bottom: 12px; +} +.section-title { + font-size: clamp(28px, 3.5vw, 42px); + font-weight: 800; + line-height: 1.2; + margin-bottom: 16px; + letter-spacing: -0.03em; +} +.section-sub { + color: var(--text-secondary); + font-size: 16px; + max-width: 620px; + line-height: 1.7; +} + +/* ── Stat card ── */ +.stat-card { text-align: center; padding: 24px 16px; } +.stat-value { + font-size: clamp(32px, 4vw, 48px); + font-weight: 800; + letter-spacing: -0.03em; + color: var(--text); +} +.stat-label { + color: var(--muted); + font-size: 12px; + margin-top: 4px; + text-transform: uppercase; + letter-spacing: 0.8px; +} + +/* ── Agent card ── */ +.agent-card { + padding: 24px; + border-radius: var(--radius-lg); + background: var(--surface); + border: 1px solid var(--border); + box-shadow: var(--shadow-sm); + transition: box-shadow var(--transition), border-color var(--transition); +} +.agent-card:hover { + box-shadow: var(--shadow-md); + border-color: #cbd5e1; +} + +/* ── Step number ── */ +.step-num { + width: 32px; + height: 32px; + border-radius: 8px; + background: var(--accent-light); + color: var(--accent); + display: flex; + align-items: center; + justify-content: center; + font-weight: 800; + font-size: 13px; + flex-shrink: 0; +} + +/* ── Quote table row ── */ +.quote-row { + transition: background 0.15s; +} +.quote-row:hover { + background: #f8fafc; +} + +/* ── Tool chip ── */ +.tool-chip { + padding: 5px 12px; + border-radius: 6px; + background: var(--surface); + border: 1px solid var(--border); + font-size: 12px; + font-family: 'JetBrains Mono', monospace; + color: var(--muted); + transition: border-color 0.15s, color 0.15s; +} +.tool-chip:hover { + border-color: var(--accent); + color: var(--text-secondary); +} + +/* ── Comparison table row ── */ +.compare-row { + display: flex; + justify-content: space-between; + align-items: center; + padding: 14px 20px; + border-radius: 8px; + font-size: 14px; +} diff --git a/landing/app/layout.tsx b/landing/app/layout.tsx new file mode 100644 index 0000000..0a5bb88 --- /dev/null +++ b/landing/app/layout.tsx @@ -0,0 +1,16 @@ +import type { Metadata } from 'next'; +import './globals.css'; + +export const metadata: Metadata = { + title: 'CircuLink — Manufacturing Waste-to-Revenue MCP Platform', + description: + 'Agentic AI multi-agent MCP platform for Industry 4.0. 6 autonomous agents turn factory waste into verified, matched, transacted revenue.', +}; + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} diff --git a/landing/app/not-found.tsx b/landing/app/not-found.tsx new file mode 100644 index 0000000..657798e --- /dev/null +++ b/landing/app/not-found.tsx @@ -0,0 +1,11 @@ +export default function NotFound() { + return ( +
+

Page Not Found

+

The page you are looking for does not exist.

+ + Return Home + +
+ ); +} diff --git a/landing/app/page.tsx b/landing/app/page.tsx new file mode 100644 index 0000000..4b77262 --- /dev/null +++ b/landing/app/page.tsx @@ -0,0 +1,875 @@ +'use client'; + +import { + Factory, + Recycle, + Brain, + Search, + GitMerge, + Truck, + TrendingUp, + ShieldCheck, + MapPin, + Phone, + ArrowRight, + Plug, + Terminal, + Workflow, + Zap, + Globe, + Gauge, + ChevronRight, + IndianRupee, + Quote, +} from 'lucide-react'; +import dynamic from 'next/dynamic'; +import { SpotlightCard } from '@/components/SpotlightCard'; +import { CountUp } from '@/components/CountUp'; +import { FadeIn } from '@/components/FadeIn'; +import { CodeBlock } from '@/components/CodeBlock'; + +const Iridescence = dynamic(() => import('@/components/Iridescence'), { ssr: false }); + +const MCP_URL = 'https://relink-6a64ea0e-aravindco-amrita-university-coimbatore.app.nitrocloud.ai'; + +export default function Home() { + return ( +
+
+ ); +} + +/* ── Nav ── */ +function Nav() { + return ( +
+ +
+ ); +} + +/* ── Hero ── */ +function Hero() { + return ( +
+ {/* Iridescence Canvas Background */} +
+ +
+ + {/* Radial overlay gradient for seamless text legibility without masking the animation */} +
+ +
+ +
+ “waste to revenue” +
+
+ + + + LIVE MCP SERVER — MANUFACTURING & INDUSTRY 4.0 + + + + +

+ One factory's waste. +
+ Another's raw material. +

+
+ + +

+ Relink is a platform of six autonomous AI agents that sense factory surplus, verify it + with vision AI, match buyers across industrial zones, and close the loop — turning + manufacturing waste into a revenue stream. +

+
+ + +
+ + Connect to MCP + + + Integration Guide + +
+
+ + +
+ {[ + { value: 4, suffix: 'B+', label: 'Tonnes waste per year' }, + { value: 15, suffix: '%', label: 'Currently reused' }, + { value: 500, suffix: 'B+', label: 'Market by 2030', prefix: '$' }, + { value: 22, suffix: '', label: 'MCP tools live' }, + ].map((s) => ( +
+
+ {s.prefix} + +
+
{s.label}
+
+ ))} +
+
+
+
+ ); +} + +/* ── Problem ── */ +function Problem() { + const rows = [ + { flow: 'Production scheduling', status: 'Optimized', ok: true }, + { flow: 'Machine health (IoT + predictive maintenance)', status: 'Optimized', ok: true }, + { flow: 'Quality control (computer vision)', status: 'Optimized', ok: true }, + { flow: 'Inbound supply chain', status: 'Optimized', ok: true }, + { flow: 'Waste and byproduct outflow', status: 'Pen, paper and a phone call', ok: false }, + ]; + + return ( +
+ +
The Industry 4.0 Blind Spot
+

+ Every manufacturing flow got digitized. +
+ Except the one coming out the back door. +

+

+ Factories run SCADA, MES, and predictive maintenance on every production line — then + sell 50 tonnes of monthly scrap to whichever dealer called first, at 30–40% below + fair value. That asymmetry costs Indian manufacturing an estimated{' '} + ₹25,000 crore every year. +

+
+ + +
+ {rows.map((r) => ( +
+ {r.flow} + + {r.status} + +
+ ))} +
+
+
+ ); +} + +/* ── Agents ── */ +function Agents() { + const agents = [ + { + icon: Factory, + name: 'Intake Agent', + type: 'Proactive', + desc: 'Polls ERP disposal queues, accepts photo uploads, and answers multilingual voice calls from MSME factories. Sellers set their own price.', + color: 'var(--emerald)', + tag: 'emerald', + }, + { + icon: ShieldCheck, + name: 'Verification Agent', + type: 'Autonomous', + desc: 'Gemini Vision grades material health (A/B/C), classifies downstream uses, benchmarks price, and scores seller trust without human intervention.', + color: 'var(--accent)', + tag: 'blue', + }, + { + icon: Search, + name: 'Sourcing Agent', + type: 'Conversational', + desc: 'Natural-language BOM decomposition plus cluster analysis that recommends the best industrial zone to source from based on density, price, and trust.', + color: 'var(--violet)', + tag: 'violet', + }, + { + icon: GitMerge, + name: 'Matching Agent', + type: 'Autonomous', + desc: 'Solves the many-to-many allocation problem — combines sellers to fill one buyer's BOM at minimum cost plus transport.', + color: 'var(--amber)', + tag: 'amber', + }, + { + icon: Truck, + name: 'Logistics Agent', + type: 'On-demand', + desc: 'Google Maps MCP integration for routes, freight estimates, and transporter matching when a buyer requests delivery assistance.', + color: 'var(--accent)', + tag: 'blue', + }, + { + icon: TrendingUp, + name: 'Prediction Agent', + type: 'Proactive', + desc: 'Reads production schedules and forecasts waste before it exists — then pre-notifies matched buyers via WhatsApp. Predictive supply.', + color: 'var(--rose)', + tag: 'rose', + }, + ]; + + return ( +
+ +
Agentic Architecture
+

+ Six autonomous agents. +
+ One event-driven MCP nervous system. +

+

+ Each agent is a standalone MCP server. They communicate through Supabase Realtime — + when the Intake Agent writes a listing, the Verification Agent wakes up automatically. No + polling. No orchestrator bottleneck. +

+
+ +
+ {agents.map((a, i) => ( + +
+
+
+ +
+
+
{a.name}
+ + {a.type} + +
+
+

+ {a.desc} +

+
+
+ ))} +
+
+ ); +} + +/* ── MCP Guide ── */ +function McpGuide() { + const curlList = `curl ${MCP_URL}/mcp \\ + -H "Content-Type: application/json" \\ + -d '{ + "jsonrpc": "2.0", + "id": 1, + "method": "tools/list" + }'`; + + const curlCall = `curl ${MCP_URL}/mcp \\ + -H "Content-Type: application/json" \\ + -d '{ + "jsonrpc": "2.0", + "id": 2, + "method": "tools/call", + "params": { + "name": "search_materials", + "arguments": { + "material_type": "aluminum_scrap", + "max_price_per_kg": 150, + "max_radius_km": 100 + } + } + }'`; + + const claudeConfig = `{ + "mcpServers": { + "relink": { + "url": "${MCP_URL}/mcp", + "transport": "http" + } + } +}`; + + const tools = [ + 'register_seller', 'create_listing_with_price', 'voice_intake_to_listing', + 'sync_erp_surplus', 'analyze_material_health', 'classify_material_usage', + 'suggest_fair_price', 'calculate_seller_trust_score', 'detect_listing_anomalies', + 'search_materials', 'recommend_best_place_to_source', 'get_seller_contact', + 'compare_listings', 'find_optimal_matches', 'rank_suppliers_by_multi_objective', + 'calculate_route', 'estimate_freight_cost', 'find_nearby_transporters', + 'schedule_pickup', 'forecast_waste_generation', 'get_compliance_report', + 'calculate_esg_impact', + ]; + + return ( +
+ +
MCP Integration Guide
+

+ Connect any MCP client in{' '} + 60 seconds. +

+

+ Relink speaks the Model Context Protocol over streamable HTTP. Claude, ChatGPT, + NitroStudio, or your own agent framework can discover and call all 22 tools with no SDK + lock-in. +

+
+ +
+ +
+
1
+ Discover the tools +
+ +
+ + +
+
2
+ Call any tool +
+ +
+ + +
+
3
+ Wire into your client +
+ +
+
+ + +
+

+ All 22 live tools +

+
+ {tools.map((t) => ( + + {t} + + ))} +
+
+
+
+ ); +} + +/* ── Use Cases ── */ +function UseCases() { + const cases = [ + { + icon: Factory, + title: 'Auto Stamping Plant, Pune', + tag: 'SELLER', + tagStyle: 'tag-emerald', + body: 'Uploads a photo of 500 kg Al-6061 stamping scrap. Gemini Vision grades it B, benchmarks at ₹148/kg. Manager quotes ₹140/kg. Listed in 40 seconds with a GST-verified trust badge.', + metric: '₹4,70,000 per year from disposal cost to revenue', + }, + { + icon: Search, + title: 'Die-Casting Unit, Chakan', + tag: 'BUYER', + tagStyle: 'tag-blue', + body: 'Types “need 500 kg aluminum scrap for die-casting, budget ₹145/kg”. Sourcing Agent recommends the Talegaon zone — 7 sellers, average ₹132/kg, Grade A. Buyer taps “Get Seller Contact” and calls directly.', + metric: '₹20,000 saved on a single 2-tonne order', + }, + { + icon: Phone, + title: 'Fabrication MSME, Coimbatore', + tag: 'VOICE', + tagStyle: 'tag-violet', + body: 'No ERP, no computer. Calls in, speaks in Tamil: “200 kg mild steel offcuts, want ₹28/kg.” The voice agent transcribes, structures, and lists it — the digital divide closed with a phone call.', + metric: '63% of Indian manufacturing output is MSME', + }, + { + icon: TrendingUp, + title: 'Prediction Engine, Every Factory', + tag: 'PROACTIVE', + tagStyle: 'tag-amber', + body: 'The production schedule shows a 10,000-panel batch run Thursday. The Prediction Agent knows that means roughly 500 kg scrap by Friday — and pre-notifies three matched buyers on WhatsApp before the waste exists.', + metric: 'Deals done before waste is generated', + }, + ]; + + return ( +
+ +
Real Scenarios
+

+ Built for the factory floor,{' '} + not the slide deck. +

+
+ +
+ {cases.map((c, i) => ( + + +
+
+
+ +
+ {c.tag} +
+

+ {c.title} +

+

+

+ + {c.metric} +
+
+
+
+ ))} +
+
+ ); +} + +/* ── Pricing / Quotes ── */ +function PricingQuotes() { + const quotes = [ + { material: 'Aluminum Scrap (6061)', grade: 'B', seller: 140, benchmark: '135-155', virgin: 230, trend: '+4%', up: true }, + { material: 'HDPE Regrind', grade: 'A', seller: 32, benchmark: '28-35', virgin: 85, trend: '+2%', up: true }, + { material: 'Steel Offcut (MS)', grade: 'B', seller: 28, benchmark: '26-34', virgin: 68, trend: '-1%', up: false }, + { material: 'Copper Wire Scrap', grade: 'B', seller: 620, benchmark: '580-680', virgin: 850, trend: '+6%', up: true }, + ]; + + return ( +
+ +
Live Benchmark Quotes
+

+ Sellers set the price.{' '} + AI keeps everyone honest. +

+

+ Every listing shows the seller's own quoted price next to the independent AI + benchmark — protecting uninformed sellers from underpricing and giving buyers a fair + reference point. +

+
+ + +
+
+ Material + Grade + Seller + AI Benchmark + Virgin + Trend +
+ {quotes.map((q) => ( +
+ {q.material} + + + {q.grade} + + + + {q.seller} + + + {q.benchmark} + + + {q.virgin} + + + {q.trend} + +
+ ))} +
+
+
+ ); +} + +/* ── Footer ── */ +function Footer() { + return ( + + ); +} diff --git a/landing/components/CodeBlock.tsx b/landing/components/CodeBlock.tsx new file mode 100644 index 0000000..e08e866 --- /dev/null +++ b/landing/components/CodeBlock.tsx @@ -0,0 +1,53 @@ +'use client'; + +import { useState } from 'react'; +import { Check, Copy } from 'lucide-react'; + +export function CodeBlock({ + code, + language, + filename, +}: { + code: string; + language: string; + filename: string; +}) { + const [copied, setCopied] = useState(false); + + const copy = async () => { + await navigator.clipboard.writeText(code); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + return ( +
+
+ + + + {filename} + +
+
+        {code}
+      
+
+ ); +} diff --git a/landing/components/CountUp.tsx b/landing/components/CountUp.tsx new file mode 100644 index 0000000..0b3b8a0 --- /dev/null +++ b/landing/components/CountUp.tsx @@ -0,0 +1,57 @@ +'use client'; + +import { useEffect, useRef, useState } from 'react'; + +export function CountUp({ + end, + duration = 2000, + suffix = '', + prefix = '', + decimals = 0, +}: { + end: number; + duration?: number; + suffix?: string; + prefix?: string; + decimals?: number; +}) { + const [count, setCount] = useState(0); + const [started, setStarted] = useState(false); + const ref = useRef(null); + + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting && !started) setStarted(true); + }, + { threshold: 0.3 } + ); + if (ref.current) observer.observe(ref.current); + return () => observer.disconnect(); + }, [started]); + + useEffect(() => { + if (!started) return; + let startTime: number; + let frame: number; + + const animate = (time: number) => { + if (!startTime) startTime = time; + const progress = Math.min((time - startTime) / duration, 1); + const eased = 1 - Math.pow(1 - progress, 3); + setCount(eased * end); + if (progress < 1) frame = requestAnimationFrame(animate); + }; + + frame = requestAnimationFrame(animate); + return () => cancelAnimationFrame(frame); + }, [started, end, duration]); + + return ( + + {prefix} + {count.toFixed(decimals)} + {suffix} + + ); +} diff --git a/landing/components/FadeIn.tsx b/landing/components/FadeIn.tsx new file mode 100644 index 0000000..703215e --- /dev/null +++ b/landing/components/FadeIn.tsx @@ -0,0 +1,35 @@ +'use client'; + +import { motion } from 'framer-motion'; +import type { ReactNode } from 'react'; + +export function FadeIn({ + children, + delay = 0, + direction = 'up', + className = '', +}: { + children: ReactNode; + delay?: number; + direction?: 'up' | 'down' | 'left' | 'right'; + className?: string; +}) { + const offsets = { + up: { y: 40 }, + down: { y: -40 }, + left: { x: 40 }, + right: { x: -40 }, + }; + + return ( + + {children} + + ); +} diff --git a/landing/components/Iridescence.tsx b/landing/components/Iridescence.tsx new file mode 100644 index 0000000..fa0edda --- /dev/null +++ b/landing/components/Iridescence.tsx @@ -0,0 +1,165 @@ +'use client'; + +import { Renderer, Program, Mesh, Color, Triangle } from 'ogl'; +import { useEffect, useRef, ComponentPropsWithoutRef } from 'react'; + +interface IridescenceProps extends Omit, 'color'> { + color?: [number, number, number] | number[]; + speed?: number; + amplitude?: number; + mouseReact?: boolean; +} + +const vertexShader = ` +attribute vec2 uv; +attribute vec2 position; + +varying vec2 vUv; + +void main() { + vUv = uv; + gl_Position = vec4(position, 0, 1); +} +`; + +const fragmentShader = ` +precision highp float; + +uniform float uTime; +uniform vec3 uColor; +uniform vec3 uResolution; +uniform vec2 uMouse; +uniform float uAmplitude; +uniform float uSpeed; + +varying vec2 vUv; + +void main() { + float mr = min(uResolution.x, uResolution.y); + vec2 uv = (vUv.xy * 2.0 - 1.0) * uResolution.xy / mr; + + uv += (uMouse - vec2(0.5)) * uAmplitude; + + float d = -uTime * 0.5 * uSpeed; + float a = 0.0; + for (float i = 0.0; i < 8.0; ++i) { + a += cos(i - d - a * uv.x); + d += sin(uv.y * i + a); + } + d += uTime * 0.5 * uSpeed; + vec3 col = vec3(cos(uv * vec2(d, a)) * 0.6 + 0.4, cos(a + d) * 0.5 + 0.5); + col = cos(col * cos(vec3(d, a, 2.5)) * 0.5 + 0.5) * uColor; + gl_FragColor = vec4(col, 1.0); +} +`; + +export default function Iridescence({ + color = [1, 1, 1], + speed = 1.0, + amplitude = 0.1, + mouseReact = true, + className = '', + style, + ...rest +}: IridescenceProps) { + const ctnDom = useRef(null); + const mousePos = useRef({ x: 0.5, y: 0.5 }); + + useEffect(() => { + if (!ctnDom.current) return; + const ctn = ctnDom.current; + const renderer = new Renderer({ alpha: true, antialias: true }); + const gl = renderer.gl; + + let program: Program; + + function resize() { + if (!ctn) return; + const width = ctn.offsetWidth || ctn.clientWidth || window.innerWidth; + const height = ctn.offsetHeight || ctn.clientHeight || window.innerHeight; + renderer.setSize(width, height); + if (program) { + program.uniforms.uResolution.value = new Color( + gl.canvas.width, + gl.canvas.height, + gl.canvas.width / (gl.canvas.height || 1) + ); + } + } + window.addEventListener('resize', resize, false); + + const resizeObserver = new ResizeObserver(() => resize()); + resizeObserver.observe(ctn); + + resize(); + + const geometry = new Triangle(gl); + program = new Program(gl, { + vertex: vertexShader, + fragment: fragmentShader, + uniforms: { + uTime: { value: 0 }, + uColor: { value: new Color(...color) }, + uResolution: { + value: new Color(gl.canvas.width, gl.canvas.height, gl.canvas.width / (gl.canvas.height || 1)) + }, + uMouse: { value: new Float32Array([mousePos.current.x, mousePos.current.y]) }, + uAmplitude: { value: amplitude }, + uSpeed: { value: speed } + } + }); + + const mesh = new Mesh(gl, { geometry, program }); + let animateId: number; + + function update(t: number) { + animateId = requestAnimationFrame(update); + program.uniforms.uTime.value = t * 0.001; + renderer.render({ scene: mesh }); + } + animateId = requestAnimationFrame(update); + + gl.canvas.style.display = 'block'; + gl.canvas.style.width = '100%'; + gl.canvas.style.height = '100%'; + gl.canvas.style.position = 'absolute'; + gl.canvas.style.inset = '0'; + ctn.appendChild(gl.canvas); + + function handleMouseMove(e: MouseEvent) { + const rect = ctn.getBoundingClientRect(); + const x = (e.clientX - rect.left) / rect.width; + const y = 1.0 - (e.clientY - rect.top) / rect.height; + mousePos.current = { x, y }; + program.uniforms.uMouse.value[0] = x; + program.uniforms.uMouse.value[1] = y; + } + + const targetEl = mouseReact ? window : null; + if (targetEl) { + targetEl.addEventListener('mousemove', handleMouseMove); + } + + return () => { + cancelAnimationFrame(animateId); + window.removeEventListener('resize', resize); + resizeObserver.disconnect(); + if (targetEl) { + targetEl.removeEventListener('mousemove', handleMouseMove); + } + if (ctn.contains(gl.canvas)) { + ctn.removeChild(gl.canvas); + } + gl.getExtension('WEBGL_lose_context')?.loseContext(); + }; + }, [color, speed, amplitude, mouseReact]); + + return ( +
+ ); +} diff --git a/landing/components/SpotlightCard.tsx b/landing/components/SpotlightCard.tsx new file mode 100644 index 0000000..bf3145c --- /dev/null +++ b/landing/components/SpotlightCard.tsx @@ -0,0 +1,28 @@ +'use client'; + +import { useRef, useState, type ReactNode, type MouseEvent } from 'react'; + +export function SpotlightCard({ children, className = '' }: { children: ReactNode; className?: string }) { + const ref = useRef(null); + const [position, setPosition] = useState({ x: 50, y: 50 }); + + const handleMouseMove = (e: MouseEvent) => { + if (!ref.current) return; + const rect = ref.current.getBoundingClientRect(); + setPosition({ + x: ((e.clientX - rect.left) / rect.width) * 100, + y: ((e.clientY - rect.top) / rect.height) * 100, + }); + }; + + return ( +
+ {children} +
+ ); +} diff --git a/landing/next-env.d.ts b/landing/next-env.d.ts new file mode 100644 index 0000000..830fb59 --- /dev/null +++ b/landing/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/landing/next.config.js b/landing/next.config.js new file mode 100644 index 0000000..ff815fd --- /dev/null +++ b/landing/next.config.js @@ -0,0 +1,12 @@ +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +/** @type {import('next').NextConfig} */ +const nextConfig = { + images: { unoptimized: true }, +}; + +export default nextConfig; diff --git a/landing/package-lock.json b/landing/package-lock.json new file mode 100644 index 0000000..7944614 --- /dev/null +++ b/landing/package-lock.json @@ -0,0 +1,1023 @@ +{ + "name": "circulink-landing", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "circulink-landing", + "version": "1.0.0", + "dependencies": { + "framer-motion": "^11.0.0", + "lucide-react": "^0.460.0", + "next": "^15.0.0", + "ogl": "^1.0.11", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "typescript": "^5.7.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@next/env": { + "version": "15.5.22", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.22.tgz", + "integrity": "sha512-O5BlKb3KtsHkvO0gjjV66PuJnAgCtIEIzwkt50HRAHsQkU1t77eksIXSZV84/WMtZJjWrnDUPKHVRi0D62nSAA==", + "license": "MIT" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "15.5.22", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.22.tgz", + "integrity": "sha512-/VISwtffSg8+fVvBbXdglsvruCsdbBC4dG25iU6xascKVqfQKsj/OtjGnOEkIS7pX5GB9e9/r5QprpicsGL3gw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "15.5.22", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.22.tgz", + "integrity": "sha512-NiA9ve8hbiuhG/Q17a2mZDRVxMTtg3rTOgjLnDaLlE+AEPAQlkkuKrfePEbeOrgYmX0U2KGX4EVEn09hXU5GlQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.5.22", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.22.tgz", + "integrity": "sha512-vAPa9vltW+UW/KWtjXeSUFgV3wb1x9d/BeyC6WFI6eBpL0D2f70oGwtOp6193mNW3qusrpgBzMQferPf+Zh8Dw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "15.5.22", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.22.tgz", + "integrity": "sha512-iknK80pWlNDnkdSr13bd8mMuG3Z2oTxODwsZHvuMY7caMk77+rBLdHVWsy8v2EVa3ZojJ/+wJX5fnq8va6Gv8A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "15.5.22", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.22.tgz", + "integrity": "sha512-penuEdkwU2OOAiS+n4LE8T/VIoCfAI01QcLZTJ2xc3+l4Q22L/DzURocmI2LU1b+8BMQoLAP1Sze3uYAZT05Bg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "15.5.22", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.22.tgz", + "integrity": "sha512-ZM0BKJm3FZ+guG6WT6PcyOLtp6paZ5tngcJC/uUKvLW4Y0TQnnVi1+UGdo8Q6Yxp5gaS82pmC1rD/oFlhkWB3g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.5.22", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.22.tgz", + "integrity": "sha512-rY/YaumrZaS0//94BnHLF5VSRp0GFUO4GvXNuoCBb0cGSci96yO+p1JaNL2aq9YZAYv9cuZRziV02x5IQH/wjg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "15.5.22", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.22.tgz", + "integrity": "sha512-s5IA4cyrbR2XK/5NWcu5dp8CfPBiKME+UhvNperia7uQybEgg5+LIhGMiY37WQE4rcI4owsDcU4IVUjLoTuDkA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@types/node": { + "version": "22.20.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", + "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", + "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "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-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/framer-motion": { + "version": "11.18.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz", + "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==", + "license": "MIT", + "dependencies": { + "motion-dom": "^11.18.1", + "motion-utils": "^11.18.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/lucide-react": { + "version": "0.460.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.460.0.tgz", + "integrity": "sha512-BVtq/DykVeIvRTJvRAgCsOwaGL8Un3Bxh8MbDxMhEWlZay3T4IpEKDEpwt5KZ0KJMHzgm6jrltxlT5eXOWXDHg==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + } + }, + "node_modules/motion-dom": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz", + "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==", + "license": "MIT", + "dependencies": { + "motion-utils": "^11.18.1" + } + }, + "node_modules/motion-utils": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz", + "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/next": { + "version": "15.5.22", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.22.tgz", + "integrity": "sha512-mrtal1sRxO4YrlDS98sDuIvGZivKbFix8w7oAL9ZynfOgc3cADQOQgvwtMooc18Qr8bKzvQAcHwHZ0mbJ7zcfQ==", + "license": "MIT", + "dependencies": { + "@next/env": "15.5.22", + "@swc/helpers": "0.5.15", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "15.5.22", + "@next/swc-darwin-x64": "15.5.22", + "@next/swc-linux-arm64-gnu": "15.5.22", + "@next/swc-linux-arm64-musl": "15.5.22", + "@next/swc-linux-x64-gnu": "15.5.22", + "@next/swc-linux-x64-musl": "15.5.22", + "@next/swc-win32-arm64-msvc": "15.5.22", + "@next/swc-win32-x64-msvc": "15.5.22", + "sharp": "^0.34.3" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/ogl": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ogl/-/ogl-1.0.11.tgz", + "integrity": "sha512-kUpC154AFfxi16pmZUK4jk3J+8zxwTWGPo03EoYA8QPbzikHoaC82n6pNTbd+oEaJonaE8aPWBlX7ad9zrqLsA==", + "license": "Unlicense" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.8" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "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/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/landing/package.json b/landing/package.json new file mode 100644 index 0000000..6caa133 --- /dev/null +++ b/landing/package.json @@ -0,0 +1,25 @@ +{ + "name": "circulink-landing", + "version": "1.0.0", + "type": "module", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "framer-motion": "^11.0.0", + "lucide-react": "^0.460.0", + "next": "^15.0.0", + "ogl": "^1.0.11", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "typescript": "^5.7.0" + } +} diff --git a/landing/tsconfig.json b/landing/tsconfig.json new file mode 100644 index 0000000..f5fc2f7 --- /dev/null +++ b/landing/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2020", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [{ "name": "next" }], + "paths": { "@/*": ["./*"] } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +}