← back to Crazy News Channel
cartoons/20260924-tollpoints-diamond-status.html
162 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>The Booth That Became an Airline — P24 Cartoon Desk</title>
<meta name="description" content="An original invented-satire cartoon: Ashtabula's toll bridge loyalty program escalates from gift-card points to a Gold Lounge to its own currency and chartered airline." />
<link rel="stylesheet" href="../assets/style.css" />
</head>
<body data-mood="business" class="cartoon-page">
<header>
<div>
<div class="kicker">P24 Cartoon Desk · Invented Satire · Ashtabula, OH</div>
<h1>The Booth That Became an Airline</h1>
<p class="dek">Ashtabula's toll bridge rewards E-ZPass loyalty with points, gift cards, a Gold Lounge, and eventually its own currency and chartered flight.</p>
</div>
<div class="header-actions">
<a class="back" href="index.html">← Cartoon Desk</a>
</div>
</header>
<main>
<figure>
<div class="phead"><span>TollPoints Loyalty Program, Booth No. 3</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 a toll bridge loyalty-points program escalating out of control"></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, boards, and toll booths are invented. No actual bridge was incorporated. 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 boothStructure(svg){
el('rect', { x: 190, y: 70, width: 100, height: 130, fill: 'var(--panel)', stroke: 'var(--rule)', 'stroke-width': 3 }, svg);
el('rect', { x: 210, y: 90, width: 60, height: 34, fill: 'none', stroke: 'var(--rule)', 'stroke-width': 2.4 }, svg);
txt(svg, 240, 145, 10, '700', 'currentColor', 'BOOTH NO. 3');
el('rect', { x: 60, y: 210, width: 360, height: 12, fill: 'currentColor', opacity: .3, rx: 3 }, svg);
}
function car(svg, cx, cy, s, color){
const g = el('g', {}, svg);
el('rect', { x: cx - s*0.55, y: cy - s*0.18, width: s*1.1, height: s*0.34, rx: s*0.12, fill: color }, g);
el('rect', { x: cx - s*0.28, y: cy - s*0.4, width: s*0.56, height: s*0.28, rx: s*0.08, fill: color }, g);
el('circle', { cx: cx - s*0.32, cy: cy + s*0.16, r: s*0.14, fill: 'currentColor' }, g);
el('circle', { cx: cx + s*0.32, cy: cy + s*0.16, r: s*0.14, fill: 'currentColor' }, g);
return g;
}
function card(svg, cx, cy, w, h, label, sub, color){
const g = el('g', {}, svg);
el('rect', { x: cx - w/2, y: cy - h/2, width: w, height: h, rx: 8, fill: 'var(--panel)', stroke: color, 'stroke-width': 3 }, g);
txt(svg, cx, cy - 4, 11, '800', color, label);
if (sub) txt(svg, cx, cy + 14, 8.5, '400', 'var(--ink-dim)', sub);
return g;
}
function scene(svg, stage){
svg.innerHTML = '';
if (stage === 0) {
boothStructure(svg);
car(svg, 100, 226, 90, '#0f6b52');
card(svg, 380, 100, 150, 62, 'TOLLPOINTS', '1 pt / crossing', 'var(--seal)');
txt(svg, 240, 40, 13, '700', 'currentColor', '"Everybody else has a punch card."');
} else if (stage === 1) {
boothStructure(svg);
car(svg, 90, 226, 80, '#0f6b52');
car(svg, 150, 226, 80, '#6b4fa0');
car(svg, 210, 226, 80, '#b5302f');
card(svg, 380, 90, 150, 60, '+340%', 'repeat crossings', 'var(--accent)');
txt(svg, 240, 40, 12, '700', 'currentColor', 'One Buick: 41 crossings today.');
} else if (stage === 2) {
boothStructure(svg);
for (let i=0;i<4;i++) car(svg, 60 + i*45, 226, 60, i%2===0 ? '#0f6b52' : '#6b4fa0');
txt(svg, 240, 40, 12, '700', 'var(--accent)', 'Idling overnight toward Gold Commuter status.');
card(svg, 380, 90, 150, 46, 'GOLD TIER', '500 crossings', 'var(--seal)');
} else if (stage === 3) {
el('rect', { x: 300, y: 60, width: 150, height: 90, rx: 8, fill: 'var(--panel)', stroke: 'var(--seal)', 'stroke-width': 3 }, svg);
txt(svg, 375, 90, 11, '800', 'var(--seal)', 'GOLD LOUNGE');
txt(svg, 375, 108, 9, '400', 'var(--ink-dim)', 'pretzels + concierge');
official(svg, 375, 135, 18, '#0f6b52', null);
boothStructure(svg);
car(svg, 90, 226, 80, '#0f6b52');
} else if (stage === 4) {
boothStructure(svg);
el('path', { d: 'M 330 60 L 380 40 L 430 60 L 405 68 L 405 90 L 355 90 L 355 68 Z', fill: 'var(--accent2)' }, svg);
txt(svg, 380, 108, 10, '700', 'currentColor', 'TOLLCOIN AIR');
txt(svg, 380, 122, 8.5, '400', 'var(--ink-dim)', 'twice-daily to the waffle house');
card(svg, 150, 90, 150, 46, 'TOLLCOIN', 'its own currency', 'var(--seal)');
car(svg, 90, 226, 80, '#0f6b52');
}
}
const HEADLINES = ["Ashtabula County Toll Bridge Launches Driver Loyalty Points Program", "TollPoints Program Sees Unexpected Surge in Repeat Bridge Crossings", "Drivers Circling Toll Bridge Overnight to Reach 'Gold Commuter' Tier", "Toll Authority Opens Members-Only Lounge Inside Booth No. 3", "Ashtabula Toll Bridge Points Program Now Backed by Its Own Currency, Airline"];
const STAMPS = ["LAUNCHED", "SURGING", "GOLD TIER", "LOUNGE OPEN", "DIAMOND"];
const CONCLUSION_MSG = "The story has concluded. Somewhere, a toll booth still dreams of becoming a country.";
const state = { stage: 0 };
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', CONCLUSION_MSG); 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>