﻿/* ==========================================================================
   app.css  -  systeme visuel de l'application de bureau.

   Direction : SaaS sombre haut de gamme. Trois regles tenues partout :

   1. Le relief vient de surfaces PLUS CLAIRES et d'un liserÃ© haut, jamais
      d'ombres portees â€” dans le noir une ombre ne se voit pas. Seuls les
      elements qui flottent reellement au-dessus du contenu (menus, modales)
      ont une vraie ombre, parce que la profondeur y est le sujet.
   2. Le texte est en blanc casse. Le blanc pur sur fond noir fatigue et fait
      baver les caracteres.
   3. Espacements sur une echelle stricte : 4 / 8 / 12 / 16 / 24 / 32 / 48.
      Aucune valeur arbitraire.

   Changer --acc suffit a rehabiller toute l'application.
   ========================================================================== */

/* --- Jetons --------------------------------------------------------------- */

:root {
  /* surfaces, du plus profond au plus eleve */
  --fond:      #08080a;
  --chrome:    #0c0c0f;
  --surf-1:    #121216;
  --surf-2:    #17171c;
  --surf-3:    #1e1e25;

  --bord:      rgba(255, 255, 255, .07);
  --bord-fort: rgba(255, 255, 255, .13);
  --liseret:   rgba(255, 255, 255, .05);

  --txt:   #f1f1f3;
  --txt-2: #a3a3ad;
  --txt-3: #6e6e79;

  --acc:       #ff7900;
  --acc-vif:   #ff9433;
  --acc-sourd: rgba(255, 121, 0, .13);
  --acc-bord:  rgba(255, 121, 0, .28);
  --sur-acc:   #1c0e00;

  --bon:     #22c55e;
  --alerte:  #fab219;
  --grave:   #ec835a;
  --danger:  #ef4444;
  --info:    #4d94ec;

  /* espacements */
  --e1: 4px;  --e2: 8px;  --e3: 12px;
  --e4: 16px; --e5: 24px; --e6: 32px; --e7: 48px;

  /* rayons */
  --r-xs: 6px; --r-s: 9px; --r: 12px; --r-l: 16px; --r-xl: 20px;
  --r-p: 999px;

  --ombre-flottante: 0 1px 2px rgba(0, 0, 0, .5),
                     0 12px 32px -8px rgba(0, 0, 0, .75);
  --ressort: cubic-bezier(.16, 1, .3, 1);

  /* Les animations, reglees une fois pour toute l'equipe — voir le mode
     « Regler la disposition » dans la barre. Les trois valent 1 tant que
     personne n'y touche : l'application bouge alors comme elle a ete
     dessinee. La fenetre les repose sur la racine au demarrage.

     --anim-vit  multiplie les durees : 2 = deux fois plus lent, 0 = instantane
     --anim-amp  multiplie l'ampleur des entrees et des sorties : 0 = sur place
     --anim-ech  echelle des boites qui s'ouvrent par-dessus la page */
  --anim-vit: 1;
  --anim-amp: 1;
  --anim-ech: 1;
}

/* --- Theme clair : la charte Orange ----------------------------------------

   Les valeurs viennent de l'Orange Design System (Boosted) : orange #FF7900,
   noir, blanc, la grille de gris officielle, et les couleurs fonctionnelles
   maison. Rien d'invente.

   Deux regles de la charte commandent tout le reste, et elles vont a
   l'encontre de ce qu'on ferait spontanement :

   * Sur un aplat orange, le texte est NOIR. Du blanc sur #FF7900 ne donne que
     2,3:1 de contraste â€” illisible au soleil, et refuse par la charte comme
     par le WCAG.
   * L'orange ne porte pas le texte courant. #FF7900 sur blanc plafonne a
     2,9:1, sous le minimum de 4,5:1. Il sert donc aux aplats, aux bordures et
     aux pictogrammes ; quand il faut ecrire en orange, on descend a #A64F00,
     qui passe a 5:1 sans quitter la teinte.

   C'est pour cela que le theme clair est Â« orange et blanc Â» sans etre
   Â« du texte orange partout Â» : la charte l'interdit. */

:root[data-theme="clair"] {
  /* surfaces : la page est grise, ce qui s'eleve devient blanc */
  --fond:      #f6f6f6;
  --chrome:    #ffffff;
  --surf-1:    #ffffff;
  --surf-2:    #ffffff;
  --surf-3:    #eeeeee;

  --bord:      #dddddd;
  --bord-fort: #cccccc;
  --liseret:   #eeeeee;

  --txt:   #000000;              /* le noir, texte principal de la charte */
  --txt-2: #666666;
  /* La charte liste #999999, mais il ne donne que 2,85:1 sur blanc : trop
     pale pour du texte. #767676 est le gris le plus clair qui atteigne 4,5:1,
     et il reste dans la meme famille. */
  --txt-3: #767676;

  --acc:       #ff7900;          /* l'orange, inchange : c'est la marque */
  --acc-vif:   #a64f00;          /* sa declinaison lisible sur blanc */
  --acc-sourd: rgba(255, 121, 0, .10);
  --acc-bord:  rgba(255, 121, 0, .45);
  --sur-acc:   #000000;          /* noir sur orange, jamais blanc */

  /* couleurs fonctionnelles Orange, en version lisible sur fond clair */
  --bon:     #008611;
  --alerte:  #8f5e00;
  --grave:   #cd3c14;
  --danger:  #cd3c14;
  --info:    #0a3ec5;

  --ombre-flottante: 0 1px 2px rgba(0, 0, 0, .12),
                     0 12px 32px -8px rgba(0, 0, 0, .22);

  /* Commande les composants dessines par le navigateur lui-meme : la piste du
     lecteur audio, les ascenseurs, le calendrier des champs date. Sans cette
     ligne ils restaient noirs au milieu d'une page blanche. */
  color-scheme: light;

  --defile:      #c4c4c4;        /* ascenseur */
  --defile-fort: #9a9a9a;
  --carte-fond:  #e9e9e9;        /* fond de la carte geographique */
  --mot-actif:   #000000;        /* mot suivi pendant l'ecoute */

  --danger-txt: #cd3c14;
  --alerte-txt: #8f5e00;
  --bon-txt:    #008611;
}

/* Le dÃ©gradÃ© de la marque perd son noir de fond : sur blanc, l'orange plein
   suffit et reste conforme. */
:root[data-theme="clair"] .marque .pastille {
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .10) inset,
              0 6px 18px -8px rgba(255, 121, 0, .55);
}

/* Le surlignage des mots-cles s'appuie sur des fonds tres pales dans le
   theme sombre ; sur blanc il faut le rendre un peu plus dense pour rester
   visible. */
:root[data-theme="clair"] mark.cle {
  background: color-mix(in srgb, var(--cle-teinte) 22%, transparent);
}

:root {

  --lateral: 246px;
  --titre-h: 38px;

  color-scheme: dark;

  --defile:      #2a2a32;
  --defile-fort: #3a3a45;
  --carte-fond:  #101014;
  --mot-actif:   #ffffff;

  /* Le texte d'alerte n'est pas la couleur d'alerte : sur fond sombre il faut
     l'eclaircir, sur fond clair l'assombrir. Deux jetons distincts evitent de
     dupliquer les regles qui s'en servent. */
  --danger-txt: #ff8f85;
  --alerte-txt: #f4d08a;
  --bon-txt:    #7de3a0;
}

/* --- Base ----------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  overflow: hidden;                  /* c'est le contenu qui defile, pas la page */
}

body {
  background: var(--fond);
  color: var(--txt);
  font-family: "Inter var", Inter, "Segoe UI Variable Text", "Segoe UI",
               system-ui, -apple-system, sans-serif;
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Le contenu se selectionne, la carrosserie non. L'inverse â€” tout bloquer pour
   Â« faire application Â» â€” rendait incopiables les transcriptions, les adresses
   et les numeros, c'est-a-dire precisement ce qu'on vient chercher ici. */
.barre-titre, .nav, .segmente, .onglets, .numero, .boite, .puce,
button, .btn, label.case, label.interrupteur {
  user-select: none;
  -webkit-user-select: none;
}

/* Sauf l'adresse a taper sur le telephone : elle, on veut pouvoir la copier. */
.pied, .pied * { user-select: text; -webkit-user-select: text; }

h1, h2, h3, h4 { margin: 0; font-weight: 650; letter-spacing: -.022em; }
h1 { font-size: 26px; letter-spacing: -.03em; }
h2 { font-size: 17px; }
h3 { font-size: 14px; }
p  { margin: 0; }

a { color: var(--acc-vif); text-decoration: none; }
a:hover { text-decoration: underline; }

::selection { background: rgba(255, 121, 0, .3); }

:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}

::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-thumb {
  background: var(--defile);
  border: 3px solid transparent;
  background-clip: padding-box;
  border-radius: var(--r-p);
}
::-webkit-scrollbar-thumb:hover { background: var(--defile-fort); background-clip: padding-box; }
::-webkit-scrollbar-track { background: transparent; }

/* --- Ossature ------------------------------------------------------------- */

.appli {
  display: grid;
  grid-template-rows: var(--titre-h) 1fr;
  height: 100vh;
}

/* Barre de titre maison : la fenetre est sans cadre, c'est elle qui la
   remplace. La zone de glissement est declaree cote pywebview. */
.barre-titre {
  display: flex;
  align-items: center;
  gap: var(--e3);
  padding-left: var(--e3);
  background: var(--chrome);
  border-bottom: 1px solid var(--bord);
  -webkit-user-select: none;
}

.barre-titre .titre {
  font-size: 12px;
  color: var(--txt-3);
  letter-spacing: .01em;
}

/* Retour en arriere. La barre de titre entiere sert de poignee de fenetre :
   ces boutons doivent explicitement s'en extraire, sinon le clic est avale
   par le deplacement de la fenetre. */
.histo {
  display: flex;
  gap: 2px;
  -webkit-app-region: no-drag;
  app-region: no-drag;
}

.histo button {
  width: 28px; height: 24px;
  display: grid; place-items: center;
  border: 0;
  border-radius: var(--r-xs);
  background: transparent;
  color: var(--txt-2);
  cursor: pointer;
  transition: background calc(.12s * var(--anim-vit)), color calc(.12s * var(--anim-vit));
}
.histo button svg { width: 15px; height: 15px; }
.histo button:hover:not(:disabled) { background: var(--surf-3); color: var(--txt); }
.histo button:active:not(:disabled) { background: var(--surf-2); }
/* Desactive : visible mais eteint. Le cacher ferait sauter la barre d'un
   ecran a l'autre, et on ne saurait plus ou cliquer. */
.histo button:disabled { opacity: .28; cursor: default; }

.fenetre-actions { margin-left: auto; display: flex; height: 100%; }

.fenetre-actions button {
  width: 46px;
  height: 100%;
  border: 0;
  background: transparent;
  color: var(--txt-2);
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: background calc(.12s * var(--anim-vit)), color calc(.12s * var(--anim-vit));
}
.fenetre-actions button:hover { background: var(--surf-2); color: var(--txt); }
.fenetre-actions button.fermer:hover { background: #c0392b; color: #fff; }
.fenetre-actions svg { width: 10px; height: 10px; }

.corps {
  display: grid;
  grid-template-columns: var(--lateral) 1fr;
  min-height: 0;
}

/* --- Barre laterale ------------------------------------------------------- */

.lateral {
  background: var(--chrome);
  border-right: 1px solid var(--bord);
  display: flex;
  flex-direction: column;
  padding: var(--e4) var(--e3) var(--e3);
  gap: var(--e4);
  min-height: 0;
  overflow-y: auto;
}

.marque { display: flex; align-items: center; gap: var(--e3); padding: 0 var(--e2); }

.marque .pastille {
  width: 34px; height: 34px;
  border-radius: 10px;
  background: linear-gradient(140deg, var(--acc-vif), var(--acc) 55%, #d95f00);
  display: grid; place-items: center;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .12) inset,
              0 6px 18px -6px rgba(255, 121, 0, .65);
  flex: none;
}
.marque .pastille svg { width: 19px; height: 19px; color: #22120a; }

/* Le logo porte deja son fond noir et son liseret orange : pas de pastille
   degradee derriere, qui ferait deux cadres l'un dans l'autre. */
.marque .pastille-logo {
  width: 38px; height: 38px;
  flex: none;
  border-radius: 11px;
  object-fit: contain;
  filter: drop-shadow(0 5px 14px rgba(255, 121, 0, .35));
}

.marque .nom { font-weight: 700; letter-spacing: -.02em; font-size: 15px; }
.marque .sous { font-size: 11px; color: var(--txt-3); margin-top: -2px; }

.nav { display: flex; flex-direction: column; gap: 2px; }

.nav .rubrique {
  font-size: 10.5px;
  font-weight: 650;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--txt-3);
  padding: var(--e3) var(--e2) var(--e1);
}

.nav button {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px var(--e2);
  border: 0;
  border-radius: var(--r-s);
  background: transparent;
  color: var(--txt-2);
  font: inherit;
  font-weight: 500;
  font-size: 13.5px;
  cursor: pointer;
  position: relative;
  transition: background calc(.12s * var(--anim-vit)), color calc(.12s * var(--anim-vit));
}
.nav button svg { width: 17px; height: 17px; flex: none; opacity: .9; }
.nav button:hover { background: var(--surf-2); color: var(--txt); }

.nav button.actif { background: var(--acc-sourd); color: var(--acc-vif); font-weight: 600; }
.nav button.actif::before {
  content: "";
  position: absolute;
  left: -12px;
  top: 50%; translate: 0 -50%;
  width: 3px; height: 18px;
  border-radius: 0 3px 3px 0;
  background: var(--acc);
}
.nav button .compteur {
  margin-left: auto;
  font-size: 11px;
  font-weight: 600;
  color: var(--txt-3);
  background: var(--surf-2);
  border-radius: var(--r-p);
  padding: 1px 7px;
}
.nav button.actif .compteur { background: rgba(255, 121, 0, .18); color: var(--acc-vif); }

.lateral .bas { margin-top: auto; display: flex; flex-direction: column; gap: var(--e2); }

.bloc-lateral {
  background: var(--surf-1);
  border: 1px solid var(--bord);
  border-radius: var(--r);
  padding: 10px var(--e3) var(--e3);
}
.bloc-lateral .etiquette {
  font-size: 10.5px; font-weight: 650; letter-spacing: .08em;
  text-transform: uppercase; color: var(--txt-3);
  display: block; margin-bottom: 6px;
}

.pied {
  font-size: 11px;
  color: var(--txt-3);
  padding: 0 var(--e2);
  display: flex; align-items: center; gap: 6px;
}
.pied .point { width: 6px; height: 6px; border-radius: 50%; background: var(--bon); flex: none; }

/* --- Zone principale ------------------------------------------------------ */

.principal {
  min-width: 0;
  overflow-y: auto;
  position: relative;
  background:
    radial-gradient(900px 320px at 78% -12%, rgba(255, 121, 0, .07), transparent 70%),
    var(--fond);
}

.page { padding: var(--e5) var(--e6) var(--e7); max-width: 1560px; }

.entete-page {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--e4);
  flex-wrap: wrap;
  margin-bottom: var(--e5);
}
.entete-page .sous { color: var(--txt-3); font-size: 13px; margin-top: 3px; }
.entete-page .actions { display: flex; gap: var(--e2); align-items: center; }

/* Titre et contexte cote a cote plutot qu'empiles : deux lignes pour trois
   mots prenaient de la hauteur juste au-dessus de la liste, la ou elle manque.
   Alignes sur la ligne de base, sinon le petit texte flotte au milieu du gros.
   Le decalage vertical pose plus haut ne vaut que pour la version empilee. */
.entete-page .titre-ligne {
  display: flex; align-items: baseline; flex-wrap: wrap;
  gap: var(--e2);
}
.entete-page .titre-ligne .sous { margin-top: 0; }

.section { margin-top: var(--e6); }
.section > .chapeau {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--e3); margin-bottom: var(--e3);
}
.section .chapeau .aide { font-size: 12.5px; color: var(--txt-3); }

/* --- Grilles -------------------------------------------------------------- */

.grille { display: grid; gap: var(--e4); }
.g2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.g3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.g4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.g5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.g-auto { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.g-2-1 { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
.g-1-1 { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }

@media (max-width: 1240px) {
  .g5, .g4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .g3, .g-2-1, .g-1-1 { grid-template-columns: minmax(0, 1fr); }
}

/* --- Cartes --------------------------------------------------------------- */

.carte {
  background: var(--surf-1);
  border: 1px solid var(--bord);
  border-radius: var(--r-l);
  padding: var(--e4);
  background-image: linear-gradient(180deg, var(--liseret), transparent 90px);
}
.carte.serree { padding: var(--e3); border-radius: var(--r); }
.carte.pleine { padding: 0; overflow: hidden; }

.carte > .chapeau {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--e3); margin-bottom: var(--e3);
}
.carte > .chapeau h2 { font-size: 14.5px; }

/* --- Le portefeuille courant ---------------------------------------------- */
/* Le choix vivait dans la barre latérale, visible partout et changé trois fois
   par an. Il se coche maintenant dans sa page. Le bouton radio fait le travail
   du « un seul à la fois » — on ne le simule pas en JavaScript. */

.pf-choix {
  display: flex; align-items: center; gap: var(--e2);
  cursor: pointer; min-width: 0;
}
.pf-choix input { accent-color: var(--acc); width: 16px; height: 16px; flex: none; }
.pf-choix b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* La ligne « Tous », posée au-dessus de la grille : même geste que les cartes,
   sans en être une — elle ne décrit aucun secteur. */
.pf-tous {
  padding: var(--e3);
  margin-bottom: var(--e3);
  border: 1px solid var(--bord);
  border-radius: var(--r);
  background: var(--surf-1);
}

/* Coché : le liseré d'accent suffit à le désigner d'un coup d'œil dans une
   grille, sans repeindre la carte entière. */
.pf-courant { border-color: var(--acc); }

.cliquable { cursor: pointer; transition: border-color calc(.15s * var(--anim-vit)), background calc(.15s * var(--anim-vit)), transform calc(.15s * var(--anim-vit)) var(--ressort); }
.cliquable:hover { border-color: var(--bord-fort); background-color: var(--surf-2); transform: translateY(-1px); }
.cliquable:active { transform: translateY(0) scale(.995); transition-duration: calc(.05s * var(--anim-vit)); }

/* --- Tuiles d'indicateur -------------------------------------------------- */

.tuile {
  background: var(--surf-1);
  border: 1px solid var(--bord);
  border-radius: var(--r-l);
  padding: var(--e4);
  background-image: linear-gradient(180deg, var(--liseret), transparent 70px);
  position: relative;
  overflow: hidden;
}
.tuile .etiquette {
  font-size: 10.5px; font-weight: 650; letter-spacing: .085em;
  text-transform: uppercase; color: var(--txt-3);
  display: flex; align-items: center; gap: 6px;
}
.tuile .valeur {
  font-size: 30px; font-weight: 700; letter-spacing: -.035em;
  line-height: 1.1; margin-top: 10px;
  font-variant-numeric: tabular-nums;
}
.tuile .detail { font-size: 12px; color: var(--txt-3); margin-top: 3px; }
.tuile.accentuee { border-color: var(--acc-bord); }
.tuile.accentuee .valeur { color: var(--acc-vif); }
.tuile .filigrane {
  position: absolute; right: -8px; top: -10px;
  font-size: 60px; opacity: .05; pointer-events: none;
}

/* Le ton dit ce que vaut le chiffre, la ou l'accent disait seulement qu'il
   etait le principal. De l'argent gagne est vert, un compte de choses en
   retard est jaune — et c'est la meme couleur que partout ailleurs dans
   l'ecran, sinon le code couleur ne s'apprend jamais. */
.tuile.ton-bon {
  border-color: color-mix(in srgb, var(--bon) 32%, var(--bord));
}
.tuile.ton-bon .valeur { color: var(--bon-txt); }
.tuile.ton-alerte {
  border-color: color-mix(in srgb, var(--alerte) 45%, var(--bord));
}
.tuile.ton-alerte .valeur { color: var(--alerte-txt); }

/* --- Pastilles de statut --------------------------------------------------
   Fond teinte plutot qu'aplat : un aplat jaune avec du texte blanc est
   illisible, un fond jaune a 14 % avec du texte jaune passe partout. */

/* La couleur du statut arrive dans --t. Les trois usages en sont derives ici,
   ce qui permet au theme clair de l'assombrir d'un seul endroit : les teintes
   du serveur sont calees pour du fond noir et virent au fluo sur blanc. */
.puce {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px;
  border-radius: var(--r-p);
  font-size: 11.5px; font-weight: 600;
  white-space: nowrap; line-height: 1.6;
  background: color-mix(in srgb, var(--t, #8b8b96) 13%, transparent);
  color: var(--t, #8b8b96);
  border: 1px solid color-mix(in srgb, var(--t, #8b8b96) 27%, transparent);
}
.puce.mini { font-size: 10.5px; padding: 1px 7px; }

:root[data-theme="clair"] .puce {
  background: color-mix(in srgb, var(--t, #8b8b96) 14%, #ffffff);
  /* Assez de noir melange pour repasser au-dessus de 4,5:1 sur le fond pale.
     A 62 % le jaune de « À rappeler » retombait a 4,15:1 ; 54 % le fait
     passer sans denaturer les autres teintes. */
  color: color-mix(in srgb, var(--t, #8b8b96) 54%, #000000);
  border-color: color-mix(in srgb, var(--t, #8b8b96) 45%, #ffffff);
}

.etiq {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; font-weight: 600;
  padding: 2px 8px;
  border-radius: var(--r-xs);
  background: var(--surf-3);
  color: var(--txt-2);
}

/* --- Boutons -------------------------------------------------------------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 7px;
  padding: 8px 14px;
  border-radius: 10px;
  border: 1px solid var(--bord);
  background: var(--surf-2);
  color: var(--txt);
  font: inherit; font-size: 13px; font-weight: 550;
  cursor: pointer;
  white-space: nowrap;
  transition: background calc(.14s * var(--anim-vit)), border-color calc(.14s * var(--anim-vit)), transform calc(.12s * var(--anim-vit)) var(--ressort),
              box-shadow calc(.14s * var(--anim-vit)), opacity calc(.14s * var(--anim-vit));
}
.btn svg { width: 15px; height: 15px; flex: none; }
.btn:hover { background: var(--surf-3); border-color: var(--bord-fort); transform: translateY(-1px); }
/* Le bouton s'enfonce sous le doigt. Sur le terrain, gante, sous le soleil, on
   ne voit pas toujours l'ecran changer : la course du bouton, elle, se sent —
   et elle dit que l'appui est passe, avant meme que le serveur reponde. */
.btn:active { transform: translateY(1px) scale(.97); transition-duration: calc(.04s * var(--anim-vit)); }
.btn:disabled { opacity: .4; cursor: not-allowed; transform: none; }
/* Le `display` de .btn l'emporte sur le [hidden]{display:none} du navigateur :
   sans cette ligne, un bouton masque par `el.hidden = true` reste a l'ecran.
   C'est ainsi que « Retirer » s'affichait alors qu'il n'y avait rien a
   retirer, dans les preferences. */
.btn[hidden] { display: none; }

.btn.primaire {
  background: var(--acc);
  border-color: transparent;
  color: var(--sur-acc);
  font-weight: 660;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .14) inset,
              0 8px 22px -10px rgba(255, 121, 0, .9);
}
/* Le bouton principal se souleve un peu plus que les autres : son ombre
   s'ecarte au survol, se resserre a l'appui. */
.btn.primaire:hover {
  background: var(--acc-vif);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .18) inset,
              0 12px 26px -10px rgba(255, 121, 0, 1);
}
.btn.primaire:active {
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .12) inset,
              0 4px 12px -8px rgba(255, 121, 0, .9);
}

.btn.fantome { background: transparent; border-color: transparent; color: var(--txt-2); }
.btn.fantome:hover { background: var(--surf-2); color: var(--txt); }

.btn.danger { color: var(--danger-txt); border-color: rgba(239, 68, 68, .3); background: rgba(239, 68, 68, .08); }
.btn.danger:hover { background: rgba(239, 68, 68, .16); border-color: rgba(239, 68, 68, .5); }

/* Ranger un interlocuteur dans une famille : quatre boutons cote a cote,
   repetes sur soixante lignes. Ils portent la teinte de leur famille comme
   les pastilles de statut — fond a 13 %, texte et bord dans la couleur —
   parce qu'a cette densite l'emoji seul ne se distingue plus : on cherche le
   bouton vert, pas le troisieme en partant de la gauche.

   Le survol monte a 26 % et fait ressortir le libelle. Il reste replie le
   reste du temps : quatre mots par ligne, ce sont deux cent quarante mots
   dans un panneau ou l'on vient trancher, pas lire. */
/* Les durees du survol tiennent en deux variables, ici et nulle part ailleurs.
   Avec la courbe maison — un expo-out qui place presque tout le mouvement au
   debut — allonger n'ajoute pas de latence : le bouton part aussi vite, il
   finit plus doucement. C'est la traine qui donne le velours, pas le delai.

   L'appui ne suit pas ce reglage et reste a soixante millisecondes, plus bas :
   un clic amorti donne l'impression que la machine n'a pas entendu. */
.btn.famille {
  --t-survol: calc(.6s * var(--anim-vit));
  --t-lustre: calc(1.2s * var(--anim-vit));
  --p: 0%;                       /* ce que le modele croit a cette famille */
  position: relative; overflow: hidden;
  padding: 4px 9px; font-size: 12px; border-radius: 9px;
  color: var(--t, #8b8b96);
  border: 1px solid color-mix(in srgb, var(--t, #8b8b96) 26%, transparent);
  /* Deux fonds superposes. Dessus, une pente verticale claire en haut :
     c'est elle qui donne le relief, et elle le prend de la lumiere plutot
     que d'une ombre — sur un fond a #08080a une ombre ne se voit pas.
     Dessous, la jauge : le bouton se remplit jusqu'au pourcentage qu'il
     annonce. Le chiffre est ecrit a cote, mais on n'a pas besoin de le lire
     pour voir lequel des quatre penche, et c'est ce qui rend soixante lignes
     descendables au regard. */
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--t, #8b8b96) 20%, transparent) 0%,
      color-mix(in srgb, var(--t, #8b8b96) 10%, transparent) 100%),
    linear-gradient(90deg,
      color-mix(in srgb, var(--t, #8b8b96) 26%, transparent) var(--p),
      transparent var(--p));
  box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 7%, transparent);
  transition: transform var(--t-survol) var(--ressort),
              box-shadow var(--t-survol) var(--ressort),
              border-color var(--t-survol) var(--ressort),
              background-size var(--t-survol) var(--ressort);
}

/* Le lustre : une bande claire qui traverse une fois au survol, sans jamais
   repasser — une brillance en boucle cesse d'etre un detail de finition pour
   devenir un clignotant. Elle est en ::after et non sur le fond pour ne pas
   entrer en conflit avec la jauge, qui porte, elle, une information.

   Sa traversee est plus lente que le reste du survol, et c'est voulu : elle
   arrive apres que le bouton s'est pose, comme un reflet qui suit le
   mouvement au lieu de l'accompagner. Sortie en meme temps, on ne voyait plus
   qu'un flash au moment ou l'oeil arrivait. */
.btn.famille::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(105deg, transparent 38%,
              color-mix(in srgb, #fff 22%, transparent) 50%, transparent 62%);
  transform: translateX(-130%);
  pointer-events: none;
}
.btn.famille:hover {
  transform: translateY(-1px);
  border-color: color-mix(in srgb, var(--t, #8b8b96) 62%, transparent);
  color: var(--t, #8b8b96);
  box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 14%, transparent),
              0 4px 14px -6px color-mix(in srgb, var(--t, #8b8b96) 75%, transparent);
}
.btn.famille:hover::after {
  animation: lustre var(--t-lustre) cubic-bezier(.25, .5, .2, 1);
}
@keyframes lustre { to { transform: translateX(130%); } }

/* L'appui redescend, et vite : un retour amorti sur un clic donne
   l'impression que la machine n'a pas entendu. */
.btn.famille:active {
  transform: translateY(0) scale(.97);
  transition-duration: calc(.06s * var(--anim-vit));
  box-shadow: inset 0 2px 5px -2px color-mix(in srgb, #000 55%, transparent);
}
.btn.famille:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--t, #8b8b96) 70%, transparent);
  outline-offset: 2px;
}

/* Celle vers laquelle le modele penche : un cerne, pas une couleur de plus.
   Quatre teintes en concurrence, il fallait que la premiere se distingue
   sans en ajouter une cinquieme. */
.btn.famille.penche {
  border-color: color-mix(in srgb, var(--t, #8b8b96) 65%, transparent);
  box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 12%, transparent),
              0 0 0 1px color-mix(in srgb, var(--t, #8b8b96) 22%, transparent);
}
.btn.famille .pct {
  margin-left: 5px; font-variant-numeric: tabular-nums;
  font-weight: 700; opacity: .92;
}
/* Une famille sur laquelle le modele n'a presque rien vu : son chiffre est
   la, mais en retrait, et l'infobulle dit pourquoi. */
.btn.famille.maigre .pct { opacity: .5; font-weight: 500; }

@media (prefers-reduced-motion: reduce) {
  .btn.famille { transition: none; }
  .btn.famille:hover { transform: none; }
  .btn.famille:hover::after { animation: none; }
  .btn.famille:active { transform: none; }
}
/* Les libelles apparaissent a l'entree dans LA LIGNE, pas au survol du bouton.
   La nuance fait tout : deplie bouton par bouton, celui qu'on survole
   s'elargit et pousse les trois autres vers la droite — on visait « Privé »,
   on clique « Service ». Deplies ensemble, la ligne s'etablit une fois pendant
   qu'on arrive dessus, et plus rien ne bouge au moment de viser. */
.ligne-inconnu .btn.famille .nom { display: none; margin-left: 4px; font-weight: 600; }
.ligne-inconnu:hover .btn.famille .nom { display: inline; }

/* Le chevron ne se montre qu'au survol de la ligne, ou quand elle est
   ouverte. Soixante chevrons permanents dans une liste dense font un peigne :
   ils annoncent tous la meme chose et plus aucun ne se lit. La largeur, elle,
   est reservee en permanence — sinon l'apparition decalerait le nom. */
.ligne-inconnu .chevron {
  display: inline-block; width: 12px;
  color: var(--txt-3); opacity: 0;
  transition: opacity var(--t-survol, calc(.6s * var(--anim-vit))) var(--ressort);
}
.ligne-inconnu:hover .chevron,
.ligne-inconnu.ouverte .chevron { opacity: 1; }
.ligne-inconnu.ouverte .chevron { color: var(--acc); }
.ligne-inconnu .ecoute:hover b { color: var(--acc); }

/* La ligne dont l'apercu est ouvert reste designee pendant qu'on ecoute : la
   carte est en dessous de la liste, et sans ce reperage on ne sait plus de
   qui elle parle des qu'on a fait defiler de trois lignes. */
.ligne-inconnu.ouverte {
  background: var(--acc-sourd);
  box-shadow: inset 2px 0 0 var(--acc);
  border-radius: var(--r-xs);
}

/* Sur fond pale, la teinte pure ne passe pas le contraste — meme correction
   que les pastilles, voir le commentaire de .puce. La jauge et la pente sont
   refaites ici plutot que teintees : melangees vers le blanc et non vers le
   transparent, sinon elles se posent sur le gris de la page et non sur la
   surface de la carte. La pente s'inverse aussi, le clair venant du bas :
   au-dessus d'un fond blanc, une bande claire en tete ne se voit pas. */
:root[data-theme="clair"] .btn.famille {
  color: color-mix(in srgb, var(--t, #8b8b96) 54%, #000000);
  border-color: color-mix(in srgb, var(--t, #8b8b96) 45%, #ffffff);
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--t, #8b8b96) 10%, #ffffff) 0%,
      color-mix(in srgb, var(--t, #8b8b96) 20%, #ffffff) 100%),
    linear-gradient(90deg,
      color-mix(in srgb, var(--t, #8b8b96) 30%, #ffffff) var(--p),
      #ffffff var(--p));
  box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--t, #8b8b96) 18%, #ffffff);
}
:root[data-theme="clair"] .btn.famille:hover {
  border-color: color-mix(in srgb, var(--t, #8b8b96) 60%, #ffffff);
  box-shadow: 0 4px 12px -6px color-mix(in srgb, var(--t, #8b8b96) 55%, #ffffff);
}
:root[data-theme="clair"] .btn.famille::after {
  background: linear-gradient(105deg, transparent 38%,
              color-mix(in srgb, #fff 65%, transparent) 50%, transparent 62%);
}
:root[data-theme="clair"] .btn.famille.penche {
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--t, #8b8b96) 30%, #ffffff);
}

.btn.mini { padding: 4px 9px; font-size: 12px; border-radius: 8px; }
.btn.carre { padding: 6px; width: 30px; height: 30px; }
.btn.bloc { width: 100%; }
.btn.grand { padding: 12px 18px; font-size: 14px; border-radius: var(--r); }

.groupe-btn { display: flex; gap: var(--e2); flex-wrap: wrap; }

/* Boutons de resultat de visite : la cible principale du terrain, donc grande. */
.resultats { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--e2); }
.btn-resultat {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  padding: 14px 8px;
  border-radius: var(--r);
  border: 1px solid var(--bord);
  background: var(--surf-2);
  color: var(--txt);
  font: inherit; font-size: 12.5px; font-weight: 600;
  cursor: pointer;
  transition: background calc(.14s * var(--anim-vit)), border-color calc(.14s * var(--anim-vit)), transform calc(.12s * var(--anim-vit)) var(--ressort);
}
.btn-resultat .emo {
  font-size: 20px; line-height: 1;
  transition: transform calc(.16s * var(--anim-vit)) var(--ressort);
}
.btn-resultat:hover { transform: translateY(-2px); background: var(--surf-3); }
/* L'icone grandit avant que le bouton bouge : c'est elle qu'on vise du pouce,
   c'est elle qui doit confirmer qu'on est dessus. */
.btn-resultat:hover .emo { transform: scale(1.14); }
.btn-resultat:active { transform: translateY(0) scale(.97); transition-duration: calc(.04s * var(--anim-vit)); }

/* --- Controle segmente ---------------------------------------------------- */

.segmente {
  display: inline-flex;
  padding: 3px;
  gap: 2px;
  background: var(--surf-1);
  border: 1px solid var(--bord);
  border-radius: 11px;
}
.segmente button {
  padding: 5px 13px;
  border: 0; border-radius: 8px;
  background: transparent;
  color: var(--txt-2);
  font: inherit; font-size: 12.5px; font-weight: 550;
  cursor: pointer;
  transition: background calc(.14s * var(--anim-vit)), color calc(.14s * var(--anim-vit));
}
.segmente button:hover { color: var(--txt); }
.segmente button.actif { background: var(--surf-3); color: var(--txt); font-weight: 620; }

/* --- Onglets -------------------------------------------------------------- */

.onglets {
  display: flex; gap: var(--e5);
  border-bottom: 1px solid var(--bord);
  margin-bottom: var(--e4);
  overflow-x: auto;
}
.onglets button {
  padding: 0 0 11px;
  border: 0; border-bottom: 2px solid transparent;
  background: transparent;
  color: var(--txt-3);
  font: inherit; font-size: 13.5px; font-weight: 550;
  cursor: pointer;
  white-space: nowrap;
  margin-bottom: -1px;
  transition: color calc(.14s * var(--anim-vit)), border-color calc(.14s * var(--anim-vit));
}
.onglets button:hover { color: var(--txt-2); }
.onglets button.actif { color: var(--txt); font-weight: 650; border-color: var(--acc); }

/* --- Champs --------------------------------------------------------------- */

.champ { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.champ > label {
  font-size: 11.5px; font-weight: 600; color: var(--txt-2);
  letter-spacing: .01em;
}
.champ .indice { font-size: 11.5px; color: var(--txt-3); }

input[type="text"], input[type="password"], input[type="number"],
input[type="date"], input[type="search"], select, textarea {
  width: 100%;
  padding: 8px 11px;
  background: var(--surf-1);
  border: 1px solid var(--bord);
  border-radius: 10px;
  color: var(--txt);
  font: inherit; font-size: 13px;
  transition: border-color calc(.14s * var(--anim-vit)), background calc(.14s * var(--anim-vit)), box-shadow calc(.14s * var(--anim-vit));
}
textarea { resize: vertical; min-height: 70px; line-height: 1.55; }

input::placeholder, textarea::placeholder { color: var(--txt-3); }

input:hover, select:hover, textarea:hover { border-color: var(--bord-fort); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--acc);
  background: var(--surf-2);
  box-shadow: 0 0 0 3px rgba(255, 121, 0, .16);
}

select {
  appearance: none;
  padding-right: 30px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%236e6e79' stroke-width='1.6' stroke-linecap='round'%3E%3Cpath d='M3 4.5 6 7.5 9 4.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 12px;
  cursor: pointer;
}
option { background: var(--surf-2); color: var(--txt); }

input[type="date"]::-webkit-calendar-picker-indicator { filter: invert(.6); cursor: pointer; }

.recherche { position: relative; }
.recherche svg {
  position: absolute; left: 11px; top: 50%; translate: 0 -50%;
  width: 15px; height: 15px; color: var(--txt-3); pointer-events: none;
}
.recherche input { padding-left: 34px; }

/* case a cocher et interrupteur */
.case { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font-size: 13px; }
.case input { position: absolute; opacity: 0; width: 0; height: 0; }
.case .boite {
  width: 17px; height: 17px; flex: none;
  border: 1.5px solid var(--bord-fort);
  border-radius: 5px;
  background: var(--surf-1);
  display: grid; place-items: center;
  transition: background calc(.14s * var(--anim-vit)), border-color calc(.14s * var(--anim-vit));
}
.case .boite svg { width: 11px; height: 11px; color: var(--sur-acc); opacity: 0; }
.case input:checked + .boite { background: var(--acc); border-color: var(--acc); }
.case input:checked + .boite svg { opacity: 1; }
.case input:focus-visible + .boite { box-shadow: 0 0 0 3px rgba(255, 121, 0, .25); }

/* Grille de cases a cocher : les offres d'une vente se pointent au doigt,
   debout devant une porte. D'ou des pavÃ©s larges plutot qu'une liste serree. */
.cases { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: var(--e2); }
.cases .case {
  padding: 11px 12px;
  border: 1px solid var(--bord);
  border-radius: var(--r);
  background: var(--surf-1);
  font-size: 13.5px; font-weight: 550;
  transition: background calc(.14s * var(--anim-vit)), border-color calc(.14s * var(--anim-vit));
}
.cases .case:hover { background: var(--surf-2); }
.cases .case:has(input:checked) { border-color: var(--acc); background: rgba(255, 121, 0, .09); }

/* Derniere case de la grille, reservee a l'administrateur : le trait pointille
   dit qu'elle n'est pas une offre mais le moyen d'en creer une. */
.cases .case.ajout {
  justify-content: center;
  border-style: dashed;
  color: var(--txt-3);
  font: inherit; font-size: 13px; font-weight: 550;
  cursor: pointer;
}
.cases .case.ajout:hover { color: var(--txt); border-color: var(--bord-fort); }
.cases .case.ajout svg { width: 15px; height: 15px; }

.cases .saisie-offre {
  display: flex; align-items: center; gap: 6px;
  padding: 5px;
  border: 1px solid var(--acc);
  border-radius: var(--r);
  background: var(--surf-1);
}
.cases .saisie-offre input {
  flex: 1; min-width: 0;
  padding: 6px 8px;
  font-size: 13px;
}
.cases .saisie-offre .btn { flex: none; }

.interrupteur { display: inline-flex; align-items: center; gap: 9px; cursor: pointer; font-size: 13px; }
.interrupteur input { position: absolute; opacity: 0; }
.interrupteur .piste {
  width: 34px; height: 20px; flex: none;
  border-radius: var(--r-p);
  background: var(--surf-3);
  border: 1px solid var(--bord);
  position: relative;
  transition: background calc(.16s * var(--anim-vit));
}
.interrupteur .piste::after {
  content: "";
  position: absolute; top: 2px; left: 2px;
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--txt-2);
  transition: translate calc(.18s * var(--anim-vit)) var(--ressort), background calc(.16s * var(--anim-vit));
}
.interrupteur input:checked + .piste { background: var(--acc); border-color: transparent; }
.interrupteur input:checked + .piste::after { translate: 14px 0; background: var(--sur-acc); }

/* --- Barre de progression ------------------------------------------------- */

.progression {
  height: 6px;
  background: var(--surf-3);
  border-radius: var(--r-p);
  overflow: hidden;
}
.progression > i {
  display: block; height: 100%;
  background: linear-gradient(90deg, var(--acc), var(--acc-vif));
  border-radius: var(--r-p);
  transition: width calc(.5s * var(--anim-vit)) var(--ressort);
}
.progression.fine { height: 4px; }

/* --- Listes --------------------------------------------------------------- */

.liste { display: flex; flex-direction: column; }
.liste > * + * { border-top: 1px solid var(--bord); }

/* Un portefeuille entier tient dans la liste — deux mille quarante-huit rangs
   pour Sotteville, et ils sont poses tels quels. `content-visibility: auto` a
   ete essaye puis retire : sur un enfant de conteneur flex, Chrome ignore le
   `contain-intrinsic-size` qui l'accompagne, si bien que les rangs hors ecran
   reservaient 23 px au lieu de 62 — l'ascenseur annoncait 48 000 px de haut au
   lieu de 127 000, et s'allongeait a mesure qu'on descendait. Sans lui, une
   mise en page forcee sur les deux mille rangs se mesure a moins d'une
   milliseconde : il n'y avait rien a gagner. */

.rang {
  display: flex; align-items: center; gap: var(--e3);
  padding: 11px var(--e4);
  transition: background calc(.12s * var(--anim-vit));
}
.rang:hover { background: var(--surf-2); }
.rang.selectionne { background: var(--acc-sourd); }

/* Etape dont la visite est faite : la ligne prend la couleur du resultat en
   degrade â€” franche contre le filet de gauche, eteinte avant la pastille de
   droite. Le regard descend la liste par la tranche au lieu de buter sur six
   aplats. Le degrade s'eteint vers la meme couleur en alpha zero et non vers
   `transparent`, qui virerait au gris en chemin. La couleur arrive en variable
   inline, sinon le survol ne pourrait plus reprendre la main. */
.rang.teinte {
  position: relative;
  background: linear-gradient(90deg, var(--t-vif) 0%, var(--t-doux) 45%,
                                     var(--t-nul) 92%);
}
.rang.teinte::before {          /* le filet s'eclaire vers le haut, comme une
                                   tranche prise de biais par la lumiere */
  content: "";
  position: absolute; inset: 0 auto 0 0; width: 3px;
  background: linear-gradient(180deg, var(--teinte), var(--t-survol));
}
.rang.teinte:hover {
  background: linear-gradient(90deg, var(--t-survol) 0%, var(--t-doux) 60%,
                                     var(--t-nul) 100%);
}

/* La porte ouverte dans l'encart de gauche. Elle l'emporte sur la teinte de son
   statut — c'est celle qu'on est en train de lire, il faut la retrouver du
   regard sans la chercher — mais garde son filet a gauche, qui passe a l'orange
   le temps qu'elle soit affichee. Le selecteur nomme l'attribut de la ligne
   pour que la regle ne deborde pas sur les listes des autres vues, ou
   « selectionne » veut deja dire autre chose. */
.rang[data-etape-ligne] { position: relative; }
.rang[data-etape-ligne].selectionne { background: var(--acc-sourd); }
.rang[data-etape-ligne].selectionne::before {
  content: "";
  position: absolute; inset: 0 auto 0 0; width: 3px;
  background: var(--acc);
}

/* Les portes du jour. Un seul cadre jaune, autour du bloc entier, plutot qu'un
   lisere par ligne : quinze cadres empiles se lisaient comme quinze alertes,
   alors qu'ils ne disaient qu'une chose — « voila la journee ». Un cadre le dit
   d'un coup, et la liste redevient une liste.

   Un cadre plutot qu'un fond : chaque ligne garde la teinte de son statut,
   qu'un aplat de plus rendrait illisible. En « outline » et non en « border »,
   pour que le cadre se pose par-dessus sans decaler le bloc d'un pixel ni
   desaligner la colonne des numeros. Le JS ouvre le bloc avant la premiere
   porte du jour et le referme apres la derniere — voir VUES.tournee.suivi. */
.bloc-du-jour {
  display: flex; flex-direction: column;
  /* `flex: none` n'est pas cosmetique. La liste est un conteneur flex a hauteur
     bornee, et un enfant en `overflow: hidden` perd sa taille minimale
     automatique : le bloc se laissait alors comprimer et rognait sa derniere
     porte d'une quinzaine de pixels, la seule ligne de la liste a etre coupee. */
  flex: none;
  margin: var(--e2) 0;
  border-radius: var(--r-s, 9px);
  outline: 2px solid var(--alerte, #fab219);
  outline-offset: -2px;
  overflow: hidden;                /* les fonds de survol suivent les angles */
  animation: respire-du-jour calc(2.6s * var(--anim-vit)) ease-in-out infinite;
}
/* Le trait de separation general s'arrete au bord du bloc : il doublerait le
   cadre. A l'interieur, les lignes gardent le leur, sinon elles se collent. */
.liste > .bloc-du-jour { border-top: 0; }
.bloc-du-jour > * + * { border-top: 1px solid var(--bord); }

/* La legende du cadre, facon fieldset : de quoi savoir ce qu'il encadre sans
   remonter a la carte « Objectif du jour ». Elle porte le compte de portes et
   les kilometres du jour, les deux chiffres qu'on redemande en cours de route. */
.bloc-du-jour .titre-du-jour {
  padding: 6px var(--e4);
  font-size: 11.5px; font-weight: 700; letter-spacing: .01em;
  color: var(--alerte, #fab219);
  background: color-mix(in srgb, var(--alerte, #fab219) 12%, transparent);
}
.rang.du-jour .numero { background: var(--alerte, #fab219); color: #1a1a1f; }

/* Le cadre respire. Un trait fixe se fond dans le decor des qu'on a defile deux
   fois — trois cents lignes plus bas, on ne sait plus lequel des jaunes est
   celui du jour. La couleur bat donc lentement, sur les 2,6 s du halo des
   alertes pour que l'application n'ait qu'un seul pouls. Un seul battement, et
   non plus une vague de retards ligne a ligne : le bloc est un objet, il pulse
   comme un objet.

   Seule la couleur bouge, jamais la geometrie. Un bloc qui gigote au milieu
   d'une liste qu'on parcourt au doigt fait rater la ligne visee, et le cadre
   est en outline precisement pour ne rien decaler. */
@keyframes respire-du-jour {
  0%, 100% { outline-color: var(--alerte, #fab219); }
  50%      { outline-color: color-mix(in srgb, var(--alerte, #fab219) 30%,
                                      transparent); }
}

@media (prefers-reduced-motion: reduce) {
  .bloc-du-jour { animation: none; }        /* le cadre reste, plein et fixe */
}

/* Retour visuel du bouton de reprise : la ligne rejointe se signale une
   seconde, sinon on ne sait pas laquelle on vient d'atteindre. */
@keyframes clignote-ligne {
  0%, 100% { background: transparent; }
  35%      { background: rgba(250, 178, 25, .22); }
}
.rang.clignote { animation: clignote-ligne calc(.55s * var(--anim-vit)) ease-in-out 3; }

/* La prise pour deplacer une fiche dans la tournee.

   Une poignee plutot que la ligne entiere : sur un telephone, tirer n'importe
   ou dans une liste qui defile ferait partir la fiche a chaque fois qu'on veut
   descendre. « touch-action: none » est ce qui rend le geste possible au
   doigt — sans lui, le navigateur prend le mouvement pour un defilement et ne
   nous envoie plus rien. Discrete au repos, elle se revele au survol de sa
   ligne : deux mille poignees a pleine encre feraient une colonne de bruit. */
.rang .poignee-rang {
  flex: none;
  display: grid; place-items: center;
  width: 18px; height: 26px;
  margin-left: -4px;
  border-radius: 5px;
  color: var(--txt-3);
  opacity: .3;
  cursor: grab;
  touch-action: none;
}
.rang:hover .poignee-rang { opacity: .75; }
.rang .poignee-rang:hover { opacity: 1; background: var(--surf-3); }
.rang .poignee-rang svg { width: 14px; height: 14px; }

/* La ligne pendant qu'on la tient. Elle reste dans le flux — c'est elle qu'on
   deplace entre ses voisines, pas un fantome pose par-dessus — et se signale
   par un cadre tirete : on doit voir ou elle est rendue, y compris quand elle
   passe sur un rang deja teinte par son statut. */
.rang.se-glisse {
  background: var(--surf-3);
  outline: 2px dashed var(--acc);
  outline-offset: -2px;
  border-radius: var(--r-xs);
}

body.en-glissade { cursor: grabbing; user-select: none; }
body.en-glissade .poignee-rang { cursor: grabbing; }

/* Le survol souleve les lignes d'un pixel (voir .cliquable). Pendant le
   glissement, cela les fait vibrer sous le doigt juste au moment ou l'on vise
   une frontiere entre deux : on le suspend. */
body.en-glissade .rang { transform: none; }

/* Bouton posé au-dessus de la page, hors du flux : sur trois cents étapes, la
   commande doit rester sous le pouce quel que soit l'endroit où on a défilé. */
.flottant-bas {
  position: fixed; right: 24px; bottom: 24px; z-index: 60;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 11px 16px;
  border: 1px solid var(--bord);
  border-radius: 999px;
  background: var(--surf-3);
  color: var(--txt);
  font: inherit; font-size: 13px; font-weight: 600;
  box-shadow: 0 6px 22px rgba(0, 0, 0, .45);
  cursor: pointer;
}
.flottant-bas:hover { background: var(--acc); color: var(--sur-acc); border-color: transparent; }
.flottant-bas svg { width: 16px; height: 16px; }

@media (max-width: 980px) {
  /* La barre du bas occupe déjà le pouce : le bouton se pose au-dessus. */
  .flottant-bas { right: 14px; bottom: 84px; padding: 10px 14px; }
  .flottant-bas span { display: none; }        /* la flèche suffit */
}
.rang .principal-txt { min-width: 0; flex: 1; }
.rang .nom { font-weight: 600; font-size: 13.5px; }
.rang .meta { font-size: 12px; color: var(--txt-3); margin-top: 1px; }
.rang .fin { margin-left: auto; display: flex; align-items: center; gap: var(--e2); flex: none; }

/* Le rang du client dans son portefeuille. Pas la pastille ronde ci-dessous :
   a quatre chiffres elle deborderait. Un chiffre discret, cale a droite sur
   une largeur fixe, pour que les unites et les milliers tombent sur la meme
   colonne et que les noms ne se decalent pas d'une ligne a l'autre. Les
   chiffres tabulaires servent exactement a ca. */
.rang .rang-numero {
  flex: none; width: 34px;
  text-align: right;
  font-size: 11.5px; font-weight: 600;
  color: var(--txt-3);
  font-variant-numeric: tabular-nums;
}
.rang.selectionne .rang-numero { color: var(--acc-vif); }

.numero {
  width: 26px; height: 26px; flex: none;
  border-radius: 50%;
  display: grid; place-items: center;
  font-size: 11.5px; font-weight: 700;
  background: var(--surf-3);
  color: var(--txt-2);
  font-variant-numeric: tabular-nums;
}
.numero.courant { background: var(--acc); color: var(--sur-acc); }
.numero.fait { background: rgba(34, 197, 94, .16); color: var(--bon); }

.fait-barre .nom { text-decoration: line-through; opacity: .5; }
.fait-barre .meta { opacity: .5; }

/* --- Equipe : la grille du mois -------------------------------------------

   Une case par jour, la couleur du statut en fond. On lit un mois entier d'un
   coup d'oeil : les jours travailles font un bloc vert, un conge une bande
   bleue, et ce qui manque saute aux yeux parce que la case reste vide. */

.mois-jours {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: var(--e2);
}

.mois-jours .entete-jour {
  font-size: 10.5px; font-weight: 650; letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--txt-3);
  text-align: center;
  padding-bottom: 2px;
}

.case-jour {
  position: relative;
  min-height: 64px;
  padding: 6px;
  border: 1px solid var(--bord);
  border-radius: var(--r-s);
  background: var(--surf-1);
  cursor: pointer;
  transition: border-color calc(.14s * var(--anim-vit)), background calc(.14s * var(--anim-vit)), transform calc(.1s * var(--anim-vit)) var(--ressort);
}
.case-jour:hover { border-color: var(--bord-fort); transform: translateY(-1px); }
/* Les jours des mois voisins comblent la semaine : ils sont la pour l'alignement
   et ne se cliquent pas. */
.case-jour.dehors { opacity: .25; cursor: default; background: transparent; }
.case-jour.dehors:hover { transform: none; border-color: var(--bord); }
.case-jour.aujourdhui { border-color: var(--acc); }
/* Une journee a venir se declare (on pose ses conges a l'avance), mais elle
   s'affiche en retrait : ce n'est pas encore du travail fait. */
.case-jour.futur:not(.pose) { opacity: .55; }

.case-jour .num {
  font-size: 12px; font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--txt-3);
}
.case-jour.aujourdhui .num { color: var(--acc-vif); }

.case-jour.pose {
  background: color-mix(in srgb, var(--j) 14%, var(--surf-1));
  border-color: color-mix(in srgb, var(--j) 45%, transparent);
}
.case-jour .lettre {
  position: absolute; left: 7px; bottom: 6px;
  font-size: 18px; font-weight: 700; line-height: 1;
  color: var(--j);
}

/* Le visa du responsable. Discret tant qu'il manque, franc une fois pose :
   c'est l'absence qui doit se voir, pas la presence. */
.case-jour .visa {
  position: absolute; right: 5px; bottom: 5px;
  width: 19px; height: 19px;
  display: grid; place-items: center;
  border: 1px solid var(--bord-fort);
  border-radius: 50%;
  background: var(--surf-2);
  color: var(--txt-3);
  font-size: 11px; line-height: 1;
  cursor: pointer;
  padding: 0;
  transition: background calc(.14s * var(--anim-vit)), color calc(.14s * var(--anim-vit)), border-color calc(.14s * var(--anim-vit));
}
.case-jour .visa:hover { border-color: var(--bon); color: var(--bon); }
.case-jour .visa.ok {
  background: var(--bon); border-color: var(--bon); color: #06240f;
}

.case-jour .pastille-note {
  position: absolute; right: 7px; top: 7px;
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--txt-3);
}

/* La legende, qui sert aussi de compteur du mois. */
.puce-jour {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 9px;
  border: 1px solid var(--bord);
  border-radius: var(--r-p);
  font-size: 11.5px;
  color: var(--txt-2);
}
.puce-jour i {
  width: 9px; height: 9px; border-radius: 3px; background: var(--j);
}
.puce-jour b { color: var(--txt); font-variant-numeric: tabular-nums; }

.titre-menu .point-statut,
.flottant .point-statut {
  display: inline-block;
  width: 9px; height: 9px; border-radius: 3px;
  margin-right: 8px;
}

/* --- Equipe : conges et classements ---------------------------------------- */

.conge.attente { border-color: var(--acc-bord); }
.conge.refuse .nom, .conge.annule .nom { text-decoration: line-through; }
.conge.refuse, .conge.annule { opacity: .6; }

.classement { display: flex; flex-direction: column; gap: 7px; }

.ligne-classement {
  display: flex; align-items: center; gap: var(--e3);
  padding: 5px 8px;
  border-radius: var(--r-xs);
}
/* Se retrouver dans le classement doit etre immediat : c'est la premiere
   chose qu'on y cherche. */
.ligne-classement.moi { background: var(--acc-sourd); }
.ligne-classement .place {
  width: 26px; flex: none; text-align: center;
  font-size: 13px; font-variant-numeric: tabular-nums;
  color: var(--txt-3);
}
.ligne-classement .qui { width: 130px; flex: none; font-size: 13px; }
.ligne-classement.moi .qui { font-weight: 650; color: var(--acc-vif); }
.ligne-classement .barre {
  flex: 1; min-width: 60px; height: 9px;
  background: var(--surf-3);
  border-radius: var(--r-p);
  overflow: hidden;
}
.ligne-classement .barre i {
  display: block; height: 100%;
  background: var(--acc);
  border-radius: var(--r-p);
  transition: width calc(.4s * var(--anim-vit)) var(--ressort);
}
/* L'objectif atteint change la couleur, pas la longueur : la barre reste
   comparable d'une ligne a l'autre. */
.ligne-classement .barre i.atteint { background: var(--bon); }
.ligne-classement .valeur {
  min-width: 34px; text-align: right;
  font-variant-numeric: tabular-nums;
}

@media (max-width: 700px) {
  .case-jour { min-height: 52px; padding: 4px; }
  .case-jour .lettre { font-size: 15px; }
  .ligne-classement .qui { width: 90px; }
}

/* --- Le lecteur d'ecoute --------------------------------------------------

   Une frise qui dessine qui parle et quand, puis une rangee de commandes.
   L'orange reste la couleur de « moi », partout dans l'application : les
   autres voix prennent des teintes franchement differentes, pour qu'on lise
   l'alternance a la couleur sans avoir a compter les bandes. */

:root {
  --v-a: #4d94ec;
  --v-b: #22c55e;
  --v-c: #b57cff;
  --v-d: #fab219;
  --frise-h: 34px;
}

.lecteur-riche { margin-top: var(--e4); }

.frise {
  position: relative;
  height: var(--frise-h);
  margin-bottom: var(--e3);
  border-radius: var(--r-xs);
  overflow: hidden;
  cursor: pointer;
}
.frise .fond {
  position: absolute; inset: 0;
  background: var(--surf-3);
}

/* Chaque replique est une bande posee a sa place dans la duree. Elles sont
   fines et nombreuses : un contour les rendrait illisibles, un fond plein les
   distingue tres bien. */
.frise .bande {
  position: absolute; top: 6px; bottom: 6px;
  min-width: 2px;
  border-radius: 2px;
  opacity: .55;
  transition: opacity calc(.12s * var(--anim-vit));
}
.frise:hover .bande { opacity: .8; }

/* Ce qui est deja ecoute s'assombrit : on voit d'un coup ou on en est, meme
   quand la tete de lecture tombe dans un silence. */
.frise .lu {
  position: absolute; top: 0; bottom: 0; left: 0; width: 0;
  background: rgba(0, 0, 0, .38);
  pointer-events: none;
}
:root[data-theme="clair"] .frise .lu { background: rgba(0, 0, 0, .13); }

.frise .tete {
  position: absolute; top: 0; bottom: 0; left: 0;
  width: 2px; margin-left: -1px;
  background: var(--txt);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .45);
  pointer-events: none;
}

.commandes {
  display: flex; align-items: center; gap: var(--e2);
  flex-wrap: wrap;
}
.commandes .horloge {
  font-variant-numeric: tabular-nums;
  font-size: 12.5px;
  letter-spacing: .02em;
}
.commandes .btn.carre.mini {
  font-size: 11.5px; font-weight: 650;
  font-variant-numeric: tabular-nums;
}

/* --- Chercher dans la transcription --------------------------------------- */

.outils-transcription {
  display: flex; align-items: center; gap: var(--e2);
  flex-wrap: wrap;
  margin-bottom: var(--e4);
  padding-bottom: var(--e3);
  border-bottom: 1px solid var(--bord);
}
.recherche.mini input { padding: 5px 9px 5px 30px; font-size: 12.5px; }
.recherche.mini svg { width: 15px; height: 15px; }
.segmente.mini button { padding: 4px 9px; font-size: 11.5px; }
#t-compte.vide { color: var(--grave); }

/* La replique d'une voix qu'on a mise de cote reste dans le fil, mais en
   retrait : la masquer ferait perdre le fil de la conversation, et le suivi de
   l'ecoute la traverse quand meme. */
.tour.hors-voix { opacity: .22; }
.tour.hors-voix .bulle { background: transparent; }

/* --- Apprendre ta voix, extrait par extrait -------------------------------- */

/* Une ligne par question : ecouter a gauche, ce qui est dit au milieu, la
   reponse a droite. Toujours la meme geometrie, parce qu'on en enchaine dix
   d'affilee et que le pouce doit retrouver le bouton sans le chercher. */
.questions-voix { display: flex; flex-direction: column; gap: var(--e2); margin-top: var(--e3); }
.question-voix {
  display: flex; align-items: center; gap: var(--e2);
  padding: var(--e2);
  border: 1px solid var(--bord);
  border-radius: var(--r-s);
  background: var(--surf-1);
}
.question-voix .dit { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
/* Le texte tient sur une ligne : ce qui compte est ce qu'on entend, pas ce
   qu'on lit — et une transcription douteuse ne doit pas souffler la reponse. */
.question-voix .texte {
  font-size: 13.5px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* --- Ce que le modele a compris de l'appel --------------------------------- */

/* Le panneau qui coiffe la transcription : l'issue de la vente, le resume, et
   ce que le client a dit. Il est encadre et non pas pose a plat, parce qu'il
   ne se lit pas comme la conversation en dessous — c'est une lecture, pas une
   parole, et la confusion serait grave sur un dossier client. */
.lecture-appel {
  border: 1px solid var(--bord);
  border-radius: var(--r);
  background: var(--surf-1);
  padding: var(--e3);
}
.lecture-appel .resume {
  margin: var(--e3) 0 0;
  font-size: 13.5px; line-height: 1.55;
  color: var(--txt-2);
}
/* Les capitales conviennent a un intitule de deux mots ; sur la phrase qui
   explique une liste vide, elles se lisent comme un avertissement. */
.lecture-appel .titre-idees {
  margin-top: var(--e3);
  text-transform: uppercase; letter-spacing: .04em; font-size: 10.5px;
}
.lecture-appel .sans-idees { margin-top: var(--e3); line-height: 1.5; }

/* Une vente perdue ne se lit pas comme le reste : c'est la seule etiquette de
   ce panneau qui demande qu'on fasse quelque chose aujourd'hui. */
.lecture-appel .etiq.issue.perte {
  background: color-mix(in srgb, var(--danger) 18%, transparent);
  color: var(--danger);
}

.lecture-appel .idees {
  list-style: none; margin: var(--e2) 0 0; padding: 0;
  display: flex; flex-direction: column; gap: 2px;
}
/* Chaque idee est un bouton pleine largeur : on clique n'importe ou sur la
   ligne pour aller l'ecouter, pas seulement sur la minute. */
.lecture-appel .idee button {
  display: flex; align-items: baseline; gap: var(--e2);
  width: 100%; padding: 6px 8px;
  border: 0; border-radius: var(--r-xs);
  background: transparent; color: var(--txt);
  font: inherit; font-size: 13.5px; line-height: 1.45;
  text-align: left; cursor: pointer;
  transition: background calc(.12s * var(--anim-vit));
}
.lecture-appel .idee button:hover { background: var(--surf-3); }
.lecture-appel .idee .genre { flex: none; font-size: 13px; }
.lecture-appel .idee .minute {
  margin-left: auto; flex: none;
  font-size: 11px; color: var(--txt-3); font-variant-numeric: tabular-nums;
}
/* Une idee dont on n'a pas retrouve la minute : meme place, mais elle ne fait
   pas semblant d'etre cliquable. */
.lecture-appel .idee.sourde {
  display: flex; align-items: baseline; gap: var(--e2);
  padding: 6px 8px;
  font-size: 13.5px; line-height: 1.45; color: var(--txt-2);
}

/* --- Transcription d'un appel --------------------------------------------- */

/* Deux colonnes de bulles : celui qui appelle a gauche, toi a droite. On lit
   qui parle a la position avant meme de lire l'etiquette. */
.transcription { display: flex; flex-direction: column; gap: var(--e3); }

.tour { display: flex; align-items: flex-start; gap: var(--e2); }
.tour.moi { flex-direction: row-reverse; }
/* une relance du meme locuteur se colle a la precedente : le fil se lit par
   blocs de parole, pas par lignes */
.tour.suite { margin-top: calc(-1 * var(--e3) + 3px); }
.tour.suite .bulle { border-top-left-radius: var(--r-xs); }
.tour.suite.moi .bulle {
  border-top-left-radius: var(--r-l); border-top-right-radius: var(--r-xs);
}

.tour .minute {
  flex: none; margin-top: 7px;
  padding: 2px 6px;
  border: 0; border-radius: var(--r-xs);
  background: transparent;
  color: var(--txt-3);
  font: inherit; font-size: 10.5px;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  opacity: 0; transition: opacity calc(.14s * var(--anim-vit)), background calc(.14s * var(--anim-vit));
}
.tour:hover .minute { opacity: 1; }
.tour .minute:hover { background: var(--surf-3); color: var(--txt); }

.tour .bulle {
  max-width: 76%;
  padding: 9px 13px;
  border-radius: var(--r-l);
  background: var(--surf-2);
  border: 1px solid var(--bord);
  font-size: 13.5px; line-height: 1.6;
  color: var(--txt);
}
.tour.moi .bulle {
  background: rgba(255, 121, 0, .1);
  border-color: rgba(255, 121, 0, .3);
}
.tour.sansvoix .bulle { max-width: 100%; border-style: dashed; }

.tour .qui {
  font-size: 10.5px; font-weight: 650; letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--txt-3);
  margin-bottom: 3px;
}
.tour.moi .qui { color: var(--acc-vif); }

/* --- Mots-cles surlignes et recommandation --------------------------------

   Le surlignage se fait en fond teinte, jamais en aplat sature : la bulle
   reste un texte qui se lit, pas un damier. Chaque famille garde sa couleur,
   et un trait sous le mot indique qu'il y a quelque chose a survoler â€” la
   couleur seule ne suffit pas a le dire. */

mark.cle {
  /* repli d'abord : si color-mix n'est pas compris, la declaration suivante
     est ignoree et le surlignage reste visible au lieu de disparaitre. */
  background: rgba(255, 255, 255, .10);
  border-bottom: 1.5px dotted var(--cle-teinte);
  background: color-mix(in srgb, var(--cle-teinte) 16%, transparent);
  border-bottom: 1.5px dotted color-mix(in srgb, var(--cle-teinte) 70%, transparent);
  border-radius: 3px;
  padding: 0 2px;
  color: inherit;
  cursor: help;
  transition: background calc(.12s * var(--anim-vit));
}
mark.cle:hover {
  background: color-mix(in srgb, var(--cle-teinte) 30%, transparent);
}

mark.cle[data-fam="depart"] { --cle-teinte: var(--danger); }
mark.cle[data-fam="refus"]  { --cle-teinte: var(--txt-2); }
mark.cle[data-fam="panne"]  { --cle-teinte: var(--alerte); }
mark.cle[data-fam="argent"] { --cle-teinte: var(--grave); }
mark.cle[data-fam="vente"]  { --cle-teinte: var(--bon); }
mark.cle[data-fam="rdv"]    { --cle-teinte: var(--info); }
mark.cle[data-fam="urgent"] { --cle-teinte: var(--danger); }

.legende-cles { flex-wrap: wrap; gap: var(--e2); margin-bottom: var(--e3); }
.puce-cle {
  padding: 2px 8px;
  border-radius: var(--r-p);
  background: var(--surf-3);
  border: 1px solid var(--bord);
  font-size: 11px; color: var(--txt-2);
}

/* Ce que le message appelle comme geste, sous le message. A la difference de
   l'infobulle, ca ne se survole pas : les messages les plus tranchants ne
   portent aucun mot-cle, donc rien a survoler, et le conseil doit se voir en
   lisant le fil. Discret par defaut, teinte en rouge quand l'echeance est
   passee — c'est la seule chose qu'on veuille reperer en faisant defiler. */
.msg-action {
  margin-top: 8px; padding: 8px 10px;
  border-radius: var(--r-s);
  background: var(--surf-2);
  border: 1px solid var(--bord);
  border-left: 3px solid var(--acc);
}
.msg-action.retard {
  border-left-color: var(--mauvais);
  background: color-mix(in srgb, var(--mauvais) 8%, var(--surf-2));
}
.msg-action .rangee { flex-wrap: wrap; gap: 6px; }

/* Une seule infobulle pour toute la page, en position fixe : dans le cadre
   defilant des messages, une infobulle en absolu serait coupee par le bord. */
.reco-flottante {
  position: fixed; z-index: 90;
  max-width: 340px;
  padding: 10px 12px;
  border-radius: var(--r);
  background: var(--surf-3);
  border: 1px solid var(--bord-fort);
  border-top: 2px solid var(--reco-teinte, var(--acc));
  box-shadow: var(--ombre-flottante);
  font-size: 12.5px; line-height: 1.5;
  pointer-events: none;                  /* jamais entre le curseur et le mot */
  animation: reco-entree calc(.12s * var(--anim-vit)) var(--ressort);
}
.reco-flottante.dessous { border-top: 1px solid var(--bord-fort);
                          border-bottom: 2px solid var(--reco-teinte, var(--acc)); }
.reco-titre  { font-weight: 650; margin-bottom: 4px; }
.reco-texte  { color: var(--txt); }

/* Une bulle qui porte un bouton doit recevoir le curseur ; les autres restent
   transparentes, sinon elles captent le survol des lignes qu'elles couvrent. */
.reco-flottante.agissante { pointer-events: auto; }
.reco-pied {
  margin-top: 10px;
  display: flex; align-items: center; justify-content: flex-end;
  gap: 8px; flex-wrap: wrap;
}
.reco-pied.muet {
  display: block;
  font-size: 11.5px; color: var(--txt-3);
}
.reco-source {
  margin-top: 8px; padding-top: 6px;
  border-top: 1px solid var(--liseret);
  font-size: 10.5px; color: var(--txt-3);
}

@keyframes reco-entree {
  from { opacity: 0; transform: translateY(calc(3px * var(--anim-amp))); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .reco-flottante { animation: none; }
}

/* --- Suivi de l'ecoute ----------------------------------------------------

   Le mot prononce est surligne, ce qui vient d'etre dit reste pleinement
   lisible, la suite s'estompe : on retrouve d'un coup d'oeil ou en est
   l'ecoute. Tant que rien n'est joue, le texte garde sa couleur normale â€”
   la transcription se lit aussi sans l'audio. */

.tour .mot {
  border-radius: var(--r-xs);
  transition: color calc(.18s * var(--anim-vit)) ease, background-color calc(.12s * var(--anim-vit)) ease;
}
.transcription .mot:hover { background: var(--surf-3); cursor: pointer; }

.transcription.karaoke .mot { color: var(--txt-3); }
/* « passe », et non « dit » : .dit appartient au fil de l'assistant, ou
   elle vaut display:flex. Un mot qui la portait devenait un bloc, et la
   transcription se lisait en colonne des qu'on l'ecoutait. */
.transcription.karaoke .mot.passe { color: var(--txt); }
.transcription.karaoke .mot.actif {
  color: var(--mot-actif);
  background: rgba(255, 121, 0, .34);
  /* le surlignage deborde legerement du mot : colle aux lettres, il etrangle */
  box-shadow: 0 0 0 2px rgba(255, 121, 0, .34);
}
/* dans tes propres bulles, deja teintees d'orange, il faut appuyer plus fort */
.transcription.karaoke .tour.moi .mot.actif {
  color: var(--sur-acc);
  background: var(--acc-vif);
  box-shadow: 0 0 0 2px var(--acc-vif);
}

/* Les occurrences cherchees. La visee courante est pleine, les autres sont
   juste teintees : on sait ou on est dans la liste sans quitter le fil. */
.transcription .mot.trouve {
  background: rgba(77, 148, 236, .22);
  box-shadow: 0 0 0 1px rgba(77, 148, 236, .35);
}
.transcription .mot.vise {
  background: var(--info);
  color: #fff;
  box-shadow: 0 0 0 2px var(--info);
}

/* la phrase en cours se detache du reste du fil */
.tour.encours .bulle { border-color: var(--acc-bord); }
/* tes bulles sont deja cerclees d'orange : il faut y appuyer davantage */
.tour.encours.moi .bulle { border-color: var(--acc); }

.travaux { border-color: var(--acc); }

/* --- Le panneau de veille --------------------------------------------------

   Il repond a « il fait quoi, la ? » et rien d'autre. D'ou la hierarchie :
   l'etape en gras, la phrase qui l'explique juste dessous, puis ce qui passe
   a l'instant dans un cadre a part — c'est la ligne qu'on vient lire, elle ne
   doit pas se confondre avec le commentaire. Les compteurs viennent apres,
   parce qu'ils changent lentement, et le temps restant ferme le bloc. */

/* Replie, le panneau n'est plus qu'une bande : la carte perd son ventre et ne
   garde que sa bordure. La classe est doublee — .carte.veille-carte — parce
   que .carte.serree pose deja un padding, et qu'a specificite egale c'est la
   derniere reglee qui gagnerait. */
.carte.veille-carte { padding: 0; overflow: hidden; }
.veille-carte .dedans > .rangee { margin: 0; }

/* La bande, toujours la. Un bouton pleine largeur plutot qu'une barre avec un
   chevron dedans : la cible est la ligne entiere, on ne vise pas dix pixels. */
.veille-bandeau {
  display: flex; align-items: center; gap: var(--e2);
  width: 100%;
  padding: 9px 14px;
  border: 0; background: transparent;
  color: var(--txt);
  font: inherit; font-size: 13px;
  text-align: left; cursor: pointer;
  transition: background calc(.15s * var(--anim-vit));
}
.veille-bandeau:hover { background: var(--surf-2); }
.veille-bandeau b { font-weight: 600; }
.veille-bandeau .chevron {
  color: var(--txt-3); font-size: 15px; line-height: 1;
  transform: rotate(180deg);
  transition: transform calc(.35s * var(--anim-vit)) ease;
}
.veille-carte.ouvert .veille-bandeau .chevron { transform: rotate(0deg); }

/* Le repli reprend .deroulant — meme technique que le reste de l'application :
   grid-template-rows passe de 0fr a 1fr, et le navigateur interpole entre
   « rien » et « la hauteur du contenu » sans qu'on ait a la mesurer.
   Deux ecarts, parce que la classe qui declenche est ici sur la carte et non
   sur le deroulant lui-meme : la transition remplace l'animation, et le
   contenu reste visible pendant qu'il se replie — le cacher d'un coup ferait
   disparaitre le texte avant que la hauteur ait bouge. */
.veille-carte > .deroulant {
  transition: grid-template-rows calc(.35s * var(--anim-vit)) cubic-bezier(.4, 0, .2, 1),
              opacity calc(.25s * var(--anim-vit)) ease;
  opacity: 0;
}
.veille-carte > .deroulant > * { visibility: visible; }
.veille-carte.ouvert > .deroulant { grid-template-rows: 1fr; opacity: 1; }

.veille-carte .dedans {
  display: flex; flex-direction: column; gap: var(--e3);
  padding: 0 14px;
}
.veille-carte.ouvert .dedans { padding: 2px 14px 14px; }

/* Qui prefere que rien ne bouge n'a pas a subir le depliement. */
@media (prefers-reduced-motion: reduce) {
  .veille-carte > .deroulant,
  .veille-bandeau .chevron { transition: none; }
}

/* La phrase qui dit a quoi sert l'etape. Grise et serree : elle se lit une
   fois, on ne la relit pas a chaque coup d'oeil. */
.veille-quoi {
  margin: 0;
  font-size: 12.5px; line-height: 1.5;
  color: var(--txt-2);
  max-width: 68ch;
}

/* Le chemin des six etapes. Celle en cours est allumee, les precedentes
   restent lisibles pour situer l'avancement dans le tour. */
.veille-chemin { display: flex; flex-wrap: wrap; gap: var(--e1); }
.veille-chemin .pas {
  font-size: 11px; padding: 3px 8px;
  border: 1px solid var(--bord);
  border-radius: 999px;
  color: var(--txt-3);
  white-space: nowrap;
}
.veille-chemin .pas.faite { color: var(--txt-2); border-color: var(--bord-fort); }
.veille-chemin .pas.ici {
  color: var(--acc-vif);
  border-color: var(--acc-bord);
  background: var(--acc-sourd);
  font-weight: 600;
}

/* Ce qui passe a l'instant : un appel, un fichier, une recherche en cours. */
.veille-sujet {
  display: flex; align-items: baseline; gap: var(--e2);
  padding: 10px 12px;
  border-radius: var(--r-s);
  background: var(--surf-3);
  border-left: 3px solid var(--acc);
  font-size: 13px; color: var(--txt);
}
.veille-sujet > span {
  font-size: 10px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--txt-3); white-space: nowrap;
}

.veille-bilan { font-size: 12.5px; color: var(--txt-2); }
/* L'anomalie se signale sans crier : elle n'appelle aucune action. */
.veille-muettes {
  margin-left: var(--e2);
  padding: 1px 7px;
  border-radius: 999px;
  border: 1px solid var(--bord);
  color: var(--txt-3);
  font-size: 11px;
  cursor: help;
}

/* Quatre compteurs de meme largeur : le regard compare des colonnes, pas des
   phrases. Celui qui a du retard s'allume, les autres restent gris — sinon
   « tout est a jour » aurait la meme force visuelle qu'une file de cent. */
.veille-tuiles {
  display: grid; gap: var(--e2);
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.veille-tuile {
  padding: 10px 12px;
  border: 1px solid var(--bord);
  border-radius: var(--r-s);
  background: var(--surf-2);
}
.veille-tuile.chaude { border-color: var(--acc-bord); background: var(--acc-sourd); }
.veille-tuile .t { font-size: 11.5px; color: var(--txt-2); }
.veille-tuile .v {
  font-size: 22px; font-weight: 650; line-height: 1.2;
  margin-top: 2px; color: var(--txt);
  font-variant-numeric: tabular-nums;
}
.veille-tuile.chaude .v { color: var(--acc-vif); }
.veille-tuile .d { font-size: 11px; color: var(--txt-3); line-height: 1.4; }

.veille-fin {
  padding: 10px 12px;
  border-radius: var(--r-s);
  background: var(--surf-3);
  font-size: 13px; color: var(--txt-2);
}
.veille-fin b { color: var(--txt); }
.veille-fin.bon { color: var(--bon); }

/* --- La file en cours ------------------------------------------------------

   La barre n'est pas animee par une boucle : le rendu lui donne la largeur
   qu'elle devra avoir au prochain rafraichissement, et la transition fait le
   chemin. « linear » et non « ease » : un travail regulier ne doit pas
   sembler ralentir au milieu. */

.veille-file { display: flex; flex-direction: column; gap: var(--e2); }

.veille-ligne {
  padding: 9px 12px;
  border-radius: var(--r-s);
  background: var(--surf-3);
  border-left: 3px solid var(--acc);
}
.veille-ligne .tete { display: flex; align-items: baseline; gap: var(--e2); }
.veille-ligne .quoi {
  font-size: 13px; color: var(--txt);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.veille-ligne .rail {
  margin-top: 7px; height: 4px;
  border-radius: 999px;
  background: var(--bord-fort);
  overflow: hidden;
}
.veille-ligne .rail > i {
  display: block; height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--acc), var(--acc-vif));
  transition: width calc(2.6s * var(--anim-vit)) linear;
}

/* Ce qui vient d'etre fait. La ligne s'efface par son opacite, calculee sur
   son age : rien a retirer, rien qui clignote. */
.veille-finis {
  display: flex; flex-direction: column; gap: 2px;
  padding-left: var(--e2);
  border-left: 2px solid var(--bord);
}
.veille-fini {
  display: flex; align-items: baseline; gap: var(--e2);
  font-size: 12px; color: var(--txt-2);
  animation: veille-entree calc(.35s * var(--anim-vit)) ease-out;
}
@keyframes veille-entree {
  from { opacity: 0; transform: translateY(calc(-4px * var(--anim-amp))); }
}

/* --- Le curseur de puissance ------------------------------------------------

   Quatre chiffres soudes, gros comme le reste de la ligne de reglages. Le
   bloc precedent — titre, quatre paves, phrase d'explication — pesait autant
   que les compteurs qu'il accompagnait, pour un reglage qu'on touche deux
   fois par mois. Ce qu'il expliquait vit maintenant dans les infobulles. */

.crans {
  display: inline-flex;
  border: 1px solid var(--bord);
  border-radius: var(--r-xs);
  overflow: hidden;
}
.crans .cran {
  padding: 3px 9px;
  border: 0; border-right: 1px solid var(--bord);
  background: transparent;
  color: var(--txt-3);
  font-size: 11.5px; font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: background calc(.15s * var(--anim-vit)), color calc(.15s * var(--anim-vit));
}
.crans .cran:last-child { border-right: 0; }
.crans .cran:hover { background: var(--surf-3); color: var(--txt); }
.crans .cran.ici {
  background: var(--acc-sourd);
  color: var(--acc-vif);
  font-weight: 650;
}

/* Les reglages et le detail technique vivent maintenant dans des repliants :
   le filet et l'ecart viennent de .repli. Il ne reste ici qu'a defaire ceux
   que la premiere rangee de chaque repliant pose pour son propre compte —
   deux traits colles ne separent rien de plus qu'un seul. Les suivantes
   gardent le leur : c'est ce qui separe le moteur du modele de lecture. */
.veille-carte .dedans > .repli { margin-top: 0; }
.veille-carte .repli-dedans > .rangee:first-child {
  margin-top: 0 !important; border-top: 0 !important; padding-top: 0 !important;
}
.journal {
  margin-top: 10px;
  font-size: 11.5px; line-height: 1.6;
  color: var(--txt-3);
  font-family: var(--mono, ui-monospace, "Cascadia Mono", monospace);
}

/* --- Messages vocaux ------------------------------------------------------ */

/* La pastille porte l'urgence, jamais la couleur seule : le niveau est aussi
   ecrit en toutes lettres dans la fiche, avec ce qui l'a declenche. */
.rang.message .pastille-urgence {
  width: 9px; height: 9px; flex: none;
  border-radius: 50%;
  background: var(--u);
  box-shadow: 0 0 0 3px var(--u-doux);
}

.jauge {
  display: flex; align-items: center; gap: var(--e3);
  padding: 11px 14px;
  border-radius: var(--r);
  background: var(--u-doux);
  border: 1px solid var(--u-bord);
}
.jauge .emo { font-size: 17px; line-height: 1; }
.jauge b { color: var(--u); font-size: 13.5px; }

/* --- Tableaux ------------------------------------------------------------- */

.tableau-cadre {
  border: 1px solid var(--bord);
  border-radius: var(--r);
  overflow: auto;
  max-height: 460px;
}
/* Un tableau dont on connait la taille d'avance — les sept jours d'une semaine
   — se montre en entier. Le plafond ci-dessus est fait pour les listes qui
   peuvent compter des centaines de lignes ; ici il ne cachait que la ligne de
   total, c'est-a-dire la plus utile. */
.tableau-cadre.entier { max-height: none; }
.tableau { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.tableau th {
  position: sticky; top: 0; z-index: 1;
  background: var(--surf-2);
  text-align: left;
  font-size: 10.5px; font-weight: 650; letter-spacing: .07em;
  text-transform: uppercase; color: var(--txt-3);
  padding: 9px var(--e3);
  border-bottom: 1px solid var(--bord);
  white-space: nowrap;
}
.tableau td {
  padding: 9px var(--e3);
  border-bottom: 1px solid var(--bord);
  color: var(--txt-2);
  vertical-align: top;
}
.tableau tr:last-child td { border-bottom: 0; }
.tableau tbody tr:hover td { background: var(--surf-2); color: var(--txt); }
.tableau .nombre { text-align: right; font-variant-numeric: tabular-nums; color: var(--txt); font-weight: 600; }

/* En-tete sur deux etages : la seconde ligne se cale sous la premiere. Sans
   ce decalage les deux collent au meme « top » et se superposent des que le
   tableau defile. La hauteur vient du padding et de la casse du titre. */
.tableau thead tr:first-child th  { top: 0; }
.tableau thead tr:nth-child(2) th { top: 31px; }
.tableau thead tr:first-child th:not(:empty) {
  text-align: center;
  border-bottom: 1px solid var(--liseret);
  color: var(--txt-2);
}

/* La ligne de total ferme le tableau, elle ne le surplombe pas : sans cette
   remise a zero elle heritait du « sticky » des en-tetes et venait se coller
   en haut au premier defilement. */
.tableau tfoot th {
  position: static;
  background: var(--surf-2);
  color: var(--txt);
  font-size: 12.5px; text-transform: none; letter-spacing: 0;
  border-top: 1px solid var(--bord-fort); border-bottom: 0;
}

/* --- Etats vides ---------------------------------------------------------- */

.vide {
  display: flex; flex-direction: column; align-items: center; gap: var(--e3);
  padding: var(--e7) var(--e4);
  text-align: center;
  color: var(--txt-3);
}
.vide .emo { font-size: 30px; opacity: .55; }
.vide .titre { color: var(--txt-2); font-weight: 600; font-size: 14px; }
.vide p { font-size: 12.5px; max-width: 380px; line-height: 1.6; }

.note {
  display: flex; gap: 10px;
  padding: var(--e3);
  border-radius: var(--r);
  background: var(--surf-1);
  border: 1px solid var(--bord);
  font-size: 12.5px;
  color: var(--txt-2);
  line-height: 1.6;
}
.note.attention { background: rgba(250, 178, 25, .07); border-color: rgba(250, 178, 25, .25); color: var(--alerte-txt); }
.note.grave { background: rgba(239, 68, 68, .07); border-color: rgba(239, 68, 68, .28); color: var(--danger-txt); }
.note .emo { flex: none; }

/* --- Menus flottants ------------------------------------------------------ */

.flottant {
  position: fixed;
  z-index: 60;
  min-width: 230px;
  max-height: 62vh;
  overflow-y: auto;
  padding: 6px;
  background: var(--surf-2);
  border: 1px solid var(--bord-fort);
  border-radius: var(--r);
  box-shadow: var(--ombre-flottante);
  animation: apparait calc(.14s * var(--anim-vit)) var(--ressort);
}
.flottant .titre-menu {
  font-size: 10.5px; font-weight: 650; letter-spacing: .08em;
  text-transform: uppercase; color: var(--txt-3);
  padding: 7px 10px 5px;
}
.flottant button {
  display: flex; align-items: center; gap: 8px;
  width: 100%; text-align: left;
  padding: 8px 10px;
  border: 0; border-radius: 8px;
  background: transparent;
  color: var(--txt);
  font: inherit; font-size: 13px;
  cursor: pointer;
}
.flottant button:hover { background: var(--surf-3); }
.flottant hr { border: 0; border-top: 1px solid var(--bord); margin: 5px 0; }

@keyframes apparait {
  from { opacity: 0; transform: translateY(calc(-6px * var(--anim-amp))) scale(calc(1 - .02 * var(--anim-amp))); }
  to   { opacity: 1; transform: none; }
}

/* Pose au-dessus de son point — voir menuFlottant({ vers: "haut" }). Il en
   sort et il y retourne : le mouvement part du bas et l'origine est le bord
   qui touche le point, pas le centre de la boite. */
.flottant.vers-haut {
  transform-origin: 50% 100%;
  animation: apparait-dessus calc(.16s * var(--anim-vit)) var(--ressort);
}
@keyframes apparait-dessus {
  from { opacity: 0; transform: translateY(calc(7px * var(--anim-amp))) scale(calc(1 - .03 * var(--anim-amp))); }
  to   { opacity: 1; transform: none; }
}

/* Le depart. Sans lui le menu s'effacait d'un coup, ce qui se lit comme un
   raté d'affichage plutot que comme une fermeture — surtout au second clic
   sur la meme pastille, ou l'on cherche a voir qu'on vient de refermer.
   « forwards » garde le dernier etat : la boite reste invisible pendant les
   quelques millisecondes qui separent la fin de l'animation de son retrait. */
.flottant.part {
  pointer-events: none;
  animation: part-menu calc(.13s * var(--anim-vit)) ease forwards;
}
@keyframes part-menu {
  to { opacity: 0; transform: translateY(calc(-4px * var(--anim-amp))) scale(.98); }
}
.flottant.vers-haut.part { animation: part-menu-dessus calc(.13s * var(--anim-vit)) ease forwards; }
@keyframes part-menu-dessus {
  to { opacity: 0; transform: translateY(calc(5px * var(--anim-amp))) scale(calc(1 - .03 * var(--anim-amp))); }
}

@media (prefers-reduced-motion: reduce) {
  /* Le menu parait et s'en va sans glisser. Il disparait des qu'il part —
     l'animation etait ce qui le retirait de l'ecran, il faut donc le cacher
     autrement le temps que la boite soit reellement enlevee. */
  .flottant, .flottant.vers-haut { animation: none; }
  .flottant.part, .flottant.vers-haut.part { animation: none; opacity: 0; }
}

/* --- Modales -------------------------------------------------------------- */

.voile {
  position: fixed; inset: 0; z-index: 70;
  background: rgba(0, 0, 0, .62);
  backdrop-filter: blur(3px);
  display: grid; place-items: center;
  padding: var(--e5);
  animation: fondu calc(.16s * var(--anim-vit)) ease;
}
@keyframes fondu { from { opacity: 0; } to { opacity: 1; } }

.modale {
  width: min(560px, 100%);
  max-height: 86vh;
  display: flex; flex-direction: column;
  background: var(--surf-1);
  border: 1px solid var(--bord-fort);
  border-radius: var(--r-xl);
  box-shadow: var(--ombre-flottante);
  animation: monte calc(.2s * var(--anim-vit)) var(--ressort);
  overflow: hidden;
}
.modale.large { width: min(880px, 100%); }
@keyframes monte {
  from { opacity: 0; transform: translateY(calc(14px * var(--anim-amp))) scale(calc(1 - .02 * var(--anim-amp))); }
  to   { opacity: 1; transform: none; }
}

.modale > header {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--e3);
  padding: var(--e4) var(--e5);
  border-bottom: 1px solid var(--bord);
}
.modale > .contenu { padding: var(--e5); overflow-y: auto; display: grid; gap: var(--e4); }
.modale > footer {
  display: flex; justify-content: flex-end; gap: var(--e2);
  padding: var(--e3) var(--e5) var(--e4);
  border-top: 1px solid var(--bord);
}

/* --- Notifications -------------------------------------------------------- */

.toasts {
  position: fixed; z-index: 90;
  right: var(--e5); bottom: var(--e5);
  display: flex; flex-direction: column; gap: var(--e2);
  pointer-events: none;
}
.toast {
  display: flex; align-items: flex-start; gap: 10px;
  min-width: 260px; max-width: 420px;
  padding: 11px var(--e3);
  background: var(--surf-2);
  border: 1px solid var(--bord-fort);
  border-left: 3px solid var(--acc);
  border-radius: var(--r);
  box-shadow: var(--ombre-flottante);
  font-size: 13px;
  pointer-events: auto;
  animation: glisse calc(.22s * var(--anim-vit)) var(--ressort);
}
.toast.bon { border-left-color: var(--bon); }
.toast.mauvais { border-left-color: var(--danger); }
.toast .fermer { margin-left: auto; background: none; border: 0; color: var(--txt-3); cursor: pointer; font-size: 15px; line-height: 1; }
@keyframes glisse {
  from { opacity: 0; transform: translateX(calc(24px * var(--anim-amp))); }
  to   { opacity: 1; transform: none; }
}


/* --- L'echelle des boites flottantes ---------------------------------------

   Le troisieme reglage. Il ne touche que ce qui s'ouvre par-dessus la page —
   menus, fenetres, bulles, messages — et jamais la mise en page elle-meme :
   agrandir une colonne se fait par la taille de l'interface, sur le compte.
   « scale » est une propriete a part, elle se compose avec les « transform »
   des animations sans les ecraser. */
.flottant, .modale, .reco-flottante, .toast { scale: var(--anim-ech); }

/* --- Graphiques ----------------------------------------------------------- */

.graphique { width: 100%; display: block; overflow: visible; }
.graphique .grille-fond { stroke: rgba(255, 255, 255, .06); stroke-width: 1; }
.graphique .axe {
  fill: var(--txt-3);
  font-size: 10.5px;
  font-family: inherit;
}
.graphique .barre { transition: opacity calc(.14s * var(--anim-vit)); }
.graphique .barre:hover { opacity: .82; }
.graphique .valeur-barre { fill: var(--txt-2); font-size: 10.5px; font-weight: 600; }

.legende { display: flex; flex-wrap: wrap; gap: var(--e3); font-size: 11.5px; color: var(--txt-2); }
.legende span { display: inline-flex; align-items: center; gap: 6px; }
.legende i { width: 9px; height: 9px; border-radius: 3px; flex: none; }

/* --- Carte Leaflet -------------------------------------------------------- */

.carte-geo {
  height: 480px;
  border-radius: var(--r);
  overflow: hidden;
  border: 1px solid var(--bord);
  /* Leaflet empile ses propres calques jusqu'a 1000 : sans plancher ici, la
     carte passe par-dessus les menus (60), les modales (70) et les toasts
     (90). Un z-index sur le conteneur enferme toute cette pile dedans. */
  position: relative;
  z-index: 0;
  background: var(--surf-2);
}
.marqueur-tournee {
  display: grid; place-items: center;
  width: 26px; height: 26px;
  border-radius: 50%;
  border: 2px solid rgba(0, 0, 0, .55);
  color: #fff;
  font: 700 11px/1 "Segoe UI", sans-serif;
}
/* Les portes du jour : cerclees de jaune, comme leur ligne dans la liste, et
   posees au-dessus des autres pour rester lisibles dans un paquet de points. */
.marqueur-tournee.du-jour {
  border-color: var(--alerte);
  box-shadow: 0 0 0 2px rgba(250, 178, 25, .45);
  z-index: 500;
}
.leaflet-container { background: var(--carte-fond) !important; font-family: inherit !important; }
.leaflet-popup-content-wrapper, .leaflet-popup-tip {
  background: var(--surf-2) !important;
  color: var(--txt) !important;
  border: 1px solid var(--bord-fort);
  border-radius: var(--r) !important;
  box-shadow: var(--ombre-flottante) !important;
}
.leaflet-control-zoom a {
  background: var(--surf-2) !important;
  color: var(--txt) !important;
  border-color: var(--bord) !important;
}
.leaflet-control-attribution {
  background: rgba(0, 0, 0, .5) !important;
  color: var(--txt-3) !important;
  font-size: 9.5px !important;
}

/* --- Semaine d'agenda ----------------------------------------------------- */

/* --- Agenda au mois --------------------------------------------------------

   Sept colonnes fixes et des cases de hauteur egale : c'est l'alignement des
   colonnes qui fait lire un mois d'un coup d'oeil. Une case qui s'allonge
   quand elle se remplit deforme la grille entiere, donc au-dela de trois
   rendez-vous on compte le reste au lieu de l'afficher. */
.mois {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 6px;
}

.mois-entete {
  text-align: center;
  font-size: 10.5px; font-weight: 650; letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--txt-3);
  padding-bottom: var(--e1);
}

.mois-case {
  background: var(--surf-1);
  border: 1px solid var(--bord);
  border-radius: var(--r-s);
  padding: 6px;
  min-height: 96px;
  display: flex; flex-direction: column; gap: 4px;
  cursor: pointer;
  transition: background calc(.12s * var(--anim-vit)), border-color calc(.12s * var(--anim-vit));
}
.mois-case:hover { background: var(--surf-2); border-color: var(--bord-fort); }

/* Les jours des mois voisins restent lisibles mais s'effacent : les masquer
   ferait des trous dans la grille, les afficher pleins les ferait confondre
   avec le mois qu'on regarde. */
.mois-case.dehors { opacity: .38; }

.mois-case.aujourdhui { border-color: var(--acc-bord); background: rgba(255, 121, 0, .06); }
.mois-case.choisi { border-color: var(--acc); box-shadow: 0 0 0 1px var(--acc) inset; }

.mois-num {
  font-size: 12.5px; font-weight: 650;
  color: var(--txt-2);
  font-variant-numeric: tabular-nums;
}
.mois-case.aujourdhui .mois-num { color: var(--acc-vif); }

.mois-reste { font-size: 10.5px; color: var(--txt-3); padding-left: 2px; }

.pastille-rdv.mini {
  padding: 3px 5px;
  font-size: 10.5px;
  border-radius: 6px;
  display: flex; align-items: baseline; gap: 4px;
}
.pastille-rdv.mini b { flex: none; }
/* Un rendez-vous perso s'efface dans la grille : il est lÃ  pour ne pas etre
   oublie, pas pour occuper l'oeil au milieu du travail. */
.pastille-rdv.perso { background: transparent; border-style: dashed; }
.pastille-rdv.perso .qui { color: var(--txt-3); }

/* Case a cocher compacte, pour tenir dans une rangee de boutons mini. */
label.case.mini { padding: 3px 8px; font-size: 11.5px; gap: 6px; }
label.case.mini .boite { width: 14px; height: 14px; }
.pastille-rdv.mini .qui { display: inline; }

@media (max-width: 700px) {
  .mois-case { min-height: 64px; padding: 4px; }
  .mois-case .pastille-rdv.mini .qui { display: none; }
}

.semaine { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--e2); }
.jour-col {
  background: var(--surf-1);
  border: 1px solid var(--bord);
  border-radius: var(--r);
  padding: var(--e2);
  min-height: 150px;
  display: flex; flex-direction: column; gap: 6px;
}
.jour-col.aujourdhui { border-color: var(--acc-bord); background: rgba(255, 121, 0, .05); }
.jour-col .jour-titre {
  text-align: center; font-size: 11.5px; font-weight: 650;
  color: var(--txt-2); padding-bottom: 5px;
  border-bottom: 1px solid var(--bord);
}
.jour-col.aujourdhui .jour-titre { color: var(--acc-vif); }
.jour-col .jour-titre b { display: block; font-size: 16px; color: var(--txt); letter-spacing: -.02em; }
.jour-col.aujourdhui .jour-titre b { color: var(--acc-vif); }

.pastille-rdv {
  background: var(--surf-2);
  border: 1px solid var(--bord);
  border-left: 3px solid var(--info);
  border-radius: 8px;
  padding: 5px 7px;
  font-size: 11.5px;
  cursor: pointer;
  transition: background calc(.12s * var(--anim-vit)), transform calc(.12s * var(--anim-vit));
}
.pastille-rdv:hover { background: var(--surf-3); transform: translateX(1px); }
.pastille-rdv b { font-variant-numeric: tabular-nums; }
.pastille-rdv .qui { color: var(--txt-2); display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* --- Divers --------------------------------------------------------------- */

.muet { color: var(--txt-3); }
.petit { font-size: 12px; }
.mono { font-family: "Cascadia Code", Consolas, monospace; font-size: 12px; }
.gras { font-weight: 650; }
.rangee { display: flex; align-items: center; gap: var(--e2); flex-wrap: wrap; }
.colonne { display: flex; flex-direction: column; gap: var(--e3); }
.espace { flex: 1; }
.sep { height: 1px; background: var(--bord); border: 0; margin: var(--e4) 0; }
.tronque { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.defile { overflow-y: auto; }

.chargement {
  width: 16px; height: 16px;
  border: 2px solid var(--bord-fort);
  border-top-color: var(--acc);
  border-radius: 50%;
  animation: tourne calc(.7s * var(--anim-vit)) linear infinite;
}
@keyframes tourne { to { transform: rotate(360deg); } }

.voile-chargement {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  background: rgba(8, 8, 10, .6);
  z-index: 20;
}

/* --- Adaptations ---------------------------------------------------------- */

@media (max-width: 980px) {
  :root { --lateral: 0px; }
  .corps { grid-template-columns: 1fr; }
  .lateral {
    position: fixed; z-index: 80;
    top: var(--titre-h); bottom: 0; left: 0; width: 260px;
    transform: translateX(-100%);
    transition: transform calc(.22s * var(--anim-vit)) var(--ressort);
  }
  .lateral.ouvert { transform: none; box-shadow: var(--ombre-flottante); }
  .page { padding: var(--e4) var(--e4) var(--e7); }
  .g2, .g-1-1 { grid-template-columns: minmax(0, 1fr); }
  .semaine { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .resultats { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 981px) { .bascule-menu { display: none; } }

/* --- Liste + fiche ---------------------------------------------------------

   Le gabarit de quatre ecrans : la liste a gauche, ce qu'on a ouvert a droite.
   Les largeurs passent par une variable plutot que par un style en ligne, sinon
   la version telephone ne pourrait pas les empiler — un style en ligne bat
   toujours une feuille de style. */

.maitre-detail {
  display: grid;
  grid-template-columns: minmax(0, var(--colonne-liste, 380px)) minmax(0, 1fr);
  gap: var(--e4);
  align-items: start;
}
/* La liste passe a droite. Sur la tournee c'est la porte en cours qu'on lit
   d'abord ; la liste sert a sauter d'une adresse a l'autre, elle vient apres.
   Le reste du gabarit — volet collant, hauteur calculee — ne bouge pas. */
.maitre-detail.inverse {
  grid-template-columns: minmax(0, 1fr) minmax(0, var(--colonne-liste, 380px));
}

.maitre-detail .volet-liste { position: sticky; top: 0; }
.maitre-detail .liste.defile { max-height: calc(100vh - var(--haut-liste, 300px)); }
.transcription.defile { max-height: calc(100vh - var(--haut-liste, 320px)); }

/* La conversation dans la fiche client. Plusieurs appels s'y empilent, chacun
   avec la sienne : sans encart, une fiche a cinq appels se deroule sur dix
   ecrans et l'historique en dessous devient inatteignable. */
.transcription.encart {
  margin-top: var(--e3);
  max-height: 300px;
  overflow-y: auto;
}

/* --- L'alerte qui demande une reaction --------------------------------------

   Un depart annonce n'est pas une information de plus dans une liste : c'est
   quelque chose a faire aujourd'hui. Une etiquette grise de la meme taille que
   les autres se lit au meme rythme que les autres, c'est-a-dire pas du tout.

   D'ou la cloche, qui sonne brievement puis se tait — trois secousses toutes
   les six secondes, pas un clignotement continu qu'on finit par masquer
   mentalement. Le halo ne bat que pour les departs confirmes : si tout pulse,
   plus rien ne ressort. Les bulles d'une meme liste sont decalees les unes des
   autres, sinon la page entiere tressaute d'un bloc. */

.alerte-bulle {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 11px 4px 8px;
  border-radius: 999px;
  font-size: 11.5px; font-weight: 650; white-space: nowrap;
  color: var(--a);
  background: color-mix(in srgb, var(--a) 15%, transparent);
  border: 1px solid color-mix(in srgb, var(--a) 45%, transparent);
}
.alerte-bulle .cloche {
  /* inline-block, et pas seulement pour la forme : `transform` n'a aucun effet
     sur une boite inline, la cloche restait droite sans que rien ne le dise. */
  display: inline-block;
  font-style: normal; font-size: 12.5px; line-height: 1;
  transform-origin: 50% 15%;              /* la cloche pivote par son attache */
  /* Trois secondes, pas six. Une sonnerie d'une seconde et demie toutes les six
     secondes, on la rate : on regarde l'ecran trois secondes, il ne se passe
     rien, et on conclut que rien ne bouge. A ce rythme-la elle se voit sans
     s'imposer. */
  animation: sonnerie calc(3s * var(--anim-vit)) var(--retard, calc(0s * var(--anim-vit))) infinite;
}
/* L'onde qui s'ecarte de la bulle. Elle passe par un pseudo-element plutot que
   par une `box-shadow` animee : une couleur construite avec `color-mix()` a
   l'interieur de keyframes n'est pas interpolable, et le navigateur abandonne
   alors l'animation sans rien dire. Un anneau qu'on agrandit en le faisant
   disparaitre s'interpole toujours. */
.alerte-bulle.urgente { position: relative; }
.alerte-bulle.urgente::after {
  content: "";
  position: absolute; inset: -1px;
  border-radius: inherit;
  border: 1px solid var(--a);
  pointer-events: none;
  animation: halo calc(2.6s * var(--anim-vit)) var(--retard, calc(0s * var(--anim-vit))) infinite;
}

/* La cloche part franc puis s'amortit, comme un battant qu'on lache. Les
   premieres secousses sont larges — en dessous d'une quinzaine de degres, sur
   un glyphe de douze pixels, le mouvement ne se lit pas. */
@keyframes sonnerie {
  0%, 62%, 100% { transform: rotate(0); }
  66% { transform: rotate(-22deg); }
  71% { transform: rotate(18deg); }
  76% { transform: rotate(-13deg); }
  81% { transform: rotate(9deg); }
  86% { transform: rotate(-5deg); }
  91% { transform: rotate(2deg); }
}

@keyframes halo {
  0%   { transform: scale(1);    opacity: .65; }
  70%  { transform: scale(1.22); opacity: 0; }
  100% { transform: scale(1.22); opacity: 0; }
}

/* Une carte qui porte une alerte non traitee s'annonce des le bord : un filet
   vertical, comme les etapes de tournee. C'est la meme grammaire. */
.carte.alerte-vive {
  position: relative;
  border-color: color-mix(in srgb, var(--a) 40%, transparent);
}
.carte.alerte-vive::before {
  content: "";
  position: absolute; inset: 0 auto 0 0; width: 3px;
  border-radius: 3px 0 0 3px;
  background: var(--a);
}

@media (prefers-reduced-motion: reduce) {
  .alerte-bulle .cloche, .alerte-bulle.urgente::after { animation: none; }
  .alerte-bulle.urgente::after { opacity: .5; }
}

/* Revenir sur un pointage, depuis la liste de la tournee. Le bouton s'efface
   tant qu'on ne le cherche pas : il y a trois cents lignes, et une flechette
   pleine sur chacune ferait une colonne de bruit. Il reste atteignable au
   clavier, et toujours visible au doigt — sur un telephone il n'y a pas de
   survol pour le reveler. */
.rendre-a-visiter { opacity: 0; transition: opacity calc(.14s * var(--anim-vit)); }
.rang:hover .rendre-a-visiter,
.rendre-a-visiter:focus-visible { opacity: 1; }

@media (hover: none) {
  .rendre-a-visiter { opacity: .55; }
}

/* --- La journee de terrain, sur le tableau de bord --------------------------

   Trois choses se decident le matin, debout, avant de partir : le temps qu'il
   fera, la route a couvrir, et l'ordre des portes. Elles tenaient dans un tiers
   de largeur avec le prochain client pour seule information ; elles ont
   maintenant la ligne entiere. */

.meteo {
  border: 1px solid var(--bord);
  border-radius: var(--r);
  background: var(--surf-1);
  padding: var(--e3);
}

/* Une colonne par heure, de dix a dix-huit. Elles se serrent plutot que de
   defiler : neuf colonnes tiennent, et une bande qui defile ne se lit pas d'un
   coup d'oeil — c'est pourtant tout ce qu'on lui demande. */
.meteo .heures {
  display: grid;
  grid-template-columns: repeat(9, minmax(0, 1fr));
  gap: var(--e1);
  margin-top: var(--e3);
}
.meteo .heure {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 8px 2px;
  border-radius: var(--r-s);
  background: var(--surf-2);
  transition: background calc(.14s * var(--anim-vit)), transform calc(.14s * var(--anim-vit)) var(--ressort);
}
.meteo .heure:hover { background: var(--surf-3); transform: translateY(-2px); }
/* L'heure ou il faudra de quoi se couvrir se repere sans lire les chiffres :
   c'est la seule qui change de couleur. */
.meteo .heure.mouillee {
  background: color-mix(in srgb, var(--info) 16%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--info) 35%, transparent);
}
.meteo .quand { font-size: 10.5px; color: var(--txt-3); font-variant-numeric: tabular-nums; }
.meteo .ciel { font-size: 16px; line-height: 1.1; }
.meteo .deg { font-size: 13px; font-weight: 650; font-variant-numeric: tabular-nums; }
.meteo .goutte { font-size: 10px; color: var(--info); font-variant-numeric: tabular-nums; }
.meteo .goutte.sec { color: var(--txt-3); }

/* Les portes du jour. Denses : quinze lignes doivent tenir sans qu'on ait a
   defiler, sinon autant ouvrir la tournee. */
.portes { display: flex; flex-direction: column; margin-top: var(--e3); }
.portes > * + * { border-top: 1px solid var(--bord); }
.porte {
  display: flex; align-items: center; gap: var(--e3);
  padding: 8px var(--e2);
  cursor: pointer;
  transition: background calc(.12s * var(--anim-vit)), transform calc(.14s * var(--anim-vit)) var(--ressort);
}
.porte:hover { background: var(--surf-2); transform: translateX(3px); }
.porte .principal-txt { min-width: 0; flex: 1; }
.porte .nom { font-weight: 600; font-size: 13px; }
.porte .meta { font-size: 11.5px; color: var(--txt-3); }
.porte .fin { margin-left: auto; display: flex; align-items: center; gap: var(--e2); flex: none; }

/* Le numero de passage, pas l'identifiant de la fiche : c'est l'ordre dans
   lequel on sonne. */
.porte .numero-porte {
  flex: none;
  width: 24px; height: 24px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums;
  color: var(--txt-2);
  background: var(--surf-2);
  border: 1px solid var(--bord);
}
/* Celle qui vient : c'est la seule qu'on cherche vraiment dans la liste. */
.porte.prochaine { background: var(--acc-sourd); }
.porte.prochaine .numero-porte {
  background: var(--acc); color: var(--sur-acc); border-color: transparent;
}
.porte.faite { opacity: .5; }
.porte.faite .nom { text-decoration: line-through; text-decoration-thickness: 1px; }
.porte.faite .numero-porte { color: var(--bon-txt); }

@media (max-width: 980px) {
  /* Sur un telephone, neuf colonnes d'heures deviennent illisibles : on passe
     a une bande qui defile du pouce. */
  .meteo .heures {
    grid-template-columns: repeat(9, 58px);
    overflow-x: auto;
    padding-bottom: 4px;
  }
  .porte { padding: 11px var(--e2); }
}

/* ===========================================================================
   Ce qui rend l'ecran vivant
   ===========================================================================

   Une interface figee donne l'impression de ne pas repondre, meme quand elle
   repond. Trois regles tiennent cette section, et elles disent surtout ce
   qu'on ne fait pas :

   1. Le mouvement sert a expliquer, pas a decorer. Un bloc qui arrive monte de
      quelques pixels — il vient d'apparaitre ; une ligne survolee se decale —
      elle est sous le curseur. Rien ne bouge sans raison.
   2. Court. Entre 120 et 320 ms : au-dela on attend l'interface, et sur le
      terrain on n'attend pas.
   3. Jamais sur ce qu'on manipule en masse. Les deux mille rangs de la liste
      clients n'ont pas d'animation d'entree : ils se posent d'un coup.

   Tout est desactivable d'un bloc, en bas de cette section. */

/* L'ecran qui arrive. Une seule animation sur le conteneur, plus une cascade
   tres courte sur ses premiers blocs : la page se pose au lieu d'apparaitre
   d'un coup, et l'oeil sait par ou commencer. */
@keyframes entree-bloc {
  from { opacity: 0; transform: translateY(calc(7px * var(--anim-amp))); }
  to   { opacity: 1; transform: none; }
}

/* « backwards » et non « both » : il faut tenir l'etat de depart pendant le
   retard de la cascade, mais surtout pas l'etat d'arrivee apres. Une animation
   qui se fige en avant garde la main sur la propriete et neutralise tout ce
   qui viendrait ensuite — c'est ce qui empechait les tuiles de se soulever au
   survol : leur translation d'entree, figee a « none », gagnait sur le :hover. */
/* La cascade ne joue qu'a l'ARRIVEE sur un ecran, d'ou la classe « entrant »
   posee par rendre() dans ce seul cas.

   Sans elle, elle rejouait a chaque rendu — donc a chaque clic, puisque tout
   passe par un rendu complet. Avec « backwards », chaque bloc tient son etat
   de depart pendant son retard : opacite zero. Quatre blocs, seize centiemes
   de retard et trois dixiemes d'animation faisaient une demi-seconde de page
   vide a chaque fois qu'on ouvrait une conversation. */
#vue.entrant .page > * {
  animation: entree-bloc calc(.3s * var(--anim-vit)) var(--ressort) backwards;
}
#vue.entrant .page > *:nth-child(2) { animation-delay: calc(.045s * var(--anim-vit)); }
#vue.entrant .page > *:nth-child(3) { animation-delay: calc(.09s * var(--anim-vit)); }
#vue.entrant .page > *:nth-child(4) { animation-delay: calc(.13s * var(--anim-vit)); }
#vue.entrant .page > *:nth-child(n+5) { animation-delay: calc(.16s * var(--anim-vit)); }

/* Les tuiles d'indicateur arrivent l'une apres l'autre : c'est la premiere
   chose qu'on lit d'un tableau de bord, autant qu'elles se comptent d'elles
   memes. Cinq au maximum — au-dela la cascade se voit comme un retard. */
#vue.entrant .grille > .tuile { animation: entree-bloc calc(.34s * var(--anim-vit)) var(--ressort) backwards; }
#vue.entrant .grille > .tuile:nth-child(2) { animation-delay: calc(.05s * var(--anim-vit)); }
#vue.entrant .grille > .tuile:nth-child(3) { animation-delay: calc(.1s * var(--anim-vit)); }
#vue.entrant .grille > .tuile:nth-child(4) { animation-delay: calc(.15s * var(--anim-vit)); }
.grille > .tuile:nth-child(n+5) { animation-delay: calc(.2s * var(--anim-vit)); }

/* Une tuile repond au survol : elle se souleve et son chiffre respire. Sans
   cela, la moitie haute de chaque ecran est une image morte. */
.tuile {
  transition: transform calc(.18s * var(--anim-vit)) var(--ressort), border-color calc(.18s * var(--anim-vit)), background calc(.18s * var(--anim-vit));
}
.tuile:hover { transform: translateY(-2px); border-color: var(--bord-fort); }
.tuile .valeur { transition: transform calc(.18s * var(--anim-vit)) var(--ressort); }
.tuile:hover .valeur { transform: scale(1.03); }

/* Une ligne de liste glisse legerement sous le curseur. Trois pixels suffisent
   — c'est le mouvement qu'on percoit, pas sa distance. */
.rang { transition: background calc(.12s * var(--anim-vit)), transform calc(.14s * var(--anim-vit)) var(--ressort); }
.rang:hover { transform: translateX(3px); }
.rang.selectionne:hover { transform: none; }   /* deja designee, elle ne bouge plus */

/* Les pastilles et etiquettes s'eclairent au passage, comme le reste. */
.puce, .etiq { transition: border-color calc(.15s * var(--anim-vit)), color calc(.15s * var(--anim-vit)), background calc(.15s * var(--anim-vit)); }

/* Le chiffre d'une progression se remplit depuis zero a l'apparition : on voit
   la barre se poser, donc on comprend qu'elle mesure quelque chose. */
@keyframes remplissage { from { transform: scaleX(0); } to { transform: scaleX(1); } }
/* Pas les barres fines : celle des travaux en cours est repeinte toutes les
   deux secondes et demie, et l'animation repartirait de zero a chaque fois —
   une barre qui recule pendant qu'elle avance. */
.progression:not(.fine) > i {
  transform-origin: left center;
  animation: remplissage calc(.55s * var(--anim-vit)) var(--ressort) both;
}

/* --- Suivi : de la vente au raccordement -----------------------------------

   Trois jalons relies par un trait, et la somme posee au bout. Le trait n'est
   pas une jauge de pourcentage : il n'y a rien de continu entre « vendue » et
   « raccordee ». Il dit jusqu'ou on est alle, et il s'arrete la ou l'argent
   est ecrit — c'est ce qui rend evident que le montant depend du dernier
   jalon atteint.

   Trois couleurs tiennent tout l'ecran, et une seule fois chacune : vert ce
   qui est tombe, orange ce qui est promis, jaune ce qui traine. Les tuiles,
   la barre du mois, les jetons d'etat et les sommes les repetent — on lit la
   page d'un coup d'oeil avant d'en lire les mots. */

/* --- Ou en est le mois -----------------------------------------------------

   Les deux tuiles disent combien ; elles ne disent pas la proportion. Une
   barre, elle, repond sans qu'on ait a diviser de tete : voila la part du
   mois qui est deja tombee, voila celle qui attend encore un branchement.
   La part acquise est pleine, la part promise est hachuree — ce qui n'est
   pas encore la ne merite pas un aplat. */

.bilan-mois h2 { font-size: 14.5px; }
.bilan-mois .part {
  font-size: 19px; font-weight: 700; letter-spacing: -.02em;
  color: var(--bon-txt);
  font-variant-numeric: tabular-nums;
}
.bilan-mois .balance {
  height: 10px;
  margin: var(--e3) 0 var(--e2);
  border-radius: var(--r-p);
  overflow: hidden;
  background-color: var(--surf-3);
  background-image: repeating-linear-gradient(-45deg,
      var(--acc-sourd) 0 6px, transparent 6px 12px);
}
.bilan-mois .balance > i {
  display: block; height: 100%;
  width: var(--part);
  border-radius: var(--r-p);
  background: linear-gradient(90deg,
      color-mix(in srgb, var(--bon) 55%, transparent), var(--bon));
  /* Elle se pose depuis zero : on voit la part se remplir, donc on comprend
     qu'elle mesure quelque chose. Un peu de retard sur l'arrivee du bloc,
     pour que la carte soit deja la quand la barre part. */
  animation: balance-pose calc(.65s * var(--anim-vit)) var(--ressort) calc(.18s * var(--anim-vit)) backwards;
}
@keyframes balance-pose { from { width: 0; } }

/* La hachure derive doucement tant qu'on reste dessus, et seulement la : ce
   qui attend a l'air d'attendre au lieu d'avoir l'air fige. Au repos elle ne
   bouge pas — une trame qui defile toute seule est un clignotant. */
.bilan-mois:hover .balance { animation: hachure-glisse calc(1.9s * var(--anim-vit)) linear infinite; }
@keyframes hachure-glisse { to { background-position: 17px 0; } }

.bilan-mois .legende { margin-top: 6px; gap: var(--e4); }
.jalon-legende { display: inline-flex; align-items: center; gap: 6px; color: var(--txt-2); }
.jalon-legende::before {
  content: ""; width: 8px; height: 8px; border-radius: 50%;
}
.jalon-legende.bon::before { background: var(--bon); }
.jalon-legende.attente::before { background: var(--acc); }
/* Ce compte-la n'apparait que s'il y a de quoi : une legende qui affiche
   « 0 a traiter » tous les jours apprend a l'oeil a ne plus la lire. */
.jalon-legende.agir { color: var(--alerte-txt); }
.jalon-legende.agir::before { background: var(--alerte); }

/* --- Le jeton d'etat d'une vente -------------------------------------------

   Un mot au bout de la premiere ligne, et il repond a la seule question qu'on
   se pose en descendant la liste : celle-ci, j'en fais quoi ? Le retard porte
   son nombre de jours — « en retard » sans quantite ne dit pas s'il faut s'en
   occuper aujourd'hui. */

.jeton-etat {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px;
  border-radius: var(--r-p);
  border: 1px solid var(--bord);
  background: var(--surf-3);
  color: var(--txt-2);
  font-size: 11.5px; font-weight: 600; line-height: 1.6;
  white-space: nowrap;
  transition: background calc(.18s * var(--anim-vit)), border-color calc(.18s * var(--anim-vit)), color calc(.18s * var(--anim-vit));
}
.jeton-etat.acquise {
  color: var(--bon-txt);
  background: color-mix(in srgb, var(--bon) 13%, transparent);
  border-color: color-mix(in srgb, var(--bon) 32%, transparent);
}
.jeton-etat.attente {
  color: var(--acc-vif);
  background: var(--acc-sourd);
  border-color: var(--acc-bord);
}
.jeton-etat.retard {
  color: var(--alerte-txt);
  background: color-mix(in srgb, var(--alerte) 14%, transparent);
  border-color: color-mix(in srgb, var(--alerte) 38%, transparent);
}
/* Rien n'est promis ni en retard : il manque juste une date, et c'est un
   trou dans le dossier plutot qu'une alerte. Le trait discontinu le dit sans
   couleur. */
.jeton-etat.a-dater {
  background: transparent; color: var(--txt-3);
  border-style: dashed;
}
/* La cloche ne sonne que sur les trois premiers retards — au-dela le
   battement cesse d'etre un signal. Le retard decale evite que trois jetons
   sonnent dans la meme seconde. */
.jeton-etat .cloche {
  display: inline-block;
  transform-origin: 50% 15%;
  animation: sonnerie calc(4.5s * var(--anim-vit)) var(--retard, calc(0s * var(--anim-vit))) infinite;
}

/* Les tuiles du mois repondent comme les cartes : rien de ce qui est sous le
   curseur ne doit rester inerte. Plus discretes qu'une vente — elles ne
   s'ouvrent pas, elles comptent — mais pas mortes pour autant. */
[data-bloc="s-compteurs"] .tuile {
  transition: transform calc(.45s * var(--anim-vit)) var(--ressort), box-shadow calc(.45s * var(--anim-vit)) var(--ressort),
              border-color calc(.3s * var(--anim-vit));
}
[data-bloc="s-compteurs"] .tuile:hover {
  /* Elle garde les deux pixels de levee de toutes les tuiles, et grandit en
     plus : c'est le meme geste que les cartes en dessous. */
  transform: translateY(-3px) scale(1.04);
  box-shadow: 0 12px 26px -14px rgba(0, 0, 0, .85);
}
[data-bloc="s-compteurs"] .tuile .valeur { transition: transform calc(.45s * var(--anim-vit)) var(--ressort); }
[data-bloc="s-compteurs"] .tuile:hover .valeur { transform: scale(1.1); }

/* --- La carte d'une vente -------------------------------------------------- */

.suivi-vente {
  margin-bottom: var(--e3);
  position: relative;
  transition: border-color calc(.3s * var(--anim-vit)), background-color calc(.3s * var(--anim-vit)),
              transform calc(.45s * var(--anim-vit)) var(--ressort), box-shadow calc(.45s * var(--anim-vit)) var(--ressort);
}
/* Un fond a peine repris sous le curseur. La formule vaut dans les deux
   themes : elle eclaircit sur fond noir, elle assombrit sur fond blanc. */
.suivi-vente:hover { background-color: color-mix(in srgb, var(--txt) 4%, var(--surf-1)); }

/* Et la carte se souleve, franchement : trois pour cent. Un pour cent et demi
   se voyait au comparatif, cote a cote, et disparaissait a l'usage — on
   regarde une carte a la fois, sans rien pour la mesurer. La colonne entiere
   doit se resserrer derriere celle qu'on vise.

   Trois pour cent et pas davantage : sur les 1 496 px d'une page au maximum
   de sa largeur, la carte deborde deja de 22 px de chaque cote, et le
   rembourrage de la page en fait 32. Au-dela, elle toucherait le bord de la
   fenetre.

   Le ressort du theme donne le geste : elle monte un peu au-dela puis se
   pose, la ou une transition lineaire l'aurait fait grossir comme on gonfle
   un ballon. Elle passe devant ses voisines le temps du survol, sinon la
   carte d'en dessous mordrait sur son ombre.

   Ces regles ne sont enfermees dans aucune requete media. « hover: hover »
   semblait la protection evidente contre le tactile, mais elle decrit le
   pointeur PRINCIPAL : sur un poste dont l'ecran est tactile — ou dans la
   fenetre WebView2 du bureau — Windows peut declarer le doigt principal, la
   requete devient fausse alors qu'une souris est branchee, et le survol
   n'agrandit plus rien sans que rien ne l'explique. Le telephone est ecarte
   plus bas par « any-hover: none », qui demande qu'AUCUN pointeur ne sache
   survoler. */
.suivi-vente:hover {
  transform: scale(1.03);
  box-shadow: 0 14px 34px -14px rgba(0, 0, 0, .85);
  z-index: 2;
}

/* Et tout ce qu'elle porte respire avec elle.
   ---------------------------------------------------------------------
   La carte grandit de trois et demi : ses elements suivraient de toute facon,
   mais de trois et demi eux aussi, ce qui ne se distingue pas du reste.
   Chacun prend donc nettement plus, et pas la meme chose — le nom moins que
   le jeton d'etat, qui porte la reponse qu'on cherche, moins que les
   pastilles de la barre, qui sont ce qu'on vient lire.

   Tout part au meme instant. Un decalage entre le nom, le jeton et les
   pastilles faisait joli sur le papier, mais a l'usage la carte semblait
   mettre du temps a repondre : on survole, et une partie de ce qu'on
   regarde bouge apres coup. Ce qui est sous le curseur repond tout de
   suite, et c'est la duree — pas le retard — qui donne la douceur.

   L'ancrage compte autant que l'echelle : ce qui est cale a gauche grandit
   vers la droite, ce qui est cale a droite vers la gauche. Sans quoi le nom
   du client sortirait de la carte par la gauche. */
.suivi-vente [data-client],
.suivi-vente .etiq,
.suivi-vente .jeton-etat,
.suivi-vente .rangee.petit span,
.suivi-vente .somme,
.suivi-vente .bout .petit,
.suivi-vente .rangee .btn.mini {
  transition: transform calc(.45s * var(--anim-vit)) var(--ressort), color calc(.3s * var(--anim-vit)), background-color calc(.3s * var(--anim-vit));
}
.suivi-vente [data-client] { transform-origin: left center; }
.suivi-vente .etiq,
.suivi-vente .rangee.petit span { transform-origin: left center; }
.suivi-vente .jeton-etat,
.suivi-vente .somme,
.suivi-vente .bout .petit { transform-origin: right center; }

.suivi-vente:hover [data-client] { transform: scale(1.1); }
.suivi-vente:hover .etiq { transform: scale(1.09); }
.suivi-vente:hover .rangee.petit span { transform: scale(1.07); }
.suivi-vente:hover .jeton-etat { transform: scale(1.13); }
.suivi-vente:hover .jalon .pastille { transform: scale(calc(1 + .2 * var(--anim-amp))); }
.suivi-vente:hover .bulle-evt { transform: translateX(-50%) scale(1.22); }
.suivi-vente:hover .somme { transform: scale(1.15); }
.suivi-vente:hover .bout .petit { transform: scale(1.07); }
.suivi-vente:hover .rangee .btn.mini { transform: scale(1.06); }

/* Sur un appareil ou RIEN ne sait survoler — un telephone, pas un poste dont
   l'ecran est tactile — le survol ne se retire jamais : la carte touchee
   resterait grande jusqu'au prochain tap. « any-hover: none » est la seule
   requete qui distingue vraiment les deux cas ; « hover: none » et
   « pointer: coarse » sont vrais des que Windows declare le doigt pointeur
   principal, souris branchee ou non, et emportaient l'effet avec eux. */
@media (any-hover: none) {
  .suivi-vente:hover,
  .suivi-vente:hover [data-client],
  .suivi-vente:hover .etiq,
  .suivi-vente:hover .rangee.petit span,
  .suivi-vente:hover .jeton-etat,
  .suivi-vente:hover .jalon .pastille,
  .suivi-vente:hover .somme,
  .suivi-vente:hover .bout .petit,
  .suivi-vente:hover .rangee .btn.mini { transform: none; box-shadow: none; }
  .suivi-vente:hover .bulle-evt { transform: translateX(-50%); }
  [data-bloc="s-compteurs"] .tuile:hover {
    transform: translateY(-2px); box-shadow: none;
  }
  [data-bloc="s-compteurs"] .tuile:hover .valeur { transform: scale(1.03); }
}

/* L'etat teinte le haut de la carte au lieu de se contenter du liser :
   dans une colonne de vingt ventes, la couleur se voit de loin, la bordure
   demande de s'approcher. */
.suivi-vente.confirmee {
  border-color: color-mix(in srgb, var(--bon) 40%, var(--bord));
  background-image: linear-gradient(180deg,
      color-mix(in srgb, var(--bon) 8%, transparent), transparent 96px);
}
/* La date est passee et personne n'a pointe, ou bien un evenement reste
   ouvert sur la barre : dans les deux cas quelqu'un attend un geste, et c'est
   la meme couleur. Jamais sur une vente branchee — elle est finie, l'argent
   est tombe, et une note restee ouverte ne doit pas la faire crier. */
.suivi-vente.en-retard,
.suivi-vente.a-traiter:not(.confirmee) {
  border-color: color-mix(in srgb, var(--alerte) 55%, var(--bord));
  background-image: linear-gradient(180deg,
      color-mix(in srgb, var(--alerte) 9%, transparent), transparent 96px);
}

/* Les cartes arrivent en cascade, et du cote d'ou l'on vient quand on tourne
   le mois : le geste se voit au lieu de se deviner. La cascade est bornee a
   dix rangs par le rendu — au-dela elle se lirait comme un retard. */
@keyframes entree-vente {
  from { opacity: 0; transform: translate(calc(var(--sens, 0) * 20px * var(--anim-amp)), calc(8px * var(--anim-amp))); }
  to   { opacity: 1; transform: none; }
}
.suivi-vente {
  animation: entree-vente calc(.34s * var(--anim-vit)) var(--ressort) backwards;
  animation-delay: calc(var(--i, 0) * calc(.04s * var(--anim-vit)));
}

.parcours {
  display: flex; align-items: center; gap: var(--e4);
  margin-top: var(--e2);
  /* Le haut est plus haut que le bas : c'est la rangee des evenements qui
     s'y loge, au-dessus du trait. Elle est reservee meme quand la vente n'a
     rien a montrer — sinon le « + » qui parait au survol pousserait la carte
     entiere vers le bas au moment ou l'on vise un bouton. */
  padding: 30px 0 var(--e2);
}
.parcours .jalons {
  flex: 1; min-width: 0;
  display: flex; align-items: flex-start;
  position: relative;
}
/* Le trait passe derriere les pastilles, d'un centre a l'autre : il part donc
   d'un sixieme et s'arrete a cinq sixiemes, et non de bord a bord. */
.parcours .jalons::before,
.parcours .jalons::after {
  content: "";
  position: absolute; top: 10px; height: 2px;
  left: 16.66%; right: 16.66%;
  border-radius: 2px;
}
.parcours .jalons::before { background: var(--bord); }
/* La part parcourue : un jalon sur trois n'avance de rien, deux en font la
   moitie, trois la remplissent. */
.parcours .jalons::after {
  background: var(--acc);
  right: calc(16.66% + (3 - var(--atteints)) * 33.33%);
  transition: right calc(.3s * var(--anim-vit)) var(--ressort);
  /* Elle se pose depuis la gauche a l'arrivee de la carte : la barre raconte
     le chemin au lieu d'etre deja dessinee. 116,65 %, c'est la position de
     zero jalon atteint — largeur nulle. */
  animation: barre-pose calc(.5s * var(--anim-vit)) var(--ressort) calc(calc(.12s * var(--anim-vit)) + var(--i, 0) * calc(.04s * var(--anim-vit))) backwards;
}
@keyframes barre-pose { from { right: 116.65%; } }
.suivi-vente.confirmee .parcours .jalons::after { background: var(--bon); }

.parcours .jalon {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  position: relative; z-index: 1;
  text-align: center;
}
.parcours .jalon .pastille {
  width: 22px; height: 22px; border-radius: 50%;
  border: 2px solid var(--bord);
  background: var(--surf-1);
  display: grid; place-items: center;
  transition: background calc(.18s * var(--anim-vit)), border-color calc(.18s * var(--anim-vit));
}
.parcours .jalon .pastille svg { width: 12px; height: 12px; color: var(--sur-acc); }
.parcours .jalon.fait .pastille { background: var(--acc); border-color: var(--acc); }
.suivi-vente.confirmee .parcours .jalon.fait .pastille {
  background: var(--bon); border-color: var(--bon);
}
/* Les jalons atteints se cochent l'un apres l'autre, dans l'ordre ou ils ont
   eu lieu, juste derriere la barre qui les relie. C'est la meme phrase que la
   barre, dite point par point. */
@keyframes jalon-pose { from { transform: scale(calc(1 - .65 * var(--anim-amp))); opacity: 0; } }
.parcours .jalon.fait .pastille {
  animation: jalon-pose calc(.36s * var(--anim-vit)) var(--ressort) backwards;
  animation-delay: calc(calc(.18s * var(--anim-vit)) + var(--i, 0) * calc(.04s * var(--anim-vit)) + var(--j, 0) * calc(.09s * var(--anim-vit)));
}
.parcours .jalon .nom { font-size: 11.5px; color: var(--txt-3); }
.parcours .jalon.fait .nom { color: var(--txt-2); font-weight: 600; }

/* La date sous le jalon. C'est la place naturelle de ces trois dates —
   vendue tel jour, prevue tel jour, branchee tel jour — et elles y disent en
   plus quelque chose qu'aucune ne disait seule : le temps qu'il a fallu, et
   si le branchement a glisse par rapport au rendez-vous. */
.parcours .jalon .quand {
  margin-top: -3px;
  font-size: 10.5px; line-height: 1.3;
  color: var(--txt-3);
  font-variant-numeric: tabular-nums;
  transition: color calc(.18s * var(--anim-vit));
}
.parcours .jalon.fait .quand { color: var(--txt-2); }

/* --- Ce qui se pose sur la barre -------------------------------------------

   Entre deux jalons il se passe des choses, et c'est la qu'elles se posent.
   Chaque segment porte ses pastilles et, au bout de la file, le « + » qui en
   ajoute une. Le placement horizontal est calcule au rendu — voir
   VUES.suivi.segments() — parce que lui seul sait combien il y en a ; la
   feuille de style ne fait que les dessiner et les animer.

   Le « + » ne se montre qu'au survol du parcours. C'est un outil, pas une
   information : affiche en permanence sur vingt ventes il ferait une rangee
   de boutons au milieu de l'ecran, et la barre cesserait de se lire. */

/* Elles ne sont PAS sur le trait. Posees a la hauteur des jalons et de la
   meme taille qu'eux, elles se lisaient comme un quatrieme, un cinquieme
   jalon : trois points valent une etape, les autres racontent ce qui s'est
   passe entre deux. On les monte donc au-dessus de la ligne, plus petites, et
   une tige les y rattache — la ou elles pendent, il s'est passe quelque
   chose. */
.parcours .jalons > .ajout-jalon,
.parcours .jalons > .bulle-evt {
  position: absolute; top: -23px; z-index: 2;
  width: 22px; height: 22px;
  padding: 0;
  border-radius: 50%;
  border: 1.5px solid var(--bord);
  background: var(--surf-1);
  color: var(--txt-2);
  display: grid; place-items: center;
  font-size: 12px; line-height: 1;
  cursor: pointer;
  transform: translateX(-50%);
  transition: transform calc(.16s * var(--anim-vit)) var(--ressort), background calc(.16s * var(--anim-vit)),
              border-color calc(.16s * var(--anim-vit)), color calc(.16s * var(--anim-vit)), opacity calc(.16s * var(--anim-vit)),
              box-shadow calc(.16s * var(--anim-vit));
}
/* La tige. Deux pixels qui descendent jusqu'au trait : sans elle, les
   pastilles flottent au-dessus de la barre sans dire a quel endroit du
   parcours elles se rapportent. */
.parcours .jalons > .ajout-jalon::before,
.parcours .jalons > .bulle-evt::before {
  content: "";
  position: absolute; top: 100%; left: 50%;
  width: 2px; height: 12px; margin-left: -1px;
  background: var(--bord);
  border-radius: 2px;
  transition: background calc(.16s * var(--anim-vit)), height calc(.16s * var(--anim-vit));
}

.parcours .ajout-jalon {
  opacity: 0; pointer-events: none;
  border-style: dashed;
  color: var(--txt-3);
  transform: translateX(-50%) scale(.5);
}
.parcours .ajout-jalon svg { width: 12px; height: 12px; }
.parcours .ajout-jalon::before { background: var(--bord); opacity: .6; }
.parcours:hover .ajout-jalon,
.parcours .ajout-jalon:focus-visible {
  opacity: 1; pointer-events: auto;
  transform: translateX(-50%) scale(1);
}
.parcours .ajout-jalon:hover {
  transform: translateX(-50%) scale(1.22);
  border-color: var(--acc); border-style: solid;
  color: var(--acc-vif);
  background: var(--acc-sourd);
}
.parcours .ajout-jalon:hover::before { background: var(--acc); }

.parcours .bulle-evt {
  border-color: var(--bord-fort); background: var(--surf-3);
  box-shadow: 0 1px 3px -1px rgba(0, 0, 0, .35);
}
.parcours .bulle-evt::before { background: var(--bord-fort); }
/* Au survol elle se souleve : c'est la seule chose de la barre qui s'ouvre au
   clic, et rien ne le disait. */
.parcours .bulle-evt:hover {
  transform: translateX(-50%) translateY(-2px) scale(1.25);
  border-color: var(--acc);
  box-shadow: 0 4px 10px -3px rgba(0, 0, 0, .5);
}
.parcours .bulle-evt:hover::before { background: var(--acc); height: 14px; }
/* Pointe : la chose a ete faite, elle reste comme trace et s'efface d'un ton.
   On ne la retire pas — c'est elle qui explique le delai, plus tard. Assez
   pale pour passer apres le reste, assez nette pour rester lisible : a
   moitie effacee, l'emoji qui la remplit n'etait plus reconnaissable. */
.parcours .bulle-evt.fait { opacity: .72; }
.parcours .bulle-evt.fait:hover { opacity: 1; }
/* Ouverte : quelqu'un attend un geste. Meme jaune que la carte, meme cloche
   que les departs annonces — c'est la meme grammaire dans toute l'application. */
.parcours .bulle-evt.ouvert {
  border-color: var(--alerte);
  background: color-mix(in srgb, var(--alerte) 20%, var(--surf-1));
}
.parcours .bulle-evt.ouvert::before { background: var(--alerte); }
.parcours .bulle-evt .cloche {
  position: absolute; top: -7px; right: -7px;
  font-size: 9px; line-height: 1;
  transform-origin: 50% 10%;
  animation: sonnerie calc(4.5s * var(--anim-vit)) var(--retard, calc(0s * var(--anim-vit))) infinite;
}
.parcours .bulle-evt.ouvert::after {
  content: "";
  position: absolute; inset: -2px;
  border-radius: 50%;
  border: 1px solid var(--alerte);
  pointer-events: none;
  animation: halo calc(2.6s * var(--anim-vit)) var(--retard, calc(0s * var(--anim-vit))) infinite;
}
/* Celle qu'on vient de poser sort de nulle part : sans ce depart, on choisit
   un scenario et une pastille de plus apparait quelque part sur la barre. */
.parcours .bulle-evt.neuve { animation: bulle-arrive calc(.45s * var(--anim-vit)) var(--ressort) backwards; }
@keyframes bulle-arrive {
  from { transform: translateX(-50%) translateY(calc(10px * var(--anim-amp))) scale(calc(1 - 1 * var(--anim-amp))); opacity: 0; }
}

/* Le menu du « + ». Chaque situation annonce ce qu'elle va declencher AVANT
   qu'on la choisisse : choisir, ici, agit — ca pose un rappel dans l'agenda,
   ca reclame une date. Le dire apres coup dans un toast serait le dire trop
   tard. */
.flottant .rang-scenario { align-items: flex-start; gap: 10px; min-width: 300px; }
.flottant .rang-scenario .emo {
  font-size: 15px; line-height: 1.35; flex: none;
  width: 20px; text-align: center;
}
.flottant .rang-scenario .txt {
  display: flex; flex-direction: column; gap: 1px;
  flex: 1; min-width: 0;
}
.flottant .rang-scenario .txt b { font-weight: 600; }
.flottant .rang-scenario .txt i {
  font-style: normal; font-size: 11.5px; line-height: 1.4; color: var(--txt-3);
}
.flottant .rang-scenario .suites {
  display: flex; flex-direction: column; gap: 3px;
  align-items: flex-end; flex: none;
}
.flottant .rang-scenario .suites i {
  font-style: normal; font-size: 10px; white-space: nowrap;
  padding: 1px 7px; border-radius: var(--r-p);
  background: var(--surf-3); color: var(--txt-3);
}
.flottant .rang-scenario:hover .suites i { background: var(--surf-1); }

.flottant .detail-evt {
  margin: 0; padding: 1px 10px 6px;
  max-width: 320px;
  font-size: 12px; line-height: 1.5; color: var(--txt-2);
}

/* La demande d'origine, dans la fiche d'un evenement venu d'un dossier
   traite. Citee et non redigee : c'est la parole du client, pas la notre, et
   le filet a gauche le dit sans qu'on ait a l'ecrire. Elle vient avant la
   note et avant les boutons, parce qu'on relit une reponse en se demandant
   d'abord a quoi elle repondait. */
.demande-evt {
  max-width: 320px;
  padding: 6px 0 6px 10px;
  border-left: 2px solid var(--acc);
  background: linear-gradient(90deg,
              color-mix(in srgb, var(--acc) 9%, transparent), transparent 70%);
  border-radius: 0 var(--r-p) var(--r-p) 0;
}
/* Dans la fenetre de correction, elle rappelle la question au-dessus des deux
   champs qui portent la reponse : plus large, et la place ne manque pas. */
.voile .demande-evt { max-width: none; padding: 8px 0 8px 12px; }
.flottant .demande-evt { margin: 2px 10px 8px; }
.demande-evt b {
  display: block;
  font-size: 10px; font-weight: 650; letter-spacing: .06em;
  text-transform: uppercase; color: var(--txt-3);
  margin-bottom: 3px;
}
.demande-evt span {
  display: block;
  font-size: 12px; line-height: 1.5; color: var(--txt);
}
.voile .demande-evt span { font-size: 13px; color: var(--txt-2); }

/* --- Le survol du parcours ------------------------------------------------

   Trois choses se produisent, et chacune repond a une question qu'on se pose
   en passant sur une vente. Ou en est-elle — la barre s'epaissit et s'allume,
   le regard suit le reflet jusqu'a la ou elle s'arrete. Que manque-t-il — le
   prochain jalon non atteint respire, et c'est le seul element qui bouge tout
   seul. Combien — la somme sort de son gris.

   Rien n'est en boucle permanente : le reflet ne traverse qu'une fois, la
   respiration s'arrete des qu'on s'en va. Une animation qui tourne sans qu'on
   la sollicite cesse d'etre une finition pour devenir un clignotant — c'est la
   meme regle que le lustre des boutons de famille. */

.parcours .jalons::before,
.parcours .jalons::after {
  transition: height calc(.34s * var(--anim-vit)) var(--ressort), top calc(.34s * var(--anim-vit)) var(--ressort),
              box-shadow calc(.42s * var(--anim-vit)) var(--ressort), right calc(.3s * var(--anim-vit)) var(--ressort);
}
/* La carte entiere reveille sa barre, et plus seulement la barre elle-meme :
   on passe sur une vente pour la regarder, pas pour viser trois pastilles. */
.parcours:hover .jalons::before,
.parcours:hover .jalons::after,
.suivi-vente:hover .jalons::before,
.suivi-vente:hover .jalons::after { height: 3px; top: 9.5px; }
/* La lueur reprend la couleur de l'etat : orange tant que c'est promis, vert
   quand c'est acquis. Elle ne dit rien de neuf, elle appuie ce que la barre
   dit deja. */
.parcours:hover .jalons::after,
.suivi-vente:hover .jalons::after {
  box-shadow: 0 0 10px -1px color-mix(in srgb, var(--acc) 70%, transparent);
}
.suivi-vente.confirmee .parcours:hover .jalons::after,
.suivi-vente.confirmee:hover .jalons::after {
  box-shadow: 0 0 10px -1px color-mix(in srgb, var(--bon) 70%, transparent);
}

/* Le reflet epouse exactement la part parcourue : memes bornes que le
   remplissage, au pixel. Sur une vente ou rien n'est atteint il est large de
   zero, donc invisible, et c'est la bonne reponse — il n'y a rien a parcourir. */
.parcours .brille {
  position: absolute; top: 9.5px; height: 3px;
  left: 16.66%;
  right: calc(16.66% + (3 - var(--atteints)) * 33.33%);
  border-radius: 2px;
  pointer-events: none;
  background: linear-gradient(90deg, transparent 0%,
              color-mix(in srgb, #fff 75%, transparent) 50%, transparent 100%);
  background-size: 55% 100%;
  background-repeat: no-repeat;
  background-position: -60% 0;
}
/* Plus lente que l'epaississement, et c'est voulu : elle arrive apres que la
   barre s'est posee, comme un reflet qui suit le mouvement au lieu de
   l'accompagner. */
.parcours:hover .brille,
.suivi-vente:hover .brille { animation: parcours-reflet calc(1.25s * var(--anim-vit)) cubic-bezier(.25, .5, .2, 1); }
@keyframes parcours-reflet { to { background-position: 160% 0; } }

/* Le prochain jalon a atteindre : le premier qui ne soit pas fait. Deux cas,
   et il faut les deux — celui d'une vente ou rien n'est encore atteint, et
   celui, general, du jalon qui suit le dernier fait. */
.parcours:hover .jalon:not(.fait):first-child .pastille,
.parcours:hover .jalon.fait + .jalon:not(.fait) .pastille,
.suivi-vente:hover .jalon:not(.fait):first-child .pastille,
.suivi-vente:hover .jalon.fait + .jalon:not(.fait) .pastille {
  animation: parcours-respire calc(2.1s * var(--anim-vit)) var(--ressort) infinite;
  border-color: var(--acc);
}
@keyframes parcours-respire {
  0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 var(--acc-sourd); }
  50%      { transform: scale(1.12); box-shadow: 0 0 0 5px transparent; }
}

/* Les libelles sortent de leur gris avec la barre : ils sont volontairement
   discrets au repos — trois mots sous chaque vente, sur une liste de vingt,
   feraient un mur — et lisibles des qu'on s'y interesse. Les dates suivent le
   meme chemin, d'un cran en retrait. */
.parcours .jalon .nom { transition: color calc(.3s * var(--anim-vit)); }
.parcours:hover .jalon .nom,
.suivi-vente:hover .jalon .nom { color: var(--txt-2); }
.parcours:hover .jalon.fait .nom,
.suivi-vente:hover .jalon.fait .nom { color: var(--txt); }
.parcours:hover .jalon.fait .quand,
.suivi-vente:hover .jalon.fait .quand { color: var(--txt); }

/* Chaque pastille repond aussi pour elle-meme : l'infobulle explique ce
   qu'est ce jalon, encore faut-il que quelque chose invite a s'y arreter. */
.parcours .jalon .pastille {
  transition: background calc(.3s * var(--anim-vit)), border-color calc(.3s * var(--anim-vit)), transform calc(.4s * var(--anim-vit)) var(--ressort);
}
.parcours .jalon:hover .pastille { transform: scale(1.22); }

.parcours .bout { flex: none; text-align: right; min-width: 116px; }
.parcours .somme {
  font-size: 19px; font-weight: 700; letter-spacing: -.02em;
  color: var(--txt-3);
  font-variant-numeric: tabular-nums;
}
/* Tant que ce n'est pas branche, la somme reste eteinte : elle est promise,
   pas gagnee. La couleur arrive avec la confirmation, et c'est tout le sujet
   de l'ecran. */
.parcours .somme.acquise { color: var(--bon); }
/* La somme s'eclaire avec la barre : c'est la meme phrase en deux endroits —
   voila ou en est la vente, voila ce que ca fait. Un montant deja acquis ne
   bouge pas, il est a sa couleur definitive. */
.parcours .somme { transition: color calc(.3s * var(--anim-vit)), transform calc(.45s * var(--anim-vit)) var(--ressort); }
/* Seule la couleur se joue ici : l'echelle est celle que la carte donne a
   tout ce qu'elle porte, sinon le montant sautait d'une taille a l'autre
   selon qu'on survolait le haut de la carte ou la barre. */
.parcours:hover .somme:not(.acquise),
.suivi-vente:hover .somme:not(.acquise) { color: var(--txt-2); }

/* --- La vente qu'on vient de pointer ---------------------------------------

   Confirmer un raccordement, c'est le seul moment de l'application ou de
   l'argent change de nature. Le toast dit que c'est fait ; il ne dit pas ou.
   Le halo repond a cette moitie-la de la question : il bat deux fois autour de
   la carte concernee, la somme se pose en vert, et c'est fini — le rendu
   suivant ne le rejoue pas. */
.suivi-vente.vient-de-tomber::after {
  content: "";
  position: absolute; inset: -1px;
  border-radius: inherit;
  pointer-events: none;
  border: 1px solid var(--bon);
  box-shadow: 0 0 24px -6px color-mix(in srgb, var(--bon) 75%, transparent);
  animation: eclat-acquis calc(1.3s * var(--anim-vit)) ease-out calc(.25s * var(--anim-vit)) 2 both;
}
@keyframes eclat-acquis {
  0%   { opacity: 0; }
  18%  { opacity: 1; }
  100% { opacity: 0; }
}
@keyframes somme-tombe {
  0%   { transform: scale(1); }
  35%  { transform: scale(calc(1 + .2 * var(--anim-amp))); }
  100% { transform: scale(1); }
}
.suivi-vente.vient-de-tomber .somme { animation: somme-tombe calc(.8s * var(--anim-vit)) var(--ressort) calc(.3s * var(--anim-vit)); }

@media (prefers-reduced-motion: reduce) {
  .parcours:hover .brille { animation: none; }
  .parcours:hover .jalon:not(.fait):first-child .pastille,
  .parcours:hover .jalon.fait + .jalon:not(.fait) .pastille { animation: none; }
  .parcours .jalon:hover .pastille { transform: none; }
  /* La carte grandit toujours sous le curseur — c'est ce qui designe la
     vente qu'on regarde, et le mouvement reduit ne demande pas de supprimer
     ce qui aide a se reperer. Ce qui part, c'est le glissement : le
     changement de taille est immediat au lieu d'etre joue. */
  .suivi-vente,
  .suivi-vente [data-client],
  .suivi-vente .etiq,
  .suivi-vente .rangee.petit span,
  .suivi-vente .jeton-etat,
  .suivi-vente .jalon .pastille,
  .suivi-vente .bulle-evt,
  .suivi-vente .somme,
  .suivi-vente .bout .petit,
  .suivi-vente .rangee .btn.mini,
  [data-bloc="s-compteurs"] .tuile,
  [data-bloc="s-compteurs"] .tuile .valeur { transition-duration: calc(0s * var(--anim-vit)); }
  .suivi-vente,
  .parcours .jalons::after,
  .parcours .jalon.fait .pastille,
  .bilan-mois .balance > i,
  .bilan-mois:hover .balance,
  .jeton-etat .cloche,
  .parcours .bulle-evt .cloche,
  .parcours .bulle-evt.neuve,
  .suivi-vente.vient-de-tomber .somme { animation: none; }
  /* Le halo d'une pastille ouverte s'arrete, mais son cercle reste : c'est lui
     qui distingue « il y a quelque chose a faire » de « c'est classe ». */
  .parcours .bulle-evt.ouvert::after { animation: none; opacity: .7; }
  .parcours .ajout-jalon:hover,
  .parcours .bulle-evt:hover { transform: translateX(-50%); }
  /* Le halo ne bat plus, mais il reste : c'est lui qui designe la carte qu'on
     vient de pointer, et sans mouvement il faut bien que quelque chose la
     montre. Il disparait au rendu suivant, comme le battement. */
  .suivi-vente.vient-de-tomber::after { animation: none; opacity: .8; }
}

/* Le bareme, dans Administration : un champ etroit au bout de la ligne du
   motif, aligne a droite pour qu'une colonne de montants se lise d'un trait. */
.montant-motif { display: flex; align-items: center; gap: 4px; flex: none; }
.montant-motif input {
  width: 78px; text-align: right;
  font-variant-numeric: tabular-nums;
}
.montant-motif span { color: var(--txt-3); font-size: 12.5px; }

@media (max-width: 980px) {
  /* Au doigt, la somme passe sous la barre : cote a cote, il ne reste ni
     assez de largeur pour les trois libelles, ni pour le montant. */
  .parcours { flex-direction: column; align-items: stretch; gap: var(--e2); }
  .parcours .bout { text-align: left; }
  /* La somme est passee a gauche : elle doit donc grandir vers la droite. Avec
     l'ancrage de la mise en page large, le survol la poussait hors de la
     carte. */
  .suivi-vente .somme,
  .suivi-vente .bout .petit { transform-origin: left center; }

  /* Et le mois ne se survole pas : la ligne de legende s'empile plutot que de
     serrer le delai moyen contre les deux comptes. */
  .bilan-mois .legende { gap: var(--e2); }
}

/* Un bandeau qui se deroule quand il devient vrai qu'il y a quelque chose a
   dire — les precisions d'une vente, une fois une offre cochee. Le mouvement
   explique : il montre d'ou vient ce bloc de questions, qui sinon apparaitrait
   d'un coup au milieu du formulaire et donnerait l'impression d'avoir rate un
   ecran.

   La hauteur passe par une ligne de grille de 0fr a 1fr : c'est le seul moyen
   d'aller vers une hauteur qu'on ne connait pas d'avance. Un max-height en dur
   aurait fallu deviner combien font deux segments et un champ de date, et se
   serait desaccorde a la premiere ligne de texte ajoutee.

   L'ouverture est une animation et non une transition, et la difference n'est
   pas cosmetique : « ouvert » pose tout de suite l'etat d'arrivee, et
   l'animation ne fait que rejouer l'arrivee par-dessus. Avec une transition,
   c'est elle qui porte la hauteur — si elle ne demarre pas, ou se fige (un
   onglet en arriere-plan suffit), le bandeau reste a zero et la vente perd ses
   trois questions sans que rien ne le dise. Aucune propriete qui decide de la
   visibilite ne doit dependre d'une animation qui aurait le droit de ne pas
   tourner. La refermeture, elle, coupe net : c'est un retour en arriere, il
   n'a rien a expliquer.

   Referme, le bandeau n'est pas seulement invisible : sa visibilite tombe, ce
   qui le sort du parcours au clavier. Sans cela, la tabulation traversait six
   commandes de hauteur nulle. */
@keyframes deroule {
  from { grid-template-rows: 0fr; opacity: 0; }
}
.deroulant { display: grid; grid-template-rows: 0fr; }
.deroulant > * { overflow: hidden; min-height: 0; visibility: hidden; }
.deroulant.ouvert {
  grid-template-rows: 1fr;
  animation: deroule calc(.24s * var(--anim-vit)) var(--ressort);
}
.deroulant.ouvert > * { visibility: visible; }

/* Les onglets et segments : le fond suit le doigt. */
.segmente button, .onglets button {
  transition: background calc(.14s * var(--anim-vit)), color calc(.14s * var(--anim-vit)), transform calc(.1s * var(--anim-vit)) var(--ressort);
}
.segmente button:active, .onglets button:active { transform: scale(.96); }

/* Le chapeau d'une carte au survol de celle-ci : rien ne bouge, mais le titre
   s'eclaircit. C'est le minimum pour qu'une carte paraisse vivante sans
   qu'elle gigote. */
.carte { transition: border-color calc(.18s * var(--anim-vit)), background calc(.18s * var(--anim-vit)); }

/* Le fil de progression, a la place du trou noir. Deux pixels en haut de la
   fenetre pendant que le serveur repond : l'ecran precedent reste lisible et
   reste manipulable, on sait seulement que quelque chose est parti. Surtout
   pas de fondu sur le contenu — ce serait refaire le clignotement qu'on vient
   d'enlever. */
#vue.chargeant::before {
  content: ""; position: fixed; inset: 0 0 auto; height: 2px; z-index: 60;
  background: linear-gradient(90deg, transparent, var(--acc), transparent);
  background-size: 38% 100%; background-repeat: no-repeat;
  animation: fil-charge calc(.9s * var(--anim-vit)) linear infinite;
  pointer-events: none;
}
@keyframes fil-charge {
  from { background-position: -38% 0; }
  to   { background-position: 138% 0; }
}
@media (prefers-reduced-motion: reduce) {
  #vue.chargeant::before { animation: none; background: var(--acc); }
}

@media (prefers-reduced-motion: reduce) {
  #vue.entrant .page > *, #vue.entrant .grille > .tuile,
  .progression > i { animation: none; }
  .tuile:hover, .rang:hover, .tuile:hover .valeur { transform: none; }
  /* Le bandeau apparait au lieu de descendre. Il s'ouvre quand meme : la
     hauteur vient de la classe, pas de l'animation. */
  .deroulant.ouvert { animation: none; }
}

/* La composition d'un portefeuille : une seule barre, un segment par statut.
   Les couleurs sont celles des pastilles de la liste des clients — la meme
   information ne prend pas deux palettes selon l'ecran. */
.barre-statuts {
  display: flex;
  height: 12px;
  border-radius: 6px;
  overflow: hidden;
  background: var(--surf-2);
}
.barre-statuts i { display: block; height: 100%; }
.barre-statuts i + i { border-left: 1px solid var(--surf); }

/* Le volet de filtres, sous la barre de recherche. Il se detache par un filet
   et non par un fond : c'est le prolongement de la barre, pas un second bloc
   pose par-dessus. */
.volet-filtres {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--bord);
}

/* --- Conversations ---------------------------------------------------------

   L'ecran qui reunit les trois canaux. Deux exigences s'y opposent : une liste
   de trois cents interlocuteurs doit se parcourir vite, et le fil de droite
   doit se lire comme une conversation. D'ou une liste dense et sans ornement,
   et un fil aere ou seule la carte ouverte a du relief. */

/* Les initiales, a la place d'une icone identique pour tout le monde : dans une
   liste longue, c'est ce qui permet de reperer une fiche au defilement. La
   teinte reste sobre — l'accent est reserve a ce qui est selectionne. */
.ava {
  flex: none;
  width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 11.5px; font-weight: 700; letter-spacing: .02em;
  color: var(--ava, var(--txt-2));
  background: color-mix(in srgb, var(--ava, var(--txt-3)) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--ava, var(--txt-3)) 26%, transparent);
}
.ava.grand { width: 44px; height: 44px; font-size: 14px; }
.rang.selectionne .ava { --ava: var(--acc); }

.rang.qui { padding: 10px var(--e4); }
.rang.qui .colonne-fin {
  flex-direction: column; align-items: flex-end; gap: 3px;
  margin-left: auto; text-align: right;
}
.canaux { display: flex; gap: 6px; align-items: center; }
.canaux .canal {
  display: inline-flex; align-items: center; gap: 2px;
  font-size: 11px; color: var(--txt-3);
}
.canaux .canal b { font-weight: 600; font-variant-numeric: tabular-nums; }

/* Le depli de l'interlocuteur ouvert. Il vit dans la liste, sous son rang, et
   doit se lire comme une dependance de celui-ci : fond creuse, filet a gauche
   qui le rattache, jamais de bordure complete qui en ferait un bloc autonome. */
.deplie {
  background: var(--surf-2);
  border-left: 2px solid var(--acc);
  padding: 4px 0;
}
.deplie > * + * { border-top: 1px solid var(--bord); }
.sous-rang {
  display: flex; align-items: center; gap: var(--e2);
  width: 100%; padding: 7px var(--e4) 7px 26px;
  background: none; border: 0; cursor: pointer; text-align: left;
  color: var(--txt-2); font: inherit; font-size: 12px;
}
.sous-rang:hover { background: var(--acc-sourd); color: var(--txt); }
.sous-rang.actif { color: var(--acc-vif); font-weight: 600; }
.sous-rang .tronque { flex: 1; min-width: 0; }

/* Le fil. La journee sert de repere : sans elle, quatre-vingts echanges etales
   sur deux ans se lisent comme une bouillie. Le trait la traverse pour qu'on
   la voie comme une separation, pas comme un titre de plus. */
.fil-unifie { display: flex; flex-direction: column; gap: var(--e3); }
.jour-fil {
  display: flex; align-items: center; gap: var(--e3);
  margin: var(--e3) 0 0;
  font-size: 11.5px; font-weight: 600; text-transform: lowercase;
  color: var(--txt-3);
}
.jour-fil::before, .jour-fil::after {
  content: ""; height: 1px; background: var(--bord); flex: 1;
}
.jour-fil::before { max-width: 24px; }

/* --- Une entree du fil ------------------------------------------------------

   Toutes les entrees ont la meme geometrie, qu'il s'agisse d'un SMS, d'un
   appel ou d'un message vocal : une etiquette qui dit ce que c'est, puis le
   contenu. C'est ce qui remplace les cartes — un appel ne fait plus un pave
   au milieu d'une conversation ecrite.

   Le liseret de gauche porte le canal en couleur. Il tient lieu de colonne :
   quatre appels dans la journee se suivent le long du meme trait, et le
   regard voit la serie sans lire quatre etiquettes. */

.fil-entree {
  display: flex; flex-direction: column; gap: 3px;
  padding-left: var(--e3);
  border-left: 2px solid var(--bord);
}
.fil-entree[data-canal="appel"]  { border-left-color: var(--acc-bord); }
.fil-entree[data-canal="vocale"] { border-left-color: var(--info, var(--acc-bord)); }
.fil-entree[data-canal="sms"]    { border-left-color: var(--bord); }
/* Ce qu'on a envoye soi-meme se range a droite, etiquette comprise : la
   bulle seule changeait de cote et son etiquette restait a gauche. */
.fil-entree[data-sens="moi"] { align-items: flex-end; text-align: right; }

.fil-etiq {
  display: flex; align-items: baseline; gap: var(--e2); flex-wrap: wrap;
  font-size: 11px; color: var(--txt-3);
}
.fil-etiq .canal { font-weight: 650; color: var(--txt-2); }
.fil-etiq .quand { font-variant-numeric: tabular-nums; }
.fil-etiq .quand::before { content: "· "; color: var(--txt-3); }
.fil-etiq .marque { font-weight: 600; }

/* Un appel replie tient dans une bulle, comme un message. Elle occupe toute
   la largeur : le texte d'un appel est un compte rendu, pas une replique, et
   le decaler donnerait a croire que quelqu'un l'a dit a droite ou a gauche. */
.tour.appel { display: block; }
.tour.appel .bulle {
  display: flex; align-items: baseline; gap: var(--e2);
  max-width: 100%;
  cursor: pointer;
  transition: border-color calc(.12s * var(--anim-vit)), background calc(.12s * var(--anim-vit));
}
.tour.appel .bulle:hover { border-color: var(--bord-fort); background: var(--surf-3); }
.tour.appel .extrait {
  flex: 1; min-width: 0;
  color: var(--txt-2);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
/* « Ecouter » ne prend pas la place d'un bouton : il se montre au survol, et
   la bulle entiere reste la cible. */
.tour.appel .ecouter {
  flex: none;
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11.5px; color: var(--acc-vif);
  opacity: 0; transition: opacity calc(.14s * var(--anim-vit));
}
.tour.appel .bulle:hover .ecouter { opacity: 1; }

/* Celui qu'on ecoute. Le liseré suffit a le distinguer de ses voisins : un
   fond different, sur une carte qui porte deja un lecteur et une
   transcription, ferait un bloc trop lourd au milieu du fil. */
.ech-ouvert { border-color: var(--acc); box-shadow: 0 0 0 1px var(--acc-sourd); }
.ech-ouvert .transcription { margin-top: var(--e3); }

/* Le trou dans la journee. Plus discret que la barre du jour — c'est un
   respir, pas un chapitre — mais il porte l'heure, qui est la seule chose
   qu'on veuille savoir a cet endroit. */
.fil-heure {
  display: flex; align-items: center; gap: var(--e2);
  color: var(--txt-3); font-size: 10.5px;
  font-variant-numeric: tabular-nums;
}
.fil-heure::before, .fil-heure::after {
  content: ""; height: 1px; background: var(--bord); flex: 1;
}
.fil-heure::before { max-width: 40px; }

/* Les SMS gardent leurs bulles. L'heure n'est plus dans la marge : elle est
   passee dans l'etiquette de l'entree, avec le canal — un seul endroit ou
   lire « quoi, quand », pour les trois canaux a la fois. */
.tour.sms { display: block; }
.tour.sms .bulle { display: inline-block; text-align: left; }

.fiche-qui .chapeau { align-items: flex-start; }

@media (max-width: 980px) {
  .deplie { border-left-width: 3px; }
  .sous-rang { padding: 11px var(--e4) 11px 22px; }
}

/* ===========================================================================
   Telephone
   ===========================================================================

   L'application sert d'abord sur le terrain, debout, une main sur l'appareil.
   Trois principes tiennent toute cette section :

     - ce qui se touche fait au moins 44 px de haut. En dessous, on rate la
       cible une fois sur trois, et une visite mal enregistree se corrige au
       bureau le soir ;
     - rien d'important ne va en haut de l'ecran, hors de portee du pouce : la
       navigation descend en bas, les actions restent dans le flux ;
     - une seule colonne. Deux colonnes sur 390 px de large, ce sont deux
       colonnes illisibles.

   Les encoches et la barre de gestes sont prises en compte par env(safe-area-*),
   qui vaut zero partout ailleurs : aucune regle en double a maintenir. */

.nav-bas { display: none; }

@media (max-width: 700px) {

  /* --- La barre du bas --- */

  .nav-bas {
    position: fixed; z-index: 90;
    left: 0; right: 0; bottom: 0;
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 2px;
    padding: 6px 4px calc(6px + env(safe-area-inset-bottom));
    background: color-mix(in srgb, var(--chrome) 92%, transparent);
    backdrop-filter: blur(12px);
    border-top: 1px solid var(--bord);
  }
  .nav-bas button {
    display: flex; flex-direction: column; align-items: center; gap: 3px;
    min-height: 46px;
    padding: 5px 2px;
    border: 0; border-radius: var(--r-s);
    background: transparent;
    color: var(--txt-3);
    font: inherit; font-size: 10px; font-weight: 600;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .nav-bas button svg { width: 21px; height: 21px; }
  .nav-bas button.actif { color: var(--acc-vif); }
  .nav-bas button span {
    max-width: 100%;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }

  /* la barre flotte au-dessus du contenu : on lui reserve sa hauteur, sinon
     elle masque la derniere carte de chaque page */
  .principal { padding-bottom: calc(74px + env(safe-area-inset-bottom)); }

  /* Le tiroir garde tout le menu, mais s'ouvre desormais par « Plus ». Il
     descend jusqu'en bas : la barre du bas est au-dessus de lui. */
  .lateral { width: min(280px, 82vw); padding-bottom: calc(80px + env(safe-area-inset-bottom)); }
  .bascule-menu { display: none; }        /* « Plus » l'a remplace */

  .barre-titre { padding-top: env(safe-area-inset-top); }

  /* --- Une seule colonne, partout --- */

  .maitre-detail { grid-template-columns: minmax(0, 1fr); gap: var(--e3); }
  .maitre-detail .volet-liste { position: static; }
  /* La liste ne prend plus tout l'ecran : on doit pouvoir atteindre la fiche
     en dessous sans defiler pendant dix secondes. */
  .maitre-detail .liste.defile { max-height: 46vh; }
  .transcription.defile { max-height: none; }
  /* Un encart qui defile dans une page qui defile deja : au doigt, on ne sait
     plus lequel des deux on pousse. La conversation se deroule, et c'est la
     page qui porte le geste. */
  .transcription.encart { max-height: none; overflow-y: visible; }

  .grille, .g2, .g3, .g4, .g5, .g-1-1, .g-2-1 {
    grid-template-columns: minmax(0, 1fr);
  }
  .colonne { gap: var(--e3); }
  .semaine { grid-template-columns: minmax(0, 1fr); }
  .resultats { grid-template-columns: repeat(2, 1fr); }

  .page, .principal > #vue { padding: var(--e3) var(--e3) var(--e5); }
  .carte { padding: var(--e3); border-radius: var(--r); }
  .carte.serree { padding: 10px; }

  /* --- Ce qui se touche --- */

  .btn { min-height: 44px; padding: 10px 16px; }
  .btn.mini { min-height: 38px; padding: 8px 12px; font-size: 12.5px; }
  .btn.carre { min-width: 44px; }
  .btn.carre.mini { min-width: 38px; }
  input, select, textarea {
    min-height: 44px;
    /* 16 px pile : en dessous, iOS zoome sur le champ des qu'on le touche et
       ne revient jamais en arriere */
    font-size: 16px;
  }
  .segmente button { min-height: 40px; padding: 9px 12px; }
  .onglets button { min-height: 42px; }
  .puce { min-height: 34px; padding: 7px 12px; }
  .rang { padding: 12px 4px; }

  /* Une rangee d'actions qui deborde se met a la ligne au lieu de pousser le
     dernier bouton hors de l'ecran. */
  .rangee { flex-wrap: wrap; }
  .entete-page { flex-direction: column; align-items: flex-start; gap: var(--e3); }
  .entete-page .actions { width: 100%; }
  .entete-page .actions .btn { flex: 1; justify-content: center; }
  h1 { font-size: 23px; }

  /* --- Ecrans particuliers --- */

  /* La minute d'un tour de parole n'apparait qu'au survol : sans souris, elle
     n'apparaitrait jamais. */
  .tour .minute { opacity: 1; }
  .tour .bulle { max-width: 88%; }
  .commandes { gap: 6px; }
  .commandes .btn.carre { min-width: 40px; min-height: 40px; }
  .frise { height: 40px; }               /* plus haute : on la vise au doigt */

  .case-jour { min-height: 54px; padding: 4px; }
  .case-jour .lettre { font-size: 15px; bottom: 4px; left: 5px; }
  .case-jour .visa { width: 22px; height: 22px; }   /* cible tactile */
  .mois-jours { gap: 5px; }

  .ligne-classement { gap: var(--e2); padding: 7px 4px; }
  .ligne-classement .qui { width: 78px; font-size: 12.5px; }
  .ligne-classement .place { width: 22px; }

  /* Un tableau ne se met pas en colonne : on le fait defiler lateralement,
     et on le dit par une ombre au bord. */
  .tableau { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .tableau table { min-width: 560px; }

  /* La modale devient une feuille qui monte du bas : c'est le geste attendu,
     et le contenu long reste atteignable. */
  .voile { align-items: flex-end; }
  .modale {
    width: 100%; max-width: none;
    max-height: 92vh;
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    padding-bottom: calc(var(--e4) + env(safe-area-inset-bottom));
  }
  .modale .contenu { max-height: 68vh; overflow-y: auto; }

  .toasts { bottom: calc(80px + env(safe-area-inset-bottom)); left: 12px; right: 12px; }

  /* L'infobulle de recommandation vise le doigt, pas le curseur : elle
     deborderait d'un ecran etroit. */
  .reco-flottante { max-width: calc(100vw - 24px); }
}

/* Ecran vraiment etroit : on gagne ce qu'on peut. */
@media (max-width: 380px) {
  .nav-bas button span { font-size: 9px; }
  .ligne-classement .qui { width: 62px; }
  .case-jour { min-height: 46px; }
}

/* Sur un ecran tactile, le survol n'existe pas : tout ce qui n'apparaissait
   qu'au survol doit etre visible d'emblee. La requete vise le pointeur, pas la
   largeur — une tablette large reste tactile. */
@media (hover: none) {
  .tour .minute { opacity: 1; }
  .frise .bande { opacity: .75; }
}

/* --- Mode disposition ------------------------------------------------------

   Regler un ecran a la main. Ce mode n'existe que pour un compte habilite, et
   uniquement quand il l'ouvre : hors edition, aucun de ces elements n'est dans
   la page. Le terrain ne peut donc pas deplacer un bloc par megarde.

   Les blocs sont cernes d'un pointille pendant le reglage — sans ce reperage,
   on ne sait pas ce qui est deplacable et ce qui ne l'est pas. */

.dispo-edition #vue [data-bloc] {
  position: relative;
  outline: 1px dashed var(--acc-bord);
  outline-offset: 3px;
  border-radius: var(--r-xs);
  min-height: 56px;                     /* de quoi loger la barre d'un bloc vide */
  /* La barre se pose DANS le bloc et pousse son contenu vers le bas. Debordant
     au-dessus, elle sortait de l'ecran pour le premier bloc de la page et se
     faisait rogner par les conteneurs qui coupent leur trop-plein. */
  padding-top: 34px;
}

/* Un bloc masque reste visible pendant le reglage, mais delave : on doit
   pouvoir le retrouver pour le rallumer, sans croire qu'il est actif. */
.dispo-edition #vue [data-bloc].bloc-masque {
  opacity: .32;
  filter: grayscale(1);
}

.dispo-outils {
  position: absolute; top: 5px; right: 8px; z-index: 40;
  display: flex; align-items: center; gap: 2px;
  padding: 3px 4px 3px 10px;
  border: 1px solid var(--acc-bord);
  border-radius: var(--r-p);
  background: var(--surf-3);
  box-shadow: var(--ombre-flottante);
}
.dispo-outils .nom {
  font-size: 11px; font-weight: 650; color: var(--txt-2);
  margin-right: 4px; white-space: nowrap;
}
.dispo-outils button {
  display: grid; place-items: center;
  width: 24px; height: 24px;
  border: 0; border-radius: var(--r-p);
  background: transparent;
  color: var(--txt-2);
  font: inherit; font-size: 13px;
  cursor: pointer;
}
.dispo-outils button:hover { background: var(--acc); color: var(--sur-acc); }
.dispo-outils svg { width: 14px; height: 14px; }

/* La carte geographique capte la molette et les clics : pendant le reglage on
   la neutralise, sinon on zoome au lieu de deplacer le bloc. */
.dispo-edition #vue .carte-geo { pointer-events: none; }

/* Les poignees de bord. Une bande fine sur l'arete, invisible au repos et qui
   s'allume quand la souris s'en approche : elle doit se trouver sans encombrer
   le bloc qu'elle borde. Elles n'existent que pendant le reglage. */
.dispo-poignee {
  position: absolute; z-index: 41;
  background: transparent;
  transition: background calc(.12s * var(--anim-vit));
}
.dispo-poignee.droite {
  top: 8px; bottom: 8px; right: -5px; width: 10px;
  cursor: col-resize;
  border-radius: var(--r-p);
}
.dispo-poignee.bas {
  left: 8px; right: 8px; bottom: -5px; height: 10px;
  cursor: row-resize;
  border-radius: var(--r-p);
}
.dispo-poignee:hover { background: var(--acc); }

/* Pendant le tirage, le curseur ne doit pas selectionner du texte au passage,
   ni la carte capter le geste. */
.dispo-tirage, .dispo-tirage * { user-select: none !important; }
.dispo-tirage { cursor: grabbing; }

.dispo-poignee.gauche {
  top: 8px; bottom: 8px; left: -5px; width: 10px;
  cursor: col-resize;
  border-radius: var(--r-p);
}

/* --- Agenda : la journee heure par heure ------------------------------------

   Une colonne ou l'heure est une distance : un rendez-vous d'une demi-heure
   fait la moitie d'un rendez-vous d'une heure, et un trou de deux heures se
   voit comme un trou de deux heures. C'est ce que la liste ne pouvait pas
   dire, et c'est ce qu'on cherche quand on cale un rendez-vous au telephone. */

.agenda-jour {
  position: relative;
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr);
  gap: var(--e2);
  margin-top: var(--e3);
}
.agenda-heures { position: relative; }
.agenda-heure {
  position: absolute; left: 0; right: 0;
  font-size: 11px; font-variant-numeric: tabular-nums;
  color: var(--txt-3);
  transform: translateY(-6px);
  text-align: right;
}

.agenda-piste {
  position: relative;
  border-left: 1px solid var(--bord);
}

/* Les bandes vides. Invisibles au repos — un quadrillage de vingt-quatre
   boutons ferait un damier illisible — elles s'annoncent au survol. */
.creneau-libre {
  position: absolute; left: 0; right: 0;
  display: flex; align-items: center;
  padding: 0 var(--e3);
  border: 0; border-top: 1px dashed transparent;
  background: transparent;
  color: var(--txt-3);
  font: inherit; font-size: 11px;
  cursor: pointer;
}
.creneau-libre:nth-child(odd) { border-top-color: var(--liseret); }
.creneau-libre span { opacity: 0; transition: opacity calc(.12s * var(--anim-vit)); }
.creneau-libre:hover {
  background: var(--acc-sourd);
  border-top-color: var(--acc-bord);
}
.creneau-libre:hover span { opacity: 1; color: var(--acc-vif); }

.agenda-rdv {
  position: absolute;
  overflow: hidden;
  display: flex; align-items: baseline; gap: 6px;
  padding: 4px 8px;
  border-radius: var(--r-xs);
  border-left: 3px solid var(--acc);
  background: var(--surf-3);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .35);
  font-size: 12px;
  cursor: pointer;
}
.agenda-rdv:hover { background: var(--surf-2); outline: 1px solid var(--acc-bord); }
.agenda-rdv b { font-variant-numeric: tabular-nums; color: var(--txt); }
.agenda-rdv .qui {
  color: var(--txt-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.agenda-rdv.perso { background: var(--surf-2); }

@media (max-width: 700px) {
  /* Au doigt, une bande d'un quart d'heure ne se vise pas : on garde la
     colonne mais on lui donne de l'air. */
  .agenda-jour { grid-template-columns: 38px minmax(0, 1fr); }
  .agenda-rdv { font-size: 11.5px; }
}

/* Un rendez-vous qu'aucune fiche ne porte : bordure en pointille, pour qu'il
   se repere dans la colonne sans qu'on ait a ouvrir sa carte. */
.agenda-rdv.orphelin {
  border-left-style: dashed;
  background: repeating-linear-gradient(135deg,
    var(--surf-3) 0 8px, var(--surf-2) 8px 16px);
}

/* --- Barre laterale rabattue -----------------------------------------------

   Sur un portefeuille de trois cents lignes, deux cent quarante-six pixels de
   menu sont deux cent quarante-six pixels de moins pour la liste et la fiche.
   Repliee, la barre garde ses icones : on continue de naviguer d'un clic, et
   on ne perd que les libelles — que trois jours d'usage rendent inutiles.

   Le repli ne s'applique qu'au grand ecran. En dessous de 980 px, la barre est
   deja un tiroir qui se retire entierement : la replier n'aurait aucun sens. */

.replier-lateral { display: none; }
@media (min-width: 981px) {
  .replier-lateral { display: inline-flex; }
}

.appli.replie { --lateral: 60px; }

/* Ce qui ne tient pas dans un rail disparait : les libelles, les compteurs,
   et les encarts du bas — choix du compte, du portefeuille, adresse reseau.
   Ils reviennent d'un clic, et rien n'est perdu entre-temps. */
.appli.replie .lateral { padding-left: var(--e2); padding-right: var(--e2); }
.appli.replie .marque .nom,
.appli.replie .marque .sous,
.appli.replie .nav button span,
.appli.replie .nav .rubrique,
.appli.replie .lateral .bas { display: none; }

.appli.replie .marque { justify-content: center; padding: 0; }
.appli.replie .nav button {
  justify-content: center;
  padding: 10px 0;
}
/* Le filet de la rubrique active se cale sur le bord du rail, sinon il sort
   du cadre une fois les libelles retires. */
.appli.replie .nav button.actif::before { left: -8px; }

/* Le libelle revient au survol, en info-bulle : sans lui, une icone qu'on
   n'a pas encore apprise n'est qu'un dessin. */
.appli.replie .nav button { position: relative; }
.appli.replie .nav button::after {
  content: attr(data-libelle);
  position: absolute; left: calc(100% + 8px); top: 50%;
  translate: 0 -50%;
  padding: 5px 9px;
  border: 1px solid var(--bord);
  border-radius: var(--r-xs);
  background: var(--surf-3);
  color: var(--txt);
  font-size: 12px; font-weight: 500;
  white-space: nowrap;
  box-shadow: var(--ombre-flottante);
  opacity: 0; pointer-events: none;
  transition: opacity calc(.12s * var(--anim-vit));
  z-index: 50;
}
.appli.replie .nav button:hover::after { opacity: 1; }

/* Le bouton pivote pour dire dans quel sens il agit. */
.appli.replie .replier-lateral { transform: scaleX(-1); }

/* La grille des primes : origine du contrat en lignes, type de pose en
   colonnes. Depliee sous la ligne qu'elle concerne, pour qu'on voie de quelle
   offre il s'agit sans avoir a s'en souvenir. */
.primes-grille {
  display: grid;
  grid-template-columns: auto repeat(2, minmax(0, 120px));
  gap: var(--e2);
  align-items: center;
  margin: 0 var(--e4) var(--e3) 44px;
  padding: var(--e3);
  border: 1px solid var(--bord);
  border-radius: var(--r-s);
  background: var(--surf-1);
}
.primes-grille .tete {
  font-size: 12px; font-weight: 600; color: var(--txt-2);
  white-space: nowrap;
}

/* La fiche de saisie CRM. Ce qui manque se signale par un filet a gauche :
   sur cinquante lignes, on doit voir d'un coup ce qui reste a aller chercher
   ailleurs, sans lire chaque libelle.

   Chaque valeur est un champ, y compris celles que Larry a deduites : une
   deduction fausse se corrige sur place. Le champ reste discret tant qu'on n'y
   touche pas — a cinquante cases empilees, cinquante cadres pleins font une
   grille illisible. */
.crm-ligne .meta { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; }
.crm-ligne.a-completer { border-left: 3px solid var(--alerte); }

/* Un numéro parti en 33G. Le rouge l'emporte sur le jaune de « à compléter » :
   une case vide se remplit, une case fausse se recopie — et c'est la seconde
   qui coûte. Voir crm.a_corriger(). */
.crm-ligne.a-corriger { border-left: 3px solid var(--danger); }
.crm-ligne.a-corriger input { color: var(--danger-txt); }
.etiq.grave {
  background: rgba(239, 68, 68, .1);
  color: var(--danger-txt);
}
.crm-ligne input[type="text"] {
  padding: 5px 8px;
  background: transparent;
  border-color: transparent;
  font-size: 13.5px;
}
.crm-ligne:hover input[type="text"] { border-color: var(--bord); }
.crm-ligne input[type="text"]:focus { background: var(--surf-2); }

/* Le corps d'une fenetre modale est une colonne flexible : ses enfants se
   partagent la hauteur disponible et se laissent comprimer. Une fiche de
   cinquante lignes s'y retrouvait ecrasee a une ligne et demie par section.
   Ces blocs-la gardent leur taille, et c'est la fenetre qui defile. */
.crm-bloc { flex: none; }

/* --- Expedition VAD ---------------------------------------------------------

   La fenetre qui suit le pilote. Elle sert a trancher en quelques secondes, le
   regard revenant de Chrome : d'ou une carte qui tient en un coup d'oeil — qui,
   quand, combien de cases — et, dessous, la seule liste qui merite d'etre lue,
   celle des doutes. Le journal complet reste accessible, replie : il dit tout,
   y compris l'anodin, et une liste ou l'anodin cotoie le douteux ne se lit
   plus. */

.vad-tete {
  display: flex; align-items: center; gap: var(--e2);
  margin-bottom: var(--e3);
}

.vad-carte {
  background: var(--surf-2); border: 1px solid var(--bord);
  border-radius: var(--r); padding: var(--e4);
}
.vad-carte .nom { font-size: 17px; font-weight: 650; }
.vad-carte .meta { color: var(--txt-3); font-size: 12.5px; margin-top: 2px; }

/* Le compte a rebours. Il ne clignote pas et ne rougit qu'a la fin : une
   minute qui s'egrene sous les yeux presse deja bien assez. */
.vad-compte {
  font-family: "Cascadia Code", Consolas, monospace;
  font-size: 15px; font-variant-numeric: tabular-nums;
  color: var(--txt-2); padding: 3px var(--e2);
  border: 1px solid var(--bord); border-radius: var(--r-p);
}
.vad-compte.presse { color: var(--danger); border-color: var(--danger); }

.vad-doutes { margin-top: var(--e4); }
.vad-doutes > .titre {
  font-size: 12px; font-weight: 600; letter-spacing: .3px;
  text-transform: uppercase; color: var(--alerte);
  margin-bottom: var(--e2);
}
.vad-doutes ul { list-style: none; display: grid; gap: var(--e2); }
.vad-doutes li {
  position: relative; padding-left: var(--e4);
  font-size: 13px; line-height: 1.5; color: var(--txt-2);
}
.vad-doutes li::before {
  content: ""; position: absolute; left: 0; top: 7px;
  width: 6px; height: 6px; border-radius: 50%; background: var(--alerte);
}

.vad-net {
  margin-top: var(--e4); display: flex; align-items: center; gap: var(--e2);
  color: var(--bon); font-size: 13px;
}

.vad-replie { margin-top: var(--e4); }

/* --- Le pupitre du pilote eBuro ---------------------------------------------

   Ce qu'il voit, et ce qu'on lui dit. Deux blocs l'un sous l'autre, dans une
   fenetre qui reste ouverte pendant qu'il travaille : la vue en direct se
   rafraichit deux fois par seconde, le fil une fois.

   L'image garde sa place meme entre deux prises — un cadre qui s'affaisse
   d'un demi-ecran deux fois par seconde rendrait la fenetre illisible. D'ou
   la hauteur minimale et le fond sombre : c'est un ecran eteint, pas un trou.
*/
.pupitre-ecran {
  position: relative;
  min-height: 220px;
  border-radius: var(--r2);
  background: var(--surf-3);
  border: 1px solid var(--bord);
  overflow: hidden;
  display: flex; align-items: center; justify-content: center;
}
.pupitre-ecran img {
  display: block; width: 100%; height: auto;
  /* On clique dedans pour lui montrer où cliquer : le curseur le dit avant
     qu'on ait à l'expliquer. */
  cursor: crosshair;
}
.pupitre-ecran .muet {
  padding: var(--e4); text-align: center;
  color: var(--txt-3); font-size: 12.5px;
}
.pupitre-ecran .pastille {
  position: absolute; top: 8px; right: 8px;
  padding: 2px 8px; border-radius: 999px;
  background: rgba(0, 0, 0, .55); color: #fff;
  font-size: 11px; letter-spacing: .3px;
}

/* Le fil. Deux voix : la sienne a gauche, la notre a droite — la meme
   convention que partout, et elle se lit sans lire les noms. */
.pupitre-fil {
  max-height: 260px; overflow-y: auto;
  display: grid; gap: var(--e2);
  padding: var(--e3); margin-top: var(--e2);
  border-radius: var(--r2);
  background: var(--surf-2);
  border: 1px solid var(--bord);
}
.pupitre-fil .dit {
  max-width: 82%;
  padding: 6px 10px; border-radius: var(--r2);
  font-size: 12.5px; line-height: 1.5;
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.pupitre-fil .dit.lui {
  justify-self: start;
  background: var(--surf-3); color: var(--txt-2);
}
.pupitre-fil .dit.moi {
  justify-self: end;
  background: var(--acc-sourd); color: var(--txt);
}
.pupitre-fil .dit .quand {
  display: block; margin-top: 2px;
  font-size: 10.5px; color: var(--txt-3);
}
.pupitre-fil .vide {
  color: var(--txt-3); font-size: 12.5px; text-align: center;
  padding: var(--e3) 0;
}

.pupitre-dire { display: flex; gap: var(--e2); margin-top: var(--e2); }
.pupitre-dire input { flex: 1; }

/* Ce qu'il a retenu. Une ligne par consigne, avec de quoi la lui retirer :
   un geste appris sur un ecran qui a change de dessin clique desormais a
   cote, et ca ne se voit qu'une fois que c'est fait. */
.pupitre-appris { margin-top: var(--e3); display: grid; gap: 4px; }
.pupitre-appris .un {
  display: flex; align-items: center; gap: var(--e2);
  font-size: 12px; color: var(--txt-3);
}
.pupitre-appris .un b { color: var(--txt-2); font-weight: 600; }

/* --- Larry : le dictaphone et l'echange -------------------------------------

   Deux colonnes, comme partout ailleurs : l'echange a gauche parce que c'est
   lui qu'on regarde, les memos a droite parce qu'on y revient. La proportion
   est inversee par rapport aux autres ecrans — ici la liste est l'accessoire,
   pas le sujet.

   Le fil occupe toute la hauteur disponible et defile tout seul vers le bas.
   La barre du micro reste collee dessous : elle ne doit jamais partir avec le
   defilement, sinon on cherche le bouton au milieu d'une conversation. */

.maitre-detail.larry {
  grid-template-columns: minmax(0, 1fr) minmax(0, var(--colonne-memos, 340px));
}

.larry-echange {
  display: flex; flex-direction: column;
  /* Le fil prend la fenetre moins l'en-tete. Une hauteur libre le laissait
     grandir avec la conversation, et la barre du micro descendait sous le pli
     au bout de cinq echanges. */
  height: calc(100vh - var(--haut-liste, 240px));
  min-height: 420px;
}

.larry-echange .fil {
  flex: 1;
  overflow-y: auto;
  padding: var(--e4);
  display: flex; flex-direction: column; gap: var(--e3);
  scroll-behavior: smooth;
}

/* --- Les bulles ------------------------------------------------------------

   Pas de couleur pleine cote Larry. Un aplat orange sur une reponse de six
   lignes fatigue, et la charte reserve l'orange aux surfaces courtes. Ce qui
   distingue les deux voix, c'est l'alignement et la surface, pas la teinte. */

.dit { display: flex; max-width: 100%; }
.dit.moi { justify-content: flex-end; }
.dit.lui { flex-direction: column; align-items: flex-start; gap: var(--e2); }

.dit .bulle {
  max-width: 82%;
  padding: 10px 14px;
  border-radius: var(--r-l);
  font-size: 13.5px; line-height: 1.6;
  white-space: pre-wrap;            /* une dictee garde ses retours a la ligne */
  overflow-wrap: anywhere;          /* une adresse longue ne deborde pas */
}
.dit.moi .bulle {
  background: var(--acc-sourd);
  border: 1px solid var(--acc-bord);
  border-bottom-right-radius: var(--r-xs);
}
.dit.lui .bulle {
  background: var(--surf-2);
  border: 1px solid var(--bord);
  border-bottom-left-radius: var(--r-xs);
  color: var(--txt);
}

/* --- Une proposition, et son bouton ----------------------------------------

   Elle ne ressemble pas a une bulle, et c'est voulu : ce n'est pas Larry qui
   parle, c'est quelque chose qui attend une decision. Le liseré accentué la
   detache du fil sans crier. */

.proposition {
  display: flex; flex-direction: column; gap: var(--e2);
  margin-left: var(--e3);
  padding: var(--e3);
  background: var(--surf-1);
  border: 1px solid var(--acc-bord);
  border-left: 3px solid var(--acc);
  border-radius: var(--r);
  max-width: 82%;
}
.proposition .quoi {
  display: flex; align-items: baseline; gap: var(--e2);
  font-size: 13.5px; color: var(--txt); line-height: 1.5;
}
.proposition .genre { font-size: 15px; flex: none; }

/* Validee ou refusee, la carte reste dans le fil : on doit pouvoir relire ce
   que Larry avait propose, et voir ce qu'on en a fait. Elle s'efface, elle ne
   disparait pas. */
.proposition.faite, .proposition.ignoree {
  flex-direction: row; align-items: center; gap: var(--e2);
  font-size: 12.5px;
  border-left-width: 1px;
  padding: 8px var(--e3);
}
.proposition.faite {
  border-color: rgba(34, 197, 94, .3);
  color: var(--bon);
  background: rgba(34, 197, 94, .06);
}
.proposition.faite svg { width: 15px; height: 15px; flex: none; }
.proposition.ignoree { border-color: var(--bord); color: var(--txt-3); }

/* --- L'ecran d'accueil du fil ---------------------------------------------- */

.larry-accueil {
  margin: auto;
  max-width: 420px; text-align: center;
  color: var(--txt-2); font-size: 13.5px; line-height: 1.7;
}
.larry-accueil .pastille-logo.grand { margin: 0 auto var(--e3); display: block; }
.larry-accueil p + p { margin-top: var(--e3); }

/* --- La barre du micro ----------------------------------------------------- */

.larry-barre {
  display: flex; align-items: flex-end; gap: var(--e2);
  padding: var(--e3);
  border-top: 1px solid var(--bord);
  background: var(--surf-1);
}

.larry-barre .saisie { flex: 1; min-width: 0; position: relative; }
.larry-barre textarea {
  resize: none;
  max-height: 132px;
  line-height: 1.5;
}

/* Le compteur se pose PAR-DESSUS le champ pendant l'enregistrement : lui
   reserver une ligne a cote ferait sauter la barre de vingt pixels a chaque
   fois qu'on appuie sur le micro. */
.larry-barre .chrono {
  position: absolute; inset: 0;
  display: flex; align-items: center;
  padding: 0 12px;
  background: var(--surf-1);
  border: 1px solid var(--bord);
  border-radius: 10px;
  font-size: 13px; color: var(--txt-2);
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}
.larry-barre .chrono[hidden] { display: none; }

.larry-barre .micro {
  flex: none;
  width: 38px; height: 38px;
  border-radius: 50%;
  border: 1px solid var(--bord-fort);
  background: var(--surf-2);
  color: var(--txt);
  display: grid; place-items: center;
  cursor: pointer;
  transition: background calc(.14s * var(--anim-vit)), border-color calc(.14s * var(--anim-vit)), transform calc(.1s * var(--anim-vit)) var(--ressort);
}
.larry-barre .micro svg { width: 19px; height: 19px; }
.larry-barre .micro:hover:not(:disabled) {
  background: var(--surf-3); border-color: var(--acc-bord);
}
.larry-barre .micro:disabled { opacity: .4; cursor: not-allowed; }

/* En enregistrement, le bouton devient rouge et respire. L'anneau qui pulse
   autour dit « ca tourne » depuis l'autre bout de la piece — un simple
   changement de couleur passe inapercu quand on regarde ailleurs. */
.larry-barre .micro.actif {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}
.larry-barre .micro.actif::after {
  content: "";
  position: absolute;
  width: 38px; height: 38px;
  border-radius: 50%;
  border: 2px solid var(--danger);
  animation: micro-onde calc(1.6s * var(--anim-vit)) ease-out infinite;
}
@keyframes micro-onde {
  from { transform: scale(1);   opacity: .7; }
  to   { transform: scale(1.7); opacity: 0; }
}
.larry-barre .micro { position: relative; }

/* Personne n'a demande d'animation : on la coupe. L'etat rouge suffit a dire
   que le micro est ouvert. */
@media (prefers-reduced-motion: reduce) {
  .larry-barre .micro.actif::after { animation: none; opacity: .5; }
  .larry-echange .fil { scroll-behavior: auto; }
}

/* --- Les memos -------------------------------------------------------------- */

.larry-memos .recherche { margin: var(--e3) 0; }
.larry-memos .liste { border-top: 1px solid var(--bord); }

.memo { padding: 10px var(--e1); }
.memo .entete {
  display: flex; align-items: flex-start; gap: var(--e2);
  cursor: pointer;
}
.memo .entete .quoi { flex: 1; min-width: 0; }
.memo .entete .titre {
  font-size: 13px; line-height: 1.45; color: var(--txt);
  /* Deux lignes au plus : un memo dicte fait trois cents caracteres, et son
     titre est son debut. Sans borne, une entree occupait la moitie du volet. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.memo.ouvert .entete .titre { -webkit-line-clamp: unset; }
.memo .entete .etiq { flex: none; }

.memo .detail { padding: var(--e2) 0 var(--e1); }
.memo .detail .texte {
  font-size: 12.5px; line-height: 1.65; color: var(--txt-2);
  white-space: pre-wrap;
  margin-bottom: var(--e3);
}
.memo .detail audio {
  width: 100%; height: 32px;
  margin-bottom: var(--e2);
}

/* --- Ecran etroit ----------------------------------------------------------
   Les memos passent sous l'echange, et le fil rend sa hauteur fixe : sur un
   telephone, deux zones qui defilent chacune de leur cote sont ingerables. */
@media (max-width: 980px) {
  .larry-echange { height: auto; min-height: 0; }
  .larry-echange .fil { max-height: 58vh; }
}

/* --- Le bandeau du reseau ----------------------------------------------------

   Il ne dit rien quand tout va bien : une application qui annonce en
   permanence qu'elle est en ligne apprend a ne plus etre lue. Il n'apparait
   donc que hors ligne, ou quand des gestes attendent de partir. */

#bandeau-reseau {
  position: fixed; z-index: 85;
  left: 50%; transform: translateX(-50%);
  bottom: calc(var(--e4) + env(safe-area-inset-bottom, 0px));
  display: flex; align-items: center; gap: var(--e2);
  max-width: calc(100vw - 2 * var(--e4));
  padding: var(--e2) var(--e4);
  background: var(--surf-3); color: var(--txt);
  border: 1px solid var(--bord-fort); border-radius: var(--r-p);
  box-shadow: var(--ombre-flottante);
  font-size: 13px;
}
#bandeau-reseau.hors-ligne {
  border-color: var(--alerte);
  box-shadow: var(--ombre-flottante), 0 0 0 1px var(--alerte) inset;
}
#bandeau-reseau .petit { color: var(--txt-3); }

/* Sur un telephone, la barre du bas mange deja la largeur : la phrase
   d'explication saute, l'etiquette et le compte suffisent. */
@media (max-width: 700px) {
  #bandeau-reseau .petit { display: none; }
}

/* --- Le trajet d'une ecriture ---------------------------------------------

   Le bandeau ci-dessus ne parle que des ennuis : hors ligne, en attente, en
   train de rattraper. Quand tout va bien, il n'y a rien — et c'est justement
   ce qui manquait le jour ou la base est partie sur un serveur. Une vente
   cochee s'ecrivait jusqu'ici a dix centimetres de la main ; elle traverse
   maintenant un tunnel et un reseau, et aucun signe a l'ecran ne le disait.
   On fermait la fenetre en se demandant si c'etait parti.

   Deux temps, et pas un de plus. Le point qui court pendant le voyage, la
   coche quand le serveur a repondu. La coche n'est pas une politesse : elle
   n'apparait qu'APRES la reponse, donc elle dit « c'est ecrit la-bas » et non
   « c'est envoye ». La nuance est tout ce qui compte ici.

   Meme place que le bandeau, et jamais en meme temps que lui : quand il y a
   un ennui, c'est lui qui parle, et deux boites au meme endroit se
   recouvriraient. */

#vol-reseau {
  position: fixed; z-index: 84;
  left: 50%; transform: translateX(-50%);
  bottom: calc(var(--e4) + env(safe-area-inset-bottom, 0px));
  display: flex; align-items: center; gap: var(--e2);
  padding: var(--e2) var(--e4);
  background: var(--surf-3); color: var(--txt-2);
  border: 1px solid var(--bord-fort); border-radius: var(--r-p);
  box-shadow: var(--ombre-flottante);
  font-size: 13px;
  animation: vol-entre calc(.18s * var(--anim-vit)) ease-out;
}
#vol-reseau.pose { color: var(--txt); border-color: var(--bon); }
#vol-reseau.pose.efface { opacity: 0; transition: opacity calc(.35s * var(--anim-vit)) ease-in; }

/* La piste et le point : quarante-six pixels, c'est court, et c'est voulu.
   Une barre de progression promettrait une duree qu'on ne connait pas — un
   aller-retour tient en trente millisecondes sur le reseau local et en une
   seconde depuis une cave. Le point tourne en boucle sans jamais pretendre
   dire ou l'on en est. */
.vol-piste {
  position: relative; width: 46px; height: 2px;
  background: var(--bord-fort); border-radius: 2px;
}
.vol-piste i {
  position: absolute; top: -3px; left: 0;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--acc);
  animation: vol-court calc(.9s * var(--anim-vit)) ease-in-out infinite;
}
.vol-coche { color: var(--bon); font-weight: 700; animation: vol-pose calc(.3s * var(--anim-vit)) ease-out; }

@keyframes vol-court {
  0%   { left: 0;    opacity: .15; }
  20%  { opacity: 1; }
  80%  { opacity: 1; }
  100% { left: 38px; opacity: .15; }
}
@keyframes vol-entre {
  from { opacity: 0; transform: translateX(-50%) translateY(calc(6px * var(--anim-amp))); }
}
@keyframes vol-pose {
  from { opacity: 0; transform: scale(calc(1 - .45 * var(--anim-amp))); }
}

/* Le point qui court est la seule chose animee ; sans lui, la boite dit la
   meme chose, en fixe. La coche, elle, reste : c'est une information, pas un
   effet. */
@media (prefers-reduced-motion: reduce) {
  #vol-reseau { animation: none; }
  .vol-piste i { animation: none; left: 19px; opacity: 1; }
  .vol-coche { animation: none; }
}

/* --- Le secteur de tournée qui se dessine ---------------------------------
   La limite apparaissait d'un coup, aplat compris : on voyait une tache
   orange sans savoir par où elle passait. Le trait court maintenant le long
   de la frontière et le fond monte derrière lui — le temps de suivre du
   regard quelle commune on vient de prendre. La longueur du tracé varie d'un
   contour à l'autre, donc c'est le JS qui pose --course. */

.secteur-trace {
  animation: secteur-trait calc(.85s * var(--anim-vit)) ease-out forwards,
             secteur-fond calc(.85s * var(--anim-vit)) ease-out forwards;
}

@keyframes secteur-trait {
  from { stroke-dashoffset: var(--course, 0); }
  to   { stroke-dashoffset: 0; }
}

@keyframes secteur-fond {
  from { fill-opacity: 0; }
  to   { fill-opacity: .08; }
}

/* Le point du centre arrive après le trait, avec un léger dépassement : il
   dit « c'est de là qu'on part », et le décalage évite qu'il se confonde
   avec les points clients déjà posés. */
.secteur-point {
  transform-box: fill-box;
  transform-origin: center;
  animation: secteur-pointe calc(.45s * var(--anim-vit)) calc(.25s * var(--anim-vit)) cubic-bezier(.2, .8, .3, 1.5) backwards;
}

@keyframes secteur-pointe {
  from { transform: scale(0); opacity: 0; }
  to   { transform: scale(1); opacity: 1; }
}

/* Le secteur qui s'en va ne disparaît pas d'un coup : il s'efface pendant
   que le suivant se dessine. Sans ce recouvrement, on voyait la carte se
   vider, puis voyager, puis se remplir — trois temps pour un seul geste. */
.secteur-part {
  animation: secteur-sortie calc(.45s * var(--anim-vit)) ease-in forwards;
  pointer-events: none;
}

@keyframes secteur-sortie {
  to { opacity: 0; }
}

/* Les quartiers d'une commune : un trait fin, posé sous le secteur retenu.
   Ils arrivent en fondu après deux secondes d'arrêt, donc discrètement — ce
   n'est pas une action demandée, c'est une proposition. */
.quartier-trait {
  animation: fondu calc(.35s * var(--anim-vit)) ease-out backwards;
  cursor: pointer;
  transition: fill-opacity calc(.18s * var(--anim-vit)) ease-out, stroke-opacity calc(.18s * var(--anim-vit)) ease-out;
}

.quartier-vise {
  fill-opacity: .22 !important;
  stroke-opacity: 1 !important;
}

@media (prefers-reduced-motion: reduce) {
  .secteur-trace, .secteur-point, .secteur-part, .quartier-trait {
    animation: none;
  }
  .secteur-trace { stroke-dashoffset: 0; }
  .secteur-part { opacity: 0; }
}

/* --- Le réseau des rues qui s'allume --------------------------------------
   Chaque rue est un trait qui se remplit depuis son extrémité, et le retard
   au départ croît avec l'éloignement du centre : l'ensemble se propage vers
   les bords comme un courant qui gagne le réseau. Le retard est calculé en
   JS et posé en --retard, parce qu'il dépend du secteur et de sa taille. */

.rue-eteinte {
  stroke-dasharray: var(--course, 100);
  stroke-dashoffset: var(--course, 100);
  animation: rue-allume calc(.55s * var(--anim-vit)) ease-out var(--retard, calc(0s * var(--anim-vit))) forwards;
}

@keyframes rue-allume {
  from { stroke-dashoffset: var(--course, 100); opacity: .25; }
  to   { stroke-dashoffset: 0; opacity: 1; }
}

/* Les rues où il y a des portes se distinguent par leur couleur et leur
   épaisseur, et par rien d'autre.

   Elles portaient un halo — un drop-shadow SVG. C'est l'un des filtres les
   plus coûteux du navigateur, recalculé à chaque reprojection : soixante-
   quinze rues haloées suffisaient à hacher le zoom. L'orange plein sur trois
   pixels se voit tout aussi bien, et ne coûte rien. */
.rue-portes {
  stroke-linecap: round;
}

/* La liste sous la carte. Chaque ligne arrive avec le même retard que sa rue
   sur la carte : on lit la liste au rythme où le réseau s'allume. */
.rue-ligne {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 5px 0;
  border-bottom: 1px solid var(--bord, #23232b);
  animation: fondu calc(.3s * var(--anim-vit)) ease-out var(--retard, calc(0s * var(--anim-vit))) backwards;
  cursor: pointer;
}

.rue-ligne:hover { color: var(--txt, #fff); }
.rue-ligne .compte { margin-left: auto; font-variant-numeric: tabular-nums; }
.rue-ligne.vide { opacity: .45; }

@media (prefers-reduced-motion: reduce) {
  .rue-eteinte, .rue-ligne { animation: none; }
  .rue-eteinte { stroke-dashoffset: 0; }
}

/* --- Pas de cadre de focus sur la carte -----------------------------------
   Leaflet rend ses polygones focusables. Cliquer un secteur y posait donc le
   focus, et la regle generale de `:focus-visible` dessinait un contour de
   deux pixels autour de sa BOITE ENGLOBANTE — un grand carre orange en
   travers de la carte, sans rapport avec la forme selectionnee.

   La navigation au clavier ne perd rien : c'est le conteneur qui se deplace
   aux fleches, et il garde son cadre a lui quand on l'atteint par tabulation.
   Un trace, lui, ne se « visite » pas au clavier. */

.leaflet-interactive:focus,
.leaflet-interactive:focus-visible,
.leaflet-container:focus {
  outline: none;
}

/* --- Un secteur encore en préparation --------------------------------------
   Il ne contient que des rues : on sait où aller, pas encore chez qui. Le
   confondre avec un vrai portefeuille ferait calculer une tournée de panneaux
   de rue, alors on le sort de la file : liseré rayé, pas de case à cocher, et
   ce qu'il attend écrit dessus.

   Les hachures plutôt qu'une couleur : une teinte de plus se lirait comme un
   statut parmi les autres, alors que c'est d'un état de chantier qu'il s'agit
   — la même convention que les bandes d'un balisage. */

.pf-prepa {
  border-style: dashed;
  border-color: var(--bord-fort);
  position: relative;
  overflow: hidden;
}

.pf-prepa::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 5px;
  background: repeating-linear-gradient(
    -45deg, var(--acc, #ff7900) 0 6px, transparent 6px 12px);
  opacity: .85;
}

.pf-prepa .chapeau b { color: var(--txt-2); }

.pf-etiq-prepa {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .2px;
  color: var(--acc, #ff7900);
  background: color-mix(in srgb, var(--acc, #ff7900) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--acc, #ff7900) 35%, transparent);
}

/* La jauge d'avancement : combien de rues ont trouvé leurs habitants. Elle
   remplace la répartition par statut, qui n'a rien à dire tant que tout le
   monde est « À visiter ». */
.pf-jauge {
  height: 8px;
  border-radius: 999px;
  background: var(--surf-3);
  overflow: hidden;
}

.pf-jauge > i {
  display: block;
  height: 100%;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--acc, #ff7900) 70%, #fff) 0%, var(--acc, #ff7900) 100%);
  transition: width calc(.5s * var(--anim-vit)) cubic-bezier(.2, .8, .3, 1);
}

/* Les rues du secteur, dépliables : on veut souvent vérifier qu'on a bien
   attrapé le bon quartier avant d'y verser un fichier. */
.pf-rues {
  margin-top: 12px;
  border-top: 1px solid var(--bord);
  padding-top: 10px;
}

.pf-rues summary {
  cursor: pointer;
  font-size: 12.5px;
  color: var(--txt-2);
  list-style: none;
  user-select: none;
}

.pf-rues summary::-webkit-details-marker { display: none; }
.pf-rues summary::before { content: "▸ "; display: inline-block; transition: transform calc(.2s * var(--anim-vit)); }
.pf-rues[open] summary::before { content: "▾ "; }
.pf-rues summary:hover { color: var(--txt); }

.pf-rues .liste {
  margin-top: 8px;
  max-height: 190px;
  overflow: auto;
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
}

.pf-rues .liste span {
  font-size: 11.5px;
  padding: 3px 8px;
  border-radius: 7px;
  background: var(--surf-3);
  color: var(--txt-2);
  animation: fondu calc(.25s * var(--anim-vit)) ease-out backwards;
}

.pf-rues .liste span.faite {
  background: color-mix(in srgb, var(--acc, #ff7900) 18%, transparent);
  color: var(--txt);
}

@media (prefers-reduced-motion: reduce) {
  .pf-jauge > i { transition: none; }
  .pf-rues .liste span { animation: none; }
}

/* --- Le probleme, en tete de « Qu'as-tu fait pour X ? » ---------------------

   Meme parti que le panneau de lecture d'un appel : encadre, et non pose a
   plat. Ce qui est ecrit la n'est pas de nous — c'est le message du client ou
   ce que le modele en a compris — et la fenetre, elle, demande ce que NOUS
   avons fait. Les deux ne doivent pas se lire d'un seul tenant. */
.suite-dossier {
  border: 1px solid var(--bord);
  border-radius: var(--r);
  background: var(--surf-1);
  padding: var(--e3);
  margin-bottom: var(--e3);
}

.suite-txt {
  margin-top: var(--e3);
  font-size: 13.5px; line-height: 1.55;
  color: var(--txt-2);
  white-space: pre-wrap;
}
/* Ses mots a lui, entre guillemets : la meme mise en retrait que dans la
   liste, pour qu'on reconnaisse le meme texte d'un ecran a l'autre. */
.suite-dossier .suite-txt.cite {
  padding-left: var(--e3);
  border-left: 2px solid var(--liseret);
}
/* Replie. Un resume de six minutes d'appel pousse hors de l'ecran les boutons
   sur lesquels on vient cliquer : on en montre quatre lignes, le reste se
   demande. */
.suite-dossier .suite-txt.replie {
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.suite-dossier [data-suite-deplier] { margin-top: 4px; }

/* Une vente perdue : la seule etiquette du bloc qui demande qu'on fasse
   quelque chose aujourd'hui. */
.suite-dossier .etiq.suite-issue.perte {
  background: color-mix(in srgb, var(--danger) 18%, transparent);
  color: var(--danger);
}

.suite-titre-idees {
  margin-top: var(--e3);
  text-transform: uppercase; letter-spacing: .04em; font-size: 10.5px;
}
/* Les phrases relevees. Sans minute et sans bouton : ici il n'y a rien a
   reecouter, la bande son est restee dans les Conversations. */
.suite-idees {
  list-style: none; margin: var(--e2) 0 0; padding: 0;
  display: flex; flex-direction: column; gap: 2px;
}
.suite-idees li {
  display: flex; align-items: baseline; gap: var(--e2);
  padding: 4px 0;
  font-size: 13.5px; line-height: 1.45; color: var(--txt-2);
}

/* Ce que la circonstance reclame — « rappeler avant vendredi ». C'est la
   consigne, pas le probleme : elle ferme le bloc, en accent. */
.suite-demande {
  margin-top: var(--e3);
  padding-top: var(--e2);
  border-top: 1px solid var(--bord);
  color: var(--txt);
}

/* --- Le repliant, partout le meme ------------------------------------------

   Une seule forme pour tout ce qu'on plie : un filet au-dessus qui le detache
   de ce qui precede, un chevron, un titre qui tient sur une ligne, et le
   detail dessous. Toujours la meme geometrie, parce qu'on en croise six sur
   le meme ecran et que l'oeil doit reconnaitre « il y a quelque chose la
   derriere » sans lire.

   Le titre est en gras et la precision en gris a cote : c'est cette
   precision — « 12 extraits tranches », « veille toutes les 30 min » — qui
   evite d'ouvrir neuf fois sur dix. Voir repli() dans app.js. */

.repli {
  margin-top: var(--e3);
  border-top: 1px solid var(--bord);
  padding-top: var(--e2);
}

/* Sans filet : pour un repliant deja pose dans un cadre a lui, ou imbrique
   dans un autre — deux traits colles ne separent rien de plus qu'un seul. */
.repli.nu { border-top: 0; padding-top: 0; }

.repli > summary {
  display: flex; align-items: baseline; gap: var(--e2);
  flex-wrap: wrap;
  padding: 4px 0;
  font-size: 12.5px;
  color: var(--txt-2);
  cursor: pointer;
  list-style: none;
  user-select: none;
}
/* Le triangle du navigateur est remplace par le notre : le sien ne tourne pas
   et ne se teinte pas. */
.repli > summary::-webkit-details-marker { display: none; }
.repli > summary::marker { content: ""; }
.repli > summary:hover { color: var(--txt); }
.repli > summary:hover .repli-tete { color: var(--txt); }

.repli > summary .chevron {
  flex: none;
  width: 12px;
  font-size: 12px; line-height: 1;
  color: var(--txt-3);
  transition: transform calc(.2s * var(--anim-vit)) ease;
}
.repli > summary:hover .chevron { color: var(--acc); }
.repli[open] > summary .chevron { transform: rotate(90deg); }

.repli .repli-tete {
  display: flex; align-items: baseline; gap: var(--e2);
  flex-wrap: wrap;
  flex: 1; min-width: 0;
  font-weight: 600; color: var(--txt);
}
/* Ce qui dispense d'ouvrir : l'etat du bloc, en gris, sur la meme ligne. */
.repli .repli-sous {
  font-weight: 400; font-size: 11.5px; color: var(--txt-3);
}

.repli-dedans { margin-top: var(--e2); }

/* Qui prefere que rien ne bouge n'a pas a subir le chevron qui tourne. */
@media (prefers-reduced-motion: reduce) {
  .repli > summary .chevron { transition: none; }
}

/* --- Où en est chaque vente du relevé eBuro ---------------------------------

   Une ligne par client, trois coches et l'alerte. Le nom d'abord — c'est par
   lui qu'on cherche —, les marques ensuite, sur une ligne qui se replie quand
   la fenêtre est étroite plutôt que de couper un libellé en deux. */

.profils { margin-top: var(--e4); display: grid; gap: var(--e2); }
.profils > .titre {
  font-size: 12px; font-weight: 600; letter-spacing: .3px;
  text-transform: uppercase; color: var(--txt-3);
  margin-bottom: var(--e1);
}
.profil {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--e2);
  padding: 7px var(--e3);
  border: 1px solid var(--bord); border-radius: var(--r-s);
  background: var(--surf-2);
}
/* Ce qui est passé s'efface un peu : ce qui reste à faire doit ressortir. */
.profil.passe { opacity: .82; }
.profil.alerte { border-left: 3px solid var(--danger); }
.profil > .nom {
  font-size: 13px; font-weight: 600; color: var(--txt);
  flex: 1 1 12ch; min-width: 12ch;
}
.profil .marques {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--e3);
}
.profil .marque {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; color: var(--txt-2); white-space: nowrap;
}
.coche {
  display: inline-flex; align-items: center; justify-content: center;
  width: 15px; height: 15px; border-radius: 50%;
  font-size: 11px; line-height: 1; font-weight: 700;
}
.coche.faite { background: rgba(34, 197, 94, .16); color: var(--bon); }
.coche.vide { border: 1px solid var(--bord-fort); color: var(--txt-3); }
.coche.muette { color: var(--txt-3); }
