/* ==========================================================================
   mobile.css, interface telephone d'aiudit
   --------------------------------------------------------------------------
   REGLE ABSOLUE : ce fichier ne doit JAMAIS modifier le rendu sur ordinateur.
   Toutes les regles vivent donc a l'interieur de @media (max-width: 820px).
   Le seul bloc hors de cette media query masque les elements injectes par
   mobile.js (prefixe .m-) au cas ou la fenetre serait elargie apres coup.

   style.css n'est pas touche : mobile.css est charge APRES lui dans chaque
   template, il gagne donc a specificite egale et n'a pas besoin de !important
   dans la majorite des cas.

   Sommaire
     0.  Jetons mobiles
     1.  Fondations (body, safe areas, cibles tactiles)
     2.  Barre de titre (app-header compacte)
     3.  Barre d'onglets basse (.m-tabbar, injectee)
     4.  Feuilles modales (.m-sheet, injectees)
     5.  Contenu : main, panels, toolbars
     6.  Listes de mandats
     7.  Detail mandat : onglets, sources, depot, reglages
     8.  Analyse : cycles, tables d'arbre, selecteur d'annees
     9.  Tables -> cartes (compte, document, ecriture, mapping)
     10. Modales plein ecran
     11. Tickets & messages
     12. Mapping
     13. Mon compte & cabinets
     14. Connexion / MFA / mot de passe
     15. Widget ticket flottant
     16. Bandeau d'alerte
     17. Dictee vocale
     18. Accessibilite & preferences systeme
   ========================================================================== */

@media (max-width: 820px) {

  /* ---------- 0. Jetons mobiles ------------------------------------------ */

  :root {
    --m-gutter: 14px;
    /* La gouttiere de page vaut ici celle du telephone. `--gouttiere` (2 rem)
       ne sert pas qu'a rembourrer : la barre collante des tickets s'en sert en
       marge negative pour aller d'un bord a l'autre du conteneur. Laissee a
       2 rem sur un ecran qui n'en offre que 14 px, elle tirait la bande 18 px
       hors de la page de chaque cote, et c'est la page entiere qui defilait
       lateralement. Le jeton se recale, la regle du grand ecran reste vraie. */
    --gouttiere: var(--m-gutter);
    --m-tabbar-h: 54px;
    /* Ce que la barre occupe vraiment : la hauteur d'un onglet, le filet d'un
       pixel qui la detache de la page, et l'encoche du bas. Ecrit une fois ici
       parce que quatre regles s'en servent pour se ranger au-dessus d'elle --
       le creux du bas de page, le bouton flottant, le widget de ticket, et ce
       meme widget quand un bouton flottant est deja la. Toutes additionnaient
       l'onglet et l'encoche en oubliant le filet : elles reservaient 54 px
       pour une barre qui en fait 55, et la derniere ligne de chaque page se
       terminait sous le trait, d'un pixel -- releve sur /dossiers, /tickets,
       /messages, /mapping et l'analyse d'un mandat. C'est la correction que
       --h-entete porte deja cote bureau, et pour la meme raison : la hauteur
       d'une barre, c'est son contenu plus son trait. */
    --m-tabbar-hors-tout: calc(var(--m-tabbar-h) + 1px + var(--m-safe-bottom));
    --m-header-h: 52px;          /* recalcule par mobile.js sur la vraie hauteur */
    --m-safe-top: env(safe-area-inset-top, 0px);
    --m-safe-bottom: env(safe-area-inset-bottom, 0px);
    --m-tap: 44px;               /* cible tactile minimale (HIG / Material) */
    /* Le plancher du texte dans un champ. Safari agrandit la page a la mise
       au point sur tout champ dont le texte fait moins de 16 px, et ne la rend
       plus ensuite. C'est le pendant de --m-tap : l'un est la mesure du doigt,
       l'autre celle de ce que le systeme accepte de laisser tranquille. La
       feuille le posait a quinze endroits, en reecrivant sa raison a six
       d'entre eux, et l'oubliait au seizieme -- la zone ou l'on ecrit a
       l'assistance. Il se declare ici, et une seule regle l'applique. */
    --m-corps-champ: 16px;
    --m-surface: rgba(255, 255, 255, .86);
    /* La feuille du telephone est l'equivalent tactile de la fenetre modale
       de l'ordinateur : meme arrondi, une seule grande surface flottante
       pour tout le site. */
    --m-radius-sheet: var(--radius-lg);
  }

  /* ---------- 1. Fondations ---------------------------------------------- */

  html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
  }

  body {
    -webkit-tap-highlight-color: transparent;
    overscroll-behavior-y: contain;
    font-size: 15px;
    /* Place pour la barre d'onglets basse, filet et encoche compris. */
    padding-bottom: var(--m-tabbar-hors-tout);
  }

  /* Pages sans barre d'onglets (connexion, MFA...). */
  body.m-no-tabbar { padding-bottom: var(--m-safe-bottom); }

  /* Verrouillage du defilement quand une feuille modale est ouverte. */
  body.m-scroll-locked {
    position: fixed;
    inset: 0;
    overflow: hidden;
    width: 100%;
  }

  /* Les zones defilables gardent l'inertie iOS. */
  .tree-table-wrap,
  .modal-table-wrap,
  .toolbar,
  .cycles-nav,
  .m-sheet-body,
  .m-chiprow,
  .exercice-selecteur {
    -webkit-overflow-scrolling: touch;
  }

  /* Barres de defilement masquees sur les rangees horizontales. */
  .toolbar::-webkit-scrollbar,
  .cycles-nav::-webkit-scrollbar,
  .m-chiprow::-webkit-scrollbar,
  .dossier-tabs::-webkit-scrollbar,
  .exercice-selecteur::-webkit-scrollbar { display: none; }
  .toolbar, .cycles-nav, .m-chiprow, .dossier-tabs,
  .exercice-selecteur { scrollbar-width: none; }

  /* La seule regle qui pose le plancher du texte des champs, pour toute la
     feuille : les trois controles de formulaire, et les trois zones editables
     qui n'en sont pas. Le `!important` est ce qui rend inutile de le repeter
     ailleurs -- c'est pourtant ce que faisaient quatorze autres regles, qui
     redeclaraient une valeur que celle-ci leur imposait deja.

     `.ticket-widget-texte` entre dans la liste. C'est la zone de frappe du
     widget de ticket, presente sur chaque page d'ou l'on peut ecrire a
     l'assistance, et la seule chose qui echappait au plancher : elle gardait
     le corps du texte courant, 13.6 px, donc sous le seuil. Ouvrir le widget
     et taper dedans agrandissait la page, et Safari ne la rend plus. Sa
     jumelle du popover de commentaires avait recu les 16 px a la main ; elle,
     jamais -- personne ne l'avait vue, parce que le nombre etait ecrit
     quinze fois et la liste nulle part.

     La raison du nombre est ecrite avec le jeton. */
  input, select, textarea,
  .ticket-card-texte-edit,
  .ticket-card-suivi-input,
  .ticket-widget-texte {
    font-size: var(--m-corps-champ) !important;
  }

  /* Cibles tactiles : tout ce qui se clique fait au moins 44px de haut. */
  .icon-btn,
  .btn-link,
  .btn-primary,
  .dossier-sort-btn,
  .cycles-nav-btn,
  .modal-close,
  .toolbar-search-clear,
  /* Les quatre reponses du programme de travail. Ce fichier n'avait aucune
     regle pour ce panneau : elles arrivaient sur le telephone a la hauteur
     qu'elles ont sur l'ecran, la plus petite du site, alors qu'elles sont la
     seule chose qu'on y touche. */
  .programme-choix-btn,
  /* Ce qui deplie une section de la page Cabinet. Un `<summary>` n'est ni un
     bouton ni un lien : il n'entrait dans aucune des familles ci-dessus, et
     rien d'autre ne le mesurait. Les deux replis de la page -- « Identite et
     coordonnees », sur chaque cabinet et sur le formulaire qui en ajoute un,
     et « Ce que la ronde a trouve » sous le nommage -- arrivaient donc sur le
     telephone a dix-neuf pixels de haut, la hauteur de leur unique ligne de
     texte : sept rangees larges de deux cent quatre-vingts pixels et hautes
     comme un mot. Elles ouvrent chacune une section entiere. */
  .cabinet-identite-titre,
  .ronde-detail > summary,
  /* Le bouton qui deplie les acces d'un collaborateur. Il a sa propre ligne et
     la forme d'un bouton a l'arret depuis le 28 aout 2026, parce qu'« on ne
     trouve pas l'endroit » ; il restait a vingt-huit pixels de haut sur le
     telephone -- trouvable, et pas visable. Sa hauteur d'ordinateur est
     ecrite (`min-height: var(--h-sm)` dans style.css), donc c'est bien un
     plancher qu'on releve et non une taille qu'on invente. */
  .equipe-acces-bouton {
    min-height: var(--m-tap);
  }

  /* La bascule segmentee est un cadre qui contient ses boutons : c'est elle,
     et non eux, qui porte la cible tactile. Poser le plancher sur le bouton
     donnait une bande de 46 px -- 44 de bouton, plus le liseré du cadre de
     part et d'autre --, deux pixels de plus que tout ce qui l'entoure, sur les
     trois bascules du mapping et les quatre du mandat.

     C'est l'inverse exact de ce que dit la regle d'ordinateur, quelques lignes
     plus haut dans style.css : « le bouton se laisse etirer par le cadre
     plutot que de le pousser ». Ici le bouton poussait. Le plancher passe donc
     sur le cadre, et les boutons s'etirent pour le remplir, comme ils le font
     deja sur --h-sm. */
  .unit-toggle { min-height: var(--m-tap); }

  .btn-sm { min-height: 38px; }

  /* ... sauf un bouton primaire : il n'y en a jamais deux dans une meme rangee
     dense, c'est l'action principale de son formulaire ("Publier l'alerte",
     "Envoyer a l'agent"). Il reprend la cible standard. La regle vient apres
     .btn-sm, a specificite superieure : elle gagne dans les deux sens. */
  .btn-primary.btn-sm { min-height: var(--m-tap); }

  /* Les boutons purement iconiques restent carres.

     Cette ligne ne faisait rien, et ce depuis qu'elle est ecrite. Elle porte
     une seule classe ; la feuille du grand ecran mesure les memes boutons sous
     deux (`.btn-link.btn-icon-only`, `.btn-primary.btn-icon-only`) et sous
     trois (`.btn-primary.btn-sm.btn-icon-only`), et une classe ne peut rien
     contre deux. Aucun bouton iconique du site n'echappe a l'une de ces
     familles : la regle etait donc morte partout ou elle avait a servir, et
     elle laissait derriere elle des rectangles de 28 de large pour 44 de haut
     -- « Retirer le compte », « Retirer le logo du cabinet », « Retirer le
     tampon numerique », la corbeille d'un collaborateur --, tous destructeurs,
     tous a la moitie de la cible.

     Deux choses le disaient deja sans qu'on les entende. Le commentaire de la
     regle suivante decrit l'effet de celle-ci au present -- « la regle
     ci-dessus ne monte que la largeur a la cible tactile » -- alors qu'elle ne
     montait rien ; si elle a quand meme donne le bon carre aux lignes de
     reglages, c'est parce qu'elle y ecrit les deux mesures a la main. Et
     `parite.json` affirme du site, dans sa section `cibles`, qu'« un bouton
     reduit a son icone devient un carre de 44 (.btn-icon-only) » : c'est sur
     cette phrase que l'app a ete alignee, et elle etait fausse.

     La regle est donc reecrite aux specificites ou elle a affaire, et sur les
     deux mesures plutot que sur la seule largeur : un carre se dit en deux
     nombres. Les deux tableaux qui gardent leur bouton dense le disent chacun
     dans sa propre regle, portee par un identifiant, et restent hors de
     portee de celle-ci. */
  .btn-icon-only,
  .btn-link.btn-icon-only,
  .btn-primary.btn-icon-only,
  .btn-link.btn-sm.btn-icon-only,
  .btn-primary.btn-sm.btn-icon-only {
    min-width: var(--m-tap);
    min-height: var(--m-tap);
  }

  /* Un bouton-lien de ligne de reglages porte lui aussi un cadre depuis le
     26 aout 2026, et il reprend la cible tactile pleine plutot que les 38 px
     de .btn-sm. Ces rangees-la se rangent en colonne sur un telephone : la
     raison d'etre du barreau dense -- plusieurs petits boutons cote a cote --
     n'y vaut plus, et le cadre rendait enfin visible ce que personne ne voyait
     tant que le bouton etait a plat, six pixels d'ecart avec le bouton plein
     juste au-dessus. */
  .reglages-row > .btn-link,
  .reglages-piece > .btn-link,
  .reglages-presence-row > .btn-link {
    min-height: var(--m-tap);
  }

  /* L'icone de reglages porte un cadre depuis le 26 aout 2026, et ce cadre est
     un carre de --h-sm sur ordinateur. La regle ci-dessus ne monte que la
     largeur a la cible tactile : le carre serait devenu un rectangle de 44x28.
     Il monte donc des deux cotes. */
  .reglages-row > .btn-link.btn-icon-only,
  .reglages-piece > .btn-link.btn-icon-only,
  .reglages-presence-row > .btn-link.btn-icon-only,
  /* Les deux corbeilles de la page Cabinet, ajoutees le 7 septembre 2026 pour
     la meme raison, trouvees en mesurant les rangees une a une au telephone.

     Ce fichier avait monte a la cible tactile chacun des controles d'une
     rangee de gestion, nommement et separement : le champ de nom, les
     initiales, la roue de couleur, le bouton d'enregistrement, la liste des
     roles. Il avait laisse de cote le seul des cinq qui detruit quelque chose.
     Les deux corbeilles restaient a 26x26 pour un cabinet et 28x44 pour un
     collaborateur, cote a cote sur la meme page, memes gestes, deux tailles,
     aucune a la cible -- pendant que tout ce qui les entoure faisait 44.

     Elles ne resistaient pas par choix mais par specificite : la feuille du
     grand ecran les mesure sous trois classes (`.cabinet-gestion-form >
     .btn-link.btn-icon-only`, et l'autre de meme), et deux planchers ecrits
     sous une seule classe ne peuvent rien contre ca. C'est le defaut que
     style.css nomme deja quelques lignes plus bas, a propos de --h-case :
     « une hauteur plus courte ecrite seule ne fait donc rien du tout ». Ici il
     jouait dans l'autre sens, et rien ne le disait. */
  .cabinet-gestion-form > .btn-link.btn-icon-only,
  .equipe-gestion-form > .btn-link.btn-icon-only {
    width: var(--m-tap);
    height: var(--m-tap);
  }

  /* Meme chose pour les deux boutons de la carte d'un intervenant, carres de
     --h-sm sur ordinateur : la feuille d'ordinateur y desarme le plancher pose
     plus haut sur .icon-btn, il faut donc redemander la cible tactile ici, sur
     les deux mesures. */
  .intervenant-actions > .icon-btn {
    width: var(--m-tap);
    height: var(--m-tap);
  }

  /* Un calendrier se vise au pouce : ses cases passent de 28 px a la cible
     tactile, et la seule mesure a changer est celle-la -- la grille, la
     hauteur d'une case et les deux fleches de mois s'y accrochent toutes.
     Le panneau atteint alors 344 px, ce qui tient dans un telephone de 390 ;
     en dessous, le plafond le rend a la page et les colonnes se resserrent
     d'elles-memes, parce qu'elles sont ecrites en `minmax(0, ...)`. */
  .date-panneau {
    --date-case: var(--m-tap);
    max-width: calc(100vw - var(--m-gutter) * 2);
  }

  /* ---------- 2. Barre de titre ------------------------------------------ */

  .app-header {
    background: var(--m-surface);
    backdrop-filter: saturate(180%) blur(18px);
    -webkit-backdrop-filter: saturate(180%) blur(18px);
    z-index: 40;
  }

  .app-header-inner {
    padding: var(--espace-xs) var(--m-gutter);
    padding-top: calc(var(--espace-xs) + var(--m-safe-top));
    gap: var(--espace-sm);
    min-height: 52px;
  }

  /* Le jambage du `p` d'Alpboard occupe le cinquieme bas du fichier : cale sur
     sa boite entiere, le mot rendait des capitales d'un cinquieme trop petites
     pour la barre. Il est agrandi d'autant, et le jambage repris en marge
     negative, qui laisse a la barre la hauteur qu'elle avait. */
  .app-logo { height: 29px; margin-bottom: -5px; }

  /* La version reste utile (le badge suit le deploiement) mais discrete. */
  .app-version {
    font-size: var(--fs-3xs);
    padding: var(--espace-3xs) var(--espace-xs);
  }

  /* La navigation du header part dans la barre basse : on la masque ici.
     On garde en revanche les controles metier (pastille d'avancement,
     tuile cabinet, icone de type) qui appartiennent au mandat ouvert. */
  .app-header .app-nav { display: none; }
  .app-header .app-nav-fin > .icon-btn,
  .app-header .app-nav-end > .icon-btn,
  .app-header > .app-header-inner > .icon-btn { display: none; }

  /* Sauf la cloche, qui reste dans la barre de titre.

     La barre d'onglets basse est pleine a cinq destinations : un sixieme
     libelle -- « Notifications », le plus long des six -- ne tient pas dans un
     sixieme d'ecran de 375 px, et il passerait a la ligne en poussant la
     hauteur de la barre. La cloche reste donc en haut, sans son libelle : une
     icone et sa pastille suffisent, c'est deja la forme qu'elle a dans toutes
     les applications ou elle existe.

     Elle est la seule des trois entrees de l'entete a y rester, et pour une
     raison : les deux autres se cherchent (on va aux messages, on va aux
     tickets), celle-ci se signale. Une pastille qu'on ne voit nulle part
     n'avertit personne. */
  .app-header .app-nav-fin > a[href="/notifications"].icon-btn {
    display: inline-flex;
    padding-left: var(--espace-xs);
    padding-right: var(--espace-xs);
  }

  .app-header .app-nav-fin > a[href="/notifications"] > span:not(.icon-btn-badge) {
    display: none;
  }

  /* La fenetre de reglages part de l'en-tete avec le reste de la
     navigation : elle s'ouvre au survol, et un telephone ne survole rien.
     Elle revient en bas, dans l'onglet Reglages de la barre, qui en recopie
     le contenu dans une feuille (voir `construireOngletReglages` dans
     mobile.js). Son panneau reste donc dans le document, seulement
     invisible : c'est la que la feuille va le chercher. */
  .app-header .app-reglages { display: none; }

  .app-nav-fin,
  .app-nav-end { margin-left: auto; }

  /* Voir le commentaire de `.dossier-header-right` dans style.css : la barre du
     telephone ne montre ni les destinations ni les reglages, il n'y reste pas
     six groupes a separer.

     Les trois qui restent -- la piste des annees, l'etat de celle qu'on
     regarde, la tuile du titulaire -- font 440 px a eux seuls. Le groupe passe
     bien a la ligne, `.app-header-inner` etant en `wrap`, mais une ligne ne
     retrecit pas ce qu'elle porte : dans un ecran de 375 il sortait par la
     droite de soixante-dix-neuf pixels, et la page ne defilait pas de cote.
     L'etat de l'exercice et le cabinet titulaire etaient donc hors d'atteinte
     sur un telephone.

     L'app fait depuis toujours ce qu'il fallait faire ici, et le dit :
     `BarreExercicesAudit.swift` pose la piste des annees dans un
     `ScrollView(.horizontal)` aux indicateurs masques et cale l'etat a sa
     droite, « sans cela, une seule annee occuperait toute la largeur et
     repousserait l'etat hors de l'ecran ». Meme geste ici, avec les memes
     pieces que les onglets du mandat plus bas dans ce fichier : la piste
     defile, sa barre de defilement est masquee en tete de ce fichier, et ce
     qui la suit garde sa taille.

     `flex: 1 1 100%` lui donne la ligne entiere plutot que le reliquat de la
     precedente : sans cela le groupe reste cale a droite par son
     `margin-left: auto` et la piste n'a plus que ce qui depasse du titre pour
     defiler. */
  .dossier-header-right {
    gap: var(--espace-xs);
    flex: 1 1 100%;
    min-width: 0;
  }

  .exercice-selecteur {
    flex: 1 1 auto;
    min-width: 0;
    overflow-x: auto;
    /* Le creux qui laisse passer l'ombre de l'onglet retenu sans que le
       defilement la coupe : `--shadow-sm` descend de trois pixels quand le
       rembourrage de la piste en fait deux et demi. L'app pose le meme creux
       pour la meme raison (`.padding(.vertical, Espaces.xxxs)`). */
    padding: var(--espace-2xs) var(--espace-3xs);
  }

  /* Le plancher du doigt sur le modele, que sa propre copie avait deja.

     La rangee de tri de la liste des mandats a ete dessinee sur ce selecteur
     -- « meme piste teintee bordee, meme ecart de 2 px entre les segments,
     meme onglet retenu en blanc releve par --shadow-sm », dit sa regle en
     section 6 --, et elle s'est donnee dans la meme phrase « meme cible de
     44 px ». Le modele, lui, ne l'a jamais prise : ses onglets tenaient sur
     27 px au telephone quand les cinq segments copies sur eux en font 44.

     Ce n'est pas une forme qui se transcrit ici mais une mesure : 44 px est
     la largeur d'un doigt, et changer d'exercice est le geste qui ouvre tout
     le reste de la fiche. */
  .exercice-onglet { min-height: var(--m-tap); }

  /* L'etat et le titulaire ferment le rang a leur taille : ce sont eux qu'on
     vise, la piste est ce qui defile. */
  .dossier-signataire,
  .dossier-header-right > .dossier-cabinet-tile { flex: none; }

  .app-titre,
  .dossier-detail-titre {
    font-size: var(--fs-md);
    min-width: 0;
    flex: 1 1 auto;
  }

  .dossier-detail-badges { gap: var(--espace-2xs); }

  /* Bouton retour injecte par mobile.js quand la page en a un dans sa nav. */
  .m-back {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Cible tactile pleine : c'est le bouton le plus utilise de l'interface,
       il ne peut pas etre le seul a rester sous les 44px. Le retrait negatif
       compense le rembourrage ajoute pour que l'icone reste alignee sur la
       gouttiere de la page. */
    width: var(--m-tap);
    height: var(--m-tap);
    flex: none;
    margin-left: -.6rem;
    border: 0;
    border-radius: 999px;
    background: none;
    color: var(--gray-6);
    cursor: pointer;
    transition: background .15s var(--ease);
  }
  .m-back:active { background: var(--gray-2); }
  .m-back .icon { width: var(--icon-xl); height: var(--icon-xl); stroke-width: 2; }

  /* ---------- 3. Barre d'onglets basse ----------------------------------- */

  .m-tabbar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 70;
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    background: rgba(255, 255, 255, .9);
    backdrop-filter: saturate(180%) blur(20px);
    -webkit-backdrop-filter: saturate(180%) blur(20px);
    border-top: 1px solid var(--gray-3);
    padding-bottom: var(--m-safe-bottom);
  }

  .m-tab {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--espace-3xs);
    height: var(--m-tabbar-h);
    text-decoration: none;
    color: var(--gray-5);
    font-size: var(--fs-3xs);
    font-weight: 600;
    letter-spacing: var(--suivi-serre);
    -webkit-tap-highlight-color: transparent;
    transition: color .15s var(--ease);
  }

  .m-tab .icon {
    width: var(--icon-xl);
    height: var(--icon-xl);
    stroke-width: 1.7;
    transition: transform .2s var(--ease-spring);
  }

  .m-tab:active .icon { transform: scale(.88); }
  .m-tab.active { color: var(--cyan-dark); }
  .m-tab.active .icon { stroke-width: 2.1; }

  /* Petit trait au-dessus de l'onglet actif, comme une app native. */
  .m-tab.active::before {
    content: "";
    position: absolute;
    top: 0;
    width: 26px;
    height: 2.5px;
    border-radius: 0 0 999px 999px;
    background: var(--cyan);
  }

  .m-tab-badge {
    position: absolute;
    top: 4px;
    left: 50%;
    margin-left: var(--espace-2xs);
    min-width: 16px;
    height: 16px;
    padding: 0 var(--espace-2xs);
    border-radius: 999px;
    background: var(--red);
    color: #fff;
    font-size: var(--fs-3xs);
    font-weight: 700;
    line-height: 16px;
    text-align: center;
  }

  /* La seconde pastille -- ce qui attend un geste -- se pose a droite de la
     premiere plutot que par-dessus, et garde l'orange de « À valider ». */
  .m-tab-badge-action {
    margin-left: 22px;
    background: var(--orange);
  }

  /* ---------- 4. Feuilles modales (bottom sheets) ------------------------ */

  .m-sheet-backdrop {
    position: fixed;
    inset: 0;
    z-index: 90;
    background: var(--voile-flou);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
    opacity: 0;
    transition: opacity .22s var(--ease);
  }
  .m-sheet-backdrop.open { opacity: 1; }

  .m-sheet {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 91;
    display: flex;
    flex-direction: column;
    max-height: 88vh;
    background: #fff;
    border-radius: var(--m-radius-sheet) var(--m-radius-sheet) 0 0;
    box-shadow: var(--shadow-lg);
    transform: translateY(100%);
    transition: transform .28s var(--ease);
    padding-bottom: var(--m-safe-bottom);
    touch-action: pan-y;
  }
  .m-sheet.open { transform: translateY(0); }

  .m-sheet-grip {
    flex: none;
    display: flex;
    justify-content: center;
    padding: var(--espace-sm) 0 var(--espace-2xs);
    cursor: grab;
  }
  .m-sheet-grip::before {
    content: "";
    width: 38px;
    height: 4px;
    border-radius: 999px;
    background: var(--gray-3);
  }

  .m-sheet-header {
    flex: none;
    display: flex;
    align-items: center;
    gap: var(--espace-sm);
    padding: var(--espace-2xs) var(--m-gutter) var(--espace-sm);
    border-bottom: 1px solid var(--gray-2);
  }

  .m-sheet-title {
    margin: 0;
    font-size: var(--fs-lg);
    font-weight: 700;
    flex: 1;
  }

  .m-sheet-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--m-tap);
    height: var(--m-tap);
    flex: none;
    border: 0;
    border-radius: 999px;
    background: var(--gray-1);
    color: var(--gray-5);
    cursor: pointer;
  }
  .m-sheet-close .icon { width: var(--icon-md); height: var(--icon-md); }

  /* Le corps qui defile d'une feuille est le corps qui defile d'une page :
     exactement le creux d'.app-main, plus bas dans « 5. Contenu ». Il ecrivait
     `.8rem` en haut -- la bonne valeur, mais a la main, donc elle ne suivrait
     pas le barreau s'il bougeait -- et 1.1rem en bas, qui ne tombe sur aucun. */
  .m-sheet-body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: var(--espace) var(--m-gutter) var(--espace-lg);
    overscroll-behavior: contain;
  }

  /* Le contenu deplace dans une feuille reprend une mise en page verticale. */
  .m-sheet-body .toolbar {
    display: block;
    margin: 0 0 var(--espace);
    overflow: visible;
  }
  .m-sheet-body .toolbar-group {
    display: flex;
    flex-wrap: wrap;
    gap: var(--espace-sm);
    margin-bottom: var(--espace);
  }
  .m-sheet-body .toolbar-label {
    width: 100%;
    margin-bottom: var(--espace-3xs);
  }

  /* Tout formulaire deplace dans une feuille se met en colonne pleine largeur,
     avec des cibles a --m-tap : c'est la seule facon de saisir au pouce. */
  .m-sheet-body input[type="text"],
  .m-sheet-body input[type="search"],
  .m-sheet-body input[type="number"],
  .m-sheet-body input[type="date"],
  .m-sheet-body select,
  .m-sheet-body textarea {
    width: 100%;
    min-height: var(--m-tap);
  }

  .m-sheet-body .btn-primary,
  .m-sheet-body .btn-secondary {
    width: 100%;
    min-height: var(--m-tap);
    justify-content: center;
  }

  /* "Annuler" est un lien texte sur ordinateur : au doigt il lui faut la
     meme surface que les autres actions, sinon il est increvable a viser. */
  .m-sheet-body .btn-link {
    display: flex;
    width: 100%;
    min-height: var(--m-tap);
    align-items: center;
    justify-content: center;
    margin-top: var(--espace-2xs);
  }

  /* ---------- 5. Contenu : main, panels, toolbars ------------------------ */

  .app-main {
    padding: var(--espace) var(--m-gutter) var(--espace-lg);
  }

  /* L'ecart entre deux blocs vaut l'air laisse apres le dernier : --espace-lg,
     celui que .app-main pose en bas trois lignes plus haut. Ces deux creux se
     lisent l'un apres l'autre en bas de page, et rien ne justifiait qu'ils
     different de 2,4 px. Il valait 17,6. */
  .panel { margin-bottom: var(--espace-lg); }

  .toolbar {
    /* Le champ de recherche prend sa propre rangee (cf. plus bas) ; les
       groupes de boutons se replient sur la ou les rangees suivantes. */
    flex-wrap: wrap;
    justify-content: flex-start;
    align-items: center;
    gap: var(--espace-xs);
    margin-bottom: var(--espace);
    padding-bottom: var(--espace-3xs);
  }

  /* Le talon ci-dessus est celui d'une barre posee a nu sur la page. Quand la
     barre ferme la tuile, il s'ajoute au creux du bas : le blanc sous elle
     vaut alors 2,4 px de plus que celui qui la separe du haut, et la tuile
     parait pencher. C'est le cas de la barre de commande des tickets, seule
     dans la sienne.

     Une barre qui en coiffe d'autres le garde. La regle disait `> .toolbar`
     sans plus de precision jusqu'au 30 aout 2026, et retirait donc aussi le
     talon de la barre de tete d'une liste -- la ou il ne tombe pas dans le
     creux de la tuile mais dans l'ecart avec la table.

     Le second selecteur est celui que la feuille emploie deja pour le meme
     besoin (`.tuile > :last-child`) : la barre de /dossiers est bien la
     derniere qu'on voie, mais un message d'erreur `[hidden]` la suit dans
     le document, et `:last-child` seul la manquait. */
  .tuile > .toolbar:last-child,
  .tuile > .toolbar:not([hidden]):not(:has(~ :not([hidden]))) {
    padding-bottom: 0;
  }

  /* Et une barre qui est elle-meme la tuile ne l'a pas davantage : son creux
     du bas est celui de la tuile, et le talon de 2,4 px le rognait a 2,4 pour
     16 en haut. C'est le cas de la barre de commandes de l'analyse. */
  .toolbar.tuile { padding-bottom: var(--espace-md); }

  /* Un groupe garde sa taille naturelle tant qu'il tient, et la barre replie
     autour de lui : c'est ce que disait `flex: none`. Mais `none`, c'est aussi
     `flex-shrink: 0`, et un groupe plus large que l'ecran entier ne pouvait
     alors plus rien faire -- ni retrecir, ni plier -- que sortir par la droite.
     Les cinq fenetres de periode de la page de consommation faisaient 373 px a
     elles seules, et la page entiere defilait lateralement de 18 px.

     `0 1 auto` garde la premiere moitie de l'intention : le retrecissement ne
     s'applique qu'a ce qui deborde deja sa ligne, et comme la barre replie, un
     groupe trop long recoit d'abord une ligne pour lui seul. Il ne se resserre
     donc que contre la largeur de l'ecran, jamais contre un voisin. */
  .toolbar-group { flex: 0 1 auto; min-width: 0; flex-wrap: wrap; gap: var(--espace-xs); }

  /* Le trait qui separe deux zones de la barre n'a plus rien a separer ici.
     La barre replie : deux groupes voisins dans le document se retrouvent
     l'un sous l'autre, et un trait vertical entre deux rangees empilees ne
     dit rien. Pire, `mobile.js` remonte la recherche en tete (`order: -1`) :
     l'ordre a l'ecran n'est plus celui du document, donc le trait ne tombe
     meme plus la ou il croit tomber.

     Meme motif que la feuille de filtres plus bas, ou il restait « un trait
     vertical d'un pixel flottant a gauche des carres d'etat, rattache a
     rien ». */
  .toolbar:has(.toolbar-search-group) > .toolbar-group + .toolbar-group::before {
    content: none;
  }

  /* mobile.js remonte la recherche en tete (order: -1). Sur une rangee
     partagee elle tombait a 139px, illisible : on lui donne la rangee
     entiere. `flex-basis: 100%` force le retour a la ligne des suivants. */
  .toolbar-search-group {
    flex: 1 0 100%;
    min-width: 0;
  }

  /* La boite de recherche est bordee et l'entoure de 2px : c'est le champ qui
     recoit le focus, pas la boite, donc c'est lui qui doit porter la cible. */
  .toolbar-search input { min-height: var(--m-tap); }

  .toolbar-label { font-size: var(--fs-sm); }

  /* Rangee de puces horizontale (filtres, annees, cycles...). */
  .m-chiprow {
    display: flex;
    gap: var(--espace-xs);
    overflow-x: auto;
    padding: var(--espace-3xs) 0 var(--espace-xs);
    margin: 0 calc(var(--m-gutter) * -1) var(--espace-sm);
    padding-left: var(--m-gutter);
    padding-right: var(--m-gutter);
  }

  .m-chip {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: var(--espace-xs);
    height: var(--m-tap);
    padding: 0 var(--espace);
    border: 1px solid var(--gray-3);
    border-radius: 999px;
    background: #fff;
    color: var(--gray-6);
    font-family: var(--sans);
    font-size: var(--fs-sm);
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: background .15s var(--ease), border-color .15s var(--ease), color .15s var(--ease);
  }
  .m-chip .icon { width: var(--icon); height: var(--icon); }
  .m-chip:active { transform: scale(.97); }
  .m-chip.active {
    background: var(--cyan-pale);
    border-color: var(--cyan);
    color: var(--cyan-dark);
  }
  .m-chip-count {
    display: inline-block;
    min-width: 18px;
    padding: 0 var(--espace-2xs);
    border-radius: 999px;
    background: var(--gray-2);
    color: var(--gray-5);
    font-size: var(--fs-2xs);
    line-height: 18px;
    text-align: center;
  }
  .m-chip.active .m-chip-count { background: var(--cyan); color: #fff; }

  /* Bouton d'action flottant (nouveau mandat, nouveau ticket...). */
  .m-fab {
    position: fixed;
    right: var(--m-gutter);
    bottom: calc(var(--m-tabbar-hors-tout) + 14px);
    z-index: 65;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--espace-xs);
    height: 50px;
    min-width: 50px;
    padding: 0 var(--espace-md);
    border: 0;
    border-radius: 999px;
    background: var(--cyan);
    color: #fff;
    font-family: var(--sans);
    font-size: var(--fs);
    font-weight: 700;
    box-shadow: 0 8px 22px rgba(0, 174, 239, .34), var(--shadow-md);
    cursor: pointer;
    transition: transform .18s var(--ease-spring), box-shadow .18s var(--ease);
  }
  .m-fab .icon { width: var(--icon-lg); height: var(--icon-lg); stroke-width: 2.2; }
  .m-fab:active { transform: scale(.94); }

  /* ---------- 6. Listes de mandats --------------------------------------- */

  /* Filtres et formulaire de creation partent dans des feuilles modales :
     mobile.js les deplace, on masque juste la version en ligne restante. */
  #filtre-tuiles,
  #filtre-tuiles-cabinets,
  #filtre-etats { flex-wrap: wrap; }

  .filtre-tuile,
  .filtre-etat-tuile { min-width: 38px; min-height: 38px; }

  /* Le rond grandit au doigt : ses initiales suivent, sans quoi elles
     resteraient calees sur les 34 px du bureau. */
  .filtre-tuile { --avatar-d: 38px; }

  /* Sur ordinateur ces quatre boutons sont un en-tete de colonnes, et ils en
     ont la typographie : petites capitales grises espacees, calees par des
     marges fixes pour tomber pile au-dessus du nom, de l'etat, de l'exercice
     et du titulaire. La-bas c'est juste.

     Ici il n'y a pas de colonne -- la ligne mobile est sur deux rangees --, et
     la meme rangee de capitales posee au-dessus d'une liste de cartes annoncait
     des colonnes qui n'existent pas : « NOM » au-dessus d'un chevron,
     « EXERCICE » au-dessus du nom du client, « TITULAIRE » au-dessus du vide.
     L'intention etait deja ecrite ici -- « une simple paire trier par » --,
     mais seules les marges avaient ete defaites : le dessin, lui, continuait
     d'annoncer un tableau.

     On lui donne donc la forme de ce qu'elle est, et le produit en a deja une :
     le selecteur segmente de l'en-tete de la fiche mandat (`.exercice-selecteur`
     dans style.css). Meme piste teintee bordee, meme ecart de 2 px entre les
     segments, meme onglet retenu en blanc releve par --shadow-sm. Le rayon, lui,
     passe a 999px : celui du selecteur d'exercice vaut --radius-sm pour une
     barre haute de 28 px et parait carre a 50, et la rangee de filtres juste
     au-dessus est deja ronde.

     Rien ne change cote comportement : memes boutons, meme ordre, meme cible de
     44 px, meme chevron qui dit le sens.

     La piste, elle, descend d'un barreau : le sol de la page vaut --gray-1
     depuis le 31 aout 2026, et un creux ne se lit que s'il est plus sombre que
     ce sur quoi il est pose. Meme geste, meme raison, que la barre d'onglets
     plus bas. */
  .dossier-list-header {
    display: flex;
    align-items: center;
    gap: var(--espace-2xs);
    padding: var(--espace-3xs);
    margin-bottom: var(--espace-sm);
    background: var(--gray-2);
    border: 1px solid var(--gray-3);
    border-radius: 999px;
  }

  /* Les largeurs et marges fixes de l'ordinateur visaient des colonnes ; a leur
     place, quatre segments egaux qui se partagent la piste. Le selecteur passe
     par `[data-sort]` pour rien d'autre que le poids : les reperes de colonnes
     sont ecrits en (0,3,0) dans style.css, et une regle plus legere les laissait
     intacts -- « Titulaire » restait large de 42 px, cale a 83 px de son
     voisin, au milieu d'une piste par ailleurs bien segmentee. */
  .dossier-list-header .dossier-sort-btn[data-sort] {
    flex: 1;
    min-width: 0;
    width: auto;
    margin: 0;
    justify-content: center;
    padding: 0 var(--espace-xs);
    border-radius: 999px;
    font-size: var(--fs-xs);
    text-transform: none;
    letter-spacing: normal;
  }

  .dossier-list-header .dossier-sort-btn.active {
    background: #fff;
    color: var(--black);
    font-weight: 600;
    box-shadow: var(--shadow-sm);
  }

  /* Le chevron du sens n'est cache qu'a l'opacite sur ordinateur : il garde sa
     place dans les quatre en-tetes, ce qui les laisse tous alignes sur la meme
     colonne. Dans un segment centre il fait l'inverse -- 16 px reserves a droite
     poussent les quatre libelles d'autant a gauche de leur milieu. Les segments
     ayant ici une largeur egale que leur contenu ne decide pas, le retirer des
     inactifs ne bouge rien d'autre que le centrage. */
  .dossier-list-header .dossier-sort-btn:not(.active) .dossier-sort-icon {
    display: none;
  }

  /* La carte que decrit le commentaire de `.tuile-liste`, quelques regles plus
     bas -- « la liste est deja une carte a cette largeur » -- portait son
     cadre, son arrondi et son ombre, mais pas son fond : elle empruntait le
     blanc du corps. Le sol etant passe au gris (voir `body` dans style.css),
     elle reprend le sien, sans quoi ses cent trente-quatre compteurs de FEC,
     gris eux aussi, s'y effaceraient. */
  .dossier-list {
    border: 1px solid var(--gray-3);
    border-radius: var(--radius-lg);
    background: #fff;
    overflow: hidden;
    box-shadow: var(--shadow-sm);
  }

  /* Le filet qui separe deux lignes est plus clair ici que sur ordinateur : la
     liste est une carte, et un --gray-3 a l'interieur d'un cadre --gray-3 pese
     autant que le cadre. Il etait pose en haut de la ligne du dessous, sans
     que celle du dessus perde le sien : chaque separation faisait deux pixels
     de deux gris differents. Il n'en reste qu'un, celui du bas, avec la teinte
     qu'avait l'autre. */
  .dossier-list > .dossier-item { border-bottom-color: var(--gray-2); }

  /* La tuile qui reunit la rangee de tri et la liste sur ordinateur s'efface
     ici. Deux raisons, chacune suffisante.

     La liste est deja une carte a cette largeur -- celle juste au-dessus --, et
     la rangee de tri un controle segmente : un cadre de plus autour des deux
     serait un cadre autour d'un cadre.

     Et le creux d'une tuile fait quarante pixels de large. La ligne, elle, est
     mesuree au pixel : son seul groupe d'etat en pese 171, et c'est en rendant
     seize pixels -- l'ecart entre ses colonnes et son blanc lateral, remis sur
     leur barreau -- qu'elle a cesse de sortir de la liste par la droite sous
     380 px, comme le dit la regle plus bas. Quarante de moins l'y
     renverraient, avec le nom du mandat. */
  .tuile-liste {
    padding: 0;
    border: 0;
    background: none;
  }

  /* Sur ordinateur la ligne de mandat tient sur une seule rangee : tuile
     cabinet, icone de type, nom, compteur FEC, etat, assigne. A 375px, les
     cinq elements de droite sont incompressibles (~230px) et etranglaient le
     nom a 52px : soit deux mots par ligne pour une raison sociale.
     On passe donc sur deux rangees : le nom occupe seul la premiere, en pleine
     largeur ; tout le reste devient une ligne de metadonnees en dessous.
     Aucune information n'est retiree, elle est seulement re-repartie. */
  /* Les quatre colonnes de la ligne et le groupe d'etat qui occupe la
     derniere ne se compriment pas : ce qui reste pour les separer, ce sont
     l'ecart entre elles et le blanc lateral. Ecrits a la main (8 px et 12),
     ils prenaient un cran de plus que le barreau voisin de l'echelle, et ces
     seize pixels manquaient a la ligne des que l'ecran descendait sous
     380 px -- la ligne sortait alors de la liste par la droite. Remis sur
     leur barreau, ils lui en rendent presque dix (27 aout 2026). */
  /* La grille change la voie du nom : le logo et la pastille de type ne sont
     plus devant lui, c'est la tuile du cabinet qui l'est, sur ses deux
     rangees. Deux termes a reecrire, le reste de la somme ne bouge pas --
     et la liste depliee des exercices suit sans qu'on la touche. */
  .dossier-item {
    --dossier-voie-nom: calc(
        var(--espace-2xs) + var(--icon-sm) + var(--espace-3xs)
      + var(--espace-xs)
      + var(--espace-sm)
      + 38px + var(--espace-xs));
  }

  .dossier-link {
    display: grid;
    grid-template-columns: auto auto auto minmax(0, 1fr);
    align-items: center;
    column-gap: var(--espace-xs);
    row-gap: var(--espace-2xs);
    padding: var(--espace-sm);
    min-height: 58px;
  }

  /* La tuile cabinet sert de reperage visuel de la ligne entiere : elle
     s'etend sur les deux rangees, comme une vignette. */
  .dossier-cabinet-slot {
    grid-column: 1;
    grid-row: 1 / span 2;
    width: 38px;
  }

  .dossier-nom {
    grid-column: 2 / -1;
    grid-row: 1;
    font-size: var(--fs-md);
    font-weight: 600;
    line-height: 1.25;
    /* Deux lignes maximum : les raisons sociales sont souvent longues. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    white-space: normal;
  }

  .dossier-icon { grid-column: 2; grid-row: 2; }

  .dossier-fec-count {
    grid-column: 3;
    grid-row: 2;
    font-size: var(--fs-2xs);
    /* Annule le `margin-left: auto` du bureau, qui poussait le compteur au
       bout de la rangee flex ; ici la grille place deja les colonnes. */
    margin-left: 0;
  }

  /* Les trois carres d'avancement (urgent / en cours / termine) font 28px et
     sont separes de 6px : au doigt, une ligne sur trois change d'etat par
     erreur, et c'est une action qui s'ecrit en base sans confirmation. On les
     amene a la cible standard, ce qui porte le rang de 96 a 144px.

     La derniere colonne peut descendre sous son contenu (`minmax(0, 1fr)`
     plus haut) et le groupe qui l'occupe passe a la ligne : sous 380 px les
     quatre pastilles -- etat, exercice, collaborateurs, signataire -- font
     171 px a elles seules, et la rangee sortait de la liste par la droite de
     dix-huit pixels, en emmenant le nom du mandat avec elle. Sans `wrap`, une
     colonne qui peut retrecir ne fait que rogner ce qu'elle contient.

     Au-dessus de 380 px la ligne tient et rien ne passe a la ligne : ces deux
     mots ne changent que ce qui ne rentrait pas. C'est le meme geste que le
     passage a deux rangees decrit plus haut -- l'information est re-repartie,
     jamais retiree. */
  .dossier-statut-group {
    grid-column: 4;
    grid-row: 2;
    justify-self: start;
    flex-wrap: wrap;
    gap: var(--espace-xs);
  }

  /* Le slot a une largeur figee sur ordinateur -- --col-statut, celle du rang
     de trois carres qui dit le meme etat -- pour que le badge cadenas, plus
     etroit que le rang, ne decale pas le compteur FEC de la ligne voisine.
     A 144px le rang en debordait et passait sous l'avatar du collaborateur ;
     ici la grille aligne deja les colonnes, la largeur fixe ne sert a rien. */
  .dossier-statut-slot { width: auto; }
  .avancement-seg { width: var(--m-tap); height: var(--m-tap); }

  /* Les collaborateurs secondaires reprennent depuis V3.35 le meme groupe de
     bulles chevauchees horizontalement qu'sur ordinateur (22px, deja lisible
     au doigt sans infobulle) : plus de reglage mobile specifique necessaire. */

  /* Le chevron mange de la place pour rien : la ligne entiere est cliquable. */
  .dossier-chevron { display: none; }

  .dossier-empty { padding: var(--espace-xl) var(--espace); text-align: center; }

  /* Formulaire de creation, quand il reste en ligne. */
  .dossier-form {
    display: flex;
    flex-direction: column;
    gap: var(--espace);
    align-items: stretch;
  }
  /* dossiers.js ouvre/ferme le formulaire via l'attribut hidden. */
  .dossier-form[hidden] { display: none; }
  .dossier-form input,
  .dossier-form select { width: 100%; }

  /* ---------- 7. Detail mandat ------------------------------------------- */

  /* Onglets Analyse / Source / Mandat en controle segmente.

     `1fr` partout tant qu'ils tiennent, `max-content` au minimum : ecrit pour
     trois onglets, ce controle en porte sept depuis le 26 aout 2026 (Conso,
     Mandat, DA, Comptable). Sept colonnes egales font 49 px chacune quand
     « Comptable » en demande 65 : les libelles se cassaient en deux lignes et
     « DA » depassait de la pastille sans que rien ne permette d'y aller --
     `overflow-x` valait `visible`, et la page ne defilait pas non plus.

     Le `1fr` reste : il vaut `minmax(auto, 1fr)`, et `auto` sur une piste de
     grille prend pour plancher la taille min-content de son element -- soit,
     le `white-space: nowrap` ci-dessous aidant, le libelle entier. Les
     segments restent donc egaux tant qu'ils tiennent (deux onglets dans
     Messages, cinq dans Mapping) et debordent sans s'ecraser sinon.
     `overflow-x: auto` est la piece qui manquait pour aller chercher ce
     debordement : la regle qui masque la barre de defilement, en tete de ce
     fichier, le supposait deja. */
  .dossier-tabs {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    overflow-x: auto;
    gap: var(--espace-3xs);
    margin-bottom: var(--espace-md);
    padding: var(--espace-3xs);
    border: 0;
    border-radius: 999px;
    /* Le creux d'un controle segmente ne se lit que s'il est plus sombre que
       ce qui l'entoure. Il valait --gray-1 quand la page etait blanche ; le sol
       a pris cette teinte le 31 aout 2026 (voir `body` dans style.css), et le
       creux comme son filet interieur descendent chacun d'un barreau pour
       garder l'ecart qu'ils avaient. */
    background: var(--gray-2);
    box-shadow: inset 0 0 0 1px var(--gray-3);
  }

  .dossier-tab-btn {
    position: relative;
    /* Les memes boutons servent d'onglets au mapping, hors controle segmente :
       ils y restaient a 33px de haut. */
    min-height: var(--m-tap);
    padding: var(--espace-sm) var(--espace-xs);
    border: 0;
    border-radius: 999px;
    /* Un onglet est une etiquette, pas un paragraphe. */
    white-space: nowrap;
    font-size: var(--fs-sm);
    color: var(--gray-5);
    margin-bottom: 0;
    transition: background .18s var(--ease), color .18s var(--ease);
  }

  .dossier-tab-btn.active {
    background: #fff;
    color: var(--black);
    border-bottom-color: transparent;
    box-shadow: var(--shadow-sm);
  }

  /* Le point de statut reste en ligne apres le libelle, comme sur ordinateur.
     Il y avait ici un `top: 6px; right: 8px` qui ne faisait rien : la pastille
     n'est `position: absolute` nulle part, donc les deux valeurs etaient
     ignorees. Elle restait en ligne, et dans une colonne de 49 px elle passait
     a la ligne suivante -- c'est ce qui faisait flotter « Source » huit pixels
     plus haut que ses voisins. Les colonnes ayant repris leur largeur
     naturelle, la place est revenue et le decalage a disparu. */

  /* Onglet Source : la zone d'import remonte au-dessus de la liste des fichiers.
     Sur ordinateur la liste tient dans l'ecran et la zone reste visible en
     dessous ; sur telephone, quatre exercices font deja 1 000px de cartes et il
     fallait defiler jusqu'en bas pour importer un FEC : l'action principale de
     l'onglet. Le [hidden] doit etre retabli explicitement : le `display: flex`
     ci-dessous l'emporterait sur le [hidden] par defaut du navigateur et les
     deux autres onglets afficheraient leur contenu en meme temps. */
  #tab-panel-source { display: flex; flex-direction: column; }
  #tab-panel-source[hidden] { display: none; }
  #tab-panel-source > #drop-section { order: -1; }

  /* Zone de depot des FEC. */
  .drop-zone { padding: var(--espace-xl) var(--espace-md); }
  .drop-zone-icon { width: 30px; height: 30px; }
  .drop-zone-title { font-size: var(--fs-md); }
  .drop-zone-sub { font-size: var(--fs-xs); }

  /* Le groupe qui relie le nom d'une piece a ses gestes ne sert qu'a
     l'ordinateur, ou la rangee aligne tout a la meme distance. Ici la rangee
     est une pile et c'est l'empilement qui dit le regroupement : le groupe se
     dissout, ses enfants redeviennent les elements de la rangee, et la
     disposition du telephone reste celle du 26 aout 2026, au pixel pres.

     `display: contents` ne defait que la boite, pas l'arbre : les selecteurs
     ci-dessous doivent quand meme traverser le groupe, d'ou les
     `> .reglages-piece >` qui doublent chaque combinateur direct. */
  .reglages-piece { display: contents; }

  /* Reglages du mandat : tout passe en colonne. */
  .reglages-row {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--espace-xs);
  }
  .reglages-row input,
  .reglages-row select,
  .reglages-row .btn-primary { width: 100%; }
  /* Le champ de date du volet DA porte une largeur de 11rem, posee pour le
     tenir a cote du selecteur de cloture. Ecrite avec assez de poids pour
     passer devant la ligne du dessus, elle la traversait aussi ici, ou il n'y
     a plus de rangee : une case a demi large entre un selecteur et un bouton
     qui prennent toute la colonne. Une largeur qui dit « pas plus que mon
     voisin » n'a plus rien a dire quand il n'y a plus de voisin. */
  .reglages-row .aglae-input-exercice[type="date"] { width: 100%; }
  /* Nom, SIREN, jour et mois de cloture heritaient du champ dense d'ordinateur
     (41px). Ce sont les seuls champs de saisie du mandat : ils prennent la
     cible tactile ; le corps de leur texte leur vient de la regle unique des
     fondations. Le jour et le mois gardent leur width: 4.5em en style inline, que
     la regle ci-dessus ne peut de toute facon pas surcharger. */
  .reglages-row input,
  .reglages-row input[type="text"],
  .reglages-row input[type="number"],
  .reglages-row select { min-height: var(--m-tap); }
  /* Meme plancher, meme raison, pour la liste deroulante qui porte
     `.reglages-input-grow`. La feuille d'ordinateur la nomme avec une classe
     de plus -- `.reglages-row select.reglages-input-grow`, posee le 29 aout
     2026 pour la caler sur --h-champ a cote des cases de saisie --, et cette
     classe de plus passe devant la ligne du dessus quel que soit l'ordre des
     fichiers. Le groupe du mandat et la tete du groupe restaient donc a 34 px
     sur le telephone, seules de leur panneau, pendant que les cases voisines
     montaient a 44. Une hauteur d'ordinateur qui survit au telephone parce
     qu'elle est ecrite avec un poids de plus n'est pas un arbitrage. */
  .reglages-row select.reglages-input-grow { min-height: var(--m-tap); }

  /* Les structures d'un groupe (`/groupes/<groupe_id>`), la ou l'on vient les
     lire et corriger le rattachement. Mesure au navigateur a 375 px : la
     rangee faisait 24 px, le nom 19, et la croix qui retire une structure du
     groupe 18 sur 18 -- la plus petite cible du site, et la seule qui detruise
     quelque chose.

     La croix prend le carre de 44, comme toutes les corbeilles de la feuille.
     Le nom prend la meme hauteur par sa `line-height` et non par un affichage
     en boite flexible : la regle d'ordinateur le coupe avec des points de
     suspension quand il depasse -- « c'est lui qu'on coupe quand la ligne est
     trop longue, jamais le geste » --, et une boite flexible les lui prendrait
     en faisant de son texte un enfant anonyme. La rangee, elle, rend son creux
     vertical : ses deux enfants portent desormais la mesure, et l'ajouter au
     leur ferait une rangee de 57 px.

     Rien a porter dans l'app : l'ecran d'un groupe est arbitre `web_seul` dans
     `app/parite.json` -- ouvrir, renommer et fermer un groupe est un acte sur
     le portefeuille, et il se fait de la ou l'on voit le portefeuille entier.
     Le selecteur de groupe du mandat, lui, existe des deux cotes : c'est un
     `Picker` de `VueReglagesMandat.swift`, pose par iOS sur une rangee de
     liste, donc deja au plancher. */
  .reglages-groupe-membres-liste li {
    padding-top: 0;
    padding-bottom: 0;
  }
  .reglages-groupe-membre-nom {
    min-height: var(--m-tap);
    line-height: var(--m-tap);
  }
  .reglages-groupe-membre-retirer {
    width: var(--m-tap);
    height: var(--m-tap);
  }

  /* Une rangee qui finit par des boutons a icone cesse d'etre une colonne
     pure. Empiles, ces boutons-la prenaient chacun une ligne entiere pour
     44 px de dessin : le justificatif de la nomination en alignait trois l'un
     sous l'autre, contre le bord gauche, avec trois cents pixels de vide a
     cote de chacun -- trois carres qui ne se lisaient plus comme les trois
     gestes d'un meme fichier, et le logo comme la charte laissaient leur
     corbeille seule au bas de la pile.

     La rangee repasse donc en ligne qui se replie. Ce qui etait pleine largeur
     le reste -- une base de 100 % le garde, la ou l'etirement transversal de
     la colonne ne joue plus --, et les boutons a icone, larges de 44, se
     rangent cote a cote sur la ligne qui suit. */
  .reglages-row:has(> .btn-link.btn-icon-only, > .reglages-piece > .btn-link.btn-icon-only) {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
  }
  /* La rangee de la photo de « Mon compte » est ecartee des deux lignes qui
     peuvent l'atteindre : elle a sa propre mise en page, plus bas dans ce
     fichier, et sans cette exception cette liste-ci la lui prend. Les trois
     autres lignes ne la concernent pas -- une rangee de photo ne contient ni
     pastille de charte ni piece jointe --, elles restent donc telles quelles. */
  .reglages-row:not(.compte-photo-ligne):has(> .btn-link.btn-icon-only, > .reglages-piece > .btn-link.btn-icon-only) > .btn-link:not(.btn-icon-only),
  .reglages-row:has(> .btn-link.btn-icon-only, > .reglages-piece > .btn-link.btn-icon-only) > .charte-couleur,
  .reglages-row:has(> .btn-link.btn-icon-only, > .reglages-piece > .btn-link.btn-icon-only) > .charte-apercu,
  .reglages-row:has(> .btn-link.btn-icon-only, > .reglages-piece > .btn-link.btn-icon-only) > .reglages-piece > .btn-link:not(.btn-icon-only),
  .reglages-row:not(.compte-photo-ligne):has(> .btn-link.btn-icon-only, > .reglages-piece > .btn-link.btn-icon-only) > .reglages-hint-inline {
    flex: 0 0 100%;
  }

  /* Les boutons a icone ferment le bloc a droite, comme la croix d'un membre
     de l'equipe et les deux boutons d'une adresse. Au bord gauche, sous une
     pile de boutons pleine largeur, un carre de 44 px se lit comme une ligne
     qui n'a pas su se remplir ; au bord droit, comme le geste plus discret qui
     termine le bloc.

     Le cadre du logo est la seule chose de ces rangees qui ne fasse ni la
     pleine largeur ni un bouton : sans cette exception, l'apercu du logo
     partait a droite avec eux, loin du titre qui l'annonce. */
  .reglages-row:has(> .btn-link.btn-icon-only, > .reglages-piece > .btn-link.btn-icon-only) > .logo-client-cadre {
    margin-right: auto;
  }

  /* Une rangee de rangs de carres reste une rangee. Deux groupes poses cote a
     cote y disent quelque chose que la colonne defait : « Type de mission »
     aligne d'abord les deux carres qui s'excluent -- audit legal ou audit
     contractuel --, puis, apres l'ecart de la rangee, celui de la
     consolidation, qui s'ajoute a l'un ou a l'autre. Le gabarit le dit dans
     ses propres termes : « l'ecart de `.reglages-row` le separe sans le
     detacher de la rubrique ».

     En colonne, cet ecart devient un saut de ligne. Le carre de la
     consolidation tombait donc seul contre le bord gauche, sous une paire de
     la meme taille, avec deux cent soixante-huit pixels de vide a cote de lui
     -- non plus le troisieme geste de la rubrique, mais un reglage sans titre
     pose en dessous. La lecture exactement inverse de celle qui est demandee.

     Les deux autres rubriques de carres -- forme juridique, etats financiers
     -- n'ont qu'un groupe et ne bougent pas. Celle-ci tient en 146 px la ou le
     panneau en offre 362 sur un telephone de 390, et 292 sur un de 320 : elle
     ne se replie sur aucune largeur ou le site s'affiche. */
  .reglages-row:has(> .type-pill) {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
  }
  /* Les champs du volet CNCC prennent la cible tactile, comme tous les autres
     champs du panneau. Ils ne l'avaient jamais recue : mesures a 34,78 px sur
     un telephone le 27 aout 2026, cinquante-trois cases a remplir dans le seul
     bloc du panneau qui restait sous le doigt, entre des rangees natives a 44.
     Deux tailles de case empilees dans un meme panneau, et la plus petite est
     celle qu'on remplit le plus longtemps.

     La date fixe sa hauteur au lieu d'un plancher : c'est le seul champ que
     `--h-champ` fait descendre sur ordinateur, et sans cette ligne il
     resterait a 32 px au milieu de voisines a 44. La plaque de la reponse
     deduite suit ses cases -- c'est la rangee qu'elle complete. */
  .aglae-input:not([type="checkbox"]),
  .aglae-calcul { min-height: var(--m-tap); }
  .aglae-input[type="date"] { height: var(--m-tap); }

  .reglages-group { margin-bottom: var(--espace-xl); }
  .reglages-hint { font-size: var(--fs-xs); }

  /* Intervenants et presence en ligne : les deux listes libres du panneau
     tenaient leur grille d'ordinateur telle quelle. A quatre colonnes on
     lisait deja mal ; le telephone ajoute le 26 aout 2026 en fait six, et
     « 06 12 34 56 78 » ne tient pas dans un sixieme de telephone. Chaque
     ligne devient donc une pile de champs pleine largeur, separee de la
     suivante par un filet -- sans quoi douze champs a la file ne diraient
     plus ou finit une personne et ou commence la suivante. */
  .reglages-intervenant-row,
  .reglages-presence-row {
    grid-template-columns: 1fr;
    /* La meme valeur qu'avant, ecrite avec le barreau qui la porte : une pile
       de champs se serre plus qu'une rangee du panneau. */
    gap: var(--espace-xs);
    padding-bottom: var(--espace);
    border-bottom: 1px solid var(--gray-2);
  }
  .reglages-intervenant-row:last-child,
  .reglages-presence-row:last-child { border-bottom: none; padding-bottom: 0; }

  /* Les deux boutons d'une adresse -- l'ouvrir, la retirer -- reviennent sur
     une meme ligne, au bout de la pile. En une seule colonne ils prenaient
     chacun la leur : la fleche seule contre le bord gauche, la croix seule
     contre le bord droit, deux bandes de 44 px ou il n'y avait rien d'autre
     et deux boutons qui ne se rattachaient plus a rien. Une premiere colonne
     laissee vide les pousse cote a cote a droite, ou ils se lisent comme ce
     qu'ils sont : les deux gestes de l'adresse ecrite juste au-dessus. */
  /* Une colonne de plus en tete depuis le 30 aout 2026 : l'icone de la nature
     du lien. Elle reste sur la ligne de la liste deroulante, ou elle a son sens
     -- c'est ce qu'elle illustre --, et non seule au-dessus de la pile. */
  .reglages-presence-row { grid-template-columns: auto 1fr auto auto; }
  .reglages-presence-row > select { grid-column: 2 / -1; }
  .reglages-presence-row > input { grid-column: 1 / -1; }
  .reglages-presence-row > .reglages-presence-icone { grid-column: 1; }
  .reglages-presence-row > .reglages-presence-ouvrir { grid-column: 3; }
  .reglages-presence-row > .reglages-presence-retirer { grid-column: 4; }
  .reglages-intervenant-row input,
  .reglages-presence-row input,
  .reglages-presence-row input[type="text"],
  .reglages-presence-row input[type="url"],
  .reglages-presence-row select { min-height: var(--m-tap); }
  /* Le bouton de retrait revient a droite : pleine largeur, un rond de 28px
     centre sur la ligne ressemblait a un champ vide de plus. */
  .reglages-intervenant-retirer { justify-self: end; }

  /* La carte d'un intervenant lue -- le rond, le nom, puis les coordonnees --
     tenait ses trois colonnes cote a cote. Sur un telephone, les coordonnees
     n'en recevaient qu'un tiers, et une pastille ne se coupe pas : une adresse
     comme « jscorbeels.acbbnatation@gmail.com » depassait le panneau de 120 px
     et faisait glisser toute la page vers la droite. Les coordonnees passent
     donc sous le nom, ou elles ont la largeur entiere de la carte.

     Deux precautions vont avec le retour a la ligne. Le nom prend `flex-basis:
     0` : sans cela « Jean-Sebastien CORBEELS » etait plus large que la place
     restante a cote du rond, et une ligne flexible se coupe avant de se
     retrecir -- le rond restait seul sur sa ligne, le nom dessous. Et la carte
     s'aligne en haut : ses deux boutons, centres sur une carte devenue haute,
     flottaient au milieu du vide a cote des coordonnees.

     La pastille garde `max-width` en garde-fou : c'est ce qui empeche une
     adresse encore plus longue de recommencer a pousser la page. */
  .intervenant-carte { align-items: flex-start; }
  .intervenant-lecture { flex-wrap: wrap; }
  .intervenant-identite { flex: 1 1 0; }
  .intervenant-details { flex: 1 0 100%; }

  /* Le garde-fou ci-dessus arrete bien la page, mais il coupait les adresses
     sans le dire. Une pastille est un element flexible : a defaut de place elle
     se retrecit avant que la ligne ne se replie, et `overflow: hidden` tranchait
     ce qui depassait -- sans points de suspension, `text-overflow` valant
     `clip`. A 320 px, neuf des vingt pastilles etaient coupees, jusqu'a 81 px :
     « jscorbeels.acbbnatation@gmail.com » s'affichait comme une adresse en
     « .co », complete a l'oeil et fausse a la lecture, et « Egalement dans 3
     autres mandats » s'arretait sur « man ».

     Une coordonnee ne se devine pas : elle se lit en entier ou elle ne sert a
     rien. La pastille cesse donc de se retrecir -- c'est la ligne qui se replie,
     et chaque adresse trop longue prend la sienne --, et si une seule d'entre
     elles depassait encore la largeur du telephone, elle passe a la ligne dans
     sa propre pastille au lieu d'etre tranchee. Le garde-fou reste : rien ne
     pousse plus la page qu'avant.

     L'app coupe au meme endroit, mais elle le montre (`.truncationMode(.middle)`
     dans `VueReglagesMandat`) et l'adresse y reste lisible dans son champ de
     saisie. C'etait le site, seul, qui coupait en silence. */
  .intervenant-detail {
    max-width: 100%;
    overflow: hidden;
    flex-shrink: 0;
    white-space: normal;
    overflow-wrap: anywhere;
  }

  /* La fonction est une pastille comme les autres, et se replie comme elles.
     Elle avait garde son `nowrap` d'ordinateur : une pastille qui ne se replie
     ni ne se retrecit depasse sa voie des que la voie retrecit. La sienne l'a
     fait le 30 aout 2026, quand la carte de l'intervenant est passee dans une
     tuile : les vingt pixels de creux de chaque cote lui ont pris quarante, et
     « Fonction a renseigner » sortait de six pixels a droite. Le defaut ne
     datait pas des tuiles -- sans elles, la meme pastille sortait deja de
     trente-six pixels sur un telephone de 320 --, mais les tuiles l'ont
     ramene a la largeur d'un iPhone ordinaire. */
  .intervenant-fonction {
    max-width: 100%;
    white-space: normal;
    overflow-wrap: anywhere;
  }

  /* L'equipe du mandat, ecrite la veille des deux listes ci-dessus, n'avait
     pas eu leur passage au telephone. Elle gardait sa grille d'ordinateur --
     cinq colonnes sur une ligne, dont la liste des cadences, large de sa plus
     longue option (« Etre informe a chaque ajout ou suppression de fichier »,
     441 px une fois le texte a 16 px). Le panneau Mandat debordait de 312 px
     sur un telephone de 390 : les croix de retrait etaient hors de l'ecran, et
     toute la page glissait lateralement (releve le 26 aout 2026).

     Une personne devient donc un bloc et non une ligne : qui elle est sur la
     premiere ligne -- son rond, son nom, le mot qui dit si elle signe, sa croix
     --, ce qu'elle recoit sur la seconde, pleine largeur. La grille passe de la
     liste a chaque bloc, sans quoi les colonnes de la premiere ligne resteraient
     communes a tous et le nom le plus long les fixerait pour les autres. Un
     filet separe les blocs, comme pour les intervenants juste au-dessus. */
  .reglages-equipe {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--espace);
  }

  .reglages-equipe-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto auto;
    grid-template-areas:
      "rond nom signe retirer"
      "suivi suivi suivi suivi";
    align-items: center;
    gap: var(--espace-xs);
    padding-bottom: var(--espace);
    border-bottom: 1px solid var(--gray-2);
  }

  .reglages-equipe-row:last-child { border-bottom: none; padding-bottom: 0; }
  .reglages-equipe-row > .dossier-avatar { grid-area: rond; }
  .reglages-equipe-nom { grid-area: nom; }
  .reglages-equipe-signataire { grid-area: signe; }
  .reglages-equipe-suivi { grid-area: suivi; }
  .reglages-equipe-retirer { grid-area: retirer; justify-self: end; }

  /* Les six croix de retrait des deux listes du panneau -- l'equipe et la
     presence en ligne -- prennent la cible tactile pleine. Elles etaient les
     cinq seuls controles du panneau a rester a 28 px sur un telephone, au
     milieu de cinquante-six voisins a 44 : sous le doigt, une case de 28 se
     rate, et elle se rate au profit de la liste deroulante posee juste dessous.
     La taille est la seule chose qui change -- le carre cerne reste le dessin
     du retrait, ici comme sur ordinateur. */
  .reglages-equipe-retirer,
  .reglages-intervenant-retirer {
    width: var(--m-tap);
    height: var(--m-tap);
  }

  /* Meme cible tactile que les champs voisins. */
  .reglages-equipe-suivi { width: 100%; min-height: var(--m-tap); }

  /* Exception : la date de cloture est un couple jour/mois. Empiles, les deux
     champs se lisaient comme deux reglages distincts -- "JJ" d'un cote, "MM"
     de l'autre --, alors qu'ils font une seule date. On remet la rangee, le
     bouton passe dessous.

     Leur largeur tenait dans un style en ligne, qui passe devant tout. Elle est
     devenue la classe `.reglages-champ-court` ; une classe, elle, ne passe plus
     devant `.reglages-row input`, ecrit juste au-dessus. Les deux champs
     reprenaient donc les 362 px de la colonne et se rempilaient : la rangee
     etait bien redevenue une rangee, mais chacun y tenait toute la ligne, et le
     defaut que ce bloc corrige etait revenu par la largeur. On redit donc la
     largeur, avec assez de poids pour les deux bandes -- celle-ci et celle des
     600-820 px, ou `.reglages-row > input` remet `width: auto`. */
  .reglages-row:has(> #reglages-cloture-jour-input) {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
  }
  .reglages-row:has(> #reglages-cloture-jour-input) > .btn-primary { flex: 1 0 100%; }
  .reglages-row > input.reglages-champ-court { width: var(--champ-court); }

  .dossier-assigne-select { min-height: var(--m-tap); }

  .cabinet-toggle {
    width: 100%;
    overflow-x: auto;
  }
  /* Les segments faisaient 37x26 : a la souris c'est confortable, au doigt on
     tape a cote. Ils s'etalent sur la largeur du groupe et montent a la cible
     standard. `flex: 1 0 auto` plutot que `1` pour qu'un cabinet de plus fasse
     defiler la rangee au lieu de comprimer les segments. */
  .cabinet-toggle .cabinet-seg {
    flex: 1 0 auto;
    min-height: var(--m-tap);
  }

  /* Le rond du titulaire ne s'etale pas avec les tuiles de cabinet : un rond
     etire n'est plus un rond, c'est une gelule -- exactement l'argument ecrit
     deux regles plus bas pour les carres du type de mandat, et qui n'avait pas
     ete tenu ici. La rangee TITULAIRE porte deux groupes separes par un filet,
     et le `flex: 1 0 auto` de la tuile passait par-dessus le `flex: none` du
     rond : les quatre initiales des personnes physiques y devenaient des
     pastilles de 110 px de large, au milieu d'un panneau ou tous les autres
     ronds -- les croix de l'equipe, celles de la presence en ligne, les
     vignettes des intervenants -- sont ronds.

     Le rond prend la cible tactile pleine plutot que ses 28 px d'ordinateur :
     c'est la meme mesure que les corbeilles rondes du panneau, et un disque de
     28 px se vise mal au doigt. */
  .cabinet-toggle .cabinet-seg-personne {
    flex: none;
    width: var(--m-tap);
    height: var(--m-tap);
  }
  /* Les deux carres du type de mandat montent a la cible tactile, comme ceux
     de l'avancement (.avancement-seg). Ils ne s'etirent pas sur la largeur du
     groupe : un carre etire n'est plus un carre, et il ne se lirait plus comme
     les autres selecteurs d'etat de la page. */
  .type-pill {
    /* La rangee des reglages etire ce qu'elle contient sur toute la largeur :
       sans cela, le groupe s'etendrait a 360px derriere deux carres de 44. */
    align-self: flex-start;
  }
  .type-pill .type-seg {
    width: var(--m-tap);
    height: var(--m-tap);
  }
  .type-pill .type-seg-icon { width: var(--icon); height: var(--icon); }

  /* Le carre au logo suit ses voisins ici aussi : leurs traces montent a
     --icon, le logo monte au meme barreau, et le carre s'elargit d'autant. Sans
     cette regle il restait a 14 px de haut a cote de traces de 16 -- l'ecart
     d'un pixel que la feuille s'interdit --, et surtout dans un carre de 44 qui
     ne laisse que 30 px utiles a une bulle qui en demande 44. */
  .type-pill .type-seg:has(> .type-seg-logo) {
    width: auto;
    padding: 0 calc((var(--m-tap) - var(--icon)) / 2);
  }

  .type-seg-logo { height: var(--icon); }

  /* La marque carree monte au meme barreau que les traces voisins, mais sans
     elargir son carre : c'est tout l'interet de sa forme (voir
     .type-seg-marque dans style.css). */
  .type-seg-marque { height: var(--icon); max-width: var(--icon); }

  /* ---------- 8. Analyse : cycles et tables d'arbre ---------------------- */

  /* La barre des cycles va d'un bord a l'autre de l'ecran : ses vignettes
     defilent sous le doigt, et la derniere doit pouvoir venir se placer au
     milieu de la vue. Elle sort donc de la gouttiere, et la tuile s'y reduit
     a ses deux filets -- un cadre arrondi pose a cheval sur le bord de la vue
     ne se ferme nulle part. Le creux lateral redevient celui de la gouttiere,
     pour la meme raison qu'il vaut celui de la tuile sur ordinateur : les
     vignettes partent du bord ou part tout le reste de la page. */
  .cycles-nav {
    flex-wrap: nowrap;
    overflow-x: auto;
    gap: var(--espace-xs);
    padding: var(--espace-sm) 0;
    margin: 0 calc(var(--m-gutter) * -1) var(--espace-md);
    padding-left: var(--m-gutter);
    padding-right: var(--m-gutter);
    scroll-snap-type: x proximity;
  }

  .cycles-nav.tuile {
    border-left: 0;
    border-right: 0;
    border-radius: 0;
  }

  .cycles-nav-btn {
    flex: none;
    scroll-snap-align: center;
    padding: var(--espace-xs) var(--espace-sm);
    font-size: var(--fs-xs);
  }

  /* La colonne de statut du programme de travail prenait 136 px fixes dans un
     panneau qui en compte 360 : l'intitule tombait a moins de la moitie de la
     largeur -- cinq lignes la ou il en faut deux -- et sous la pastille il
     restait une colonne de blanc haute comme toute la question. Une colonne
     d'etiquettes ne se justifie que s'il reste la place d'une seconde colonne.
     La rangee se met donc en pile, et la pastille reprend la largeur de son
     texte au lieu de reserver celle du plus long des quatre. */
  .programme-question {
    flex-direction: column;
    align-items: stretch;
    gap: var(--espace-2xs);
  }

  .programme-statut {
    width: auto;
    align-self: flex-start;
  }

  .cycle-section-title { font-size: var(--fs-md); }

  /* Pas de creux special ici : le mobile reduisait ce seul blanc a 19,2 sans
     toucher aux 24 qui separent les cycles, ce qui ne gagnait que 4,8 pixels
     une fois dans la page et lui donnait deux rythmes au lieu d'un. La regle
     de bureau suffit aux deux. */

  .tree-table-wrap {
    overflow-x: auto;
    border-radius: var(--radius);
    scroll-snap-type: x proximity;
  }

  .tree-table { font-size: var(--fs-xs); }

  /* La tuile d'un cycle va d'un bord a l'autre de l'ecran : elle abandonne
     ses bords verticaux et son arrondi, et son creux lateral devient la
     gouttiere de la page. Ce n'est pas une coquetterie, c'est une contrainte
     comptee deux fois dans les regles qui suivent : le nom d'un cycle et la
     rangee de son tableau ont tous deux ete arbitres sur les 347 px de
     fenetre d'un telephone de 375. Les 20 px de creux d'une tuile ordinaire
     les ramenaient a 305, et les deux arbitrages tombaient ensemble : cinq
     en-tetes de cycle sur dix passaient d'une ligne a trois, et la deuxieme
     annee du tableau -- 148 + 98 + 98 = 344 -- ne rentrait plus. La tuile
     prend donc la meme forme que l'index des cycles pose au-dessus d'elle,
     et les 347 px sont rendus intacts. Comme celle de l'index, la regle
     vaut pour tout ce bloc, jusqu'a 820 px : c'est le telephone qui la rend
     obligatoire, la tablette qui s'aligne sur lui. */
  .cycle-section {
    margin-left: calc(var(--m-gutter) * -1);
    margin-right: calc(var(--m-gutter) * -1);
    padding-left: var(--m-gutter);
    padding-right: var(--m-gutter);
    border-left: 0;
    border-right: 0;
    border-radius: 0;
  }

  /* La barre de commandes de l'analyse prend la meme forme, non pour gagner
     de la place -- mesuree a 320, 375 et 390, elle a exactement la meme
     hauteur dans les deux cas -- mais parce qu'elle ouvre cette colonne :
     la barre, l'index des cycles et les cycles eux-memes sont trois bandes
     qui se suivent, et une seule rentree de 14 px au milieu se lirait comme
     un defaut. Elle est designee par ou elle est posee et non par
     `.toolbar.tuile`, qui vaudrait pour toute barre faisant tuile : c'est
     une reponse a la largeur des cycles, pas une forme de barre. */
  #results-section > .toolbar {
    margin-left: calc(var(--m-gutter) * -1);
    margin-right: calc(var(--m-gutter) * -1);
    padding-left: var(--m-gutter);
    padding-right: var(--m-gutter);
    border-left: 0;
    border-right: 0;
    border-radius: 0;
  }

  /* Table a largeur naturelle : la colonne Poste est figee a gauche et les
     colonnes numeriques defilent horizontalement sous le doigt. */
  #tree-tables-container .tree-table {
    width: max-content;
    min-width: 100%;
  }
  /* Arithmetique de la rangee visible, a 375px de large (moins les gouttieres
     du panel il reste 347px de fenetre) : la colonne Poste est figee, donc elle
     mange en permanence sa largeur. Avec 190 + 106 il ne restait que 51px pour
     la deuxieme annee : on ne pouvait jamais comparer N et N-1 d'un coup d'oeil,
     alors que c'est tout l'interet de la feuille maitresse.
     148 + 98 + 98 = 344 : les deux exercices tiennent ensemble, la Variation
     reste accessible en poussant du doigt vers la gauche. */
  #tree-tables-container .tree-table col.col-poste { width: 148px; }
  #tree-tables-container .tree-table col.col-annee { width: 98px; }
  #tree-tables-container .tree-table col.col-variation { width: 100px; }

  /* Colonne masquee par le selecteur d'annees (mobile.js). En table-layout
     fixed, une largeur nulle sur le <col> replie vraiment la colonne : pas
     besoin de display:none, qui decalerait les cellules suivantes. */
  #tree-tables-container .tree-table col.m-col-off { width: 0; }
  /* `.tree-table thead th.th-variation` (0-2-2), declare plus bas, l'emportait
     sur `.tree-table th.m-col-off` et gardait sa font-size : l'en-tete
     "VARIATION" en nowrap imposait alors 54px de min-content a une colonne
     censee etre repliee. Le prefixe #tree-tables-container passe la regle a
     (1-2-2) et tranche independamment de l'ordre de declaration. */
  #tree-tables-container .tree-table th.m-col-off,
  #tree-tables-container .tree-table td.m-col-off {
    padding-left: 0;
    padding-right: 0;
    font-size: 0;
    overflow: hidden;
    white-space: nowrap;
    /* Sans cela il reste un liseré d'1px par colonne repliee. */
    border-left-width: 0;
    border-right-width: 0;
  }

  .tree-table thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--gray-1);
    padding: var(--espace-xs) var(--espace-sm);
    font-size: var(--fs-2xs);
    /* "2024 (en cours)" en nowrap impose une largeur mini de 113px a la colonne
       et faisait deborder la grille reglee juste au-dessus. On autorise le
       retour a la ligne : le millesime reste sur la premiere ligne, la mention
       d'exercice ouvert passe dessous. Rien n'est perdu. */
    white-space: normal;
    line-height: 1.2;
  }
  .tree-table thead th.th-variation { font-size: var(--fs-3xs); }

  .tree-table td { padding: var(--espace-xs) var(--espace-sm); }

  /* Colonne Poste figee a gauche. Le fond doit etre opaque pour que les
     colonnes numeriques passent dessous sans transparence ; les teintes de
     controle sont donc reappliquees en background-image par-dessus le blanc. */
  .tree-table td.label-cell,
  .tree-table thead th:first-child {
    position: sticky;
    left: 0;
    z-index: 3;
    background-color: #fff;
    border-right: 1px solid var(--gray-2);
  }
  .tree-table thead th:first-child { z-index: 4; background-color: var(--gray-1); }

  .tree-table tr[data-controle="valide"] > td.label-cell {
    background-image: linear-gradient(var(--controle-valide-bg), var(--controle-valide-bg));
  }
  .tree-table tr[data-controle="probleme"] > td.label-cell {
    background-image: linear-gradient(var(--controle-probleme-bg), var(--controle-probleme-bg));
  }

  /* Le hover n'existe pas au doigt : on neutralise pour eviter les etats
     colles apres un tap sur iOS. */
  .tree-table tbody tr:not(.row-verification):hover td { background: none; }
  .tree-table tbody tr:not(.row-verification):hover td.label-cell { background-color: #fff; }

  .tree-table tr.level-0 > td.label-cell { font-size: var(--fs-sm); }

  /* Le chevron reste etroit : le nom du poste juste a cote plie et deplie la
     meme ligne (cf. renderNodeRow dans app.js), et c'est lui la vraie cible :
     plusieurs centaines de pixels. L'elargir couterait de l'indentation a
     chaque niveau de l'arbre, sur un ecran qui n'en a pas. On lui donne
     seulement la hauteur de la ligne. */
  .row-toggle { min-height: 26px; }
  .row-account,
  .row-label-text { padding: var(--espace-3xs) 0; }

  /* Deplier / replier un cycle : deux boutons pousses a droite du titre de
     section, ou il reste une centaine de pixels libres. Aucune raison qu'ils
     restent a 24px.

     La bulle de commentaire du cycle prend la meme boite, dans la meme
     declaration. Elle est portee par le titre lui-meme, juste avant les
     bascules, alors que la regle qui la dimensionne plus bas vise les lignes
     de l'arbre -- hautes de 26 px, ou 24 est la bonne taille et le dit. Sur le
     titre, cette valeur posait un bouton de 24 a dix pixels de trois bascules
     de 38, son icone a 16 contre 18 : quatre commandes du meme cycle cote a
     cote, dont la premiere fait figure de rebut. C'est mot pour mot l'ecart
     que la regle du dessous a servi a supprimer entre la bulle et le trombone
     -- « un pouce de difference que rien ne justifiait ». Une seule
     declaration pour les quatre : elles ne peuvent plus se desaccorder.

     Le titre seulement, et le telephone seulement : sur le bureau
     `margin-left: auto` pousse les bascules au bord droit pendant que la bulle
     reste contre le texte, et mille pixels les separent -- deux boutons qui ne
     se voient pas ne se comparent pas. */
  .cycle-section-toggle-btn,
  .cycle-section-title > .controle-commentaire-btn { width: 38px; height: 38px; }
  .cycle-section-toggle-btn .icon,
  .cycle-section-title > .controle-commentaire-btn .icon {
    width: var(--icon-md);
    height: var(--icon-md);
  }

  /* Et l'ecart entre les trois bascules reste celui d'ici. Sur le bureau elles
     ont reçu celui de la barre de la vue, qui porte les memes commandes, et
     c'est la le bon raisonnement : deux rangs visibles ensemble se dessinent
     pareil. Sur le telephone, le meme geste coute une rangee. Les bascules y
     font 38 px pour se laisser toucher, et le titre d'un cycle ne tient deja
     que de justesse : onze pixels de plus renvoyaient un cycle entier a
     l'etage du dessous -- 38 px devenus 86 a 375, deux cycles passes de 38 a 43
     a 390. C'est mot pour mot ce que le repli mesure du nom, quelques lignes
     plus bas, a servi a eviter : « corriger 320 aurait coute vingt lignes de
     haut a chaque cycle sur tous les autres telephones ». */
  .cycle-section-toggles { gap: var(--espace-2xs); }

  /* Le titre d'un cycle tient sur une rangee qui ne pliait pas. Ses quatre
     commandes valent 178 px a elles seules ; il reste 114 px pour le nom, et un
     nom d'un seul mot ne sait pas retrecir en dessous de lui-meme --
     « IMMOBILISATIONS » en mesure 132. La rangee depassait donc du panneau, et
     comme la page, elle, ne defile pas lateralement, les bascules sortaient de
     l'ecran : cinquante-deux pixels sur ce cycle-la, trente-huit sur
     « FOURNISSEURS », c'est-a-dire un bouton et demi que personne ne pouvait
     plus atteindre a 320 px -- un iPhone SE, ou n'importe quel telephone en
     zoom d'affichage.

     La rangee plie desormais quand elle ne tient pas. Les cycles au nom court
     gardent leur ligne unique ; les autres posent leurs bascules en dessous, a
     droite, ou `margin-left: auto` les tenait deja. C'est la meme reponse qu'a
     la date de cloture des reglages : une rangee qui deborde se replie, elle ne
     se coupe pas.

     A 390 px rien ne bougeait deja, et rien ne bouge : la place suffit. */
  .cycle-section-title { flex-wrap: wrap; }

  /* Encore faut-il qu'elle ne plie que lorsqu'il le faut. Une rangee flexible
     decide de plier avant d'essayer de retrecir, et elle mesure le nom du cycle
     deploye sur une seule ligne : a 390 px, ou tout tenait jusqu'ici en une
     rangee de 43 px -- le nom sur deux lignes, les commandes a cote --, le
     simple fait d'autoriser le repli envoyait les bascules a l'etage du dessous
     et faisait passer l'en-tete a 86 px. Corriger 320 aurait coute vingt lignes
     de haut a chaque cycle sur tous les autres telephones.

     On mesure donc le nom sur le mot le plus long, ce qu'il occupe vraiment
     quand il plie. La rangee tient alors a 390 comme avant, et ne se dedouble
     qu'a partir du moment ou meme ce mot ne rentre plus. */
  .cycle-section-title > span:not([class]) { flex: 1 1 min-content; }

  /* Les deux commandes d'un controle (la bulle du commentaire, le trombone du
     justificatif) sont dessinees pour la souris : 20x20, et surtout opacity
     .35 tant que la ligne n'est pas survolee. Au doigt il n'y a pas de survol,
     donc elles resteraient invisibles en permanence. On les rend lisibles et on
     les agrandit. --icon-xl et pas 44 : ces lignes d'arbre font 26px de haut et
     il y en a des centaines, c'est la meme exception assumee que .row-toggle
     juste au-dessus, la cible reste largement doublee en surface.

     Les deux ensemble, et a la meme taille. La bulle avait ete agrandie seule,
     a 28px ; le trombone, ecrit apres elle, etait reste au dessin de la souris.
     Cote a cote sur la ligne d'un meme compte, les deux commandes du meme geste
     n'avaient donc ni la meme boite ni la meme icone : un pouce de difference
     que rien ne justifiait. */
  .controle-commentaire-btn,
  .controle-justificatif-btn {
    width: var(--icon-xl);
    height: var(--icon-xl);
    opacity: .75;
  }
  .controle-commentaire-btn.a-commentaire,
  .controle-justificatif-btn.a-justificatif { opacity: 1; }
  .controle-commentaire-btn .icon,
  .controle-justificatif-btn .icon { width: var(--icon); height: var(--icon); }

  /* Une ligne d'arbre porte un nom, et a cote de lui des commandes : le chevron
     qui plie la section, la bulle du commentaire, le trombone du justificatif.
     Tous etaient dans le meme flot de texte. Sur les 183 px de la colonne figee,
     les boutons tombaient donc la ou le nom les laissait : 288 abscisses
     differentes sur 714 lignes de compte, tantot au milieu de la phrase, tantot
     seuls sur une troisieme rangee sous elle. La ligne montait a 60 px pour un
     nom qui en demande 18, et le montant, centre dans sa cellule, ne se lisait
     plus en face de son compte. Les 61 lignes de sous-total avaient le meme
     desordre : le nom d'une section repliee passait sous son propre chevron.

     La cellule devient une rangee : le nom prend ce qui reste, les commandes
     forment une gouttiere a droite, a la meme abscisse sur toutes les lignes.
     Centrees, elles tombent sur l'axe du montant : le nom se lit en haut, la
     commande et le chiffre au milieu.

     `min-width: 0` est ce qui autorise le repli : sans lui un element de rangee
     ne descend jamais sous la largeur de son mot le plus long, et le nom
     pousserait les commandes hors de la colonne.

     Restreint a `#tree-tables-container` : le mapping a lui aussi des
     `td.label-cell`, mais elles portent un champ de saisie et pas un nom. */
  #tree-tables-container .tree-table td.label-cell {
    display: flex;
    align-items: center;
    gap: var(--espace-2xs);
  }
  /* `margin: 0` parce que chacun de ces enfants portait deja sa propre marge de
     voisinage : .35rem pour la bulle et pour le logo du cycle, .25rem pour le
     trombone. Additionnees au `gap`, elles auraient rendu trois ecarts
     differents dans une rangee qui n'en veut qu'un. */
  #tree-tables-container .tree-table td.label-cell > * { flex: none; margin: 0; }
  #tree-tables-container .tree-table td.label-cell > .row-account,
  #tree-tables-container .tree-table td.label-cell > .row-label-text { flex: 1 1 auto; min-width: 0; }
  /* Quatre lignes de sous-total portent leur nom en texte nu, sans element pour
     l'envelopper : rien a faire grandir, donc rien qui pousse la bulle a droite,
     et elle restait collee a la fin de la phrase. Elle s'y pousse elle-meme.
     Partout ailleurs le nom occupe deja tout l'espace libre, et cette marge
     automatique ne vaut rien. */
  #tree-tables-container .tree-table td.label-cell > .controle-commentaire-btn { margin-left: auto; }

  /* Le popover de commentaires est ancre en absolu sous son bouton, a une
     position calculee en JS pour une souris. Sur telephone ce bouton vit dans
     une ligne d'arbre qui defile lateralement, souvent tres bas dans la page :
     le popover atterrissait de biais, sous la barre d'onglets, ou coupe par le
     bord. On le repose au bas de l'ecran, comme les autres feuilles de
     l'interface. app.js pose top/left en style inline, d'ou le !important :
     confine au media query, l'affichage ordinateur ne le voit jamais. */
  .commentaire-popover {
    position: fixed !important;
    top: auto !important;
    left: 0 !important;
    right: 0;
    bottom: var(--m-clavier, 0px);
    width: auto;
    max-width: none;
    /* Le clavier repousse la feuille vers le haut : on lui retire d'autant de
       hauteur, sinon son en-tete sortirait par le haut de l'ecran. */
    max-height: calc(82vh - var(--m-clavier, 0px));
    display: flex;
    flex-direction: column;
    border: none;
    border-radius: var(--m-radius-sheet) var(--m-radius-sheet) 0 0;
    /* Devenue feuille du bas, elle prend l'ombre des feuilles du bas. `.m-sheet`
       -- meme position, meme rayon, meme facon de monter de l'ecran -- porte
       --shadow-lg ; celle-ci s'etait ecrit la sienne, au noir pur et vers le haut,
       et etait la seule feuille du produit a ne pas flotter comme les autres. */
    box-shadow: var(--shadow-lg);
    animation: none;
  }

  .commentaire-popover-header {
    padding: var(--espace) var(--espace-md);
    font-size: var(--fs-lg);
  }

  .commentaire-popover-close {
    width: var(--m-tap);
    height: var(--m-tap);
    margin-right: -.4rem;
  }

  .commentaire-popover-close .icon { width: var(--icon-md); height: var(--icon-md); }

  /* La liste prend la place restante entre l'en-tete et le formulaire, au lieu
     de sa hauteur fixe de 260px : sur un ecran haut on voit plus de messages,
     sur un ecran court le formulaire reste visible. */
  .commentaire-popover-list {
    flex: 1;
    min-height: 0;
    max-height: none;
    padding: var(--espace-sm) var(--espace-md);
  }

  .commentaire-avatar { --avatar-d: 26px; }
  .commentaire-auteur-nom { font-size: var(--fs); }
  .commentaire-date { font-size: var(--fs-sm); }
  .commentaire-texte { font-size: var(--fs-md); }

  .commentaire-delete-btn { width: 32px; height: 32px; }
  .commentaire-delete-btn .icon { width: var(--icon); height: var(--icon); }

  /* Le formulaire est colle en bas de la feuille : il garde la marge de la
     barre gestuelle sous lui, sauf quand le clavier est ouvert (le decalage
     est alors porte par la feuille entiere). */
  .commentaire-popover-form {
    padding: var(--espace-sm) var(--espace-md) calc(var(--espace) + env(safe-area-inset-bottom));
  }

  /* Meme forme que dans style.css : accrochee a l'enveloppe, sinon la variante
     de bureau -- desormais a deux classes -- l'emporterait sur celle-ci malgre
     l'ordre des feuilles. Le corps du texte n'est plus repris ici : la regle
     des fondations couvre desormais la classe elle-meme. */
  .commentaire-popover-champ-texte .ticket-widget-texte {
    min-height: 62px;
    padding: var(--espace-sm) var(--mic-reserve) var(--espace-sm) var(--espace-sm);
  }

  .commentaire-popover-form .commentaire-add-btn {
    align-self: stretch;
    min-height: var(--m-tap);
    font-size: var(--fs-md);
  }

  .variation-cell { font-size: var(--fs-2xs); }

  /* Selecteur d'annees affichees, injecte au-dessus des tables. */
  .m-yearbar {
    display: flex;
    align-items: center;
    gap: var(--espace-xs);
    overflow-x: auto;
    margin-bottom: var(--espace-sm);
  }
  .m-yearbar-label {
    flex: none;
    font-size: var(--fs-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: var(--suivi-entete);
    color: var(--gray-4);
    padding-right: var(--espace-3xs);
  }

  .controle-banner { font-size: var(--fs-sm); padding: var(--espace-sm) var(--espace); }

  /* Quand l'equilibre est bon, app.js vide le bandeau sans le masquer : sur
     ordinateur ce reliquat de 19px passe inapercu, sur telephone il ouvre un
     trou entre la barre d'outils et les cycles. */
  .controle-banner:empty { display: none; }

  /* Menu de selection des annees. */
  .annees-menu { width: min(88vw, 320px); }

  /* ---------- 9. Tables -> cartes ---------------------------------------- */

  /* mobile.js pose .m-cards sur les tables trop larges pour un telephone et
     copie l'en-tete de colonne dans data-label sur chaque cellule. La table
     devient alors une liste de cartes libelle / valeur.

     Le pied de tableau (`tfoot`) suit le corps : il manquait a cette liste, et
     comme le reste ne s'y trouve pas non plus, il restait un
     `table-footer-group` dans une table passee en `display: block` -- donc mis
     en page a sa largeur naturelle, sans rapport avec celle de la table. Le
     seul pied du produit est la ligne de totaux de la consommation : sur un
     telephone de 390 px, ses huit colonnes se posaient sur 497 px et sortaient
     du cadre par la droite, moities de montants coupees. */

  table.m-cards,
  table.m-cards > tbody,
  table.m-cards > tbody > tr,
  table.m-cards > tbody > tr > td,
  table.m-cards > tfoot,
  table.m-cards > tfoot > tr,
  table.m-cards > tfoot > tr > td {
    display: block;
    width: auto;
  }

  table.m-cards { border-collapse: separate; min-width: 0; }
  table.m-cards > thead { display: none; }

  /* Les regles d'affichage ci-dessus l'emportent sur le [hidden] par defaut du
     navigateur (specificite superieure) : il faut le retablir explicitement,
     sinon les lignes filtrees (filtre du mapping des racines, notamment)
     resteraient visibles en mode cartes. */
  table.m-cards > tbody > tr[hidden],
  table.m-cards > tbody > tr > td[hidden],
  table.m-cards > tfoot > tr[hidden],
  table.m-cards > tfoot > tr > td[hidden] { display: none; }

  table.m-cards > tbody > tr,
  table.m-cards > tfoot > tr {
    position: relative;
    /* Colonne flex plutot que bloc : l'empilement est identique, mais la carte
       peut alors reordonner ses cellules. La colonne titre n'est pas toujours la
       premiere du tableau (dans le visualiseur de FEC, "Libelle compte" est la
       cinquieme) et le nom se retrouvait en gras au milieu de la carte, sous des
       lignes anonymes. Le titre remonte en tete, cf. `order` plus bas. */
    display: flex;
    flex-direction: column;
    margin-bottom: var(--espace-sm);
    padding: var(--espace-sm) var(--espace);
    border: 1px solid var(--gray-3);
    border-radius: var(--radius);
    background: #fff;
    box-shadow: var(--shadow-sm);
  }

  /* Dans une tuile, la carte descend d'un rang. Bordure `--gray-3`,
     arrondi `--radius` et ombre portee sont la signature d'une surface
     posee sur la page : repetee a l'interieur d'une tuile qui porte deja
     exactement ce cadre, elle donne deux cadres jumeaux emboites a 20 px
     l'un de l'autre, et la tuile parait doublee d'un trait. Le second rang
     de la feuille -- `--gray-2` et `--radius-sm`, celui de `.tuile-encart`
     -- separe les cartes sans redessiner la tuile. L'ombre tombe avec :
     une surface a l'interieur d'une autre ne flotte au-dessus de rien.

     Une carte hors tuile garde le premier rang, c'est alors elle la
     surface de tete. */
  .tuile table.m-cards > tbody > tr,
  .tuile table.m-cards > tfoot > tr {
    border-color: var(--gray-2);
    border-radius: var(--radius-sm);
    box-shadow: none;
  }

  /* La derniere carte ne pousse pas sa marge hors de la table. Les lignes sont
     des blocs, la table aussi, et rien ne ferme le bas de celle-ci : la marge
     basse de la derniere s'en echappait et s'ajoutait au creux de la tuile --
     25,6 px en bas pour 16 en haut, sur les tables du Mapping a 390. */
  table.m-cards > :last-child > tr:last-child { margin-bottom: 0; }

  table.m-cards > tbody > tr > td,
  table.m-cards > tfoot > tr > td {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--espace);
    padding: var(--espace-3xs) 0;
    border: 0;
    font-size: var(--fs-sm);
    text-align: right;
    white-space: normal;
    overflow-wrap: anywhere;
  }

  table.m-cards > tbody > tr > td::before,
  table.m-cards > tfoot > tr > td::before {
    content: attr(data-label);
    flex: none;
    max-width: 45%;
    text-align: left;
    color: var(--gray-4);
    font-size: var(--fs-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: var(--suivi-entete);
  }

  /* Cellule vide ou colonne sans en-tete : inutile de l'afficher en carte. */
  table.m-cards > tbody > tr > td.m-empty,
  table.m-cards > tfoot > tr > td.m-empty { display: none; }

  /* Le numero d'ecriture est le seul chemin vers la piece complete. Rendu tel
     quel c'est un texte de 8x20px : impossible a viser au doigt, et rien ne dit
     qu'il est cliquable une fois le survol indisponible. En carte on lui donne
     la cible tactile standard et un fond qui le designe comme un bouton. */
  table.m-cards .row-ecriture-num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: var(--m-tap);
    min-height: var(--m-tap);
    padding: 0 var(--espace-sm);
    border-radius: var(--radius);
    background: var(--cyan-pale);
    font-weight: 600;
  }

  /* Cellule de titre : pleine largeur, sans etiquette, toujours en tete de carte
     quelle que soit sa position dans la ligne d'origine. */
  table.m-cards > tbody > tr > td.m-card-title,
  table.m-cards > tfoot > tr > td.m-card-title {
    order: -1;
    /* Flex plutot que block : plusieurs titres commencent par une icone collee
       au texte (nom de fichier FEC, libelle de cycle). L'ecart vient du gap,
       et l'icone garde sa taille pendant que le libelle se replie. */
    display: flex;
    align-items: flex-start;
    /* Une cellule de carte ordinaire ecarte ses deux bouts -- l'etiquette a
       gauche, la valeur a droite --, et c'est de la qu'elle tient son
       `space-between`. Un titre n'est pas une paire : c'est une suite qui se
       lit de gauche a droite, un creux, une icone, un nom, parfois un badge.
       Herite tel quel, l'ecart se repartissait entre ses morceaux et dependait
       de la longueur du nom -- deux dossiers voisins de la liste Juridique
       posaient leur icone a 104 et a 122 px, leur nom a 171 et a 207, alors
       que les fichiers en dessous, aux noms plus longs, restaient groupes.
       Les titres du produit qui n'ont qu'un morceau ne changent pas. */
    justify-content: flex-start;
    gap: var(--espace-xs);
    margin-bottom: var(--espace-2xs);
    padding-bottom: var(--espace-xs);
    border-bottom: 1px solid var(--gray-2);
    text-align: left;
    font-size: var(--fs);
    font-weight: 600;
    color: var(--black);
    /* Un nom de FEC fait 28 caracteres sans espace : sans cela il deborde de
       la carte au lieu de passer a la ligne. */
    overflow-wrap: anywhere;
  }
  table.m-cards > tbody > tr > td.m-card-title > .icon,
  table.m-cards > tbody > tr > td.m-card-title > svg,
  table.m-cards > tfoot > tr > td.m-card-title > .icon,
  table.m-cards > tfoot > tr > td.m-card-title > svg { flex: none; }
  /* Le chevron d'un repertoire et le creux qui le remplace sur la ligne d'un
     fichier gardent leur taille pour la meme raison que l'icone : c'est le
     retrait, et un retrait qui retrecit n'en est plus un. Le creux est ecrit
     18px et son commentaire promet que les noms tombent sous les noms ; en
     carte, ou la cellule est une rangee flexible, il se laissait comprimer
     d'autant que le nom etait long -- 18px sur un dossier, 15,8 puis 14 sur
     les deux fichiers suivants, dont les noms partaient donc de trois
     abscisses differentes. Le repli va au nom, qui sait passer a la ligne. */
  table.m-cards > tbody > tr > td.m-card-title > .juridique-toggle,
  table.m-cards > tbody > tr > td.m-card-title > .juridique-toggle-creux,
  table.m-cards > tbody > tr > td.m-card-title > .row-toggle { flex: none; }
  table.m-cards > tbody > tr > td.m-card-title::before,
  table.m-cards > tfoot > tr > td.m-card-title::before { content: none; }

  /* Cellule d'actions (boutons) : alignee a droite, pas d'etiquette. */
  table.m-cards > tbody > tr > td.m-card-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--espace-xs);
    margin-top: var(--espace-xs);
    padding-top: var(--espace-xs);
    border-top: 1px solid var(--gray-2);
  }
  table.m-cards > tbody > tr > td.m-card-actions::before { content: none; }

  /* Montants : chiffres alignes, teinte conservee. */
  table.m-cards > tbody > tr > td.num,
  table.m-cards > tfoot > tr > td.num {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
  }

  /* Ligne unique "Aucune ecriture" / "Chargement..." : pas de carte. */
  table.m-cards > tbody > tr > td[colspan],
  table.m-cards > tfoot > tr > td[colspan] {
    display: block;
    text-align: center;
    color: var(--gray-5);
    font-size: var(--fs-sm);
  }
  table.m-cards > tbody > tr > td[colspan]::before,
  table.m-cards > tfoot > tr > td[colspan]::before { content: none; }

  /* Les barres du Dashboard : le nom passe AU-DESSUS de sa piste.

     Sur ordinateur les trois parties tiennent sur une ligne, la colonne de
     gauche allant jusqu'a 12rem. Sur un telephone ces 12rem prenaient les
     deux tiers de la largeur et il restait 71 px de piste : quatre barres
     dont trois moignons, alors qu'une barre n'existe que pour etre comparee
     a ses voisines. Le nom prend donc sa propre ligne, et la piste toute la
     largeur moins le chiffre. */
  .tka-barre {
    grid-template-columns: 1fr auto;
    gap: var(--espace-3xs) var(--espace-sm);
  }
  .tka-barre-nom { grid-column: 1 / -1; }

  /* La ligne de totaux garde les deux signaux qu'elle a sur ordinateur -- le
     gras et le fond gris --, mais portes par la carte et non par chaque
     cellule : `.conso-table tfoot td` pose ce fond cellule par cellule, ce qui
     en mode carte peindrait des bandes grises separees par le blanc de la
     carte. La barre haute de 2 px n'a plus lieu d'etre non plus : une carte a
     deja un bord tout autour, et elle est la derniere. */
  table.m-cards > tfoot > tr {
    background: var(--gray-1);
    font-weight: 600;
  }
  table.m-cards > tfoot > tr > td {
    background: none;
    border-top: 0;
  }

  /* Un libelle de total etale sur plusieurs colonnes ("Depassement facturable",
     "Credit inclus consomme") n'est pas la ligne grise et centree d'un tableau
     vide : c'est le titre de la carte, et il prend le meme trait de separation
     que `.m-card-title` -- sans quoi les trois cartes de total de la page
     n'auraient pas la meme construction, celle dont le libelle tient dans une
     seule colonne etant seule a porter ce trait. */
  table.m-cards > tfoot > tr > td[colspan] {
    margin-bottom: var(--espace-2xs);
    padding-bottom: var(--espace-xs);
    border-bottom: 1px solid var(--gray-2);
    text-align: left;
    color: var(--black);
    font-size: var(--fs);
  }

  /* Le marqueur de controle (bande verte/orange) devient un liseré gauche. */
  table.m-cards > tbody > tr[data-controle="valide"] { border-left: 4px solid var(--green); }
  table.m-cards > tbody > tr[data-controle="probleme"] { border-left: 4px solid var(--orange); }
  table.m-cards > tbody > tr[data-controle] > td { background: none; }
  table.m-cards > tbody > tr > td.controle-cell { display: none; }

  /* Ligne de solde de cloture : sur ordinateur deux filets cyan traversent la
     rangee de part en part. En cartes chaque cellule devient un bloc, donc
     chaque cellule redessinait ses deux filets : on lisait une pile de barres
     bleues au lieu d'un separateur. On rend le filet a la carte entiere. */
  table.modal-table.m-cards > tbody > tr.row-cloture {
    background: var(--cyan-pale);
    border: 2px solid var(--cyan);
  }
  table.modal-table.m-cards > tbody > tr.row-cloture > td {
    background: none;
    border-top: none;
    border-bottom: none;
  }

  .modal-table-wrap { padding: 0 0 var(--espace-md); overflow: visible; }

  /* En cartes, chaque ecriture porte deja son cadre : la tuile du tableau
     ferait une boite de boites. Ici comme ailleurs sur telephone, ce sont les
     cartes qui sont les tuiles. */
  .compte-detail-panel .modal-table-wrap {
    padding: 0 0 var(--espace-md);
    border: 0;
    border-radius: 0;
    box-shadow: none;
  }

  /* Navigation compte precedent / suivant.
     Sur ordinateur les trois blocs (precedent, titre, suivant) tiennent sur une
     rangee. A 375px la colonne du milieu tombait a 130px : le numero de compte
     se coupait en deux lignes et les deux boutons voisins se reduisaient a un
     moignon ("10" pour 106 0000). On passe a deux rangees (le titre en entier
     au-dessus, la navigation dessous) via des zones nommees.
     Les zones sont indispensables : style.css place chaque enfant a la main
     (#compte-nav-prev { grid-column: 1 } etc.) pour survivre au masquage du
     bouton precedent, et ces regles en id survivraient a un simple changement
     de grid-template-columns : la colonne 3 serait recreee implicitement. */
  .modal-header .compte-nav {
    grid-template-columns: 1fr 1fr;
    grid-template-areas:
      "titre titre"
      "prev  next";
    align-items: center;
    gap: var(--espace-sm);
  }
  #compte-nav-prev { grid-area: prev; justify-self: start; }
  #compte-nav-next { grid-area: next; justify-self: end; }
  .modal-header .modal-compte-titre {
    grid-area: titre;
    display: block;
    justify-self: stretch;
    min-width: 0;
  }

  .compte-nav-btn {
    min-height: var(--m-tap);
    max-width: 100%;
    padding: var(--espace-xs) var(--espace-sm);
    /* Seuls sur leur rangee, les deux boutons ont besoin d'une surface visible
       pour se lire comme des boutons et non comme du texte d'entete. */
    background: var(--gray-1);
  }
  .compte-nav-btn-lib { display: none; }
  .compte-nav-btn-num { font-size: var(--fs-sm); }

  /* Les deux rangees ci-dessus n'ont pas suffi : le moignon etait toujours la.
     La navigation partage sa rangee avec « Valider le compte » et la croix, et
     comme elle est le seul element extensible de l'en-tete (flex: 1), c'est
     elle qui paie. Largeur mesuree du bouton precedent puis du suivant :
     38 et 38 a 320 px de fenetre, 66 et 66 a 375, 73 et 73 a 390, 93 et 93 a
     430, 93 et 96 a 768 -- pour un besoin de 93 et 96. « 129 0020 » se lisait
     donc « 12 » sur tout telephone, et manquait encore de trois pixels sur la
     plus large des fenetres de telephone.

     Elle prend une rangee pour elle seule dans tout ce bloc, la marque de
     controle et la croix la suivante. Pas de bascule calee sur une largeur :
     le seuil mesure (465 px) ne vaut que pour des numeros a huit chiffres, et
     un compte auxiliaire n'a pas de longueur connue -- une balance
     fournisseurs romprait la regle des le premier compte.

     Ca coute 60 px de hauteur : l'en-tete passe de 121,6 a 181,6, et comme il
     reste colle au haut de la fenetre avec les filtres, ce sont 324 px de
     commandes fixes au lieu de 264 -- deux ecritures visibles au lieu de deux
     et demie sur un telephone ordinaire. Rien ici ne peut faire mieux : poser
     la marque de controle et la croix sur la ligne du titre demanderait
     qu'elles soient dans le meme conteneur que lui, et ni une grille ni un
     flex ne sait donner deux decoupes de colonnes differentes aux deux rangees
     d'un meme bloc. Cette economie-la se prendrait dans le gabarit, pas ici.

     Les deux marges automatiques de cette rangee-la se partageaient l'espace
     libre a parts egales et posaient « Valider le compte » au milieu de nulle
     part. Une seule reste, celle de la croix : la marque revient a gauche de
     sa rangee, la croix a droite. */
  .compte-detail-panel .modal-header { flex-wrap: wrap; }
  .compte-detail-panel .compte-nav { flex: 1 1 100%; }
  .compte-detail-panel .ecriture-valider { margin-left: 0; }

  /* Curseur de plage d'annees. Les deux <input type="range"> superposes portent
     pointer-events: none et seul le rond reprend la main : la cible reelle, ce
     n'est pas la barre, c'est le rond de 15px. On l'amene a 26px et on redonne
     de la hauteur a la piste pour qu'il ne mange pas le libelle au-dessus.
     (margin-top = (hauteur piste - hauteur rond) / 2, la piste fait 4px.) */
  .year-slider-group { width: 100%; }
  .year-range-slider { width: 100%; height: 34px; }

  .year-range-slider input[type="range"]::-webkit-slider-thumb {
    width: 26px;
    height: 26px;
    margin-top: -11px;
  }
  .year-range-slider input[type="range"]::-moz-range-thumb {
    width: 26px;
    height: 26px;
  }

  /* Case "non lettrees" : la case native fait 13px et son libelle 19px de haut.
     C'est le libelle qu'on vise au doigt, on lui donne la cible standard. */
  .filter-checkbox {
    min-height: var(--m-tap);
    margin-left: 0;
  }
  .filter-checkbox input[type="checkbox"] {
    width: 20px;
    height: 20px;
    flex: none;
  }

  .modal-filters {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--espace-sm);
  }

  .modal-compte-titre { font-size: var(--fs-lg); }
  .modal-compte-lib { font-size: var(--fs-sm); }

  /* ---------- 10. Modales plein ecran ------------------------------------ */

  .modal-overlay {
    padding: 0;
    align-items: stretch;
    justify-content: stretch;
  }

  .modal {
    width: 100%;
    max-width: none;
    height: 100%;
    max-height: none;
    border-radius: 0;
    display: flex;
    flex-direction: column;
  }

  .modal-header {
    position: sticky;
    top: 0;
    z-index: 3;
    flex: none;
    gap: var(--espace-sm);
    padding: var(--espace-sm) var(--m-gutter);
    padding-top: calc(var(--espace-sm) + var(--m-safe-top));
    background: var(--m-surface);
    backdrop-filter: saturate(180%) blur(18px);
    -webkit-backdrop-filter: saturate(180%) blur(18px);
    border-bottom: 1px solid var(--gray-2);
  }

  .modal-close {
    min-width: var(--m-tap);
    margin-left: auto;
    /* Le bouton fermer ne se comprime jamais : c'est la seule sortie de la vue. */
    flex: none;
  }

  /* Le bloc titre d'un en-tete de modale est un flex item, donc min-width: auto :
     il ne peut pas descendre sous la largeur de son mot le plus long. Un nom de
     FEC fait 28 caracteres sans espace, le bloc restait a 332px et poussait la
     croix a 398px : la page entiere debordait de 23px vers la droite. On
     autorise le retrecissement et on laisse le nom se replier. */
  .modal-header > div { min-width: 0; }
  .modal-header h2 { overflow-wrap: anywhere; }

  .modal > .modal-table-wrap,
  .modal > .modal-filters {
    padding-left: var(--m-gutter);
    padding-right: var(--m-gutter);
  }

  .modal > .modal-table-wrap {
    flex: 1 1 auto;
    overflow-y: auto;
    padding-bottom: calc(var(--espace-md) + var(--m-safe-bottom));
  }

  /* ---------- 11. Tickets & messages ------------------------------------- */

  /* Le creux suit la carte depuis que la liste se colonne (style.css) : un
     `gap` de colonnage ne separe plus deux cartes l'une de l'autre. Sur le
     telephone il n'y a de toute facon qu'une colonne, mais le barreau du
     dessous vaut ici comme ailleurs. */
  .tickets-list > .ticket-card { margin-bottom: var(--espace); }

  /* Le meme rythme qu'au bureau, sur le barreau du dessous : la carte perd
     de la gouttiere, pas sa mise en page. Le bas valait 14,4 px contre 12,8
     en haut, sans raison. */
  .ticket-card {
    padding: var(--espace);
    border-radius: var(--radius-lg);
  }

  .ticket-card-header {
    flex-wrap: wrap;
    gap: var(--espace-sm);
  }

  /* "Marquer comme lu" est seul dans l'en-tete de la carte message : ce n'est
     pas une rangee dense, l'exception a 38px des .btn-sm ne s'y justifie pas. */
  .ticket-card-header > form > .btn-sm { min-height: var(--m-tap); }

  /* `flex: 1`, c'est une base de zero : la rangee ci-dessus ne comptait donc
     pas le nom dans sa largeur, ne debordait jamais et ne se repliait jamais
     malgre son `wrap`. C'est le nom qui payait la place des pastilles --
     « Christophe Rambeau » tenait sur 58 px et se coupait au milieu des mots,
     sur quatre lignes. En comptant sa largeur naturelle, la rangee deborde
     pour de bon et ce sont les pastilles qui passent dessous : l'en-tete est
     passe de 139 a 93 px de haut a 390 px de large. */
  .ticket-card-meta { flex: 1 1 auto; min-width: 0; }
  .ticket-card-date { font-size: var(--fs-2xs); }

  .ticket-card-texte,
  .ticket-card-texte-edit { font-size: var(--fs); }

  .ticket-card-suivi-form,
  .ticket-card-texte-form {
    flex-wrap: wrap;
    gap: var(--espace-sm);
  }

  /* Plus de champ d'etiquettes ici : il a quitte la carte le 30 aout 2026. */
  .ticket-card-suivi-input {
    width: 100%;
    /* 33px de haut : sous la cible tactile. */
    min-height: var(--m-tap);
  }

  /* Celui-la porte un micro, et un micro tactile fait --m-tap a lui seul : son
     plancher n'est plus la cible mais la place qu'il faut a ce bouton. */
  .ticket-card-suivi-input { min-height: var(--mic-place); }

  .ticket-card-page { font-size: var(--fs-xs); flex-wrap: wrap; }
  /* Le lien vers la page d'origine du ticket est un texte de 18px de haut :
     on lui donne de la hauteur sans le sortir de sa ligne. */
  .ticket-card-page a {
    display: inline-flex;
    align-items: center;
    min-height: var(--m-tap);
  }

  /* La rangee tenait sur une ligne quand elle portait trois etats et rien
     d'autre. Elle en porte quatre depuis « A valider », plus le transfert et la
     poubelle : 389 px de libelles a plat pour 332 de carte. Personne n'avait
     deborde pour autant -- les segments se sont simplement laisses ecraser
     jusqu'a leur mot le plus long, et deux d'entre eux ont casse leur libelle
     en cours de route : « Envoye a l'Agent » sur trois lignes de quatre
     caracteres, « A valider » sur deux, entre « Nouveau » et « Resolu » restes
     d'un seul tenant. Une rangee de quatre boutons dont deux ecrivent en
     colonne.

     Un segment ne s'ecrase donc plus et n'ecrit plus sur deux lignes, et c'est
     la rangee qui passe a la ligne quand elle est pleine.

     Restait a dire ou elle passe, et la premiere reponse etait fausse : « les
     quatre etats occupent 322 px, ils tiennent, dans l'ordre, sur la premiere
     ligne ». Ces 322 px etaient compares aux 332 de la carte, en oubliant les
     32 px de rembourrage de la boite elle-meme. Mesure au navigateur a 375 px
     le 7 septembre 2026 : la ligne offre 285 px et les quatre etats en
     demandent 334. Trois passaient ; le quatrieme tombait a la ligne suivante,
     ou il retrouvait le transfert et la poubelle. « Resolu », dernier etat du
     parcours, se lisait donc comme le voisin de deux gestes qui n'en sont pas
     un, et la regle graduee semblait s'arreter a « A valider ».

     Les quatre etats prennent donc la moitie de la ligne chacun : deux rangs
     de deux, egaux, ou le parcours se lit de gauche a droite puis de haut en
     bas. Les deux lignes sont pleines, si bien que le transfert et la poubelle
     tombent d'eux-memes sur la troisieme, ranges a droite -- ce que le
     decrochage de .6rem fait sur ordinateur, le retour a la ligne le fait ici.

     Rien a porter dans l'app : sa regle graduee (`VueDetailTicket.pipeline`)
     ne contient que les quatre etats, le transfert et la suppression vivant
     ailleurs sur cet ecran. Le defaut corrige ici n'y existe pas. */
  .ticket-statut-pipeline {
    flex-wrap: wrap;
    row-gap: var(--espace-xs);
  }
  /* `.ticket-statut-pipeline-form` portait ici un `flex: 1` sans effet : la
     feuille d'ordinateur en fait un `display: contents`, donc une boite qui
     n'existe pas, et une boite qui n'existe pas ne se partage pas une ligne.
     Ce sont les boutons qu'elle enveloppe qui sont les elements de la rangee,
     et c'est donc sur eux que la mesure se pose. */
  /* Les trois segments du pipeline (nouveau / envoye / resolu) font 27px de
     haut et sont colles les uns aux autres : au doigt on change de statut par
     erreur. Ils passent a la cible standard. */
  .ticket-statut-seg {
    min-height: var(--m-tap);
    padding: 0 var(--espace-sm);
    flex: 1 0 calc(50% - var(--espace-2xs));
    white-space: nowrap;
  }

  /* La poubelle de la carte est un `.icon-btn` et non un `.btn-icon-only` :
     elle echappe donc au carre de 44 pose en tete de fichier pour les boutons
     reduits a leur icone. La feuille d'ordinateur la tient carree par une
     largeur minimale de --h-sm ; ici la hauteur montait bien a 44 par
     `.icon-btn` et la largeur restait a 28. C'est le rectangle de 28 sur 44
     que cette regle-la a ete reecrite pour faire disparaitre, retrouve sur le
     seul geste de la carte qui ne se defait pas.

     Dans l'app, supprimer un ticket est une action de balayage
     (`VueTickets`, `.swipeActions`) : iOS la pose sur toute la hauteur de la
     ligne, la cible y est donnee. */
  .ticket-statut-pipeline .ticket-card-supprimer { min-width: var(--m-tap); }

  .ticket-statut-pipeline .ticket-card-transfert { margin-left: auto; }

  .ticket-card-telechargements { flex-wrap: wrap; gap: var(--espace-xs); }

  .ticket-card-thread-msg { font-size: var(--fs-sm); }

  .alertes-bandeau-admin-form {
    display: flex;
    flex-direction: column;
    gap: var(--espace-sm);
    align-items: stretch;
  }
  .alertes-bandeau-admin-input {
    width: 100%;
    min-height: var(--m-tap);
  }

  /* La rangee d'ecriture monte a la cible tactile : la pilule et le rond
     partagent `--chat-ligne`, et la reserve de droite suit le bouton de ticket
     du telephone, plus petit et cale sur la gouttiere. */
  .chat-ecrire {
    --chat-ligne: var(--m-tap);
    --chat-reserve: calc(46px + var(--m-gutter) + var(--espace-xs));
  }

  /* ---------- 12. Mapping ------------------------------------------------ */

  .mapping-filtre-input { width: 100%; min-height: var(--m-tap); }

  /* Le mapping du bilan est un arbre : sa structure en colonnes porte le sens,
     il reste une table a defilement lateral avec la premiere colonne figee.
     Les trois autres tables (cycles, sous-totaux, racines) sont des listes
     d'enregistrements editables : mobile.js les passe en cartes (cf. 9), ou
     chaque champ est sur sa propre ligne, editable sans defilement lateral.

     Ce defilement lateral n'existait nulle part : la table fait 595px et aucun
     de ses ancetres ne portait overflow-x, donc c'est la page entiere qui
     defilait de biais (609px de contenu pour 375px d'ecran). Il a d'abord ete
     porte par le panneau lui-meme, faute de conteneur ou le mettre. Le panneau
     est une tuile depuis le 30 aout 2026, et une tuile a un creux : un
     conteneur de defilement qui porte le creux ne peint pas son bord droit en
     fin de course. La table est donc enveloppee dans le `.table-defilante` de
     la maison -- celui que porte deja la table des racines, deux tuiles plus
     bas --, et il n'y a plus rien a regler ici : l'enveloppe reste un ancetre
     des cellules, c'est ce dont depend le `position: sticky` plus bas. */

  #bilan-mapping-table { font-size: var(--fs-xs); width: max-content; min-width: 100%; }
  #bilan-mapping-table th,
  #bilan-mapping-table td { padding: var(--espace-2xs) var(--espace-xs); }
  #bilan-mapping-table input[type="text"] {
    min-width: 140px;
    min-height: var(--m-tap);
  }

  /* Ajouter / supprimer un poste : deux boutons-icones de 34px de large, colles
     l'un a l'autre, dont l'un est destructif. La table defile lateralement, on
     peut leur donner la cible standard sans contraindre la mise en page. */
  #bilan-mapping-table .mapping-actions-cell .btn-link {
    min-width: var(--m-tap);
    min-height: var(--m-tap);
  }

  #bilan-mapping-table td:first-child,
  #bilan-mapping-table th:first-child {
    position: sticky;
    left: 0;
    z-index: 2;
    background-color: #fff;
    border-right: 1px solid var(--gray-2);
  }
  #bilan-mapping-table thead th:first-child { background-color: var(--gray-1); z-index: 3; }

  /* Toutes les lignes d'une carte de mapping respirent pareil. Les champs
     avaient .3rem, les faits (la racine, le code, la case a cocher) gardaient
     les .16rem de la regle generale : le code d'un cycle se retrouvait a trois
     pixels de l'etiquette de la ligne suivante, quand les champs voisins
     s'ecartaient de neuf. */
  table.mapping-table.m-cards > tbody > tr > td { padding: var(--espace-2xs) 0; }

  /* Une carte n'a pas de groupes de colonnes a separer. L'en-tete qui les
     declarait est masque ici et chaque champ prend toute la largeur : le trait
     qui ouvre un groupe sur le tableau large deviendrait un trait vertical pose
     au bord d'un champ, avec la meme chose de part et d'autre. */
  table.mapping-table.m-cards > tbody > tr > td.mapping-groupe-debut { border-left: none; }

  /* Une carte de mapping est un formulaire, pas une fiche de lecture. Le couple
     etiquette-a-gauche / valeur-a-droite convient a un montant, pas a un champ
     de saisie : chaque champ prenait la largeur que lui laissait son etiquette,
     et la colonne de champs se retrouvait alignee ni a gauche ni a droite. On
     empile (etiquette au-dessus, champ sur toute la largeur) et chaque champ
     recoit la cible tactile standard. 16px de texte pour que le clavier iOS ne
     zoome pas la page a chaque prise de focus. */
  table.mapping-table.m-cards > tbody > tr > td:has(> select),
  table.mapping-table.m-cards > tbody > tr > td:has(> input[type="text"]) {
    flex-direction: column;
    align-items: stretch;
    gap: var(--espace-2xs);
    padding: var(--espace-2xs) 0;
    text-align: left;
  }
  /* 45 % de la carte suffisent a une etiquette posee en face d'un montant, pas
     a « Association · Sous-total » ni a « Entreprise · Applicable », qui se
     repliaient en deux lignes a cote d'un champ de trois lettres. Ici
     l'etiquette prend ce qu'il lui faut et cede devant la valeur plutot que
     devant une part fixe de la carte. */
  table.mapping-table.m-cards > tbody > tr > td::before {
    flex: 0 1 auto;
    min-width: 0;
    max-width: none;
  }
  table.mapping-table.m-cards > tbody > tr > td > select,
  table.mapping-table.m-cards > tbody > tr > td > input[type="text"] {
    width: 100%;
    max-width: 100%;
    min-width: 0;
    min-height: var(--m-tap);
  }

  /* Sauf les colonnes de code. Le sens usuel d'un compte, le code d'un cycle,
     celui d'un sous-total : une a trois lettres, et la feuille leur donne 3.5 a
     4.2rem sur ordinateur. La regle d'empilement ci-dessus est ecrite pour des
     noms ; sur ces champs-la elle etirait un « C » seul au milieu de 338 px,
     sous une etiquette qui en occupait 90. Une fiche de racine montait ainsi a
     495 px pour sept champs d'une lettre, et il y en a 601 a la suite.

     Ils reprennent la forme des autres lignes de la carte (etiquette a gauche,
     valeur a droite) a une largeur commune qui les range en colonne sous le
     meme bord. `center` et non `baseline` : un controle de 44 px ne se pose pas
     sur la ligne de base d'une etiquette qui en fait 13, et qui se replie
     souvent en deux. */
  table.mapping-table.m-cards > tbody > tr > td:has(> input.mapping-cycle-input),
  table.mapping-table.m-cards > tbody > tr > td:has(> select.mapping-sens-input),
  table.mapping-table.m-cards > tbody > tr > td:has(> .mapping-cycle-badge) {
    flex-direction: row;
    align-items: center;
    gap: var(--espace);
  }
  table.mapping-table.m-cards > tbody > tr > td > input.mapping-cycle-input,
  table.mapping-table.m-cards > tbody > tr > td > select.mapping-sens-input,
  table.mapping-table.m-cards .mapping-cycle-badge > input[type="text"] {
    flex: none;
    width: 4.2rem;
    max-width: 4.2rem;
  }
  /* Colonne "Code" des cycles et des sous-totaux : l'application y affiche
     l'icone du cycle et laisse le code dans l'attribut title. Sur ordinateur le
     survol le revele ; au doigt il n'y a pas de survol, et le code (qui est
     l'identifiant du cycle, celui qu'on saisit dans la table des racines)
     devenait invisible. On le remet a cote de l'icone. */
  table.mapping-table.m-cards .mapping-cycle-badge[title]::after {
    content: attr(title);
    font-size: var(--fs-sm);
    font-weight: 600;
    letter-spacing: var(--suivi-serre);
  }

  /* Le code cycle de la table des racines est enveloppe dans une pastille, il
     echappe donc au selecteur d'enfant direct ci-dessus. Il reste etroit, c'est
     un code de trois lettres, mais il doit avoir la meme hauteur de frappe. */
  table.mapping-table.m-cards .mapping-cycle-badge > input[type="text"] {
    min-height: var(--m-tap);
  }

  table.mapping-table.m-cards > tbody > tr > td.m-card-title > input[type="text"] {
    max-width: 100%;
    width: 100%;
  }
  /* Une carte affiche sa colonne titre sans etiquette : dans une fiche de
     lecture, le nom de la ligne est son titre et n'a pas besoin d'etre annonce.
     Le mapping en fait un champ de saisie, et la carte y perdait la moitie de
     son sens : « Nom (entreprise) » se presentait comme une boite anonyme
     au-dessus de « Nom (association) », son exacte symetrique, etiquetee elle.
     Sans compter que le titre, etant une boite de saisie comme les autres, ne
     ressemblait pas davantage a un titre. On lui rend son etiquette ; il reste
     en tete de carte, au-dessus du trait. */
  table.mapping-table.m-cards > tbody > tr > td.m-card-title:has(> input[type="text"]) {
    flex-direction: column;
    align-items: stretch;
    gap: var(--espace-2xs);
  }
  table.mapping-table.m-cards > tbody > tr > td.m-card-title:has(> input[type="text"])::before {
    content: attr(data-label);
  }
  /* Les cases a cocher "Visible / Applicable" restent a droite, sans etirement.
     C'est la cellule entiere qui les bascule (mobile.js), pas la case seule :
     on lui donne la hauteur de la cible tactile et le curseur qui l'annonce. */
  table.m-cards > tbody > tr > td:has(> input[type="checkbox"]) {
    min-height: var(--m-tap);
    /* Les cellules d'une carte alignent leurs deux moities sur la ligne de
       base, ce qui est juste entre deux textes. Une case de 22 px n'en a pas :
       elle se posait sur la premiere ligne de l'etiquette, et « ENTREPRISE ·
       APPLICABLE », qui se replie, laissait son second mot pendre sous elle. */
    align-items: center;
    cursor: pointer;
    -webkit-tap-highlight-color: rgba(0, 0, 0, .06);
  }
  table.m-cards > tbody > tr > td > input[type="checkbox"] {
    flex: none;
    width: 22px;
    height: 22px;
  }

  /* ---------- 13. Mon compte & cabinets ---------------------------------- */

  .compte-panel { max-width: none; }

  /* Une rangee qui s'aligne sur son bouton doit le suivre quand il grandit.

     `style.css` arbitre les deux champs du compte d'un registre sur le bouton
     pose a cote d'eux -- « elles ne vivent pas dans une rangee dense, elles
     vivent a cote d'un bouton, et c'est sur lui qu'elles s'alignent » --, et
     sur ordinateur la rangee tient parole : ses cinq elements font 28 px,
     tous. Au doigt, les boutons montent au plancher et les champs restaient
     sur la mesure d'ordinateur : cinq hauteurs dans une rangee, 31, 31, 44,
     38 et 44, les deux plus petites etant les seules ou l'on tape -- un
     courriel et un mot de passe, qu'on ne peut ni deviner ni recoller.

     La regle porte sur la rangee entiere plutot que sur les deux champs :
     c'est l'alignement qui est arbitre, pas leur taille propre. */
  .registre-compte-champs > * { min-height: var(--m-tap); }

  /* `.compte-form` n'a plus de grille a lui : c'est le panneau qui range les
     huit blocs de la page, et il se replie ici sur une colonne tout seul.

     La regle qui vivait ici remettait le formulaire en colonne flex, et y
     redisait `align-items: stretch`. `align-items` ne veut en effet pas dire la
     meme chose des deux cotes : dans une grille il aligne les blocs en haut de
     leur rangee -- verticalement --, en flex colonne il bascule d'axe et
     « start » y veut dire « chaque bloc a la largeur de son contenu ». Les
     quatre `<fieldset>` se calaient donc a 239 px dans les 362 de la colonne,
     et l'adresse de connexion, plus longue que la case ainsi retrecie, se
     coupait a « leonard.minh.rambeau@g ». Une grille a une colonne n'a pas ce
     defaut : `justify-items` y vaut `stretch` d'origine. */

  /* Les deux gestes de fin de page se rangent l'un sous l'autre des que la
     colonne se retrecit : la deconnexion y prend toute la largeur (voir plus
     bas), et deux boutons de largeurs si differentes cote a cote sur un
     telephone tiennent moins bien qu'empiles. */
  .compte-pied {
    flex-direction: column;
    align-items: stretch;
    gap: var(--espace-sm);
  }
  .compte-form input { width: 100%; }

  /* La file des intervenants les montre tels qu'ils seront, donc a la taille
     des ronds d'une ligne de mandat -- 56 px. Deux files de quatre ronds cote
     a cote font 450 px de large, soit soixante de trop des le premier
     telephone, et un `<fieldset>` ne sait pas retrecir sous la largeur
     minimale de son contenu : c'est tout le formulaire du compte qui partait a
     droite avec elles. Les ronds de l'apercu reprennent ici la taille des
     tuiles de filtre, qui est celle des ronds du telephone. */
  .compte-intervenants-file .dossier-avatar { --avatar-d: 38px; }

  /* Les trois gestes de la photo tenaient sur trois rangees pleine largeur.
     La colonne du `.reglages-row` -- juste pour un champ de saisie et le
     bouton qui le valide -- donnait 362 px a « Cadrer », qui en demande cent,
     et les memes 362 a la corbeille, qui est une icone de 44. Celle-ci se
     retrouvait seule sur sa ligne, en rouge, sous un lien lui-meme seul sur la
     sienne : trois etages pour un seul groupe de commandes, et le geste
     destructeur isole tout en bas, la ou l'oeil le prend pour la fin du bloc.

     La rangee revient donc, avec un retour a la ligne. « Remplacer la photo »
     garde la largeur pleine -- c'est l'action principale --, « Cadrer » et la
     corbeille se partagent la seconde ligne, l'un prenant la place, l'autre
     rangee a droite a la taille du doigt. C'est le geste de la date de
     cloture, deux cents lignes plus haut : on redonne la rangee, et le
     principal passe seul sur la sienne.

     L'etat (« Photo enregistree ») garde sa propre ligne : c'est une phrase,
     pas une commande. Tant qu'il n'a rien a dire il ne prend rien, sinon la
     rangee garderait sous elle l'ecart d'une ligne vide. */
  .compte-photo-ligne {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
  }
  /* Ces quatre lignes ne changeaient rien depuis le jour ou elles ont ete
     ecrites : la rangee finit par une corbeille, donc la liste generique des
     rangees a boutons a icone la prenait d'abord, et un `:has()` la fait peser
     (0,6,0) la ou `.compte-photo-ligne > .btn-link` ne pese que (0,2,0). Le
     commentaire ci-dessus decrivait ainsi une mise en page que personne
     n'avait sous les yeux : « Cadrer » recevait quand meme la pleine largeur,
     la corbeille tombait seule sur une troisieme ligne contre le bord droit,
     et les trois boutons prenaient 148 px de haut pour 253 px de large dont
     362 etaient disponibles. L'exception posee la-haut leur rend la main. */
  .compte-photo-ligne > .btn-primary,
  .compte-photo-ligne > .reglages-hint-inline { flex: 1 0 100%; }
  .compte-photo-ligne > .btn-link { flex: 1; }
  .compte-photo-ligne > .btn-icon-only { flex: none; }
  .compte-photo-ligne > .reglages-hint-inline:empty { display: none; }

  /* Le choix d'une couleur : la roue est un bouton, elle passe donc a la cible
     standard. Les seize pastilles qui vivaient ici ont disparu le 24 aout
     2026 ; elles etaient collees les unes aux autres et au doigt, la voisine
     partait pour l'autre. Les trois ecritures HEX, RVB et HSL, qui prenaient
     ensuite toute la largeur une par ligne, ont disparu le lendemain : la roue
     est desormais seule ici comme ailleurs. */
  .choix-couleur input.choix-couleur-roue { width: calc(var(--m-tap) * 1.4); height: var(--m-tap); }

  /* Les deux roues de la charte d'un client prennent la meme cible tactile que
     celle de « Mon compte » : c'est le meme geste, et une roue de 28 px se vise
     mal au doigt. Elles gardent la hauteur de la rangee sur ordinateur ; ici la
     rangee se replie, plus rien ne les y oblige.

     Le mot repasse au-dessus de sa roue, la ou il est a cote d'elle sur un
     ecran large : empilees, les deux rangees calaient chacune sa roue derriere
     son mot, et « Principale » est plus court que « Secondaire » -- les deux
     roues partaient donc de deux abscisses differentes, seules de la colonne a
     ne pas commencer au meme bord que l'apercu et les boutons. */
  .charte-couleur {
    flex-direction: column;
    align-items: flex-start;
  }

  .charte-couleur input[type="color"] {
    width: calc(var(--m-tap) * 1.4);
    height: var(--m-tap);
  }

  /* L'apercu suit : il n'est pas un bouton, mais colle a des boutons de 44 px
     il ferait une bande basse au milieu d'une colonne de cibles pleines. */
  .charte-apercu { height: var(--m-tap); }

  /* Et ses aplats s'etendent avec lui. Etire sur toute la colonne comme les
     boutons qui l'entourent, l'apercu gardait ses trois bandes de 26 px
     serrees contre le bord gauche : cent vingt-huit pixels de dessin, puis
     deux cent trente-trois de blanc, le tout dans un cadre. Un cadre autour
     de rien ne se lit pas comme un apercu, mais comme une ligne qui n'a pas
     su se remplir -- exactement ce que ce fichier reproche deja a un carre de
     44 px laisse seul contre le bord.

     Ce qui grandit, ce sont les aplats et eux seuls : le commentaire de la
     rubrique dit qu'« une charte se juge sur ses aplats », et la place rendue
     leur revient donc plutot qu'a l'encre, qui n'est qu'un mot et n'a rien a
     gagner a etre calee plus large. Le nom du client reste a la fin de la
     bande, dans le meme ordre que sur un ecran large.

     `flex-grow` seul, sans la forme courte : la base reste `auto`, donc les
     26 px. Des 600 px l'apercu revient a sa largeur naturelle, il n'y a plus
     d'espace libre a distribuer, et les bandes reprennent leur mesure sans
     qu'on ait a le redire. */
  .charte-apercu-bande { flex-grow: 1; }

  /* La corbeille d'une mission porte un cadre depuis le 27 aout 2026 : elle
     reprend donc la cible tactile pleine, comme les deux autres corbeilles du
     panneau. */
  .reglages-exercice-retirer {
    width: var(--m-tap);
    height: var(--m-tap);
  }

  /* Une ligne de cabinet devient une petite carte verticale : la ligne unique
     du grand ecran demanderait ici six cibles cote a cote dans la largeur d'un
     pouce.

     Elle tenait sur trois rangees, et la deuxieme etait a moitie vide. La roue
     du systeme mesure 62 px -- `--m-tap` fois 1,4, fixe vingt lignes plus haut
     --, et la case `coul` lui en donnait 233 : 171 px de blanc entre la
     couleur et le bord droit de la carte, sous lesquels « Enregistrer »
     occupait a lui seul une troisieme rangee. Le bouton remonte donc a cote de
     la roue, ou il prend precisement ce vide, et la carte perd une rangee.

     Quatre colonnes plutot que trois : la deuxieme est celle de la roue, ce
     qui evite que la largeur intrinseque du champ de nom -- il en traverse
     deux -- ne vienne la gonfler. */
  .cabinet-gestion-form {
    display: grid;
    grid-template-columns: auto calc(var(--m-tap) * 1.4) minmax(0, 1fr) auto;
    grid-template-areas:
      "apercu nom  nom   suppr"
      "init   coul valid valid";
    gap: var(--espace-sm);
    align-items: center;
  }

  /* Le cadre est porte par le bloc, pas par la rangee : la feuille du grand
     ecran le dit et donne la raison, qui vaut ici aussi -- une bordure posee
     sur la seule rangee laisserait le depliage « Identite et coordonnees »
     sortir par en dessous. La rangee en portait une quand meme, avec son fond
     blanc, son ombre et trois quarts de rem de rembourrage, a l'interieur du
     cadre du bloc : chaque cabinet s'affichait en carte dans une carte, deux
     bordures grises separees de dix pixels de blanc.

     Le bloc prend donc ce que portait la rangee. Depuis le 30 aout 2026 le
     cadre lui vient de `.tuile-encart`, commun aux trois zones de la page, et
     il ne reste ici que le creux -- plus large sur telephone qu'a l'ecran,
     parce qu'une ligne repliee sur quatre rangees a besoin d'air en haut et en
     bas la ou une ligne unique n'en demande pas. */
  .cabinet-gestion-bloc {
    padding: var(--espace-sm);
  }

  /* Le creux lateral d'un encart se resserre d'un cran sur telephone : la
     tuile qui le porte a deja pris 20 px de chaque cote, sur une gouttiere de
     page qui en prend autant. Trois retraits emboites laissaient a une ligne
     de cabinet moins de la moitie de la largeur de l'ecran. */
  .tuile-encart {
    padding-left: var(--espace-sm);
    padding-right: var(--espace-sm);
  }

  /* `start` et non l'etirement par defaut d'une case de grille : la pastille du
     cabinet est un carre de 26 px de large, et elle prenait toute la colonne --
     un bandeau rouge de 157 px portant un « + » au milieu, la ou la ligne du
     nouveau cabinet attend une vignette. */
  .cabinet-gestion-apercu { grid-area: apercu; justify-self: start; }
  /* `min-width: 0` : le champ traverse deux colonnes, et sans plancher a zero
     c'est sa largeur intrinseque -- une vingtaine de caracteres -- qui aurait
     ete reclamee a la premiere des deux. */
  .cabinet-gestion-nom { width: 100%; min-width: 0; }
  /* La case des initiales n'en porte que quatre (`maxlength="4"`), mais un
     champ de texte sans largeur declaree occupe par defaut une vingtaine de
     caracteres, et `width: 100%` dans une colonne `auto` ne la borne pas : le
     pourcentage se mesure sur une colonne que la case elle-meme dimensionne.
     Elle s'etalait donc sur 157 px, emportait la pastille avec elle, et il ne
     restait plus au nom commercial que la moitie de l'ecran. Bornee ici sur le
     jeton des cases courtes, qui suit la taille du texte qu'elle porte. */
  .cabinet-gestion-initiales { width: var(--champ-court); }
  .cabinet-gestion-nom,
  .cabinet-gestion-initiales { min-height: var(--m-tap); }
  .cabinet-gestion-nom { grid-area: nom; }
  .cabinet-gestion-initiales { grid-area: init; }
  .cabinet-gestion-couleur { grid-area: coul; }
  /* La roue reprend la cible du doigt, que la ligne du grand ecran ramenait a
     la hauteur des cases (26 px). */
  .cabinet-gestion-couleur input.choix-couleur-roue { height: var(--m-tap); }
  .cabinet-gestion-form > .btn-primary { grid-area: valid; width: 100%; min-height: var(--m-tap); }
  .cabinet-gestion-supprimer { grid-area: suppr; }
  /* Ici la rangee est une grille nommee, pas une file : la colonne de la
     poubelle se referme d'elle-meme quand personne ne l'occupe, et le nom
     commercial prend toute la carte -- ce qu'on veut.

     Le grand ecran, lui, tient en permanence la colonne du voyant
     d'enregistrement pour que rien ne saute a la frappe. Cette carte-ci n'a pas
     de colonne a tenir : ses cases sont nommees, un voyant muet ne deplace
     personne, et une case reservee de 7 rem deviendrait une troisieme rangee
     vide sous chaque cabinet. Le voyant reprend donc ici sa place ordinaire --
     absent quand il se tait, une rangee entiere quand il parle. */
  .cabinet-gestion-form .champ-etat[hidden] { display: none !important; }
  .cabinet-gestion-form .champ-etat {
    grid-column: 1 / -1;
    min-width: 0;
    text-align: left;
  }
  /* Elle n'a pas de poubelle non plus : la colonne se referme bien, mais
     l'ecart qui la precedait reste, et le nom du nouveau cabinet s'arretait
     8 px avant le bouton d'en dessous. Il va donc jusqu'au bord. */
  .cabinet-gestion-ajout .cabinet-gestion-nom { grid-column: 2 / -1; }

  .cabinet-gestion-list { display: flex; flex-direction: column; gap: var(--espace-sm); }

  /* Meme raison que la ligne de cabinet juste au-dessus, et meme remede. Les
     cinq colonnes que la rangee des collaborateurs a prises sur grand ecran le
     26 aout 2026 valent 470 px a elles seules -- 80 de plus que l'ecran -- et
     une grille, contrairement a une rangee souple, ne se replie pas d'elle-meme
     : la page entiere debordait vers la droite. Elle redevient ici une petite
     carte, identite en haut, role sur sa propre ligne. */
  .equipe-gestion-form {
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: var(--espace-2xs) var(--espace-sm);
  }
  /* La carte porte desormais le cadre, le fond et l'ombre pour les deux
     ecrans (voir style.css, refonte du 27 aout 2026) : la rangee les reprenait
     ici, et les deux se superposaient -- une carte dans une carte, deux ombres
     l'une sur l'autre. */
  .equipe-gestion-apercu { grid-column: 1; grid-row: 1 / span 2; align-self: start; }
  .equipe-gestion-identite { grid-column: 2; grid-row: 1; }
  .equipe-gestion-supprimer { grid-column: 3; grid-row: 1; }
  .equipe-gestion-role,
  .equipe-gestion-role-lecture { grid-column: 2 / -1; grid-row: 2; width: 100%; }
  .equipe-gestion-role { min-height: var(--m-tap); }
  /* Le role ecrit en toutes lettres n'a plus de liste deroulante sur laquelle
     s'aligner : il se range sous le nom, sans le rembourrage qui lui servait a
     imiter un champ. */
  .equipe-gestion-role-lecture { padding-left: 0; padding-right: 0; }

  /* La ligne d'invitation empile son bouton : il porte une phrase entiere et ne
     tient pas a cote du champ dans la largeur d'un telephone. */
  .equipe-gestion-ajout {
    grid-template-columns: auto minmax(0, 1fr);
    background: none;
  }
  .equipe-gestion-ajout > .equipe-gestion-apercu { grid-row: 1; }
  .equipe-gestion-email-input {
    grid-column: 2;
    grid-row: 1;
    min-height: var(--m-tap);
  }
  .equipe-gestion-ajout > .btn-primary {
    grid-column: 1 / -1;
    grid-row: 2;
    width: 100%;
    min-height: var(--m-tap);
    justify-content: center;
  }

  /* L'ecart au renvoi qui le precede est desormais celui de la tuile qui les
     porte tous les deux. */
  /* La deconnexion prenait ici toute la largeur et centrait son libelle, parce
     qu'elle pendait au bas de la page sans aucun bord gauche a rejoindre : le
     retrait optique que `style.css` lui pose n'avait alors plus d'objet. La
     tuile du pied lui rend ce bord. Les deux gestes reprennent donc la meme
     regle, et leur encre tombe sur le creux de la tuile, la ou tombent les
     titres des cinq tuiles au-dessus -- au lieu d'un libelle cale a gauche et
     l'autre au milieu, a onze pixels l'un de l'autre. */

  /* ---------- 14. Connexion / MFA / mot de passe -------------------------- */

  /* Le haut et le bas valaient 24 et 32 px : une difference qui placait la
     carte quand la marge la placait, et qui ne fait plus que la decentrer
     maintenant que c'est le centrage qui la place. */
  .login-wrap {
    padding: var(--espace-xl) var(--m-gutter);
    min-height: auto;
  }

  /* La carte portait trois valeurs differentes pour ses quatre cotes --
     22,4 en haut, 18,4 sur les cotes, 25,6 en bas -- dont aucune sur
     l'echelle. Un seul barreau, celui du dessous de l'ordinateur. */
  .login-card {
    width: 100%;
    max-width: none;
    padding: var(--espace-lg);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
  }

  /* `.login-card button`, et non `.login-card .btn-primary` comme c'etait ecrit
     ici : les six gabarits d'authentification posent un `<button type="submit">`
     nu -- ils n'ont jamais porte cette classe, et la regle ne designait donc
     rien. Le bouton d'action des six pages restait a 41 px sur telephone, sous
     les 44 de `--m-tap` que tout le reste du produit respecte, et 2 px plus bas
     que les champs juste au-dessus de lui : la premiere page qu'on voit du
     produit avait son bouton principal plus petit que ses cases de saisie.

     Les champs prennent la meme mesure. Ils en sont a 42,8 px sur telephone --
     leur texte y passe a 16 px pour empecher iOS de zoomer a la mise au point,
     ce qui les fait grandir d'un pixel par rapport a l'ordinateur, mais pas
     jusqu'a la cible. Toute la colonne de la carte tient desormais sur une
     seule hauteur.

     `width: 100%` retire du bouton : `.login-card button` le porte deja dans
     `style.css`, pour les deux surfaces. */
  .login-card input,
  .login-card button { min-height: var(--m-tap); }
  .login-card input { width: 100%; }
  .login-sub { font-size: var(--fs); }

  /* `.mfa-enroll-wrap` ne redit plus le retrait de `.login-wrap`, qu'elle
     porte de toute facon : la page de l'enrolement cumule les deux classes, et
     les deux valeurs etaient devenues la meme.

     Les deux regles qui suivaient sont parties le 31 aout 2026. `.mfa-qr`
     bridait la largeur de la boite a 220 pixels : la boite n'ayant plus ni
     cadre ni creux, elle prend toute la largeur et centre son carre au lieu de
     l'aligner a gauche. `.mfa-secret` redisait `--fs-sm`, deja pose sur le
     bureau, et un `overflow-wrap` que la cle porte maintenant elle-meme. */

  .disclaimer { font-size: var(--fs-2xs); }

  /* ---------- 15. Widget ticket flottant --------------------------------- */

  /* Le bouton flottant remonte au-dessus de la barre d'onglets. Quand un FAB
     de page est present, mobile.js pose .m-has-fab et le widget se decale
     encore, pour venir se poser au-dessus du FAB plutot que par-dessus.
     Attention : c'est le conteneur .ticket-widget qui porte le position:fixed,
     le bouton lui-meme est en flux statique. Decaler le bouton n'a aucun
     effet : les offsets doivent aller sur le conteneur. */
  .ticket-widget {
    bottom: calc(var(--m-tabbar-hors-tout) + 14px);
    right: var(--m-gutter);
  }
  body.m-has-fab .ticket-widget {
    bottom: calc(var(--m-tabbar-hors-tout) + 76px);
  }

  .ticket-widget-toggle {
    width: 46px;
    height: 46px;
  }

  /* Le conteneur du widget est a z-index 1000, bien au-dessus des feuilles
     modales (91) : sa bulle flottait par-dessus le contenu des feuilles.
     On l'escamote tant que le scroll est verrouille, c'est-a-dire tant qu'une
     feuille est ouverte : y compris le panneau du widget lui-meme, qui
     recouvre de toute facon cette zone et porte son propre bouton de
     fermeture (#ticket-widget-close). On ne touche qu'au bouton : masquer le
     conteneur emporterait le panneau avec lui. */
  body.m-scroll-locked .ticket-widget-toggle { display: none; }

  /* Le popover de commentaires est une feuille lui aussi (cf. §11) mais il ne
     verrouille pas le scroll : il est pose par app.js, partage avec
     l'ordinateur, et la page derriere doit rester lisible. Sa bulle recouvrait
     la zone de saisie : on l'escamote pendant qu'il est ouvert. */
  body:has(.commentaire-popover) .ticket-widget-toggle { display: none; }

  /* Le panneau devient une feuille pleine largeur ancree en bas. */
  .ticket-widget-panel {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    width: 100%;
    max-width: none;
    max-height: 86vh;
    display: flex;
    flex-direction: column;
    border-radius: var(--m-radius-sheet) var(--m-radius-sheet) 0 0;
    box-shadow: var(--shadow-lg);
    padding-bottom: var(--m-safe-bottom);
    z-index: 95;
  }
  /* ticket-widget.js ouvre/ferme le panneau via l'attribut hidden : la regle
     ci-dessus l'emporterait sinon sur le defaut du navigateur (meme
     specificite, declaree plus tard) et le panneau resterait ouvert. */
  .ticket-widget-panel[hidden] { display: none; }

  /* La largeur du mode Assistant (style.css, min(440px, 100vw - 3rem)) passait
     devant le `width: 100%` ci-dessus par sa double classe : la feuille
     s'arretait a 342 px sur 390, un creux a droite. */
  .ticket-widget-panel.est-en-assistant { width: 100%; }

  /* L'ouverture d'une feuille monte du bas de l'ecran, pas du coin du bouton
     comme sur ordinateur : c'est le bord ou elle est ancree. Une courbe sans
     rebond, `--ease-spring` la decollerait du bas en depassant. */
  .ticket-widget-panel {
    animation-name: ticket-widget-feuille;
    animation-duration: .3s;
    animation-timing-function: var(--ease);
  }

  .ticket-widget-header { flex: none; }
  .ticket-widget-body { flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain; }
  .ticket-widget-footer { flex: none; flex-wrap: wrap; gap: var(--espace-sm); }
  .ticket-widget-footer .btn-primary { width: 100%; min-height: var(--m-tap); }
  .ticket-widget-texte { min-height: 110px; }
  .ticket-widget-dropzone { padding: var(--espace); font-size: var(--fs-sm); }

  /* ---------- 16. Bandeau d'alerte --------------------------------------- */

  /* Le meme barreau pour le creux et pour l'ecart : 8,8 et 8 etaient deux
     valeurs a la main, voisines sans etre egales, la ou une seule suffit. */
  .alerte-bandeau {
    padding: var(--espace-sm) var(--m-gutter);
    padding-top: calc(var(--espace-sm) + var(--m-safe-top));
    font-size: var(--fs-sm);
    align-items: flex-start;
    gap: var(--espace-sm);
  }
  .alerte-bandeau-texte { line-height: 1.35; }
  .alerte-bandeau-fermer {
    min-width: var(--m-tap);
    min-height: var(--m-tap);
    flex: none;
  }
  /* Le bandeau occupe deja l'encoche : le header n'a plus a la reserver. */
  body.m-has-bandeau .app-header-inner { padding-top: var(--espace-xs); }

  /* ---------- 17. Dictee vocale ------------------------------------------ */

  /* Le micro se grossit par son enveloppe et non par lui-meme : un min-width
     pose sur le bouton l'agrandissait sans que le champ d'a cote l'apprenne,
     et le texte finissait dessous. En passant par --mic-taille, la reserve du
     champ et la hauteur qu'il tient suivent d'elles-memes. */
  .ticket-widget-champ-texte,
  .ticket-card-suivi-champ {
    --mic-taille: var(--m-tap);
  }

  .ticket-widget-mic-statut,
  .ticket-card-texte-mic-statut,
  .ticket-card-suivi-mic-statut { font-size: var(--fs-xs); }

  /* ---------- 17 bis. Etats masques -------------------------------------- */

  /* mobile.css declare des display: sur des elements que le JS pilote via
     l'attribut hidden. A specificite egale, la derniere regle gagne : sans
     ces garde-fous, .m-sheet { display: flex } l'emporterait sur le
     [hidden] par defaut du navigateur et la feuille resterait visible. */
  .m-sheet[hidden],
  .m-sheet-backdrop[hidden],
  .m-yearbar[hidden],
  .m-chiprow[hidden],
  .m-tabbar[hidden],
  .m-fab[hidden],
  .m-chip[hidden],
  .m-back[hidden],
  .m-tab[hidden],
  .m-sheet-header[hidden],
  .m-sheet-grip[hidden] { display: none; }

  /* Element retire de la mise en page mobile (deplace ailleurs par mobile.js). */
  .m-hidden { display: none !important; }

  /* Avec un bouton retour, le logo devient redondant et vole la largeur du
     titre : on le retire de la barre. */
  body.m-has-back .app-logo-link,
  body.m-has-back .app-version { display: none; }

  /* Titres de section a l'interieur d'une feuille modale. */
  .m-sheet-section-title {
    margin: 0 0 var(--espace-sm);
    font-size: var(--fs-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: var(--suivi-entete);
    color: var(--gray-4);
  }
  /* Deux marges verticales voisines fusionnent : ce qui precede un titre de
     section pose deja --espace (12,8) sous lui, donc un titre qui reprendrait
     --espace ne separerait plus rien. --espace-md est le barreau juste
     au-dessus, et le premier qui fasse encore quelque chose. Il valait 17,6. */
  .m-sheet-section-title:not(:first-child) { margin-top: var(--espace-md); }

  /* Les tuiles de filtre respirent dans la feuille : elles passent a la ligne
     et gagnent une taille confortable au doigt.

     Meme ecart que sur l'ecran, et calcule de la meme facon : les deux anneaux
     de la tuile retenue sont des `box-shadow`, ils debordent de
     --tuile-anneau sans prendre de place. L'ecart porte donc le blanc d'un
     rang de carres plus ces deux rayons -- et ici les tuiles font 44 px et
     passent a la ligne, ce qui met aussi deux rangs l'un sous l'autre.

     Les carres d'etat, eux, n'ont pas d'anneau : retenus, ils prennent une
     bordure et un fond, rien qui deborde de la boite. Leur ecart n'a donc pas
     a le reserver et reste sur son barreau. */
  .m-filters-sheet .filtre-tuiles {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--espace-xs) + 2 * var(--tuile-anneau));
  }
  .m-filters-sheet .filtre-etats {
    display: flex;
    flex-wrap: wrap;
    gap: var(--espace-md);
  }
  .m-filters-sheet .filtre-tuile,
  .m-filters-sheet .filtre-etat-tuile {
    width: var(--m-tap);
    height: var(--m-tap);
    min-width: var(--m-tap);
    min-height: var(--m-tap);
  }
  .m-filters-sheet .filtre-tuile { --avatar-d: var(--m-tap); }

  /* Le trait qui separe les deux filtres dans la barre d'outils de l'ecran n'a
     plus rien a separer ici : la feuille les range l'un sous l'autre, chacun
     sous son intitule. Il restait un trait vertical d'un pixel flottant a
     gauche des carres d'etat, rattache a rien. */
  .m-filters-sheet .filtre-etats {
    margin-left: 0;
    padding-left: 0;
    border-left: 0;
  }

  /* Feuille "Affichage" de l'onglet Analyse : format des montants, arborescence
     et exports. Chaque groupe recupere la pleine largeur : le segmente des
     unites s'etale, les actions s'empilent en cibles de --m-tap. */
  .m-analyse-sheet .toolbar-group {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--espace-xs);
    margin: 0;
  }
  .m-analyse-sheet .unit-toggle {
    display: flex;
    width: 100%;
  }
  .m-analyse-sheet .unit-btn {
    flex: 1;
    min-height: var(--m-tap);
  }
  .m-analyse-sheet .btn-link {
    display: flex;
    width: 100%;
    min-height: var(--m-tap);
    align-items: center;
    justify-content: flex-start;
    gap: var(--espace-xs);
  }

  /* Formulaire de creation de mandat dans sa feuille. */
  .m-form-sheet .dossier-form { display: flex; }
  .m-form-sheet .dossier-form .btn-primary { min-height: var(--m-tap); }
  .m-form-sheet .dossier-form[hidden] { display: none; }

  /* ---------- 18. Accessibilite & preferences ---------------------------- */

  /* Pas d'etat :hover colle apres un tap. */
  @media (hover: none) {
    .dossier-link:hover,
    .icon-btn:hover,
    .cycles-nav-btn:hover,
    .dossier-tab-btn:hover { background: inherit; }
  }

  @media (prefers-reduced-motion: reduce) {
    .m-sheet,
    .m-sheet-backdrop,
    .m-tab .icon,
    .m-fab { transition: none; }
  }

  /* ---------- 19. Le Journal des modifications --------------------------- */

  /* La grille du journal passe en une colonne des 900 px, et la colonne des
     editions remonte en premier (`order: -1`). Sur un iPad c'est le bon
     choix : le sommaire coiffe l'edition. Sur un telephone, le meme geste
     donnait autre chose -- on ouvre le journal du matin et on tombe sur une
     liste de dates, haute de 156 px pour trois editions et bornee a 60vh,
     soit 487 px des qu'il y en a une dizaine, avec son propre defilement
     vertical au milieu d'une page qui defile deja. C'est le piege classique :
     le doigt qui veut faire descendre la page fait descendre la liste.

     La bande qui defile en largeur le defait. Elle garde l'ordre (le sommaire
     reste en tete), elle garde les liens (aucun n'est cache derriere un pli),
     et elle rend sa hauteur au contenu : un rang au lieu d'une colonne. Le
     defilement horizontal ne peut plus voler celui de la page, et la barre
     reste cachee comme sur `.unit-toggle`, dont c'est la forme reprise ici.

     Chaque edition devient une carte de 10,5 rem : deux tiennent dans 375 px,
     assez pour qu'on voie qu'il y en a d'autres. La largeur est ce qui donne
     son sens a l'ellipse du sujet, qui sans borne ne coupait jamais. */
  .jm-aside {
    position: static;
    padding: var(--espace-sm);
  }
  .jm-aside-liste {
    display: flex;
    gap: var(--espace-xs);
    max-height: none;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
  }
  .jm-aside-liste::-webkit-scrollbar { display: none; }
  .jm-aside-liste > li { flex: none; }
  .jm-aside-lien {
    width: 10.5rem;
    border: 1px solid var(--gray-3);
    border-left-width: 2px;
    border-radius: var(--radius-sm);
    padding: var(--espace-xs) var(--espace-sm);
  }

  /* Les quatre mesures de l'entete sont un rang qui se replie, avec un ecart
     de 1,4 rem entre elles : a 375 px elles se pavaient 1, puis 2, puis 1,
     chacune calee sous un mot de longueur differente. Quatre nombres qu'on
     lit ensemble se rangent en carre, pas en escalier. */
  .jm-mesures {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--espace-sm) var(--espace-md);
  }

  /* La page de l'acceptation a rendu son creux du haut a la frise, qui le
     peint pour qu'aucune tuile ne passe au-dessus d'elle quand elle se colle
     (voir style.css). Le creux du telephone n'est pas celui du bureau : sans
     cette ligne, la frise en poserait trente-deux ou la page en posait
     treize. */
  .acceptation-page .acceptation-frise { padding-top: var(--espace); }

  /* Le rapport du FEC rend lui aussi son creux du haut a sa bande de tete, et
     le creux du telephone n'est pas celui du bureau. Mais la bande ne se colle
     pas ici : l'entete, puis le resume replie sur quatre rangs de pastilles,
     prennent deux cent quatre-vingts pixels a 390 -- le tiers de l'ecran, pour
     un outil dont on se sert trois fois. Sur un ecran de 844 px, remonter
     coute moins cher que perdre un tiers de la page. */
  .cfec-tete {
    position: static;
    padding-top: var(--espace);
  }

  /* Les notifications gardent la fenetre, mais rendent la bande de l'appareil
     au defilement. Elle dit que le navigateur bloque les notifications et
     propose de les activer : cent cinquante-cinq pixels sur les sept cent
     trente et un du telephone, un cinquieme de l'ecran retire en permanence a
     la liste des clients, pour une phrase qu'on lit une fois. Sur un grand
     ecran elle ne coute rien et reste ou elle est.

     La grille rend donc son debordement et son creux du pied a la page, faute
     de quoi ils s'ajouteraient a ceux-la. */
  .app-main-pleine.notifs-page {
    display: block;
    overflow: auto;
    padding-bottom: calc(52px + var(--espace-xl) * 2);
  }

  .app-main-pleine > .notifs {
    overflow: visible;
    padding-bottom: 0;
  }
}

/* ==========================================================================
   Le panneau Mandat, des qu'une rangee tient sa ligne (tablette, telephone
   couche).

   Tout ce qui precede est ecrit pour un telephone debout : les rangees de
   reglages y passent en colonne et chaque controle prend la pleine largeur,
   parce qu'un champ ne tient pas a cote d'un autre dans 390 px. Cette bande
   de regles vaut pourtant jusqu'a 820 px, c'est-a-dire sur un iPad, et la
   elle ne decrit plus rien de vrai : « Detourer le fond » y devenait une
   barre de 792 px de large, posee sous deux autres barres identiques, et un
   bouton fantome pleine largeur -- fond blanc, filet gris, texte a gauche --
   ne se distingue plus d'un champ de saisie vide. Le panneau se lisait comme
   une pile de champs dont trois se trouvaient etre cliquables, et la
   corbeille finissait seule sur sa propre ligne. Les deux roues de la charte
   calaient chacune sous son mot, avec sept cents pixels de vide a cote.

   Le plancher se mesure, il ne se choisit pas : un champ du panneau ne
   descend pas sous 260 px (`.reglages-row input[type="text"]`), et l'ecart
   d'une rangee vaut .6rem -- deux controles cote a cote demandent donc
   530 px, gouttieres comprises. En dessous, la colonne du telephone reste la
   bonne reponse et rien ne change ; au-dessus, la rangee redevient une
   rangee qui se replie, comme sur ordinateur.

   Ce qui ne bouge pas : la cible tactile de 44 px et le texte a 16 px. Elles
   ne parlent pas de la largeur de l'ecran mais du doigt qui le touche.
   ========================================================================== */

@media (min-width: 600px) and (max-width: 820px) {
  .reglages-row {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
  }

  .reglages-row > input,
  .reglages-row > select,
  .reglages-row > .btn-primary { width: auto; }

  /* La rangee est redevenue une rangee : le champ de date reprend la largeur
     qui le tient a cote du selecteur de cloture. */
  .reglages-row .aglae-input-exercice[type="date"] { width: 11rem; }

  /* Le mot de la charte revient a cote de sa roue : c'est la pile qui l'avait
     fait passer au-dessus, et il n'y a plus de pile. */
  .charte-couleur {
    flex-direction: row;
    align-items: center;
  }

  /* Les rangees a bouton-icone gardent le pli, plus l'exception : leurs
     boutons n'ont plus a faire la pleine largeur, donc les icones n'ont plus
     a fuir a droite pour ne pas se retrouver seules au bas d'une pile. Elles
     suivent le dernier bouton, comme sur ordinateur. */
  .reglages-row:has(> .btn-link.btn-icon-only, > .reglages-piece > .btn-link.btn-icon-only) { justify-content: flex-start; }

  /* Les deux selecteurs qui portent `:not(.compte-photo-ligne)` le reprennent
     ici tel quel. Ce n'est pas une precaution, c'est une egalite de poids : la
     regle du telephone qu'ils annulent l'ecrit, donc elle pesait une classe de
     plus, donc elle gagnait -- et ces deux-la seulement restaient a 100 %.

     Le bloc ci-dessus disait ce qu'il fallait et ne l'obtenait pas. Entre 600
     et 820 px, le panneau Mandat empilait des boutons a filet larges de sept
     cents pixels pour une etiquette de cent -- « Deduire du logo », « Voir le
     modele », « Telecharger » --, chacun sur sa ligne, et la corbeille tombait
     seule contre le bord gauche en dessous. Un bouton a filet aussi large se
     lit comme une barre, pas comme un bouton ; une corbeille seule au bord
     gauche, comme une ligne qui n'a pas su se remplir. Les deux reproches que
     ce fichier fait ailleurs, faits ici en meme temps.

     La rangee de la photo reste hors des deux listes, dans les deux blocs :
     elle a sa mise en page plus bas, et `.compte-photo-ligne > .btn-primary`
     s'en charge. Sans le `:not`, ce bloc-ci la lui reprendrait. */
  .reglages-row:not(.compte-photo-ligne):has(> .btn-link.btn-icon-only, > .reglages-piece > .btn-link.btn-icon-only) > .btn-link:not(.btn-icon-only),
  .reglages-row:has(> .btn-link.btn-icon-only, > .reglages-piece > .btn-link.btn-icon-only) > .charte-couleur,
  .reglages-row:has(> .btn-link.btn-icon-only, > .reglages-piece > .btn-link.btn-icon-only) > .charte-apercu,
  .reglages-row:has(> .btn-link.btn-icon-only, > .reglages-piece > .btn-link.btn-icon-only) > .reglages-piece > .btn-link:not(.btn-icon-only),
  .reglages-row:not(.compte-photo-ligne):has(> .btn-link.btn-icon-only, > .reglages-piece > .btn-link.btn-icon-only) > .reglages-hint-inline { flex: 0 1 auto; }

  .reglages-row:has(> .btn-link.btn-icon-only, > .reglages-piece > .btn-link.btn-icon-only) > .logo-client-cadre { margin-right: 0; }

  /* « Remplacer la photo » suit la meme regle que les autres, ce qu'un
     `flex-basis: 100%` ecrit pour le telephone l'empechait de faire : la
     rangee de la photo etait la seule du panneau a barrer encore l'ecran a
     700 px, et les deux gestes qui la suivent tombaient sous elle faute de
     place. Les trois tiennent sur une ligne des 600 px. */
  .compte-photo-ligne > .btn-primary { flex: 0 1 auto; }

  /* Et « Cadrer » avec lui. Cette ligne manquait : sur le telephone il prend
     la place que la corbeille laisse au bas de la pile -- c'est la mise en
     page voulue --, mais des que les trois boutons reviennent sur une seule
     ligne, ce meme `flex: 1` lui donnait tout ce que ses deux voisins ne
     prenaient pas : 467 px sur une tablette de 700, 567 sur une de 800, pour
     une etiquette de 61.

     Un bouton a filet de cette largeur, pose a cote d'un bouton plein et d'une
     corbeille encadree, ne se lit plus comme un bouton mais comme un champ de
     saisie vide -- meme filet gris clair, meme arrondi, meme fond blanc, et un
     mot cale a gauche comme une valeur. Ce sont les trois memes gestes qu'a
     1512 px, ou il mesure 61 px : il n'y a pas de raison qu'ils changent de
     nature entre les deux. */
  .compte-photo-ligne > .btn-link { flex: 0 1 auto; }

  /* Le segmente du titulaire reprend sa largeur naturelle. Les tuiles de
     cabinet s'etalaient sur toute la largeur du groupe -- une regle ecrite pour
     un telephone debout, ou trois tuiles de 120 px valent mieux que trois
     timbres. A 820 px elles se partageaient a trois les sept cents pixels que
     les ronds ne prenaient plus : « af » se lisait au centre d'un bouton de
     193 px, et la rangee TITULAIRE etait la seule du panneau a barrer l'ecran.
     La rangee est redevenue une rangee ; le segmente redevient un segmente. La
     cible tactile ne bouge pas : elle est tenue par la hauteur, pas par la
     largeur. */
  .cabinet-toggle {
    width: auto;
    max-width: 100%;
  }

  .cabinet-toggle .cabinet-seg { flex: none; }

  /* L'equipe reprend la grille de l'ordinateur, celle ou toutes les lignes
     partagent leurs colonnes. La pile en deux etages -- le collaborateur en
     haut, ce qu'il suit en dessous sur toute la largeur -- est la reponse juste
     a 390 px, ou rien d'autre ne tient ; a 820 px elle donnait une liste
     deroulante de 450 px posee sous un nom de 263, et une section de plus a
     lire en zigzag.

     C'est bien la grille commune qu'on reprend, et non une grille par ligne :
     une grille par ligne laisse chaque nom fixer sa propre colonne, et les
     listes deroulantes ondulent d'une ligne a l'autre avec la longueur des
     noms -- l'argument est ecrit tel quel au-dessus de la regle d'ordinateur.
     Seule la derniere colonne peut se serrer (`minmax(0, max-content)`) : un
     nom long ne doit pas pousser la croix hors de l'ecran. */
  .reglages-equipe {
    display: grid;
    grid-template-columns: auto auto max-content minmax(0, max-content) 1fr;
    align-items: center;
    gap: var(--espace-xs) var(--espace-sm);
  }

  /* La ligne redevient une boite -- `subgrid`, comme sur ordinateur -- et non le
     `display: contents` qu'elle etait ici : elle garde ainsi le filet qui separe
     les personnes, deja pose par le bloc telephone juste au-dessus, tout en
     rendant ses colonnes a la liste pour que les rangees s'alignent.

     Les zones nommees de ce meme bloc telephone doivent etre effacees a la main
     (`grid-template-areas: none`) : sans cela la ligne garde une grille de deux
     rangees nommees par-dessus les colonnes heritees. */
  .reglages-equipe-row {
    display: grid;
    grid-template-columns: subgrid;
    grid-template-areas: none;
    grid-column: 1 / -1;
    align-items: center;
    padding-bottom: var(--espace-xs);
  }

  /* Les cinq zones nommees appartenaient a la grille de la ligne, qui ne les
     porte plus : sans ce retour a `auto`, les cinq elements visent des lignes de
     grille introuvables et se superposent tous dans la meme case. Le placement
     redevient celui de l'ordre du gabarit, comme sur ordinateur. */
  .reglages-equipe-row > .dossier-avatar,
  .reglages-equipe-nom,
  .reglages-equipe-signataire,
  .reglages-equipe-suivi {
    grid-area: auto;
    justify-self: auto;
  }

  /* La croix vise le bord droit de la liste, comme la corbeille des trois autres
     listes du panneau -- c'est deja ce que fait le bloc telephone, et la colonne
     `1fr` ajoutee ci-dessus lui en donne la place. */
  .reglages-equipe-retirer {
    grid-area: auto;
    justify-self: end;
  }

  .reglages-equipe-suivi { width: auto; }

  /* Le bandeau d'alerte redevient une rangee. Sa forme empilee est ecrite pour
     un telephone debout, ou un champ de saisie et son bouton ne tiennent pas
     cote a cote ; a 820 px ils y tiennent trois fois. « Publier l'alerte »
     s'etirait donc a 761 px pour une etiquette de 115, sous un champ de la
     meme largeur -- un bouton plein aussi long ne se lit plus comme un bouton
     mais comme une seconde barre, et la paire ne se lisait plus comme un
     formulaire d'une ligne.

     Le champ garde la cible tactile et son corps de 16 px : cette largeur est
     aussi celle d'une tablette, et sous 16 px iOS zoome a la prise de focus.
     C'est la hauteur qui tient la cible, pas la largeur -- le segmente du
     titulaire, plus haut dans ce bloc, le dit dans les memes termes. */
  .alertes-bandeau-admin-form {
    flex-direction: row;
    align-items: center;
  }

  /* Les deux listes de la page du cabinet reprennent leurs rangees. Elles se
     replient en petites cartes verticales pour une raison que leurs
     commentaires ecrivent noir sur blanc : « la ligne unique du grand ecran
     demanderait ici six cibles cote a cote dans la largeur d'un pouce », et
     « les cinq colonnes valent 470 px a elles seules -- 80 de plus que
     l'ecran ». Les deux enonces sont vrais d'un telephone de 390 px et faux
     d'une tablette de 700 : 470 px y laissent encore deux cents pixels.

     Ce que la carte donnait a la place : « Ajouter » etale sur 618 px pour une
     etiquette de 68, « Generer un lien d'invitation » sur 758 pour 215 -- une
     barre cyan pleine largeur sous un champ de la meme largeur --, et la
     couleur du cabinet reportee sur une seconde rangee alors que la premiere
     s'arretait au tiers.

     Rien n'est reecrit ici : chaque ligne rend une propriete que le bloc
     telephone avait prise, et la feuille du grand ecran reprend la main. Les
     cibles tactiles, elles, ne sont pas rendues -- une tablette se touche
     aussi, et c'est la hauteur qui les tient. */
  .cabinet-gestion-form {
    display: flex;
    flex-wrap: wrap;
    gap: var(--espace-2xs) var(--espace-sm);
  }
  .cabinet-gestion-form > .btn-primary { width: auto; }

  .equipe-gestion-form { grid-template-columns: auto minmax(0, 1fr) 8.5rem var(--h-sm); }
  .equipe-gestion-apercu {
    grid-column: auto;
    grid-row: auto;
    align-self: auto;
  }
  .equipe-gestion-identite,
  .equipe-gestion-supprimer {
    grid-column: auto;
    grid-row: auto;
  }
  .equipe-gestion-role,
  .equipe-gestion-role-lecture {
    grid-column: auto;
    grid-row: auto;
    width: auto;
  }
  /* Le role ecrit en toutes lettres retrouve le rembourrage qui le cale sur la
     verticale des listes deroulantes voisines : sans lui, la seule ligne sans
     menu de la pile commencait deux caracteres a gauche des autres. */
  .equipe-gestion-role-lecture {
    padding-left: var(--espace-sm);
    padding-right: var(--espace-sm);
  }

  .equipe-gestion-ajout { grid-template-columns: auto 1fr auto; }
  .equipe-gestion-ajout > .equipe-gestion-apercu { grid-row: auto; }
  .equipe-gestion-email-input {
    grid-column: auto;
    grid-row: auto;
  }
  .equipe-gestion-ajout > .btn-primary {
    grid-column: auto;
    grid-row: auto;
    width: auto;
  }
}

/* ==========================================================================
   Hors mobile : filet de securite. Si la fenetre est elargie apres que
   mobile.js a injecte sa navigation (rotation, redimensionnement), les
   elements mobiles disparaissent et l'interface bureau reprend la main
   telle quelle.
   ========================================================================== */

@media (min-width: 821px) {
  .m-tabbar,
  .m-fab,
  .m-back,
  .m-sheet,
  .m-sheet-backdrop,
  .m-chiprow,
  .m-yearbar { display: none !important; }
}

/* Le vote des noms : son pied fixe se pose au-dessus de la barre d'onglets
   au lieu de passer dessous (voir « LE VOTE DES NOMS » dans style.css). */
@media (max-width: 820px) {
  body:not(.m-no-tabbar) .tn .pied { bottom: var(--m-tabbar-hors-tout); }
}

/* LE TELEPHONE EN PORTRAIT, SANS SURVOL (23 septembre 2026 : « sur la version
   telephone du site certains reglages comme les survols ou le deploiement de
   la liste des reglages ne fonctionnent pas bien »). Voir mobile.js,
   `construireOngletReglages` et `ouvrirAuDoigt`. */
@media (max-width: 820px) {
  /* L'onglet Reglages est un bouton, pas un lien : il ouvre une feuille. */
  button.m-tab {
    border: 0;
    background: none;
    padding: 0;
    font-family: inherit;
    cursor: pointer;
  }

  /* La feuille des reglages : les entrees de la fenetre de l'en-tete, a la
     mesure du doigt. */
  .m-reglages-corps { padding-top: var(--espace-xs); }
  .m-reglages-corps .app-reglages-identite {
    padding: var(--espace-xs) var(--espace-2xs) var(--espace-sm);
  }
  .m-reglages-corps .app-reglages-nom { font-size: var(--fs-md); }
  .m-reglages-corps .app-reglages-email { font-size: var(--fs-sm); }
  .m-reglages-corps .app-reglages-lien {
    min-height: var(--m-tap);
    font-size: var(--fs-md);
    padding: var(--espace-xs) var(--espace-sm);
  }
  .m-reglages-corps .app-reglages-lien .icon {
    width: var(--icon-lg);
    height: var(--icon-lg);
  }
  .m-reglages-corps .app-reglages-lien:hover { background: none; color: var(--gray-5); }
  .m-reglages-corps .app-reglages-lien.actif,
  .m-reglages-corps .app-reglages-lien:active { background: var(--gray-1); color: var(--black); }
  .m-reglages-corps .app-reglages-deconnexion .app-reglages-lien { color: var(--red-dark); }

  /* Les panneaux de survol ne s'ouvrent plus sur place : un appui les montre
     en feuille. Sans cette regle, le survol qu'un navigateur tactile simule
     au premier appui les poserait aussi en bulle, derriere la feuille, et
     l'aide d'un libelle resterait ouverte tant que son bouton garde le focus. */
  body.m-mobile :is(.dossier-fec-count, .dossier-exercice-fec, .dossier-echeance-ancre,
                    .explication-appel, .intervenant-detail-lien)
    :is(.dossier-fec-resume, .dossier-echeance-resume, .explication, .intervenant-mandats) {
    display: none;
  }
  body.m-mobile .avatar-noms { display: none; }

  /* La copie dans la feuille perd tout ce qui en faisait une bulle : sa
     position, sa largeur fixe, son cadre et son ombre. La feuille est deja
     la surface. */
  body.m-mobile .m-survol-corps > :is(.dossier-fec-resume, .dossier-echeance-resume,
                                      .explication, .intervenant-mandats) {
    position: static;
    inset: auto;
    transform: none;
    display: flex;
    flex-direction: column;
    gap: var(--espace-sm);
    width: auto;
    min-width: 0;
    max-width: none;
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    white-space: normal;
    font-size: var(--fs-md);
    line-height: 1.5;
    color: var(--gray-6);
    text-align: left;
  }
  body.m-mobile .m-survol-corps > *::before,
  body.m-mobile .m-survol-corps > *::after { display: none; }
  body.m-mobile .m-survol-corps > * { overflow-x: auto; }
  body.m-mobile .m-survol-corps table { width: 100%; font-size: var(--fs-sm); }
  /* Le resume des FEC tenait sur une ligne par fichier, a la largeur d'une
     bulle d'ordinateur : au telephone, le resultat sortait a droite. Le nom
     du fichier se replie, les nombres et les dates non. */
  body.m-mobile .m-survol-corps .dossier-fec-resume-table td { white-space: normal; overflow-wrap: anywhere; }
  body.m-mobile .m-survol-corps .dossier-fec-resume-table td.num,
  body.m-mobile .m-survol-corps .dossier-fec-resume-table td:first-child { white-space: nowrap; overflow-wrap: normal; }

  /* La pile des notifications se tient au-dessus de tout (z-index 1001), et
     donc au-dessus d'une feuille ouverte, dont elle cachait le bas. Elle se
     retire le temps de la feuille, comme la bulle des tickets plus haut, et
     revient a sa fermeture. */
  body.m-scroll-locked .notif-pile { display: none; }
  body.m-mobile .m-survol-corps a { min-height: var(--m-tap); }

  .m-survol-noms {
    list-style: none;
    margin: 0;
    padding: 0;
  }
  .m-survol-noms li {
    display: flex;
    align-items: center;
    min-height: var(--m-tap);
    font-size: var(--fs-md);
    border-bottom: 1px solid var(--gray-2);
  }
  .m-survol-noms li:last-child { border-bottom: 0; }


  /* LES AUTRES PAGES EN PORTRAIT (23 septembre 2026 : « fais pareil pour les
     autres pages en vertical »). Meme principe que les resumes ci-dessus : ce
     que la souris revelait en passant, le doigt doit le trouver sans viser.

     Messages : la barre d'un message suit un appui sur sa bulle (mobile.js,
     `basculerActionsDuMessage`) et non plus le survol, que Safari ne simule
     pas sur une bulle. Plus haute qu'au bureau, pour le pouce. */
  body.m-mobile .chat-message .chat-actions { display: none; }
  body.m-mobile .chat-message.m-actions-ouvertes .chat-actions {
    display: flex;
    top: calc(-1 * var(--espace-xl));
  }
  body.m-mobile .chat-actions .chat-reagir,
  body.m-mobile .chat-actions .chat-action {
    min-width: 36px;
    min-height: 36px;
    font-size: var(--fs-md);
  }
  body.m-mobile .chat-message.m-actions-ouvertes .chat-bulle {
    box-shadow: 0 0 0 2px var(--cyan-light);
  }
  /* L'etiquette du raccourci clavier de la dictee (« ⌥ M ») : aucun clavier
     n'est la pour s'en servir. */
  .dictee-raccourci { display: none; }
  /* La bulle « Suggerer une modification » se posait sur le bouton d'envoi
     d'une conversation ouverte : elle remonte au-dessus de la zone d'ecriture. */
  body:has(#chat-ecrire:not([hidden])) .ticket-widget {
    bottom: calc(var(--m-tabbar-hors-tout) + 84px);
  }

  /* Tickets : la croix qui dit « un appui enleve l'urgence » ne paraissait
     qu'au survol de la plaque. Elle reste a demi-teinte, comme sur la pastille. */
  body.m-mobile button.ticket-card-titre .ticket-card-urgent-croix { opacity: .55; }

  /* Synthese de groupe : le crayon qui rend la saisie et le bouton de courbe
     etaient invisibles hors survol. Discrets, mais la. */
  body.m-mobile .synthese-groupe-crayon,
  body.m-mobile .synthese-groupe-courbe-btn { opacity: .55; }

  /* Onglet Assistant d'un mandat : la carte d'un constat est une grille dont
     la colonne du texte valait `1fr`, c'est-a-dire au moins la largeur de son
     contenu. Une phrase de rendez-vous tenue sur une ligne l'elargissait donc
     jusqu'a 800 px, et la moitie de la carte (le formulaire « Au », « Mode »)
     partait hors de l'ecran. La colonne se borne a l'ecran, et la phrase
     passe a la ligne au lieu d'etre coupee de points de suspension. */
  .assistant-constat { grid-template-columns: auto minmax(0, 1fr); }
  .assistant-constat > * { min-width: 0; }
  .arrete-rdv-phrase { white-space: normal; }
  /* Dans la meme carte, deux rangees gardaient leur largeur de bureau : le
     champ « Mode » du rendez-vous, une liste deroulante large de ses options,
     et la ligne d'un rapport, dont le nom refusait de se couper et poussait
     « Emettre le rapport » hors de l'ecran. Le champ se borne a la carte, la
     ligne du rapport passe a la ligne et ses gestes avec elle. */
  .arrete-rdv-ajout .arrete-champ { max-width: 100%; min-width: 0; }
  .arrete-rdv-ajout .arrete-champ > select { flex: 1 1 auto; min-width: 0; }
  .arrete-rapports li { flex-wrap: wrap; }
  .arrete-rapport-nom { flex: 0 1 auto; min-width: 0; }
}
