  /* ─── FENÊTRE STOCK (plan entreprise) — même moule que budget ─── */
  /* Fenêtre, barre, onglets, corps et message viennent du SOCLE. */
  .st-titre { font-weight: var(--p-appuye); }
  .st-tabs:empty { display: none; }
  /* Un refus doit SAUTER AUX YEUX : en texte simple il passait
     inaperçu (22/08, Adam a cru avoir créé une commande jamais partie). */
  .st-msg.err {
    color: var(--danger); font-weight: 600;
    border: 1px solid var(--danger); border-radius: var(--r-objet);
    padding: 8px 10px; background: color-mix(in srgb, var(--danger) 12%, transparent);
  }

  /* CE QUI A DISPARU SE DIT — mais discrètement : ce n'est ni une
     erreur, ni un avertissement. C'est un état de fait, énoncé une
     fois en bas de l'écran. Un cadre rouge ferait croire à une panne
     exactement là où l'on veut dire l'inverse. */
  .st-acces {
    /* `margin` sur les DEUX axes verticaux depuis que la note se pose
       en tete de `#st-body` : sans marge basse elle collerait au
       premier bloc de la page. L'autre usage (page entierement
       interdite) l'affiche seule, donc la marge basse n'y gene pas. */
    color: var(--muted); padding: 10px 12px; margin: 10px 0;
    border-left: 2px solid var(--accent); border-radius: 0 8px 8px 0;
    background: color-mix(in srgb, var(--accent) 7%, transparent);
    font-size: .95em;
  }

  /* Gabarit commun des écrans stock (maquette proto/stock-ui figée le
     20/08). Tout suit le thème actif — aucune couleur en dur. */
  /* Les tuiles viennent du SOCLE (deux colonnes sous 640px comprises,
     règle qui venait d'ici et qui sert désormais aux quatre écrans). */
  .st-kpi .d { font-size: var(--t-etiquette); color: var(--muted); }
  .st-bloc { margin-top: 10px; background: var(--panel); border: 1px solid var(--line);
             border-radius: var(--r-objet); padding: 8px 10px; }
  .st-bloc .titre { font-size: var(--t-etiquette); color: var(--muted); text-transform: uppercase;
                    letter-spacing: .05em; margin-bottom: 4px; }
  /* ⛔⛔ CE FLEX DÉBORDAIT, ET ADAM L'A VU SUR SA CAPTURE DU 16/09 À
     22h14 (écran Documents) : les liens « imprimer » et « CSV » posés
     PAR-DESSUS la deuxième ligne du texte, le « ⤓ CSV » coupé au bord
     droit, et toute la page prise d'un défilement HORIZONTAL.
     La cause, mesurée : un flex d'UNE ligne (pas de `flex-wrap`) dont
     l'enfant texte garde le `min-width: auto` par défaut — *il refuse
     donc de descendre sous la largeur de son mot le plus long*, pousse
     `.x` hors du cadre, et `align-items: center` place les actions au
     MILIEU vertical d'un texte de trois lignes, c'est-à-dire dessus.
     ⚠⚠ ET LE REMÈDE ÉTAIT DÉJÀ DANS CE FICHIER, quatre jours plus tôt,
     sur une autre capture du même téléphone : `.bg-form` (12/09) a reçu
     `flex-wrap` + un plancher pour exactement ce défaut, et le
     commentaire qui l'accompagne dit déjà *« un correctif qui ne cherche
     pas ses frères en laisse un »*. **Celui-ci était le frère.**
     ⚠ LES DEUX PROPRIÉTÉS SONT INERTES TANT QUE LA LIGNE TIENT : le wrap
     n'agit qu'au débordement, et `min-width: 0` ne change rien à un texte
     qui n'a pas besoin de rétrécir. *Leurs deux modes d'échec tombent du
     bon côté* — c'est ce qui permet de les poser sur un gabarit partagé
     sans avoir la capture de chaque écran qui l'emploie.
     ⚠ Ce qui périmerait ce filet : qu'une ligne d'alerte se mette à
     porter un contenu si long que l'enveloppement devienne son rendu
     NORMAL — ce serait le signe qu'elle veut `.st-edition`, pas un
     réglage de plus ici. */
  .st-alerte { display: flex; align-items: center; flex-wrap: wrap;
               gap: 8px; padding: 5px 2px;
               cursor: pointer; border-radius: var(--r-fin); }
  .st-alerte > * { min-width: 0; }
  .st-alerte:hover { background: var(--panel2); }
  .st-alerte .pastille { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--accent); }
  .st-alerte .pastille.rouge { background: var(--danger); }
  .st-alerte .x { margin-left: auto; color: var(--muted);
    min-height: var(--cible-mini); min-width: var(--cible-mini);
    display: inline-flex; align-items: center; justify-content: center; }
  /* ─── UNE FICHE D'ÉDITION (16/09, après le refus d'Adam) ───────────
     ⛔ RÉPARER LE FLEX N'AURAIT PAS SUFFI, ET C'EST LE FOND DU DÉFAUT :
     `.st-alerte` est le gabarit d'une LIGNE — une phrase, une action au
     bout. Une édition imprimable est une FICHE : un titre, ce qu'elle
     porte, et ses actions. *Le défaut n'était pas un réglage manqué,
     c'était un gabarit employé hors de son domaine de validité* — et un
     gabarit détourné ne produit aucune erreur, il produit un écran qu'on
     ne peut pas lire.
     ⛔ LES ACTIONS SONT SOUS LE TEXTE, À TOUTE LARGEUR, sans point de
     bascule. *Un gabarit qui change de forme à une largeur donnée se
     casse à cette largeur-là, et personne ne regarde jamais le produit
     exactement là.*
     ⚠ LA PORTÉE EST EN CORPS DE TEXTE, PAS EN ÉTIQUETTE : elle n'est pas
     une mention secondaire comme la référence sous un nom de client —
     c'est elle qui décide QUEL document on imprime, et deux éditions de
     même allure ne se distinguent que par elle une fois sur le papier.
     Sur 11 px elle ne se lit pas, et on imprime alors au hasard. */
  /* ⚠ DEUX NOMS, UNE SEULE DÉCLARATION — et c'est la règle du dépôt,
     pas une économie de lignes : une édition imprimable et un document
     déposé sont le même OBJET d'écran (un titre, ce qu'il est, ses
     actions), donc ils ne peuvent pas diverger sans qu'on le décide.
     *Deux blocs jumeaux finissent toujours par se séparer, et c'est
     celui qu'on ne regarde pas qui prend du retard.* Le produit le fait
     déjà pour ses huit familles de boutons de navigation.
     ⛔ `.st-doc` est né d'un FRÈRE trouvé dans l'heure : l'onglet
     voisin — le même écran, à un clic de celui qu'Adam venait de
     valider — rendait ses trois actions en `.st-tag`, c'est-à-dire des
     badges de onze pixels, dans un tableau à cinq colonnes qui défilait
     horizontalement sur téléphone. */
  #stock .st-edition, #stock .st-doc { padding: 12px 2px;
    border-top: 1px solid var(--line); }
  #stock .st-edition:first-child,
  #stock .st-doc:first-child { border-top: 0; padding-top: 2px; }
  #stock .st-edition .nom, #stock .st-doc .nom { font-size: var(--t-lu);
    font-weight: var(--p-appuye); }
  #stock .st-edition .portee, #stock .st-doc .portee {
    font-size: var(--t-corps); color: var(--muted); margin-top: 3px; }
  /* ⚠ QUATRE PORTEURS, UNE SEULE DÉCLARATION. L'accueil du 31-B pose
     des actes au bas d'un `.st-bloc` et de l'encart de valeur : écrire
     les mêmes quatre propriétés une seconde fois aurait fabriqué une
     famille jumelle, et *c'est celle qu'on ne regarde pas qui finit par
     avoir tort.* On rallonge le sélecteur, pas la feuille. */
  #stock .st-edition .actes, #stock .st-doc .actes,
  #stock .st-bloc .actes, #stock .st-valeur .actes { display: flex;
    flex-wrap: wrap; gap: var(--e-2); margin-top: 9px; }
  /* LES RATTACHEMENTS restent des badges, et c'est leur vrai métier :
     ils se LISENT, ils ne se visent pas au pouce. *Une cible de 44 px
     n'est due qu'à ce qu'on doit ATTEINDRE* — et le détachement, lui,
     est passé dans les actions, où il a la taille d'un bouton. */
  #stock .st-doc .liens { display: flex; flex-wrap: wrap; gap: 6px;
    margin-top: 5px; }
  .st-tuiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
               gap: 7px; margin-top: 10px; }
  .st-tuile { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-objet);
              padding: 10px 11px; cursor: pointer; text-align: left; font: inherit; color: var(--text); }
  .st-tuile:hover { border-color: var(--accent); }
  .st-tuile .ic { font-size: 18px; }
  .st-tuile .n { font-weight: 600; margin-top: 2px; }
  .st-tuile .d { font-size: var(--t-etiquette); color: var(--muted); margin-top: 1px; }
  /* margin sur les DEUX bords : deux barres empilées (filtres puis
     création) se touchaient, faute de marge haute (22/08 : « les cadres
     se touchent, ce n'est pas esthétique »). */
  /* ─── L'EN-TÊTE D'UN ÉCRAN DU STOCK (13/09) ────────────────────────
     Retour d'Adam sur le catalogue déployé : « ça n'a ni queue ni tête,
     brouillon ». Quatre choses : le FIL (où l'on est), le TITRE (ce
     qu'on regarde), le SOUS-TITRE (ce que la page montre), les ACTIONS
     à droite du titre — jamais flottantes au-dessus des filtres.
     Aucune couleur en dur : les jetons du thème, sinon un chantier
     THÈMES repeindrait le produit en laissant cette en-tête derrière. */
  #stock .st-entete-bloc { margin: 2px 0 14px; padding-bottom: 12px;
    border-bottom: 1px solid var(--line2); }
  #stock .st-entete { display: flex; align-items: flex-start;
    justify-content: space-between; gap: 12px; flex-wrap: wrap; }
  #stock .st-entete-txt { min-width: 0; }
  #stock .st-fil { font-size: var(--t-etiquette); color: var(--muted);
    text-transform: uppercase; letter-spacing: .07em; margin-bottom: 4px; }
  /* 19px = « titre de panneau » de l'échelle typographique. Une taille
     hors échelle ferait rougir test_fenetres_ui, et elle aurait
     raison : un titre choisi au pixel ne suit plus le reste. */
  #stock .st-h1 { margin: 0; font-size: var(--t-chiffre);
    font-weight: var(--p-appuye); line-height: 1.25; }
  #stock .st-sous { margin: 8px 0 0; font-size: var(--t-note);
    color: var(--muted); }
  #stock .st-entete-actions { display: flex; gap: 8px; flex-wrap: wrap;
    align-items: center; }
  /* Sous 520 px de PANNEAU (pas de fenêtre : le chat peut occuper la
     moitié de l'écran), les actions passent sous le titre et prennent
     toute la largeur — deux boutons serrés côte à côte à 44 px de haut
     se ratent au doigt. */
  @container nimzou-stock (max-width: 520px) {
    #stock .st-entete-actions { width: 100%; }
    #stock .st-entete-actions > button { flex: 1; min-width: 140px; }
  }

  /* UNE BARRE DE FILTRES ÉTIQUETÉE — la variante en grille (13/09).
     `.st-filtres` seule reste ce qu'elle était : une rangée de
     contrôles nus, encore utilisée par une vingtaine d'écrans.
     ⚠ La grille annule `flex: 1` et `min-width: 120px` du champ : en
     grille ils ne servent plus à rien et le min-width empêcherait la
     colonne de rétrécir sous 120 px, donc de se replier. */
  #stock .st-filtres.st-grille { display: grid; gap: 12px;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    align-items: end; margin: 0 0 10px; }
  /* ⛔ UNE SEULE HAUTEUR, SINON LES ÉTIQUETTES SE DÉCALENT (13/09,
     chevauchement vu par Adam sur ordinateur). Le bloc Rechercher
     porte un bouton à 44 px quand les selects font 34 : avec
     `align-items: end` les colonnes s'alignent par le BAS, donc
     « CATÉGORIE » tombait dix pixels plus bas que « RECHERCHER » et
     le champ voisin semblait chevaucher son étiquette.
     44 px est la cible tactile du produit (`--cible`) : ces champs
     étaient de toute façon sous le minimum, et la règle les y
     ramène au lieu d'inventer une hauteur pour aligner. */
  #stock .st-filtres.st-grille input:not([type="checkbox"]),
  #stock .st-filtres.st-grille select { flex: none; width: 100%;
    min-width: 0; height: var(--cible); }

  /* ═══ L'ANNUAIRE DES FOURNISSEURS (maquette validée le 20/09) ═════
     DEUX COLONNES SUR PC, la fiche à droite SANS superposition — et
     sur téléphone la fiche REMPLACE la liste. ⚠ Le basculement se
     fait par une CLASSE posée par le JS (`.fiche-ouverte`), jamais par
     un `hidden` calculé à la largeur : *une largeur lue en JS ne suit
     pas une rotation d'écran, une media query si.* */
  #stock .st-annuaire { display: grid; gap: 0;
    grid-template-columns: minmax(240px, 300px) minmax(0, 1fr); }
  #stock .st-four-liste { padding-right: 14px;
    border-right: 1px solid var(--line); }
  #stock .st-four-item { display: flex; align-items: flex-start; gap: 10px;
    width: 100%; text-align: left; padding: 12px 10px;
    min-height: var(--cible); background: none; border: 1px solid transparent;
    border-radius: var(--r-objet); color: inherit; cursor: pointer; }
  #stock .st-four-item:hover { border-color: var(--line2); }
  #stock .st-four-item[aria-current="true"] { background: var(--panel2);
    border-color: var(--accent); }
  /* LE MONOGRAMME EST UN REPÈRE, PAS UNE DONNÉE : il se dérive du nom,
     donc il ne ment jamais et ne demande aucune colonne. */
  #stock .st-four-mono { flex: none; width: 34px; height: 34px;
    display: grid; place-items: center; border-radius: var(--r-fin);
    background: var(--panel2); color: var(--accent);
    font-size: var(--t-etiquette); font-weight: 600; }
  /* 600 et non 650 : la graisse de titre de la charte (DM Sans). */
  #stock .st-four-item b { display: block; font-weight: 600; line-height: 1.4; }
  #stock .st-four-item span { display: block; color: var(--muted);
    font-size: var(--t-note); margin-top: 3px; }
  #stock .st-four-fiche { padding: 0 0 0 20px; min-width: 0; }
  /* ⛔ LES CHAMPS DE L'ANNUAIRE S'AFFICHAIENT BRUTS (Adam, 25/09 : « il
     manque le css ici »). L'habit des champs du stock ne vit que dans
     les blocs de filtres (`.st-filtres input`) ; ceux de l'annuaire — la
     recherche comme la fiche — n'y sont pas, donc le navigateur les
     dessinait à sa façon : fond blanc, bord fin. Ils prennent ici l'habit
     de la barre étiquetée (`.st-filtres.st-grille`), par les mêmes
     jetons et à la même hauteur (`--cible`). */
  #stock .st-annuaire .st-champ input:not([type="checkbox"]),
  #stock .st-annuaire .st-champ textarea {
    background: var(--panel2); color: var(--text);
    border: 1px solid var(--line2); border-radius: var(--r-objet);
    padding: 0 10px; font-family: inherit;
    width: 100%; min-width: 0; height: var(--cible); }
  #stock .st-annuaire .st-champ textarea { height: auto;
    padding: 8px 10px; resize: vertical; }
  /* Habillés, les champs de la fiche collaient chacun à l'étiquette du
     suivant : le même écart que la barre étiquetée (`gap: 12px`). */
  #stock .st-four-fiche > .st-champ { margin-bottom: 12px; }
  #stock .st-four-stats { display: grid; gap: 8px; margin: 14px 0 18px;
    grid-template-columns: repeat(3, 1fr); }
  #stock .st-four-stat { padding: 11px 10px; border-radius: var(--r-objet);
    background: var(--panel2); }
  #stock .st-four-stat b { display: block; font-size: var(--t-chiffre);
    font-weight: 600; }
  #stock .st-four-stat span { display: block; color: var(--muted);
    font-size: var(--t-note); margin-top: 4px; }
  /* L'APERÇU DU LOT : il MONTRE l'effet au lieu d'énoncer la règle.
     *Un gérant ne lit pas « arrondi au multiple supérieur » — il lit
     « 3 lots de 5 ».* */
  #stock .st-four-lot { display: flex; align-items: center; gap: 10px; }
  /* Le pas-a-pas : deux carres de 44, la cible tactile d'Adam. Ils
     prennent `.st-nav` comme tout bouton du module, et cette regle ne
     fait que les CONTRAINDRE ici — elle ne redefinit aucune couleur,
     donc le theme continue de les porter. */
  #stock .st-four-lot .st-nav { min-width: 44px; flex: none; padding: 0; }
  #stock .st-four-lot output { min-width: 44px; text-align: center;
    font-size: var(--t-chiffre); font-weight: 600; }
  #stock .st-four-apercu { display: flex; justify-content: space-between;
    align-items: center; gap: 12px; padding: 12px 14px; margin: 12px 0 0;
    border: 1px solid var(--line); border-radius: var(--r-objet);
    background: var(--panel2); }
  #stock .st-four-apercu b { color: var(--accent); }
  #stock .st-four-barre { position: sticky; bottom: 0; z-index: 2;
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    padding: 12px 0; margin-top: 18px;
    border-top: 1px solid var(--line); background: var(--panel); }
  #stock .st-four-barre .st-msg { margin-right: auto; }
  #stock .st-four-retour { display: none; }

  @media (max-width: 820px) {
    /* LA FICHE REMPLACE LA LISTE — c'est une vraie page, pas un
       panneau posé dessus. *Superposer laisserait la liste défiler
       derrière, et le retour arrière du téléphone fermerait l'écran
       entier au lieu de refermer la fiche.* */
    #stock .st-annuaire { display: block; }
    #stock .st-four-liste { padding-right: 0; border-right: 0; }
    #stock .st-four-fiche { padding-left: 0; }
    #stock .st-annuaire:not(.fiche-ouverte) .st-four-fiche { display: none; }
    #stock .st-annuaire.fiche-ouverte .st-four-liste { display: none; }
    #stock .st-four-retour { display: inline-flex; }
    #stock .st-four-stats { grid-template-columns: repeat(3, 1fr); gap: 6px; }
  }
  /* LA SAISIE EN SÉRIE n'est pas un filtre : elle ÉCRIT. Ses deux
     filets la détachent de la recherche, exactement comme la maquette.
     ⚠ Le select y perd l'habillage que `.st-filtres select` lui
     donnait — sans les trois lignes ci-dessous il repartait au style
     natif du navigateur, ce qui ne lève aucune erreur et se voit
     seulement à l'œil. */
  #stock .st-serie { display: flex; align-items: center; gap: 10px;
    flex-wrap: wrap; margin: 12px 0; padding: 11px 0;
    border-top: 1px solid var(--line2); border-bottom: 1px solid var(--line2); }
  #stock .st-serie-l { font-size: var(--t-etiquette); color: var(--muted);
    text-transform: uppercase; letter-spacing: .04em;
    font-weight: var(--p-appuye); }
  #stock .st-serie select { background: var(--panel2); color: var(--text);
    border: 1px solid var(--line2); border-radius: var(--r-objet);
    height: 34px; padding: 0 10px; font-family: inherit;
    max-width: 100%; }

  /* LA RECHERCHE ET SON BOUTON (13/09, proposition ChatGPT #2) :
     Entrée ne se voit pas, et sur un clavier virtuel la touche de
     validation n'est pas toujours celle qu'on croit. Le bouton hérite
     de `.st-nav`, donc de la cible tactile du produit — rien à
     redéclarer ici, et il suivra si elle change. */
  #stock .st-recherche { display: flex; gap: 6px; min-width: 0; }
  /* ⛔ LE CHAMP PORTE SON HABIT LUI-MÊME (25/09, Adam au banc, sur
     « Consulter une fiche » : « est-ce un problème CSS ou c'est conçu
     de la sorte ? »). Il ne l'avait qu'à l'intérieur d'un bloc
     `.st-filtres` : posé ailleurs, il s'affichait brut — fond blanc,
     coins carrés. Les valeurs sont celles de `.st-filtres input` ; dans
     une grille, la règle de la grille garde la hauteur, et la largeur
     revient au champ (règle ci-dessous). */
  #stock .st-recherche input { flex: 1; min-width: 0;
    background: var(--panel2); color: var(--text);
    border: 1px solid var(--line2); border-radius: var(--r-objet);
    height: var(--cible); padding: 0 10px; font-family: inherit; }
  #stock .st-recherche button { flex: none; }
  /* ⛔ DANS UNE GRILLE, LA LOUPE ÉTAIT POUSSÉE HORS DE SA CASE (relevé
     au banc le 26/09, reprise n° 22) : la règle de la grille impose
     `flex: none; width: 100%` au champ, qui prenait toute la case — la
     loupe passait sous le sélecteur voisin au PC et hors de l'écran au
     téléphone. Le champ d'une recherche reprend sa place flexible. */
  #stock .st-filtres.st-grille .st-recherche input:not([type="checkbox"]) {
    flex: 1; width: auto; }
  /* La catégorie SOUS le nom : elle distingue la référence 13 peinte de
     la 13 brute — deux articles pour une seule référence, et sans elle
     la liste montre deux lignes qui semblent identiques. */
  #stock .st-art-cat { color: var(--muted); }
  #stock .st-reperes { display: flex; flex-direction: column; gap: 3px;
    align-items: flex-start; }
  #stock .st-reperes-d { color: var(--muted); }
  /* L'AIDE DÉPLIABLE : fermée elle ne coûte qu'une ligne, donc elle ne
     gêne pas qui sait déjà — et elle porte ce qu'aucune colonne ne peut
     dire (pourquoi un stock estimé n'entre pas dans les alertes). */
  #stock .st-aide { margin: 14px 0 4px; padding: 10px 12px;
    border: 1px solid var(--line2); border-radius: var(--r-objet);
    background: var(--panel); }
  #stock .st-aide summary { cursor: pointer; min-height: var(--cible-mini);
    display: flex; align-items: center; font-weight: var(--p-appuye); }
  /* ⛔ `display: flex` RETIRE LE TRIANGLE DU NAVIGATEUR (reprise n° 22) :
     rien ne disait que ces cartes s'ouvrent. Un chevron dessiné, qui
     tourne à l'ouverture — tracé en bordures, donc il suit le thème. */
  #stock .st-aide summary::before { content: ""; flex: none;
    width: 7px; height: 7px; margin: 0 12px 0 3px;
    border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
    transform: rotate(-45deg); }
  #stock .st-aide[open] > summary::before { transform: rotate(45deg); }
  #stock .st-aide ul { margin: 6px 0 0; padding-left: 18px;
    color: var(--muted); font-size: var(--t-note); }
  #stock .st-aide li { margin: 6px 0; }
  /* ⛔ LE DÉPLIABLE PORTE UNE LISTE DE DÉFINITIONS À L'ACCUEIL : chaque
     chiffre y a son nom ET sa portée exacte, et une puce ne dit pas
     lequel des deux on lit. Le `ul` reste : c'est la forme de l'écran
     État, et les deux vivent dans la même enveloppe. */
  /* ─── LE CHOIX D'UNE OPÉRATION AU SCANNER (32-B) ──────────────────
     ⛔ LA CARTE ENTIÈRE EST LE BOUTON. Au doigt, viser un petit lien au
     bout d'une fiche est le geste qu'on rate — et ce hub est fait pour
     être pris en main debout, une caisse dans l'autre bras. *Une cible
     de 44 px posée dans une carte de 90 laisse 46 px qui ne font rien,
     et rien ne dit à l'utilisateur lesquels.*
     ⚠ `.nom` et `.portee` reprennent le vocabulaire de `.st-edition` :
     même rôle, même mot. Contextualisés par leur parent, ils ne se
     marchent pas dessus — et un troisième nom pour la même chose est
     une divergence qui attend. */
  /* ─── UN ARTICLE SCANNÉ QUI ATTEND SA QUANTITÉ (32-C2) ────────────
     ⛔ La quantité se tape (décision d'Adam du 17/09) et ça ne peut pas
     se faire pendant le scan : la caméra occupe l'écran entier. Ces
     lignes sont donc le SAS entre les deux — *un article scanné est une
     intention ; c'est la quantité qui en fait une ligne.*
     ⚠ Le champ ne rétrécit pas sous 5em : au doigt, une case de deux
     caractères se rate, et c'est la case qui porte tout le sens. */
  #stock .st-scanne { display: flex; align-items: center; gap: var(--e-2);
    flex-wrap: wrap; padding: 7px 2px;
    border-top: 1px solid var(--line); }
  #stock .st-scanne:first-of-type { border-top: 0; }
  #stock .st-scanne .n { flex: 1 1 140px; min-width: 0;
    font-size: var(--t-corps); }
  #stock .st-scanne input { flex: 0 0 auto; width: 5.5em; min-width: 5em;
    text-align: right; font-variant-numeric: tabular-nums; }
  /* ⚠ CE QUE LA CASE PORTE DÉJÀ (32-D) — l'inventaire au scan REMPLACE
     un total, il ne l'additionne jamais : *une nouvelle lecture rouvre
     le total, elle ne s'y ajoute pas* (contrat de la fiche 32). Sans
     cette mention, porter écraserait en silence ce qui a été compté une
     heure plus tôt.
     ⚠ Elle est en taille d'étiquette et en gris : c'est un repère, pas
     une donnée — la donnée est dans la case, et elle seule part. */
  #stock .st-scanne .d { flex: 0 0 auto; font-size: var(--t-etiquette);
    color: var(--muted); }

  #stock .st-choix-grille { display: grid; gap: var(--e-2);
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    margin-bottom: 14px; }
  /* La ligne « Pas de caméra ici » posée au-dessus (25/09) : sans cet
     écart, son cadre touchait les cartes. */
  #stock .st-bloc + .st-choix-grille { margin-top: var(--e-2); }
  #stock .st-choix { display: block; width: 100%; min-width: 0;
    min-height: var(--cible); padding: 12px 14px;
    background: var(--panel); border: 1px solid var(--line);
    border-radius: var(--r-objet); color: var(--text);
    font: inherit; text-align: left; cursor: pointer; }
  #stock .st-choix:hover { background: var(--panel2); }
  #stock .st-choix .nom { display: block; font-size: var(--t-lu);
    font-weight: var(--p-appuye); }
  #stock .st-choix .portee { display: block; font-size: var(--t-corps);
    color: var(--muted); margin-top: 3px; }

  /* LA FORME A COUPLES de `stAide` : un mot, puis ce qu'il veut
     dire. Elle remplace le <dl> ecrit a la main de l'accueil —
     le style le suit, il ne lui survit pas en classe fantome. */
  #stock .st-aide li > b { font-weight: var(--p-appuye); }
  #stock .st-aide-d { color: var(--muted); }

  /* ─── L'ACCUEIL DU STOCK (lot 31-B, maquette 31 validée) ──────────
     ⛔ TROIS REPÈRES PHYSIQUES, ET LA VALEUR N'EN EST PAS UN. Les
     quatre tuiles d'avant posaient « Entrepôt » et « Valeur (vente
     TTC) » côte à côte : *le voisinage faisait lire « la valeur de mon
     entrepôt »*, alors que le serveur cumule TOUS LES LIEUX et n'y
     garde que les soldes positifs. Le chiffre était juste, sa PLACE le
     rendait faux — et rien ne pouvait le signaler, puisqu'aucun test ne
     lit une mise en page. La valeur part donc en colonne, sous son
     propre titre et sa propre portée.
     ⚠ `auto-fit` plutôt qu'un nombre de colonnes : la fenêtre du stock
     partage la scène avec la conversation, donc la largeur qui compte
     est celle du PANNEAU, jamais celle de l'écran. Trois repères
     tiennent en ligne dès 600px de contenu, passent à deux, puis à un —
     sans qu'aucun point de bascule soit inventé. */
  #stock .st-kpis.st-trois {
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
  /* La portée sous le chiffre tient sur DEUX lignes : ce que le nombre
     compte, puis ce qu'il ne compte pas. La seconde est la seule chose
     qui empêche d'additionner deux cartes qui ne se recouvrent pas. */
  #stock .st-kpi .d + .d { margin-top: 3px; }

  /* La grille de l'accueil : ce qu'on surveille à gauche, ce que ça
     vaut à droite. Une seule colonne tant que le cadre est étroit. */
  #stock .st-bord { display: grid; gap: var(--e-3); align-items: start;
    margin-top: 12px; }
  #stock .st-bord > * { min-width: 0; }
  #stock .st-valeur { background: var(--panel); border: 1px solid var(--line);
    border-radius: var(--r-objet); padding: 12px; }
  #stock .st-valeur h3 { margin: 0 0 10px; font-size: var(--t-etiquette);
    color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }
  #stock .st-valeur .ligne { margin-bottom: 10px; }
  #stock .st-valeur .l { display: block; font-size: var(--t-note);
    color: var(--muted); }
  #stock .st-valeur .m { font-size: var(--t-chiffre);
    font-weight: var(--p-appuye); font-variant-numeric: tabular-nums; }
  #stock .st-valeur .note { margin: 0 0 10px; font-size: var(--t-note);
    color: var(--muted); }
  /* L'HEURE DIT QU'UNE LECTURE A RÉUSSI, jamais la date d'un mouvement.
     Le bouton relit ; il n'écrit rien. */
  #stock .st-lecture { display: flex; align-items: center; flex-wrap: wrap;
    gap: var(--e-2); margin: 2px 0 12px; }
  #stock .st-lecture .q { font-size: var(--t-note); color: var(--muted); }

  #stock .st-champ { display: grid; gap: 5px; min-width: 0; }
  #stock .st-champ-l { font-size: var(--t-etiquette); color: var(--muted);
    font-weight: var(--p-appuye); text-transform: uppercase;
    letter-spacing: .04em; }
  /* ⛔ UN CHAMP QUI PREND TOUTE LA LARGEUR DE SA GRILLE (13/09,
     proposition 09, forme vue par Adam sur l'écran réel). Un nom de
     client est long et il commande tout le reste de la commande : le
     ranger dans une colonne à côté d'un select le tronque. La maquette
     validée lui donne sa propre ligne, les deux autres champs se
     partageant celle du dessous. */
  #stock .st-filtres.st-grille .st-champ-large { grid-column: 1 / -1; }
  /* ⛔ LA DOUBLE EPAISSEUR DES LISTES DEROULANTES (Adam au banc,
     21/09). On bordait le `select` — trait + rayon de 12px — sans
     jamais neutraliser son apparence NATIVE : le navigateur dessinait
     donc son propre controle, un rectangle qui ne suit pas le rayon,
     A L'INTERIEUR de notre boite. *Deux traits concentriques, et
     aucune regle fautive : c'est l'ABSENCE d'une ligne qui se voyait.*
     ⚠ `appearance: none` EMPORTE AUSSI LA FLECHE, donc elle se
     redessine ici. Deux degrades croises et pas une image : *un SVG en
     data-URI fige sa couleur, et ce produit a quatre themes.*
     `currentColor` suit le texte, donc le theme, sans qu'aucune regle
     s'en occupe — c'est le meme choix que les icones du rail.
     ⚠⚠ `background-image` SEUL, jamais le raccourci `background` :
     les regles voisines posent `background: var(--panel2)`, et un
     raccourci ici remettrait leur couleur a zero. */
  #stock select {
    -webkit-appearance: none; -moz-appearance: none; appearance: none;
    background-image:
      linear-gradient(45deg, transparent 50%, currentColor 50%),
      linear-gradient(135deg, currentColor 50%, transparent 50%);
    background-position: right 15px center, right 10px center;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
    padding-right: 30px;
  }
  /* ⛔ UN SELECT HORS D'UNE BARRE DE FILTRES N'AVAIT QUE SA FLÈCHE (Adam
     au banc, 26/09, les réglages de Documents). L'habillage vit sur
     `.st-filtres select` et `.st-serie select` : un `select` posé par
     `stChamp` ailleurs gardait le fond et le texte du navigateur, une
     barre blanche sur un thème sombre, sans aucune erreur. C'est le
     défaut déjà réparé pour la saisie en série, qui revenait par un
     autre conteneur. Même habillage ici, en `background-color` et
     jamais le raccourci (il effacerait la flèche ci-dessus), avec la
     place de la flèche gardée à droite. */
  #stock .st-champ select { background-color: var(--panel2); color: var(--text);
    border: 1px solid var(--line2); border-radius: var(--r-objet);
    height: 34px; padding: 0 30px 0 10px; font-family: inherit; max-width: 100%; }
  /* Dans un panneau, le champ est seul sur sa ligne : la grille de
     `.st-champ` étirait le select d'un bord à l'autre, et étirait aussi
     la case à cocher, que le navigateur dessine alors au MILIEU de sa
     largeur. */
  #stock .st-bloc > .st-champ select { justify-self: start; min-width: 240px; }
  #stock .st-bloc > .st-champ input[type="checkbox"] { justify-self: start; }
  /* Le champ de saisie posé par `stChamp` dans un panneau n'avait aucun
     habit (Adam, 01/10, « Nouvelle foire ») : même habit que l'annuaire. */
  #stock .st-bloc > .st-champ > input:not([type="checkbox"]):not([type="radio"]),
  #stock .st-bloc > .st-champ > textarea {
    background: var(--panel2); color: var(--text);
    border: 1px solid var(--line2); border-radius: var(--r-objet);
    padding: 0 10px; font-family: inherit;
    height: var(--cible); min-width: 0; max-width: 100%; justify-self: start; }
  #stock .st-bloc > .st-champ > input:not([type]),
  #stock .st-bloc > .st-champ > input[type="text"],
  #stock .st-bloc > .st-champ > input[type="search"] { width: min(100%, 480px); }
  #stock .st-bloc > .st-champ > textarea { width: 100%; height: auto;
    padding: 8px 10px; resize: vertical; }
  /* La démarque connue d'une foire (01/10) : la quantité, puis son motif,
     dans la cellule. Même habit que les autres champs du stock. */
  #stock .st-demarque { display: flex; gap: 6px; align-items: center;
    justify-content: flex-end; }
  #stock .st-demarque input { width: 4.5em; text-align: right; }
  #stock .st-demarque select { background-color: var(--panel2); color: var(--text);
    border: 1px solid var(--line2); border-radius: var(--r-objet);
    height: 34px; padding: 0 26px 0 8px; font-family: inherit; }

  .st-filtres { display: flex; gap: 6px; flex-wrap: wrap; margin: 8px 0; }
  /* Un formulaire de CRÉATION n'est pas une barre de filtres : il
     s'encadre, pour qu'on voie qu'un panneau s'est ouvert. */
  .st-panneau { border: 1px solid var(--line2); border-radius: var(--r-objet);
    background: var(--panel); padding: 11px 12px; margin: 10px 0; }
  .st-panneau > .titre { font-size: var(--t-etiquette); color: var(--muted);
    text-transform: uppercase; letter-spacing: .04em; margin-bottom: 8px; }
  .st-panneau .st-filtres { margin: 0; }
  /* Onglets d'un écran du stock (Documents : éditer / bibliothèque). */
  .st-onglets { display: flex; gap: 6px; margin: 4px 0 10px;
    border-bottom: 1px solid var(--line2); }
  .st-onglet:hover { color: var(--text); }
  .st-panneau .st-actions { margin-top: 9px; }
  /* contrôles-pilules cliquables (case de filtre, imprimer, CSV) — plus
     grands et aérés que les badges st-tag (22/08 : « c'est collé ») */
  .st-pilule-action { font-size: var(--t-corps); background: var(--panel2);
    border: 1px solid var(--line2); border-radius: 999px; padding: 7px 14px;
    color: var(--text); cursor: pointer; text-decoration: none;
    /* `font-family: inherit` (13/09) : la règle ne servait qu'à des
       <a> et des <label>, qui héritent tout seuls. Un <button> ne le
       fait pas — il retombe sur la police du SYSTÈME, et la pilule
       cesse d'être de la même espèce que ses voisines sans qu'aucune
       règle ne l'ait demandé. Neutre pour l'existant. */
    font-family: inherit;
    display: inline-flex; align-items: center; }
  .st-pilule-action:hover { border-color: var(--accent); }
  /* Une pilule qui DÉPLIE dit qu'elle est ouverte. Posée après sa
     base : à spécificité égale, l'ordre source tranche. */
  .st-pilule-action[aria-expanded="true"] { border-color: var(--accent);
    color: var(--accent); }

  /* ── L'ÉCRAN FOURNISSEURS (13/09, proposition 03) ──────────────
     Ce qui n'existait pas encore ; le reste entre dans les familles
     du produit — les deux vues sont des `.st-tab`, les documents et
     le dépliage des `.st-pilule-action`. */
  /* Une CASE À COCHER n'est pas un champ : son libellé se lit À CÔTÉ.
     `stChamp` empile étiquette et contrôle — une case sous son
     étiquette se lirait comme une colonne vide. Toute la ligne est
     tapable, et elle monte à la cible tactile. */
  #stock .st-case { display: flex; align-items: center; gap: 9px;
    min-height: var(--cible); cursor: pointer;
    font-size: var(--t-corps); min-width: 0; }
  /* Le RESTE d'une commande : le chiffre, et SOUS lui ce qui empêche
     de le lire comme une livraison attendue (un brouillon n'engage
     rien, une annulée ne livrera jamais).
     ⚠ Aligné à DROITE, comme sa colonne : un conteneur flex calé à
     gauche dans une cellule numérique est le défaut d'alignement
     payé QUATRE fois le 13/09. */
  #stock .st-reste { display: flex; flex-direction: column;
    align-items: flex-end; gap: 3px; }
  #stock .st-reste-fort { font-weight: var(--p-appuye); }
  /* Le détail des commandes d'origine s'ouvre SOUS sa ligne, dans le
     tableau : au-dessus, il obligeait à refaire de mémoire le lien
     entre l'article et « le bloc d'à côté ». */
  #stock .st-origines > td { padding: 0 0 12px;
    border-bottom: 1px solid var(--line); }
  /* Un lien de document sans liste à imprimer ne promet rien. */
  #stock .st-serie a[aria-disabled="true"] { opacity: .45;
    pointer-events: none; }

  /* ── LA COMMANDE FOURNISSEUR (13/09, proposition 04) ───────────
     Le pied des lignes préparées : ce qu'on a demandé en tout, et le
     rattrapage d'un retrait. Les deux ne s'affichent qu'en mode
     commande — les dix autres écrans de saisie gardent leur forme,
     qui n'a pas été soumise. */
  #stock .st-pied-lignes { display: flex; align-items: center; gap: 12px;
    flex-wrap: wrap; margin-top: 10px; padding-top: 10px;
    border-top: 1px solid var(--line2); }
  #stock .st-total { font-weight: var(--p-appuye);
    font-size: var(--t-lu); }
  /* LE RÉCIT D'UNE RÉCEPTION (13/09, proposition 06) : il partait
     dans un message qui s'efface, APRÈS un changement d'écran. Sept
     mouvements répartis sur deux commandes plus un surplus tiennent
     mal sur une ligne, et c'est pourtant tout ce qui dit au
     magasinier ce que le système a décidé à sa place. Les puces sont
     rentrées : la liste vit dans un bloc qui a déjà sa marge. */
  #stock .st-bloc ul { margin: 0; padding-left: 20px; }
  #stock .st-bloc li { font-size: var(--t-corps); margin: 3px 0; }
  /* ⛔ UN BLOC VIDE NE SE MONTRE PAS (25/09, même capture) : la zone des
     résultats de « Consulter une fiche » naît vide, et son cadre
     s'affichait en barre blanche sous le champ, sans rien dedans. */
  #stock .st-bloc:empty { display: none; }
  .st-filtres input, .st-filtres select {
    background: var(--panel2); color: var(--text); border: 1px solid var(--line2);
    /* Le champ seul, pas la barre : ce sont des contrôles côte à
       côte, sans aucun texte aligné dessus — monter la barre
       grossirait ce qui n'a rien à suivre. */
    border-radius: var(--r-objet); height: 34px; padding: 0 10px; font-family: inherit;
  }
  .st-filtres input { flex: 1; min-width: 120px; }
  /* une CASE À COCHER n'est pas un champ de saisie : sans cette ligne
     elle héritait de flex:1 et min-width:120px — étirée en carré blanc
     de 120 px, son libellé projeté à l'autre bout (22/08 : « ce carré
     blanc en bas de page, il correspond à quoi ? »). */
  .st-filtres input[type="checkbox"] { flex: none; min-width: 0;
    width: auto; height: auto; align-self: center; }
  /* Une option qui ENGAGE (mettre des réfs à zéro) se voit comme telle :
     encadrée, la case collée à son libellé, jamais noyée dans une barre
     de filtres. */
  .st-choix-lourd { display: flex; align-items: flex-start; gap: 9px;
    border: 1px solid var(--danger); border-radius: var(--r-objet);
    padding: 10px 12px; margin: 10px 0; }
  .st-choix-lourd label { cursor: pointer; font-size: var(--t-corps); }
  .st-choix-lourd input { flex: none; margin-top: 2px; }
  /* un tableau ne démarre jamais collé à ce qui le précède */
  .st-scroll { overflow-x: auto; margin-top: 10px; }
  .st-table { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
  .st-table th { text-align: left; font-size: var(--t-etiquette); color: var(--muted); text-transform: uppercase;
                 letter-spacing: .04em; padding: 6px 7px; border-bottom: 1px solid var(--line); }
  .st-table th.num, .st-table td.num { text-align: right; }
  /* ⭐ UN CHIFFRE CALÉ À DROITE, PUIS UN TEXTE CALÉ À GAUCHE : leurs
     titres se touchaient à 14 px (« SEUIL SITUATION », « RESTE
     STATUT »). De l'air à la jonction seulement (Adam, 30/09 : « oui »,
     reprise n° 22, point 4). Sans `#stock`, exprès : les cartes du
     téléphone (`#stock .st-scroll:not(.st-tab-large)`) remettent leur
     propre marge à zéro et doivent garder la main. */
  .st-table th.num + th:not(.num),
  .st-table td.num + td:not(.num) { padding-left: 22px; }
  /* Rapports : mêmes colonnes entre catégories, sans couper dates et nombres. */
  #stock .st-rapport-titre { margin: var(--e-3) 0 var(--e-2); }
  #stock .st-rapport-table .st-table { table-layout: fixed; }
  #stock .st-rapport-table .st-table th,
  #stock .st-rapport-table .st-table td { overflow-wrap: normal; word-break: normal; }
  #stock .st-rapport-nombre { white-space: nowrap; }
  @container nimzou-stock (min-width: 601px) {
    #stock .st-rapport-table .st-table { min-width: 1080px; }
  }
  @container nimzou-stock (max-width: 600px) {
    #stock .st-rapport-table colgroup { display: none; }
  }
  /* La cellule monte avec son champ. Pas par nécessité de largeur :
     l'input est en `5.5em`, donc sa case suivait toute seule. Mais le
     texte de la cellule voisine serait resté à 14 à côté d'un champ
     à 16, dans la même ligne. La table défile déjà (.st-scroll). */
  .st-table td { padding: 6px 7px; border-bottom: 1px solid var(--line);
                 font-size: var(--t-lu); }
  .st-table tbody tr.lien { cursor: pointer; }
  .st-table tbody tr.lien:hover { background: var(--panel); }
  .st-neg { color: var(--danger); font-weight: 600; }
  .st-tag { font-size: var(--t-etiquette); background: var(--panel2); border: 1px solid var(--line2);
            border-radius: 999px; padding: 1px 7px; color: var(--muted); white-space: nowrap; }
  /* le badge qui doit ARRÊTER l'œil : stock insuffisant, ligne annulée */
  .st-tag.alerte { color: var(--danger); border-color: var(--danger); }
  /* Boutons discrets : les classes existantes gardent leur apparence. */
  #stock .st-action { font-family: inherit; line-height: inherit; cursor: pointer; }
  #stock .st-action:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  #stock .st-action:disabled { cursor: default; }
  #stock .st-action.st-nue { border: 0; padding: 0; background: transparent;
    color: inherit; font-size: inherit; }
  /* Pas `.st-tag` ici : elle pose déjà son propre fond (`--panel2`).
     Seuls les postes et les suggestions n'en avaient pas. */
  .st-action.st-poste,
  .st-action.st-suggestion { background: transparent; }
  #stock button.st-poste, #stock button.st-suggestion { width: 100%; text-align: left;
    border-top: 0; border-left: 0; border-right: 0;
    color: inherit; font-size: inherit; }
  #stock button.st-photo { padding: 0; }
  #stock .st-suggestion .t, #stock .st-suggestion .l1,
  #stock .st-suggestion .l2 { display: block; }
  /* la référence du document, sous le nom du client (22/08) : présente
     pour qui la cherche, effacée pour qui lit la liste */
  .st-sous { display: block; font-size: var(--t-etiquette); color: var(--muted); }
  .st-table td input, .st-cellule-edit {
    background: var(--panel2); color: var(--text); border: 1px solid var(--line2);
    border-radius: var(--r-fin); padding: 3px 6px; font-family: inherit; width: 5.5em; text-align: right;
  }
  /* même piège dans les tableaux : une case à cocher n'a pas à faire
     5,5 em de large ni à être alignée à droite (foires « Préparée ? »,
     proposition de commande) */
  .st-table td input[type="checkbox"] { width: auto; margin: 0; }
  .st-photo { width: 104px; height: 104px; flex: none; background: var(--panel2);
              border: 1px dashed var(--line2); border-radius: 12px; display: flex;
              align-items: center; justify-content: center; flex-direction: column;
              color: var(--muted); font-size: var(--t-note); cursor: pointer; overflow: hidden; }
  .st-photo img { width: 100%; height: 100%; object-fit: cover; }
  .st-poste { display: flex; justify-content: space-between; padding: 5px 6px;
              border-bottom: 1px solid var(--line); }
  .st-poste .q { font-variant-numeric: tabular-nums; }
  /* Suggestions de saisie (22/08) : photo + réf + catégorie + stock */
  .st-suggestion { display: flex; align-items: center; gap: 10px; padding: 5px 6px;
                   border-bottom: 1px solid var(--line); cursor: pointer;
                   border-radius: 8px; }
  .st-suggestion:hover, .st-suggestion.actif { background: var(--panel2); }
  .st-suggestion img, .st-sugg-vide {
    width: 44px; height: 44px; flex: none; border-radius: 8px;
    object-fit: cover; background: var(--panel2);
    display: flex; align-items: center; justify-content: center; font-size: 20px; }
  .st-suggestion .l2 { font-size: var(--t-note); color: var(--muted); }
  .st-actions { display: flex; gap: 8px; margin-top: 10px; justify-content: flex-end; flex-wrap: wrap; }
  /* Barre qui SUIT le regard quand la liste est longue (listing de
     saisie de 1 330 lignes) : le bouton reste sous la main. */
  .st-collante {
    position: sticky; top: 0; z-index: 3; align-items: center;
    background: var(--panel); border-bottom: 1px solid var(--line);
    padding: 8px 0; margin-top: 0;
  }
  /* Même principe en bas : les gestes d'une commande de 141 lignes
     doivent rester sous la main sans descendre jusqu'au bout. */
  .st-collante-bas {
    position: sticky; bottom: 0; z-index: 3; align-items: center;
    background: var(--panel); border-top: 1px solid var(--line);
    padding: 8px 0;
  }
  /* ⛔ DANS LE STOCK, LA BARRE DU BAS EST UNE CARTE, PAS UNE BANDE (Adam,
     26/09, en recette, deux captures). Elle a été écrite pour un panneau
     de la couleur des cartes, où son fond ne se voyait pas. La fenêtre du
     stock est peinte en `--bg` (mesuré au banc, dans les deux mises en
     page) : le même fond y dessinait une bande sans bord ni marge, et le
     rappel comme les boutons en touchaient les deux côtés. Elle prend la
     forme des blocs du stock (`.st-bloc`) : la carte de saisie, juste
     au-dessus, a ce bord, ce rayon et cette marge. La barre de la fiche
     fournisseur avait le même défaut. */
  #stock .st-collante-bas, #stock .st-four-barre {
    border: 1px solid var(--line); border-radius: var(--r-objet);
    padding: 8px 10px; }
  /* ⚠ LE COMPTEUR DU LISTING NE CHANGE PAS LA HAUTEUR DE SA BARRE (revue
     adverse du 23/09, mesuré au téléphone 412 px) : en s'allongeant
     (« · 1 à corriger », un total qui passe 1 000), il faisait passer la
     barre collante de 61 à 93 px, et le listing bougeait de 32 px sous le
     doigt — le toucher suivant tombait à côté de sa case. Au téléphone, il
     prend sa propre ligne et deux lignes de hauteur, d'office. */
  @container nimzou-stock (max-width: 600px) {
    #stock .st-compteur-listing { flex: 1 1 100%; min-height: 3em; min-height: 2lh; }
  }
  .st-nav.primary { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }

  /* STOCK STRUCTURÉ — proposition ciblée, limitée au panneau Stock.
     Les couleurs restent celles du thème produit actif. */
  #stock { container-type: inline-size; container-name: nimzou-stock; }
  /* ⛔ LES MARGES LATÉRALES DU STOCK (Adam, 22/09 : *« les marges de ce
     document sont trop justes »*). `.st-body` ne portait QUE
     `padding-top` — le texte, les tableaux et les boutons touchaient
     les deux bords. *Une règle qui pose un seul côté ne se lit pas
     comme un oubli : elle se lit comme une décision.*
     ⚠ POSÉ SUR `#stock` SEUL, jamais sur la règle partagée
     `.bg-body, .ag-body, .rv-body, .st-body` : les trois autres modules
     ne sont pas passés à la recette, et élargir un correctif à des
     écrans que personne n'a regardés déplace le risque au lieu de le
     réduire. */
  #stock .st-body { padding-left: var(--e-4); padding-right: var(--e-4); }

  /* ⛔ LA BULLE « i » (Adam, 22/09 : *« on dirait un livre, pas un
     module de stock — ça fera peur au lieu de rassurer »*). Mesuré :
     72 395 signes de prose en 1 504 phrases, dont 377 messages
     TOUJOURS visibles contre 7 blocs dépliables et 24 infobulles.
     ⚠ UN `<details>`, PAS UN `title` : l'infobulle native n'apparaît
     qu'au SURVOL — donc jamais sous un doigt, et ce module se saisit
     debout. Le navigateur porte l'ouverture, le clavier et le doigt.
     ⚠⚠ `list-style: none` ET le pseudo WebKit : le triangle de
     `summary` se retire par DEUX chemins selon le moteur, et en oublier
     un laisse une flèche collée au rond — sans erreur, évidemment. */
  #stock .st-bulle { display: inline-block; position: relative;
    vertical-align: middle; margin-left: 6px; }
  #stock .st-bulle > summary { position: relative; list-style: none;
    cursor: pointer; display: inline-flex; align-items: center;
    justify-content: center; width: 22px; height: 22px;
    border-radius: 50%; border: 1px solid var(--line2);
    background: var(--panel2); color: var(--muted);
    font-size: var(--t-etiquette); font-style: italic;
    font-weight: var(--p-appuye); line-height: 1; }
  #stock .st-bulle > summary::-webkit-details-marker { display: none; }
  /* ⚠ LA CIBLE FAIT 44 px SANS QUE LE ROND GROSSISSE : un « i » de
     44 px serait un bouton, un « i » de 22 px qu'on ne peut pas viser
     ne serait rien. C'est la cible tactile du produit, tenue par une
     zone invisible. */
  #stock .st-bulle > summary::before { content: ""; position: absolute;
    inset: -11px; border-radius: 50%; }
  #stock .st-bulle > summary:hover,
  #stock .st-bulle[open] > summary { color: var(--accent);
    border-color: var(--accent); }
  /* ⚠ `text-transform` ET `letter-spacing` REMIS À PLAT : l'étiquette
     de champ qui accueille la bulle est en PETITES CAPITALES espacées,
     et une phrase entière hériterait des deux. */
  #stock .st-bulle-t { position: absolute; z-index: 5; top: 27px;
    left: 0; width: min(320px, 76vw); padding: 10px 12px;
    border: 1px solid var(--line2); border-radius: var(--r-objet);
    background: var(--panel); color: var(--text);
    font-size: var(--t-note); font-weight: 400; font-style: normal;
    text-transform: none; letter-spacing: 0; line-height: 1.45;
    box-shadow: 0 10px 28px var(--bubble-shadow); }
  /* ⚠ PRÈS DU BORD DROIT, la bulle sortirait de l'écran : dans une
     barre d'actions, elle s'accroche à DROITE. */
  #stock .st-actions .st-bulle-t { left: auto; right: 0; }
  /* ⚠ DANS UNE BARRE CALÉE À GAUCHE, elle s'ouvre vers la DROITE ; et le
     rond se centre sur la hauteur des boutons — étiré par la barre, il
     restait collé en haut (Adam, 25/09 : « regarde les espacements »).
     Ce qui dépasse encore se recale à l'ouverture (`stBulle`). */
  #stock .st-actions.st-a-gauche .st-bulle-t { left: 0; right: auto; }
  #stock .st-actions > .st-bulle { align-self: center; }

  /* ═══ LES DEUX TEMPS D'UNE SAISIE (Adam, 25/09, en recette) ═══════
     D'abord les prérequis, PUIS la saisie — avec un rappel d'une ligne
     et « Modifier ». *« Toute opération doit apparaître au moment où il
     faut : ne pas laisser les yeux partir de partout. »* Racine :
     `stDeuxTemps` dans web/modules/stock.js. */
  #stock .st-rappel { display: flex; align-items: center; gap: 10px;
    flex-wrap: wrap; margin: 0 0 12px; padding: 10px 14px;
    border: 1px solid var(--line2); border-left: 4px solid var(--accent);
    border-radius: var(--r-objet); background: var(--panel2); }
  #stock .st-rappel-bloc { flex: 1 1 14em; min-width: 0; }
  /* « Vous saisissez » : la forme du fil de l'en-tête (`.st-fil`). */
  #stock .st-rappel-l { font-size: var(--t-etiquette); color: var(--muted);
    text-transform: uppercase; letter-spacing: .07em; margin-bottom: 2px; }
  /* Le rappel à la taille du titre de l'écran (`.st-h1`) : Adam, 25/09,
     « pour que l'on sache à tout moment où on saisit ». */
  #stock .st-rappel-t { font-size: var(--t-chiffre);
    font-weight: var(--p-appuye); line-height: 1.25; }
  /* Le même, en court, dans la barre collante d'« Enregistrer » : à
     gauche, en gras — elle reste sous les yeux quand le bandeau du haut
     est parti avec le défilement. */
  #stock .st-rappel-court { margin-right: auto; min-width: 0;
    font-weight: var(--p-appuye); }
  /* Ce qui aide à choisir se lit SOUS le champ, pas à l'autre bout de
     l'écran. */
  #stock .st-actions.st-a-gauche { justify-content: flex-start; }
  /* `auto-fill` et non `auto-fit` : seul à l'écran, un premier champ
     s'étirerait sur toute la largeur (auto-fit replie les colonnes
     vides). */
  #stock .st-filtres.st-grille.st-grille-fixe {
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); }
  /* ⛔ `hidden` NE CACHAIT PAS CES BLOCS : leur classe pose un
     `display`, qui l'emporte sur celui du navigateur, et l'élément
     « caché » restait à l'écran. Vu par Adam le 25/09 en recette : la
     barre « Chercher : réf, nom ou code-barres » restait affichée sur
     l'onglet « Sur le listing », alors que ses résultats, eux, étaient
     bien cachés — on tapait, rien ne se passait.
     ⛔⛔ PAS UNE RÈGLE GÉNÉRALE `#stock [hidden]`, ET C'EST PAYÉ : posée
     en production le 25/09 à 13h33, retirée au déploiement suivant. La
     navigation latérale
     (`.st-lat`) porte `hidden` pour son tiroir de téléphone et se
     RÉAFFICHE exprès sur grand écran (`#stock .st-lat[hidden] {
     display: block }`) — la règle générale l'a fait disparaître.
     *Un élément caché par l'attribut peut être montré exprès par la
     feuille de style.* Donc une règle PAR CLASSE, et chaque classe qui
     pose un `display` déclare ici son `[hidden]`. */
  #stock .st-filtres[hidden], #stock .st-actions[hidden],
  #stock .st-champ[hidden] { display: none; }

  /* Le nom d'un nouveau fournisseur et la création de sa fiche, sur une
     ligne (`stChoixFournisseur`, 30/09). */
  #stock .st-four-nouveau { display: flex; gap: 8px; flex-wrap: wrap;
    align-items: flex-end; }
  #stock .st-four-nouveau > .st-champ { flex: 1 1 240px; }
  #stock .st-four-nouveau > .st-nav { min-height: var(--cible); }
  #stock .st-four-nouveau[hidden] { display: none; }

  /* ═══ LA NAVIGATION LATÉRALE DU STOCK (lot 31, 16/09) ════════════
     Adam : *« la refonte a pour but de faciliter et de montrer un côté
     pro ; actuellement c'est propre mais pas pro, et ce n'est même pas
     le standard des applications. »* Et sa précision, qui commande tout
     ce bloc : *« ce n'est pas un double menu, c'est un DÉPLACEMENT de
     menu sur une navigation latérale tout en gardant la vue d'ensemble —
     si le menu latéral fait doublon, aucun intérêt. »*
     ▶ La grille de dix tuiles qui occupait l'accueil DEVIENT cette
     barre. Une seule liste (`ST_RUBRIQUES`), jamais deux.
     ⚠ LE SEUIL EST SUR LE CONTENEUR, JAMAIS SUR LA FENÊTRE. Au bureau,
     la fenêtre du stock partage la scène avec la conversation : sa
     largeur n'est pas celle du navigateur. *Un seuil posé sur la
     fenêtre replierait le menu au mauvais moment, ou jamais.* Le
     conteneur existe depuis le lot des tables. */
  #stock .st-cadre { flex: 1; display: flex; min-height: 0; gap: var(--e-3); }
  #stock .st-contenu { flex: 1; min-width: 0;
    display: flex; flex-direction: column; }
  /* ⛔ LA GOUTTIERE HORIZONTALE N'EST PAS DU CONFORT : `.entree` fait
     `width: 100%`, donc sans elle la pilule de l'entree courante — un
     aplat d'accent a 12px de rayon — vient TOUCHER le trait de
     separation, et le bord gauche de la fenetre. *Un rayon qui meurt
     contre une ligne se lit comme un defaut de rendu, pas comme un
     choix.* (Verdict d'Adam au banc, 21/09.)
     ⚠ ELLE VIT ICI ET PLUS DANS LA SURCHARGE MOBILE, qui portait
     deja `var(--e-2) 4px` : *le defaut ne survivait que sur la moitie
     qu'on ne regardait pas.* Deux valeurs pour la meme intention
     divergent, et c'est celle qu'on ne regarde pas qui a tort. */
  #stock .st-lat { flex: none; width: 216px; overflow-y: auto;
    -webkit-overflow-scrolling: touch; padding: var(--e-2);
    border-right: 1px solid var(--line); }
  #stock .st-lat .groupe { font-size: var(--t-etiquette); color: var(--muted);
    text-transform: uppercase; letter-spacing: .05em;
    padding: 14px 10px 4px; }
  #stock .st-lat .groupe:first-child { padding-top: 2px; }
  /* ⛔ UNE ENTRÉE EST UNE CIBLE : 44 px, comme tout ce qui se vise au
     pouce dans ce produit. *La règle du 07/09 vaut d'autant plus ici
     que cette barre est le chemin de TOUS les écrans du module.* */
  #stock .st-lat .entree { display: flex; align-items: center; gap: 9px;
    width: 100%; min-height: var(--cible); padding: 0 10px;
    background: none; border: 0; border-radius: var(--r-objet);
    color: var(--text); font: inherit; font-size: var(--t-corps);
    text-align: left; cursor: pointer; }
  #stock .st-lat .entree:hover { background: var(--panel2); }
  /* L'ICÔNE SUIT LA COULEUR DU TEXTE (`currentColor` dans le symbole),
     donc elle devient blanche sur l'entrée courante sans qu'une règle
     s'en occupe. `flex: none` l'empêche d'être écrasée par un libellé
     long — « Proposition de commande » passe à la ligne, pas elle. */
  #stock .st-lat .entree svg { width: 19px; height: 19px; flex: none;
    display: block; }
  /* L'ENTRÉE COURANTE SE VOIT SANS ÊTRE LUE — et elle est désignée par
     `aria-current`, l'attribut que le navigateur ET le lecteur d'écran
     comprennent tous les deux. *Peindre une classe à la main ferait
     deux vérités : ce qui est visible et ce qui est annoncé.* */
  #stock .st-lat .entree[aria-current="page"] {
    background: var(--accent); color: var(--on-accent);
    font-weight: var(--p-appuye); }
  #stock .st-lat .entree[hidden] { display: none; }
  #stock .st-voile { display: none; }
  /* ── LE REPLI ────────────────────────────────────────────────────
     Sous ce seuil, la barre devient une SURFACE OPAQUE au-dessus du
     contenu : au téléphone, 216 px pris à une largeur de 360 ne
     laisseraient pas de place au stock lui-même. */
  @container nimzou-stock (min-width: 721px) {
    /* Le menu est là : le bouton qui l'ouvrirait serait le doublon. */
    #st-menu { display: none; }
    /* ⛔⛔ SANS CETTE LIGNE LA BARRE EST INVISIBLE SUR PC, et rien ne le
       signale. Le JavaScript pose `hidden` au chargement pour que la
       SURFACE du téléphone parte fermée ; au-dessus du seuil, l'attribut
       garde pourtant son `display: none` natif — la règle de la barre ne
       déclare aucun `display`, donc rien ne le contredit.
       *Un commentaire de `main.js` affirmait le contraire une minute
       avant que cette ligne n'existe : une intention écrite n'est pas un
       comportement, et le seul juge est le navigateur.* */
    /* L'ACCUEIL PASSE À DEUX COLONNES EN MÊME TEMPS QUE LE MENU SE
       DÉPLIE : c'est le même moment où le cadre a de la largeur à
       donner. *Un second seuil, posé ailleurs, se réglerait tout seul
       de travers le jour où celui-ci bougerait.* */
    #stock .st-bord { grid-template-columns: minmax(0, 1fr) 280px; }
    #stock .st-lat[hidden] { display: block; }
  }
  @container nimzou-stock (max-width: 720px) {
    /* ⛔⛔ `z-index: 3` NE SUFFISAIT PAS, ET ADAM L'A VU : « quand je
       reviens sur le menu, il y a en bas le mot ARTICLE qui apparaît au
       milieu ». C'est l'en-tête figée du catalogue
       (`.st-tab-large th:first-child`, z-index 3) qui TRAVERSAIT la
       surface — et les deux barres collantes du stock sont dans le même
       cas. *À z-index égal, c'est l'ORDRE DU DOCUMENT qui tranche : la
       barre est écrite AVANT le contenu, donc le contenu gagne.*
       ⚠ Le menu replié est une SURFACE : il passe au-dessus de tout le
       module. 12 et 11 le placent au-dessus des collantes (3) et bien en
       dessous des overlays (30) et du scan (47) — *un menu qui couvrirait
       une boîte de dialogue serait le défaut symétrique.*
       ⚠ Ce défaut ne se voit QUE sur un écran qui porte un élément
       collant, et seulement menu OUVERT : aucun contrôle de texte ne
       pouvait l'attraper, il fallait un œil. */
    #stock .st-lat { position: absolute; z-index: 12; top: 0; bottom: 0;
      left: 0; width: min(280px, 82%); background: var(--bg);
      border-right: 1px solid var(--line2);
      box-shadow: 0 0 24px var(--bubble-shadow); }
    #stock .st-lat[hidden] { display: none; }
    /* LE VOILE FERME AU CLIC, et il existe pour ça seul : sans lui, on
       tape à côté du menu et rien ne se passe — *un menu qu'on ne sait
       pas fermer se referme en quittant l'écran, ce qui est pire.* */
    #stock .st-voile { display: block; position: absolute; inset: 0;
      z-index: 11; background: var(--veil-bg); }
    #stock .st-voile[hidden] { display: none; }
    #stock .st-cadre { position: relative; }
  }
  #stock .st-editable {
    display: inline-flex; align-items: center; justify-content: space-between;
    gap: 6px; min-height: 44px; max-width: 100%; padding: 6px 9px;
    background: var(--panel2); color: var(--text);
    border: 1px solid var(--line2); border-radius: var(--r-fin);
    font: inherit; text-align: inherit; cursor: pointer;
  }
  #stock .st-editable-valeur { min-width: 0; overflow-wrap: anywhere; }
  /* ✎ LE CRAYON (22/09) — il porte l'action à la place du mot
     « Modifier », retiré le même jour parce qu'il coupait les prix en
     deux (le motif complet est dans `stCelluleEditable`).
     ⛔ LE SOULIGNEMENT PART AVEC LE MOT, ET CE N'EST PAS UN OUBLI : il
     signalait un TEXTE cliquable. Souligné, un pictogramme devient un
     dessin barré par un trait.
     ⚠ LA TAILLE NE SUIT PLUS --t-etiquette (11 px, calé sur un mot de
     huit lettres) : *un crayon de 11 px ne se voit plus, et l'indice
     visible était toute la raison d'être de cet élément.*
     ⛔ ELLE PREND --t-lu, ET PAS UN NOMBRE : `test_fenetres_ui` a
     refusé le 15 px du premier jet — *il n'est nulle part dans
     l'échelle (11, 12, 14, 16, 19, 24), et son motif écrit dit qu'une
     valeur hors échelle « n'arrondit pas pareil d'un poste à
     l'autre ».* Le garde a mordu sur le geste du jour, il avait
     raison, et la valeur juste était à un cran. */
  #stock .st-editable-action {
    color: var(--accent); font-size: var(--t-lu); line-height: 1;
    flex-shrink: 0; opacity: .85;
  }
  #stock .st-cellule-edit { min-height: 44px; max-width: 100%; font-size: var(--t-lu); }
  @container nimzou-stock (min-width: 601px) {
    #stock .st-scroll {
      max-height: min(65dvh, 40rem); overflow: auto;
      scrollbar-gutter: stable; scroll-padding-top: 4rem;
    }
    #stock .st-table th {
      position: sticky; top: 0; z-index: 1; background: var(--panel2);
      color: var(--text); box-shadow: 0 1px 0 var(--line);
    }
  }
  /* ⛔⛔ `:not(.st-tab-large)` — EXCLURE AU LIEU DE DÉTRICOTER
     (13/09, décision d'Adam après QUATRE défauts d'alignement dans
     la même journée, tous vus par lui au banc et aucun par un
     test). Ces règles imposent six propriétés aux cellules pour en
     faire des cartes ; un tableau qui veut rester un tableau devait
     les annuler TOUTES, une par une. En oublier une ne lève rien :
     le tableau s'affiche, il est simplement faux — c'est comme ça
     que le `tbody` en table indépendante, les td numériques rabattus
     à gauche et le remplissage à 8px sont passés.
     ⚠ La classe vit sur le CONTENEUR `.st-scroll` (c'est lui que
     `stTable` rend), donc l'exclusion se pose là — et elle ne change
     RIEN pour les autres écrans du stock : ils ne portent pas la
     classe, ils gardent exactement ces règles. */
  @container nimzou-stock (max-width: 600px) {
    #stock .st-scroll:not(.st-tab-large) { max-height: none; overflow: visible; }
    #stock .st-scroll:not(.st-tab-large) .st-table,
    #stock .st-scroll:not(.st-tab-large) .st-table tbody { display: block; width: 100%; }
    #stock .st-scroll:not(.st-tab-large) .st-table thead {
      position: absolute; width: 1px; height: 1px; padding: 0;
      overflow: hidden; clip-path: inset(50%); white-space: nowrap;
    }
    #stock .st-scroll:not(.st-tab-large) .st-table tbody tr:not([hidden]) {
      display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: 12px; padding: 16px 0; border-bottom: 1px solid var(--line);
    }
    #stock .st-scroll:not(.st-tab-large) .st-table td,
    #stock .st-scroll:not(.st-tab-large) .st-table td.num {
      min-width: 0; padding: 0; border: 0; text-align: left; overflow-wrap: anywhere;
    }
    #stock .st-scroll:not(.st-tab-large) .st-table td:first-child { grid-column: 1 / -1; }
    #stock .st-scroll:not(.st-tab-large) .st-table td[data-label]:not([data-label=""])::before {
      content: attr(data-label); display: block; margin-bottom: 5px;
      font-size: var(--t-etiquette); color: var(--muted);
    }
    #stock .st-table td .st-tag { white-space: normal; }
    #stock .st-table td input:not([type="checkbox"]) { max-width: 100%; min-height: 44px; }
    #stock .st-editable { width: 100%; flex-wrap: wrap; }
  }

  /* CATALOGUE — colonne Article (13/09, proposition ChatGPT #1, écran
     par écran). Scopée à .st-tab-large : ce n'est PAS une règle
     générique sur .st-table, les autres écrans du stock n'ont pas tous
     une colonne à figer pendant le défilement horizontal.
     ⛔ CORRIGÉ LE MÊME JOUR : la conversion en cartes que la proposition
     tactile donne à TOUS les .st-table sous 600 px a été appliquée ici
     par erreur — IMPLEMENTATION.md § 2 demandait explicitement de
     GARDER une présentation tabulaire à petite largeur (« ne pas
     transformer systématiquement les lignes en fiches/cartes »), Adam
     l'a vu au banc et ça ne ressemblait à rien. Cette règle prime
     donc, à toute largeur, sur la conversion générique. */
  /* ── LES DONNEES DE DEMONSTRATION (verrou V3, maquette validee par
     Adam le 20/09 : SE2). Deux marques, et elles ne se remplacent pas.
     ⛔ LA PASTILLE PORTE UN MOT, la teinte n'est qu'un RENFORT : *une
     ligne seulement coloree se lit mal sur un telephone en plein
     soleil, et pas du tout par qui distingue mal les couleurs.* Le
     stock se consulte debout dans un entrepot -- c'est le cas d'usage,
     pas un cas limite. ── */
  #stock .st-demo {
    display: inline-block; font-size: var(--t-etiquette); font-weight: 700;
    letter-spacing: .04em; padding: 1px 6px; border-radius: 999px;
    background: var(--essai); color: var(--bg);
    margin-left: 6px; vertical-align: 1px; white-space: nowrap;
  }
  #stock .st-essai-bandeau {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    background: var(--panel2); border: 1px solid var(--essai);
    border-radius: 12px; padding: 10px 12px; margin: 0 0 12px;
    font-size: 14px; line-height: 1.5;
  }
  #stock .st-essai-bandeau .st-essai-txt { flex: 1 1 240px; }
  #stock .st-essai-bandeau b { color: var(--essai); }
  #stock .st-essai-bandeau small { display: block; color: var(--muted); font-size: var(--t-note); }
  /* Cible 44 px : le bandeau se lit et se clique debout, une main prise. */
  #stock .st-essai-bandeau button { min-height: 44px; }
  #stock .st-essai-part { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 2px 16px; }
  #stock .st-essai-part div { display: flex; justify-content: space-between; gap: 10px; padding: 3px 0; }
  #stock .st-essai-part b { font-variant-numeric: tabular-nums; }
  #stock .st-essai-reste { border: 1px solid var(--danger); border-radius: 12px; padding: 10px 12px; margin: 12px 0; }
  #stock .st-essai-reste li { margin: 5px 0; line-height: 1.5; }
  #stock .st-essai-reste .st-essai-note { color: var(--muted); font-size: var(--t-note); margin-top: 8px; }

  #stock .st-article {
    display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  }
  #stock .st-article .st-nom {
    background: none; border: 0; padding: 0; margin: 0; font: inherit;
    font-weight: 600; color: var(--text); text-align: left; cursor: pointer;
    min-height: 44px; display: flex; align-items: center;
  }
  #stock .st-article .st-nom:disabled { color: var(--muted); cursor: default; }
  #stock .st-article .st-nom:hover:not(:disabled) { color: var(--accent); }
  /* Les origines d'un article (Fournisseurs › Par article) : le lien et
     ses quantités sur la même ligne — pas collées en haut de la rangée
     (25/09). */
  #stock .st-origines .st-poste { align-items: center; }
  /* LE CLIENT SE LIT D'ABORD (Adam, 25/09, en recette de la liste des
     commandes clients) : en gras, entre sa référence et sa date. */
  #stock .st-article .st-client { font-weight: var(--p-appuye); }
  /* Le défilement HORIZONTAL et la colonne figée restent actifs à
     TOUTE largeur — le générique réserve `overflow-x: auto` à ≥601px et
     le remplace par `overflow: visible` en dessous (qui tue aussi le
     défilement horizontal) ; le catalogue le veut à 320px aussi, c'est
     un tableau de comparaison, pas une fiche. Le plafond de hauteur
     du desktop (≥601px, règle générique) n'est pas touché ici — il ne
     s'applique déjà pas sous 601px, rien à répéter. */
  #stock .st-tab-large.st-scroll { overflow-x: auto; }
  /* LA CASE ÉDITABLE REMPLIT SA CELLULE (13/09, retour d'Adam : « on
     reconnaît chaque colonne mais ça ne commence pas là où commence la
     colonne »). Ailleurs dans le stock elle est `inline-flex`, donc
     large comme son contenu : dans une colonne de tarifs, « 11,00 »
     et « 6,20 » ne font pas la même largeur et leurs bords partent en
     dents de scie d'une ligne à l'autre. Ici toutes commencent et
     finissent au même endroit — c'est le `width: 100%` de la maquette
     validée. Scopé au catalogue : c'est le seul écran où six colonnes
     éditables s'alignent côte à côte. */
  #stock .st-tab-large .st-editable { display: flex; width: 100%; }
  /* ⛔ LE PLANCHER QUI EMPÊCHE UN PRIX DE SE COUPER EN DEUX (22/09,
     vu par Adam au banc : « quand l'écran est plus petit ce n'est pas
     top » — `11,00` s'affichait « 11,0 » puis « 0 », sur deux
     lignes). La cause n'est pas la coupure : c'est la LARGEUR. Neuf
     colonnes se partagent la place, le navigateur comprime la plus
     souple, et `overflow-wrap: anywhere` — légitime pour un EAN de
     treize chiffres — casse alors n'importe où, y compris au milieu
     d'un nombre. *Un nombre coupé ne lève rien : il s'affiche, et il
     se lit faux.*
     ✅ Sous ce plancher, le tableau DÉFILE au lieu de comprimer —
     `.st-tab-large.st-scroll` porte déjà `overflow-x: auto` à toute
     largeur, posé le 13/09 pour exactement cette raison.
     ⚠ 6 em et pas 5 : châssis mesuré à 39 px (marges 16, bordures 2,
     crayon 15, écart 6), il reste 57 px pour la valeur quand `11,00`
     en chiffres tabulaires en demande 45. *Un plancher calé au bord
     d'un écart se retourne au premier prix à trois chiffres.* */
  #stock .st-tab-large .st-editable { min-width: 6em; }
  /* ⭐ LA COLONNE FIGÉE PREND LA TEINTE DE SA LIGNE (Adam, 30/09 : « ok »,
     reprise n° 22, point 3). Elle partait du fond des cartes, alors que
     les lignes laissent voir le fond de la fenêtre : une case blanche sur
     une ligne beige. Le trait à droite dit seul qu'elle reste en place.
     Deux variables et pas deux règles : un tableau posé DANS une carte
     (`.st-bloc`) garde le fond de la carte, et les spécificités du zèbre
     et du survol, plus bas, ne bougent pas. */
  #stock .st-tab-large { --fige: var(--bg); --fige-zebre: var(--zebre); }
  #stock .st-bloc .st-tab-large { --fige: var(--panel);
    --fige-zebre: var(--zebre-fige); }
  #stock .st-tab-large .st-table td:first-child,
  #stock .st-tab-large .st-table th:first-child {
    position: sticky; left: 0; z-index: 2; background: var(--fige);
    box-shadow: 1px 0 0 var(--line);
  }
  #stock .st-tab-large .st-table th:first-child {
    z-index: 3; background: var(--panel2);
  }
  /* ── UNE LIGNE SUR DEUX PLUS CONTRASTÉE (22/09, Adam au banc).
     `tbody` et pas `table` : l'en-tête a son propre fond, et il est
     COLLANT — le teinter le ferait clignoter au défilement.
     ⚠ LES SPÉCIFICITÉS SONT CHOISIES, PAS SUBIES, et c'est ce qui rend
     l'ordre des quatre règles indifférent : le zèbre (1,2,2) bat le
     fond de table, le zèbre figé (1,4,3) bat la colonne collée
     (1,3,1), et les deux survols (1,3,2) et (1,5,3) battent chacun le
     zèbre qui leur correspond. *Un empilement qui tient par l'ordre du
     fichier se casse au premier déplacement, sans rien signaler.* ── */
  #stock .st-table tbody tr:nth-child(even) { background: var(--zebre); }
  /* ⚠ PAS DE `tbody` DANS LES DEUX RÈGLES DU TABLEAU LARGE, et ce
     n'est ni un oubli ni un contournement : le garde du 13/09
     (`test_stock_ui.js`) REFUSE la forme `.st-tab-large .st-table
     tbody`, quelle que soit la propriété — le tableau large ne doit
     plus porter aucune contre-règle à tenir à jour. Il a mordu, et le
     sélecteur ne servait à RIEN : le `thead` n'a pas de `td`, et son
     unique `tr` est impair. *Un sélecteur inutile tombe ; on n'adapte
     pas un garde quand le code peut s'écrire autrement.* */
  #stock .st-tab-large .st-table tr:nth-child(even) td:first-child {
    background: var(--fige-zebre);
  }
  #stock .st-table tbody tr.lien:hover { background: var(--survol-ligne); }
  #stock .st-tab-large .st-table tr.lien:hover td:first-child {
    background: var(--survol-ligne);
  }
  /* CE QUI RESTE PROPRE AU TABLEAU LARGE en petite largeur, et
     RIEN D'AUTRE : des colonnes assez larges pour rester lisibles
     pendant le défilement, et une première colonne resserrée pour
     ne pas manger l'écran. Les sept contre-règles qui vivaient ici
     ont disparu avec leur motif : la conversion en cartes ne les
     atteint plus. *Une annulation ligne à ligne se périme au
     premier ajout dans ce qu'elle annule ; une exclusion, non.* */
  @container nimzou-stock (max-width: 600px) {
    #stock .st-tab-large .st-table { width: max-content; min-width: 100%; }
    #stock .st-tab-large .st-table td { min-width: 120px; }
    #stock .st-tab-large .st-table td:first-child,
    #stock .st-tab-large .st-table th:first-child {
      min-width: 130px; max-width: 150px;
    }
  }

  /* ── LA LIGNE RESSERRÉE QUAND ON PILOTE À LA SOURIS (22/09, demande
     d'Adam au banc : « la hauteur des lignes peut être plus réduite et
     optimisée »).
     ⛔ LA CIBLE DE 44 px N'EST PAS ABANDONNÉE : ELLE EST RENDUE À SON
     MOTIF, qui est écrit trois fois dans ce fichier — *« on saisit
     debout, un carton dans la main »*, *« sinon on tape à côté »*.
     Ce motif nomme LE DOIGT, jamais la souris. `pointer: fine` dit que
     le pointeur principal est précis ; sous un doigt, rien ne bouge et
     les 44 px restent entiers. *On ne remplace pas un critère
     constatable par un jugement : on le remplace par un AUTRE critère
     constatable, plus proche du motif.*
     ⚠ CE QUI LE PÉRIMERAIT : un appareil tactile dont le navigateur
     déclare un pointeur fin — un convertible en mode tablette le fait
     selon les cas. Le jour où l'on tape à côté sur un écran tactile,
     la condition se resserre au lieu de disparaître.
     ⚠ MESURÉ AVANT, SUR LE CATALOGUE : 77 px par ligne (12 de marges,
     18 de référence, 3 d'écart, 44 de nom). Après : 50. *Le chiffre
     se remesure, il ne se relit pas — il dépend de la taille du
     texte, qu'un thème peut changer.* ── */
  @media (pointer: fine) {
    #stock .st-editable { min-height: 32px; padding: 3px 8px; }
    #stock .st-cellule-edit { min-height: 32px; }
    /* ⛔ LA DENSITÉ SE POSE SUR `.st-table`, JAMAIS SUR
       `.st-tab-large .st-table td` — et ce n'est pas un détail de
       style : le garde du 13/09 (`test_stock_ui.js`) REFUSE toute
       règle `padding` sur cette seconde forme, parce que le tableau
       large ne doit plus porter AUCUNE contre-règle à tenir à jour.
       *Un garde ne distingue pas l'intention, il distingue la forme —
       et on n'adapte pas un garde quand on peut écrire le code
       autrement.* Il a mordu, il avait raison.
       ✅ Le tableau large HÉRITE d'ici, et la conversion en cartes
       continue de gagner sous 600 px : (1,3,1) contre (1,1,1). */
    #stock .st-table td { padding: 3px 7px; }
    #stock .st-article { gap: 0; }
    #stock .st-article .st-nom { min-height: 26px; }
  }

  /* LE SCANNER (31/08) — surcouche plein écran au-dessus du stock.
     z-index 47 : au-dessus de #gel (46), le plus haut du produit. La
     caméra doit couvrir TOUT, y compris un écran ouvert derrière — et
     elle se ferme d'elle-même, donc elle ne masque jamais durablement.

     ⚠ PREMIÈRE VERSION : fond #000, texte #fff, rayon 10, taille 15.
     `test_fenetres_ui` a nommé les quatre, et il avait raison sur toute
     la ligne. En cherchant le motif à écrire pour l'exception, il n'y en
     avait aucun : *le noir d'un scanner est une habitude d'application
     photo, pas un besoin* — la vidéo se voit aussi bien sur le fond du
     thème, et un blanc en dur serait devenu illisible le jour d'un thème
     clair. Zéro exception demandée au registre, et le résultat suit les
     quatre thèmes au lieu d'en imposer un cinquième.
     *Le contrôle ne m'a pas fait perdre du temps : il m'a fait trouver
     que je n'avais pas de raison.* */
  .st-scan {
    position: fixed; inset: 0; z-index: 47; background: var(--bg);
    display: flex; flex-direction: column;
    align-items: center; justify-content: center; gap: 12px;
    padding: max(12px, var(--safe-b, 0px));
  }
  /* ⛔ LA CAMÉRA RESTE OUVERTE DEPUIS LE 32-C (décision d'Adam du
     17/09 : « on continue, l'arrêt doit être manuel ») : elle empile ce
     qu'elle a lu, donc la vidéo cède de la hauteur à la liste.
     *68vh convenait à un écran qui n'affichait rien d'autre.* */
  .st-scan video {
    max-width: 100%; max-height: 44vh;
    border-radius: 12px; object-fit: contain;
  }
  /* CE QUI A ÉTÉ LU — le dernier EN HAUT : c'est celui qu'on vient de
     viser, et le seul qu'on vérifie sans lâcher le carton. Elle défile :
     une liste qui pousserait le bouton « Terminer » hors de l'écran
     enlèverait la seule sortie que la décision du 17/09 laisse. */
  .st-scan-lues { width: min(92vw, 420px); max-height: 26vh;
    overflow-y: auto; -webkit-overflow-scrolling: touch; }
  .st-scan-lues .l { display: flex; align-items: center; gap: 8px;
    padding: 6px 8px; border-top: 1px solid var(--line); }
  .st-scan-lues .l:first-child { border-top: 0; }
  .st-scan-lues .n { flex: 1; min-width: 0; font-size: var(--t-corps);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .st-scan-lues .x { flex: none; font-weight: var(--p-appuye);
    font-variant-numeric: tabular-nums; color: var(--accent); }
  /* La cible : elle ne détecte rien, elle DIT où viser. Sans repère on
     promène le téléphone au hasard et on croit que le scan est cassé. */
  .st-scan-cible {
    position: absolute; width: min(74vw, 320px); height: 132px;
    border: 2px solid var(--accent); border-radius: 12px;
    pointer-events: none;
  }
  .st-scan-info {
    color: var(--text); margin: 0; text-align: center;
    font-size: 14px; max-width: 90vw;
  }
  .st-scan-barre { display: flex; gap: 8px; }
