diff --git a/README.md b/README.md
index b2ab434..dc051d0 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,10 @@
-# Arboretum
+
+
+
-> A self-hosted web dashboard for your git worktrees and the Claude Code sessions running on them — from any device.
+
+ A self-hosted web dashboard for your git worktrees and the Claude Code sessions running on them — from any device.
+
**Status: MVP.** The worktree-first dashboard, session discovery & resume, multi-repo worktree lifecycle, live session states, the web terminal, and mobile supervision (installable PWA, Web Push when a session needs you, approve/deny without opening a terminal) are implemented and tested.
diff --git a/brand/README.md b/brand/README.md
new file mode 100644
index 0000000..b92265a
--- /dev/null
+++ b/brand/README.md
@@ -0,0 +1,42 @@
+# Brand assets
+
+Logo and icon assets for Arboretum. Source artwork is a neon circuit-tree (green
+branches, cyan session nodes, a `>_` prompt at the base) on a dark background.
+
+| File | Use |
+| --- | --- |
+| `arboretum-logo-source.png` | Master artwork (opaque dark background). Keep; everything else derives from it. |
+| `arboretum-logo.png` | Full logo, **transparent**. Best on dark surfaces (the wordmark is light). |
+| `arboretum-logo-on-dark.png` | Full logo on the app background `#09090b`. Safe on any theme — used in the README. |
+| `arboretum-mark.png` | Square, **transparent**, tree only (no wordmark). Ideal **Gitea repo avatar** — reads on both light and dark. |
+
+The transparent versions are extracted by luminance (alpha ∝ brightness), the clean
+way to lift glow-on-black artwork: the dark background becomes fully transparent, the
+bright strokes stay opaque, and the glow halos stay semi-transparent so the logo sits
+correctly on any dark surface.
+
+## Gitea
+
+Upload `arboretum-mark.png` as the repository avatar (Settings → uploads a square image;
+the tree-only mark stays legible at small sizes and works on Gitea's light and dark themes).
+Use `arboretum-logo.png` (transparent) on dark pages, or `arboretum-logo-on-dark.png`
+when the surrounding background might be light.
+
+## App / favicon assets
+
+The web-facing assets live in `packages/web/public/` and are wired into the SPA:
+
+- `icon.svg` — scalable favicon, redrawn to match the brand (vector, glow, `>_`).
+- `icon-192.png` / `icon-512.png` — maskable PWA icons (tree on `#09090b`, content in the safe zone).
+- `apple-touch-icon.png` — iOS home-screen icon (180×180).
+- `favicon.ico` — multi-size favicon (16/32/48), transparent.
+- `logo.png` — transparent full logo for in-app use.
+
+## Regenerate
+
+```bash
+python3 brand/build-assets.py # uses brand/arboretum-logo-source.png
+python3 brand/build-assets.py other.png # or pass another source
+```
+
+Requires Python with Pillow + numpy. Writes both `brand/` and `packages/web/public/`.
diff --git a/brand/arboretum-logo-on-dark.png b/brand/arboretum-logo-on-dark.png
new file mode 100644
index 0000000..141a5c7
Binary files /dev/null and b/brand/arboretum-logo-on-dark.png differ
diff --git a/brand/arboretum-logo-source.png b/brand/arboretum-logo-source.png
new file mode 100644
index 0000000..1596fa5
Binary files /dev/null and b/brand/arboretum-logo-source.png differ
diff --git a/brand/arboretum-logo.png b/brand/arboretum-logo.png
new file mode 100644
index 0000000..9971eca
Binary files /dev/null and b/brand/arboretum-logo.png differ
diff --git a/brand/arboretum-mark.png b/brand/arboretum-mark.png
new file mode 100644
index 0000000..442652c
Binary files /dev/null and b/brand/arboretum-mark.png differ
diff --git a/brand/build-assets.py b/brand/build-assets.py
new file mode 100644
index 0000000..7018f13
--- /dev/null
+++ b/brand/build-assets.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python3
+"""Génère le jeu complet d'assets de marque Arboretum depuis le logo source.
+
+Source : un PNG « néon sur fond sombre » (arbre-circuit + texte « Arboretum »).
+On extrait l'alpha par luminance (méthode propre pour ce type d'artwork glow-on-black) :
+chaque pixel reçoit une transparence proportionnelle à sa luminosité, ce qui rend le
+fond sombre totalement transparent, garde le cœur des traits opaque et conserve le halo
+en semi-transparent — donc lisible sur n'importe quel fond.
+
+Sorties :
+ brand/arboretum-logo.png logo complet transparent (haute déf) — Gitea / README
+ brand/arboretum-mark.png marque carrée transparente (arbre seul) — avatar Gitea
+ brand/arboretum-logo-on-dark.png logo complet sur fond #09090b (fallback fond clair)
+ packages/web/public/logo.png logo complet transparent, optimisé pour l'UI
+ packages/web/public/icon-192.png icône PWA maskable (arbre, fond #09090b)
+ packages/web/public/icon-512.png icône PWA maskable (arbre, fond #09090b)
+ packages/web/public/apple-touch-icon.png icône iOS 180 (arbre, fond #09090b)
+ packages/web/public/favicon.ico favicon transparent (arbre, 16/32/48)
+
+Usage : python3 brand/build-assets.py
+"""
+import sys
+import numpy as np
+from PIL import Image
+
+SRC = sys.argv[1] if len(sys.argv) > 1 else "brand/arboretum-logo-source.png"
+BG = (9, 9, 11) # #09090b — couleur de fond du dashboard (manifest background_color/theme_color)
+
+# Découpe verticale arbre / texte (mesurée sur la source)
+TREE_Y = (130, 930) # arbre + curseur >_
+FULL_Y = (130, 1060) # arbre + texte
+
+
+def extract_rgba(path):
+ """Charge la source et calcule l'alpha par luminance (floor + knee)."""
+ rgb = np.asarray(Image.open(path).convert("RGB"), dtype=np.float32)
+ maxc = rgb.max(axis=2)
+ # floor > canal max du fond (~14) pour annuler totalement le fond ; knee = seuil d'opacité.
+ floor, knee = 20.0, 185.0 # < floor : transparent ; >= knee : opaque
+ alpha = np.clip((maxc - floor) / (knee - floor), 0.0, 1.0) * 255.0
+ alpha[alpha < 8] = 0.0 # gate anti-bruit : un vrai fond transparent et une bbox serrée
+ out = np.dstack([rgb, alpha]).astype(np.uint8)
+ return Image.fromarray(out, "RGBA")
+
+
+def crop_band(rgba, y0, y1):
+ """Recadre une bande verticale puis serre sur le contenu non transparent."""
+ band = rgba.crop((0, y0, rgba.width, y1))
+ bbox = band.getbbox() # bbox sur l'alpha
+ return band.crop(bbox)
+
+
+def square(content, pad_frac, bg=None):
+ """Centre `content` dans un carré ; bg=None → transparent, sinon fond plein."""
+ side = round(max(content.size) / (1 - 2 * pad_frac))
+ fill = (0, 0, 0, 0) if bg is None else (*bg, 255)
+ canvas = Image.new("RGBA", (side, side), fill)
+ canvas.paste(content, ((side - content.width) // 2, (side - content.height) // 2), content)
+ return canvas
+
+
+def save(img, path, size=None):
+ if size:
+ img = img.resize((size, size), Image.LANCZOS)
+ img.save(path)
+ print(f" {path} {img.size[0]}x{img.size[1]}")
+
+
+def main():
+ rgba = extract_rgba(SRC)
+ full = crop_band(rgba, *FULL_Y) # logo complet transparent serré
+ tree = crop_band(rgba, *TREE_Y) # arbre seul transparent serré
+
+ print("brand/")
+ save(full, "brand/arboretum-logo.png")
+ save(square(tree, 0.08), "brand/arboretum-mark.png")
+ # logo complet sur fond sombre (pour surfaces claires où la transparence gêne)
+ on_dark = Image.new("RGBA", (full.width + 160, full.height + 160), (*BG, 255))
+ on_dark.paste(full, (80, 80), full)
+ save(on_dark, "brand/arboretum-logo-on-dark.png")
+
+ print("packages/web/public/")
+ # logo UI : largeur max 600 px, transparent
+ ui = full.copy()
+ ui.thumbnail((600, 600), Image.LANCZOS)
+ ui.save("packages/web/public/logo.png")
+ print(f" packages/web/public/logo.png {ui.size[0]}x{ui.size[1]}")
+ # icônes maskable : arbre dans la zone sûre (contenu ~80 %), fond #09090b
+ mask = square(tree, 0.12, bg=BG)
+ save(mask, "packages/web/public/icon-192.png", 192)
+ save(mask, "packages/web/public/icon-512.png", 512)
+ save(mask, "packages/web/public/apple-touch-icon.png", 180)
+ # favicon : arbre transparent, multi-tailles
+ fav = square(tree, 0.04)
+ fav.save("packages/web/public/favicon.ico", sizes=[(16, 16), (32, 32), (48, 48)])
+ print(" packages/web/public/favicon.ico 16/32/48")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/packages/web/index.html b/packages/web/index.html
index 7a5507b..e35a8a9 100644
--- a/packages/web/index.html
+++ b/packages/web/index.html
@@ -7,11 +7,12 @@
+
-
+
Arboretum
diff --git a/packages/web/public/apple-touch-icon.png b/packages/web/public/apple-touch-icon.png
new file mode 100644
index 0000000..c975719
Binary files /dev/null and b/packages/web/public/apple-touch-icon.png differ
diff --git a/packages/web/public/favicon.ico b/packages/web/public/favicon.ico
new file mode 100644
index 0000000..94b9ac9
Binary files /dev/null and b/packages/web/public/favicon.ico differ
diff --git a/packages/web/public/icon-192.png b/packages/web/public/icon-192.png
new file mode 100644
index 0000000..6ec9ef6
Binary files /dev/null and b/packages/web/public/icon-192.png differ
diff --git a/packages/web/public/icon-512.png b/packages/web/public/icon-512.png
new file mode 100644
index 0000000..98bbb47
Binary files /dev/null and b/packages/web/public/icon-512.png differ
diff --git a/packages/web/public/icon.svg b/packages/web/public/icon.svg
index fa1523c..12a05d7 100644
--- a/packages/web/public/icon.svg
+++ b/packages/web/public/icon.svg
@@ -1,18 +1,46 @@
diff --git a/packages/web/public/logo.png b/packages/web/public/logo.png
new file mode 100644
index 0000000..e824952
Binary files /dev/null and b/packages/web/public/logo.png differ
diff --git a/packages/web/public/manifest.webmanifest b/packages/web/public/manifest.webmanifest
index a4a1a3d..11721fe 100644
--- a/packages/web/public/manifest.webmanifest
+++ b/packages/web/public/manifest.webmanifest
@@ -8,6 +8,8 @@
"background_color": "#09090b",
"theme_color": "#09090b",
"icons": [
- { "src": "/icon.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "any maskable" }
+ { "src": "/icon.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "any" },
+ { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "maskable" },
+ { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
]
}
diff --git a/packages/web/src/views/LoginView.vue b/packages/web/src/views/LoginView.vue
index c9bf9df..c7543fb 100644
--- a/packages/web/src/views/LoginView.vue
+++ b/packages/web/src/views/LoginView.vue
@@ -5,7 +5,10 @@
@submit.prevent="onSubmit"
>
-
{{ t('common.appName') }}
+
+

+
{{ t('common.appName') }}
+
{{ t('login.title') }}