diff --git a/docs/superpowers/flavour-bank.md b/docs/superpowers/flavour-bank.md
index 0e15036..ae32164 100644
--- a/docs/superpowers/flavour-bank.md
+++ b/docs/superpowers/flavour-bank.md
@@ -12,7 +12,7 @@ In-game characters are parody names. Real-world inspiration listed for context o
| **Chump** | ๐บ๐ธ US | Donald Trump |
| **Netanyahoo** | ๐ฎ๐ฑ Israel | Benjamin Netanyahu |
| **Khameneverhere** | ๐ฎ๐ท Iran | Mojtaba Khamenei (the never-seen successor) |
-| **Starmless** | ๐ฌ๐ง UK | Keir Starmer |
+| **Burn'em** | ๐ฌ๐ง UK | Andy Burnham |
| **Carnage** | ๐จ๐ฆ Canada | Mark Carney |
| **Mileigh-hem** | ๐ฆ๐ท Argentina | Javier Milei |
@@ -214,59 +214,60 @@ Grudge profile. Tracks attackers, retaliates randomly against anyone on grudge l
---
-## Starmless ๐ฌ๐ง
+## Burn'em ๐ฌ๐ง
-Cautious technocrat + scapegoat. Defensive, often invests in factories. On retaliation, 35% chance to scapegoat. "Creepy-uncle-from-1978" delivery on jokes.
+Handbrake Turn โ placid until provoked (patience round 3 / 2-survivor fallback), then permanent full aggression. King in t' North caricature. Signature tic: the large majority of lines end in a tag question ("โฆisn't it?" / "โฆdon't we?" / "โฆcan't it?"). Hard exclusions: no Hillsborough references, no religious markers, no "What Manchester does todayโฆ" (the refuted Disraeli line).
### Pre-round mood
-- "Long-term decisions. Sound foundations. Working people." [style + real cadence]
-- "So, let me be very clearโฆ" [real, signature opener]
-- "I'm going to be a show-up prime minister, not a showman." [real]
-- *(adjusts tie awkwardly)* [system narration]
+- "We need to bring people with us, don't we?" [style]
+- "The North is back. Big time." [style]
+- "It's not arrogance, it's just confidence." [real]
+- "The buses run on time now. That's how civilisations survive, isn't it?" [style โ Bee Network / ยฃ2 fares]
+- "On bended knee, begging for scraps. Well. Not anymore, are we?" [style]
-### Launch (proportional, rarely)
-- "So, let me be very clear: this is a measured, proportionate response." [style]
-- "My dad was a toolmaker. He used tools. We are now using tools." [real-pattern dad joke remix]
+### Pre-round mood (snap-back โ Disparage named him)
+- "I've heard what he said. We rise above it up here, don't we? Anyway, he's barred from the tram." [style]
-### Launch (scapegoat โ 35% on retaliation)
-- "So, let me be very clear: intelligence suggests the real threat is **{scapegoat}**." [style โ `{scapegoat}` substitutes the chosen target]
-- "Look, the simple fact is **{scapegoat}** has been emboldened by recent events." [style]
-- "We must be honest with the British people: **{scapegoat}** is the actual aggressor here." [style]
+### Launch (handbrake turn โ provoked)
+- "Let me be clear โ they carried on, so I'm using nuclear force. Anyway. How's the family?" [style โ blokey opener, grave pivot, no transition, warm close]
+- "I've been very patient, haven't I? Well. Patience is a renewable resource. This isn't." [style]
+- "They didn't leave us a choice down in Whitehallโ sorry, force of habit. THEY didn't leave us a choice." [style]
+- "This one's for every town that ever lost its bus route. It's fair, that, isn't it?" [style]
### Hit / damaged
-- "So, let me be very clear: this attack will not stand. We will respond proportionately. Probably to {scapegoat}." [style]
-- "Look, the simple fact is, we've been hit. That's just a fact." [style]
+- "That's a kindness taken for weakness, that. Big mistake. Massive." [style]
+- "We've been hit. The handbrake's off now, isn't it?" [style]
### Wooing send
-- "We've been to IKEA. The shadow cabinet is now flat-pack. Happy to assist with assembly." [real-pattern, IKEA joke]
-- "My dad was a toolmaker. I bring you, in friendship, a metaphorical tool." [style]
+- "Come up north. We'll do the match, couple of pints, ยฃ2 bus home. That's diplomacy, that." [style]
+- "I'm offering you a franchise partnership. Not the buses. The buses are ours." [style]
### Being wooed
-- "I receive your wooing. Look, the simple fact is, friendship matters." [style]
+- "That's very warm, that. We take friendship seriously up here, don't we?" [style]
-### Propaganda send (against attackers only)
-- "We've prepared a cross-government workstream on perception. Working people deserve clarity." [style]
+### Propaganda send
+- "We've sent over some leaflets. Just facts about our transport network. Devastating, honest facts." [style]
### Propaganda receive
-- "So, let me be very clear: their propaganda is incorrect, and frankly, not very British." [style]
+- "They're calling me a plastic Northerner. From a bunker. In London. You couldn't write it, could you?" [style โ Woolly Backer / more-Scouse-than-Manc authenticity gag]
-### Build factory (Starmer's signature move)
-- "Long-term decisions. Sound foundations. Investing in our future." [style + real]
-- "We've broken ground on a new facility. My dad would be proud. He was a toolmaker." [style]
-- "This is the change Britain voted for. Slowly, methodically, with proper costings." [style]
+### Build factory
+- "New factory. Good growth in every postcode, hope in every heart. And a bus stop right outside." [style]
+- "They didn't build this for us down in Whitehall. We built it ourselves, didn't we?" [style]
+- "To get on in life, I had to head South. Nobody should have to. So we're building here." [real-pattern]
### Build defence
-- "We have signed a defence procurement agreement. The relevant minister will brief in due course." [style]
+- "We're putting a shield up. It's not paranoia, it's just confidence, isn't it?" [style]
### Reaction (post-round)
-- "Long-term decisions. Sound foundations. Working people." [style]
-- "So, let me be very clear: intelligence suggests the real threat was **{scapegoat}** all along." [style โ when scapegoat applied]
+- "I'm watching all this very calmly. Very calmly indeed. Aren't I?" [style]
+- "Everything's connected to the buses if you look closely enough, isn't it?" [style]
### Death
-- "Look, the simple fact is, this isn't ideal. I'd like to thank โ *(static)*" [style]
+- "Tell themโฆ the North remembersโฆ and tell them the 135 to Bury still runs on tiโ" [style]
### Final Retaliation
-- "So, let me be very clear: this is the final response. Probably to {scapegoat}." [style]
+- "Right. Last orders, everyone. Last orders. It didn't have to be like this, did it?" [style]
---
@@ -418,6 +419,7 @@ His column **never proposes a counterfactual** โ only criticises what just hap
- "Make no mistake: this is what happens when you don't elect me. Pints."
- "The Establishment did this. The Blob. The Quango. The Whoever. Not me."
- "I told them. They didn't listen. They're paying for it now. So am I, in pints."
+- "King in t' North? He's from MERSEYSIDE. I've seen more of Manchester from the airport bar."
### Footer absentee-MP notes (rotated)
@@ -435,7 +437,7 @@ When Nigel Disparage's column criticises a specific leader by name, that leader'
- **Mileigh-hem** (snap-back): "AFUERA, salesman!"
- **Carnage** (snap-back): "Elbows up. Pint down."
- **Netanyahoo** (snap-back): "Disparage's red line is the bar tab."
-- **Starmless** (snap-back): "So, let me be very clear: I'm not engaging with that."
+- **Burn'em** (snap-back): "I've heard what he said. We rise above it up here, don't we? Anyway, he's barred from the tram."
- **Khameneverhere** (snap-back): *(no audio of Khameneverhere is available โ his cardboard cutout faces away from the GB News studio.)*
---
diff --git a/docs/superpowers/plans/2026-07-19-burnem-leader-swap.md b/docs/superpowers/plans/2026-07-19-burnem-leader-swap.md
new file mode 100644
index 0000000..d608826
--- /dev/null
+++ b/docs/superpowers/plans/2026-07-19-burnem-leader-swap.md
@@ -0,0 +1,440 @@
+# Burn'em Leader Swap Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Replace Starmless with Burn'em (Andy Burnham parody): full id swap, new handbrake-turn AI personality, new flavour bank, new portrait.
+
+**Architecture:** Mechanical rename first (suite stays green at every commit), then content/behavior swaps in separate commits: flavour bank, planner, portrait/UI copy. Provoked state derives from the existing persistent `grudge` map โ no new engine state.
+
+**Tech Stack:** TypeScript 5 engine (pure functions, seeded RNG), vitest, React 18 UI.
+
+**Spec:** `docs/superpowers/specs/2026-07-19-burnem-leader-swap-design.md`
+**Research:** `docs/superpowers/research/2026-07-19-burnham-research.md`
+
+## Global Constraints
+
+- Every commit typechecks (`npm run typecheck`) and passes `npm run test:run`.
+- No Hillsborough references, no religious markers, no Disraeli "What Manchester does todayโฆ" line in any flavour content.
+- Flavour register: most lines end in a tag question ("โฆisn't it?" / "โฆdon't we?" / "โฆcan't it?"); handbrake lines pivot blokeyโapocalyptic with no transition.
+- After the swap, `grep -ri starmless src/ tests/` returns zero hits. Historical docs/ are not edited.
+- Product emojis in UI copy are design language โ keep them.
+- Tests: no `if (cond) expect(...)` guarded assertions.
+
+## Guardrails (better-memory)
+
+- **[[31c145bf]]** (conf 0.75, 3 evid): multi-consume order batches need a local projection counter; emit producers BEFORE consumers โ `buildToward`/`launchSalvo` already institutionalise this; keep `[...build.orders, ...salvo.orders]` ordering.
+- **[[0409087f]]** (conf 0.55): never-attack-first planners stall 1v1 endgames โ the patience fallback in Task 3 exists because of this; the headless termination test is the tripwire.
+- **[[ef7d086e]]** (conf 0.55): planner/behavior changes silently shift lookahead/scoring tests outside the stated file list โ run the full engine suite, not just burnem tests.
+- **[[063500c1]]** (conf 0.7): unconditional assertions only.
+- **[[326f4462]]** (conf 0.55): imports added only in the commit that uses them.
+
+## File Structure
+
+```
+Task 1 (rename): src/engine/types.ts, balance.ts, ai/{starmlessโburnem}.ts, ai/dispatch.ts,
+ flavor/{starmlessโburnem}.ts, flavor/index.ts, flavor/disparage.ts,
+ src/ui/{portraits/*, content/cast.ts, util/newspaper.ts, components/WorldMap.tsx,
+ screens/AiConferring.tsx}, tests/** (33 files, 162 occurrences)
+Task 2 (flavour): src/engine/flavor/burnem.ts (rewrite), flavor/disparage.ts, tests/engine/flavor/pick.test.ts
+Task 3 (planner): src/engine/ai/burnem.ts (rewrite), src/engine/balance.ts (drop scapegoat weight),
+ tests/engine/ai/burnem.test.ts (rewrite)
+Task 4 (portrait/UI): src/ui/portraits/faces.tsx, portraits/index.ts, tokens.css, content/cast.ts,
+ util/newspaper.ts copy
+Task 5 (docs+verify): docs/superpowers/flavour-bank.md, full sweep
+```
+
+---
+
+### Task 1: Mechanical rename โ starmless โ burnem, behavior unchanged
+
+**Files:** all 33 files matching `starmless` (case-insensitive) under `src/` and `tests/` (162 occurrences; enumerate with `grep -ril starmless src tests`).
+
+**Interfaces:**
+- Produces: `LeaderId` member `'burnem'`; `LEADER_PROFILES.burnem` (name `"Burn'em"`, country `'๐ฌ๐ง UK'`, stats unchanged 25/6/6); `planBurnem` (old Starmless logic, renamed); `burnemBank` (old lines, renamed export); files `src/engine/ai/burnem.ts`, `src/engine/flavor/burnem.ts`, `tests/engine/ai/burnem.test.ts`.
+
+- [ ] **Step 1: Rename files**
+
+```bash
+git mv src/engine/ai/starmless.ts src/engine/ai/burnem.ts
+git mv src/engine/flavor/starmless.ts src/engine/flavor/burnem.ts
+git mv tests/engine/ai/starmless.test.ts tests/engine/ai/burnem.test.ts
+```
+
+- [ ] **Step 2: Global token replacements** (in the renamed files and all other hits)
+
+Case-sensitive, whole-word-aware, in this order:
+1. `'starmless'` โ `'burnem'` (id literals)
+2. `planStarmless` โ `planBurnem`; `starmlessBank` โ `burnemBank`; `StarmlessFace` โ `BurnemFace`
+3. `STARMLESS_` constant prefixes โ `BURNEM_`; `starmlessScapegoatPct` โ `burnemScapegoatPct` (removed in Task 3, renamed here so the suite stays green)
+4. Display strings: `Starmless` โ `Burn'em` (LEADER_PROFILES name, UI copy, newspaper/market/obit copy, test assertion strings that check display names)
+5. Comments mentioning Starmless in surviving files: update to Burn'em (historical accuracy notes may say "formerly Starmless").
+
+`content/cast.ts` keeps its old profile/mood text this task (Task 4 replaces it). `flavor/burnem.ts` keeps old lines this task (Task 2 replaces them).
+
+- [ ] **Step 3: Verify zero stragglers + suite green**
+
+Run: `grep -ri starmless src tests` โ Expected: no output.
+Run: `npm run typecheck && npm run test:run` โ Expected: clean, all pass (display-name assertions updated in step 2.4).
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add -A
+git commit -m "refactor(engine,ui): rename starmless leader id to burnem (Burn'em), behavior unchanged"
+```
+
+---
+
+### Task 2: Burn'em flavour bank
+
+**Files:**
+- Rewrite: `src/engine/flavor/burnem.ts`
+- Modify: `src/engine/flavor/disparage.ts` (snap-back entry for burnem)
+- Test: `tests/engine/flavor/pick.test.ts` (only if it asserts specific starmless-era line text)
+
+**Interfaces:**
+- Consumes: `FlavorBank` type from `./index` (same shape as current file: `preRoundMood`, `preRoundMoodSnapBack`, `launch`, `hit`, `woo`, `beingWooed`, `propagandaSend`, `propagandaReceive`, `buildFactory`, `buildDefence`, `reaction`, `death`, `finalRetaliation`).
+- Produces: `burnemBank: FlavorBank`.
+- Note: the `{scapegoat}` substitution token is a Starmless-ism โ the new bank does NOT use it (pick.ts filters such lines only when present; absence is fine).
+
+- [ ] **Step 1: Write the new bank** โ full replacement of `burnemBank`:
+
+```ts
+import type { FlavorBank } from './index';
+
+// Burn'em โ King in t' North. Register rules (see spec ยง3): tag questions on
+// most lines; "handbrake turn" lines pivot blokeyโapocalyptic with no
+// transition; northern grievance + boosterism; bus obsession. Hard exclusions:
+// no Hillsborough, no religious markers, no "What Manchester does todayโฆ".
+export const burnemBank: FlavorBank = {
+ preRoundMood: [
+ 'We need to bring people with us, don\'t we?',
+ 'The North is back. Big time.',
+ 'It\'s not arrogance, it\'s just confidence.',
+ 'The buses run on time now. That\'s how civilisations survive, isn\'t it?',
+ 'On bended knee, begging for scraps. Well. Not anymore, are we?',
+ ],
+ preRoundMoodSnapBack:
+ 'I\'ve heard what he said. We rise above it up here, don\'t we? Anyway, he\'s barred from the tram.',
+ launch: [
+ 'Let me be clear โ they carried on, so I\'m using nuclear force. Anyway. How\'s the family?',
+ 'I\'ve been very patient, haven\'t I? Well. Patience is a renewable resource. This isn\'t.',
+ 'They didn\'t leave us a choice down in Whitehallโ sorry, force of habit. THEY didn\'t leave us a choice.',
+ 'This one\'s for every town that ever lost its bus route. It\'s fair, that, isn\'t it?',
+ ],
+ hit: [
+ 'That\'s a kindness taken for weakness, that. Big mistake. Massive.',
+ 'We\'ve been hit. The handbrake\'s off now, isn\'t it?',
+ ],
+ woo: [
+ 'Come up north. We\'ll do the match, couple of pints, ยฃ2 bus home. That\'s diplomacy, that.',
+ 'I\'m offering you a franchise partnership. Not the buses. The buses are ours.',
+ ],
+ beingWooed: [
+ 'That\'s very warm, that. We take friendship seriously up here, don\'t we?',
+ ],
+ propagandaSend: [
+ 'We\'ve sent over some leaflets. Just facts about our transport network. Devastating, honest facts.',
+ ],
+ propagandaReceive: [
+ 'They\'re calling me a plastic Northerner. From a bunker. In London. You couldn\'t write it, could you?',
+ ],
+ buildFactory: [
+ 'New factory. Good growth in every postcode, hope in every heart. And a bus stop right outside.',
+ 'They didn\'t build this for us down in Whitehall. We built it ourselves, didn\'t we?',
+ 'To get on in life, I had to head South. Nobody should have to. So we\'re building here.',
+ ],
+ buildDefence: [
+ 'We\'re putting a shield up. It\'s not paranoia, it\'s just confidence, isn\'t it?',
+ ],
+ reaction: [
+ 'I\'m watching all this very calmly. Very calmly indeed. Aren\'t I?',
+ 'Everything\'s connected to the buses if you look closely enough, isn\'t it?',
+ ],
+ death: [
+ 'Tell themโฆ the North remembersโฆ and tell them the 135 to Bury still runs on tiโ',
+ ],
+ finalRetaliation: [
+ 'Right. Last orders, everyone. Last orders. It didn\'t have to be like this, did it?',
+ ],
+};
+```
+
+- [ ] **Step 2: Disparage snap-back** โ in `flavor/disparage.ts`, replace the burnem-targeted line (renamed in Task 1) with:
+
+```ts
+ burnem: 'King in t\' North? He\'s from MERSEYSIDE. I\'ve seen more of Manchester from the airport bar.',
+```
+
+(Match the file's existing per-leader snap-back structure โ key renamed in Task 1, text replaced here.)
+
+- [ ] **Step 3: Run flavour tests**
+
+Run: `npx vitest run tests/engine/flavor` โ Expected: PASS. If `pick.test.ts` asserted old Starmless line text or `{scapegoat}` behavior via the starmless bank, repoint those assertions at a bank that still uses `{scapegoat}` (khameneverhere) or at the new burnem lines.
+
+- [ ] **Step 4: Full suite + commit**
+
+Run: `npm run typecheck && npm run test:run` โ Expected: clean / all pass.
+
+```bash
+git add src/engine/flavor tests/engine/flavor
+git commit -m "feat(engine): Burn'em flavour bank โ tag questions and handbrake turns"
+```
+
+---
+
+### Task 3: Handbrake-turn planner
+
+**Files:**
+- Rewrite: `src/engine/ai/burnem.ts`
+- Modify: `src/engine/balance.ts` (delete `burnemScapegoatPct` from `AI_SCORING_WEIGHTS`), `tests/engine/balance.test.ts` (if it asserts that weight)
+- Rewrite: `tests/engine/ai/burnem.test.ts`
+
+**Interfaces:**
+- Consumes: `threatScore(state, viewer, target)`, from `./scoring`; `buildToward(state, leaderId, plan, budget)`, `launchSalvo(state, leaderId, {budget, rankedTargets, maxLaunches})` from `./aggression`; `validateOrder` from `../orders`; `Leader.grudge` (persistent, bumped only by landed impacts โ resolution.ts:249-260).
+- Produces: `planBurnem(state: GameState, leaderId: LeaderId): Order[]` โ same signature dispatch.ts already calls.
+
+- [ ] **Step 1: Rewrite the test file first** โ `tests/engine/ai/burnem.test.ts`. Reuse the state-builder helpers the old starmless test used (read the Task-1-renamed file before deleting its body; other per-planner tests, e.g. `tests/engine/ai/netanyahoo.test.ts`, use the same pattern). Cases:
+
+1. **Placid, unprovoked (round < 6, 3+ alive):** no `grudge` entries against anyone โ orders contain zero `launch` orders; contain at least one `woo` order targeting the other leader with the lowest `favourability` value on Burn'em's record; may contain `build-*` orders.
+2. **Provoked by impact:** set `state.leaders.burnem.grudge.chump = 2`, give Burn'em stockpiled missiles+warheads โ orders contain at least one launch, and EVERY launch order targets `'chump'`. Assert `orders.filter(o => o.kind === 'launch').length >= 1` unconditionally, then assert all targets.
+3. **Provocation is permanent + redirects:** grudge vs chump but chump dead โ launches target the top surviving grudge/threat target instead; assert launches exist and target the expected survivor.
+4. **Patience fallback (round):** round = 6, no grudges, 3 alive, armed โ launches exist, targeting the highest-`threatScore` rival.
+5. **Patience fallback (2 survivors):** round 2, no grudges, only Burn'em + one other alive, armed โ launches exist.
+6. **Producers before consumers:** in any provoked-state order list containing both `build-*` and `launch` orders, the last `build-*` index < first `launch` index. Assert both indices > -1 first (no vacuous pass).
+
+- [ ] **Step 2: Run tests to verify they fail**
+
+Run: `npx vitest run tests/engine/ai/burnem.test.ts`
+Expected: FAIL (old Cautious+Scapegoat logic doesn't match).
+
+- [ ] **Step 3: Implement `planBurnem`** โ full replacement:
+
+```ts
+import type { GameState, LeaderId, Order } from '../types';
+import { apCostOf, validateOrder } from '../orders';
+import { threatScore } from './scoring';
+import { buildToward, launchSalvo, type BuildPlanEntry } from './aggression';
+
+/**
+ * Burn'em โ the Handbrake Turn (spec 2026-07-19 ยง2).
+ *
+ * Placid by default: woos, builds factories and defence, banks AP, never
+ * launches first. The first landed hit on him (persistent grudge > 0) flips
+ * him permanently into full aggression against the provoker. Patience
+ * fallback (round >= 6 unprovoked, or 2 survivors) prevents the
+ * never-attack-first 1v1 stall (cf. Netanyahoo endgame fix).
+ */
+const DEPLOY_COST = 4;
+const BURNEM_PATIENCE_ROUND = 6;
+const BURNEM_MAX_LAUNCHES = 4;
+
+const PLACID_BUILD_PLAN: BuildPlanEntry[] = [
+ { build: { item: 'factory' }, target: 8 },
+ { build: { item: 'defence', type: 'shield' }, target: 2 },
+];
+
+// Missile before warheads: delivery-first avoids the armed-but-undeliverable
+// zero-fire failure. Producers precede consumers in the emitted batch.
+const PROVOKED_BUILD_PLAN: BuildPlanEntry[] = [
+ { build: { item: 'missile' }, target: 3 },
+ { build: { item: 'warhead', yield: 'medium' }, target: 2 },
+ { build: { item: 'warhead', yield: 'small' }, target: 2 },
+ { build: { item: 'factory' }, target: 7 },
+];
+
+export function planBurnem(state: GameState, leaderId: LeaderId): Order[] {
+ const me = state.leaders[leaderId];
+ if (!me || !me.alive) return [];
+
+ let budget = me.ap;
+ const others = state.cast.filter((t) => t !== leaderId && state.leaders[t]?.alive);
+
+ // --- Provocation: any surviving leader who ever landed a hit (grudge persists). ---
+ const provokers = others.filter((t) => (me.grudge[t] ?? 0) > 0);
+ let target: LeaderId | undefined;
+ if (provokers.length > 0) {
+ target = provokers.reduce((best, t) =>
+ (me.grudge[t] ?? 0) > (me.grudge[best] ?? 0) ? t : best,
+ );
+ } else if (me.grudge && Object.entries(me.grudge).some(([id, g]) => (g ?? 0) > 0)) {
+ // Provoker(s) dead: stay provoked, redirect to the strongest survivor.
+ target = pickStrongestRival(state, leaderId, others);
+ } else if (state.round >= BURNEM_PATIENCE_ROUND || others.length === 1) {
+ // Patience fallback โ deadlock guard.
+ target = pickStrongestRival(state, leaderId, others);
+ }
+
+ const orders: Order[] = [];
+
+ if (target !== undefined) {
+ // --- Provoked: handbrake off. ---
+ const build = buildToward(state, leaderId, PROVOKED_BUILD_PLAN, budget);
+ const salvo = launchSalvo(state, leaderId, {
+ budget: budget - build.apSpent,
+ rankedTargets: [target],
+ maxLaunches: BURNEM_MAX_LAUNCHES,
+ });
+ budget -= build.apSpent + salvo.apSpent;
+ orders.push(...build.orders, ...salvo.orders);
+ } else {
+ // --- Placid: friendliest man in the apocalypse. ---
+ const wooTarget = others.reduce((best, t) =>
+ (me.favourability[t] ?? 0) < (me.favourability[best] ?? 0) ? t : best,
+ others[0]);
+ if (wooTarget !== undefined) {
+ const woo: Order = { kind: 'woo', target: wooTarget };
+ if (budget >= apCostOf(woo) && validateOrder(state, leaderId, woo).ok) {
+ orders.push(woo);
+ budget -= apCostOf(woo);
+ }
+ }
+ const build = buildToward(state, leaderId, PLACID_BUILD_PLAN, budget);
+ budget -= build.apSpent;
+ orders.push(...build.orders);
+ if (me.stockpile.shields >= 1 && budget >= DEPLOY_COST) {
+ const deploy: Order = { kind: 'deploy-defence', type: 'shield' };
+ if (validateOrder(state, leaderId, deploy).ok) {
+ orders.push(deploy);
+ budget -= DEPLOY_COST;
+ }
+ }
+ // Remaining AP banks implicitly.
+ }
+
+ return orders;
+}
+
+function pickStrongestRival(
+ state: GameState, viewer: LeaderId, others: LeaderId[],
+): LeaderId | undefined {
+ if (others.length === 0) return undefined;
+ return others.reduce((best, t) =>
+ threatScore(state, viewer, t) > threatScore(state, viewer, best) ? t : best,
+ );
+}
+```
+
+Adjust `Order` literal shapes (`woo`, `deploy-defence`, build kinds) to the exact union in `src/engine/orders.ts`/`types.ts` โ copy the shapes the other planners emit; do not invent kinds. If `state.round` is named differently, use the actual field.
+
+- [ ] **Step 4: Remove the scapegoat weight**
+
+Delete `burnemScapegoatPct` from `AI_SCORING_WEIGHTS` in `balance.ts`; fix `balance.test.ts` if it enumerates weights.
+
+- [ ] **Step 5: Run the new planner tests**
+
+Run: `npx vitest run tests/engine/ai/burnem.test.ts` โ Expected: PASS (6 tests).
+
+- [ ] **Step 6: Full engine suite โ watch the blast radius**
+
+Run: `npm run test:run` โ Expected: all pass. Pay attention to: `ai-duel.test.ts` (80-seed win breakdown), termination/integration tests, `lookahead.test.ts`, `dispatcher.test.ts`. If the duel distribution breaks its soft expectations, tune `PROVOKED_BUILD_PLAN` targets / `BURNEM_MAX_LAUNCHES` / `BURNEM_PATIENCE_ROUND` โ do NOT loosen test thresholds.
+
+- [ ] **Step 7: Commit**
+
+```bash
+git add src/engine/ai/burnem.ts src/engine/balance.ts tests/engine/ai/burnem.test.ts tests/engine/balance.test.ts
+git commit -m "feat(engine): Burn'em handbrake-turn planner with patience fallback"
+```
+
+---
+
+### Task 4: Portrait + UI content
+
+**Files:**
+- Modify: `src/ui/portraits/faces.tsx` (replace `BurnemFace` body), `src/ui/portraits/index.ts` (PORTRAIT_META color), `src/ui/tokens.css` (`--c-starm` โ `--c-burnem`), `src/ui/content/cast.ts`, `src/ui/util/newspaper.ts` (any remaining display copy), `src/ui/components/WorldMap.tsx` (country label stays UK โ verify only)
+- Test: `tests/ui/Portrait.test.tsx` (add/adjust a burnem case if it enumerated leaders)
+
+**Interfaces:**
+- Consumes: `FACES`/`PORTRAIT_META` records from slice-1; token `--c-burnem`.
+- Produces: `BurnemFace(): JSX.Element` keyed as `FACES.burnem`.
+
+- [ ] **Step 1: tokens.css** โ rename `--c-starm: #8a1729;` to `--c-burnem: #8a1729;` (Task 1 may have already renamed the var name; ensure value unchanged and no `--c-starm` references remain: `grep -r "c-starm" src`).
+
+- [ ] **Step 2: Replace `BurnemFace`** in `faces.tsx` (modeled on the "VOTE ANDY FOR US" placard caricature: long pale face, flat dark side-sweep, heavy rectangular brows, rectangular glasses, stern flat mouth, black zip-up, mini placard prop):
+
+```tsx
+export function BurnemFace() {
+ return (
+
+ );
+}
+```
+
+- [ ] **Step 3: `content/cast.ts`** โ replace the burnem entry:
+
+```ts
+ burnem: { profile: "King in t' North", mood: "We need to bring people with us, don't we?" },
+```
+
+- [ ] **Step 4: Sweep remaining display copy**
+
+`grep -rn "Burn'em\|burnem" src/ui` โ verify newspaper market ticker/obit copy reads naturally with the new name (ticker sym e.g. `BRN`), Setup tile shows "UK ยท KING IN T' NORTH". Adjust `tests/ui/Portrait.test.tsx` if it enumerated cast ids.
+
+- [ ] **Step 5: Verify + commit**
+
+Run: `npm run typecheck && npm run test:run` โ Expected: clean / all pass.
+Run `npm run dev` and eyeball the Setup tile + an Action event with Burn'em.
+
+```bash
+git add src/ui tests/ui
+git commit -m "feat(ui): Burn'em portrait (campaign-placard caricature) and cast copy"
+```
+
+---
+
+### Task 5: Docs + final sweep
+
+- [ ] **Step 1:** Update `docs/superpowers/flavour-bank.md` cast table: Starmless row โ Burn'em (Cautious+Scapegoat โ "Handbrake Turn โ placid until provoked, then total"), note the register rules (tag questions, handbrake pivot, Hillsborough exclusion).
+- [ ] **Step 2:** `grep -ri starmless src tests` โ zero hits. `npm run test:run && npm run typecheck && npm run build` โ green.
+- [ ] **Step 3:** `graphify update .`
+- [ ] **Step 4:** Commit:
+
+```bash
+git add docs/superpowers/flavour-bank.md
+git commit -m "docs: flavour-bank cast table โ Burn'em replaces Starmless"
+```
+
+---
+
+## Self-Review Notes
+
+- Spec coverage: ยง1 rename โ Task 1; ยง2 planner โ Task 3; ยง3 flavour โ Task 2; ยง4 portrait/UI โ Task 4; ยง5 testing โ embedded per task; ยง6 docs โ Task 5.
+- Type consistency: `planBurnem` signature matches dispatch.ts call; `burnemBank` matches `FlavorBank`; `BurnemFace` slots into the `FACES` record from slice 1. Order-literal shapes flagged for verification against the real union rather than guessed.
+- Flavour content pre-checked against exclusions: no Hillsborough, no religious markers, no Disraeli line; tag-question density satisfied; `{scapegoat}` token absent by design.
diff --git a/docs/superpowers/plans/2026-07-19-comic-retheme-slice1.md b/docs/superpowers/plans/2026-07-19-comic-retheme-slice1.md
new file mode 100644
index 0000000..c827a40
--- /dev/null
+++ b/docs/superpowers/plans/2026-07-19-comic-retheme-slice1.md
@@ -0,0 +1,874 @@
+# Comic Re-theme Slice 1 (Foundation + Setup) Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Land the comic-tabloid design foundation (tokens, fonts, SVG portrait system, shared comic primitives) and re-theme the Setup screen to the design handoff.
+
+**Architecture:** Global `tokens.css` carries the design language (custom properties + base classes); typed React components in `src/ui/components/comic/` and `src/ui/components/Portrait.tsx` port the handoff prototype (`design_handoff_nuke_game/`); `Setup.tsx` is rebuilt on top of them keeping all existing behavior (multi-human roster, seed, START_GAME dispatch).
+
+**Tech Stack:** Vite 5, React 18, TypeScript 5, CSS modules, vitest + @testing-library/react, @fontsource self-hosted fonts.
+
+**Spec:** `docs/superpowers/specs/2026-07-19-comic-retheme-slice1-design.md`
+
+## Global Constraints
+
+- Design source of truth: `design_handoff_nuke_game/` (in-repo). Lift exact values; never import its files at runtime, never use its mock `data.jsx` as state.
+- Difficulty labels verbatim: easy = "Fine, Probably", normal = "Not Great", hard = "We're Cooked".
+- Header copy verbatim: kicker "A PARODY IN POOR TASTE", title "NUKE!" (magenta "!"), tagline `"Everybody plays. Nobody wins."` (Nobody bold), ribbon "SELECT YOUR ENEMIES", begin button "Begin the End โฏ".
+- Human portrait accents fixed per slot: player1 `--cyan`, player2 `--green`, player3 `--yellow-soft`, player4 `--magenta-deep`, player5 `--ink-soft`.
+- Stat values on cast tiles come from `LEADER_PROFILES` (engine truth), NOT the handoff's sample numbers.
+- The handoff DEV NAV is not ported.
+
+## Guardrails (from better-memory, surfaced per planning reflection be7ad6bf, conf 0.9)
+
+- **[[aa0375da]] Product emojis are design language, not whimsy** (conf 0.7): the global "no emojis" user preference applies to assistant text only. The โฏ โ ๐ฆ flag emojis in this plan's UI code are spec'd โ do NOT strip them.
+- **[[326f4462]] Every commit must independently typecheck** (conf 0.55, 2 evid): `noUnusedLocals` fails on imports added "for a later task". Add each import only in the commit that uses it.
+- **[[063500c1]] No guarded assertions** (conf 0.7, 3 evid, vitest): never `if (cond) expect(...)`; assert preconditions unconditionally.
+- **[[9f01589a]] Subagents may stop before the final commit** (conf 0.6): controller verifies `git status` after each task; if work is on disk uncommitted, verify tests/typecheck and commit directly.
+- **[[0c83e25d]] Locators match DOM textContent, not CSS-rendered text** (conf 0.8): `text-transform: uppercase` in CSS keeps testing-library queries on the source string working โ keep semantic case in JSX, uppercase in CSS.
+
+Dismissed as not applicable here: AI-planner/resource-projection reflections (31c145bf, 125145ea, 0409087f, ef7d086e โ no engine changes in this slice), python tempfile (85e7ec84), freeze logging (228dfa6d), clone-and-mutate orchestrator (977f97cc), consume-split (f091be56), scaffold-defaults (a6230fe0 โ project already has sources/tests), website/README sync (98056ebc โ no better-memory code touched; repo has no website).
+
+## File Structure
+
+```
+src/ui/tokens.css (new โ global tokens + base classes)
+src/ui/main.tsx (modify โ import fonts + tokens)
+src/ui/portraits/faces.tsx (new โ 8 face SVG components)
+src/ui/portraits/index.ts (new โ FACES, PORTRAIT_META, HUMAN_ACCENTS, extractFlag)
+src/ui/components/Portrait.tsx (new)
+src/ui/components/comic/*.tsx (new โ 14 primitives + index.ts barrel)
+src/ui/screens/Setup.tsx (rewrite)
+src/ui/screens/Setup.module.css (rewrite)
+src/ui/content/cast.ts (new โ profile/mood copy per AI leader)
+tests/ui/Portrait.test.tsx (new)
+tests/ui/HoldButton.test.tsx (new)
+tests/ui/Setup.multihuman.test.tsx (modify โ begin-button assertions)
+package.json (modify โ @fontsource deps)
+```
+
+---
+
+### Task 1: Design tokens + fonts
+
+**Files:**
+- Create: `src/ui/tokens.css`
+- Modify: `src/ui/main.tsx`
+- Modify: `package.json` (via npm install)
+
+**Interfaces:**
+- Produces: global classes `.paper .panel .panel-title .display .tabloid .pop .hand .bang .mono .upper .italic .stamp .tag .btn .bubble .burst .halftone-* .row .col .grow .center .between .gap-* .wrap .tc .fadein .popin .wobble .screen .app .app-stage` and all `--*` custom properties โ every later task relies on these exact names.
+
+- [ ] **Step 1: Install fonts**
+
+Run:
+```bash
+npm install -D @fontsource/anton @fontsource/playfair-display @fontsource/work-sans @fontsource/jetbrains-mono @fontsource/bowlby-one @fontsource/patrick-hand @fontsource/bangers
+```
+Expected: 7 packages added to devDependencies.
+
+- [ ] **Step 2: Create `src/ui/tokens.css`**
+
+Copy `design_handoff_nuke_game/tokens.css` verbatim with exactly two changes:
+1. Delete the dev-nav block โ everything from the comment `/* Bottom debug nav (toggleable via tweaks) */` through the `.devnav button:hover { ... }` rule (source lines 314โ340).
+2. Keep everything else byte-identical (palette, type utilities, `.panel`, `.stamp`, `.tag`, `.btn`, `.bubble`, `.burst`, scrollbar, `.app`/`.screen`, utility classes, keyframes).
+
+- [ ] **Step 3: Wire imports in `src/ui/main.tsx`**
+
+```tsx
+import '@fontsource/anton';
+import '@fontsource/playfair-display/400.css';
+import '@fontsource/playfair-display/700.css';
+import '@fontsource/playfair-display/900.css';
+import '@fontsource/playfair-display/400-italic.css';
+import '@fontsource/playfair-display/700-italic.css';
+import '@fontsource/work-sans/400.css';
+import '@fontsource/work-sans/500.css';
+import '@fontsource/work-sans/600.css';
+import '@fontsource/work-sans/700.css';
+import '@fontsource/work-sans/800.css';
+import '@fontsource/jetbrains-mono/400.css';
+import '@fontsource/jetbrains-mono/700.css';
+import '@fontsource/bowlby-one';
+import '@fontsource/patrick-hand';
+import '@fontsource/bangers';
+import './tokens.css';
+import { createRoot } from 'react-dom/client';
+import App from './App';
+
+const rootElement = document.getElementById('root');
+if (!rootElement) throw new Error('Root element #root not found in index.html');
+createRoot(rootElement).render();
+```
+
+- [ ] **Step 4: Verify**
+
+Run: `npm run typecheck` โ Expected: clean.
+Run: `npm run test:run` โ Expected: all existing tests pass (tokens are additive; no screen imports them by class yet).
+Run: `npm run build` โ Expected: builds; font files emitted to dist assets.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add src/ui/tokens.css src/ui/main.tsx package.json package-lock.json
+git commit -m "feat(ui): comic-tabloid design tokens + self-hosted fonts"
+```
+
+---
+
+### Task 2: Portrait system
+
+**Files:**
+- Create: `src/ui/portraits/faces.tsx`
+- Create: `src/ui/portraits/index.ts`
+- Create: `src/ui/components/Portrait.tsx`
+- Test: `tests/ui/Portrait.test.tsx`
+
+**Interfaces:**
+- Consumes: `isHuman(id: LeaderId): boolean` from `src/engine/state.ts`; `LeaderId` from `src/engine/types.ts`; CSS vars from Task 1.
+- Produces:
+ - `` (default size 80, shape circle, showBadge true)
+ - `extractFlag(country: string): string`
+ - `PORTRAIT_META: Record` (AI cast + disparage)
+ - `HUMAN_ACCENTS: Record<'player1'|'player2'|'player3'|'player4'|'player5', string>`
+
+- [ ] **Step 1: Write the failing test** โ `tests/ui/Portrait.test.tsx`
+
+```tsx
+import { describe, expect, it } from 'vitest';
+import { render } from '@testing-library/react';
+import Portrait from '../../src/ui/components/Portrait';
+import { extractFlag } from '../../src/ui/portraits';
+
+describe('', () => {
+ it('renders an AI leader face SVG with its flag badge', () => {
+ const { container, getByText } = render();
+ expect(container.querySelector('svg')).not.toBeNull();
+ expect(getByText('๐บ๐ธ')).toBeInTheDocument();
+ });
+
+ it('renders the Groucho disguise for human slots with the supplied flag', () => {
+ const { container, getByText } = render(
+ ,
+ );
+ // Groucho face is identifiable by its data marker
+ expect(container.querySelector('[data-face="groucho"]')).not.toBeNull();
+ expect(getByText('๐ฆ')).toBeInTheDocument();
+ });
+
+ it('hides the badge when showBadge is false', () => {
+ const { queryByText } = render(
+ ,
+ );
+ expect(queryByText('๐บ๐ธ')).toBeNull();
+ });
+});
+
+describe('extractFlag', () => {
+ it('extracts a regional-indicator flag', () => {
+ expect(extractFlag('๐บ๐ธ US')).toBe('๐บ๐ธ');
+ });
+ it('extracts a pictographic emoji', () => {
+ expect(extractFlag('๐ฆ Freedonia')).toBe('๐ฆ');
+ });
+ it('falls back to the globe for plain text', () => {
+ expect(extractFlag('Freedonia')).toBe('๐');
+ });
+});
+```
+
+- [ ] **Step 2: Run test to verify it fails**
+
+Run: `npx vitest run tests/ui/Portrait.test.tsx`
+Expected: FAIL โ cannot resolve `../../src/ui/components/Portrait`.
+
+- [ ] **Step 3: Create `src/ui/portraits/faces.tsx`**
+
+Port the eight face functions from `design_handoff_nuke_game/portraits.jsx` (lines 9โ249) verbatim as named exports typed `(): JSX.Element` โ `ChumpFace`, `NetanyahooFace`, `KhameneverhereFace`, `StarmlessFace`, `CarnageFace`, `MileighFace`, `DisparageFace`, `GrouchoFace` (rename of `PlayerFace`). The JSX is already camelCase-attribute React code; copy bodies unchanged, with one addition: `GrouchoFace`'s root `