diff --git a/lib/slideshows.ts b/lib/slideshows.ts index 9c1873aa..9aa39683 100644 --- a/lib/slideshows.ts +++ b/lib/slideshows.ts @@ -28,8 +28,10 @@ import { overhang2026BackupConfig } from "@/slideshows/overhang-2026-backup/conf import { overhang2026LightningConfig } from "@/slideshows/overhang-2026-lightning/config"; import { blsTaxsim2026Config } from "@/slideshows/bls-taxsim-2026/config"; import { scottishGovernment2026Config } from "@/slideshows/scottish-government-2026/config"; +import { goodGrowthFoundation2026Config } from "@/slideshows/good-growth-foundation-2026/config"; export const slideshows: SlideshowConfig[] = [ + goodGrowthFoundation2026Config, scottishGovernment2026Config, blsTaxsim2026Config, overhang2026Config, diff --git a/public/images/good-growth-foundation-2026/qr-autumn-budget-webinar.svg b/public/images/good-growth-foundation-2026/qr-autumn-budget-webinar.svg new file mode 100644 index 00000000..e951100a --- /dev/null +++ b/public/images/good-growth-foundation-2026/qr-autumn-budget-webinar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/logos/orgs/10-downing-street.png b/public/logos/orgs/10-downing-street.png new file mode 100644 index 00000000..5383954d Binary files /dev/null and b/public/logos/orgs/10-downing-street.png differ diff --git a/public/logos/orgs/centre-for-policy-studies.png b/public/logos/orgs/centre-for-policy-studies.png new file mode 100644 index 00000000..cff5e897 Binary files /dev/null and b/public/logos/orgs/centre-for-policy-studies.png differ diff --git a/public/logos/orgs/house-of-lords.svg b/public/logos/orgs/house-of-lords.svg new file mode 100644 index 00000000..0703f71e --- /dev/null +++ b/public/logos/orgs/house-of-lords.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/public/logos/orgs/new-economics-foundation.png b/public/logos/orgs/new-economics-foundation.png new file mode 100644 index 00000000..8da0ef04 Binary files /dev/null and b/public/logos/orgs/new-economics-foundation.png differ diff --git a/public/logos/orgs/niesr.png b/public/logos/orgs/niesr.png new file mode 100644 index 00000000..45857c54 Binary files /dev/null and b/public/logos/orgs/niesr.png differ diff --git a/public/logos/orgs/nuffield-foundation.svg b/public/logos/orgs/nuffield-foundation.svg new file mode 100644 index 00000000..23157511 --- /dev/null +++ b/public/logos/orgs/nuffield-foundation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/logos/orgs/tax-policy-associates.jpg b/public/logos/orgs/tax-policy-associates.jpg new file mode 100644 index 00000000..29344da3 Binary files /dev/null and b/public/logos/orgs/tax-policy-associates.jpg differ diff --git a/public/logos/orgs/wpi-economics.svg b/public/logos/orgs/wpi-economics.svg new file mode 100644 index 00000000..02be66d7 --- /dev/null +++ b/public/logos/orgs/wpi-economics.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/screenshots/good-growth-foundation-2026/energy-autumn-budget.jpg b/public/screenshots/good-growth-foundation-2026/energy-autumn-budget.jpg new file mode 100644 index 00000000..00de83ee Binary files /dev/null and b/public/screenshots/good-growth-foundation-2026/energy-autumn-budget.jpg differ diff --git a/public/screenshots/good-growth-foundation-2026/free-childcare-reform.jpg b/public/screenshots/good-growth-foundation-2026/free-childcare-reform.jpg new file mode 100644 index 00000000..3eddb6d2 Binary files /dev/null and b/public/screenshots/good-growth-foundation-2026/free-childcare-reform.jpg differ diff --git a/public/screenshots/good-growth-foundation-2026/marriage-calculator-uk.jpg b/public/screenshots/good-growth-foundation-2026/marriage-calculator-uk.jpg new file mode 100644 index 00000000..dd1043ee Binary files /dev/null and b/public/screenshots/good-growth-foundation-2026/marriage-calculator-uk.jpg differ diff --git a/public/screenshots/good-growth-foundation-2026/middle-east-war-living-standards.jpg b/public/screenshots/good-growth-foundation-2026/middle-east-war-living-standards.jpg new file mode 100644 index 00000000..9c4b523a Binary files /dev/null and b/public/screenshots/good-growth-foundation-2026/middle-east-war-living-standards.jpg differ diff --git a/public/screenshots/good-growth-foundation-2026/policyengine-joss-paper-cover.webp b/public/screenshots/good-growth-foundation-2026/policyengine-joss-paper-cover.webp new file mode 100644 index 00000000..d305a5a9 Binary files /dev/null and b/public/screenshots/good-growth-foundation-2026/policyengine-joss-paper-cover.webp differ diff --git a/public/screenshots/good-growth-foundation-2026/targeted-energy-discount.jpg b/public/screenshots/good-growth-foundation-2026/targeted-energy-discount.jpg new file mode 100644 index 00000000..34b135ed Binary files /dev/null and b/public/screenshots/good-growth-foundation-2026/targeted-energy-discount.jpg differ diff --git a/public/screenshots/good-growth-foundation-2026/uk-ai-study.jpg b/public/screenshots/good-growth-foundation-2026/uk-ai-study.jpg new file mode 100644 index 00000000..0b78a90b Binary files /dev/null and b/public/screenshots/good-growth-foundation-2026/uk-ai-study.jpg differ diff --git a/public/screenshots/good-growth-foundation-2026/uk-triple-lock.jpg b/public/screenshots/good-growth-foundation-2026/uk-triple-lock.jpg new file mode 100644 index 00000000..a5fa111b Binary files /dev/null and b/public/screenshots/good-growth-foundation-2026/uk-triple-lock.jpg differ diff --git a/slideshows/good-growth-foundation-2026/README.md b/slideshows/good-growth-foundation-2026/README.md new file mode 100644 index 00000000..2e3767e7 --- /dev/null +++ b/slideshows/good-growth-foundation-2026/README.md @@ -0,0 +1,63 @@ +# PolicyEngine UK: an introduction, 23 October 2026 + +10 slides at `/slides/good-growth-foundation-2026`: an introduction to +PolicyEngine UK for the Good Growth Foundation, for a 20 to 30 minute slot. + +1. Title +2. What PolicyEngine is (the rules, the households and the results) +3. How microsimulation works (six build steps) +4. What the UK model covers +5. From surveys to a population that represents the UK (the data) +6. What the weights are calibrated to (UK calibration targets) +7. A targeted energy bill discount (an analysis example) +8. Recent UK research +9. Who uses PolicyEngine UK +10. Thank you, with the Autumn Budget webinar invitation and its registration QR code + +Slides 2 and 9 come from the 9 October introduction deck +(`introduction-2026`, policyengine-slides #77); slide 2 merges its "What +PolicyEngine is" and "How it works" slides. Slides 5 and 7 come from the +Scottish Government deck of the same day (`scottish-government-2026`), told +for the whole UK: the pipeline drops the Scotland-only stage, and the decile +chart covers Great Britain. Slide 3 follows the microsimulation walkthrough in +the UK event 2025 deck (`model-scope`, slide 2), with illustrative households +and figures rather than survey records. + +- `config.ts` controls the slide order and meeting metadata. +- `content.ts` holds the sources, model coverage, pipeline stages, energy + figures and the community list, with a comment on where each comes from. +- `slides/index.tsx` contains the slides; `slides/figures.tsx` draws the + pipeline, the target table and the two energy charts in HTML; + `slides/microsim.tsx` is the walkthrough, which reveals one step per build. + +Figures and sources: + +- Model coverage is read from the policyengine-uk 2.146.6 parameter tree; + 732 of its 795 parameter files carry a reference, and its YAML tests hold + over 4,000 cases. +- The pipeline and target table cite Microcosm at the commit the Scottish + Government deck used (main on 8 October 2026). The table groups the 31 + families of the national target references (1,231 rows) and the six of the + local ones (20,885 rows), with one row count for each dataset: the + local-area dataset binds every national target as well as its own. The + national file has held over 1,000 rows in every build since the 4 October + release. +- The energy figures are the Resolution Foundation's flat option in 2026-27 + from uk-energy-reforms 14bf3b0 (Microcosm UK 2024-25 national release, + policyengine-uk 2.102.3), and match the dashboard's data file. The slide + shows the Great Britain total, the eleven regions (each with an effective + sample of 273 to 734 households) and national income deciles; it leaves + poverty changes out. +- Recent research shows the eight newest UK items on + policyengine.org/uk/research on 23 October 2026, newest first, with covers + from policyengine-app-v2 (`website/public/assets/posts`). + +The closing slide invites the audience to the Autumn Budget 2026 webinar +(Wednesday 28 October, 4:30pm UK time), as announced on the policyengine.org +home page. `public/images/good-growth-foundation-2026/qr-autumn-budget-webinar.svg` +encodes the Zoom registration link; it was generated locally with segno and +checked by decoding it from slide screenshots at three screen sizes. + +Logos in `public/logos/orgs/` come from the introduction deck, except NIESR's, +which comes from policyengine-app-v2. The community grid leaves out the Good +Growth Foundation itself. diff --git a/slideshows/good-growth-foundation-2026/config.ts b/slideshows/good-growth-foundation-2026/config.ts new file mode 100644 index 00000000..125b1f5e --- /dev/null +++ b/slideshows/good-growth-foundation-2026/config.ts @@ -0,0 +1,36 @@ +import type { SlideshowConfig } from "@/lib/types"; +import { + ClosingSlide, + CoverageSlide, + EnergyDiscountSlide, + MicrosimSlide, + PipelineSlide, + PublicationsSlide, + TargetsSlide, + TitleSlide, + UsersSlide, + WhatSlide, +} from "./slides"; +import { MICROSIM_STEPS } from "./slides/microsim"; + +export const goodGrowthFoundation2026Config: SlideshowConfig = { + id: "good-growth-foundation-2026", + title: "PolicyEngine UK: an introduction", + description: + "PolicyEngine UK for the Good Growth Foundation: how microsimulation works, the model, its data and calibration targets, an energy discount example and recent UK research.", + date: "2026-10-23", + location: "Good Growth Foundation", + footerText: "Good Growth Foundation · 23 October 2026", + slides: [ + TitleSlide, + WhatSlide, + { component: MicrosimSlide, builds: MICROSIM_STEPS }, + CoverageSlide, + PipelineSlide, + TargetsSlide, + EnergyDiscountSlide, + PublicationsSlide, + UsersSlide, + ClosingSlide, + ], +}; diff --git a/slideshows/good-growth-foundation-2026/content.ts b/slideshows/good-growth-foundation-2026/content.ts new file mode 100644 index 00000000..a2b7b6eb --- /dev/null +++ b/slideshows/good-growth-foundation-2026/content.ts @@ -0,0 +1,422 @@ +// Content for the Good Growth Foundation introduction, 23 October 2026. +// The intro, how-it-works and community slides come from the 9 October +// introduction deck (policyengine-slides #77); the pipeline and energy slides +// from the Scottish Government deck of the same day, told for the whole UK. + +// Microcosm links are pinned to the commit the Scottish Government deck used +// (main on 8 October 2026), the closest to the 4 October national release. +// The model links are pinned to policyengine-uk 2.146.6 (main on 22 October). +const microcosmCommit = "75167a688ea83316654f1d794542124b4277bda9"; +const modelCommit = "849975f27ba8fec45e78f5f48332c80a55de7cc2"; +const energyCommit = "14bf3b0afc121b092bbd7545c9fb4963e8ab5c72"; +const microcosm = `https://github.com/PolicyEngine/microcosm/blob/${microcosmCommit}`; +const ukBuild = `${microcosm}/packages/microcosm-build/src/microcosm/build/uk`; + +export type Source = { label: string; href: string }; + +export const sources = { + home: { label: "policyengine.org", href: "https://www.policyengine.org/uk" }, + model: { + label: "PolicyEngine UK code", + href: "https://github.com/PolicyEngine/policyengine-uk", + }, + data: { + label: "Microcosm data pipeline", + href: "https://github.com/PolicyEngine/microcosm", + }, + parameters: { + label: "policyengine-uk 2.146.6 parameters", + href: `https://github.com/PolicyEngine/policyengine-uk/tree/${modelCommit}/policyengine_uk/parameters/gov`, + }, + tests: { + label: "Model tests", + href: `https://github.com/PolicyEngine/policyengine-uk/tree/${modelCommit}/policyengine_uk/tests`, + }, + pipeline: { + label: "UK build graph", + href: `${microcosm}/docs/uk-full-build-graph.md`, + }, + inputs: { label: "UK source manifest", href: `${ukBuild}/spec/sources.yaml` }, + geography: { + label: "Geography assignment", + href: `${microcosm}/docs/geography-assignment.md`, + }, + nationalTargets: { + label: "National targets", + href: `${ukBuild}/target_references.json`, + }, + energyDashboard: { + label: "Targeted energy bill discount dashboard", + href: "https://www.policyengine.org/uk/targeted-energy-discount", + }, + energyAnalysis: { + label: "uk-energy-reforms analysis", + href: `https://github.com/PolicyEngine/uk-energy-reforms/tree/${energyCommit}/analyses/rf-billing-me-softly`, + }, + energyProposal: { + label: "Resolution Foundation, Billing me softly", + href: "https://www.resolutionfoundation.org/publications/billing-me-softly/", + }, + no10Post: { + label: "PolicyEngine at No 10", + href: "https://www.policyengine.org/uk/research/policyengine-10-downing-street", + }, + hansard: { + label: "Hansard, 24 February 2026", + href: "https://hansard.parliament.uk/Lords/2026-02-24/debates/A381F7D6-0A3C-48FD-8D9E-67751E25877A/NationalInsuranceContributions(EmployerPensionsContributions)Bill", + }, + tpa: { + label: "Tax Policy Associates, September 2026", + href: "https://taxpolicy.org.uk/2026/09/10/universities-uk-national-insurance-proposal/", + }, + cps: { + label: "Centre for Policy Studies, July 2023", + href: "https://cps.org.uk/research/family-friendly-taxation/", + }, + nef: { + label: "New Economics Foundation, July 2026", + href: "https://neweconomics.org/2026/07/insecure-low-quality-work-a-major-driver-of-uks-neets-and-inactivity-crisis", + }, + nuffield: { + label: "Nuffield Foundation project", + href: "https://www.nuffieldfoundation.org/project/enhancing-localising-and-democratising-tax-benefit-policy-analysis", + }, + research: { + label: "UK research", + href: "https://www.policyengine.org/uk/research", + }, + niesr: { + label: "NIESR membership", + href: "https://niesr.ac.uk/membership/individual-membership", + }, + localTargets: { + label: "Local targets", + href: `${ukBuild}/local_target_references.json`, + }, +} satisfies Record; + +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) => ( + +
+ 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}

+
+ ); + })} +
+ + + + ); +} + +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.alt} +
+

{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.name} + ) : ( + + {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} + > + QR code: register for the Autumn Budget 2026 webinar +
+

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. +

+
+ ); +}