Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
b5eb60a
Update IMA deck to the corrected model (PR #21)
MaxGhenis Jul 1, 2026
86f3146
Update deck to the OBR near-threshold-target build
MaxGhenis Jul 2, 2026
d3773c7
Deck: definitive-build numbers (below-threshold liability exclusion)
MaxGhenis Jul 2, 2026
3d23eaa
Turnover-dist caption: state the registered-business frame
MaxGhenis Jul 2, 2026
5680630
Deck: shrink-to-fit safety net for short windows; clicker PageUp/Page…
vahid-ahmadi Jul 2, 2026
ded79c4
Deck: drop the £90k spurious-bunching exhibit and related text
vahid-ahmadi Jul 2, 2026
1828ab7
Deck: move profit equation to right column; drop formulation-A label …
vahid-ahmadi Jul 2, 2026
5e04a09
Deck footer: capitalize Firm microsimulation
vahid-ahmadi Jul 2, 2026
4f256dd
Deck: compact reform-menu table so it fits shorter windows
vahid-ahmadi Jul 2, 2026
f472484
Deck: bottom legend on formulation-A figure; page numbers in PDF export
vahid-ahmadi Jul 2, 2026
f1e65b4
Deck: drop reduced-rate second-order offsets bullet from behavioural …
vahid-ahmadi Jul 2, 2026
2652813
Deck: drop power-test bullet from bunching slide
vahid-ahmadi Jul 2, 2026
30f5a37
Deck: restore profit equation beneath the dominated-region figure
vahid-ahmadi Jul 2, 2026
f4163d2
Deck: restore excess-mass estimator equation on the bunching slide
vahid-ahmadi Jul 2, 2026
82f1600
Deck: side-consistent scaling numbers
MaxGhenis Jul 2, 2026
5df10a2
Deck: shorten reform-menu caption
vahid-ahmadi Jul 2, 2026
a7fb8ae
Deck: shorten turnover-distribution caption
vahid-ahmadi Jul 2, 2026
4543838
Deck: move firm-problem equation below the figure; enlarge left-colum…
vahid-ahmadi Jul 2, 2026
8e3a3c2
Deck: drop partial-efficiency-metric and transparency bullets from co…
vahid-ahmadi Jul 2, 2026
4333349
Deck: bump body text one size up; drop freeze-history clause
vahid-ahmadi Jul 2, 2026
852bf4d
Deck: drop level-rise zero-offset bullet from behavioural slide
vahid-ahmadi Jul 2, 2026
bb728a2
Deck: drop overshoot clause from conclusion static bullet
vahid-ahmadi Jul 2, 2026
d74a4a8
Deck: larger section subtitles and kickers
vahid-ahmadi Jul 2, 2026
ad61e42
Deck: restore per-year baseline/policy threshold labels on the anchor…
vahid-ahmadi Jul 2, 2026
1cdb6c5
Deck: 'checked against' rather than 'validated' for the HMRC anchor
vahid-ahmadi Jul 2, 2026
4d3df4c
Deck: show page counter in fullscreen mode
vahid-ahmadi Jul 2, 2026
f02c389
Deck: hide Next.js dev-tools indicator
vahid-ahmadi Jul 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,197 changes: 1,197 additions & 0 deletions paper/slides/presentation/bun.lock

Large diffs are not rendered by default.

59 changes: 57 additions & 2 deletions paper/slides/presentation/components/core/Slide.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,61 @@
"use client";

import { ReactNode } from "react";
import { ReactNode, useLayoutEffect, useRef } from "react";
import Image from "@/components/core/BasePathImage";
import { useSlideshowContextSafe } from "@/components/core/SlideshowContext";

// Shrink-to-fit safety net: if the slide content is taller than the available
// content box (e.g. a short browser window), scale the content down just enough
// to fit — white background still fills the window and the footer stays put.
// At normal 16:9 window sizes the content fits and no transform is applied, so
// the DOM and visuals are identical to an unscaled slide.
function useFitToContentBox() {
const contentRef = useRef<HTMLDivElement>(null);

useLayoutEffect(() => {
const element = contentRef.current;
if (!element) return;

const update = () => {
// Measure at natural scale (transforms do not affect layout, but they do
// affect client rects, so clear ours before measuring). This runs before
// paint, so the reset is never visible.
element.style.transform = "";
const box = element.getBoundingClientRect();
if (box.height === 0) return;

// Content extent relative to the content box, including anything that
// spills above it (centered layouts overflow both ways).
let minTop = 0;
let maxBottom = box.height;
for (const descendant of element.querySelectorAll("*")) {
const rect = descendant.getBoundingClientRect();
if (rect.height === 0 && rect.width === 0) continue;
minTop = Math.min(minTop, rect.top - box.top);
maxBottom = Math.max(maxBottom, rect.bottom - box.top);
}

const needed = maxBottom - minTop;
if (needed > box.height + 1) {
const scale = box.height / needed;
// Anchor the (scaled) content extent to the top of the content box and
// re-centre it horizontally after the width shrinks.
const translateX = ((1 - scale) * box.width) / 2;
element.style.transformOrigin = "top left";
element.style.transform = `translate(${translateX}px, ${-scale * minTop}px) scale(${scale})`;
}
};

update();
// Re-fit when the window changes and once webfonts have swapped in.
window.addEventListener("resize", update);
document.fonts?.ready.then(update).catch(() => undefined);
return () => window.removeEventListener("resize", update);
}, []);

return contentRef;
}

interface SlideProps {
children: ReactNode;
className?: string;
Expand All @@ -23,6 +75,7 @@ export default function Slide({
}: SlideProps) {
const context = useSlideshowContextSafe();
const footerText = context?.footerText ?? "";
const contentRef = useFitToContentBox();

return (
<section
Expand All @@ -41,7 +94,9 @@ export default function Slide({
isCover || isEnd ? "flex items-center justify-center px-20" : "px-16 pb-28 pt-20",
].join(" ")}
>
<div className="h-full w-full">{children}</div>
<div className="h-full w-full" ref={contentRef}>
{children}
</div>
</div>
)}

Expand Down
17 changes: 17 additions & 0 deletions paper/slides/presentation/components/core/SlideshowViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ function SlideshowViewerClient({ config }: SlideshowViewerProps) {
} else if (event.key === "ArrowLeft") {
event.preventDefault();
setCurrentSlide((previous) => Math.max(previous - 1, 0));
} else if (event.key === "PageDown") {
// Conference presenter clickers emit PageDown/PageUp.
event.preventDefault();
setCurrentSlide((previous) => Math.min(previous + 1, slides.length - 1));
} else if (event.key === "PageUp") {
event.preventDefault();
setCurrentSlide((previous) => Math.max(previous - 1, 0));
} else if (event.key === "Home") {
event.preventDefault();
setCurrentSlide(0);
Expand Down Expand Up @@ -85,6 +92,16 @@ function SlideshowViewerClient({ config }: SlideshowViewerProps) {
>
<div className="slide-active">{currentSlideElement}</div>

{(isExport || isFullscreen) && (
<div className="pointer-events-none fixed bottom-0 right-0 z-50 flex h-18 items-center px-8 text-white">
<span className="text-sm font-semibold">
{currentSlide < (config.mainSlideCount ?? slides.length)
? `${currentSlide + 1} / ${config.mainSlideCount ?? slides.length}`
: "Appendix"}
</span>
</div>
)}

{!isFullscreen && !isExport && (
<div className="pointer-events-none fixed bottom-0 left-0 right-0 z-50 flex h-18 items-center justify-end gap-4 px-8 text-white">
<button
Expand Down
4 changes: 2 additions & 2 deletions paper/slides/presentation/components/layout/SectionSlide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ export default function SectionSlide({ section, title, subtitle }: SectionSlideP
<Slide className="bg-pe-light">
<div className="flex h-full items-center">
<div>
<div className="mb-5 text-sm font-bold uppercase tracking-[0.24em] text-pe-teal">
<div className="mb-5 text-lg font-bold uppercase tracking-[0.24em] text-pe-teal">
{section}
</div>
<h1 className="max-w-5xl text-6xl font-extrabold leading-tight tracking-tight text-pe-dark">
{title}
</h1>
<p className="mt-8 max-w-4xl text-2xl leading-snug text-slate-600">
<p className="mt-8 max-w-5xl text-3xl leading-snug text-slate-600">
{subtitle}
</p>
</div>
Expand Down
2 changes: 1 addition & 1 deletion paper/slides/presentation/components/layout/SlideTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default function SlideTitle({ children, kicker, className = "" }: SlideTi
return (
<div className={className}>
{kicker && (
<div className="mb-3 text-sm font-bold uppercase tracking-[0.22em] text-pe-teal">
<div className="mb-3 text-base font-bold uppercase tracking-[0.22em] text-pe-teal">
{kicker}
</div>
)}
Expand Down
1 change: 1 addition & 0 deletions paper/slides/presentation/next.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
devIndicators: false,
images: {
unoptimized: true,
},
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions paper/slides/presentation/slides/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ import {

export const vatIma2026Config: SlideshowConfig = {
id: "vat-ima-2026",
title: "A Firm-Level Microsimulation for VAT Policy Analysis",
title: "An Open Firm-Level Microsimulation of the UK VAT Registration Threshold",
description:
"PolicyEngine conference deck for the IMA World Congress 2026 — costing UK VAT registration-threshold reforms on synthetic firm data.",
date: "2026-07-01",
location: "IMA World Congress 2026, Brussels",
footerText: "IMA World Congress 2026 · firm microsimulation",
footerText: "IMA World Congress 2026 · Firm microsimulation",
speakers: [{ name: "Vahid Ahmadi", title: "PolicyEngine" }],
// Slides 0–24 are the main deck; 25+ are appendix (excluded from the counter).
mainSlideCount: 25,
Expand Down
Loading
Loading