/* =====================================================================
   Signal360 - bibliothèque de composants d'interface.

   Une seule feuille, chargée sur toutes les pages par enTeteSite(),
   enTeteBord() et enTeteAdmin(). Elle ne décrit que des composants :
   ni page, ni écran, ni exception. Ce qui est propre à un écran reste
   dans sa feuille.

   Elle n'invente aucune valeur : couleurs, rayons, ombres et anneau de
   focus sont ceux de la charte, déclarés dans accueil.css. Les deux
   seules variables qu'elle ne peut pas y trouver - le rouge et le vert,
   qui viennent du pont d'app.css - sont appelées avec leur repli, car
   les pages publiques ne chargent pas ce pont.

   Sommaire
     1. Couche flottante        - le plan où vivent bulles et panneaux
     2. Infobulle
     3. Menu flottant           - clic droit, menu déroulant, filtres
     4. Modale
     5. Champ de saisie
     6. Case à cocher et bouton radio
     7. Interrupteur
     8. Tableau                 - tri, filtres de colonnes, recherche
     9. Calendrier et heure
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. Couche flottante

   Tout ce qui survole la page - bulle, menu, panneau de filtre - est
   déposé ici, à la racine du document. Un panneau né dans un tableau à
   défilement horizontal serait sinon coupé par son cadre.
   --------------------------------------------------------------------- */

.signal-couche{
  position:fixed;inset:0;z-index:300;
  pointer-events:none;                 /* la couche ne bloque rien... */
}
.signal-couche>*{pointer-events:auto}  /* ...mais ses hôtes reçoivent bien */

/* Les composants flottants naissent effacés et montent d'un cheveu :
   assez pour situer d'où ils viennent, pas assez pour faire attendre. */
@keyframes signal-apparition{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}

/* ---------------------------------------------------------------------
   2. Infobulle

   Elle explique, elle ne commente pas : un texte court, jamais un
   paragraphe. Elle apparaît au survol après un délai - une bulle qui
   surgit au moindre passage de souris est un papillonnement - et
   immédiatement au clavier, où l'intention ne fait aucun doute.
   --------------------------------------------------------------------- */

.infobulle{
  position:absolute;max-width:280px;
  padding:8px 11px;
  font-family:inherit;font-size:13px;font-weight:500;line-height:1.45;
  letter-spacing:-.005em;
  color:#fff;background:var(--nuit);
  border-radius:var(--r-s);
  box-shadow:0 6px 20px rgba(16,26,46,.22);
  animation:signal-apparition .12s ease-out;
}
.infobulle[hidden]{display:none}
.infobulle strong{color:#fff;font-weight:700}

/* La flèche est le même carré tourné, jamais un caractère typographique. */
.infobulle::after{
  content:'';position:absolute;width:8px;height:8px;
  background:var(--nuit);transform:rotate(45deg);
}
.infobulle[data-place="haut"]::after  {bottom:-4px;left:var(--pointe,50%);margin-left:-4px}
.infobulle[data-place="bas"]::after   {top:-4px;   left:var(--pointe,50%);margin-left:-4px}
.infobulle[data-place="gauche"]::after{right:-4px; top:var(--pointe,50%); margin-top:-4px}
.infobulle[data-place="droite"]::after{left:-4px;  top:var(--pointe,50%); margin-top:-4px}

/* L'avis - « Copié », « Capture enregistrée » - n'est pas une infobulle,
   même s'il en partage le nœud.

   Il ne désigne aucun élément : pas de pointe, sinon un losange posé là
   sans rien viser. Il ne suit pas non plus ce qu'on a cliqué : il paraît
   toujours en haut de la fenêtre, au milieu, où l'œil le retrouve d'une
   fois sur l'autre. Et il est entièrement arrondi - la pastille dit,
   avant même qu'on la lise, qu'elle ne fait que passer. */
.infobulle.avis{
  left:50%;top:16px;
  transform:translateX(-50%);
  padding:9px 18px;
  border-radius:999px;                 /* arrondi plein : une pastille */
  box-shadow:0 8px 24px rgba(16,26,46,.28);
  animation:signal-avis .14s ease-out;
}
.infobulle.avis::after{content:none}

/* L'avis descend d'un cheveu comme les autres surfaces, mais sans perdre
   son centrage : sa translation horizontale est reprise dans chaque
   image de l'animation. */
@keyframes signal-avis{
  from{opacity:0;transform:translate(-50%,-6px)}
  to  {opacity:1;transform:translate(-50%,0)}
}

/* Le repère d'aide : un rond discret qui ne prend pas la place d'un mot. */
.indice{
  display:inline-grid;place-items:center;
  width:16px;height:16px;margin-left:5px;padding:0;
  vertical-align:-2px;
  color:var(--texte-doux);background:none;
  border:1px solid var(--bord);border-radius:50%;
  cursor:help;
}
.indice svg{width:11px;height:11px;--ico-accent:currentColor}
.indice:hover,.indice:focus-visible{color:var(--rose);border-color:var(--rose-pale);background:var(--rose-clair)}

/* Un mot expliqué se signale par un pointillé, jamais par une couleur :
   la couleur est réservée au lien. */
[data-infobulle]:not(.indice):not(button):not(a):not(th):not([class]){
  border-bottom:1px dotted var(--texte-doux);cursor:help;
}

/* ---------------------------------------------------------------------
   3. Menu flottant

   Le même objet sert au clic droit, au menu déroulant d'un bouton et
   au panneau de filtre d'une colonne. Un seul dessin, donc un seul
   apprentissage.
   --------------------------------------------------------------------- */

.menu-flottant{
  position:absolute;min-width:196px;max-width:320px;
  padding:6px;
  background:var(--surface);
  border:1px solid var(--bord);border-radius:var(--r-m);
  box-shadow:var(--ombre);
  animation:signal-apparition .12s ease-out;
}
.menu-flottant[hidden]{display:none}

.menu-flottant-entete{
  padding:8px 10px 9px;margin:-6px -6px 4px;
  font-size:11.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--texte-doux);
  background:var(--bord-clair);
  border-bottom:1px solid var(--bord);border-radius:var(--r-m) var(--r-m) 0 0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}

.menu-flottant-article{
  display:flex;align-items:center;gap:10px;width:100%;
  padding:8px 10px;
  font-family:inherit;font-size:14px;font-weight:500;line-height:1.35;
  text-align:left;color:var(--nuit);
  background:none;border:0;border-radius:var(--r-s);
  cursor:pointer;
}
.menu-flottant-article svg,
.menu-flottant-place{width:16px;height:16px;flex:none;color:var(--texte-doux)}
.menu-flottant-article .raccourci{
  margin-left:auto;padding-left:14px;
  font-size:11.5px;color:var(--texte-doux);letter-spacing:.02em;
}
/* Le repérage se fait à une seule classe, posée par le clavier comme par
   la souris : les deux ne peuvent pas désigner deux articles à la fois. */
.menu-flottant-article.vise{background:var(--bord-clair)}
.menu-flottant-article.vise svg{color:var(--rose)}
.menu-flottant-article[disabled]{color:var(--texte-doux);cursor:default;opacity:.55}
.menu-flottant-article[disabled].vise{background:none}
.menu-flottant-article.rouge{color:var(--rouge,#B3261E)}
.menu-flottant-article.rouge.vise{background:#FDF1F0}
.menu-flottant-article.rouge svg{color:currentColor}

.menu-flottant-separateur{height:1px;margin:5px 4px;background:var(--bord);border:0}

/* Le bouton « … » qui appelle le menu au clic gauche. Il ne se voit
   qu'au survol de sa ligne : une colonne d'actions permanente pèserait
   plus lourd que ce qu'elle sert. */
.menu-appel{
  display:grid;place-items:center;
  width:26px;height:26px;padding:0;
  color:var(--texte-doux);background:none;
  border:1px solid transparent;border-radius:var(--r-s);
  cursor:pointer;
}
.menu-appel svg{width:15px;height:15px;--ico-accent:currentColor}
.menu-appel:hover,.menu-appel[aria-expanded="true"]{
  color:var(--rose);background:var(--rose-clair);border-color:var(--rose-pale);
}
tr .menu-appel{opacity:0}
tr:hover .menu-appel,tr:focus-within .menu-appel,
.menu-appel[aria-expanded="true"]{opacity:1}

/* ---------------------------------------------------------------------
   4. Modale

   Bâtie sur <dialog> : le navigateur fournit la capture du clavier, la
   touche d'échappement et le fond inerte. On ne réécrit que l'aspect.
   --------------------------------------------------------------------- */

/* Trois bandes, et le corps seul défile : une tête qui nomme et qui
   ferme, un corps, un pied qui conclut. Sans cette colonne, un formulaire
   long poussait le pied hors de l'écran - or la seule chose qu'on cherche
   dans une modale, c'est le bouton qui la termine. */
.modale{
  width:min(560px,calc(100vw - 32px));
  max-height:min(86vh,880px);padding:0;
  color:var(--texte);background:var(--surface);
  border:0;border-radius:var(--r-l);overflow:hidden;
  box-shadow:0 24px 64px rgba(16,26,46,.24);
}
/* « display » ne se pose que sur la fenêtre ouverte : posé sur .modale, il
   l'emporterait sur le « display:none » que le navigateur applique à un
   <dialog> fermé, et la fenêtre s'afficherait à même la page. */
.modale[open]{display:flex;flex-direction:column}
.modale::backdrop{background:rgba(16,26,46,.42);backdrop-filter:blur(2px)}
.modale[open]{animation:modale-entree .16s ease-out}
.modale[open]::backdrop{animation:modale-fond .16s ease-out}
@keyframes modale-entree{from{opacity:0;transform:translateY(8px) scale(.985)}to{opacity:1;transform:none}}
@keyframes modale-fond{from{opacity:0}to{opacity:1}}

.modale-large{width:min(760px,calc(100vw - 32px))}

/* La tête est un bandeau, filet compris : le titre à gauche, la fermeture
   à droite, et une ligne qui dit où commence le contenu. Elle ne défile
   pas - on doit pouvoir refermer sans remonter. */
.modale-tete{
  flex:none;display:flex;align-items:center;gap:16px;
  padding:15px 20px;border-bottom:1px solid var(--bord);
}
.modale-titre{margin:0;font-size:16.5px;font-weight:700;color:var(--nuit);letter-spacing:-.02em}
/* Ronde, comme la croix qu'elle porte, et rose au survol : c'est le geste
   de sortie, il se distingue sans peser. */
.modale-fermer{
  flex:none;display:grid;place-items:center;
  width:30px;height:30px;margin-left:auto;padding:0;
  color:var(--texte-doux);background:none;
  border:0;border-radius:50%;cursor:pointer;
}
.modale-fermer svg{width:15px;height:15px;--ico-accent:currentColor}
.modale-fermer:hover{color:var(--rose);background:var(--rose-clair)}

/* Le corps est la seule bande qui défile ; « min-height:0 » le lui permet,
   sans quoi un enfant flexible refuse de descendre sous sa hauteur propre. */
.modale-corps{
  flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  padding:18px 20px;font-size:14.5px;line-height:1.6;
}
.modale-corps p{margin:0 0 12px;max-width:none}
.modale-corps p:last-child{margin-bottom:0}
.modale-corps strong{color:var(--nuit)}

.modale-pied{
  flex:none;
  display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:10px;
  padding:14px 20px;
  background:var(--bord-clair);
  border-top:1px solid var(--bord);
  border-radius:0 0 var(--r-l) var(--r-l);
}
/* Une modale d'avertissement ne change pas de dessin, seulement d'accent :
   c'est le bouton qui porte la gravité, pas la fenêtre. */
.modale-pied .bouton-danger{background:var(--rouge,#B3261E);color:#fff;border-color:var(--rouge,#B3261E)}
.modale-pied .bouton-danger:hover{background:#8E1E17;border-color:#8E1E17}

@media (max-width:520px){
  .modale-pied{flex-direction:column-reverse}
  .modale-pied .bouton{width:100%}
}

/* ---------------- La capture d'écran (document 29) ----------------

   La fenêtre qui suit une capture : ce qui a été pris, puis à qui
   l'envoyer. L'aperçu passe d'abord parce qu'il est la réponse à la
   question qu'on se pose en premier - « qu'est-ce qui a été pris ? » ;
   le formulaire ne vient qu'ensuite. */
.capture-apercu{
  display:block;width:100%;max-height:min(38vh,300px);
  object-fit:contain;object-position:center top;
  /* Un fond gris derrière l'image : une capture au fond blanc, posée sur
     une fenêtre blanche, paraîtrait n'avoir pas de bord. */
  background:var(--bord-clair);
  border:1px solid var(--bord);border-radius:var(--r-m);
}
.capture-mesure{
  margin:8px 0 0;font-size:12.5px;color:var(--texte-doux);text-align:center;
}
.capture-refus{margin-bottom:16px;font-size:13.5px}

.capture-champs{display:grid;gap:7px;margin-top:18px}
.capture-champs label{
  margin:0;font-size:13.5px;font-weight:700;color:var(--nuit);
}
.capture-champs label+label{margin-top:9px}
.capture-champs label .facultatif{font-weight:400;color:var(--texte-doux)}
.capture-champs select,.capture-champs textarea{
  width:100%;padding:10px 12px;font:inherit;font-size:14.5px;color:var(--nuit);
  background:var(--surface);border:1px solid var(--bord);border-radius:var(--r-s);outline:0;
}
.capture-champs textarea{resize:vertical;line-height:1.55}
/* La note qui remplace les champs quand il n'y a personne à qui écrire :
   elle occupe leur place, pour que la fenêtre ne paraisse pas amputée. */
.capture-seul{
  margin:0;padding:12px 14px;
  font-size:13.5px;line-height:1.55;color:var(--texte-doux);
  background:var(--bord-clair);border-radius:var(--r-s);
}
.capture-champs select:focus,.capture-champs textarea:focus{
  border-color:var(--rose);box-shadow:var(--anneau);
}

/* L'attente se lit sur le contenu, qui pâlit, et non sur le bouton : le
   temps d'un envoi, la fenêtre entière est hors d'atteinte. */
.capture[aria-busy] .modale-corps{opacity:.45;pointer-events:none}

/* ---------------------------------------------------------------------
   5. Champ de saisie

   Le champ nu reste réglé par app.css. Ce qui suit ne concerne que le
   champ habillé : une icône, un bouton d'effacement, une unité, un
   compteur, un état fautif.
   --------------------------------------------------------------------- */

.champ-saisie{position:relative;display:block}
.champ-saisie input,
.champ-saisie textarea{
  width:100%;padding:11px 13px;
  font:inherit;font-size:15px;color:var(--nuit);
  background:var(--surface);
  border:1px solid var(--bord);border-radius:var(--r-s);
  outline:0;
}
.champ-saisie input::placeholder,
.champ-saisie textarea::placeholder{color:var(--texte-doux)}
.champ-saisie input:hover,.champ-saisie textarea:hover{border-color:#DDE1E9}
.champ-saisie input:focus,.champ-saisie textarea:focus{border-color:var(--rose);box-shadow:var(--anneau)}
.champ-saisie input[disabled],.champ-saisie textarea[disabled]{background:var(--bord-clair);color:var(--texte-doux);cursor:not-allowed}

/* Le champ effaçable porte sa propre croix, à la charte et pilotable. Celle
   que le navigateur pose de lui-même sur « input[type=search] » en ferait
   une seconde, collée à la première - deux croix pour un seul geste. */
.champ-saisie input[type="search"]::-webkit-search-cancel-button{
  -webkit-appearance:none;appearance:none;
}

/* L'icône de tête désigne la nature du champ ; elle est muette. */
.champ-saisie>svg{
  position:absolute;left:13px;top:50%;transform:translateY(-50%);
  width:17px;height:17px;color:var(--texte-doux);
  --ico-accent:currentColor;pointer-events:none;
}
.champ-saisie:has(>svg) input{padding-left:40px}

.champ-saisie-effacer{
  position:absolute;right:8px;top:50%;transform:translateY(-50%);
  display:grid;place-items:center;width:24px;height:24px;padding:0;
  color:var(--texte-doux);background:none;border:0;border-radius:50%;
  cursor:pointer;
}
.champ-saisie-effacer svg{width:12px;height:12px;--ico-accent:currentColor}
.champ-saisie-effacer:hover{color:var(--nuit);background:var(--bord-clair)}
.champ-saisie-effacer[hidden]{display:none}
.champ-saisie:has(.champ-saisie-effacer:not([hidden])) input{padding-right:36px}

/* L'unité ou le suffixe : « € », « jours », « km ». */
.champ-saisie-suffixe{
  position:absolute;right:13px;top:50%;transform:translateY(-50%);
  font-size:13.5px;color:var(--texte-doux);pointer-events:none;
}

/* Le bloc d'un champ : intitulé, champ, aide. C'est lui qui porte le
   rythme vertical d'un formulaire, pas les champs eux-mêmes. */
.champ-bloc{margin-bottom:18px}
.champ-bloc:last-child{margin-bottom:0}

.champ-etiquette{
  display:block;margin-bottom:6px;
  font-size:13.5px;font-weight:700;color:var(--nuit);
}
.champ-etiquette .facultatif{font-weight:400;color:var(--texte-doux)}
.champ-aide{margin-top:6px;font-size:12.5px;line-height:1.5;color:var(--texte-doux)}
.champ-compteur{margin-top:6px;font-size:12px;color:var(--texte-doux);text-align:right}
.champ-compteur.proche{color:var(--rose)}
.champ-compteur.depasse{color:var(--rouge,#B3261E);font-weight:600}

/* L'erreur se dit, elle ne se colore pas seulement : le texte porte le
   motif, la couleur ne fait que le signaler. */
.champ-erreur svg{flex:none;width:14px;height:14px;margin-top:2px;--ico-accent:currentColor}
.champ-erreur{
  display:flex;align-items:flex-start;gap:6px;
  margin-top:6px;font-size:12.5px;line-height:1.5;
  color:var(--rouge,#B3261E);font-weight:600;
}
.champ-saisie.fautif input,
.champ-saisie.fautif textarea,
input.fautif,textarea.fautif{border-color:var(--rouge,#B3261E)}
.champ-saisie.fautif input:focus,
.champ-saisie.fautif textarea:focus{box-shadow:0 0 0 3px rgba(179,38,30,.12)}

/* ---------------------------------------------------------------------
   6. Case à cocher et bouton radio

   Elles sont reprises à la racine, sur le champ natif : toute case déjà
   posée dans le site en hérite sans qu'on touche à son gabarit. Le
   champ garde son rôle, son état et son clavier ; seul son dessin
   change. « data-brut » rend une case au navigateur.
   --------------------------------------------------------------------- */

/* La règle porte sur le champ natif lui-même : toute case déjà posée
   dans le site en hérite sans qu'on touche à son gabarit. Les
   exclusions passent par :where(), qui ne pèse rien : les composants
   qui suivent peuvent ainsi les régler sans surenchère de sélecteur.
   « data-brut » rend une case au navigateur. */
input[type="checkbox"]:where(:not([data-brut],[data-interrupteur],.bascule)),
input[type="radio"]:where(:not([data-brut])){
  appearance:none;-webkit-appearance:none;
  flex:none;display:inline-grid;place-content:center;
  /* Le remplissage doit être remis à zéro : les feuilles d'écran en
     posent sur « input » sans distinguer la case du champ de texte, et
     un remplissage plus large que la case en fixe la taille. */
  width:18px;height:18px;min-width:0;max-width:none;margin:0;padding:0;
  background:var(--surface);
  border:1.5px solid #C9CFDB;
  cursor:pointer;
}
input[type="checkbox"]:where(:not([data-brut],[data-interrupteur],.bascule)){border-radius:5px}
input[type="radio"]:where(:not([data-brut])){border-radius:50%}

input[type="checkbox"]:where(:not([data-brut],[data-interrupteur],.bascule)):hover,
input[type="radio"]:where(:not([data-brut])):hover{border-color:var(--rose)}

input[type="checkbox"]:where(:not([data-brut],[data-interrupteur],.bascule)):checked,
input[type="checkbox"]:where(:not([data-brut],[data-interrupteur],.bascule)):indeterminate,
input[type="radio"]:where(:not([data-brut])):checked{background:var(--rose);border-color:var(--rose)}

/* La coche est tracée, pas écrite : deux filets et une rotation, comme
   le chevron du sélecteur. */
input[type="checkbox"]:where(:not([data-brut],[data-interrupteur],.bascule))::before{
  content:'';width:5px;height:9px;
  transform:rotate(45deg) translate(0,-1px) scale(0);
  border-right:2px solid #fff;border-bottom:2px solid #fff;
  border-radius:1px;
  transition:transform .14s cubic-bezier(.2,.8,.3,1.4);
}
input[type="checkbox"]:where(:not([data-brut],[data-interrupteur],.bascule)):checked::before{
  transform:rotate(45deg) translate(0,-1px) scale(1);
}
/* L'état indéterminé - « une partie seulement » - se dit d'un trait. */
input[type="checkbox"]:where(:not([data-brut],[data-interrupteur],.bascule)):indeterminate::before{
  width:9px;height:0;
  transform:none;border-right:0;border-bottom:2px solid #fff;
}
input[type="radio"]:where(:not([data-brut]))::before{
  content:'';width:7px;height:7px;border-radius:50%;background:#fff;
  transform:scale(0);transition:transform .14s cubic-bezier(.2,.8,.3,1.4);
}
input[type="radio"]:where(:not([data-brut])):checked::before{transform:scale(1)}

input[type="checkbox"]:where(:not([data-brut],[data-interrupteur],.bascule)):disabled,
input[type="radio"]:where(:not([data-brut])):disabled{
  background:var(--bord-clair);border-color:var(--bord);cursor:not-allowed;
}
input[type="checkbox"]:where(:not([data-brut],[data-interrupteur],.bascule)):disabled::before{border-color:var(--texte-doux)}
input[type="radio"]:where(:not([data-brut])):disabled::before{background:var(--texte-doux)}

/* La case avec son intitulé : toute la ligne est cliquable. */
.coche{
  display:inline-flex;align-items:flex-start;gap:10px;
  font-size:14.5px;font-weight:400;line-height:1.5;color:var(--texte);
  cursor:pointer;
}
.coche input{margin-top:1px}
.coche:hover{color:var(--nuit)}
.coche.bloc{display:flex;padding:7px 0}

/* ---------------------------------------------------------------------
   7. Interrupteur

   Il vaut pour un réglage qui prend effet aussitôt - une alerte qu'on
   suspend, une diffusion qu'on coupe. Pour un choix qu'un formulaire
   validera plus tard, la case à cocher reste la bonne forme.

   Le champ porte « data-interrupteur » : la règle des cases l'a écarté,
   il n'y a donc rien à défaire ici.
   --------------------------------------------------------------------- */

.interrupteur{
  display:inline-flex;align-items:center;gap:11px;
  font-size:14.5px;color:var(--texte);cursor:pointer;
}
.interrupteur input{
  appearance:none;-webkit-appearance:none;
  flex:none;position:relative;
  width:38px;height:22px;margin:0;padding:0;
  background:#C9CFDB;border:0;border-radius:999px;
  cursor:pointer;transition:background .18s ease;
}
.interrupteur input::before{
  content:'';position:absolute;top:3px;left:3px;
  width:16px;height:16px;border-radius:50%;
  background:#fff;box-shadow:0 1px 2px rgba(16,26,46,.28);
  transition:transform .18s cubic-bezier(.3,.9,.4,1.2);
}
.interrupteur input:checked{background:var(--rose)}
.interrupteur input:checked::before{transform:translateX(16px)}
.interrupteur input:hover{background:#B6BECE}
.interrupteur input:checked:hover{background:var(--rose-sombre)}
.interrupteur input:disabled{background:var(--bord);cursor:not-allowed}
.interrupteur input:disabled::before{box-shadow:none}
.interrupteur-texte{font-weight:500}
.interrupteur input:checked~.interrupteur-texte{color:var(--nuit)}
.interrupteur.attente{opacity:.55;pointer-events:none}

/* ---------------------------------------------------------------------
   8. Tableau

   Un tableau enrichi reste un tableau : les lignes sont celles que le
   serveur a rendues, le tri et les filtres ne font que masquer et
   réordonner ce qui est déjà là. Rien n'est demandé au réseau, et le
   tableau reste entier pour qui n'a pas de JavaScript.
   --------------------------------------------------------------------- */

.tableau-cadre{
  background:var(--surface);
  border:1px solid var(--bord);border-radius:var(--r-m);
  /* « clip » rogne les angles sans créer de zone de défilement - c'est ce
     qui laisse l'en-tête s'accrocher à la page. « hidden » reste le repli
     des navigateurs qui l'ignorent : ils perdent l'en-tête collant, pas
     le tableau. */
  overflow:hidden;overflow:clip;
}

/* Le défilement de côté n'est posé que lorsqu'il sert. Une zone de
   défilement, même purement horizontale, devient le point d'accroche de
   l'en-tête : il se collerait alors au haut du tableau, c'est-à-dire
   nulle part. Le script ne pose « deborde » qu'après mesure. */
.tableau-defilement{overflow:visible}
.tableau-defilement.deborde{overflow-x:auto}
.tableau-defilement.hauteur-propre{overflow:auto;max-height:var(--tableau-hauteur,none)}

.tableau-barre{
  display:flex;align-items:center;flex-wrap:wrap;gap:12px;
  padding:12px 14px;
  background:var(--bord-clair);border-bottom:1px solid var(--bord);
}
.tableau-barre .champ-saisie{flex:1 1 240px;max-width:340px}
.tableau-barre .champ-saisie input{padding-top:8px;padding-bottom:8px;font-size:14px}
.tableau-barre[hidden]{display:none}
.tableau-compte{font-size:13px;color:var(--texte-doux);white-space:nowrap}
.tableau-compte strong{color:var(--nuit);font-weight:700}
.tableau-remise{
  margin-left:auto;padding:7px 13px;
  font-family:inherit;font-size:13px;font-weight:600;
  color:var(--rose);background:var(--surface);
  border:1px solid var(--rose-pale);border-radius:var(--r-s);
  cursor:pointer;
}
.tableau-remise:hover{background:var(--rose-clair);border-color:var(--rose)}
.tableau-remise[hidden]{display:none}

.tableau-cadre table{width:100%;border-collapse:collapse;font-size:14.5px}

/* L'en-tête tient en haut de la fenêtre pendant qu'on descend la liste.
   C'est « position: sticky » qui le tient, et non un déplacement au
   défilement : le navigateur le compose lui-même, sans jamais retarder
   d'une image sur le reste de la page.

   « --plafond » est ce qui masque le haut de la fenêtre - le bandeau du
   site quand il y en a un. Il est mesuré une fois et vaut zéro dans
   l'espace abonné, dont le haut est libre. */
.tableau-cadre thead th{
  position:sticky;top:var(--plafond,0px);z-index:2;
  padding:0;
  background:var(--bord-clair);
  border-bottom:1px solid var(--bord);
  text-align:left;vertical-align:middle;
}
.tableau-tete{
  display:flex;align-items:center;gap:4px;
  padding:9px 14px;
  font-size:11.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;
  color:var(--texte-doux);white-space:nowrap;
}
.tableau-tete-intitule{
  flex:1 1 auto;min-width:0;
  display:flex;align-items:center;gap:5px;
  padding:2px 0;
  font:inherit;letter-spacing:inherit;text-transform:inherit;color:inherit;
  background:none;border:0;text-align:left;cursor:pointer;
}
.tableau-tete-intitule:hover{color:var(--nuit)}
.tableau-tete-intitule span{overflow:hidden;text-overflow:ellipsis}

/* Le sens du tri est dessiné, et l'en-tête trié reprend la couleur
   d'accent : la flèche seule serait trop petite pour se voir. */
.tableau-fleche{flex:none;width:8px;height:8px;opacity:0}
.tableau-fleche::before{
  content:'';display:block;width:6px;height:6px;
  border-left:1.5px solid currentColor;border-top:1.5px solid currentColor;
  transform:rotate(45deg) translate(1px,1px);
}
.tableau-tete-intitule:hover .tableau-fleche{opacity:.4}
th[aria-sort] .tableau-tete{color:var(--rose)}
th[aria-sort] .tableau-fleche{opacity:1}
th[aria-sort="descending"] .tableau-fleche::before{transform:rotate(-135deg) translate(-2px,-2px)}

.tableau-filtre{
  flex:none;display:grid;place-items:center;
  width:22px;height:22px;padding:0;
  color:var(--texte-doux);background:none;
  border:0;border-radius:5px;cursor:pointer;
}
.tableau-filtre svg{width:13px;height:13px;--ico-accent:currentColor}
.tableau-filtre:hover{color:var(--nuit);background:#E4E8EF}
.tableau-filtre.actif,.tableau-filtre[aria-expanded="true"]{color:var(--rose);background:var(--rose-clair)}

.tableau-cadre tbody td{
  padding:12px 14px;vertical-align:top;line-height:1.5;
  border-bottom:1px solid var(--bord-clair);
}
.tableau-cadre tbody tr:last-child td{border-bottom:0}
.tableau-cadre tbody tr:hover{background:#FBFCFD}
.tableau-cadre tbody tr.hors-filtre{display:none}
.tableau-cadre tbody tr.designee{background:var(--rose-clair)}

/* Un tableau dense - sept colonnes d'administration - resserre son
   corps et ses gouttières sans changer de dessin. */
.tableau-cadre.dense table{font-size:13px}
.tableau-cadre.dense .tableau-tete{padding:8px 12px;font-size:10.5px}
.tableau-cadre.dense tbody td{padding:10px 12px}
.tableau-cadre.dense .tableau-barre{padding:10px 12px}

.tableau-vide td{
  padding:34px 14px;
  color:var(--texte-doux);text-align:center;font-size:14px;
}

/* Le panneau de filtre d'une colonne : une recherche, des valeurs, un
   pied. Il tient dans un menu flottant, dont il reprend le cadre. */
.filtre-colonne{width:262px}
.filtre-colonne .filtre-recherche{
  width:100%;padding:8px 10px;margin-bottom:6px;
  font:inherit;font-size:13.5px;color:var(--nuit);
  background:var(--bord-clair);
  border:1px solid transparent;border-radius:var(--r-s);outline:0;
}
.filtre-colonne .filtre-recherche:focus{background:var(--surface);border-color:var(--rose);box-shadow:var(--anneau)}

.filtre-tris{display:flex;gap:6px;margin-bottom:6px}
.filtre-tris button{
  flex:1;padding:6px 8px;
  font:inherit;font-size:12.5px;font-weight:600;color:var(--texte);
  background:var(--surface);border:1px solid var(--bord);border-radius:var(--r-s);
  cursor:pointer;
}
.filtre-tris button:hover{color:var(--rose);border-color:var(--rose-pale)}

.filtre-liste{max-height:238px;overflow-y:auto;margin:0;padding:2px 0;list-style:none}
.filtre-choix{
  display:flex;align-items:center;gap:9px;
  padding:6px 8px;border-radius:var(--r-s);
  font-size:13.5px;line-height:1.4;color:var(--texte);cursor:pointer;
}
.filtre-choix:hover{background:var(--bord-clair)}
.filtre-choix .valeur{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.filtre-choix .nombre{flex:none;font-size:11.5px;color:var(--texte-doux);font-variant-numeric:tabular-nums}
.filtre-choix.tout{font-weight:700;color:var(--nuit);border-bottom:1px solid var(--bord);border-radius:0;margin-bottom:3px;padding-bottom:8px}
.filtre-rien{padding:16px 8px;font-size:13px;color:var(--texte-doux);text-align:center}

.filtre-pied{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  margin:6px -6px -6px;padding:9px 12px;
  background:var(--bord-clair);border-top:1px solid var(--bord);
  border-radius:0 0 var(--r-m) var(--r-m);
}
.filtre-pied button{
  font:inherit;font-size:13px;font-weight:600;
  background:none;border:0;padding:0;cursor:pointer;
}
.filtre-effacer{color:var(--texte-doux)}
.filtre-effacer:hover{color:var(--nuit)}
.filtre-appliquer{color:var(--rose)}
.filtre-appliquer:hover{color:var(--rose-sombre)}

@media (max-width:640px){
  .tableau-barre{padding:10px}
  .tableau-barre .champ-saisie{max-width:none}
  .tableau-remise{margin-left:0}
}

/* ---------------------------------------------------------------------
   9. Calendrier et heure

   Le champ natif reste dans le formulaire et porte la valeur soumise -
   même règle que le sélecteur : le composant n'est que la présentation,
   et sans JavaScript la saisie native demeure.
   --------------------------------------------------------------------- */

input[data-date].monte,input[data-heure].monte,input[data-second].monte,
input[data-jetons].monte{
  position:absolute;width:1px;height:1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;
}

/* Ce que le sélecteur de période replie : le libellé du champ de fin, et le
   « Du » du premier - le bouton dit la période entière. Le champ replié
   reste dans le formulaire et continue d'être soumis. Le « !important »
   est assumé : la page qui a posé ces libellés leur donne souvent une mise
   en forme plus spécifique (« .bord-choix label »), et un repli qui ne
   gagne pas à tous les coups laisse un « au » orphelin dans l'en-tête. */
.periode-repliee{display:none !important}

.date-champ{
  display:inline-flex;align-items:center;gap:9px;
  min-width:172px;padding:10px 13px;
  /* Le composant peut naître dans un libellé en capitales (.filtres) :
     il revient à la composition courante, comme le fait le sélecteur. */
  font:inherit;font-size:15px;font-weight:500;color:var(--nuit);
  letter-spacing:-.005em;text-transform:none;
  text-align:left;
  background:var(--surface);
  border:1px solid var(--bord);border-radius:var(--r-s);
  cursor:pointer;
}
.date-champ svg{width:16px;height:16px;flex:none;color:var(--texte-doux)}
.date-champ .valeur{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.date-champ .vide{color:var(--texte-doux);font-weight:400}
.date-champ:hover{border-color:#DDE1E9}
.date-champ[aria-expanded="true"]{border-color:var(--rose);box-shadow:var(--anneau)}
.date-champ[disabled]{background:var(--bord-clair);color:var(--texte-doux);cursor:not-allowed}
.date-champ-large{width:100%}

.calendrier{width:280px;padding:12px}

.calendrier-tete{
  display:flex;align-items:center;gap:6px;margin-bottom:10px;
}
.calendrier-mois{
  flex:1;text-align:center;
  font-size:14.5px;font-weight:700;color:var(--nuit);
  text-transform:capitalize;
}
.calendrier-pas{
  flex:none;display:grid;place-items:center;
  width:28px;height:28px;padding:0;
  color:var(--texte-doux);background:none;
  border:1px solid transparent;border-radius:var(--r-s);cursor:pointer;
}
.calendrier-pas::before{
  content:'';width:7px;height:7px;
  border-left:1.5px solid currentColor;border-bottom:1.5px solid currentColor;
}
.calendrier-pas.avant::before{transform:rotate(45deg) translate(1px,-1px)}
.calendrier-pas.apres::before{transform:rotate(-135deg) translate(1px,-1px)}
.calendrier-pas:hover{color:var(--rose);background:var(--rose-clair);border-color:var(--rose-pale)}
.calendrier-pas[disabled]{opacity:.3;cursor:default;background:none;border-color:transparent;color:var(--texte-doux)}

.calendrier-jours{
  display:grid;grid-template-columns:repeat(7,1fr);gap:2px;
  margin-bottom:4px;
}
.calendrier-jours span{
  padding:4px 0;
  font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;
  color:var(--texte-doux);text-align:center;
}
.calendrier-grille{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}

.calendrier-jour{
  display:grid;place-items:center;
  height:32px;padding:0;
  font:inherit;font-size:13.5px;font-weight:500;color:var(--texte);
  background:none;border:1px solid transparent;border-radius:var(--r-s);
  cursor:pointer;font-variant-numeric:tabular-nums;
}
.calendrier-jour.hors{color:var(--texte-doux);opacity:.5}
.calendrier-jour:hover:not([disabled]){background:var(--bord-clair);color:var(--nuit)}
.calendrier-jour.vise{border-color:var(--rose-pale);background:var(--rose-clair)}
/* Aujourd'hui se signale d'un point, jamais d'un cadre : le cadre est
   réservé au jour choisi. */
.calendrier-jour.aujourdhui{font-weight:800;color:var(--nuit);position:relative}
.calendrier-jour.aujourdhui::after{
  content:'';position:absolute;bottom:4px;left:50%;transform:translateX(-50%);
  width:4px;height:4px;border-radius:50%;background:var(--rose);
}
.calendrier-jour.choisi,.calendrier-jour.choisi:hover{
  background:var(--rose);color:#fff;border-color:var(--rose);font-weight:700;
}
.calendrier-jour.choisi.aujourdhui::after{background:#fff}
.calendrier-jour[disabled]{color:var(--texte-doux);opacity:.32;cursor:default}

/* La période (document 40, R2). Les deux modes en tête du calendrier - un
   commutateur à deux positions, jamais plus - et l'aide d'une ligne qui dit
   le geste attendu. */
.periode-choix{margin-bottom:10px}
.periode-modes{
  display:flex;gap:3px;padding:3px;
  background:var(--bord-clair);border-radius:var(--r-s);
}
.periode-mode{
  flex:1;padding:6px 0;
  font:inherit;font-size:12.5px;font-weight:700;color:var(--texte-doux);
  background:none;border:0;border-radius:calc(var(--r-s) - 3px);cursor:pointer;
}
.periode-mode:hover{color:var(--nuit)}
.periode-mode.actif{
  background:var(--surface);color:var(--nuit);
  box-shadow:0 1px 2px rgba(16,26,46,.10);
}
.periode-aide{
  margin-top:7px;
  font-size:11.5px;color:var(--texte-doux);text-align:center;
}
.periode-aide:empty{display:none}

/* Le libellé au-dessus des deux onglets : il dit le geste attendu. */
.periode-invite{
  margin:0 0 7px;
  font-size:12.5px;font-weight:600;color:var(--nuit);
}

/* La saisie au clavier, au-dessus de la grille. En mode Période, deux
   champs que « au » relie - la même phrase que le bouton du filtre. */
.calendrier-saisie{
  display:flex;align-items:center;gap:6px;margin-bottom:10px;
}
.calendrier-saisie-champ{
  flex:1;width:100%;min-width:0;padding:6px 8px;
  font:inherit;font-size:12.5px;color:var(--nuit);
  /* Centrée et aérée : « 26/08/2026 » se relit chiffre à chiffre, et les
     barres cessent de coller aux quantièmes. */
  text-align:center;letter-spacing:.08em;font-variant-numeric:tabular-nums;
  background:var(--surface);border:1px solid var(--bord);border-radius:var(--r-s);
}
.calendrier-saisie-champ::placeholder{letter-spacing:.04em}
.calendrier-saisie-champ::placeholder{color:var(--texte-doux)}
.calendrier-saisie-champ:focus{outline:none;border-color:var(--rose);box-shadow:var(--anneau)}
.calendrier-saisie-champ.invalide{border-color:var(--rouge)}
.calendrier-saisie-champ.invalide:focus{box-shadow:0 0 0 3px rgba(179,38,30,.12)}
.calendrier-saisie-lien{flex:none;font-size:12px;color:var(--texte-doux)}

/* Le libellé du bouton de période sans libellé propre (le filtre du
   Signal), posé au-dessus : la colonne vient du composant, et le
   libellé revient à la composition courante, le bouton pouvant naître
   dans un libellé en capitales. */
.date-groupe{
  display:inline-flex;flex-direction:column;align-items:flex-start;gap:4px;
}
.date-invite{
  font-size:11.5px;font-weight:600;color:var(--texte-doux);
  text-transform:none;letter-spacing:0;
}

/* L'étendue entre les deux bornes : un fond doux d'un seul tenant, les
   bornes pleines comme un jour choisi. */
.calendrier-jour.dans-periode{
  background:var(--rose-clair);color:var(--nuit);border-radius:0;
}
.calendrier-jour.dans-periode:hover:not([disabled]){background:var(--rose-pale)}
.calendrier-jour.periode-debut{border-radius:var(--r-s) 0 0 var(--r-s)}
.calendrier-jour.periode-fin{border-radius:0 var(--r-s) var(--r-s) 0}
.calendrier-jour.periode-debut.periode-fin{border-radius:var(--r-s)}
.calendrier-jour.periode-debut,.calendrier-jour.periode-fin,
.calendrier-jour.dans-periode.choisi{
  background:var(--rose);color:#fff;border-color:var(--rose);font-weight:700;
}

.calendrier-pied{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  margin:10px -12px -12px;padding:9px 14px;
  background:var(--bord-clair);border-top:1px solid var(--bord);
  border-radius:0 0 var(--r-m) var(--r-m);
}
.calendrier-pied button{
  font:inherit;font-size:13px;font-weight:600;
  background:none;border:0;padding:0;cursor:pointer;
}
.calendrier-aujourdhui{color:var(--rose)}
.calendrier-aujourdhui:hover{color:var(--rose-sombre)}
.calendrier-effacer{color:var(--texte-doux)}
.calendrier-effacer:hover{color:var(--nuit)}
/* Le pied à geste unique (data-butoir) : seul, un lien collé à gauche
   paraîtrait orphelin - il se centre. */
.calendrier-butoir{margin:0 auto}

/* L'heure : deux colonnes qu'on parcourt, plutôt qu'un champ qu'on
   déchiffre. Les minutes vont de cinq en cinq - au-delà, on écrit. */
.horloge{display:flex;padding:0;width:168px}
.horloge-colonne{
  flex:1;max-height:224px;overflow-y:auto;
  /* Aucune marge en tête : l'intitulé collant doit couvrir toute la
     largeur de ce qui passe dessous, sinon les valeurs le débordent. */
  padding:0 6px 6px;list-style:none;margin:0;
  scrollbar-width:thin;
}
.horloge-colonne+.horloge-colonne{border-left:1px solid var(--bord)}
.horloge-entete{
  position:sticky;top:0;padding:8px 0 6px;
  font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--texte-doux);text-align:center;background:var(--surface);
}
.horloge-valeur{
  display:block;width:100%;padding:6px 0;
  font:inherit;font-size:13.5px;font-weight:500;color:var(--texte);
  text-align:center;font-variant-numeric:tabular-nums;
  background:none;border:0;border-radius:var(--r-s);cursor:pointer;
}
.horloge-valeur:hover{background:var(--bord-clair);color:var(--nuit)}
.horloge-valeur.choisi{background:var(--rose);color:#fff;font-weight:700}

/* Le panneau qui porte à la fois le calendrier et l'horloge : ils se
   posent côte à côte et le menu qui les contient ne garde aucune marge
   propre - les deux blocs se touchent par un filet, pas par un vide. */
.menu-flottant.date-panneau{padding:0;display:flex;align-items:stretch;overflow:hidden}
.date-panneau .horloge{border-left:1px solid var(--bord)}
.date-panneau .calendrier-pied{border-radius:0}
.date-panneau .calendrier:only-child .calendrier-pied{border-radius:0 0 var(--r-m) var(--r-m)}

/* Le panneau de filtre porte lui aussi un pied à fond plein : il faut
   que le cadre le rogne, sinon l'angle dépasse. */
.menu-flottant.filtre-colonne{overflow:hidden}

/* Un intitulé lu mais non vu : une colonne d'actions n'a rien à afficher
   en tête, mais elle doit se nommer pour qui l'écoute. */
.hors-vue{
  position:absolute;width:1px;height:1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;
}

/* Un en-tête qu'on ne trie pas ne se donne pas des airs de bouton. */
span.tableau-tete-intitule{cursor:default}

/* ---------------------------------------------------------------------
   Réduction des animations

   La charte le pose déjà globalement dans accueil.css ; on le redit ici
   pour les animations nommées, que la règle générale ne couvre pas.
   --------------------------------------------------------------------- */
@media (prefers-reduced-motion:reduce){
  .infobulle,.menu-flottant,.modale[open],.modale[open]::backdrop{animation:none}
}

/* =====================================================================
   Tuile chiffrée et pastille d'état
   ---------------------------------------------------------------------
   Partagées par l'espace abonné et l'administration (document 15, T1).
   Elles vivaient sous le préfixe « ad- » dans admin.css ; leur place est
   ici, où les deux espaces peuvent les atteindre.
   ===================================================================== */

/* Les séparations sont posées par les cellules et non par un fond que les
   filets laisseraient voir : une dernière rangée incomplète reste blanche
   au lieu de finir sur un pavé gris. */
.tuiles{
  display:grid;margin-top:14px;
  grid-template-columns:repeat(auto-fit,minmax(158px,1fr));
  background:var(--surface);border:1px solid var(--bord);
  border-radius:var(--r-m);overflow:hidden;
}
.tuile{
  display:grid;gap:2px;align-content:start;padding:16px 18px;
  background:var(--surface);box-shadow:inset -1px -1px 0 var(--bord);
}
/* Une tuile cliquable reste une tuile : le soulignement des liens en
   ferait un paragraphe de texte souligné, chiffre compris. */
a.tuile,a.tuile:hover{text-decoration:none}
a.tuile:hover{background:var(--bord-clair)}
.tuile strong{font-size:23px;font-weight:800;color:var(--nuit);line-height:1.1}
.tuile-intitule{font-size:11.5px;color:var(--texte-doux);line-height:1.4}
.tuile-detail{margin-top:3px;font-size:11.5px;color:var(--texte-doux);line-height:1.4}

/* La tuile qui porte l'essentiel. Une seule par bandeau : deux accents ne
   désignent plus rien. */
.tuile.tuile-rose strong{color:var(--rose)}
.tuile.tuile-rose{background:var(--rose-clair)}
a.tuile.tuile-rose:hover{background:var(--rose-pale)}

.tuile-tendance{margin-top:6px;font-size:11.5px;font-weight:700;letter-spacing:.01em}
.tuile-tendance.tuile-neutre{color:var(--texte-doux)}
.tuile-tendance.tuile-bon{color:var(--vert,#2F7D5B)}
.tuile-tendance.tuile-mauvais{color:var(--ambre,#9A6B18)}

/* ---------------- Pastille d'état ---------------- */
.pastilles-etat{
  display:grid;gap:10px;margin-top:22px;
  grid-template-columns:repeat(auto-fill,minmax(232px,1fr));
}
.pastille-etat{
  display:flex;gap:11px;align-items:flex-start;
  padding:13px 15px;border-radius:var(--r-m);
  border:1px solid var(--bord);background:var(--surface);
}
a.pastille-etat:hover{border-color:var(--texte-doux)}
.pastille-etat-point{
  flex:none;width:9px;height:9px;margin-top:6px;border-radius:50%;
  background:var(--texte-doux);
}
.pastille-etat-texte{display:grid;gap:1px;min-width:0}
.pastille-etat-texte strong{font-size:13.5px;font-weight:700;color:var(--nuit);line-height:1.35}
.pastille-etat-texte span{font-size:12px;color:var(--texte-doux);line-height:1.45}
/* Les couleurs d'état ne sont déclarées qu'en administration. Cette feuille
   étant chargée après elle, y poser un « :root » écraserait ses valeurs :
   on passe par des replis, qui servent l'espace abonné sans rien imposer. */
.pastille-vert .pastille-etat-point{background:var(--vert,#2F7D5B)}
.pastille-vert{background:var(--vert-clair,#EDF6F1);border-color:#D6E9DF}
.pastille-ambre .pastille-etat-point{background:var(--ambre,#9A6B18)}
.pastille-ambre{background:var(--ambre-clair,#FBF4E6);border-color:#F0E2C4}
.pastille-rouge .pastille-etat-point{background:var(--rouge,#C13756)}
.pastille-rouge{background:var(--rouge-clair,#FDF0F3);border-color:var(--rose-pale)}

/* ---------------- Courbe d'évolution ----------------
   Elle ne se lit pas, elle se reconnaît : dire si le chiffre au-dessus est
   un accident ou une habitude. D'où sa discrétion - un trait fin, gris,
   dont seul le dernier point porte l'accent. */
.tuile-courbe{display:block;margin-top:10px}
.courbe{display:block;width:100%;height:24px;overflow:visible}
.courbe polyline{
  fill:none;stroke:var(--texte-doux);stroke-width:1.2;
  stroke-linejoin:round;stroke-linecap:round;
  /* Le tracé s'étire à la largeur de la tuile : sans cela l'épaisseur du
     trait s'étirerait avec lui et la courbe s'épaissirait à droite. */
  vector-effect:non-scaling-stroke;
}
.courbe .courbe-fin{fill:var(--rose)}
a.tuile:hover .courbe polyline{stroke:var(--nuit)}

/* ---------------- Marqueur d'état ----------------
   Un mot, sur un fond qui en dit le ton. Il porte un **état**, jamais un
   titre : « Active » et « Recommandée » rendus en gras à la place du nom
   se lisaient comme le nom de la chose. */
.marqueur{
  display:inline-block;padding:3px 10px;border-radius:999px;
  font-size:11px;font-weight:700;letter-spacing:.04em;
  text-transform:uppercase;white-space:nowrap;vertical-align:1px;
}
.marqueur-neutre{background:var(--bord-clair);color:var(--texte-doux)}
.marqueur-rose{background:var(--rose-clair);color:var(--rose)}
.marqueur-vert{background:var(--vert-clair,#EDF6F1);color:var(--vert,#2F7D5B)}
.marqueur-ambre{background:var(--ambre-clair,#FBF4E6);color:var(--ambre,#9A6B18)}
.marqueur-rouge{background:var(--rouge-clair,#FDF0F3);color:var(--rouge,#C13756)}

/* ---------------- Commutateur d'état ----------------
   L'apparence d'un interrupteur, le comportement d'un lien : pour ce qui
   s'active en passant par une étape - un enrôlement, une vérification. Il
   reprend exactement la piste et le galet de « .interrupteur » : deux
   commutateurs qui ne se ressemblent pas à moitié seraient pires que deux
   qui ne se ressemblent pas du tout. */
.commutateur{
  display:inline-flex;align-items:center;gap:10px;
  font-size:12.5px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;
  color:var(--texte-doux);white-space:nowrap;
}
.commutateur-piste{
  flex:none;position:relative;width:38px;height:22px;border-radius:999px;
  background:#C9CFDB;transition:background .18s ease;
}
.commutateur-piste::before{
  content:'';position:absolute;top:3px;left:3px;
  width:16px;height:16px;border-radius:50%;
  background:#fff;box-shadow:0 1px 2px rgba(16,26,46,.28);
  transition:transform .18s cubic-bezier(.3,.9,.4,1.2);
}
.commutateur-actif .commutateur-piste{background:var(--rose)}
.commutateur-actif .commutateur-piste::before{transform:translateX(16px)}
.commutateur-actif{color:var(--rose)}
a.commutateur:hover .commutateur-piste{background:#B6BECE}
a.commutateur:hover{color:var(--nuit)}
/* Figé : l'état en vigueur ne s'éteint pas d'une pichenette. Le curseur le
   dit avant le clic. */
.commutateur-fige{cursor:default}

/* ---------------- Fil d'Ariane ----------------
   Le séparateur est tracé par la feuille, non écrit dans le balisage : un
   chevron dans le texte serait lu à voix haute par un lecteur d'écran, une
   fois par maillon. */
.fil-ariane ol{
  display:flex;flex-wrap:wrap;align-items:center;gap:0;
  margin:0;padding:0;list-style:none;
  font-size:12.5px;line-height:1.5;
}
.fil-ariane li{display:flex;align-items:center}
.fil-ariane li+li::before{
  content:'';flex:none;
  width:5px;height:5px;margin:0 10px;
  border-top:1.5px solid var(--texte-doux);border-right:1.5px solid var(--texte-doux);
  transform:rotate(45deg);opacity:.7;
}
.fil-ariane a{color:var(--texte-doux);font-weight:600}
.fil-ariane a:hover{color:var(--rose)}
.fil-ariane [aria-current]{color:var(--nuit);font-weight:700}

/* ---------------- La main qui salue ----------------
   La vignette du bonjour, tracée par mainSalut(). Elle se dimensionne sur
   le texte qui la porte - dans un titre de 25 px elle fait 26 px - et se
   cale sur la ligne de base comme le ferait une lettre.

   Le salut n'est pas continu : quatre oscillations, puis la main retombe
   et se tient tranquille un peu plus de deux secondes. Une main qui
   s'agite sans fin dans un titre finit par happer l'œil à chaque
   relecture de l'écran ; une main qui salue puis s'arrête dit bonjour une
   fois, ce qui est exactement ce qu'on lui demande.

   Le tracé déborde du carré de 24 quand la main penche : « overflow »
   reste visible, faute de quoi l'auriculaire serait coupé net au sommet
   du geste. */
.salut{
  display:inline-block;vertical-align:-.17em;
  width:1.06em;height:1.06em;margin-left:.14em;
  overflow:visible;
}

/* Les doigts vont en s'affinant, de l'index à l'auriculaire. L'écart est
   d'un dixième de pixel sur la grille de 24 - invisible pris isolément,
   c'est pourtant lui qui empêche la main de se lire comme un râteau. */
.salut-doigts path,.salut-pouce{fill:none;stroke-linecap:round}
.salut-doigts path{stroke-width:2.5}
.salut-doigts path:nth-child(3){stroke-width:2.4}
.salut-doigts path:nth-child(4){stroke-width:2.2}
.salut-pouce{stroke-width:3}

/* L'arc ouvert du logo, en deux échos : c'est le motif signature de la
   maison, et le seul endroit où le rose entre dans la vignette. Le trait
   s'éteint à ses deux bouts - le dégradé est dans le SVG - de sorte que
   l'arc naisse et meure dans le vide plutôt que de s'arrêter net. */
.salut-ondes path{
  fill:none;stroke-width:1.35;stroke-linecap:round;
  opacity:.34;animation:salut-onde 5s ease-in-out infinite;
}
.salut-ondes path+path{stroke-width:1.15;animation-delay:.16s}

/* Le pivot est au poignet - bas de la paume, au milieu - et non au centre
   du carré : une main qui tourne autour de son centre ne salue pas, elle
   dévisse. */
.salut-main{
  transform-box:view-box;transform-origin:11.4px 21px;
  animation:salut-agiter 5s ease-in-out infinite;
}

@keyframes salut-agiter{
  0%,56%,100%{transform:rotate(0deg)}
  8%  {transform:rotate(15deg)}
  18% {transform:rotate(-9deg)}
  28% {transform:rotate(15deg)}
  38% {transform:rotate(-9deg)}
  47% {transform:rotate(8deg)}
}
@keyframes salut-onde{
  0%,56%,100%{opacity:.3}
  20%,40%{opacity:.9}
}

/* Éteinte pour qui a demandé moins de mouvement. La consigne générale de
   la charte raccourcit les animations à un centième de milliseconde : sur
   une boucle infinie cela ne calme rien, cela fait vibrer. Ici on coupe le
   mouvement pour de bon, et les arcs se posent à leur pleine valeur. */
@media (prefers-reduced-motion:reduce){
  .salut-main,.salut-ondes path{animation:none}
  .salut-ondes path{opacity:.42}
}

/* =====================================================================
   Chargement d'un bouton d'action - le retour au clic.

   Se connecter, créer son compte, recevoir un code, enregistrer : dès
   qu'un bouton déclenche un aller-retour serveur, il le dit. boutons.js
   pose « .est-charge », remplace l'intitulé par un libellé d'attente adapté
   (« Connexion en cours… », « Envoi en cours… », « Redirection en cours… »)
   et fige la largeur pour que rien ne saute.

   L'attente ne se lit pas au centre mais SUR LA BORDURE : la bordure fixe
   s'efface, et un segment lumineux court le long du contour - coins arrondis
   compris. Le tour est tracé par un dégradé conique dont on fait pivoter
   l'origine (« --tour ») : l'anneau, lui, ne bouge pas, seule la lumière se
   déplace, ce qui la garde alignée sur un bouton de n'importe quelle forme.

   Sa couleur est « currentColor » : elle épouse chaque variante sans une
   règle de plus - blanc sur un bouton plein, rose sur un contour, rouge sur
   un bouton de danger, bleu sur l'administration.
   ===================================================================== */
@property --tour{ syntax:'<angle>'; inherits:false; initial-value:0turn; }

.est-charge{
  position:relative;
  pointer-events:none;              /* pas de second envoi tant qu'on attend */
  cursor:progress;
  border-color:transparent !important;   /* la bordure fixe cède la place */
}
/* Le libellé d'attente reste lisible, au-dessus de l'anneau. */
.est-charge > .bouton-libelle{ position:relative; z-index:1; }

.est-charge::before{
  content:'';
  position:absolute; inset:0;
  border-radius:inherit;
  box-sizing:border-box;
  padding:2.5px;                    /* l'épaisseur du trait qui court */
  background:conic-gradient(from var(--tour), currentColor, transparent 130deg);
  /* On ne garde du dégradé que l'anneau : le plein du bouton est retiré du
     masque (content-box), le pourtour reste. */
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
          mask-composite:exclude;
  animation:est-charge-tour .9s linear infinite;
  pointer-events:none;
}
@keyframes est-charge-tour{ to{ --tour:1turn; } }

/* Moins de mouvement : le trait ne court plus mais cerne le bouton, discret
   et entier, pour que « en cours » se voie encore - le libellé d'attente le
   dit déjà. On force la valeur car la charte raccourcit toute animation à
   .01ms, ce qui sur une boucle ne la calme pas mais la fait vibrer. */
@media (prefers-reduced-motion:reduce){
  .est-charge::before{
    animation:none !important;
    background:currentColor;
    opacity:.35;
  }
}

/* =====================================================================
   Champ à jetons (input[data-jetons]) - la saisie à virgules rendue
   visible : chaque mot retenu devient un jeton qu'on retire d'un clic.
   Même silhouette que le champ des sociétés (societes.css) ; c'est le
   cadre qui porte l'état du champ, la saisie intérieure n'a rien à en
   montrer - d'où les remises à zéro appuyées, pour l'emporter sur les
   règles générales des formulaires quel que soit l'ordre des feuilles.
   ===================================================================== */
.jetons{
  display:flex;flex-wrap:wrap;align-items:center;gap:6px;
  padding:6px 8px;background:var(--surface);
  border:1px solid var(--bord);border-radius:var(--r-s);
  cursor:text;transition:border-color .14s,box-shadow .14s;
}
.jetons:hover{border-color:#DDE1E9}
.jetons:focus-within{border-color:var(--rose);box-shadow:var(--anneau)}

.jetons-liste{
  display:flex;flex-wrap:wrap;gap:6px;min-width:0;
  list-style:none;margin:0;padding:0;
}
.jetons-liste:empty{display:none}

.jetons-jeton{
  display:inline-flex;align-items:center;gap:6px;max-width:100%;
  padding:4px 5px 4px 10px;
  font-size:13.5px;color:var(--nuit);
  background:var(--rose-clair);border:1px solid var(--rose-pale);
  border-radius:var(--r-s);
}
.jetons-mot{
  font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.jetons-retirer{
  flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
  width:20px;height:20px;padding:0;
  color:var(--texte-doux);background:none;border:0;border-radius:50%;
  cursor:pointer;transition:color .12s,background-color .12s;
}
.jetons-retirer:hover{color:var(--rose-sombre);background:rgba(217,76,106,.12)}
.jetons-retirer:focus-visible{outline:2px solid var(--rose);outline-offset:1px}
.jetons-retirer .ico{width:13px;height:13px}

.jetons > input.jetons-saisie[type="text"]{
  flex:1 1 12ch;width:auto;min-width:12ch;
  font:inherit;font-size:14px;color:var(--nuit);
  padding:5px 4px;background:none;
  border:0;border-radius:0;outline:0;box-shadow:none;
}
.jetons > input.jetons-saisie[type="text"]:hover,
.jetons > input.jetons-saisie[type="text"]:focus,
.jetons > input.jetons-saisie[type="text"]:focus-visible{
  border:0;outline:0;box-shadow:none;
}
.jetons > input.jetons-saisie[type="text"]::placeholder{color:var(--texte-doux)}
