diff --git a/lib/slideshows.ts b/lib/slideshows.ts index 579fe69..9c1873a 100644 --- a/lib/slideshows.ts +++ b/lib/slideshows.ts @@ -27,8 +27,10 @@ import { overhang2026Config } from "@/slideshows/overhang-2026/config"; import { overhang2026BackupConfig } from "@/slideshows/overhang-2026-backup/config"; import { overhang2026LightningConfig } from "@/slideshows/overhang-2026-lightning/config"; import { blsTaxsim2026Config } from "@/slideshows/bls-taxsim-2026/config"; +import { scottishGovernment2026Config } from "@/slideshows/scottish-government-2026/config"; export const slideshows: SlideshowConfig[] = [ + scottishGovernment2026Config, blsTaxsim2026Config, overhang2026Config, overhang2026LightningConfig, diff --git a/lib/speakers.ts b/lib/speakers.ts index 57e6580..8ac05be 100644 --- a/lib/speakers.ts +++ b/lib/speakers.ts @@ -36,6 +36,11 @@ export const speakers: Record = { title: 'Data Scientist', photo: '/headshots/ben-ogorek.jpg', }, + 'vahid-ahmadi': { + name: 'Vahid Ahmadi', + title: 'Research Associate', + photo: '/headshots/vahid-ahmadi.png', + }, 'maria-juaristi': { name: 'Maria Juaristi', title: 'PolicyEngine', diff --git a/public/headshots/vahid-ahmadi.png b/public/headshots/vahid-ahmadi.png new file mode 100644 index 0000000..17a654c Binary files /dev/null and b/public/headshots/vahid-ahmadi.png differ diff --git a/public/screenshots/scottish-government-2026/autumn-budget-2025-in-review.jpg b/public/screenshots/scottish-government-2026/autumn-budget-2025-in-review.jpg new file mode 100644 index 0000000..59377b9 Binary files /dev/null and b/public/screenshots/scottish-government-2026/autumn-budget-2025-in-review.jpg differ diff --git a/public/screenshots/scottish-government-2026/autumn-budget-2025.png b/public/screenshots/scottish-government-2026/autumn-budget-2025.png new file mode 100644 index 0000000..8c2d910 Binary files /dev/null and b/public/screenshots/scottish-government-2026/autumn-budget-2025.png differ diff --git a/public/screenshots/scottish-government-2026/electricity-vat-cut.jpg b/public/screenshots/scottish-government-2026/electricity-vat-cut.jpg new file mode 100644 index 0000000..5ad08cc Binary files /dev/null and b/public/screenshots/scottish-government-2026/electricity-vat-cut.jpg differ diff --git a/public/screenshots/scottish-government-2026/energy-price-shock-calculator.jpg b/public/screenshots/scottish-government-2026/energy-price-shock-calculator.jpg new file mode 100644 index 0000000..28b4732 Binary files /dev/null and b/public/screenshots/scottish-government-2026/energy-price-shock-calculator.jpg differ diff --git a/public/screenshots/scottish-government-2026/lvt-council-tax.jpg b/public/screenshots/scottish-government-2026/lvt-council-tax.jpg new file mode 100644 index 0000000..e218706 Binary files /dev/null and b/public/screenshots/scottish-government-2026/lvt-council-tax.jpg differ diff --git a/public/screenshots/scottish-government-2026/middle-east-war-living-standards.jpg b/public/screenshots/scottish-government-2026/middle-east-war-living-standards.jpg new file mode 100644 index 0000000..a09f602 Binary files /dev/null and b/public/screenshots/scottish-government-2026/middle-east-war-living-standards.jpg differ diff --git a/public/screenshots/scottish-government-2026/scotland-income-tax-reform.jpg b/public/screenshots/scottish-government-2026/scotland-income-tax-reform.jpg new file mode 100644 index 0000000..12fb296 Binary files /dev/null and b/public/screenshots/scottish-government-2026/scotland-income-tax-reform.jpg differ diff --git a/public/screenshots/scottish-government-2026/scottish-budget-2026-27.png b/public/screenshots/scottish-government-2026/scottish-budget-2026-27.png new file mode 100644 index 0000000..6d6cf4d Binary files /dev/null and b/public/screenshots/scottish-government-2026/scottish-budget-2026-27.png differ diff --git a/public/screenshots/scottish-government-2026/targeted-energy-discount.jpg b/public/screenshots/scottish-government-2026/targeted-energy-discount.jpg new file mode 100644 index 0000000..d0c0829 Binary files /dev/null and b/public/screenshots/scottish-government-2026/targeted-energy-discount.jpg differ diff --git a/public/screenshots/scottish-government-2026/uk-ai-study.jpg b/public/screenshots/scottish-government-2026/uk-ai-study.jpg new file mode 100644 index 0000000..0802b45 Binary files /dev/null and b/public/screenshots/scottish-government-2026/uk-ai-study.jpg differ diff --git a/public/screenshots/scottish-government-2026/uk-cliff-watch.jpg b/public/screenshots/scottish-government-2026/uk-cliff-watch.jpg new file mode 100644 index 0000000..d0c0286 Binary files /dev/null and b/public/screenshots/scottish-government-2026/uk-cliff-watch.jpg differ diff --git a/public/screenshots/scottish-government-2026/young-worker-nics.jpg b/public/screenshots/scottish-government-2026/young-worker-nics.jpg new file mode 100644 index 0000000..11a89db Binary files /dev/null and b/public/screenshots/scottish-government-2026/young-worker-nics.jpg differ diff --git a/slideshows/scottish-government-2026/README.md b/slideshows/scottish-government-2026/README.md new file mode 100644 index 0000000..c8e9284 --- /dev/null +++ b/slideshows/scottish-government-2026/README.md @@ -0,0 +1,96 @@ +# Scottish Government meeting, 9 October 2026 + +17 slides at `/slides/scottish-government-2026`, in five parts, each opened by a +section slide: + +1. How we build the UK's microdata: the pipeline with the sources that feed + each stage and the Scottish target table (slides 3–5) +2. Modelling property income: a waterfall of what each source measures, and the + steps that build landlords' income (slides 6–8) +3. An analysis example: the targeted energy bill discount, by region and across + Scotland's income distribution (slides 9–10) +4. Published work since April (slides 11–13) +5. Plan for the Autumn Budget (slides 14–16) + +The deck follows two earlier Scottish Government presentations in +`PolicyEngine/policyengine-demo`: + +- [February, PR #1](https://github.com/PolicyEngine/policyengine-demo/pull/1) + (commit `c927448b371013ee428acdb3f2e407b27a01adbe`): methodology, the Enhanced + FRS pipeline, the 1,428 Scotland calibration targets and the Scottish Budget + 2026-27 dashboard. +- [March, PR #2](https://github.com/PolicyEngine/policyengine-demo/pull/2) + (commit `159b8bcc469bcee8681972b6046636dfbfc241b0`): Spring Statement 2026, + research from February and March, and the Claude plugin. + +It doesn't repeat that material. Part 1 is framed as what changed since +February, and part 4 starts in April. + +## Editing and presenting + +- `config.ts` controls the slide order and meeting metadata. +- `content.ts` holds the pinned sources, tables, publication cards and Budget plan. +- `slides/index.tsx` contains the slides. +- `slides/figures.tsx` draws the pipeline graph, the target table, the property + income waterfall and step diagram, and the two energy charts (HTML and CSS, no chart + library). The pipeline and target table follow + [l0-ima-2026](https://github.com/PolicyEngine/policyengine-slides/tree/main/slideshows/l0-ima-2026). +- `SPEAKER-NOTES.md` gives the presenter track, sources and what not to claim. +- Presenter headshots come from `lib/speakers` (Vahid's is from the app-v2 team page). +- Cover images for part 4 are in `public/screenshots/scottish-government-2026/`, + copied from `policyengine-app-v2` (`app/public/assets/posts/`) and resized. + `autumn-budget-2025-in-review.jpg` is a 1600×1000 screenshot of the live review + dashboard, taken on 8 October 2026. + +Run `bun install --frozen-lockfile`, then `bun dev`. Open +`http://localhost:3000/slides/scottish-government-2026`. Arrow keys move between +slides and `F` enters fullscreen. Links open in a new tab. Export with +`bun run export scottish-government-2026 /path/to/output.pdf`. + +`ReleaseChecksSlide`, which embeds the UK calibration diagnostics site, is out +of the slide order: on 9 October the embed showed "HF fetch failed 401" with a +private dataset name. Add it back to `config.ts` once the dashboard loads its +release. + +Slide 10 charts the targeted energy bill discount and links its dashboard. Slide +15 links both the 2025 +Budget dashboard and [Autumn Budget 2025 in review](https://github.com/PolicyEngine/autumn-budget-2025-in-review), +including its public review dashboard. + +## Evidence and status + +Checked on 8 October 2026: + +- Microcosm main `75167a688ea83316654f1d794542124b4277bda9` and policyengine-uk + main `f1a9a3cc2885a42e58484d335e8ba0a02509f749`. +- Publications: `policyengine-app-v2` main `c83e129de5b14c0972f3070617490e8100e8fcc8` + (`app/src/data/posts/posts.json` and `app/src/data/apps/apps.json`). Every + link on the slides returned HTTP 200. +- Autumn Budget 2026 repository: `eb77d72b5e353b0cb85fecaf806f3a081753bbfa`. + It still contains the inherited 2025 measures, so part 5 is a plan. +- Scottish target inventory at the pinned Microcosm commit: 58 national rows for + `S92000003`; 831 local-authority rows across 32 Scottish councils; 1,026 + constituency rows across 57 Scottish Westminster constituencies. Counts refer + to target definitions, rather than passed fit checks. Council-tax band H has + 31 local-authority rows; other council-tax bands have 32. + +- Property income: HMRC Property Rental Income Statistics 2026 (individual + landlords, 2024-25: rent received £49.81bn, expenses £30.03bn; finance costs + pro-rated from all landlords' £12.82bn of £34.75bn). The step diagram + follows Microcosm #1145 as of 8 October, with HMRC's rental statistics kept as + a check rather than a calibration target. +- Energy discount: `uk-energy-reforms` main `14bf3b0afc121b092bbd7545c9fb4963e8ab5c72`, + results for 2026-27, Resolution Foundation flat option (`rf_flat`), Microcosm + UK national release (`aa31bdf6`), policyengine-uk 2.102.3. Region rows are the + published `by_region` results. Scotland's deciles were computed from the same + run, after checking that it reproduces the published GB deciles and Scotland's + region row; each decile's effective sample is 38 to 102 households. + +What the deck deliberately does not claim: + +- no constituency or council fit figures: the local release isn't certified; +- no property income results from the #1145 test builds: the decisions are + still open; +- nothing about Scotland setting its own property income rates; +- no VAT or fuel duty totals from the certified release, which are being fixed + in open policyengine-uk pull requests. diff --git a/slideshows/scottish-government-2026/SPEAKER-NOTES.md b/slideshows/scottish-government-2026/SPEAKER-NOTES.md new file mode 100644 index 0000000..d07d678 --- /dev/null +++ b/slideshows/scottish-government-2026/SPEAKER-NOTES.md @@ -0,0 +1,334 @@ +# Scottish Government presenter notes + +Meeting: 9 October 2026. This follows the February deck (methodology, the +Enhanced FRS pipeline, Scotland calibration targets, the Scottish Budget +dashboard) and the March update (Spring Statement 2026, research from February +and March, the Claude plugin). Don't re-explain those; refer back to them. + +About 23 minutes plus discussion: roughly 5 on part 1, 5 on part 2, 5 on part 3, +3 on part 4 and 5 on part 5. + +## 1. Title + +PolicyEngine: data pipeline, property income and Budget planning. Presenters: +Vahid Ahmadi, Max Ghenis and María Juaristi. + +## 2. Today + +Five parts: how we build Scotland's microdata, how we model property income, an +analysis example (a targeted energy bill discount), what we have published since +April, and the Autumn Budget plan. + +## 3. Part 1: How we build the UK's microdata + +## 4. From surveys to a population that represents the UK + +Read left to right. Each box below is a stage; the grey box above it names the +data that feeds it. + +1. Survey households: the Family Resources Survey 2024-25 gives people, families, + earnings and benefits. +2. Add tax records: the Survey of Personal Incomes adds high incomes and income + detail the survey misses. +3. Fill the gaps: wealth from the Wealth and Assets Survey, spending from the + Living Costs and Food Survey, bus travel from the National Travel Survey and + capital gains from HMRC statistics. Values are learned from similar + households in those sources, which keeps the variation between households + rather than giving everyone an average. This is statistical matching, not + linking the same household across surveys. +4. Place in Scotland: this runs only in the local-area build, which is still + being validated; the national release used in part 3 stops at region. In + the local build each household is given a 2022 Output Area drawn at random + in proportion to census household counts within its region, which gives its + council area and Westminster constituency. Council and constituency fit + comes from calibration, not from the survey. +5. Match official totals: calibration changes how many real households each + record represents. It doesn't change a record's income or circumstances. + The weights are fitted to UK totals, including Scotland's own (next slide), + so Scotland is represented within one UK dataset. + +Say which dataset this is: Microcosm UK, our new build. Its first certified +national release (4 October) is what part 3 uses. The website's UK default +still runs on the Enhanced FRS from February, now release 1.56.16. + +Keep the build machinery out of the explanation. If asked what changed since +February: the spine is FRS 2024-25, high incomes come from the SPI, and local +geography uses Scottish Output Areas with council and 2024 Westminster +constituency lookups. The Effects of Taxes and Benefits survey also feeds the +gap-filling stage (VAT and benefits in kind); it's left off the slide for space. + +Sources: [UK build graph](https://github.com/PolicyEngine/microcosm/blob/75167a688ea83316654f1d794542124b4277bda9/docs/uk-full-build-graph.md), +[source manifest](https://github.com/PolicyEngine/microcosm/blob/75167a688ea83316654f1d794542124b4277bda9/packages/microcosm-build/src/microcosm/build/uk/spec/sources.yaml), +[geography assignment](https://github.com/PolicyEngine/microcosm/blob/75167a688ea83316654f1d794542124b4277bda9/docs/geography-assignment.md). +Layout adapted from the [IMA pipeline diagram](https://github.com/PolicyEngine/policyengine-slides/blob/main/slideshows/l0-ima-2026/components/PipelineDiagram.tsx). + +## 5. The official Scottish totals we match + +The live calibration dashboard slide was taken out on 9 October: its embed +showed "HF fetch failed 401" and a private dataset name. Don't open the +dashboard on the call or quote fit percentages. + +1,915 target rows: 58 Scotland-wide, 831 across 32 council areas and 1,026 +across 57 Westminster constituencies. Only the 58 Scotland-wide rows are in a +released dataset (the national release); the 1,857 council and constituency +rows are for the local-area build, still being validated. Say plainly that +council and constituency results aren't released yet. In February we showed +1,428 targets; the definitions have grown since, but this isn't a production +upgrade yet. + +- Population by age: 9 age bands plus children under 16 and babies under 1 + Scotland-wide; 8 bands in each council and constituency (NRS estimates for + Scottish areas). +- Universal Credit: Scotland-wide, the households with a child under 1; in + constituencies, also by number of children. +- Council tax: dwellings by band A–H plus the total Scotland-wide; bands A–H by + council. Band H has 31 council rows, the others 32. +- Taxpayers, income and income tax: ten income bands, Scotland-wide. +- Bus: passenger revenue and government support, from Scottish Transport + Statistics. + +These are Westminster constituencies, not Scottish Parliament constituencies. +Output Areas locate records; they aren't a target level. + +To reproduce the counts, select reference rows whose ledger selector's +`geography_id` starts with `S`, then group by `geography_level` and family. Do +this separately in the national and local files. + +Sources: [national targets](https://github.com/PolicyEngine/microcosm/blob/75167a688ea83316654f1d794542124b4277bda9/packages/microcosm-build/src/microcosm/build/uk/target_references.json), +[local targets](https://github.com/PolicyEngine/microcosm/blob/75167a688ea83316654f1d794542124b4277bda9/packages/microcosm-build/src/microcosm/build/uk/local_target_references.json). + +## 6. Part 2: Modelling property income + +## 7. Landlords' income, measured three ways + +Read the waterfall left to right. It starts from HMRC's rent received by +individual landlords in 2024-25 and takes off one kind of cost at a time. The +three dark bars are the totals each source counts, all for 2024-25; the light +bars between them are what the next source leaves out. + +- Rent received, £49.8bn: HMRC's property rental income statistics, before any + costs. +- Less allowable expenses, £19.0bn: repairs, letting fees, insurance and other + running costs come off before tax. +- Taxable profit, about £31bn: what the SPI records and income tax is charged + on. It still includes mortgage interest. +- Less mortgage interest, £11.1bn: since 2020-21 it isn't deducted; it earns a + 20% tax reduction instead, rising to 22% from April 2027. That leaves £19.8bn + of profit after all costs, which no source publishes directly. +- Less capital repaid, and coverage, £4.4bn: the gap between that £19.8bn and + the survey's total. Part is the mortgage capital the survey also takes off; + part is landlords and rent the survey doesn't capture. The two can't be + separated from published figures. +- Rent after mortgage payments, £15.4bn: what the FRS asks for. Landlords give + rent "after paying for" the items on show card K6: mortgage payments, + repairs, loan interest, rent, rates and insurance, legal costs and services. + This is the FRS 2024-25 grossed up: 1,022 adults reporting a profit, 1.75m + landlords. Reported losses (£0.3bn, 67 adults) and sub-letting rent (£0.5bn) + are left out; net of losses it is £15.1bn. + +Open part 2 by crediting Helen: "Thank you again for passing on HMRC's +clarification. This chart is that point worked through. The rental statistics +count rent before allowable expenses. When we wrote in June that the Enhanced +FRS calibrated to them, that overstated taxable property income." Vahid +already thanked her in writing on 5 October. + +The Enhanced FRS, still the website's UK default (1.56.16), multiplies SPI +property profit by 1.9, the 2020-21 ratio of rent received to profit +(policyengine-uk-data hmrc_spi.py), so it treats about £56bn as taxable +property income: more than the £49.8bn of rent itself, against roughly £31bn. + +Every figure on slides 7 and 8 is a UK total, and the property calibration +targets have no Scotland split. If asked about Scotland: in the FRS, Scotland's +landlord income rests on 68 adults, grossed up to 97,000 landlords and £0.75bn. +HMRC has 163,000 landlords with a Scottish address, with £2.82bn of rent and +£1.53bn of rent less expenses. + +If asked what today's data hold: Microcosm #1106 (5 October, the day after +the certified national release) says that release binds no property-income +target and its property income sits close to the raw FRS, which is likely too +low for landlords. Microcosm #1145, the next slide, is the fix and is still a +draft. If asked whether earlier published results were affected: analyses on +the Enhanced FRS carried the overstatement; check which dataset a given +dashboard used before answering for it. + +The "about £31bn" is HMRC's rent received less the expenses other than mortgage +interest. The SPI's own published figure is £29.35bn for 2023-24, about £31.8bn +uprated to 2025, so the two agree. The bar under the SPI label shows the +HMRC-derived £31bn, and its note gives the SPI's own £29.4bn. Slide 8 explains how we build each piece. + +HMRC publishes individuals' rent received (£49.81bn) and total expenses +(£30.03bn). Mortgage interest is published for all landlords (£12.82bn of +£34.75bn of expenses), so the £11.1bn is pro-rated by individuals' share of +expenses. + +Sources: [HMRC property rental income statistics 2026](https://www.gov.uk/government/statistics/property-rental-income-statistics/property-rental-income-statistics-2026), +[Microcosm #1106](https://github.com/PolicyEngine/microcosm/issues/1106). + +## 8. How we build landlords' income, step by step + +Two lanes merge. The top lane is landlords in the household survey; the bottom +lane is copies of survey households that carry incomes from tax records. Each +box names the data that informs it. + +1. FRS households: survey landlords report their rent after mortgage payments + (show card K6 nets mortgage interest and capital). A reported loss counts as + zero. +2. Copy households: 10,000 survey households are copied at random to carry + tax-record incomes, and they take half the population weight of working-age + households (a fifth for pension-age ones). Extra copies hold the incomes over + £200,000, sized to HMRC's taxpayer counts for those bands. +3. Draw tax-record incomes: each adult in a copy gets a full set of incomes, + including property profit and finance costs, from SPI taxpayers of a similar + age, sex and region (a quantile regression forest). Survey records keep their + own reported incomes. +4. Add back mortgage interest: survey landlords get finance costs from SPI + landlords with a similar profit and age: whether they have any follows the + SPI's share in their profit band, and the amount comes from a second model. + The interest is added back to their profit, so the model relieves it once, + as the tax reduction. Mortgage capital stays netted. +5. Rent received: every landlord with a profit, from either lane, is ranked by + profit and placed into HMRC's bands of rent received, keeping HMRC's share of + landlords in each band. Expenses are rent received less profit. +6. Calibrate weights: the national calibration matches the SPI's number of + landlords and their property income by total-income band (2023-24 tables, + unscaled). HMRC's rental statistics are a check, not a target: binding them + pulled the SPI amounts out of line in the test builds. +7. Compute tax: policyengine-uk applies the rules: Scottish rates for Scottish + taxpayers, the £1,000 allowance or actual expenses (not both), and a 20% tax + reduction on finance costs (22% from April 2027). + +In the test build of the final head (not a release), at calibrated weights: +rent received is £45.7bn against HMRC's £49.8bn for individuals, finance costs +£10.6bn against £11.1bn, and 2.77m landlords against 2.85m. The tax reduction +comes to about £1.9bn for 1.0m landlords in 2025. + +The model change (policyengine-uk #2172, released in 2.123.0) is merged. The +data change (Microcosm #1145) is a draft and details may change: whether the +survey add-back stays and whether it extends to reported losses (both put to +Max), how the open top band of rent received is set, and how landlords without +profit are treated. + +On Scotland: the GOV.UK rate table is marked as not applying to Scotland. In the +model, Scottish taxpayers' property income is taxed at the ordinary Scottish +rates, and finance-cost relief uses the single UK property basic rate. Don't say +anything about Scotland setting its own property rates; we have not checked +that. + +Sources: [Microcosm #1145](https://github.com/PolicyEngine/microcosm/pull/1145), +[policyengine-uk #2172](https://github.com/PolicyEngine/policyengine-uk/pull/2172), +[GOV.UK rates paper](https://www.gov.uk/government/publications/changes-to-tax-rates-for-property-savings-dividend-income/changes-to-tax-rates-for-property-savings-dividend-income). + +## 9. Part 3: An analysis example + +## 10. A targeted energy bill discount + +The Resolution Foundation's option from +[Billing me softly](https://www.resolutionfoundation.org/publications/billing-me-softly/): +a cut in gas and electricity unit rates, ready to switch on if needed in early +2027 (January to March), which RF sizes at about £2bn, an average of £175 per +eligible household. We model it as a flat £175 for households in Great Britain +that receive a means-tested benefit (the Warm Home Discount list), or where no +member has taxable income of £24,000 or more. Say "RF's average of £175", +never "£175 a year". 2026-27, on the Microcosm UK national release with policyengine-uk +2.102.3, as the dashboard shows by default. + +- GB: £2.09bn for 11.95m households (42% of 28.6m). +- Scotland: 1.16m of 2.66m households (44%) would be eligible, £204m or about + a tenth of the GB cost if all took it up. 24% qualify through a benefit and + would be enrolled automatically; 20% qualify only through the income test, + which RF says would rely on self-declaration this winter, so take-up there + would fall short. +- Today's Warm Home Discount works differently in Scotland: only Pension Credit + Guarantee Credit households are data-matched. Officials may raise it. +- If asked about the household count: our Scotland total is calibrated to + the council tax base, 2.62m chargeable dwellings in September 2025, carried + forward to 2026-27. That is a dwelling count, so it sits about 2% above + NRS's 2.57m households for 2025. If pressed on why the two differ, offer to + follow up rather than guess. +- Left chart: share of households eligible by region, split by route and sorted + by the total. The North East is highest (55%); London is lowest (35%), where + the income test reaches only 6% beyond those on benefits. +- Right chart: average gain per household for each tenth of Scotland's people, + ranked by household income after housing costs, adjusted for household size. + Averages include households that get nothing, so they track the share + reached: 94% of the lowest tenth (£164 on average) and 3% of the highest (£6). + +The Scottish deciles are computed for this slide from the same run as the +dashboard (validated against the published GB deciles and Scotland's region +row); the dashboard itself shows GB deciles. Each Scottish decile rests on an +effective sample of 38 to 102 households, so quote the shape rather than single +pounds. + +The dashboard also has the tiered option, a £2bn budget version, a bill-share +version and a household calculator. + +Sources: [dashboard](https://www.policyengine.org/uk/targeted-energy-discount), +[analysis at uk-energy-reforms 14bf3b0](https://github.com/PolicyEngine/uk-energy-reforms/tree/14bf3b0afc121b092bbd7545c9fb4963e8ab5c72/analyses/rf-billing-me-softly). + +## 11. Part 4: Published work since April + +## 12. Energy and the cost of living + +Each card opens the live dashboard. The energy work builds up from the April +price-shock analysis. We published the electricity VAT cut costing on +21 July 2026, the day the Prime Minister announced it; don't say we +anticipated it. The zero rate has applied across Great Britain, including +Scotland, since 1 October. The live dashboard's cover is a press photo of the +Prime Minister, so don't click through to it on the call. The +targeted discount card is the analysis from part 3. + +## 13. Work, benefits and new shocks + +CliffWatch is a tool officials can use directly. The land value tax post +(Progress and Poverty, 4 June 2026) used PolicyEngine to model replacing council +tax with a flat land value tax, which is relevant to Scotland's council tax +reform debate; its card shows the post's decile chart. The income tax card is +the dashboard (1 April) costing Reform UK Scotland's pre-election proposal, and +the card names the party because the dashboard does. We cost proposals from +any party on request. It ran on the Enhanced FRS, so its figures may move on +the new data. The two employer NICs +dashboards and the research library are linked in the line under the cards. +The bus fare cap and free childcare work is England only, so it's left off. + +Source: [UK research library](https://www.policyengine.org/uk/research). + +## 14. Part 5: Plan for the Autumn Budget + +## 15. Where we are starting + +Show the two dashboards we are building on: the UK Autumn Budget 2025 +dashboard and our Scottish Budget 2026-27 dashboard, which this team told us in +February they had studied. Don't re-present their results. + +Autumn Budget 2025 in review (published costings side by side, worked +household examples and a timeline) is a preview not yet on policyengine.org, +and its repository is private; mention it only as work in progress. + +Sources: [2025 dashboard](https://www.policyengine.org/uk/autumn-budget-2025), +[Scottish Budget 2026-27 dashboard](https://www.policyengine.org/uk/scottish-budget-2026-27). + +## 16. How Budget day will run + +The UK Budget is on Wednesday 28 October; the draft Scottish Budget 2027-28 +and the SFC's forecasts follow on Thursday 3 December. The SFC doesn't cost UK +Budget measures, so reconciling with it waits for 3 December. + +The development repository still contains the inherited 2025 measures. This +is a plan, not completed 2026 analysis. No Scotland-specific output is built +yet, and the national release has no constituency geography, so there will be +no Scottish council or constituency results on 28 October. Whether we repeat +the Scottish Budget dashboard on 3 December is Max's call; if he hasn't made +it, ask them what would help rather than promising it. + +## 17. Thank you + +## Likely questions + +- Data access: the code is open; the built dataset contains UK Data Service + licensed microdata and sits in a private repository. Don't offer "the data". +- Finance-cost relief at 22% from April 2027: the GOV.UK paper says relief is + given at the property basic rate (22%) and doesn't address Scottish + taxpayers. In the model, Scottish taxpayers' property income is taxed at the + ordinary Scottish rates and relief uses the UK property basic rate. +- Fit to Scottish statistics: the live diagnostics dashboard is down, so don't + quote fit percentages; offer to follow up in writing. diff --git a/slideshows/scottish-government-2026/config.ts b/slideshows/scottish-government-2026/config.ts new file mode 100644 index 0000000..7be996c --- /dev/null +++ b/slideshows/scottish-government-2026/config.ts @@ -0,0 +1,49 @@ +import type { SlideshowConfig } from "@/lib/types"; +import { + AgendaSlide, + AnalysisSectionSlide, + Budget2025Slide, + BudgetDaySlide, + BudgetSectionSlide, + ClosingSlide, + DataSectionSlide, + EnergyDiscountSlide, + EnergyWorkSlide, + PipelineSlide, + PropertyConceptsSlide, + PropertyModelSlide, + PropertySectionSlide, + ResearchSectionSlide, + TargetsSlide, + TitleSlide, + WorkBenefitsSlide, +} from "./slides"; + +export const scottishGovernment2026Config: SlideshowConfig = { + id: "scottish-government-2026", + title: "PolicyEngine: data pipeline, property income and Budget planning", + description: + "Follow-up to the February and March meetings: how Microcosm UK builds Scotland's microdata, property income, a targeted energy discount, UK work since April and the Autumn Budget 2026 plan.", + date: "2026-10-09", + location: "Scottish Government", + footerText: "Scottish Government · 9 October 2026", + slides: [ + TitleSlide, + AgendaSlide, + DataSectionSlide, + PipelineSlide, + TargetsSlide, + PropertySectionSlide, + PropertyConceptsSlide, + PropertyModelSlide, + AnalysisSectionSlide, + EnergyDiscountSlide, + ResearchSectionSlide, + EnergyWorkSlide, + WorkBenefitsSlide, + BudgetSectionSlide, + Budget2025Slide, + BudgetDaySlide, + ClosingSlide, + ], +}; diff --git a/slideshows/scottish-government-2026/content.ts b/slideshows/scottish-government-2026/content.ts new file mode 100644 index 0000000..9fd4329 --- /dev/null +++ b/slideshows/scottish-government-2026/content.ts @@ -0,0 +1,459 @@ +// Microcosm and policyengine-uk links are pinned to the commits checked on +// 8 October 2026 (microcosm main 75167a68, policyengine-uk main f1a9a3cc). +const microcosmCommit = "75167a688ea83316654f1d794542124b4277bda9"; +const budgetCommit = "eb77d72b5e353b0cb85fecaf806f3a081753bbfa"; +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 = { + pipeline: { + label: "UK build graph", + href: `${microcosm}/docs/uk-full-build-graph.md`, + }, + geography: { + label: "Geography assignment", + href: `${microcosm}/docs/geography-assignment.md`, + }, + inputs: { label: "UK source manifest", href: `${ukBuild}/spec/sources.yaml` }, + nationalTargets: { + label: "National targets", + href: `${ukBuild}/target_references.json`, + }, + localTargets: { + label: "Local targets", + href: `${ukBuild}/local_target_references.json`, + }, + release: { + label: "Release certification", + href: `${microcosm}/docs/uk-national-release-assembly-runbook-806.md`, + }, + diagnostics: { + label: "Live UK calibration diagnostics", + href: "https://calibration-diagnostics.vercel.app/calibration/dashboard/microcosm?country=uk", + }, + propertyIssue: { + label: "Microcosm #1106", + href: "https://github.com/PolicyEngine/microcosm/issues/1106", + }, + propertyEngine: { + label: "policyengine-uk #2172", + href: "https://github.com/PolicyEngine/policyengine-uk/pull/2172", + }, + pris: { + label: "HMRC property rental income statistics 2026", + href: "https://www.gov.uk/government/statistics/property-rental-income-statistics/property-rental-income-statistics-2026", + }, + propertyRates: { + label: "GOV.UK property, savings and dividend rates", + href: "https://www.gov.uk/government/publications/changes-to-tax-rates-for-property-savings-dividend-income/changes-to-tax-rates-for-property-savings-dividend-income", + }, + budget2025: { + label: "Autumn Budget 2025 dashboard", + href: "https://www.policyengine.org/uk/autumn-budget-2025", + }, + scottishBudget: { + label: "Scottish Budget 2026-27 dashboard", + href: "https://www.policyengine.org/uk/scottish-budget-2026-27", + }, + propertyData: { + label: "Microcosm #1145 (draft)", + href: "https://github.com/PolicyEngine/microcosm/pull/1145", + }, + budgetReviewDashboard: { + label: "Autumn Budget 2025 in review", + href: "https://autumn-budget-2025-in-review.vercel.app/uk/autumn-budget-2025-in-review", + }, + budget2026: { + label: "2026 development repository", + href: `https://github.com/PolicyEngine/uk-autumn-budget-dashboard-2026/tree/${budgetCommit}`, + }, + methodNote: { + label: "Constituency method note (Microcosm #1131)", + href: "https://github.com/PolicyEngine/microcosm/issues/1131", + }, + research: { + label: "UK research library", + href: "https://www.policyengine.org/uk/research", + }, + 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/", + }, +} satisfies Record; + +// The Microcosm UK build, told for Scotland: what each stage adds and which +// sources feed it (spine stages in sources.yaml at the pinned commit). +export const pipelineStages = [ + { + title: "Survey households", + adds: "People, families, earnings and benefits", + sources: ["Family Resources Survey 2024-25 (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 Scotland", + adds: "Local-area build, in validation: an Output Area, council and constituency for each household", + sources: ["NRS 2022 Output Areas and lookups"], + }, + { + title: "Match official totals", + adds: "Reweight households to UK and Scottish statistics", + sources: ["NRS, ONS, HMRC, DWP and Scottish Government statistics"], + }, +]; + +// Scottish target rows at the pinned Microcosm commit, grouped by family. +// Counts are reference rows (target definitions), not a claim that the local +// build has passed release checks. Columns: Scotland, 32 council areas, +// 57 Westminster constituencies. +export type TargetRow = { + family: string; + publisher: string; + scotland: number; + councils: number; + constituencies: number; +}; + +export const scotlandTargets: TargetRow[] = [ + { family: "Population by age", publisher: "NRS and ONS mid-year estimates", scotland: 11, councils: 256, constituencies: 456 }, + { family: "Employment and self-employment income", publisher: "HMRC", scotland: 0, councils: 128, constituencies: 228 }, + { family: "Universal Credit households", publisher: "DWP", scotland: 1, councils: 32, constituencies: 285 }, + { family: "Council tax dwellings by band", publisher: "Scottish Government", scotland: 9, councils: 255, constituencies: 0 }, + { family: "Housing tenure", publisher: "Scotland's Census 2022", scotland: 0, councils: 128, constituencies: 0 }, + { family: "Households", publisher: "Scotland's Census 2022", scotland: 0, councils: 32, constituencies: 57 }, + { family: "Taxpayers, income and tax by band", publisher: "HMRC", scotland: 30, councils: 0, constituencies: 0 }, + { family: "State Pension recipients", publisher: "DWP", scotland: 2, councils: 0, constituencies: 0 }, + { family: "Capital gains and gains taxpayers", publisher: "HMRC", scotland: 2, councils: 0, constituencies: 0 }, + { family: "Bus revenue and government support", publisher: "Scottish Transport Statistics", scotland: 2, councils: 0, constituencies: 0 }, + { family: "Scottish Child Payment spending", publisher: "Scottish Government", scotland: 1, councils: 0, constituencies: 0 }, +]; + +// HMRC Property Rental Income Statistics 2026, individual landlords, 2024-25. +// Taxable profit here is rent received less the expenses other than finance +// costs; the SPI's own figure is £29.35bn for 2023-24 (Table 3.7). +// Receipts (Table 2) and total expenses (Table 6) are published for +// individuals; residential finance costs (Table 8, £12.82bn for all landlords) +// are pro-rated to individuals by their share of expenses (30.03 / 34.75). +const prisIndividualExpenses = 30.03; +const prisFinanceCosts = 12.82 * (prisIndividualExpenses / 34.75); + +const prisReceipts = 49.81; +const prisOtherExpenses = prisIndividualExpenses - prisFinanceCosts; + +// FRS 2024-25, grossed with GROSS4: ROYYR1 (rent from other property after +// the show card K6 items, including mortgage payments) x 52 for the 1,022 +// adults reporting a profit, 1.75m landlords. Reported losses (£0.32bn) and +// sub-letting rent (£0.49bn) are left out. +const frsRentAfterMortgage = 15.4; + +// Waterfall from rent received down to what the FRS asks for. The three totals +// are what each source counts; the deductions between them are what the next +// source leaves out. The last deduction is the residual between profit after +// all costs and the survey's total. +export type WaterfallStep = { + kind: "total" | "deduction"; + label: string; + value: number; + valueLabel: string; + note: string; + source?: string; +}; + +const prisProfitAfterCosts = prisReceipts - prisIndividualExpenses; +const frsResidual = prisProfitAfterCosts - frsRentAfterMortgage; + +export const propertyWaterfall: WaterfallStep[] = [ + { + kind: "total", + label: "Rent received", + value: prisReceipts, + valueLabel: "£49.8bn", + note: "Everything tenants pay, before any costs", + source: "HMRC rental income statistics", + }, + { + kind: "deduction", + label: "Allowable expenses", + value: prisOtherExpenses, + valueLabel: `−£${prisOtherExpenses.toFixed(1)}bn`, + note: "Repairs, letting fees, insurance: deducted before tax", + }, + { + kind: "total", + label: "Taxable profit", + value: prisReceipts - prisOtherExpenses, + valueLabel: "about £31bn", + note: "What income tax is charged on; the SPI records £29.4bn for 2023-24", + source: "Survey of Personal Incomes", + }, + { + kind: "deduction", + label: "Mortgage interest", + value: prisFinanceCosts, + valueLabel: `−£${prisFinanceCosts.toFixed(1)}bn`, + note: "Not deducted: it earns a 20% tax reduction instead, 22% from April 2027", + }, + { + kind: "deduction", + label: "Capital repaid, and coverage", + value: frsResidual, + valueLabel: `−£${frsResidual.toFixed(1)}bn`, + note: "Mortgage capital, which the survey also takes off, and landlords it misses", + }, + { + kind: "total", + label: "Rent after mortgage payments", + value: frsRentAfterMortgage, + valueLabel: `£${frsRentAfterMortgage.toFixed(1)}bn`, + note: "What landlords report in the survey", + source: "Family Resources Survey", + }, +]; + +// Microcosm #1145 (draft): the steps that build landlords' income, from the +// stage declarations in sources.yaml at the PR head (spi_support_channel, +// spi_income_band_donors, hmrc_spi_income_spine, property_components). Two +// lanes, survey landlords and the tax-record copies, merge before rent +// received. Details may change before merge. +export type PropertyStep = { title: string; text: string; source: string }; + +export const propertyLanes = { + survey: { + label: "Landlords in the household survey", + steps: [ + { + title: "FRS households", + text: "Landlords report rent after mortgage payments", + source: "FRS 2024-25", + }, + { + title: "Add back mortgage interest", + text: "Finance costs drawn from similar tax-record landlords, added to profit", + source: "SPI 2022-23", + }, + ], + }, + copies: { + label: "Copies carrying tax-record incomes", + steps: [ + { + title: "Copy households", + text: "10,000 survey households copied, plus copies for incomes over £200,000", + source: "FRS · HMRC taxpayer counts", + }, + { + title: "Draw tax-record incomes", + text: "Each adult gets profit and finance costs from taxpayers like them", + source: "SPI 2022-23", + }, + ], + }, +} satisfies Record; + +export const propertyMergedSteps: PropertyStep[] = [ + { + title: "Rent received", + text: "Landlords ranked by profit into HMRC's bands; expenses are the difference", + source: "HMRC rental statistics", + }, + { + title: "Calibrate weights", + text: "Match landlord numbers and property income by income band", + source: "HMRC income tables 2023-24", + }, + { + title: "Compute tax", + text: "£1,000 allowance or expenses, and a 20% tax reduction on finance costs", + source: "policyengine-uk", + }, +]; + +// Targeted energy bill discount (uk-energy-reforms main 14bf3b0): the +// Resolution Foundation's flat £175 option, 2026-27, Microcosm UK national +// release, policyengine-uk 2.102.3. Regions are Great Britain's eleven. +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 }, +]; + +// Scotland only, from the same run: deciles rank Scotland's people by household +// income after housing costs, adjusted for household size. Average gains +// include households that receive nothing. +export const scotlandEnergyDeciles = [ + { decile: 1, averageGain: 164, shareReceiving: 0.94 }, + { decile: 2, averageGain: 159, shareReceiving: 0.91 }, + { decile: 3, averageGain: 141, shareReceiving: 0.81 }, + { decile: 4, averageGain: 119, shareReceiving: 0.68 }, + { decile: 5, averageGain: 80, shareReceiving: 0.46 }, + { decile: 6, averageGain: 30, shareReceiving: 0.17 }, + { decile: 7, averageGain: 13, shareReceiving: 0.07 }, + { decile: 8, averageGain: 14, shareReceiving: 0.08 }, + { decile: 9, averageGain: 13, shareReceiving: 0.07 }, + { decile: 10, averageGain: 6, shareReceiving: 0.03 }, +]; + +export const scotlandEnergyHeadline = { + households: "2.66m", + recipients: "1.16m", + cost: "£204m", + gbCost: "£2.09bn", +}; + +// Section 4: published UK work, 8 April – 8 October 2026 (app-v2 c83e129). +export type Publication = { + title: string; + date: string; + finding: string; + href: string; + image: string; + alt: string; +}; + +const cover = (file: string) => `/screenshots/scottish-government-2026/${file}`; + +export const energyPublications: Publication[] = [ + { + title: "Middle East war and UK living standards", + date: "September 2026", + finding: + "How energy, fuel and food price rises hit households in 2027-28, with ten policy responses compared", + href: "https://www.policyengine.org/uk/middle-east-war-living-standards", + image: cover("middle-east-war-living-standards.jpg"), + alt: "Cover image for the Middle East war living standards dashboard", + }, + { + title: "Targeted energy bill discount", + date: "October 2026", + finding: + "Cost, reach and distributional effects of the Resolution Foundation's proposed discount", + href: "https://www.policyengine.org/uk/targeted-energy-discount", + image: cover("targeted-energy-discount.jpg"), + alt: "Cover image for the targeted energy bill discount dashboard", + }, + { + title: "Temporary VAT cut on domestic electricity", + date: "July 2026", + finding: + "Fiscal cost and distributional impact of a six-month cut from 5% to 0% from October 2026", + href: "https://www.policyengine.org/uk/electricity-vat-cut", + image: cover("electricity-vat-cut.jpg"), + alt: "VAT on household electricity cut from 5% to 0%", + }, + { + title: "Energy price shock", + date: "April 2026", + finding: + "Price shock scenarios split by electricity and gas, with five policy responses", + href: "https://www.policyengine.org/uk/energy-price-shock", + image: cover("energy-price-shock-calculator.jpg"), + alt: "Cover image for the energy price shock dashboard", + }, +]; + +export const workPublications: Publication[] = [ + { + title: "UK CliffWatch", + date: "June 2026", + finding: + "Where benefit withdrawal and taxes create cliffs and high marginal rates as earnings rise", + href: "https://www.policyengine.org/uk/uk-cliff-watch", + image: cover("uk-cliff-watch.jpg"), + alt: "Cover image for UK CliffWatch", + }, + { + title: "Who bears the AI shock?", + date: "August 2026", + finding: + "AI employment, wage and capital shocks traced through the tax-benefit system", + href: "https://www.policyengine.org/uk/research/uk-ai-study", + image: cover("uk-ai-study.jpg"), + alt: "Cover image for the UK AI study", + }, + { + title: "Replacing council tax with a land value tax", + date: "June 2026", + finding: + "A revenue-neutral swap modelled with PolicyEngine: most households gain and poverty edges down", + href: "https://progressandpoverty.substack.com/p/how-replacing-council-tax-with-a", + image: cover("lvt-council-tax.jpg"), + alt: "Chart from the land value tax post: average net income change by income decile, 2026-27", + }, + { + title: "Reform UK Scotland's income tax proposal", + date: "April 2026", + finding: + "Costing the party's plan to replace Scotland's six bands with the rest-of-UK structure, then cut rates", + href: "https://www.policyengine.org/uk/scotland-income-tax-reform", + image: cover("scotland-income-tax-reform.jpg"), + alt: "Scotland's six income tax bands beside the rest-of-UK three", + }, +]; + +export const nicsLinks = [ + { + label: "young workers", + href: "https://www.policyengine.org/uk/young-worker-nics", + }, + { + label: "recently inactive employees", + href: "https://www.policyengine.org/uk/nics-exemption-inactive-employees", + }, +]; + +// Section 5: Autumn Budget 2026. +export const budgetPlan = [ + { + stage: "Before 28 October", + title: "Pin the model and data", + text: "Fix the policyengine-uk version and data release, check current law and prepare likely scenarios.", + }, + { + stage: "28 October: UK Budget day", + title: "Encode and test the measures", + text: "Read the published documents, implement each measure and test its timing and scope.", + }, + { + stage: "As results clear review", + title: "Publish the dashboard", + text: "Fiscal, distributional and household results, with Scotland shown separately.", + }, + { + stage: "After the OBR and SFC publish", + title: "Reconcile with official figures", + text: "Explain differences from OBR costings and, for Scotland, from the Scottish Fiscal Commission forecasts published with the draft Scottish Budget on 3 December, and revise.", + }, +]; diff --git a/slideshows/scottish-government-2026/slides/deck.module.css b/slideshows/scottish-government-2026/slides/deck.module.css new file mode 100644 index 0000000..004acf3 --- /dev/null +++ b/slideshows/scottish-government-2026/slides/deck.module.css @@ -0,0 +1,1386 @@ +/* Publication cards: a fixed-ratio cover keeps the grid even. */ +.cover { + aspect-ratio: 3 / 2; + height: auto; + max-height: 28vh; +} + +/* 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; +} + +.diagnosticsEmbed { + width: 100%; + height: min(650px, calc(100vh - 390px)); + border: 1px solid var(--color-gray-200); + border-radius: 0.5rem; + background: var(--bg-secondary); +} + +/* 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; +} + +/* Budget starting points: two dashboard previews, each a link. */ +.previewGrid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 2.5rem; +} + +.previewCard { + display: block; + overflow: hidden; + border: 1px solid var(--color-gray-200); + border-radius: 0.75rem; + background: white; + box-shadow: 0 1px 2px rgb(0 0 0 / 0.05); + transition: border-color 150ms, box-shadow 150ms; +} + +.previewCard:hover { + border-color: var(--pe-teal); + box-shadow: 0 4px 12px rgb(0 0 0 / 0.08); + text-decoration: none !important; +} + +.previewImage { + display: block; + width: 100%; + height: 38vh; + border-bottom: 1px solid var(--color-gray-200); + object-fit: cover; + object-position: top; +} + +.previewBody { + border-left: 4px solid var(--pe-teal); + padding: 1.1rem 1.4rem 1.25rem; +} + +.previewBody h2 { + color: var(--pe-dark); + font-size: 1.6rem; + font-weight: 700; + line-height: 1.25; +} + +.previewBody p { + margin-top: 0.4rem; + color: var(--color-gray-600); + font-size: 1.2rem; + line-height: 1.4; +} + +.previewBody span { + display: inline-block; + margin-top: 0.75rem; + color: var(--pe-teal); + font-size: 1.15rem; + font-weight: 600; +} + +.previewCard:hover .previewBody span { + text-decoration: underline; +} + +/* Agenda: five rows that fit a laptop screen. */ +.agenda { + display: grid; + gap: 0; +} + +.agendaRow { + display: grid; + grid-template-columns: 4.5rem 1fr; + align-items: baseline; + gap: 1.5rem; + border-bottom: 1px solid var(--color-gray-200); + padding: 1.1rem 0; +} + +.agendaRow > span { + color: var(--pe-teal); + font-size: 2.25rem; + font-weight: 600; +} + +.agendaRow h2 { + color: var(--pe-dark); + font-size: 1.75rem; + font-weight: 600; + line-height: 1.25; +} + +.agendaRow p { + margin-top: 0.25rem; + color: var(--color-gray-600); + font-size: 1.3rem; + line-height: 1.4; +} + +/* 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); +} + +/* Scottish target table, in the style of l0-ima-2026's target surface. */ +.targetTable { + overflow: hidden; + border: 1px solid var(--color-gray-200); + border-radius: 0.5rem; +} + +.targetRow { + display: grid; + grid-template-columns: 2.8fr 2fr 0.8fr 0.8fr 1fr 0.6fr; + align-items: baseline; + gap: 1rem; + border-top: 1px solid var(--color-gray-100); + padding: 0.55rem 1.5rem; + color: var(--color-gray-700); + font-size: 1.1rem; +} + +.targetHead { + border-top: 0; + background: var(--bg-secondary); + color: var(--pe-dark); + font-size: 0.8rem; + 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; +} + +.tableNote { + margin-top: 1rem; + color: var(--color-gray-500); + font-size: 1rem; +} + +/* Property income: a waterfall from rent received to the survey's measure. */ +.waterfall { + --wf-gap: 1.25rem; + padding-top: 1.5rem; +} + +.wfPlot { + position: relative; + height: 20rem; + margin-left: 3.75rem; +} + +.wfGrid { + position: absolute; + right: 0; + left: 0; + border-top: 1px solid var(--color-gray-200); +} + +.wfGrid span { + position: absolute; + right: calc(100% + 0.6rem); + color: var(--color-gray-500); + font-size: 0.85rem; + font-variant-numeric: tabular-nums; + transform: translateY(-50%); + white-space: nowrap; +} + +.wfColumns, +.wfCaptions { + display: grid; + grid-template-columns: repeat(6, minmax(0, 1fr)); + gap: var(--wf-gap); +} + +.wfColumns { + position: absolute; + inset: 0; +} + +.wfColumn { + position: relative; +} + +.wfConnector { + position: absolute; + left: calc(-18% - var(--wf-gap)); + width: calc(36% + var(--wf-gap)); + border-top: 2px dotted var(--color-gray-400); +} + +.wfBar { + position: absolute; + right: 18%; + left: 18%; + display: flex; + align-items: center; + justify-content: center; +} + +.wfBar_total { + border-radius: 4px 4px 0 0; + background: var(--color-teal-700); +} + +.wfBar_deduction { + border-radius: 4px; + background: var(--color-gray-300); +} + +.wfBarInterest { + background: var(--pe-amber-light); +} + +.wfInside { + color: var(--pe-darker); + font-size: 1.15rem; + font-variant-numeric: tabular-nums; + font-weight: 700; +} + +/* A short deduction bar puts its label underneath. */ +.wfBelow { + position: absolute; + top: calc(100% + 0.3rem); +} + +.wfValue { + position: absolute; + right: 0; + left: 0; + margin-bottom: 0.3rem; + color: var(--pe-dark); + font-size: 1.15rem; + font-variant-numeric: tabular-nums; + font-weight: 700; + text-align: center; +} + +.wfCaptions { + margin-top: 0.75rem; + margin-left: 3.75rem; + border-top: 2px solid var(--color-gray-300); + padding-top: 0.75rem; +} + +.wfCaptions h2 { + color: var(--pe-dark); + font-size: 1.15rem; + font-weight: 700; + line-height: 1.25; +} + +.wfCaptionDeduction h2 { + color: var(--color-gray-600); +} + +.wfCaptions p { + margin: 0.3rem 0 0.5rem; + color: var(--color-gray-600); + font-size: 1rem; + line-height: 1.35; +} + +.waterfallTakeaway { + margin-top: 1.5rem; + border-left: 4px solid var(--pe-teal); + padding-left: 1.25rem; + color: var(--color-gray-700); + font-size: 1.25rem; + line-height: 1.45; +} + +.finePrint { + margin-top: 1.25rem; + color: var(--color-gray-500); + font-size: 0.95rem; +} + +/* Property income: two lanes of steps that merge into the shared steps. */ +.flow { + display: grid; + grid-template-columns: + minmax(0, 1fr) 2.25rem minmax(0, 1fr) 2.75rem minmax(0, 1fr) + 2.25rem minmax(0, 1fr) 2.25rem minmax(0, 1fr); + grid-template-rows: auto minmax(0, 1fr) 2.5rem minmax(0, 1fr) auto; +} + +.laneLabel { + padding: 0.35rem 0; + color: var(--pe-teal-dark); + font-size: 0.85rem; + font-weight: 700; + letter-spacing: 0.12em; + text-transform: uppercase; +} + +.flowStep { + display: flex; + flex-direction: column; + gap: 0.45rem; + border: 2px solid var(--pe-teal-dark); + border-radius: 0.6rem; + background: white; + padding: 0.9rem 1rem; +} + +.flowStepCopy { + background: var(--pe-light); +} + +.flowStepModel { + border-color: var(--pe-amber); + background: color-mix(in srgb, var(--pe-amber) 10%, white); +} + +.flowStepHead { + display: flex; + align-items: flex-start; + gap: 0.55rem; +} + +.flowNumber { + display: inline-flex; + width: 1.75rem; + height: 1.75rem; + flex-shrink: 0; + align-items: center; + justify-content: center; + border-radius: 9999px; + background: var(--pe-teal-dark); + color: white; + font-size: 0.85rem; + font-weight: 700; +} + +.flowStepModel .flowNumber { + background: var(--pe-amber); +} + +.flowStep h2 { + color: var(--pe-dark); + font-size: 1.25rem; + font-weight: 800; + line-height: 1.2; +} + +.flowStep p { + flex: 1; + color: var(--color-gray-600); + font-size: 1.05rem; + line-height: 1.35; +} + +.flowSource { + align-self: flex-start; + border: 1px solid var(--color-gray-300); + border-radius: 9999px; + background: var(--bg-secondary); + padding: 0.15rem 0.65rem; + color: var(--color-gray-700); + font-size: 0.85rem; + font-weight: 600; + white-space: nowrap; +} + +/* Connectors: a line with an arrowhead, across or down. */ +.flowArrow, +.flowDown, +.mergeTop, +.mergeMid, +.mergeBottom { + position: relative; +} + +.flowArrow { + align-self: center; + height: 1rem; +} + +.flowArrow::before, +.mergeMid::after { + content: ""; + position: absolute; + top: 50%; + right: 8px; + left: 4px; + height: 2px; + background: var(--color-gray-400); + transform: translateY(-50%); +} + +.flowArrow::after { + content: ""; + position: absolute; + top: 50%; + right: 0; + border-top: 6px solid transparent; + border-bottom: 6px solid transparent; + border-left: 8px solid var(--color-gray-400); + transform: translateY(-50%); +} + +.flowDown::before { + content: ""; + position: absolute; + top: 4px; + bottom: 8px; + left: 50%; + width: 2px; + background: var(--color-gray-400); + transform: translateX(-50%); +} + +.flowDown::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%); +} + +/* The merge: each lane's line turns towards the middle, which points on. */ +.mergeTop::before, +.mergeBottom::before { + content: ""; + position: absolute; + top: 50%; + left: 4px; + width: calc(50% - 3px); + height: 2px; + background: var(--color-gray-400); +} + +.mergeTop::after, +.mergeBottom::after, +.mergeMid::before { + content: ""; + position: absolute; + left: 50%; + width: 2px; + background: var(--color-gray-400); +} + +.mergeTop::after { + top: 50%; + bottom: 0; +} + +.mergeBottom::after { + top: 0; + height: calc(50% + 2px); +} + +.mergeMid::before { + top: 0; + bottom: 0; +} + +.mergeMid::after { + left: 50%; + right: 8px; +} + +.mergeMid i { + position: absolute; + top: 50%; + right: 0; + border-top: 6px solid transparent; + border-bottom: 6px solid transparent; + border-left: 8px solid var(--color-gray-400); + transform: translateY(-50%); +} + +.status { + margin-top: 1.25rem; + color: var(--color-gray-500); + font-size: 1rem; +} + +/* 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; +} + +.regionScotland { + background: var(--pe-light); +} + +.regionScotland .regionLabel { + color: var(--pe-dark); + font-weight: 700; +} + +.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) { + .previewGrid { + gap: 2rem; + } + + .previewImage { + height: 32vh; + } + + .previewBody { + padding: 0.75rem 1.1rem 0.9rem; + } + + .previewBody h2 { + font-size: 1.3rem; + } + + .previewBody p { + font-size: 1rem; + } + + .previewBody span { + margin-top: 0.5rem; + font-size: 1rem; + } + + .agendaRow { + padding: 0.7rem 0; + } + + .agendaRow > span { + font-size: 1.8rem; + } + + .agendaRow h2 { + font-size: 1.4rem; + } + + .agendaRow p { + font-size: 1.1rem; + } + + .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; + } + + .targetRow { + padding: 0.25rem 1.25rem; + font-size: 0.92rem; + } + + .targetHead { + font-size: 0.72rem; + } + + .tableNote { + margin-top: 0.6rem; + font-size: 0.9rem; + } + + + .waterfall { + padding-top: 1.25rem; + } + + .wfPlot { + height: 12rem; + } + + .wfGrid span { + font-size: 0.75rem; + } + + .wfCaptions { + margin-top: 0.5rem; + padding-top: 0.5rem; + } + + .wfCaptions h2 { + font-size: 0.95rem; + } + + .wfCaptions p { + font-size: 0.82rem; + } + + .wfInside, + .wfValue { + font-size: 0.95rem; + } + + .waterfallTakeaway { + margin-top: 0.9rem; + font-size: 1.05rem; + } + + .energyStats { + font-size: 1.15rem; + } + + .flow { + grid-template-columns: + minmax(0, 1fr) 1.75rem minmax(0, 1fr) 2.25rem minmax(0, 1fr) + 1.75rem minmax(0, 1fr) 1.75rem minmax(0, 1fr); + grid-template-rows: auto minmax(0, 1fr) 2rem minmax(0, 1fr) auto; + } + + .laneLabel { + font-size: 0.72rem; + } + + .flowStep { + gap: 0.35rem; + padding: 0.65rem 0.7rem; + } + + .flowNumber { + width: 1.45rem; + height: 1.45rem; + font-size: 0.75rem; + } + + .flowStep h2 { + font-size: 1rem; + } + + .flowStep p { + font-size: 0.86rem; + } + + .flowSource { + font-size: 0.72rem; + } + + .status { + margin-top: 0.75rem; + font-size: 0.9rem; + } + + .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) { + .previewImage { + height: 30vh; + } + + .previewBody h2 { + font-size: 1.1rem; + } + + .previewBody p, + .previewBody span { + font-size: 0.88rem; + } + + .agendaRow { + padding: 0.5rem 0; + } + + .agendaRow h2 { + font-size: 1.2rem; + } + + .agendaRow p { + font-size: 0.95rem; + } + + .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; + } + + .targetRow { + padding: 0.18rem 1rem; + font-size: 0.85rem; + } + + .targetHead { + font-size: 0.68rem; + } + + .tableNote, + .finePrint, + .status { + margin-top: 0.6rem; + font-size: 0.85rem; + } + + .wfPlot { + height: 10rem; + } + + .wfCaptions h2 { + font-size: 0.85rem; + } + + .wfCaptions p { + font-size: 0.75rem; + } + + .wfInside, + .wfValue { + font-size: 0.85rem; + } + + .waterfallTakeaway { + margin-top: 0.6rem; + font-size: 0.9rem; + } + + .energyStats { + font-size: 1rem; + } + + .flow { + grid-template-rows: auto minmax(0, 1fr) 1.5rem minmax(0, 1fr) auto; + } + + .flowStep { + padding: 0.5rem 0.6rem; + } + + .flowStep h2 { + font-size: 0.92rem; + } + + .flowStep p { + font-size: 0.8rem; + } + + .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; + } + + .cover { + max-height: 22vh; + } +} + +/* 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; + } + + .cover { + max-height: 17vh; + } +} diff --git a/slideshows/scottish-government-2026/slides/figures.tsx b/slideshows/scottish-government-2026/slides/figures.tsx new file mode 100644 index 0000000..6257af7 --- /dev/null +++ b/slideshows/scottish-government-2026/slides/figures.tsx @@ -0,0 +1,314 @@ +import { Fragment, type CSSProperties } from "react"; +import { + energyRegions, + pipelineStages, + propertyLanes, + propertyMergedSteps, + propertyWaterfall, + type PropertyStep, + scotlandEnergyDeciles, + scotlandTargets, +} from "../content"; +import styles from "./deck.module.css"; + +// The pipeline layout follows l0-ima-2026's PipelineDiagram, 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 count = (n: number) => (n === 0 ? "–" : n.toLocaleString("en-GB")); + +export function TargetTable() { + const totals = scotlandTargets.reduce( + (sum, row) => ({ + scotland: sum.scotland + row.scotland, + councils: sum.councils + row.councils, + constituencies: sum.constituencies + row.constituencies, + }), + { scotland: 0, councils: 0, constituencies: 0 }, + ); + const all = totals.scotland + totals.councils + totals.constituencies; + return ( +
+
+
Target family
+
Source
+
Scotland
+
Councils
+
Constituencies
+
Rows
+
+ {scotlandTargets.map((row) => ( +
+
{row.family}
+
{row.publisher}
+ {[row.scotland, row.councils, row.constituencies].map((n, i) => ( +
+ {count(n)} +
+ ))} +
+ {count(row.scotland + row.councils + row.constituencies)} +
+
+ ))} +
+
Total
+
+
{count(totals.scotland)}
+
{count(totals.councils)}
+
{count(totals.constituencies)}
+
{count(all)}
+
+
+ ); +} + +// A waterfall from rent received down to what the FRS asks for. Each total is +// what a source counts; each deduction column says what the next total leaves +// out, and a deduction can follow another. Heights share one £0–50bn axis. +const WATERFALL_MAX = 50; + +// Each column's span on the axis, and the level its connector starts from: +// a deduction hangs from the level the previous column ended at. +const waterfallColumns = propertyWaterfall.reduce< + { step: (typeof propertyWaterfall)[number]; bottom: number; top: number; connector: number | null }[] +>((columns, step, index) => { + const last = columns[index - 1]; + const previous = last ? (last.step.kind === "deduction" ? last.bottom : last.top) : 0; + const deduction = step.kind === "deduction"; + return [ + ...columns, + { + step, + bottom: deduction ? previous - step.value : 0, + top: deduction ? previous : step.value, + connector: last ? previous : null, + }, + ]; +}, []); + +export function PropertyWaterfall() { + const pct = (value: number) => `${(value / WATERFALL_MAX) * 100}%`; + return ( +
+
+ {[0, 10, 20, 30, 40, 50].map((tick) => ( +
+ £{tick}bn +
+ ))} +
+ {waterfallColumns.map(({ step, bottom, top, connector }) => ( +
+ {connector !== null && ( +
+ ))} +
+
+
+ {propertyWaterfall.map((step) => ( +
+

+ {step.kind === "deduction" ? "− " : ""} + {step.label} +

+

{step.note}

+ {step.source && {step.source}} +
+ ))} +
+
+ ); +} + +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; + const scotland = r.region === "Scotland"; + return ( +
+ {r.region} + + + + {share(r.eligible)} + +
+ ); + })} +
+
+ ); +} + +export function ScotlandDecileChart() { + const max = Math.max(...scotlandEnergyDeciles.map((d) => d.averageGain)); + return ( +
+
+

Scotland: average gain per household

+

Includes households that get nothing. Each tenth rests on 38 to 102 survey households.

+
+
+ {scotlandEnergyDeciles.map((d) => ( +
+ £{d.averageGain} + +
+ ))} +
+
+ {scotlandEnergyDeciles.map((d) => ( + {d.decile} + ))} +
+

+ Income decile within Scotland, after housing costs (1 = lowest) +

+
+ ); +} + +function StepBox({ + step, + number, + className = "", + style, +}: { + step: PropertyStep; + number: number; + className?: string; + style?: CSSProperties; +}) { + return ( +
+
+ {number} +

{step.title}

+
+

{step.text}

+ {step.source} +
+ ); +} + +// Two lanes (survey landlords above, tax-record copies below) merge into the +// steps every landlord goes through. Grid rows: lane label, survey lane, gap, +// copies lane, lane label; the two lanes share one height so the merge +// connector meets the merged steps at their centre. +export function PropertyFlow() { + const [frs, addBack] = propertyLanes.survey.steps; + const [copy, draw] = propertyLanes.copies.steps; + const merged = (column: number) => ({ gridColumn: column, gridRow: "2 / 5" }); + return ( +
+

+ {propertyLanes.survey.label} +

+ +
+ ); +} diff --git a/slideshows/scottish-government-2026/slides/index.tsx b/slideshows/scottish-government-2026/slides/index.tsx new file mode 100644 index 0000000..52cd285 --- /dev/null +++ b/slideshows/scottish-government-2026/slides/index.tsx @@ -0,0 +1,539 @@ +import type { ReactNode } from "react"; +import { + IconBrandGithub, + IconBrandLinkedin, + 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 { + budgetPlan, + energyPublications, + nicsLinks, + scotlandEnergyHeadline, + sources, + workPublications, + type Publication, + type Source, +} from "../content"; +import styles from "./deck.module.css"; +import { + PipelineGraph, + PropertyWaterfall, + PropertyFlow, + RegionEligibilityChart, + ScotlandDecileChart, + TargetTable, +} from "./figures"; + +const sections = { + data: "1 · The UK's microdata", + property: "2 · Property income", + analysis: "3 · Analysis example", + research: "4 · Published work, April–October 2026", + budget: "5 · Plan for the Autumn Budget", +}; + +function ExternalLink({ + children, + href, + className = "", +}: { + children: ReactNode; + href: string; + className?: string; +}) { + return ( + + {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 && } +
+ ); +} + +function PublicationCard({ item }: { item: Publication }) { + return ( + + {item.alt} +
+

{item.date}

+

+ {item.title} +

+

+ {item.finding} +

+
+
+ ); +} + +export function TitleSlide() { + return ( + + ); +} + +function SectionSlide({ + part, + title, + subtitle, +}: { + part: number; + title: string; + subtitle: string; +}) { + return ( + +
+

+ Part {part} of {agenda.length} +

+

+ {title} +

+
+

{subtitle}

+
+ + ); +} + +const agenda = [ + { + title: "How we build the UK's microdata", + text: "The sources, the pipeline and the Scottish targets", + }, + { + title: "Modelling property income", + text: "Which data informs each piece, and how it enters the dataset", + }, + { + title: "An analysis example", + text: "A targeted energy bill discount, by region and across Scotland", + }, + { + title: "Published work since April", + text: "UK analysis and tools, with links", + }, + { + title: "Plan for the Autumn Budget", + text: "Building on the 2025 dashboard, and how Budget day will run", + }, +]; + +export function AgendaSlide() { + return ( + +
+ {agenda.map((item, index) => ( +
+ {String(index + 1).padStart(2, "0")} +
+

{item.title}

+

{item.text}

+
+
+ ))} +
+ + ); +} + +export function DataSectionSlide() { + return ( + + ); +} + +export function PipelineSlide() { + return ( + + + + ); +} + +export function TargetsSlide() { + return ( + + +

+ The national release calibrates to the 58 Scotland-wide rows. The 1,857 + council and constituency rows are for the local-area build, which is still + being validated. +

+ + ); +} + +export function ReleaseChecksSlide() { + return ( + +
+

Review Scotland's population, income and benefit targets.

+ Open the live dashboard ↗ +
+