diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md
index 3f045ca..aaf1175 100644
--- a/.planning/ROADMAP.md
+++ b/.planning/ROADMAP.md
@@ -161,10 +161,10 @@ Plans:
5. La page article contient un JSON-LD `BreadcrumbList` : Accueil → Blog → Titre de l'article
**Plans:** 4 plans
Plans:
-- [ ] 06-01-PLAN.md — Content schema Zod extension (draft/wordCount/minutes) + Nitro reading-time hook + draft:true sur test articles
-- [ ] 06-02-PLAN.md — i18n keys blog.*/nav.blog/a11y.blog* + lien Blog dans AppHeader + BlogCard.vue unifié (default + compact)
-- [ ] 06-03-PLAN.md — Page listing app/pages/blog/index.vue (hero + grid + empty state, SSR bilingue)
-- [ ] 06-04-PLAN.md — BlogToc.vue + BlogPrevNext.vue + enrichissement app/pages/blog/[slug].vue (breadcrumb + header + TOC + surround)
+- [ ] 07-01-PLAN.md — Install nuxt-schema-org + schema updated + definePerson/defineWebSite global + sitemap.sources
+- [ ] 07-02-PLAN.md — resolveOgImage helper + og-blog-default.jpg + [slug].vue useSeoMeta enrichi + defineArticle/defineBreadcrumb
+- [ ] 07-03-PLAN.md — index.vue useSeoMeta enrichi + defineWebPage(CollectionPage) + defineBreadcrumb
+- [ ] 07-04-PLAN.md — server/api/__sitemap__/urls.ts (bilingue, draft:false, alternates hreflang, lastmod=updated||date)
### Phase 8: Content & Cocon Semantique
**Goal**: Le blog est lance avec au moins 2 articles Hytale de qualite, et un visiteur qui arrive sur /hytale decouvre les articles recents — le cocon semantique entre blog et page hytale est etabli
diff --git a/.planning/phases/07-seo-blog/07-01-PLAN.md b/.planning/phases/07-seo-blog/07-01-PLAN.md
new file mode 100644
index 0000000..6531a23
--- /dev/null
+++ b/.planning/phases/07-seo-blog/07-01-PLAN.md
@@ -0,0 +1,213 @@
+---
+phase: 07-seo-blog
+plan: 01
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - package.json
+ - pnpm-lock.yaml
+ - nuxt.config.ts
+ - content.config.ts
+ - app/app.vue
+ - app/utils/seo-person.ts
+autonomous: true
+requirements: [SEO-11, SEO-12]
+must_haves:
+ truths:
+ - "nuxt-schema-org est installé et chargé comme module Nuxt"
+ - "Schema Zod blog_fr/blog_en accepte `updated` (ISO string) en plus de `image`"
+ - "Une identité Person Killian globale (definePerson) + defineWebSite est émise dans chaque page SSR"
+ - "nuxt.config.ts référence /api/__sitemap__/urls dans sitemap.sources"
+ artifacts:
+ - path: "app/utils/seo-person.ts"
+ provides: "KILLIAN_PERSON_ID const + killianPerson object (dérivé de siteConfig)"
+ contains: "export const KILLIAN_PERSON_ID"
+ - path: "content.config.ts"
+ provides: "blogSchema étendu avec updated.optional()"
+ contains: "updated: z.string().optional()"
+ - path: "nuxt.config.ts"
+ provides: "module nuxt-schema-org + sitemap.sources"
+ contains: "nuxt-schema-org"
+ - path: "app/app.vue"
+ provides: "useSchemaOrg global (definePerson + defineWebSite)"
+ contains: "useSchemaOrg"
+ key_links:
+ - from: "app/app.vue"
+ to: "app/utils/seo-person.ts"
+ via: "import killianPerson"
+ pattern: "killianPerson"
+ - from: "nuxt.config.ts"
+ to: "/api/__sitemap__/urls"
+ via: "sitemap.sources"
+ pattern: "sitemap.*sources.*__sitemap__/urls"
+---
+
+
+Fondation SEO Blog : installer `nuxt-schema-org`, étendre le schema Zod `blog_fr`/`blog_en` avec `updated`, déclarer l'identité Killian globale (Person + WebSite) dans `app.vue`, et brancher le sitemap dynamique sur un endpoint Nitro (déclaration uniquement — l'endpoint est créé plan 07-04).
+
+Purpose: Aucun des plans Wave 2 ne peut fonctionner sans (a) le module `nuxt-schema-org` présent dans `modules[]`, (b) le champ `updated` queryable, (c) l'identité Person disponible par `@id` global, (d) `sitemap.sources` wiré.
+Output: package installé, 1 fichier utilitaire créé, 3 fichiers config/racine modifiés.
+
+
+
+@$HOME/.claude/get-shit-done/workflows/execute-plan.md
+@$HOME/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/07-seo-blog/07-CONTEXT.md
+@.planning/phases/07-seo-blog/07-RESEARCH.md
+@.planning/phases/07-seo-blog/07-PATTERNS.md
+@nuxt.config.ts
+@content.config.ts
+@app/app.vue
+@app/data/site.ts
+
+
+Depuis app/data/site.ts :
+- `siteConfig.url` = 'https://killiandalcin.fr'
+- `siteConfig.social` = tableau avec entrées Gitea, LinkedIn, Discord, Email (reprendre `url` pour `sameAs`)
+
+Depuis content.config.ts (existant) :
+```ts
+const blogSchema = z.object({
+ title: z.string(),
+ description: z.string(),
+ date: z.string(),
+ tags: z.array(z.string()).optional(),
+ image: z.string().optional(), // DÉJÀ présent (D-14 #2 = no-op)
+ draft: z.boolean().optional().default(false),
+ wordCount: z.number().optional(),
+ minutes: z.number().optional(),
+})
+```
+
+Depuis app/app.vue (existant) : `useHead` + `useLocaleHead({ seo: true })` — NE PAS remplacer, APPEND.
+
+Auto-imports nuxt-schema-org (une fois module ajouté) : `useSchemaOrg`, `definePerson`, `defineWebSite`, `defineArticle`, `defineBreadcrumb`, `defineWebPage`.
+
+
+
+
+
+
+ Task 1: Installer nuxt-schema-org + étendre content.config.ts (schema updated)
+ package.json, pnpm-lock.yaml, content.config.ts
+
+ - package.json (vérifier absence de nuxt-schema-org)
+ - content.config.ts (schéma actuel, ligne 3-12)
+ - .planning/phases/07-seo-blog/07-RESEARCH.md §Standard Stack (version cible ^6.0.4)
+ - .planning/phases/07-seo-blog/07-RESEARCH.md Pitfall 8 (cache invalidation)
+ - .planning/phases/07-seo-blog/07-PATTERNS.md §content.config.ts (modify)
+
+
+ 1. Installer : `pnpm add -D nuxt-schema-org@^6.0.4` (D-01, D-04 — NE PAS installer `@nuxtjs/seo` umbrella).
+ 2. Dans `content.config.ts`, modifier `blogSchema` : ajouter exactement la ligne `updated: z.string().optional(),` entre `date: z.string(),` et `tags: z.array(z.string()).optional(),` (D-13, D-14). Ne PAS toucher aux autres champs (`image` déjà présent).
+ 3. Vider les caches pour forcer la re-ingestion : `rm -rf node_modules/.cache/content .nuxt` (Pitfall 8 RESEARCH).
+
+
+ grep -q '"nuxt-schema-org"' package.json && grep -q 'updated: z.string().optional()' content.config.ts && pnpm typecheck
+
+ nuxt-schema-org^6.0.4 dans devDependencies, `updated: z.string().optional()` présent dans blogSchema, caches vidés, typecheck exit 0.
+
+
+
+ Task 2: Enregistrer module + sitemap.sources dans nuxt.config.ts, créer app/utils/seo-person.ts, brancher useSchemaOrg global dans app/app.vue
+ nuxt.config.ts, app/utils/seo-person.ts, app/app.vue
+
+ - nuxt.config.ts (lignes 1-82 entier, surtout modules[] 5-13)
+ - app/app.vue (10 lignes entier)
+ - app/data/site.ts (lignes 5-43 — source url + social)
+ - .planning/phases/07-seo-blog/07-PATTERNS.md §seo-person.ts, §nuxt.config.ts, §app.vue
+ - .planning/phases/07-seo-blog/07-RESEARCH.md §Pattern 1 (Global Schema Identity)
+
+
+ 1. **nuxt.config.ts** :
+ - Ajouter `'nuxt-schema-org'` dans `modules[]` après `'@nuxtjs/sitemap'` (ligne ~12).
+ - Ajouter, au même niveau d'indentation que `site:` et `i18n:`, le bloc :
+ ```ts
+ sitemap: {
+ sources: ['/api/__sitemap__/urls'],
+ },
+ ```
+ - Ne PAS modifier `site`, `i18n`, `content`, `runtimeConfig`, `gtag`, `vite`.
+ 2. **Créer `app/utils/seo-person.ts`** avec le contenu exact (pattern `app/utils/countWords.ts` : JSDoc top + export nommé + const typé) :
+ ```ts
+ /**
+ * Global Person identity for schema.org (Killian Dal-Cin).
+ * Consumed by: app/app.vue (definePerson global) and app/pages/blog/[slug].vue (author/publisher @id ref).
+ * Derives URLs from siteConfig — single source of truth.
+ */
+ import { siteConfig } from '~/data/site'
+
+ export const KILLIAN_PERSON_ID = '#killian'
+
+ export const killianPerson = {
+ '@id': KILLIAN_PERSON_ID,
+ name: "Killian' Dal-Cin",
+ url: siteConfig.url,
+ jobTitle: siteConfig.jobTitle,
+ sameAs: siteConfig.social
+ .filter((s) => s.name !== 'Email')
+ .map((s) => s.url),
+ } as const
+ ```
+ 3. **app/app.vue** : APPEND (ne pas remplacer) après le bloc `useHead({...})` existant, AVANT la fermeture `` :
+ ```ts
+ import { killianPerson } from '~/utils/seo-person'
+
+ useSchemaOrg([
+ definePerson(killianPerson),
+ defineWebSite({
+ name: "Killian' Dal-Cin — Hytale Plugin Developer",
+ inLanguage: ['fr-FR', 'en-US'],
+ }),
+ ])
+ ```
+ Ne pas toucher au `` ni au `useLocaleHead`/`useHead` existants.
+
+
+ grep -q "'nuxt-schema-org'" nuxt.config.ts && grep -q "/api/__sitemap__/urls" nuxt.config.ts && grep -q "KILLIAN_PERSON_ID" app/utils/seo-person.ts && grep -q "definePerson(killianPerson)" app/app.vue && pnpm typecheck && pnpm dev --port 3000 & sleep 10 && curl -s http://localhost:3000/ | grep -q '"@type":"Person"' && kill %1
+
+ Module chargé sans erreur ; `curl /` contient un `