/* public/shared/ui/components.css — bibliothèque de composants partagés
   Uniquement des variables de tokens.css — aucune
   couleur littérale.

   Deux familles de règles ici :
   1. Styles d'éléments de base (button/input/select/textarea/a/h1/h2) et
      classes de compatibilité (.pill/.ok/.bad/.warn/.row) : tokens.css
      a retiré le <style> inline de dashboard.html qui portait
      ces règles ; les pages existantes (public/dashboard/pages/**) les
      utilisent encore telles quelles — sans ce bloc elles resteraient sans
      style jusqu'à leur migration vers les classes .ui-*.
   2. Les composants .ui-* consommés par dialogs.js / states.js / badge.js
      et par les pages du dashboard. */

/* ---------- Éléments de base ---------- */
button {
  font-family: inherit; font-size: var(--font-3); font-weight: 600;
  background: var(--brand-solid); color: var(--on-brand); border: 0;
  border-radius: var(--radius-sm); padding: var(--space-2) var(--space-3);
  cursor: pointer; margin: var(--space-1) var(--space-1) 0 0;
}
button:hover { filter: brightness(1.08); }
button:disabled { opacity: .5; cursor: not-allowed; filter: none; }
button.secondary { background: var(--panel-2); color: var(--text); border: 1px solid var(--border); }
button.danger { background: var(--bad); color: var(--on-brand); }

input, select, textarea {
  font-family: inherit; font-size: var(--font-3); width: 100%;
  background: var(--panel); color: var(--text); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: var(--space-2) var(--space-3); margin: var(--space-2) 0;
}
input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent);
}

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

h1 { margin: 0; font-size: var(--font-6); letter-spacing: -.02em; }
h2 { margin: 0 0 var(--space-3); font-size: var(--font-4); letter-spacing: .02em; }

.row { padding: var(--space-3) 0; border-top: 1px solid var(--border); }
.row:first-child { border-top: 0; padding-top: 0; }

.pill {
  display: inline-flex; align-items: center; gap: var(--space-1);
  border-radius: 999px; padding: var(--space-1) var(--space-2);
  font-size: var(--font-1); border: 1px solid var(--border); color: var(--muted);
}
.ok { color: var(--ok); }
.bad { color: var(--bad); }
.warn { color: var(--warn); }

/* ---------- Calques (tiroir / modale) ---------- */
.ui-layer {
  position: fixed; inset: 0; z-index: 1000; display: flex;
  background: color-mix(in srgb, var(--bg) 70%, transparent);
}
.ui-layer-drawer { justify-content: flex-end; }
.ui-layer-modal { align-items: center; justify-content: center; padding: var(--space-4); }

.ui-drawer {
  display: flex; flex-direction: column; height: 100%; max-width: 100%;
  background: var(--panel); border-left: 1px solid var(--border); box-shadow: var(--shadow);
}
.ui-drawer-left { border-left: 0; border-right: 1px solid var(--border); order: -1; }
.ui-drawer > header {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-4); border-bottom: 1px solid var(--border); flex: 0 0 auto;
}
.ui-drawer > header h2 { margin: 0; }
.ui-drawer-body { padding: var(--space-4); overflow-y: auto; flex: 1 1 auto; }
@media (max-width: 720px) { .ui-drawer { width: 100% !important; max-width: 100vw; } }

.ui-modal {
  display: flex; flex-direction: column; width: 100%; max-width: 520px; max-height: 90vh;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: var(--space-4); gap: var(--space-3); overflow: hidden;
}
.ui-modal h2 { margin: 0; }
.ui-modal-body { overflow-y: auto; flex: 1 1 auto; }
.ui-modal footer { display: flex; justify-content: flex-end; gap: var(--space-2); }

.ui-icon-btn {
  background: transparent; color: var(--muted); border: 0; border-radius: var(--radius-sm);
  padding: var(--space-1) var(--space-2); cursor: pointer; font-size: var(--font-3); margin: 0;
}
.ui-icon-btn:hover { color: var(--text); background: var(--panel-2); }

/* ---------- Boutons .ui-btn ---------- */
.ui-btn {
  font-family: inherit; font-size: var(--font-3); font-weight: 600; cursor: pointer;
  border-radius: var(--radius-sm); padding: var(--space-2) var(--space-3); border: 1px solid transparent;
  margin: 0;
}
.ui-btn-default { background: var(--panel-2); color: var(--text); border-color: var(--border); }
.ui-btn-primary { background: var(--brand-solid); color: var(--on-brand); }
.ui-btn-danger { background: var(--bad); color: var(--on-brand); }
.ui-btn:hover { filter: brightness(1.08); }
.ui-btn:disabled { opacity: .5; cursor: not-allowed; filter: none; }

/* ---------- Badge ---------- */
.ui-badge {
  display: inline-flex; align-items: center; gap: var(--space-1);
  border-radius: 999px; padding: var(--space-1) var(--space-2);
  font-size: var(--font-1); font-weight: 600; border: 1px solid var(--border);
}
.ui-badge-ok { color: var(--ok); border-color: var(--ok); }
.ui-badge-warn { color: var(--warn); border-color: var(--warn); }
.ui-badge-bad { color: var(--bad); border-color: var(--bad); }
.ui-badge-info { color: var(--info); border-color: var(--info); }
.ui-badge-neutral { color: var(--muted); border-color: var(--border); }

/* ---------- États ---------- */
.ui-empty, .ui-error {
  display: flex; flex-direction: column; align-items: center; text-align: center; gap: var(--space-2);
  padding: var(--space-6) var(--space-4); color: var(--muted);
}
.ui-empty h3 { margin: 0; color: var(--text); font-size: var(--font-4); }
.ui-error p { margin: 0; color: var(--bad); }

.ui-skeleton { display: flex; flex-direction: column; gap: var(--space-2); }
.ui-skeleton-row {
  height: var(--space-6); border-radius: var(--radius-sm);
  background: linear-gradient(90deg, var(--panel-2) 25%, var(--panel) 50%, var(--panel-2) 75%);
  background-size: 200% 100%; animation: ui-skeleton-pulse 1.4s ease-in-out infinite;
}
@keyframes ui-skeleton-pulse { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

/* ---------- Tuile KPI ---------- */
.ui-kpi {
  display: flex; flex-direction: column; gap: var(--space-1);
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  padding: var(--space-4);
}
.ui-kpi-label { color: var(--muted); font-size: var(--font-2); }
.ui-kpi-value { color: var(--text); font-size: var(--font-6); font-weight: 700; letter-spacing: -.02em; }
.ui-kpi-delta { font-size: var(--font-2); font-weight: 600; }
.ui-kpi-delta-up { color: var(--ok); }
.ui-kpi-delta-down { color: var(--bad); }
.ui-kpi-delta-flat { color: var(--muted); }
.ui-kpi-hint { color: var(--muted); font-size: var(--font-1); }
/* Les deux tons d'ALERTE d'une tuile (le mur de plateau les
   pose sur « aucun opérateur disponible » et « une prise en main attend »).
   Ils étaient dessinés dans la feuille d'un seul écran ; ils vivent avec la
   tuile, comme sa grille juste en dessous. */
.ui-kpi-warn { border-color: var(--warn); }
.ui-kpi-crit { border-color: var(--bad); }
/* La grille des tuiles. Elle vit ICI, avec la tuile qu'elle dispose : posée
   dans la feuille d'un seul écran, elle serait à réécrire au suivant — ce qui
   était déjà arrivé (`shell-kpi-grid`, shell.css). Sans elle, `ui-kpi-grid`
   était une classe déclarée par le rendu et jamais dessinée : les neuf tuiles
   des Rapports s'empilaient en colonne pleine largeur. */
.ui-kpi-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-3);
}

/* ---------- Formulaire ---------- */
.ui-form { display: flex; flex-direction: column; gap: var(--space-3); }
.ui-field { display: flex; flex-direction: column; gap: var(--space-1); font-size: var(--font-2); color: var(--muted); }
/* Une case à cocher se lit EN LIGNE (case puis libellé), pas empilée : voir la
   note de buildField() dans public/shared/ui/form.js. */
.ui-field-check { flex-direction: row; align-items: center; gap: var(--space-2); }
/* Le RADIO comme la case à cocher. La règle globale
   `input, select, textarea { width: 100% }` (ligne 27) s'applique aussi aux
   ronds de choix : un radio dans un `.ui-field-check` s'étirait sur toute la
   ligne et projetait son libellé au bord opposé. « Qui appelle ? » sur la fiche
   campagne montrait trois ronds empilés au milieu et « IA / Humain / Hybride »
   contre le bord droit, sans qu'on puisse apparier les deux. */
.ui-field-check input[type="checkbox"], .ui-field-check input[type="radio"] { width: auto; flex: 0 0 auto; }
.ui-field-check > span { color: var(--text); }
.ui-field input, .ui-field select, .ui-field textarea { margin: 0; }

/* ---------- Formulaire générique ---------- */
.ui-field-hint { color: var(--muted); font-size: var(--font-1); }
.ui-field-error { color: var(--bad); font-size: var(--font-1); }
.ui-field input[aria-invalid="true"], .ui-field select[aria-invalid="true"], .ui-field textarea[aria-invalid="true"] {
  border-color: var(--bad); outline-color: var(--bad);
}
.ui-form-error {
  color: var(--bad); font-size: var(--font-2); background: color-mix(in srgb, var(--bad) 12%, transparent);
  border: 1px solid var(--bad); border-radius: var(--radius-sm); padding: var(--space-2) var(--space-3);
}
.ui-form-actions { display: flex; justify-content: flex-end; gap: var(--space-2); }

/* ---------- Curseur ----------
   Le curseur et sa valeur sont UN champ : le libellé au-dessus, la piste et le
   chiffre côte à côte en dessous — sinon le chiffre s'éloigne de la poignée
   qu'il décrit. La piste prend la place restante et le chiffre garde une
   largeur fixe, pour qu'il ne fasse pas sauter la piste à chaque dizaine. Le
   remplissage vertical donne à la poignée une zone tactile visable au doigt.
   Tokens seulement, jamais une couleur littérale. */
.ui-range { display: grid; grid-template-columns: 1fr auto; grid-template-areas: "label label" "track value"; align-items: center; column-gap: var(--space-3); }
.ui-range > span { grid-area: label; }
.ui-range input[type="range"] { grid-area: track; width: 100%; min-width: 0; margin: 0; padding: var(--space-2) 0; accent-color: var(--brand); background: transparent; border: 0; }
.ui-range .ui-range-value { grid-area: value; min-width: 4.5ch; text-align: right; color: var(--text); font-size: var(--font-3); font-variant-numeric: tabular-nums; }
.ui-range .ui-field-hint, .ui-range .ui-field-error { grid-column: 1 / -1; }
.ui-range input[type="range"]:disabled { opacity: .5; cursor: not-allowed; }

/* ---------- Graphique ---------- */
.ui-chart { position: relative; width: 100%; aspect-ratio: 16 / 7; }
.ui-chart canvas { width: 100% !important; height: 100% !important; }

/* ---------- Table (base ; complétée par le DataTable) ---------- */
.ui-table { width: 100%; border-collapse: collapse; font-size: var(--font-3); }
.ui-table th, .ui-table td { text-align: left; padding: var(--space-2) var(--space-3); border-bottom: 1px solid var(--border); }
.ui-table th { color: var(--muted); font-weight: 600; font-size: var(--font-1); text-transform: uppercase; letter-spacing: .04em; }

/* Une table simple plus large que sa carte (un tiroir de 640 px) défile sur place. */
.ui-table-scroll { overflow-x: auto; max-width: 100%; }

/* ---------- DataTable serveur ---------- */
.ui-datatable { display: flex; flex-direction: column; gap: var(--space-3); }
.ui-table-toolbar { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--space-3); }
.ui-table-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-3); flex: 1 1 auto; margin: 0; }
.ui-table-form input[type="search"] { min-width: 220px; }
.ui-table-filter { display: flex; flex-direction: column; gap: var(--space-1); font-size: var(--font-2); color: var(--muted); }
.ui-table-filter input, .ui-table-filter select { margin: 0; }
/* Une case à cocher se lit sur la même ligne que son libellé, comme `.ui-field-check`. */
.ui-table-filter:has(> input[type="checkbox"]) { flex-direction: row; align-items: center; gap: var(--space-2); }
.ui-table-filter > input[type="checkbox"] { width: auto; flex: 0 0 auto; }
.ui-table-filter-daterange { display: flex; gap: var(--space-2); }
.ui-table-export {
  display: inline-flex; align-items: center; gap: var(--space-1);
  background: var(--panel-2); color: var(--text); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3); font-size: var(--font-3); font-weight: 600; text-decoration: none; white-space: nowrap;
}
.ui-table-export:hover { filter: brightness(1.08); }

.ui-table th[data-sort] { cursor: pointer; user-select: none; }
.ui-table th[data-sort]:hover, .ui-table th[data-sort]:focus-within { color: var(--text); }
.ui-table-sort { all: unset; cursor: pointer; }
.ui-table-sort:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm); }
.ui-table th[data-sort]::after { content: ''; display: inline-block; width: var(--space-3); }
.ui-table th[data-sort][aria-sort="ascending"]::after { content: '▲'; margin-left: var(--space-1); }
.ui-table th[data-sort][aria-sort="descending"]::after { content: '▼'; margin-left: var(--space-1); }
.ui-table tbody tr:hover { background: var(--panel-2); }
/* La sélection (les actions en masse) : une case par ligne, sa cible tactile au jeton de la peau. */
.ui-table .ui-table-select-cell { width: 1%; padding-top: 0; padding-bottom: 0; }
.ui-table-select { display: inline-flex; align-items: center; justify-content: center; min-width: var(--target-min, 44px); min-height: var(--target-min, 44px); margin: 0; cursor: pointer; }
.ui-table-select input { width: auto; margin: 0; cursor: pointer; }

/* rowHref : un vrai <a> par ligne (accessible, ouvrable dans un nouvel
   onglet), étiré sur toute la ligne plutôt qu'une navigation pilotée par
   script sur un clic générique de <tr>. */
.ui-table tbody tr.ui-table-row-linked { position: relative; }
.ui-table-row-link { position: absolute; inset: 0; z-index: 0; }

.ui-table-status { padding: var(--space-4) 0; }

.ui-table-pagination { display: flex; align-items: center; justify-content: flex-end; gap: var(--space-3); color: var(--muted); font-size: var(--font-2); }
.ui-table-pagination button {
  font-family: inherit; font-size: var(--font-2); font-weight: 600; cursor: pointer;
  background: var(--panel-2); color: var(--text); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: var(--space-1) var(--space-3); margin: 0;
}
.ui-table-pagination button:disabled { opacity: .5; cursor: not-allowed; }

/* Cartes sous 720 px : chaque ligne devient un bloc, chaque cellule porte
   son étiquette de colonne via data-label (posé par le composant). */
@media (max-width: 720px) {
  .ui-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .ui-table, .ui-table tbody, .ui-table tr, .ui-table td { display: block; width: 100%; }
  .ui-table tr { border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: var(--space-3); overflow: hidden; }
  .ui-table td {
    display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
    text-align: right; border-bottom: 1px solid var(--border);
  }
  .ui-table td:last-child { border-bottom: 0; }
  .ui-table td::before { content: attr(data-label); color: var(--muted); font-weight: 600; font-size: var(--font-1); text-transform: uppercase; letter-spacing: .04em; text-align: left; }
}

/* ---------- Carte de page (states.js::card) et onglets ---------- */
/* `.ui-card` était déclarée DEUX fois à onze lignes
   d'intervalle (doublon retiré) : mêmes fond, bordure,
   rayon et padding, la seconde ajoutant seulement `margin-bottom`. Un
   lecteur qui tombait sur la première croyait tenir la définition de la
   carte et se trompait d'une marge. Un seul bloc désormais — celui qui
   gagnait de toute façon dans la cascade, donc AUCUN changement de rendu. */
.ui-card {
  background: var(--panel);
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: var(--space-4); margin-bottom: var(--space-4);
}
/* P4-D — une page publique courte (inscription au Marché) : une carte centrée, étroite. */
.ui-public-page { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: var(--space-5) var(--space-4); }
.ui-public-page > .ui-card { width: 100%; max-width: 400px; margin: 0; }
.ui-public-page .ui-status { min-height: 1.4em; margin: 0; color: var(--muted); font-size: var(--font-2); }
.ui-public-page .ui-status[data-tone="bad"] { color: var(--bad); }
.ui-public-page .ui-status[data-tone="ok"] { color: var(--ok); }
.ui-tabs { display: flex; gap: var(--space-2); border-bottom: 1px solid var(--border); }
.ui-tabs button { background: transparent; color: var(--muted); border-radius: var(--radius-sm) var(--radius-sm) 0 0; padding: var(--space-2) var(--space-3); margin: 0; }
.ui-tabs button[aria-selected="true"] { color: var(--text); background: var(--panel-2); }
.ui-card-header {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: var(--space-3); margin-bottom: var(--space-3);
}
.ui-card-header h2 {
  margin: 0; font-size: var(--font-3); color: var(--muted);
  text-transform: uppercase; letter-spacing: .06em;
}

/* Actions groupées dans une cellule de tableau (agenda : terminer /
   replanifier / annuler). */
.ui-row-actions { display: flex; flex-wrap: wrap; gap: var(--space-1); }

/* ---------- Menu d'appel (services/dialer.js) ---------- */
/* « Appeler » ouvre le même menu sur les quatre écrans qui
   composent (fiche appel, fiche prospect, fiche personne, file du poste). Il
   vit sur <body>, positionné sous son ancre : une carte à `overflow` ne peut
   pas le rogner. Tokens uniquement. */
.callbar-menu {
  /* Au-dessus des calques (.ui-layer, 1000) : « Appeler » s'ouvre aussi depuis la fiche d'un contact. */
  z-index: 1100; min-width: 180px;
  display: flex; flex-direction: column; gap: var(--space-1);
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: var(--space-2);
}
.callbar-menu-item { justify-content: flex-start; width: 100%; }

/* ---------- Barre d'appel (components/callBar.js) ---------- */
/* Le seul élément permanent de la coquille. Elle occupe sa
   propre rangée sous la sortie de page (#shell-callbar, shell.css) : elle ne
   recouvre donc jamais le bas d'un écran, et rien n'a à lui réserver de marge.
   Tokens uniquement, utilisable à 390 px (les rangées se replient). */
.callbar {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  font-size: var(--font-2); color: var(--text);
  min-height: 64px; box-sizing: border-box;
}
.callbar-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2);
}
.callbar-label { font-weight: 600; }
.callbar-dot {
  width: 10px; height: 10px; border-radius: 50%; display: inline-block; flex: 0 0 auto;
}
.callbar-dot-ok { background: var(--ok); }
.callbar-dot-bad { background: var(--bad); }

.callbar-dialpad { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); flex: 1 1 220px; }
.callbar-dialpad input {
  flex: 1 1 140px; min-width: 0;
  background: var(--panel-2); border: 1px solid var(--border); color: var(--text);
  border-radius: var(--radius-sm); padding: var(--space-2) var(--space-3);
}
.callbar-dial-status { color: var(--muted); }

/* Sonnerie : l'interlocuteur d'abord, les deux gestes ensuite. */
.callbar-caller { display: flex; flex-direction: column; gap: 2px; flex: 1 1 200px; }
.callbar-caller .sub { color: var(--muted); font-size: var(--font-1); }
[data-state="ringing"] .callbar-caller b { font-size: var(--font-4); }

/* M13 : le brief d'un transfert de l'IA, lisible pendant tout l'appel ; borné en hauteur, il défile. */
.callbar-brief { max-height: 160px; overflow-y: auto; padding: var(--space-2) var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--panel-2); }
.callbar-brief summary { font-weight: 600; cursor: pointer; min-height: var(--target-min, 44px); display: flex; align-items: center; }
.callbar-brief p, .callbar-brief ul { margin: var(--space-1) 0; }

/* En appel : la durée est le seul chiffre qui compte, elle se lit de loin. */
.callbar-timer { font-variant-numeric: tabular-nums; font-size: var(--font-4); font-weight: 600; }
.callbar-dtmf {
  display: grid; grid-template-columns: repeat(6, minmax(32px, 1fr));
  gap: var(--space-1); flex: 0 1 260px;
}
.callbar-dtmf button { padding: var(--space-1) 0; min-width: 0; }

/* Wrap-up : le résultat, la note, les suites, puis la sortie. */
.callbar-wrapup { display: flex; flex-direction: column; gap: var(--space-2); }
.callbar-wrapup select,
.callbar-wrapup textarea {
  background: var(--panel-2); border: 1px solid var(--border); color: var(--text);
  border-radius: var(--radius-sm); padding: var(--space-2) var(--space-3);
  font-family: inherit; font-size: var(--font-2);
}
.callbar-wrapup textarea { flex: 1 1 240px; min-height: 40px; resize: vertical; }
.callbar-acw { color: var(--muted); font-variant-numeric: tabular-nums; }
/* Le repli n'est pas un détail discret : c'est la seule chose qui dit à
   l'opérateur que sa qualification n'est PAS enregistrée. */
.callbar-fallback {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2);
  border: 1px solid var(--warn); border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
}
.callbar-fallback a { color: var(--accent); }

@media (max-width: 720px) {
  .callbar { padding: var(--space-2) var(--space-3); }
  .callbar-dtmf { flex: 1 1 100%; grid-template-columns: repeat(6, minmax(28px, 1fr)); }
}

/* Deux choses qu'un opérateur doit voir sans les
   chercher : combien d'après-appels attendent derrière celui-ci, et le fait
   que le serveur n'a PAS confirmé sa présence (il ne recevra pas d'appel). */
.callbar-wrap-count {
  font-size: var(--font-1); color: var(--warn);
  border: 1px solid var(--warn); border-radius: var(--radius-sm);
  padding: 0 var(--space-2);
}
.callbar-presence-warning {
  border-top: 1px solid var(--bad); padding-top: var(--space-2); color: var(--bad);
}

/* ---------- Secret affiché une seule fois ---------- */
/* Une clé d'API fait 57 caractères sans le moindre espace : sans `word-break`,
   elle déborde de la modale au lieu de revenir à la ligne, et la fin du secret
   — la seule partie qui compte — devient invisible. Monospace pour qu'un `0` et
   un `O` se distinguent à la relecture, sélectionnable pour qu'une copie à la
   main reste possible là où le presse-papier n'existe pas. */
.ui-code {
  display: block; width: 100%; box-sizing: border-box;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--font-2); line-height: 1.5;
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3); margin: var(--space-3) 0;
  overflow-wrap: anywhere; word-break: break-all; user-select: all;
}

/* P5-C : la face d'une pièce d'identité dans la fiche de la console (jamais plus large que la modale). */
.ui-compliance-image { display: block; max-width: 100%; height: auto; margin: var(--space-2) 0; border: 1px solid var(--border); border-radius: var(--radius-sm); }

/* ---------- L'Atlas (spec 2026-10-26 § 5) : six éléments, deux matières, la carte ----------
   Hors de la peau (console, écrans sans `data-skin`), chaque jeton propre à la peau retombe sur un jeton existant : un texte
   sur une encre (4,5:1 au moins), un filet, un contour ou un point sur une couleur de fond ou d'alerte (3:1 suffit). */
.ui-sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

.ui-cartouche { display: inline-flex; flex-direction: column; align-items: center; gap: 1px; padding: 6px 12px 7px; background: var(--panel); color: var(--text); border: var(--line, 1.5px) solid var(--rule, var(--border)); outline: 1px solid var(--rule, var(--border)); outline-offset: 3px; text-decoration: none; }
.ui-cartouche:focus-visible { outline: 2px solid var(--accent); }
.ui-cartouche-label { font-size: var(--font-1); font-weight: 700; letter-spacing: .14em; text-transform: uppercase; font-stretch: 75%; }
.ui-cartouche-value { font-size: var(--font-6); font-weight: 800; line-height: 1.05; font-variant-numeric: tabular-nums; }
.ui-cartouche-sub { font-family: var(--font-mono, monospace); font-size: var(--font-1); }
.ui-cartouche-ink { color: var(--text); }
.ui-cartouche-money { border-color: var(--money, var(--warn)); outline-color: var(--money, var(--warn)); }
.ui-cartouche-money .ui-cartouche-label, .ui-cartouche-money .ui-cartouche-value { color: var(--money, var(--text)); }

.ui-plaque { display: inline-block; padding: 2px 7px; border-radius: var(--radius-sm); font-size: var(--font-3); font-weight: 700; font-stretch: 80%; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ui-plaque-route { background: var(--live, var(--brand-solid)); color: var(--on-brand); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--on-brand) 55%, transparent); }
.ui-plaque-ink { background: var(--text); color: var(--bg); }
.ui-plaque-locked { background: transparent; color: var(--text); border: 1.5px dashed var(--text); padding: 1px 6px; }

.ui-lignes { display: flex; flex-direction: column; }
.ui-lignes-head { display: flex; justify-content: space-between; padding-bottom: var(--space-2); font-family: var(--font-mono, monospace); font-size: var(--font-1); letter-spacing: .06em; text-transform: uppercase; }
.ui-lignes-list { list-style: none; margin: 0; padding: 0; border-top: var(--line, 1.5px) solid var(--rule, var(--border)); border-bottom: var(--line, 1.5px) solid var(--rule, var(--border)); }
.ui-ligne + .ui-ligne { border-top: 1px solid var(--rule, var(--border)); }
.ui-ligne-body { position: relative; display: grid; grid-template-columns: 14px 28px minmax(0, 1fr) auto; column-gap: var(--space-3); row-gap: 3px; align-items: center; min-height: var(--target-min, 44px); padding: var(--space-2) 0; color: var(--text); text-decoration: none; }
.ui-lampe { width: 12px; height: 12px; border-radius: 50%; border: 1.5px solid var(--text); box-sizing: border-box; }
.ui-lampe-on { background: var(--live, var(--brand-solid)); border: 0; box-shadow: 0 0 0 1.5px var(--text); }
/* Couleurs forcées : fonds et ombres disparaissent, le voyant allumé reste un disque plein. */
@media (forced-colors: active) { .ui-lampe { border: 1px solid CanvasText; } .ui-lampe-on { forced-color-adjust: none; background: CanvasText; } }
.ui-ligne-code { font-family: var(--font-mono, monospace); font-size: var(--font-1); }
.ui-ligne-title { font-size: var(--font-4); font-weight: 700; font-stretch: 90%; }
.ui-ligne-value { font-family: var(--font-mono, monospace); font-size: var(--font-4); font-weight: 600; color: var(--money, var(--text)); text-align: right; }
.ui-ligne-meta { grid-column: 3; font-size: var(--font-2); color: var(--muted); }
.ui-ligne-region { grid-column: 4; font-family: var(--font-mono, monospace); font-size: var(--font-1); color: var(--muted); text-align: right; }

.ui-echelle { display: flex; flex-direction: column; gap: 5px; }
.ui-echelle-ends, .ui-echelle-ticks { display: flex; justify-content: space-between; font-size: var(--font-1); }
.ui-echelle-ends { font-weight: 700; letter-spacing: .1em; text-transform: uppercase; font-stretch: 75%; }
.ui-echelle-ticks { color: var(--muted); font-variant-numeric: tabular-nums; }
.ui-echelle-bar { display: flex; gap: 2px; padding: 2px; height: 13px; box-sizing: border-box; border: var(--line, 1.5px) solid var(--rule, var(--border)); }
.ui-echelle-seg { flex: 1 1 0; background: var(--panel-2); }
.ui-echelle-on { background: var(--text); }

.ui-itineraire { position: relative; list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(var(--steps, 4), minmax(0, 1fr)); }
.ui-itineraire::before { content: ""; position: absolute; left: calc(50% / var(--steps, 4)); right: calc(50% / var(--steps, 4)); top: 7px; height: 2px; background: var(--text); }
.ui-etape { position: relative; display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; font-size: var(--font-1); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; font-stretch: 75%; }
.ui-etape-point { width: 16px; height: 16px; border-radius: 50%; box-sizing: border-box; background: var(--text); }
.ui-etape-label { overflow-wrap: anywhere; }
.ui-etape-done { color: var(--text); }
.ui-etape-current { color: var(--live, var(--brand)); font-weight: 800; }
.ui-etape-current .ui-etape-point { background: var(--panel); border: 3.5px solid var(--live, var(--brand-solid)); }
.ui-etape-todo { color: var(--muted); font-weight: 600; }
.ui-etape-todo .ui-etape-point { background: var(--panel); border: 1.5px solid var(--text); }

.ui-tampon { display: inline-block; padding: 2px 8px; border: 2px solid currentColor; font-size: var(--font-1); font-weight: 800; letter-spacing: .14em; text-transform: uppercase; font-stretch: 75%; transform: rotate(-3deg); }
.ui-tampon-route { color: var(--live, var(--brand)); }
.ui-tampon-ink { color: var(--text); outline: 1px solid currentColor; outline-offset: 2px; transform: rotate(2deg); }
.ui-tampon-pending { color: var(--muted); border-style: dashed; transform: rotate(-2deg); }

.ui-pointilles { border: var(--line, 1.5px) dashed var(--rule, var(--border)); }
.ui-hachures { background: repeating-linear-gradient(45deg, var(--bg) 0 5px, var(--panel-2) 5px 6px); }
/* Les hachures de l'argent sont aussi le fond du cartouche d'argent : une seule règle. */
.ui-hachures-money, .ui-cartouche-money { background: repeating-linear-gradient(135deg, var(--panel) 0 6px, var(--hatch, var(--panel-2)) 6px 7px); }

/* La carte du tableau de bord sous la peau de l'Atlas : limites de départements en trait mixte, chaleur en teintes d'encre. */
:root[data-skin="atlas"] .w-map-shape { fill: var(--map-0); stroke: var(--map-edge); stroke-width: .8; stroke-dasharray: 6 2 1.5 2; }
:root[data-skin="atlas"] .w-map-shape[data-heat="1"] { fill: var(--map-1); }
:root[data-skin="atlas"] .w-map-shape[data-heat="2"] { fill: var(--map-2); }
:root[data-skin="atlas"] .w-map-shape[data-heat="3"] { fill: var(--map-3); }
:root[data-skin="atlas"] .w-map-shape[data-heat="4"] { fill: var(--map-4); }
:root[data-skin="atlas"] .w-map-shape[role="button"]:hover, :root[data-skin="atlas"] .w-map-shape[role="button"]:focus-visible { stroke: var(--text); stroke-width: 2; stroke-dasharray: none; }
:root[data-skin="atlas"] .w-map-shape.w-map-selected { stroke: var(--live); stroke-width: 2.5; stroke-dasharray: none; }
:root[data-skin="atlas"] .w-map-shape.w-map-context { fill: var(--panel-2); opacity: .7; }
