/* ============================================================
   AfroPayroll — design system v4 "Clean Professional" (19/07/2026, demande
   par Issaka Keita : reset complet apres le v3 "Control Tower" sombre a
   accents neon, juge trop "gadget high-tech" pour un outil de paie. Direction
   : clair par defaut, un seul accent restreint (teal profond, desature),
   gris neutres froids, entetes de tableau neutres (fini les blocs noirs) -
   les surfaces de TRAVAIL (tableaux, formulaires, cartes) restent claires et
   sobres. Le theme sombre reste disponible via la bascule, comme compagnon
   raffine, mais n'est plus le theme par defaut (cf. base.html).

   v4.1 (19/07/2026, meme session, demande "plus robuste, moderne, high-tech")
   - reintroduit un peu de "tech" mais uniquement dans le CHROME de
   navigation (menu lateral, marque, barre de progression), jamais dans les
   surfaces de travail : le menu lateral redevient sombre en PERMANENCE (les
   deux themes, cf. --sidebar-* non redefinis dans le bloc sombre plus bas)
   avec un accent vif dedie (--accent-glow) et un leger glow sur la marque et
   l'etat actif.

   v4.2 (08/08/2026, demande Issaka Keita : "change la couleur de
   l'application en copiant celle de Outlook", capture d'ecran du client
   Outlook classique a l'appui) - l'accent unique du systeme (--teal et ses
   derives : --heading-teal, --accent-glow, --gradient-brand, le degrade du
   menu lateral) passe du teal desature au bleu Outlook/Fluent (#0078D4 et sa
   famille). Seule la TEINTE change ; la structure du v4 (surfaces de travail
   claires, chrome sombre permanent pour le menu lateral) est conservee telle
   quelle.

   v4.3 (10/08/2026, chantier "AfroPayroll SaaS" - refonte progressive vers
   un produit RH & paie commercialisable) - alignement du systeme de tokens
   sur le design system cible valide par Issaka Keita : --teal/--heading-teal
   (couleur principale) et --teal-dark (couleur foncee) passent du bleu
   Outlook (#0078D4) a #1677D2 / #123B63 ; --grey-bg, --text-primary,
   --text-secondary, --success, --danger, --amber sont recales sur les
   valeurs exactes du brief (fond #F6F8FB, texte #172033/#667085, succes
   #12B76A, danger #F04438, avertissement #F79009) ; nouveau token --info
   (#2E90FA, pas encore cable dans un composant, reserve aux prochains
   usages "information"). --radius-md (boutons/champs) passe de 6px a 7px et
   --radius-lg (cartes) de 8px a 10px. --navy (encre des boutons primaires
   .btn-navy et des titres, cf. --heading-navy) n'est PAS touche par ce
   changement : c'est une famille de tokens neutre distincte de l'accent
   bleu, deja choisie deliberement le 19/07/2026 ("un seul accent
   restreint") - une fusion des deux relève d'une decision de hierarchie de
   boutons (Phase 3/4), pas d'un simple recalage de valeurs. */

:root {
  /* color-scheme (18/07/2026, releve : icone du selecteur de date quasi
     invisible en theme sombre) - sans cette declaration, le navigateur
     rend TOUJOURS les controles natifs (icone + popup du <input
     type="date">, curseurs de type range, cases a cocher natives...) en
     clair, quel que soit le theme visuel de la page - icone gris fonce sur
     fond sombre, illisible. Bascule avec le theme actif (cf. le bloc
     :root[data-theme="dark"] plus bas). */
  color-scheme: light;
  --navy: #1f2a37;
  --navy-dark: #10151d;
  --navy-light: #37455a;
  /* Design system "AfroPayroll SaaS" (10/08/2026, cf. changelog v4.3 plus
     haut) - couleur principale #1677D2, remplace le bleu Outlook/Fluent
     (#0078D4). */
  --teal: #1677d2;
  --teal-light: #3d93de;
  --teal-dark: #123b63;
  --amber: #f79009;
  --purple: #5b4fc4;

  --grey-bg: #f6f8fb;
  --grey-bg-alt: #eef0f3;
  --grey-border: #e2e5ea;
  --grey-border-strong: #c7ccd4;
  --danger: #f04438;
  --danger-bg: #fdecea;
  --success: #12b76a;
  --success-bg: #e6f9f1;
  --info: #2e90fa;
  --text-primary: #172033;
  --text-secondary: #667085;
  --text-muted: #8891a0;

  --surface: #ffffff;
  --surface-alt: #fafbfc;
  --row-hover: #f2f7f6;
  /* Bande de navigation du haut (ruban + tiroir) - 10/09/2026, demande
     d'Issaka Keita : nettement plus grisée que le reste de l'application
     (fond de page #f6f8fb, cartes blanches), pour la détacher du contenu. */
  --nav-band: #e7ebf1;
  --flash-warning-bg: #fff7e6;
  --flash-warning-text: #93370d;
  /* Titres : une seule encre neutre pour h1 ET h2 (19/07/2026, finalise la
     simplification amorcee le 16/07 : --heading-teal ne sert plus pour le
     TEXTE des titres, seulement pour les usages d'accent explicites - liens,
     etat actif, puce de .section-eyebrow, icones KPI). */
  --heading-navy: #172033;
  --heading-teal: #1677d2;
  --text-muted-on-bg: #667085;
  --text-on-tinted: #4b5563;

  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;

  /* Accent "glow" dedie (19/07/2026, v4.1) - plus vif/sature que --teal
     (reserve aux surfaces de travail claires), utilise uniquement sur le
     chrome sombre permanent (marque, etat actif, barre de progression) ou un
     accent aussi vif resterait illisible/criard sur fond clair. Constant
     dans les deux themes (non redefini plus bas), comme le menu lateral. */
  --accent-glow: #4f9fe8;

  /* Les jetons --sidebar-* (menu lateral bleu permanent, 05/08/2026-
     08/09/2026) ont disparu le 09/09/2026 avec le passage au ruban de
     navigation clair .app-ribbon, qui reutilise les jetons de surface
     standard (--surface/--surface-alt/--heading-teal) au lieu d'un jeu de
     couleurs dedie - cf. .app-ribbon plus bas pour le detail. */

  --gradient-brand: linear-gradient(135deg, #123b63 0%, var(--accent-glow) 100%);
  --gradient-brand-soft: rgba(22, 119, 210, 0.06);

  --radius-sm: 4px;
  --radius-md: 7px;
  --radius-lg: 10px;
  --radius-pill: 999px;

  --shadow-xs: 0 1px 2px rgba(15, 23, 32, 0.04);
  --shadow-freeze-edge: 2px 0 4px -2px rgba(15, 23, 32, 0.18);
  --shadow-sm: 0 2px 6px rgba(15, 23, 32, 0.06);
  --shadow-md: 0 8px 20px rgba(15, 23, 32, 0.08);
  --shadow-lg: 0 20px 44px rgba(15, 23, 32, 0.14);
  --shadow-focus: 0 0 0 3px rgba(22, 119, 210, 0.18);
  --shadow-focus-danger: 0 0 0 3px rgba(240, 68, 56, 0.18);

  --ease: cubic-bezier(0.4, 0, 0.2, 1);
}

/* ---------------------------------------------------- theme sombre (v4) -
   compagnon raffine de la bascule (19/07/2026) - PLUS le theme par defaut
   (cf. base.html : un visiteur sans preference stockee recoit desormais le
   theme clair). Moins sature que le v3 "Control Tower" (pas de glow), meme
   structure de tokens que le theme clair ci-dessus. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --navy: #64748b;
  --navy-dark: #05070a;
  --navy-light: #7c8ba3;
  /* Couleur principale, variante eclaircie pour rester lisible sur fond
     sombre (10/08/2026, cf. changelog v4.3 en tete de fichier). */
  --teal: #4c9fef;
  --teal-light: #7ab8f5;
  --teal-dark: #123b63;
  --amber: #fdb022;
  --purple: #8f83e0;
  --grey-bg: #0f1115;
  --grey-bg-alt: #161920;
  --grey-border: #262b33;
  --grey-border-strong: #333a44;
  --danger: #f97066;
  --danger-bg: rgba(240, 68, 56, 0.12);
  --success: #3ddb8f;
  --success-bg: rgba(18, 183, 106, 0.12);
  --info: #2e90fa;
  --text-primary: #e7e9ec;
  --text-secondary: #a3aab5;
  --text-muted: #767e8a;

  --surface: #161920;
  --surface-alt: #1b1f27;
  /* Bande de navigation du haut : plus claire que le fond de page #0f1115
     et distincte des cartes #161920 - même rôle qu'en thème clair. */
  --nav-band: #22262f;
  --row-hover: rgba(76, 159, 239, 0.06);
  --flash-warning-bg: rgba(253, 176, 34, 0.12);
  --flash-warning-text: #fdb022;
  --heading-navy: #e7e9ec;
  --heading-teal: #4c9fef;
  --text-muted-on-bg: #a3aab5;
  --text-on-tinted: #b7bec8;

  /* --accent-glow et --gradient-brand ne sont PAS redefinis ici (19/07/2026,
     v4.1) : la marque reste constante dans les deux themes, cf. le bloc
     :root ci-dessus. Le ruban de navigation (.app-ribbon, 09/09/2026)
     reutilise --surface/--heading-teal, deja redefinis ci-dessus pour le
     theme sombre - rien de dedie a redefinir ici non plus. */

  --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.35);
  --shadow-freeze-edge: 2px 0 4px -1px rgba(0, 0, 0, 0.5);
  --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.4);
  --shadow-md: 0 10px 24px rgba(0, 0, 0, 0.45);
  --shadow-lg: 0 20px 48px rgba(0, 0, 0, 0.55);
  --shadow-focus: 0 0 0 3px rgba(76, 159, 239, 0.28);
  --shadow-focus-danger: 0 0 0 3px rgba(249, 112, 102, 0.28);
}

* { box-sizing: border-box; }

html { -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; }

/* Lien generique (16/07/2026) : aucune regle globale n'existait avant cette
   refonte - chaque lien "nu" (tableaux, texte d'aide...) heritait du bleu
   par defaut du navigateur, particulierement visible/deplace sur le fond
   quasi-noir du theme sombre. .btn/.nav-icon/.card etc. definissent deja
   leur propre couleur et ne sont pas affectes (specificite egale ou
   superieure sur ces classes). */
a { color: var(--heading-teal); }
a:hover { color: var(--teal-light); }

body {
  /* Police "Inter" (10/08/2026, design system SaaS) - chargee via Google
     Fonts dans base.html <head> (avec repli silencieux si hors-ligne, cf.
     usage desktop) ; la pile systeme reste le filet de secours immediat si
     la police distante n'a pas encore charge ou n'est pas disponible. */
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Roboto, Arial, sans-serif;
  margin: 0;
  background: var(--grey-bg);
  color: var(--text-primary);
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}
/* 09/09/2026 : le menu lateral fixe (qui reservait cet espace via
   padding-left) est remplace par le ruban horizontal .app-ribbon -
   plus aucune page n'a besoin de reserver d'espace lateral, connectee ou
   non (la classe body.no-sidebar devient donc sans effet et n'est plus
   posee cote HTML). */

/* Scrollbar soignee (Chromium/Edge) - detail "produit fini" */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--grey-border-strong); border-radius: var(--radius-pill); border: 2px solid var(--grey-bg); }
::-webkit-scrollbar-thumb:hover { background: #a9b6c0; }

/* ------------------------------------------------------------ nprogress-bar */
/* Barre de progression de navigation (16/07/2026) - cf. base.html. */
#nprogress-bar {
  position: fixed;
  top: 0;
  left: 0;
  height: 3px;
  width: 0;
  opacity: 0;
  background: var(--gradient-brand);
  z-index: 400;
  box-shadow: 0 0 8px rgba(0, 120, 212, 0.6);
}

/* Etat de chargement d'un bouton apres soumission d'un formulaire
   (16/07/2026) - empeche visuellement/fonctionnellement la double-
   soumission (cf. base.html). */
.btn-loading { position: relative; color: transparent !important; pointer-events: none; }
.btn-loading::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 15px;
  height: 15px;
  margin: -7.5px 0 0 -7.5px;
  border: 2px solid rgba(255, 255, 255, 0.4);
  border-top-color: #fff;
  border-radius: 50%;
  animation: btn-spin 0.6s linear infinite;
}
.btn-outline.btn-loading::after { border-color: rgba(11, 61, 92, 0.3); border-top-color: var(--navy); }
@keyframes btn-spin { to { transform: rotate(360deg); } }

/* --------------------------------------------------------- app-ribbon --- */
/* Ruban de navigation façon Infor SunSystems (09/09/2026, demande explicite
   d'Issaka Keita sur captures fournies) - remplace le menu lateral fixe
   bleu marine par un bandeau clair d'onglets horizontaux + un tiroir de
   liens juste en dessous. Contrairement a l'ancien .app-sidebar, ce n'est
   plus jamais un panneau colore permanent : fond --surface (identique au
   reste des surfaces de travail), un seul jeton d'accent (--heading-teal)
   pour l'onglet actif - responsive par construction (defilement horizontal
   des onglets, grille du tiroir qui se reforme), aucun tiroir coulissant
   plein ecran ni media query dediee necessaire. */
.app-ribbon {
  background: var(--nav-band);
  border-bottom: 1px solid var(--grey-border);
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0 20px;
  position: sticky;
  top: 0;
  z-index: 60;
}
.ribbon-brand {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 16px 12px 0;
  font-weight: 700;
  font-size: 0.96rem;
  letter-spacing: -0.01em;
  color: var(--heading-navy);
  flex-shrink: 0;
}
.ribbon-brand::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: radial-gradient(circle at 30% 30%, #ffffff, var(--accent-glow) 65%);
  box-shadow: 0 0 8px var(--accent-glow);
  flex-shrink: 0;
}
.ribbon-tabs {
  display: flex;
  /* Occupe l'espace entre la marque et le contexte utilisateur (cale a
     droite) ; min-width:0 pour pouvoir retrecir et faire defiler les
     onglets plutot que de repousser le contexte utilisateur hors ecran. */
  flex: 1 1 auto;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: thin;
}
.ribbon-tab {
  appearance: none;
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--text-secondary);
  font: inherit;
  font-size: 0.82rem;
  font-weight: 500;
  padding: 13px 14px 11px;
  white-space: nowrap;
  cursor: pointer;
  flex-shrink: 0;
  transition: color 0.15s var(--ease), border-color 0.15s var(--ease);
}
.ribbon-tab:hover { color: var(--heading-navy); }
.ribbon-tab.active { color: var(--heading-teal); border-bottom-color: var(--heading-teal); font-weight: 600; }

/* Tiroir (09/09/2026) : un seul .ribbon-panel visible a la fois (les autres
   caches via [hidden], jamais retires du DOM - cf. base.html pour la
   palette de commandes qui en depend). */
.ribbon-drawer {
  background: var(--nav-band);
  border-bottom: 1px solid var(--grey-border-strong);
  box-shadow: var(--shadow-xs);
  padding: 6px 14px;
}
.ribbon-panel[hidden] { display: none; }
/* Le tiroir ouvert s'etale en colonnes de 3 rangees maxi, de gauche a
   droite sur toute la largeur (demande explicite du 09/09/2026 : limiter
   la profondeur de la bande) - un tiroir de 15 liens (ex. "Listes de
   reference") tient ainsi sur 3 rangees / 5 colonnes plutot que 15 lignes
   empilees. Le flux "column" remplit chaque colonne de haut en bas ("3 sur
   une colonne, ainsi de suite") ; overflow-x en filet de securite si
   l'ecran est trop etroit pour toutes les colonnes. */
.ribbon-panel {
  display: grid;
  grid-auto-flow: column;
  grid-template-rows: repeat(3, auto);
  /* Colonnes calées à gauche, largeur bornée : un tiroir de 2-3 liens ne
     s'étire pas sur toute la fenêtre (10/09/2026), un tiroir long
     remplit vers la droite et défile au besoin. */
  grid-auto-columns: minmax(190px, 20rem);
  justify-content: start;
  gap: 1px 8px;
  overflow-x: auto;
}
.ribbon-panel a {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--text-primary);
  text-decoration: none;
  padding: 8px 10px;
  font-size: 0.82rem;
  font-weight: 500;
  border-radius: var(--radius-sm);
  transition: color 0.15s var(--ease), background-color 0.15s var(--ease);
}
.ribbon-panel a .nav-icon { display: inline-flex; align-items: center; justify-content: center; width: 15px; flex-shrink: 0; color: var(--text-secondary); }
.ribbon-panel a:hover { color: var(--heading-teal); background: var(--surface); }
.ribbon-panel a.active {
  color: var(--heading-teal);
  background: var(--surface);
  font-weight: 600;
}
.ribbon-panel a.active .nav-icon { color: var(--heading-teal); }

/* Pastille de comptage (17/07/2026) - nombre d'elements en attente d'une
   decision de l'utilisateur courant sur un lien d'approbation (Timesheet /
   Conges / Missions), cf. routes_users.py::inject_pending_counts(). Absente
   du DOM quand le compte est a 0 (cf. base.html), jamais juste masquee.
   Regle de base non scopee a .app-sidebar (17/07/2026) : reutilisee aussi
   sur la carte "A traiter" du tableau de bord. */
.nav-badge {
  margin-left: auto;
  background: var(--teal);
  color: #04211d;
  font-size: 0.66rem;
  font-weight: 700;
  line-height: 1;
  padding: 3px 6px;
  border-radius: var(--radius-pill);
  flex-shrink: 0;
}
/* Le badge garde son fond --teal par defaut y compris sur un lien
   .ribbon-panel a.active (09/09/2026) : contrairement a l'ancien menu
   lateral sombre, le fond actif du tiroir reste clair (--surface) - le
   contraste du badge par defaut y est deja suffisant, aucune variante
   dediee necessaire. */
/* Variante ambre (tableau de bord) - #fff sur --amber ne passait pas 3:1 en
   theme sombre (2.11:1 mesure) ; encre foncee dediee, comme le badge teal
   ci-dessus utilise deja une encre foncee plutot que du blanc. */
.nav-badge-amber { background: var(--amber); color: #100b03; }

/* ------------------------------------------------- contexte utilisateur */
/* Identite / langue / mot de passe / deconnexion / recherche / theme -
   10/09/2026, demande explicite d'Issaka Keita : ramene sur la MEME ligne
   que les onglets du ruban et cale a l'extreme droite (fin de .app-ribbon),
   la bande .topbar separee juste en dessous est supprimee. */
.app-ribbon .user-box {
  margin-left: auto;
  flex-shrink: 0;
  padding-left: 16px;
  font-size: 0.82rem;
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  gap: 10px;
}
.app-ribbon .user-box a { color: var(--heading-teal); text-decoration: none; font-weight: 600; }
.app-ribbon .user-box a:hover { text-decoration: underline; }

/* Identite du compte connecte (16/07/2026, demande "design better" - le nom
   et le role tenaient auparavant sur une seule ligne compacte "Nom (role)",
   peu lisible). Nom en avant, role en second plan discret juste en dessous. */
.user-identity { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.2; }
.user-identity-name { font-weight: 600; color: var(--text-primary); }
.user-identity-role { font-size: 0.72rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; }

/* Separateur discret entre les groupes du bandeau superieur (identite /
   langue / compte / actions) - remplace l'absence totale d'espacement qui
   faisait fusionner visuellement "FR" et "EN" en un seul mot illisible. */
.topbar-divider { width: 1px; height: 20px; background: var(--grey-border); flex-shrink: 0; }

/* Bascule de langue en pilule segmentee (16/07/2026) - remplace 2 liens nus
   accoles ("FRIN") par un controle explicite a 2 etats. */
.lang-switch {
  display: inline-flex;
  background: var(--grey-bg-alt);
  border-radius: var(--radius-pill);
  padding: 2px;
  gap: 2px;
}
.app-ribbon .user-box .lang-switch a {
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  font-size: 0.74rem;
  font-weight: 700;
  color: var(--text-muted);
}
.app-ribbon .user-box .lang-switch a:hover { text-decoration: none; color: var(--text-primary); }
.app-ribbon .user-box .lang-switch a.active { background: var(--surface); color: var(--heading-teal); box-shadow: var(--shadow-xs); }

/* Plus de sidebar fixe a compenser depuis le 09/09/2026 (cf. .app-ribbon) -
   le container n'a besoin que d'un espacement interieur regulier, jamais de
   marge asymetrique. max-width volontairement absent (demande du
   08/07/2026 : le contenu doit utiliser toute la largeur disponible). */
.container { max-width: none; margin: 22px 28px; }

/* Ecrans "sans session" (connexion, mot de passe oublie...) - 19/07/2026,
   remplace la simple carte flottante (margin: auto) par une mise en page
   verticalement centree avec une marque visible : c'etait jusqu'ici la
   seule page de l'application ou "AfroPayroll" n'apparaissait nulle part
   (la marque vit dans .ribbon-brand, absent avant connexion). */
.auth-shell {
  min-height: 70vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 22px;
  padding: 24px 16px;
}
.auth-brand {
  display: flex;
  align-items: center;
  gap: 9px;
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: -0.01em;
  color: var(--heading-navy);
}
.auth-brand::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent-glow);
  box-shadow: 0 0 10px var(--accent-glow);
  flex-shrink: 0;
}
.auth-card { width: 100%; max-width: 380px; box-shadow: var(--shadow-md); }
/* Accroche commerciale sous la marque (10/08/2026, positionnement SaaS) -
   affichee sur les pages sans session (connexion, mot de passe oublie),
   juste sous .auth-brand. */
.auth-tagline { margin: -14px 0 0; font-size: 0.85rem; color: var(--text-secondary); text-align: center; }

/* Pages d'erreur 404/500 (19/07/2026) - remplace le simple <h1> generique
   par un traitement du code d'erreur coherent avec le reste du systeme (les
   chiffres sont toujours en monospace, cf. .num/.kpi .value). Utilisable
   authentifie (ruban de navigation present, carte centree dans le contenu)
   ou non (cf. .auth-shell plus haut). */
.error-page { max-width: 460px; margin: 70px auto; text-align: center; box-shadow: var(--shadow-md); }
.error-code {
  font-family: var(--font-mono);
  font-size: 4rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--heading-teal);
  margin: 0 0 4px;
  line-height: 1;
}
.error-title { font-size: 1.1rem; font-weight: 600; color: var(--heading-navy); margin: 0 0 8px; }
.error-desc { color: var(--text-secondary); margin: 0 0 22px; }

/* Pages "compactes" (08/07/2026, demande par Issaka Keita ; resserre
   encore le 31/08/2026, demande explicite "au strict minimum") : Employes
   et Paie mensuelle (liste + detail d'une periode) contiennent de larges
   tableaux de travail - on reduit l'espace haut/bas/lateral autour d'elles
   (par rapport au reste de l'application) pour laisser le maximum de place
   au tableau. Applique via {% block body_class %}compact-page{% endblock %}
   dans le template (cf. base.html).
   BUG corrige le 31/08/2026 : .container definit sa marge via le
   raccourci `margin: 22px 28px` (cf. regle .container plus haut) - cette
   regle-ci ne redefinissait jusqu'ici QUE `margin-top` (+ un `padding`
   s'ajoutant PAR-DESSUS), laissant `margin-right/bottom/left` a leurs
   valeurs larges d'origine (28px/22px/28px) : l'espace lateral/bas
   "compact" etait donc en realite PLUS grand qu'une page normale, jamais
   reduit comme le nom de la classe le laissait croire. Le raccourci
   `margin` est desormais explicitement redefini sur les 4 cotes. */
body.compact-page .container { margin: 6px 8px; padding: 0; }
body.compact-page h1 { margin: 0 0 6px; }
body.compact-page .card { padding: 10px 12px; margin-bottom: 8px; }
body.compact-page .kpi-row { margin-bottom: 8px; }
body.compact-page .kpi { padding: 10px 12px; }
/* .kpi-icon (taille/marge compacte) : regle complete plus bas, juste apres
   la definition des badges colores .kpi-icon-*, pour eviter 2 fragments
   distants du meme selecteur qui se contredisent selon l'ordre du fichier. */
body.compact-page .card h2 { font-size: 0.95rem; margin-bottom: 6px; }
body.compact-page .footer-note { margin: 10px 0; }

/* ---------------------------------------------------------- tabs-nav --- */
/* Onglets (12/07/2026) - utilises par la page Listes de reference pour
   remplacer un long defilement unique par des sections consultables une a
   une. Generique : reutilisable par toute autre page ayant besoin du meme
   decoupage (.tabs-nav + boutons .tab-btn[data-tab], panneaux .tab-panel
   affiches/masques via JS, cf. lists_view.html). */
.tabs-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--grey-border);
  padding-bottom: 0;
}
.tab-btn {
  background: transparent;
  border: 1px solid transparent;
  border-bottom: none;
  color: var(--navy);
  font-weight: 600;
  font-size: 0.88rem;
  padding: 9px 14px;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  cursor: pointer;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.tab-btn:hover { background: var(--grey-bg-alt); }
.tab-btn.active {
  background: var(--surface);
  color: var(--heading-navy);
  border-color: var(--grey-border);
  border-bottom: 1px solid var(--surface);
  margin-bottom: -1px;
}

/* Barre d'onglets defilante de la fiche employe (18/08/2026, demande
   explicite d'Issaka Keita) - PORTEE VOLONTAIREMENT LIMITEE a
   #fiche-employe-tabs (id, pas la classe .tabs-nav generique ci-dessus) :
   les autres pages qui reutilisent .tabs-nav (Listes de reference,
   Parametres) gardent leur comportement "passe a la ligne" inchange.
   #fiche-employe-tabs reste au contraire sur UNE SEULE ligne et defile
   horizontalement (11 onglets ne tiennent plus tous sur un ecran etroit),
   avec une scrollbar visible en bas de la barre plutot que masquee. */
.tabs-nav-wrap {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-bottom: 20px;
}
.tabs-nav-wrap .tabs-nav { margin-bottom: 0; }
#fiche-employe-tabs {
  flex-wrap: nowrap;
  overflow-x: auto;
  min-width: 0;
  scrollbar-width: thin;
  scrollbar-color: var(--grey-border-strong) var(--grey-bg-alt);
}
#fiche-employe-tabs .tab-btn { flex-shrink: 0; white-space: nowrap; }
#fiche-employe-tabs::-webkit-scrollbar { height: 8px; }
#fiche-employe-tabs::-webkit-scrollbar-track { background: var(--grey-bg-alt); }
#fiche-employe-tabs::-webkit-scrollbar-thumb { background: var(--grey-border-strong); border-radius: 4px; }
.tabs-nav-arrow {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface);
  border: 1px solid var(--grey-border);
  border-radius: var(--radius-sm);
  color: var(--navy);
  font-size: 1.15rem;
  line-height: 1;
  cursor: pointer;
}
.tabs-nav-arrow:hover { background: var(--grey-bg-alt); border-color: var(--grey-border-strong); }

/* ------------------------------------------------------------------ card */
.card {
  background: var(--surface);
  border: 1px solid var(--grey-border);
  border-radius: var(--radius-lg);
  padding: 20px 22px;
  margin-bottom: 20px;
  box-shadow: var(--shadow-xs);
  transition: box-shadow 0.18s var(--ease), border-color 0.18s var(--ease);
}
.card:hover { box-shadow: var(--shadow-sm); border-color: var(--grey-border-strong); }

/* Sections de paramètres repliables (10/09/2026, demande d'Issaka Keita :
   référentiels & workflow d'approbation repliés au repos, dépliés à la
   demande). <details>/<summary> natif - aucun JS. */
details.param-collapsible { margin-bottom: 12px; }
.param-summary {
  color: var(--heading-navy);
  font-size: 1.08rem;
  font-weight: 650;
  letter-spacing: -0.005em;
  list-style: none;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  display: flex;
  align-items: center;
  gap: 10px;
}
.param-summary::-webkit-details-marker { display: none; }
.param-summary::before {
  content: "";
  width: 0;
  height: 0;
  border-left: 6px solid var(--text-secondary);
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  flex-shrink: 0;
  transition: transform 0.15s var(--ease);
}
details.param-collapsible[open] .param-summary { margin-bottom: 14px; }
details.param-collapsible[open] .param-summary::before { transform: rotate(90deg); }
.param-summary:hover, .card:hover .param-summary { color: var(--heading-teal); }
.param-summary:hover::before { border-left-color: var(--heading-teal); }

h1 {
  color: var(--heading-navy);
  font-size: 1.6rem;
  font-weight: 700;
  letter-spacing: -0.015em;
  margin: 0 0 18px 0;
}
h2 {
  color: var(--heading-navy);
  font-size: 1.08rem;
  font-weight: 650;
  letter-spacing: -0.005em;
  margin: 0 0 14px 0;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ----------------------------------------------------------------- icon */
/* Icones SVG inline (12/07/2026, remplacement des emojis - cf. icons.py) -
   generique, utilisee partout ou une icone accompagne du texte (nav,
   titres de section, boutons). currentColor : herite automatiquement la
   couleur du texte parent, pas de gestion de couleur separee necessaire. */
.icon { display: inline-block; flex-shrink: 0; vertical-align: -0.15em; }

/* --------------------------------------------------------------- badges */
.badge {
  display: inline-block;
  padding: 3px 11px;
  border-radius: var(--radius-pill);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  background: var(--grey-bg-alt);
  color: var(--text-on-tinted);
}
.badge-brouillon { background: var(--grey-bg-alt); color: var(--text-on-tinted); }
.badge-valide_preparateur { background: var(--flash-warning-bg); color: var(--flash-warning-text); }
.badge-cloture { background: var(--success-bg); color: var(--success); }
/* Statut "annule" du module Conges (18/07/2026) - meme traitement visuel
   attenue que brouillon (etat definitif mais non-actif, pas d'urgence). */
.badge-annule { background: var(--grey-bg-alt); color: var(--text-muted); }

/* ---------------------------------------------------------- row-actions */
/* Actions par ligne de tableau (18/07/2026, demande "form not high tech,
   make more pro and readable") - remplace un mur de liens texte accoles
   ("Modifier Relevé INPS Simuler net Supprimer Suspendre") par des
   boutons-icones compacts, scannables meme sur des dizaines de lignes. Le
   libelle reste disponible via title="" (tooltip navigateur + lecteur
   d'ecran), jamais uniquement visuel. Fonctionne aussi bien sur un <a> que
   sur un <button type=submit> dans un <form>, poses cote a cote. */
.row-actions { display: flex; align-items: center; gap: 2px; flex-wrap: wrap; }
.row-action-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  text-decoration: none;
  flex-shrink: 0;
  transition: background-color 0.14s var(--ease), color 0.14s var(--ease), border-color 0.14s var(--ease);
}
.row-action-btn:hover { background: var(--grey-bg-alt); color: var(--heading-navy); border-color: var(--grey-border); }
.row-action-btn.row-action-danger:hover { background: var(--danger-bg); color: var(--danger); border-color: var(--danger); }
.row-action-btn.row-action-amber:hover { background: var(--flash-warning-bg); color: var(--amber); border-color: var(--amber); }
.row-action-sep { width: 1px; height: 18px; background: var(--grey-border); margin: 0 3px; flex-shrink: 0; }

/* ---------------------------------------------------------- action-menu */
/* Menu d'actions "⋮" par ligne (10/08/2026, design system SaaS) - pour les
   listes a nombreuses actions (ex. Employés) ou l'ancien rang d'icones
   .row-action-btn devenait trop large/dense. Composant generique reutilise
   par n'importe quel tableau : cf. le gestionnaire de clic delegue dans
   base.html (une seule ecoute globale, pas de JS par instance). Un <form>
   POST peut etre place directement DANS .action-menu-panel comme n'importe
   quel autre item (data-confirm-message continue de fonctionner normalement,
   cf. la modale de confirmation partagee plus bas dans base.html). */
.action-menu { position: relative; display: inline-flex; }
/* position: fixed, plutot que absolute (15/08/2026, rapport de test
   utilisateur, constat A6) : un .action-menu vit presque toujours dans un
   conteneur de tableau en overflow-x: auto (.table-wrap/.table-freeze-
   scroll), qui force implicitement aussi overflow-y a non-visible (regle du
   spec CSS : un axe non-visible entraine l'autre) - le panneau, positionne
   en absolute, etait alors rogne/invisible des que la ligne se trouvait pres
   du bord du conteneur (derniere ligne, ou liste filtree tenant sur une
   hauteur reduite). En fixed, le panneau est positionne en JS (cf.
   base.html) via getBoundingClientRect() du bouton declencheur - echappe a
   tout overflow ancetre, quel que soit le tableau qui l'héberge. */
.action-menu-panel {
  position: fixed;
  z-index: 400;
  min-width: 210px;
  background: var(--surface);
  border: 1px solid var(--grey-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  padding: 5px;
  display: none;
}
.action-menu.open .action-menu-panel { display: block; }
.action-menu-item {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 7px 9px;
  border: none;
  background: transparent;
  color: var(--text-primary);
  font-size: 0.85rem;
  text-align: left;
  text-decoration: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.action-menu-item:hover { background: var(--grey-bg-alt); }
.action-menu-item.action-menu-item-danger { color: var(--danger); }
.action-menu-item.action-menu-item-danger:hover { background: var(--danger-bg); }
.action-menu-item svg { flex-shrink: 0; opacity: 0.8; }
.action-menu-form { margin: 0; }
.action-menu-sep { height: 1px; background: var(--grey-border); margin: 5px 3px; }

/* --------------------------------------------------------------- tables */
table { width: 100%; border-collapse: collapse; font-size: 0.87rem; }
th, td { padding: 9px 11px; border-bottom: 1px solid var(--grey-border); text-align: left; }
th {
  background: var(--grey-bg-alt);
  color: var(--text-secondary);
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  border-bottom: 1px solid var(--grey-border-strong);
  position: sticky;
  top: 0;
}
tbody tr { transition: background-color 0.1s var(--ease); }
tbody tr:nth-child(even) { background: var(--surface-alt); }
tbody tr:hover { background: var(--row-hover); }
.table-wrap { overflow-x: auto; border-radius: var(--radius-md); }
.text-right { text-align: right; }

/* Réconciliation Fiscale et Sociale (10/09/2026, demande d'Issaka Keita) :
   une ligne par période, le détail des 5 types d'impôt/cotisation replié
   à l'intérieur (bouton triangle). */
.reco-table tbody.reco-groupe { border-bottom: 2px solid var(--grey-border-strong); }
.reco-table .reco-synthese { cursor: default; font-weight: 500; }
.reco-table .reco-toggle-cell { width: 34px; text-align: center; }
.reco-toggle {
  appearance: none;
  border: none;
  background: transparent;
  color: var(--text-secondary);
  font-size: 0.8rem;
  line-height: 1;
  padding: 4px 6px;
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.reco-toggle:hover { background: var(--grey-bg-alt); color: var(--heading-teal); }
.reco-groupe.reco-ouvert .reco-synthese { background: var(--gradient-brand-soft); }
.reco-detail[hidden] { display: none; }
.reco-detail > td { padding: 0 12px 4px 34px; background: var(--surface-alt); }
.reco-sous-table { width: 100%; font-size: 0.82rem; margin: 0; background: var(--surface); border: 1px solid var(--grey-border); border-radius: var(--radius-sm); overflow: hidden; }
.reco-sous-table th {
  position: static;
  background: transparent;
  border-bottom: 1px solid var(--grey-border);
  font-size: 0.66rem;
}
.reco-sous-table th, .reco-sous-table td { padding: 7px 10px; }
.reco-sous-table tbody tr:nth-child(even) { background: transparent; }
.reco-sous-table tbody tr:hover { background: var(--row-hover); }
.reco-sous-table tr:last-child td { border-bottom: none; }

.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }

/* ---------------------------------------------------------------- boutons */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 9px 17px;
  border-radius: var(--radius-sm);
  border: none;
  cursor: pointer;
  font-size: 0.87rem;
  font-weight: 600;
  text-decoration: none;
  color: #fff;
  background: var(--teal);
  box-shadow: var(--shadow-xs);
  transition: transform 0.12s var(--ease), box-shadow 0.12s var(--ease), background-color 0.12s var(--ease), opacity 0.12s var(--ease);
}
.btn:hover { transform: translateY(-1px); box-shadow: var(--shadow-sm); opacity: 0.96; }
.btn:active { transform: translateY(0); box-shadow: var(--shadow-xs); }
.btn-navy { background: var(--navy); }
.btn-navy:hover { background: var(--navy-light); }
.btn-teal { background: var(--teal); }
.btn-teal:hover { background: var(--teal-light); box-shadow: var(--shadow-sm), 0 0 14px -4px var(--accent-glow); }
.btn-amber { background: var(--amber); }
.btn-danger { background: var(--danger); }
.btn-outline { background: var(--surface); color: var(--navy); border: 1.5px solid var(--grey-border-strong); box-shadow: none; }
.btn-outline:hover { border-color: var(--navy); background: var(--grey-bg-alt); color: var(--heading-navy); }

/* ------------------------------------------------------------ theme-toggle */
/* Recherche (palette de commandes) + bascule theme sombre/clair (16/07/2026) -
   boutons ronds neutres sur le bandeau superieur clair/sombre selon le
   theme actif. Seule l'icone lune OU soleil est visible a la fois, pilotee
   par [data-theme]. */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--grey-bg-alt);
  border: none;
  color: var(--text-secondary);
  width: 28px;
  height: 28px;
  border-radius: var(--radius-pill);
  cursor: pointer;
  margin-left: 4px;
  flex-shrink: 0;
  transition: background-color 0.15s var(--ease), color 0.15s var(--ease);
}
.theme-toggle:hover { background: var(--grey-border); color: var(--text-primary); }
.theme-toggle-icon-light { display: none; }
:root[data-theme="dark"] .theme-toggle-icon-dark { display: none; }
:root[data-theme="dark"] .theme-toggle-icon-light { display: inline-flex; }

/* --------------------------------------------------------- command-palette */
/* Ctrl/Cmd+K (16/07/2026, demande "design high-tech") - navigation rapide,
   liste construite en JS a partir de .ribbon-drawer (cf. base.html). */
.command-palette {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 12vh;
}
/* Une regle d'auteur (ci-dessus) l'emporte TOUJOURS sur la regle de la
   feuille de style user-agent [hidden]{display:none}, quelle que soit la
   specificite - sans cette ligne, l'attribut `hidden` pose/retire par le JS
   (cf. base.html) n'a plus aucun effet et la palette reste visible en
   permanence des le chargement de la page. */
.command-palette[hidden] { display: none; }
.command-palette-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(7, 20, 30, 0.5);
  backdrop-filter: blur(2px);
}
.command-palette-box {
  position: relative;
  width: min(560px, 92vw);
  max-height: 60vh;
  background: var(--surface);
  border: 1px solid var(--grey-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.command-palette-input-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--grey-border);
  color: var(--text-muted);
}
.command-palette-input-row input {
  border: none;
  padding: 0;
  font-size: 1rem;
  background: transparent;
  color: var(--text-primary);
  box-shadow: none;
}
.command-palette-input-row input:focus { box-shadow: none; }
.command-palette-input-row kbd {
  font-size: 0.68rem;
  color: var(--text-muted);
  border: 1px solid var(--grey-border);
  border-radius: var(--radius-sm);
  padding: 2px 6px;
  font-family: inherit;
}
#command-palette-results {
  list-style: none;
  margin: 0;
  padding: 6px;
  overflow-y: auto;
}
.command-palette-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 0.88rem;
  color: var(--text-primary);
}
.command-palette-item.active, .command-palette-item:hover { background: var(--grey-bg-alt); }
.command-palette-item-icon { display: flex; color: var(--teal); flex-shrink: 0; }
.command-palette-empty { padding: 14px 10px; color: var(--text-muted); font-size: 0.85rem; text-align: center; }

/* ------------------------------------------------------------- empty-state */
/* Etat "aucune donnee" (16/07/2026, demande "design high-tech") - remplace
   un simple texte "Aucun ..." perdu dans une cellule de tableau par une
   icone + un message centre, pour ne pas donner l'impression d'une page
   cassee a un nouvel utilisateur face a un tableau encore vide. */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 34px 16px;
  color: var(--text-muted);
  text-align: center;
}
.empty-state svg { opacity: 0.45; }
.empty-state p { margin: 0; font-size: 0.88rem; }

/* ----------------------------------------------------------------- forms */
.form-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px 22px; }
@media (max-width: 600px) {
  .user-role-detail { display: none; }
  /* Le nom du compte n'est pas une information critique en usage mobile
     (17/07/2026, releve par Issaka Keita : le bloc identite ne tenait pas
     dans la largeur disponible et debordait hors ecran a gauche, invisible)
     - masque avec le role juste au-dessus pour liberer la place necessaire
     au reste du bandeau (langue, mot de passe, deconnexion, recherche,
     theme) sur une seule ligne. */
  .user-identity { display: none; }
  .topbar-divider:first-of-type { display: none; }
}
@media (max-width: 700px) {
  .form-grid { grid-template-columns: 1fr; }
  .container { margin: 14px 12px; }
}

/* Sections repliables (Fin de contrat, Exonerations...) : <details> stylee
   comme .card, ouverte par defaut uniquement si des donnees existantes s'y
   rattachent deja (cf. l'attribut "open" pose cote template). */
details.card { padding: 0; }
details.card > summary {
  list-style: none;
  cursor: pointer;
  padding: 20px 22px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
details.card > summary::-webkit-details-marker { display: none; }
details.card > summary h2 { margin-bottom: 0; }
details.card > summary .details-chevron { color: var(--text-muted); transition: transform 0.15s var(--ease); flex-shrink: 0; }
details.card[open] > summary .details-chevron { transform: rotate(180deg); }
details.card > .details-body { padding: 0 22px 20px; }

/* Info-bulle declenchee au clic (details/summary natif : accessible
   clavier/tactile, sans JS dedie) - deplace les longs paragraphes d'aide
   hors du flux principal du formulaire, disponibles a la demande plutot
   qu'imposes en permanence. */
.info-disclosure { position: relative; display: inline-flex; margin-left: 2px; }
.info-disclosure summary { list-style: none; cursor: pointer; color: var(--text-muted); display: flex; }
.info-disclosure summary::-webkit-details-marker { display: none; }
.info-disclosure summary:hover, .info-disclosure[open] summary { color: var(--heading-teal); }
.info-disclosure .info-popover {
  position: absolute;
  top: 100%;
  left: 0;
  margin-top: 8px;
  width: min(360px, 80vw);
  background: var(--surface);
  border: 1px solid var(--grey-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  padding: 12px 14px;
  z-index: 45;
  font-size: 0.78rem;
  font-weight: 400;
  line-height: 1.5;
  color: var(--text-secondary);
}

label { display: block; font-size: 0.8rem; font-weight: 650; color: var(--text-secondary); margin-bottom: 4px; letter-spacing: 0.01em; }
input, select, textarea {
  width: 100%;
  padding: 9px 11px;
  border: 1.5px solid var(--grey-border);
  border-radius: var(--radius-sm);
  font-size: 0.9rem;
  font-family: inherit;
  color: var(--text-primary);
  background: var(--surface);
  transition: border-color 0.14s var(--ease), box-shadow 0.14s var(--ease);
}
input:hover, select:hover, textarea:hover { border-color: var(--grey-border-strong); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--teal);
  box-shadow: var(--shadow-focus);
}
/* Validation native (19/07/2026, demande "form validation") - retour visuel
   coherent avec le systeme de design (bordure/anneau --danger) au lieu du
   contour rouge brut par defaut du navigateur. :user-invalid (pas
   :invalid seul) ne s'applique qu'APRES une interaction - perte de focus ou
   tentative de soumission - jamais des le chargement de page sur un champ
   requis encore vide. La bulle native ("Veuillez remplir ce champ") reste
   inchangee, hors de portee du CSS ; cf. scroll-margin-top plus haut pour sa
   position sous la nav collante. */
input:user-invalid, select:user-invalid, textarea:user-invalid { border-color: var(--danger); }
input:user-invalid:focus, select:user-invalid:focus, textarea:user-invalid:focus {
  border-color: var(--danger);
  box-shadow: var(--shadow-focus-danger);
}
input[type=checkbox] { width: auto; accent-color: var(--teal); }

/* Champ de recherche avec icone (18/07/2026, demande "more pro and
   readable") - meme motif que .password-field ci-dessous (icone superposee
   en position absolue). L'input global etant width:100% (cf. plus haut),
   un max-width est necessaire ici pour eviter qu'un champ de recherche seul
   n'occupe toute la largeur d'une barre d'outils flex et ne rejette les
   boutons voisins a la ligne suivante. */
.search-field { position: relative; max-width: 280px; flex: 1 1 220px; }

/* Barre de filtres en ligne (31/08/2026, demande explicite) - les <select>
   de filtre sont width:100% par defaut (regle globale input/select/textarea
   ci-dessus) : sans cette classe, chacun occupe toute la largeur du
   conteneur flex et force les suivants a la ligne d'en dessous (empilement
   vertical au lieu d'une seule ligne de filtres). Largeur bornee plutot que
   fixe pour rester lisible sur les libelles les plus longs ("Tous les
   types de contrat") tout en tenant sur une seule ligne aux largeurs
   d'ecran usuelles. */
.filter-select { flex: 1 1 150px; width: auto; max-width: 190px; }
.search-field input { padding-left: 34px; }
.search-field .icon { position: absolute; top: 50%; left: 10px; transform: translateY(-50%); color: var(--text-muted); pointer-events: none; }

/* -------------------------------------------- afficher/masquer mot de passe */
.password-field { position: relative; }
.password-field input { padding-right: 38px; }
.password-toggle {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  background: none;
  border: none;
  cursor: pointer;
  padding: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  border-radius: var(--radius-sm);
  transition: color 0.14s var(--ease), background-color 0.14s var(--ease);
}
.password-toggle:hover { color: var(--navy); background: var(--grey-bg-alt); }
.password-toggle svg { display: block; }

/* ----------------------------------------------------------------- flash */
.flash {
  padding: 11px 16px;
  border-radius: var(--radius-sm);
  margin-bottom: 14px;
  font-size: 0.89rem;
  border-left: 4px solid transparent;
  box-shadow: var(--shadow-xs);
}
.flash-success { background: var(--success-bg); color: var(--success); border-left-color: var(--success); }
.flash-danger { background: var(--danger-bg); color: var(--danger); border-left-color: var(--danger); }
.flash-warning { background: var(--flash-warning-bg); color: var(--flash-warning-text); border-left-color: var(--amber); }

/* Notifications toast (16/07/2026) - le message flash() est genere par
   Flask exactement comme avant ; ce JS (cf. base.html) le detache juste du
   flux normal de page vers ce conteneur flottant. */
.toast-container {
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: 300;
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: min(360px, calc(100vw - 40px));
}
.toast {
  margin-bottom: 0;
  box-shadow: var(--shadow-lg);
  position: relative;
  padding-right: 34px;
  animation: toast-in 0.22s var(--ease);
}
.toast.toast-leaving { animation: toast-out 0.2s var(--ease) forwards; }
.toast-close {
  position: absolute;
  top: 6px;
  right: 6px;
  background: none;
  border: none;
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  color: inherit;
  opacity: 0.6;
  padding: 4px;
}
.toast-close:hover { opacity: 1; }
@keyframes toast-in { from { opacity: 0; transform: translateX(16px); } to { opacity: 1; transform: translateX(0); } }
@keyframes toast-out { to { opacity: 0; transform: translateX(16px); } }
@media (max-width: 480px) { .toast-container { left: 12px; right: 12px; width: auto; } }

/* ------------------------------------------------------------------- kpi */
/* KPI (06/09/2026, refonte "cartes a liseret superieur" demandee par
   Issaka Keita sur exemple visuel) : cartes SEPAREES (fini le quadrillage
   a hairlines partagees de la refonte "Control Tower" du 16/07/2026),
   chacune avec coins arrondis, ombre legere et un filet de couleur en
   HAUT de carte plutot qu'a gauche. La couleur reste un signal de SENS,
   jamais une rotation arbitraire (meme principe que .kpi-icon-* juste en
   dessous, "la classe de couleur est posee cote template selon le sens de
   chaque metrique") : .kpi-accent-* est pose explicitement sur les cartes
   dont le sens s'y prete (teal = metrique neutre/volume, amber = en
   attente/necessite une action, green = positif/valide/conforme, red =
   urgent/en retard/bloquant, purple = indicateur social/RH) ; une carte
   sans classe garde un liseret neutre discret. */
.kpi-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 14px; }
.kpi {
  display: block;
  background: var(--surface);
  border: 1px solid var(--grey-border);
  border-top: 3px solid var(--grey-border-strong);
  border-radius: var(--radius-lg);
  padding: 16px 18px 15px;
  position: relative;
  box-shadow: var(--shadow-xs);
  transition: box-shadow 0.18s var(--ease), transform 0.12s var(--ease), border-color 0.18s var(--ease);
  color: inherit;
  text-decoration: none;
  cursor: pointer;
}
.kpi:hover { box-shadow: var(--shadow-sm); transform: translateY(-1px); }
.kpi:focus-visible { outline: 2px solid var(--teal); outline-offset: -2px; }
.kpi.featured, .kpi.kpi-accent-teal { border-top-color: var(--teal); }
.kpi.kpi-accent-amber { border-top-color: var(--amber); }
.kpi.kpi-accent-green { border-top-color: var(--success); }
.kpi.kpi-accent-red { border-top-color: var(--danger); }
.kpi.kpi-accent-purple { border-top-color: var(--purple); }
/* KPI "necessite une action" (10/08/2026, ex. Timesheet non soumis) -
   valeur teintee en plus du liseret ambre : signale un compteur qui
   appelle une action, sans etre aussi grave qu'une erreur (--danger
   reserve aux comptes reellement bloquants/en retard). */
.kpi.kpi-alert { border-top-color: var(--amber); }
.kpi.kpi-alert .value, .kpi.kpi-alert .kpi-value { color: var(--amber); }
.kpi .value, .kpi .kpi-value {
  display: block;
  font-family: var(--font-mono);
  font-size: 1.55rem;
  font-weight: 600;
  color: var(--heading-navy);
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
}
.kpi .label { font-size: 0.76rem; color: var(--text-secondary); margin-top: 4px; }
.kpi .kpi-label { display: block; font-size: 0.76rem; color: var(--text-secondary); margin-bottom: 4px; }
.kpi .kpi-sub { font-size: 0.7rem; color: var(--text-muted); margin-top: 5px; }

/* Badges d'icone colores (31/08/2026, demande explicite : "des encadres
   bien colores" sur le tableau de bord) - remplace l'icone plate/monochrome
   d'origine par un badge en carre arrondi, teinte selon la categorie de la
   metrique. Palette reprise EXACTEMENT des tokens de marque deja existants
   (--teal/--info/--purple/--amber/--success/--danger) - jamais une teinte
   inventee hors de ce systeme. Fond a faible opacite (lisible dans les deux
   themes sans recalage), icone en couleur pleine - la classe de couleur est
   posee cote template (dashboard.html) selon le sens de chaque metrique
   (RH/effectifs en bleu-teal, alertes/attention en ambre, indicateurs
   sociaux en violet, elements positifs pour l'employe en vert...), jamais
   une rotation arbitraire. Repli sur l'ancienne teinte unique (--heading-
   teal, fond neutre) si aucune classe de couleur n'est posee. */
.kpi .kpi-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; margin-bottom: 10px;
  border-radius: var(--radius-lg);
  background: var(--grey-bg-alt); color: var(--heading-teal);
}
.kpi .kpi-icon svg { width: 19px; height: 19px; }
.kpi .kpi-icon-teal   { background: rgba(22, 119, 210, 0.12); color: var(--teal); }
.kpi .kpi-icon-info   { background: rgba(46, 144, 250, 0.12); color: var(--info); }
.kpi .kpi-icon-purple { background: rgba(91, 79, 196, 0.12); color: var(--purple); }
.kpi .kpi-icon-amber  { background: rgba(247, 144, 9, 0.14); color: var(--amber); }
.kpi .kpi-icon-green  { background: rgba(18, 183, 106, 0.12); color: var(--success); }
.kpi .kpi-icon-red    { background: rgba(240, 68, 56, 0.12); color: var(--danger); }
body.compact-page .kpi .kpi-icon,
.dashboard-compact .kpi .kpi-icon { width: 30px; height: 30px; border-radius: var(--radius-md); margin-bottom: 6px; }
body.compact-page .kpi .kpi-icon svg,
.dashboard-compact .kpi .kpi-icon svg { width: 16px; height: 16px; }
/* Le liseret superieur reprend AUTOMATIQUEMENT la couleur de badge deja
   choisie "selon le sens de chaque metrique" (cf. commentaire ci-dessus) -
   aucune nouvelle decision de couleur, juste le meme choix applique en
   plus au sommet de la carte. */
.kpi:has(.kpi-icon-teal)   { border-top-color: var(--teal); }
.kpi:has(.kpi-icon-info)   { border-top-color: var(--info); }
.kpi:has(.kpi-icon-purple) { border-top-color: var(--purple); }
.kpi:has(.kpi-icon-amber)  { border-top-color: var(--amber); }
.kpi:has(.kpi-icon-green)  { border-top-color: var(--success); }
.kpi:has(.kpi-icon-red)    { border-top-color: var(--danger); }

/* Tendance +/- vs mois precedent, et mini-graphique d'historique
   (15/07/2026, demande "dashboard high-tech avec tendances") - cf.
   icons.py::render_sparkline et app.py::dashboard(). */
.kpi-trend {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 0.7rem;
  font-weight: 700;
  margin-top: 6px;
  color: var(--text-muted);
}
.kpi-trend.trend-up { color: var(--success); }
.kpi-trend.trend-down { color: var(--danger); }
.kpi-spark { margin-top: 8px; color: var(--teal); opacity: 0.85; }
.kpi-spark .sparkline { display: block; }

/* Repartition de la masse salariale - barre empilee + legende (18/07/2026,
   demande "engineering niveau industrie"). Segments generes cote serveur en
   %, pas de librairie de graphiques (coherent avec la CSP, cf. icons.py). */
.cost-bar { display: flex; height: 22px; border-radius: var(--radius-sm); overflow: hidden; background: var(--grey-bg-alt); }
.cost-seg { height: 100%; transition: width 0.3s var(--ease); }
.cost-seg-net { background: var(--teal); }
.cost-seg-retenues { background: var(--amber); }
.cost-seg-charges { background: var(--purple); }
.cost-seg-autres { background: var(--grey-border-strong); }
.cost-legend { display: flex; flex-direction: column; gap: 7px; margin-top: 14px; }
.cost-legend-item { display: flex; align-items: baseline; gap: 7px; font-size: 0.8rem; color: var(--text-secondary); }
.cost-legend-item b { font-family: var(--font-mono); color: var(--heading-navy); font-weight: 600; font-variant-numeric: tabular-nums; }
.cost-pct { color: var(--text-muted); font-size: 0.75rem; }
.cost-dot { width: 9px; height: 9px; border-radius: var(--radius-pill); flex-shrink: 0; }

/* Effectif par departement - barres horizontales (18/07/2026, meme
   demande). Largeur relative au departement le plus peuple. */
.dept-bars { display: flex; flex-direction: column; gap: 9px; }
.dept-bar-row { display: grid; grid-template-columns: minmax(90px, 30%) 1fr auto; align-items: center; gap: 10px; }
.dept-bar-label { font-size: 0.78rem; color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dept-bar-track { background: var(--grey-bg-alt); border-radius: var(--radius-sm); height: 10px; overflow: hidden; }
.dept-bar-fill { background: var(--teal); height: 100%; border-radius: var(--radius-sm); transition: width 0.3s var(--ease); }
.dept-bar-value { font-family: var(--font-mono); font-size: 0.78rem; font-weight: 600; color: var(--heading-navy); font-variant-numeric: tabular-nums; min-width: 1.5em; text-align: right; }

/* Evolution de la masse salariale sur 12 mois - barres verticales
   (18/07/2026, "meilleure suggestion possible"). Meme approche generation
   serveur que le reste (pas de librairie de graphiques). */
/* overflow-x: auto + colonnes a largeur FIXE (pas flex:1) : avec 12
   colonnes, une carte etroite (mobile, ou tablette en disposition 2
   colonnes) n'a pas la place d'etaler 12 libelles "MM/AA" sans les faire
   se chevaucher - constate a 768px (tablette) ou les libelles se
   collaient les uns aux autres. Meme logique que .table-wrap : au lieu de
   comprimer jusqu'a l'illisible, on defile horizontalement. */
.trend-chart { display: flex; align-items: flex-end; gap: 6px; height: 140px; overflow-x: auto; overflow-y: hidden; padding-bottom: 2px; }
.trend-bar-col { flex: 1 0 30px; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; height: 100%; }
.trend-bar { width: 100%; background: var(--teal); border-radius: 3px 3px 0 0; min-height: 2px; transition: height 0.3s var(--ease); }
.trend-bar-col:hover .trend-bar { background: var(--teal-light); }
.trend-bar-label { font-size: 0.6rem; color: var(--text-muted); margin-top: 5px; white-space: nowrap; }

/* -------------------------------------------------- en-tete de page (dashboard) */
.dash-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 22px;
}
.dash-header h1 { margin-bottom: 4px; }
.dash-subtitle { color: var(--text-muted-on-bg); font-size: 0.92rem; margin: 0; }

/* Petit intitule de section en majuscules, avec un trait d'accent teal -
   sert a hierarchiser visuellement les groupes de KPI du dashboard
   (Apercu / Effectifs / Conges / Paie / Cumuls annuels) sans avoir a
   utiliser un <h2> a chaque fois (trop lourd visuellement pour un simple
   separateur de section). */
.section-eyebrow {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-muted-on-bg);
  margin: 28px 0 10px;
  display: flex;
  align-items: center;
}
.section-eyebrow:first-of-type { margin-top: 0; }
.section-eyebrow::before {
  content: "";
  display: inline-block;
  width: 16px;
  height: 2px;
  border-radius: 2px;
  background: var(--gradient-brand);
  margin-right: 9px;
}

/* ---------------------------------------------------------------- footer */
.footer-note { font-size: 0.78rem; color: var(--text-muted-on-bg); text-align: center; margin: 28px 0; }

.anomalie { color: var(--danger); font-weight: 700; }

/* Tableau a entete et 2 premieres colonnes figees, avec defilement
   horizontal ET vertical (07/07/2026) - utilise sur le tableau des
   bulletins de la page Periode (beaucoup de colonnes + beaucoup de lignes).
   Volontairement scope a .table-freeze-scroll (et non ajoute a .table-wrap
   globalement) pour ne pas modifier la mise en forme des autres tableaux de
   l'application (Listes, Employes...) qui n'ont pas besoin de colonnes
   figees. Le conteneur a une hauteur maximale bornee (max-height) pour que
   le defilement vertical se produise DANS le tableau plutot que sur toute
   la page. */
.table-freeze-scroll {
  max-height: 65vh;
  overflow: auto;
  border: 1px solid var(--grey-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-xs);
}
.table-freeze-scroll table { margin: 0; }
.table-freeze-scroll thead th {
  position: sticky;
  top: 0;
  z-index: 3;
}
.table-freeze-scroll th:nth-child(1),
.table-freeze-scroll td:nth-child(1) {
  position: sticky;
  left: 0;
  z-index: 2;
  width: 90px;
  max-width: 90px;
  background: var(--surface);
}
.table-freeze-scroll th:nth-child(2),
.table-freeze-scroll td:nth-child(2) {
  position: sticky;
  left: 90px;
  z-index: 2;
  width: 170px;
  max-width: 170px;
  white-space: normal;
  background: var(--surface);
  box-shadow: var(--shadow-freeze-edge);
}
.table-freeze-scroll thead th:nth-child(1),
.table-freeze-scroll thead th:nth-child(2) {
  z-index: 4;
  background: var(--grey-bg-alt);
}
.table-freeze-scroll tbody tr:hover td:nth-child(1),
.table-freeze-scroll tbody tr:hover td:nth-child(2) {
  background: var(--row-hover);
}
.table-freeze-scroll tbody tr.row-solde-tout-compte td {
  background: var(--flash-warning-bg);
}
.table-freeze-scroll tbody tr.row-solde-tout-compte:hover td {
  background: var(--grey-border);
}

/* ------------------------------------------------ dashboard compact (14/07/2026)
   Demande par Issaka Keita : tenir le tableau de bord dans un seul coup
   d'oeil, sans defilement. Volontairement scope a .dashboard-compact
   (classe posee uniquement sur templates/dashboard.html) pour ne pas
   reduire les cartes/KPI des AUTRES pages (Periode, etc.) qui partagent
   les memes classes generiques .card/.kpi/.section-eyebrow. */
.dashboard-compact .dash-header { margin-bottom: 10px; }
.dashboard-compact h1 { font-size: 1.28rem; margin-bottom: 2px; }
.dashboard-compact .dash-subtitle { font-size: 0.76rem; }
.dashboard-compact .section-eyebrow {
  margin: 10px 0 6px;
  font-size: 0.66rem;
}
.dashboard-compact .section-eyebrow:first-of-type { margin-top: 0; }
.dashboard-compact .kpi-row { grid-template-columns: repeat(auto-fit, minmax(128px, 1fr)); }
/* Legere reprise de respiration (19/07/2026) - le v4 clair a des bordures
   fines et un contraste plus doux que l'ancien theme sombre a cellules
   distinctes ; les tailles/paddings les plus serres (herites du v3) avaient
   davantage besoin du fort contraste de fond pour rester lisibles. */
.dashboard-compact .kpi { padding: 8px 11px; }
.dashboard-compact .kpi .value, .dashboard-compact .kpi .kpi-value { font-size: 1.04rem; font-weight: 700; }
.dashboard-compact .kpi .label { font-size: 0.64rem; margin-top: 1px; line-height: 1.2; }
.dashboard-compact .kpi .kpi-label { font-size: 0.64rem; margin-bottom: 1px; line-height: 1.2; }
.dashboard-compact .kpi .kpi-sub { font-size: 0.6rem; margin-top: 1px; }
/* .kpi-icon (taille/marge) : cf. la regle commune avec body.compact-page,
   posee juste apres les badges colores .kpi-icon-* plus haut dans ce
   fichier - un seul endroit pour cette taille compacte. */
.dashboard-compact .kpi-trend { font-size: 0.62rem; margin-top: 2px; }
.dashboard-compact .kpi-spark { margin-top: 3px; }
.dashboard-compact .kpi-spark .sparkline { width: 72px; height: 20px; }
.dashboard-compact .card { padding: 10px 14px; margin-bottom: 0; }
.dashboard-compact .card h2 { font-size: 0.85rem; margin-bottom: 8px; }
.dashboard-compact .card .btn { padding: 5px 10px; font-size: 0.76rem; margin-bottom: 4px; }
.dashboard-compact table { font-size: 0.74rem; }
.dashboard-compact th, .dashboard-compact td { padding: 4px 8px; }
.dashboard-compact .dash-bottom-row {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  align-items: flex-start;
  margin-top: 14px;
}
.dashboard-compact .dash-bottom-row .card { flex: 1 1 280px; min-width: 260px; }
@media (max-width: 700px) {
  /* align-items: stretch (18/07/2026) - en colonne, "flex-start" herite de
     la mise en page en ligne devient l'axe HORIZONTAL : sans cette
     surcharge, une carte dont le contenu ne force pas naturellement 100%
     (tableau, ligne flex a plusieurs colonnes) retombe a sa largeur de
     contenu minimale au lieu de occuper toute la largeur disponible -
     invisible tant qu'aucune carte de .dash-bottom-row ne contenait ce
     genre de contenu, releve en testant les nouvelles cartes tendance/
     evenements sur mobile. */
  .dashboard-compact .dash-bottom-row { flex-direction: column; align-items: stretch; }
  /* min-width: 0 (18/07/2026) - meme cause racine que ci-dessus : le
     min-width: 260px pense pour l'ecretage en LIGNE (desktop) devient sans
     effet des qu'un enfant (tableau, ligne flex a plusieurs colonnes) a une
     taille de contenu minimale superieure a 260px - la taille automatique
     basee sur le contenu l'emporte alors sur le stretch et fait deborder la
     carte hors du viewport. Sans impact desktop (regle scopee a ce media). */
  .dashboard-compact .dash-bottom-row .card { min-width: 0; width: 100%; }
}

/* Rangee "acces rapide" du tableau de bord (31/08/2026, demande explicite :
   "reduire ... et mettre sur la meme ligne") - la recherche d'une fiche
   employe (a gauche) et les Actions rapides (a droite, remontees depuis le
   bas de la page) tiennent desormais sur une seule ligne compacte, juste
   sous l'entete, plutot que d'occuper chacune une largeur/position
   distincte (recherche en pleine largeur tout en haut, Actions rapides tout
   en bas). justify-content: space-between ecarte les deux cartes vers les
   bords plutot que de les etirer pour remplir la ligne. */
.dashboard-compact .dash-top-row {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 14px;
}
.dashboard-compact .dash-top-row .card { flex: 0 1 340px; margin-bottom: 0; }
/* Actions rapides (derniere carte de la ligne) : largeur portee a 480px
   (31/08/2026, demande explicite : "mettez ces 3 boutons sur la meme
   ligne") - les 3 boutons ("+ Nouvelle periode de paie" etant le plus
   long) ne tenaient pas sur une seule ligne a 300px et repassaient chacun
   a la ligne. */
.dashboard-compact .dash-top-row .card:last-child { flex-basis: 480px; }
@media (max-width: 700px) {
  .dashboard-compact .dash-top-row .card { flex-basis: 100%; }
}

/* "A traiter" en ligne dans l'entete (31/08/2026, demande explicite :
   integrer ce qui etait une carte encadree pleine largeur directement a la
   suite du sous-titre "Vue d'ensemble - periode en cours", dans une police
   plus petite que le titre) - remplace l'ancienne carte .card a bordure
   ambre par une simple liste de liens sur la meme ligne que le sous-titre
   (repasse a la ligne si necessaire, flex-wrap). */
.dash-a-traiter {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-left: 10px;
  padding-left: 10px;
  border-left: 1px solid var(--grey-border);
  vertical-align: middle;
}
.dash-a-traiter-label {
  color: var(--amber);
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
}
.dash-a-traiter a {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--text-secondary);
  text-decoration: none;
  white-space: nowrap;
}
.dash-a-traiter a:hover { color: var(--teal); text-decoration: underline; }

/* ------------------------------------------------------------- timesheet */
.badge-soumis { background: var(--flash-warning-bg); color: var(--flash-warning-text); }
.badge-approuve { background: var(--success-bg); color: var(--success); }
.badge-rejete { background: var(--danger-bg); color: var(--danger); }
.badge-non_soumis { background: var(--grey-bg-alt); color: var(--text-on-tinted); }
.badge-en_attente { background: var(--flash-warning-bg); color: var(--flash-warning-text); }
.badge-suspendu { background: var(--flash-warning-bg); color: var(--flash-warning-text); }
/* Module Missions (18/07/2026) : etapes intermediaires du workflow a 3
   niveaux - meme code couleur "en cours" que badge-soumis. */
.badge-approuve_superviseur, .badge-approuve_securite { background: var(--flash-warning-bg); color: var(--flash-warning-text); }
/* Module Conges (01/09/2026) : demande d'annulation en attente de decision
   du superviseur - meme code couleur "en cours" que badge-soumis. */
.badge-annulation_demandee { background: var(--flash-warning-bg); color: var(--flash-warning-text); }

.ts-nav-mois {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}
.ts-nav-mois .ts-mois-label {
  font-weight: 700;
  color: var(--heading-navy);
  font-size: 1.02rem;
  min-width: 130px;
  text-align: center;
}

.ts-grid-wrap { overflow-x: auto; border-radius: var(--radius-md); }
table.ts-grid { font-size: 0.76rem; }
table.ts-grid th, table.ts-grid td { padding: 5px 6px; text-align: center; white-space: nowrap; }
table.ts-grid th.ts-col-projet, table.ts-grid td.ts-col-projet { text-align: left; min-width: 190px; white-space: normal; }
table.ts-grid td.ts-weekend { background: var(--grey-bg-alt); color: var(--text-muted); }
/* th.ts-weekend distinct de td.ts-weekend (19/07/2026) - le fond des th de
   week-end etait auparavant le meme --grey-bg-alt que le corps du tableau,
   ce qui les distinguait clairement du header par defaut (alors sombre).
   Avec le header v4 deja en --grey-bg-alt, cette regle etait devenue
   invisible (meme couleur que ses voisins) : la colonne week-end ne se
   distinguait plus dans la ligne d'entete. --grey-border-strong offre un
   palier supplementaire, visible sur le header comme sur le corps. */
table.ts-grid th.ts-weekend { background: var(--grey-border-strong); color: var(--text-secondary); }
/* Jours feries (30/08/2026) - couleur distincte du gris week-end (--flash-warning-bg/
   --amber, deja utilises pour les etats "en attente/alerte" ailleurs dans l'appli) ;
   contrairement au week-end, la cellule reste saisissable (l'input n'est pas cache). */
table.ts-grid td.ts-ferie { background: var(--flash-warning-bg); }
table.ts-grid th.ts-ferie { background: var(--amber); color: #100b03; }
table.ts-grid input.ts-heures {
  width: 44px;
  padding: 3px 2px;
  text-align: center;
  font-size: 0.76rem;
}
table.ts-grid td.ts-weekend input { display: none; }
table.ts-grid td.ts-total-ligne, table.ts-grid th.ts-total-ligne { font-weight: 700; background: var(--surface-alt); }
table.ts-grid tfoot td { font-weight: 700; background: var(--grey-bg-alt); color: var(--heading-navy); border-top: 1px solid var(--grey-border-strong); }
.ts-actions-row { display: flex; gap: 10px; margin-top: 16px; flex-wrap: wrap; }
.ts-commentaire-rejet {
  background: var(--danger-bg);
  color: var(--danger);
  border-left: 4px solid var(--danger);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  margin-bottom: 16px;
  font-size: 0.87rem;
}

/* Modale de confirmation (19/07/2026, remplace les confirm() natifs du
   navigateur - rapport de test utilisateur : window.confirm() bloquait le
   rendu des outils d'automatisation, non stylable, peu accessible). Une
   seule instance partagee (base.html), peuplee dynamiquement par
   modal-confirmation.js selon l'attribut data-confirm-message du bouton
   ou du formulaire declencheur. */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 32, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  z-index: 1000;
}
.modal-box {
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 24px 26px;
  max-width: 460px;
  width: 100%;
}
.modal-box p {
  margin: 0 0 6px 0;
  color: var(--text-primary);
  font-size: 0.95rem;
  line-height: 1.5;
  white-space: pre-line;
}
.modal-confirmation-saisie { margin-top: 14px; }
.modal-confirmation-saisie label {
  display: block;
  font-size: 0.82rem;
  color: var(--text-secondary);
  margin-bottom: 6px;
}
.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 20px;
}

/* ---------------------------------------------------------- organigramme */
/* Organigramme (18/08/2026, demande explicite d'Issaka Keita) : arbre
   VERTICAL indente (pas le classique "boites+lignes horizontales"), choisi
   parce que les donnees reelles de l'organisation ont souvent tres peu de
   liens hierarchiques renseignes (35 employes sur 36 sans superviseur dans
   le rapport de test du 15/08/2026) - un organigramme horizontal produirait
   des dizaines de mini-arbres cote a cote, illisible et enorme en largeur.
   La variante verticale reste compacte et lisible quel que soit le nombre
   de racines, avec un simple defilement vertical de page. */
.orgtree, .orgtree ul {
  list-style: none;
  margin: 0;
  padding-left: 0;
}
.orgtree ul {
  padding-left: 22px;
  margin-left: 15px;
  border-left: 1px dashed var(--grey-border-strong);
}
.orgtree li {
  padding: 6px 0;
}
.orgtree-card {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 7px 12px;
  border: 1px solid var(--grey-border);
  border-radius: var(--radius-md);
  background: var(--surface);
}
.orgtree-avatar {
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--heading-navy);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.85rem;
}
.orgtree-nom {
  font-weight: 700;
  color: var(--text-primary);
  font-size: 0.88rem;
  line-height: 1.3;
}
.orgtree-meta {
  font-size: 0.76rem;
  color: var(--text-secondary);
  line-height: 1.3;
}
.orgtree-count {
  margin-left: 4px;
}

/* ------------------------------------------------- organigramme (module) */
/* Module Organigramme du 22/08/2026 (demande explicite d'Issaka Keita) :
   arbre HORIZONTAL en boites et connecteurs, conformement a la maquette de
   reference fournie. Il remplace l'arbre vertical indente du 18/08/2026
   (classes .orgtree-*, conservees plus haut : elles servent encore a la vue
   Equipe, ou l'arbre est court par construction).
   Le choix vertical d'origine avait ete fait parce que 35 employes sur 36
   n'avaient aucun superviseur renseigne - d'ou des dizaines de racines cote
   a cote. Ce qui rend le format horizontal tenable ici : le pliage par
   defaut au-dela du 2e niveau, le zoom/deplacement, et surtout l'ecran
   d'affectation en masse des superviseurs, qui traite la cause.
   Aucune dependance externe : la CSP de l'application est default-src
   'self', aucun CDN n'est charge nulle part. */
:root {
  --org-line: #c3ccd8;
  --org-card-bg: var(--surface);
  --org-dim: 0.24;
}
:root[data-theme="dark"] { --org-line: #3d4a5c; }

.org-toolbar {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  margin-bottom: 12px;
}
.org-toolbar .org-search { flex: 1 1 260px; min-width: 200px; position: relative; }
.org-toolbar .org-search input { width: 100%; }
.org-suggestions {
  position: absolute; z-index: 40; left: 0; right: 0; top: calc(100% + 4px);
  background: var(--surface); border: 1px solid var(--grey-border);
  border-radius: var(--radius-md); box-shadow: var(--shadow-md);
  max-height: 280px; overflow-y: auto;
}
.org-suggestions button {
  display: block; width: 100%; text-align: left; padding: 7px 10px;
  background: none; border: 0; cursor: pointer; font-size: 0.82rem;
  color: var(--text-primary);
}
.org-suggestions button:hover, .org-suggestions button.is-cible { background: var(--row-hover); }
.org-suggestions .org-sugg-meta { display: block; font-size: 0.72rem; color: var(--text-secondary); }

.org-stats { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.org-stat {
  flex: 1 1 120px; padding: 8px 12px; border: 1px solid var(--grey-border);
  border-radius: var(--radius-md); background: var(--surface);
}
.org-stat b { display: block; font-size: 1.15rem; color: var(--heading-navy); line-height: 1.2; }
.org-stat span { font-size: 0.72rem; color: var(--text-secondary); }
.org-stat.is-alerte { border-color: var(--amber); background: var(--flash-warning-bg); }
.org-stat.is-alerte b { color: var(--amber); }

.org-filtres {
  display: none; flex-wrap: wrap; gap: 10px; align-items: flex-end;
  padding: 12px; border: 1px solid var(--grey-border);
  border-radius: var(--radius-md); background: var(--surface-alt); margin-bottom: 12px;
}
.org-filtres.is-ouvert { display: flex; }
.org-filtres label { font-size: 0.72rem; color: var(--text-secondary); display: block; }
.org-filtres .org-filtre-champ { flex: 1 1 160px; }
.org-filtres select { width: 100%; }
.org-filtres .org-cases { display: flex; flex-direction: column; gap: 4px; flex: 1 1 220px; }
.org-filtres .org-cases label { display: flex; gap: 6px; align-items: center; font-size: 0.78rem; color: var(--text-primary); }

.org-viewport {
  position: relative; overflow: hidden; height: 68vh; min-height: 420px;
  border: 1px solid var(--grey-border); border-radius: var(--radius-md);
  background: var(--grey-bg-alt); cursor: grab;
}
.org-viewport.is-drag { cursor: grabbing; }
.org-viewport:fullscreen { height: 100vh; border-radius: 0; }
.org-canvas { transform-origin: 0 0; padding: 28px; display: inline-block; }
.org-zoom {
  position: absolute; right: 12px; bottom: 12px; z-index: 20;
  display: flex; flex-direction: column; gap: 4px;
}
.org-zoom button {
  width: 32px; height: 32px; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--grey-border); background: var(--surface);
  border-radius: var(--radius-sm); cursor: pointer; color: var(--text-primary);
}
.org-zoom button:hover { background: var(--row-hover); }

.org-tree { display: flex; gap: 30px; align-items: flex-start; }
.org-branch { display: flex; flex-direction: column; align-items: center; position: relative; }
.org-children { display: flex; gap: 18px; position: relative; margin-top: 22px; }
.org-children::before {
  content: ""; position: absolute; top: -22px; left: 50%;
  width: 0; height: 22px; border-left: 2px solid var(--org-line);
}
.org-children > .org-branch { padding-top: 22px; }
.org-children > .org-branch::after {
  content: ""; position: absolute; top: 0; left: 0; right: 0;
  border-top: 2px solid var(--org-line);
}
.org-children > .org-branch::before {
  content: ""; position: absolute; top: 0; left: 50%;
  height: 22px; border-left: 2px solid var(--org-line);
}
.org-children > .org-branch:first-child::after { left: 50%; }
.org-children > .org-branch:last-child::after { right: 50%; }
.org-children > .org-branch:only-child::after { display: none; }
/* Lien FONCTIONNEL (pointilles) : reserve, aucun lien de ce type n'existe
   dans le modele de donnees actuel (un seul superviseur par employe). La
   classe est definie pour que la legende reste exacte le jour ou la notion
   sera introduite - elle n'est posee par aucun code aujourd'hui. */
.org-children > .org-branch.est-fonctionnel::before,
.org-children > .org-branch.est-fonctionnel::after { border-style: dashed; }

.org-card {
  position: relative; width: 188px; padding: 10px 10px 8px;
  border: 1px solid var(--grey-border); border-top: 3px solid var(--navy);
  border-radius: var(--radius-md); background: var(--org-card-bg);
  box-shadow: var(--shadow-xs); text-align: center; cursor: pointer;
  transition: box-shadow var(--ease), border-color var(--ease), opacity var(--ease);
}
.org-card:hover { box-shadow: var(--shadow-md); }
.org-card.is-selection { border-color: var(--teal); box-shadow: var(--shadow-focus); }
.org-card.is-match { border-color: var(--amber); box-shadow: 0 0 0 3px var(--flash-warning-bg); }
.org-card.is-attenue { opacity: var(--org-dim); }
.org-dep-0 { border-top-color: #0b3d5c; }
.org-dep-1 { border-top-color: #1565c0; }
.org-dep-2 { border-top-color: #2e7d54; }
.org-dep-3 { border-top-color: #0f8b8d; }
.org-dep-4 { border-top-color: #6a4bab; }
.org-dep-5 { border-top-color: #b26a1f; }

.org-avatar {
  width: 44px; height: 44px; border-radius: 50%; margin: 0 auto 6px;
  display: flex; align-items: center; justify-content: center;
  font-size: 0.82rem; font-weight: 700; color: #fff; overflow: hidden;
  background: var(--heading-navy);
}
.org-avatar img { width: 100%; height: 100%; object-fit: cover; }
.org-dep-0 .org-avatar { background: #0b3d5c; }
.org-dep-1 .org-avatar { background: #1565c0; }
.org-dep-2 .org-avatar { background: #2e7d54; }
.org-dep-3 .org-avatar { background: #0f8b8d; }
.org-dep-4 .org-avatar { background: #6a4bab; }
.org-dep-5 .org-avatar { background: #b26a1f; }

.org-nom { font-size: 0.8rem; font-weight: 600; color: var(--text-primary); line-height: 1.25; }
.org-fonction { font-size: 0.72rem; color: var(--text-secondary); margin-top: 2px; line-height: 1.25; }
.org-meta { font-size: 0.67rem; color: var(--text-muted); margin-top: 3px; }
.org-pastille {
  position: absolute; top: 8px; right: 8px; width: 9px; height: 9px; border-radius: 50%;
  border: 2px solid var(--surface);
}
.org-pastille.statut-actif { background: var(--success); }
.org-pastille.statut-conge { background: var(--amber); }
.org-pastille.statut-suspendu { background: var(--danger); }
.org-effectif {
  display: inline-flex; align-items: center; gap: 3px; margin-top: 6px;
  font-size: 0.67rem; color: var(--text-secondary);
  background: var(--grey-bg-alt); border-radius: var(--radius-pill); padding: 1px 7px;
}
.org-toggle {
  position: absolute; bottom: -11px; left: 50%; transform: translateX(-50%);
  width: 22px; height: 22px; border-radius: 50%; z-index: 5;
  border: 1px solid var(--grey-border); background: var(--surface);
  color: var(--text-secondary); font-size: 0.72rem; line-height: 1;
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.org-toggle:hover { border-color: var(--teal); color: var(--teal); }

.org-legend {
  display: flex; flex-wrap: wrap; gap: 14px; margin-top: 10px;
  font-size: 0.72rem; color: var(--text-secondary);
}
.org-legend span { display: inline-flex; align-items: center; gap: 5px; }
.org-legend i { width: 12px; height: 12px; border-radius: 3px; display: inline-block; }
.org-legend .trait { width: 18px; height: 0; border-top: 2px solid var(--org-line); border-radius: 0; }
.org-legend .trait-pointille { border-top-style: dashed; }

.org-panel {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(360px, 92vw); z-index: 120;
  background: var(--surface); border-left: 1px solid var(--grey-border);
  box-shadow: var(--shadow-lg); padding: 18px; overflow-y: auto;
  transform: translateX(100%); transition: transform var(--ease);
}
.org-panel.is-ouvert { transform: translateX(0); }
.org-panel h2 { font-size: 1rem; margin: 6px 0 2px; }
.org-panel-fermer { position: absolute; top: 10px; right: 10px; background: none; border: 0; cursor: pointer; color: var(--text-secondary); }
.org-panel-avatar { width: 68px; height: 68px; border-radius: 50%; margin: 4px auto 8px; }
.org-panel dl { display: grid; grid-template-columns: 45% 55%; gap: 5px 8px; font-size: 0.78rem; margin: 12px 0; }
.org-panel dt { color: var(--text-secondary); }
.org-panel dd { margin: 0; color: var(--text-primary); }
.org-panel .org-panel-actions { display: flex; flex-direction: column; gap: 6px; margin-top: 12px; }
.org-panel-equipe { margin-top: 14px; border-top: 1px solid var(--grey-border); padding-top: 10px; }
.org-panel-equipe button {
  display: flex; gap: 8px; align-items: center; width: 100%; text-align: left;
  padding: 6px; border: 0; background: none; cursor: pointer; border-radius: var(--radius-sm);
  color: var(--text-primary); font-size: 0.78rem;
}
.org-panel-equipe button:hover { background: var(--row-hover); }

@media (max-width: 720px) {
  .org-viewport { height: 60vh; }
  .org-card { width: 158px; }
  .org-stat { flex-basis: 45%; }
}

@media print {
  .app-ribbon, .ribbon-drawer, .org-toolbar, .org-filtres, .org-zoom, .org-panel { display: none !important; }
  .org-viewport { height: auto; overflow: visible; border: 0; background: #fff; }
  .org-canvas { transform: none !important; }
  .org-card { box-shadow: none; }
}
/* Pliage : le repli initial (au-dela du 2e niveau) est pose par le serveur
   sur la branche, static/js/organigramme.js ne fait que basculer la classe.
   Sans JavaScript, l'arbre reste donc replie au-dela du 2e niveau mais
   entierement present dans le HTML - et l'impression, elle, l'affiche en
   entier (regle @media print ci-dessous). */
.org-branch.is-plie > .org-children { display: none; }
@media print {
  .org-branch.is-plie > .org-children { display: flex !important; }
}
/* Liste nominative des employes sans superviseur, sur la page Organigramme
   (22/08/2026). Hauteur bornee : sur une organisation ou presque personne
   n'est rattache, la liste peut compter des dizaines de lignes et
   repousserait l'organigramme lui-meme hors de l'ecran. */
.org-liste-defilante { max-height: 232px; overflow-y: auto; }
.org-liste-manquants table { width: 100%; font-size: 0.78rem; }
.org-liste-manquants th {
  position: sticky; top: 0; z-index: 1;
  background: var(--flash-warning-bg); color: var(--amber);
  text-align: left; font-weight: 600; padding: 4px 8px;
}
.org-liste-manquants td { padding: 4px 8px; border-top: 1px solid var(--grey-border); }
@media print { .org-liste-defilante { max-height: none; overflow: visible; } }
