/*
  Feuille unique, sans framework. La palette est celle de la marque
  (tailwind.config.js) ; les contrastes retenus tiennent le AA sur fond clair —
  d'où `teal-700` pour le texte et le `teal` plein réservé aux aplats.
*/

:root {
  --navy: #00163d;
  --navy-deep: #0a192c;
  --navy-100: #e6ebf2;
  --navy-50: #f4f7fa;
  --teal: #009ea7;
  --teal-700: #00727a;
  --teal-100: #e0f3f4;
  --orange: #ef8200;
  --danger: #b42318;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--navy-50);
  color: var(--navy);
  font: 15px/1.55 system-ui, -apple-system, "Segoe UI", sans-serif;
}

a { color: var(--teal-700); }

/* ------------------------------------------------------------ structure -- */

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.9rem 1.5rem;
  background: var(--navy);
  color: #fff;
}

.brand { color: #fff; font-weight: 800; letter-spacing: -0.01em; text-decoration: none; }
.brand span { color: var(--teal); font-weight: 600; }

.page { max-width: 68rem; margin: 0 auto; padding: 2rem 1.5rem 4rem; }

h1 { font-size: 1.6rem; letter-spacing: -0.02em; margin: 0 0 1.25rem; }
h2 { font-size: 1.05rem; margin: 1.75rem 0 0.6rem; }

.card {
  background: #fff;
  border: 1px solid var(--navy-100);
  border-radius: 12px;
  padding: 1.5rem;
  margin-bottom: 1.5rem;
  box-shadow: 0 1px 2px rgba(0, 22, 61, 0.06);
}

.card--narrow { max-width: 24rem; margin: 3rem auto; }
.card-head { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.card-head h1 { margin: 0 0.4rem 0 0; }

/* --------------------------------------------------------- formulaires -- */

.stack { display: grid; gap: 1rem; }
.stack label { display: grid; gap: 0.35rem; font-weight: 600; font-size: 0.9rem; }

input, select, textarea {
  font: inherit;
  padding: 0.55rem 0.7rem;
  border: 1px solid #c8d2e0;
  border-radius: 8px;
  background: #fff;
  color: inherit;
  font-weight: 400;
}

input:focus-visible, select:focus-visible, textarea:focus-visible, .btn:focus-visible {
  outline: 2px solid var(--teal-700);
  outline-offset: 2px;
}

textarea { resize: vertical; }

.btn {
  display: inline-block;
  padding: 0.55rem 1.1rem;
  border: 1px solid var(--navy);
  border-radius: 8px;
  background: var(--navy);
  color: #fff;
  font: inherit;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}

.btn--ghost { background: #fff; color: var(--navy); }

.link {
  border: 0;
  background: none;
  padding: 0;
  color: inherit;
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}

.link--danger { color: var(--danger); font-size: 0.9rem; }

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  align-items: center;
  margin-bottom: 1.25rem;
}

.filters input[name="q"] { min-width: 15rem; flex: 1; }
.check { display: flex; align-items: center; gap: 0.35rem; font-size: 0.9rem; }

/* --------------------------------------------------------------- listes -- */

.counters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  list-style: none;
  margin: 0 0 1.25rem;
  padding: 0;
}

.counters a {
  display: block;
  padding: 0.5rem 0.85rem;
  background: #fff;
  border: 1px solid var(--navy-100);
  border-radius: 999px;
  color: var(--navy);
  text-decoration: none;
  font-size: 0.9rem;
}

.counters b { color: var(--teal-700); }

.table {
  width: 100%;
  border-collapse: collapse;
  background: #fff;
  border: 1px solid var(--navy-100);
  border-radius: 12px;
  overflow: hidden;
}

.table th, .table td { padding: 0.7rem 0.9rem; text-align: left; border-bottom: 1px solid var(--navy-100); }
.table th { background: var(--navy-50); font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.05em; }
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background: var(--teal-100); }

/* Une demande jamais traitée se repère sans lire la colonne « Statut ». */
.is-new td:first-child { box-shadow: inset 3px 0 0 var(--orange); }

.nowrap { white-space: nowrap; }
.muted { color: #4a5a72; }
.small { font-size: 0.8rem; word-break: break-word; }
.empty { padding: 2.5rem; text-align: center; color: #4a5a72; background: #fff; border-radius: 12px; }

.tag {
  display: inline-block;
  padding: 0.15rem 0.6rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 700;
  background: var(--navy-100);
}

.tag--nouveau { background: #ffe8cc; color: #8a4b00; }
.tag--en-cours { background: var(--teal-100); color: var(--teal-700); }
.tag--traite { background: #dcf3e3; color: #1a6b39; }
.tag--archive { background: var(--navy-100); color: #4a5a72; }
.tag--spam { background: #fde2e0; color: var(--danger); }

.pager { display: flex; gap: 1rem; align-items: center; justify-content: center; margin-top: 1.25rem; }
.back { margin: 0 0 1rem; }

.facts { display: grid; grid-template-columns: 10rem 1fr; gap: 0.4rem 1rem; margin: 1.25rem 0 0; }
.facts dt { font-weight: 700; font-size: 0.85rem; }
.facts dd { margin: 0; }

/* `pre-wrap` : les retours à la ligne du visiteur sont porteurs de sens. */
.message { white-space: pre-wrap; background: var(--navy-50); padding: 1rem; border-radius: 8px; }

.alert {
  padding: 0.7rem 0.9rem;
  border-radius: 8px;
  background: #fde2e0;
  color: var(--danger);
  font-size: 0.9rem;
}

@media (max-width: 640px) {
  .facts { grid-template-columns: 1fr; }
  .facts dd { margin-bottom: 0.5rem; }
  /* Masque « Formulaire », déductible du reste de la ligne. Restreint au
     tableau des demandes : la même colonne porte le nom du produit dans le
     catalogue, et l'y masquer rendrait la liste illisible. */
  .table--demandes th:nth-child(2), .table--demandes td:nth-child(2) { display: none; }
}

/* ============================================================== contenu == */

.onglets { display: flex; gap: 0.25rem; margin-right: auto; margin-left: 1.5rem; }

.onglets a {
  padding: 0.35rem 0.85rem;
  border-radius: 999px;
  color: #c3cede;
  text-decoration: none;
  font-size: 0.9rem;
  font-weight: 600;
}

.onglets a[aria-current="page"] { background: rgba(255, 255, 255, 0.12); color: #fff; }

.page--large { max-width: 84rem; }

.bandeau {
  padding: 0.85rem 1.1rem;
  border-radius: 10px;
  margin-bottom: 1.5rem;
  font-size: 0.95rem;
}

.bandeau--ok { background: #dcf3e3; color: #14532d; }
.bandeau--bloque { background: #fde2e0; color: var(--danger); }

.aide { color: #4a5a72; font-size: 0.85rem; margin: 0 0 0.9rem; }
.liste-simple { margin: 0; padding-left: 1.1rem; }
.liste-simple li { margin-bottom: 0.35rem; }

/* -------------------------------------------------------------- pastilles -- */

.tag--ok { background: #dcf3e3; color: #1a6b39; }
.tag--ul { background: var(--teal-100); color: var(--teal-700); }
.tag--masque { background: var(--navy-100); color: #4a5a72; }

/* Orange et non rouge : un champ vide n'est pas une faute, c'est un travail
   en cours. Le rouge est réservé à ce qui empêche de publier. */
.tag--manque { background: #ffe8cc; color: #8a4b00; }

.est-masque td:first-child { box-shadow: inset 3px 0 0 #94a3b8; }
.est-masque td:nth-child(2) { opacity: 0.6; }
.est-bloquant td:first-child { box-shadow: inset 3px 0 0 var(--danger); }
.est-avertissement td:first-child { box-shadow: inset 3px 0 0 var(--orange); }

/* ------------------------------------------------------------ formulaires -- */

.grille { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem; }
.grille label { display: grid; gap: 0.35rem; font-weight: 600; font-size: 0.9rem; }
.grille .large { grid-column: 1 / -1; }

/*
  Les deux langues côte à côte, ligne à ligne. Le point important est l'en-tête
  collant : sur une fiche de quarante champs, sans lui, on perd de vue laquelle
  des deux colonnes on est en train de remplir.
*/
.bilingue { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem 1.25rem; }

.bilingue > .entete {
  position: sticky;
  top: 0;
  z-index: 1;
  background: #fff;
  padding: 0.45rem 0;
  border-bottom: 2px solid var(--navy-100);
  font-weight: 700;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.bilingue label { display: grid; gap: 0.3rem; }
.bilingue .libelle { font-weight: 600; font-size: 0.82rem; color: #4a5a72; }

/* Un champ vide se repère sans lire, dans l'une ou l'autre colonne. */
.manque { border-color: var(--orange); background: #fffaf3; }

/* ---------------------------------------------------------------- lignes -- */

/* Cinq colonnes de saisie ne tiennent pas sous 60 rem : on fait défiler le
   tableau seul, plutôt que la page entière en travers. */
.defilable { overflow-x: auto; margin-bottom: 0.5rem; }

.table--lignes { table-layout: auto; }
.table--lignes td { padding: 0.3rem 0.4rem; vertical-align: middle; }
.table--lignes th { padding: 0.5rem 0.4rem; }
.table--lignes input { width: 100%; min-width: 9rem; padding: 0.4rem 0.5rem; }
.table--lignes tbody tr:hover { background: transparent; }

.col-rang { width: 4.5rem; }
.col-ops { width: 7rem; }
.rang-champ { min-width: 0 !important; width: 3.2rem !important; text-align: center; }
.ligne-neuve td { background: var(--navy-50); }
.ligne-neuve input::placeholder { color: #8a97ab; }

.rang {
  border: 1px solid #c8d2e0;
  background: #fff;
  border-radius: 6px;
  padding: 0.2rem 0.45rem;
  font: inherit;
  line-height: 1;
  cursor: pointer;
}

.rang:disabled { opacity: 0.35; cursor: default; }
.rang--danger { color: var(--danger); border-color: #eab8b4; }
.reprendre { font-size: 0.85rem; color: #4a5a72; margin: 0 0 0.5rem; }
.reprendre select { padding: 0.3rem 0.45rem; }

/* La barre d'enregistrement suit le défilement : sur une fiche longue, avoir à
   remonter pour valider est le meilleur moyen de perdre une saisie. */
.barre-enregistrer {
  position: sticky;
  bottom: 0;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.9rem 1.25rem;
  background: #fff;
  border: 1px solid var(--navy-100);
  border-radius: 12px;
  box-shadow: 0 -2px 10px rgba(0, 22, 61, 0.08);
}

code {
  background: var(--navy-50);
  padding: 0.1rem 0.35rem;
  border-radius: 4px;
  font-size: 0.85em;
}

/* --------------------------------------------------------- publication -- */

.tag--en-cours { background: #ffe8cc; color: #8a4b00; }
.tag--reussie { background: #dcf3e3; color: #1a6b39; }
.tag--echouee { background: #fde2e0; color: var(--danger); }

.journal,
.diff {
  background: var(--navy-deep);
  color: #d7e0ec;
  padding: 0.9rem 1.1rem;
  border-radius: 8px;
  overflow-x: auto;
  font: 12.5px/1.6 ui-monospace, "Cascadia Mono", Consolas, monospace;
  margin: 0;
  /* `pre-wrap` sur le journal seulement : un diff doit garder ses colonnes,
     un message d'erreur de GitHub doit pouvoir se replier. */
  white-space: pre;
}

.journal { white-space: pre-wrap; word-break: break-word; }

.diff span { display: block; }
.diff .d-plus { color: #7ee0a1; }
.diff .d-moins { color: #ff9d94; }
.diff .d-repere { color: #8fb3d9; }

/* ------------------------------------------------- arbre de copie -- */

/* Un groupe ou une liste occupe les deux colonnes : c'est un bloc de sens,
   pas un champ, et le couper en deux le rendrait illisible. */
.pleine-largeur { grid-column: 1 / -1; }

.groupe {
  border: 1px solid var(--navy-100);
  border-radius: 10px;
  padding: 0.4rem 1rem 1rem;
  margin: 0.5rem 0 1rem;
  min-width: 0; /* sans quoi un fieldset refuse de rétrécir dans une grille */
}

.groupe > legend {
  padding: 0 0.5rem;
  font-weight: 700;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--teal-700);
}

/* Sans légende, le cadre n'entoure plus rien de nommé : il devient du bruit. */
.groupe--nu { border: 0; padding: 0; margin: 0; }

.bilingue--simple { grid-template-columns: 1fr; max-width: 42rem; }

.item-liste {
  border-left: 3px solid var(--navy-100);
  padding: 0.3rem 0 0.3rem 0.9rem;
  margin-bottom: 1rem;
}

.item-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.4rem;
}

.rang-etiquette {
  display: inline-block;
  min-width: 1.6rem;
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  background: var(--navy-100);
  font-size: 0.75rem;
  font-weight: 700;
  text-align: center;
}

.bilingue .aide { margin: -0.4rem 0 0.4rem; }
.bilingue label .aide { margin: 0; }

/* Un formulaire par bouton dans une cellule de tableau : il ne doit pas
   pousser la ligne à la ligne suivante. */
.en-ligne { display: inline; }

.cases { display: flex; flex-wrap: wrap; align-items: center; gap: 1.25rem; margin: 1rem 0 0; }
.cases .link--danger { margin-left: auto; }

/* --- Vignettes des visuels ------------------------------------------------
 *
 * Servies par le site en ligne, jamais par le backend : voir cms/apercus.py.
 * D'où la boîte fixe et le `contain` — les rendus produit, les photos
 * d'installation et la plaque de certification n'ont pas le même format, et
 * une vignette qui change de taille d'une ligne à l'autre rendrait la liste
 * illisible.
 *
 * Fond clair plutôt que blanc : les rendus produit sont détourés sur fond
 * transparent, et plusieurs batteries sont blanches. */
.visuel { display: flex; align-items: flex-start; gap: 1rem; }
.visuel__corps { flex: 1; min-width: 0; }

.vignette {
  flex: none;
  box-sizing: border-box;
  width: 7rem;
  height: 7rem;
  padding: 0.3rem;
  border: 1px solid var(--navy-100);
  border-radius: 8px;
  background: var(--navy-50);
  object-fit: contain;
}

/* Visuel pas encore publié : ses dérivés ne sont pas en ligne, donc absents du
   manifeste. On le dit, plutôt que de laisser un cadre d'image cassée. */
.vignette--absente {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.5rem;
  color: #64748b;
  font-size: 0.7rem;
  line-height: 1.3;
  text-align: center;
}

.table .vignette { width: 4.5rem; height: 4.5rem; }

@media (max-width: 40rem) {
  .visuel { flex-direction: column; }
}

@media (max-width: 60rem) {
  .bilingue { grid-template-columns: 1fr; }
  /* Une seule colonne : les en-têtes « Français » / « English » n'ont plus de
     sens, le libellé de chaque champ suffit à se repérer. */
  .bilingue > .entete { display: none; }
  .grille { grid-template-columns: 1fr; }
  .onglets { margin-left: 0; }
}

/* ============================================================ visiteurs == */
/*
  La section de fréquentation. Trois familles seulement — les tuiles chiffrées,
  les barres, et quelques pastilles — parce que tout le reste (`.table`,
  `.filters`, `.pager`, `.card`, `.facts`) sert déjà ailleurs et n'avait aucune
  raison d'être redéfini.

  Une teinte unique pour tous les graphiques : le teal de la marque. Ces
  graphiques répondent tous à « lequel est le plus gros », c'est-à-dire à une
  magnitude, qui se code par la longueur. Une couleur par série coderait une
  identité que personne ne cherche — et la palette de marque ne tient de toute
  façon pas comme jeu catégoriel : navy-700 et teal-700 ne se distinguent pas
  l'un de l'autre en vision déficiente, et l'orange tombe à 2,6:1 sur fond clair.
*/

.entete-ecran {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}

.entete-ecran h1 { margin-bottom: 0; }

/* Navigation interne à la section. Distincte de `.onglets`, qui vit dans la
   barre sombre du haut : ici le fond est clair, et le contraste s'inverse. */
.onglets-secondaires, .periodes {
  display: flex;
  gap: 0.25rem;
  flex-wrap: wrap;
}

.onglets-secondaires a, .periodes a {
  padding: 0.35rem 0.8rem;
  border-radius: 999px;
  color: #4a5a72;
  font-size: 0.9rem;
  font-weight: 600;
  text-decoration: none;
}

.onglets-secondaires a:hover, .periodes a:hover { background: var(--navy-100); }

.onglets-secondaires a[aria-current], .periodes a[aria-current] {
  background: var(--navy);
  color: #fff;
}

.periodes { margin: 1.25rem 0 0; }

/* ------------------------------------------------------------- icônes -- */

/* `currentColor` : la même icône suit le teal d'une tuile et le gris d'une
   ligne de tableau, sans être redéclinée. */
.ico {
  width: 1.25rem;
  height: 1.25rem;
  flex: none;
  vertical-align: -0.2em;
}

.ico--petit { width: 0.95rem; height: 0.95rem; }

/* --------------------------------------------------------- tuiles -- */

.tuiles {
  display: grid;
  /* 12 rem et non 11 : « 17 min 50 s » passait à la ligne entre le nombre et
     son unité, ce qui se lisait « 17 min 50 » puis « s ». */
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: 1rem;
  margin: 1.5rem 0 2rem;
}

.tuile {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 1.1rem 1.2rem;
  border: 1px solid var(--navy-100);
  border-radius: 12px;
  background: #fff;
}

.tuile-ico { color: var(--teal-700); }

.tuile-valeur {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 1.9rem;
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.02em;
  white-space: nowrap;
}

.tuile-libelle { color: #4a5a72; font-size: 0.85rem; font-weight: 600; }
.tuile-note { color: #64748b; font-size: 0.75rem; }

.tuile--direct { border-color: var(--teal); }

/* Réservée à ce qui change vraiment sous les yeux. L'étendre à autre chose lui
   ferait perdre sa seule fonction : signaler que la page est vivante. */
.pastille {
  width: 0.55rem;
  height: 0.55rem;
  flex: none;
  border-radius: 50%;
  background: #16a34a;
  box-shadow: 0 0 0 0 rgba(22, 163, 74, 0.55);
  animation: battement 2s ease-out infinite;
}

@keyframes battement {
  70%  { box-shadow: 0 0 0 0.5rem rgba(22, 163, 74, 0); }
  100% { box-shadow: 0 0 0 0 rgba(22, 163, 74, 0); }
}

/* Éteinte : plus de battement reçu depuis plus d'un intervalle. Le visiteur est
   peut-être déjà parti, et la pastille verte de ses voisins ne doit pas laisser
   croire le contraire. */
.pastille--muette { background: #94a3b8; animation: none; box-shadow: none; }

/* Une animation en boucle sur une page qui se recharge toute seule est
   exactement ce que ce réglage système demande de calmer. */
@media (prefers-reduced-motion: reduce) {
  .pastille { animation: none; }
}

/* ------------------------------------------------------------ blocs -- */

.bloc {
  margin: 1.5rem 0;
  padding: 1.25rem 1.4rem;
  border: 1px solid var(--navy-100);
  border-radius: 12px;
  background: #fff;
}

.bloc-titre {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 1rem;
  color: var(--navy);
  font-size: 1rem;
}

.bloc-titre .ico { color: var(--teal-700); }

.colonnes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
  gap: 0 1rem;
}

/* --------------------------------------------------- barres horizontales -- */

/* Un vrai tableau, et non un SVG : le chiffre reste sélectionnable, la lecture
   d'écran fonctionne sans description alternative, et la « vue tableau » qu'on
   doit à toute visualisation accessible est alors le graphique lui-même. */
.table--barres { table-layout: auto; border: 0; }
.table--barres td, .table--barres th { border: 0; padding: 0.3rem 0.5rem 0.3rem 0; }
.table--barres tbody tr:hover { background: transparent; }

/* Les libellés de classement sont des `th`, mais ce ne sont pas des en-têtes de
   colonne : ce sont des données. La casse forcée et le fond gris de `.table th`
   sont donc à défaire — « /PRODUITS/S48200/ » n'est pas l'adresse réelle de la
   page, et le crier n'aide personne. */
/* Sélecteur à deux classes, et c'est nécessaire : `.table th` porte une classe
   et un élément, ce qui l'emporterait sur un `.barre-cle` seul — les libellés
   restaient en majuscules sur fond gris malgré la règle. */
.table--barres .barre-cle {
  max-width: 14rem;
  overflow: hidden;
  background: transparent;
  color: var(--navy);
  font-size: 0.85rem;
  font-weight: 500;
  letter-spacing: 0;
  text-align: left;
  text-overflow: ellipsis;
  text-transform: none;
  white-space: nowrap;
}

.barre-piste { width: 100%; min-width: 4rem; }

.barre {
  display: block;
  height: 0.6rem;
  min-width: 2px;
  border-radius: 4px;
  background: var(--teal);
}

/* La valeur est écrite à côté de chaque barre : lire le graphique ne doit pas
   exiger de survoler quoi que ce soit. */
.barre-valeur {
  color: var(--navy);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  text-align: right;
  white-space: nowrap;
}

/* ----------------------------------------------------- barres verticales -- */

/* Le seul graphique en SVG : une suite temporelle se lit horizontalement, ce
   qu'un tableau ne rend pas. */
.graphe { width: 100%; height: var(--hauteur, 120px); }
.graphe svg { width: 100%; height: 100%; overflow: visible; }

.barre-v { fill: var(--teal); }

/* Un jour sans visite garde sa place, en trait fantôme : le retirer ferait
   mentir la forme de la période. */
.barre-v--vide { fill: var(--navy-100); }

.axe {
  display: flex;
  justify-content: space-between;
  margin: 0.4rem 0 0;
  color: #64748b;
  font-size: 0.75rem;
}

/* ---------------------------------------------------------- pastilles -- */

.tag--ordinateur { background: var(--navy-100); color: #33415c; }
.tag--mobile { background: var(--teal-100); color: var(--teal-700); }
.tag--tablette { background: #ede9fe; color: #5b21b6; }
.tag--neutre { background: var(--navy-100); color: #4a5a72; }
.tag--robot { background: #ffe8cc; color: #8a4b00; }
.tag--en-ligne { background: #dcf3e3; color: #14532d; }

.tag .ico { vertical-align: -0.15em; }

@media (max-width: 60rem) {
  /* L'adresse IP et le fournisseur sont ce qu'on regarde en dernier : sur un
     téléphone, ils poussent la page vue hors de l'écran. Le parcours du
     visiteur, lié depuis la ligne, les porte toujours. */
  .table--direct .colonne-large,
  .table--historique .colonne-large { display: none; }
}

/* La barre du haut porte désormais trois onglets et le bouton de déconnexion.
   Sous 30 rem, la somme dépasse la largeur d'un téléphone et poussait le bouton
   hors de l'écran — mesuré à 59 px de débordement horizontal. Le retour à la
   ligne coûte une rangée de haut et rend la barre entièrement atteignable. */
@media (max-width: 30rem) {
  .topbar { flex-wrap: wrap; gap: 0.5rem 0.75rem; padding: 0.75rem 1rem; }
  .onglets { order: 3; width: 100%; margin-left: 0; }
}
