Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8153cac
Add cross-focus-area impact dashboard
Jun 29, 2026
c0df232
Refine impact dashboard copy and logic-model alignment
Jun 29, 2026
4379aba
feat(impact): multi-role PL pills + bottleneck-based role taxonomy
Jun 30, 2026
b5ab011
fix(impact): drop per-role descriptions from detail modal
Jun 30, 2026
81148d5
fix(impact): swap Q1/Q2 (Q1=did it matter/impact, Q2=did it happen/ou…
Jun 30, 2026
f8c6115
fix(impact): real hover tooltip on PL role pills
Jun 30, 2026
0446181
impact(redesign): two-axis color tokens + role/logic-model copy
Jul 1, 2026
b998abd
impact(redesign): cards use THE FIELD / OUR HAND split + live signal
Jul 1, 2026
7280622
impact(redesign): measuring A/B/C cards + color-coded chain + toolkit…
Jul 1, 2026
81e19b1
impact(redesign): review pass
Jul 1, 2026
37fa01a
impact: fold in redesign + detail/measuring refinements
Jul 1, 2026
543d831
impact: methodology jump link, black field color, roles-as-modal, copy
Jul 1, 2026
49d9823
impact: simplify methodology to three questions
Jul 1, 2026
5ebaa0e
impact: make the whole 'What difference are we making?' card clickable
Jul 1, 2026
6fa04c4
impact: link detail cards to focus-area insights + measuring copy
Jul 1, 2026
b9c80bd
prototype(impact): crowd-forecast signals on inflection points
Jul 6, 2026
6275785
impact: move crowd forecast into the horizontal Live-signal band
Jul 6, 2026
857a2c7
feat(impact): show market volume (money at stake) in Live signal band
Jul 6, 2026
19e0c57
feat(impact): restructure detail modal + fold in market signals
Jul 6, 2026
211a813
refactor(impact): reorder field, separate sections, restore PL role p…
Jul 6, 2026
879a9d6
impact: put Our hand & Live signal on grey cards like The field
Jul 6, 2026
7536455
impact: hide live-signal detail in preview; add Provenance signals
Jul 6, 2026
cb790e4
impact: move Turkey Wikipedia-on-IPFS signal to 'Communication that c…
Jul 6, 2026
ab31999
impact: make two-axis accents dark-mode legible
Jul 6, 2026
218ffb2
impact: drop GainForest/Glow Q3 evidence line from outcomes-verified …
Jul 7, 2026
5106794
radar: auto-advance slides with story-style progress bars
Jul 7, 2026
5b4d38f
Add named Capital-role ventures as contribution outputs
claude Jul 7, 2026
3f3643a
Linkify named ventures in contribution copy; drop hedge text
claude Jul 7, 2026
e675ced
Fix inflection point card text: 'How PL is making a difference'
Jul 7, 2026
42fb68e
impact: relabel field subtitle to 'Inflection point status and impact'
Jul 7, 2026
6ff8f73
impact: add "How to read this" overlay + methodology callout, mark fo…
lksbrssr Jul 7, 2026
1e3666d
impact: drop the "How to read this" methodology pull-quote
lksbrssr Jul 7, 2026
fbe11d6
impact: retune crowd-forecast markets
lksbrssr Jul 7, 2026
c202710
impact: reassign Metaculus WBE forecasts + drop World from programmab…
lksbrssr Jul 7, 2026
08cf753
Merge remote-tracking branch 'origin/main' into lukas/impact-dashboard
Jul 14, 2026
71e126d
impact: rename 'Capital' role to 'Funding' in methodology modal
Jul 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,9 @@ NEXT_PUBLIC_ADMIN_DID=did:plc:pgwr6hkosgznfl5nz7egajei
# GraphQL indexer URL for org.plresearch.* records
# Default: https://api.hi.gainforest.app/graphql
# INDEXER_URL=http://localhost:4000/graphql

# Metaculus API token — enables live community-forecast readings on the /impact
# dashboard (e.g. the whole-brain-emulation question). Without it the question
# is still linked, just without a live number. Create a token at
# https://www.metaculus.com/accounts/settings/
# METACULUS_API_TOKEN=
Binary file added docs/screenshots/impact-modal-restructured.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/impact-signals-grid.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/impact-signals-livebar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/impact-signals-modal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/impact-signals-platforms.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ body {
background-color: var(--color-white);
}

/* Impact dashboard two-axis accents — theme-aware so the “field” ink and the
progress track stay legible in dark mode (ImpactDashboard / MeasuringQuestions). */
:root {
--impact-field: #131316; /* the field axis (neutral ink) */
--impact-field-ink: #ffffff; /* text on a field-accent fill */
--impact-field-track: #e5e7eb; /* empty progress segment */
--impact-hand: #1982f4; /* our-hand axis (blue) */
}

/* ===== Dark mode =====
Rather than annotating every component, we remap the semantic color
tokens. Because Tailwind v4 utilities compile to var(--color-*),
Expand All @@ -85,6 +94,13 @@ html.dark {
--color-gray: #9A9EAB;

--color-blue: #3B96F6; /* slightly brighter accents on dark */

/* Two-axis accents: light ink reads on dark surfaces; the field fill flips
to light so white badge text is swapped for dark ink. */
--impact-field: #e9e9ee;
--impact-field-ink: #15171c;
--impact-field-track: #3a3f4b;
--impact-hand: #3b96f6;
}

/* Keep the footer's intentional dark design in both themes by
Expand Down
124 changes: 124 additions & 0 deletions src/app/impact/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import type { Metadata } from 'next'
import Breadcrumb from '@/components/Breadcrumb'
import ImpactDashboard, { type LiveMetric, type LiveOutputs } from '@/components/ImpactDashboard'
import MeasuringQuestions from '@/components/MeasuringQuestions'
import { fetchSimocracyStats } from '@/lib/simocracy'
import { fetchGainforestStats } from '@/lib/gainforest'
import { fetchGlowStats } from '@/lib/glow'
import { resolveAllSignals } from '@/lib/market-signals'
import { FIELD_COLOR, HAND_COLOR } from '@/lib/inflection-points'

// Pull live output metrics for the Economies & Governance inflection points from
// the same sources as the FA2 live dashboard. These are Q3 OUTPUTS (the work
// PL-backed teams have produced) — never Q2 progress toward a threshold.
export const revalidate = 60

async function fetchLiveOutputs(): Promise<LiveOutputs> {
const compact = (n: number) =>
new Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 1 }).format(n)
const out: LiveOutputs = {}

const [sim, gf, glow] = await Promise.allSettled([
fetchSimocracyStats(),
fetchGainforestStats(),
fetchGlowStats(),
])

// A binding decision at scale — Simocracy (a PL-supported deliberation mechanism).
if (sim.status === 'fulfilled' && !sim.value.degraded) {
const t = sim.value.totals
const metrics: LiveMetric[] = [
{ n: t.uniqueHumans, label: 'participants' },
{ n: t.totalSims, label: 'simulations' },
{ n: t.totalGatherings, label: 'gatherings' },
]
.filter((m) => m.n > 0)
.map((m) => ({ value: compact(m.n), label: m.label }))
if (metrics.length) out['A binding decision at scale'] = metrics
}

// Capital that pays on verified outcomes — GainForest + Glow (PL-backed MRV teams).
const verified: LiveMetric[] = []
if (gf.status === 'fulfilled' && !gf.value.degraded) {
const g = gf.value
if (g.observations > 0) verified.push({ value: compact(g.observations), label: 'species observations' })
if (g.certifiedOrgs > 0) verified.push({ value: compact(g.certifiedOrgs), label: 'certified orgs' })
}
if (glow.status === 'fulfilled' && !glow.value.degraded) {
const gl = glow.value
if (gl.activeFarms > 0) verified.push({ value: compact(gl.activeFarms), label: 'active solar farms' })
if (gl.carbon > 0) verified.push({ value: compact(gl.carbon), label: 'tCO₂ / wk' })
}
if (verified.length) out['Capital that pays on verified outcomes'] = verified

return out
}

export const metadata: Metadata = {
title: 'Impact',
description:
'How we measure whether PL R&D’s work matters: the inflection points we have pre-registered across every focus area, and how we will know if they happen.',
}

export default async function ImpactPage() {
const [liveOutputs, marketSignals] = await Promise.all([
fetchLiveOutputs(),
resolveAllSignals(),
])
return (
<div>
{/* Hero */}
<div className="max-w-6xl mx-auto px-6 pt-8">
<Breadcrumb items={[{ label: 'Impact' }]} />
<div className="pt-8 pb-10">
<h1 className="text-2xl lg:text-[44px] font-semibold leading-[1.1] tracking-tight mb-5 max-w-2xl">
Our impact
</h1>
<p className="text-lg text-gray-600 leading-relaxed max-w-2xl">
Across our four focus areas we have named a small set of{' '}
<strong className="font-semibold text-black">inflection points</strong> — specific,
observable, falsifiable shifts we believe would be catalytic, and that have not yet
happened.
</p>
<a
href="#methodology"
className="mt-6 inline-flex items-center gap-2 text-gray-600 hover:text-gray-900 transition-colors font-medium text-[15px]"
>
Our methodology
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</a>
</div>
</div>

{/* Inflection points — grey full-bleed section to set it apart from the rest of the site */}
<section className="border-y border-gray-200 bg-gray-100">
<div className="max-w-6xl mx-auto px-6 py-14 lg:py-16">
<h2 className="text-xl lg:text-2xl font-semibold tracking-tight mb-2">
Inflection points we are tracking
</h2>
<p className="text-base text-gray-600 leading-relaxed max-w-3xl mb-8">
Select a focus area. Each card shows the defined threshold (did it happen), the cascade we
expect if it matters, and the PL contribution we would trace.
</p>
<ImpactDashboard liveOutputs={liveOutputs} marketSignals={marketSignals} />
</div>
</section>

{/* Our methodology */}
<div id="methodology" className="max-w-6xl mx-auto px-6 py-14 lg:py-16 scroll-mt-24">
<h2 className="text-xl lg:text-2xl font-semibold tracking-tight mb-2">Our methodology</h2>
<p className="text-base text-gray-600 leading-relaxed max-w-3xl mb-8">
For every inflection point we ask three questions — different jobs that should not be
collapsed into a single score. We hold two axes apart:{' '}
<strong className="font-semibold" style={{ color: HAND_COLOR }}>our hand</strong> (the work
we control) and{' '}
<strong className="font-semibold" style={{ color: FIELD_COLOR }}>the field</strong> (the
change that follows, with or without us).
</p>
<MeasuringQuestions />
</div>
</div>
)
}
9 changes: 7 additions & 2 deletions src/app/insights/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ const FALLBACK_CARDS = {
'card-blog': { title: 'Blog' },
}

export default async function InsightsPage() {
export default async function InsightsPage({
searchParams,
}: {
searchParams: Promise<{ area?: string }>
}) {
const { area: initialArea } = await searchParams
const page = await fetchPage('insights')
const hero = getSection(page, 'hero')
const heroTitle = hero?.title || FALLBACK_HERO_TITLE
Expand Down Expand Up @@ -201,7 +206,7 @@ export default async function InsightsPage() {
</div>
)}

<InsightsExplorer sections={insightSections} areas={areaDefs} />
<InsightsExplorer sections={insightSections} areas={areaDefs} initialArea={initialArea} />

<EditPageButton rkey="insights" />
</div>
Expand Down
Loading