diff --git a/CLAUDE.md b/CLAUDE.md
index 3298d48..dfa309b 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -3058,3 +3058,101 @@ n'affirme rien — comparer avec `RangDesEnvies`, qui avait dû reconduire un or
| `GET /api/version` avec `-p:Version=0.4.1 -p:MaBibliDateBuild=…` | `{"numero":"0.4.1","publiee":true}` |
**618 tests au vert** (506 au début de la série), aucun avertissement de compilation.
+
+## La 7ᵉ série vérifiée en navigateur — trois défauts, tous invisibles aux tests (2026-08-21)
+
+La série Q à Y s'était close sur un aveu : « **aucun** écran n'a été regardé s'afficher ». Le
+balayage l'a été le 2026-08-21, à **320, 375 et 1280 px**, sur les vingt-deux routes de
+l'application. Les 618 tests ne pouvaient rien en dire — ils couvrent les services et l'API, pas
+la mise en page ni le réseau réel.
+
+⚠️ **Le panneau du navigateur d'automatisation ne compose pas d'image** : aucune capture d'écran
+n'est possible ici. Ce n'est pas un obstacle, et c'est même instructif — la vérification s'est
+faite sur la **géométrie réelle** (`getBoundingClientRect`, `scrollWidth`) et les styles calculés,
+qui prouvent un débordement de 8 px qu'un œil sur une capture aurait laissé passer.
+
+### Ce que la vérification a confirmé
+
+| Lot | Vérifié |
+|---|---|
+| Q | ordre titre → type → thèmes → auteurs → rôles → reste ; les rôles n'apparaissent **qu'à deux auteurs** |
+| R | à 375 px menu **plein écran** (375×812, `fixed`, focalisé, Échap ferme) ; à 1280 px rangée collante sous un en-tête `sticky top:0` |
+| S | remontée d'un cran sur **cinq branches** éprouvées, et l'arrêt au catalogue : `/revues/3/edition → /revues/3 → /revues → / → /` |
+| T | actions à droite sur la ligne du titre à 1280 px (bord droit commun) ; elles passent à la ligne à 375 px, ce qui est la bonne dégradation |
+| U | champ « Nombre de pages (facultatif) » en `` |
+| W | actions en tête, ISSN à tiret, numéros du plus récent au plus ancien, **sans date en dernier** ; l'édition d'un numéro rouvre bien ses articles joints par « ; », « Ukraine, deux ans après » restant entier |
+| X | deux sections, « 2 livres et 2 revues en envie », « Éditer » sur chacune |
+| Y | manuel, contact, version (« version de développement », dit honnêtement), licence AGPL v3 |
+
+Toutes les routes du client (`@page`) figurent bien dans la table de `RemonteeRoutes` — contrôle
+refait à la main, la table étant explicite et rien ne garantissant en machine qu'on n'en oublie
+pas une.
+
+### ⚠️ Défaut 1 — l'arbre des séries débordait de 8 px à 320 px
+
+`.arbre-ligne` est elle-même un **élément de flex** (son `
` l'est), et `min-width: auto` lui
+interdisait de descendre sous sa largeur intrinsèque : un titre de sous-série indenté poussait le
+compte « 0 sur 2 tomes » à 328 px sur un écran de 320.
+
+C'est **mot pour mot la leçon du lot M**, déjà écrite ici — « le point qui fait tenir la ligne à
+320 px n'est pas le `flex` mais `min-width: 0` » — simplement non appliquée à ce composant-là.
+`.arbre-titre` l'avait, la **ligne** ne l'avait pas. La règle vaut à chaque étage de la
+cascade de flex, pas au seul étage où l'on a été mordu.
+
+### ⚠️ Défaut 2 — l'ISBN à tirets faisait défiler toute la fiche
+
+`.code-isbn` portait `white-space: nowrap`. Un ISBN en monospace mesure **147 px**, là où la
+colonne des valeurs de la fiche en offre 111 à 375 px et **56 à 320 px** : le code débordait sa
+colonne, et avec lui la page entière — **395 px de contenu pour un écran de 320**.
+
+⚠️ Deux choses à retenir, au-delà du symptôme :
+
+- **Une règle tardive annulait un garde-fou délibéré.** `.fiche-champs dd` porte `min-width: 0`
+ et `word-break: break-word` *précisément* pour cela ; `nowrap`, plus spécifique, les défaisait
+ sur la seule valeur qui en avait besoin.
+- **Le défaut de CSS était déjà le bon comportement.** Sans `nowrap`, le code tient sur une ligne
+ quand il y a la place (vérifié à 1280 px) et ne se coupe **qu'à un tiret** quand il n'y en a
+ pas — le tiret étant le seul endroit où une coupure se relit. Deux lignes à 375 px, trois à
+ 320 px : c'est le prix, et il est sans commune mesure avec une page qui défile de travers.
+
+Décidé avec l'utilisateur. Les deux autres pistes ont été écartées : donner sa pleine largeur à
+la ligne ISBN **ne suffit pas à 320 px** (144 px disponibles pour 147 nécessaires), et réduire la
+police rendrait illisible, sur le petit écran, un code qu'on y recopie chez le libraire.
+
+### ⚠️ Défaut 3 — le relais de couvertures rendait 404 sur une couverture qui existe
+
+Le plus sérieux des trois, et le seul qu'aucune inspection du code n'aurait donné.
+
+`GET /api/couvertures` partageait le délai de 10 s des sources bibliographiques. Or les
+couvertures OpenLibrary aboutissent sur `archive.org`, qui les **extrait d'une archive ZIP à la
+volée** : mesuré le 2026-08-21, 5,6 s / 14,9 s / 5,5 s en amont, et **8,3 s → 200, 7,3 s → 200,
+10,003 s → 404** à travers le relais. Une fois sur trois, l'image existante était déclarée
+introuvable.
+
+⚠️ **La conséquence n'est pas une image manquante, elle est cumulative** : un 404 signifie que
+rien n'est rangé en IndexedDB, donc que le client rejoue CORS **puis** relais à *chaque* affichage
+de la fiche — exactement le défaut que ce relais existe pour corriger, et que CLAUDE.md notait
+comme réglé (« l'erreur CORS n'apparaît qu'une fois par couverture et par appareil »).
+
+⚠️ **Et il était indistinguable d'une URL inconnue**, par construction : tout refus du relais
+répond 404 pour ne pas faire de lui un oracle. Cette règle reste juste, mais elle a un revers à
+connaître — **le 404 ne prouve rien**, et un défaut du relais ne se diagnostique qu'en le
+chronométrant. C'est la même leçon que « Robert A. Harper » : une source intermittente produit
+exactement les symptômes d'une source lacunaire.
+
+**Correction : un délai propre au relais, `timeoutRelaisCouvertures` = 30 s.** Les deux appels
+n'ont pas le même témoin — un lookup ISBN fait patienter quelqu'un devant un formulaire, d'où les
+10 s et la bascule sur la source suivante ; le relais remplit un cache **en tâche de fond**, et
+abandonner n'y fait rien gagner, cela perd la couverture. La lecture reste bornée à 5 Mio.
+Vérifié après correction : **cinq appels sur cinq en 200**, 49 153 octets.
+
+### Ce que le balayage n'a PAS trouvé
+
+Aucun débordement horizontal sur les vingt-deux routes, aux trois largeurs, après correction.
+Aucune erreur de console autre que les erreurs CORS attendues (première tentative directe avant
+repli sur le relais, documentée). 618 tests toujours au vert.
+
+⚠️ **La base de développement a été garnie pour l'occasion** — un cycle de deux sous-séries et
+cinq tomes, une revue à trois numéros, deux envies de livres et deux de revues. Sans elle, la
+moitié des écrans de la série s'affichait vide et ne prouvait rien. Elle est restée en place :
+le prochain balayage en aura le même besoin.
diff --git a/IDEES.md b/IDEES.md
index 3dd5a83..5364f94 100644
--- a/IDEES.md
+++ b/IDEES.md
@@ -707,11 +707,20 @@ Aux items déjà ouverts plus haut — **A5** (alléger les couvertures), **K1**
couverture), **N2** (stocker l'URL finale après redirections), les **homonymes**, **OpenLibrary
en second rideau** et l'**EAN-2 à confirmer en kiosque** — la série du 2026-08-21 en ajoute trois.
-- **Vérifier en navigateur tous les écrans touchés.** ⚠️ C'est le plus important : **aucun** n'a
- été regardé s'afficher. Formulaire livre réordonné, menu collant et plein écran, flèche de
- retour sur chaque branche, actions en tête des fiches série et revue, édition d'un numéro,
- deux sections de la liste d'envies, page « À propos ». Les services et l'API sont couverts par
- 618 tests ; le rendu ne l'est par rien.
+- ~~**Vérifier en navigateur tous les écrans touchés.**~~ ✅ **Fait le 2026-08-21** — voir
+ `CLAUDE.md`, section « La 7ᵉ série vérifiée en navigateur ». Vingt-deux routes, à 320, 375 et
+ 1280 px. Les huit lots se comportent comme décrit, et **trois défauts** que les 618 tests ne
+ pouvaient pas voir en sont sortis : l'arbre des séries débordait de 8 px à 320 px (`min-width`
+ manquant sur la ligne — la leçon du lot M, non appliquée à cet étage), l'ISBN à tirets faisait
+ défiler toute la fiche (`white-space: nowrap` annulant le garde-fou de `.fiche-champs dd`), et
+ surtout **le relais de couvertures rendait 404 une fois sur trois** sur une couverture qui
+ existe, `archive.org` mettant 5 à 15 s là où le délai coupait à 10.
+
+ ⚠️ Ce que l'exercice a montré au-delà des trois correctifs : **le rendu ne se vérifie pas à
+ l'œil**. Le débordement de 8 px n'était visible que par `scrollWidth`, et le 404 intermittent du
+ relais que par un chronométrage — aucune capture d'écran n'aurait donné l'un ni l'autre. La
+ bonne question n'est pas « est-ce que ça ressemble à quelque chose ? » mais « qu'est-ce que la
+ géométrie et le réseau disent réellement ? ».
- **Réordonner les envies de revues.** Elles ont un `Rang` et une section à elles, mais aucun
écran pour les déplacer — les livres, eux, ont `/souhaits/ordre`. Le besoin n'est pas constaté :
la section est courte par nature. À faire le jour où elle ne l'est plus.
diff --git a/MaBibli.Api/Program.cs b/MaBibli.Api/Program.cs
index 6e39083..c48880d 100644
--- a/MaBibli.Api/Program.cs
+++ b/MaBibli.Api/Program.cs
@@ -36,6 +36,19 @@ builder.Services.AddScoped(http =>
{
http.BaseAddress = new Uri(BnfClient.UrlBase);
@@ -67,7 +80,7 @@ builder.Services.AddScoped();
// le SSRF.
builder.Services.AddHttpClient(http =>
{
- http.Timeout = timeoutSources;
+ http.Timeout = timeoutRelaisCouvertures;
http.DefaultRequestHeaders.UserAgent.ParseAdd("MaBibli/0.1 (bibliotheque personnelle auto-hebergee)");
})
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
diff --git a/MaBibli.Client/wwwroot/css/app.css b/MaBibli.Client/wwwroot/css/app.css
index 26c5c11..39e2cb3 100644
--- a/MaBibli.Client/wwwroot/css/app.css
+++ b/MaBibli.Client/wwwroot/css/app.css
@@ -1080,9 +1080,16 @@ body {
/* Un ISBN se relit chiffre à chiffre : une chasse fixe évite de confondre 1 et l,
0 et O, et rend les tranches séparées par des tirets alignées d'une ligne à l'autre. */
+/* ⚠️ PAS de « white-space: nowrap » ici, et ce n'est pas un oubli. Un ISBN à tirets mesure
+ 147 px en monospace, là où la colonne des valeurs de la fiche n'en offre que 111 à 375 px et
+ 56 à 320 px : « nowrap » le faisait déborder, et avec lui TOUTE la page — 395 px de contenu
+ pour un écran de 320, mesuré le 2026-08-21. Il annulait au passage le « word-break » dont
+ « .fiche-champs dd » est justement muni pour cela.
+ Le défaut de CSS est exactement ce qu'on veut : le code tient sur une seule ligne quand il y
+ a la place, et ne se coupe qu'à un tiret quand il n'y en a pas — le tiret étant le seul
+ endroit où une coupure se relit. */
.code-isbn {
font-family: ui-monospace, "SFMono-Regular", "Menlo", "Consolas", monospace;
- white-space: nowrap;
}
/* Titre d'écran et son bouton de réglages sur la même ligne : le bouton « Filtrer » du
@@ -1173,6 +1180,11 @@ body {
gap: 0.5rem;
min-height: 2.25rem;
padding: 0.2rem 0;
+ /* ⚠️ Même piège qu'au lot M : la ligne est elle-même un élément de flex (son
l'est),
+ et « min-width: auto » lui interdit de descendre sous sa largeur intrinsèque. Sans cette
+ ligne, un titre de sous-série indenté pousse le compte « 0 sur 2 tomes » hors de l'écran
+ et fait défiler la page horizontalement (mesuré à 320 px : 328 px de large). */
+ min-width: 0;
}
.arbre-plier,