. It must:
+
+1. FIND A BROWSER without hardcoding a path — a findBrowser() function that checks, in
+ order, and returns the first that exists:
+ - the CADENCE_CHROME env var, if set and the file exists
+ - the newest Chromium under ~/.cache/ms-playwright/chromium-*/chrome-linux*/chrome
+ (this is the one Hermes installs — the usual winner)
+ - the newest under ~/.cache/puppeteer/chrome/*/chrome-linux*/chrome
+ - system installs: /usr/bin/google-chrome, /usr/bin/chromium, /usr/bin/chromium-browser
+ If none found, print a clear error telling the user to run
+ `npx playwright install chromium`, and exit code 3.
+
+2. Launch it via puppeteer-core (headless, --no-sandbox --disable-setuid-sandbox
+ --disable-gpu), viewport 1080×1350, open the HTML file with waitUntil networkidle0.
+
+3. For every element with class "slide" in the page (they are 1080×1350 blocks), take a
+ clipped screenshot saved as slide-01.png, slide-02.png, … into the output dir. Use
+ page-absolute clip coordinates (getBoundingClientRect + window.scrollX/scrollY).
+
+4. Before screenshotting, if the page defines window.__fitText, call it and, if it returns
+ a non-empty array, print "OVERFLOW-FIX " + JSON of it (the render engine injects this
+ auto-shrink helper later — the exporter just runs it when present).
+
+Test: write a tiny test HTML with two .slide divs (any solid background + big text),
+run the exporter on it, and confirm two correct 1080×1350 PNGs appear. Report the browser
+path findBrowser() picked.
+```
+
+## Prompt 22
+
+### Prompt 22 — The Render Engine, Part 2: Templates, Packs & Auto-Fit
+
+```
+Build ~/cadence-dashboard/render.py — the render engine. Python stdlib only.
+
+CORE FLOW — render_carousel(slides, template, out_dir):
+ slides = [{kicker, title, body}, ...]. Build ONE self-contained HTML document with one
+ per slide (1080×1350 each), inline CSS, write it to
+ /carousel.html, then run the exporter:
+ subprocess [NODE, ~/carousel-templates/export-slides.js, carousel.html, out_dir].
+ NODE = shutil.which("node") or the Hermes-bundled ~/.hermes/node/bin/node.
+ Return {ok, count, fixes, stderr} — parse any "OVERFLOW-FIX [...]" line from the
+ exporter's stdout into `fixes`.
+
+ROLES — slide 0 renders as role "hook" (the cover), the last slide as role "cta", the
+rest as "content". Templates style the three roles differently (cover = biggest type).
+
+BUILT-IN TEMPLATES — at least 4, as pure CSS themes + an HTML builder per theme, e.g.:
+ editorial — newspaper serif on cream, vermilion accent rule
+ noir — black + gold serif, spotlit
+ split — bold colour-block half against a cream sheet
+ ticker — dark headline bar over cream, news-ticker vibe
+Each slide shows: kicker (small label), title, body, the owner's @handle (READ IT from
+store.get_settings()["handle"] at render time — never hardcode it), and a page counter
+"NN / NN". Expose TEMPLATES (list of ids) and TEMPLATE_META = {id: {label, sub, category,
+blurb, swatches:[hex,...]}} for the picker UI later. Category for these: "cadence".
+
+READABILITY FLOOR — authored body text never below 28px. (Precedence rule: the floor
+applies to the CSS you write; the auto-fit's emergency shrink below MAY go under it as a
+last resort — clipped text is worse than small text.)
+
+AUTO-FIT — inject a