/* Feuille de style de la console — design system (docs/FRONTEND-DESIGN-SYSTEM.md).
 *
 * CE QUI A CHANGÉ, ET POURQUOI
 *
 * L'ancienne feuille n'avait pas de langage de composants : neuf valeurs de
 * `border-radius`, deux systèmes de pastilles concurrents, une barre de
 * navigation recopiée dans six pages. Chaque écran était dessiné à part, donc
 * chaque écran vieillissait à part.
 *
 * Tout part désormais de TOKENS (les variables de `:root`). Un composant ne
 * porte jamais une couleur ni un rayon en dur : il référence un token. Changer
 * la palette, c'est changer `:root` — et rien d'autre.
 *
 * THÈME. Le design system est clair ; l'ancienne console était sombre, et pour
 * une raison réelle : un poste de supervision tourne en continu. Les deux
 * cohabitent donc — clair par défaut (§11 du DS), sombre sous `.sombre`, au
 * choix de l'utilisateur et mémorisé par navigateur. Les deux palettes sont
 * vérifiées au contraste WCAG AA.
 *
 * LA COULEUR NE PORTE JAMAIS L'INFORMATION SEULE. Règle héritée et conservée :
 * chaque pastille de verdict porte son libellé écrit, lisible en daltonisme
 * comme à l'impression en noir et blanc.
 */

/* ==========================================================================
   1. Police — servie localement (voir web/vendor/README.md)
   ========================================================================== */

/* Chargée depuis NOTRE serveur et non depuis fonts.googleapis.com : sur un
   réseau filtrant, une police distante ne se charge pas. Ici la page se
   contente alors du repli système, sans jamais rester nue. */
@font-face {
  font-family: "Public Sans";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("/vendor/fonts/public-sans-300.woff2") format("woff2");
}
@font-face {
  font-family: "Public Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/vendor/fonts/public-sans-400.woff2") format("woff2");
}
@font-face {
  font-family: "Public Sans";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/vendor/fonts/public-sans-500.woff2") format("woff2");
}
@font-face {
  font-family: "Public Sans";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/vendor/fonts/public-sans-600.woff2") format("woff2");
}
@font-face {
  font-family: "Public Sans";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/vendor/fonts/public-sans-700.woff2") format("woff2");
}

/* ==========================================================================
   2. Tokens — thème clair (défaut)
   ========================================================================== */

:root {
  color-scheme: light;

  /* Fondations */
  --background: #fdfdfd;
  --foreground: #2f2b3d;
  --card: #ffffff;
  --card-foreground: #2f2b3d;

  /* Marque — teal */
  --primary: #113b3a;
  --primary-foreground: #ffffff;
  --accent: #113b3a;
  --accent-secondary: #2a7573;

  /* Neutres */
  --secondary: #f1f1f2;
  --secondary-foreground: #2f2b3d;
  --muted: #f5f5f5;
  --muted-foreground: #6b6e7b;

  /* Sémantiques. `--warning` (« à traiter ») n'est PAS un rouge : ce n'est pas
     une erreur mais une action attendue d'un humain.

     TEINTES ASSOMBRIES PAR RAPPORT AU DESIGN SYSTEM, ET C'EST DÉLIBÉRÉ.
     Le DS donne #FF4C51, #28C76F, #00BAD1 et #FF9F43 : de belles couleurs, mais
     leur contraste sur une carte blanche tombe entre 2,04 et 3,28 — sous le
     seuil WCAG AA de 4,5 exigé pour du texte. Or ces couleurs portent ici des
     VERDICTS (« Refusé », « À traiter ») lus par un superviseur : les rendre
     illisibles serait perdre l'information au moment où elle compte.
     On garde donc la teinte du DS, assombrie jusqu'à passer AA. Les valeurs
     vives d'origine restent utilisées telles quelles en fond (`*-bg`) et en
     thème sombre, où le rapport s'inverse. */
  --destructive: #d92d33;  /* 4,80 sur blanc */
  --destructive-foreground: #ffffff;
  --success: #15803d;      /* 5,02 */
  --info: #0e7490;         /* 5,36 */
  --warning: #a35b00;      /* 5,18 */

  /* Fonds teintés des états — assez pâles pour porter du texte foncé */
  --success-bg: #eaf7f0;
  --destructive-bg: #fdecec;
  --warning-bg: #fdf3e5;
  --info-bg: #e6f7fa;
  --primary-bg: #eaf0ef;

  /* Bordures et champs */
  --border: #e6e6e8;
  --input: #e6e6e8;
  --ring: #113b3a;

  /* Rayons — UNE échelle, dérivée d'une seule base */
  --radius: 0.375rem;
  --radius-sm: 0.25rem;
  --radius-md: 0.375rem;
  --radius-lg: 0.5rem;
  --radius-xl: 0.75rem;
  --radius-full: 999px;

  /* Ombres — élévation Material */
  --shadow-sm: 0 2px 6px rgba(47, 43, 61, 0.1);
  --shadow-md: 0 4px 16px rgba(47, 43, 61, 0.14);
  --shadow-lg: 0 6px 24px rgba(47, 43, 61, 0.16);
  --shadow-xl: 0 8px 32px rgba(47, 43, 61, 0.18);
  --shadow-accent: 0 4px 14px rgba(17, 59, 58, 0.2);

  /* Typographie */
  --font-body: "Public Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* Espacement — échelle de 4 px */
  --e1: 0.25rem;
  --e2: 0.5rem;
  --e3: 0.75rem;
  --e4: 1rem;
  --e5: 1.25rem;
  --e6: 1.5rem;
  --e8: 2rem;
  --e10: 2.5rem;
  --e12: 3rem;

  /* Ossature */
  --barre-l: 260px;      /* barre latérale déployée */
  --entete-h: 56px;      /* en-tête collant */
  --transition: 200ms ease-out;
}

/* ==========================================================================
   3. Tokens — thème sombre
   ========================================================================== */

/* Posé sur <html> par le sélecteur de thème (app.js). Seuls les tokens sont
   redéfinis : aucun composant ne connaît le thème actif. */
.sombre {
  color-scheme: dark;

  --background: #0f1419;
  --foreground: #e6e4f0;
  --card: #192028;
  --card-foreground: #e6e4f0;

  /* Le teal foncé du thème clair serait illisible sur fond sombre : la marque
     s'éclaircit pour garder son contraste, comme le prévoit le DS (§11). */
  --primary: #4db6ac;
  --primary-foreground: #06201e;
  --accent: #2a7573;
  --accent-secondary: #4db6ac;

  --secondary: #1e262f;
  --secondary-foreground: #e6e4f0;
  --muted: #1e262f;
  --muted-foreground: #a3a8b8;

  --destructive: #ff6b70;
  --destructive-foreground: #2a0b0d;
  --success: #4ade80;
  --info: #38bdf8;
  --warning: #fbbf24;

  --success-bg: rgba(74, 222, 128, 0.14);
  --destructive-bg: rgba(255, 107, 112, 0.14);
  --warning-bg: rgba(251, 191, 36, 0.15);
  --info-bg: rgba(56, 189, 248, 0.14);
  --primary-bg: rgba(77, 182, 172, 0.14);

  --border: #2a3240;
  --input: #2a3240;
  --ring: #4db6ac;

  --shadow-sm: 0 2px 6px rgba(0, 0, 0, 0.3);
  --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.35);
  --shadow-lg: 0 6px 24px rgba(0, 0, 0, 0.4);
  --shadow-xl: 0 8px 32px rgba(0, 0, 0, 0.45);
  --shadow-accent: 0 4px 14px rgba(77, 182, 172, 0.25);
}

/* ==========================================================================
   4. Base
   ========================================================================== */

*,
*::before,
*::after {
  box-sizing: border-box;
}

/* `hidden` DOIT l'emporter sur nos `display`.
 *
 * La règle navigateur `[hidden] { display: none }` a la même spécificité qu'un
 * sélecteur de classe : le `display: flex` de `.bandeau` la battait par simple
 * ordre de déclaration. Conséquence observée en jouant la page : un bandeau
 * d'erreur VIDE restait affiché en permanence — un cadre rouge et une icône
 * sans texte, alors qu'aucune erreur n'était survenue. C'est l'exact contraire
 * de ce qu'on attend d'un signal d'alerte, et cela touchait tout composant
 * masqué par attribut (`.bandeau`, `.compteur`, `.conversation`).
 *
 * `!important` est ici légitime : `hidden` est une intention sans ambiguïté,
 * aucune mise en forme ne doit pouvoir la contredire. */
[hidden] {
  display: none !important;
}

html {
  /* Le thème se pose sur <html> : sans cette couleur, un rechargement en mode
     sombre laisse voir un flash blanc le temps du premier rendu. */
  background: var(--background);
}

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 0.875rem;
  line-height: 1.55;
  background: var(--background);
  color: var(--foreground);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1,
h2,
h3 {
  margin: 0;
  line-height: 1.25;
  font-weight: 600;
  letter-spacing: -0.01em;
}

h1 { font-size: 1.5rem; }
h2 { font-size: 1.125rem; }
h3 { font-size: 1rem; }

a { color: var(--primary); }

/* Le focus visible est une aide à la navigation clavier, pas une décoration :
   il est posé une fois, ici, pour TOUT élément focusable. */
:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* Réservé aux lecteurs d'écran : un intitulé lu mais non affiché. */
.lecteur-seul {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Premier arrêt de tabulation : sauter la navigation pour atteindre le
   contenu. Invisible tant qu'il n'a pas le focus. */
.saut-contenu {
  position: absolute;
  top: var(--e2);
  left: var(--e2);
  z-index: 100;
  padding: var(--e2) var(--e4);
  background: var(--primary);
  color: var(--primary-foreground);
  border-radius: var(--radius-md);
  font-weight: 500;
  transform: translateY(-200%);
  transition: transform var(--transition);
}
.saut-contenu:focus {
  transform: translateY(0);
}

/* ==========================================================================
   5. Icônes
   ========================================================================== */

/* Un <svg><use href="/vendor/icones.svg#nom"/></svg>. Le trait est porté ICI
   et non dans le sprite : une icône prend la couleur de son contexte. */
.ico {
  width: 1.125rem;
  height: 1.125rem;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.ico-sm { width: 1rem; height: 1rem; }
.ico-lg { width: 1.5rem; height: 1.5rem; }
.ico-xl { width: 2.5rem; height: 2.5rem; stroke-width: 1.5; }

/* ==========================================================================
   6. Ossature — barre latérale, en-tête, contenu
   ========================================================================== */

.appli {
  min-height: 100vh;
}

/* --- Barre latérale ------------------------------------------------------ */

.barre-laterale {
  position: fixed;
  inset: 0 auto 0 0;
  width: var(--barre-l);
  display: flex;
  flex-direction: column;
  background: var(--card);
  border-right: 1px solid var(--border);
  z-index: 50;
  transition: transform 250ms ease-in-out;
}

.marque {
  display: flex;
  align-items: center;
  gap: var(--e3);
  height: var(--entete-h);
  padding: 0 var(--e4);
  border-bottom: 1px solid var(--border);
  flex: none;
}

.marque-logo {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-md);
  background: var(--primary);
  color: var(--primary-foreground);
  flex: none;
}

.marque-texte {
  font-weight: 600;
  font-size: 0.9375rem;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.navigation {
  flex: 1;
  overflow-y: auto;
  padding: var(--e4) var(--e3);
  display: flex;
  flex-direction: column;
  gap: var(--e1);
}

.nav-titre {
  padding: var(--e2) var(--e3) var(--e1);
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted-foreground);
}

/* 44 px de haut : c'est le minimum tactile. L'ancienne navigation tenait sur
   33 px, inatteignable au doigt sans viser. */
.nav-lien {
  display: flex;
  align-items: center;
  gap: var(--e3);
  min-height: 44px;
  padding: var(--e2) var(--e3);
  border-radius: var(--radius-md);
  color: var(--muted-foreground);
  text-decoration: none;
  font-weight: 500;
  transition: background var(--transition), color var(--transition);
}

.nav-lien:hover {
  background: var(--muted);
  color: var(--foreground);
}

/* L'onglet courant se lit à trois signaux — fond, couleur, et le libellé qui
   reste écrit : la couleur seule ne dit jamais où l'on est. */
.nav-lien[aria-current="page"] {
  background: var(--primary-bg);
  color: var(--primary);
  font-weight: 600;
}

.nav-pied {
  flex: none;
  padding: var(--e3);
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: var(--e1);
}

.nav-lien-danger:hover {
  background: var(--destructive-bg);
  color: var(--destructive);
}

/* --- En-tête ------------------------------------------------------------- */

.entete {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: var(--e3);
  height: var(--entete-h);
  padding: 0 var(--e4);
  background: color-mix(in srgb, var(--card) 85%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border);
}

.entete-titre {
  font-size: 0.9375rem;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.entete-actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--e2);
}

/* --- Zone de contenu ----------------------------------------------------- */

.contenu {
  padding-left: var(--barre-l);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.page {
  flex: 1;
  width: 100%;
  max-width: 90rem;
  margin: 0 auto;
  padding: var(--e6) var(--e6) var(--e10);
  display: flex;
  flex-direction: column;
  gap: var(--e5);
}

/* --- En-tête de page ----------------------------------------------------- */

.page-entete {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--e4);
}

.page-titre {
  font-size: 1.5rem;
}

.page-sous-titre {
  margin: var(--e2) 0 0;
  color: var(--muted-foreground);
  max-width: 65ch;
}

.etiquette-section {
  display: inline-flex;
  align-items: center;
  gap: var(--e2);
  margin-bottom: var(--e3);
  padding: var(--e1) var(--e3);
  border: 1px solid color-mix(in srgb, var(--primary) 30%, transparent);
  background: var(--primary-bg);
  border-radius: var(--radius-md);
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--primary);
}

.point-pulse {
  width: 0.375rem;
  height: 0.375rem;
  border-radius: var(--radius-full);
  background: currentColor;
  animation: pulsation 2s ease-in-out infinite;
}

@keyframes pulsation {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.35); opacity: 0.6; }
}

/* ==========================================================================
   7. Boutons
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--e2);
  min-height: 38px;
  padding: var(--e2) var(--e4);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  font: inherit;
  font-weight: 500;
  line-height: 1.2;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--transition), border-color var(--transition),
              color var(--transition), box-shadow var(--transition),
              transform 100ms ease-out;
}

.btn:active:not(:disabled) { transform: scale(0.98); }

.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.btn-primaire {
  background: var(--primary);
  color: var(--primary-foreground);
  border-color: var(--primary);
}
.btn-primaire:hover:not(:disabled) {
  background: color-mix(in srgb, var(--primary) 88%, black);
  box-shadow: var(--shadow-accent);
}

.btn-secondaire {
  background: var(--secondary);
  color: var(--secondary-foreground);
  border-color: var(--secondary);
}
.btn-secondaire:hover:not(:disabled) {
  background: color-mix(in srgb, var(--secondary) 85%, var(--foreground) 4%);
}

.btn-contour {
  background: transparent;
  color: var(--foreground);
  border-color: var(--border);
}
.btn-contour:hover:not(:disabled) {
  background: var(--muted);
  border-color: color-mix(in srgb, var(--primary) 35%, transparent);
}

.btn-fantome {
  background: transparent;
  color: var(--muted-foreground);
}
.btn-fantome:hover:not(:disabled) {
  background: var(--muted);
  color: var(--foreground);
}

.btn-danger {
  background: var(--destructive);
  color: var(--destructive-foreground);
  border-color: var(--destructive);
}
.btn-danger:hover:not(:disabled) {
  background: color-mix(in srgb, var(--destructive) 88%, black);
}

.btn-sm {
  min-height: 32px;
  padding: var(--e1) var(--e3);
  font-size: 0.8125rem;
}

.btn-lg {
  min-height: 44px;
  padding: var(--e3) var(--e6);
  font-size: 0.9375rem;
}

/* Bouton réduit à une icône : le nom de l'action passe alors par `aria-label`,
   sans quoi il n'existe plus pour un lecteur d'écran. */
.btn-icone {
  min-height: 38px;
  width: 38px;
  padding: 0;
}

/* Bouton qui a l'apparence d'un lien — pour une action secondaire dans un
   tableau, où un vrai bouton alourdirait chaque ligne. */
.btn-lien {
  background: none;
  border: none;
  padding: var(--e1) 0;
  font: inherit;
  font-weight: 500;
  color: var(--primary);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.btn-lien:hover { opacity: 0.75; }

/* Attente : le bouton garde sa largeur (pas de saut) et dit ce qu'il fait. */
.btn[aria-busy="true"] { pointer-events: none; opacity: 0.75; }

.tourniquet {
  width: 1rem;
  height: 1rem;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: var(--radius-full);
  animation: rotation 700ms linear infinite;
  flex: none;
}

@keyframes rotation {
  to { transform: rotate(360deg); }
}

/* ==========================================================================
   8. Cartes
   ========================================================================== */

.carte {
  background: var(--card);
  color: var(--card-foreground);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

.carte-entete {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--e4);
  padding: var(--e5) var(--e6);
  border-bottom: 1px solid var(--border);
}

.carte-titre {
  display: flex;
  align-items: center;
  gap: var(--e2);
  font-size: 1rem;
  font-weight: 600;
}

.carte-corps { padding: var(--e6); }

.carte-corps-compact { padding: var(--e4) var(--e5); }

/* Une carte sans marge intérieure : pour un tableau, qui porte la sienne. */
.carte-nue { overflow: hidden; }

.aide {
  color: var(--muted-foreground);
  font-size: 0.8125rem;
  margin: var(--e2) 0 0;
  max-width: 75ch;
}

.aide code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  padding: 0.05rem 0.3rem;
  border-radius: var(--radius-sm);
  background: var(--muted);
  border: 1px solid var(--border);
}

/* ==========================================================================
   9. Compteurs
   ========================================================================== */

.compteurs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: var(--e4);
}

.compteur {
  display: flex;
  align-items: flex-start;
  gap: var(--e3);
  padding: var(--e5);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--transition);
}

.compteur-icone {
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: var(--radius-md);
  background: var(--muted);
  color: var(--muted-foreground);
  flex: none;
}

.compteur-valeur {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}

.compteur-libelle {
  color: var(--muted-foreground);
  font-size: 0.8125rem;
}

/* Un compteur à zéro reste NEUTRE : « 0 refusé » est une bonne nouvelle, le
   peindre en rouge attirerait l'œil à tort. La classe est posée par le JS. */
.compteur.zero .compteur-valeur { color: var(--muted-foreground); }

.compteur[data-ton="attente"] .compteur-valeur { color: var(--warning); }
.compteur[data-ton="attente"] .compteur-icone {
  background: var(--warning-bg);
  color: var(--warning);
}
.compteur[data-ton="ok"] .compteur-valeur { color: var(--success); }
.compteur[data-ton="ok"] .compteur-icone {
  background: var(--success-bg);
  color: var(--success);
}
.compteur[data-ton="refus"] .compteur-valeur { color: var(--destructive); }
.compteur[data-ton="refus"] .compteur-icone {
  background: var(--destructive-bg);
  color: var(--destructive);
}

/* Ne s'allume que s'il y a réellement quelque chose à traiter. */
.compteur.urgent {
  border-color: var(--warning);
  background: var(--warning-bg);
}

/* ==========================================================================
   10. Bandeaux
   ========================================================================== */

.bandeau {
  display: flex;
  align-items: flex-start;
  gap: var(--e3);
  padding: var(--e4);
  border: 1px solid;
  border-radius: var(--radius-lg);
  font-size: 0.875rem;
}

.bandeau .ico { margin-top: 0.1rem; }

.bandeau-alerte {
  background: var(--warning-bg);
  border-color: color-mix(in srgb, var(--warning) 40%, transparent);
  color: var(--warning);
}

.bandeau-erreur {
  background: var(--destructive-bg);
  border-color: color-mix(in srgb, var(--destructive) 40%, transparent);
  color: var(--destructive);
}

.bandeau-info {
  background: var(--info-bg);
  border-color: color-mix(in srgb, var(--info) 40%, transparent);
  color: var(--info);
}

.bandeau-succes {
  background: var(--success-bg);
  border-color: color-mix(in srgb, var(--success) 40%, transparent);
  color: var(--success);
}

/* Le texte du bandeau reprend la couleur du foreground quand il est long :
   un paragraphe entier en teinte d'alerte se lit mal. */
.bandeau-corps { color: var(--foreground); }
.bandeau-titre { font-weight: 600; }

/* ==========================================================================
   11. Barre d'outils — filtres et recherche
   ========================================================================== */

.outils {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e3);
}

.groupe-filtres {
  display: inline-flex;
  padding: 0.1875rem;
  gap: 0.1875rem;
  background: var(--muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

/* 36 px : sous les 44 px du tactile pur, mais ces boutons sont groupés et
   accompagnés — le compromis est assumé pour ne pas faire enfler la barre.
   En dessous de 640 px ils passent à 44 px (voir le responsive). */
.filtre {
  min-height: 36px;
  padding: var(--e1) var(--e3);
  border: none;
  background: transparent;
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--muted-foreground);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--transition), color var(--transition);
}

.filtre:hover { color: var(--foreground); }

/* `aria-pressed` porte l'état pour le lecteur d'écran ; la CSS ne fait que le
   rendre visible. Un seul état, une seule source. */
.filtre[aria-pressed="true"] {
  background: var(--card);
  color: var(--primary);
  box-shadow: var(--shadow-sm);
}

.champ-recherche {
  position: relative;
  flex: 1 1 18rem;
  min-width: 0;
  max-width: 26rem;
}

.champ-recherche .ico {
  position: absolute;
  left: var(--e3);
  top: 50%;
  transform: translateY(-50%);
  color: var(--muted-foreground);
  pointer-events: none;
}

/* La place réservée à la loupe est posée plus bas, juste après la règle
   générique des champs de saisie (chercher « Place pour la loupe »). Elle ne
   peut pas vivre ici : `input[type="search"]` est déclaré après, et à
   spécificité égale c'est la dernière déclaration qui gagne — le padding
   retombait alors à 12 px et le texte saisi passait SOUS l'icône. */

.maj {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: var(--e2);
  color: var(--muted-foreground);
  font-size: 0.75rem;
  white-space: nowrap;
}

/* ==========================================================================
   12. Formulaires
   ========================================================================== */

.champ {
  display: flex;
  flex-direction: column;
  gap: var(--e2);
}

.champ + .champ { margin-top: var(--e5); }

.champ label,
.etiquette {
  font-weight: 500;
  font-size: 0.8125rem;
  color: var(--foreground);
}

input[type="text"],
input[type="search"],
input[type="password"],
input[type="number"],
textarea,
select {
  width: 100%;
  min-height: 40px;
  padding: var(--e2) var(--e3);
  font: inherit;
  font-size: 0.875rem;
  color: var(--foreground);
  background: var(--background);
  border: 1px solid var(--input);
  border-radius: var(--radius-md);
  transition: border-color var(--transition), box-shadow var(--transition);
}

/* Place pour la loupe du champ de recherche.
 *
 * POSÉE ICI, ET PAS AVEC LE COMPOSANT. La règle générique ci-dessus cible
 * `input[type="search"]` : même spécificité que `.champ-recherche input`
 * (0,1,1), donc à égalité c'est la DERNIÈRE déclaration qui gagne. Écrite plus
 * haut, elle était écrasée et le padding retombait à 12 px — le texte saisi
 * passait alors sous l'icône. Constaté à l'écran, pas déduit. */
.champ-recherche input[type="search"],
.champ-recherche input[type="text"] {
  padding-left: 2.5rem;
}

input::placeholder,
textarea::placeholder { color: var(--muted-foreground); opacity: 0.8; }

input:focus,
textarea:focus,
select:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 18%, transparent);
}

input:disabled,
textarea:disabled {
  background: var(--muted);
  opacity: 0.7;
  cursor: not-allowed;
}

/* Cible tactile : la case elle-même fait 16 px, mais tout le libellé est
   cliquable, ce qui donne une zone confortable. */
.case {
  display: inline-flex;
  align-items: center;
  gap: var(--e2);
  min-height: 40px;
  cursor: pointer;
  user-select: none;
}

.case input[type="checkbox"],
.case input[type="radio"] {
  width: 1.0625rem;
  height: 1.0625rem;
  accent-color: var(--primary);
  cursor: pointer;
  flex: none;
}

/* Choix présenté en cartes cliquables : plus lisible qu'une liste de radios
   nues quand chaque option demande une phrase d'explication. */
.choix {
  display: grid;
  gap: var(--e3);
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}

.choix-option {
  display: flex;
  align-items: flex-start;
  gap: var(--e3);
  padding: var(--e4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--card);
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition);
}

.choix-option:hover { border-color: color-mix(in srgb, var(--primary) 40%, transparent); }

.choix-option:has(input:checked) {
  border-color: var(--primary);
  background: var(--primary-bg);
}

.choix-option input { margin-top: 0.15rem; flex: none; accent-color: var(--primary); }

.choix-titre { font-weight: 600; display: block; }
.choix-detail { color: var(--muted-foreground); font-size: 0.8125rem; }

/* Barre d'actions collante : l'ancienne page posait « Enregistrer » au bas de
   2 200 px de formulaire, donc hors de vue pendant toute la saisie. */
/* Barre d'actions qui reste visible pendant toute la saisie.
 *
 * `margin-top: auto` N'EST PAS COSMÉTIQUE. Le parent `.page` est une colonne
 * flex, et dans une colonne flex un enfant `sticky` ne colle que dans les
 * limites de SA ligne — c'est-à-dire de sa propre hauteur. Sans cette marge
 * automatique qui le repousse en fin de colonne, il se figeait au milieu du
 * document, par-dessus le formulaire : constaté à l'écran sur `/edition`, où
 * « Enregistrer » recouvrait le champ « Première question ». */
.actions-collantes {
  position: sticky;
  bottom: 0;
  z-index: 20;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e3);
  padding: var(--e4) var(--e6);
  /* `auto` en haut : c'est LUI qui repousse la barre en fin de colonne (voir
     le commentaire ci-dessus). Écrit dans le raccourci `margin`, sans quoi une
     déclaration `margin` postérieure le remettrait à zéro — c'est exactement
     ce qui venait d'annuler le correctif. Les marges latérales négatives font
     affleurer la barre aux bords de la page, la marge basse la colle au pied. */
  margin: auto calc(-1 * var(--e6)) calc(-1 * var(--e10));
  background: color-mix(in srgb, var(--card) 92%, transparent);
  backdrop-filter: blur(8px);
  border-top: 1px solid var(--border);
}

.actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e3);
}

/* ==========================================================================
   13. Tableaux
   ========================================================================== */

/* Le tableau défile DANS son cadre : la page, elle, ne défile jamais
   horizontalement. Règle héritée, et vérifiée à 390 px.

   L'OMBRE N'EST PAS DÉCORATIVE. Un tableau de treize colonnes coupé net au
   bord de l'écran ressemble à un tableau de six colonnes : rien ne dit qu'il
   continue, et l'utilisateur ne pense pas à faire défiler ce qu'il croit
   complet. Ces deux dégradés — attachés l'un au cadre, l'autre au contenu —
   s'annulent quand on atteint le bord et ne restent visibles que du côté où il
   reste quelque chose à lire. C'est l'équivalent visuel d'une page cornée. */
.tableau-cadre {
  overflow-x: auto;
  border-radius: var(--radius-lg);
  background:
    linear-gradient(to right, var(--card) 30%, transparent) left / 2.5rem 100% no-repeat local,
    linear-gradient(to left, var(--card) 30%, transparent) right / 2.5rem 100% no-repeat local,
    linear-gradient(to right, rgba(47, 43, 61, 0.16), transparent) left / 1rem 100% no-repeat scroll,
    linear-gradient(to left, rgba(47, 43, 61, 0.16), transparent) right / 1rem 100% no-repeat scroll;
}

/* En thème sombre, une ombre noire sur fond sombre ne se voit pas : on éclaire
   plutôt que d'assombrir. */
.sombre .tableau-cadre {
  background:
    linear-gradient(to right, var(--card) 30%, transparent) left / 2.5rem 100% no-repeat local,
    linear-gradient(to left, var(--card) 30%, transparent) right / 2.5rem 100% no-repeat local,
    linear-gradient(to right, rgba(255, 255, 255, 0.14), transparent) left / 1rem 100% no-repeat scroll,
    linear-gradient(to left, rgba(255, 255, 255, 0.14), transparent) right / 1rem 100% no-repeat scroll;
}

/* La barre de défilement horizontale d'un tableau est une COMMANDE, pas un
   artefact : sur les plateformes qui la masquent par défaut, l'utilisateur
   perd le seul indice restant. On la garde fine mais présente. */
.tableau-cadre { scrollbar-width: thin; }
.tableau-cadre::-webkit-scrollbar { height: 0.5rem; }
.tableau-cadre::-webkit-scrollbar-track { background: var(--muted); }
.tableau-cadre::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--muted-foreground) 40%, transparent);
  border-radius: var(--radius-full);
}
.tableau-cadre::-webkit-scrollbar-thumb:hover {
  background: color-mix(in srgb, var(--muted-foreground) 65%, transparent);
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.8125rem;
}

thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  text-align: left;
  padding: var(--e3) var(--e4);
  background: var(--muted);
  color: var(--muted-foreground);
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  white-space: nowrap;
  border-bottom: 1px solid var(--border);
}

tbody td {
  padding: var(--e3) var(--e4);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
  vertical-align: middle;
}

tbody tr:last-child td { border-bottom: none; }

tbody tr { transition: background var(--transition); }
tbody tr:hover td { background: var(--muted); }

td.mono {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
}

td.date {
  color: var(--muted-foreground);
  font-variant-numeric: tabular-nums;
}

/* Une cellule vide ne se met pas en couleur : un tiret coloré dans la colonne
   « Permis » se lit comme un numéro alors qu'il signale son absence. */
td.vide-cell { color: var(--muted-foreground); }

/* Texte libre : borné, pour ne pas étirer le tableau. Le contenu entier reste
   accessible en infobulle. */
td.tronque {
  max-width: 20rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

td.texte { white-space: normal; max-width: 34rem; line-height: 1.45; }

/* Une ligne en attente reste repérable après un tri ou un filtre. */
tr.statut-a_traiter td:first-child {
  box-shadow: inset 3px 0 0 var(--warning);
}

/* En-tête triable : le tri est une amélioration, l'état est porté par
   `aria-sort` que le lecteur d'écran annonce. */
th.triable {
  cursor: pointer;
  user-select: none;
}
th.triable:hover { color: var(--foreground); }

th.triable .ico {
  width: 0.875rem;
  height: 0.875rem;
  opacity: 0.4;
  vertical-align: -0.15em;
  margin-left: var(--e1);
}
th[aria-sort] { color: var(--primary); }
th[aria-sort] .ico { opacity: 1; }

/* ==========================================================================
   14. Pastilles
   ========================================================================== */

/* UN seul système de pastilles, là où il y en avait deux (`.pastille` et
   `.badge`), avec des rendus différents pour la même notion. */
.pastille {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  padding: 0.15rem var(--e2);
  border: 1px solid transparent;
  border-radius: var(--radius-full);
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.4;
  white-space: nowrap;
}

.pastille-point {
  width: 0.375rem;
  height: 0.375rem;
  border-radius: var(--radius-full);
  background: currentColor;
  flex: none;
}

/* Verdicts. Chaque pastille porte SON LIBELLÉ : la couleur ne dit jamais seule
   de quoi il s'agit. */
.pastille-ok,
.pastille[data-statut="autorise"],
.pastille[data-statut="completed"] {
  background: var(--success-bg);
  color: var(--success);
  border-color: color-mix(in srgb, var(--success) 35%, transparent);
}

.pastille-refus,
.pastille[data-statut="refuse"],
.pastille[data-statut="error"] {
  background: var(--destructive-bg);
  color: var(--destructive);
  border-color: color-mix(in srgb, var(--destructive) 35%, transparent);
}

.pastille-attente,
.pastille[data-statut="a_traiter"],
.pastille[data-statut="abandoned"] {
  background: var(--warning-bg);
  color: var(--warning);
  border-color: color-mix(in srgb, var(--warning) 35%, transparent);
}

/* « Inconnu » n'est pas « faux » : une date absente ou un PDP « N/D » ne vaut
   ni conforme ni refusé — il conduit à un transfert humain. */
.pastille-neutre,
.pastille[data-statut="inconnu"],
.pastille[data-statut="in_progress"] {
  background: var(--muted);
  color: var(--muted-foreground);
  border-color: var(--border);
}

.pastille-info {
  background: var(--info-bg);
  color: var(--info);
  border-color: color-mix(in srgb, var(--info) 35%, transparent);
}

/* Appartenance à un process — volontairement HORS de la palette des verdicts :
   vert/rouge/ambre disent l'issue d'une demande, le process n'est qu'une
   appartenance. */
.pastille-process {
  background: var(--muted);
  color: var(--muted-foreground);
  border-color: var(--border);
  font-weight: 500;
}

/* Nombre d'éléments regroupés sur une ligne (jusqu'à 21 habilitations pour une
   même personne). La pastille dit combien, l'infobulle détaille. */
.jeton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.375rem;
  height: 1.25rem;
  padding: 0 var(--e1);
  margin-right: var(--e2);
  border-radius: var(--radius-full);
  background: var(--muted);
  border: 1px solid var(--border);
  color: var(--muted-foreground);
  font-size: 0.6875rem;
  font-weight: 600;
}

/* ==========================================================================
   15. États — vide, chargement, erreur
   ========================================================================== */

.etat-vide {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--e2);
  padding: var(--e12) var(--e6);
  color: var(--muted-foreground);
}

.etat-vide .ico-xl { color: var(--muted-foreground); opacity: 0.45; }
.etat-vide-titre { font-size: 1rem; font-weight: 600; color: var(--foreground); }
.etat-vide p { margin: 0; max-width: 46ch; font-size: 0.8125rem; }
.etat-vide .btn { margin-top: var(--e3); }

/* Squelette de chargement : montre la FORME de ce qui arrive, au lieu d'un
   « Chargement… » qui ne dit rien du contenu attendu. */
.squelette {
  border-radius: var(--radius-sm);
  background: linear-gradient(
    90deg,
    var(--muted) 25%,
    color-mix(in srgb, var(--muted) 60%, var(--card)) 50%,
    var(--muted) 75%
  );
  background-size: 200% 100%;
  animation: glissement 1.4s ease-in-out infinite;
}

@keyframes glissement {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}

.squelette-ligne { height: 0.75rem; }

/* Ligne d'état d'une action (enregistrement, connexion…). */
.etat {
  display: inline-flex;
  align-items: center;
  gap: var(--e2);
  font-size: 0.8125rem;
  color: var(--muted-foreground);
}
.etat-ok { color: var(--success); }
.etat-erreur { color: var(--destructive); }

/* ==========================================================================
   16. Fil de conversation
   ========================================================================== */

.conversation {
  display: flex;
  flex-direction: column;
  gap: var(--e3);
}

/* Les deux voix se distinguent par un LIBELLÉ, pas seulement par la position :
   lu d'un bloc, un dialogue de dix tours devient illisible. */
.tour {
  max-width: min(46rem, 85%);
  padding: var(--e3) var(--e4);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
}

.tour-agent { align-self: flex-start; border-bottom-left-radius: var(--radius-sm); }

.tour-appelant {
  align-self: flex-end;
  background: var(--primary-bg);
  border-color: color-mix(in srgb, var(--primary) 25%, transparent);
  border-bottom-right-radius: var(--radius-sm);
}

.tour-qui {
  display: flex;
  align-items: center;
  gap: var(--e2);
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted-foreground);
  margin-bottom: var(--e1);
}

.tour-heure { font-weight: 400; letter-spacing: 0; text-transform: none; opacity: 0.8; }

/* Segment encore provisoire (transcription en cours). */
.tour-provisoire { opacity: 0.6; font-style: italic; }

/* Zone de transcription dépliée sous une ligne de tableau. */
tr.ligne-conversation > td {
  background: var(--muted);
  padding: var(--e4) var(--e5);
  white-space: normal;
}

.transcript-boite {
  max-height: 24rem;
  overflow-y: auto;
}

/* ==========================================================================
   17. Boîte de dialogue
   ========================================================================== */

.dialogue {
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
  color: var(--card-foreground);
  box-shadow: var(--shadow-xl);
  max-width: min(32rem, calc(100vw - 2rem));
  width: 100%;
}

.dialogue::backdrop {
  background: rgba(15, 20, 25, 0.5);
  backdrop-filter: blur(2px);
}

.dialogue-entete {
  display: flex;
  align-items: flex-start;
  gap: var(--e3);
  padding: var(--e5) var(--e6) var(--e3);
}

.dialogue-icone {
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--radius-md);
  background: var(--destructive-bg);
  color: var(--destructive);
  flex: none;
}

.dialogue-corps { padding: 0 var(--e6) var(--e5); }
.dialogue-corps p { margin: 0; color: var(--muted-foreground); }

.dialogue-pied {
  display: flex;
  justify-content: flex-end;
  gap: var(--e3);
  padding: var(--e4) var(--e6);
  border-top: 1px solid var(--border);
  background: var(--muted);
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}

/* ==========================================================================
   18. Notifications
   ========================================================================== */

.notifications {
  position: fixed;
  right: var(--e4);
  bottom: var(--e4);
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: var(--e2);
  pointer-events: none;
}

.notification {
  display: flex;
  align-items: flex-start;
  gap: var(--e3);
  min-width: 16rem;
  max-width: min(24rem, calc(100vw - 2rem));
  padding: var(--e3) var(--e4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--card);
  box-shadow: var(--shadow-lg);
  font-size: 0.8125rem;
  pointer-events: auto;
  animation: entree-notification 200ms ease-out;
}

.notification-ok { border-left: 3px solid var(--success); }
.notification-ok .ico { color: var(--success); }
.notification-erreur { border-left: 3px solid var(--destructive); }
.notification-erreur .ico { color: var(--destructive); }
.notification-info { border-left: 3px solid var(--info); }
.notification-info .ico { color: var(--info); }

@keyframes entree-notification {
  from { opacity: 0; transform: translateY(0.5rem); }
  to { opacity: 1; transform: translateY(0); }
}

/* ==========================================================================
   19. Page de connexion
   ========================================================================== */

/* Pas de barre latérale ici : tant qu'on n'est pas connecté, il n'y a nulle
   part où aller. */
.page-connexion {
  display: grid;
  place-items: center;
  min-height: 100vh;
  padding: var(--e6);
  background: var(--background);
}

.boite-connexion { width: 100%; max-width: 24rem; }

.connexion-marque {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--e3);
  margin-bottom: var(--e6);
}

.formulaire-connexion {
  display: flex;
  flex-direction: column;
  gap: var(--e4);
  /* Le formulaire suit le paragraphe d'aide, qui ne porte qu'un `margin-top`
     de `--e2` : sans cette marge, « Identifiant » venait se coller au texte. */
  margin-top: var(--e5);
}

/* Ici le `gap` ci-dessus espace DÉJÀ les champs. La règle générique
   `.champ + .champ { margin-top: --e5 }` s'y ajoutait, et les deux cumulés
   creusaient avant « Mot de passe » un écart deux fois plus large qu'entre le
   titre et son aide. Un seul mécanisme d'espacement par conteneur. */
.formulaire-connexion .champ + .champ { margin-top: 0; }

.formulaire-connexion .btn { width: 100%; }

.note-connexion {
  margin: var(--e5) 0 0;
  text-align: center;
  color: var(--muted-foreground);
  font-size: 0.75rem;
}

/* Message posé par le serveur dans la page (seul HTML composé côté Python). */
.message-connexion {
  display: flex;
  align-items: flex-start;
  gap: var(--e2);
  margin: var(--e4) 0 0;
  padding: var(--e3) var(--e4);
  border: 1px solid;
  border-radius: var(--radius-md);
  font-size: 0.8125rem;
  color: var(--foreground);
}
.message-connexion.erreur {
  background: var(--destructive-bg);
  border-color: color-mix(in srgb, var(--destructive) 40%, transparent);
}
.message-connexion.info {
  background: var(--info-bg);
  border-color: color-mix(in srgb, var(--info) 40%, transparent);
}
.message-connexion.ok {
  background: var(--success-bg);
  border-color: color-mix(in srgb, var(--success) 40%, transparent);
}

/* ==========================================================================
   20. Responsive
   ========================================================================== */

/* --- Tablette : la barre latérale se rétracte en tiroir ------------------- */

@media (max-width: 1024px) {
  /* La barre rangée hors écran ci-dessous reste dans la boîte de défilement :
     `translateX(-100%)` déplace le rendu, pas la zone défilable. Sans ce
     confinement, la page gagnait 199 px de défilement horizontal à 390 px —
     mesuré, pas supposé.

     Le confinement va sur <html> et NON sur <body> : la barre est en
     `position: fixed`, et un élément fixe est contenu par le viewport, pas
     par son ancêtre — un `overflow` posé sur <body> le laissait donc
     déborder (body restait à 390 px pendant que html montait à 589).
     On confine ici et non globalement : au-dessus de 1024 px la barre est
     visible et n'a rien à déborder, et les tableaux gardent leur propre
     `overflow-x: auto` dans `.tableau-cadre`. */
  html { overflow-x: hidden; }

  .barre-laterale {
    transform: translateX(-100%);
    box-shadow: var(--shadow-xl);
    width: 17rem;
  }

  /* Ouvert par le bouton « menu » de l'en-tête (app.js). */
  .barre-laterale.ouverte { transform: translateX(0); }

  .contenu { padding-left: 0; }

  .voile {
    position: fixed;
    inset: 0;
    z-index: 40;
    background: rgba(15, 20, 25, 0.5);
    backdrop-filter: blur(2px);
    border: none;
    padding: 0;
    animation: apparition 150ms ease-out;
  }

  @keyframes apparition {
    from { opacity: 0; }
    to { opacity: 1; }
  }

  .page { padding: var(--e5) var(--e5) var(--e10); }

  .actions-collantes { margin-left: calc(-1 * var(--e5)); margin-right: calc(-1 * var(--e5)); padding-inline: var(--e5); }
}

/* Le bouton menu n'existe qu'en dessous de 1024 px : au-delà, la barre est
   toujours là et un bouton pour l'ouvrir n'aurait pas de sens. */
.btn-menu { display: none; }

@media (max-width: 1024px) {
  .btn-menu { display: inline-flex; }
}

/* --- Mobile -------------------------------------------------------------- */

@media (max-width: 640px) {
  .page {
    padding: var(--e4) var(--e4) var(--e8);
    gap: var(--e4);
  }

  .page-titre { font-size: 1.25rem; }

  /* Deux compteurs par ligne plutôt qu'un : l'ancienne page consommait 203 px
     rien qu'en compteurs, et 551 px avant la première donnée. */
  .compteurs {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--e3);
  }

  .compteur {
    flex-direction: column;
    gap: var(--e2);
    padding: var(--e4);
  }

  .compteur-valeur { font-size: 1.375rem; }
  .compteur-icone { width: 1.75rem; height: 1.75rem; }
  .compteur-icone .ico { width: 1rem; height: 1rem; }

  /* Les filtres deviennent de vraies cibles tactiles et défilent
     horizontalement plutôt que de s'empiler sur trois rangées. */
  .outils { gap: var(--e2); }

  .groupe-filtres {
    width: 100%;
    overflow-x: auto;
    scrollbar-width: none;
  }
  .groupe-filtres::-webkit-scrollbar { display: none; }

  .filtre { min-height: 44px; padding-inline: var(--e4); }

  .champ-recherche { flex: 1 1 100%; max-width: none; }

  .maj { margin-left: 0; width: 100%; }

  .carte-entete,
  .carte-corps { padding: var(--e4); }

  .actions-collantes {
    margin-left: calc(-1 * var(--e4));
    margin-right: calc(-1 * var(--e4));
    padding-inline: var(--e4);
  }

  .actions-collantes .btn { flex: 1; }

  .tour { max-width: 92%; }

  .notifications { left: var(--e3); right: var(--e3); bottom: var(--e3); }
  .notification { max-width: none; }
}

/* --- Impression ---------------------------------------------------------- */

/* Un journal d'accès s'imprime : on retire l'ossature et on rend les cadres au
   noir, la couleur ne portant de toute façon jamais l'information seule. */
@media print {
  .barre-laterale,
  .entete,
  .outils,
  .actions-collantes,
  .notifications { display: none !important; }

  .contenu { padding-left: 0; }
  .page { max-width: none; padding: 0; }
  .carte { box-shadow: none; border-color: #999; }
  body { background: #fff; color: #000; }
}

/* --- Mouvement réduit ---------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
