From 68361080654fc25b3ba3316ea32e2fcbc1731ad9 Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Mon, 9 Mar 2026 19:00:49 -0700 Subject: [PATCH] Initial commit: FlowFi website - flowfi.network 7-section narrative journey with interactive Sankey diagrams, flow sandbox (@xyflow/react), and animated pipe visualizations. Co-Authored-By: Claude Opus 4.6 --- .dockerignore | 8 + .gitignore | 31 + Dockerfile | 11 + app/globals.css | 172 +++ app/layout.tsx | 36 + app/page.tsx | 21 + components/sandbox/FlowCanvas.tsx | 124 ++ components/sandbox/FlowToolbar.tsx | 77 + components/sandbox/edges/AnimatedPipeEdge.tsx | 51 + components/sandbox/nodes/PipeNode.tsx | 16 + components/sandbox/nodes/SinkNode.tsx | 24 + components/sandbox/nodes/SourceNode.tsx | 20 + components/sandbox/presets.ts | 62 + components/sections/CTASection.tsx | 53 + components/sections/ExtractivePipeSection.tsx | 39 + components/sections/HeroSection.tsx | 81 ++ components/sections/NatureFlowsSection.tsx | 37 + components/sections/RegenerativeSection.tsx | 39 + components/sections/SandboxSection.tsx | 42 + components/sections/TransitionSection.tsx | 29 + components/ui/ScrollReveal.tsx | 27 + components/ui/SectionHeader.tsx | 19 + components/visualizations/AnimatedPipe.tsx | 47 + components/visualizations/FlowParticles.tsx | 35 + components/visualizations/SankeyDiagram.tsx | 171 +++ components/visualizations/WaterDistortion.tsx | 38 + docker-compose.yml | 34 + lib/sankey-data.ts | 94 ++ lib/types.ts | 25 + next.config.mjs | 13 + package.json | 31 + pnpm-lock.yaml | 1271 +++++++++++++++++ postcss.config.mjs | 8 + tsconfig.json | 41 + 34 files changed, 2827 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 app/globals.css create mode 100644 app/layout.tsx create mode 100644 app/page.tsx create mode 100644 components/sandbox/FlowCanvas.tsx create mode 100644 components/sandbox/FlowToolbar.tsx create mode 100644 components/sandbox/edges/AnimatedPipeEdge.tsx create mode 100644 components/sandbox/nodes/PipeNode.tsx create mode 100644 components/sandbox/nodes/SinkNode.tsx create mode 100644 components/sandbox/nodes/SourceNode.tsx create mode 100644 components/sandbox/presets.ts create mode 100644 components/sections/CTASection.tsx create mode 100644 components/sections/ExtractivePipeSection.tsx create mode 100644 components/sections/HeroSection.tsx create mode 100644 components/sections/NatureFlowsSection.tsx create mode 100644 components/sections/RegenerativeSection.tsx create mode 100644 components/sections/SandboxSection.tsx create mode 100644 components/sections/TransitionSection.tsx create mode 100644 components/ui/ScrollReveal.tsx create mode 100644 components/ui/SectionHeader.tsx create mode 100644 components/visualizations/AnimatedPipe.tsx create mode 100644 components/visualizations/FlowParticles.tsx create mode 100644 components/visualizations/SankeyDiagram.tsx create mode 100644 components/visualizations/WaterDistortion.tsx create mode 100644 docker-compose.yml create mode 100644 lib/sankey-data.ts create mode 100644 lib/types.ts create mode 100644 next.config.mjs create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 postcss.config.mjs create mode 100644 tsconfig.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..4b8e4c7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +node_modules +.next +out +.git +.gitignore +*.md +.env* +.DS_Store diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..10828d8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# env files +.env*.local +.env + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2925e60 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM node:20-alpine AS builder +WORKDIR /app +RUN corepack enable && corepack prepare pnpm@latest --activate +COPY package.json pnpm-lock.yaml ./ +RUN pnpm install --frozen-lockfile +COPY . . +RUN pnpm build + +FROM nginx:alpine +COPY --from=builder /app/out /usr/share/nginx/html +EXPOSE 80 diff --git a/app/globals.css b/app/globals.css new file mode 100644 index 0000000..6edeb17 --- /dev/null +++ b/app/globals.css @@ -0,0 +1,172 @@ +@import "tailwindcss"; + +@theme { + --color-void: #0a0a0a; + --color-nothing: #1a1a2e; + --color-less: #16213e; + --color-anti-green: #e94560; + --color-flow-green: #2dd4bf; + --color-zen: #f5f5dc; + --color-sloth: #c3b091; + + --font-family-mono: "Courier New", monospace; + --font-family-marker: var(--font-marker), "Permanent Marker", cursive; + --font-family-caveat: var(--font-caveat), "Caveat", cursive; +} + +/* Pipe flow animation — core effect */ +@keyframes pipe-flow { + 0% { stroke-dashoffset: 24; } + 100% { stroke-dashoffset: 0; } +} + +@keyframes pipe-flow-fast { + 0% { stroke-dashoffset: 24; } + 100% { stroke-dashoffset: 0; } +} + +@keyframes pipe-flow-reverse { + 0% { stroke-dashoffset: 0; } + 100% { stroke-dashoffset: 24; } +} + +.animate-pipe-flow { + stroke-dasharray: 8 4; + animation: pipe-flow 1.5s linear infinite; +} + +.animate-pipe-flow-fast { + stroke-dasharray: 8 4; + animation: pipe-flow 0.8s linear infinite; +} + +.animate-pipe-flow-slow { + stroke-dasharray: 8 4; + animation: pipe-flow 2.5s linear infinite; +} + +/* Particle travel along path */ +@keyframes travel-path { + 0% { offset-distance: 0%; opacity: 0; } + 5% { opacity: 1; } + 95% { opacity: 1; } + 100% { offset-distance: 100%; opacity: 0; } +} + +.animate-particle { + offset-rotate: 0deg; + animation: travel-path var(--duration, 3s) linear infinite; + animation-delay: var(--delay, 0s); +} + +/* Water distortion */ +@keyframes turbulence { + 0% { } + 50% { } + 100% { } +} + +/* Scroll reveal */ +@keyframes reveal-up { + from { opacity: 0; transform: translateY(30px); } + to { opacity: 1; transform: translateY(0); } +} + +/* Breathing / pulsing */ +@keyframes breathe { + 0%, 100% { transform: scale(1); } + 50% { transform: scale(1.02); } +} + +@keyframes slow-pulse { + 0%, 100% { opacity: 0.3; } + 50% { opacity: 0.6; } +} + +@keyframes drift { + 0%, 100% { transform: translateX(0); } + 50% { transform: translateX(20px); } +} + +@keyframes flow-gradient { + 0% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + 100% { background-position: 0% 50%; } +} + +@keyframes float-up { + 0% { opacity: 0.6; transform: translateY(0) rotate(0deg); } + 100% { opacity: 0; transform: translateY(-200px) rotate(10deg); } +} + +.animate-breathe { animation: breathe 6s ease-in-out infinite; } +.animate-slow-pulse { animation: slow-pulse 8s ease-in-out infinite; } +.animate-drift { animation: drift 12s ease-in-out infinite; } +.animate-flow-gradient { + background-size: 200% 200%; + animation: flow-gradient 8s ease-in-out infinite; +} + +/* Wobble rotations */ +.wobble-1 { transform: rotate(-1.5deg); } +.wobble-2 { transform: rotate(0.8deg); } +.wobble-3 { transform: rotate(-0.5deg); } +.wobble-4 { transform: rotate(1.2deg); } + +/* Scrawl underlines */ +.scrawl-underline { + position: relative; + display: inline-block; +} +.scrawl-underline::after { + content: ''; + position: absolute; + left: -4%; + bottom: -4px; + width: 108%; + height: 4px; + background: var(--color-flow-green); + transform: rotate(-0.5deg) scaleX(0.95); + border-radius: 2px; +} + +/* Sandbox node styles */ +.flow-node { + border: 2px solid rgba(245, 245, 220, 0.2); + border-radius: 8px; + padding: 12px; + background: rgba(26, 26, 46, 0.9); + backdrop-filter: blur(4px); + min-width: 120px; +} + +.flow-node:hover { + border-color: var(--color-flow-green); +} + +/* Disable feTurbulence on mobile for performance */ +@media (max-width: 768px) { + .water-distortion { + filter: none !important; + } +} + +/* React Flow overrides */ +.react-flow__background { + background-color: var(--color-void) !important; +} + +.react-flow__controls button { + background-color: var(--color-nothing) !important; + border-color: rgba(245, 245, 220, 0.1) !important; + color: var(--color-zen) !important; + fill: var(--color-zen) !important; +} + +.react-flow__controls button:hover { + background-color: var(--color-less) !important; +} + +.react-flow__minimap { + background-color: var(--color-void) !important; +} diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 0000000..3f0c457 --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,36 @@ +import type { Metadata } from 'next' +import { Permanent_Marker, Caveat } from 'next/font/google' +import './globals.css' + +const marker = Permanent_Marker({ + weight: '400', + subsets: ['latin'], + variable: '--font-marker', +}) + +const caveat = Caveat({ + subsets: ['latin'], + variable: '--font-caveat', +}) + +export const metadata: Metadata = { + title: 'FlowFi — Everything Flows', + description: 'Finance as metabolism, not mechanism. Pre-distributive. Re-distributive. Naturally.', + openGraph: { + title: 'FlowFi — Everything Flows', + description: 'Finance as metabolism, not mechanism. Pre-distributive. Re-distributive. Naturally.', + url: 'https://flowfi.network', + siteName: 'FlowFi', + type: 'website', + }, +} + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + + {children} + + + ) +} diff --git a/app/page.tsx b/app/page.tsx new file mode 100644 index 0000000..296344a --- /dev/null +++ b/app/page.tsx @@ -0,0 +1,21 @@ +import HeroSection from '@/components/sections/HeroSection' +import NatureFlowsSection from '@/components/sections/NatureFlowsSection' +import ExtractivePipeSection from '@/components/sections/ExtractivePipeSection' +import RegenerativeSection from '@/components/sections/RegenerativeSection' +import TransitionSection from '@/components/sections/TransitionSection' +import SandboxSection from '@/components/sections/SandboxSection' +import CTASection from '@/components/sections/CTASection' + +export default function Home() { + return ( +
+ + + + + + + +
+ ) +} diff --git a/components/sandbox/FlowCanvas.tsx b/components/sandbox/FlowCanvas.tsx new file mode 100644 index 0000000..e0d9cd8 --- /dev/null +++ b/components/sandbox/FlowCanvas.tsx @@ -0,0 +1,124 @@ +'use client' + +import { useCallback, useState, useMemo } from 'react' +import { + ReactFlow, + Controls, + MiniMap, + Background, + useNodesState, + useEdgesState, + addEdge, + type Connection, + type Node, + BackgroundVariant, +} from '@xyflow/react' +import '@xyflow/react/dist/style.css' + +import SourceNode from './nodes/SourceNode' +import PipeNode from './nodes/PipeNode' +import SinkNode from './nodes/SinkNode' +import AnimatedPipeEdge from './edges/AnimatedPipeEdge' +import FlowToolbar from './FlowToolbar' +import { presets } from './presets' + +const nodeTypes = { + source: SourceNode, + pipe: PipeNode, + sink: SinkNode, +} + +const edgeTypes = { + animatedPipe: AnimatedPipeEdge, +} + +export default function FlowCanvas() { + const defaultPreset = typeof window !== 'undefined' && window.innerWidth < 768 ? 0 : 0 + const [nodes, setNodes, onNodesChange] = useNodesState(presets[defaultPreset].nodes) + const [edges, setEdges, onEdgesChange] = useEdgesState(presets[defaultPreset].edges) + const [isPlaying, setIsPlaying] = useState(true) + const [nodeIdCounter, setNodeIdCounter] = useState(100) + + const onConnect = useCallback( + (connection: Connection) => { + setEdges((eds) => + addEdge( + { ...connection, type: 'animatedPipe', data: { flowRate: 3 } }, + eds + ) + ) + }, + [setEdges] + ) + + const handleAddNode = useCallback( + (type: 'source' | 'pipe' | 'sink') => { + const id = `n${nodeIdCounter}` + setNodeIdCounter((c) => c + 1) + + const labels = { source: 'Source', pipe: 'Pipe', sink: 'Sink' } + const newNode: Node = { + id, + type, + position: { x: 200 + Math.random() * 200, y: 100 + Math.random() * 200 }, + data: { + label: labels[type], + ...(type === 'source' ? { flowRate: 5 } : {}), + ...(type === 'sink' ? { fillLevel: 0 } : {}), + }, + } + setNodes((nds) => [...nds, newNode]) + }, + [nodeIdCounter, setNodes] + ) + + const handleLoadPreset = useCallback( + (index: number) => { + const preset = presets[index] + setNodes(preset.nodes) + setEdges(preset.edges) + }, + [setNodes, setEdges] + ) + + const handleReset = useCallback(() => { + setNodes(presets[2].nodes) // Blank preset + setEdges(presets[2].edges) + }, [setNodes, setEdges]) + + const showMiniMap = typeof window !== 'undefined' && window.innerWidth >= 768 + + return ( +
+ setIsPlaying(!isPlaying)} + /> + + + + {showMiniMap && ( + + )} + +
+ ) +} diff --git a/components/sandbox/FlowToolbar.tsx b/components/sandbox/FlowToolbar.tsx new file mode 100644 index 0000000..87b241a --- /dev/null +++ b/components/sandbox/FlowToolbar.tsx @@ -0,0 +1,77 @@ +'use client' + +import { presets } from './presets' + +interface FlowToolbarProps { + onAddNode: (type: 'source' | 'pipe' | 'sink') => void + onLoadPreset: (index: number) => void + onReset: () => void + isPlaying: boolean + onTogglePlay: () => void +} + +export default function FlowToolbar({ + onAddNode, + onLoadPreset, + onReset, + isPlaying, + onTogglePlay, +}: FlowToolbarProps) { + return ( +
+ {/* Add nodes */} +
+ + + +
+ + {/* Presets */} +
+ {presets.map((preset, i) => ( + + ))} +
+ + {/* Controls */} +
+ + +
+
+ ) +} diff --git a/components/sandbox/edges/AnimatedPipeEdge.tsx b/components/sandbox/edges/AnimatedPipeEdge.tsx new file mode 100644 index 0000000..381133f --- /dev/null +++ b/components/sandbox/edges/AnimatedPipeEdge.tsx @@ -0,0 +1,51 @@ +'use client' + +import { BaseEdge, getBezierPath, type EdgeProps } from '@xyflow/react' + +export default function AnimatedPipeEdge({ + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, + data, + style, +}: EdgeProps) { + const [edgePath] = getBezierPath({ + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, + }) + + const flowRate = (data as any)?.flowRate ?? 3 + const pipeWidth = Math.max(2, Math.min(12, flowRate * 2)) + + return ( + <> + {/* Outer wall */} + + {/* Inner flow with animation */} + + + ) +} diff --git a/components/sandbox/nodes/PipeNode.tsx b/components/sandbox/nodes/PipeNode.tsx new file mode 100644 index 0000000..44ac16e --- /dev/null +++ b/components/sandbox/nodes/PipeNode.tsx @@ -0,0 +1,16 @@ +'use client' + +import { Handle, Position, type NodeProps } from '@xyflow/react' + +export default function PipeNode({ data }: NodeProps) { + return ( +
+ + +
+
+
{(data as any).label || 'Pipe'}
+
+
+ ) +} diff --git a/components/sandbox/nodes/SinkNode.tsx b/components/sandbox/nodes/SinkNode.tsx new file mode 100644 index 0000000..1bfa421 --- /dev/null +++ b/components/sandbox/nodes/SinkNode.tsx @@ -0,0 +1,24 @@ +'use client' + +import { Handle, Position, type NodeProps } from '@xyflow/react' + +export default function SinkNode({ data }: NodeProps) { + const fillLevel = (data as any).fillLevel ?? 0 + + return ( +
+ +
+
+
{(data as any).label || 'Sink'}
+ {/* Fill level indicator */} +
+
+
+
+
+ ) +} diff --git a/components/sandbox/nodes/SourceNode.tsx b/components/sandbox/nodes/SourceNode.tsx new file mode 100644 index 0000000..f567598 --- /dev/null +++ b/components/sandbox/nodes/SourceNode.tsx @@ -0,0 +1,20 @@ +'use client' + +import { Handle, Position, type NodeProps } from '@xyflow/react' + +export default function SourceNode({ data }: NodeProps) { + return ( +
+ +
+
+
{(data as any).label || 'Source'}
+ {(data as any).flowRate !== undefined && ( +
+ {(data as any).flowRate} units/s +
+ )} +
+
+ ) +} diff --git a/components/sandbox/presets.ts b/components/sandbox/presets.ts new file mode 100644 index 0000000..b52d6d6 --- /dev/null +++ b/components/sandbox/presets.ts @@ -0,0 +1,62 @@ +import type { Node, Edge } from '@xyflow/react' + +export interface FlowPreset { + name: string + nodes: Node[] + edges: Edge[] +} + +export const extractivePreset: FlowPreset = { + name: 'Extractive', + nodes: [ + { id: 's1', type: 'source', position: { x: 50, y: 50 }, data: { label: 'Labor', flowRate: 5 } }, + { id: 's2', type: 'source', position: { x: 50, y: 180 }, data: { label: 'Nature', flowRate: 8 } }, + { id: 's3', type: 'source', position: { x: 50, y: 310 }, data: { label: 'Creativity', flowRate: 4 } }, + { id: 'p1', type: 'pipe', position: { x: 300, y: 160 }, data: { label: 'Finance' } }, + { id: 'k1', type: 'sink', position: { x: 550, y: 80 }, data: { label: 'Shareholders', fillLevel: 85 } }, + { id: 'k2', type: 'sink', position: { x: 550, y: 230 }, data: { label: 'Executives', fillLevel: 70 } }, + { id: 'k3', type: 'sink', position: { x: 550, y: 370 }, data: { label: 'Public Good', fillLevel: 10 } }, + ], + edges: [ + { id: 'e1', source: 's1', target: 'p1', type: 'animatedPipe', data: { flowRate: 5 } }, + { id: 'e2', source: 's2', target: 'p1', type: 'animatedPipe', data: { flowRate: 8 } }, + { id: 'e3', source: 's3', target: 'p1', type: 'animatedPipe', data: { flowRate: 4 } }, + { id: 'e4', source: 'p1', target: 'k1', type: 'animatedPipe', data: { flowRate: 8 } }, + { id: 'e5', source: 'p1', target: 'k2', type: 'animatedPipe', data: { flowRate: 6 } }, + { id: 'e6', source: 'p1', target: 'k3', type: 'animatedPipe', data: { flowRate: 1 } }, + ], +} + +export const regenerativePreset: FlowPreset = { + name: 'Regenerative', + nodes: [ + { id: 's1', type: 'source', position: { x: 50, y: 100 }, data: { label: 'Commons', flowRate: 6 } }, + { id: 's2', type: 'source', position: { x: 50, y: 280 }, data: { label: 'Ecology', flowRate: 5 } }, + { id: 'p1', type: 'pipe', position: { x: 280, y: 60 }, data: { label: 'Community' } }, + { id: 'p2', type: 'pipe', position: { x: 280, y: 200 }, data: { label: 'Care' } }, + { id: 'p3', type: 'pipe', position: { x: 280, y: 340 }, data: { label: 'Knowledge' } }, + { id: 'k1', type: 'sink', position: { x: 520, y: 100 }, data: { label: 'Stewardship', fillLevel: 60 } }, + { id: 'k2', type: 'sink', position: { x: 520, y: 280 }, data: { label: 'Regeneration', fillLevel: 55 } }, + ], + edges: [ + { id: 'e1', source: 's1', target: 'p1', type: 'animatedPipe', data: { flowRate: 3 } }, + { id: 'e2', source: 's1', target: 'p2', type: 'animatedPipe', data: { flowRate: 3 } }, + { id: 'e3', source: 's2', target: 'p2', type: 'animatedPipe', data: { flowRate: 3 } }, + { id: 'e4', source: 's2', target: 'p3', type: 'animatedPipe', data: { flowRate: 3 } }, + { id: 'e5', source: 'p1', target: 'k1', type: 'animatedPipe', data: { flowRate: 3 } }, + { id: 'e6', source: 'p2', target: 'k1', type: 'animatedPipe', data: { flowRate: 3 } }, + { id: 'e7', source: 'p2', target: 'k2', type: 'animatedPipe', data: { flowRate: 3 } }, + { id: 'e8', source: 'p3', target: 'k2', type: 'animatedPipe', data: { flowRate: 3 } }, + ], +} + +export const blankPreset: FlowPreset = { + name: 'Blank', + nodes: [ + { id: 's1', type: 'source', position: { x: 100, y: 200 }, data: { label: 'Source', flowRate: 5 } }, + { id: 'k1', type: 'sink', position: { x: 500, y: 200 }, data: { label: 'Sink', fillLevel: 0 } }, + ], + edges: [], +} + +export const presets = [regenerativePreset, extractivePreset, blankPreset] diff --git a/components/sections/CTASection.tsx b/components/sections/CTASection.tsx new file mode 100644 index 0000000..b117caf --- /dev/null +++ b/components/sections/CTASection.tsx @@ -0,0 +1,53 @@ +'use client' + +import ScrollReveal from '@/components/ui/ScrollReveal' + +export default function CTASection() { + return ( +
+
+ +
+ +

+ Ready to flow? +

+
+ + +

+ You already are. +

+
+ + +

+ NoFi → FlowFi → ??? +

+
+ + + + ← back to NoFi + + +
+ + {/* Footer */} + +
+ ) +} diff --git a/components/sections/ExtractivePipeSection.tsx b/components/sections/ExtractivePipeSection.tsx new file mode 100644 index 0000000..3ccb306 --- /dev/null +++ b/components/sections/ExtractivePipeSection.tsx @@ -0,0 +1,39 @@ +'use client' + +import SankeyDiagram from '@/components/visualizations/SankeyDiagram' +import SectionHeader from '@/components/ui/SectionHeader' +import ScrollReveal from '@/components/ui/ScrollReveal' +import { extractiveFlows } from '@/lib/sankey-data' + +export default function ExtractivePipeSection() { + return ( +
+
+ {/* Red tint overlay */} +
+ +
+ The Extractive Pattern + + +
+ +
+
+ + +
+ The plumbing hired a marketing team and went public. +
+
+
+
+ ) +} diff --git a/components/sections/HeroSection.tsx b/components/sections/HeroSection.tsx new file mode 100644 index 0000000..fe65b16 --- /dev/null +++ b/components/sections/HeroSection.tsx @@ -0,0 +1,81 @@ +'use client' + +export default function HeroSection() { + return ( +
+ {/* Background with water ripple SVG filter */} + + + + + + + + + + + + + + + + + + + + +
+ +
+

+ everything flows +

+ +

+ Flow + Fi + . +

+ +

+ Everything flows. The question is: where? +

+ + + ← back to NoFi + + +
+ scroll to flow ↓ +
+
+
+ ) +} diff --git a/components/sections/NatureFlowsSection.tsx b/components/sections/NatureFlowsSection.tsx new file mode 100644 index 0000000..440db89 --- /dev/null +++ b/components/sections/NatureFlowsSection.tsx @@ -0,0 +1,37 @@ +'use client' + +import SankeyDiagram from '@/components/visualizations/SankeyDiagram' +import SectionHeader from '@/components/ui/SectionHeader' +import ScrollReveal from '@/components/ui/ScrollReveal' +import { naturalFlows } from '@/lib/sankey-data' + +export default function NatureFlowsSection() { + return ( +
+
+ +
+ Natural Flows + + +
+ +
+
+ + +

+ In living systems, value doesn't accumulate. It circulates. +

+
+
+
+ ) +} diff --git a/components/sections/RegenerativeSection.tsx b/components/sections/RegenerativeSection.tsx new file mode 100644 index 0000000..c3132f3 --- /dev/null +++ b/components/sections/RegenerativeSection.tsx @@ -0,0 +1,39 @@ +'use client' + +import SankeyDiagram from '@/components/visualizations/SankeyDiagram' +import SectionHeader from '@/components/ui/SectionHeader' +import ScrollReveal from '@/components/ui/ScrollReveal' +import { regenerativeFlows } from '@/lib/sankey-data' + +export default function RegenerativeSection() { + return ( +
+
+ {/* Green tint overlay */} +
+ +
+ The Regenerative Alternative + + +
+ +
+
+ + +

+ Pre-distributive. Re-distributive. Naturally. +

+
+
+
+ ) +} diff --git a/components/sections/SandboxSection.tsx b/components/sections/SandboxSection.tsx new file mode 100644 index 0000000..013d3f5 --- /dev/null +++ b/components/sections/SandboxSection.tsx @@ -0,0 +1,42 @@ +'use client' + +import dynamic from 'next/dynamic' +import SectionHeader from '@/components/ui/SectionHeader' +import ScrollReveal from '@/components/ui/ScrollReveal' + +const FlowCanvas = dynamic(() => import('@/components/sandbox/FlowCanvas'), { + ssr: false, + loading: () => ( +
+ loading canvas... +
+ ), +}) + +export default function SandboxSection() { + return ( +
+
+ +
+ Flow Sandbox + + +

+ Reconnect the flows. What would you build? +

+
+ + + + + + +

+ drag nodes • connect ports • load presets • build your own flow system +

+
+
+
+ ) +} diff --git a/components/sections/TransitionSection.tsx b/components/sections/TransitionSection.tsx new file mode 100644 index 0000000..56d625e --- /dev/null +++ b/components/sections/TransitionSection.tsx @@ -0,0 +1,29 @@ +'use client' + +import ScrollReveal from '@/components/ui/ScrollReveal' + +const lines = [ + 'NoFi was the diagnosis.', + 'FlowFi is the prognosis.', + 'Not no flows — better flows.', + 'Not no finance — living finance.', + 'Finance as metabolism, not mechanism.', +] + +export default function TransitionSection() { + return ( +
+
+ +
+ {lines.map((line, i) => ( + +

+ {line} +

+
+ ))} +
+
+ ) +} diff --git a/components/ui/ScrollReveal.tsx b/components/ui/ScrollReveal.tsx new file mode 100644 index 0000000..4e2c13d --- /dev/null +++ b/components/ui/ScrollReveal.tsx @@ -0,0 +1,27 @@ +'use client' + +import { useRef } from 'react' +import { motion, useInView } from 'framer-motion' + +interface ScrollRevealProps { + children: React.ReactNode + className?: string + delay?: number +} + +export default function ScrollReveal({ children, className = '', delay = 0 }: ScrollRevealProps) { + const ref = useRef(null) + const isInView = useInView(ref, { once: true, margin: '-100px' }) + + return ( + + {children} + + ) +} diff --git a/components/ui/SectionHeader.tsx b/components/ui/SectionHeader.tsx new file mode 100644 index 0000000..fd9721e --- /dev/null +++ b/components/ui/SectionHeader.tsx @@ -0,0 +1,19 @@ +import ScrollReveal from './ScrollReveal' + +interface SectionHeaderProps { + children: React.ReactNode + wobble?: 1 | 2 | 3 | 4 + className?: string +} + +export default function SectionHeader({ children, wobble = 1, className = '' }: SectionHeaderProps) { + return ( + +

+ {children} +

+
+ ) +} diff --git a/components/visualizations/AnimatedPipe.tsx b/components/visualizations/AnimatedPipe.tsx new file mode 100644 index 0000000..66b7bd6 --- /dev/null +++ b/components/visualizations/AnimatedPipe.tsx @@ -0,0 +1,47 @@ +'use client' + +interface AnimatedPipeProps { + d: string + width?: number + color?: string + speed?: 'slow' | 'normal' | 'fast' + opacity?: number +} + +export default function AnimatedPipe({ + d, + width = 4, + color = '#2dd4bf', + speed = 'normal', + opacity = 0.6, +}: AnimatedPipeProps) { + const speedClass = { + slow: 'animate-pipe-flow-slow', + normal: 'animate-pipe-flow', + fast: 'animate-pipe-flow-fast', + }[speed] + + return ( + + {/* Outer wall */} + + {/* Inner flow */} + + + ) +} diff --git a/components/visualizations/FlowParticles.tsx b/components/visualizations/FlowParticles.tsx new file mode 100644 index 0000000..c0df7af --- /dev/null +++ b/components/visualizations/FlowParticles.tsx @@ -0,0 +1,35 @@ +'use client' + +interface FlowParticlesProps { + pathId: string + count?: number + color?: string + size?: number + duration?: number +} + +export default function FlowParticles({ + pathId, + count = 3, + color = '#2dd4bf', + size = 3, + duration = 3, +}: FlowParticlesProps) { + return ( + <> + {Array.from({ length: count }).map((_, i) => ( + + ))} + + ) +} diff --git a/components/visualizations/SankeyDiagram.tsx b/components/visualizations/SankeyDiagram.tsx new file mode 100644 index 0000000..3ab59fa --- /dev/null +++ b/components/visualizations/SankeyDiagram.tsx @@ -0,0 +1,171 @@ +'use client' + +import { useMemo } from 'react' +import { + sankey as d3Sankey, + sankeyLinkHorizontal, + SankeyNode, + SankeyLink, +} from 'd3-sankey' +import { FlowData } from '@/lib/types' +import AnimatedPipe from './AnimatedPipe' +import FlowParticles from './FlowParticles' +import WaterDistortion from './WaterDistortion' + +interface SankeyDiagramProps { + data: FlowData + width?: number + height?: number + showLabels?: boolean + speed?: 'slow' | 'normal' | 'fast' + showParticles?: boolean + showDistortion?: boolean + maxParticles?: number + className?: string +} + +interface SNode { + id: string + label?: string + color?: string +} + +interface SLink { + source: string + target: string + value: number + color?: string +} + +export default function SankeyDiagram({ + data, + width = 800, + height = 400, + showLabels = false, + speed = 'normal', + showParticles = true, + showDistortion = false, + maxParticles = 6, + className = '', +}: SankeyDiagramProps) { + const { nodes, links } = useMemo(() => { + const sankeyNodes = data.nodes.map(n => ({ ...n })) + const sankeyLinks = data.links.map(l => ({ + source: l.source, + target: l.target, + value: l.value, + color: l.color, + })) + + const generator = (d3Sankey as any)() + .nodeId((d: any) => d.id) + .nodeWidth(12) + .nodePadding(16) + .extent([[1, 1], [width - 1, height - 1]]) + + const result = generator({ + nodes: sankeyNodes, + links: sankeyLinks, + }) + + return { + nodes: result.nodes as any[], + links: result.links as any[], + } + }, [data, width, height]) + + const linkPath = sankeyLinkHorizontal() + const filterId = `distortion-${Math.random().toString(36).slice(2, 7)}` + + let particleCount = 0 + + return ( + + + {showDistortion && } + {/* Define paths for particles */} + {links.map((link, i) => { + const d = linkPath(link as any) + return d ? ( + + ) : null + })} + + + + {/* Links */} + {links.map((link, i) => { + const d = linkPath(link as any) + if (!d) return null + const linkWidth = Math.max(2, (link as any).width || 4) + return ( + + ) + })} + + {/* Particles */} + {showParticles && links.map((link, i) => { + if (particleCount >= maxParticles) return null + const pCount = Math.min(2, maxParticles - particleCount) + particleCount += pCount + return ( + + ) + })} + + {/* Nodes */} + {nodes.map((node, i) => ( + + + {showLabels && node.label && ( + + {node.label} + + )} + + ))} + + + ) +} diff --git a/components/visualizations/WaterDistortion.tsx b/components/visualizations/WaterDistortion.tsx new file mode 100644 index 0000000..b459e01 --- /dev/null +++ b/components/visualizations/WaterDistortion.tsx @@ -0,0 +1,38 @@ +'use client' + +interface WaterDistortionProps { + id?: string + intensity?: number +} + +export default function WaterDistortion({ + id = 'water-distortion', + intensity = 0.003, +}: WaterDistortionProps) { + return ( + + + + + + + ) +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..cf6dbbc --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,34 @@ +services: + flowfi: + build: . + restart: unless-stopped + labels: + - "traefik.enable=true" + - "traefik.http.routers.flowfi.rule=Host(`flowfi.network`) || Host(`www.flowfi.network`)" + - "traefik.http.services.flowfi.loadbalancer.server.port=80" + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:80/"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 15s + networks: + - traefik-public + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + cap_add: + - NET_BIND_SERVICE + - CHOWN + - SETGID + - SETUID + - DAC_OVERRIDE + read_only: true + tmpfs: + - /tmp + - /var/cache/nginx + +networks: + traefik-public: + external: true diff --git a/lib/sankey-data.ts b/lib/sankey-data.ts new file mode 100644 index 0000000..9b626a4 --- /dev/null +++ b/lib/sankey-data.ts @@ -0,0 +1,94 @@ +import { FlowData } from './types' + +// S2: Natural flows — abstract organic Sankey (unlabeled, acyclic) +// Represents sunlight/rain → photosynthesis/absorption → distribution → endpoints +export const naturalFlows: FlowData = { + nodes: [ + { id: 'sun', color: '#fbbf24' }, + { id: 'rain', color: '#60a5fa' }, + { id: 'photo', color: '#34d399' }, + { id: 'soil', color: '#c3b091' }, + { id: 'canopy', color: '#34d399' }, + { id: 'roots', color: '#a78bfa' }, + { id: 'stream', color: '#60a5fa' }, + { id: 'mycelium', color: '#a78bfa' }, + { id: 'fauna', color: '#f59e0b' }, + { id: 'atmosphere', color: '#94a3b8' }, + { id: 'humus', color: '#92704f' }, + ], + links: [ + { source: 'sun', target: 'photo', value: 8, color: '#fbbf2480' }, + { source: 'sun', target: 'canopy', value: 4, color: '#fbbf2440' }, + { source: 'rain', target: 'stream', value: 5, color: '#60a5fa80' }, + { source: 'rain', target: 'soil', value: 6, color: '#60a5fa60' }, + { source: 'photo', target: 'canopy', value: 5, color: '#34d39980' }, + { source: 'photo', target: 'roots', value: 3, color: '#34d39960' }, + { source: 'soil', target: 'roots', value: 4, color: '#c3b09180' }, + { source: 'soil', target: 'mycelium', value: 3, color: '#c3b09160' }, + { source: 'roots', target: 'mycelium', value: 3, color: '#a78bfa80' }, + { source: 'canopy', target: 'atmosphere', value: 4, color: '#34d39960' }, + { source: 'canopy', target: 'fauna', value: 3, color: '#34d39940' }, + { source: 'mycelium', target: 'humus', value: 4, color: '#a78bfa60' }, + { source: 'stream', target: 'atmosphere', value: 3, color: '#60a5fa60' }, + { source: 'fauna', target: 'humus', value: 2, color: '#f59e0b60' }, + { source: 'roots', target: 'humus', value: 2, color: '#a78bfa40' }, + ], +} + +// S3: Extractive pattern — finance funnel (labeled, acyclic) +export const extractiveFlows: FlowData = { + nodes: [ + { id: 'labor', label: 'Labor', color: '#e9456060' }, + { id: 'creativity', label: 'Creativity', color: '#e9456060' }, + { id: 'nature', label: 'Nature', color: '#e9456060' }, + { id: 'communities', label: 'Communities', color: '#e9456060' }, + { id: 'finance', label: 'Finance', color: '#e94560' }, + { id: 'shareholders', label: 'Shareholders', color: '#e94560cc' }, + { id: 'executives', label: 'Executives', color: '#e94560cc' }, + { id: 'tax-havens', label: 'Tax Havens', color: '#e94560aa' }, + { id: 'public-good', label: 'Public Good', color: '#e9456030' }, + ], + links: [ + { source: 'labor', target: 'finance', value: 10, color: '#e9456040' }, + { source: 'creativity', target: 'finance', value: 8, color: '#e9456040' }, + { source: 'nature', target: 'finance', value: 12, color: '#e9456040' }, + { source: 'communities', target: 'finance', value: 6, color: '#e9456040' }, + { source: 'finance', target: 'shareholders', value: 15, color: '#e9456080' }, + { source: 'finance', target: 'executives', value: 12, color: '#e9456080' }, + { source: 'finance', target: 'tax-havens', value: 7, color: '#e9456060' }, + { source: 'finance', target: 'public-good', value: 2, color: '#e9456020' }, + ], +} + +// S4: Regenerative alternative — redistributive mesh (labeled, acyclic) +// Uses separate input/output nodes to represent the circular nature without actual cycles +export const regenerativeFlows: FlowData = { + nodes: [ + { id: 'commons-in', label: 'Commons', color: '#2dd4bf' }, + { id: 'labor-r', label: 'Labor', color: '#2dd4bfcc' }, + { id: 'ecology', label: 'Ecology', color: '#34d399' }, + { id: 'care', label: 'Care', color: '#a78bfa' }, + { id: 'creativity-r', label: 'Creativity', color: '#60a5fa' }, + { id: 'community', label: 'Community', color: '#fbbf24' }, + { id: 'knowledge', label: 'Knowledge', color: '#f472b6' }, + { id: 'stewardship', label: 'Stewardship', color: '#34d399cc' }, + { id: 'commons-out', label: 'Commons', color: '#2dd4bf' }, + ], + links: [ + { source: 'commons-in', target: 'labor-r', value: 5, color: '#2dd4bf60' }, + { source: 'commons-in', target: 'ecology', value: 6, color: '#2dd4bf60' }, + { source: 'commons-in', target: 'care', value: 5, color: '#2dd4bf60' }, + { source: 'commons-in', target: 'creativity-r', value: 4, color: '#2dd4bf60' }, + { source: 'labor-r', target: 'community', value: 4, color: '#2dd4bf40' }, + { source: 'labor-r', target: 'knowledge', value: 3, color: '#2dd4bf40' }, + { source: 'ecology', target: 'stewardship', value: 4, color: '#34d39960' }, + { source: 'ecology', target: 'community', value: 3, color: '#34d39940' }, + { source: 'care', target: 'community', value: 4, color: '#a78bfa60' }, + { source: 'care', target: 'knowledge', value: 3, color: '#a78bfa40' }, + { source: 'creativity-r', target: 'knowledge', value: 3, color: '#60a5fa60' }, + { source: 'creativity-r', target: 'community', value: 3, color: '#60a5fa40' }, + { source: 'community', target: 'commons-out', value: 6, color: '#fbbf2460' }, + { source: 'knowledge', target: 'commons-out', value: 4, color: '#f472b660' }, + { source: 'stewardship', target: 'commons-out', value: 4, color: '#34d39960' }, + ], +} diff --git a/lib/types.ts b/lib/types.ts new file mode 100644 index 0000000..ca8b310 --- /dev/null +++ b/lib/types.ts @@ -0,0 +1,25 @@ +export interface FlowNode { + id: string + label?: string + color?: string +} + +export interface FlowLink { + source: string + target: string + value: number + color?: string +} + +export interface FlowData { + nodes: FlowNode[] + links: FlowLink[] +} + +export interface SandboxNode { + id: string + type: 'source' | 'pipe' | 'sink' + label: string + flowRate?: number + fillLevel?: number +} diff --git a/next.config.mjs b/next.config.mjs new file mode 100644 index 0000000..4f5b548 --- /dev/null +++ b/next.config.mjs @@ -0,0 +1,13 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + output: 'export', + distDir: 'out', + typescript: { + ignoreBuildErrors: true, + }, + images: { + unoptimized: true, + }, +} + +export default nextConfig diff --git a/package.json b/package.json new file mode 100644 index 0000000..f1e3f45 --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "flowfi-network", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "react": "^19.2.4", + "react-dom": "^19.2.4", + "next": "^16.1.6", + "d3-sankey": "^0.12.3", + "d3-shape": "^3.2.0", + "@xyflow/react": "^12.6.0", + "framer-motion": "^12.6.0" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.1.18", + "@types/node": "^25.2.3", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@types/d3-sankey": "^0.12.5", + "@types/d3-shape": "^3.1.7", + "postcss": "^8.5.6", + "tailwindcss": "^4.1.18", + "typescript": "^5.9.3" + }, + "packageManager": "pnpm@10.23.0" +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..139953f --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1271 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@xyflow/react': + specifier: ^12.6.0 + version: 12.10.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + d3-sankey: + specifier: ^0.12.3 + version: 0.12.3 + d3-shape: + specifier: ^3.2.0 + version: 3.2.0 + framer-motion: + specifier: ^12.6.0 + version: 12.35.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + next: + specifier: ^16.1.6 + version: 16.1.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: + specifier: ^19.2.4 + version: 19.2.4 + react-dom: + specifier: ^19.2.4 + version: 19.2.4(react@19.2.4) + devDependencies: + '@tailwindcss/postcss': + specifier: ^4.1.18 + version: 4.2.1 + '@types/d3-sankey': + specifier: ^0.12.5 + version: 0.12.5 + '@types/d3-shape': + specifier: ^3.1.7 + version: 3.1.8 + '@types/node': + specifier: ^25.2.3 + version: 25.4.0 + '@types/react': + specifier: ^19.2.14 + version: 19.2.14 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.14) + postcss: + specifier: ^8.5.6 + version: 8.5.8 + tailwindcss: + specifier: ^4.1.18 + version: 4.2.1 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@emnapi/runtime@1.8.1': + resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@next/env@16.1.6': + resolution: {integrity: sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==} + + '@next/swc-darwin-arm64@16.1.6': + resolution: {integrity: sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@16.1.6': + resolution: {integrity: sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@16.1.6': + resolution: {integrity: sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-arm64-musl@16.1.6': + resolution: {integrity: sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-x64-gnu@16.1.6': + resolution: {integrity: sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-linux-x64-musl@16.1.6': + resolution: {integrity: sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-win32-arm64-msvc@16.1.6': + resolution: {integrity: sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@16.1.6': + resolution: {integrity: sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + + '@tailwindcss/node@4.2.1': + resolution: {integrity: sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==} + + '@tailwindcss/oxide-android-arm64@4.2.1': + resolution: {integrity: sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.2.1': + resolution: {integrity: sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.2.1': + resolution: {integrity: sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.2.1': + resolution: {integrity: sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': + resolution: {integrity: sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': + resolution: {integrity: sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.2.1': + resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.2.1': + resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.2.1': + resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.2.1': + resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': + resolution: {integrity: sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.2.1': + resolution: {integrity: sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.2.1': + resolution: {integrity: sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==} + engines: {node: '>= 20'} + + '@tailwindcss/postcss@4.2.1': + resolution: {integrity: sha512-OEwGIBnXnj7zJeonOh6ZG9woofIjGrd2BORfvE5p9USYKDCZoQmfqLcfNiRWoJlRWLdNPn2IgVZuWAOM4iTYMw==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-drag@3.0.7': + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@1.0.11': + resolution: {integrity: sha512-4pQMp8ldf7UaB/gR8Fvvy69psNHkTpD/pVw3vmEi8iZAB9EPMBruB1JvHO4BIq9QkUUd2lV1F5YXpMNj7JPBpw==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-sankey@0.12.5': + resolution: {integrity: sha512-/3RZSew0cLAtzGQ+C89hq/Rp3H20QJuVRSqFy6RKLe7E0B8kd2iOS1oBsodrgds4PcNVpqWhdUEng/SHvBcJ6Q==} + + '@types/d3-selection@3.0.11': + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + + '@types/d3-shape@1.3.12': + resolution: {integrity: sha512-8oMzcd4+poSLGgV0R1Q1rOlx/xdmozS4Xab7np0eamFFUYq71AU9pOCJEFnkXW2aI/oXdVYJzw6pssbSut7Z9Q==} + + '@types/d3-shape@3.1.8': + resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} + + '@types/d3-transition@3.0.9': + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + + '@types/d3-zoom@3.0.8': + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + + '@types/node@25.4.0': + resolution: {integrity: sha512-9wLpoeWuBlcbBpOY3XmzSTG3oscB6xjBEEtn+pYXTfhyXhIxC5FsBer2KTopBlvKEiW9l13po9fq+SJY/5lkhw==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.14': + resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + + '@xyflow/react@12.10.1': + resolution: {integrity: sha512-5eSWtIK/+rkldOuFbOOz44CRgQRjtS9v5nufk77DV+XBnfCGL9HAQ8PG00o2ZYKqkEU/Ak6wrKC95Tu+2zuK3Q==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + + '@xyflow/system@0.0.75': + resolution: {integrity: sha512-iXs+AGFLi8w/VlAoc/iSxk+CxfT6o64Uw/k0CKASOPqjqz6E0rb5jFZgJtXGZCpfQI6OQpu5EnumP5fGxQheaQ==} + + baseline-browser-mapping@2.10.0: + resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} + engines: {node: '>=6.0.0'} + hasBin: true + + caniuse-lite@1.0.30001777: + resolution: {integrity: sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ==} + + classcat@5.0.5: + resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + d3-array@2.12.1: + resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@1.0.9: + resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-sankey@0.12.3: + resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-shape@1.3.7: + resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + enhanced-resolve@5.20.0: + resolution: {integrity: sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==} + engines: {node: '>=10.13.0'} + + framer-motion@12.35.2: + resolution: {integrity: sha512-dhfuEMaNo0hc+AEqyHiIfiJRNb9U9UQutE9FoKm5pjf7CMitp9xPEF1iWZihR1q86LBmo6EJ7S8cN8QXEy49AA==} + 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 + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + internmap@1.0.1: + resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + lightningcss-android-arm64@1.31.1: + resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.31.1: + resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.31.1: + resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.31.1: + resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.31.1: + resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.31.1: + resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.31.1: + resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.31.1: + resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.31.1: + resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.31.1: + resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.31.1: + resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.31.1: + resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} + engines: {node: '>= 12.0.0'} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + motion-dom@12.35.2: + resolution: {integrity: sha512-pWXFMTwvGDbx1Fe9YL5HZebv2NhvGBzRtiNUv58aoK7+XrsuaydQ0JGRKK2r+bTKlwgSWwWxHbP5249Qr/BNpg==} + + motion-utils@12.29.2: + resolution: {integrity: sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + next@16.1.6: + resolution: {integrity: sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==} + engines: {node: '>=20.9.0'} + hasBin: true + 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 + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.8: + resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} + engines: {node: ^10 || ^12 || >=14} + + react-dom@19.2.4: + resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} + peerDependencies: + react: ^19.2.4 + + react@19.2.4: + resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} + engines: {node: '>=0.10.0'} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + 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 + + tailwindcss@4.2.1: + resolution: {integrity: sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==} + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + zustand@4.5.7: + resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} + 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 + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@emnapi/runtime@1.8.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@img/colour@1.1.0': + optional: true + + '@img/sharp-darwin-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + optional: true + + '@img/sharp-darwin-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + optional: true + + '@img/sharp-linux-arm@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + optional: true + + '@img/sharp-linux-ppc64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.4 + optional: true + + '@img/sharp-linux-riscv64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.2.4 + optional: true + + '@img/sharp-linux-s390x@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.4 + optional: true + + '@img/sharp-linux-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': + dependencies: + '@emnapi/runtime': 1.8.1 + optional: true + + '@img/sharp-win32-arm64@0.34.5': + optional: true + + '@img/sharp-win32-ia32@0.34.5': + optional: true + + '@img/sharp-win32-x64@0.34.5': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@next/env@16.1.6': {} + + '@next/swc-darwin-arm64@16.1.6': + optional: true + + '@next/swc-darwin-x64@16.1.6': + optional: true + + '@next/swc-linux-arm64-gnu@16.1.6': + optional: true + + '@next/swc-linux-arm64-musl@16.1.6': + optional: true + + '@next/swc-linux-x64-gnu@16.1.6': + optional: true + + '@next/swc-linux-x64-musl@16.1.6': + optional: true + + '@next/swc-win32-arm64-msvc@16.1.6': + optional: true + + '@next/swc-win32-x64-msvc@16.1.6': + optional: true + + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + + '@tailwindcss/node@4.2.1': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.20.0 + jiti: 2.6.1 + lightningcss: 1.31.1 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.2.1 + + '@tailwindcss/oxide-android-arm64@4.2.1': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.2.1': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.2.1': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.2.1': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.2.1': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.2.1': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.2.1': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.2.1': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.2.1': + optional: true + + '@tailwindcss/oxide@4.2.1': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.2.1 + '@tailwindcss/oxide-darwin-arm64': 4.2.1 + '@tailwindcss/oxide-darwin-x64': 4.2.1 + '@tailwindcss/oxide-freebsd-x64': 4.2.1 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.1 + '@tailwindcss/oxide-linux-arm64-gnu': 4.2.1 + '@tailwindcss/oxide-linux-arm64-musl': 4.2.1 + '@tailwindcss/oxide-linux-x64-gnu': 4.2.1 + '@tailwindcss/oxide-linux-x64-musl': 4.2.1 + '@tailwindcss/oxide-wasm32-wasi': 4.2.1 + '@tailwindcss/oxide-win32-arm64-msvc': 4.2.1 + '@tailwindcss/oxide-win32-x64-msvc': 4.2.1 + + '@tailwindcss/postcss@4.2.1': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.2.1 + '@tailwindcss/oxide': 4.2.1 + postcss: 8.5.8 + tailwindcss: 4.2.1 + + '@types/d3-color@3.1.3': {} + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@1.0.11': {} + + '@types/d3-path@3.1.1': {} + + '@types/d3-sankey@0.12.5': + dependencies: + '@types/d3-shape': 1.3.12 + + '@types/d3-selection@3.0.11': {} + + '@types/d3-shape@1.3.12': + dependencies: + '@types/d3-path': 1.0.11 + + '@types/d3-shape@3.1.8': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + + '@types/node@25.4.0': + dependencies: + undici-types: 7.18.2 + + '@types/react-dom@19.2.3(@types/react@19.2.14)': + dependencies: + '@types/react': 19.2.14 + + '@types/react@19.2.14': + dependencies: + csstype: 3.2.3 + + '@xyflow/react@12.10.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@xyflow/system': 0.0.75 + classcat: 5.0.5 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + zustand: 4.5.7(@types/react@19.2.14)(react@19.2.4) + transitivePeerDependencies: + - '@types/react' + - immer + + '@xyflow/system@0.0.75': + dependencies: + '@types/d3-drag': 3.0.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-zoom: 3.0.0 + + baseline-browser-mapping@2.10.0: {} + + caniuse-lite@1.0.30001777: {} + + classcat@5.0.5: {} + + client-only@0.0.1: {} + + csstype@3.2.3: {} + + d3-array@2.12.1: + dependencies: + internmap: 1.0.1 + + d3-color@3.1.0: {} + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-ease@3.0.1: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@1.0.9: {} + + d3-path@3.1.0: {} + + d3-sankey@0.12.3: + dependencies: + d3-array: 2.12.1 + d3-shape: 1.3.7 + + d3-selection@3.0.0: {} + + d3-shape@1.3.7: + dependencies: + d3-path: 1.0.9 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + detect-libc@2.1.2: {} + + enhanced-resolve@5.20.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + + framer-motion@12.35.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + motion-dom: 12.35.2 + motion-utils: 12.29.2 + tslib: 2.8.1 + optionalDependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + graceful-fs@4.2.11: {} + + internmap@1.0.1: {} + + jiti@2.6.1: {} + + lightningcss-android-arm64@1.31.1: + optional: true + + lightningcss-darwin-arm64@1.31.1: + optional: true + + lightningcss-darwin-x64@1.31.1: + optional: true + + lightningcss-freebsd-x64@1.31.1: + optional: true + + lightningcss-linux-arm-gnueabihf@1.31.1: + optional: true + + lightningcss-linux-arm64-gnu@1.31.1: + optional: true + + lightningcss-linux-arm64-musl@1.31.1: + optional: true + + lightningcss-linux-x64-gnu@1.31.1: + optional: true + + lightningcss-linux-x64-musl@1.31.1: + optional: true + + lightningcss-win32-arm64-msvc@1.31.1: + optional: true + + lightningcss-win32-x64-msvc@1.31.1: + optional: true + + lightningcss@1.31.1: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.31.1 + lightningcss-darwin-arm64: 1.31.1 + lightningcss-darwin-x64: 1.31.1 + lightningcss-freebsd-x64: 1.31.1 + lightningcss-linux-arm-gnueabihf: 1.31.1 + lightningcss-linux-arm64-gnu: 1.31.1 + lightningcss-linux-arm64-musl: 1.31.1 + lightningcss-linux-x64-gnu: 1.31.1 + lightningcss-linux-x64-musl: 1.31.1 + lightningcss-win32-arm64-msvc: 1.31.1 + lightningcss-win32-x64-msvc: 1.31.1 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + motion-dom@12.35.2: + dependencies: + motion-utils: 12.29.2 + + motion-utils@12.29.2: {} + + nanoid@3.3.11: {} + + next@16.1.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + '@next/env': 16.1.6 + '@swc/helpers': 0.5.15 + baseline-browser-mapping: 2.10.0 + caniuse-lite: 1.0.30001777 + postcss: 8.4.31 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + styled-jsx: 5.1.6(react@19.2.4) + optionalDependencies: + '@next/swc-darwin-arm64': 16.1.6 + '@next/swc-darwin-x64': 16.1.6 + '@next/swc-linux-arm64-gnu': 16.1.6 + '@next/swc-linux-arm64-musl': 16.1.6 + '@next/swc-linux-x64-gnu': 16.1.6 + '@next/swc-linux-x64-musl': 16.1.6 + '@next/swc-win32-arm64-msvc': 16.1.6 + '@next/swc-win32-x64-msvc': 16.1.6 + sharp: 0.34.5 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + + picocolors@1.1.1: {} + + postcss@8.4.31: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postcss@8.5.8: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + react-dom@19.2.4(react@19.2.4): + dependencies: + react: 19.2.4 + scheduler: 0.27.0 + + react@19.2.4: {} + + scheduler@0.27.0: {} + + semver@7.7.4: + optional: true + + sharp@0.34.5: + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.7.4 + 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 + optional: true + + source-map-js@1.2.1: {} + + styled-jsx@5.1.6(react@19.2.4): + dependencies: + client-only: 0.0.1 + react: 19.2.4 + + tailwindcss@4.2.1: {} + + tapable@2.3.0: {} + + tslib@2.8.1: {} + + typescript@5.9.3: {} + + undici-types@7.18.2: {} + + use-sync-external-store@1.6.0(react@19.2.4): + dependencies: + react: 19.2.4 + + zustand@4.5.7(@types/react@19.2.14)(react@19.2.4): + dependencies: + use-sync-external-store: 1.6.0(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + react: 19.2.4 diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..79bcf13 --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,8 @@ +/** @type {import('postcss-load-config').Config} */ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5b8462e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,41 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "paths": { + "@/*": [ + "./*" + ] + }, + "plugins": [ + { + "name": "next" + } + ] + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] +}