# Carte interactive du Bénin

Deux fichiers sortent du même code, parce que les deux usages n'ont pas les mêmes
contraintes :

| Fichier | Poids | Pour quoi |
|---|---|---|
| `index.html` | **330 Ko** | Le vrai site. Les 29 photos sont dans `photos/` et ne se chargent qu'à l'affichage : le premier écran ne coûte que ~400 Ko. À mettre en ligne avec le dossier `photos/` à côté. |
| `index-artefact.html` | 2,6 Mo | Le lien partageable. Tout est embarqué en base64 : le fichier marche seul, hors ligne, sans serveur. **Non livré dans l'archive** — il se régénère avec `build.py`. |

En ligne : https://claude.ai/code/artifact/2b44d637-a5aa-4248-99d0-230dcc5f6b4d

---

## Modifier la page

```bash
cd source
python3 build.py        # réécrit ../index.html et ../index-artefact.html
```

| Fichier | À quoi ça sert |
|---|---|
| `source/build.py` | **Le contenu des 29 lieux** (nom, département, description, « à ne pas manquer », saison), les vignettes illustrées, le relief et l'assemblage du SVG. C'est ici qu'on ajoute ou retire un lieu. |
| `source/template.html` | La mise en page : styles, colonnes, carrousel, placement des noms, comportements. |
| `source/proj.py` | La projection. Transforme `ben1.json` (geoBoundaries) en `geo.json`. |
| `source/reseau.py` | Fleuves, lacs, routes et voie ferrée : lit les données OSM/Natural Earth et écrit `reseau.json`. |
| `source/photos_build.py` | Télécharge, recadre et encode les photos en WebP. |
| `source/geo.json` | Tracés des 12 départements et positions des lieux, déjà projetés. |
| `source/reseau.json` | Le réseau projeté et simplifié (31 Ko). |
| `source/photos.json` | Les 29 photos en base64 + auteur + licence + page source. |
| `source/fonts.json` | Bricolage Grotesque et Instrument Sans, intégrées. |

### Deux conventions à connaître

Une **étoile** devant le nom marque les deux capitales — Porto-Novo la politique,
Cotonou l'économique (champ `cap` dans `build.py`). L'étoile fait partie du texte du
nom : elle passe donc par la même mesure et le même placement automatique, il n'y a
rien à régler. La fiche dit laquelle des deux.

### Les familles

Cinq familles, définies dans `CATS` (`template.html`) : plages, culture, nature,
villes, et **« Arriver au pays »** — l'aéroport et le port. Ces deux-là ne sont ni un
monument ni un marché, mais pour qui rentre ce sont les deux premiers lieux vus du
pays ; les ranger ailleurs aurait été faux. Ajouter une famille demande trois choses :
une entrée dans `CATS`, un pictogramme dans `GLYPHS`, et la couleur `--c-<nom>`
**aux trois endroits** du thème.

Cotonou, son aéroport et son port sont à cinq kilomètres les uns des autres, soit dix
unités de carte : leurs repères se recouvraient. `ECART` dans `build.py` les écarte à
l'affichage — les coordonnées réelles, affichées dans la fiche, ne bougent pas.

### Ajouter un lieu

1. `source/proj.py` : ajouter sa latitude/longitude à `SITES`, puis `python3 proj.py`.
2. `source/build.py` : ajouter son bloc de contenu dans `S`.
3. `python3 build.py`. **Le nom se place tout seul** — inutile de chercher où le mettre.

### Le flottement des repères

Les vingt-neuf repères respirent en permanence : 2,6 unités de haut, cycle de 3 s,
chacun décalé dans le cycle par un `animation-delay` **négatif** (`--ba-phase`, posé à
la construction). Le délai négatif fait démarrer tout le monde immédiatement mais à un
endroit différent de la courbe ; sans lui les vingt-neuf montent ensemble et ça fait
métronome.

Le mouvement est porté par un groupe dédié, `.flot`, qui n'entoure que le dessin :
- **l'ombre reste au sol** et se resserre au lieu de monter, ce qui fait lire un
  décollage plutôt qu'un glissement ;
- **la zone cliquable ne bouge pas**, elle ne se dérobe donc jamais sous le doigt ;
- **le survol et la sélection restent sur `.art`**, à l'intérieur : les deux
  transformations s'additionnent, le repère survolé passe donc toujours au-dessus de
  son flottement.

Piège rencontré : une transition qui part de `transform:none` **n'aboutit jamais** quand
le parent anime lui aussi `transform` — Chrome la relance à chaque image. `.art` porte
donc un `transform:translateY(0)` explicite au repos. Sans lui, le survol ne levait plus
rien.

**Les deux réglages sont en tête de la palette**, dans `template.html` :

```css
--ba-flot-hauteur:4.4px;   /* de combien la pastille monte */
--ba-flot-vitesse:2.4s;    /* durée d'un aller */
```

Il suffit de les changer et de relancer `python3 build.py`. Les décalages entre repères
se recalculent tout seuls sur le nouveau cycle. Attention : les modifier à chaud dans
l'inspecteur ne marche pas — le navigateur ne réévalue pas le `calc()` d'une animation
déjà lancée. Il faut passer par le fichier.

Le survol est volontairement **instantané** : une transition sur `transform` posée dans
ce sous-arbre ne progresse jamais (le navigateur la relance sans fin et le repère reste
à zéro — vérifié). Le halo, lui, garde son fondu.

Le tout est sous `@media (prefers-reduced-motion:no-preference)` : rien ne bouge pour
qui a désactivé les animations.

### Où se placent les noms

Il n'y a plus d'étiquettes en boîte ni de fils de rappel : chaque nom est posé
directement à côté de son repère, détouré par un liseré couleur papier. Le placement
est calculé dans le navigateur (`layoutTags`) : chaque nom essaie **huit directions à
quatre distances** et prend la première place libre — libre voulant dire sans toucher un
autre repère, un nom déjà posé, ni un toponyme du fond (villes, reliefs, fleuves).
Les lieux majeurs (`MAJEURS` dans `template.html`) choisissent en premier, portent un
repère pleine taille et un nom plus appuyé.

Si un nom tombe mal après une modification du fond, il ne faut pas le déplacer à la
main : c'est le signe qu'il manque une place, donc qu'il faut alléger le voisinage.
C'est ainsi qu'a disparu l'hydronyme « Lac Ahémé », qui doublonnait avec le nom du
repère au même endroit.

### Régler la colonne de droite

Le cadre décalé de la carte-photo tient à deux variables, dans `.carousel` :
`--fpad` (l'épaisseur du cadre autour de la photo) et `--ovl` (de combien le cadre
remonte sur la photo). Les flèches se placent toutes seules à hauteur du haut du cadre.

Les couleurs d'accent sont `--acc` (vert), `--sun` (jaune) et `--acc-ink` (le vert
foncé des textes), **à changer aux trois endroits** : clair, sombre système, sombre forcé.

### Changer une photo

Éditer `PICKS` dans `source/photos_build.py`, puis `python3 photos_build.py` et
`python3 build.py`. Le recadrage passe par PIL (`cadre()`) : redimension par le côté
qui manque, puis rognage au centre. L'encodage WebP se fait **directement depuis
l'original** — passer par un JPEG intermédiaire empilait deux compressions et coûtait
30 % de poids pour rien. Les candidats déjà trouvés sont dans `photo_candidates.json` ;
`photos_find.py` sert à en chercher d'autres sur Wikimedia Commons.

### Sur petit écran

Sous 560 px de carte, la page bascule en mode `compact` : les noms disparaissent (sauf
celui du repère choisi), les repères grossissent de 75 % — groupe `.ech`, sinon ils
feraient cinq pixels et le doigt ne pourrait pas les viser — et les vignettes de
ressources s'effacent.

La fiche, elle, **reste posée sur la carte** comme sur grand écran : la sortir en
dessous cassait la lecture, on perdait le lien avec le repère touché. Elle se contente
d'être plus compacte, de se coller aux bords, et **de perdre sa photo** — avec elle
elle faisait 559 px de haut pour une carte de 466 et débordait. Le carrousel juste en
dessous montre la photo en grand de toute façon. Sa hauteur est bornée et elle défile
toute seule pour les fiches les plus longues.

---

## Intégrer la section dans un autre site

Tout le composant vit sous une seule racine, `.benin-atlas` :

- **Le CSS est cloisonné.** Les 249 règles sont portées par `.benin-atlas`, et les
  jetons de thème sont préfixés `--ba-*` (`--ba-ink`, `--ba-card`…) pour ne pas croiser
  ceux du projet hôte. Les animations aussi : `ba-drop`, `ba-blip`, `ba-pulse`.
- **Le JavaScript n'expose rien.** Il tourne dans une fonction anonyme en mode strict ;
  aucun nom ne part dans `window`. Avant, une cinquantaine le faisait, dont `select`,
  `apply`, `html` et `card`.
- **Les identifiants sont préfixés** `ba-`, y compris ceux du SVG (`ba-landGrad`,
  `ba-pinDrop`, `ba-landClip`…), qui sont eux aussi globaux dans une page.

**Le chemin des photos tient en une ligne**, en tête du script :

```js
const PHOTOS_BASE = 'photos/';   // → '/assets/benin/' par exemple
```

C'est le seul chemin de toute la section : rien d'autre n'est appelé depuis l'extérieur,
ni police, ni image, ni script. Le HTML peut donc être déplacé n'importe où.

**Le seul style qui sort du composant** est une règle `body{margin:0;background:…}`,
signalée en clair dans la feuille. Elle sert à la page autonome ; il faut la retirer le
jour où la section est posée dans une page qui a déjà la sienne.

Reste une limite assumée : les identifiants étant fixes, **on ne peut pas poser deux
cartes sur la même page**. Il faudrait pour cela les générer, ce qui n'a pas d'intérêt
tant qu'il n'y en a qu'une.

---

## Sources et licences

Les crédits sont affichés en une ligne en bas de la carte. **Il faut les garder** :
c'est la condition de réutilisation des trois jeux de données.

- **Frontières** : [geoBoundaries](https://www.geoboundaries.org/) (ADM1, Bénin), CC BY.
  Projection équirectangulaire ; positions et distances indicatives.
- **Réseau** (fleuves, lacs, routes, voie ferrée) : [OpenStreetMap](https://www.openstreetmap.org/copyright),
  sous **ODbL** — l'attribution est obligatoire.
- **Relief et fleuve Niger** : [Natural Earth](https://www.naturalearthdata.com/), domaine public.
- **Photographies** : Wikimedia Commons, sous licence libre (CC0, CC BY, CC BY-SA).
  L'auteur et la licence de chaque image sont dans `source/photos.json`.
- **Polices** : Bricolage Grotesque et Instrument Sans, sous licence SIL Open Font.

## Regénérer les données géographiques

`reseau.json` est déjà dans le dépôt, il n'y a donc rien à retélécharger pour
travailler. Si besoin, les sources brutes se récupèrent ainsi :

- Natural Earth : `geojson/ne_10m_rivers_lake_centerlines.geojson` et `ne_10m_lakes.geojson`
  depuis le dépôt `nvkelso/natural-earth-vector`.
- OpenStreetMap : trois requêtes Overpass (cours d'eau nommés, plans d'eau du sud,
  routes `trunk`/`primary` et `railway=rail`), sur la boîte du Bénin.

Puis `python3 reseau.py`. Le script projette, découpe au pays (via un masque rasterisé,
sinon tester 50 000 points contre 30 000 arêtes prendrait des heures), simplifie en
Douglas-Peucker et recolle les tronçons — OSM livre le Couffo en 69 morceaux.
