/* ══════════════════════════════════════════════════════════════════════
   ADAPTATION AUX PETITS ÉCRANS
   ──────────────────────────────────────────────────────────────────────
   Cette feuille est chargée APRÈS `style.css` (voir `base.html`) et c'est
   sa raison d'être : une requête `@media` n'ajoute aucune spécificité, si
   bien qu'une règle de bureau écrite plus bas dans le même fichier
   écraserait silencieusement l'adaptation mobile. Le desktop irait bien,
   le mobile se dégraderait sans qu'aucun test ne le voie.

   En fichier séparé, l'ordre de cascade tient à l'ordre des <link> : plus
   personne ne PEUT écrire après le mobile, même sans connaître la règle.
   `style.css` reste donc le point d'ajout commun, sans précaution
   particulière à retenir.

   Tout est écrit en `max-width`, et non en « mobile d'abord » : `style.css`
   reste la présentation de bureau, inchangée au caractère près. Le code
   d'ici ne s'active qu'en dessous des seuils, ce qui rend une régression
   sur grand écran structurellement impossible.

   Trois paliers :
     ≤ 899 px  barre de navigation escamotable (mesure : les liens, la
               marque et l'identité demandent ~950 px même sans sous-titre) ;
     ≤ 599 px  téléphone — respiration resserrée, cibles tactiles ;
     ≥ 900 px  l'existant.
   ══════════════════════════════════════════════════════════════════════ */

/* ─── Sous 900 px : le menu passe en panneau escamotable ─────────────── */
@media (max-width: 899px) {
  /* La barre suit le défilement : la fiche client fait plusieurs dizaines
     d'écrans de haut, la navigation serait autrement hors de portée. */
  .topbar {
    position: sticky; top: 0; z-index: 50;
    flex-wrap: wrap; align-items: center; gap: 8px;
    box-shadow: 0 2px 8px rgba(40, 26, 22, .18);
  }
  /* Le sous-titre est du décor et coûte une ligne entière. */
  .sub { display: none; }

  /* Case à cocher masquée visuellement mais focalisable au clavier —
     même idiome que la bascule montant/bouteilles du radar. */
  .nav-bascule { display: block; position: absolute; opacity: 0; width: 0; height: 0; }
  .nav-hamburger {
    display: flex; align-items: center; justify-content: center;
    margin-left: auto; min-width: 44px; min-height: 44px;
    font-size: 1.5rem; line-height: 1; color: #fff; cursor: pointer;
    border-radius: 8px; user-select: none;
  }
  .nav-bascule:focus-visible ~ .nav-hamburger { outline: 2px solid #f6d97a; outline-offset: 2px; }
  .nav-fermer { display: none; }
  .nav-bascule:checked ~ .nav-hamburger .nav-ouvrir { display: none; }
  .nav-bascule:checked ~ .nav-hamburger .nav-fermer { display: inline; }

  /* Un seul balisage de liens sert les deux présentations : replié ici,
     en ligne au-dessus de 900 px. Rien à maintenir en double. */
  .nav { display: none; }
  .nav-bascule:checked ~ .nav {
    display: flex; flex-direction: column; align-items: stretch;
    flex-basis: 100%; gap: 0; margin: 6px calc(-1 * var(--pad-x)) -14px;
    max-height: calc(100vh - 120px); overflow-y: auto;
    background: rgba(0, 0, 0, .12);
  }
  .nav-bascule:checked ~ .nav a {
    display: flex; align-items: center; min-height: 44px;
    padding: 0 var(--pad-x); font-size: .95rem; opacity: 1;
    border-top: 1px solid rgba(255, 255, 255, .14);
  }
  /* L'identité et la déconnexion ne sont pas de la navigation :
     un filet plus marqué les détache en pied de panneau. */
  .nav-bascule:checked ~ .nav .who {
    display: flex; align-items: center; min-height: 40px;
    padding: 0 var(--pad-x); margin-top: 6px;
    border-top: 2px solid rgba(255, 255, 255, .32);
  }
}

/* ─── Sous 900 px : les tableaux cessent de pousser la page ──────────── */
@media (max-width: 899px) {
  /* Chaque tableau du projet vit dans une enveloppe `.table-wrap` : c'est
     ELLE qui défile, plus la page. Sans cela, un tableau plus large que
     l'écran fait glisser tout le document latéralement — le symptôme
     « le contenu dépasse ». Un test verrouille la présence de l'enveloppe
     sur chaque `<table>` des gabarits.

     Le cadre (bordure, arrondi, fond blanc) descend du tableau vers
     l'enveloppe : c'est ce qui permet aux deux voiles blancs de défiler
     AVEC le contenu (`local`) en découvrant les ombres, restées fixées au
     cadre (`scroll`). Le dégradé n'apparaît donc que du côté où il reste
     quelque chose à lire — aucune ligne de JavaScript. */
  .table-wrap {
    overflow-x: auto;
    overscroll-behavior-x: contain;
    border: 1px solid #e7e0d8;
    border-radius: 10px;
    background:
      linear-gradient(to right, #fff 30%, rgba(255, 255, 255, 0)) left center / 32px 100% no-repeat local,
      linear-gradient(to left, #fff 30%, rgba(255, 255, 255, 0)) right center / 32px 100% no-repeat local,
      radial-gradient(farthest-side at 0 50%, rgba(43, 43, 43, .16), rgba(43, 43, 43, 0)) left center / 13px 100% no-repeat scroll,
      radial-gradient(farthest-side at 100% 50%, rgba(43, 43, 43, .16), rgba(43, 43, 43, 0)) right center / 13px 100% no-repeat scroll,
      #fff;
  }
  .table-wrap > .grid { border: 0; border-radius: 0; background: transparent; }
  /* `.grid.totaux` est le seul tableau à largeur automatique : sans ceci il
     flotterait, étroit, dans une enveloppe pleine largeur. */
  .grid.totaux { width: 100%; }
  /* `.nu` : l'enveloppe n'apporte que le défilement. La matrice du profil
     d'achat porte déjà son propre quadrillage, un cadre de plus par-dessus
     ressemblerait à une erreur. */
  .table-wrap.nu { border: 0; border-radius: 0; background: none; }

  /* Le `<colgroup>` de l'écran Tâches fixe 640 px de colonnes en `style`
     inline, qu'aucune requête média ne peut surcharger. `table-layout: auto`
     rend le colgroup inopérant sans toucher au gabarit. */
  .grid.fixed { table-layout: auto; }
}

/* ─── Sous 600 px : téléphone ────────────────────────────────────────── */
@media (max-width: 599px) {
  :root { --pad-x: 14px; --marge-v: 18px; }
  .topbar { padding-top: 10px; padding-bottom: 10px; }
  .brand { font-size: .95rem; letter-spacing: .5px; }
  h1 { font-size: 1.5rem; }

  /* Une adresse e-mail ou un nom de vin ne se coupe nulle part : sans ceci,
     une seule cellule élargit tout le tableau. Seul `.grid.fixed td` le
     portait jusqu'ici. */
  .grid td { overflow-wrap: anywhere; }
  .regle { white-space: normal; }

  /* ── Mise en carte ──────────────────────────────────────────────────
     Un tableau de six colonnes qu'on fait défiler au doigt reste pénible :
     les écrans du métier passent en cartes, une par ligne. Le libellé de
     chaque cellule est lu dans son attribut `data-label` — c'est le coût
     réel du chantier, une soixantaine d'attributs posés dans les gabarits.

     `display: block` sur les éléments de tableau efface la sémantique de
     tableau des navigateurs. Ce n'est pas une perte ici : le libellé
     devient du texte visible devant sa valeur, donc lisible par tous, là
     où un en-tête de colonne détaché n'aurait plus rien à quoi se
     rattacher. Le `<thead>` est masqué en CSS, JAMAIS retiré du gabarit —
     des tests lisent ses `<th>`. */
  .table-wrap:not(.defile) { border: 0; border-radius: 0; background: none; }
  .grid.cartes { border: 0; background: transparent; }
  .grid.cartes thead { display: none; }
  .grid.cartes, .grid.cartes tbody, .grid.cartes tr, .grid.cartes td { display: block; }
  .grid.cartes tr {
    display: flex; flex-direction: column;
    margin: 0 0 10px; padding: 10px 12px;
    background: #fff; border: 1px solid #e7e0d8; border-radius: 10px;
  }
  .grid.cartes tr.suite { background: #fbf8f4; }
  .grid.cartes td { border: 0; padding: 2px 0; }
  .grid.cartes td[data-label] { display: flex; gap: 10px; align-items: baseline; }
  .grid.cartes td[data-label]::before {
    content: attr(data-label);
    flex: 0 0 6.8em;
    font-size: .72rem; text-transform: uppercase; letter-spacing: .4px;
    color: #8a8078; line-height: 1.6;
  }
  /* La cellule de titre remonte en tête de carte quelle que soit sa place
     dans la ligne : sur l'écran Tâches le client est en 3e colonne, et
     c'est pourtant lui qu'on cherche en premier. */
  .grid.cartes td.titre { order: -1; padding-bottom: 6px; font-size: 1.02rem; font-weight: 600; }
  .grid.cartes td.titre::before { content: none; }
  /* `style.css` remet `.num` en `table-cell` aligné à droite : dans une
     carte, la valeur suit son libellé comme les autres. */
  .grid.cartes td.num { text-align: left; white-space: nowrap; }
  /* Les actions occupent la largeur de la carte, sous une ligne de séparation. */
  .grid.cartes td.actions { margin-top: 8px; padding-top: 8px; border-top: 1px solid #efe9e1; }
  .grid.cartes td.actions::before { content: none; }
  .grid.cartes .row-actions, .grid.cartes .inline-form { display: flex; flex-wrap: wrap; gap: 8px; }
  .grid.cartes .row-actions form, .grid.cartes .inline-form { width: 100%; }
  .grid.cartes .row-actions button, .grid.cartes .inline-form button { width: 100%; }
  .grid.cartes .inline-form input { flex: 1 1 100%; }

  /* ── Traitement C : les tableaux qui doivent rester des tableaux ───── */
  /* La synthèse 360 est une addition, avec une ligne de somme et une ligne
     d'avoirs en négatif. En cartes, la lecture verticale qui en fait tout
     l'intérêt disparaîtrait. */
  .grid.totaux { font-size: .9rem; }
  .grid.totaux th, .grid.totaux td { padding: 3px 0 3px 10px; }
  .grid.totaux .num { font-variant-numeric: tabular-nums; }
  /* Même raison pour la grille 5 × 5 du profil d'achat : c'est une matrice,
     chaque case ne se lit qu'en croisant sa ligne et sa colonne. */
  .grille-tab { font-size: .78rem; }
  .grille-tab th, .grille-tab td { padding: 4px 5px; }
}

/* ─── Sous 600 px : les formulaires (chantier 3) ──────────────────────── */
@media (max-width: 599px) {
  /* ── Le zoom automatique de Safari iOS ──────────────────────────────
     Sous 16 px, iOS agrandit la page à la mise au point d'un champ — et
     n'annule jamais ce zoom : la vue reste agrandie et décalée, la barre
     de navigation sort de l'écran, et l'utilisateur doit pincer pour
     revenir. Mesuré sur les 8 écrans à champs : la plus petite police
     était de 13,33 px partout, y compris là où aucune taille n'est
     déclarée — les contrôles de formulaire n'héritent PAS de la taille du
     document, ils tombent sur le défaut du navigateur.

     16 px est un seuil du moteur, pas une préférence : 15,9 px zoome.
     Les boutons sont volontairement laissés de côté — on ne saisit rien
     dedans, ils ne déclenchent aucun zoom, et les agrandir déplacerait
     des mises en page qui tiennent déjà. */
  input, select, textarea { font-size: 16px; }

  /* Le sélecteur d'élément ci-dessus (0,0,1) ne suffit pas : trois règles
     de `style.css` posent une police sur un contrôle en visant une classe
     (0,1,1) et l'emportent — et une requête `@media` n'ajoute AUCUNE
     spécificité, c'est le piège décrit en tête de ce fichier. La preuve a
     été faite à la mesure : après la règle générale, `comptes` et la fiche
     client restaient à 13,6 px pendant que les six autres écrans passaient
     à 16.

     Elles sont donc reprises nommément, à spécificité égale — et
     comme `mobile.css` est chargée en dernier, l'égalité suffit à gagner.
     L'ensemble est fermé, et `test_formulaires_mobile` le vérifie : toute
     nouvelle règle de police sur un contrôle devra être répercutée ici. */
  .inline-form select, .inline-form input,
  .close-form-lg select, .close-form-lg textarea,
  .head-form select, .head-form input, .head-form textarea { font-size: 16px; }

  /* Un champ ne peut plus dépasser son conteneur : plusieurs largeurs sont
     fixées en dur dans `style.css` (140 px, 90 px, 10 rem) pour un écran de
     bureau, et il ne reste que 292 px utiles à 320 px. */
  input, select, textarea { max-width: 100%; }

  /* ── Panneau de filtres clients ─────────────────────────────────────
     Mesuré : `.filtres-ligne` faisait 375 px de large pour 305 disponibles.
     La ligne enveloppante sait pourtant se replier — c'est le `.champ` qui
     ne le savait pas : `inline-flex` sans repli, il additionnait son
     libellé, ses deux champs et le séparateur sur une seule ligne
     incompressible. Chaque critère occupe désormais sa propre ligne, et
     ses champs se partagent la largeur. */
  .filtres-ligne { flex-direction: column; align-items: stretch; }
  .filtres .champ { display: flex; flex-wrap: wrap; align-items: center; }
  /* Le libellé prend toute la ligne, les deux champs se partagent la
     suivante en encadrant leur séparateur. Sans cela — vu à l'écran — le
     « – » et le « au » restaient orphelins en bout de première ligne
     pendant que le second champ tombait seul en dessous. Le libellé est un
     `<span class="quoi">` dans le gabarit précisément pour être atteignable
     ici : un nœud de texte nu devient un élément flex anonyme, que rien ne
     peut viser. */
  .filtres .champ .quoi { flex: 1 1 100%; }
  .filtres .champ input[type=number],
  .filtres .champ input[type=date] { flex: 1 1 7rem; width: auto; min-width: 0; }
  /* 240 px de largeur minimale sur la recherche : au-dessus de ce que
     laissent les petits téléphones une fois les marges retirées. */
  .search input[type=search] { min-width: 0; }
  .search button { width: 100%; }

  /* ── Création de compte ─────────────────────────────────────────────
     `repeat(2, minmax(200px, 1fr))` réclame 412 px avec sa gouttière, et un
     `minmax` ne se laisse pas comprimer : le débordement était garanti sur
     tout téléphone (mesuré : +144 px à 320 px). Une seule colonne. */
  .form-grid { grid-template-columns: 1fr; }
  .form-grid button { width: 100%; }
  /* Même précaution sur les paramètres de règles, dont la colonne
     minimale (280 px) frôle la largeur utile d'un petit téléphone. */
  .params-form { grid-template-columns: 1fr; }

  /* ── Saisie en tête de fiche client ─────────────────────────────────
     Le panneau flottant (`absolute`, ancré à droite, 320 px) est conçu pour
     ne pas déplacer la mise en page du bureau. Sur un téléphone il n'a
     plus rien à préserver, et un panneau ancré à droite dans un en-tête
     qui se replie est fragile : il coule dans la page, en pleine largeur.
     L'ombre portée est retirée avec le flottement — sans élévation à
     suggérer, elle salissait le bord.

     Toute la chaîne compte, et deux pièges s'y cachent. `.head-actions` est
     une rangée flex dont chaque `<details>` est dimensionné par son bouton :
     poser `width: 100%` sur le seul panneau ne sert à rien, il sortait à
     203 px pour 292 disponibles. C'est la rangée qui doit passer en colonne
     étirée. Et son `flex-wrap: wrap`, hérité de `style.css`, doit tomber avec
     — en colonne, un conteneur qui s'autorise à replier range ses éléments en
     PLUSIEURS colonnes, et les étire à la largeur de leur colonne, pas à
     celle du conteneur. Les deux ont été vus à l'écran, aucun ne se devine. */
  .head-actions {
    flex: 1 1 100%; flex-direction: column;
    flex-wrap: nowrap; align-items: stretch;
  }
  .head-details { position: static; }
  /* `inline-block` laissait trois pastilles de largeurs différentes alignées
     à gauche ; en bloc elles s'accordent aux autres actions pleine largeur
     du téléphone (« Rechercher », « Créer »). */
  .head-details .add-btn { display: block; text-align: center; }
  .head-form {
    position: static; width: 100%; max-width: none;
    margin-top: 8px; box-shadow: none;
  }
  .head-actions, .head-details, .client-head { min-width: 0; }
  .head-form button[type=submit] { width: 100%; padding: 10px 14px; }

  /* ── Liens-boutons ──────────────────────────────────────────────────
     `.btn` interdit la césure pour que « Télécharger le CSV… » ne se coupe
     pas en deux sur un tableau de bord large. Sur un téléphone, la même
     règle faisait dépasser l'écran de 18 px (mesuré sur l'écran Grappe) :
     le texte se replie. */
  .btn { white-space: normal; }
}

/* ─── Sous 600 px : la fiche client (chantier 4) ──────────────────────── */
@media (max-width: 599px) {
  /* ── Les listes « libellé / valeur » ────────────────────────────────
     `grid-template-columns: max-content 1fr` fige la première colonne sur
     le libellé le plus long du bloc, et `max-content` ne se replie jamais :
     la valeur part hors de l'écran, quelle que soit sa propre longueur.

     Mesuré sur une fiche, à 320 px : quatre blocs rendus, deux malades.
     La synthèse des achats prend 215 px de colonne à cause de « Bouteille
     la moins / plus chère » et sort ses 6 valeurs ; le bloc Probance prend
     185 px avec « Dernier signalement spam » et sort ses 12. Identité
     (130 px) et Coordonnées (73 px) tenaient, mais suivent la même règle :
     deux blocs voisins présentés différemment se liraient comme une erreur.

     La colonne des libellés est donc FIXÉE, à la même largeur que les
     cartes du chantier 2 — de petites capitales sur ~78 px : c'est la
     présentation validée à l'œil, et la fiche ne doit pas faire cohabiter
     deux grammaires. Un libellé trop long s'y replie sur deux lignes,
     exactement comme dans une carte.

     ⚠️ La largeur est écrite en `rem`, pas en `em` : dans une carte, les
     6,8 em se résolvent contre la police du libellé (.72 rem → ~78 px),
     alors qu'ici l'unité s'appliquerait à la police de la liste (16 px →
     109 px). 4,9 rem redonne les mêmes 78 px.

     ⛔ Le libellé EMPILÉ au-dessus de sa valeur a été essayé et écarté à
     l'écran : lisible, mais il doublait la hauteur de chaque bloc — 24
     lignes pour le bloc Probance — alors que les valeurs de l'identité
     tiennent en deux caractères (« M. », « fr », « HL »). */
  .kv { grid-template-columns: 4.9rem 1fr; gap: 6px 10px; }
  .kv dt {
    font-size: .72rem; text-transform: uppercase; letter-spacing: .4px;
    line-height: 1.6;
  }
  .kv dd { overflow-wrap: anywhere; }

  /* ── Les étiquettes de vins et d'appellations ───────────────────────
     `.tag` interdit la césure — légitime pour « acheteur » ou « Cercle »,
     ruineux pour un nom de vin complet : « BILLECART SALMON LE RESERVE
     (Champagne) (Champagne) » mesure 351 px sur un écran de 320, et la
     grosse fiche en porte 203. Décision de l'utilisateur du 23/08 :
     le nom se replie plutôt que d'être tronqué — il reste lisible en
     entier, au prix d'un bloc plus haut. */
  .tag { white-space: normal; }

  /* ── Le radar régional ──────────────────────────────────────────────
     La SVG se met bien à l'échelle, mais ses libellés d'axes sont dessinés
     en unités du `viewBox` : 11 px déclarés dans un viewBox de 400 rendu à
     246 px donnent **6,76 px réels** à l'écran. Mesuré, pas estimé — le
     plan tablait sur « moins de 9 px », c'est bien pire, et « Vins du
     monde » en devient illisible.

     La police est donc portée à une valeur qui rende ~11 px effectifs.
     ⚠️ Aucune solution par JavaScript ici : `test_preferences_web.py`
     interdit tout `<script>` dans le bloc `.radar`, et la bascule
     montant/bouteilles repose sur des radios masqués.

     Une SVG rogne ses débordements par défaut (`overflow: hidden`) : une
     fois les libellés grossis, « Autres régions » et « Vins du monde »
     sortent du `viewBox` de 25 et 9 unités, soit ~15 px réels, et
     seraient coupés net. Le `viewBox` est un attribut du gabarit, hors de
     portée d'une requête média ; mais le panneau offre 23 px de marge
     intérieure de chaque côté, largement de quoi accueillir ces 15 px. On
     laisse donc la SVG peindre au-delà de son cadre — un débordement
     PEINT ne participe pas à la mise en page et n'élargit pas la page. */
  .radar-label { font-size: 18px; }
  .radar-graduation { font-size: 15px; }
  .radar-svg { overflow: visible; }

  /* ── Cibles tactiles propres à la fiche ─────────────────────────────
     Mesuré : 7 des 8 `<summary>` font 26 px de haut, et les deux bascules
     (montant/bouteilles, fiches/CA) 24 px — pour 44 px recommandés. Le
     `gap` sur le résumé évite que le chevron ne colle au titre quand
     celui-ci passe sur deux lignes, ce que `space-between` seul ne
     garantit pas. Le reste des cibles du projet relève du chantier 5. */
  .collapsible > summary { min-height: 44px; gap: 12px; }
  .radar-onglets label, .grille-onglets label {
    display: inline-flex; align-items: center;
    min-height: 44px; padding: 0 14px;
  }
}

/* ─── Sous 600 px : ergonomie tactile (chantier 5) ────────────────────── */
@media (max-width: 599px) {
  /* ── Les cibles à 44 px ─────────────────────────────────────────────
     Audit à 320 px sur les onze écrans : douze familles d'éléments
     interactifs sous 44 px, dont les actions de tâche à **19 px** (×67),
     les onglets à 23, les champs à 24 et les listes déroulantes à 31.

     ⛔ Les liens de TEXTE en sont volontairement exclus — 144 occurrences
     mesurées. La règle des 44 px vise les cibles autonomes, pas un lien au
     fil d'une phrase : donner 44 px de haut à chaque nom de client
     déferait la lecture des cartes du chantier 2. Le plan les rangeait
     d'ailleurs par familles de boutons, jamais de liens.

     ⛔ Les `<label>` sont exclus eux aussi : ce sont les intitulés des
     champs, dont la cible réelle est le contrôle de 44 px juste en dessous.
     Les agrandir n'ajouterait que du vide.

     `inline-flex` plutôt qu'un simple `padding` : la hauteur est alors
     garantie même quand le libellé tient sur une ligne courte, et le texte
     reste centré au lieu de coller au bord haut.

     `button` est visé sans distinction, plutôt que famille par famille :
     dans ce projet, tout `<button>` déclenche une action. Une liste de
     classes avait laissé passer le bouton « Importer » de l'écran Grappe,
     qui n'en porte aucune — et laisserait passer le prochain. */
  button,
  .btn, .tel,
  .filters a, .tabs a, .pager a,
  .head-details .add-btn {
    display: inline-flex; align-items: center; justify-content: center;
    min-height: 44px; padding-top: 0; padding-bottom: 0;
  }
  /* Le dépliant « Ciblage par les vins » est cliquable, donc c'est une cible.
     ⚠️ Mesuré à 27 px avant cette règle — et il échappait au contrôle, qui ne
     regardait que les `input` et les `button`. Un `summary` reste en
     `display: list-item` pour garder son marqueur natif : c'est le
     `min-height` qui fait la cible, pas un `inline-flex` qui l'effacerait. */
  .vins > summary { min-height: 44px; display: flex; align-items: center; }
  /* Les contrôles de saisie sont déjà à 16 px depuis le chantier 3 ; il
     leur manquait la hauteur. `select` reste à part : sur mobile, il ouvre
     le sélecteur natif du système, et sa cible est la boîte elle-même. */
  input:not([type=checkbox]):not([type=radio]), select, textarea {
    min-height: 44px;
  }
  /* La popup de rappel garde sa forme — mesurée, elle tient dans 320 px
     sans déborder. Seuls ses cinq boutons étaient à 23 px. Décision de
     l'utilisateur du 23/08 : ⛔ pas de refonte en feuille basse. */
  .remind-shortcuts .btn, .remind-actions .btn { flex: 1 1 auto; }

  /* ── Les infobulles `title=` ────────────────────────────────────────
     Le survol n'existe pas au doigt : le texte de ces attributs est
     purement et simplement ABSENT sur téléphone. Audit : quatre familles
     porteuses de contenu réel — l'aide des règles (×33 sur Tâches), les
     alertes de source (×12), la date d'un report (×9), l'aide de règle sur
     la fiche.

     Affiché par `content: attr(title)`, donc **sans toucher un seul
     gabarit** — et l'attribut reste en place, ce qui satisfait
     `test_taches_web.py:127`, qui vérifie sa présence littérale.

     ⛔ Deux porteurs de `title` sont exclus à dessein : `.back-to-top`,
     dont le titre est un nom accessible et non du contenu, et
     `.barre-seg`, dont le libellé est DÉJÀ affiché juste en dessous dans
     `.radar-valeurs` — l'audit l'avait signalé à tort, son `<span>` étant
     vide. */
  .regle[title] {
    display: block; white-space: normal;
    border-bottom: 0; cursor: auto;
  }
  .regle[title]::after {
    content: attr(title);
    display: block; margin-top: 2px;
    font-size: .78rem; font-weight: 400; color: #8a8077; line-height: 1.4;
  }
  /* Sur une pastille, le complément suit le libellé dans la même forme. */
  .tag[title]::after {
    content: " · " attr(title);
    font-weight: 400;
  }

  /* ⚠️ Corollaire, vu à l'écran : la cellule d'une carte est un conteneur
     flex en `nowrap` (chantier 2). Une pastille allongée y devient un
     élément flex incompressible, et le texte voisin est écrasé — « appel »
     se rendait en « ap / pel », coupé en plein mot par
     l'`overflow-wrap: anywhere`. Le défaut est latent depuis le chantier 2 ;
     la pastille n'a fait que l'exposer. C'est le repli de la cellule qui
     manquait : la pastille descend d'une ligne au lieu d'étouffer ses
     voisins.

     `:has()` restreint le repli aux SEULES cellules portant une pastille
     allongée. Appliqué à toutes, il faisait passer chaque valeur un peu
     longue sous son libellé — une autre présentation que celle validée au
     chantier 2, et pour des cellules qui n'avaient aucun problème. Là où
     `:has()` n'est pas connu, on retrouve simplement le comportement
     d'avant ce chantier. */
  .grid.cartes td[data-label]:has(.tag[title]) { flex-wrap: wrap; row-gap: 4px; }

  /* ── Le bouton « remonter » ─────────────────────────────────────────
     Calé à `right: 48px` pour un écran de bureau, il sortait de 19 px à
     320 px — mesuré : il n'élargit pas la page (`position: fixed`), il est
     simplement rogné. `env(safe-area-inset-bottom)` le tient au-dessus de
     la barre système des iPhone récents, où il serait autrement
     inatteignable. */
  .back-to-top {
    right: 16px;
    bottom: calc(16px + env(safe-area-inset-bottom));
    width: 48px; height: 48px; font-size: 1.5rem;
  }
}

/* ─── La barre de filtre par règle : une bande défilante ───────────────
   ⚠️ Mesuré avant d'écrire cette règle : à 320 px, cette barre passait à
   206 px de haut (cinq lignes empilées) et repoussait la PREMIÈRE tâche à
   811 px — sous la ligne de flottaison d'un écran de 800 px. On ouvrait
   « Tâches » et on ne voyait que des filtres.

   Une seule ligne qui défile horizontalement, donc. `flex-wrap: nowrap`
   seul ne suffit PAS : sans `flex: 0 0 auto`, le conteneur comprime ses
   enfants au lieu de déborder, et les libellés se cassent mot par mot —
   c'est exactement le défaut rencontré au chantier 2 sur les cellules de
   carte. Les deux déclarations vont ensemble.

   Le bureau n'est pas touché : au-delà de 599 px la barre se replie
   normalement sur plusieurs lignes. */
@media (max-width: 599px) {
  .filters.regles {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .filters.regles::-webkit-scrollbar { display: none; }
  .filters.regles > * { flex: 0 0 auto; }
}
