# 001 — HistoMétéo MVP — Spécification Technique v6

> **Itération** : v6 — intègre le feedback Reviewer (`feedback-to-architect-001-v5.md`) + évolution du design system (`HistoMeteo--Design-System-Evolution.md`).

---

## 0) Contract

- **Source of truth** : ce document (`001-histometeo-mvp.tech.v6.md`)
- **Functional integrity** : aucun critère d'acceptation de `001-histometeo-mvp.md` ne peut être modifié, ignoré ou réinterprété. Le design system évolue vers la charte définie dans `HistoMeteo--Design-System-Evolution.md`.
- **Scope** : fichiers et dossiers autorisés à créer/modifier :
  - `public/` — fichiers frontend (HTML, CSS, JS) et assets statiques (logo, favicon)
  - `tests/` — tests
  - Fichiers racine : `requirements.txt`, `README.md`
- **Forbidden changes** :
  - `docs/` — aucune modification des specs existantes
  - `.github/` — aucune modification du workflow agent
  - `src/` — aucune modification du backend (aucune route, aucun service, aucune réponse API modifiée)
  - `Dockerfile`, `pyproject.toml` — pas de modification cette itération
- **Invariants** (hérités de v5, tous préservés) :
  - INV-1 : Aucune donnée utilisateur n'est stockée — ni côté serveur, ni côté client (pas de base de données, pas de `localStorage`, pas de fichier)
  - INV-2 : Toutes les heures affichées sont en fuseau horaire `Europe/Paris` — aucun offset UTC hardcodé
  - INV-3 : La période maximale par requête est de 31 jours
  - INV-4 : Aucune clé API n'est requise
  - INV-5 : L'interface est intégralement en français **avec accents et diacritiques** — y compris le nom de l'application (`HistoMétéo`)
  - INV-6 : L'application est une page unique — pas de routeur multi-pages, l'URL reflète l'état de recherche via query parameters
  - INV-7 : Aucune injection HTML — tout contenu dynamique est inséré via `textContent`, `createElement` ou `replaceChildren()`, jamais via `innerHTML`
  - INV-8 : Le flux principal (recherche simple une commune) reste pleinement fonctionnel en l'absence d'activation du mode comparaison — aucune régression UX
- **Done when** :
  - Les 11 critères d'acceptation originaux (AC1–AC11) restent vérifiables
  - Toutes les fonctionnalités v2–v5 restent opérationnelles (R1–R12, UX/SEO)
  - Les corrections C1 + C2 du feedback v5 sont appliquées (espaces insécables)
  - Les améliorations R15 (meta description dynamique) et R16 (aria-label boutons période) sont intégrées
  - Le design system est refondu : logo, palette, typographie, structure en cards, favicon
  - **Tous** les tests passent (aucun test SKIPPED, aucun DeprecationWarning projet)
  - L'application se lance via `uvicorn src.main:app` ou `docker compose up`

---

## 1) Objectif technique

Refondre l'identité visuelle de l'application HistoMétéo en intégrant le nouveau logo officiel et en dérivant une palette de couleurs, une typographie et une structure en cartes cohérentes avec cette identité. En parallèle, corriger les déviations typographiques identifiées par le Reviewer v5 et intégrer deux améliorations recommandées (R15, R16).

Le résultat : un site au design **moderne, clair, orienté données** — fidèle à l'identité du logo (bleu/vert météo, style flat) — sans aucune modification fonctionnelle ni backend.

---

## 2) Analyse du brief

### Besoins principaux

| Besoin                                                             | Source        | Complexité | Impact                   |
| ------------------------------------------------------------------ | ------------- | ---------- | ------------------------ |
| C1 — Espaces insécables avant `?` dans `renderSeoQuestion`         | Feedback v5   | Trivial    | Typographie française    |
| C2 — Espace insécable avant `:` dans `buildPeriodSummaryParagraph` | Feedback v5   | Trivial    | Typographie française    |
| C3 — `README.md` dans le scope                                     | Feedback v5   | Trivial    | Conformité contractuelle |
| R15 — Meta description dynamique                                   | Feedback v5   | Faible     | SEO                      |
| R16 — `aria-label` sur boutons période                             | Feedback v5   | Trivial    | Accessibilité            |
| Intégration du logo (header, favicon, footer)                      | Design System | Moyen      | Identité visuelle        |
| Nouvelle palette de couleurs                                       | Design System | Moyen      | Cohérence visuelle       |
| Typographie Inter                                                  | Design System | Faible     | Lisibilité               |
| Structure en cards                                                 | Design System | Moyen      | Hiérarchie visuelle      |
| Palette graphiques Chart.js                                        | Design System | Faible     | Cohérence graphique      |
| Responsive adapté                                                  | Design System | Faible     | Mobile                   |

### Contraintes

- **Backend inchangé** : aucune route, aucun service, aucune réponse API modifiée.
- **Pas de framework JS ajouté** — le projet reste en vanilla JS.
- **Pas de nouvelle dépendance backend**.
- **Le logo est fourni en PNG** — il sera livré en deux tailles (logo principal + favicon). Le fichier source est `public/assets/logo-histometeo.png`. Un favicon dérivé sera `public/assets/favicon.png` (32×32 ou 48×48).
- **Pas de web font auto-hébergée** — Inter est chargée via Google Fonts CDN (léger, standard, fiable).
- **La structure HTML existante est préservée** — les sections sont enrichies avec des classes de cards mais l'ordre et les IDs restent identiques.
- **Les variables CSS existantes sont remplacées** — la palette change intégralement. Toute la charte graphique est pilotée par les variables CSS `:root`, donc le changement est centralisé.

### Risques

| #   | Risque                                                                                           | Mitigation                                                                                                           |
| --- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
| 1   | **Régression visuelle** — le changement de palette peut casser des contrastes ou des lisibilités | Ratio de contraste vérifié pour chaque combinaison texte/fond. Les couleurs UI sont choisies pour `WCAG AA` minimum. |
| 2   | **Performance Google Fonts** — ajout d'une requête réseau bloquante                              | `<link rel="preconnect">` + `display=swap` pour fallback instantané.                                                 |
| 3   | **Logo lourd en PNG** — impact sur le First Contentful Paint                                     | Le logo sera optimisé (< 50 Ko). Lazy load non nécessaire car il est above the fold.                                 |

---

## 3) Design minimal proposé

### 3.1 Architecture globale

```
Backend : AUCUNE modification

Frontend (modifications)
├── public/index.html        → Logo, favicon, Google Fonts, meta OG, structure cards
├── public/app.js            → Corrections C1/C2, R15 meta description, R16 aria-label
├── public/style.css         → Nouvelle palette, typographie, structure cards, ajustements
├── public/assets/            → NOUVEAU dossier
│   ├── logo-histometeo.png   → Logo principal
│   └── favicon.png           → Favicon dérivé du logo

Racine
├── README.md                → Documentation mise à jour (ajouté au scope via C3)
```

### 3.2 Nouvelles variables CSS (remplacement complet de `:root`)

L'ancienne palette (beige/teal) est remplacée par la palette dérivée du logo :

```css
:root {
  /* Palette principale — dérivée du logo */
  --blue-meteo: #2a7daf;
  --green-meteo: #6dbe45;
  --blue-light: #5cb4d6;

  /* Couleurs UI */
  --bg: #f7f9fb;
  --panel: #ffffff;
  --text: #2a2a2a;
  --muted: #6b7280;
  --accent: #2a7daf;
  --accent-hover: #1e6490;
  --accent-soft: rgba(42, 125, 175, 0.08);
  --green: #6dbe45;
  --green-hover: #5aa838;
  --error: #dc2626;
  --border: #e5e7eb;

  /* Couleurs météo (icônes, graphiques) */
  --weather-sun: #f59e0b;
  --weather-rain: #5cb4d6;
  --weather-cloud: #9ca3af;

  /* Graphiques Chart.js */
  --chart-temp: #f97316;
  --chart-precip: #5cb4d6;
  --chart-wind: #9ca3af;

  /* Ombres */
  --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.06);
  --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);

  /* Spacing */
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 16px;
  --space-lg: 24px;
  --space-xl: 32px;

  /* Radius */
  --radius: 10px;
  --radius-sm: 8px;
}
```

### 3.3 Typographie — Google Fonts Inter

Ajout dans `<head>` de `index.html` :

```html
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
  href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap"
  rel="stylesheet"
/>
```

Changement CSS :

```css
body {
  font-family:
    "Inter",
    system-ui,
    -apple-system,
    sans-serif;
}
```

Hiérarchie typographique :

| Élément                     | Taille        | Poids |
| --------------------------- | ------------- | ----- |
| H1 (titre principal)        | 2rem (32px)   | 600   |
| H2 (sections)               | 1.5rem (24px) | 600   |
| H3 (sous-sections)          | 1.15rem       | 600   |
| Texte normal                | 1rem (16px)   | 400   |
| Texte secondaire (`.muted`) | 0.875rem      | 400   |

### 3.4 Logo — Header

Le header actuel (texte simple) est remplacé par un header avec logo :

```html
<header class="hero">
  <a href="/" class="logo-link" aria-label="Accueil HistoMétéo">
    <img
      src="/assets/logo-histometeo.png"
      alt="HistoMétéo"
      class="logo"
      width="280"
      height="80"
    />
  </a>
  <p class="hero-subtitle">
    Retrouvez la météo passée, heure par heure, pour n'importe quelle commune de
    France.
  </p>
</header>
```

> Le `<h1>` textuel « HistoMétéo » est remplacé par le logo qui contient déjà le texte « HistoMétéo — Historique de la Météo ». Un `<h1>` en `sr-only` est ajouté pour l'accessibilité SEO :

```html
<h1 class="sr-only">HistoMétéo — Historique de la Météo en France</h1>
```

CSS du logo :

```css
.logo-link {
  display: inline-block;
  text-decoration: none;
}

.logo {
  height: 80px;
  width: auto;
  max-width: 100%;
}

@media (max-width: 640px) {
  .logo {
    height: 56px;
  }
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}
```

### 3.5 Favicon

Ajout dans `<head>` :

```html
<link rel="icon" type="image/png" href="/assets/favicon.png" />
```

Le fichier `favicon.png` est une version réduite (48×48) de l'icône calendrier du logo (partie gauche uniquement, sans le texte).

### 3.6 Open Graph / Partages sociaux

Ajout des meta tags dans `<head>` :

```html
<meta property="og:title" content="HistoMétéo — Historique de la Météo" />
<meta
  property="og:description"
  content="Retrouvez la météo passée, heure par heure, pour n'importe quelle commune de France."
/>
<meta property="og:image" content="/assets/logo-histometeo.png" />
<meta property="og:type" content="website" />
```

### 3.7 Footer avec logo

Le footer actuel est enrichi avec un petit logo :

```html
<footer>
  <img
    src="/assets/logo-histometeo.png"
    alt=""
    class="footer-logo"
    aria-hidden="true"
    width="140"
    height="40"
  />
  <p>
    Données :
    <a href="https://geo.api.gouv.fr" target="_blank" rel="noopener"
      >geo.api.gouv.fr</a
    >
    et
    <a href="https://open-meteo.com" target="_blank" rel="noopener"
      >Open-Meteo Historical Archive API</a
    >.
  </p>
</footer>
```

CSS footer :

```css
.footer-logo {
  height: 40px;
  width: auto;
  margin-bottom: var(--space-sm);
  opacity: 0.7;
}
```

### 3.8 Structure en cards

Chaque section de contenu (`.panel`) hérite du nouveau style card :

```css
.panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  padding: var(--space-lg);
  margin-bottom: var(--space-md);
}
```

Pas de changement de classes HTML — `.panel` est déjà utilisé sur toutes les sections. Le changement est purement CSS.

### 3.9 Boutons

**Bouton principal (Rechercher)** — passe au vert météo :

```css
.btn-primary,
button[type="submit"],
#search-button {
  background: var(--green);
  color: #fff;
  border: none;
  border-radius: var(--radius-sm);
  padding: 0.6rem 1.5rem;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s;
}

.btn-primary:hover,
#search-button:hover {
  background: var(--green-hover);
}
```

**Boutons secondaires** — restent avec bordure, accent bleu :

```css
.btn-secondary {
  background: transparent;
  border: 1px solid var(--accent);
  color: var(--accent);
  border-radius: var(--radius-sm);
  padding: 0.5rem 1rem;
  font-weight: 500;
  cursor: pointer;
  transition:
    background 0.15s,
    color 0.15s;
}

.btn-secondary:hover {
  background: var(--accent);
  color: #fff;
}
```

### 3.10 Graphiques Chart.js — Palette mise à jour

Dans `app.js`, les couleurs des datasets Chart.js doivent utiliser les nouvelles couleurs :

| Dataset        | Ancienne couleur | Nouvelle couleur       | Variable CSS     |
| -------------- | ---------------- | ---------------------- | ---------------- |
| Température    | (variable)       | `#F97316` (orange)     | `--chart-temp`   |
| Précipitations | (variable)       | `#5CB4D6` (bleu clair) | `--chart-precip` |
| Vent           | (variable)       | `#9CA3AF` (gris)       | `--chart-wind`   |

> Les couleurs sont codées en dur dans `app.js` (Chart.js n'utilise pas les CSS variables). Les remplacer par les nouvelles valeurs hexadécimales.

### 3.11 Espacement et rythme visuel

Le spacing utilise un système basé sur `8px` :

```css
/* Déjà défini dans :root via les variables --space-* */
/* Application principale */

body {
  padding-top: var(--space-lg);
}

.hero {
  padding: var(--space-xl) 0 var(--space-md);
  text-align: center;
}

.container {
  max-width: 1024px;
  margin: 0 auto;
  padding: 0 var(--space-md);
  display: grid;
  gap: var(--space-md);
}
```

### 3.12 Correction C1 — Espaces insécables avant `?` dans `renderSeoQuestion`

4 occurrences dans `app.js` à corriger dans la fonction `renderSeoQuestion` :

```js
// Avant (mode single-day, sans commune2)
`Quel temps faisait-il à ${commune1Name} le ${start} ?`
// Après
`Quel temps faisait-il à ${commune1Name} le ${start}\u00a0?`
// Avant (mode single-day, avec commune2)
`Quel temps faisait-il à ${commune1Name} et ${commune2Name} le ${start} ?`
// Après
`Quel temps faisait-il à ${commune1Name} et ${commune2Name} le ${start}\u00a0?`
// Avant (mode range, sans commune2)
`Quel temps faisait-il à ${commune1Name} du ${start} au ${end} ?`
// Après
`Quel temps faisait-il à ${commune1Name} du ${start} au ${end}\u00a0?`
// Avant (mode range, avec commune2)
`Quel temps faisait-il à ${commune1Name} et ${commune2Name} du ${start} au ${end} ?`
// Après
`Quel temps faisait-il à ${commune1Name} et ${commune2Name} du ${start} au ${end}\u00a0?`;
```

### 3.13 Correction C2 — Espace insécable avant `:` dans `buildPeriodSummaryParagraph`

1 occurrence dans `app.js` :

```js
// Avant
`Cumul de pluie : ${precip} mm.`
// Après
`Cumul de pluie\u00a0: ${precip} mm.`;
```

### 3.14 Amélioration R15 — Meta description dynamique

Ajouter un `<meta name="description">` dans le HTML avec un contenu par défaut :

```html
<meta
  name="description"
  content="Retrouvez la météo passée, heure par heure, pour n'importe quelle commune de France. Température, pluie, vent, humidité — données historiques depuis 1940."
/>
```

Dans `app.js`, mise à jour dynamique après affichage des résultats :

```js
function updateMetaDescription(commune1Name, commune2Name, startDate, endDate) {
  const meta = document.querySelector('meta[name="description"]');
  if (!meta) return;
  const fmt = (isoDate) => {
    const d = parseIsoDateAtMidnight(isoDate);
    return formatNaturalFrDate(d);
  };
  const start = fmt(startDate);
  const end = fmt(endDate);
  let content;
  if (startDate === endDate) {
    content = commune2Name
      ? `Météo historique à ${commune1Name} et ${commune2Name} le ${start}. Température, pluie, vent, humidité heure par heure.`
      : `Météo historique à ${commune1Name} le ${start}. Température, pluie, vent, humidité heure par heure.`;
  } else {
    content = commune2Name
      ? `Météo historique à ${commune1Name} et ${commune2Name} du ${start} au ${end}. Température, pluie, vent, humidité heure par heure.`
      : `Météo historique à ${commune1Name} du ${start} au ${end}. Température, pluie, vent, humidité heure par heure.`;
  }
  meta.setAttribute("content", content);
}
```

Appelée dans `renderSimpleResults` et `renderComparisonResults`, juste après `renderSeoQuestion`.

Restauration du contenu par défaut dans `clearResults` :

```js
function clearResults() {
  // ... masquer les sections existantes ...
  const meta = document.querySelector('meta[name="description"]');
  if (meta) {
    meta.setAttribute(
      "content",
      "Retrouvez la météo passée, heure par heure, pour n'importe quelle commune de France. Température, pluie, vent, humidité — données historiques depuis 1940.",
    );
  }
}
```

### 3.15 Amélioration R16 — `aria-label` sur boutons période

Dans la fonction `renderPeriodLinks`, ajouter des `aria-label` descriptifs :

```js
prevPeriodButton.setAttribute(
  "aria-label",
  canPrev
    ? `Voir la période du ${formatSummaryDate(fmt(prevStart))} au ${formatSummaryDate(fmt(prevEnd))}`
    : "Période précédente indisponible",
);
nextPeriodButton.setAttribute(
  "aria-label",
  canNext
    ? `Voir la période du ${formatSummaryDate(fmt(nextStart))} au ${formatSummaryDate(fmt(nextEnd))}`
    : "Période suivante indisponible",
);
```

### 3.16 Background page

Le fond de page perd le gradient beige et adopte un fond neutre clair :

```css
body {
  background: var(--bg);
  color: var(--text);
  font-family:
    "Inter",
    system-ui,
    -apple-system,
    sans-serif;
  margin: 0;
  padding-top: var(--space-lg);
  min-height: 100vh;
}
```

> Le double radial-gradient existant est supprimé — le fond `#F7F9FB` est propre et suffisant.

---

## 4) Plan d'implémentation

### Étape 1 — Assets : Préparer logo et favicon

**Fichiers** : `public/assets/logo-histometeo.png`, `public/assets/favicon.png`

- Créer le dossier `public/assets/`
- Placer le logo principal (`logo-histometeo.png`) — directement le fichier fourni, optimisé si > 100 Ko
- Créer un favicon (`favicon.png`, 48×48) à partir de la partie icône (calendrier) du logo

**Testable** : les fichiers sont accessibles via `http://localhost:8000/assets/logo-histometeo.png` et `http://localhost:8000/assets/favicon.png`.

---

### Étape 2 — HTML : Favicon, Google Fonts, meta OG, meta description, header, footer

**Fichier** : `public/index.html`

- Ajouter les `<link>` pour Google Fonts Inter (preconnect + stylesheet)
- Ajouter `<link rel="icon" type="image/png" href="/assets/favicon.png">`
- Ajouter `<meta name="description" ...>` (contenu par défaut)
- Ajouter les meta Open Graph (`og:title`, `og:description`, `og:image`, `og:type`)
- Remplacer le contenu du `<header class="hero">` : logo cliquable `<a>` + `<img>` + `<h1 class="sr-only">` + sous-titre `<p>`
- Mettre à jour le `<footer>` : ajouter le petit logo

**Testable** : la page affiche le logo dans le header et le footer, le favicon est visible dans l'onglet du navigateur, pas d'erreur console.

---

### Étape 3 — CSS : Nouvelle palette, typographie, cards, boutons, spacing

**Fichier** : `public/style.css`

- Remplacer intégralement le bloc `:root` par les nouvelles variables (section 3.2)
- Mettre à jour `body` : font-family Inter, background `var(--bg)`, supprimer le radial-gradient
- Mettre à jour `.hero` : centrage, padding, styles logo
- Ajouter `.logo-link`, `.logo`, `.footer-logo`, `.sr-only`
- Mettre à jour `.panel` : ombre, radius, padding adaptés
- Mettre à jour les boutons : bouton principal en vert, boutons secondaires en bleu
- Mettre à jour `.results-nav` : couleurs adaptées à la palette
- Mettre à jour `.btn-period-link` : couleurs adaptées
- Mettre à jour les styles de tableaux, accordéons, erreurs avec la nouvelle palette
- Vérifier les media queries (640px, 768px) pour le logo responsive

**Testable** : le site affiche le nouveau design. Toutes les sections sont lisibles, les contrastes sont corrects, le responsive fonctionne.

---

### Étape 4 — JS : Corrections C1 + C2 (espaces insécables)

**Fichier** : `public/app.js`

- Corriger 4 templates dans `renderSeoQuestion` : ` ?` → `\u00a0?`
- Corriger 1 template dans `buildPeriodSummaryParagraph` : ` :` → `\u00a0:`

**Testable** : recherche effectuée → la question SEO affiche une espace insécable avant `?`. Le résumé affiche une espace insécable avant `:`.

---

### Étape 5 — JS : R15 meta description dynamique + R16 aria-label

**Fichier** : `public/app.js`

- Implémenter `updateMetaDescription(commune1Name, commune2Name, startDate, endDate)`
- Appeler dans `renderSimpleResults` et `renderComparisonResults`
- Restaurer le contenu par défaut dans `clearResults`
- Ajouter `setAttribute("aria-label", ...)` dans `renderPeriodLinks`

**Testable** : après une recherche, inspecter `<meta name="description">` → contenu dynamique. Boutons période ont des `aria-label` descriptifs. Après effacement → meta restaurée.

---

### Étape 6 — JS : Couleurs Chart.js mises à jour

**Fichier** : `public/app.js`

- Remplacer les couleurs des datasets Chart.js par les nouvelles valeurs :
  - Température : `#F97316` (orange)
  - Précipitations : `#5CB4D6` (bleu clair)
  - Vent : `#9CA3AF` (gris)

**Testable** : le graphique utilise les nouvelles couleurs cohérentes avec la palette.

---

### Étape 7 — README.md : Mise à jour documentation

**Fichier** : `README.md`

- Mettre à jour la section features si nécessaire
- Documenter le nouveau design system (brièvement)
- S'assurer que les instructions de lancement sont toujours correctes

**Testable** : le README reflète l'état actuel du projet.

---

## 5) Guide pour le Développeur

### Pièges fréquents

1. **Remplacement des CSS variables** : toute la palette est pilotée par `:root`. En modifiant les variables, tous les composants changent de couleur automatiquement. Mais les références hardcodées dans `app.js` (couleurs Chart.js) doivent être mises à jour manuellement — Chart.js ne lit pas les CSS variables.

2. **Logo et `<h1>`** : le `<h1>` textuel « HistoMétéo » disparaît visuellement au profit du logo. Un `<h1 class="sr-only">` doit être conservé pour le SEO et l'accessibilité. Ne PAS supprimer le `<h1>` — l'en cacher visuellement avec `.sr-only`.

3. **Google Fonts et performance** : le `<link rel="preconnect">` doit être placé **avant** le `<link>` du stylesheet dans le `<head>`. `display=swap` est **obligatoire** pour éviter un FOIT (flash of invisible text).

4. **Espace insécable `\u00a0`** : dans les template literals JS, `\u00a0` est interprété comme un caractère Unicode, pas comme une chaîne de 6 caractères. C'est le bon usage. Ne pas utiliser `&nbsp;` (c'est du HTML, pas du texte).

5. **Favicon** : le favicon est un PNG simple. Pas besoin de `.ico`, de `manifest.json` ni de multiples tailles pour un MVP. Un seul `<link rel="icon">` suffit.

6. **`og:image` et URL relative** : les meta OG avec URL relative fonctionnent dans la plupart des crawlers mais pas tous. Pour le MVP c'est acceptable. Si un nom de domaine est ajouté plus tard, convertir en URL absolue.

### Zones de dérive

- **Ne pas ajouter de dark mode** — pas dans cette itération.
- **Ne pas ajouter d'animations complexes** — les transitions existantes (`transition: background 0.15s`) suffisent.
- **Ne pas auto-héberger la font Inter** — rester sur Google Fonts CDN.
- **Ne pas modifier la structure des réponses `/api/weather`** ni aucun fichier dans `src/`.
- **Ne pas ajouter de nouvelle section dans la navigation interne** — les 3 ancres restent identiques.
- **Ne pas toucher au `Dockerfile`** — les assets dans `public/` sont déjà copiés via `COPY public /app/public`.

### Simplifications autorisées

- Le logo PNG est utilisé tel quel (pas de version SVG à créer).
- Le favicon est un simple crop/resize du logo en PNG, pas besoin de format `.ico`.
- Les meta Open Graph utilisent des URL relatives (acceptables pour le MVP).
- Les ombres de cards sont subtiles — pas besoin de variantes hover avec ombre renforcée.
- Si le gradient de fond actuel est référencé en plusieurs endroits, le supprimer d'un coup et remplacer par `background: var(--bg)` partout.

### Décisions explicitement interdites

- Modifier la structure de la réponse de `/api/weather`
- Modifier un fichier dans `src/`
- Ajouter un routeur frontend
- Introduire un bundler, un state manager ou un framework JS
- Ajouter du `localStorage`, `sessionStorage` ou des cookies
- Ajouter un dark mode ou un theme switcher
- Modifier `Dockerfile` ou `pyproject.toml`

---

## 6) Stratégie de tests

### Tests unitaires backend

Les 27 tests existants restent inchangés et doivent continuer à passer. Aucun nouveau test backend n'est nécessaire (toutes les modifications sont frontend/CSS/assets).

### Tests manuels frontend (checklist)

#### Design system

| #   | Scénario                      | Résultat attendu                                                           |
| --- | ----------------------------- | -------------------------------------------------------------------------- |
| D1  | Page d'accueil — logo header  | Logo affiché, centré, cliquable (redirige vers `/`)                        |
| D2  | Logo responsive ≤ 640px       | Hauteur réduite (~56px), pas de débordement                                |
| D3  | Favicon dans l'onglet         | Icône calendrier visible dans l'onglet du navigateur                       |
| D4  | Palette — fond de page        | Fond `#F7F9FB` (gris très clair), plus de beige/gradient                   |
| D5  | Palette — panels              | Fond blanc, bordure `#E5E7EB`, ombre légère                                |
| D6  | Palette — texte principal     | `#2A2A2A` — bon contraste sur fond blanc                                   |
| D7  | Palette — bouton Rechercher   | Vert `#6DBE45`, texte blanc, hover plus foncé                              |
| D8  | Palette — boutons secondaires | Bordure bleue `#2A7DAF`, texte bleu, hover inversé                         |
| D9  | Typographie Inter chargée     | La police Inter est visible (vérifier via DevTools > Computed Font)        |
| D10 | Footer — logo + attribution   | Petit logo semi-transparent + liens sources                                |
| D11 | Graphique Chart.js — couleurs | Température orange, précipitations bleu clair, vent gris                   |
| D12 | Meta OG — inspection          | `<meta og:title>`, `og:description>`, `og:image>` présentes dans le source |

#### Corrections feedback v5

| #   | Scénario                                    | Résultat attendu                                                |
| --- | ------------------------------------------- | --------------------------------------------------------------- |
| F1  | Recherche simple → question SEO             | Espace insécable avant `?` (pas de retour à la ligne avant `?`) |
| F2  | Résumé période → cumul pluie                | Espace insécable avant `:` dans « Cumul de pluie : X mm »       |
| F3  | `meta[name="description"]` après recherche  | Contenu mis à jour dynamiquement avec commune + dates           |
| F4  | `meta[name="description"]` après effacement | Contenu restauré au texte par défaut                            |
| F5  | Boutons période → `aria-label`              | Libellé descriptif avec les dates de la période cible           |

#### Régression (vérification non exhaustive des fonctionnalités existantes)

| #   | Scénario                           | Résultat attendu                                                          |
| --- | ---------------------------------- | ------------------------------------------------------------------------- |
| R1  | Auto-complétion commune            | Fonctionne dès 2 caractères, accents OK                                   |
| R2  | Recherche simple → tableau horaire | Toutes colonnes affichées, heures en Europe/Paris                         |
| R3  | Mode comparaison                   | Toggle par ville, synthèse delta, deux blocs horaires                     |
| R4  | URL partageable                    | Les query params se mettent à jour, le rechargement restaure la recherche |
| R5  | Périodes prédéfinies               | Les boutons preset fonctionnent                                           |
| R6  | Navigation interne sticky          | 3 liens, sticky au scroll, scroll fluide                                  |
| R7  | Liens précédent/suivant            | Calcul correct, boutons désactivés aux limites                            |
| R8  | Mobile 360px                       | Tout est lisible, pas de débordement horizontal                           |

### Edge cases design

- Logo sur fond blanc : vérifier que le logo est bien lisible (pas de fond transparent qui s'effacerait)
- Contraste texte muted (`#6B7280`) sur fond blanc (`#FFFFFF`) : ratio ≈ 5.0:1 → WCAG AA OK
- Contraste texte principal (`#2A2A2A`) sur fond blanc : ratio ≈ 14.5:1 → WCAG AAA OK
- Contraste bouton vert (`#6DBE45`) avec texte blanc : ratio ≈ 3.1:1 → limite WCAG AA pour du texte large (boutons). Acceptable pour les boutons (texte > 18px ou bold > 14px). Si le texte est plus petit, assombrir à `#5AA838` pour le fond du bouton.

---

## 7) Risques techniques

| #   | Risque                                                                                                            | Mitigation                                                                                                                                                                                   |
| --- | ----------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1   | **Régression visuelle généralisée** — le changement de palette touche toute l'interface d'un coup                 | Les CSS variables centralisent tout. Tester systématiquement chaque section après le changement. Le plan d'implémentation sépare CSS (étape 3) et JS (étapes 4-6) pour isoler les problèmes. |
| 2   | **Contraste insuffisant bouton vert** — le vert `#6DBE45` sur blanc peut manquer de contraste pour du petit texte | Le bouton Rechercher utilise du texte blanc sur fond vert — vérifier que le font-weight 500+ et la taille 1rem suffisent. Fallback : utiliser `#5AA838` (vert plus foncé).                   |
| 3   | **Google Fonts indisponible** — si le CDN est bloqué (proxy entreprise, offline)                                  | Le fallback `system-ui` est déclaré. `display=swap` garantit que le texte est affiché immédiatement. Aucun FOIT.                                                                             |
