/* socle.css — les primitives d'écran du site.
 *
 * POURQUOI UNE SECONDE FEUILLE PARTAGÉE, À CÔTÉ DE city.css
 *
 * `city.css` habille les cinq pages d'origine — cartes, formulaires en grille,
 * tableaux de chiffres. Ce socle-ci porte le langage de `contacts.php`, jugé
 * référence le 09/09/2026 : listes denses, filtres en pastilles, tiroir
 * d'édition sous la ligne, un seul accent. Les deux cohabitent le temps que les
 * pages basculent une par une, chacune par sa `v2`.
 *
 * TOUT EST PORTÉ PAR `.ui`, un conteneur. C'est ce qui permet aux deux feuilles
 * de vivre dans la même page sans se marcher dessus — `nav.php` amène `city.css`
 * partout, et `.flash` par exemple existe des deux côtés.
 *
 * CE QUI N'EST PAS ICI : ce qui n'appartient qu'à une page. Un marqueur « lien
 * rompu » est une affaire de contacts, pas une primitive. Le jour où une
 * deuxième page en a besoin, il remonte — pas avant, sinon ce socle devient le
 * dépotoir que `city.css` a failli devenir.
 *
 * Polices système uniquement, comme partout dans ce projet : aucune requête vers
 * un fournisseur de fontes, qui verrait sinon passer chaque consultation.
 */
/* UNE BARRE DE DÉFILEMENT SUR UNE PAGE QUI NE DÉBORDE PAS — 29/09/2026,
 * signalé. `.ui` portait `min-height: 100vh`, pour que le papier beige
 * remplisse l'écran même sur une fiche courte. Mais `.ui` n'est pas seul dans
 * `<body>` : `nav.php` pose une `<nav class="nav-bar">` juste avant lui, et
 * cette barre est `position: sticky` — donc bien dans le flux, avec sa
 * propre hauteur (57 px). Le total valait donc TOUJOURS nav + 100vh, soit
 * 100vh PLUS 57 px de trop, quelle que soit la page : une barre de
 * défilement de 57 px garantie, y compris quand il n'y a rien en dessous du
 * pli à faire défiler.
 *
 * `body:has(> .ui)` REND `.ui` RESPONSABLE DE SA PROPRE PART, pas de la
 * hauteur totale. En flex-colonne, `body` porte le `min-height: 100vh` — la
 * bonne question, « la page entière doit couvrir l'écran » — et `.ui` se
 * contente de `flex: 1`, « prends ce qu'il reste après la nav ». La nav
 * garde sa hauteur naturelle, quelle qu'elle soit, et plus rien ne la compte
 * deux fois.
 *
 * `:has()` PLUTÔT QU'UNE RÈGLE SUR `body` TOUT COURT : trois pages seulement
 * posent `.ui` en enfant direct de `body` (biens.php, contacts.php,
 * diagnostic.php) — les pages plus anciennes, bâties sur `.shell`/`.page` de
 * `city.css`, n'ont pas à changer de mode de mise en page pour un bug
 * qu'elles ne partagent pas. Sans `:has()` (un navigateur ancien), `body`
 * reste un bloc ordinaire et `.ui` perd seulement le remplissage du papier
 * sur une page courte — un défaut cosmétique mineur, jamais la barre de
 * défilement qu'on corrige ici. */
body:has(> .ui) { display: flex; flex-direction: column; min-height: 100vh; }

.ui {
  --papier:   #f4f3ef;
  --surface:  #fffefc;
  --encre:    #16181c;
  --sourdine: #6c6963;
  --pale:     #9b968d;
  --trait:    #e5e1d9;
  --trait-2:  #d5d0c5;
  /* L'accent est un vert-de-gris CLAIR, pas une encre. Le premier essai
     (#1d4f4a) donnait un bouton « Enregistrer » qui se lisait comme un bloc
     sombre posé sur du papier : 7,9:1 de contraste, soit deux fois ce que la
     lisibilité demande, payé en lourdeur. #0f766e tient 4,8:1 sur blanc — au-
     dessus du seuil, et vivant. */
  --accent:   #0f766e;
  --accent-2: #0b5f58;
  --teinte:   #e2f0ee;
  --ambre:    #8a5a10;
  --rayon:    10px;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  background: var(--papier);
  color: var(--encre);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.5;
  flex: 1;
  -webkit-font-smoothing: antialiased;
}
.ui *, .ui *::before, .ui *::after { box-sizing: border-box; }
.ui ::selection { background: var(--teinte); color: var(--accent); }
.ui :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.ui a { color: inherit; text-decoration: none; }
/* `color: inherit` sur le sélecteur d'élément vaut 0-1-1 en spécificité et
   écrasait le `#fff` de `.ui-btn-plein`, qui ne vaut que 0-1-0 : le bouton
   plein rendait donc du NOIR sur son fond d'accent. C'est ce qui l'a fait
   passer pour « sombre » — ce n'était pas la couleur du fond, c'était le
   texte. Les deux règles sont séparées pour que ça ne se reproduise pas. */
/* ET LE MÊME PIÈGE VALAIT POUR LA TAILLE, sans qu'on l'ait jamais vu. `font:
   inherit` est un raccourci : il pose aussi `font-size`, à 0-1-1 — donc il
   écrasait `.ui-btn` (13,5 px) et `.ui-btn-petit` (12,5 px), qui ne valent que
   0-1-0. **Les tailles des boutons du socle n'ont jamais servi** : chaque bouton
   prenait celle de son conteneur, 14 px presque partout.

   Constaté le 10/09/2026 sur deux boutons voisins de la ligne photo — une
   étiquette à 12,5 px à côté d'un bouton à 13 px, pour la même classe. Un écart
   d'un demi-pixel de hauteur qui ne s'explique par aucune règle lisible.

   LE DÉPLIER NE SUFFISAIT PAS : première tentative, `font-family` + `font-weight`
   + `font-style`, et la GRAISSE a repris le rôle de la taille — `.ui-btn` demande
   500, le reset imposait l'héritage à 0-1-1, et un `<label class="ui-btn">` se
   retrouvait plus gras que le `<button>` d'à côté. Le même bug, déplacé d'une
   propriété.

   Il ne reste donc que la FAMILLE, la seule chose qu'un contrôle n'hérite pas
   du navigateur et qu'aucune primitive n'a de raison de redire. Tout le reste —
   taille, graisse — appartient à `.ui-btn` et compagnie.

   Corollaire de la note ci-dessus, et plus large qu'elle : dans une remise à
   zéro, se méfier autant des RACCOURCIS que des propriétés visuelles. Un
   raccourci pose des propriétés qu'on n'a pas nommées, à une spécificité que
   les primitives ne peuvent plus rattraper. */
.ui button, .ui input, .ui select, .ui textarea { font-family: inherit; }

/* `hidden` EST UNE AFFIRMATION, PAS UNE SUGGESTION — et sans cette ligne c'est
   l'inverse. Le navigateur le rend par `display: none` dans SA feuille, celle
   qui perd contre la moindre règle d'auteur : il suffit qu'une classe pose
   `display: flex` pour qu'un élément marqué `hidden` reste à l'écran.
   
   Le piège a mordu trois fois ici. Deux rustines existaient déjà, chacune pour
   un sélecteur précis — `.ui-ligne[hidden]`, `.ui-neuf > summary[hidden]` — et
   la troisième victime a été le tiroir d'édition de la frise, qui s'ouvrait
   tout seul, vide, au chargement de chaque fiche. Écrire une rustine par
   victime, c'est attendre la suivante.
   
   `!important` se justifie ici et nulle part ailleurs : `hidden` dit que
   l'élément n'est pas dans la page, et rien ne doit pouvoir contredire ça —
   surtout pas une règle de mise en page écrite trois mois plus tard. */
.ui [hidden] { display: none !important; }
.ui input { color: inherit; }
.ui button { color: inherit; }

/* ─── Ossature ─── */
.ui-page { max-width: none; margin: 0 auto; padding: 16px 28px 28px; }
.ui-corps { display: grid; grid-template-columns: 216px minmax(0, 1fr); gap: 40px; align-items: start; }

/* ─── En-tête ─── */
.ui-tete { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; margin-bottom: 14px; }
.ui-tete h1 { margin: 0; font-size: 26px; font-weight: 600; letter-spacing: -.022em; }
.ui-tete .ui-sous { margin-top: 5px; }
/* UNE PRIMITIVE DE TEXTE NE PORTE PAS D'ESPACEMENT. Celle-ci avait 5 px de
   marge haute, hérités du sous-titre d'en-tête où elle est née : partout
   ailleurs cette marge décalait la ligne de 2,5 px une fois centrée, et le
   coupable était introuvable puisque la règle ne parle que de couleur et de
   corps. L'espacement appartient à la mise en page, pas au mot. */
.ui-sous { margin: 0; color: var(--sourdine); font-size: 14px; font-variant-numeric: tabular-nums; }
.ui-sous b { color: var(--encre); font-weight: 600; }

/* ─── Rail des groupes ─── */
.ui-rail { display: flex; flex-direction: column; gap: 2px; }
.ui-rail-titre {
  font-size: 11px; font-weight: 600; letter-spacing: .09em; text-transform: uppercase;
  color: var(--pale); padding: 0 10px 8px;
}
.ui-rail-titre + .ui-rail-titre, .ui-groupe + .ui-rail-titre { padding-top: 22px; }
/* Une ligne de rail : le groupe, puis l'œil qui l'allume ou l'éteint. */
.ui-groupe-ligne { display: flex; align-items: center; }
.ui-groupe-ligne .ui-groupe { flex: 1; min-width: 0; }

/* L'ŒIL S'EFFACE TANT QU'IL N'A RIEN À DIRE. Un groupe allumé est l'état
   normal : montrer treize contrôles en permanence donnait au rail l'air d'un
   formulaire à remplir. Il revient au survol de sa ligne, au clavier, et
   RESTE VISIBLE sur un groupe éteint — c'est le seul moyen de le rallumer. */
.ui-oeil {
  flex: none; width: 26px; height: 26px; margin-right: 2px;
  display: grid; place-items: center;
  border: 0; border-radius: 7px; background: none; color: var(--pale);
  cursor: pointer; opacity: 0; transition: opacity .12s, color .12s, background .12s;
}
.ui-groupe-ligne:hover .ui-oeil,
.ui-oeil:focus-visible,
.ui-groupe-ligne.est-masque .ui-oeil { opacity: 1; }
.ui-oeil:hover { color: var(--accent); background: var(--teinte); }
.ui-oeil .i { width: 15px; height: 15px; grid-area: 1 / 1; }
.ui-oeil .i-off { display: none; }
.ui-oeil[aria-pressed="false"] .i:not(.i-off) { display: none; }
.ui-oeil[aria-pressed="false"] .i-off { display: block; }

/* « Sans groupe » n'est pas un groupe de plus : c'est ce qui reste quand on les
   a tous nommés. Un filet au-dessus le dit sans qu'on ait à l'écrire. */
.ui-groupe-ligne.est-residu { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--trait); }
.ui-groupe-ligne.est-residu .ui-groupe-nom { font-style: italic; }
/* Deux résidus à la suite forment UN bloc : « Autres contacts » puis « Sans
   groupe » n'ont pas à être séparés l'un de l'autre, seulement du reste. */
.ui-groupe-ligne.est-residu + .ui-groupe-ligne.est-residu {
  margin-top: 0; padding-top: 0; border-top: 0;
}

.ui-groupe-ligne.est-masque .ui-groupe { opacity: .38; }
.ui-groupe-ligne.est-masque:hover .ui-groupe { opacity: .65; }

.ui-groupe {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 7px 10px; border: 0; border-radius: 8px; background: none;
  cursor: pointer; text-align: left; color: var(--sourdine);
  transition: background .12s, color .12s;
}
.ui-groupe:hover { background: rgba(0,0,0,.035); color: var(--encre); }
.ui-groupe[aria-pressed="true"] { background: var(--teinte); color: var(--accent); font-weight: 600; }
.ui-groupe-nom { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-groupe-n { font-size: 12px; color: var(--pale); font-variant-numeric: tabular-nums; }
.ui-groupe[aria-pressed="true"] .ui-groupe-n { color: var(--accent-2); }

/* ─── Recherche ─── */
/* UNE PRIMITIVE DÉCRIT L'OBJET, PAS SA TAILLE. Celle-ci reposait la densité —
   13/16 px, corps de 15 — parce qu'elle est née comme la GRANDE barre en tête du
   carnet d'adresses, seule sur sa ligne. Posée dans une rangée de champs, elle y
   emportait cette taille : mesuré dans le formulaire du carnet, 50,5 px et 15 px
   de corps à côté de trois voisins à 41 px et 14 px — « pourquoi le champ de
   recherche n'est pas identique aux autres ? ».

   C'est la faute de la barre d'événement du 10/09, à l'envers : là une page
   s'était donné une densité à elle, ici une primitive imposait la sienne. La
   densité DESCEND PAR HÉRITAGE et ne se déclare qu'une fois par conteneur ; une
   page qui veut une grande barre le dit avec `.ui-chercher-large`.

   ET LA LOUPE SE RANGE PAR RAPPORT AU REMBOURRAGE, jamais à 15 px écrits en dur :
   changer la densité déplaçait sinon le dessin sans déplacer sa réserve. Même
   correction que le chevron du `<select>` en septembre. */
.ui-chercher {
  position: relative;
  --champ-pad-gauche: calc(var(--champ-pad-x) + 28px);   /* le dessin, 17 px, et l'air autour */
}
.ui-chercher-large {
  margin-bottom: 14px;
  --champ-pad-y: 13px; --champ-pad-x: 16px; --champ-corps: 15px;
}
.ui-chercher svg { position: absolute; left: var(--champ-pad-x); top: 50%; transform: translateY(-50%); color: var(--pale); }
.ui-chercher input {
  width: 100%;
  background: var(--surface); border: 1px solid var(--trait); border-radius: var(--rayon);
  outline: none; transition: border-color .12s, box-shadow .12s;
}
.ui-chercher input::placeholder { color: var(--pale); }
.ui-chercher input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--teinte); }
.ui-chercher input::-webkit-search-cancel-button { cursor: pointer; }

/* ─── Pastilles de rôle ─── */
.ui-roles { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 22px; }
.ui-role {
  padding: 5px 12px; border: 1px solid var(--trait-2); border-radius: 999px;
  background: transparent; color: var(--sourdine); font-size: 12.5px; cursor: pointer;
  transition: border-color .12s, background .12s, color .12s;
}
.ui-role:hover { border-color: var(--accent); color: var(--encre); }
.ui-role[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: #fff; }
.ui-role-n { opacity: .6; margin-left: 5px; font-variant-numeric: tabular-nums; }
/* « +162 autres » N'EST PAS UN RÔLE : il déplie le rail, il ne filtre rien. Il en
   garde la forme — c'est la même rangée — mais pas l'encre : en sourdine, il ne
   se dispute pas l'attention avec ce qu'il révèle. Et il ne prend jamais l'état
   pressé, qui appartient au filtre. */
.ui-roles-plus { color: var(--sourdine); border-style: dashed; }
.ui-roles-plus:hover { color: var(--accent); border-color: var(--accent); }
/* LA COUPE EST FAITE PAR LE CSS, AVANT LE PREMIER RENDU — et `.js` est ce qui
   la conditionne : la classe est posée par un script inline du <head>, donc
   pendant l'analyse du document. Sans JavaScript elle n'existe pas et le rail
   reste entier, comme la frise et la grille des portails.
   Cacher au script après coup, c'était afficher les 176 pastilles puis les
   effondrer sous les yeux. */
/* LE RÔLE CHOISI ÉCHAPPE À LA COUPE : replier le rail après un clic ne doit pas
   emporter la pastille qui porte le filtre en cours, sans quoi on ne voit plus
   sur quoi on trie ni comment l'éteindre. */
.js .ui-roles.est-replie .est-en-plus:not([aria-pressed="true"]) { display: none; }

/* ─── Liste ─── */
/* La liste est un panneau qui défile seul. `overflow: hidden` suffisait tant
   qu'elle poussait la page ; il faut `auto` en vertical pour qu'elle porte sa
   propre barre, et `hidden` en horizontal pour que les coins arrondis tiennent.
   La hauteur est posée en JS, mesurée et non devinée. */
.ui-liste {
  background: var(--surface); border: 1px solid var(--trait); border-radius: 14px;
  overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: var(--trait-2) transparent;
}
.ui-liste::-webkit-scrollbar { width: 10px; }
.ui-liste::-webkit-scrollbar-track { background: transparent; }
.ui-liste::-webkit-scrollbar-thumb {
  background: var(--trait-2); border-radius: 999px;
  border: 3px solid var(--surface);
}
.ui-liste::-webkit-scrollbar-thumb:hover { background: var(--pale); }
.ui-ligne {
  display: grid; grid-template-columns: 38px minmax(0, 1fr) auto; gap: 14px;
  align-items: center; padding: 11px 18px; border-top: 1px solid var(--trait);
}
.ui-ligne:first-child { border-top: 0; }
.ui-ligne:hover { background: #faf9f5; }
/* LA RANGÉE ENTIÈRE OUVRE LA FICHE, donc elle en a le curseur. Le survol
   éclaircissait déjà le fond : il promettait une cible que seul le crayon
   offrait, 30 px à l'autre bout de la ligne. Ce qui porte son propre geste —
   le mail, le numéro, le badge, la corbeille — garde le sien. */
.ui-ligne { cursor: pointer; }
.ui-ligne a, .ui-ligne button { cursor: pointer; }
.ui-ligne .ui-icone.creux { cursor: default; }
/* Couvert par la règle générale plus haut — voir `.ui [hidden]`. */

/* Un contact rattaché à un bien porte un filet d'accent à gauche : c'est la
   seule distinction qui compte dans un carnet de cette taille, et elle doit se
   voir sans lire. */
.ui-ligne.est-suivi { box-shadow: inset 2px 0 0 var(--accent); background: #fbfdfc; }
/* UNE LIGNE SUIVIE RÉAGISSAIT AU SURVOL COMME LES AUTRES — sauf qu'elle porte
   déjà un fond. À spécificité égale, la règle du suivi est écrite APRÈS celle du
   survol, donc elle gagnait : les seules rangées qui ont un bien derrière elles
   étaient exactement celles qui ne bougeaient pas sous la souris, sur une page
   où la rangée entière est devenue cliquable. Elle a donc son propre survol, un
   cran plus appuyé dans SA teinte — pas le beige des autres, qui effacerait ce
   que la bande verte annonce. */
.ui-ligne.est-suivi:hover { background: #eef7f4; }

.ui-pastille {
  width: 38px; height: 38px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 12.5px; font-weight: 600; letter-spacing: .02em;
  user-select: none;
}
.ui-qui { min-width: 0; }
/* Le poids est porté par la LIGNE, et c'est le prénom qui l'allège : le
   contraire — ligne normale, nom en gras — laisserait les fiches sans patronyme
   en poids léger, plus discrètes que les autres sans raison. */
/* LE POIDS EST BORNÉ SUR LE <b>, jamais laissé au navigateur. `<b>` vaut
   `font-weight: bolder`, une valeur RELATIVE : sur une ligne déjà à 600 elle
   monte à 700 ou 800 selon la fonte, et le nom ressortait épais et mal dessiné.
   La ligne reste au poids normal, seul le mot sur lequel on trie est appuyé. */
.ui-nom {
  font-size: 14.5px; font-weight: 400;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ui-nom b { font-weight: 600; }
.ui-quoi {
  display: flex; align-items: center; gap: 7px; margin-top: 1px;
  font-size: 12.5px; color: var(--sourdine); overflow: hidden; white-space: nowrap;
}
.ui-quoi .sep { color: var(--trait-2); }
.ui-quoi .maison { color: var(--pale); }
/* Un numéro se lit chiffre à chiffre : chasse fixe, pour que les groupes de
   deux s'alignent d'une ligne à l'autre au lieu de danser. */
.ui-quoi .numero { color: var(--sourdine); font-variant-numeric: tabular-nums; }
.ui-vide { color: var(--pale); font-style: italic; }

/* ─── Actions de ligne ─── */
.ui-actions { display: flex; align-items: center; gap: 6px; }
.ui .ui-icone {
  width: 30px; height: 30px; display: grid; place-items: center;
  border: 1px solid var(--trait); border-radius: 8px; background: var(--surface);
  color: var(--sourdine); cursor: pointer;
  transition: border-color .12s, color .12s, background .12s;
}
.ui .ui-icone:hover { border-color: var(--accent); color: var(--accent); background: var(--teinte); }
.ui .i { width: 15px; height: 15px; }
.ui .ui-icone.creux { border-color: transparent; background: none; color: var(--trait-2); cursor: default; }
.ui .ui-icone.creux:hover { border-color: transparent; background: none; color: var(--trait-2); }
/* LA PASTILLE EST DEVENUE UN LIEN — elle mène aux biens de la personne dans le
   carnet de suivi. Elle garde exactement son dessin : `text-decoration` et la
   couleur d'un `<a>` sont les deux seules choses que la balise apporte et que la
   classe ne disait pas. C'est la leçon de `.cn-creer-ici`, prise d'avance. */
.ui-suivi-badge {
  display: inline-flex; align-items: center; gap: 5px; margin-right: 4px;
  padding: 3px 9px; border-radius: 999px; background: var(--teinte); color: var(--accent);
  font-size: 11.5px; font-weight: 600; font-variant-numeric: tabular-nums;
  text-decoration: none;
}
a.ui-suivi-badge:hover,
a.ui-suivi-badge:focus-visible { background: var(--accent); color: #fff; }

/* ─── La bulle ───
   LA BULLE VIT HORS DE SA LIGNE, ET C'EST OBLIGÉ. `.ui-quoi` porte
   `overflow: hidden` — c'est ce qui fait son ellipse — donc une bulle posée en
   absolu à l'intérieur serait COUPÉE par son propre parent. D'où `position:
   fixed` et un placement calculé, qui la met du même coup hors des contextes
   d'empilement.

   Fond encre, texte papier : elle se lit comme une note posée par-dessus et non
   comme un bloc de la page. C'est le seul endroit du socle qui inverse le
   contraste, et c'est ce qui lui donne son statut de bulle.

   DANS LE SOCLE DEPUIS LE 22/09/2026, parce qu'une SECONDE page la réclame — le
   carnet d'adresses, dont la pastille des biens portait un `title` natif faute
   de mieux : une boîte blanche en fonte système au milieu d'un écran qui a sa
   propre langue. C'est le second usage qui fait monter, jamais l'anticipation. */
.ui .ui-bulle {
  position: fixed; z-index: 40; max-width: 280px;
  padding: 8px 12px; border-radius: 9px;
  background: var(--encre); color: var(--papier);
  font-size: 12.5px; font-weight: 400; line-height: 1.5;
  /* `pre-line` ET NON `normal` : une bulle porte parfois plusieurs faits — la
     tuile du prix en fond trois — et ils se séparent par un saut de ligne.
     `textContent` conserve le « \n » ; sans cette règle il se rendrait comme
     une espace et les trois phrases se colleraient en un pavé. Les bulles d'une
     seule phrase ne bougent pas, elles n'en contiennent aucun. */
  white-space: pre-line; pointer-events: none;
  box-shadow: 0 6px 20px rgba(22, 24, 28, .18);
}

/* UN FAIT PAR LIGNE (07/10/2026) : le script range chaque ligne dans son `<div>`. Le premier fait garde sa place en tête, les
   suivants sont séparés par un filet et un peu d'air ; une bulle d'une seule ligne n'en voit rien. */
.ui .ui-bulle > div + div { margin-top: 6px; padding-top: 6px; border-top: .5px solid rgba(244, 243, 239, .18); }
.ui .ui-bulle > div:first-child { font-weight: 500; }

/* ─── États ─── */
.ui-rien { padding: 54px 20px; text-align: center; color: var(--sourdine); }
.ui-rien b { display: block; color: var(--encre); font-size: 15px; margin-bottom: 4px; }

.ui-note {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 14px 18px; margin-bottom: 20px;
  background: var(--surface); border: 1px solid var(--trait); border-radius: var(--rayon);
  color: var(--sourdine);
}
.ui-note.alerte { border-color: #e4cfa8; background: #fdf8ee; color: var(--ambre); }
.ui-note b { color: var(--encre); font-weight: 600; }

/* ─── Boutons ─── */
/* UN BOUTON POSE SON INTERLIGNE, sinon sa hauteur dépend de sa balise. Un
   `<button>` part sur le `line-height: normal` du navigateur (~1,28) quand un
   `<a>` ou un `<label>` héritent celui de la page (1,5) : la même classe rendait
   30,75 px sur une étiquette et 28 px sur un bouton, côte à côte sur la ligne
   photo. Le primitif doit décrire la boîte entière, pas seulement ce qui la
   décore — ce que la balise apporte par défaut lui échappe autrement. */
.ui-btn {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 15px; border-radius: 9px; border: 1px solid var(--trait-2);
  background: var(--surface); cursor: pointer;
  font-size: 13.5px; font-weight: 500; line-height: 1.3;
  transition: border-color .12s, background .12s, color .12s;
}
.ui-btn:hover { border-color: var(--accent); color: var(--accent); }
.ui .ui-btn-plein { background: var(--accent); border-color: var(--accent); color: #fff; }
.ui .ui-btn-plein:hover { background: var(--accent-2); border-color: var(--accent-2); color: #fff; }

/* ─── Saisie d'un contact local ─── */
.ui-neuf { margin: 0; }
.ui-neuf > summary {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer; list-style: none;
  padding: 8px 15px; border: 1px dashed var(--trait-2); border-radius: 9px;
  color: var(--sourdine); font-size: 13.5px;
}
.ui-neuf > summary::-webkit-details-marker { display: none; }
.ui-neuf > summary:hover { border-color: var(--accent); color: var(--accent); }
.ui-neuf[open] > summary { margin-bottom: 14px; border-style: solid; }
.ui-formulaire {
  background: var(--surface); border: 1px solid var(--trait); border-radius: var(--rayon); padding: 18px;
}
.ui-grille { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; }
.ui-champ { display: flex; flex-direction: column; gap: 5px; }

/* ─── LES AUTRES CHAMPS DU CARNET GOOGLE — 27/09/2026 ───
   Une cellule qui prend toute la rangée : les notes et les libellés sont les
   deux seuls endroits où l'on écrit une phrase ou une liste, et le champ le
   plus large doit être celui où l'on écrit le plus. C'est la leçon de la barre
   d'événement, dont la note était le champ le plus étroit de sa ligne. */
.ui-champ-large { grid-column: 1 / -1; }

/* DEUX COLONNES, ET NON LA RANGÉE ENTIÈRE. Les six champs permanents laissent
   deux cellules libres au bout de leur seconde rangée — mail et téléphone y sont
   seuls — et « Communes couvertes » ouvrait une rangée à lui tout seul juste en
   dessous. Il remplit maintenant le trou.

   `grid-column: auto / -1` paraît dire la même chose et **ne fait rien** :
   mesuré, la cellule y rend 186 px, celle d'une colonne. Avec un début
   automatique, la fin nommée ne fixe aucune étendue — c'est `span` qui la dit. */
.ui-champ-duo { grid-column: span 2; }

/* LE DÉCLENCHEUR EST UNE CELLULE, pas une ligne sous la grille : il remplit le
   trou que laissent six champs sur quatre colonnes. Quand la rangée est pleine,
   il passe au rang suivant et se retrouve seul — il se dégrade donc vers la
   forme pleine largeur, ce qui est exactement ce qu'on lui opposait. Il se cale
   en BAS de sa cellule, à la hauteur des champs qu'il accompagne, et non au
   milieu du vide. */
.ui-champ-plus { justify-content: flex-end; }
.ui .ui-plus {
  border: 1px dashed var(--trait-2); background: none; color: var(--sourdine);
  border-radius: 999px; padding: 8px 14px; font: inherit; font-size: 13px;
  cursor: pointer; white-space: nowrap;
}
.ui .ui-plus:hover { border-color: var(--accent); color: var(--accent); }

/* LE BOUTON ET SA PASTILLE PARTAGENT UNE RANGÉE, dans une cellule qui est une
   COLONNE — sans elle, les deux s'empileraient. Elle enroule : dans une cellule
   de grille étroite, la pastille passe sous le bouton plutôt que de le serrer.

   `margin-right` est NEUTRALISÉE ici. `.ui-suivi-badge` la porte pour son usage
   d'origine — une pastille suivie d'un nom sur une ligne de contact — et en fin
   de rangée elle pousse 4 px de vide contre le bord. *Une primitive de texte ne
   devrait pas porter d'espacement* ; la retirer de la primitive toucherait deux
   pages qui la lisent, donc c'est signalé plutôt que corrigé en passant. */
.ui-plus-rangee { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.ui-plus-rangee .ui-suivi-badge { margin-right: 0; }

/* Une rangée de libellé : la clé, la valeur, et de quoi la retirer. La valeur
   est plus large que la clé — on écrit « Lyon 6e » sous « Commune », jamais
   l'inverse. */
/* UNE COMMUNE COUVERTE EST UN JETON, pas une rangée de champs : on n'en modifie
   rien — on l'ajoute ou on la retire. Une ligne clé/valeur y réserverait la
   place d'une saisie qui n'existe pas. */
.ui-jetons { display: flex; flex-wrap: wrap; gap: 6px; }
/* VIDE, ELLE SORT DU FLUX. `.ui-champ` est une colonne flex à `gap: 5px`, et un
   enfant de hauteur NULLE consomme quand même son écart : la rangée de jetons ne
   prenait pas de place, elle en fabriquait. Mesuré le 28/09/2026 sur les communes
   couvertes d'un contact — l'input du mail tombe à 21,5 px du haut de sa boîte,
   celui des communes à 26,5, sur la même rangée de grille. *Cinq pixels ne se
   nomment pas, ils se voient.*

   Même correction que `.cn-lieu-etat:empty`, posée en septembre pour exactement
   cette raison. Elle ne mord que si le balisage est réellement vide : une seule
   espace entre les deux balises et `:empty` échoue sans rien dire. */
.ui-jetons:empty { display: none; }
.ui .ui-jeton {
  display: inline-flex; align-items: center; gap: 2px;
  background: var(--teinte); color: var(--accent); border-radius: 999px;
  padding: 4px 4px 4px 12px; font-size: 13px; font-weight: 500;
}
/* Le geste se révèle au survol de SON jeton — chaque sorte de rangée déclare ce
   qui la révèle, et un jeton n'est ni une ligne de carnet ni un libellé. */
.ui .ui-jeton:hover .ui-geste { opacity: 1; }
.ui .ui-jeton .ui-geste { min-width: 20px; height: 20px; }
.ui .ui-jeton .ui-geste .i { width: 12px; height: 12px; }

.ui-libelles { display: flex; flex-direction: column; gap: 8px; }
.ui-libelle  { display: flex; align-items: center; gap: 8px; }
.ui-libelle input:first-child { flex: 0 1 200px; }
.ui-libelle input:nth-child(2) { flex: 1 1 auto; min-width: 0; }
/* Le geste de retrait se révèle au survol de SA rangée — chaque sorte de rangée
   déclare ce qui la révèle, et celle-ci n'est ni une ligne de carnet ni une
   ligne de contact d'un bien. */
.ui .ui-libelle:hover .ui-geste { opacity: 1; }
.ui-champ label {
  font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: var(--pale);
}
/* ─── Les contrôles de saisie ───
 *
 * L'HABILLAGE VIT SUR `.ui`, PAS SUR `.ui-champ`. Il a d'abord été posé sur ce
 * dernier — la boîte étiquetée d'un formulaire en grille — et tout ce qui n'y
 * était pas gardait l'apparence du navigateur : le formulaire d'événement du
 * carnet, dont les contrôles vivent EN LIGNE sans étiquette, rendait des champs
 * blancs à bordure système à côté de champs beiges. Deux apparences sur un même
 * écran se lisent comme deux formulaires.
 *
 * `.ui-champ` ne décrit donc plus que la MISE EN PAGE — étiquette au-dessus,
 * champ pleine largeur. L'apparence, elle, ne dépend pas de l'endroit.
 *
 * LES CONTRÔLES QUI DESSINENT AUTRE CHOSE QU'UNE BOÎTE sont exclus : case à
 * cocher, bouton radio, sélecteur de couleur, choix de fichier. Leur imposer un
 * fond et une bordure de champ texte les rendrait méconnaissables.
 *
 * `appearance: none` sur le `<select>` retire la flèche native, remplacée par un
 * chevron dessiné en fond : sans ça elle reste tracée par le système et ignore
 * la palette.
 */
/* LA DENSITÉ EST UNE VARIABLE, PAS UNE SURCHARGE — et c'est la correction du
 * 10/09/2026. La barre d'événement du carnet portait sa propre règle,
 * `.cn-ajout-evt select, .cn-ajout-evt input { font-size: 12.5px; padding: 7px
 * 10px }`. Elle atteignait les `select` et JAMAIS les `input` : le sélecteur
 * ci-dessous, avec ses cinq `:not()` d'attribut enchaînés, valait **0-6-1** en
 * spécificité quand celui de la page n'en vaut que 0-1-1.
 *
 * Résultat mesuré sur une seule barre : bouton 33 px, select 35, note 38,
 * montant 41, date 43. **Cinq hauteurs pour une rangée de contrôles**, sans
 * qu'aucune règle ne dise pourquoi — on croit régler la densité, on ne règle
 * que la moitié des champs.
 *
 * Deux choses ont changé, et la seconde est la vraie :
 *
 *   · le `:not()` prend une LISTE plutôt qu'une chaîne — même effet, mais 0-2-1
 *     au lieu de 0-6-1 : une primitive doit rester corrigible par une page ;
 *   · la densité descend par HÉRITAGE. Une page pose `--champ-pad-y`,
 *     `--champ-pad-x` et `--champ-corps` sur son conteneur, et les trois sortes
 *     de contrôle les prennent ensemble. Une variable héritée ne se dispute pas
 *     avec un sélecteur : il n'y a plus de guerre de spécificité à gagner.
 *
 * `font` en raccourci ne pouvait pas rester : il porte la taille avec le reste,
 * donc il fallait le déplier pour que la seule taille devienne réglable.
 *
 * LES CONTRÔLES QUI DESSINENT AUTRE CHOSE QU'UNE BOÎTE sont exclus : case à
 * cocher, bouton radio, sélecteur de couleur, choix de fichier, curseur. Leur
 * imposer un fond et une bordure de champ texte les rendrait méconnaissables.
 */
.ui {
  --champ-pad-y: 9px;
  --champ-pad-x: 12px;
  --champ-corps: inherit;

  /* UNE SEULE APPARENCE DE MENU, TENUE PAR DES JETONS. Ce site ouvre deux sortes
     de listes sous un champ — celle qu'on tape (`.cn-suggestions`) et celle d'un
     `<select>` (`::picker(select)`) — et elles rendaient deux dessins : rayon 8
     contre 10, ombre légère contre lourde, 13 px contre la densité du champ, une
     ligne à plat contre une ligne arrondie. *Deux apparences pour un même objet
     se lisent comme deux objets*, et le mot était le même à l'écran : « rôle ».

     ON NE PEUT PAS LES RÉUNIR DANS UN MÊME SÉLECTEUR : une liste de sélecteurs
     dont un morceau est inconnu est REJETÉE EN ENTIER, donc `::picker(select)`
     collé à `.cn-suggestions` emporterait les suggestions sur tout navigateur
     qui ne connaît pas la pseudo-élément. Les jetons font le travail sans cette
     dépendance — et une variable héritée ne se dispute avec aucun sélecteur. */
  --menu-rayon:     8px;
  --menu-ecart:     2px;
  --menu-haut:      260px;
  --menu-ombre:     0 6px 18px rgba(15, 23, 42, .10);
  --menu-ligne-y:   8px;
  --menu-ligne-x:   11px;
  --menu-corps:     13px;
}
/* UNE CASE À COCHER NE DESSINE PAS DE BOÎTE DE CHAMP : elle garde sa taille
   native, et son libellé se lit en clair plutôt qu'en capitales d'étiquette —
   c'est une phrase qu'on approuve, pas le nom d'une case à remplir.

   Née `.rech-case` dans la feuille du carnet pour les deux cases de la
   recherche ; montée ici le 28/09/2026 quand la barre d'événement en a réclamé
   une à son tour. *C'est le second usage qui fait monter*, jamais
   l'anticipation — et le nom dit désormais ce que l'objet EST plutôt que
   l'écran où il est né. */
.ui .ui-case {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 13px; color: var(--encre); cursor: pointer;
}
/* ET LA CASE ELLE-MÊME EST DESSINÉE — 28/09/2026, relevé sur capture : « c'est
   quoi cette case qui ne respecte pas le design ». Elle sortait en BLEU SYSTÈME,
   carrée, au milieu d'une barre où tout le reste est papier chaud à bord
   arrondi.

   Ce n'était pas un oubli mais une conséquence : l'habillage des champs EXCLUT
   explicitement les cases — leur imposer le fond et la bordure d'un champ texte
   les rendrait méconnaissables — et personne n'avait repris la main derrière
   cette exclusion. *Une exemption qui ne propose rien à la place laisse le
   navigateur décider, et il décide dans sa langue à lui.*

   LE MOTIF EST CELUI DU CHEVRON DU `<select>`, vingt lignes plus bas : un
   `appearance: none` et un tracé en data-URI. C'est déjà la façon dont cette
   feuille reprend un dessin au navigateur, et en ouvrir une seconde serait deux
   façons de faire la même chose.

   `--trait-2` ET NON `--trait` : le filet des champs est calculé pour une boîte
   de deux cents pixels ; sur un carré de seize il disparaît. C'est le même
   raisonnement que la pastille du socle, qui prend déjà le trait appuyé.

   RAYON 5 PX ET NON 8 : à cette taille, le rayon des champs ferait une case
   presque ronde, donc un bouton radio. La forme doit continuer de dire ce que
   le contrôle EST.

   Le focus est celui de toute la page — `.ui :focus-visible`, l'anneau d'accent
   — il n'y a rien à redire ici. Et `:checked` vaut 0-3-0 contre le 0-2-0 de
   `.ui input:focus`, qui poserait sinon `--surface` par-dessus l'accent au
   moment précis où l'on vient de cocher. */
.ui .ui-case input[type="checkbox"] {
  appearance: none; margin: 0; flex: none;
  width: 16px; height: 16px;
  background: var(--papier);
  border: 1px solid var(--trait-2); border-radius: 5px;
  cursor: pointer;
  transition: background-color .12s, border-color .12s;
}
.ui .ui-case input[type="checkbox"]:checked {
  background-color: var(--accent); border-color: var(--accent);
  background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 12.5 4.5 4.5L19 7.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: center; background-size: 11px;
}
.ui input:not([type="checkbox"], [type="radio"], [type="color"], [type="file"], [type="range"]),
.ui select,
.ui textarea {
  padding: var(--champ-pad-y) var(--champ-pad-x); background: var(--papier);
  /* UN CHAMP À ICÔNE RÉSERVE SA PLACE PAR UNE VARIABLE, pas par une surcharge.
     `.ui-chercher input` posait `padding: 13px 16px 13px 44px` — 0-1-1 — et la
     règle ci-dessus vaut 0-2-1 : elle l'écrasait, l'icône se retrouvait POSÉE SUR
     le texte de l'invite. Régression introduite le 10/09/2026 en passant la
     densité en variables, et invisible pour qui ne relit pas la recherche.
     Une variable héritée ne se dispute avec personne — même raison que la
     densité elle-même, et que le chevron du `<select>`. */
  padding-left: var(--champ-pad-gauche, var(--champ-pad-x));
  border: 1px solid var(--trait); border-radius: 8px; outline: none;
  /* `font-family` n'est pas répété ici : la remise à zéro le pose déjà sur les
     mêmes contrôles, plus `button`. Le redire n'ajoutait rien et faisait le seul
     doublon de cette feuille. */
  font-size: var(--champ-corps); font-weight: inherit;
  font-style: inherit; line-height: inherit; color: inherit;
  transition: border-color .12s, background .12s;
}
.ui textarea { resize: vertical; min-height: 76px; }
/* Le chevron se range par rapport au rembourrage, jamais à un nombre écrit à
   côté : changer la densité déplacerait le dessin sans déplacer sa réserve. */
.ui select {
  appearance: none; cursor: pointer;
  padding-right: calc(var(--champ-pad-x) + 20px);
  background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236c6963' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right calc(var(--champ-pad-x) - 2px) center;
  background-size: 14px;
}
/* LA LIGNE CHOISIE PREND L'ACCENT, PAS LE BLEU DU SYSTÈME — 16/09/2026, sur
   capture : « possible de mettre du vert plutôt que bleu ? ». Le menu déroulant
   d'un `<select>` est dessiné par le NAVIGATEUR : la feuille ne l'atteint pas, à
   une exception près — l'`<option>` elle-même accepte un fond et une encre.

   Vérifié avant d'écrire, et les deux choses comptaient : la règle s'applique
   bien (`rgb(15,118,110)` sur blanc au calcul), ET elle **ne repeint pas le
   select fermé** — mesuré identique avant et après, `rgb(244,243,239)`. Dans
   certains navigateurs, teindre l'option colore aussi la boîte au repos, ce qui
   aurait mis du vert plein au milieu d'une rangée de champs papier.

   Limite honnête : selon le système, le menu reste peint par l'OS et ignore ces
   deux déclarations. Là on ne peut rien — c'est la même frontière que le
   `<datalist>` des rôles, dont le pavé à deux lignes n'était pas rattrapable. */
.ui option:checked,
.ui option:hover { background-color: var(--accent); color: #fff; }

/* ─── LE MENU DÉROULANT REPRIS AU NAVIGATEUR ───
 *
 * 16/09/2026. La règle ci-dessus teint la ligne CHOISIE, et c'est tout ce qu'on
 * pouvait atteindre : le SURVOL restait le bleu du système, parce que le menu
 * lui-même est dessiné hors de la page. `appearance: base-select` le rend au
 * document — le sélecteur devient un élément comme un autre, et son menu un
 * `::picker(select)` qu'on habille.
 *
 * TOUT EST DANS UN `@supports`, et ce n'est pas de la prudence décorative : sans
 * lui, `option { padding }` partirait aussi vers les navigateurs qui gardent le
 * menu natif, où il s'applique à moitié et selon des règles qui ne sont pas les
 * nôtres. Ici, ou bien on tient le menu entier, ou bien on n'y touche pas.
 *
 * Chrome 135+ seulement à ce jour. Firefox et Safari gardent exactement le rendu
 * d'avant — c'est une amélioration progressive, pas un remplacement.
 *
 * ON HÉRITE DE TOUT LE DESSIN, y compris de ce qu'on ne voulait pas refaire :
 * l'encadré, l'ombre, le rembourrage, la coche. C'est le prix, et il se paie une
 * fois. La coche est masquée parce que la ligne choisie porte déjà l'accent en
 * plein — deux marques pour un même état, ce serait la redite qu'on retire
 * partout ailleurs. */
@supports (appearance: base-select) {
  .ui select,
  .ui select::picker(select) { appearance: base-select; }

  /* Le chevron reste le NÔTRE, celui du fond de `.ui select` : `base-select`
     ajoute son propre `::picker-icon`, et les garder tous les deux dessinerait
     deux flèches. On masque le sien plutôt que de refaire le dessin. */
  .ui select::picker-icon { display: none; }

  .ui select::picker(select) {
    background: var(--surface);
    border: 1px solid var(--trait);
    border-radius: var(--menu-rayon);
    padding: 0;
    margin-top: var(--menu-ecart);
    max-height: var(--menu-haut);
    /* Comme la liste de suggestions : c'est le débordement qui découpe les
       angles de la première et de la dernière ligne sur le rayon de la boîte. */
    overflow-y: auto;
    box-shadow: var(--menu-ombre);
    /* LA POSITION RESTE CELLE DE CHROME, et ce n'est pas un choix — c'est un
       échec, noté pour qu'on ne le retente pas. Le menu s'aligne pour mettre la
       ligne CHOISIE sous le curseur, donc il remonte et recouvre ce qui est
       au-dessus : mesuré à 156 px plus haut que le champ, par-dessus le bloc
       voisin. Toutes les autres listes du site descendent.

       `position-area` est pourtant supporté ici (Chrome 145, `CSS.supports` le
       confirme) — mais la déclaration est REJETÉE sur `::picker(select)` quelle
       que soit sa forme : la valeur calculée reste `start span-end`, celle de
       l'agent. Essayé en `block-end span-inline-end` et en `end span-end`, avec
       `position-try-fallbacks`. Le reste du dessin passe, cette propriété non. */
  }
  .ui select option {
    padding: var(--menu-ligne-y) var(--menu-ligne-x);
    background: transparent;
    color: var(--encre);
    font-size: var(--menu-corps);
  }
  .ui select option::checkmark { display: none; }
  /* Le survol et le clavier disent la MÊME chose : c'est la ligne qu'on vise. */
  .ui select option:hover,
  .ui select option:focus { background: var(--teinte); color: var(--accent); }
  /* LA VALEUR COURANTE EST UN APLAT D'ACCENT, et c'est le menu de référence du
     site — les autres listes viennent à lui. Je l'avais éteint en teinte pour
     l'aligner sur la liste de suggestions : c'était prendre le sens inverse de
     la demande, et ça retirait la seule marque qui dise « c'est là que tu en
     es ». Un aplat n'est pas de trop ici : il ne désigne pas une action, il
     désigne un état, et il n'y en a qu'un par menu. */
  .ui select option:checked { background: var(--accent); color: #fff; }
}

.ui input:focus,
.ui select:focus,
.ui textarea:focus { border-color: var(--accent); background-color: var(--surface); }
.ui input::placeholder,
.ui textarea::placeholder { color: var(--pale); }

/* CETTE LIGNE NEUTRALISE `city.css`, elle n'est pas décorative.
   
   `city.css` porte `select, input[type="number"] { width: 100% }` sur des
   sélecteurs d'ÉLÉMENT nus — 0-0-1, sans conteneur — et `nav.php` amène cette
   feuille sur toutes les pages. Un `<select>` posé dans une barre du socle
   prenait donc la ligne entière et poussait tout le reste au rang suivant, sans
   qu'aucune règle du socle ne parle de largeur : on cherche la cause chez soi
   alors qu'elle vient d'à côté.
   
   C'est la faute déjà relevée sur `.ct2 button { color: inherit }` — une
   propriété visuelle sur un sélecteur d'élément finit toujours par atteindre ce
   qu'elle ne visait pas. Ici on ne peut pas la retirer sans toucher aux cinq
   pages d'origine : on la borne au périmètre du socle.
   
   Dans une boîte étiquetée, le champ reprend toute sa colonne. En ligne, il se
   dimensionne lui-même — c'est la mise en page qui décide, pas l'apparence. */
.ui select,
.ui input[type="number"] { width: auto; }

.ui-champ input,
.ui-champ select,
.ui-champ textarea { width: 100%; }
/* UNE AIDE RESPIRE DES DEUX CÔTÉS. Elle ne portait que sa marge du haut : ça
   suffit tant qu'elle termine son bloc, et ça colle dès qu'elle en introduit un
   — la phrase « en gris, ce que dit l'annonce » touchait l'étiquette du premier
   champ. Une marge asymétrique est une hypothèse sur la place de l'élément, et
   cette hypothèse finit toujours par être fausse. */
.ui-aide { margin: 14px 0; font-size: 12.5px; color: var(--sourdine); }
/* DANS UNE BOÎTE ÉTIQUETÉE, C'EST LA BOÎTE QUI ESPACE. `.ui-champ` est une
   colonne flex à `gap: 5px` : l'aide y ajouterait ses 14 px aux 5 du gap et
   ouvrirait un trou entre l'étiquette et le champ qu'elle introduit. Même
   principe que `.ui-sous`, dont la marge avait dû être rendue à l'en-tête : une
   primitive de texte ne décide pas de sa place. */
.ui-champ .ui-aide { margin: 0; }
.ui-formulaire .ui-btn { margin-top: 14px; }

@media (max-width: 820px) {
  .ui-page { padding: 22px 16px 70px; }
  .ui-corps { grid-template-columns: 1fr; gap: 26px; }
  .ui-rail { position: static; flex-direction: row; flex-wrap: wrap; }
  .ui-rail-titre { width: 100%; padding-top: 0 !important; }
  .ui-groupe { width: auto; border: 1px solid var(--trait-2); border-radius: 999px; padding: 5px 12px; }
  .ui-tete { flex-direction: column; align-items: flex-start; }
}

/* ─── Actions d'en-tête ─── */
.ui-tete-actions { display: flex; gap: 8px; align-items: center; }

/* ─── Marqueur d'une valeur posée ici ─── */
.ui-marque {
  padding: 2px 8px; border-radius: 999px;
  border: 1px solid var(--trait-2); color: var(--pale);
  font-size: 11px; letter-spacing: .02em;
}

/* ─── Édition en ligne ───
   Le formulaire s'insère DANS la liste, entre deux lignes : il doit se lire
   comme un tiroir ouvert sous la ligne, pas comme un bloc posé par-dessus. */
.ui-edition {
  padding: 18px 18px 18px 70px;
  background: #fbfaf6;
  border-top: 1px solid var(--trait);
  box-shadow: inset 3px 0 0 var(--accent);
}
/* LA RANGÉE QUI TERMINE UN FORMULAIRE — un tiroir d'édition comme un formulaire
   de fouille. Elle s'appelait `.ui-edition-pied`, ce qui la disait réservée au
   tiroir : le formulaire du carnet en avait besoin et l'a empruntée sous un nom
   qui décrivait autre chose, quatrième emprunt de ce genre. Renommée le
   15/09/2026 parce que c'est la NATURE de l'objet qui doit tenir dans le nom,
   pas le premier endroit où on l'a posé.

   HUITIÈME COLLISION ÉVITÉE DE JUSTESSE : le premier nom essayé était
   `.ui-actions`, DÉJÀ PRIS quinze lignes plus haut par la rangée d'icônes d'une
   ligne de contact. Les deux auraient partagé un sélecteur, et le `margin-top:
   14px` d'ici aurait fait descendre les icônes de chaque ligne du carnet
   d'adresses. Le préfixe `ui-` était bon des deux côtés : *c'est le MOT qui
   entre en collision, pas la famille* — et le seul garde-fou est le `grep`
   avant de nommer, pas après. */
/* LES BOUTONS FINISSENT À DROITE, dans tous les pieds — 16/09/2026. Ils étaient
   à gauche ici et à droite dans le pied du formulaire d'événement, dont le
   commentaire affirmait pourtant que la droite était « la forme de tous les
   formulaires du site ». *Elle l'était d'un côté seulement*, et une colonne qui
   empile quatre blocs montrait « Ajouter » à droite puis « Rattacher » et
   « Enregistrer » à gauche.
   
   La droite est le bon côté : c'est là que finit la lecture d'un formulaire, et
   c'est déjà ce que `.ui-pied .ui-aide { flex: 1 }` supposait — une aide qui
   pousse les boutons devant elle n'a de sens que si les boutons vont au bout. */
.ui-pied {
  display: flex; align-items: center; justify-content: flex-end;
  gap: 10px; flex-wrap: wrap; margin-top: 14px;
}
.ui-pied .ui-aide { margin: 0; flex: 1; min-width: 220px; }

/* Le bloc de création n'est plus déclenché par son propre résumé : celui-ci est
   masqué, et le <details> ne sert qu'à porter l'état. Son masquage passe par la
   règle générale plus haut. */
.ui-neuf[open] { margin: 0 0 18px; }
.ui-neuf:not([open]) { margin: 0; }

/* ─── Choix des groupes ───
   Des cases à cocher, pas un menu multiple : treize libellés se lisent d'un
   coup et se cliquent au doigt, là où un <select multiple> demande d'apprendre
   qu'on garde Ctrl enfoncé. */
.ui-groupes-choix {
  margin: 16px 0 0; padding: 14px 16px;
  border: 1px solid var(--trait); border-radius: var(--rayon); background: var(--papier);
}
.ui-groupes-choix legend {
  padding: 0 6px; font-size: 11px; font-weight: 600;
  letter-spacing: .07em; text-transform: uppercase; color: var(--pale);
}
.ui-groupes-choix label {
  display: inline-flex; align-items: center; gap: 7px; margin: 4px 14px 4px 0;
  font-size: 13px; cursor: pointer;
}
.ui-groupes-choix input { accent-color: var(--accent); width: 15px; height: 15px; cursor: pointer; }
.ui-groupes-choix[disabled] { opacity: .55; }
.ui-groupes-choix[disabled] label { cursor: default; }
.ui-groupes-choix .ui-aide { margin: 8px 0 0; }


/* ─── Le bandeau posé par assets/ajax.js ───
   Il émet `.flash` / `.flash-ok` / `.flash-erreur`, les tons partagés du site.
   Cette page n'ayant pas les cartes de `city.css`, elle leur donne son propre
   habillage plutôt que d'importer une feuille pour trois règles. */
.ui .flash {
  padding: 13px 18px; margin: 0 0 18px; border-radius: var(--rayon);
  background: var(--surface); border: 1px solid var(--trait); color: var(--sourdine);
}
.ui .flash-ok { border-color: #bfdcd4; background: var(--teinte); color: var(--accent); }
.ui .flash-erreur { border-color: #e4cfa8; background: #fdf8ee; color: var(--ambre); }

/* ─── L'attente ───
   `pointer-events: none` sur le bloc verrouillé : sans lui l'overlay se voit
   mais ne retient rien, et un second clic part pendant le premier. */
.ui .bloc-en-cours { position: relative; pointer-events: none; }
.ui .bloc-en-cours::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  background: rgba(255, 254, 252, .6);
}
.ui [aria-busy="true"] { opacity: .6; cursor: progress; }

/* UN TIROIR QUI TRAVAILLE SE LIT — 22/09/2026, sur constat : « ça masque le
   contact, on n'a aucune information comme quoi c'est en train d'enregistrer ».
   Le voile et le grisé se cumulaient (opacité .6 SOUS un calque à 60 % de
   papier) : le tiroir devenait illisible, ce qui est la langue du DÉSACTIVÉ et
   non celle du EN COURS.
   Le voile reste — c'est `pointer-events` qui empêche le second envoi, et le
   figé doit se voir — mais le grisé part : on n'a pas besoin d'effacer ce qu'on
   vient d'écrire pour dire qu'on y touche plus.
   ET LA LIGNE D'AIDE PASSE AU-DESSUS. Le calque est un `::after` sans
   `z-index` : un enfant positionné à 1 se peint par-dessus. Sans ça le mot qui
   annonce l'attente serait justement voilé par elle. */
.ui .ui-dit-attente[aria-busy="true"] { opacity: 1; }
.ui .ui-dit-attente[aria-busy="true"] .ui-aide {
  position: relative; z-index: 1; color: var(--accent); font-weight: 500;
}



/* ─── Ce qu'un bouton VA FAIRE, dit avant le clic ───
 *
 * Trois intentions, et la famille était incomplète : seule `danger` existait
 * ici. `biens.php` posait donc `btn-attention` et `btn-positif`, qui viennent de
 * `city.css` — et qui, sur une page du socle, **ne désignent rien**. Les deux
 * feuilles déclarent leur survol à la même spécificité (0-2-0) et `socle.css`
 * est chargée APRÈS : `.ui-btn:hover` gagne, la couleur d'intention n'apparaît
 * ni au repos ni au survol. Mesuré le 11/09/2026 — « Archiver » rendait
 * exactement les mêmes trois couleurs que « Voir l'annonce ».
 *
 * Quatrième classe de la journée à ne désigner personne, après `.sous`,
 * `ui-ui-num` et `.cn-quand`. Celle-ci se taisait au pire endroit : sur le seul
 * bouton de la barre qui retire un bien du carnet.
 *
 * `danger` SE MARQUE DÈS LE REPOS : on ne découvre pas qu'un bouton détruit au
 * moment où la souris est déjà dessus.
 *
 * ET IL RESTE SEUL DEPUIS LE 24/09/2026. `positif` et `attention` ont été
 * écrites ce jour-là de 2026 pour « Remettre au carnet » et « Archiver » ; les
 * deux boutons sont devenus des PASTILLES RONDES la semaine suivante
 * (`cn-rond-positif`, `cn-rond-attention`), et plus rien ne les portait. Elles
 * ne se voyaient qu'au survol, donc leur disparition ne se voyait pas du tout.
 *
 * *Une règle sans porteur est un piège* : elle se relit comme une primitive
 * disponible, et le prochain qui écrira `class="ui-btn ui-btn-positif"`
 * obtiendra un bouton neutre sans comprendre pourquoi. Relevé douze fois dans
 * ce projet. Pour les rendre, il faudra les réécrire — c'est trois lignes, et
 * les couleurs sont dans l'historique :
 *
 *     positif    #2e8f5e / #eaf6f0 / #1a7f45   le vert des chiffres solides
 *     attention  var(--ambre) / #fdf6e7        l'ambre des chiffres fragiles
 */
.ui .ui-btn-danger { border-color: #e0c0b6; color: #9c3a1c; }
.ui .ui-btn-danger:hover { border-color: #9c3a1c; background: #fdf2ef; color: #9c3a1c; }
/* REVENUES LE 23/10/2026 avec la refonte de secteurs.php — « Réactiver », « Ajouter » et « Désactiver ». Au survol
   seulement, comme à l'origine : au repos, une rangée de trois couleurs se lirait comme un feu tricolore. */
.ui .ui-btn-positif:hover { border-color: #2e8f5e; background: #eaf6f0; color: #1a7f45; }
.ui .ui-btn-attention:hover { border-color: var(--ambre); background: #fdf6e7; color: var(--ambre); }

/* ─── Ajouts du 09/09/2026, réclamés par le carnet ───
   Chacune est entrée ici parce qu'une SECONDE page en avait besoin — jamais par
   anticipation. Une primitive qui ne sert qu'à un écran appartient à cet écran. */

/* Un bloc de surface : ce que `city.css` appelle une carte, sans son ombre ni
   son rayon marqué. Il sépare, il ne surélève pas. */
.ui-bloc {
  background: var(--surface); border: 1px solid var(--trait);
  border-radius: 14px; padding: 20px;
}
.ui-bloc + .ui-bloc { margin-top: 18px; }
/* UN TITRE DE BLOC, DEUX FAÇONS DE L'ATTEINDRE. `.ui-bloc > h2` ne vise que
   l'enfant DIRECT — voulu, pour qu'un titre de contenu ne l'attrape pas au
   passage — mais un bloc à onglets pose ses titres dans ses volets, d'un cran
   plus bas. Plutôt que de recopier les trois déclarations dans la feuille de la
   page (le doublon que ce projet répète), la classe les rend disponibles là où
   la descendance directe ne suffit pas. */
.ui-bloc > h2,
.ui-titre {
  margin: 0 0 16px; font-size: 15px; font-weight: 600; letter-spacing: -.01em;
}
.ui-bloc > h2 .ui-sous,
.ui-titre .ui-sous { font-weight: 400; }

/* Une action de ligne ou de tableau : même bouton, plus court. Une variante en
   classe plutôt qu'un `style=` posé sur place — c'est le prochain doublon qui
   commence. */
.ui-btn-petit { padding: 5px 11px; font-size: 12.5px; border-radius: 8px; }

/* UN BOUTON DANS UNE BOÎTE DE CHAMP NE S'ÉTIRE PAS À LA LARGEUR DU CHAMP.
   `.ui-champ` est une COLONNE flex, donc ses enfants s'étirent par défaut — ce
   qui est juste pour un `<input>`, dont la largeur est celle de la saisie, et
   faux pour un bouton, dont la largeur est celle de son mot. Mesuré le
   27/09/2026 sur « + Ajouter un libellé » : **778 px pour un contenu de 127**,
   dans une cellule pleine largeur. Étiré à ce point il ne se lit plus comme un
   bouton mais comme un champ de plus. */
.ui-champ > .ui-btn { align-self: flex-start; }

/* Des chiffres qui s'alignent d'une ligne à l'autre. */
.ui-num { font-variant-numeric: tabular-nums; text-align: right; }

/* ─── Onglets ───
   Un souligné qui suit l'onglet actif, sans encadré ni fond : la page dessous
   est déjà un bloc, et empiler deux contenants pour une bascule de vue ajoute
   une bordure sans ajouter de sens. */
.ui-onglets {
  display: flex; gap: 22px; margin: 0 0 18px;
  border-bottom: 1px solid var(--trait);
}
/* UN ONGLET QUI NAVIGUE EST UN LIEN — 28/09/2026. Le socle ne connaissait que
   le `button`, parce que les onglets d'une carte d'annonce basculent un panneau
   déjà rendu. Ceux d'une simulation neuve changent de MODE, donc d'URL, et *un
   bouton qui navigue trompe qui s'en sert au clavier ou au lecteur d'écran* —
   c'est la règle qui a déjà fait d'« Annuler » un lien sur le carnet
   d'adresses. Seule la liste d'éléments s'élargit ; aucune déclaration ne bouge.
   `text-decoration` et `color` sont les deux seules choses qu'un `<a>` apporte
   et que ces règles ne disaient pas. */
.ui-onglets button,
.ui-onglets a {
  border: 0; background: none; cursor: pointer;
  text-decoration: none;
  padding: 9px 2px; margin-bottom: -1px;
  border-bottom: 2px solid transparent;
  color: var(--sourdine); font-size: 13.5px;
}
/* L'ACTIF PASSE APRÈS LE SURVOL, et pas par goût de l'ordre : les deux règles
   valent 0-2-1, donc à égalité c'est la DERNIÈRE écrite qui gagne. Dans l'autre
   sens, survoler l'onglet COURANT lui reprenait son accent pour lui rendre de
   l'encre — il perdait sa couleur au moment précis où on le vise. */
.ui-onglets button[aria-selected="true"],
.ui-onglets a[aria-selected="true"] {
  color: var(--accent); border-bottom-color: var(--accent); font-weight: 600;
}
.ui-onglets button:hover,
.ui-onglets a:hover { color: var(--encre); }
.ui-onglets button[aria-selected="true"]:hover,
.ui-onglets a[aria-selected="true"]:hover { color: var(--accent); }

/* ─── LES PERSONNES RATTACHÉES À UN BIEN ───
 *
 * DANS LE SOCLE PARCE QUE DEUX PAGES LE RÉCLAMENT — `biens.php` et le carnet v2 d'alors
 * l'affichent avec le même gabarit, `carnetPersonneLigne()`. La règle du projet
 * est qu'on ne monte rien ici par anticipation ; ici le second usage existait le
 * jour même, et le recopier dans les deux feuilles du carnet aurait été le
 * doublon habituel.
 *
 * La pastille, l'écriture du nom et les icônes viennent des primitives déjà
 * posées pour le carnet d'adresses : trois écrans qui montrent une personne
 * doivent la montrer pareil.
 */
.ui .ui-personnes { list-style: none; margin: 0; padding: 0; }
.ui .ui-personne {
  /* UNE RANGÉE, 09/10/2026 (variante 2) : initiales, nom au-dessus du métier, et à droite UN groupe de gestes —
     la maisonnette des biens avec son nombre en exposant, puis appeler, écrire, échanges, modifier. Le rôle sur le
     bien n'est plus affiché : c'est ce qui rend le groupe assez court pour laisser sa place au nom. */
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 10px; align-items: center;
  padding: 9px 0; border-top: 1px solid var(--trait);
}
.ui .ui-personne:first-child { border-top: 0; }
/* Plus petite que dans le carnet d'adresses : là-bas elle ancre une liste de
   992 lignes qu'on balaie, ici elle accompagne trois noms qu'on lit. */
.ui .ui-personne .ui-pastille { width: 30px; height: 30px; font-size: 11.5px; }
.ui .ui-personne-qui { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.ui .ui-personne-qui .ui-personne-nom, .ui .ui-personne-qui .ui-sous { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ui .ui-personne-nom { font-size: 13.5px; }
/* Le poids vit sur le PATRONYME, pas sur le bloc : posé sur le conteneur il
   emporterait le prénom, et `<b>` valant `bolder` — une valeur RELATIVE — le nom
   monterait à 700 par-dessus. Piège déjà payé sur le carnet d'adresses. */
.ui .ui-personne-nom b { font-weight: 600; }
.ui .ui-personne-nom .prenom { font-weight: 400; }
.ui .ui-personne .ui-sous { font-size: 12px; }
.ui .ui-personne-role { margin: 0; }
/* Le menu de rôle se fait discret tant qu'on ne le vise pas : c'est un réglage,
   pas un renseignement qu'on vient lire. Il reprend sa boîte au survol et au
   clavier.

   SA DENSITÉ PASSE PAR LES JETONS, PAS PAR UN `padding` ÉCRIT À LA MAIN — et
   c'est ce qui collait le chevron au mot. Le raccourci posait `3px 22px`, quand
   le chevron du socle se range à `calc(var(--champ-pad-x) - 2px)` du bord sur
   14 px de large : il commençait donc à 24 px du bord et le texte s'arrêtait à
   22 — deux pixels, c'est-à-dire rien. *Une réserve écrite en dur à côté d'un
   dessin qui se calcule finit toujours par ne plus le désigner.*

   Par les jetons, la règle générale refait les deux ensemble : le texte
   s'arrête à `pad-x + 20`, le chevron occupe `pad-x - 2` à `pad-x + 12`, et
   l'écart vaut huit pixels quelle que soit la densité choisie ici. */
.ui .ui-personne-role select {
  --champ-pad-y: 3px; --champ-pad-x: 8px; --champ-corps: 12px;
  border-color: transparent; background-color: transparent; color: var(--sourdine);
}
.ui .ui-personne-role select:hover,
.ui .ui-personne-role select:focus {
  border-color: var(--trait-2); background-color: var(--surface); color: var(--encre);
}
.ui .ui-personne-gestes { display: flex; align-items: center; gap: 4px; }
/* LE NOMBRE DE BIENS EN EXPOSANT SUR LA MAISONNETTE : dans le carré des autres gestes, posé sur son coin haut droit. */
.ui .ui-icone-biens { position: relative; text-decoration: none; }
.ui .ui-icone-biens sup {
  position: absolute; top: 1px; right: 3px; font-size: 9.5px; line-height: 1; font-weight: 600;
  font-variant-numeric: tabular-nums; color: var(--accent);
}
.ui .ui-personne-gestes form { margin: 0; }
.ui .ui-personne .ui-icone { width: 26px; height: 26px; }
/* ─── LE GESTE DISCRET DE FIN DE LIGNE ───
 *
 * UNE SEULE FAÇON D'AFFICHER, relevée par l'utilisateur : le « ✕ » de la frise
 * est un glyphe nu, celui de la liste de contacts était un bouton encadré. Deux
 * apparences pour un même geste sur deux encarts qu'on lit à la suite — et c'est
 * le nu qui gagne, parce qu'un geste correctif ne doit pas peser autant que les
 * actions qu'on vient faire : appeler, écrire, ouvrir.
 *
 * Le crayon, la coche et les deux croix partageaient DÉJÀ neuf déclarations
 * recopiées trois fois dans la feuille du carnet — `.cn-modifier`, `.cn-solder`,
 * `.cn-retirer` — et la version encadrée en faisait une quatrième. Elles vivent
 * ici, et chaque bouton ne garde que sa couleur d'intention.
 *
 * UNE CIBLE DE 24 PX, quelle que soit la taille du glyphe : mesuré en essayant
 * de cliquer, un « ✕ » de 13 × 16 s'attrape une fois sur quatre.
 */
.ui .ui-geste {
  border: 0; background: none; cursor: pointer; color: var(--sourdine);
  font-size: 12px; line-height: 1;
  min-width: 24px; height: 24px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
}
/* Chaque sorte de rangée déclare ce qui la révèle : la primitive dit à quoi ça
   ressemble, la page dit sur quoi on passe la souris. */
.ui .ui-personne:hover .ui-geste,
.ui .ui-ligne:hover .ui-geste,
.ui .ui-geste:focus-visible { opacity: 1; }
/* LES TROIS GESTES DU SITE — 04/11/2026, règle validée (voir CLAUDE.md) :
   SUPPRIMER = poubelle rouge (`.ui-geste.ui-geste-danger` + #i-jeter) ;
   RETIRER d'une sélection sans rien détruire = croix neutre (`.ui-geste` + #i-croix) ;
   MODIFIER = crayon (`.ui-geste.ui-geste-accent` + #i-crayon).
   ILS SONT TOUJOURS VISIBLES (04/11/2026, demandé : « arrête de cacher les boutons poubelles ») : la primitive
   naissait à `opacity: 0` et chaque rangée déclarait ce qui la révèle. Les règles « :hover … .ui-geste »
   qui restent dans les feuilles des pages ne font plus rien. */
.ui .ui-geste { border-radius: 7px; }
.ui .ui-geste > svg { width: 14px; height: 14px; }
.ui .ui-geste:not(.ui-geste-danger):not(.ui-geste-accent):not(.ui-geste-vert):not([aria-disabled="true"]):hover { color: var(--encre); background: var(--teinte); }
.ui .ui-geste-danger:not([aria-disabled="true"]):hover { background: #fbeaea; }
/* `.ui-x` : LA croix de « retirer » et de « fermer », dessinée par la feuille (le tracé de #i-croix) pour les
   boutons fabriqués en JavaScript ou par un composant partagé, qui ne savent pas si la page a émis le sprite.
   Le bouton reste vide ; la croix prend la couleur du texte. */
.ui .ui-x { font-size: 0; display: inline-grid; place-items: center; }
.ui .ui-x::before {
  content: ""; display: block; width: 10px; height: 10px; background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='5 5 14 14'%3E%3Cpath d='M8.5 8.5 15.5 15.5M15.5 8.5 8.5 15.5' stroke='%23000' stroke-width='3' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='5 5 14 14'%3E%3Cpath d='M8.5 8.5 15.5 15.5M15.5 8.5 8.5 15.5' stroke='%23000' stroke-width='3' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat;
}
/* UN GESTE QUI NE PEUT RIEN FAIRE RESTE VISIBLE ET DIT POURQUOI. Il n'est pas
   `disabled` : un bouton désactivé n'émet aucun événement de souris, donc sa
   bulle ne s'ouvrirait jamais — et une explication qu'on ne peut pas lire ne
   vaut pas mieux qu'un bouton absent. `aria-disabled` le dit, `tabindex="-1"`
   lui évite de prendre un arrêt de tabulation par ligne, et aucun écouteur ne
   le vise. */
.ui .ui-geste[aria-disabled="true"] { cursor: default; color: var(--trait-2); }
/* LE ROUGE DÈS QUE LE GESTE EST VISIBLE, plus seulement sous la souris. Il
   n'apparaît qu'au survol de sa rangée, donc il ne pèse jamais au repos : lui
   demander un second survol pour dire ce qu'il fait, c'était le dire au moment
   où il est trop tard. C'est la règle déjà posée sur `.btn-danger`, qui se
   marque au repos — *on ne découvre pas qu'un bouton détruit au moment où la
   souris est déjà dessus.* */
.ui .ui-geste-danger       { color: #b4451f; }
.ui .ui-geste-danger:hover { color: #8f3517; }
/* Le glyphe d'un geste est plus petit que celui d'une icône encadrée : il n'a
   pas de cadre pour le tenir, et il voisine des boutons de 30 px. */
.ui .ui-geste .i { width: 14px; height: 14px; }
.ui .ui-geste-accent:hover  { color: var(--accent); }
.ui .ui-geste-vert:hover    { color: #1a7f45; }
.ui .ui-personne .ui-icone .i { width: 14px; height: 14px; }

/* L'état du lien, quand il y a quelque chose à dire. Ambre pour le PROBLÈME —
   une fiche Google qui ne répond plus — et neutre pour l'absence : une ligne
   saisie ici n'a rien perdu, il lui manque un lien qu'on pose en deux clics.
   Leur donner la même couleur ferait passer une absence pour une panne. */
.ui .cn-rompu { color: var(--ambre); }
.ui .cn-local { color: var(--sourdine); }

/* ─── LA LISTE DE SUGGESTIONS ───
 *
 * Montée de `biens.css` le 16/09/2026, quand une TROISIÈME page l'a réclamée — le
 * champ « Rôle » du carnet d'adresses, qui remplace un `<datalist>` impossible à
 * habiller. Le socle portait déjà `.cn-creer-ici`, `.cn-rompu` et `.cn-local`,
 * c'est-à-dire les feuilles de l'arbre sans son tronc.
 *
 * `top: 100%` N'EST PAS DÉCORATIF. Sans lui la boîte prend sa « position
 * statique » — et dans un conteneur FLEX, la position statique d'un enfant sorti
 * du flux est le coin de départ du conteneur, pas l'endroit où il serait tombé.
 * `.ui-champ` étant une colonne flex, la liste se posait 43 px AU-DESSUS du champ
 * qu'elle complète. */
.ui .cn-suggestions {
  position: absolute; z-index: 20; left: 0; right: 0; top: 100%;
  margin-top: var(--menu-ecart);
  background: var(--surface); border: 1px solid var(--trait);
  border-radius: var(--menu-rayon); box-shadow: var(--menu-ombre);
  max-height: var(--menu-haut); overflow-y: auto;
}
/* UNE LIGNE DE SUGGESTION, QUELLE QUE SOIT SA BALISE. Toutes ces règles ne
   visaient que `button`, et « Créer … dans le carnet » est un `<a>` : il
   NAVIGUE, donc c'est un lien — un bouton qui navigue trompe qui s'en sert au
   clavier, règle déjà tenue ailleurs. Il ne recevait donc ni bloc, ni
   rembourrage, ni taille : son sous-titre se collait au bord de la boîte, deux
   pixels à gauche du titre qu'il accompagne, et le lien gardait son soulignement.
   *Un sélecteur écrit sur la BALISE plutôt que sur l'objet laisse passer le
   premier frère qui change de balise* — c'est la famille des emprunts de classe
   déjà relevés huit fois ici, pris par l'autre bout. */
.ui .cn-suggestions button,
.ui .cn-creer-ici {
  display: block; width: 100%; text-align: left; border: 0; background: none;
  font: inherit; font-size: var(--menu-corps); cursor: pointer;
  padding: var(--menu-ligne-y) var(--menu-ligne-x);
  color: inherit; text-decoration: none;
}
/* LA LIGNE VISÉE PREND L'ACCENT, comme dans le menu d'un `<select>` : la teinte
   seule laissait l'encre noire, donc les deux listes du site disaient « c'est
   cette ligne-là » de deux façons. Une seule langue — fond teinté, texte accent —
   et le sous-titre garde sa sourdine, qui est déjà sa règle. */
.ui .cn-suggestions button:hover,
.ui .cn-suggestions button:focus,
.ui .cn-creer-ici:hover,
.ui .cn-creer-ici:focus { background: var(--teinte); color: var(--accent); outline: none; }
/* LA LIGNE VISÉE AU CLAVIER N'AVAIT AUCUNE RÈGLE — `est-vise` était posée par le
   script depuis le premier jour et ne désignait rien dans les trois feuilles.
   Parcourir la liste aux flèches ne se voyait donc pas, ce qui revient à ne pas
   pouvoir la parcourir. Douzième classe sans porteur relevée dans ce projet, et
   la première trouvée par son ABSENCE de règle plutôt que par un rendu faux. */
.ui .cn-suggestions button.est-vise { background: var(--teinte); color: var(--accent); }
/* LE TITRE D'UNE SUGGESTION N'EST PAS EN GRAS. `autocompleter()` l'enveloppe dans
   un `<strong>` — c'est juste sémantiquement, c'est la partie principale de la
   ligne — et personne ne rattrapait le poids que le navigateur y met. Résultat :
   les 176 rôles s'affichaient tous en gras à côté d'un menu de `<select>` dont
   les lignes sont au poids normal, et les deux listes se lisaient comme deux
   objets pour cette seule raison.

   Le sous-titre se distingue déjà par sa TAILLE et sa COULEUR ; il n'a pas besoin
   qu'on alourdisse ce qu'il accompagne. Même correction que le `<b>` de la liste
   des contacts, borné à 600 parce que `bolder` est une valeur relative. */
.ui .cn-suggestions strong,
.ui .cn-creer-ici strong { font-weight: inherit; }
.ui .cn-suggestions .ui-sous,
.ui .cn-creer-ici .ui-sous { font-size: 11.5px; }
/* ELLE OCCUPE LA MÊME COLONNE QUE LES LIGNES. Son rembourrage était écrit en dur
   — 11/13 — quand les lignes suivent `--menu-ligne-*` : son texte tombait deux
   pixels à droite des titres qu'elle surmonte, dans une boîte où tout est
   aligné. Un écart de deux pixels ne se nomme pas, il se voit. */
.ui .cn-rien-trouve {
  margin: 0; padding: var(--menu-ligne-y) var(--menu-ligne-x);
  font-size: var(--menu-corps); color: var(--sourdine);
}

/* Un champ qui porte une liste en est l'ancre : sans parent positionné, la boîte
   se cale sur la PAGE. Le `:has()` fait que le prochain n'aura rien à déclarer. */
.ui .ui-champ:has(> .cn-suggestions) { position: relative; }

/* ET QUAND LA BOÎTE VIT UN CRAN PLUS BAS, C'EST SON ENVELOPPE QUI ANCRE.
   `.cn-champ-liste` enveloppe un champ qui porte quelque chose SOUS lui — une
   ligne d'état, des jetons déjà posés. Le `:has()` ci-dessus ne voit que
   l'enfant DIRECT, donc il ne l'atteint pas : sans parent positionné la liste se
   cale sur la page. Mesuré le 27/09/2026 sur les communes couvertes d'un
   contact : la liste du rôle tombait juste — 180 px de large, 2 px sous son
   champ — quand celle des communes rendait **1 440 px de large à y = 1002**,
   c'est-à-dire pleine largeur tout en bas du document, sous le pli. De là où on
   regarde, une autocomplétion branchée qui dessine hors de l'écran, c'est une
   autocomplétion absente.

   ELLE VIVAIT DANS `assets/biens.css`, ET C'EST CE QUI LA RENDAIT FAUSSE. Le
   composant de critères la pose lui aussi, et `recherche.php` ne tenait que
   parce qu'elle charge cette feuille en plus — *une classe posée par un
   composant partagé ne peut pas avoir ses règles dans la feuille d'une page*,
   la leçon de `.cm-champ-commune` en août. Troisième porteur, donc elle monte.

   `flex` ET NON `block` : un `<input>` est de niveau EN LIGNE, donc un conteneur
   bloc lui réserve en plus la place des jambages sous la ligne de base — trois
   ou quatre pixels que la liste aurait pris pour du jeu. En flex l'élément est
   blockifié, et la boîte fait exactement la hauteur du champ. */
.ui .cn-champ-liste { position: relative; display: flex; gap: 8px; align-items: stretch; }
.ui .cn-champ-liste > input { flex: 1 1 auto; min-width: 0; }

/* « Créer … ici » ferme la liste de suggestions et n'est pas une suggestion : le
   filet le sépare de ce qui précède. Dans le socle parce que les deux pages du
   carnet le posent — et à 0-2-0, ce qui passe devant le `border: 0` de
   `.cn-suggestions button` (0-1-1) sans avoir à crier. */
.ui .cn-creer-ici { border-top: 1px solid var(--trait); }
.ui .cn-creer-ici strong { color: var(--accent); }

/* LE FORMULAIRE DE RATTACHEMENT EMPILE SES CHAMPS, ET IL NE LES ESPAÇAIT PAS.
   `.cn-contact` n'a longtemps porté qu'un champ : `position: relative` lui
   suffisait. Au second, l'étiquette « Son rôle sur ce bien » venait coller
   l'`input` du dessus — un bloc n'espace pas ses enfants tout seul. C'est la
   boîte qui espace, jamais la primitive de texte : même règle que `.ui-aide`,
   dont la marge avait dû être rendue à l'en-tête. */
.ui .cn-contact { display: flex; flex-direction: column; gap: 14px; }

/* Le rôle ne prend pas toute la ligne : il choisit dans cinq mots, le champ de
   recherche a besoin de la largeur. */
.ui .cn-champ-role { max-width: 190px; }

/* --- Le ⓘ d'une explication ------------------------------------------------
   MONTÉ ICI LE 25/09/2026, au SECOND usage : né `.cn-nego-info` sur la fiche
   d'un bien, réclamé par les deux en-têtes de pourcentage de la recherche. Le
   nom disait « négociation » alors que l'objet dit « il y a quelque chose à
   lire » — un nom qui ment finit par produire une règle qu'on n'ose plus
   toucher.
   Il n'emprunte PAS `.rg-info` de `city.css`, qui porte le bleu des pages
   d'origine : deux palettes sur un même écran se lisent comme deux sites.
   `cursor: help` et pas `pointer` — il n'emmène nulle part, il explique. */
.ui .ui-info {
  width: 17px; height: 17px; padding: 0; margin-left: 5px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--trait-2); border-radius: 50%; background: transparent;
  /* UN « i » DROIT, PAS ITALIQUE — 29/09/2026 : l'italique à 11 px se lisait « / », puis un `th` en
     capitales le mettait en « I » : `text-transform: none` est nécessaire.
     Et `font: … inherit` n'était pas valide (`inherit` n'est pas une famille) : la
     déclaration entière tombait, le bouton gardait la fonte du navigateur. */
  color: var(--pale); font-family: inherit; font-size: 11.5px; font-weight: 700;
  line-height: 1; font-style: normal; text-transform: none; letter-spacing: 0; cursor: help;
  vertical-align: 2px;
}
.ui .ui-info:hover, .ui .ui-info:focus-visible { border-color: var(--accent); color: var(--accent); }

/* Un bouton éteint se lit éteint, quel que soit son style : fond neutre, texte lisible (plus de blanc sur teinte pâle) — 26/10/2026 */
.ui .ui-btn:disabled, .ui .ui-btn[aria-disabled="true"] { background: var(--papier, #f4f3ef); border-color: var(--trait); color: var(--sourdine); opacity: 1; filter: none; cursor: not-allowed; box-shadow: none; }
.ui .ui-btn:disabled:hover { background: var(--papier, #f4f3ef); border-color: var(--trait); color: var(--sourdine); }

/* L'AUTORISATION D'URBANISME PROBABLE (`autorisation_urba.php`), dans l'étude et la simulation. La pastille dit la
   demande : PC en accent plein, DP en teinte, rien en sourdine — ce n'est pas une alerte, c'est une démarche à prévoir. */
.ui .urba-auto { font-size: 13.5px; line-height: 1.5; }
.ui .urba-auto-tete { margin: 0 0 8px; display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.ui .urba-auto-pastille { display: inline-block; min-width: 30px; padding: 2px 8px; border-radius: 999px; font-size: 12px; font-weight: 700; text-align: center; }
.ui .urba-auto-pastille.est-pc { background: var(--accent); color: #fff; }
.ui .urba-auto-pastille.est-dp { background: var(--teinte); color: var(--accent); }
.ui .urba-auto-pastille.est-rien { background: var(--papier); color: var(--sourdine); border: 1px solid var(--trait); }
.ui .urba-auto-raisons, .ui .urba-auto-reserves { margin: 0 0 8px; padding-left: 18px; }
.ui .urba-auto-reserves { color: var(--sourdine); font-size: 12.5px; }
.ui .urba-auto-art { color: var(--pale); font-size: 11.5px; margin-left: 4px; white-space: nowrap; }
.ui .urba-auto-faits { margin: 0 0 8px; }
.ui .urba-auto-faits:empty { display: none; }
.ui .urba-auto-note { margin: 0; color: var(--pale); font-size: 12px; }
.urba-auto-copro { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--trait, #e5e1d9); }

/* LE PLANCHER DE 14 m² (CCH L111-6-1) : une interdiction, pas une réserve — rouge, au-dessus des raisons. 28/10/2026. */
.ui .urba-auto-alerte { margin: 0 0 8px; padding: 7px 10px; border-radius: 7px; background: #fdf3f1; color: #b5392b; font-size: 12.5px; font-weight: 500; }
