;
+
+export const steps = [
+ {
+ title: "The rules",
+ text: "Tax and benefit law written as open code: income tax, National Insurance, Universal Credit, Child Benefit, Council Tax Reduction, VAT and more.",
+ },
+ {
+ title: "The households",
+ text: "A representative picture of UK households built from the Family Resources Survey, HMRC tax records and other surveys, matched to official statistics.",
+ },
+ {
+ title: "The results",
+ text: "Pick a policy and see who gains and loses: how much one family gains or loses, the cost to government, and the effects by income group, region and constituency.",
+ },
+];
+
+// What policyengine-uk 2.146.6 covers, read from its parameter tree
+// (policyengine_uk/parameters/gov). 732 of the 795 parameter files there carry
+// a legislative or guidance reference; the YAML tests hold over 4,000 cases.
+// Inheritance tax has no parameters or variables.
+export const coverage = [
+ {
+ title: "Taxes on income",
+ items: [
+ "Income tax, with Scotland's own rates",
+ "National Insurance",
+ "Capital gains tax",
+ "Student loan repayments",
+ ],
+ },
+ {
+ title: "Taxes on spending and property",
+ items: [
+ "VAT, fuel, alcohol and tobacco duties",
+ "Council tax, and domestic rates in Northern Ireland",
+ "Stamp duty, and its Scottish and Welsh equivalents",
+ "Business rates",
+ ],
+ },
+ {
+ title: "Benefits",
+ items: [
+ "Universal Credit and Pension Credit",
+ "Child Benefit and the State Pension",
+ "Disability and carer benefits",
+ "Scottish Child Payment and the benefit cap",
+ ],
+ },
+ {
+ title: "Childcare and students",
+ items: [
+ "Tax-Free Childcare",
+ "Funded childcare hours in England and Scotland",
+ "Student maintenance support",
+ ],
+ },
+];
+
+// The Microcosm UK build, told for the whole UK: what each stage adds and
+// which sources feed it (sources.yaml and the national target references at
+// the pinned commit; the target file held 1,230 rows on 4 October, 1,231 on
+// 8 October and 1,322 on 22 October, so the slide says "over 1,000").
+export const pipelineStages = [
+ {
+ title: "Survey households",
+ adds: "People, families, earnings and benefits",
+ // A non-breaking hyphen keeps "2024-25" on one line.
+ sources: ["Family Resources Survey 2024\u201125 (DWP)"],
+ },
+ {
+ title: "Add tax records",
+ adds: "High and detailed incomes",
+ sources: ["Survey of Personal Incomes (HMRC)"],
+ },
+ {
+ title: "Fill the gaps",
+ adds: "Wealth, spending, travel and capital gains",
+ sources: [
+ "Wealth and Assets Survey",
+ "Living Costs and Food Survey",
+ "National Travel Survey",
+ "HMRC capital gains statistics",
+ ],
+ },
+ {
+ title: "Place in local areas",
+ adds: "For the local-area dataset: a small area, council and constituency for each household",
+ sources: ["Census small areas (ONS, NRS and NISRA)"],
+ },
+ {
+ title: "Match official totals",
+ adds: "Reweight households to over 1,000 UK statistics",
+ sources: ["ONS, HMRC, DWP, OBR and other official statistics"],
+ },
+];
+
+// UK calibration targets at the pinned Microcosm commit, grouped from the
+// 31 target families in the national target references (1,231 rows) and the
+// six in the local ones (20,885 rows). "12 regions" means England's nine
+// regions plus Wales, Scotland and Northern Ireland; Universal Credit,
+// State Pension and most DWP rows are for Great Britain, with Northern
+// Ireland's from the Department for Communities. The local-area dataset binds
+// every national row as well as its own (microcosm full_targets.py: the local
+// line binds the national register), so its column adds the two.
+export type TargetGroup = {
+ targets: string;
+ level: string;
+ source: string;
+ national: number;
+ local: number;
+};
+
+export const targetGroups: TargetGroup[] = [
+ { targets: "Population by age", level: "UK, 12 regions, constituencies, LAs", source: "ONS", national: 149, local: 149 + 8072 },
+ { targets: "Households, by type and number", level: "UK, constituencies, LAs", source: "ONS, Census", national: 10, local: 10 + 1011 },
+ { targets: "Taxpayers, income and tax, by band", level: "UK, 12 regions, constituencies, LAs", source: "HMRC personal income statistics", national: 562, local: 562 + 4031 },
+ { targets: "Capital gains and gains taxpayers", level: "UK, 12 regions", source: "HMRC", national: 105, local: 105 },
+ { targets: "Salary sacrifice and Tax-Free Childcare", level: "UK", source: "HMRC", national: 10, local: 10 },
+ { targets: "Tax receipts and benefit spending", level: "UK", source: "OBR", national: 23, local: 23 },
+ { targets: "Universal Credit households", level: "Great Britain, Scotland, constituencies, LAs", source: "DWP", national: 118, local: 118 + 3508 },
+ { targets: "Pensions and Pension Credit", level: "Great Britain, 12 regions", source: "DWP, DfC (Northern Ireland)", national: 74, local: 74 },
+ { targets: "Other benefits, benefit cap, two-child limit", level: "Great Britain", source: "DWP", national: 43, local: 43 },
+ { targets: "Council tax dwellings, by band", level: "English regions, Wales, Scotland, LAs", source: "MHCLG and devolved governments", national: 100, local: 100 + 2511 },
+ { targets: "Housing tenure and private rents", level: "LAs", source: "ONS", national: 0, local: 1752 },
+ { targets: "Student loans and funded childcare", level: "England", source: "Student Loans Company, DfE", national: 18, local: 18 },
+ { targets: "Bus and rail support", level: "Nations, English regions", source: "DfT, ORR, devolved governments", national: 10, local: 10 },
+ { targets: "Land values, energy spending, other totals", level: "UK, Scotland", source: "ONS, ISC, Scottish Government", national: 9, local: 9 },
+];
+
+/** Rows the local-area dataset binds for constituencies and local authorities. */
+export const localAreaRows = 20885;
+
+export type Card = {
+ title: string;
+ date: string;
+ text: string;
+ href: string;
+ image: string;
+ alt: string;
+};
+
+const shot = (file: string) => `/screenshots/good-growth-foundation-2026/${file}`;
+
+// The eight newest UK items on policyengine.org/uk/research on 23 October
+// 2026, newest first; covers come from policyengine-app-v2's
+// website/public/assets/posts.
+export const publications: Card[] = [
+ {
+ title: "The Burnham plan vs the triple lock",
+ date: "16 October 2026",
+ text: "Budget impact to 2039-40, forecast paths and which households have less income.",
+ href: "https://www.policyengine.org/uk/triple-lock",
+ image: shot("uk-triple-lock.jpg"),
+ alt: "Cover image for the Burnham plan and triple lock analysis",
+ },
+ {
+ title: "Energy bills and the Autumn Budget 2026",
+ date: "14 October 2026",
+ text: "What households will pay for energy, and what support options would cost and reach.",
+ href: "https://www.policyengine.org/uk/energy-autumn-budget",
+ image: shot("energy-autumn-budget.jpg"),
+ alt: "Cover image for the energy bills and Autumn Budget 2026 dashboard",
+ },
+ {
+ title: "Our microsimulation package, published in JOSS",
+ date: "12 October 2026",
+ text: "The peer-reviewed paper on policyengine, the Python package behind PolicyEngine.",
+ href: "https://www.policyengine.org/uk/research/policyengine-joss-paper",
+ image: shot("policyengine-joss-paper-cover.webp"),
+ alt: "Cover image for the Journal of Open Source Software paper post",
+ },
+ {
+ title: "Targeted energy bill discount",
+ date: "5 October 2026",
+ text: "Cost, reach and distributional effects of the Resolution Foundation's proposal.",
+ href: "https://www.policyengine.org/uk/targeted-energy-discount",
+ image: shot("targeted-energy-discount.jpg"),
+ alt: "Cover image for the targeted energy bill discount dashboard",
+ },
+ {
+ title: "The Middle East war and UK living standards",
+ date: "25 September 2026",
+ text: "Energy, fuel and food price rises in 2027-28, with ten policy responses compared.",
+ href: "https://www.policyengine.org/uk/middle-east-war-living-standards",
+ image: shot("middle-east-war-living-standards.jpg"),
+ alt: "Cover image for the Middle East war and living standards dashboard",
+ },
+ {
+ title: "Marriage calculator",
+ date: "11 September 2026",
+ text: "A couple's taxes and benefits living together, against living apart.",
+ href: "https://www.policyengine.org/uk/marriage",
+ image: shot("marriage-calculator-uk.jpg"),
+ alt: "Cover image for the UK marriage calculator",
+ },
+ {
+ title: "Free childcare hours and a 75% subsidy",
+ date: "3 September 2026",
+ text: "15 free hours from nine months, with a subsidy replacing Tax-Free Childcare.",
+ href: "https://www.policyengine.org/uk/free-childcare-reform",
+ image: shot("free-childcare-reform.jpg"),
+ alt: "Cover image for the free childcare reform dashboard",
+ },
+ {
+ title: "Who bears the AI shock?",
+ date: "4 August 2026",
+ text: "AI employment, wage and capital shocks traced through the tax-benefit system.",
+ href: "https://www.policyengine.org/uk/research/uk-ai-study",
+ image: shot("uk-ai-study.jpg"),
+ alt: "Cover image for the UK AI incidence study",
+ },
+];
+
+// Targeted energy bill discount (uk-energy-reforms main 14bf3b0): the RF flat
+// option in 2026-27 on the Microcosm UK 2024-25 national release,
+// policyengine-uk 2.102.3. Figures match the dashboard's data file
+// (dashboard/public/data/targeted_energy_discount_results.json). Regions are
+// Great Britain's eleven; each rests on an effective sample of 273 to 734
+// households. Poverty changes are left off the slide.
+export const energyHeadline = {
+ households: "28.6m",
+ recipients: "11.95m",
+ share: "42%",
+ cost: "£2.09bn",
+};
+
+export const energyRegions = [
+ { region: "North East", passported: 0.391, eligible: 0.547 },
+ { region: "West Midlands", passported: 0.31, eligible: 0.482 },
+ { region: "Wales", passported: 0.294, eligible: 0.467 },
+ { region: "Yorkshire and the Humber", passported: 0.288, eligible: 0.458 },
+ { region: "North West", passported: 0.293, eligible: 0.452 },
+ { region: "Scotland", passported: 0.241, eligible: 0.438 },
+ { region: "East Midlands", passported: 0.249, eligible: 0.435 },
+ { region: "South West", passported: 0.227, eligible: 0.401 },
+ { region: "East of England", passported: 0.211, eligible: 0.364 },
+ { region: "South East", passported: 0.203, eligible: 0.356 },
+ { region: "London", passported: 0.297, eligible: 0.353 },
+];
+
+// Great Britain, from the same run: deciles rank people by household income
+// after housing costs, adjusted for household size. Average gains include
+// households that receive nothing.
+export const energyDeciles = [
+ { decile: 1, averageGain: 153, shareReceiving: 0.87 },
+ { decile: 2, averageGain: 147, shareReceiving: 0.84 },
+ { decile: 3, averageGain: 120, shareReceiving: 0.68 },
+ { decile: 4, averageGain: 108, shareReceiving: 0.62 },
+ { decile: 5, averageGain: 78, shareReceiving: 0.44 },
+ { decile: 6, averageGain: 53, shareReceiving: 0.3 },
+ { decile: 7, averageGain: 39, shareReceiving: 0.22 },
+ { decile: 8, averageGain: 15, shareReceiving: 0.08 },
+ { decile: 9, averageGain: 9, shareReceiving: 0.05 },
+ { decile: 10, averageGain: 6, shareReceiving: 0.03 },
+];
+
+// The Autumn Budget webinar, as announced on the policyengine.org home page
+// banner and in the 21 October UK newsletter. The QR code encodes this exact
+// link; it was generated locally with segno (version 5, error correction M).
+export const webinar = {
+ title: "Autumn Budget 2026: PolicyEngine's same-day first look",
+ when: "Wednesday 28 October, 4:30pm UK time",
+ text: "A free online webinar on the Budget's main tax and benefit measures and what they mean for households.",
+ href: "https://us06web.zoom.us/meeting/register/2DLUeZAbRwKkKEK-2JrFrQ#/registration",
+ qr: "/images/good-growth-foundation-2026/qr-autumn-budget-webinar.svg",
+};
+
+export type User = {
+ name: string;
+ initials: string;
+ logo?: string;
+ source?: Source;
+};
+
+// From the introduction deck's community slide, without the Good Growth
+// Foundation itself, plus NIESR. Logos come from the introduction deck's
+// branch (House of Lords included) and policyengine-app-v2 (NIESR).
+export const users: User[] = [
+ {
+ name: "No 10 data science team",
+ logo: "/logos/orgs/10-downing-street.png",
+ initials: "10",
+ source: sources.no10Post,
+ },
+ {
+ name: "House of Lords",
+ logo: "/logos/orgs/house-of-lords.svg",
+ initials: "HL",
+ source: sources.hansard,
+ },
+ {
+ name: "Tax Policy Associates",
+ logo: "/logos/orgs/tax-policy-associates.jpg",
+ initials: "TPA",
+ source: sources.tpa,
+ },
+ {
+ name: "Centre for Policy Studies",
+ logo: "/logos/orgs/centre-for-policy-studies.png",
+ initials: "CPS",
+ source: sources.cps,
+ },
+ {
+ name: "New Economics Foundation",
+ logo: "/logos/orgs/new-economics-foundation.png",
+ initials: "NEF",
+ source: sources.nef,
+ },
+ {
+ name: "WPI Economics",
+ logo: "/logos/orgs/wpi-economics.svg",
+ initials: "WPI",
+ },
+ {
+ name: "Nuffield Foundation",
+ logo: "/logos/orgs/nuffield-foundation.svg",
+ initials: "NF",
+ source: sources.nuffield,
+ },
+ {
+ name: "National Institute of Economic and Social Research",
+ logo: "/logos/orgs/niesr.png",
+ initials: "NIESR",
+ source: sources.niesr,
+ },
+];
diff --git a/slideshows/good-growth-foundation-2026/slides/deck.module.css b/slideshows/good-growth-foundation-2026/slides/deck.module.css
new file mode 100644
index 00000000..4a9ad19d
--- /dev/null
+++ b/slideshows/good-growth-foundation-2026/slides/deck.module.css
@@ -0,0 +1,1205 @@
+/* Leave space for the viewer's controls beside the meeting date. */
+.deck :global(.gradient-footer) {
+ padding-right: 15rem;
+}
+
+.takeaway {
+ margin-top: 1.75rem;
+ border-left: 4px solid var(--pe-teal);
+ padding-left: 1.25rem;
+ color: var(--pe-dark);
+ font-size: 1.5rem;
+ line-height: 1.4;
+}
+
+/* Figure slides: centre the body below the title, clear of the sources line. */
+.centerBody {
+ display: flex;
+ min-height: 0;
+ flex: 1;
+ flex-direction: column;
+ justify-content: center;
+ padding-bottom: 1.5rem;
+}
+
+/* Data pipeline: sources feed each stage from above. */
+.pipeline {
+ border: 1px solid var(--color-gray-200);
+ border-radius: 0.75rem;
+ background: white;
+ padding: 1.75rem;
+ box-shadow: 0 1px 2px rgb(0 0 0 / 0.05);
+}
+
+.pipelineHeader {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 1.25rem;
+ font-size: 0.9rem;
+ font-weight: 700;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+}
+
+.pipelineGrid {
+ display: grid;
+ grid-template-columns: repeat(5, minmax(0, 1fr));
+ column-gap: 1.75rem;
+}
+
+.sourceBox {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ gap: 0.35rem;
+ border: 1px solid var(--color-gray-200);
+ border-radius: 0.5rem;
+ background: var(--bg-secondary);
+ padding: 0.85rem 1rem;
+ color: var(--color-gray-700);
+ font-size: 1.05rem;
+ line-height: 1.3;
+ text-align: center;
+}
+
+.feed {
+ position: relative;
+ height: 2.25rem;
+}
+
+.feed::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ bottom: 7px;
+ left: 50%;
+ width: 2px;
+ background: var(--color-gray-300);
+ transform: translateX(-50%);
+}
+
+.feed::after {
+ content: "";
+ position: absolute;
+ bottom: 0;
+ left: 50%;
+ border-top: 8px solid var(--color-gray-400);
+ border-right: 6px solid transparent;
+ border-left: 6px solid transparent;
+ transform: translateX(-50%);
+}
+
+.stageBox {
+ position: relative;
+ min-height: 10rem;
+ border: 2px solid var(--pe-teal-dark);
+ border-radius: 0.6rem;
+ background: var(--pe-light);
+ padding: 1.1rem 1rem;
+ text-align: center;
+}
+
+.stageBox:not(.stageBoxLast)::after {
+ content: "";
+ position: absolute;
+ top: 50%;
+ left: 100%;
+ width: calc(1.75rem + 2px);
+ height: 2px;
+ background: var(--color-gray-300);
+}
+
+.stageBoxLast {
+ border-color: var(--pe-amber);
+ background: color-mix(in srgb, var(--pe-amber) 10%, white);
+}
+
+.stageNumber {
+ display: inline-flex;
+ width: 2rem;
+ height: 2rem;
+ align-items: center;
+ justify-content: center;
+ border-radius: 9999px;
+ background: var(--pe-teal-dark);
+ color: white;
+ font-size: 0.95rem;
+ font-weight: 700;
+}
+
+.stageBoxLast .stageNumber {
+ background: var(--pe-amber);
+}
+
+.stageBox h2 {
+ margin-top: 0.6rem;
+ color: var(--pe-dark);
+ font-size: 1.4rem;
+ font-weight: 800;
+ line-height: 1.2;
+}
+
+.stageBox p {
+ margin-top: 0.45rem;
+ color: var(--color-gray-600);
+ font-size: 1.05rem;
+ line-height: 1.35;
+}
+
+.pipelineFooter {
+ margin-top: 1.5rem;
+ border: 1px solid var(--color-gray-300);
+ border-radius: 0.5rem;
+ background: var(--bg-secondary);
+ padding: 0.9rem 1.5rem;
+ color: var(--color-gray-500);
+ font-size: 1.2rem;
+ text-align: center;
+}
+
+.pipelineFooter strong {
+ color: var(--pe-dark);
+}
+
+.finePrint {
+ margin-top: 1.25rem;
+ color: var(--color-gray-500);
+ font-size: 0.95rem;
+}
+
+/* Energy discount charts, drawn in HTML. Colours follow the dashboard. */
+.energyStats {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ gap: 2rem;
+ margin-bottom: 1.75rem;
+ color: var(--color-gray-600);
+ font-size: 1.3rem;
+}
+
+.energyStats strong {
+ color: var(--pe-dark);
+}
+
+.energyGrid {
+ display: grid;
+ grid-template-columns: 1.15fr 1fr;
+ align-items: start;
+ gap: 3.5rem;
+}
+
+.chart figcaption h2 {
+ color: var(--pe-dark);
+ font-size: 1.35rem;
+ font-weight: 700;
+}
+
+.legend {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.25rem 1.25rem;
+ margin-top: 0.4rem;
+ color: var(--color-gray-600);
+ font-size: 0.95rem;
+}
+
+.legend i {
+ display: inline-block;
+ width: 0.8rem;
+ height: 0.8rem;
+ margin-right: 0.4rem;
+ border-radius: 2px;
+ vertical-align: -0.05rem;
+}
+
+.swatchPassported,
+.barPassported {
+ background: var(--color-teal-700);
+}
+
+.swatchIncome,
+.barIncome {
+ background: var(--color-teal-400);
+}
+
+.chartNote {
+ margin-top: 0.4rem;
+ color: var(--color-gray-500);
+ font-size: 0.95rem;
+}
+
+.regionRows {
+ display: grid;
+ gap: 0.3rem;
+ margin-top: 1rem;
+}
+
+.regionRow {
+ display: grid;
+ grid-template-columns: 14rem 1fr;
+ align-items: center;
+ gap: 0.75rem;
+ border-radius: 0.3rem;
+ padding: 0.1rem 0.4rem;
+ font-size: 1.02rem;
+}
+
+.regionLabel {
+ overflow: hidden;
+ color: var(--color-gray-700);
+ text-align: right;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.regionTrack {
+ display: flex;
+ height: 1.35rem;
+ align-items: center;
+ gap: 2px;
+}
+
+.regionTrack > span:not(.regionValue) {
+ height: 100%;
+}
+
+.barIncome {
+ border-radius: 0 4px 4px 0;
+}
+
+.regionValue {
+ margin-left: 0.5rem;
+ color: var(--color-gray-700);
+ font-size: 0.95rem;
+ font-variant-numeric: tabular-nums;
+ font-weight: 600;
+}
+
+.decilePlot {
+ display: grid;
+ grid-template-columns: repeat(10, minmax(0, 1fr));
+ align-items: end;
+ gap: 0.6rem;
+ height: 17rem;
+ margin-top: 1rem;
+ border-bottom: 1px solid var(--color-gray-300);
+}
+
+.decileColumn {
+ display: flex;
+ height: 100%;
+ flex-direction: column;
+ align-items: center;
+ justify-content: flex-end;
+}
+
+.decileValue {
+ margin-bottom: 0.3rem;
+ color: var(--color-gray-700);
+ font-size: 0.95rem;
+ font-variant-numeric: tabular-nums;
+ font-weight: 600;
+}
+
+.decileBar {
+ width: 72%;
+ border-radius: 4px 4px 0 0;
+ background: var(--pe-teal);
+}
+
+.decileAxis {
+ display: grid;
+ grid-template-columns: repeat(10, minmax(0, 1fr));
+ gap: 0.6rem;
+ margin-top: 0.35rem;
+ color: var(--color-gray-600);
+ font-size: 0.95rem;
+ text-align: center;
+}
+
+.axisCaption {
+ margin-top: 0.3rem;
+ color: var(--color-gray-500);
+ font-size: 0.95rem;
+ text-align: center;
+}
+
+/* Global `a { color: inherit }` outranks Tailwind's layered colour utilities,
+ so the link takes its colour from here. */
+.dashboardLink {
+ flex-shrink: 0;
+ color: var(--pe-teal);
+ font-size: 1.15rem;
+ font-weight: 600;
+}
+
+/* Compact figures for laptop screens, up to a 1440 x 900 display. */
+@media (max-height: 1000px) {
+
+ .pipeline {
+ padding: 1rem 1.25rem;
+ }
+
+ .pipelineHeader {
+ margin-bottom: 0.6rem;
+ font-size: 0.75rem;
+ }
+
+ .pipelineGrid {
+ column-gap: 1.25rem;
+ }
+
+ .stageBox:not(.stageBoxLast)::after {
+ width: calc(1.25rem + 2px);
+ }
+
+ .sourceBox {
+ gap: 0.2rem;
+ padding: 0.55rem 0.6rem;
+ font-size: 0.85rem;
+ }
+
+ .feed {
+ height: 1.3rem;
+ }
+
+ .stageBox {
+ min-height: 6.5rem;
+ padding: 0.65rem 0.6rem;
+ }
+
+ .stageNumber {
+ width: 1.6rem;
+ height: 1.6rem;
+ font-size: 0.8rem;
+ }
+
+ .stageBox h2 {
+ margin-top: 0.4rem;
+ font-size: 1.1rem;
+ }
+
+ .stageBox p {
+ margin-top: 0.3rem;
+ font-size: 0.88rem;
+ }
+
+ .pipelineFooter {
+ margin-top: 0.75rem;
+ padding: 0.5rem 1rem;
+ font-size: 1rem;
+ }
+
+ .energyStats {
+ font-size: 1.15rem;
+ }
+
+ .energyStats {
+ margin-bottom: 1rem;
+ }
+
+ .chart figcaption h2 {
+ font-size: 1.15rem;
+ }
+
+ .legend,
+ .chartNote,
+ .decileValue,
+ .decileAxis,
+ .axisCaption,
+ .regionValue {
+ font-size: 0.85rem;
+ }
+
+ .regionRows {
+ gap: 0.1rem;
+ margin-top: 0.5rem;
+ }
+
+ .regionRow {
+ font-size: 0.88rem;
+ }
+
+ .regionTrack {
+ height: 1rem;
+ }
+
+ .decilePlot {
+ height: 11rem;
+ margin-top: 0.75rem;
+ }
+
+ .dashboardLink {
+ font-size: 1rem;
+ }
+}
+
+@media (max-height: 760px) {
+
+ .pipeline {
+ padding: 0.85rem 1rem;
+ }
+
+ .pipelineHeader {
+ margin-bottom: 0.5rem;
+ font-size: 0.7rem;
+ }
+
+ .sourceBox {
+ padding: 0.45rem 0.5rem;
+ font-size: 0.8rem;
+ }
+
+ .feed {
+ height: 1.1rem;
+ }
+
+ .stageBox {
+ min-height: 6rem;
+ padding: 0.55rem;
+ }
+
+ .stageBox h2 {
+ margin-top: 0.3rem;
+ font-size: 1rem;
+ }
+
+ .stageBox p {
+ font-size: 0.82rem;
+ }
+
+ .pipelineFooter {
+ margin-top: 0.6rem;
+ padding: 0.4rem 1rem;
+ font-size: 0.9rem;
+ }
+
+ .energyStats {
+ font-size: 1rem;
+ }
+
+ .energyStats {
+ margin-bottom: 0.6rem;
+ }
+
+ .chart figcaption h2 {
+ font-size: 1.1rem;
+ }
+
+ .legend,
+ .chartNote,
+ .decileValue,
+ .decileAxis,
+ .axisCaption,
+ .regionValue {
+ font-size: 0.8rem;
+ }
+
+ .regionRows {
+ gap: 0.12rem;
+ margin-top: 0.5rem;
+ }
+
+ .regionRow {
+ grid-template-columns: 11.5rem 1fr;
+ font-size: 0.82rem;
+ }
+
+ .regionTrack {
+ height: 0.95rem;
+ }
+
+ .decilePlot {
+ height: 10.5rem;
+ margin-top: 0.6rem;
+ }
+
+ .dashboardLink {
+ font-size: 0.9rem;
+ }
+}
+
+/* Title slide: keep the logo, speakers and date clear of each other on laptops. */
+@media (max-height: 760px) {
+ .titleCover {
+ padding-top: 6rem !important;
+ }
+
+ .titleCover > :global(*) {
+ margin-bottom: 1.25rem !important;
+ }
+
+ .titleCover > :global(:first-child) {
+ display: none;
+ }
+
+ .titleCover h1 {
+ font-size: 2.25rem;
+ }
+
+ .titleCover :global(.w-28) {
+ width: 5rem;
+ height: 5rem;
+ }
+}
+
+/* Keep the shared slide template legible on a laptop during the meeting. */
+@media (max-height: 850px) {
+ .deck > :global(.absolute.inset-0) {
+ padding-top: 3rem;
+ }
+
+ .deck :global(.mb-10) {
+ margin-bottom: 1.5rem;
+ }
+
+ .deck h1 {
+ font-size: 2.625rem;
+ }
+
+ .deck :global(.mt-12) {
+ margin-top: 2rem;
+ }
+
+ .deck :global(.mt-10),
+ .deck :global(.mt-9) {
+ margin-top: 1.5rem;
+ }
+
+ .deck :global(.mt-8),
+ .deck :global(.mt-7) {
+ margin-top: 1.25rem;
+ }
+
+ .deck :global(.mt-6) {
+ margin-top: 0.5rem;
+ }
+
+ .deck th,
+ .deck td {
+ padding-top: 0.625rem;
+ padding-bottom: 0.625rem;
+ }
+
+ .deck :global(.space-y-6) > :not(:last-child) {
+ margin-bottom: 1rem;
+ }
+
+ .deck :global(.space-y-4) > :not(:last-child) {
+ margin-bottom: 0.5rem;
+ }
+
+ .deck :global(.pb-5) {
+ padding-bottom: 1rem;
+ }
+
+ .deck :global(.pb-3) {
+ padding-bottom: 0.5rem;
+ }
+
+ .deck :global(.space-y-5) > :not(:last-child) {
+ margin-bottom: 0.75rem;
+ }
+
+ .deck :global(.space-y-3) > :not(:last-child) {
+ margin-bottom: 0.5rem;
+ }
+}
+
+/* Safari on a laptop leaves less than 760px for the slide. */
+@media (max-height: 760px) {
+ .deck > :global(.absolute.inset-0) {
+ padding-top: 2rem;
+ }
+
+ .deck :global(.mb-10) {
+ margin-bottom: 1rem;
+ }
+
+ .deck h1 {
+ font-size: 2.25rem;
+ }
+
+ .deck :global(.text-xl) {
+ font-size: 1.05rem;
+ line-height: 1.45rem;
+ }
+
+ .deck :global(.text-2xl) {
+ font-size: 1.25rem;
+ line-height: 1.7rem;
+ }
+
+ .deck :global(.text-lg) {
+ font-size: 0.95rem;
+ line-height: 1.35rem;
+ }
+
+ .deck :global(.text-base) {
+ font-size: 0.85rem;
+ line-height: 1.2rem;
+ }
+
+ .deck th,
+ .deck td {
+ padding-top: 0.35rem;
+ padding-bottom: 0.35rem;
+ }
+
+ .deck :global(.pb-4) {
+ padding-bottom: 0.5rem;
+ }
+
+ .deck :global(.space-y-5) > :not(:last-child) {
+ margin-bottom: 0.5rem;
+ }
+
+ .deck :global(.pt-5) {
+ padding-top: 0.75rem;
+ }
+
+ .deck :global(.mt-6) {
+ margin-top: 0.75rem;
+ }
+
+ .deck :global(.p-4) {
+ padding: 0.6rem 0.75rem;
+ }
+}
+
+/* Global `a { color: inherit }` outranks Tailwind's colour utilities, so the
+ open-code banner on slide 2 takes its text colour from here. */
+.codeBanner {
+ color: var(--pe-dark);
+}
+
+/* Model coverage: one short list per family of rules. */
+.coverageList {
+ margin-top: 0.75rem;
+ display: grid;
+ gap: 0.55rem;
+ color: var(--color-gray-700);
+ font-size: 1.15rem;
+ line-height: 1.35;
+}
+
+.coverageList li {
+ position: relative;
+ padding-left: 1rem;
+}
+
+.coverageList li::before {
+ content: "";
+ position: absolute;
+ top: 0.6em;
+ left: 0;
+ width: 0.4rem;
+ height: 0.4rem;
+ border-radius: 9999px;
+ background: var(--pe-teal);
+}
+
+/* Community grid: logos share one height; initials stand in for a mark we
+ cannot use. */
+.communityLogo {
+ height: 64px;
+}
+
+.communityInitials {
+ display: flex;
+ width: 4rem;
+ align-items: center;
+ justify-content: center;
+ border-radius: 0.5rem;
+ background: color-mix(in srgb, var(--pe-teal) 10%, white);
+ color: var(--pe-teal);
+ font-size: 1rem;
+ font-weight: 700;
+}
+
+@media (max-height: 1000px) {
+ .coverageList {
+ gap: 0.4rem;
+ font-size: 1rem;
+ }
+
+ .takeaway {
+ margin-top: 1.25rem;
+ font-size: 1.25rem;
+ }
+}
+
+@media (max-height: 760px) {
+ .coverageList {
+ margin-top: 0.5rem;
+ gap: 0.3rem;
+ font-size: 0.9rem;
+ }
+
+ .takeaway {
+ margin-top: 0.9rem;
+ font-size: 1.05rem;
+ }
+
+ .finePrint {
+ margin-top: 0.6rem;
+ font-size: 0.85rem;
+ }
+
+ .communityLogo {
+ height: 44px;
+ }
+
+ .communityInitials {
+ width: 3rem;
+ font-size: 0.85rem;
+ }
+}
+
+/* Microsimulation walkthrough: one table that fills in over six steps. Cells
+ not yet revealed keep their space, so the table never jumps. */
+.microsim {
+ margin-top: 0.5rem;
+}
+
+.msGroups,
+.msRow {
+ display: grid;
+ grid-template-columns: repeat(8, minmax(0, 1fr));
+ column-gap: 0.4rem;
+}
+
+.msGroups {
+ margin-bottom: 0.6rem;
+}
+
+.msGroup {
+ border-radius: 0.4rem;
+ background: color-mix(in srgb, var(--pe-teal) 75%, white);
+ padding: 0.35rem 0.5rem;
+ color: white;
+ font-size: 1.05rem;
+ font-weight: 700;
+ text-align: center;
+}
+
+.msTable {
+ display: grid;
+ row-gap: 0.3rem;
+}
+
+.msHead {
+ display: flex;
+ min-height: 3.6rem;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ border-radius: 0.3rem;
+ padding: 0.35rem 0.4rem;
+ color: white;
+ font-size: 1rem;
+ font-weight: 600;
+ line-height: 1.2;
+ text-align: center;
+}
+
+.msHead em {
+ font-size: 0.8rem;
+ font-weight: 400;
+ opacity: 0.85;
+}
+
+.msHead_household,
+.msHead_weight {
+ background: var(--pe-dark);
+}
+
+.msHead_baseline {
+ background: var(--color-gray-600);
+}
+
+.msHead_reform {
+ background: var(--color-teal-700);
+}
+
+.msCell {
+ border-radius: 0.3rem;
+ padding: 0.45rem 0.4rem;
+ color: var(--color-gray-700);
+ font-size: 1.1rem;
+ font-variant-numeric: tabular-nums;
+ text-align: center;
+}
+
+.msCell_baseline {
+ background: var(--color-gray-100);
+}
+
+.msCell_reform {
+ background: color-mix(in srgb, var(--pe-teal) 10%, white);
+}
+
+.msChange {
+ color: var(--pe-dark);
+ font-weight: 700;
+}
+
+.msEllipsis {
+ padding-top: 0;
+ padding-bottom: 0;
+ background: transparent;
+ color: var(--color-gray-400);
+}
+
+.msTotal .msCell {
+ border-top: 2px solid var(--color-gray-300);
+ color: var(--pe-dark);
+ font-weight: 700;
+}
+
+.msHidden {
+ visibility: hidden;
+}
+
+.msGroup:not(.msHidden),
+.msHead:not(.msHidden),
+.msCell:not(.msHidden) {
+ animation: msReveal 300ms ease-out;
+}
+
+@keyframes msReveal {
+ from {
+ opacity: 0;
+ transform: translateY(4px);
+ }
+
+ to {
+ opacity: 1;
+ transform: none;
+ }
+}
+
+.msCaption {
+ display: flex;
+ align-items: center;
+ gap: 0.9rem;
+ margin-top: 1.5rem;
+ border-left: 4px solid var(--pe-teal);
+ padding-left: 1rem;
+ color: var(--pe-dark);
+ font-size: 1.4rem;
+ line-height: 1.35;
+}
+
+.msCaption span {
+ display: inline-flex;
+ width: 2.1rem;
+ height: 2.1rem;
+ flex-shrink: 0;
+ align-items: center;
+ justify-content: center;
+ border-radius: 9999px;
+ background: var(--pe-teal);
+ color: white;
+ font-size: 1rem;
+ font-weight: 700;
+}
+
+.msNote {
+ margin-top: 0.9rem;
+ color: var(--color-gray-500);
+ font-size: 0.95rem;
+}
+
+/* Calibration targets table. */
+.targetTable {
+ overflow: hidden;
+ border: 1px solid var(--color-gray-200);
+ border-radius: 0.5rem;
+}
+
+.targetRow {
+ display: grid;
+ grid-template-columns: 2.3fr 2.3fr 1.8fr 0.9fr 0.9fr;
+ align-items: baseline;
+ gap: 1rem;
+ border-top: 1px solid var(--color-gray-100);
+ padding: 0.4rem 1.25rem;
+ color: var(--color-gray-700);
+ font-size: 1.02rem;
+ line-height: 1.3;
+}
+
+.targetHead {
+ border-top: 0;
+ background: var(--bg-secondary);
+ color: var(--pe-dark);
+ font-size: 0.78rem;
+ font-weight: 700;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+}
+
+.targetTotal {
+ border-top: 2px solid var(--color-gray-200);
+ background: var(--pe-light);
+ color: var(--pe-dark);
+ font-weight: 700;
+}
+
+
+
+/* Recent research: a 4 x 2 grid of cards with even covers. */
+.compactCover {
+ aspect-ratio: 16 / 9;
+ height: auto;
+ max-height: 15vh;
+}
+
+@media (max-height: 1000px) {
+ .msGroup {
+ padding: 0.25rem 0.4rem;
+ font-size: 0.92rem;
+ }
+
+ .msHead {
+ min-height: 3rem;
+ font-size: 0.88rem;
+ }
+
+ .msHead em {
+ font-size: 0.72rem;
+ }
+
+ .msCell {
+ padding: 0.32rem 0.35rem;
+ font-size: 0.98rem;
+ }
+
+ .msCaption {
+ margin-top: 1rem;
+ font-size: 1.2rem;
+ }
+
+ .msNote {
+ margin-top: 0.6rem;
+ font-size: 0.88rem;
+ }
+
+ .targetRow {
+ padding: 0.2rem 1.1rem;
+ font-size: 0.9rem;
+ }
+
+ .targetHead {
+ font-size: 0.7rem;
+ }
+
+
+ .compactCover {
+ max-height: 12vh;
+ }
+}
+
+@media (max-height: 760px) {
+ .msGroups {
+ margin-bottom: 0.4rem;
+ }
+
+ .msHead {
+ min-height: 2.6rem;
+ font-size: 0.8rem;
+ }
+
+ .msCell {
+ padding: 0.25rem 0.3rem;
+ font-size: 0.9rem;
+ }
+
+ .msCaption {
+ margin-top: 0.7rem;
+ font-size: 1.05rem;
+ }
+
+ .msCaption span {
+ width: 1.7rem;
+ height: 1.7rem;
+ font-size: 0.85rem;
+ }
+
+ .msNote {
+ font-size: 0.8rem;
+ }
+
+ .targetRow {
+ gap: 0.75rem;
+ padding: 0.15rem 1rem;
+ font-size: 0.8rem;
+ }
+
+ .targetHead {
+ font-size: 0.64rem;
+ }
+
+ .compactCover {
+ max-height: 9vh;
+ }
+
+ .compactText {
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ }
+}
+
+.tableNote {
+ margin-top: 0.75rem;
+ color: var(--color-gray-500);
+ font-size: 1rem;
+}
+
+@media (max-height: 760px) {
+ .tableNote {
+ margin-top: 0.4rem;
+ font-size: 0.85rem;
+ }
+}
+
+/* Closing slide: the webinar invitation with its registration QR code, above
+ the contact links. The QR sits on white with its own quiet zone. */
+.closingTitle {
+ margin-bottom: 2rem;
+}
+
+.webinarCard {
+ display: flex;
+ width: 100%;
+ max-width: 64rem;
+ align-items: center;
+ gap: 2rem;
+ border: 1px solid rgb(255 255 255 / 0.25);
+ border-radius: 1.25rem;
+ background: rgb(255 255 255 / 0.1);
+ padding: 1.25rem 1.75rem;
+ text-align: left;
+ transition: background-color 150ms;
+}
+
+.webinarCard:hover {
+ background: rgb(255 255 255 / 0.18);
+ text-decoration: none !important;
+}
+
+.webinarQr {
+ width: 13rem;
+ height: 13rem;
+ flex-shrink: 0;
+ border-radius: 0.75rem;
+ background: white;
+}
+
+.webinarEyebrow {
+ color: rgb(255 255 255 / 0.75);
+ font-size: 0.95rem;
+ font-weight: 700;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+}
+
+.webinarTitle {
+ margin-top: 0.4rem;
+ font-size: 2rem;
+ font-weight: 700;
+ line-height: 1.2;
+}
+
+.webinarWhen {
+ margin-top: 0.6rem;
+ font-size: 1.35rem;
+ font-weight: 600;
+}
+
+.webinarText {
+ margin-top: 0.35rem;
+ color: rgb(255 255 255 / 0.85);
+ font-size: 1.15rem;
+ line-height: 1.4;
+}
+
+.webinarCta {
+ display: inline-block;
+ margin-top: 0.9rem;
+ border-radius: 9999px;
+ background: white;
+ padding: 0.3rem 0.9rem;
+ color: var(--pe-dark);
+ font-size: 1rem;
+ font-weight: 700;
+}
+
+.closingContacts {
+ margin-top: 2rem;
+}
+
+@media (max-height: 1000px) {
+ .closingTitle {
+ margin-bottom: 1.25rem;
+ font-size: 3rem;
+ }
+
+ .webinarQr {
+ width: 11rem;
+ height: 11rem;
+ }
+
+ .webinarTitle {
+ font-size: 1.6rem;
+ }
+
+ .webinarWhen {
+ font-size: 1.15rem;
+ }
+
+ .webinarText {
+ font-size: 1rem;
+ }
+
+ .closingContacts {
+ margin-top: 1.25rem;
+ }
+}
+
+@media (max-height: 760px) {
+ .closingTitle {
+ margin-bottom: 0.6rem;
+ font-size: 2.25rem;
+ }
+
+ .webinarCard {
+ padding: 1rem 1.25rem;
+ }
+
+ .webinarQr {
+ width: 9.5rem;
+ height: 9.5rem;
+ }
+
+ .webinarTitle {
+ font-size: 1.35rem;
+ }
+
+ .webinarText {
+ display: none;
+ }
+
+ .webinarCta {
+ margin-top: 0.6rem;
+ font-size: 0.9rem;
+ }
+
+ .closingContacts {
+ margin-top: 0.9rem;
+ }
+}
diff --git a/slideshows/good-growth-foundation-2026/slides/figures.tsx b/slideshows/good-growth-foundation-2026/slides/figures.tsx
new file mode 100644
index 00000000..a54d4974
--- /dev/null
+++ b/slideshows/good-growth-foundation-2026/slides/figures.tsx
@@ -0,0 +1,152 @@
+import {
+ energyDeciles,
+ energyRegions,
+ pipelineStages,
+ targetGroups,
+} from "../content";
+import styles from "./deck.module.css";
+
+// The pipeline layout follows the Scottish Government deck's PipelineGraph,
+// with the sources that feed each stage drawn above it.
+export function PipelineGraph() {
+ const last = pipelineStages.length - 1;
+ return (
+
+
+ Microcosm UK: one set of households, built up step by step
+ Weighted to UK totals
+
+
+ {pipelineStages.map((stage) => (
+
+ {stage.sources.map((source) => (
+
{source}
+ ))}
+
+ ))}
+ {pipelineStages.map((stage) => (
+
+ ))}
+ {pipelineStages.map((stage, index) => (
+
+
{index + 1}
+
{stage.title}
+
{stage.adds}
+
+ ))}
+
+
+ One weighted set of households {" "}
+ carried through every stage, then into PolicyEngine's tax-benefit model
+
+
+ );
+}
+
+const share = (value: number) => `${Math.round(value * 100)}%`;
+
+export function RegionEligibilityChart() {
+ return (
+
+
+ Households eligible, by region
+
+ On a means-tested benefit
+ Income test alone
+
+
+
+ {energyRegions.map((r) => {
+ const incomeOnly = r.eligible - r.passported;
+ return (
+
+ {r.region}
+
+
+
+ {share(r.eligible)}
+
+
+ );
+ })}
+
+
+ );
+}
+
+export function DecileChart() {
+ const max = Math.max(...energyDeciles.map((d) => d.averageGain));
+ return (
+
+
+ Average gain per household, by income decile
+ Includes households that get nothing.
+
+
+ {energyDeciles.map((d) => (
+
+ £{d.averageGain}
+
+
+ ))}
+
+
+ {energyDeciles.map((d) => (
+ {d.decile}
+ ))}
+
+
+ Income decile of people in Great Britain, after housing costs (1 = lowest)
+
+
+ );
+}
+
+const count = (n: number) => (n === 0 ? "–" : n.toLocaleString("en-GB"));
+
+// UK calibration targets: what each group binds at and who publishes it, with
+// the rows bound by the national dataset and by the local-area dataset.
+export function TargetTable() {
+ const national = targetGroups.reduce((sum, row) => sum + row.national, 0);
+ const local = targetGroups.reduce((sum, row) => sum + row.local, 0);
+ return (
+
+
+
Targets
+
Binds at
+
Source
+
National dataset
+
Local-area dataset
+
+ {targetGroups.map((row) => (
+
+
{row.targets}
+
{row.level}
+
{row.source}
+
+ {count(row.national)}
+
+
{count(row.local)}
+
+ ))}
+
+
Total
+
+
+
{count(national)}
+
{count(local)}
+
+
+ );
+}
diff --git a/slideshows/good-growth-foundation-2026/slides/index.tsx b/slideshows/good-growth-foundation-2026/slides/index.tsx
new file mode 100644
index 00000000..f7e05391
--- /dev/null
+++ b/slideshows/good-growth-foundation-2026/slides/index.tsx
@@ -0,0 +1,424 @@
+import type { ReactNode } from "react";
+import {
+ IconBrandGithub,
+ IconBrandLinkedin,
+ IconChartBar,
+ IconFileCode,
+ IconHome,
+ IconMail,
+ IconWorld,
+} from "@tabler/icons-react";
+import Image from "@/components/core/BasePathImage";
+import Slide from "@/components/core/Slide";
+import CoverSlide from "@/components/layout/CoverSlide";
+import SlideHeader from "@/components/layout/SlideHeader";
+import SlideTitle from "@/components/layout/SlideTitle";
+import { speakers } from "@/lib/speakers";
+import {
+ coverage,
+ energyHeadline,
+ localAreaRows,
+ publications,
+ sources,
+ steps,
+ users,
+ type Card,
+ type Source,
+ webinar,
+} from "../content";
+import styles from "./deck.module.css";
+import { DecileChart, PipelineGraph, RegionEligibilityChart, TargetTable } from "./figures";
+import { MicrosimWalkthrough } from "./microsim";
+
+const sections = {
+ model: "The model",
+ data: "The data",
+ analysis: "An analysis example",
+};
+
+function ExternalLink({
+ children,
+ href,
+ className = "",
+}: {
+ children: ReactNode;
+ href: string;
+ className?: string;
+}) {
+ return (
+ event.stopPropagation()}
+ className={`pointer-events-auto hover:underline ${className}`}
+ >
+ {children}
+
+ );
+}
+
+function SourceLine({ items }: { items: Source[] }) {
+ return (
+
+ Sources
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+ );
+}
+
+function Frame({
+ section,
+ title,
+ subtitle,
+ children,
+ references = [],
+ center = false,
+}: {
+ section?: string;
+ title: string;
+ subtitle?: string;
+ children: ReactNode;
+ references?: Source[];
+ /** Centre the body in the space below the title (for figure slides). */
+ center?: boolean;
+}) {
+ return (
+
+
+
+ {section && (
+
+ {section}
+
+ )}
+ {title}
+ {subtitle && (
+
+ {subtitle}
+
+ )}
+
+ {center ?
{children}
: children}
+
+ {references.length > 0 && }
+
+ );
+}
+
+export function TitleSlide() {
+ return (
+
+ );
+}
+
+const stepIcons = [IconFileCode, IconHome, IconChartBar];
+
+// The 9 October introduction deck's "What PolicyEngine is" and "How it works"
+// slides, merged: the rules, the households and the results, with the open
+// code underneath.
+export function WhatSlide() {
+ return (
+
+
+ A model of the UK tax and benefit system that anyone can use, free, in
+ a web browser.
+
+
+ {steps.map((step, index) => {
+ const Icon = stepIcons[index];
+ return (
+
+
+
+
+
+
+ {index + 1} {step.title}
+
+
+
{step.text}
+
+ );
+ })}
+
+
+
+ All the code is public, so anyone can see how each number is calculated.
+
+
+ );
+}
+
+export function MicrosimSlide() {
+ return (
+
+
+
+ );
+}
+
+export function CoverageSlide() {
+ return (
+
+
+ {coverage.map((group) => (
+
+
{group.title}
+
+ {group.items.map((item) => (
+ {item}
+ ))}
+
+
+ ))}
+
+
+ 732 of the model's 795 parameter files cite the law or guidance
+ they come from, and more than 4,000 automated tests check the
+ calculations.
+
+
+ It can also model behavioural responses, such as changes in work and in
+ when capital gains are realised. It does not model inheritance tax.
+
+
+ );
+}
+
+export function PipelineSlide() {
+ return (
+
+
+
+ );
+}
+
+export function TargetsSlide() {
+ return (
+
+
+
+ );
+}
+
+export function EnergyDiscountSlide() {
+ const h = energyHeadline;
+ return (
+
+
+
+ {h.recipients} of {h.households} households ({h.share})
+ would be eligible. If all took it up, it would cost{" "}
+ {h.cost} in 2026-27.
+
+
+
+ Explore other designs in the dashboard ↗
+
+
+
+
+
+
+
+
+ );
+}
+
+function CompactTile({ item }: { item: Card }) {
+ return (
+
+
+
+
{item.date}
+
+ {item.title}
+
+
+ {item.text}
+
+
+
+ );
+}
+
+export function PublicationsSlide() {
+ return (
+
+
+ {publications.map((item) => (
+
+ ))}
+
+
+ );
+}
+
+export function UsersSlide() {
+ return (
+
+
+ {users.map((user) => {
+ const logo = user.logo ? (
+
+ ) : (
+
+ {user.initials}
+
+ );
+ const body = (
+
+ {logo}
+
+ {user.name}
+
+
+ );
+ return user.source ? (
+
+ {body}
+
+ ) : (
+
{body}
+ );
+ })}
+
+
+ );
+}
+
+const contacts = [
+ { label: "policyengine.org/uk", url: sources.home.href, Icon: IconWorld },
+ { label: "hello@policyengine.org", url: "mailto:hello@policyengine.org", Icon: IconMail },
+ { label: "github.com/PolicyEngine", url: "https://github.com/PolicyEngine", Icon: IconBrandGithub },
+ {
+ label: "linkedin.com/company/thepolicyengine",
+ url: "https://www.linkedin.com/company/thepolicyengine",
+ Icon: IconBrandLinkedin,
+ },
+];
+
+export function ClosingSlide() {
+ return (
+
+ Thank you
+ event.stopPropagation()}
+ className={styles.webinarCard}
+ >
+
+
+
Next week · join our webinar
+
{webinar.title}
+
{webinar.when}
+
{webinar.text}
+
Scan the code to register, free
+
+
+
+
+ );
+}
diff --git a/slideshows/good-growth-foundation-2026/slides/microsim.tsx b/slideshows/good-growth-foundation-2026/slides/microsim.tsx
new file mode 100644
index 00000000..a3c9e2b5
--- /dev/null
+++ b/slideshows/good-growth-foundation-2026/slides/microsim.tsx
@@ -0,0 +1,139 @@
+"use client";
+
+import { useSlideshowContextSafe } from "@/components/core/SlideshowContext";
+import styles from "./deck.module.css";
+
+// A walkthrough of microsimulation in six build steps, after the UK event
+// 2025 deck (model-scope, slide 2): one household under current law, the
+// reform, behaviour, more households, weights, then weighted totals. The
+// households and figures are illustrative, not survey records or estimates.
+export const MICROSIM_STEPS = 6;
+
+type Column = {
+ key: string;
+ label: string;
+ note?: string;
+ step: number;
+ kind: "household" | "baseline" | "reform" | "weight";
+};
+
+const columns: Column[] = [
+ { key: "earnings", label: "Earnings", step: 1, kind: "household" },
+ { key: "children", label: "Children", step: 1, kind: "household" },
+ { key: "baseline", label: "Net tax", note: "Current law", step: 1, kind: "baseline" },
+ { key: "static", label: "Net tax", note: "Reform", step: 2, kind: "reform" },
+ { key: "earningsChange", label: "Change in earnings", step: 3, kind: "reform" },
+ { key: "behaviour", label: "Net tax", note: "With behaviour", step: 3, kind: "reform" },
+ { key: "change", label: "Change in net tax", step: 3, kind: "reform" },
+ { key: "weight", label: "Weight", note: "Households like it", step: 5, kind: "weight" },
+];
+
+const households: { step: number; cells: Record }[] = [
+ {
+ step: 1,
+ cells: {
+ earnings: "£100,000", children: "0", baseline: "£31,440", static: "£30,690",
+ earningsChange: "−£100", behaviour: "£30,650", change: "−£790", weight: "0.4m",
+ },
+ },
+ {
+ step: 4,
+ cells: {
+ earnings: "£45,000", children: "2", baseline: "£6,740", static: "£6,090",
+ earningsChange: "+£200", behaviour: "£6,150", change: "−£590", weight: "1.1m",
+ },
+ },
+ {
+ step: 4,
+ cells: {
+ earnings: "£20,000", children: "1", baseline: "−£9,330", static: "−£9,400",
+ earningsChange: "£0", behaviour: "−£9,400", change: "−£70", weight: "0.3m",
+ },
+ },
+];
+
+const totals: Record = {
+ earnings: "Total", children: "", baseline: "£300bn", static: "£286bn",
+ earningsChange: "+£2bn", behaviour: "£287bn", change: "−£13bn", weight: "29.4m",
+};
+
+const groups = [
+ { label: "Households", step: 4, span: "1 / 3" },
+ { label: "Policies", step: 2, span: "3 / 5" },
+ { label: "Behaviour", step: 3, span: "5 / 8" },
+];
+
+const captions = [
+ "Start with one household and calculate its taxes and benefits under current law.",
+ "Apply the reform: the same household, recalculated under the new rules.",
+ "Add behaviour: if people work a little more or less, their taxes change again.",
+ "Repeat for every household in the data, each with its own circumstances.",
+ "Weight each household by how many similar households it stands for.",
+ "Add up: the weighted totals give the cost, and the rows give who gains and loses.",
+];
+
+export function MicrosimWalkthrough() {
+ const ctx = useSlideshowContextSafe();
+ const step = ctx?.buildStep ?? MICROSIM_STEPS;
+ const shown = (from: number) => (step >= from ? "" : styles.msHidden);
+ return (
+
+
+ {groups.map((g) => (
+
+ {g.label}
+
+ ))}
+
+
+
+ {columns.map((c) => (
+
+ {c.label}
+ {c.note && {c.note} }
+
+ ))}
+
+ {households.map((h, i) => (
+
+ {columns.map((c) => (
+
+ {h.cells[c.key]}
+
+ ))}
+
+ ))}
+
+ {columns.map((c) => (
+
+ …
+
+ ))}
+
+
+ {columns.map((c) => (
+
+ {totals[c.key]}
+
+ ))}
+
+
+
+ {Math.min(step, MICROSIM_STEPS)}
+ {captions[Math.min(step, MICROSIM_STEPS) - 1]}
+
+
+ Illustrative households and figures, not survey records or PolicyEngine
+ estimates. Net tax is taxes paid minus benefits received.
+
+
+ );
+}