/* =========================================================================
   Toutadmin — jetons de thème
   =========================================================================

   Deux axes, indépendants l'un de l'autre :

     • la PALETTE, posée par l'administration sur <html data-palette>, vaut pour
       toute l'instance. Trois valeurs : institutionnel (défaut), moderne, vif.
     • le MODE clair ou sombre, posé sur <html data-theme>, appartient à chaque
       personne. Le script de thème résout « système » en une valeur concrète
       avant le premier rendu, ce qui évite de dupliquer les palettes dans une
       media query.

   Chaque bloc de palette redéfinit l'INTÉGRALITÉ des jetons de couleur : sans
   cela, le bloc sombre par défaut (:root[data-theme="dark"], spécificité 0,2,0)
   l'emporterait sur une palette claire de même poids. Les blocs sombres de
   palette portent deux attributs (0,3,0) et tranchent donc toujours.
   ========================================================================= */

:root,
[data-palette-preview="institutionnel"] {
  /* Marque : bleu profond mais lumineux, constant dans les deux thèmes */
  --brand-deep: #0b46d1;
  --brand-deep-2: #0a2f8f;
  --brand-ink: #ffffff;

  /* Surfaces */
  --bg: #eef1f6;
  --surface: #ffffff;
  --surface-rgb: 255 255 255;
  --surface-2: #f7f9fc;
  --surface-hover: #f2f6fd;
  --border: #d5dce6;
  --border-strong: #b9c4d4;

  /* Texte */
  --text: #10193a;
  --text-secondary: #55618a;
  /* Assez sombre pour être lu : sous 4,5:1, un texte d'aide n'aide personne.
     Mesuré sur la surface la plus claire où il apparaît. */
  --text-tertiary: #68738c;
  --text-on-brand: rgba(255, 255, 255, 0.78);

  /* Accent interactif */
  --accent: #0a66ff;
  --accent-hover: #0b52cc;
  /* En clair, le ton de l'accent porte déjà du blanc : le fond des boutons
     pleins est le même. En sombre, il ne peut pas l'être — voir le bloc
     correspondant. */
  --accent-fill: var(--accent);
  --accent-fill-hover: var(--accent-hover);
  --accent-soft: #e8f0ff;
  --accent-ring: rgba(10, 102, 255, 0.24);
  --accent-contrast: #ffffff;

  /* États */
  --success: #0a7d32;
  /* blanc sur vert profond : 5,26:1 */
  --success-contrast: #ffffff;
  --success-bg: #e7f4ec;
  --success-border: #b6ddc4;
  --warning: #96560a;
  --warning-bg: #fdf2e3;
  --warning-border: #efd3a8;
  --danger: #c8102e;
  /* blanc sur rouge profond : 5,88:1 */
  --danger-contrast: #ffffff;
  --danger-bg: #fdecef;
  --danger-border: #f2c2ca;

  /* La teinte de l’ombre appartient à la palette ; sa géométrie, non :
     elle est bâtie une seule fois, plus bas, à partir de cette encre. */
  --shadow-ink: 16 25 58;

  --radius: 9px;
  --radius-lg: 16px;
  --sidebar-width: 248px;
  --page-max: 1320px;
  --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

:root[data-theme="dark"] {
  /* Institutionnel, en sombre : des neutres à peine froids, jamais marine.

     Ce bloc redéfinit l'intégralité des jetons : voir l'avertissement en tête
     de fichier. Chaque rapport annoncé ici a été mesuré, pas estimé. */

  --brand-deep: #123a86;
  --brand-deep-2: #0b2456;
  --brand-ink: #ffffff;

  /* Quatre plans, du plus bas au plus haut. Le pas de luminance entre le fond
     et la carte vaut 1,13:1 — le même qu'en clair, car le rapport de
     contraste se tasse près du noir et n'ira pas plus loin sans délaver la
     carte. C'est donc le contour qui porte l'élévation, et non l'ombre. */
  --bg: #090b0f;
  --surface: #171a22;
  --surface-rgb: 23 26 34;
  --surface-2: #1e222b;
  --surface-hover: #262a34;

  /* Un trait qu'on voit : 1,43:1 sur la carte, 1,62:1 sur le fond.
     En clair il vaut 1,38:1 et l'ombre fait le reste ; ici il travaille seul. */
  --border: #31363f;
  --border-strong: #474d5a;

  /* Textes, mesurés sur la surface la plus claire où ils apparaissent — le
     survol, #262a34. Le tertiaire y tient 4,55:1 : sous 4,5:1, un texte
     d'aide n'aide personne, en sombre comme en clair. */
  --text: #f0f1f5;
  --text-secondary: #a9afbe;
  --text-tertiary: #8a91a2;
  --text-on-brand: rgba(255, 255, 255, 0.80);

  /* L'accent a deux métiers, et sur fond sombre un seul ton ne peut pas les
     faire tous les deux : un bleu assez clair pour se lire sur la carte est
     trop clair pour porter du blanc. On sépare donc le ton du texte
     (6,58:1 sur la carte) du fond des boutons pleins, qui reste celui
     du thème clair et porte du blanc à 4,82:1.

     Le fond « doux » reste à 10 % là où le clair se permet un aplat franc :
     au-delà, l'étiquette d'accent s'éclaircit assez pour que son propre texte
     repasse sous 4,5:1. Mesuré à l'écran, pas au tableau. */
  --accent: #6f9dff;
  --accent-hover: #8fb2ff;
  --accent-fill: #0a66ff;
  --accent-fill-hover: #0b52cc;
  --accent-soft: rgba(111, 157, 255, 0.1);
  --accent-ring: rgba(111, 157, 255, 0.32);
  --accent-contrast: #ffffff;

  /* Les trois états. Sur leur propre fond translucide, chacun tient au moins
     4,86:1 ; en aplat, c'est une encre sombre qui les surmonte. */
  --success: #4ade80;
  --success-contrast: #052e16;
  --success-bg: rgba(74, 222, 128, 0.14);
  --success-border: rgba(74, 222, 128, 0.32);
  --warning: #fbbf24;
  --warning-contrast: #2a1a00;
  --warning-bg: rgba(251, 191, 36, 0.14);
  --warning-border: rgba(251, 191, 36, 0.32);
  --danger: #fb7185;
  --danger-contrast: #2b0510;
  --danger-bg: rgba(251, 113, 133, 0.14);
  --danger-border: rgba(251, 113, 133, 0.32);

  /* Une ombre noire sur un fond presque noir ne change rien : mesuré, le fond
     assombri de 22 % ne s'écarte du fond que de 1,01:1. On la garde pour le
     peu qu'elle donne sous les fenêtres flottantes, et on compte sur le trait. */
  --shadow-ink: 0 0 0;
}


/* ---------- Palette « Moderne » : ardoise et indigo ---------- */
/* Neutres presque sans teinte, un indigo violacé pour l'action, des angles
   nettement plus doux : le vocabulaire des interfaces contemporaines. */

:root[data-palette="moderne"],
[data-palette-preview="moderne"] {
  --brand-deep: #242640;
  --brand-deep-2: #15162a;
  --brand-ink: #ffffff;

  --bg: #f5f6fa;
  --surface: #ffffff;
  --surface-rgb: 255 255 255;
  --surface-2: #fafafd;
  --surface-hover: #f2f1fd;
  --border: #e4e5f0;
  --border-strong: #ccced2;

  --text: #16172a;
  --text-secondary: #545675;
  /* Assez sombre pour être lu : sous 4,5:1, un texte d'aide n'aide personne.
     Mesuré sur la surface la plus claire où il apparaît. */
  --text-tertiary: #6c6f89;
  --text-on-brand: rgba(255, 255, 255, 0.76);

  --accent: #5546d6;
  --accent-hover: #4638bd;
  --accent-fill: var(--accent);
  --accent-fill-hover: var(--accent-hover);
  --accent-soft: #eeecfe;
  --accent-ring: rgba(85, 70, 214, 0.24);
  --accent-contrast: #ffffff;

  --success: #0b7150;
  /* blanc sur vert profond : 6,01:1 */
  --success-contrast: #ffffff;
  --success-bg: #e7f6f0;
  --success-border: #b2e0ce;
  --warning: #8d5a06;
  --warning-bg: #fdf3e4;
  --warning-border: #edd3a6;
  --danger: #c62648;
  /* blanc sur rouge profond : 5,57:1 */
  --danger-contrast: #ffffff;
  --danger-bg: #fdecf0;
  --danger-border: #f4c1cd;

  /* Ombres plus larges et plus douces : la profondeur remplace les traits. */
  /* La teinte de l’ombre appartient à la palette ; sa géométrie, non :
     elle est bâtie une seule fois, plus bas, à partir de cette encre. */
  --shadow-ink: 22 23 42;

  --radius: 10px;
  --radius-lg: 18px;
}

:root[data-palette="moderne"][data-theme="dark"] {
  /* Moderne, en sombre : des neutres sans teinte, un indigo qui reste vif.

     Ce bloc redéfinit l'intégralité des jetons : voir l'avertissement en tête
     de fichier. Chaque rapport annoncé ici a été mesuré, pas estimé. */

  --brand-deep: #2b2d4d;
  --brand-deep-2: #1a1b2e;
  --brand-ink: #ffffff;

  /* Quatre plans, du plus bas au plus haut. Le pas de luminance entre le fond
     et la carte vaut 1,11:1 — le même qu'en clair, car le rapport de
     contraste se tasse près du noir et n'ira pas plus loin sans délaver la
     carte. C'est donc le contour qui porte l'élévation, et non l'ombre. */
  --bg: #0a0a0d;
  --surface: #17171c;
  --surface-rgb: 23 23 28;
  --surface-2: #1d1d24;
  --surface-hover: #26262f;

  /* Un trait qu'on voit : 1,39:1 sur la carte, 1,54:1 sur le fond.
     En clair il vaut 1,38:1 et l'ombre fait le reste ; ici il travaille seul. */
  --border: #31313b;
  --border-strong: #484855;

  /* Textes, mesurés sur la surface la plus claire où ils apparaissent — le
     survol, #26262f. Le tertiaire y tient 4,72:1 : sous 4,5:1, un texte
     d'aide n'aide personne, en sombre comme en clair. */
  --text: #f0f0f4;
  --text-secondary: #a9a9ba;
  --text-tertiary: #8f8fa1;
  --text-on-brand: rgba(255, 255, 255, 0.78);

  /* L'accent a deux métiers, et sur fond sombre un seul ton ne peut pas les
     faire tous les deux : un bleu assez clair pour se lire sur la carte est
     trop clair pour porter du blanc. On sépare donc le ton du texte
     (7,01:1 sur la carte) du fond des boutons pleins, qui reste celui
     du thème clair et porte du blanc à 6,49:1.

     Le fond « doux » reste à 10 % là où le clair se permet un aplat franc :
     au-delà, l'étiquette d'accent s'éclaircit assez pour que son propre texte
     repasse sous 4,5:1. Mesuré à l'écran, pas au tableau. */
  --accent: #a394ff;
  --accent-hover: #bcb1ff;
  --accent-fill: #5546d6;
  --accent-fill-hover: #4638bd;
  --accent-soft: rgba(163, 148, 255, 0.1);
  --accent-ring: rgba(163, 148, 255, 0.32);
  --accent-contrast: #ffffff;

  /* Les trois états. Sur leur propre fond translucide, chacun tient au moins
     4,99:1 ; en aplat, c'est une encre sombre qui les surmonte. */
  --success: #4ade80;
  --success-contrast: #052e16;
  --success-bg: rgba(74, 222, 128, 0.14);
  --success-border: rgba(74, 222, 128, 0.32);
  --warning: #fbbf24;
  --warning-contrast: #2a1a00;
  --warning-bg: rgba(251, 191, 36, 0.14);
  --warning-border: rgba(251, 191, 36, 0.32);
  --danger: #fb7185;
  --danger-contrast: #2b0510;
  --danger-bg: rgba(251, 113, 133, 0.14);
  --danger-border: rgba(251, 113, 133, 0.32);

  /* Une ombre noire sur un fond presque noir ne change rien : mesuré, le fond
     assombri de 22 % ne s'écarte du fond que de 1,01:1. On la garde pour le
     peu qu'elle donne sous les fenêtres flottantes, et on compte sur le trait. */
  --shadow-ink: 0 0 0;
  --radius: 10px;
  --radius-lg: 18px;
}

/* ---------- Palette « Vif » : magenta et violet ---------- */
/* Couleurs franches, navigation en dégradé violet-magenta, fonds légèrement
   teintés. Le contraste du texte reste celui d'une application de travail. */

:root[data-palette="vif"],
[data-palette-preview="vif"] {
  --brand-deep: #7c12c8;
  --brand-deep-2: #bd0f72;
  --brand-ink: #ffffff;

  --bg: #fbf6fc;
  --surface: #ffffff;
  --surface-rgb: 255 255 255;
  --surface-2: #fdf8fe;
  --surface-hover: #fbedf7;
  --border: #ecdcef;
  --border-strong: #d6bcdc;

  --text: #21102a;
  --text-secondary: #6a4675;
  /* Assez sombre pour être lu : sous 4,5:1, un texte d'aide n'aide personne.
     Mesuré sur la surface la plus claire où il apparaît. */
  --text-tertiary: #86668f;
  --text-on-brand: rgba(255, 255, 255, 0.82);

  --accent: #cc1268;
  --accent-hover: #a80d55;
  --accent-fill: var(--accent);
  --accent-fill-hover: var(--accent-hover);
  --accent-soft: #ffe8f2;
  --accent-ring: rgba(204, 18, 104, 0.26);
  --accent-contrast: #ffffff;

  --success: #067a52;
  /* blanc sur vert profond : 5,37:1 */
  --success-contrast: #ffffff;
  --success-bg: #e1f8ee;
  --success-border: #9fe4c6;
  --warning: #a8560b;
  --warning-bg: #fff0e0;
  --warning-border: #ffcd9c;
  --danger: #d2153b;
  /* blanc sur rouge profond : 5,37:1 */
  --danger-contrast: #ffffff;
  --danger-bg: #ffe9ed;
  --danger-border: #ffbcc7;

  /* La teinte de l’ombre appartient à la palette ; sa géométrie, non :
     elle est bâtie une seule fois, plus bas, à partir de cette encre. */
  --shadow-ink: 76 12 62;

  --radius: 9px;
  --radius-lg: 16px;
}

:root[data-palette="vif"][data-theme="dark"] {
  /* Vif, en sombre : des neutres teintés de prune, un magenta franc.

     Ce bloc redéfinit l'intégralité des jetons : voir l'avertissement en tête
     de fichier. Chaque rapport annoncé ici a été mesuré, pas estimé. */

  --brand-deep: #7c12c8;
  --brand-deep-2: #a1085f;
  --brand-ink: #ffffff;

  /* Quatre plans, du plus bas au plus haut. Le pas de luminance entre le fond
     et la carte vaut 1,10:1 — le même qu'en clair, car le rapport de
     contraste se tasse près du noir et n'ira pas plus loin sans délaver la
     carte. C'est donc le contour qui porte l'élévation, et non l'ombre. */
  --bg: #0d080f;
  --surface: #1c141f;
  --surface-rgb: 28 20 31;
  --surface-2: #241b28;
  --surface-hover: #2d2232;

  /* Un trait qu'on voit : 1,40:1 sur la carte, 1,54:1 sur le fond.
     En clair il vaut 1,38:1 et l'ombre fait le reste ; ici il travaille seul. */
  --border: #3b2d40;
  --border-strong: #54425b;

  /* Textes, mesurés sur la surface la plus claire où ils apparaissent — le
     survol, #2d2232. Le tertiaire y tient 4,78:1 : sous 4,5:1, un texte
     d'aide n'aide personne, en sombre comme en clair. */
  --text: #f6eff7;
  --text-secondary: #c0a8c6;
  --text-tertiary: #a288a9;
  --text-on-brand: rgba(255, 255, 255, 0.84);

  /* L'accent a deux métiers, et sur fond sombre un seul ton ne peut pas les
     faire tous les deux : un bleu assez clair pour se lire sur la carte est
     trop clair pour porter du blanc. On sépare donc le ton du texte
     (7,44:1 sur la carte) du fond des boutons pleins, qui reste celui
     du thème clair et porte du blanc à 5,45:1.

     Le fond « doux » reste à 10 % là où le clair se permet un aplat franc :
     au-delà, l'étiquette d'accent s'éclaircit assez pour que son propre texte
     repasse sous 4,5:1. Mesuré à l'écran, pas au tableau. */
  --accent: #ff7ab5;
  --accent-hover: #ff9cc8;
  --accent-fill: #cc1268;
  --accent-fill-hover: #a80d55;
  --accent-soft: rgba(255, 122, 181, 0.1);
  --accent-ring: rgba(255, 122, 181, 0.32);
  --accent-contrast: #ffffff;

  /* Les trois états. Sur leur propre fond translucide, chacun tient au moins
     4,98:1 ; en aplat, c'est une encre sombre qui les surmonte. */
  --success: #4ade80;
  --success-contrast: #052e16;
  --success-bg: rgba(74, 222, 128, 0.15);
  --success-border: rgba(74, 222, 128, 0.32);
  --warning: #fbbf24;
  --warning-contrast: #2a1a00;
  --warning-bg: rgba(251, 191, 36, 0.15);
  --warning-border: rgba(251, 191, 36, 0.32);
  --danger: #fb7185;
  --danger-contrast: #2b0510;
  --danger-bg: rgba(251, 113, 133, 0.15);
  --danger-border: rgba(251, 113, 133, 0.32);

  /* Une ombre noire sur un fond presque noir ne change rien : mesuré, le fond
     assombri de 22 % ne s'écarte du fond que de 1,01:1. On la garde pour le
     peu qu'elle donne sous les fenêtres flottantes, et on compte sur le trait. */
  --shadow-ink: 0 0 0;
  --radius: 9px;
  --radius-lg: 16px;
}

/* ---------- Base ---------- */

* { box-sizing: border-box; }

/* =========================================================================
   Élévation
   =========================================================================

   Une ombre plate d'une seule couche ne ressemble à rien de réel : la lumière
   du monde produit à la fois un contact serré sous l'objet et une diffusion
   large autour. D'où deux couches par niveau, l'une courte et dense, l'autre
   longue et ténue — c'est ce qui fait qu'une surface a l'air posée au-dessus
   d'une autre plutôt que dessinée dessus.

   Chaque palette fournit son encre (--shadow-ink) ; la géométrie est commune,
   sans quoi le thème sombre hériterait d'ombres calculées pour du papier.
   ========================================================================= */

:root {
  /* Ce que le navigateur dessine lui-même et que la feuille ne touche pas :
     la liste dépliée d'un menu, le calendrier d'un champ de date, la barre de
     défilement, le jaune du remplissage automatique. Sans cette ligne, tout
     cela reste blanc au milieu d'une page noire. */
  color-scheme: light;

  --shadow-xs:
    0 1px 2px rgb(var(--shadow-ink) / 0.06);
  --shadow-sm:
    0 1px 2px rgb(var(--shadow-ink) / 0.06),
    0 4px 12px rgb(var(--shadow-ink) / 0.06);
  --shadow-md:
    0 2px 4px rgb(var(--shadow-ink) / 0.06),
    0 12px 28px rgb(var(--shadow-ink) / 0.10);
  --shadow-lg:
    0 4px 8px rgb(var(--shadow-ink) / 0.07),
    0 24px 48px rgb(var(--shadow-ink) / 0.14);
  /* Le niveau des choses qui flottent vraiment : fenêtres, menus, panneaux. */
  --shadow-float:
    0 8px 16px rgb(var(--shadow-ink) / 0.10),
    0 32px 72px rgb(var(--shadow-ink) / 0.22);

  /* Le liseré clair au sommet d'une surface : une arête qui accroche la
     lumière, ce que fait tout objet réel et aucun rectangle plat. */
  --edge-light: inset 0 1px 0 rgb(255 255 255 / 0.6);

  /* Le trait qui tient l'arête d'une carte. Sur blanc, une encre d'ombre très
     diluée suffit et se fond dans l'ombre portée ; sur noir, il faut un vrai
     trait clair, puisque l'ombre ne porte plus. */
  --card-border: rgb(var(--shadow-ink) / 0.06);

  --radius-sm: 6px;
  --radius-pill: 999px;

  /* Verre : le fond d'une barre ou d'une fenêtre qui laisse deviner ce qu'il
     y a dessous, sans jamais laisser le texte se battre avec. */
  --glass: rgb(var(--surface-rgb) / 0.78);
  --glass-blur: saturate(180%) blur(20px);

  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
}

:root[data-theme="dark"] {
  color-scheme: dark;

  /* Sur fond sombre, une ombre ne se voit plus — mesuré : un noir à 22 % posé
     sur le fond ne s'en écarte que de 1,01:1. Ce sont donc l'arête claire et
     le contour qui séparent les plans, et l'arête monte en conséquence. */
  --edge-light: inset 0 1px 0 rgb(255 255 255 / 0.10);
  --card-border: var(--border);
}

html, body {
  margin: 0;
  padding: 0;
  /* Un aplat uniforme ne donne rien à survoler : la nappe très douce sous les
     surfaces est ce qui les fait paraître posées au-dessus de quelque chose. */
  background:
    radial-gradient(1200px 600px at 12% -8%, rgb(var(--shadow-ink) / 0.05), transparent 60%),
    var(--bg);
  background-attachment: fixed;
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 15px;
  -webkit-font-smoothing: antialiased;
}

body { min-height: 100vh; }

h1, h2, h3, h4 { margin: 0; font-weight: 600; letter-spacing: -0.01em; color: var(--text); }
h1 { font-size: 1.4rem; }
h2 { font-size: 1.2rem; }
h3 { font-size: 1rem; }
p { line-height: 1.55; margin: 0; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

.muted { color: var(--text-secondary); }
.small { font-size: 0.82rem; }
/* Une empreinte SHA-256 est une chaîne de 64 caractères sans espace : sans
   coupure autorisée, elle pousse sa carte au-delà de l'écran sur mobile. */
.mono { font-family: var(--font-mono); font-size: 0.85em; overflow-wrap: anywhere; }
.text-warning { color: var(--warning); }
.text-danger { color: var(--danger); }

/* ---------- Marque ---------- */

.brand-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius);
  background: var(--brand-deep);
  color: #fff;
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.02em;
  flex-shrink: 0;
}

.brand-text { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
.brand-name { font-weight: 600; font-size: 0.92rem; }
.brand-panel { font-size: 0.72rem; color: var(--text-tertiary); }

/* ---------- Structure : navigation latérale + contenu ---------- */

.app-shell {
  display: grid;
  grid-template-columns: var(--sidebar-width) 1fr;
  min-height: 100vh;
  /* La gouttière autour du rail : sans elle, la barre est une bande collée au
     bord de l'écran ; avec elle, c'est un objet posé sur la page. */
  gap: 14px;
  padding: 14px;
  padding-inline-end: 0;
}

.sidebar {
  background: linear-gradient(168deg, var(--brand-deep) 0%, var(--brand-deep-2) 100%);
  color: var(--brand-ink);
  display: flex;
  flex-direction: column;
  padding: 18px 12px;
  position: sticky;
  top: 14px;
  height: calc(100vh - 28px);
  padding-bottom: 14px;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-float);
  /* Vingt-cinq entrées ne tiennent pas sur l'écran d'un portable : le rail
     défile. Il ne le faisait pas auparavant — les dernières rubriques étaient
     simplement hors d'atteinte, coupées par le bas de la fenêtre. */
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgb(255 255 255 / 0.22) transparent;
}

/* La marque reste en tête pendant que les rubriques défilent sous elle. */
.sidebar-brand {
  position: sticky;
  top: 0;
  z-index: 1;
  background: linear-gradient(180deg, var(--brand-deep) 72%, transparent 100%);
}

/* Une lueur au sommet du rail : la marque prend la lumière, comme une arête. */
.sidebar::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  height: 160px;
  background: radial-gradient(120% 100% at 50% 0%, rgb(255 255 255 / 0.14), transparent 70%);
  pointer-events: none;
}
.sidebar > * { position: relative; }

.sidebar .brand-mark { background: rgba(255, 255, 255, 0.16); }
.sidebar .brand-name { color: #fff; }
.sidebar .brand-panel { color: rgba(255, 255, 255, 0.6); }

.sidebar-brand { display: flex; align-items: center; gap: 10px; padding: 4px 8px 20px; }
.sidebar-foot { flex-shrink: 0; }

.sidebar-nav { display: flex; flex-direction: column; gap: 1px; flex: 1; min-width: 0; }

.side-link {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 10px 12px;
  border: none;
  border-inline-start: 3px solid transparent;
  border-radius: 0 var(--radius) var(--radius) 0;
  background: transparent;
  color: var(--text-on-brand);
  font-family: var(--font-sans);
  font-size: 0.88rem;
  font-weight: 500;
  text-align: start;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.side-link:hover { background: rgba(255, 255, 255, 0.09); color: #fff; text-decoration: none; }
.side-link.is-active { background: rgba(255, 255, 255, 0.14); color: #fff; border-inline-start-color: #fff; font-weight: 600; }
.side-link svg { flex-shrink: 0; }
.side-link-label { flex: 1; }

.side-link-quiet { font-size: 0.83rem; color: rgba(255, 255, 255, 0.66); }

.nav-badge {
  min-width: 19px;
  height: 19px;
  padding: 0 6px;
  border-radius: 10px;
  background: #fff;
  color: var(--brand-deep);
  font-size: 0.7rem;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.sidebar-foot {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 14px;
  margin-top: 14px;
  border-top: 1px solid rgba(255, 255, 255, 0.16);
}

.app-main { display: flex; flex-direction: column; min-width: 0; }

/* ---------- Bandeau de contenu ---------- */

.app-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 24px;
  /* Du verre plutôt qu'un bandeau opaque : le contenu passe dessous et se
     devine, ce qui dit à l'œil lequel des deux plans est au-dessus. */
  background: var(--glass);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-bottom: 1px solid rgb(var(--shadow-ink) / 0.07);
  position: sticky;
  top: 0;
  z-index: 10;
}

/* Sans le repli, le verre laisse lire le texte au travers : on rend le fond
   opaque là où le navigateur ne sait pas flouter. */
@supports not (backdrop-filter: blur(1px)) {
  .app-header { background: var(--surface); }
}

.app-header-title h1 { font-size: 1.15rem; letter-spacing: -0.015em; }
.app-header-sub { font-size: 0.8rem; color: var(--text-secondary); margin-top: 1px; }
.app-header-side, .app-header-right { display: flex; align-items: center; gap: 12px; }

.user-chip { display: flex; align-items: center; gap: 9px; padding-inline-end: 12px; border-inline-end: 1px solid var(--border); }
.user-avatar {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--accent-fill);
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.7rem;
  font-weight: 700;
  flex-shrink: 0;
}
.user-meta { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.user-name { font-size: 0.83rem; font-weight: 600; }
.user-role { font-size: 0.72rem; color: var(--text-tertiary); }

/*
 * La zone de contenu se déclare « contenant » : ses grilles comptent alors
 * leurs colonnes sur la largeur qu'elles ont réellement, et non sur celle de
 * la fenêtre. C'est toute la différence quand on replie le rail — la fenêtre
 * n'a pas bougé, le contenu a gagné cent quatre-vingts pixels, et une règle
 * écrite en « largeur de fenêtre » ne peut pas le savoir.
 *
 * La largeur maximale, elle, est un confort de lecture : replier le rail est
 * justement la façon de dire qu'on veut de la place, donc elle suit.
 */
.page {
  padding: 24px 24px 72px;
  max-width: var(--page-max);
  width: 100%;
  container-type: inline-size;
  container-name: page;
}

.page-intro { margin-bottom: 18px; }
.page-intro h2 { margin-bottom: 3px; }
.page-intro p { font-size: 0.87rem; color: var(--text-secondary); }

/* ---------- Sélecteur de thème ---------- */

.theme-toggle {
  display: inline-flex;
  gap: 1px;
  padding: 2px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--surface-2);
}
.theme-opt, .theme-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 24px;
  border: none;
  border-radius: 2px;
  background: transparent;
  color: var(--text-tertiary);
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.theme-opt:hover, .theme-btn:hover { color: var(--text); }
.theme-opt.is-active, .theme-btn.is-active { background: var(--accent-fill); color: #fff; }

/* Dans le rail, le sélecteur occupe toute la largeur disponible et se lit
   comme ce qu'il est : trois choix entre lesquels on tranche. Tassé dans un
   coin, il ressemblait à un défaut d'affichage plutôt qu'à un réglage. */
.sidebar .theme-toggle {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2px;
  align-self: stretch;
  padding: 3px;
  border-color: rgba(255, 255, 255, 0.18);
  background: rgba(255, 255, 255, 0.08);
}
.sidebar .theme-opt, .sidebar .theme-btn { width: auto; height: 28px; border-radius: var(--radius-sm); color: rgba(255, 255, 255, 0.6); }
.sidebar .theme-opt:hover, .sidebar .theme-btn:hover { background: rgba(255, 255, 255, 0.1); color: var(--brand-ink); }
.sidebar .theme-opt.is-active, .sidebar .theme-btn.is-active { background: var(--brand-ink); color: var(--brand-deep); }

/* ---------- Cartes ---------- */

.card {
  background: var(--surface);
  /* En clair, une carte se détache par son ombre et le liseré ne fait que
     tenir l'arête. En sombre, l'ombre ne porte plus et le liseré devient le
     seul séparateur : le jeton change de valeur avec le thème. */
  border: 1px solid var(--card-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm), var(--edge-light);
  padding: 20px 22px;
  /* Sans cela, un tableau large impose sa largeur à la carte (min-width auto
     des éléments de grille) au lieu de défiler dans .table-wrap. */
  min-width: 0;
}
.card + .card { margin-top: 14px; }
/* Rythme vertical constant entre les blocs d'une page, quel que soit leur type. */
.page > .card,
.page > .grid-2,
.page > .stack,
.page > .tool-grid,
.page > .people-grid,
.page > .people,
.page > .mail-layout { margin-bottom: 14px; }
.page > *:last-child { margin-bottom: 0; }
.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
.card h3 { font-size: 1rem; letter-spacing: -0.01em; }
.card-sub { font-size: 0.8rem; color: var(--text-secondary); margin-top: 2px; }
.hint { margin-top: 12px; font-size: 0.78rem; color: var(--text-tertiary); line-height: 1.5; }

.grid-2 { display: grid; grid-template-columns: 360px 1fr; gap: 14px; align-items: start; }
.stack { display: grid; gap: 14px; }

/* ---------- Indicateurs ---------- */

/* Quatre colonnes quand il y a quatre chiffres, deux quand il y en a deux : la
   rangée occupe la largeur qu'on lui donne au lieu de laisser un demi-écran
   vide à droite. Le plancher de 210 px garde un nombre et son libellé sur une
   ligne lisible. */
.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 12px;
  margin-bottom: 18px;
}

.stat-card {
  background: var(--surface);
  border: 1px solid rgb(var(--shadow-ink) / 0.06);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm), var(--edge-light);
  padding: 16px 18px;
  display: flex;
  align-items: center;
  gap: 13px;
  transition: transform 0.18s var(--ease-out), box-shadow 0.18s var(--ease-out);
}
/* Le liseré d'accent devient une lueur sous la pastille : la couleur sert à
   désigner, plus à border. */
.stat-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md), var(--edge-light); }
.stat-icon {
  width: 38px;
  height: 38px;
  border-radius: var(--radius);
  background: var(--accent-soft);
  box-shadow: 0 0 0 1px rgb(var(--shadow-ink) / 0.04), 0 6px 14px var(--accent-ring);
  color: var(--accent);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.stat-body { display: flex; flex-direction: column; min-width: 0; }
.stat-value { font-size: 1.6rem; font-weight: 700; line-height: 1.1; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.stat-label { font-size: 0.76rem; color: var(--text-secondary); margin-top: 2px; }

/* Une carte de chiffre qui mène quelque part.
   Quatre nombres traités à l'identique se valent à l'œil : « 2 instances
   muettes » et « 0 archive servie » n'ont pourtant pas le même poids. Le ton
   ne suffit pas à le dire — il ne se voit pas de tout le monde —, donc la
   carte porte aussi le mot, et le chiffre mène à l'écran qui l'explique. */
a.stat-card { color: inherit; text-decoration: none; }
a.stat-card:hover .stat-label { color: var(--text); }
a.stat-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.stat-card.is-warning,
.stat-card.is-danger { border-inline-start-width: 3px; border-inline-start-style: solid; }
.stat-card.is-warning { border-inline-start-color: var(--warning); }
.stat-card.is-warning .stat-value { color: var(--warning); }
.stat-card.is-danger { border-inline-start-color: var(--danger); }
.stat-card.is-danger .stat-value { color: var(--danger); }
/* Le mot qui double la couleur : « à traiter », « en retard ». Il tient sur la
   même ligne que le libellé et ne dépend d'aucune teinte pour se lire. */
.stat-note { font-size: 0.72rem; font-weight: 600; color: var(--text-secondary); }

/* Un groupe de rubriques, et son titre qui les plie.

   Deux métiers cohabitent dans ce rail — ce qui tourne chez les clients, et ce
   qui se vend depuis le site — et rien ne le disait : les rubriques se
   suivaient comme si elles parlaient de la même chose. Le titre est maintenant
   un bouton : on replie le métier dont on ne s'occupe pas, et le rail tient en
   trois lignes au lieu de se relire en entier à chaque visite.

   Replié, le rail n'a plus la place d'un mot : le titre laisse alors un simple
   trait, qui sépare sans nommer, et les rubriques restent visibles — un rail
   d'icônes derrière un pli fermé serait une impasse. */
.side-group { margin: 12px 0 2px; }
.sidebar-nav > .side-group:first-child { margin-top: 0; }

.side-group-tete {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  margin: 0 0 4px;
  padding: 4px 10px;
  border: none;
  background: transparent;
  color: rgba(255, 255, 255, 0.45);
  font-family: var(--font-sans);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-align: start;
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: color 0.12s ease, background 0.12s ease;
}
.side-group-tete:hover { color: rgba(255, 255, 255, 0.78); background: rgba(255, 255, 255, 0.06); }
.side-group-tete:focus-visible { outline: 2px solid var(--brand-ink); outline-offset: 2px; }
.side-group-nom { flex: 1; }
/* Le chevron pointe vers le bas quand le groupe est ouvert, vers la rubrique
   quand il est fermé : la direction dit ce qui se passera au clic. */
.side-group-chevron { flex-shrink: 0; transition: transform 0.16s var(--ease-out); }

.side-group-liste { display: flex; flex-direction: column; gap: 1px; }

/* Le pli, posé sur <html> avant le premier rendu. Une règle par groupe : il y
   en a deux, et les nommer vaut mieux qu'un sélecteur qui devinerait. Le pli ne
   vaut que rail déplié — replié, le rail n'a plus de titre à cliquer, et un
   groupe fermé y cacherait des icônes sans rien pour les rouvrir. */
:root:not([data-rail="replie"])[data-groupes-fermes~="cms"] [data-groupe="cms"] > .side-group-liste,
:root:not([data-rail="replie"])[data-groupes-fermes~="site"] [data-groupe="site"] > .side-group-liste,
:root:not([data-rail="replie"])[data-groupes-fermes~="reglages"] [data-groupe="reglages"] > .side-group-liste {
  display: none;
}
[data-groupes-fermes~="cms"] [data-groupe="cms"] > .side-group-tete .side-group-chevron,
[data-groupes-fermes~="site"] [data-groupe="site"] > .side-group-tete .side-group-chevron,
[data-groupes-fermes~="reglages"] [data-groupe="reglages"] > .side-group-tete .side-group-chevron {
  transform: rotate(-90deg);
}

/* Six chiffres tiennent mal sur une rangée qui se remplit toute seule : cinq
   en haut, un orphelin en dessous. On les range donc par diviseurs de six —
   trois par trois quand la place le permet, six de front quand elle abonde —
   et jamais quatre, qui laisserait deux orphelins.

   Mesuré sur le contenu, pas sur la fenêtre : replier le rail rend cent
   quatre-vingts pixels, et c'est exactement ce qui fait passer une rangée de
   trois à six. Le contenu se réorganise au lieu de simplement s'étirer.

   Sans prise en charge des requêtes de contenant, rien de tout cela ne
   s'applique et la rangée garde son compte automatique : plus dense, jamais
   cassée. */
@container page (min-width: 860px) {
  .stats-grid.stats-trois { grid-template-columns: repeat(3, 1fr); }
}
@container page (min-width: 1200px) {
  .stats-grid.stats-trois { grid-template-columns: repeat(6, 1fr); }
}
[data-rail="replie"] .side-group-tete {
  padding: 0;
  margin-inline: 8px;
  border-top: 1px solid rgba(255, 255, 255, 0.16);
  color: transparent;
  overflow: hidden;
  height: 1px;
  margin-block: 12px 8px;
  cursor: default;
}
[data-rail="replie"] .side-group-tete:hover { background: transparent; }
[data-rail="replie"] .sidebar-nav > .side-group:first-child > .side-group-tete { margin-block-start: 0; }

/* ---------- Raccourcis du tableau de bord ----------

   Des gestes posés à plat, par famille, comme le fait un panneau d'hébergeur :
   on cherche « ce que je veux faire », pas « où c'est rangé ». La tuile porte
   donc un verbe, son icône, et une ligne qui dit ce qu'elle fait — sans quoi
   une grille d'icônes oblige à cliquer pour savoir.

   Les colonnes se comptent toutes seules : quatre sur un écran large, une sur
   un téléphone, sans point de rupture à maintenir. Et comme le compte se fait
   sur la largeur disponible, replier le rail en ajoute une — c'est la même
   règle qui sert les deux cas, il n'y a rien à tenir à jour. */
.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(232px, 1fr));
  gap: 10px;
}

.tile {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  padding: 13px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
  color: inherit;
  text-decoration: none;
  transition: border-color 0.14s var(--ease-out), background 0.14s var(--ease-out),
              transform 0.14s var(--ease-out);
}
.tile:hover {
  background: var(--surface-hover);
  border-color: var(--accent);
  transform: translateY(-1px);
  /* Une tuile se survole comme une carte : le soulignement des liens de texte
     y ferait deux traits sous deux lignes, et rien de plus lisible. */
  text-decoration: none;
}
.tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* La pastille d'icône : assez colorée pour situer la tuile d'un coup d'œil,
   assez discrète pour ne pas transformer la grille en vitrine. */
.tile-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  flex-shrink: 0;
  border-radius: var(--radius);
  background: var(--accent-soft);
  color: var(--accent);
}
.tile:hover .tile-icon { background: var(--accent-fill); color: var(--accent-contrast); }

.tile-text { display: flex; flex-direction: column; min-width: 0; gap: 2px; }
.tile-name { font-size: 0.87rem; font-weight: 600; }
/* Une ligne d'explication, et une seule : au-delà, ce n'est plus un raccourci
   mais un paragraphe. Elle se coupe proprement plutôt que de pousser la tuile. */
.tile-what {
  font-size: 0.76rem;
  color: var(--text-secondary);
  overflow-wrap: anywhere;
}

@media (prefers-reduced-motion: reduce) {
  .tile, .tile:hover { transition: none; transform: none; }
}

/* ---------- Onglets (repli mobile de la navigation) ---------- */

.tab-panel { display: none; }
.tab-panel.is-active { display: block; }

/* Un libellé réservé aux lecteurs d'écran. Sans cette règle, « sr-only » ne
   masque rien : le mot s'affiche en toutes lettres à côté de l'icône qu'il
   était censé nommer. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* L'attribut « hidden » ne vaut qu'un « display: none » d'origine, que la
   moindre règle de composant écrase — un bouton masqué par hidden restait
   visible parce que « .btn » lui impose un display. Ici, il tranche. */
[hidden] { display: none !important; }

/* ---------- Formulaires ---------- */

label { display: flex; flex-direction: column; gap: 5px; font-size: 0.8rem; font-weight: 500; color: var(--text-secondary); }

input, select, textarea {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  padding: 9px 12px;
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 0.88rem;
  outline: none;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
input::placeholder, textarea::placeholder { color: var(--text-tertiary); }
input:hover, select:hover, textarea:hover { border-color: var(--text-tertiary); }
input:focus, select:focus, textarea:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring), 0 2px 8px var(--accent-ring);
}
textarea { resize: vertical; }

.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.form-grid .span-2 { grid-column: 1 / -1; }
/* Un champ qui tient toute la largeur d'un écran de bureau se saisit mal : le
   curseur part à un mètre du libellé, et rien n'y dit combien on en attend.
   Une raison sociale ou une description tiennent dans soixante signes ; le
   champ s'arrête donc là, et le reste de la ligne demeure vide exprès. */
.form-grid .span-2 > input[type="text"],
.form-grid .span-2 > input:not([type]),
.form-grid .span-2 > textarea { max-inline-size: 62ch; }
.form-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* Un bouton collé à la dernière ligne d'une liste en fait partie à l'œil. */
.summary-list + .form-actions { margin-top: 14px; }

/* ---------- Le choix des sections d'un administrateur ----------

   Des cases groupées comme le rail les range : on coche ce qu'on reconnaît de
   l'écran, pas une liste de mots plate. Chaque case dit aussi ce que la section
   ouvre — « Licences » ne dit pas si l'on peut révoquer, la ligne en dessous le
   dit. */
.sections-choix { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 12px; margin: 10px 0 14px; }
.sections-groupe { border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px 12px; margin: 0; min-width: 0; }
.sections-groupe legend {
  padding: 0 6px;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}
.check { display: flex; align-items: flex-start; gap: 9px; padding: 6px 0; cursor: pointer; }
.check input { width: 16px; height: 16px; margin: 2px 0 0; flex-shrink: 0; accent-color: var(--accent); }
.check > span { display: flex; flex-direction: column; font-size: 0.85rem; min-width: 0; }
.check .cell-sub { margin-top: 1px; }
.field-label { display: block; font-size: 0.8rem; font-weight: 600; color: var(--text-secondary); }
/* « Tableau de bord, Licences, Journal » sur une colonne étroite se coupe mot à
   mot et devient illisible : la colonne se réserve de quoi tenir deux noms. */
.cell-sections { min-width: 160px; }
/* Un intitulé de colonne tient sur une ligne par défaut, ce qui vaut mieux —
   sauf quand il est à lui seul plus large que sa colonne et repousse le
   tableau hors de l'écran. Celui-là s'autorise deux lignes. */
thead th.th-wrap { white-space: normal; }

/* ---------- Sections qu'on déplie ----------

   Un écran de gestion sert d'abord à regarder ce qui existe ; on n'y crée
   qu'ensuite. Deux formulaires ouverts en permanence repoussaient la liste des
   licences sous la ligne de flottaison. Ils se replient donc, et se rouvrent
   d'eux-mêmes quand la saisie a échoué — au moment précis où on en a besoin. */
.disclosure { border-top: 1px solid var(--border); margin-top: 4px; padding-top: 4px; }
.disclosure > summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 2px;
  cursor: pointer;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--accent);
  list-style: none;
  border-radius: var(--radius-sm);
}
.disclosure > summary::-webkit-details-marker { display: none; }
/* Le chevron tourne d'un quart de tour : la flèche dit l'état, le mot dit le
   reste. Pas d'animation quand le système la refuse. */
.disclosure > summary::before {
  content: '';
  width: 7px;
  height: 7px;
  border-inline-end: 2px solid currentColor;
  border-block-end: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 0.16s var(--ease-out);
}
.disclosure[open] > summary::before { transform: rotate(45deg); }
.disclosure > summary:hover { color: var(--accent-hover); }
.disclosure > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.disclosure > .form-grid { margin-top: 6px; margin-bottom: 8px; }
@media (prefers-reduced-motion: reduce) {
  .disclosure > summary::before { transition: none; }
}

/* ---------- Boutons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  padding: 8px 15px;
  border-radius: var(--radius);
  font-family: var(--font-sans);
  font-size: 0.84rem;
  font-weight: 600;
  line-height: 1.2;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  box-shadow: var(--shadow-xs), var(--edge-light);
  transition:
    background 0.14s var(--ease-out),
    border-color 0.14s var(--ease-out),
    color 0.14s var(--ease-out),
    box-shadow 0.14s var(--ease-out),
    transform 0.14s var(--ease-out);
}
.btn:hover {
  background: var(--surface-hover);
  border-color: var(--text-tertiary);
  box-shadow: var(--shadow-sm), var(--edge-light);
  transform: translateY(-1px);
  text-decoration: none;
}
.btn:active { transform: translateY(0); box-shadow: var(--shadow-xs); }
.btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-ring); }

.btn-primary {
  background: linear-gradient(180deg, var(--accent-fill) 0%, var(--accent-fill-hover) 100%);
  border-color: var(--accent-fill-hover);
  color: var(--accent-contrast);
  box-shadow:
    0 1px 2px rgb(var(--shadow-ink) / 0.14),
    0 6px 16px var(--accent-ring),
    inset 0 1px 0 rgb(255 255 255 / 0.22);
}
.btn-primary:hover {
  background: linear-gradient(180deg, var(--accent-hover) 0%, var(--accent-hover) 100%);
  border-color: var(--accent-hover);
  color: var(--accent-contrast);
  box-shadow:
    0 2px 4px rgb(var(--shadow-ink) / 0.16),
    0 10px 24px var(--accent-ring),
    inset 0 1px 0 rgb(255 255 255 / 0.22);
}

.btn-outline { background: transparent; border-color: var(--accent); color: var(--accent); }
.btn-outline:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }

.btn-quiet, .btn-ghost { background: transparent; border-color: transparent; color: var(--text-secondary); box-shadow: none; }
.btn-quiet:hover, .btn-ghost:hover { background: var(--surface-hover); border-color: var(--border); color: var(--text); box-shadow: var(--shadow-xs); }

.btn-danger { color: var(--danger); border-color: var(--danger-border); background: transparent; box-shadow: none; }
.btn-danger:hover { background: var(--danger-bg); border-color: var(--danger); color: var(--danger); }

.btn-sm { padding: 5px 11px; font-size: 0.77rem; border-radius: var(--radius-sm); }
.btn-block { width: 100%; }

/* ---------- Messages ---------- */

.flash {
  display: flex;
  gap: 10px;
  padding: 11px 14px;
  border: 1px solid;
  border-inline-start-width: 3px;
  border-radius: var(--radius);
  font-size: 0.85rem;
  margin-bottom: 16px;
}
.flash-success { background: var(--success-bg); border-color: var(--success-border); border-inline-start-color: var(--success); color: var(--success); }
.flash-error { background: var(--danger-bg); border-color: var(--danger-border); border-inline-start-color: var(--danger); color: var(--danger); }

.banner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 14px;
  border: 1px solid var(--border);
  border-inline-start: 3px solid var(--accent);
  border-radius: var(--radius);
  background: var(--surface);
  font-size: 0.85rem;
  margin-bottom: 16px;
}
.banner svg { flex-shrink: 0; color: var(--accent); }
.banner span { flex: 1; }
.banner.is-warning { border-inline-start-color: var(--warning); background: var(--warning-bg); color: var(--warning); }
.banner.is-warning svg { color: var(--warning); }
.banner.is-danger { border-inline-start-color: var(--danger); background: var(--danger-bg); color: var(--danger); }
.banner.is-danger svg { color: var(--danger); }

/* ---------- Tableaux ---------- */

/* Un tableau large a le droit de défiler, et il doit le dire : les deux ombres
   posées sur les bords ne s'affichent que du côté où il reste quelque chose à
   voir. C'est le vieux tour des fonds attachés au contenu — deux dégradés qui
   suivent le défilement, deux qui restent collés aux bords et les recouvrent
   quand on est en butée. Aucun script, et rien à recalculer au redimensionnement. */
.table-wrap {
  overflow-x: auto;
  margin: 0 -22px -20px;
  background:
    linear-gradient(to right, var(--surface) 30%, rgb(var(--surface-rgb) / 0)) left center,
    linear-gradient(to left, var(--surface) 30%, rgb(var(--surface-rgb) / 0)) right center,
    linear-gradient(to right, rgb(var(--shadow-ink) / 0.10), rgb(var(--shadow-ink) / 0)) left center,
    linear-gradient(to left, rgb(var(--shadow-ink) / 0.10), rgb(var(--shadow-ink) / 0)) right center;
  background-repeat: no-repeat;
  background-size: 34px 100%, 34px 100%, 12px 100%, 12px 100%;
  background-attachment: local, local, scroll, scroll;
}
/* Le retrait du bas ne vaut que si le tableau termine la carte. Sinon il fait
   remonter ce qui suit par-dessus la dernière ligne. */
.table-wrap:not(:last-child) { margin-bottom: 10px; }
/* Quand il la termine, son fond doit s'arrêter là où la carte s'arrondit :
   sinon les deux coins du bas sont remplis d'un carré clair qui déborde de la
   carte — invisible en thème clair, franc en thème sombre. */
.table-wrap:last-child {
  border-end-start-radius: var(--radius-lg);
  border-end-end-radius: var(--radius-lg);
}
.table-wrap table { min-width: 100%; }

/* Sous 720 px, un tableau à six colonnes ne rétrécit pas : il se casse. Une
   référence de licence y passait sur quatre lignes et « 12 salariés » sur
   trois. On lui rend donc une largeur de travail et c'est le cadre qui défile —
   une ligne qu'on fait glisser se lit, une ligne hachée ne se lit pas. */
@media (max-width: 720px) {
  .table-wrap table { min-width: 620px; }
}

table { width: 100%; border-collapse: collapse; }

thead th {
  text-align: start;
  padding: 10px 12px;
  background: transparent;
  border-top: none;
  border-bottom: 1px solid rgb(var(--shadow-ink) / 0.08);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-secondary);
  white-space: nowrap;
}
thead th:first-child { padding-inline-start: 20px; }
thead th:last-child { padding-inline-end: 20px; }

td.no-wrap, th.no-wrap { white-space: nowrap; }

tbody td {
  padding: 13px 12px;
  border-bottom: 1px solid rgb(var(--shadow-ink) / 0.05);
  font-size: 0.85rem;
  vertical-align: middle;
}
tbody td:first-child { padding-inline-start: 20px; }
tbody td:last-child { padding-inline-end: 20px; }
tbody tr { transition: background 0.12s var(--ease-out); }
tbody tr:hover { background: var(--surface-hover); }
tbody tr:last-child td { border-bottom: none; }

.cell-strong { font-weight: 600; }
.cell-sub { font-size: 0.76rem; color: var(--text-tertiary); }
/* Dans une cellule, la précision passe à la ligne au lieu de suivre le texte
   principal. Collée derrière lui, elle le poussait à se couper n'importe où :
   « Mairie de / Vaux Node / · en ligne » sur trois lignes, au lieu du nom sur
   une ligne et sa précision sur la suivante. */
td .cell-sub { display: block; }
/* Une colonne de montants se lit par la droite : les unités sous les unités,
   les dizaines sous les dizaines. Alignée à gauche, il faut relire chaque
   nombre en entier pour le comparer au précédent.

   Et elle ne se coupe jamais : « 11 520,00 € » passé à la ligne devient deux
   nombres à l'œil, ce qui est pire que de faire défiler le tableau. */
.num,
th.num {
  text-align: end;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* Le commentaire sous un montant — le cours du jour, la période — suit son
   nombre plutôt que de repartir à gauche. */
.num .cell-sub { text-align: end; }

/* La colonne d'actions garde une largeur plancher : les boutons se répartissent
   sur deux lignes au lieu de s'empiler un par ligne ou de sortir du cadre. */
.actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }

/* L'en-tête d'une colonne d'actions n'a rien à disposer : il ne porte qu'un
   libellé, souvent aucun. En faire une boîte flexible le réduisait à zéro de
   haut quand il était vide, et le filet du haut du tableau s'arrêtait net au
   milieu de la largeur. */
th.actions { display: table-cell; }

/* La cellule d'actions, elle, reste une cellule.
   En boîte flexible, elle sortait du modèle du tableau : sa largeur ne suivait
   plus celle de sa colonne, et son filet du bas ne tombait pas à la même
   hauteur que celui des autres cellules — le trait de séparation s'arrêtait
   donc au milieu de la ligne, juste avant les boutons. Les boutons se rangent
   maintenant par un alignement en ligne, ce qui ne demande rien au tableau. */
td.actions {
  display: table-cell;
  min-width: 196px;
  text-align: end;
  white-space: nowrap;
}

/* Au-delà de quatre gestes, la ligne déborde et l'on perd les derniers hors du
   cadre. Ils passent alors à la ligne — mieux vaut une cellule sur deux hauteurs
   qu'un bouton qu'on ne voit pas. La classe est posée par la vue, qui sait
   combien de gestes elle offre. */
td.actions.est-fournie {
  white-space: normal;
  min-width: 260px;
}
td.actions.est-fournie > * { margin-block: 2px; }
td.actions > * {
  display: inline-flex;
  gap: 6px;
  align-items: center;
  vertical-align: middle;
}
td.actions > * + * { margin-inline-start: 6px; }

/* Quand la liste de statut a rejoint sa colonne, il ne reste ici qu'un bouton :
   la largeur réservée aux deux creuserait un vide entre le statut et lui. */
td.actions.est-allegee { min-width: 0; }
/* Une liste déroulante et son bouton forment un geste : les séparer sur deux
   lignes double la hauteur de chaque ligne du tableau pour rien. La colonne
   prend la largeur qu'il lui faut ; si l'écran ne l'a pas, c'est le tableau
   qui défile, ce qui se comprend mieux qu'une ligne haute de deux étages. */
td.actions .inline-form { flex-wrap: nowrap; }

/* Dans une ligne de tableau, cinq boutons bordés côte à côte font un mur : on
   ne lit plus la ligne, on lit les boutons. Ils s'effacent donc jusqu'au
   survol de leur ligne — sauf celui qui détruit, qui reste reconnaissable. */
td.actions .btn {
  padding: 5px 10px;
  font-size: 0.78rem;
  border-radius: var(--radius-sm);
  border-color: transparent;
  background: transparent;
  color: var(--text-secondary);
  box-shadow: none;
}
tbody tr:hover td.actions .btn {
  background: var(--surface);
  border-color: var(--border);
  color: var(--text);
}
td.actions .btn:hover {
  background: var(--surface-hover);
  border-color: var(--text-tertiary);
  color: var(--text);
  box-shadow: var(--shadow-xs);
}
td.actions .btn-danger,
tbody tr:hover td.actions .btn-danger { color: var(--danger); background: transparent; border-color: transparent; }
td.actions .btn-danger:hover { background: var(--danger-bg); border-color: var(--danger-border); color: var(--danger); }
td.actions .btn-primary,
tbody tr:hover td.actions .btn-primary { color: var(--accent-contrast); }

/* Sans souris, rien ne se survole : le clavier et le tactile doivent voir les
   mêmes actions que la souris. */
td.actions:focus-within .btn { background: var(--surface); border-color: var(--border); color: var(--text); }
@media (hover: none) {
  td.actions .btn { background: var(--surface); border-color: var(--border); color: var(--text); }
}

/* ---------- Étiquettes et états ---------- */

.tag {
  display: inline-block;
  padding: 3px 10px;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  box-shadow: inset 0 0 0 1px rgb(var(--shadow-ink) / 0.06);
  font-size: 0.73rem;
  font-weight: 500;
  color: var(--text-secondary);
  white-space: nowrap;
}
.tag-accent { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); font-weight: 600; }
/* Variantes de statut : une criticité, un retard ou une note interne doivent se
   voir sans lire l'étiquette. */
.tag-success { background: var(--success-bg); border-color: var(--success-border); color: var(--success); font-weight: 600; }
.tag-warning { background: var(--warning-bg); border-color: var(--warning-border); color: var(--warning); font-weight: 600; }
.tag-danger { background: var(--danger-bg); border-color: var(--danger-border); color: var(--danger); font-weight: 600; }

/* Les pastilles du centre de journaux. Les variantes de couleur existaient
   — .chip-alerte, .chip-critique… — mais pas la forme : les libellés et leurs
   compteurs se lisaient en une seule phrase courante, séparés de points
   médians. Une pastille est un lien, d'où l'état de survol. */
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  font-size: 0.73rem;
  font-weight: 500;
  color: var(--text-secondary);
  white-space: nowrap;
}
.chip:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }

/* Niveaux du centre de journaux : les huit sévérités de la norme syslog. Trois
   familles suffisent à l'œil — ce qui saigne, ce qui inquiète, ce qui passe —
   et le libellé dit le reste. */
.tag-urgence, .tag-alerte, .tag-critique, .tag-erreur,
.chip-urgence, .chip-alerte, .chip-critique, .chip-erreur {
  background: var(--danger-bg); border-color: var(--danger-border); color: var(--danger); font-weight: 600;
}
.tag-avertissement, .chip-avertissement {
  background: var(--warning-bg); border-color: var(--warning-border); color: var(--warning); font-weight: 600;
}
.tag-remarquable, .chip-remarquable { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.tag-debogage, .chip-debogage { color: var(--text-tertiary); }
.tag-on { background: var(--success-bg); border-color: var(--success-border); color: var(--success); font-weight: 600; }
.tag-off { color: var(--text-tertiary); }

.status {
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 9px;
  border-radius: 3px;
  border: 1px solid;
  font-size: 0.73rem;
  font-weight: 600;
  white-space: nowrap;
}
.status::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
.status-on { color: var(--success); background: var(--success-bg); border-color: var(--success-border); }
.status-off { color: var(--text-tertiary); background: var(--surface-2); border-color: var(--border); }
.status-warning { color: var(--warning); background: var(--warning-bg); border-color: var(--warning-border); }
.status-danger { color: var(--danger); background: var(--danger-bg); border-color: var(--danger-border); }

.filter-pills { display: flex; gap: 6px; flex-wrap: wrap; }
.filter-pills a { text-decoration: none; }
.filter-pills a:hover { text-decoration: none; border-color: var(--accent); color: var(--accent); }

.empty-state {
  padding: 30px 16px;
  text-align: center;
  color: var(--text-tertiary);
  background: var(--surface-2);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
  font-size: 0.86rem;
}

.inline-form { display: flex; align-items: center; gap: 6px; flex-wrap: nowrap; }
.input-sm { width: 96px; padding: 5px 8px; font-size: 0.78rem; }

/* ---------- Connexion ---------- */

.auth-body { display: flex; flex-direction: column; min-height: 100vh; background: var(--bg); }

.auth-top { display: flex; align-items: center; justify-content: space-between; padding: 18px 24px; }
.auth-top .brand-mark { width: 30px; height: 30px; font-size: 0.72rem; }
.auth-brand { display: flex; align-items: center; gap: 10px; }

.auth-main { flex: 1; display: flex; align-items: center; justify-content: center; padding: 20px; }

.auth-card {
  width: 100%;
  max-width: 400px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-top: 3px solid var(--brand-deep);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  padding: 32px 30px;
}
.auth-card h1 { font-size: 1.3rem; margin-bottom: 5px; }
.auth-card .auth-sub { font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 22px; }
.auth-form { display: flex; flex-direction: column; gap: 14px; }
.auth-foot { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border); font-size: 0.76rem; color: var(--text-tertiary); line-height: 1.5; }

.error-page { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; padding: 40px 20px; text-align: center; }

/* ---------- Espace membre : outils ---------- */

.tool-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(272px, 1fr)); gap: 12px; }

.tool-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 11px;
}
.tool-card-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.tool-icon {
  width: 34px;
  height: 34px;
  border-radius: var(--radius);
  background: var(--accent-soft);
  color: var(--accent);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.tool-name { font-size: 0.95rem; font-weight: 600; }
.tool-ref { font-size: 0.76rem; color: var(--text-tertiary); }

.cred-box { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-2); padding: 9px 11px; display: flex; flex-direction: column; gap: 7px; }
.cred-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: 0.79rem; }
.cred-row .k { color: var(--text-tertiary); }
.cred-row .v { font-family: var(--font-mono); font-size: 0.78rem; font-weight: 600; overflow-wrap: anywhere; }

.tool-note { font-size: 0.79rem; color: var(--text-secondary); }
.tool-foot { margin-top: auto; padding-top: 4px; font-size: 0.73rem; color: var(--text-tertiary); }

/* ---------- Pointage ---------- */

.timer-display {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 2.4rem;
  font-weight: 700;
  text-align: center;
  padding: 14px 0 18px;
  letter-spacing: 0.02em;
}
.timer-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); }
.timer-stats > div { display: flex; flex-direction: column; gap: 2px; }
.timer-stats .stat-value { font-size: 1.05rem; }

/* ---------- Assistant d'installation ---------- */

/* La scène : la fenêtre a quelque chose derrière elle, et c'est ce quelque
   chose qui la fait flotter. Deux halos aux couleurs de la palette, très
   étalés, posés sur le fond — pas une image, rien à charger, et ils suivent le
   thème sans qu'on s'en occupe. */
.install-scene {
  min-height: 100vh;
  min-height: 100dvh;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 28px 20px;
  background:
    radial-gradient(1100px 620px at 12% -10%, rgb(var(--shadow-ink) / 0.10), transparent 60%),
    radial-gradient(900px 560px at 108% 110%, rgb(var(--shadow-ink) / 0.09), transparent 62%),
    var(--bg);
}

.install-window {
  position: relative;
  width: 100%;
  max-width: 1000px;
  display: grid;
  grid-template-columns: 286px 1fr;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: calc(var(--radius-lg) + 6px);
  box-shadow: var(--shadow-float);
  overflow: hidden;
  animation: install-pose 0.42s cubic-bezier(0.22, 1, 0.36, 1) both;
}

@keyframes install-pose {
  from { opacity: 0; transform: translateY(14px) scale(0.985); }
  to { opacity: 1; transform: none; }
}

/* La barre d'avancement coiffe la fenêtre entière, rail compris : elle parle
   de l'installation, pas de la colonne de droite. */
.install-progress {
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  height: 3px;
  background: var(--border);
  z-index: 2;
}
.install-progress-fill {
  display: block;
  height: 100%;
  width: 0;
  background: linear-gradient(90deg, var(--brand-deep), var(--accent));
  transition: width 0.55s cubic-bezier(0.22, 1, 0.36, 1);
}
.install-progress-fill[data-at="0"] { width: 0%; }
.install-progress-fill[data-at="1"] { width: 17%; }
.install-progress-fill[data-at="2"] { width: 33%; }
.install-progress-fill[data-at="3"] { width: 50%; }
.install-progress-fill[data-at="4"] { width: 67%; }
.install-progress-fill[data-at="5"] { width: 83%; }
.install-progress-fill[data-at="6"] { width: 100%; }

/* Le rail bleu, le même que celui du produit : on installe le logiciel qu'on
   voit déjà. */
.install-aside {
  padding: 26px 20px 22px;
  background: linear-gradient(168deg, var(--brand-deep) 0%, var(--brand-deep-2) 100%);
  color: var(--brand-ink);
  display: flex;
  flex-direction: column;
}
.install-brand { display: flex; align-items: center; gap: 10px; margin-bottom: 26px; }
.install-brand .brand-mark {
  width: 32px;
  height: 32px;
  font-size: 0.74rem;
  background: rgba(255, 255, 255, 0.14);
  color: var(--brand-ink);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22);
}
.install-brand .brand-text { display: flex; flex-direction: column; line-height: 1.25; }
.install-brand .brand-name { font-size: 0.92rem; font-weight: 600; }
.install-brand .brand-panel { font-size: 0.7rem; opacity: 0.7; }

.install-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }

/* Le trait qui relie les étapes : il dit qu'elles se suivent, ce qu'une simple
   liste de puces ne dit pas. */
.install-step {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 9px 10px;
  border-radius: var(--radius);
  transition: background 0.2s ease;
}
.install-step::before {
  content: '';
  position: absolute;
  inset-inline-start: 23px;
  top: 34px;
  bottom: -2px;
  width: 1px;
  background: rgba(255, 255, 255, 0.16);
}
.install-step:last-child::before { display: none; }

.step-badge {
  position: relative;
  z-index: 1;
  width: 27px;
  height: 27px;
  flex-shrink: 0;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.10);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.18);
  transition: background 0.25s ease, color 0.25s ease, box-shadow 0.25s ease;
}
.step-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; padding-top: 3px; }
.step-name { font-size: 0.83rem; font-weight: 500; }
.step-hint { font-size: 0.7rem; opacity: 0.62; line-height: 1.35; }

.install-step.is-todo { opacity: 0.55; }
.install-step.is-done .step-badge { background: var(--brand-ink); color: var(--brand-deep); box-shadow: none; }
.install-step.is-current {
  background: rgba(255, 255, 255, 0.10);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.14);
}
.install-step.is-current .step-badge {
  background: var(--brand-ink);
  color: var(--brand-deep);
  box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.14);
}

.install-aside-foot {
  margin: auto 0 0;
  padding-top: 18px;
  font-size: 0.7rem;
  line-height: 1.5;
  opacity: 0.6;
}

/* ---------- La colonne de droite ---------- */

.install-stage { padding: 34px 36px 30px; display: flex; flex-direction: column; }
.stage-head { margin-bottom: 22px; }
.stage-count {
  margin: 0 0 6px;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--accent);
}
.install-stage h1 { font-size: 1.34rem; margin: 0 0 5px; }
.stage-sub { font-size: 0.83rem; color: var(--text-secondary); margin: 0; line-height: 1.55; max-width: 54ch; }

.install-form { display: flex; flex-direction: column; flex: 1; }
.install-field { display: flex; flex-direction: column; gap: 5px; margin-top: 18px; }
.field-hint { font-size: 0.74rem; font-weight: 400; color: var(--text-tertiary); line-height: 1.45; margin: 0; }

.install-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  margin-top: auto;
  padding-top: 24px;
}
.install-note { font-size: 0.76rem; color: var(--text-tertiary); margin: 16px 0 0; }
.install-warn {
  margin: 16px 0 0;
  padding: 10px 13px;
  border-radius: var(--radius);
  background: var(--warning-bg);
  color: var(--warning);
  font-size: 0.78rem;
  line-height: 1.5;
}

/* ---------- Les choix : langue, moteur ---------- */

/* Seize langues : une grille de vignettes se parcourt d'un regard, là où une
   liste déroulante demande de l'ouvrir pour savoir ce qu'elle contient. */
.choice-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(146px, 1fr));
  gap: 8px;
}
.choice {
  position: relative;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  background: var(--surface);
  transition: border-color 0.16s ease, background 0.16s ease, transform 0.16s ease;
}
.choice:hover { border-color: var(--accent); transform: translateY(-1px); }
.choice input { position: absolute; opacity: 0; width: 0; height: 0; }
.choice-flag { font-size: 1.05rem; line-height: 1; }
.choice-label { font-size: 0.82rem; font-weight: 500; }
.choice.is-picked {
  border-color: var(--accent);
  background: var(--accent-soft);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.choice:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }

.engine-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 12px; }
.engine {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 15px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  background: var(--surface);
  transition: border-color 0.16s ease, background 0.16s ease, transform 0.16s ease;
}
.engine:hover { border-color: var(--accent); transform: translateY(-1px); }
.engine input { position: absolute; opacity: 0; width: 0; height: 0; }
.engine-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.engine-name { font-size: 0.9rem; font-weight: 600; }
.engine-tag {
  padding: 1px 6px;
  border-radius: 3px;
  background: var(--success-bg);
  color: var(--success);
  font-size: 0.66rem;
  font-weight: 600;
}
.engine-tag.is-off { background: var(--surface-hover); color: var(--text-tertiary); }
.engine-sub { font-size: 0.76rem; color: var(--text-tertiary); line-height: 1.45; }
.engine.is-picked { border-color: var(--accent); background: var(--accent-soft); box-shadow: inset 0 0 0 1px var(--accent); }
.engine.is-off { opacity: 0.55; cursor: not-allowed; }
.engine.is-off:hover { border-color: var(--border); transform: none; }
.engine:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }

.mysql-fields { border: none; margin: 18px 0 0; padding: 0; }

/* ---------- La pose ---------- */

.install-run { display: flex; flex-direction: column; flex: 1; }

.recap { margin: 0; display: flex; flex-direction: column; gap: 2px; }
.recap-row {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}
.recap-row:last-child { border-bottom: none; }
.recap dt { margin: 0; font-size: 0.8rem; color: var(--text-secondary); }
.recap dd { margin: 0; font-size: 0.84rem; font-weight: 600; text-align: end; overflow-wrap: anywhere; }

.run-steps { list-style: none; margin: 20px 0 0; padding: 0; display: flex; flex-direction: column; gap: 3px; }
.run-step {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  border-radius: var(--radius);
  background: var(--surface-2);
  border: 1px solid transparent;
  opacity: 0.6;
  transition: opacity 0.25s ease, background 0.25s ease, border-color 0.25s ease;
}
.run-label { font-size: 0.84rem; font-weight: 500; }
.run-said { margin-inline-start: auto; font-size: 0.76rem; color: var(--text-tertiary); text-align: end; }

/* Le curseur : un anneau qui tourne tant que le palier n'a pas répondu. Il
   tourne parce que le serveur travaille, et il s'arrête parce qu'il a fini —
   c'est toute la différence avec une animation posée là pour faire patienter. */
.run-dot {
  width: 17px;
  height: 17px;
  flex-shrink: 0;
  border-radius: 50%;
  border: 2px solid var(--border-strong);
  position: relative;
  transition: border-color 0.25s ease, background 0.25s ease;
}
.run-step.is-running { opacity: 1; background: var(--accent-soft); border-color: var(--accent); }
.run-step.is-running .run-dot {
  border-color: var(--accent-soft);
  border-top-color: var(--accent);
  animation: run-spin 0.72s linear infinite;
}
@keyframes run-spin { to { transform: rotate(360deg); } }

.run-step.is-done { opacity: 1; }
.run-step.is-done .run-dot {
  border-color: var(--success);
  background: var(--success);
  animation: run-pop 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}
.run-step.is-done .run-dot::after {
  content: '';
  position: absolute;
  inset-inline-start: 4px;
  inset-block-start: 1px;
  width: 4px;
  height: 8px;
  border: solid var(--success-contrast);
  border-width: 0 2px 2px 0;
  transform: rotate(42deg);
}
@keyframes run-pop { from { transform: scale(0.4); } to { transform: scale(1); } }

.run-step.is-failed { opacity: 1; background: var(--danger-bg); border-color: var(--danger); }
.run-step.is-failed .run-dot { border-color: var(--danger); background: var(--danger); }
.run-step.is-failed .run-said { color: var(--danger); font-weight: 600; }

.run-error {
  margin: 16px 0 0;
  padding: 11px 14px;
  border-radius: var(--radius);
  background: var(--danger-bg);
  color: var(--danger);
  font-size: 0.8rem;
  line-height: 1.5;
}
.run-error.is-good { background: var(--success-bg); color: var(--success); }

.btn.is-working { pointer-events: none; opacity: 0.75; }

@media (prefers-reduced-motion: reduce) {
  .install-window { animation: none; }
  .run-step.is-running .run-dot { animation: none; border-top-color: var(--accent); }
  .run-step.is-done .run-dot { animation: none; }
  .install-progress-fill { transition: none; }
}
.check-list { list-style: none; margin: 18px 0 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.check-item { display: flex; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--border); }
.check-item:last-child { border-bottom: none; }
.check-mark {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.72rem;
  font-weight: 700;
  flex-shrink: 0;
}
.check-mark.is-ok { background: var(--success-bg); color: var(--success); }
.check-mark.is-fail { background: var(--danger-bg); color: var(--danger); }
.check-mark.is-warn { background: var(--warning-bg); color: var(--warning); }
.check-body { display: flex; flex-direction: column; min-width: 0; }
.check-label { font-size: 0.85rem; font-weight: 500; }
.check-tag {
  margin-inline-start: 5px;
  padding: 1px 5px;
  border-radius: 3px;
  background: var(--surface-hover);
  color: var(--text-tertiary);
  font-size: 0.66rem;
  font-weight: 500;
  vertical-align: 1px;
}
.check-detail { font-size: 0.76rem; color: var(--text-tertiary); overflow-wrap: anywhere; }

.summary-list, .detail-list { margin: 18px 0 0; display: flex; flex-direction: column; gap: 2px; }
.summary-list > div, .detail-list > div { display: flex; justify-content: space-between; gap: 14px; padding: 9px 0; border-bottom: 1px solid var(--border); }
.summary-list > div:last-child, .detail-list > div:last-child { border-bottom: none; }
.summary-list dt, .detail-list dt { font-size: 0.8rem; color: var(--text-secondary); margin: 0; }
.summary-list dd, .detail-list dd { font-size: 0.85rem; font-weight: 600; margin: 0; text-align: end; }

/* ---------- Ce que le balisage demandait sans que rien ne réponde ---------- */


/* Une colonne de cases à cocher. Sans elle, les .check héritaient de la
   largeur pleine des champs de formulaire et l'étiquette passait dessous. */
.checks { display: flex; flex-direction: column; gap: 6px; }

/* La grille générique. .grid-2 existait seule ; trois et quatre colonnes
   retombaient donc en pile, ce qui est exactement ce qu'on ne voulait pas. */
.grid { display: grid; gap: 14px; align-items: start; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; align-items: start; }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; align-items: start; }

/* Une carte dans une carte : même dessin, un ton en dessous, pour qu'on voie
   qu'elle appartient à celle qui la contient. */
.sub-card {
  padding: 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.sub-card + .sub-card { margin-top: 10px; }
.sub-card h3 { margin: 0 0 8px; font-size: 0.92rem; }

/* Les trois chiffres de l'organigramme. Sans règle, le nombre et son libellé
   se touchaient : « 4Salariés ». */
.kpi-row { display: flex; flex-wrap: wrap; gap: 18px; margin-bottom: 14px; }
.kpi { display: flex; flex-direction: column; min-width: 90px; }
.kpi strong { font-size: 1.6rem; font-weight: 700; line-height: 1.1; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.kpi span { font-size: 0.76rem; color: var(--text-secondary); margin-top: 2px; }

/* Une barre de filtres qui va à la ligne — ce qu'.inline-form refuse de faire. */
.toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 14px; }
.toolbar input, .toolbar select { width: auto; min-width: 160px; }

/* Un select posé dans une cellule de tableau : il ne doit pas s'étaler sur
   toute la colonne comme un champ de formulaire. */
.inline-select { width: auto; min-width: 130px; padding: 5px 8px; font-size: 0.78rem; }

/* Du texte préformaté qu'on donne à lire : contexte JSON d'une entrée de
   journal, corps d'un document mis à la signature, code rendu une fois. */
.code-block, .code-display, .document-body {
  margin: 10px 0 0;
  padding: 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: 0.78rem;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.code-display { font-size: 0.9rem; font-weight: 600; letter-spacing: 0.04em; text-align: center; }

/* Une liste d'étiquettes : des .tag posés côte à côte, sans puces. */
.tag-list { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 0; padding: 0; list-style: none; }

/* Les étapes d'un circuit d'approbation. */
.steps { margin: 10px 0 0; padding: 0 0 0 18px; display: flex; flex-direction: column; gap: 4px; }
.steps li { font-size: 0.84rem; }

/* Le bandeau d'information : .flash-success et .flash-error existaient, le
   ton neutre non — il restait donc muet là où il devait se faire remarquer. */
.flash-info { background: var(--accent-soft); border-color: var(--accent); border-inline-start-color: var(--accent); color: var(--accent); }

/* ---------- Avatars ---------- */

.user-avatar img,
.avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; display: block; }

.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 700;
  font-size: 0.82rem;
  flex-shrink: 0;
  overflow: hidden;
}
.avatar-lg { width: 84px; height: 84px; font-size: 1.5rem; }
.avatar-sm { width: 30px; height: 30px; font-size: 0.7rem; }

/* ---------- Sélecteur de langue ---------- */

.lang-grid, .lang-picker { display: flex; flex-wrap: wrap; gap: 6px; }

.lang-option {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text-secondary);
  font-family: var(--font-sans);
  font-size: 0.8rem;
  cursor: pointer;
  text-decoration: none;
}
.lang-option:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
.lang-option.is-active { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.lang-flag { font-size: 1.05rem; line-height: 1; }

.lang-inline { display: flex; align-items: center; gap: 4px; }
.lang-inline .lang-option { padding: 4px 7px; }

/* ---------- Actualités ---------- */

.news-item { padding: 14px 0; border-bottom: 1px solid var(--border); }
.news-item:first-child { padding-top: 0; }
.news-item:last-child { border-bottom: none; padding-bottom: 0; }
.news-head { display: flex; align-items: center; gap: 8px; margin-bottom: 5px; flex-wrap: wrap; }
.news-title { font-weight: 600; font-size: 0.92rem; }
.news-meta { font-size: 0.74rem; color: var(--text-tertiary); }
.news-body { font-size: 0.85rem; color: var(--text-secondary); line-height: 1.55; white-space: pre-line; }

/* ---------- Carte manager ---------- */

.manager-card { display: flex; align-items: center; gap: 14px; }
.manager-card .manager-name { font-weight: 600; font-size: 0.95rem; }
.manager-card .manager-role { font-size: 0.79rem; color: var(--text-secondary); }

/* ---------- Annuaire ---------- */

.people-grid, .people { display: grid; grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); gap: 12px; }

.person-card {
  display: flex;
  gap: 12px;
  padding: 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.person-card:hover { border-color: var(--accent); }
.person-body { min-width: 0; }
.person-name { font-weight: 600; font-size: 0.93rem; }
.person-role { font-size: 0.79rem; color: var(--text-secondary); margin-bottom: 6px; }
.person-line { font-size: 0.78rem; color: var(--text-tertiary); overflow-wrap: anywhere; }
.person-bio { margin-top: 7px; font-size: 0.78rem; color: var(--text-secondary); line-height: 1.5; }

.search-bar { display: flex; gap: 8px; max-width: 460px; }

/* ---------- Messagerie ---------- */

.mail-layout { display: grid; grid-template-columns: 340px 1fr; gap: 14px; align-items: start; }

.mail-tabs { display: flex; gap: 2px; padding: 3px; background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius); }
.mail-tab {
  flex: 1;
  text-align: center;
  padding: 6px 10px;
  border-radius: 3px;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-secondary);
  text-decoration: none;
}
.mail-tab:hover { color: var(--text); text-decoration: none; }
.mail-tab.is-active { background: var(--surface); color: var(--accent); box-shadow: var(--shadow-xs); }

.mail-list { display: flex; flex-direction: column; margin: 12px -20px -18px; }
.mail-row {
  display: flex;
  gap: 10px;
  padding: 11px 20px;
  border-top: 1px solid var(--border);
  text-decoration: none;
  color: inherit;
}
.mail-row:hover { background: var(--surface-hover); text-decoration: none; }
.mail-row.is-unread { background: var(--accent-soft); }
.mail-row.is-open { border-inline-start: 3px solid var(--accent); padding-inline-start: 17px; }
/* Conteneur en colonne : sans cela les enfants restent inline et l'ellipsis ne s'applique pas. */
.mail-row-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.mail-row-top { display: flex; justify-content: space-between; gap: 8px; }
.mail-from { font-size: 0.84rem; font-weight: 600; }
.mail-date { font-size: 0.72rem; color: var(--text-tertiary); white-space: nowrap; }
.mail-subject { font-size: 0.82rem; margin-top: 1px; }
.mail-preview { font-size: 0.76rem; color: var(--text-tertiary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.mail-reader-head { display: flex; justify-content: space-between; gap: 14px; align-items: flex-start; padding-bottom: 12px; border-bottom: 1px solid var(--border); margin-bottom: 14px; }
.mail-reader-body { font-size: 0.88rem; line-height: 1.65; white-space: pre-line; color: var(--text); }

/* ---------- Agenda ---------- */

.agenda-layout { display: grid; grid-template-columns: 1fr 340px; gap: 14px; align-items: start; margin-bottom: 14px; }

.calendar-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.calendar-month { text-transform: capitalize; }
.calendar-nav { display: flex; align-items: center; gap: 4px; }

.calendar-wrap { overflow-x: auto; }
.calendar { width: 100%; min-width: 560px; border-collapse: collapse; table-layout: fixed; }
.calendar th {
  padding: 8px 6px;
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-tertiary);
  text-align: start;
}

.cal-cell {
  height: 96px;
  vertical-align: top;
  padding: 6px;
  border: 1px solid var(--border);
  background: var(--surface);
}
.cal-cell.is-weekend { background: var(--surface-2); }
.cal-cell.is-outside { background: var(--surface-2); opacity: 0.55; }
.cal-cell.is-today { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }

.cal-day { display: block; font-size: 0.76rem; font-weight: 600; color: var(--text-secondary); margin-bottom: 4px; }
.cal-cell.is-today .cal-day { color: var(--accent); }

.cal-event {
  display: block;
  padding: 2px 6px;
  margin-bottom: 3px;
  border-radius: 3px;
  border-inline-start: 3px solid var(--accent);
  background: var(--accent-soft);
  color: var(--text);
  font-size: 0.72rem;
  line-height: 1.35;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cal-event-time { font-variant-numeric: tabular-nums; color: var(--text-secondary); margin-inline-end: 4px; }
.cal-event.is-conge { border-inline-start-color: var(--success); background: var(--success-bg); }
.cal-event.is-cse { border-inline-start-color: var(--warning); background: var(--warning-bg); }
.cal-event.is-contrat { border-inline-start-color: var(--danger); background: var(--danger-bg); }
/* Ce qui vient d'un collègue se distingue de ce qui vient de soi. */
.cal-event.is-partage { border-inline-start-color: var(--text-tertiary); background: var(--surface-2); }
.cal-event.is-absence { border-inline-start-color: var(--text-secondary); background: var(--surface-2); font-style: italic; }
.cal-event.is-salle { border-inline-start-color: var(--brand-deep); background: var(--accent-soft); }
.cal-event.is-formation { border-inline-start-color: var(--brand-deep-2); background: var(--accent-soft); }
.cal-more { font-size: 0.7rem; color: var(--text-tertiary); }

.calendar-legend { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-top: 12px; font-size: 0.76rem; color: var(--text-secondary); }
/* Chaque entrée reste solidaire de sa pastille quand la légende passe à la ligne. */
.legend-item { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.legend-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--accent); flex-shrink: 0; }
.legend-dot.is-conge { background: var(--success); }
.legend-dot.is-cse { background: var(--warning); }
.legend-dot.is-contrat { background: var(--danger); }
.legend-dot.is-partage { background: var(--text-tertiary); }
.legend-dot.is-absence { background: var(--text-secondary); }

.timeline { display: grid; gap: 10px; }
.timeline-item { display: flex; align-items: flex-start; gap: 9px; }
.timeline-item .legend-dot { margin-top: 5px; }
.timeline-body { display: flex; flex-direction: column; min-width: 0; }
.timeline-title { font-size: 0.86rem; font-weight: 600; }

/* ---------- Organisation : services et équipes ---------- */

.org-list { list-style: none; display: grid; gap: 10px; }
.org-item { padding: 12px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-2); display: grid; gap: 8px; }
/* Une ligne « à regarder » porte son ton sur la tranche, du côté où l'œil
   commence. La pastille au bout de la ligne dit la même chose en toutes
   lettres : la couleur ne fait que la répéter, elle ne la remplace pas. */
.org-item.ton-grave { border-inline-start: 3px solid var(--danger); }
.org-item.ton-attention { border-inline-start: 3px solid var(--warning); }
.org-item.ton-info { border-inline-start: 3px solid var(--accent); }
.org-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.org-name { display: block; font-size: 0.9rem; font-weight: 650; }
.org-managers { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
/* Dans un tableau qui défile, un menu déroulant écrasé devient inutilisable. */
.table-wrap td select { min-width: 132px; }

/* ---------- Filtrage ATS ---------- */

/* Le score se lit d'un coup d'œil, sans avoir à comparer des nombres. */
.score-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 10px;
  border-radius: 999px;
  border: 1px solid;
  font-size: 0.78rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.score-badge.is-ok { color: var(--success); background: var(--success-bg); border-color: var(--success-border); }
.score-badge.is-low { color: var(--warning); background: var(--warning-bg); border-color: var(--warning-border); }
.score-badge.is-none { color: var(--text-tertiary); background: var(--surface-2); border-color: var(--border); }

/* Un champ étiqueté dans une barre d'actions horizontale. */
.inline-field { display: inline-flex !important; align-items: center; gap: 6px; margin: 0; }
.inline-field input { width: 5.5rem; }

.candidate-row .row-actions { margin-top: 8px; }

/* Le sélecteur de fichier natif est habillé aux couleurs du reste des champs :
   sans cela il affiche un bouton système et un libellé non traduit. */
.candidate-row input[type="file"] {
  max-width: 230px;
  padding: 4px 6px;
  font-size: 12px;
  color: var(--text-secondary);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.candidate-row input[type="file"]::file-selector-button {
  margin-inline-end: 8px;
  padding: 4px 10px;
  font: inherit;
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
}
.candidate-row input[type="file"]:hover::file-selector-button { border-color: var(--accent); color: var(--accent); }

/* ---------- Saisie d'écriture comptable ---------- */

/* Compte, libellé, débit, crédit : une ligne de journal se lit en colonnes. */
.entry-line { display: grid; grid-template-columns: 2fr 2fr 1fr 1fr; gap: 6px; margin-bottom: 6px; }
.entry-line select, .entry-line input { min-width: 0; }

/* ---------- Jauge de consommation ---------- */

.meter { display: block; height: 8px; border-radius: 4px; background: var(--surface-2); border: 1px solid var(--border); overflow: hidden; }
.meter-fill { display: block; height: 100%; width: 0; background: var(--accent); border-radius: 4px; }
.meter-fill.is-warn { background: var(--warning); }
.meter-fill.is-over { background: var(--danger); }

/* ---------- CSE ---------- */

.benefit-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(272px, 1fr)); gap: 12px; }

.benefit-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  min-width: 0;
}
.benefit-card:hover { border-color: var(--accent); }
.benefit-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.benefit-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius);
  background: var(--accent-soft);
  color: var(--accent);
}
.benefit-title { font-size: 0.95rem; font-weight: 650; }
.benefit-discount { font-size: 1.1rem; font-weight: 700; color: var(--accent); }
.benefit-text { font-size: 0.82rem; color: var(--text-secondary); line-height: 1.55; overflow-wrap: anywhere; }
.benefit-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; margin-top: auto; padding-top: 8px; }

.code-chip { display: inline-flex; align-items: center; gap: 6px; }
.code-chip code {
  padding: 3px 8px;
  border: 1px dashed var(--border-strong);
  border-radius: 3px;
  background: var(--surface-2);
  font-size: 0.8rem;
  font-weight: 600;
}

.person-list, .meeting-list, .candidate-list, .timeline { list-style: none; }
.person-list { display: grid; gap: 2px; }
.person-row { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--border); }
.person-row:last-child { border-bottom: none; }
.person-row .person-body { flex: 1; display: flex; flex-direction: column; gap: 1px; }

.meeting-list { display: grid; gap: 12px; }
.meeting-item { padding: 14px; border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface-2); display: grid; gap: 8px; }
/* Sans cela, un tableau imbriqué élargit la carte au lieu de défiler dedans. */
.meeting-item > * { min-width: 0; }
.meeting-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.meeting-title { font-size: 0.92rem; font-weight: 650; }

.minutes summary { cursor: pointer; font-size: 0.82rem; font-weight: 600; color: var(--accent); }
.minutes p { margin-top: 8px; font-size: 0.82rem; color: var(--text-secondary); line-height: 1.6; white-space: pre-wrap; }

.election-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
.election-title { font-size: 1rem; font-weight: 650; }

.section-label { margin: 16px 0 8px; font-size: 0.8rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-tertiary); }

.candidate-list { display: grid; gap: 8px; }
.candidate-row { padding: 12px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-2); }
.candidate-row:hover { border-color: var(--accent); }
.candidate-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

input:disabled, select:disabled, textarea:disabled { background: var(--surface-2); color: var(--text-tertiary); cursor: not-allowed; }

/* label passe en colonne par défaut : une case à cocher, elle, se lit sur une ligne. */
.check-row, .check { display: flex !important; flex-direction: row; align-items: flex-start; gap: 9px; }
.check-row input, .check input { width: auto; margin-top: 3px; flex-shrink: 0; }

.row-actions { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }

/* ---------- Responsive ---------- */

@media (max-width: 1080px) {
  .agenda-layout { grid-template-columns: 1fr; }
  .grid-2 { grid-template-columns: 1fr; }
  .stats-grid { grid-template-columns: repeat(2, 1fr); }
  .mail-layout { grid-template-columns: 1fr; }
}

@media (max-width: 900px) {
  .app-shell { grid-template-columns: 1fr; }
  .sidebar {
    position: static;
    height: auto;
    min-width: 0;
    flex-direction: row;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
  }
  .sidebar-brand { padding: 0; padding-inline: 4px 8px; }
  .sidebar-brand .brand-text { display: none; }
  /* La barre de navigation défile seule au lieu d'élargir toute la page. */
  .sidebar-nav { flex-direction: row; gap: 4px; flex: 1; overflow-x: auto; }
  .side-link { border-inline-start: none; border-bottom: 3px solid transparent; border-radius: var(--radius); padding: 8px 11px; white-space: nowrap; }
  .side-link.is-active { border-inline-start-color: transparent; border-bottom-color: #fff; }
  /* Le pied défile comme la navigation : à mesure que les espaces s'ajoutent,
     il finirait sinon par pousser la page au-delà de l'écran. */
  .sidebar-foot { flex-direction: row; align-items: center; margin: 0; padding: 0; padding-inline-start: 10px; border-top: none; border-inline-start: 1px solid rgba(255, 255, 255, 0.16); min-width: 0; overflow-x: auto; }
  .side-link-quiet .side-link-label { display: none; }
}

@media (max-width: 720px) {
  .stats-grid { grid-template-columns: 1fr; }
  .form-grid { grid-template-columns: 1fr; }
  .form-grid .span-2 { grid-column: auto; }
  .page { padding: 16px 14px 48px; }
  .app-header { padding: 10px 14px; flex-wrap: wrap; }
  /* On dégraisse l'en-tête, pas les actions de la page : un bouton principal garde son libellé. */
  .user-meta, .app-header .btn-label { display: none; }
  .user-chip { padding-inline-end: 0; border-inline-end: none; }
  .timer-stats { grid-template-columns: 1fr; }
  .table-wrap { margin: 0 -20px -18px; }
  .auth-card { padding: 26px 20px; }
  /* Sur téléphone la fenêtre occupe l'écran : le rail passe au-dessus et se
     réduit à la seule étape en cours — les cinq autres prendraient la moitié
     de la hauteur pour ne rien dire de plus. */
  .install-scene { padding: 0; align-items: stretch; }
  .install-window { grid-template-columns: 1fr; border-radius: 0; border: none; min-height: 100dvh; }
  .install-aside { padding: 20px 18px 16px; }
  .install-brand { margin-bottom: 16px; }
  .install-step { display: none; }
  .install-step.is-current { display: flex; }
  .install-step.is-current { background: transparent; box-shadow: none; padding-inline: 0; }
  .install-step::before { display: none; }
  .install-aside-foot { display: none; }
  .install-stage { padding: 24px 18px 22px; }
  .install-actions { flex-direction: column-reverse; align-items: stretch; }
  .install-actions .btn { width: 100%; justify-content: center; }
  .install-note { text-align: center; }
  .choice-grid { grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); }
  .recap-row { flex-direction: column; gap: 2px; }
  .recap dd { text-align: start; }
  .people-grid, .people { grid-template-columns: 1fr; }
  .grid-3, .grid-4 { grid-template-columns: 1fr; }
  .entry-line { grid-template-columns: 1fr 1fr; }
  /* Sur téléphone, la grille du mois tient en entier : chaque événement devient une
     barre colorée, et la liste sous le calendrier en porte le détail. */
  .calendar { min-width: 0; }
  .calendar th { padding: 6px 2px; font-size: 0.62rem; }
  .cal-cell { height: 56px; padding: 3px; }
  .cal-day { font-size: 0.7rem; margin-bottom: 3px; }
  .cal-event {
    height: 5px;
    padding: 0;
    margin-bottom: 2px;
    border-radius: 3px;
    border-inline-start: none;
    background: var(--accent);
    text-indent: -999em;
  }
  .cal-event.is-conge { background: var(--success); }
  .cal-event.is-cse { background: var(--warning); }
  .cal-event.is-contrat { background: var(--danger); }
  .cal-event.is-partage { background: var(--text-tertiary); }
  .cal-event.is-absence { background: var(--text-secondary); }
  .cal-event.is-salle { background: var(--brand-deep); }
  .cal-event.is-formation { background: var(--brand-deep-2); }
  .cal-event-time, .cal-more { display: none; }

  /* Sur téléphone, la barre se réduit aux icônes : seule la section courante est nommée. */
  .sidebar-nav .side-link-label { display: none; }
  .sidebar-nav .side-link.is-active .side-link-label { display: inline; }
  /* Le rail devient une barre horizontale : un titre de famille n'y a plus de
     colonne à coiffer, il se glisse entre deux icônes et brouille la barre.
     Les familles restent lisibles sur le tableau de bord, qui les nomme.

     Le groupe s'efface, pas ses rubriques : « display: contents » laisse les
     liens se ranger dans la barre comme s'il n'y avait jamais eu de groupe.
     Masquer le groupe entier, ici, aurait vidé la navigation du téléphone. */
  .sidebar-nav .side-group,
  .sidebar-nav .side-group-liste { display: contents; }
  .sidebar-nav .side-group-tete { display: none; }
}

/* ---------- Première connexion ---------- */

.auth-note { margin-top: 14px; font-size: 12px; line-height: 1.5; color: var(--text-tertiary); }
.auth-section { margin: 22px 0 8px; font-size: 0.82rem; font-weight: 600; color: var(--text-secondary); }
.btn-link { background: none; border: 0; padding: 0; color: var(--accent); font: inherit; cursor: pointer; }
.btn-link:hover { text-decoration: underline; }

/* ---------- Double authentification ---------- */

.totp-qr { display: block; margin: 12px auto; border: 1px solid var(--border); border-radius: var(--radius); background: #fff; padding: 8px; }
.totp-secret { display: inline-block; padding: 3px 7px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; letter-spacing: 1px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 6px; word-break: break-all; }
.code-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 6px; margin: 10px 0; padding: 0; list-style: none; }
.code-list li { padding: 6px 8px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; text-align: center; background: var(--surface-2); border: 1px solid var(--border); border-radius: 6px; }

/* ---------- Console de sécurité ---------- */

/* L'en-tête est collant : une ancre doit s'arrêter en dessous, pas dessous lui. */
.tab-panel[id], .card[id] { scroll-margin-top: 84px; }
.audit-action { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; white-space: nowrap; }
.audit-detail { max-width: 320px; overflow-wrap: anywhere; }
.nowrap { white-space: nowrap; }
.pager { margin-top: 10px; }

/* ---------- Projets ---------- */

/* Un tableau par statut : les colonnes défilent horizontalement plutôt que de
   se tasser, pour rester lisibles quel que soit le nombre de tâches. */
.board { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(240px, 1fr); gap: 12px; overflow-x: auto; padding-bottom: 6px; }
.board-column { min-width: 0; padding: 12px; background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius); }
.board-title { margin: 0 0 10px; font-size: 13px; text-transform: uppercase; letter-spacing: .04em; color: var(--text-secondary); }
.board-card { padding: 10px; margin-bottom: 8px; background: var(--surface); border: 1px solid var(--border); border-radius: 10px; }
.board-card p { margin: 4px 0; }
.board-card .row-actions { margin-top: 8px; gap: 4px; }
.board-card select { max-width: 120px; }

.project-row { align-items: flex-start; }
.project-main { min-width: 0; flex: 1; }
.project-main .meter { margin: 6px 0 2px; max-width: 420px; }
/* .org-item est une grille : le bouton d'ouverture ne doit pas s'étirer sur toute la ligne. */
.project-row { grid-template-columns: 1fr auto; align-items: start; }
.project-row > .btn { justify-self: end; }
.meter-fill.is-danger { background: var(--danger); }

@media (max-width: 720px) {
  .board { grid-auto-flow: row; grid-auto-columns: auto; }
}

/* ---------- Support ---------- */

.ticket-body { white-space: pre-wrap; overflow-wrap: anywhere; margin: 8px 0 0; line-height: 1.55; }
.thread { list-style: none; margin: 0 0 14px; padding: 0; display: grid; gap: 10px; }
.thread-item { padding: 12px; background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius); }
/* Une note interne doit se distinguer d'un coup d'œil de ce que lit le demandeur. */
.thread-item.is-internal { background: var(--warning-bg); border-color: var(--warning-border); }

.article-body { white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.65; margin: 0; }

/* ---------- Parcours et compétences ---------- */

.org-item.is-done { opacity: .62; }
.matrix-head { font-size: 11px; font-weight: 600; white-space: nowrap; }
.matrix-cell { text-align: center; white-space: nowrap; }
.matrix-cell .btn-link { display: block; margin: 2px auto 0; font-size: 11px; }

tr.is-muted { opacity: .55; }
tr.is-current { background: var(--accent-soft); }

/* ---------- Coffre-fort ---------- */

/* L'empreinte doit être lisible sans attirer l'œil : c'est une preuve, pas une donnée de travail. */
.fingerprint { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; letter-spacing: .02em; }

/* ---------- Choix de la palette (console d'administration) ---------- */

.palette-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 14px; }

.palette-card {
  display: grid;
  gap: 8px;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface-2);
}
.palette-card.is-active { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-ring); }
.palette-name { font-weight: 650; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.palette-text { font-size: 0.78rem; line-height: 1.5; color: var(--text-secondary); }

/* L'aperçu emprunte les jetons de la palette qu'il montre : il ne peut pas
   mentir, puisqu'il n'a pas de couleurs à lui. Il montre toujours le mode
   clair — le mode reste un réglage personnel. */
.palette-preview {
  display: flex;
  height: 104px;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.palette-nav {
  flex: 0 0 34%;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 8px;
  background: linear-gradient(180deg, var(--brand-deep) 0%, var(--brand-deep-2) 100%);
}
.palette-mark { width: 18px; height: 18px; border-radius: 4px; background: var(--brand-ink); opacity: .92; }
.palette-line { height: 6px; border-radius: 3px; background: var(--brand-ink); opacity: .42; }
.palette-line.is-short { width: 62%; }
.palette-line.is-dark { background: var(--text); opacity: .22; }
.palette-body { flex: 1; padding: 10px; background: var(--bg); }
.palette-card-sample {
  display: flex;
  flex-direction: column;
  gap: 6px;
  height: 100%;
  padding: 9px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}
.palette-button { margin-top: auto; height: 16px; width: 64%; border-radius: var(--radius); background: var(--accent-fill); }

/* ---------- Sauvegardes ---------- */

/* Le nom d'une archive fait une quarantaine de caractères : le champ de
   confirmation doit pouvoir le contenir, sinon la consigne devient un piège. */
.confirm-name { min-width: 260px; }
@media (max-width: 720px) { .confirm-name { min-width: 0; width: 100%; } }

/* ---------- Direction : matrice des risques ---------- */

/* Les quatre niveaux reprennent les couleurs d'état déjà définies : la matrice
   se relit d'un coup d'œil, et suit la palette de l'instance comme le reste. */
/* Une matrice s'embrasse d'un regard : elle ne s'étale pas sur toute la
   largeur comme un tableau de données, sinon les cases perdent leur forme. */
.table-wrap .risk-matrix { width: auto; min-width: 0; }
.risk-matrix th, .risk-matrix td { text-align: center; padding: 9px 12px; }
.risk-matrix tbody th { width: 150px; text-align: end; }
.risk-cell { font-weight: 650; min-width: 64px; }
.risk-level-1 { background: var(--success-bg); color: var(--success); }
.risk-level-2 { background: var(--accent-soft); color: var(--accent); }
.risk-level-3 { background: var(--warning-bg); color: var(--warning); }
.risk-level-4 { background: var(--danger-bg); color: var(--danger); }

/* ---------- Planning ---------- */

.planning-grid td { vertical-align: top; }
.planning-cell { min-width: 118px; padding: 6px; }
.shift {
  display: block; margin-bottom: 4px; padding: 5px 7px; border-radius: var(--radius);
  border: 1px solid var(--accent-ring); background: var(--accent-soft); color: var(--text-primary);
  font-size: 0.76rem; line-height: 1.35;
}
.shift-oncall { background: var(--warning-bg); border-color: var(--warning-border); }
.shift-remote { background: var(--success-bg); border-color: var(--success-border); }
/* Un brouillon se voit : personne ne doit organiser sa semaine dessus. */
.shift.is-draft { border-style: dashed; opacity: 0.75; }
.shift-time { display: block; font-weight: 650; }
.shift-label { display: block; color: var(--text-secondary); }
.shift .inline-form { display: inline; }
.shift .btn-link { padding: 0; font-size: 0.76rem; }

/* ---------- Organigramme ---------- */

.chart { display: grid; gap: 14px; }
.chart-department { display: grid; gap: 10px; }
.chart-managers {
  display: flex; align-items: center; gap: 6px;
  font-size: 0.8rem; font-weight: 600; color: var(--accent);
}
.chart-managers.chart-empty { color: var(--text-tertiary); font-weight: 500; }
.chart-branch-title {
  font-size: 0.82rem; font-weight: 650; text-transform: uppercase;
  letter-spacing: 0.03em; color: var(--text-tertiary);
}
/* Les équipes se rangent côte à côte tant que la place le permet, puis
   s'empilent : un organigramme illisible n'organise rien. */
.chart-teams { display: grid; grid-template-columns: repeat(auto-fill, minmax(252px, 1fr)); gap: 12px; }
.chart-team, .chart-branch {
  display: grid; gap: 8px; padding: 12px;
  border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface-2);
}
.chart-people { list-style: none; display: grid; gap: 2px; }
.chart-person { display: flex; align-items: center; gap: 8px; padding: 5px 0; }
.chart-person.is-me { font-weight: 650; }
.chart-person.chart-empty { color: var(--text-tertiary); font-size: 0.8rem; }
.chart-avatar {
  flex: none; width: 26px; height: 26px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-soft); color: var(--accent);
  font-size: 0.68rem; font-weight: 700;
}
.chart-person-body { display: flex; flex-direction: column; min-width: 0; }
.chart-person-name { font-size: 0.84rem; }
.chart-person-role { font-size: 0.76rem; color: var(--text-secondary); }

/* ---------- Parapheur ---------- */

/* Le rang du signataire est la donnée qui compte : le circuit est ordonné. */
.signer-row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.signer-row select { flex: 2; min-width: 0; }
.signer-row input { flex: 1; min-width: 0; }
.signer-rank {
  flex: none; width: 24px; height: 24px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface-2); border: 1px solid var(--border);
  font-size: 0.74rem; font-weight: 700; color: var(--text-secondary);
}
@media (max-width: 720px) {
  .signer-row { flex-wrap: wrap; }
  .signer-row select, .signer-row input { flex: 1 1 100%; }
}

/* =========================================================================
   Fenêtres
   =========================================================================

   Avant celles-ci, chaque confirmation passait par window.confirm() : la boîte
   grise du système, précédée de « demo.toutadmin.com indique… », sans rapport
   avec le produit et impossible à traduire ou à styler. Cent vingt-sept
   endroits l'utilisaient.

   Une fenêtre à nous coûte trois choses qu'il faut assumer : le fond doit
   s'assombrir pour dire que le reste est suspendu, le clavier ne doit pas
   pouvoir sortir de la fenêtre, et l'échappement doit la fermer. Sans ces
   trois-là, une jolie boîte est une régression.
   ========================================================================= */

.dialog-backdrop {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  /* Le flou dit que ce qui est derrière n'est pas cliquable : l'œil renonce
     à lire un texte flou bien avant que la main n'essaie de cliquer. */
  background: rgb(var(--shadow-ink) / 0.42);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  animation: dialog-fade 0.18s var(--ease-out) both;
}

.dialog {
  width: 100%;
  max-width: 460px;
  background: var(--surface);
  border: 1px solid rgb(var(--shadow-ink) / 0.08);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-float), var(--edge-light);
  padding: 26px 26px 20px;
  animation: dialog-rise 0.22s var(--ease-out) both;
}

.dialog-title { font-size: 1.05rem; letter-spacing: -0.01em; margin: 0 0 8px; }
.dialog-text { font-size: 0.88rem; line-height: 1.55; color: var(--text-secondary); margin: 0; }
.dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid rgb(var(--shadow-ink) / 0.07);
}

/* Une confirmation destructrice ne doit pas avoir le même bouton qu'un envoi
   ordinaire : c'est la dernière occasion de comprendre ce qu'on fait. */
.dialog-danger .dialog-title { color: var(--danger); }
.btn-destroy {
  background: linear-gradient(180deg, var(--danger) 0%, var(--danger) 100%);
  border-color: var(--danger);
  color: var(--danger-contrast);
  box-shadow: 0 1px 2px rgb(var(--shadow-ink) / 0.14), 0 6px 16px rgb(var(--shadow-ink) / 0.12);
}
.btn-destroy:hover { background: var(--danger); border-color: var(--danger); color: var(--danger-contrast); }

@keyframes dialog-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes dialog-rise {
  from { opacity: 0; transform: translateY(12px) scale(0.98); }
  to { opacity: 1; transform: none; }
}

/* Le mouvement sert à dire d'où vient la fenêtre. Quand il gêne, l'état final
   suffit : on ne supprime pas la fenêtre, on supprime le trajet. */
@media (prefers-reduced-motion: reduce) {
  .dialog-backdrop, .dialog { animation-duration: 0.01ms; }
  .card, .stat-card, .btn { transition-duration: 0.01ms; }
}

/* La page ne défile plus derrière la fenêtre : sinon la molette emporte le
   fond et la fenêtre paraît flotter à la dérive. */
body.dialog-open { overflow: hidden; }

@media (max-width: 720px) {
  .dialog { padding: 22px 20px 18px; }
  .dialog-actions { flex-direction: column-reverse; }
  .dialog-actions .btn { width: 100%; }
}

/* =========================================================================
   Rail replié
   =========================================================================

   Deux cent quarante-huit pixels de navigation permanente, c'est confortable
   sur un grand écran et coûteux sur un portable : sur un treize pouces, cela
   fait un cinquième de la largeur, pris à un tableau qui en manque.

   Replié, le rail ne garde que les icônes. Les libellés ne sont pas retirés du
   document, seulement masqués à l'œil : un lecteur d'écran les lit toujours,
   et l'infobulle les rend à la souris. Le choix tient dans le navigateur de
   chaque personne — ce n'est pas un réglage de l'instance — et il est appliqué
   avant le premier rendu, sinon le rail large apparaîtrait puis sauterait.
   ========================================================================= */

/* Le bouton vit au pied du rail, au-dessus du compte et de la déconnexion :
   les trois réglages qui appartiennent à la personne sont au même endroit. Il
   avait sa place en tête, contre la marque, où il fallait se souvenir qu'un
   bouton d'affichage se trouvait parmi les rubriques — et où, replié, il
   passait sous elle au prix d'un positionnement absolu. */
.rail-toggle {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  flex-shrink: 0;
  padding: 9px 12px;
  border: 1px solid rgb(255 255 255 / 0.18);
  border-radius: var(--radius-sm);
  background: rgb(255 255 255 / 0.08);
  color: rgb(255 255 255 / 0.72);
  font-family: var(--font-sans);
  font-size: 0.8rem;
  text-align: start;
  cursor: pointer;
  transition: background 0.14s var(--ease-out), color 0.14s var(--ease-out);
}
.rail-toggle svg { flex-shrink: 0; }
.rail-toggle-label { flex: 1; }
.rail-toggle:hover { background: rgb(255 255 255 / 0.16); color: var(--brand-ink); }
.rail-toggle:focus-visible { outline: 2px solid var(--brand-ink); outline-offset: 2px; }

:root[data-rail="replie"] {
  --sidebar-width: 66px;
  --page-max: 1500px;
}

[data-rail="replie"] .sidebar { padding-inline: 8px; }
[data-rail="replie"] .sidebar-brand { justify-content: center; padding-inline: 0; }
[data-rail="replie"] .sidebar-brand .brand-text { display: none; }

/* Replié, il ne reste que l'icône — le libellé est masqué à l'œil par la règle
   commune des libellés du rail, et lu par un lecteur d'écran comme avant.
   Aucun positionnement absolu ici : au pied, le bouton n'a plus rien à
   recouvrir ni à éviter. */
[data-rail="replie"] .rail-toggle { justify-content: center; padding-inline: 0; }

[data-rail="replie"] .side-link { justify-content: center; padding-inline: 0; border-inline-start-width: 0; border-radius: var(--radius); }
[data-rail="replie"] .side-link.is-active { border-inline-start-color: transparent; box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.28); }

/* Masqué à l'œil, pas au lecteur d'écran : « display: none » retirerait le nom
   du lien, et un rail d'icônes anonymes n'est utilisable par personne. */
[data-rail="replie"] .side-link-label {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Le compteur ne peut plus s'aligner à droite d'un libellé absent : il devient
   une pastille sur l'icône, qui dit « il y a quelque chose » sans le nombre. */
[data-rail="replie"] .side-link { position: relative; }
[data-rail="replie"] .nav-badge {
  position: absolute;
  inset-block-start: 4px;
  inset-inline-end: 8px;
  min-width: 8px;
  height: 8px;
  padding: 0;
  border-radius: 50%;
  font-size: 0;
  box-shadow: 0 0 0 2px var(--brand-deep);
}

[data-rail="replie"] .sidebar-foot { align-items: center; }
/* Trois choix côte à côte ne tiennent pas dans soixante-six pixels : la
   grille passe à une colonne, et le sélecteur s'empile sous les rubriques. */
[data-rail="replie"] .sidebar-foot .theme-toggle { grid-template-columns: 1fr; align-self: center; }

/* Sur téléphone le rail est déjà une barre horizontale : le repli n'a plus de
   sens, et le bouton n'a rien à y faire. */
@media (max-width: 900px) {
  .rail-toggle { display: none; }
  :root[data-rail="replie"] { --sidebar-width: auto; }
  [data-rail="replie"] .side-link-label { position: static; width: auto; height: auto; margin: 0; clip-path: none; }
  [data-rail="replie"] .sidebar-brand { padding-bottom: 0; }
  [data-rail="replie"] .nav-badge { position: static; min-width: 19px; height: 19px; font-size: 0.7rem; border-radius: 10px; box-shadow: none; }
}

/* =========================================================================
   Écrans denses
   =========================================================================

   Deux habitudes coûtaient beaucoup de place : le statut d'une ligne montré
   deux fois — une pastille et une liste déroulante portant la même valeur —
   et un formulaire de création déplié en permanence sur un tiers de l'écran,
   y compris les neuf fois sur dix où l'on vient consulter.

   Les règles ci-dessous n'ont d'effet qu'une fois le DOM transformé par
   ecrans.js. Sans lui, elles ne s'appliquent à rien et les écrans restent ce
   qu'ils étaient : c'est voulu.
   ========================================================================= */

/* La liste de statut prend la place de la pastille et lui emprunte sa
   couleur : sans elle, on perdrait le seul repère qui se lit sans lire. */
.status-select {
  width: auto;
  min-width: 0;
  padding: 3px 26px 3px 9px;
  border-radius: var(--radius-pill);
  border: 1px solid;
  background-color: var(--surface-2);
  font-size: 0.73rem;
  font-weight: 600;
  line-height: 1.5;
  cursor: pointer;
  /* La flèche native se dessine ici, pour que le fond reste celui de l'état. */
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 13px) 52%, calc(100% - 9px) 52%;
  background-size: 4px 4px, 4px 4px;
  background-repeat: no-repeat;
}
.status-select.is-sending { opacity: 0.6; cursor: progress; }
.status-select.status-on { color: var(--success); background-color: var(--success-bg); border-color: var(--success-border); }
.status-select.status-warning { color: var(--warning); background-color: var(--warning-bg); border-color: var(--warning-border); }
.status-select.status-danger { color: var(--danger); background-color: var(--danger-bg); border-color: var(--danger-border); }
.status-select.status-off { color: var(--text-tertiary); background-color: var(--surface-2); border-color: var(--border); }
.status-form { display: inline-flex; }

/* Le panneau de création, replié : la grille redevient une seule colonne et
   rend sa moitié gauche aux données. */
.create-toggle { margin-bottom: 14px; }
.create-toggle::before { content: '+'; margin-inline-end: 7px; font-size: 1.05em; line-height: 0; }
.create-toggle.is-open::before { content: '×'; }
/* Trois chiffres qui se lisent ensemble.
   Ils étaient rangés dans une table à deux colonnes : dans une carte large —
   et elle le devient dès que le formulaire de création se replie — le libellé
   reste à gauche, le nombre part à mille pixels de là, et l'œil doit traverser
   l'écran pour les apparier. Côte à côte, chaque nombre reste sous son
   libellé. */
.figures {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 44px;
  margin: 0;
  padding: 2px 0;
}
.figures > div { display: flex; flex-direction: column; gap: 1px; min-width: 110px; }
.figures dt { margin: 0; font-size: 0.78rem; color: var(--text-secondary); }
.figures dd {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Le dépliant d'une ligne de tableau : il appartient à la ligne du dessus, pas
   à une ligne à part. Sans filet au-dessus, les deux se lisent ensemble. */
.ligne-depliante > td { border-top: none; padding-top: 0; }
.ligne-depliante .minutes { margin: 0; }

.grid-2-replie { grid-template-columns: 1fr; }
.grid-2-replie > .card:first-child { display: none; }

/* =========================================================================
   Le peu que l'espace éditeur ajoute.

   Tout ce qui précède est la feuille du CMS, copiée telle quelle : un diff
   entre les deux fichiers doit rester vide au-dessus de cette ligne. Ce qui
   suit est ce que le panneau a et que le produit n'a pas — deux choses.
   ========================================================================= */

/* La marque du rail est ici un lien vers le tableau de bord : le CMS y met un
   bloc inerte, parce qu'il a un écran d'accueil par rôle et pas un seul. */
a.sidebar-brand { text-decoration: none; color: inherit; }
a.sidebar-brand:hover .brand-name { color: #fff; }

/* Qui est connecté, au-dessus du bouton de déconnexion. */
.side-who {
  margin: 0 0 8px;
  padding-inline: 10px;
  font-size: 0.78rem;
  color: rgba(255, 255, 255, 0.66);
  overflow-wrap: anywhere;
}

/* Sur un écran étroit, le rail devient une barre : l'identifiant y prendrait
   la place des rubriques, comme le CMS y efface les libellés discrets. */
@media (max-width: 900px) {
  .side-who { display: none; }
}

/* Un champ de saisie libre dans une barre en ligne : assez large pour un nom
   de client, pas pour toute la carte. .input-sm, lui, est fait pour un nombre. */
.input-md { width: auto; min-width: 220px; max-width: 320px; }

/* =========================================================================
   L'espace client
   =========================================================================

   Il ne ressemble pas au panneau, et c'est voulu : le panneau est un outil
   qu'on ouvre tous les jours et qui mérite son rail ; l'espace est un lieu où
   l'on passe — regarder une facture, changer un effectif, poser une question.
   Il prend donc la forme du site : une barre en haut, une largeur de lecture,
   un pied. Les cartes, les tableaux et les boutons, eux, sont ceux du panneau :
   deux systèmes de composants pour une même maison, c'est deux fois le travail
   et deux fois les défauts.

   Les jetons de couleur sont communs : l'espace suit donc le thème clair, le
   thème sombre et la palette sans une ligne de plus.
   ========================================================================= */

.espace-body { background: var(--bg); min-height: 100vh; display: flex; flex-direction: column; }
.espace-wrap { width: 100%; max-width: 1160px; margin-inline: auto; padding-inline: 20px; }

.skip {
  position: absolute;
  inset-inline-start: -9999px;
  top: 0;
  padding: 10px 14px;
  background: var(--surface);
  z-index: 20;
}
.skip:focus { inset-inline-start: 0; }

/* La barre du site : la marque à gauche, ses pages, puis le thème et la sortie. */
.espace-nav {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--glass);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border-bottom: 1px solid rgb(var(--shadow-ink) / 0.07);
}
@supports not (backdrop-filter: blur(1px)) {
  .espace-nav { background: var(--surface); }
}
.espace-nav-in { display: flex; align-items: center; gap: 18px; min-height: 62px; }
.espace-brand { display: flex; align-items: center; gap: 10px; text-decoration: none; color: inherit; }
.espace-brand .brand-name { font-size: 1.02rem; font-weight: 600; letter-spacing: -0.01em; }
/* La marque prend deux tons, comme sur le site : c'est à cela qu'on la
   reconnaît autant qu'au sigle. */
.brand-accent { color: var(--accent); }
.espace-nav-links { display: flex; align-items: center; gap: 4px; margin-inline-start: 8px; flex: 1; }
.espace-nav-links a {
  padding: 7px 11px;
  border-radius: var(--radius-sm);
  font-size: 0.86rem;
  color: var(--text-secondary);
  text-decoration: none;
}
.espace-nav-links a:hover { background: var(--surface-2); color: var(--text); }
.espace-nav-right { display: flex; align-items: center; gap: 10px; }
.espace-qui { font-size: 0.84rem; color: var(--text-secondary); max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.espace-sortie { display: inline-flex; }

/* Les rubriques de l'espace : des onglets sous la barre, pas un rail. Six
   entrées ne méritent pas une colonne, et sur un téléphone la rangée défile
   au lieu d'élargir la page. */
.espace-rubriques { border-bottom: 1px solid var(--border); background: var(--surface); }
.espace-rubriques-in { display: flex; gap: 2px; overflow-x: auto; scrollbar-width: thin; }
.espace-onglet {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 12px 13px;
  border-bottom: 2px solid transparent;
  font-size: 0.87rem;
  color: var(--text-secondary);
  text-decoration: none;
  white-space: nowrap;
}
.espace-onglet:hover { color: var(--text); background: var(--surface-2); }
.espace-onglet.is-active { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }
.espace-onglet svg { flex-shrink: 0; }

.espace-main { flex: 1; padding-block: 26px 64px; }
.espace-tete { margin-bottom: 20px; }
.espace-tete h1 { font-size: 1.5rem; letter-spacing: -0.02em; }
.espace-tete p { margin-top: 4px; color: var(--text-secondary); font-size: 0.9rem; }

/* Un formulaire seul au milieu de la page : connexion, inscription. */
.espace-form-carte {
  max-width: 460px;
  margin-inline: auto;
  background: var(--surface);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm), var(--edge-light);
  padding: 26px 24px;
}
.espace-form-large { max-width: 720px; }
.stack-champs { display: flex; flex-direction: column; gap: 14px; }
.espace-alt { margin-top: 16px; font-size: 0.86rem; color: var(--text-secondary); text-align: center; }
.espace-note { margin-top: 18px; }
.espace-section {
  margin: 8px 0 0;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}
.espace-adresse { line-height: 1.7; font-size: 0.9rem; margin-top: 8px; }

.espace-pied { border-top: 1px solid var(--border); background: var(--surface); }
.espace-pied-in { padding-block: 20px; font-size: 0.82rem; color: var(--text-secondary); }
.espace-pied-note { color: var(--text-tertiary); margin-top: 3px; }

/* Un fil de discussion : les messages du client à gauche, les nôtres décalés et
   teintés. La couleur ne porte pas l'information — chaque message est signé. */
.fil { display: flex; flex-direction: column; gap: 12px; }
.fil-message {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px 14px;
  background: var(--surface-2);
}
.fil-message p { margin-top: 6px; font-size: 0.9rem; line-height: 1.6; }
.fil-tete { display: flex; align-items: baseline; gap: 10px; }
.fil-editeur { background: var(--accent-soft); border-color: var(--accent-ring); margin-inline-start: 24px; }

.input-sm { width: auto; min-width: 90px; max-width: 120px; }

@media (max-width: 720px) {
  .espace-nav-links { display: none; }
  .espace-qui { display: none; }
  .fil-editeur { margin-inline-start: 0; }
}

/* Le prix annoncé pendant qu'on tape l'effectif. Vide tant que le script n'a
   rien à dire — sans JavaScript, la phrase d'aide sous le champ suffit. */
.prix-vif { font-size: 0.95rem; font-weight: 600; color: var(--accent); min-height: 1.2em; }
.prix-vif:empty { display: none; }

/* Le sélecteur de langue de l'espace : discret, à côté du thème. */
.espace-langue select {
  width: auto;
  min-width: 0;
  padding: 5px 8px;
  font-size: 0.8rem;
}
.espace-langue { display: inline-flex; align-items: center; gap: 6px; }
@media (max-width: 720px) { .espace-langue select { max-width: 120px; } }

/* =========================================================================
   La fenêtre : un détail, ou un PDF, sans quitter la liste
   =========================================================================

   Le fond est translucide et flouté : la liste reste visible derrière, et l'on
   sait d'où l'on vient. Opaque, la fenêtre aurait l'air d'une autre page — et
   l'on cherche le bouton « retour » qui n'existe pas.
   ========================================================================= */

.fenetre-fond {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgb(var(--shadow-ink) / 0.42);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
@supports not (backdrop-filter: blur(1px)) {
  .fenetre-fond { background: rgb(var(--shadow-ink) / 0.6); }
}
/* Le fond fixe couvre la page ; sans cela, la page défile derrière la fenêtre
   et l'on perd sa ligne. */
body.fenetre-ouverte { overflow: hidden; }

.fenetre {
  width: min(880px, 100%);
  max-height: min(88vh, 900px);
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-float);
  overflow: hidden;
}
.fenetre-tete {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
}
.fenetre-tete h2 { font-size: 1.02rem; flex: 1; letter-spacing: -0.01em; }
.fenetre-actions { display: flex; gap: 8px; }
.fenetre-corps { padding: 18px; overflow-y: auto; }
.fenetre-lien { margin-top: 14px; font-size: 0.88rem; display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; }
.fenetre-montant { margin-top: 16px; }

/* Le PDF se lit dans la fenêtre : c'est le lecteur du navigateur, pas le
   nôtre — il sait déjà zoomer, chercher et imprimer. */
.fenetre-pdf {
  width: 100%;
  height: min(70vh, 760px);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
}

.icone-bouton {
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text-secondary);
  cursor: pointer;
  font-size: 0.85rem;
}
.icone-bouton:hover { color: var(--text); border-color: var(--text-tertiary); }

/* ---------- Les suggestions du champ de recherche ---------- */

.search-bar { position: relative; }
.suggestions {
  position: absolute;
  inset-block-start: calc(100% + 4px);
  inset-inline: 0;
  z-index: 15;
  background: var(--surface);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  overflow: hidden;
  max-height: 320px;
  overflow-y: auto;
}
.suggestion {
  display: flex;
  align-items: baseline;
  gap: 10px;
  width: 100%;
  padding: 9px 12px;
  border: none;
  border-bottom: 1px solid var(--border);
  background: transparent;
  font-family: var(--font-sans);
  font-size: 0.85rem;
  text-align: start;
  cursor: pointer;
  color: var(--text);
}
.suggestion:last-child { border-bottom: none; }
.suggestion:hover, .suggestion:focus-visible { background: var(--surface-2); }
.suggestion-numero { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.8rem; }
.suggestion-client { flex: 1; color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.suggestion-montant { font-weight: 600; font-variant-numeric: tabular-nums; }

/* ---------- Ce qui vient de changer ---------- */

/* La liste se tient à jour toute seule : un chiffre qui change sans rien dire
   passe inaperçu, et l'on se demande ensuite si on a mal lu. Il s'éclaire une
   fois, doucement, et revient à sa couleur. */
@keyframes vient-de-changer {
  from { background: var(--accent-soft); }
  to { background: transparent; }
}
.vient-de-changer { animation: vient-de-changer 1.6s var(--ease-out); }
@media (prefers-reduced-motion: reduce) {
  .vient-de-changer { animation: none; }
}
.pouls { font-variant-numeric: tabular-nums; color: var(--text-tertiary); }

/* Les entêtes qui trient sont des liens : ils se lisent comme du texte, et se
   cliquent comme un bouton. */
.table-cliquable thead a { color: inherit; text-decoration: none; }
.table-cliquable thead a:hover { color: var(--accent); }

/* Un délai se règle par un nombre, et un nombre ne dit rien tout seul : « 7 »
   n'est un réglage que si la ligne d'à côté dit sept quoi, et ce qui se passe à
   zéro. Le champ tient donc à droite du titre, et l'explication passe dessous,
   sur toute la largeur — la lire ne demande pas de survoler quoi que ce soit. */
.reglage-nombre { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 3px 10px; padding: 9px 0; }
.reglage-nombre input { width: 5.5rem; justify-self: end; }
.reglage-nombre .cell-sub { grid-column: 1 / -1; }
.reglage-nombre + .reglage-nombre { border-top: 1px solid var(--border); }
/* Les deux colonnes de réglages n'ont pas la même hauteur, et rien n'oblige la
   plus courte à s'étirer pour rattraper l'autre : un cadre à moitié vide
   laisse croire qu'il manque quelque chose dedans. */
.reglages-auto { align-items: start; }

/* ---------- Le rouet, et le chantier ---------- */

/* Une synchronisation dure près d'une minute : sans rien à regarder, on croit
   la page figée et on recharge — ce qui abandonne le chantier en cours. Le
   rouet ne décore donc pas, il répond à « est-ce que ça avance ? ». Il est
   « aria-hidden » parce que le texte d'à côté dit la même chose en mieux, et
   que la barre porte déjà « aria-live ». */
.rouet {
  display: inline-block;
  width: 13px;
  height: 13px;
  margin-inline-end: 7px;
  vertical-align: -1px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: rouet 0.7s linear infinite;
}
@keyframes rouet { to { transform: rotate(360deg); } }

/* Le respect du réglage système n'est pas une option ici : une animation qui
   tourne sans fin est exactement ce que « prefers-reduced-motion » demande
   d'arrêter. On garde le disque, on enlève la rotation — l'information « il se
   passe quelque chose » reste portée par le texte, qui est le vrai message. */
@media (prefers-reduced-motion: reduce) {
  .rouet { animation: none; border-top-color: currentColor; opacity: 0.55; }
}

.chantier { margin: 12px 0 4px; }
.chantier .meter { margin-bottom: 6px; }
.chantier .hint { margin: 0; }

/* Le bouton garde sa largeur quand le rouet apparaît : un bouton qui s'élargit
   au clic déplace ce qui l'entoure, et l'on croit avoir raté sa cible. */
[data-bouton-synchro] { min-width: 13.5rem; }

/* Le titre d'un chapitre de fiche porte sa pastille de crédit sur la même
   ligne : séparés, on lit le taux sans lire ce qu'il vaut, et c'est justement
   ce qu'on veut rendre impossible. */
.fiche-titre {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 18px 0 6px;
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}
.fiche-titre:first-of-type { margin-top: 10px; }

/* L'avancement d'un chantier. Un « progress » natif : le navigateur sait déjà
   le dessiner, l'annoncer aux lecteurs d'écran et le rendre sans JavaScript, et
   surtout il n'oblige pas à écrire une largeur dans un attribut « style » — ce
   que la politique de sécurité du panneau interdit, à raison. */
.avancee {
  display: block;
  width: 100%;
  height: 6px;
  margin-bottom: 7px;
  appearance: none;
  border: 0;
  border-radius: 999px;
  background: var(--surface-2);
  overflow: hidden;
}
.avancee::-webkit-progress-bar { background: var(--surface-2); border-radius: 999px; }
.avancee::-webkit-progress-value { background: var(--accent); border-radius: 999px; transition: inline-size 0.25s ease; }
.avancee::-moz-progress-bar { background: var(--accent); border-radius: 999px; }

/* Les sources d'un registre : un titre, ce qu'elles apportent, quand on les a
   interrogées. Du texte suivi, donc aligné à gauche et à pleine largeur — la
   liste de définitions du panneau colle la valeur à droite du terme, ce qui va
   bien à « TVA : 20 % » et très mal à un paragraphe. */
.sources { list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 14px; }
.source { padding-inline-start: 11px; border-inline-start: 2px solid var(--border); }
.source-tete { display: flex; align-items: center; gap: 8px; margin: 0 0 3px; font-weight: 600; font-size: 0.87rem; }
.source-quoi { margin: 0; font-size: 0.84rem; color: var(--text-secondary); max-width: 78ch; }
.source .cell-sub { margin-top: 3px; }
/* Une précision sous une valeur passe à la ligne : collée derrière, on lit
   « 29 à 42 % du brutVariable selon la taille », qui ne veut rien dire. */
.summary-list dd .cell-sub { display: block; margin-top: 2px; }
/* Une cadence se choisit dans une liste, et une liste de six intitulés ne tient
   pas dans la largeur d'un champ de nombre : celle-ci prend ce qu'il lui faut. */
.reglage-choix { width: auto; max-width: 15rem; justify-self: end; }

/* ═══════════════════════════════════════════════════════════════════════
   La fenêtre de l'installeur

   Une fenêtre de cinq cents pixels, ouverte par le CMS qu'un client installe.
   On y vient pour une chose — récupérer une clé — et l'on repart. D'où
   l'absence de barre, de pied et de menu : tout ce qui inviterait à aller
   ailleurs serait une invitation à ne pas finir.
   ═══════════════════════════════════════════════════════════════════════ */

.popup-body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  min-height: 100vh;
}

.popup {
  max-width: 34rem;
  margin: 0 auto;
  padding: 20px 20px 32px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.popup-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border);
}

/* La marque, pour qu'on sache chez qui l'on a tapé son mot de passe. Elle ne
   mène nulle part : ce n'est pas le moment de partir visiter le site. */
.popup-brand { display: inline-flex; align-items: center; gap: 8px; }

/* La destination, nommée en grand et tout en haut.

   C'est la seule chose que le client doit vérifier avant de cliquer : l'origine
   de retour est choisie par la page qui a ouvert cette fenêtre, et un CMS
   auto-hébergé n'a pas d'adresse connue d'avance — il n'y a donc pas de liste
   blanche possible. Personne ne peut décider à la place du client ; tout le
   monde peut lire une adresse. */
.popup-vers {
  margin: 0;
  padding: 12px 14px;
  border: 1px solid var(--accent-border, var(--border));
  border-radius: var(--radius);
  background: var(--surface-2);
  font-size: 0.88rem;
  color: var(--text-secondary);
}

.popup-vers strong {
  display: block;
  margin-top: 2px;
  font-size: 1rem;
  color: var(--text);
  word-break: break-all;
}

.popup-vers-ko {
  border-color: var(--warning-border);
  background: var(--warning-bg);
  color: var(--warning);
}

.popup-titre { margin: 4px 0 0; font-size: 1.32rem; }
.popup-lede { margin: 0; font-size: 0.92rem; color: var(--text-secondary); }
.popup-vide { margin: 0; font-size: 0.92rem; color: var(--text-tertiary); }

.popup-note {
  margin: 4px 0 0;
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--text-tertiary);
}

.popup-alt { margin: 0; font-size: 0.88rem; color: var(--text-secondary); }
.popup-qui { margin: 8px 0 0; font-size: 0.78rem; color: var(--text-tertiary); }

.popup-form { display: flex; flex-direction: column; gap: 12px; }
.popup-form label { display: flex; flex-direction: column; gap: 5px; font-size: 0.86rem; font-weight: 600; }
.popup-form input,
.popup-form select { font-size: 1rem; }

/* La liste des licences. Un clic par licence, et rien qui se déclenche tout
   seul — même quand il n'y en a qu'une : c'est la décision d'envoyer une clé
   d'activation à une adresse, et elle appartient au client. */
.popup-liste { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }

.popup-item form {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
}

.popup-item-quoi { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.popup-item-quoi strong { font-size: 0.92rem; word-break: break-all; }
.popup-item-note { font-size: 0.76rem; color: var(--text-tertiary); }

/* Une licence révoquée ou suspendue n'ouvre plus rien. On la montre — elle
   existe, le client la cherche — mais on ne la propose pas. */
.popup-item.is-morte form { opacity: 0.6; }

.popup-envoi { display: flex; flex-direction: column; gap: 12px; }

.popup-etat {
  margin: 0;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
  font-size: 0.92rem;
}

.popup-etat.is-ok { border-color: var(--success-border); background: var(--success-bg); color: var(--success); font-weight: 600; }
.popup-etat.is-warn { border-color: var(--warning-border); background: var(--warning-bg); color: var(--warning); }

/* La clé reste affichée même après un envoi réussi. Si la fenêtre a été
   ouverte à la main, si l'installeur a été rechargé, si le message se perd :
   le client la copie. Un écran qui se referme en emportant la seule copie de
   ce qu'on venait chercher serait une faute. */
.popup-cle { display: flex; flex-direction: column; gap: 5px; font-size: 0.86rem; font-weight: 600; }

.popup-cle input {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.92rem;
  font-weight: 400;
  word-break: break-all;
}
