Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 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
c5dc6c0
impact-v2: add acceleration-centered data model (field-velocity.ts)
lksbrssr Jul 29, 2026
39cb45f
impact-v2: add ImpactDashboardV2 (velocity basket + sensors + our hand)
lksbrssr Jul 29, 2026
45171d1
impact-v2: add MeasuringQuestionsV2 methodology section
lksbrssr Jul 29, 2026
74e2f8a
impact-v2: add /impact-v2 route (reframe hero + cross-link to /impact)
lksbrssr Jul 29, 2026
58e1a71
impact-v2: add side-by-side screenshots (both dashboards, light + dark)
lksbrssr Jul 29, 2026
8c2b1af
Merge latest main into impact-v2 (sync site chrome + Insights/radar u…
lksbrssr Jul 29, 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 @@ -25,3 +25,9 @@ NEXT_PUBLIC_COOKIE_CONSENT=on
# 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=
23 changes: 23 additions & 0 deletions docs/screenshots/README-impact-v2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Impact dashboard — v1 vs v2 (side-by-side)

Screenshots for the stacked PR that adds the acceleration-centered `/impact-v2`
alongside the unchanged inflection-point `/impact` (PR #29). Captured at ~1280×600.

## v2 — `/impact-v2` (acceleration reframe)

| File | What it shows |
|---|---|
| `v2-light-cards.png` / `v2-dark-cards.png` | Field card leads with the **velocity basket**; hand card shows tool → condition → observed movement |
| `v2-light-sensors.png` / `v2-dark-sensors.png` | "Our sensors: inflection points" list with status chips (incl. **RETIRED**, illustrative-draft caption) |
| `v2-light-sensor-modal.png` | **Retired** sensor modal — status + logged post-mortem |
| `v2-dark-sensor-modal.png` | **Missed · field moved** sensor modal — distinct amber post-mortem |
| `v2-dark-toolkit.png` | Methodology: the seven-tool toolkit with `releases:` tags + "needs co-funders" markers |
| `v2-dark-toolkit-modal.png` | Toolkit modal (Culture) — full description + what-changed + co-funder gap |
| `v2-light-howtoread-modal.png` | How-to-read v2 — X→Y contract + two-axis legend + five sensor-status legend |

## v1 — `/impact` (unchanged from PR #29)

| File | What it shows |
|---|---|
| `v1-light-cards.png` / `v1-dark-cards.png` | Original inflection-point cards (The field / Our hand / Live signal) |
| `v1-light-modal.png` / `v1-dark-modal.png` | Original stacked grey-card detail modal |
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.
Binary file added docs/screenshots/v1-dark-cards.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/v1-dark-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/v1-light-cards.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/v1-light-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/v2-dark-cards.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/v2-dark-sensor-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/v2-dark-sensors.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/v2-dark-toolkit-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/v2-dark-toolkit.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/v2-light-cards.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/v2-light-howtoread-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/v2-light-sensor-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/v2-light-sensors.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 @@ -95,6 +95,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 @@ -120,6 +129,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
87 changes: 87 additions & 0 deletions src/app/impact-v2/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import type { Metadata } from 'next'
import Breadcrumb from '@/components/Breadcrumb'
import ImpactDashboardV2 from '@/components/ImpactDashboardV2'
import MeasuringQuestionsV2 from '@/components/MeasuringQuestionsV2'
import { resolveAllSignals } from '@/lib/market-signals'
import { FIELD_COLOR, HAND_COLOR } from '@/lib/field-velocity'

// v2 of the impact dashboard — acceleration-centered. Reuses the same live
// crowd-forecast integration as v1 (resolveAllSignals) on the field axis.
export const revalidate = 60

export const metadata: Metadata = {
title: 'Impact (v2 — acceleration)',
description:
'A draft reframe of the impact dashboard: the target is field acceleration itself, measured by a velocity basket per focus area. Inflection points are demoted to pre-registered sensors.',
}

export default async function ImpactV2Page() {
const marketSignals = await resolveAllSignals()
return (
<div>
{/* Hero */}
<div className="max-w-6xl mx-auto px-6 pt-8">
<Breadcrumb items={[{ label: 'Impact (v2)' }]} />
<div className="pt-8 pb-10">
<span className="mb-4 inline-flex items-center rounded-full border border-gray-200 bg-gray-50 px-3 py-1 text-[11px] font-semibold uppercase tracking-wide text-gray-500">
Draft for review · acceleration reframe
</span>
<h1 className="text-2xl lg:text-[44px] font-semibold leading-[1.1] tracking-tight mb-5 max-w-2xl">
Is the field accelerating?
</h1>
<p className="text-lg text-gray-600 leading-relaxed max-w-2xl">
The target is <strong className="font-semibold text-black">field acceleration itself</strong> —
measured by a basket of velocity signals per focus area. Our{' '}
<strong className="font-semibold" style={{ color: HAND_COLOR }}>inflection points</strong> are
demoted to <strong className="font-semibold text-black">pre-registered sensors</strong>: dated,
falsifiable markers we expect acceleration to produce. Being wrong about a marker is a logged
learning event, not a failure — the markers stay on the board.
</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]"
>
How we read this
<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>
{/* Low-key cross-link back to the original methodology (v2 only). */}
<p className="mt-4 text-[13px] text-gray-400">
Original methodology (inflection-point framing):{' '}
<a href="/impact/" className="underline decoration-dotted underline-offset-2 hover:text-gray-700">
/impact
</a>
</p>
</div>
</div>

{/* Dashboard — 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">
Field acceleration, focus area by focus area
</h2>
<p className="text-base text-gray-600 leading-relaxed max-w-3xl mb-8">
Select a focus area. The field card leads with the velocity basket — is the field moving
faster — then lists our inflection-point sensors. The hand card shows the conditions we are
pushing on, and whether they moved.
</p>
<ImpactDashboardV2 marketSignals={marketSignals} />
</div>
</section>

{/* 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-10">
Three questions we hold apart — the field (
<strong className="font-semibold" style={{ color: FIELD_COLOR }}>Y</strong>), our hand (
<strong className="font-semibold" style={{ color: HAND_COLOR }}>X</strong>), and the attribution
that connects them.
</p>
<MeasuringQuestionsV2 />
</div>
</div>
)
}
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 @@ -28,7 +28,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 @@ -223,7 +228,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