/* =====================================================================
   SOCLE GESTION-* — Composants.
   Récupérable tel quel par toute application « Gestion-* ». Ne contient
   aucune couleur littérale : tout vient de tokens.css.
   Navigation : tiroir escamotable (56 px replié / 224 px déplié), état
   porté par l'attribut data-drawer sur .app, retenu par cookie.
   ===================================================================== */

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--fs-base);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { font-family: var(--font-body); font-weight: 600; margin: 0; }
a { color: var(--brand); }
.num, .mono { font-family: var(--font-num); font-variant-numeric: tabular-nums; }

/* =====================================================================
   Coquille
   ===================================================================== */
.app { display: flex; flex-direction: column; min-height: 100vh; }
.app-body { display: flex; align-items: stretch; flex: 1; min-height: 0; }
.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.screen { padding: 14px 16px 24px; }

/* ---------------------------------------------------------------------
   Bandeau d'authentification contournée (développement seulement)

   Aucune couleur littérale : --danger et --surface sont des jetons, et c'est
   la seule teinte sémantique qui convienne — l'application est ouverte à qui
   l'atteint. Il prend toute la largeur et vit ENTRE la barre supérieure et le
   corps, donc il ne défile pas avec l'écran : un avertissement qu'on peut
   faire sortir du champ en descendant d'une page n'avertit plus.
   --------------------------------------------------------------------- */
.bandeau-contournement {
  padding: 7px 16px;
  background: var(--danger);
  color: var(--surface);
  /* --fs-base et NON --fs-small : celui-ci vaut 11 px, bon pour un nombre et
     illisible pour une phrase (cf. l'aide d'un champ, § 5 de CLAUDE.md). Un
     avertissement qu'on renonce à lire n'avertit pas. */
  font-size: var(--fs-base);
  line-height: 1.4;
}
.bandeau-contournement code {
  background: transparent;
  color: inherit;
  font-family: var(--font-num);
}

/* ---------------------------------------------------------------------
   Bandeau supérieur : nom de l'application au centre, sur toute la largeur
   --------------------------------------------------------------------- */
.appbar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  height: var(--h-appbar);
  padding: 0 10px;
  background: var(--rail);
  color: var(--rail-fg);
}

.ab-left { display: flex; align-items: center; gap: 2px; min-width: 0; }
.ab-right { display: flex; align-items: center; gap: 8px; justify-self: end; }

.ab-name {
  justify-self: center;
  font-weight: 600;
  font-size: 15px;
  white-space: nowrap;
}

.ab-toggle {
  width: 30px; height: 30px;
  border: 0; border-radius: 6px;
  background: transparent; color: inherit;
  cursor: pointer; opacity: .8;
  display: grid; place-items: center;
}
.ab-toggle:hover { opacity: 1; background: rgba(255, 255, 255, .12); }
.ab-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; opacity: 1; }
.ab-toggle svg { width: 17px; height: 17px; }

.ab-exercice {
  font-family: var(--font-num);
  font-size: var(--fs-small);
  padding: 3px 9px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .13);
}

/* Favoris : une icône par écran marqué, puis l'étoile de l'écran courant.
   Séparée du reste du bandeau par un filet, pour qu'elle se lise comme une
   zone et non comme la suite des commandes de gauche. */
.favoris {
  display: flex; align-items: center; gap: 1px;
  padding-left: 8px; margin-left: 2px;
  border-left: 1px solid rgba(255, 255, 255, .18);
}
.fav, .fav-toggle {
  width: 28px; height: 28px;
  display: grid; place-items: center;
  border: 0; border-radius: 5px;
  background: transparent; color: inherit;
  opacity: .72; cursor: pointer;
}
.fav svg, .fav-toggle svg { width: 16px; height: 16px; }
.fav:hover, .fav-toggle:hover { opacity: 1; background: rgba(255, 255, 255, .12); }
.fav:focus-visible, .fav-toggle:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px; opacity: 1;
}
/* L'écran où l'on se trouve : le même repère que la navigation latérale. */
.fav.is-active { opacity: 1; background: rgba(255, 255, 255, .14); box-shadow: inset 0 -2px 0 var(--accent); }
.fav-toggle.is-on { opacity: 1; color: var(--accent); }
.fav-toggle[disabled] { opacity: .4; cursor: progress; }

/* ---------------------------------------------------------------------
   Navigation latérale : tiroir escamotable
   --------------------------------------------------------------------- */
.sidenav {
  width: var(--w-rail);
  flex: 0 0 var(--w-rail);
  background: var(--rail);
  color: var(--rail-fg);
  padding: 7px 0;
  display: flex; flex-direction: column; gap: 1px;
  transition: width .16s ease, flex-basis .16s ease;
}
.app[data-drawer="open"] .sidenav {
  width: var(--w-drawer);
  flex-basis: var(--w-drawer);
}
@media (prefers-reduced-motion: reduce) { .sidenav { transition: none; } }

.sn-sec { position: relative; }

/* Écran courant : repère turquoise sur le bord du tiroir. */
.sn-sec.is-active::before {
  content: "";
  position: absolute; left: 0; top: 4px; bottom: 4px;
  width: 3px; border-radius: 0 2px 2px 0;
  background: var(--accent);
}

.sn-btn {
  width: 100%; height: 38px;
  border: 0; background: transparent; color: inherit;
  cursor: pointer; opacity: .78;
  display: flex; align-items: center; gap: 11px;
  padding: 0 10px 0 13px;
  font: inherit; font-size: 13.5px; text-align: left;
  text-decoration: none;
}
.sn-btn:hover { opacity: 1; background: rgba(255, 255, 255, .1); }
.sn-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; opacity: 1; }
.sn-btn svg { width: 18px; height: 18px; flex: none; }
.sn-sec.is-active > .sn-btn { opacity: 1; background: rgba(255, 255, 255, .14); font-weight: 600; }

.sn-lab { display: none; white-space: nowrap; overflow: hidden; }
.app[data-drawer="open"] .sn-lab { display: block; }

.sn-items-title {
  font-size: var(--fs-th);
  font-weight: 600; letter-spacing: .09em; text-transform: uppercase;
  color: var(--muted);
  padding: 4px 9px 6px;
}
.sn-items a {
  display: block;
  padding: 5px 9px;
  border-radius: 4px;
  font-size: 13.5px;
  text-decoration: none;
  white-space: nowrap;
}

/* Replié : les écrans de la section sortent en volet au survol. */
.app[data-drawer="closed"] .sn-items {
  position: absolute; left: 50px; top: -3px; z-index: 40;
  min-width: 216px; display: none;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--ombre-volet);
  padding: 5px;
}
.app[data-drawer="closed"] .sn-sec:hover .sn-items,
.app[data-drawer="closed"] .sn-sec:focus-within .sn-items { display: block; }
.app[data-drawer="closed"] .sn-items a { color: var(--fg); }
.app[data-drawer="closed"] .sn-items a:hover { background: var(--line-soft); }
.app[data-drawer="closed"] .sn-items a.active { color: var(--brand); font-weight: 600; }

/* Déplié : les écrans sont dans le flux, indentés sous leur section. */
.app[data-drawer="open"] .sn-items-title { display: none; }
.app[data-drawer="open"] .sn-items { padding: 1px 0 5px; }
.app[data-drawer="open"] .sn-items a {
  color: var(--rail-fg); opacity: .72;
  padding: 4px 10px 4px 42px;
  border-radius: 0;
}
.app[data-drawer="open"] .sn-items a:hover { opacity: 1; background: rgba(255, 255, 255, .08); }
.app[data-drawer="open"] .sn-items a.active {
  opacity: 1; font-weight: 600;
  box-shadow: inset 2px 0 0 var(--accent);
}

/* ---------------------------------------------------------------------
   En-tête d'écran : titre à gauche, filtres de l'écran à droite.
   Seul endroit où vivent les filtres — ils ne sont jamais répétés dans
   le corps de l'écran.
   --------------------------------------------------------------------- */
.pagehead {
  display: flex; align-items: center; gap: 14px;
  padding: 0 16px;
  min-height: var(--h-pagehead);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.pagehead h1 { font-size: var(--fs-h1); font-weight: 600; letter-spacing: -.005em; }
.pagehead-sep { flex: 1; }

/* Sélecteur segmenté (périodes, vues). */
.seg { display: flex; border: 1px solid var(--line); border-radius: 999px; overflow: hidden; }
.seg a {
  font-size: 12.5px; text-decoration: none;
  color: var(--muted); padding: 3px 12px;
  background: var(--surface);
}
.seg a + a { border-left: 1px solid var(--line); }
.seg a:hover { color: var(--fg); background: var(--line-soft); }
.seg a.active { background: var(--brand); color: var(--surface); font-weight: 600; }

/* =====================================================================
   Indicateurs : bandeau continu, pas quatre grosses cartes
   ===================================================================== */
.kpis {
  display: flex;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  margin-bottom: 14px;
}
.kpi { flex: 1; padding: 8px 13px 9px; border-left: 1px solid var(--line-soft); min-width: 0; }
.kpi:first-child { border-left: 0; }
.kpi-label {
  font-size: var(--fs-th);
  font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  color: var(--muted); white-space: nowrap;
}
.kpi-value {
  font-family: var(--font-num);
  font-size: var(--fs-kpi);
  font-weight: 500; letter-spacing: -.035em;
  font-variant-numeric: tabular-nums;
  line-height: 1.35;
}
.kpi-value.accent { color: var(--accent-ink); }
.kpi-sub { font-size: var(--fs-small); color: var(--muted); font-variant-numeric: tabular-nums; }

/* =====================================================================
   Cartes
   ===================================================================== */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: 14px;
}
.card-head {
  display: flex; align-items: baseline; gap: 10px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--line-soft);
}
.card-head h2 { font-size: var(--fs-h2); font-weight: 600; }
.card-note { font-size: var(--fs-small); color: var(--muted); }
.card-sep { flex: 1; }

/* =====================================================================
   Tableaux
   La densité vient du contenu des cellules, pas d'une police rapetissée :
   l'unité monte dans l'en-tête, la synthèse est à l'euro, les en-têtes
   tiennent sur deux lignes, les chiffres sont tabulaires.
   ===================================================================== */
.table-scroll { overflow-x: auto; }

table { width: 100%; border-collapse: collapse; font-size: var(--fs-table); }

th, td { padding: var(--pad-cell); text-align: right; white-space: nowrap; }

thead th {
  font-family: var(--font-body);
  font-size: var(--fs-th);
  font-weight: 600; line-height: 1.25; letter-spacing: .02em;
  color: var(--muted);
  vertical-align: bottom;
  border-bottom: 1px solid var(--line);
}
.th-sub { display: block; font-weight: 400; opacity: .78; }

tbody td {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  border-bottom: 1px solid var(--line-soft);
}
tbody tr:hover td { background: var(--line-soft); }

/* Première colonne : libellé, aligné à gauche, en condensé. */
th.col-label, td.col-label { text-align: left; }
td.col-label {
  font-family: var(--font-dense);
  font-size: var(--fs-table-label);
  font-weight: 500;
}

td.val-pos { color: var(--pos); font-weight: 500; }
td.val-low { color: var(--warn); font-weight: 500; }
td.val-zero { color: var(--muted); opacity: .5; }

tfoot th {
  font-family: var(--font-num);
  font-size: var(--fs-table);
  font-weight: 500;
  padding: 5px 7px;
  text-align: right;
  font-variant-numeric: tabular-nums;
  border-top: 1.5px solid var(--line);
  background: var(--line-soft);
}
tfoot th.col-label {
  font-family: var(--font-body);
  font-size: var(--fs-small);
  font-weight: 600; letter-spacing: .05em;
  text-align: left;
}

/* Tableaux de liste CRUD : texte proportionnel, pas de chiffres alignés. */
.crud-list tbody td { font-family: var(--font-body); font-size: 13px; text-align: left; }
.crud-list th { text-align: left; }

/* =====================================================================
   Graphiques
   Les libellés d'axe vivent dans le SVG (ils doivent s'aligner sur le
   tracé) ; les légendes sont du HTML, donc à taille réelle.
   ===================================================================== */
.charts { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 1100px) { .charts { grid-template-columns: 1fr; } }

/* Graphique seul : borné en largeur. Un SVG en width:100% est mis à
   l'échelle, donc l'étaler sur 1 300 px gonflerait ses libellés à ~38 px. */
.charts-solo { display: grid; grid-template-columns: minmax(0, 560px); margin-bottom: 14px; }

.chart-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 12px 8px;
}
.chart-card h2 { font-size: 13px; font-weight: 600; }
.chart-unit { font-size: var(--fs-small); color: var(--muted); margin: 1px 0 6px; }
.chart-svg { width: 100%; height: auto; display: block; }

.legend { display: flex; flex-wrap: wrap; gap: 2px 12px; margin-bottom: 5px; }
.legend span { display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; color: var(--chart-text); }
.legend i { width: 9px; height: 9px; border-radius: 2px; display: block; flex: none; }

.empty { font-size: 13px; color: var(--muted); padding: 10px 0; margin: 0; }

/* =====================================================================
   Formulaires, boutons, modales
   ===================================================================== */
label { display: block; font-size: 13px; color: var(--muted); }

input, select, textarea, button { font-family: inherit; font-size: 13.5px; }

input, select, textarea {
  padding: 4px 7px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--surface);
  color: var(--fg);
}
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--brand); outline-offset: -1px;
}
input[type="checkbox"] { width: 1.05rem; height: 1.05rem; }

button {
  border: 0;
  border-radius: 4px;
  padding: 5px 12px;
  background: var(--brand);
  color: var(--surface);
  cursor: pointer;
}
button:hover { background: var(--brand-strong); }
button:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

.btn-ghost {
  background: transparent;
  color: var(--fg);
  border: 1px solid var(--line);
}
.btn-ghost:hover { background: var(--line-soft); }

/* Bouton d'action de tableau réduit à une icône : chaque ligne tient sur
   une seule ligne, quel que soit le nombre d'actions. */
.btn-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0;
}
.btn-icon svg { width: 15px; height: 15px; display: block; }

.crud-actions { display: flex; flex-direction: row; gap: 4px; }
.crud-delete { background: var(--danger); }
.crud-delete:hover { background: var(--danger); filter: brightness(.9); }

/* Modales de création/modification : boîte centrée sur fond assombri.

   « font-size » et « white-space » ne sont PAS décoratifs ici, ils réparent un
   HÉRITAGE : la modale de modification vit dans la cellule d'actions de sa
   ligne — c'est ce qui la fait ouvrir sans une ligne de JavaScript — donc elle
   héritait de « .crud-list tbody td { font-size: 13px } » et du
   « white-space: nowrap » de « th, td ». Les champs n'en souffraient pas,
   leurs tailles étant absolues ; la première PHRASE posée dans une modale,
   elle, s'est retrouvée à 13 px et INSÉCABLE, donc à faire défiler la modale
   horizontalement. La modale de création, rendue dans l'en-tête de la carte,
   n'héritait de rien : les deux formulaires du même objet ne se ressemblaient
   plus.
   Une modale est une couche flottante, et elle n'est dans la cellule que par
   nécessité de parenté : elle ne doit donc rien tenir du tableau. Ne pas
   retirer ces deux lignes « puisque rien ne les emploie » — ce qui les emploie
   est ailleurs, dans deux règles sur « td ». */
dialog.modal {
  border: 0;
  border-radius: 8px;
  padding: 0;
  width: min(560px, 92vw);
  font-size: var(--fs-base);
  white-space: normal;
  color: var(--fg);
  background: var(--surface);
  box-shadow: var(--ombre-modale);
}
dialog.modal::backdrop { background: var(--voile-modale); }

.modal-header { padding: 11px 16px; border-bottom: 1px solid var(--line); }
.modal-header h2 { font-size: var(--fs-h2); }
/* Le corps défile, l'en-tête et le pied restent — donc les boutons sont
   toujours sous la main, quelle que soit la longueur du formulaire.
   80vh et non 70 : mesuré sur le plus long des formulaires, celui d'une
   dépense, qui demande 678 px. À 70vh il dépassait encore de 48 px sur un
   écran de 900, alors que 80vh en offre 720. Et la marge est sûre : corps
   + en-tête + pied font 0,8 × H + 100 px, soit 820 px sur un écran de 900 et
   714 px sur 768, l'un et l'autre sous la hauteur maximale que le navigateur
   accorde à un « dialog ». Sur un écran plus court, c'est lui qui borne, et le
   corps défile — ce qui est le comportement voulu, pas un défaut. */
.modal-body { padding: 12px 16px; max-height: 80vh; overflow-y: auto; }
.modal-footer {
  padding: 11px 16px;
  border-top: 1px solid var(--line);
  display: flex; align-items: center; justify-content: flex-end; gap: 8px;
}
/* Bascule vers une autre saisie : un lien, pas un bouton. Ce n'est pas une
   action sur ce formulaire, c'est en ouvrir un autre — et le lire comme un
   troisième bouton ferait hésiter devant « Annuler » et « Créer ». */
.modal-bascule {
  font-size: 12.5px; color: var(--accent-ink); text-decoration: none;
  border-bottom: 1px dotted currentColor;
}
.modal-bascule:hover { color: var(--brand-strong); }

/* Ligne de formulaire « à l'ancienne » : un libellé, puis le champ.

   UNE GRILLE ET NON UNE RANGÉE FLEX, pour une seule raison : l'aide d'un champ
   doit pouvoir prendre la LARGEUR ENTIÈRE de la ligne. En flex, elle était
   nécessairement dans la boîte du champ, donc dans une colonne de 334 px —
   46 caractères par ligne dans une modale de 560. Une grille en fait un élément
   à part entière, qu'une seule déclaration fait couvrir les deux colonnes.
   Le gain mesuré : les six aides de l'écran d'une dépense passent de 29 lignes
   à 10, et le formulaire de 1022 px à ~640. Aucune police n'a été réduite, ce
   que le § 6 des consignes interdit — c'est la MESURE qui était fausse. */
.form-row {
  display: grid;
  grid-template-columns: 180px minmax(0, 1fr);
  align-items: center;
  gap: 4px 14px;
  padding: 5px 0;
}
.form-row + .form-row { border-top: 1px solid var(--line-soft); }
.form-row label { text-align: right; }
.form-row input, .form-row select, .form-row textarea { width: 100%; }

/* Champ avec son bouton [+] de création à la volée : la liste prend la place,
   le bouton prend la sienne et ne la cède pas. D'où « flex: 1 » sur la liste
   et « flex: none » sur le bouton — sans quoi le bouton se comprimerait sur un
   champ étroit, et un bouton de 12 px ne se vise plus.
   Le bouton est discret (btn-ghost) : au bout de quelque temps les listes sont
   complètes et il ne sert plus. */
.field-creation { display: flex; align-items: center; gap: 4px; }
.field-creation select { flex: 1; min-width: 0; }
.field-creation .btn-creation { flex: none; }

/* L'explication d'un champ, sous lui. Visible, et non derrière un survol : une
   explication qu'il faut découvrir est une explication que personne ne trouve.
   Quatre décisions :
   - LA RANGÉE ENTIÈRE, « grid-column: 1 / -1 ». L'aide est sœur du champ dans
     la grille (cf. field_row_html), donc elle n'est plus tenue par la colonne
     du champ : la mesure passe de 46 à ~65 caractères par ligne. C'est le
     geste qui a ramené l'écran d'une dépense dans la hauteur de la modale,
     sans toucher à une seule taille de police.
   - PAS de font-size. C'est une PHRASE, pas un nombre qu'on survole : --fs-small
     vaut 11 px, convenable pour un axe ou pour l'aperçu TTC, illisible pour de
     la prose. Le § 6 des consignes tient à ce qu'aucun geste de densité ne
     rapetisse la police ; ici c'est la COULEUR qui dit « secondaire »
     (--muted, 5,70:1 sur --surface, donc largement au-dessus du plancher).
   - « max-width » en « ch » et non en pixels, et elle RESTE : la mesure de
     lecture se compte en caractères, et une ligne de 100 caractères ne se lit
     pas mieux qu'une de 46. Prendre la rangée entière n'est pas prendre toute
     la largeur possible.
   - Pas de « margin-top » : l'écart vient du « gap » de rangée de la grille,
     qui vaut pour tous ses éléments. Les deux se seraient additionnés. */
.form-row .field-aide {
  grid-column: 1 / -1;
  display: block;
  max-width: 62ch;
  color: var(--muted);
  line-height: 1.35;
}

/* Aperçu d'une valeur dérivée du montant saisi (le TTC) : une indication, pas
   une donnée. D'où le gris de --muted et la taille réduite — le chrome est
   plus discret que la donnée, comme sur les axes des graphiques. Il reste
   VIDE tant qu'aucun montant n'est saisi, donc sa place ne doit pas se
   réserver : un bloc à la ligne, qui ne pousse rien quand il est vide. */
.form-row .apercu {
  display: block;
  margin-top: 2px;
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-small);
  color: var(--muted);
}
.form-row .apercu:empty { margin-top: 0; }

/* ---------------------------------------------------------------- */
/* Documentation : prose, dans la même carte que le reste des écrans. */
/* ---------------------------------------------------------------- */
.doc {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 11px 16px 14px;
  margin-bottom: 14px;
  max-width: 1000px;
}
.doc-head { display: flex; align-items: baseline; gap: 10px; }
.doc-head h2 { font-size: var(--fs-h1); }
.doc-count {
  font-family: var(--font-dense);
  font-size: var(--fs-small);
  color: var(--muted);
  white-space: nowrap;
}
.doc-count.is-missing { color: var(--warn); }
.doc-note {
  font-size: var(--fs-small);
  color: var(--muted);
  margin-top: 2px;
  max-width: 78ch;
}
.doc code {
  font-family: var(--font-num);
  font-size: .92em;
  background: var(--line-soft);
  border-radius: 3px;
  padding: 0 3px;
}
.doc-sec { border-top: 1px solid var(--line-soft); padding-top: 11px; margin-top: 11px; }
.doc-sec-head { display: flex; align-items: center; gap: 7px; }
.doc-sec-head svg { width: 15px; height: 15px; color: var(--brand); flex: 0 0 auto; }
.doc-sec-head h3 { font-size: var(--fs-h2); flex: 0 0 auto; }
.doc-descr { font-size: var(--fs-base); margin-top: 3px; max-width: 86ch; }
.doc-descr.is-missing { color: var(--warn); }
.doc-url {
  font-family: var(--font-num);
  font-size: var(--fs-small);
  color: var(--muted);
  text-decoration: none;
}
a.doc-url:hover { color: var(--brand); text-decoration: underline; }
/* Un écran par ligne : identité à gauche sur largeur fixe, rôle à droite. */
.doc-entries { list-style: none; margin-top: 9px; }
.doc-entry {
  display: grid;
  grid-template-columns: 230px minmax(0, 1fr);
  gap: 4px 16px;
  padding: 6px 0;
}
.doc-entry + .doc-entry { border-top: 1px solid var(--line-soft); }
.doc-entry-id { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.doc-entry-id a { font-weight: 500; }
.doc-entry .doc-descr { margin-top: 0; }
/* Workflow : l'ordre est l'information, donc la numérotation reste. */
.doc-etapes { margin-top: 7px; padding-left: 22px; font-size: var(--fs-base); }
.doc-etapes li { padding: 2px 0; }
.doc-declencheur {
  font-family: var(--font-dense);
  font-size: var(--fs-small);
  color: var(--brand);
  background: var(--brand-soft);
  border-radius: 3px;
  padding: 1px 6px;
  white-space: nowrap;
}

/* =====================================================================
   Planning mensuel (inc/planning.php)
   Les jours en lignes, les ressources en colonnes : un mois ferait 31
   colonnes de ~30 px, trop peu pour un code client non tronqué.
   ===================================================================== */
.planning .card-head { flex-wrap: wrap; }
.planning .legend { margin: 0; }
.pl-table { table-layout: auto; }
.pl-table th, .pl-table td { border-bottom: 1px solid var(--line-soft); }
/* Un filet avant chaque cellule sauf la première : les colonnes
   d'intervenants se lisent alors comme des colonnes, et non comme une nappe
   de pastilles. Le filet avant « Libres » est plus marqué, pour détacher la
   colonne de synthèse des colonnes de données. */
.pl-table tr > * + * { border-left: 1px solid var(--line); }
.pl-table .pl-free { border-left-width: 2px; }
.pl-table thead th {
  text-align: left; vertical-align: bottom;
  padding-bottom: 5px; border-bottom: 2px solid var(--line);
}
/* En-tête de colonne : le trigramme porte la colonne, le nom complet le
   précise en dessous, en gris. */
/* 78 px et non 92 : la largeur de colonne est reprise à la pastille, qui en
   avait besoin pour respirer — « MUXCV€ » tient dans 78 px avec son nouveau
   rembourrage, et la grille d'un mois se resserre d'autant. */
.pl-table thead th:not(.pl-day):not(.pl-free) {
  font-family: var(--font-dense); font-size: var(--fs-h2);
  color: var(--fg); min-width: 78px;
}
.pl-table thead .th-sub { display: block; font-weight: 400; }
.pl-table tbody td { padding: 2px 4px; text-align: left; font-family: var(--font-body); }
/* Colonne de date : étroite, le jour de semaine en gris devant le numéro. */
th.pl-day {
  width: 1%; white-space: nowrap; text-align: right;
  font-family: var(--font-dense); font-size: var(--fs-table-label);
  font-weight: 500; color: var(--fg);
}
.pl-dow { color: var(--muted); font-size: var(--fs-small); margin-right: 5px; }
/* Gouttière des semaines : une colonne étroite, le numéro écrit une seule fois
   par semaine. Fond neutre et NON transparent, pour qu'elle reste une
   gouttière quand la ligne prend la teinte d'un week-end, d'un férié ou
   d'aujourd'hui — sinon elle changerait de couleur sept fois par mois et ne se
   lirait plus comme une colonne. Même raison pour le survol, qui s'arrête à
   son bord. */
th.pl-sem {
  width: 1%; white-space: nowrap; text-align: right; padding: 2px 6px;
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-small); font-weight: 400; color: var(--muted);
  background: var(--bg);
  border-right: 1px solid var(--line);
}
.pl-free {
  width: 1%; text-align: right; white-space: nowrap;
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  color: var(--muted);
}
/* Trois états de ligne, trois teintes, cf. tokens.css. La date n'est PAS
   grisée sur les lignes non ouvrées : sous ces bleus --muted tombe sous
   4,5:1, et la teinte de la ligne le dit déjà. */
tr.is-weekend { background: var(--plan-weekend); }
/* Jour férié : non ouvré comme un week-end, mais pour une autre raison — d'où
   un second bleu, plus profond, et le nom du jour en infobulle. */
tr.is-chome { background: var(--plan-chome); }
tr.is-chome th.pl-day { font-style: italic; cursor: help; }
/* Aujourd'hui : la gouttière d'accent est conservée, et la ligne prend un vert
   clair, plus clair que la gouttière. */
tr.is-today th.pl-day { box-shadow: inset 3px 0 0 var(--accent); }
tr.is-today { background: var(--plan-today); }
/* Rembourrage 2px 9px et non 1px 6px : la pastille est la cible du clic droit
   et du glisser-déposer, et elle porte maintenant jusqu'à six caractères
   (« MUXCV€ »). Ce qu'elle gagne est pris sur la largeur de la colonne, pas
   ajouté à la grille. */
.pl-tag {
  display: inline-block;
  font-family: var(--font-dense); font-size: var(--fs-table-label); font-weight: 500;
  border-radius: 3px; padding: 2px 9px; margin: 1px 3px 1px 0;
  cursor: default;
}
/* Un liseré de 1 px dans l'encre de la pastille elle-même (currentColor, donc
   une seule règle pour les quatre slots). Sans lui, une pastille se dissout
   dans la teinte de sa ligne : la pastille « prestation » est un bleu pâle, et
   sur un week-end bleu l'écart tombait à 3 ΔE — elle disparaissait. Le liseré
   rend la pastille indépendante de la teinte de sa ligne, celles d'aujourd'hui
   et du survol comprises, et le restera si ces teintes changent encore. */
.pl-tag { box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 30%, transparent); }
.pl-swatch { width: 9px; height: 9px; border-radius: 2px; display: inline-block; flex: 0 0 auto; }
/* Le slot passe par --tag-bg, et non directement par background : c'est ce qui
   permet aux règles de TON, juste dessous, de rendre la même teinte plus ou
   moins pâle sans connaître laquelle. Une seule déclaration de la valeur. */
.pl-tag.is-s0, .pl-swatch.is-s0 { --tag-bg: var(--slot0-bg); background: var(--tag-bg); color: var(--slot0-fg); }
.pl-tag.is-s1, .pl-swatch.is-s1 { --tag-bg: var(--slot1-bg); background: var(--tag-bg); color: var(--slot1-fg); }
.pl-tag.is-s2, .pl-swatch.is-s2 { --tag-bg: var(--slot2-bg); background: var(--tag-bg); color: var(--slot2-fg); }
.pl-tag.is-s3, .pl-swatch.is-s3 { --tag-bg: var(--slot3-bg); background: var(--tag-bg); color: var(--slot3-fg); }

/* TON : l'intensité d'une pastille dans la teinte de sa nature (clé 'ton' de
   render_planning). Deux leviers seulement, et c'est délibéré :
   - le FOND, mélangé vers la surface, donc d'autant plus pâle qu'on est loin
     d'un engagement. L'encre ne bouge pas, donc le contraste du texte ne fait
     que MONTER quand le fond pâlit : un ton effacé reste lisible ;
   - la GRAISSE, et il n'y en a que trois à disposition — 400, 500, 600 sont
     les seules empattements servis depuis assets/fonts/, un font-weight: 300
     retomberait sur 400 sans rien dire. Le ton 2 est l'aspect de référence,
     celui d'avant : il n'a donc aucune règle.
   Ces règles viennent APRÈS les slots et à spécificité égale : c'est l'ordre
   du fichier qui fait gagner le ton sur l'aplat plein. Ne pas les remonter. */
.pl-tag.is-t0 { background: color-mix(in srgb, var(--tag-bg) 32%, var(--surface)); font-weight: 400; }
.pl-tag.is-t1 { background: color-mix(in srgb, var(--tag-bg) 64%, var(--surface)); }
.pl-tag.is-t3 { font-weight: 600; }
/* Écartée (clé 'ecarte') : l'occupation n'entre dans aucun chiffre. Le rouge
   est dans l'ENCRE et non dans le fond — l'aplat doit continuer de dire la
   nature — et comme le liseré est en currentColor, il passe au rouge avec le
   texte, sans une règle de plus. */
.pl-tag.is-ecarte {
  background: color-mix(in srgb, var(--tag-bg) 26%, var(--surface));
  color: var(--danger); font-weight: 400;
}
/* Marque écrite après le code (clé 'suffixe') : un caractère, en gras pour
   qu'il se détache d'un ton léger, et discret pour ne pas se lire comme une
   partie du code. */
.pl-tag-suf { margin-left: 3px; font-weight: 600; opacity: .8; }
.planning tfoot td, .planning tfoot th {
  font-family: var(--font-dense); font-size: var(--fs-table-label);
  color: var(--muted); border-top: 1px solid var(--line); border-bottom: 0;
}

/* Navigation de mois (nav_mois_html) : quatre zones de largeur FIXE, et un
   groupe de largeur constante. Les filtres d'écran sont calés sur le bord
   droit (.pagehead-sep porte flex: 1), donc la position des chevrons dépend
   de la largeur de TOUT le groupe, pas seulement de la zone du mois : d'où
   le flex: 0 0 auto sur chaque zone, la quatrième toujours rendue (inerte
   sur le mois courant) et l'indice placé avant. Sans quoi il faut viser à
   nouveau à chaque pas. */
.nav-mois-groupe { display: inline-flex; align-items: center; gap: 7px; }
.nav-mois {
  display: inline-flex; align-items: stretch; height: 24px;
  border: 1px solid var(--line); border-radius: 999px;
  overflow: hidden; background: var(--surface);
}
.nm-pas {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; color: var(--muted); text-decoration: none;
}
/* Le gabarit qui tient la place du retour au mois courant quand on y est. */
.nm-pas.is-inerte { color: var(--line); pointer-events: none; }
.nm-pas svg { width: 15px; height: 15px; }
.nm-pas:hover { color: var(--fg); background: var(--line-soft); }
.nav-mois > * + * { border-left: 1px solid var(--line); }
/* Le nom du mois EST le déclencheur du sélecteur natif : l'input le recouvre
   en transparence. Atteindre un mois lointain devient un geste au lieu de
   dix clics sur un chevron. */
.nm-mois {
  flex: 0 0 auto;
  position: relative; width: 136px; overflow: hidden; white-space: nowrap;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 12.5px; font-weight: 600; color: var(--fg); cursor: pointer;
}
.nm-mois:hover { background: var(--line-soft); }
.nm-mois input {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; cursor: pointer;
  border: 0; padding: 0; margin: 0; min-width: 0; background: none;
}
.nm-courant {
  height: 24px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface);
}
.nm-indice {
  margin-right: 2px; white-space: nowrap; font-size: 12.5px; color: var(--accent-ink);
  text-decoration: none; border-bottom: 1px dotted currentColor;
}

/* Case libre d'un jour ouvré : un emplacement de saisie, donc cliquable.
   La plage retenue est un aplat turquoise — un aplat, jamais du texte. */
.pl-table td.pl-libre { cursor: pointer; }
.pl-table td.pl-libre:hover { background: var(--line-soft); }
.pl-table td.pl-libre.is-selected { background: var(--accent); }
.pl-tag[data-plan-menu] { cursor: context-menu; }
/* Prestation de plusieurs jours : les pastilles des jours contigus ne
   s'arrondissent qu'aux extrémités du bloc, ce qui les lit comme une seule
   prestation plutôt que comme N pastilles identiques. */
.pl-tag.is-debut { border-bottom-left-radius: 0; border-bottom-right-radius: 0; margin-bottom: 0; }
.pl-tag.is-milieu { border-radius: 0; margin-top: 0; margin-bottom: 0; }
.pl-tag.is-fin { border-top-left-radius: 0; border-top-right-radius: 0; margin-top: 0; }

/* Menu contextuel du planning (socle.js). Ses coordonnées sont posées en
   JavaScript : la position du curseur est une donnée, pas une décision
   graphique — tout le reste de son apparence est ici. */
.ctxmenu {
  position: fixed; z-index: 60; min-width: 158px; padding: 4px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--ombre-volet);
}
.ctxmenu a, .ctxmenu button {
  display: block; width: 100%; padding: 5px 10px; border: 0; border-radius: 4px;
  font-family: inherit; font-size: var(--fs-base); font-weight: 400;
  text-align: left; background: none; color: var(--fg); text-decoration: none;
}
.ctxmenu a:hover, .ctxmenu a:focus,
.ctxmenu button:hover, .ctxmenu button:focus { background: var(--line-soft); outline: none; }
.ctxmenu button:disabled { color: var(--muted); }
/* Titre de groupe : un menu qui enchaîne « Supprimer » et une liste de
   trigrammes sans rien dire est illisible. */
.ctx-titre {
  padding: 7px 10px 3px; margin-top: 3px; border-top: 1px solid var(--line-soft);
  font-size: var(--fs-th); font-weight: 600; letter-spacing: .07em;
  text-transform: uppercase; color: var(--muted);
}

/* Déplacement d'une occupation : la pastille en vol s'efface, la case visée
   se signale. Un seul repère à la fois, celui de la colonne d'origine. */
/* Marque d'attention sur une pastille (item 'marque' de render_planning) :
   un filet le long du bord gauche, dans la couleur d'alerte. Un filet et non
   un aplat ni une bordure complète : l'aplat dit déjà la nature, et une
   bordure découperait un bloc de plusieurs jours en N pastilles alors que le
   filet y court sans rupture. Le pourquoi est dans l'infobulle, jamais dans
   la seule couleur. */
/* Le filet d'alerte s'ajoute au liseré, il ne le remplace pas : box-shadow est
   une seule propriété, donc les deux ombres sont déclarées ensemble. Les
   écraser ferait disparaître le liseré sur les pastilles marquées — soit
   exactement celles qu'on veut voir. */
.pl-tag.is-marque {
  box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 30%, transparent),
              inset 2px 0 0 var(--warn);
}
.pl-swatch.is-marque { box-shadow: inset 2px 0 0 var(--warn); }

/* Même marque dans une liste CRUD (clé 'marque' d'un objet de schema.php) :
   le filet sur la première cellule, comme is-today sur le planning, et la
   raison en infobulle sur la ligne. */
tr.is-marque > td:first-child { box-shadow: inset 3px 0 0 var(--warn); }
tr.is-marque { cursor: help; }

/* « pointer » et non « grab » : la main ouverte de grab a son point actif au
   milieu d'une main large, donc on ne sait pas laquelle de deux pastilles
   voisines est visée. La main qui pointe a son point actif au bout de l'index.
   Le glisser-déposer fonctionne de la même façon avec l'une ou l'autre. */
.pl-tag[data-plan-glisser] { cursor: pointer; }
/* Et surtout : la pastille pointée se DÉSIGNE elle-même. Un curseur, même
   précis, laisse deviner ; un liseré franc sous le pointeur répond. C'est la
   vraie réponse à « laquelle des deux est pointée ». */
.pl-tag[data-plan-menu]:hover,
.pl-tag[data-plan-glisser]:hover {
  box-shadow: inset 0 0 0 2px currentColor;
}
.pl-tag.is-marque[data-plan-menu]:hover,
.pl-tag.is-marque[data-plan-glisser]:hover {
  box-shadow: inset 0 0 0 2px currentColor, inset 2px 0 0 var(--warn);
}
.pl-tag.is-glisse { opacity: .4; }
.pl-table td.is-cible { box-shadow: inset 0 0 0 2px var(--accent); }

/* Rendu Markdown (inc/markdown.php) : les mêmes jetons que le reste, pour
   qu'un fichier de spécification suive la charte sans style dédié. */
.doc.md > h2 { font-size: var(--fs-h1); margin-bottom: 2px; }
.doc.md > h3 {
  font-size: var(--fs-h2); margin-top: 14px;
  border-top: 1px solid var(--line-soft); padding-top: 11px;
}
.doc.md > h3:first-of-type { border-top: 0; padding-top: 0; }
.doc.md > h4 { font-size: var(--fs-h2); color: var(--muted); margin-top: 11px; }
.md-p { font-size: var(--fs-base); margin-top: 6px; max-width: 86ch; }
.md-list { font-size: var(--fs-base); margin-top: 6px; padding-left: 22px; max-width: 86ch; }
.md-list li { padding: 2px 0; }
.md-list strong { font-weight: 600; }
.md-quote {
  margin-top: 10px; padding: 7px 12px; max-width: 86ch;
  border-left: 3px solid var(--accent);
  background: var(--surface-muted, var(--line-soft));
  font-size: var(--fs-base);
}
.md-rule { border: 0; border-top: 1px solid var(--line); margin: 14px 0; }
.md-code {
  margin-top: 10px; padding: 9px 12px; overflow-x: auto;
  background: var(--line-soft); border-radius: var(--radius);
  font-family: var(--font-num); font-size: var(--fs-table); line-height: 1.45;
}
.md-code code { background: none; padding: 0; }
/* Un bloc « lang-mermaid » EST un bloc de code jusqu'à ce que Mermaid le rende,
   et c'est volontaire : sans JavaScript, ou le temps du chargement, le schéma
   reste lisible en texte. Mermaid pose data-processed sur l'élément qu'il a
   remplacé par un SVG — à partir de là, le chrome du code n'a plus de sens, et
   un fond gris derrière un schéma qui porte le sien ferait une boîte dans une
   boîte. */
.md-code.lang-mermaid[data-processed] {
  background: none; padding: 0; overflow-x: auto;
  font-family: var(--font-body); line-height: normal;
}
.md-code.lang-mermaid[data-processed] svg { display: block; max-width: 100%; height: auto; }
.md-table { margin-top: 8px; }
.md-table th { text-align: left; }
.md-table td {
  text-align: left; font-family: var(--font-body); font-size: 13px;
  vertical-align: top; max-width: 46ch;
}
.doc-source {
  font-size: var(--fs-small); color: var(--muted);
  margin-top: 12px; padding-top: 8px; border-top: 1px solid var(--line-soft);
}

/* Swagger UI est intégré en iframe, pas en ligne : socle.css porte des règles
   sur button, input, table, th et td qui casseraient ses composants. La page
   documentation/swagger.php est donc servie nue, et isolée ici. */
.swagger-frame {
  display: block; width: 100%; height: calc(100vh - 210px); min-height: 460px;
  border: 0; background: var(--surface);
}

/* =====================================================================
   Comptes : menu du compte, écran de connexion, formulaires posés.
   Aucune couleur littérale ici — tout vient de tokens.css.
   ===================================================================== */

/* Menu du compte, tout à droite du bandeau. C'est un <details> : le
   navigateur sait l'ouvrir, le fermer et l'annoncer, donc pas une ligne de
   JavaScript. On masque le marqueur triangulaire par défaut, qui n'a pas sa
   place à côté d'une pastille d'initiales. */
.usermenu { position: relative; }
.usermenu > summary { list-style: none; cursor: pointer; display: flex; align-items: center; }
.usermenu > summary::-webkit-details-marker { display: none; }
.um-pastille {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--accent); color: var(--rail);
  font-family: var(--font-dense); font-size: var(--fs-small); font-weight: 600;
  letter-spacing: .02em;
}
.usermenu > summary:hover .um-pastille { box-shadow: 0 0 0 2px var(--rail-fg); }
.um-panneau {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 60;
  min-width: 190px; padding: 4px;
  background: var(--surface); color: var(--fg);
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--ombre-volet);
}
.um-tete {
  display: flex; flex-direction: column; gap: 1px;
  padding: 6px 9px 8px; border-bottom: 1px solid var(--line-soft); margin-bottom: 4px;
}
.um-tete span { font-size: var(--fs-small); color: var(--muted); }
.um-panneau a,
.um-panneau button {
  display: block; width: 100%; text-align: left;
  padding: 6px 9px; border: 0; border-radius: 3px;
  background: none; color: var(--fg); font: inherit; cursor: pointer;
  text-decoration: none;
}
.um-panneau a:hover,
.um-panneau button:hover { background: var(--brand-soft); }
.um-panneau form { margin: 0; }

/* Écran de connexion : page autonome, sans tiroir ni bandeau — ils n'ont
   aucun sens avant de savoir qui est là. */
.page-connexion { background: var(--rail); }
.connexion {
  min-height: 100vh; display: flex; align-items: center; justify-content: center;
  padding: 24px 16px;
}
.connexion-carte { width: 100%; max-width: 360px; padding: 20px; }
.connexion-tete { margin-bottom: 14px; }
.connexion-tete h1 { font-size: var(--fs-h1); margin: 0 0 3px; }
.connexion-tete p { margin: 0; font-size: var(--fs-small); color: var(--muted); }
.connexion-pied { margin-top: 14px; display: flex; justify-content: flex-end; }
.connexion-erreur,
.form-erreur {
  margin: 0 0 10px; padding: 7px 10px; border-radius: 3px;
  font-size: var(--fs-small);
  color: var(--danger); background: var(--surface);
  box-shadow: inset 3px 0 0 var(--danger), inset 0 0 0 1px var(--line);
}
.form-ok {
  margin: 0 0 10px; padding: 7px 10px; border-radius: 3px;
  font-size: var(--fs-small);
  color: var(--pos); background: var(--surface);
  box-shadow: inset 3px 0 0 var(--pos), inset 0 0 0 1px var(--line);
}

/* Formulaire posé dans une carte, et non dans une modale : le profil et la
   restauration ne sont pas des lignes de liste, ils n'ont pas de liste. */
.form-pose { margin-top: 2px; }
.form-aide { margin: 0 0 12px; font-size: var(--fs-small); color: var(--muted); max-width: 70ch; }
.form-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }
.danger-note { color: var(--danger); }
.btn-danger { background: var(--danger); }
.btn-lien {
  display: inline-block; padding: 5px 11px; border-radius: 3px;
  background: var(--brand); color: #fff; text-decoration: none; font-size: var(--fs-base);
}
.btn-lien:hover { background: var(--brand-strong); }

/* Champ de liaison : des cases à cocher, parce qu'une liste à choix multiple
   demande de garder une touche enfoncée, ce que personne ne devine. */
.field-coches { display: flex; flex-wrap: wrap; gap: 4px 14px; }
.coche { display: inline-flex; align-items: center; gap: 5px; font-size: var(--fs-base); }
.coche input { margin: 0; }

/* Rôle requis par une entrée, dans la structure des menus engendrée. Une
   pastille discrète : c'est une contrainte, pas le sujet de la ligne. Les
   deux rôles ouverts — tout compte connecté, sans connexion — se distinguent,
   parce que ce sont eux qu'on relit quand on cherche une porte ouverte. */
.doc-role {
  font-family: var(--font-dense); font-size: var(--fs-small);
  padding: 1px 6px; border-radius: 9px; white-space: nowrap;
  background: var(--line-soft); color: var(--muted);
}
.doc-role.is-ouvert { background: var(--brand-soft); color: var(--brand-strong); }

/* Message repris après une redirection (cf. render_flash() dans inc/auth.php).

   C'est un <dialog> NON modal : il raconte ce qui vient de se passer, il ne
   demande pas d'arbitrage, donc il n'a pas à bloquer l'écran sur lequel on
   vient d'arriver. Fermé par <form method="dialog">, donc sans JavaScript.

   display:flex est porté par [open] et non par .flash : une déclaration
   d'auteur l'emporte sur la feuille du navigateur quelle que soit la
   spécificité, et un .flash { display: flex } nu annulerait le
   dialog:not([open]) { display: none } du navigateur — le message resterait
   donc affiché après sa fermeture.

   z-index sous 60, celui du volet du menu du compte : un message informatif
   ne passe pas devant un menu qu'on vient d'ouvrir. */
.flash {
  position: fixed; left: 50%; top: calc(var(--h-appbar) + 10px);
  transform: translateX(-50%);
  z-index: 50;
  max-width: min(74ch, calc(100vw - 32px));
  margin: 0; padding: 9px 12px;
  border: 0; border-radius: var(--radius);
  background: var(--surface); color: var(--fg);
  font-size: var(--fs-base); line-height: 1.35;
  box-shadow: inset 3px 0 0 var(--warn), inset 0 0 0 1px var(--line), var(--ombre-volet);
}
.flash[open] {
  display: flex; align-items: flex-start; gap: 16px;
  animation: flash-sortie 600ms ease-in 5s forwards;
}

/* Le message s'efface seul 5 s après son affichage, sans JavaScript — mais
   PAS pendant qu'on le lit : le survol et le focus clavier suspendent
   l'animation, et elle reprend quand on s'en éloigne. Sans cette pause, un
   message disparaîtrait sous les yeux de qui vient d'y porter le curseur, et
   le bouton « Fermer » se déroberait au moment du clic.

   visibility est posée à part, à 99 % puis à 100 % : elle ne s'interpole pas
   comme une opacité, et la déclarer d'emblée ferait sauter le message au lieu
   de le laisser s'estomper. À la fin, `hidden` le retire pour de bon — du
   dessin, du survol et de l'arbre d'accessibilité — là où une opacité nulle
   l'aurait laissé capter les clics. */
@keyframes flash-sortie {
  from { opacity: 1; visibility: visible; }
  99%  { opacity: 0; visibility: visible; }
  to   { opacity: 0; visibility: hidden; }
}
.flash[open]:hover,
.flash[open]:focus-within { animation-play-state: paused; }
.flash p { margin: 0; }
.flash form { margin: 0; flex: 0 0 auto; }
.flash button {
  background: transparent; color: var(--brand);
  padding: 2px 4px; font-size: var(--fs-small);
}
.flash button:hover { background: var(--brand-soft); }
.flash.is-info { box-shadow: inset 3px 0 0 var(--brand), inset 0 0 0 1px var(--line), var(--ombre-volet); }
.flash.is-ok { box-shadow: inset 3px 0 0 var(--pos), inset 0 0 0 1px var(--line), var(--ombre-volet); }

/* Bouton « copier » d'un en-tête de colonne du planning. Les règles globales
   sur `button` le feraient ressembler à une action de formulaire : il est
   remis à plat ici, parce que c'est une commodité posée contre un libellé, pas
   un bouton de la page. Il ne prend donc ni fond ni bordure, et son trait
   suit --muted comme le reste du chrome. */
.pl-res { display: inline-flex; align-items: center; gap: 4px; }
.pl-copie {
  background: none; border: 0; padding: 0; margin: 0;
  display: inline-flex; align-items: center;
  color: var(--muted); cursor: pointer;
  /* L'en-tête est en Barlow Semi Condensed : sans cette remise à zéro, la
     hauteur de ligne du bouton décalerait le trigramme d'un pixel. */
  line-height: 1;
}
.pl-copie svg { width: 13px; height: 13px; }
.pl-copie:hover { color: var(--brand); background: none; }
/* Confirmation de copie : la couleur sémantique du positif, 1,5 s, posée par
   socle.js. Pas de texte — il élargirait l'en-tête de colonne le temps de
   s'afficher, et les colonnes du planning ne doivent pas bouger. */
.pl-copie.is-copie { color: var(--pos); }

/* Adresse d'abonnement sur l'écran du profil : elle doit être lisible
   caractère par caractère, donc en chiffres monospace, et elle est longue,
   donc elle se coupe au besoin plutôt que d'élargir la carte. */
.abonnement-url {
  font-family: var(--font-num); font-size: var(--fs-small);
  word-break: break-all; color: var(--muted);
}

/* Un message d'INFORMATION n'est pas une alerte : la gouttière passe au bleu
   des actions. Le ton par défaut, 'alerte', garde --warn, posé par la règle de
   base ci-dessus. box-shadow étant une seule propriété, les trois ombres se
   redéclarent ensemble — en écraser une les ferait toutes disparaître, comme
   pour les pastilles marquées du planning. */
.flash.is-info {
  box-shadow: inset 3px 0 0 var(--brand), inset 0 0 0 1px var(--line), var(--ombre-volet);
}

/* Le même « copier » en bouton de formulaire (écran du profil) : il garde
   l'habillage global des `button` et ne reçoit que la confirmation de copie,
   posée par socle.js comme sur celui du planning. */
.btn-copie.is-copie { background: var(--pos); }
