diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md
index 2221051..111dfe7 100644
--- a/docs/ROADMAP.md
+++ b/docs/ROADMAP.md
@@ -80,6 +80,110 @@
---
+## ⚪ Backlog — Média (images, audio, vidéo)
+
+### 108. Support complet des images — arborescence, visionneuse, indexation
+
+- **Effort :** 3-4 jours | **Impact :** 🟡 | **Zone :** backend (indexer, main) + frontend (viewer, utils)
+- **Statut :** ⚪ Prévu
+- **Description :** parité fonctionnelle des images avec les autres fichiers du vault : apparition dans l'arborescence, ouverture directe dans une visionneuse dédiée (zoom molette, pan, navigation dossier), indexation (nom + métadonnées), miniatures. Actuellement seul l'affichage *inline* dans un document markdown (`![[image.png]]`) fonctionne ; l'image isolée est invisible dans l'arborescence et son affichage standalone est cassé.
+- **Architecture actuelle (vérifiée sur `main` @ v2.16.6) :**
+ - `SUPPORTED_EXTENSIONS` (`backend/indexer.py:61`) : aucune extension image → images filtrées de l'arborescence et de l'index.
+ - `api_file_view()` (`backend/main.py:~2411`) : branche `IMAGE_EXTENSIONS` (`.png .jpg .jpeg .gif .svg .webp .bmp .ico`) qui retourne `is_image: true`, MAIS le `` généré pointe vers `/api/file/{vault}/raw?path=…` — un endpoint qui retourne du **JSON** (`FileRawResponse`, `main.py:1569`), pas des octets d'image. Affichage standalone cassé. Le frontend (`frontend/js/viewer.js:600`, branche `data.is_image`) reproduit la même URL cassée pour le bouton « Plein écran ».
+ - `/api/image/{vault}` (`backend/main.py:3160`) : endpoint existant et fonctionnel qui sert les octets avec le bon MIME type. C'est LUI que doivent utiliser le `` backend et le frontend. L'auth navigateur fonctionne (middleware JWT avec fallback cookie `access_token`).
+ - `image_processor.py` + `attachment_indexer.py` : réécriture des 4 syntaxes Obsidian (`![[img]]`, ``…) vers `/api/image` avec résolution multi-stratégie → **l'inline markdown fonctionne déjà**, aucun changement requis.
+ - CSP : `img-src 'self' data: blob:` — compatible, rien à assouplir.
+- **Sous-tâches :**
+
+##### A. Backend — arborescence & indexation (1 j)
+ - [ ] **A1.** Créer `backend/media_types.py` : constantes partagées `IMAGE_EXTENSIONS`, `AUDIO_EXTENSIONS`, `VIDEO_EXTENSIONS` (réutilisées par #109) ; remplacer le set local de `api_file_view()`.
+ - [ ] **A2.** Intégrer les extensions images à `SUPPORTED_EXTENSIONS` (`indexer.py:61`) **avec branche binaire** : `index_document()` ne doit JAMAIS `read_text()`/`read_bytes()` une image — indexer nom/taille/mtime uniquement, `content: ""` pour que le TF-IDF reste propre.
+ - [ ] **A3.** Vérifier le reindex watchdog sur création/suppression d'image (même filtre d'extensions, couvert par A2).
+ - [ ] **A4.** Recherche : le nom de fichier est déjà indexé (`chatScreenshot.png` trouvable par « screenshot ») ; ajouter le filtre `ext:png`/`ext:jpg` (mécanisme des filtres `ext:` existants) ; comptabiliser les images à part dans les compteurs du dashboard (#11).
+
+##### B. Backend — correction affichage standalone (0,5 j)
+ - [ ] **B1.** Fix `` cassé : `api_file_view()` → `src="/api/image/{vault}?path=…"` (URL encodée) ; idem `viewer.js:600` (bouton Plein écran).
+ - [ ] **B2.** Métadonnées : dimensions via `` frontend (`naturalWidth/naturalHeight` — gratuit, recommandé ; Pillow seulement si C1 est retenu).
+ - [ ] **B3.** Sécurité SVG : sur `/api/image`, ajouter `Content-Security-Policy: sandbox` (ou `Content-Disposition: attachment`) quand `ext == .svg` — un SVG ouvert directement dans un onglet exécute son JS (XSS same-origin) ; dans une balise `` il est inoffensif.
+
+##### C. Backend — miniatures (1 j, phase 2 tolérable)
+ - [ ] **C1.** Dépendance `pillow>=10.0` (wheels précompilés, rien de système dans `python:3.11-slim`).
+ - [ ] **C2.** `GET /api/media/{vault}/thumb` : miniature 256 px WebP, cache disque `/data/.obsigate-cache/thumbs/{sha1(path+mtime)}.webp` (invalidation naturelle par mtime). SVG : servir l'original. GIF animé : première frame.
+ - [ ] **C3.** Génération dans un thread pool (`run_in_executor`), fallback original si timeout 2 s.
+
+##### D. Frontend — visionneuse (1 j)
+ - [ ] **D1.** `EXT_ICONS` (`frontend/js/utils.js`) : extensions images → icône Lucide `image`.
+ - [ ] **D2.** Visionneuse digne de ce nom dans `viewer.js` : image centrée `object-fit:contain`, **zoom molette (0,1×–8×), pan au drag, double-clic reset**, boutons +/−/reset, badge de zoom.
+ - [ ] **D3.** Navigation ←/→ entre images du même dossier (données `list_directory` déjà disponibles) ; bouton « Ouvrir original » (nouvel onglet, `/api/image`).
+ - [ ] **D4.** Barre d'outils : Télécharger (déjà fonctionnel), lightbox plein viewport (fond `rgba(0,0,0,.9)`), panneau métadonnées repliable (dimensions, taille, type, date).
+ - [ ] **D5.** (Optionnel 🟢) Vue galerie vignettes pour les dossiers d'images via `/api/media/thumb`, `loading="lazy"`.
+ - [ ] **D6.** Split View (#75) : rendu dans `getContentArea()` du panneau actif ; vérifier le cache d'onglets.
+
+##### E. Tests (0,5 j)
+ - [ ] **E1.** `test_image_api.py` : `/api/image` octets + MIME ; SVG avec header sandbox ; `api_file_view` → `is_image: true` avec URL `/api/image` dans le html.
+ - [ ] **E2.** `test_image_indexing.py` : image visible dans `list_directory`, indexée avec contenu vide, watcher OK, rien de binaire dans le TF-IDF.
+ - [ ] **E3.** Playwright (#58) : clic sur une image de l'arborescence → visionneuse rendue ; zoom molette applique la transform.
+
+- **Points d'attention / risques**
+ - **Ne jamais lire les octets dans l'indexeur** — dérive n°1 d'un A2 bâclé (UnicodeDecodeError de masse dans les logs, TF-IDF pollué).
+ - **`_attachments/`** : les vaults Obsidian réels contiennent des milliers d'images → vérifier la fluidité de l'arborescence et le coût du scan d'index.
+ - **HEIC (iPhone)** : non décodable par les navigateurs → hors scope v1, documenter la limitation ; `pillow-heif` en v2 si demande.
+ - **Parité Obsidian Desktop** : la visionneuse D2 doit atteindre le même confort zoom/pan, sinon l'utilisateur repart sur le desktop.
+
+---
+
+### 109. Support audio & vidéo — lecteurs intégrés HTML5
+
+- **Effort :** 2-3 jours | **Impact :** 🟡 | **Zone :** backend (media streaming) + frontend (viewer)
+- **Statut :** ⚪ Prévu — dépend du socle `media_types.py` de #108-A1
+- **Description :** prise en charge des fichiers audio (`.mp3 .m4a .aac .wav .ogg .oga .opus .flac`) et vidéo (`.mp4 .webm .mov .m4v`) avec la même parité que les autres fichiers : apparition dans l'arborescence, indexation du nom, lecture directe dans le viewer via les balises HTML5 `