/* Module d'import fournisseurs - feuille de style.
   Surface de type outil metier : une seule famille typographique, palette
   restreinte (neutres + un accent), densite assumee sur le tableau de controle.
   L'ecran est utilise en bureau eclaire : le theme clair est la reference,
   le theme sombre suit la preference du systeme. */

:root {
  /* « light dark » laisse le navigateur suivre le systeme : c'est le
     comportement voulu quand aucun theme n'est choisi. Les deux selecteurs
     plus bas le remplacent des qu'un choix est fait. */
  color-scheme: light dark;

  --ground: #f4f5f7;
  --surface: #ffffff;
  --surface-2: #fafbfc;
  --panneau: #eef0f3;
  --bord: #d8dce2;
  --bord-fort: #c2c8d0;

  --texte: #16191d;
  --texte-doux: #5b6472;
  --texte-faible: #67707d;

  --accent: #1f5fa9;
  --accent-fort: #17497f;
  --accent-doux: #e8f0f9;
  --accent-texte: #ffffff;

  --alerte: #a6631a;
  --alerte-fond: #fdf4e6;
  --alerte-bord: #e8c48c;

  --danger: #a32d2d;
  --danger-fond: #fbeded;
  --danger-bord: #e5b4b4;

  --succes: #1f6b45;
  --succes-fond: #eaf5ef;
  --succes-bord: #a9d4bd;

  --rayon: 8px;
  --rayon-l: 12px;
  --ombre: 0 1px 2px rgba(16, 22, 31, .06), 0 4px 12px rgba(16, 22, 31, .06);
  --ombre-forte: 0 8px 28px rgba(16, 22, 31, .14);

  --police: "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  --police-donnees: "Segoe UI", system-ui, sans-serif;
  --police-code: "Cascadia Mono", Consolas, "SF Mono", monospace;

  --transition: 160ms cubic-bezier(.2, .6, .3, 1);
}

/* --- Theme sombre -------------------------------------------------------
   Trois etats, et l'ordre des regles compte :

   1. sans `data-theme`, on SUIT le systeme -- c'est le comportement d'avant,
      et celui de la grande majorite des postes ;
   2. `data-theme="sombre"` force le sombre, meme sur un systeme clair ;
   3. `data-theme="clair"` force le clair : d'ou le `:not([data-theme="clair"])`
      sur la regle systeme, sans quoi un choix « clair » explicite serait
      ecrase par un systeme regle en sombre.

   Les variables ne sont definies qu'UNE fois, partagees par les deux
   selecteurs : deux copies finiraient par diverger.

   `color-scheme` suit le choix, et ce n'est PAS cosmetique : c'est lui qui
   dit au navigateur comment dessiner ce qu'il dessine lui-meme -- cases a
   cocher, boutons radio, ascenseurs, listes deroulantes. Le laisser a
   « light dark » sur un theme force donnait une case NOIRE sur fond clair,
   parce que le navigateur suivait le systeme pendant que la page suivait le
   choix de l'acheteur. */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="clair"]) {
    color-scheme: dark;
    --ground: #14171b;
    --surface: #1b1f24;
    --surface-2: #20252b;
    --panneau: #22272e;
    --bord: #333a43;
    --bord-fort: #434c57;

    --texte: #e8ecf1;
    --texte-doux: #a3adba;
    --texte-faible: #9aa4b1;

    --accent: #5c9ee0;
    --accent-fort: #7fb5ea;
    --accent-doux: #1b2c3f;

    --alerte: #e0a860;
    --alerte-fond: #2e2415;
    --alerte-bord: #4d3a1c;

    --danger: #e08585;
    --danger-fond: #2e1a1a;
    --danger-bord: #4d2626;

    --succes: #77c9a0;
    --succes-fond: #16261e;
    --succes-bord: #27412f;

    --ombre: 0 1px 2px rgba(0, 0, 0, .3), 0 4px 12px rgba(0, 0, 0, .28);
    --ombre-forte: 0 8px 28px rgba(0, 0, 0, .45);
  }
}

/* Un theme clair FORCE : les variables de `:root` s'appliquent deja, seul
   `color-scheme` doit etre ramene a « light » -- sinon le navigateur
   continuerait de dessiner ses cases en sombre sur un fond clair. */
:root[data-theme="clair"] {
  color-scheme: light;
}

:root[data-theme="sombre"] {
  color-scheme: dark;
    --ground: #14171b;
    --surface: #1b1f24;
    --surface-2: #20252b;
    --panneau: #22272e;
    --bord: #333a43;
    --bord-fort: #434c57;

    --texte: #e8ecf1;
    --texte-doux: #a3adba;
    --texte-faible: #9aa4b1;

    --accent: #5c9ee0;
    --accent-fort: #7fb5ea;
    --accent-doux: #1b2c3f;

    --alerte: #e0a860;
    --alerte-fond: #2e2415;
    --alerte-bord: #4d3a1c;

    --danger: #e08585;
    --danger-fond: #2e1a1a;
    --danger-bord: #4d2626;

    --succes: #77c9a0;
    --succes-fond: #16261e;
    --succes-bord: #27412f;

    --ombre: 0 1px 2px rgba(0, 0, 0, .3), 0 4px 12px rgba(0, 0, 0, .28);
    --ombre-forte: 0 8px 28px rgba(0, 0, 0, .45);
}

* { box-sizing: border-box; }

/* La place de la barre de defilement est reservee EN PERMANENCE.
   
   Sans cela, deposer un fichier a l'etape 1 allonge la page, la barre
   apparait, et le contenu centre se decale de 7 px vers la gauche -- tout
   l'ecran semble bouger alors que rien n'a change de largeur. Le meme saut se
   produit a chaque passage d'etape, selon la hauteur de la vue.

   `stable` garde la gouttiere meme quand la page tient dans la fenetre : la
   largeur utile ne change plus jamais. */
html { scrollbar-gutter: stable; }

body {
  margin: 0;
  overflow-x: hidden;
  font-family: var(--police);
  font-size: 14px;
  line-height: 1.5;
  color: var(--texte);
  background: var(--ground);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -.01em; }
h1 { font-size: 1.0625rem; }
h2 { font-size: 1.25rem; }
h3 { font-size: .9375rem; }
p { margin: 0; }

/* ------------------------------------------------------------------ entete */
.entete {
  position: sticky;
  /* `sticky` etablit deja un contexte de positionnement : le bouton
     « Terminer la session » s'y centre en absolu. */
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 0 20px;
  height: 56px;
  background: var(--surface);
  border-bottom: 1px solid var(--bord);
}

/* La marque est un bouton (retour a l'etape 1) mais ne doit pas en avoir
   l'air : ni fond, ni bordure, ni cadre. Seul le curseur et un leger
   eclaircissement au survol signalent qu'elle est cliquable. */
.marque {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
  padding: 4px 6px;
  margin-left: -6px;      /* compense le padding : le logo reste aligne */
  border: 0;
  border-radius: var(--rayon);
  background: none;
  font-family: inherit;
  text-align: left;
  color: inherit;
  cursor: pointer;
}
.marque:hover { background: var(--panneau); }
.marque:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.marque strong { font-size: .9375rem; font-weight: 650; letter-spacing: -.015em; }
.marque span { color: var(--texte-faible); font-size: .75rem; }

/* Fil des etapes : indique la position dans l'assistant.
   Sur petit ecran, seule l'etape courante reste affichee : le fil complet
   ferait deborder l'entete. */
.etapes { display: flex; align-items: center; gap: 4px; margin-left: auto; min-width: 0; }


.etape {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 12px 5px 8px;
  border: 0;
  background: none;
  font-family: inherit;
  border-radius: 100px;
  color: var(--texte-faible);
  font-size: .8125rem;
  cursor: pointer;
  transition: color var(--transition), background var(--transition);
}
/* Une etape franchie invite au retour ; l'etape courante et celles non
   atteintes ne sont pas des cibles de navigation. */
.etape:not(:disabled):hover { background: var(--panneau); color: var(--texte); }
.etape:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.etape:disabled { cursor: default; }
.etape[data-etat="faite"]:not(:disabled) { color: var(--accent); }
.etape[data-etat="faite"]:not(:disabled):hover { background: var(--accent-doux); }
.etape[data-etat="active"] { color: var(--accent); background: var(--accent-doux); font-weight: 600; }
.etape[data-etat="faite"] { color: var(--texte-doux); }

.etape i {
  display: grid;
  place-items: center;
  width: 20px; height: 20px;
  border-radius: 50%;
  border: 1.5px solid currentColor;
  font-size: .6875rem;
  font-style: normal;
  font-weight: 600;
}
.etape[data-etat="active"] i { background: var(--accent); border-color: var(--accent); color: var(--accent-texte); }
.etape[data-etat="faite"] i { background: var(--texte-doux); border-color: var(--texte-doux); color: var(--surface); }

.separateur-etape { width: 16px; height: 1px; background: var(--bord-fort); }

/* --------------------------------------------------------------- structure */
.zone { max-width: 1080px; margin: 0 auto; padding: 28px 20px 64px; }
.zone--large { max-width: 1500px; }
/* Les etapes de saisie tiennent dans une colonne : au-dela, l'oeil doit
   parcourir toute la largeur de l'ecran pour un formulaire de trois champs. */
.zone--etroite { max-width: 660px; }
/* L'ecran 1 s'elargit une fois le compte rendu de lecture affiche. */
.zone--etroite:has(#lecture:not([hidden])) { max-width: 940px; }
.vue[hidden] { display: none; }

.titre-vue { margin-bottom: 20px; }
.titre-vue p { color: var(--texte-doux); margin-top: 5px; }

/* ------------------------------------------------------------------- depot */
/* Chaque ecran ne porte plus qu'une seule zone de depot : elle prend toute
   la largeur disponible plutot que de partager une grille. */
.depot--pleine-largeur { margin-bottom: 16px; min-height: 168px; }

/* Separateur entre les deux facons de fournir l'export SAP. */
.ou {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 14px 0;
  color: var(--texte-faible);
  font-size: .8125rem;
}
.ou::before, .ou::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--bord);
}

.grille-depot {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  align-items: start;
}
@media (max-width: 860px) { .grille-depot { grid-template-columns: 1fr; } }

.depot {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 188px;
  padding: 24px;
  text-align: center;
  background: var(--surface);
  border: 1.5px dashed var(--bord-fort);
  border-radius: var(--rayon-l);
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition);
}
.depot:hover { border-color: var(--accent); background: var(--surface-2); }
.depot:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.depot[data-survol="oui"] { border-color: var(--accent); background: var(--accent-doux); border-style: solid; }
.depot[data-rempli="oui"] { border-style: solid; border-color: var(--succes-bord); background: var(--succes-fond); }

/* Pendant la lecture. Le vert de « rempli » annonce une reussite qui n'est
   PAS acquise : le fichier est depose, pas encore lu. Sur un catalogue de
   quelques megaoctets, l'ecran restait plusieurs secondes en vert, fournisseur
   reconnu -- tout disait que ca avait marche -- puis le verdict tombait.

   Neutre plutot que rouge : rien n'a echoue, on ne sait simplement pas
   encore. La regle passe APRES celle de « rempli » pour l'emporter. */
.depot[data-lecture="encours"] {
  border-color: var(--bord-fort);
  background: var(--panneau);
}
.depot[data-lecture="encours"] .depot__icone,
.depot[data-lecture="encours"] .depot__fichier { color: var(--texte-doux); }

/* La fleche de depot cede la place a une rondelle, AU CENTRE de la zone.
   C'est la que le regard est -- il vient d'y deposer un fichier -- et c'est
   donc la que le travail doit se voir. Le bloc « Lecture » en dessous porte
   deja une rondelle, mais elle demande de baisser les yeux pour comprendre
   que quelque chose se passe.

   La rondelle prend la PLACE du SVG dans le flux, elle ne se pose pas dessus :

   - `.depot__icone` est un <svg>, et les elements SVG n'affichent aucun
     pseudo-element -- le style se calculait, rien ne se dessinait ;
   - un positionnement absolu demanderait de connaitre la hauteur du haut de
     la zone, un nombre qui devient faux des que le contenu change.

   Le SVG devient invisible mais garde sa place ; la bordure animee est
   dessinee sur lui, aux memes dimensions. Rien a creer ni a nettoyer. */
.depot[data-lecture="encours"] .depot__icone {
  /* Le trace disparait, la boite reste : c'est elle qu'on habille. */
  stroke: transparent;
  /* La meme taille que la fleche qu'elle remplace : rien ne doit bouger dans
     la zone au moment ou la lecture commence. `box-sizing: border-box` est
     herite globalement, la bordure tient donc DANS ces 30 px. */
  width: 30px;
  height: 30px;
  border: 3px solid var(--bord-fort);
  border-right-color: var(--accent);
  border-radius: 50%;
  animation: tourner .7s linear infinite;
}

.depot__icone { color: var(--texte-faible); transition: color var(--transition); }
.depot[data-survol="oui"] .depot__icone,
.depot:hover .depot__icone { color: var(--accent); }
.depot[data-rempli="oui"] .depot__icone { color: var(--succes); }

.depot__titre { font-weight: 600; }
.depot__aide { color: var(--texte-faible); font-size: .8125rem; }
.depot__requis { color: var(--danger); }
.depot__fichier { display: flex; align-items: center; gap: 8px; font-size: .8125rem; color: var(--succes); font-weight: 600; }
.depot__retirer {
  border: 0; background: none; padding: 2px 6px; cursor: pointer;
  color: var(--texte-faible); border-radius: 4px; font-size: .875rem; line-height: 1;
}
.depot__retirer:hover { color: var(--danger); background: var(--danger-fond); }
/* Champ natif masque : l'attribut hidden le couvre deja, cette regle
   garantit le masquage meme si un navigateur applique display a hidden. */
.champ-fichier, [hidden] { display: none !important; }

.exports { margin-bottom: 4px; }
.exports__ligne { display: flex; gap: 8px; align-items: center; }
.exports__ligne select { flex: 1; min-width: 0; }

/* Retrait d'un export : action rare, elle reste discrete. */
.exports__retirer {
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  background: var(--surface);
  color: var(--texte-faible);
  cursor: pointer;
  font-size: .8125rem;
  line-height: 1;
  transition: color var(--transition), background var(--transition);
}
.exports__retirer:hover:not(:disabled) { color: var(--danger); background: var(--danger-fond); }
.exports__retirer:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.exports__retirer:disabled { opacity: .4; cursor: not-allowed; }

.exports__pied {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-top: 4px;
}
.exports__pied small { flex: 1; }

/* Lien de service : presente mais jamais mise en avant. */
.lien-discret {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  font-size: .75rem;
  color: var(--texte-faible);
  text-decoration: underline;
  cursor: pointer;
}
.lien-discret:hover { color: var(--danger); }
.lien-discret:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Compte rendu de lecture affiche a l'etape 1 : ce que le module a compris
   du fichier, avant tout traitement. */
.lecture {
  padding: 14px 16px;
  margin-bottom: 16px;
  background: var(--surface);
  border: 1px solid var(--bord);
  border-radius: var(--rayon-l);
}
.lecture__entete {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
}
.lecture__titre { font-weight: 600; font-size: .875rem; }
.lecture__entete .bouton { margin-left: auto; }

/* Les cinq compteurs tiennent sur une ligne : ils se lisent d'un coup d'oeil
   et chacun donne acces aux lignes qu'il compte. */
.lecture__comptes {
  display: flex;
  gap: 1px;
  margin: 0 0 10px;
  background: var(--bord);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  overflow-x: auto;
}

.compteur {
  flex: 1 1 0;
  min-width: 104px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 9px 11px;
  background: var(--surface);
  border: 0;
  /* La separation est PORTEE par le compteur, pas laissee au `gap`.
     Le conteneur defile (`overflow-x: auto`) : quand les compteurs se serrent,
     le pixel de fond entre eux tombe sous le pixel physique et disparait --
     les separations n'etaient plus visibles a l'etape 2. Un trait explicite
     tient a toutes les largeurs. */
  border-left: 1px solid var(--bord);
  border-bottom: 2px solid transparent;
  font-family: inherit;
  text-align: left;
  color: var(--texte);
}

/* Pas de trait avant le premier : il doublerait la bordure du conteneur. */
.compteur:first-child { border-left: 0; }

.compteur__libelle {
  font-size: .6875rem;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--texte-doux);
  white-space: nowrap;
}
.compteur__valeur {
  font-size: 1.0625rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.compteur__part {
  font-size: .6875rem;
  font-weight: 500;
  color: var(--texte-faible);
  margin-left: 2px;
}

.compteur--cliquable { cursor: pointer; transition: background var(--transition); }
.compteur--cliquable:hover { background: var(--surface-2); }
.compteur--cliquable:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.compteur--cliquable[aria-selected="true"] {
  background: var(--accent-doux);
  border-bottom-color: var(--accent);
}
.compteur--cliquable[aria-selected="true"] .compteur__libelle {
  color: var(--accent);
  font-weight: 600;
}

.compteur--accent .compteur__valeur { color: var(--accent); }
.compteur--alerte .compteur__valeur { color: var(--alerte); }

.apercu__titre {
  font-size: .75rem;
  font-weight: 600;
  color: var(--texte-doux);
  margin-bottom: 6px;
}
.texte-faible { color: var(--texte-faible); }

.lecture__constats { margin-top: 10px; }
.lecture__constats .message { margin-bottom: 6px; font-size: .8125rem; }
.lecture__constats .message:last-child { margin-bottom: 0; }

.lecture__apercu { margin-top: 12px; overflow-x: auto; }
.lecture__apercu table { font-size: .78125rem; }
.lecture__apercu th, .lecture__apercu td { padding: 5px 9px; white-space: nowrap; }

/* Case a cocher accompagnee d'un libelle explicatif. */
.case-ligne {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 14px;
  margin-bottom: 16px;
  background: var(--surface);
  border: 1px solid var(--bord);
  border-radius: var(--rayon-l);
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition);
}
.case-ligne:hover { border-color: var(--bord-fort); }
.case-ligne:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-doux);
}
.case-ligne input { margin-top: 2px; flex-shrink: 0; }
.case-ligne span { display: flex; flex-direction: column; gap: 2px; }
.case-ligne strong { font-size: .875rem; font-weight: 600; }
.case-ligne small { color: var(--texte-doux); font-size: .8125rem; line-height: 1.45; }

/* Une case qui ne s'applique PAS ici. « Ce fournisseur n'est pas encore dans
   SAP » n'a plus de sens des qu'il y est : la cocher passerait l'etape SAP
   alors que des articles existent, et le module creerait des doublons de ce
   qu'il aurait du mettre a jour.

   Grisee plutot que masquee : une case qui disparait laisse croire a un bug,
   une case grisee dit qu'elle ne s'applique pas. Le curseur le confirme au
   survol ; le POURQUOI passe par `data-infobulle`, gere par delegation. */
.case-ligne[data-inapplicable="oui"] {
  background: var(--panneau);
  border-color: var(--bord);
  cursor: not-allowed;
}
.case-ligne[data-inapplicable="oui"]:hover { border-color: var(--bord); }
.case-ligne[data-inapplicable="oui"] strong,
.case-ligne[data-inapplicable="oui"] small {
  color: var(--texte-faible);
}
.case-ligne[data-inapplicable="oui"] input { cursor: not-allowed; }

/* ------------------------------------------------------------- formulaires */

.champ { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.champ--large { margin-bottom: 16px; }
.champ--court { max-width: 220px; margin-bottom: 4px; }
.champ label { font-size: .8125rem; font-weight: 600; color: var(--texte-doux); }
.champ small { color: var(--texte-faible); font-size: .75rem; }

select, input[type="number"], input[type="search"], input[type="text"] {
  font-family: inherit;
  font-size: .875rem;
  color: var(--texte);
  background: var(--surface);
  border: 1px solid var(--bord-fort);
  border-radius: var(--rayon);
  padding: 8px 10px;
  transition: border-color var(--transition), box-shadow var(--transition);
}
select:hover, input:hover:not(:disabled) { border-color: var(--texte-faible); }
select:focus, input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-doux);
}
select:disabled, input:disabled { background: var(--panneau); color: var(--texte-faible); cursor: not-allowed; }

/* ----------------------------------------------------------------- boutons */
.bouton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: inherit;
  font-size: .875rem;
  font-weight: 600;
  padding: 9px 18px;
  border-radius: var(--rayon);
  border: 1px solid transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--transition), border-color var(--transition), color var(--transition);
}
.bouton:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.bouton--primaire { background: var(--accent); color: var(--accent-texte); }
.bouton--primaire:hover:not(:disabled) { background: var(--accent-fort); }
.bouton--primaire:disabled { background: var(--bord-fort); color: var(--texte-faible); cursor: not-allowed; }

.bouton--neutre { background: var(--surface); color: var(--texte); border-color: var(--bord-fort); }
.bouton--neutre:hover:not(:disabled) { background: var(--panneau); }
.bouton--neutre:disabled { color: var(--texte-faible); cursor: not-allowed; }

.bouton--discret { background: none; color: var(--texte-doux); padding: 7px 12px; }
.bouton--discret:hover:not(:disabled) { background: var(--panneau); color: var(--texte); }

.barre-actions { display: flex; align-items: center; gap: 12px; margin-top: 20px; }
.barre-actions .pousse { margin-left: auto; }

/* Indicateur de chargement : etat, pas decoration. */
.rondelle {
  width: 15px; height: 15px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: tourner .7s linear infinite;
}
@keyframes tourner { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .rondelle { animation-duration: 2s; } }

/* ---------------------------------------------------------------- messages */
.message {
  display: flex;
  gap: 10px;
  padding: 11px 14px;
  border-radius: var(--rayon);
  border: 1px solid var(--bord);
  background: var(--surface-2);
  font-size: .875rem;
  margin-bottom: 10px;
}
.message--alerte { background: var(--alerte-fond); border-color: var(--alerte-bord); color: var(--alerte); }
.message--erreur { background: var(--danger-fond); border-color: var(--danger-bord); color: var(--danger); }
.message--succes { background: var(--succes-fond); border-color: var(--succes-bord); color: var(--succes); }

/* Un message qui OUVRE quelque chose. Deux situations le portent : un format
   jamais lu, et un profil qui ne retrouve plus ses colonnes -- toutes deux se
   reparent au meme endroit, la fenetre de correspondance.

   La fenetre s'ouvre deja d'elle-meme ; ce clic sert a la ROUVRIR quand on
   l'a fermee par erreur. Le message est ou le regard va quand on cherche quoi
   faire : c'est la que l'action doit etre. */
.message[data-cliquable="oui"] { cursor: pointer; }
.message[data-cliquable="oui"]:hover { border-color: currentColor; }
.message[data-cliquable="oui"]:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}
/* Dit que le message est cliquable, sans repeter la couleur du bandeau --
   qui porte deja la gravite. */
.message__action { text-decoration: underline; font-weight: 600; }
.message strong { font-weight: 650; }

/* ------------------------------------------------------------- indicateurs */
/* Les indicateurs sont aussi les filtres du tableau : un chiffre et le moyen
   de voir les lignes qui le composent ne font qu'un. */
.indicateurs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
  gap: 1px;
  margin: 18px 0 20px;
  background: var(--bord);
  border: 1px solid var(--bord);
  border-radius: var(--rayon-l);
  overflow: hidden;
}

.indicateur {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 13px 16px;
  background: var(--surface);
  text-align: left;
  font-family: inherit;
  border: 0;
  /* Le soulignement marque la selection sans deplacer le contenu. */
  border-bottom: 2px solid transparent;
  color: var(--texte);
  transition: background var(--transition);
}

.indicateur__libelle { color: var(--texte-doux); font-size: .75rem; }
.indicateur__valeur {
  font-size: 1.5rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.02em;
}

.indicateur--filtrant { cursor: pointer; transition: background var(--transition); }
.indicateur--filtrant:hover { background: var(--surface-2); }
.indicateur--filtrant:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.indicateur--filtrant[aria-selected="true"] {
  background: var(--accent-doux);
  border-bottom-color: var(--accent);
}
.indicateur--filtrant[aria-selected="true"] .indicateur__libelle {
  color: var(--accent);
  font-weight: 600;
}

.indicateur--alerte .indicateur__valeur { color: var(--alerte); }
.indicateur--danger .indicateur__valeur { color: var(--danger); }
.indicateur--accent .indicateur__valeur { color: var(--accent); }

/* Part du total, en complement du chiffre absolu. */
.indicateur__part {
  font-size: .75rem;
  font-weight: 500;
  color: var(--texte-faible);
  letter-spacing: 0;
  margin-left: 4px;
}
.indicateur--alerte .indicateur__part { color: var(--alerte); }

/* Trace des interventions manuelles : discrete, mais cliquable. */
.actions-manuelles {
  padding: 9px 14px;
  background: var(--surface-2);
  border: 1px solid var(--bord);
  border-top: 0;
  font-size: .8125rem;
  color: var(--texte-doux);
}
.lien-filtre {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  color: var(--accent);
  text-decoration: underline;
  cursor: pointer;
}
.lien-filtre:hover { color: var(--accent-fort); }
.lien-filtre:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* --------------------------------------------------------------- commandes */
/* Un bandeau unique entre les indicateurs et le tableau : reglage du seuil
   a gauche, recherche a droite. */
.commandes {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  padding: 11px 14px;
  margin-bottom: 14px;
  background: var(--surface);
  border: 1px solid var(--bord);
  border-radius: var(--rayon-l);
  font-size: .8125rem;
  color: var(--texte-doux);
}

.commandes__seuil {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  min-width: 0;
}
.commandes__seuil label { font-weight: 600; white-space: nowrap; }

.commandes input[type="range"] {
  width: 180px;
  accent-color: var(--accent);
  cursor: pointer;
}
.commandes input[type="range"]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.commandes output {
  min-width: 42px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--texte);
}
.commandes__effet { color: var(--texte-faible); white-space: nowrap; }

.recherche { margin-left: auto; min-width: 230px; max-width: 380px; }
@media (max-width: 860px) {
  .recherche { margin-left: 0; width: 100%; }
}

/* ------------------------------------------------------------------ tableau */
.cadre-tableau {
  background: var(--surface);
  border: 1px solid var(--bord);
  border-radius: var(--rayon-l) var(--rayon-l) 0 0;
  overflow: auto;
  /* Le tableau defile a l'interieur de son cadre : sans hauteur bornee, c'est
     la page qui defilerait et l'en-tete sortirait de vue. */
  max-height: min(62vh, 620px);
  overscroll-behavior: contain;
}

table { width: 100%; border-collapse: collapse; font-size: .8125rem; }

thead th {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--surface-2);
  border-bottom: 1px solid var(--bord);
  padding: 9px 12px;
  text-align: left;
  font-size: .75rem;
  font-weight: 600;
  color: var(--texte-doux);
  white-space: nowrap;
}

tbody td {
  padding: 8px 12px;
  border-bottom: 1px solid var(--bord);
  vertical-align: middle;
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr { transition: background var(--transition); }
tbody tr:hover { background: var(--surface-2); }
tbody tr[data-exclue="oui"] { opacity: .5; }
tbody tr[data-exclue="oui"] .designation, tbody tr[data-exclue="oui"] .ref { text-decoration: line-through; }

.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ref { font-family: var(--police-code); font-size: .78125rem; white-space: nowrap; }
.designation { max-width: 300px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.col-etroite { width: 1%; }

/* Cellule modifiable : discrete au repos, evidente au survol. */
.modifiable {
  position: relative;
  cursor: text;
  border-radius: 4px;
  padding: 3px 6px;
  margin: -3px -6px;
  transition: background var(--transition), box-shadow var(--transition);
}
.modifiable:hover { background: var(--accent-doux); box-shadow: inset 0 0 0 1px var(--bord-fort); }
.modifiable:focus {
  outline: none;
  background: var(--surface);
  box-shadow: 0 0 0 2px var(--accent);
}
.modifiable[data-invalide="oui"] { box-shadow: 0 0 0 2px var(--danger); background: var(--danger-fond); }
.modifiable[data-modifiee="oui"] { background: var(--accent-doux); font-weight: 600; }

.etiquette {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 100px;
  font-size: .6875rem;
  font-weight: 600;
  white-space: nowrap;
}
.etiquette--maj { background: var(--accent-doux); color: var(--accent); }
.etiquette--nouveau { background: var(--succes-fond); color: var(--succes); }
.etiquette--inchange { background: var(--panneau); color: var(--texte-faible); }
.etiquette--supprimer { background: var(--alerte-fond); color: var(--alerte); }

/* En-tetes triables : le tri est une action, il doit se voir. */
.entetes th[data-tri] {
  cursor: pointer;
  user-select: none;
  transition: background var(--transition);
}
.entetes th[data-tri]:hover { background: var(--panneau); }
.entetes th[data-tri]::after {
  content: "↕";
  margin-left: 5px;
  opacity: .3;
  font-size: .75rem;
}
.entetes th[data-sens="asc"]::after { content: "↑"; opacity: 1; color: var(--accent); }
.entetes th[data-sens="desc"]::after { content: "↓"; opacity: 1; color: var(--accent); }

/* Icone de filtre dans l'en-tete : discrete au repos, visible au survol. */
/* Pas de `position: relative` ici : elle ecraserait le `sticky` de l'en-tete.
   Un element sticky sert deja de reference aux enfants positionnes. */
.entetes th[data-filtrable] { padding-right: 26px; }

.filtre-icone {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  border: 0;
  background: none;
  padding: 2px 4px;
  border-radius: 4px;
  cursor: pointer;
  font-size: .875rem;
  line-height: 1;
  color: var(--texte-faible);
  opacity: .55;
  transition: opacity var(--transition), color var(--transition);
}
.entetes th:hover .filtre-icone { opacity: 1; }
.filtre-icone:hover { color: var(--accent); background: var(--panneau); }
.filtre-icone:focus-visible {
  opacity: 1;
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* Une colonne filtree se repere sans ouvrir le panneau. */
.entetes th[data-filtre="actif"] { background: var(--accent-doux); }
.entetes th[data-filtre="actif"] .filtre-icone { opacity: 1; color: var(--accent); }
.entetes th[data-filtre="actif"]::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--accent);
}

/* Panneau de saisie, ancre sous l'en-tete. */
.filtre-colonne {
  position: fixed;
  z-index: 60;
  width: 240px;
  padding: 12px;
  background: var(--surface);
  border: 1px solid var(--bord-fort);
  border-radius: var(--rayon-l);
  box-shadow: var(--ombre-forte);
}
.filtre-colonne[hidden] { display: none; }

.filtre-colonne__titre {
  display: block;
  font-size: .75rem;
  font-weight: 600;
  color: var(--texte-doux);
  margin-bottom: 6px;
}
.filtre-colonne input { width: 100%; font-size: .8125rem; }

.filtre-colonne__actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 10px;
}
.filtre-colonne__actions .bouton { padding: 6px 12px; font-size: .8125rem; }

/* Statut modifiable : un rapprochement automatique peut se tromper. */
.statut {
  font-family: inherit;
  font-size: .6875rem;
  font-weight: 600;
  padding: 3px 6px;
  border-radius: 100px;
  border: 1px solid transparent;
  cursor: pointer;
  max-width: 118px;
}
.statut--maj { background: var(--accent-doux); color: var(--accent); }
.statut--nouveau { background: var(--succes-fond); color: var(--succes); }
.statut--inchange { background: var(--panneau); color: var(--texte-faible); }
.statut--ignore { background: var(--danger-fond); color: var(--danger); }
.statut:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.statut__marque {
  margin-left: 4px;
  color: var(--accent);
  font-size: .75rem;
}

/* Ligne informative : consultable, mais hors de l'import. */
.ligne--informative { color: var(--texte-doux); }

.variation { font-variant-numeric: tabular-nums; font-weight: 600; }
/* La couleur signale l'ampleur de l'ecart, pas son sens : une baisse de 99 %
   est une anomalie a controler, pas une bonne nouvelle. */
.variation--normale { color: var(--texte-doux); }
.variation--forte { color: var(--alerte); }
.variation--extreme { color: var(--danger); }
.variation__sens { font-weight: 400; color: var(--texte-faible); margin-left: 3px; }

.marque-alerte { color: var(--alerte); cursor: help; }

/* Case a cocher : conserve l'affordance native, agrandie pour le pointage. */
.case { width: 16px; height: 16px; accent-color: var(--accent); cursor: pointer; }
.case:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ------------------------------------------------------------------- etats */
.etat-vide {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 56px 24px;
  text-align: center;
  color: var(--texte-faible);
}
.etat-vide strong { color: var(--texte-doux); font-weight: 600; }

/* Squelette de chargement plutot qu'un indicateur au centre du contenu. */
.squelette td > span {
  display: block;
  height: 11px;
  border-radius: 4px;
  background: linear-gradient(90deg, var(--panneau) 25%, var(--surface-2) 50%, var(--panneau) 75%);
  background-size: 200% 100%;
  animation: glisser 1.4s ease-in-out infinite;
}
@keyframes glisser { to { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) { .squelette td > span { animation: none; } }

/* Ferme le bloc du tableau : bordures et coins arrondis en bas. */
.pagination {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  background: var(--surface-2);
  border: 1px solid var(--bord);
  border-top: 0;
  border-radius: 0 0 var(--rayon-l) var(--rayon-l);
  color: var(--texte-doux);
  font-size: .8125rem;
}
.pagination .pousse { margin-left: auto; display: flex; align-items: center; gap: 4px; }

/* Navigation entre pages : discrete, elle ne doit pas concurrencer le CTA. */
.page-fleche {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  background: var(--surface);
  color: var(--texte-doux);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  transition: background var(--transition), color var(--transition);
}
.page-fleche:hover:not(:disabled) { background: var(--panneau); color: var(--texte); }
.page-fleche:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.page-fleche:disabled { opacity: .4; cursor: not-allowed; }

/* ---------------------------------------------------------------- resultat */
.resultat { text-align: center; padding: 32px 0 12px; }
.resultat__marque {
  display: grid;
  place-items: center;
  width: 52px; height: 52px;
  margin: 0 auto 16px;
  border-radius: 50%;
  background: var(--succes-fond);
  color: var(--succes);
  border: 1px solid var(--succes-bord);
}
.resultat h2 { margin-bottom: 6px; }
.resultat p { color: var(--texte-doux); }

.fichiers { display: grid; gap: 10px; max-width: 660px; margin: 24px auto 0; }
.fichier {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 14px 16px;
  background: var(--surface);
  border: 1px solid var(--bord);
  border-radius: var(--rayon-l);
  text-align: left;
}
.fichier__icone { color: var(--accent); flex-shrink: 0; }
.fichier__texte { min-width: 0; flex: 1; }
.fichier__nom { font-weight: 600; overflow-wrap: anywhere; }
.fichier__compte {
  font-weight: 500;
  font-size: .8125rem;
  color: var(--accent);
  margin-left: 4px;
}
.fichier__info { color: var(--texte-doux); font-size: .8125rem; }
/* Le nom reel du fichier, pour le retrouver apres telechargement. */
.fichier__fichier {
  font-family: var(--police-code);
  font-size: .6875rem;
  color: var(--texte-faible);
  margin-top: 3px;
  overflow-wrap: anywhere;
}

.barre-actions--centre { justify-content: center; }
.bouton--large { padding: 12px 28px; font-size: .9375rem; }
.fichier .bouton { margin-left: auto; flex-shrink: 0; }

.chemin {
  font-family: var(--police-code);
  font-size: .75rem;
  color: var(--texte-faible);
  overflow-wrap: anywhere;
  margin-top: 18px;
  text-align: center;
}

.note-visuelle { color: var(--texte-faible); font-size: .8125rem; margin-top: 6px; }

/* --------------------------------------------------------------- infobulle */
.infobulle {
  position: fixed;
  z-index: 50;
  max-width: 300px;
  padding: 8px 11px;
  background: var(--texte);
  color: var(--surface);
  border-radius: 6px;
  font-size: .78125rem;
  line-height: 1.45;
  box-shadow: var(--ombre-forte);
  pointer-events: none;
  /* Le detail d'une unite tient en plusieurs faits — contenu, carton, motif.
     Sans cela, ils s'agglutinent en un pave que personne ne lit. */
  white-space: pre-line;
}
.infobulle[hidden] { display: none; }

.sr-uniquement {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ------------------------------------------------------------ petits ecrans
   Place en fin de feuille : ces regles doivent l'emporter sur les regles de
   base declarees plus haut, a specificite egale. */
@media (max-width: 620px) {
  .entete { gap: 12px; }
  .marque span { display: none; }
  .separateur-etape { display: none; }
  .etape { display: none; padding: 5px 10px 5px 7px; }
  .etape[data-etat="active"] { display: flex; }
  .zone { padding: 20px 14px 48px; }
  .barre-actions { flex-wrap: wrap; }
  .barre-actions .pousse { margin-left: 0; width: 100%; }
  .barre-actions .pousse .bouton { width: 100%; }
  .fichier { flex-wrap: wrap; }
  .fichier .bouton { margin-left: 0; width: 100%; }
}


/* ------------------------------------------- recherche de fournisseur
   Une liste deroulante devient impraticable au-dela d'une centaine
   d'entrees : on filtre a la frappe, dans un panneau ancre sous le champ. */
.recherche-fournisseur__zone {
  position: relative;
}
.recherche-fournisseur__zone input[type="text"] {
  width: 100%;
  padding-right: 34px;   /* place pour la croix d'effacement */
}
.recherche-fournisseur__effacer {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--texte-faible);
  font-size: 1.05rem;
  line-height: 1;
  cursor: pointer;
}
.recherche-fournisseur__effacer:hover {
  background: var(--panneau);
  color: var(--texte);
}
.recherche-fournisseur__liste {
  position: absolute;
  /* Au-dessus de tout le contenu de l'etape : la liste recouvre la zone de
     depot le temps du choix. */
  z-index: 70;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  max-height: 268px;
  overflow-y: auto;
  margin: 0;
  padding: 4px;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  box-shadow: 0 10px 26px rgb(15 23 42 / .13);
}
.recherche-fournisseur__item {
  padding: 7px 10px;
  border-radius: calc(var(--rayon) - 3px);
  font-size: .875rem;
  cursor: pointer;
}
.recherche-fournisseur__item:hover,
.recherche-fournisseur__item.est-survole {
  background: var(--panneau);
}
/* Le detail suit le nom sur la MEME ligne : a 249 fournisseurs, une entree
   sur deux lignes double la hauteur de la liste et fait defiler pour rien. */
.recherche-fournisseur__item small {
  color: var(--texte-faible);
  font-size: .75rem;
  line-height: 1.35;
}
/* L'entree de sortie, elle, garde son explication en dessous : elle est
   unique et merite sa place. */
.recherche-fournisseur__item--nouveau small {
  display: block;
  margin-top: 1px;
}
/* L'option de sortie, detachee des profils par un filet. */
.recherche-fournisseur__item--nouveau {
  margin-top: 4px;
  padding-top: 8px;
  border-top: 1px solid var(--bord);
  color: var(--accent);
  font-weight: 500;
}
.recherche-fournisseur__item--nouveau:hover,
.recherche-fournisseur__item--nouveau.est-survole {
  background: var(--accent-doux);
}
.recherche-fournisseur__vide {
  padding: 8px 10px;
  color: var(--texte-faible);
  font-size: .8125rem;
}


/* ------------------------------- lignes ecrasees et lignes conservees
   Une ligne perdue est suivie de celle qui la remplace : c'est en
   confrontant les deux que l'acheteur juge si le doublon est anodin. */
.apercu__ecrasee td {
  /* La ligne perdue s'efface au profit de celle qui la remplace. */
  opacity: .72;
  border-bottom: 0;
}
.apercu__conservee td {
  background: var(--surface-2);
  border-bottom: 0;
}
.apercu__conservee .num:first-child {
  color: var(--texte-faible);
}
/* Valeur qui differe entre la ligne perdue et celle conservee : c'est
   precisement ce qu'il faut regarder. */
/* Pas de fond colore ici : sur la teinte de surface-2 des lignes conservees,
   le texte d'alerte tombe a 4,36:1, sous le seuil de lisibilite. La couleur
   et la graisse suffisent a attirer l'oeil (4,58:1). */
.apercu__conservee td.apercu__divergent {
  color: var(--alerte);
  font-weight: 600;
}
.apercu__conservee-note td {
  padding-top: 0;
  padding-bottom: 7px;
  background: var(--surface-2);
  color: var(--texte-faible);
  font-size: .75rem;
}
.apercu__conservee-note strong {
  color: var(--alerte);
}


/* ---------------------------------------- divergence d'unite de prix
   Sous la ligne concernee : les colonnes qu'on regarde pour trancher,
   plutot que d'obliger a rouvrir le fichier Excel. */
.ligne-unite td {
  padding-top: 0;
  padding-bottom: 8px;
  background: var(--alerte-fond);
  border-bottom: 1px solid var(--bord);
}
.unite {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 14px;
  font-size: .8125rem;
  color: var(--texte-doux);
}
.unite__ecart {
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--alerte);
  color: var(--surface);
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}
.unite__texte { color: var(--alerte); }
.unite__champ { white-space: nowrap; }
.unite__champ b {
  font-weight: 600;
  color: var(--texte-faible);
}
.unite__ecartee, .unite__reintegree {
  padding: 1px 8px;
  border-radius: 999px;
  border: 1px solid var(--bord);
  font-size: .75rem;
}
.unite__reintegree {
  border-color: var(--bord-fort);
  color: var(--texte-doux);
}
.bouton-lien {
  border: 0;
  background: none;
  padding: 0;
  color: var(--accent);
  font-family: inherit;
  font-size: .8125rem;
  text-decoration: underline;
  cursor: pointer;
}
.bouton-lien:hover { color: var(--accent-fort); }


/* --------------------------------------- table de conversion des unites */
/* Terminer la session : present seulement en mode heberge.

   Volontairement discret -- c'est une sortie, pas une action courante -- mais
   pas cache : l'utilisateur doit pouvoir effacer ses fichiers sans chercher. */
/* Le bouton occupe la zone du MILIEU et s'y centre.

   `margin: auto` sur un seul axe le collerait a droite ou le decalerait selon
   la largeur du logo. Ici les deux marges automatiques le tiennent au centre
   de l'espace restant, quelle que soit la largeur des deux bords. */
/* Le bouton se centre sur la BARRE, pas sur l'espace restant.

   Les marges automatiques le placaient au milieu de ce qui reste entre le
   logo et le menu : mesure a 671px sur une barre de 1240, soit 51px trop a
   droite. Les deux bords n'ont pas la meme largeur, et aucune marge ne
   corrige cela.

   Le positionnement absolu le detache du flux : il se centre sur la barre
   entiere, quelle que soit la largeur de ce qui l'entoure. */
.entete__terminer {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  padding: 6px 12px;
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  background: none;
  color: var(--texte-faible);
  font: inherit;
  font-size: 0.86rem;
  cursor: pointer;
}
.entete__terminer:hover {
  background: var(--panneau);
  color: var(--texte);
}
.entete__terminer:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.entete__terminer[hidden] { display: none; }

.entete__reglages {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  /* L'icone reste COLLEE au logo, a gauche.

     `margin-left: auto` la poussait contre la barre d'etapes -- qui vit dans
     cette meme entete -- donc vers le milieu de l'ecran, loin de la marque a
     laquelle elle appartient. C'est le `gap` de l'entete qui l'espace
     desormais, et rien d'autre. */
  margin-left: 0;
  border: 0;
  border-radius: var(--rayon);
  background: none;
  color: var(--texte-faible);
  cursor: pointer;
}
.entete__reglages:hover { background: var(--panneau); color: var(--texte); }
.entete__reglages:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* Le panneau couvre la fenetre entiere et centre son contenu.
 *
 * Pas de correction de barre de defilement ici, et c'est delibere :
 * `overflow-x: hidden` sur le `body` en fait le bloc conteneur des elements
 * `fixed`. La gouttiere est donc DEJA deduite -- le panneau part de 1499 px
 * quand la fenetre en fait 1514.
 *
 * Une tentative de retrancher `--barre-defilement` a ete mesuree puis
 * retiree : elle deduisait la gouttiere une SECONDE fois, et decalait le
 * panneau de 15 px vers la gauche (192 a gauche contre 207 a droite, la ou
 * la regle nue donne 200/200). Le defaut d'origine venait d'ailleurs. */
.panneau {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  /* La colonne ne doit JAMAIS depasser la fenetre.
   *
   * Une piste de grille se dimensionne sur la largeur INTRINSEQUE de son
   * contenu -- avant tout defilement. L'apercu du mapping porte une table en
   * `white-space: nowrap` : chez BOSCH, 47 colonnes font 6 056 px. La piste
   * s'etirait a 6 130 px, et `place-items: center` centrait le panneau
   * dedans : il partait a x = 2 539 sur une fenetre de 1 514, invisible.
   * L'ecran ne montrait plus que le voile gris du fond.
   *
   * `minmax(0, 1fr)` autorise la piste a se retrecir sous son contenu ;
   * l'`overflow-x: auto` de l'apercu prend alors le relais, comme prevu. */
  grid-template-columns: minmax(0, 1fr);
  place-items: center;
  padding: 24px;
}
.panneau__fond {
  position: absolute;
  inset: 0;
  background: rgb(15 23 42 / .48);
}
.panneau__contenu {
  position: relative;
  width: min(860px, 100%);
  max-height: min(80vh, 720px);
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  box-shadow: 0 18px 48px rgb(15 23 42 / .28);
}
.panneau__entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--bord);
}
.panneau__entete h2 { font-size: 1rem; font-weight: 650; }
.panneau__fermer {
  width: 28px;
  height: 28px;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--texte-faible);
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
}
.panneau__fermer:hover { background: var(--panneau); color: var(--texte); }
.panneau__corps { padding: 18px; overflow-y: auto; }

.conversions__intro {
  margin-bottom: 10px;
  color: var(--texte-doux);
  font-size: .875rem;
  line-height: 1.5;
}
.conversions__fichier {
  margin-bottom: 18px;
  font-size: .75rem;
  color: var(--texte-faible);
  word-break: break-all;
}
.conversions__fournisseur {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 18px 0 8px;
  font-size: .9375rem;
  font-weight: 650;
}
.conversions__compte { color: var(--texte-faible); font-size: .75rem; font-weight: 400; }
.conversions__table { width: 100%; border-collapse: collapse; font-size: .8125rem; }
.conversions__table th {
  padding: 6px 10px;
  border-bottom: 1px solid var(--bord);
  color: var(--texte-faible);
  font-size: .75rem;
  font-weight: 600;
  text-align: left;
}
.conversions__table td { padding: 6px 10px; border-bottom: 1px solid var(--bord); }
.conversions__facteur {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--accent);
}

/* Conversion appliquee : ton neutre, ce n'est pas une alerte. */
.ligne-unite--convertie td { background: var(--surface-2); }
.unite__converti {
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--accent-doux);
  color: var(--accent-fort);
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}
.ligne-unite--convertie .unite__texte { color: var(--texte-faible); }

/* ------------------------------------------- decision sur les anomalies */
.apercu__aide {
  margin-left: 10px;
  color: var(--texte-faible);
  font-size: .75rem;
  font-weight: 400;
}
.apercu__bilan {
  margin: 2px 0 8px;
  color: var(--texte-doux);
  font-size: .8125rem;
}
.anomalie__gravite {
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--alerte-fond);
  color: var(--alerte);
  font-size: .6875rem;
  font-weight: 650;
}
/* Ligne ecartee : estompee, comme une ligne exclue du tableau de controle. */
tr[data-ecartee="oui"] td:not(.col-etroite) { opacity: .5; }

/* ------------------------------------------ masquage d'un bandeau lu */
.message { position: relative; padding-right: 34px; }
.message__fermer {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 22px;
  height: 22px;
  border: 0;
  border-radius: 50%;
  background: none;
  color: inherit;
  opacity: .55;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}
.message__fermer:hover { opacity: 1; background: rgb(0 0 0 / .07); }

/* -------------------------------------- groupes du tableau « À vérifier » */
.groupe td { padding: 0; background: var(--panneau); }
.groupe__bascule {
  display: flex;
  align-items: baseline;
  gap: 10px;
  width: 100%;
  padding: 7px 12px;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: .8125rem;
  text-align: left;
  color: var(--texte);
  cursor: pointer;
}
.groupe__bascule:hover { background: var(--surface-2); }
.groupe__chevron { color: var(--texte-faible); font-size: .75rem; }
.groupe__compte {
  padding: 0 7px;
  border-radius: 999px;
  background: var(--surface);
  color: var(--texte-doux);
  font-size: .75rem;
  font-variant-numeric: tabular-nums;
}
.groupe__aide {
  color: var(--texte-faible);
  font-size: .75rem;
  font-weight: 400;
}
.um { color: var(--texte-doux); font-size: .8125rem; }

/* --------------------------------------------- colonne « Conversion »
   Vert : la conversion vient de la table et a ete appliquee.
   Orange : l'ecart est observe dans les prix, mais rien ne le justifie. */
.conversion {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 999px;
  font-weight: 650;
  font-size: .75rem;
  font-variant-numeric: tabular-nums;
  cursor: help;
}
/* Conversion appliquee : ton neutre. C'est un fait acquis, pas une alerte
   ni une reussite a celebrer -- seul l'ecart non traite merite la couleur. */
.conversion--appliquee {
  background: var(--panneau);
  color: var(--texte-doux);
  border: 1px solid var(--bord);
}
.conversion--observee {
  background: var(--alerte-fond);
  color: var(--alerte);
  border: 1px solid var(--alerte-bord);
}
/* La ligne depliee ne porte plus l'alerte : elle sert de note discrete. */
.ligne-unite td { background: var(--surface-2); }

/* Article hors perimetre dans SAP : discret mais identifiable. */
.marque-stock {
  display: inline-block;
  padding: 0 5px;
  border-radius: 3px;
  background: var(--panneau);
  color: var(--texte-faible);
  font-size: .6875rem;
  font-weight: 650;
  cursor: help;
}

/* ------------------------------- choix entre deux references en double
   Les deux lignes du couple sont montrees ensemble : c'est en les
   comparant qu'on decide laquelle garder. */
.doublon td { border-bottom: 0; }
/* Pas de fond colore : la case cochee dit deja ce qui part a l'import, et
   la ligne ecartee est estompee comme partout ailleurs. */
.doublon__note td {
  padding-top: 0;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--bord);
  color: var(--texte-faible);
  font-size: .75rem;
}
.doublon__note strong { color: var(--alerte); }

/* Compteur d'articles ecartes : present, mais en retrait. */
.compteur--exclu .compteur__valeur { color: var(--texte-faible); }

/* ------------------------------------------------- assistant cartographie
   Un panneau plus large que celui des conversions : il montre le fichier
   ligne a ligne, et c'est sur ces lignes que le mapping se juge. */
.panneau__contenu--large { width: min(1100px, 100%); max-height: min(88vh, 860px); }

.panneau__pied {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 18px;
  border-top: 1px solid var(--bord);
}

.lien-action {
  display: inline-block;
  margin-top: 6px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--accent);
  font: inherit;
  font-size: .8125rem;
  text-align: left;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.lien-action:hover { color: var(--accent-fort); }

.carto__intro, .carto__situation {
  margin-bottom: 8px;
  color: var(--texte-doux);
  font-size: .875rem;
  line-height: 1.5;
}
/* Onglet et ligne d'en-tete sont DEVINES : l'acheteur doit pouvoir corriger
   la detection sans quitter l'ecran. */
.carto__situation {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  color: var(--texte-faible);
  font-size: .8125rem;
}
.carto__situation label { display: inline-flex; align-items: center; gap: 6px; }
.carto__situation select,
.carto__situation input {
  padding: 3px 7px;
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  background: var(--surface);
  color: var(--texte);
  font: inherit;
  font-size: .8125rem;
}
.carto__situation input { width: 64px; }
.carto__situation select:disabled { background: var(--panneau); color: var(--texte-doux); }

.carto__identite {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr;
  gap: 12px;
  margin: 14px 0 4px;
}
.carto__identite .champ { display: flex; flex-direction: column; gap: 4px; }
.carto__identite span { font-size: .8125rem; font-weight: 600; }
.carto__identite small { font-weight: 400; color: var(--texte-faible); }
.carto__identite input {
  padding: 7px 10px;
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  background: var(--surface);
  color: var(--texte);
  font: inherit;
  font-size: .875rem;
}
.carto__identite input[readonly] { background: var(--panneau); color: var(--texte-doux); }

.carto__titre {
  margin: 18px 0 8px;
  font-size: .875rem;
  font-weight: 650;
}
.carto__note { margin-bottom: 6px; color: var(--texte-faible); font-size: .8125rem; }

/* L'attente pendant que le catalogue repart au serveur. Le panneau s'ouvre
   VIDE le temps de l'envoi -- plusieurs secondes sur un gros fichier -- et
   un texte immobile ne dit pas si quelque chose avance. */
.carto__attente {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 28px 4px;
}

.carto__table { width: 100%; border-collapse: collapse; font-size: .8125rem; }
.carto__table th, .carto__table td {
  padding: 7px 10px;
  border-bottom: 1px solid var(--bord);
  text-align: left;
  vertical-align: middle;
}
.carto__table thead th {
  color: var(--texte-faible);
  font-size: .75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .02em;
}
.carto__table tbody th { font-weight: 600; white-space: nowrap; }

.carto__select {
  width: 100%;
  max-width: 340px;
  padding: 6px 8px;
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  background: var(--surface);
  color: var(--texte);
  font: inherit;
  font-size: .8125rem;
}
.carto__select:disabled { background: var(--panneau); color: var(--texte-doux); }

.carto__indices { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.carto__source, .carto__confiance, .carto__requis {
  padding: 1px 6px;
  border: 1px solid var(--bord);
  border-radius: 10px;
  font-size: .6875rem;
  white-space: nowrap;
}
.carto__source { color: var(--texte-doux); background: var(--panneau); }
.carto__confiance { color: var(--texte-faible); }
.carto__requis {
  margin-left: 6px;
  border-color: var(--bord-fort);
  color: var(--texte-doux);
}
.carto__motif { color: var(--texte-faible); font-size: .75rem; }

/* L'apercu porte de vraies lignes produit : le seul moyen de verifier qu'une
   colonne contient bien ce que son libelle annonce. */
.carto__apercu { overflow-x: auto; border: 1px solid var(--bord); border-radius: var(--rayon); }
.carto__apercu table { border-collapse: collapse; font-size: .75rem; white-space: nowrap; }
.carto__apercu th, .carto__apercu td {
  padding: 5px 9px;
  border-bottom: 1px solid var(--bord);
  border-right: 1px solid var(--bord);
  text-align: left;
}
.carto__apercu thead th { background: var(--panneau); font-weight: 600; }
.carto__apercu thead span { font-weight: 400; color: var(--texte-faible); }
.carto__apercu td { font-family: var(--police-donnees); color: var(--texte-doux); }

.carto__yaml pre {
  padding: 12px;
  overflow-x: auto;
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  background: var(--panneau);
  font-family: var(--police-code);
  font-size: .75rem;
  line-height: 1.5;
}

.carto__etat { font-size: .8125rem; color: var(--texte-doux); }
.carto__etat[data-gravite="manque"] { color: var(--alerte); }

@media (max-width: 780px) {
  .carto__identite { grid-template-columns: 1fr; }
}

/* Proposition retenue mais peu sure : elle merite un coup d'oeil a l'apercu. */
.carto__confiance[data-faible="oui"] {
  border-color: var(--alerte-bord);
  color: var(--alerte);
}

/* ------------------------------------------- groupement par categorie
   Deux niveaux : famille, puis sous-famille. Le second se distingue par un
   retrait et une graisse moindre -- pas par une couleur : le tableau en
   compte deja assez. */
.groupe--n2 td { background: transparent; }
.groupe--n2 .groupe__bascule {
  padding-left: 30px;
  font-weight: 500;
  color: var(--texte-doux);
}
.groupe--n2 .groupe__bascule strong { font-weight: 500; }
.groupe--n2 .groupe__compte { background: transparent; border: 1px solid var(--bord); }

/* Le nombre de decimales : meme allure que les bascules voisines, pour que la
   barre de reglages garde une seule grammaire visuelle. */
/* L'etat du format d'un fichier d'import : verifie contre un modele SAP, ou
   propose par le module. Assez visible pour se remarquer, assez discret pour
   ne pas passer avant le nom du fichier. */
.fichier__format {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 3px;
  font-size: .75rem;
  font-weight: 500;
}
.fichier__format[data-confirme="oui"] { color: var(--succes); }
.fichier__format[data-confirme="non"] { color: var(--alerte); }

.commandes__decimales {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: .8125rem;
  color: var(--texte-doux);
  white-space: nowrap;
}
.commandes__decimales select {
  padding: 2px 6px;
  font: inherit;
  color: var(--texte);
  background: var(--surface);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
}

.commandes__bascule {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: .8125rem;
  color: var(--texte-doux);
  cursor: pointer;
  white-space: nowrap;
}
.commandes__bascule[hidden] { display: none; }

/* Sur quoi porte la comparaison : PA brut (defaut), PA net, ou PV.
   Trois choix exclusifs et courts -- un segment plutot qu'une liste
   deroulante : la valeur retenue se lit sans ouvrir quoi que ce soit, et
   l'acheteur bascule d'un clic pour verifier. */
/* Une precaution, pas une alerte : deux bases identiques ne sont pas une
   erreur, c'est la consequence du mode « avec remise ». */
.commandes__note {
  font-size: .75rem;
  color: var(--texte-faible);
  font-style: italic;
  max-width: 28rem;
}
.commandes__note[hidden] { display: none; }

.commandes__base {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  white-space: nowrap;
}
.commandes__base-titre { font-size: .8125rem; color: var(--texte-doux); }

/* Au-dessus des compteurs : c'est lui qui commande ce qu'ils comptent. La
   note explicative le suit sur la meme ligne quand elle s'affiche. */
.commandes__base--haut {
  margin-bottom: 10px;
  flex-wrap: wrap;
  gap: 8px 12px;
}

.segments {
  display: inline-flex;
  padding: 2px;
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  background: var(--panneau);
}
.segments__choix {
  padding: 4px 11px;
  border: 0;
  border-radius: calc(var(--rayon) - 3px);
  background: none;
  color: var(--texte-doux);
  font-size: .8125rem;
  font-weight: 500;
  font-family: inherit;
  cursor: pointer;
  transition: background var(--transition), color var(--transition);
}
.segments__choix:hover { color: var(--texte); }
/* Le choix actif : `aria-pressed` porte l'etat, le style le suit. Un seul
   attribut a tenir a jour, et les lecteurs d'ecran l'annoncent. */
.segments__choix[aria-pressed="true"] {
  background: var(--surface);
  color: var(--texte);
  box-shadow: var(--ombre);
}
.segments__choix:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Un prix qu'on ne peut pas corriger ici : hors du brut, la correction n'a
   pas de sens -- c'est le brut qui part dans SAP. */
.num-lecture { color: var(--texte-doux); cursor: help; }

/* ------------------------------------------------------ remise par code
   Trois etats, distingues par la seule couleur du texte : le tableau en
   compte deja assez pour ne pas y ajouter des aplats. */
.remise {
  padding: 1px 6px;
  border: 1px solid var(--bord);
  border-radius: 10px;
  font-size: .75rem;
  white-space: nowrap;
  cursor: help;
}
.remise--confirmee { color: var(--texte-doux); }
.remise--provisoire,
.remise--divergente,
.remise--inconnue {
  border-color: var(--alerte-bord);
  color: var(--alerte);
}

/* Saisie du taux, en tete du groupe : un geste pour tout le code. */
.saisie-taux td { padding: 0; background: var(--panneau); }
.saisie-taux__corps {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 9px;
  padding: 9px 30px;
  font-size: .8125rem;
  color: var(--texte-doux);
}
.saisie-taux__champ {
  width: 74px;
  padding: 4px 8px;
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  background: var(--surface);
  color: var(--texte);
  font: inherit;
  font-size: .8125rem;
}
.saisie-taux__unite { margin-left: -5px; color: var(--texte-faible); }
.saisie-taux__etat { font-size: .75rem; }
.saisie-taux__etat[data-gravite="erreur"] { color: var(--danger); }

.bouton--petit { padding: 4px 11px; font-size: .8125rem; }

/* Le YAML a recopier dans la table Clabots. */
.bloc-yaml {
  display: inline-block;
  margin-top: 5px;
  padding: 6px 9px;
  border-radius: var(--rayon);
  background: var(--panneau);
  font-family: var(--police-code);
  font-size: .75rem;
  white-space: pre;
}

/* ------------------------------------------------- origine du prix d'achat
   Un prix calcule ne s'accorde pas la meme confiance qu'un prix publie :
   l'acheteur doit savoir dans quel cas il se trouve. */
.origine-prix {
  margin-top: 2px;
  font-size: .8125rem;
  color: var(--texte-faible);
}
.origine-prix strong { color: var(--texte-doux); font-weight: 600; }
.origine-prix[data-source="aucune"] strong,
.origine-prix[data-source="aucune"] { color: var(--alerte); }

/* Menu de l'engrenage : deux ecrans de consultation. */
.menu-reglages {
  position: absolute;
  top: 46px;
  left: 292px;
  z-index: 60;
  min-width: 190px;
  padding: 5px;
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  background: var(--surface);
  box-shadow: var(--ombre-forte);
}
.menu-reglages[hidden] { display: none; }
.menu-reglages button {
  display: block;
  width: 100%;
  padding: 7px 11px;
  border: 0;
  border-radius: 5px;
  background: none;
  color: var(--texte);
  font: inherit;
  font-size: .8125rem;
  text-align: left;
  cursor: pointer;
}
.menu-reglages button:hover { background: var(--panneau); }

/* Liste des fournisseurs : d'ou vient le prix, quels controles. */
.fournisseurs__intro {
  margin-bottom: 12px;
  color: var(--texte-doux);
  font-size: .875rem;
  line-height: 1.5;
}
.fournisseurs__table { width: 100%; border-collapse: collapse; font-size: .8125rem; }
.fournisseurs__table th, .fournisseurs__table td {
  padding: 7px 10px;
  border-bottom: 1px solid var(--bord);
  text-align: left;
  vertical-align: top;
}
/* La colonne d'action. Le bouton reste DISCRET : effacer un profil sert a
   tester la reconnaissance automatique, ce n'est pas le geste courant de
   l'ecran -- et c'est irreversible sur le disque. Il se colore en danger au
   survol, une fois la main dessus. */
/* Un role pose faute de mieux. Assez visible pour appeler un regard, assez
   discret pour ne pas se lire comme une erreur : l'onglet est ecarte, ce qui
   est sur -- il reste a confirmer qu'il n'apporte rien. */
.carto__role-mesure--defaut { color: var(--alerte); }

.fournisseurs__action { text-align: right; white-space: nowrap; }
/* Deux boutons cote a cote : sans cet espace ils se touchent, et « Ouvrir »
   se clique a la place d'« Effacer ». */
.fournisseurs__action .bouton + .bouton { margin-left: .375rem; }

/* La date de derniere MAJ. Chiffres alignes en colonne : des dates de
   largeurs differentes se comparent mal a l'oeil. */
.fournisseurs__maj {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* Une date de PROFIL ne dit pas qu'un import a eu lieu -- seulement que le
   YAML a ete retouche. Elle s'affiche en retrait pour qu'on ne la lise pas
   comme un traitement. */
.fournisseurs__maj[data-origine="profil"] { color: var(--texte-faible); }
.fournisseurs__effacer { padding: 3px 8px; font-size: .75rem; }
.fournisseurs__effacer:hover:not(:disabled) {
  background: var(--danger-fond);
  color: var(--danger);
}
.fournisseurs__effacer:disabled { opacity: .5; cursor: wait; }

.fournisseurs__table thead th {
  color: var(--texte-faible);
  font-size: .75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .02em;
}
.fournisseurs__table tbody th { font-weight: 600; white-space: nowrap; }
.fournisseurs__detail { color: var(--texte-doux); }
.fournisseurs__source { white-space: nowrap; }
.fournisseurs__source[data-source="code"],
.fournisseurs__source[data-source="calcul"] { color: var(--accent); font-weight: 600; }
.fournisseurs__source[data-source="aucune"] { color: var(--alerte); font-weight: 600; }
.fournisseurs__controle {
  display: inline-block;
  padding: 1px 6px;
  border: 1px solid var(--bord);
  border-radius: 10px;
  font-size: .6875rem;
  color: var(--texte-doux);
}
.fournisseurs__erreur { color: var(--danger); }

/* --------------------------------------------------- colonnes masquables
   Masquer par CSS plutot que reconstruire le tableau : le tri, les filtres
   et la selection restent intacts, et le geste est instantane. */
.cadre-tableau[data-sans-remise="oui"] [data-colonne="remise"],
.cadre-tableau[data-sans-code-sap="oui"] [data-colonne="code-sap"] { display: none; }
.cadre-tableau[data-sans-code-stock="oui"] [data-colonne="code-stock"] { display: none; }
.cadre-tableau[data-sans-unites="oui"] [data-colonne="unites"] {
  display: none;
}

/* ------------------------------------------- conversion d'une reference
   La cellule est un bouton : cliquer dessus definit le facteur. */
button.conversion {
  border: 1px solid var(--bord);
  background: none;
  font: inherit;
  cursor: pointer;
}
button.conversion:hover { border-color: var(--bord-fort); background: var(--panneau); }

.conversion-ligne__faits { width: 100%; margin: 12px 0; border-collapse: collapse; font-size: .8125rem; }
.conversion-ligne__faits th,
.conversion-ligne__faits td { padding: 5px 0; border-bottom: 1px solid var(--bord); text-align: left; }
.conversion-ligne__faits th { width: 42%; font-weight: 500; color: var(--texte-faible); }

.conversion-ligne__saisie { display: flex; flex-direction: column; gap: 5px; margin-top: 14px; }
.conversion-ligne__saisie span { font-size: .8125rem; font-weight: 600; }
.conversion-ligne__saisie input {
  max-width: 160px;
  padding: 7px 10px;
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  background: var(--surface);
  color: var(--texte);
  font: inherit;
}
.conversion-ligne__saisie small { color: var(--texte-faible); font-size: .75rem; line-height: 1.45; }
.conversion-ligne__apercu {
  margin-top: 12px;
  padding: 9px 11px;
  border-radius: var(--rayon);
  background: var(--panneau);
  font-size: .8125rem;
  color: var(--texte-doux);
}
.conversion-ligne__apercu[data-gravite="erreur"] { color: var(--danger); }

/* -------------------------------------- lecture de l'export SAP en cours
   Vingt secondes sans signe visible font croire a un ecran fige : l'acheteur
   reclique, ou change d'avis. La rondelle dit que le travail avance. */
.lecture__titre .rondelle {
  display: inline-block;
  vertical-align: -2px;
  margin-right: 7px;
  color: var(--accent);
}

/* Compteurs fantomes : la place est tenue, l'ecran ne saute pas quand les
   vrais chiffres arrivent. */
.compteur--fantome span {
  display: block;
  height: 10px;
  border-radius: 4px;
  background: linear-gradient(90deg, var(--panneau) 25%, var(--surface-2) 50%, var(--panneau) 75%);
  background-size: 200% 100%;
  animation: glisser 1.4s ease-in-out infinite;
}
.compteur--fantome .compteur__libelle { width: 76%; }
.compteur--fantome .compteur__valeur { width: 46%; height: 15px; margin-top: 7px; }
@media (prefers-reduced-motion: reduce) {
  .compteur--fantome span { animation: none; }
}

/* Colonnes de pastilles (« Remise », « Conversion ») : leur contenu n'est pas
   un nombre mais une etiquette. Aligner a droite comme un prix la collait au
   bord, et l'en-tete restait a gauche -- deux reperes qui se contredisent. */
.pastille { text-align: center; white-space: nowrap; }

/* ------------------------------------------ table des conversions, editable
   Huit colonnes : la table se lit sans avoir a ouvrir le YAML. */
.conversions__cadre { overflow-x: auto; }
.conversions__table th, .conversions__table td { white-space: nowrap; }
.conversions__table .designation {
  max-width: 240px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.conversions__fichier {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
/* Le chemin du fichier peut etre long : il cede la place aux boutons plutot
   que de les repousser a la ligne suivante. */
.conversions__fichier > code {
  flex: 1 1 260px;
  min-width: 0;
  overflow-wrap: anywhere;
}
.conversions__actions { display: flex; gap: 10px; }
.bouton-lien--danger { color: var(--danger); }

/* Formulaire d'ajout ou de modification. */
.edition-conversion {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin-bottom: 12px;
}
.edition-conversion .champ,
#corps-edition-conversion > .champ { display: flex; flex-direction: column; gap: 4px; }
#corps-edition-conversion span { font-size: .8125rem; font-weight: 600; }
#corps-edition-conversion small { font-weight: 400; color: var(--texte-faible); }
#corps-edition-conversion input,
#corps-edition-conversion select {
  padding: 7px 10px;
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  background: var(--surface);
  color: var(--texte);
  font: inherit;
  font-size: .875rem;
}
#corps-edition-conversion input[readonly],
#corps-edition-conversion select:disabled {
  background: var(--panneau);
  color: var(--texte-doux);
}

/* Ce que la reference designe reellement dans SAP : sans cette verification,
   une faute de frappe creerait une regle qui ne s'applique jamais. */
.edition-conversion__article {
  min-height: 20px;
  margin: 4px 0 14px;
  padding: 8px 11px;
  border-radius: var(--rayon);
  font-size: .8125rem;
  line-height: 1.45;
}
.edition-conversion__article[data-etat="attente"] { color: var(--texte-faible); }
.edition-conversion__article[data-etat="connu"] {
  background: var(--panneau);
  color: var(--texte-doux);
}
.edition-conversion__article[data-etat="inconnu"] {
  background: var(--alerte-fond);
  color: var(--alerte);
  border: 1px solid var(--alerte-bord);
}

@media (max-width: 700px) {
  .edition-conversion { grid-template-columns: 1fr; }
}
.edition-conversion__article small {
  display: block;
  margin-top: 3px;
  font-weight: 400;
  font-size: .75rem;
  color: var(--texte-faible);
}

/* Compte rendu d'un import de remises : ce qui a ete ajoute, et surtout ce
   qui a ete REMPLACE -- un taux ecrase doit rester retrouvable. */
.conversions__aide {
  margin: 8px 0 0;
  font-size: .8125rem;
  line-height: 1.5;
  color: var(--texte-doux);
}
.conversions__aide:empty { margin: 0; }
.conversions__aide[data-gravite="succes"] { color: var(--succes); }
.conversions__aide[data-gravite="attention"] {
  padding: 8px 11px;
  border: 1px solid var(--alerte-bord);
  border-radius: var(--rayon);
  background: var(--alerte-fond);
  color: var(--alerte);
}
.conversions__aide[data-gravite="erreur"] { color: var(--danger); }
.conversions__aide code {
  padding: 1px 5px;
  border-radius: 4px;
  background: var(--panneau);
  font-family: var(--police-code);
  font-size: .75rem;
}

/* --- Formats d'export ---------------------------------------------------
   Un onglet par type d'action, un tableau de colonnes, un apercu. Les
   couleurs restent celles du theme : ce qui distingue une ligne reglee d'une
   ligne a regler doit tenir a la lisibilite, pas a la decoration. */

.onglets {
  display: flex;
  gap: 2px;
  margin: 12px 0 4px;
  border-bottom: 1px solid var(--bord);
}

.onglet {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 8px 14px;
  border: 1px solid transparent;
  border-bottom: none;
  border-radius: 4px 4px 0 0;
  background: none;
  color: var(--texte-doux);
  font: inherit;
  cursor: pointer;
}

.onglet:hover { background: var(--surface-2); color: var(--texte); }

.onglet[aria-current="true"] {
  background: var(--surface);
  border-color: var(--bord);
  color: var(--texte);
  font-weight: 600;
  /* Recouvre la bordure du conteneur : l'onglet actif se lit comme
     rattache a son contenu. */
  margin-bottom: -1px;
  padding-bottom: 9px;
}

.onglet__compte {
  font-size: 0.82em;
  font-weight: 400;
  color: var(--texte-faible);
}

.pastille-origine {
  display: inline-block;
  padding: 1px 7px;
  border: 1px solid var(--bord);
  border-radius: 10px;
  background: var(--surface-2);
  font-size: 0.85em;
  color: var(--texte-doux);
}

/* Une colonne sans regle laissera une cellule vide : la marque doit se voir
   en balayant le tableau, sans avoir a lire chaque ligne. */
.ligne--a-regler td:first-child {
  border-left: 3px solid var(--alerte-bord);
}

.formats__reste { color: var(--alerte); }
.formats__pret { color: var(--succes); }

.apercu { margin: 10px 0 16px; }
.apercu__cadre { overflow-x: auto; border: 1px solid var(--bord); border-radius: 4px; }

.apercu__table { font-size: 0.88em; }
.apercu__table th { vertical-align: top; white-space: nowrap; }
.apercu__table td { white-space: nowrap; }

.apercu__metier {
  display: block;
  font-weight: 400;
  font-size: 0.85em;
  color: var(--texte-faible);
}

/* Le chemin de la table est une reference, pas le sujet de l'ecran : il ne
   doit pas prendre toute la largeur ni pousser les boutons a la ligne. */
.chemin-long {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
  direction: rtl;   /* la fin du chemin -- le nom du fichier -- reste lisible */
  text-align: left;
}

/* --- Etat du format d'un fournisseur ------------------------------------
   Deux choses differentes, que l'ecran confondait : « absent de SAP » (la
   case du bas) et « format jamais lu » (cette pastille). La couleur ne porte
   jamais l'information seule -- le texte de l'entree la repete. */



/* La pastille doit se voir en balayant une liste de 40 lignes : la couleur
   seule ne suffit pas, et un daltonien ne la distingue pas du tout. Elle
   porte donc aussi une FORME -- pleine ou creuse -- et son infobulle. */
.pastille-format {
  display: inline-block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  font-size: 0;               /* le point sert de gabarit, pas de glyphe */
  vertical-align: baseline;
  margin-right: 6px;
}

.pastille-format--connu {
  background: var(--succes);
  border: 1px solid var(--succes);
}

.pastille-format--inconnu {
  background: none;           /* creuse : lisible sans distinguer la couleur */
  border: 2px solid var(--alerte);
}


/* --- Etape 1 : l'ecran se resserre une fois le fichier lu ---------------
   Avant lecture, la zone de depot doit APPELER le geste : elle est grande,
   centree, avec son icone. Apres, elle n'a plus qu'a dire quel fichier est
   retenu -- 188 px pour un nom de fichier repoussaient le compte rendu et le
   bouton hors de l'ecran (1 315 px de contenu pour 709 px de fenetre).

   La zone reste cliquable : changer de fichier doit rester possible, mais
   discretement. */

.vue[data-lu="oui"] .depot--pleine-largeur {
  min-height: 0;
  margin-bottom: 12px;
}

.vue[data-lu="oui"] .depot {
  flex-direction: row;
  justify-content: flex-start;
  gap: 10px;
  min-height: 0;
  padding: 10px 14px;
  text-align: left;
}

/* L'icone et l'invitation ont fait leur office : les garder occuperait la
   place du compte rendu, qui est desormais le sujet de l'ecran. */
.vue[data-lu="oui"] .depot__icone,
.vue[data-lu="oui"] .depot__titre {
  display: none;
}

/* Le sous-titre explique quoi faire ; une fois fait, il n'apprend plus rien. */
.vue[data-lu="oui"] .titre-vue p {
  display: none;
}

/* La case « pas encore dans SAP » se decide AVANT de deposer : apres lecture,
   elle n'est plus qu'un rappel. On la garde -- l'acheteur peut se raviser --
   mais resserree. */
.vue[data-lu="oui"] .case-ligne {
  padding: 8px 12px;
  margin-bottom: 12px;
}

/* L'apercu des lignes retenues sert a VERIFIER que la lecture est juste : on
   le regarde une fois, pas a chaque import. Replie, il rend 300 px a l'ecran
   et ramene le bouton dans la fenetre.

   Les categories qui demandent une DECISION -- doublons a departager,
   anomalies a trancher -- ne sont jamais repliees : les cacher ferait passer
   un choix pour un detail. C'est `data-decision` qui les distingue. */

.lecture__apercu[data-repliable="oui"]:not([data-ouvert="oui"]) table,
.lecture__apercu[data-repliable="oui"]:not([data-ouvert="oui"]) .apercu__bilan {
  display: none;
}

.apercu__bascule {
  border: 0;
  background: none;
  padding: 2px 6px;
  margin-left: 8px;
  font: inherit;
  font-size: .8125rem;
  color: var(--accent);
  cursor: pointer;
  border-radius: 4px;
}
.apercu__bascule:hover { background: var(--accent-doux); }
.apercu__bascule:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Le bas de page reserve 64 px pour detacher le bouton du bord. Utile quand
   le contenu remplit l'ecran ; du vide gaspille quand il ne le remplit pas.
   Une fois le fichier lu, l'ecran doit tenir : on ramene la marge a ce qui
   suffit pour que le bouton ne colle pas au bord. */
.vue[data-lu="oui"] {
  padding-bottom: 24px;
}

/* Le libelle « Fournisseur existant » et l'aide sous le champ expliquent un
   choix DEJA fait : le nom retenu est dans le champ, sous les yeux. */
.vue[data-lu="oui"] #bloc-fournisseur label,
.vue[data-lu="oui"] #aide-fournisseur {
  display: none;
}
.vue[data-lu="oui"] #bloc-fournisseur {
  margin-bottom: 10px;
}

/* --- Wizard de mapping : ce qui bloque, ce qui doute --------------------
   Deux etats, deux couleurs, et jamais la couleur seule : la colonne de
   droite dit toujours POURQUOI la ligne est signalee.

   Depuis que le tableau part du FICHIER, une ligne est une colonne -- elle ne
   peut donc plus « manquer ». Le rouge a suivi le sens : il vit sous le
   tableau, sur les champs qu'aucune colonne n'alimente.

   orange -- une proposition sous le seuil : ca passe, mais ca merite un
             regard sur l'extrait a gauche */

.carto__ligne[data-etat="doute"] > * {
  background: var(--alerte-fond);
}
.carto__ligne[data-etat="doute"] th {
  box-shadow: inset 3px 0 0 var(--alerte);
}

/* La colonne du fichier : sa lettre, son en-tete, et deux valeurs reelles.
   L'extrait est ce qui tranche quand deux libelles se ressemblent -- « Prix
   tarif » et « Nouveau prix tarif » ne se distinguent que la. */
.carto__table--fichier tbody th {
  white-space: normal;
  max-width: 22rem;
}
.carto__lettre {
  display: inline-block;
  min-width: 1.9rem;
  margin-right: 6px;
  padding: 1px 5px;
  border-radius: 3px;
  background: var(--panneau);
  color: var(--texte-faible);
  font-size: .6875rem;
  font-weight: 600;
  text-align: center;
}
.carto__extrait {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-top: 2px;
  padding-left: calc(1.9rem + 6px);
  color: var(--texte-faible);
  font-size: .6875rem;
  font-weight: 400;
  overflow-wrap: anywhere;
}

/* Les champs qu'aucune colonne n'alimente. C'est le seul endroit ou le rouge
   subsiste : un champ obligatoire oublie n'a plus de ligne dans le tableau,
   il ne se verrait donc nulle part ailleurs. */
.carto__absents {
  margin-top: 12px;
  padding: 10px 12px;
  border-radius: 6px;
  background: var(--panneau);
}
.carto__absents h4 {
  margin: 0 0 6px;
  font-size: .75rem;
  font-weight: 600;
  color: var(--texte-faible);
  text-transform: uppercase;
  letter-spacing: .04em;
}
.carto__absents ul {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.carto__absent {
  padding: 2px 8px;
  border-radius: 3px;
  background: var(--surface);
  color: var(--texte-faible);
  font-size: .75rem;
}
.carto__absent[data-manque="oui"] {
  background: var(--danger-fond);
  color: var(--texte);
  box-shadow: inset 2px 0 0 var(--danger);
}
/* « obligatoire » n'a de sens que si le champ manque VRAIMENT. Les deux prix
   le sont ENSEMBLE : des que l'un est servi, l'autre cesse d'etre un manque,
   et le mot doit disparaitre sans qu'on redessine le rappel. */
.carto__absent[data-manque="non"] strong { display: none; }

/* Sur quoi le module s'appuie. Les libelles seuls trouvent la reference 8 fois
   sur 11 ; le rapprochement SAP la trouve 10 fois. La difference change ce que
   l'acheteur doit relire -- le bandeau dit donc lequel a parle, et le dit AUSSI
   quand personne n'a pu verifier. */
.carto__juge {
  margin: 0 0 12px;
  padding: 8px 12px;
  border-radius: 6px;
  font-size: .8125rem;
  line-height: 1.5;
}
.carto__juge[data-fonde="sap"] {
  background: var(--succes-fond);
  color: var(--texte);
  box-shadow: inset 3px 0 0 var(--succes);
}
.carto__juge[data-fonde="libelles"] {
  background: var(--alerte-fond);
  color: var(--texte);
  box-shadow: inset 3px 0 0 var(--alerte);
}

/* Trois sections, dans l'ordre du parcours : ce qu'on lit du fichier, ce
   qu'on en tire, de quoi verifier. Sans separation, l'ecran etait une suite
   de tableaux sans hierarchie. */
/* Comment le fournisseur cote ses prix. Deux facons, jamais les deux -- et
   le mode commande ce que le mapping EXIGE : le tarif seul en mode remise,
   les deux prix sinon. */
/* Meme cadre que `.carto__section` -- voir « Un seul motif de section ».
   Le trait de separation datait d'une epoque ou ce bloc vivait DANS une
   section ; depuis le decoupage en onglets, il est au meme niveau que les
   cartes, et le trait creait une rupture de rythme. */
.carto__mode {
  display: grid;
  gap: 8px;
  padding: 14px 16px;
  margin-bottom: 14px;
  background: var(--surface);
  border: 1px solid var(--bord);
  border-radius: var(--rayon-l);
}
/* Le choix actif ressort ; l'autre reste lisible, donc choisissable, mais ne
   retient pas le regard. */
.carto__mode[data-mode="avec_remise"] .case-ligne:has(input[value="sans_remise"]),
.carto__mode[data-mode="sans_remise"] .case-ligne:has(input[value="avec_remise"]) {
  opacity: .55;
}
.carto__mode .case-ligne b { font-weight: 600; color: var(--texte); }
/* « regle generale » : la cotation la plus repandue chez les fournisseurs.
   Discret, car c'est une indication, pas une contrainte.

   Titre et mention vivent dans une LIGNE a part. Les mettre cote a cote dans
   le meme `strong` ne suffisait pas : le conteneur est un flex en colonne, et
   tout enfant direct y est blockifie -- `display: inline` reste sans effet. */
.case-ligne .carto__mode-titre {
  /* Selecteur plus fort que `.case-ligne span`, qui impose `column` a TOUS
     les span descendants -- y compris celui-ci. */
  flex-direction: row;
  align-items: baseline;
  gap: 6px;
}
.carto__mode-note {
  font-style: normal;
  font-weight: 400;
  font-size: .8125rem;
  color: var(--texte-faible);
}

.carto__edi {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: .8125rem;
  color: var(--texte-doux);
}
.carto__edi input { width: 6rem; }

/* D'ou vient l'unite d'un prix : declaree pour tout le fichier, ou lue dans
   une colonne. Les deux s'excluent -- le mode grise celle qui ne sert pas,
   pour qu'aucune valeur ne reste renseignee sans effet. */
/* UNE colonne, comme le bloc de cotation.
   La grille en portait deux : le libelle a gauche, son menu a droite, hors
   du cadre de l'option. Depuis que le menu vit DANS la ligne cochee -- pour
   que le cadre gris l'englobe -- la seconde colonne n'a plus d'occupant, et
   la premiere se comprimait a un mot par ligne. */
.carto__unite {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 8px;
  padding: 14px 16px;
  margin-bottom: 14px;
  background: var(--surface);
  border: 1px solid var(--bord);
  border-radius: var(--rayon-l);
}
.carto__unite-titre {
  grid-column: 1 / -1;
  /* La taille de `.carto__titre` : deux tailles pour le meme niveau
     hierarchique se lisent comme deux niveaux. */
  font-size: .875rem;
  font-weight: 650;
  color: var(--texte);
}
/* « obligatoire » se lit, il n'est pas seulement rouge : la couleur ne porte
   jamais l'information seule. */
.carto__unite-titre em {
  margin-left: 6px;
  color: var(--danger);
  font-style: normal;
  font-size: .75rem;
  font-weight: 500;
}
.carto__unite select { min-width: 13rem; }
.carto__unite select:disabled { opacity: .5; }
/* La branche inactive s'efface sans disparaitre : elle reste lisible, donc
   choisissable, mais ne retient pas le regard. */
.carto__unite[data-mode="fixe"] .case-ligne:has(input[value="colonne"]),
.carto__unite[data-mode="colonne"] .case-ligne:has(input[value="fixe"]) {
  opacity: .55;
}
.case-ligne--serree { margin-bottom: 0; padding: 8px 12px; }

.carto__section {
  padding: 14px 16px;
  margin-bottom: 14px;
  background: var(--surface);
  border: 1px solid var(--bord);
  border-radius: var(--rayon-l);
}
.carto__section .carto__titre { margin-top: 0; }
.carto__aide {
  margin: 0 0 10px;
  color: var(--texte-faible);
  font-size: .8125rem;
}

/* --------------------------------------------------- roles des onglets */
/* Huit catalogues sur quatorze ont plusieurs onglets. Jusqu'ici un seul
   comptait, et les autres etaient ignores en silence -- il fallait ecrire le
   YAML a la main pour declarer la table de remises de BOSCH. */
.carto__roles {
  display: grid;
  gap: 8px;
  padding: 14px 16px;
  margin-bottom: 14px;
  background: var(--surface);
  border: 1px solid var(--bord);
  border-radius: var(--rayon-l);
}
.carto__roles-table { width: 100%; border-collapse: collapse; font-size: .8125rem; }
.carto__roles-table th {
  padding: 4px 8px 6px;
  text-align: left;
  font-size: .6875rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--texte-faible);
  border-bottom: 1px solid var(--bord);
}
.carto__roles-table td { padding: 7px 8px; border-bottom: 1px solid var(--bord); vertical-align: top; }
.carto__roles-table tr:last-child td { border-bottom: none; }
.carto__roles-table select { width: 100%; max-width: 220px; }

/* Le nombre de lignes et de colonnes : c'est souvent lui qui dit de quel
   onglet il s'agit, quand le nom ne le dit pas (« Blad1 », « Feuille 14 »). */
.carto__role-mesure {
  display: block;
  margin-top: 2px;
  font-size: .75rem;
  color: var(--texte-faible);
}

/* « lu » ou « enregistre seulement » : deux roles seulement agissent. Le taire
   ferait attendre un traitement qui n'existe pas. */
.carto__role-etat {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 999px;
  font-size: .6875rem;
  font-weight: 600;
  background: var(--panneau);
  color: var(--texte-faible);
  border: 1px solid var(--bord);
}
.carto__role-etat--actif {
  background: var(--succes-fond);
  color: var(--succes);
  border-color: var(--succes-bord);
}
.carto__role-aide {
  display: block;
  margin-top: 3px;
  font-size: .75rem;
  color: var(--texte-faible);
}

/* --------------------------------------- fiche de configuration fournisseur */
/* Quatre onglets plutot qu'une colonne de sections. L'ecran portait tout d'un
   bloc : chez BOSCH, 47 lignes de correspondance separaient le nom du
   fournisseur du premier prix, et le bandeau du bas annoncait des manques
   dont on ne voyait plus le champ.

   Le motif d'onglets est celui des formats d'export -- meme classe `.onglet`,
   meme comportement. En inventer un second ferait deux grammaires dans le
   meme ecran. */
.carto__onglets {
  position: sticky;
  top: 0;
  z-index: 2;
  /* Le corps du panneau defile SOUS la barre : sans fond opaque, les lignes
     du tableau la traverseraient. */
  background: var(--surface);
  margin: 0 0 16px;
  padding-top: 2px;
}

/* Un onglet inactif ne se clique pas, et dit pourquoi au survol. Le laisser
   cliquable ouvrirait un ecran vide ; le masquer ferait varier la barre selon
   le fichier, et personne ne decouvrirait que la fonction existe. */
.carto__onglets .onglet:disabled {
  opacity: .45;
  cursor: not-allowed;
}
.carto__onglets .onglet:disabled:hover { background: none; color: var(--texte-doux); }

/* Un seul motif de section dans toute la fiche : une carte encadree.
   Trois blocs -- roles des onglets, cotation, unite -- etaient separes par un
   simple trait, heritage d'une epoque ou ils vivaient DANS une section. Au
   meme niveau que les cartes, le trait creait une rupture de rythme : deux
   grammaires pour un seul niveau hierarchique. */
.carto__panneau > :first-child { margin-top: 0; }

/* Note sous un champ : ce que la valeur signifie, ou d'ou elle vient. */
.champ__note {
  display: block;
  margin-top: 4px;
  font-size: .75rem;
  color: var(--texte-faible);
}

/* Le facteur de conversion : un nombre qu'on compare d'une ligne a l'autre. */
.carto__facteur { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Un etat vide qui dit ce qui se passera, pas seulement qu'il n'y a rien. */
.carto__vide {
  padding: 14px 16px;
  border: 1px dashed var(--bord);
  border-radius: var(--rayon);
  color: var(--texte-faible);
  font-size: .8125rem;
}

/* Un manque nomme l'onglet qui le porte, et y mene. Depuis le decoupage en
   onglets, ce qui manque n'est plus forcement a l'ecran : le lire sans
   pouvoir y aller obligerait a chercher lequel des quatre le contient. */
.lien-manque {
  padding: 0;
  border: none;
  background: none;
  color: inherit;
  font: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.lien-manque:hover { text-decoration-thickness: 2px; }
.lien-manque:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }

/* --------------------------------- le choix imbrique dans une case-ligne */
/* Le menu vit DANS la ligne cochee, aligne a droite : le sortir du cadre
   laissait croire a un reglage independant, alors qu'il ne s'applique qu'a
   cette option. Le cadre gris englobe donc son menu. */
.case-ligne--avec-choix { align-items: center; }
.case-ligne--avec-choix > span:not(.carto__provenance) { flex: 1; min-width: 0; }

.carto__provenance {
  /* Selecteur plus fort que `.case-ligne span`, qui impose `column` a TOUS
     les span descendants -- ce menu compris. */
  flex-direction: column;
  align-items: flex-end;
  gap: 3px;
  flex: 0 0 auto;
  margin-left: auto;
}
.carto__provenance select { min-width: 12rem; }

/* L'aide sous le menu : ce que la provenance choisie implique. Sans elle,
   « Code de remise » ne dit pas qu'il faut AUSSI une table code -> taux. */
.carto__provenance-aide {
  max-width: 15rem;
  color: var(--texte-faible);
  font-size: .75rem;
  line-height: 1.35;
  text-align: right;
}

/* Un menu grise ne se lit pas comme un menu vide : l'option ne s'applique
   pas encore, elle n'est pas a remplir. */
.carto__provenance select:disabled {
  background: var(--panneau);
  color: var(--texte-faible);
  cursor: not-allowed;
}

/* Le taux general, sous son propre choix. Il vivait dans « Identite » -- avec
   le nom et le code SAP -- alors que c'est un reglage de COTATION : il ne
   s'applique que si la remise vient d'un taux unique. Le laisser saisissable
   en face d'une colonne de taux faisait renseigner une valeur sans effet. */
.carto__taux-global {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 2px;
}
.carto__taux-global > span {
  /* Selecteur plus fort que `.case-ligne span`, qui impose `column`. */
  flex-direction: row;
  gap: 4px;
  font-size: .8125rem;
  color: var(--texte-doux);
  white-space: nowrap;
}
.carto__taux-global input {
  width: 5rem;
  padding: 4px 8px;
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  background: var(--surface);
  color: var(--texte);
  font: inherit;
  font-size: .8125rem;
  text-align: right;
}

/* ------------------------------------ plusieurs onglets de catalogue */
/* Chez CARAT, quatre onglets : « Update tarif general » porte 24 colonnes,
   « Importfile Promo » 28, « Nouveaute » 20. Trois structures, trois
   mappings -- un seul jeu de colonnes ne peut pas decrire les trois. */
.carto__sous-onglets { margin-bottom: 14px; }
.carto__sous-onglets .onglets { margin-top: 6px; }

/* Un catalogue dont la structure differe et qui n'a pas encore SON mapping.
   Le signaler ici evite la faute silencieuse : appliquer le mapping du
   principal a cet onglet rendrait zero ligne, sans erreur. */
.carto__sous-onglet-alerte { color: var(--alerte); font-weight: 600; }

.carto__role-mesure--alerte {
  color: var(--alerte);
  font-weight: 600;
}

/* ------------------------------------- ce qui manque au profil fournisseur */
/* L'ecran affichait « 1 796 articles retenus » et un bouton actif, alors que
   le profil ne declarait ni cotation ni unite : le module appliquait un
   defaut jamais confirme -- « PIECE » en dur, et le prix brut s'il existe. */
.manques-profil {
  margin-top: 14px;
  padding: 14px 16px;
  border: 1px solid var(--alerte-bord);
  border-radius: var(--rayon-l);
  background: var(--alerte-fond);
  font-size: .8125rem;
}
.manques-profil[data-gravite="bloquant"] {
  border-color: var(--danger-bord);
  background: var(--danger-fond);
}
.manques-profil > strong { display: block; margin-bottom: 8px; color: var(--texte); }
.manques-profil ul { margin: 0 0 12px; padding-left: 18px; }
.manques-profil li { margin-bottom: 6px; }
.manques-profil li strong { display: block; color: var(--texte); font-weight: 600; }
/* Le DETAIL compte plus que le titre : « unite non declaree » n'alarme
   personne, « le module ecrira PIECE en dur » se comprend tout de suite. */
.manques-profil li span { color: var(--texte-doux); }
/* Un manque qui BLOQUE se distingue de celui qui avertit : sinon l'acheteur
   corrige au hasard jusqu'a ce que le bouton s'active. */
.manques-profil li[data-bloquant="oui"] strong::after {
  content: " — bloquant";
  color: var(--danger);
  font-weight: 600;
}

/* ------------------------------------------- reglage replie, sous un tableau */
/* Un reglage que la plupart des catalogues n'emploient pas : le marqueur de
   suppression. L'afficher ouvert allongerait un ecran deja dense ; le cacher
   dans une autre section le rendrait introuvable -- il decrit une COLONNE du
   catalogue, sa place est sous le tableau des colonnes. */
.carto__repli {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--bord);
}
.carto__repli summary {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: .8125rem;
  font-weight: 600;
  color: var(--texte);
  cursor: pointer;
}
.carto__repli summary:hover { color: var(--accent); }
/* L'etat courant se lit SANS ouvrir : « aucun » ou la regle en clair. Sans
   lui, il faut deplier chaque fois pour savoir si quelque chose est regle. */
.carto__repli-etat { font-weight: 400; color: var(--texte-faible); }
.carto__repli[open] summary { margin-bottom: 10px; }
/* Le dernier element d'un repli ne doit pas laisser sa marge sous lui : elle
   s'ajoute au padding de la section, et la bordure semble flotter loin du
   texte -- 25 px au lieu de 14. */
.carto__repli > :last-child { margin-bottom: 0; }

/* Le comptage du marqueur : une action de VERIFICATION, pas un reglage.
   Elle vit sous les deux champs qu'elle mesure, alignee a gauche comme eux. */
.carto__compter {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 10px;
  flex-wrap: wrap;
}
.carto__compter-resultat {
  font-size: .8125rem;
  color: var(--texte-faible);
}
/* Le compte lui-meme ressort : c'est le chiffre qu'on est venu chercher. */
.carto__compter-resultat strong { color: var(--texte); }
/* Zero article reconnu n'est pas une erreur, mais merite un regard : la regle
   est peut-etre juste, ou la valeur mal orthographiee. */
.carto__compter-resultat[data-vide="oui"] strong { color: var(--alerte); }

/* Un lien discret sous un tableau : « afficher les colonnes vides ». Il ouvre
   une possibilite sans peser -- ce n'est pas une action courante. */
.lien-discret {
  margin-top: 10px;
  padding: 0;
  border: none;
  background: none;
  color: var(--texte-faible);
  font: inherit;
  font-size: .75rem;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.lien-discret:hover { color: var(--accent); }
.lien-discret:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ----------------------------------------------------------------------- */
/* Ecran d'entree                                                          */
/*                                                                          */
/* Une colonne unique, centree verticalement, sur le fond de page. Pas de   */
/* carte posee sur un fond colore : l'ecran n'a qu'une chose a faire, et    */
/* l'encadrer ajouterait une frontiere sans contenu de l'autre cote.        */
/* ----------------------------------------------------------------------- */
.connexion {
  position: fixed;
  inset: 0;
  z-index: 1000;
  overflow-y: auto;
  display: grid;
  place-items: center;
  padding: 40px 24px;
  background: var(--ground);
}

/* `min-width: 0` sur l'element de grille.

   Sans lui, sa largeur minimale est celle de son CONTENU : le titre
   « Import des catalogues fournisseurs » refusait de se couper et poussait la
   colonne au-dela de la fenetre. La page debordait a droite et le bouton
   « Ouvrir » sortait de l'ecran -- visible seulement sur une capture etroite,
   jamais en lisant le code. C'est le defaut le plus courant des elements de
   grille et de flex, et le plus discret. */
.connexion > * { min-width: 0; }
.connexion[hidden] { display: none; }

.connexion__cadre {
  width: 100%;
  min-width: 0;
  max-width: 460px;
}

.connexion__logo {
  display: block;
  height: 30px;
  width: auto;
  margin-bottom: 40px;
}

.connexion__intro { margin-bottom: 28px; }

.connexion__titre {
  margin: 0 0 6px;
  font-size: 1.45rem;
  font-weight: 600;
  /* Sous -0.02em les capitales de « SAP » se resserrent trop. */
  letter-spacing: -0.018em;
  line-height: 1.25;
  color: var(--texte);
  text-wrap: balance;
}

.connexion__accroche {
  margin: 0;
  max-width: 46ch;
  font-size: 0.94rem;
  line-height: 1.55;
  color: var(--texte-doux);
}

.connexion__form { margin-bottom: 32px; }

.connexion__label {
  display: block;
  margin-bottom: 7px;
  font-size: 0.82rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--texte-doux);
}

/* Champ et bouton sur une ligne : une seule saisie, un seul geste. En
   dessous de 420px ils passent l'un sous l'autre -- un bouton de 90px a cote
   d'un champ de 120px ne se remplit pas au pouce. */
.connexion__saisie {
  display: flex;
  gap: 8px;
}

.connexion__champ {
  flex: 1 1 auto;
  min-width: 0;
  padding: 11px 13px;
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  background: var(--surface);
  color: var(--texte);
  font: inherit;
  font-size: 0.95rem;
  transition: border-color var(--transition), box-shadow var(--transition);
}
.connexion__champ::placeholder { color: var(--texte-faible); }
.connexion__champ:focus {
  outline: 0;
  border-color: var(--accent);
  /* Un halo de focus, pas une ombre portee : il double le contour sans
     donner au champ une elevation qu'il n'a pas. */
  box-shadow: 0 0 0 3px var(--accent-doux);
}

.connexion__valider {
  flex: 0 0 auto;
  padding: 11px 22px;
  border: 1px solid transparent;
  border-radius: var(--rayon);
  background: var(--accent);
  /* La variable, jamais un blanc litteral : le theme sombre pourrait en
     vouloir un autre, et un test du projet refuse les couleurs en dur --
     y compris CITEES DANS UN COMMENTAIRE, ce que sa regex ne distingue pas. */
  color: var(--accent-texte);
  font: inherit;
  font-size: 0.95rem;
  font-weight: 500;
  cursor: pointer;
  transition: background var(--transition);
}
.connexion__valider:hover:not(:disabled) { background: var(--accent-fort); }
.connexion__valider:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.connexion__valider:disabled { opacity: .6; cursor: default; }

/* Le message d'echec ne dit jamais CE QUI a echoue : un mot de passe
   incorrect et une adresse bloquee se ressemblent, volontairement. */
.connexion__erreur {
  margin: 12px 0 0;
  font-size: 0.86rem;
  line-height: 1.5;
  color: var(--danger);
}
.connexion__erreur[hidden] { display: none; }

/* --- Avertissement d'usage ---------------------------------------------

   Un filet a gauche plutot qu'un cadre : le texte doit se LIRE, et quatre
   bords en feraient une note de bas de page qu'on saute.

   Le filet prend la teinte du BORD d'alerte, pas l'alerte pleine : ce texte
   informe, il n'annonce pas un incident. La couleur pleine criait plus fort
   que le champ de saisie, qui est l'action de l'ecran. */
.avis {
  padding: 16px 0 0 16px;
  border-left: 2px solid var(--alerte-bord);
}

.avis__titre {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 10px;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--texte);
}

/* Une pastille plutot qu'un tiret dans le titre : le statut se voit d'un
   coup d'oeil, et la phrase reste une phrase. */
.avis__marque {
  padding: 2px 7px;
  border-radius: 4px;
  background: var(--alerte-fond);
  border: 1px solid var(--alerte-bord);
  color: var(--alerte);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  text-transform: none;
}

.avis__corps {
  margin: 0 0 10px;
  max-width: 58ch;
  font-size: 0.83rem;
  line-height: 1.6;
  color: var(--texte-doux);
}
.avis__corps:last-child { margin-bottom: 0; }
.avis__corps strong {
  font-weight: 600;
  color: var(--texte);
}

/* Ce qui EST conserve se replie : la precision doit rester accessible -- un
   avertissement qui affirme plus que le systeme ne fait se retourne contre
   celui qui l'invoque -- sans allonger l'ecran pour qui vient se connecter. */
.avis__details { margin-bottom: 10px; }

/* Le chevron est REDESSINE : une feuille de style du projet masque le
   marqueur natif de `<details>`, et « Ce qui est conserve » se lisait alors
   comme une phrase morte -- rien ne disait qu'on pouvait l'ouvrir. */
.avis__resume {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 8px;
  font-size: 0.8rem;
  color: var(--texte-doux);
  cursor: pointer;
  user-select: none;
  list-style: none;
}
.avis__resume::-webkit-details-marker { display: none; }
.avis__resume::before {
  content: "";
  width: 0;
  height: 0;
  border-left: 4px solid currentColor;
  border-top: 3.5px solid transparent;
  border-bottom: 3.5px solid transparent;
  transition: transform var(--transition);
}
.avis__details[open] .avis__resume::before { transform: rotate(90deg); }
.avis__resume:hover { color: var(--texte); }
.avis__resume:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

.avis__corps--nuance {
  margin-bottom: 0;
  color: var(--texte-faible);
}

.avis__mention {
  margin: 14px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--bord);
  max-width: 58ch;
  font-size: 0.77rem;
  line-height: 1.55;
  color: var(--texte-faible);
}

@media (max-width: 420px) {
  .connexion { padding: 28px 18px; }
  .connexion__saisie { flex-direction: column; }
  .connexion__valider { width: 100%; }
  .connexion__logo { height: 26px; margin-bottom: 32px; }
}

/* ----------------------------------------------------------------------- */
/* Logotype Clabots dans l'entete                                          */
/*                                                                          */
/* Le SVG fait 693x108 : un logotype long, pas une pastille. On le borne    */
/* par la HAUTEUR et la largeur suit. Sans cette regle il s'affiche a sa    */
/* taille native -- 108px de haut dans une barre qui en mesure 56, mesure   */
/* avant correction.                                                        */
/* ----------------------------------------------------------------------- */
.marque__logo {
  display: block;
  height: 22px;
  width: auto;
  transition: opacity var(--transition);
}
.marque:hover .marque__logo { opacity: .82; }

/* Le cyan de marque perd son assise sur fond sombre : une legere remontee de
   luminosite l'en detache sans le denaturer. */
:root[data-theme="sombre"] .marque__logo {
  filter: brightness(1.18);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="clair"]):not([data-theme="sombre"]) .marque__logo {
    filter: brightness(1.18);
  }
}
