jekyll-theme-zer0 1.26.0 → 1.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +558 -5
  3. data/README.md +10 -27
  4. data/_data/authors.yml +4 -3
  5. data/_data/backlog.yml +28 -0
  6. data/_data/consumers.yml +147 -0
  7. data/_data/features.yml +142 -23
  8. data/_data/i18n/fr.yml +98 -0
  9. data/_data/i18n/languages.yml +36 -0
  10. data/_data/i18n/manifest.yml +1502 -0
  11. data/_data/navigation/docs.yml +2 -0
  12. data/_data/navigation/main.yml +6 -0
  13. data/_data/series.yml +19 -0
  14. data/_data/theme-manifest.yml +348 -268
  15. data/_data/ui-text.yml +36 -246
  16. data/_includes/README.md +25 -2
  17. data/_includes/analytics/google-tag-manager-body.html +11 -2
  18. data/_includes/analytics/google-tag-manager-head.html +16 -6
  19. data/_includes/analytics/posthog.html +33 -4
  20. data/_includes/components/abc-letter.html +43 -0
  21. data/_includes/components/author-avatar-url.html +4 -2
  22. data/_includes/components/book-card.html +42 -0
  23. data/_includes/components/book-nav.html +80 -0
  24. data/_includes/components/book-plate.html +31 -0
  25. data/_includes/components/book-toc.html +47 -0
  26. data/_includes/components/bookshelf.html +68 -0
  27. data/_includes/components/card-grid.html +60 -0
  28. data/_includes/components/data-card.html +95 -0
  29. data/_includes/components/env-switcher.html +3 -1
  30. data/_includes/components/halfmoon.html +5 -1
  31. data/_includes/components/language-toggle.html +81 -0
  32. data/_includes/components/page-feedback.html +65 -2
  33. data/_includes/components/search-modal.html +2 -2
  34. data/_includes/components/shortcuts-modal.html +1 -1
  35. data/_includes/components/theme-controls-bar.html +10 -2
  36. data/_includes/components/theme-customizer.html +8 -2
  37. data/_includes/components/translation-notice.html +27 -0
  38. data/_includes/content/intro.html +16 -15
  39. data/_includes/content/seo.html +9 -3
  40. data/_includes/core/color-mode-init.html +13 -4
  41. data/_includes/core/favicon.html +46 -0
  42. data/_includes/core/footer.html +9 -4
  43. data/_includes/core/head.html +18 -0
  44. data/_includes/core/header.html +9 -4
  45. data/_includes/core/hreflang.html +33 -0
  46. data/_includes/core/i18n.html +36 -0
  47. data/_includes/custom/body-end.html +18 -0
  48. data/_includes/custom/body-start.html +17 -0
  49. data/_includes/custom/footer.html +18 -0
  50. data/_includes/custom/head.html +18 -0
  51. data/_includes/navigation/breadcrumbs.html +1 -1
  52. data/_includes/navigation/local-graph.html +28 -2
  53. data/_includes/navigation/nav-tree.html +3 -3
  54. data/_includes/navigation/navbar.html +5 -4
  55. data/_includes/navigation/sidebar-config.html +21 -0
  56. data/_includes/navigation/sidebar-nav.html +4 -0
  57. data/_includes/navigation/sidebar-pagetree.html +150 -0
  58. data/_includes/navigation/sidebar-right.html +3 -2
  59. data/_includes/navigation/unified-drawer.html +9 -3
  60. data/_includes/obsidian/full-graph.html +165 -136
  61. data/_layouts/404.html +260 -0
  62. data/_layouts/README.md +2 -0
  63. data/_layouts/article.html +7 -1
  64. data/_layouts/book-abc.html +106 -0
  65. data/_layouts/book-story.html +91 -0
  66. data/_layouts/book.html +113 -0
  67. data/_layouts/collection.html +17 -5
  68. data/_layouts/default.html +5 -3
  69. data/_layouts/home.html +5 -2
  70. data/_layouts/landing.html +9 -0
  71. data/_layouts/news.html +159 -33
  72. data/_layouts/root.html +29 -9
  73. data/_layouts/section.html +61 -17
  74. data/_sass/components/_book.scss +423 -0
  75. data/_sass/core/_navbar.scss +13 -0
  76. data/_sass/core/_obsidian.scss +295 -7
  77. data/_sass/layouts/_navbar-extras.scss +6 -1
  78. data/_sass/theme/_backgrounds.scss +21 -8
  79. data/assets/css/main.scss +1 -0
  80. data/assets/js/auto-hide-nav.js +5 -1
  81. data/assets/js/halfmoon.js +26 -0
  82. data/assets/js/obsidian-graph.js +707 -265
  83. data/assets/js/obsidian-local-graph.js +161 -54
  84. data/assets/js/search-modal.js +4 -1
  85. data/scripts/README.md +20 -26
  86. data/scripts/bin/audit-consumer +1 -1
  87. data/scripts/bin/manifest +31 -5
  88. data/scripts/bin/sync-plugins +0 -1
  89. data/scripts/bin/validate +5 -1
  90. data/scripts/dev/rasterize-svg.js +65 -0
  91. data/scripts/features/generate-preview-images +49 -1390
  92. data/scripts/features/install-preview-generator +55 -33
  93. data/scripts/install/README.md +55 -25
  94. data/scripts/install/ai/client.sh +302 -93
  95. data/scripts/install/ai/prompts/spec.schema.json +1 -1
  96. data/scripts/install/ai/prompts/wizard.system.md +8 -17
  97. data/scripts/install/ai/wizard.sh +10 -5
  98. data/scripts/install/apply.sh +7 -3
  99. data/scripts/install/cli.sh +54 -4
  100. data/scripts/install/config.sh +167 -0
  101. data/scripts/install/doctor.sh +38 -0
  102. data/scripts/install/plan.sh +10 -0
  103. data/scripts/install/spec.sh +15 -7
  104. data/scripts/install/template.sh +4 -0
  105. data/scripts/lib/README.md +1 -5
  106. data/scripts/lib/install/deploy/README.md +3 -9
  107. data/scripts/lib/preview_generator.py +2261 -1341
  108. data/scripts/propagate.rb +277 -0
  109. data/scripts/translate.rb +1204 -0
  110. metadata +34 -3
  111. data/_plugins/preview_image_generator.rb +0 -351
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: af7de9769f05880ab652483a7ff6109e79d04ff6404c71163822ce4cecac3d23
4
- data.tar.gz: 7c968f45cbba4655ad0eb5aa25a55e7b5f178957d65036d47236ba7809592733
3
+ metadata.gz: 8b72b3e6bf60fc9df7f31ecd56d157770fede085973db6d2153980c3004bc0fc
4
+ data.tar.gz: 369c0158b06d41d52c3f737d9a88a44e2cb9265653db55bf376e0651a502980b
5
5
  SHA512:
6
- metadata.gz: 3ec2a5423346be22b17120fc586396501218877b70373eae7df03395b4b91211e8d03cf45d79ec71ad717344eae248b967dd5e810092cbf045f0d796ffad1c61
7
- data.tar.gz: 55ec85fa55c7ca9c918413e16c86d3a275301a0282a989039face47fad9b9e3dc27f5fcda6ff7eb5e74faf0a557e8307c43a059e2e2bd1ebf8df011e46e82b9c
6
+ metadata.gz: 0d89cbf414b78be0b73fd22f8102dfacf8e38f10c561b2cc50fb4b492b8840d15e38f3b83243a589cd32f7fe7d72e4eb009a8dba44f09634e5e6611195971566
7
+ data.tar.gz: cbcbec935073100919c21c9f3f542124a291529a93212706b8c7e82773a4361d7b272cc60ec966f077213c6c7ae23d36a60ad129c08bfd33506d382172109a88
data/CHANGELOG.md CHANGED
@@ -5,6 +5,410 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ Two kinds of section appear below. `## [X.Y.Z]` headings are generated by
9
+ release-please from Conventional Commits and are the authoritative record of
10
+ what shipped in each release. `## Detailed notes — vX.Y.Z` headings are
11
+ hand-written prose covering the same releases in more depth; they sit below
12
+ their version because release-please inserts each new release at the top of the
13
+ file. Only `## [Unreleased]` describes work that has not shipped yet.
14
+
15
+ ## [1.28.0](https://github.com/bamr87/zer0-mistakes/compare/v1.27.0...v1.28.0) (2026-08-09)
16
+
17
+
18
+ ### Features
19
+
20
+ * **books:** children's picture-book collection with immersive layouts and components ([#328](https://github.com/bamr87/zer0-mistakes/issues/328)) ([e059d1a](https://github.com/bamr87/zer0-mistakes/commit/e059d1afdc95073c5ce5bf9d1516b362979ad402))
21
+ * **ci:** deploy the site via Actions so examples reach the live domain ([#353](https://github.com/bamr87/zer0-mistakes/issues/353)) ([959243c](https://github.com/bamr87/zer0-mistakes/commit/959243c0e8e504fe5d8dba44b89016b413bc88da))
22
+ * **docs:** theme-canary reporting contract for downstream theme-scouts (FF-0019) ([#358](https://github.com/bamr87/zer0-mistakes/issues/358)) ([c1c1b30](https://github.com/bamr87/zer0-mistakes/commit/c1c1b309c5504b014602982fc96ec80c3f8f70de))
23
+ * **examples:** add serialized reading-blog example site ([#339](https://github.com/bamr87/zer0-mistakes/issues/339)) ([aca0b53](https://github.com/bamr87/zer0-mistakes/commit/aca0b53b88067bda80fb299f9d5148ccb185d618))
24
+ * **includes:** config-driven favicon, GTM gating, og:image fallback fix, sidebar nav aliases ([#327](https://github.com/bamr87/zer0-mistakes/issues/327)) ([fe8aacc](https://github.com/bamr87/zer0-mistakes/commit/fe8aacc234003f9f170fcb3b8b29c60ac18ed94b))
25
+ * **install:** Claude Code OAuth + multi-provider AI + config-file layer ([#330](https://github.com/bamr87/zer0-mistakes/issues/330)) ([fde414c](https://github.com/bamr87/zer0-mistakes/commit/fde414cce5286f90de1ad02ca6ca40d634be5611))
26
+ * **layouts:** book-abc board-book layout + ABC art-style skins ([#336](https://github.com/bamr87/zer0-mistakes/issues/336)) ([d88038e](https://github.com/bamr87/zer0-mistakes/commit/d88038e806e0d1a492e5d12b4e10e55fd240d692))
27
+ * **navigation:** nav: pages sidebar mode — auto-build the tree from page URLs ([#333](https://github.com/bamr87/zer0-mistakes/issues/333)) ([c0c627d](https://github.com/bamr87/zer0-mistakes/commit/c0c627d5f50051e8f5eb4f0d6681244acaaa8d85))
28
+ * **obsidian:** revamp graph frontend with Obsidian-style controls ([#366](https://github.com/bamr87/zer0-mistakes/issues/366)) ([7143d9c](https://github.com/bamr87/zer0-mistakes/commit/7143d9cd137408ad82981c9f9835204bab3a3db5))
29
+ * **theme:** consumer-adoption hardening — mobile overlay fixes, zero-data resilience, extension hooks ([#338](https://github.com/bamr87/zer0-mistakes/issues/338)) ([84bcce1](https://github.com/bamr87/zer0-mistakes/commit/84bcce15e66b0bc312f513cef4a9dd11f972a049))
30
+
31
+
32
+ ### Bug Fixes
33
+
34
+ * **books:** pin abc-demo permalink so its cover lands at /books/abc-demo/ ([#345](https://github.com/bamr87/zer0-mistakes/issues/345)) ([943f463](https://github.com/bamr87/zer0-mistakes/commit/943f463c73081e7cc3d914c28a2ec2eea3d1b5ae))
35
+ * **ci:** give the Dependabot label job an explicit GH_REPO ([#370](https://github.com/bamr87/zer0-mistakes/issues/370)) ([808b20e](https://github.com/bamr87/zer0-mistakes/commit/808b20ebbdcd5b0bc139b90947297cec6d226787))
36
+ * **ci:** prefer CLAUDE_CODE_OAUTH_TOKEN over ANTHROPIC_API_KEY in all AI workflows ([#334](https://github.com/bamr87/zer0-mistakes/issues/334)) ([043848e](https://github.com/bamr87/zer0-mistakes/commit/043848eb84222240efc121e8112ddc12ab959a64))
37
+ * **ci:** unbreak the production Pages build, frozen since 2026-08-05 ([#356](https://github.com/bamr87/zer0-mistakes/issues/356)) ([05465cf](https://github.com/bamr87/zer0-mistakes/commit/05465cfd5480a6bdfb0d164d56a892f2825ec1cf))
38
+ * **i18n:** exclude admin config page from translation to unbreak Pages deploy ([#349](https://github.com/bamr87/zer0-mistakes/issues/349)) ([3f4890c](https://github.com/bamr87/zer0-mistakes/commit/3f4890cee3dd59b7f1cbc6ec235a863017b075fb))
39
+ * **i18n:** normalise generated prose so translation runs keep CI green ([#342](https://github.com/bamr87/zer0-mistakes/issues/342)) ([70a7234](https://github.com/bamr87/zer0-mistakes/commit/70a723425b8cc6a77e1b845b212a8a7d127a7779))
40
+ * **navigation:** mark only the current page active in the sidebar nav-tree ([#331](https://github.com/bamr87/zer0-mistakes/issues/331)) ([3f1b723](https://github.com/bamr87/zer0-mistakes/commit/3f1b723169258993d4530dd5817740e62734ccd4))
41
+ * **sass:** restore mobile navigation in the example, collapsed to a 60px sliver ([#363](https://github.com/bamr87/zer0-mistakes/issues/363)) ([8c7d87a](https://github.com/bamr87/zer0-mistakes/commit/8c7d87af4fd476f06c16ce58ca4d4cd0e2c4169d))
42
+
43
+ ## [Unreleased]
44
+
45
+ ### Added
46
+
47
+ - **Obsidian graph frontend revamp** — the graph views now mirror the Obsidian
48
+ desktop experience. The full graph page gains a floating **Graph settings**
49
+ card (Filters / Display / Forces): search with `tag:`/`path:` operators,
50
+ per-collection toggles generated from the wiki-index (count badge + color
51
+ swatch, doubling as the legend), show-orphans and show-unresolved switches,
52
+ node-size / link-thickness / label-fade sliders, repel-force and
53
+ link-distance sliders with animated re-layout, plus a zoom / fit / fullscreen
54
+ button stack, an Obsidian-style hover preview card, live dark-mode restyling,
55
+ and localStorage persistence. The local graph panel gains a depth selector
56
+ (1–3) and outgoing/incoming direction switches that re-render the subgraph in
57
+ place. Same vendored cytoscape.js, unchanged `wiki-index.json` contract
58
+ (evidence:
59
+ [`test/visual/evidence/obsidian-graph-revamp/`](test/visual/evidence/obsidian-graph-revamp/README.md);
60
+ regression: `test/visual/features/obsidian-graph.spec.js`).
61
+
62
+ - **Theme-canary reporting contract** (`docs/systems/theme-canary-contract.md`) —
63
+ the canonical upstream bug-intake protocol for downstream theme-scout agents
64
+ (it-journey, lifehacker.dev): required `theme-canary` + `from-<consumer>`
65
+ labels, a deterministic 12-hex dedupe key + hidden body marker, the mandated
66
+ open-AND-closed pre-filing search, a 5-issues-per-consumer-per-run cap, and a
67
+ required evidence format that records the theme version/commit observed (so
68
+ fixed-in-release triage is mechanical). Ships with the matching
69
+ `theme-canary.yml` issue form.
70
+
71
+ ### Fixed
72
+
73
+ - **Mobile navigation opened as an unusable 60px sliver in the Swerve of Shore
74
+ example.** Its skin set `backdrop-filter` on `.navbar` for a frosted sticky
75
+ header, which — per the CSS spec, alongside `transform`/`filter`/`perspective`
76
+ — makes that element the containing block for its `position: fixed`
77
+ descendants. The theme's main-nav panel (`#bdNavbar`, an `.offcanvas-lg`) is
78
+ one, so it sized itself against the 60px navbar instead of the viewport:
79
+ tapping the hamburger dimmed the page and showed a bare title bar with **0 of
80
+ 14 navigation links** reachable. The effect now lives on a `.navbar::before`
81
+ pseudo-element — visually identical, not an ancestor — restoring the
82
+ full-height panel with **14 of 14** links (evidence:
83
+ [`test/visual/evidence/mobile-nav-containing-block/`](test/visual/evidence/mobile-nav-containing-block/README.md)
84
+ — panel 60px → 780px at 390 and 768). A new smoke-tier spec
85
+ (`test/visual/features/mobile-nav-containing-block.spec.js`) guards the
86
+ invariant on the theme and names the offending ancestor when it fails.
87
+
88
+ - **The example's home and blog pages overflowed and ran edge-to-edge on
89
+ phones.** Their feed grid used Bootstrap's `.row` gutters without a container
90
+ to absorb the −12px margins, making the page 13px wider than the screen, and
91
+ neither section carried an inline padding, so 15 of 30 text blocks sat flush
92
+ against the bezel. Now `gx-0 gy-4` plus a 0.75rem mobile gutter matching the
93
+ theme's own 12px inset — zero horizontal overflow at 320–768, banner still
94
+ full-bleed.
95
+
96
+ - **GitHub Pages production deploy no longer fails at all — the site had been
97
+ frozen since 2026-08-05.** Three independent faults, each of which aborted the
98
+ whole production `jekyll build`, so nothing published after #346:
99
+ 1. `_layouts/collection.html` sorted `site[page.collection]` without checking
100
+ it resolved. A page can select this layout in front matter while living
101
+ outside any collection, making that lookup `nil`; Liquid's `sort` then
102
+ raises `Cannot sort a null object.` and kills the *entire* build, not just
103
+ the page. It now falls back to an empty list.
104
+ 2. `scripts/translate.rb` copied `layout: collection` into the flat `fr/**`
105
+ tree, which is exactly the misconfiguration above — `fr/docs/index.md` was
106
+ the page that took the site down. Collection-only layouts are now dropped
107
+ from generated translations so the `path: fr` default (`layout: default`)
108
+ applies, and the affected page is regenerated.
109
+ 3. `CHANGELOG.md` is a published page (`/CHANGELOG/`), so its prose is parsed
110
+ as Liquid. Two entries quoted Liquid tags without a `raw` guard; one of
111
+ them genuinely executed `include_relative` on `_config.yml`, which in turn
112
+ tripped over a literal `mermaid` tag written inside a config comment.
113
+ Both entries are now guarded and the config comment no longer spells the
114
+ tag out.
115
+
116
+ - **Production builds are now verified before merge.** `.github/workflows/pages.yml`
117
+ builds (never deploys) on pull requests. No other job ran `jekyll build`
118
+ against the production config, which is why all three faults above reached
119
+ `main` with CI green.
120
+
121
+ - **GitHub Pages production deploy no longer fails on the translated config
122
+ page.** The admin config utility `pages/_about/settings/config.md` embeds a
123
+ raw `_config.yml` dump via {% raw %}`{% include_relative _config.yml %}`{% endraw %}, but the
124
+ translation pipeline copied the page into `fr/about/settings/` without its
125
+ sibling data file, so the production `github-pages` build died with
126
+ `Could not locate the included file '_config.yml'`. The page is now excluded
127
+ from translation (`translation.exclude` in `_config.yml`) — it has no
128
+ prose to translate anyway — and the stale `fr/about/settings/config.md` plus
129
+ its `_data/i18n/manifest.yml` entry are removed.
130
+ - **Translation runs no longer break the `oneline` check.** `scripts/translate.rb`
131
+ writes whatever the provider returns, and a provider is free to soft-wrap a
132
+ translated paragraph across several lines — no prompt instruction reliably
133
+ prevents it. Wrapped prose then landed in `fr/**`, turned
134
+ `markdown-oneline.yml` red on the translation PR, and stayed red on `main`
135
+ after it merged. Two of the currently committed translations were in exactly
136
+ that state.
137
+ - `translate.rb` now normalises every page it writes through
138
+ `tools/unwrap-prose.py` — the same tool CI runs, so the rule has one source
139
+ of truth instead of a reimplementation that could drift. Best-effort: a
140
+ missing `python3` warns rather than failing an otherwise good run.
141
+ - `.github/workflows/translate.yml` runs the same tool as a guaranteed
142
+ backstop immediately before committing, then `--check`s its own work so a
143
+ silent failure cannot just move the breakage to the PR's CI run.
144
+ - The tool is resolved relative to `translate.rb` rather than `--root`;
145
+ `--root` points at the content tree being translated, so the previous
146
+ lookup missed the tool whenever the two differed.
147
+ - Unwrapped the two affected files, so `oneline` passes on `main` again.
148
+ - Regression test in `test/test_i18n.sh` backed by a new `stub-wrap`
149
+ provider that soft-wraps at 40 columns. Asserting "output is unwrapped"
150
+ against the plain stub would have passed whether or not normalisation ran.
151
+
152
+ ### Added
153
+
154
+ - **`examples/` — self-contained demo sites built on the theme.** New top-level
155
+ directory (excluded from the theme's own site build and from the gem) for
156
+ complete Jekyll sites that show the theme configured for a particular kind of
157
+ site. Each has its own `_config.yml`, `Gemfile`, and content, and builds
158
+ against the local theme sources via `_config_dev.yml`.
159
+ - `examples/swerve-of-shore/` — **Swerve of Shore**, a serialized literary
160
+ reading blog rebuilt from
161
+ [swerveofshore.com](https://www.swerveofshore.com/) with the site owner's
162
+ permission. 52 pages, 0 broken internal links. Exercises a `series` output
163
+ collection with nested per-section paths, a data-driven taxonomy
164
+ (`_data/episodes.yml`) that generates the sidebar rail, the episode
165
+ schedule with live entry counts, and a section page for every node — so a
166
+ nav target resolves even where nothing is written yet. Also covers article
167
+ hero images (`show_hero`), entries ordered by number rather than date, the
168
+ shared `post-card` component in a single-column feed, and a full editorial
169
+ light-mode restyle done entirely through `assets/css/user-overrides.css` —
170
+ no theme files touched. The site's identity, taxonomy, entry titles/dates,
171
+ and featured images are reproduced, along with a short quoted excerpt per
172
+ entry (the opening the original publishes in its own RSS feed), attributed
173
+ and linked back. The essays themselves are not reproduced.
174
+ - `examples/README.md` — how to run one and the conventions for adding
175
+ another.
176
+ - **Theme propagation system — releases now reach the sites built on them.**
177
+ Releasing ended at RubyGems; nothing told the five downstream consumers a new
178
+ version existed, so all three pinned sites were still on v1.26.0 when v1.27.0
179
+ shipped and the two unpinned ones had been silently tracking `main`.
180
+ - `_data/consumers.yml` — registry of every consumer, its consumption mode
181
+ (`remote_theme_pinned` / `remote_theme_floating` / `gem` / `path`), and every
182
+ file that holds a version pin.
183
+ - `scripts/propagate.rb` — reports pin drift across the registry
184
+ (`--format text|json|github`, `--strict`) or dispatches a `theme-release`
185
+ event to each consumer (`--dispatch`).
186
+ - `.github/workflows/propagate-theme.yml` — dispatches on release publish;
187
+ weekly drift report. Never pushes to a consumer: each one opens its own PR so
188
+ its own CI gates the bump.
189
+ - `templates/consumer/` — the consumer's half of the contract:
190
+ `bump-theme-pins.sh` (rewrites `remote_theme`, `theme_repo`, and Gemfile gem
191
+ constraints in one pass; `--check`, `--dry-run`, `--latest`, `--pin`),
192
+ `theme-bump.yml` (dispatch + weekly schedule + manual), and a
193
+ `.theme-overrides.yml` starter. The schedule matters most for gem-mode
194
+ consumers, which have no `remote_theme` and so pick up nothing automatically.
195
+ - `docs/systems/theme-propagation.md` — how the whole flow fits together.
196
+ - **`./scripts/bin/manifest --check`** — verifies the committed theme manifest
197
+ matches the working tree, ignoring `generated_at`. `--dry-run` now emits only
198
+ YAML on stdout (progress logs moved to stderr) so its output can be diffed
199
+ directly.
200
+ - **Consumer extension hooks (`_includes/custom/`).** Four empty stubs the
201
+ chrome now includes at fixed points — `custom/head.html` (end of `<head>`),
202
+ `custom/body-start.html` (right after `<body>`, e.g. GTM noscript),
203
+ `custom/footer.html` (after the theme footer), `custom/body-end.html` (last
204
+ thing before `</body>`). A consumer site shadows just the stub to inject
205
+ JSON-LD, fonts, verification tags, or scripts — no more forking
206
+ `core/head.html`/`root.html` and drifting from the theme. Documented in
207
+ `_includes/README.md`.
208
+ - **`components/card-grid.html` + `components/data-card.html`.** A generic,
209
+ data-driven card grid (title/url/icon/badge/meta-pills/buttons schema) for
210
+ org hub dashboards, fleet registries, service grids, and link boards —
211
+ the markup consumer sites have been hand-rolling per page. Includes an
212
+ optional empty-state message.
213
+ - **`_layouts/404.html` — the 404 page as a theme layout.** The full 404
214
+ experience (including the unconfigured-site setup guide) now lives in the
215
+ theme; a consumer's `/404.html` shrinks from a 224-line vendored copy to a
216
+ 4-line front-matter stub (`layout: "404"`). Setup mode is branded with
217
+ `site.title` instead of a hardcoded theme welcome, and the stub page's body
218
+ renders below the standard content for site-specific additions.
219
+ - **`color_mode_lock` config.** Pin the site to one color mode: visitor
220
+ preference and OS scheme are ignored, the light/dark/auto switcher is
221
+ removed, and `core/color-mode-init.html` + `halfmoon.js` enforce the lock
222
+ pre-paint. Replaces consumer JS workarounds that re-asserted a mode on every
223
+ `matchMedia` change.
224
+ - **PostHog privacy options** (`posthog.privacy.*`): `respect_gpc` (honor the
225
+ Global Privacy Control signal alongside DNT, default on), `require_consent`
226
+ (start opted out with memory-only persistence until the visitor accepts the
227
+ cookie-consent "analytics" category), and `geoip_disable` (no IP→location
228
+ resolution). Upstreams the deltas consumer sites were maintaining in a
229
+ full fork of `analytics/posthog.html`.
230
+ - **`news:` config surface for the news/section layouts.** `category_base`
231
+ (e.g. `/categories/` instead of the hardcoded `/news/`), `index_url`,
232
+ `hero: false`, `archives_url`, and a `newsletter` block (endpoint-or-mailto,
233
+ or hidden entirely — the old markup shipped a form that submitted nowhere).
234
+
235
+ ### Changed
236
+
237
+ - **The news/section layouts work with zero `_data`.** Section navigation
238
+ falls back from `_data/navigation/posts.yml` to `site.categories`; the hero
239
+ falls back to the newest post when nothing is marked `breaking`/`featured`;
240
+ tags links render only when a tags page exists (honoring `site.tags_page`);
241
+ archive month links render only when an archives page exists. A data-less
242
+ consumer now gets a fully working magazine homepage instead of empty chrome
243
+ and 404 links.
244
+ - **Zero-`_data` resilience across components.** The skin quick-select and
245
+ theme customizer fall back to the compiled skin registry when
246
+ `_data/theme_skins.yml` is absent; the page-feedback widget falls back to a
247
+ generic four-type taxonomy when `_data/feedback_types.yml` is absent; the
248
+ landing get-started band (theme install cards) renders only when the site
249
+ ships a `landing.get_started` block — a data-less consumer no longer
250
+ advertises the theme's own install methods.
251
+ - **`home` layout accepts `hide_intro` as an alias of `hide_title`**, matching
252
+ the flag other layouts use for "suppress the generated page header".
253
+ - **`halfmoon.js` honors `color_mode_default`.** The switcher no longer
254
+ overrides the configured default with the OS preference on first visit
255
+ (previously the page could flip modes right after load).
256
+ - The default skin fallback is now `air` everywhere (`root.html`,
257
+ controls bar, customizer) — `dark` was never a compiled skin, so the old
258
+ fallback selected a skin that doesn't exist.
259
+
260
+ ### Fixed
261
+
262
+ - **`_data/theme-manifest.yml` was 13 minors stale.** It sat at `1.14.0` while
263
+ the theme shipped to `1.27.0`, so every SHA-256 in it was wrong and
264
+ `scripts/bin/audit-consumer` — which reads it to classify consumer files —
265
+ silently compared against a June snapshot. Root cause: the file's header said
266
+ it regenerated during `scripts/bin/release`, but release-please became the
267
+ canonical release path and knows nothing about it. Regenerated, and now held
268
+ current by a CI gate (`version.rb` ↔ manifest version) plus
269
+ `manifest-sync.yml`, which regenerates it inside the release PR.
270
+ - **`test/fixtures/consumer-gem/_layouts/default.html` had drifted from the
271
+ theme layout it mirrors**, so the "`--fix` deletes IDENTICAL files" audit test
272
+ had been failing since the layout changed in #310 — the fixture was being
273
+ classified `DIFFERS_UNJUSTIFIED` rather than `IDENTICAL`, testing the opposite
274
+ of its intent. Re-synced; the audit suite is green again (21/21).
275
+ - **Mobile overlays no longer trap under the page chrome.** The
276
+ `.zer0-bg-body` background utility isolates the body stacking context and
277
+ pushes the noise overlay to `z-index: -1` instead of force-elevating every
278
+ direct child (`position: relative; z-index: 1`). That old rule made `<main>`
279
+ a stacking context that (a) capped the docs-sidebar offcanvas below the
280
+ fixed header — its close button was unreachable on mobile — and (b) beat
281
+ FAB `position: fixed` rules on specificity, dropping them into flow and
282
+ adding 16px of horizontal scroll on content pages.
283
+ - **Main-nav offcanvas paints above its backdrop on mobile.** The fixed header
284
+ is lifted over Bootstrap's body-level backdrop only while `#bdNavbar` is
285
+ opening/open/closing, so the menu is actually tappable instead of rendering
286
+ dimmed behind the backdrop.
287
+ - **The unified drawer's Search button works.** It targeted `#search-modal`
288
+ but the modal's id is `#siteSearchModal`; it now uses the shared
289
+ `data-search-toggle` binding, which also closes the drawer before opening
290
+ search.
291
+ - The measured header height is published as `--zer0-header-height` on
292
+ `:root` (set by `auto-hide-nav.js`), so stylesheets can align overlays with
293
+ the real header instead of hard-coding its pixel height.
294
+
295
+ ### Added
296
+
297
+ - **ABC board books (`book-abc` layout) — the "ABC & Language" series.** A new
298
+ immersive, single-scroll toddler alphabet layout (`_layouts/book-abc.html`)
299
+ driven entirely by an `alphabet:` front-matter list — one big letter, one
300
+ word, one picture per card, with A–Z quick-jump anchors and a cover hero.
301
+ The big letter is HTML typography (never baked into the art), and a card
302
+ whose plate is still `planned` shows a tinted "illustration coming soon"
303
+ placeholder so a book reads end-to-end before any art renders. New
304
+ `_includes/components/abc-letter.html` renders each card; `_sass/components/_book.scss`
305
+ gains `.abc-board` / `.abc-letter-*` styles plus a `.abc-style--<id>` skin per
306
+ shared art-style id. `components/bookshelf.html` now lists both `book` and
307
+ `book-abc` books, grouped into series shelves from `_data/series.yml`;
308
+ `book-card.html` shows a letter count for ABC books. Ships a demo book
309
+ (`pages/_books/abc-demo/`) + default series metadata (`_data/series.yml`).
310
+ Regression test [`test/visual/features/book-abc.spec.js`](test/visual/features/book-abc.spec.js)
311
+ and before/after evidence in
312
+ [`test/visual/evidence/book-abc/`](test/visual/evidence/book-abc/). Docs:
313
+ [`docs/features/abc-books.md`](docs/features/abc-books.md).
314
+ - **`nav: pages` sidebar mode — auto-build the left tree from page URLs.** A new
315
+ sidebar mode (`_includes/navigation/sidebar-pagetree.html`) derives a
316
+ collapsible section tree purely from page permalinks under a `sidebar.base`
317
+ prefix — no curated `_data/navigation/*.yml` file to write or keep in sync.
318
+ Enable per page / collection / site with `sidebar: {nav: pages, base: /docs/,
319
+ order_by: nav_order, title: …}`. It covers plain pages (`site.html_pages`) and
320
+ collection documents (`site.documents`) alike, groups them by first path
321
+ segment, humanizes section labels from the URL (never leaking a generic
322
+ "Index" title), sorts each section by a numeric `order_by` (natural order),
323
+ supports per-page `sidebar_label` / `sidebar_exclude`, and marks only the
324
+ current page active with its section expanded server-side — pure Liquid, so
325
+ it is GitHub Pages / `remote_theme` safe. Documented at
326
+ `/docs/features/sidebar-page-tree/`.
327
+ - **Claude Code OAuth in the AI installer** — the spec-driven installer
328
+ (`scripts/bin/install`) is now multi-provider. `scripts/install/ai/client.sh`
329
+ resolves a provider via `ZER0_AI_PROVIDER` (default `auto`), preferring the
330
+ logged-in `claude` CLI (**Claude Code OAuth** — zero key handling), then the
331
+ Anthropic Messages API (`CLAUDE_CODE_OAUTH_TOKEN` OAuth bearer or
332
+ `ANTHROPIC_API_KEY`), then OpenAI. `install doctor` reports the active
333
+ provider; `--ai-provider` / `--ai-model` / `ZER0_AI_MODEL` tune it; `--no-ai`
334
+ / `ZER0_NO_AI=1` disable it. User context is sanitized before every call.
335
+ - **Config-file layer for the installer** — new `scripts/install/config.sh`
336
+ discovers and merges `~/.config/zer0/install.yml`, `<target>/zer0.install.yml`,
337
+ `<target>/.zer0/config.yml`, and `--config FILE` (precedence:
338
+ defaults < profile < config < env < flags). Recognises site / github / theme /
339
+ deploy / agents / tasks / ai keys; API keys stay environment-only.
340
+ - **`install suggest`** subcommand — recommend a profile + deploy target
341
+ (AI-assisted with rule-based fallback) — plus `--yes` as an alias for
342
+ `--auto-accept`.
343
+ - Installer regression matrix (`test/test_installer.sh`) now covers AI
344
+ provider resolution, text extraction across provider shapes, the config-file
345
+ layer + precedence, and the `doctor` AI check (all offline).
346
+
347
+ ### Changed
348
+
349
+ - Installer spec default `ai.provider` is now `auto` (was `openai`); the AI
350
+ wizard records the provider that actually served the run.
351
+ - **CI workflows now honor `CLAUDE_CODE_OAUTH_TOKEN`** — `ui-audit.yml`,
352
+ `ai-content-review.yml`, and `ci-self-repair.yml` previously gated their
353
+ Claude Code agent tier on `ANTHROPIC_API_KEY` alone, silently ignoring a
354
+ configured OAuth token. They now follow the same house convention as
355
+ `claude.yml` / `issue-autopilot.yml`: `CLAUDE_CODE_OAUTH_TOKEN` preferred,
356
+ `ANTHROPIC_API_KEY` used only when it's absent.
357
+
358
+ ### Fixed
359
+
360
+ - **Left-sidebar nav highlighted every item as "active".** `nav-tree.html`
361
+ used {% raw %}`{% assign is_active = page.url == item.url %}`{% endraw %}, but Liquid `assign`
362
+ does not evaluate `==` — it stored `page.url` (always truthy), so every link
363
+ rendered with the active style. Replaced the three broken assigns
364
+ (`is_active`, `child_active`, `gc_active`) with proper conditional assigns so
365
+ only the current page is marked active.
366
+ - **`install deploy` no longer clobbers site content.** `spec_write` treated an
367
+ empty `SPEC_TASKS` as "use the full default task list", so a deploy-only run
368
+ re-ran `config`/`pages`/`nav` and overwrote a customised `_config.yml`,
369
+ `index.md`, and navigation. Empty now serialises to `[]`; `deploy` is
370
+ deploy-only and also skips agent-file rewrites.
371
+ - **github-pages / remote profile emitted a broken `remote_theme`.** The remote
372
+ `_config.yml` template resolved `{{GITHUB_REPO}}` to the *site's* repo; it now
373
+ uses a dedicated `{{THEME_REMOTE}}` variable (default `bamr87/zer0-mistakes`,
374
+ overridable via `THEME_REMOTE`).
375
+ - **Deploy workflow templates had unsubstituted variables.** Added
376
+ `{{DEFAULT_BRANCH}}`, `{{RUBY_VERSION}}`, and `{{SITE_NAME}}` to the template
377
+ renderer, so the generated `jekyll-gh-pages.yml` (and docker-prod/azure-swa
378
+ artifacts) no longer contain literal `{{…}}` tokens. Regression tests assert
379
+ no unresolved tokens survive in any deploy artifact.
380
+ - **Agent files were written twice** when `agents` appeared in both the task
381
+ list and `SPEC_AGENTS`; `apply.sh` now runs the agents task at most once.
382
+ - **The remote / github-pages Gemfile failed to build on Ruby 3.x.** It paired
383
+ the legacy `github-pages` gem with a standalone `jekyll-remote-theme`, which
384
+ bundler resolved to an ancient github-pages 170 (Jekyll 3.6 / kramdown 1.14,
385
+ `rexml` LoadError). The remote Gemfile now pins modern Jekyll +
386
+ `jekyll-remote-theme` + `webrick`, and both the Gemfile and the remote
387
+ `_config.yml` template add `jekyll-include-cache` (required by the theme's
388
+ layouts).
389
+
390
+ ## [1.27.0](https://github.com/bamr87/zer0-mistakes/compare/v1.26.0...v1.27.0) (2026-07-22)
391
+
392
+
393
+ ### Features
394
+
395
+ * **analytics:** wire posthog.html into head.html ([#317](https://github.com/bamr87/zer0-mistakes/issues/317)) ([7977fe1](https://github.com/bamr87/zer0-mistakes/commit/7977fe189dec3b62eb9779feac56ec27e513f2c3))
396
+ * **article:** show_hero front-matter flag opts standard posts into the hero image ([#309](https://github.com/bamr87/zer0-mistakes/issues/309)) ([6344473](https://github.com/bamr87/zer0-mistakes/commit/6344473c14c613825ab7ac9083b87f17a2a1e858))
397
+ * **i18n:** AI-generated multilingual support with language toggle ([#316](https://github.com/bamr87/zer0-mistakes/issues/316)) ([72b2ab6](https://github.com/bamr87/zer0-mistakes/commit/72b2ab6980742e26305a22e3aff535f465ade084))
398
+ * **scripts:** Claude-orchestrated preview images — Claude analyzes & reviews, image models render ([#296](https://github.com/bamr87/zer0-mistakes/issues/296)) ([acac205](https://github.com/bamr87/zer0-mistakes/commit/acac2051ec79a81c04efb1af76fb4031e106a852))
399
+
400
+
401
+ ### Bug Fixes
402
+
403
+ * **a11y:** keep a single &lt;main&gt; landmark per page ([#310](https://github.com/bamr87/zer0-mistakes/issues/310)) ([fae920e](https://github.com/bamr87/zer0-mistakes/commit/fae920eb5c60b5e792e9842c9b112bd5eb60e391)), closes [#299](https://github.com/bamr87/zer0-mistakes/issues/299)
404
+ * **author:** build avatar path with relative_url, never protocol-relative ([#306](https://github.com/bamr87/zer0-mistakes/issues/306)) ([ac597d2](https://github.com/bamr87/zer0-mistakes/commit/ac597d2c029157bb47da5a336812f764a3946da4))
405
+ * **ci:** secret scan matches added lines only, not context/removals/hunk headers ([#301](https://github.com/bamr87/zer0-mistakes/issues/301)) ([1b7175d](https://github.com/bamr87/zer0-mistakes/commit/1b7175ddece3d95aefc406f3b59df682db9fd0f2))
406
+ * **includes:** gate Quick Links Dev row on is_production ([#308](https://github.com/bamr87/zer0-mistakes/issues/308)) ([03b6877](https://github.com/bamr87/zer0-mistakes/commit/03b687713b8552c6ceb6b2ab30ac8caea0d6632d))
407
+ * **obsidian:** graph reads OBSIDIAN_CONFIG.wikiIndexUrl that js-cdn.html emits ([#305](https://github.com/bamr87/zer0-mistakes/issues/305)) ([2875912](https://github.com/bamr87/zer0-mistakes/commit/28759128b7b352a054e642d153e5e38e16aa1e48))
408
+ * **sass:** stack left-side FABs to prevent obsidian/feedback overlap ([#289](https://github.com/bamr87/zer0-mistakes/issues/289)) ([6cc4596](https://github.com/bamr87/zer0-mistakes/commit/6cc4596dff28d164386a16a6637420f84b4571bf))
409
+ * **test:** repoint ZER0-003 to test_install_legacy_flags.sh after retirement ([#292](https://github.com/bamr87/zer0-mistakes/issues/292)) ([fd7a2ce](https://github.com/bamr87/zer0-mistakes/commit/fd7a2ce256e32d2f630bd82732227b54ba13d3dd))
410
+ * **theme:** apply relative_url to preview_path exactly once ([#307](https://github.com/bamr87/zer0-mistakes/issues/307)) ([012e577](https://github.com/bamr87/zer0-mistakes/commit/012e577fe3131e0be42f7d33a6f5161dc666cc81))
411
+
8
412
  ## [1.26.0](https://github.com/bamr87/zer0-mistakes/compare/v1.25.0...v1.26.0) (2026-07-07)
9
413
 
10
414
 
@@ -12,10 +416,159 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
12
416
 
13
417
  * **feedback:** runtime "Improve this page" capture → GitHub issue (logs + AI triage) ([#286](https://github.com/bamr87/zer0-mistakes/issues/286)) ([f7fb20d](https://github.com/bamr87/zer0-mistakes/commit/f7fb20df74c22df7eb823344121c60617f06c7b6))
14
418
 
15
- ## [Unreleased]
419
+ ## Detailed notes — v1.27.0
16
420
 
17
- ### Changed
421
+ ### Added
422
+
423
+ - **Multilingual support via AI-generated translations** (ZER0-078) — the
424
+ theme now serves alternate-language versions of the site without storing
425
+ hand-written translations in source. English under `pages/**` +
426
+ `_data/ui-text.yml` `en` is the only human-maintained language; a
427
+ build-time utility ([`scripts/translate.rb`](scripts/translate.rb),
428
+ Claude Code OAuth with API-key fallback, stdlib-only Ruby) generates the
429
+ alternates and the
430
+ [`translate.yml`](.github/workflows/translate.yml) workflow commits them
431
+ via PR — one large `mode=full` dispatch job for the initial conversion,
432
+ plus bounded incremental runs whenever English content changes on `main`:
433
+ - Generated artifacts: `fr/**` page files (plain Jekyll pages with
434
+ explicit `/fr<en-url>` permalinks — GitHub Pages safe-mode friendly, no
435
+ plugin required), `_data/i18n/<lang>.yml` UI strings, and
436
+ `_data/i18n/manifest.yml` (en URL → translations map + SHA-based
437
+ incremental change detection with pruning of deleted sources).
438
+ - Markdown safety by construction: per-line segment translation with
439
+ `⟦N⟧` masking of code, Liquid, wiki-links and URLs, placeholder
440
+ set-equality validation with one retry, code fences never sent.
441
+ - Frontend: navbar **language toggle**
442
+ ([`language-toggle.html`](_includes/components/language-toggle.html))
443
+ with per-page availability states and `localStorage` preference,
444
+ `hreflang` alternates in `<head>`, per-page `<html lang>`, and a
445
+ machine-translation disclosure banner linking back to the original.
446
+ - Refactor: UI-string lookup centralized in
447
+ [`core/i18n.html`](_includes/core/i18n.html) and made per-page-language
448
+ aware (cached chrome includes take a `lang` parameter so each language
449
+ gets its own `include_cached` entry); the hand-maintained es/fr/de/ar
450
+ blocks in `_data/ui-text.yml` were **removed** in favor of the
451
+ generated pipeline.
452
+ - Offline test suite `test/test_i18n.sh` (stub provider, no network),
453
+ registered as the `i18n` suite in `test_runner.sh`, plus the
454
+ `language-toggle` Playwright smoke spec; docs in
455
+ [`docs/systems/multilingual-translation.md`](docs/systems/multilingual-translation.md).
456
+ (evidence:
457
+ [`test/visual/evidence/language-toggle/`](test/visual/evidence/language-toggle/README.md)
458
+ and
459
+ [`test/visual/evidence/language-toggle-fr/`](test/visual/evidence/language-toggle-fr/README.md)
460
+ — toggle at 6 widths + generated French page with 0px overflow, code
461
+ blocks byte-identical)
462
+
463
+ - **`show_hero` front-matter flag** — a post of any `post_type` can now opt
464
+ its `preview:` image into the top-of-article hero with `show_hero: true`,
465
+ without being promoted to `featured`/`breaking` (layout width, sidebar,
466
+ typography, and the post-type badge keep their `post_type` defaults);
467
+ existing posts are unchanged by construction — the flag is falsy everywhere
468
+ it isn't set ([#303](https://github.com/bamr87/zer0-mistakes/issues/303)).
469
+ (evidence:
470
+ [`test/visual/evidence/show-hero/`](test/visual/evidence/show-hero/README.md)
471
+ — hero renders only on the opted-in demo post; whole-build before/after
472
+ diff: exactly 1 page differs)
473
+
474
+ - **Claude-orchestrated preview-image engine** (ZER0-004) — the three
475
+ divergent implementations (1,404-line Bash engine, drifted Python
476
+ duplicate, dead Jekyll plugin) are consolidated into ONE Python engine,
477
+ [`scripts/lib/preview_generator.py`](scripts/lib/preview_generator.py).
478
+ Claude directs; an image model renders:
479
+ - **Analyze** (`prompt_engine: claude`, default) — Claude reads each
480
+ article and writes a subject-specific art-direction brief, replacing the
481
+ generic template prompt that produced unrepresentative banners.
482
+ - **Produce** — the configured renderer executes the brief: `openai`
483
+ (gpt-image-2 / DALL-E, default), `xai` (grok-2-image), `stability`,
484
+ **`gemini` (new)** — or `local`, upgraded from a `.txt` stub to a real
485
+ deterministic SVG/PNG banner (sanitized, rasterized via `rsvg-convert` →
486
+ `inkscape` → `magick` → the new Playwright helper
487
+ [`scripts/dev/rasterize-svg.js`](scripts/dev/rasterize-svg.js)).
488
+ - **Review** (`review_engine: claude`, default) — Claude inspects the
489
+ rendered image with vision against the article and style; when it
490
+ misrepresents the subject or contains text artifacts, Claude writes a
491
+ corrected prompt and the engine regenerates once.
492
+ - Claude credential chain: `CLAUDE_CODE_OAUTH_TOKEN` (`claude setup-token`)
493
+ → `ANTHROPIC_AUTH_TOKEN` → `ANTHROPIC_API_KEY` → a logged-in `claude` CLI
494
+ (zero-setup for Claude Code users); orchestration degrades gracefully to
495
+ template prompts without one.
496
+ - New unit suite [`test/test_preview_generator.py`](test/test_preview_generator.py)
497
+ (83 tests, zero network) wired into `test_core.sh`.
18
498
 
499
+ ### Fixed
500
+
501
+ - **Obsidian graph loads its wiki index on project sites** — the full-graph
502
+ renderer (`assets/js/obsidian-graph.js`) now reads the baseurl-aware
503
+ `window.OBSIDIAN_CONFIG.wikiIndexUrl` that `js-cdn.html` emits (falling back
504
+ to the legacy `OBSIDIAN_WIKI_INDEX_URL` override, then the `<base>`-relative
505
+ path), instead of only the never-set legacy global — whose fallback dropped
506
+ the baseurl and 404'd the `wiki-index.json` fetch on GitHub Pages project
507
+ sites, leaving `/docs/obsidian/graph/` a "Failed to load graph data" alert
508
+ ([#294](https://github.com/bamr87/zer0-mistakes/issues/294)). (evidence:
509
+ [`test/visual/evidence/obsidian-graph-index/`](test/visual/evidence/obsidian-graph-index/README.md)
510
+ — wiki-index fetch 404 → 200, alert → rendered graph, on a baseurl'd build)
511
+
512
+ - **Quick Links no longer ships a dead `localhost:4000` Dev row to production
513
+ visitors** — the env-switcher's Development URL row (rendered in the
514
+ site-wide Settings offcanvas on every page) is now wrapped in
515
+ {% raw %}`{% unless is_production %}`{% endraw %}, using the same `env-detect.html` flag the card
516
+ already keys its badge on; serving locally with `JEKYLL_ENV=production`
517
+ still counts as dev context, so the row stays available for Docker workflows
518
+ ([#298](https://github.com/bamr87/zer0-mistakes/issues/298)). (evidence:
519
+ [`test/visual/evidence/env-switcher-prod/`](test/visual/evidence/env-switcher-prod/README.md)
520
+ — production Settings-chrome `localhost:4000` occurrences 5 → 0, Dev row
521
+ intact in development)
522
+ - **Author avatars never render as protocol-relative `//assets/…` URLs** —
523
+ `author-avatar-url.html` now builds the relative-path branch in a capture,
524
+ collapses doubled slashes, and applies `relative_url` exactly once, instead
525
+ of the manual `{{ site.baseurl }}/{{ site.public_folder }}{{ avatar }}`
526
+ concatenation that produced `src="//assets/…"` (a URL browsers resolve
527
+ against a host named `assets`) on consumer sites where `public_folder` is
528
+ unset or carries a leading slash
529
+ ([#297](https://github.com/bamr87/zer0-mistakes/issues/297)). (evidence:
530
+ [`test/visual/evidence/author-avatar-url/`](test/visual/evidence/author-avatar-url/README.md)
531
+ — 4 protocol-relative avatars → 0, all avatars loading, on a misconfigured
532
+ consumer build)
533
+
534
+ - **Intro hero banner renders on project sites** — `content/intro.html` now
535
+ applies `relative_url` to the banner image exactly once (in the
536
+ `preview_path` assignment logic), instead of a second time at the point of
537
+ use, which doubled the baseurl segment on any site with a non-empty
538
+ `baseurl` (`url('/reponame/reponame/assets/…')` → 404 → gradient-only hero)
539
+ and mangled absolute preview URLs
540
+ ([#293](https://github.com/bamr87/zer0-mistakes/issues/293)). (evidence:
541
+ [`test/visual/evidence/intro-banner-baseurl/`](test/visual/evidence/intro-banner-baseurl/README.md)
542
+ — background fetch 404 → 200 on both preview branches of a baseurl'd build)
543
+
544
+ - **Preview-image config keys `enabled`, `assets_prefix`, `auto_prefix` and
545
+ `collections` are now honored by the generator** (the Bash engine ignored
546
+ them), and front-matter `preview:` updates are scoped to the front-matter
547
+ block only — the previous file-wide `sed` could corrupt body lines starting
548
+ with `preview:`. External-URL previews are no longer silently regenerated,
549
+ and a configured model from the wrong vendor family falls back to the
550
+ provider default instead of a guaranteed API error.
551
+
552
+ - **Left-side FAB overlap** — the Obsidian local-graph button and the
553
+ page-feedback "Improve this page" button were both positioned at
554
+ `bottom: 1rem; left: 1rem` and completely overlapped each other. The
555
+ Obsidian FAB is now at slot 2 (`bottom: offset + size + gap`), stacking
556
+ above the feedback FAB — the same pattern the TOC FAB uses above
557
+ back-to-top on the right side. The docs-sidebar restore-mode FAB was
558
+ also offset to slot 2 so it cannot overlap the feedback FAB on mobile.
559
+ Closes #288. (evidence:
560
+ [`test/visual/evidence/fab-stack/`](test/visual/evidence/fab-stack/README.md)
561
+ — feedback↔obsidian FAB overlap 3136px² → 0; regression test
562
+ [`test/visual/features/fab-stack.spec.js`](test/visual/features/fab-stack.spec.js)).
563
+
564
+ ### Changed
565
+
566
+ - **Preview-image generation is now Claude-orchestrated by default**
567
+ (`prompt_engine: claude` + `review_engine: claude`; disable with
568
+ `--prompt-engine template --review none`). The renderer default stays
569
+ `openai`. `scripts/features/generate-preview-images` is now a thin wrapper
570
+ delegating to the Python engine (full CLI flag surface preserved;
571
+ `rake preview:*` and the VS Code tasks work unchanged).
19
572
  - **CI test tiering** — the PR-blocking Playwright gate is now the new
20
573
  `critical` project (118 `@critical`-tagged, user-facing tests: navigation,
21
574
  search, mobile survival, theming baseline, security) instead of the full
@@ -51,7 +604,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
51
604
  * **navigation:** remove redundant entries from navigation menus ([916eca2](https://github.com/bamr87/zer0-mistakes/commit/916eca269726c984c5c0c9dc9533c83d9975f906))
52
605
  * **sass:** mobile audit — contain author card, restore consent z-order, grow tap targets ([#275](https://github.com/bamr87/zer0-mistakes/issues/275)) ([8ec9867](https://github.com/bamr87/zer0-mistakes/commit/8ec9867858ff87e9b8a6fcd4a68ec7e50582c3c1))
53
606
 
54
- ## [Unreleased]
607
+ ## Detailed notes — v1.25.0
55
608
 
56
609
  ### Changed
57
610
 
@@ -161,7 +714,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
161
714
  * **search:** existence-gate search modal form action for remote-theme consumers ([#257](https://github.com/bamr87/zer0-mistakes/issues/257)) ([b915c4d](https://github.com/bamr87/zer0-mistakes/commit/b915c4dead5eff3d6a2986720ebacdcf2bed5e33))
162
715
  * **skins:** map dark + contrast skins to their background assets ([#245](https://github.com/bamr87/zer0-mistakes/issues/245)) ([fb8808e](https://github.com/bamr87/zer0-mistakes/commit/fb8808e5ec754785b8239b01bccfab99569b70fa)), closes [#240](https://github.com/bamr87/zer0-mistakes/issues/240)
163
716
 
164
- ## [Unreleased]
717
+ ## Detailed notes — v1.24.0–v1.25.0
165
718
 
166
719
  ### Bug Fixes
167
720
 
@@ -252,7 +805,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
252
805
 
253
806
  * remove Year of AI / org content-hub pivot, restore theme landing page ([#180](https://github.com/bamr87/zer0-mistakes/issues/180)) ([3960f65](https://github.com/bamr87/zer0-mistakes/commit/3960f65b1af4c750216deda0d032a309b0f93b78))
254
807
 
255
- ## [Unreleased]
808
+ ## Detailed notes — v1.24.0
256
809
 
257
810
  ### Added
258
811
  - **Visual-evidence standard + reusable evidence kit.** UI/behavioural changes