diff --git a/.github/workflows/jekyll-gh-pages.yml b/.github/workflows/tbdocs-gh-pages.yml
similarity index 100%
rename from .github/workflows/jekyll-gh-pages.yml
rename to .github/workflows/tbdocs-gh-pages.yml
diff --git a/builder/PLAN-13.md b/builder/PLAN-13.md
new file mode 100644
index 00000000..4190530c
--- /dev/null
+++ b/builder/PLAN-13.md
@@ -0,0 +1,499 @@
+## PLAN-13: Phase 13 --- Inline SVG embedding with zoom / export controls
+
+Replaces `` rendering with build-time SVG inlining,
+and unifies the gantt-chart injection with the same pipeline. Every
+`.svg` image referenced from markdown (including `.dot`-generated
+diagrams) is read from disk at render time, embedded directly in the
+page HTML, and wrapped in a controls bar (Download SVG, Copy SVG,
+Download PNG, Copy PNG) with a click-to-zoom fullscreen overlay.
+
+The gantt chart on the BuildInfo page flows through the same
+markdown-it plugin as every other SVG. Because the gantt's timing
+data is not available until after the build completes, the plugin
+inlines a committed placeholder SVG; a minimal post-build step
+substitutes the real content into the already-written HTML. The
+wrapper markup, controls, zoom wiring, and JS are identical ---
+the only difference is *when* the SVG content arrives.
+
+What Phase 13 does NOT do:
+
+- Add dark-mode colour support for Graphviz `.dot` diagrams. The
+ gantt SVG already carries `html.dark-mode` CSS rules that work
+ because it is inline; the `.dot` SVGs generated by
+ `@hpcc-js/wasm-graphviz` do not. Adding dark-mode classes to
+ `.dot` source files is a separate concern.
+- Change the existing `.dot` → `.svg` regeneration pipeline
+ (`dot.mjs`). Diagrams are still rendered from `.dot` source by
+ WASM Graphviz; this plan only changes how the resulting `.svg`
+ files are embedded in pages. (One `.dot` source fix WAS needed:
+ `pdf-render-pipeline.dot` had `margin=12` in its `graph [...]`
+ defaults block, which Graphviz interprets as 12 *inches* on the
+ root graph --- producing a 2239×2406 pt SVG with 864 pt of dead
+ space. Moved the `margin=12` to each `subgraph cluster_*` where
+ it means 12 *points* of intra-cluster padding.)
+- Touch the offline or PDF build passes. Inline SVGs are already
+ part of the page HTML by the time those passes run; no
+ rewriting is needed.
+- Add new npm dependencies.
+
+Target wall-clock impact: negligible. The three `.dot` SVGs
+total ~30 KB; reading them adds < 1 ms to the dispatch task. The
+per-page render cost is unchanged (string concatenation vs.
+`
` tag emission).
+
+---
+
+## 1. Current state
+
+SVG images in markdown (``) render
+as `
`. The browser makes a
+separate HTTP request per SVG. Page CSS cannot reach the SVG
+internals (dark-mode styling, link colours, etc.). No zoom or
+download controls exist.
+
+The gantt chart on the BuildInfo page has a separate code path:
+`injectGanttChart()` in `tbdocs.mjs` (lines 756--816) runs after
+the build completes and patches the written HTML, injecting the
+inline SVG, four control links, a PNG-export function, and a
+click-to-zoom script. This is ~60 lines of self-contained HTML /
+JS generation that duplicates the pattern every future SVG would need.
+
+---
+
+## 2. Architecture
+
+### 2.1. Data flow: SVG contents through the pipeline
+
+```
+ dot.mjs (seed) dispatch (main thread)
+ ┌─────────────────┐ ┌──────────────────────────┐
+ │ .dot → .svg │──staticFiles──▸ │ reads .svg file contents │
+ │ regeneration │ │ into svgContentsMap │
+ └─────────────────┘ │ packs into sharedSAB │
+ └────────────┬─────────────┘
+ docs/assets/images/gantt.svg │
+ (committed placeholder) │
+ ───── also in staticFiles ────────────────────────┘
+ │
+ ┌──────────────────────┘
+ ▼
+ ┌──────────────────────────────────┐
+ │ cpu-worker renderEnvInit │
+ │ unpackShared → svgContentsMap │
+ │ createMarkdownIt({ ..., │
+ │ svgContents }) │
+ └──────────────┬───────────────────┘
+ ▼
+ ┌──────────────────────────────────┐
+ │ svgInlinePlugin (image renderer) │
+ │ • src ends in .svg? │
+ │ • content in ctx.svgContents? │
+ │ → emit wrapper + inline SVG │
+ │ → set page.hasSvg = true │
+ └──────────────┬───────────────────┘
+ ▼
+ ┌──────────────────────────────────┐
+ │ templatePhase / renderHead │
+ │ page.hasSvg → include │
+ │ svg-inline.js \n`
+ : "")
+```
+
+`defer` keeps the load non-blocking, same as `theme-switch.js`.
+
+### 5.4. `svg-inline.js`
+
+New file at `docs/assets/js/svg-inline.js`. ~80 lines, no
+dependencies, IIFE.
+
+Behaviours:
+
+1. **CSS injection**. On load, inject a `