/* Backoffice EMDR — s'appuie sur les tokens de style.css (thème clair). */

/* LE FOND DE PAGE N'EST PLUS BLANC, et c'est ce qui fait le plus de travail dans
   tout ce fichier : tant que la page et les cartes partagent le même blanc, une
   carte n'est qu'un liseré, un tableau flotte dans le vide et la barre du haut ne
   se distingue de rien. Sur un fond légèrement plus sombre, chaque .card, .panel
   et .table redevient un objet posé — sans qu'une seule de leurs règles change.

   Le jeton est SCOPÉ au backoffice (--color-app-bg, appliqué à body.bo) et ne
   touche pas --color-background : celui-ci peint aussi l'écran du patient
   (.emdr-stage) et le site public. Un fond gris derrière le point qui balaie
   n'aurait rien à y faire. */
.bo { min-height: 100%; background: var(--color-app-bg); }

/* ── Barre du haut ──────────────────────────────────────────────────────────── */
/* Elle est HORIZONTALE et tient sur UNE ligne. Ce qui l'y contraint n'est pas une
   hauteur fixe — ce serait un vœu — mais le fait que la navigation ne porte plus
   que quatre à cinq entrées, les autres étant repliées. `flex-wrap: nowrap` rend
   la promesse vérifiable : si un jour une entrée de trop était ajoutée, la barre
   ne s'empilerait pas en silence, elle serrerait, et cela se verrait. */
.bo-header { background: var(--color-surface); border-block-end: 1px solid var(--color-border);
             position: sticky; inset-block-start: 0; z-index: 20;
             box-shadow: 0 1px 0 rgba(57,54,60,.04), 0 2px 12px rgba(57,54,60,.05); }
.bo-bar { max-width: 1320px; margin: 0 auto; display: flex; align-items: center; flex-wrap: nowrap;
          gap: 18px; padding: 0 22px; min-block-size: 58px; }

.bo-brand { display: flex; align-items: center; flex-shrink: 0; }
.bo-brand a { display: inline-flex; align-items: center; gap: 8px; color: var(--color-text);
              font-weight: var(--font-weight-bold); text-decoration: none; letter-spacing: .3px; }
.bo-logo { height: 50px; display: block; }
.bo-logo-sombre { display: none; }
:root[data-theme="dark"] .bo-logo-clair  { display: none; }
:root[data-theme="dark"] .bo-logo-sombre { display: block; }
.bo-brand-tag { font-size: 11px; font-weight: var(--font-weight-semibold); text-transform: uppercase;
                letter-spacing: .08em; color: var(--color-primary); background: var(--color-primary-soft);
                border-radius: var(--radius-pill); padding: 3px 8px; }

.bo-nav { display: flex; align-items: center; gap: 2px; flex: 1 1 auto; min-width: 0; flex-wrap: nowrap; }

/* Un lien de premier rang et un <summary> de groupe se ressemblent au pixel près :
   c'est la même rangée, le lecteur n'a pas à savoir lequel des deux il vise. */
.bo-link { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap;
           color: var(--color-text-secondary); text-decoration: none; font-size: 14px;
           font-weight: var(--font-weight-medium); padding: 8px 11px; border-radius: var(--radius-sm);
           cursor: pointer; list-style: none; user-select: none;
           transition: background var(--transition-fast), color var(--transition-fast); }
.bo-link::-webkit-details-marker { display: none; }   /* Safari : le triangle natif */
.bo-link:hover { color: var(--color-text); background: var(--color-background-soft); }

/* L'ONGLET COURANT SE LIT AU FOND ET AU TRAIT, pas à la couleur seule. L'ancien
   repère — un texte corail — reposait sur la seule teinte : invisible en
   niveaux de gris, et discutable pour une déficience de perception des rouges.
   Le trait sous l'entrée, lui, se voit sans couleur. */
.bo-link.is-on { color: var(--color-primary); background: var(--color-primary-soft); position: relative; }
.bo-link.is-on::after { content: ""; position: absolute; inset-inline: 10px; inset-block-end: -1px;
                        block-size: 2px; background: var(--color-primary); border-radius: 2px 2px 0 0; }
.bo-link .icone { flex-shrink: 0; opacity: .85; }
.bo-link.is-on .icone { opacity: 1; }
/* `nav-locked` et NON `is-locked` : la feuille porte plus bas un `.is-locked`
   générique qui ajoute `pointer-events: none`. Une entrée verrouillée doit rester
   cliquable — son lien mène à la page des offres, c'est toute son utilité. */
.bo-link.nav-locked { opacity: .6; }
.bo-link.nav-locked:hover { opacity: .9; }

/* Le chevron : deux traits CSS, pas une police d'icônes ni un caractère unicode
   qui changerait de dessin d'un système à l'autre. Il pivote à l'ouverture. */
.bo-caret { inline-size: 7px; block-size: 7px; flex-shrink: 0; margin-inline-start: 1px;
            border-inline-end: 1.6px solid currentColor; border-block-end: 1.6px solid currentColor;
            transform: translateY(-2px) rotate(45deg); transition: transform var(--transition-fast); opacity: .7; }
details[open] > .bo-link .bo-caret { transform: translateY(1px) rotate(-135deg); }

/* ── Menus déroulants ───────────────────────────────────────────────────────── */
.bo-menu { position: relative; }
.bo-sub { position: absolute; inset-block-start: calc(100% + 7px); inset-inline-start: 0; z-index: 30;
          min-inline-size: 224px; padding: 6px; background: var(--color-surface);
          border: 1px solid var(--color-border); border-radius: var(--radius-lg);
          box-shadow: var(--shadow-md); display: flex; flex-direction: column; gap: 1px; }
/* Le menu de droite s'aligne sur SON bord de fin, sinon il déborderait de l'écran.
   inset-inline-end : en arabe, c'est le bord gauche, et rien n'est à réécrire. */
.bo-sub-fin { inset-inline-start: auto; inset-inline-end: 0; min-inline-size: 250px; }

.bo-sub-item { display: flex; align-items: center; gap: 10px; padding: 7px 10px; line-height: 1.4; border-radius: var(--radius-sm);
               color: var(--color-text); text-decoration: none; font-size: 14px; white-space: nowrap;
               background: transparent; border: 0; inline-size: 100%; text-align: start;
               font-family: inherit; cursor: pointer; }
.bo-sub-item:hover { background: var(--color-background-soft); }
.bo-sub-item .icone { flex-shrink: 0; color: var(--color-text-secondary); }
.bo-sub-item.is-on { background: var(--color-primary-soft); color: var(--color-primary); font-weight: var(--font-weight-semibold); }
.bo-sub-item.is-on .icone { color: var(--color-primary); }
.bo-sub-item.is-danger { color: var(--color-error); }
.bo-sub-item.is-danger .icone { color: var(--color-error); }
.bo-sub-item.nav-locked { opacity: .65; }   /* même raison que .bo-link.nav-locked */
/* Le cadenas d'une entrée de PREMIER RANG suit le mot ; celui d'une ligne de menu
   se range au bout, où la liste aligne ses fins. Deux gestes différents parce que
   les deux contenants n'ont pas la même largeur : une rangée horizontale se serre
   sur son texte, une liste verticale a une marge droite commune. */
.bo-lock { opacity: .8; }
.bo-sub-lock { margin-inline-start: auto; opacity: .8; }
.bo-sub-tete { padding: 8px 11px 10px; margin-block-end: 4px; border-block-end: 1px solid var(--color-border-light);
               display: flex; flex-direction: column; gap: 2px; }
.bo-sub-tete strong { font-size: 14px; font-weight: var(--font-weight-semibold); }
.bo-sub-tete span { font-size: 12px; color: var(--color-text-secondary); }

/* Le compteur des demandes, dans la ligne du menu. */
.bo-compteur { margin-inline-start: auto; min-inline-size: 20px; text-align: center; padding: 1px 6px;
               border-radius: var(--radius-pill); background: var(--color-coral); color: #fff;
               font-size: 11px; font-weight: var(--font-weight-bold); font-variant-numeric: tabular-nums; }

/* ── Bloc de droite ─────────────────────────────────────────────────────────── */
.bo-tools { display: flex; align-items: center; gap: 10px; flex-shrink: 0; margin-inline-start: auto; }

.bo-compte { display: inline-flex; align-items: center; gap: 8px; padding: 5px 9px 5px 5px;
             border: 1px solid var(--color-border); border-radius: var(--radius-pill);
             cursor: pointer; list-style: none; user-select: none; background: var(--color-surface); }
.bo-compte::-webkit-details-marker { display: none; }
.bo-compte:hover { border-color: var(--color-lavender); background: var(--color-background-soft); }
.bo-avatar { position: relative; inline-size: 28px; block-size: 28px; border-radius: 50%; flex-shrink: 0;
             display: inline-flex; align-items: center; justify-content: center;
             background: var(--color-primary); color: var(--color-background);
             font-size: 11.5px; font-weight: var(--font-weight-bold); letter-spacing: .3px; }
.bo-who { font-size: 13px; color: var(--color-text); max-inline-size: 150px; overflow: hidden;
          text-overflow: ellipsis; white-space: nowrap; }

/* LA PASTILLE EXISTE PARCE QUE LE COMPTEUR EST REPLIÉ. Une demande de patient
   rangée dans un menu fermé ne se verrait plus du tout : le point la fait
   remonter sur le déclencheur. Il ne porte pas le nombre — il dit « ouvrez »,
   le nombre est une ligne plus bas. */
.bo-pastille { position: absolute; inset-block-start: -5px; inset-inline-end: -6px;
               min-inline-size: 17px; block-size: 17px; padding: 0 4px; border-radius: var(--radius-pill);
               display: inline-flex; align-items: center; justify-content: center;
               background: var(--color-error); color: #fff; border: 2px solid var(--color-surface);
               font-size: 10px; font-weight: var(--font-weight-bold); line-height: 1;
               font-variant-numeric: tabular-nums; }

/* Réservé aux lecteurs d'écran : présent dans l'arbre d'accessibilité, absent de
   l'image. clip-path plutôt que display:none ou visibility:hidden, qui retirent
   aussi le texte de cet arbre — c'est-à-dire exactement ce qu'on veut garder. */
.sr-only { position: absolute; inline-size: 1px; block-size: 1px; padding: 0; margin: -1px;
           overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }

/* ── Menu burger (petits écrans) ────────────────────────────────────────────── */
.bo-burger { display: none; background: transparent; border: 1px solid var(--color-border);
             color: var(--color-text); border-radius: var(--radius-sm); inline-size: 38px; block-size: 38px;
             cursor: pointer; align-items: center; justify-content: center; }
.bo-burger:hover { border-color: var(--color-lavender); }

/* Le seuil est à 1040 px et non à 900 : c'est la largeur en dessous de laquelle
   cinq entrées, le badge d'offre, le sélecteur de langue et le nom cessent de
   tenir côte à côte. Mesuré sur la version française, la plus longue. */
@media (max-width: 1040px) {
  .bo-bar { flex-wrap: wrap; row-gap: 8px; padding-block: 10px; }
  .bo-brand { margin-inline-end: auto; }
  .bo-burger { display: inline-flex; }
  .bo-who { display: none; }

  /* L'ORDRE EST REDÉCLARÉ, sans quoi le tiroir se glisse ENTRE le logo et le
     bouton qui l'a ouvert. .bo-nav précède .bo-tools dans le document — c'est le
     bon ordre de lecture, et il faut le garder pour le clavier et les lecteurs
     d'écran — mais une fois la barre passée sur deux lignes, il pousse le badge,
     la langue, le compte et le burger tout en bas de la liste dépliée. `order`
     ne déplace que le rendu : la source, elle, ne bouge pas. */
  .bo-brand { order: 1; }
  .bo-tools { order: 2; }
  .bo-nav   { order: 3; display: none; flex-basis: 100%; }

  /* Déplié, tout redevient une liste verticale — et les groupes s'ouvrent EN PLACE
     plutôt qu'en surcouche : sur un écran étroit, un menu flottant sort du cadre. */
  .bo-header.open .bo-nav { display: flex; flex-direction: column; align-items: stretch; gap: 2px;
                            padding-block-start: 8px; border-block-start: 1px solid var(--color-border-light); }
  .bo-header.open .bo-link { padding: 11px 12px; }
  .bo-header.open .bo-link.is-on::after { content: none; }
  .bo-header.open .bo-menu .bo-sub { position: static; box-shadow: none; border: 0;
                                     padding: 2px 0 6px; margin-inline-start: 26px;
                                     border-inline-start: 1px solid var(--color-border-light);
                                     border-radius: 0; min-inline-size: 0; }
  /* Le menu de droite, lui, reste flottant : il est ancré au bouton du nom, qui
     ne descend pas dans le tiroir. */
  .bo-header.open .bo-menu-fin .bo-sub { position: absolute; box-shadow: var(--shadow-md);
                                         border: 1px solid var(--color-border); padding: 6px;
                                         margin-inline-start: 0; border-radius: var(--radius-lg);
                                         min-inline-size: 250px; }
}
@media (max-width: 560px) {
  .bo-bar { gap: 10px; padding-inline: 14px; }
  .plan-badge { display: none; }   /* le plan reste lisible dans le menu du compte */
}

.bo-main { max-width: 1320px; margin: 0 auto; padding: 26px 22px 60px; }

/* ── Icônes ─────────────────────────────────────────────────────────────────── */
/* Un SVG inline est en display:inline par défaut : il s'assoit alors sur la ligne
   de base et décale d'un ou deux pixels tout ce qui l'accompagne. `block` +
   l'alignement du parent flex règlent la question une fois pour toutes. */
.icone { display: block; flex-shrink: 0; }

/* Titres */
.page-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 22px; flex-wrap: wrap; }
.page-head h1 { font-size: 22px; margin: 0; font-weight: var(--font-weight-semibold); }
.page-head .sub { color: var(--color-text-secondary); font-size: 13px; margin-top: 3px; }

/* Cartes / grilles */
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; }
.card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg); padding: 18px; box-shadow: var(--shadow-sm); }
.card h3 { margin: 0 0 6px; font-size: 14px; font-weight: var(--font-weight-semibold); }
.stat { font-size: 30px; font-weight: var(--font-weight-bold); font-variant-numeric: tabular-nums; color: var(--color-text); }
.stat-label { color: var(--color-text-secondary); font-size: 12px; text-transform: uppercase; letter-spacing: .6px; }

/* Boutons — MODIFICATEURS SEULEMENT
 *
 * Le SOCLE du composant (.btn, .btn:hover, .btn-run, .btn-stop, .btn-ghost)
 * vivait ici en double de assets/style.css, chargé juste avant. Les deux
 * définitions ayant la même spécificité, c'était la dernière lue qui gagnait,
 * déclaration par déclaration : app.css imposait sa taille (10px 16px / 14px)
 * mais laissait passer le `width: 100%` du socle, qu'aucune des deux ne
 * rétablissait. D'où les six rustines « .prefixe-* .btn { width: auto } »
 * semées dans cette feuille, et les `style="width:auto"` en ligne dans les
 * gabarits d'administration : deux façons de réparer localement un défaut qui
 * n'avait qu'une seule cause. Le socle reste donc seul maître du composant, la
 * pleine largeur redevient un choix explicite du gabarit (.btn-block), et on ne
 * garde ci-dessous que les MODIFICATEURS absents de style.css. */
.btn-sm { padding: 6px 11px; font-size: 13px; }
.btn-primary { background: var(--color-primary); color: #fff; border-color: var(--color-primary); }
.btn-primary:hover { background: var(--color-primary-hover); border-color: var(--color-primary-hover); }
.btn-coral { background: var(--color-coral-soft); color: var(--color-error); border-color: var(--color-coral-light); }
.btn-danger { background: transparent; color: var(--color-error); border-color: var(--color-coral-light); }
.danger-zone { border-color: #eec6c1; background: color-mix(in srgb, var(--color-error-background) 40%, var(--color-surface)); }
.danger-zone h3 { color: var(--color-error); }
.danger-zone .check { display: flex; align-items: center; gap: 8px; font-size: 14px; color: var(--color-text-soft); }
.btn-link { background: none; border: 0; padding: 0; color: var(--color-primary); font: inherit; font-size: 12px; cursor: pointer; text-decoration: underline; }
.perma-sub { margin-top: 5px; font-size: 12px; color: var(--color-text-soft); }
.perma-sub .sep { margin: 0 5px; opacity: .5; }
.copy-perma.ok { border-color: var(--color-sage, #9db8a0); }
.btn[disabled] { opacity: .5; cursor: not-allowed; }

/* Formulaires */
/* Deux largeurs de formulaire, et deux intentions distinctes.

   .form est une COLONNE DE SAISIE : une suite de champs qu'on lit de haut en bas.
   560 px la maintiennent dans la longueur de ligne où l'œil retrouve le début de
   la ligne suivante sans effort. L'élargir ne servirait personne.

   .form.wide est une PAGE DE TRAVAIL : des cartes côte à côte, chacune avec son
   sujet. Elle était plafonnée à 860 px à l'intérieur d'un .bo-main de 1320 — soit
   460 px de largeur utile abandonnés, et surtout des colonnes si étroites que deux
   champs datetime-local n'y tenaient plus (voir .grid-2 juste en dessous). Ce qui
   la borne désormais, c'est .bo-main, c'est-à-dire la page elle-même. */
.form { max-inline-size: 560px; }
.form.wide { max-inline-size: none; }
.form-row { margin-bottom: 16px; }
.form-row label { display: block; font-size: 13px; margin-bottom: 6px; color: var(--color-text); font-weight: var(--font-weight-medium); }
.form-row .hint { color: var(--color-text-secondary); font-size: 12px; font-weight: var(--font-weight-regular); }
/* min-inline-size: 0 — un champ doit accepter d'être plus étroit que son contenu
   naturel. Sans cela, la piste peut bien être compressible (minmax(0,1fr)), le
   champ, lui, refuse encore de rétrécir et ressort de sa boîte. Les deux vont
   ensemble ; l'une sans l'autre ne suffit pas. */
.input, select.input, textarea.input { inline-size: 100%; min-inline-size: 0; padding: 11px 12px; border-radius: var(--radius-sm); background: var(--color-surface); border: 1px solid var(--color-border); color: var(--color-text); font-family: inherit; font-size: 14px; }
/* minmax(0, 1fr) ET NON 1fr. La différence n'est pas cosmétique : `1fr` vaut
   `minmax(auto, 1fr)`, et la taille minimale `auto` d'une piste de grille est la
   largeur MIN-CONTENT de ce qu'elle porte. Une piste ne peut donc jamais devenir
   plus étroite que son contenu — elle pousse à la place.

   Mesuré sur session_edit.php avant correction : deux champs datetime-local, dont
   la largeur incompressible est d'environ 197 px chacun (date + heure + bouton de
   calendrier), réclamaient 408 px dans une boîte qui n'en offrait que 383. Les
   25 px manquants sortaient de la carte, et le bord droit du champ « Fin » était
   coupé. Le zéro rend la piste vraiment compressible ; le champ se resserre alors
   au lieu de déborder. */
.grid-2 { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 14px; }
@media (max-width: 620px) { .grid-2 { grid-template-columns: 1fr; } }
.actions { display: flex; gap: 10px; margin-top: 8px; flex-wrap: wrap; }

/* Tables */
.table { width: 100%; border-collapse: collapse; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg); overflow: hidden; }
.table th, .table td { text-align: start; padding: 12px 14px; border-bottom: 1px solid var(--color-border-light); font-size: 14px; }
.table th { color: var(--color-text-secondary); font-size: 12px; text-transform: uppercase; letter-spacing: .6px; font-weight: var(--font-weight-semibold); background: var(--color-background-soft); }
.table tr:last-child td { border-bottom: none; }
/* Ligne non relevée (demandes des patients). Un liseré porteur plutôt qu'un fond
   coloré : la colonne « Motif » contient du texte libre, et un fond teinté sous un
   paragraphe nuit à sa lisibilité — or c'est justement ce qu'il faut lire. */
.table tr.is-unread td { background: color-mix(in srgb, var(--color-coral) 7%, var(--color-surface)); }
/* Le liseré est tracé par un pseudo-élément et non par un `box-shadow: inset`.
 * Un box-shadow ne connaît que des décalages physiques : le liseré restait à
 * GAUCHE en arabe, c'est-à-dire au milieu du tableau une fois les colonnes
 * miroitées, où il se lisait comme un filet de séparation égaré. Le
 * pseudo-élément, lui, se pose sur `inset-inline-start` et suit le sens de
 * lecture. On a écarté `border-inline-start`, qui aurait ajouté 3px à la
 * première cellule des seules lignes non relevées et désaligné la colonne. */
.table tr.is-unread td:first-child { position: relative; }
.table tr.is-unread td:first-child::before { content: ""; position: absolute; inset-block: 0; inset-inline-start: 0; width: 3px; background: var(--color-coral); }
.table .muted { color: var(--color-text-secondary); }
.table-wrap { overflow-x: auto; }

/* Cellule d'ACTIONS en fin de ligne.
 *
 * Les boutons peuvent passer à la ligne. Forcés sur une seule par un
 * white-space:nowrap, ils débordaient de leur cellule — et `.table` porte un
 * overflow:hidden, posé pour arrondir ses angles, qui les DÉCOUPAIT en silence.
 * Le bouton n'était pas hors écran : il était invisible, et aucune barre de
 * défilement n'apparaissait pour aller le chercher, puisque le tableau, lui,
 * tenait dans sa largeur (ticket #213).
 *
 * `flex-end` est déjà relatif au sens d'écriture : dans une ligne en arabe il
 * désigne le bord gauche, qui est bien la fin de la ligne. La surcharge
 * [dir="rtl"] qui forçait `flex-start` ne miroitait donc rien — elle renvoyait
 * les boutons du côté du DÉBUT de ligne, seule cellule du tableau à ne pas se
 * retourner avec les autres. Supprimée. */
.cell-actions { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; align-items: center; }

/* Texte LIBRE dans un tableau — un motif écrit par un patient, par exemple.
 * Borné, sinon il prend toute la largeur disponible et étrangle les colonnes
 * voisines. `anywhere` : une adresse collée sans espace ne doit pas l'élargir
 * au-delà de cette borne. */
.cell-text { display: block; max-width: 46ch; overflow-wrap: anywhere; }

/* Badges / statuts (couleurs douces) */
.tag { display: inline-block; padding: 3px 9px; border-radius: var(--radius-pill); font-size: 12px; font-weight: var(--font-weight-semibold); }
.tag-pending   { background: var(--color-warning-background); color: var(--color-warning); }
.tag-active    { background: var(--color-success-background); color: var(--color-success); }
.tag-suspended { background: var(--color-error-background);   color: var(--color-error); }
.tag-scheduled { background: var(--color-info-background);    color: var(--color-info); }
.tag-live      { background: var(--color-success-background); color: var(--color-success); }
.tag-done      { background: var(--color-border-light);       color: var(--color-text-secondary); }
.tag-canceled  { background: var(--color-error-background);   color: var(--color-error); }

/* Flash */
.flash { padding: 12px 16px; border-radius: var(--radius-md); margin-bottom: 16px; font-size: 14px; border: 1px solid; }
.flash-ok    { background: var(--color-success-background); border-color: #bfe0d0; color: #3f5f52; }
.flash-error { background: var(--color-error-background);   border-color: #eec6c1; color: #8f4a44; }
.flash-info  { background: var(--color-info-background);    border-color: #d6cee6; color: #5b4f78; }

/* Bandeaux d'état (.notice)
 *
 * Voisins de .flash, et volontairement distincts : un .flash rend compte d'une
 * action qui vient d'avoir lieu (« enregistré », « échec »), un .notice énonce
 * un état DURABLE de la page — un essai en cours, un profil incomplet, un pays
 * non desservi. Un état permanent ne doit pas emprunter le vocabulaire visuel
 * d'un accusé de réception, sans quoi on apprend à le chasser du regard.
 *
 * DÉFAUT CORRIGÉ ICI : .notice et .notice-info étaient posées dans une
 * vingtaine de gabarits sans exister nulle part dans la feuille. Le bandeau
 * d'essai de register.php et deux des quatre bandeaux du tableau de bord
 * praticien s'affichaient donc en texte nu, sans fond, sans bordure et sans la
 * marge qui les sépare du formulaire qui les suit.
 *
 * La boîte est portée par les TROIS sélecteurs et non par .notice seule : le
 * balisage existant emploie .notice-warn SANS .notice (app/session_edit.php),
 * et cet écran perdrait sa boîte si le socle n'était accroché qu'à .notice.
 *
 * Contrastes : le fond est dérivé de la SURFACE, avec une pointe de teinte
 * mélangée dedans, et le texte reste --color-text. Le rapport est donc celui du
 * corps de page — très au-delà des 4,5:1 exigés — en clair comme en sombre.
 * Poser --color-warning sur --color-warning-background, comme le ferait un
 * jeton de statut repris tel quel, retomberait sous le seuil. */
.notice, .notice-info, .notice-warn {
  margin: 0 0 16px; padding: 12px 16px; border-radius: var(--radius-md);
  border: 1px solid var(--color-border); background: var(--color-background-soft);
  color: var(--color-text); font-size: 14px;
}
.notice-info { background: color-mix(in srgb, var(--color-info) 12%, var(--color-surface)); border-color: color-mix(in srgb, var(--color-info) 45%, var(--color-border)); }
.notice-warn { background: color-mix(in srgb, var(--color-coral) 18%, var(--color-surface)); border-color: var(--color-coral); }

/* Encart du code promotionnel (app/pricing.php) — fantôme lui aussi : le bloc
 * s'affichait sans contour, si bien que le champ « code » flottait au milieu de
 * la grille tarifaire sans qu'on voie à quoi il se rattachait. Contour tireté,
 * comme .lock-cta et .dev-hint : la maison réserve ce trait à ce qui est
 * facultatif. */
.coupon-box { margin: 0 0 18px; padding: 14px 16px; border: 1px dashed var(--color-border); border-radius: var(--radius-md); background: var(--color-background-soft); }

/* Légende des abréviations d'un tableau (admin/users.php, collaborateurs.php,
 * journal.php). Fantôme sans conséquence visible : les trois gabarits portent
 * la mise en forme complète en attribut `style`. La classe est définie ici à
 * l'identique pour que le lot des gabarits puisse retirer ces styles en ligne
 * sans rien déplacer. */
.legend { display: flex; flex-wrap: wrap; gap: 22px; margin: 0 0 16px; font-size: 13px; color: var(--color-text-secondary); }

/* Auth pages */
.auth { min-height: 100%; display: flex; align-items: center; justify-content: center; padding: 24px; }
.auth .card { max-width: 420px; width: 100%; }
.auth .brand-logo { height: 34px; display: block; margin: 0 auto 16px; }
.auth h1 { font-size: 22px; margin: 0 0 4px; font-weight: var(--font-weight-semibold); }
.auth .sub { color: var(--color-text-secondary); font-size: 13px; margin-bottom: 20px; }
.auth .alt { margin-top: 18px; font-size: 13px; color: var(--color-text-secondary); text-align: center; }
.dev-hint { margin-top: 16px; padding: 10px 12px; border-radius: var(--radius-sm); background: var(--color-background-soft); border: 1px dashed var(--color-border); color: var(--color-text-secondary); font-size: 12px; font-family: ui-monospace, Menlo, monospace; text-align: center; line-height: 1.6; }

/* Agenda (mois) */
.cal { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.cal .dow { color: var(--color-text-secondary); font-size: 11px; text-transform: uppercase; letter-spacing: .6px; text-align: center; padding: 4px 0; }
.cal .cell { min-height: 92px; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: 6px; }
.cal .cell.out { opacity: .45; }
.cal .cell.today { border-color: var(--color-primary); box-shadow: 0 0 0 1px var(--color-primary) inset; }
.cal .cell .num { font-size: 12px; color: var(--color-text-secondary); }
/* LES PASTILLES D'ÉVÉNEMENT ÉCRIVAIENT EN TEINTE PLEINE SUR FOND DOUX, et c'est
   le couple qui ne marche pas : --color-info sur --color-info-background donnait
   3,61:1, --color-success sur le sien 2,71:1. À 11 px, sur des noms de patients
   et des heures de rendez-vous, c'était sous le plancher de 4,5:1 — et le pire
   des trois portait justement la séance EN COURS.

   Les triplets -bg-subtle / -text-emphasis existaient déjà, mesurés entre 5,94 et
   7,96:1, et s'échangent proprement en thème sombre. Ils n'étaient simplement pas
   appelés ici. Aucune valeur nouvelle n'a été inventée. */
.cal .ev { display: block; margin-top: 4px; font-size: 11px; padding: 3px 6px; border-radius: var(--radius-xs); background: var(--color-info-bg-subtle); color: var(--color-info-text-emphasis); text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: var(--font-weight-medium); }
.cal .ev.live { background: var(--color-success-bg-subtle); color: var(--color-success-text-emphasis); }
.cal .ev.done { background: var(--color-border-light); color: var(--color-text); }
.cal-head { display: flex; align-items: center; gap: 14px; margin-bottom: 14px; }
.cal-head h2 { margin: 0; font-size: 17px; font-weight: var(--font-weight-semibold); }
.cal-head h2::first-letter { text-transform: uppercase; }   /* même raison que .cal-title */

.empty { color: var(--color-text-secondary); text-align: center; padding: 40px; border: 1px dashed var(--color-border); border-radius: var(--radius-lg); }
.pseudo { font-family: ui-monospace, Menlo, monospace; color: var(--color-text-secondary); }

/* RTL — bloc vidé, et c'est le but.
 * Les trois surcharges qui vivaient ici ont disparu avec leur cause :
 *  · .table th/td compensait un `text-align: left` devenu `start` ;
 *  · .form-row label / .page-head / .card ne compensaient RIEN — aucune de ces
 *    règles ne fixait d'alignement, elles héritaient déjà de `start`, donc de
 *    la droite en arabe. La surcharge redisait le défaut du navigateur ;
 *  · .bo-nav de même : sans `justify-content`, un conteneur flex part déjà du
 *    début de ligne, à droite en arabe.
 * Ne reste ici que la règle de la maison : écrire logique en amont plutôt que
 * miroiter en aval. */

/* Offres / gating */
.plan-badge { display: inline-flex; align-items: center; gap: 6px; text-decoration: none; padding: 4px 10px; border-radius: var(--radius-pill); font-size: 12px; font-weight: var(--font-weight-semibold); background: var(--color-lavender-soft); color: var(--color-primary); border: 1px solid var(--color-border); }
.plan-badge.studio { color: var(--color-success); background: var(--color-success-background); }
/* (Les règles de verrou vivent désormais en tête de fichier, avec la barre.) */
.lock-cta { display: inline-flex; align-items: center; gap: 6px; margin-top: 8px; font-size: 12px; color: var(--color-text-secondary); text-decoration: none; padding: 6px 10px; border: 1px dashed var(--color-border); border-radius: var(--radius-sm); }
.lock-cta:hover { color: var(--color-text); border-color: var(--color-lavender); }
.is-locked { opacity: .5; pointer-events: none; filter: grayscale(.3); }

/* Page tarifs */
.plans { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; align-items: stretch; }
@media (max-width: 820px) { .plans { grid-template-columns: 1fr; } }
.region-picker { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin: 0 0 18px; font-size: 14px; }
.region-picker label { color: var(--color-text-soft); font-weight: 600; }
.region-picker select { padding-block: 7px; padding-inline: 12px 30px; border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-surface); color: var(--color-text); font: inherit; }
.region-picker .region-hint { color: var(--color-text-soft); font-size: 12.5px; }
.plan-card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg); padding: 22px; display: flex; flex-direction: column; box-shadow: var(--shadow-sm); }
.plan-card.current { border-color: var(--color-primary); box-shadow: 0 0 0 1px var(--color-primary) inset, var(--shadow-md); }
.plan-card h3 { margin: 0 0 4px; font-size: 18px; font-weight: var(--font-weight-semibold); }
.plan-price { font-size: 30px; font-weight: var(--font-weight-bold); margin: 10px 0 2px; color: var(--color-text); }
.plan-price .cur { font-size: 14px; color: var(--color-text-secondary); font-weight: var(--font-weight-medium); }
.plan-year { color: var(--color-text-secondary); font-size: 13px; margin-bottom: 14px; }
.vat-note { margin-top: 18px; line-height: 1.5; max-width: 70ch; }
.feat-list { list-style: none; padding: 0; margin: 6px 0 18px; font-size: 14px; }
.feat-list li { padding: 6px 0; border-bottom: 1px solid var(--color-border-light); }
.feat-list li::before { content: "✓ "; color: var(--color-success); }
.plan-card .btn { margin-top: auto; }
.tag-current { align-self: flex-start; margin-bottom: 8px; }

/* Widget de signalement (praticien) */
/* `bottom` reste physique à dessein : `dir` ne retourne que l'axe EN LIGNE, et
 * « en bas » veut dire la même chose dans les huit langues servies. Seul le
 * côté droite/gauche devient logique — c'est lui, et lui seul, qui miroite. */
.report-fab { position: fixed; bottom: 20px; inset-inline-end: 20px; z-index: 40; width: 48px; height: 48px; border-radius: 50%; border: 1px solid var(--color-border); background: var(--color-surface); color: var(--color-primary); font-size: 20px; cursor: pointer; box-shadow: var(--shadow-md); }
.report-modal { position: fixed; inset: 0; z-index: 41; background: rgba(57,54,60,.25); }
.report-panel { position: fixed; bottom: 20px; inset-inline-end: 20px; width: 340px; max-width: calc(100vw - 40px); background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg); padding: 16px; box-shadow: var(--shadow-lg); }
.report-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
.report-x { background: none; border: none; color: var(--color-text-secondary); font-size: 22px; cursor: pointer; line-height: 1; }
.report-lbl { display: block; font-size: 12px; color: var(--color-text-secondary); margin: 10px 0 4px; }

/* Avertissement de synchro (séance live) */
.sync-warn { margin-top: 12px; font-size: 13px; padding: 9px 12px; border-radius: var(--radius-sm); font-weight: var(--font-weight-medium); }
.sync-warn.check { background: var(--color-warning-background); color: var(--color-warning); }
.sync-warn.problem { background: var(--color-error-background); color: var(--color-error); }

/* Mode cabinet (plein écran hors visio) */
.cabinet { position: fixed; inset: 0; z-index: 100; background: #fff; }
.cabinet-stage { position: absolute; inset: 0; }
/* `left: 50%` + `translateX(-50%)` est l'idiome de CENTRAGE, pas une indication
 * de côté : le convertir en `inset-inline-start` décentrerait la barre en arabe,
 * la translation, elle, restant physique. On le laisse donc tel quel. */
.cabinet-ctl { position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%); z-index: 102; display: flex; gap: 10px; background: rgba(255,255,255,.92); border: 1px solid var(--color-border); border-radius: var(--radius-pill); padding: 8px 12px; box-shadow: var(--shadow-md); transition: opacity .3s ease; }
.cabinet-ctl .btn { padding: 10px 18px; }

/* ---- Détail d'une campagne de tests (admin/test-run.php) ----
 * Classes préfixées .trun-* : elles n'entrent en collision avec rien et restent
 * réutilisables par le tableau de bord des tests (barre + légende notamment).
 * Les filtres sont 100 % CSS (radios masqués + sélecteurs frères) : cette page
 * ne charge aucun script. */
.trun-h2 { font-size: 15px; margin: 26px 0 10px; font-weight: var(--font-weight-semibold); }
.trun-h3 { font-size: 14px; margin: 18px 0 8px; font-weight: var(--font-weight-semibold); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.trun-meta { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px 22px; }
.trun-meta > div > div { font-size: 14px; margin-top: 3px; }

/* Barre de verdict : une seule ligne proportionnelle, dans l'ordre réussis /
 * échoués / erreurs / ignorés. Les segments nuls font 0 % et disparaissent. */
.trun-bar { display: flex; height: 14px; border-radius: var(--radius-pill); overflow: hidden; background: var(--color-border-light); }
.trun-bar > span { display: block; height: 100%; }
.trun-bar .seg-vide { background: var(--color-border-light); }
.seg-passed  { background: var(--color-success); }
.seg-failed  { background: var(--color-error); }
.seg-error   { background: var(--color-warning); }
.seg-skipped { background: var(--color-border); }
.trun-legend { display: flex; flex-wrap: wrap; gap: 6px 18px; margin-top: 12px; font-size: 13px; color: var(--color-text); }
/* La pastille précède son libellé : sa marge doit donc suivre le sens de
 * lecture, faute de quoi elle se colle au mot en arabe et laisse le blanc du
 * mauvais côté. */
.trun-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-inline-end: 7px; }
.trun-legend .muted { color: var(--color-text-secondary); }

/* Filtres sans rechargement : les radios pilotent l'affichage des lignes. */
.trun-zone { position: relative; }
.trun-radio { position: absolute; opacity: 0; width: 0; height: 0; }
.trun-filters { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0 0 6px; }
.trun-filters label { cursor: pointer; font-size: 13px; padding: 5px 12px; border-radius: var(--radius-pill); border: 1px solid var(--color-border); background: var(--color-surface); color: var(--color-text-secondary); }
.trun-filters label:hover { border-color: var(--color-lavender); color: var(--color-text); }
#trun-f-tout:checked ~ .trun-filters label[for="trun-f-tout"],
#trun-f-def:checked  ~ .trun-filters label[for="trun-f-def"],
#trun-f-cri:checked  ~ .trun-filters label[for="trun-f-cri"],
#trun-f-maj:checked  ~ .trun-filters label[for="trun-f-maj"],
#trun-f-min:checked  ~ .trun-filters label[for="trun-f-min"] { background: var(--color-lavender-soft); color: var(--color-primary); border-color: var(--color-lavender); font-weight: var(--font-weight-semibold); }
#trun-f-tout:focus-visible ~ .trun-filters label[for="trun-f-tout"],
#trun-f-def:focus-visible  ~ .trun-filters label[for="trun-f-def"],
#trun-f-cri:focus-visible  ~ .trun-filters label[for="trun-f-cri"],
#trun-f-maj:focus-visible  ~ .trun-filters label[for="trun-f-maj"],
#trun-f-min:focus-visible  ~ .trun-filters label[for="trun-f-min"] { outline: 2px solid var(--color-primary); outline-offset: 2px; }

/* Toujours viser tbody tr : sans cela, tr:not(.gr-…) emporterait aussi la ligne
 * d'en-tête du tableau, qui ne porte évidemment aucune classe de gravité. */
#trun-f-def:checked ~ .trun-res tbody tr.st-passed,
#trun-f-def:checked ~ .trun-res tbody tr.st-skipped,
#trun-f-cri:checked ~ .trun-res tbody tr:not(.gr-critique),
#trun-f-maj:checked ~ .trun-res tbody tr:not(.gr-majeur),
#trun-f-min:checked ~ .trun-res tbody tr:not(.gr-mineur) { display: none; }
/* Une suite dont plus aucune ligne n'est visible s'efface avec son titre.
 * Règles séparées : un navigateur sans :has() les ignore sans rien casser
 * (le bloc reste affiché, tableau vide — dégradation acceptable). */
#trun-f-def:checked ~ .trun-res .trun-suite:not(:has(tr.st-failed, tr.st-error)) { display: none; }
#trun-f-cri:checked ~ .trun-res .trun-suite:not(:has(tr.gr-critique)) { display: none; }
#trun-f-maj:checked ~ .trun-res .trun-suite:not(:has(tr.gr-majeur))   { display: none; }
#trun-f-min:checked ~ .trun-res .trun-suite:not(:has(tr.gr-mineur))   { display: none; }

.trun-cas { white-space: nowrap; }          /* un identifiant de cas ne se coupe pas au trait d'union */
.trun-msg { max-width: 520px; font-size: 13px; }
.trun-detail > summary { cursor: pointer; color: var(--color-text-secondary); font-size: 12px; margin-top: 4px; }
.trun-detail pre { white-space: pre-wrap; word-break: break-word; font-size: 12px; margin: 6px 0 0; padding: 8px 10px; background: var(--color-background-soft); border: 1px solid var(--color-border-light); border-radius: var(--radius-sm); max-height: 320px; overflow: auto; }
.trun-nav { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px; margin: 28px 0 0; padding-top: 18px; border-top: 1px solid var(--color-border-light); font-size: 13px; }

/* ---- Tableau de bord des tests (admin/tests.php) ----
 * Préfixe .tdash-* pour ne heurter ni .trun-* (page de détail) ni le reste du
 * backoffice. La barre de verdict et sa légende sont volontairement REPRISES de
 * .trun-bar / .seg-* / .trun-legend : la même information doit avoir la même
 * forme d'un écran à l'autre. */

/* Bandeau de verdict : la réponse à « est-ce que mon application va bien ? ». */
.tdash-verdict { border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-surface); box-shadow: var(--shadow-sm); padding: 20px 22px; margin: 0 0 18px; }
.tdash-verdict.ok     { border-color: #bfe0d0; background: var(--color-success-background); }
.tdash-verdict.ko     { border-color: #eec6c1; background: var(--color-error-background); }
.tdash-verdict.neutre { background: var(--color-background-soft); }
.tdash-verdict .v-titre { margin: 0 0 2px; font-size: 21px; font-weight: var(--font-weight-semibold); }
.tdash-verdict .sub { color: var(--color-text-secondary); font-size: 13px; margin-bottom: 14px; }
.tdash-verdict .trun-bar { height: 12px; }

/* Listes de constats (échecs critiques, régressions) à l'intérieur d'un .flash. */
.tdash-liste { list-style: none; margin: 10px 0 0; padding: 0; }
.tdash-liste li { padding: 7px 0; border-top: 1px solid currentColor; border-top-color: color-mix(in srgb, currentColor 22%, transparent); }
.tdash-msg, .tdash-liste .tdash-msg { font-size: 13px; opacity: .85; margin-top: 3px; white-space: pre-wrap; }

/* Journal poussé au fil de l'eau pendant la campagne. */
.tdash-live { margin: 0 0 22px; }
.tdash-flux { margin: 0; padding: 14px 16px; max-height: 340px; overflow: auto; background: var(--color-background-soft); border: 1px solid var(--color-border); border-radius: var(--radius-lg); font-family: ui-monospace, Menlo, monospace; font-size: 12.5px; line-height: 1.7; white-space: pre-wrap; word-break: break-word; color: var(--color-text); }
.tdash-flux .f-ok { color: var(--color-success); }
.tdash-flux .f-ko { color: var(--color-error); font-weight: var(--font-weight-semibold); }
.tdash-flux .f-er { color: var(--color-warning); font-weight: var(--font-weight-semibold); }
.tdash-flux .f-na { color: var(--color-text-secondary); }

/* Formulaire de lancement. */
.tdash-lancer { border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-surface); box-shadow: var(--shadow-sm); padding: 18px 20px; margin: 0 0 8px; }
.tdash-suites { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 10px; }
.tdash-suite { display: flex; gap: 11px; align-items: flex-start; padding: 12px 14px; border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-background-soft); cursor: pointer; }
.tdash-suite:hover { border-color: var(--color-lavender); }
.tdash-suite.hs { opacity: .55; cursor: not-allowed; }
.tdash-suite input { margin-top: 3px; flex-shrink: 0; }
.tdash-suite .s-corps { display: block; min-width: 0; }
.tdash-suite .s-nom  { display: block; font-weight: var(--font-weight-semibold); font-size: 14px; }
.tdash-suite .s-desc { display: block; font-size: 13px; color: var(--color-text-secondary); margin: 2px 0 6px; }
.tdash-suite .s-etat { display: block; font-size: 12px; color: var(--color-text-secondary); }
.tdash-actions { display: flex; gap: 8px; flex-wrap: wrap; margin: 14px 0 16px; }
.tdash-options { display: grid; gap: 8px; margin: 0 0 18px; font-size: 14px; }
.tdash-options label { display: block; cursor: pointer; }
.tdash-options .muted { font-size: 13px; }

/* Résultats, groupés par suite. */
.tdash-suite-bloc { border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-surface); overflow: hidden; margin: 0 0 16px; box-shadow: var(--shadow-xs); }
.tdash-suite-bloc > header { padding: 14px 16px; background: var(--color-background-soft); border-bottom: 1px solid var(--color-border-light); }
.tdash-suite-bloc .h-titre { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; font-size: 15px; }
.tdash-suite-bloc .h-desc { margin: 2px 0 10px; }
.tdash-cas { padding: 12px 16px; border-bottom: 1px solid var(--color-border-light); font-size: 14px; }
.tdash-cas:last-child { border-bottom: none; }
.tdash-cas.ko { border-inline-start: 3px solid var(--color-error); background: color-mix(in srgb, var(--color-error-background) 45%, transparent); }
.tdash-cas.na { border-inline-start: 3px solid var(--color-border); }
.tdash-cas.ok { border-inline-start: 3px solid var(--color-success); }
.tdash-cas .c-tete { display: flex; gap: 9px; align-items: center; flex-wrap: wrap; }
.tdash-cas .c-id { font-family: ui-monospace, Menlo, monospace; font-size: 12px; color: var(--color-text-secondary); }
.tdash-cas .c-titre { font-weight: var(--font-weight-medium); }
.tdash-cas .c-msg { margin-top: 6px; white-space: pre-wrap; }
.tdash-cas .c-detail { margin: 8px 0 0; padding: 10px 12px; background: var(--color-background-soft); border-radius: var(--radius-sm); font-size: 12px; white-space: pre-wrap; overflow-x: auto; }
.tdash-cas details > summary { cursor: pointer; font-size: 12px; color: var(--color-text-secondary); margin-top: 6px; }
.tdash-repli > summary { cursor: pointer; padding: 11px 16px; font-size: 13px; color: var(--color-text-secondary); border-bottom: 1px solid var(--color-border-light); }
.tdash-repli > summary:hover { color: var(--color-text); }
.tdash-repli[open] > summary { font-weight: var(--font-weight-semibold); }

/* Historique et hygiène. */
.tdash-suites-col { max-width: 240px; word-break: break-word; font-size: 12px; }
.tdash-hygiene { margin-bottom: 10px; }
.tdash-hygiene p { font-size: 13.5px; line-height: 1.7; }

/* ---- Maintenance (admin/maintenance.php) ----
 * Préfixe .mnt-* pour ne heurter ni .trun-* ni .tdash-*. Tout ce qui pouvait
 * être repris l'a été (.cards, .card, .table, .tag*, .flash*, .btn*, .empty,
 * .dev-hint) : seuls les onglets, les chips de filtre en LIENS (les .trun-filters
 * sont pilotées par des radios, inutilisables ici où l'état doit tenir dans
 * l'URL) et les blocs dépliés d'un ticket demandaient des règles propres. */
.mnt-cards a.card { display: block; color: inherit; text-decoration: none; transition: border-color .15s ease; }
.mnt-cards a.card:hover { border-color: var(--color-lavender); }

/* Onglets : deux familles, deux tableaux. L'onglet actif porte le trait plein. */
.mnt-tabs { display: flex; gap: 6px; margin: 22px 0 14px; border-bottom: 1px solid var(--color-border); }
.mnt-tabs a { display: inline-flex; align-items: center; gap: 8px; padding: 9px 16px; font-size: 14px; text-decoration: none; color: var(--color-text-secondary); border: 1px solid transparent; border-bottom: none; border-radius: var(--radius-sm) var(--radius-sm) 0 0; margin-bottom: -1px; }
.mnt-tabs a:hover { color: var(--color-text); }
.mnt-tabs a.active { color: var(--color-text); font-weight: var(--font-weight-semibold); background: var(--color-surface); border-color: var(--color-border); border-bottom: 1px solid var(--color-surface); }
.mnt-pill { min-width: 20px; padding: 1px 7px; border-radius: var(--radius-pill); background: var(--color-background-soft); border: 1px solid var(--color-border-light); font-size: 12px; font-variant-numeric: tabular-nums; }
.mnt-tabs a.active .mnt-pill { background: var(--color-lavender-soft); border-color: var(--color-lavender); color: var(--color-primary); }

/* Filtres : mêmes chips que .trun-filters, mais en liens (l'état survit au POST). */
.mnt-filtres { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0 0 14px; }
.mnt-filtres > a { font-size: 13px; padding: 5px 12px; border-radius: var(--radius-pill); border: 1px solid var(--color-border); background: var(--color-surface); color: var(--color-text-secondary); text-decoration: none; }
.mnt-filtres > a:hover { border-color: var(--color-lavender); color: var(--color-text); }
.mnt-filtres > a.active { background: var(--color-lavender-soft); color: var(--color-primary); border-color: var(--color-lavender); font-weight: var(--font-weight-semibold); }
.mnt-sep { width: 1px; height: 22px; background: var(--color-border); margin: 0 4px; }
.mnt-recherche { display: flex; gap: 8px; align-items: center; margin-inline-start: auto; }
.mnt-recherche .input { width: 260px; padding: 5px 10px; font-size: 13px; }
.mnt-recherche .btn { white-space: nowrap; }

.mnt-table td { vertical-align: top; }
.mnt-cas { font-family: ui-monospace, Menlo, monospace; font-size: 12px; color: var(--color-text-secondary); white-space: nowrap; }
.mnt-detail > summary { cursor: pointer; color: var(--color-text-secondary); font-size: 12px; margin-top: 4px; }
.mnt-detail > summary:hover { color: var(--color-text); }
.mnt-texte { margin: 6px 0 0; font-size: 13px; line-height: 1.6; white-space: pre-wrap; word-break: break-word; }
.mnt-detail pre { white-space: pre-wrap; word-break: break-word; font-size: 12px; margin: 6px 0 0; padding: 8px 10px; background: var(--color-background-soft); border: 1px solid var(--color-border-light); border-radius: var(--radius-sm); max-height: 320px; overflow: auto; }

/* Purge : une décision explicite, séparée du tableau par un filet. */
.mnt-purge { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin: 26px 0 0; padding-top: 16px; border-top: 1px solid var(--color-border-light); font-size: 12.5px; }

/* ---- Sélection des tests à jouer (admin/tests.php, section « Lancer ») ----
 * Préfixe .tsel-* : la liste dépliable remplace la grille de cases par suite.
 * Une suite = un <details> natif ; ses cas = un .table ordinaire, pour que la
 * lecture d'un cas soit la même ici que partout ailleurs dans le backoffice. */
.tsel-liste { display: grid; gap: 10px; margin: 0 0 16px; }
.tsel-aide { margin: -8px 0 14px; }
/* Le raccourci du quotidien se distingue des quatre autres, sans crier. */
.tsel-vedette { border-color: var(--color-lavender); background: var(--color-lavender-soft); color: var(--color-primary); font-weight: var(--font-weight-semibold); }

.tsel-suite { border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-background-soft); overflow: hidden; }
.tsel-suite > summary { cursor: pointer; padding: 12px 14px; list-style: none; }
.tsel-suite > summary::-webkit-details-marker { display: none; }
/* Chevron dessiné en CSS : aucune image, aucune police d'icônes à charger. */
.tsel-suite > summary::before { content: '▸'; color: var(--color-text-secondary); margin-inline-end: 8px; display: inline-block; transition: transform var(--transition-fast); }
.tsel-suite[open] > summary::before { transform: rotate(90deg); }
.tsel-suite > summary:hover { background: var(--color-lavender-soft); }
.tsel-suite.hs { opacity: .6; }
.tsel-suite.hs .tsel-sum { padding: 12px 14px; }

/* Une seule ligne fluide : case, nom, compteur, verdict, description. */
.tsel-sum { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 6px 12px; width: calc(100% - 22px); vertical-align: top; }
.tsel-sum .s-nom { font-weight: var(--font-weight-semibold); font-size: 14px; }
.tsel-sum .s-desc { flex-basis: 100%; font-size: 13px; color: var(--color-text-secondary); }
.tsel-sum .s-etat { font-size: 12px; color: var(--color-text-secondary); }
.tsel-count { font-size: 12px; color: var(--color-text-secondary); font-variant-numeric: tabular-nums; }
.tsel-master { flex-shrink: 0; align-self: center; }

.tsel-cas { border: none; border-radius: 0; border-top: 1px solid var(--color-border-light); }
.tsel-cas th, .tsel-cas td { padding: 7px 12px; font-size: 13px; }
.tsel-cas td.c-titre { color: var(--color-text); }
.tsel-cas td.c-dernier { white-space: nowrap; font-size: 12px; }
.tsel-cas .c-pick { width: 34px; }
/* Le dernier verdict connu se lit au filet de gauche, avant même de lire le
 * badge : c'est ce qui fait qu'on repère une suite rouge en la survolant. */
.tsel-cas tbody tr.st-failed td:first-child,
.tsel-cas tbody tr.st-error  td:first-child { box-shadow: inset 3px 0 0 var(--color-error); }
.tsel-cas tbody tr.st-passed td:first-child { box-shadow: inset 3px 0 0 var(--color-success); }
.tsel-cas tbody tr.st-skipped td:first-child { box-shadow: inset 3px 0 0 var(--color-border); }
.tsel-cas tbody tr.st-failed, .tsel-cas tbody tr.st-error { background: color-mix(in srgb, var(--color-error-background) 40%, transparent); }

/* Compteur + estimation, juste au-dessus du bouton : la dernière chose lue
 * avant de lancer trois minutes de campagne. */
.tsel-bilan { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; padding: 11px 14px; margin: 0 0 16px; border: 1px solid var(--color-lavender); border-radius: var(--radius-md); background: var(--color-lavender-soft); font-size: 13.5px; color: var(--color-text); }

/* ---- Progression pendant la campagne (admin/tests.php) ----
 * La barre REPREND .trun-bar et .seg-* : la même information a la même forme
 * que le verdict, l'historique et la page de détail. Le reste de la largeur
 * demeure la teinte de fond de .trun-bar — c'est ce qui donne la progression
 * sans qu'aucun élément supplémentaire soit nécessaire. */
.tprog { padding: 14px 16px; margin: 0 0 10px; border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-surface); box-shadow: var(--shadow-xs); }
.tprog.ko { border-color: color-mix(in srgb, var(--color-error) 35%, var(--color-border)); background: var(--color-error-background); }
.tprog .trun-bar { height: 12px; }
.tprog-line { margin-top: 9px; font-size: 13px; color: var(--color-text); display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: baseline; }
.tprog-line .muted { color: var(--color-text-secondary); }
.tprog-compte { gap: 4px 18px; font-size: 12.5px; color: var(--color-text-secondary); font-variant-numeric: tabular-nums; }
.tprog-compte .c-ok b { color: var(--color-success); }
.tprog-compte .c-ko b { color: var(--color-error); }
.tprog-compte .c-er b { color: var(--color-warning); }
#tp-cur { max-width: 100%; overflow-wrap: anywhere; }

/* ---- Frise des défauts (historique) ----
 * Vingt barres, de la plus ancienne à la plus récente. Répond à « est-ce que
 * ça s'améliore ? », question à laquelle un tableau de vingt lignes ne répond
 * pas. Pur CSS et données déjà chargées : aucune requête, aucune dépendance. */
.tdash-frise { display: flex; align-items: flex-end; gap: 4px; height: 48px; padding: 0 2px; margin: 0 0 2px; }
.tdash-frise .fr-bar { flex: 1 1 0; min-width: 6px; background: var(--color-error); border-radius: var(--radius-xs) var(--radius-xs) 0 0; opacity: .8; transition: opacity var(--transition-fast); }
.tdash-frise .fr-bar:hover { opacity: 1; }
.tdash-frise .fr-bar.ok { background: var(--color-success); }
.tdash-frise .fr-bar.na { background: var(--color-border); }

/* Lien d'un cas en défaut vers le ticket de maintenance qu'il a ouvert. */
.tdash-ticket { font-size: 12px; padding: 2px 9px; border-radius: var(--radius-pill); background: var(--color-lavender-soft); border: 1px solid var(--color-lavender); color: var(--color-primary); text-decoration: none; white-space: nowrap; }
.tdash-ticket:hover { background: var(--color-lavender-light); }
/* Barres bornées en largeur : avec trois campagnes seulement, des barres
 * étirées sur toute la ligne se liraient comme un bandeau d'alerte. */
.tdash-frise .fr-bar { max-width: 46px; }

/* ── Complétude du profil ────────────────────────────────────────────────────
   La barre est une incitation, pas un reproche : couleur d'accent tant qu'il
   reste à faire, couleur de succès à 100 %, et aucune teinte d'alerte — rien
   ici n'est en défaut, il s'agit d'informations facultatives. */
.completion { margin-bottom: 22px; }
.completion-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.completion-pct { font-size: 13px; color: var(--color-text-secondary); font-variant-numeric: tabular-nums; }
.completion-left { margin-top: 10px; font-size: 13px; color: var(--color-text-secondary); }

.progress {
  display: block; height: 8px; margin-top: 10px; border-radius: 999px;
  background: var(--color-surface-sunken, rgba(77, 66, 84, .10)); overflow: hidden;
}
.progress-bar {
  display: block; height: 100%; border-radius: 999px;
  background: var(--color-accent, #AAA0BC);
  transition: width .35s ease;
}
.progress-bar.is-done { background: var(--color-success, #7C9A83); }

/* Rappel discret sur le tableau de bord : une bande cliquable d'un seul tenant. */
.nudge { display: flex; align-items: center; gap: 14px; text-decoration: none; color: inherit; margin-bottom: 20px; }
.nudge-txt { flex: 0 1 auto; }
.nudge-bar { flex: 1 1 120px; max-width: 260px; margin-top: 0; }
@media (max-width: 640px) {
  .nudge { flex-wrap: wrap; }
  .nudge-bar { order: 3; flex-basis: 100%; max-width: none; }
}

/* ── Maintenance : sélection de tickets et export du prompt ──────────────── */
.mnt-col-case { width: 34px; text-align: center; }
.mnt-barre {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 10px 14px; margin-bottom: 12px; border-radius: var(--radius-md, 10px);
  background: var(--color-surface-sunken, rgba(77, 66, 84, .06));
}
.mnt-barre-n { font-weight: var(--font-weight-medium, 500); font-variant-numeric: tabular-nums; }
.mnt-barre-aide { font-size: 12px; }
.mnt-apercu, .trun-apercu { margin-bottom: 14px; }
.mnt-apercu textarea, .trun-apercu textarea { width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
@media (max-width: 640px) { .mnt-barre-aide { flex-basis: 100%; } }

/* ── Numéro de version ───────────────────────────────────────────────────────
   Discret jusqu'à l'effacement : il ne s'adresse qu'à celui qui le cherche.
   Position statique en bas de page plutôt que fixe : rien ne doit recouvrir le
   contenu sur un petit écran pour une information que personne ne lit deux fois. */
.app-version {
  margin: 28px 0 12px;
  text-align: center;
  font-size: 11px;
  letter-spacing: .02em;
  color: var(--color-text-secondary);
  opacity: .55;
  font-variant-numeric: tabular-nums;
}
.app-version:hover { opacity: .9; }

/* DENSITÉ DU BACK-OFFICE — ce que le doublon `.btn` portait sans le dire.
 *
 * En retirant la définition concurrente de cette feuille, on a rendu au socle de
 * style.css une portée qu'il n'avait jamais eue : ses 14px/15px ne s'appliquaient
 * en pratique qu'à index.php, seule page à ne pas charger app.css. Tout le reste
 * du produit — administration, espace praticien, écrans d'authentification —
 * recevait le 10px/14px d'ici. Le doublon supprimé, tous les boutons de
 * l'application ont grossi d'un coup, sans que personne l'ait demandé.
 *
 * Ces deux lignes ne réintroduisent pas le doublon : elles ne portent QUE la
 * densité, là où elle s'appliquait déjà. La page d'accueil garde la sienne, plus
 * généreuse, qui convient à une vitrine et pas à un tableau de bord. */
.btn { padding: 10px 16px; font-size: 14px; }

/* ══════════════════════════════════════════════════════════════════════════════
   GRILLE DE PAGE À DEUX COLONNES — et pourquoi .grid-2 ne pouvait pas la porter.

   session_edit.php emboîtait un .grid-2 dans un .grid-2 : le premier partageait
   la PAGE en deux colonnes, le second partageait une PAIRE DE CHAMPS. Les deux
   ne se replient pas à la même largeur, et .grid-2 ne se repliait qu'à 620 px.
   Sur une tablette — 820 px environ —, la page restait donc en deux colonnes
   d'à peu près 380 px, dans lesquelles deux champs datetime-local se
   partageaient 180 px chacun. D'où la colonne de droite écrasée, et d'où le fait
   que le mobile allait bien : sous 620 px, tout retombait sur une colonne.

   La séparation est ici explicite : .grid-page découpe une PAGE et se replie tôt
   (1080 px), .grid-2 découpe des CHAMPS et se replie tard. Emboîtées, elles ne
   se marchent plus dessus.

   minmax(0, 1fr) et non 1fr : la taille minimale par défaut d'une piste de
   grille est `auto`, c'est-à-dire la largeur du contenu. Un tableau large ou un
   mot long y élargit sa colonne au lieu de défiler, et la seconde s'écrase — le
   même symptôme, par un autre chemin.
   ══════════════════════════════════════════════════════════════════════════════ */
.grid-page { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 18px; align-items: start; }
@media (max-width: 1080px) { .grid-page { grid-template-columns: minmax(0, 1fr); } }
/* ══════════════════════════════════════════════════════════════════════════════
   AGENDA — barre d'outils, sélecteur de vue, grilles mois / semaine / jour.

   À coller À LA SUITE du bloc .cal existant (assets/app.css, ~ligne 187) : ce
   bloc le COMPLÈTE, il ne le remplace pas. Là où une propriété est déjà posée
   par « .cal .ev », le sélecteur d'ici est d'une spécificité au moins égale —
   aucun !important n'a été nécessaire.

   PROPRIÉTÉS LOGIQUES PARTOUT. Pas un left, pas un right : en arabe, dir="rtl"
   retourne la grille, l'ordre des colonnes et la gouttière des heures sans
   qu'une seule règle supplémentaire soit écrite.
   ══════════════════════════════════════════════════════════════════════════════ */

/* ── Barre d'outils ─────────────────────────────────────────────────────────── */
.cal-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-block-end: 14px; }
.cal-nav     { display: flex; gap: 6px; }
/* PAS text-transform: capitalize. Il met une majuscule à CHAQUE mot, et rendait
   « Semaine Du 31/08 Au 06/09/2026 » — deux majuscules fautives en français, et
   la même faute en espagnol, en italien et en portugais, dont les titres de mois
   sont eux aussi en minuscules. ::first-letter n'en touche qu'une, celle qui
   manquait vraiment, et laisse les langues sans casse (arabe, chinois) intactes. */
.cal-title   { margin: 0; margin-inline-end: auto; font-size: 17px; font-weight: var(--font-weight-semibold); }
.cal-title::first-letter { text-transform: uppercase; }

/* Sélecteur de vue : trois liens soudés, l'actif enfoncé. */
.cal-views { display: inline-flex; border: 1px solid var(--color-border); border-radius: var(--radius-pill); overflow: hidden; }
.cal-view  { padding: 6px 14px; font-size: 13px; text-decoration: none; color: var(--color-text-secondary);
             background: var(--color-surface); border-inline-start: 1px solid var(--color-border); }
.cal-view:first-child { border-inline-start: 0; }
.cal-view:hover       { background: var(--color-background-soft); color: var(--color-text); }
/* Le texte de l'onglet actif prend la couleur du FOND de page, jamais un blanc
   littéral : en thème sombre, --color-primary devient clair et un blanc en dur
   deviendrait illisible. Les deux jetons s'inversent ensemble. */
.cal-view.is-on { background: var(--color-primary); color: var(--color-background); }

/* ── Vue MOIS ───────────────────────────────────────────────────────────────── */
.cal-month .cell { display: flex; flex-direction: column; gap: 3px; }
.cal-daynum { align-self: flex-start; text-decoration: none; padding: 1px 5px; border-radius: var(--radius-xs); }
.cal-daynum:hover { background: var(--color-primary-soft); color: var(--color-primary); }
.cal-month .cell.today .cal-daynum { color: var(--color-primary); font-weight: var(--font-weight-semibold); }

/* La zone libre d'une case ou d'un créneau : c'est elle qui porte « créer ici ».
   En vue mois elle est muette au clavier (le numéro du jour porte déjà le lien
   annoncé) ; dans les grilles horaires elle est le seul lien du créneau, donc
   focusable — l'anneau de focus global (:focus-visible) la révèle. */
.cal-free { display: block; flex: 1 1 auto; min-block-size: 16px; border-radius: var(--radius-xs); }
.cal-free:hover { background: var(--color-primary-soft); }

/* ── Vues SEMAINE et JOUR ───────────────────────────────────────────────────── */
/* UNE seule grille porte l'en-tête, la gouttière et les créneaux : un seul
   contexte de grille, donc un seul alignement. Deux grilles superposées finissent
   toujours par se décaler d'un pixel selon la police du poste. */
.cal-grid { display: grid; grid-template-columns: 56px repeat(var(--cal-jours), minmax(0, 1fr));
            gap: 1px; background: var(--color-border-light);
            border: 1px solid var(--color-border); border-radius: var(--radius-md); overflow: hidden; }

.cal-corner  { background: var(--color-surface); }
.cal-dayhead { background: var(--color-surface); padding: 8px 4px; text-align: center; text-decoration: none;
               color: var(--color-text-secondary); display: flex; flex-direction: column; gap: 2px; }
.cal-dayhead:hover { background: var(--color-background-soft); }
.cal-dayhead-dow   { font-size: 11px; text-transform: uppercase; letter-spacing: .6px; }
.cal-dayhead-num   { font-size: 17px; font-weight: var(--font-weight-semibold); color: var(--color-text); }
.cal-dayhead.is-today { background: var(--color-primary-soft); }
.cal-dayhead.is-today .cal-dayhead-num { color: var(--color-primary); }

/* La gouttière n'écrit l'heure qu'au sommet de l'heure ; la demi-heure garde sa
   cellule (l'alignement en dépend) mais pas son texte. */
.cal-hour       { background: var(--color-surface); padding: 2px 6px; font-size: 11px;
                  color: var(--color-text-muted); text-align: end; }
.cal-hour-half  { color: transparent; }
.cal-hour-wide  { font-size: 10px; line-height: 1.2; text-align: start; color: var(--color-text-secondary); }

.cal-slot { background: var(--color-surface); min-block-size: 26px; display: flex; flex-direction: column;
            gap: 2px; padding: 1px; }
.cal-slot.is-hour  { border-block-start: 1px solid var(--color-border-light); }
.cal-slot.is-today { background: var(--color-surface-warm); }
/* L'instant présent, posé par le SERVEUR au rendu de la page : un trait, pas une
   animation — il ne prétend pas suivre l'heure, il dit où en était l'agenda quand
   la page a été construite. */
.cal-slot.is-now   { box-shadow: inset 0 2px 0 0 var(--color-coral); }
/* Bande « hors plage » : ce qui tombe avant 07:00 ou après 21:00 est rassemblé
   là. Une grille qui cacherait un rendez-vous mentirait. */
.cal-slot-out { background: var(--color-background-soft); min-block-size: 22px; }

/* ── Pastilles d'événement, communes aux trois vues ─────────────────────────── */
.cal-ev { display: block; font-size: 11px; padding: 2px 6px; border-radius: var(--radius-xs);
          text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
          background: var(--color-info-background); color: var(--color-info); }
.cal-ev:hover { filter: brightness(.96); }
.cal .ev.cal-ev-live, .cal-ev-live { background: var(--color-success-bg-subtle); color: var(--color-success-text-emphasis); }
.cal .ev.cal-ev-done, .cal-ev-done { background: var(--color-border-light); color: var(--color-text); }
/* ANNULÉE — LA COULEUR NE SUFFIT PAS. Le texte est barré, le cadre passe en
   pointillé, et le libellé porte déjà un « ✕ » écrit côté serveur : un praticien
   daltonien, une impression en noir et blanc, le signal tient sans la teinte. */
.cal .ev.cal-ev-canceled, .cal-ev-canceled { background: transparent; border: 1px dashed var(--color-border);
                                             color: var(--color-text-muted); text-decoration: line-through; }

/* ── Écrans étroits ─────────────────────────────────────────────────────────── */
/* La grille horaire ne se replie pas en liste : sa valeur EST le quadrillage. On
   resserre la gouttière et le texte, ce qui suffit jusqu'aux petites tablettes ;
   en dessous, la vue JOUR (une colonne) est la bonne réponse, et le sélecteur de
   vue est juste au-dessus. */
@media (max-width: 720px) {
  .cal-grid      { grid-template-columns: 40px repeat(var(--cal-jours), minmax(0, 1fr)); }
  .cal-hour      { font-size: 10px; padding-inline: 3px; }
  .cal-dayhead-num { font-size: 15px; }
  .cal-ev        { font-size: 10px; padding-inline: 3px; }
  .cal-title     { inline-size: 100%; margin-inline-end: 0; }
}

/* ══════════════════════════════════════════════════════════════════════════════
   PATIENTS — la liste, les vignettes, la fiche.

   D'OÙ VIENT LE DESSIN. De contacts-list.html et contacts-grid.html du paquet
   Vuesy, dont on reprend l'AGENCEMENT (barre de tête « titre + compteur » d'un
   côté, bascule de présentation et bouton d'ajout de l'autre ; pastille
   d'initiales devant le nom ; vignette à trois étages) et RIEN D'AUTRE : pas une
   classe, pas une couleur, pas une ligne de Bootstrap. Les valeurs sortent
   toutes des jetons de assets/style.css, si bien que le thème sombre suit sans
   qu'une seule règle lui soit consacrée.

   CE QU'ON N'A PAS REPRIS, et pourquoi :
     · les avatars photographiques — un dossier de patient chiffré au repos n'a
       pas de portrait à montrer ; la pastille d'initiales tient le même rôle de
       repère visuel sans ajouter la moindre donnée à l'écran ;
     · la case à cocher de chaque ligne — elle n'ouvre sur aucune action de masse
       dans ce produit, et une case qui ne sert à rien apprend à ne plus regarder
       les cases ;
     · le menu « … » de fin de ligne — il cache derrière un clic des gestes déjà
       peu nombreux, et un menu déroulant fait de <details> flottants dans un
       tableau à défilement horizontal se coupe sur son propre conteneur.

   PROPRIÉTÉS LOGIQUES PARTOUT : pas un left, pas un right, pas un width. En
   arabe, dir="rtl" retourne la barre de tête, les pastilles et les vignettes
   sans qu'une règle de miroir soit écrite.
   ══════════════════════════════════════════════════════════════════════════════ */

/* ── Compteur du titre ──────────────────────────────────────────────────────── */
/* Le nombre à côté du titre : pas de phrase autour, donc pas de pluriel à
   accorder dans huit langues. Sert aussi au décompte des notes sur la fiche. */
.pat-compte { color: var(--color-text-secondary); font-weight: var(--font-weight-regular);
              font-variant-numeric: tabular-nums; }
/* La phrase qui explique la colonne « lien permanent », juste au-dessus d'elle. */
.pat-intro { margin: 0 0 var(--space-4); font-size: 13px; }

/* ── Formulaire de création, replié ─────────────────────────────────────────── */
/* Un <details> natif : l'état d'ouverture est celui du navigateur, le formulaire
   reste DANS le document quand il est fermé — donc dans l'ordre du clavier, dans
   l'arbre d'accessibilité, et son jeton CSRF est servi à chaque rendu de la
   page. Une fenêtre modale aurait demandé du script pour s'ouvrir, du script
   pour se fermer, et un piège de focus à écrire à la main. */
.pat-ajout { border: 1px dashed var(--color-border); border-radius: var(--radius-lg);
             background: var(--color-background-soft); margin-block-end: var(--space-5); }
.pat-ajout > summary { display: flex; align-items: center; gap: var(--space-2);
                       padding: var(--space-3) var(--space-4); cursor: pointer; list-style: none;
                       font-size: 14px; font-weight: var(--font-weight-semibold); color: var(--color-primary); }
.pat-ajout > summary::-webkit-details-marker { display: none; }   /* Safari : le triangle natif */
.pat-ajout > summary:hover { color: var(--color-primary-hover); }
.pat-ajout-corps { padding: 0 var(--space-4) var(--space-4); }
.pat-ajout .form { max-inline-size: none; }

/* ── Identité : pastille d'initiales + nom + pseudonyme ─────────────────────── */
.pat-ident { display: flex; align-items: center; gap: var(--space-3); min-inline-size: 0; }
.pat-ident-corps { min-inline-size: 0; }
.pat-avatar { flex: none; display: inline-flex; align-items: center; justify-content: center;
              inline-size: 38px; block-size: 38px; border-radius: 50%;
              background: var(--color-primary-soft); color: var(--color-primary);
              border: 1px solid var(--color-border);
              font-size: 13px; font-weight: var(--font-weight-bold); letter-spacing: .3px; }
.pat-avatar-lg { inline-size: 54px; block-size: 54px; font-size: 18px; }
/* `break-word` ET NON `anywhere`, et la différence se voit dans un tableau.
   `anywhere` autorise la coupure entre deux lettres, ce qui ramène la largeur
   MIN-CONTENT de la cellule à un seul caractère : le navigateur en conclut que
   la colonne « Patient » peut tenir dans 70 px, la laisse écraser par la colonne
   voisine, et l'on obtient « Thoma / s R. » sur un téléphone. `break-word` ne
   coupe un mot que s'il ne tient pas seul sur la ligne — la colonne réclame donc
   la largeur du nom le plus long, et le nom se replie proprement à l'espace. */
.pat-nom { display: block; color: var(--color-text); text-decoration: none;
           font-weight: var(--font-weight-semibold); overflow-wrap: break-word; }
.pat-nom:hover { color: var(--color-primary); text-decoration: underline; }
/* Le pseudonyme est un identifiant d'un seul tenant : « Patient #112 » coupé en
   deux lignes se lit comme deux informations. */
.pat-sous { display: block; margin-block-start: 1px; font-size: 12px; white-space: nowrap;
            font-family: ui-monospace, Menlo, monospace; color: var(--color-text-secondary); }

/* ── Les trois compteurs, en ligne ──────────────────────────────────────────── */
/* Un zéro reste écrit mais s'efface : le lire coûte moins cher que de compter
   les rubriques absentes pour deviner laquelle vaut zéro. */
.pat-mini { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3);
            font-size: 12.5px; color: var(--color-text-secondary); }
.pat-mini b { color: var(--color-text); font-weight: var(--font-weight-semibold);
              font-variant-numeric: tabular-nums; }
.pat-mini .est-zero { opacity: .45; }

/* ── Vignettes ──────────────────────────────────────────────────────────────── */
/* minmax(260px, 1fr) : en dessous de 260 px, l'adresse d'un patient et les trois
   compteurs ne tiennent plus sur deux lignes. auto-fill remplit la largeur
   disponible sans qu'aucun palier ne soit écrit à la main. */
.pat-cartes { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
              gap: var(--space-4); }
.pat-carte { display: flex; flex-direction: column; gap: var(--space-3);
             padding: var(--space-5); background: var(--color-surface);
             border: 1px solid var(--color-border); border-radius: var(--radius-lg);
             box-shadow: var(--shadow-sm);
             transition: border-color var(--transition-fast), box-shadow var(--transition-fast); }
.pat-carte:hover { border-color: var(--color-lavender); box-shadow: var(--shadow-md); }
.pat-carte-mail { font-size: 13px; color: var(--color-text-secondary); overflow-wrap: anywhere; }
.pat-carte-perma { font-size: 13px; }
/* `margin-block-start: auto` colle le pied au bas de la carte : les boutons de
   toutes les vignettes d'une rangée s'alignent, quelle que soit la longueur des
   adresses au-dessus. */
.pat-carte-pied { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2);
                  margin-block-start: auto; padding-block-start: var(--space-3);
                  border-block-start: 1px solid var(--color-border-light); }

/* ── Fiche d'un patient ─────────────────────────────────────────────────────── */
/* LES TROIS COMPTEURS, TOUJOURS SUR UNE LIGNE.
   .cards règle ses pistes sur 220 px, taille juste pour une tuile de tableau de
   bord mais qui, sur un téléphone, empile les trois nombres et repousse la fiche
   d'un écran entier. Ici les trois valeurs se comparent — « 8 à venir, 10
   réalisées, 0 annulée » est UNE phrase — et se lisent donc côte à côte, à
   390 px comme à 1440. La borne de 620 px les empêche de s'étirer sur toute la
   largeur d'un grand écran, où trois nombres de deux chiffres n'ont rien à faire
   dans des tuiles de 430 px. */
.pat-bilan { grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
             max-inline-size: 620px; margin-block-end: var(--space-5); }
@media (max-width: 620px) {
  .pat-bilan .card { padding: var(--space-4) var(--space-3); }
  .pat-bilan .stat { font-size: 26px; }
}
.pat-panneau { margin-block-end: var(--space-4); }
/* Deux colonnes : l'intitulé se serre sur son texte, la valeur prend le reste.
   minmax(0, 1fr) et non 1fr — une adresse sans espace élargirait sinon la piste
   au lieu de se replier. */
.pat-faits { display: grid; grid-template-columns: auto minmax(0, 1fr);
             gap: var(--space-2) var(--space-5); margin: 0; font-size: 14px; }
.pat-faits dt { color: var(--color-text-secondary); font-size: 12px;
                text-transform: uppercase; letter-spacing: .6px; }
.pat-faits dd { margin: 0; overflow-wrap: anywhere; }
@media (max-width: 560px) {
  /* Sous 560 px, l'intitulé et la valeur ne tiennent plus côte à côte : la
     définition passe au-dessous de son terme, comme une vraie liste de
     définitions. */
  .pat-faits { grid-template-columns: minmax(0, 1fr); gap: var(--space-1) 0; }
  .pat-faits dd { margin-block-end: var(--space-3); }
}
.pat-perma { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
/* `text-align: start` sur un champ qui porte lui-même dir="ltr" : la feuille de
   base aligne à droite TOUT champ situé dans une page arabe ([dir="rtl"] input),
   sans regarder la direction de l'élément. Une adresse web est du texte latin,
   elle commence à gauche — même au milieu d'un écran qui se lit à l'envers. */
.pat-perma .input { flex: 1 1 240px; min-inline-size: 0; font-size: 12.5px;
                    font-family: ui-monospace, Menlo, monospace; text-align: start; }
.pat-tronque { margin-block: var(--space-3) 0; }
/* Sous 720 px, l'extrait de note quitte le tableau des rendez-vous passés : le
   texte complet est rendu quelques centimètres plus bas, dans la section des
   notes. Rien n'est caché — la même page le porte deux fois, et sur un écran
   étroit une seule des deux fois vaut la peine. */
@media (max-width: 720px) { .pat-col-note { display: none; } }
.pat-note-p { margin: var(--space-2) 0 0; }

/* Une note, en entier. Le texte est du TEXTE : sélectionnable, imprimable, lu
   par un lecteur d'écran. `pre-wrap` conserve les paragraphes du praticien — un
   compte rendu écrit en paragraphes se relit en paragraphes. */
.pat-note { padding: var(--space-4); margin-block-end: var(--space-3);
            background: var(--color-background-soft);
            border: 1px solid var(--color-border-light); border-radius: var(--radius-md); }
.pat-note-tete { display: flex; flex-wrap: wrap; align-items: baseline;
                 gap: var(--space-2) var(--space-3); margin-block-end: var(--space-2);
                 font-size: 13px; }
.pat-note-corps { white-space: pre-wrap; overflow-wrap: anywhere; max-inline-size: 78ch;
                  font-size: 14.5px; line-height: var(--line-height-relaxed); color: var(--color-text); }

/* ── Mouvement désarmé ──────────────────────────────────────────────────────── */
/* La seule animation de ce lot est l'adoucissement du survol des vignettes. Qui
   a demandé moins de mouvement n'en veut pas non plus ici : la bordure et
   l'ombre changent toujours — l'information de survol reste — mais d'un coup. */
@media (prefers-reduced-motion: reduce) {
  .pat-carte { transition: none; }
}

/* ── Écrans étroits ─────────────────────────────────────────────────────────── */
@media (max-width: 620px) {
  /* .page-head passe déjà sur deux lignes tout seul (flex-wrap) : il n'y a que la
     pastille de la fiche à resserrer, un disque de 54 px mangeant un quart de la
     largeur d'un téléphone. */
  .pat-avatar-lg { inline-size: 44px; block-size: 44px; font-size: 15px; }
}
