@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Sora:wght@600;700;800&family=IBM+Plex+Sans:wght@400;500;600&display=swap');

:root {
  --ink: #101820;
  --brand: #17365D;
  --steel-800: #17365D;
  --steel-700: #1F4A7A;
  --steel-600: #2E4557;

  --frost-500: #2B93B8;
  --frost-100: #DCEEF3;

  --copper-500: #B96A34;
  --copper-100: #F1E1D2;

  --paper: #EDF1F1;
  --panel: #FFFFFF;
  --text: #1B262E;
  --text-muted: #5B6B72;
  --line: #CBD5D8;

  --ok: #3F8F6F;
  --warn: #C98A2E;
  --danger: #B5432F;
  --violet-500: #6E4FA3;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  background: var(--paper);
  color: var(--text);
  font-family: 'IBM Plex Sans', sans-serif;
  font-size: 15px;
  line-height: 1.5;
}

h1, h2, h3, h4 {
  font-family: 'Space Grotesk', sans-serif;
  font-weight: 700;
  color: var(--brand);
  margin: 0 0 .6em;
  letter-spacing: -0.01em;
}

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

.logoBig {
  font-family: 'Space Grotesk', sans-serif;
  font-weight: 700;
  font-size: 1.3rem;
  color: #fff;
  letter-spacing: 0.01em;
}
.logoBig span { color: var(--frost-500); font-weight: 500; }

/* ---------- Structure : menu latéral façon tableau de contrôle ---------- */

/* Structure "mobile-first" : par défaut (téléphone/tablette), le menu et le
   contenu suivent le flux normal de la page — c'est elle qui défile, comme
   avant. Le menu qui défile "à part", en colonne indépendante, n'est activé
   que plus bas, dans un bloc @media (min-width: 861px) réservé au grand
   écran (ordinateur) — demande du 27/09/2026, revue le 28/09/2026 après
   blocage constaté sur téléphone (Chrome ET l'appli installée) avec la
   version précédente, qui appliquait ces règles par défaut puis tentait de
   les annuler pour mobile. Cette page ne doit jamais dépendre d'une
   annulation : le comportement mobile "normal" doit rester la valeur par
   défaut, jamais une exception. */
.app-shell { display: flex; }

.sidebar {
  width: 100%;
  flex-shrink: 0;
  background: var(--steel-800);
  display: flex;
  flex-direction: column;
}

.sidebar-brand {
  background: #fff;
  padding: 1.3rem 1.2rem;
  text-align: center;
  box-shadow: 0 2px 8px rgba(0,0,0,.18);
  position: relative;
  z-index: 1;
  flex-shrink: 0;
}
.sidebar-brand .brand-logo { width: 100%; max-width: 214px; height: auto; }

.sidebar nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 1.4rem 1rem;
}

.sidebar nav a {
  color: #C7D3D8;
  padding: .55rem .7rem;
  border-radius: 3px;
  border-left: 3px solid transparent;
  font-size: .93rem;
  text-decoration: none;
  transition: background .12s ease, border-color .12s ease, color .12s ease;
  flex-shrink: 0;
}
.sidebar nav a:hover {
  background: var(--steel-700);
  border-left-color: var(--frost-500);
  color: #fff;
}
.sidebar nav a.logout { margin-top: auto; color: #8FA0A8; }

.container { flex: 1; padding: 2rem 2.4rem; width: 100%; max-width: none; }

/* ---------- Grand écran (ordinateur) : menu détaché avec son propre
   défilement, indépendant du contenu ---------- */
@media (min-width: 861px) {
  .app-shell { height: 100vh; overflow: hidden; }

  .sidebar { width: 232px; height: 100vh; overflow: hidden; }

  .sidebar nav {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    /* Barre de défilement fine et sombre, cohérente avec le fond .steel-800
       (au lieu de la barre grise par défaut du navigateur). */
    scrollbar-width: thin;
    scrollbar-color: var(--steel-700) transparent;
  }
  .sidebar nav::-webkit-scrollbar { width: 6px; }
  .sidebar nav::-webkit-scrollbar-track { background: transparent; }
  .sidebar nav::-webkit-scrollbar-thumb { background: var(--steel-700); border-radius: 3px; }
  .sidebar nav::-webkit-scrollbar-thumb:hover { background: var(--frost-500); }

  .container { height: 100vh; overflow-y: auto; }
}

/* ---------- Panneaux (remplace les cartes-à-ombre génériques) ---------- */

.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-left: 3px solid var(--frost-500);
  padding: 1.4rem 1.6rem;
  margin-bottom: 1.6rem;
}
.card:nth-of-type(2n) { border-left-color: var(--copper-500); }

.muted { color: var(--text-muted); font-size: .9rem; }

.card-header-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: .6rem;
  margin-bottom: 1rem;
}
.card-header-row h3 { margin: 0; }
.card-header-actions { display: flex; align-items: center; gap: 1rem; font-size: .88rem; }
.card-header-actions input[type="search"] {
  width: 200px;
  padding: .35rem .5rem;
  font-size: .85rem;
}
.card-header-actions .inline-form button {
  padding: .35rem .7rem;
  font-size: .8rem;
}

.alert {
  padding: .7rem 1rem;
  margin-bottom: 1rem;
  border-left: 3px solid;
  font-size: .92rem;
}
.alert.error { background: #FBEAE6; border-color: var(--danger); color: #7A2E1E; }
.alert.ok { background: #E7F3EE; border-color: var(--ok); color: #1F5C45; }

/* ------------------------------------------------------------------
 * Notifications flottantes (demande du 03/10/2026) : un message .alert
 * marqué "flash" (confirmation ou erreur après une action — accès
 * refusé, permission insuffisante, validation, enregistrement...) ne
 * s'affiche plus comme un bloc figé en haut de page, mais comme une
 * boîte de dialogue flottante au-dessus de l'interface, qui reste
 * entièrement visible et utilisable dessous. Voir app.js pour le
 * bouton de fermeture et la fermeture automatique. Les .alert SANS la
 * classe "flash" (ex. l'erreur de la fenêtre d'ajout d'article) ne
 * sont pas concernés et restent affichés à leur emplacement d'origine.
 * ------------------------------------------------------------------ */
.flash-toast-stack {
  position: fixed;
  top: 1rem;
  right: 1rem;
  z-index: 9999;
  display: flex;
  flex-direction: column;
  gap: .6rem;
  max-width: min(420px, calc(100vw - 2rem));
}
.flash-toast-stack .alert.flash {
  margin-bottom: 0;
  border-radius: 6px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .18);
  background: var(--panel, #fff);
  display: flex;
  align-items: flex-start;
  gap: .6rem;
  animation: flash-toast-in .2s ease-out;
}
.flash-toast-stack .alert.flash .flash-text { flex: 1; }
.flash-toast-stack .alert.flash .flash-close {
  background: none;
  border: none;
  cursor: pointer;
  font: inherit;
  font-size: 1.15rem;
  line-height: 1;
  color: inherit;
  opacity: .55;
  padding: 0 .1rem;
}
.flash-toast-stack .alert.flash .flash-close:hover { opacity: 1; }
@keyframes flash-toast-in {
  from { opacity: 0; transform: translateY(-10px); }
  to { opacity: 1; transform: translateY(0); }
}
@media (max-width: 480px) {
  .flash-toast-stack { left: 1rem; right: 1rem; max-width: none; }
}

/* Statut d'enregistrement automatique (voir wireAutosave() dans app.js). */
.autosave-status {
  grid-column: 1 / -1;
  font-size: .85rem;
  color: var(--text-muted);
  min-height: 1.2em;
}
.autosave-status.saving { color: var(--text-muted); font-style: italic; }
.autosave-status.ok { color: var(--ok); }
.autosave-status.error { color: var(--danger); font-weight: 600; }

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

.form { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); column-gap: .7rem; row-gap: 1.4rem; align-items: start; }
/* Laisse la place aux désignations flottantes (dates, combos) pour qu'elles ne
   se collent pas au titre de la carte, au texte d'introduction ou à la barre
   d'actions juste au-dessus (dès que le formulaire n'est pas le tout premier
   élément de la carte, quel que soit ce qui le précède). */
.card > * ~ form.form,
.card > * ~ form.form-row { margin-top: 1.4rem; }
.form textarea { grid-column: 1 / -1; min-height: 4.5rem; }
.form .form-row {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  column-gap: .7rem;
  row-gap: 1.4rem;
}
.form label { display: flex; flex-direction: column; gap: .25rem; font-size: .85rem; color: var(--text-muted); }
.form button, .inline-form button { grid-column: auto; justify-self: start; }
.form .btn-save-row { grid-column: 1 / -1; display: flex; align-items: center; gap: 1rem; margin-top: .2rem; }
/* Fiche Achat-Vente (05/10/2026) : suffixe "GNF" accolé aux champs Montant du
   devis / Montant du bon de commande client — le couple input+suffixe reste
   une seule cellule de la grille .form, pour que le "GNF" ne décale pas
   l'alignement des autres champs. */
.montant-field { display: flex; align-items: center; gap: .4rem; }
.montant-field .montant-input { flex: 1; min-width: 0; }
.montant-field .montant-suffix { color: var(--text-muted); font-size: .85rem; white-space: nowrap; }

/* Libellé flottant réutilisable (05/10/2026) : un champ vide affiche son
   placeholder normalement, un champ rempli n'affiche que sa valeur (rien de
   changé pour ces deux cas) — mais au survol ou au focus, son titre apparaît
   brièvement juste au-dessus pour rester identifiable, sans déplacer ni
   masquer la valeur ni changer la taille du champ. Marquage, réutilisable
   pour n'importe quel champ du logiciel :
     <div class="field-float"><input ...><label>Titre du champ</label></div>
   (le <label> peut suivre un <input>, <select> ou <textarea> ; sa position
   dans le HTML n'a pas d'importance, l'affichage est géré ici en CSS). */
.field-float { position: relative; }
.field-float > label {
  position: absolute;
  left: .65rem;
  top: -.5rem;
  font-size: .72rem;
  font-weight: 600;
  color: var(--brand);
  background: var(--panel);
  padding: 0 .35rem;
  line-height: 1;
  border-radius: 3px;
  pointer-events: none;
  opacity: 0;
  transform: translateY(3px);
  transition: opacity .12s ease, transform .12s ease;
  white-space: nowrap;
  max-width: calc(100% - .5rem);
  overflow: hidden;
  text-overflow: ellipsis;
}
.field-float:hover > label,
.field-float:focus-within > label {
  opacity: 1;
  transform: translateY(0);
}

/* Champs date : la désignation reste visible en permanence au-dessus du champ
   (les <input type="date"> natifs n'affichent pas de placeholder), et se colore
   au focus pour confirmer où l'utilisateur se trouve. */
.date-field { position: relative; }
.date-field .date-caption {
  position: absolute;
  top: -1.2rem;
  left: .1rem;
  font-size: .66rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .03em;
  color: var(--text-muted);
  white-space: nowrap;
  pointer-events: none;
}
.date-field:focus-within .date-caption { color: var(--brand); }

/* Champ de recherche (combobox) : un input texte relié à une <datalist>,
   avec un champ caché qui porte la vraie valeur (id) envoyée au serveur. */
.combo { position: relative; }
.combo .combo-caption {
  position: absolute;
  top: -1.2rem;
  left: .1rem;
  font-size: .66rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .03em;
  color: var(--text-muted);
  white-space: nowrap;
  pointer-events: none;
}
.combo:focus-within .combo-caption { color: var(--brand); }
.combo-input.input-error { border-color: var(--danger); }
.supplier-combo-hint, .combo-hint {
  color: var(--danger);
  font-size: .78rem;
  font-weight: 600;
  margin-top: .25rem;
}

input, select, textarea {
  font-family: inherit;
  font-size: .92rem;
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--line);
  padding: .5rem .6rem;
  width: 100%;
}
input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--frost-500);
  outline-offset: 1px;
  border-color: var(--frost-500);
}

button {
  font-family: 'Space Grotesk', sans-serif;
  font-weight: 600;
  font-size: .88rem;
  color: #fff;
  background: var(--steel-700);
  border: none;
  padding: .55rem 1.1rem;
  cursor: pointer;
  transition: background .12s ease;
}
button:hover { background: var(--frost-500); }

.inline-form { display: flex; gap: .4rem; align-items: center; }
.inline-form input { width: auto; }

.btn-link {
  display: inline-block;
  font-family: 'Space Grotesk', sans-serif;
  font-weight: 600;
  font-size: .88rem;
  color: #fff;
  background: var(--steel-700);
  padding: .55rem 1.1rem;
  margin-right: .5rem;
  text-decoration: none;
}
.btn-link:hover { background: var(--frost-500); text-decoration: none; }

/* ---------- Tableaux : registre technique, chiffres alignés ---------- */

table { width: 100%; border-collapse: collapse; font-size: .9rem; }
th {
  text-align: left;
  font-family: 'Space Grotesk', sans-serif;
  font-weight: 700;
  font-size: .78rem;
  color: var(--brand);
  border-bottom: 2px solid var(--steel-700);
  padding: .5rem .6rem;
}
td { padding: .55rem .6rem; border-bottom: 1px solid var(--line); font-variant-numeric: tabular-nums; }
td.amount { color: var(--brand); font-weight: 700; }
tr:nth-child(even) td { background: #F5F8F8; }
tr:hover td { background: var(--frost-100); }

/* Ligne de tableau modifiable au double-clic (Parc climatiseurs, 04/10/2026) :
   curseur "main" + survol un peu plus marqué pour signaler que la ligne est
   cliquable, en plus du survol discret déjà appliqué à tous les tableaux
   ci-dessus. Classe ajoutée uniquement sur les lignes concernées, sans
   toucher aux autres tableaux. */
tr.row-clickable { cursor: pointer; }
tr.row-clickable:hover td { background: var(--frost-100); box-shadow: inset 0 0 0 9999px rgba(43, 147, 184, .05); }

/* ---------- Tableau de bord : indicateurs façon afficheur technique ---------- */

.kpi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  margin-bottom: 1.6rem;
}
.kpi {
  background: var(--panel);
  padding: 1.1rem 1.3rem;
}
.kpi .kpi-label {
  font-family: 'Space Grotesk', sans-serif;
  font-size: .74rem;
  color: var(--text-muted);
  text-transform: none;
  margin-bottom: .3rem;
}
.kpi .kpi-value {
  font-family: 'Sora', sans-serif;
  font-weight: 800;
  font-size: 1.85rem;
  color: var(--brand);
  font-variant-numeric: tabular-nums;
}
.kpi.warn .kpi-value { color: var(--danger); }
.kpi.frost .kpi-value { color: var(--frost-500); }
.kpi.copper .kpi-value { color: var(--copper-500); }

/* ---------- Tableau de bord : design validé (03/10/2026), cartes KPI dédiées ----------
   Classes volontairement distinctes de .kpi/.kpi-grid ci-dessus, qui restent utilisées
   telles quelles par Stock et Comptabilité générale — on ne touche pas à ces pages. */
.dashboard-subline {
  font-family: 'Space Grotesk', sans-serif;
  font-size: .82rem;
  color: var(--text-muted);
  margin: -.5rem 0 1.1rem;
}
.dashboard-kpi-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1rem;
  margin-bottom: 1.6rem;
}
.dashboard-kpi-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 1px 3px rgba(16, 24, 32, .06);
  padding: 1.3rem 1.4rem;
}
.dashboard-kpi-label {
  font-family: 'Space Grotesk', sans-serif;
  font-size: .78rem;
  font-weight: 600;
  color: var(--steel-700);
  margin-bottom: .55rem;
}
.dashboard-kpi-value {
  font-family: 'Sora', sans-serif;
  font-weight: 800;
  font-size: 1.85rem;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}
.dashboard-kpi-value--steel { color: var(--steel-700); }
.dashboard-kpi-value--copper { color: var(--copper-500); }
.dashboard-kpi-value--danger { color: var(--danger); }
.dashboard-kpi-value--ok { color: var(--ok); }
.dashboard-kpi-value--brand { color: var(--brand); }
.dashboard-kpi-value--violet { color: var(--violet-500); }

.badge {
  display: inline-block;
  font-size: .76rem;
  padding: .1rem .5rem;
  border: 1px solid var(--danger);
  color: var(--danger);
}

.days-tag {
  display: inline-block;
  font-size: .82rem;
  font-weight: 600;
  padding: .15rem .55rem;
  white-space: nowrap;
}
.days-tag.overdue { color: var(--danger); background: #FBEAE6; }
.days-tag.today { color: var(--warn); background: #FBF1E1; font-weight: 700; }
.days-tag.upcoming { color: var(--text-muted); }

.nature-tag {
  display: inline-block;
  padding: .18rem .55rem;
  border-radius: 999px;
  font-size: .78rem;
  font-weight: 600;
}
.nature-tag.recette { color: #1F5C45; background: #E7F3EE; }
.nature-tag.encaissement { color: var(--brand); background: #E7EDF5; }
.nature-tag.depense { color: var(--danger); background: #FBEAE6; }

/* Lignes de vente (Ventes) : petit tableau éditable dans le formulaire. */
.form table.line-items { grid-column: 1 / -1; width: 100%; border-collapse: collapse; margin-top: .3rem; }
.line-items th {
  text-align: left;
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .03em;
  color: var(--text-muted);
  padding: 0 .4rem .35rem;
  font-weight: 600;
}
/* Panneau "Articles / prestations" du devis : intitulés de colonnes tels
   qu'écrits (pas de majuscules forcées), fidèle à la référence du
   20/09/2026 — sans toucher au tableau du bon de livraison, resté inchangé. */
.subcard .line-items th {
  text-transform: none;
  font-size: .78rem;
  letter-spacing: normal;
}
.line-items td { padding: .3rem .4rem; vertical-align: top; }
/* Les champs remplissent leur colonne (voir <colgroup> dans devis_form.php)
   plutôt que d'avoir une largeur fixe étroite qui laissait un grand vide
   autour d'eux — c'est ce qui donnait des fenêtres "petites et trop
   espacées" (demande du 20/09/2026). */
/* Désignation sur plusieurs lignes (voir autoGrowDescription() dans app.js). */
.line-items textarea.line-description {
  min-height: 2.3rem;
  height: 2.3rem;
  resize: vertical;
  overflow: hidden;
  line-height: 1.35;
}
.line-items .line-total { white-space: nowrap; font-weight: 600; color: var(--brand); padding-top: .6rem; }
.stock-hint { font-size: .72rem; color: var(--text-muted); margin-top: .2rem; min-height: 1em; }
.btn-remove-line {
  background: var(--brand);
  color: #fff;
  border: 1px solid var(--brand);
  padding: .5rem .7rem;
  font-size: .85rem;
}
.btn-remove-line:hover { background: var(--frost-500); border-color: var(--frost-500); }

/* Bloc "Articles / prestations" du devis : son propre panneau, avec titre +
   texte d'explication et le bouton "+ Ajouter une ligne" aligné à droite du
   titre (reproduit la disposition demandée le 20/09/2026). */
.subcard {
  grid-column: 1 / -1;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 1.2rem 1.4rem;
  margin-top: .4rem;
}
.subcard .card-header-row { margin-bottom: 1.1rem; }
.subcard .card-header-row .btn-primary { grid-column: auto; flex-shrink: 0; }

/* Bloc "Rentabilité de cette affaire" (charges + montant client, 23/09/2026). */
.profit-row { display: flex; flex-wrap: wrap; gap: 1.6rem; margin: .6rem 0; }
.profit-item { min-width: 160px; }
.profit-item .profit-label { color: var(--text-muted); font-size: .82rem; }
.profit-item .profit-value { font-size: 1.15rem; font-weight: 700; }
.profit-value.profit-positive { color: var(--ok); }
.profit-value.profit-negative { color: var(--danger); }
.charge-form-row { display: flex; flex-wrap: wrap; gap: .6rem; align-items: flex-end; margin-top: .8rem; }
.charge-form-row select, .charge-form-row input { min-width: 140px; }
/* Charges de cette affaire (05/10/2026) : "Précision" est le champ principal
   pour décrire la charge (ex. "Dédouanement marchandises au port de
   Kamsar") — il absorbe l'espace restant de la ligne, tout en gardant
   "Type de charge", "Montant" et "Date" côte à côte sur la même ligne. */
.charge-form-row .field-wide { flex: 2 1 280px; }
.charge-form-row .field-wide input,
.charge-form-row .field-wide textarea { width: 100%; }
/* Précision (05/10/2026) : passé en textarea pour les détails longs sur
   plusieurs lignes (voir autoGrowDescription() dans app.js, déjà utilisé
   pour la désignation des articles) — hauteur d'une ligne par défaut, et ne
   s'agrandit que si le texte saisi en a besoin. */
.field-precision-textarea {
  min-height: 2.3rem;
  height: 2.3rem;
  resize: none;
  overflow: hidden;
  line-height: 1.35;
}
/* Charges de cette affaire : une précision sur plusieurs lignes doit rester
   lisible dans le tableau (sans quoi les retours à la ligne saisis seraient
   ignorés par le HTML et tout s'afficherait sur une seule ligne). */
.charge-precision-cell { white-space: pre-line; }

/* Sélecteur "Fiche Stock / Fiche Achat-Vente" (23/09/2026, revu le 23/09/2026
   pour un vrai choix en "fenêtres" cliquables plutôt que de simples boutons
   radio isolés). */
.fiche-type-row { display: flex; flex-wrap: wrap; gap: .9rem; margin: .6rem 0; }
.fiche-type-choice {
  display: flex; align-items: center; gap: .7rem;
  border: 1.5px solid var(--line); border-radius: 6px;
  background: var(--panel); padding: .85rem 1.3rem;
  font-weight: 600; cursor: pointer; flex: 1 1 220px; min-width: 220px;
  transition: border-color .15s, background-color .15s;
}
.fiche-type-choice:hover { border-color: var(--frost-500); }
.fiche-type-choice.active { border-color: var(--brand); background: var(--frost-100); color: var(--brand); }
.fiche-type-choice input[type="radio"] { width: 1.15rem; height: 1.15rem; accent-color: var(--brand); flex-shrink: 0; }
.fiche-type-choice span { line-height: 1.25; }
.fiche-type-hint { margin-top: -.2rem; }

/* Ligne de charge en cours de modification (23/09/2026). */
.row-being-edited { background: var(--frost-100); }
.row-actions { display: flex; gap: .6rem; align-items: center; }

/* Bloc "Catégorie et devise" du bon de commande (achats locaux vs.
   extérieurs, choix de la devise et du taux de change — 22/09/2026). */
.currency-box { padding: 1rem 1.4rem; }
.currency-box > strong { display: block; margin-bottom: .3rem; }
.currency-box .muted { margin: 0 0 .8rem; font-size: .9rem; }
.currency-category-row { display: flex; gap: 1.6rem; flex-wrap: wrap; margin-bottom: .8rem; }
.currency-category-row label { display: flex; align-items: center; gap: .45rem; font-weight: 600; cursor: pointer; }
.currency-fields-row { display: flex; gap: .8rem; flex-wrap: wrap; }
.currency-fields-row select,
.currency-fields-row input { min-width: 220px; }
.currency-fx-hint { margin-top: .5rem; font-size: .85rem; }

/* ---------- Panneau "Articles / prestations" du DEVIS : lignes en grille
   CSS (et non plus un <table>), portées directement de la maquette de
   référence fournie le 20/09/2026 — mêmes proportions de colonnes et même
   hauteur généreuse (76px) sur chaque champ, pour ne plus jamais revenir à
   des champs "petits et trop espacés". Scopé à ".chain-devis" seul : ne
   touche ni le tableau du BL, ni celui de la facture. ---------- */
.sales-items-wrap { grid-column: 1 / -1; }
.sales-items-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  margin-bottom: .8rem;
}
.sales-items-head strong { display: block; font-size: 1rem; color: var(--text); }
.small-btn {
  background: var(--brand);
  color: #fff;
  border: none;
  border-radius: 6px;
  padding: .55rem 1.1rem;
  font-size: .88rem;
  font-weight: 600;
  cursor: pointer;
  flex-shrink: 0;
}
.small-btn:hover { background: var(--frost-500); }

/* En-têtes de colonnes : fond coloré + texte bleu en gras, comme la
   référence (demande du 20/09/2026, point 5). */
.sales-items-table-head {
  display: grid;
  grid-template-columns: minmax(125px,1.05fr) minmax(220px,2.25fr) minmax(75px,.72fr) minmax(100px,.78fr) minmax(130px,1.05fr) minmax(75px,.70fr) minmax(145px,1.15fr) 36px;
  gap: .5rem;
  font-size: .76rem;
  font-weight: 700;
  color: var(--brand);
  background: var(--frost-100);
  padding: .55rem .6rem;
  margin-bottom: .6rem;
  border-radius: 4px;
}

#sale-items-body { display: flex; flex-direction: column; gap: .6rem; }

.sale-row.chain-devis {
  display: grid;
  grid-template-columns: minmax(125px,1.05fr) minmax(220px,2.25fr) minmax(75px,.72fr) minmax(100px,.78fr) minmax(130px,1.05fr) minmax(75px,.70fr) minmax(145px,1.15fr) 36px;
  gap: .5rem;
  align-items: start;
}
/* Chanfreins (coins arrondis) sur les quatre côtés de chaque champ, comme
   la référence — le reste de l'application garde son registre "technique"
   à coins droits, ce changement est scopé aux lignes du devis uniquement
   (demande du 20/09/2026, point 1). */
.sale-row .ref-col { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
.sale-row .ref-col .combo { position: relative; min-height: 76px; }
.sale-row .line-ref { min-height: 76px; border-radius: 6px; }
.sale-row .designation-wrap { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
.sale-row .designation-wrap textarea.line-description {
  width: 100%;
  min-height: 76px;
  height: 76px;
  resize: vertical;
  overflow: auto;
  line-height: 1.35;
  border-radius: 6px;
}
.sale-row input[type="number"] { min-height: 76px; border-radius: 6px; }
.sale-row .unit-select { width: 100%; min-width: 0; min-height: 76px; border-radius: 6px; }
.sale-row .unit-select:disabled { opacity: 1; color: var(--text); background: #F4F6F8; }
.line-total-view {
  min-height: 76px;
  background: #F7F9FB;
  border: 1px solid var(--line);
  border-radius: 6px;
  font-weight: 700;
  color: var(--brand);
  text-align: right;
  padding: .5rem .6rem;
}
/* Bouton de suppression de ligne : nettement plus grand et plus visible
   qu'avant, comme la référence (demande du 20/09/2026, point 4). */
.sale-row .remove-row {
  align-self: start;
  min-height: 76px;
  width: 100%;
  border-radius: 6px;
  font-size: 1.35rem;
  line-height: 1;
  padding: 0;
}

@media (max-width: 1100px) {
  .sales-items-table-head, .sale-row.chain-devis {
    grid-template-columns: 1fr 1.9fr .65fr .7fr .95fr .6fr 1fr 36px;
  }
}
@media (max-width: 900px) {
  .sales-items-table-head { display: none; }
  .sale-row.chain-devis { grid-template-columns: 1fr 1fr; }
  .sale-row .unit-select { min-height: 52px; }
}

/* ---------- Même traitement pour BON DE LIVRAISON et FACTURE (dont la
   FACTURE DE SERVICE, qui réutilise facture_form.php) : lignes en grille,
   champs à 76px, coins arrondis, en-tête colorée — demande du 21/09/2026
   ("applique la même chose"). Pas de bouton de suppression de ligne ici :
   contrairement au devis, les lignes d'un BL/d'une facture proviennent
   toujours d'un document source et ne se suppriment pas une à une. Classes
   ".head-bl"/".head-facture" et ".chain-bl"/".chain-facture" scopées à part
   pour ne rien changer au devis, déjà validé. ---------- */
.sales-items-list { display: flex; flex-direction: column; gap: .6rem; }

/* Valeur affichée en lecture seule (référence, désignation, unité, qté,
   PU...) : même registre visuel que les champs saisissables (76px, coins
   arrondis) mais fond neutre, pour bien les distinguer des champs actifs. */
.field-view {
  min-height: 76px;
  background: #F7F9FB;
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: .5rem .6rem;
  display: flex;
  align-items: center;
  color: var(--text);
  font-size: .88rem;
  line-height: 1.35;
  min-width: 0;
  overflow-wrap: anywhere;
}
.field-view.designation-view { align-items: flex-start; white-space: pre-line; }
.field-view.amount-view { justify-content: flex-end; font-variant-numeric: tabular-nums; }

/* Colonne "Qté à livrer" du BL : champ de saisie + indication du reste,
   empilés comme la colonne référence du devis. */
.sale-row .qty-col { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }

.sales-items-table-head.head-bl,
.sale-row.chain-bl {
  grid-template-columns: minmax(90px,.85fr) minmax(220px,2.2fr) minmax(80px,.65fr) minmax(90px,.75fr) minmax(140px,1.15fr) minmax(120px,1fr) minmax(140px,1.15fr);
}
/* Variante à 8 colonnes : à partir du 2e BL d'un même devis, une colonne
   "Reste avant" s'intercale entre "Commandé" et "Qté à livrer" — logique de
   chaîne de livraisons partielles décrite dans BL_CLAUDE.pdf (21/09/2026). */
.sales-items-table-head.head-bl-chained,
.sale-row.chain-bl-chained {
  grid-template-columns: minmax(90px,.85fr) minmax(220px,2.2fr) minmax(80px,.65fr) minmax(80px,.75fr) minmax(80px,.75fr) minmax(140px,1.15fr) minmax(120px,1fr) minmax(140px,1.15fr);
}
.sales-items-table-head.head-facture,
.sale-row.chain-facture {
  grid-template-columns: minmax(90px,.85fr) minmax(220px,2.2fr) minmax(80px,.65fr) minmax(80px,.65fr) minmax(120px,1fr) minmax(80px,.65fr) minmax(140px,1.15fr);
}
/* Facture de service sans code (Électroménager...) : la colonne « Code » est
   supprimée (pas seulement vidée) — mêmes colonnes, sans la première. */
.sales-items-table-head.head-facture.no-code,
.sale-row.chain-facture.no-code {
  grid-template-columns: minmax(220px,2.2fr) minmax(80px,.65fr) minmax(80px,.65fr) minmax(120px,1fr) minmax(80px,.65fr) minmax(140px,1.15fr);
}
/* Onglet Achats — Demande d'achat : 7 colonnes (Réf/Désignation/Quantité/
   Unité/Prix estimé/Total estimé/✕), sans remise (une demande n'engage
   personne sur un prix ni une remise, contrairement au devis/bon de
   commande) mais avec bouton de suppression de ligne comme le devis. */
.sales-items-table-head.head-demande,
.sale-row.chain-demande {
  grid-template-columns: minmax(125px,1.05fr) minmax(220px,2.25fr) minmax(75px,.72fr) minmax(100px,.78fr) minmax(130px,1.05fr) minmax(145px,1.15fr) 36px;
}

.sales-items-table-head.head-bl,
.sales-items-table-head.head-bl-chained,
.sales-items-table-head.head-facture,
.sales-items-table-head.head-demande {
  display: grid;
  gap: .5rem;
  font-size: .76rem;
  font-weight: 700;
  color: var(--brand);
  background: var(--frost-100);
  padding: .55rem .6rem;
  margin-bottom: .6rem;
  border-radius: 4px;
}
.sale-row.chain-bl,
.sale-row.chain-bl-chained,
.sale-row.chain-facture,
.sale-row.chain-demande {
  display: grid;
  gap: .5rem;
  align-items: start;
  margin-bottom: .6rem;
}
/* Valeur figée "Reste avant" : même registre que .field-view mais fond
   légèrement teinté pour signaler qu'il s'agit d'un repère de chaîne, pas
   d'une simple valeur du devis. */
.field-view.chain-view { background: #EEF4FA; border-color: #D6E1EC; color: var(--brand); font-weight: 600; justify-content: flex-end; }

@media (max-width: 1100px) {
  .sales-items-table-head.head-bl, .sale-row.chain-bl {
    grid-template-columns: .9fr 1.9fr .6fr .65fr 1fr .85fr 1fr;
  }
  .sales-items-table-head.head-bl-chained, .sale-row.chain-bl-chained {
    grid-template-columns: .9fr 1.9fr .55fr .6fr .6fr 1fr .85fr 1fr;
  }
  .sales-items-table-head.head-facture, .sale-row.chain-facture {
    grid-template-columns: .9fr 1.9fr .6fr .6fr .85fr .6fr 1fr;
  }
  .sales-items-table-head.head-facture.no-code, .sale-row.chain-facture.no-code {
    grid-template-columns: 1.9fr .6fr .6fr .85fr .6fr 1fr;
  }
  .sales-items-table-head.head-demande, .sale-row.chain-demande {
    grid-template-columns: 1fr 1.9fr .65fr .7fr .95fr 1fr 36px;
  }
}
@media (max-width: 900px) {
  .sales-items-table-head.head-bl, .sales-items-table-head.head-bl-chained, .sales-items-table-head.head-facture, .sales-items-table-head.head-demande { display: none; }
  .sale-row.chain-bl, .sale-row.chain-bl-chained, .sale-row.chain-facture, .sale-row.chain-facture.no-code { grid-template-columns: 1fr 1fr; }
  .sale-row.chain-demande { grid-template-columns: 1fr 1fr; }
}

/* Ligne de rappel "Livraisons précédentes" (chaîne de BL) et récapitulatif
   "Quantités restantes non encore livrées" en bas du BL — voir
   BL_CLAUDE.pdf (21/09/2026). */
.bl-chain-note { grid-column: 1 / -1; }
.bl-chain-note strong { color: var(--brand); }
.bl-remaining-wrap { grid-column: 1 / -1; margin-top: .6rem; }
.bl-remaining-title { margin: 0 0 .5rem; font-size: .95rem; color: var(--brand); }
.bl-remaining-table {
  width: 100%;
  border-collapse: collapse;
  background: #EEF4FA;
  border: 1px solid #D6E1EC;
  border-radius: 6px;
  overflow: hidden;
}
.bl-remaining-table th, .bl-remaining-table td {
  padding: .5rem .75rem;
  border-bottom: 1px solid #D6E1EC;
  text-align: left;
  font-size: .92rem;
}
.bl-remaining-table th { color: var(--brand); font-weight: 700; }
.bl-remaining-table tr:last-child td { border-bottom: none; }
.bl-closed-note {
  grid-column: 1 / -1;
  margin-top: .6rem;
  padding: .7rem 1rem;
  background: #E7F3EE;
  border: 1px solid #CFE8DC;
  border-radius: 6px;
  color: #1F5C45;
  font-weight: 600;
}

/* Ligne TVA du devis : plus de mot "TVA" au-dessus (juste la liste
   déroulante, demande du 20/09/2026, 4e vague) et un espacement resserré
   avec le tableau au-dessus et le récapitulatif en dessous. Ce champ est en
   flux normal (pas de légende en position absolue comme .date-caption), il
   ne peut donc jamais remonter par-dessus "En stock : ...", quelle que soit
   sa hauteur — même prudence que la correction précédente. */
.devis-vat-row { grid-column: 1 / -1; margin: .5rem 0 .6rem; }
.devis-vat-select { max-width: 220px; }

/* Récapitulatif horizontal (devis) : une seule ligne, au lieu de la boîte
   verticale utilisée sur la facture (voir .calc-box). Fond coloré et
   montants en gras, comme la référence (demande du 20/09/2026, point 3). */
.calc-bar {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .35rem 1.6rem;
  background: var(--frost-100);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: .8rem 1.1rem;
  font-size: .88rem;
}
.calc-bar span { color: var(--brand); }
.calc-bar strong { color: var(--brand); font-weight: 700; margin-left: .3rem; }
.calc-bar .calc-bar-total strong { color: var(--brand); font-size: 1.1rem; }
.form .btn-secondary, .btn-secondary {
  grid-column: 1 / -1;
  justify-self: start;
  background: var(--panel);
  color: var(--brand);
  border: 1px solid var(--brand);
}
.btn-secondary:hover { background: #E7EDF5; }
.sale-total {
  grid-column: 1 / -1;
  text-align: right;
  font-size: .95rem;
  color: var(--text-muted);
  padding: .2rem .4rem;
}
.sale-total strong { color: var(--brand); font-size: 1.1rem; }

.checkbox-line {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: .5rem;
  font-size: .9rem;
  color: var(--text);
}
.checkbox-line input[type="checkbox"] { width: auto; }
.stock-cost-hint { grid-column: 1 / -1; font-size: .78rem; margin: 0; }

.phone-link { color: var(--brand); font-weight: 600; white-space: nowrap; }

body.login {
  background: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
}

.loginbox {
  background: var(--panel);
  padding: 2.4rem 2.2rem 2rem;
  width: 320px;
  display: flex;
  flex-direction: column;
  gap: .8rem;
  position: relative;
  border-top: 4px solid var(--frost-500);
}
.loginbox p { color: var(--text-muted); font-size: .85rem; margin: -.4rem 0 .6rem; }
.login-logo { max-width: 200px; width: 100%; height: auto; align-self: center; }

/* ---------- Adaptation mobile / tablette ---------- */

@media (max-width: 860px) {
  /* Sur mobile/tablette, le menu redevient un bandeau horizontal (logo +
     modules) et c'est le contenu qui défile normalement en dessous — le
     défilement indépendant à deux colonnes n'existe qu'au-dessus de 861px
     (voir plus haut). Le bandeau reste VISIBLE À L'ÉCRAN pendant le
     défilement (demande du 28/09/2026) via "position: fixed" — "sticky" seul
     ne s'est pas comporté de façon fiable sur le téléphone de test. Comme
     "fixed" retire le bandeau du flux normal de la page, app.js réserve en
     JS la place correspondante en haut de .container (sa hauteur exacte
     variant selon l'écran et la police, voir adjustMobileHeaderSpacing()). */
  .app-shell { flex-direction: column; }

  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    width: 100%;
    flex-direction: row;
    align-items: center;
    gap: 0;
    padding: 0;
    z-index: 10;
  }
  .sidebar-brand { border-bottom: none; border-right: 3px solid var(--steel-700); padding: .5rem .8rem; flex-shrink: 0; }
  .sidebar-brand .brand-logo { max-width: 110px; }
  .sidebar nav {
    flex-direction: row;
    gap: 2px;
    padding: .5rem .8rem;
    overflow-x: auto;
  }
  .sidebar nav a {
    white-space: nowrap;
    border-left: none;
    border-bottom: 3px solid transparent;
    padding: .5rem .6rem;
  }
  .sidebar nav a:hover { border-left-color: transparent; border-bottom-color: var(--frost-500); }
  .sidebar nav a.logout { margin-top: 0; margin-left: auto; }

  .container { padding: 1.2rem 1rem; max-width: 100%; }

  .form { grid-template-columns: 1fr 1fr; }
  .card { padding: 1.1rem 1.2rem; }

  .card-header-row { flex-direction: column; align-items: flex-start; }
  .card-header-actions { width: 100%; flex-wrap: wrap; }
  .card-header-actions .inline-form { flex: 1; }
  .card-header-actions .inline-form input[type="search"] { flex: 1; width: auto; }

  table { display: block; overflow-x: auto; white-space: nowrap; }

  .dashboard-kpi-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 520px) {
  html, body { font-size: 14.5px; }
  .form { grid-template-columns: 1fr; }
  .kpi-grid { grid-template-columns: repeat(2, 1fr); }
  .dashboard-kpi-grid { grid-template-columns: 1fr; }
  .loginbox { width: 88vw; padding: 2rem 1.4rem 1.6rem; }
  .sidebar nav a { font-size: .85rem; }
  .sidebar-brand .brand-logo { max-width: 85px; }
}

/* ---------- Chaîne documentaire Ventes (Devis -> BL -> Facture) ---------- */

.doc-title-row { display: flex; align-items: center; gap: .6rem; margin-bottom: .2rem; }
.doc-title-row h3 { margin: 0; }

.badge-draft { border-color: var(--text-muted); color: var(--text-muted); }
.badge-valid { border-color: var(--ok); color: var(--ok); }
.badge-cancelled { border-color: var(--danger); color: var(--danger); background: #FBEAE6; }

/* Statuts prospect/client (fiche client — suivi des prospects, 23/09/2026). */
.badge-prospect { border-color: var(--text-muted); color: var(--text-muted); }
.badge-interesse { border-color: var(--copper-500); color: var(--copper-500); background: var(--copper-100); }

.status-filter-tabs { display: flex; flex-wrap: wrap; gap: .4rem; margin: .8rem 0 1rem; }
.status-filter-tab {
  display: inline-block;
  padding: .3rem .7rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: .85rem;
  color: var(--text-muted);
  text-decoration: none;
  background: var(--panel);
}
.status-filter-tab:hover { border-color: var(--brand); color: var(--brand); }
.status-filter-tab.active { border-color: var(--brand); background: var(--brand); color: #fff; font-weight: 600; }

.doc-type-tag {
  display: inline-block;
  padding: .12rem .5rem;
  border-radius: 999px;
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .02em;
}
.doc-type-devis { color: var(--frost-500); background: var(--frost-100); }
.doc-type-bl { color: var(--copper-500); background: var(--copper-100); }
.doc-type-facture { color: var(--brand); background: #E7EDF5; }

.doc-actions { white-space: nowrap; }
.doc-actions a { margin-right: .6rem; font-size: .85rem; }

/* "Documents commerciaux" (Ventes) : le BL et la Facture d'un Devis restent
   repliés sous celui-ci par défaut (demande du 24/09/2026). */
.doc-type-cell { white-space: nowrap; }
.chain-toggle, .chain-toggle-sample {
  background: none;
  border: 1px solid var(--line);
  border-radius: 4px;
  cursor: pointer;
  font-size: .7rem;
  line-height: 1;
  color: var(--brand);
  padding: .15rem .35rem;
  margin-right: .4rem;
}
.chain-toggle:hover { border-color: var(--brand); background: var(--frost-100); }
.chain-toggle-sample { cursor: default; padding: .05rem .3rem; }
tr.chain-row td:first-child { padding-left: 1.7rem; }
tr.chain-row { background: #FAFBFC; }

.workflow-bar {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  align-items: center;
  background: var(--frost-100);
  border: 1px solid var(--line);
  padding: .6rem .8rem;
  margin: .6rem 0 1rem;
}
.workflow-bar .inline-form { display: inline-flex; }

.btn-primary { background: var(--brand); color: #fff; border: 1px solid var(--brand); }
.btn-primary:hover { background: var(--frost-500); border-color: var(--frost-500); }
/* Fiche Achat-Vente (05/10/2026) : "Enregistrer les références",
   "Enregistrer les montants" et "Dissocier ce bon de commande" reprennent
   exactement le style de "+ Ajouter la charge" (.btn-primary ci-dessus) —
   fond bleu foncé, texte blanc, même bordure, même rayon, même effet au
   survol — sans changer leur position (grid-column/justify-self, comme
   .btn-secondary) ni leur fonctionnement. */
.btn-av-primary {
  grid-column: 1 / -1;
  justify-self: start;
  background: var(--brand);
  color: #fff;
  border: 1px solid var(--brand);
}
.btn-av-primary:hover { background: var(--frost-500); border-color: var(--frost-500); }
.btn-danger { background: transparent; color: var(--danger); border: 1px solid var(--danger); }
.btn-danger:hover { background: #FBEAE6; }
a.btn-primary, a.btn-danger, a.btn-secondary {
  display: inline-block;
  padding: .45rem .8rem;
  text-decoration: none;
  font-size: .9rem;
}

/* Lignes de devis : les largeurs sont désormais portées par le <colgroup>
   de la table (voir devis_form.php) — les champs remplissent leur colonne. */

/* Lignes de BL : reprise figée du devis, seule la quantité livrée se saisit. */
.bl-line-row td { padding: .35rem .4rem; }
.bl-line-quantity { width: 6rem; }
.bl-line-total { white-space: nowrap; font-weight: 600; color: var(--brand); }

.calc-box {
  grid-column: 1 / -1;
  margin-left: auto;
  width: min(320px, 100%);
  border: 1px solid var(--line);
  padding: .6rem .8rem;
}
.calc-box > div { display: flex; justify-content: space-between; padding: .18rem 0; font-size: .88rem; }
.calc-box .calc-total { border-top: 1px solid var(--line); margin-top: .25rem; padding-top: .4rem; font-size: 1rem; }
.calc-box .calc-total strong { color: var(--brand); }

/* ---------- Recherche de référence article (liste + "Créer") ---------- */
/* Remplace le <datalist> natif : liste déroulante maison filtrée par préfixe
   de référence, avec "+ Créer" toujours en dernière ligne (voir onRefInput()/
   onRefFocus() dans app.js). */

.ref-suggestions {
  position: absolute;
  left: 0; right: 0; top: 100%;
  z-index: 20;
  background: var(--panel);
  border: 1px solid var(--line);
  box-shadow: 0 6px 16px rgba(20, 30, 45, .14);
  max-height: 15rem;
  overflow-y: auto;
}
.ref-suggestion {
  padding: .45rem .6rem;
  cursor: pointer;
  border-bottom: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: .1rem;
}
.ref-suggestion:last-child { border-bottom: none; }
.ref-suggestion:hover, .ref-suggestion.active { background: var(--bg, #F3F6F9); }
.ref-suggestion strong { font-size: .85rem; }
.ref-suggestion span { font-size: .74rem; color: var(--text-muted); }
.ref-suggestion-create {
  color: var(--frost-500);
  font-weight: 600;
  flex-direction: row;
  align-items: center;
  gap: .3rem;
}
.ref-suggestion-create:hover { background: #EAF2FA; color: var(--brand); }

/* Règles génériques (23/09/2026 puis généralisées le 25/09/2026 : d'abord
   scopées aux seuls #article-modal/#facture-modal, désormais tout élément
   <dialog> en profite -- ex. les fenêtres de détail "Recette du mois" /
   "Dépense du mois" / "Charges communes du mois" de la Comptabilité générale). */
dialog {
  border: none;
  border-radius: 4px;
  padding: 1.4rem 1.6rem;
  width: min(520px, 92vw);
  box-shadow: 0 12px 40px rgba(0,0,0,.25);
}
dialog.article-modal-wide { width: min(680px, 94vw); }
dialog::backdrop { background: rgba(23,54,93,.45); }
dialog h3 { margin-top: 0; }
dialog table { width: 100%; margin: .8rem 0; }
dialog th, dialog td { padding: .4rem .6rem; text-align: left; }
/* ---------- Ventes : fenêtre « Annuler » / « Dupliquer » (04/10/2026,
   refonte visuelle du 04/10/2026) ----------
   Remplace les boutons "Dévalider" du module Ventes (Devis/BL/Facture) : un
   seul bouton "Annuler", qui ouvre cette fenêtre quand il y a un choix à
   faire (document validé). UN SEUL composant visuel (.confirm-modal),
   identique pour Devis/BL/Facture — seuls titre, message, description et
   l'action backend du <form> changent d'une vue à l'autre. Remplace la
   toute première version (boîte cassée : largeur dépendant du contenu,
   zones vides, texte débordant, scrollbar horizontale). */
.alert.info { background: var(--frost-100); border-color: var(--frost-500); color: var(--steel-700); }

dialog.confirm-modal {
  /* Écrase le <dialog> générique ci-dessus : largeur FIXE indépendante du
     contenu (point 2 de la demande) -- jamais une largeur "au contenu". */
  width: min(600px, calc(100% - 24px));
  max-width: 600px;
  padding: 0;
  border-radius: 8px;
  overflow: hidden; /* coins arrondis nets sur le bandeau d'en-tête plein cadre */
  box-sizing: border-box;
}
.confirm-modal, .confirm-modal *, .confirm-modal *::before, .confirm-modal *::after {
  box-sizing: border-box;
}
.confirm-modal form {
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 40px); /* garde-fou écran très petit -- jamais de hauteur fixe par ailleurs */
  overflow: hidden;
}

.confirm-modal-header {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .6rem;
  background: var(--brand);
  color: #fff;
  padding: .8rem 1.1rem;
}
.confirm-modal-header h3 {
  margin: 0;
  font-size: 1.02rem;
  font-weight: 700;
  color: #fff;
}
.confirm-modal-close {
  flex-shrink: 0;
  background: none;
  border: none;
  color: #fff;
  font-size: 1.3rem;
  line-height: 1;
  padding: .15rem .3rem;
  cursor: pointer;
  border-radius: 4px;
}
.confirm-modal-close:hover { background: rgba(255,255,255,.15); }

.confirm-modal-body {
  flex: 1 1 auto;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 1rem 1.1rem;
}

.confirm-modal-warning {
  display: flex;
  align-items: flex-start;
  gap: .6rem;
  margin: 0 0 1rem;
}
.confirm-modal-warning-icon {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: #E8960C;
  color: #fff;
  font-weight: 700;
  font-size: .78rem;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: .1rem;
}
.confirm-modal-warning p {
  margin: 0;
  color: var(--steel-700);
  font-size: .9rem;
  line-height: 1.45;
}

.cancel-option {
  display: flex;
  align-items: flex-start;
  gap: .7rem;
  width: 100%;
  border: 1px solid #CFE3F5;
  border-radius: 6px;
  background: #EEF6FC;
  padding: .75rem .85rem;
  margin-bottom: .7rem;
  cursor: pointer;
  overflow: hidden;
}
.cancel-option:last-child { margin-bottom: 0; }
.cancel-option input[type="radio"] {
  /* Neutralise la règle générique "input, select, textarea { width:100%;
     padding:...; border:...; background:... }" du début de ce fichier, qui
     sinon transforme ce radio en énorme pavé bordé (bug constaté le
     04/10/2026 : immense zone bleue vide, texte et icône invisibles). */
  width: 16px;
  height: 16px;
  min-width: 16px;
  padding: 0;
  margin: .2rem 0 0;
  border: 0;
  background: none;
  flex-shrink: 0;
  accent-color: var(--brand);
}
.cancel-option--danger input[type="radio"] { accent-color: var(--danger); }
.cancel-option-icon {
  flex-shrink: 0;
  font-size: 1.05rem;
  line-height: 1.3;
  color: var(--brand);
  margin-top: .05rem;
}
.cancel-option-body {
  display: flex;
  flex-direction: column;
  gap: .3rem;
  flex: 1;
  min-width: 0;
}
.cancel-option-body strong { font-size: .95rem; font-weight: 700; color: var(--brand); }
.cancel-option-body span {
  font-size: .84rem;
  color: var(--text-muted);
  line-height: 1.45;
  white-space: normal;
  overflow-wrap: break-word;
}
.cancel-option-body .cancel-option-ref { color: var(--steel-700); }
.cancel-option-body .cancel-option-ref strong { color: var(--steel-700); font-size: .84rem; }
.cancel-option:has(input:checked) { border-color: var(--brand); background: #DCEEFB; }

.cancel-option--danger { border-color: #F3C9BD; background: #FCEEEA; }
.cancel-option--danger .cancel-option-icon { color: var(--danger); }
.cancel-option--danger .cancel-option-body strong { color: var(--danger); }
.cancel-option--danger:has(input:checked) { border-color: var(--danger); background: #F8DCD4; }

.confirm-modal-footer {
  flex-shrink: 0;
  display: flex;
  justify-content: flex-end;
  gap: .6rem;
  padding: .8rem 1.1rem;
  border-top: 1px solid var(--line);
}
.confirm-modal-footer .btn-secondary,
.confirm-modal-footer .btn-primary {
  padding: .45rem .9rem;
}
@media (max-width: 480px) {
  .confirm-modal-header, .confirm-modal-body, .confirm-modal-footer { padding-left: .85rem; padding-right: .85rem; }
}

.am-name-textarea {
  font-family: inherit;
  font-size: inherit;
  resize: vertical;
  min-height: 6rem;
  line-height: 1.4;
  padding: .55rem .7rem;
}

/* ---------- Comptabilité générale : classement des modules, barres, charges communes ---------- */
.two-col-cards {
  display: grid;
  grid-template-columns: 1.3fr 1fr;
  gap: 1.2rem;
  align-items: start;
}
@media (max-width: 900px) {
  .two-col-cards { grid-template-columns: 1fr; }
}
/* Recette du mois / Dépense du mois / Charges communes du mois (demande du
   25/09/2026) : les trois côte à côte, même hauteur d'aperçu (previewLimit
   réduit à 3 côté contrôleur), pour qu'elles s'affichent toutes les trois en
   même temps, parfaitement alignées. */
.three-col-cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.2rem;
  align-items: stretch;
}
.three-col-cards > .card {
  display: flex;
  flex-direction: column;
  height: 100%;
}
/* La ligne "Voir le détail" est poussée en bas de chaque carte (margin-top:
   auto) pour que les 3 cartes s'alignent en haut ET en bas quel que soit le
   nombre de lignes affichées (demande du 25/09/2026, point 3). */
.three-col-cards .card-footer-link {
  margin-top: auto;
  padding-top: .6rem;
}
@media (max-width: 1100px) {
  .three-col-cards { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 700px) {
  .three-col-cards { grid-template-columns: 1fr; }
}
/* Situation de paiement / Date d'échéance / Montant payé (facture) : 3
   champs simples, alignés en ligne (demande du 25/09/2026, point 1).
   Correction du 02/10/2026 : ce bloc est un enfant direct de <form
   class="form"> (grille CSS à colonnes ~200px) ; sans grid-column: 1 / -1
   il restait coincé dans UNE seule colonne de la grille, trop étroite
   pour les 3 champs, d'où l'empilement vertical malgré le flex-wrap.
   Même correctif que .form textarea et .form .btn-save-row. */
.payment-status-row {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
  margin: 1rem 0;
}
.payment-status-field {
  display: flex;
  flex-direction: column;
  gap: .3rem;
  flex: 1 1 200px;
}
.module-bar-row {
  display: flex;
  align-items: center;
  gap: .8rem;
  padding: .5rem 0;
  border-bottom: 1px solid var(--line);
}
.module-bar-row:last-child { border-bottom: none; }
.module-bar-label { width: 160px; flex-shrink: 0; font-size: .85rem; }
.module-bar-track {
  flex: 1;
  position: relative;
  height: 16px;
  background: #EEF2F1;
  border-radius: 3px;
  overflow: hidden;
}
.module-bar-ca { position: absolute; top: 0; left: 0; height: 100%; background: #CFE3DB; border-radius: 3px; }
.module-bar-net { position: absolute; top: 0; left: 0; height: 100%; background: var(--brand); border-radius: 3px; }
.module-bar-value { width: 120px; text-align: right; font-weight: 700; font-variant-numeric: tabular-nums; flex-shrink: 0; }

/* ---------- Comptabilité d'un module : sous-nav vers les 6 autres ---------- */
.module-nav {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem .6rem;
  margin-bottom: 1.2rem;
}
.module-nav a {
  padding: .3rem .7rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: .82rem;
  color: var(--text-muted);
  text-decoration: none;
}
.module-nav a:hover { border-color: var(--brand); color: var(--brand); }
.module-nav a.active { background: var(--brand); border-color: var(--brand); color: #fff; font-weight: 600; }

/* ---------- Comptabilité générale (v2) : sous-libellé KPI, graphiques en barres verticales ---------- */
.kpi .kpi-sub {
  margin-top: .3rem;
  font-size: .74rem;
  color: var(--text-muted);
  font-weight: 600;
}
.vbar-chart {
  display: flex;
  align-items: flex-end;
  gap: .9rem;
  height: 190px;
  padding: 0 .3rem .6rem;
  border-bottom: 1px solid var(--line);
  overflow-x: auto;
}
.vbar-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  flex: 1;
  min-width: 64px;
  height: 100%;
}
.vbar-value { font-size: .72rem; font-weight: 700; margin-bottom: .3rem; white-space: nowrap; }
.vbar { width: 60%; min-width: 28px; border-radius: 3px 3px 0 0; transition: height .2s; }
.vbar-label { font-size: .7rem; color: var(--text-muted); margin-top: .5rem; text-align: center; line-height: 1.15; }

/* ---------- Filtre Bureau/Atelier (composant central, 02/10/2026) ---------- */
/* Un seul style, partagé par tous les modules concernés par le */
/* cloisonnement multi-sites — voir App\Auth\OrgFilterBar. Jamais sur le */
/* module Administration (il reste global, pas de filtre à y poser). */

.org-filter-bar {
  /* Bande repliée sur son contenu (demande du 04/10/2026) : "inline-flex"
     au lieu de "flex" pour que le fond bleu clair n'entoure QUE les deux
     filtres, au lieu de s'étirer jusqu'à l'autre bout de la page comme le
     ferait un bloc 100% de largeur. "max-width: 100%" + flex-wrap évitent
     tout débordement horizontal sur petit écran (voir media query plus
     bas, inchangée). Composant central (App\Auth\OrgFilterBar) : ce style
     s'applique donc automatiquement, et de façon identique, à TOUS les
     modules qui affichent le filtre Bureau/Atelier. */
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  max-width: 100%;
  /* Espacement propre ENTRE les deux groupes (Bureau / Atelier) — demande
     du 03/10/2026. Chaque groupe garde son libellé collé à sa liste (voir
     .org-filter-group ci-dessous) : ce gap ne les sépare jamais. */
  gap: 1.1rem;
  background: var(--frost-100);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: .6rem .9rem;
  margin: 0 0 1.2rem;
  font-size: .85rem;
}
/* Un groupe = un libellé + sa liste, toujours côte à côte (jamais le
   libellé au-dessus) : c'est .org-filter-bar qui retourne à la ligne ENTRE
   deux groupes sur petit écran, jamais l'intérieur d'un groupe. */
.org-filter-group {
  display: flex;
  align-items: center;
  gap: .4rem;
  flex: 0 0 auto;
}
.org-filter-caption,
.org-filter-label {
  font-weight: 600;
  color: var(--steel-700);
  white-space: nowrap;
}
.org-filter-select {
  /* Longueur horizontale des 2 fenêtres SEULEMENT (demande du 03/10/2026) :
     les 2 libellés + leurs listes doivent occuper environ la moitié de la
     bande, sans jamais l'étirer jusqu'au bout. flex-basis en % (plutôt
     qu'un px fixe) pour que ça reste vrai quelle que soit la largeur de
     l'écran ; min/max-width évitent les cas extrêmes (fenêtre de
     navigateur très étroite ou très large). Rien d'autre sur cette ligne
     n'a changé : même bande (.org-filter-bar), même hauteur, mêmes
     couleurs/bordures (héritées du style <select> global).
  */
  flex: 0 1 22%;
  width: auto;
  min-width: 200px;
  max-width: 360px;
}
.org-filter-bar--readonly { background: var(--panel); }
.org-filter-badge {
  display: inline-block;
  padding: .25rem .65rem;
  border: 1px solid var(--brand);
  border-radius: 999px;
  color: var(--brand);
  font-weight: 600;
  font-size: .82rem;
  background: var(--frost-100);
}
@media (max-width: 700px) {
  /* Sur petit écran SEULEMENT (demande du 03/10/2026) : les deux groupes
     peuvent passer sur deux lignes — chaque groupe reste néanmoins toujours
     sur sa propre ligne, libellé et liste toujours côte à côte. */
  .org-filter-bar { gap: .5rem; width: 100%; }
  .org-filter-group { flex: 1 1 100%; }
  .org-filter-select { flex: 1 1 auto; min-width: 0; width: 100%; }
}

/* Titre de champ au survol (05/10/2026) — rendu identique à .field-float > label
   (page « Ajouter un client », Fiche Achat-Vente) : le titre se pose sur le bord
   supérieur du champ au passage de la souris. Voir le bloc « Titre des champs au
   survol » dans app.js. Un seul élément flottant, hors flux : il ne déplace ni ne
   redimensionne jamais aucun champ ni aucune ligne du formulaire. */
.field-tooltip {
  position: fixed;
  z-index: 100000;
  pointer-events: none;
  font-size: .72rem;
  font-weight: 600;
  color: var(--brand);
  background: var(--panel);
  padding: 0 .35rem;
  line-height: 1;
  border-radius: 3px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  opacity: 0;
  transform: translateY(3px);
  transition: opacity .12s ease, transform .12s ease;
}
.field-tooltip.is-visible { opacity: 1; transform: translateY(0); }

/* Titres qui restaient affichés en permanence au-dessus des champs (dates,
   listes, recherches) : plus visibles. Leur texte reste dans la page et
   fournit le titre affiché au survol (voir app.js). */
.date-caption,
.combo-caption { display: none; }

/* Vue consolidée du stock par référence (06/10/2026) : ligne de total. */
.row-total td { background: var(--panel-alt, #eef3f7); }

/* Ligne retrouvée après une action (conservation de la position, 06/10/2026). */
@keyframes rowFlash { 0%, 55% { background-color: rgba(255, 213, 79, .55); } 100% { background-color: transparent; } }
tr.row-flash > td, tr.row-flash > th { animation: rowFlash 3s ease-out; }

/* Inventaire : le tableau des articles occupe toute la largeur de la carte
   « Nouveau comptage » (il tenait auparavant dans une seule colonne de la grille .form). */
.form .inventaire-table-wrap { grid-column: 1 / -1; width: 100%; min-width: 0; }
.form .inventaire-table-wrap table { width: 100%; }
.form .inventaire-table-wrap td, .form .inventaire-table-wrap th { word-break: normal; overflow-wrap: normal; }
.form .inventaire-table-wrap td:nth-child(1), .form .inventaire-table-wrap th:nth-child(1) { white-space: nowrap; }
.form .inventaire-table-wrap input[type=number] { min-width: 7rem; }
.form .inventaire-table-wrap input[type=search] { max-width: 320px; }

/* Lien présenté comme un bouton d'action (ex. « Inventaire » en haut du Stock) :
   reprend la typographie et les dimensions des <button> bleus (.btn-primary). */
a.btn-as-button {
  display: inline-block;
  font-family: 'Space Grotesk', sans-serif;
  font-weight: 600;
  font-size: .88rem;
  line-height: normal;
  padding: .55rem 1.1rem;
  text-decoration: none;
  cursor: pointer;
  transition: background .12s ease;
}
a.btn-as-button:hover { text-decoration: none; }
