diff --git a/assets/README.md b/assets/README.md
new file mode 100644
index 00000000..918d4674
--- /dev/null
+++ b/assets/README.md
@@ -0,0 +1,20 @@
+# Assets
+
+## `demo.cast` / `demo.svg`
+
+`demo.svg` (embedded in the root `README.md`) is rendered from `demo.cast`, an
+[asciinema v2](https://docs.asciinema.org/manual/asciicast/v2/) recording.
+
+The cast is staged (hand-authored timings), but **every output line must match
+the real CLI byte for byte** — the scaffold lines `zuke setup --yes` prints, the
+`--list` format (plain text, two-space column padding), and the executor's run
+output (an 80-wide `═` header block per target, a blank line after each
+target's footer, the Build Summary table, and the closing verdict line). When
+the renderer or `zuke setup` output changes, re-verify each frame against a
+real run and update the cast in the same PR.
+
+Render the SVG with [svg-term-cli](https://github.com/marionebl/svg-term-cli):
+
+```sh
+npx svg-term-cli --in assets/demo.cast --out assets/demo.svg --window
+```
diff --git a/assets/demo.cast b/assets/demo.cast
index c50a9d91..c8612e0e 100644
--- a/assets/demo.cast
+++ b/assets/demo.cast
@@ -1,111 +1,132 @@
-{"version":2,"width":90,"height":30,"timestamp":1719045600,"env":{"SHELL":"/bin/bash","TERM":"xterm-256color"}}
+{"version":2,"width":90,"height":30,"timestamp":1755079380,"env":{"SHELL":"/bin/bash","TERM":"xterm-256color"}}
[0.6,"o","\u001b[32m❯\u001b[0m "]
-[1.05,"o","d"]
-[1.09,"o","e"]
-[1.13,"o","n"]
-[1.17,"o","o"]
-[1.21,"o"," "]
-[1.25,"o","r"]
-[1.29,"o","u"]
-[1.33,"o","n"]
-[1.37,"o"," "]
-[1.41,"o","-"]
-[1.45,"o","A"]
-[1.49,"o"," "]
-[1.53,"o","j"]
-[1.57,"o","s"]
-[1.61,"o","r"]
-[1.65,"o",":"]
-[1.69,"o","@"]
-[1.73,"o","z"]
-[1.77,"o","u"]
-[1.81,"o","k"]
-[1.85,"o","e"]
-[1.89,"o","/"]
-[1.93,"o","c"]
-[1.97,"o","l"]
-[2.01,"o","i"]
-[2.05,"o"," "]
-[2.09,"o","s"]
-[2.13,"o","e"]
-[2.17,"o","t"]
-[2.21,"o","u"]
-[2.25,"o","p"]
-[2.64,"o","\r\n"]
-[3.14,"o","\u001b[32m✓\u001b[0m Scaffolded \u001b[1mzuke.ts\u001b[0m, ./zuke, ./zuke.ps1, deno.json\r\n"]
-[3.89,"o","\u001b[32m❯\u001b[0m "]
-[4.34,"o","c"]
-[4.38,"o","a"]
-[4.42,"o","t"]
-[4.46,"o"," "]
-[4.5,"o","z"]
-[4.54,"o","u"]
-[4.58,"o","k"]
-[4.62,"o","e"]
-[4.66,"o","."]
-[4.7,"o","t"]
-[4.74,"o","s"]
-[5.13,"o","\r\n"]
-[5.43,"o","\u001b[90mimport\u001b[0m { Build, target, run } \u001b[90mfrom\u001b[0m \"jsr:@zuke/core\";\r\n"]
-[5.48,"o","\u001b[90mimport\u001b[0m { DenoTasks } \u001b[90mfrom\u001b[0m \"jsr:@zuke/deno\";\r\n"]
-[5.53,"o","\r\n"]
-[5.58,"o","\u001b[90mclass\u001b[0m \u001b[33mPipeline\u001b[0m \u001b[90mextends\u001b[0m Build {\r\n"]
-[5.63,"o"," lint = target().executes(() => DenoTasks.lint());\r\n"]
-[5.68,"o"," test = target().dependsOn(\u001b[36mthis.lint\u001b[0m) \u001b[90m// a reference, not \"lint\"\u001b[0m\r\n"]
-[5.73,"o"," .executes(() => DenoTasks.test((s) => s.allowAll()));\r\n"]
-[5.78,"o","}\r\n"]
-[5.83,"o","\r\n"]
-[5.88, "o", "\u001b[90mawait\u001b[0m run(Pipeline);\r\n"]
-[6.78,"o","\u001b[32m❯\u001b[0m "]
-[7.23,"o","."]
-[7.27,"o","/"]
-[7.31,"o","z"]
-[7.35,"o","u"]
-[7.39,"o","k"]
-[7.43,"o","e"]
-[7.47,"o"," "]
-[7.51,"o","-"]
-[7.55,"o","-"]
-[7.59,"o","l"]
-[7.63,"o","i"]
-[7.67,"o","s"]
-[7.71,"o","t"]
-[8.1,"o","\r\n"]
-[8.5,"o","\u001b[1mTargets:\u001b[0m\r\n"]
-[8.62,"o"," lint Lint the workspace\r\n"]
-[8.74,"o"," test Run the test suite \u001b[90m(depends on: lint)\u001b[0m\r\n"]
-[9.55,"o","\u001b[32m❯\u001b[0m "]
-[10,"o","."]
-[10.04,"o","/"]
-[10.08,"o","z"]
-[10.12,"o","u"]
-[10.16,"o","k"]
-[10.2,"o","e"]
-[10.24,"o"," "]
-[10.28,"o","t"]
-[10.32,"o","e"]
-[10.36,"o","s"]
-[10.4,"o","t"]
-[10.79,"o","\r\n"]
-[11.29,"o","\u001b[2m════════════════════════════════════════════════════════════════════════════════\u001b[0m\r\n"]
-[11.34,"o","\u001b[1m\u001b[36mlint\u001b[0m\r\n"]
-[11.39,"o","\u001b[2m════════════════════════════════════════════════════════════════════════════════\u001b[0m\r\n"]
-[11.84,"o","\u001b[32m✔\u001b[0m lint \u001b[2msucceeded in 0.1s\u001b[0m\r\n"]
-[12.19,"o","\u001b[2m════════════════════════════════════════════════════════════════════════════════\u001b[0m\r\n"]
-[12.24,"o","\u001b[1m\u001b[36mtest\u001b[0m\r\n"]
-[12.29,"o","\u001b[2m════════════════════════════════════════════════════════════════════════════════\u001b[0m\r\n"]
-[12.64,"o","\u001b[2mrunning 3 tests\u001b[0m\r\n"]
-[12.69,"o","\u001b[32mok\u001b[0m | 3 passed | 0 failed \u001b[2m(0.8s)\u001b[0m\r\n"]
-[13.19,"o","\u001b[32m✔\u001b[0m test \u001b[2msucceeded in 0.8s\u001b[0m\r\n"]
-[13.69,"o","\r\n"]
-[13.75,"o","\u001b[1mBuild Summary\u001b[0m\r\n"]
-[13.81,"o","\u001b[2m───────────────────────────\u001b[0m\r\n"]
-[13.87,"o","\u001b[1mTarget Status Duration\u001b[0m\r\n"]
-[13.93,"o","\u001b[2m───────────────────────────\u001b[0m\r\n"]
-[13.99,"o","lint \u001b[32mSucceeded\u001b[0m 0.1s\r\n"]
-[14.05,"o","test \u001b[32mSucceeded\u001b[0m 0.8s\r\n"]
-[14.11,"o","\u001b[2m───────────────────────────\u001b[0m\r\n"]
-[14.17,"o","\u001b[1mTotal \u001b[0m \u001b[1m 0.9s\u001b[0m\r\n"]
-[14.23,"o","\r\n"]
-[14.29,"o","\u001b[1m\u001b[32m✔ Build succeeded — 2/2 targets in 0.9s · 2026-06-23 12:00\u001b[0m\r\n"]
-[15.75,"o","\u001b[32m❯\u001b[0m "]
+[1.09,"o","d"]
+[1.13,"o","e"]
+[1.17,"o","n"]
+[1.21,"o","o"]
+[1.25,"o"," "]
+[1.29,"o","r"]
+[1.33,"o","u"]
+[1.37,"o","n"]
+[1.41,"o"," "]
+[1.45,"o","-"]
+[1.49,"o","A"]
+[1.53,"o"," "]
+[1.57,"o","j"]
+[1.61,"o","s"]
+[1.65,"o","r"]
+[1.69,"o",":"]
+[1.73,"o","@"]
+[1.77,"o","z"]
+[1.81,"o","u"]
+[1.85,"o","k"]
+[1.89,"o","e"]
+[1.93,"o","/"]
+[1.97,"o","c"]
+[2.01,"o","l"]
+[2.05,"o","i"]
+[2.09,"o"," "]
+[2.13,"o","s"]
+[2.17,"o","e"]
+[2.21,"o","t"]
+[2.25,"o","u"]
+[2.29,"o","p"]
+[2.33,"o"," "]
+[2.37,"o","-"]
+[2.41,"o","-"]
+[2.45,"o","y"]
+[2.49,"o","e"]
+[2.53,"o","s"]
+[2.88,"o","\r\n"]
+[3.78,"o","Scaffolding Zuke into the current directory:\r\n"]
+[4.03,"o"," create zuke.ts\r\n"]
+[4.15,"o"," create zuke\r\n"]
+[4.27,"o"," create zuke.ps1\r\n"]
+[4.39,"o"," create zuke.json\r\n"]
+[4.51,"o"," create deno.json\r\n"]
+[4.63,"o"," create .gitignore\r\n"]
+[4.93,"o","Done — 6 file(s) written. Next: ./zuke\r\n"]
+[5.83,"o","\u001b[32m❯\u001b[0m "]
+[6.32,"o","c"]
+[6.36,"o","a"]
+[6.4,"o","t"]
+[6.44,"o"," "]
+[6.48,"o","z"]
+[6.52,"o","u"]
+[6.56,"o","k"]
+[6.6,"o","e"]
+[6.64,"o","."]
+[6.68,"o","t"]
+[6.72,"o","s"]
+[7.07,"o","\r\n"]
+[7.47,"o","\u001b[90mimport\u001b[0m { Build, run, target } \u001b[90mfrom\u001b[0m \"jsr:@zuke/core@^1\";\r\n"]
+[7.52,"o","\u001b[90mimport\u001b[0m { DenoTasks } \u001b[90mfrom\u001b[0m \"jsr:@zuke/deno@^1\";\r\n"]
+[7.57,"o","\r\n"]
+[7.62,"o","\u001b[90mclass\u001b[0m \u001b[33mPipeline\u001b[0m \u001b[90mextends\u001b[0m Build {\r\n"]
+[7.67,"o"," lint = target()\r\n"]
+[7.72,"o"," .description(\"Lint the workspace\")\r\n"]
+[7.77,"o"," .executes(() => DenoTasks.lint());\r\n"]
+[7.82,"o","\r\n"]
+[7.87,"o"," test = target()\r\n"]
+[7.92,"o"," .description(\"Run the test suite\")\r\n"]
+[7.97,"o"," .dependsOn(\u001b[36mthis.lint\u001b[0m) \u001b[90m// a reference, not \"lint\"\u001b[0m\r\n"]
+[8.02,"o"," .executes(() => DenoTasks.test((s) => s.allowAll()));\r\n"]
+[8.07,"o","}\r\n"]
+[8.12,"o","\r\n"]
+[8.17,"o","\u001b[90mawait\u001b[0m run(Pipeline);\r\n"]
+[9.17,"o","\u001b[32m❯\u001b[0m "]
+[9.66,"o","."]
+[9.7,"o","/"]
+[9.74,"o","z"]
+[9.78,"o","u"]
+[9.82,"o","k"]
+[9.86,"o","e"]
+[9.9,"o"," "]
+[9.94,"o","-"]
+[9.98,"o","-"]
+[10.02,"o","l"]
+[10.06,"o","i"]
+[10.1,"o","s"]
+[10.14,"o","t"]
+[10.49,"o","\r\n"]
+[10.89,"o","Targets:\r\n"]
+[10.94,"o"," lint Lint the workspace\r\n"]
+[10.99,"o"," test Run the test suite (depends on: lint)\r\n"]
+[11.99,"o","\u001b[32m❯\u001b[0m "]
+[12.48,"o","."]
+[12.52,"o","/"]
+[12.56,"o","z"]
+[12.6,"o","u"]
+[12.64,"o","k"]
+[12.68,"o","e"]
+[12.72,"o"," "]
+[12.76,"o","t"]
+[12.8,"o","e"]
+[12.84,"o","s"]
+[12.88,"o","t"]
+[13.23,"o","\r\n"]
+[13.73,"o","\u001b[2m════════════════════════════════════════════════════════════════════════════════\u001b[0m\r\n"]
+[13.78,"o","\u001b[1m\u001b[36mlint\u001b[0m\r\n"]
+[13.83,"o","\u001b[2m════════════════════════════════════════════════════════════════════════════════\u001b[0m\r\n"]
+[14.73,"o","Checked 24 files\r\n"]
+[15.03,"o","\u001b[32m✔\u001b[0m lint \u001b[2msucceeded in 1.2s\u001b[0m\r\n"]
+[15.08,"o","\r\n"]
+[15.43,"o","\u001b[2m════════════════════════════════════════════════════════════════════════════════\u001b[0m\r\n"]
+[15.48,"o","\u001b[1m\u001b[36mtest\u001b[0m\r\n"]
+[15.53,"o","\u001b[2m════════════════════════════════════════════════════════════════════════════════\u001b[0m\r\n"]
+[16.03,"o","running 3 tests from ./tests/pipeline_test.ts\r\n"]
+[17.43,"o","\u001b[32mok\u001b[0m | 3 passed | 0 failed \u001b[2m(1.9s)\u001b[0m\r\n"]
+[17.73,"o","\u001b[32m✔\u001b[0m test \u001b[2msucceeded in 2.4s\u001b[0m\r\n"]
+[17.78,"o","\r\n"]
+[18.18,"o","\u001b[1mBuild Summary\u001b[0m\r\n"]
+[18.24,"o","\u001b[2m───────────────────────────\u001b[0m\r\n"]
+[18.3,"o","\u001b[1mTarget Status Duration\u001b[0m\r\n"]
+[18.36,"o","\u001b[2m───────────────────────────\u001b[0m\r\n"]
+[18.42,"o","lint \u001b[32mSucceeded\u001b[0m 1.2s\r\n"]
+[18.48,"o","test \u001b[32mSucceeded\u001b[0m 2.4s\r\n"]
+[18.54,"o","\u001b[2m───────────────────────────\u001b[0m\r\n"]
+[18.6,"o","\u001b[1mTotal \u001b[0m \u001b[1m 3.6s\u001b[0m\r\n"]
+[18.66,"o","\r\n"]
+[18.72,"o","\u001b[1m\u001b[32m✔ Build succeeded — 2/2 targets in 3.6s · 2026-08-13 10:03\u001b[0m\r\n"]
+[19.92,"o","\u001b[32m❯\u001b[0m "]
+[23.42,"o","\u001b[0m"]
diff --git a/assets/demo.svg b/assets/demo.svg
index 676340cc..18492ae1 100644
--- a/assets/demo.svg
+++ b/assets/demo.svg
@@ -1 +1 @@
-
\ No newline at end of file
+❯❯deno❯denorun❯denorun-A❯denorun-Ajsr:@zuke/cli❯denorun-Ajsr:@zuke/clisetup❯denorun-Ajsr:@zuke/clisetup--yesScaffoldingZukeintothecurrentdirectory:createzuke.tscreatezukecreatezuke.ps1createzuke.jsoncreatedeno.jsoncreate.gitignoreDone—6file(s)written.Next:./zuke❯cat❯catzuke.tsimport{Build,run,target}from"jsr:@zuke/core@^1";import{DenoTasks}from"jsr:@zuke/deno@^1";classPipelineextendsBuild{lint=target().description("Linttheworkspace").executes(()=>DenoTasks.lint());test=target().description("Runthetestsuite").dependsOn(this.lint)//areference,not"lint".executes(()=>DenoTasks.test((s)=>s.allowAll()));}awaitrun(Pipeline);❯.❯./❯./z❯./zu❯./zuk❯./zuke❯./zuke--listTargets:lintLinttheworkspacetestRunthetestsuite(dependson:lint)❯./zuketest════════════════════════════════════════════════════════════════════════════════lintChecked24files✔lintsucceededin1.2stestrunning3testsfrom./tests/pipeline_test.tsok|3passed|0failed(1.9s)✔testsucceededin2.4sBuildSummary───────────────────────────TargetStatusDurationlintSucceeded1.2stestSucceeded2.4sTotal3.6s✔Buildsucceeded—2/2targetsin3.6s·2026-08-1310:03❯d❯de❯den❯denor❯denoru❯denorun-❯denorun-Aj❯denorun-Ajs❯denorun-Ajsr❯denorun-Ajsr:❯denorun-Ajsr:@❯denorun-Ajsr:@z❯denorun-Ajsr:@zu❯denorun-Ajsr:@zuk❯denorun-Ajsr:@zuke❯denorun-Ajsr:@zuke/❯denorun-Ajsr:@zuke/c❯denorun-Ajsr:@zuke/cl❯denorun-Ajsr:@zuke/clis❯denorun-Ajsr:@zuke/clise❯denorun-Ajsr:@zuke/cliset❯denorun-Ajsr:@zuke/clisetu❯denorun-Ajsr:@zuke/clisetup-❯denorun-Ajsr:@zuke/clisetup--❯denorun-Ajsr:@zuke/clisetup--y❯denorun-Ajsr:@zuke/clisetup--ye❯c❯ca❯catz❯catzu❯catzuk❯catzuke❯catzuke.❯catzuke.t❯./zuke-❯./zuke--❯./zuke--l❯./zuke--li❯./zuke--lis❯./zuket❯./zukete❯./zuketes
\ No newline at end of file