/* ============================================================================
   SIME — Système de Design
   ============================================================================
   Direction artistique : inspirée du sceau/médaille SIME — bronze gravé,
   vert pétrole profond, et l'idée d'une "vision augmentée de l'entreprise"
   (l'œil stylisé du logo). On évite le look "dashboard SaaS générique" :
   les surfaces évoquent le métal gravé et le papier d'archive technique,
   pas le glassmorphism ou les dégradés violet/bleu par défaut.

   Palette :
     --sime-nuit      #0E1A2B  (fond profond, capuchon du logo)
     --sime-petrole   #1B4D4A  (vert pétrole — anneau du sceau)
     --sime-bronze    #8B5A2B  (bronze — rayons, métal gravé)
     --sime-or        #C9A24B  (or — liserés, accents actifs)
     --sime-creme      #EDE6D6 (papier/carton, fond clair)
     --sime-encre      #1A1410 (texte sur fond clair)

   Typographie :
     Display : "Fraunces" (serif à empattements marqués, gravure/médaille)
     Corps   : "Inter" (neutre, très lisible, dense en données)
     Mono    : "JetBrains Mono" (codes, montants, numéros de documents)
   ============================================================================ */

@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;9..144,600;9..144,700&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');

:root {
  /* Couleurs de marque */
  --sime-nuit: #0E1A2B;
  --sime-nuit-clair: #16263D;
  --sime-petrole: #1B4D4A;
  --sime-petrole-clair: #25665F;
  --sime-bronze: #8B5A2B;
  --sime-bronze-clair: #B07A45;
  --sime-or: #C9A24B;
  --sime-or-clair: #E0C275;
  --sime-creme: #EDE6D6;
  --sime-creme-fonce: #DDD3BC;
  --sime-encre: #1A1410;

  /* Sémantique */
  --couleur-succes: #3F7A5C;
  --couleur-attention: #C97A2B;
  --couleur-danger: #B0413E;
  --couleur-info: #3D6E8F;

  /* Surfaces */
  --surface-app: var(--sime-creme);
  --surface-carte: #FAF7F0;
  --surface-sidebar: var(--sime-nuit);
  --surface-bordure: #CBBFA0;

  /* Typographie */
  --police-display: "Fraunces", Georgia, serif;
  --police-corps: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --police-mono: "JetBrains Mono", "SF Mono", Consolas, monospace;

  /* Échelle */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 14px;
  --ombre-carte: 0 1px 2px rgba(14, 26, 43, 0.06), 0 4px 12px rgba(14, 26, 43, 0.08);
  --ombre-flottante: 0 8px 24px rgba(14, 26, 43, 0.18);

  --espace-1: 4px;
  --espace-2: 8px;
  --espace-3: 12px;
  --espace-4: 16px;
  --espace-5: 24px;
  --espace-6: 32px;
  --espace-7: 48px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--surface-app);
  color: var(--sime-encre);
  font-family: var(--police-corps);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, .display {
  font-family: var(--police-display);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--sime-nuit);
  margin: 0;
}

h1 { font-size: 1.9rem; }
h2 { font-size: 1.4rem; }
h3 { font-size: 1.1rem; }

code, .mono, .valeur-monetaire, .numero-document {
  font-family: var(--police-mono);
  font-feature-settings: "tnum";
}

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

button, input, select, textarea {
  font-family: inherit;
  font-size: 0.95rem;
}

/* Focus visible — accessibilité clavier */
:focus-visible {
  outline: 2px solid var(--sime-or);
  outline-offset: 2px;
}

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

/* ----------------------------------------------------------------------------
   Layout applicatif
---------------------------------------------------------------------------- */
.app-shell {
  display: grid;
  grid-template-columns: 264px 1fr;
  min-height: 100vh;
}

.sidebar {
  background: var(--surface-sidebar);
  color: var(--sime-creme);
  display: flex;
  flex-direction: column;
  padding: var(--espace-5) var(--espace-4);
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}

/* CORRECTIF 2026-07-08 — bug rapporté : sidebar et contenu se chevauchent
   de façon désordonnée sur desktop. Cause réelle : .app-shell est une
   grille à 2 colonnes (264px 1fr), mais le HTML généré par app.js place
   TROIS enfants directs dedans (.sidebar, .sidebar__voile, puis le
   wrapper barre-mobile+contenu). Sans cette règle, .sidebar__voile
   (le voile d'arrière-plan du tiroir mobile — vide mais bien présent
   dans le DOM même sur desktop) occupait silencieusement la 2ᵉ colonne
   de la grille en ligne 1, repoussant le vrai contenu applicatif dans
   une ligne 2 / colonne 1 coincée sous la sidebar. .sidebar__voile ne
   doit exister visuellement/dans la grille QUE sur mobile (voir la
   media query ci-dessous, qui le réactive en position fixed) ; display:
   none ici le retire entièrement du flux de la grille sur desktop. */
.sidebar__voile {
  display: none;
}

.barre-mobile {
  display: none;
  align-items: center;
  gap: var(--espace-3);
  padding: var(--espace-3) var(--espace-4);
  background: var(--surface-carte);
  border-bottom: 1px solid var(--surface-bordure);
  position: sticky;
  top: 0;
  z-index: 50;
}

.barre-mobile__bouton {
  background: none;
  border: none;
  cursor: pointer;
  padding: var(--espace-2);
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.barre-mobile__bouton span {
  display: block;
  width: 22px;
  height: 2px;
  background: var(--sime-nuit);
  border-radius: 2px;
}

.barre-mobile__titre {
  font-weight: 700;
  color: var(--sime-nuit);
}

/* Barre d'onglets mobile fixée en bas d'écran (Axe 7 — suite, inspirée du
   modèle partagé) : display:none par défaut (desktop), rebasculée en
   flex uniquement dans le @media (max-width: 860px) ci-dessous — même
   patron que .barre-mobile juste au-dessus. */
.barre-onglets-mobile {
  display: none;
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 90;
  background: var(--surface-carte);
  border-top: 1px solid var(--surface-bordure);
  padding-bottom: env(safe-area-inset-bottom, 0px);
  box-shadow: 0 -4px 16px rgba(0,0,0,0.08);
}

/* Correctif 2026-09-02 : masquée tant qu'un champ de saisie a le focus
   (clavier virtuel ouvert) — voir app.js, écouteurs focusin/focusout
   globaux. `display:none` plutôt que `visibility:hidden` : sinon elle
   continuerait à occuper sa hauteur fixe et resterait cliquable/visible
   par-dessus le clavier sur certains navigateurs. */
.barre-onglets-mobile--masquee {
  display: none !important;
}

.barre-onglets-mobile__item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: var(--espace-2) 0;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--sime-petrole);
  font-size: 0.7rem;
  font-family: inherit;
  text-decoration: none;
}

.barre-onglets-mobile__icone {
  font-size: 1.25rem;
  line-height: 1;
}

.barre-onglets-mobile__item.actif {
  color: var(--sime-or);
  font-weight: 600;
}

/* Ce bloc DOIT rester après la règle de base .sidebar ci-dessus : à
   spécificité égale (un seul sélecteur de classe des deux côtés), la
   cascade CSS retient la déclaration la plus tardive dans le fichier.
   Le mettre avant, comme précédemment, neutralisait purement et
   simplement display:none sur mobile — la sidebar restait affichée
   plein écran quelle que soit la largeur, exactement le symptôme
   remonté (impossible de voir/atteindre le contenu après un tap). */
@media (max-width: 860px) {
  .app-shell { grid-template-columns: 1fr; }
  .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: min(320px, 85vw);
    z-index: 100;
    box-shadow: 8px 0 24px rgba(0,0,0,0.25);
    transform: translateX(-100%);
    transition: transform 0.25s ease;
    visibility: hidden;
  }
  .sidebar.ouverte {
    transform: translateX(0);
    visibility: visible;
  }
  .barre-mobile { display: flex !important; }
  .barre-onglets-mobile { display: flex !important; }
  /* CORRECTIF 2026-09-04 : la réserve de place pour la barre du bas
     (padding-bottom) a été déplacée plus loin dans ce fichier, juste après
     la règle de base .contenu — voir le commentaire à cet endroit pour la
     raison exacte (même famille de bug "ordre des règles CSS" que celui
     déjà rencontré et documenté ailleurs dans ce fichier). */
  .sidebar__voile {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(14,26,43,0.5);
    z-index: 99;
    opacity: 0;
    transition: opacity 0.25s ease;
  }
  .sidebar__voile.visible { display: block; opacity: 1; }
}

/* Cahier des charges §5 — Optimisation Desktop : le menu vertical
   complet convient au mobile, mais gaspille une largeur précieuse sur un
   écran d'ordinateur. Le bouton de bascule n'existe qu'à partir de cette
   largeur (sur mobile, la sidebar est un tiroir plein écran temporaire —
   la réduire n'aurait pas de sens). */
@media (min-width: 861px) {
  .sidebar__bascule { display: flex; align-items: center; justify-content: center; }

  body.sidebar-reduite .app-shell { grid-template-columns: 76px 1fr; }
  body.sidebar-reduite .sidebar { padding-left: var(--espace-3); padding-right: var(--espace-3); }
  body.sidebar-reduite .sidebar__marque { justify-content: center; }
  body.sidebar-reduite .sidebar__marque-texte,
  body.sidebar-reduite .sidebar__pied,
  body.sidebar-reduite .nav-module__libelle,
  body.sidebar-reduite .nav-module__sous-liste,
  body.sidebar-reduite .nav-module__chevron {
    display: none;
  }
  body.sidebar-reduite .sidebar__bascule { position: absolute; top: 0; left: 50%; transform: translate(-50%, -44px); }
  body.sidebar-reduite .nav-module { justify-content: center; }

  /* Très grands écrans (postes de travail bureau, moniteurs élargis) :
     le contenu profite de la largeur au lieu de rester figé à la même
     mise en page que sur un ordinateur portable de 13". */
  .contenu { max-width: 1800px; }
}

@media (min-width: 1440px) {
  .app-shell { grid-template-columns: 288px 1fr; }
  body.sidebar-reduite .app-shell { grid-template-columns: 76px 1fr; }
}

.sidebar__marque {
  display: flex;
  align-items: center;
  gap: var(--espace-3);
  padding-bottom: var(--espace-5);
  border-bottom: 1px solid rgba(201, 162, 75, 0.25);
  margin-bottom: var(--espace-5);
  position: relative;
}

.sidebar__marque-texte {
  min-width: 0;
  overflow: hidden;
}

.sidebar__bascule {
  display: none; /* réactivé uniquement en desktop, voir media query ci-dessous */
  margin-left: auto;
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 1px solid rgba(201, 162, 75, 0.4);
  background: transparent;
  color: var(--sime-or-clair);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
}

.sidebar__bascule:hover {
  background: rgba(201, 162, 75, 0.12);
}

.nav-module__badge {
  /* Correctif 2026-08-31 (réaménagement demandé) : badge élargi en
     pilule (au lieu d'un cercle strict) pour accueillir une abréviation
     de 2-3 lettres (voir ABREVIATION_PAR_MODULE dans app.js) plutôt
     qu'une seule lettre — "Maintenance" et "Magasin" affichaient tous
     deux "M", "Achats" et "Administration" tous deux "A". */
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  height: 26px;
  padding: 0 4px;
  border-radius: 13px;
  background: rgba(201, 162, 75, 0.14);
  border: 1px solid rgba(201, 162, 75, 0.3);
  color: var(--sime-or-clair);
  font-family: var(--police-display);
  font-size: 0.68rem;
  letter-spacing: 0.02em;
  flex-shrink: 0;
}

.nav-module__libelle {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.sidebar__sceau {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, var(--sime-or-clair), var(--sime-bronze) 65%, var(--sime-nuit-clair) 100%);
  border: 1.5px solid var(--sime-or);
  flex-shrink: 0;
}

.sidebar__nom {
  font-family: var(--police-display);
  font-size: 1.15rem;
  letter-spacing: 0.04em;
  color: var(--sime-or-clair);
}

.sidebar__sous-titre {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: rgba(237, 230, 214, 0.55);
}

.nav-module,
.nav-module__sous-item {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

/* Regroupement du menu par catégorie métier (Axe 7 — suite, demande
   explicite : "mieux organiser au lieu d'aligner comme ça"). Un simple
   libellé discret, pas un composant interactif — la catégorie elle-même
   ne se replie pas, seuls les modules qu'elle contient le font. */
.nav-categorie { margin-top: var(--espace-4); }
.nav-categorie:first-of-type { margin-top: 0; }
.nav-categorie__titre {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--espace-2);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(237, 230, 214, 0.45);
  padding: var(--espace-1) var(--espace-3) var(--espace-2);
  margin-top: var(--espace-2);
  text-decoration: none;
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: color 0.15s ease, background 0.15s ease;
}
.nav-categorie__titre:hover {
  color: var(--sime-or-clair);
  background: rgba(201, 162, 75, 0.08);
}
.nav-categorie__voir-tout {
  font-weight: 500;
  text-transform: none;
  letter-spacing: normal;
  font-size: 0.68rem;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.nav-categorie__titre:hover .nav-categorie__voir-tout { opacity: 0.85; }
body.sidebar-reduite .nav-categorie__titre { display: none; }

.nav-module {
  display: flex;
  align-items: center;
  gap: var(--espace-3);
  padding: var(--espace-3) var(--espace-3);
  border-radius: var(--radius-md);
  color: rgba(237, 230, 214, 0.78);
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.nav-module:hover {
  background: rgba(201, 162, 75, 0.1);
  color: var(--sime-creme);
}

.nav-module.actif {
  background: rgba(201, 162, 75, 0.16);
  color: var(--sime-or-clair);
  border-left: 3px solid var(--sime-or);
  padding-left: calc(var(--espace-3) - 3px);
}

/* Repli/dépli (Axe 7 — refonte navigation) : un module avec sous-modules
   n'affiche sa liste QUE si son groupe porte la classe .ouvert (posée en
   JS — voir construireSidebar dans app.js). Avant ce changement, TOUS les
   sous-modules de TOUS les modules étaient visibles en permanence :
   c'était la vraie source de la densité reprochée à la navigation
   (30+ lignes systématiquement affichées, la plupart sans rapport avec
   l'écran consulté). */
.nav-module__groupe .nav-module__sous-liste { display: none; }
.nav-module__groupe.ouvert .nav-module__sous-liste { display: flex; }

.nav-module__chevron {
  margin-left: auto;
  font-size: 0.7rem;
  transition: transform 0.15s ease;
  opacity: 0.6;
  flex-shrink: 0;
}
.nav-module__groupe.ouvert .nav-module__chevron { transform: rotate(90deg); }

.nav-module__sous-liste {
  margin-left: var(--espace-6);
  display: flex;
  flex-direction: column;
  gap: var(--espace-1);
}

.nav-module__sous-item {
  font-size: 0.85rem;
  color: rgba(237, 230, 214, 0.6);
  padding: var(--espace-1) var(--espace-2);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.nav-module__sous-item:hover { color: var(--sime-creme); }

.nav-module__sous-item.actif {
  color: var(--sime-or-clair);
  font-weight: 600;
}

/* Recherche rapide dans la sidebar (Axe 7) : filtre les libellés de
   modules/sous-modules à la frappe, sans requête réseau — tout est déjà
   en mémoire côté client (voir construireSidebar). */
.sidebar__recherche {
  margin: 0 0 var(--espace-3);
  padding: var(--espace-2) var(--espace-3);
  border-radius: var(--radius-md);
  border: 1px solid rgba(237,230,214,0.18);
  background: rgba(255,255,255,0.05);
  color: var(--sime-creme);
  font-size: 0.85rem;
  width: 100%;
}
.sidebar__recherche::placeholder { color: rgba(237,230,214,0.45); }
.sidebar__recherche:focus { outline: none; border-color: var(--sime-or); }
body.sidebar-reduite .sidebar__recherche { display: none; }

.sidebar__pied {
  /* CORRECTIF 2026-07-21 — bug rapporté : le bouton "Se déconnecter"
     n'était atteignable qu'en défilant jusqu'au tout bas d'une liste de
     modules parfois longue. position:sticky (le parent .sidebar est déjà
     le conteneur défilant, voir overflow-y:auto ci-dessus) le maintient
     désormais visible en permanence, quel que soit le nombre de modules. */
  position: sticky;
  bottom: 0;
  margin-top: var(--espace-4);
  padding-top: var(--espace-4);
  padding-bottom: var(--espace-2);
  font-size: 0.75rem;
  color: rgba(237, 230, 214, 0.4);
  background: var(--surface-sidebar);
  border-top: 1px solid rgba(237, 230, 214, 0.12);
}

/* ----------------------------------------------------------------------------
   Contenu principal
---------------------------------------------------------------------------- */
.contenu {
  padding: var(--espace-6) var(--espace-7);
}

/* CORRECTIF 2026-09-04 (bug rapporté avec capture d'écran : la page
   Pilotage — et par extension toute page se terminant près du bas
   d'écran, ex. Messagerie Interne — se retrouvait coupée par la barre
   d'onglets mobile fixe, "Photage" et les dernières cartes à peine
   visibles). Cause réelle : cette règle .contenu (padding: ...) est un
   RACCOURCI qui réinitialise les 4 côtés, y compris padding-bottom.
   Cette règle-ci vivait auparavant dans le bloc @media plus HAUT dans le
   fichier (juste après .sidebar) — à spécificité égale (.contenu des deux
   côtés), la déclaration la PLUS TARDIVE dans le fichier gagne toujours,
   quel que soit l'ordre des media queries : la règle de base ci-dessus,
   plus bas dans le fichier, écrasait donc systématiquement le
   padding-bottom protecteur, sur TOUTES les pages, dès que le viewport
   passait sous 860px. Replacée ICI, après la règle qu'elle doit
   surcharger, exactement le même principe que le correctif déjà appliqué
   à .messagerie-shell__fil plus loin dans ce fichier (voir son
   commentaire, 2026-09-02) — l'ordre des règles CSS compte autant que la
   spécificité. */
@media (max-width: 860px) {
  .contenu { padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px)); }
}

.entete-page {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  margin-bottom: var(--espace-6);
  flex-wrap: wrap;
  gap: var(--espace-4);
}

.entete-page__eyebrow {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--sime-bronze);
  font-weight: 600;
  margin-bottom: var(--espace-1);
}

/* ----------------------------------------------------------------------------
   Tableau de bord — sections regroupées (Cahier des charges §5 : lisibilité
   des indicateurs dès la connexion, plutôt qu'une grille plate de chiffres
   sans hiérarchie).
---------------------------------------------------------------------------- */
.section-dashboard {
  margin-bottom: var(--espace-6);
}

.section-dashboard__titre {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  font-size: 0.95rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--sime-bronze);
  margin-bottom: var(--espace-3);
  padding-bottom: var(--espace-2);
  border-bottom: 1px solid var(--surface-bordure);
}

/* Correctif 2026-08-31 (demande : rendre le tableau de bord plus lisible
   au premier coup d'œil, sur téléphone comme sur ordinateur) : chaque
   section porte désormais le même badge que son module dans la sidebar
   (voir ABREVIATION_PAR_MODULE dans app.js) — un repère visuel identique
   entre le menu et le tableau de bord, pour retrouver plus vite quelle
   section correspond à quel module en un coup d'œil, surtout utile en
   défilement rapide sur mobile. */
.section-dashboard__badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 22px;
  padding: 0 4px;
  border-radius: 11px;
  background: rgba(201, 162, 75, 0.14);
  border: 1px solid rgba(201, 162, 75, 0.3);
  color: var(--sime-or);
  font-family: var(--police-display);
  font-size: 0.62rem;
  letter-spacing: 0.02em;
  text-transform: none;
  flex-shrink: 0;
}

/* ----------------------------------------------------------------------------
   Pages "hub" de catégorie/module (Axe 7 — suite) : grille de cartes en
   pleine page, remplace la navigation en liste verticale imbriquée pour
   les niveaux catégorie -> module -> sous-module. Voir
   renderHubCategorie()/renderHubModule() dans app.js.
---------------------------------------------------------------------------- */
.grille-hub {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: var(--espace-4);
}
@media (max-width: 640px) {
  .grille-hub { grid-template-columns: repeat(2, 1fr); gap: var(--espace-3); }
}
.carte-hub {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--espace-2);
  background: var(--surface-carte);
  border: 1px solid var(--surface-bordure);
  border-radius: var(--radius-lg);
  padding: var(--espace-4);
  text-decoration: none;
  color: inherit;
  transition: border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}
.carte-hub:hover {
  border-color: var(--sime-or);
  transform: translateY(-2px);
  box-shadow: var(--ombre-carte);
}
.carte-hub__badge {
  /* Correctif 2026-08-31 — même raison que .nav-module__badge ci-dessus :
     pilule plutôt que cercle strict, pour une abréviation 2-3 lettres. */
  min-width: 36px;
  height: 36px;
  padding: 0 6px;
  border-radius: 18px;
  background: rgba(201, 162, 75, 0.14);
  color: var(--sime-or);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.78rem;
  font-family: var(--police-mono);
}
.carte-hub__libelle {
  font-weight: 600;
  color: var(--sime-nuit);
}
.carte-hub__desc {
  font-size: 0.78rem;
  color: var(--sime-petrole);
}

/* ----------------------------------------------------------------------------
   Cartes & KPI — esthétique "médaille gravée"
---------------------------------------------------------------------------- */
.grille-kpi {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--espace-4);
  margin-bottom: var(--espace-6);
}

/* Correctif 2026-08-31 (demande : "comment se présente le tableau de
   bord... utilisé avec téléphone et ordinateur") : jusqu'ici, les 14 KPI
   étaient tous au même niveau visuel — sur mobile, lire "où en est mon
   argent" demandait de défiler 6 sections. La rangée héro met les 4
   chiffres qu'un dirigeant regarde en premier (Trésorerie, Résultat,
   Créances, Dettes — voir KPI_CLES_PRIORITAIRES dans app.js) tout en
   haut, dans un format plus grand, SANS attendre le défilement — aussi
   utile pressé sur téléphone qu'au bureau sur grand écran. 2 colonnes dès
   le mobile (comme .grille-kpi ci-dessous) plutôt qu'une liste verticale
   de 4, pour rester visible sans défiler même sur un petit écran. */
.grille-kpi--hero {
  grid-template-columns: repeat(4, 1fr);
  gap: var(--espace-3);
  margin-bottom: var(--espace-5);
}
@media (max-width: 900px) {
  .grille-kpi--hero { grid-template-columns: repeat(2, 1fr); }
}
/* Correctif 2026-09-02 (signalé sur capture d'écran réelle) : "Trésorerie
   disponible : -6 871 732 FCFA" débordait de sa carte sur téléphone —
   .carte-kpi__montant est en white-space:nowrap (correctif du 2026-08-31,
   pour ne plus jamais couper un chiffre en plein milieu), mais 2 colonnes
   sur un écran de ~360px ne laissent qu'environ 150px par carte : bien
   trop étroit pour un nombre à 7-8 chiffres avec signe négatif. En
   dessous de 480px (téléphones), une seule colonne — chaque carte hero
   récupère alors toute la largeur de l'écran, largement suffisant. */
@media (max-width: 480px) {
  .grille-kpi--hero { grid-template-columns: 1fr; }
}

/* CORRECTIF 2026-07-22 — bug/retour rapporté : sur mobile, minmax(220px,1fr)
   ne laisse jamais la place à 2 colonnes (220×2 > largeur d'écran utile),
   donc le tableau de bord s'affichait en une seule colonne verticale peu
   scannable, contrairement au modèle partagé (2 colonnes de cartes KPI). */
@media (max-width: 640px) {
  .grille-kpi {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--espace-3);
  }
  /* CORRECTIF 2026-07-22 — bug rapporté (capture d'écran) : les valeurs
     à 6-7 chiffres ("726370 FCFA") étaient tronquées à mi-caractère par
     .carte-kpi { overflow:hidden } combiné à un texte qui ne pouvait pas
     se couper (pas d'espace interne).
     CORRECTIF 2026-08-31 (nouvelle capture d'écran réelle) : le
     overflow-wrap:anywhere posé ici pour régler CE bug-là en créait un
     PIRE : un montant qui ne tenait toujours pas sur une ligne se
     coupait n'importe où, y compris EN PLEIN MILIEU du nombre
     ("4 500 FCFA" rendu "450" / "0" / "FCFA" sur 3 lignes). Retiré —
     .carte-kpi__montant/__devise (voir plus bas) réglent maintenant les
     deux bugs à la fois : le nombre ne se coupe JAMAIS (nowrap), seule
     l'unité "FCFA" peut passer à la ligne suivante. */
  .carte-kpi { padding: var(--espace-4) var(--espace-3); }
  .carte-kpi__valeur {
    /* Correctif 2026-09-02 (signalé : "les montants de 6 chiffres en
       allant débordent") : une taille fixe reste trop grande pour
       certaines largeurs d'écran/de carte — clamp() la fait varier en
       continu avec la largeur de la fenêtre plutôt que sauter entre
       quelques paliers fixes, donc le nombre a beaucoup plus de chances
       de tenir sur une ligne quelle que soit la largeur réelle de
       l'appareil (voir aussi min-width:0 sur .carte-kpi ci-dessus, qui
       traite la cause du débordement plutôt que seulement sa taille). */
    font-size: clamp(0.85rem, 4.5vw, 1.15rem);
    line-height: 1.25;
  }
  .carte-kpi__label { font-size: 0.72rem; }
  /* La rangée héro reste plus grande que les cartes normales même sur
     mobile — c'est tout l'intérêt (voir commentaire ci-dessus) — mais
     doit tout de même rétrécir un cran pour ne pas déborder à 2
     colonnes sur les petits écrans. */
  .carte-kpi--hero .carte-kpi__valeur { font-size: clamp(1rem, 6vw, 1.35rem); }
  .carte-kpi--hero .carte-kpi__label { font-size: 0.68rem; }
}

.carte {
  background: var(--surface-carte);
  border: 1px solid var(--surface-bordure);
  border-radius: var(--radius-lg);
  box-shadow: var(--ombre-carte);
  padding: var(--espace-5);
}

.carte-kpi {
  position: relative;
  overflow: hidden;
  /* Correctif 2026-09-02 (signalé : "les montants de 6 chiffres en allant
     débordent") : la vraie cause n'était pas la largeur de colonne — un
     enfant de grille refuse PAR DÉFAUT de rétrécir sous la largeur
     naturelle de son contenu (min-width:auto implicite), même si sa piste
     de grille est plus étroite et même si le texte est en overflow:hidden.
     Un montant en white-space:nowrap forçait donc la carte ENTIÈRE (donc
     la colonne, donc potentiellement la page) à s'élargir pour lui faire
     de la place, quel que soit le nombre de colonnes choisi par ailleurs.
     min-width:0 autorise la carte à rétrécir normalement ; combiné à
     .carte-kpi__montant en clamp() ci-dessous (qui réduit la police en
     continu selon la largeur réellement disponible, plutôt qu'une seule
     taille fixe qui reste trop grande pour certaines largeurs), le
     nombre reste sur une ligne ET dans les limites de la carte. */
  min-width: 0;
}

.carte-kpi::before {
  content: "";
  position: absolute;
  top: 0; left: 0;
  width: 100%;
  height: 3px;
  background: linear-gradient(90deg, var(--sime-or), var(--sime-bronze));
}

/* Rangée héro (voir .grille-kpi--hero ci-dessus) : fond légèrement teinté
   et bordure or pour se distinguer d'un simple coup d'œil des sections
   "détail" plus bas — sans s'écarter de la charte or/nuit existante. */
.carte-kpi--hero {
  background: linear-gradient(160deg, rgba(201, 162, 75, 0.08), var(--surface-carte) 60%);
  border-color: rgba(201, 162, 75, 0.35);
}
.carte-kpi--hero .carte-kpi__valeur {
  /* clamp() ici aussi (voir correctif 2026-09-02 détaillé dans la media
     query mobile plus haut) : 4 colonnes desktop peuvent redevenir
     étroites sur une fenêtre de largeur moyenne (tablette, fenêtre
     restreinte) — pas seulement sur téléphone. */
  font-size: clamp(1.2rem, 2.4vw, 1.7rem);
}

/* Correctif 2026-08-31 : une carte KPI n'était jusqu'ici qu'un chiffre à
   lire — quand elle pointe vers un écran (route définie dans
   SECTIONS_DASHBOARD/KPI_CLES_PRIORITAIRES), curseur et survol sur
   desktop, et un retour visuel identique au tap sur mobile (:active),
   signalent qu'elle est un raccourci et pas seulement une statistique. */
.carte-kpi--cliquable {
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.carte-kpi--cliquable:hover,
.carte-kpi--cliquable:active {
  transform: translateY(-2px);
  box-shadow: var(--ombre-flottante);
  border-color: rgba(201, 162, 75, 0.4);
}

.carte-kpi__label {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--sime-petrole);
  font-weight: 600;
  margin-bottom: var(--espace-2);
}

.carte-kpi__valeur {
  font-family: var(--police-display);
  /* Correctif 2026-09-02 : clamp() plutôt qu'une taille fixe, même
     raison que les correctifs détaillés plus haut (media query mobile
     et .carte-kpi--hero) — la grille normale (.grille-kpi) peut aussi
     retomber à 2-3 colonnes assez étroites sur une fenêtre de bureau
     redimensionnée, pas seulement sur téléphone. */
  font-size: clamp(1.3rem, 2.2vw, 2rem);
  color: var(--sime-nuit);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25em;
}

/* Correctif 2026-08-31 (signalé sur capture d'écran réelle — voir les
   correctifs 2026-07-22 et 2026-08-31 dans la media query mobile
   ci-dessus pour l'historique complet du bug) : un montant formaté est
   maintenant deux éléments distincts plutôt qu'un seul bloc de texte —
   voir formatMontant() dans app.js. Le nombre lui-même (.carte-kpi__montant)
   ne se coupe JAMAIS, quelle que soit la largeur disponible ; seule
   l'unité (.carte-kpi__devise), plus petite et discrète, peut redescendre
   à la ligne si la carte est trop étroite pour les deux côte à côte. */
.carte-kpi__montant {
  white-space: nowrap;
}
.carte-kpi__devise {
  font-size: 0.55em;
  font-family: var(--police-corps, inherit);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--sime-petrole);
  white-space: nowrap;
}

.carte-kpi__variation {
  font-size: 0.82rem;
  font-weight: 600;
  margin-top: var(--espace-1);
}

.carte-kpi__variation.positive { color: var(--couleur-succes); }
.carte-kpi__variation.negative { color: var(--couleur-danger); }

.carte-kpi--indisponible {
  opacity: 0.65;
}

.carte-kpi--indisponible::before {
  background: var(--surface-bordure);
}

.carte-kpi__valeur--indisponible {
  color: var(--sime-petrole);
}

.carte-kpi__note {
  font-size: 0.75rem;
  color: var(--sime-petrole);
  margin-top: var(--espace-2);
  font-style: italic;
  line-height: 1.4;
}

/* ----------------------------------------------------------------------------
   Tableaux
---------------------------------------------------------------------------- */
table.table-sime {
  width: 100%;
  border-collapse: collapse;
  background: var(--surface-carte);
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--ombre-carte);
}

table.table-sime th {
  background: var(--sime-nuit);
  color: var(--sime-or-clair);
  text-align: left;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: var(--espace-3) var(--espace-4);
}

table.table-sime td {
  padding: var(--espace-3) var(--espace-4);
  border-bottom: 1px solid var(--surface-bordure);
  font-size: 0.92rem;
}

table.table-sime tbody tr:hover { background: rgba(201, 162, 75, 0.06); }

/* ----------------------------------------------------------------------------
   Badges de statut
---------------------------------------------------------------------------- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.75rem;
  font-weight: 600;
  padding: 3px 10px;
  border-radius: 100px;
}

.badge-succes  { background: rgba(63, 122, 92, 0.14); color: var(--couleur-succes); }
.badge-attente { background: rgba(201, 122, 43, 0.14); color: var(--couleur-attention); }
.badge-danger  { background: rgba(176, 65, 62, 0.14); color: var(--couleur-danger); }
.badge-info    { background: rgba(61, 110, 143, 0.14); color: var(--couleur-info); }

/* ----------------------------------------------------------------------------
   Boutons
---------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--espace-2);
  border: none;
  border-radius: var(--radius-md);
  padding: 10px 18px;
  font-weight: 600;
  font-size: 0.9rem;
  cursor: pointer;
  transition: transform 0.08s ease, box-shadow 0.15s ease;
}

.btn:active { transform: translateY(1px); }

.btn-primaire {
  background: var(--sime-petrole);
  color: white;
}
.btn-primaire:hover { background: var(--sime-petrole-clair); }

.btn-or {
  background: linear-gradient(135deg, var(--sime-or-clair), var(--sime-bronze));
  color: var(--sime-nuit);
}

.btn-fantome {
  background: transparent;
  border: 1px solid var(--surface-bordure);
  color: var(--sime-nuit);
}

/* ----------------------------------------------------------------------------
   Alerte de marge (Produits Composés) — signal visuel demandé au cahier des charges
---------------------------------------------------------------------------- */
.alerte-marge {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  padding: var(--espace-3) var(--espace-4);
  border-radius: var(--radius-md);
  font-size: 0.88rem;
  font-weight: 600;
  margin-top: var(--espace-3);
}

.alerte-marge.ok { background: rgba(63, 122, 92, 0.1); color: var(--couleur-succes); }
.alerte-marge.insuffisante { background: rgba(176, 65, 62, 0.12); color: var(--couleur-danger); }

/* ----------------------------------------------------------------------------
   Formulaires
---------------------------------------------------------------------------- */
.champ {
  display: flex;
  flex-direction: column;
  gap: var(--espace-1);
  margin-bottom: var(--espace-4);
}

.champ label {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--sime-nuit);
}

.champ input, .champ select, .champ textarea {
  border: 1px solid var(--surface-bordure);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  background: white;
  color: var(--sime-encre);
}

.champ input:focus, .champ select:focus, .champ textarea:focus {
  border-color: var(--sime-or);
}

/* ----------------------------------------------------------------------------
   Panneau SuperAdmin — composition de forfaits (cases à cocher modules)
---------------------------------------------------------------------------- */
.forfait-builder {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--espace-5);
}

@media (max-width: 720px) {
  .forfait-builder { grid-template-columns: 1fr; }
}

.module-checklist {
  border: 1px solid var(--surface-bordure);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.module-checklist__entete {
  background: var(--sime-creme-fonce);
  padding: var(--espace-3) var(--espace-4);
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: var(--espace-2);
}

.module-checklist__sous-item {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  padding: var(--espace-2) var(--espace-4) var(--espace-2) var(--espace-6);
  border-top: 1px solid var(--surface-bordure);
}

.module-checklist__sous-item--verrouille {
  background: rgba(46,125,140,0.06);
  color: var(--sime-petrole);
}

.module-checklist__astuce {
  margin-left: auto;
  font-size: 0.7rem;
  font-weight: 500;
  color: var(--sime-petrole);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

/* ----------------------------------------------------------------------------
   Composant InfiniteList — états de chargement, vide, erreur
---------------------------------------------------------------------------- */
.infinite-list__sentinel {
  height: 1px;
}

.infinite-list__loading,
.infinite-list__empty {
  padding: var(--espace-6);
  text-align: center;
  color: var(--sime-petrole);
  font-style: italic;
}

.infinite-list__error {
  padding: var(--espace-4);
  text-align: center;
  color: var(--couleur-danger);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--espace-2);
}

.liste-clients {
  display: flex;
  flex-direction: column;
}

/* ----------------------------------------------------------------------------
   Composant EntitySelector — recherche + dropdown de sélection
---------------------------------------------------------------------------- */
.entity-selector {
  position: relative;
}

.entity-selector__input {
  width: 100%;
  border: 1px solid var(--surface-bordure);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  background: white;
  color: var(--sime-encre);
}

.entity-selector__input:focus {
  border-color: var(--sime-or);
  outline: none;
}

.entity-selector__dropdown {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  background: var(--surface-carte);
  border: 1px solid var(--surface-bordure);
  border-radius: var(--radius-md);
  box-shadow: var(--ombre-flottante);
  max-height: 220px;
  overflow-y: auto;
  z-index: 20;
}

.entity-selector__option {
  padding: var(--espace-2) var(--espace-3);
  cursor: pointer;
  font-size: 0.9rem;
}

.entity-selector__option:hover {
  background: rgba(201, 162, 75, 0.1);
}

.entity-selector__empty {
  padding: var(--espace-3);
  color: var(--sime-petrole);
  font-style: italic;
  font-size: 0.85rem;
}

/* ----------------------------------------------------------------------------
   Lignes de formulaire à 2/3 champs côte à côte — correctif 2026-08-31
   (signalé : "beaucoup de coins qui débordent") : 5 écrans (abonnement,
   offres, profil...) posaient chacun leur propre
   style="display:grid; grid-template-columns: 1fr 1fr..." EN LIGNE, sans
   AUCUN repli mobile — sur un petit écran, "Montant réglé" et
   "Référence" (voir capture d'écran signalée) débordaient purement et
   simplement hors de l'écran. Classes partagées, une seule fois
   responsive, à réutiliser partout au lieu de réinventer un style en
   ligne à chaque nouveau formulaire.
---------------------------------------------------------------------------- */
.champ-ligne {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--espace-3);
}
.champ-ligne--3 {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: var(--espace-3);
}
@media (max-width: 640px) {
  .champ-ligne,
  .champ-ligne--3 {
    grid-template-columns: 1fr;
  }
}

/* ----------------------------------------------------------------------------
   Tableaux — correctif 2026-08-31 (signalé : "beaucoup de coins qui
   débordent") : 17 écrans (Bons de Livraison, Commandes de Vente, Bons
   d'Achat, Sorties Diverses...) utilisent un <table> HTML brut, AUCUN
   n'était enveloppé dans un conteneur à défilement horizontal — sur
   mobile, un tableau à 4-5 colonnes ne rentre jamais dans 360px de large
   et débordait purement et simplement hors de l'écran (voire hors de la
   carte/modale qui la contient). Règle globale plutôt que 17 correctifs
   individuels : le <table> lui-même défile horizontalement DANS ses
   propres limites — technique standard (display:block sur l'élément
   <table> uniquement ; ses enfants thead/tbody/tr/td gardent leur
   comportement de table par défaut, donc l'alignement des colonnes reste
   correct) — plutôt que de faire déborder toute la page.
---------------------------------------------------------------------------- */
.contenu table,
dialog table {
  display: block;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  max-width: 100%;
}

/* ----------------------------------------------------------------------------
   Messagerie (fil de discussion) — support SIME (support-sime-screen.js)
   ET messagerie interne (messagerie-interne-screen.js), même vocabulaire
   visuel volontairement partagé entre les deux : bulles alignées à droite
   pour "moi", à gauche pour l'interlocuteur, coin cassé du côté de
   l'émetteur (convention universelle de messagerie). Demande explicite :
   "le rendu doit être très professionnel" — pas de fioritures, la
   hiérarchie vient uniquement de la couleur (or/nuit) et de l'alignement.
---------------------------------------------------------------------------- */
.messagerie-fil-carte {
  display: flex;
  flex-direction: column;
}
.messagerie-fil {
  max-height: 50vh;
  min-height: 180px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--espace-3);
  padding: var(--espace-2) var(--espace-1);
}
.messagerie-fil__formulaire {
  display: flex;
  gap: var(--espace-2);
  align-items: flex-end;
  margin-top: var(--espace-3);
  padding-top: var(--espace-3);
  border-top: 1px solid var(--surface-bordure);
}
.messagerie-fil__formulaire textarea {
  flex: 1;
  resize: none;
  /* Correctif 2026-09-02 (signalé : "sa déborde au niveau de la
     messagerie interne") : un <textarea> a une largeur minimale
     intrinsèque par défaut (~20 caractères, l'attribut `cols` implicite)
     — un enfant flex refuse de rétrécir sous cette largeur tant que
     min-width n'est pas explicitement remis à zéro, MÊME avec flex:1.
     Sur un écran étroit, ça poussait le bouton "Envoyer" hors du cadre
     (toute la ligne du formulaire débordait horizontalement) — exactement
     la même famille de bug que .carte-kpi (min-width:auto implicite),
     corrigée le même jour pour les cartes du tableau de bord. */
  min-width: 0;
}

.messagerie-bulle {
  max-width: 78%;
  padding: var(--espace-2) var(--espace-3);
  border-radius: var(--radius-md);
  line-height: 1.45;
  font-size: 0.92rem;
  word-break: break-word;
}
.messagerie-bulle--moi {
  align-self: flex-end;
  background: var(--sime-nuit);
  color: #fff;
  border-bottom-right-radius: 4px;
}
.messagerie-bulle--autre {
  align-self: flex-start;
  background: var(--surface-alt, #f5f3ee);
  color: var(--sime-nuit);
  border-bottom-left-radius: 4px;
}
.messagerie-bulle__meta {
  font-size: 0.72rem;
  opacity: 0.65;
  margin-top: 4px;
}
@media (max-width: 640px) {
  .messagerie-bulle { max-width: 88%; font-size: 0.88rem; }
}

/* ----------------------------------------------------------------------------
   Coquille de messagerie à deux volets (liste de conversations + fil
   ouvert) — messagerie-interne-screen.js (nouveau) ET whatsapp-screen.js
   (retrofit, voir son commentaire dédié) partagent ce composant.
   Correctif 2026-08-31 (signalé : "beaucoup de coins qui débordent" +
   demande explicite "le rendu doit être très professionnel") :
   whatsapp-screen.js posait déjà ce même agencement à deux colonnes en
   ligne, SANS repli mobile (minmax(260px,340px) 1fr — sur un écran de
   360px, il ne reste que 100px pour le fil de discussion, inutilisable).
   Ici, sous les 860px : une SEULE colonne à la fois (liste OU fil, jamais
   les deux empilés — ce qui donnerait une page interminable) — basculée
   par la classe modificateur --fil-ouvert posée en JS quand une
   conversation est sélectionnée.
---------------------------------------------------------------------------- */
.messagerie-shell {
  display: grid;
  grid-template-columns: minmax(260px, 340px) 1fr;
  gap: var(--espace-4);
  height: calc(100vh - 220px);
  min-height: 420px;
}
.messagerie-shell__liste {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* Correctif 2026-09-02 : même bug de fond que le textarea plus bas —
     .messagerie-shell est une grille (minmax(260px,340px) 1fr), et un
     enfant de grille refuse par défaut de rétrécir sous la largeur de
     son contenu. Défense en profondeur : ce conteneur explicite
     min-width:0 lui-même plutôt que de dépendre uniquement du correctif
     posé sur le <textarea> à l'intérieur. */
  min-width: 0;
}
.messagerie-shell__liste-defilement {
  overflow-y: auto;
  flex: 1;
}
.messagerie-shell__fil {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--surface-bordure);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--surface-carte);
  /* Correctif 2026-09-02 — même raison que .messagerie-shell__liste
     ci-dessus : ce volet-ci est celui qui contient le formulaire d'envoi
     (le vrai point de débordement signalé). */
  min-width: 0;
}
.messagerie-shell__fil-entete {
  display: flex;
  align-items: center;
  gap: var(--espace-3);
  padding: var(--espace-3) var(--espace-4);
  border-bottom: 1px solid var(--surface-bordure);
  flex-shrink: 0;
}
.messagerie-shell__fil-retour {
  display: none;
  background: none;
  border: none;
  font-size: 1.2rem;
  cursor: pointer;
  color: var(--sime-nuit);
  padding: 0 var(--espace-2) 0 0;
}
@media (max-width: 860px) {
  .messagerie-shell__fil-retour { display: inline-flex; }
}
.messagerie-shell__fil-corps {
  flex: 1;
  overflow-y: auto;
  padding: var(--espace-4);
  display: flex;
  flex-direction: column;
  gap: var(--espace-3);
}
.messagerie-shell__fil-formulaire {
  display: flex;
  gap: var(--espace-2);
  align-items: flex-end;
  padding: var(--espace-3);
  border-top: 1px solid var(--surface-bordure);
  flex-shrink: 0;
}
.messagerie-shell__fil-formulaire textarea {
  flex: 1;
  resize: none;
  max-height: 100px;
  /* Correctif 2026-09-02 — même bug, même raison que
     .messagerie-fil__formulaire textarea ci-dessus (voir son
     commentaire) : sans min-width:0, la ligne du formulaire (Messagerie
     Interne ET WhatsApp, qui partagent ce composant) débordait
     horizontalement sur un écran étroit au lieu de laisser le champ de
     texte rétrécir. */
  min-width: 0;
}
.messagerie-shell__vide {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1;
  color: var(--sime-petrole);
  text-align: center;
  padding: var(--espace-5);
}

/* Correctif 2026-09-02 (bug confirmé sur capture d'écran réelle, signalé
   "sa déborde... le problème d'affichage est nul") : ce bloc DOIT venir
   après toutes les règles de base .messagerie-shell__liste/__fil
   ci-dessus, pas avant. Il vivait auparavant juste après la déclaration
   de .messagerie-shell, donc AVANT ces règles de base dans le fichier —
   à spécificité égale (une seule classe), c'est la règle la PLUS TARDIVE
   dans le fichier qui gagne, quel que soit l'ordre des media queries.
   Résultat concret : sur mobile, .messagerie-shell__fil restait
   TOUJOURS visible (display:flex de la règle de base, plus tardive,
   écrasait le display:none voulu ici) — liste ET fil s'empilaient tous
   les deux au lieu de n'en montrer qu'un à la fois, gonflant toute la
   page (d'où, en cascade, le clavier qui semblait "déborder" : la page
   était simplement bien plus haute que prévu). */
@media (max-width: 860px) {
  .messagerie-shell {
    grid-template-columns: 1fr;
    /* Correctif 2026-09-02 (signalé sur capture d'écran réelle) : le
       formulaire d'envoi (dernier enfant du fil, tout en bas) se
       retrouvait caché sous la barre d'onglets mobile fixe — ce calc
       ne soustrayait que l'entête de page, jamais les 64px (+ zone de
       sécurité iOS) de cette barre, contrairement à .contenu qui, lui,
       en tient compte via son padding-bottom (voir plus haut). */
    height: calc(100vh - 180px - 64px - env(safe-area-inset-bottom, 0px));
  }
  .messagerie-shell__liste { display: flex; }
  .messagerie-shell__fil { display: none; }
  .messagerie-shell--fil-ouvert .messagerie-shell__liste { display: none; }
  .messagerie-shell--fil-ouvert .messagerie-shell__fil { display: flex; }
}

/* Correctif 2026-09-02 (signalé : "ça cache du bas, on ne peut pas
   scroller pour rendre visible") : `100vh` correspond au viewport de
   MISE EN PAGE, que Chrome Android ne réduit PAS quand le clavier
   virtuel s'ouvre (seul le viewport VISUEL rétrécit — voir aussi le
   correctif "barre d'onglets qui flotte" plus haut dans app.js, même
   cause). Masquer la barre du bas ne suffit pas seul : .messagerie-
   shell garde une hauteur calculée sur 100vh, donc peut rester plus
   grand que l'espace RÉELLEMENT visible une fois le clavier ouvert —
   son bas (le formulaire d'envoi) sort alors de l'écran, et comme ni
   .messagerie-shell ni .messagerie-shell__fil ne sont eux-mêmes
   scrollables (seule la liste des messages À L'INTÉRIEUR l'est), rien
   ne permet de défiler pour le faire réapparaître.
   `100dvh` (viewport dynamique) suit, lui, le viewport VISUEL en temps
   réel — clavier ouvert ou fermé. Règle séparée plutôt que remplacer
   directement `100vh` ci-dessus : un navigateur qui ne connaît pas
   `dvh` ignore une déclaration invalide et garde la précédente (repli
   automatique, aucun risque de casser quoi que ce soit ailleurs). */
@media (max-width: 860px) {
  .messagerie-shell {
    height: calc(100dvh - 180px - 64px - env(safe-area-inset-bottom, 0px));
  }
}

/* Ligne de contact/conversation — avatar en initiales, aperçu tronqué,
   badge de non-lus. Même vocabulaire visuel qu'un client de messagerie
   professionnel (Slack/Teams), pas de fioriture superflue. */
.contact-ligne {
  display: flex;
  align-items: center;
  gap: var(--espace-3);
  padding: var(--espace-3);
  border-radius: var(--radius-md);
  cursor: pointer;
}
.contact-ligne:hover {
  background: var(--surface-alt, #f5f3ee);
}
.contact-ligne--actif {
  background: rgba(201, 162, 75, 0.14);
}
.contact-ligne__avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--sime-nuit);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-family: var(--police-display);
  font-size: 0.9rem;
  flex-shrink: 0;
}
.contact-ligne__contenu {
  flex: 1;
  min-width: 0;
}
.contact-ligne__nom {
  font-weight: 600;
  color: var(--sime-nuit);
}
.contact-ligne__apercu {
  font-size: 0.85rem;
  color: var(--sime-petrole);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.contact-ligne__meta {
  text-align: right;
  flex-shrink: 0;
}
.contact-ligne__heure {
  font-size: 0.72rem;
  color: var(--sime-petrole);
}

.lignes-document {
  display: flex;
  flex-direction: column;
  gap: var(--espace-3);
  margin: var(--espace-4) 0;
}

.ligne-document {
  display: grid;
  grid-template-columns: 110px 1fr 90px 110px 110px 32px;
  gap: var(--espace-2);
  align-items: center;
  padding: var(--espace-2);
  background: var(--surface-carte);
  border: 1px solid var(--surface-bordure);
  border-radius: var(--radius-sm);
}

@media (max-width: 720px) {
  .ligne-document {
    grid-template-columns: 1fr;
  }
}

/* Correctif 2026-08-31 (signalé : "beaucoup de coins qui débordent") :
   .ligne-document ci-dessus a DÉJÀ un repli mobile correct depuis
   longtemps — mais demandes-achat-screen.js, produits-composes-screen.js
   et sorties-diverses-screen.js posent chacun un style="grid-template-
   columns: ..." EN LIGNE sur l'élément (pour un nombre de colonnes
   différent, moins large que la variante à 6 colonnes ci-dessus), ce qui
   ANNULE la media query par simple spécificité CSS (un style en ligne
   gagne toujours face à une règle de classe, y compris dans une media
   query) — sur mobile, ces 3 écrans débordaient donc bel et bien.
   Modificateurs dédiés, chacun avec SA propre variante mobile, pour que
   ces 3 écrans n'aient plus besoin d'un style en ligne du tout. */
.ligne-document--4col {
  grid-template-columns: 110px 1fr 90px 32px;
}
@media (max-width: 640px) {
  .ligne-document--4col { grid-template-columns: 1fr; }
}
.ligne-document--3col {
  grid-template-columns: 1fr 90px 32px;
}
@media (max-width: 640px) {
  .ligne-document--3col { grid-template-columns: 1fr; }
}
.ligne-document--5col {
  grid-template-columns: 1fr 90px 110px 110px 32px;
}
@media (max-width: 720px) {
  .ligne-document--5col { grid-template-columns: 1fr; }
}

.ligne-document__supprimer {
  background: transparent;
  border: none;
  color: var(--couleur-danger);
  cursor: pointer;
  font-size: 1.1rem;
  padding: 4px;
}

.ligne-document__total {
  display: flex;
  justify-content: flex-end;
  font-family: var(--police-display);
  font-size: 1.2rem;
  padding: var(--espace-3) 0;
  border-top: 2px solid var(--sime-or);
  margin-top: var(--espace-2);
}

/* ----------------------------------------------------------------------------
   Éditeur de lignes d'Offre Commerciale — plus de colonnes que
   .ligne-document (référence, unité, remise, TVA), plus une description
   optionnelle en pleine largeur, donc une carte plutôt qu'une simple grille.
---------------------------------------------------------------------------- */
.ligne-offre {
  background: var(--surface-carte);
  border: 1px solid var(--surface-bordure);
  border-radius: var(--radius-sm);
  padding: var(--espace-2);
  margin-bottom: var(--espace-3);
}

.ligne-offre__grille {
  display: grid;
  grid-template-columns: 90px 1.4fr 90px 70px 70px 100px 70px 70px 110px 32px;
  gap: var(--espace-2);
  align-items: center;
}

.ligne-offre__grille input, .ligne-offre__grille select {
  width: 100%;
}

.ligne-offre__description {
  width: 100%;
  margin-top: var(--espace-2);
  font-size: 0.85rem;
}

@media (max-width: 900px) {
  .ligne-offre__grille {
    grid-template-columns: 1fr 1fr;
  }
}

/* ----------------------------------------------------------------------------
   Module Projet — détail en onglets (2026-08-21, réorganisation)
   Remplace l'ancien détail "tout empilé" (statut + GANTT + PERT + finances
   sur un seul long défilement) par une en-tête stable + de vrais onglets
   internes, cohérent avec le patron .onglet-* déjà utilisé ailleurs
   (ex: tresorerie-screen.js) mais formalisé ici en classes réutilisables.
---------------------------------------------------------------------------- */
.projet-entete-detail {
  padding-bottom: var(--espace-4);
  border-bottom: 1px solid var(--surface-bordure);
  margin-bottom: var(--espace-4);
}

.projet-entete-detail__ligne-titre {
  display: flex;
  align-items: center;
  gap: var(--espace-3);
  flex-wrap: wrap;
}

.projet-entete-detail__statut {
  display: flex;
  align-items: flex-end;
  gap: var(--espace-3);
  margin-top: var(--espace-4);
  flex-wrap: wrap;
}

.projet-entete-detail__statut .champ {
  margin-bottom: 0;
  min-width: 180px;
}

.projet-panneau__actions {
  display: flex;
  gap: var(--espace-3);
  margin-bottom: var(--espace-4);
  flex-wrap: wrap;
}

.projet-souscarte { margin-bottom: var(--espace-4); }

.projet-pied-detail {
  display: flex;
  gap: var(--espace-3);
  margin-top: var(--espace-6);
  padding-top: var(--espace-4);
  border-top: 1px solid var(--surface-bordure);
  justify-content: flex-end;
}

.btn-petit {
  padding: 6px 12px;
  font-size: 0.8rem;
}
