/* ============================================================
   OpenContact v6 — feuille de production « Utilitaire 98 »
   Mobile d'abord (390 px), le desktop est l'élargissement.
   Quatre zones : Aujourd'hui · Mes pistes · Échanger · Moi.
   Les tokens (styles/tokens/) portent l'identité ; ici, la mise
   en écran. Le moteur n'est jamais concerné par ce fichier.
   ============================================================ */
/* les tokens sont chargés par index.html (<link>, en parallèle) —
   ne pas remettre d'@import ici : il sérialise les téléchargements */

:root{
  --nav-h:53px;
  --ctl:var(--control-h-touch);      /* mobile d'abord : cibles 44px */
  /* `max()` et pas 16px sec : le plancher reste 16 px — en dessous, iOS
     zoome tout seul à la mise au point d'un champ — mais la valeur suit
     la police choisie par l'utilisateur quand il l'a agrandie. */
  --input-fs:max(16px, 1rem);
}
/* La largeur dit le DESSIN, le pointeur dit la MAIN — ce ne sont pas la
   même question, et 901 px seul répondait aux deux.
   Une tablette tactile en paysage fait 1024 à 1366 px : elle passait donc
   en ergonomie souris. Mesuré, 10 cibles sur 11 tombaient sous 44 px, la
   plus petite à 30 px, pour un doigt — sous le plancher d'Apple (44 pt)
   comme sous WCAG 2.5.5 (44×44). Et `--input-fs` redescendait à 14 px,
   ce qui rouvre le zoom automatique d'iOS à la mise au point d'un champ,
   exactement ce que le 16 px ci-dessus existe pour éviter.
   `pointer:fine` est la bonne question : une souris, un trackpad ou un
   stylet la valident (iPad + trackpad compris) ; un doigt non. Le §5 de
   CLAUDE.md ne bouge pas — le tableau, la nav haute et les colonnes
   restent commandés par la largeur, parce que c'est l'USAGE qui change
   là. Seule l'ergonomie suit désormais ce qui touche l'écran. */
@media (min-width:901px) and (pointer:fine){
  :root{ --ctl:var(--control-h); --input-fs:var(--text-14); }
}

/* ============ SOCLE ============ */
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
[hidden]{display:none!important}
button,a,label,select,summary,input{touch-action:manipulation}
body{
  background:var(--surface-desktop);color:var(--text-body);
  font-family:var(--font-ui);font-size:var(--text-14);line-height:var(--leading-ui);
  overflow:hidden;
}
button{font-family:inherit;cursor:pointer}
input,select,textarea{font-family:inherit;color:var(--text-body)}
/* Lu, jamais vu. Surtout pas `display:none` ni `visibility:hidden` :
   les deux retirent l'élément de l'arbre d'accessibilité, et la région
   vivante devient muette — c'est le piège classique de ce motif. */
.visuellement-cache{
  position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;
}
/* Caché par `translateY(-100%)` et non par un `top` négatif : un `top`
   en px ne suit pas la police. À texte agrandi, le lien dépassait ses
   48 px et une bande d'accent restait collée en haut de CHAQUE écran —
   relevé à l'audit d'octobre 2026, à 200 % en 320 px. Le pourcentage
   est celui de sa propre hauteur, quelle qu'elle soit. */
.skip{
  position:fixed;top:0;left:10px;z-index:5000;transform:translateY(-100%);
  display:inline-flex;align-items:center;min-height:var(--ctl);
  background:var(--accent);color:var(--on-accent);
  font-weight:var(--weight-bold);font-size:var(--text-13);padding:10px 14px;
  border:1px solid var(--border-strong);border-top:0;text-decoration:none;transition:transform 120ms;
}
.skip:focus{transform:none}
.app{display:flex;flex-direction:column;height:100dvh}
.main{flex:1;min-height:0;position:relative}
.view{height:100%;overflow-y:auto;padding:16px 14px 40px;
  padding-left:calc(14px + env(safe-area-inset-left));
  padding-right:calc(14px + env(safe-area-inset-right));}
.page-inner{max-width:640px;margin:0 auto}

/* ---- icône pixel (masque CSS teinté par currentColor) ---- */
.ic{
  display:inline-block;flex:none;width:16px;height:16px;vertical-align:-3px;
  background:currentColor;
  -webkit-mask-position:center;mask-position:center;
  -webkit-mask-size:contain;mask-size:contain;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  image-rendering:pixelated;
}
.ic-14{width:14px;height:14px;vertical-align:-2px}
.ic-20{width:20px;height:20px;vertical-align:-4px}
.ic-24{width:24px;height:24px}
.ic-12{width:12px;height:12px;vertical-align:-1px}

/* ============ EN-TÊTE ============ */
.hdr{
  display:flex;align-items:center;gap:14px;height:46px;flex:none;z-index:1300;
  padding:0 calc(8px + env(safe-area-inset-right)) 0 calc(14px + env(safe-area-inset-left));
  background:var(--surface-window);border-bottom:1px solid var(--border-strong);
}
/* le logo est un LIEN (retour à l'accueil) : il obéit à la règle des
   cibles, comme le reste — 22 px de haut, c'était le texte, pas la cible */
.brand{display:flex;align-items:center;gap:9px;text-decoration:none;color:var(--text-body);flex:none;
  min-height:var(--ctl)}
.brand img,.brand svg{image-rendering:pixelated;display:block}
.brand b{
  font-family:var(--font-pixel);font-weight:400;font-size:var(--pixel-16);
  letter-spacing:.02em;text-transform:uppercase;
}
.topnav{display:none;gap:2px;align-items:center}
/* la nav haute suit `--ctl` comme tout le reste : 32 px sous une souris,
   44 px sous un doigt. Sans hauteur minimale elle restait à 30 px — la
   cible la plus tapée de l'app, et la plus petite, sur une tablette. */
.topnav a{
  display:inline-flex;align-items:center;min-height:var(--ctl);
  text-decoration:none;color:var(--text-muted);font-size:var(--text-13);font-weight:var(--weight-semibold);
  border:1px solid transparent;border-radius:var(--radius-1);padding:5px 10px;
}
@media (hover:hover){
.topnav a:hover{color:var(--text-body);background:var(--surface-chrome)}
}
.topnav a.on{color:var(--select-text);background:var(--select-bg);border-color:var(--border-strong)}
/* en rangée, le mot suffit ; l'icône n'est qu'à la barre latérale */
.topnav a>.ic{display:none}
.hdr-actions{display:flex;gap:8px;margin-left:auto;align-items:center}
.hdr .btn-cta{display:none}
@media (min-width:901px){
  .topnav{display:flex}
  .hdr .btn-cta{display:inline-flex}
}
/* AU POUCE, PAS D'EN-TÊTE (décision du mainteneur, 12 octobre 2026). Il
   ne portait que le logo et la bascule du thème : 46 px pris en haut de
   CHAQUE écran pour rien qu'on y fasse, pendant que la barre du bas
   porte déjà la navigation. Le titre de l'écran (« Échanger », « Moi »)
   dit où l'on est ; le thème passe dans Réglages. Au poste l'en-tête
   reste : il y porte la navigation et « Ajouter une piste ». La marge
   du haut suit l'encoche quand l'app est installée. */
@media (max-width:900px){
  .hdr{display:none}
  .app{padding-top:env(safe-area-inset-top)}
}
/* LA BARRE LATÉRALE DU POSTE (R1, décision 0003 ; la
   classe `nav-laterale`, posée par index.html depuis la 7.0.0). L'en-tête
   devient une colonne à gauche : le logo, « Ajouter » (la seule action
   remplie, en tête comme le « Nouveau » de Gmail ou de Notion), puis
   les quatre destinations, l'icône devant le mot. Une rangée de quatre
   onglets en haut d'un écran large laissait 900 px vides à côté d'eux ;
   une colonne garde chaque destination au même endroit, le mot entier,
   et rend la hauteur à ce qu'on lit. La barre d'état reste en bas, sur
   toute la largeur : elle n'appartient à aucun écran. */
@media (min-width:901px){
  .app.nav-laterale{display:grid;grid-template-columns:14rem minmax(0,1fr);
    grid-template-rows:auto minmax(0,1fr) auto}
  .nav-laterale>.hdr{grid-column:1;grid-row:1 / 3;flex-direction:column;align-items:stretch;gap:12px;
    height:auto;min-height:0;overflow-y:auto;
    padding:12px 10px 12px calc(10px + env(safe-area-inset-left));
    border-bottom:0;border-right:1px solid var(--border-strong)}
  .nav-laterale>.save-warn{grid-column:2;grid-row:1}
  .nav-laterale>.main{grid-column:2;grid-row:2}
  .nav-laterale>.statusbar{grid-column:1 / -1;grid-row:3}
  .nav-laterale .brand{padding:0 4px}
  .nav-laterale .hdr-actions{order:1;margin-left:0}
  .nav-laterale .hdr-actions .btn-cta{flex:1;justify-content:center}
  .nav-laterale .topnav{order:2;flex-direction:column;align-items:stretch;gap:2px}
  .nav-laterale .topnav a{gap:10px;font-size:var(--text-14);padding:5px 10px}
  .nav-laterale .topnav a>.ic{display:inline-block}
}

/* ============ BARRE D'ÉTAT (desktop) ============ */
.statusbar{
  flex:none;display:none;gap:18px;align-items:center;padding:5px 14px;
  background:var(--surface-chrome);border-top:1px solid var(--border-strong);
  font-family:var(--font-mono);font-size:var(--text-12);color:var(--text-muted);z-index:1300;
}
.statusbar .grow{flex:1}
.statusbar span{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
@media (min-width:901px){ .statusbar{display:flex} }

/* ============ NAV BASSE (mobile) ============ */
.bottomnav{
  display:flex;position:fixed;left:0;right:0;bottom:0;z-index:1500;height:calc(var(--nav-h) + env(safe-area-inset-bottom));
  background:var(--surface-chrome);border-top:1px solid var(--border-strong);
  padding:4px calc(4px + env(safe-area-inset-right)) calc(4px + env(safe-area-inset-bottom)) calc(4px + env(safe-area-inset-left));
  align-items:stretch;gap:2px;
}
.bottomnav a{
  flex:1;min-width:0;min-height:var(--control-h-touch);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
  text-decoration:none;color:var(--text-muted);
  font-size:var(--text-11);font-weight:var(--weight-semibold);
  border:1px solid transparent;border-radius:var(--radius-1);
  white-space:nowrap;overflow:hidden;
}
.bottomnav a.on{background:var(--select-bg);color:var(--select-text);border-color:var(--border-strong)}
/* L'ONGLET QUI ATTEND (le mainteneur, 28 octobre 2026 : « pas de
   notification, une couleur qui change dans l'onglet »). Pas de chiffre
   (le calme, R10) : le mot et l'icône passent à l'accent, et le point
   carré des conversations non lues de la boîte (`.bx-pt`) suit le mot.
   La couleur seule ne parle pas à tout le monde (WCAG 1.4.1) : la forme
   du point dit la même chose, et le nom de l'onglet aussi. */
.bottomnav a.attend,.topnav a.attend{color:var(--accent)}
.bottomnav a.attend .bn-l::after,.topnav a.attend::after{
  content:'';display:inline-block;width:6px;height:6px;margin-left:4px;vertical-align:.1em;background:var(--accent)}
/* Un libellé d'onglet trop long était coupé AUX DEUX BOUTS : centré dans
   un `overflow:hidden`, « Aujourd'hui » rendait « ujourd'hu ». On perd
   alors le début, qui est justement ce qu'on lit. Le libellé prend sa
   propre boîte pour pouvoir s'élider — la fin s'en va, le début reste.
   Ça se voit dès qu'un utilisateur agrandit sa police. */
.bn-l{display:block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Le ⊕ était la cible la PLUS ÉTROITE de la barre (52 px) pour le geste
   le plus précieux de l'app — noter une entreprise qu'on vient de
   croiser. Il passe à 64 px : c'est le maximum mesuré avant que
   « Aujourd'hui » ne se coupe sur un écran de 320 px (à 68 px il se
   coupe). Les onglets descendent de 62 à 59 px, aucun libellé n'en
   souffre, et le centre de la barre devient enfin son point d'appui. */
.bn-add{
  flex:none;width:64px;margin:0 2px;align-self:stretch;
  display:inline-flex;align-items:center;justify-content:center;
  background:var(--accent);color:var(--on-accent);
  /* le relief du primaire (§6) : la même touche que « Écrire » */
  border:1px solid var(--border-strong);border-radius:var(--radius-1);
  box-shadow:var(--bevel-up), var(--shadow-raised);
}
.bn-add:active{background:var(--accent-hover);box-shadow:var(--bevel-down);transform:translate(1px,1px)}
.app{padding-bottom:calc(var(--nav-h) + env(safe-area-inset-bottom))}
/* FENÊTRE MOYENNE (600-900 px : une tablette en portrait, un téléphone
   pliant ouvert, une fenêtre de poste rétrécie). Material 3 la nomme
   « medium » et n'y étire plus la barre du téléphone ; l'iPad resserre
   la sienne au centre. Étirée sur 768 px, chaque onglet faisait 150 px,
   l'onglet allumé devenait un bloc navy de 170 px, et le « + » flottait
   seul au milieu d'une bande vide. Les onglets gardent ici une largeur
   de TÉLÉPHONE, en `rem` pour suivre la police, et la barre se centre :
   la même rangée sous le même pouce, quelle que soit la largeur. */
@media (min-width:600px) and (max-width:900px){
  .bottomnav{justify-content:center}
  .bottomnav a{flex:0 1 7.5rem}
}
@media (min-width:901px){
  .bottomnav{display:none}
  .app{padding-bottom:0}
}

/* ============ BOUTONS ============ */
/* LE « 98 » D'ORIGINE (décision du mainteneur, 28 octobre 2026). Trois
   styles se sont succédé : la boîte 98 jusqu'au 13 octobre, le bouton
   texte sans boîte du 13 au 19, l'aplat teinté ensuite. Mis côte à côte
   sur la même fiche, le mainteneur a choisi le premier : « remets les
   anciens styles ». C'est l'identité de l'app (§4 : bevels francs,
   ombres dures), et c'est celui où l'on voit tout de suite ce qui se
   touche — un mot teal sans boîte se confondait avec un lien, l'aplat
   se lisait comme un fond. Ce qui a été appris entre-temps reste : la
   disposition (un geste par ligne, la case « Fait », « Clôturer » à
   « Où j'en suis »), la cible (`--ctl`, 44 px au doigt, 32 à la souris)
   et le libellé à 14 px au pouce.
   Trois formes :
   ① le PRIMAIRE — rempli d'accent, en relief, un par vue ;
   ② le SECONDAIRE — la boîte claire 98 : trait, biseau, ombre dure ;
     rouge pour ce qui détruit (`.btn-danger`) ;
   ③ le NEUTRE (`.btn-ghost`) — un mot sourd sans boîte, pour ce qu'on
     fait peu ou qui ne fait pas avancer (« Retour », « Passer »,
     « Clôturer ») ; il s'enfonce à l'appui comme les autres.
   Les aplats teintés restent au kit pour un ÉTAT allumé (`.sort-on`). */
:root{
  --tonal:color-mix(in srgb, var(--accent) 13%, var(--surface-window));
  --tonal-fort:color-mix(in srgb, var(--accent) 24%, var(--surface-window));
  /* un libellé de bouton se lit à la taille du doigt : 14 px au pouce
     (Material : 14 sp ; il était à 13, perdu dans 44 px de boîte),
     13 à la souris */
  --btn-fs:var(--text-14);
}
@media (min-width:901px) and (pointer:fine){ :root{ --btn-fs:var(--text-13) } }
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:7px;
  font-family:var(--font-ui);font-weight:var(--weight-semibold);font-size:var(--btn-fs);
  min-height:var(--ctl);padding:5px 14px;border-radius:var(--radius-1);
  background:var(--surface-chrome);color:var(--text-body);
  border:1px solid var(--border-strong);
  box-shadow:var(--bevel-up), var(--shadow-raised);
  /* `white-space:normal` : un libellé de bouton PLIE, il ne déborde pas.
     Mesuré à police doublée (WCAG 1.4.4) — « Ajouter ma première piste »
     sortait de son cadre de 30 px, texte par-dessus la bordure. À taille
     normale rien ne plie : aucun libellé de l'app n'atteint sa largeur
     disponible, le pliage ne s'enclenche que quand la place manque
     vraiment. `text-wrap:balance` répartit alors les deux lignes au lieu
     de laisser un mot seul en bas. */
  user-select:none;white-space:normal;text-wrap:balance;text-decoration:none;
}
.btn .ic{flex:none}
@media (hover:hover){
.btn:hover{background:var(--surface-window)}
}
/* l'appui se VOIT, toujours : le biseau s'enfonce, le bouton descend */
.btn:active{box-shadow:var(--bevel-down);transform:translate(2px,2px);background:var(--surface-chrome)}
.btn:disabled,.btn-off{pointer-events:none;color:var(--text-disabled);box-shadow:var(--bevel-up);opacity:.6}
/* ① le primaire : rempli d'accent, le même relief */
.btn-primary{background:var(--accent);border-color:var(--border-strong);color:var(--on-accent)}
.btn-primary:active{background:var(--accent-hover)}
.btn-primary:disabled,.btn-primary.btn-off{background:var(--accent);color:var(--on-accent)}
@media (hover:hover){
.btn-primary:hover{background:var(--accent-hover)}
}
/* ouverte, la ligne du mot de passe fait du cadenas une icône du champ */
@media (hover:hover){
.lockrow.on .lr-lock.btn-contour:hover{background:none}
}
/* Un bouton peut être un LIEN : « Ouvrir dans Mail », « Appeler »,
   « Itinéraire ». Le fond était protégé du survol (`.btn-primary:hover`),
   l'ENCRE ne l'était pas — et `a:hover` (0,1,1) bat `.btn-primary` (0,1,0).
   Le libellé prenait donc `--text-link-hover`, c'est-à-dire exactement
   `--accent-hover` : mesuré à 1.00:1 sur son propre fond, le mot
   disparaissait purement et simplement. Au doigt c'est pire qu'à la
   souris — iOS garde le survol collé après le tap, donc le bouton se
   vide AU MOMENT où on s'en sert, et le reste. */
@media (hover:hover){
a.btn:hover{color:var(--text-body)}
}
@media (hover:hover){
a.btn.btn-primary:hover{color:var(--on-accent)}
}
@media (hover:hover){
a.btn.btn-danger:hover{color:var(--red)}
}
/* ② le rouge de ce qui détruit, dans sa boîte */
.btn-danger{color:var(--red)}
@media (hover:hover){
.btn-danger:hover{background:var(--red-wash)}
}
/* ③ le neutre : un mot sourd, sans boîte */
.btn-ghost{background:transparent;border-color:transparent;box-shadow:none;color:var(--text-muted)}
@media (hover:hover){
.btn-ghost:hover{background:var(--surface-chrome);color:var(--text-body)}
}
.btn-ghost:active{transform:none;box-shadow:var(--bevel-down)}
/* un neutre qui détruit reste rouge : le mot dit le danger, même sans boîte */
.btn-ghost.btn-danger{color:var(--red)}
/* Le compact se distingue par son TEXTE et son padding, jamais par sa
   hauteur : il suit la main comme tout le reste (`--ctl`, 44 px au
   doigt, 32 à la souris). Il était écrit à 36 px — donc, au poste, plus
   haut que le bouton normal, le champ et la rangée voisins (32). */
.btn-sm{min-height:var(--ctl);padding:2px 11px;font-size:var(--text-13)}
@media (min-width:901px) and (pointer:fine){ .btn-sm{font-size:var(--text-12)} }
.btn-sm.btn-primary{padding:2px 13px}
.icon-btn{width:var(--ctl);height:var(--ctl);padding:0}
/* une recherche que l'app PROPOSE n'est pas un geste posé : « proposé en
   pointillé, posé en plein » (§6) — pas d'aplat, un trait pointillé */
.btn[data-prop]{background:transparent;border:1px dashed var(--border-strong);color:var(--text-body)}
.linklike{
  background:none;border:0;color:var(--text-link);font-weight:var(--weight-semibold);font-size:var(--text-12);
  text-decoration:underline;text-underline-offset:2px;
  display:inline-flex;align-items:center;gap:6px;min-height:var(--ctl);padding:0 8px;border-radius:var(--radius-1);
}
/* le pictogramme accompagne, il ne se souligne pas avec le mot */
.linklike .ic{text-decoration:none}
@media (hover:hover){
.linklike:hover{color:var(--text-link-hover)}
}

/* ============ ÉTIQUETTES ============ */
.tag-share,.tag-priv,.tag-beta{
  display:inline-flex;align-items:center;gap:4px;
  font-family:var(--font-pixel);font-size:var(--pixel-8);font-weight:400;
  text-transform:uppercase;letter-spacing:.06em;
  padding:3px 6px 2px;border:1px solid currentColor;border-radius:var(--radius-1);
  white-space:nowrap;vertical-align:middle;
}
.tag-share{color:var(--green);background:var(--green-wash)}
.tag-priv{color:var(--text-muted);background:var(--surface-chrome);background-image:var(--dither-img);background-size:var(--dither-size)}
.tag-beta{color:var(--amber);background:var(--amber-wash)}
.conf-ok{color:var(--green);font-size:var(--text-11);font-weight:var(--weight-bold);font-family:var(--font-mono)}
.conf-doubt{color:var(--amber);font-size:var(--text-11);font-weight:var(--weight-bold);font-family:var(--font-mono)}

/* ============ CHAMPS ============ */
.field{margin-bottom:12px}
.field label{
  /* `anywhere` : « L’entreprise de recrutement@exemple.fr » (Rattacher)
     n'a pas un espace où se plier, et sortait de la feuille à 200 % */
  display:block;overflow-wrap:anywhere;font-size:var(--text-11);font-weight:var(--weight-bold);
  letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--text-muted);margin-bottom:4px;
}
.lbl-soft{font-weight:var(--weight-medium);text-transform:none;letter-spacing:0}
.field input,.field textarea{
  width:100%;background:var(--surface-field);border:1px solid var(--border-field);
  box-shadow:var(--bevel-field);border-radius:var(--radius-1);
  padding:0 10px;font-size:var(--input-fs);height:var(--ctl);
}
/* `display:block` : un `<textarea>` est en ligne par défaut, et la ligne
   qui le porte réserve sous lui la place d'un jambage — 5 px de plus sous
   chaque champ de texte que sous un `<input>` voisin, mesuré dans le
   profil (e2e-finitions ⑥). */
.field textarea{display:block;min-height:68px;height:auto;padding:8px 10px;resize:vertical;line-height:var(--leading-body)}
.field input::placeholder,.field textarea::placeholder{color:var(--text-muted)}
.field select{
  width:100%;min-width:0;text-overflow:ellipsis;
  appearance:none;height:var(--ctl);padding:0 30px 0 10px;border-radius:var(--radius-1);
  font-family:var(--font-ui);font-size:var(--input-fs);font-weight:var(--weight-medium);color:var(--text-body);
  background:var(--surface-field) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='%235C6672' d='M7 8h2v2H7V8Zm4 4H9v-2h2v2Zm2 0v2h-2v-2h2Zm2-2h-2v2h2v-2Zm0 0V8h2v2h-2Z'/%3E%3C/svg%3E") no-repeat right 7px center / 16px 16px;
  border:1px solid var(--border-field);box-shadow:var(--bevel-field);cursor:pointer;
}
html[data-theme="dark"] .field select{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='%239AA4B0' d='M7 8h2v2H7V8Zm4 4H9v-2h2v2Zm2 0v2h-2v-2h2Zm2-2h-2v2h2v-2Zm0 0V8h2v2h-2Z'/%3E%3C/svg%3E");
}
/* Deux champs par rang au POSTE seulement. Au pouce, la moitié d'une
   feuille de 390 px fait 172 px : « Chargée de recrutement » y devient
   « Chargée de recruter » et une adresse email « lea.fontaine@capgei ».
   Un champ qui ne montre pas sa propre valeur ne sert à rien — et le
   seuil de 349 px qui traînait ici n'appartenait à aucun contexte : la
   règle de l'application est UN seuil, 901 px. */
/* `min-width:0` SUR LES ENFANTS, et ce n'est pas décoratif.
   Par défaut un enfant de grille refuse de descendre sous sa largeur
   MINIMALE de contenu — et pour un `<select>`, WebKit calcule cette
   largeur sur son option la plus longue. Résultat sur iPhone, jamais
   reproductible sous Chromium : « Sophie Fontaine — Directeur des
   systèmes d'information » élargit la colonne au-delà de la feuille,
   et toute la feuille « Écrire » se met à glisser de droite à gauche,
   libellés coupés au bord. `max-width:100%` n'y peut rien : c'est
   `min-width` qui l'emporte. */
/* `row-gap:0` : chaque enfant est un `.field`, qui porte déjà sa marge
   de 12 px. L'écart de rang s'y AJOUTAIT — 22 px entre « Formation » et
   « École », 12 partout ailleurs dans la même feuille (audit d'octobre
   2026). Un rythme vertical, pas deux. */
.grid2{display:grid;grid-template-columns:1fr;gap:0 10px}
.grid2>*{min-width:0}
@media (min-width:901px){ .grid2{grid-template-columns:1fr 1fr} }
/* l'exception : deux valeurs COURTES et connues d'avance (deux mots de
   la phrase de secours, deux dates) tiennent côte à côte à toutes les
   tailles. Calées sur leur BAS : un libellé qui porte une donnée à
   droite (« Fin · 2 ans ») est plus haut que son voisin, et les deux
   champs se décalaient de 4 px — c'est le champ qu'on compare, pas le
   libellé. */
.grid2.grid2-tight{grid-template-columns:1fr 1fr;align-items:end}
.hint{font-size:var(--text-12);color:var(--text-muted);margin-top:7px;line-height:1.5}
.hint.warn{color:var(--amber)}

/* ============ MON PROFIL ============
   Quatre groupes rangés dans l'ordre où le recruteur lit. Pas de cadre
   par groupe : quatre `fieldset` encadrés dans une feuille, c'est deux
   de plus que ce que §6 tolère sur une page entière. Un filet et un
   titre en casse de phrase suffisent — et le titre ne ressemble pas aux
   libellés de champ (capitales grises), sinon on ne sait plus lequel
   coiffe l'autre. Le `legend` flotte pour sortir du trait : posé
   dessus, il le couperait comme une encoche de group box 98. */
.pf-grp{border:0;margin:0;padding:0;min-width:0}
.pf-grp+.pf-grp{margin-top:4px;padding-top:12px;border-top:1px dashed var(--border-soft)}
.pf-grp>legend{
  float:left;width:100%;padding:0;margin:0 0 10px;
  font-size:var(--text-13);font-weight:var(--weight-bold);color:var(--text-body);
}
.pf-grp>legend+*{clear:both}
.pf-grp .datechips{margin-bottom:12px}
/* « Autour de » : la ville, puis ses trois rayons juste dessous — le
   rayon appartient à la ville, il ne fait pas un groupe à lui */
.pf-ou .pf-rayon{margin:8px 0 0}
.pf-duree{font-size:var(--text-12);color:var(--text-muted)}
/* « Mon parcours » : la liste des expériences, puis « Ajouter » dessous */
.pf-par{margin-bottom:8px}
.ex-quoi{margin:0 0 12px}
/* L'aperçu est une DONNÉE, pas un champ : ni bordure ni fond (§6,
   sobriété 3 — ce qui se tape a un cadre). Un filet à gauche, comme une
   citation dans un mail : c'est exactement ce qu'il montre. */
.pf-ap{margin-top:6px;padding-top:12px;border-top:1px dashed var(--border-soft)}
.pf-ap-l{
  display:block;font-size:var(--text-11);font-weight:var(--weight-bold);
  letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--text-muted);
}
.pf-ap-t{
  margin-top:8px;padding:2px 0 2px 12px;border-left:2px solid var(--border-frame);
  font-size:var(--text-14);line-height:1.5;color:var(--text-body);overflow-wrap:anywhere;
}
.pf-ap-t p{margin:0}
.pf-ap-t .pf-ap-sep{height:.6em}
.pf-creux{color:var(--text-muted);text-decoration:underline dotted;text-underline-offset:3px}
/* une note qui PORTE UNE ACTION : elle cesse d'être une ligne de texte et
   devient une rangée, pour que le contrôle qu'elle contient garde la
   taille du doigt sans que le texte se colle à lui (ui/mail.js) */
.hint-act{display:flex;align-items:center;flex-wrap:wrap;gap:4px 8px}
/* LE CHAMP GARDE DE QUOI SE LIRE, ou « Affiner » passe dessous. Un
   plancher en `rem` sur le champ (§5, « une icône ne grandit pas avec le
   texte ») : à 100 % et 125 % rien ne bouge, à 200 % sur un 320 px le
   champ ne gardait que « Métie » à côté d'un « Affiner » qui prenait la
   moitié de la rangée — relevé sous WebKit, audit d'octobre 2026. Le
   contenu se réorganise, il ne se perd pas (WCAG 1.4.10). */
.search-wrap{position:relative;display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px}
.search-wrap .search{flex:1 1 8rem;min-width:0}
/* ---- la barre qui COMMANDE la liste reste avec la liste ----
   Mesuré, 40 pistes en 360×640 : le titre part à 90 px de défilement,
   le champ de recherche et « Affiner » avec lui. À 1200 px il ne reste
   à l'écran ni titre, ni recherche, ni état de filtre — rien que des
   noms d'entreprises. Revenir chercher coûtait quatre balayages.
   Une barre d'outils appartient à ce qu'elle outille : elle se colle.
   On ne colle QUE cette ligne — le titre de l'écran est déjà dit par
   l'onglet allumé en bas, et les puces de filtre ont leur compte dans
   « Affiner ③ ». Un pixel permanent de plus se paierait pour rien.
   Elle mesure ~55 px sur 640, soit le dixième de hauteur que NN/g donne
   pour plafond utile ; la satisfaction chute au-delà de 20-30 %.
   AU REPOS ELLE N'EXISTE PAS : ni fond, ni trait, ni relief. Le décor
   n'apparaît que décrochée (`.est-collee`, posée par la sentinelle de
   `ui/dom.js`) — sur une liste de trois lignes, rien ne s'ajoute jamais.
   Le `::after` habille les 16 px de marge haute de la vue : sans lui,
   les lignes défileraient à découvert au-dessus de la barre. */
@media (max-width:900px){
#view-pistes .search-wrap{
  position:sticky;top:0;z-index:6;
  margin-left:calc(-14px - env(safe-area-inset-left));
  margin-right:calc(-14px - env(safe-area-inset-right));
  margin-bottom:4px;
  padding:0 calc(14px + env(safe-area-inset-right)) 10px calc(14px + env(safe-area-inset-left));
}
/* Le trait se fait à l'OMBRE, pas à la bordure : une bordure qui
   apparaît pousse le contenu d'un pixel, et l'ancrage du défilement
   compense — la liste dérivait de 1 px à chaque décrochage (mesuré :
   700 → 701 → 702). Une ombre ne prend pas de place. */
#view-pistes .search-wrap.est-collee,
#view-pistes .pi-cherche .search-wrap{
  background:var(--surface-chrome);
  box-shadow:0 1px 0 0 var(--border-strong), var(--shadow-raised);
}
#view-pistes .search-wrap.est-collee::after,
#view-pistes .pi-cherche .search-wrap::after{
  content:'';position:absolute;left:0;right:0;bottom:100%;height:16px;
  background:var(--surface-chrome);
}
}
/* Au poste, c'est l'autre repère qui manque : les trois colonnes du
   tableau perdaient leur en-tête au bout d'un écran, et il ne restait
   que trois piles de cartes sans nom. C'est le même défaut, pas la même
   réponse — le champ de recherche, lui, revient d'une seule touche
   (« / »), ce qu'un pouce n'a pas. Le titre de colonne se colle donc au
   poste, la barre de commande au pouce. */
@media (min-width:901px){
  /* `-16px` et non `0` : un élément collant s'arrête sur la boîte de
     CONTENU du défileur, donc 16 px sous le haut de la vue — et une
     carte défilait à découvert dans cette bande. Le titre monte jusqu'au
     bord, sous la barre de l'app, sans laisser de fente. */
  .bcol-h{position:sticky;top:-16px;z-index:4}
}
/* La touche s'annonce dans le champ qu'elle ouvre — l'idiome de tous
   les postes de commandement, et la seule façon de rendre un raccourci
   découvrable sans écrire un écran d'aide. Elle est en retrait : c'est
   une indication, pas une cible ; elle disparaît dès qu'on tape. */
.kbd-hint{
  align-self:center;position:relative;z-index:1;pointer-events:none;
  margin-left:-38px;                /* revient DANS le champ, contre son bord */
  font-family:var(--font-mono);font-size:var(--text-11);line-height:1;
  color:var(--text-disabled);background:var(--surface-chrome);
  border:1px solid var(--border-soft);padding:3px 6px;
}
.search-wrap .search:focus ~ .kbd-hint,
.search-wrap .search:not(:placeholder-shown) ~ .kbd-hint{opacity:0}
/* ↓ prend la place de « / » quand le curseur est dans la barre ET
   qu'il y a du texte : c'est alors la touche qui sert. Une seule touche
   annoncée à la fois — deux se pousseraient l'une l'autre. */
.search-wrap .kbd-bas{display:none}
.search-wrap .search:focus:not(:placeholder-shown) ~ .kbd-hint:not(.kbd-bas){display:none}
.search-wrap .search:focus:not(:placeholder-shown) ~ .kbd-bas{display:block;opacity:1}
/* Entrée s'annonce sur la carte qu'elle ouvrirait — seulement tant que le
   curseur est dans la barre, seul moment où la touche a ce sens. Posée
   dans l'angle, hors du flux : elle ne pousse ni le nom ni la sous-ligne. */
.kbd-enter{
  display:none;position:absolute;top:7px;right:8px;z-index:1;pointer-events:none;
  font-family:var(--font-mono);font-size:var(--text-11);line-height:1;
  color:var(--text-muted);background:var(--surface-chrome);
  border:1px solid var(--border-soft);padding:3px 5px;
}
#view-pistes:has(#piQ:focus) .est-premier .kbd-enter{display:block}
#view-pistes:has(#piQ:focus) .est-premier{border-color:var(--accent)}
.search{
  width:100%;height:var(--ctl);
  background:var(--surface-field);border:1px solid var(--border-field);border-radius:var(--radius-1);
  box-shadow:var(--bevel-field);padding:0 12px;font-size:var(--input-fs);
}
.search::placeholder{color:var(--text-muted)}
.search::-webkit-search-cancel-button,.search::-webkit-search-decoration{-webkit-appearance:none;appearance:none}
/* tri actif (critère changé ou ordre inversé) : marqué, sans voler le teal du héros */
.sort-on{background:var(--tonal-fort);border-color:var(--accent);color:var(--accent-hover)}
/* barre au-dessus d'une liste de choix (tri dans les feuilles) */
.listbar{display:flex;align-items:center;gap:8px;margin-bottom:10px}
/* Le même défaut que sur « Mes pistes », dans une feuille : « Donner »
   et « Prospecter » ouvrent 2400 px de liste dans 720 px de fenêtre —
   trois écrans et demi — et « Tout » comme « Affiner » partaient avec la
   première. Ce sont exactement les deux gestes qu'on cherche une fois
   descendu. Même motif, mêmes règles : rien au repos, la barre ne prend
   fond et relief que décrochée, et le trait se fait à l'ombre pour ne
   pas pousser la liste d'un pixel. Le `::after` couvre les 16 px de
   marge haute du corps de la feuille. */
.modal-b .listbar{position:sticky;top:0;z-index:5;
  margin-left:-18px;margin-right:-18px;padding:0 18px 6px;margin-bottom:4px}
.modal-b .listbar.est-collee{
  background:var(--surface-window);
  box-shadow:0 1px 0 0 var(--border-soft), var(--shadow-raised);
}
.modal-b .listbar.est-collee::after{
  content:'';position:absolute;left:0;right:0;bottom:100%;height:16px;
  background:var(--surface-window);
}
.stick-guet{height:1px;margin-bottom:-1px}
.listbar .linklike,.listbar>span{flex:1;min-width:0}
/* Les deux commandes d'une liste — « Tout » et « Affiner » — sont des
   SŒURS : même poids, même grammaire, aux deux bouts de la barre. Elles
   commandent la liste, elles ne rivalisent pas avec elle : pas de cadre,
   pas de biseau, l'encre de la métadonnée. Un cadre revient uniquement
   quand la commande PORTE quelque chose (un filtre actif), jamais parce
   qu'elle existe. Elles gardent 44 px de haut : légères à l'œil, pleines
   sous le pouce. */
.lb-act{
  display:inline-flex;align-items:center;gap:6px;flex:none;
  min-height:var(--ctl);padding:0 8px;
  background:none;border:1px solid transparent;border-radius:var(--radius-1);
  font:inherit;font-size:var(--text-12);font-weight:var(--weight-semibold);
  color:var(--text-muted);cursor:pointer;text-align:left;
}
.lb-act:first-child{margin-left:-8px}      /* le mot s'aligne sur la liste */
.listbar>.lb-act:last-child{margin-left:auto;margin-right:-8px}
.lb-act .ic{flex:none}
@media (hover:hover){
.lb-act:hover{color:var(--text-link)}
}
.lb-act[aria-pressed="true"]{color:var(--accent)}
.lb-act[aria-pressed="true"] .ic{color:var(--accent)}
/* Un filtre actif se dit UNE fois : par son compte. J'avais ajouté en
   plus un cadre et un aplat teal — trois signaux pour le même fait, et
   une commande qui doublait de poids à côté de « Tout », resté une case
   nue sur la même ligne. Le `margin-right:0` la faisait en outre SAUTER
   de 8 px en s'activant : une cible qui bouge sous le doigt au moment
   même où l'on vient de s'en servir. */
.lb-act.on{color:var(--accent)}
.lb-act.on .ic{color:var(--accent)}
/* ---- la barre de commande d'une liste à cocher (ui/affiner.js) ----
   UN SEUL OBJET, pas trois. Elle a d'abord été une case biseautée, un
   champ blanc et un entonnoir biseauté posés côte à côte : trois boîtes
   de natures différentes, dont deux presque vides pour un glyphe de
   20 px — le défaut que §6 nomme déjà pour « Affiner » dans une feuille
   (« un cadre presque vide, plus lourd que les lignes qu'il commande »).

   C'est maintenant une barre d'outils au sens « Utilitaire 98 » : un
   cadre, un fond, et des SEGMENTS séparés d'un trait d'un pixel. Le
   champ domine parce qu'il est le segment élastique ; les deux
   commandes tiennent leur mot de fin. Le relief appartient à
   l'ensemble, plus à chaque morceau. */
.lb-cmd{display:block}
/* La HAUTEUR se pose sur les segments, pas sur le cadre. Posée sur le
   cadre, le trait d'un pixel de chaque côté la rognait : les trois
   cibles rendaient 42 px pour un plancher de 44 (WCAG 2.5.5). Le cadre
   grandit donc de deux pixels, et chaque segment fait sa vraie taille. */
.lb-box{
  display:flex;align-items:stretch;width:100%;min-width:0;
  background:var(--surface-field);
  border:1px solid var(--border-field);border-radius:var(--radius-1);
  box-shadow:var(--bevel-field);
  overflow:hidden;
}
/* la mise au point se voit sur l'OBJET, pas sur le champ seul — sinon
   deux reliefs se superposent et la barre se disloque à l'œil */
.lb-box:focus-within{border-color:var(--accent)}
.lb-seg{
  flex:none;min-width:var(--ctl);min-height:var(--ctl);
  display:inline-flex;align-items:center;justify-content:center;gap:4px;
  background:none;border:0;padding:0 10px;border-radius:0;
  color:var(--text-muted);font:inherit;font-size:var(--text-13);
  cursor:pointer;
}
.lb-seg .ic{flex:none}
.lb-seg.on,.lb-seg[aria-pressed="true"]{color:var(--accent)}
.lb-seg.on .ic,.lb-seg[aria-pressed="true"] .ic{color:var(--accent)}
@media (hover:hover){
.lb-seg:hover{background:var(--surface-chrome);color:var(--text-body)}
}
.lb-seg:active{background:var(--surface-chrome)}
/* le trait qui sépare deux segments : un pixel, comme une barre
   d'outils — jamais une marge, qui ferait trois objets à nouveau */
.lb-find + .lb-seg,.lb-seg + .lb-find{border-left:1px solid var(--border-soft)}
.lb-find{position:relative;flex:1;min-width:0;min-height:var(--ctl);display:flex;align-items:center}
.lb-find .srch-i{
  position:absolute;left:10px;top:50%;transform:translateY(-50%);
  pointer-events:none;color:var(--text-muted);
  -webkit-mask-image:url(../assets/icons/search.svg);mask-image:url(../assets/icons/search.svg);
}
/* le champ n'a plus ni cadre ni fond : il EST le milieu de la barre */
.lb-q{
  flex:1;width:100%;min-width:0;height:100%;
  background:none;border:0;box-shadow:none;border-radius:0;
  padding:0 10px 0 32px;font-size:var(--input-fs);color:var(--text-body);
}
.lb-q:focus{outline:none}
.lb-q::placeholder{color:var(--text-muted)}
.lb-q::-webkit-search-cancel-button,.lb-q::-webkit-search-decoration{-webkit-appearance:none;appearance:none}

/* ce qui ne partira pas, dit dans le pied, contre le bord opposé aux
   deux canaux : une DONNÉE (mono, discrète), pas un avertissement — un
   compte qui prend une couleur d'alerte crie pour un geste qu'on peut
   défaire d'un tap */
/* Ce qui part, compté au-dessus des deux canaux — sur SA ligne, pas
   glissé à côté d'eux. Posé dans la rangée, il poussait « À distance »
   au rang suivant : le pied passait à deux rangs par accident, et les
   deux canaux perdaient leur symétrie. Une ligne pleine largeur le rend
   délibéré, et elle n'existe que quand il y a quelque chose à dire.
   C'est une DONNÉE — mono, discrète, jamais une couleur d'alerte : un
   compte qui crie pour un geste défaisable d'un tap. */
.dn-cut{flex:1 0 100%;order:-1;text-align:left;
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted)}
/* Les TROIS canaux sur une rangée, au pouce aussi. La règle générale du
   pied (`flex:1 1 40%`) en case deux par rang : « Texte », arrivé le
   1er octobre 2026, tombait seul sur un second rang, étiré sur toute la
   largeur — un bouton secondaire plus large que le primaire, et la
   symétrie avec « Recevoir » (QR · Fichier · Texte) perdue. Ici les trois
   se partagent la rangée ; à texte agrandi ils se replient (WCAG 1.4.10),
   jamais un libellé coupé. */
@media (max-width:900px){
  .modal-donner .modal-f .btn{flex:1 1 0;min-width:max-content}
}
/* La case tient l'AXE des cases de la liste : c'est ce qui la fait lire
   « tout cocher » sans le mot. Une ligne de liste est en retrait de son
   cadre (1 px de trait + 14 px de marge) ; le segment reprend ce retrait
   — et il aligne à GAUCHE, sinon l'icône se déplacerait dès que le
   compte apparaît à côté d'elle, et l'axe bougerait au fil de la
   sélection. */
.lb-all{justify-content:flex-start;padding:0 10px 0 14px}
.lb-all[aria-pressed="true"] .ic{color:var(--accent)}
/* feuille « Trier » : la pile des niveaux (principal + départages) */
/* puces d'état sous la recherche (#8) — la croix enlève, la puce de tri
   s'inverse au tap ; le sens vit dans la puce */
/* les filtres posés : UNE ligne qui glisse, jamais une pile qui repousse
   la liste hors de l'écran (mesuré : trois étiquettes empilées mangeaient
   74 px et le premier résultat ne venait qu'à 258 px) */
.chips-row{
  display:flex;flex-wrap:nowrap;gap:6px;margin:-6px 0 12px;
  overflow-x:auto;padding:2px 2px 4px;
}
/* … et au pouce, elle glisse jusqu'au BORD DE L'ÉCRAN. Coupée au bord
   du contenu, à 14 px du bord, la dernière étiquette (« Lille · 15 km »)
   se lisait comme un objet raté, pas comme une rangée qui continue —
   relevé à l'audit d'octobre 2026. C'est le dessin de toute rangée de
   puces qui glisse (Material 3, iOS) : la coupe au bord de l'écran dit
   « il y en a d'autres », la coupe au milieu dit « c'est cassé ». La
   marge rentre ce que le padding sort, le premier objet reste aligné
   sur le reste de la page. */
@media (max-width:900px){
  /* sans barre de défilement : la coupe au bord de l'écran dit déjà
     qu'il y a une suite, et WebKit en dessinait une sous les étiquettes */
  .chips-row{scrollbar-width:none}
  .chips-row::-webkit-scrollbar{display:none}
  .chips-row{
    margin-left:calc(-14px - env(safe-area-inset-left));
    margin-right:calc(-14px - env(safe-area-inset-right));
    padding-left:calc(14px + env(safe-area-inset-left));
    padding-right:calc(14px + env(safe-area-inset-right));
    scroll-padding-inline:14px;
  }
}
.st-chip{
  flex:none;display:inline-flex;align-items:center;gap:6px;
  min-height:var(--ctl);padding:4px 11px;white-space:nowrap;cursor:pointer;
  background:var(--surface-chrome);border:1px solid var(--border-strong);
  border-radius:var(--radius-1);box-shadow:var(--bevel-up);
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-body);
}
.st-chip .dotc{width:8px;height:8px;border:1px solid var(--border-strong);flex:none}
.st-chip:active{box-shadow:var(--bevel-down);transform:translate(1px,1px)}
/* La croix vit DANS la puce : un seul bouton, qui dit ce qu'il fait
   (Baymard, filtres appliqués). Discrète — c'est le mot qu'on lit. */
.st-chip .ic-12{margin-left:1px;opacity:.55}
/* SANS BOÎTE (retour du mainteneur, 13 octobre : « les paramètres qui
   se rajoutent quand on entre un mot prennent trop de place »). Sous la
   barre, une étiquette ACCOMPAGNE ce qu'on a tapé : c'est la règle du
   bouton texte (§6), appliquée à la rangée. Trois boîtes de 44 px en
   police mono débordaient l'écran dès la troisième (« Lille · 15 km »
   coupée au bord) ; en texte, avec leur icône et leur croix, les trois
   tiennent sur une ligne de 390 px. La cible reste celle du doigt (§5) :
   la boîte invisible fait `--ctl`, on n'a retiré que l'habit. Le
   proposé garde son pointillé, sous le MOT : « proposé en pointillé,
   posé en plein » se lit toujours. */
.chips-row{gap:0}
.chips-row .st-chip,.chips-row .prop-chip{
  background:none;border:0;box-shadow:none;gap:5px;padding:4px 10px 4px 6px;
  font-family:var(--font-ui);font-size:var(--text-14);color:var(--text-body);
}
.chips-row .st-chip .ic-14,.chips-row .prop-chip .ic-14{color:var(--accent)}
.chips-row .st-chip .ic-12{opacity:.45}
.chips-row .prop-chip{color:var(--text-muted);text-decoration:underline dotted;text-underline-offset:4px}
.chips-row .st-chip:active,.chips-row .prop-chip:active{
  box-shadow:none;background:var(--surface-chrome);transform:translate(1px,1px)}
/* Le panneau des propositions de la barre vide : accroché SOUS la barre,
   par-dessus la liste — jamais dedans, sinon il la pousse au focus et la
   remonte au départ du curseur (voir `propsHTML`, ui/pistes.js). Les
   puces s'y replient par rangs : toutes visibles, rien à faire défiler. */
#piProps{position:absolute;left:0;right:0;top:100%;z-index:9}
#piProps:empty{display:none}
.props-pan{
  display:flex;flex-wrap:wrap;gap:6px;margin-top:6px;padding:8px;
  background:var(--surface-window);border:1px solid var(--border-strong);
  border-radius:var(--radius-1);box-shadow:var(--shadow-raised);
}
@media (max-width:900px){
  /* au pouce la barre déborde jusqu'aux bords (pour coller) : le panneau,
     lui, s'aligne sur le CHAMP */
  #view-pistes .search-wrap #piProps{
    left:calc(14px + env(safe-area-inset-left));right:calc(14px + env(safe-area-inset-right));
    top:calc(100% - 10px);
  }
}
/* PROPOSÉ, PAS POSÉ. Une recherche que la barre suggère (barre vide) ou
   un élargissement (« Nord (59) +3 ») n'est pas un filtre actif : trait
   pointillé, aucun relief, fond transparent — « tu peux », là où la
   puce pleine dit « c'est actif ». Même hauteur, même police : les deux
   vivent sur la même rangée et ne doivent pas la faire sauter. */
.prop-chip{
  flex:none;display:inline-flex;align-items:center;gap:6px;
  min-height:var(--ctl);padding:4px 11px;white-space:nowrap;cursor:pointer;
  background:transparent;border:1px dashed var(--border-strong);border-radius:var(--radius-1);
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-body);
}
.prop-chip .ic{color:var(--text-muted)}
.prop-chip .fl-n{font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted)}
.prop-chip:active{background:var(--teal-wash);border-style:solid;transform:translate(1px,1px)}
@media (hover:hover){
  .prop-chip:hover{border-color:var(--accent);color:var(--accent)}
  .prop-chip:hover .ic{color:var(--accent)}
}
/* le « puis par » (avancé) se replie dans la section Trier */
.srt-adv{margin:2px 0 14px}
.srt-adv>summary{
  cursor:pointer;user-select:none;list-style:none;
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);
  /* discret à l'œil, pas au doigt : la ligne fait la hauteur d'une cible
     (§5), le texte reste petit */
  min-height:var(--ctl);display:flex;align-items:center;gap:6px;
}
.srt-adv>summary::-webkit-details-marker{display:none}
.srt-adv>summary::before{content:'▸';font-size:9px}
.srt-adv[open]>summary::before{content:'▾'}
.srt-adv[open]>summary{margin-bottom:8px}
.srt-dir{font-family:var(--font-mono)}
/* feuille « Filtrer » : chips statut/domaine en deux colonnes — un tap
   ajoute la valeur, un re-tap la retire, et plusieurs valeurs d'une
   même famille cohabitent (« cyber ou cloud ») */
.fl-grid{display:grid;gap:7px;margin-bottom:14px;
  /* deux colonnes tant qu'une moitié garde 8 rem, une seule sinon : le
     seuil est en `rem`, il suit la police. En `1fr 1fr`, « Cybersécurité »
     imposait sa largeur à la colonne et la feuille coupait la seconde au
     bord droit, à 200 % en 320 px (audit 7.1.0) */
  grid-template-columns:repeat(auto-fill,minmax(min(100%,max(8rem,calc((100% - 7px) / 2))),1fr))}
.fl-chip{
  display:flex;align-items:center;gap:8px;min-height:var(--ctl);
  padding:5px 10px;text-align:left;cursor:pointer;
  background:var(--surface-window);border:1px solid var(--border-strong);
  border-radius:var(--radius-1);box-shadow:var(--bevel-up);
  font-family:var(--font-ui);font-size:var(--text-13);color:var(--text-body);
}
.fl-chip:active{box-shadow:var(--bevel-down);transform:translate(1px,1px)}
/* le compte à droite de la puce : combien de pistes ce filtre garderait.
   Un statut à zéro reste visible (savoir qu'on n'a aucune réponse est
   une information) mais s'éteint — il ne peut rien filtrer. */
.fl-chip .fl-n{margin-left:auto;font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted)}
.fl-chip.on .fl-n{color:inherit;opacity:.75}
.fl-chip.fl-off{opacity:.45;cursor:not-allowed;box-shadow:none}
.fl-chip.on{background:var(--teal-wash);border-color:var(--accent);color:var(--accent);font-weight:var(--weight-semibold)}
.fl-chip .dotc{width:8px;height:8px;border:1px solid var(--border-strong);flex:none}

/* ============ TÊTE DE PAGE ============ */
.td-head{display:flex;align-items:baseline;gap:12px;margin-bottom:14px;flex-wrap:wrap}
/* UN SEUL `h1` PAR ÉCRAN, ET C'EST LE NOM DE L'ÉCRAN.
   Le document n'en avait aucun : chaque vue commençait en `h2`, ce qui
   laisse un lecteur d'écran sans point d'entrée et fait sauter un rang
   d'emblée (W3C WAI, structure de titres, 1.3.1). Le titre visible EST
   le titre de la page ici — la route en fait une page. Rien ne change à
   l'écran : c'est la même règle de style, sur la balise juste. */
.td-head h1{font-size:var(--text-20);letter-spacing:var(--tracking-tight)}
.td-date{font-family:var(--font-mono);font-size:var(--text-12);color:var(--text-muted)}
/* AU POUCE, le compteur de « Mes pistes » (« 7 écrites · 3 réponses ·
   1 décrochée ») passe SOUS le titre, sur toute la largeur, comme le
   sous-titre d'un grand titre d'iOS ; « Choisir » reste en haut à
   droite. Deux essais avant : en ligne, dès la police d'un iPhone
   (17 px), « Choisir » tombait seul à gauche (vu sous WebKit) ; replié
   dans une colonne étroite entre le titre et le bouton, il empilait ses
   trois chiffres sur trois rangs de 12 px (audit du 28 octobre 2026). */
@media (max-width:900px){
  .pi-head{row-gap:2px}
  .pi-head #piCount{order:3;flex:1 0 100%}
  /* le TITRE prend la place, pas une marge : à 200 % sur un 320 px,
     « Choisir » passe sur son rang et y revient au bord GAUCHE
     (e2e-decroche) — `margin-left:auto` l'envoyait seul à droite */
  .pi-head h1{flex:1 0 auto}
}
.td-date .nw{white-space:nowrap}
/* le retour d'un 2ᵉ écran : nu, comme la poubelle au survol — un chevron
   qui n'est pas un bouton encadré. La ligne se centre sur le titre. */
.td-back{align-items:center;gap:4px}
.td-back .abtn{color:var(--text-muted)}
/* la cible pleine du doigt (§5) : le chevron faisait 32 px de large,
   sous le plancher de 44 que tout le reste respecte. Il garde sa place
   à l'œil — collé au bord comme le titre — en reprenant à gauche la
   moitié de ce qu'il gagne. */
.td-back .abtn{margin-left:calc((14px - var(--ctl)) / 2)}
.done-line{
  display:inline-flex;align-items:center;gap:6px;margin:0 0 10px;
  font-family:var(--font-mono);font-size:var(--text-12);font-weight:var(--weight-semibold);color:var(--green);
}
.td-foot{margin-top:16px;display:flex}
/* l'anneau se pose DEDANS : le pied vit au bord du panneau qui défile,
   et un anneau dehors y perdait un tiers de son aire (e2e-focus) */
.td-foot:focus-visible{outline-offset:-2px}
/* le pied d'« Aujourd'hui » : au pouce les lignes s'empilent comme
   avant ; au poste elles se rangent sur un rang sous un filet, pour
   qu'on les lise comme un pied de page et non comme un lien égaré
   sous la colonne la plus courte du tableau */
.td-under{margin-top:16px}
@media (max-width:900px){
  /* Au pouce, le pied est un PIED : il se pose en bas de la vue quand la
     journée est courte — la zone qu'une main atteint sans changer de
     prise — au lieu de flotter sous la dernière tranche avec 440 px de
     vide dessous. « 2 pistes sans prochaine action → » est justement le
     prochain geste quand il n'y a rien à faire : il doit être sous le
     pouce, pas à 27 % de la hauteur. Journée chargée, il suit le contenu :
     `margin-top:auto` ne prend que la place qui reste, s'il en reste. */
  #view-aujourdhui>.page-inner{display:flex;flex-direction:column;min-height:100%}
  /* Pas de filet ici. Il devait dire « le travail s'arrête, le pied
     commence » ; à l'usage personne ne sait à quoi il sert, et sur une
     journée courte il vient se poser contre la dernière carte, où il se
     lit comme un bord de cette carte. Le pied se distingue déjà par sa
     position — tout en bas — et par son style de lien. Un séparateur
     qu'il faut expliquer ne sépare rien. */
  #view-aujourdhui>.page-inner>.td-under{margin-top:auto}
  /* R3 : « Écrire les N mails » tient le pied, au-dessus de la barre, et
     y RESTE quand la journée défile (`sticky`). Le pied de liens passe
     au-dessus de lui : deux marges automatiques se partageraient la place
     et laisseraient les liens flotter au milieu. */
  #view-aujourdhui>.page-inner>.td-pret{margin-top:auto}
  #view-aujourdhui>.page-inner>.td-under+.td-pret{margin-top:12px}
  .td-pret{position:sticky;bottom:0;z-index:3;padding:10px 0 2px;background:var(--surface-desktop)}
  .td-pret>.btn{width:100%}
}
@media (min-width:901px){
  .td-head>#tdPartir{margin-left:auto;align-self:center}
}
@media (min-width:901px){
  .td-under{
    display:flex;flex-wrap:wrap;align-items:center;gap:8px 20px;
    margin-top:20px;padding-top:14px;border-top:1px dashed var(--border-soft);
  }
  .td-under .td-triage{width:auto}
  .td-under .td-foot{margin-top:0}
  /* AU POSTE, LE TABLEAU PREND LA HAUTEUR DE SA JOURNÉE (audit
     adaptatif, octobre 2026). Il s'étirait jusqu'au bas de la vue pour
     que le pied s'y pose : relevé en 1280 × 800, trois cadres de 600 px
     pour UNE carte chacun — 85 % de chaque cadre vide, et c'est le vide
     qui portait l'encre (bordure, relief, fond). Au pouce, le pied
     descend parce que la main est en bas (§5) ; une souris n'a pas de
     zone facile, et un cadre vide n'est pas une place qui a un
     propriétaire, c'est un manque qu'on encadre. Les tableaux de
     référence font pareil : une colonne de Trello ou de Notion s'arrête
     sous sa dernière carte. Le pied SUIT donc le travail du jour, sous
     son filet, comme la fin d'une page. */
}
/* Au poste, une tranche qui suit le tableau (« Sans nouvelles », « Tes
   amis ») se range sur SES colonnes : en pleine largeur, ses lignes
   faisaient 1 060 px et le geste tombait à 900 px du nom qu'il
   concerne — la faute de proximité que « Échanger » avait déjà payée
   (NN/g, loi de proximité ; Fitts : la cible loin de l'œil). Trois
   colonnes, la même gouttière que le tableau, et chaque ligne garde
   ses gestes à portée de son nom. */
@media (min-width:901px){
  #view-aujourdhui>.page-wide .tranche .tr-rows{
    display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;align-items:start
  }
}
/* « À trier (N) » — la ligne calme qui regroupe les entrants (#10) */
.td-triage{
  display:flex;align-items:center;gap:8px;width:100%;margin-top:16px;
  padding:10px 12px;min-height:var(--ctl);cursor:pointer;
  background:var(--surface-chrome);border:1px dashed var(--border-strong);
  border-radius:var(--radius-0);
  font:inherit;font-size:var(--text-13);color:var(--text-muted);text-align:left;
}
.td-triage .tr-n{margin-left:auto}
.td-triage:active{box-shadow:var(--bevel-down)}

/* ============ TRANCHES ============ */
.tranche{margin-top:18px}
.tr-h{
  display:flex;align-items:center;gap:7px;
  font-size:var(--text-11);font-weight:var(--weight-bold);
  letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--text-muted);margin-bottom:8px;
}
.tr-n{
  font-family:var(--font-mono);font-size:var(--text-11);
  background:var(--surface-chrome);border:1px solid var(--border-soft);
  padding:0 6px;border-radius:var(--radius-1);
}
/* le titre d'une tranche pliable vit DANS son `<summary>` : le résumé
   garde son rôle de bouton, le `<h3>` sa place dans le plan. Il reprend
   la mise en page de `.tr-h` pour que rien ne bouge à l'écran. */
.tr-h>h2{
  display:flex;align-items:center;gap:7px;
  font:inherit;color:inherit;letter-spacing:inherit;text-transform:inherit;
}
/* ============ OÙ L'ON CHERCHE : deux onglets de texte ============
   (docs/presentation-recherche.md) « Mes pistes » et « À découvrir »,
   chacun avec son compte. LÉGER, à la demande du mainteneur : aucune
   boîte, aucun aplat — le mot actif en encre et un trait du châssis sous
   lui (§4 : le navy dit « où tu es »), l'autre en gris. La cible garde
   sa hauteur de doigt (§5) ; seul l'habit part. Et sa LARGEUR : « Amis »
   ne faisait que 35 px de mot, sous le doigt c'est 44 au moins — le mot
   se centre sur son trait, qui dit la cible (ce qu'on voit est ce qu'on
   touche, §5). */
.portee{display:flex;flex-wrap:wrap;gap:0 22px;margin:-2px 0 10px}
.pt{
  display:inline-flex;align-items:center;justify-content:center;gap:6px;
  min-height:var(--ctl);min-width:var(--ctl);padding:0 1px;
  background:none;border:0;border-bottom:2px solid transparent;border-radius:0;cursor:pointer;
  font-size:var(--text-14);font-weight:var(--weight-semibold);color:var(--text-muted);
}
.pt.on{color:var(--text-body);border-bottom-color:var(--select-bg)}
.pt-n{font-family:var(--font-mono);font-size:var(--text-11);font-weight:var(--weight-regular);color:var(--text-muted)}
.pt[aria-busy="true"] .pt-n{opacity:.6}
@media (hover:hover){
  .pt:not(.on):hover{color:var(--text-body)}
}
/* « Affiner » ne regarde que tes pistes : dans « À découvrir », il n'est pas.
   La touche « / » rentre dans le champ par une marge négative que le
   bouton voisin absorbait : sans lui, le champ débordait de 9 px
   (mesuré). Elle se pose donc dans le champ, contre son bord droit. */
/* pendant la frappe au pouce, la liste a de quoi défiler jusqu'à la
   barre (ui/pistes.js, `amenerBarre`) — le surplus vit sous le clavier */
.pi-cherche #piBody{min-height:100vh;min-height:100dvh}
.pt-dec #piAffiner,.pt-dec .kbd-bas,.pi-vide #piAffiner{display:none}
.pt-dec .search-wrap .kbd-hint{position:absolute;right:8px;top:50%;transform:translateY(-50%);margin:0}

/* ============ « À DÉCOUVRIR », LA VUE ============
   Ce que l'annuaire connaît et que tu n'as pas. Un cadre, des rangées
   sans relief séparées d'un pointillé — la forme de toute liste de
   l'app (§6). AUCUN bouton sur une ligne : elle ouvre l'aperçu, qui
   porte le seul geste plein. */
.dc-list{
  background:var(--surface-window);border:1px solid var(--border-strong);
  border-radius:var(--radius-1);
}
.dc-row{border-bottom:1px dashed var(--border-soft)}
.dc-row:last-child{border-bottom:0}
.dc-main{display:flex;flex-direction:column;gap:3px;min-height:var(--ctl);justify-content:center;
  padding:9px 12px;cursor:pointer;text-align:left}
/* le nom est l'identité de la ligne : il plie, il ne se coupe pas (§4) */
.dc-nom{
  font-size:var(--text-14);font-weight:var(--weight-semibold);color:var(--text-body);
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;white-space:normal;
}
/* la sous-ligne est une DONNÉE : où, à quelle distance, quelle taille —
   elle s'élide par la fin, et c'est la taille qu'on peut perdre */
.dc-sub{
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* ajoutée : un mot, devant — l'état, pas un bouton */
.dc-ok{display:inline-flex;align-items:center;gap:3px;margin-right:8px;color:var(--accent);font-weight:var(--weight-semibold)}
/* « nouveau » et « ta formation » : un mot devant, en accent — ce qui
   CHANGE ou ce qui DÉPARTAGE (§6, règle 1), jamais un mark-* : rien ne
   presse */
.dc-neuf,.dc-ton,.dc-ami{margin-right:8px;color:var(--accent);font-weight:var(--weight-semibold)}
/* dans l'aperçu, l'ami qui y est passé prend le bandeau de la fiche :
   le même objet, le même dessin, là où l'on décide d'ajouter */
.ap-ami{margin:10px 0 0}
/* la ligne porte le geste « Pas pour moi » : la rangée défile sous le
   doigt, et à la souris la croix vit au bout */
.dc-row .sw-in{display:flex;align-items:stretch}
.dc-row .dc-main{flex:1;min-width:0}
.dc-row .hov-del{margin-right:6px}
.dc-sel .sw-in{background:transparent}
/* écarter n'est pas supprimer : le dessous ne prend pas le rouge */
.dc-row .sw-under{background:var(--surface-chrome);color:var(--text-body)}
@media (hover:hover){
  .dc-row .hov-del:hover{color:var(--text-body)}
}
.dc-row .hov-del:focus-visible{color:var(--text-body)}
.dc-ecl{margin-top:4px}
.dc-etat{margin:0;padding:12px 2px;font-family:var(--font-mono);font-size:var(--text-12);color:var(--text-muted)}
.dc-err{display:flex;align-items:center;flex-wrap:wrap;gap:8px 12px}
.dc-vue .tr-more{margin-top:6px}
/* qui recrute en alternance, autour d'ici : un lien, APRÈS la liste,
   calé sur son bord — il emmène ailleurs, il ne pèse pas plus */
.dc-offres{margin:8px 0 0 -8px;gap:6px}
.dc-offres span{min-width:0}
/* la source se nomme (la licence de l'annuaire le demande) : en pied,
   discrète, mais une cible entière au doigt */
.dc-src-l{margin:2px 0 0;font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted)}
.dc-src{display:inline-flex;align-items:center;min-height:var(--ctl);color:var(--text-muted)}
@media (hover:hover){
  .dc-main:hover .dc-nom{text-decoration:underline;text-underline-offset:3px}
}
/* AU POSTE : la liste et l'aperçu CÔTE À CÔTE (Material 3, liste-
   détail). L'aperçu reste en vue pendant que la liste défile ; la ligne
   choisie le dit par un liseré du châssis (§4 : « où tu es »), sans
   aplat. */
@media (min-width:901px){
  .dc-split{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);gap:24px;align-items:start}
  .dc-detail{position:sticky;top:12px;padding:4px 0 0;max-height:calc(100vh - 140px);overflow:auto}
  .dc-sel{background:var(--surface-chrome);box-shadow:inset 3px 0 0 var(--select-bg)}
}

/* ============ L'APERÇU — sans un lien à toucher ============
   QUI (le nom), OÙ (le lieu, et une alerte en ligne), puis la carte
   (ce qu'elle fait, ses missions, sa taille, à qui écrire), puis les liens. Le
   reste en petit gris. */
.ap-nom{margin:0;font-size:var(--text-16);font-weight:var(--weight-bold);color:var(--text-body);line-height:1.25}
.ap-faits{margin:4px 0 0;font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted)}
/* `inline-block` : un badge EN LIGNE garde son cadre hors de la boîte
   de ligne, et quand la ligne se replie il mord sur le rang du dessus —
   « Procédure collective » chevauchait « Saint-Andre-Lez-Lille · 3 km »
   sous WebKit (audit d'octobre 2026). En bloc en ligne, sa hauteur
   compte, et le rang s'ouvre pour lui. */
.ap-faits .mark{display:inline-block;margin:2px 0 2px 8px;line-height:1.3}
/* ============ LA CARTE D'UNE ENTREPRISE (ui/carte.js) ============
   La même dans l'aperçu et dans « À savoir ». Rien n'y est un bouton :
   une phrase, puis trois lignes qui aident à choisir — les missions, la
   taille, à qui écrire. Le seul lien mène à la personne. */
.ct{margin:12px 0 0}
.ct:empty{display:none}
.ct-quoi{display:flex;align-items:flex-start;gap:10px}
.ct-quoi p{margin:0;font-size:var(--text-14);line-height:1.45;color:var(--text-body)}
.ct-logo{flex:none;width:40px;height:40px;padding:4px;box-sizing:border-box;object-fit:contain;
  background:var(--surface-logo);border:1px solid var(--border-soft)}
.ap .ct-tete{margin-bottom:6px}
/* « colle à ta formation » : un atout, donc l'accent — jamais un mark-*,
   rien ne presse (§6, la recommandation de « Par où commencer ») */
.ct-ton{display:inline-flex;align-items:center;gap:3px;margin-left:4px;white-space:nowrap;
  font-weight:var(--weight-semibold);color:var(--accent)}
/* le lien qui trouve la personne suit son nom, à la taille du doigt —
   mais sa cible ne POUSSE pas la ligne : les marges négatives rendent au
   rang la hauteur d'une ligne de texte, et les 44 px débordent sur le
   libellé et le pointillé, qui ne se tapent pas. Sans elles, « Écrire
   à » était la seule rangée de la carte plus haute que ses sœurs, son
   texte décalé de 12 px (audit d'octobre 2026). */
.ct-li{vertical-align:middle;margin:calc((1.5em - var(--ctl)) / 2) 0 calc((1.5em - var(--ctl)) / 2) 2px}
.ap .ct .fk{padding-left:0;padding-right:0}
/* dans « À savoir », la carte prend le cadre de la fiche : sa tête
   s'aligne sur les rangées, et un pointillé la sépare de ce qui suit */
.fi-know > .ct{margin:0}
.fi-know .ct-tete{padding:8px 10px 10px;border-bottom:1px dashed var(--border-soft)}
.fi-know > .ct .fk:last-child{border-bottom:1px dashed var(--border-soft)}
.ct-le{margin-left:6px;font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted)}
.ct-src{margin:10px 0 0;font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted)}
.ap-agir{display:flex;align-items:center;flex-wrap:wrap;gap:6px 12px;margin:12px 0 0}
.ap-pris{display:inline-flex;align-items:center;gap:6px;font-size:var(--text-13);font-weight:var(--weight-semibold);color:var(--accent)}
/* ---- CE QUE L'ANNUAIRE APPORTE À LA FICHE : la carte, rien d'autre ----
   (minimalisme, 7 octobre 2026 : plus un lien, plus un bouton autour) */
/* la source, sous la carte de « À savoir », calée sur ses rangées */
.fi-know .ct-src{margin:0;padding:6px 10px 8px}
/* sous le nom : une entreprise fermée, au langage d'urgence */
.fa-etat-nom:not(:empty){display:inline-flex;align-items:center;gap:6px;margin-left:8px}
/* « Ajouter un contact » : le dirigeant proposé, en pointillé */
.ce-sugg{display:flex;align-items:center;flex-wrap:wrap;gap:6px 8px;margin:0 0 12px}
.tr-late .tr-h{color:var(--amber)}
.tr-late .tr-h .tr-n{border-color:var(--amber);background:var(--amber-wash)}
/* replier/déplier « Bientôt » est un geste au pouce comme un autre :
   la ligne fait 44 px, pas la hauteur de ses 11 px de texte */
/* La CIBLE fait 44 px, la mise en page n'en prend pas 44. Le repli de
   « Bientôt » affichait un en-tête de 44 px à côté de deux en-têtes de
   17 px : trois titres de même rang, dont un 2,6 fois plus haut — c'est
   ça qui cassait l'harmonie de l'écran. Le rembourrage donne le pouce,
   les marges négatives le rendent à la mise en page : le doigt garde ses
   44 px, l'œil retrouve le rythme des deux autres. */
details.tranche>summary{
  cursor:pointer;list-style:none;user-select:none;
  /* Tout le rembourrage passe AU-DESSUS du libellé, et la marge négative
     le rend à la mise en page. En le répartissant, la carte du dessous
     recouvrait les 6 derniers pixels de la cible et la ramenait à 38 px —
     sous le plancher tactile, sans que rien ne le montre. Ici : libellé
     aligné sur un `h3`, 8 px sous lui comme les deux autres tranches, et
     44 px de cible que personne ne recouvre. */
  padding:19px 0 8px;min-height:var(--ctl);margin:-19px 0 0;
}
details.tranche>summary::-webkit-details-marker{display:none}
details.tranche>summary::before{
  content:'';width:0;height:0;flex:none;
  border-left:5px solid var(--accent);border-top:4px solid transparent;border-bottom:4px solid transparent;
}
details.tranche[open]>summary::before{
  border-left:4px solid transparent;border-right:4px solid transparent;
  border-top:5px solid var(--accent);border-bottom:0;
}
.tr-rows{display:flex;flex-direction:column;gap:7px}
.tr-more{align-self:flex-start}

/* ============ LIGNES D'ACTION ============ */
.act-row{
  position:relative;border:1px solid var(--border-strong);border-radius:var(--radius-0);
  background:var(--surface-window);box-shadow:var(--shadow-raised);
  overflow:hidden;touch-action:pan-y;
}
.act-under{
  position:absolute;inset:0;display:flex;align-items:center;gap:6px;padding:0 16px;
  font-weight:var(--weight-bold);font-size:var(--text-13);opacity:0;
}
.act-under-done{background:var(--green-wash);color:var(--green);justify-content:flex-start}
.act-under-report{background:var(--amber-wash);color:var(--amber);justify-content:flex-end}
.swipe-done .act-under-done{opacity:1}
.swipe-report .act-under-report{opacity:1}
/* LES ICÔNES NE VOLENT PAS LA PLACE DU NOM À TEXTE AGRANDI.
   Trois icônes ne grandissent pas avec le texte : leur largeur est
   figée par `--ctl`. Mesuré en 320 px, la rangée fait 290 px dont 148
   pour les gestes — le nom de la piste, qui est l'attribut distinctif
   de la ligne (§6), n'en garde que 134, quelle que soit la taille du
   texte. À 150 % il lui faut quatre rangs pour trois disponibles, et
   « Thales Digital Identity and Security » perdait sa fin.
   La rangée se replie donc : les gestes passent DESSOUS et le nom
   prend toute la largeur. Le seuil est un plancher en `rem` sur la
   colonne du nom — il suit la police, sans média query, et l'ordre de
   lecture ne change pas. 6,5 rem est mesuré, pas choisi : c'est la
   seule valeur qui laisse 100 % ET 125 % strictement identiques
   (134 px en 320, 170 en 360, 200 en 390) et qui replie exactement aux
   tailles où le nom se coupait — 150 % en 320 px, 175 % en 360.
   C'est WCAG 1.4.10 (le contenu se réorganise, il ne se perd pas).
   `column-gap` remplace la marge : la zone morte de 8 px entre ouvrir
   la fiche et les gestes reste. Sur une rangée partagée, c'est la
   colonne du nom (`flex:1`) qui pousse les gestes à droite ; repliés
   sur leur propre rang, ils reviennent au bord gauche, sous le nom
   qu'ils concernent (e2e-decroche). */
.act-in{position:relative;display:flex;align-items:center;flex-wrap:wrap;
  column-gap:8px;row-gap:8px;
  background:var(--surface-window);transition:transform 60ms linear}
/* Une zone morte entre OUVRIR la fiche et les trois gestes : leurs
   bords se touchaient (0 px mesuré), et un pouce posé à la frontière
   ouvrait la fiche au lieu de marquer « Fait ». Deux conséquences très
   différentes ne doivent pas partager une arête. */
.act-main{flex:1;min-width:6.5rem;padding:9px 0 9px 12px;cursor:pointer;display:flex;flex-direction:column;gap:2px}
@media (hover:hover){
.act-main:hover .act-verb{color:var(--text-link)}
}
.act-verb{
  font-size:var(--text-14);font-weight:var(--weight-semibold);line-height:1.35;
  /* trois lignes, pas deux : à police doublée un nom d'entreprise en
     demande trois, et il ne doit pas se couper (voir `.row-item h3`) */
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;
}
/* L'échéance d'abord, ce qu'on a prévu de faire ensuite.
   L'asymétrie qui a coûté le plus cher ici est réglée à la racine : le
   verbe avait droit à deux lignes et le nom pas même à une entière, si
   bien qu'« Orange Cyberdefense » se lisait « Orange Cy… » sur un petit
   téléphone — une piste qu'on ne reconnaît plus ne sert à rien. Le nom
   occupe maintenant la place à deux lignes ; c'est le verbe, répété
   d'une ligne à l'autre et doublé par les boutons, qui s'élide. */
.act-sub{
  display:flex;align-items:center;flex-wrap:wrap;gap:2px 6px;min-width:0;
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);
}
.act-sub .act-who{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* ce qu'on a prévu de faire est une PHRASE, pas une donnée : elle garde
   la police de texte courant au milieu d'une sous-ligne en mono */
.act-sub .act-do{
  font-family:var(--font-ui);font-size:var(--text-12);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
/* La raison qui a fait monter la ligne : de l'accent, jamais un
   `mark-*`. Le langage d'urgence reste réservé à ce qui RÉCLAME
   quelque chose — ici rien ne presse, c'est un atout. Elle ne
   s'élide pas : c'est le reste de la sous-ligne qui cède. */
.act-sub .act-vecu{flex:none;color:var(--accent);font-weight:var(--weight-semibold)}
/* Sur les plus petits écrans il manquait 7 px au nom, pris par les
   retraits de la ligne. On les rend — jamais les 8 px entre les gestes,
   qui protègent le doigt. */
@media (max-width:340px){
  .act-in .act-main{padding-left:8px}
  .act-in .act-btns{padding-right:0}
}
.act-sub .act-when{flex:none}
/* La marque se range DANS la ligne sans la grandir. Mesuré sur une
   journée réelle : les lignes marquées faisaient 60 px et les autres 56 —
   quatre pixels d'écart qui ne disent rien du contenu, seulement qu'un
   badge est là. Une liste dont le pas change au gré d'un badge se balaie
   moins bien. `line-height:1` ramène la marque dans la boîte de ligne du
   texte qu'elle accompagne. */
.act-sub .mark{padding:1px 6px;line-height:1}
/* Trois gestes aux conséquences distinctes — écrire, reporter, clore —
   plus « ouvrir la fiche » à leur gauche. Ils se touchaient (0 px
   mesuré) : un pouce posé à la frontière ouvrait la fiche au lieu de
   marquer « Fait ». 8 px partout, et chaque geste ne touche plus que
   lui-même quand la visée dérape de 6 px. */
.act-btns{display:flex;flex:none;padding-right:8px;gap:8px}
/* LA CASE « FAIT », EN TÊTE (19 octobre 2026) : le dessin de toute liste
   de tâches. Vide au repos, verte et cochée à l'appui — et elle le reste
   pendant que la ligne s'en va (`act-gone`). La cible est celle du doigt,
   la case dessinée est petite : c'est une case, pas un bouton de plus. */
.act-fait{
  flex:none;align-self:center;width:var(--ctl);height:var(--ctl);margin-left:4px;
  display:inline-flex;align-items:center;justify-content:center;
  background:none;border:0;padding:0;cursor:pointer;
}
.act-case{
  width:20px;height:20px;display:inline-flex;align-items:center;justify-content:center;
  border:1.5px solid var(--border-strong);border-radius:var(--radius-1);
  background:var(--surface-field);box-shadow:var(--bevel-field);color:var(--surface-field);
}
.act-case .ic{opacity:0}
.act-fait:active .act-case,.act-gone .act-case{background:var(--green);border-color:var(--green);box-shadow:none}
.act-fait:active .act-case .ic,.act-gone .act-case .ic{opacity:1}
@media (hover:hover){
.act-fait:hover .act-case{border-color:var(--green)}
}
.act-fait+.act-main{padding-left:4px}
/* « Reprendre » (R2) : le repère d'une ligne sans case tient la place de
   la case, pour que le nom garde un seul bord dans la liste */
.act-sorte{
  flex:none;align-self:center;width:var(--ctl);height:var(--ctl);margin-left:4px;
  display:inline-flex;align-items:center;justify-content:center;color:var(--text-muted);
}
.act-sorte+.act-main{padding-left:4px}
/* L'ANNEAU VA AU NOM. Depuis que la case « Fait » ouvre la ligne et que
   le geste la ferme, le contrôle qui ouvre la fiche n'en couvre plus que
   le milieu : l'anneau posé sur la RANGÉE l'entourait de loin, et le
   relevé de `e2e-focus` n'y trouvait plus que deux bords — un trait en
   haut, un trait en bas, pas un cadre. Il se pose donc DANS le nom
   (`outline-offset` négatif, que le rognage de la rangée ne touche pas),
   et la rangée rend le sien. */
.act-in>.act-main.sw-cible:focus-visible{outline:var(--focus-outline);outline-offset:-2px}
.act-row:has(> .act-in > .sw-cible:focus-visible){outline:none}
/* le second geste d'une ligne ne se montre qu'à la SOURIS : au doigt,
   il vit au glissé et dans la fiche, et la ligne garde un seul mot */
.act-sec{display:none}
@media (pointer:fine){ .act-sec{display:inline-flex} }
.btn.icon-btn{padding:0}
/* actions d'une ligne : des icônes nues, jamais des boutons — au survol,
   au focus ou au tap, SEULE LA COULEUR change. Aucun aplat de fond,
   aucun biseau : le carré derrière l'icône était le bruit à retirer. */
.abtn{
  /* 44 px au pouce, 32 px à la souris — comme tout contrôle de l'app
     (`--ctl`). C'était figé à 44 partout : dans une colonne de 300 px du
     tableau « Aujourd'hui », trois icônes mangeaient la moitié de la
     largeur pour du mobilier qui ne varie jamais. */
  width:var(--ctl);height:var(--ctl);
  display:inline-flex;align-items:center;justify-content:center;
  background:none;border:0;padding:0;color:var(--text-muted);border-radius:var(--radius-1);
}
.abtn:focus-visible, .abtn:active{color:var(--text-body)}
@media (hover:hover){
.abtn:hover{color:var(--text-body)}
}
.act-gone{transition:opacity 150ms var(--ease-out),transform 150ms var(--ease-out);opacity:0;transform:translateX(20px)}

/* ============ ÉTATS VIDES ============ */
.td-empty{
  margin-top:20px;background:var(--surface-window);border:1px solid var(--border-strong);
  box-shadow:var(--shadow-raised);padding:26px 18px;text-align:center;
}
.tde-ic{
  display:inline-flex;align-items:center;justify-content:center;width:44px;height:44px;
  border:1px solid var(--border-strong);background:var(--teal-wash);color:var(--accent);
  margin-bottom:12px;box-shadow:var(--shadow-raised);
}
.tde-ic.ok{background:var(--green-wash);color:var(--green)}
.td-empty h2{font-size:var(--text-16);font-weight:var(--weight-bold);letter-spacing:var(--tracking-tight);margin-bottom:5px}
.td-empty p{font-size:var(--text-13);color:var(--text-muted);line-height:1.6;max-width:340px;margin:0 auto 14px}
/* sans phrase sous le titre, les gestes gardent leur distance au titre */
.td-empty h2+.tde-actions{margin-top:14px}
/* les deux gestes d'un vide, TAILLÉS À LEUR MOT et côte à côte — au
   pouce ils s'empilaient sur 280 px chacun, deux barres pour trois mots */
.tde-actions{display:flex;flex-wrap:wrap;justify-content:center;gap:8px;margin:0 auto}
/* PREMIERS PAS (lot F4) : une liste ordonnée dans UN cadre, des rangées
   sans relief séparées d'un pointillé (§6, « Choisir parmi 2-5 options »).
   Ce qui reste à faire se tape, avec son chevron ; ce qui est fait se
   coche en accent et passe en encre sourde — l'encre va à ce qui reste. */
.pp-liste{margin:0;padding:0;list-style:none;border:1px solid var(--border-frame);background:var(--surface-field)}
.pp-liste li + li{border-top:1px dashed var(--border-soft)}
.pp-et{display:flex;align-items:center;gap:10px;width:100%;min-height:var(--ctl);padding:4px 12px;
  background:none;border:0;border-radius:0;font:inherit;font-size:var(--text-14);color:var(--text-body);text-align:left}
.pp-l{flex:1;min-width:0;overflow-wrap:anywhere}
/* ce qui manque au profil : une donnée sous le pas, en encre sourde */
.pp-s{display:block;margin-top:1px;font-size:var(--text-12);color:var(--text-muted)}
button.pp-et{cursor:pointer}
button.pp-et:active{background:var(--surface-raised)}
@media (hover:hover){ button.pp-et:hover{background:var(--surface-raised)} }
.pp-et:not(button){color:var(--text-muted)}
.pp-fait .ic{color:var(--accent)}
.td-empty .pp-liste{max-width:340px;margin:16px auto 0;text-align:left}
.td-empty .pp-liste + .tde-actions{margin-top:12px}
.tr-pas .pp-liste{margin-top:6px}
.empty-list{padding:26px 14px;text-align:center;color:var(--text-muted);font-size:var(--text-13);line-height:1.6}

/* ============ SUPPRESSION AU GESTE (motif unique) ============
   glisser vers la gauche (tactile) / poubelle au survol (desktop) —
   toujours suivie d'un « Annuler » ~30 s, jamais de confirmation */
.sw{position:relative;overflow:hidden;touch-action:pan-y}
.sw .sw-in{position:relative;background:var(--surface-window);transition:transform 60ms linear}
.sw-under{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:flex-end;gap:6px;
  padding:0 16px;background:var(--red-wash);color:var(--red);
  font-weight:var(--weight-bold);font-size:var(--text-13);opacity:0;
}
.swipe-del .sw-under{opacity:1}
.sw-gone{transition:opacity 150ms var(--ease-out),transform 150ms var(--ease-out);opacity:0;transform:translateX(-20px)}
/* ---- L'ANNEAU DE FOCUS D'UNE RANGÉE QUI SE ROGNE ----
   `overflow:hidden` est ce qui garde la poubelle cachée sous la rangée
   pendant le glissement. Mais un `outline` se dessine À L'EXTÉRIEUR du
   contrôle (`--focus-offset`), c'est-à-dire exactement là où ce rognage
   tombe — et le contrôle qui OUVRE la rangée la remplit bord à bord.
   Mesuré sur les 150 contrôles de l'app, avant / après focus, en
   comptant les pixels qui changent : la ligne du fil ne rendait RIEN au
   doigt (0 % de l'aire exigée, contraste 0), la ligne d'une piste un
   quart, la rangée d'« Aujourd'hui » un huitième. Ce quart n'était même
   pas un cadre : les deux bords verticaux survivaient, le haut et le bas
   étaient coupés — deux traits qui ne disent rien.
   C'est la RANGÉE qui porte l'anneau, donc : `overflow` rogne les
   descendants d'un élément, jamais son propre contour. Le contrôle rend
   le sien pour qu'il n'y en ait pas deux.
   Et le contrôle concerné se NOMME (`sw-cible`) : sans nom, la règle
   devrait désigner `.ri-main`, `.act-main` et `.ec-row` une par une,
   c'est-à-dire une liste tenue à la main — et une liste tenue à la main
   finit toujours par mentir (§9).
   La rangée, elle, ne se nomme pas : elle se DÉDUIT. Le motif est
   toujours `rangée > enveloppe > contrôle` — `.sw-in` pour une piste et
   pour le fil, `.act-in` pour « Aujourd'hui » —, donc la rangée est le
   GRAND-PARENT, et `:has(> * > …)` la désigne sans nommer aucune des
   deux enveloppes. Une première version disait `.sw:has(…)` : elle
   ratait « Aujourd'hui », dont la rangée porte son propre geste (Fait /
   Reporter) et n'a donc jamais reçu la classe `sw`. Deux rangées sur
   trois passaient de « rogné » à « rien du tout ».
   Le garde de `e2e-ux-audit.mjs` mesure l'aire réellement rendue : une
   rangée neuve qui oublie le nom, ou qui le pose à une autre
   profondeur, rougit sans que personne ait à y penser. */
.sw-cible:focus-visible{outline:none}
/* LE TITRE D'UN ÉCRAN REÇOIT LE FOCUS, PAS UN CONTOUR. Il le reçoit pour
   être ANNONCÉ au changement d'onglet (app.js) ; ce n'est pas un
   contrôle, rien ne s'y tape. Chromium allume pourtant `:focus-visible`
   sur un focus posé par le programme quand aucun doigt ni aucune souris
   n'a encore servi — un retour arrière, un lien ouvert — et l'audit
   d'octobre 2026 l'a photographié en pointillé autour de « Mes pistes ».
   WCAG 2.4.7 vise les composants qu'on actionne, pas les titres. */
.view h1[tabindex="-1"]:focus-visible{outline:none}
/* `:focus-within` d'abord : il ne change RIEN à ce que la règle
   désigne (un petit-enfant qui a le focus, c'est un focus dedans), mais
   c'est un drapeau, et il écarte d'un coup chaque élément qui ne contient
   pas le focus. Sans lui, la règle était essayée sur les 870 éléments de
   la page à chaque feuille ouverte — la plus chère de l'app après le
   toast (porte « Performance », statistiques de sélecteurs de Chromium). */
:focus-within:has(> * > .sw-cible:focus-visible){outline:var(--focus-outline);outline-offset:var(--focus-offset)}
/* poubelle au survol (souris) : un élément du flux, pas un survol
   flottant — la colonne est réservée, rien ne saute ni ne déborde. Nue
   comme toute action de ligne : ni cadre, ni biseau, ni ombre, ni aplat
   (seule la couleur bouge). Absente au doigt : là, c'est le glissement. */
.hov-del{
  display:none;flex:none;width:32px;height:32px;
  align-items:center;justify-content:center;align-self:center;
  background:none;border:0;padding:0;color:var(--text-muted);
  opacity:0;pointer-events:none;
}
@media (hover:hover) and (pointer:fine){ .hov-del{display:inline-flex} }
@media (hover:hover){
  .sw:hover .hov-del,.sw:focus-within .hov-del{opacity:1;pointer-events:auto}
}
.hov-del:focus-visible{color:var(--red)}
@media (hover:hover){
.hov-del:hover{color:var(--red)}
}

/* ============ LISTE DES PISTES ============ */
.rows{display:flex;flex-direction:column;gap:7px}
.row-item{
  background:var(--surface-window);border:1px solid var(--border-strong);border-radius:var(--radius-0);
  box-shadow:var(--shadow-raised);cursor:pointer;
}
/* le rembourrage vertical appartient à la ZONE QUI OUVRE, pas au
   conteneur : posé sur .sw-in, les 9 px du haut et du bas ne
   réagissaient pas au tap alors que la ligne, elle, faisait bien 44 px */
.row-item .sw-in{
  display:flex;align-items:center;gap:10px;min-height:var(--control-h-touch);padding:0 12px;
}
@media (hover:hover){
.row-item:hover .sw-in{background:var(--surface-field)}
}
.row-item .dotc{width:8px;height:8px;border-radius:0;border:1px solid var(--border-strong);flex:none}
/* la zone qui ouvre la fiche prend TOUTE la hauteur de la ligne : à
   35 px, les quelques pixels de marge haut et bas ne réagissaient pas,
   alors que la ligne, elle, en fait bien 44 */
.row-item .ri-main{
  flex:1;min-width:0;align-self:stretch;padding:9px 0;
  display:flex;flex-direction:column;justify-content:center;
}
/* LE NOM D'UNE PISTE NE SE COUPE PAS — il plie.
   Mesuré à police doublée (WCAG 1.4.4, le droit d'agrandir le texte de
   200 % sans rien perdre) : « Cyberprotect Solutions Aquitaine » rendait
   « Cyberprotect Sol… », et une piste qu'on ne reconnaît plus ne sert à
   rien. Deux lignes au lieu d'une élision : à taille normale RIEN ne
   change — mesuré, la ligne fait 72 px dans les deux cas, aucun nom
   n'atteint le second rang — et à police doublée les quatre noms
   redeviennent entiers. C'est la sous-ligne, qui porte des données et
   non l'identité, qui garde le droit de s'élider.

   TROIS RANGS, ET PAS DEUX. Deux ne suffisaient pas : mesuré à 200 %
   sur un 320 px, « Thales Digital Identity and Security » perdait
   encore 37 px — son dernier rang. Et le troisième rang ne coûte
   RIEN : à 100 % la ligne fait 55 px avec deux comme avec trois (aucun
   nom n'atteint le second rang), et à 200 % elle faisait déjà 162 px —
   le plafond coupait donc le nom DANS une boîte qui avait la place.
   Un quatrième rang, lui, n'ajoute plus rien : le plafond reste, il
   protège toujours la liste d'un nom qui s'emballe. */
.row-item h3{
  font-size:var(--text-14);font-weight:var(--weight-semibold);
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;
}
.row-item .ri-sub{
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);line-height:1.5;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}
.row-item .ri-sub b{color:var(--text-body);font-weight:var(--weight-semibold)}
.row-item .ri-sub .nw{white-space:nowrap}
/* ---- CE QUI A FAIT REMONTER LA LIGNE ----
   N'existe que pendant une recherche, et seulement quand la ligne ne
   montre pas déjà la réponse : de l'encre sur ce qui CHANGE, qui s'en
   va avec la recherche. Le mot trouvé prend le langage de la
   SÉLECTION (navy), jamais celui de l'urgence (teal, ambre, rouge) —
   l'app n'a qu'un seul langage d'urgence, et « tu as tapé ça » n'en
   est pas. Une ligne, jamais deux : un extrait qui pousse la liste
   coûterait plus qu'il n'explique. */
.ri-hit{
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);
  line-height:1.5;margin-top:2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* Le bloc de SÉLECTION, pas un fond teinté. Mesuré : `--navy-wash`
   sur le fond d'une ligne donnait 1,12:1 en clair et 1,03:1 en
   sombre — un décor invisible qui ne signalait rien. Le bloc navy est
   l'idiome 98 pour « voilà ce que tu as demandé », il se voit dans
   les deux thèmes, et il reste plus petit que le nom : la ligne se
   lit toujours dans le bon ordre. */
.ri-hit mark{
  background:var(--select-bg);color:var(--select-text);
  border-radius:0;padding:0 2px;
}
/* La raison qui a fait monter la ligne pendant une recherche — « Léa y
   a été en alternance », « prend des alternants » : le même accent que
   sur « Aujourd'hui » (`.act-vecu`), jamais un `mark-*`. Elle passe
   devant l'extrait, qui s'élide le premier. */
.ri-hit .ri-why{color:var(--accent);font-weight:var(--weight-semibold)}
/* « Sans « Lille » · 4 » : ce qu'on retrouverait, sous la phrase du vide */
.el-alt{display:flex;flex-wrap:wrap;justify-content:center;gap:8px;margin-top:12px}
.el-alt .fl-n{font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);margin-left:4px}
/* ---- UN ÉTAT QUI POUSSE À AGIR — le même langage partout ----
   L'échéance d'une piste, la copie qui manque : c'est la même question
   posée à l'utilisateur (« ça, ça réclame quelque chose »), donc la
   même marque, où qu'elle soit. Quatre crans, et l'échelle est
   MONOTONE : plus c'est urgent, plus c'est lourd — sinon « demain »,
   plus long et encadré, pèse plus que le court « –2 j » et l'œil part
   au mauvais endroit (mesuré, pas supposé). Seuls les deux crans qui
   réclament un geste maintenant portent un cadre ; le reste n'est que
   du texte, de plus en plus effacé. Ce qui ne réclame rien ne met
   RIEN — c'est le vide, en face, qui fait ressortir les autres. */
.mark{
  flex:none;font-family:var(--font-mono);font-size:var(--text-11);font-weight:var(--weight-semibold);
  border:1px solid transparent;border-radius:var(--radius-1);padding:2px 7px;white-space:nowrap;
  color:var(--text-muted);
}
.mark.mark-late{ color:var(--amber);  background:var(--amber-wash); border-color:var(--amber);
                 font-weight:var(--weight-bold) }
.mark.mark-now{  color:var(--accent); background:var(--teal-wash);  border-color:var(--accent) }
.mark.mark-soon{ color:var(--text-body) }
.mark.mark-far{  color:var(--text-muted) }
.row-closed{opacity:.72}
.tr-closed{margin-top:20px}

/* ============ CARTES DE PAGE ============ */
.pcard{
  background:var(--surface-window);border:1px solid var(--border-strong);border-radius:var(--radius-0);
  box-shadow:var(--shadow-raised);padding:14px 16px;margin-bottom:12px;
}
.pcard h3{font-size:var(--text-14);font-weight:var(--weight-bold);margin-bottom:4px;display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.pcard .pd{font-size:var(--text-12);color:var(--text-muted);line-height:1.55;margin-bottom:10px}
.pcard .pd:last-child{margin-bottom:0}

/* ============ GROUP BOX (feuille de propriétés 98) ============
   Le cadre fin dont la légende est posée DANS le trait. Les guidelines
   d'origine le disent « visually heavy, use sparingly » : il ne sert que
   là où une page rassemble plusieurs familles de réglages autour d'un
   objet — « Moi ». Ailleurs, la carte (`pcard`) reste la règle.
   Le trait a son propre ton (`--border-frame`) : en `--border-soft` il
   valait exactement les pointillés qu'il contient, et le cadre cessait
   de se lire comme un cadre — flagrant en thème sombre. */
.fset{
  border:1px solid var(--border-frame);border-radius:var(--radius-0);
  padding:2px 12px 10px;margin:0 0 12px;min-width:0;
}
.fset>legend{
  font-size:var(--text-11);font-weight:var(--weight-bold);
  text-transform:uppercase;letter-spacing:var(--tracking-wide);
  color:var(--text-muted);padding:0 6px;margin-left:-6px;
}
/* Une précision qui qualifie le cadre se pose DANS sa légende, mais nue :
   `tag-priv` y apportait sa bordure et sa trame, et le filet du cadre lui
   passait derrière — un jeton posé en travers du trait, qui se lisait
   comme un contrôle (§6 : bordure et fond appartiennent à ce qui se
   tape). Même encre que la légende, un cran plus clair, rien de plus. */
.lg-note{
  font-weight:var(--weight-regular);opacity:.8;
  display:inline-flex;align-items:center;gap:4px;margin-left:2px;
}
/* un cadre sans légende (l'écran porte déjà son titre) ne doit pas
   garder l'encoche que le navigateur réserve au `legend` */
.fset-plain{padding-top:6px}
.fset .pd{font-size:var(--text-12);color:var(--text-muted);line-height:1.55;margin:0}
/* le cadre entier prend le bord ambre quand l'état qu'il porte est celui
   qui peut TOUT coûter (aucune copie, sans serveur derrière) — même
   signal que le bac « Contacts à rattacher », et seulement quand c'est
   vrai. Un cadre alerté en permanence ne serait plus une alerte. */
.fset.fs-alert{border-left:3px solid var(--amber)}

/* ============ EN-TÊTE D'OBJET ============
   « Put the object's name on the first page » + « display the appropriate
   icon in the upper-left corner » : la page « Moi » dit d'abord QUI, pas
   une carte « Mon profil » qui répète son propre bouton. */
.obj{display:flex;gap:12px;align-items:flex-start;margin-bottom:14px}
.obj>.ic{color:var(--text-muted);margin-top:3px}
.obj-m{flex:1;min-width:0}
.obj-n{display:block;font-size:var(--text-16);font-weight:var(--weight-bold);margin-bottom:3px}
.obj-s{
  font-family:var(--font-mono);font-size:var(--text-12);color:var(--text-muted);
  line-height:1.6;min-width:0;
}
/* Une donnée se coupe à sa fin, elle ne se brise pas au milieu : une
   adresse rendue « …@example » sur une ligne et « .fr » sur la suivante
   n'est plus une adresse, c'est un accident. Les points de suspension
   disent qu'il en reste, et « Modifier » la montre entière. */
.obj-l{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Ce qui MANQUE au profil, en creux : la même encre que les données (un
   gris plus clair tomberait sous 4,5:1, WCAG 1.4.3), un pointillé et un
   point d'interrogation pour dire « à remplir ». Jamais de couleur :
   ça ne réclame rien d'urgent (§6, `mark-*` reste à ce qui presse). */
/* Contrairement aux données, le creux se REPLIE au lieu de s'élider :
   « formation ? · école ? · stage ou alternance ? · email ? », chaque
   manque est une tâche, et celle qu'on couperait serait oubliée. */
.obj-creux{white-space:normal;text-decoration:underline dotted;text-underline-offset:3px}
.obj>.btn{flex:none}
/* sans nom, l'objet n'a plus que son geste : l'icône se centre sur lui */
.obj-vide{align-items:center}
.obj-vide>.ic{margin-top:0}
/* « Moi » rempli : le nom face à son bouton, les données dessous sur
   toute la largeur. Une grille, pas un flottant : les lignes élidées
   (`overflow:hidden`) se rétréciraient à côté du flottant au lieu de
   passer dessous. Deux colonnes seulement pour les données — l'icône
   garde la sienne, l'alignement des textes ne bouge pas. */
.obj-moi{display:grid;grid-template-columns:auto minmax(0,1fr) auto;column-gap:12px;row-gap:2px;align-items:center}
.obj-moi>.obj-s{grid-column:2 / -1}
.obj-moi .obj-n{margin-bottom:0;overflow-wrap:break-word;hyphens:auto}   /* comme la fiche : un nom plie, il ne passe pas sous « Modifier » (« Maheydine » à 200 % en 320 px) */
/* les actions d'une carte se partagent la largeur : deux boutons de
   libellés inégaux ne peuvent plus finir de tailles différentes. Une
   commande à icône garde la sienne. Le plafond évite l'inverse — un
   bouton d'un seul mot étiré sur 700 px de carte, sur l'ordinateur. */
.pc-actions{display:flex;gap:8px;flex-wrap:wrap}
.pc-actions>.btn{flex:1 1 0;min-width:0;max-width:240px}
.pc-actions>.icon-btn{flex:0 0 auto}
/* La flèche du pli est un ÉLÉMENT DE LA LIGNE, pas un signe posé devant :
   en `inline-block` elle restait seule sur la première ligne dès que le
   titre pliait en deux (vu sur « Les deux relances… » à 390 px). */
details.pcard-details>summary{
  cursor:pointer;list-style:none;user-select:none;margin:-4px 0;
  /* déplier est un geste comme un autre : la ligne fait 44 px au pouce,
     pas la hauteur de son titre (19 px mesurés sur « Connexion avancée ») */
  min-height:var(--ctl);
  display:flex;align-items:center;gap:8px;
}
details.pcard-details>summary::-webkit-details-marker{display:none}
details.pcard-details>summary h3{margin-bottom:0;flex:1;min-width:0}
details.pcard-details>summary::before{
  content:'';flex:none;width:0;height:0;
  border-left:5px solid var(--accent);border-top:4px solid transparent;border-bottom:4px solid transparent;
}
details.pcard-details[open]>summary::before{
  border-left:4px solid transparent;border-right:4px solid transparent;
  border-top:5px solid var(--accent);border-bottom:0;
}
details.pcard-details[open]>summary{margin-bottom:10px}
/* Un repli DANS une feuille : `pcard` a le fond de la fenêtre, un cadre
   et une ombre portée — sur le corps d'une feuille, qui a exactement le
   même fond, ça donnait une boîte flottante posée sur une boîte
   identique, puis des champs encadrés dedans. Trois niveaux de cadre
   pour une section. Ici le repli se CREUSE au lieu de se poser : le ton
   du chrome, pas d'ombre — il se lit comme une partie de la feuille. */
.modal-b details.pcard-details{
  background:var(--surface-chrome);box-shadow:none;
}
.modal-b details.pcard-details .hint{margin-top:5px}
.modal-b details.pcard-details .pc-actions{margin-top:12px}
.modal-b details.pcard-details .linklike{margin-top:10px}
/* CV & lettres : la carte ne porte que deux portes — « CV » et
   « Lettres » — donc elle ne grandit plus avec le nombre de documents ;
   la liste, l'ajout et les gestes vivent dans la feuille du type. */
.doc-vide{text-align:center;padding:26px 8px;font-size:var(--text-13);color:var(--text-muted)}
.doc-row{border-bottom:1px dashed var(--border-soft)}
.doc-row:last-of-type{border-bottom:0}
.doc-row .sw-in{display:flex;align-items:center;gap:8px}
/* la ligne EST le bouton : elle prend toute la hauteur de contrôle
   (44 px au pouce, 32 px à la souris) sans cesser d'être du texte —
   sinon l'ellipse ne fonctionnerait plus */
.doc-name{
  flex:1;min-width:0;font-size:var(--text-13);font-weight:var(--weight-semibold);
  line-height:20px;cursor:pointer;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  padding:calc((var(--ctl) - 20px) / 2) 0;
}
.doc-size{flex:none;font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted)}
/* ---- le mot de passe facultatif : une ligne, deux états (ui/dom.js) ----
   Au repos : [ Télécharger ·········· ][ 🔒 Chiffrer ] — l'action tient
   la ligne, le cadenas reste compact à son bout. Tapé, ils ÉCHANGENT
   leur place : le cadenas s'étire en champ, l'action se serre à son mot,
   et la ligne garde sa hauteur, donc rien ne pousse ce qui est dessous.
   La place que tient l'action est celle que le champ prendra : c'est ce
   qui fait lire le geste comme un échange, pas comme un bouton étiré.
   Taillée à son mot (6.36.0), la ligne avait un trou à droite et
   l'ouverture ne se lisait plus — retiré le jour même, à la demande du
   mainteneur (« j'aimais bien comment c'était »).

   L'étirement se joue sur `flex-grow`, pas sur `width` : une largeur en
   `auto` ne s'anime pas, alors qu'un `flex-grow` de 0 à 1 se transitionne
   et laisse le navigateur répartir la place restante. Les deux enfants
   changent en même temps, ce qui suffit à faire lire le geste comme un
   échange de place et non comme deux animations. */
.lockrow{display:flex;align-items:center;gap:8px;min-width:0}
/* FERMÉE, à texte doublé sur un petit téléphone, « Télécharger » et
   « Chiffrer » ne tiennent plus côte à côte : le second passe dessous
   plutôt que de raboter le premier (WCAG 1.4.10). À taille normale rien
   ne se replie, et OUVERTE la saisie reste dans la ligne (la ligne ne
   change pas de hauteur — e2e-ux-audit, F10) */
.lockrow:not(.on){flex-wrap:wrap}
.lr-do{flex:1 1 auto;min-width:0;transition:flex-grow var(--dur-3) var(--ease-out)}
.lockrow.on .lr-do{flex-grow:0}
/* fermé, le groupe est un bouton ; ouvert, il devient LE champ — c'est
   lui qui porte le cadre, et le cadenas passe à plat dedans, où il sert
   d'icône et de bouton pour refermer */
.lr-pw{
  display:flex;align-items:center;flex:0 1 auto;min-width:0;
  border:1px solid transparent;border-radius:var(--radius-1);
  transition:flex-grow var(--dur-3) var(--ease-out);
}
.lockrow.on .lr-pw{
  flex:1 1 auto;
  background:var(--surface-field);border-color:var(--border-field);
  box-shadow:var(--bevel-field);
}
.lr-lock{flex:0 0 auto;display:inline-flex;align-items:center;gap:7px}
.lockrow.on .lr-lock{
  background:none;border-color:transparent;box-shadow:none;
  color:var(--accent);padding-right:2px;
}
/* le mot cède la place à la saisie : c'est le seul endroit où il part,
   et il part parce que le champ dit désormais la même chose */
.lr-w{
  display:inline-block;overflow:hidden;white-space:nowrap;max-width:9em;
  transition:max-width var(--dur-3) var(--ease-out),opacity var(--dur-2) var(--ease-out);
}
.lockrow.on .lr-w{max-width:0;opacity:0}
.lr-pass{
  flex:0 1 0;min-width:0;width:0;overflow:hidden;
  height:var(--ctl);padding:0;border:0;background:none;opacity:0;
  font-size:var(--input-fs);color:var(--text-body);
  transition:flex-grow var(--dur-3) var(--ease-out),opacity var(--dur-2) var(--ease-out);
}
.lockrow.on .lr-pass{flex:1 1 0;width:auto;padding:0 10px 0 2px;opacity:1}
.lr-pass::placeholder{color:var(--text-muted)}
.lr-pass:focus{outline:none}
.lockrow.on .lr-pw:focus-within{outline:2px solid var(--accent);outline-offset:1px}
@media (prefers-reduced-motion:reduce){
  .lr-do,.lr-pw,.lr-pass,.lr-w{transition:none}
}
/* « Moi » éclaté (#20) : colonnes desktop, porte Réglages mobile */
/* La version, discrète et toujours là — c'est une donnée, pas un titre.
   Mesuré : « Moi » rempli n'occupe que 456 px sur 745, et la ligne de
   version tombait à 60 % de la hauteur au pouce, 50 % au poste — c'est-
   à-dire au MILIEU d'un vide que personne ne possédait. Un pied de page
   posé là ne se lit pas comme du calme, il se lit comme un oubli.
   La page prend donc sa région (§5-2) et la version descend au pied. */
#view-moi .page-inner{display:flex;flex-direction:column;min-height:100%}
/* `--text-muted` et pas `--text-disabled` : mesuré 2,43:1 en clair et
   3,54:1 en sombre pour du texte de 11 px, sous le plancher AA de 4,5.
   C'était le SEUL nœud de texte de l'application sous le seuil, sur les
   quatre écrans et dans les deux thèmes — et « c'est décoratif » n'est
   pas une exception que WCAG accorde à du texte. Il passe à 5,4:1 et
   7,0:1 sans changer de rôle : il reste la donnée la plus discrète de
   la page, simplement lisible. */
.moi-ver{
  margin:18px 0 0;text-align:center;
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);
}
.moi-ver{margin-top:auto;padding-top:18px}
/* Au poste, la barre d'état porte déjà « OpenContact 6.15.0 », en
   permanence et en bas : le répéter dans la page dit deux fois la même
   chose, et la seconde fois au mauvais endroit. */
@media (min-width:901px){ .moi-ver{display:none} }
.moi-cols{display:grid;grid-template-columns:1fr 1fr;gap:14px;align-items:start}
.moi-cols .pcard:last-child,.moi-cols .fset:last-child{margin-bottom:0}
/* au pouce, « Réglages » est une porte seule sous les cadres : elle
   s'aligne sur leur contenu (même retrait) sans porter de cadre à elle */
.rg-row.moi-rg{padding:0 13px}   /* 1px de cadre + 12px de retrait : le nom tombe pile sous « Modèles d'emails » */
/* l'état « sans copie », au-dessus du bouton qui le règle */
.copie-etat{display:flex;align-items:flex-start;gap:6px;margin:2px 0 8px}
.copie-etat .ic{flex:none;margin-top:1px}
/* « Ma copie », en tête des Réglages (décision 0009) : la ligne du
   fichier, puis la phrase de secours ; quand la copie manque vraiment,
   le groupe prend le bord ambre que §6 réserve à ce qui peut tout coûter */
.rg-copie .lockrow{margin:6px 0 2px}
.rg-copie.fs-alert{border-left:3px solid var(--amber);padding-left:9px}
/* « Installer l'app » : deux gestes, numérotés parce qu'ils se FONT dans
   cet ordre — c'est une séquence, le numéro dit quelque chose de vrai */
.inst-pas{margin:0;padding-left:22px;display:flex;flex-direction:column;gap:10px;
  font-size:var(--text-14);line-height:1.5}
.inst-pas b{font-weight:var(--weight-semibold);white-space:nowrap}
.inst-pas .ic{vertical-align:-2px}
/* « Depuis mes e-mails » : les mêmes pas, mais l'étape 1 est un bouton et
   l'étape 3 un champ — le geste se fait là où il est numéroté */
.mail-pas .field{margin:6px 0 0}
.mail-pas textarea{min-height:120px}
/* Réglages : une ligne = une porte (#7) — pictogramme, nom, état,
   chevron. Aucun bouton : c'est la ligne entière qui se tape, et le
   bord droit cesse d'être en dents de scie. */
.rg-row{
  display:flex;align-items:center;gap:10px;width:100%;min-height:var(--ctl);
  background:none;border:0;padding:0;font:inherit;color:inherit;text-align:left;cursor:pointer;
  border-bottom:1px dashed var(--border-soft);
}
.rg-row.rg-last{border-bottom:0}
/* une ligne qui attend qu'une AUTRE soit faite : elle s'efface au lieu
   de répéter l'ordre. Elle reste tapable — le tap mène au pré-requis. */
.rg-row.rg-dep .rg-n{font-weight:var(--weight-regular);color:var(--text-muted)}
/* le NOM ne descend jamais sous son mot le plus long (min-width:auto) :
   c'est l'ÉTAT qui s'élide pour lui faire place. À 200 % en 320 px,
   « Protection » passait sous « non protégé » (audit 7.1.0, §4 : ce qui
   porte une identité ne se coupe jamais, une donnée peut s'élider) */
.rg-n{
  flex:1;display:flex;align-items:center;gap:8px;
  font-size:var(--text-14);font-weight:var(--weight-semibold);
}
.rg-n .ic{flex:none;color:var(--text-muted)}
.rg-s{
  flex:0 1 auto;min-width:0;max-width:52%;font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.rg-row>.ic{flex:none;color:var(--text-muted)}
@media (hover:hover){
.rg-row:hover .rg-n{color:var(--text-link)}
}
/* les deux gestes de Réglages CONTINUENT la liste : mêmes rangées, pas
   de second cadre dans le premier. Seul l'effacement se détache, par
   l'air (`.pick-sortie`), en rouge et en dernier. */
.rg-foot{border-top:1px dashed var(--border-soft)}
/* les groupes des Réglages (28 octobre 2026) : le titre d'une section,
   la grammaire des légendes de cadre — un mot, pas une ligne qu'on tape */
.rg-sec{margin:14px 0 2px;font-size:var(--text-11);font-weight:var(--weight-bold);
  text-transform:uppercase;letter-spacing:var(--tracking-wide);color:var(--text-muted)}
.rg-sec:first-child,legend+.rg-sec{margin-top:4px}
/* « Signaler un problème » : le texte à coller se lit EN ENTIER avant
   de partir — c'est ce qui remplace la promesse « rien de personnel »
   par une preuve. Bloc mono, sélectionnable, jamais un champ : rien
   ne s'y saisit.
   Il REPLIE ses lignes au lieu de défiler : aligner les rapports pour
   les comparer est le confort du mainteneur, lire ce qu'on envoie est
   la raison d'être de l'écran — sur un 390 px, défiler cachait la
   moitié de chaque ligne. Ce qui part n'en dépend pas : la copie prend
   la chaîne, jamais le rendu. */
.diag{
  margin:10px 0 0;padding:10px 12px;
  background:var(--surface-field);border:1px solid var(--border-soft);
  font-family:var(--font-mono);font-size:var(--text-11);line-height:1.6;
  color:var(--text-body);user-select:text;
  white-space:pre-wrap;overflow-wrap:anywhere;
}

/* ============ FEUILLES (fenêtres système) ============ */
.overlay{
  position:fixed;inset:0;z-index:3000;display:flex;align-items:flex-end;justify-content:center;
  background:color-mix(in srgb, var(--ink) 20%, transparent);
}
.overlay::before{content:'';position:absolute;inset:0;background:var(--dither-strong)}
/* N8 : une feuille ouverte sur une autre remplace sa fenêtre (desktop) —
   la précédente attend ici, cachée, et revient à la fermeture */
.overlay.ov-behind{display:none}
.modal{
  position:relative;background:var(--surface-window);border:1px solid var(--border-strong);
  width:100%;max-width:560px;border-radius:var(--radius-0);
  box-shadow:var(--shadow-modal);
  max-height:92dvh;display:flex;flex-direction:column;
  transition:transform var(--dur-3) var(--ease-out);   /* retour du glisser */
}
@media (pointer:coarse){ .modal-h{touch-action:none} }
.modal-h{
  display:flex;align-items:center;gap:8px;flex:none;
  min-height:var(--titlebar-h);padding:3px 4px 3px 10px;
  background:var(--titlebar-accent-bg);color:var(--on-navy);
  border-bottom:1px solid var(--border-strong);user-select:none;
}
/* LE TITRE D'UNE FEUILLE SE LIT (audit, octobre 2026). Il était en
   police pixel de 8 px, capitales espacées : relevé sur les captures,
   « DONNER », « MON PROFIL », « NOUVELLE PISTE » faisaient 6 px de haut
   — et c'est le SEUL titre de la plupart des feuilles. Apple et
   Material placent le plancher d'un libellé à 11 pt/sp. Silkscreen
   n'existe qu'à 8, 16 et 24 px (§4) : on passe à sa taille native
   suivante, celle du logo — l'identité reste, la lecture revient. Un
   titre trop long s'élide par la fin, jamais aux deux bouts. */
.modal-h h2{
  flex:1;min-width:0;overflow:hidden;
  font-family:var(--font-pixel);font-size:var(--pixel-16);font-weight:400;
  text-transform:uppercase;letter-spacing:.02em;
  display:flex;align-items:center;gap:8px;
}
.modal-h h2 span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* `overflow-x:hidden` est une CEINTURE, pas une correction : aucune
   feuille de l'app ne défile latéralement par dessin. Si un jour un
   contenu déborde quand même — un moteur qui calcule autrement, un
   libellé qu'on n'avait pas prévu — la feuille ne se met pas à glisser
   sous le doigt, ce qui est le symptôme le plus déroutant qui soit :
   on croit avoir cassé l'application. La cause, elle, se corrige à sa
   source (voir `.grid2>*`). */
.modal-b{padding:16px 18px;overflow-y:auto;overflow-x:hidden}
/* L'HISTORIQUE garde la largeur que le fil avait dans la page. Ses
   colonnes (compte, date, chevron) sont réglées au pixel pour 292 à
   332 px (voir `.ec-n`) ; passé dans une feuille, il en perdait 10, et
   à 360 px une rangée sur six repartait sur deux étages selon la police
   — la date ne tenait plus sa colonne. Les rangées portent déjà leur
   propre marge intérieure (`.ec-row`), le texte reste à 18 px du bord. */
.modal-histo .modal-b{padding-left:13px;padding-right:13px}
.modal-f{
  padding:12px 18px calc(12px + env(safe-area-inset-bottom));
  border-top:1px solid var(--border-strong);background:var(--surface-chrome);
  display:flex;gap:8px;justify-content:flex-end;flex-wrap:wrap;flex:none;
}
.modal-f .btn{flex:1 1 40%}
/* un secondaire dans le pied se TAILLE À SON MOT (§6, les trois formes) :
   le primaire prend le reste — à parts égales, « Ajouter à mes pistes »
   pliait sur deux lignes à côté de « Pas pour moi » */
.modal-f .btn-sm{flex:0 0 auto}
.x{
  flex:none;min-width:var(--control-h-touch);min-height:var(--control-h-touch);
  padding:0;display:inline-flex;align-items:center;justify-content:center;
  background:var(--surface-chrome);color:var(--ink);
  border:1px solid var(--border-strong);border-radius:var(--radius-1);
  box-shadow:var(--bevel-up);font-size:13px;line-height:1;
}
.x:active{box-shadow:var(--bevel-down)}
/* l'anneau de la croix se pose sur la barre de titre, donc sur le navy :
   à l'encre, il y rendait 1,4:1 — invisible (WCAG 2.4.11, relevé le
   13 octobre 2026 en balayant pour la première fois une feuille). Il
   prend l'encre de la barre. */
.modal-h .x:focus-visible{outline-color:var(--on-navy)}
.cf-msg{font-size:var(--text-13);line-height:1.6;color:var(--text-muted)}
@media (min-width:901px){
  .overlay{align-items:center;padding:22px}
  .modal-f{padding-bottom:12px}
  .modal-f .btn{flex:0 0 auto}
}
/* LE CLAVIER NE COUVRE PAS LA FEUILLE (ui/zone-visible.js). Sur iPhone,
   le clavier ne rétrécit pas la page : tant qu'il est ouvert, la feuille
   se loge dans la zone qu'il laisse visible — sa barre de titre en haut,
   son pied juste au-dessus des touches — et c'est elle qui défile. Une
   feuille qui se donnait sa hauteur (« Écrire », un groupe, une
   conversation) prend celle de la zone. Le message GARDE son plancher :
   sans lui, le cadre descend sous son champ et la ligne d'aide se pose
   par-dessus le texte (relevé ici même, comme en 360 × 640) — c'est le
   corps de la feuille qui défile. */
.clavier-ouvert .overlay{top:var(--zone-top,0);bottom:auto;height:var(--zone-h,100%)}
.clavier-ouvert .modal{max-height:calc(var(--zone-h,100dvh) - 8px)}
.clavier-ouvert .modal-compose,.clavier-ouvert .modal-groupe{height:calc(var(--zone-h,100dvh) - 8px)}
.clavier-ouvert .modal-f{padding-bottom:12px}
/* À la souris, la croix appartient au CHÂSSIS : elle prend la hauteur de
   sa barre de titre, comme les boutons de légende d'une fenêtre de
   bureau. Elle se décidait sur la LARGEUR — une tablette tactile en
   paysage recevait donc 28 px sous le doigt. La taille suit la main. */
@media (min-width:901px) and (pointer:fine){
  .x{min-width:var(--titlebar-h);min-height:var(--titlebar-h);width:var(--titlebar-h);height:var(--titlebar-h)}
}
/* la barre de titre se prend à la souris — le curseur est le seul signe
   dont ce geste a besoin, et il ne coûte pas un pixel. Au doigt, la même
   barre sert à refermer d'un glissement : pas de curseur, pas de conflit. */
@media (pointer:fine){
  .modal-h{cursor:grab}
  .modal-h.mh-drag{cursor:grabbing}
  .modal-h button{cursor:pointer}
  .modal-confirm .modal-h{cursor:default}
}

/* champ de gabarit sans code (#17) : texte réel, jetons surlignés en
   douceur — jamais de {{...}} à l'écran */
.tpl-field{
  display:block;width:100%;min-height:130px;padding:9px 11px;
  background:var(--surface-field);border:1px solid var(--border-strong);
  border-radius:var(--radius-1);box-shadow:var(--bevel-field);
  font-family:var(--font-ui);font-size:var(--text-14);line-height:1.55;color:var(--text-body);
  white-space:pre-wrap;overflow-wrap:break-word;
}
.tpl-field:focus{outline:2px solid var(--accent);outline-offset:-1px}
.tpl-field.tpl-single{min-height:var(--ctl);white-space:nowrap;overflow-x:auto;padding-top:10px}
/* Les jetons se taisent au repos : un message doit se lire comme un
   message, pas comme du code au milieu d'une phrase. Ils ne se signalent
   qu'au survol, au toucher ou au clavier — là où on va les manipuler. */
.tvar{
  display:inline;background:none;color:inherit;
  border-bottom:1px solid color-mix(in srgb, var(--accent) 28%, transparent);
  border-radius:var(--radius-1);padding:0 1px;white-space:nowrap;cursor:default;
}
/* le repère ne se réveille qu'au moment où l'on vient toucher le jeton —
   pas quand on écrit à côté */
@media (hover:hover){
.tvar:hover{background:var(--teal-wash);color:var(--accent);border-bottom-color:var(--accent)}
}

/* une seule ligne d'aide à la place des neuf boutons d'insertion */
.tpl-insert{
  margin:4px 0 0;font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);
}
.tpl-insert b{color:var(--accent)}
/* la petite liste qui s'ouvre au « @ », sous le curseur */
.at-pop{
  position:fixed;z-index:3500;min-width:150px;max-width:200px;max-height:168px;overflow-y:auto;
  background:var(--surface-window);border:1px solid var(--border-strong);
  border-radius:var(--radius-1);box-shadow:var(--shadow-modal);
}
.at-pop button{
  display:block;width:100%;text-align:left;background:none;border:0;
  padding:8px 11px;font-family:var(--font-ui);font-size:var(--text-13);color:var(--text-body);
  border-bottom:1px dashed var(--border-soft);cursor:pointer;
}
.at-pop button:last-child{border-bottom:0}
.at-pop button.on{background:var(--teal-wash);color:var(--accent);font-weight:var(--weight-semibold)}
/* Prospecter (#17) : UNE piste = UN objet. Le cadre, le biseau et le
   fond appartiennent au duo ; ce qu'il contient — la case à cocher et,
   s'il y a un choix, la personne visée — n'a plus de bord à soi. Deux
   boîtes empilées faisaient lire six objets pour trois pistes, et la
   seconde, blanche et plus haute que la première, tirait l'œil vers le
   détail au lieu de la piste. */
.pk-duo{
  display:flex;flex-direction:column;gap:0;
  border:1px solid var(--border-strong);border-radius:var(--radius-1);
  background:var(--surface-chrome);box-shadow:var(--bevel-up), var(--shadow-raised);
  overflow:hidden;
}
.pk-duo .pick{margin:0;border:0;border-radius:0;box-shadow:none;background:none}
.pk-duo .pick:active{box-shadow:none;transform:none}
.pk-duo .pick:active .pk-m{transform:translate(1px,1px)}
/* la personne visée : une commande, mais SUBORDONNÉE — un filet
   pointillé pour la frontière (l'idiome des lignes voisines partout
   ailleurs), le retrait qui la range sous le nom, et `--ctl` parce
   qu'elle s'ouvre vraiment (44 px au pouce, 32 px à la souris). */
.pk-who{
  display:flex;align-items:center;gap:5px;min-height:var(--ctl);
  padding:2px 12px 2px 46px;margin-top:-2px;
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-link);
  text-align:left;cursor:pointer;background:none;border:0;
  border-top:1px dashed var(--border-soft);
}
.pk-who>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pk-who>.ic{flex:none;color:var(--text-muted)}
@media (hover:hover){
.pk-who:hover{color:var(--text-link);text-decoration:underline}
}
.pk-who:active{box-shadow:var(--bevel-down)}
.pk-who.pk-add{color:var(--accent);padding-left:40px}

/* la ligne « joindre CV / LM » du composeur (#16) — discrète, au-dessus
   de l'envoi ; absente sans messagerie connectée */
.attach-line{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:2px 0 8px}
.attach-line:empty{display:none}
.att-sel{
  display:inline-flex;align-items:center;gap:6px;
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);
}
.att-sel select{min-height:32px;padding:2px 8px;max-width:170px}

/* ============ FEUILLES D'ACTION ============ */
.na-company{
  display:flex;align-items:center;gap:8px;margin-bottom:14px;
  font-family:var(--font-mono);font-size:var(--text-12);font-weight:var(--weight-medium);color:var(--text-muted);
  /* un nom long PLIE ; une adresse, qui n'a pas d'espace, se coupe où il
     faut plutôt que de sortir de la feuille (texte à 200 %, 320 px) */
  overflow-wrap:anywhere;
}
.na-company::before{content:'';width:8px;height:8px;flex:none;border:1px solid var(--border-strong);background:var(--teal)}
/* ---- direct (P2P) ---- */
.sy-phrase{display:flex;justify-content:center;margin:6px 0 10px}
.sy-phrase span{
  font-family:var(--font-mono);font-size:20px;font-weight:var(--weight-bold);
  letter-spacing:.08em;padding:10px 16px;user-select:all;
  border:1px dashed var(--border-strong);background:var(--surface-chrome);
}
/* ---- relier sans retaper : le QR de la phrase, et le presse-papier ----
   Le QR reste PETIT. Il ne se scanne pas de loin comme celui d'un
   partage à un camarade : les deux appareils sont dans les mêmes mains,
   à trente centimètres. Lui donner la largeur d'une carte de partage en
   ferait l'objet dominant d'un écran dont le sujet est ailleurs — la
   liaison, son état, mes appareils. */
.sy-link{display:flex;flex-direction:column;align-items:center;gap:8px;margin:0 0 10px}
.qr-wrap.qr-mini{max-width:168px;padding:10px;margin:0}
@media (min-width:901px){ .qr-wrap.qr-mini{max-width:168px} }
.sy-status{
  display:flex;align-items:center;gap:8px;justify-content:center;margin-top:12px;
  font-family:var(--font-mono);font-size:var(--text-12);color:var(--text-muted);
}
/* les deux réglages du lien, au fond du volet : ils se lisent à gauche,
   à la suite, et celui qui COUPE porte le rouge — jamais l'inverse */
/* 8 px sous le résumé qui ouvre le volet : ouvrir les réglages et
   changer la phrase de liaison ne sont pas le même geste, et leurs
   bords se touchaient à 6 px (CLAUDE.md §5) */
.lk-set{margin-top:8px}
/* ---- OÙ J'EN SUIS, SANS LE LIRE ----
   Protéger ses données, c'est quatre écrans à la suite. Rien ne disait
   où l'on en était : on avançait dans un couloir sans savoir s'il en
   restait un ou cinq. Des traits pleins derrière, vides devant —
   « 98 » jusqu'au bout : deux états francs, aucun dégradé, aucun
   compte à lire. */
.pf-steps{display:flex;gap:5px;margin:0 0 14px}
.pf-steps span{
  width:26px;height:5px;flex:none;
  background:var(--surface-field);border:1px solid var(--border-strong);
}
.pf-steps span.on{background:var(--accent);border-color:var(--accent)}
/* le fichier produit rejoint l'inventaire de la copie : il est le seul
   fait NEUF de la liste, il porte donc la seule encre forte */
.cp-what .cp-file{border-color:var(--accent);color:var(--text-body)}
.cp-what .cp-file b{font-family:var(--font-mono);font-size:var(--text-12);word-break:break-all}
.sy-log{margin-top:10px}
/* ---- Échanger : ce qui réclame quelque chose ----
   La tranche de tête. Même grammaire que le fil qu'elle surplombe et
   que « En retard » sur « Aujourd'hui » : un libellé en petites
   capitales, son compte, puis des lignes au trait. Elle ne s'encadre
   pas — ce serait un objet de plus à lire, alors qu'il s'agit de la
   même chose qu'ailleurs : des pistes qui réclament un geste. */
.ec-repr{margin:0 0 20px}
.ec-rep{
  display:flex;align-items:center;gap:10px;width:100%;
  min-height:var(--ctl);padding:9px 0;
  border:0;border-bottom:1px dashed var(--border-soft);border-radius:0;
  background:none;color:var(--text-body);font-family:var(--font-ui);
  text-align:left;cursor:pointer;
}
.ec-rep:last-child{border-bottom-color:transparent}
@media (hover:hover){ .ec-rep:hover{background:var(--surface-field)} }
.rep-m{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
/* la TÊTE porte l'attribut distinctif — le nom de la piste, jamais le
   donneur : deux lignes voisines viennent souvent de la même personne
   (NN/g, « The Anatomy of a List Entry ») */
.rep-m>b{font-size:var(--text-13);font-weight:var(--weight-semibold);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rep-m>span{font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}


/* Le fil des échanges : ce que l'écran a enfin à montrer. C'est un
   PANNEAU — un cadre qui tient sa région, où les lignes se remplissent
   par le haut et dont la place restante lui appartient. Sans cadre, un
   journal court laissait un trou et un journal long jetait sa date à
   700 px de son libellé : c'est le bord qui fait qu'une liste se lit
   comme une liste. */
/* Le fil n'est PAS encadré. C'est une tranche — la même grammaire que
   « En retard » ou « Bientôt » : un libellé, son compte, puis les lignes.
   Rendu au trait, l'écran respire sur toute sa largeur au lieu de tenir
   dans une boîte, et le journal cesse d'être un objet de plus à lire. */
.ec-fil{margin-top:18px;display:flex;flex-direction:column}
/* le corps glisse, la légende reste — elle vit dans le trait du cadre */
.ec-body{flex:1;min-height:0;display:flex;flex-direction:column;overflow:auto;-webkit-overflow-scrolling:touch}
/* UNE LIGNE NE SE FAIT PAS ÉCRASER POUR TENIR DANS LE CADRE. Dans une
   colonne flex de hauteur bornée, un enfant garde `flex-shrink:1` : il
   RÉTRÉCIT sous sa propre hauteur de contenu, et `overflow:auto` sur le
   parent n'y change rien — le débordement n'existe plus, donc rien ne
   défile. Ici la ligne porte en plus `overflow:hidden` (le motif de
   suppression au geste), si bien que le texte comprimé se faisait
   raboter net : mesuré à 200 % en 360×640, une rangée de 88 px logée
   dans une ligne de 35 px, 44 px d'encre coupés par le trait pointillé.
   C'est le défaut photographié sur le téléphone du mainteneur.
   Les lignes gardent donc leur hauteur, et c'est le CORPS qui défile —
   ce que WCAG 1.4.10 demande d'ailleurs à texte agrandi. */
.ec-body>*{flex:none}
/* La phrase se pose EN HAUT de la région, là où commencerait la première
   ligne — pas au milieu. Centrée, elle tombait au centre optique de la
   page au poste : la seule encre de 640 px de haut, posée sur du vide, ce
   qui est exactement ce que la mesure appelle un écran raté. Un seul
   dessin pour les deux contextes : la région ne change pas d'usage entre
   le pouce et l'écran, seulement de hauteur. */
.ec-rien{
  font-size:var(--text-13);color:var(--text-muted);text-align:left;
  margin:0;padding:12px 2px 0;
}
/* Le panneau garde sa taille : c'est LUI qui défile quand le journal
   déborde, jamais la page. `height` et non `min-height` — sinon huit
   lignes repoussaient les gestes à 101 % de la hauteur sur un 360×640,
   hors de l'écran donc hors du pouce, exactement ce qu'on venait de
   corriger. Conséquence : « Donner » se pose au même endroit quel que
   soit le remplissage (63 / 72 / 74 % selon le téléphone), et la mémoire
   du geste tient. */
#view-reseau>.page-inner{display:flex;flex-direction:column;height:100%}
/* l'historique vit dans sa feuille : il n'a plus de place à tenir */
.modal .ec-fil{margin-top:0}
/* la date tient sa COLONNE (voir ui/echanger.js) : typo de donnée —
   mono, discrète — et surtout `flex:none`, elle ne rétrécit jamais.
   C'est la phrase qui s'élide ; une date coupée en deux ne dit plus
   rien, et des dates en escalier ne se balaient pas. */
.ec-when{flex:none;white-space:nowrap;font-family:var(--font-mono);
  font-size:var(--text-11);color:var(--text-muted)}
.ec-when b{color:var(--text-body);font-weight:var(--weight-semibold)}
@media (min-width:901px){
  /* le panneau prend TOUTE la hauteur de la vue : les gestes n'ont pas
     besoin de 1060 px, le fil si — et un cadre qui s'arrête à mi-écran
     laisserait sous lui le vide que le cadre servait à supprimer */
  /* LISTE-DÉTAIL. Deux étapes ont mené ici, et la seconde ne renie pas
     la première : on a d'abord SUPPRIMÉ une colonne qui ne montrait
     rien (340 px de large, 149 px de haut, 450 px de vide sous elle),
     puis on en a ajouté une qui montre une DONNÉE — les pistes que
     l'échange retenu a fait circuler. La règle n'a pas bougé : une
     colonne se mérite par ce qu'elle affiche.
     C'est le motif liste-détail (Material 3 « list-detail », Apple HIG
     « split view ») : on parcourt à gauche, on lit à droite, sans ouvrir
     ni perdre sa place. Et c'est la seule chose qui justifie DEUX
     dessins ici (§5) : au pouce on descend puis on remonte, au poste on
     garde les deux sous les yeux — un usage qui diffère, pas une taille.
     La barre d'actions passe au-dessus, sur toute la largeur : les trois
     canaux d'un échange tiennent sur une ligne au lieu de deux, et
     « Donner » cesse d'être un timbre de 165 px. */
  #view-reseau>.page-inner{max-width:1060px}
}
/* Le message tout prêt de « Demander à … » : il se LIT et se
   sélectionne, il ne se tape pas. `user-select:all` pour que le doigt
   qui ne trouve pas le bouton puisse quand même l'attraper d'un appui
   long. */
.gr-mot{
  margin:10px 0 0;padding:12px 14px;user-select:all;
  border-left:3px solid var(--accent);background:var(--teal-wash);
  font-size:var(--text-14);line-height:1.6;
}
/* Une déclaration qui porte un prénom MÈNE quelque part : elle se tape,
   prend la hauteur de cible et son chevron. Sans prénom, c'est la
   nôtre — elle reste du texte. */
button.fi-vecu{
  width:100%;min-height:var(--ctl);text-align:left;cursor:pointer;font:inherit;
}
button.fi-vecu b{flex:1;min-width:0}

.qr-prog,.scan-prog{
  margin-top:8px;text-align:center;
  font-family:var(--font-mono);font-size:var(--text-12);color:var(--text-muted);
}
.sy-devs{margin-top:16px}
/* une ligne = une phrase + sa date. Le canal, le nombre et la date
   s'empilaient en L sur deux rangs : trois blocs que l'œil devait
   recomposer avant de comprendre. */
/* La ligne MÈNE quelque part : elle est devenue une cible, donc 44 px
   au pouce comme toute cible de l'app. Deux lignes voisines ont le
   droit de se toucher — le trait pointillé dit la frontière, et se
   tromper d'un cran coûte un tap pour revenir (CLAUDE.md §5). Les
   anciennes entrées restent des <div> : même dessin, sans le chevron
   ni le survol, parce qu'elles n'ouvrent rien. */
/* le trait passe de la LIGNE à son cadre : c'est lui qui porte
   désormais le geste de suppression, et le contenu glisse à l'intérieur */
.ec-l{border-bottom:1px dashed var(--border-soft)}
.ec-l:last-of-type{border-bottom:0}
/* `.sw .sw-in` habille normalement une carte (`--surface-window`). Ici
   le fil est une TRANCHE, posée à même la page : lui donner un fond de
   carte le transformerait en liste de boîtes — l'inverse de ce qu'on
   cherche. Il prend donc le fond de la page, juste assez opaque pour
   couvrir la poubelle pendant le glissement. */
/* Et la ligne du fil EST une ligne. Sans `display:flex`, `.sw-in` reste
   un BLOC : la poubelle du geste de suppression se posait SOUS le texte,
   collée à gauche, et chaque échange occupait 65 px de haut dont la
   moitié pour un bouton invisible au repos. Mesuré : ligne 33 px, cadre
   65 px, poubelle à y+33 et x=0. Tous les autres porteurs du motif
   (`.row-item`, `.doc-row`, `.orow`) posent ce `flex` ; le fil l'avait
   oublié en naissant, et le trop-plein d'air se lisait comme un dessin
   raté avant qu'on en trouve la cause. */
/* les 5 px de droite sont ceux de la poubelle du survol : collée au
   bord de la rangée, son anneau de focus était rogné du même coup */
.ec-l .sw-in{display:flex;align-items:center;background:var(--surface-desktop);padding-right:5px}
.ec-row{
  /* 8 px et non 10 : à 320 px les quatre colonnes plus le chevron
     demandent 289 px pour 282 disponibles, et c'est le CHEVRON qui
     était chassé sur un second étage — toutes les rangées passaient de
     44 à 52 px sans qu'aucun texte ne se replie. Deux pixels par
     gouttière rendent les sept qui manquaient. */
  display:flex;align-items:center;flex-wrap:wrap;column-gap:8px;row-gap:2px;width:100%;
  min-height:var(--ctl);padding:9px 5px;
  border:0;border-radius:0;
  background:none;color:var(--text-body);font-family:var(--font-ui);text-align:left;
}
button.ec-row{cursor:pointer}
/* LE CHEVRON EST UNE AFFORDANCE, PAS DU CONTENU. Il héritait de
   l'encre de corps : huit glyphes sombres identiques, un par rang,
   aussi présents que les noms — la définition du papier peint (§6,
   règle 1). L'app a déjà tranché ailleurs (`.rg-row>.ic`) : sourd. */
.ec-row>.ic{color:var(--text-muted)}
@media (hover:hover){
button.ec-row:hover{background:var(--surface-field)}
}
/* ---- L'ANNEAU DE FOCUS DU FIL, LE SEUL CAS SANS DEHORS ----
   Ailleurs, une rangée rognée peut porter l'anneau à la place de son
   contrôle : son propre contour échappe à son propre `overflow`. Le fil
   n'a pas cette issue. Trois boîtes de MÊME largeur s'emboîtent —
   `.ec-row` remplit `.ec-l`, qui remplit `.ec-body`, qui défile — donc
   l'anneau posé dehors est rogné à chaque étage. Mesuré : 0 % de l'aire
   exigée au pouce. (Les 42 % relevés au poste ne venaient même pas de
   l'anneau : c'était la poubelle qui apparaît au `:focus-within`.)
   Il se pose donc DEDANS, et la rangée gagne les 5 px de rembourrage
   qui l'empêchent de mordre le « R » de « Reçu ». C'est le seul endroit
   de l'app qui déroge à `--focus-offset`, et il est nommé.
   Et il se pose sur l'ENVELOPPE, pas sur la rangée. Posé sur `.ec-l`,
   il rendait la mesure au vert — 103 % — pendant que l'œil ne voyait
   RIEN : `.sw-in` est positionnée et opaque (c'est elle qui couvre la
   poubelle pendant le glissement), donc elle recouvrait l'anneau
   dessiné sous elle, et il ne restait que le mince liseré du bas.
   Un anneau intérieur se pose sur la boîte qui PEINT, sinon il passe
   dessous. C'est la limite de l'instrument, écrite ici pour qu'elle ne
   se repaie pas : la mesure propose, l'œil tranche (§6). */
.ec-l:has(.sw-cible:focus-visible){outline:none}
.ec-l:has(.sw-cible:focus-visible)>.sw-in{outline:var(--focus-outline);outline-offset:-2px}
/* la poubelle du fil pour la même raison : 32 px dans une rangée de 36,
   il lui reste 2 px de chaque côté. À `--focus-offset` (2 px d'écart +
   2 de trait) son anneau perdait le haut et le bas et se lisait comme
   deux crochets ; collé au bord, il tient tout entier. */
.ec-l .hov-del:focus-visible{outline-offset:0}
.ec-body .tr-more{margin-top:10px}

/* ---- LE MOUVEMENT DU FIL, ET SEULEMENT DEUX ----
   « Plus d'animation » n'est pas un objectif (§4) : le mouvement est
   pré-attentif, donc c'est le signal le plus cher de l'interface. Il ne
   répond qu'à trois questions, et les deux gestes d'ici répondent tous
   deux à la PREMIÈRE — d'où ça vient ?
   Rien d'autre ne bouge sur cet écran, exprès : ni le survol, ni la
   ligne retenue, ni les deux verbes. `transform` et `opacity`
   uniquement, et `prefers-reduced-motion` coupe tout depuis le socle. */
@keyframes ec-deplie{ from{opacity:0;transform:translateY(-5px)} to{opacity:1;transform:none} }
/* les lignes révélées par « Voir les N autres » : elles se déplient vers
   le bas, dans le sens où la liste s'allonge */
.ec-neuve{animation:ec-deplie var(--dur-3) var(--ease-out) both}

@keyframes ec-arrive{ from{opacity:0;transform:translateX(-6px)} to{opacity:1;transform:none} }
/* la conversation, au poste : elle arrive de la gauche, du côté de la
   liste qui vient de la demander (`.bx-volet>.dock`). Court — c'est une
   apparition discrète (`--dur-2`), pas un déplacement de fenêtre. */
/* le rappel de contexte en tête de la feuille d'un échange : de qui,
   par quel canal, quel jour — la ligne qu'on vient de taper */
.ec-quand{
  margin:0 0 10px;
  font-family:var(--font-mono);font-size:var(--text-12);color:var(--text-muted);
}
/* UN JOURNAL N'EST PAS UN TITRE. La ligne portait le même gras que le
   nom d'une piste (`--weight-semibold`), si bien que l'historique pesait
   autant que les deux gestes qu'il surplombe — alors qu'il raconte ce
   qui est DÉJÀ fait. Deux ou trois niveaux de hiérarchie au maximum
   (NN/g, poids visuel) : ici les gestes, puis le fil, puis la date. */
/* LA PHRASE PLIE, ELLE NE S'AMPUTE PAS. Elle était en `nowrap` +
   ellipse : à texte agrandi elle rendait « 4 pistes do… », « 24 pistes
   reç… » — on ne sait plus si c'est donné ou reçu, c'est-à-dire
   exactement ce que la ligne existe pour dire. §4 tranche : ce qui porte
   une IDENTITÉ plie (`-webkit-line-clamp` généreux), ce qui porte une
   DONNÉE s'élide. La date, elle, garde sa colonne — c'est la donnée.
   Ce que ça coûte, mesuré : à 100 % rien ne plie nulle part (44 px
   partout, le dessin ne bouge pas d'un pixel) ; à 125 % sur un 360 px
   les deux phrases les plus longues passent à deux lignes (62 px) ;
   au-delà, tout plie. C'est le bon échange — une ligne de deux rangs
   se lit, « 24 pistes reç… » ne se lit pas.

   Le nombre de lignes n'est PAS plafonné, et c'est délibéré : un
   `-webkit-line-clamp` a été essayé, il reproduisait la photo — un
   `-webkit-box` empêche la rangée de rendre sa vraie hauteur, la
   deuxième ligne se faisait couper par l'`overflow:hidden` du motif de
   suppression. On ne contraint pas une boîte qu'un parent coupe déjà.

   Et l'interligne se FIXE. Laissé à `normal`, la hauteur de boîte suit
   la police de si près que l'encre des jambages en dépasse ; avec
   `overflow:hidden` juste à côté, elle se fait raboter. Invisible en
   émulation, photographiée sur un vrai téléphone : le trait pointillé
   passait à travers les lettres. */
/* ---- LES TROIS VALEURS DE LA LIGNE, ET LEUR ENCRE ----
   Une seule valeur porte l'encre : AVEC QUI. C'est la seule qui varie
   vraiment d'une ligne à l'autre (Léa, le groupe, QR, fichier, Awa) —
   le sens ne prend que deux valeurs et le compte est une donnée. §6,
   règle 1 : l'encre va à ce qui change. Rendu ainsi, le fil offre un
   seul point sombre par rang, et la colonne des noms se balaie.
   Rien n'est en gras : un journal raconte ce qui est DÉJÀ fait, il ne
   doit pas peser autant que les deux gestes qu'il surplombe. */
.ec-dir{
  flex:none;line-height:1.35;
  /* LE SENS EST UNE DONNÉE, PAS UN TITRE — donc la typo de donnée de
     l'app : mono, 11 px, sourde, comme le compte et la date. À 13 px en
     Public Sans il faisait la taille du NOM et lui disputait le regard,
     alors qu'il ne prend que deux valeurs. Descendu ici, la rangée se
     lit comme ce qu'elle est : un nom, seul en encre de corps, entre
     trois colonnes de données. Et le mono aligne « Reçu » et « Donné »
     au caractère près, ce que la police de texte ne faisait pas. */
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);
  /* la colonne se cale sur le plus long des deux mots — « Donné ». En
     `rem`, donc elle suit la police du navigateur au lieu de coincer le
     texte agrandi contre le nom. */
  min-width:2.6rem;
}
.ec-avec{
  font-size:var(--text-13);color:var(--text-body);line-height:1.35;
  /* L'IDENTITÉ PLIE, ELLE NE S'AMPUTE PAS (§4). « Marie-Charlotte »
     coupé n'est plus personne ; c'est le compte, à côté, qui a le droit
     de disparaître. */
  white-space:normal;overflow-wrap:break-word;
}
.ec-n{
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);
  line-height:1.35;white-space:nowrap;
}
/* ---- LE PLANCHER, ET POURQUOI LA RANGÉE SE REPLIE ----
   Quatre valeurs sur un rang de 292 px ne tiennent pas à texte agrandi,
   et une colonne étranglée ne rétrécit pas : elle CASSE LE MOT. Mesuré
   à 320 px / 200 % avant ce plancher : « Léa » rendu L / é / a, une
   lettre par rang. Le nom est l'identité de la ligne — c'est même la
   seule raison pour laquelle on l'a mis en avant.

   Le remède est celui que §5 a déjà écrit pour « Aujourd'hui » et que
   `.act-in` / `.act-main` porte : conteneur repliable, et un PLANCHER
   EN REM sur la colonne du texte. En rem, il suit la police du
   navigateur tout seul — pas de média query, pas un réglage de plus.
   Sous le plancher, la rangée se replie et la date passe dessous
   (WCAG 1.4.10 : le contenu se réorganise, il ne se perd pas).

   6 rem est MESURÉ, pas choisi. Balayage de 0 à 6,5 rem sur 320 / 360 /
   390 px × 100 → 200 % : c'est la plus petite valeur qui ne casse plus
   un seul mot (5,5 en cassait encore à 320/200 % et 360/150 %), et elle
   ne rallonge aucun rang à 100 %. La même valeur tient au poste, où
   elle laisse 100 %, 125 % ET 150 % identiques au pixel près — sans
   elle, 1280 px à 200 % rendait des rangs de 159 px et cinq mots
   cassés.
   Ce qu'elle coûte, dit franchement : à 320 px et 125 % seulement, le
   rang moyen passe de 61 à 68 px, parce que la rangée se replie là où
   l'ancienne phrase tenait encore. C'est le prix d'un nom qui ne se
   coupe jamais et qui commence toujours au même x — et le panneau
   défile, donc rien ne se perd. */
/* LE COMPTE A SA COLONNE, COMME LA DATE — à toutes les largeurs.
   Ce dessin disait « au pouce : deux colonnes, pas quatre », le nom et
   le compte formant une phrase qui plie ensemble. La conséquence était
   inévitable et le mainteneur l'a vue avant moi : le compte SUIT le
   nom, donc son abscisse dépend de la longueur du nom. Mesuré sur un
   vrai fil, à 320, 360, 390 et 430 px : quatre abscisses différentes
   pour le compte, trois pour la date, et la ligne « partage en
   groupe » à deux étages sous 390 px.
   Or c'est précisément ce que NN/g (*The Anatomy of a List Entry*)
   interdit et que ce dépôt a déjà tranché pour la date : chaque
   information à la même place d'une ligne à l'autre, sinon on LIT au
   lieu de balayer. Ce qui valait pour la date vaut pour le compte.
   Le nom garde son droit de PLIER (§4 : une identité ne s'ampute
   jamais) — il plie dans SA colonne, et les deux colonnes de données
   ne bougent plus. Un canal trop long fait donc grandir sa seule
   rangée, il ne déplace plus rien. */
.ec-quoi{display:contents}
/* 6 REM, ET C'EST LE PLANCHER D'ORIGINE — repris tel quel après
   l'avoir cassé. En sortant le compte de la phrase j'ai descendu ce
   plancher à 3 rem, en mesurant la seule chose que je regardais : les
   colonnes tenaient, les rangs faisaient 44 px, le chevron restait sur
   son étage. Ce que 3 rem coûtait ne se voyait pas là — il se voyait
   dans le garde d'à côté, qui gardait autre chose : « Marie-Charlotte »
   se faisait CASSER AU MILIEU D'UN MOT à 320 et 360 px. §4 l'interdit
   sans réserve : ce qui porte une identité plie, il ne s'ampute jamais.
   Le balayage complet — planchers de 3 à 6 rem × largeurs 320/360/390/
   430 × 100 % et 125 % — ne laisse qu'une valeur propre partout, et
   c'est celle qui était déjà là.
   Ce que 6 rem coûte, dit franchement : à 320 px les rangées font 52 px
   au lieu de 44, parce que le nom se replie. Elles le font TOUTES, donc
   la liste garde son pas — c'est le prix d'un nom entier, et le panneau
   défile. */
.ec-row .ec-avec{flex:1;min-width:6rem}
/* UN PLANCHER, ET LA COLONNE DU COMPTE RESTE UNE LIMITE ASSUMÉE.
   La colonne fixe a été essayée, mesurée, et retirée : elle tient
   l'alignement mais elle ne RENTRE PAS. À 320 et 360 px, cinq colonnes
   plus le plancher de 6 rem du nom demandent plus que les 292 à 332 px
   disponibles, et c'est le CHEVRON qui part sur un second étage — seul,
   sous chaque rangée. Balayé de 5,75 à 6,5 rem de colonne, de 4,2 à
   4,8 rem de date et de 5 à 8 px de gouttière : aucune combinaison ne
   le sauve sous 390 px, ni à 125 %. C'est de l'arithmétique, pas du
   goût.
   Ce qui a vraiment réglé le défaut photographié, c'est `canalCourt` —
   le canal ne dit plus « partage en groupe » mais « groupe », donc il
   ne pousse plus rien. Ce qui reste, c'est le vocabulaire du compte
   lui-même (« rien de neuf », « 148 complétées ») qui varie de 8 à
   14 caractères.
   La limite est donc ASSUMÉE, pas ouverte, et `e2e-colonnes.mjs` la
   nomme avec sa raison. Elle se rediscute le jour où l'on tranche la
   vraie question, qui est de TEXTE et non de CSS : ce compte a-t-il
   besoin de répéter « pistes » à chaque rangée (§6, règle 1 — l'encre
   va à ce qui change) ? Raccourci, tout rentre. C'est une décision du
   mainteneur, pas un réglage. */
.ec-n{flex:none;text-align:right;min-width:3.9rem}
/* LA DATE PREND UNE LARGEUR FIXE, et c'est ce qui rend la colonne des
   comptes possible. Mesuré sans elle : « aujourd'hui » fait 72 px,
   « hier » 26 — le compte, posé juste avant, se faisait pousser de
   46 px selon la date de la ligne, et ses trois bords droits tombaient
   à 551, 597 et 557. Une colonne qui bouge d'une ligne à l'autre ne se
   balaie pas, c'est-à-dire qu'elle ne sert plus à rien.
   Le même réglage règle un second défaut du relevé : 10 px seulement
   séparaient deux colonnes de même gris et de même police, qui se
   lisaient comme un seul bloc. La date étant calée à droite dans une
   boîte fixe, l'écart se creuse tout seul là où la date est courte. */
.ec-when{min-width:4.8rem;text-align:right}
/* PAS DE POINT MÉDIAN ENTRE LE NOM ET LE COMPTE, et c'est une
   correction. Il vivait dans un `::before` sur le compte,
   précédé d'une espace : la coupure tombait
   sur cette espace, et le point partait à la ligne suivante EN TÊTE —
   « · 24 pistes », un signe de ponctuation orphelin ouvrant un rang.
   Photographié sur un vrai téléphone, et reproduit à 390 px / 125 %,
   c'est-à-dire le réglage courant de quelqu'un qui veut y voir.
   Le déplacer en fin de nom l'aurait sauvé de l'orphelinage mais l'aurait
   laissé pendre en bout de rang. Il ne sert simplement à rien : 13 px
   d'encre sombre en Public Sans contre 11 px de gris en mono, la
   séparation est déjà faite par la forme. Le poste s'en passait déjà —
   les deux surfaces disent maintenant la même chose de la même façon. */

/* ---- POURQUOI IL N'Y A PAS DE `subgrid` ICI ----
   Il y en a eu, et il a cassé l'app sur le téléphone du mainteneur :
   les rangées rendaient une tranche d'encre de quelques pixels, le
   trait pointillé passant à travers les lettres — le défaut vertical
   que §4 décrit, en pire, sur les dix lignes à la fois.
   La cause de la CAUSE est la seule qui compte : le mécanisme
   (`subgrid` sur trois niveaux + `container-type` + `@container`)
   n'avait été vérifié que sur Chromium, parce que c'est le seul moteur
   installé ici. Safari n'a jamais été mesuré une seule fois. Trois
   fonctionnalités récentes empilées sur le moteur qu'on ne peut pas
   tester, ce n'est pas de l'audace, c'est un pari — et §9 dit qu'on ne
   livre pas sur la foi d'une relecture.
   La règle qui en reste : **ce qui ne peut pas être mesuré sur le
   moteur de l'utilisateur se tient au socle le plus commun.** Les
   colonnes tiennent donc en flex, avec des largeurs qui ne grandissent
   pas — moins élégant, vérifiable partout. */


/* Ce média vient APRÈS les règles de base, et ce n'est pas un détail de
   rangement : posé plus haut dans le fichier — là où vit le reste du
   dessin de poste — il était écrasé par elles à spécificité égale, et
   le registre ne s'est jamais affiché. Une règle de surface se pose
   après ce qu'elle surcharge, ou elle ne surcharge rien. */
@media (min-width:901px){
  /* ---- AU POSTE, LA LIGNE DEVIENT UN REGISTRE ----
     Et c'est ici que les deux surfaces divergent vraiment, pour une
     raison d'USAGE, pas de taille (§5) : au pouce on LIT une ligne à la
     fois pour retrouver une chose ; au poste le fil est un NAVIGATEUR —
     on descend aux flèches et le contenu se lit à droite, donc on
     COMPARE des rangs entre eux.

     Ce que ça change, mesuré : la rangée fait 555 px et le texte s'y
     arrêtait à 249, laissant 306 px de vide au milieu — plus de la
     moitié du rang. La date ne peut pas revenir dans la phrase :
     l'annexe dit pourquoi (la ligne s'élidait d'un bloc et c'est la
     DATE qui se faisait couper). Mais ce vide peut se remplir de
     STRUCTURE plutôt que de rien : le compte quitte la phrase et prend
     sa propre colonne, alignée à droite.

     Elle est en IBM Plex Mono, et c'est ce qui la rend utile : l'unité
     ayant partout la même largeur, aligner les fins de cellule aligne
     les CHIFFRES. « 3 », « 24 » et « 100 » se comparent d'un coup d'œil
     au lieu de flotter au fil du texte — c'est exactement ce qu'un
     grand écran doit rendre et qu'un téléphone ne peut pas offrir. */
  /* LE MÉCANISME EST DESCENDU DANS LES RÈGLES DE BASE : les colonnes du
     compte et de la date valent maintenant à TOUTES les largeurs, parce
     que « chaque information à la même place d'une ligne à l'autre » ne
     dépend pas de la taille de l'écran. Ce qui reste ici est ce qui
     diffère vraiment — des colonnes plus GÉNÉREUSES, que seule la
     largeur du poste permet.
     (`.ec-avec` reste un enfant de `.ec-quoi` dans le DOM : c'est le
     RENDU que `display:contents` aplatit, pas l'arbre, donc le
     sélecteur enfant direct ne le verrait jamais.) */
  .ec-n{min-width:5.2rem}
}
.ec-row-m{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.ec-row-m b{font-size:var(--text-13);font-weight:var(--weight-semibold)}
.ec-row-m .tag-beta{margin-left:2px}
.ec-sub{font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted)}
.dev-row{
  display:flex;align-items:center;gap:8px;min-height:var(--ctl);
  padding:4px 0;border-bottom:1px dashed var(--border-soft);
}
.dev-row:last-of-type{border-bottom:0}
.dev-row b{font-size:var(--text-13);font-weight:var(--weight-semibold)}
.dev-sub{flex:1;font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted)}

/* ---- autocomplétion d'adresse ---- */
.ac-wrap{position:relative}
.ac-list{
  position:absolute;left:0;right:0;top:100%;z-index:6;
  display:flex;flex-direction:column;
  background:var(--surface-field);border:1px solid var(--border-strong);
  box-shadow:var(--shadow-raised);
}
.ac-item{
  text-align:left;padding:8px 10px;min-height:var(--ctl);
  font-family:var(--font-mono);font-size:var(--text-12);color:var(--text-body);
  border:0;background:none;cursor:pointer;
}
.ac-item:focus{background:var(--select-bg);color:var(--select-text)}
@media (hover:hover){
.ac-item:hover{background:var(--select-bg);color:var(--select-text)}
}
.ac-item+.ac-item{border-top:1px solid var(--border-soft)}
/* le nom d'une entreprise qui se propose : son nom en texte courant, la
   ville et la taille en donnée dessous — jamais un code (ui/nom-annuaire.js) */
.ac-nom{display:flex;flex-direction:column;gap:2px;justify-content:center}
.ac-nom b{font-family:var(--font-ui);font-size:var(--text-14);font-weight:var(--weight-semibold)}
.ac-nom span{font-size:var(--text-11);color:var(--text-muted)}
.ac-nom:focus span{color:inherit}
@media (hover:hover){ .ac-nom:hover span{color:inherit} }

/* LES INDICATEURS NATIFS PRENNENT LE DESSIN DE L'APP. Le ▼ d'un champ à
   propositions (`<input list>`) et l'agenda d'un champ de date sont
   dessinés par le navigateur — un triangle plein et un agenda noir,
   étrangers au jeu d'icônes (§4 : pixelarticons, pas d'autre pack).
   Relevé à l'audit d'octobre 2026 sur « Prochaine action ? ». Le même
   chevron que les listes déroulantes, le même agenda que les puces ; la
   teinte passe par le masque, donc suit le thème. Seul Chromium les
   dessine — Safari n'en montre aucun, il n'y a rien à rattraper. */
.field input[list]::-webkit-calendar-picker-indicator,
.field input[type="date"]::-webkit-calendar-picker-indicator,
.field input[type="month"]::-webkit-calendar-picker-indicator{
  -webkit-appearance:none;appearance:none;
  width:16px;height:16px;padding:0;margin:0 0 0 6px;opacity:1;cursor:pointer;
  background:var(--text-muted);color:transparent;
  -webkit-mask:url(../assets/icons/calendar.svg) no-repeat center / 16px 16px;
  mask:url(../assets/icons/calendar.svg) no-repeat center / 16px 16px;
}
.field input[list]::-webkit-calendar-picker-indicator{
  -webkit-mask-image:url(../assets/icons/chevron-down.svg);mask-image:url(../assets/icons/chevron-down.svg);
}
/* SUR IPHONE, UN CHAMP DE DATE ou DE MOIS A SA LARGEUR À LUI : Safari
   lui donne une largeur minimale, centre sa valeur, et `width:100%` ne
   l'en fait pas descendre. Photographié sur le téléphone du mainteneur
   (7 octobre 2026) : dans « Ajouter à mon parcours », « Fin » sortait de
   sa colonne et passait sous le bord de la feuille. Réservé à iOS
   (`-webkit-touch-callout` n'existe que là) : ailleurs le champ se
   tient, et l'apparence native garde son sélecteur. */
@supports (-webkit-touch-callout:none){
  .field input[type="date"],.field input[type="month"]{
    -webkit-appearance:none;appearance:none;display:block;min-width:0;max-width:100%;
  }
  .field input[type="date"]::-webkit-date-and-time-value,
  .field input[type="month"]::-webkit-date-and-time-value{text-align:left}
}
.date-row{display:flex;gap:8px;align-items:stretch}
.date-row input{flex:1;min-width:0}
.date-row .btn{flex:none}
.datechips{display:flex;gap:7px;flex-wrap:wrap}
/* UNE PUCE FAIT LA TAILLE DE SON MOT.
   Elle était en `flex:1 1 auto` : dès qu'une puce se retrouvait seule
   sur son rang, elle s'étirait sur toute la largeur. Mesuré à police
   agrandie — celle que règle quelqu'un qui veut y voir — « J'y suis
   passé » rendait quatre blocs pleine largeur empilés, 197 px d'écran
   pour quatre mots, et « CDI » devenait un bouton de 352 px. Un bouton
   large annonce une action lourde ; ce sont des étiquettes qu'on
   effleure. Elles se dimensionnent donc sur leur texte et se rangent
   par rang, comme des puces de filtre — le motif que l'app emploie
   déjà pour exactement ce geste. */
.dchip{
  flex:0 1 auto;min-height:var(--ctl);padding:0 12px;
  border:1px solid var(--border-strong);border-radius:var(--radius-1);
  /* un trait, pas de relief : Material dessine la puce de filtre à
     plat, l'élévation est pour une puce posée sur une image. Rangées
     par trois ou quatre, des puces biseautées se lisaient comme autant
     de gros boutons. Le relief revient à l'appui, enfoncé. */
  background:var(--surface-chrome);box-shadow:none;
  font-weight:var(--weight-semibold);font-size:var(--text-13);
  color:var(--text-body);   /* jamais la couleur de bouton du navigateur */
}
.dchip:active{box-shadow:var(--bevel-down)}
/* le raccourci de date porte le JOUR qu'il vise : « +3 jours » se
   calcule, « mer. 05/08 » se lit. Le nom reste la ligne qu'on vise. */
.dchip-d{
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;
  padding:5px 10px;min-height:var(--control-h-touch);line-height:1.2;
}
.dchip-d span{font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted)}
.dchip-d.on span{color:var(--on-accent);opacity:.8}
/* 8 px, le chiffre de la règle — et pas 7 — entre les FICHES d'une liste
   à cocher (`.pk`). Une liste d'ACTIONS, elle, est faite de rangées qui
   se touchent (plus bas) : §5-3 l'autorise pour une liste, le pointillé
   dit la frontière, et se tromper de rangée coûte un tap pour revenir. */
.pick-list{display:flex;flex-direction:column;gap:8px}
/* `color` EST OBLIGATOIRE ici. Sans elle, un `<button>` retombe sur la
   couleur de texte du navigateur : encre sous Chromium — donc invisible
   en développement — mais BLEU SYSTÈME sous iOS. Toutes les listes de
   choix de l'app s'affichaient en bleu Apple sur un vrai téléphone, ce
   que rien ne pouvait montrer ici.
   Et `flex-wrap` : quand la donnée de droite est longue (un nom de
   fichier de 28 caractères), elle écrasait le libellé au point de faire
   passer « Télécharger » SOUS sa propre icône. Elle descend d'une ligne
   au lieu de le broyer. */
.pick{
  display:flex;align-items:center;justify-content:space-between;gap:4px 10px;
  flex-wrap:wrap;min-height:var(--ctl);padding:8px 14px;
  border:1px solid var(--border-strong);border-radius:var(--radius-1);
  background:var(--surface-chrome);box-shadow:var(--bevel-up), var(--shadow-raised);
  font:inherit;font-size:var(--text-14);color:var(--text-body);text-align:left;
}
/* LE LIBELLÉ PREND LA PLACE, LA NOTE SUIT — et c'est lui qui plie quand
   il n'y en a plus. En `flex:none`, le libellé ne cédait rien : c'est la
   note qui décrochait, et `margin-left:auto` la jetait alors seule au
   bord droit d'un rang à elle. Mesuré à 320 px, à 125 % et à 200 % :
   « coller le texte » passait sous « Par message » pendant que ses deux
   sœurs gardaient la leur en ligne — trois lignes qui ne se lisaient
   plus pareil. C'est `flex-grow` qui tient la note à droite désormais,
   et la note ne décroche donc plus jamais. */
.pick b{font-weight:var(--weight-semibold);flex:1 1 auto;min-width:0}
/* `>` et pas un espace : la donnée de droite est un ENFANT DIRECT du
   bouton. Sans le chevron, la règle attrapait aussi la ligne d'état
   nichée dans `.pk-m` (« À contacter · ✉ Chloé »), que le `margin-left:auto`
   jetait à droite — les cartes de Prospecter se désalignaient les unes
   par rapport aux autres selon qu'elles portaient un contact ou non. */
.pick>span{
  font-family:var(--font-mono);font-size:var(--text-12);color:var(--text-muted);
  flex:none;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
/* `.pk-m` = le bloc « nom + son état » d'une ligne de liste. Il portait
   sa structure UNIQUEMENT sous `.pk` (les listes à cocher) : sous
   `.pick`, ses deux enfants coulaient en ligne comme du texte, et la
   ligne s'enroulait — « Capgemini À contacter · » puis « Valenciennes »
   à la ligne, le chevron chassé tout en bas d'une boîte deux fois trop
   haute, et des hauteurs de lignes toutes différentes selon la longueur
   du nom. Six listes vivaient avec ça (échanges, réception, don,
   prospection, « → qui », appareils). Le nom tient sa ligne, l'état la
   sienne, chacun se coupe à sa fin, et le chevron garde sa place. */
.pick .pk-m,.pk .pk-m{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px;text-align:left}
/* LA SOUS-LIGNE S'ÉLIDE : « statut · ville · qui est visé », des
   données — la ligne reste compréhensible sans sa fin (§4). */
.pick .pk-m>span,.pk .pk-m span{
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
/* LE NOM PLIE : c'est l'identité de la ligne, et dans une liste où
   l'on COCHE, un nom amputé n'est pas un défaut d'esthétique, c'est
   une erreur de décision — « Société Générale G… » ne se distingue
   plus de son homonyme. Il était en `nowrap` + ellipse, et se coupait
   dès 100 % sur un 320 px : l'état quotidien d'un petit téléphone, pas
   un cas limite. Trois rangs, la même valeur et la même raison que
   `.row-item h3` — l'app n'a qu'une règle pour ça.
   Ce que ça coûte, mesuré : à 390 px et au poste, RIEN (pas de 52 px
   inchangé, aucun nom n'atteint le second rang) ; en 320 et 360 px les
   seules lignes qui grandissent (52 → 71 px) sont celles dont le nom
   se faisait couper. Le pas ne se casse que là où de l'information se
   perdait. */
.pick .pk-m>b,.pk .pk-m b{
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;
  overflow-wrap:break-word;
}
.pick .pk-m span{font-family:var(--font-mono);font-size:var(--text-12);color:var(--text-muted)}
.pick:disabled{opacity:.62;cursor:not-allowed;box-shadow:none}
.pick:active{box-shadow:var(--bevel-down);transform:translate(1px,1px)}
/* ---- LES ACTIONS D'UNE FEUILLE SONT DES RANGÉES, PAS DES BLOCS ----
   Chaque option était un bouton complet — cadre, relief, ombre, 8 px
   d'air — et une feuille de cinq gestes devenait une pile de cinq
   briques pleine largeur. Le mainteneur l'a dit sans détour : « les
   boutons beaucoup trop gros » (30 septembre 2026). La taille d'un
   contrôle annonce son poids (Material 3 : l'emphase se dose, une seule
   action remplie par vue) ; cinq briques égales disent cinq actions
   lourdes, et aucune.
   Une liste d'actions prend donc la forme que l'app donne DÉJÀ à ses
   listes — « Réglages », « Modèles d'emails » : UN cadre, des rangées de
   hauteur de contrôle séparées d'un pointillé, sans relief. La rangée
   reste un `<button>` (un geste qui agit n'est jamais un lien) et garde
   sa cible de 44 px ; seul l'habit disparaît. Les rangées se touchent,
   et §5-3 l'autorise pour une liste : le pointillé dit la frontière.
   Les listes à cocher (`.pk`) ne changent pas — ce sont des fiches à
   deux étages, pas des gestes. Portée : les FEUILLES, et les gestes qui
   continuent la liste de Réglages ; le panneau d'« Échanger » au poste
   garde sa propre grammaire de tranche (plus haut). */
.modal-b .pick-list:not(:has(.pk)){
  gap:0;padding:0 12px;
  background:var(--surface-window);border:1px solid var(--border-strong);
  border-radius:var(--radius-0);box-shadow:var(--shadow-raised);
}
.rg-foot .pick-list{gap:0}
/* UN CHOIX PARMI DEUX OU TROIS (`choixHTML`) : la rangée retenue porte
   sa coche d'accent au bout ; les autres, rien — l'encre va à ce qui est
   choisi (§6, règle 1). Le libellé garde son poids de liste. */
.pick-choix b{font-weight:var(--weight-regular)}
.pick-choix .ic{flex:none;color:var(--accent)}
.pick-choix[aria-checked="true"] b{font-weight:var(--weight-semibold)}
.modal-b .pick:not(.pk),.rg-foot .pick{
  border:0;border-bottom:1px dashed var(--border-soft);border-radius:0;
  background:none;box-shadow:none;padding:6px 0;
}
/* la dernière n'a pas de trait, mais garde son épaisseur : à 0 elle
   perdait 1 px et devenait la seule rangée d'une autre hauteur */
.modal-b .pick:not(.pk):last-child,.rg-foot .pick:last-child{border-bottom-color:transparent}
.modal-b .pick:not(.pk):active,.rg-foot .pick:active{box-shadow:none;transform:none;background:var(--surface-chrome)}
@media (hover:hover){
.modal-b .pick:not(.pk):not(.pick-danger):hover b,.rg-foot .pick:not(.pick-danger):hover b{color:var(--text-link)}
}
.pick-close b::before{
  content:"";display:inline-block;width:8px;height:8px;margin-right:9px;vertical-align:1px;
  background:var(--c);
}
.dup-note{
  background:var(--amber-wash);border:1px solid var(--amber);color:var(--amber);
  border-radius:var(--radius-0);padding:10px 12px;font-size:var(--text-13);line-height:1.5;
  margin-bottom:12px;display:flex;align-items:center;gap:8px;flex-wrap:wrap;
}
.dup-note b{color:inherit}
.dup-note .btn{color:var(--text-body)}

/* ============ FICHE PISTE ============ */
.fi-closed{
  display:flex;align-items:center;gap:8px;
  border:1px solid var(--border-strong);border-left:3px solid var(--c);
  background:var(--surface-chrome);padding:10px 12px;margin-bottom:14px;
  font-size:var(--text-13);
}
.fi-closed .fic-t{flex:1;min-width:0}
.fi-closed .btn{flex:none}
.seg3{display:flex;flex-wrap:wrap;--bascule:0rem}
/* RANGÉE OU PILE, JAMAIS ROGNÉ (octobre 2026). Un segment ne descend pas
   sous son plus long mot (`min-width:auto`) : quand la rangée ne tient
   plus, il sortait de la feuille — « Réponse » perdait 10 px à 200 % en
   320 px, rogné par la ceinture `overflow-x:hidden`. La bascule de la
   carte de suivi passe toute la rangée en pile d'un coup, au seuil
   `--bascule` : n segments × le plus long mot, MESURÉ, en rem pour
   suivre la police. Un contrôle qui tient toujours (« Ouvert · Sur
   demande ») n'en déclare pas et reste une rangée. */
.seg3 .seg{
  flex:1 1 calc((var(--bascule) - 100%) * 999);min-height:var(--ctl);padding:4px 6px;
  border:1px solid var(--border-strong);border-radius:var(--radius-1);
  /* À PLAT (19 octobre 2026) : chaque segment portait son propre biseau,
     si bien que trois mots d'un même choix se lisaient comme trois gros
     boutons côte à côte. Le choix est UN contrôle : un cadre, des cases
     plates sur le blanc du champ, et seule la retenue s'enfonce. */
  background:var(--surface-field);box-shadow:none;
  font-weight:var(--weight-semibold);font-size:var(--text-13);color:var(--text-muted);
}
/* les bords se joignent dans le sens de la pile : à gauche en rangée,
   en haut en pile — le même calcul que la bascule */
.seg3 .seg+.seg{margin-left:clamp(-1px, (var(--bascule) - 100%) * 999, 0px);
  margin-top:clamp(-1px, (100% - var(--bascule)) * 999, 0px)}
/* le statut de la fiche : trois segments, le plus long mot « contacter »
   rend 68 px à 16 px de police — 3 × 68 = 12,8 rem */
.seg3.seg3-statut{--bascule:13rem}
.seg3 .seg.on{background:var(--accent);color:var(--on-accent);box-shadow:var(--bevel-down)}
.na-box{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  border:1px dashed var(--border-strong);border-radius:var(--radius-1);
  padding:10px 12px;background:var(--surface-chrome);
}
.na-box.late{border-color:var(--amber);background:var(--amber-wash)}
.na-cur{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.na-cur b{font-size:var(--text-14)}
.na-cur span{font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted)}
.na-none{font-size:var(--text-12);color:var(--text-muted);line-height:1.5}
.na-btn{width:100%;min-height:var(--ctl);text-align:left;font:inherit;color:var(--text-body);cursor:pointer}
.na-btn>.ic{flex:none;color:var(--text-muted)}
.na-btn:active{box-shadow:var(--bevel-down);transform:translateY(1px)}
@media (hover:hover){
.na-btn:hover{border-color:var(--accent)}
.na-btn.late:hover{border-color:var(--amber)}
}
.fi-hist{margin-top:4px}
/* « À savoir » et « Historique » s'ouvrent d'un tap — même règle, et
   surtout MÊME SIGNE que « Bientôt » : sans le triangle, ces deux mots
   flottaient en teal au bas de la fiche sans rien dire de ce qu'ils
   font. Un même geste ne se montre pas de deux façons dans une app. */
.fi-hist summary{
  display:flex;align-items:center;gap:7px;min-height:var(--ctl);
  cursor:pointer;list-style:none;user-select:none;
  /* le dessin des AUTRES titres de la fiche (« Contacts », « Mes
     notes ») : en teal, ces deux mots se lisaient comme deux liens au
     bas de la fiche — et un mot en couleur qui ne mène nulle part est
     exactement ce que le relevé des boutons a compté de trop */
  font-size:var(--text-11);font-weight:var(--weight-bold);letter-spacing:var(--tracking-wide);
  text-transform:uppercase;color:var(--text-muted);padding:8px 0;
}
.fi-hist>summary::-webkit-details-marker{display:none}
.fi-hist>summary::before{
  content:'';width:0;height:0;flex:none;
  border-left:5px solid var(--accent);border-top:4px solid transparent;border-bottom:4px solid transparent;
}
.fi-hist[open]>summary::before{
  border-left:4px solid transparent;border-right:4px solid transparent;
  border-top:5px solid var(--accent);border-bottom:0;
}
/* contacts compacts (#15) : une ligne par personne, dépliable — les
   règles .ct-* (meta, acts, note) servent aussi dans .ctc-body */
.ctc-list{display:flex;flex-direction:column;gap:6px}
.ctc{background:var(--surface-chrome);border:1px solid var(--border-soft);border-radius:var(--radius-1)}
.ctc>summary{
  list-style:none;cursor:pointer;user-select:none;
  display:flex;align-items:center;gap:8px;min-height:var(--ctl);padding:5px 10px;
  /* à texte doublé, le nom et son rôle ne tiennent plus côte à côte :
     le rôle passe au rang suivant plutôt que l'un des deux soit rogné
     (WCAG 1.4.10 — le contenu se réorganise, il ne se perd pas) */
  flex-wrap:wrap;
}
.ctc>summary::-webkit-details-marker{display:none}
.ctc>summary::before{content:'▸';font-size:9px;color:var(--text-muted);flex:none}
.ctc[open]>summary::before{content:'▾'}
/* LE NOM D'UN CONTACT EST UNE IDENTITÉ : IL PLIE, IL NE S'ÉLIDE PAS.
   C'est la règle de §6 pour les listes à cocher, et elle vaut ici pour
   la même raison — « Léa Barbaste » amputé ne se distingue plus de son
   homonyme, et un nom coupé n'est pas un défaut d'esthétique mais une
   erreur d'identification. Le rôle qui l'accompagne, lui, est une
   DONNÉE : il garde le droit de s'élider.
   Le plafond de 62 % et le `nowrap` faisaient l'inverse : à texte
   doublé le nom se rognait de 3 px. Trois pixels, c'est un seuil que
   les métriques de police font basculer — ça tenait sur ma machine, ça
   ne tenait pas sur le runner. La correction ne vise donc pas les trois
   pixels, elle retire la contrainte qui les produit. */
.ctc>summary b{
  flex:0 1 auto;min-width:0;
  font-size:var(--text-13);font-weight:var(--weight-semibold);
  white-space:normal;overflow-wrap:break-word;
}
.ctc-sub{
  flex:1 1 auto;
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.ctc-body{padding:2px 10px 10px;border-top:1px dashed var(--border-soft)}
.ctc-body .ct-acts{
  /* LA FICHE CONTACT D'iOS : les gestes d'une personne sont des TUILES
     égales, l'icône au-dessus du mot, sur une rangée (19 octobre 2026).
     `auto-fill` et pas `auto-fit` : une personne qui n'a qu'une adresse
     donne UNE tuile, qui doit garder sa largeur de tuile — étirée sur
     toute la carte, elle redevenait la brique qu'on a retirée.
     Quatre mots teal posés en ligne (« Écrire Appeler SMS WhatsApp »)
     se lisaient comme une phrase de liens ; des tuiles se lisent comme
     quatre façons de joindre la même personne. Elles se partagent la
     largeur à parts égales et passent à la ligne quand elles n'y
     tiennent plus — jamais rognées. Au poste, la rangée s'arrête à
     28 rem : quatre tuiles étirées sur 600 px redevenaient des barres. */
  display:grid;grid-template-columns:repeat(auto-fill,minmax(4.5rem,1fr));gap:6px;
  margin-top:9px;max-width:28rem;
}
/* CHAQUE CONTRÔLE FAIT LA TAILLE DE SON MOT — c'est le cas déjà tranché
   pour les puces (§6), qui se rejouait ici sans qu'on le voie.
   `flex:1 1 auto` répartit la place restante entre les contrôles : seul
   sur son rang, « Écrire » prenait donc les 348 px de la carte, et
   « WhatsApp » en faisait autant sous les trois autres. Un bouton large
   annonce une action lourde ; joindre quelqu'un par SMS n'en est pas
   une, et surtout WhatsApp se retrouvait plus en avant qu'« Appeler »
   alors que rien ne le justifie (M3 : l'emphase dit l'importance).
   Le poste portait déjà `flex:0 0 auto` — c'est le pouce, l'ergonomie
   la plus contrainte, qui avait gardé la mauvaise valeur. */
.ctc-body .ct-acts .btn{flex-direction:column;gap:3px;min-width:0;padding:7px 4px 6px;font-size:var(--text-12)}
.ctc-body .ct-acts .btn .ic{width:16px;height:16px}
.ctc-body .ct-meta{font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);margin-top:6px;word-break:break-all}
.ctc-body .ct-note{font-size:var(--text-12);color:var(--text-muted);margin-top:6px;font-style:italic}
.ctc-body .ct-mod{margin-top:8px}
/* « + N personnes connues » : les reçues dormantes, repliées (#14) */
.ctc-known{margin-top:8px}
.ctc-known>summary{
  list-style:none;cursor:pointer;user-select:none;min-height:28px;
  display:flex;align-items:center;gap:6px;
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);
}
.ctc-known>summary::-webkit-details-marker{display:none}
.ctc-known>summary::before{content:'▸';font-size:9px}
.ctc-known[open]>summary::before{content:'▾'}
.ctc-known[open]>summary{margin-bottom:6px}
.timeline{list-style:none;margin:4px 0 0;padding:0}
.timeline li{font-size:var(--text-12);color:var(--text-muted);padding:4px 0;border-bottom:1px dashed var(--border-soft);display:flex;gap:9px}
.timeline li:last-child{border-bottom:0}
.timeline .d{font-family:var(--font-mono);font-size:var(--text-11);color:var(--accent);flex:none;padding-top:2px}

/* ============ FICHE : outils, à savoir, contacts ============ */
.lbl-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:4px}
.lbl-row label{margin-bottom:0}
/* Au pouce, cette ligne suit le cadre « À savoir », dont la DERNIÈRE
   rangée porte déjà « Itinéraire », calé à droite comme « Compléter ».
   Deux boutons alignés, à quelques pixels l'un de l'autre, et l'un des
   deux QUITTE l'application pour ouvrir un plan — se tromper là ne coûte
   pas un tap, ça coûte de perdre sa place. D'où un vrai palier entre le
   cadre et cette ligne, et pas les 8 px d'usage. */
/* La ligne d'identité porte le sous-titre à gauche et « Modifier » à
   droite, au pouce comme au poste — le bloc ne vit plus en bas de la
   feuille. C'est `flex-grow` sur `.fi-obj` qui tient les outils à
   droite, et non plus `margin-left:auto` : le bloc d'identité absorbe
   la place libre même sans sous-titre, donc le geste ne saute pas à
   gauche pour autant. La nuance compte depuis que la rangée se replie —
   `margin-left:auto` aurait jeté « Compléter » seul au bord droit d'un
   rang à lui, 230 px de vide à sa gauche, le défaut même qu'on venait
   de corriger sur l'itinéraire et sur le bandeau de clôture. */
.fi-top{display:flex;align-items:flex-start;gap:12px;margin-bottom:10px;flex-wrap:wrap}
/* LE NOM PASSE AVANT LE GESTE QUAND LA PLACE MANQUE (§5, WCAG 1.4.10 :
   le contenu se réorganise, il ne se perd pas). « Compléter » ne
   grandit pas avec le texte mais il prend sa part à toutes les tailles :
   mesuré, il laissait 138 px au nom sur un 320 px — et
   « Interprofessionnel » en demande 141 À 100 %. Le nom d'une piste se
   cassait donc en plein mot dans l'état le plus ordinaire qui soit, un
   petit téléphone sans réglage particulier.
   Le seuil est un PLANCHER EN REM, pas une media query : il suit la
   police du navigateur tout seul, sans un réglage de plus dans l'app.
   Et la valeur se MESURE : balayée de 10 à 13 rem sur 4 largeurs × 5
   tailles de police, 10,5 est la plus petite qui replie à 320 px à
   100 % — 10,25 y laisse encore le mot coupé — et c'est aussi celle
   qui replie le MOINS ailleurs (12 fois contre 14 à 11 rem). Elle ne
   change strictement rien là où le nom tenait déjà. Reste un cas que
   nulle mise en page ne sauve : 320 px à 200 %, où le mot seul demande
   282 px pour une feuille qui n'en offre que 254. C'est là que
   `hyphens` sert. */
.fi-top .fi-obj{flex:1 1 10.5rem;min-width:0;margin-bottom:0}
/* LE NOM PLIE, IL NE SE COUPE PAS (§4 : une identité ne s'ampute
   jamais). `.obj-n` est un bloc, donc il passe à la ligne tout seul —
   c'est la sous-ligne, une DONNÉE, qui garde le droit de s'élider. */
/* `hyphens` d'abord, la casse brutale seulement en dernier recours : un
   mot plus long que la boîte existe (à 200 % sur un 320 px), et
   « Inter-professionnel » se lit, « Interprofessionne / l » un peu
   moins, « Grou / pem / ent » pas du tout. */
.fi-obj .obj-n{overflow-wrap:break-word;hyphens:auto}
.fi-tools{display:flex;align-items:center;gap:10px;margin:0;flex-wrap:wrap}
/* la zone de notes suit son contenu (ui/fiche.js) : pas de plancher à
   68 px ici, c'est le script qui donne la hauteur */
/* la hauteur est pilotée en JS (le champ suit son contenu) : ici, juste
   le fait qu'elle GLISSE au lieu de sauter d'une ligne d'un coup */
/* « J'y suis passé » : un ATOUT, pas une urgence — d'où le teal de
   l'accent et pas l'échelle `mark-*`, réservée à ce qui presse. Posé
   tout en haut de la fiche : c'est la seule ligne qui fait passer une
   candidature de 3 % à 40 % de réponses. */
.fi-vecu{
  display:flex;align-items:center;gap:7px;margin:0 0 14px;padding:8px 10px;
  border:1px solid var(--accent);background:var(--teal-wash);
  color:var(--accent);font-size:var(--text-13);
}
.fi-vecu b{font-weight:var(--weight-semibold)}
/* deux personnes qui peuvent te porter (un ami, une déclaration) : deux
   bandeaux, 8 px entre eux — le même geste, mais deux personnes */
.fi-vecu + .fi-vecu{margin-top:-6px}
#fiNotes{min-height:44px;overflow-y:hidden;resize:none;
  transition:height var(--dur-2) var(--ease-out)}
/* un chiffre qui bouge deux fois par mois n'a pas à porter un cadre :
   encadré, il se déguisait en chose qui se tape (elle ne se tape pas) */
.fi-score{
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);
}
/* La fiche au poste : le travail à gauche, le dossier à droite. Une
   fenêtre de 560 px au milieu de 1280 était l'écran du pouce resservi
   tel quel — et sur l'écran le PLUS ouvert de l'application. */
@media (min-width:901px){
  .modal-fiche{max-width:900px}
  .fi-cols{display:grid;grid-template-columns:1fr 1fr;gap:8px 22px;align-items:stretch}
  .fi-cols>div{min-width:0;display:flex;flex-direction:column}
  /* le travail est plus court que le dossier : plutôt que laisser un
     trou à gauche, les notes prennent la hauteur qui reste — c'est la
     seule zone de cette fenêtre où l'on écrit vraiment */
  .fi-cols>.fi-travail>.field:last-child{flex:1;display:flex;flex-direction:column;min-height:0}
  /* `1 0 auto`, pas `flex:1` : ce dernier pose une base de ZÉRO, et la
     hauteur que le champ se donne en grandissant était ignorée — au
     poste comme sur tablette, une note de trois lignes restait coincée
     sur une, le reste caché sous `overflow:hidden`. La base `auto` garde
     sa hauteur de contenu ; le reste de la colonne s'ajoute par-dessus.
     (Le plancher de 150 px qui vivait ici ne s'est jamais appliqué :
     `#fiNotes` l'écrasait. Il part.) */
  .fi-cols>.fi-travail>.field:last-child textarea{flex:1 0 auto}
  /* Écrire au poste : la fenêtre prend une hauteur franche et la donne
     au message. Les deux menus (destinataire, modèle) cessent aussi de
     tronquer leur libellé — c'est la largeur qui manquait. */
  /* le formulaire d'une piste, au poste : deux colonnes. Les paires qui
     tiennent déjà sur un rang (Entreprise/Ville, Site/Adresse) gardent
     toute la largeur ; le reste se range côte à côte. La hauteur est
     divisée par deux — le formulaire cesse de déborder, et le contact
     redevient visible sans défiler. */
  .ed-form{display:grid;grid-template-columns:1fr 1fr;gap:0 20px;align-items:start}
  .ed-form>.grid2{grid-column:1 / -1}
  .modal-compose{max-width:820px;height:min(88dvh,800px)}
  .modal-compose .fld-body textarea{min-height:240px}
}
/* ÉCRIRE : LA ZONE DE RÉDACTION TIENT SA RÉGION — le panneau du §5,
   appliqué à l'endroit où il manquait.
   Ce qui varie dans ce composeur, ce n'est pas le message : c'est TOUT
   CE QUI EST AU-DESSUS. Une piste sans notes et à un seul contact coûte
   230 px de préambule, une piste bien renseignée en coûte 400 — et
   c'est le champ de rédaction qui payait la différence. Mesuré en
   390×844 : 289 px de brouillon dont 168 visibles (58 %), 388 px dont
   168 pour le gabarit de relance (43 %). La taille de la zone
   d'écriture décidée par ce qu'on savait de l'entreprise, et la feuille
   arrêtée à 612 px sur 776 disponibles sans jamais défiler : de la
   place perdue à côté d'un brouillon coupé.
   La feuille prend donc son allocation, et le message reçoit un
   plancher en `dvh` — c'est lui qui rend la place STABLE d'une piste à
   l'autre. `flex:1` par-dessus le laisse grandir là où il reste de la
   place, au poste comme sur une piste peu renseignée. */
.modal-compose{height:92dvh}
.modal-compose .modal-b{flex:1;min-height:0;display:flex;flex-direction:column}
/* Rien au-dessus du message ne se serre : ni à qui l'on écrit, ni
   l'objet, ni ce qu'on sait d'elle. Deux tentatives sont mortes ici et
   valent d'être dites — un panneau qui défile tranchait la carte
   « À savoir » au milieu d'une ligne, cadre pointillé compris, ce qui
   se lit comme un défaut d'affichage ; et le même panneau avec
   `min-height:0` descendait sous ses propres enfants, le champ Message
   venant se poser PAR-DESSUS l'objet en 360×640 sans que `.modal-b` en
   sache rien. Quand tout ne tient pas, c'est la FEUILLE qui défile —
   comme tout composeur de messagerie. */
/* PAS de `min-height:0` sur le corps non plus : le cadre garde le
   plancher de son champ. Il tombait sinon à 134 px pendant que le champ
   en gardait 170 — le champ débordait sur la ligne d'aide. */
/* le cadre grandit avec son champ et ne RÉTRÉCIT jamais sous lui
   (`flex:1 0 auto`) : dans cette colonne bornée, `flex:1` le laissait
   descendre sous son contenu, et la ligne d'aide se posait sur la fin
   du message (§4, la même faute verticalement) — le corps défile */
.modal-compose .fld-body{flex:1 0 auto;min-height:min(30dvh,300px);display:flex;flex-direction:column}
/* il grandit avec son texte (`champGrandit`) : `flex:1 0 auto`, jamais
   `flex:1` — une base nulle ignorerait la hauteur qu'il se donne (§6) */
.modal-compose .fld-body textarea{flex:1 0 auto;resize:none;overflow:hidden}
.fld-body textarea{min-height:170px}
/* L'OBJET S'AFFICHE EN ENTIER — il grandit avec son texte (JS), le
   plafond de quatre lignes vit dans `ui/mail.js`. `resize:none` : il se
   dimensionne déjà tout seul, une poignée ne ferait que le dérégler. */
.fld-subj label{display:flex;justify-content:space-between;align-items:baseline;gap:10px}
.fld-subj textarea{min-height:var(--ctl);resize:none;overflow-y:auto}
/* le compteur est une DONNÉE, pas une alerte : il ne change jamais de
   couleur. Même famille que les compteurs du produit — mono, discret. */
.fld-n{font-family:var(--font-mono);font-weight:var(--weight-medium);letter-spacing:0}
/* une valeur qu'on lit sans pouvoir la changer : ni bordure ni relief —
   ce qui se tape a un cadre, le reste se pose en texte (§6, sobriété 3) */
.fld-ro{font-size:var(--input-fs);line-height:1.4;padding:2px 0;color:var(--text-body)}
/* Une fiche remplie à 43 % ne RÉCLAME rien : elle ne coûte pas une
   opportunité, contrairement à une relance en retard — qui, elle, porte
   déjà l'ambre trois blocs plus haut. Deux ambres sur un écran diluent
   celui qui compte. Le signal est le verbe du bouton d'à côté, qui passe
   de « Modifier » à « Compléter » : un mot, pas une couleur d'alerte.
   Les gestes ne se poussent pas au bord droit (`margin-left:auto`) :
   le nom absorbe la place libre (`.fi-obj`), et depuis que « Donner »
   les rejoint (R1, 7.0.0), un rang replié jetait « Donner » seul au
   bord droit, 148 px de vide à sa gauche (e2e-decroche). */
.fi-know{border:1px solid var(--border-soft);border-radius:var(--radius-1);background:var(--surface-chrome)}
/* « À savoir » rapatrié dans le composeur : mêmes lignes que sur la
   fiche, dans un cadre au trait discontinu pour dire que c'est de la
   matière à lire, pas un champ à remplir. */
.ml-know{
  margin:0 0 12px;padding:2px 10px 0;
  border:1px dashed var(--border-strong);background:var(--surface-chrome);
}
.ml-know-h{
  display:block;padding:7px 0 2px;font-family:var(--font-mono);
  font-size:var(--text-11);color:var(--text-muted);
  text-transform:uppercase;letter-spacing:.04em;
}
.ml-know .fk{padding-left:0;padding-right:0}
/* ---- AU POUCE, L'ÉTIQUETTE SE MET AU-DESSUS DE SA VALEUR ----
   La colonne de gauche était fixée à 72 px. Mesuré sur un corps de
   388 px : 72 px d'étiquette plus la gouttière et le rembourrage, c'est
   **24 % de la largeur** dépensés pour des mots qui ne changent JAMAIS
   (§6, règle 1 : l'encre va à ce qui change, pas à ce qui est
   permanent). Il restait 248 px au contenu, d'où trois à quatre rangs
   de repli sur « Process », « Conseils » et « En bref ».
   La source est constante sur ce point : sur un écran étroit, une
   étiquette posée À GAUCHE « ne laisse que très peu de place au champ »
   (Baymard, position des libellés), et l'empilement est le motif prévu
   quand la place horizontale manque (ServiceNow Horizon,
   *label value stacked*). L'étiquette reprend donc sa ligne, le contenu
   prend les 388 px, et on relit sans balayer de gauche à droite.
   AU POSTE ELLE RESTE À GAUCHE : la place existe, et les deux colonnes
   se balaient mieux qu'une suite de paires empilées (§5 — on ne fait
   deux dessins que si l'usage diffère, et ici c'est la LARGEUR
   disponible qui diffère vraiment). */
.fk{display:block;padding:7px 10px;font-size:var(--text-12);
  border-bottom:1px dashed var(--border-soft)}
.fk:last-child{border-bottom:0}
.fk-l{
  display:block;margin-bottom:2px;font-family:var(--font-mono);font-size:var(--text-11);
  color:var(--text-muted);text-transform:uppercase;letter-spacing:.04em;
}
@media (min-width:901px){
  .fk{display:flex;gap:10px;align-items:baseline}
  /* 6,5 em = 72 px à taille normale ; en `em`, la colonne suit la police —
     en px, « TECHNOS » débordait sur sa valeur à 200 % (audit 7.1.0) */
  .fk-l{display:inline;flex:none;width:6.5em;margin-bottom:0}
}
.fk-v{min-width:0;line-height:1.5;word-break:break-word}
/* le site est un LIEN à part entière dans sa ligne de données, pas un mot
   au milieu d'une phrase : il obéit à la règle des cibles */
a.fk-v{display:inline-flex;align-items:center;gap:5px;min-height:var(--ctl)}
/* L'adresse porte un GESTE, pas seulement une valeur. Il se pose sous
   elle, calé sur son bord gauche, taillé à son mot : un bord stable
   dans les deux ergonomies (§6, « une valeur a UN BORD »). `.fk-v` est
   déjà la boîte qui tient la colonne du poste — le geste vit DEDANS,
   pour ne pas avoir à réinventer cette colonne une seconde fois. */
/* UNE VALEUR QUI A ÉTÉ SAISIE EN LIGNES SE RELIT EN LIGNES. Le HTML
   replie tout espace blanc, sauts de ligne compris : sans ceci, une
   adresse écrite sur trois rangs se réaffiche sur un seul et le champ
   libre ne sert à rien. `pre-line` garde les retours et replie le
   reste — une double espace tapée par erreur ne se voit pas. */
.fk-lignes{white-space:pre-line}
.fk-go{display:flex;flex-direction:column}
.fk-go .btn{align-self:flex-start;margin-top:6px}
.fk-tags{display:flex;gap:5px;flex-wrap:wrap}
.fk-tag{
  border:1px solid var(--border-strong);background:var(--surface-window);
  padding:1px 7px;border-radius:var(--radius-1);font-size:var(--text-11);font-weight:var(--weight-semibold);
}
/* UN CHAMP D'UNE SEULE LIGNE QUI SE REPLIE POUR SE LIRE. Même dessin
   qu'un `<input>` tant que la valeur tient — `min-height:var(--ctl)` lui
   donne la hauteur d'une cible, donc rien ne bouge à l'ouverture d'une
   fiche ordinaire. `resize:none` parce que la hauteur est calculée
   (`champGrandit`) : une poignée qui se bat avec le script est une
   promesse qu'on ne tient pas. `overflow-y:hidden` parce qu'il n'y a
   PAS de plafond ici — contrairement à l'objet d'un mail, qui vise
   40-60 caractères et garde le sien. */
/* Le texte d'un rang se CENTRE dans la hauteur d'une cible, comme dans
   un `<input>` voisin : avec 4 px fixes, il collait au bord haut et
   laissait 10 px vides dessous — côte à côte avec « Prénom et nom », la
   feuille du profil avait deux dessins de champ (audit d'octobre 2026).
   À texte agrandi le rang dépasse la cible, et le plancher de 2 px
   reprend la main. */
.fld-1l textarea{
  min-height:var(--ctl);height:auto;resize:none;overflow-y:hidden;
  padding:max(2px, calc((var(--ctl) - var(--input-fs) * var(--leading-body) - 2px) / 2)) 10px;
}
/* UNE ADRESSE POSTALE EST UN CHAMP LIBRE, ET SA HAUTEUR DE DÉPART
   ENSEIGNE SON FORMAT. Deux lignes au repos, c'est ce qui dit « tu peux
   aller à la ligne » sans qu'aucune phrase n'ait à le dire — et c'est
   aussi ce qui permet au `placeholder` de montrer une adresse sur deux
   lignes sans se faire rogner (un champ d'une ligne l'aurait coupé, et
   personne ne l'aurait vu : un placeholder ne compte pas dans
   `scrollHeight`, donc `champGrandit` ne l'aurait pas rattrapé).
   Au-delà, c'est `champGrandit` qui donne la hauteur : `resize:none`
   parce qu'une poignée qui se bat avec le script est une promesse qu'on
   ne tient pas, `overflow-y:hidden` parce qu'il n'y a pas de plafond. */
.fld-adr textarea{
  min-height:calc(2 * 1.5em + 18px);height:auto;resize:none;overflow-y:hidden;
}
.ta-s{min-height:48px}
.ckline{display:flex;align-items:center;gap:9px;font-size:var(--text-13);min-height:var(--ctl);cursor:pointer;user-select:none}
/* la case suit la taille de son libellé : en `px` elle restait à 18
   pendant que le texte grandissait, et quelqu'un qui agrandit sa police
   pour y voir se retrouvait avec la case la plus petite de l'écran.
   1,4 em rend 18 px à la taille par défaut — rien ne bouge pour tout le
   monde, tout suit pour qui en a besoin. `font-size:inherit` n'est pas
   décoratif : un contrôle de formulaire ne l'hérite PAS, donc sans lui
   l'`em` se calculait sur la police par défaut du navigateur et la case
   restait figée à 19 px, exactement le défaut qu'on corrige. La CIBLE, elle,
   n'a jamais été la case : c'est l'étiquette entière (352 × 44 au
   pouce), et taper son bord droit bascule bien la case. */
.ckline input{font-size:inherit;width:1.4em;height:1.4em;accent-color:var(--accent)}
.dchip.on{background:var(--accent);color:var(--on-accent);box-shadow:none}
.cp-carry{
  display:flex;align-items:center;gap:8px;overflow:hidden;
  border:1px solid var(--border-strong);background:var(--teal-wash);color:var(--text-body);
  padding:8px 12px;border-radius:var(--radius-0);
  font-family:var(--font-mono);font-size:var(--text-12);margin-bottom:4px;
}
.cp-carry span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ============ CONTACTS À RATTACHER (bac) ============ */
.tr-orph{margin:0 0 18px}
.orow{
  background:var(--surface-window);border:1px solid var(--border-strong);
  border-left:3px solid var(--amber);border-radius:var(--radius-0);
  box-shadow:var(--shadow-raised);
}
/* La RANGÉE faisait déjà 44 px ; la partie tapable, elle, n'en faisait
   que 32 — `align-items:center` lui donnait la hauteur de son texte, et
   les 26 px de marge autour n'appartenaient à personne. Une cible ne se
   mesure pas sur la ligne qui la contient mais sur ce qui répond au
   doigt : elle prend donc toute la hauteur de sa rangée (`stretch`), et
   son contenu se recentre dedans. Le bouton, lui, garde la sienne. */
.orow .sw-in{
  display:flex;align-items:stretch;gap:10px;min-height:var(--control-h-touch);
  padding:7px 8px 7px 12px;
}
.orow .sw-in>.btn{align-self:center}
.orow .o-main{flex:1;min-width:0;cursor:pointer;display:flex;flex-direction:column;justify-content:center}
/* le nom d'une personne est une IDENTITÉ : il plie, il ne s'ampute
   pas — même règle que `.row-item h3`, même motif. Mesuré à 200 % en
   320 px : « Awa Diallo » perdait 5 px dans une colonne de 123. C'est
   la sous-ligne (rôle, adresse) qui garde le droit de s'élider. */
.orow h3{font-size:var(--text-13);font-weight:var(--weight-semibold);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.orow .o-sub{font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tr-orph .hint{margin:8px 0 0}

/* ============ TABLEAU 3 COLONNES (desktop) ============ */
.page-wide{max-width:1060px}
/* `1fr` vaut `minmax(auto,1fr)` : une colonne dont le contenu est plus
   large (une date « sam. 08/08 ») pousse et écrase ses voisines — à
   901 px, la colonne du milieu tombait à 245 px contre 303. `minmax(0,…)`
   rend les trois colonnes vraiment égales, à toutes les largeurs. */
/* `align-items:start` laissait chaque colonne s'arrêter sur sa dernière
   carte : 605 px, 725 px, 310 px côte à côte, et sous les deux courtes
   un vide qui n'appartenait à personne. Étirées, les trois cadres se
   partagent la région — §5.2 : « une place qui a un propriétaire n'est
   plus un manque ». Le fond dithéré des colonnes vides fait le reste. */
.board{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;align-items:stretch;margin-top:6px}
.bcol{display:flex;flex-direction:column}
.bcol-rows{flex:1}
.bcol{
  border:1px solid var(--border-strong);background:var(--surface-chrome);
  border-radius:var(--radius-0);box-shadow:var(--shadow-raised);
}
.bcol-h{
  display:flex;align-items:center;gap:7px;padding:8px 10px;
  border-bottom:1px solid var(--border-strong);background:var(--surface-window);
  font-size:var(--text-11);font-weight:var(--weight-bold);
  letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--text-muted);
}
.bcol-h::before{content:'';width:8px;height:8px;flex:none;border:1px solid var(--border-strong);background:var(--c)}
.bcol-rows{display:flex;flex-direction:column;gap:8px;padding:10px}
/* « Aujourd'hui » au poste de commandement : les trois tranches en
   colonnes, mêmes briques que le tableau de « Mes pistes ». L'en-tête
   porte déjà son icône — le carré de couleur du tableau des statuts
   n'a rien à dire ici. Seule « En retard » prend l'ambre : l'encre va
   à la colonne qui réclame quelque chose. */
/* CHAQUE COLONNE S'ARRÊTE SOUS SA DERNIÈRE CARTE (§6, règle 3 : « une
   colonne de Trello ou de Notion s'arrête sous sa dernière carte »).
   Étirées à la plus haute, les colonnes courtes encadraient du vide :
   « Aujourd'hui » à une carte sous 250 px de cadre, à côté de
   « Bientôt » à quatre (audit du 28 octobre 2026). Le tableau de « Mes
   pistes », lui, garde ses colonnes égales : on y DÉPOSE une carte, et
   le bas d'une colonne courte doit rester une cible (F9). */
.td-board{margin-top:14px;align-items:start}
.td-board .bcol-h::before{display:none}
.td-board .tr-late .bcol-h{color:var(--amber)}
.td-board .tr-late .bcol-h .tr-n{border-color:var(--amber);background:var(--amber-wash)}
.bcard{
  background:var(--surface-window);border:1px solid var(--border-strong);border-radius:var(--radius-0);
  box-shadow:var(--bevel-up), var(--shadow-raised);cursor:pointer;
  font-family:var(--font-ui);color:var(--text-body);
}
.bcard .sw-in{display:flex;align-items:flex-start;gap:8px;padding:9px 11px}
/* Au clavier, la carte visée par ↑ ↓ ← → défile jusqu'à l'écran — mais
   pas SOUS le titre de colonne collé en haut, qui la recouvrirait */
.bcard .bc-main{scroll-margin-top:56px;scroll-margin-bottom:40px}
.bcard .bc-main{flex:1;display:flex;flex-direction:column;gap:3px;text-align:left;min-width:0}
.bcard .hov-del{align-self:flex-start}
@media (hover:hover){
.bcard:hover .sw-in{background:var(--surface-field)}
}
.bcard b{font-size:var(--text-13);font-weight:var(--weight-semibold)}
.bc-sub{
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;
}
.bc-na{font-size:var(--text-12);max-width:100%;display:flex;flex-wrap:wrap;align-items:baseline;column-gap:4px}
/* LE TEXTE S'ÉLIDE, LA MARQUE RESTE. Les deux partageaient une boîte
   bridée à deux lignes : depuis la barre latérale du poste (R1), les
   colonnes ont rétréci, et à 200 % « Relancer le service RH −2 »
   perdait sa troisième ligne — c'est-à-dire le retard, la seule chose
   qui réclamait (e2e-ux-audit, 7.0.0). La marque est maintenant hors
   de la bride : sur la ligne du texte quand elle tient, dessous sinon. */
.bc-na-t{min-width:0;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.bc-na .mark{flex:none;display:inline-block;vertical-align:baseline}
/* le compte de personnes vit dans la sous-ligne : son icône s'assoit sur
   la ligne de texte au lieu de flotter */
.bc-sub .ic{vertical-align:-2px;margin-right:1px}
.bc-none{color:var(--text-muted)}
.bcol-empty{padding:18px 10px;text-align:center;color:var(--text-muted);font-size:var(--text-12);background-image:var(--dither-img);background-size:var(--dither-size)}
/* glisser une carte vers une autre colonne = changer le statut :
   états francs (opacité nette, contour accent), aucun fondu */
.bcard.drag-src{opacity:.45}
.bcol.drop-ok{outline:2px solid var(--accent);outline-offset:-1px}
.bcol.drop-ok .bcol-rows{background:var(--teal-wash)}

/* ============ SÉLECTION (Prospecter) ============ */
.pk{justify-content:flex-start}
/* la personne posée sur la ligne d'état : son icône se lit dans l'encre
   de la ligne, pas plus fort qu'elle */
.pk .pk-m span .ic{vertical-align:-2px;margin-right:1px}
.pk .ic-on{display:none;color:var(--accent)}
.pk.on .ic-on{display:inline-block}
.pk.on .ic-off{display:none}
/* L'ÉTAT VIT DANS LA CASE, PAS DANS UN APLAT.
   Il y a eu trois versions, et les deux premières sont la leçon.
   ① La ligne cochée portait `--select-bg`, le navy qui dit « tu es sur
      cet onglet » dans les deux barres de navigation. Le même aplat
      disait deux choses sans rapport.
   ② Elle est passée au lavis teal + liseré d'accent — la bonne famille
      de couleur, mais toujours un aplat. Résultat mis côte à côte avec
      « Donner », qui lui ne remplit rien : deux écrans qui ne se
      ressemblaient pas pour le MÊME geste, cocher une piste. Et sur la
      carte à deux étages, l'aplat ne couvrait que l'étage du haut — la
      ligne des personnes restait beige, ce qui coupait l'objet en deux.
   ③ Donc : rien du tout. La carte reste une carte, entière et calme, et
      c'est la CASE qui porte l'état — vide, ou cochée en accent. C'est
      le dessin de « Donner », choisi par le mainteneur comme référence,
      et c'est le plus sobre : sur vingt-quatre lignes, l'encre ne bouge
      que là où l'œil la cherche déjà.
   Ce qui se verrouille reste la séparation : le navy au châssis, le
   teal au contenu — mais le teal se dit ici par un pictogramme, pas
   par une surface. */
.pk.on .ic-on{color:var(--accent)}
/* ---- ce qu'on a explicitement ÉCARTÉ ----
   Un seul état de plus, et il n'existe que dans les listes qui partent
   de « tout coché » (Donner, le partage en groupe, les personnes
   visées) : là, une ligne non cochée n'est pas « pas encore choisie »,
   elle est SORTIE de ce qui part. C'est une information, et elle mérite
   son encre. Dans une liste qui part de rien, cet état n'existe pas —
   ce n'est pas une inégalité de traitement, c'est un état en moins. */
.pk-inverse .pk:not(.on){background-image:var(--dither-img);background-size:var(--dither-size);color:var(--text-muted)}
.pk-inverse .pk:not(.on) .pk-m b{color:var(--text-muted)}
/* une piste écartée l'est ENTIÈRE : sa commande « qui » s'éteint avec
   elle, sinon l'objet disait deux choses à la fois */
.pk-duo.pk-out .pk-who{background-image:var(--dither-img);background-size:var(--dither-size);color:var(--text-muted)}
.pk-duo.pk-out .pk-who .ic{color:var(--text-muted)}
/* LES FICHES À COCHER SONT À PLAT (19 octobre 2026). Elles gardaient le
   biseau et l'ombre portée d'un bouton de 98 : vingt-quatre briques en
   relief dans « Prospecter » et « Donner », alors que la seule chose qui
   s'y décide vit dans la CASE. Un relief dit « je suis un geste » ; une
   fiche de liste dit « je suis une piste ». Le cadre reste (deux étages,
   un objet), dans le trait des cadres de groupe, et l'appui s'enfonce. */
.pick.pk,.pk-duo{background-color:var(--surface-window);border-color:var(--border-frame);box-shadow:none}
.pick.pk:active{background-color:var(--surface-chrome);box-shadow:var(--bevel-down)}
/* dans un duo, c'est le DUO qui porte le fond : la ligne qu'on coche
   reste transparente, cochée ou non (plus haut, « l'état vit dans la
   case » — e2e-ux-audit le mesure) */
.pk-duo .pick.pk,.pk-duo .pick.pk:active{background-color:transparent;box-shadow:none}

/* ============ ÉCHANGER : QR, SCAN, APERÇU, ANNULER ============ */
.qr-wrap{
  background:#fff;border:1px solid var(--border-strong);box-shadow:var(--shadow-raised);
  padding:16px;max-width:340px;margin:0 auto 12px;
}
.qr-wrap svg{display:block;width:100%;height:auto}
/* un QR se scanne de loin et de travers : sur le poste de commandement,
   la fenêtre a la place, autant la donner au code */
@media (min-width:901px){ .qr-wrap{max-width:420px} }
/* « Mon QR » : ce que l'image contient, écrit dessous — le nom, puis
   les entreprises. Rien de caché derrière un code (ui/amis.js). */
.mq-donne{display:flex;flex-direction:column;align-items:center;gap:4px;text-align:center;max-width:340px;margin:0 auto}
.mq-donne b{font-size:var(--text-16);font-weight:var(--weight-bold);overflow-wrap:anywhere}
/* `>` : le visage, devant le nom, est aussi un `span` — il garde sa teinte */
.mq-donne>span{font-family:var(--font-mono);font-size:var(--text-12);color:var(--text-muted);overflow-wrap:anywhere}
/* le parcours d'un ami : une entreprise qui est déjà dans tes pistes le
   dit en accent — c'est ce que l'ami te rapporte, un atout et non une
   urgence (jamais `mark-*`) */
.am-par .pick .pk-m .am-tienne{color:var(--accent);font-weight:var(--weight-semibold)}
.am-par .pick.am-ro{cursor:default}
.am-par .pick.am-ro:active{transform:none;box-shadow:none;background:none}
.am-par .pick.am-ro>.btn{flex:none;margin-left:auto}
/* LE NOM NE SE COUPE PAS, LE BOUTON PASSE DESSOUS (WCAG 1.4.10, §5).
   À 320 px texte doublé, « + Piste » laissait 4 rem au nom : « Société
   Générale Global… » se faisait couper au troisième rang. Le plancher
   est en rem, sur la colonne du texte — il suit la police tout seul :
   à 100 % le bouton reste sur la ligne, plus grand il descend. */
.am-par .pick.am-ro>.pk-m{min-width:9rem}
/* LA DEMANDE À SES AMIS (lot 3), DANS « CONTACTS » (12 octobre 2026) :
   sous la liste, là où la personne trouvée prendra sa place. Le geste
   est une ligne en pointillé — celle d'« Ajouter un ami » et d'« À
   trier » : un geste, pas un contact de plus. Partie, la demande devient
   un FAIT, en gris : rien ne presse, rien ne se tape. Le contact qu'un
   ami donne y est PROPOSÉ : le pointillé dit « pas encore à toi »,
   l'accent dit l'atout ; un tap ouvre son aperçu. */
.fi-demander,.fi-don{
  display:flex;align-items:center;gap:8px;width:100%;min-height:var(--ctl);margin-top:6px;padding:5px 10px;
  background:none;border:1px dashed var(--border-strong);border-radius:var(--radius-1);
  font:inherit;font-size:var(--text-13);color:var(--text-muted);text-align:left;cursor:pointer;
}
.fi-don{border-color:var(--accent);color:var(--accent)}
.fi-don b{flex:1;min-width:0;font-weight:var(--weight-semibold);overflow-wrap:anywhere}
.fi-demander:active,.fi-don:active{background:var(--surface-raised)}
@media (hover:hover){ .fi-demander:hover,.fi-don:hover{background:var(--surface-raised)} }
/* LA CARTE D'UNE DEMANDE (lot F3, idées G et I) — la même sur la fiche
   et dans « Amis ». Un fait, pas un bouton : un cadre de groupe SANS
   relief (le relief se mérite, §4), la phrase, puis le suivi comme un
   colis — trois barres de progression 98, creusées, remplies de blocs
   nets. L'étape faite est pleine, celle en cours à moitié, la suivante
   vide : c'est la barre qui dit l'état, le mot ne fait que le nommer. */
.dmc{margin:6px 0 0;padding:9px 10px 10px;border:1px solid var(--border-frame);background:var(--surface-field)}
.dmc-p{display:flex;align-items:flex-start;gap:7px;margin:0;
  font-size:var(--text-13);font-weight:var(--weight-semibold);line-height:1.4;color:var(--text-body)}
.dmc-p > .ic{flex:none;margin-top:.15em}
.dmc-t{min-width:0;overflow-wrap:anywhere}
.dmc-s{display:block;margin-top:2px;font:var(--weight-regular) var(--text-12)/1.4 var(--font-mono);color:var(--text-muted)}
/* TROIS COLONNES, OU UNE SOUS L'AUTRE — jamais deux puis une : la base
   bascule toute la rangée d'un coup (la « bascule » de Heydon Pickering,
   du flex que tout moteur rend depuis des années, §9). Le seuil est en
   rem : il suit la police du navigateur, sans média query (§5). */
.dmc-suivi{display:flex;flex-wrap:wrap;gap:8px;margin:10px 0 0;padding:0;list-style:none}
.dmc-et{flex:1 1 calc((14rem - 100%) * 999);min-width:0;display:flex;flex-direction:column;gap:4px}
.dmc-et b{font-size:var(--text-13);font-weight:var(--weight-semibold);line-height:1.3;color:var(--text-muted)}
.dmc-fait b,.dmc-cours b{color:var(--text-body)}
.dmc-bar{display:block;height:12px;padding:2px;border:1px solid var(--border-strong);
  background:var(--surface-field);box-shadow:var(--bevel-down)}
.dmc-bar i{display:block;width:0;height:100%;
  background:repeating-linear-gradient(90deg,var(--accent) 0 6px,transparent 6px 8px)}
.dmc-fait .dmc-bar i{width:100%}
.dmc-cours .dmc-bar i{width:50%}
/* la ligne d'une étape PLIE, elle ne s'élide pas : ce qui compte vient
   à la fin — « chez un ami de Léa » rendait « chez un ami de L… » même
   au poste, le seul mot qui dit qui remercier. Vide, elle garde sa
   hauteur : les trois barres restent alignées. */
.dmc-q{display:block;min-height:1.35em;font:var(--weight-regular) var(--text-12)/1.35 var(--font-mono);color:var(--text-muted);
  overflow-wrap:anywhere}
.dmc-cours .dmc-q{color:var(--accent)}
/* dans « Amis » : les demandes par métier et ville, au-dessus de la
   liste ; le geste qui en fait une, sous la liste */
.am-dem{margin:0 0 8px}
.am-dem + :not(.am-dem){margin-top:12px}
.am-demander{margin:12px 0 0}
/* à qui elle part, sous la phrase : des prénoms, rien d'autre */
.dm-qui{display:flex;align-items:center;gap:7px;margin:10px 0 0;color:var(--text-muted);font-size:var(--text-13)}
/* QUI demande, ou qui donne : la phrase que la barre portait, en tête du
   contenu — à la taille d'un nom, lisible, et elle plie */
/* à qui l'on écrit : la piste, en tête du composeur — elle PLIE, et le
   rang dans une série (« 2/5 ») se lit à droite, en donnée */
.ml-pour{display:flex;align-items:baseline;gap:10px;margin:0 0 10px;font-size:var(--text-14);line-height:1.3}
.ml-pour b{flex:1;min-width:0;font-weight:var(--weight-bold);overflow-wrap:anywhere}
.ml-n{flex:none;font-family:var(--font-mono);font-size:var(--text-12);color:var(--text-muted)}
.dn-n{margin:0 0 10px}
.qr-wrap + .dn-n{margin:8px 0 0}
.dm-de{margin:0 0 4px;font-size:var(--text-16);line-height:1.3;color:var(--text-body)}
.dm-de b{font-weight:var(--weight-bold)}
.dm-de + .dm-chez{margin-top:0}
.dm-qui + .pick-list{margin-top:10px}
/* « chez AZTEK SAS » continue le titre (« Inès cherche quelqu'un ») : il
   vient AVANT le contact, qui prend sa place d'objet */
.dm-chez{margin:0 0 12px}
/* PRÉSENTER AU LIEU DE DONNER (lot F3b) : la case et, au bout, le lien
   qui va demander — deux gestes aux conséquences différentes, donc un
   écart (§5, règle 3) ; à texte agrandi, le lien passe dessous */
.dr-accord{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;column-gap:16px;margin:12px 0 0}
/* chez qui reçoit : le contact attend son mail — un atout, l'accent */
.dm-accord{display:flex;align-items:center;gap:7px;margin:10px 0 0;color:var(--accent);
  font-size:var(--text-13);font-weight:var(--weight-semibold)}
/* un état d'un mot (« Déjà dans tes amis. ») : il se lit, il ne se tape pas */
/* L'ONGLET « AMIS » (lot 4) : la rangée d'une piste. Ce qu'un ami
   t'ouvre DANS TES PISTES prend l'accent — un atout, jamais un mark-* ;
   la ligne choisie, au poste, le liseré du châssis (« où tu es », §4). */
.am-row .am-tienne{color:var(--accent);font-weight:var(--weight-semibold)}
/* ce qui n'est pas lu se compte au bout de la ligne : la zone qui
   ouvre garde toute la ligne, le texte prend la place qui reste */
.am-row .ri-main.am-main{flex-direction:row;align-items:center;justify-content:flex-start;gap:10px}
.am-txt{flex:1;min-width:0;display:flex;flex-direction:column;justify-content:center}
.am-ecrire{margin:4px 0 12px}
/* une conversation avec un ami : le fil d'un groupe, sans prénom sur
   chaque bulle — on n'y est que deux — et ses retours à la ligne gardés */
.ms-m p{white-space:pre-line}
.ms-m .gr-qui{display:inline-flex;align-items:center;gap:4px}
.gr-moi.ms-m .gr-qui{align-self:flex-end}
/* une salve se serre : la bulle qui suit sans heure colle à sa voisine */
.ms-fil .ms-suite{margin-top:-8px}
.ms-vide{margin:24px 0;text-align:center;font-size:var(--text-13);color:var(--text-muted)}
.am-detail .ap-nom,.am-nom{margin-bottom:10px}
@media (min-width:901px){
  .am-sel .sw-in{background:var(--surface-chrome);box-shadow:inset 3px 0 0 var(--select-bg)}
}
/* « Ajouter un ami », en bout de liste : la ligne calme de « À trier »,
   en pointillé — un geste, pas un ami de plus */
.am-ajout{
  display:flex;align-items:center;gap:8px;width:100%;margin-top:8px;
  padding:0 14px;min-height:var(--ctl);cursor:pointer;
  background:none;border:1px dashed var(--border-strong);border-radius:var(--radius-0);
  font:inherit;font-size:var(--text-13);color:var(--text-muted);text-align:left;
}
.am-ajout:active{background:var(--surface-raised)}
@media (hover:hover){ .am-ajout:hover{background:var(--surface-raised);color:var(--text-body)} }
.am-etat{display:flex;align-items:center;gap:7px;margin:12px 0 0;color:var(--accent);font-size:var(--text-13);font-weight:var(--weight-semibold)}
/* ---------- LA BOÎTE « Échanger » (ui/conversations.js) ----------
   (13 octobre 2026 : « exactement comme les messageries
   traditionnelles »). UN cadre, une rangée par conversation, un
   pointillé entre elles — la rangée de §6. Chaque rangée : le visage,
   le nom (il plie, il ne se coupe pas : c'est l'identité), le dernier
   message (une donnée : il s'élide), l'heure et ce qui n'est pas lu.
   Ce qui attend prend le gras et son chiffre en accent ; le reste se
   tait. */
/* 4 px de marge intérieure : l'anneau de focus se pose DEHORS (2 px de
   trait à 2 px du bord) et `overflow` le rognerait là (§4) */
.bx-zone{flex:1;min-height:0;overflow:auto;-webkit-overflow-scrolling:touch;padding:4px;margin:-4px -4px 0;
  display:flex;flex-direction:column}
.bx-zone>*{flex:none}
/* au pouce, le cadre TIENT sa région (§5, le panneau) : la place sous la
   dernière conversation lui appartient, l'historique se pose dessous,
   juste au-dessus des deux gestes */
.bx-zone>.bx-liste{flex:1 0 auto}
.bx-filtres{margin:0 0 10px}
/* vide, la phrase ouvre le cadre, le geste suit */
.bx-rien{margin:0;padding:12px 12px 4px;font-size:var(--text-13);color:var(--text-muted)}
.bx-filtres .dchip:disabled{color:var(--text-disabled);border-style:dashed;cursor:default}
.bx-liste{
  display:flex;flex-direction:column;margin:0 0 16px;
  background:var(--surface-window);border:1px solid var(--border-strong);border-radius:var(--radius-0);
  box-shadow:var(--shadow-raised);
}
.bx-liste>*{position:relative}
.bx-liste>*+*::before{content:'';position:absolute;left:12px;right:12px;top:0;border-top:1px dashed var(--border-soft);pointer-events:none}
.bx-l{
  display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:4px 10px;
  width:100%;min-height:calc(var(--ctl) + 12px);padding:8px 12px;
  border:0;background:none;font:inherit;color:var(--text-body);text-align:left;cursor:pointer;
  -webkit-touch-callout:none;-webkit-user-select:none;user-select:none;
}
.bx-l:active{background:var(--surface-raised)}
@media (hover:hover){ .bx-l:hover{background:var(--surface-raised)} }
.bx-m{min-width:0;display:flex;flex-direction:column;gap:2px}
.bx-n{font-size:var(--text-14);font-weight:var(--weight-regular);line-height:1.3;overflow-wrap:anywhere;
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow:hidden}
.bx-p{font-family:var(--font-mono);font-size:var(--text-11);line-height:1.4;color:var(--text-muted);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.bx-nonlu .bx-n{font-weight:var(--weight-bold)}
.bx-nonlu .bx-p{color:var(--text-body)}
.bx-d{align-self:start;display:flex;flex-direction:column;align-items:flex-end;gap:5px;padding-top:2px}
.bx-d time{font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);white-space:nowrap}
.bx-i{display:flex;align-items:center;gap:5px;min-height:16px;color:var(--text-muted)}
/* au calme (R10) : un point d'accent, carré comme un pixel — « pas lu »,
   sans dire combien */
.bx-pt{display:block;width:8px;height:8px;background:var(--accent)}
/* un groupe où l'on a demandé à entrer : un état, pas une porte */
.bx-attente{cursor:default}
.bx-attente .bx-n{color:var(--text-muted)}
/* « Écrire à un ami », « Nouveau groupe » : les gestes qui commencent
   une conversation, en dernier, en encre sourde — la ligne « Ajouter un
   compte » d'iOS */
.bx-plus{
  display:flex;align-items:center;gap:8px;width:100%;min-height:var(--ctl);padding:0 12px;
  border:0;background:none;font:inherit;font-size:var(--text-13);color:var(--text-muted);text-align:left;cursor:pointer;
}
.bx-plus:active{background:var(--surface-raised)}
@media (hover:hover){ .bx-plus:hover{background:var(--surface-raised);color:var(--text-body)} }
/* « Mes amis », une ligne de la liste (R1) : elle MÈNE — son compte et
   son chevron, à l'encre du texte, comme une rangée de Réglages — et ne
   se lit pas comme les gestes sourds qui la suivent */
.bx-amis{
  display:flex;align-items:center;gap:8px;width:100%;min-height:var(--ctl);padding:0 12px;
  border:0;background:none;font:inherit;font-size:var(--text-14);color:var(--text-body);text-align:left;cursor:pointer;
}
.bx-amis-l{flex:1;min-width:0;text-align:left}
.bx-amis-n{font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted)}
.bx-amis:active{background:var(--surface-raised)}
@media (hover:hover){ .bx-amis:hover{background:var(--surface-raised)} }
/* « Mes demandes » en tête de Réseau : les cartes de la fiche, et le
   contact donné juste dessous */
.rz-dem{margin:0 0 16px}
/* PLUSIEURS DEMANDES (R10) : une ligne chacune dans un cadre — le dessin
   de la boîte, juste dessous —, l'entreprise en tête, et le suivi réduit
   à trois cases : pleine (faite), à moitié (en cours), vide (à venir).
   La même lecture que les barres de la carte, à la taille d'une ligne. */
.rz-liste{display:flex;flex-direction:column;margin-top:6px;
  background:var(--surface-window);border:1px solid var(--border-strong);box-shadow:var(--shadow-raised)}
.rz-liste>*{position:relative}
.rz-liste>*+*::before{content:'';position:absolute;left:12px;right:12px;top:0;border-top:1px dashed var(--border-soft);pointer-events:none}
.rz-l{display:grid;grid-template-columns:minmax(0,1fr) auto auto;align-items:center;gap:4px 10px;
  width:100%;min-height:calc(var(--ctl) + 12px);padding:8px 12px;
  border:0;background:none;font:inherit;color:var(--text-body);text-align:left;cursor:pointer}
.rz-l:active{background:var(--surface-raised)}
@media (hover:hover){ .rz-l:hover{background:var(--surface-raised)} }
.rz-m{min-width:0;display:flex;flex-direction:column;gap:2px}
.rz-m b{font-size:var(--text-14);line-height:1.3;overflow-wrap:anywhere}
.rz-s{font-family:var(--font-mono);font-size:var(--text-11);line-height:1.4;color:var(--text-muted);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rz-s-don{color:var(--accent);font-weight:var(--weight-bold)}
.rz-pas{display:flex;gap:3px}
.rz-pas i{display:block;width:10px;height:10px;border:1px solid var(--border-strong);background:var(--surface-field)}
.rz-pas i.rz-f{background:var(--accent);border-color:var(--accent)}
.rz-pas i.rz-c::after{content:'';display:block;width:50%;height:100%;background:var(--accent)}
.rz-dem .tr-h{margin-bottom:2px}
.rz-carte + .rz-carte,.rz-don + .rz-carte{margin-top:10px}
/* LES DEUX VERBES DE L'ÉCHANGE (décision 0008, 29 octobre 2026) :
   « Donner » et « Recevoir » côte à côte, là où sont les gens. Au pouce,
   sous la boîte, juste au-dessus de la barre — la place du pouce (§5) ;
   les deux pèsent pareil : ni l'un ni l'autre n'est l'action probable,
   c'est la situation qui choisit. Au poste, à droite du titre, comme
   « Écrire les N mails » sur « Aujourd'hui ». */
.rz-pied{flex:none;display:flex;gap:8px;padding:10px 0 2px}
.rz-pied>.btn{flex:1 1 0;min-width:0}
.rz-verbes{display:flex;gap:8px;margin-left:auto;align-self:center}
/* l'historique : une ligne qui mène à sa feuille */
.bx-histo{
  display:flex;align-items:center;gap:8px;width:100%;min-height:var(--ctl);padding:0 2px;margin:0 0 6px;
  border:0;border-top:1px dashed var(--border-soft);background:none;
  font:inherit;font-size:var(--text-13);color:var(--text-muted);text-align:left;cursor:pointer;
}
.bx-histo-l{flex:1;min-width:0;text-align:left}
.bx-histo-n{font-family:var(--font-mono);font-size:var(--text-11)}
@media (hover:hover){ .bx-histo:hover{color:var(--text-body)} }
/* les visages : la hauteur d'une rangée, des pixels nets (ui/visage.js) */
.vz{display:inline-flex;align-items:center;justify-content:center;flex:none;width:36px;height:36px;
  background:var(--surface-chrome);border:1px solid var(--border-strong);overflow:hidden}
.vz svg{display:block;width:100%;height:100%;shape-rendering:crispEdges;fill:currentColor}
.vz-0{color:var(--teal)}
.vz-1{color:var(--ink)}
.vz-2{color:var(--amber)}
.vz-3{color:var(--green)}
.vz-4{color:var(--red)}
.vz-5{color:var(--ink-2)}
/* un groupe : ses initiales en lettres pixel — jamais un visage */
.vz-g{font-family:var(--font-pixel);font-size:var(--pixel-16);color:var(--text-body)}
.bx-ami{display:flex;align-items:center;gap:10px}
/* « 3 nouveaux » : où l'on s'était arrêté, et le bouton qui y descend
   (ui/fil.js) — l'accent, parce que c'est ce qui a changé */
.fil-nouv{display:flex;align-items:center;gap:8px;margin:2px 0;list-style:none;
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--accent);text-transform:uppercase;letter-spacing:.06em}
.fil-nouv::before,.fil-nouv::after{content:'';flex:1;border-top:1px dashed var(--accent)}
/* le morceau d'avant (ui/fil.js, relevé à grande échelle) : en tête de
   la fenêtre, un lien qui déplie — rien d'autre ne bouge */
.fil-avant{display:flex;justify-content:center;list-style:none;margin:2px 0}
/* le jour, une fois, au milieu du fil (lot F2) : une étiquette sourde
   qui se lit, pas une ligne qu'on tape — le gris, puisque rien n'y a
   changé */
.fil-jour{display:flex;justify-content:center;margin:4px 0;list-style:none}
.fil-jour span{padding:2px 8px;border:1px solid var(--border-soft);background:var(--surface-window);
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted)}
/* un petit visage devant le prénom de qui écrit dans un groupe */
.vz.vz-s{width:16px;height:16px;border:0;background:none;vertical-align:-3px;margin-right:4px}
/* moyen : devant un nom de membre, en tête de la fiche d'un ami */
.vz.vz-m{width:24px;height:24px}
/* dans la barre d'une conversation : le visage de l'ami, sur sa case
   grise — le navy du châssis autour, comme l'icône d'une fenêtre 98 */
.modal-h .vz.vz-barre{width:22px;height:22px}
/* MON VISAGE (ui/mon-visage.js, 28 octobre 2026) : la grille des visages,
   en grand — une case est une cible (`--ctl`), allumée elle prend la
   teinte du dessin (la classe `vz-N` de la grille donne `currentColor`).
   Pas d'arrondi, pas de flou : des pixels, comme le visage lui-même. */
.mv-grille{display:grid;grid-template-columns:repeat(5,var(--ctl));gap:3px;justify-content:center;
  width:max-content;max-width:100%;margin:4px auto 0;padding:6px;border:1px solid var(--border-strong);background:var(--surface-chrome)}
/* `color:inherit` : un bouton ne prend pas la couleur de son parent (le
   navigateur lui donne la sienne) — sans lui, les cases s'allumaient en
   noir quelle que soit la teinte */
.mv-c{width:var(--ctl);height:var(--ctl);padding:0;color:inherit;border:1px solid var(--border-soft);background:var(--surface-field);cursor:pointer}
.mv-c.on{background:currentColor;border-color:currentColor}
.mv-c:active{transform:translate(1px,1px)}
/* six teintes : un carré plein chacune, la retenue cochée et bordée */
.mv-teintes{display:flex;justify-content:center;flex-wrap:wrap;gap:8px;margin:14px 0 0}
.mv-t{display:inline-flex;align-items:center;justify-content:center;width:var(--ctl);height:var(--ctl);padding:0;
  border:1px solid var(--border-strong);background:currentColor;cursor:pointer}
.mv-t .ic{color:var(--surface-window)}
/* la retenue : un double filet net (pas l'anneau de focus, qui garde le sien) */
.mv-t[aria-checked="true"]{box-shadow:0 0 0 2px var(--surface-window),0 0 0 4px var(--text-body)}
/* l'aperçu : la ligne où les amis le verront, à ses trois tailles */
.mv-apercu{display:flex;align-items:center;justify-content:center;gap:10px;margin:16px 0 0;font-size:var(--text-14)}
.mv-apercu b{font-weight:var(--weight-semibold)}
.mv-origine{display:flex;margin:14px auto 0}
.moi-visage .rg-s{display:inline-flex}
.mq-nom{display:inline-flex;align-items:center;gap:8px}
.gr-nom{display:inline-flex;align-items:center;gap:8px;min-width:0}
.am-nom{display:flex;align-items:center;gap:10px}
/* un lien dans un message : il emmène ailleurs, il se souligne — et une
   adresse longue plie au lieu d'élargir la bulle */
.lien-msg{color:var(--accent);text-decoration:underline;overflow-wrap:anywhere}
/* « @Karim » se lit comme un nom ; ce qui ME nomme prend un liseré
   d'accent sur la bulle (Slack, Discord) */
.gr-cite{font-weight:var(--weight-semibold);color:var(--accent)}
.gr-m.gr-pour-moi p{border-left:3px solid var(--accent)}
/* les prénoms qui s'ouvrent à « @ », au-dessus du champ */
.gr-propose{width:100%;margin:0}
/* une mention dans la boîte : l'arobase devant le chiffre, en accent */
.bx-at{font-family:var(--font-mono);font-size:var(--text-12);font-weight:var(--weight-bold);color:var(--accent)}
.fil-bas{
  position:sticky;bottom:8px;display:flex;align-items:center;gap:6px;margin:10px 0 0 auto;
  min-height:var(--ctl);padding:0 12px;width:max-content;max-width:100%;
  background:var(--accent);color:var(--on-accent);border:1px solid var(--border-strong);
  box-shadow:var(--shadow-raised);font:inherit;font-size:var(--text-13);font-weight:var(--weight-semibold);cursor:pointer;
}
@media (min-width:901px){
  /* LES DEUX VOLETS (Discord, Slack ; Material 3 « list-detail ») : la
     liste à gauche, la conversation choisie posée à droite, la même
     hauteur — chacun défile chez lui */
  #view-reseau>.page-inner.ec-boite{max-width:1180px}
  .bx-cols{flex:1;min-height:0;display:grid;grid-template-columns:minmax(17rem,22rem) minmax(0,1fr);gap:18px}
  .bx-gauche{min-height:0;overflow:auto;padding:4px;margin:-4px}
  .bx-seule{max-width:36rem}
  .bx-volet{min-height:0;display:flex}
  /* `min-width:0` à chaque étage : un enfant flex garde sinon la largeur
     MINIMALE de son contenu. La ligne des prénoms d'un groupe de 45
     (« Adam · Arthur · … · toi », insécable, élidée) en faisait 1 921 px :
     le volet sortait de l'écran, son champ et « Envoyer » avec, et le
     jour du fil, centré dans 2 003 px, tombait hors de vue (audit du
     28 octobre 2026). §4 : la cause type est toujours celle-là. */
  .bx-volet>.dock{flex:1;min-width:0;display:flex;min-height:0;animation:ec-arrive var(--dur-2) var(--ease-out) both}
  .dock>.modal{min-width:0;max-width:none;height:100%;max-height:none;box-shadow:var(--shadow-raised)}
  .dock>.modal-groupe{height:100%}
  /* « tu es ici » : la couleur du châssis, celle de l'onglet allumé (§4) */
  .bx-sel{background:var(--select-bg)!important}
  .bx-sel,.bx-sel .bx-p,.bx-sel .bx-d time,.bx-sel .bx-i{color:var(--select-text)}
}
/* LA FICHE À CÔTÉ DE LA LISTE (R1, décision 0003 — en préversion) : le
   motif des conversations, appliqué aux pistes. La page tient la hauteur
   de l'écran ; la liste défile à gauche, la fiche posée défile chez
   elle à droite, son pied « Écrire » toujours visible. La ligne choisie
   prend la couleur du châssis : « tu es ici » (§4). */
@media (min-width:901px){
  #view-pistes>.page-inner.pi-ld{display:flex;flex-direction:column;height:100%}
  .pi-ld>#piBody{flex:1;min-height:0}
  .pi-split{height:100%;display:grid;grid-template-columns:minmax(17rem,24rem) minmax(0,1fr);gap:18px}
  .pi-gauche{min-height:0;overflow:auto;padding:4px;margin:-4px}
  .pi-volet{min-height:0;display:flex}
  .pi-volet>.dock{flex:1;min-width:0;display:flex;min-height:0}
  /* le corps prend la place, le pied « Écrire » se pose en bas du volet */
  .pi-volet .modal-b{flex:1;min-height:0}
  .pi-vue .seg{flex:0 0 auto;padding:4px 12px}
  .row-item.pi-sel,.row-item.pi-sel .sw-in{background:var(--select-bg)}
  .row-item.pi-sel h3,.row-item.pi-sel .ri-sub,.row-item.pi-sel .ri-sub b{color:var(--select-text)}
  /* la croix de la ligne choisie vit sur le navy du châssis : elle en
     prend l'encre, et son anneau aussi — en rouge et en pointillé
     d'encre, elle rendait 1,7:1 sous le focus (e2e-focus, 7.0.0), la
     faute de la croix des feuilles une surface plus loin (§4) */
  .row-item.pi-sel .hov-del,.row-item.pi-sel .hov-del:focus-visible{color:var(--select-text);outline-color:var(--select-text)}
}
@media (min-width:901px) and (hover:hover){
  .row-item.pi-sel:hover .sw-in{background:var(--select-bg)}
}
/* la feuille d'un groupe : elle se donne sa hauteur (§6, « Écrire ») —
   le fil défile, le pied reste sous le pouce */
.modal-groupe{height:92dvh}
.modal-groupe .modal-b{flex:1;min-height:0}
/* UNE CONVERSATION PREND L'ÉCRAN, au pouce (audit au téléphone, 28
   octobre 2026). Posée à 92 % de la hauteur, elle laissait voir le haut
   de « Réseau » derrière elle : une bande qu'on ne peut pas taper, prise
   au fil, et une conversation qui se lisait comme une fenêtre posée sur
   la liste plutôt que comme l'écran qu'on vient d'ouvrir — toutes les
   messageries ouvrent la conversation en plein écran. Sa barre se pousse
   sous l'encoche ; clavier ouvert, elle prend toute la zone qu'il
   laisse, sans la marge des autres feuilles. */
@media (max-width:900px){
  .modal-groupe{height:100dvh;max-height:100dvh;border-width:0}
  .modal-groupe .modal-h{padding-top:calc(3px + env(safe-area-inset-top))}
  .clavier-ouvert .modal-groupe{height:var(--zone-h,100dvh);max-height:var(--zone-h,100dvh)}
}
/* la tête d'un groupe : sous la barre, hors du défilement (ui/groupes.js).
   Clavier ouvert, elle s'efface — on écrit, la place va au fil. */
.gr-tete{flex:none;padding:0 18px;background:var(--surface-window);border-bottom:1px solid var(--border-soft)}
.gr-tete:empty{display:none}
.clavier-ouvert .gr-tete{display:none}
.gr-membres{
  display:flex;align-items:center;gap:8px;width:100%;min-height:var(--ctl);padding:4px 0;margin:0;
  border:0;background:none;
  font:inherit;font-size:var(--text-13);color:var(--text-muted);text-align:left;cursor:pointer;
}
.gr-tete>*+*{border-top:1px dashed var(--border-soft)}
.gr-membres-t{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left}
.gr-ephem{display:flex;align-items:center;justify-content:center;gap:6px;margin:10px 0;
  font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);text-align:center}
.gr-fil{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}
.gr-m{display:flex;flex-direction:column;gap:2px;max-width:85%;align-self:flex-start}
.gr-m.gr-moi{align-self:flex-end;text-align:right}
.gr-qui{font-size:var(--text-12);color:var(--text-muted)}
.gr-qui b{color:var(--text-body);font-weight:var(--weight-semibold)}
.gr-qui time{font-family:var(--font-mono);font-size:var(--text-11)}
.gr-m p{margin:0;padding:8px 12px;border:1px solid var(--border-strong);background:var(--surface-chrome);
  font-size:var(--text-14);line-height:1.45;overflow-wrap:anywhere;text-align:left}
.gr-moi p{background:var(--teal-wash);border-color:var(--accent)}
.gr-don p{display:flex;align-items:center;gap:6px;font-weight:var(--weight-semibold)}
.gr-don .btn-sm{align-self:flex-start;margin-top:4px}
.gr-pris{display:flex;align-items:center;gap:6px;margin-top:4px;color:var(--accent);font-size:var(--text-13);font-weight:var(--weight-semibold)}
/* le pied d'un groupe : Donner, le champ, Envoyer. À texte agrandi le
   champ passe sur sa propre ligne plutôt que de se serrer entre deux
   boutons — sa base est en `rem`, elle suit la police toute seule */
.gr-pied{display:flex;align-items:flex-end;flex-wrap:wrap;gap:8px;width:100%}
/* six lignes au plus, puis le champ défile chez lui : au-dessus de lui
   vit la conversation à laquelle on répond (Messages, WhatsApp). C'est
   l'exception nommée de `champGrandit` (§6) — un message de mille
   signes poussait le pied jusqu'à la barre et le fil disparaissait. */
.gr-pied .gr-champ{flex:1 1 7rem;min-width:0;min-height:var(--ctl);padding:10px 12px;line-height:1.4;resize:none;
  overflow-x:hidden;overflow-y:auto;max-height:calc(6 * 1.4em + 22px);
  /* le dessin d'un champ, et SA taille : sans elle, le navigateur donnait
     13 px fixes — sourd au texte agrandi, et sous les 16 px qui évitent le
     zoom d'iOS à la mise au point (§5) — audit 7.1.0 */
  font-family:var(--font-ui);font-size:var(--input-fs);color:var(--text-body);
  background:var(--surface-field);border:1px solid var(--border-field);
  box-shadow:var(--bevel-field);border-radius:var(--radius-1)}
.gr-pied .btn{flex:none}
.gr-inviter{display:flex;flex-direction:column;align-items:center;gap:8px;margin:4px 0 8px}
/* « Inviter » : le libellé d'une section (celui de « Contacts », « Où
   j'en suis »), calé à gauche au-dessus du QR qu'il nomme */
.gr-inv-t{align-self:stretch;font-size:var(--text-11);font-weight:var(--weight-bold);
  letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--text-muted)}
.gr-inviter .qr-wrap{margin:0 auto;width:100%;max-width:240px;padding:12px}
.gr-code{display:flex;align-items:baseline;gap:8px;margin:0;font-size:var(--text-13);color:var(--text-muted)}
.gr-code b{font-family:var(--font-mono);font-size:var(--text-16);color:var(--text-body);letter-spacing:.04em}
.gr-noms{list-style:none;margin:0 0 12px;padding:0}
.gr-noms li{display:flex;align-items:center;min-height:var(--ctl);border-bottom:1px dashed var(--border-soft);font-size:var(--text-14)}
.gr-etat{margin:8px 0 0;font-size:var(--text-14);color:var(--text-body)}
.gr-entete{margin:0 0 6px}
/* une demande : la phrase, puis ce qui la concerne — relayée par qui,
   ce que MON téléphone a trouvé (en accent : c'est ce qui change), et
   le geste. Trouvée par moi, elle prend le bord d'accent. */
.gr-dem p{display:flex;align-items:flex-start;gap:6px;font-weight:var(--weight-semibold)}
.gr-trouvee p{border-color:var(--accent);box-shadow:inset 3px 0 0 var(--accent)}
.gr-sous{font-size:var(--text-12);color:var(--text-muted)}
.gr-tu{display:flex;align-items:center;gap:6px;color:var(--accent);font-size:var(--text-13);font-weight:var(--weight-semibold)}
.gr-dem .btn-sm{align-self:flex-start;margin-top:4px}
.gr-moi.gr-dem .btn-sm{align-self:flex-end}
.gr-rep{margin-top:12px}
/* LOT 5c — le groupe « sur demande ».
   Qui attend à la porte : la même bulle qu'une demande trouvée (le bord
   d'accent : quelqu'un attend, c'est à moi), ses deux gestes dessous. */
.gr-entree{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:6px 10px;
  padding:6px 0 6px 10px;box-shadow:inset 3px 0 0 var(--accent)}
.gr-entree p{display:flex;align-items:center;gap:6px;margin:0;min-width:0;font-size:var(--text-14)}
.gr-entree p b{font-weight:var(--weight-semibold)}
.gr-entree-g{display:flex;flex-wrap:wrap;gap:8px;margin-left:auto}
/* la demande trouvée par MON téléphone, en tête : toute la largeur */
.gr-tete .gr-entete{margin:6px 0}
.gr-tete .gr-m{max-width:none}
/* qui ouvre la porte se lit à côté du prénom ; le geste au bout */
.gr-noms li{justify-content:space-between;gap:8px;flex-wrap:wrap}
.gr-noms li>span{min-width:0;overflow-wrap:anywhere}
.gr-role{font-family:var(--font-mono);font-size:var(--text-12);color:var(--text-muted)}
/* LOT 5c2 — chez un responsable, la ligne d'un autre membre s'ouvre sur
   ses gestes (« Nommer adjoint », « Retirer du groupe ») : la ligne
   entière est la cible, le chevron dit qu'elle s'ouvre */
.gr-membre{
  display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;min-height:var(--ctl);padding:0;
  border:0;background:none;font:inherit;color:inherit;text-align:left;cursor:pointer;
}
.gr-membre>span{min-width:0;overflow-wrap:anywhere}
.gr-membre .ic{flex:none;color:var(--text-muted)}
/* LES RÉGLAGES D'UN GROUPE (décisions 31 à 35) : les rangées de « Moi ›
   Réglages », dans un cadre — un nom, son état, un chevron */
.gr-regl,.gr-actions{margin:0 0 12px;padding:0 12px}
/* « qui écrit : les responsables » : l'état prend la place du champ,
   en encre sourde, à côté de « Donner » qui reste */
.gr-seuls{flex:1 1 10rem;margin:0;align-self:center;font-size:var(--text-13);color:var(--text-muted)}
#grNouvelleInv{display:flex;margin:16px auto 0}
/* « Groupe » : un titre de section au-dessus de chaque cadre, comme les
   tranches d'« Aujourd'hui » — le compte des membres dans sa pastille */
.gr-sec{margin:16px 0 8px}
.gr-membre:active{background:var(--surface-raised)}
@media (hover:hover){ .gr-membre:hover{background:var(--surface-raised)} }
/* un message retiré garde sa place, en gris : qui l'a retiré, rien de
   ce qu'il disait */
.gr-retire p{display:flex;align-items:center;gap:6px;background:none;border-style:dashed;color:var(--text-muted);font-size:var(--text-13)}
/* le geste « Retirer » sur une bulle (le motif de la suppression) : la
   bulle reste une colonne, la croix au survol se pose dans son coin */
.gr-m.sw>.sw-in{display:flex;flex-direction:column;gap:2px;background:transparent}
.gr-m .hov-del{position:absolute;top:0;right:0}
.gr-m.gr-moi .hov-del{right:auto;left:0}
.rc-de{display:flex;align-items:center;gap:7px;margin:0 0 8px;font-size:var(--text-13);color:var(--text-muted)}
.scan-box{
  position:relative;background:#000;border:1px solid var(--border-strong);
  box-shadow:var(--shadow-raised);overflow:hidden;aspect-ratio:1/1;max-height:46dvh;margin-bottom:10px;
}
.scan-box video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.scan-mark{
  position:absolute;inset:14%;border:2px solid #fff;
  clip-path:polygon(0 0,26% 0,26% 6%,6% 6%,6% 26%,0 26%,0 0,
    100% 0,100% 26%,94% 26%,94% 6%,74% 6%,74% 0,
    100% 100%,74% 100%,74% 94%,94% 94%,94% 74%,100% 74%,
    0 100%,0 74%,6% 74%,6% 94%,26% 94%,26% 100%);
  opacity:.9;pointer-events:none;
}
.rc-recap{text-align:left}
.rc-big{
  font-size:var(--text-16);font-weight:var(--weight-bold);letter-spacing:var(--tracking-tight);
  margin-bottom:10px;
}
.rc-lines{list-style:none;margin:0 0 12px;padding:0;display:flex;flex-direction:column;gap:6px}
.rc-lines li{
  display:flex;align-items:center;gap:8px;font-size:var(--text-13);
  padding:7px 10px;background:var(--surface-chrome);border:1px solid var(--border-soft);border-radius:var(--radius-1);
}
.rc-lines .rc-warn{color:var(--amber);border-color:var(--amber);background:var(--amber-wash)}
.undo-bar{
  position:fixed;left:50%;transform:translateX(-50%);z-index:3600;
  bottom:calc(var(--nav-h) + 16px + env(safe-area-inset-bottom));
  display:flex;align-items:center;gap:12px;width:calc(100% - 24px);max-width:480px;
  background:var(--surface-window);border:1px solid var(--border-strong);
  box-shadow:var(--shadow-modal);padding:9px 12px;
  font-family:var(--font-mono);font-size:var(--text-12);line-height:1.45;
  animation:oc-up var(--dur-3) var(--ease-out);   /* la barre se pose (#23) */
}
.undo-bar span{flex:1;min-width:0}
.undo-bar .btn{flex:none}
/* LE TOAST A UN SEUL ANCRAGE À LA FOIS : le bas, ou le haut quand une
   feuille est ouverte (`body:has(> .overlay) .toast`, plus bas). Chaque
   règle qui le déplace dit donc de quel côté elle joue. Posés ensemble,
   haut ET bas étirent la boîte entre les deux : photographié le
   30 septembre 2026, une colonne de 641 px sur les champs de la
   capture, juste après la toute première piste — la règle du pied
   d'« Aujourd'hui », qui porte un id, l'emportait sur `bottom:auto`.
   Gardé par e2e-parcours-neuf (le toast n'est jamais plus haut que son
   texte, dans chaque état et chaque combinaison). */
/* CHAQUE `:has()` DE CE BLOC DIT LE CHEMIN (`>`), jamais « quelque part
   dessous » (porte « Performance »). La feuille, la barre Annuler et le toast sont des
   enfants directs de `<body>`, l'avertissement vit dans `.app` : sans le
   chemin, chaque calcul du toast parcourait TOUTE la page pour savoir
   s'il y avait une feuille — douze fois pendant le seul tap qui ouvre
   un groupe, le sélecteur le plus cher de l'app (statistiques de
   sélecteurs de Chromium). Avec, il regarde une vingtaine d'enfants.
   Une feuille posée ailleurs que sous `<body>` ne déplacerait plus le
   toast : `openSheet` est le seul à en poser une. */
/* le toast laisse la place à la barre Annuler quand elle est là */
body:not(:has(> .overlay)):has(> .undo-bar) .toast{bottom:calc(var(--nav-h) + 76px + env(safe-area-inset-bottom))}
/* … et à un PIED posé au pouce, qui occupe la même bande. Mesuré sur
   « Aujourd'hui » : 34 px de recouvrement, et un doigt visant « 1 piste
   sans prochaine action → » touchait LE TOAST — juste après avoir ajouté
   une piste, donc au moment précis où ce lien est le prochain geste.
   Tout écran qui descend une action au pouce doit repasser ici. */
@media (max-width:900px){
  body:not(:has(> .overlay)):has(> .app > .main > #view-aujourdhui:not([hidden]) > .page-inner > .td-under) .toast{
    /* 88 et non 76 : à 76 il restait 8 px de recouvrement sur le bord
       HAUT du lien — le centre était libre, le bord non */
    bottom:calc(var(--nav-h) + 88px + env(safe-area-inset-bottom));
  }
}
@media (min-width:901px){
  .undo-bar{bottom:40px}
  body:not(:has(> .overlay)):has(> .undo-bar) .toast{bottom:100px}
}
/* … et elle suit le toast quand une FEUILLE est ouverte : en haut, dans
   la bande que la feuille laisse au voile. Ancrée en bas, elle se posait
   sur le corps de la feuille — relevé à l'audit d'octobre 2026 : sur
   « Mes appareils », elle cachait « Rejoindre » ; sur une feuille haute,
   elle tombait sur le pied, à l'endroit exact où le pouce cherche
   « Enregistrer ». Un seul ancrage à la fois, comme le toast : le bas
   se libère (`bottom:auto`). Le toast descend alors d'une voie. */
body:has(> .overlay) .undo-bar{bottom:auto;top:calc(env(safe-area-inset-top) + 8px)}
body:has(> .undo-bar):has(> .overlay) .toast{top:calc(env(safe-area-inset-top) + 82px)}

/* ============ ALERTE SAUVEGARDE / TOAST ============ */
/* L'avertissement le plus grave de l'app : ce que l'étudiant saisit
   n'ira nulle part. Il est PERSISTANT — un toast de trois secondes ne
   passe donc ni devant lui ni sur son bouton de secours (mesuré : 54 px
   de recouvrement, et un doigt au centre de « Garder une copie »
   touchait le toast). D'où z-index au-dessus, et une largeur explicite :
   à 195 px il se pliait en 110 px de haut et devenait illisible. */
.save-warn{
  position:fixed;top:56px;left:50%;transform:translateX(-50%);z-index:4200;
  background:var(--red-wash);color:var(--red);border:1px solid var(--red);
  box-shadow:var(--shadow-modal);
  font-weight:var(--weight-semibold);font-size:var(--text-13);padding:10px 14px;
  display:flex;gap:10px;align-items:center;flex-wrap:wrap;
  width:calc(100% - 24px);max-width:520px;
}
.save-warn span{flex:1 1 190px;min-width:0}
.save-warn button{flex:none;margin-left:auto}
/* le toast s'efface devant lui : il descend d'une voie tant qu'il est là.
   Seulement quand il est ancré en HAUT (feuille ouverte) : ancré en bas,
   il ne le croise pas, et un `top` posé en plus l'étirait jusqu'au pied. */
body:has(> .app > #saveWarn:not([hidden])):has(> .overlay) .toast{top:calc(env(safe-area-inset-top) + 148px)}
/* la barre Annuler, ancrée en haut sous une feuille, fait de même : elle
   passe sous l'avertissement, et le toast sous elle */
body:has(> .app > #saveWarn:not([hidden])):has(> .overlay) .undo-bar{top:calc(env(safe-area-inset-top) + 128px)}
body:has(> .app > #saveWarn:not([hidden])):has(> .undo-bar):has(> .overlay) .toast{top:calc(env(safe-area-inset-top) + 202px)}
.save-warn button{
  background:var(--surface-window);border:1px solid var(--red);color:var(--red);
  border-radius:var(--radius-1);padding:5px 11px;font-weight:var(--weight-bold);min-height:var(--ctl);
  box-shadow:var(--bevel-up);
}
.save-warn button:active{box-shadow:var(--bevel-down)}
.toast{
  position:fixed;bottom:calc(var(--nav-h) + 20px + env(safe-area-inset-bottom));
  left:50%;transform:translateX(-50%) translateY(4px);z-index:4000;
  display:flex;align-items:center;gap:8px;
  background:var(--surface-window);color:var(--text-body);
  border:1px solid var(--border-strong);box-shadow:var(--shadow-raised);
  font-family:var(--font-mono);font-size:var(--text-12);padding:8px 12px;
  opacity:0;pointer-events:none;transition:opacity var(--dur-2) var(--ease-out),transform var(--dur-2) var(--ease-out);
  /* LA LARGEUR DE SON TEXTE, PAS LA MOITIÉ DE L'ÉCRAN. Posé à `left:50%`,
     un élément fixe ne dispose que de la moitié droite pour se
     dimensionner : « Rien à donner pour l'instant — ajoute d'abord une
     piste. » pliait sur trois rangs dans une boîte de 150 px, au milieu
     d'un écran de 390 (audit du 28 octobre 2026). `max-content` le
     mesure sur son texte, le plafond le garde dans l'écran. */
  width:max-content;max-width:calc(100% - 40px);text-align:left;line-height:1.5;
}
.toast::before{content:'';width:8px;height:8px;flex:none;border:1px solid var(--border-strong);background:var(--teal)}
.toast.on{opacity:1;transform:translateX(-50%) translateY(0);pointer-events:auto}
/* fermer une barre transitoire : balayer (tactile) / ✕ (desktop) */
.bar-x{
  display:none;flex:none;width:28px;height:28px;align-items:center;justify-content:center;
  background:none;border:1px solid var(--border-soft);border-radius:var(--radius-1);
  color:var(--text-muted);font-size:12px;line-height:1;padding:0;
}
@media (hover:hover){
.bar-x:hover{background:var(--surface-chrome);color:var(--text-body)}
}
@media (hover:hover){ .bar-x{display:inline-flex} }
body:has(> .overlay) .toast{bottom:auto;top:calc(env(safe-area-inset-top) + 62px)}
@media (min-width:901px){ .toast{bottom:44px} }

/* ============ MOTION (#23 : objets nets, déplacements doux) ============
   Le contraste est la signature : le skin « 98 » reste figé, seul le
   mouvement entre états est doux — feuille qui monte, fenêtre qui se
   pose, liste qui se réorganise (softReorder), barre qui arrive. Court,
   ease-out, transform/opacity seulement ; reduced-motion coupe tout
   (base.css). Les objets, eux, gardent steps() — la secousse du verrou. */
@keyframes oc-fade{from{opacity:0}}
/* `translate` et pas `transform` : c'est une propriété SÉPARÉE, qui se
   compose avec `transform`. Une fenêtre qu'on a déplacée porte déjà un
   `transform` en ligne ; une image-clé qui écrit `transform` l'écrasait,
   et la fenêtre revenait au centre le temps de l'animation. */
@keyframes oc-up{from{opacity:0;translate:0 12px}}
@keyframes oc-down{from{opacity:0;translate:0 -12px}}
/* ancrée en haut, la barre Annuler arrive du haut : d'où elle vient (§4 ①) */
body:has(> .overlay) .undo-bar{animation-name:oc-down}
.overlay{animation:oc-fade var(--dur-2) var(--ease-out)}
.overlay .modal{animation:oc-up var(--dur-3) var(--ease-out)}
.view:not([hidden])>.page-inner{animation:oc-fade var(--dur-2) var(--ease-out)}

/* ---- la sortie (règle du pic et de la fin) ----
   Une expérience se retient par son sommet et par sa FIN (Kahneman) —
   et la fin d'une feuille, c'est sa fermeture, répétée des dizaines de
   fois par session. Elle s'évaporait d'un coup.
   Elle part maintenant VERS LE BAS : la même direction que le pouce qui
   la pousse. Michotte l'a montré sur des carrés qui s'entrechoquent —
   un mouvement n'est perçu comme CAUSÉ par le geste que s'il part sans
   délai et dans le même sens. */
@keyframes oc-fade-out{to{opacity:0}}
@keyframes oc-sheet-out{to{opacity:0;translate:0 18px}}
.overlay.ov-out{pointer-events:none;animation:oc-fade-out var(--dur-out) var(--ease-in) forwards}
.overlay.ov-out .modal{animation:oc-sheet-out var(--dur-out) var(--ease-in) forwards}

/* ---- « ça a changé pendant que tu ne regardais pas » ----
   Un changement instantané n'est pas « moins joli » : il n'est PAS VU.
   On modifie une piste dans sa fiche, on referme, et la ligne derrière
   est silencieusement différente — l'action ne se voit jamais atterrir.
   Un lavis d'accent, une fois, sur la seule ligne concernée. C'est
   l'encre qui va à ce qui change (§6), sur le canal le plus fort de
   l'interface — donc à ne dépenser que là. Ce n'est PAS le langage
   d'urgence : `mark-*` reste réservé à ce qui réclame une action. */
@keyframes oc-vu{0%{opacity:0} 18%{opacity:.24} 100%{opacity:0}}
.vu-change::after{
  content:'';position:absolute;inset:0;z-index:2;pointer-events:none;
  background:var(--accent);opacity:0;
  animation:oc-vu var(--dur-vu) var(--ease-out);
}
/* un champ que l'app vient de remplir (`montrerRemplis`) : le lavis
   couvre le libellé et la valeur, il lui faut son repère */
.field.vu-change{position:relative}

/* ============ LE VERROUILLAGE (profil protégé) ============
   Plein écran au-dessus de tout ; pavé 44 px au pouce (mobile),
   saisie clavier (ordinateur). Bevels francs, steps() — pas de flou. */
/* Tant que l'app est verrouillée, rien de ce qui flotte ne se voit :
   le toast et la barre « Annuler » portent des noms de pistes, et
   l'écran de verrouillage est censé ne rien montrer. Masqués, pas
   détruits — leur minuterie continue derrière. */
html.oc-locked .toast,
html.oc-locked .undo-bar{display:none}
.lock{
  /* au-dessus de l'en-tête (1300) et de la nav (1500), sous les
     feuilles (3000) — « Code oublié ? » s'ouvre par-dessus */
  position:fixed;inset:0;z-index:2500;display:flex;align-items:center;justify-content:center;
  background:var(--surface-desktop);
}
.lock-in{display:flex;flex-direction:column;align-items:center;gap:10px;padding:24px;width:min(340px,92vw)}
.lock-title{font-family:var(--font-pixel);font-size:24px;letter-spacing:.02em;color:var(--text-body)}
.lock-state{font-family:var(--font-mono);font-size:var(--text-12);color:var(--text-muted);text-transform:uppercase;letter-spacing:.14em}
.lock-body{display:flex;flex-direction:column;align-items:center;gap:6px;width:100%}
.lock-dots{display:flex;gap:12px;justify-content:center;padding:16px 0 10px}
.lock-dots .dot{
  width:12px;height:12px;border:1px solid var(--border-strong);
  background:var(--surface-field);box-shadow:var(--bevel-field);
}
.lock-dots .dot.on{background:var(--accent);box-shadow:none}
.lock-err{animation:lock-shake .28s steps(4)}
@keyframes lock-shake{
  25%{transform:translateX(-7px)} 50%{transform:translateX(7px)}
  75%{transform:translateX(-4px)} 100%{transform:translateX(0)}
}
.lock-pad{display:grid;grid-template-columns:repeat(3,72px);gap:10px;justify-content:center}
.pad-k{
  height:56px;font-family:var(--font-mono);font-size:20px;font-weight:var(--weight-semibold);
  color:var(--text-body);background:var(--surface-raised);
  border:1px solid var(--border-strong);box-shadow:var(--bevel-up);
  display:flex;align-items:center;justify-content:center;
}
.pad-k:active{box-shadow:var(--bevel-down);transform:translate(1px,1px)}
.pad-side{color:var(--text-muted)}
.pad-off .pad-k{opacity:.45;pointer-events:none}
.lock-msg{min-height:18px;font-size:var(--text-12);color:var(--amber);margin:2px 0 0;text-align:center}
.modal .lock-pad{grid-template-columns:repeat(3,64px)}
.modal .pad-k{height:48px}
@media (min-width:901px){
  .lock-pad{grid-template-columns:repeat(3,64px);gap:8px}
  .pad-k{height:44px;font-size:17px}
}
/* la phrase de secours : 12 mots numérotés, mono, à recopier */
.phrase-grid{
  display:grid;grid-template-columns:1fr 1fr;gap:6px 16px;margin:0 0 12px;
  padding:12px 14px 12px 34px;border:1px solid var(--border-strong);
  background:var(--surface-field);box-shadow:var(--bevel-field);
  font-family:var(--font-mono);font-size:var(--text-14);
}
.phrase-grid li{padding:2px 0}
.phrase-grid li::marker{color:var(--text-muted);font-size:var(--text-11)}
/* les trois lignes de valeur du parcours de création */
/* Les « pourquoi » : du texte, pas des boutons. Ils portaient bordure et
   fond surélevé, exactement l'habit d'un `.pick` — on tapait dessus pour
   rien. Une phrase avec son pictogramme suffit ; les boutons redeviennent
   les seules choses qui ont l'air de se taper. */
.lk-whys{display:flex;flex-direction:column;gap:6px}
.lk-why{display:flex;align-items:flex-start;gap:10px;line-height:var(--leading-body)}
.lk-why .ic{color:var(--accent);flex:none;margin-top:3px}
/* anneau d'appareils : rôles et lignes ouvrables */
.tag-main{
  display:inline-block;padding:1px 6px;margin-left:6px;
  font-family:var(--font-mono);font-size:var(--text-11);
  color:var(--on-accent);background:var(--accent);border:1px solid var(--border-strong);
}
.dev-open{
  width:100%;text-align:left;background:none;border:0;cursor:pointer;
  color:inherit;font:inherit;
}
@media (hover:hover){
.dev-open:hover{background:var(--surface-chrome)}
}
.pick-danger b{color:var(--red)}
/* UN GESTE QUI CHANGE DES DONNÉES EST UN BOUTON, et un geste destructif
   a son propre groupe. Il vivait en mot souligné — la forme d'un lien,
   qui promet d'emmener ailleurs — sous la liste qu'il côtoyait (NN/g :
   un lien emmène, un bouton agit). Il devient une ligne `.pick-danger`,
   pictogramme ET mot, séparée des autres gestes par plus que leur écart
   ordinaire : deux conséquences opposées ne partagent pas une arête
   (§5-3), et le destructif ne se pose jamais là où le pouce tape par
   habitude (Apple). En rangée (`.pick-danger`) quand il continue une
   liste — Effacer cet appareil, Effacer ses données ; en bouton à sa
   taille (`.btn-danger`) quand il est seul — Rompre le lien, Ne plus
   protéger, Restaurer cette copie, Retirer un appareil. */
.pick-sortie{margin-top:16px}
/* une ligne d'appareil s'ouvre : le chevron le dit, comme partout */
.dev-open>.ic{flex:none;color:var(--text-muted)}
/* campagnes : la ligne groupée d'Aujourd'hui + la feuille du jour */
.camp-line{
  display:flex;align-items:center;gap:8px;width:100%;margin:0 0 10px;
  padding:10px 12px;min-height:var(--ctl);text-align:left;
  background:var(--surface-raised);color:var(--text-body);
  border:1px solid var(--border-strong);border-left:3px solid var(--accent);
  box-shadow:var(--bevel-up), var(--shadow-raised);
  font-size:var(--text-13);font-weight:var(--weight-semibold);
}
.camp-line:active{box-shadow:var(--bevel-down);transform:translate(1px,1px)}
.camp-line .ic{color:var(--accent)}
.camp-line span{flex:1;min-width:0;text-align:left}   /* jamais tronquée (#10) */
.camp-line em{font-style:normal;font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-link)}
/* la ligne d'une campagne : le nom garde toute sa largeur, l'état passe
   dessous, la marque tient la place forte à droite — même grammaire
   qu'une ligne de « Mes pistes », rien ne se replie */
.cp-row{align-items:center;gap:10px}
.cp-m{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;text-align:left}
.cp-m b{display:flex;align-items:center;gap:7px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cp-sub{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.camp-send{border:1px solid var(--border-soft);background:var(--surface-raised);margin-bottom:7px}
.camp-send summary{
  display:flex;align-items:center;gap:10px;list-style:none;cursor:pointer;
  padding:9px 12px;min-height:var(--ctl);
}
.camp-send summary::-webkit-details-marker{display:none}
.camp-send .cs-m{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.camp-send .cs-m b{font-size:var(--text-13)}
.camp-send .cs-sub{font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted)}
.camp-send .cs-body{
  white-space:pre-wrap;padding:10px 12px;margin:0 10px 10px;
  border:1px solid var(--border-soft);background:var(--surface-field);
  box-shadow:var(--bevel-field);font-size:var(--text-12);line-height:1.55;
  max-height:220px;overflow-y:auto;
}
/* les personnes visées par une campagne (#1) : une entreprise, ses gens,
   et le geste qui n'arrête que celle qui a répondu */
.cw-grp{margin-top:10px}
.cw-grp:first-of-type{margin-top:4px}
.cw-h{
  display:flex;align-items:center;justify-content:space-between;gap:8px;
  font-size:var(--text-13);
}
.cw-h .linklike{flex:none}
.cw-n{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--text-13)}
.cw-grp .ec-row .cs-sub{
  flex:none;font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);
}
.cz-recap{border:1px solid var(--border-strong);background:var(--surface-raised);padding:12px 14px;margin-bottom:12px}
.cz-recap>b{display:block;margin-bottom:8px}
.cz-lines{display:flex;flex-direction:column;gap:5px;font-size:var(--text-12);color:var(--text-muted)}
.cz-lines .ic{color:var(--accent)}
.cz-preview{padding:8px 0;border-bottom:1px dashed var(--border-soft);font-size:var(--text-12)}
.cz-preview:last-child{border-bottom:0}
.cz-preview .cz-subj{color:var(--text-muted);font-family:var(--font-mono);font-size:var(--text-11)}

/* le compte d'« Affiner » dans les feuilles : la place va à la liste,
   le bouton dit seulement combien de réglages sont actifs (#8) */
.icon-btn.af-btn{width:auto;padding:0 8px}
.af-n{
  margin-left:5px;padding:0 5px;min-width:16px;
  display:inline-flex;align-items:center;justify-content:center;
  background:var(--accent);color:var(--on-navy);
  font-family:var(--font-mono);font-size:var(--text-11);line-height:1.6;
  border-radius:var(--radius-1);
}

/* ============ R3-b : LA SÉRIE AU POSTE (ui/serie.js, décision 0003) ============
   Une fenêtre, trois colonnes : la file, le mail, ce qui aide à l'écrire.
   Le mail est le composeur de toujours, posé dans sa colonne : sa barre
   de titre redirait celle de la fenêtre (« Écrire » deux fois), elle part.
   La colonne de droite n'existe que si elle a quelque chose à dire. */
.modal-serie{width:min(1240px,96vw);max-width:none;height:min(92dvh,900px)}
.modal-serie>.modal-b{flex:1;min-height:0;padding:0;overflow:hidden}
.se-grille{display:grid;grid-template-columns:minmax(11rem,14rem) minmax(0,1fr) auto;height:100%;min-height:0}
.se-file{border-right:1px solid var(--border-soft);overflow:auto;padding:8px 0}
.se-liste{list-style:none;margin:0;padding:0}
.se-l{display:flex;flex-direction:column;align-items:flex-start;gap:2px;width:100%;min-height:var(--ctl);
  padding:8px 12px;background:none;border:0;border-bottom:1px dashed var(--border-soft);text-align:left;color:var(--text-body)}
.se-l .se-n{font-size:var(--text-14);font-weight:var(--weight-semibold);line-height:1.3}
.se-l .se-e{font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted)}
/* « où tu es » : la couleur du châssis, comme la piste choisie de la liste */
.se-l.on{background:var(--select-bg);color:var(--select-text)}
.se-l.on .se-e{color:inherit}
.se-l:focus-visible{outline:var(--focus-outline);outline-offset:-2px}
.se-mail{display:flex;min-height:0;min-width:0}
.se-mail>.dock{flex:1;display:flex;min-height:0}
.se-mail>.dock>.modal{box-shadow:none;border:0}
.se-mail>.dock>.modal>.modal-h{display:none}
.se-aide{width:18rem;overflow:auto;padding:12px 14px;border-left:1px solid var(--border-soft);
  display:flex;flex-direction:column;gap:14px}
.se-aide:empty{display:none}
.se-aide .ml-know{margin:0}
.se-h{font-size:var(--text-11);text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);margin:0 0 6px;font-weight:var(--weight-semibold)}
.se-notes{margin:0;white-space:pre-line;font-size:var(--text-13)}
.se-hist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px;font-size:var(--text-13)}
.se-d{font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted)}

/* ============ R4 : MA SEMAINE (ui/semaine.js) ============
   Une ligne en tête d'« Aujourd'hui », qui se touche (le chevron le dit)
   : le mot, le compte, une barre 98 qui se remplit. Un nombre, jamais
   un retard : la barre n'a qu'une couleur, l'accent, et rien ne rougit
   quand elle reste courte. */
/* elle se replie plutôt que de sortir : à 320 px ou texte doublé, les
   nombres passent dessous — DANS `.ts-dit` ; le chevron reste à côté,
   au bord droit de la LIGNE. Replié avec les mots, il tombait seul sur
   un rang à lui, 327 px de vide à sa gauche (e2e-decroche, 125 %). */
.td-semaine{display:flex;align-items:center;gap:10px;width:100%;min-height:var(--ctl);margin:0 0 6px;
  padding:6px 10px;background:var(--surface-window);border:1px solid var(--border-soft);text-align:left;color:var(--text-body)}
.td-semaine .ts-dit{flex:1;min-width:0;display:flex;flex-wrap:wrap;align-items:center;gap:4px 10px}
.td-semaine .ts-l{font-size:var(--text-12);font-weight:var(--weight-semibold);color:var(--text-muted);text-transform:uppercase;letter-spacing:.05em;flex:none}
.td-semaine .ts-n{font-family:var(--font-mono);font-size:var(--text-13);flex:0 1 auto;min-width:0}
.td-semaine .ts-barre{flex:1;min-width:3rem;height:8px;border:1px solid var(--border-strong);background:var(--surface-field)}
.td-semaine .ts-barre>i{display:block;height:100%;background:var(--accent)}
.td-semaine>.ic:last-child{flex:none;color:var(--text-muted)}
.td-semaine:focus-visible{outline:var(--focus-outline);outline-offset:2px}
.sm-bloc{margin-bottom:16px}
.sm-h{font-size:var(--text-11);text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);margin:0 0 8px;font-weight:var(--weight-semibold)}
.sm-pas{display:flex;align-items:center;gap:12px}
.sm-pas .btn{min-width:var(--ctl);justify-content:center;font-size:var(--text-16)}
.sm-obj{font-family:var(--font-mono);font-size:var(--text-16);min-width:6ch;text-align:center}
.sm-fait{margin:8px 0 0;color:var(--text-muted);font-size:var(--text-13)}
.sm-fait b{color:var(--text-body)}
.sm-phrase{margin:0;font-size:var(--text-14)}

/* ============================================================
   CEUX QUI AIDENT (lot R7, docs/propositions/R7-ceux-qui-aident.md)
   La feuille « Décroché » : l'entreprise, la ligne du parcours (une case),
   le mot qui manque au profil (des puces), puis « Je peux aider » — le
   même titre de section que « Ma semaine », la même liste à cocher que
   Moi. Aucun dessin neuf : des motifs qui existent.
   ============================================================ */
.dc-quoi{margin:8px 0 0}
.dc-h{margin-top:16px}

/* ============================================================
   LE CLAVIER AU POSTE (lot R8, docs/propositions/R8-le-clavier.md)
   Une touche s'écrit DANS ce qu'elle commande (§5) : une petite case de
   clavier, en mono, en retrait — une indication, pas une cible. Au poste
   seulement : `toucheHTML` ne l'écrit qu'au-dessus de 901 px.
   Sur une LIGNE de liste, la touche n'agit que sur la ligne qui a le
   curseur : elle ne se montre que là (`:focus-within`), sinon chaque
   ligne porterait sa lettre — un papier peint (§6, règle 1). Posée dans
   l'angle du bouton, hors du flux : rien ne bouge quand elle paraît.
   ============================================================ */
.kbd-btn{
  display:inline-block;margin-left:6px;padding:1px 4px;
  font-family:var(--font-mono);font-size:var(--text-11);line-height:1.2;font-weight:var(--weight-regular);
  color:var(--text-muted);background:var(--surface-chrome);border:1px solid var(--border-soft);
  vertical-align:1px;pointer-events:none;
}
.btn-primary .kbd-btn{color:inherit;background:transparent;border-color:currentColor;opacity:.8}
.act-row .act-fait,.act-row [data-a="mail"]{position:relative}
.act-row .kbd-btn{display:none;position:absolute;top:-7px;right:-7px;margin:0;z-index:1}
.act-row:focus-within .kbd-btn{display:inline-block}
.na-btn .kbd-btn{flex:none;align-self:center}
/* au doigt, même à la largeur du poste (une tablette), la lettre écrite
   n'est que du bruit : §5, le pointeur dit la main. Elle reste annoncée
   (`aria-keyshortcuts`) et agit si un clavier est branché. */
@media not all and (pointer:fine){
  .kbd-btn,.act-row:focus-within .kbd-btn{display:none}
}
/* « Chercher » dans l'en-tête, à côté de « Ajouter » : discret, il ne
   pèse jamais plus que le geste principal. Dans la barre latérale (lot
   « reseau »), la rangée n'a pas la place de deux boutons : « Ajouter »
   garde la sienne, « Chercher » passe dessous, sa touche au bout — la
   forme d'un champ de recherche, comme dans toute barre latérale. */
.hdr-palette{gap:6px}
.kbd-btn{white-space:nowrap}
.nav-laterale .hdr-actions:has(.hdr-palette){flex-direction:column;align-items:stretch}
.nav-laterale .hdr-palette{order:2;justify-content:flex-start}
.nav-laterale .hdr-palette .kbd-btn{margin-left:auto}
/* la fenêtre « Chercher » : un champ, une liste dans un cadre — la rangée
   choisie au clavier prend le surlignage du châssis, comme un résultat
   qu'on a demandé (§4 : le navy dit ce que tu as demandé). Son dessin est
   le sien : les rangées des feuilles (`.pick`) poussent le libellé à
   droite et effacent tout fond. */
.modal-palette .pk-q{width:100%;margin:0 0 10px}
.pk-liste{margin:0;padding:0;list-style:none;
  background:var(--surface-window);border:1px solid var(--border-strong);box-shadow:var(--shadow-raised)}
.pk-o{display:flex;align-items:center;gap:8px;min-height:var(--ctl);padding:4px 12px;cursor:pointer;
  font-size:var(--text-14);color:var(--text-body);border-bottom:1px dashed var(--border-soft)}
.pk-o:last-child{border-bottom-color:transparent}
.pk-o b{flex:0 1 auto;min-width:0;overflow-wrap:anywhere;font-weight:var(--weight-semibold)}
.pk-o .pk-s{flex:1;min-width:0;text-align:right;font-family:var(--font-mono);font-size:var(--text-11);color:var(--text-muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pk-o>.ic{flex:none;color:var(--text-muted)}
.pk-o.pk-sel{background:var(--select-bg);color:var(--select-text)}
.pk-o.pk-sel>.ic,.pk-o.pk-sel .pk-s{color:var(--select-text)}
.pk-vide{padding:10px 2px;color:var(--text-muted);font-size:var(--text-13)}

/* ---------- ton parcours, en un fichier (lot R9-a) ----------
   « Ajouter » et « Exporter » sous la liste, taillés à leur mot ; la
   feuille d'export montre le fichier ligne par ligne, comme « Mon QR »
   montre ce qu'il donne : le nom en tête, puis les données en mono. */
.pf-par-gestes{display:flex;flex-wrap:wrap;gap:8px}
.cv-donne{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}
.cv-donne li{display:flex;flex-direction:column;gap:1px;min-width:0}
.cv-donne b{font-weight:var(--weight-semibold);overflow-wrap:anywhere}
.cv-donne .cv-nom b{font-size:var(--text-16);font-weight:var(--weight-bold)}
.cv-donne span{font-family:var(--font-mono);font-size:var(--text-12);color:var(--text-muted);overflow-wrap:anywhere}
.cv-donne .cv-experience:first-of-type,.cv-donne .cv-formation + .cv-experience{margin-top:6px}
