/*
 * Stocken — feuille de style maison, sans framework ni outil de build (décision a).
 * Mobile-first : les règles de base ciblent le mobile, les media queries élargissent
 * vers tablette puis bureau. Cibles tactiles confortables (44px minimum) partout ;
 * les écrans de scan dédiés arrivent au lot 7 et pourront étendre ces variables.
 * Charte graphique Stocken (CHARTE-GRAPHIQUE_STOCKEN.pdf) : couleurs vérifiées à l'identique,
 * typographie volontairement différente (Inter, pas Raleway — décision explicite : Raleway
 * ne vit que dans les logos, qui sont des images).
 */

@font-face {
  font-family: "InterVariable";
  src: url("/fonts/InterVariable-latin.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Couleurs de marque (CHARTE-GRAPHIQUE_STOCKEN.pdf, page « colorimétrique ») */
  --bleu-principal: #05627c; /* titres, navigation, boutons principaux — texte blanc, contraste 6,9 */
  --bleu-secondaire: #007eab; /* éléments actifs, contour de focus (non-texte, 3:1 atteint) */
  /* Couleur dérivée (décision JS, docs/02) : le bleu secondaire assombri, même teinte, pour le
     texte des liens — 5,07:1 sur le fond de page, 5,49:1 sur blanc, 4,71:1 sur fond teinté. */
  --bleu-secondaire-texte: #007199;
  --orange-action: #f49522; /* fond du bouton d'action principal, un seul par écran */
  /* Recette du 14/10/2026 : survol et appui du bouton orange, un orange plus foncé, jamais gris (texte
     --texte : 5,33:1 au survol, 4,83:1 à l'appui). */
  --orange-action-survol: #e2861a;
  --orange-action-appui: #d97e14;
  --orange-secondaire: #f9af42; /* fonds et surbrillances uniquement, jamais de texte dessus */
  --gris-secondaire: #c5c5c5; /* bordures et séparateurs uniquement, jamais de texte dessus */
  --texte: #1a2b33;

  /* États — toujours accompagnés d'une icône et d'un libellé, jamais la couleur seule.
     L'alerte est délibérément jaune/or (teinte ~49°), pas orange (~33-36°), pour ne
     jamais se confondre avec --orange-action / --orange-secondaire. */
  --etat-succes-texte: #1e7a3c;
  --etat-succes-fond: #e4f5ea;
  --etat-erreur-texte: #a4262c;
  --etat-erreur-fond: #fbeaea;
  --etat-alerte-texte: #5c4b00;
  --etat-alerte-fond: #fff1b8;
  --etat-information-texte: #0b4f68;
  --etat-information-fond: #e1f0f5;

  /* Neutres */
  --couleur-fond: #f4f6f7;
  --couleur-surface: #ffffff;
  --couleur-texte-discret: #5b6470;
  --couleur-bordure: var(--gris-secondaire);

  --police: "InterVariable", "Inter", system-ui, -apple-system, "Segoe UI", Roboto, Arial,
    sans-serif;

  --rayon: 8px;
  /* R-DET-60 : en-tête d'un tableau, teinte légère du bleu sur la surface. */
  --fond-entete-tableau: color-mix(in srgb, var(--bleu-principal) 6%, var(--couleur-surface));
  --espace-1: 0.5rem;
  --espace-2: 1rem;
  --espace-3: 1.5rem;
  --espace-4: 2rem;
  /* R-DET-91 (vague design 4) : un seul gabarit, les mêmes marges latérales pour l'en-tête et le contenu ;
     24 px à partir de 640 px (bloc @media plus bas). Le cadre fait 1 280 px au plus ; au-delà, l'en-tête
     garde ses marges alignées sur celles du contenu centré. */
  --marge-page: 1rem;
  --largeur-cadre: 1280px;
  --marge-entete: max(var(--marge-page), calc((100% - var(--largeur-cadre)) / 2 + var(--marge-page)));

  --cible-tactile-min: 44px;

  /* Échelle typographique (refonte des fiches, docs/02 sous R-VIS-04) : titres plus grands
     sur PC, libellé et valeur distingués par la taille, la graisse et la couleur. */
  --taille-h1: 1.5rem;
  --taille-h2: 1.25rem;
  --taille-h3: 1.0625rem;
  --taille-libelle: 0.875rem;
  --taille-valeur: 1rem;
  --graisse-h1: 700;
  --graisse-h2: 650;
  --graisse-h3: 600;
  --graisse-libelle: 600;
  --graisse-valeur: 400;

  /* Pas de thème sombre (décision validée) : une seule palette, claire. */
  color-scheme: light;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--couleur-fond);
  color: var(--texte);
  font-family: var(--police);
  font-optical-sizing: auto;
  font-size: 16px;
  line-height: 1.5;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

h1,
h2,
h3 {
  color: var(--bleu-principal);
  line-height: 1.25;
}

h1 {
  margin: 0 0 var(--espace-2);
  font-size: var(--taille-h1);
  font-weight: var(--graisse-h1);
}

/* Complément d'un titre de page (site, marque) : sous le titre, jamais accolé par un tiret (R-DET-41). */
h1:has(+ .sous-titre-page) {
  margin-bottom: var(--espace-1);
}

.sous-titre-page {
  overflow-wrap: anywhere;
  margin: 0 0 var(--espace-2);
  color: var(--couleur-texte-discret);
  font-size: 1.125rem;
  font-weight: 600;
}

h2 {
  margin: var(--espace-4) 0 var(--espace-2);
  font-size: var(--taille-h2);
  font-weight: var(--graisse-h2);
}

h3 {
  margin: var(--espace-3) 0 var(--espace-1);
  font-size: var(--taille-h3);
  font-weight: var(--graisse-h3);
}

@media (min-width: 1024px) {
  :root {
    --taille-h1: 2rem;
    --taille-h2: 1.5rem;
    --taille-h3: 1.125rem;
  }
}

/* Libellé d'une valeur (dt, en-tête de colonne) et valeur (dd). */
dt,
th {
  font-size: var(--taille-libelle);
  font-weight: var(--graisse-libelle);
}

dd {
  font-size: var(--taille-valeur);
  font-weight: var(--graisse-valeur);
}

a {
  color: var(--bleu-secondaire-texte);
}

/* Visibilité du focus clavier, sur tout élément interactif */
:focus-visible {
  outline: 3px solid var(--bleu-secondaire);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Chiffres à largeur fixe : tableaux, quantités, prix (partout où l'alignement en
   colonne compte). */
table,
.nombres-tabulaires {
  font-variant-numeric: tabular-nums;
}

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

th,
td {
  text-align: left;
  padding: var(--espace-1) var(--espace-2);
  border-bottom: 1px solid var(--couleur-bordure);
}

th {
  color: var(--bleu-principal);
}

/* Zéro barré, formes distinctes 0/O et 1/I/l — n° AR, référence de modèle, code-barres.
   Fonctionnalités OpenType d'Inter vérifiées dans sa documentation (docs/_data/feature_
   samples.yml du dépôt rsms/inter) : « zero » (zéro barré) et « ss02 » (Disambiguation —
   « increases visual difference between similar-looking characters »). */
.reference {
  font-variant-numeric: slashed-zero tabular-nums;
  font-feature-settings:
    "ss02" 1,
    "tnum" 1;
}

/* En-tête (R-NAV2-01).
   Replié par défaut, jusqu'à 1 279px (téléphone, tablette, portable 1 024px) : ligne 1 = logo,
   Scanner, bouton Menu ; ligne 2 = site actif ; modules et compte dans le panneau du <details>.
   À partir de 1 280px : barre complète, voir le bloc @media en fin de section. */

.entete {
  position: relative;
  display: grid;
  /* La colonne du logo peut céder : avec une barre de défilement classique, une fenêtre de 320 px n'en laisse
     que 305, et le logo se réduit à l'échelle plutôt que de faire déborder l'en-tête (critique du 07/10/2026). */
  grid-template-columns: minmax(0, auto) minmax(0, 1fr) auto auto;
  grid-template-areas:
    "logo . scan menu"
    "site site site site";
  align-items: center;
  column-gap: var(--espace-1);
  padding: var(--espace-1) var(--marge-entete);
  background: var(--couleur-surface);
  border-bottom: 1px solid var(--couleur-bordure);
}

.entete__logo {
  grid-area: logo;
  display: flex;
  align-items: center;
  min-width: 0;
  min-height: var(--cible-tactile-min);
}

.entete__logo img {
  display: block;
  max-width: 100%;
  height: auto;
  max-height: 28px;
  width: auto;
}

/* Site actif : seconde ligne pleine largeur tant que l'en-tête est replié. */
.entete__site {
  grid-area: site;
  display: flex;
  min-width: 0;
  margin-top: var(--espace-1);
}

.entete__site .pastille-site {
  max-width: 100%;
}

/* R-SCN-10 : « Scanner » reste à portée tant que l'en-tête est replié. Bleu principal plein :
   l'orange appartient au geste principal de chaque écran (R-VIS-02), pas à l'en-tête. */
.entete__scan {
  grid-area: scan;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  min-height: var(--cible-tactile-min);
  padding: 0 var(--espace-2);
  border-radius: var(--rayon);
  background: var(--bleu-principal);
  color: #ffffff;
  font-weight: 600;
  text-decoration: none;
}

.entete__scan:hover {
  filter: brightness(0.93);
}

.entete__menu {
  grid-area: menu;
}

/* Bouton Menu : le <summary> natif, ouvert et fermé sans script. */
.entete__menu-bouton {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  min-height: var(--cible-tactile-min);
  padding: 0 var(--espace-2);
  border: 2px solid var(--bleu-principal);
  border-radius: var(--rayon);
  color: var(--bleu-principal);
  font-weight: 600;
  list-style: none;
  cursor: pointer;
  user-select: none;
}

.entete__menu-bouton::-webkit-details-marker {
  display: none;
}

.entete__menu-bouton:hover {
  background: color-mix(in srgb, var(--bleu-principal) 8%, transparent);
}

.entete__menu[open] > .entete__menu-bouton {
  background: var(--bleu-principal);
  color: #ffffff;
}

/* Panneau du menu, sous l'en-tête, pleine largeur. */
.entete__nav {
  position: absolute;
  top: 100%;
  right: 0;
  left: 0;
  z-index: 10;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--espace-1) var(--espace-2) var(--espace-2);
  background: var(--couleur-surface);
  border-bottom: 1px solid var(--couleur-bordure);
  box-shadow: 0 12px 24px rgb(0 0 0 / 12%);
}

/* Visiteur non connecté : un seul lien, pas de panneau. */
.entete__nav--visiteur {
  grid-area: menu;
  position: static;
  padding: 0;
  border: 0;
  box-shadow: none;
}

.entete__module {
  display: flex;
  align-items: center;
  min-height: 48px;
  padding: 0 var(--espace-2);
  border-radius: var(--rayon);
  color: var(--texte);
  font-weight: 600;
  text-decoration: none;
}

.entete__module:hover {
  background: color-mix(in srgb, var(--bleu-principal) 8%, transparent);
}

/* R-NAV2-03 : l'entrée du module courant est marquée active — fond teinté et couleur,
   en plus d'aria-current. */
.entete__module[aria-current="page"] {
  background: color-mix(in srgb, var(--bleu-principal) 12%, transparent);
  color: var(--bleu-principal);
}

/* Dans le panneau, compte et déconnexion s'alignent sur les modules (sélecteurs qualifiés par
   .entete__nav : la règle générale .entete__action, plus bas, centrerait sinon). */
.entete__nav .entete__action {
  justify-content: flex-start;
  padding: 0 var(--espace-2);
}

.entete__nav .entete__compte {
  margin-top: var(--espace-1);
  padding-top: var(--espace-1);
  border-top: 1px solid var(--couleur-bordure);
  border-radius: 0;
}

.entete__compte-textes {
  display: flex;
  flex-direction: column;
  line-height: 1.25;
}

.entete__compte-libelle {
  color: var(--couleur-texte-discret);
  font-size: 0.875rem;
  font-weight: 400;
}

.entete__deconnexion {
  margin: 0;
}

.entete__deconnexion .entete__action {
  justify-content: flex-start;
  width: 100%;
}

/* Menu du compte de la barre complète : absent du panneau replié, qui garde ses deux entrées. */
.entete__compte-menu {
  display: none;
}

/* Actions de l'en-tête (Mon compte, Se déconnecter) : icône + texte, cible tactile 44px. */
.entete__action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  min-height: var(--cible-tactile-min);
  min-width: var(--cible-tactile-min);
  padding: 0 var(--espace-1);
  border: none;
  border-radius: var(--rayon);
  background: transparent;
  color: var(--bleu-principal);
  text-decoration: none;
  font-family: inherit;
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
}

.entete__action:hover {
  background: color-mix(in srgb, var(--bleu-principal) 8%, transparent);
}

.entete__action-icone {
  flex: none;
  width: 22px;
  height: 22px;
}

/* En-tête réduit de l'écran de scan (décision JS, docs/07) : une seule ligne — logo compact,
   site actif en petit, Menu ; pas de bouton Scanner. Sur écran large, l'en-tête complet reste. */
.entete--scan {
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "logo site menu";
}

.entete--scan .entete__site {
  margin-top: 0;
}

@media (max-width: 1279px) {
  .entete--scan .pastille-site {
    padding: 0 var(--espace-1);
    font-size: 0.8125rem;
  }
}

/* Téléphone : logo, Scanner et Menu doivent tenir sur une ligne sans défilement horizontal
   (R-VIS-10) — marges resserrées, logo réduit à l'échelle (jamais déformé, R-VIS-07). */
@media (max-width: 639px) {
  .entete__scan,
  .entete__menu-bouton {
    gap: 0.3rem;
    padding: 0 var(--espace-1);
  }
}

@media (max-width: 399px) {
  .entete__logo img {
    max-height: 24px;
  }
}

/* Très petits écrans (320px) : bouton Menu en icône seule, lu « Menu » par les lecteurs
   d'écran — Scanner garde son texte, c'est le geste de terrain. */
@media (max-width: 369px) {
  .entete__menu-bouton {
    justify-content: center;
    width: var(--cible-tactile-min);
    padding: 0;
  }

  .entete__menu-texte {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
}

/* Barre complète à partir de 1 280px (R-NAV2-01) : logo, site actif, modules, compte. Le
   <details> est forcé ouvert par ::details-content (Chrome 131+, Safari 18.4+, Firefox 143+).
   Repli, sur un navigateur plus ancien : @supports écarte ce bloc et l'en-tête reste replié,
   bouton Menu compris — utilisable, moins direct. */
@supports selector(::details-content) {
  @media (min-width: 1280px) {
    .entete:not(.entete--replie) {
      display: flex;
      align-items: center;
      gap: var(--espace-1);
      padding: var(--espace-1) var(--marge-entete);
    }

    .entete:not(.entete--replie) .entete__site {
      flex: none;
      max-width: 11rem;
      margin-top: 0;
    }

    .entete:not(.entete--replie) .entete__site .pastille-site {
      max-width: 100%;
    }

    .entete:not(.entete--replie) .entete__scan,
    .entete:not(.entete--replie) .entete__menu-bouton {
      display: none;
    }

    .entete:not(.entete--replie) .entete__menu {
      flex: 1;
      min-width: 0;
    }

    .entete:not(.entete--replie) .entete__menu::details-content {
      content-visibility: visible;
      display: contents;
    }

    .entete:not(.entete--replie) .entete__nav {
      position: static;
      flex-direction: row;
      align-items: center;
      gap: 0.125rem;
      padding: 0;
      border: 0;
      box-shadow: none;
    }

    /* Modules en onglets : même famille que les onglets de page (.onglets). Une entrée tient sur
       une ligne (« Stock ESA », « Stock bois ») : la barre de huit entrées tient à 1 280 px. */
    .entete:not(.entete--replie) .entete__module {
      min-height: var(--cible-tactile-min);
      padding: 0 0.375rem;
      border-radius: 0;
      color: var(--bleu-principal);
      font-size: 0.875rem;
      white-space: nowrap;
    }

    /* Soulignement du module actif : une ombre intérieure de 3px (même rendu qu'une bordure). */
    .entete:not(.entete--replie) .entete__module[aria-current="page"] {
      background: transparent;
      box-shadow: inset 0 -3px 0 var(--bleu-principal);
    }

    .entete:not(.entete--replie) .entete__compte {
      margin: 0 0 0 auto;
      padding: 0 var(--espace-1);
      border: 0;
      border-radius: var(--rayon);
    }

    .entete:not(.entete--replie) .entete__compte-libelle {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip: rect(0 0 0 0);
      white-space: nowrap;
    }

    .entete:not(.entete--replie) .entete__deconnexion .entete__action {
      width: auto;
    }

    /* Le lien et le bouton du panneau replié laissent place au menu du compte. */
    .entete:not(.entete--replie) .entete__nav > .entete__compte,
    .entete:not(.entete--replie) .entete__nav > .entete__deconnexion {
      display: none;
    }

    .entete:not(.entete--replie) .entete__compte-menu {
      position: relative;
      display: block;
      margin-left: auto;
    }

    .entete:not(.entete--replie) .entete__compte-bouton {
      list-style: none;
      white-space: nowrap;
    }

    .entete:not(.entete--replie) .entete__compte-bouton::-webkit-details-marker {
      display: none;
    }

    .entete:not(.entete--replie) .entete__compte-nom {
      max-width: 8rem;
      overflow: hidden;
      text-overflow: ellipsis;
    }

    .entete:not(.entete--replie) .entete__compte-titre {
      margin: 0;
      padding: var(--espace-1);
      color: var(--couleur-texte-discret);
      font-size: 0.875rem;
      white-space: nowrap;
    }

    .entete:not(.entete--replie) .entete__compte-menu[open] > .entete__compte-bouton {
      background: color-mix(in srgb, var(--bleu-principal) 12%, transparent);
    }

    .entete:not(.entete--replie) .entete__compte-panneau {
      position: absolute;
      top: calc(100% + var(--espace-1));
      right: 0;
      z-index: 10;
      display: flex;
      flex-direction: column;
      gap: 2px;
      min-width: 12rem;
      padding: var(--espace-1);
      border: 1px solid var(--couleur-bordure);
      border-radius: var(--rayon);
      background: var(--couleur-surface);
      box-shadow: 0 12px 24px rgb(0 0 0 / 12%);
    }

    .entete:not(.entete--replie) .entete__compte-panneau .entete__action {
      justify-content: flex-start;
      width: 100%;
      white-space: nowrap;
    }
  }
}

/* Conteneur principal */

.conteneur {
  flex: 1;
  width: 100%;
  max-width: var(--largeur-cadre);
  margin: 0 auto;
  padding: var(--espace-3) var(--marge-page);
}

.pied {
  padding: var(--espace-2);
  text-align: center;
  color: var(--couleur-texte-discret);
  font-size: 0.875rem;
}

/* Connexion : logo rond centré au-dessus du formulaire */
.section-connexion {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.section-connexion__logo {
  display: block;
  width: 96px;
  height: 96px;
  margin-bottom: var(--espace-2);
}

.section-connexion form {
  text-align: left;
  width: 100%;
}

/* Bloc sur fond bleu principal (seul contexte où logo-clair.png est utilisé) */
.bloc-bleu {
  background: var(--bleu-principal);
  color: #ffffff;
  padding: var(--espace-3);
  border-radius: var(--rayon);
}

.bloc-bleu h1,
.bloc-bleu h2,
.bloc-bleu h3 {
  color: #ffffff;
}

/* Formulaires */

form {
  display: flex;
  flex-direction: column;
  gap: var(--espace-2);
  max-width: 420px;
}

/* Un fieldset prend par défaut la largeur de son contenu (min-inline-size: min-content) : un groupe de
   listes déroulantes le faisait déborder à 320 px (Nouvel inventaire, critique design du 07/10/2026). */
fieldset {
  min-inline-size: 0;
}

/* R-DET-64 (recette du 14/10/2026) : dans un formulaire, champs texte, listes et zones de saisie ont la même
   largeur, celle de leur colonne. À partir de 1 024 px, un formulaire de saisie (sans classe, au moins deux
   champs, hors connexion) occupe la largeur de la page en deux colonnes ; une zone de texte, un fichier, des
   cases ou des choix, et tout ce qui n'est pas un champ (titres, boutons, blocs) prennent une ligne entière. */
.champ > :is(input:not([type="checkbox"], [type="radio"], [type="hidden"]), select, textarea) {
  width: 100%;
}

/* R-DET-66 (recette du 14/10/2026) : un bouton n'est jamais collé au champ ou au cadre qu'il accompagne
   (« Ajouter un code-barres », « Effacer la signature ») et garde sa largeur ; deux formulaires qui se suivent
   (Nouvelle navette, puis la recherche des navettes) sont séparés. */
.champ > :is(button, .bouton-secondaire, .bouton-tertiaire, .bouton-action) {
  align-self: flex-start;
  margin-top: var(--espace-1);
}

form + form {
  margin-top: var(--espace-3);
}

@media (min-width: 1024px) {
  form:not([class]):not(.section-connexion form):has(> .champ ~ .champ) {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--espace-2) var(--espace-3);
    align-items: start;
    /* R-DET-91 : dans le cadre unique de 1 280 px, un formulaire de saisie garde sa largeur propre. */
    max-width: 60rem;
  }

  form:not([class]):not(.section-connexion form):has(> .champ ~ .champ) > :not(.champ),
  form:not([class]):not(.section-connexion form):has(> .champ ~ .champ) > .champ:has(textarea, input[type="file"], input[type="checkbox"], input[type="radio"], .boutons-choix, fieldset) {
    grid-column: 1 / -1;
  }

  form:not([class]):not(.section-connexion form):has(> .champ ~ .champ) > :is(button, a) {
    justify-self: start;
  }
}

.champ {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

/* R-TAB-01 : recherche du composant tableau — en ligne, sans la largeur maximale des
   formulaires de saisie. */
.tableau-recherche {
  flex-direction: row;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: var(--espace-1) var(--espace-2);
  max-width: none;
  /* R-DET-66, R-DET-81 : la barre de filtres et ses boutons ne touchent pas le tableau qui suit. */
  margin-bottom: var(--espace-3);
}

/* Recherche compacte : le champ prend la place disponible, son bouton reste à côté, même sur
   téléphone ; l'export (tertiaire) passe à la ligne s'il le faut. */
.tableau-recherche .champ {
  flex: 1 1 10rem;
  min-width: 0;
}

.tableau-recherche .champ:has(select) {
  flex-basis: 100%;
}

/* Tri en cartes (critique du 06/10/2026) : « Trier par » et ses deux listes sur une seule ligne,
   même à 320 px, pour ne pas repousser la première carte. En tableau (écran large), les en-têtes
   trient : la ligne s'efface mais reste dans le formulaire, pour qu'une recherche garde le tri. */
.tableau-tri {
  display: flex;
  flex: 1 1 100%;
  align-items: center;
  gap: var(--espace-1);
  min-width: 0;
}

.tableau-tri__libelle {
  font-size: var(--taille-libelle);
  font-weight: var(--graisse-libelle);
  white-space: nowrap;
}

.tableau-tri select {
  flex: 3 1 0;
  min-width: 0;
}

/* La colonne a des libellés plus longs que l'ordre (« Ordre habituel » contre « Croissant »). */
.tableau-tri [data-tableau-tri-colonne] {
  flex-grow: 4;
}

/* Écran étroit (critique du 07/10/2026) : « Trier par » et la colonne sur une ligne, l'ordre sur la
   suivante, chaque liste assez large pour son plus long libellé (« Prix de vente TTC », « Décroissant »). */
@media (max-width: 479px) {
  .tableau-tri {
    flex-wrap: wrap;
    row-gap: var(--espace-1);
  }

  .tableau-tri [data-tableau-tri-ordre] {
    flex-basis: 100%;
  }
}

@media (min-width: 1280px) {
  .tableau-tri {
    display: none;
  }
}

/* Recherche à critères secondaires (stock bois, R-DET-85, vague design 4) : les critères principaux et
   Rechercher sur une rangée, les dimensions repliées dans « Plus de critères » à toutes les largeurs ; le
   <details> s'ouvre de lui-même quand l'un d'eux est renseigné, et passe alors sous la rangée. */
.recherche-criteres {
  flex-basis: auto;
}

.recherche-criteres[open] {
  flex-basis: 100%;
}

.recherche-criteres > summary {
  display: inline-flex;
  cursor: pointer;
}

.recherche-criteres__corps {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--espace-1) var(--espace-2);
  padding-top: var(--espace-1);
}

/* R-DET-89 : en tableau, une cellule qui ne se coupe pas (dimensions, emplacement du stock bois). */
@container liste (min-width: 1200px) {
  .tableau-cartes .sans-coupure {
    white-space: nowrap;
  }
}

/* R-DET-84 (vague design 4) : le libellé « Rechercher » reste visible à toutes les largeurs ; le champ de
   recherche est plus large que les filtres qui l'accompagnent. */
@media (min-width: 640px) {
  .tableau-recherche .champ,
  .tableau-recherche .champ:has(select) {
    flex: 0 1 16rem;
  }

  .tableau-recherche .champ:has(#tableau-recherche) {
    flex: 0 1 24rem;
  }
}

/* R-DET-83 (vague design 4) : à partir de 1 280 px, les onglets (à gauche) et la recherche (à droite)
   partagent une rangée, sur le filet des onglets ; plus étroit, la recherche passe dessous. */
@media (min-width: 1280px) {
  .barre-liste {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    column-gap: var(--espace-3);
    margin-bottom: var(--espace-3);
    border-bottom: 1px solid var(--couleur-bordure);
  }

  .barre-liste > .onglets {
    margin-bottom: 0;
    border-bottom: 0;
  }

  .barre-liste > .tableau-recherche {
    flex: 0 1 auto;
    flex-wrap: nowrap;
    margin-bottom: var(--espace-1);
  }

  /* Largeur fixée : le formulaire se dimensionne sur son contenu, le champ ne doit pas s'y réduire. */
  .barre-liste > .tableau-recherche .champ:has(#tableau-recherche) {
    flex: 0 0 20rem;
    width: 20rem;
  }

  /* R-DET-85 : les quatre critères du stock bois, Rechercher et Effacer tiennent sur une rangée. */
  .tableau-recherche.recherche-bois > .champ {
    flex: 0 1 14rem;
  }

  .barre-liste > .tableau-recherche .champ:has(select) {
    flex: 0 1 12rem;
  }
}

/* Une case et son libellé forment une cible de 44 px de haut : on touche la ligne, pas seulement la case
   (critique du 06/10/2026, matrice des droits comprise). */
.champ--case {
  flex-direction: row;
  align-items: center;
  gap: var(--espace-1);
  min-height: var(--cible-tactile-min);
}

.champ--case > label {
  display: flex;
  flex: 1;
  align-items: center;
  min-height: var(--cible-tactile-min);
  cursor: pointer;
}

label {
  font-weight: var(--graisse-libelle);
}

.champ__mention {
  font-weight: 400;
  color: var(--couleur-texte-discret);
  font-size: 0.8125rem;
}

/* Recette du 14/10/2026 : un champ sans attribut type est un champ texte ; sans ce sélecteur il gardait
   le rendu du navigateur et changeait de taille au focus (commentaire du solde à payer). */
input:not([type]),
input[type="email"],
input[type="password"],
input[type="tel"],
input[type="text"],
input[type="date"],
input[type="number"],
input[type="file"],
select,
textarea {
  /* Une option longue (« État consolidé au … ») ne fait jamais déborder la page à 320 px. */
  max-width: 100%;
  min-height: var(--cible-tactile-min);
  padding: var(--espace-1);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
  font-size: 1rem;
  font-family: inherit;
  background: var(--couleur-surface);
  color: var(--texte);
}

textarea {
  min-height: calc(var(--cible-tactile-min) * 2);
  resize: vertical;
}

/* R-DET-64, R-DET-81 : une liste déroulante a la hauteur d'un champ texte dans tout navigateur. Le rendu natif
   (Safari en tête) ignore hauteur et marges internes : il est remplacé par la boîte commune, avec un chevron
   dessiné en dégradés (aucune image, rien à autoriser dans la politique de sécurité). */
select:not([multiple]) {
  appearance: none;
  padding-right: calc(var(--espace-1) + 1.5rem);
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position:
    calc(100% - 1.25rem) 50%,
    calc(100% - 0.9375rem) 50%;
  background-size: 0.3125rem 0.3125rem;
  background-repeat: no-repeat;
}

input[type="checkbox"],
input[type="radio"] {
  width: 1.25rem;
  height: 1.25rem;
  /* R-DET-101 : les cases et boutons radio aux couleurs de la marque, partout. */
  accent-color: var(--bleu-principal);
}

.champ--radio {
  flex-direction: row;
  align-items: center;
  gap: var(--espace-1);
}

/* R-DET-82 (recette du 14/10/2026) : le formulaire article montre les champs du type choisi, sans script. Une
   option « Cuisine complète » porte data-cuisine ; sans type, seuls les champs communs restent. Le champ partagé
   (modèle d'un appareil, désignation d'une cuisine) change de libellé. Sans :has, tout reste visible : chaque
   champ dit pour quel type il vaut, le libellé partagé se lit « Modèle / Désignation », et la page ignore de toute
   façon les champs d'un autre type. */
.selon-type--cuisine::before {
  content: " / ";
}

@supports selector(:has(a)) {
  .champ__mention-type {
    display: none;
  }

  form:has([data-type-article] option[data-cuisine]:checked) :is(.champ--appareil, .selon-type--appareil),
  form:has([data-type-article] option:not([data-cuisine]):checked) .champ--cuisine,
  form:has([data-type-article] option:not([data-cuisine]):checked) .selon-type--cuisine,
  form:has([data-type-article] option[value=""]:checked) :is(.champ--appareil, .champ--cuisine, .selon-type--cuisine) {
    display: none;
  }

  form:has([data-type-article] option[data-cuisine]:checked) .selon-type--cuisine::before {
    content: none;
  }
}

input:disabled,
select:disabled,
textarea:disabled {
  /* La couleur seule : le chevron d'une liste déroulante désactivée reste dessiné. */
  background-color: var(--couleur-fond);
  color: var(--couleur-texte-discret);
  cursor: not-allowed;
}

/* Champ en erreur : bordure rouge en renfort visuel, le message est relié par
   aria-describedby (porté dans le balisage, pas en CSS) pour les lecteurs d'écran. */
.champ--erreur input,
.champ--erreur select,
.champ--erreur textarea {
  border-color: var(--etat-erreur-texte);
}

.champ__erreur {
  color: var(--etat-erreur-texte);
  font-size: 0.875rem;
}

/* Boutons — quatre niveaux (R-VIS-02).
   .bouton-action      : orange plein, texte --texte — le seul geste principal de l'écran
                         (jamais blanc sur orange, contraste 2,3 sinon, R-VIS-01).
   .bouton-secondaire  : contour bleu, fond blanc — tous les autres gestes. C'est aussi le
                         rendu de tout <button> sans classe.
   .bouton-tertiaire   : texte bleu sans contour — gestes discrets d'une ligne (Ouvrir). Rechercher, Filtrer et
                         Exporter sont des boutons secondaires (R-DET-62, recette du 14/10/2026).
   .bouton-destructif  : contour et texte rouge sobre — retirer, refuser, annuler, supprimer ;
                         précédé d'une confirmation (.confirmation) quand aucun motif n'est saisi.
   Le bleu plein n'est plus un niveau de bouton : il reste au bouton Scanner de l'en-tête et
   aux choix sélectionnés (.boutons-choix__bouton[aria-current]). */
button,
.bouton-secondaire,
.bouton-tertiaire,
.bouton-destructif,
.bouton-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: var(--cible-tactile-min);
  padding: var(--espace-1) var(--espace-3);
  border: 2px solid transparent;
  border-radius: var(--rayon);
  font-family: inherit;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.25;
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color 0.15s ease,
    filter 0.15s ease;
}

button,
.bouton-secondaire {
  background: var(--couleur-surface);
  border-color: var(--bleu-principal);
  color: var(--bleu-principal);
}

.bouton-tertiaire {
  background: transparent;
  color: var(--bleu-principal);
  padding-inline: var(--espace-2);
}

.bouton-destructif {
  background: var(--couleur-surface);
  border-color: var(--etat-erreur-texte);
  color: var(--etat-erreur-texte);
}

.bouton-action {
  background: var(--orange-action);
  border-color: var(--orange-action);
  color: var(--texte);
}

button:hover,
.bouton-secondaire:hover,
.bouton-tertiaire:hover {
  background: color-mix(in srgb, var(--bleu-principal) 8%, var(--couleur-surface));
}

.bouton-destructif:hover {
  background: var(--etat-erreur-fond);
}

.bouton-action:hover {
  background: var(--orange-action-survol);
  border-color: var(--orange-action-survol);
}

button:active,
.bouton-secondaire:active,
.bouton-tertiaire:active {
  background: color-mix(in srgb, var(--bleu-principal) 16%, var(--couleur-surface));
}

.bouton-destructif:active {
  background: color-mix(in srgb, var(--etat-erreur-texte) 16%, var(--couleur-surface));
}

.bouton-action:active {
  background: var(--orange-action-appui);
  border-color: var(--orange-action-appui);
}

/* Étape de confirmation d'un geste destructif : plein, texte blanc (contraste 7,4). */
.bouton-destructif--confirmer {
  background: var(--etat-erreur-texte);
  color: #ffffff;
}

.bouton-destructif--confirmer:hover,
.bouton-destructif--confirmer:active {
  background: var(--etat-erreur-texte);
  filter: brightness(0.9);
}

button:disabled,
.bouton-secondaire:disabled,
.bouton-tertiaire:disabled,
.bouton-destructif:disabled,
.bouton-action:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  filter: none;
}

button:disabled:hover,
.bouton-secondaire:disabled:hover,
.bouton-tertiaire:disabled:hover {
  background: var(--couleur-surface);
}

.bouton-action:disabled:hover {
  background: var(--orange-action);
  border-color: var(--orange-action);
}

/* Confirmation sans script : le <summary> porte le geste destructif, le corps déplié dit la
   conséquence et porte le bouton de confirmation ; refermer le bloc annule. */
.confirmation {
  display: inline-block;
}

.confirmation > summary {
  list-style: none;
}

.confirmation > summary::-webkit-details-marker {
  display: none;
}

.confirmation[open] > summary {
  background: var(--etat-erreur-fond);
}

/* R-DET-71 (recette du 14/10/2026) : ouverte, une confirmation prend toute la largeur disponible, en texte et
   boutons de taille normale ; dans une galerie, sa vignette passe sur une ligne entière (la confirmation tenait
   dans les 160 px de la photo). Le bouton qui l'a ouverte la referme. */
.confirmation[open] {
  display: block;
  width: 100%;
}

.galerie-photos__vignette:has(> .confirmation[open]) {
  flex-basis: 100%;
  width: 100%;
}

/* R-DET-66 : le bouton Supprimer ne touche pas la photo. */
.galerie-photos__vignette > .confirmation {
  margin-top: var(--espace-1);
}

.confirmation__corps {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--espace-1);
  max-width: 40rem;
  margin-top: var(--espace-1);
  padding: var(--espace-2);
  font-size: 1rem;
  border: 1px solid var(--etat-erreur-texte);
  border-radius: var(--rayon);
  background: var(--etat-erreur-fond);
  color: var(--etat-erreur-texte);
}

.confirmation__corps p {
  margin: 0;
}

.confirmation__corps form {
  margin: 0;
}

/* Barre d'actions d'une page ou d'une fiche : le geste principal (orange) en tête, les autres
   en secondaire ; passe à la ligne sur téléphone. */
/* R-DET-95 (vague design 4) : un titre de page et son signal, à sa droite (Prêts : les retards). */
.titre-page {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--espace-1) var(--espace-2);
}

.titre-page > :is(h1, h2) {
  margin: 0;
}

/* Signal fort : alerte pleine, icône et libellé (jamais la couleur seule), lien de 44 px. */
.signal-alerte {
  display: inline-flex;
  align-items: center;
  gap: var(--espace-1);
  min-height: var(--cible-tactile-min);
  padding: 0 var(--espace-2);
  border-radius: var(--rayon);
  background: var(--etat-alerte-texte);
  color: #ffffff;
  font-weight: 700;
  text-decoration: none;
}

.signal-alerte:hover,
.signal-alerte:focus-visible {
  text-decoration: underline;
}

.barre-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-1);
  margin: var(--espace-2) 0;
}

.barre-actions form {
  margin: 0;
}

/* « Plus » d'une barre d'actions (SAV, critique du 06/10/2026) : sur téléphone, les gestes secondaires se
   replient sous un bouton ; ouverts, ils passent à la ligne sous la barre. */
.barre-actions__plus > summary {
  list-style: none;
  cursor: pointer;
}

.barre-actions__plus > summary::-webkit-details-marker {
  display: none;
}

.barre-actions__plus[open] {
  flex-basis: 100%;
}

.barre-actions__plus[open] > summary {
  background: color-mix(in srgb, var(--bleu-principal) 12%, transparent);
}

.barre-actions__plus-liste {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-1);
  margin-top: var(--espace-1);
}

/* R-DET-83 (vague design 4) : sur téléphone, les commandes repliées se lisent en liste, une par ligne. */
@media (max-width: 639px) {
  .barre-actions__plus-liste {
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
  }
}

/* À partir de 640 px, la barre est entière : « Plus » forcé ouvert, son bouton masqué. Un navigateur sans
   ::details-content garde « Plus », un geste de plus, rien de perdu. */
@supports selector(::details-content) {
  @media (min-width: 640px) {
    .barre-actions__plus,
    .barre-actions__plus-liste {
      display: contents;
    }

    .barre-actions__plus::details-content {
      content-visibility: visible;
      display: contents;
    }

    .barre-actions__plus > summary {
      display: none;
    }
  }
}

/* Compteur dans un bouton (« Ventes à valider 3 ») : pastille, pas de la couleur seule. */
.pastille-compteur {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.5rem;
  height: 1.5rem;
  padding: 0 0.4rem;
  border-radius: 999px;
  background: var(--bleu-principal);
  color: #ffffff;
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
}

/* Bouton « en cours d'envoi » : désactivé (anti double-clic) le temps de la requête, avec
   un indicateur visuel — jamais la couleur/l'opacité seule. Un futur lot bascule cette
   classe et le libellé au moment de la soumission (hors périmètre ici : cette page ne
   montre que l'état, pas le script qui l'active). */
.roue-chargement {
  flex: none;
  width: 16px;
  height: 16px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: tourner 0.6s linear infinite;
}

@keyframes tourner {
  to {
    transform: rotate(360deg);
  }
}

/* Messages d'état : toujours une icône + un libellé, jamais la couleur seule. */

.alerte {
  display: flex;
  align-items: flex-start;
  gap: var(--espace-1);
  /* .alerte est un <p> : sans cette remise à zéro, sa marge par défaut de navigateur
     (1em haut/bas) s'ajoutait au gap du conteneur .pile qui l'entoure, doublant
     l'espacement entre deux messages consécutifs. L'espacement standard entre blocs
     vient uniquement de .pile désormais. */
  margin: 0;
  padding: var(--espace-2);
  border-radius: var(--rayon);
}

.alerte__icone {
  flex: none;
  width: 20px;
  height: 20px;
  margin-top: 2px;
}

.alerte--succes {
  background: var(--etat-succes-fond);
  color: var(--etat-succes-texte);
}

.alerte--erreur {
  background: var(--etat-erreur-fond);
  color: var(--etat-erreur-texte);
}

.alerte--alerte {
  background: var(--etat-alerte-fond);
  color: var(--etat-alerte-texte);
}

.alerte--information {
  background: var(--etat-information-fond);
  color: var(--etat-information-texte);
}

/* Fiche (mon compte...) */

.fiche {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--espace-1) var(--espace-2);
}

.fiche dt {
  font-weight: 600;
  color: var(--couleur-texte-discret);
}

.fiche dd {
  margin: 0;
}

.discret {
  color: var(--couleur-texte-discret);
  font-size: 0.875rem;
}

/* Tableau réactif : cartes empilées sous 640px plutôt qu'un défilement horizontal.
   data-etiquette sur chaque <td> fournit l'intitulé de colonne affiché en petit écran ;
   <thead> reste dans le DOM (utile aux lecteurs d'écran) mais visuellement masqué. */
.tableau-reactif {
  width: 100%;
}

@media (max-width: 639px) {
  .tableau-reactif:not(.tableau-cartes) thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }

  .tableau-reactif:not(.tableau-cartes) tr {
    display: block;
    margin-bottom: var(--espace-2);
    border: 1px solid var(--couleur-bordure);
    border-radius: var(--rayon);
    padding: var(--espace-1) var(--espace-2);
  }

  /* Une valeur longue (adresse e-mail, code) passe à la ligne plutôt que d'élargir la page. */
  .tableau-reactif:not(.tableau-cartes) td {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--espace-1) var(--espace-2);
    min-width: 0;
    overflow-wrap: anywhere;
    border-bottom: none;
    padding: var(--espace-1) 0;
  }

  .tableau-reactif:not(.tableau-cartes) td::before {
    content: attr(data-etiquette);
    font-weight: 600;
    color: var(--bleu-principal);
  }
}

/* R-DET-60 (recette du 14/10/2026) : un tableau se lit comme un tableau — fond de surface distinct de la
   page, cadre, en-tête teinté, lignes séparées. Même présentation pour les listes en tableau et les tableaux
   compacts, plus bas. */
@media (min-width: 640px) {
  .tableau-reactif:not(.tableau-cartes) {
    border-collapse: separate;
    border-spacing: 0;
    border: 1px solid var(--couleur-bordure);
    border-radius: var(--rayon);
    background: var(--couleur-surface);
  }

  .tableau-reactif:not(.tableau-cartes) thead th {
    background: var(--fond-entete-tableau);
  }

  .tableau-reactif:not(.tableau-cartes) thead th:first-child {
    border-top-left-radius: calc(var(--rayon) - 1px);
  }

  .tableau-reactif:not(.tableau-cartes) thead th:last-child {
    border-top-right-radius: calc(var(--rayon) - 1px);
  }

  .tableau-reactif:not(.tableau-cartes) tbody tr:last-child > * {
    border-bottom: 0;
  }
}

/* Listes métier en cartes (SAV, Articles — refonte du tableau, docs/06b sous R-TAB-01).
   Une même ligne <tr> sert de carte tant que la liste fait moins de 1 200px, et de ligne de
   tableau au-delà : requête de conteneur sur .liste. Un navigateur qui ne connaît pas les
   requêtes de conteneur garde les cartes à toutes les largeurs (même repli que l'en-tête).
   data-carte place chaque cellule dans la carte : selection, titre, sous-titre, meta, lieu,
   etat, prix, actions ; une cellule vide y est masquée. */
.liste {
  container: liste / inline-size;
}

/* La liste vit dans le formulaire de sélection (cases à cocher) : il ne la bride pas à la
   largeur des formulaires de saisie. */
form:has(> .liste) {
  max-width: none;
}

.tableau-cartes,
.tableau-cartes tbody {
  display: block;
}

/* En carte, pas d'en-tête : chaque valeur porte son libellé, et des liens de tri invisibles ne doivent pas
   recevoir le focus clavier (critique du 06/10/2026). Le tri reste en tableau et par l'adresse. */
.tableau-cartes thead {
  display: none;
}

/* Case de sélection d'une carte : une zone de 44 px autour de la case, sans agrandir la carte. */
.case-selection {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--cible-tactile-min);
  min-height: var(--cible-tactile-min);
  margin: -0.75rem 0 -0.75rem -0.75rem;
  cursor: pointer;
}

/* R-DET-98 : la case « tout sélectionner » de l'en-tête a la taille des cases de ligne. */
.case-selection input[type="checkbox"],
input[data-tableau-tout-selectionner] {
  width: 1.5rem;
  height: 1.5rem;
  margin: 0;
}

/* R-DET-98 : en tableau, la case de ligne prend le retrait des autres cellules ; la zone de 44 px reste, à droite
   de la case. */
@container liste (min-width: 1200px) {
  .tableau-cartes .case-selection {
    justify-content: flex-start;
    margin: -0.75rem 0;
  }
}

/* R-DET-97 : la barre de sélection flotte en bas de l'écran, dans le cadre de la page ; elle ne pousse jamais le
   tableau. Tant qu'elle est visible, la page gagne en bas la place qu'elle occupe, pour ne pas masquer la dernière
   ligne. */
[data-tableau-actions-groupees] {
  position: fixed;
  right: var(--marge-entete);
  bottom: max(var(--espace-2), env(safe-area-inset-bottom));
  left: var(--marge-entete);
  z-index: 20;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-1) var(--espace-2);
  padding: var(--espace-1) var(--espace-2);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
  background: var(--couleur-surface);
  box-shadow: 0 4px 16px rgb(0 0 0 / 16%);
}

.actions-groupees__compteur {
  margin-right: auto;
  font-weight: 600;
}

body:has([data-tableau-actions-groupees]:not([hidden])) .conteneur {
  padding-bottom: 12rem;
}

@media (min-width: 640px) {
  body:has([data-tableau-actions-groupees]:not([hidden])) .conteneur {
    padding-bottom: 7rem;
  }
}

.tableau-cartes tr {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem var(--espace-2);
  margin-bottom: var(--espace-2);
  padding: var(--espace-2);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
  background: var(--couleur-surface);
}

.tableau-cartes tr:hover {
  border-color: var(--bleu-principal);
}

/* Deux sauts de ligne invisibles : après la ligne du titre (R-DET-88 : le titre qui cède sa place au bouton
   « Actions » ne la cède pas aux détails), entre les détails et l'emplacement. Les prix ont leur propre ligne. */
.tableau-cartes tr::before,
.tableau-cartes tr::after {
  content: "";
  flex-basis: 100%;
  height: 0;
}

.tableau-cartes tr::before {
  order: 5;
}

.tableau-cartes tr::after {
  order: 3;
}

.tableau-cartes td {
  display: block;
  min-width: 0;
  max-width: 100%;
  padding: 0;
  border: 0;
  /* Lot 20 (R-DET-42) : un code, une adresse ou un texte lu sans espace ne fait jamais déborder la carte. */
  overflow-wrap: anywhere;
}

.tableau-cartes pre {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.tableau-cartes select,
.tableau-cartes input:not([type="checkbox"], [type="radio"]) {
  max-width: 100%;
}

.tableau-cartes td:empty {
  display: none;
}

.tableau-cartes [data-carte="selection"] {
  order: 1;
  position: relative;
  z-index: 1;
}

/* R-DET-88 : le titre cède la place au bouton « Actions », qui reste sur sa ligne ; le titre passe à la ligne. */
.tableau-cartes [data-carte="titre"] {
  order: 2;
  flex: 1 1 0;
  min-width: 0;
  font-weight: 600;
}

/* R-DET-88 (vague design 4) : le sous-titre ne domine jamais le titre (référence, dates, emprunteur). */
.tableau-cartes [data-carte="sous-titre"] {
  order: 3;
  flex-basis: 100%;
}

.tableau-cartes [data-carte="meta"] {
  order: 4;
  color: var(--couleur-texte-discret);
  font-size: 0.875rem;
}

.tableau-cartes [data-carte="lieu"] {
  order: 6;
  color: var(--couleur-texte-discret);
}

.tableau-cartes [data-carte="lieu"] + [data-carte="lieu"] {
  margin-left: calc(0.3rem - var(--espace-2));
}

.tableau-cartes [data-carte="lieu"] + [data-carte="lieu"]::before {
  content: "· ";
  color: var(--couleur-texte-discret);
}

.tableau-cartes [data-carte="etat"] {
  order: 7;
}

/* R-DET-89 (vague design 4) : en carte, les montants (vente, achat, marge…) une ligne chacun, libellés sur une
   même colonne et montants alignés à gauche : plus d'escalier. */
.tableau-cartes [data-carte="prix"] {
  order: 9;
  flex-basis: 100%;
  font-size: 0.9375rem;
}

.tableau-cartes td[data-carte="prix"] {
  text-align: start;
  white-space: normal;
}

.tableau-cartes [data-carte="prix"][data-carte-libelle]::before {
  display: inline-block;
  min-width: 6rem;
}

/* Intertitre d'un groupe de lignes (famille d'un référentiel) : un titre entre les cartes, pas une carte. */
.tableau-cartes tr.tableau__groupe {
  margin: var(--espace-2) 0 var(--espace-1);
  padding: 0;
  border: 0;
  background: none;
}

.tableau-cartes tr.tableau__groupe th {
  display: block;
  text-align: left;
}

@container liste (min-width: 1200px) {
  .tableau-cartes tr.tableau__groupe th {
    display: table-cell;
  }
}

/* R-DET-88 (vague design 4) : le bouton « Actions » sur la ligne du titre, à droite ; la carte gagne une rangée. */
.tableau-cartes [data-carte="actions"] {
  order: 2;
  margin-left: auto;
}

/* Intitulé court d'une valeur ambiguë seule (« AR », « Entré le », « Vente ») : carte seulement. */
/* R-DET-88 : le libellé est séparé de sa valeur (« Objets : … »). */
.tableau-cartes [data-carte-libelle]::before {
  content: attr(data-carte-libelle) "\00a0:\00a0";
  color: var(--couleur-texte-discret);
  font-weight: 400;
}

.tableau-cartes .libelle-carte {
  color: var(--couleur-texte-discret);
}

.ligne-detail {
  display: block;
  color: var(--couleur-texte-discret);
  font-size: 0.875rem;
  font-weight: 400;
}

.tableau-cartes [data-carte="etat"] .ligne-detail {
  display: inline;
  margin-left: var(--espace-1);
}

/* Achat et marge : une ligne chacun dans la carte, jamais un « · » en début de ligne. */
.tableau-cartes .ligne-valeur {
  display: block;
  white-space: nowrap;
}

/* Toucher la carte ouvre la fiche : le lien du code couvre toute la carte ; la case à cocher et
   le menu Actions passent au-dessus. Le focus clavier se voit sur la carte entière.
   R-DET-87 (vague design 4) : le nom d'une ligne est un lien bleu, sans soulignement (il revient au survol et au
   focus) ; en tableau, ni gras. Les autres liens d'une ligne suivent le même style. */
.ligne-lien,
.tableau-cartes td a:not([class]) {
  color: var(--bleu-secondaire-texte);
  text-decoration: none;
}

.ligne-lien strong {
  font-weight: inherit;
}

.ligne-lien:hover,
.tableau-cartes td a:not([class]):is(:hover, :focus-visible) {
  text-decoration: underline;
}

.tableau-cartes .ligne-lien::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--rayon);
}

.tableau-cartes .ligne-lien:focus-visible {
  outline: none;
}

.tableau-cartes tr:has(.ligne-lien:focus-visible) {
  outline: 3px solid var(--bleu-secondaire);
  outline-offset: 2px;
}

/* R-DET-78 (recette du 14/10/2026) : menu « Actions » d'une ligne — un bouton secondaire à chevron qui ouvre un
   popover natif (sans script, R-TEC-50) : fermé au clic ailleurs et par Échap, focus rendu au bouton, affiché
   au-dessus de tout, donc jamais coupé par le cadre du tableau ni par une liste qui défile. Les actions gardent
   leur ordre : Ouvrir, l'action principale, puis le reste. */
.actions-ligne {
  anchor-scope: --actions-ligne;
}

.actions-ligne__bouton {
  anchor-name: --actions-ligne;
  white-space: nowrap;
}

.actions-ligne__bouton::after {
  content: "";
  width: 1rem;
  height: 1rem;
  background: currentColor;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* Repli : sans positionnement par ancre, le menu s'ouvre en feuille en bas de l'écran, sur toute la largeur
   (décision JS Q2), comme sur téléphone ; toujours lisible, jamais coupé. */
.actions-ligne__liste[popover] {
  position: fixed;
  inset: auto 0 0;
  width: 100%;
  max-width: none;
  max-height: 70vh;
  margin: 0;
  padding: var(--espace-1) 0;
  overflow-y: auto;
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon) var(--rayon) 0 0;
  background: var(--couleur-surface);
  color: var(--texte);
  box-shadow: 0 12px 24px rgb(0 0 0 / 12%);
}

/* Ancré sous le bouton, aligné sur son bord droit ; s'ouvre vers le haut (ou de l'autre côté) quand la place
   manque, dernière ligne comprise. */
@supports (anchor-scope: --a) and (position-area: block-end) {
  .actions-ligne__liste[popover] {
    position-anchor: --actions-ligne;
    position-area: block-end span-inline-start;
    position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
    inset: auto;
    width: max-content;
    min-width: 14rem;
    max-width: min(24rem, calc(100vw - 2rem));
    max-height: min(70vh, 30rem);
    margin-block: 0.25rem;
    border-radius: var(--rayon);
  }
}

.actions-ligne__liste:popover-open {
  display: flex;
  flex-direction: column;
}

/* Entrées verticales, pleine largeur, 44px (Q3), quel que soit le style d'origine de l'action. */
.actions-ligne__liste:popover-open > :is(a, button),
.actions-ligne__liste:popover-open > form > button,
.actions-ligne__liste:popover-open > .confirmation > summary {
  display: flex;
  justify-content: flex-start;
  width: 100%;
  min-height: var(--cible-tactile-min);
  padding: 0 var(--espace-2);
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--bleu-principal);
  font-weight: 600;
  text-align: left;
}

.actions-ligne__liste:popover-open > :is(a, button):hover,
.actions-ligne__liste:popover-open > form > button:hover,
.actions-ligne__liste:popover-open > .confirmation > summary:hover {
  background: color-mix(in srgb, var(--bleu-principal) 8%, var(--couleur-surface));
}

.actions-ligne__liste:popover-open > form {
  max-width: none;
  margin: 0;
}

/* Un geste qui demande un champ (zone d'arrivée d'un élément reçu tardivement) : le champ garde sa place. */
.actions-ligne__liste:popover-open > form:has(select, input:not([type="hidden"])) {
  padding: var(--espace-1) var(--espace-2);
}

/* Un geste destructif garde sa couleur ; sa confirmation se déplie dans le menu (Q4). */
.actions-ligne__liste:popover-open > .confirmation {
  display: block;
  width: 100%;
}

.actions-ligne__liste:popover-open > .confirmation > summary.bouton-destructif {
  color: var(--etat-erreur-texte);
}

.actions-ligne__liste:popover-open > .confirmation > .confirmation__corps {
  margin: 0 var(--espace-2) var(--espace-1);
}

/* Geste à formulaire (photo d'une offre) : un <details> qui se déplie sur place, dans le menu ou seul. */
.repli-action > summary {
  list-style: none;
}

.repli-action > summary::-webkit-details-marker {
  display: none;
}

.repli-action__corps {
  margin-top: var(--espace-1);
}

.actions-ligne__liste:popover-open > .repli-action > summary {
  display: flex;
  justify-content: flex-start;
  width: 100%;
  min-height: var(--cible-tactile-min);
  padding: 0 var(--espace-2);
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--bleu-principal);
  font-weight: 600;
}

.actions-ligne__liste:popover-open > .repli-action > .repli-action__corps {
  margin: 0 var(--espace-2) var(--espace-1);
}

/* Pastille d'état : une seule famille (critique design du 06/10/2026), six tons. Neutre = état normal
   (En stock, Actif), atténué = état passé (Sorti, Désactivé), information = engagement (Vendu, Réservé,
   En transit, Au dépôt), succès = terminé ou validé, alerte = à traiter (Dormant, En retard, Solde à
   payer), erreur = bloquant. Le libellé est toujours porté ; succès, alerte et erreur portent en plus
   leur icône, dessinée ici (masque, couleur du texte) : un état ne tient jamais à la couleur seule.
   Contrastes du texte sur son fond ≥ 5,2:1. */
.pastille-etat {
  display: inline-flex;
  align-items: center;
  gap: 0.3125rem;
  padding: 0.25rem 0.5rem;
  border-radius: var(--rayon);
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.25;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  vertical-align: middle;
}

/* Deux pastilles côte à côte ne se touchent pas (hors conteneur flex, qui a son propre écart). */
.pastille-etat + .pastille-etat {
  margin-left: 0.375rem;
}

.fiche-entete__etat > .pastille-etat,
.barre-actions > .pastille-etat {
  margin-left: 0;
}

.pastille-etat--neutre {
  background: #e8eef1;
  color: var(--texte);
}

.pastille-etat--attenue {
  background: #eef0f2;
  color: var(--couleur-texte-discret);
}

.pastille-etat--information {
  background: var(--etat-information-fond);
  color: var(--etat-information-texte);
}

.pastille-etat--succes {
  background: var(--etat-succes-fond);
  color: var(--etat-succes-texte);
}

.pastille-etat--alerte {
  background: var(--etat-alerte-fond);
  color: var(--etat-alerte-texte);
}

.pastille-etat--erreur {
  background: var(--etat-erreur-fond);
  color: var(--etat-erreur-texte);
}

.pastille-etat--succes::before,
.pastille-etat--alerte::before,
.pastille-etat--erreur::before {
  content: "";
  flex: none;
  width: 0.9375rem;
  height: 0.9375rem;
  background: currentColor;
  mask: var(--pastille-icone) center / contain no-repeat;
}

.pastille-etat--succes {
  --pastille-icone: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M8 12.5l2.5 2.5L16 9'/%3E%3C/svg%3E");
}

.pastille-etat--alerte {
  --pastille-icone: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 4l9 16H3L12 4z'/%3E%3Cpath d='M12 10v4'/%3E%3Cpath d='M12 17.5v.01'/%3E%3C/svg%3E");
}

.pastille-etat--erreur {
  --pastille-icone: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M9 9l6 6M15 9l-6 6'/%3E%3C/svg%3E");
}

/* Tableau, à partir de 1 200px de liste. */
@container liste (min-width: 1200px) {
  .tableau-cartes {
    display: table;
    border-collapse: separate;
    border-spacing: 0;
    border: 1px solid var(--couleur-bordure);
    border-radius: var(--rayon);
    background: var(--couleur-surface);
  }

  .tableau-cartes thead th {
    background: var(--fond-entete-tableau);
  }

  .tableau-cartes thead th:first-child {
    border-top-left-radius: calc(var(--rayon) - 1px);
  }

  .tableau-cartes thead th:last-child {
    border-top-right-radius: calc(var(--rayon) - 1px);
  }

  .tableau-cartes tbody tr:last-child > * {
    border-bottom: 0;
  }

  .tableau-cartes thead {
    position: static;
    display: table-header-group;
    width: auto;
    height: auto;
    overflow: visible;
    clip: auto;
    white-space: normal;
  }

  .tableau-cartes tbody {
    display: table-row-group;
  }

  .tableau-cartes tr {
    position: static;
    display: table-row;
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
  }

  .tableau-cartes tr:hover {
    background: color-mix(in srgb, var(--bleu-principal) 4%, transparent);
  }

  .tableau-cartes tr::before,
  .tableau-cartes tr::after {
    content: none;
  }

  .tableau-cartes th,
  .tableau-cartes td,
  .tableau-cartes td:empty {
    display: table-cell;
    padding: var(--espace-1);
    border-bottom: 1px solid var(--couleur-bordure);
    vertical-align: middle;
  }

  /* En tableau, un mot ne se coupe jamais : la coupure n'existe que pour la carte, sinon chaque colonne
     tombe à la largeur d'une lettre (« Ouvri / r »). */
  .tableau-cartes td {
    max-width: none;
    overflow-wrap: normal;
  }

  .tableau-cartes [data-carte="sous-titre"],
  .tableau-cartes [data-carte="meta"],
  .tableau-cartes [data-carte="prix"] {
    color: inherit;
    font-size: 1rem;
    font-weight: 400;
  }

  .tableau-cartes [data-carte="lieu"] + [data-carte="lieu"] {
    margin-left: 0;
  }

  .tableau-cartes [data-carte="lieu"] + [data-carte="lieu"]::before,
  .tableau-cartes [data-carte-libelle]::before,
  .tableau-cartes .ligne-lien::after {
    content: none;
  }

  .tableau-cartes .libelle-carte {
    display: none;
  }

  .tableau-cartes .ligne-lien:focus-visible {
    outline: 3px solid var(--bleu-secondaire);
    text-decoration: underline;
  }

  .tableau-cartes .ligne-valeur {
    display: block;
  }

  .tableau-cartes [data-carte="etat"] .ligne-detail {
    display: block;
    margin-left: 0;
  }

  /* Code-barres sur une ligne : il se couperait sur ses traits d'union. Les détails courts (type,
     n° AR, dates) passent à la ligne quand la place manque, plutôt que d'élargir la table au-delà
     de 1 280 px (Stock bois, Articles ; critique du 06/10/2026). */
  .tableau-cartes [data-carte="titre"] {
    white-space: nowrap;
    /* R-DET-87 : en tableau, le nom de la ligne n'est pas en gras. */
    font-weight: 400;
  }

  /* Un code (code d'élément, n° AR, référence, contremarque) reste sur une ligne : il se couperait sur
     son trait d'union (« CH-110- / CLIP »). Un libellé long marqué texte-long passe à la ligne ; le
     détail sous un code aussi. */
  .tableau-cartes td.reference:not(.texte-long) {
    white-space: nowrap;
  }

  .tableau-cartes td.reference .ligne-detail {
    white-space: normal;
  }

  /* Un détail long (auteur « Nom (e-mail) », référence, résumé du journal) passe à la ligne : sur une seule
     ligne, il élargissait la table au-delà de 1 280 px (critique du 06/10/2026). */
  .tableau-cartes .texte-long {
    min-width: 10rem;
    white-space: normal;
  }

  /* Montants : chiffres à largeur fixe, alignés à droite, sur une ligne ; leur intitulé peut passer
     à la ligne (« Prix d'achat HT / Marge HT »). */
  .tableau-cartes .nombre,
  .tableau-cartes th[data-tableau-colonne="PrixVenteTtc"] {
    text-align: right;
  }

  .tableau-cartes td.nombre {
    white-space: nowrap;
  }

  /* R-DET-89 : la grille des montants est propre aux cartes ; en tableau, un montant reste aligné à droite. */
  .tableau-cartes td.nombre[data-carte="prix"] {
    text-align: right;
    white-space: nowrap;
  }

  /* Liste trop large pour tenir même ainsi (Suivi, Sorties : onze colonnes) : la table défile dans
     son cadre, en-têtes compris, et la page ne défile pas. Le cadre se signale (critique du
     06/10/2026) : une ombre sur le bord derrière lequel des colonnes sont masquées, sans script (les
     fonds « local » suivent le contenu et couvrent l'ombre au bout du défilement), et une barre de
     défilement toujours visible, même sous macOS. */
  .liste__defilement {
    overflow-x: auto;
    background:
      linear-gradient(to right, var(--couleur-fond) 30%, transparent) left center / 2.5rem 100% no-repeat local,
      linear-gradient(to left, var(--couleur-fond) 30%, transparent) right center / 2.5rem 100% no-repeat local,
      linear-gradient(to right, color-mix(in srgb, var(--texte) 18%, transparent), transparent) left center / 0.875rem 100% no-repeat scroll,
      linear-gradient(to left, color-mix(in srgb, var(--texte) 18%, transparent), transparent) right center / 0.875rem 100% no-repeat scroll;
  }

  .liste__defilement::-webkit-scrollbar {
    height: 0.75rem;
  }

  .liste__defilement::-webkit-scrollbar-track {
    border-radius: 999px;
    background: color-mix(in srgb, var(--bleu-principal) 8%, transparent);
  }

  .liste__defilement::-webkit-scrollbar-thumb {
    border: 2px solid transparent;
    border-radius: 999px;
    background: color-mix(in srgb, var(--bleu-principal) 45%, transparent) padding-box;
  }

  @supports not selector(::-webkit-scrollbar) {
    .liste__defilement {
      scrollbar-color: color-mix(in srgb, var(--bleu-principal) 45%, transparent) color-mix(in srgb, var(--bleu-principal) 8%, transparent);
    }
  }

  /* R-DET-78 : en tableau, le bouton « Actions » est compact, 32px : usage souris et clavier sur PC ; la cible
     de 44px reste celle des cartes tactiles (PRODUCT.md, Accessibilité). Même à une seule action, le menu
     s'ouvre par ce bouton (R-DET-78 amendée). */
  .actions-ligne__bouton {
    min-height: 32px;
    padding: 0 var(--espace-1);
    border-width: 1px;
    font-size: 0.875rem;
    white-space: nowrap;
  }
}

/* Fiche d'un élément (refonte des fiches) : en-tête, grille d'informations, blocs. */
.fiche-entete {
  margin-bottom: var(--espace-2);
}

.fiche-entete__code {
  margin: 0;
  color: var(--texte);
  font-size: 1.25rem;
  font-weight: 600;
}

.fiche-entete h1 {
  margin: 0.25rem 0 var(--espace-1);
}

.fiche-entete__etat {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-1) var(--espace-2);
  margin: 0;
}

/* Grille d'informations : une colonne sur téléphone, deux à partir de 640px ; libellé au-dessus
   de sa valeur. Une valeur absente n'est pas rendue. */
.fiche-grille {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--espace-2) var(--espace-3);
  margin: var(--espace-2) 0;
}

.fiche-grille > div {
  min-width: 0;
}

.fiche-grille dt {
  color: var(--couleur-texte-discret);
}

.fiche-grille dd {
  margin: 0.125rem 0 0;
  overflow-wrap: anywhere;
}

@media (min-width: 640px) {
  .fiche-grille {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Bloc à part dans une fiche (prix, vente en cours), et bloc de création ou d'invitation des
   pages d'administration (.bloc-creation). */
.bloc,
.bloc-creation {
  margin: var(--espace-3) 0;
  padding: var(--espace-2);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
  background: var(--couleur-surface);
}

.bloc > h2,
.bloc-creation > h2 {
  margin-top: 0;
  font-size: var(--taille-h3);
}

/* Migration v1, écran « Ce compte v1 est… » (lot 19d) : seuls les champs du cas choisi s'affichent, sans
   script. Un navigateur qui ne connaît pas :has() affiche tous les champs ; le serveur ne lit que ceux du cas. */
@supports selector(:has(*)) {
  .import-v1 [data-cas] {
    display: none;
  }

  .import-v1:has(#choix-magasin-nouveau:checked) [data-cas~="magasin-nouveau"],
  .import-v1:has(#choix-magasin-vide:checked) [data-cas~="magasin-vide"],
  .import-v1:has(#choix-depot-nouveau:checked) [data-cas~="depot-nouveau"],
  .import-v1:has(#choix-depot-stock:checked) [data-cas~="depot-stock"] {
    display: block;
  }
}

/* Aperçu du logo d'une enseigne (référentiel des marques, SuperAdmin) : jamais déformé. */
.logo-enseigne {
  display: block;
  max-width: 12rem;
  max-height: 4rem;
  height: auto;
  margin: var(--espace-1) 0;
  object-fit: contain;
}

/* Adresse e-mail affichée seule (Mon compte) : sans espace, elle se coupe n'importe où plutôt que de
   déborder à 320 px (critique du 06/10/2026). */
.adresse-longue {
  overflow-wrap: anywhere;
}

/* Lien public d'une opération : une adresse n'a pas d'espace, elle se coupe n'importe où plutôt que
   de faire déborder la fiche de 200 px à 320 px (critique du 06/10/2026). */
.lien-public {
  overflow-wrap: anywhere;
}

/* Mention d'une liste qui défile dans son cadre (Suivi, Sorties) : affichée dès que la liste passe en
   tableau (requête de taille, comprise partout ; onze colonnes y dépassent toujours le cadre), masquée
   quand le cadre ne défile pas là où la requête d'état de défilement existe (Chrome, Edge), sans script ;
   collée au bord gauche du cadre pendant le défilement. */
.liste__defilement {
  container-type: scroll-state;
}

.liste__defilement-aide {
  display: none;
  position: sticky;
  left: 0;
  margin: 0 0 var(--espace-1);
  color: var(--couleur-texte-discret);
  font-size: 0.875rem;
}

@container liste (min-width: 1200px) {
  .liste__defilement-aide {
    display: block;
  }
}

@container not scroll-state(scrollable: x) {
  .liste__defilement-aide {
    display: none;
  }
}

/* Barre de filtres en ligne (journal d'audit, lectures d'étiquette, référentiels). En ligne et non
   en colonne comme un formulaire : sinon chaque champ prenait 12rem de haut, collé à droite, et une
   date débordait de 32 px à 320 px (critique du 06/10/2026). */
.barre-outils {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--espace-1) var(--espace-2);
  max-width: none;
  margin: var(--espace-2) 0;
}

.champ--en-ligne {
  flex: 1 1 12rem;
  min-width: 0;
}

/* Listes resserrées (détail d'une demande d'ajout, faits d'un import). */
.liste-compacte,
.liste-faits {
  margin: 0;
  padding-left: var(--espace-3);
}

.liste-compacte li,
.liste-faits li {
  margin: 0.125rem 0;
}

.liste-compacte {
  font-size: 0.875rem;
}

/* Administration : cartes regroupées en sections. */
.section-cartes > h2 {
  margin-bottom: 0;
}

.section-cartes .grille-cartes,
.section-cartes > .gestes-rapides,
.section-cartes > .cartes-sites {
  margin-top: var(--espace-2);
}

/* Actions d'une ligne de la liste des utilisateurs. */
.barre-actions--ligne {
  margin: 0;
}

/* En tableau (640px et plus) : l'adresse et les deux actions d'une ligne tiennent sur une ligne. */
@media (min-width: 640px) {
  .barre-actions--ligne {
    flex-wrap: nowrap;
  }

  .barre-actions--ligne .bouton-secondaire,
  .barre-actions--ligne .bouton-destructif {
    white-space: nowrap;
  }

  .tableau-reactif .courriel {
    white-space: nowrap;
  }
}

/* Bloc repliable (Déplacer, Ré-étiqueter, Ajouter une photo…) : fermé par défaut, ouvert par
   son bouton de la barre d'actions (lien ?ouvrir=…#…, rendu ouvert par le serveur) ou après
   une erreur de saisie. Sans script. */
.bloc-repliable {
  margin: var(--espace-2) 0;
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
  background: var(--couleur-surface);
  scroll-margin-top: var(--espace-2);
}

.bloc-repliable > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: var(--cible-tactile-min);
  padding: 0 var(--espace-2);
  color: var(--bleu-principal);
  font-weight: var(--graisse-h3);
  list-style: none;
  cursor: pointer;
}

.bloc-repliable > summary::-webkit-details-marker {
  display: none;
}

.bloc-repliable > summary::after {
  content: "+";
  font-size: 1.25rem;
  font-weight: 400;
}

.bloc-repliable[open] > summary::after {
  content: "−";
}

.bloc-repliable > summary:hover {
  background: color-mix(in srgb, var(--bleu-principal) 6%, var(--couleur-surface));
}

.bloc-repliable__corps {
  padding: 0 var(--espace-2) var(--espace-2);
}

/* Historique d'un élément en liste compacte (téléphone) : pas de carte encadrée, une ligne de
   séparation entre deux événements. En tableau à partir de 1 200px, comme les listes. */
.tableau-cartes--compact tr {
  margin: 0;
  padding: var(--espace-1) 0;
  border: 0;
  border-bottom: 1px solid var(--couleur-bordure);
  border-radius: 0;
  background: none;
}

.tableau-cartes--compact tr:hover {
  border-color: var(--couleur-bordure);
}

.tableau-cartes--compact [data-carte="sous-titre"] {
  font-size: 0.9375rem;
  font-weight: 400;
}

/* L'historique vit dans une fiche (960px au plus) : il passe en tableau dès 640px de liste,
   pas à 1 200px comme les listes métier. */
@container liste (min-width: 640px) {
  .tableau-cartes--compact {
    display: table;
    border-collapse: separate;
    border-spacing: 0;
    border: 1px solid var(--couleur-bordure);
    border-radius: var(--rayon);
    background: var(--couleur-surface);
  }

  .tableau-cartes--compact thead th {
    background: var(--fond-entete-tableau);
  }

  .tableau-cartes--compact thead th:first-child {
    border-top-left-radius: calc(var(--rayon) - 1px);
  }

  .tableau-cartes--compact thead th:last-child {
    border-top-right-radius: calc(var(--rayon) - 1px);
  }

  .tableau-cartes--compact tbody tr:last-child > * {
    border-bottom: 0;
  }

  .tableau-cartes--compact thead {
    position: static;
    display: table-header-group;
    width: auto;
    height: auto;
    overflow: visible;
    clip: auto;
    white-space: normal;
  }

  .tableau-cartes--compact tbody {
    display: table-row-group;
  }

  .tableau-cartes--compact tr {
    display: table-row;
    padding: 0;
    border: 0;
  }

  .tableau-cartes--compact tr::before,
  .tableau-cartes--compact tr::after,
  .tableau-cartes--compact [data-carte-libelle]::before {
    content: none;
  }

  /* Recette du 14/10/2026 : en tableau, le lien d'une ligne ne s'étire plus. Un table-row n'est pas un bloc
     conteneur fiable : sans cette règle, son pseudo-élément couvrait la page, en-tête compris, et tout clic
     ouvrait la dernière ligne (menu SAV qui ouvrait une navette). Même neutralisation qu'à 1 200 px. */
  .tableau-cartes--compact .ligne-lien::after {
    content: none;
  }

  .tableau-cartes--compact .ligne-lien:focus-visible {
    outline: 3px solid var(--bleu-secondaire);
    text-decoration: underline;
  }

  .tableau-cartes--compact [data-carte="titre"] {
    font-weight: 400;
  }

  /* Marge latérale resserrée : sept colonnes aux intitulés insécables (« Contremarque »,
     « Destinataire ») tiennent dans une fiche de 656px (mouvements d'une fourniture). */
  .tableau-cartes--compact th,
  .tableau-cartes--compact td,
  .tableau-cartes--compact td:empty {
    display: table-cell;
    padding: var(--espace-1) 0.375rem;
    border-bottom: 1px solid var(--couleur-bordure);
    vertical-align: top;
  }

  /* Même règle que les listes en tableau (critique du 06/10/2026) : un mot ne se coupe jamais
     (« Réassor / t »), un code reste sur une ligne (il se couperait sur ses traits d'union), un
     libellé (« Réservation close par la sortie ») et les détails courts passent à la ligne quand
     la place manque plutôt que de faire déborder la fiche. */
  .tableau-cartes--compact td {
    max-width: none;
    overflow-wrap: normal;
  }

  .tableau-cartes--compact [data-carte="titre"].reference,
  .tableau-cartes--compact td.reference:not(.texte-long) {
    white-space: nowrap;
  }

  .tableau-cartes--compact [data-carte="sous-titre"],
  .tableau-cartes--compact [data-carte="meta"] {
    color: inherit;
    font-size: 1rem;
  }
}

/* Masqué à l'écran, lu par les lecteurs d'écran (titre de l'écran de scan, h1 de l'accueil). */
.visuellement-masque {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Champ fichier : sans script, le champ natif reste visible sous son libellé ; fichier-choisi.js
   le cache derrière le bouton « Ajouter une photo » et affiche le nom du fichier choisi. */
.champ-fichier--ameliore input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.champ-fichier--ameliore label {
  align-self: flex-start;
}

.champ-fichier--ameliore:focus-within label {
  outline: 3px solid var(--bleu-secondaire);
  outline-offset: 2px;
}

.champ-fichier__nom:empty {
  display: none;
}

.champ-fichier__nom {
  font-weight: 600;
  overflow-wrap: anywhere;
}

/* Bouton pleine largeur (Utiliser la caméra). */
.bouton-large {
  width: 100%;
}

/* Écran de scan : la file hors réseau en bandeau permanent (information ; alerte dès un conflit). */
.bandeau-file {
  margin: 0 0 var(--espace-1);
}

.bandeau-file__icone[hidden] {
  display: none;
}

.detail-file {
  margin-bottom: var(--espace-2);
}

.detail-file > summary {
  min-height: var(--cible-tactile-min);
  display: flex;
  align-items: center;
  color: var(--bleu-principal);
  font-weight: 600;
  cursor: pointer;
}

/* Réserve et zone de la réception en résumé : elles reprennent le dernier choix. */
.resume-emplacement > summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem var(--espace-1);
  min-height: var(--cible-tactile-min);
  list-style: none;
  cursor: pointer;
}

.resume-emplacement > summary::-webkit-details-marker {
  display: none;
}

.resume-emplacement__libelle {
  font-weight: var(--graisse-libelle);
}

.resume-emplacement__modifier {
  color: var(--bleu-secondaire-texte);
  text-decoration: underline;
}

.resume-emplacement[open] .resume-emplacement__modifier {
  display: none;
}

.resume-emplacement__champs {
  display: flex;
  flex-direction: column;
  gap: var(--espace-2);
  margin-top: var(--espace-1);
}

/* Écran de scan : les formulaires prennent toute la largeur de l'écran. */
[data-scan-ecran] form {
  max-width: none;
}

/* R-DET-68 (recette du 14/10/2026), R-DET-92 (vague design 4) : la recherche, en tête de l'accueil, occupe la
   largeur d'un formulaire du cadre. */
/* R-DET-99 (maquette A, recette JS du 14/10, soir) : la recherche en bandeau sur toute la largeur du cadre. */
.recherche-accueil {
  max-width: none;
  padding: var(--espace-2);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
  background: var(--couleur-surface);
}

@media (min-width: 640px) {
  .recherche-accueil {
    padding: var(--espace-3);
  }
}

.recherche-accueil input[type="text"] {
  min-height: 56px;
  padding-inline: var(--espace-2);
  font-size: 1.125rem;
}

.recherche-accueil .bouton-action {
  min-height: 56px;
  font-size: 1.125rem;
}

@media (min-width: 640px) {
  .recherche-accueil__ligne {
    display: flex;
    align-items: flex-end;
    gap: var(--espace-1);
  }

  .recherche-accueil__ligne .champ {
    flex: 1;
  }
}

/* QR code d'activation de la double authentification (R-2FA-01) : rendu en SVG dans la page,
   dimensionné ici plutôt qu'en attribut de style — la CSP à nonce ne laisse passer aucun style
   en ligne (R-TEC-40). */
.qr-code {
  display: block;
  width: 12rem;
  height: 12rem;
  margin: var(--espace-2) 0;
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
}

/* Codes de secours : lisibles, alignés, faciles à recopier ou à imprimer. */
.codes-de-secours {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  gap: var(--espace-1) var(--espace-2);
  margin: var(--espace-2) 0;
  padding: 0;
  list-style: none;
}

.codes-de-secours li {
  padding: var(--espace-1) var(--espace-2);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
  background: #fff;
  text-align: center;
}

/* Ligne de titre d'un groupe dans un tableau : les types d'éléments se lisent famille par
   famille (R-CLA-03). Sur téléphone, la ligne devient un bloc comme les autres. */
.tableau__groupe th {
  padding-top: var(--espace-3);
  color: var(--bleu-principal);
  text-align: left;
}

.tableau__groupe th .discret {
  font-weight: 400;
}

/* Grille de cartes de menu : un point d'entrée par section (R-NAV-02). Une colonne sur
   téléphone, autant que la largeur le permet ensuite. */
/* R-DET-91 (vague design 4) : une grille sans trous — les cartes de la dernière rangée s'élargissent pour remplir
   la ligne (trois par rangée au plus dans le cadre de 1 280 px). */
.grille-cartes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-2);
  margin-top: var(--espace-3);
}

.grille-cartes > * {
  flex: 1 1 20rem;
  min-width: 0;
}

/* R-DET-94 : sous 640 px, une liste de liens par section — icône et titre sur une ligne de 44 px, sans la
   description, qui reprend au-delà. */
@media (max-width: 639px) {
  .section-cartes .grille-cartes {
    flex-direction: column;
    gap: 0;
    border: 1px solid var(--couleur-bordure);
    border-radius: var(--rayon);
    background: var(--couleur-surface);
    overflow: hidden;
  }

  .section-cartes .grille-cartes > .carte-menu {
    flex: none;
    flex-direction: row;
    align-items: center;
    gap: var(--espace-2);
    min-height: var(--cible-tactile-min);
    padding: var(--espace-1) var(--espace-2);
    border: 0;
    border-radius: 0;
  }

  .section-cartes .grille-cartes > .carte-menu + .carte-menu {
    border-top: 1px solid var(--couleur-bordure);
  }

  .section-cartes .grille-cartes > .carte-menu .carte-menu__texte {
    display: none;
  }
}

.carte-menu {
  display: flex;
  flex-direction: column;
  gap: var(--espace-1);
  padding: var(--espace-3);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
  background: #fff;
  color: var(--couleur-texte);
  text-decoration: none;
}

.carte-menu:hover,
.carte-menu:focus-visible {
  border-color: var(--bleu-principal);
  box-shadow: 0 2px 8px rgb(0 0 0 / 8%);
}

.carte-menu__icone {
  width: 32px;
  height: 32px;
  color: var(--bleu-principal);
}

.carte-menu__titre {
  font-size: 1.125rem;
  font-weight: 600;
  color: var(--bleu-principal);
}

.carte-menu__texte {
  color: var(--couleur-texte-discret);
  font-size: 0.9375rem;
}

/* Tableau de bord des administrateurs (R-TDB-01, R-TDB-03) : une colonne à 320 px, trois ou quatre à 1 280 px, dans
   le même ordre ; aucune carte ne déborde (les textes longs passent à la ligne). La bordure de gauche reprend le ton
   de la pastille, qui porte aussi son libellé : la gravité ne tient jamais à la couleur seule. */
.tableau-de-bord > h2 {
  margin-bottom: 0;
}

.cartes-indicateurs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  gap: var(--espace-2);
  margin: var(--espace-2) 0 0;
  padding: 0;
  list-style: none;
}

.carte-indicateur {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  border: 1px solid var(--couleur-bordure);
  border-left-width: 4px;
  border-radius: var(--rayon);
  background: var(--couleur-surface);
}

.carte-indicateur--erreur {
  border-left-color: var(--etat-erreur-texte);
}

.carte-indicateur--alerte {
  border-left-color: var(--etat-alerte-texte);
}

.carte-indicateur--information {
  border-left-color: var(--etat-information-texte);
}

.carte-indicateur:hover,
.carte-indicateur:focus-within {
  border-color: var(--bleu-principal);
  box-shadow: 0 2px 8px rgb(0 0 0 / 8%);
}

/* Le lien (magasin) ou le bouton d'entrée dans le premier site (groupe) couvre toute la carte. */
.carte-indicateur__principal {
  display: flex;
  flex-direction: column;
  gap: var(--espace-1);
  width: 100%;
  min-width: 0;
  padding: var(--espace-2) var(--espace-3);
  border: 0;
  background: none;
  color: var(--texte);
  font: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.carte-indicateur__formulaire .carte-indicateur__principal::after {
  content: "";
  position: absolute;
  inset: 0;
}

/* Sur une carte du groupe, les sites de la répartition passent au-dessus de la surface étirée. */
.carte-indicateur > .carte-indicateur__lignes,
.carte-indicateur > .carte-indicateur__autres {
  position: relative;
  z-index: 1;
  padding: 0 var(--espace-3) var(--espace-2);
}

.carte-indicateur__entete {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--espace-1);
}

.carte-indicateur__libelle {
  font-weight: 600;
  color: var(--bleu-principal);
  overflow-wrap: anywhere;
}

.carte-indicateur__valeur {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.carte-indicateur__precision {
  color: var(--couleur-texte-discret);
  font-size: 0.9375rem;
  overflow-wrap: anywhere;
}

.carte-indicateur__lignes {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 0.9375rem;
}

.carte-indicateur__ligne {
  display: flex;
  justify-content: space-between;
  gap: var(--espace-1);
  min-width: 0;
  overflow-wrap: anywhere;
}

.carte-indicateur__ligne > .formulaire-en-ligne {
  display: block;
  width: 100%;
}

.carte-indicateur__ligne--autres {
  color: var(--couleur-texte-discret);
}

.carte-indicateur__nombre {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.carte-indicateur__site {
  display: flex;
  justify-content: space-between;
  gap: var(--espace-1);
  width: 100%;
  min-height: var(--cible-tactile-min);
  align-items: center;
  padding: 0;
  border: 0;
  background: none;
  color: var(--bleu-secondaire-texte);
  font: inherit;
  text-align: left;
  text-decoration: underline;
  cursor: pointer;
}

.carte-indicateur__autres > summary {
  min-height: var(--cible-tactile-min);
  display: flex;
  align-items: center;
  color: var(--bleu-secondaire-texte);
  cursor: pointer;
}

/* Code de rattachement : lu et recopié à la main, donc grand et espacé (R-RAT-05). */
.code-rattachement {
  font-size: 1.75rem;
  letter-spacing: 0.15em;
  font-weight: 600;
  color: var(--bleu-principal);
  margin: var(--espace-2) 0;
}

/* Bloc « Stock du groupe » : le stock propre à un compte Groupe est présenté à part de
   la liste des magasins, jamais comme un magasin de plus (R-TEN-06). */
.bloc-stock-groupe {
  margin-top: var(--espace-4);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
  padding: var(--espace-2) var(--espace-3);
  background: color-mix(in srgb, var(--bleu-principal) 5%, var(--couleur-surface));
}

.bloc-stock-groupe h2 {
  margin-top: 0;
}

/* Formulaire en ligne : un champ et son bouton sur une seule ligne, à l'intérieur d'une
   cellule de tableau (renommage d'un site, par exemple). Passe à la ligne sous 640px. */
.formulaire-ligne {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-1);
}

.formulaire-ligne input:not([type]),
.formulaire-ligne input[type="text"],
.formulaire-ligne select {
  flex: 1 1 10rem;
  min-width: 0;
  margin: 0;
}

.formulaire-ligne button {
  flex: 0 0 auto;
  padding: 0 var(--espace-2);
  font-size: 0.875rem;
}

/* État vide guidé (critique du 06/10/2026) : une phrase qui dit quoi faire et, quand on a le droit, le geste
   qui remplit la liste. Pas d'illustration : la mascotte prévue n'est pas dessinée. */
.etat-vide {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--espace-2);
  margin: var(--espace-2) 0;
  padding: var(--espace-3);
  border: 1px dashed var(--couleur-bordure);
  border-radius: var(--rayon);
  background: var(--couleur-surface);
}

/* Page d'erreur : centrée, sans cadre, avec le logo rond. */
.etat-vide--page {
  align-items: center;
  text-align: center;
  border: 0;
  background: none;
}

.etat-vide--page img {
  width: 96px;
  height: 96px;
}

.etat-vide p {
  margin: 0;
  max-width: 60ch;
  font-size: 1.0625rem;
}

/* Aide de page (décision JS du 07/10/2026) : fermée par défaut, la phrase essentielle reste lisible dans le
   titre ; un clic (ou Entrée) déplie le détail. */
/* R-DET-86 (vague design 4) : un bloc repliable sans bordure latérale épaisse, le même sur toutes les pages. */
.aide-page {
  margin: 0 0 var(--espace-2);
  padding: var(--espace-1) var(--espace-2);
  border-radius: var(--rayon);
  background: var(--etat-information-fond);
  color: var(--etat-information-texte);
}

.aide-page > summary {
  min-height: var(--cible-tactile-min);
  display: flex;
  align-items: center;
  cursor: pointer;
  font-weight: 600;
}

.aide-page > p {
  margin: 0 0 var(--espace-1);
  max-width: 70ch;
  color: var(--texte);
}

/* Une adresse e-mail dans un texte du journal reste sur une ligne : elle se couperait sur son trait d'union
   (« gerant.herblay@ets- / baey.test ») ; le texte autour passe à la ligne (critique design du 07/10/2026). */
.courriel {
  white-space: nowrap;
}

/* Démarrer (docs/21-demarrer.md) : l'avancement, puis cinq gestes numérotés dans l'ordre fixe (R-DMR-01).
   Le premier geste à faire se détache par son contour et son bouton orange ; un geste fait passe au
   vert, son bouton devient un lien discret (R-DMR-02). */
.demarrer-avancement {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-1) var(--espace-2);
  margin: 0 0 var(--espace-3);
}

.demarrer-avancement__texte {
  margin: 0;
  max-width: 60ch;
}

.demarrer-avancement__choix {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-1);
  margin: 0;
}

.demarrer {
  counter-reset: geste;
  display: grid;
  gap: var(--espace-2);
  max-width: 48rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.demarrer__geste {
  counter-increment: geste;
  display: grid;
  grid-template-columns: 2.5rem minmax(0, 1fr);
  column-gap: var(--espace-2);
  align-items: start;
  padding: var(--espace-2) var(--espace-3) var(--espace-3) var(--espace-2);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
  background: var(--couleur-surface);
}

.demarrer__geste::before {
  content: counter(geste);
  grid-row: 1 / span 3;
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  background: #e8eef1;
  color: var(--bleu-principal);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.demarrer__geste--prochain {
  border-color: var(--bleu-principal);
}

.demarrer__geste--prochain::before {
  background: var(--bleu-principal);
  color: #fff;
}

.demarrer__geste--fait::before {
  background: var(--etat-succes-fond);
  color: var(--etat-succes-texte);
}

.demarrer__entete {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-1);
  min-height: 2.5rem;
}

.demarrer__titre {
  margin: 0;
  font-size: var(--taille-h3);
  font-weight: var(--graisse-h2);
}

.demarrer__phrase {
  margin: var(--espace-1) 0 var(--espace-2);
  max-width: 60ch;
}

.demarrer__geste--fait .demarrer__phrase {
  color: var(--couleur-texte-discret);
}

.demarrer__geste > a {
  justify-self: start;
}

.demarrer__lien {
  display: inline-flex;
  align-items: center;
  min-height: var(--cible-tactile-min);
}

@media (max-width: 479px) {
  .demarrer__geste {
    grid-template-columns: 2rem minmax(0, 1fr);
    column-gap: var(--espace-1);
    padding: var(--espace-2);
  }

  .demarrer__geste::before {
    width: 2rem;
    height: 2rem;
  }

  .demarrer__entete {
    min-height: 2rem;
  }
}

/* Pastille de site actif (en-tête) : toujours visible, cliquable seulement si
   l'utilisateur a plusieurs sites accessibles. */
/* Bloc en ligne et non flex : « text-overflow » ne s'applique qu'à un bloc, et un nom de site trop long
   doit finir par des points de suspension, pas être coupé net. */
.pastille-site {
  display: inline-block;
  min-height: var(--cible-tactile-min);
  line-height: calc(var(--cible-tactile-min) - 2px);
  vertical-align: middle;
  padding: 0 var(--espace-2);
  border: 1px solid var(--bleu-principal);
  border-radius: 999px;
  color: var(--bleu-principal);
  font-size: 0.875rem;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  max-width: 12rem;
  overflow: hidden;
  text-overflow: ellipsis;
}

.pastille-site:hover {
  background: color-mix(in srgb, var(--bleu-principal) 8%, transparent);
}

/* Variante non cliquable : un seul site accessible, donc pas de sélecteur. */
.pastille-site--fixe {
  border-color: var(--gris-secondaire);
  color: var(--couleur-texte-discret);
}

.pastille-site--fixe:hover {
  background: transparent;
}

/* Pagination */
/* La pagination passe à la ligne plutôt que de déborder à 320 px. */
.pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-1);
}

.pagination__lien {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--cible-tactile-min);
  min-height: var(--cible-tactile-min);
  border-radius: var(--rayon);
  color: var(--bleu-principal);
  text-decoration: none;
  font-weight: 600;
}

.pagination__lien:hover {
  background: color-mix(in srgb, var(--bleu-principal) 8%, transparent);
}

.pagination__lien[aria-current="page"] {
  background: var(--bleu-principal);
  color: #ffffff;
}

/* R-SAV-07 : les onglets En stock / Sortis / Dormants, même famille visuelle que la pagination. */
/* Les onglets passent à la ligne à 320 px (« Autres sites » ne sort plus de l'écran). */
.onglets {
  display: flex;
  flex-wrap: wrap;
  column-gap: var(--espace-2);
  border-bottom: 1px solid var(--couleur-bordure);
  margin-bottom: var(--espace-2);
}

.onglets__lien {
  display: inline-flex;
  align-items: center;
  min-height: var(--cible-tactile-min);
  padding: 0 var(--espace-1);
  color: var(--couleur-texte-discret);
  text-decoration: none;
  font-weight: 600;
  border-bottom: 3px solid transparent;
}

.onglets__lien[aria-current="page"] {
  color: var(--bleu-principal);
  border-bottom-color: var(--bleu-principal);
}

/* R-SAV-09 : mode et personne de sortie choisis par de gros boutons, pas une liste déroulante. */
.boutons-choix {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-1);
  margin: var(--espace-1) 0;
}

.boutons-choix__sous-titre {
  flex-basis: 100%;
  font-weight: 600;
  color: var(--couleur-texte-discret);
  margin: var(--espace-1) 0 0;
}

.boutons-choix__bouton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--cible-tactile-min);
  min-height: var(--cible-tactile-min);
  padding: 0 var(--espace-2);
  border: 2px solid var(--bleu-principal);
  border-radius: var(--rayon);
  background: #ffffff;
  color: var(--bleu-principal);
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}

.boutons-choix__bouton:hover {
  background: color-mix(in srgb, var(--bleu-principal) 8%, transparent);
}

.boutons-choix__bouton[aria-current="true"] {
  background: var(--bleu-principal);
  color: #ffffff;
}

/* R-SAV-10 : photos d'un colis, servies à leur taille d'origine et réduites ici — pas de
   traitement serveur, une vignette suffit pour l'usage actuel. */
.galerie-photos {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-2);
  margin: var(--espace-1) 0;
}

.galerie-photos__vignette {
  width: 160px;
}

.galerie-photos__vignette img {
  width: 160px;
  height: 160px;
  object-fit: cover;
  border-radius: var(--rayon);
  border: 1px solid var(--couleur-bordure);
  display: block;
}

/* R-PRE-59 : vignettes compactes des photos de prêt (fiche du prêt, page Retour). */
.galerie-photos--petite .galerie-photos__vignette,
.galerie-photos--petite .galerie-photos__vignette img {
  width: 96px;
  height: 96px;
}

/* Liste sans puces des fiches (prolongations, bons, réservations d'un objet, historique). */
/* R-DET-93 (vague design 4) : l'activité des 7 derniers jours en tuiles chiffrées ; une tuile qui mène à sa liste
   est un lien de 44 px au moins, cadre bleu au survol. Accueil du groupe : une carte par site. */
.tuiles-activite {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: var(--espace-1);
  margin: var(--espace-1) 0 var(--espace-2);
}

.tuiles-activite--serrees {
  grid-template-columns: repeat(auto-fit, minmax(6.5rem, 1fr));
  margin-bottom: var(--espace-1);
}

.tuile-activite {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.125rem;
  min-height: var(--cible-tactile-min);
  padding: var(--espace-1) var(--espace-2);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
  background: var(--couleur-surface);
  color: var(--texte);
  text-decoration: none;
}

a.tuile-activite:hover,
a.tuile-activite:focus-visible {
  border-color: var(--bleu-principal);
}

.tuile-activite__nombre {
  color: var(--bleu-principal);
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}

a.tuile-activite .tuile-activite__libelle {
  color: var(--bleu-secondaire-texte);
}

.tuile-activite__libelle {
  color: var(--couleur-texte-discret);
  font-size: 0.875rem;
  font-weight: 600;
}

/* R-DET-99 : les gestes rapides en tuiles (icône, nom, à quoi il sert) ; deux par rangée sur téléphone, sans la
   ligne d'explication, une rangée au-delà. */
.gestes-rapides {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--espace-2);
}

.geste-rapide {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-height: var(--cible-tactile-min);
  padding: var(--espace-2);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
  background: var(--couleur-surface);
  color: var(--texte);
  text-decoration: none;
}

.geste-rapide:hover,
.geste-rapide:focus-visible {
  border-color: var(--bleu-principal);
  box-shadow: 0 2px 8px rgb(0 0 0 / 8%);
}

.geste-rapide .carte-menu__icone {
  width: 28px;
  height: 28px;
  margin-bottom: 0.25rem;
}

.geste-rapide__titre {
  color: var(--bleu-principal);
  font-size: 1.0625rem;
  font-weight: 600;
}

.geste-rapide__texte {
  display: none;
  color: var(--couleur-texte-discret);
  font-size: 0.875rem;
}

@media (min-width: 640px) {
  .gestes-rapides {
    grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  }

  .geste-rapide {
    padding: var(--espace-2) var(--espace-3);
  }

  .geste-rapide__texte {
    display: block;
  }
}

@media (min-width: 1024px) {
  .geste-rapide {
    justify-content: flex-end;
    min-height: 8.5rem;
  }

  .accueil-site .tuiles-activite:not(.tuiles-activite--serrees) .tuile-activite {
    min-height: 7rem;
    padding: var(--espace-2) var(--espace-3);
  }

  .accueil-site .tuiles-activite:not(.tuiles-activite--serrees) .tuile-activite__nombre {
    font-size: 2.25rem;
  }
}

/* R-DET-101 : la fiche d'un rôle occupe le gabarit (hors de la limite de 420 px des formulaires de saisie) ; un bloc
   par module, sur deux colonnes équilibrées au-delà de 1 024 px ; une ligne par permission, séparée par un filet. */
form.fiche-role {
  display: block;
  max-width: none;
}

.fiche-role__tete {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--espace-2) var(--espace-3);
  margin-bottom: var(--espace-3);
}

.fiche-role__tete h1 {
  margin: 0;
}

.fiche-role__infos {
  margin: 0.25rem 0 0;
  color: var(--couleur-texte-discret);
}

.fiche-role__infos strong {
  color: var(--texte);
  font-weight: 600;
}

.fiche-role__modules {
  column-gap: var(--espace-3);
}

@media (min-width: 1024px) {
  .fiche-role__modules {
    columns: 2;
  }
}

.module-droits {
  break-inside: avoid;
  margin: 0 0 var(--espace-3);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
  background: var(--couleur-surface);
}

.module-droits__tete {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--espace-2);
  padding: var(--espace-2) var(--espace-2) 0;
}

.module-droits__tete h2 {
  margin: 0;
  font-size: var(--taille-h3);
}

.module-droits__compte {
  color: var(--couleur-texte-discret);
  font-size: 0.875rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.module-droits__aide {
  margin: 0.25rem var(--espace-2) 0;
  color: var(--couleur-texte-discret);
  font-size: 0.875rem;
}

.module-droits__liste {
  margin: var(--espace-1) 0 0;
  padding: 0;
  list-style: none;
}

.droit {
  display: grid;
  grid-template-columns: 1.5rem minmax(0, 1fr);
  gap: 0 0.75rem;
  align-items: start;
  padding: 0.75rem var(--espace-2);
  border-top: 1px solid color-mix(in srgb, var(--couleur-bordure) 60%, transparent);
}

.droit:hover {
  background: color-mix(in srgb, var(--bleu-principal) 3%, var(--couleur-surface));
}

.droit input[type="checkbox"] {
  width: 1.5rem;
  height: 1.5rem;
  margin: 0;
}

.droit__libelle {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 0.5rem;
  min-height: 1.5rem;
  cursor: pointer;
}

.droit__court {
  color: var(--texte);
  font-weight: 600;
}

.droit:has(input:checked) .droit__court {
  color: var(--bleu-principal);
}

.droit__inclut {
  color: var(--couleur-texte-discret);
  font-size: 0.8125rem;
  font-weight: 400;
}

.droit__explication {
  flex-basis: 100%;
  margin-top: 0.125rem;
  color: var(--couleur-texte-discret);
  font-size: 0.875rem;
  font-weight: 400;
  line-height: 1.4;
}

.fiche-role__pied {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-2);
}

/* R-DET-101 : liste des rôles vue par l'administrateur du groupe — une case par ligne, un seul bouton sous la liste. */
.case-ligne {
  display: inline-flex;
  align-items: center;
  gap: var(--espace-1);
  min-height: var(--cible-tactile-min);
}

.case-ligne label {
  font-weight: 400;
  cursor: pointer;
}

.barre-liste-pied {
  margin-top: var(--espace-2);
}

/* R-DET-99 : un titre de section et son lien de suite sur la même ligne (« Journal des mouvements »). */
.titre-section {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0 var(--espace-2);
  margin: var(--espace-4) 0 var(--espace-1);
}

.titre-section > h2 {
  margin: 0;
}

/* R-DET-99 : les grilles de l'accueil remplissent leurs rangées (auto-fit), sans place vide à droite. */
.cartes-sites {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
  gap: var(--espace-2);
}

.carte-site {
  padding: var(--espace-2);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
  background: var(--couleur-surface);
}

.carte-site__titre {
  margin: 0 0 var(--espace-1);
}

.carte-site__actif {
  margin: 0;
}

.liste-simple {
  margin: 0;
  padding: 0;
  list-style: none;
}

.liste-simple li {
  padding: var(--espace-1) 0;
  border-bottom: 1px solid var(--couleur-bordure);
}

.liste-simple li:last-child {
  border-bottom: 0;
}

/* Champ et bouton sur une ligne (code d'objet + Ajouter, R-PRE-51), comme les formulaires
   courts de l'inventaire : la ligne est en rangée, sinon la base flexible du champ devient sa
   hauteur (.champ est en colonne). */
.champ--ligne {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--espace-1) var(--espace-2);
}

.champ--ligne label {
  flex-basis: 100%;
}

.champ--ligne input {
  flex: 1 1 12rem;
  min-width: 0;
}

/* Groupe de champs d'un formulaire long (Emprunteur, Dates, Objets prêtés…) : le rendu d'un
   bloc du système de design, légende en titre de bloc — jamais la bordure grise du navigateur. */
.groupe-champs {
  display: flex;
  flex-direction: column;
  gap: var(--espace-2);
  margin: 0;
  padding: var(--espace-2);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
  background: var(--couleur-surface);
  min-width: 0;
}

.groupe-champs > legend {
  padding: 0 var(--espace-1);
  font-size: var(--taille-h3);
  font-weight: var(--graisse-libelle);
  color: var(--bleu-principal);
}

/* R-PRE-51 : une ligne du nouveau prêt — l'objet, son état et ses photos au départ. */
.ligne-pret {
  margin: var(--espace-2) 0;
  padding: var(--espace-2);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
  background: var(--couleur-surface);
}

.ligne-pret__titre {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-1);
  align-items: baseline;
  margin: 0 0 var(--espace-1);
}

/* Décision D9 : comparaison départ / retour, une carte par objet — deux colonnes sur grand
   écran, empilées en dessous de 1 200 px. */
.comparaison-pret {
  margin: var(--espace-3) 0;
  padding: var(--espace-2);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
  background: var(--couleur-surface);
}

.comparaison-pret__titre {
  margin-top: 0;
  font-size: 1.125rem;
}

.comparaison-pret__colonnes {
  display: grid;
  gap: var(--espace-3);
}

.comparaison-pret__depart {
  padding: var(--espace-2);
  border-radius: var(--rayon);
  background: var(--couleur-fond);
}

.comparaison-pret h3 {
  margin: 0 0 var(--espace-1);
  font-size: 1rem;
}

@media (min-width: 1200px) {
  .comparaison-pret__colonnes {
    grid-template-columns: 1fr 1fr;
  }
}

/* Utilitaires de mise en page — page de démonstration /styles. Classes plutôt que style="",
   la CSP n'autorise aucun style en ligne (un attribut style="" ne peut pas porter de nonce,
   à la différence d'un élément <style>). */
.rangee {
  display: flex;
  gap: var(--espace-3);
  flex-wrap: wrap;
}

.rangee--serree {
  gap: var(--espace-2);
}

.rangee--centree {
  align-items: center;
}

.pile {
  display: flex;
  flex-direction: column;
  gap: var(--espace-2);
}

.texte-grand {
  font-size: 1.5rem;
}

.forme-en-ligne {
  display: inline;
}

.marge-haute {
  margin-top: var(--espace-2);
}

/* Page /styles : catalogue des boutons, états survol et focus figés pour la revue (mêmes
   valeurs que les règles :hover et :focus-visible des boutons). */
.catalogue-boutons {
  display: block;
  overflow-x: auto;
}

.catalogue-boutons td,
.catalogue-boutons th {
  vertical-align: middle;
}

.demo-survol > .bouton-secondaire,
.demo-survol > .bouton-tertiaire {
  background: color-mix(in srgb, var(--bleu-principal) 8%, var(--couleur-surface));
}

.demo-survol > .bouton-destructif {
  background: var(--etat-erreur-fond);
}

.demo-survol > .bouton-action {
  background: var(--orange-action-survol);
  border-color: var(--orange-action-survol);
}

.demo-focus > button {
  outline: 3px solid var(--bleu-secondaire);
  outline-offset: 2px;
}

/* Page /styles : titre de fiche de démonstration, à la taille du h1 sans en être un. */
.texte-titre-demo {
  margin: 0.25rem 0 var(--espace-1);
  color: var(--bleu-principal);
  font-size: var(--taille-h1);
  font-weight: var(--graisse-h1);
  line-height: 1.25;
}

/* Page /styles : liste étroite, pour voir la carte à côté du tableau. */
.demo-liste-etroite {
  max-width: 420px;
}

/* Page /styles : en-tête replié montré menu ouvert ; le panneau, positionné sous l'en-tête,
   reste dans le cadre grâce à la marge basse. */
.demo-entete {
  max-width: 420px;
  padding-bottom: 22rem;
  border: 1px dashed var(--gris-secondaire);
}

/* R-SAV-15 : cadre de signature visible — fond contrasté, ligne de base, indice « Signez ici ».
   Jamais un attribut style="" : la CSP le bloque silencieusement, sans nonce possible dessus. */
/* Le pavé suit la largeur disponible, 400 px au plus : signature.js convertit les coordonnées selon la
   taille affichée, le tracé reste juste à 320 px. */
.pave-signature {
  position: relative;
  display: block;
  max-width: 402px;
  border: 1px solid var(--couleur-bordure);
  background: var(--couleur-fond);
}

.pave-signature__canvas {
  display: block;
  width: 100%;
  height: auto;
  touch-action: none;
}

.pave-signature__indice {
  position: absolute;
  left: var(--espace-2);
  right: var(--espace-2);
  bottom: var(--espace-2);
  border-bottom: 1px dashed var(--gris-secondaire);
  padding-bottom: 2px;
  color: var(--couleur-texte-discret);
  font-size: 0.85rem;
  pointer-events: none;
}

/* R-TAB-01 : un en-tête triable se clique, en mode client comme en repli serveur — jamais
   souligné ni coloré comme un lien : un indicateur de tri (flèche) suffit à le distinguer. */
th[data-tableau-colonne] {
  cursor: pointer;
}

th[data-tableau-colonne] a {
  color: inherit;
  text-decoration: none;
}

/*
 * Un élément masqué par JavaScript (ou par l'attribut hidden posé côté serveur) doit
 * réellement disparaître, quelle que soit la balise. Sans cette règle, la première déclaration
 * d'origine auteur qui pose un display (button { display: inline-flex }, .alerte { display:
 * flex }, …) l'emporte sur le [hidden] de l'agent utilisateur : à priorité égale, une règle
 * d'origine auteur prime toujours sur une règle d'origine agent utilisateur, quelle que soit sa
 * spécificité. Découvert deux fois (boutons de listes dépendantes, R-SAV-13 ; bandeau de
 * nouvelle version du service worker, lot 7a) : une règle générale plutôt qu'un correctif par
 * sélecteur, pour ne pas le découvrir une troisième fois.
 */
[hidden] {
  display: none !important;
}

.bloc-bleu .discret {
  color: rgba(255, 255, 255, 0.8);
}

/* Tablette et plus : assez de place pour le logo complet (avec signature), l'e-mail et
   le texte des actions de l'en-tête à côté de leur icône. */
@media (min-width: 640px) {
  :root {
    --marge-page: 1.5rem;
  }

  .conteneur {
    padding: var(--espace-4) var(--marge-page);
  }

  .entete:not(.entete--replie) .entete__logo img {
    height: 44px;
    max-height: none;
  }
}

/* Barre complète : logo à 40px de haut (jamais déformé, R-VIS-07), pour que les huit entrées tiennent
   à 1 280px. */
@media (min-width: 1280px) {
  .entete:not(.entete--replie) .entete__logo img {
    height: 40px;
    max-height: none;
  }
}

/* Bandeau de session assistée (R-ASS-03) : visible en permanence, pleine largeur, aux couleurs
   d'alerte de la charte — icône et libellé, jamais la couleur seule. En colonne sur mobile. */
.bandeau-assistance {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--espace-1);
  padding: var(--espace-1) var(--espace-2);
  background: var(--etat-alerte-fond);
  color: var(--etat-alerte-texte);
  border-bottom: 1px solid var(--couleur-bordure);
}

.bandeau-assistance__texte {
  display: flex;
  align-items: center;
  gap: var(--espace-1);
  margin: 0;
}

.bandeau-assistance__actions {
  display: flex;
  align-items: center;
  gap: var(--espace-1);
}

/* Rappel de double authentification à activer (R-DET-11) : pleine largeur sous l'en-tête, comme le
   bandeau de session assistée. */
.bandeau-double-authentification,
.bandeau-lecture-seule {
  padding: var(--espace-1) var(--espace-2);
  border-radius: 0;
  border-bottom: 1px solid var(--couleur-bordure);
}

/* R-ABO-53 : en lecture seule, les boutons qui modifient sont masqués dans la page ; l'export d'une sélection
   reste. La protection est le filtre serveur (FiltreLectureSeule) : ceci n'est que la politesse de l'écran. */
.lecture-seule main form[method="post"]:not([action*="ExporterSelection"]) button:not([formaction*="ExporterSelection"]),
.lecture-seule main form[method="post"]:not([action*="ExporterSelection"]) input[type="submit"],
.lecture-seule main button[form]:not([formaction*="ExporterSelection"]) {
  display: none;
}

/* Écran de scan, lot 7b : caméra et filigrane de cadrage de l'étiquette logistique (paysage,
   ≈ 1,7 : 1, code-barres en haut à gauche, textes à gauche, masque autour). Un guide seulement :
   le code se lit partout dans l'image, la capture exige juste que ses barres soient dans le cadre. */
.scan-camera {
  position: relative;
  overflow: hidden;
  /* Recette iPhone 7b : un cadre au ratio de l'écran tenu en main — 3:4 en portrait, 4:3 en
     paysage —, que la vidéo remplit entièrement (cover) : plus de colonne étroite dans une
     grande bande noire. La largeur est bornée pour que le cadre tienne dans 70 % de la hauteur. */
  width: min(100%, calc(70vh * 3 / 4));
  aspect-ratio: 3 / 4;
  margin-inline: auto;
  border-radius: var(--rayon);
  background: #000;
}

@media (orientation: landscape) {
  .scan-camera {
    width: min(100%, calc(70vh * 4 / 3));
    aspect-ratio: 4 / 3;
  }
}

.scan-camera video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Le conteneur est exactement l'image visible (la vidéo le couvre) : le filigrane s'y cale. */
.scan-filigrane {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.scan-filigrane[hidden] {
  display: none;
}

/* Dimensions posées par scan.js : 92 % de la largeur de la vue, hauteur au ratio de
   l'étiquette, bornée à 92 % de la hauteur (paysage). */
.scan-filigrane__cadre {
  position: relative;
  width: 92%;
  aspect-ratio: 1.7 / 1;
  max-height: 92%;
  border: 3px solid #fff;
  border-radius: var(--rayon);
  /* Masque semi-transparent hors du cadre, sans élément supplémentaire. */
  box-shadow: 0 0 0 100vmax rgb(0 0 0 / 45%);
}

.scan-filigrane__code {
  position: absolute;
  top: 6%;
  left: 5%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  width: 42%;
  height: 20%;
  border: 2px dashed #fff;
  background: rgb(255 255 255 / 12%);
  color: #fff;
  font-size: 0.75rem;
}

.scan-filigrane__textes {
  position: absolute;
  top: 32%;
  left: 5%;
  width: 58%;
  height: 58%;
  border: 2px dashed rgb(255 255 255 / 70%);
}

.scan-filigrane__consigne {
  position: absolute;
  right: 0;
  bottom: 4%;
  left: 0;
  margin: 0;
  padding: 0 var(--espace-2);
  color: #fff;
  font-weight: 600;
  text-align: center;
  text-shadow: 0 1px 3px #000;
}

/* R-OCR-13 : une valeur proposée par la lecture reste signalée tant que l'utilisateur ne l'a pas
   changée. */
.champ__lu {
  margin-left: var(--espace-1);
  padding: 0 var(--espace-1);
  border-radius: var(--rayon);
  background: var(--etat-information-fond);
  color: var(--etat-information-texte);
  font-size: 0.75rem;
  font-weight: 600;
}

.champ__lu[hidden] {
  display: none;
}

/* Boucle de lecture (lot 7b) : check-list sous la caméra, cochée au fil des lectures. */
.scan-checklist {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--espace-1);
  margin-top: var(--espace-1);
}

.scan-checklist[hidden] {
  display: none;
}

.scan-checklist__liste {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-1) var(--espace-2);
  margin: 0;
  padding: 0;
  list-style: none;
  font-weight: 600;
}

.scan-checklist__liste li::before {
  content: "☐ ";
}

.scan-checklist__liste li[data-coche]::before {
  content: "☑ ";
  color: var(--etat-succes-texte);
}

/* Polish : transitions courtes (150ms au plus) sur les éléments qui changent d'état au survol ou
   à l'ouverture — résumés de <details>, liens-onglets, cartes. */
.bloc-repliable > summary,
.actions-ligne__bouton,
.confirmation > summary,
.resume-emplacement > summary,
.detail-file > summary,
.entete__menu-bouton,
.entete__module,
.entete__action,
.onglets__lien,
.pagination__lien,
.pastille-site,
.carte-menu,
.boutons-choix__bouton,
.tableau-cartes tr {
  transition:
    background-color 0.15s ease,
    border-color 0.15s ease,
    box-shadow 0.15s ease,
    color 0.15s ease;
}

/* Mouvement réduit demandé par le système : aucune transition ni animation qui dure. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* --- Inventaire par scan (lot 12) ------------------------------------------------------------ */

/* Nouvel inventaire : la cible se range sous son périmètre. */
.champ--imbrique {
  margin-left: calc(1.25rem + var(--espace-1));
}

/* Session ouverte en tête de la liste, et champ de code sur PC : des blocs clairs sur le fond. */
.inventaire-ouvert,
.inventaire-pc {
  margin: var(--espace-2) 0;
  padding: var(--espace-2);
  background: var(--couleur-surface);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
}

.inventaire-ouvert h2,
.inventaire-pc h2 {
  margin-top: 0;
}

/* Formulaires courts de la session (code au clavier, compte d'une fourniture) et de la fiche d'un
   transfert (ajout, réception) : alignés à gauche, champ et bouton sur une ligne, comme la recherche du
   composant tableau. */
.inventaire-pc__formulaire,
.inventaire-fournitures__compte,
.transfert-formulaire {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: flex-start;
  gap: var(--espace-1) var(--espace-2);
  max-width: none;
}

.inventaire-pc__formulaire .champ,
.inventaire-fournitures__compte .champ,
.transfert-formulaire .champ {
  flex: 0 1 16rem;
  min-width: 0;
  margin: 0;
}

/* Question posée après un scan (attendu ailleurs, élément sorti, boîte à compter). */
.inventaire-question {
  padding: var(--espace-2);
  background: var(--etat-alerte-fond);
  color: var(--etat-alerte-texte);
  border-radius: var(--rayon);
}

.inventaire-question__code {
  margin: 0 0 var(--espace-1);
  font-weight: var(--graisse-h3);
}

.inventaire-fournitures,
.inventaire-manquants {
  list-style: none;
  margin: 0;
  padding: 0;
}

.inventaire-fournitures > li,
.inventaire-manquants > li {
  padding: var(--espace-1) 0;
  border-bottom: 1px solid var(--couleur-bordure);
}

.inventaire-fournitures p,
.inventaire-manquants p {
  margin: 0 0 var(--espace-1);
}

/* Une ligne par fourniture : désignation · champ Compté · Valider. */
.inventaire-fournitures > li {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--espace-1) var(--espace-2);
}

.inventaire-fournitures > li > p {
  flex: 0 1 auto;
  align-self: center;
}

.inventaire-zone {
  margin: 0;
}

/* Bandeau permanent du mode Inventorier sur l'écran de scan : périmètre, compteur, zone en cours. */
.bandeau-inventaire {
  display: flex;
  flex-direction: column;
  gap: var(--espace-1);
  margin: 0 0 var(--espace-2);
  padding: var(--espace-1) var(--espace-2);
  background: var(--etat-information-fond);
  color: var(--etat-information-texte);
  border-radius: var(--rayon);
}

.bandeau-inventaire__titre {
  margin: 0;
  font-weight: var(--graisse-h3);
}

.bandeau-inventaire .champ {
  margin: 0;
}

.bandeau-inventaire select {
  min-height: var(--cible-tactile-min);
}

.bandeau-inventaire a {
  align-self: flex-start;
  min-height: var(--cible-tactile-min);
  display: inline-flex;
  align-items: center;
}

/* Fiche résumée du scan : « Non vu à l'inventaire du … » et « Dernier inventaire : … ». */
.indicateurs-inventaire {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-1) var(--espace-2);
  margin: var(--espace-1) 0;
}

/* --- Opérations commerciales (lot 14) -------------------------------------------------------- */

/* Avancement : barre native, sans style en ligne (CSP). R-DET-90 (vague design 4) : une piste cerclée et teintée,
   lisible même à 0 %, la part faite en bleu principal. */
progress.avancement,
meter.avancement {
  width: 8rem;
  height: 0.75rem;
  vertical-align: middle;
  accent-color: var(--bleu-principal);
}

progress.avancement {
  appearance: none;
  overflow: hidden;
  border: 1px solid var(--couleur-texte-discret);
  border-radius: 999px;
  background: color-mix(in srgb, var(--bleu-principal) 14%, var(--couleur-surface));
}

progress.avancement::-webkit-progress-bar {
  background: color-mix(in srgb, var(--bleu-principal) 14%, var(--couleur-surface));
}

progress.avancement::-webkit-progress-value {
  background: var(--bleu-principal);
}

progress.avancement::-moz-progress-bar {
  background: var(--bleu-principal);
}

.compte-a-rebours {
  font-weight: 600;
  color: var(--bleu-principal);
}

.formulaire-en-ligne {
  display: inline;
}

.vignette-offre {
  display: block;
  width: 64px;
  height: 48px;
  margin-bottom: 0.25rem;
  object-fit: cover;
  border-radius: var(--rayon);
}

.visuel-operation {
  display: block;
  max-width: 100%;
  max-height: 200px;
  margin-bottom: var(--espace-1);
  border-radius: var(--rayon);
}

.face-client__qr img {
  display: block;
  background: var(--couleur-surface);
  padding: var(--espace-1);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
}

.champ--court {
  max-width: 7rem;
}

/* Classement : rang, prénom et initiale, ventes validées ; soi-même en évidence. */
.classement {
  list-style: none;
  margin: 0;
  padding: 0;
}

.classement li {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  padding: var(--espace-1);
  border-bottom: 1px solid var(--couleur-bordure);
}

.classement li strong {
  margin-left: auto;
}

.classement__rang {
  min-width: 2rem;
  font-weight: 700;
  color: var(--bleu-principal);
}

.classement__moi {
  background: var(--etat-information-fond);
  border-radius: var(--rayon);
}

/* Face client : mode présentation et page publique, plein écran, sans barre de navigation. */
.page-face-client {
  background: var(--couleur-fond);
}

.conteneur-face-client {
  max-width: 1280px;
  margin: 0 auto;
  padding: var(--espace-2);
}

.face-client__quitter {
  margin: 0;
  text-align: right;
  font-size: 0.875rem;
}

.face-client__entete {
  text-align: center;
  margin-bottom: var(--espace-3);
}

.face-client__visuel {
  display: block;
  width: 100%;
  max-height: 260px;
  object-fit: cover;
  border-radius: var(--rayon);
  margin-bottom: var(--espace-2);
}

.face-client__magasin {
  margin: 0;
  color: var(--couleur-texte-discret);
  font-weight: 600;
}

.face-client__titre {
  margin: 0.25rem 0;
  color: var(--bleu-principal);
}

.face-client__dates {
  margin: 0;
}

.face-client__rebours {
  display: inline-block;
  margin: var(--espace-1) 0;
  padding: 0.25rem var(--espace-2);
  border-radius: var(--rayon);
  background: var(--bleu-principal);
  color: #ffffff;
  font-weight: 700;
}

.face-client__avantages {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--espace-1);
  list-style: none;
  margin: 0 0 var(--espace-3);
  padding: 0;
}

.face-client__offres {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--espace-2);
  list-style: none;
  margin: 0;
  padding: 0;
}

.offre-client {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: var(--espace-2);
  background: var(--couleur-surface);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
}

.offre-client--partie {
  opacity: 0.6;
}

.offre-client__ruban {
  position: absolute;
  top: var(--espace-1);
  right: var(--espace-1);
  padding: 0.125rem var(--espace-1);
  border-radius: var(--rayon);
  background: var(--texte);
  color: #ffffff;
  font-weight: 700;
  font-size: 0.8125rem;
  text-transform: uppercase;
}

.offre-client__photo {
  width: 100%;
  height: 160px;
  object-fit: cover;
  border-radius: var(--rayon);
}

.offre-client__nom {
  margin: 0;
  font-size: 1.125rem;
}

.offre-client__marque,
.offre-client__compteur {
  margin: 0;
  color: var(--couleur-texte-discret);
}

.offre-client__prix {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 var(--espace-1);
  margin: 0;
}

/* Recette du lot 14 : un montant ne se coupe jamais entre le nombre et « € », ni le compteur. */
.offre-client__reference,
.offre-client__operation,
.offre-client__compteur {
  white-space: nowrap;
}

.offre-client__reference {
  color: var(--couleur-texte-discret);
}

.offre-client__operation {
  font-size: 1.5rem;
  color: var(--bleu-principal);
}

.offre-client .bouton-action {
  margin-top: auto;
}

.face-client__mention {
  margin-top: var(--espace-3);
  text-align: center;
  font-size: 0.8125rem;
  color: var(--couleur-texte-discret);
}

/* Écran de vitrine (?ecran=1) : police plus grande, compte à rebours en évidence. */
.face-client--ecran {
  font-size: 1.25rem;
}

.face-client--ecran .face-client__rebours {
  font-size: 2rem;
}

.face-client--ecran .offre-client__operation {
  font-size: 2rem;
}

/* Au-delà de 6 offres, la grille défile lentement de haut en bas puis remonte (CSS seul). Mouvement
   réduit demandé : pas de défilement, la grille reste lisible par la barre de défilement. */
.face-client__cadre--defilement {
  height: 70vh;
  overflow: hidden;
}

.face-client__cadre--defilement .face-client__offres {
  animation: defilement-offres 40s linear infinite alternate;
}

@keyframes defilement-offres {
  from {
    transform: translateY(0);
  }

  to {
    transform: translateY(calc(-100% + 70vh));
  }
}

@media (prefers-reduced-motion: reduce) {
  .face-client__cadre--defilement {
    height: auto;
    overflow: visible;
  }

  .face-client__cadre--defilement .face-client__offres {
    animation: none;
  }
}

/* Suivi et comptabilité de stock (lot 15, docs/15) ------------------------------------------ */

/* Filtres d'une liste de suivi (période, origine, motif, nature…) : en ligne, sans la largeur
   maximale des formulaires de saisie ; une colonne sur téléphone. */
.filtres-ligne {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--espace-1) var(--espace-2);
  max-width: none;
}

/* Un champ rétrécit sous la largeur de sa liste déroulante : la comparaison A/B des clôtures
   débordait de 357 px à 320 px (critique du 06/10/2026). */
.filtres-ligne .champ {
  flex: 1 1 100%;
  min-width: 0;
}

@media (min-width: 640px) {
  .filtres-ligne .champ {
    flex: 0 1 13rem;
  }
}

/* Nombre de lignes et total d'une liste, sous les filtres. */
.totaux-liste {
  margin: var(--espace-1) 0;
  color: var(--couleur-texte-discret);
}

.totaux-liste strong {
  color: var(--bleu-principal);
}

/* Synthèse d'un état du stock (R-CPT-32) : catégories, total en gras, montants alignés à droite. */
.synthese-etat {
  max-width: 56rem;
  margin-bottom: var(--espace-2);
  background: var(--couleur-surface);
}

.synthese-etat .nombre {
  text-align: right;
  white-space: nowrap;
}

.synthese-etat .synthese-etat__total th,
.synthese-etat .synthese-etat__total td {
  font-weight: var(--graisse-h2);
  border-top: 2px solid var(--bleu-principal);
}

/* Mentions sous la synthèse : classement, valorisation, vendus non sortis, annexes. */
.mentions-etat {
  margin: 0 0 var(--espace-2);
  padding-left: var(--espace-3);
  color: var(--couleur-texte-discret);
}

/* Montant d'une ligne de détail, aligné à droite (tableaux de l'état et des clôtures). Le montant
   reste sur une ligne, son intitulé peut y passer : « Transferts envoyés » faisait déborder
   l'activité des 7 derniers jours de l'accueil du groupe à 768 px (critique du 06/10/2026). */
.liste .nombre {
  text-align: right;
}

.liste td.nombre {
  white-space: nowrap;
}

/* Retard d'une livraison (R-CPT-21) : rouge d'erreur, toujours avec le libellé « J+n ». */
.retard {
  color: var(--etat-erreur-texte);
  font-weight: var(--graisse-libelle);
}

/* Synthèse de l'état et comparaison de clôtures (recette du lot 15) : sous 1 200px de liste, une
   carte par catégorie, chaque chiffre avec son intitulé — jamais de débordement de 320 à 1 280px ;
   tableau au-delà. Sans requête de conteneur, les cartes restent (même repli que les listes). */
.synthese-cartes {
  display: block;
  max-width: none;
  background: transparent;
}

.synthese-cartes thead {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.synthese-cartes tbody {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr));
  gap: var(--espace-2);
}

.synthese-cartes tr {
  display: block;
  padding: var(--espace-2);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
  background: var(--couleur-surface);
}

.synthese-cartes th,
.synthese-cartes td {
  display: flex;
  justify-content: space-between;
  gap: var(--espace-2);
  padding: 0.25rem 0;
  border: 0;
}

.synthese-cartes th[scope="row"] {
  padding-bottom: var(--espace-1);
  font-size: var(--taille-h3);
}

.synthese-cartes td::before {
  content: attr(data-etiquette);
  color: var(--couleur-texte-discret);
  font-weight: 400;
  text-align: left;
  white-space: normal;
}

.synthese-cartes .synthese-etat__total {
  border: 2px solid var(--bleu-principal);
}

.synthese-cartes .synthese-etat__total th,
.synthese-cartes .synthese-etat__total td {
  border-top: 0;
}

@container liste (min-width: 1200px) {
  .synthese-cartes {
    display: table;
    max-width: 56rem;
    background: var(--couleur-surface);
  }

  .synthese-cartes thead {
    position: static;
    display: table-header-group;
    width: auto;
    height: auto;
    overflow: visible;
    clip: auto;
  }

  .synthese-cartes tbody {
    display: table-row-group;
  }

  .synthese-cartes tr {
    display: table-row;
    padding: 0;
    border: 0;
    background: transparent;
  }

  .synthese-cartes th,
  .synthese-cartes td {
    display: table-cell;
    padding: var(--espace-1) var(--espace-2);
    border-bottom: 1px solid var(--couleur-bordure);
  }

  .synthese-cartes th[scope="row"] {
    font-size: inherit;
  }

  .synthese-cartes td::before {
    content: none;
  }

  .synthese-cartes .synthese-etat__total {
    border: 0;
  }

  .synthese-cartes .synthese-etat__total th,
  .synthese-cartes .synthese-etat__total td {
    border-top: 2px solid var(--bleu-principal);
  }
}

/* R-RGP-52 : mention d'information, en petit corps, au-dessus du cadre de signature et en aperçu. */
.mention-information {
  font-size: 0.8125rem;
  color: var(--couleur-texte-discret);
  max-width: 70ch;
}

/* R-RGP-21 : à la place d'une signature effacée, sur les fiches de sortie et de prêt. */
.signature-effacee {
  font-style: italic;
  color: var(--couleur-texte-discret);
}

/* R-RGP-50 : liens légaux du pied de page, sur toutes les pages. */
.pied__liens {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--espace-1) var(--espace-3);
  font-size: 0.8125rem;
}

.pied__liens a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

.pied--face-client {
  margin-top: var(--espace-4);
}

/* R-RGP-50 : page légale publique, texte long lisible. */
.page-legale {
  max-width: 70ch;
}

.page-legale__traitement {
  margin-top: var(--espace-3);
}


/* R-SOL-03 : bloc « Solde à payer » de la sortie. Décision 10 du plan 16c : l'avertissement des espèces
   n'apparaît que quand le mode Espèces est choisi, par la feuille de style seule, sans script. */
.bloc-solde {
  margin: var(--espace-2) 0;
  padding: var(--espace-2);
  border: 1px solid var(--etat-alerte-texte);
  border-radius: var(--rayon);
}

.bloc-solde legend {
  font-weight: 600;
  padding: 0 var(--espace-1);
}

.bloc-solde__especes {
  display: none;
}

.bloc-solde:has(option[value="Especes"]:checked) .bloc-solde__especes {
  display: block;
}

/* R-ABO-62 : la section « Alertes » de Supervision → Abonnements, une alerte par ligne, pastille en tête. */
.liste-alertes-supervision {
  display: grid;
  gap: var(--espace-1);
  margin: 0;
  padding: 0;
  list-style: none;
}

.liste-alertes-supervision li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--espace-1);
}

/* R-ABO-20 : le champ leurre de l'inscription, hors écran et hors tabulation ; seul un robot le remplit. */
.champ-leurre {
  position: absolute;
  left: -10000px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Lot 27 : fenêtre (R-NDV-04). Sans script, le <dialog open> rendu par le serveur se lit comme un bloc en tête de
   page, avec son bouton ; fenetre.js en fait une modale (showModal) : fond voilé, focus tenu, Échap la masque. */
.fenetre {
  position: static;
  width: auto;
  max-width: none;
  margin: var(--espace-2) 0 var(--espace-3);
  padding: var(--espace-3);
  border: 1px solid var(--couleur-bordure);
  border-radius: var(--rayon);
  background: var(--couleur-surface);
  color: var(--texte);
}

.fenetre:modal {
  position: fixed;
  inset: 0;
  margin: auto;
  width: min(36rem, calc(100% - 2 * var(--marge-page)));
  max-height: calc(100% - 2 * var(--espace-4));
  overflow-y: auto;
  box-shadow: 0 8px 32px rgb(26 43 51 / 0.2);
}

.fenetre::backdrop {
  background: rgb(26 43 51 / 0.45);
}

.fenetre > h2 {
  margin-top: 0;
}

.fenetre__sous-titre {
  margin: var(--espace-2) 0 var(--espace-1);
  font-weight: 600;
  color: var(--bleu-principal);
}

.fenetre__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-2);
  align-items: center;
  margin-top: var(--espace-3);
}
