← back to Crazy News Channel Shadowman
cartoons/20260924-casserole-succession.html
166 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>The Casserole Succession — P24 Political Cartoon Desk</title>
<meta name="description" content="An original invented-satire cartoon: Savannah imposes term limits on its ceremonial Bean Queen, the sash won't come off, and a courthouse finally settles it." />
<link rel="stylesheet" href="../assets/style.css" />
</head>
<body data-mood="politics" class="cartoon-page">
<header>
<div>
<div class="kicker">P24 Political Cartoon Desk · Invented Satire · Savannah, GA</div>
<h1>The Casserole Succession</h1>
<p class="dek">Savannah's council is debating whether to impose term limits on the city's ceremonial "Bean Queen" title, a bean-casserole honor that has never had one.</p>
</div>
<div class="header-actions">
<a class="back" href="index.html">← Cartoon Desk</a>
</div>
</header>
<main>
<figure>
<div class="phead"><span>Chatham Co. Ceremonial Registry</span><span id="stamp"></span></div>
<svg class="art" id="art" viewBox="0 0 480 320" role="img" tabindex="0" aria-label="Editorial cartoon about the Bean Queen succession dispute"></svg>
<figcaption id="cap"></figcaption>
</figure>
<div class="dock">
<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 / → advance the story · Enter/Space on the art also advances</p>
</main>
<footer>All officials, courts, and casseroles are invented. No actual bean dish was deposed. 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 txt(svg, x, y, size, weight, fill, content, anchor){
const t = el('text', { x, y, 'text-anchor': anchor||'middle', 'font-family':'-apple-system,sans-serif', 'font-size':size, 'font-weight':weight||'400', fill: fill||'currentColor' }, svg);
t.textContent = content;
return t;
}
function official(svg, cx, cy, r, color, label){
const g = el('g', {}, svg);
el('circle', { cx, cy, r, fill: color, 'fill-opacity': '0.15', stroke: color, 'stroke-width': 3 }, g);
el('circle', { cx, cy: cy - r*0.35, r: r*0.28, fill: color }, g);
el('path', { d: `M ${cx - r*0.5} ${cy + r*0.5} Q ${cx} ${cy + r*0.1} ${cx + r*0.5} ${cy + r*0.5}`, fill: 'none', stroke: color, 'stroke-width': r*0.22, 'stroke-linecap':'round' }, g);
if (label) txt(svg, cx, cy + r + 16, 11, '400', 'var(--ink-dim)', label);
return g;
}
function casserole(svg, cx, cy, s, crowned, sashed){
const g = el('g', {}, svg);
el('ellipse', { cx, cy: cy+s*0.1, rx: s*0.42, ry: s*0.22, fill: '#8a5a2b' }, g);
el('ellipse', { cx, cy: cy+s*0.02, rx: s*0.42, ry: s*0.22, fill: '#c9a227' }, g);
for (let i=0;i<6;i++){
const bx = cx - s*0.3 + i*(s*0.6/5);
el('circle', { cx: bx, cy: cy - s*0.02 + (i%2)*4, r: s*0.045, fill: '#b5302f' }, g);
}
if (sashed) {
el('path', { d: `M ${cx-s*0.34} ${cy-s*0.14} L ${cx+s*0.3} ${cy+s*0.2}`, stroke: '#b5302f', 'stroke-width': 8, opacity: .85 }, g);
txt(svg, cx, cy+s*0.42, 9, '800', '#fff', 'BEAN QUEEN');
el('rect', { x: cx-s*0.24, y: cy+s*0.32, width: s*0.48, height: s*0.14, rx: 3, fill: '#b5302f' }, g);
txt(svg, cx, cy+s*0.42, 9, '800', '#fff', 'BEAN QUEEN');
}
if (crowned) {
const cr = el('g', { transform: `translate(${cx},${cy-s*0.24})` }, g);
el('path', { d: 'M -16 6 L -16 -10 L -6 2 L 0 -14 L 6 2 L 16 -10 L 16 6 Z', fill: 'var(--seal)' }, cr);
}
return g;
}
const HEADLINES = [
"Savannah Council to Debate Term Limits for Ceremonial 'Bean Queen' Title",
"Council Imposes Two-Year Term Limit on Bean Queen After 'Dynasty' Concerns",
"Ousted Bean Queen Refuses to Relinquish Sash, Cites 'Unconstitutional' Term Limit",
'Bean Queen Succession Dispute Reaches Chatham County Courthouse',
'Court Rules Bean Queen Title Passes by Casserole, Not Bloodline, Ending Whitfield Reign'
];
const STAMPS = ['DEBATED', 'LIMITED', 'CONTESTED', 'IN COURT', 'RESOLVED'];
const state = { stage: 0 };
function scene(svg, stage){
svg.innerHTML = '';
if (stage === 0) {
casserole(svg, 150, 200, 200, true, true);
official(svg, 380, 220, 28, '#0f6b52', 'Councilmember Trask');
txt(svg, 150, 300, 12, '700', 'currentColor', 'A ceremonial title, never once term-limited.');
txt(svg, 350, 90, 11, '400', 'var(--ink-dim)', '"How long can a casserole reign?"');
} else if (stage === 1) {
casserole(svg, 200, 200, 190, true, true);
const stamp = el('g', { transform: 'translate(360,140) rotate(-12)' }, svg);
el('circle', { cx: 0, cy: 0, r: 40, fill: 'none', stroke: 'var(--accent2)', 'stroke-width': 5 }, stamp);
txt(svg, 360, 136, 10, '800', 'var(--accent2)', '2-YEAR');
txt(svg, 360, 150, 10, '800', 'var(--accent2)', 'LIMIT');
} else if (stage === 2) {
casserole(svg, 190, 200, 200, true, true);
const bubble = el('g', {}, svg);
el('path', { d: 'M 300 80 h 140 v 56 h -60 l -16 20 v -20 h -64 z', fill: 'var(--panel)', stroke: 'var(--accent)', 'stroke-width': 3 }, bubble);
txt(svg, 370, 102, 10.5, '700', 'var(--accent)', '"UNCONSTITUTIONAL');
txt(svg, 370, 116, 10.5, '700', 'var(--accent)', 'AND IN BAD TASTE."');
} else if (stage === 3) {
el('rect', { x: 320, y: 70, width: 10, height: 200, fill: 'var(--rule)' }, svg);
for (let i=0;i<3;i++){ el('rect', { x: 300+i*14, y: 70, width: 8, height: 200, fill: 'var(--rule)', opacity: .6 - i*0.15 }, svg); }
txt(svg, 400, 100, 10, '700', 'currentColor', 'CHATHAM CO.');
txt(svg, 400, 115, 9, '400', 'var(--ink-dim)', 'COURTHOUSE');
casserole(svg, 160, 220, 190, true, true);
official(svg, 410, 220, 24, '#6b4fa0', 'Presiding Judge');
} else if (stage === 4) {
casserole(svg, 150, 210, 150, false, false);
txt(svg, 150, 290, 10, '400', 'var(--ink-dim)', 'former sash, retired');
casserole(svg, 340, 200, 190, true, true);
const gavel = el('g', { transform: 'translate(240,90) rotate(-18)' }, svg);
el('rect', { x: -5, y: -26, width: 10, height: 38, rx: 3, fill: '#8a5a2b' }, gavel);
el('rect', { x: -18, y: -40, width: 36, height: 16, rx: 3, fill: '#6b5f4d' }, gavel);
txt(svg, 240, 60, 11, '800', 'currentColor', 'RULING: BY CASSEROLE, NOT BLOODLINE');
}
}
function say(kind, msg){ const s = document.getElementById('status'); s.className = kind; s.textContent = msg; }
function paint(){
scene(document.getElementById('art'), state.stage);
document.getElementById('cap').textContent = HEADLINES[state.stage];
document.getElementById('stamp').textContent = STAMPS[state.stage];
const btn = document.getElementById('twistBtn');
if (state.stage >= HEADLINES.length - 1) { btn.textContent = 'Story Concluded'; btn.disabled = true; }
else { btn.textContent = 'Plot Twist!'; btn.disabled = false; }
}
function advance(){
if (state.stage >= HEADLINES.length - 1) { say('info', 'The story has concluded. The sash has been reassigned, by casserole.'); return; }
state.stage++;
paint();
say('ok', `Stage ${state.stage + 1} of ${HEADLINES.length}.`);
}
document.getElementById('twistBtn').addEventListener('click', advance);
const art = document.getElementById('art');
art.addEventListener('click', advance);
art.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); advance(); } });
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(); advance(); }
});
paint();
say('info', 'Case file open. Press N, click the art, or hit Plot Twist to advance the story.');
})();
</script>
</body>
</html>