← back to Crazy News Channel
Add filibuster-marathon P24 cartoon (3-panel SVG, click-advance)
edcd6b332e31479c9e76256a0e97cb93497153b0 · 2026-09-24 12:40:57 -0700 · Steve Abrams
Delegate Holloway filibusters the invented Assembly, cycling reading
material (phone book -> cereal box -> furniture instructions -> his
own bill) via Plot Twist. Panel 2 has a melting Dali-style clock;
panel 3 shows the chamber asleep at their desks. Fully self-contained,
matches the commission-turtles.html house style. Verified click-advance
+ plot-twist cycling with Playwright, zero console/page errors.
Also fixed manifest.js, which a concurrent process had left with a
JS syntax error (a raw object literal appended after the array's
closing bracket) — folded that entry properly into the array and
appended this cartoon's entry alongside it.
TK-12146
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V29xaoBwcFwJyR4KC43ptn
Files touched
A cartoons/20260924-filibuster-marathon.htmlM cartoons/manifest.js
Diff
commit edcd6b332e31479c9e76256a0e97cb93497153b0
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Sep 24 12:40:57 2026 -0700
Add filibuster-marathon P24 cartoon (3-panel SVG, click-advance)
Delegate Holloway filibusters the invented Assembly, cycling reading
material (phone book -> cereal box -> furniture instructions -> his
own bill) via Plot Twist. Panel 2 has a melting Dali-style clock;
panel 3 shows the chamber asleep at their desks. Fully self-contained,
matches the commission-turtles.html house style. Verified click-advance
+ plot-twist cycling with Playwright, zero console/page errors.
Also fixed manifest.js, which a concurrent process had left with a
JS syntax error (a raw object literal appended after the array's
closing bracket) — folded that entry properly into the array and
appended this cartoon's entry alongside it.
TK-12146
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V29xaoBwcFwJyR4KC43ptn
---
cartoons/20260924-filibuster-marathon.html | 349 +++++++++++++++++++++++++++++
cartoons/manifest.js | 16 +-
2 files changed, 363 insertions(+), 2 deletions(-)
diff --git a/cartoons/20260924-filibuster-marathon.html b/cartoons/20260924-filibuster-marathon.html
new file mode 100644
index 0000000..601b697
--- /dev/null
+++ b/cartoons/20260924-filibuster-marathon.html
@@ -0,0 +1,349 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0" />
+<title>The Filibuster Marathon — P24 Political Cartoon Desk</title>
+<meta name="description" content="An original invented-satire strip: Delegate Holloway filibusters the fictional Assembly by reading anything within arm's reach, while a Dali-style clock melts and the chamber falls asleep standing up." />
+<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)}
+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; } }
+.seal-anim{transition:transform .3s ease}
+@media (prefers-reduced-motion: reduce){ .seal-anim{transition:none} }
+</style>
+</head>
+<body>
+<header>
+ <div>
+ <div class="kicker">P24 Political Cartoon Desk · Invented Satire</div>
+ <h1>The Filibuster Marathon</h1>
+ <p class="dek">Delegate Holloway holds the floor of the invented Assembly by reading anything within arm's reach. Nobody has stopped him. Nobody remembers why he started.</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 Floor Is His</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 · Time, Melting</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 Chamber Sleeps</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!</button>
+ <div id="status" role="status" aria-live="polite"></div>
+ </div>
+ <p class="hint">Keys: N/→ next panel · T plot twist (swaps Holloway's reading material) · click a panel to advance</p>
+</main>
+<footer>All delegates, the Assembly, and its chamber are invented. No actual legislative body was inconvenienced, though several fictional ones dozed off. 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 reading material Holloway filibusters with, cycled by Plot Twist ----
+ const MATERIALS = [
+ { label: 'the Springfield County phone book', sub: 'Vol. 2, "Aab–Kle"', color: '#b5302f' },
+ { label: 'a family-size cereal box', sub: 'nutrition panel, both sides', color: '#0f6b52' },
+ { label: 'bookshelf assembly instructions', sub: 'translated from Swedish, badly', color: '#c9a227' },
+ { label: 'his own bill', sub: 'still stuck in committee', color: '#6b4fa0' },
+ ];
+ let materialIdx = 0;
+
+ // ---- invented Assembly members, asleep at their desks in panel 3 ----
+ const MEMBERS = [
+ { name: 'Delegate Oduya', color: '#b5302f' },
+ { name: 'Delegate Prine', color: '#0f6b52' },
+ { name: 'Delegate Vance-Kettle', color: '#c9a227' },
+ { name: 'Delegate Abernathy', color: '#6b4fa0' },
+ { name: 'Delegate Suh', color: '#0b3d91' },
+ { name: 'Delegate Marrow', color: '#b5302f' },
+ ];
+
+ function podium(svg, cx, cy){
+ el('rect', { x: cx-24, y: cy, width:48, height:60, fill:'currentColor', opacity:0.35, rx:4 }, svg);
+ el('rect', { x: cx-30, y: cy-6, width:60, height:10, fill:'currentColor', opacity:0.5, rx:2 }, svg);
+ }
+
+ function holloway(svg, cx, cy, r, color, material){
+ const g = el('g', {}, svg);
+ // body
+ el('path', { d: `M ${cx - r*0.55} ${cy + r*0.7} Q ${cx} ${cy + r*0.25} ${cx + r*0.55} ${cy + r*0.7}`, fill: 'none', stroke: color, 'stroke-width': r*0.32, 'stroke-linecap':'round' }, g);
+ // head
+ el('circle', { cx, cy: cy - r*0.15, r: r*0.34, fill: color }, g);
+ // held reading material (a rectangle tilted up, "held open")
+ const mg = el('g', { transform: `rotate(-8 ${cx + r*0.35} ${cy + r*0.25})` }, g);
+ el('rect', { x: cx + r*0.05, y: cy + r*0.05, width: r*0.75, height: r*0.55, fill: 'var(--panel)', stroke: color, 'stroke-width': 2, rx: 2 }, mg);
+ for (let i=0;i<3;i++){
+ el('line', { x1: cx + r*0.15, y1: cy + r*0.2 + i*8, x2: cx + r*0.68, y2: cy + r*0.2 + i*8, stroke: color, 'stroke-width': 1.4, opacity:0.5 }, mg);
+ }
+ if (material) {
+ const t = el('text', { x: cx, y: cy + r + 34, 'text-anchor':'middle', 'font-family':'-apple-system,sans-serif', 'font-weight':'700', 'font-size':'11', fill: 'currentColor' }, g);
+ t.textContent = 'Delegate Holloway';
+ const t2 = el('text', { x: cx, y: cy + r + 48, 'text-anchor':'middle', 'font-family':'-apple-system,sans-serif', 'font-size':'9.5', fill: 'var(--ink-dim)' }, g);
+ t2.textContent = 'reading: ' + material.label;
+ }
+ return g;
+ }
+
+ function chamberClock(svg, cx, cy, r, color){
+ el('circle', { cx, cy, r, fill:'none', stroke: color, 'stroke-width': 3 }, svg);
+ el('line', { x1:cx, y1:cy, x2:cx, y2:cy-r*0.6, stroke: color, 'stroke-width':2.4, 'stroke-linecap':'round' }, svg);
+ el('line', { x1:cx, y1:cy, x2:cx+r*0.4, y2:cy+r*0.25, stroke: color, 'stroke-width':2.4, 'stroke-linecap':'round' }, svg);
+ }
+
+ function meltingClock(svg, cx, cy, r, color, melt){
+ // a Dali-style melting clock: the face droops over an edge, hands bend with it
+ const droop = 18 + melt * 14;
+ const path = `M ${cx-r} ${cy-r*0.2}
+ C ${cx-r} ${cy-r}, ${cx+r} ${cy-r}, ${cx+r} ${cy-r*0.2}
+ C ${cx+r} ${cy+r*0.5}, ${cx+r*0.5} ${cy+r*0.9+droop}, ${cx} ${cy+r*0.95+droop}
+ C ${cx-r*0.5} ${cy+r*0.9+droop}, ${cx-r} ${cy+r*0.5}, ${cx-r} ${cy-r*0.2} Z`;
+ el('path', { d: path, fill:'var(--panel)', stroke: color, 'stroke-width': 3 }, svg);
+ // droopy hands, bent partway down the melt
+ el('path', { d: `M ${cx} ${cy} Q ${cx-4} ${cy-r*0.35} ${cx-2} ${cy-r*0.55}`, fill:'none', stroke: color, 'stroke-width':2.2, 'stroke-linecap':'round' }, svg);
+ el('path', { d: `M ${cx} ${cy} Q ${cx+r*0.2} ${cy+r*0.15} ${cx+r*0.3+droop*0.3} ${cy+r*0.35+droop*0.5}`, fill:'none', stroke: color, 'stroke-width':2.2, 'stroke-linecap':'round' }, svg);
+ // a few numerals sliding down the droop
+ ['XII','III','VI'].forEach((n,i) => {
+ const t = el('text', { x: cx - r*0.5 + i*r*0.5, y: cy - r*0.05 + i*(3+melt*3), 'text-anchor':'middle', 'font-family':'Georgia,serif', 'font-size':'9', fill: color, opacity: 0.75 }, svg);
+ t.textContent = n;
+ });
+ }
+
+ function sleepingMember(svg, cx, cy, r, color, standing){
+ const g = el('g', {}, svg);
+ if (standing) {
+ // asleep standing up, head lolled back
+ el('path', { d: `M ${cx - r*0.4} ${cy + r*0.9} L ${cx - r*0.3} ${cy - r*0.1} L ${cx + r*0.3} ${cy - r*0.1} L ${cx + r*0.4} ${cy + r*0.9} Z`, fill: color, opacity:0.3 }, g);
+ el('circle', { cx: cx + r*0.12, cy: cy - r*0.45, r: r*0.3, fill: color }, g);
+ } else {
+ // slumped forward over a desk
+ el('rect', { x: cx - r*0.6, y: cy + r*0.35, width: r*1.2, height: r*0.18, fill:'currentColor', opacity:0.35, rx:2 }, g);
+ el('ellipse', { cx, cy: cy + r*0.15, rx: r*0.5, ry: r*0.32, fill: color, opacity: 0.9 }, g);
+ el('circle', { cx: cx - r*0.15, cy: cy - r*0.05, r: r*0.28, fill: color }, g);
+ }
+ // "Z" sleep marks
+ const zg = el('g', { opacity: 0.7 }, g);
+ ['z','Z','Z'].forEach((z,i) => {
+ const t = el('text', { x: cx + r*0.55 + i*7, y: cy - r*0.5 - i*10, 'font-family':'-apple-system,sans-serif', 'font-weight':'700', 'font-size': 8 + i*3, fill: color }, zg);
+ t.textContent = z;
+ });
+ return g;
+ }
+
+ function panel0(svg, material){
+ svg.innerHTML = '';
+ podium(svg, 130, 190);
+ holloway(svg, 130, 130, 46, material.color, material);
+ chamberClock(svg, 250, 55, 22, 'var(--ink-dim)');
+ const t = el('text', { x: 250, y: 90, 'text-anchor':'middle', 'font-family':'-apple-system,sans-serif', 'font-size':'8.5', fill:'var(--ink-dim)' }, svg);
+ t.textContent = 'Day 1, Hour 1';
+ const b = el('text', { x:150, y:270, 'text-anchor':'middle', 'font-family':'-apple-system,sans-serif', 'font-size':'11', 'font-weight':'700', fill:'currentColor' }, svg);
+ b.textContent = '"I yield to no one," says Delegate Holloway.';
+ const b2 = el('text', { x:150, y:286, 'text-anchor':'middle', 'font-family':'-apple-system,sans-serif', 'font-size':'9.5', fill:'var(--ink-dim)' }, svg);
+ b2.textContent = 'He opens ' + material.label + ' to page one.';
+ }
+
+ function panel1(svg, material, melt){
+ svg.innerHTML = '';
+ podium(svg, 100, 195);
+ holloway(svg, 100, 138, 40, material.color, null);
+ meltingClock(svg, 205, 120, 46, 'var(--accent)', melt);
+ const t = el('text', { x: 205, y: 60, 'text-anchor':'middle', 'font-family':'-apple-system,sans-serif', 'font-size':'8.5', fill:'var(--ink-dim)' }, svg);
+ t.textContent = 'Hour ' + (14 + melt*11) + ', give or take';
+ const b = el('text', { x:150, y:270, 'text-anchor':'middle', 'font-family':'-apple-system,sans-serif', 'font-size':'11', 'font-weight':'700', fill:'currentColor' }, svg);
+ b.textContent = 'He is now on ' + material.sub + '.';
+ const b2 = el('text', { x:150, y:286, 'text-anchor':'middle', 'font-family':'-apple-system,sans-serif', 'font-size':'9.5', fill:'var(--ink-dim)' }, svg);
+ b2.textContent = 'The chamber clock has stopped pretending to be on time.';
+ }
+
+ function panel2(svg, wave){
+ svg.innerHTML = '';
+ // rows of desks; some slumped, one asleep standing at the aisle mic
+ const cols = 3, rows = 2;
+ let i = 0;
+ for (let r0=0; r0<rows; r0++){
+ for (let c0=0; c0<cols; c0++){
+ const mx = 62 + c0*90;
+ const my = 90 + r0*95;
+ const mem = MEMBERS[(i + wave) % MEMBERS.length];
+ sleepingMember(svg, mx, my, 26, mem.color, false);
+ i++;
+ }
+ }
+ // one delegate asleep standing at the aisle microphone, mid-objection
+ sleepingMember(svg, 250, 235, 24, MEMBERS[(wave+2)%MEMBERS.length].color, true);
+ el('rect', { x: 244, y: 258, width: 4, height: 24, fill:'currentColor', opacity:0.4 }, svg);
+ el('circle', { cx: 246, cy: 256, r: 4, fill:'currentColor', opacity:0.5 }, svg);
+ const b = el('text', { x:150, y:280, 'text-anchor':'middle', 'font-family':'-apple-system,sans-serif', 'font-size':'11', 'font-weight':'700', fill:'currentColor' }, svg);
+ b.textContent = 'Nobody has left the chamber. Several have left consciousness.';
+ }
+
+ const CAPTIONS = [
+ 'Delegate Holloway takes the floor of the invented Assembly to object to a routine motion, and does not intend to give it back.',
+ 'Hours blur. The reading material changes; the objection, somehow, does not resolve.',
+ 'The Assembly remains, technically, in session.',
+ ];
+
+ const state = { revealed: 0, twists: 0 };
+
+ function renderPanel(i){
+ const svg = document.getElementById('art' + i);
+ const material = MATERIALS[materialIdx];
+ if (i === 0) panel0(svg, material);
+ if (i === 1) panel1(svg, material, state.twists % 3);
+ if (i === 2) panel2(svg, state.twists);
+ document.getElementById('cap' + i).textContent = CAPTIONS[i];
+ document.getElementById('stamp' + i).textContent = i <= state.revealed ? ['ON THE RECORD','STILL SPEAKING','QUORUM UNCLEAR'][i] : '';
+ const cover = document.getElementById('cover' + i);
+ cover.hidden = i <= state.revealed - 1 || (i === state.revealed);
+ 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'; }
+ }
+
+ function paintAll(){
+ for (let i=0;i<3;i++) {
+ renderPanel(i);
+ document.getElementById('art'+i).setAttribute('tabindex', i === state.revealed ? '0' : '-1');
+ }
+ syncButtons();
+ }
+
+ 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 say(kind, msg){
+ const s = document.getElementById('status');
+ s.className = kind; s.textContent = msg;
+ }
+
+ function next(){
+ if (state.revealed >= 3) { say('info', 'All 3 panels are already on file. Try Plot Twist!'); return; }
+ state.revealed++;
+ 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
+ say('ok', `Panel ${state.revealed} processed.`);
+ syncButtons();
+ if (state.revealed >= 3) say('ok', 'All 3 panels processed. Press Plot Twist! to change what Holloway is reading.');
+ }
+
+ function twist(){
+ if (state.revealed < 3) { say('err', 'ERROR FB-409 · Premature twist — process all 3 panels first.'); return; }
+ materialIdx = (materialIdx + 1) % MATERIALS.length;
+ state.twists++;
+ renderPanel(0); renderPanel(1); renderPanel(2);
+ say('ok', `Plot twist #${state.twists}: Delegate Holloway has moved on to ${MATERIALS[materialIdx].label}.`);
+ }
+
+ document.getElementById('nextBtn').addEventListener('click', next);
+ document.getElementById('twistBtn').addEventListener('click', twist);
+ for (let i=0;i<3;i++) {
+ document.getElementById('cover'+i).addEventListener('click', () => { if (i === state.revealed) next(); });
+ document.getElementById('art'+i).addEventListener('click', () => { if (i === state.revealed) next(); });
+ document.getElementById('art'+i).addEventListener('keydown', (e) => { if ((e.key==='Enter'||e.key===' ') && i===state.revealed) { e.preventDefault(); next(); } });
+ }
+
+ 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(); twist(); }
+ });
+
+ paintAll();
+ say('info', 'Case file open. Press N or click Panel 1 to begin.');
+})();
+</script>
+</body>
+</html>
diff --git a/cartoons/manifest.js b/cartoons/manifest.js
index c949dde..1e26505 100644
--- a/cartoons/manifest.js
+++ b/cartoons/manifest.js
@@ -191,8 +191,7 @@ window.P24_CARTOONS = [
"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
- }
-];
+ },
{
"id": "conrad-filibuster-qwen25",
"title": "The Filibuster Marathon — Paul Conrad Style (AI Model Contest)",
@@ -207,3 +206,16 @@ window.P24_CARTOONS = [
"model": "qwen25-7b",
"challenge_id": "56406f116d2a"
},
+ {
+ "id": "filibuster-marathon",
+ "title": "The Filibuster Marathon",
+ "file": "20260924-filibuster-marathon.html",
+ "created_at": "2026-09-24T19:15:00Z",
+ "category": "Political Cartoon",
+ "section": "politics",
+ "blurb": "Delegate Holloway holds the floor of the invented Assembly by reading anything within arm's reach — a phone book, a cereal box, furniture instructions, his own bill — while a Dali-style clock melts and the chamber falls asleep standing up.",
+ "thumb": null,
+ "story_id": null,
+ "ai_generated": false
+ }
+];
← 6db3d93 Add Model Arena winner: The Filibuster Marathon (Paul Conrad
·
back to Crazy News Channel
·
Feature Conrad-style Filibuster Marathon ink cartoon on P24 122888f →