/* ------------------------------------------------------------------ composants
   Une section par composant, dans l'ordre du fichier .js correspondant.
   Aucune valeur brute : si une couleur ou une taille manque, elle s'ajoute
   dans tokens.css, pas ici.                                                  */


/* === Coque ================================================================
   Le gabarit général. Sur téléphone, une colonne avec la barre d'onglets en
   bas ; sur écran, une barre latérale fixe et le contenu à côté.            */

.coque {
  /* dvh et non % : un min-height en pourcentage se résout à zéro quand le
     parent est en hauteur automatique, et le fond s'arrêtait au bas du
     contenu au lieu de descendre jusqu'en bas de l'écran. */
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--fond-2);
  /* Rien ne fait glisser la page de côté, même si un écran rate la règle.
     `clip` et non `hidden` : hidden ferait de la coque un conteneur de
     défilement, et la barre latérale cesserait de coller. */
  overflow-x: clip;
}

.coque__contenu {
  /* Le haut suit la zone sûre : installée sur l'écran d'accueil, l'app passe
     sous la barre d'état, et sans ce retrait le titre allait sous l'heure. */
  padding: env(safe-area-inset-top) var(--marge-page)
           calc(var(--hauteur-onglets) + env(safe-area-inset-bottom) + var(--e-6));
  max-width: var(--largeur-contenu);
  margin: 0 auto;
}

/* Le gabarit du bureau est dans bureau.css, chargé au-delà de 900 px seulement. */


/* === Barre d'onglets (téléphone) ==========================================
   Quatre onglets et un bouton d'action au centre. Le bouton est au centre
   parce qu'il est le geste le plus fréquent et le plus atteignable au pouce. */

.onglets {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  display: flex; align-items: stretch;
  height: calc(var(--hauteur-onglets) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  background: color-mix(in srgb, var(--fond) 88%, transparent);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-top: 1px solid var(--trait);
}

.onglets__lien {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; padding: var(--e-1) 2px;
  color: var(--texte-3);
  transition: color var(--d-1);
}
.onglets__lien.est-actif { color: var(--accent-texte); }
.onglets__lien svg { width: 24px; height: 24px; }
.onglets__libelle {
  /* Douze pixels et non onze : c'est la plus petite taille lisible debout, et
     la seule de l'app sous la legende. */
  font: 500 12px/14px var(--police);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Le badge de la file d'attente : un compte, pas une pastille muette, pour
   qu'on sache combien de saisies attendent sans ouvrir l'écran. */
.onglets__badge {
  position: absolute; top: 4px; left: 50%; margin-left: 4px;
  min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: var(--r-rond);
  background: var(--accent); color: var(--sur-accent);
  font: 700 11px/18px var(--police); text-align: center;
}
.onglets__lien { position: relative; }

.onglets__action {
  flex: 0 0 auto; width: 72px;
  display: flex; align-items: center; justify-content: center;
}
.onglets__bouton {
  width: var(--touche-terrain); height: var(--touche-terrain);
  border-radius: var(--r-rond);
  background: var(--accent); color: var(--sur-accent);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 6px 16px color-mix(in srgb, var(--accent) 35%, transparent);
  transition: transform var(--d-1) var(--courbe);
}
.onglets__bouton:active { transform: scale(.92); }
.onglets__bouton svg { width: 26px; height: 26px; }


/* === En-tête ==============================================================
   La rangée de navigation d'abord (chevron et nom de l'écran d'où l'on vient,
   actions à droite), puis le grand titre sur toute la largeur. Le bouton
   retour ne décale plus le titre. Dès que le grand titre passe sous le haut
   de l'écran, une barre compacte de 60 px se superpose, fixe, avec les
   mêmes trois éléments.

   La barre est hors du flux, et la mise en page ne change jamais au
   défilement : c'est ce qui a arrêté le tremblement de l'en-tête, qui venait
   d'un bloc remplacé par un autre plus court au passage d'un seuil.         */

.entete { padding: var(--e-2) 0 var(--e-3); }
.entete--simple { padding-top: var(--e-5); }
.entete__nav {
  display: flex; align-items: center; justify-content: space-between; gap: var(--e-2);
  min-height: var(--touche);
}
.entete__retour {
  display: inline-flex; align-items: center; gap: 1px;
  min-height: var(--touche); min-width: 0; padding: 0 var(--e-2) 0 0; margin-left: -6px;
  color: var(--texte); font: var(--t-corps);
}
.entete__retour svg { width: 22px; height: 22px; flex: 0 0 auto; }
.entete__retour span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.entete__actions { display: flex; align-items: center; gap: var(--e-1); margin-left: auto; }
.entete__titre {
  font: var(--t-grand-titre); letter-spacing: -.01em;
  overflow-wrap: anywhere;
}
.entete__nav + .entete__titre { margin-top: var(--e-1); }
.entete__sous { font: var(--t-secondaire); color: var(--texte-2); margin-top: 2px; }

.entete__barre {
  position: fixed; top: 0; left: 0; right: 0; z-index: 30;
  padding-top: env(safe-area-inset-top);
  background: color-mix(in srgb, var(--fond-2) 88%, transparent);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid var(--trait);
  /* Invisible ET hors d'atteinte tant qu'on est en haut : une barre
     transparente qui reste tapable volerait les appuis de la rangée de
     navigation qu'elle recouvre. */
  visibility: hidden; opacity: 0;
  transition: opacity var(--d-1), visibility 0s var(--d-1);
}
.entete--collee .entete__barre { visibility: visible; opacity: 1; transition: opacity var(--d-1); }
.entete__barre-dedans {
  /* Soixante pixels, le chiffre de 7.5 : la même hauteur sur tous les écrans,
     avec ou sans bouton retour. Le contenu s'aligne sur la colonne de page. */
  min-height: 60px; max-width: var(--largeur-contenu); margin: 0 auto;
  padding: var(--e-1) var(--marge-page);
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: var(--e-2);
}
.entete__barre-titre {
  font: var(--t-sous-titre); min-width: 0; text-align: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.entete__barre-dedans .entete__actions { justify-content: flex-end; }


/* === Carte ================================================================ */

.carte {
  background: var(--surface);
  border-radius: var(--r-3);
  padding: var(--e-4);
  box-shadow: var(--ombre-1);
}
.carte + .carte { margin-top: var(--e-3); }
.carte--plate { box-shadow: none; border: 1px solid var(--trait); }

.section { margin-top: var(--e-6); }
.section__titre {
  font: var(--t-legende);
  text-transform: uppercase; letter-spacing: var(--interlettre-legende);
  color: var(--texte-2);
  /* Pas de marge a droite : l'action porte son retrait, sans marge negative. */
  margin: 0 0 var(--e-2) var(--e-1);
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--e-3);
}


/* === Liste et ligne =======================================================
   Le gabarit iOS : icône, titre, sous-titre, valeur, chevron. Le filet part
   du texte et non du bord, pour que l'oeil suive la colonne.                 */

.liste { background: var(--surface); border-radius: var(--r-3); overflow: hidden; box-shadow: var(--ombre-1); }

.ligne {
  display: flex; align-items: center; gap: var(--e-3);
  width: 100%; min-height: var(--touche);
  padding: var(--e-3) var(--e-4);
  text-align: left;
  transition: background var(--d-1);
}
.ligne + .ligne { box-shadow: inset 0 1px 0 var(--trait); }
.ligne--cliquable:hover { background: var(--surface-2); }
.ligne--cliquable:active { background: var(--surface-3); }

.ligne__icone { flex: 0 0 auto; color: var(--texte-3); display: flex; }
.ligne__icone svg { width: 20px; height: 20px; }
/* Le ticket lui-même, à la place du pictogramme de ticket. */
.ligne__vignette {
  flex: 0 0 auto; width: 40px; height: 40px; border-radius: var(--r-1);
  overflow: hidden; background: var(--surface-2);
}
.ligne__vignette img { width: 100%; height: 100%; object-fit: cover; }
.ligne__milieu { flex: 1; min-width: 0; }
/* En bloc, et pas seulement stylés : ce sont des span, donc en ligne par
   défaut, et le sous-titre venait se coller au titre sur la même ligne. */
.ligne__titre { display: block; font: var(--t-corps); overflow: hidden; text-overflow: ellipsis; }
.ligne__sous {
  font: var(--t-secondaire); color: var(--texte-2); margin-top: 1px;
  /* Deux lignes au plus : une seule coupait « Jossour L... » sur chaque ligne
     du journal, trois feraient de la liste un paragraphe. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; overflow-wrap: anywhere;
}
.ligne__sous--long { display: block; -webkit-line-clamp: none; }
.ligne__valeur { flex: 0 0 auto; font: var(--t-corps-fort); color: var(--texte); }
.ligne__valeur--douce { color: var(--texte-2); font: var(--t-secondaire); }
.ligne__chevron { flex: 0 0 auto; color: var(--texte-3); display: flex; }
.ligne__chevron svg { width: 18px; height: 18px; }


/* === Résumé d'un chantier =================================================
   L'anneau, une phrase d'état, puis les chiffres côte à côte sur une grille
   qui ne coupe jamais une valeur. Trois tuiles séparées à 390 px coupaient
   le coût en deux et laissaient un tiret pour budget.                       */

.resume {
  background: var(--surface); border-radius: var(--r-3);
  box-shadow: var(--ombre-1); padding: var(--e-4); margin-top: var(--e-3);
}
.resume__tete { display: flex; align-items: center; gap: var(--e-4); }
.resume__texte { flex: 1; min-width: 0; }
.resume__phrase { font: var(--t-corps-fort); }
.resume__aide { font: var(--t-secondaire); color: var(--texte-2); margin-top: 2px; }
/* Les chiffres du résumé (coût, jours, budget) sont dans patron.css. */

/* === Galerie des photos de chantier =======================================
   Une grille serrée par jour, comme la pellicule du téléphone : c'est le
   dessin que tout le monde sait lire, et il montre trois fois plus de photos
   que des tuiles de 88 px espacées.                                          */

.galerie-jour { margin-top: var(--e-4); }
.galerie-jour__titre {
  font: var(--t-legende); text-transform: uppercase;
  letter-spacing: var(--interlettre-legende); color: var(--texte-2);
  margin: 0 var(--e-1) var(--e-2);
}
.galerie {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 3px;
  border-radius: var(--r-2); overflow: hidden;
}
.galerie__tuile {
  position: relative; aspect-ratio: 1; padding: 0; border: 0;
  background: var(--surface-2); overflow: hidden; cursor: pointer;
}
.galerie__tuile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.galerie__ajout {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  background: var(--accent-doux); color: var(--accent-texte);
  font: var(--t-legende); text-align: center; padding: var(--e-2);
}
.galerie__ajout svg { width: 24px; height: 24px; }
@media (min-width: 600px) { .galerie { grid-template-columns: repeat(5, 1fr); } }

/* Le chiffre de tête : un solde, un coût. Les centimes descendent d'un cran,
   parce qu'on lit d'abord les dirhams. */
.chiffre-tete { font: var(--t-chiffre); letter-spacing: -.01em; }
.chiffre-tete__decimales { font-size: .55em; color: var(--texte-2); }
.chiffre-tete--danger { color: var(--danger); }


/* === Jauge et anneau ====================================================== */

.jauge { display: block; }
.jauge__haut {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--e-3); margin-bottom: 6px;
}
/* Deux lignes plutot qu'une coupe : deux mesures « Derouillage et peinture... »
   ne se distinguaient plus. */
.jauge__libelle {
  font: var(--t-corps); min-width: 0;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.jauge__valeur { font: var(--t-corps-fort); white-space: nowrap; flex: 0 0 auto; }
.jauge__piste { height: 8px; border-radius: var(--r-rond); background: var(--surface-2); overflow: hidden; }
.jauge__part {
  display: block; height: 100%; border-radius: var(--r-rond);
  background: var(--accent);
  width: calc(var(--part, 0) * 1%);
  transition: width var(--d-3) var(--courbe);
}
.jauge__part--finie { background: var(--ok); }
.jauge__part--depasse { background: var(--alerte); }
.jauge + .jauge { margin-top: var(--e-4); }

.anneau { position: relative; flex: 0 0 auto; width: 56px; height: 56px; }
.anneau svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.anneau__piste { stroke: var(--surface-2); }
.anneau__part { stroke: var(--accent); transition: stroke-dashoffset var(--d-3) var(--courbe); }
.anneau__part--finie { stroke: var(--ok); }
.anneau__texte {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font: 700 15px/1 var(--police-marque);
}


/* === Segments et pastilles ================================================
   Segments : un choix exclusif, on en prend un. Pastilles : un filtre, c'est
   un réglage. Deux objets différents, donc deux dessins différents.          */

.segments {
  display: flex; gap: var(--e-1); padding: var(--e-1);
  background: var(--surface-2); border-radius: var(--r-2);
  overflow-x: auto; max-width: 100%;
}
/* Le degrade de bord dit que la rangee continue hors ecran, et seulement
   quand c'est vrai : le composant mesure et pose ces classes. Pose d'office,
   le voile estompait le dernier segment d'une rangee qui tenait en entier. */
.defile--droite {
  mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%);
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%);
}
.defile--gauche {
  mask-image: linear-gradient(to right, transparent 0, #000 28px);
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 28px);
}
.defile--gauche.defile--droite {
  mask-image: linear-gradient(to right, transparent 0, #000 28px, #000 calc(100% - 28px), transparent 100%);
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 28px, #000 calc(100% - 28px), transparent 100%);
}
.segments__choix {
  /* Les libelles ne se compriment pas jusqu'a se toucher : la rangee defile
     plutot, avec le degrade de bord qui le dit. Quatre segments dont un long
     se chevauchaient a 390 px. */
  /* Quarante-quatre pixels, comme toute zone tapable : a trente-six on vise le
     segment voisin une fois sur cinq, et c'est un filtre qu'on manipule debout.
     La variante `--gros` monte a cinquante-six, elle, parce qu'on la touche
     avec des gants. */
  flex: 1 0 auto; min-width: 0; min-height: var(--touche);
  padding: var(--e-2) var(--e-3); border-radius: var(--r-1);
  font: 600 14px/18px var(--police); color: var(--texte-2);
  white-space: nowrap;
  display: flex; align-items: center; justify-content: center; gap: 6px;
  transition: background var(--d-1), color var(--d-1);
}
.segments__choix svg { width: 18px; height: 18px; flex: 0 0 auto; }
.segments__choix.est-actif { background: var(--surface); color: var(--texte); box-shadow: var(--ombre-1); }

/* Au-delà de quatre choix : des pastilles qui défilent, sans pilule. Six
   segments dans une pilule de 358 px ne peuvent que se chevaucher ou se
   couper. L'actif est en « fort », qui s'inverse en thème sombre : en noir
   de charte, il disparaissait sur le fond sombre. */
.segments--defilant { background: none; padding: 2px 0; gap: var(--e-2); border-radius: 0; }
.segments--defilant .segments__choix {
  flex: 0 0 auto; padding: 0 var(--e-4);
  border: 1px solid var(--trait-fort); border-radius: var(--r-rond);
  background: var(--surface); color: var(--texte-2); font: 600 14px/18px var(--police);
}
.segments--defilant .segments__choix.est-actif {
  background: var(--fort); border-color: var(--fort); color: var(--sur-fort); box-shadow: none;
}

/* Variante des formulaires : des cases tapables avec des gants.

   Une case prend la largeur de son nom et passe a la ligne quand la rangee
   est pleine. L'ancien dessin en tiers d'ecran (« flex: 1 1 30% ») coupait
   « Jossour Lalla Asma » et « Caisse Redouane » en plein mot, le texte
   sortant de sa case : trois cases par rangee quel que soit le nom, c'est
   une grille pour des mots courts, pas pour des noms de chantier. */
.segments--gros { background: none; padding: 0; flex-wrap: wrap; gap: var(--e-2); overflow: visible;
  mask-image: none; -webkit-mask-image: none; }
.segments--gros .segments__choix {
  flex: 1 1 auto; min-width: 0; max-width: 100%; min-height: var(--touche-terrain);
  padding: var(--e-2) var(--e-4);
  border: 2px solid var(--trait-fort); background: var(--surface);
  border-radius: var(--r-2); font: 600 16px/20px var(--police); color: var(--texte);
}
/* Le nom ne sort jamais de sa case : au pire, sur une case seule sur sa
   rangee et plus large que l'ecran, il se termine par des points. */
.segments--gros .segments__choix > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.segments--gros .segments__choix.est-actif {
  background: var(--accent); border-color: var(--accent); color: var(--sur-accent); box-shadow: none;
}
/* Avec une icone, ce sont des tuiles : deux par rangee sur telephone, trois
   des 600 px. A trois par rangee de 358 px, « Hebergement » ne tenait pas. */
.segments--gros .segments__choix--tuile {
  flex-basis: calc(50% - var(--e-2)); flex-direction: column; gap: 4px;
  white-space: normal; text-align: center; padding: var(--e-3) var(--e-3);
}
.segments--gros .segments__choix--tuile svg { width: 22px; height: 22px; }
@media (min-width: 600px) {
  .segments--gros .segments__choix--tuile { flex-basis: calc(33.333% - var(--e-2)); }
}

.pastilles {
  display: flex; gap: 6px; overflow-x: auto;
  padding: 2px 0; margin: 0 0 var(--e-3);
}
.pastille {
  flex: 0 0 auto; min-height: var(--touche); padding: 6px 14px;
  border: 1px solid var(--trait-fort); border-radius: var(--r-rond);
  background: var(--surface); color: var(--texte-2);
  font: 600 14px/18px var(--police); white-space: nowrap;
  transition: background var(--d-1), color var(--d-1), border-color var(--d-1);
}
.pastille:hover { border-color: var(--texte-3); }
.pastille.est-actif { background: var(--fort); border-color: var(--fort); color: var(--sur-fort); }


/* === Boutons ==============================================================
   Trois seulement : principal, secondaire, discret. Une quatrième variante
   serait un quatrième niveau d'importance, et il n'y en a pas quatre.        */

.bouton {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--e-2);
  min-height: var(--touche); padding: var(--e-3) var(--e-5);
  border-radius: var(--r-2); font: var(--t-corps-fort);
  transition: transform var(--d-1) var(--courbe), background var(--d-1), opacity var(--d-1);
}
.bouton:active { transform: scale(.98); }
.bouton svg { width: 20px; height: 20px; }
.bouton[disabled] { opacity: .45; pointer-events: none; }

.bouton--principal { background: var(--accent); color: var(--sur-accent); }
/* Le geste principal du terrain. Il s'inverse en sombre : le noir de la charte
   sur un fond noir ne se voit pas, et c'est le bouton le plus important. */
.bouton--fort { background: var(--fort); color: var(--sur-fort); }
.bouton--valider { background: var(--ok-plein); color: var(--sur-couleur); }
.bouton--secondaire { background: var(--surface-2); color: var(--texte); }
.bouton--danger { background: var(--danger-doux); color: var(--danger); }
.bouton--discret { color: var(--accent-texte); padding: var(--e-2) var(--e-3); }

/* Pleine largeur, gros, avec sa phrase d'explication : le geste principal d'un
   écran de terrain. Le texte est grand, donc le blanc sur accent reste au
   niveau AA malgré le contraste de 3,8:1 de la charte. */
.bouton--large {
  width: 100%; min-height: var(--touche-terrain);
  padding: var(--e-5); border-radius: var(--r-3);
  font: 700 20px/24px var(--police); text-align: left;
  justify-content: flex-start; flex-direction: column; align-items: flex-start; gap: 4px;
}
.bouton--large small { font: 500 14px/18px var(--police); opacity: .9; }
.bouton--large + .bouton--large { margin-top: var(--e-3); }


/* === Champs =============================================================== */

.champ { display: block; margin-top: var(--e-5); }
.champ__etiquette {
  display: block; font: var(--t-legende);
  text-transform: uppercase; letter-spacing: var(--interlettre-legende);
  color: var(--texte-2); margin-bottom: var(--e-2);
}
.champ__etiquette em { font-style: normal; text-transform: none; letter-spacing: 0; }

.champ__saisie {
  width: 100%; min-height: var(--touche-terrain);
  padding: var(--e-3) var(--e-4);
  background: var(--surface);
  border: 2px solid var(--trait-fort); border-radius: var(--r-2);
  font: var(--t-corps); color: var(--texte);
  transition: border-color var(--d-1);
}
.champ__saisie::placeholder { color: var(--texte-3); }
.champ__saisie:focus { border-color: var(--accent); }
.champ__saisie--faux { border-color: var(--danger); }
textarea.champ__saisie { min-height: 88px; resize: vertical; }

.champ__aide { font: var(--t-secondaire); color: var(--texte-2); margin-top: var(--e-2); }
.champ__faute { font: var(--t-secondaire); color: var(--danger); margin-top: var(--e-2); }

.champ__mdp { position: relative; }
.champ__oeil {
  position: absolute; right: var(--e-2); top: 50%; transform: translateY(-50%);
  width: var(--touche); height: var(--touche);
  display: flex; align-items: center; justify-content: center; color: var(--texte-2);
}
.champ__oeil svg { width: 22px; height: 22px; }
.champ__mdp .champ__saisie { padding-right: calc(var(--touche) + var(--e-2)); }

/* Un champ faux se signale par un mouvement court, jamais par une page rouge :
   l'erreur est à côté du champ, pas à côté de l'écran. */
@keyframes tx-secousse {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-5px); }
  75% { transform: translateX(5px); }
}
.secousse { animation: tx-secousse var(--d-2) var(--courbe); }


/* === Avatar et badge ====================================================== */

.avatar {
  flex: 0 0 auto;
  width: 36px; height: 36px; border-radius: var(--r-rond);
  display: flex; align-items: center; justify-content: center;
  background: var(--fond-teinte, var(--chantier-1)); color: var(--sur-teinte);
  font: 700 14px/1 var(--police);
}
.avatar--grand { width: 48px; height: 48px; font-size: 18px; }

.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: var(--r-rond);
  font: 600 12px/16px var(--police);
  background: var(--surface-2); color: var(--texte-2);
}
.badge svg { width: 13px; height: 13px; }
.badge--ok { background: var(--ok-doux); color: var(--ok); }
.badge--accent { background: var(--accent-doux); color: var(--accent-texte); }
.badge--alerte { background: var(--alerte-doux); color: var(--alerte); }
.badge--danger { background: var(--danger-doux); color: var(--danger); }


/* === Feuille ==============================================================
   Un formulaire ou un détail, par-dessus l'écran. Sur téléphone elle monte du
   bas ; sur écran elle se centre. Dans les deux cas c'est une route : le
   bouton retour du navigateur la ferme.                                      */

.voile {
  position: fixed; inset: 0; z-index: 50;
  background: var(--voile);
  display: flex; align-items: flex-end; justify-content: center;
  animation: tx-fondu var(--d-2) var(--courbe);
}
@keyframes tx-fondu { from { opacity: 0; } to { opacity: 1; } }

.feuille {
  /* dvh : sur iPhone, 92vh compte la barre d'adresse repliee, et le pied de la
     feuille passait sous la barre de Safari quand elle etait depliee. */
  width: 100%; max-height: 92vh; max-height: 92dvh;
  display: flex; flex-direction: column;
  background: var(--fond-2);
  border-radius: var(--r-4) var(--r-4) 0 0;
  box-shadow: var(--ombre-3);
  animation: tx-monte var(--d-3) var(--courbe);
  /* Sert au retour en place quand le glissement n'a pas atteint le seuil. Le
     geste lui-même coupe cette transition, sinon la feuille traînerait derrière
     le doigt d'un tiers de seconde. */
  transition: transform var(--d-2) var(--courbe);
}
@keyframes tx-monte { from { transform: translateY(100%); } to { transform: translateY(0); } }

/* La zone par laquelle on attrape la feuille. `touch-action: none` est ce qui
   empêche le navigateur de prendre le geste pour un défilement et de couper
   les événements de pointeur au bout de quelques pixels. */
.feuille__prise { flex: 0 0 auto; touch-action: none; cursor: grab; }
.feuille__prise:active { cursor: grabbing; }

.feuille__poignee {
  flex: 0 0 auto; align-self: center;
  width: 40px; height: 5px; margin: var(--e-2) 0 0;
  border-radius: var(--r-rond); background: var(--trait-fort);
}
.feuille__tete {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: var(--e-3);
  padding: var(--e-3) var(--e-4);
}
.feuille__fermer {
  width: var(--touche); height: var(--touche);
  display: flex; align-items: center; justify-content: center;
  color: var(--texte-2); margin-left: calc(var(--e-2) * -1);
}
/* Taille explicite : un svg sans dimensions prend sa case dans Chrome et zero
   dans Safari. La croix etait invisible sur l'iPhone, visible sur les captures. */
.feuille__fermer svg { width: 22px; height: 22px; }
.feuille__titre { flex: 1; min-width: 0; font: var(--t-sous-titre); }
.feuille__etape { font: var(--t-secondaire); color: var(--texte-2); }
.feuille__corps {
  flex: 1; overflow-y: auto; padding: 0 var(--e-4) var(--e-4);
  /* Jamais de defilement de cote : un corps qui defile verticalement defile
     aussi de cote des qu'un enfant depasse, et sur iPhone le champ date le
     faisait. Le doigt decalait alors tout le formulaire vers la gauche. */
  overflow-x: hidden;
  /* Arrive au bout, le defilement ne se transmet pas a la page du dessous. */
  overscroll-behavior: contain;
}
.feuille__pied {
  flex: 0 0 auto;
  display: flex; gap: var(--e-3);
  padding: var(--e-3) var(--e-4) calc(var(--e-4) + env(safe-area-inset-bottom));
  border-top: 1px solid var(--trait); background: var(--surface);
}
.feuille__pied .bouton { flex: 1; min-height: var(--touche-terrain); }


/* === Toast ================================================================ */

.toasts {
  position: fixed; z-index: 60;
  left: 50%; transform: translateX(-50%);
  bottom: calc(var(--hauteur-onglets) + env(safe-area-inset-bottom) + var(--e-4));
  display: flex; flex-direction: column; gap: var(--e-2);
  width: min(420px, calc(100vw - var(--e-8)));
}
.toast {
  display: flex; align-items: center; gap: var(--e-3);
  padding: var(--e-3) var(--e-4); border-radius: var(--r-2);
  background: var(--noir); color: var(--sur-noir);
  box-shadow: var(--ombre-3); font: var(--t-secondaire);
  animation: tx-toast var(--d-2) var(--courbe);
}
@keyframes tx-toast { from { transform: translateY(12px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
.toast svg { width: 20px; height: 20px; flex: 0 0 auto; }
.toast--ok svg { color: var(--ok); }
.toast--faute svg { color: var(--danger); }
/* La zone tapable fait bien 44 px : elle est obtenue par des marges negatives,
   pour que le toast lui-meme garde sa hauteur. Un « Annuler » de vingt pixels
   se rate une fois sur trois avec des gants. */
.toast__action {
  margin-left: auto; color: var(--accent); font: var(--t-corps-fort);
  padding: var(--e-3) var(--e-2);
  margin-top: calc(var(--e-3) * -1); margin-bottom: calc(var(--e-3) * -1);
}


/* === Bandeaux ============================================================= */

.bandeau {
  display: flex; align-items: center; gap: var(--e-2);
  padding: var(--e-2) var(--marge-page);
  margin: 0 calc(var(--marge-page) * -1);
  font: var(--t-secondaire); font-weight: 600;
}
.bandeau svg { width: 18px; height: 18px; flex: 0 0 auto; }
.bandeau--reseau { background: var(--alerte-doux); color: var(--alerte); }
.bandeau--maj { background: var(--accent-doux); color: var(--accent-texte); }
.bandeau__action { margin-left: auto; text-decoration: underline; }


/* L'attente du tout premier chargement, avant même de savoir si une session
   existe. Elle imite la forme de l'écran qui va venir. */
.attente {
  display: grid; gap: var(--e-3);
  padding: var(--e-6); max-width: 520px; margin: 0 auto;
}


/* === État vide et squelette =============================================== */

.vide {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: var(--e-3); padding: var(--e-10) var(--e-5);
  color: var(--texte-2);
}
.vide svg { width: 40px; height: 40px; color: var(--texte-3); }
.vide__titre { font: var(--t-sous-titre); color: var(--texte); }
.vide__phrase { font: var(--t-secondaire); max-width: 34ch; }

/* Des formes, pas un tourniquet : on voit tout de suite la disposition qui
   arrive, et l'attente paraît plus courte. */
.squelette {
  border-radius: var(--r-1);
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
  background-size: 400% 100%;
  animation: tx-scintille 1.4s ease infinite;
}
@keyframes tx-scintille { from { background-position: 100% 0; } to { background-position: 0 0; } }
@media (prefers-reduced-motion: reduce) { .squelette { animation: none; } }


/* === Confirmation ========================================================= */

.confirmation { width: min(400px, 100%); }
.confirmation__phrase { font: var(--t-corps); color: var(--texte-2); margin-top: var(--e-2); }


/* === Écran de connexion ===================================================
   Fond noir de la charte, formulaire sur une surface claire. C'est le seul
   écran de l'app qui ne suit pas le thème : la marque y est le sujet.        */

.connexion {
  min-height: 100dvh; background: var(--noir); color: var(--sur-noir);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: var(--e-6);
}
.connexion__marque { display: flex; align-items: baseline; gap: 8px; margin-bottom: var(--e-8); }
.connexion__marque img { height: 20px; width: auto; }
.connexion__marque span { font: 500 22px/1 var(--police-marque); }

.connexion__carte {
  width: min(420px, 100%);
  background: var(--surface); color: var(--texte);
  border-radius: var(--r-4); padding: var(--e-6);
  box-shadow: var(--ombre-3);
}
.connexion__carte .champ:first-of-type { margin-top: 0; }
.connexion__bouton { width: 100%; min-height: var(--touche-terrain); margin-top: var(--e-6); }
.connexion__note { font: var(--t-secondaire); color: var(--texte-2); text-align: center; margin-top: var(--e-4); }
.connexion__faute {
  background: var(--danger-doux); color: var(--danger);
  border-radius: var(--r-2); padding: var(--e-3); margin-bottom: var(--e-4);
  font: var(--t-secondaire); font-weight: 600;
}


/* ==========================================================================
   LOT 1 : la saisie et les écrans du terrain
   ========================================================================== */

/* === Cartes du terrain ====================================================
   Une carte porte un chiffre ou un chantier. Les légendes sont en petites
   capitales : c'est ce qui permet de lire un solde sans lire son étiquette.  */

.carte__legende {
  font: var(--t-legende); text-transform: uppercase;
  letter-spacing: var(--interlettre-legende); color: var(--texte-2);
}
.carte__legende--suite { margin-top: var(--e-4); }
.carte__phrase { font: var(--t-corps-fort); margin-top: var(--e-2); }
.carte__aide { font: var(--t-secondaire); color: var(--texte-2); margin-top: var(--e-2); }
.carte__aide--date { color: var(--alerte); }

.carte--caisse .chiffre-tete { margin-top: var(--e-1); }

/* La tête d'une carte de chantier est tapable en entier : viser l'anneau ou
   viser le nom doit ouvrir la même chose. */
.carte__tete {
  display: flex; align-items: center; gap: var(--e-3);
  width: 100%; text-align: left; min-height: var(--touche);
}
.carte__tete-texte { flex: 1; min-width: 0; display: block; }
.carte__titre { display: block; font: var(--t-sous-titre); }
.carte__sous {
  display: block; font: var(--t-secondaire); color: var(--texte-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.carte--chantier .jauge { margin-top: var(--e-4); }
.carte__geste { margin-top: var(--e-4); width: 100%; }
.carte--mesure .quantite { margin-top: var(--e-2); }

/* Une mission. Elle se distingue d'une carte de chantier par un filet d'accent
   a gauche, et rien d'autre : c'est la seule chose de l'accueil qui ait une
   echeance, et elle doit se remarquer sans crier. */
.carte--mission { border-left: 3px solid var(--accent); }
.mission__tete { display: flex; align-items: flex-start; gap: var(--e-3); }
.mission__marque { flex: 0 0 auto; color: var(--accent-texte); display: flex; }
/* Safari rend un svg sans taille CSS en 0 par 0 : toujours la poser. */
.mission__marque svg { width: 22px; height: 22px; }
.mission__texte { flex: 1; min-width: 0; }
/* Le lieu passe a la ligne au lieu d'etre coupe. « Chez Nejmi · Raba... » ne
   dit pas ou aller, et c'est la seule raison d'etre de cette ligne. Deux
   lignes suffisent a « Chez untel, telle ville, tel immeuble ». */
.carte--mission .carte__sous {
  white-space: normal; display: -webkit-box;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical; line-clamp: 2;
}
.mission__but {
  margin: var(--e-3) 0 0; font: var(--t-secondaire); color: var(--texte-2);
  white-space: pre-wrap;
}
/* Deux gestes cote a cote, et qui passent l'un sous l'autre a 320 px plutot
   que de comprimer leurs libelles. */
.mission__gestes { display: flex; flex-wrap: wrap; gap: var(--e-2); margin-top: var(--e-4); }
.mission__gestes > * { flex: 1 1 auto; min-width: 0; }
/* L'etat d'une mission et le mot du retour, sous son nom, dans une liste. */
.ligne__etiquettes { display: flex; flex-wrap: wrap; align-items: center; gap: var(--e-2); margin-top: var(--e-2); }
/* Le compte rendu, dans la fiche : un mot de quelqu'un, pas une donnee. On le
   met entre guillemets et on garde ses retours a la ligne. */
.mission__rendu {
  margin: var(--e-3) 0 0; padding-left: var(--e-3);
  border-left: 2px solid var(--trait); white-space: pre-wrap;
  font: var(--t-corps); color: var(--texte);
}

.mission__mot {
  font: var(--t-secondaire); color: var(--texte-2); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.section__aide {
  margin: var(--e-2) 0 0; font: var(--t-secondaire); color: var(--texte-2);
}

.section__action {
  font: var(--t-secondaire); font-weight: 600; color: var(--accent-texte);
  min-height: var(--touche); padding: 0 var(--e-1); flex: 0 0 auto;
}


/* === Fil des changements ==================================================
   Le journal, en colonne, avec un filet vertical qui relie les points : c'est
   ce qui fait lire la suite comme une chronologie et non comme une liste.    */

.fil { position: relative; padding-left: var(--e-5); }
.fil::before {
  content: ''; position: absolute; left: 4px; top: 8px; bottom: 8px;
  width: 2px; background: var(--trait);
}
.fil__ligne { position: relative; display: flex; gap: var(--e-3); padding: var(--e-2) 0; }
.fil__point {
  position: absolute; left: calc(var(--e-5) * -1 + 1px); top: 14px;
  width: 8px; height: 8px; border-radius: var(--r-rond);
  /* Gris et non rose : un point de chronologie n'est pas une action, et
     l'accent ne vaut que s'il est rare. */
  background: var(--texte-3); box-shadow: 0 0 0 3px var(--fond-2);
}
.fil__texte { font: var(--t-secondaire); color: var(--texte); }
.fil__quand { display: block; font: var(--t-legende); color: var(--texte-2); margin-top: 2px; }


/* === Champ montant ========================================================
   Le plus gros élément de l'app. On tape un montant en regardant ailleurs :
   il doit se relire d'un coup d'oeil, sans rapprocher le téléphone.          */

.montant {
  display: flex; align-items: baseline; justify-content: center; gap: var(--e-2);
  padding: var(--e-6) var(--e-4);
  background: var(--surface); border: 2px solid transparent; border-radius: var(--r-3);
  box-shadow: var(--ombre-1); margin-top: var(--e-4);
  transition: border-color var(--d-1);
}
/* Le focus se pose sur la boite entiere et non sur l'input : un contour de la
   largeur du champ dessinait un rectangle sans rapport avec le chiffre. */
.montant:focus-within { border-color: var(--accent); }
/* La largeur suit le contenu, par l'attribut size : le couple chiffre + DH
   reste ainsi centre et grandit des deux cotes. Avec une largeur figee, le
   zero se retrouvait colle a droite d'une boite a moitie vide. */
.montant__saisie {
  width: auto; max-width: 8ch; min-width: 1ch;
  border: 0; background: none; text-align: right; outline: none;
  font: var(--t-chiffre); font-variant-numeric: tabular-nums;
  color: var(--texte); padding: 0;
}
.montant__saisie:focus-visible { outline: none; }
.montant__saisie::placeholder { color: var(--texte-3); }
.montant__devise { font: var(--t-chiffre-2); color: var(--texte-2); }


/* === Champ quantité ======================================================= */

.quantite { display: flex; align-items: stretch; gap: var(--e-2); }
.quantite__bouton {
  flex: 0 0 auto; width: var(--touche-terrain); min-height: var(--touche-terrain);
  display: flex; align-items: center; justify-content: center;
  background: var(--surface-2); border-radius: var(--r-2); color: var(--texte);
}
.quantite__bouton svg { width: 22px; height: 22px; }
.quantite__bouton[disabled] { opacity: .35; pointer-events: none; }
.quantite__milieu {
  /* min-width: 0 est indispensable : sans lui, un element flex refuse de
     descendre sous la largeur naturelle de son contenu, et l'input garde sa
     taille par defaut de vingt caracteres. Le bouton + sortait de l'ecran. */
  flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--e-2);
  padding: 0 var(--e-4); min-height: var(--touche-terrain);
  background: var(--surface); border: 2px solid var(--trait-fort); border-radius: var(--r-2);
}
.quantite__milieu:focus-within { border-color: var(--accent); }
.quantite__saisie {
  flex: 1; width: 100%; min-width: 0; border: 0; background: none; padding: 0;
  font: var(--t-chiffre-2); font-variant-numeric: tabular-nums; color: var(--texte);
}
.quantite__unite { font: var(--t-corps-fort); color: var(--texte-2); }

/* Le champ date, tel que Safari sur iPhone le dessine : sans ces lignes il
   garde l'apparence et la largeur du systeme, ignore `width: 100%` et
   deborde du formulaire de la largeur de ses marges. La valeur, elle, se
   cale a gauche comme dans tout autre champ, et garde une hauteur de ligne
   meme vide, sinon le champ s'ecrase. */
.champ__saisie--date {
  margin-top: var(--e-3); display: block; min-width: 0;
  -webkit-appearance: none; appearance: none;
}
.champ__saisie--date::-webkit-date-and-time-value { text-align: left; min-height: 22px; }


/* === Suggestions ==========================================================
   Un article déjà acheté, un fournisseur déjà utilisé. Elles remplissent un
   champ, elles ne changent pas de mode : d'où l'étiquette et non l'onglet.   */

.suggestions { display: flex; flex-wrap: wrap; gap: var(--e-2); margin-top: var(--e-2); }
.suggestion {
  min-height: var(--touche); padding: var(--e-2) var(--e-4);
  border: 1px solid var(--trait-fort); border-radius: var(--r-rond);
  background: var(--surface); color: var(--texte); font: var(--t-secondaire);
  transition: background var(--d-1), border-color var(--d-1), color var(--d-1);
}
.suggestion:hover { border-color: var(--texte-3); }
.suggestion.est-actif {
  background: var(--accent); border-color: var(--accent); color: var(--sur-accent); font-weight: 600;
}


/* === Photos =============================================================== */

.photos { margin-top: var(--e-4); }
.photos__gestes { display: flex; gap: var(--e-3); }
.photos__geste {
  flex: 1; min-height: 120px; padding: var(--e-4);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  background: var(--surface); border: 2px dashed var(--trait-fort); border-radius: var(--r-3);
  color: var(--texte); text-align: center;
}
.photos__geste svg { width: 28px; height: 28px; color: var(--accent-texte); }
.photos__geste span { font: var(--t-corps-fort); }
.photos__geste small { font: var(--t-legende); color: var(--texte-2); }
.photos__geste[disabled] { opacity: .4; pointer-events: none; }

.photos__liste { display: flex; flex-wrap: wrap; gap: var(--e-2); margin-top: var(--e-3); }
.photos__tuile {
  position: relative; width: 88px; height: 88px;
  border-radius: var(--r-2); overflow: hidden; background: var(--surface-2);
}
.photos__tuile img { width: 100%; height: 100%; object-fit: cover; }
.photos__tuile--attente {
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
  background-size: 400% 100%; animation: tx-scintille 1.4s ease infinite;
}
.photos__tuile--lien { border: 0; padding: 0; cursor: pointer; }
.photos__retirer {
  position: absolute; top: 2px; right: 2px;
  width: 28px; height: 28px; border-radius: var(--r-rond);
  display: flex; align-items: center; justify-content: center;
  background: var(--voile-photo); color: var(--sur-couleur);
}
.photos__retirer svg { width: 16px; height: 16px; }

/* La photo en grand, par-dessus tout. Une croix pour en sortir, en haut a
   droite, la ou tout le monde la cherche ; le fond ferme aussi, la photo non. */
.loupe {
  position: fixed; inset: 0; z-index: 70;
  background: var(--voile-photo-fort);
  display: flex; align-items: center; justify-content: center; padding: var(--e-4);
  animation: tx-fondu var(--d-2) var(--courbe);
}
.loupe img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: var(--r-2); }
.loupe__fermer {
  position: absolute; top: calc(var(--e-2) + env(safe-area-inset-top)); right: var(--e-2);
  width: var(--touche); height: var(--touche); border-radius: var(--r-rond);
  display: flex; align-items: center; justify-content: center;
  background: var(--voile-photo); color: var(--sur-couleur);
}
.loupe__fermer svg { width: 22px; height: 22px; }
/* La vignette d'une photo pas encore envoyee s'ouvre aussi en grand. */
.photos__voir { display: block; width: 100%; height: 100%; padding: 0; }


/* === Feuille de saisie ==================================================== */

.feuille--saisie .champ:first-child { margin-top: var(--e-2); }
.recap__ligne { font: var(--t-corps); margin-top: var(--e-2); }
.recap__ligne--douce { color: var(--texte-2); font: var(--t-secondaire); }
.carte--recap { margin-top: var(--e-5); background: var(--accent-doux); box-shadow: none; }

/* Le repli des mesures terminées : présent, mais pas au premier plan. */
.repli {
  display: flex; align-items: center; justify-content: space-between; gap: var(--e-3);
  width: 100%; min-height: var(--touche); margin-top: var(--e-4);
  padding: var(--e-3) var(--e-4);
  background: var(--surface-2); border-radius: var(--r-2);
  font: var(--t-secondaire); font-weight: 600; color: var(--texte-2);
}


/* === File d'attente ======================================================= */

.carte--attente { padding: 0; overflow: hidden; }
.attente-ligne { padding: var(--e-4); }
.attente-ligne + .attente-ligne { box-shadow: inset 0 1px 0 var(--trait); }
.attente-ligne--refus { background: var(--danger-doux); }
.attente-ligne__haut { display: flex; align-items: center; gap: var(--e-3); }
.attente-ligne__titre { flex: 1; min-width: 0; font: var(--t-corps); }
.attente-ligne__sous { font: var(--t-secondaire); color: var(--texte-2); margin-top: 2px; }
.attente-ligne__raison { font: var(--t-secondaire); color: var(--danger); margin-top: var(--e-2); }
.attente-ligne__gestes { display: flex; gap: var(--e-2); margin-top: var(--e-3); }
.attente-ligne__gestes .bouton { flex: 1; }

.badge--attente { background: var(--alerte-doux); color: var(--alerte); }

.note-cache {
  font: var(--t-legende); color: var(--alerte);
  margin-top: var(--e-3); text-align: center;
}
.charger { display: flex; justify-content: center; margin-top: var(--e-5); }
.charger .bouton { min-width: 200px; }


/* === Détail d'une ligne =================================================== */

.detail__montant { font: var(--t-chiffre); letter-spacing: -.02em; margin-top: var(--e-2); }
.detail__titre { font: var(--t-sous-titre); color: var(--texte-2); margin-bottom: var(--e-5); }
.detail__faits { margin-top: var(--e-5); }
.detail__horodatage { font: var(--t-legende); color: var(--texte-2); margin-top: var(--e-4); }


/* === Fiche de chantier ==================================================== */

.adresse {
  display: flex; align-items: center; gap: var(--e-2);
  min-height: var(--touche); margin-top: var(--e-3);
  font: var(--t-secondaire); color: var(--accent-texte);
}
.adresse svg { width: 18px; height: 18px; flex: 0 0 auto; }
.adresse span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.fiche__tete { display: flex; align-items: center; gap: var(--e-4); }
.fiche__gestes { display: flex; gap: var(--e-3); margin-top: var(--e-6); }
.fiche__gestes .bouton { flex: 1; min-height: var(--touche-terrain); }

.bandeau--clos { background: var(--ok-doux); color: var(--ok); }


/* === Confirmation plein cadre =============================================
   Après un enregistrement. Elle occupe tout l'écran : une confirmation qu'on
   peut rater fait ressaisir la même dépense une deuxième fois.               */

.fini {
  min-height: 100dvh; padding: var(--e-8);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--e-4); text-align: center;
  background: var(--ok-plein); color: var(--sur-couleur);
  animation: tx-fondu var(--d-2) var(--courbe);
}
.fini--attente { background: var(--noir); color: var(--sur-noir); }
.fini__rond {
  width: 88px; height: 88px; border-radius: var(--r-rond);
  display: flex; align-items: center; justify-content: center;
  background: var(--sur-couleur-voile);
}
.fini__rond svg { width: 44px; height: 44px; stroke-width: 2.4; }
.fini__quoi { font: var(--t-titre); }
.fini__etat { font: var(--t-corps); opacity: .9; max-width: 30ch; }
.fini .bouton--doux { margin-top: var(--e-4); }

/* Un bouton discret sur un fond coloré : il doit rester lisible sans voler la
   vedette au message. */
.bouton--doux { background: var(--sur-couleur-voile); color: inherit; }


/* === Fondu de changement d'onglet =========================================
   Un fondu court et rien d'autre. Un glissement latéral suggérerait un ordre
   entre les onglets, qui n'existe pas, et coûterait une image sur un téléphone
   d'entrée de gamme. La clé du bloc change à chaque famille d'écrans, ce qui
   rejoue l'animation ; naviguer dans la même famille ne rejoue rien.         */

.coque__ecran { animation: tx-fondu var(--d-1) var(--courbe); }


/* Bulle, aide des raccourcis, installation : dans plus.css, chargé par leurs modules différés. */
