diff --git a/lib/slideshows.ts b/lib/slideshows.ts index dd175f8..dfea92a 100644 --- a/lib/slideshows.ts +++ b/lib/slideshows.ts @@ -29,10 +29,10 @@ import { overhang2026LightningConfig } from "@/slideshows/overhang-2026-lightnin 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"; -import { introduction2026Config } from "@/slideshows/introduction-2026/config"; +import { autumnBudget2026Config } from "@/slideshows/autumn-budget-2026/config"; export const slideshows: SlideshowConfig[] = [ - introduction2026Config, + autumnBudget2026Config, goodGrowthFoundation2026Config, scottishGovernment2026Config, blsTaxsim2026Config, diff --git a/next.config.ts b/next.config.ts index c66c230..71f0735 100644 --- a/next.config.ts +++ b/next.config.ts @@ -8,6 +8,16 @@ const nextConfig: NextConfig = { env: { NEXT_PUBLIC_BASE_PATH: '/slides', }, + // The Autumn Budget 2026 webinar deck was introduction-2026 until 28 October. + async redirects() { + return [ + { + source: '/introduction-2026/:path*', + destination: '/autumn-budget-2026/:path*', + permanent: true, + }, + ]; + }, // Pin the workspace root so Turbopack does not silently pick up a stray // lockfile from a parent directory during local development or CI. turbopack: { diff --git a/public/images/introduction-2026/dashboard-qr.svg b/public/images/autumn-budget-2026/dashboard-qr.svg similarity index 100% rename from public/images/introduction-2026/dashboard-qr.svg rename to public/images/autumn-budget-2026/dashboard-qr.svg diff --git a/public/logos/funders/nuffield-foundation-white.svg b/public/logos/funders/nuffield-foundation-white.svg new file mode 100644 index 0000000..a53f698 --- /dev/null +++ b/public/logos/funders/nuffield-foundation-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/slideshows/introduction-2026/README.md b/slideshows/autumn-budget-2026/README.md similarity index 58% rename from slideshows/introduction-2026/README.md rename to slideshows/autumn-budget-2026/README.md index f2d846c..d016199 100644 --- a/slideshows/introduction-2026/README.md +++ b/slideshows/autumn-budget-2026/README.md @@ -1,10 +1,12 @@ -# Analysing the UK Autumn Budget 2026 with PolicyEngine +# Analysing the Autumn Budget 2026 with PolicyEngine -5 slides at `/slides/introduction-2026`, for the 28 October 2026 webinar: +5 slides at `/slides/autumn-budget-2026`, for the 28 October 2026 webinar +(`/slides/introduction-2026` redirects here): what PolicyEngine is, how we prepared for Budget day, and a slide with the live [Autumn Budget 2026 dashboard](https://ab2026-local-preview.vercel.app/uk/autumn-budget-2026) embedded in it, where the analysis is presented. The closing slide links the -same address, with its QR code. +same address, with its QR code, and credits the Nuffield Foundation, which +funds our UK Budget analysis. - `config.ts` controls the slide order and metadata. - `content.ts` holds the sources, the three pillars of What PolicyEngine is, @@ -19,5 +21,13 @@ same address, with its QR code. here goes stale when the dashboard's data change. - `SPEAKER-NOTES.md` gives the presenter track. -`public/images/introduction-2026/dashboard-qr.svg` encodes +`public/images/autumn-budget-2026/dashboard-qr.svg` encodes the dashboard's address. + +`public/logos/funders/nuffield-foundation-white.svg` is the Nuffield +Foundation's white logo, downloaded from nuffieldfoundation.org on 22 October +2026 (3,432 bytes, SHA-256 +`b3727385be9295f8daa3938c745f87b2e00e5675130354f6394ae6b3fec5654c`). That day +its communications team asked for "funded" rather than "supported", and for +its logo, the white version on a dark background. Keep the file as +downloaded; don't recolour or redraw it. diff --git a/slideshows/introduction-2026/SPEAKER-NOTES.md b/slideshows/autumn-budget-2026/SPEAKER-NOTES.md similarity index 96% rename from slideshows/introduction-2026/SPEAKER-NOTES.md rename to slideshows/autumn-budget-2026/SPEAKER-NOTES.md index 343c303..db82a2e 100644 --- a/slideshows/introduction-2026/SPEAKER-NOTES.md +++ b/slideshows/autumn-budget-2026/SPEAKER-NOTES.md @@ -65,4 +65,6 @@ that address. ## 5. Close -The dashboard link, with a QR code for phones, and how to reach us. +The dashboard link, with a QR code for phones, and how to reach us. Thank +the Nuffield Foundation, which funds our UK Budget analysis ("funded by", +not "supported by"). diff --git a/slideshows/introduction-2026/config.ts b/slideshows/autumn-budget-2026/config.ts similarity index 79% rename from slideshows/introduction-2026/config.ts rename to slideshows/autumn-budget-2026/config.ts index 5644fa2..d0f3e01 100644 --- a/slideshows/introduction-2026/config.ts +++ b/slideshows/autumn-budget-2026/config.ts @@ -7,9 +7,9 @@ import { WhatSlide, } from "./slides"; -export const introduction2026Config: SlideshowConfig = { - id: "introduction-2026", - title: "Analysing the UK Autumn Budget 2026 with PolicyEngine", +export const autumnBudget2026Config: SlideshowConfig = { + id: "autumn-budget-2026", + title: "Analysing the Autumn Budget 2026 with PolicyEngine", description: "What PolicyEngine is, how we prepared for Budget day, and the analysis, presented live on the dashboard.", date: "2026-10-28", diff --git a/slideshows/introduction-2026/content.ts b/slideshows/autumn-budget-2026/content.ts similarity index 90% rename from slideshows/introduction-2026/content.ts rename to slideshows/autumn-budget-2026/content.ts index ea68e52..de14782 100644 --- a/slideshows/introduction-2026/content.ts +++ b/slideshows/autumn-budget-2026/content.ts @@ -56,6 +56,19 @@ export const sources = { export const about = "PolicyEngine is a nonprofit building free, open-source tools that help people understand tax and benefit policies."; +// The Nuffield Foundation's credit. On 22 October its communications team +// asked for "funded" rather than "supported", and for its logo, the white +// version on a dark background. The logo is the white one from +// nuffieldfoundation.org, kept as downloaded (3,432 bytes, SHA-256 +// b3727385…); its own size is 117.862 by 70. +export const funder = { + label: "Funded by", + name: "Nuffield Foundation", + logo: "/logos/funders/nuffield-foundation-white.svg", + width: 118, + height: 70, +}; + // What PolicyEngine is: the rules, the households and the results. The rules // are the main ones in the Good Growth Foundation deck's checked list. export const pillars = [ diff --git a/slideshows/autumn-budget-2026/funder.test.ts b/slideshows/autumn-budget-2026/funder.test.ts new file mode 100644 index 0000000..d927e27 --- /dev/null +++ b/slideshows/autumn-budget-2026/funder.test.ts @@ -0,0 +1,31 @@ +import { createHash } from "node:crypto"; +import { readFileSync } from "node:fs"; +import path from "node:path"; +import { describe, expect, it } from "vitest"; +import { funder } from "./content"; + +// The white logo as downloaded from nuffieldfoundation.org on 22 October. Keep +// it byte for byte: don't recolour or redraw it. +const DOWNLOADED_SHA256 = + "b3727385be9295f8daa3938c745f87b2e00e5675130354f6394ae6b3fec5654c"; + +describe("the Nuffield Foundation credit on the closing slide", () => { + const file = path.join(__dirname, "../../public", funder.logo); + const bytes = readFileSync(file); + + it("uses the logo exactly as downloaded", () => { + expect(bytes.length).toBe(3432); + expect(createHash("sha256").update(bytes).digest("hex")).toBe(DOWNLOADED_SHA256); + }); + + it("matches the logo's own dimensions", () => { + const svg = bytes.toString("utf8"); + const size = (name: string) => Number(svg.match(new RegExp(`${name}="([\\d.]+)"`))?.[1]); + expect(Math.round(size("width"))).toBe(funder.width); + expect(Math.round(size("height"))).toBe(funder.height); + }); + + it('reads "Funded by"', () => { + expect(funder.label).toBe("Funded by"); + }); +}); diff --git a/slideshows/autumn-budget-2026/slides/ClosingSlide.test.tsx b/slideshows/autumn-budget-2026/slides/ClosingSlide.test.tsx new file mode 100644 index 0000000..a089e51 --- /dev/null +++ b/slideshows/autumn-budget-2026/slides/ClosingSlide.test.tsx @@ -0,0 +1,35 @@ +import { act } from "react"; +import { createRoot, type Root } from "react-dom/client"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { funder, sources } from "../content"; +import { ClosingSlide } from "./index"; + +let container: HTMLDivElement; +let root: Root; + +beforeEach(async () => { + (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; + container = document.createElement("div"); + document.body.appendChild(container); + root = createRoot(container); + await act(async () => root.render()); +}); + +afterEach(() => { + act(() => root.unmount()); + container.remove(); +}); + +describe("the closing slide's funder credit", () => { + it('reads "Funded by" beside the Nuffield Foundation logo', () => { + const logo = container.querySelector(`img[alt="${funder.name}"]`); + expect(logo).not.toBeNull(); + expect(decodeURIComponent(logo!.getAttribute("src") ?? "")).toContain(funder.logo); + expect(logo!.closest("a")?.textContent).toContain("Funded by"); + }); + + it("links to the Nuffield Foundation's page for the project", () => { + const logo = container.querySelector(`img[alt="${funder.name}"]`); + expect(logo?.closest("a")?.getAttribute("href")).toBe(sources.nuffield.href); + }); +}); diff --git a/slideshows/introduction-2026/slides/DashboardEmbed.tsx b/slideshows/autumn-budget-2026/slides/DashboardEmbed.tsx similarity index 100% rename from slideshows/introduction-2026/slides/DashboardEmbed.tsx rename to slideshows/autumn-budget-2026/slides/DashboardEmbed.tsx diff --git a/slideshows/introduction-2026/slides/deck.module.css b/slideshows/autumn-budget-2026/slides/deck.module.css similarity index 92% rename from slideshows/introduction-2026/slides/deck.module.css rename to slideshows/autumn-budget-2026/slides/deck.module.css index ec0deb6..5df9931 100644 --- a/slideshows/introduction-2026/slides/deck.module.css +++ b/slideshows/autumn-budget-2026/slides/deck.module.css @@ -466,6 +466,25 @@ color: var(--pe-teal); } +/* The funder's credit: its white logo as downloaded, on the slide's dark background. */ +.closingFunder { + display: flex; + align-items: center; + gap: 1.25rem; + color: inherit; +} + +.closingFunderLabel { + font-size: 1rem; + font-weight: 500; + opacity: 0.85; +} + +.closingFunderLogo { + width: auto; + height: 4.5rem; +} + /* Title slide: keep the logo, speakers and date clear of each other on laptops. */ @media (max-height: 760px) { .titleCover { @@ -592,6 +611,10 @@ width: 1.9rem; height: 1.9rem; } + + .closingFunderLogo { + height: 4rem; + } } /* Safari on a laptop leaves less than 760px for the slide. */ @@ -637,4 +660,40 @@ .prepText { font-size: 0.875rem; } + + /* The closing slide makes room for the funder's credit, so "Thank you" + stays clear of the PolicyEngine logo above it. */ + .closing { + gap: 1rem; + } + + .closingTitle { + font-size: 2.4rem; + } + + .closingCard { + padding: 0.75rem; + } + + .closingQr { + width: 6rem; + height: 6rem; + } + + .closingContact { + padding: 0.35rem 0.9rem; + } + + .closingContactIcon { + width: 1.7rem; + height: 1.7rem; + } + + .closingFunder { + gap: 1rem; + } + + .closingFunderLogo { + height: 3rem; + } } diff --git a/slideshows/introduction-2026/slides/index.tsx b/slideshows/autumn-budget-2026/slides/index.tsx similarity index 95% rename from slideshows/introduction-2026/slides/index.tsx rename to slideshows/autumn-budget-2026/slides/index.tsx index a5f4019..90d07fc 100644 --- a/slideshows/introduction-2026/slides/index.tsx +++ b/slideshows/autumn-budget-2026/slides/index.tsx @@ -25,6 +25,7 @@ import SlideTitle from "@/components/layout/SlideTitle"; import { speakers } from "@/lib/speakers"; import { about, + funder, lastBudget, mockBudgets, pillars, @@ -277,7 +278,7 @@ export function ClosingSlide() {

Thank you

QR code for the Autumn Budget 2026 dashboard ))} + + {funder.label} + {funder.name} + ); diff --git a/slideshows/good-growth-foundation-2026/README.md b/slideshows/good-growth-foundation-2026/README.md index 2e3767e..2042f61 100644 --- a/slideshows/good-growth-foundation-2026/README.md +++ b/slideshows/good-growth-foundation-2026/README.md @@ -15,7 +15,7 @@ PolicyEngine UK for the Good Growth Foundation, for a 20 to 30 minute slot. 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 +(`introduction-2026`, since renamed `autumn-budget-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