From a08a6bdb69d26ff50fb2855428232c22ea8746ca Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Mon, 9 Mar 2026 21:39:32 -0700 Subject: [PATCH] Remove bright gradient bands, fix Sankey data, add newsletter CTA - Replace bright gradient section backgrounds with flat bg-deep - Remove wave distortion (showDistortion=false) from all diagrams - Fix circular link errors in naturalFlows by breaking feedback cycles - Add multi-dimensional flow colors (energy, resource, signal, commons) - Update CTA: "Dive into the Current" with newsletter subscription - Newsletter posts to listmonk via newsletter-api (FlowFi list #26) Co-Authored-By: Claude Opus 4.6 --- components/sections/CTASection.tsx | 66 ++++++++- components/sections/ExtractivePipeSection.tsx | 7 +- components/sections/NatureFlowsSection.tsx | 25 ++-- components/sections/RegenerativeSection.tsx | 20 +-- components/sections/TransitionSection.tsx | 2 +- components/visualizations/SankeyDiagram.tsx | 9 +- lib/sankey-data.ts | 139 ++++++++++-------- 7 files changed, 172 insertions(+), 96 deletions(-) diff --git a/components/sections/CTASection.tsx b/components/sections/CTASection.tsx index edad61d..f306631 100644 --- a/components/sections/CTASection.tsx +++ b/components/sections/CTASection.tsx @@ -1,8 +1,37 @@ 'use client' +import { useState, FormEvent } from 'react' import ScrollReveal from '@/components/ui/ScrollReveal' +const FLOWFI_LIST_UUID = 'fefebe2c-0966-4df5-81ec-1eae9b9dce3f' +const SUBSCRIBE_URL = 'https://newsletter.jeffemmett.com/subscribe' + export default function CTASection() { + const [email, setEmail] = useState('') + const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle') + + async function handleSubmit(e: FormEvent) { + e.preventDefault() + if (!email) return + + setStatus('loading') + try { + const res = await fetch(SUBSCRIBE_URL, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email, list_uuid: FLOWFI_LIST_UUID }), + }) + if (res.ok) { + setStatus('success') + setEmail('') + } else { + setStatus('error') + } + } catch { + setStatus('error') + } + } + return (
@@ -21,18 +50,49 @@ export default function CTASection() { -

- You already are. +

+ Dive into the current.

+ {status === 'success' ? ( +

+ You're in the current now. Welcome. +

+ ) : ( +
+ setEmail(e.target.value)} + placeholder="your@email.com" + required + className="w-full sm:flex-1 bg-ocean/40 border border-flow/15 rounded-full px-6 py-3 text-foam/70 placeholder:text-mist/25 text-sm font-light focus:outline-none focus:border-flow/40 focus:bg-ocean/60 transition-all duration-500" + /> + +
+ )} + {status === 'error' && ( +

+ Something went wrong. Try again? +

+ )} +
+ +

NoFi → FlowFi → ???

- + -
- - {/* Red pollution tint — subtle, unsettling */} -
+
The Extractive Pattern -
+
-
- - {/* Subtle teal ambient light */} -
+
Natural Flows + +
+ Energy + Resources + Signals + Commons +
+
+ -
+
@@ -34,7 +37,7 @@ export default function NatureFlowsSection() {

- In living systems, value doesn't accumulate. It circulates. + In living systems, value doesn't accumulate. It circulates — across dimensions.

diff --git a/components/sections/RegenerativeSection.tsx b/components/sections/RegenerativeSection.tsx index 5417cb3..1cc9970 100644 --- a/components/sections/RegenerativeSection.tsx +++ b/components/sections/RegenerativeSection.tsx @@ -8,25 +8,27 @@ import { regenerativeFlows } from '@/lib/sankey-data' export default function RegenerativeSection() { return (
-
- - {/* Green life glow */} -
+
The Regenerative Alternative + +
+ Economic + Governance + Data +
+
+ -
+
diff --git a/components/sections/TransitionSection.tsx b/components/sections/TransitionSection.tsx index f523ca4..eed6568 100644 --- a/components/sections/TransitionSection.tsx +++ b/components/sections/TransitionSection.tsx @@ -13,7 +13,7 @@ const lines = [ export default function TransitionSection() { return (
-
+
{/* Flowing light streak */}
{node.label} diff --git a/lib/sankey-data.ts b/lib/sankey-data.ts index 9b626a4..5f8e470 100644 --- a/lib/sankey-data.ts +++ b/lib/sankey-data.ts @@ -1,47 +1,53 @@ import { FlowData } from './types' -// S2: Natural flows — abstract organic Sankey (unlabeled, acyclic) -// Represents sunlight/rain → photosynthesis/absorption → distribution → endpoints +// Flow dimension colors: +// Economic = teal (#2dd4bf) +// Governance/Delegation = purple (#a78bfa) +// Data/Knowledge = blue (#60a5fa) +// Energy/Resources = amber (#fbbf24) + +// S2: Multi-dimensional flows in a living system +// Same nodes participate in multiple flow types +// Note: d3-sankey requires acyclic graphs, so feedback loops are +// represented with separate source/sink nodes (e.g. soil-in → soil-out) 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' }, + { id: 'sun', label: 'Energy', color: '#fbbf24' }, + { id: 'water', label: 'Water', color: '#38bdf8' }, + { id: 'soil', label: 'Substrate', color: '#c3b091' }, + { id: 'producers', label: 'Producers', color: '#34d399' }, + { id: 'consumers', label: 'Consumers', color: '#60a5fa' }, + { id: 'decomposers', label: 'Decomposers', color: '#a78bfa' }, + { id: 'signals', label: 'Signals', color: '#f472b6' }, + { id: 'nutrients', label: 'Nutrients', color: '#c3b091' }, + { id: 'commons', label: 'Commons', color: '#94a3b8' }, ], 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' }, + // Energy flows (amber) + { source: 'sun', target: 'producers', value: 10, color: '#fbbf2460' }, + { source: 'producers', target: 'consumers', value: 6, color: '#fbbf2440' }, + { source: 'consumers', target: 'decomposers', value: 4, color: '#fbbf2430' }, + // Resource flows (teal) + { source: 'soil', target: 'producers', value: 6, color: '#2dd4bf50' }, + { source: 'water', target: 'producers', value: 5, color: '#38bdf850' }, + { source: 'decomposers', target: 'nutrients', value: 5, color: '#2dd4bf40' }, + // Information/signal flows (pink) + { source: 'producers', target: 'signals', value: 3, color: '#f472b640' }, + { source: 'signals', target: 'consumers', value: 3, color: '#f472b640' }, + { source: 'signals', target: 'decomposers', value: 2, color: '#f472b630' }, + // Commons flows (grey-blue) + { source: 'consumers', target: 'commons', value: 3, color: '#94a3b840' }, + { source: 'producers', target: 'commons', value: 4, color: '#94a3b830' }, ], } -// S3: Extractive pattern — finance funnel (labeled, acyclic) +// S3: Extractive pattern — all dimensions funnel through one bottleneck 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: 'labor', label: 'Labor', color: '#e9456050' }, + { id: 'data', label: 'Data', color: '#e9456050' }, + { id: 'nature', label: 'Nature', color: '#e9456050' }, + { id: 'governance', label: 'Governance', color: '#e9456050' }, { id: 'finance', label: 'Finance', color: '#e94560' }, { id: 'shareholders', label: 'Shareholders', color: '#e94560cc' }, { id: 'executives', label: 'Executives', color: '#e94560cc' }, @@ -49,46 +55,53 @@ export const extractiveFlows: FlowData = { { 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' }, + // Economic flows in (muted red) + { source: 'labor', target: 'finance', value: 10, color: '#e9456035' }, + { source: 'nature', target: 'finance', value: 12, color: '#e9456035' }, + // Data flows in (muted red-blue) + { source: 'data', target: 'finance', value: 8, color: '#e9456030' }, + // Delegation captured (muted red-purple) + { source: 'governance', target: 'finance', value: 6, color: '#e9456028' }, + // All flows out through narrow channels + { source: 'finance', target: 'shareholders', value: 16, color: '#e9456070' }, + { source: 'finance', target: 'executives', value: 12, color: '#e9456060' }, + { source: 'finance', target: 'tax-havens', value: 6, color: '#e9456050' }, + { source: 'finance', target: 'public-good', value: 2, color: '#e9456018' }, ], } -// S4: Regenerative alternative — redistributive mesh (labeled, acyclic) -// Uses separate input/output nodes to represent the circular nature without actual cycles +// S4: Regenerative — multi-dimensional flows that interconnect +// Economic, governance, and knowledge flows between same nodes export const regenerativeFlows: FlowData = { nodes: [ - { id: 'commons-in', label: 'Commons', color: '#2dd4bf' }, - { id: 'labor-r', label: 'Labor', color: '#2dd4bfcc' }, + { id: 'commons', label: 'Commons', color: '#2dd4bf' }, + { id: 'labor-r', label: 'Workers', color: '#2dd4bfbb' }, { id: 'ecology', label: 'Ecology', color: '#34d399' }, { id: 'care', label: 'Care', color: '#a78bfa' }, - { id: 'creativity-r', label: 'Creativity', color: '#60a5fa' }, + { id: 'data-r', label: 'Data', 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' }, + { id: 'stewardship', label: 'Stewardship', color: '#34d399bb' }, + { id: 'public', label: 'Public Good', 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' }, + // Economic flows (teal) + { source: 'commons', target: 'labor-r', value: 5, color: '#2dd4bf45' }, + { source: 'commons', target: 'ecology', value: 4, color: '#2dd4bf45' }, + { source: 'labor-r', target: 'community', value: 4, color: '#2dd4bf35' }, + { source: 'ecology', target: 'stewardship', value: 3, color: '#2dd4bf35' }, + // Delegation/governance flows (purple) + { source: 'commons', target: 'care', value: 4, color: '#a78bfa40' }, + { source: 'care', target: 'community', value: 3, color: '#a78bfa35' }, + { source: 'community', target: 'public', value: 5, color: '#a78bfa30' }, + // Data/knowledge flows (blue) + { source: 'commons', target: 'data-r', value: 4, color: '#60a5fa40' }, + { source: 'data-r', target: 'knowledge', value: 4, color: '#60a5fa35' }, + { source: 'knowledge', target: 'public', value: 4, color: '#60a5fa30' }, + // Cross-dimension connections + { source: 'care', target: 'knowledge', value: 2, color: '#a78bfa25' }, + { source: 'ecology', target: 'community', value: 2, color: '#34d39930' }, + { source: 'stewardship', target: 'public', value: 3, color: '#34d39930' }, + { source: 'labor-r', target: 'knowledge', value: 2, color: '#2dd4bf25' }, ], }