diff --git a/themes/bonsai/CHANGELOG.md b/themes/bonsai/CHANGELOG.md
index c232bd8..3c72035 100644
--- a/themes/bonsai/CHANGELOG.md
+++ b/themes/bonsai/CHANGELOG.md
@@ -6,6 +6,7 @@ All notable changes to this project are documented here. Format follows [Keep a
### Added
- **Favicon polish** — opt-in `params.faviconSvg` and `params.appleTouchIcon` for SVG and iOS home-screen icons. Default behavior unchanged when unset.
+- **Demo-only gallery CSS** — `/themes/` and `/variants/` pages load a separate `static/css/gallery.css`; `static/css/bonsai.css` no longer ships gallery selectors to user sites. Saves ~1.9 KB raw / ~200 B gzipped on every real site. New `head_extra` block in `baseof.html` enables per-page stylesheet additions.
### Changed
- **A11y** — sakura accent darkened `#d4456a → #c93f63` (4.04 → 4.49 vs bg) and koi accent darkened `#c8521e → #bd4c1c` (4.17 → 4.63 vs bg) to reach WCAG AA on the gallery accent chip. Brand intent preserved (cherry blossom pink / koi orange). README hex table synced.
diff --git a/themes/bonsai/README.md b/themes/bonsai/README.md
index 00811d0..6ee369f 100644
--- a/themes/bonsai/README.md
+++ b/themes/bonsai/README.md
@@ -19,7 +19,7 @@ Bonsai treats your bio page the same way: a quiet, well-pruned page that surface
- **35 icons out of the box** — 25 brand (GitHub, Mastodon, Bluesky, X, Threads, LinkedIn, Instagram…) + 10 utility (mail, globe, rss…). Vendored from [Simple Icons](https://simpleicons.org) and [Lucide](https://lucide.dev).
- **Light & dark mode** — respects `prefers-color-scheme`; optional toggle.
- **Zero JavaScript by default** — pure HTML + CSS; opt-in JS for theme toggle only.
-- **Fast** — ~3 KB gzipped CSS, no web fonts (system stack), no runtime fetches.
+- **Fast** — < 3 KB gzipped CSS, no web fonts (system stack), no runtime fetches.
- **Accessible** — semantic HTML, focus-visible outlines, `prefers-reduced-motion`.
- **Responsive** — mobile-first, looks right at every viewport.
diff --git a/themes/bonsai/layouts/_default/baseof.html b/themes/bonsai/layouts/_default/baseof.html
index 0432ddd..590894d 100644
--- a/themes/bonsai/layouts/_default/baseof.html
+++ b/themes/bonsai/layouts/_default/baseof.html
@@ -2,6 +2,7 @@
{{- partial "head.html" . -}}
+ {{- block "head_extra" . }}{{- end }}
diff --git a/themes/bonsai/layouts/themes/single.html b/themes/bonsai/layouts/themes/single.html
index 973c32d..bdb5ca4 100644
--- a/themes/bonsai/layouts/themes/single.html
+++ b/themes/bonsai/layouts/themes/single.html
@@ -1,3 +1,7 @@
+{{ define "head_extra" }}
+
+{{ end }}
+
{{ define "main" }}
Color themes
diff --git a/themes/bonsai/layouts/variants/single.html b/themes/bonsai/layouts/variants/single.html
index d3b7aa1..b73ba65 100644
--- a/themes/bonsai/layouts/variants/single.html
+++ b/themes/bonsai/layouts/variants/single.html
@@ -1,3 +1,7 @@
+{{ define "head_extra" }}
+
+{{ end }}
+
{{ define "main" }}
Layout variants
diff --git a/themes/bonsai/plans/260510-0238-v0-4-release/phase-02-extract-gallery-css-11.md b/themes/bonsai/plans/260510-0238-v0-4-release/phase-02-extract-gallery-css-11.md
new file mode 100644
index 0000000..5e582cc
--- /dev/null
+++ b/themes/bonsai/plans/260510-0238-v0-4-release/phase-02-extract-gallery-css-11.md
@@ -0,0 +1,48 @@
+---
+phase: 2
+title: Extract gallery CSS (#11)
+status: completed
+priority: P2
+effort: 30m
+dependencies: []
+---
+
+# Phase 2: Extract gallery CSS (#11)
+
+## Overview
+
+Move `.themes-gallery__*` + `.variants-gallery__*` selectors out of `static/css/bonsai.css` into a new `static/css/gallery.css` referenced only by the demo pages. End-user sites stop paying for ~300 gzipped bytes of CSS they never use.
+
+## Related Code Files
+
+- Modify: `static/css/bonsai.css` — remove gallery rules
+- Create: `static/css/gallery.css` — new file with the extracted rules
+- Modify: `layouts/themes/single.html` — load gallery.css for this page only
+- Modify: `layouts/variants/single.html` — load gallery.css for this page only
+- Modify: `layouts/_default/baseof.html` (if needed) — add `head_extra` block hook
+
+## Implementation Steps
+
+1. Identify and excise gallery selectors from `static/css/bonsai.css`. Selectors: `.themes-gallery`, `.themes-gallery__card`, `.themes-gallery__name`, `.themes-gallery__hex`, `.themes-gallery__chip`, `.themes-gallery__chip--accent`, `.variants-gallery`, `.variants-gallery__card`, `.variants-gallery__name`, `.variants-gallery__desc`, `.variants-gallery__code`. Plus any responsive `@media` rules that target only those selectors.
+2. Create `static/css/gallery.css` with the excised rules.
+3. Add a `{{- block "head_extra" . -}}{{- end }}` hook in `layouts/_default/baseof.html` `` (after the main stylesheet link).
+4. In `layouts/themes/single.html` and `layouts/variants/single.html`, define the block: `{{ define "head_extra" }} {{ end }}`.
+5. Verify `/icons/` page does NOT pull in `gallery.css` (it has no `head_extra` block).
+6. Build + curl: confirm `/themes/` and `/variants/` pull both stylesheets; `/` and `/icons/` pull only `bonsai.css`.
+7. Measure: `bonsai.css` raw + gzipped should drop ~1,200 raw / ~300 gzipped.
+8. Update CHANGELOG (Unreleased) and README's CSS budget claim if it shifts back under 3 KB.
+
+## Success Criteria
+
+- [ ] `static/css/bonsai.css` no longer contains gallery selectors
+- [ ] `static/css/gallery.css` exists and renders gallery markup correctly
+- [ ] `/themes/` and `/variants/` load both stylesheets
+- [ ] `/` and `/icons/` load only `bonsai.css`
+- [ ] `bonsai.css` gzipped < 3 KB
+- [ ] `hugo --gc --minify` clean
+- [ ] PR opened against main
+
+## Risk Assessment
+
+- **Risk:** `head_extra` block name conflicts with future Hugo theme usage. **Mitigation:** name is theme-local, undocumented externally; safe.
+- **Risk:** missed responsive rules during excision. **Mitigation:** grep `gallery` in CSS before commit; only matches in gallery.css.
diff --git a/themes/bonsai/plans/260510-0238-v0-4-release/phase-03-add-more-icons-10.md b/themes/bonsai/plans/260510-0238-v0-4-release/phase-03-add-more-icons-10.md
new file mode 100644
index 0000000..5143ce2
--- /dev/null
+++ b/themes/bonsai/plans/260510-0238-v0-4-release/phase-03-add-more-icons-10.md
@@ -0,0 +1,64 @@
+---
+phase: 3
+title: "Add more icons (#10)"
+status: pending
+priority: P3
+effort: "30m"
+dependencies: []
+---
+
+# Phase 3: Add more icons (#10)
+
+## Overview
+
+Vendor ~10 additional brand + utility SVGs to the existing 35-icon set. Mechanical: add entries to `data/icons.yaml`, run `scripts/sync-icons.sh`, update README tables, verify `/icons/` gallery.
+
+## Shortlist (≤ 10, picked for breadth)
+
+Brand (Simple Icons CC0):
+- `bandcamp` — musicians
+- `soundcloud` — musicians
+- `spotify` — artist profiles
+- `figma` — designers
+- `dribbble` — designers
+- `stackoverflow` — devs
+- `matrix` — fediverse messaging
+
+Utility (Lucide ISC):
+- `book-open` — bookshelves, reading lists
+- `download` — resume / vCard
+- `heart` — sponsor / support
+
+Total +10 → 45 icons. Soft ceiling at ~50 for v0-line per issue.
+
+## Related Code Files
+
+- Modify: `data/icons.yaml`
+- Run: `scripts/sync-icons.sh`
+- Generated: `assets/icons/brand/*.svg`, `assets/icons/ui/*.svg`
+- Modify: `README.md` icon tables (both `` blocks)
+
+## Implementation Steps
+
+1. Inspect current `data/icons.yaml` structure to match the entry schema.
+2. Append the 10 shortlisted entries (correct slug, license attribution).
+3. Run `scripts/sync-icons.sh` — verify SVGs land in `assets/icons/brand/` and `assets/icons/ui/`.
+4. Inspect downloaded SVGs: confirm size budget (each typically < 1 KB).
+5. Update README icon tables under both `` blocks (brand + utility), keep alphabetical or current order convention.
+6. Update README intro line if it claims a fixed count (e.g. "35 icons" → "45 icons").
+7. Build + curl `/icons/` to confirm new icons render in gallery.
+8. Update CHANGELOG (Unreleased).
+
+## Success Criteria
+
+- [ ] 10 new SVGs vendored under `assets/icons/`
+- [ ] `data/icons.yaml` updated with 10 new entries
+- [ ] README icon tables updated; count line updated
+- [ ] `/icons/` gallery renders all new icons
+- [ ] `hugo --gc --minify` clean
+- [ ] PR opened against main
+
+## Risk Assessment
+
+- **Risk:** Simple Icons / Lucide rename or remove a slug. **Mitigation:** `sync-icons.sh` fails loudly if slug missing; pick alternates from the original issue list.
+- **Risk:** SVG size larger than expected (some Simple Icons brand marks are heavy). **Mitigation:** check raw sizes after sync; reject if any single icon > 3 KB.
diff --git a/themes/bonsai/plans/260510-0238-v0-4-release/phase-04-optional-rss-feed-8.md b/themes/bonsai/plans/260510-0238-v0-4-release/phase-04-optional-rss-feed-8.md
new file mode 100644
index 0000000..5be78fd
--- /dev/null
+++ b/themes/bonsai/plans/260510-0238-v0-4-release/phase-04-optional-rss-feed-8.md
@@ -0,0 +1,54 @@
+---
+phase: 4
+title: "Optional RSS feed (#8)"
+status: pending
+priority: P3
+effort: "45m"
+dependencies: []
+---
+
+# Phase 4: Optional RSS feed (#8)
+
+## Overview
+
+Add `params.rss = true` opt-in. When enabled, the theme stops adding `RSS` to its `disableKinds` recommendation and renders an RSS feed of `[[params.links]]`. Adds ` ` to ``.
+
+## Design notes
+
+- Hugo respects user `disableKinds` in their `hugo.toml`. The theme can't force-enable RSS — but it can:
+ - Document the param in README
+ - Provide an `index.rss.xml` template that runs when the user removes `RSS` from `disableKinds`
+ - Add the ` ` in `head.html` when `params.rss` is true (regardless of `disableKinds` — feed crawler will 404 if user mismatches; that's their config)
+- Item shape: `` = link title, ` ` = link URL, `` = optional description, `` = build time (links lack intrinsic dates).
+
+## Related Code Files
+
+- Create: `layouts/index.rss.xml` — template for the feed
+- Modify: `layouts/partials/head.html` — emit ` ` when `params.rss = true`
+- Modify: `README.md` — document the new param + the `disableKinds` removal step
+
+## Implementation Steps
+
+1. Create `layouts/index.rss.xml` rendering an RSS 2.0 feed:
+ - `` populated from `params.name`, `params.tagline`, `params.bio`, site `baseURL`, build `time.Now`
+ - `- ` per `[[params.links]]` entry; `
` = build time; `` = link URL
+2. In `layouts/partials/head.html`, when `site.Params.rss` is true: ` `. Hugo emits `index.xml` for the home RSS template.
+3. README: add `rss` to the params table; add a short "Enable RSS" subsection explaining that user must remove `RSS` from their `disableKinds` line and set `params.rss = true`.
+4. Build + curl `/index.xml` — confirm valid RSS 2.0 (well-formed XML, items present); confirm `` of `/` carries the alternate link when `rss = true`.
+5. Validate against W3C feed validator if reachable; if not, manually inspect for required elements.
+6. Update CHANGELOG (Unreleased).
+
+## Success Criteria
+
+- [ ] `layouts/index.rss.xml` exists and renders well-formed RSS 2.0 when feature on
+- [ ] `` carries ` ` when `params.rss = true`
+- [ ] No RSS link emitted when `params.rss` unset/false (default)
+- [ ] README documents the new param + `disableKinds` step
+- [ ] `hugo --gc --minify` clean
+- [ ] PR opened against main
+
+## Risk Assessment
+
+- **Risk:** User leaves `RSS` in `disableKinds` and the alternate link 404s. **Mitigation:** README is explicit; theme cannot override user's `disableKinds`.
+- **Risk:** Build-time pubDate makes the feed change every build, polluting feed readers. **Mitigation:** documented as a known limitation; alternative is a per-link `pubDate` field (out of scope).
+- **Risk:** Emoji/unicode in `params.tagline` breaks XML. **Mitigation:** Hugo's `transform.XMLEscape` or default escaping in templates handles it.
diff --git a/themes/bonsai/plans/260510-0238-v0-4-release/phase-07-v0-4-0-release-prep.md b/themes/bonsai/plans/260510-0238-v0-4-release/phase-07-v0-4-0-release-prep.md
new file mode 100644
index 0000000..47b6ff2
--- /dev/null
+++ b/themes/bonsai/plans/260510-0238-v0-4-release/phase-07-v0-4-0-release-prep.md
@@ -0,0 +1,38 @@
+---
+phase: 7
+title: "v0.4.0 release prep"
+status: pending
+priority: P3
+effort: "30m"
+dependencies: [2, 3, 4]
+---
+
+# Phase 7: v0.4.0 release prep
+
+## Overview
+
+After phases 2, 3, 4 merge to main, sync `CHANGELOG.md`, bump version references, tag `v0.4.0`, push tag.
+
+## Implementation Steps
+
+1. Pull latest `main`, confirm phases 2/3/4 PRs are merged.
+2. Move `## [Unreleased]` section to `## [0.4.0] — 2026-05-10` (or actual merge date).
+3. Add fresh `## [Unreleased]` section above with v0.5 placeholders (#7 OG auto-gen, #9 multi-section bio explicitly listed as deferred from v0.4).
+4. Cross-check README for stale "v0.3" or version mentions; update where needed.
+5. Reissue `theme.toml` `min_version` only if a phase added a Hugo feature requiring a newer Hugo version (none expected here).
+6. Final build: `hugo --gc --minify --themesDir ../..` from `exampleSite/` — clean.
+7. `git tag v0.4.0 -m "v0.4.0: gallery CSS extraction, more icons, opt-in RSS"`
+8. `git push origin v0.4.0`
+9. Run `/ck:journal` for the v0.4.0 release retro.
+
+## Success Criteria
+
+- [ ] CHANGELOG has dated `## [0.4.0]` section
+- [ ] `## [Unreleased]` section exists with v0.5 deferral notes
+- [ ] `git tag v0.4.0` exists locally and on remote
+- [ ] Build clean across all 3 demo pages
+
+## Risk Assessment
+
+- **Risk:** auto-tag without user approval. **Mitigation:** ask before `git push origin v0.4.0`; tag is the publish event, deserves explicit consent.
+- **Risk:** stale "35 icons" claim in README after Phase 3. **Mitigation:** Phase 3 success criterion already covers this.
diff --git a/themes/bonsai/plans/260510-0238-v0-4-release/plan.md b/themes/bonsai/plans/260510-0238-v0-4-release/plan.md
new file mode 100644
index 0000000..7833041
--- /dev/null
+++ b/themes/bonsai/plans/260510-0238-v0-4-release/plan.md
@@ -0,0 +1,43 @@
+---
+title: 'v0.4 release: ship gallery-CSS extraction + more icons + opt-in RSS'
+description: >-
+ Ship issues #11, #10, #8 each as its own PR; tag v0.4.0. Issues #7 (OG
+ auto-gen) and #9 (multi-section bio) deferred to v0.5 per their own deferral
+ notes.
+status: pending
+priority: P2
+created: 2026-05-10T00:00:00.000Z
+---
+
+# v0.4 release: ship the three well-scoped v0-line enhancements
+
+## Overview
+
+Per scope-check: ship the three well-scoped issues now, defer the two design-heavy ones.
+
+- **Phase 2** Extract gallery CSS (#11) — pure refactor, smallest scope
+- **Phase 3** Add more icons (#10) — mechanical, data-driven
+- **Phase 4** Optional RSS feed (#8) — additive opt-in
+- **Phase 7** v0.4.0 release prep
+
+Each phase ships as its own feature branch + PR (matches repo pattern).
+
+## Deferred to v0.5
+
+- **#7 Auto-generate OG image** — issue text: *"If no path meets [≤30 KB binary], defer to v0.5"*. Of 5 candidate approaches, only `pyftsubset Inter` + base PNG might fit; needs measured prototype before committing.
+- **#9 Multi-section bio** — issue text: *"Likely a 2026-Q3 candidate, not v0.4"*. Schema design dispute; needs `/ck:brainstorm` before a plan.
+
+## Phases (active)
+
+| Phase | Name | Status |
+|-------|------|--------|
+| 2 | [Extract gallery CSS (#11)](./phase-02-extract-gallery-css-11.md) | Completed |
+| 3 | [Add more icons (#10)](./phase-03-add-more-icons-10.md) | Pending |
+| 4 | [Optional RSS feed (#8)](./phase-04-optional-rss-feed-8.md) | Pending |
+| 7 | [v0.4.0 release prep](./phase-07-v0-4-0-release-prep.md) | Pending |
+
+(Phase numbers preserved from original 7-phase scaffold for stable references.)
+
+## Dependencies
+
+- Phase 7 depends on Phases 2, 3, 4. Each of 2/3/4 is independent and ships as a separate PR.
diff --git a/themes/bonsai/static/css/bonsai.css b/themes/bonsai/static/css/bonsai.css
index 286dbe8..9ce2ff1 100644
--- a/themes/bonsai/static/css/bonsai.css
+++ b/themes/bonsai/static/css/bonsai.css
@@ -364,95 +364,8 @@ body {
[data-theme="light"] .theme-toggle .theme-toggle__sun { display: none; }
[data-theme="light"] .theme-toggle .theme-toggle__moon { display: block; }
-/* ============================================================
- Themes gallery (exampleSite/themes/) — 4 mini bio cards
- ============================================================ */
-
-.themes-gallery {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
- gap: 1.5rem;
- max-width: 60rem;
- margin: 2rem auto;
-}
-
-.themes-gallery__card {
- background: var(--bonsai-bg);
- border: 1px solid var(--bonsai-border);
- border-radius: var(--bonsai-radius);
- padding: 1.5rem;
- text-align: center;
-}
-
-.themes-gallery__name {
- font-family: var(--bonsai-font-display);
- font-size: 1.1rem;
- margin: 0 0 .25rem;
- color: var(--bonsai-text);
-}
-
-.themes-gallery__hex {
- color: var(--bonsai-muted);
- font-family: ui-monospace, monospace;
- font-size: .75rem;
- margin: 0 0 1rem;
-}
-
-.themes-gallery__chip {
- display: inline-block;
- padding: .4rem .75rem;
- background: var(--bonsai-surface);
- color: var(--bonsai-text);
- border: 1px solid var(--bonsai-border);
- border-radius: var(--bonsai-radius);
- font-size: .85rem;
-}
-
-.themes-gallery__chip--accent {
- background: var(--bonsai-accent);
- color: var(--bonsai-bg);
- border-color: var(--bonsai-accent);
-}
-
-/* ============================================================
- Variants gallery (exampleSite/variants/) — 3 sample bio cards
- ============================================================ */
-
-.variants-gallery {
- display: grid;
- grid-template-columns: 1fr;
- gap: 2rem;
- max-width: 36rem;
- margin: 2rem auto;
-}
-
-.variants-gallery__card {
- background: var(--bonsai-surface);
- border: 1px solid var(--bonsai-border);
- border-radius: var(--bonsai-radius);
- padding: 1.5rem;
-}
-
-.variants-gallery__name {
- font-family: var(--bonsai-font-display);
- font-size: 1.1rem;
- margin: 0 0 .25rem;
- color: var(--bonsai-text);
-}
-
-.variants-gallery__desc {
- color: var(--bonsai-muted);
- font-size: .85rem;
- margin: 0 0 .5rem;
-}
-
-.variants-gallery__code {
- display: inline-block;
- margin-bottom: 1rem;
- font-family: ui-monospace, monospace;
- font-size: .75rem;
- color: var(--bonsai-muted);
-}
+/* Note: gallery selectors (.themes-gallery, .variants-gallery) live in gallery.css
+ and load only on /themes/ and /variants/ demo pages via the head_extra block. */
@media (prefers-reduced-motion: reduce) {
.link, .link:hover, .link:active,
diff --git a/themes/bonsai/static/css/gallery.css b/themes/bonsai/static/css/gallery.css
new file mode 100644
index 0000000..2dfac23
--- /dev/null
+++ b/themes/bonsai/static/css/gallery.css
@@ -0,0 +1,92 @@
+/* Bonsai gallery styles — loaded only on /themes/ and /variants/ demo pages.
+ * Kept out of bonsai.css so end-user sites don't ship demo-only selectors. */
+
+/* ============================================================
+ Themes gallery (exampleSite/themes/) — 4 mini bio cards
+ ============================================================ */
+
+.themes-gallery {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
+ gap: 1.5rem;
+ max-width: 60rem;
+ margin: 2rem auto;
+}
+
+.themes-gallery__card {
+ background: var(--bonsai-bg);
+ border: 1px solid var(--bonsai-border);
+ border-radius: var(--bonsai-radius);
+ padding: 1.5rem;
+ text-align: center;
+}
+
+.themes-gallery__name {
+ font-family: var(--bonsai-font-display);
+ font-size: 1.1rem;
+ margin: 0 0 .25rem;
+ color: var(--bonsai-text);
+}
+
+.themes-gallery__hex {
+ color: var(--bonsai-muted);
+ font-family: ui-monospace, monospace;
+ font-size: .75rem;
+ margin: 0 0 1rem;
+}
+
+.themes-gallery__chip {
+ display: inline-block;
+ padding: .4rem .75rem;
+ background: var(--bonsai-surface);
+ color: var(--bonsai-text);
+ border: 1px solid var(--bonsai-border);
+ border-radius: var(--bonsai-radius);
+ font-size: .85rem;
+}
+
+.themes-gallery__chip--accent {
+ background: var(--bonsai-accent);
+ color: var(--bonsai-bg);
+ border-color: var(--bonsai-accent);
+}
+
+/* ============================================================
+ Variants gallery (exampleSite/variants/) — 3 sample bio cards
+ ============================================================ */
+
+.variants-gallery {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 2rem;
+ max-width: 36rem;
+ margin: 2rem auto;
+}
+
+.variants-gallery__card {
+ background: var(--bonsai-surface);
+ border: 1px solid var(--bonsai-border);
+ border-radius: var(--bonsai-radius);
+ padding: 1.5rem;
+}
+
+.variants-gallery__name {
+ font-family: var(--bonsai-font-display);
+ font-size: 1.1rem;
+ margin: 0 0 .25rem;
+ color: var(--bonsai-text);
+}
+
+.variants-gallery__desc {
+ color: var(--bonsai-muted);
+ font-size: .85rem;
+ margin: 0 0 .5rem;
+}
+
+.variants-gallery__code {
+ display: inline-block;
+ margin-bottom: 1rem;
+ font-family: ui-monospace, monospace;
+ font-size: .75rem;
+ color: var(--bonsai-muted);
+}