[object Object]

← back to Crazy News Channel Shadowman

Unify P24 stylesheet: shared external CSS across front page, cartoon gallery, and all 18 strip pages

648126984a2a91afa15b3d59a19216c6e0c0c803 · 2026-09-24 13:04:18 -0700 · Steve Abrams

Extracts the front page's 818-line inline <style> into assets/style.css
(Part 1, verbatim) and links it from index.html, cartoons/index.html
(Part 2: gallery-only grid/card/controls rules, deduped against Part 1),
and all 18 cartoon strip pages (Part 3), replacing three drifting inline
stylesheets with one shared file — still pure file:// static HTML, no
server/build step.

The 18 strips previously hand-rolled a separate "parchment newspaper"
serif theme (--ink/--ink-dim/--rule/--seal, Georgia font, a per-file
light/dark toggle). Rather than rewrite each strip's SVG-drawing JS
(several read these var() names directly at render time), the legacy
names are aliased onto the shared mint-green token system on the `body`
selector (not `:root` — aliasing at :root freezes the substitution using
html's own default-mood value instead of re-resolving per body[data-mood],
verified by isolated repro) so every strip re-themes automatically,
including a `data-mood="<category>"` attribute per strip matching its
manifest.js section (13 politics, 1 each business/entertainment/scitech/
sports/weather).

Also found and fixed: 17 of 18 strips explicitly set data-theme="light"
on load by default (only one has real 3-state auto/dark/light logic), so
a literal parchment-color override on data-theme="light" was permanently
masking every mood palette. Light is now a no-op (mood palette shows by
default); the toggle's "dark" state layers a night-viewing neutral on
top of whichever mood is active, verified working via Playwright click.

Verified with Chrome headless screenshots + Playwright (front page,
gallery, and 3 varied strips: politics 3-panel, scitech, weather) —
correct per-category theming, legible contrast, 0 console/page errors,
click-advance SVG interaction intact, dark-mode toggle intact.

Out of scope (left untouched, in-flight from other concurrent agents):
cartoons/manifest.js, cartoons/reference/*, and the newer cartoon files
not yet in the 18-file manifest (filibuster-marathon, come-in-and-get-me-
meter, conrad-filibuster-ink, gold-tier-treadmill, row-by-row-marshal) —
those will need the same link+data-mood treatment once their owning
tickets land.

TK-12151

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V29xaoBwcFwJyR4KC43ptn

Files touched

Diff

commit 648126984a2a91afa15b3d59a19216c6e0c0c803
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 24 13:04:18 2026 -0700

    Unify P24 stylesheet: shared external CSS across front page, cartoon gallery, and all 18 strip pages
    
    Extracts the front page's 818-line inline <style> into assets/style.css
    (Part 1, verbatim) and links it from index.html, cartoons/index.html
    (Part 2: gallery-only grid/card/controls rules, deduped against Part 1),
    and all 18 cartoon strip pages (Part 3), replacing three drifting inline
    stylesheets with one shared file — still pure file:// static HTML, no
    server/build step.
    
    The 18 strips previously hand-rolled a separate "parchment newspaper"
    serif theme (--ink/--ink-dim/--rule/--seal, Georgia font, a per-file
    light/dark toggle). Rather than rewrite each strip's SVG-drawing JS
    (several read these var() names directly at render time), the legacy
    names are aliased onto the shared mint-green token system on the `body`
    selector (not `:root` — aliasing at :root freezes the substitution using
    html's own default-mood value instead of re-resolving per body[data-mood],
    verified by isolated repro) so every strip re-themes automatically,
    including a `data-mood="<category>"` attribute per strip matching its
    manifest.js section (13 politics, 1 each business/entertainment/scitech/
    sports/weather).
    
    Also found and fixed: 17 of 18 strips explicitly set data-theme="light"
    on load by default (only one has real 3-state auto/dark/light logic), so
    a literal parchment-color override on data-theme="light" was permanently
    masking every mood palette. Light is now a no-op (mood palette shows by
    default); the toggle's "dark" state layers a night-viewing neutral on
    top of whichever mood is active, verified working via Playwright click.
    
    Verified with Chrome headless screenshots + Playwright (front page,
    gallery, and 3 varied strips: politics 3-panel, scitech, weather) —
    correct per-category theming, legible contrast, 0 console/page errors,
    click-advance SVG interaction intact, dark-mode toggle intact.
    
    Out of scope (left untouched, in-flight from other concurrent agents):
    cartoons/manifest.js, cartoons/reference/*, and the newer cartoon files
    not yet in the 18-file manifest (filibuster-marathon, come-in-and-get-me-
    meter, conrad-filibuster-ink, gold-tier-treadmill, row-by-row-marshal) —
    those will need the same link+data-mood treatment once their owning
    tickets land.
    
    TK-12151
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01V29xaoBwcFwJyR4KC43ptn
---
 assets/style.css                                   | 963 +++++++++++++++++++++
 cartoons/20260924-budget-pie-fight.html            |  23 +-
 cartoons/20260924-casserole-succession.html        |  23 +-
 cartoons/20260924-commission-turtles.html          |  23 +-
 cartoons/20260924-conrad-filibuster-qwen25-7b.html |  36 +-
 cartoons/20260924-filibuster-vs-cloture.html       |  23 +-
 cartoons/20260924-four-minutes-ahead.html          |  23 +-
 cartoons/20260924-gnome-who-wouldnt-hush.html      |  23 +-
 cartoons/20260924-hedge-non-aggression-pact.html   |  23 +-
 cartoons/20260924-jackalope-custody-battle.html    |  23 +-
 cartoons/20260924-lobbyist-revolving-door.html     |  23 +-
 cartoons/20260924-meter-that-wouldnt-yield.html    |  23 +-
 cartoons/20260924-pork-barrel-train.html           |  23 +-
 cartoons/20260924-quorum-call-hide-seek.html       |  23 +-
 cartoons/20260924-row-by-row-only.html             |  23 +-
 .../20260924-the-corner-with-its-own-weather.html  |  23 +-
 .../20260924-the-zipper-that-ended-an-inning.html  |  23 +-
 cartoons/20260924-tollpoints-diamond-status.html   |  23 +-
 cartoons/20260924-tuber-bank-of-chugwater.html     |  23 +-
 cartoons/index.html                                | 165 +---
 index.html                                         | 811 +----------------
 21 files changed, 1024 insertions(+), 1342 deletions(-)

diff --git a/assets/style.css b/assets/style.css
new file mode 100644
index 0000000..40a01f6
--- /dev/null
+++ b/assets/style.css
@@ -0,0 +1,963 @@
+/* ============================================================
+   PANDEMONIUM-24 — shared stylesheet
+   Extracted from index.html (TK-12151) so the front page, the
+   cartoon gallery (cartoons/index.html), and all cartoon strip
+   pages (cartoons/2026*.html) share ONE mint-green/sans-serif
+   design system with per-category data-mood theming, instead of
+   copy-pasted/drifting inline <style> blocks. Pure CSS, no
+   fetch()/build step — safe for offline file:// use.
+
+   PART 1 — site-wide (verbatim from the former index.html inline
+            stylesheet: reset, mood palettes, chrome, story grid,
+            ticker, admin panel, article view, chaos index, etc.)
+   PART 2 — cartoon gallery (cartoons/index.html) additions
+   PART 3 — cartoon strip pages: legacy token aliases + the
+            per-file light/dark toggle, layered on top of Part 1
+   ============================================================ */
+
+/* ============================================================
+   PANDEMONIUM-24 — styles
+   Sections: 1. Reset & base  2. Mood theming (CSS custom props)
+   3. Chrome (header/nav)     4. Breaking klaxon overlay
+   5. Story grid & cards      6. Ticker (motion + reduced-motion)
+   7. Empty / completion states  8. Focus & a11y helpers
+   9. Responsive breakpoints  10. Reduced-motion overrides
+   ============================================================ */
+
+/* ---------- 1. Reset & base ---------- */
+*, *::before, *::after { box-sizing: border-box; }
+html, body { margin: 0; padding: 0; }
+/* overflow-x on body alone is not enough: with html's overflow unset, the
+   UA propagates body's overflow to the viewport's scroller, but
+   document.documentElement.scrollWidth can still report the wider,
+   unclipped value (verified empirically). Setting it on html too stops the
+   propagation and clips scrollWidth for real, everywhere. */
+html { overflow-x: hidden; }
+/* The [hidden] attribute must always win over any class-based `display`
+   rule below (several elements here are toggled via `.hidden = true/false`
+   in JS while also carrying a class that sets display:flex/grid). Without
+   this, equal-specificity cascade order lets the class rule beat the UA
+   [hidden] stylesheet and the element stays visible. */
+[hidden] { display: none !important; }
+body {
+  min-height: 100vh;
+  font-family: -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
+  color: var(--text);
+  background: radial-gradient(circle at 20% -10%, var(--bg2), var(--bg) 60%);
+  transition: background-color .5s ease, color .5s ease;
+  line-height: 1.4;
+  /* reserve room for the fixed chyron ticker so it never overlaps content */
+  padding-bottom: 3rem;
+  /* Safety net: the chaos-tier-3 TV-bezel tilt rotates a very tall element
+     (main#channelContainer), and rotating a tall box even a fraction of a
+     degree widens its visual bounding box (width*cos + height*sin) beyond
+     the viewport — confirmed via CSSOM bisection to be the sole cause of a
+     real horizontal scrollbar at 390-1024px at max chaos. overflow-x:hidden
+     here clips that leak (vertical/position:sticky is untouched) without
+     changing the tilt's visible effect or any layout. */
+  overflow-x: hidden;
+}
+h1, h2, h3, p { margin: 0; }
+button { font-family: inherit; }
+img, svg { max-width: 100%; display: block; }
+
+.visually-hidden {
+  position: absolute !important;
+  width: 1px; height: 1px;
+  padding: 0; margin: -1px;
+  overflow: hidden;
+  clip: rect(0 0 0 0);
+  white-space: nowrap;
+  border: 0;
+}
+
+/* ---------- 2. Mood theming — CSS custom properties ----------
+   Default ("all") is the pale mint/green salvaged from the original
+   seed attempt. Every other category swaps the whole palette. */
+:root, body[data-mood="all"] {
+  --bg: #cdf5e0;
+  --bg2: #f2fff2;
+  --panel: #ffffff;
+  --panel-border: #0f9d7833;
+  --accent: #0f9d78;
+  --accent2: #056659;
+  --text: #123524;
+  --text-dim: #12352499;
+  --chrome: #ffffff;
+  --focus: #0b3d91;
+  --mood-label: "Calm-ish (Default)";
+}
+body[data-mood="politics"] {
+  --bg: #2a0b0b; --bg2: #4a0f0f; --panel: #3a1212; --panel-border: #ff3b3b55;
+  --accent: #ff3b3b; --accent2: #ffb703; --text: #ffe9e9; --text-dim: #ffd7d7aa;
+  --chrome: #220808; --focus: #ffd166;
+}
+body[data-mood="weather"] {
+  --bg: #071a33; --bg2: #0c2b52; --panel: #0e2340; --panel-border: #4fd1ff55;
+  --accent: #4fd1ff; --accent2: #a0e9ff; --text: #eaf6ff; --text-dim: #cfeeffaa;
+  --chrome: #05132c; --focus: #ffe066;
+}
+body[data-mood="sports"] {
+  --bg: #2b1400; --bg2: #4a2200; --panel: #3a1c00; --panel-border: #ff980055;
+  --accent: #ff9800; --accent2: #ffd166; --text: #fff3e0; --text-dim: #ffe0b3aa;
+  --chrome: #200f00; --focus: #4fd1ff;
+}
+body[data-mood="entertainment"] {
+  --bg: #2a0a2e; --bg2: #4a0f52; --panel: #3a1140; --panel-border: #ff5fd155;
+  --accent: #ff5fd1; --accent2: #b388ff; --text: #fbe9ff; --text-dim: #eecbffaa;
+  --chrome: #210822; --focus: #ffe066;
+}
+body[data-mood="business"] {
+  --bg: #08210f; --bg2: #0e3a1c; --panel: #0d2c16; --panel-border: #2ecc7155;
+  --accent: #2ecc71; --accent2: #f1c40f; --text: #eafff0; --text-dim: #c9f5d6aa;
+  --chrome: #06180c; --focus: #f1c40f;
+}
+body[data-mood="scitech"] {
+  --bg: #0a0a23; --bg2: #170b3d; --panel: #140c33; --panel-border: #00f0ff55;
+  --accent: #00f0ff; --accent2: #ff00e6; --text: #eafcff; --text-dim: #c9f3ffaa;
+  --chrome: #08081c; --focus: #ff00e6;
+}
+body[data-mood="uncategorized"] {
+  --bg: #e9edf0; --bg2: #ffffff; --panel: #ffffff; --panel-border: #54657833;
+  --accent: #546578; --accent2: #2f3b47; --text: #20272d; --text-dim: #20272d99;
+  --chrome: #ffffff; --focus: #0b3d91;
+}
+
+/* ---------- 3. Chrome (header / nav) ---------- */
+.chrome {
+  position: sticky; top: 0; z-index: 20;
+  display: flex; flex-wrap: wrap; align-items: center; gap: .75rem 1.25rem;
+  padding: .75rem 1rem;
+  background: var(--chrome);
+  /* No backdrop-filter: combined with position:sticky it caused WebKit/Safari
+     to ghost/bleed scrolled-past content through the header during scroll
+     (CTA-verified, cta/webkit-bug-repro/). --chrome is now fully opaque so
+     no blur was doing visible work anyway. translateZ forces a stable
+     compositing layer as cheap insurance against the same class of bug. */
+  transform: translateZ(0);
+  border-bottom: 2px solid var(--panel-border);
+}
+.brand { display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; color: inherit; text-decoration: none; }
+.brand:hover .wordmark { text-decoration: underline; }
+.logo-badge {
+  display: inline-flex; align-items: center; justify-content: center;
+  width: 2.4rem; height: 2.4rem; border-radius: .4rem;
+  background: var(--accent); color: var(--bg);
+  font-weight: 900; font-size: .95rem; letter-spacing: -1px;
+  box-shadow: 0 0 0 2px var(--accent2);
+}
+.wordmark { font-size: clamp(1.1rem, 1.5vw + .8rem, 1.6rem); font-weight: 800; letter-spacing: .5px; }
+.tagline { font-size: .72rem; color: var(--text-dim); font-style: italic; }
+
+.live-badge {
+  display: inline-flex; align-items: center; gap: .4rem;
+  font-weight: 700; font-size: .8rem; letter-spacing: .5px;
+  padding: .3rem .6rem; border-radius: 999px;
+  border: 1px solid var(--panel-border);
+  background: color-mix(in srgb, var(--panel) 70%, transparent);
+}
+.rec-dot {
+  width: .55rem; height: .55rem; border-radius: 50%;
+  background: #ff3b3b;
+  box-shadow: 0 0 6px 1px #ff3b3b;
+  animation: rec-blink 1.4s steps(1) infinite;
+}
+@keyframes rec-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: .25; } }
+
+.controls { display: flex; align-items: center; gap: .75rem; margin-left: auto; flex-wrap: wrap; }
+
+.klaxon-btn {
+  cursor: pointer;
+  border: 2px solid var(--accent2);
+  background: var(--panel);
+  color: var(--text);
+  font-weight: 800;
+  font-size: .85rem;
+  padding: .55rem .9rem;
+  border-radius: .5rem;
+  letter-spacing: .3px;
+  transition: transform .15s ease, background-color .2s ease, color .2s ease;
+}
+.klaxon-btn:hover { transform: translateY(-1px); }
+.klaxon-btn[aria-pressed="true"] {
+  background: #ff2222; border-color: #ffd166; color: #fff;
+  animation: klaxon-btn-pulse 1s ease-in-out infinite;
+}
+@keyframes klaxon-btn-pulse {
+  0%, 100% { box-shadow: 0 0 0 0 #ff222266; }
+  50% { box-shadow: 0 0 0 8px #ff222200; }
+}
+
+.sound-toggle { display: inline-flex; align-items: center; gap: .4rem; font-size: .78rem; color: var(--text-dim); cursor: pointer; }
+.sound-toggle input { width: 1rem; height: 1rem; accent-color: var(--accent); cursor: pointer; }
+.audio-note { font-size: .7rem; color: var(--text-dim); max-width: 16rem; }
+
+/* Admin toggle — deliberately NOT themed by mood vars: fixed slate/mono
+   "control room" look so it never reads as part of the in-universe broadcast. */
+.admin-toggle {
+  cursor: pointer;
+  border: 1.5px dashed #6b7280;
+  background: #20242b; color: #d7dee5;
+  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
+  font-size: .72rem; font-weight: 700; letter-spacing: .3px;
+  padding: .5rem .75rem; border-radius: .4rem;
+  transition: background-color .15s ease, border-color .15s ease, color .15s ease;
+}
+.admin-toggle:hover { background: #2a2f37; }
+.admin-toggle[aria-expanded="true"] { background: #343a40; border-style: solid; border-color: #7dd3fc; color: #7dd3fc; }
+
+/* ---------- category filter (nav) ---------- */
+.category-filter { padding: .6rem 1rem; border-bottom: 1px solid var(--panel-border); }
+.category-back-link { display: inline-block; font-size: .8rem; font-weight: 700; color: var(--accent2); margin-bottom: .3rem; text-decoration: none; }
+.category-back-link:hover { text-decoration: underline; }
+.category-blurb { color: var(--text-dim); font-size: .88rem; margin-top: .2rem; }
+.category-filter fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
+.chip-row {
+  display: flex; gap: .5rem; overflow-x: auto; padding-bottom: .25rem;
+  scrollbar-width: thin; min-width: 0;
+}
+.chip-row::-webkit-scrollbar { height: 6px; }
+.chip-row::-webkit-scrollbar-thumb { background: var(--panel-border); border-radius: 4px; }
+
+.chip-item { position: relative; flex: 0 0 auto; }
+.chip-item input { position: absolute; opacity: 0; width: 1px; height: 1px; }
+.chip-item label {
+  display: inline-block;
+  padding: .45rem .9rem;
+  border-radius: 999px;
+  border: 1.5px solid var(--panel-border);
+  background: var(--panel);
+  color: var(--text);
+  font-size: .8rem; font-weight: 700; letter-spacing: .2px;
+  cursor: pointer; white-space: nowrap;
+  transition: background-color .2s ease, color .2s ease, transform .12s ease;
+}
+.chip-item label:hover { transform: translateY(-1px); }
+.chip-item input:checked + label {
+  background: var(--accent); color: var(--bg); border-color: var(--accent2);
+}
+.chip-item input:focus-visible + label {
+  outline: 3px solid var(--focus); outline-offset: 2px;
+}
+
+/* ---------- 4. Breaking klaxon overlay ---------- */
+.breaking-banner {
+  position: relative;
+  margin: 0 1rem; margin-top: .75rem;
+  padding: .75rem 1rem;
+  background: linear-gradient(90deg, #b30000, #ff3b3b, #b30000);
+  background-size: 200% 100%;
+  color: #fff;
+  font-weight: 900;
+  border-radius: .4rem;
+  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
+  border: 2px solid #ffd166;
+  box-shadow: 0 0 24px 2px #ff000055;
+  animation: breaking-sweep 1.4s linear infinite, breaking-flash 0.6s ease-in-out infinite;
+}
+@keyframes breaking-sweep { 0% { background-position: 0% 0; } 100% { background-position: 200% 0; } }
+@keyframes breaking-flash { 0%, 100% { filter: brightness(1); } 50% { filter: brightness(1.35); } }
+.breaking-banner .bb-icon { font-size: 1.2rem; }
+.breaking-banner .bb-sub { font-weight: 600; font-size: .82rem; opacity: .95; }
+
+/* full-screen edge vignette while breaking is active */
+body.breaking-mode::before {
+  content: "";
+  position: fixed; inset: 0; z-index: 15; pointer-events: none;
+  box-shadow: inset 0 0 0 6px #ff2222, inset 0 0 60px 10px #ff222266;
+  animation: vignette-pulse 1s ease-in-out infinite;
+}
+@keyframes vignette-pulse { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }
+
+/* ---------- 5. Story grid & cards ---------- */
+main#channelContainer {
+  position: relative;
+  max-width: 1200px;
+  margin: 1rem auto 0;
+  padding: 1rem 1rem 1.5rem;
+  border-radius: .75rem;
+  background: color-mix(in srgb, var(--panel) 55%, transparent);
+  box-shadow: inset 0 0 0 1px var(--panel-border), inset 0 40px 80px -40px #00000055;
+  overflow: hidden;
+}
+/* subtle static CRT scanline texture — no motion, safe under reduced-motion */
+main#channelContainer::after {
+  content: "";
+  position: absolute; inset: 0; pointer-events: none;
+  background: repeating-linear-gradient(
+    to bottom, rgba(255,255,255,0.035) 0px, rgba(255,255,255,0.035) 1px,
+    transparent 2px, transparent 4px
+  );
+  mix-blend-mode: overlay;
+}
+
+.channel-meta { display: flex; justify-content: space-between; align-items: baseline; gap: .5rem; flex-wrap: wrap; margin-bottom: .75rem; }
+.channel-meta h2 { font-size: .95rem; letter-spacing: 1px; text-transform: uppercase; color: var(--accent); }
+.mood-readout { font-size: .75rem; color: var(--text-dim); }
+
+.story-grid {
+  display: grid;
+  grid-template-columns: 1fr;
+  gap: 1rem;
+}
+.story-card {
+  position: relative;
+  background: var(--panel);
+  border: 1px solid var(--panel-border);
+  border-radius: .6rem;
+  padding: 1rem;
+  display: flex; flex-direction: column; gap: .55rem;
+  box-shadow: 0 4px 18px -10px #00000066;
+}
+.story-photo {
+  display: block; width: calc(100% + 2rem); max-width: none; height: auto;
+  aspect-ratio: 800 / 457; object-fit: cover;
+  margin: -1rem -1rem .2rem; border-radius: .6rem .6rem 0 0;
+  background: var(--panel-border);
+}
+.story-card.concluded .story-photo { filter: grayscale(.6); }
+.article-photo img { width: 100%; height: auto; border-radius: .5rem; display: block; }
+/* Cartoon-thumb base style (card-view / "All Categories" default — the
+   compact inline-link treatment for newspaper-mode overrides this above). A
+   politics story with a linked cartoon shows this INSTEAD OF a photo, same
+   banner slot, so it never looks like a missing image. */
+.story-cartoon-thumb {
+  display: flex; align-items: center; gap: .6rem;
+  width: calc(100% + 2rem); margin: -1rem -1rem .2rem; padding: 1.1rem 1rem;
+  border-radius: .6rem .6rem 0 0;
+  background: color-mix(in srgb, var(--accent) 12%, var(--panel));
+  border-bottom: 1px solid var(--panel-border);
+  text-decoration: none; color: var(--accent2);
+  font-weight: 700; font-size: .95rem;
+}
+.story-cartoon-thumb:hover, .story-cartoon-thumb:focus-visible { background: color-mix(in srgb, var(--accent) 20%, var(--panel)); }
+.story-cartoon-thumb .ct-icon { font-size: 1.3rem; }
+.story-card.concluded .story-cartoon-thumb { filter: grayscale(.5); }
+.story-card.lead { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent), 0 8px 24px -8px #00000077; }
+.story-card .kicker { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
+.badge-cat {
+  font-size: .68rem; font-weight: 800; letter-spacing: .5px; text-transform: uppercase;
+  padding: .18rem .5rem; border-radius: 999px; background: var(--accent); color: var(--bg);
+}
+.badge-lead {
+  font-size: .68rem; font-weight: 800; letter-spacing: .5px; text-transform: uppercase;
+  padding: .18rem .5rem; border-radius: 999px; background: var(--accent2); color: #fff;
+}
+.story-card h3 { font-size: clamp(1rem, .4vw + 1rem, 1.35rem); line-height: 1.25; }
+.story-card.lead h3 { font-size: clamp(1.25rem, 1.2vw + 1rem, 1.9rem); }
+.story-detail { font-size: .9rem; color: var(--text-dim); }
+.story-meta { font-size: .74rem; color: var(--text-dim); display: flex; gap: .5rem; flex-wrap: wrap; }
+
+.escalation {
+  display: flex; align-items: center; gap: .5rem; margin-top: .2rem;
+}
+.escalation .level-label { font-size: .72rem; font-weight: 800; letter-spacing: .5px; text-transform: uppercase; }
+.escalation .bar { display: flex; gap: 3px; }
+.escalation .seg { width: 1.1rem; height: .4rem; border-radius: 2px; background: var(--panel-border); }
+.escalation .seg.on { background: var(--accent); }
+.story-card.concluded { opacity: .96; }
+.concluded-ribbon {
+  align-self: flex-start;
+  font-size: .7rem; font-weight: 800; letter-spacing: .5px; text-transform: uppercase;
+  padding: .2rem .55rem; border-radius: .3rem;
+  background: var(--text); color: var(--bg);
+}
+
+/* ---------- 5b. Category-view "Newspaper Column" (V4) ----------
+   Applied only when state.category !== "all" (a specific category route,
+   e.g. #/category/scitech) via .story-grid.newspaper-mode, toggled in
+   renderStories(). Restyles the SAME storyCardHTML() markup used by the "All
+   Categories" card stack — no markup fork, so patchStoryGrid()'s keyed morph
+   reconcile never has to bridge a shape change from this toggle alone. True
+   CSS multi-column flow (column-count), inline category label instead of the
+   badge pill, headline-only density (detail paragraph hidden), a left rail
+   whose height reads escalation, and a compact photo/cartoon thumbnail. */
+.np-cat-label { display: none; }
+.story-grid.newspaper-mode {
+  display: block;
+  column-gap: 1.5rem;
+  column-count: 1;
+}
+@media (min-width: 640px) { .story-grid.newspaper-mode { column-count: 2; } }
+@media (min-width: 1024px) { .story-grid.newspaper-mode { column-count: 3; } }
+@media (min-width: 1440px) { .story-grid.newspaper-mode { column-count: 4; } }
+.story-grid.newspaper-mode .story-card {
+  break-inside: avoid;
+  display: block;
+  margin: 0 0 .9rem;
+  padding: 0 0 .1rem .7rem;
+  border: 0;
+  border-left: 3px solid var(--panel-border);
+  border-radius: 0;
+  box-shadow: none;
+}
+.story-grid.newspaper-mode .story-card.lead { border-left-color: var(--accent); border-left-width: 4px; }
+.story-grid.newspaper-mode .story-card.concluded { border-left-color: var(--text-dim); }
+.story-grid.newspaper-mode .story-card .kicker { display: none; }
+.story-grid.newspaper-mode .story-card .np-cat-label {
+  display: block;
+  font: 800 .62rem/1 -apple-system, sans-serif;
+  text-transform: uppercase; letter-spacing: .05em;
+  color: var(--accent); margin-bottom: .2rem;
+}
+.story-grid.newspaper-mode .story-card.concluded .np-cat-label { color: var(--text-dim); }
+.story-grid.newspaper-mode .story-card h3 {
+  font-family: Georgia, "Times New Roman", serif;
+  font-size: 1rem; line-height: 1.22; margin-bottom: .25rem;
+}
+.story-grid.newspaper-mode .story-card.lead h3 { font-size: 1.3rem; font-style: italic; }
+.story-grid.newspaper-mode .story-card .story-detail,
+.story-grid.newspaper-mode .story-card .story-read-more { display: none; }
+.story-grid.newspaper-mode .story-card .escalation { margin: .3rem 0 0; gap: .35rem; }
+.story-grid.newspaper-mode .story-card .escalation .level-label { display: none; }
+.story-grid.newspaper-mode .story-card .escalation .seg { width: .55rem; height: .18rem; }
+.story-grid.newspaper-mode .story-card .story-meta {
+  font: 500 .66rem/1.3 ui-monospace, Menlo, monospace;
+  color: var(--text-dim); gap: .45rem; margin-top: .25rem;
+}
+.story-grid.newspaper-mode .story-card .story-meta span:last-child { display: none; } /* drop the "Updated HH:MM" clause — density */
+.story-grid.newspaper-mode .story-card .story-photo {
+  float: left; width: 56px; height: 56px; margin: 0 .55rem .3rem 0;
+  border-radius: .3rem; aspect-ratio: 1/1; object-fit: cover;
+}
+.story-grid.newspaper-mode .story-card .story-cartoon-thumb {
+  display: flex; align-items: center; gap: .4rem;
+  font: 700 .7rem/1.2 -apple-system, sans-serif;
+  color: var(--accent2); text-decoration: none;
+  border: 1px dashed var(--panel-border); border-radius: .35rem;
+  padding: .3rem .5rem; margin-bottom: .3rem;
+}
+.story-grid.newspaper-mode .story-card .story-cartoon-thumb:hover,
+.story-grid.newspaper-mode .story-card .story-cartoon-thumb:focus-visible { border-color: var(--accent); }
+.story-grid.newspaper-mode .story-card .ct-icon { font-size: .95rem; }
+
+/* ---------- 6. Ticker ---------- */
+.ticker-wrap {
+  position: fixed; left: 0; right: 0; bottom: 0; z-index: 20;
+  display: flex; align-items: stretch;
+  background: var(--chrome);
+  border-top: 2px solid var(--panel-border);
+  height: 3rem;
+}
+body.breaking-mode .ticker-wrap { border-top-color: #ff3b3b; }
+.ticker-label {
+  flex: 0 0 auto;
+  display: flex; align-items: center;
+  padding: 0 .8rem;
+  font-weight: 900; font-size: .78rem; letter-spacing: .5px;
+  background: var(--accent); color: var(--bg);
+}
+body.breaking-mode .ticker-label { background: #ff2222; color: #fff; }
+.ticker-scroll { position: relative; overflow: hidden; flex: 1 1 auto; min-width: 0; display: flex; align-items: center; }
+.ticker-track {
+  display: inline-flex; white-space: nowrap; gap: 3rem; padding-right: 3rem;
+  will-change: transform;
+  animation: ticker-scroll 38s linear infinite;
+  font-size: .85rem; font-weight: 600;
+}
+body.breaking-mode .ticker-track { color: #ff5959; font-weight: 800; }
+@keyframes ticker-scroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
+.ticker-static {
+  flex: 1 1 auto; min-width: 0; display: flex; align-items: center; padding: 0 .9rem;
+  font-size: .85rem; font-weight: 700;
+  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
+}
+
+/* ---------- 7. Empty / completion states ---------- */
+.empty-state, .meltdown-banner {
+  text-align: center; padding: 2.5rem 1rem; border: 2px dashed var(--panel-border);
+  border-radius: .6rem; color: var(--text-dim);
+}
+.empty-state .es-emoji, .meltdown-banner .mb-emoji { font-size: 2rem; display: block; margin-bottom: .5rem; }
+.empty-state h3, .meltdown-banner h3 { color: var(--text); margin-bottom: .35rem; }
+.meltdown-banner {
+  margin-top: 1rem; border-color: var(--accent); border-style: solid;
+  background: color-mix(in srgb, var(--accent) 12%, var(--panel));
+}
+.story-card.just-added { outline: 3px dashed var(--accent); outline-offset: 2px; }
+
+/* ---------- 7b. Admin panel (backstage / control-room, not on-air) ----------
+   Deliberately fixed slate/mono palette independent of --mood vars so it
+   never gets confused with the in-universe broadcast chrome. Renders in
+   normal document flow (an expand/collapse section, not an overlay) so it
+   can never overlap content or fight the ticker/vignette z-index stack. */
+.admin-panel {
+  max-width: 1200px;
+  margin: 1rem auto 0;
+  padding: 1rem 1.25rem 1.25rem;
+  border-radius: .6rem;
+  background: #1b1f24;
+  color: #e8eaed;
+  border: 2px dashed #4a5057;
+  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
+}
+.admin-panel h2 { font-size: .95rem; letter-spacing: .5px; text-transform: uppercase; color: #7dd3fc; margin-bottom: .15rem; }
+.admin-panel h3 { font-size: .78rem; letter-spacing: .4px; text-transform: uppercase; color: #b8c0c8; margin-bottom: .5rem; }
+.admin-hint { font-size: .72rem; color: #9aa4ad; margin-bottom: 1rem; font-style: italic; }
+.admin-panel hr { border: none; border-top: 1px dashed #3a4048; margin: 1.1rem 0; }
+
+.admin-form { display: grid; gap: .5rem; }
+.admin-form label { font-size: .7rem; text-transform: uppercase; letter-spacing: .4px; color: #b8c0c8; }
+.admin-form input[type="text"], .admin-form select {
+  width: 100%; padding: .5rem .6rem; border-radius: .35rem;
+  border: 1.5px solid #4a5057; background: #262b31; color: #f2f4f6;
+  font-family: inherit; font-size: .85rem;
+}
+.admin-form input[aria-invalid="true"] { border-color: #ff6b6b; }
+.admin-form input[type="text"]:focus-visible,
+.admin-form select:focus-visible,
+.admin-panel button:focus-visible {
+  outline: 3px solid #7dd3fc; outline-offset: 2px;
+}
+.admin-error { font-size: .78rem; color: #ff8b8b; font-weight: 700; }
+.admin-success { font-size: .78rem; color: #7dffb0; font-weight: 700; }
+.admin-submit-btn {
+  justify-self: start; margin-top: .2rem;
+  cursor: pointer; border: none; border-radius: .35rem;
+  padding: .55rem 1.1rem; font-weight: 800; font-size: .82rem;
+  background: #7dd3fc; color: #0b1a24;
+}
+.admin-reset-btn {
+  justify-self: start; margin-top: 1rem;
+  cursor: pointer; border: 1.5px solid #4a5057; border-radius: .35rem;
+  padding: .5rem 1rem; font-weight: 700; font-size: .78rem;
+  background: transparent; color: #e8eaed;
+}
+.admin-reset-btn:hover { background: #262b31; }
+
+.admin-refresh-row { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; margin-top: .6rem; }
+.admin-refresh-btn {
+  justify-self: start;
+  cursor: pointer; border: 1.5px solid #7dd3fc; border-radius: .35rem;
+  padding: .5rem 1rem; font-weight: 700; font-size: .78rem;
+  background: transparent; color: #7dd3fc;
+}
+.admin-refresh-btn:hover { background: #7dd3fc22; }
+.admin-refresh-btn:disabled { opacity: .55; cursor: wait; }
+.admin-refresh-status { font-size: .75rem; color: #9aa4ad; }
+.admin-refresh-status.is-error { color: #ff8b8b; }
+.admin-refresh-status.is-ok { color: #8ce99a; }
+
+.admin-story-list { list-style: none; margin: 0 0 .5rem; padding: 0; display: grid; gap: .5rem; max-height: 16rem; overflow-y: auto; }
+.admin-story-row {
+  /* min-width: 0 overrides the browser's implicit min-width:auto on grid
+     items — without it this row (and its nowrap/ellipsis child text) won't
+     shrink below its content's min-content width, pushing the Delete
+     button off-screen at narrow viewports (the same class of bug as the
+     ticker/fieldset overflow fixed earlier in this project). */
+  min-width: 0;
+  display: flex; align-items: center; justify-content: space-between; gap: .75rem;
+  padding: .5rem .6rem; border: 1px solid #343a40; border-radius: .4rem; background: #22262b;
+}
+.admin-story-info { flex: 1 1 auto; display: flex; flex-direction: column; gap: .15rem; min-width: 0; }
+.admin-story-cat { font-size: .62rem; text-transform: uppercase; letter-spacing: .4px; color: #7dd3fc; }
+.admin-story-headline { font-size: .8rem; color: #e8eaed; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
+.admin-delete-btn {
+  flex: 0 0 auto; cursor: pointer; border: 1.5px solid #ff6b6b; background: transparent;
+  color: #ff8b8b; border-radius: .35rem; padding: .35rem .6rem; font-weight: 700; font-size: .72rem;
+}
+.admin-delete-btn:hover { background: #ff6b6b22; }
+.admin-empty { font-size: .8rem; color: #9aa4ad; padding: .5rem 0; }
+@media (min-width: 768px) {
+  .admin-panel-body { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; align-items: start; }
+}
+.admin-form textarea {
+  width: 100%; padding: .5rem .6rem; border-radius: .35rem;
+  border: 1.5px solid #4a5057; background: #262b31; color: #f2f4f6;
+  font-family: inherit; font-size: .85rem; resize: vertical; min-height: 6rem;
+}
+.admin-form-hint { font-size: .68rem; color: #9aa4ad; margin-top: -.25rem; }
+
+/* ---------- 7c. Feature articles ("More From PANDEMONIUM-24") ---------- */
+.features-section {
+  max-width: 1200px;
+  margin: 1.25rem auto 0;
+  padding: 1rem 1rem 1.5rem;
+}
+.features-grid { display: grid; grid-template-columns: 1fr; gap: 1rem; }
+.feature-card {
+  background: var(--panel);
+  border: 1px solid var(--panel-border);
+  border-radius: .6rem;
+  padding: 1rem;
+  display: flex; flex-direction: column; gap: .5rem;
+  box-shadow: 0 4px 18px -10px #00000066;
+}
+.feature-card h3 { font-size: 1.05rem; line-height: 1.3; }
+.features-empty-note { color: var(--text-dim); font-size: .85rem; padding: .5rem 0; }
+@media (min-width: 768px) { .features-grid { grid-template-columns: repeat(2, 1fr); } }
+@media (min-width: 1024px) { .features-grid { grid-template-columns: repeat(3, 1fr); } }
+
+/* Shared "open article" link treatment (story cards + feature cards) */
+.story-card h3 a, .feature-card h3 a { color: inherit; text-decoration: none; }
+.story-card h3 a:hover, .feature-card h3 a:hover { text-decoration: underline; }
+.story-read-more {
+  align-self: flex-start; margin-top: .1rem;
+  font-size: .82rem; font-weight: 700; color: var(--accent2);
+  text-decoration: none;
+}
+.story-read-more:hover { text-decoration: underline; }
+
+/* ---------- 7d. Article view (hash-routed, #/article/<id>) ----------
+   Long-form reading layout: comfortable measure (~65ch), generous
+   line-height, still inherits the current category mood's palette. */
+.article-view { max-width: 100%; }
+.article-inner {
+  max-width: 68ch;
+  margin: 1rem auto 0;
+  padding: 1.25rem 1.25rem 2rem;
+  background: var(--panel);
+  border-radius: .75rem;
+  box-shadow: inset 0 0 0 1px var(--panel-border), inset 0 40px 80px -40px #00000055;
+  color: var(--text);
+}
+.article-back-btn {
+  cursor: pointer; border: 1.5px solid var(--panel-border); background: transparent;
+  color: var(--text); font-weight: 700; font-size: .85rem;
+  padding: .5rem .8rem; border-radius: .4rem; margin-bottom: 1.1rem;
+}
+.article-back-btn:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
+.article-cat-badge { display: inline-block; margin-bottom: .6rem; }
+.article-headline { font-size: clamp(1.4rem, 1.6vw + 1rem, 2.1rem); line-height: 1.2; margin-bottom: .5rem; }
+.article-dek { font-size: 1.05rem; color: var(--text-dim); line-height: 1.5; margin-bottom: .9rem; }
+.article-meta-row {
+  font-size: .8rem; color: var(--text-dim); font-weight: 700;
+  display: flex; gap: .75rem; flex-wrap: wrap;
+  padding-bottom: .85rem; margin-bottom: 1.1rem;
+  border-bottom: 1px dashed var(--panel-border);
+}
+.article-dateline { letter-spacing: .3px; }
+.article-photo { margin: 0 0 1.25rem; }
+.article-photo svg { width: 100%; height: auto; border-radius: .5rem; display: block; }
+.article-photo figcaption { font-size: .78rem; color: var(--text-dim); font-style: italic; margin-top: .45rem; }
+.article-body p {
+  margin: 0 0 1.1rem; font-size: 1.02rem; line-height: 1.75; max-width: 65ch;
+}
+.article-body p.kicker { font-style: italic; color: var(--text-dim); }
+.live-update-block {
+  border: 2px dashed var(--accent); border-radius: .5rem;
+  padding: .8rem 1rem; margin: 0 0 1.25rem;
+  background: color-mix(in srgb, var(--accent) 10%, var(--panel));
+}
+.live-update-label {
+  display: block; font-weight: 900; font-size: .74rem; letter-spacing: .5px;
+  text-transform: uppercase; color: var(--accent2); margin-bottom: .4rem;
+}
+.live-update-headline { font-weight: 800; margin-bottom: .3rem; }
+.live-update-detail { color: var(--text-dim); font-size: .92rem; }
+.live-update-concluded { margin-top: .5rem; }
+.article-source-link { margin-top: 1rem; }
+.article-source-link a {
+  display: inline-block;
+  color: var(--accent);
+  font-weight: 800;
+  text-decoration: none;
+  border: 1px solid var(--accent);
+  border-radius: 6px;
+  padding: .5rem .9rem;
+}
+.article-source-link a:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
+.related-section { margin-top: 1.5rem; padding-top: 1rem; border-top: 1px dashed var(--panel-border); }
+.related-section h3 { font-size: .85rem; text-transform: uppercase; letter-spacing: .5px; color: var(--accent); margin-bottom: .6rem; }
+.related-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
+.related-list a { color: var(--accent2); font-weight: 700; }
+.article-not-found { text-align: center; }
+.article-not-found h2 { margin-bottom: .5rem; }
+@media (min-width: 768px) { .article-inner { padding: 1.75rem 2rem 2.25rem; } }
+@media (min-width: 1024px) { .article-inner { padding: 2rem 2.5rem 2.5rem; } }
+
+/* ---------- 8. Focus & a11y helpers ---------- */
+a, button, input, [tabindex] { outline-offset: 2px; }
+:focus-visible {
+  outline: 3px solid var(--focus);
+  outline-offset: 2px;
+  border-radius: 2px;
+}
+.klaxon-btn:focus-visible { outline: 3px solid var(--focus); }
+
+/* ---------- 8b. CHAOS INDEX — global escalation gauge + broadcast decay ----------
+   One computed number (0-100) drives everything below via body.chaos-tier-0..3
+   classes + a couple of CSS custom properties. Every rule here targets the
+   chrome/body/container level, NEVER the story-card markup itself, so
+   patchStoryGrid()'s keyed reconcile never has to re-render a card just
+   because chaos ticked up (headline stamps are pure ::before content). */
+.chaos-row {
+  /* flex-basis 100% inside .chrome's flex-wrap:wrap always forces this onto
+     its own line, so its own width/content changes can never reflow the
+     brand/live-clock/controls line above it. flex-wrap:wrap on ITS OWN
+     children is the overflow safety valve at narrow widths (390px) — if the
+     fixed-width band/pct pieces don't fit alongside the track, they wrap to
+     a second line inside this row instead of ever overflowing horizontally. */
+  flex: 1 1 100%;
+  display: flex; flex-wrap: wrap; align-items: center; gap: .4rem .6rem;
+  padding: .4rem 0 0;
+  margin-top: .4rem;
+  border-top: 1px dashed var(--panel-border);
+  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
+  font-size: .72rem;
+}
+.chaos-tag { flex: 0 0 auto; font-weight: 900; letter-spacing: .6px; color: var(--text-dim); }
+/* Fixed min-width sized to the longest label ("Post-Journalism") so the
+   readout's own box never changes width as the label text changes length —
+   the one thing that could otherwise nudge the controls sitting above it. */
+.chaos-band { flex: 0 0 auto; min-width: 10.5rem; font-weight: 800; letter-spacing: .3px; color: var(--text); }
+.chaos-band-icon { margin-right: .3rem; }
+.chaos-track {
+  flex: 1 1 auto; max-width: 14rem; height: .55rem; border-radius: 999px;
+  background: var(--panel-border); overflow: hidden; position: relative;
+}
+.chaos-fill {
+  position: absolute; inset: 0 auto 0 0; width: 0%;
+  background: var(--accent); border-radius: inherit;
+  transition: width .5s ease, background-color .4s ease;
+}
+.chaos-pct { flex: 0 0 auto; min-width: 3ch; text-align: right; font-variant-numeric: tabular-nums; color: var(--text-dim); }
+
+body.chaos-tier-1 .chaos-fill { background: #e0a300; }
+body.chaos-tier-2 .chaos-fill { background: #ff8c00; }
+body.chaos-tier-3 .chaos-fill { background: #ff2222; }
+
+/* Ticker degradation: each tier shortens the scroll loop so the chyron
+   visibly speeds up. The reduced-motion path never uses this animation at
+   all (ticker-track animation is fully disabled + JS swaps to the static
+   frame renderer), so this is motion-safe by construction. */
+/* Duration is set from JS (setTickerSpeed) as track length / reading speed,
+   so the crawl moves at a constant, readable px/s however many headlines
+   are in the loop. 90s is only the pre-measure fallback. */
+.ticker-track { animation: ticker-scroll var(--ticker-dur, 90s) linear infinite; }
+
+/* Broadcast losing its grip: headline stamps via generated content only —
+   zero markup change, so these can flip every tick without ever touching
+   patchStoryGrid()'s diffing. */
+body.chaos-tier-1 .story-card:not(.concluded) h3 a::before,
+body.chaos-tier-2 .story-card:not(.concluded) h3 a::before,
+body.chaos-tier-3 .story-card:not(.concluded) h3 a::before {
+  content: "[DEVELOPING] "; color: var(--accent2); font-weight: 900;
+}
+
+/* Lower-third anchor caption — appears at tier 2+, cycles a fixed set of
+   "anchor notes" that get more unhinged per tier. position:fixed, so
+   showing/hiding it never reflows anything in normal document flow. */
+.anchor-caption {
+  position: fixed; left: 0; right: 0; bottom: 3rem; z-index: 19;
+  padding: .4rem .9rem;
+  background: color-mix(in srgb, var(--chrome) 88%, #000 12%);
+  color: var(--text);
+  border-top: 1px dashed var(--accent2);
+  font-size: .78rem; font-weight: 700; font-style: italic;
+  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
+}
+
+/* TV-bezel tilt — the container physically drifts a fraction of a degree at
+   max tier. Gated entirely behind (prefers-reduced-motion: no-preference)
+   so reduced-motion users NEVER get the transform; the unconditional
+   box-shadow rule right below is their static equivalent (a hot red edge
+   instead of a tilt). Only the broadcast container tilts — chrome/nav/admin
+   controls are untouched so their click targets never move. */
+body.chaos-tier-3 main#channelContainer {
+  box-shadow: inset 0 0 0 1px var(--panel-border), inset 0 40px 80px -40px #00000055, inset 0 0 0 3px #ff222277;
+}
+@media (prefers-reduced-motion: no-preference) {
+  main#channelContainer { transition: transform .6s ease; }
+  body.chaos-tier-3 main#channelContainer { transform: rotate(.6deg); }
+}
+
+/* PLEASE STAND BY — full-screen flash at chaos 100 (all stories concluded).
+   pointer-events:none the whole time, so it can cover the entire viewport
+   without ever blocking a click on the klaxon/chips/admin/article links
+   underneath it. JS never arms this under reduced motion — the existing
+   static #meltdownBanner text is that audience's equivalent. Blink cycle is
+   .7s (~1.4Hz, well under the 3-flashes-per-second ceiling) x4 then it's
+   hidden again by JS, so it's transient, not a permanent strobe. */
+.standby-flash {
+  position: fixed; inset: 0; z-index: 30; pointer-events: none;
+  display: flex; align-items: center; justify-content: center;
+  background: rgba(0,0,0,.6); color: #fff; text-align: center;
+  font-weight: 900; letter-spacing: 2px;
+  font-size: clamp(1.1rem, 1rem + 2vw, 2.2rem);
+}
+.standby-flash.flashing { animation: standby-blink .7s steps(1, end) 4; }
+@keyframes standby-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
+
+/* ---------- 9. Responsive breakpoints ---------- */
+@media (min-width: 560px) {
+  .controls { margin-left: 0; }
+}
+@media (min-width: 768px) {
+  .chrome { padding: 1rem 1.5rem; }
+  .category-filter { padding: .75rem 1.5rem; }
+  main#channelContainer { padding: 1.25rem 1.5rem 1.75rem; }
+  .admin-panel { padding: 1.25rem 1.5rem 1.75rem; }
+  .story-grid { grid-template-columns: repeat(2, 1fr); }
+  .story-card.lead { grid-column: 1 / -1; }
+}
+@media (min-width: 1024px) {
+  .chrome { padding: 1rem 2rem; }
+  .category-filter { padding: .75rem 2rem; }
+  main#channelContainer { padding: 1.5rem 2rem 2rem; }
+  .admin-panel { padding: 1.5rem 2rem 2rem; }
+  .story-grid { grid-template-columns: repeat(3, 1fr); align-items: stretch; }
+  .story-card.lead {
+    grid-column: 1 / -1;
+    display: grid; grid-template-columns: 3fr 2fr; column-gap: 1.5rem; row-gap: .55rem;
+    align-content: start;
+  }
+  .story-card.lead > * { grid-column: 2; }
+  .story-card.lead > .story-photo {
+    grid-column: 1; grid-row: 1 / span 8;
+    width: calc(100% + 1rem); margin: -1rem 0 -1rem -1rem;
+    height: calc(100% + 2rem); border-radius: .6rem 0 0 .6rem;
+  }
+}
+@media (min-width: 1280px) {
+  .wordmark { letter-spacing: .8px; }
+  .ticker-track { font-size: .92rem; }
+}
+
+/* ---------- 10. Reduced-motion overrides ---------- */
+@media (prefers-reduced-motion: reduce) {
+  * { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
+  .rec-dot { animation: none; opacity: 1; }
+  .klaxon-btn[aria-pressed="true"] { animation: none; box-shadow: 0 0 0 4px #ff222255; }
+  .breaking-banner { animation: none; background: #b30000; }
+  body.breaking-mode::before { animation: none; opacity: .8; }
+  .ticker-track { animation: none; }
+}
+
+/* ============================================================
+   PART 2 — CARTOON GALLERY (cartoons/index.html only)
+   Additive rules on top of Part 1's shared reset/palette/chrome/
+   chip-row (the gallery page reuses those verbatim). Only the
+   gallery-specific chrome/grid/card/controls rules live here.
+   ============================================================ */
+:root { --card-w: 300px; }
+
+/* Gallery page has no fixed ticker, so drop the site body's ticker
+   clearance and slightly faster palette transition. */
+body { padding-bottom: 0; transition: background-color .4s ease, color .4s ease; }
+
+.chrome h1 { font-size: 1.15rem; letter-spacing: .02em; margin: 0; }
+.chrome a.home { color: var(--text-dim); text-decoration: none; font-size: .85rem; border: 1px solid var(--panel-border); padding: 6px 10px; border-radius: 6px; }
+.chrome a.home:hover { color: var(--text); }
+
+main { max-width: 1200px; margin: 0 auto; padding: 20px; }
+.dek { color: var(--text-dim); max-width: 60ch; margin-bottom: 18px; }
+
+/* Category filter sits inside <main> here (not the sticky sub-nav band
+   used on the front page), so it just needs its own bottom margin —
+   the chip-row/chip-item rules themselves are shared from Part 1. */
+.category-filter { margin-bottom: 18px; }
+
+.controls { display: flex; gap: 16px; flex-wrap: wrap; align-items: center; background: var(--panel); border: 1px solid var(--panel-border); border-radius: 10px; padding: 12px 16px; margin-bottom: 20px; }
+.controls label { font-size: .8rem; color: var(--text-dim); display: flex; gap: 8px; align-items: center; }
+.controls select, .controls input[type=range] { accent-color: var(--accent); }
+.controls select { background: var(--chrome); color: var(--text); border: 1px solid var(--panel-border); border-radius: 6px; padding: 4px 8px; }
+.view-toggle { display: flex; gap: 4px; }
+.view-toggle button { background: var(--chrome); color: var(--text-dim); border: 1px solid var(--panel-border); border-radius: 6px; padding: 5px 12px; font-size: .8rem; cursor: pointer; font-family: inherit; }
+.view-toggle button:hover { color: var(--text); }
+.view-toggle button[aria-pressed="true"] { background: var(--accent); color: var(--chrome); font-weight: 700; border-color: var(--accent); }
+#count { margin-left: auto; font-size: .8rem; color: var(--text-dim); }
+.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--card-w), 1fr)); gap: 18px; }
+
+/* ---------- Per-category card accent ----------
+   Each card carries data-cat, which sets --cat-accent to that category's
+   mood --accent (see Part 1's body[data-mood] blocks). Keeps cards
+   multicolored in the "All Categories" view regardless of the page's
+   current mood. */
+.card[data-cat="politics"] { --cat-accent: #ff3b3b; }
+.card[data-cat="weather"] { --cat-accent: #4fd1ff; }
+.card[data-cat="sports"] { --cat-accent: #ff9800; }
+.card[data-cat="entertainment"] { --cat-accent: #ff5fd1; }
+.card[data-cat="business"] { --cat-accent: #2ecc71; }
+.card[data-cat="scitech"] { --cat-accent: #00f0ff; }
+.card[data-cat="uncategorized"] { --cat-accent: #546578; }
+
+.card { background: var(--panel); border: 1px solid var(--panel-border); border-left: 5px solid var(--cat-accent, var(--accent)); border-radius: 12px; overflow: hidden; display: flex; flex-direction: column; text-decoration: none; color: var(--text); transition: transform .15s ease, border-color .15s ease; }
+.card:hover, .card:focus-visible { transform: translateY(-3px); border-color: var(--accent); }
+.card .thumb { aspect-ratio: 4/3; background: var(--chrome) center/cover no-repeat; display: flex; align-items: center; justify-content: center; font-size: 2.4rem; position: relative; }
+.card .body { padding: 12px 14px; display: flex; flex-direction: column; gap: 6px; min-width: 0; }
+.card .cat-badge {
+  align-self: flex-start;
+  font-size: .68rem; font-weight: 800; letter-spacing: .3px; text-transform: uppercase;
+  padding: .15rem .5rem; border-radius: 999px;
+  background: var(--cat-accent, var(--accent)); color: var(--chrome);
+}
+.card .title { font-weight: 700; font-size: 1rem; }
+.card .meta { font-size: .75rem; color: var(--text-dim); }
+.card .blurb { font-size: .85rem; color: var(--text-dim); }
+.grid.list-mode { display: flex; flex-direction: column; gap: 10px; }
+.grid.list-mode .card { flex-direction: row; align-items: stretch; }
+.grid.list-mode .card:hover, .grid.list-mode .card:focus-visible { transform: translateX(3px); }
+.grid.list-mode .card .thumb { aspect-ratio: auto; width: 120px; min-width: 120px; font-size: 1.6rem; }
+.grid.list-mode .card .body { flex: 1; justify-content: center; min-width: 0; overflow: hidden; }
+.grid.list-mode .card .title, .grid.list-mode .card .blurb { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+@media (max-width: 480px) {
+  .grid.list-mode .card .thumb { width: 72px; min-width: 72px; font-size: 1.2rem; }
+  .grid.list-mode .card .body { padding: 10px; }
+}
+.empty { background: var(--panel); border: 1px dashed var(--panel-border); border-radius: 12px; padding: 40px 20px; text-align: center; color: var(--text-dim); }
+.empty a { display: inline-block; margin-top: 10px; }
+footer { max-width: 1200px; margin: 30px auto; padding: 0 20px 30px; color: var(--text-dim); font-size: .75rem; }
+@media (prefers-reduced-motion: reduce) { .card { transition: none; } }
+
+
+/* ============================================================
+   PART 3 — CARTOON STRIP PAGES (cartoons/2026*.html)
+   The 18 individual strip pages were built against an older
+   "parchment newspaper" token set (--ink, --ink-dim, --rule,
+   --seal) and a per-file light/dark toggle. Rather than rewrite
+   every strip's markup/JS (several draw their SVG panels by
+   reading these var() names directly at render time), these
+   names are aliased onto the shared mint-green token system so
+   the strips automatically re-theme with it — including the
+   per-category body[data-mood] palettes in Part 1.
+
+   IMPORTANT: this alias block is declared on the bare `body`
+   selector (NOT `:root`/html). Custom-property var() references
+   are substituted using the referenced property's cascaded value
+   on the SAME element where the alias is declared — declaring it
+   on :root would freeze --ink-dim etc. using html's own (default
+   "all" mood) --text-dim, which does not update when a specific
+   body[data-mood="..."] block further down overrides --text-dim
+   only on <body>. Declaring the alias directly on `body` makes it
+   resolve using body's own final cascaded --text/--text-dim/
+   --accent/--accent2 (whichever mood block — or the dark/light
+   toggle override below — won), so it repaints correctly. */
+body {
+  --ink: var(--text);
+  --ink-dim: var(--text-dim);
+  --rule: var(--accent);
+  --seal: var(--accent2);
+}
+
+/* Universal link color the strips relied on (front page styles its
+   links per-context instead, so this was missing from Part 1). */
+a { color: var(--accent2); }
+
+/* Each strip ships its own "Dark mode: on/off" toggle button that sets
+   data-theme on <html> (see each file's inline script). IMPORTANT: 17
+   of the 18 strips default to explicitly setting data-theme="light"
+   on load (only one has a true 3-state auto/dark/light toggle) — so
+   "light" is each page's normal/default state, not a user opt-in. It
+   must stay a NO-OP here (no bg/panel/text override) so the active
+   body[data-mood="..."] palette from Part 1 is what actually renders
+   by default. Only the explicit "dark" state (reached by the user
+   clicking the toggle button, or a real OS dark preference on the one
+   auto-capable strip) dims things further, as a night-viewing variant
+   layered on top of whichever mood is active. These overrides target
+   the BODY element directly (not :root) so they win over the
+   per-category body[data-mood="..."] blocks in Part 1 that also
+   declare these same custom properties directly on <body>. Only bg/
+   panel/text are touched — --accent/--accent2 stay mood-colored so a
+   politics strip in dark mode still reads as politics-red, just on a
+   darker backdrop. */
+html[data-theme="dark"] body {
+  --bg: #0a0f0c; --bg2: #10201780; --panel: #141f18; --panel-border: #2c3d3255;
+  --text: #eef7f0; --text-dim: #c3d4c9aa;
+}
+@media (prefers-color-scheme: dark) {
+  html:not([data-theme="light"]):not([data-theme="dark"]) body {
+    --bg: #0a0f0c; --bg2: #10201780; --panel: #141f18; --panel-border: #2c3d3255;
+    --text: #eef7f0; --text-dim: #c3d4c9aa;
+  }
+}
diff --git a/cartoons/20260924-budget-pie-fight.html b/cartoons/20260924-budget-pie-fight.html
index cae7fad..e75ffa1 100644
--- a/cartoons/20260924-budget-pie-fight.html
+++ b/cartoons/20260924-budget-pie-fight.html
@@ -5,26 +5,8 @@
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>Who Gets the Last Slice — P24 Political Cartoon Desk</title>
 <meta name="description" content="An original invented-satire strip: an invented town budget is a pie that diners keep re-slicing smaller at a banquet table. Drag the slider to re-carve it and watch the caption rewrite live." />
+<link rel="stylesheet" href="../assets/style.css" />
 <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)}
@@ -57,12 +39,11 @@ button.theme{margin-left:auto;background:transparent;border:1px solid var(--ink-
 .slider-row input[type=range]{width:100%;accent-color:var(--accent)}
 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>
+<body data-mood="politics">
 <header>
   <div>
     <div class="kicker">P24 Political Cartoon Desk &middot; Invented Satire</div>
diff --git a/cartoons/20260924-casserole-succession.html b/cartoons/20260924-casserole-succession.html
index 17c689c..a6576c8 100644
--- a/cartoons/20260924-casserole-succession.html
+++ b/cartoons/20260924-casserole-succession.html
@@ -5,26 +5,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" />
 <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)}
@@ -46,10 +28,9 @@ button.theme{margin-left:auto;background:transparent;border:1px solid var(--ink-
 #status.info{border-color:var(--focus);color:var(--focus)}
 footer{max-width:640px;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; } }
 </style>
 </head>
-<body>
+<body data-mood="politics">
 <header>
   <div>
     <div class="kicker">P24 Political Cartoon Desk &middot; Invented Satire &middot; Savannah, GA</div>
diff --git a/cartoons/20260924-commission-turtles.html b/cartoons/20260924-commission-turtles.html
index c4fa735..5adb13a 100644
--- a/cartoons/20260924-commission-turtles.html
+++ b/cartoons/20260924-commission-turtles.html
@@ -5,26 +5,8 @@
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>Commissions All the Way Down — P24 Political Cartoon Desk</title>
 <meta name="description" content="An original invented-satire strip: a blue-ribbon commission recommends forming a commission to study its own recommendation, nesting smaller and smaller." />
+<link rel="stylesheet" href="../assets/style.css" />
 <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)}
@@ -52,12 +34,11 @@ button.theme{margin-left:auto;background:transparent;border:1px solid var(--ink-
 #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>
+<body data-mood="politics">
 <header>
   <div>
     <div class="kicker">P24 Political Cartoon Desk &middot; Invented Satire</div>
diff --git a/cartoons/20260924-conrad-filibuster-qwen25-7b.html b/cartoons/20260924-conrad-filibuster-qwen25-7b.html
index b75961d..7130643 100644
--- a/cartoons/20260924-conrad-filibuster-qwen25-7b.html
+++ b/cartoons/20260924-conrad-filibuster-qwen25-7b.html
@@ -2,19 +2,31 @@
 <html lang="en">
 <head>
     <meta charset="UTF-8">
-    <title>The Filibuster Marathon</title>
+    <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 cartoon: a filibustering delegate stands flat and unmoved as the chamber clock winds on. Model Arena pick (qwen25-7b)." />
+    <link rel="stylesheet" href="../assets/style.css" />
     <style>
         body {
-            background-color: #cdf5e0;
-            color: black;
-            font-family: 'Times New Roman', serif;
             display: flex;
+            flex-direction: column;
             justify-content: center;
             align-items: center;
-            height: 100vh;
-            margin: 0;
-            overflow: hidden;
+            min-height: 100vh;
+            gap: 1rem;
+            padding: 1rem;
         }
+        .back {
+            align-self: flex-start;
+            font-family: -apple-system, sans-serif;
+            font-size: .8rem;
+            color: var(--text-dim);
+            text-decoration: none;
+            border: 1px solid var(--panel-border);
+            border-radius: 6px;
+            padding: 5px 10px;
+        }
+        .back:hover { color: var(--text); }
         .cartoon {
             width: 100%;
             max-width: 1440px;
@@ -24,19 +36,21 @@
         .title {
             font-size: 2em;
             margin-bottom: 1em;
+            color: var(--text);
         }
         .lectern, .delegate, .phonebook, .clock {
-            fill: black;
+            fill: var(--text);
         }
         .clock-dial, .clock-hand, .chamber-members {
-            stroke: black;
+            stroke: var(--text);
         }
         .accent {
-            color: #0f9d78;
+            color: var(--accent);
         }
     </style>
 </head>
-<body>
+<body data-mood="politics">
+<a class="back" href="index.html">&larr; Cartoon Desk</a>
 <div class="cartoon">
     <div class="title">The Filibuster Marathon</div>
     <svg width="100%" height="400" viewBox="0 0 500 400">
diff --git a/cartoons/20260924-filibuster-vs-cloture.html b/cartoons/20260924-filibuster-vs-cloture.html
index 6eafd5e..68f1cbd 100644
--- a/cartoons/20260924-filibuster-vs-cloture.html
+++ b/cartoons/20260924-filibuster-vs-cloture.html
@@ -5,26 +5,8 @@
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>Cloture o'Clock — P24 Political Cartoon Desk</title>
 <meta name="description" content="An original invented-satire strip: the Forum of Delegates' ceremonial floor clock is wound tight by one side and wound right back by the other, ticking forward and backward until it just gives up." />
+<link rel="stylesheet" href="../assets/style.css" />
 <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)}
@@ -52,12 +34,11 @@ button.theme{margin-left:auto;background:transparent;border:1px solid var(--ink-
 #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>
+<body data-mood="politics">
 <header>
   <div>
     <div class="kicker">P24 Political Cartoon Desk &middot; Invented Satire</div>
diff --git a/cartoons/20260924-four-minutes-ahead.html b/cartoons/20260924-four-minutes-ahead.html
index 31f3db0..be7bfc3 100644
--- a/cartoons/20260924-four-minutes-ahead.html
+++ b/cartoons/20260924-four-minutes-ahead.html
@@ -5,26 +5,8 @@
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>Four Minutes Ahead of Everyone — P24 Political Cartoon Desk</title>
 <meta name="description" content="An original invented-satire cartoon: Bozeman sets its town clock four minutes fast, declares its own time zone, and petitions to secede from Mountain Time entirely." />
+<link rel="stylesheet" href="../assets/style.css" />
 <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)}
@@ -46,10 +28,9 @@ button.theme{margin-left:auto;background:transparent;border:1px solid var(--ink-
 #status.info{border-color:var(--focus);color:var(--focus)}
 footer{max-width:640px;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; } }
 </style>
 </head>
-<body>
+<body data-mood="politics">
 <header>
   <div>
     <div class="kicker">P24 Political Cartoon Desk &middot; Invented Satire &middot; Bozeman, MT</div>
diff --git a/cartoons/20260924-gnome-who-wouldnt-hush.html b/cartoons/20260924-gnome-who-wouldnt-hush.html
index d1a5f7f..e110f9f 100644
--- a/cartoons/20260924-gnome-who-wouldnt-hush.html
+++ b/cartoons/20260924-gnome-who-wouldnt-hush.html
@@ -5,26 +5,8 @@
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>The Gnome Who Wouldn't Hush — P24 Political Cartoon Desk</title>
 <meta name="description" content="An original invented-satire cartoon: Duluth taxes loud lawn gnomes, registers them, ankle-monitors repeat offenders, and finally convenes a Gnome Parole Board." />
+<link rel="stylesheet" href="../assets/style.css" />
 <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)}
@@ -46,10 +28,9 @@ button.theme{margin-left:auto;background:transparent;border:1px solid var(--ink-
 #status.info{border-color:var(--focus);color:var(--focus)}
 footer{max-width:640px;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; } }
 </style>
 </head>
-<body>
+<body data-mood="politics">
 <header>
   <div>
     <div class="kicker">P24 Political Cartoon Desk &middot; Invented Satire &middot; Duluth, MN</div>
diff --git a/cartoons/20260924-hedge-non-aggression-pact.html b/cartoons/20260924-hedge-non-aggression-pact.html
index c0f0ac5..dfd9ba3 100644
--- a/cartoons/20260924-hedge-non-aggression-pact.html
+++ b/cartoons/20260924-hedge-non-aggression-pact.html
@@ -5,26 +5,8 @@
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>The Hedge Non-Aggression Pact — P24 Political Cartoon Desk</title>
 <meta name="description" content="An original invented-satire cartoon: Washington regulates statue-shaped hedges, fines a homeowner, gets accused of hedge espionage, and signs a topiary treaty with Idaho." />
+<link rel="stylesheet" href="../assets/style.css" />
 <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)}
@@ -46,10 +28,9 @@ button.theme{margin-left:auto;background:transparent;border:1px solid var(--ink-
 #status.info{border-color:var(--focus);color:var(--focus)}
 footer{max-width:640px;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; } }
 </style>
 </head>
-<body>
+<body data-mood="politics">
 <header>
   <div>
     <div class="kicker">P24 Political Cartoon Desk &middot; Invented Satire &middot; Spokane, WA</div>
diff --git a/cartoons/20260924-jackalope-custody-battle.html b/cartoons/20260924-jackalope-custody-battle.html
index 716f17e..a2c84b6 100644
--- a/cartoons/20260924-jackalope-custody-battle.html
+++ b/cartoons/20260924-jackalope-custody-battle.html
@@ -5,26 +5,8 @@
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>The Jackalope Custody Battle — P24 Cartoon Desk</title>
 <meta name="description" content="An original invented-satire cartoon: two Iowa towns feud over which one truly owns the jackalope legend, ending in a statue that commutes between them by flatbed." />
+<link rel="stylesheet" href="../assets/style.css" />
 <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)}
@@ -46,10 +28,9 @@ button.theme{margin-left:auto;background:transparent;border:1px solid var(--ink-
 #status.info{border-color:var(--focus);color:var(--focus)}
 footer{max-width:640px;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; } }
 </style>
 </head>
-<body>
+<body data-mood="entertainment">
 <header>
   <div>
     <div class="kicker">P24 Cartoon Desk &middot; Invented Satire &middot; Story City, IA</div>
diff --git a/cartoons/20260924-lobbyist-revolving-door.html b/cartoons/20260924-lobbyist-revolving-door.html
index fb35ce0..5b3d57f 100644
--- a/cartoons/20260924-lobbyist-revolving-door.html
+++ b/cartoons/20260924-lobbyist-revolving-door.html
@@ -5,26 +5,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." />
+<link rel="stylesheet" href="../assets/style.css" />
 <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)}
@@ -53,12 +35,11 @@ button.theme{margin-left:auto;background:transparent;border:1px solid var(--ink-
 .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>
+<body data-mood="politics">
 <header>
   <div>
     <div class="kicker">P24 Political Cartoon Desk &middot; Invented Satire</div>
diff --git a/cartoons/20260924-meter-that-wouldnt-yield.html b/cartoons/20260924-meter-that-wouldnt-yield.html
index 2c029ee..e64b415 100644
--- a/cartoons/20260924-meter-that-wouldnt-yield.html
+++ b/cartoons/20260924-meter-that-wouldnt-yield.html
@@ -5,26 +5,8 @@
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>The Meter That Wouldn't Yield — P24 Political Cartoon Desk</title>
 <meta name="description" content="An original invented-satire cartoon: Erie's most reliable parking meter is honored, seated, re-elected, and sworn in as honorary mayor — and then won't leave the office." />
+<link rel="stylesheet" href="../assets/style.css" />
 <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)}
@@ -46,10 +28,9 @@ button.theme{margin-left:auto;background:transparent;border:1px solid var(--ink-
 #status.info{border-color:var(--focus);color:var(--focus)}
 footer{max-width:640px;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; } }
 </style>
 </head>
-<body>
+<body data-mood="politics">
 <header>
   <div>
     <div class="kicker">P24 Political Cartoon Desk &middot; Invented Satire &middot; Erie, PA</div>
diff --git a/cartoons/20260924-pork-barrel-train.html b/cartoons/20260924-pork-barrel-train.html
index 5b2331f..f809ff4 100644
--- a/cartoons/20260924-pork-barrel-train.html
+++ b/cartoons/20260924-pork-barrel-train.html
@@ -5,26 +5,8 @@
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>The Pork Barrel Express — P24 Political Cartoon Desk</title>
 <meta name="description" content="An original invented-satire strip: a fictional infrastructure bill turns into a runaway train as wildly unrelated riders couple on car by car. Click a car for its invented cost." />
+<link rel="stylesheet" href="../assets/style.css" />
 <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)}
@@ -54,12 +36,11 @@ button.theme{margin-left:auto;background:transparent;border:1px solid var(--ink-
 #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>
+<body data-mood="politics">
 <header>
   <div>
     <div class="kicker">P24 Political Cartoon Desk &middot; Invented Satire</div>
diff --git a/cartoons/20260924-quorum-call-hide-seek.html b/cartoons/20260924-quorum-call-hide-seek.html
index f7d5b51..8f197fb 100644
--- a/cartoons/20260924-quorum-call-hide-seek.html
+++ b/cartoons/20260924-quorum-call-hide-seek.html
@@ -5,26 +5,8 @@
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>The Quorum Call — P24 Political Cartoon Desk</title>
 <meta name="description" content="An original invented-satire strip: an invented legislative chamber plays hide-and-seek to dodge a quorum call, with a new hiding spot each plot twist." />
+<link rel="stylesheet" href="../assets/style.css" />
 <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)}
@@ -52,12 +34,11 @@ button.theme{margin-left:auto;background:transparent;border:1px solid var(--ink-
 #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>
+<body data-mood="politics">
 <header>
   <div>
     <div class="kicker">P24 Political Cartoon Desk &middot; Invented Satire</div>
diff --git a/cartoons/20260924-row-by-row-only.html b/cartoons/20260924-row-by-row-only.html
index e5ffe2e..9c5c535 100644
--- a/cartoons/20260924-row-by-row-only.html
+++ b/cartoons/20260924-row-by-row-only.html
@@ -5,26 +5,8 @@
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>Row by Row Only — P24 Political Cartoon Desk</title>
 <meta name="description" content="An original invented-satire cartoon: Kansas legislates the one true way to eat corn on the cob, deputizes Corn Marshals, and bans the cheating devices known as cob holders." />
+<link rel="stylesheet" href="../assets/style.css" />
 <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)}
@@ -46,10 +28,9 @@ button.theme{margin-left:auto;background:transparent;border:1px solid var(--ink-
 #status.info{border-color:var(--focus);color:var(--focus)}
 footer{max-width:640px;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; } }
 </style>
 </head>
-<body>
+<body data-mood="politics">
 <header>
   <div>
     <div class="kicker">P24 Political Cartoon Desk &middot; Invented Satire &middot; Topeka, KS</div>
diff --git a/cartoons/20260924-the-corner-with-its-own-weather.html b/cartoons/20260924-the-corner-with-its-own-weather.html
index de74139..e27d213 100644
--- a/cartoons/20260924-the-corner-with-its-own-weather.html
+++ b/cartoons/20260924-the-corner-with-its-own-weather.html
@@ -5,26 +5,8 @@
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>The Corner With Its Own Weather — P24 Cartoon Desk</title>
 <meta name="description" content="An original invented-satire cartoon: a persistent crosswind at one small-town intersection escalates from a windsock to a paid Wind Spotter to an annual Hat-Chasing Festival." />
+<link rel="stylesheet" href="../assets/style.css" />
 <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)}
@@ -46,10 +28,9 @@ button.theme{margin-left:auto;background:transparent;border:1px solid var(--ink-
 #status.info{border-color:var(--focus);color:var(--focus)}
 footer{max-width:640px;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; } }
 </style>
 </head>
-<body>
+<body data-mood="weather">
 <header>
   <div>
     <div class="kicker">P24 Cartoon Desk &middot; Invented Satire &middot; Cut Bank, MT</div>
diff --git a/cartoons/20260924-the-zipper-that-ended-an-inning.html b/cartoons/20260924-the-zipper-that-ended-an-inning.html
index 1a9dff0..6379069 100644
--- a/cartoons/20260924-the-zipper-that-ended-an-inning.html
+++ b/cartoons/20260924-the-zipper-that-ended-an-inning.html
@@ -5,26 +5,8 @@
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>The Zipper That Ended an Inning — P24 Cartoon Desk</title>
 <meta name="description" content="An original invented-satire cartoon: a minor league mascot's costume zipper failure escalates into a league-wide mascot-safety bureaucracy." />
+<link rel="stylesheet" href="../assets/style.css" />
 <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)}
@@ -46,10 +28,9 @@ button.theme{margin-left:auto;background:transparent;border:1px solid var(--ink-
 #status.info{border-color:var(--focus);color:var(--focus)}
 footer{max-width:640px;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; } }
 </style>
 </head>
-<body>
+<body data-mood="sports">
 <header>
   <div>
     <div class="kicker">P24 Cartoon Desk &middot; Invented Satire &middot; Chillicothe, OH</div>
diff --git a/cartoons/20260924-tollpoints-diamond-status.html b/cartoons/20260924-tollpoints-diamond-status.html
index 0dd1e9a..3b3ab35 100644
--- a/cartoons/20260924-tollpoints-diamond-status.html
+++ b/cartoons/20260924-tollpoints-diamond-status.html
@@ -5,26 +5,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" />
 <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)}
@@ -46,10 +28,9 @@ button.theme{margin-left:auto;background:transparent;border:1px solid var(--ink-
 #status.info{border-color:var(--focus);color:var(--focus)}
 footer{max-width:640px;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; } }
 </style>
 </head>
-<body>
+<body data-mood="business">
 <header>
   <div>
     <div class="kicker">P24 Cartoon Desk &middot; Invented Satire &middot; Ashtabula, OH</div>
diff --git a/cartoons/20260924-tuber-bank-of-chugwater.html b/cartoons/20260924-tuber-bank-of-chugwater.html
index 8097dfd..f71b973 100644
--- a/cartoons/20260924-tuber-bank-of-chugwater.html
+++ b/cartoons/20260924-tuber-bank-of-chugwater.html
@@ -5,26 +5,8 @@
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>The Tuber Bank of Chugwater — P24 Cartoon Desk</title>
 <meta name="description" content="An original invented-satire cartoon: a Wyoming lab's potato-battery pilot escalates from soil sensors to a custom potato cultivar to a wired 'tuber bank' backing the county grid." />
+<link rel="stylesheet" href="../assets/style.css" />
 <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)}
@@ -46,10 +28,9 @@ button.theme{margin-left:auto;background:transparent;border:1px solid var(--ink-
 #status.info{border-color:var(--focus);color:var(--focus)}
 footer{max-width:640px;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; } }
 </style>
 </head>
-<body>
+<body data-mood="scitech">
 <header>
   <div>
     <div class="kicker">P24 Cartoon Desk &middot; Invented Satire &middot; Chugwater, WY</div>
diff --git a/cartoons/index.html b/cartoons/index.html
index 7cbd09e..4d898e7 100644
--- a/cartoons/index.html
+++ b/cartoons/index.html
@@ -5,170 +5,7 @@
 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 <title>P24 Political Cartoon Desk — PANDEMONIUM-24</title>
 <meta name="description" content="The P24 Political Cartoon Desk archive — original invented satire strips, updated daily." />
-<style>
-/* ---------- Mood theming — CSS custom properties ----------
-   Mirrors the palette blocks in ../index.html exactly (:root, body[data-mood]).
-   Default ("all") is the pale mint/green from the main page's default mood. */
-:root { --card-w: 300px; }
-:root, body[data-mood="all"] {
-  --bg: #cdf5e0;
-  --bg2: #f2fff2;
-  --panel: #ffffff;
-  --panel-border: #0f9d7833;
-  --accent: #0f9d78;
-  --accent2: #056659;
-  --text: #123524;
-  --text-dim: #12352499;
-  --chrome: #ffffff;
-  --focus: #0b3d91;
-}
-body[data-mood="politics"] {
-  --bg: #2a0b0b; --bg2: #4a0f0f; --panel: #3a1212; --panel-border: #ff3b3b55;
-  --accent: #ff3b3b; --accent2: #ffb703; --text: #ffe9e9; --text-dim: #ffd7d7aa;
-  --chrome: #220808; --focus: #ffd166;
-}
-body[data-mood="weather"] {
-  --bg: #071a33; --bg2: #0c2b52; --panel: #0e2340; --panel-border: #4fd1ff55;
-  --accent: #4fd1ff; --accent2: #a0e9ff; --text: #eaf6ff; --text-dim: #cfeeffaa;
-  --chrome: #05132c; --focus: #ffe066;
-}
-body[data-mood="sports"] {
-  --bg: #2b1400; --bg2: #4a2200; --panel: #3a1c00; --panel-border: #ff980055;
-  --accent: #ff9800; --accent2: #ffd166; --text: #fff3e0; --text-dim: #ffe0b3aa;
-  --chrome: #200f00; --focus: #4fd1ff;
-}
-body[data-mood="entertainment"] {
-  --bg: #2a0a2e; --bg2: #4a0f52; --panel: #3a1140; --panel-border: #ff5fd155;
-  --accent: #ff5fd1; --accent2: #b388ff; --text: #fbe9ff; --text-dim: #eecbffaa;
-  --chrome: #210822; --focus: #ffe066;
-}
-body[data-mood="business"] {
-  --bg: #08210f; --bg2: #0e3a1c; --panel: #0d2c16; --panel-border: #2ecc7155;
-  --accent: #2ecc71; --accent2: #f1c40f; --text: #eafff0; --text-dim: #c9f5d6aa;
-  --chrome: #06180c; --focus: #f1c40f;
-}
-body[data-mood="scitech"] {
-  --bg: #0a0a23; --bg2: #170b3d; --panel: #140c33; --panel-border: #00f0ff55;
-  --accent: #00f0ff; --accent2: #ff00e6; --text: #eafcff; --text-dim: #c9f3ffaa;
-  --chrome: #08081c; --focus: #ff00e6;
-}
-body[data-mood="uncategorized"] {
-  --bg: #e9edf0; --bg2: #ffffff; --panel: #ffffff; --panel-border: #54657833;
-  --accent: #546578; --accent2: #2f3b47; --text: #20272d; --text-dim: #20272d99;
-  --chrome: #ffffff; --focus: #0b3d91;
-}
-
-*,*::before,*::after{box-sizing:border-box}
-html{overflow-x:hidden}
-[hidden]{display:none!important}
-.visually-hidden {
-  position: absolute !important;
-  width: 1px; height: 1px;
-  padding: 0; margin: -1px;
-  overflow: hidden;
-  clip: rect(0 0 0 0);
-  white-space: nowrap;
-  border: 0;
-}
-body{
-  margin:0; min-height:100vh; font-family:-apple-system,"Segoe UI",Helvetica,Arial,sans-serif;
-  color:var(--text); background:radial-gradient(circle at 20% -10%, var(--bg2), var(--bg) 60%);
-  line-height:1.4;
-  transition: background-color .4s ease, color .4s ease;
-}
-a{color:var(--accent2)}
-h1,h2,p{margin:0}
-:focus-visible{outline:3px solid var(--focus); outline-offset:2px}
-.chrome{background:var(--chrome); border-bottom:3px solid var(--accent); padding:14px 20px; display:flex; align-items:center; gap:14px; flex-wrap:wrap}
-.logo-badge{background:var(--accent); color:var(--chrome); font-weight:900; padding:4px 10px; border-radius:4px; letter-spacing:.05em}
-.chrome h1{font-size:1.15rem; letter-spacing:.02em}
-.chrome a.home{color:var(--text-dim); text-decoration:none; font-size:.85rem; border:1px solid var(--panel-border); padding:6px 10px; border-radius:6px}
-.chrome a.home:hover{color:var(--text)}
-main{max-width:1200px; margin:0 auto; padding:20px}
-.dek{color:var(--text-dim); max-width:60ch; margin-bottom:18px}
-
-/* ---------- Category filter (chip row) — mirrors ../index.html ---------- */
-.category-filter{ margin-bottom:18px; }
-.category-filter fieldset{ border:0; margin:0; padding:0; min-width:0; }
-.chip-row{
-  display:flex; gap:.5rem; overflow-x:auto; padding-bottom:.25rem;
-  scrollbar-width:thin; min-width:0;
-}
-.chip-row::-webkit-scrollbar{ height:6px; }
-.chip-row::-webkit-scrollbar-thumb{ background:var(--panel-border); border-radius:4px; }
-.chip-item{ position:relative; flex:0 0 auto; }
-.chip-item input{ position:absolute; opacity:0; width:1px; height:1px; }
-.chip-item label{
-  display:inline-block;
-  padding:.45rem .9rem;
-  border-radius:999px;
-  border:1.5px solid var(--panel-border);
-  background:var(--panel);
-  color:var(--text);
-  font-size:.8rem; font-weight:700; letter-spacing:.2px;
-  cursor:pointer; white-space:nowrap;
-  transition:background-color .2s ease, color .2s ease, transform .12s ease;
-}
-.chip-item label:hover{ transform:translateY(-1px); }
-.chip-item input:checked + label{
-  background:var(--accent); color:var(--bg); border-color:var(--accent2);
-}
-.chip-item input:focus-visible + label{
-  outline:3px solid var(--focus); outline-offset:2px;
-}
-
-.controls{display:flex; gap:16px; flex-wrap:wrap; align-items:center; background:var(--panel); border:1px solid var(--panel-border); border-radius:10px; padding:12px 16px; margin-bottom:20px}
-.controls label{font-size:.8rem; color:var(--text-dim); display:flex; gap:8px; align-items:center}
-.controls select, .controls input[type=range]{accent-color:var(--accent)}
-.controls select{background:var(--chrome); color:var(--text); border:1px solid var(--panel-border); border-radius:6px; padding:4px 8px}
-.view-toggle{display:flex; gap:4px}
-.view-toggle button{background:var(--chrome); color:var(--text-dim); border:1px solid var(--panel-border); border-radius:6px; padding:5px 12px; font-size:.8rem; cursor:pointer; font-family:inherit}
-.view-toggle button:hover{color:var(--text)}
-.view-toggle button[aria-pressed="true"]{background:var(--accent); color:var(--chrome); font-weight:700; border-color:var(--accent)}
-#count{margin-left:auto; font-size:.8rem; color:var(--text-dim)}
-.grid{display:grid; grid-template-columns:repeat(auto-fill, minmax(var(--card-w), 1fr)); gap:18px}
-
-/* ---------- Per-category card accent ---------- */
-/* Each card carries data-cat, which sets --cat-accent to that category's
-   mood --accent (see body[data-mood] blocks above). This keeps cards
-   multicolored in the "All Categories" view regardless of the page's
-   current mood, and still tracks the palette if these values ever change. */
-.card[data-cat="politics"]{ --cat-accent:#ff3b3b; }
-.card[data-cat="weather"]{ --cat-accent:#4fd1ff; }
-.card[data-cat="sports"]{ --cat-accent:#ff9800; }
-.card[data-cat="entertainment"]{ --cat-accent:#ff5fd1; }
-.card[data-cat="business"]{ --cat-accent:#2ecc71; }
-.card[data-cat="scitech"]{ --cat-accent:#00f0ff; }
-.card[data-cat="uncategorized"]{ --cat-accent:#546578; }
-
-.card{background:var(--panel); border:1px solid var(--panel-border); border-left:5px solid var(--cat-accent, var(--accent)); border-radius:12px; overflow:hidden; display:flex; flex-direction:column; text-decoration:none; color:var(--text); transition:transform .15s ease, border-color .15s ease}
-.card:hover, .card:focus-visible{transform:translateY(-3px); border-color:var(--accent)}
-.card .thumb{aspect-ratio:4/3; background:var(--chrome) center/cover no-repeat; display:flex; align-items:center; justify-content:center; font-size:2.4rem; position:relative}
-.card .body{padding:12px 14px; display:flex; flex-direction:column; gap:6px; min-width:0}
-.card .cat-badge{
-  align-self:flex-start;
-  font-size:.68rem; font-weight:800; letter-spacing:.3px; text-transform:uppercase;
-  padding:.15rem .5rem; border-radius:999px;
-  background:var(--cat-accent, var(--accent)); color:var(--chrome);
-}
-.card .title{font-weight:700; font-size:1rem}
-.card .meta{font-size:.75rem; color:var(--text-dim)}
-.card .blurb{font-size:.85rem; color:var(--text-dim)}
-.grid.list-mode{display:flex; flex-direction:column; gap:10px}
-.grid.list-mode .card{flex-direction:row; align-items:stretch}
-.grid.list-mode .card:hover, .grid.list-mode .card:focus-visible{transform:translateX(3px)}
-.grid.list-mode .card .thumb{aspect-ratio:auto; width:120px; min-width:120px; font-size:1.6rem}
-.grid.list-mode .card .body{flex:1; justify-content:center; min-width:0; overflow:hidden}
-.grid.list-mode .card .title, .grid.list-mode .card .blurb{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
-@media (max-width:480px){
-  .grid.list-mode .card .thumb{width:72px; min-width:72px; font-size:1.2rem}
-  .grid.list-mode .card .body{padding:10px}
-}
-.empty{background:var(--panel); border:1px dashed var(--panel-border); border-radius:12px; padding:40px 20px; text-align:center; color:var(--text-dim)}
-.empty a{display:inline-block; margin-top:10px}
-footer{max-width:1200px; margin:30px auto; padding:0 20px 30px; color:var(--text-dim); font-size:.75rem}
-@media (prefers-reduced-motion: reduce){ .card{transition:none} body{transition:none} }
-</style>
+<link rel="stylesheet" href="../assets/style.css" />
 </head>
 <body data-mood="all">
 <header class="chrome">
diff --git a/index.html b/index.html
index 3cb147f..a1097f5 100644
--- a/index.html
+++ b/index.html
@@ -6,816 +6,7 @@
 <title>PANDEMONIUM-24 — Wall-to-Wall Chaos Coverage</title>
 <meta name="description" content="A satirical invented 24-hour news channel with escalating breaking stories, a live ticker, a BREAKING klaxon, and a category filter that retunes the whole channel's mood." />
 
-<style>
-/* ============================================================
-   PANDEMONIUM-24 — styles
-   Sections: 1. Reset & base  2. Mood theming (CSS custom props)
-   3. Chrome (header/nav)     4. Breaking klaxon overlay
-   5. Story grid & cards      6. Ticker (motion + reduced-motion)
-   7. Empty / completion states  8. Focus & a11y helpers
-   9. Responsive breakpoints  10. Reduced-motion overrides
-   ============================================================ */
-
-/* ---------- 1. Reset & base ---------- */
-*, *::before, *::after { box-sizing: border-box; }
-html, body { margin: 0; padding: 0; }
-/* overflow-x on body alone is not enough: with html's overflow unset, the
-   UA propagates body's overflow to the viewport's scroller, but
-   document.documentElement.scrollWidth can still report the wider,
-   unclipped value (verified empirically). Setting it on html too stops the
-   propagation and clips scrollWidth for real, everywhere. */
-html { overflow-x: hidden; }
-/* The [hidden] attribute must always win over any class-based `display`
-   rule below (several elements here are toggled via `.hidden = true/false`
-   in JS while also carrying a class that sets display:flex/grid). Without
-   this, equal-specificity cascade order lets the class rule beat the UA
-   [hidden] stylesheet and the element stays visible. */
-[hidden] { display: none !important; }
-body {
-  min-height: 100vh;
-  font-family: -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
-  color: var(--text);
-  background: radial-gradient(circle at 20% -10%, var(--bg2), var(--bg) 60%);
-  transition: background-color .5s ease, color .5s ease;
-  line-height: 1.4;
-  /* reserve room for the fixed chyron ticker so it never overlaps content */
-  padding-bottom: 3rem;
-  /* Safety net: the chaos-tier-3 TV-bezel tilt rotates a very tall element
-     (main#channelContainer), and rotating a tall box even a fraction of a
-     degree widens its visual bounding box (width*cos + height*sin) beyond
-     the viewport — confirmed via CSSOM bisection to be the sole cause of a
-     real horizontal scrollbar at 390-1024px at max chaos. overflow-x:hidden
-     here clips that leak (vertical/position:sticky is untouched) without
-     changing the tilt's visible effect or any layout. */
-  overflow-x: hidden;
-}
-h1, h2, h3, p { margin: 0; }
-button { font-family: inherit; }
-img, svg { max-width: 100%; display: block; }
-
-.visually-hidden {
-  position: absolute !important;
-  width: 1px; height: 1px;
-  padding: 0; margin: -1px;
-  overflow: hidden;
-  clip: rect(0 0 0 0);
-  white-space: nowrap;
-  border: 0;
-}
-
-/* ---------- 2. Mood theming — CSS custom properties ----------
-   Default ("all") is the pale mint/green salvaged from the original
-   seed attempt. Every other category swaps the whole palette. */
-:root, body[data-mood="all"] {
-  --bg: #cdf5e0;
-  --bg2: #f2fff2;
-  --panel: #ffffff;
-  --panel-border: #0f9d7833;
-  --accent: #0f9d78;
-  --accent2: #056659;
-  --text: #123524;
-  --text-dim: #12352499;
-  --chrome: #ffffff;
-  --focus: #0b3d91;
-  --mood-label: "Calm-ish (Default)";
-}
-body[data-mood="politics"] {
-  --bg: #2a0b0b; --bg2: #4a0f0f; --panel: #3a1212; --panel-border: #ff3b3b55;
-  --accent: #ff3b3b; --accent2: #ffb703; --text: #ffe9e9; --text-dim: #ffd7d7aa;
-  --chrome: #220808; --focus: #ffd166;
-}
-body[data-mood="weather"] {
-  --bg: #071a33; --bg2: #0c2b52; --panel: #0e2340; --panel-border: #4fd1ff55;
-  --accent: #4fd1ff; --accent2: #a0e9ff; --text: #eaf6ff; --text-dim: #cfeeffaa;
-  --chrome: #05132c; --focus: #ffe066;
-}
-body[data-mood="sports"] {
-  --bg: #2b1400; --bg2: #4a2200; --panel: #3a1c00; --panel-border: #ff980055;
-  --accent: #ff9800; --accent2: #ffd166; --text: #fff3e0; --text-dim: #ffe0b3aa;
-  --chrome: #200f00; --focus: #4fd1ff;
-}
-body[data-mood="entertainment"] {
-  --bg: #2a0a2e; --bg2: #4a0f52; --panel: #3a1140; --panel-border: #ff5fd155;
-  --accent: #ff5fd1; --accent2: #b388ff; --text: #fbe9ff; --text-dim: #eecbffaa;
-  --chrome: #210822; --focus: #ffe066;
-}
-body[data-mood="business"] {
-  --bg: #08210f; --bg2: #0e3a1c; --panel: #0d2c16; --panel-border: #2ecc7155;
-  --accent: #2ecc71; --accent2: #f1c40f; --text: #eafff0; --text-dim: #c9f5d6aa;
-  --chrome: #06180c; --focus: #f1c40f;
-}
-body[data-mood="scitech"] {
-  --bg: #0a0a23; --bg2: #170b3d; --panel: #140c33; --panel-border: #00f0ff55;
-  --accent: #00f0ff; --accent2: #ff00e6; --text: #eafcff; --text-dim: #c9f3ffaa;
-  --chrome: #08081c; --focus: #ff00e6;
-}
-body[data-mood="uncategorized"] {
-  --bg: #e9edf0; --bg2: #ffffff; --panel: #ffffff; --panel-border: #54657833;
-  --accent: #546578; --accent2: #2f3b47; --text: #20272d; --text-dim: #20272d99;
-  --chrome: #ffffff; --focus: #0b3d91;
-}
-
-/* ---------- 3. Chrome (header / nav) ---------- */
-.chrome {
-  position: sticky; top: 0; z-index: 20;
-  display: flex; flex-wrap: wrap; align-items: center; gap: .75rem 1.25rem;
-  padding: .75rem 1rem;
-  background: var(--chrome);
-  /* No backdrop-filter: combined with position:sticky it caused WebKit/Safari
-     to ghost/bleed scrolled-past content through the header during scroll
-     (CTA-verified, cta/webkit-bug-repro/). --chrome is now fully opaque so
-     no blur was doing visible work anyway. translateZ forces a stable
-     compositing layer as cheap insurance against the same class of bug. */
-  transform: translateZ(0);
-  border-bottom: 2px solid var(--panel-border);
-}
-.brand { display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; color: inherit; text-decoration: none; }
-.brand:hover .wordmark { text-decoration: underline; }
-.logo-badge {
-  display: inline-flex; align-items: center; justify-content: center;
-  width: 2.4rem; height: 2.4rem; border-radius: .4rem;
-  background: var(--accent); color: var(--bg);
-  font-weight: 900; font-size: .95rem; letter-spacing: -1px;
-  box-shadow: 0 0 0 2px var(--accent2);
-}
-.wordmark { font-size: clamp(1.1rem, 1.5vw + .8rem, 1.6rem); font-weight: 800; letter-spacing: .5px; }
-.tagline { font-size: .72rem; color: var(--text-dim); font-style: italic; }
-
-.live-badge {
-  display: inline-flex; align-items: center; gap: .4rem;
-  font-weight: 700; font-size: .8rem; letter-spacing: .5px;
-  padding: .3rem .6rem; border-radius: 999px;
-  border: 1px solid var(--panel-border);
-  background: color-mix(in srgb, var(--panel) 70%, transparent);
-}
-.rec-dot {
-  width: .55rem; height: .55rem; border-radius: 50%;
-  background: #ff3b3b;
-  box-shadow: 0 0 6px 1px #ff3b3b;
-  animation: rec-blink 1.4s steps(1) infinite;
-}
-@keyframes rec-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: .25; } }
-
-.controls { display: flex; align-items: center; gap: .75rem; margin-left: auto; flex-wrap: wrap; }
-
-.klaxon-btn {
-  cursor: pointer;
-  border: 2px solid var(--accent2);
-  background: var(--panel);
-  color: var(--text);
-  font-weight: 800;
-  font-size: .85rem;
-  padding: .55rem .9rem;
-  border-radius: .5rem;
-  letter-spacing: .3px;
-  transition: transform .15s ease, background-color .2s ease, color .2s ease;
-}
-.klaxon-btn:hover { transform: translateY(-1px); }
-.klaxon-btn[aria-pressed="true"] {
-  background: #ff2222; border-color: #ffd166; color: #fff;
-  animation: klaxon-btn-pulse 1s ease-in-out infinite;
-}
-@keyframes klaxon-btn-pulse {
-  0%, 100% { box-shadow: 0 0 0 0 #ff222266; }
-  50% { box-shadow: 0 0 0 8px #ff222200; }
-}
-
-.sound-toggle { display: inline-flex; align-items: center; gap: .4rem; font-size: .78rem; color: var(--text-dim); cursor: pointer; }
-.sound-toggle input { width: 1rem; height: 1rem; accent-color: var(--accent); cursor: pointer; }
-.audio-note { font-size: .7rem; color: var(--text-dim); max-width: 16rem; }
-
-/* Admin toggle — deliberately NOT themed by mood vars: fixed slate/mono
-   "control room" look so it never reads as part of the in-universe broadcast. */
-.admin-toggle {
-  cursor: pointer;
-  border: 1.5px dashed #6b7280;
-  background: #20242b; color: #d7dee5;
-  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
-  font-size: .72rem; font-weight: 700; letter-spacing: .3px;
-  padding: .5rem .75rem; border-radius: .4rem;
-  transition: background-color .15s ease, border-color .15s ease, color .15s ease;
-}
-.admin-toggle:hover { background: #2a2f37; }
-.admin-toggle[aria-expanded="true"] { background: #343a40; border-style: solid; border-color: #7dd3fc; color: #7dd3fc; }
-
-/* ---------- category filter (nav) ---------- */
-.category-filter { padding: .6rem 1rem; border-bottom: 1px solid var(--panel-border); }
-.category-back-link { display: inline-block; font-size: .8rem; font-weight: 700; color: var(--accent2); margin-bottom: .3rem; text-decoration: none; }
-.category-back-link:hover { text-decoration: underline; }
-.category-blurb { color: var(--text-dim); font-size: .88rem; margin-top: .2rem; }
-.category-filter fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
-.chip-row {
-  display: flex; gap: .5rem; overflow-x: auto; padding-bottom: .25rem;
-  scrollbar-width: thin; min-width: 0;
-}
-.chip-row::-webkit-scrollbar { height: 6px; }
-.chip-row::-webkit-scrollbar-thumb { background: var(--panel-border); border-radius: 4px; }
-
-.chip-item { position: relative; flex: 0 0 auto; }
-.chip-item input { position: absolute; opacity: 0; width: 1px; height: 1px; }
-.chip-item label {
-  display: inline-block;
-  padding: .45rem .9rem;
-  border-radius: 999px;
-  border: 1.5px solid var(--panel-border);
-  background: var(--panel);
-  color: var(--text);
-  font-size: .8rem; font-weight: 700; letter-spacing: .2px;
-  cursor: pointer; white-space: nowrap;
-  transition: background-color .2s ease, color .2s ease, transform .12s ease;
-}
-.chip-item label:hover { transform: translateY(-1px); }
-.chip-item input:checked + label {
-  background: var(--accent); color: var(--bg); border-color: var(--accent2);
-}
-.chip-item input:focus-visible + label {
-  outline: 3px solid var(--focus); outline-offset: 2px;
-}
-
-/* ---------- 4. Breaking klaxon overlay ---------- */
-.breaking-banner {
-  position: relative;
-  margin: 0 1rem; margin-top: .75rem;
-  padding: .75rem 1rem;
-  background: linear-gradient(90deg, #b30000, #ff3b3b, #b30000);
-  background-size: 200% 100%;
-  color: #fff;
-  font-weight: 900;
-  border-radius: .4rem;
-  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
-  border: 2px solid #ffd166;
-  box-shadow: 0 0 24px 2px #ff000055;
-  animation: breaking-sweep 1.4s linear infinite, breaking-flash 0.6s ease-in-out infinite;
-}
-@keyframes breaking-sweep { 0% { background-position: 0% 0; } 100% { background-position: 200% 0; } }
-@keyframes breaking-flash { 0%, 100% { filter: brightness(1); } 50% { filter: brightness(1.35); } }
-.breaking-banner .bb-icon { font-size: 1.2rem; }
-.breaking-banner .bb-sub { font-weight: 600; font-size: .82rem; opacity: .95; }
-
-/* full-screen edge vignette while breaking is active */
-body.breaking-mode::before {
-  content: "";
-  position: fixed; inset: 0; z-index: 15; pointer-events: none;
-  box-shadow: inset 0 0 0 6px #ff2222, inset 0 0 60px 10px #ff222266;
-  animation: vignette-pulse 1s ease-in-out infinite;
-}
-@keyframes vignette-pulse { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }
-
-/* ---------- 5. Story grid & cards ---------- */
-main#channelContainer {
-  position: relative;
-  max-width: 1200px;
-  margin: 1rem auto 0;
-  padding: 1rem 1rem 1.5rem;
-  border-radius: .75rem;
-  background: color-mix(in srgb, var(--panel) 55%, transparent);
-  box-shadow: inset 0 0 0 1px var(--panel-border), inset 0 40px 80px -40px #00000055;
-  overflow: hidden;
-}
-/* subtle static CRT scanline texture — no motion, safe under reduced-motion */
-main#channelContainer::after {
-  content: "";
-  position: absolute; inset: 0; pointer-events: none;
-  background: repeating-linear-gradient(
-    to bottom, rgba(255,255,255,0.035) 0px, rgba(255,255,255,0.035) 1px,
-    transparent 2px, transparent 4px
-  );
-  mix-blend-mode: overlay;
-}
-
-.channel-meta { display: flex; justify-content: space-between; align-items: baseline; gap: .5rem; flex-wrap: wrap; margin-bottom: .75rem; }
-.channel-meta h2 { font-size: .95rem; letter-spacing: 1px; text-transform: uppercase; color: var(--accent); }
-.mood-readout { font-size: .75rem; color: var(--text-dim); }
-
-.story-grid {
-  display: grid;
-  grid-template-columns: 1fr;
-  gap: 1rem;
-}
-.story-card {
-  position: relative;
-  background: var(--panel);
-  border: 1px solid var(--panel-border);
-  border-radius: .6rem;
-  padding: 1rem;
-  display: flex; flex-direction: column; gap: .55rem;
-  box-shadow: 0 4px 18px -10px #00000066;
-}
-.story-photo {
-  display: block; width: calc(100% + 2rem); max-width: none; height: auto;
-  aspect-ratio: 800 / 457; object-fit: cover;
-  margin: -1rem -1rem .2rem; border-radius: .6rem .6rem 0 0;
-  background: var(--panel-border);
-}
-.story-card.concluded .story-photo { filter: grayscale(.6); }
-.article-photo img { width: 100%; height: auto; border-radius: .5rem; display: block; }
-/* Cartoon-thumb base style (card-view / "All Categories" default — the
-   compact inline-link treatment for newspaper-mode overrides this above). A
-   politics story with a linked cartoon shows this INSTEAD OF a photo, same
-   banner slot, so it never looks like a missing image. */
-.story-cartoon-thumb {
-  display: flex; align-items: center; gap: .6rem;
-  width: calc(100% + 2rem); margin: -1rem -1rem .2rem; padding: 1.1rem 1rem;
-  border-radius: .6rem .6rem 0 0;
-  background: color-mix(in srgb, var(--accent) 12%, var(--panel));
-  border-bottom: 1px solid var(--panel-border);
-  text-decoration: none; color: var(--accent2);
-  font-weight: 700; font-size: .95rem;
-}
-.story-cartoon-thumb:hover, .story-cartoon-thumb:focus-visible { background: color-mix(in srgb, var(--accent) 20%, var(--panel)); }
-.story-cartoon-thumb .ct-icon { font-size: 1.3rem; }
-.story-card.concluded .story-cartoon-thumb { filter: grayscale(.5); }
-.story-card.lead { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent), 0 8px 24px -8px #00000077; }
-.story-card .kicker { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
-.badge-cat {
-  font-size: .68rem; font-weight: 800; letter-spacing: .5px; text-transform: uppercase;
-  padding: .18rem .5rem; border-radius: 999px; background: var(--accent); color: var(--bg);
-}
-.badge-lead {
-  font-size: .68rem; font-weight: 800; letter-spacing: .5px; text-transform: uppercase;
-  padding: .18rem .5rem; border-radius: 999px; background: var(--accent2); color: #fff;
-}
-.story-card h3 { font-size: clamp(1rem, .4vw + 1rem, 1.35rem); line-height: 1.25; }
-.story-card.lead h3 { font-size: clamp(1.25rem, 1.2vw + 1rem, 1.9rem); }
-.story-detail { font-size: .9rem; color: var(--text-dim); }
-.story-meta { font-size: .74rem; color: var(--text-dim); display: flex; gap: .5rem; flex-wrap: wrap; }
-
-.escalation {
-  display: flex; align-items: center; gap: .5rem; margin-top: .2rem;
-}
-.escalation .level-label { font-size: .72rem; font-weight: 800; letter-spacing: .5px; text-transform: uppercase; }
-.escalation .bar { display: flex; gap: 3px; }
-.escalation .seg { width: 1.1rem; height: .4rem; border-radius: 2px; background: var(--panel-border); }
-.escalation .seg.on { background: var(--accent); }
-.story-card.concluded { opacity: .96; }
-.concluded-ribbon {
-  align-self: flex-start;
-  font-size: .7rem; font-weight: 800; letter-spacing: .5px; text-transform: uppercase;
-  padding: .2rem .55rem; border-radius: .3rem;
-  background: var(--text); color: var(--bg);
-}
-
-/* ---------- 5b. Category-view "Newspaper Column" (V4) ----------
-   Applied only when state.category !== "all" (a specific category route,
-   e.g. #/category/scitech) via .story-grid.newspaper-mode, toggled in
-   renderStories(). Restyles the SAME storyCardHTML() markup used by the "All
-   Categories" card stack — no markup fork, so patchStoryGrid()'s keyed morph
-   reconcile never has to bridge a shape change from this toggle alone. True
-   CSS multi-column flow (column-count), inline category label instead of the
-   badge pill, headline-only density (detail paragraph hidden), a left rail
-   whose height reads escalation, and a compact photo/cartoon thumbnail. */
-.np-cat-label { display: none; }
-.story-grid.newspaper-mode {
-  display: block;
-  column-gap: 1.5rem;
-  column-count: 1;
-}
-@media (min-width: 640px) { .story-grid.newspaper-mode { column-count: 2; } }
-@media (min-width: 1024px) { .story-grid.newspaper-mode { column-count: 3; } }
-@media (min-width: 1440px) { .story-grid.newspaper-mode { column-count: 4; } }
-.story-grid.newspaper-mode .story-card {
-  break-inside: avoid;
-  display: block;
-  margin: 0 0 .9rem;
-  padding: 0 0 .1rem .7rem;
-  border: 0;
-  border-left: 3px solid var(--panel-border);
-  border-radius: 0;
-  box-shadow: none;
-}
-.story-grid.newspaper-mode .story-card.lead { border-left-color: var(--accent); border-left-width: 4px; }
-.story-grid.newspaper-mode .story-card.concluded { border-left-color: var(--text-dim); }
-.story-grid.newspaper-mode .story-card .kicker { display: none; }
-.story-grid.newspaper-mode .story-card .np-cat-label {
-  display: block;
-  font: 800 .62rem/1 -apple-system, sans-serif;
-  text-transform: uppercase; letter-spacing: .05em;
-  color: var(--accent); margin-bottom: .2rem;
-}
-.story-grid.newspaper-mode .story-card.concluded .np-cat-label { color: var(--text-dim); }
-.story-grid.newspaper-mode .story-card h3 {
-  font-family: Georgia, "Times New Roman", serif;
-  font-size: 1rem; line-height: 1.22; margin-bottom: .25rem;
-}
-.story-grid.newspaper-mode .story-card.lead h3 { font-size: 1.3rem; font-style: italic; }
-.story-grid.newspaper-mode .story-card .story-detail,
-.story-grid.newspaper-mode .story-card .story-read-more { display: none; }
-.story-grid.newspaper-mode .story-card .escalation { margin: .3rem 0 0; gap: .35rem; }
-.story-grid.newspaper-mode .story-card .escalation .level-label { display: none; }
-.story-grid.newspaper-mode .story-card .escalation .seg { width: .55rem; height: .18rem; }
-.story-grid.newspaper-mode .story-card .story-meta {
-  font: 500 .66rem/1.3 ui-monospace, Menlo, monospace;
-  color: var(--text-dim); gap: .45rem; margin-top: .25rem;
-}
-.story-grid.newspaper-mode .story-card .story-meta span:last-child { display: none; } /* drop the "Updated HH:MM" clause — density */
-.story-grid.newspaper-mode .story-card .story-photo {
-  float: left; width: 56px; height: 56px; margin: 0 .55rem .3rem 0;
-  border-radius: .3rem; aspect-ratio: 1/1; object-fit: cover;
-}
-.story-grid.newspaper-mode .story-card .story-cartoon-thumb {
-  display: flex; align-items: center; gap: .4rem;
-  font: 700 .7rem/1.2 -apple-system, sans-serif;
-  color: var(--accent2); text-decoration: none;
-  border: 1px dashed var(--panel-border); border-radius: .35rem;
-  padding: .3rem .5rem; margin-bottom: .3rem;
-}
-.story-grid.newspaper-mode .story-card .story-cartoon-thumb:hover,
-.story-grid.newspaper-mode .story-card .story-cartoon-thumb:focus-visible { border-color: var(--accent); }
-.story-grid.newspaper-mode .story-card .ct-icon { font-size: .95rem; }
-
-/* ---------- 6. Ticker ---------- */
-.ticker-wrap {
-  position: fixed; left: 0; right: 0; bottom: 0; z-index: 20;
-  display: flex; align-items: stretch;
-  background: var(--chrome);
-  border-top: 2px solid var(--panel-border);
-  height: 3rem;
-}
-body.breaking-mode .ticker-wrap { border-top-color: #ff3b3b; }
-.ticker-label {
-  flex: 0 0 auto;
-  display: flex; align-items: center;
-  padding: 0 .8rem;
-  font-weight: 900; font-size: .78rem; letter-spacing: .5px;
-  background: var(--accent); color: var(--bg);
-}
-body.breaking-mode .ticker-label { background: #ff2222; color: #fff; }
-.ticker-scroll { position: relative; overflow: hidden; flex: 1 1 auto; min-width: 0; display: flex; align-items: center; }
-.ticker-track {
-  display: inline-flex; white-space: nowrap; gap: 3rem; padding-right: 3rem;
-  will-change: transform;
-  animation: ticker-scroll 38s linear infinite;
-  font-size: .85rem; font-weight: 600;
-}
-body.breaking-mode .ticker-track { color: #ff5959; font-weight: 800; }
-@keyframes ticker-scroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
-.ticker-static {
-  flex: 1 1 auto; min-width: 0; display: flex; align-items: center; padding: 0 .9rem;
-  font-size: .85rem; font-weight: 700;
-  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
-}
-
-/* ---------- 7. Empty / completion states ---------- */
-.empty-state, .meltdown-banner {
-  text-align: center; padding: 2.5rem 1rem; border: 2px dashed var(--panel-border);
-  border-radius: .6rem; color: var(--text-dim);
-}
-.empty-state .es-emoji, .meltdown-banner .mb-emoji { font-size: 2rem; display: block; margin-bottom: .5rem; }
-.empty-state h3, .meltdown-banner h3 { color: var(--text); margin-bottom: .35rem; }
-.meltdown-banner {
-  margin-top: 1rem; border-color: var(--accent); border-style: solid;
-  background: color-mix(in srgb, var(--accent) 12%, var(--panel));
-}
-.story-card.just-added { outline: 3px dashed var(--accent); outline-offset: 2px; }
-
-/* ---------- 7b. Admin panel (backstage / control-room, not on-air) ----------
-   Deliberately fixed slate/mono palette independent of --mood vars so it
-   never gets confused with the in-universe broadcast chrome. Renders in
-   normal document flow (an expand/collapse section, not an overlay) so it
-   can never overlap content or fight the ticker/vignette z-index stack. */
-.admin-panel {
-  max-width: 1200px;
-  margin: 1rem auto 0;
-  padding: 1rem 1.25rem 1.25rem;
-  border-radius: .6rem;
-  background: #1b1f24;
-  color: #e8eaed;
-  border: 2px dashed #4a5057;
-  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
-}
-.admin-panel h2 { font-size: .95rem; letter-spacing: .5px; text-transform: uppercase; color: #7dd3fc; margin-bottom: .15rem; }
-.admin-panel h3 { font-size: .78rem; letter-spacing: .4px; text-transform: uppercase; color: #b8c0c8; margin-bottom: .5rem; }
-.admin-hint { font-size: .72rem; color: #9aa4ad; margin-bottom: 1rem; font-style: italic; }
-.admin-panel hr { border: none; border-top: 1px dashed #3a4048; margin: 1.1rem 0; }
-
-.admin-form { display: grid; gap: .5rem; }
-.admin-form label { font-size: .7rem; text-transform: uppercase; letter-spacing: .4px; color: #b8c0c8; }
-.admin-form input[type="text"], .admin-form select {
-  width: 100%; padding: .5rem .6rem; border-radius: .35rem;
-  border: 1.5px solid #4a5057; background: #262b31; color: #f2f4f6;
-  font-family: inherit; font-size: .85rem;
-}
-.admin-form input[aria-invalid="true"] { border-color: #ff6b6b; }
-.admin-form input[type="text"]:focus-visible,
-.admin-form select:focus-visible,
-.admin-panel button:focus-visible {
-  outline: 3px solid #7dd3fc; outline-offset: 2px;
-}
-.admin-error { font-size: .78rem; color: #ff8b8b; font-weight: 700; }
-.admin-success { font-size: .78rem; color: #7dffb0; font-weight: 700; }
-.admin-submit-btn {
-  justify-self: start; margin-top: .2rem;
-  cursor: pointer; border: none; border-radius: .35rem;
-  padding: .55rem 1.1rem; font-weight: 800; font-size: .82rem;
-  background: #7dd3fc; color: #0b1a24;
-}
-.admin-reset-btn {
-  justify-self: start; margin-top: 1rem;
-  cursor: pointer; border: 1.5px solid #4a5057; border-radius: .35rem;
-  padding: .5rem 1rem; font-weight: 700; font-size: .78rem;
-  background: transparent; color: #e8eaed;
-}
-.admin-reset-btn:hover { background: #262b31; }
-
-.admin-refresh-row { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; margin-top: .6rem; }
-.admin-refresh-btn {
-  justify-self: start;
-  cursor: pointer; border: 1.5px solid #7dd3fc; border-radius: .35rem;
-  padding: .5rem 1rem; font-weight: 700; font-size: .78rem;
-  background: transparent; color: #7dd3fc;
-}
-.admin-refresh-btn:hover { background: #7dd3fc22; }
-.admin-refresh-btn:disabled { opacity: .55; cursor: wait; }
-.admin-refresh-status { font-size: .75rem; color: #9aa4ad; }
-.admin-refresh-status.is-error { color: #ff8b8b; }
-.admin-refresh-status.is-ok { color: #8ce99a; }
-
-.admin-story-list { list-style: none; margin: 0 0 .5rem; padding: 0; display: grid; gap: .5rem; max-height: 16rem; overflow-y: auto; }
-.admin-story-row {
-  /* min-width: 0 overrides the browser's implicit min-width:auto on grid
-     items — without it this row (and its nowrap/ellipsis child text) won't
-     shrink below its content's min-content width, pushing the Delete
-     button off-screen at narrow viewports (the same class of bug as the
-     ticker/fieldset overflow fixed earlier in this project). */
-  min-width: 0;
-  display: flex; align-items: center; justify-content: space-between; gap: .75rem;
-  padding: .5rem .6rem; border: 1px solid #343a40; border-radius: .4rem; background: #22262b;
-}
-.admin-story-info { flex: 1 1 auto; display: flex; flex-direction: column; gap: .15rem; min-width: 0; }
-.admin-story-cat { font-size: .62rem; text-transform: uppercase; letter-spacing: .4px; color: #7dd3fc; }
-.admin-story-headline { font-size: .8rem; color: #e8eaed; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
-.admin-delete-btn {
-  flex: 0 0 auto; cursor: pointer; border: 1.5px solid #ff6b6b; background: transparent;
-  color: #ff8b8b; border-radius: .35rem; padding: .35rem .6rem; font-weight: 700; font-size: .72rem;
-}
-.admin-delete-btn:hover { background: #ff6b6b22; }
-.admin-empty { font-size: .8rem; color: #9aa4ad; padding: .5rem 0; }
-@media (min-width: 768px) {
-  .admin-panel-body { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; align-items: start; }
-}
-.admin-form textarea {
-  width: 100%; padding: .5rem .6rem; border-radius: .35rem;
-  border: 1.5px solid #4a5057; background: #262b31; color: #f2f4f6;
-  font-family: inherit; font-size: .85rem; resize: vertical; min-height: 6rem;
-}
-.admin-form-hint { font-size: .68rem; color: #9aa4ad; margin-top: -.25rem; }
-
-/* ---------- 7c. Feature articles ("More From PANDEMONIUM-24") ---------- */
-.features-section {
-  max-width: 1200px;
-  margin: 1.25rem auto 0;
-  padding: 1rem 1rem 1.5rem;
-}
-.features-grid { display: grid; grid-template-columns: 1fr; gap: 1rem; }
-.feature-card {
-  background: var(--panel);
-  border: 1px solid var(--panel-border);
-  border-radius: .6rem;
-  padding: 1rem;
-  display: flex; flex-direction: column; gap: .5rem;
-  box-shadow: 0 4px 18px -10px #00000066;
-}
-.feature-card h3 { font-size: 1.05rem; line-height: 1.3; }
-.features-empty-note { color: var(--text-dim); font-size: .85rem; padding: .5rem 0; }
-@media (min-width: 768px) { .features-grid { grid-template-columns: repeat(2, 1fr); } }
-@media (min-width: 1024px) { .features-grid { grid-template-columns: repeat(3, 1fr); } }
-
-/* Shared "open article" link treatment (story cards + feature cards) */
-.story-card h3 a, .feature-card h3 a { color: inherit; text-decoration: none; }
-.story-card h3 a:hover, .feature-card h3 a:hover { text-decoration: underline; }
-.story-read-more {
-  align-self: flex-start; margin-top: .1rem;
-  font-size: .82rem; font-weight: 700; color: var(--accent2);
-  text-decoration: none;
-}
-.story-read-more:hover { text-decoration: underline; }
-
-/* ---------- 7d. Article view (hash-routed, #/article/<id>) ----------
-   Long-form reading layout: comfortable measure (~65ch), generous
-   line-height, still inherits the current category mood's palette. */
-.article-view { max-width: 100%; }
-.article-inner {
-  max-width: 68ch;
-  margin: 1rem auto 0;
-  padding: 1.25rem 1.25rem 2rem;
-  background: var(--panel);
-  border-radius: .75rem;
-  box-shadow: inset 0 0 0 1px var(--panel-border), inset 0 40px 80px -40px #00000055;
-  color: var(--text);
-}
-.article-back-btn {
-  cursor: pointer; border: 1.5px solid var(--panel-border); background: transparent;
-  color: var(--text); font-weight: 700; font-size: .85rem;
-  padding: .5rem .8rem; border-radius: .4rem; margin-bottom: 1.1rem;
-}
-.article-back-btn:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
-.article-cat-badge { display: inline-block; margin-bottom: .6rem; }
-.article-headline { font-size: clamp(1.4rem, 1.6vw + 1rem, 2.1rem); line-height: 1.2; margin-bottom: .5rem; }
-.article-dek { font-size: 1.05rem; color: var(--text-dim); line-height: 1.5; margin-bottom: .9rem; }
-.article-meta-row {
-  font-size: .8rem; color: var(--text-dim); font-weight: 700;
-  display: flex; gap: .75rem; flex-wrap: wrap;
-  padding-bottom: .85rem; margin-bottom: 1.1rem;
-  border-bottom: 1px dashed var(--panel-border);
-}
-.article-dateline { letter-spacing: .3px; }
-.article-photo { margin: 0 0 1.25rem; }
-.article-photo svg { width: 100%; height: auto; border-radius: .5rem; display: block; }
-.article-photo figcaption { font-size: .78rem; color: var(--text-dim); font-style: italic; margin-top: .45rem; }
-.article-body p {
-  margin: 0 0 1.1rem; font-size: 1.02rem; line-height: 1.75; max-width: 65ch;
-}
-.article-body p.kicker { font-style: italic; color: var(--text-dim); }
-.live-update-block {
-  border: 2px dashed var(--accent); border-radius: .5rem;
-  padding: .8rem 1rem; margin: 0 0 1.25rem;
-  background: color-mix(in srgb, var(--accent) 10%, var(--panel));
-}
-.live-update-label {
-  display: block; font-weight: 900; font-size: .74rem; letter-spacing: .5px;
-  text-transform: uppercase; color: var(--accent2); margin-bottom: .4rem;
-}
-.live-update-headline { font-weight: 800; margin-bottom: .3rem; }
-.live-update-detail { color: var(--text-dim); font-size: .92rem; }
-.live-update-concluded { margin-top: .5rem; }
-.article-source-link { margin-top: 1rem; }
-.article-source-link a {
-  display: inline-block;
-  color: var(--accent);
-  font-weight: 800;
-  text-decoration: none;
-  border: 1px solid var(--accent);
-  border-radius: 6px;
-  padding: .5rem .9rem;
-}
-.article-source-link a:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
-.related-section { margin-top: 1.5rem; padding-top: 1rem; border-top: 1px dashed var(--panel-border); }
-.related-section h3 { font-size: .85rem; text-transform: uppercase; letter-spacing: .5px; color: var(--accent); margin-bottom: .6rem; }
-.related-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
-.related-list a { color: var(--accent2); font-weight: 700; }
-.article-not-found { text-align: center; }
-.article-not-found h2 { margin-bottom: .5rem; }
-@media (min-width: 768px) { .article-inner { padding: 1.75rem 2rem 2.25rem; } }
-@media (min-width: 1024px) { .article-inner { padding: 2rem 2.5rem 2.5rem; } }
-
-/* ---------- 8. Focus & a11y helpers ---------- */
-a, button, input, [tabindex] { outline-offset: 2px; }
-:focus-visible {
-  outline: 3px solid var(--focus);
-  outline-offset: 2px;
-  border-radius: 2px;
-}
-.klaxon-btn:focus-visible { outline: 3px solid var(--focus); }
-
-/* ---------- 8b. CHAOS INDEX — global escalation gauge + broadcast decay ----------
-   One computed number (0-100) drives everything below via body.chaos-tier-0..3
-   classes + a couple of CSS custom properties. Every rule here targets the
-   chrome/body/container level, NEVER the story-card markup itself, so
-   patchStoryGrid()'s keyed reconcile never has to re-render a card just
-   because chaos ticked up (headline stamps are pure ::before content). */
-.chaos-row {
-  /* flex-basis 100% inside .chrome's flex-wrap:wrap always forces this onto
-     its own line, so its own width/content changes can never reflow the
-     brand/live-clock/controls line above it. flex-wrap:wrap on ITS OWN
-     children is the overflow safety valve at narrow widths (390px) — if the
-     fixed-width band/pct pieces don't fit alongside the track, they wrap to
-     a second line inside this row instead of ever overflowing horizontally. */
-  flex: 1 1 100%;
-  display: flex; flex-wrap: wrap; align-items: center; gap: .4rem .6rem;
-  padding: .4rem 0 0;
-  margin-top: .4rem;
-  border-top: 1px dashed var(--panel-border);
-  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
-  font-size: .72rem;
-}
-.chaos-tag { flex: 0 0 auto; font-weight: 900; letter-spacing: .6px; color: var(--text-dim); }
-/* Fixed min-width sized to the longest label ("Post-Journalism") so the
-   readout's own box never changes width as the label text changes length —
-   the one thing that could otherwise nudge the controls sitting above it. */
-.chaos-band { flex: 0 0 auto; min-width: 10.5rem; font-weight: 800; letter-spacing: .3px; color: var(--text); }
-.chaos-band-icon { margin-right: .3rem; }
-.chaos-track {
-  flex: 1 1 auto; max-width: 14rem; height: .55rem; border-radius: 999px;
-  background: var(--panel-border); overflow: hidden; position: relative;
-}
-.chaos-fill {
-  position: absolute; inset: 0 auto 0 0; width: 0%;
-  background: var(--accent); border-radius: inherit;
-  transition: width .5s ease, background-color .4s ease;
-}
-.chaos-pct { flex: 0 0 auto; min-width: 3ch; text-align: right; font-variant-numeric: tabular-nums; color: var(--text-dim); }
-
-body.chaos-tier-1 .chaos-fill { background: #e0a300; }
-body.chaos-tier-2 .chaos-fill { background: #ff8c00; }
-body.chaos-tier-3 .chaos-fill { background: #ff2222; }
-
-/* Ticker degradation: each tier shortens the scroll loop so the chyron
-   visibly speeds up. The reduced-motion path never uses this animation at
-   all (ticker-track animation is fully disabled + JS swaps to the static
-   frame renderer), so this is motion-safe by construction. */
-/* Duration is set from JS (setTickerSpeed) as track length / reading speed,
-   so the crawl moves at a constant, readable px/s however many headlines
-   are in the loop. 90s is only the pre-measure fallback. */
-.ticker-track { animation: ticker-scroll var(--ticker-dur, 90s) linear infinite; }
-
-/* Broadcast losing its grip: headline stamps via generated content only —
-   zero markup change, so these can flip every tick without ever touching
-   patchStoryGrid()'s diffing. */
-body.chaos-tier-1 .story-card:not(.concluded) h3 a::before,
-body.chaos-tier-2 .story-card:not(.concluded) h3 a::before,
-body.chaos-tier-3 .story-card:not(.concluded) h3 a::before {
-  content: "[DEVELOPING] "; color: var(--accent2); font-weight: 900;
-}
-
-/* Lower-third anchor caption — appears at tier 2+, cycles a fixed set of
-   "anchor notes" that get more unhinged per tier. position:fixed, so
-   showing/hiding it never reflows anything in normal document flow. */
-.anchor-caption {
-  position: fixed; left: 0; right: 0; bottom: 3rem; z-index: 19;
-  padding: .4rem .9rem;
-  background: color-mix(in srgb, var(--chrome) 88%, #000 12%);
-  color: var(--text);
-  border-top: 1px dashed var(--accent2);
-  font-size: .78rem; font-weight: 700; font-style: italic;
-  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
-}
-
-/* TV-bezel tilt — the container physically drifts a fraction of a degree at
-   max tier. Gated entirely behind (prefers-reduced-motion: no-preference)
-   so reduced-motion users NEVER get the transform; the unconditional
-   box-shadow rule right below is their static equivalent (a hot red edge
-   instead of a tilt). Only the broadcast container tilts — chrome/nav/admin
-   controls are untouched so their click targets never move. */
-body.chaos-tier-3 main#channelContainer {
-  box-shadow: inset 0 0 0 1px var(--panel-border), inset 0 40px 80px -40px #00000055, inset 0 0 0 3px #ff222277;
-}
-@media (prefers-reduced-motion: no-preference) {
-  main#channelContainer { transition: transform .6s ease; }
-  body.chaos-tier-3 main#channelContainer { transform: rotate(.6deg); }
-}
-
-/* PLEASE STAND BY — full-screen flash at chaos 100 (all stories concluded).
-   pointer-events:none the whole time, so it can cover the entire viewport
-   without ever blocking a click on the klaxon/chips/admin/article links
-   underneath it. JS never arms this under reduced motion — the existing
-   static #meltdownBanner text is that audience's equivalent. Blink cycle is
-   .7s (~1.4Hz, well under the 3-flashes-per-second ceiling) x4 then it's
-   hidden again by JS, so it's transient, not a permanent strobe. */
-.standby-flash {
-  position: fixed; inset: 0; z-index: 30; pointer-events: none;
-  display: flex; align-items: center; justify-content: center;
-  background: rgba(0,0,0,.6); color: #fff; text-align: center;
-  font-weight: 900; letter-spacing: 2px;
-  font-size: clamp(1.1rem, 1rem + 2vw, 2.2rem);
-}
-.standby-flash.flashing { animation: standby-blink .7s steps(1, end) 4; }
-@keyframes standby-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
-
-/* ---------- 9. Responsive breakpoints ---------- */
-@media (min-width: 560px) {
-  .controls { margin-left: 0; }
-}
-@media (min-width: 768px) {
-  .chrome { padding: 1rem 1.5rem; }
-  .category-filter { padding: .75rem 1.5rem; }
-  main#channelContainer { padding: 1.25rem 1.5rem 1.75rem; }
-  .admin-panel { padding: 1.25rem 1.5rem 1.75rem; }
-  .story-grid { grid-template-columns: repeat(2, 1fr); }
-  .story-card.lead { grid-column: 1 / -1; }
-}
-@media (min-width: 1024px) {
-  .chrome { padding: 1rem 2rem; }
-  .category-filter { padding: .75rem 2rem; }
-  main#channelContainer { padding: 1.5rem 2rem 2rem; }
-  .admin-panel { padding: 1.5rem 2rem 2rem; }
-  .story-grid { grid-template-columns: repeat(3, 1fr); align-items: stretch; }
-  .story-card.lead {
-    grid-column: 1 / -1;
-    display: grid; grid-template-columns: 3fr 2fr; column-gap: 1.5rem; row-gap: .55rem;
-    align-content: start;
-  }
-  .story-card.lead > * { grid-column: 2; }
-  .story-card.lead > .story-photo {
-    grid-column: 1; grid-row: 1 / span 8;
-    width: calc(100% + 1rem); margin: -1rem 0 -1rem -1rem;
-    height: calc(100% + 2rem); border-radius: .6rem 0 0 .6rem;
-  }
-}
-@media (min-width: 1280px) {
-  .wordmark { letter-spacing: .8px; }
-  .ticker-track { font-size: .92rem; }
-}
-
-/* ---------- 10. Reduced-motion overrides ---------- */
-@media (prefers-reduced-motion: reduce) {
-  * { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
-  .rec-dot { animation: none; opacity: 1; }
-  .klaxon-btn[aria-pressed="true"] { animation: none; box-shadow: 0 0 0 4px #ff222255; }
-  .breaking-banner { animation: none; background: #b30000; }
-  body.breaking-mode::before { animation: none; opacity: .8; }
-  .ticker-track { animation: none; }
-}
-</style>
+<link rel="stylesheet" href="assets/style.css" />
 </head>
 <body data-mood="all">
 

← c130b62 Backfill story_id links for 9 orphaned cartoons + codify lin  ·  back to Crazy News Channel Shadowman  ·  Animated Filibuster Marathon on P24 front page (WAN 2.2 i2v, 7bf67a3 →