/* WebPhone — standard téléphonique : laque sombre, signal ambre, jack jade */

:root {
  --ink:      #08191A;
  --lacquer:  #0F2A2B;
  --edge:     #1C4142;
  --amber:    #E9B341;
  --jade:     #46C99A;
  --coral:    #E1584B;
  --mist:     #D6E4E1;
  --dim:      #7C9895;

  /* La hauteur de l'en-tete d'une conversation. La barre de choix la reprend :
     elle prend sa PLACE, et le fil ne doit pas sauter quand l'une remplace
     l'autre. */
  --h-tete: 43px;

  --mono: ui-monospace, "SF Mono", "Roboto Mono", Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

* { box-sizing: border-box; }

/* Un display explicite (.panel est en flex) l'emporte sur le display:none
   implicite de l'attribut hidden : sans cette regle, tous les ecrans
   s'empilent sur la meme page. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  min-height: 100%;
  background: var(--ink);
  color: var(--mist);
  font-family: var(--sans);
  -webkit-text-size-adjust: 100%;
}

body {
  display: flex;
  justify-content: center;
  padding: max(16px, env(safe-area-inset-top)) 16px max(16px, env(safe-area-inset-bottom));
  /* Toute la hauteur, pour que la conversation puisse pousser sa saisie tout
     en bas. « dvh » et non « vh » : sur mobile, vh compte le viewport large,
     celui qu on a barre d adresse repliee, et la saisie passait sous le bord. */
  min-height: 100vh;
  min-height: 100dvh;
}

.phone {
  width: 100%;
  max-width: 420px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* ---------------------------------------------------------------- bandeau */
.bar {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--edge);
}

.mark {
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--mist);
}

.state {
  flex: 0 1 auto;
  text-align: right;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--dim);
}

.state::before {
  content: "";
  display: inline-block;
  width: 7px; height: 7px;
  margin-right: 7px;
  border-radius: 50%;
  background: currentColor;
  vertical-align: middle;
}

.state[data-tone="ring"]  { color: var(--amber); animation: pulse 1.1s ease-in-out infinite; }
.state[data-tone="live"]  { color: var(--jade); }
.state[data-tone="ended"] { color: var(--coral); }

@keyframes pulse { 50% { opacity: .35; } }

/* ------------------------------------------------------------- formulaire */
.panel { display: flex; flex-direction: column; gap: 16px; }

.field { display: flex; flex-direction: column; gap: 7px; }

.field-label {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--dim);
}

input {
  width: 100%;
  padding: 14px 15px;
  background: var(--lacquer);
  border: 1px solid var(--edge);
  border-radius: 3px;
  color: var(--mist);
  font-family: var(--sans);
  font-size: 16px;
}

input:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 1px;
}

input.dial {
  font-family: var(--mono);
  font-size: 26px;
  letter-spacing: .16em;
  text-transform: uppercase;
  text-align: center;
  color: var(--amber);
}

input::placeholder { color: #4C6D6B; letter-spacing: normal; }
input.dial::placeholder { letter-spacing: .16em; }

.hint {
  margin: 0;
  font-size: 13px;
  line-height: 1.55;
  color: var(--dim);
}

/*
 * PREVENIR SOI-MEME, PAR SMS.
 *
 * Sous le message d'erreur, dans son ton : c'est la suite de ce qu'il dit, pas
 * une alerte de plus. Le lien reste un bouton — on le touche avec le pouce.
 */
.sms-soi {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 0 0 10px;
  font-size: .9rem;
  opacity: .9;
}
.sms-soi .btn { flex: none; }

.error {
  margin: 0;
  padding: 11px 13px;
  border-left: 2px solid var(--coral);
  background: rgba(225, 88, 75, .1);
  color: #F3B4AE;
  font-size: 13px;
  line-height: 1.5;
}

/* ---------------------------------------------------------------- boutons */
.btn {
  padding: 16px;
  border: 0;
  border-radius: 3px;
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: .16em;
  text-transform: uppercase;
  cursor: pointer;
}

.btn-call {
  background: var(--amber);
  color: var(--ink);
  font-weight: 700;
}

.btn-call:disabled { opacity: .45; cursor: progress; }

.btn-ghost {
  background: transparent;
  border: 1px solid var(--edge);
  color: var(--dim);
}

.btn:focus-visible { outline: 2px solid var(--mist); outline-offset: 2px; }

/*
 * CE QU'ON VIENT DE VISER — la conversation, ou le salon, ou l'on est attendu.
 * Posee par « porterLeRegard », otee au premier regard ailleurs.
 */
.vise {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
  border-radius: 10px;
}

/* ------------------------------------------------------------ appel actif */
.panel-call { gap: 22px; }

.line-id {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

.line-label {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--dim);
}

.line-id strong {
  font-family: var(--mono);
  font-size: 19px;
  letter-spacing: .14em;
  color: var(--amber);
  font-weight: 500;
}

/* Vumètre : une réglette de 24 segments par voie, alimentée par l'audio réel */
.meters { display: flex; flex-direction: column; gap: 14px; }

.meter { display: flex; flex-direction: column; gap: 8px; }

.meter-name {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--dim);
}

.bars {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 46px;
}

.bars i {
  flex: 1;
  height: 4px;
  min-height: 4px;
  background: var(--edge);
  border-radius: 1px;
  transition: height .07s linear, background-color .12s linear;
}

.bars i.on  { background: var(--jade); }
.bars i.hot { background: var(--amber); }

.meter-self .bars { height: 26px; opacity: .75; }
.meter-self .bars i.on { background: var(--dim); }

.timer {
  font-family: var(--mono);
  font-size: 44px;
  font-variant-numeric: tabular-nums;
  letter-spacing: .04em;
  line-height: 1;
}

.quality {
  margin-top: -14px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--dim);
}

.controls { display: flex; gap: 12px; }

.key {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  padding: 18px 10px;
  background: var(--lacquer);
  border: 1px solid var(--edge);
  border-radius: 3px;
  color: var(--mist);
  cursor: pointer;
}

.key-glyph { font-size: 17px; line-height: 1; }

.key-text {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
}

.key[aria-pressed="true"] {
  background: var(--amber);
  border-color: var(--amber);
  color: var(--ink);
}

.key-end { border-color: rgba(225, 88, 75, .5); color: #F0968D; }
.key-end:active { background: var(--coral); color: var(--ink); }

.key:focus-visible { outline: 2px solid var(--mist); outline-offset: 2px; }

/* ------------------------------------------------------------------- pied */
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ------------------------------------------------------------ votre poste */
/*
 * Le cadre du numero.
 *
 * L'image est posee EN ABSOLU, et non dans le flux.
 *
 * Elle etait d'abord une colonne de grille couvrant les trois lignes
 * (grid-row: 1 / -1). CSS grid distribue alors la hauteur d'un element qui
 * s'etend sur les lignes qu'il couvre : ses 62 px gonflaient la premiere, qui
 * ne contient qu'une etiquette de 12 px. Le cadre y perdait 50 px pour rien.
 *
 * En absolu, elle ne pese plus sur le calcul des hauteurs. La place qu'elle
 * occupe est reservee par le remplissage a droite -- sans quoi le champ du nom
 * passerait dessous.
 */
/*
 * SANS CADRE NI FOND (demande du 2026-09-13, apres essai de trois versions).
 * Le numero, en ambre et en grand, se suffit : l'encadrer n'ajoutait qu'une
 * boite de plus a une page qui en comptait neuf.
 *
 * On garde la bordure, TRANSPARENTE, plutot que de la retirer : la hauteur du
 * bloc ne bouge pas d'un pixel, et rien en dessous ne remonte.
 */
.own-line {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 13px 82px 13px 16px;
  /* Une CARTE D IDENTITE, la seule chose cadree de l accueil : le numero, le nom et
     l image forment un bloc, distinct des portes sans cadre qui suivent
     (ergonomie, lot 3, 2026-09-27). */
  background: var(--lacquer);
  border: 1px solid var(--edge);
  border-radius: 6px;
}

.own-number {
  font-family: var(--mono);
  font-size: 32px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: .22em;
  line-height: 1;
  color: var(--amber);
}

.own-line input {
  padding: 7px 10px 7px 0;
  font-size: 14px;
  background: transparent;
  border-color: transparent;
  border-bottom-color: var(--edge);
  border-radius: 0;
}

.own-line input:focus-visible { outline-offset: 2px; }

/* --------------------------------------------------------- ligne partagée */
.shared {
  border-top: 1px solid var(--edge);
  padding-top: 16px;
}

.shared summary {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--dim);
  cursor: pointer;
}

.shared summary:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; }

.shared > *:not(summary) { margin-top: 14px; }
.shared .field { gap: 7px; }
.shared .btn { width: 100%; }

/* Les cinq groupes de la page Reglages : un accordeon, le premier ouvert. Sept ecrans
   d'aides et de cases sur telephone (audit du 2026-09-27) ; on ouvre ce qu'on cherche. */
.prefs-groupe { padding-top: 12px; margin-top: 6px; }
.prefs-groupe > summary { display: flex; align-items: center; gap: 10px; list-style: none; }
.prefs-groupe > summary::-webkit-details-marker { display: none; }
.prefs-groupe > summary::before { content: '▸'; font-size: 12px; transition: transform .15s; }
.prefs-groupe[open] > summary::before { transform: rotate(90deg); }
.prefs-groupe > summary .prefs-section { margin: 0; padding-top: 0; border-top: none; }

/* Une aide longue derriere « En savoir plus » : le reglage se lit d'abord, l'explication
   vient a qui la demande. */
.aide { margin-top: 6px; }
.aide > summary {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--dim);
  cursor: pointer;
  list-style: none;
}
.aide > summary::-webkit-details-marker { display: none; }
.aide > summary::before { content: '+ '; }
.aide[open] > summary::before { content: '− '; }
.aide > summary:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; }
.aide > .hint { margin-top: 6px; }
/* La description d une case, sortie du libelle : elle s aligne sous le titre, colle a la case
   qu elle explique (ergonomie, 2026-09-27). */
.aide-case { margin: -8px 0 8px 32px; }
.pref-sous .aide-case { margin-left: 32px; }

/* ------------------------------------------------------------ appel entrant */
.panel-ring {
  align-items: center;
  gap: 14px;
  padding: 30px 16px;
  background: var(--lacquer);
  border: 1px solid var(--amber);
  border-radius: 3px;
  text-align: center;
}

.caller-number {
  font-family: var(--mono);
  font-size: 46px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: .18em;
  line-height: 1;
  color: var(--amber);
  animation: pulse 1.1s ease-in-out infinite;
}

.caller-name {
  font-size: 16px;
  color: var(--mist);
}

.panel-ring .controls { width: 100%; margin-top: 8px; }

.key-take {
  border-color: rgba(70, 201, 154, .5);
  color: var(--jade);
}

.key-take:active { background: var(--jade); color: var(--ink); }

/* ---------------------------------------------------------------- onglets */
/* ---------------------------------------------------------------- accueil */

/* Six portes en deux colonnes. Elles tiennent sur un ecran de telephone sans
   defiler, et surtout elles ne coutent plus rien aux autres pages : la barre
   d'onglets qu'elles remplacent se payait en haut de chacune. */
/*
 * Les boutons n'ont plus de marge en tete.
 *
 * Ils en avaient 18, qui s'ajoutaient aux 16 de l'ecartement du panneau : 34 px
 * avant les boutons, meme quand rien ne s'intercalait. L'avis de nouvelle
 * version, lui, disparait vraiment quand il n'y en a pas -- il est « hidden »
 * et ne pose aucun display qui l'en empecherait -- donc l'ecart se referme de
 * lui-meme, et s'ouvre juste ce qu'il faut quand l'avis parait.
 */
.accueil {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}

.carte {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  padding: 15px 13px 14px;
  /* Sans cadre ni fond — voir « .own-line ». Bordure transparente : meme
     taille qu'avant, et l'etat « armee » peut encore la colorer. */
  background: transparent;
  border: 1px solid transparent;
  border-radius: 3px;
  color: var(--mist);
  text-align: left;
  cursor: pointer;
  transition: background .12s;
}

/*
 * SANS CADRE, UNE TUILE NE RESSEMBLE PLUS A UN BOUTON. Elle doit donc REPONDRE
 * au doigt : le fond d'avant reparait pendant l'appui. Au survol aussi, mais
 * seulement la ou un survol existe — sur un ecran tactile, « :hover » reste
 * colle apres le toucher, et la tuile aurait l'air encore enfoncee.
 */
.carte:active { background: var(--lacquer); }
@media (hover: hover) {
  .carte:hover { background: var(--lacquer); }
}
.carte:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

/*
 * Le signe d une carte : un dessin, desormais, et non plus un caractere.
 *
 * « stroke: currentColor » et « fill: none » : l icone prend la couleur du
 * texte de sa carte, y compris quand celle-ci change d etat. Rien a peindre
 * deux fois, et une seule regle a tenir.
 *
 * « pointer-events: none » : la carte est un bouton, et le dessin ne doit pas
 * intercepter le doigt a sa place.
 */
/*
 * LES ICONES DE L'APPLICATION.
 *
 * Elles suivent la taille de texte de ce qui les porte — « 1em » —, donc une
 * touche d'appel a 30 px donne une icone de 30 px sans regle de plus. Elles
 * prennent aussi sa couleur : « currentColor » fait que le jade de
 * « décrocher » et le corail de « raccrocher » se posent tout seuls.
 *
 * Elles remplacent des caracteres — ☎ 📹 🎤 — que la police du systeme
 * dessinait, et donc autrement sur chaque appareil : en couleur sur Windows,
 * au trait ailleurs, absents parfois.
 */
.icone {
  display: inline-block;
  vertical-align: -.15em;
  width: 1em;
  height: 1em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  pointer-events: none;
}

/* Un bouton de barre ecrit en 10 px : son icone ne peut pas suivre le texte
   sans devenir illisible. */
.btn-mini .icone { width: 17px; height: 17px; vertical-align: -.3em; }

/* Ce qui doit etre PLEIN dans une icone au trait : les trois points d'un
   menu, les yeux d'un visage. Sans cela, « fill: none » en ferait des
   anneaux. */
.icone .plein { fill: currentColor; stroke: none; }

/* Une icone accolee a un mot — « Photo », « Administration » — respire a sa
   droite, sinon les deux se touchent. */
.btn-mini .icone, .btn .icone { margin-right: 6px; }

/*
 * ET UNE ICONE SEULE NE RESPIRE PAS : elle se centrerait de travers dans son
 * bouton. La regle du dessus vise le cas frequent — un signe puis un mot ; ces
 * boutons-la n'ont pas de mot.
 */
.fil-fleche .icone,
#gerer-retour .icone,
.fiche-geste .icone { margin-right: 0; }

/* Le meme air, pour les signes qui precedent une valeur et non un mot. */
.book-tel .icone, .marque .icone { margin-right: 5px; }
/* Un signe devant un nom de fichier, de salon, ou dans une ligne d'etat. */
.apercu .icone, .choix-dessous .icone, .jointe-nom .icone,
.book-etat .icone, #chat-nom .icone { margin-right: 4px; }

.auteur-admin .icone { width: 12px; height: 12px; margin-right: 4px; vertical-align: -.1em; }
.book-tel .icone { width: 13px; height: 13px; }
.num .icone { width: 14px; height: 14px; vertical-align: -.15em; }

/* Grande, l'icone n'a plus besoin d'un trait aussi epais pour se tenir. */
.key-icone .key-glyph .icone { stroke-width: 1.5; }

.carte-signe {
  width: 26px;
  height: 26px;
  display: block;
  color: var(--jade);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  pointer-events: none;
}

/* Les cartes que l'administration accorde poste par poste : le signe prend
   l'ambre, pour dire qu'elles ne sont pas la pour tout le monde. */
.carte-reglee .carte-signe { color: var(--amber); }

/* Armee : la carte prend la couleur de ce qu'elle va faire. Une annonce part
   vers tout le monde -- on ne la lance pas par megarde, et l'on doit voir
   qu'on est a un doigt de parler. */
/* ELLE GARDE SON CADRE ET SON FOND : c'est une securite, pas un ornement. Au
   premier appui l'annonce s'arme ; le second parle a TOUS les postes. Deux
   classes battent une seule : cette regle l'emporte sur la tuile sans cadre,
   au survol et a l'appui compris. */
.carte.armee,
.carte.armee:active,
.carte.armee:hover {
  border-color: var(--coral);
  background: rgba(225, 88, 75, .12);
}

.carte.armee .carte-signe,
.carte.armee .carte-nom { color: var(--coral); }

.carte-nom {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .15em;
  text-transform: uppercase;
}

/* La note dit ce qu'on trouve derriere la porte : le seul mot du titre ne
   suffit pas a distinguer « Messages » de « Salons ». */
.carte-note {
  font-size: 12px;
  line-height: 1.35;
  color: var(--dim);
}

/* La pastille se pose dans le coin, comme sur l'onglet qu'elle remplace. */
.carte .badge {
  position: absolute;
  top: 9px;
  right: 9px;
}

/* Le retour occupe l'emplacement du nom : meme famille, meme graisse, pour
   que l'en-tete ne bouge pas d'une page a l'autre. */
.mark-retour {
  padding: 0;
  background: none;
  border: 0;
  color: var(--amber);
  cursor: pointer;
}

.mark-retour:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; }

/* ------------------------------------------- en-tete d'une conversation */

/*
 * MEME MISE EN PAGE QU'UNE LIGNE DE LA LISTE DES CONVERSATIONS.
 *
 * Le visage, puis deux lignes a cote — le nom, et dessous ce qu'on sait de la
 * personne. On passe de la liste au fil sans avoir a relire : c'est le meme
 * objet, presente pareil.
 *
 * Les actions sont groupees a leur propre bloc, a droite. Melangees au texte,
 * un nom un peu long les poussait hors de l'ecran.
 */
.fil-tete-chat {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 10px;
  /* La conversation commence sans qu on lui trace une frontiere : le trait
     est pose par « .fil-tete:not(.fil-tete-chat) », qui l epargne ici. */
  padding-bottom: 8px;
}

/* Les deux lignes. « min-width: 0 » est indispensable : sans lui un enfant
   flex refuse de retrecir sous la largeur de son texte, et le nom pousserait
   les boutons dehors au lieu de se couper. */
.fil-tete-chat .fil-txt {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.fil-tete-chat .fil-l1,
.fil-tete-chat .fil-l2 {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.fil-tete-chat .fil-nom {
  flex: 0 1 auto;
  min-width: 0;
  font-size: 15px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* La seconde ligne : l'etat, ou « ecrit… ». Elle ne crie pas — c'est un
   renseignement, pas un titre. */
.fil-tete-chat .fil-etat {
  flex: 0 1 auto;
  min-width: 0;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Quelqu'un est en train d'ecrire : cela ne dure que quelques secondes, et
   doit se remarquer sans faire sursauter. */
.fil-tete-chat .fil-etat.ecrit { color: var(--jade); }

/* Le sujet du salon, sur la meme seconde ligne que l'etat. */
.fil-tete-chat .fil-sujet {
  flex: 0 1 auto;
  min-width: 0;
  font-size: 12px;
  color: var(--dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Le point ne parait qu'accompagne : sans etat a sa droite, il ne dirait rien.
   « :not(:empty) » sur le voisin serait plus juste mais ne s'ecrit pas en CSS
   dans ce sens ; on s'en remet donc au script, qui le cache. */
.fil-tete-chat .dot { flex: none; }

/* Les trois actions, ensemble et a droite. */
.fil-tete-actions {
  flex: none;
  display: flex;
  align-items: center;
  gap: 2px;
}

.fil-tete-chat .btn-mini { flex: none; }

/* La fleche est un geste, pas un mot : elle se resserre. */
.fil-fleche {
  padding: 6px 10px;
  font-size: 16px;
  line-height: 1;
}

/* Appeler est l'action principale ; Effacer ne doit pas lui disputer l'oeil,
   et moins encore attirer le pouce -- c'est la seule des deux qui detruise. */
.fil-appeler {
  border-color: rgba(70, 201, 154, .45);
  color: var(--jade);
}

.fil-appeler:disabled { border-color: var(--edge); color: var(--dim); }

/*
 * En haut d une conversation, on ne lit pas : on vise.
 *
 * Les trois boutons perdent leur cadre et leur mot pour ne garder qu un
 * symbole. Leur nom reste dans « aria-label » — ce qui disparait est le
 * cadre, pas l information. La zone touchable ne retrecit pas : le
 * remplissage remplace la largeur que prenait le texte.
 */
.fil-tete-chat .btn-mini {
  border: none;
  background: none;
  padding: 6px 8px;
  font-size: 17px;
  line-height: 1;
}

.fil-tete-chat .btn-mini:not(:disabled):active { background: none; opacity: .55; }
.fil-tete-chat .fil-fleche { font-size: 22px; padding: 4px 6px; }
.fil-tete-chat .fil-appeler { color: var(--jade); }
.fil-tete-chat .fil-appeler:disabled { color: var(--dim); }

/* Le numero du correspondant, a cote de la sortie. Chiffres a chasse fixe :
   c'est un numero qu'on lit et qu'on recopie, pas un mot. */
.bar-num {
  font-family: var(--mono);
  font-size: 15px;
  font-variant-numeric: tabular-nums;
  letter-spacing: .12em;
  color: var(--amber);
}

/* ------------------------------------------------------------ appel rapide */

.raccourcis-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 4px;
}

/* Une grille qui se remplit d'elle-meme : trois colonnes sur un telephone,
   davantage des que la largeur le permet, sans palier a ecrire. */
.raccourcis {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(94px, 1fr));
  gap: 8px;
}

.raccourci {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  padding: 9px 10px;
  background: var(--lacquer);
  border: 1px solid var(--edge);
  border-radius: 3px;
  color: var(--mist);
  text-align: left;
  cursor: pointer;
}

.raccourci:hover:not(:disabled) { border-color: var(--jade); }
.raccourci:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

/* Injoignable : le bouton reste lisible mais se retire du jeu. L'effacer
   ferait disparaitre un poste de la grille d'un appel a l'autre, et l'on
   chercherait un raccourci qui a bouge. */
.raccourci:disabled { opacity: .4; cursor: default; }

.raccourci .dot { margin-bottom: 1px; }

.raccourci-num {
  font-family: var(--mono);
  font-size: 16px;
  font-variant-numeric: tabular-nums;
  letter-spacing: .08em;
  color: var(--amber);
  font-weight: 500;
}

/* Le nom se coupe plutot que d'elargir la colonne : la grille doit rester
   reguliere, c'est ce qui rend les raccourcis repérables du pouce. */
.raccourci-nom {
  align-self: stretch;
  font-size: 11px;
  color: var(--dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --------------------------------------------------------------- annuaire */
.book {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.book-row {
  display: flex;
  /* Centre sur l'avatar : les deux etages s'equilibrent de part et d'autre de
     son milieu, comme dans la liste des messages. */
  align-items: center;
  gap: 12px;
  padding: 9px 8px;
  /* Plus de carte encadree par poste : trente cadres empiles font une grille,
     alors qu'un annuaire est une liste de gens. L'espacement suffit. */
  background: none;
  border: none;
  border-radius: 10px;
}

/* Sa propre ligne se marque par une teinte, le cadre qui la disait n'existant
   plus. Assez faible pour ne pas se lire comme une alerte. */
.book-moi { background: rgba(233, 179, 65, .08); }

/* Pastille de presence : c'est elle qu'on lit en premier, avant le nom. */
.dot {
  flex: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--edge);
}

.dot[data-etat="libre"]  { background: var(--jade); }

.dot[data-etat="occupe"] { background: var(--amber); }

/* Pendant que l autre tape : la pastille bat doucement. Le mot dit deja
   ce qui se passe ; le battement le rend visible du coin de l oeil. */
.dot[data-etat="ecrit"] {
  background: var(--amber);
  animation: bat 1.1s ease-in-out infinite;
}

@keyframes bat {
  0%, 100% { opacity: 1; }
  50%      { opacity: .35; }
}

/* Une animation qui clignote n est pas pour tout le monde. */
@media (prefers-reduced-motion: reduce) {
  .dot[data-etat="ecrit"] { animation: none; }
}

/* ------------------------------------------------------- pieces jointes */

/* Les deux boutons de la saisie tiennent ensemble : le trombone est une action
   de second rang, il ne doit pas concurrencer « Envoyer ». */
.fil-boutons {
  display: flex;
  align-items: flex-end;
  gap: 6px;
}

#chat-joindre, #chat-photo {
  font-size: 15px;
  line-height: 1;
  padding: 9px 10px;
}

.fil-jointe {
  margin-top: 6px;
  color: var(--amber);
}

.jointe { display: block; }

/* UNE POSITION PARTAGEE : ce qu'on lit d'abord (le repere, la precision), puis
   les deux gestes utiles. Pas de carte dessinee : la charger demanderait
   d'appeler un service de cartes a chaque ouverture du fil. */
.position-carte {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 12px;
  border: 1px solid var(--edge);
  border-radius: 10px;
  background: rgba(70, 201, 154, .06);
}
.position-tete { display: flex; align-items: center; gap: 6px; font-weight: 600; color: var(--jade); }
.position-tete .icone { width: 18px; height: 18px; }
.position-coords { font-family: var(--mono); font-size: 12.5px; color: var(--mist); }
.position-precision { font-size: 12px; color: var(--dim); }
.position-gestes { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 2px; }
.position-gestes a { text-decoration: none; }

/* Une image se regarde dans la conversation. Bornee en hauteur : une photo de
   telephone remplirait l'ecran a elle seule. */
.jointe-image {
  display: block;
  max-width: 100%;
  max-height: 260px;
  width: auto;
  border-radius: 4px;
  border: 1px solid var(--edge);
  background: var(--ink);
}

.bulle .jointe + * { margin-top: 8px; }

/* Ce qui ne se regarde pas se nomme, et se telecharge. */
.jointe-nom {
  display: inline-block;
  font-family: var(--mono);
  font-size: 13px;
  color: var(--mist);
  text-decoration: none;
  padding: 8px 10px;
  border-radius: 4px;
  border: 1px solid var(--edge);
  background: var(--ink);
}

a.jointe-nom:hover, a.jointe-nom:focus-visible { border-color: var(--amber); }

/* Une version plus recente attend : assez visible pour qu'on la voie, assez
   discrete pour ne pas couper l'usage. */
.maj-appli {
  margin-top: 6px;
  padding: 9px 12px;
  border-radius: 4px;
  border: 1px solid var(--amber);
  border: 1px solid color-mix(in srgb, var(--amber) 40%, transparent);
  background: var(--lacquer);
  background: color-mix(in srgb, var(--amber) 10%, var(--lacquer));
  color: var(--mist);
}

.maj-appli a { color: var(--amber); }

/* ------------------------------------------------- teintes des bulles */

/* Une seule gamme, deux usages : la bulle prend sa couleur du panneau, qui
   porte le choix. Le texte clair reste lisible sur les six. */
#panel-chat {
  --b-jade:    #1B4A42;
  --b-teal:    #1D3C3E;
  --b-ardoise: #2A3340;
  --b-indigo:  #262C50;
  --b-prune:   #3A2540;
  --b-terre:   #3B2C22;
}

.bulle { background: var(--teinte-autre, #1D3C3E); }
.bulle.moi { background: var(--teinte-moi, #1B4A42); }

#panel-chat[data-moi="jade"]    { --teinte-moi: var(--b-jade); }
#panel-chat[data-moi="teal"]    { --teinte-moi: var(--b-teal); }
#panel-chat[data-moi="ardoise"] { --teinte-moi: var(--b-ardoise); }
#panel-chat[data-moi="indigo"]  { --teinte-moi: var(--b-indigo); }
#panel-chat[data-moi="prune"]   { --teinte-moi: var(--b-prune); }
#panel-chat[data-moi="terre"]   { --teinte-moi: var(--b-terre); }

#panel-chat[data-autre="jade"]    { --teinte-autre: var(--b-jade); }
#panel-chat[data-autre="teal"]    { --teinte-autre: var(--b-teal); }
#panel-chat[data-autre="ardoise"] { --teinte-autre: var(--b-ardoise); }
#panel-chat[data-autre="indigo"]  { --teinte-autre: var(--b-indigo); }
#panel-chat[data-autre="prune"]   { --teinte-autre: var(--b-prune); }
#panel-chat[data-autre="terre"]   { --teinte-autre: var(--b-terre); }

.teintes [data-teinte="jade"]    { background: #1B4A42; }
.teintes [data-teinte="teal"]    { background: #1D3C3E; }
.teintes [data-teinte="ardoise"] { background: #2A3340; }
.teintes [data-teinte="indigo"]  { background: #262C50; }
.teintes [data-teinte="prune"]   { background: #3A2540; }
.teintes [data-teinte="terre"]   { background: #3B2C22; }

.teintes button[aria-pressed="true"] { border-color: var(--amber); }
.teintes button:disabled { opacity: .28; cursor: default; }

/* ------------------------------------------------------- fond du fil */

/* Le fond se pose sur le corps du fil, et sur lui seul : l'en-tete et la
   saisie gardent celui de l'application, ce qui les detache du fil. */
#chat-corps { background: var(--fond-fil, transparent); }

#panel-chat[data-fond="ardoise"] { --fond-fil: transparent; }
#panel-chat[data-fond="encre"]   { --fond-fil: #0A0D10; }
#panel-chat[data-fond="foret"]   { --fond-fil: #0D1E16; }
#panel-chat[data-fond="abysse"]  { --fond-fil: #0A1522; }
#panel-chat[data-fond="prune"]   { --fond-fil: #16101B; }
#panel-chat[data-fond="brique"]  { --fond-fil: #1A1210; }

#chat-corps {
  border-radius: 8px;
  padding-left: 8px;
  padding-right: 8px;
}

.fonds {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 8px 0 4px;
}

.fonds button {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 2px solid var(--edge);
  cursor: pointer;
  padding: 0;
}

.fonds button[aria-pressed="true"] { border-color: var(--amber); }
.fonds button:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

.fonds [data-fond="ardoise"] { background: var(--ink); }
.fonds [data-fond="encre"]   { background: #0A0D10; }
.fonds [data-fond="foret"]   { background: #0D1E16; }
.fonds [data-fond="abysse"]  { background: #0A1522; }
.fonds [data-fond="prune"]   { background: #16101B; }
.fonds [data-fond="brique"]  { background: #1A1210; }

/* ------------------------------------------------------------- salons */

.fils-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Un rang de l'annuaire des salons : le nom, ce dont on y parle, qui le tient,
   et ce qu'on peut en faire. Quatre lignes plutot qu'une, parce qu'un salon se
   choisit sur son sujet, pas sur son nom seul. */
.salon-rang {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 12px 0;
  border-bottom: 1px solid var(--edge);
}

.salon-tete {
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.salon-tete strong { font-size: 1.05rem; }

.salon-rang .apercu {
  color: var(--mist);
  opacity: .85;
}

.salon-rang .book-actions {
  display: flex;
  gap: 8px;
  margin-top: 4px;
}

/* Une demande en attente n'est pas une action : elle se lit, elle ne se
   clique pas. */
.salon-attente {
  align-self: center;
  font-style: italic;
}

/* Refuser est le geste qu'on ne veut pas faire par megarde. */
.btn-mini.refus {
  color: var(--coral);
  border-color: var(--coral);
  border-color: color-mix(in srgb, var(--coral) 40%, transparent);
}

/* Le formulaire de creation : encadre, pour qu'on voie qu'il s'agit d'une
   parenthese dans la liste et non d'un ecran de plus. */
.salon-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  margin-bottom: 12px;
  border: 1px solid var(--edge);
  border-radius: 6px;
  background: var(--lacquer);
}

.salon-form input {
  width: 100%;
  font: inherit;
  color: var(--mist);
  background: var(--ink);
  border: 1px solid var(--edge);
  border-radius: 4px;
  padding: 10px 12px;
}

.salon-form input:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: -1px;
}

.salon-form-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}

/* La pastille se lit a cote du nom ; l'etat en toutes lettres suit le numero,
   pour qui ne distingue pas les couleurs. */
.fil-ligne {
  display: flex;
  align-items: center;
  gap: 7px;
}

.book-num {
  font-family: var(--mono);
  font-size: 17px;
  font-variant-numeric: tabular-nums;
  letter-spacing: .1em;
  color: var(--amber);
  font-weight: 500;
}

.book-nom {
  flex: 1;
  min-width: 0;
  font-size: 14px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.book-etat {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--dim);
}

/*
 * « en ligne » revient dans l'en-tete de la conversation, sur telephone aussi.
 *
 * Il en avait ete retire faute de place : il disputait la ligne du nom, et
 * l'on preferait le nom. L'en-tete a maintenant DEUX lignes, comme la liste
 * des conversations, et cette seconde ligne est faite pour lui — c'est aussi
 * elle qui dit « ecrit… ». La question de la place ne se pose plus.
 */
#chat-etat { display: inline; }

.btn-mini {
  flex: none;
  padding: 7px 11px;
  background: transparent;
  border: 1px solid var(--edge);
  border-radius: 3px;
  color: var(--mist);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  cursor: pointer;
}

.btn-mini:disabled { opacity: .3; cursor: not-allowed; }
.btn-mini:not(:disabled):active { background: var(--edge); }
.btn-mini:focus-visible { outline: 2px solid var(--mist); outline-offset: 2px; }


.btn-mini-danger { border-color: rgba(225, 88, 75, .45); color: #F0968D; }
.btn-mini-danger:not(:disabled):active { background: var(--coral); color: var(--ink); }

/* Droit accorde ou refuse : la teinte porte l'etat, la coche le confirme pour
   qui ne distingue pas les couleurs. */
/* Le numero de version de l'application installee. Discret, mais lisible :
   une mention qu'on ne peut pas lire ne renseigne personne. */
/* Son espace lui appartient : quand il n'y a rien a dire, il n'y a rien a
   ecarter non plus. */
/* A cote du nom : plus petite, plus pale, et sans les majuscules espacees du
   nom — « V4.6 » en capitales ecartees se lirait comme un sigle. */
.mark-version {
  margin-left: 10px;
  font-size: 11px;
  letter-spacing: .04em;
  text-transform: none;
  color: var(--dim);
}

/* ---------------------------------------------------------------- bureau
   Le mode etroit reste la reference : tout ce qui suit ne s'applique qu'a
   partir d'un ecran large, et seulement si l'utilisateur l'a accepte. */

/* Sur telephone, le duo est la colonne qui porte le fil : il doit s etirer,
   sinon « flex: 1 » sur le panneau ne s applique a rien et la saisie reste
   collee sous le dernier message. */
.duo {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

.duo[hidden] { display: none; }

body.bureau .phone { max-width: 1180px; }

/* Le cadre y fait 1180 px : deux cartes seraient des pancartes de 580 px.
   Trois colonnes, et la grille garde des proportions de bouton. */
body.bureau .accueil { grid-template-columns: repeat(3, 1fr); }

body.bureau .duo {
  display: grid;
  grid-template-columns: minmax(280px, 360px) 1fr;
  gap: 22px;
  align-items: start;
  /* Cote a cote, on ne s etire pas : la liste et le fil gardent la hauteur de
     leur contenu, comme avant. */
  flex: 0 1 auto;
}

/* Cote a cote, le fil n'a plus besoin de bouton retour ni d'occuper la
   hauteur d'un ecran entier : la liste reste visible a cote. */
body.bureau #chat-retour { display: none; }

/* Un salon occupe toute la largeur : la liste des conversations PRIVEES n'a
   rien a faire a cote de lui, et la colonne qu'elle laisse ne doit pas rester
   vide. */
body.bureau .duo[data-seul] { grid-template-columns: 1fr; }

/* Et le retour reparait : sans la liste a cote, il n'y a plus d'autre chemin
   vers l'annuaire des salons. */
body.bureau .duo[data-seul] #chat-retour { display: inline-block; }
body.bureau .panel-fil { min-height: 0; }
body.bureau .fil-corps { max-height: 58vh; }

/* Les listes profitent de la largeur, sans que le texte s'etire jusqu'a
   devenir penible a lire. */
body.bureau .book, body.bureau .fils { max-width: none; }
body.bureau .bulle { max-width: 62%; }

.fil-invite { margin: 24px 0; text-align: center; }

/* ---------------------------------------------------------------- messages
   Liste des fils, puis le fil lui-meme. */

.fils { list-style: none; margin: 0; padding: 0; }

.fil-ligne {
  display: flex;
  /* Centre sur la hauteur de l'avatar : les deux lignes de texte s'equilibrent
     de part et d'autre de son milieu. « baseline » alignait le nom sur le haut
     de la pastille et laissait l'apercu pendre en dessous. */
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 9px 6px;
  background: none;
  /* Plus de trait entre les conversations : l'espacement dit deja ou l'une
     finit et ou la suivante commence, et un filet par ligne fabriquait une
     grille la ou il n'y a qu'une liste de gens. */
  border: none;
  border-radius: 10px;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: inherit;
}

.fil-ligne:focus-visible { outline: 2px solid var(--amber); outline-offset: -2px; }

/* Le salon se distingue des fils a deux : c'est un lieu, pas un correspondant. */
.fil-ligne.salon .num { color: var(--jade); letter-spacing: .06em; }

/* Dans un salon, chaque message dit qui parle : sans cela on ne suit plus. */
.bulle .auteur {
  display: block;
  margin-bottom: 3px;
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--amber);
}

/* Cote a cote, on doit voir quelle conversation est ouverte a droite. */
body.bureau .fil-ligne[aria-current="true"] {
  background: var(--lacquer);
  box-shadow: inset 3px 0 0 var(--amber);
}
.fil-ligne:active { background: var(--lacquer); }

/* L'avatar mene la ligne : c'est sa hauteur qui donne celle des deux etages. */
.fil-ligne .avatar { width: 40px; height: 40px; font-size: 13px; }

/* La colonne du milieu prend toute la place qui reste, et la rend en ellipses
   plutot qu'en debordant. « min-width: 0 » est indispensable : sans lui un
   enfant flex refuse de retrecir sous la largeur de son texte. */
.fil-txt {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.fil-l1 {
  display: flex;
  align-items: baseline;
  gap: 7px;
  min-width: 0;
}

.fil-ligne .qui {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: .96rem;
  font-weight: 600;
}

/* Le numero accompagne le nom sans lui disputer l'oeil : c'est un identifiant,
   pas un titre. */
.fil-ligne .num {
  flex: none;
  font-family: var(--mono);
  font-size: .7rem;
  letter-spacing: .08em;
  color: var(--amber);
  opacity: .8;
}

.fil-ligne .apercu {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--dim);
  font-size: .82rem;
}

/* L'heure et le compte, en face, alignes a droite et empiles. */
.fil-cote {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 5px;
  /* Les heures se lisent en colonne : elles doivent s'aligner d'une ligne a
     l'autre, et non danser au gre de la largeur des chiffres. */
  font-variant-numeric: tabular-nums;
}

.fil-ligne .quand { font-size: .72rem; color: var(--dim); white-space: nowrap; }

.fil-ligne .badge {
  flex: none;
  min-width: 20px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--jade);
  color: var(--ink);
  font-size: .72rem;
  font-weight: 700;
  text-align: center;
}

/* Le fil occupe la hauteur : la saisie reste en bas, le corps defile. */
.panel-fil { display: flex; flex-direction: column; min-height: 60vh; }
.panel-fil[hidden] { display: none; }

.fils-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

/* Effacer est irreversible pour celui qui le fait : le bouton ne doit pas
   attirer l'oeil comme une action ordinaire, mais rester trouvable. */
#chat-vider, #chats-vider {
  color: var(--coral);
  border-color: var(--coral);
  border-color: color-mix(in srgb, var(--coral) 40%, transparent);
}

/* Sans bandeau au centre, c'est la bulle qui doit dire l'urgence. */
.bulle.urgente {
  border-left: 3px solid var(--coral);
  background: var(--lacquer);
  background: color-mix(in srgb, var(--coral) 12%, var(--lacquer));
}

.fil-tete {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: 10px;
}

/* Les autres ecrans gardent leur trait ; la conversation, non. On le pose ici
   plutot que de le retirer plus haut : « .fil-tete-chat » est declaree AVANT
   « .fil-tete » et pese autant, donc elle ne pourrait pas la contredire. */
.fil-tete:not(.fil-tete-chat):not(.fil-tete-choix) { border-bottom: 1px solid var(--edge); }

/*
 * La barre de choix, en haut, a la place de l en-tete.
 *
 * Elle en reprend exactement les mesures : le fil ne doit pas sauter d un
 * pixel quand l une remplace l autre. Ses boutons sont ceux de l en-tete —
 * sans cadre, un symbole seul, une zone touchable large — pour qu on ne les
 * decouvre pas comme des objets nouveaux.
 */
/*
 * EXACTEMENT LA HAUTEUR DE L'EN-TETE QU'ELLE REMPLACE.
 *
 * Sans cela le fil remonte au moment ou l'on choisit, et redescend en sortant.
 * Un saut n'a pas de sens quand rien n'a change de place.
 *
 * La mesure est ecrite UNE SEULE FOIS, dans « --h-tete » : l'en-tete est passe
 * a deux lignes et l'ecart est revenu, parce que la hauteur etait ecrite a
 * deux endroits et que l'un des deux avait ete oublie.
 */
.fil-tete-chat,
.fil-tete-choix { min-height: var(--h-tete); }

.fil-tete-choix {
  gap: 7px;
  padding-bottom: 8px;
}

.fil-tete-choix .btn-mini {
  border: none;
  background: none;
  padding: 6px 8px;
  font-size: 17px;
  line-height: 1;
  flex: none;
}

.fil-tete-choix .btn-mini:not(:disabled):active { background: none; opacity: .55; }
.fil-tete-choix .fil-fleche { font-size: 19px; padding: 4px 6px; }

/* Le geste qu on ne defait pas se signale, ici comme dans le menu. */
.sel-danger { color: #F0968D; }

.fil-qui { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.fil-qui strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fil-num { font-family: var(--mono); font-size: .8rem; letter-spacing: .12em; color: var(--dim); }

.fil-plus { align-self: center; margin: 10px 0 0; }

.fil-corps {
  flex: 1;
  overflow-y: auto;
  /* Un plancher, et pas seulement un plafond : sans lui le fil ne fait que la
     hauteur de ses messages, et l ecran reste vide en dessous — c est ce qui
     annulait la place gagnee en escamotant les onglets. */
  /* Releve de 55 a 62vh le jour ou l'en-tete est passe de deux etages a un :
     les 48 px rendus allaient a la marge du bas, pas a la conversation. Le
     plafond reste a 68vh -- sur telephone, vh compte le viewport large, celui
     qu'on a barre d'adresse repliee, et coller au plafond ferait deborder la
     page des qu'elle se deploie. */
  /* Plus de hauteur en « vh » : le fil prend simplement ce qui reste entre
     l en-tete et la saisie, laquelle se retrouve ainsi tout en bas quelle que
     soit la taille de l ecran. « min-height: 0 » est indispensable — sans lui
     un enfant flex refuse de retrecir sous la taille de son contenu, et le
     fil deborderait au lieu de defiler. */
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  /* Serre : deux messages d'un meme auteur se touchent presque, et c'est la
     marge de « .bulle:not(.suite) » qui separe les prises de parole. */
  gap: 2px;
  /* De quoi loger la queue des bulles sans que le fil se mette a glisser
     lateralement : elle deborde de 7 px, la marge en fait 10. */
  padding: 12px 10px;
}

/* Une bulle epouse son contenu : « Ok » ne doit pas occuper les trois quarts de
   l'ecran. Sans « align-self », un enfant de colonne flex s'etire — c'est ce qui
   donnait a toutes les bulles recues la meme largeur, quelle que soit leur
   longueur. */
.bulle {
  align-self: flex-start;
  max-width: 78%;
  padding: 7px 11px 6px;
  /* La queue se pose en haut, du cote de celui qui parle ; le coin qui la
     porte se carre pour l accueillir. Les trois autres restent arrondis. */
  position: relative;
  border-radius: 4px 14px 14px 14px;
  white-space: normal;
  line-height: 1.45;
  font-size: .95rem;

  /* L'heure vient se ranger a la suite du texte quand la place le permet, et
     passe a la ligne sinon. C'est ce qui donne aux messages courts leur allure
     ramassee. */
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  column-gap: 8px;
  row-gap: 1px;
}

.bulle .dit {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  min-width: 0;
}

/* Les siens a droite, ceux d'en face a gauche : on lit le sens sans legende. */
.bulle.moi {
  align-self: flex-end;
  border-radius: 14px 4px 14px 14px;
}

/*
 * La queue.
 *
 * Un triangle decoupe, pose au coin haut du cote de celui qui parle. Il prend
 * « background: inherit » : quelle que soit la teinte choisie dans les
 * reglages — et meme le melange d une bulle urgente — il suit sans qu on ait
 * a la nommer une seconde fois.
 *
 * Seule la premiere bulle d un groupe la porte : une queue par prise de
 * parole, comme dans une bande dessinee. Les suivantes s arrondissent.
 */
.bulle:not(.suite)::after {
  content: '';
  position: absolute;
  top: 0;
  left: -7px;
  width: 9px;
  height: 13px;
  background: inherit;
  clip-path: polygon(100% 0, 100% 100%, 0 0);
}

.bulle.moi:not(.suite)::after {
  left: auto;
  right: -7px;
  clip-path: polygon(0 0, 0 100%, 100% 0);
}

/* Deux messages d'affilee du meme auteur : le coin se rearrondit, le groupe se
   lit comme une seule prise de parole. */
.bulle.suite { border-top-left-radius: 14px; }
.bulle.moi.suite { border-top-right-radius: 14px; }

/* La marge separe les prises de parole, pas les messages : c'est elle qui fait
   respirer le fil, tandis que le « gap » serre l'interieur d'un groupe. */
.bulle:not(.suite) { margin-top: 8px; }
.fil-corps > *:first-child { margin-top: 0; }

/*
 * La date, glissee entre deux messages.
 *
 * Elle ne parait qu au changement de jour : dans une conversation d une seule
 * journee, on ne la voit qu une fois, tout en haut.
 */
.jour {
  align-self: center;
  margin: 16px 0 4px;
  padding: 3px 11px;
  border-radius: 11px;
  background: var(--edge);
  background: color-mix(in srgb, var(--edge) 60%, transparent);
  color: var(--mist);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  white-space: nowrap;
}

.bulle .pied {
  margin-left: auto;
  font-size: .68rem;
  line-height: 1;
  padding-bottom: 1px;
  /* Le fond des bulles ayant ete eclairci, « --dim » tombait a 3,8 de contraste
     — sous le seuil de lisibilite d un petit texte. */
  color: rgba(214, 228, 225, .68);
  white-space: nowrap;
  flex: none;
}
/* Les deux echelons ne se valent pas, et l'oeil doit le lire sans compter les
   traits : « arrive » est une information de service, en gris ; « lu » est ce
   qu'on attendait, en vert. */
.bulle.moi .pied .lu { color: var(--jade); }
.bulle.moi .pied .remis { color: rgba(214, 228, 225, .55); }

/* Ce qui doit occuper toute la largeur de la bulle, et non se ranger en ligne. */
.bulle .auteur, .bulle .jointe { flex: 1 0 100%; }

/* Dans un groupe, le nom ne se dit qu une fois : le repeter a chaque bulle
   alourdit sans rien apprendre. */
.bulle.suite .auteur { display: none; }

.fil-saisie {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  padding-top: 8px;
}

/* Le champ et ses icones ne forment qu'un seul objet : c'est ce qui rend au
   texte la largeur que trois boutons alignes lui prenaient. */
.fil-pilule {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: flex-end;
  gap: 0;
  /* Le champ porte le fond choisi pour la conversation : la zone d ecriture
     et celle de lecture ne font qu une. « ardoise » vaut transparent, et
     laisse alors paraitre le fond de la page — comme le fil lui-meme. */
  background: var(--fond-fil, var(--ink));
  border: 1px solid var(--edge);
  border-radius: 22px;
  padding: 0 4px 0 14px;
}

.fil-pilule:focus-within { border-color: var(--amber); }

.fil-saisie textarea {
  flex: 1;
  min-width: 0;
  /* Une seule ligne au repos, qui grandit avec le texte jusqu'a une borne :
     deux lignes reservees d'avance coutaient de la hauteur pour rien. */
  resize: none;
  height: 40px;
  max-height: 120px;
  overflow-y: auto;
  font: inherit;
  font-size: .95rem;
  line-height: 1.45;
  color: var(--mist);
  background: none;
  border: none;
  padding: 10px 0;
}

.fil-saisie textarea:focus-visible { outline: none; }

.fil-icone {
  flex: none;
  background: none;
  border: none;
  color: var(--mist);
  font-size: 16px;
  line-height: 1;
  padding: 11px 7px;
  cursor: pointer;
  border-radius: 50%;
}

.fil-icone:hover:not(:disabled) { background: rgba(214, 228, 225, .08); }
.fil-icone:disabled { opacity: .4; cursor: default; }
.fil-icone:focus-visible { outline: 2px solid var(--amber); outline-offset: -2px; }

/* Rond, plein, seul a droite : on sait ou l'on appuie sans lire. */
.fil-envoi {
  flex: none;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  border: none;
  background: var(--jade);
  color: var(--ink);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}

.fil-envoi:disabled { opacity: .45; cursor: default; }
.fil-envoi:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

.fil-aide { margin: 6px 0 0; }

/* Sur un ecran tactile, le clavier a sa propre touche d'envoi : le rappel de
   Ctrl+Entree n'y apprend rien et coutait une ligne entiere. */
@media (pointer: coarse) {
  .fil-aide { display: none; }
}

/* Etat des trois maillons d'une notification. */
.diagnostic {
  list-style: none;
  margin: 6px 0 16px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: .85rem;
}

.diagnostic li {
  display: flex;
  align-items: baseline;
  gap: 8px;
  color: var(--dim);
}

.diagnostic .marque { flex: none; font-size: .9em; }

/* Le bouton de reparation tient dans la ligne du maillon qu'il repare : on lit
   la croix et l'on agit a cote, sans chercher a quoi s'applique un bouton pose
   plus bas. */
.diagnostic .btn-mince {
  flex: none;
  margin-left: auto;
  padding: 3px 10px;
  font-size: .8rem;
  white-space: nowrap;
}
.diagnostic .bon .marque { color: var(--jade); }
.diagnostic .mauvais .marque { color: var(--coral); }
.diagnostic .bon { color: var(--mist); }

/* Une preference : la case, puis le libelle et son explication. */
/*
 * UN CODE QU'ON RECOPIE A LA MAIN.
 *
 * Le code de cession d'une ligne se lit sur un ecran et se tape sur un autre :
 * gros, espace, et en chiffres monospaces pour qu'aucun caractere ne se
 * confonde avec un autre. Le meme style existait deja dans la console, ou
 * l'administration fabrique le meme genre de code ; il manquait ici, et le
 * code s'affichait en texte ordinaire.
 */
.code-rattachement {
  margin: 8px 0;
  padding: 12px 14px;
  border: 1px dashed var(--amber);
  border-radius: 8px;
  background: rgba(233, 179, 65, .07);
  color: var(--amber);
  font-family: var(--mono);
  font-size: 1.3rem;
  letter-spacing: .18em;
  text-align: center;
  user-select: all;
}

.pref {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 0;
  cursor: pointer;
}

.pref input[type="checkbox"] {
  flex: none;
  width: 20px;
  height: 20px;
  margin-top: 2px;
  accent-color: var(--jade);
}

.pref-texte { display: flex; flex-direction: column; gap: 4px; }

/* Un reglage qu'on ne peut pas encore prendre. Il ne disparait pas : le savoir
   disponible plus tard fait partie de ce qu'on vient lire ici. */
/* Les reglages qui dependent d'une case au-dessus d'eux. Le retrait dit la
   subordination mieux qu'un cadre : ils n'existent que par elle. */
.pref-sous {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-left: 18px;
  padding-left: 12px;
  border-left: 2px solid var(--edge);
}

.pref-sous[hidden] { display: none; }

/* Le titre d'une section de reglages. Un trait au-dessus plutot qu'un cadre
   autour : ce qui suit lui appartient jusqu'au trait suivant, et le regard le
   comprend sans qu'on l'encadre. Le premier n'a pas de trait — il n'y a rien
   au-dessus de lui a separer. */
.prefs-section {
  margin: 26px 0 2px;
  padding-top: 18px;
  border-top: 1px solid var(--edge);
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--mist);
}

.prefs-section:first-of-type { margin-top: 4px; padding-top: 0; border-top: none; }

/* Le pied de page des reglages : le bouton, et ce qu'il a fait. */
.prefs-pied {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 26px;
  padding-top: 18px;
  border-top: 1px solid var(--edge);
}

.prefs-pied .hint { margin: 0; }
.pref-etat-ok { color: var(--jade); }

/* La fiche prend la hauteur de ce qu'elle raconte, sans plus.

   Elle reprend la mise en page du volet de transfert — meme tete, meme pied —
   mais pas sa hauteur : celui-la se remplit d'une liste qu'on fait defiler et
   occupe donc tout l'ecran, alors qu'une fiche tient en dix lignes. Elle
   flottait au milieu d'un grand vide. */
#profil .feuille-choix { height: auto; max-height: min(88vh, 720px); }
#profil .feuille-corps { flex: 0 1 auto; }

/* La fiche : le visage et l'identite cote a cote, puis les faits en deux
   colonnes. Une liste de definitions, parce que c'en est une — chaque ligne
   est une question et sa reponse. */
.profil-tete {
  display: flex;
  align-items: center;
  gap: 14px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--edge);
}

.profil-image {
  flex: none;
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
  line-height: 0;
}

.profil-identite { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.profil-nom { font-size: 1.05rem; }

.profil-numero {
  font-family: var(--mono);
  font-size: .82rem;
  letter-spacing: .12em;
  color: var(--dim);
}

.profil-marque { font-size: .78rem; color: var(--dim); }
.profil-ok { color: var(--jade); }

.profil-lignes {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 8px 16px;
  margin: 16px 0 0;
  font-size: .88rem;
}

.profil-lignes dt {
  color: var(--dim);
  /* Les intitules ne se coupent pas : c'est la valeur qui a besoin de place. */
  white-space: nowrap;
}

.profil-lignes dd { margin: 0; text-align: right; overflow-wrap: anywhere; }

/* Ce qui vient de l'administration. L'ambre est deja la couleur des choses
   qui viennent d'en haut dans cette application — l'urgent, l'annonce. */
.auteur-admin {
  color: var(--amber);
  font-size: .72rem;
  letter-spacing: .04em;
}

/* La demande de contact, au-dessus de la saisie. Elle informe et attend ; elle
   ne doit ni se confondre avec un message, ni couper la conversation. */
.fil-demande {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 8px;
  padding: 8px 10px;
  border: 1px solid var(--edge);
  border-radius: 10px;
  background: rgba(233, 179, 65, .07);
  font-size: .82rem;
}

.fil-demande[hidden] { display: none; }
.fil-demande-mot { flex: 1 1 auto; min-width: 0; }
.fil-demande-actions { display: flex; gap: 7px; flex: none; }

.fil-admin-bandeau {
  margin: 0 0 6px;
  color: var(--amber);
}

.pref-inerte { opacity: .55; }
.pref-inerte input { cursor: default; }
.pref-texte strong { font-weight: 600; color: var(--mist); }
.pref-texte .hint { margin: 0; }

/* Les retours a la ligne d'un message doivent se voir. */
#toast-texte, .msg-multi { white-space: pre-wrap; }

.msg-champ, #toast-champ {
  resize: vertical;
  min-height: 62px;
  line-height: 1.5;
}

/* Repondre depuis le bandeau : le bouton ouvre la conversation. */
.toast-repondre { display: none; }
.toast.important .toast-repondre { display: inline-block; align-self: flex-start; }

/* Favori : une etoile qui se remplit. Discrete tant qu'elle est vide, pour ne
   pas parasiter la lecture d'un annuaire ou peu de lignes sont marquees. */
.book-etoile {
  flex: none;
  background: none;
  border: none;
  padding: 2px 4px;
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  color: var(--dim);
  opacity: .45;
}

.book-etoile.marque { color: var(--amber); opacity: 1; }
/*
 * LE MEME DESSIN POUR LES DEUX ETATS — c'est le remplissage qui change.
 * « ★ » et « ☆ » etaient deux caracteres distincts, dessines par la police du
 * systeme : ils pouvaient differer de taille, de graisse, et sur certains
 * appareils l'un arrivait en couleur et l'autre au trait.
 */
.book-etoile .icone { width: 16px; height: 16px; margin: 0; vertical-align: -.15em; }
.book-etoile.marque .icone { fill: currentColor; }
.book-etoile:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

/* Sortie audio : dans l'application, un rang de boutons plutot qu'une liste. */
.sortie-seg { width: 100%; }
.sortie-seg .seg-btn { flex: 1 1 0; min-width: 0; }

/* Diffusion d'un message a plusieurs postes. */
.diffusion {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 18px;
}

.diffusion textarea {
  width: 100%;
  resize: vertical;
  min-height: 68px;
  font: inherit;
  font-size: .95rem;
  line-height: 1.5;
  color: var(--mist);
  background: var(--ink);
  border: 1px solid var(--edge);
  border-radius: 4px;
  padding: 10px 12px;
}

.diffusion textarea:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: -1px;
}

/* Case de selection dans l'annuaire, en mode administrateur. */
.book-choix {
  flex: none;
  width: 18px;
  height: 18px;
  margin-right: 2px;
  accent-color: var(--jade);
}

.btn-mini.droit { border-color: rgba(124, 152, 149, .3); color: var(--dim); }
.btn-mini.droit.accorde { border-color: rgba(70, 201, 154, .5); color: var(--jade); }
.btn-mini.droit:not(:disabled):active { background: var(--edge); }

/* Poste fermé mais abonné aux notifications : joignable malgré tout, donc ni
   la pastille verte des présents, ni le gris des injoignables. */
.dot[data-etat="notifiable"] {
  background: transparent;
  box-shadow: inset 0 0 0 2px var(--jade);
}

/* ------------------------------------------------------------- réglages */
.reglages {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-bottom: 18px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--edge);
}

.reglage {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  font-size: 13px;
}

.reglage > span:first-child { color: var(--mist); }

.reglage em {
  font-style: normal;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--dim);
}

/* L'état du code est cliquable : c'est par là qu'on le retire. */
#rg-code-etat { cursor: pointer; text-decoration: underline dotted; }

.reglage-champ {
  flex: none;
  display: flex;
  align-items: baseline;
  gap: 6px;
}

.reglage-champ input {
  width: 96px;
  padding: 8px 10px;
  font-size: 14px;
  text-align: right;
}

.reglage-champ input[type="text"],
.reglage-champ input[type="password"] {
  width: 150px;
  text-align: left;
}

#rg-etat:empty { display: none; }

/* --------------------------------------------------------------- journal */
.badge {
  display: inline-block;
  min-width: 16px;
  padding: 1px 4px;
  margin-left: 4px;
  border-radius: 8px;
  background: var(--coral);
  color: var(--ink);
  font-size: 9px;
  font-weight: 700;
  line-height: 14px;
  text-align: center;
  vertical-align: middle;
}

.log-sens {
  flex: none;
  font-size: 15px;
  line-height: 1;
  color: var(--dim);
}

.log-manque .log-sens,
.log-manque .book-num { color: var(--coral); }

.log-quand {
  flex: none;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: .08em;
  color: var(--dim);
}

/* La duree ne se cache plus sur ecran etroit : elle a son propre etage
   desormais, et n'y dispute la place a personne. La regle datait de la ligne
   unique, ou l'heure et le nom devaient primer. */

/* ------------------------------------------------------------ interphone */
.panel-diff, .panel-ecoute { gap: 16px; }

/*
 * L'ECRAN D'ECOUTE N'A PLUS DE CADRE.
 *
 * Il en avait un — fond laque, liseré jade, vingt-six pixels de marge — hérité
 * du temps où il n'affichait qu'un numéro et un bouton. Autour d'une image,
 * un cadre dans un cadre : on encadrait ce qui se suffit.
 *
 * ET IL S'ELARGIT. Le reste de l'application est pensé pour un téléphone ;
 * regarder un écran de bureau dans une colonne de quatre cents pixels était le
 * seul endroit où cette largeur coûtait quelque chose.
 */
.panel-ecoute {
  gap: 10px;
  padding: 0;
  background: none;
  border: 0;
  text-align: left;
  width: 100%;
  max-width: min(980px, 100%);
  margin: 0 auto;
}

.panel-ecoute .caller-number { animation: none; color: var(--jade); }
.panel-ecoute .key-end, .panel-diff .key-end { width: 100%; }
.panel-diff .book-row { padding: 9px 12px; }

/* Un poste qui diffuse : ni libre, ni simplement occupé. */
.dot[data-etat="diffusion"] {
  background: var(--jade);
  box-shadow: 0 0 0 3px rgba(70, 201, 154, .28);
}

/* ---------------------------------------------------------- sortie audio */
.sortie {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: -8px;
}

.sortie select {
  padding: 9px 11px;
  background: var(--lacquer);
  border: 1px solid var(--edge);
  border-radius: 3px;
  color: var(--mist);
  font-family: var(--sans);
  font-size: 13px;
}

.sortie select:focus-visible { outline: 2px solid var(--amber); outline-offset: 1px; }

/* ------------------------------------------------------------ conférence */
.panel-conf { gap: 20px; }
.panel-conf .meters { gap: 12px; }
.panel-conf .bars { height: 30px; }
.panel-conf .meter-self .bars { height: 26px; }

/* Un poste en conférence : ni libre, ni en tête-à-tête. */
.dot[data-etat="conference"] {
  background: var(--amber);
  box-shadow: 0 0 0 3px rgba(233, 179, 65, .25);
}

/* =========================================================================
   Annuaire et journal : mise en page revue
   Le tri tient sur la meme ligne que le filtre, et les actions descendent sur
   leur propre bande — elles debordaient de l'ecran sur mobile.
   ========================================================================= */

.book-tools {
  align-items: center;
  gap: 8px;
}

.tri-select {
  flex: 1;
  min-width: 0;
  padding: 9px 10px;
  background: var(--lacquer);
  border: 1px solid var(--edge);
  border-radius: 3px;
  color: var(--mist);
  font-family: var(--sans);
  font-size: 13px;
}

.tri-select:focus-visible { outline: 2px solid var(--amber); outline-offset: 1px; }

/* Deux etages : identite au-dessus, actions en dessous. En administration
   seulement -- ailleurs la ligne tient sur un seul etage. */
.book-row {
  flex-wrap: wrap;
  row-gap: 9px;
}

/* ============================================ LES AUDITEURS, EN LIGNE ==
 *
 * Une liste de NOMS, pas de fiches. Ils se rangent cote a cote et passent a la
 * ligne quand la place manque — sur un telephone elle manque tout de suite, et
 * l'on retombe sur un par ligne sans qu'aucune regle ne le dise.
 *
 * Ce n'est pas de l'agrement : trente auditeurs empiles poussaient hors de
 * l'ecran le vumetre et la touche d'arret, qui sont ce qu'on regarde en
 * diffusant.
 */
.book-en-ligne {
  flex-direction: row;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 4px;
}

.book-en-ligne .book-row {
  /* « auto » et non « 1 » : chaque pastille prend la largeur de son nom. A
     parts egales, un nom court occuperait autant qu'un nom long. */
  flex: 0 1 auto;
  width: auto;
  gap: 7px;
  padding: 4px 11px 4px 8px;
  border: 1px solid var(--edge);
  border-radius: 999px;
  background: rgba(28, 65, 66, .22);
  row-gap: 0;
  flex-wrap: nowrap;
  cursor: default;
}

.book-en-ligne .book-num { font-size: 12px; letter-spacing: .04em; }
.book-en-ligne .book-nom { font-size: 12.5px; }
/* L'etat ne merite pas sa colonne : il ne dit quelque chose que pendant la
   seconde ou la liaison s'etablit. */
.book-en-ligne .book-etat { font-size: 11px; color: var(--dim); }

/* Toute la ligne s'actionne : elle doit le dire, au survol comme au clavier. */
.book-row[data-ouvrir] { cursor: pointer; }
.book-row[data-ouvrir]:hover { background: var(--lacquer); }
.book-row[data-ouvrir]:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
}

/*
 * Le nom ne reclame plus sa largeur de contenu.
 *
 * « auto » la lui rendait, et c'est sur cette largeur que le navigateur decide
 * des retours a la ligne : un nom un peu long poussait l'etat a l'etage du
 * dessous -- trois lignes sur trente a 360 px. La regle datait de la mise en
 * page a deux etages, quand chaque poste portait ses boutons.
 *
 * Avec une base nulle (le « flex: 1 » de .book-nom), le nom prend ce qui reste
 * et se coupe d'un point de suspension s'il le faut. La ligne tient sur un
 * etage, et l'etat reste lisible a cote du nom.
 */
.book-row .book-nom { min-width: 0; }

/* La bande d'actions prend toute la largeur et pousse ses boutons a droite. */
.book-actions {
  flex: 1 0 100%;
  display: flex;
  /* Six actions ne tiennent plus sur une ligne de telephone : l'annuaire
     d'administration en compte autant depuis que « Salons » s'y ajoute, et
     sans cela c'est la page entiere qui defilait de cote (488 px pour 430). */
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 7px;
}

.book-actions .btn-mini { flex: 0 0 auto; }

/* Les actions du carnet : deux boutons, a droite, qui ne prennent que leur
   place. Elles passent a la ligne sur un ecran etroit plutot que d'ecraser le
   nom, mais ne la reclament pas d'emblee. */
/*
 * LE BOUTON D'UN GESTE QU'ON NE DEFAIT PAS.
 *
 * « .btn-mini-danger » existait pour les petits boutons de liste ; il n'y avait
 * rien pour un bouton de pleine taille. Retirer quelqu'un du carnet ou le
 * bloquer ne doit pas se presenter comme « Appeler ».
 */
.btn-danger {
  background: none;
  color: #F0968D;
  border: 1px solid rgba(225, 88, 75, .45);
}
.btn-danger:not(:disabled):active { background: var(--coral); color: var(--ink); }

/*
 * LES GESTES DE LA FICHE : DES SIGNES SUR UNE SEULE RANGEE.
 *
 * Ils etaient quatre boutons ecrits sur deux rangees — « Appeler », « Écrire »,
 * « Retirer », « Bloquer » — et pesaient plus que ce qu'on venait lire. Le mot
 * n'est pas perdu : il vit dans « title » et « aria-label ».
 *
 * ILS RESTENT GRANDS. Un signe n'excuse pas une cible etroite : 44 px, c'est
 * la pulpe d'un doigt. Ce qu'on gagne est la hauteur de la fiche, pas la
 * surface du bouton.
 *
 * ET LES DEUX SORTES SE DISTINGUENT A L OEIL — joindre est ambre et plein,
 * defaire est corail et creux — parce qu'un signe se reconnait moins vite
 * qu'un mot : la couleur fait la moitie du travail.
 */
.fiche-geste {
  flex: none;
  min-width: 56px;
  padding: 8px 10px 6px;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  /* Le mot sous le signe (lot 4, 2026-09-27) : le rond devient une pastille. */
  border-radius: 14px;
  border: 1px solid var(--edge);
  background: transparent;
  color: var(--dim);
  cursor: pointer;
  transition: background .12s, color .12s, border-color .12s;
}

/* L'icone d'un bouton ordinaire laisse 6 px a sa droite pour un mot ; ici il
   n'y a pas de mot, et cette marge decentrerait le signe dans son rond. */
.fiche-geste .icone { width: 20px; height: 20px; margin: 0; vertical-align: 0; }

.fiche-geste:hover { color: var(--mist); border-color: var(--dim); }
.fiche-geste:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

/* Appeler : c'est pour cela qu'on ouvre la fiche neuf fois sur dix. */
.fiche-geste.geste-appel {
  background: var(--amber);
  border-color: var(--amber);
  color: var(--ink);
}
.fiche-geste.geste-appel:hover { color: var(--ink); border-color: var(--amber); }

/* Ouvrir : laisser entrer dans son carnet, rendre sa ligne a un poste bloque. */
.fiche-geste.geste-oui { color: var(--jade); border-color: rgba(70, 201, 154, .45); }
.fiche-geste.geste-oui:hover { color: var(--jade); border-color: var(--jade); }
.fiche-geste.geste-oui:active { background: var(--jade); color: var(--ink); }

/* Defaire un lien, fermer une porte : creux, et corail. */
.fiche-geste.geste-danger { color: #F0968D; border-color: rgba(225, 88, 75, .45); }
.fiche-geste.geste-danger:hover { color: #F0968D; border-color: var(--coral); }
.fiche-geste.geste-danger:active { background: var(--coral); color: var(--ink); }

/*
 * LES GESTES DU CARNET SE TIENNENT A DROITE, loin de « Appeler ».
 *
 * C'est ce que faisaient les deux rangees d'avant : eviter qu'une main qui
 * vise « Appeler » tombe sur « Retirer ». Une rangee unique le fait par
 * l'ecart plutot que par la hauteur.
 */
.feuille-pied .fiche-geste[data-carnet] { margin-left: auto; }
.feuille-pied .fiche-geste[data-carnet] ~ .fiche-geste[data-carnet] { margin-left: 0; }

/*
 * UNE LIGNE DU CARNET QUI MENE QUELQUE PART.
 *
 * Elle portait ses actions ; elle ouvre maintenant une fiche. Le chevron dit
 * qu'il y a un ailleurs — sans lui, rien ne distingue une ligne qu'on touche
 * d'une ligne qu'on lit.
 */
.fil-ligne-ouvrable { cursor: pointer; }
.fil-ligne-ouvrable:hover,
.fil-ligne-ouvrable:focus-visible { background: rgba(255, 255, 255, .04); }
.fil-ligne-ouvrable:focus-visible { outline: 2px solid var(--amber); outline-offset: -2px; }

.fil-chevron {
  flex: none;
  color: var(--dim);
  font-size: 1.3rem;
  line-height: 1;
  padding: 0 .2rem;
}

/*
 * Le pied de la fiche d'un contact : ses actions, cote a cote.
 *
 * « flex-wrap » plutot qu'une largeur fixe : « Retirer » et « Bloquer »
 * tiennent sur une ligne, « Accepter » et « Refuser » aussi, mais on ne sait
 * pas ce qu'on y mettra un jour.
 */
#contact-fiche-actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: flex-end;
}
#contact-fiche-actions .btn { flex: 1 1 auto; }

/* L'image de la fiche d'un contact ne s'ouvre pas en grand — elle n'est pas
   la notre, et rien ne dit que son porteur veut qu'on la scrute. Pas de
   curseur « main » qui promettrait un geste inexistant. */
#contact-fiche-avatar { cursor: default; }

.carnet-actions {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 7px;
}

#panel-contacts .fil-ligne { flex-wrap: wrap; gap: 10px; }
#panel-contacts .fils { display: flex; flex-direction: column; gap: 8px; }

/* Le journal n'a plus de bande d'actions : l'heure n'a plus rien a contourner. */

@media (max-width: 380px) {
  /* L'administration garde ses six actions : elles, se serrent encore. */
  .book-actions .btn-mini { padding: 7px 9px; }
}

/* ---------------------------------------------------------- la page propre d'un role
   (2026-09-28, cahier ch. 21) Un poste dont le role d'extension a sa propre
   page n'est plus un telephone : tout « .phone » disparait, sauf ce qui dit
   quelque chose a qui est devant (le toast, l'erreur, la version perimee).
   L'ecran de l'extension vit hors de « .phone ». Ce qui reste passe devant lui. */
body.page-propre .phone > :not(#toast):not(#error):not(#maj-ui) { display: none !important; }
body.page-propre .toast, body.page-propre .maj-ui { z-index: 1000; }
body.page-propre .error {
  position: fixed;
  left: 50%;
  bottom: 16px;
  transform: translateX(-50%);
  z-index: 1000;
  width: min(420px, calc(100vw - 24px));
  background: var(--ink);
}

/* ---------------------------------------------------------- messages texte */
.toast {
  position: fixed;
  left: 50%;
  top: 12px;
  transform: translateX(-50%);
  z-index: 20;
  width: min(420px, calc(100vw - 24px));
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 14px;
  /* Sans cadre : c'est la VALEUR du fond qui detache, pas un trait. Un ton
     au-dessus de la carte, pour qu'il se lise comme pose dessus, et une ombre
     portee pour le decoller. Le liseret jade d'avant faisait un rectangle de
     plus sur un ecran qui n'en manque pas. */
  background: rgba(31, 72, 73, .96);
  border: none;
  border-radius: 10px;
  box-shadow: 0 8px 26px rgba(0, 0, 0, .5);
}

/* Un message recu ne doit pas se confondre avec un accuse d'envoi : il vient
   au centre de l'ecran, plus grand, et l'ombre demesuree fait office de voile
   sans ajouter d'element — une ombre ne capte pas les clics, l'application
   reste donc utilisable dessous. */
.toast.important {
  top: 50%;
  transform: translate(-50%, -50%);
  width: min(460px, calc(100vw - 32px));
  flex-direction: column;
  gap: 8px;
  padding: 18px 20px;
  border-left: 4px solid var(--amber);
  border-radius: 8px;
  box-shadow: 0 0 0 100vmax rgba(8, 25, 26, .62), 0 18px 48px rgba(0, 0, 0, .6);
  animation: toast-parait .2s ease-out;
}

@keyframes toast-parait {
  from { opacity: 0; transform: translate(-50%, -46%); }
  to   { opacity: 1; transform: translate(-50%, -50%); }
}

@media (prefers-reduced-motion: reduce) {
  .toast.important { animation: none; }
}

.toast.important strong {
  max-width: none;
  font-size: 11px;
  color: var(--amber);
}

.toast.important span {
  font-size: 1.05rem;
  line-height: 1.55;
}

/* En pile, le bouton de fermeture tombait sous le texte, ou on ne le cherche
   pas. Il reprend sa place au coin, la ou on ferme les choses. */
.toast.important {
  position: fixed;
  padding-right: 44px;
}

/* Le bouton de fermeture seulement : viser tous les boutons du bandeau
   collait aussi ceux de la reponse dans le coin, les uns sur les autres. */
#toast-fermer { position: static; }

.toast.important > #toast-fermer {
  position: absolute;
  top: 12px;
  right: 12px;
}

.toast strong {
  flex: none;
  max-width: 45%;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--jade);
  line-height: 1.5;
}

.toast span {
  flex: 1;
  min-width: 0;
  font-size: 13px;
  line-height: 1.5;
  word-break: break-word;
}

.toast button {
  flex: none;
  padding: 0 2px;
  background: transparent;
  border: 0;
  color: var(--dim);
  font-size: 13px;
  cursor: pointer;
}

/* Saisie d'un message sur sa propre bande, sous la ligne du poste. */
.msg-zone {
  flex: 1 0 100%;
  display: flex;
  gap: 7px;
  margin-top: 2px;
}

.msg-champ {
  flex: 1;
  min-width: 0;
  padding: 8px 10px;
  font-size: 14px;
}

/* =========================================================================
   Règles réintroduites : une réécriture précédente du fichier les avait
   emportées. Elles restaient indispensables aux filtres, au cadenas et à
   l'édition des noms.
   ========================================================================= */

.book-tools { display: flex; }

.seg {
  flex: none;
  display: flex;
  border: 1px solid var(--edge);
  border-radius: 3px;
  overflow: hidden;
}

.seg-btn {
  padding: 9px 12px;
  background: transparent;
  border: 0;
  color: var(--dim);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  cursor: pointer;
  white-space: nowrap;
}

.seg-btn + .seg-btn { border-left: 1px solid var(--edge); }
.seg-btn[aria-pressed="true"] { background: var(--edge); color: var(--mist); }
.seg-btn:focus-visible { outline: 2px solid var(--amber); outline-offset: -2px; }

.book-count {
  margin: 0;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--dim);
}

.book-nom-editable {
  cursor: text;
  text-decoration: underline dotted var(--edge);
  text-underline-offset: 3px;
}

.book-nom-champ {
  flex: 1 1 auto;
  min-width: 0;
  padding: 5px 8px;
  font-size: 14px;
}

.cadenas {
  flex: none;
  padding: 2px 4px;
  background: transparent;
  border: 0;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  opacity: .5;
}

.cadenas.ferme { opacity: 1; }
/* Au trait, il lui faut deux pixels de plus qu'a l'emoji pour se lire. */
.cadenas .icone { width: 15px; height: 15px; margin: 0; vertical-align: -.2em; }
.cadenas:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

/* Sur écran étroit, les libellés du segment se resserrent plutôt que de
   pousser le tri à la ligne suivante. */
@media (max-width: 380px) {
  .seg-btn { padding: 9px 9px; letter-spacing: .06em; }
}

/* L'etat de la liaison, au bas de l'accueil. Discret : on ne le cherche pas,
   on tombe dessus quand on cherche pourquoi un appel n'aboutit pas. */
.liaison {
  margin-top: 4px;
  padding-top: 12px;
  border-top: 1px solid var(--edge);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: #4C6D6B;
}

/* ------------------------------------------------------- messages vocaux */

/* Pendant l'enregistrement, la pilule s'efface : il n'y a plus qu'a parler,
   puis a trancher. Meme hauteur que la pilule, pour que rien ne saute. */
.fil-dictee {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 9px;
  min-height: 40px;
  padding: 5px 12px;
  background: var(--lacquer);
  border: 1px solid var(--coral);
  border-radius: 20px;
}

/* Le point qui bat : c'est lui qui dit que le micro est ouvert, sans avoir a
   le lire. */
.dictee-point {
  flex: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--coral);
  animation: bat 1.2s ease-in-out infinite;
}

@keyframes bat {
  0%, 100% { opacity: 1; }
  50%      { opacity: .25; }
}

@media (prefers-reduced-motion: reduce) {
  .dictee-point { animation: none; }
}

.dictee-duree {
  flex: none;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: 14px;
  color: var(--mist);
}

/* L'aide cede la place la premiere : sur un ecran etroit, la duree et les deux
   decisions valent mieux qu'un mot. */
.dictee-aide {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 11px;
  color: var(--dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.fil-dictee .btn-mini { flex: none; padding: 6px 10px; }
.dictee-annuler { color: var(--dim); }
.dictee-envoyer { border-color: rgba(70, 201, 154, .45); color: var(--jade); }

/* Le lecteur, dans la bulle. Il prend la largeur qu'on lui laisse plutot que
   celle qu'il reclame : sans cela, il debordait de la bulle sur un telephone. */
.jointe-voix {
  display: block;
  width: 100%;
  max-width: 232px;
  min-width: 180px;
  height: 36px;
}

/* ------------------------------------------------------------- traduction */

/* Discret dans la bulle : c'est une offre, pas une action qu'on reclame. */
.traduire {
  align-self: flex-start;
  margin-top: 2px;
  padding: 0;
  background: none;
  border: 0;
  color: var(--jade);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  cursor: pointer;
}

.traduire:disabled { color: var(--dim); cursor: default; }
.traduire:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

/* La traduction se pose SOUS l'original, separee d'un filet : on voit d'un
   coup d'oeil ce qui a ete ecrit et ce qu'on en comprend. L'original ne
   disparait jamais. */
.traduit {
  display: block;
  width: 100%;
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid rgba(255, 255, 255, .12);
}

.traduit-source {
  display: block;
  margin-bottom: 2px;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--dim);
}

.traduit-texte {
  display: block;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  font-style: italic;
  color: var(--mist);
}

/* Elle a sa place sur la ligne, a cote de la duree — jamais dedans : le
   journal masque « .book-etat » sous 380 px, et la liaison y disparaissait
   avec elle, sur le telephone justement ou l'on vient la lire. */
.log-row .log-liaison { flex: none; }

/* Le chemin qu'a pris la voix. Le direct en jade, le relais en ambre : ce
   n'est pas une faute, mais c'est la que la latence se paie — et c'est
   justement ce qu'on vient chercher dans ce journal. */
.log-liaison {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--jade);
}

.log-liaison.relais { color: var(--amber); }

/*
 * D'OU VIENT L'APPEL, sur la ligne du journal.
 *
 * Même graisse et même taille que la nature de la liaison, juste à côté : ce
 * sont deux renseignements de même rang, et deux tailles différentes auraient
 * fait passer l'un pour un titre de l'autre. L'ambre les distingue du vert de
 * « direct » — celui-ci dit une qualité, celui-là un détour.
 */
.log-renvoi {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--amber);
}

/* Le micro occupe la meme case que l'envoi : meme taille, meme rondeur, pour
   que la bascule ne fasse pas sauter la ligne. */
.fil-micro { font-size: 17px; }

/*
 * Le choix photo/video/fichier, dans l'application Android seulement.
 *
 * Il prend la PLACE de la pilule, il ne s'ajoute pas dessous. Depliee sous la
 * ligne de saisie, cette barre allongeait le panneau d'une hauteur entiere :
 * il depassait l'ecran, et une barre de defilement paraissait — desagreable,
 * et pour rien.
 *
 * Meme hauteur et meme rondeur que la pilule qu'elle remplace : la ligne ne
 * saute pas quand on ouvre le choix, ni quand on le referme.
 */
.fil-choix {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  /* 42 px : exactement la hauteur de la pilule au repos, mesuree. A 40, la
     ligne bougeait de deux pixels en ouvrant le choix. */
  min-height: 42px;
  padding: 4px 8px;
  background: var(--ink);
  border: 1px solid var(--edge);
  border-radius: 22px;
}

/* Les libelles se coupent avant de deborder : quatre boutons sur un ecran de
   360 px, cela se joue a quelques pixels. */
.fil-choix .btn-mini {
  flex: 0 1 auto;
  min-width: 0;
  padding: 6px 9px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.fil-choix .choix-annuler {
  flex: none;
  margin-left: auto;
  color: var(--dim);
}

/* La video dans la bulle : elle prend la largeur qu'on lui laisse, jamais
   celle qu'elle reclame — une video de telephone fait 1080 de large. */
.jointe-film {
  display: block;
  width: 100%;
  max-width: 232px;
  max-height: 300px;
  border-radius: 3px;
  background: #000;
}

/* ------------------------------------------------------------ visionneuse */

/* Par-dessus tout, et cadree a l'ecran : l'image debordait dans
   l'application, au point qu'il n'y avait plus rien d'autre a toucher. */
.visionneuse {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 16px;
  /* Assez opaque pour que l'image se detache, assez sombre pour qu'on
     comprenne que le fond se touche pour fermer. */
  background: rgba(4, 10, 12, .94);
}

.visionneuse img {
  max-width: 100%;
  /* Le bouton et le lien gardent leur place : sans cette reserve, une image
     haute les poussait hors de l'ecran — et l'on retombait sur le probleme
     qu'on vient de corriger. */
  max-height: calc(100vh - 130px);
  object-fit: contain;
  border-radius: 3px;
}

.visionneuse-fermer {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 40px;
  height: 40px;
  background: var(--lacquer);
  border: 1px solid var(--edge);
  border-radius: 50%;
  color: var(--mist);
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
}

.visionneuse-fermer:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

.visionneuse-lien {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--amber);
  text-decoration: none;
  border-bottom: 1px solid rgba(233, 179, 65, .4);
}

/* ===========================================================================
 * Le menu de la conversation, et le choix des messages
 * ======================================================================== */

/* Le menu se pose PAR-DESSUS le fil, il ne le pousse pas : sans « absolute »,
   l ouvrir ferait sauter toute la conversation d une hauteur de menu. */
/* Le panneau du fil s etire sur toute la hauteur disponible : c est lui qui
   pousse la saisie vers le bas. */
.panel-fil {
  position: relative;
  flex: 1;
  min-height: 0;
}

.menu {
  position: absolute;
  top: 40px;
  right: 0;
  z-index: 30;
  min-width: 210px;
  display: flex;
  flex-direction: column;
  padding: 5px;
  background: var(--lacquer);
  border: 1px solid var(--edge);
  border-radius: 10px;
  box-shadow: 0 10px 26px rgba(0, 0, 0, .45);
}

.menu-item {
  appearance: none;
  text-align: left;
  padding: 10px 12px;
  border: none;
  border-radius: 6px;
  background: none;
  color: var(--mist);
  font: inherit;
  font-size: .92rem;
  cursor: pointer;
}

.menu-item:hover { background: var(--edge); }
.menu-item:focus-visible { background: var(--edge); outline: 2px solid var(--mist); outline-offset: -2px; }
.menu-item-danger { color: #F0968D; }

/* La barre qui remplace la saisie pendant qu on choisit. */
.fil-choisis {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-top: 8px;
}

.fil-tete-choix .fil-choisis-compte {
  /* Le compte prend le milieu, comme le nom qu il remplace, et s efface
     avant les boutons quand la largeur manque. */
  font-size: 12px;
  color: var(--mist);
}

.fil-choisis-compte {
  flex: 1;
  min-width: 0;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/*
 * La case a cocher.
 *
 * Elle est dessinee par « ::before », donc DANS la bulle et en premier — la
 * bulle etant deja une boite flex, la case y prend naturellement la premiere
 * place, devant le texte. Elle n a pas besoin d etre cliquable pour
 * elle-meme : pendant le choix, c est toute la bulle qui coche et decoche.
 *
 * « ::after » sert deja a la queue ; on ne pouvait donc pas s en servir ici.
 */
.fil-corps.choisir .bulle::before {
  content: "☐";
  flex: none;
  align-self: center;
  margin-right: 2px;
  font-size: 15px;
  line-height: 1;
  color: var(--dim);
}

.fil-corps.choisir .bulle.choisie::before {
  content: "☑";
  color: var(--amber);
}

/* Choisie : elle se detache sans changer de teinte — la couleur dit deja qui
   parle, elle ne peut pas dire deux choses a la fois. */
.fil-corps.choisir .bulle.choisie { box-shadow: 0 0 0 2px var(--amber); }

/* Pendant le choix, on coche : on ne selectionne pas du texte par megarde, et
   un appui long n appelle pas la loupe du telephone. */
.fil-corps.choisir {
  user-select: none;
  -webkit-user-select: none;
}

.fil-corps.choisir .bulle { cursor: pointer; }

/* Les boutons interieurs d une bulle — traduire, ouvrir une piece jointe — se
   taisent pendant le choix : l appui doit cocher, pas agir. */
.fil-corps.choisir .bulle a,
.fil-corps.choisir .bulle button { pointer-events: none; }

/* Par quelle porte un poste entre : application Android, ou navigateur. */
.book-appli {
  flex: none;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: .1em;
  padding: 2px 5px;
  border-radius: 3px;
  border: 1px solid var(--edge);
  color: var(--dim);
}

.book-appli[data-appli="apk"] {
  border-color: var(--jade);
  border-color: color-mix(in srgb, var(--jade) 45%, transparent);
  color: var(--jade);
}

/* Le raccourci installe : entre l'application et l'onglet, et sa couleur le
   dit — ni le vert de l'application native, ni le gris de l'onglet. */
/* L'image d'une station. Elle occupe la largeur sans jamais deborder, et son
   fond noir evite le clignotement blanc entre deux images. */
/* Les outils sous l'image : changer de camera, agrandir, detacher. Ils tiennent
   sur une ligne et passent a la suivante si l'ecran est etroit. */
.station-outils {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 8px;
}

.station-video {
  width: 100%;
  max-height: 40vh;
  border-radius: 10px;
  background: #000;
  object-fit: contain;
}

/* ==================================================== LA SCENE D'ECOUTE ==
 *
 * Une surface noire, et TOUT LE RESTE DESSUS. Plus de cadres, plus
 * d'intitules : ce qui accompagne une image n'a pas a lui disputer la place.
 *
 * « position: relative » ici, « absolute » pour les deux bandeaux : c'est ce
 * qui permet a l'image de garder ses proportions sans que rien ne la pousse.
 */
.scene {
  position: relative;
  width: 100%;
  border-radius: 14px;
  background: #000;
  overflow: hidden;
  /* Sans image — une webradio — la scene devient une barre. Un seul
     agencement, deux hauteurs, plutot que deux ecrans a tenir d'accord. */
  min-height: 96px;
}
.scene .station-video {
  display: block;
  border-radius: 0;
  max-height: 56vh;
}

/*
 * LES DEUX BANDEAUX. Un degrade et non un fond plein : sur une image claire il
 * faut du contraste, sur une image sombre un aplat ferait une barre grise en
 * travers de ce qu'on regarde.
 */
.scene-tete,
.scene-pied {
  position: absolute;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  pointer-events: none;   /* la scene reste cliquable, les boutons repondent */
}
.scene-tete {
  top: 0;
  background: linear-gradient(to bottom, rgba(0, 0, 0, .72), transparent);
}
.scene-pied {
  bottom: 0;
  background: linear-gradient(to top, rgba(0, 0, 0, .72), transparent);
}
.scene-tete > *,
.scene-pied > * { pointer-events: auto; }

/*
 * AU REPOS, ILS S'EFFACENT.
 *
 * On ne les retire pas du document — « visibility » et « opacity », et non
 * « display » : un bouton retire puis remis perdrait le focus du clavier, et
 * l'image sauterait a chaque aller-retour.
 *
 * « visibility: hidden » en plus de l'opacite : un element seulement
 * transparent reste cliquable et lisible par un lecteur d'ecran, ce qui
 * reviendrait a laisser des boutons invisibles sous le doigt.
 */
.scene-calme .scene-tete,
.scene-calme .scene-pied {
  opacity: 0;
  visibility: hidden;
  transition: opacity .45s ease, visibility 0s .45s;
}
.scene-tete,
.scene-pied { transition: opacity .18s ease; }

/* L'image seule : le curseur n'a plus rien a designer, autant le retirer. */
.scene-calme { cursor: none; }

@media (prefers-reduced-motion: reduce) {
  .scene-tete, .scene-pied,
  .scene-calme .scene-tete, .scene-calme .scene-pied { transition: none; }
}

/* Le point qui bat : il dit que ca vit, sans un mot et sans place. */
.scene-vif {
  flex: none;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--jade);
  box-shadow: 0 0 0 0 rgba(70, 201, 154, .55);
  animation: scene-battre 2.4s ease-out infinite;
}
@keyframes scene-battre {
  0%   { box-shadow: 0 0 0 0 rgba(70, 201, 154, .5); }
  70%  { box-shadow: 0 0 0 7px rgba(70, 201, 154, 0); }
  100% { box-shadow: 0 0 0 0 rgba(70, 201, 154, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .scene-vif { animation: none; }
}

.scene-nom {
  font-size: .92rem;
  font-weight: 500;
  color: #fff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .8);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.scene-num {
  flex: none;
  font-family: var(--mono);
  font-size: .72rem;
  color: rgba(255, 255, 255, .68);
  text-shadow: 0 1px 3px rgba(0, 0, 0, .8);
}

/*
 * LES BOUTONS : SANS CADRE, ET DE LA TAILLE D'UN DOIGT.
 *
 * Trente-six pixels : c'est le minimum sous lequel on rate la cible sur un
 * telephone. Le glyphe, lui, est petit — c'est la surface qui compte, pas le
 * dessin.
 */
.scene-btn {
  flex: none;
  width: 36px; height: 36px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, .42);
  color: #fff;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  transition: background .15s;
}
.scene-btn:hover { background: rgba(255, 255, 255, .18); }
.scene-btn:active { background: rgba(255, 255, 255, .3); }
.scene-btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
/* Quitter : la seule couleur de l'ecran, parce que c'est la seule action qui
   ne se rattrape pas d'un second clic. */
.scene-btn-fin { color: #FF9E93; }
.scene-btn-fin:hover { background: rgba(225, 88, 75, .55); color: #fff; }

/* La croix est a droite, tout ce qui precede la pousse. */
.scene-tete .scene-btn-fin { margin-left: auto; }

/* Le vumetre prend la place qui reste : sans image, c'est lui qui dit que
   quelque chose arrive. */
.bars-scene { flex: 1; min-width: 0; height: 22px; opacity: .9; }

/* Une phrase de rappel n'a pas a peser autant qu'une consigne. */
.hint-mince { opacity: .6; font-size: .78rem; }

.book-appli[data-appli="windows"] {
  border-color: var(--mist, #D6E4E1);
  border-color: color-mix(in srgb, var(--mist, #D6E4E1) 45%, transparent);
  color: var(--mist, #D6E4E1);
}

.book-appli[data-appli="pwa"] {
  border-color: var(--amber);
  border-color: color-mix(in srgb, var(--amber) 45%, transparent);
  color: var(--amber);
}

/* ------------------------------------------------------- les emoticones */

/* Le tiroir se pose au-dessus du fil, comme le menu : l ouvrir ne doit pas
   faire sauter la conversation. Ancre au bas du panneau, juste au-dessus de
   la saisie. */
.emojis {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 58px;
  z-index: 25;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px;
  background: var(--lacquer);
  border: 1px solid var(--edge);
  border-radius: 12px;
  box-shadow: 0 -8px 26px rgba(0, 0, 0, .45);
}

.emojis-onglets {
  display: flex;
  gap: 4px;
  overflow-x: auto;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--edge);
}

.emojis-onglet {
  flex: none;
  appearance: none;
  border: none;
  background: none;
  border-radius: 6px;
  padding: 5px 8px;
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
  opacity: .5;
}

.emojis-onglet[aria-selected="true"] { opacity: 1; background: var(--edge); }
.emojis-onglet:focus-visible { outline: 2px solid var(--mist); outline-offset: -2px; }

/* Une grille qui se remplit toute seule : sur un ecran etroit elle donne six
   colonnes, sur un large une dizaine, sans qu on ait a le decider. */
.emojis-grille {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(38px, 1fr));
  gap: 2px;
  max-height: 190px;
  overflow-y: auto;
}

.emojis-un {
  appearance: none;
  border: none;
  background: none;
  border-radius: 6px;
  padding: 4px 0;
  font-size: 21px;
  line-height: 1.25;
  cursor: pointer;
}

.emojis-un:hover, .emojis-un:focus-visible { background: var(--edge); outline: none; }

/* Dans une bulle, une emoticone seule se lit mal a la taille du texte. */
.bulle .dit { font-variant-emoji: emoji; }

/* --------------------------------------------------------- la recherche */

.chercher {
  width: 100%;
  font: inherit;
  font-size: .92rem;
  color: var(--mist);
  background: var(--ink);
  border: 1px solid var(--edge);
  border-radius: 20px;
  padding: 9px 14px;
}

.chercher::placeholder { color: var(--dim); }
.chercher:focus-visible { outline: none; border-color: var(--amber); }

/* La croix que le navigateur ajoute dans un champ « search » est noire sur
   fond sombre : on l eclaircit plutot que de la masquer, elle sert. */
.chercher::-webkit-search-cancel-button {
  filter: invert(1) opacity(.5);
  cursor: pointer;
}

.fil-recherche {
  display: flex;
  align-items: center;
  gap: 8px;
}

.fil-recherche .chercher { flex: 1; min-width: 0; }

.fil-trouve-compte {
  margin: 0;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--dim);
}

/* ------------------------------------------------------------ les visages */

/* Un rond qui porte soit une image, soit deux lettres. Les deux cas ont
   exactement la meme taille : une liste ou les lignes sautent selon que
   chacun a mis sa photo se lirait mal. */
.avatar {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--lacquer);
  border: 1px solid var(--edge);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .04em;
  color: var(--dim);
  user-select: none;
}

.avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.avatar-mini { width: 20px; height: 20px; font-size: 8px; }
.avatar-petit { width: 26px; height: 26px; font-size: 10px; }

.avatar-grand {
  width: 84px;
  height: 84px;
  font-size: 26px;
  letter-spacing: .06em;
}

.chat-avatar { flex: none; display: inline-flex; }

/* Dans un salon, le visage precede le nom sur la meme ligne. */
.bulle .auteur {
  display: flex;
  align-items: center;
  gap: 6px;
}

.avatar-reglage {
  display: flex;
  align-items: center;
  gap: 16px;
}

.avatar-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

/* --------------------------------------------------------- le transfert */

/* ======================================================= OUVRIR UN CANAL ==

   L'ecran d'ouverture d'un canal de diffusion. Il vit dans DEUX maisons — une
   fenetre modale sur ordinateur, une page sur telephone — et le style ne s'en
   soucie pas : il habille le formulaire, pas son logement. */

/* Plus large que les autres boites : deux colonnes de cartes doivent tenir
   cote a cote, sinon la fenetre modale n apporte rien sur un grand ecran.

   DEUX CLASSES, ET NON UNE SEULE : « .voile-boite.voile-large » et non
   « .voile-large ». A specificite egale, c est la regle ECRITE EN DERNIER qui
   gagne — et « .voile-boite { max-width: 420px } » vient plus bas dans ce
   fichier. La boite restait donc etroite, les cartes sur une colonne, et rien
   n avait l air casse : juste une fenetre modale qui n apportait rien. */
/*
 * IL GRANDIT AVEC LA FENETRE.
 *
 * Six cent vingt pixels au plus, c'etait etroit des qu'on agrandit : un
 * ascenseur interieur apparaissait alors que l'ecran etait aux trois quarts
 * vide. « min() » lui laisse prendre la place disponible sans jamais deborder,
 * et la borne haute empeche une ligne de texte de courir sur deux mille
 * pixels — au-dela, on ne lit plus, on balaye.
 */
.voile-boite.voile-large {
  max-width: min(920px, 92vw);
  max-height: min(92vh, 900px);
}

.canal-choix {
  display: grid;
  gap: 8px;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  margin: 0 0 4px;
}

.canal-choix[hidden] { display: none; }

.canal-carte {
  display: grid;
  grid-template-columns: 1.6rem 1fr;
  grid-template-rows: auto auto;
  gap: 2px 10px;
  align-items: start;
  padding: 11px 13px;
  text-align: left;
  cursor: pointer;
  border: 1px solid var(--edge);
  border-radius: 11px;
  background: rgba(28, 65, 66, .18);
  color: var(--mist);
  font: inherit;
}

.canal-carte:hover { border-color: var(--dim); }
.canal-carte:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

/* DEUX signes, et non la seule couleur : le fond plus clair porte le meme
   message pour qui distingue mal l'ambre du vert sombre. */
.canal-carte-prise {
  border-color: var(--amber);
  background: rgba(233, 179, 65, .13);
}

.canal-signe { grid-row: 1 / 3; font-size: 1.15rem; line-height: 1.3; }
.canal-nom { font-weight: 600; font-size: .93rem; }
.canal-note { font-size: .78rem; line-height: 1.45; color: var(--dim); }
.canal-carte-prise .canal-nom { color: var(--amber); }

/* CE QU'ON S'APPRETE A DIFFUSER, juste au-dessus du bouton qui le fera : la
   derniere chose lue avant le geste, et non une note perdue plus haut. */
.canal-resume {
  margin: 6px 0 0;
  padding: 10px 12px;
  border-left: 2px solid var(--amber);
  background: rgba(233, 179, 65, .07);
  border-radius: 0 8px 8px 0;
  font-size: .86rem;
  line-height: 1.5;
  color: var(--mist);
}

/* Un voile par-dessus tout : on choisit un destinataire, on ne navigue pas. */
.voile {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(3, 10, 11, .72);
}

.voile[hidden] { display: none; }

.voile-boite {
  width: 100%;
  max-width: 420px;
  max-height: 82vh;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 18px;
  background: var(--lacquer);
  border: 1px solid var(--edge);
  border-radius: 14px;
  box-shadow: 0 18px 44px rgba(0, 0, 0, .5);
}

.voile-titre { font-size: 1rem; }
.voile-boite .hint { margin: 0; }

/* La liste prend la hauteur qui reste, et defile seule : la boite, elle, ne
   doit jamais depasser l ecran. */
.voile-boite .book {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  margin: 0;
}

.own-avatar {
  position: absolute;
  right: 16px;
  top: 50%;
  transform: translateY(-50%);
  appearance: none;
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
  border-radius: 50%;
  /* Sans cela le bouton se reserve une ligne de texte sous le rond. */
  line-height: 0;
}

.own-avatar:focus-visible { outline: 2px solid var(--mist); outline-offset: 3px; }
.own-avatar .avatar { width: 54px; height: 54px; font-size: 18px; }

/* ------------------------------------------------------ le telephone */

.tel-reglage {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

#tel-champ {
  flex: 1;
  min-width: 150px;
  font-family: var(--mono);
  font-size: .95rem;
  letter-spacing: .06em;
  color: var(--mist);
  background: var(--ink);
  border: 1px solid var(--edge);
  border-radius: 6px;
  padding: 9px 11px;
}

#tel-champ:focus-visible { outline: none; border-color: var(--amber); }
.tel-actions { display: flex; gap: 8px; }

/* Le numero dans une ligne d annuaire. Valide, il passe au jade et porte sa
   marque : c est le seul endroit ou la distinction se lit d un coup d oeil. */
.book-tel {
  flex: none;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .04em;
  color: var(--dim);
}

.book-tel.verifie { color: var(--jade); }

.dial-trouve { color: var(--amber); }

/* ---------------------------------------------------------- l appel video */

/* Le bloc des images. Il ne parait que pendant un appel video ; le panneau
   d appel reste exactement ce qu il etait pour un appel audio. */
.visio {
  position: relative;
  width: 100%;
  aspect-ratio: 3 / 4;
  max-height: 58vh;
  border-radius: 12px;
  overflow: hidden;
  background: #000;
}

.visio[hidden] { display: none; }

/* Le correspondant occupe le cadre. « cover » et non « contain » : deux
   bandes noires autour d un visage n aident personne, on prefere recadrer. */
.visio-loin {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* La vignette. C'est un BOUTON : on la touche pour l'echanger avec la grande
   image, et elle doit donc se viser au doigt comme au clavier. */
.visio-moi {
  position: absolute;
  right: 10px;
  bottom: 10px;
  width: 28%;
  max-width: 120px;
  aspect-ratio: 3 / 4;
  padding: 0;
  overflow: hidden;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, .35);
  background: #000;
  cursor: pointer;
  line-height: 0;
}

.visio-moi video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.visio-moi:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

/*
 * Le miroir suit LE FLUX, pas l'element : c'est sa propre image qu'on veut
 * voir comme dans une glace — sans quoi lever la main droite la fait paraitre
 * a gauche, et le geste devient penible. Elle garde donc son miroir en passant
 * de la vignette au grand cadre, et le correspondant n'en a jamais.
 */
.miroir { transform: scaleX(-1); }

/* En plein ecran, le cadre prend tout : la vignette et les commandes restent
   par-dessus, puisque c'est le CADRE qu'on met en plein ecran, pas la video. */
.visio:fullscreen {
  width: 100vw;
  height: 100vh;
  max-height: none;
  aspect-ratio: auto;
  border-radius: 0;
}

.visio:fullscreen .visio-loin { object-fit: contain; }

/*
 * Les commandes de la camera, POSEES SUR L IMAGE.
 *
 * En bas du cadre, sur un voile sombre : elles restent lisibles quelle que
 * soit l image, et elles ne prennent plus une ligne de la page pour trois
 * boutons. « pointer-events » revient aux boutons seuls, pour que le reste du
 * bandeau ne vole pas les clics destines a l image.
 */
.visio-actions {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  display: flex;
  gap: 8px;
  justify-content: center;
  flex-wrap: wrap;
  padding: 10px 8px;
  background: linear-gradient(to top, rgba(3, 10, 11, .78), transparent);
  pointer-events: none;
}

.visio-actions[hidden] { display: none; }


/*
 * Le correspondant et le decompte, sous l image.
 *
 * En appel AUDIO cette enveloppe est une simple colonne : la page ne change
 * pas d un pixel. En appel VIDEO elle devient une ligne, le pseudo d un cote
 * et le temps de l autre — l image occupe deja la hauteur, le reste doit se
 * faire discret.
 */
.appel-tete {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.call-nom { display: none; }

body.en-visio .appel-tete {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

body.en-visio .appel-tete .timer {
  font-size: 30px;
  letter-spacing: .06em;
}

/* En video, le pseudo prend la premiere place et le numero passe au second
   plan : on sait a qui l on parle, on le voit. */
body.en-visio .call-nom {
  display: block;
  font-size: 15px;
  color: var(--mist);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

body.en-visio .line-label { display: none; }

body.en-visio #line-code {
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: .12em;
  color: var(--dim);
}

/* Pendant un appel video, le vumetre a moins a dire que l image : il se fait
   discret plutot que de lui disputer la hauteur. */
body.en-visio .meters { display: none; }

/* En bureau, l image a de la place : on lui donne des proportions d ecran. */
body.bureau .visio { aspect-ratio: 16 / 9; max-height: 52vh; }

/* ------------------------------- le visage sur les ecrans d appel */

/* Le visage a gauche, le numero et le nom a droite : on reconnait quelqu un
   avant de lire quatre chiffres. */
.appel-qui, .line-id {
  display: flex;
  align-items: center;
  gap: 14px;
}

.appel-dit {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.appel-visage { flex: none; display: inline-flex; }

/* Assez grand pour se reconnaitre d un coup d oeil, sans voler la place du
   numero — c est lui qu on dicte et qu on note. */
.avatar-appel { width: 52px; height: 52px; font-size: 17px; }


/* =========================================================================
 * Les boutons devenus des symboles
 *
 * Sur les ecrans d'appel — celui qui sonne comme celui qui parle — et sur
 * l'ecran Telephone. Le mot n'est pas perdu : il vit dans « title » et
 * « aria-label ». Ce qui disparait est le cadre et l'etiquette.
 *
 * Les ecrans Conference et Annonce gardent leurs touches ecrites : ils n'ont
 * pas ete demandes, et l'on ne change pas ce qu'on ne vous a pas propose.
 * D'ou « .key-icone », qui ne vise que les quatre concernees.
 * ====================================================================== */

.key-icone {
  flex: 1;
  flex-direction: column;
  justify-content: center;
  /* Le mot sous le signe (lot 4, 2026-09-27) : colonne, et une pastille plutot qu un rond. */
  gap: 7px;
  padding: 12px 10px;
  background: none;
  border: none;
  border-radius: 16px;
}
.key-icone .key-text { color: var(--dim); }

.key-icone .key-glyph { font-size: 30px; }

/* Le rond ne parait qu'au doigt pose : au repos, rien ne cadre le symbole. */
.key-icone:not(:disabled):active { background: rgba(255, 255, 255, .07); }
.key-icone:focus-visible { outline: 2px solid var(--mist); outline-offset: 2px; }

/* Decrocher en vert, raccrocher et refuser en rouge : la couleur dit l'acte
   avant que le dessin ne soit lu. */
.key-icone.key-take .key-glyph { color: var(--jade); }
.key-icone.key-end { border: none; }
.key-icone.key-end .key-glyph { color: #F0968D; }
.key-icone.key-end:active { background: rgba(225, 88, 75, .16); }


/* Micro coupe : le symbole est barre. Plus de cadre pour porter un etat, donc
   c'est le dessin qui le porte — et c'est la convention partout ailleurs. */
.key-icone[aria-pressed="true"] { background: none; color: inherit; }

.barre { position: relative; }

.barre::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 76%;
  height: 2px;
  background: currentColor;
  transform: translate(-50%, -50%) rotate(-45deg);
  border-radius: 2px;
  pointer-events: none;
}

.key-icone.barre .key-glyph { opacity: .55; }

/* Sur une touche LARGE — elles occupent la moitie de la ligne chacune —, la
   barre doit se poser sur le SYMBOLE et non sur le bouton : sinon elle le
   traverse de part en part au lieu de barrer le dessin. */
.key-icone.barre::after { content: none; }
.key-icone.barre .key-glyph { position: relative; display: inline-block; }

.key-icone.barre .key-glyph::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 118%;
  height: 2px;
  background: currentColor;
  transform: translate(-50%, -50%) rotate(-45deg);
  border-radius: 2px;
  opacity: 1;
}

/* --------------------------------- les commandes de la camera, sur l'image */

.visio-cmd {
  pointer-events: auto;
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  font-size: 19px;
  line-height: 1;
  color: #fff;
  background: rgba(8, 25, 26, .55);
  border: none;
  border-radius: 50%;
  padding: 0;
  cursor: pointer;
}

.visio-cmd:not(:disabled):active { background: rgba(8, 25, 26, .9); }
.visio-cmd.actif { background: rgba(233, 179, 65, .85); color: var(--ink); }
.visio-cmd:disabled { opacity: .45; cursor: not-allowed; }
.visio-cmd:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

/* ------------------------------------------- les trois gestes du Telephone */

/* Cote a cote et centres : trois actions de meme rang, visees du pouce. */
.dial-actions {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 22px;
}

.rond {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 62px;
  height: 62px;
  font-size: 26px;
  line-height: 1;
  color: var(--mist);
  background: var(--lacquer);
  border: 1px solid var(--edge);
  border-radius: 50%;
  cursor: pointer;
}

/* Appeler est le geste principal : lui seul porte la couleur. */
.rond-appel {
  color: var(--jade);
  border-color: var(--jade);
  border-color: color-mix(in srgb, var(--jade) 45%, transparent);
}

.rond:not(:disabled):active { background: var(--edge); }
.rond:disabled { opacity: .4; cursor: not-allowed; }
.rond:focus-visible { outline: 2px solid var(--mist); outline-offset: 3px; }

/* La nature de l appel, en tete de l ecran de decrochage. */
.ring-nature {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.ring-signe { font-size: 15px; line-height: 1; }

/* Un appel video s annonce en ambre : il demande une decision de plus — se
   montrer — et cela doit se voir avant de decrocher. */
.ring-nature.video { color: var(--amber); }


/* ================================================== LA BARRE DE LIENS

   Le chemin entre un salon et les conversations privees qu'on y a nouees.
   Dans le salon elle liste les prives ; dans un prive elle offre le retour
   au salon, puis les autres prives du meme salon. Une seule barre pour les
   deux sens : deux objets auraient demande d'apprendre deux choses. */

.fil-liens {
  display: flex;
  gap: 6px;
  padding: 7px 12px;
  border-bottom: 1px solid var(--edge);
  /* Elle defile plutot que de passer a la ligne : une barre qui grandit
     ferait sauter la conversation chaque fois qu'un lien s'ajoute. */
  overflow-x: auto;
  scrollbar-width: none;
}

.fil-liens::-webkit-scrollbar { display: none; }

/* « display: flex » l'emporterait sur l'attribut « hidden », et la barre
   resterait visible et vide. Le piege est connu, la parade aussi. */
.fil-liens[hidden] { display: none; }

.lien {
  flex: none;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 5px 11px 5px 6px;
  background: var(--lacquer);
  border: 1px solid var(--edge);
  border-radius: 999px;
  color: var(--mist);
  font-size: .8rem;
  cursor: pointer;
}

.lien:hover { border-color: rgba(70, 201, 154, .45); }
.lien:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

.lien-nom {
  max-width: 11ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Le retour au salon porte le jade, comme tout ce qui mene a quelqu'un.
   Il n'a pas d'avatar : c'est un lieu, pas une personne. */
.lien-salon {
  padding-left: 11px;
  color: var(--jade);
  border-color: rgba(70, 201, 154, .45);
}

.lien-compte {
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: var(--amber);
  color: var(--ink);
  font-family: var(--mono);
  font-size: .62rem;
  font-weight: 700;
}

/* Un message vient d'arriver : le bouton bat. Seul le FOND change — une
   animation qui touche a la taille ou a la position ferait sauter toute la
   barre, et le voisin qu'on visait se deroberait sous le pouce. */
.lien.neuf {
  border-color: var(--amber);
  color: var(--amber);
  animation: lien-bat 1.5s ease-in-out infinite;
}

@keyframes lien-bat {
  0%, 100% { background: var(--lacquer); }
  50%      { background: rgba(233, 179, 65, .22); }
}

@media (prefers-reduced-motion: reduce) {
  /* On ne clignote pas pour qui a demande le calme : la teinte suffit a dire
     qu'il y a du neuf. */
  .lien.neuf { animation: none; background: rgba(233, 179, 65, .22); }
}

/* ------------------------------------------------ Un prive PAR-DESSUS un salon

   Le salon n'est pas quitte : il passe DERRIERE, assombri et inerte, pendant
   que la conversation privee se pose sur le bas de l'ecran comme une feuille.
   On voit donc toujours d'ou l'on vient, et l'on y retourne d'un doigt sur le
   decor.

   Le decor n'est pas une copie : ce sont les vraies bulles du salon, deplacees
   la le temps de l'aparte et reprises telles quelles en refermant. Rien n'est
   redessine, et les lecteurs audio ou video ouverts gardent leur etat. */

.fil-derriere {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  /* La meme mise en page que le fil, sans quoi le decor ne ressemblerait pas
     a ce qu'on vient de quitter. */
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 12px 10px;
  cursor: pointer;
  /* Assombrir par un filtre plutot que par un voile pose dessus : « ::after »
     deviendrait un element de la colonne flex et se rangerait sous les bulles
     au lieu de les couvrir. Le filtre, lui, prend tout d'un coup. */
  filter: brightness(.42) saturate(.75);
}

.fil-derriere[hidden] { display: none; }

/* Le decor court sous tout le panneau : l'en-tete et la barre de liens
   doivent donc devenir opaques, sinon on verrait des bulles derriere eux. */
.panel-fil.sur-le-salon .fil-tete,
.panel-fil.sur-le-salon .fil-liens {
  position: relative;
  z-index: 1;
  background: var(--ink);
}

/* La feuille, et la bande de salon qu'elle laisse voir au-dessus d'elle.

   La bande se reserve en unites absolues, et non en pourcentage : un
   « flex-basis » en % exige une hauteur DEFINIE du conteneur, et le panneau
   est parfois dimensionne par son contenu — la feuille tombait alors a la
   hauteur de ses messages, et la bande a quarante pixels.

   Aucune marge : la feuille monte au plus haut, contre la barre de liens.
   Sur telephone, la conversation privee est ce qu'on lit ; le salon n'est
   qu'un rappel, et une bande d'un pouce en haut donnait a la feuille l'air
   de flotter au milieu de rien.

   Il reste malgre tout une lisiere d'une quarantaine de pixels — l'espacement
   propre au panneau, qui separe deja ses elements. Elle laisse voir le haut
   d'une bulle assombrie derriere le liseret ambre : juste de quoi dire ou
   l'on est pose, sans prendre de place a ce qu'on lit.

   Une valeur en « dvh » a ete essayee puis retiree : elle demandait un
   plancher, parce qu'une unite de viewport vaut zero dans une page qui n'est
   pas affichee. Zero n'a pas ce probleme.

   Et un PLANCHER, parce qu'une unite de viewport peut valoir zero : dans une
   page qui n'est pas affichee, « innerHeight » vaut 0 et « 26dvh » avec lui.
   « min » choisissait alors zero, la bande disparaissait, et l'on se
   retrouvait avec le comportement d'avant sans qu'aucune regle n'ait l'air
   fausse. Le « max » garantit qu'il reste toujours de quoi voir le salon. */
/* « #chat-corps » et non « .fil-corps » : l identifiant porte deja un rayon
   de 8 px, et un selecteur d ID bat trois classes. La meme collision de
   cascade avait deja fait gagner « .fil-tete » sur « .fil-tete-chat ». */
.panel-fil.sur-le-salon #chat-corps {
  flex: 1;
  min-height: 0;
  margin-top: 0;
  position: relative;
  z-index: 1;
  background: var(--ink);
  border-top: 1px solid rgba(233, 179, 65, .55);
  border-radius: 14px 14px 0 0;
}

.panel-fil.sur-le-salon .fil-saisie {
  position: relative;
  z-index: 1;
  background: var(--ink);
}

.panel-fil.sur-le-salon #chat-corps,
.panel-fil.sur-le-salon .fil-saisie {
  animation: volet-monte .28s cubic-bezier(.22, .7, .3, 1);
}

@keyframes volet-monte {
  from { transform: translateY(24px); opacity: .3; }
  to   { transform: none;             opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  /* Le liseré ambre suffit a dire ou l'on est ; le mouvement, lui, se refuse. */
  .panel-fil.sur-le-salon #chat-corps,
  .panel-fil.sur-le-salon .fil-saisie { animation: none; }
}

/* ------------------------------------------- Qui est la, dans le salon

   La meme pastille que partout ailleurs, en plus petite, devant le pseudo.
   Elle ne decore pas : c'est elle qui annonce si un appui long ouvrira une
   conversation ou repondra qu'il n'y a personne. */

.dot-mini {
  width: 7px;
  height: 7px;
  /* Elle ne doit pas retrecir quand un pseudo long occupe la ligne. */
  flex: none;
}

/* Le pseudo est ce qu'on maintient enfonce : il se donne comme touchable. */
.auteur-joignable { cursor: pointer; }

/* Sans cela, un appui prolonge sur Android selectionne le texte et sort sa
   propre poignee de selection par-dessus la notre. Le geste est a nous. */
.bulle .auteur {
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

/* Un salon : la bulle entiere est le geste.

   Couper la selection du texte n'est pas un detail de confort. Sur Android,
   un appui prolonge sur du texte selectionnable ouvre la loupe et ses
   poignees vers 300 ms, soit AVANT notre propre minuterie de 480 : les deux
   se declenchaient l'un sur l'autre, et le volet s'ouvrait derriere une barre
   de selection qu'on n'avait pas demandee.

   Ce que cela coute : on ne peut plus selectionner un bout de message du doigt
   dans un salon. Le menu du haut copie toujours la conversation entiere. */
.fil-corps.dans-un-salon {
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

/* Et la bulle se donne comme touchable, puisqu'elle l'est desormais toute. */
.fil-corps.dans-un-salon .bulle { cursor: pointer; }

/* Deux conversations avec la meme personne : l'ordinaire, et celle nee dans un
   salon. Sans cette mention elles se ressembleraient a s'y meprendre, et l'on
   repondrait dans l'une en croyant repondre dans l'autre. */
.fil-origine {
  display: inline-block;
  margin-left: 2px;
  padding: 1px 6px;
  border-radius: 999px;
  background: rgba(233, 179, 65, .14);
  border: 1px solid rgba(233, 179, 65, .38);
  color: var(--amber);
  font-family: var(--mono);
  font-size: .6rem;
  letter-spacing: .04em;
  vertical-align: middle;
  /* Un nom de salon long ne doit pas repousser l'apercu du message. */
  max-width: 12ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ------------------------------------------------------------- Un accuse

   « Copié », « Effacé », « Transféré » : la confirmation de ce qu'on vient de
   faire, et non l'annonce de ce qui arrive.

   Elle se pose EN BAS, pres du geste et du pouce, et non en haut ou elle
   recouvrait l'en-tete de la conversation — c'est-a-dire le nom de l'endroit
   ou l'on venait justement d'agir.

   Elle epouse son texte au lieu de barrer l'ecran : deux mots dans une bande
   de 420 px se lisent comme une alerte, alors que ce n'est qu'un accuse. */

.toast.accuse {
  top: auto;
  /* Au-dessus de la zone de saisie, et de la barre du systeme sur telephone. */
  bottom: calc(84px + env(safe-area-inset-bottom));
  width: auto;
  max-width: min(420px, calc(100vw - 32px));
  /* Un plancher : « width: auto » laisse une pilule se reduire a ses marges
     si la largeur du viewport est mal connue — c'est arrive au banc d'essai,
     et rien n'interdit qu'un navigateur reel ait le meme hoquet au premier
     rendu. Deux mots ont droit a une pastille, pas a un sliver. */
  min-width: 150px;
  align-items: center;
  padding: 10px 16px;
  border-radius: 999px;
  /* Un peu moins opaque que le bandeau d'annonce : il passe, il ne s'impose
     pas. Assez peu, toutefois, pour rester lisible sur n'importe quel fond —
     le contraste du texte reste au-dessus de 7 pour 1. */
  background: rgba(31, 72, 73, .94);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .45);
  animation: accuse-monte .18s ease-out;
}

/* Le titre ne se coupe pas : « Copié » et « Effacé » se lisent d un bloc. */
.toast.accuse #toast-titre { color: var(--jade); flex: none; }

@keyframes accuse-monte {
  from { transform: translate(-50%, 8px); opacity: 0; }
  to   { transform: translate(-50%, 0);   opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .toast.accuse { animation: none; }
}

/* ------------------------------------------- L'annuaire, sur deux etages

   Le nom en haut, l'etat en dessous, tous deux centres sur l'avatar. Ce qui
   qualifie le poste — son numero, son telephone, par quelle porte il entre —
   accompagne le nom ; ce qui change d'une minute a l'autre reste en bas. */

.book-row .avatar { width: 40px; height: 40px; font-size: 13px; }

.book-txt {
  /* Base ZERO, et non « auto ».
     La fiche est dans un conteneur qui enveloppe — il le faut, pour que la
     bande d'actions de l'administration passe en pleine largeur. Or un tel
     conteneur decide de replier d'apres la taille NATURELLE de chaque
     element, avant toute compression : avec « auto », un nom long donnait au
     bloc une largeur naturelle enorme, et le bloc entier basculait sous
     l'avatar — 100 px de haut au lieu de 58, a 320 px de large.
     Avec une base nulle, il ne force jamais le repli : il prend la place qui
     reste, et c'est le nom qui cede en ellipse. C'est ce que fait deja la
     liste des messages, d'ou son immunite. */
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* La premiere ligne ne se replie JAMAIS : un poste doit tenir sur deux etages,
   quel que soit son nom. Une fiche qui grandit selon la longueur d'un prenom
   fait respirer la liste de travers — trente postes, trente hauteurs.

   C'est le nom qui cede en premier, en ellipse : c'est le plus long, et le
   seul dont on devine la fin. Le telephone cede ensuite, si le nom a deja
   atteint son plancher. Le numero et la porte, eux, ne cedent pas : tronquer
   un identifiant vaut moins que de ne pas le montrer. */
.book-l1 {
  display: flex;
  align-items: baseline;
  flex-wrap: nowrap;
  gap: 6px;
  min-width: 0;
}

.book-l2 {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.book-row .book-nom {
  flex: 1 1 auto;
  /* Un plancher : quatre caracteres et des points de suspension valent mieux
     qu'un nom reduit a rien du tout. */
  min-width: 4ch;
  font-size: .96rem;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Le numero reste lisible : dans un annuaire, c'est souvent lui qu'on cherche.
   Plus discret que le nom, plus present que dans la liste des messages. */
.book-row .book-num {
  flex: none;
  font-size: .82rem;
  letter-spacing: .08em;
  font-weight: 500;
}

.book-row .book-etat {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Le telephone cede a son tour, mais apres le nom : il est plus court, et
   c'est un numero — on n'en devine pas la fin. */
.book-row .book-tel {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* -------------------------------------- Un appel reste sans reponse

   La fleche dit deja le sens ; barree, elle dit qu'il n'y a pas eu de
   reponse. Un seul signe pour deux informations : deux symboles cote a cote
   se seraient disputes le coup d'oeil, dans une liste ou l'on ne fait que
   passer.

   Barree pour tout ce qui n'a pas abouti — manque, refuse, occupe,
   injoignable — mais rouge pour le seul cas qui appelle une action : un
   appel qu'ON a manque. Ce qu'on emet et qui reste sans reponse se lit sans
   s'alarmer. */

.log-sens { position: relative; }

.log-sans-reponse::after {
  content: '';
  position: absolute;
  left: -2px;
  right: -2px;
  top: 50%;
  height: 1.5px;
  background: currentColor;
  border-radius: 1px;
  transform: rotate(-35deg);
  transform-origin: center;
}

.log-manquee { color: var(--coral); }

/* ------------------------------------------- Le journal, sur deux etages

   En haut QUI, en bas CE QUI S'EST PASSE, et en face le jour puis l'heure.
   Le jour d'abord : c'est par lui qu'on situe un appel, l'heure ne precise
   qu'ensuite. */

.log-cote {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  /* Les heures se lisent en colonne : elles doivent s'aligner d'une ligne a
     l'autre, et non danser au gre de la largeur des chiffres. */
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.log-jour {
  font-size: .72rem;
  color: var(--mist);
}

.log-heure {
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .04em;
  color: var(--dim);
}

/* Toute la ligne est la porte : elle doit le dire, au survol comme au clavier. */
.log-row[data-ouvrir] { cursor: pointer; }
.log-row[data-ouvrir]:hover { background: var(--lacquer); }
.log-row[data-ouvrir]:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

/* Le menu d'un appel. Il se pose sous la ligne visee ; « top » est calcule au
   moment de l'ouvrir, ce qui suppose un panneau qui serve de repere. */
.panel#panel-log { position: relative; }

.menu-log {
  top: 0;
  right: 8px;
  min-width: 180px;
}

/* Le jour, intercale entre les appels. Dans une liste, un separateur est un
   element de liste : glisser un « div » entre deux « li » ferait un balisage
   invalide, et le lecteur d'ecran y perdrait son compte. */
.log-jour-sep {
  display: flex;
  justify-content: center;
  list-style: none;
}

/* Moins d'air que dans une conversation : le journal est une liste dense, et
   la marge de 16 px y creusait un trou a chaque changement de jour. */
.log-jour-sep .jour { margin: 10px 0 6px; }

/* ------------------------------- Choisir des conversations dans la liste

   Meme geste et meme allure que dans une conversation : un appui long
   designe, le menu du haut agit. Les regles sont jumelles de celles de
   « .fil-corps.choisir », a ceci pres qu'ici ce sont des lignes et non des
   bulles. */

.panel#panel-chats { position: relative; }

/* Le menu se pose sous les trois points, en haut a droite du panneau. */
.menu-chats { top: 34px; right: 0; min-width: 230px; }

/* Les trois points ne portent ni cadre ni fond : c'est un signe, pas un
   bouton de plus dans une barre qui en compte deja. */
.fils-menu {
  border: none;
  background: none;
  padding: 4px 8px;
  font-size: 17px;
  line-height: 1;
}

/* Pendant le choix, on coche : on ne selectionne pas du texte par megarde, et
   un appui long n'appelle pas la loupe du telephone. */
.fils.choisir {
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

.fils.choisir .fil-ligne { cursor: pointer; }

/* La ligne choisie se cerne d'ambre, comme la bulle choisie. Un liseret
   plutot qu'un fond : le fond sert deja a dire quelle conversation est
   ouverte a cote, en mode bureau, et les deux se confondraient. */
.fils.choisir .fil-ligne.choisie {
  box-shadow: 0 0 0 2px var(--amber);
}

/* ------------------------------- « Une nouvelle version est disponible »

   Une page installee ne se recharge pas d'elle-meme, et sa fenetre n'a pas de
   barre d'adresse pour l'y forcer. Le bandeau tient donc en haut, au-dessus de
   tout, et ne s'efface pas tout seul : une version perimee ne cesse pas de
   l'etre parce qu'on a regarde ailleurs. */

.maj-ui {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: max(8px, env(safe-area-inset-top)) 14px 8px;
  background: rgba(233, 179, 65, .94);
  color: var(--ink);
  font-size: .82rem;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .35);
}

.maj-ui[hidden] { display: none; }

/* Le bouton se detache sur l'ambre : le style ordinaire, taille pour un fond
   sombre, y serait illisible. */
.maj-ui .btn-mini {
  flex: none;
  border-color: rgba(8, 25, 26, .45);
  color: var(--ink);
  font-weight: 600;
}

.maj-ui .btn-mini:not(:disabled):active { background: rgba(8, 25, 26, .12); }


/* ============================================ LE VOLET « À QUI ? »

   Il n'avait aucune mise en forme : ses lignes portaient une classe absente
   de cette feuille, et se rendaient donc en texte brut, avatar collé au
   numéro. C'était la seule liste de l'application dans ce cas.

   Elle prend ici la forme des trois autres — visage, deux étages —, avec en
   plus ce qu'un choix demande : des sections, une pastille qui dit ce qui
   est retenu, et un pied qui rappelle ce qu'on s'apprête à envoyer. */

.feuille-choix {
  /* Presque tout l'écran : on choisit dans une liste, pas dans une lucarne. */
  max-width: 460px;
  height: min(88vh, 720px);
  padding: 14px 12px 12px;
  gap: 10px;
}

.feuille-tete {
  display: flex;
  align-items: center;
  gap: 10px;
}

.feuille-retour {
  flex: none;
  border: none;
  background: none;
  padding: 4px 8px;
  font-size: 18px;
  line-height: 1;
}

.feuille-tete .voile-titre { flex: 1; min-width: 0; }

.feuille-corps {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 1px;
  /* De quoi loger l'anneau de la ligne retenue sans qu'il soit rogné. */
  padding: 2px;
  margin: 0 -2px;
}

/* Le titre d'une section. Discret : il classe, il n'annonce pas. */
.choix-section {
  padding: 12px 8px 5px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--dim);
}

.choix-section:first-child { padding-top: 2px; }

/* Une ligne : visage, deux étages, pastille. La même forme que la liste des
   messages et l'annuaire — on ne réapprend rien d'un écran à l'autre. */
.choix-ligne {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 8px;
  background: none;
  border: none;
  border-radius: 10px;
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
}

.choix-ligne:hover { background: var(--lacquer); }
.choix-ligne:focus-visible { outline: 2px solid var(--amber); outline-offset: -2px; }

.choix-ligne .avatar { width: 40px; height: 40px; font-size: 13px; }

/* Base nulle, comme partout ailleurs : sans elle un nom long donnerait au
   bloc une largeur naturelle énorme, et la pastille serait poussée dehors. */
.choix-txt {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.choix-nom {
  font-size: .96rem;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.choix-dessous {
  font-size: .8rem;
  color: var(--dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* La pastille. Vide, c'est un anneau ; retenue, elle se remplit et porte la
   coche. Rien ne bouge de place entre les deux états — une liste où les
   lignes sautent au moment du choix se vise mal. */
.choix-coche {
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 2px solid var(--edge);
  display: flex;
  align-items: center;
  justify-content: center;
}

.choix-ligne.retenu .choix-coche {
  background: var(--jade);
  border-color: var(--jade);
}

.choix-ligne.retenu .choix-coche::after {
  content: '✓';
  color: var(--ink);
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
}

.choix-ligne.retenu { background: rgba(70, 201, 154, .10); }

/* Le pied : ce qu'on envoie, à combien, et le bouton pour le faire. */
.feuille-pied {
  flex: none;
  display: flex;
  align-items: center;
  gap: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--edge);
}

.feuille-quoi {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: .84rem;
  color: var(--dim);
}

.feuille-envoi { flex: none; }
.feuille-envoi:disabled { opacity: .35; cursor: default; }

/* ------------------------------------------------------------- La langue

   Sous les tuiles de l'accueil, discret mais lisible sans savoir ou regarder :
   quelqu'un qui ne lit pas le francais doit trouver ce choix SANS avoir a
   comprendre le reste de la page. C'est pourquoi il n'est pas dans les
   reglages, derriere une porte qu'il faudrait d'abord savoir nommer. */
.langues {
  display: flex;
  gap: 8px;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 18px;
}
/* Dans les reglages, le choix suit l'alignement du formulaire : centre, il
   flotterait seul au milieu d'une colonne ou tout part de la gauche. */
.langues-reglages { justify-content: flex-start; margin-top: 6px; margin-bottom: 14px; }

.langue {
  appearance: none;
  border: 1px solid var(--edge);
  background: transparent;
  color: var(--dim);
  font-family: var(--sans);
  font-size: 12px;
  letter-spacing: .04em;
  padding: 6px 14px;
  border-radius: 999px;
  cursor: pointer;
  transition: color .15s, border-color .15s, background .15s;
}
.langue:hover { color: var(--mist); border-color: var(--jade); }
.langue:focus-visible { outline: 2px solid var(--jade); outline-offset: 2px; }
/* Celle qui est en cours ne se contente pas d'une couleur : la teinte seule
   se perd pour qui la distingue mal. */
.langue-mienne {
  color: var(--ink);
  background: var(--jade);
  border-color: var(--jade);
  font-weight: 600;
}


/* ===========================================================================
 * La conversation tient dans l'ecran
 *
 * Rien ne bornait la hauteur du fil : quarante messages donnaient une page de
 * quatre mille pixels, et TOUT partait vers le haut — l'en-tete, le mot
 * « ecrit… », et jusqu'au champ de saisie qu'il fallait aller chercher tout en
 * bas. Le fil « defilait » a l'interieur d'un cadre qui n'avait pas de fin.
 *
 * On borne la page a la hauteur de la fenetre, et c'est le corps du fil — lui
 * seul — qui defile. L'en-tete reste en haut, la saisie en bas.
 *
 * « dvh » et non « vh » : sur mobile, vh compte le viewport large, celui qu'on
 * a barre d'adresse repliee, et la saisie passerait sous le bord.
 * ======================================================================== */
body.fil-plein {
  /*
   * LE REPLI D'ABORD, LA BONNE VALEUR ENSUITE.
   *
   * « dvh » date de Chrome 108 (fin 2022). Une WebView Android plus ancienne
   * jette la declaration — et sans repli, ce corps n'avait plus AUCUNE hauteur :
   * « min-height: 0 » et « overflow: hidden » achevaient le travail, et l'ecran
   * de conversation se repliait a rien. C'est le genre de panne qui ne se voit
   * que sur le telephone, et jamais au bureau.
   *
   * La ligne 45 avait deja ce repli ; celle-ci ne l'avait pas.
   */
  height: 100vh;
  height: 100dvh;
  min-height: 0;
  overflow: hidden;
}

/* Sans « min-height: 0 », un enfant flex refuse de retrecir sous la taille de
   son contenu : la colonne garderait la hauteur des quarante messages, et
   l'on n'aurait rien borne du tout. */
body.fil-plein .phone {
  height: 100%;
  min-height: 0;
}

body.fil-plein .duo,
body.fil-plein .panel-fil {
  min-height: 0;
  flex: 1;
}


/* ===========================================================================
 * L'etagere d'une station
 *
 * Des adresses que la station met a disposition, et que l'auditeur ouvre
 * lui-meme. Repliees : une station qui en porte vingt ne doit pas repousser
 * les autres hors de l'ecran.
 * ======================================================================== */
.station-liens {
  list-style: none;
  margin: -4px 0 8px;
  padding: 0 0 0 14px;
  border-left: 2px solid var(--edge);
}

.station-liens summary {
  cursor: pointer;
  font-size: 12px;
  color: var(--dim);
  padding: 4px 0;
}
.station-liens summary:hover { color: var(--mist); }

.station-liens ul {
  list-style: none;
  margin: 4px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.station-liens li {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}

.station-liens a {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--jade);
  font-size: 13px;
}

/* Ou cela mene. On ne clique pas a l'aveugle sur une adresse qu'un autre poste
   a posee : l'hote se lit avant, en petit. */
.station-lien-ou {
  flex: none;
  font-family: var(--mono);
  font-size: 10px;
  color: var(--dim);
}

/* Le mot qui dit pourquoi la liste est vide. Il ne se referme pas : c'est un
   ETAT qui dure, pas une explication qu'on lit une fois. */
.annuaire-ferme {
  border-left: 2px solid var(--amber);
  padding-left: 12px;
  margin: 12px 0;
}

/* ---------------------------------------------------------------------------
 * PASSER L'APPEL — les deux bandeaux
 *
 * Ils vivent DANS l'ecran d'appel, entre l'etat de la ligne et les touches :
 * c'est la qu'on regarde quand le son s'arrete. Plus bas, sous les touches, ils
 * seraient hors du champ sur un telephone.
 *
 * DEUX COULEURS, ET C'EST LA TOUTE L'INFORMATION. Ambre chez le relais : il y a
 * quelque chose a faire, quelqu'un attend. Jade chez celui qu'on tient : rien a
 * faire, rien de casse — c'est un etat, pas une alerte. Le mot le dit aussi,
 * mais la couleur se lit avant le mot.
 * ------------------------------------------------------------------------- */
.attente {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin-top: -6px;
  padding: 10px 12px;
  border: 1px solid var(--edge);
  border-left-width: 3px;
  border-radius: 3px;
  background: var(--lacquer);
}

.attente .hint { flex-basis: 100%; }

.attente-mot {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 14px;
  color: var(--mist);
}

.attente-cmd { display: flex; gap: 8px; }

/* Ambre : quelqu un attend une decision de vous. */
.attente-relais { border-left-color: var(--amber); }
.attente-relais .attente-mot { color: var(--amber); }

/* Jade : vous n avez rien a faire, et rien n est casse. */
.attente-moi { border-left-color: var(--jade); }
.attente-moi .attente-mot { color: var(--jade); animation: pulse 1.6s ease-in-out infinite; }

/*
 * LE CHOIX DE LA FAÇON, en tete de la fenetre.
 *
 * « flex: none » est herite de « .seg », qui vit d ordinaire dans une rangee.
 * Ici il est seul sur sa ligne et doit en prendre toute la largeur, sans quoi
 * les deux boutons se serrent a gauche et l on ne voit plus lequel est choisi.
 */
#passer-facon { width: 100%; }
#passer-facon .seg-btn { flex: 1; text-align: center; }

/* ---------------------------------------------------------------------------
 * LA FILE D'ATTENTE DU STANDARD
 *
 * La liste vit sur l'ecran d'accueil, au-dessus des tuiles : c'est ce qu'on
 * regarde en premier quand on tient le standard, et cela ne se cherche pas.
 * Elle ne parait que lorsque quelqu'un attend — une liste vide en permanence
 * finirait par ne plus se voir du tout.
 * ------------------------------------------------------------------------- */
.file-bloc {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
  border: 1px solid var(--edge);
  border-left: 3px solid var(--amber);
  border-radius: 3px;
  background: var(--lacquer);
}

.file-tete {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.file-tete .field-label { margin: 0; color: var(--amber); }

.file-liste { margin: 0; }

/*
 * LES INVITATIONS DANS UN GROUPE, sur l'accueil (2026-09-14). Chaque rang est
 * une colonne (« .salon-rang ») ; « .book-actions » y gardait sa base de 100 %,
 * prevue pour une ligne, et devenait une HAUTEUR : mesure, deux boutons de
 * 103 pixels qui debordaient du cadre.
 */
#invitations-bloc .field-label { margin: 0; color: var(--amber); }
#invitations-liste { margin: 0; }
#invitations-liste .salon-rang:last-child { border-bottom: 0; padding-bottom: 0; }
#invitations-liste .book-actions { flex: none; justify-content: flex-start; }

/* Chaque ligne porte son rang, comme un numero de ticket. */
.file-rang {
  flex: none;
  min-width: 22px;
  font-family: var(--mono);
  font-size: 13px;
  color: var(--amber);
  text-align: right;
}

/*
 * ET LE BANDEAU DE CELUI QUI PATIENTE.
 *
 * Ambre, comme la liste, et non jade : ce n'est pas le meme etat qu'une mise en
 * attente par quelqu'un. La, on est tenu par une personne qui va revenir ; ici
 * l'on fait la queue, et le mot doit le dire aussi clairement que la couleur.
 */
.attente-file { border-left-color: var(--amber); }
.attente-file .attente-mot { color: var(--amber); animation: pulse 1.6s ease-in-out infinite; }

/* ====================================== LA PAGE D'INFORMATIONS (2026-09-19)
 *
 * LE BANDEAU : pleine largeur, au-dessus de la grille, et un filet ambre a
 * gauche — la meme ecriture que « la commande prete » au comptoir : ce qui
 * demande un regard porte un filet, ce qui s'actionne porte une tuile. */
.infos-bandeau {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 13px 14px;
  background: var(--lacquer);
  border: 1px solid var(--edge);
  border-left: 3px solid var(--amber);
  border-radius: 3px;
  color: var(--mist);
  text-align: left;
  font: inherit;
  cursor: pointer;
}
.infos-bandeau:active { background: var(--edge); }
@media (hover: hover) { .infos-bandeau:hover { background: var(--edge); } }
.infos-bandeau:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }
.infos-signe { width: 22px; height: 22px; flex-shrink: 0; color: var(--amber); fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.infos-bandeau-txt { flex-grow: 1; display: flex; flex-direction: column; gap: 3px; min-width: 0; }
/* Un titre long ne pousse pas la pastille hors du bandeau : il se coupe. */
.infos-bandeau-titre { font-family: var(--mono); font-size: 11px; letter-spacing: .15em; text-transform: uppercase; color: var(--amber); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.infos-bandeau-note { font-size: 12px; line-height: 1.35; color: var(--dim); }
.infos-point { width: 8px; height: 8px; flex-shrink: 0; border-radius: 50%; background: var(--coral); }

/* L'ECRAN. Une page qu'on lit : de la place entre les blocs, et rien qui
   ressemble a un bouton sauf ce qui en est un. */
.panel-infos { gap: 18px; }
.infos-titre { margin: 0; font-size: 25px; line-height: 1.2; font-weight: 600; color: var(--amber); text-wrap: balance; }
.infos-quand { font-family: var(--mono); font-size: 11px; letter-spacing: .1em; color: var(--dim); }
.infos-blocs { display: flex; flex-direction: column; gap: 18px; }
.infos-texte { margin: 0; font-size: 15px; line-height: 1.55; white-space: pre-line; }
.infos-media { display: flex; flex-direction: column; gap: 7px; }
.infos-media img, .infos-media video { display: block; width: 100%; height: auto; border: 1px solid var(--edge); border-radius: 3px; background: var(--lacquer); }
/* « color-scheme » : le lecteur du navigateur se dessine alors en sombre,
   au lieu de poser une pilule blanche au milieu d une page noire. C est la
   maniere prevue pour cela — un filtre inverserait aussi ses icones. */
.infos-media audio { width: 100%; color-scheme: dark; }
.infos-legende { font-size: 12px; line-height: 1.4; color: var(--dim); }
.infos-lien {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  align-self: flex-start;
  padding: 12px 14px;
  min-height: 44px;
  box-sizing: border-box;
  border: 1px solid var(--edge);
  border-radius: 3px;
  background: var(--lacquer);
  color: var(--jade);
  text-decoration: none;
  font-size: 14px;
}
.infos-lien svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }

/* La legende d un geste de la fiche du contact (lot 4, 2026-09-27). */
.fiche-geste-mot {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: inherit;
  opacity: .85;
}
