diff --git a/ATTRIBUTION.md b/ATTRIBUTION.md
index 10635b69..9caf1c27 100644
--- a/ATTRIBUTION.md
+++ b/ATTRIBUTION.md
@@ -16,7 +16,22 @@ iconography, images or other Adobe assets; open licences only; every asset attri
| `assets/fonts/Inter-Italic.ttf` | The Inter Project Authors | https://github.com/rsms/inter | OFL-1.1 (`assets/fonts/OFL-Inter.txt`) |
| `assets/fonts/NotoSerif-Regular.ttf` | The Noto Project Authors | https://github.com/notofonts/latin-greek-cyrillic | OFL-1.1 (`assets/fonts/OFL-NotoSerif.txt`) |
| `assets/fonts/JetBrainsMono-Regular.ttf` | The JetBrains Mono Project Authors | https://github.com/JetBrains/JetBrainsMono | OFL-1.1 (`assets/fonts/OFL-JetBrainsMono.txt`) |
-| `apps/effectcraft-web/web/favicon.svg` | EffectCraft contributors | Original work: web app icon, the code-drawn EffectCraft mark (split rounded square, keyframe diamond) as SVG | MIT OR Apache-2.0 |
+| `apps/effectcraft-web/web/favicon.svg` | EffectCraft contributors | Original work: web app icon, a copy of `assets/app-icon/effectcraft-small.svg` (the unicorn app icon) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
+| `assets/app-icon/effectcraft-1024.png` | EffectCraft contributors | Original work: effectCraft app icon, 1024 px (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
+| `assets/app-icon/effectcraft-macos-512.png` | EffectCraft contributors | Original work: effectCraft runtime Dock icon (macOS) (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
+| `assets/app-icon/effectcraft-small.svg` | EffectCraft contributors | Original work: effectCraft app icon, light vector (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
+| `assets/app-icon/effectcraft.icns` | EffectCraft contributors | Original work: effectCraft macOS app icon (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
+| `assets/app-icon/effectcraft.ico` | EffectCraft contributors | Original work: effectCraft Windows app icon (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
+| `assets/app-icon/effectcraft.svg` | EffectCraft contributors | Original work: effectCraft app icon, master vector (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
+| `assets/app-icon/hicolor/128x128/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 128x128 (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
+| `assets/app-icon/hicolor/16x16/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 16x16 (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
+| `assets/app-icon/hicolor/24x24/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 24x24 (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
+| `assets/app-icon/hicolor/256x256/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 256x256 (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
+| `assets/app-icon/hicolor/32x32/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 32x32 (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
+| `assets/app-icon/hicolor/48x48/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 48x48 (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
+| `assets/app-icon/hicolor/512x512/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 512x512 (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
+| `assets/app-icon/hicolor/64x64/apps/ai.storyteller.effectcraft.png` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor 64x64 (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
+| `assets/app-icon/hicolor/scalable/apps/ai.storyteller.effectcraft.svg` | EffectCraft contributors | Original work: effectCraft app icon, Linux hicolor scalable (owner's ArtCraft unicorn drawing, vectorised) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) |
| `crates/lottie/tests/fixtures/bouncing-ball.json` | EffectCraft contributors | Original work: hand-written Lottie test fixture (bouncing ball, eased position keys) | MIT OR Apache-2.0 |
| `crates/lottie/tests/fixtures/shapes-gradient.json` | EffectCraft contributors | Original work: hand-written Lottie test fixture (star, rectangle, gradient fill, dashed stroke, trim paths) | MIT OR Apache-2.0 |
| `crates/lottie/tests/fixtures/precomp-matte-text.json` | EffectCraft contributors | Original work: hand-written Lottie test fixture (precomp, track matte, parented text) | MIT OR Apache-2.0 |
diff --git a/Cargo.lock b/Cargo.lock
index 68e5616f..6f31e68d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1137,6 +1137,7 @@ dependencies = [
"rfd",
"serde_json",
"winit",
+ "winresource",
]
[[package]]
@@ -4687,10 +4688,12 @@ version = "1.1.6+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "920602543f0911ab71da12c50d59701da54c196d1a2bf5cb4b75667f137a406a"
dependencies = [
+ "indexmap",
"serde_core",
"serde_spanned",
"toml_datetime",
"toml_parser",
+ "toml_writer",
"winnow",
]
@@ -4724,6 +4727,12 @@ dependencies = [
"winnow",
]
+[[package]]
+name = "toml_writer"
+version = "1.1.2+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
+
[[package]]
name = "tracing"
version = "0.1.44"
@@ -5763,6 +5772,16 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "winresource"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0986a8b1d586b7d3e4fe3d9ea39fb451ae22869dcea4aa109d287a374d866087"
+dependencies = [
+ "toml",
+ "version_check",
+]
+
[[package]]
name = "wit-bindgen"
version = "0.57.1"
diff --git a/README.md b/README.md
index d4ccd4ac..00ffa3b1 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,10 @@
+
+
+
+
EffectCraft
diff --git a/apps/effectcraft-web/web/favicon.svg b/apps/effectcraft-web/web/favicon.svg
index 0b4f3c8f..150ac8f9 100644
--- a/apps/effectcraft-web/web/favicon.svg
+++ b/apps/effectcraft-web/web/favicon.svg
@@ -1,8 +1 @@
-
+
\ No newline at end of file
diff --git a/apps/effectcraft-web/web/favicon.svg.attribution b/apps/effectcraft-web/web/favicon.svg.attribution
index e5f8b025..e723699a 100644
--- a/apps/effectcraft-web/web/favicon.svg.attribution
+++ b/apps/effectcraft-web/web/favicon.svg.attribution
@@ -1,9 +1,8 @@
asset: favicon.svg
title: EffectCraft web app icon (favicon)
-author: EffectCraft contributors
+author: EffectCraft contributors (drawing by the project owner in ArtCraft)
source: original work
license: MIT OR Apache-2.0
-added: 2026-10-01 by EffectCraft contributors (Claude agent, M15 web app)
-notes: Hand-written SVG of the code-drawn EffectCraft mark (`paint_logo` in
- crates/ui-egui/src/header.rs): a navy rounded square split into blue and violet halves
- with a white keyframe diamond, from basic shapes. Not derived from any other product's icon.
+license-file: assets/app-icon/LICENSE.txt
+added: 2026-10-02 by EffectCraft contributors (Claude agent, app icon)
+notes: Copy of assets/app-icon/effectcraft-small.svg (the lighter vector trace of the app icon). Replaces the earlier code-drawn split-square mark.
diff --git a/apps/effectcraft/Cargo.toml b/apps/effectcraft/Cargo.toml
index 5a2165aa..6b34763f 100644
--- a/apps/effectcraft/Cargo.toml
+++ b/apps/effectcraft/Cargo.toml
@@ -19,6 +19,10 @@ serde_json = { workspace = true }
rfd = { workspace = true }
cpal = { workspace = true }
+[build-dependencies]
+# Icon + VERSIONINFO for effectcraft.exe; build.rs only uses it when targeting Windows.
+winresource = "0.1"
+
[target.'cfg(target_os = "macos")'.dependencies]
# Same winit as eframe: macOS event-loop options (no app activation when driven by an agent).
winit = { version = "0.30", default-features = false }
diff --git a/apps/effectcraft/build.rs b/apps/effectcraft/build.rs
new file mode 100644
index 00000000..50eb5f34
--- /dev/null
+++ b/apps/effectcraft/build.rs
@@ -0,0 +1,27 @@
+//! Windows only: embed the app icon and version info (VERSIONINFO) into `effectcraft.exe`.
+//!
+//! On every other target this does nothing. A missing resource compiler is a warning, so a
+//! cross-compile from macOS or Linux still links, unless `EFFECTCRAFT_REQUIRE_WINRES=1` turns it
+//! into an error (for release builds).
+
+fn main() {
+ println!("cargo:rerun-if-changed=build.rs");
+ println!("cargo:rerun-if-changed=../../assets/app-icon/effectcraft.ico");
+ println!("cargo:rerun-if-env-changed=EFFECTCRAFT_REQUIRE_WINRES");
+ if std::env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("windows") {
+ return;
+ }
+ let mut res = winresource::WindowsResource::new();
+ res.set_icon("../../assets/app-icon/effectcraft.ico")
+ .set("ProductName", "EffectCraft")
+ .set("FileDescription", "EffectCraft motion graphics and visual effects")
+ .set("LegalCopyright", "Copyright (c) 2026 The EffectCraft contributors. MIT OR Apache-2.0.")
+ .set("OriginalFilename", "effectcraft.exe")
+ .set("InternalName", "effectcraft");
+ if let Err(e) = res.compile() {
+ if std::env::var_os("EFFECTCRAFT_REQUIRE_WINRES").is_some() {
+ panic!("embedding Windows resources failed: {e}");
+ }
+ println!("cargo:warning=effectcraft.exe built without icon/version resources: {e}");
+ }
+}
diff --git a/apps/effectcraft/src/main.rs b/apps/effectcraft/src/main.rs
index 70cbd933..34b40a84 100644
--- a/apps/effectcraft/src/main.rs
+++ b/apps/effectcraft/src/main.rs
@@ -11,6 +11,16 @@ mod control_server;
use effectcraft_ui_egui::EffectcraftApp;
use serde_json::json;
+/// The app icon for the window, taskbar and Dock (assets/app-icon, regenerated by
+/// `packaging/icons.sh`). macOS gets the padded variant so it sits on Apple's icon grid in the Dock.
+fn app_icon() -> egui::IconData {
+ #[cfg(target_os = "macos")]
+ let png: &[u8] = include_bytes!("../../../assets/app-icon/effectcraft-macos-512.png");
+ #[cfg(not(target_os = "macos"))]
+ let png: &[u8] = include_bytes!("../../../assets/app-icon/hicolor/256x256/apps/ai.storyteller.effectcraft.png");
+ eframe::icon_data::from_png_bytes(png).unwrap_or_default()
+}
+
fn main() -> eframe::Result {
let mut control_port: Option = std::env::var("EFFECTCRAFT_CONTROL_PORT").ok().and_then(|p| p.parse().ok());
let mut files = Vec::new();
@@ -37,7 +47,10 @@ fn main() -> eframe::Result {
.with_min_inner_size([960.0, 600.0])
.with_drag_and_drop(true)
// Started for an agent (`--control`): open without taking the user's keyboard focus.
- .with_active(control_port.is_none()),
+ .with_active(control_port.is_none())
+ .with_icon(app_icon())
+ // Wayland matches the window to `ai.storyteller.effectcraft.desktop` by this ID.
+ .with_app_id("ai.storyteller.effectcraft"),
event_loop_builder: agent_event_loop(control_port.is_some()),
..Default::default()
};
diff --git a/assets/app-icon/LICENSE.txt b/assets/app-icon/LICENSE.txt
new file mode 100644
index 00000000..5fdd611e
--- /dev/null
+++ b/assets/app-icon/LICENSE.txt
@@ -0,0 +1,13 @@
+EffectCraft app icon (effectcraft.svg, effectcraft-small.svg and every file derived from them in
+this directory: effectcraft-1024.png, effectcraft-macos-512.png, effectcraft.icns, effectcraft.ico,
+hicolor/), and the web favicon apps/effectcraft-web/web/favicon.svg.
+
+Original artwork made for this project by the project owner in ArtCraft, then vectorised (no
+third-party material). Licensed like the rest of EffectCraft, under either of
+
+ * Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
+ * MIT license (https://opensource.org/licenses/MIT)
+
+at your option.
+
+Regenerate the derived files with packaging/icons.sh.
diff --git a/assets/app-icon/README.md b/assets/app-icon/README.md
new file mode 100644
index 00000000..814ad601
--- /dev/null
+++ b/assets/app-icon/README.md
@@ -0,0 +1,42 @@
+# EffectCraft app icon
+
+**Creature:** a unicorn, as an engraved head-and-shoulders bust looking out of the tile.
+
+**Style:** an ink engraving portrait in the Crafting Apps' owl-template framing (the FilmCraft owl):
+full-bleed colour field, tight head-and-shoulders crop, the body running off the bottom and right edges.
+
+**Tile:** 512 × 512 viewBox, rounded square `rx=112`, clipped; no border or roundel.
+
+**Palette** (exactly three colours):
+
+| Colour | Hex | Used for |
+|---|---|---|
+| App colour (EffectCraft pink) | `#e0368f` | the full-bleed field |
+| Paper | `#efe9dc` | the figure |
+| Ink | `#0b0b0c` | line work and contour |
+
+**Provenance:** the project owner drew it in ArtCraft (a 2880 px engraving-style image, kept in
+craftrules at `assets/app-icons/effectcraft/source.png`). It was vectorised with craftrules
+`assets/logo-options/_tools/vectorize_tile.py`, which keys out the field and traces the ink with potrace.
+Licence: see [LICENSE.txt](LICENSE.txt) (MIT OR Apache-2.0, like the repo).
+
+## Files
+
+| File | What |
+|---|---|
+| `effectcraft.svg` | master vector, traced at 2048 px (canonical) |
+| `effectcraft-small.svg` | lighter vector (~350 KB), traced at 1024 px; also the web favicon |
+| `effectcraft-1024.png` | 1024 px render |
+| `effectcraft-macos-512.png` | runtime Dock icon on macOS, on Apple's 824/1024 grid |
+| `effectcraft.icns` | macOS icon (Apple grid) |
+| `effectcraft.ico` | Windows icon, 16–256 px; embedded in `effectcraft.exe` by `apps/effectcraft/build.rs` |
+| `hicolor/x/apps/ai.storyteller.effectcraft.png` | Linux icon theme, 16–512 px; the 256 px one is the runtime icon on Windows and Linux |
+| `hicolor/scalable/apps/ai.storyteller.effectcraft.svg` | Linux scalable icon |
+
+App ID: `ai.storyteller.effectcraft` (Wayland app ID, `packaging/linux/ai.storyteller.effectcraft.desktop`).
+
+## Regenerate
+
+Copy a new `icon-master.svg` / `icon.svg` from craftrules `assets/app-icons/effectcraft/` over
+`effectcraft.svg` / `effectcraft-small.svg`, then run `packaging/icons.sh` (needs `resvg`; `iconutil`
+on macOS for the `.icns`). Copy `effectcraft-small.svg` to `apps/effectcraft-web/web/favicon.svg` too.
diff --git a/assets/app-icon/effectcraft-1024.png b/assets/app-icon/effectcraft-1024.png
new file mode 100644
index 00000000..8635334a
Binary files /dev/null and b/assets/app-icon/effectcraft-1024.png differ
diff --git a/assets/app-icon/effectcraft-1024.png.attribution b/assets/app-icon/effectcraft-1024.png.attribution
new file mode 100644
index 00000000..1793aa51
--- /dev/null
+++ b/assets/app-icon/effectcraft-1024.png.attribution
@@ -0,0 +1,8 @@
+asset: effectcraft-1024.png
+title: EffectCraft app icon, 1024 px
+author: EffectCraft contributors (drawing by the project owner in ArtCraft)
+source: original work
+license: MIT OR Apache-2.0
+license-file: assets/app-icon/LICENSE.txt
+added: 2026-10-02 by EffectCraft contributors (Claude agent, app icon)
+notes: Rendered from effectcraft.svg with resvg by packaging/icons.sh.
diff --git a/assets/app-icon/effectcraft-macos-512.png b/assets/app-icon/effectcraft-macos-512.png
new file mode 100644
index 00000000..8f34aeb6
Binary files /dev/null and b/assets/app-icon/effectcraft-macos-512.png differ
diff --git a/assets/app-icon/effectcraft-macos-512.png.attribution b/assets/app-icon/effectcraft-macos-512.png.attribution
new file mode 100644
index 00000000..7bfafd11
--- /dev/null
+++ b/assets/app-icon/effectcraft-macos-512.png.attribution
@@ -0,0 +1,8 @@
+asset: effectcraft-macos-512.png
+title: EffectCraft runtime Dock icon (macOS)
+author: EffectCraft contributors (drawing by the project owner in ArtCraft)
+source: original work
+license: MIT OR Apache-2.0
+license-file: assets/app-icon/LICENSE.txt
+added: 2026-10-02 by EffectCraft contributors (Claude agent, app icon)
+notes: Rendered from effectcraft.svg on Apple's 824/1024 icon grid with resvg by packaging/icons.sh; embedded by apps/effectcraft/src/main.rs.
diff --git a/assets/app-icon/effectcraft-small.svg b/assets/app-icon/effectcraft-small.svg
new file mode 100644
index 00000000..150ac8f9
--- /dev/null
+++ b/assets/app-icon/effectcraft-small.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/app-icon/effectcraft-small.svg.attribution b/assets/app-icon/effectcraft-small.svg.attribution
new file mode 100644
index 00000000..6dbaab16
--- /dev/null
+++ b/assets/app-icon/effectcraft-small.svg.attribution
@@ -0,0 +1,8 @@
+asset: effectcraft-small.svg
+title: EffectCraft app icon, light vector
+author: EffectCraft contributors (drawing by the project owner in ArtCraft)
+source: original work
+license: MIT OR Apache-2.0
+license-file: assets/app-icon/LICENSE.txt
+added: 2026-10-02 by EffectCraft contributors (Claude agent, app icon)
+notes: The same drawing vectorised at 1024 px with craftrules vectorize_tile.py, about 350 KB, for places where size matters.
diff --git a/assets/app-icon/effectcraft.icns b/assets/app-icon/effectcraft.icns
new file mode 100644
index 00000000..919d6876
Binary files /dev/null and b/assets/app-icon/effectcraft.icns differ
diff --git a/assets/app-icon/effectcraft.icns.attribution b/assets/app-icon/effectcraft.icns.attribution
new file mode 100644
index 00000000..d338c26f
--- /dev/null
+++ b/assets/app-icon/effectcraft.icns.attribution
@@ -0,0 +1,8 @@
+asset: effectcraft.icns
+title: EffectCraft macOS app icon
+author: EffectCraft contributors (drawing by the project owner in ArtCraft)
+source: original work
+license: MIT OR Apache-2.0
+license-file: assets/app-icon/LICENSE.txt
+added: 2026-10-02 by EffectCraft contributors (Claude agent, app icon)
+notes: Rendered from effectcraft.svg on Apple's 824/1024 icon grid with resvg and iconutil by packaging/icons.sh.
diff --git a/assets/app-icon/effectcraft.ico b/assets/app-icon/effectcraft.ico
new file mode 100644
index 00000000..35cd557c
Binary files /dev/null and b/assets/app-icon/effectcraft.ico differ
diff --git a/assets/app-icon/effectcraft.ico.attribution b/assets/app-icon/effectcraft.ico.attribution
new file mode 100644
index 00000000..25a7faa1
--- /dev/null
+++ b/assets/app-icon/effectcraft.ico.attribution
@@ -0,0 +1,8 @@
+asset: effectcraft.ico
+title: EffectCraft Windows app icon
+author: EffectCraft contributors (drawing by the project owner in ArtCraft)
+source: original work
+license: MIT OR Apache-2.0
+license-file: assets/app-icon/LICENSE.txt
+added: 2026-10-02 by EffectCraft contributors (Claude agent, app icon)
+notes: Rendered from effectcraft.svg (16-256 px) with resvg and packed by cargo xtask ico (packaging/icons.sh).
diff --git a/assets/app-icon/effectcraft.svg b/assets/app-icon/effectcraft.svg
new file mode 100644
index 00000000..3cf53a2f
--- /dev/null
+++ b/assets/app-icon/effectcraft.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/app-icon/effectcraft.svg.attribution b/assets/app-icon/effectcraft.svg.attribution
new file mode 100644
index 00000000..21960623
--- /dev/null
+++ b/assets/app-icon/effectcraft.svg.attribution
@@ -0,0 +1,8 @@
+asset: effectcraft.svg
+title: EffectCraft app icon, master vector
+author: EffectCraft contributors (drawing by the project owner in ArtCraft)
+source: original work
+license: MIT OR Apache-2.0
+license-file: assets/app-icon/LICENSE.txt
+added: 2026-10-02 by EffectCraft contributors (Claude agent, app icon)
+notes: The owner's ArtCraft drawing of an engraved unicorn bust, vectorised at 2048 px with craftrules assets/logo-options/_tools/vectorize_tile.py (potrace; three colours). Canonical artwork; the other icon files derive from it.
diff --git a/assets/app-icon/hicolor/128x128/apps/ai.storyteller.effectcraft.png b/assets/app-icon/hicolor/128x128/apps/ai.storyteller.effectcraft.png
new file mode 100644
index 00000000..e41b16be
Binary files /dev/null and b/assets/app-icon/hicolor/128x128/apps/ai.storyteller.effectcraft.png differ
diff --git a/assets/app-icon/hicolor/128x128/apps/ai.storyteller.effectcraft.png.attribution b/assets/app-icon/hicolor/128x128/apps/ai.storyteller.effectcraft.png.attribution
new file mode 100644
index 00000000..36847426
--- /dev/null
+++ b/assets/app-icon/hicolor/128x128/apps/ai.storyteller.effectcraft.png.attribution
@@ -0,0 +1,8 @@
+asset: ai.storyteller.effectcraft.png
+title: EffectCraft app icon, Linux hicolor 128x128
+author: EffectCraft contributors (drawing by the project owner in ArtCraft)
+source: original work
+license: MIT OR Apache-2.0
+license-file: assets/app-icon/LICENSE.txt
+added: 2026-10-02 by EffectCraft contributors (Claude agent, app icon)
+notes: Rendered from effectcraft.svg with resvg by packaging/icons.sh.
diff --git a/assets/app-icon/hicolor/16x16/apps/ai.storyteller.effectcraft.png b/assets/app-icon/hicolor/16x16/apps/ai.storyteller.effectcraft.png
new file mode 100644
index 00000000..639ebefe
Binary files /dev/null and b/assets/app-icon/hicolor/16x16/apps/ai.storyteller.effectcraft.png differ
diff --git a/assets/app-icon/hicolor/16x16/apps/ai.storyteller.effectcraft.png.attribution b/assets/app-icon/hicolor/16x16/apps/ai.storyteller.effectcraft.png.attribution
new file mode 100644
index 00000000..943f87c1
--- /dev/null
+++ b/assets/app-icon/hicolor/16x16/apps/ai.storyteller.effectcraft.png.attribution
@@ -0,0 +1,8 @@
+asset: ai.storyteller.effectcraft.png
+title: EffectCraft app icon, Linux hicolor 16x16
+author: EffectCraft contributors (drawing by the project owner in ArtCraft)
+source: original work
+license: MIT OR Apache-2.0
+license-file: assets/app-icon/LICENSE.txt
+added: 2026-10-02 by EffectCraft contributors (Claude agent, app icon)
+notes: Rendered from effectcraft.svg with resvg by packaging/icons.sh.
diff --git a/assets/app-icon/hicolor/24x24/apps/ai.storyteller.effectcraft.png b/assets/app-icon/hicolor/24x24/apps/ai.storyteller.effectcraft.png
new file mode 100644
index 00000000..74d211bf
Binary files /dev/null and b/assets/app-icon/hicolor/24x24/apps/ai.storyteller.effectcraft.png differ
diff --git a/assets/app-icon/hicolor/24x24/apps/ai.storyteller.effectcraft.png.attribution b/assets/app-icon/hicolor/24x24/apps/ai.storyteller.effectcraft.png.attribution
new file mode 100644
index 00000000..10347ea9
--- /dev/null
+++ b/assets/app-icon/hicolor/24x24/apps/ai.storyteller.effectcraft.png.attribution
@@ -0,0 +1,8 @@
+asset: ai.storyteller.effectcraft.png
+title: EffectCraft app icon, Linux hicolor 24x24
+author: EffectCraft contributors (drawing by the project owner in ArtCraft)
+source: original work
+license: MIT OR Apache-2.0
+license-file: assets/app-icon/LICENSE.txt
+added: 2026-10-02 by EffectCraft contributors (Claude agent, app icon)
+notes: Rendered from effectcraft.svg with resvg by packaging/icons.sh.
diff --git a/assets/app-icon/hicolor/256x256/apps/ai.storyteller.effectcraft.png b/assets/app-icon/hicolor/256x256/apps/ai.storyteller.effectcraft.png
new file mode 100644
index 00000000..291feff6
Binary files /dev/null and b/assets/app-icon/hicolor/256x256/apps/ai.storyteller.effectcraft.png differ
diff --git a/assets/app-icon/hicolor/256x256/apps/ai.storyteller.effectcraft.png.attribution b/assets/app-icon/hicolor/256x256/apps/ai.storyteller.effectcraft.png.attribution
new file mode 100644
index 00000000..86dd6d76
--- /dev/null
+++ b/assets/app-icon/hicolor/256x256/apps/ai.storyteller.effectcraft.png.attribution
@@ -0,0 +1,8 @@
+asset: ai.storyteller.effectcraft.png
+title: EffectCraft app icon, Linux hicolor 256x256
+author: EffectCraft contributors (drawing by the project owner in ArtCraft)
+source: original work
+license: MIT OR Apache-2.0
+license-file: assets/app-icon/LICENSE.txt
+added: 2026-10-02 by EffectCraft contributors (Claude agent, app icon)
+notes: Rendered from effectcraft.svg with resvg by packaging/icons.sh. Also the runtime window/taskbar icon on Windows and Linux.
diff --git a/assets/app-icon/hicolor/32x32/apps/ai.storyteller.effectcraft.png b/assets/app-icon/hicolor/32x32/apps/ai.storyteller.effectcraft.png
new file mode 100644
index 00000000..e477dc25
Binary files /dev/null and b/assets/app-icon/hicolor/32x32/apps/ai.storyteller.effectcraft.png differ
diff --git a/assets/app-icon/hicolor/32x32/apps/ai.storyteller.effectcraft.png.attribution b/assets/app-icon/hicolor/32x32/apps/ai.storyteller.effectcraft.png.attribution
new file mode 100644
index 00000000..06f910e8
--- /dev/null
+++ b/assets/app-icon/hicolor/32x32/apps/ai.storyteller.effectcraft.png.attribution
@@ -0,0 +1,8 @@
+asset: ai.storyteller.effectcraft.png
+title: EffectCraft app icon, Linux hicolor 32x32
+author: EffectCraft contributors (drawing by the project owner in ArtCraft)
+source: original work
+license: MIT OR Apache-2.0
+license-file: assets/app-icon/LICENSE.txt
+added: 2026-10-02 by EffectCraft contributors (Claude agent, app icon)
+notes: Rendered from effectcraft.svg with resvg by packaging/icons.sh.
diff --git a/assets/app-icon/hicolor/48x48/apps/ai.storyteller.effectcraft.png b/assets/app-icon/hicolor/48x48/apps/ai.storyteller.effectcraft.png
new file mode 100644
index 00000000..be56e1aa
Binary files /dev/null and b/assets/app-icon/hicolor/48x48/apps/ai.storyteller.effectcraft.png differ
diff --git a/assets/app-icon/hicolor/48x48/apps/ai.storyteller.effectcraft.png.attribution b/assets/app-icon/hicolor/48x48/apps/ai.storyteller.effectcraft.png.attribution
new file mode 100644
index 00000000..46fbd4d4
--- /dev/null
+++ b/assets/app-icon/hicolor/48x48/apps/ai.storyteller.effectcraft.png.attribution
@@ -0,0 +1,8 @@
+asset: ai.storyteller.effectcraft.png
+title: EffectCraft app icon, Linux hicolor 48x48
+author: EffectCraft contributors (drawing by the project owner in ArtCraft)
+source: original work
+license: MIT OR Apache-2.0
+license-file: assets/app-icon/LICENSE.txt
+added: 2026-10-02 by EffectCraft contributors (Claude agent, app icon)
+notes: Rendered from effectcraft.svg with resvg by packaging/icons.sh.
diff --git a/assets/app-icon/hicolor/512x512/apps/ai.storyteller.effectcraft.png b/assets/app-icon/hicolor/512x512/apps/ai.storyteller.effectcraft.png
new file mode 100644
index 00000000..6c233775
Binary files /dev/null and b/assets/app-icon/hicolor/512x512/apps/ai.storyteller.effectcraft.png differ
diff --git a/assets/app-icon/hicolor/512x512/apps/ai.storyteller.effectcraft.png.attribution b/assets/app-icon/hicolor/512x512/apps/ai.storyteller.effectcraft.png.attribution
new file mode 100644
index 00000000..a08d1411
--- /dev/null
+++ b/assets/app-icon/hicolor/512x512/apps/ai.storyteller.effectcraft.png.attribution
@@ -0,0 +1,8 @@
+asset: ai.storyteller.effectcraft.png
+title: EffectCraft app icon, Linux hicolor 512x512
+author: EffectCraft contributors (drawing by the project owner in ArtCraft)
+source: original work
+license: MIT OR Apache-2.0
+license-file: assets/app-icon/LICENSE.txt
+added: 2026-10-02 by EffectCraft contributors (Claude agent, app icon)
+notes: Rendered from effectcraft.svg with resvg by packaging/icons.sh.
diff --git a/assets/app-icon/hicolor/64x64/apps/ai.storyteller.effectcraft.png b/assets/app-icon/hicolor/64x64/apps/ai.storyteller.effectcraft.png
new file mode 100644
index 00000000..2b3b2e97
Binary files /dev/null and b/assets/app-icon/hicolor/64x64/apps/ai.storyteller.effectcraft.png differ
diff --git a/assets/app-icon/hicolor/64x64/apps/ai.storyteller.effectcraft.png.attribution b/assets/app-icon/hicolor/64x64/apps/ai.storyteller.effectcraft.png.attribution
new file mode 100644
index 00000000..fcfe963c
--- /dev/null
+++ b/assets/app-icon/hicolor/64x64/apps/ai.storyteller.effectcraft.png.attribution
@@ -0,0 +1,8 @@
+asset: ai.storyteller.effectcraft.png
+title: EffectCraft app icon, Linux hicolor 64x64
+author: EffectCraft contributors (drawing by the project owner in ArtCraft)
+source: original work
+license: MIT OR Apache-2.0
+license-file: assets/app-icon/LICENSE.txt
+added: 2026-10-02 by EffectCraft contributors (Claude agent, app icon)
+notes: Rendered from effectcraft.svg with resvg by packaging/icons.sh.
diff --git a/assets/app-icon/hicolor/scalable/apps/ai.storyteller.effectcraft.svg b/assets/app-icon/hicolor/scalable/apps/ai.storyteller.effectcraft.svg
new file mode 100644
index 00000000..150ac8f9
--- /dev/null
+++ b/assets/app-icon/hicolor/scalable/apps/ai.storyteller.effectcraft.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/app-icon/hicolor/scalable/apps/ai.storyteller.effectcraft.svg.attribution b/assets/app-icon/hicolor/scalable/apps/ai.storyteller.effectcraft.svg.attribution
new file mode 100644
index 00000000..562c3506
--- /dev/null
+++ b/assets/app-icon/hicolor/scalable/apps/ai.storyteller.effectcraft.svg.attribution
@@ -0,0 +1,8 @@
+asset: ai.storyteller.effectcraft.svg
+title: EffectCraft app icon, Linux hicolor scalable
+author: EffectCraft contributors (drawing by the project owner in ArtCraft)
+source: original work
+license: MIT OR Apache-2.0
+license-file: assets/app-icon/LICENSE.txt
+added: 2026-10-02 by EffectCraft contributors (Claude agent, app icon)
+notes: Copy of effectcraft-small.svg, written by packaging/icons.sh.
diff --git a/packaging/icons.sh b/packaging/icons.sh
new file mode 100755
index 00000000..afeb13f8
--- /dev/null
+++ b/packaging/icons.sh
@@ -0,0 +1,62 @@
+#!/usr/bin/env bash
+# Regenerate every app icon file from assets/app-icon/effectcraft.svg (the canonical vector).
+#
+# Needs: resvg (brew install resvg / cargo install resvg). On macOS, iconutil also writes the
+# .icns. The outputs are committed, so builds and packaging never need these tools.
+#
+# packaging/icons.sh
+#
+# effectcraft.svg and effectcraft-small.svg (the lighter 1024 px trace) come from craftrules
+# (assets/app-icons/effectcraft/, which also keeps the source drawing) and are copied in by hand.
+set -euo pipefail
+ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+DIR="$ROOT/assets/app-icon"
+SVG="$DIR/effectcraft.svg"
+ID="ai.storyteller.effectcraft"
+TMP="$(mktemp -d)"
+trap 'rm -rf "$TMP"' EXIT
+
+command -v resvg >/dev/null || { echo "error: resvg not found (brew install resvg)" >&2; exit 1; }
+
+render() { resvg -w "$2" -h "$2" "$1" "$3" |[[:space:]]*$||' "$SVG" >"$MAC"
+grep -q 'viewBox="0 0 1024 1024"' "$MAC" || { echo "error: unexpected