← back to Crazy News Channel Shadowman
Add P24 cartoon: The Revolving Door (invented regulator/lobbyist revolving-door satire)
76360d34e4a9a5cd2eed19353a050f690f4c081f · 2026-09-24 10:55:05 -0700 · Steve Abrams
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Files touched
A cartoons/20260924-lobbyist-revolving-door.htmlM cartoons/manifest.js
Diff
commit 76360d34e4a9a5cd2eed19353a050f690f4c081f
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Sep 24 10:55:05 2026 -0700
Add P24 cartoon: The Revolving Door (invented regulator/lobbyist revolving-door satire)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---
cartoons/20260924-lobbyist-revolving-door.html | 319 +++++++++++++++++++++++++
cartoons/manifest.js | 11 +
2 files changed, 330 insertions(+)
diff --git a/cartoons/20260924-lobbyist-revolving-door.html b/cartoons/20260924-lobbyist-revolving-door.html
new file mode 100644
index 0000000..fb35ce0
--- /dev/null
+++ b/cartoons/20260924-lobbyist-revolving-door.html
@@ -0,0 +1,319 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0" />
+<title>The Revolving Door — P24 Political Cartoon Desk</title>
+<meta name="description" content="An original invented-satire strip: the same invented official exits an invented capitol's revolving door as a regulator and spins back in seconds later as a lobbyist, carrying a slightly bigger briefcase every time." />
+<style>
+:root{
+ --bg:#f4efe4; --panel:#fffdf6; --ink:#241c14; --ink-dim:#6b5f4d; --rule:#241c14;
+ --accent:#b5302f; --accent2:#0f6b52; --focus:#0b3d91; --seal:#c9a227;
+}
+@media (prefers-color-scheme: dark){ :root:not([data-theme="light"]){
+ --bg:#1b160f; --panel:#241d14; --ink:#f2ead9; --ink-dim:#c9bda3; --rule:#f2ead9;
+ --accent:#ff6b64; --accent2:#3ddcb0; --focus:#8ab4ff; --seal:#e0c25a;
+}}
+:root[data-theme="dark"]{
+ --bg:#1b160f; --panel:#241d14; --ink:#f2ead9; --ink-dim:#c9bda3; --rule:#f2ead9;
+ --accent:#ff6b64; --accent2:#3ddcb0; --focus:#8ab4ff; --seal:#e0c25a;
+}
+*,*::before,*::after{box-sizing:border-box}
+html{overflow-x:hidden}
+[hidden]{display:none!important}
+body{margin:0;min-height:100vh;background:var(--bg);color:var(--ink);font-family:Georgia,"Times New Roman",serif;line-height:1.4;padding-bottom:2rem}
+h1,h2,p{margin:0}
+:focus-visible{outline:3px solid var(--focus); outline-offset:2px}
+a{color:var(--accent2)}
+header{padding:16px 20px 10px;border-bottom:3px solid var(--rule);display:flex;justify-content:space-between;align-items:flex-start;flex-wrap:wrap;gap:10px}
+header a.back{font-family:-apple-system,sans-serif;font-size:.8rem;color:var(--ink-dim);text-decoration:none;border:1px solid var(--ink-dim);border-radius:6px;padding:5px 10px}
+.kicker{font-family:-apple-system,sans-serif;font-size:.7rem;letter-spacing:.08em;text-transform:uppercase;color:var(--accent)}
+h1{font-size:1.7rem;margin-top:2px}
+.dek{font-style:italic;color:var(--ink-dim);max-width:60ch;margin-top:4px}
+main{max-width:900px;margin:0 auto;padding:18px 20px}
+.strip{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}
+@media (max-width:720px){ .strip{grid-template-columns:1fr} }
+figure{margin:0;background:var(--panel);border:3px solid var(--rule);border-radius:8px;overflow:hidden;display:flex;flex-direction:column}
+.phead{display:flex;justify-content:space-between;font-family:-apple-system,sans-serif;font-size:.65rem;letter-spacing:.05em;text-transform:uppercase;color:var(--ink-dim);padding:6px 10px;border-bottom:1px solid var(--ink-dim)}
+.art{width:100%;aspect-ratio:1/1;display:block;cursor:pointer;background:transparent}
+.art:focus-visible{outline:3px solid var(--focus); outline-offset:-3px}
+figcaption{font-family:-apple-system,sans-serif;font-size:.8rem;color:var(--ink-dim);padding:8px 10px}
+.cover{width:100%;height:100%;background:repeating-linear-gradient(45deg,var(--panel),var(--panel) 10px,color-mix(in srgb, var(--panel) 85%, var(--ink)) 10px,color-mix(in srgb, var(--panel) 85%, var(--ink)) 20px);border:none;display:flex;align-items:center;justify-content:center;font-family:-apple-system,sans-serif;font-size:.85rem;color:var(--ink-dim);cursor:pointer;aspect-ratio:1/1}
+.cover:hover{color:var(--ink)}
+.dock{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin:16px 0;font-family:-apple-system,sans-serif}
+button.primary,button.twist{font-family:-apple-system,sans-serif;font-size:.9rem;font-weight:700;border-radius:6px;padding:9px 16px;cursor:pointer;border:2px solid var(--rule)}
+button.primary{background:var(--ink);color:var(--panel)}
+button.twist{background:var(--seal);color:#241c14;border-color:var(--seal)}
+button:disabled{opacity:.45;cursor:not-allowed}
+button.theme{margin-left:auto;background:transparent;border:1px solid var(--ink-dim);color:var(--ink-dim);border-radius:6px;padding:6px 10px;font-family:-apple-system,sans-serif;font-size:.8rem;cursor:pointer}
+#status{font-family:-apple-system,sans-serif;font-size:.82rem;padding:8px 12px;border-radius:6px;border:1px solid var(--ink-dim);color:var(--ink-dim);min-height:1.2em}
+#status.ok{border-color:var(--accent2);color:var(--accent2)}
+#status.err{border-color:var(--accent);color:var(--accent)}
+#status.info{border-color:var(--focus);color:var(--focus)}
+.counter{font-family:-apple-system,sans-serif;font-size:.8rem;font-weight:700;color:var(--ink);border:2px dashed var(--seal);border-radius:6px;padding:6px 12px;white-space:nowrap}
+footer{max-width:900px;margin:20px auto 0;padding:0 20px;font-family:-apple-system,sans-serif;font-size:.72rem;color:var(--ink-dim)}
+.hint{font-family:-apple-system,sans-serif;font-size:.72rem;color:var(--ink-dim);margin-top:6px}
+@media (prefers-reduced-motion: reduce){ * { animation-duration:0.001ms !important; transition-duration:0.001ms !important; } }
+.door-anim{transition:transform .45s ease}
+@media (prefers-reduced-motion: reduce){ .door-anim{transition:none} }
+</style>
+</head>
+<body>
+<header>
+ <div>
+ <div class="kicker">P24 Political Cartoon Desk · Invented Satire</div>
+ <h1>The Revolving Door</h1>
+ <p class="dek">An invented capitol's revolving door turns once. A regulator walks out. It turns again. A lobbyist walks in. Same coat, same face, slightly bigger briefcase.</p>
+ </div>
+ <div style="display:flex;flex-direction:column;gap:8px;align-items:flex-end">
+ <a class="back" href="index.html">← Cartoon Desk</a>
+ <button class="theme" id="themeBtn" type="button" aria-pressed="false">Dark mode: off</button>
+ </div>
+</header>
+<main>
+ <div class="strip" id="strip">
+ <figure class="panel" data-i="0">
+ <div class="phead"><span>Panel 1 · The Exit</span><span id="stamp0"></span></div>
+ <svg class="art" id="art0" viewBox="0 0 300 300" role="img" tabindex="0"></svg>
+ <button class="cover" id="cover0" type="button">Click, tap, or press N to begin</button>
+ <figcaption id="cap0"></figcaption>
+ </figure>
+ <figure class="panel" data-i="1">
+ <div class="phead"><span>Panel 2 · The Spin</span><span id="stamp1"></span></div>
+ <svg class="art" id="art1" viewBox="0 0 300 300" role="img" tabindex="0"></svg>
+ <button class="cover" id="cover1" type="button" disabled>In queue</button>
+ <figcaption id="cap1"></figcaption>
+ </figure>
+ <figure class="panel" data-i="2">
+ <div class="phead"><span>Panel 3 · The Return</span><span id="stamp2"></span></div>
+ <svg class="art" id="art2" viewBox="0 0 300 300" role="img" tabindex="0"></svg>
+ <button class="cover" id="cover2" type="button" disabled>In queue</button>
+ <figcaption id="cap2"></figcaption>
+ </figure>
+ </div>
+
+ <div class="dock">
+ <button class="primary" id="nextBtn" type="button">Process Panel 1</button>
+ <button class="twist" id="twistBtn" type="button">Plot Twist! (Spin the Door)</button>
+ <span class="counter" id="counter">Revolutions: 0</span>
+ <div id="status" role="status" aria-live="polite"></div>
+ </div>
+ <p class="hint">Keys: N/→ next panel · T spin the door again · click a panel to advance · once all 3 panels are open, click Panel 3 or press T to keep spinning</p>
+</main>
+<footer>All officials, agencies, and buildings are invented. No actual regulator was inconvenienced, or promoted. Part of PANDEMONIUM-24.</footer>
+
+<script>
+(function(){
+ const NS = 'http://www.w3.org/2000/svg';
+ function el(tag, attrs, parent){
+ const n = document.createElementNS(NS, tag);
+ for (const k in attrs) n.setAttribute(k, attrs[k]);
+ if (parent) parent.appendChild(n);
+ return n;
+ }
+ function reduced(){ return matchMedia('(prefers-reduced-motion: reduce)').matches; }
+
+ // ---- invented "official" — same person, alternating title, plus a rotating cast of runners-up ----
+ const OFFICIALS = [
+ { name: 'Deputy Administrator Voss', color: '#b5302f' },
+ { name: 'Under-Secretary Marchetti', color: '#0f6b52' },
+ { name: 'Regional Director Ashby', color: '#c9a227' },
+ { name: 'Special Counselor Denholm', color: '#6b4fa0' },
+ ];
+ let officialIdx = 0;
+
+ function building(svg){
+ // invented capitol facade: dome + columns, simple flat iconography
+ el('rect', { x:20, y:60, width:260, height:170, fill:'none', stroke:'currentColor', 'stroke-width':2.5, opacity:0.55 }, svg);
+ el('path', { d:'M 130 60 A 20 26 0 0 1 170 60 Z', fill:'none', stroke:'currentColor', 'stroke-width':2.5, opacity:0.55 }, svg);
+ for (let i=0;i<5;i++){
+ const x = 40 + i*45;
+ el('line', { x1:x, y1:70, x2:x, y2:225, stroke:'currentColor', 'stroke-width':3, opacity:0.35 }, svg);
+ }
+ el('rect', { x:20, y:225, width:260, height:8, fill:'currentColor', opacity:0.35 }, svg);
+ const t = el('text', { x:150, y:48, 'text-anchor':'middle', 'font-family':'-apple-system,sans-serif', 'font-size':'10', 'letter-spacing':'2', fill:'var(--ink-dim)' }, svg);
+ t.textContent = 'ROTUNDA HOUSE OF INVENTED AFFAIRS';
+ }
+
+ function figure(svg, cx, cy, r, color, briefcaseScale){
+ const g = el('g', {}, svg);
+ el('circle', { cx, cy: cy - r*1.1, r: r*0.55, fill: color }, g); // head
+ el('path', { d: `M ${cx - r*0.9} ${cy + r*0.9} Q ${cx} ${cy - r*0.1} ${cx + r*0.9} ${cy + r*0.9}`, fill:'none', stroke: color, 'stroke-width': r*0.45, 'stroke-linecap':'round' }, g); // coat/shoulders
+ // briefcase, grows with revolutions
+ const bw = 16 * briefcaseScale, bh = 12 * briefcaseScale;
+ const bx = cx + r*0.75, by = cy + r*0.55;
+ el('rect', { x: bx, y: by, width: bw, height: bh, rx: 2, fill:'#3a2c1a', stroke: color, 'stroke-width':1.5 }, g);
+ el('rect', { x: bx + bw*0.4, y: by - bh*0.18, width: bw*0.2, height: bh*0.25, fill:'#3a2c1a', stroke: color, 'stroke-width':1 }, g);
+ return g;
+ }
+
+ function doorFrame(svg, cx, cy, r, rotation){
+ const g = el('g', {}, svg);
+ el('rect', { x: cx - r - 14, y: cy - r - 14, width: (r+14)*2, height: (r+14)*2, fill:'none', stroke:'currentColor', 'stroke-width':3, opacity:0.6, rx:6 }, g);
+ el('circle', { cx, cy, r, fill: 'none', stroke:'currentColor', 'stroke-width':2, opacity:0.5 }, g);
+ const cross = el('g', { class:'door-anim', transform:`rotate(${rotation} ${cx} ${cy})` }, g);
+ el('line', { x1:cx-r, y1:cy, x2:cx+r, y2:cy, stroke:'var(--accent2)', 'stroke-width':4 }, cross);
+ el('line', { x1:cx, y1:cy-r, x2:cx, y2:cy+r, stroke:'var(--accent2)', 'stroke-width':4 }, cross);
+ return g;
+ }
+
+ function label(svg, x, y, text, color){
+ const t = el('text', { x, y, 'text-anchor':'middle', 'font-family':'-apple-system,sans-serif', 'font-weight':'800', 'font-size':'13', fill: color }, svg);
+ t.textContent = text;
+ return t;
+ }
+
+ function panel0(svg, official){
+ svg.innerHTML = '';
+ building(svg);
+ doorFrame(svg, 150, 150, 42, 0);
+ figure(svg, 210, 150, 26, official.color, 1);
+ label(svg, 210, 205, 'REGULATOR', official.color);
+ label(svg, 150, 270, official.name, 'var(--ink-dim)');
+ }
+
+ function panel1(svg, official){
+ svg.innerHTML = '';
+ building(svg);
+ doorFrame(svg, 150, 150, 42, 45);
+ // motion blur strokes suggesting mid-spin, figure obscured inside
+ for (let i=0;i<4;i++){
+ el('line', { x1:150-30+i*20, y1:110, x2:150-24+i*20, y2:190, stroke:'currentColor', 'stroke-width':2, opacity:0.18 }, svg);
+ }
+ const l1 = label(svg, 95, 150, 'REGULATOR', official.color);
+ l1.setAttribute('opacity','0.35');
+ const l2 = label(svg, 205, 150, 'LOBBYIST', official.color);
+ l2.setAttribute('opacity','0.55');
+ label(svg, 150, 270, 'mid-revolution — title pending', 'var(--ink-dim)');
+ }
+
+ function panel2(svg, official, revolutions, briefcaseScale, asLobbyist){
+ svg.innerHTML = '';
+ building(svg);
+ doorFrame(svg, 150, 150, 42, 90);
+ figure(svg, 90, 150, 26, official.color, briefcaseScale);
+ label(svg, 90, 205, asLobbyist ? 'LOBBYIST' : 'REGULATOR', official.color);
+ label(svg, 150, 270, `Revolution #${revolutions}`, 'var(--ink-dim)');
+ }
+
+ const CAPTIONS_STATIC = [
+ 'An invented official exits Rotunda House through the revolving door, badge freshly clipped, title: Regulator.',
+ 'The door keeps turning. Nobody on either side can tell which way is in and which way is out anymore.',
+ ];
+
+ const state = { revealed: 0, revolutions: 0, asLobbyist: true, briefcaseScale: 1 };
+
+ function briefcaseScaleFor(n){
+ return 1 + Math.min(n, 10) * 0.09;
+ }
+
+ function panel2Caption(){
+ const pct = Math.round((state.briefcaseScale - 1) * 100);
+ const role = state.asLobbyist ? 'lobbyist' : 'regulator';
+ return `Revolution #${state.revolutions}: ${OFFICIALS[officialIdx].name} spins back in as a ${role}. Briefcase now ${pct}% bigger than the day they started.`;
+ }
+
+ function renderPanel(i){
+ const svg = document.getElementById('art' + i);
+ const official = OFFICIALS[officialIdx];
+ if (i === 0) { panel0(svg, official); document.getElementById('cap0').textContent = CAPTIONS_STATIC[0]; }
+ if (i === 1) { panel1(svg, official); document.getElementById('cap1').textContent = CAPTIONS_STATIC[1]; }
+ if (i === 2) { panel2(svg, official, state.revolutions, state.briefcaseScale, state.asLobbyist); document.getElementById('cap2').textContent = panel2Caption(); }
+ document.getElementById('stamp' + i).textContent = i <= state.revealed ? ['EXIT LOGGED','IN TRANSIT','RE-ENTERED'][i] : '';
+ const cover = document.getElementById('cover' + i);
+ if (i === state.revealed) { cover.hidden = false; cover.disabled = false; cover.textContent = i === 0 ? 'Click, tap, or press N to begin' : 'Click, tap, or press N to process'; }
+ else { cover.hidden = i <= state.revealed - 1; }
+ }
+
+ function paintAll(){
+ for (let i=0;i<3;i++) {
+ renderPanel(i);
+ document.getElementById('art'+i).setAttribute('tabindex', i === state.revealed ? '0' : '-1');
+ }
+ syncButtons();
+ updateCounter();
+ }
+
+ function syncButtons(){
+ const nextBtn = document.getElementById('nextBtn');
+ if (state.revealed >= 3) { nextBtn.textContent = 'All 3 panels processed ✓'; nextBtn.disabled = true; }
+ else { nextBtn.textContent = `Process Panel ${state.revealed+1}`; nextBtn.disabled = false; }
+ }
+
+ function updateCounter(){
+ document.getElementById('counter').textContent = `Revolutions: ${state.revolutions}`;
+ }
+
+ function say(kind, msg){
+ const s = document.getElementById('status');
+ s.className = kind; s.textContent = msg;
+ }
+
+ function next(){
+ if (state.revealed >= 3) { say('info', 'The door is already open on all 3 frames. Try Plot Twist! to spin it again.'); return; }
+ state.revealed++;
+ if (state.revealed === 3) { state.revolutions = 1; state.briefcaseScale = briefcaseScaleFor(1); state.asLobbyist = true; }
+ for (let i=0;i<3;i++) {
+ const cover = document.getElementById('cover'+i);
+ cover.hidden = i < state.revealed;
+ cover.disabled = i > state.revealed;
+ document.getElementById('art'+i).setAttribute('tabindex', i === state.revealed ? '0' : '-1');
+ }
+ renderPanel(state.revealed - 1); // the panel that just got processed
+ if (state.revealed < 3) renderPanel(state.revealed); // the newly-active cover's label
+ if (state.revealed === 3) { renderPanel(2); updateCounter(); }
+ say('ok', `Panel ${state.revealed} processed.`);
+ syncButtons();
+ if (state.revealed >= 3) say('ok', `All 3 panels processed. Revolution #1 logged. Press Plot Twist! or click Panel 3 to spin the door again.`);
+ }
+
+ function spinDoor(){
+ if (state.revealed < 3) { say('err', 'ERROR RD-409 · The door has not finished its first turn — process all 3 panels first.'); return; }
+ state.revolutions++;
+ state.asLobbyist = !state.asLobbyist;
+ state.briefcaseScale = briefcaseScaleFor(state.revolutions);
+ if (state.revolutions % 2 === 0) officialIdx = (officialIdx + 1) % OFFICIALS.length;
+ renderPanel(0); renderPanel(1); renderPanel(2);
+ updateCounter();
+ say('ok', panel2Caption());
+ }
+
+ document.getElementById('nextBtn').addEventListener('click', next);
+ document.getElementById('twistBtn').addEventListener('click', spinDoor);
+ for (let i=0;i<3;i++) {
+ document.getElementById('cover'+i).addEventListener('click', () => { if (i === state.revealed) next(); else if (i === 2 && state.revealed >= 3) spinDoor(); });
+ document.getElementById('art'+i).addEventListener('click', () => { if (i === state.revealed) next(); else if (i === 2 && state.revealed >= 3) spinDoor(); });
+ document.getElementById('art'+i).addEventListener('keydown', (e) => {
+ if ((e.key==='Enter'||e.key===' ')) {
+ if (i===state.revealed) { e.preventDefault(); next(); }
+ else if (i === 2 && state.revealed >= 3) { e.preventDefault(); spinDoor(); }
+ }
+ });
+ }
+
+ const themeBtn = document.getElementById('themeBtn');
+ function safeGet(k,d){ try { return localStorage.getItem(k) ?? d; } catch { return d; } }
+ function safeSet(k,v){ try { localStorage.setItem(k,v); } catch {} }
+ let theme = safeGet('p24cartoon.theme', 'auto');
+ function applyTheme(){
+ if (theme === 'dark') { document.documentElement.setAttribute('data-theme','dark'); themeBtn.textContent='Dark mode: on'; themeBtn.setAttribute('aria-pressed','true'); }
+ else { document.documentElement.setAttribute('data-theme','light'); themeBtn.textContent='Dark mode: off'; themeBtn.setAttribute('aria-pressed','false'); }
+ }
+ applyTheme();
+ themeBtn.addEventListener('click', () => { theme = theme === 'dark' ? 'light' : 'dark'; safeSet('p24cartoon.theme', theme); applyTheme(); });
+
+ document.addEventListener('keydown', (e) => {
+ if (e.metaKey || e.ctrlKey || e.altKey) return;
+ const tag = (e.target.tagName || '').toLowerCase();
+ if (tag === 'input' || tag === 'textarea') return;
+ const k = e.key.toLowerCase();
+ if (k === 'n' || e.key === 'ArrowRight') { e.preventDefault(); next(); }
+ else if (k === 't') { e.preventDefault(); spinDoor(); }
+ });
+
+ paintAll();
+ say('info', 'The door is still. Press N or click Panel 1 to begin the first exit.');
+})();
+</script>
+</body>
+</html>
diff --git a/cartoons/manifest.js b/cartoons/manifest.js
index 1ddcc87..1471f52 100644
--- a/cartoons/manifest.js
+++ b/cartoons/manifest.js
@@ -180,5 +180,16 @@ window.P24_CARTOONS = [
"blurb": "A stubborn crosswind at one Montana intersection earns a windsock, a paid Wind Spotter, a proposed windbreak, and finally its own festival.",
"thumb": null,
"story_id": "sw-persistent-crosswind"
+ },
+ {
+ "id": "lobbyist-revolving-door",
+ "title": "The Revolving Door",
+ "file": "20260924-lobbyist-revolving-door.html",
+ "created_at": "2026-09-24T17:54:48Z",
+ "category": "Political Cartoon",
+ "section": "politics",
+ "blurb": "An invented capitol official spins out of the revolving door as a regulator and spins back in seconds later as a lobbyist, carrying a slightly bigger briefcase every revolution.",
+ "thumb": null,
+ "story_id": null
}
];
← 2f0a37c auto-data-snapshot: 2026-09-24T10:46:49 (1 data files) — yol
·
back to Crazy News Channel Shadowman
·
auto-data-snapshot: 2026-09-24T11:17:31 (2 data files) — car 37653be →