@import url('https://fonts.googleapis.com/css2?family=DM+Sans:opsz,wght@9..40,400;9..40,500;9..40,600;9..40,700;9..40,800;9..40,900&display=swap');

/* ============================================================
   NOCIKA — Design System v2.0
   Police  : DM Sans
   Périmètre : Hub + module Devis (liste + édition)

   Guide de lecture
   - Les classes `nk-*` sont les briques "modernes" partagées entre modules.
   - Les classes sans préfixe ou avec préfixes courts (`sb-`, `ud-`, `rcard`, ...)
     sont surtout des couches legacy Hub encore en migration.
   - Les sections sont ordonnées du plus transversal (tokens, primitives) au plus
     spécifique (clients, hub, compatibilité).
   - Quand une classe est pensée comme variante, elle complète une base au lieu
     de la remplacer (`.nk-btn` + `.nk-btn--primary`, `.nk-kpi` + `.nk-kpi--cyan`).
   ============================================================ */

/* ── TOKENS ───────────────────────────────────────────────────
   Source unique des couleurs, rayons, ombres, z-index et timings.
   Toute nouvelle primitive doit d'abord consommer ces variables avant
   d'introduire une nouvelle valeur "en dur".
*/
:root {

  /* Typographie de référence de tout le back-office. */
  --font: 'DM Sans', system-ui, -apple-system, sans-serif;

  /* Palette chaude principale : CTA et accents de premier niveau.
     Brick charte Nocika (2026-05-27 — migration lot 1). */
  --orange:    #FF6B35;
  --orange-h:  #c95214;
  --orange-l:  #fff3e8;
  --orange-m:  #f5c094;

  /* Palette froide secondaire : états interactifs, focus, liens métier.
     Cyan charte Nocika (2026-05-27 — migration lot 1).
     - `--cyan` (#00B4D8) : surfaces, fonds, bordures, indicateurs.
     - `--cyan-d` (#007A99) : TEXTE cyan sur fond clair (ratio AA confortable
       sur fond blanc et sur `--cyan-l`). Marge de sécurité par rapport au
       seuil WCAG — strict minimum aurait été `#0096BD`. */
  --cyan:      #00B4D8;
  --cyan-h:    #0c8b94;
  --cyan-l:    #e0f7f9;
  --cyan-d:    #007A99;

  /* Couleurs sémantiques utilisées par badges, KPI et feedbacks. */
  --success:   #16a34a;
  --success-l: #f0fdf4;
  --success-b: #bbf7d0;

  --danger:    #dc2626;
  --danger-l:  #fef2f2;
  --danger-b:  #fecaca;

  --warning:   #d97706;
  --warning-l: #fffbeb;
  --warning-b: #fde68a;

  /* Bleu info — réservé aux bandeaux neutres « note / simulation /
     rappel ». À ne pas confondre avec --cyan (états interactifs) ni
     --ia (violet, génération IA). */
  --info:      #1e40af;
  --info-l:    #eff6ff;
  --info-b:    #bfdbfe;

  /* Or « catégorie principale / spéciale » — folder-star, médaille.
     Usage strictement décoratif (icône), jamais en surface large. */
  --gold:      #c8881e;
  --gold-l:    #fef7e7;
  /* Nature d'un nœud d'arbre (maquette Lionel, 2026-08-10). Deux tons qui
     n'existaient pas : `--orange` (#FF6B35) est trop saturé pour un trait fin
     et `--muted` (#475569) trop foncé pour un état « effacé ». Ils sont nommés
     par leur SENS et non par leur teinte, pour qu'un changement de palette ne
     laisse pas un « gris-bleu » qui ne serait plus gris.
     Le noir des dossiers, lui, EST déjà `--ink` (#0a1f2b) — aucun token à
     créer, la maquette tombait pile dessus. */
  --nature-file:   #f4923b;
  --nature-hidden: #b3bccb;

  /* Bleu lien Google — utilisé pour rendre les URL canoniques façon
     SERP. Ne PAS étendre à d'autres usages (texte/CTA). */
  --link-google: #1a0dab;

  --ia:        #7c3aed;
  --ia-l:      #ede9fe;
  /* Traduction — couleur identité (pendant du violet IA) : le globe et tout
     marqueur « champ traduisible ». Bleu = langue/traduction, distinct du violet. */
  --trad:      #2563eb;
  --trad-l:    #eef4ff;
  --trad-b:    #bfdbfe;

  /* FRAMBOISE — ACTION de traduction assistée par IA (décision Lionel,
     2026-08-22). À distinguer des deux voisines, et c'est tout l'intérêt :
       --ia    (violet) = GÉNÉRER du contenu depuis rien ;
       --trad  (bleu)   = MARQUER un champ comme traduisible (mémo, non cliquable) ;
       --trad-ia (framboise) = DÉCLENCHER une traduction (le modèle traduit un
                               contenu existant).
     Les trois gestes coûtaient jusqu'ici la même couleur violette, ce qui
     rendait « générer » et « traduire » indiscernables sur une même carte.
     Fond PLEIN pour l'action, fond CLAIR + contour pour l'état « à traduire ».
     Déployé d'abord sur la page des blocs éditoriaux (Rédaction) uniquement. */
  --trad-ia:   #be185d;
  --trad-ia-l: #fdf2f8;
  --trad-ia-b: #fbcfe8;
  --trad-ia-d: #9d174d;
  --ia-d:      #4c1d95;
  --ia-b:      #ddd6fe;

  /* Identité couleur par LANGUE (drapeaux) — référence unique pour les pills du
     sélecteur de langue et les tags inline de label. `bg` = fond pill active,
     `text` = texte, `mid` = filet/bordure. Aligné sur PS9_SITES[nocika] (fr/it/de/en). */
  --lang-fr-bg: #EEF3FB; --lang-fr-text: #002395; --lang-fr-mid: #002395;
  --lang-de-bg: #FFF8E1; --lang-de-text: #8B6000; --lang-de-mid: #FFCE00;
  --lang-it-bg: #E8F5E9; --lang-it-text: #1B5E20; --lang-it-mid: #009246;
  --lang-en-bg: #FDECEA; --lang-en-text: #8B0000; --lang-en-mid: #C8102E;
  --lang-es-bg: #FFF1E8; --lang-es-text: #9A3412; --lang-es-mid: #EA580C;

  --manual:    #185FA5;
  --manual-l:  #E6F1FB;
  --manual-d:  #0C447C;

  --ps:        #0F6E56;
  --ps-l:      #E1F5EE;
  --ps-d:      #085041;

  /* Deep Blue charte Nocika (2026-05-27 — migration lot 1, ajusté au
     pixel sur DevTools) : `--navy` (#0a1527) est utilisé comme couleur
     de texte par défaut ET comme fond chrome (topbar + tb-logo +
     sidebar). Un seul token pour tout le Hub — pas de `--deep-blue`
     distinct. */
  --navy:      #0a1527;
  /* Couleur de texte unique des blocs / formulaires (restructuration typo
     2026-06-13, validée en showroom) : quasi-noir bleuté appliqué aux
     entêtes de bloc, labels, champs, cases, texte courant et infos. */
  --ink:       #0a1f2b;
  /* Élévation chrome (2026-05-27 — migration lot 1) — état hover et
     actif des items de la sidebar Hub. Nuance plus claire que `--navy`
     mais toujours sombre, pour que le liseré cyan du state actif
     ressorte. Pas de variante en dur, on consomme ce token partout
     dans le chrome. */
  --navy-elevated: #16294a;
  --slate:     #64748b;
  --muted:     #475569;
  --border:    #e2e8f0;
  --border-d:  #cbd5e1;
  /* Bordure marquée (ajout 2026-05-11) — cran intermédiaire entre
     --border-d et --slate, pour les séparateurs structurants
     (sidebar PIM, future propagation Hub). */
  --border-strong: #a8b5c5;
  --surface:   #f8fafc;
  --bg:        #f1f5f9;
  --white:     #ffffff;

  /* Écume charte Nocika (2026-05-27 — migration lot 1) — base des
     fonds clairs froids. Remplace la palette `--sand-*` retirée
     dans ce même lot.
     - `--ecume-bg` (#F4F9FD) : fond container principal très clair.
     - `--ecume`    (#E8F4FD) : surface secondaire / hover. */
  --ecume-bg:  #F4F9FD;
  --ecume:     #E8F4FD;
  /* État « ouvert » d'un header de groupe accordéon (sidebar
     contextuelle gauche) — bleuté neutre, sémantique « déplié »
     distincte de l'état actif (qui reste cyan). */
  --surface-open: #DCEEFB;
  /* Fonds dédiés aux tableaux (2026-05-27 — migration lot 1).
     Légèrement plus saturés que `--ecume-bg`/`--ecume` pour rester
     perceptibles quand le tableau est posé dans une `.nk-table-wrap`
     blanche sur fond écume. */
  --table-zebra: #F7FAFD;  /* lignes paires */
  --table-hover: #EEF5FB;  /* survol de ligne */

  /* Alias de compatibilité pour les anciens modules Hub.
     Ne pas réutiliser sur du nouveau code : préférer les tokens "canoniques"
     ci-dessus afin d'éviter d'étendre la dette de migration.
  */
  --nv:        var(--navy);
  --nv2:       #1a3a60;
  --cy:        var(--cyan);
  --cy-lt:     var(--cyan-l);
  --or:        var(--orange);
  --or-lt:     var(--orange-l);
  --text:      var(--navy);
  --muted2:    var(--muted);
  --border2:   var(--border-d);

  /* Rayons communs, du chip compact au conteneur principal. */
  --r-sm:  5px;
  --r-md:  8px;
  --r-lg:  11px;
  --r-xl:  14px;

  /* Ombres par profondeur visuelle croissante. */
  --shadow-sm: 0 1px 3px rgba(0,0,0,.06);
  --shadow-md: 0 4px 16px rgba(0,0,0,.08);
  --shadow-lg: 0 8px 32px rgba(0,0,0,.12);

  /* Anneau de focus standard applique sur inputs/selects. */
  --focus-ring: 0 0 0 3px rgba(14,165,176,.2);

  /* Durées d'animation limitees aux interactions UI courtes. */
  --t-fast: .14s ease;
  --t-std:  .18s ease;

  /* Ordre de superposition global — échelle canonique (refonte 2026-05-23).
     Règle d'or : aucun élément ne doit dépasser --z-toast (1000). Toute
     valeur de z-index inline trouvée hors de cette échelle = bug. La
     scale est resserrée (max 1000) pour éliminer la course aux valeurs
     extrêmes (9999/99999) qui rendait les empilements imprévisibles.
     - --z-bar      : barres horizontales internes (context bar, etc.)
     - --z-topbar   : topbar Hub (au-dessus du contenu, sous tout le reste)
     - --z-drawer   : tiroirs latéraux (drawer décli, ud)
     - --z-overlay  : voile semi-transparent au-dessus du drawer mais sous
                      les modales (cf. nk-ia-overlay overlay IA)
     - --z-modal    : modales standard ET critiques (cf. NkConfirmModal).
                      Pour deux modales superposées, le DOM order suffit
                      (le 2e overlay ajouté passe naturellement au-dessus).
     - --z-tooltip  : info-bulles (toujours au-dessus des modales pour
                      rester lisible quand on survole un champ d'une modale)
     - --z-toast    : toasts globaux (overlays toasts ont pointer-events:none,
                      donc ne bloquent pas les modales en dessous). */
  --z-bar:     100;
  --z-topbar:  200;
  --z-drawer:  500;
  --z-overlay: 600;
  --z-modal:   700;
  --z-tooltip: 900;
  --z-toast:   1000;

  /* Échelle d'espacement (ajout 2026-05-23, chantier C). Source unique
     pour paddings / margins / gaps. Voir docs/UX.md § Tokens d'espacement
     et typographie. */
  --space-xs:  2px;
  --space-sm:  4px;
  --space-md:  8px;
  --space-lg:  12px;
  --space-xl:  16px;
  --space-2xl: 24px;

  /* Échelle typographique (ajout 2026-05-23, chantier C). Évite la
     prolifération de font-size hardcodés. */
  --fs-xs:   9px;
  --fs-sm:   11px;
  --fs-base: 13px;
  --fs-md:   14px;
  --fs-lg:   18px;
  --fs-xl:   22px;

  /* Poids de police (ajout 2026-05-23, chantier C). */
  --fw-normal:   400;
  --fw-medium:   500;
  --fw-semibold: 600;
  --fw-bold:     700;

  /* Rayons supplémentaires (ajout 2026-05-23, chantier C).
     Complète --r-sm/md/lg/xl existants (5/8/11/14 px). */
  --r-xs:     2px;
  --r-pill:   20px;
  --r-circle: 50%;

  /* Gris intermédiaire (ajout 2026-05-23, chantier C). Couvre les
     ~178 occurrences restantes de #94a3b8 dans frontend/ après la
     suppression du legacy audit. À migrer progressivement (chantier D). */
  --slate-light: #94a3b8;

  /* Dimensions du shell Hub : sidebar large/compacte + topbar. */
  --sb:    265px;
  --sb-sm: 62px;
  --top:   62px;
}

/* ── RESET ─────────────────────────────────────────────────────
   Reset minimaliste : on normalise les box models et on retire les
   contrôles natifs qui gênent les composants numériques maison.
*/
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
input[type=number] { -moz-appearance: textfield; }

/* ── BASE ──────────────────────────────────────────────────────
   Le shell occupe toute la hauteur du viewport et délègue le scroll
   aux zones internes (`.nk-list__content`, sidebars, drawers...).
*/
html { height: 100vh; overflow: hidden; }

body {
  font-family: var(--font);
  /* Taille de police PAR DÉFAUT du hub relevée 14px → 15px le 2026-06-09
     (lisibilité). Tout texte qui n'impose pas sa propre taille en hérite.
     Les composants encore en taille explicite plus petite sont remontés
     progressivement vers cette base (cf. docs/UX.md § Tokens). */
  font-size: 15px;
  line-height: 1.5;
  color: var(--navy);
  background: var(--bg);
  height: 100%;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  -webkit-font-smoothing: antialiased;
}

input, select, textarea, button { font-family: var(--font); font-size: inherit; }
a { color: inherit; text-decoration: none; }
input:focus::placeholder, textarea:focus::placeholder { color: transparent; }

/* ============================================================
   BOUTONS
   Primitive de base pour toutes les actions.
   Les variantes ne doivent modifier que la sémantique visuelle
   (couleur, fond, bord), jamais la structure du bouton.
   ============================================================ */
.nk-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px;
  border: 1.5px solid transparent;
  border-radius: var(--r-md);
  font-family: var(--font);
  font-size: 14px; /* 12 → 14 le 2026-06-13 (libellé de bouton remonté sur la nouvelle base) */
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
  line-height: 1;
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast);
}
.nk-btn:disabled { opacity: .5; cursor: not-allowed; }
.nk-btn svg { width: 13px; height: 13px; flex-shrink: 0; }

.nk-btn--primary { background: var(--orange); color: var(--white); border-color: var(--orange); }
.nk-btn--primary:not(:disabled):hover { background: var(--orange-h); border-color: var(--orange-h); }

.nk-btn--success { background: var(--success); color: var(--white); }
.nk-btn--success:not(:disabled):hover { background: #15803d; }

.nk-btn--danger { background: var(--danger); color: var(--white); }
.nk-btn--danger:not(:disabled):hover { background: #b91c1c; }

.nk-btn--outline { background: var(--white); color: var(--slate); border-color: var(--border); }
.nk-btn--outline:not(:disabled):hover { border-color: var(--border-d); color: var(--navy); }
/* Variante outline DANGER (bordure + texte rouges, fond blanc) — même esprit
   que `.nk-btn--outline.nk-btn--ia` : s'utilise en `nk-btn nk-btn--outline
   nk-btn--danger`. Nécessaire car l'ordre source fait gagner `.nk-btn--outline`
   sur `.nk-btn--danger` seul (bouton « Retirer » de la black-list vidéo). */
.nk-btn--outline.nk-btn--danger { border-color: var(--danger); color: var(--danger); background: var(--white); }
.nk-btn--outline.nk-btn--danger:not(:disabled):hover { border-color: var(--danger); background: var(--danger-l); color: var(--danger); }

.nk-btn--ghost { background: transparent; color: var(--navy); }
.nk-btn--ghost:not(:disabled):hover { background: rgba(15,23,42,.06); }

.nk-btn--pdf { background: #b45309; color: var(--white); }
.nk-btn--pdf:not(:disabled):hover { background: #92400e; }

/* Bouton d'action IA — code couleur violet hub-wide (cf. CLAUDE.md
   § Génération IA). S'utilise en complément de --outline :
   `nk-btn nk-btn--outline nk-btn--ia`. Extrait des styles inline des
   modules (CMS, PIM SEO) le 2026-06-12. */
.nk-btn--outline.nk-btn--ia { border-color: var(--ia); color: var(--ia); }
.nk-btn--outline.nk-btn--ia:not(:disabled):hover { border-color: var(--ia); background: var(--ia-l); color: var(--ia); }
/* Variante PLEINE (fond violet) — actions IA primaires (« Générer la fiche »,
   « Tout générer »…). Classe distincte de `.nk-btn--ia` : ce dernier n'existe
   qu'en combinaison avec --outline, un `.nk-btn--ia` seul écraserait le fond
   des boutons outline existants (extrait des styles inline PIM, audit
   2026-07-12). */
.nk-btn--ia-solid { background: var(--ia); color: var(--white); }
.nk-btn--ia-solid:not(:disabled):hover { background: #6d28d9; }

/* ── TRADUCTION assistée par IA — framboise (2026-08-22) ──────────────────
   Même paire que le violet IA, et pour la même raison : une ACTION porte un
   fond plein, un ÉTAT porte un fond clair et un contour. Classes DISTINCTES de
   `--ia-solid` / `--ia` : « Traduire ce bloc » et « Préparer + Générer les
   blocs » cohabitent sur la même carte, et partageaient la même couleur — on
   ne pouvait pas les distinguer d'un coup d'œil. */
/* Framboise CLAIR au repos, FONCÉ au survol (demande Lionel du 2026-08-23).
   Le fond plein sombre au repos pesait autant qu'un CTA de publication, alors
   que traduire un bloc est un geste courant et réversible. Le contour et le
   texte restent en framboise pleine : l'action se lit toujours comme une
   traduction, elle ne crie plus. Au survol, le plein sombre revient — c'est
   alors le geste qu'on s'apprête à faire. */
.nk-btn--trad-solid {
  background: var(--trad-ia-l);
  border-color: var(--trad-ia);
  color: var(--trad-ia);
}
.nk-btn--trad-solid:not(:disabled):hover,
.nk-btn--trad-solid:not(:disabled):focus-visible {
  background: var(--trad-ia-d);
  border-color: var(--trad-ia-d);
  color: var(--white);
}
/* Variante CONTOUR — action de traduction SECONDAIRE, celle qu'on propose sans
   l'imposer. Écrite puis retirée le 2026-08-22 faute d'appelant (une classe
   documentée mais jamais émise est du code mort) ; REPOSÉE le 2026-08-23 avec
   son appelant : le bouton « Traduire / Générer » de `NkTradGrid`, qui est un
   contour et non un fond plein.
   L'état « à traduire », lui, se rend toujours par `.nk-badge--trad-ia`. */
.nk-btn--outline.nk-btn--trad-ia { border-color: var(--trad-ia); color: var(--trad-ia); }
.nk-btn--outline.nk-btn--trad-ia:not(:disabled):hover { border-color: var(--trad-ia); background: var(--trad-ia-l); color: var(--trad-ia); }

.nk-btn--sm { padding: 5px 10px; font-size: 13px; } /* 11 → 13 le 2026-06-13 */
.nk-btn--sm svg { width: 12px; height: 12px; }

.nk-btn--icon {
  /* `.nk-btn--icon` est appliqué sur des éléments hétérogènes (<button>,
     <a>, <span>) qui ont chacun leur display/baseline par défaut. Forcer
     display:inline-flex + align-items garantit l'alignement parfait des
     icônes 14×14 dans la zone Actions sans dépendre de la classe parente
     `.nk-btn`. line-height:1 + vertical-align:middle neutralisent les
     dérives de baseline texte (cas du texte PIM dans un bouton icône). */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px; height: 30px;
  padding: 0;
  border: 1.5px solid var(--border);
  background: var(--white);
  color: var(--slate);
  border-radius: var(--r-md);
  line-height: 1;
  vertical-align: middle;
  box-sizing: border-box;
  font-family: var(--font);
  font-size: 11px;
  cursor: pointer;
  text-decoration: none;
}
.nk-btn--icon:hover           { border-color: var(--border-d); background: var(--bg); }
.nk-btn--icon-edit:hover      { border-color: var(--cyan);    color: var(--cyan);    background: var(--cyan-l); }
.nk-btn--icon-clone:hover     { border-color: var(--warning); color: var(--warning); background: var(--warning-l); }
.nk-btn--icon-del:hover       { border-color: var(--danger);  color: var(--danger);  background: var(--danger-l); }
.nk-btn--icon-ia:hover        { border-color: var(--ia); color: var(--ia); background: var(--ia-l); }
/* Bouton-icône d'une ACTION de traduction (retraduire une langue) : framboise
   AU REPOS, pas seulement au survol — une action qui ne se signale qu'au survol
   ne se voit pas. C'est ce que faisait déjà `.nk-tradg__regen` avec le violet ;
   cette classe-ci reprend le principe avec la couleur de la traduction. */
/* `flex: 0 0 auto` REPRIS de `.nk-tradg__regen`, la classe que celle-ci
   remplace sur la page des blocs : son conteneur `.nk-pb-lang__act` est en
   `display:flex`, et sans cette ligne le bouton se comprimait sur une carte
   étroite. Une garantie qu'un simple changement de classe aurait perdue en
   silence — relevé en revue indépendante le 2026-08-22. */
.nk-btn--icon-trad            { flex: 0 0 auto; color: var(--trad-ia); }
.nk-btn--icon-trad:hover      { border-color: var(--trad-ia); color: var(--trad-ia); background: var(--trad-ia-l); }

/* ── ZONE À HAUTEUR CONSTANTE ─────────────────────────────────────────────
   Le contenu change (grille d'images / zone de dépôt / aperçu) mais la fenêtre
   NE DOIT PAS se redimensionner : une modale qui grandit et rapetisse à chaque
   bascule fait sauter les boutons du pied sous le curseur (demande Lionel,
   2026-08-17). La hauteur est donc RÉSERVÉE, et c'est la grille qui défile.
   `min-height` et non `height` : un contenu plus grand reste lisible. */
.nk-zone-fixe {
  min-height: 490px;
  display: flex; flex-direction: column;
  /* Respire au-dessus (les boutons de source) et entre ses propres blocs
     (aperçu puis champ alt) : sans ces écarts, l'image se colle au bouton et
     le champ se colle à l'image. */
  gap: var(--space-lg);
  margin-top: var(--space-md);
}

/* Séparateur d'une barre d'outils (recherche + filtre) d'avec ce qu'elle
   filtre. Le trait fait le travail qu'un espace seul ne fait pas : dire que
   ce qui est au-dessus PILOTE ce qui est en dessous. */
.nk-barre-outils {
  display: flex; align-items: center; gap: var(--space-md);
  padding-bottom: var(--space-md); margin-bottom: var(--space-md);
  border-bottom: 1px solid var(--border);
}
.nk-barre-outils > input[type="search"] { flex: 1; min-width: 0; }
.nk-barre-outils > select { width: auto; flex: 0 0 auto; }

/* Avertissement posé à GAUCHE des boutons d'un pied de modale. Il occupe sa
   place en permanence et n'apparaît que par `is-on` : le masquer par `hidden`
   ferait sauter les boutons latéralement à la première frappe. */
.nk-avert-pied {
  flex: 1; text-align: left; visibility: hidden;
  /* `--fs-base` (13px) et non `--fs-sm` : cet avertissement est du texte à LIRE
     avant de cliquer, pas une méta. C'est aussi le plancher que le Hub impose
     dans une modale (cf. CLAUDE.md § Modales). */
  font-size: var(--fs-base); color: var(--warning); line-height: 1.3;
}
.nk-avert-pied.is-on { visibility: visible; }

/* ── COUPLE LIBELLÉ + CONTRÔLE, ET PILE DE CHAMPS ─────────────────────────
   `.nk-field` n'est qu'un utilitaire de FLEX (`flex:1`) : employé comme bloc de
   formulaire il n'empile rien et n'espace rien — d'où des libellés collés à
   leur champ et des champs collés entre eux (constaté le 2026-08-17). Ces deux
   classes-ci font le travail attendu, avec l'échelle d'espacement du Hub. */
.nk-form-champ { display: flex; flex-direction: column; gap: var(--space-sm); }
.nk-form-pile  { display: flex; flex-direction: column; gap: var(--space-lg); }

/* ── LOUPE DE VIGNETTE ────────────────────────────────────────────────────
   Survoler une vignette de 60px ne dit pas ce qu'elle montre : sur une grille
   d'icônes de 24 items, l'infobulle textuelle ne remplace pas le fait de VOIR
   (demande Lionel, 2026-08-17). Le panneau s'affiche à côté de la vignette
   survolée, dimensionné sur l'image elle-même et plafonné pour ne jamais
   déborder de l'écran.

   `position: fixed` : la grille défile et le panneau doit s'en affranchir —
   en `absolute` il serait rogné par le conteneur défilant. Coordonnées posées
   en JS depuis la position réelle de la vignette.

   `pointer-events: none` : le panneau ne doit jamais intercepter le survol,
   sinon il se place sous le curseur, le `mouseleave` part et il clignote. */
.nk-media-zoom {
  position: fixed; z-index: var(--z-tooltip);
  display: flex; flex-direction: column; gap: var(--space-sm);
  padding: var(--space-md);
  background: var(--white); border: 1px solid var(--border-d);
  border-radius: var(--r-md); box-shadow: var(--shadow-lg);
  pointer-events: none;
}
.nk-media-zoom img {
  display: block; max-width: 320px; max-height: 320px;
  object-fit: contain; background: var(--surface); border-radius: var(--r-sm);
}
.nk-media-zoom__alt {
  max-width: 320px; font-size: var(--fs-sm); color: var(--muted);
  line-height: 1.35; word-break: break-word;
}

/* ── CHOIX DE SOURCE EN DEUX GRANDES TUILES ───────────────────────────────
   `.nk-segment` est une pastille compacte, faite pour basculer un affichage au
   sein d'une barre. Ici le choix est le PREMIER geste de la fenêtre et engage
   tout ce qui suit : il lui faut la surface d'un bouton, pas d'un onglet
   (demande Lionel, 2026-08-17). Deux tuiles de largeur égale, l'active en cyan.
   Réutilisable par toute fenêtre qui ouvre sur un « par où commence-t-on ». */
.nk-source-choix { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-md); }
.nk-source-choix__btn {
  /* Icône ET texte sur UNE ligne : la tuile empilée montait à ~90px et
     repoussait la grille d'images hors de vue (demande Lionel, 2026-08-17). */
  display: flex; flex-direction: row; align-items: center; justify-content: center;
  gap: var(--space-md);
  padding: var(--space-lg) var(--space-md);
  border: 1.5px solid var(--border-d); border-radius: var(--r-lg);
  background: var(--white); color: var(--muted);
  font-family: var(--font); font-size: var(--fs-md); font-weight: var(--fw-semibold);
  cursor: pointer; text-align: center;
  transition: border-color var(--t-fast), background var(--t-fast), color var(--t-fast);
}
.nk-source-choix__btn svg { width: 20px; height: 20px; flex: 0 0 auto; }
/* Le glyphe est décoratif : le libellé dit déjà tout. `display:flex` sur son
   porteur évite l'espace fantôme sous un SVG en ligne. */
.nk-source-choix__btn > span:first-child { display: flex; }
.nk-source-choix__btn:hover { border-color: var(--cyan); color: var(--cyan-d); }
/* Source indisponible : elle reste LISIBLE pour dire qu'elle existe, mais ne
   se propose plus. Le curseur barré et l'absence de réaction au survol le
   disent avant le clic. */
.nk-source-choix__btn:disabled {
  opacity: .5; cursor: not-allowed;
  background: var(--surface); color: var(--muted); border-color: var(--border);
}
.nk-source-choix__btn:disabled:hover { border-color: var(--border); color: var(--muted); }
.nk-source-choix__btn--active {
  border-color: var(--cyan); background: var(--cyan-l); color: var(--cyan-d);
}

/* ── ZONE DE DÉPÔT DE FICHIER ─────────────────────────────────────────────
   Reprise À L'IDENTIQUE du visuel de la fiche PIM (`#nk-pim-imgen-drop`), qui
   vivait en styles inline dans un seul écran : pointillé cyan sur fond clair,
   « + » en gros, titre puis précisions. Sorti en classes le 2026-08-17 pour la
   fenêtre Image des guides — deuxième usage, donc plus une variante locale.
   `.is-over` colore pendant le survol d'un fichier glissé : sans retour, on ne
   sait pas si la zone accepte le dépôt. La fiche PIM, elle, garde ses styles
   inline pour l'instant — la migrer est un lot à part, pas un effet de bord. */
.nk-dropzone {
  display: flex; flex-direction: column; justify-content: center; align-items: center;
  gap: var(--space-md);
  border: 2px dashed var(--cyan); border-radius: var(--r-lg);
  padding: var(--space-xl); background: var(--cyan-l);
  cursor: pointer; text-align: center;
  transition: background var(--t-fast), border-color var(--t-fast);
}
.nk-dropzone.is-over { background: var(--ecume); border-color: var(--cyan-d); }
.nk-dropzone__plus  { font-size: 34px; line-height: 1; font-weight: 300; color: var(--cyan); }
.nk-dropzone__title { font-size: var(--fs-md); font-weight: var(--fw-semibold); color: var(--cyan-d); }
.nk-dropzone__hint  { font-size: var(--fs-sm); color: var(--muted); line-height: 1.35; }

/* ── APERÇU D'UNE IMAGE RETENUE ───────────────────────────────────────────
   Vignette à gauche, métadonnées et actions à droite. `object-fit: contain` et
   non `cover` : on montre l'image ENTIÈRE, quitte à laisser du vide — une tuile
   rognée dans l'aperçu ferait valider un cadrage qui n'est pas celui du site. */
.nk-media-preview { display: flex; gap: var(--space-md); align-items: flex-start; }
.nk-media-preview__thumb {
  width: 132px; height: 92px; flex: 0 0 auto;
  border: 1px solid var(--border-d); border-radius: var(--r-sm);
  background: var(--surface); object-fit: contain;
}
.nk-media-preview__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--space-sm); }

/* ── ÉTAT PORTÉ PAR UN BOUTON ICÔNE (et non un survol) ────────────────────
   Les variantes ci-dessus ne colorent qu'au SURVOL : elles annoncent ce que
   fera le clic. Celles-ci colorent en PERMANENCE, parce qu'elles disent l'état
   de la chose — « il manque quelque chose » / « c'est en place ». Premier
   usage : l'icône Image d'une famille de guides, rouge tant qu'aucun visuel
   n'est défini (2026-08-17).

   Nommées par leur SENS (`ko` / `ok`) et non par leur teinte : le jour où le
   vert devient bleu, la classe reste vraie. Mêmes couples de tokens que
   `.nk-trad--todo` / `.nk-trad--done`, pour qu'un manque se lise pareil d'un
   bout à l'autre du Hub. La géométrie, elle, vient de `.nk-btn--icon` — rien
   n'est redéclaré ici. */
.nk-btn--icon-etat-ko { background: var(--danger-l);  border-color: var(--danger-b);  color: var(--danger); }
.nk-btn--icon-etat-ok { background: var(--success-l); border-color: var(--success-b); color: var(--success); }
.nk-btn--icon-etat-ko:hover { border-color: var(--danger); }
.nk-btn--icon-etat-ok:hover { border-color: var(--success); }
/* ── PASTILLE « À valider » d'un bloc éditorial (2026-08-23, demande Lionel) ──
   Elle REMPLACE deux objets d'un coup : l'ancienne pastille orange « À relire »
   et le bouton « Valider ce bloc » de la colonne d'actions (dont la règle de
   survol vivait ici — supprimée avec lui). Un seul objet à l'écran : il DIT
   l'état et PORTE l'action. Au survol comme au focus clavier, il passe au vert
   et son libellé devient « Valider ».

   ⚠️ La bascule de libellé est CSS et NON JavaScript : un handler `mouseenter`
   laisserait le libellé figé sur « Valider » si la souris quitte la carte
   pendant un re-rendu.

   ⚠️ Le survol est un RENFORT, jamais le seul signal — sur tablette il n'existe
   pas. D'où le curseur au repos et un `:focus-visible` qui montre EXACTEMENT le
   même vert : un premier appui ne doit jamais valider à l'insu de l'opérateur,
   la validation estampille son nom et la date. */
/* ⚠️ LES DEUX LIBELLÉS SONT EMPILÉS, pas alternés — et c'est un CORRECTIF, pas
   un raffinement. Avec `display:none` / `display:inline`, le bouton changeait
   de largeur au survol : « À valider — corrigé le 23-08-26 par calimero » fait
   dix fois « Valider ». Le curseur posé sur la partie droite se retrouvait donc
   HORS du bouton rétréci → le survol cessait → le libellé long revenait → le
   curseur était de nouveau dedans… le bouton clignotait sans fin (signalé par
   Lionel le 2026-08-23).

   La grille à une seule case donne au bouton la largeur du PLUS LONG des deux
   libellés, une fois pour toutes : plus rien ne bouge au survol. C'est pour ça
   que le libellé caché l'est par `visibility` et non par `display` — il doit
   continuer d'occuper sa place pour que la largeur reste la plus grande. */
.nk-pb-valider {
  display: inline-grid;
  grid-template-areas: 'nk-lbl';
  align-items: center;
  justify-items: center;
  font-family: inherit;
  font-size: 11px;
  font-weight: 600;
  padding: 2px 9px;
  border-radius: 999px;
  border: 1px solid var(--orange);
  background: var(--orange-l);
  color: var(--orange);
  white-space: nowrap;
}
.nk-pb-valider__repos,
.nk-pb-valider__survol { grid-area: nk-lbl; }
/* Le libellé de survol est INVISIBLE au repos, mais présent : voir ci-dessus.
   Vaut aussi pour la variante NON cliquable, qui n'a aucune action à annoncer. */
.nk-pb-valider__survol { visibility: hidden; }
/* Variante CLIQUABLE — seule à porter le curseur et la bascule de libellé. La
   pastille de la fenêtre d'édition, elle, reste un simple état : elle voisine
   avec « Enregistrer et valider », et deux façons de valider dans la même
   fenêtre ne se justifient pas. */
.nk-pb-valider--cliquable { cursor: pointer; }
.nk-pb-valider--cliquable:not(:disabled):hover,
.nk-pb-valider--cliquable:not(:disabled):focus-visible {
  border-color: var(--success);
  background: var(--success-l);
  color: var(--success);
}
.nk-pb-valider--cliquable:not(:disabled):focus-visible { outline: none; box-shadow: var(--focus-ring); }
/* Les deux libellés ÉCHANGENT leur visibilité, sans jamais quitter la grille :
   la largeur du bouton ne bouge pas d'un pixel (cf. l'en-tête de `.nk-pb-valider`). */
.nk-pb-valider--cliquable:not(:disabled):hover  .nk-pb-valider__repos,
.nk-pb-valider--cliquable:not(:disabled):focus-visible .nk-pb-valider__repos { visibility: hidden; }
.nk-pb-valider--cliquable:not(:disabled):hover  .nk-pb-valider__survol,
.nk-pb-valider--cliquable:not(:disabled):focus-visible .nk-pb-valider__survol { visibility: visible; }
/* Pendant l'enregistrement, le libellé est remplacé par « Validation… » et le
   bouton désactivé : il ne doit alors ni virer au vert ni sembler cliquable. */
.nk-pb-valider--cliquable:disabled { cursor: progress; opacity: .7; }

/* Aperçu d'un bloc éditorial : aération entre paragraphes et listes (la
   description longue paraissait trop compacte sinon — remarque Lionel 2026-07-13). */
.nk-pb-preview p { margin: 0 0 .7em; }
.nk-pb-preview p:last-child { margin-bottom: 0; }
.nk-pb-preview ul, .nk-pb-preview ol { margin: .4em 0 .7em; padding-left: 1.4em; }
.nk-pb-preview li { margin: 0 0 .25em; }

/* ─────────────────────────────────────────────
   ICÔNE DE STATUT DE TRADUCTION (2 états) — composant Hub partagé.
   Usage : à côté d'un champ/titre traduisible, ou dans une colonne Actions.
   Même glyphe « Languages » dans les 2 états ; seuls le fond + le badge changent.
   - `--todo` (à traduire) : fond ambre, badge ambre = nb de langues manquantes.
   - `--done` (traduit)    : fond vert, badge vert = coche.
   HTML via `NocikaShared.tradIcon({ translated, missing, title, attrs })`.
   Taille alignée sur `.nk-btn--icon` (30px) ; variante `--sm` (24px) pour un
   usage inline serré à côté d'un champ.
───────────────────────────────────────────── */
/* Globe LANCEUR de traduction (bouton cliquable, ouvre la modale/grille) : statut
   2 états — vert = traduit, rouge = à traduire, grisé = indispo. Cf. tradIcon. */
.nk-trad {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; padding: 0; flex: 0 0 auto;
  border: 1.5px solid var(--border); border-radius: var(--r-md);
  background: var(--white); color: var(--slate);
  cursor: pointer; box-sizing: border-box; vertical-align: middle;
  transition: border-color var(--t-std), background var(--t-std);
}
.nk-trad svg { width: 15px; height: 15px; }
.nk-trad--todo { background: var(--danger-l); border-color: var(--danger-b); color: var(--danger); }
.nk-trad--done { background: var(--success-l); border-color: var(--success-b); color: var(--success); }
.nk-trad--todo:hover { border-color: var(--danger); }
.nk-trad--done:hover { border-color: var(--success); }
.nk-trad__badge {
  position: absolute; top: -5px; right: -5px;
  min-width: 15px; height: 15px; padding: 0 3px;
  border-radius: 999px; display: inline-flex; align-items: center; justify-content: center;
  font-size: 9px; font-weight: 700; line-height: 1; color: var(--white);
  box-shadow: 0 0 0 1.5px var(--white); box-sizing: border-box;
}
.nk-trad--off { background: var(--surface); border-color: var(--border); color: var(--muted); cursor: not-allowed; opacity: .65; }
.nk-trad--off .nk-trad__badge { background: var(--muted); }
.nk-trad--todo .nk-trad__badge { background: var(--danger); }
.nk-trad--done .nk-trad__badge { background: var(--success); }
.nk-trad__badge svg { width: 9px; height: 9px; }
/* Variante compacte (inline champ). */
.nk-trad--sm { width: 24px; height: 24px; border-width: 1px; }
.nk-trad--sm svg { width: 13px; height: 13px; }
.nk-trad--sm .nk-trad__badge { top: -4px; right: -4px; min-width: 14px; height: 14px; font-size: 8px; }

/* Mini-globe BLEU « champ traduisible » : mémo NON cliquable posé après un
   libellé de champ ou en entête de colonne (cf. NocikaShared.tradMark). Couleur
   identité --trad, taille discrète, aucune interaction. */
.nk-trad-mark { display: inline-flex; align-items: center; vertical-align: middle; margin-left: 5px; color: var(--trad); cursor: help; }
.nk-trad-mark svg { width: 13px; height: 13px; }

/* ── Grille de traduction universelle `.nk-tradg` (nk-trad-grid.js) ──
   N champs × N langues : modale LARGE, en-tête + pied FIXES, corps = tableau
   défilant (`.nk-table-wrap`, ≤ 85vh). Réutilise tokens et pastilles de langue.
   (L'ancienne modale 1-champ `.nk-tradm` / nk-trad-modal.js a été retirée le
   2026-07-30 : tous les call-sites passent désormais par cette grille.) */
/* 1100 → 1320px le 2026-08-17 : à quatre langues, les cellules devenaient des
   fentes. La HAUTEUR ne se force pas ici — le plafond reste 85vh (charte des
   modales) et la fenêtre grandit avec son contenu, les champs longs passant
   désormais sur trois lignes. */
.nk-modal.nk-tradg { max-width: 1320px; width: 94%; max-height: 85vh; padding: 0; display: flex; flex-direction: column; overflow: hidden; }
.nk-tradg .nk-modal__head { padding: 13px 18px 12px; }
.nk-tradg .nk-modal__body { display: flex; flex-direction: column; gap: 10px; padding: 14px 18px; margin-bottom: 0; overflow-y: auto; flex: 1 1 auto; min-height: 0; }
.nk-tradg .nk-modal__foot { padding: 12px 18px; border-top: 1px solid var(--border); display: flex; align-items: center; gap: 10px; }
.nk-tradg__bar { display: flex; align-items: center; gap: 10px; }
.nk-tradg__ai { font-size: 13px; color: var(--muted); text-align: right; }
.nk-tradg__pick { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
.nk-tradg__src { background: var(--bg); }
.nk-tradg__pencil { flex: 0 0 auto; color: var(--muted); }
.nk-tradg__pencil.is-on { color: var(--lang-fr-text); border-color: var(--lang-fr-mid); background: var(--lang-fr-bg); }
.nk-tradg__cell { width: 100%; min-width: 0; }
/* Étirable VERTICALEMENT par l'utilisateur. C'était `resize: none` tant que la
   grille recalculait la hauteur à chaque frappe ; cet ajustement automatique a
   été retiré (il annulait l'étirement manuel), la poignée reprend donc sa place.
   Pas d'étirement horizontal : la largeur est celle de la colonne. */
textarea.nk-tradg__cell { resize: vertical; }
.nk-tradg__cell[readonly] { background: var(--bg); color: var(--ink); cursor: default; }
/* ↻ d'une CELLULE de la grille de traduction. FRAMBOISE depuis le 2026-08-23
   (demande Lionel) : c'est une action de traduction, et la couleur suit
   désormais la grille partagée, plus seulement la page des blocs éditoriaux.
   Coloré AU REPOS, pas seulement au survol — une action qui ne se signale
   qu'au survol ne se voit pas. */
.nk-tradg__regen { flex: 0 0 auto; color: var(--trad-ia); }
.nk-tradg__regen:hover { color: var(--trad-ia); border-color: var(--trad-ia); background: var(--trad-ia-l); }
/* ⚠️ Sauf quand la cellule GÉNÈRE au lieu de traduire — la même grille sert les
   deux (ncmenu rédige son SEO catalogue en natif). Le violet reprend alors la
   main : c'est toute la raison d'être des deux couleurs, et l'appliquer à
   l'aveugle rendrait « générer » et « traduire » de nouveau indiscernables. */
.nk-tradg__regen--gen { color: var(--ia); }
.nk-tradg__regen--gen:hover { color: var(--ia); border-color: var(--ia); background: var(--ia-l); }
/* ══════════════════════════════════════════════════════════════════════════
   LANGUES D'UN BLOC ÉDITORIAL (2026-08-21, arbitrage Lionel)

   Posées en PLEINE LARGEUR sous la carte d'un bloc déplié, via le point
   d'accroche `langueRows` de `nk-editorial-blocks.js`. Le corps de la carte EST
   le français : il n'est pas répété ici et il n'est pas nommé — il est toujours
   le premier, et c'est toujours lui la référence.

   Chaque langue se replie sur son en-tête : une fiche a quatre langues, et on
   en relit rarement quatre à la fois.
   ══════════════════════════════════════════════════════════════════════════ */
/* Les langues sont les FILLES du bloc : elles s'indentent sous lui plutôt que
   de courir d'un bord à l'autre de la carte (demande Lionel du 2026-08-22).
   La gouttière de gauche est ce qui dit l'appartenance — sans elle, une ligne
   de langue se lit comme un bloc de plus. */
.nk-pb-langs {
  border-top: 1px solid var(--border);
  padding-left: var(--space-2xl);
  /* Les lignes RESPIRENT, très légèrement (demande Lionel du 2026-08-22) :
     collées, trois langues se lisaient comme un seul pavé rayé. Le `gap` de
     4 px suffit à les détacher — et il rend le filet de séparation inutile,
     retiré du même coup : deux séparateurs pour une seule frontière, c'est un
     de trop. Même valeur en haut et en bas, pour que la première et la
     dernière ne collent ni au bloc ni au bord de la carte. */
  display: flex; flex-direction: column; gap: var(--space-sm);
  padding-top: var(--space-sm); padding-bottom: var(--space-sm);
}
.nk-pb-lang__head {
  display: flex; align-items: center; gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  background: var(--surface);
}
/* `.nk-pb-lang__fold` SUPPRIMÉE le 2026-08-22 (demande Lionel) : le chevron
   faisait du bruit sur une ligne déjà entièrement cliquable. C'est la PASTILLE
   qui porte désormais le pli — elle est le bouton, et son `aria-expanded` dit
   l'état. Le jumeau `.nk-tradg__fold` reste, lui : la grille de traduction est
   un composant partagé par une douzaine d'écrans, et son pli n'a pas la même
   histoire. */
.nk-pb-lang__note { font-size: var(--fs-base); color: var(--slate); }
.nk-pb-lang__act { margin-left: auto; display: flex; align-items: center; gap: 6px; }
/* Pastille de langue de ce bloc précis. Passée à 15px puis RÉDUITE à
   `--fs-base` le 2026-08-22 : à 15px elle pesait autant que le titre du bloc,
   une fois les lignes indentées. Reste au-dessus des 12px de `.nk-lang-pill`
   ailleurs — ici elle TITRE un texte qu'on vient lire, là-bas elle sert de
   filtre qu'on repère d'un coup d'œil.
   Portée limitée à cet en-tête : remonter la classe partout est un autre lot. */
/* `padding` NON redéclaré : `.nk-lang-pill` pose déjà `3px 11px`. Le répéter
   serait la règle inerte que ce même lot supprime plus bas. */
.nk-pb-lang__head .nk-lang-pill { font-size: var(--fs-base); }
/* Marge sur les QUATRE côtés (demande Lionel du 2026-08-22) : le texte traduit
   courait d'un bord à l'autre de la carte, collé au filet du dessus.
   MÊME typographie que le bloc français : 15px / 1.6, les valeurs que le
   composant pose en inline sur `.nk-pb-preview`. Sans elles, la traduction
   héritait des tailles par défaut du navigateur et s'affichait plus grosse
   que sa référence — relevé par Lionel le 2026-08-22, mesuré à 18px.
   ⚠️ Ne suit PAS encore le sélecteur « Taille du texte » (M/L/XL) : celui-ci
   vit dans le composant, qui ignore tout des langues. Écart connu. */
.nk-pb-lang__body { padding: var(--space-lg) var(--space-xl); font-size: 15px; line-height: 1.6; color: var(--ink); }
.nk-pb-lang__body[hidden] { display: none; }
.nk-pb-lang__empty { font-size: var(--fs-md); color: var(--slate); font-style: italic; }

/* ── DISPOSITION EMPILÉE : les langues sont des LIGNES (2026-08-21) ────────
   Le bloc `.nk-tradg__table*` a été SUPPRIMÉ avec la matrice qu'il habillait.
   Celle-ci divisait la largeur par le nombre de langues — 279 px par langue à
   quatre colonnes, soit ≈ 34 signes là où un libellé produit en fait 80 — et
   chaque langue ajoutée rétrécissait toutes les autres. Ici la langue occupe
   toute la largeur, et une langue de plus coûte une LIGNE : le coût tombe dans
   la direction où la place ne manque pas. Détail du raisonnement et des pistes
   écartées : en-tête de `frontend/shared/nk-trad-grid.js`. */
.nk-tradg__stack { display: flex; flex-direction: column; gap: var(--space-md); }

/* Le bandeau de langues `.nk-tradg__langs*` a été SUPPRIMÉ le 2026-08-21
   (demande Lionel) : il occupait une ligne entière pour répéter des pastilles
   déjà présentes en tête de chaque ligne. La case a rejoint la colonne de
   gauche, juste avant sa pastille — et le stylo la ligne de référence. */

/* Un bloc par champ. */
.nk-tradg__block { border: 1px solid var(--border); border-radius: var(--r-md); overflow: hidden; }
.nk-tradg__bhead {
  display: flex; align-items: center; gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  background: var(--surface); border-bottom: 1px solid var(--border);
}
/* `[hidden]` suffirait seul ici, mais l'explicite protège du jour où ce
   conteneur recevra un `display` : un `display: grid` battrait le style
   par défaut du navigateur et le pli cesserait de replier. */
.nk-tradg__brows[hidden] { display: none; }
/* Un champ replié n'a plus de contenu sous son en-tête : le trait de séparation
   n'aurait plus rien à séparer. */
.nk-tradg__block:has(.nk-tradg__brows[hidden]) .nk-tradg__bhead { border-bottom: 0; }
/* Le chevron du pli de la GRILLE. Il n'a plus de jumeau depuis que celui des
   langues de la fiche PIM a été retiré (2026-08-22) ; et il n'a jamais eu
   d'état `[disabled]` : la grille n'émet jamais cet attribut. */
.nk-tradg__fold {
  flex: 0 0 auto; border: 0; background: transparent; cursor: pointer;
  color: var(--slate); font-size: var(--fs-base); line-height: 1;
  padding: 2px 4px; border-radius: var(--r-sm); font-family: inherit;
}
.nk-tradg__fold:hover { color: var(--ink); background: var(--bg); }
.nk-tradg__fname { font-weight: 700; color: var(--ink); }
/* L'indicateur de mode passe EN LIGNE dans l'en-tête du bloc (il était sous le
   libellé, dans la colonne « Champ » de la matrice). */
.nk-tradg__bhead .nk-tradg__mode { display: inline; margin-top: 0; }
.nk-tradg__bstate { margin-left: auto; font-size: var(--fs-base); color: var(--slate); white-space: nowrap; }

/* Une ligne = une langue : pastille, valeur pleine largeur, action. */
.nk-tradg__row {
  display: grid; grid-template-columns: 132px 1fr 34px;
  align-items: start; gap: var(--space-md);
  padding: var(--space-sm) var(--space-md);
  border-bottom: 1px solid var(--border);
}
.nk-tradg__row:last-child { border-bottom: 0; }
/* Colonne de gauche : la case (cibles) ou le stylo (source) précède la
   pastille. `flex-wrap` protège les libellés longs — « NÉERLANDAIS » sous une
   case tiendrait mal sur une seule ligne a 132 px. */
.nk-tradg__rlang { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; padding-top: 5px; }
/* `.nk-tradg__rlang .nk-tradg__pick` SUPPRIMÉE le 2026-08-22 : elle répétait
   mot pour mot trois déclarations de `.nk-tradg__pick` sans rien y ajouter.
   Spécificité supérieure et position plus tardive n'y changeaient rien — une
   règle qui ne fait que redire ne peut pas agir, elle ne fait que donner à
   croire qu'un cas particulier existe. */
.nk-tradg__rcell { min-width: 0; }
.nk-tradg__ract { display: flex; align-items: center; justify-content: flex-end; padding-top: 3px; }
.nk-tradg__rnote { font-size: var(--fs-base); color: var(--slate); }
.nk-tradg__mode { display: block; margin-top: 5px; font-size: 12px; font-weight: 600; }
.nk-tradg__mode--gen { color: var(--ia); }
.nk-tradg__mode--trad { color: var(--muted); }
.nk-tradg__mode-ic { font-weight: 800; }

/* Variante « bouton icône texte » : taille standard nk-btn--icon mais
   contenu textuel (ex : « PIM » pour ouvrir la fiche dans le PIM depuis
   la liste NCPACK). Couleur IA violet par défaut pour distinguer des
   icônes neutres voisines. Hover violet plein, état actif inchangé. */
.nk-btn--pim-open {
  /* Base commune des variantes pim (saved / unsaved). Taille de texte
     et poids pour intégration nominale dans la zone Actions des tables. */
  font-weight: 700;
  letter-spacing: .3px;
  font-size: 10px;
}
/* Variante « pas encore importé dans le PIM Hub » : rouge danger.
   Sémantique : action requise pour pouvoir éditer la fiche PIM. */
.nk-btn--pim-unsaved {
  color: var(--danger);
  border-color: var(--danger);
}
.nk-btn--pim-unsaved:hover {
  background: var(--danger);
  color: #fff;
  border-color: var(--danger);
}
/* Variante « déjà importé » : vert success.
   Sémantique : la fiche PIM existe, clic = ouverture directe. */
.nk-btn--pim-saved {
  color: var(--success);
  border-color: var(--success);
}
.nk-btn--pim-saved:hover {
  background: var(--success);
  color: #fff;
  border-color: var(--success);
}

/* ============================================================
   BADGES
   Capsules courtes pour états métier, statuts ou affiliations.
   Les tailles compactes et variantes "site" servent surtout dans
   les tableaux et blocs client.
   ============================================================ */
.nk-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  font-weight: 700;
  border-radius: 20px;
  padding: 3px 10px;
  white-space: nowrap;
  border: 1px solid transparent;
}
.nk-badge__dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex-shrink: 0; }

.nk-badge--success { color: var(--success); background: var(--success-l); border-color: var(--success-b); }
.nk-badge--danger  { color: var(--danger);  background: var(--danger-l);  border-color: var(--danger-b); }
.nk-badge--warning { color: var(--warning); background: var(--warning-l); border-color: var(--warning-b); }
.nk-badge--cyan    { color: var(--cyan-d);  background: var(--cyan-l);    border-color: var(--cyan); }
.nk-badge--grey    { color: var(--slate);   background: var(--surface);   border-color: var(--border); }
.nk-badge--ia      { color: var(--ia);      background: var(--ia-l);      border-color: var(--ia-b); }
/* « À traduire » : fond CLAIR + contour framboise (2026-08-22). Remplace le
   rouge `--danger` sur la page des blocs éditoriaux — une langue pas encore
   traduite n'est pas une ERREUR, c'est du travail qui reste, et le rouge la
   mettait au même niveau qu'une panne. Le vert « ✓ Traduit » ne bouge pas :
   c'est bien un aboutissement. */
.nk-badge--trad-ia { color: var(--trad-ia); background: var(--trad-ia-l); border-color: var(--trad-ia-b); }
/* Second vert « Corrigé » (bloc éditorial validé une 1ʳᵉ fois PUIS retouché —
   reste un état valide, mais visuellement distinct du vert émeraude « Validé »).
   Ton olive/lime pour différencier les deux verts (demande Lionel 2026-07-15). */
.nk-badge--success-alt { color: #4d7c0f; background: #f7fee7; border-color: #bef264; }

/* Surlignage ROUGE des marqueurs de relecture laissés par l'IA dans un bloc
   éditorial (« [À COMPLÉTER] » et variantes) — appliqué au RENDU CLIENT par
   `highlightTodoMarkers` (nk-editorial-blocks.js / pim-article-section-editorial-preview.js)
   pour que le merch ne passe pas à côté. Classe plutôt que styles inline
   (revue Franck 2026-07-27). */
.nk-todo-marker { color: var(--danger); font-weight: 700; background: var(--danger-l); padding: 0 4px; border-radius: 3px; }
/* Badge « action requise » (à relire / validation requise) : orange PLEIN,
   nettement plus lisible que le warning pâle sur fond crème, qui « passait pour
   du blanc » sur les cartes de blocs éditoriaux (demande Lionel 2026-07-15). */
.nk-badge--todo    { color: var(--white);   background: var(--orange);    border-color: var(--orange); }

.nk-badge--site {
  font-size: 10px;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: var(--r-sm);
}

.nk-badge-group     { display: flex; gap: 2px; flex-wrap: wrap; }
.nk-badge--disabled { background: var(--surface); color: var(--border-d); border-color: transparent; }

/* ── CHIP — pilule de filtre (cliquable tri-état/version + variante supprimable
   « filtre actif »). Factorisé 2026-07-03 depuis les rendus inline répétés de
   la liste PIM (renderTriStateFilter / renderVersionFilter / activeFilterChips). */
.nk-chip {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--border); background: var(--white); color: var(--muted);
  border-radius: 999px; padding: 4px 12px;
  font-size: 12px; font-weight: 600; cursor: pointer; font-family: inherit;
}
.nk-chip__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--border-d); flex-shrink: 0; }
.nk-chip__sub { font-weight: 400; font-size: 11px; }
.nk-chip__x   { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; border: 0; background: transparent; border-radius: 50%; cursor: pointer; color: inherit; font-size: 12px; line-height: 1; padding: 0; }
.nk-chip--include { border-color: var(--cyan-d); background: var(--cyan-l); color: var(--cyan-d); }
.nk-chip--include .nk-chip__dot { background: var(--cyan-d); }
.nk-chip--exclude { border-color: var(--danger-b); background: var(--danger-l); color: var(--danger); text-decoration: line-through; }
.nk-chip--exclude .nk-chip__dot { background: var(--danger); }
/* Variante « filtre actif » (label + ✕), non cliquable en elle-même. */
.nk-chip--removable { gap: 4px; padding: 3px 4px 3px 10px; font-weight: 500; cursor: default; }

/* ── Langues — pill du sélecteur + tag inline de label (couleurs drapeaux) ──
   Pill : pastille du sélecteur « Contenu en » (texte coloré ; remplie quand
   active). Tag : indicateur de langue inline dans un label de champ — texte
   coloré + filet gauche, léger, dans le flux du label (pas un badge). Variables
   définies dans :root (--lang-<code>-bg/text/mid). */
.nk-lang-pill {
  display: inline-flex; align-items: center; padding: 3px 11px;
  border: 1px solid var(--border); border-radius: var(--r-pill);
  background: var(--white); color: var(--slate);
  font-family: var(--font); font-size: 12px; font-weight: 700; cursor: pointer;
  transition: background .12s ease, border-color .12s ease, color .12s ease;
}
.nk-lang-pill--fr { color: var(--lang-fr-text); }
.nk-lang-pill--de { color: var(--lang-de-text); }
.nk-lang-pill--it { color: var(--lang-it-text); }
.nk-lang-pill--en { color: var(--lang-en-text); }
.nk-lang-pill--fr:not(.active):hover { border-color: var(--lang-fr-mid); }
.nk-lang-pill--de:not(.active):hover { border-color: var(--lang-de-mid); }
.nk-lang-pill--it:not(.active):hover { border-color: var(--lang-it-mid); }
.nk-lang-pill--en:not(.active):hover { border-color: var(--lang-en-mid); }
.nk-lang-pill--fr.active { background: var(--lang-fr-bg); border-color: var(--lang-fr-mid); }
.nk-lang-pill--de.active { background: var(--lang-de-bg); border-color: var(--lang-de-mid); }
.nk-lang-pill--it.active { background: var(--lang-it-bg); border-color: var(--lang-it-mid); }
.nk-lang-pill--en.active { background: var(--lang-en-bg); border-color: var(--lang-en-mid); }

.nk-lang-tag {
  margin-left: 6px; padding-left: 5px; border-left: 2px solid transparent;
  font-size: 11px; font-weight: 700; letter-spacing: .02em;
}
.nk-lang-tag--fr { color: var(--lang-fr-text); border-left-color: var(--lang-fr-mid); }
.nk-lang-tag--de { color: var(--lang-de-text); border-left-color: var(--lang-de-mid); }
.nk-lang-tag--it { color: var(--lang-it-text); border-left-color: var(--lang-it-mid); }
.nk-lang-tag--en { color: var(--lang-en-text); border-left-color: var(--lang-en-mid); }

/* Coche « langue 100% traduite » à droite du code dans une pill du sélecteur. */
.nk-lang-pill__check { margin-left: 5px; color: #16a34a; font-weight: 700; font-size: 11px; }

/* `.nk-lang-pill__globe*` et `.nk-lang-pill__check--ko` — les deux marqueurs
   d'état posés sur la pastille le 2026-08-28 — SUPPRIMÉS le 2026-08-29 : le
   bloc Traduction de la fiche CMS porte désormais l'état, la boutique et
   l'action dans un tableau, et répéter deux de ses colonnes sur la pastille
   obligeait à choisir laquelle croire (demande Lionel). La pastille redevient
   un simple sélecteur de langue. */

/* Variante compacte : utilisée dans les tables denses (fiche client : devis,
   reprises, wallet, identifiants PS). Conserve les couleurs du variant
   sémantique parent, réduit padding + font-size. */
.nk-badge--xs { padding: 1px 7px; font-size: 9px; }

.nk-badge--price { display: inline-block; font-size: 9px; font-weight: 700; padding: 1px 5px; border-radius: var(--r-sm); background: var(--cyan-l); color: var(--cyan-d); margin-left: 4px; }
.nk-badge--price-ht { display: inline-block; font-size: 9px; font-weight: 700; padding: 1px 5px; border-radius: var(--r-sm); background: var(--warning-l); color: var(--warning); margin-left: 4px; }

/* Encadré d'aide / intro cyan — bloc informatif au-dessus d'un écran ou dans
   une modale (partagé : groupement PIM, tables admin…). Les sous-éléments
   (paragraphes de rappel, etc.) restent scopés au module. */
.nk-callout-cyan { background: var(--cyan-l); border: 1px solid var(--cyan); border-radius: var(--r-md); padding: 10px 12px; margin-bottom: 14px; font-size: 13px; line-height: 1.5; color: var(--ink); }
.nk-callout-cyan p { margin: 0; }

/* ============================================================
   BADGES SITE — couleurs métier
   Mapping visuel stable entre site e-commerce et couleur.
   Ces classes sont volontairement séparées des badges génériques car
   elles portent une convention métier, pas seulement une couleur.
   ============================================================ */
.nk-site--fs     { background: #fce8e0; color: #b33a0f; }
.nk-site--oma    { background: #deeaf5; color: #002c50; }
.nk-site--oma-de { background: #ede8fb; color: #3d1f8a; }
.nk-site--oma-it { background: #d4f5e2; color: #0a6b3a; }
.nk-site--ng     { background: #d8f2f8; color: #005f77; }
.nk-site--src    { background: #e6f5f4; color: #1e6b65; }
.nk-site--nk     { background: #fffbeb; color: #92400e; }

/* ============================================================
   FORMULAIRES
   Primitives de saisie réutilisées dans Hub, devis, clients et modales.
   La densité est pensée pour du back-office compact.
   ============================================================ */
.nk-label {
  display: block;
  font-size: 15px; /* 13 → 15 le 2026-06-13 (titre de zone d'édition aligné sur la base 15px) */
  font-weight: 600;
  color: var(--ink); /* slate → ink le 2026-06-13 : couleur de texte unique #0a1f2b */
  margin-bottom: 0; /* 4 → 0 le 2026-06-13 : le gap du .nk-form-group gère seul l'écart label↔champ (sinon il s'AJOUTE et le titre reste trop loin de sa saisie) */
}

/* ⚠️ ÉTAT DÉSACTIVÉ — c'est la PSEUDO-CLASSE qui manquait jusqu'au 2026-08-14,
   pas le style.
   ⚠️ La première rédaction de ce commentaire disait « il n'existait AUCUN style
   pour lui », et c'était FAUX : `.nk-input--disabled` (opacité + curseur) existe
   plus bas dans ce fichier depuis longtemps. Ce qui manquait, c'est qu'un champ
   portant l'ATTRIBUT `disabled` sans cette classe ne recevait rien — il était
   inerte mais parfaitement identique à un champ éditable : on cliquait dedans,
   rien ne se passait, et rien ne disait pourquoi.
   Relevé par Lionel sur l'unité d'un filtre en liste de valeurs — « unité
   affichée pour liste de valeur, ce n'est pas que pour numérique ? ». Elle
   l'était bien ; c'est l'écran qui ne le montrait pas.
   Vaut pour TOUS les champs désactivés du Hub, pas seulement celui-là.

   ⚠️ CONSÉQUENCE À CONNAÎTRE, mesurée le 2026-08-15 : deux champs CUMULENT
   désormais les deux traitements, parce qu'ils portent la classe ET l'attribut —
   `nk-client-page.js` (email du client) et `hub-admin-occasion.js` (clé de
   famille en édition). Ils reçoivent donc l'opacité .6 EN PLUS du fond grisé.
   Un troisième call site, `nk-client-modal.js`, porte la classe avec `readonly`
   et NON `disabled` : la pseudo-classe ne le matche pas, il ne cumule rien.
   Le cumul est cosmétique et n'a pas été défait ici — deux écrans hors du
   chantier Filtres changeraient d'aspect sans que Lionel l'ait demandé. */
.nk-input:disabled {
  background: var(--bg);
  color: var(--muted);
  cursor: not-allowed;
}
.nk-input:disabled::placeholder { color: var(--border-d); }

.nk-input {
  width: 100%;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 6px 9px;
  font-family: var(--font);
  font-size: 15px; /* 12 → 15 le 2026-06-12 (saisie alignée sur la base 15px hub-wide ; la variante --sm reste à 12 pour les contextes denses) */
  color: var(--ink); /* navy → ink le 2026-06-13 : couleur de texte unique #0a1f2b */
  background: var(--white);
  outline: none;
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.nk-input:focus { border-color: var(--cyan); box-shadow: var(--focus-ring); }
/* Placeholder « texte exemple » (restructuration typo 2026-06-13) : gris
   clair --slate-light, 14px, bien distinct du texte saisi (--ink).
   Disparaît au focus via la règle globale input:focus::placeholder. */
.nk-input::placeholder { color: var(--slate-light); font-size: 14px; opacity: 1; }
/* Champ vide : fond BLANC constant (était --surface gris). Uniformité des
   zones de saisie demandée 2026-06-13 — un champ vide ne se distingue plus
   d'un champ rempli par la couleur de son fond. */
.nk-input:placeholder-shown:not(:focus) { border-color: var(--border); background: var(--white); }
.nk-input--sm { padding: 4px 7px; font-size: 12px; border-radius: var(--r-sm); }
/* Variante éditeur de code/HTML : police à chasse fixe + interligne lisible.
   Réutilisable partout où l'on saisit du HTML/SQL brut dans un textarea. */
.nk-input--mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; line-height: 1.5; }
/* Champ NUMÉRIQUE COURT — deux à trois chiffres (nombre de tranches, quantité,
   rang, pourcentage). Autorisée par Lionel le 2026-08-12.

   POURQUOI elle existe : un `<input>` ne s'adapte jamais à son contenu, sa
   largeur par défaut vaut ~20 caractères. Un champ qui ne reçoit qu'un « 4 »
   s'affiche donc aussi large qu'un champ de nom. L'attribut HTML natif `size`
   réglerait ça sans CSS, mais les navigateurs l'IGNORENT sur `type="number"` —
   d'où une classe, et non un attribut.

   Elle remplace les `style="width:56px"` que chaque écran s'écrivait dans son
   coin. La valeur reste ici : la retoucher ne doit pas obliger à repasser sur
   les appelants, c'est tout l'objet de la classe.
   `text-align: center` est inclus : un nombre court se lit centré dans son
   champ, et le séparer aurait obligé chaque appelant à penser à `.nk-text-center`. */
.nk-input--num { width: 56px; text-align: center; }

.nk-select {
  width: 100%;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 6px 26px 6px 9px;
  font-family: var(--font);
  font-size: 12px;
  color: var(--navy);
  background: var(--white);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2.5' stroke-linecap='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
  appearance: none;
  -webkit-appearance: none;
  outline: none;
  cursor: pointer;
  transition: border-color var(--t-fast);
}
.nk-select:focus { border-color: var(--cyan); box-shadow: var(--focus-ring); }

.nk-form-group { display: flex; flex-direction: column; gap: 3px; } /* gap 4 → 3 le 2026-06-13 : titre de zone collé à sa saisie (combiné à .nk-label margin-bottom:0) */
/* Auto-espacement des champs empilés (2026-06-13, restructuration typo
   hub-wide) : deux .nk-form-group consécutifs dans un corps de carte
   respirent de --space-lg (12px) SANS dépendre d'une classe sur le parent.
   Le `:not(.nk-stack-sm)` évite le double-espacement quand la carte gère
   déjà l'écart via son gap (.nk-card-body.nk-stack-sm { gap: 12px }). Règle
   robuste : un formulaire en carte n'a plus jamais ses champs collés, même
   si le module oublie nk-stack-sm. */
.nk-card-body:not(.nk-stack-sm) > .nk-form-group + .nk-form-group { margin-top: var(--space-lg); }
/* Variante HORIZONTALE : le libellé à GAUCHE de sa zone de saisie, pas au-dessus
   (demande Lionel du 2026-08-14 — « utilise la largeur pour aérer »).
   Sur une carte large portant plusieurs réglages courts — un select, une unité,
   une case — l'empilement vertical du groupe par défaut gâche toute la largeur
   et allonge la carte sans rien gagner en lisibilité. En ligne, chaque réglage
   tient sur sa hauteur de champ et les suivants se rangent à côté, séparés par le
   `gap` du conteneur.
   `align-items: baseline` et non `center` : le libellé et le texte du champ
   s'alignent alors sur la même ligne d'écriture, y compris quand le champ est
   plus haut que son libellé (select, input). */
.nk-form-group--inline {
  flex-direction: row;
  align-items: baseline;
  gap: var(--space-sm);
}
/* Le libellé ne se laisse pas écraser par un champ élastique voisin. */
.nk-form-group--inline > .nk-inline-row,
.nk-form-group--inline > .nk-label { flex-shrink: 0; }

/* ─── LISTE DE RÉGLAGES : un par ligne, libellés alignés ───
   Pour une carte de paramètres (éditeur de filtre, 2026-08-14).

   ⚠️ **NOM PROPRE, et c'est une leçon payée le jour même** : la première version
   s'appelait `.nk-form-grid` — une classe qui EXISTAIT DÉJÀ, partagée par cinq
   écrans (modales client et adresse, newsletter, liste et fiche client). La
   redéfinir leur imposait en silence un `justify-items` et une media query qui
   ne les concernaient pas. Vérifier qu'un nom est libre AVANT de le définir.

   ⚠️ Deux formes essayées et rejetées le même jour : une rangée qui se replie
   (tout tassé à gauche, moitié droite vide, rien qui s'aligne d'une ligne à
   l'autre), puis deux paires par ligne (absurde dès qu'il n'y a qu'un réglage
   par ligne — une colonne vide poussait le champ au milieu de l'écran). Ces
   cartes portent trop peu de réglages pour deux colonnes.

   La largeur FIXE du libellé est ce qui fait l'alignement. `max-content`
   alignerait les libellés d'une même carte, mais pas d'une carte à l'autre — or
   c'est verticalement, en descendant l'écran, qu'on les lit. */
.nk-settings-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
.nk-settings-list > .nk-form-group,
.nk-settings-list > .nk-settings-list__wide {
  flex-direction: row;
  align-items: center;
  /* Écart entre un libellé et SA zone de saisie. Il a bougé trois fois le
     2026-08-14, et la valeur juste ne s'est trouvée qu'une fois la colonne de
     libellés supprimée : tant qu'elle existait, l'espace visible venait surtout
     d'elle, et régler ce `gap` ne changeait presque rien. Sans elle, il porte
     tout l'écart — d'où `--space-lg`, où `--space-sm` collait le libellé au
     champ. Ce qui sépare deux réglages d'une même ligne reste plus large
     (`nk-ml-lg` sur le second) : c'est ce contraste qui dit lesquels vont
     ensemble. */
  gap: var(--space-lg);
}
/* La colonne des libellés. `_labelInfo()` rend une rangée (étiquette + icône
   d'aide), d'où les deux sélecteurs. */
/* ⚠️ **AUCUNE COLONNE DE LIBELLÉS — chaque ligne est indépendante.**
   Diagnostic de Lionel le 2026-08-14, après quatre tentatives infructueuses de
   ma part : « tes problèmes viennent du colonnage ». Il a raison, et une largeur
   fixe ne pouvait pas s'en sortir — elle produisait mécaniquement l'un des trois
   défauts selon le libellé qu'on lui donnait :

     trop étroite  → le libellé se replie sur deux lignes, ou se fait couper ;
     trop large    → un vide béant après les libellés courts ;
     entre les deux → les deux à la fois, sur la même carte.

   Chaque ligne dimensionne donc son libellé sur son contenu. Les zones de saisie
   ne s'alignent plus verticalement d'une ligne à l'autre — c'est le prix, et il
   est assumé : ces cartes portent peu de réglages, et on les lit un par un.
   `nowrap` + `flex-shrink: 0` : un libellé ne se replie jamais et ne se laisse
   pas comprimer par un champ élastique voisin. */
.nk-settings-list > .nk-form-group > .nk-inline-row:first-child,
.nk-settings-list > .nk-form-group > .nk-label:first-child,
.nk-settings-list > .nk-settings-list__wide > .nk-inline-row:first-child {
  flex-shrink: 0;
  white-space: nowrap;
}
/* ⚠️ SANS CETTE RÈGLE, LES CHAMPS S'ÉTIRENT. `.nk-input` est en `width: 100%` :
   dans une ligne flex il prend alors toute la place restante — c'est ce qui
   donnait un select de quatre options courtes sur la moitié de l'écran.
   `auto` le laisse prendre la taille de sa plus longue option. */
.nk-settings-list > .nk-form-group > .nk-input { width: auto; max-width: 100%; }
/* Champs courts — unité, décimales, bornes : une largeur modeste vaut mieux
   qu'un `auto` qui rend un champ nombre plus large que son libellé. */
.nk-settings-list > .nk-field--sm > .nk-input { width: 90px; }
/* Un champ de PHRASE prend la largeur restante : la tronquer n'aide personne. */
.nk-settings-list > .nk-settings-list__wide > .nk-input { flex: 1; width: auto; }
/* ⚠️ MAIS PAS UNE LISTE DÉROULANTE (demande Lionel du 2026-08-14). Un `select`
   a un nombre fini d'options : l'étirer sur toute la largeur laisse un ruban de
   vide entre son texte et sa flèche, et fait croire à un champ libre. Il se
   dimensionne sur sa plus longue option, comme partout ailleurs dans la liste. */
.nk-settings-list > .nk-settings-list__wide > select.nk-input { flex: 0 1 auto; }
/* L'espacement vertical vient du `gap` : la marge des groupes empilés
   s'y ajouterait. */
.nk-card-body.nk-settings-list > .nk-form-group + .nk-form-group { margin-top: 0; }
/* Un FILET entre deux réglages (maquette Lionel du 2026-08-14). Il donne à
   chaque ligne sa propre bande et empêche deux réglages voisins de se lire comme
   un seul — ce que l'écart seul ne suffisait pas à faire.
   Il court d'un bord à l'autre de la carte : les marges négatives compensent le
   padding horizontal de `.nk-card-body` (16px), sinon le filet flotte en retrait
   et la carte se lit comme une liste indentée. */
.nk-settings-list > * + * {
  border-top: 1px solid var(--border);
  padding: var(--space-md) 16px 0;
  margin-left: -16px;
  margin-right: -16px;
}
/* Option RETENUE d'un sélecteur à boutons. Les options gardent toutes leur
   `--outline` — c'est ce qui les fait lire comme un choix — et seule la retenue
   passe en cyan.
   ⚠️ Ne PAS utiliser `--ghost` pour l'option non retenue : ghost retire la
   bordure, donc l'option cesse d'avoir l'air cliquable et se lit comme du texte
   (défaut du 2026-08-14, relevé par Lionel sur « Fermé (oui/non) »).
   BLEU `--info` et non le cyan des pastilles de rendu (demande Lionel du
   2026-08-14) : les deux sélecteurs se suivent dans la même carte, et deux
   marquages de la même teinte les feraient lire comme un seul groupe de choix. */
/* ⚠️ Le survol est REPRIS explicitement : `.nk-btn--outline:not(:disabled):hover`
   est plus spécifique (0,3,0) que cette classe seule (0,1,0), et repasserait
   l'option retenue en `--navy` dès qu'on la survole — elle perdrait sa marque au
   moment précis où on la vise. */
.nk-btn--selected,
.nk-btn--selected:not(:disabled):hover {
  color: var(--info);
  border-color: var(--info);
  background: var(--info-l);
  font-weight: var(--fw-semibold);
}

/* Réglage MIS EN AVANT : il tient toute la largeur, sans colonne de libellé,
   parce que sa phrase se suffit. Réservé au réglage qui change ce que le CLIENT
   voit, là où ses voisins décrivent la donnée. */
/* ⚠️ SANS BORDURE (demande Lionel du 2026-08-14). Le fond teinté suffit à
   détacher le bloc ; le trait par-dessus en faisait une boîte dans une boîte,
   alors que ses voisins n'ont qu'un filet. */
.nk-settings-list__accent {
  background: var(--cyan-l);
  border-radius: var(--r-md);
  padding: var(--space-md) var(--space-lg);
}
/* ⚠️ IL ÉCHAPPE AU FILET PLEINE LARGEUR, et pas seulement à sa bordure.
   Défaut relevé par Lionel le 2026-08-14 : la règle `> * + *` ci-dessus lui
   imposait aussi ses marges négatives et son padding, ce qui tirait l'encadré
   d'un bord à l'autre de la carte et écrasait le sien. Le bandeau redevient un
   CARRÉ posé DANS la carte, avec sa marge autour — « pas collé ».
   Les deux sélecteurs sont plus spécifiques que `> * + *` (dont le sujet `*` ne
   compte pour rien), donc l'ordre de déclaration n'entre pas en jeu. */
.nk-settings-list > * + .nk-settings-list__accent,
.nk-settings-list > .nk-settings-list__accent {
  border-top: 0;
  margin: var(--space-sm) 0;
  padding: var(--space-md) var(--space-lg);
}
/* Le réglage qui SUIT le bandeau n'a pas de filet non plus : la bordure du
   bandeau sépare déjà, deux traits à trois pixels d'écart se lisent comme une
   erreur de rendu. */
.nk-settings-list > .nk-settings-list__accent + * { border-top: 0; }

.nk-form-row   { display: flex; gap: 10px; flex-wrap: wrap; }
.nk-form-grid  { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.nk-required   { color: var(--danger); }

/* Info complémentaire sous un champ (« balise <title> — vide = Titre »).
   Classe dédiée (2026-06-13, validée en showroom) : 13px italique, couleur
   unique --ink, collée au champ (margin-top 3). À préférer à
   .nk-text-muted-sm pour les aides de saisie — cette dernière reste
   réservée aux métas / compteurs gris ailleurs. */
.nk-hint { display: block; font-size: 13px; font-style: italic; color: var(--ink); margin-top: 3px; }
/* `display: block` ci-dessus BAT le `[hidden] { display: none }` de la
   feuille du navigateur (regle d'auteur > regle UA) : sans cette ligne, un
   `.nk-hint` porteur de l'attribut `hidden` reste VISIBLE, en silence.
   Constate le 2026-08-20 sur l'indice « proposition automatique » de la
   modale de metadonnees d'image. Meme parade que `.nk-brandpick__item[hidden]`
   et `.nk-topbar-badge[hidden]`. */
.nk-hint[hidden] { display: none; }

/* Message d'erreur inline masqué par défaut puis affiché par JS
   via ajout de contenu ou classe utilitaire. */
.nk-form-error {
  display: none;
  padding: 7px 10px;
  background: var(--danger-l);
  border: 1px solid var(--danger-b);
  border-radius: var(--r-md);
  font-size: 12px;
  color: var(--danger);
}
.nk-form-warning {
  padding: 8px 12px;
  background: var(--warning-l);
  border: 1px solid var(--warning-b);
  border-radius: var(--r-md);
  font-size: 12px;
  color: var(--warning);
  line-height: 1.5;
}

/* Helpers de composition rapides pour petits formulaires legacy. */
.nk-row      { display: flex; gap: 10px; margin-bottom: 9px; flex-wrap: wrap; }
.nk-field    { flex: 1; min-width: 70px; }
.nk-field--sm { flex: 0 0 80px; }
/* `.nk-field--md` et `.nk-field--lg` supprimées 2026-05-23 (chantier C,
   Phase 0a) : variantes form jamais appliquées. */

/* Champ avec indicateur de statut asynchrone (loading, check, erreur). */
.nk-ref-wrap { position: relative; }
.nk-ref-wrap .nk-input { padding-right: 26px; }
.nk-ref-status {
  position: absolute;
  right: 6px; top: 50%;
  transform: translateY(-50%);
  font-size: 13px;
  pointer-events: none;
  line-height: 1;
}
.nk-ref-status--loading { animation: nk-spin .7s linear infinite; display: inline-block; }

/* Tooltip d'erreur ancré sous un champ positionné relativement. */
.nk-tooltip-err {
  position: absolute;
  top: calc(100% + 4px); left: 0;
  z-index: var(--z-tooltip);
  background: var(--danger);
  color: var(--white);
  font-size: 11px;
  padding: 4px 9px;
  border-radius: var(--r-sm);
  white-space: nowrap;
  pointer-events: none;
}
.nk-tooltip-err::before {
  content: '';
  position: absolute;
  bottom: 100%; left: 10px;
  border: 5px solid transparent;
  border-bottom-color: var(--danger);
}

@keyframes nk-spin { to { transform: rotate(360deg); } }

/* ============================================================
   CARDS
   Conteneurs standards pour sections éditoriales ou métiers.
   `.nk-card-head` est cliquable et peut servir de déclencheur de repli.
   ============================================================ */
.nk-card {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  margin-bottom: 22px; /* 12 → 22 le 2026-06-13 : plus d'air ENTRE les blocs (contraste avec l'espacement serré intra-bloc, pour distinguer les blocs) */
  flex-shrink: 0;
}

/* Entête de bloc — style canonique hub-wide (restructuration typo
   2026-06-12) : fond gris bleuté var(--bg) #f1f5f9, DM Sans 15px bold,
   texte « noir » var(--navy). Le titre se suffit à lui-même : les
   nouvelles entêtes n'ajoutent pas d'icône décorative avant le titre
   (retirées des blocs page CMS le 2026-06-12). */
.nk-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  font-size: 15px; /* 12 → 15 le 2026-06-12 (titres de blocs alignés sur la base 15px hub-wide) */
  font-weight: var(--fw-bold); /* 800 → 700 le 2026-06-12 (bold standard, restructuration typo) */
  color: var(--ink); /* navy → ink le 2026-06-13 : couleur de texte unique #0a1f2b */
  cursor: pointer;
  user-select: none;
}

.nk-card-head-icon {
  width: 20px; height: 20px;
  border-radius: var(--r-sm);
  background: var(--orange);
  color: var(--white);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 800;
  flex-shrink: 0;
}
/* Variantes de couleur de l'icône de carte (défaut orange). Extraites des
   `style="background:var(--…)"` inline des modules le 2026-06-12. */
.nk-card-head-icon--navy { background: var(--navy); }
.nk-card-head-icon--cyan { background: var(--cyan); }

.nk-card-head-chevron {
  margin-left: auto;
  font-size: 11px;
  color: var(--muted);
  transition: transform var(--t-std);
}
.nk-card-head.open .nk-card-head-chevron { transform: rotate(180deg); }

/* Corps de bloc (restructuration typo 2026-06-13) : texte courant 14px,
   couleur unique --ink. Padding-haut resserré (10) pour rapprocher le
   contenu de l'entête ; 16 sur les côtés, 12 en bas. */
.nk-card-body { padding: 10px 16px 12px; color: var(--ink); font-size: 14px; }
/* Espace entre éléments empilés d'un corps de bloc (champs, lignes) quand
   il porte .nk-stack-sm — plus large que le stack générique (5px) pour
   séparer les champs sans casser le regroupement label↔champ. */
.nk-card-body.nk-stack-sm { gap: 12px; }

/* Variante "panel" plus plate utilisée dans quelques sous-sections
   où l'on veut un en-tête compact sans vraie carte complète. */
.nk-panel__head {
  height: 40px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 16px;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  user-select: none;
}
.nk-panel__head.open .nk-panel__chevron { transform: rotate(180deg); }
.nk-panel__title   { font-size: 13px; font-weight: 600; color: var(--navy); }
.nk-panel__meta    { font-size: 11px; color: var(--muted); }
.nk-panel__chevron {
  margin-left: auto;
  font-size: 11px;
  color: var(--muted);
  transition: transform var(--t-std);
}

/* ============================================================
   KPI
   Cartes de synthèse compactes.
   Chaque KPI embarque ses propres variables CSS pour décliner la même
   structure avec plusieurs palettes sans dupliquer le markup.
   ============================================================ */
.nk-kpis {
  display: grid;
  grid-template-columns: repeat(4, minmax(0,1fr));
  gap: 5px;
  padding: 6px 16px 5px;
  flex-shrink: 0;
}

.nk-kpi {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 6px 10px;
  cursor: pointer;
  transition: border-color var(--t-fast);

  --kpi-bg:     var(--surface);
  --kpi-color:  var(--slate);
  --kpi-border: var(--border);
}
.nk-kpi:hover  { border-color: var(--border-d); }
.nk-kpi.active { border-color: var(--navy); box-shadow: 0 0 0 1px var(--navy); }

/* Variantes sémantiques.
   Elles alimentent `--kpi-*`, ensuite consommées par les sous-éléments. */
.nk-kpi--grey    { --kpi-bg: var(--surface);   --kpi-color: var(--slate);   --kpi-border: var(--border); }
.nk-kpi--cyan    { --kpi-bg: var(--cyan-l);    --kpi-color: var(--cyan-d);  --kpi-border: var(--cyan); }
.nk-kpi--success { --kpi-bg: var(--success-l); --kpi-color: var(--success); --kpi-border: var(--success-b); }
.nk-kpi--warning { --kpi-bg: var(--warning-l); --kpi-color: var(--warning); --kpi-border: var(--warning-b); }
.nk-kpi--danger  { --kpi-bg: var(--danger-l);  --kpi-color: var(--danger);  --kpi-border: var(--danger-b); }

/* Éléments enfants — consomment les variables du parent */
.nk-kpi__head  { display: flex; align-items: center; gap: 5px; margin-bottom: 4px; }
.nk-kpi__icon  { width: 20px; height: 20px; border-radius: var(--r-sm); display: flex; align-items: center; justify-content: center; flex-shrink: 0; background: var(--kpi-bg); }
.nk-kpi__icon svg { width: 10px; height: 10px; color: var(--kpi-color); }
.nk-kpi__label { font-size: 9px; font-weight: 700; color: var(--slate); text-transform: uppercase; letter-spacing: .5px; }
.nk-kpi__value { font-size: 15px; font-weight: 700; color: var(--navy); line-height: 1; margin-bottom: 3px; }
.nk-kpi__chip  { display: inline-flex; align-items: center; gap: 2px; font-size: 9px; font-weight: 600; padding: 1px 5px; border-radius: var(--r-sm); border: 1px solid var(--kpi-border); background: var(--kpi-bg); color: var(--kpi-color); }
.nk-kpi__dot   { display: inline-block; width: 5px; height: 5px; border-radius: 50%; background: currentColor; margin-right: 2px; vertical-align: middle; }
.nk-kpi__empty { font-size: 11px; color: var(--muted); }

/* ============================================================
   LISTE DEVIS
   Shell vertical commun aux pages de listing.
   Utilise une barre de contexte fixe puis une zone de contenu scrollable.
   ============================================================ */
.nk-list {
  font-family: var(--font);
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  width: 100%;
}

.nk-list__content {
  display: flex;
  flex-direction: column;
  flex: 1;
  overflow-y: auto;
  min-height: 0;
  padding: 16px 20px;
  gap: 12px;
}

/* Ligne de filtres et d'actions secondaires au-dessus des tableaux. */
.nk-list__filters { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* ── ZONE ÉTIQUETÉE — regroupement visuel nommé au-dessus d'un tableau ──
   Usage : <fieldset class="nk-zone"><legend class="nk-zone__legend">Recherche</legend>…
   Sépare des familles d'actions qui n'ont rien à voir entre elles et que la
   context bar mélangeait (liste PIM 2026-07-17 : « Migration » vs
   « Recherche »). Le nom de la zone dit à quoi servent les contrôles qu'elle
   contient — un bouton isolé dans une barre fourre-tout ne le dit jamais.
   fieldset/legend NATIFS : le navigateur découpe lui-même la bordure autour de
   la légende, aucun positionnement absolu à maintenir.
   `min-width:0` : un fieldset porte un `min-width:min-content` implicite qui
   l'empêche de rétrécir dans un conteneur flex (il déborderait la page). */
.nk-zone {
  min-width: 0;
  margin: 0;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: var(--space-md) var(--space-lg) var(--space-lg);
  background: var(--white);
}
.nk-zone__legend {
  margin-left: var(--space-sm);
  padding: 0 var(--space-sm);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  letter-spacing: .5px;
  text-transform: uppercase;
  color: var(--muted);
}
/* Variante IA : la zone porte des actions de génération IA — violet hub-wide
   (cf. CLAUDE.md § Génération IA). Tokens --ia-* : jamais les hexa en dur. */
.nk-zone--ia { border-color: var(--ia-b); background: var(--ia-l); }
.nk-zone--ia > .nk-zone__legend { color: var(--ia); }
/* Rangée de contrôles d'une zone : les actions à gauche, l'état/validation
   poussés à droite par `.nk-zone__spacer`. */
.nk-zone__row { display: flex; align-items: center; gap: var(--space-md); flex-wrap: wrap; }
.nk-zone__spacer { flex: 1; }

/* Bouton BASCULE d'une zone (mode engagé / non engagé) : l'état vit dans
   `aria-pressed` — sémantique native du toggle, donc lu par les lecteurs
   d'écran, et pas une classe de plus à synchroniser. Engagé = fond léger de
   l'accent ; JAMAIS le fond plein `--ia-solid`, réservé aux actions IA
   primaires (« Générer la fiche »). */
.nk-zone .nk-btn--outline.nk-btn--ia[aria-pressed="true"] {
  background: var(--ia-l);
  font-weight: var(--fw-semibold);
}

/* Variante « scindée » de la zone de contenu : pleine largeur, panneaux en
   ligne (chaque panneau gère son propre padding et son scroll). Utilisée par
   les écrans à panneau gauche + panneau droit (liste CMS catégories/pages,
   fiche CMS split SEO/contenu, vue d'édition Puck). Le panneau de droite
   re-déclare `.nk-list__content` (imbriqué) + `.nk-grow` pour retrouver le
   padding/scroll standard. */
.nk-list__content--split {
  flex-direction: row;
  padding: 0;
  gap: 0;
  overflow: hidden;
}

/* NB scroll éditeur Puck (#/cms/:id/edit/:lang) : le scroll de la zone
   centrale reste à finaliser (Puck est conçu plein écran `100dvh` + canvas
   `_PuckCanvas-root` en position:absolute + zoom « Auto » en transform:scale →
   le scroll se comporte mal une fois embarqué dans la coquille Hub bornée).
   Les essais d'override CSS par hauteur ont été retirés (fragiles / régressifs).
   À reprendre avec un outil pilotant le navigateur (inspection live du DOM
   rendu), idéalement côté config Puck (viewport/zoom) plutôt qu'en override. */

/* Wrapper visuel du champ de recherche.
   L'input réel vit à l'intérieur pour garder l'icône et les clear buttons. */
.nk-list__search {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--white);
  min-width: 200px;
  flex: 1;
  max-width: 320px;
  height: 32px;
  transition: border-color var(--t-fast);
}
.nk-list__search:focus-within { border-color: var(--cyan); }
.nk-list__search svg { width: 14px; height: 14px; flex-shrink: 0; color: var(--muted); }
.nk-list__search input {
  border: none; outline: none; background: transparent;
  font-family: var(--font); font-size: 12px; color: var(--navy);
  flex: 1; min-width: 0; height: 100%;
}
.nk-list__search input::placeholder { color: var(--muted); }

/* Select compact pour filtres de listing. */
.nk-list__select {
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  font-family: var(--font);
  font-size: 12px;
  color: var(--navy);
  background: var(--white);
  outline: none;
  cursor: pointer;
  height: 32px;
  transition: border-color var(--t-fast);
}
.nk-list__select:focus { border-color: var(--cyan); }

.nk-list__count { font-size: 12px; color: var(--muted); margin-left: auto; white-space: nowrap; }

/* ============================================================
   PAGINATION — composant partagé NkPagination (frontend/shared/nk-pagination.js).
   Une seule implémentation, identique sur toutes les listes du Hub.
   ============================================================ */
.nk-pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
  margin-top: var(--space-md);
  padding: 0 var(--space-xs);
}
.nk-pagination__info { display: flex; align-items: center; gap: var(--space-md); }
/* Plancher de police : --fs-sm (11px). « 1 à 25 sur 248 résultats » et
   « lignes par page » sont du TEXTE LISIBLE, donc jamais --fs-xs (9px, réservé
   aux micro-indicateurs non textuels) — cf. docs/UX.md § Typographie. */
.nk-pagination__count { font-size: var(--fs-sm); color: var(--muted); white-space: nowrap; }
.nk-pagination__pages { display: flex; align-items: center; gap: 2px; }
.nk-pagination__ellipsis { font-size: var(--fs-sm); color: var(--muted); padding: 0 2px; }
/* Sélecteur « [25 ▾] lignes par page » : le libellé vit HORS de l'option. */
.nk-pagination__perpage,
.nk-pagination__goto {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  font-size: var(--fs-sm);
  color: var(--muted);
  white-space: nowrap;
}
/* Précision d'écran accolée à la barre (ex. liste PIM live : « (par site
   coché) », sans quoi « Page 3 sur 12 » se lit comme une pagination globale). */
.nk-pagination__note {
  font-size: var(--fs-sm);
  color: var(--muted);
  font-style: italic;
  white-space: nowrap;
}
/* Slot d'insertion de la barre (`NkPagination.create`) : le composant en possède
   entièrement le contenu, l'écran n'y écrit jamais rien. VIDE — liste sans
   résultat, `total: 0` — il ne doit pas peser dans le `gap` de son conteneur
   flex (`.nk-list__content`, gap 12px), sinon une liste vide gagne un espace
   fantôme sous son dernier élément là où l'ancienne concaténation ne posait
   aucun nœud. */
.nk-pagination-slot:empty { display: none; }
.nk-pagination__goto-input {
  width: 52px;
  text-align: center;
  font-size: var(--fs-sm);
  padding: 2px 4px;
}
/* Compacité des contrôles dans la barre (select + boutons numérotés).
   Le padding droit doit laisser passer le chevron du `.nk-select`, posé par
   `background-position: right 7px center` : en dessous, il mord sur le nombre. */
.nk-pagination .nk-select--sm { width: auto; min-width: 56px; font-size: var(--fs-sm); padding: 4px 22px 4px 8px; }
.nk-pagination__pages .nk-btn--sm { min-width: 28px; padding: 4px 6px; font-size: var(--fs-sm); }
/* Page active en bouton PLEIN : `.nk-btn--outline.on` rend un fond --surface
   quasi indiscernable du blanc voisin. Portée limitée à la barre pour ne pas
   toucher les autres usages de `.on` dans le Hub. */
.nk-pagination__pages .nk-btn--outline.on {
  background: var(--navy);
  border-color: var(--navy);
  color: var(--white);
  font-weight: var(--fw-semibold);
}
.nk-pagination__pages .nk-btn--outline.on:not(:disabled):hover {
  background: var(--navy);
  border-color: var(--navy);
  color: var(--white);
}

.nk-list__check-label {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 15px; /* 12 → 15 le 2026-06-13 (libellé de case aligné sur la base) */
  color: var(--ink); /* slate → ink le 2026-06-13 : couleur de texte unique #0a1f2b */
  cursor: pointer;
}

.nk-td--margin      { text-align: right; white-space: nowrap; font-size: 12px; font-weight: 700; }
.nk-td--margin-sub  { font-size: 11px; margin-top: 1px; }

/* ============================================================
   TABLEAU — composant unique réutilisable
   Tableau de back-office par défaut.
   Les variantes de cellules (`.nk-td--*`) servent à changer la présentation
   sans recréer de styles par écran.
   ============================================================ */
.nk-table-wrap {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: auto;
  box-shadow: var(--shadow-sm);
}

table.nk-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font);
  font-size: 13px;
  color: var(--navy);
}

/* En-tête sticky visuellement, mais pas techniquement fixé : la logique de
   scroll reste portée par `.nk-table-wrap` pour simplifier les écrans.
   Charte Nocika lot 1 : fond blanc + filet bas 2px marqué (cran
   `--border-strong`) pour séparer franchement de la zone de données
   sans recourir à un fond navy plein. */
table.nk-table thead tr { background: var(--white); }
table.nk-table thead th {
  padding: 9px 14px;
  font-size: 10px;
  font-weight: 700;
  color: var(--slate);
  text-align: left;
  text-transform: uppercase;
  letter-spacing: .8px;
  border-bottom: 2px solid var(--border-strong);
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
}
/* Alignement des en-têtes : les utilitaires nk-td--center/right posés sur
   un <th> étaient écrasés par le text-align:left ci-dessus (spécificité).
   Règles au niveau du composant pour que l'en-tête suive l'alignement de
   sa colonne (fix générique 2026-06-12 — remplace les contournements
   ad-hoc par module, cf. .nk-audit-cat plus bas). */
table.nk-table thead th.nk-td--center { text-align: center; }
table.nk-table thead th.nk-td--right  { text-align: right; }

table.nk-table tbody tr {
  border-top: 1px solid var(--surface);
  cursor: pointer;
  transition: background var(--t-fast);
}
/* Zebra : lignes paires sur fond bleuté très clair (charte Nocika
   lot 1). `:hover` placé après pour gagner par ordre source à
   spécificité égale (0,1,1). */
table.nk-table tbody tr:nth-child(even) { background: var(--table-zebra); }
table.nk-table tbody tr:hover   { background: var(--table-hover); }
table.nk-table tbody tr.deleted { opacity: .4; }

/* Ligne ISOLÉE du reste du tableau : une entrée qui n'obéit pas aux mêmes
   règles que ses voisines et ne doit pas se lire comme elles (ex. le socle
   des filtres, groupe implicite qui ne s'affecte ni ne se désactive).
   Le sélecteur double la classe pour battre la zébrure (0,2,1 > 0,1,1), sans
   quoi une ligne isolée tombant sur un rang pair reprendrait le fond zébré.
   `:hover` reste plus spécifique encore, le survol continue donc de répondre. */
table.nk-table tbody tr.nk-tr--highlight.nk-tr--highlight { background: var(--warning-l); }
table.nk-table tbody tr.nk-tr--highlight td { border-bottom: 1px solid var(--warning); }
/* `tbody tr` porte `cursor: pointer` pour toute la table. Une ligne isolée
   n'ouvre rien au clic : garder la main du pointeur promettrait une action
   qui n'existe pas. Ses boutons, eux, rétablissent le curseur.
   ⚠️ `:not(.nk-clickable)` (2026-08-11) : ce sélectionneur battait `.nk-clickable`
   (0,2,1 contre 0,1,0), donc une ligne À LA FOIS surlignée et cliquable perdait
   la main du pointeur — l'inverse exact du mensonge que la règle existe pour
   éviter. Le cas est né avec la ligne cliquable de Filtres ▸ Contrôle ▸ Produits,
   qui surligne le dernier produit ouvert. La règle dit désormais ce qu'elle veut
   dire : rien à ouvrir SAUF si la ligne se déclare cliquable. */
table.nk-table tbody tr.nk-tr--highlight:not(.nk-clickable) { cursor: default; }

table.nk-table tbody td {
  padding: 10px 14px;
  vertical-align: middle;
  font-family: var(--font);
  font-size: 13px;
  color: var(--navy);
  font-weight: 400;
}

/* Variante dense — lignes compactes pour les listes à fort volume */
table.nk-table.nk-table--dense thead th { padding: 6px 10px; }
table.nk-table.nk-table--dense tbody td { padding: 6px 10px; font-size: 11px; }
table.nk-table.nk-table--dense tbody tr { border-top: 1px solid var(--border); }

/* Variante à colonnes équitables (largeur indépendante du contenu) — pour
   les grilles de saisie multi-langues (ex. grille de traduction SEO des
   pages CMS) où chaque colonne de langue doit avoir la même largeur. */
table.nk-table.nk-table--fixed { table-layout: fixed; }

/* Modificateurs de cellule — surchargent uniquement ce qui change */
.nk-td--num     { font-size: 12px; color: #64748b; white-space: nowrap; }
.nk-td--muted   { font-size: 11px; color: var(--muted); }
.nk-td--right   { text-align: right; white-space: nowrap; }
.nk-td--center  { text-align: center; }
.nk-td--bold    { font-weight: 700; }
/* Cascade plus spécifique pour battre `table.nk-table tbody td { font-weight: 400 }`
   qui sinon écrase la classe modificatrice quand elle est posée sur un `<td>`. */
table.nk-table tbody td.nk-td--bold { font-weight: 700; }
.nk-td--nowrap  { white-space: nowrap; }
/* Cellule alignée en HAUT (2026-08-24). Indispensable dès qu'une cellule
   `rowspan` coiffe plusieurs lignes : centrée, elle flotte au milieu du groupe
   et ne fait plus face à sa PREMIÈRE ligne — la revue des ALT montrait la
   vignette à hauteur de l'allemand alors qu'elle coiffe le français. Sert aussi
   à faire tenir sur la même ligne de base une étiquette courte et une zone de
   saisie multi-lignes, qui n'ont pas la même hauteur. */
.nk-td--top     { vertical-align: top; }
/* Ligne d'en-tête de groupe dans le tableau « Rapport d'inspection » occasion
   (fiche PIM › Occasion). Fond léger pour séparer visuellement les groupes. */
table.nk-table tbody tr.nk-occ-insp__group td { background: var(--surface); }
/* Table « Rapport d'inspection » : pas de zébrage des lignes — les
   regroupements sont identifiés uniquement par les lignes d'en-tête de groupe. */
table.nk-table.nk-occ-insp tbody tr:nth-child(even) { background: transparent; }
/* Ligne d'inspection masquée (non publiée, réactivable via ↺) : grisée. */
table.nk-table.nk-occ-insp tbody tr.nk-occ-insp__hidden { opacity: .45; }
.nk-td--sub        { font-size: 11px; color: #475569; margin-top: 2px; }
.nk-td--client     { font-size: 13px; font-weight: 600; color: #0f172a; }
.nk-td--amount     { font-size: 13px; font-weight: 700; color: #0f172a; }
.nk-td--amount-ht  { font-size: 11px; color: #94a3b8; margin-top: 2px; }

.nk-td--date       { vertical-align: middle; }
.nk-td--date-day   { font-size: 12px; color: #475569; font-weight: 500; }
.nk-td--date-time  { font-size: 10px; color: #94a3b8; margin-top: 1px; }

.nk-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  justify-content: flex-end;
}

/* Indicateur de tri ajouté dynamiquement par les modules JS. */
.nk-th__sort        { opacity: .3; font-size: 9px; margin-left: 3px; }
.nk-th__sort.active { opacity: 1; color: var(--cyan-d); }

/* Marge — couleurs sémantiques */
.nk-margin--good    { color: #16a34a; }
.nk-margin--ok      { color: #65a30d; }
.nk-margin--warning { color: var(--warning); }
.nk-margin--bad     { color: var(--danger); }

/* Menu d'actions contextuelles ancré aux lignes de tableau. */
.nk-row-menu { position: fixed; z-index: var(--z-tooltip); background: var(--white); border: 1px solid var(--border); border-radius: var(--r-md); box-shadow: var(--shadow-md); padding: 4px; min-width: 160px; opacity: 0; pointer-events: none; transition: opacity var(--t-fast); }
.nk-row-menu.open { opacity: 1; pointer-events: auto; }
.nk-row-menu__item { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-radius: var(--r-sm); cursor: pointer; font-size: 12px; font-weight: 500; color: var(--navy); border: none; background: transparent; width: 100%; text-align: left; font-family: var(--font); }
.nk-row-menu__item:hover { background: var(--surface); }
.nk-row-menu__item.danger { color: var(--danger); }
.nk-row-menu__item.danger:hover { background: var(--danger-l); }
.nk-row-menu__item svg { width: 13px; height: 13px; flex-shrink: 0; }
.nk-row-menu__sep { height: 1px; background: var(--surface); margin: 3px 0; }

.nk-list__state {
  display: flex; align-items: center; justify-content: center;
  flex-direction: column; gap: 10px;
  padding: 60px 20px;
  color: var(--muted); font-size: 13px; text-align: center;
}
.nk-list__state svg       { width: 32px; height: 32px; stroke: var(--border-d); flex-shrink: 0; }
.nk-list__state.error     { color: var(--danger); }
.nk-list__state.error svg { stroke: var(--danger-b); }

/* ============================================================
   DRAWER
   Panneau latéral hors flux, utilisé pour détails ou formulaires secondaires.
   Il doit rester monté hors des conteneurs à `overflow:hidden`.
   ============================================================ */
.nk-drawer__overlay { position: fixed; inset: 0; background: rgba(15,39,68,.35); z-index: var(--z-drawer); opacity: 0; transition: opacity var(--t-std); pointer-events: none; }
.nk-drawer__overlay.open { opacity: 1; pointer-events: auto; }
.nk-drawer { position: fixed; top: 0; right: 0; width: 360px; height: 100vh; background: var(--white); border-left: 1px solid var(--border); z-index: calc(var(--z-drawer) + 1); transform: translateX(100%); transition: transform .2s cubic-bezier(.4,0,.2,1); display: flex; flex-direction: column; }
.nk-drawer.open { transform: translateX(0); }
.nk-drawer__head { padding: 14px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 8px; flex-shrink: 0; background: var(--white); position: sticky; top: 0; z-index: 10; }
.nk-drawer__num { font-size: 11px; font-weight: 700; font-family: monospace; color: var(--navy); background: var(--surface); padding: 2px 7px; border-radius: var(--r-sm); flex-shrink: 0; }
.nk-drawer__client { flex: 1; min-width: 0; }
.nk-drawer__client-name { font-weight: 600; font-size: 13px; color: var(--navy); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nk-drawer__client-email { font-size: 11px; color: var(--muted); }
.nk-drawer__close { width: 26px; height: 26px; border-radius: var(--r-sm); border: none; background: var(--surface); cursor: pointer; display: flex; align-items: center; justify-content: center; color: var(--slate); flex-shrink: 0; }
.nk-drawer__close svg { width: 12px; height: 12px; }
/* Titre générique d'un drawer dont le head n'est pas une fiche client
   (le drawer client utilise plutôt `.nk-drawer__client-name`). */
.nk-drawer__title { font-size: 14px; font-weight: 700; color: var(--navy); flex: 1; }
.nk-drawer__body { padding: 14px 16px; flex: 1; overflow-y: auto; }
.nk-drawer__row { display: flex; justify-content: space-between; align-items: center; padding: 7px 0; border-bottom: 1px solid var(--surface); font-size: 12px; }
.nk-drawer__row:last-child { border-bottom: none; }
.nk-drawer__lbl { color: var(--slate); font-size: 11px; }
.nk-drawer__val { font-weight: 500; color: var(--navy); font-size: 12px; }
.nk-drawer__section { font-size: 10px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .6px; margin: 12px 0 6px; }
.nk-drawer__line { display: flex; align-items: flex-start; gap: 7px; padding: 7px 0; border-bottom: 1px solid var(--surface); font-size: 11px; }
.nk-drawer__line:last-child { border-bottom: none; }
.nk-drawer__item-ref { font-family: monospace; font-size: 10px; color: var(--muted); flex-shrink: 0; min-width: 52px; }
.nk-drawer__item-name { flex: 1; color: var(--navy); line-height: 1.4; }
.nk-drawer__item-qty { font-size: 10px; color: var(--muted); }
.nk-drawer__item-price { flex-shrink: 0; font-weight: 600; color: var(--navy); white-space: nowrap; }
.nk-drawer__total { background: var(--surface); border-radius: var(--r-md); padding: 10px 12px; margin-top: 12px; }
.nk-drawer__total-row { display: flex; justify-content: space-between; font-size: 12px; padding: 2px 0; }
.nk-drawer__total-row.main { font-weight: 700; font-size: 14px; border-top: 1px solid var(--border); margin-top: 5px; padding-top: 7px; }
.nk-drawer__actions { padding: 12px 16px; border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 8px; flex-shrink: 0; background: var(--white); position: sticky; bottom: 0; }
.nk-drawer__actions-row { display: flex; gap: 7px; }

/* ============================================================
   TIPS
   Accordéon natif `<details>` avec chevron pivotant à l'ouverture.
   On masque le marker natif (triangle Webkit / Firefox) pour piloter
   nous-mêmes le chevron via la classe `.nk-tips-chevron`.
   ============================================================ */
.nk-tips summary { list-style: none; outline: none; }
.nk-tips summary::-webkit-details-marker { display: none; }
.nk-tips summary::marker { display: none; }
.nk-tips-chevron { transition: transform .15s ease; }
.nk-tips[open] .nk-tips-chevron { transform: rotate(90deg); }

/* ============================================================
   PAGE HEADER
   En-tête commun aux sections de page : titre (gras, plus gros que
   le texte courant) + séparateur vertical + fil d'Ariane (muted) +
   spacer + actions (boutons à droite). Trait horizontal sous le bloc
   pour séparer du contenu de la page.
   ============================================================ */
/* Fil d'ariane de la fiche produit (PIM uniquement) : hauteur resserrée le
   2026-07-07 (moins de vide sous la context bar) — tailles de police inchangées. */
.nk-page-header { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; padding-bottom: 7px; border-bottom: 1px solid var(--border); margin-bottom: 10px; }
.nk-page-header__back {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; flex-shrink: 0;
  border: 1px solid var(--border-d); border-radius: var(--r-md);
  background: var(--white); color: var(--slate); cursor: pointer;
  transition: background .12s, color .12s, border-color .12s;
}
.nk-page-header__back:hover { background: var(--ecume); color: var(--navy); border-color: var(--muted); }
.nk-page-header__back:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.nk-page-header__title { font-size: 18px; font-weight: 700; color: var(--navy); line-height: 1.2; flex-shrink: 0; }
.nk-page-header__sep { width: 1px; height: 20px; background: var(--border-d); flex-shrink: 0; }
/* Séparateur en TIRET, et non en filet vertical comme `__sep` ci-dessus : il
   séparait le suivi de validation des actions de la section sur la même ligne
   (demande Lionel du 2026-08-14). SUPPRIMÉE le 2026-08-23 : le suivi de
   validation a quitté l'en-tête de section pour le panneau de complétude du
   menu, ce séparateur n'a donc plus rien à séparer et plus aucun émetteur.
   (Le lint de la règle d'or ne voit pas ce sens-là : il vérifie « classe émise
   ⇒ stylée », jamais l'inverse. Relevé en revue à froid.) */
.nk-page-header__crumb { display: inline-flex; align-items: center; flex-wrap: wrap; font-size: 12px; color: var(--muted); font-weight: 400; }
.nk-page-header__crumb a { color: var(--muted); text-decoration: none; }
.nk-page-header__crumb a:hover { color: var(--cyan-d); text-decoration: underline; }
.nk-page-header__crumb-sep { margin: 0 6px; color: var(--muted); }
.nk-page-header__crumb-cur { color: var(--text); }
.nk-page-header__spacer { flex: 1; }
.nk-page-header__actions { flex-shrink: 0; display: inline-flex; align-items: center; gap: 8px; }

/* Barre fil d'Ariane PIM (2026-07-19, demande Lionel) : bandeau « titre de
   rubrique + pastilles de suivi » figé (sticky) et COLLÉ sous la context bar,
   rendu comme un 2e bandeau pleine largeur.
   - Scopé `#nk-pim-root` : ne s'applique qu'à la fiche PIM (le seul contexte où
     `.nk-page-header` vit dans un panneau qui scrolle, `#nk-page-shell-content`).
   - `sticky; top:0` : reste visible pendant le scroll du contenu de section.
   - `z-index: 5` = stacking LOCAL (< 100, toléré par la règle z-index pour un
     sticky header dans un panneau) : au-dessus du contenu qui défile dessous,
     sous la topbar/overlays globaux.
   - `background: var(--surface)` : le contenu passe derrière sans transparence.
   - `margin: 0 -32px 12px` : marges latérales négatives = déborde le padding
     latéral (32px) du conteneur pour un fond + trait BORD À BORD ; marge haute
     nulle = collée à la context bar (le conteneur n'a plus de padding-top) ;
     12px sous la barre avant les boutons d'action.
   - `padding: 12px 32px` : espacement vertical ÉQUILIBRÉ autour de la flèche
     (haut = bas), latéral 32px pour réaligner le contenu sur le reste de la page.
   - `border-bottom: 2px var(--border-d)` : trait gris foncé sous la barre (même
     graisse/teinte que la bordure basse de la context bar). */
#nk-pim-root .nk-page-header {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--surface);
  margin: 0 -32px 12px;
  padding: 12px 32px;
  border-bottom: 2px solid var(--border-d);
}

/* Boutons d'action d'une section PIM (2026-07-19) : rangée sous la barre fil
   d'Ariane, alignés à GAUCHE. Extrait des styles inline de `sectionHeader`
   (une seule source, réutilisée par toutes les pages du PIM). */
.nk-pim-section-actions {
  display: flex;
  justify-content: flex-start;
  gap: var(--space-md);
  flex-wrap: wrap;
  margin: 0 0 var(--space-lg);
}

/* Modale « Génération automatique » (2026-07-20) : tableau explicatif de ce que
   fait le pipeline en un clic + état de la fiche. Rendu dans le corps DOM d'un
   NkConfirmModal (message = nœud). Styles centralisés ici pour éviter l'inline
   dans le JS. */
.nk-genauto { font-size: 13px; color: var(--text); line-height: 1.5; }
.nk-genauto__lead { margin: 0 0 12px; color: var(--muted); }
.nk-genauto__ready {
  display: flex; gap: 9px; align-items: flex-start;
  padding: 10px 12px; margin: 0 0 14px; font-size: 13px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm);
}
.nk-genauto__ready--warn {
  background: var(--warning-l, #fdeecf); border-color: var(--warning); color: var(--warning);
}
.nk-genauto__ready--warn b { color: var(--warning); }
.nk-genauto__wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--r-sm); }
.nk-genauto__table { width: 100%; border-collapse: collapse; font-size: 13px; }
.nk-genauto__table th {
  text-align: left; font-size: 12px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: var(--muted); padding: 8px 10px; white-space: nowrap;
  border-bottom: 1px solid var(--border); background: var(--surface);
}
.nk-genauto__table td { padding: 7px 12px; border-bottom: 1px solid var(--border); vertical-align: top; }
.nk-genauto__table tr:last-child td { border-bottom: 0; }
.nk-genauto__el { font-weight: 700; color: var(--text); white-space: nowrap; }
.nk-genauto__st { font-size: 13px; white-space: nowrap; }
.nk-genauto__st--ok   { color: var(--success); font-weight: 600; }
.nk-genauto__st--warn { color: var(--warning); font-weight: 600; }
.nk-genauto__st--off  { color: var(--muted); }
/* Colonne « ce que fait » dominante : sur une modale large, la description
   occupe l'essentiel → 2 lignes au lieu de 4-5, modale moins haute. */
.nk-genauto__do { color: var(--muted); font-size: 13px; width: 46%; }
.nk-genauto__tag {
  display: inline-block; font-size: 11px; font-weight: 700; padding: 2px 8px;
  border-radius: 999px; border: 1px solid; white-space: nowrap;
}
.nk-genauto__tag + .nk-genauto__tag { margin-top: 4px; }
.nk-genauto__tag--ctrl   { color: var(--warning); background: var(--warning-l, #fdeecf); border-color: var(--warning); }
.nk-genauto__tag--push   { color: var(--success); background: var(--success-l, #dcfce7); border-color: var(--success); }
.nk-genauto__tag--propal { color: var(--cyan-d, #0e7490); background: var(--cyan-l, #d3f6fb); border-color: var(--cyan); }
.nk-genauto__notes { margin: 12px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 6px; }
.nk-genauto__notes li { font-size: 13px; color: var(--muted); padding-left: 18px; position: relative; }
.nk-genauto__notes li::before { content: "\24D8"; position: absolute; left: 0; color: var(--cyan); font-weight: 700; }
.nk-genauto__cost { margin: 12px 0 0; font-size: 13px; color: var(--muted); }

/* Picker de marque (2026-07-20) — modale de recherche + auto-proposition depuis
   le titre produit, remplace le <select> de centaines de lignes (PIM Synthèse).
   Rendu dans une modale `.nk-modal__overlay` métier. */
.nk-brandpick__sugg {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 0 0 12px; padding: 10px 12px;
  background: var(--ia-l); border: 1px solid var(--ia-b); border-radius: var(--r-sm);
}
.nk-brandpick__sugg-label {
  font-size: 12px; font-weight: 700; color: var(--ia);
  text-transform: uppercase; letter-spacing: .04em;
}
/* Hauteur FIXE (pas max-height) : la modale ne change plus de taille quand le
   filtre réduit la liste — l'espace vide reste à l'intérieur du scroll. */
.nk-brandpick__list { display: flex; flex-direction: column; gap: 2px; height: 44vh; overflow-y: auto; }
.nk-brandpick__item {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  width: 100%; text-align: left; padding: 8px 12px;
  border: 1px solid transparent; border-radius: var(--r-sm);
  background: transparent; color: var(--text); font-family: inherit; font-size: 14px; cursor: pointer;
}
.nk-brandpick__item:hover { background: var(--surface); border-color: var(--border); }
/* `display:flex` ci-dessus écrase l'attribut `hidden` (UA, spécificité nulle) —
   cette règle plus spécifique rétablit le masquage du filtre de recherche. */
.nk-brandpick__item[hidden] { display: none; }
.nk-brandpick__item.is-current { background: var(--cyan-l); color: var(--navy); font-weight: 600; }
.nk-brandpick__cur { font-size: 11px; font-weight: 600; color: var(--muted); flex: none; }
.nk-brandpick__empty { padding: 14px; text-align: center; color: var(--muted); font-size: 13px; }

/* Sélecteur de marque PARTAGÉ (nk-brand-picker.js, 2026-07-29) : sous-titre
   compteur, bloc « marque actuelle », liste groupée par lettre + index A-Z,
   sélection en deux temps. Réutilise .nk-brandpick__list / __item / __cur / __empty. */
.nk-brandpick__count { font-size: var(--fs-sm); color: var(--muted); margin-top: 2px; }
.nk-brandpick__current {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 12px; border-radius: var(--r-md);
  background: var(--cyan-l); border: 1px solid var(--cyan);
}
.nk-brandpick__current-ico {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%; flex: none;
  background: var(--white); color: var(--cyan-d); font-weight: 700;
}
.nk-brandpick__current-lbl { font-size: 11px; font-weight: 700; color: var(--cyan-d); text-transform: uppercase; letter-spacing: .04em; }
.nk-brandpick__current-name { font-size: 15px; font-weight: 600; color: var(--ink); }
.nk-brandpick__remove { color: var(--danger); }
.nk-brandpick__listwrap { display: flex; gap: 6px; flex: 1; min-height: 0; }
.nk-brandpick__listwrap .nk-brandpick__list { flex: 1; height: auto; min-height: 0; position: relative; }
/* NON sticky volontairement : un entête sticky rendait le saut d'index A-Z peu
   fiable (ne remontait pas). Bande simple, bien visible, qui défile normalement. */
.nk-brandpick__group {
  padding: 6px 12px 5px; font-size: 12px; font-weight: 700; color: var(--cyan-d);
  text-transform: uppercase; letter-spacing: .08em;
  background: var(--surface); border-bottom: 1px solid var(--border);
}
.nk-brandpick__group[hidden] { display: none; }
/* Sélection de travail : anneau accentué (distinct du fond plein « marque actuelle »). */
.nk-brandpick__item.is-pending { background: var(--cyan-l); box-shadow: inset 0 0 0 2px var(--cyan); color: var(--navy); font-weight: 600; }
.nk-brandpick__az { display: flex; flex-direction: column; flex: none; padding-top: 2px; }
.nk-brandpick__az-letter {
  border: 0; background: transparent; cursor: pointer; font-family: inherit;
  font-size: 11px; line-height: 1.12; padding: 0 3px; color: var(--cyan-d); font-weight: 600;
}
.nk-brandpick__az-letter:hover:not(.is-empty) { color: var(--navy); }
.nk-brandpick__az-letter.is-empty { color: var(--border); cursor: default; }
/* Padding + bordure indispensables : le .nk-modal__foot de base n'a AUCUN padding,
   donc sans ça les boutons sont collés aux bords et rognés par l'arrondi de la modale. */
.nk-brandpick__foot { align-items: center; padding: 12px 18px; border-top: 1px solid var(--border); }
.nk-brandpick__foot-status { font-size: var(--fs-sm); color: var(--muted); }

/* ============================================================
   SEGMENT
   Contrôle segmenté (V1 / V2, statuts OOS, etc.). Visuellement aligné
   sur les pills (même radius, même hauteur). Le bouton actif prend
   l'accent cyan.
   ============================================================ */
.nk-segment { display: inline-flex; border: 1px solid var(--border); border-radius: 999px; overflow: hidden; background: var(--white); }
.nk-segment__btn { padding: 4px 14px; background: transparent; border: 0; cursor: pointer; font-family: inherit; font-size: 12px; font-weight: 600; color: var(--slate); transition: background .12s ease, color .12s ease; }
.nk-segment__btn + .nk-segment__btn { border-left: 1px solid var(--border); }
.nk-segment__btn:hover:not(.nk-segment__btn--active) { background: var(--surface); }
.nk-segment__btn--active { background: var(--cyan-l); color: var(--cyan-d); }
.nk-segment[aria-busy="true"] { opacity: .6; pointer-events: none; }

/* ============================================================
   MODALES
   Overlay centré, tailles pilotées par modificateurs.
   Les composants métier ajoutent ensuite leurs propres variantes de largeur
   ou de priorité de z-index.
   ============================================================ */
.nk-modal__overlay {
  position: fixed; inset: 0;
  background: rgba(15,23,42,.45);
  z-index: var(--z-modal);
  display: flex; align-items: center; justify-content: center;
  opacity: 0; pointer-events: none;
  transition: opacity var(--t-std);
}
.nk-modal__overlay.open { opacity: 1; pointer-events: auto; }

.nk-modal {
  background: var(--white);
  border-radius: var(--r-xl);
  padding: 22px 24px;
  width: 92%; max-width: 460px;
  transform: scale(.97);
  transition: transform var(--t-std);
  font-family: var(--font);
}
.nk-modal__overlay.open .nk-modal { transform: scale(1); }

.nk-modal--sm { max-width: 360px; }
.nk-modal--lg { max-width: 600px; }

.nk-modal__head  { padding: 15px 18px 13px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }
/* Taille de police par défaut du corps de modale relevée le 2026-06-09 pour
   lisibilité hub-wide : titre var(--fs-lg)=18px, message 15px (≈ ancienne
   modale de création +10 %). Toute modale via NkConfirmModal en hérite. */
.nk-modal__title { font-size: var(--fs-lg); font-weight: var(--fw-bold); color: var(--navy); margin-bottom: 5px; }
.nk-modal__sub   { font-size: 15px; color: var(--slate); margin-bottom: 18px; line-height: 1.55; }
/* Corps de modale en NOIR (demande Lionel, 2026-08-24). `--slate` convient à un
   sous-titre qui accompagne un titre ; il est trop pâle dès que le texte de la
   modale EST l'information à lire — une annonce d'action et ses règles, par
   exemple. Le token est `--ink`, la couleur de texte unique des blocs et des
   formulaires (CLAUDE.md § Tokens). Modifieur et non bascule de la base : les
   sous-titres existants gardent leur gris. */
.nk-modal__sub--ink { color: var(--ink); }
.nk-modal__body  { display: flex; flex-direction: column; gap: 10px; margin-bottom: 18px; }
/* `align-items: center` ajouté le 2026-08-17 : le pied peut désormais porter un
   texte à gauche des boutons (avertissement d'enregistrement de la fenêtre
   Image). Sans lui, l'étirement par défaut collait ce texte en haut du pied.
   Sans effet sur les modales existantes — leurs boutons ont la même hauteur. */
.nk-modal__foot  { display: flex; gap: 8px; justify-content: flex-end; align-items: center; }
/* Bouton de pied DÉSACTIVÉ. `NkConfirmModal` pose la couleur de son bouton
   primaire en style INLINE : l'attribut `disabled` bloquait le clic sans que
   rien ne se voie, et un bouton inactif qui a l'air actif est pire qu'un bouton
   toujours actif. `opacity` n'étant pas écrite en inline, une règle suffit —
   inutile d'aller surcharger la couleur. Vaut pour TOUTE modale qui grise son
   bouton, pas seulement la fenêtre Image. */
.nk-modal__foot button:disabled { opacity: .45; cursor: not-allowed; }

/* ─────────────────────────────────────────────
   LISTE ENCADRÉE DANS UNE MODALE DE CONSULTATION (2026-08-04)
   Une modale d'information (`NkConfirmModal` type `notice`) montre parfois la
   liste de ce qu'on vient consulter. Un filet au-dessus ET en dessous : elle se
   lit comme un bloc distinct du texte qui l'introduit et de celui qui
   l'explique, sans encadré ni fond qui la feraient passer pour une zone
   actionnable.
   AUCUNE `font-size` ici, volontairement : le corps hérite des 15 px de
   `.nk-modal__sub` (base du hub). La redéclarer figerait une valeur qu'aucun
   token ne couvre, et la ferait dériver dès que la base bouge.
   Ses VOISINES `.nk-modal-read__p` et `.nk-modal-read__row` ont été supprimées
   le 2026-08-05 : la première ne posait qu'un `margin: 0` (→ `.nk-m-0`, plus
   `.nk-mt-xl` quand deux paragraphes se suivent), la seconde une rangée flex
   déjà couverte par `.nk-inline-row`.
   ───────────────────────────────────────────── */
.nk-modal-read__list {
  margin: var(--space-xl) 0;
  padding: var(--space-lg) 0;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

/* ─────────────────────────────────────────────
   BRIEFING DE GÉNÉRATION (pim-brief-modal.js, 2026-07-20)
   Modale IA de cadrage de la génération de contenu PIM : profondeur éditoriale
   (segmenté), univers + public (chips mono-sélection), consigne libre. Réutilise
   le shell modal officiel ; ces classes ne stylent QUE le contenu propre au
   briefing. Accent violet IA via les tokens --ia* (jamais d'hexa en dur). Aucune
   longueur en nombre n'est affichée (source unique côté serveur) — seul un
   libellé qualitatif accompagne la profondeur.
───────────────────────────────────────────── */
.nk-brief { width: 94%; max-width: 680px; padding: 0; display: flex; flex-direction: column; max-height: 88vh; overflow: hidden; }
.nk-brief__head { align-items: flex-start; }
.nk-brief__titlewrap { display: flex; align-items: center; gap: 10px; min-width: 0; }
.nk-brief__spark { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; flex-shrink: 0; border-radius: var(--r-md); background: var(--ia-l); color: var(--ia); font-size: 16px; }
.nk-brief__title { margin: 0; }
.nk-brief__subtitle { font-size: 13px; color: var(--slate); margin-top: 2px; line-height: 1.4; }
.nk-brief__body { display: block; margin: 0; padding: 22px 26px; overflow-y: auto; }
.nk-brief__ctx { font-size: 13px; color: var(--slate); margin-bottom: 16px; }
.nk-brief__ctx b { color: var(--ink); font-weight: 600; }
.nk-brief__axis { margin-bottom: 26px; }
.nk-brief__axis-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.nk-brief__label { font-size: 15px; font-weight: 600; color: var(--ink); margin-bottom: 10px; }
.nk-brief__seg, .nk-brief__chips { display: flex; flex-wrap: wrap; gap: 6px; }
.nk-brief__seg-btn, .nk-brief__chip {
  font-family: inherit; font-size: 13px; font-weight: 600;
  padding: 7px 14px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--white); color: var(--slate);
  cursor: pointer; transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.nk-brief__seg-btn:hover:not(.is-on), .nk-brief__chip:hover:not(.is-on) { border-color: var(--ia-b); background: var(--ia-l); color: var(--ia-d); }
.nk-brief__seg-btn.is-on, .nk-brief__chip.is-on { background: var(--ia); border-color: var(--ia); color: var(--white); }
.nk-brief__hint { margin-top: 8px; font-size: 13px; color: var(--slate); line-height: 1.45; }
.nk-brief__secondary { display: flex; align-items: center; gap: 10px; margin-top: 4px; }
.nk-brief__secondary-spacer { flex: 1; }
.nk-brief__add { background: none; border: 0; padding: 0; font-family: inherit; font-size: 13px; font-weight: 600; color: var(--ia); cursor: pointer; }
.nk-brief__add:hover { text-decoration: underline; }
.nk-brief__consigne { margin-top: 10px; }
.nk-brief__consigne-input { width: 100%; font-size: 13px; resize: vertical; }
.nk-brief__foot { margin: 0; padding: 12px 18px; border-top: 1px solid var(--border); }

/* ─────────────────────────────────────────────
   ÉDITEUR DE CHAMP HTML MONO-CHAMP (nk-field-editor.js)
   Modale large : header + toolbar + zone visuelle (contenteditable) +
   bascule code source (</>). Visuel aligné sur l'éditeur de bloc éditorial.
───────────────────────────────────────────── */
.nk-modal--editor { width: 90vw; max-width: 900px; height: 85vh; padding: 0; display: flex; flex-direction: column; overflow: hidden; }
.nk-modal--editor .nk-modal__head { gap: 8px; }

/* REVUE DES ALT D'IMAGES (section Images du PIM, 2026-08-20).
   `.nk-modal--editor` donne la hauteur (85vh) et `padding: 0`, mais il ne suffit
   PAS à produire l'anatomie « en-tête fixe / corps défilant / pied fixe » : le
   `.nk-modal__body` de base ne défile pas, et le `.nk-modal__foot` de base n'a
   aucun padding (cf. la remarque de `.nk-brandpick__foot`). Chaque écran pose
   donc les siens, comme `.nk-tradg`. Sans ces deux règles, la table des ALT
   déborde et le pied sort de l'écran — constaté le 2026-08-20 sur une fiche de
   12 images, boutons « Fermer » et « Enregistrer » injoignables. */
.nk-altrev .nk-modal__body { padding: 14px 18px; margin-bottom: 0; overflow-y: auto; flex: 1 1 auto; min-height: 0; }
.nk-altrev .nk-modal__foot { padding: 12px 18px; border-top: 1px solid var(--border); }

/* ============================================================
   DRAPEAUX DE LANGUE (2026-08-20)

   Dessinés en CSS, JAMAIS en emoji : les emoji drapeaux ne sont pas rendus
   sous Windows, ils y apparaissent en deux lettres grises — la moitié du parc
   verrait donc « FR » au lieu du drapeau.

   Une langue = une classe `.nk-flag--<code>`. Ajouter une langue au site ne
   demande donc QUE d'ajouter une règle ici, sans toucher au JavaScript.
   FR, IT et DE sont de simples bandes. EN est un Union Jack SIMPLIFIÉ (croix
   droites, sans les diagonales) : à 23x16 px les diagonales deviennent une
   bouillie, la croix suffit à le reconnaître.
   ============================================================ */
.nk-flag {
  display: inline-block; position: relative;
  width: 23px; height: 16px; border-radius: 3px;
  box-shadow: inset 0 0 0 1px rgba(15, 23, 42, .18);
  vertical-align: middle;
}
/* Drapeau CLIQUABLE : c'est un vrai <button>, on neutralise son habillage. */
.nk-flag--btn { padding: 0; border: 0; background-color: transparent; cursor: pointer; }
.nk-flag--btn:focus-visible { outline: 2px solid var(--cy); outline-offset: 2px; }
/* Drapeau en LECTURE SEULE (image principale) : pas de curseur trompeur. */
.nk-flag--lock { cursor: not-allowed; }

.nk-flag--fr { background-image: linear-gradient(90deg, #002395 0 33.34%, #fff 33.34% 66.67%, #ed2939 66.67% 100%); }
.nk-flag--it { background-image: linear-gradient(90deg, #009246 0 33.34%, #fff 33.34% 66.67%, #ce2b37 66.67% 100%); }
.nk-flag--de { background-image: linear-gradient(180deg, #000 0 33.34%, #dd0000 33.34% 66.67%, #ffce00 66.67% 100%); }
.nk-flag--es { background-image: linear-gradient(180deg, #aa151b 0 25%, #f1bf00 25% 75%, #aa151b 75% 100%); }
/* Union Jack : l'ordre des couches COMPTE — en CSS le premier dégradé est le
   plus haut. On empile donc, du dessus vers le dessous : croix rouge, croix
   blanche, diagonales blanches, fond bleu. Sans les diagonales le drapeau se
   lit comme un drapeau nordique (constaté à l'écran le 2026-08-20) : c'est
   elles, pas la croix, qui le rendent reconnaissable à 23 px. */
.nk-flag--en {
  background-color: #012169;
  background-image:
    linear-gradient(90deg, transparent 0 43%, #c8102e 43% 57%, transparent 57%),
    linear-gradient(180deg, transparent 0 37%, #c8102e 37% 63%, transparent 63%),
    linear-gradient(90deg, transparent 0 37%, #fff 37% 63%, transparent 63%),
    linear-gradient(180deg, transparent 0 28%, #fff 28% 72%, transparent 72%),
    linear-gradient(to bottom right, transparent calc(50% - 1.5px), #fff calc(50% - 1.5px) calc(50% + 1.5px), transparent calc(50% + 1.5px)),
    linear-gradient(to bottom left, transparent calc(50% - 1.5px), #fff calc(50% - 1.5px) calc(50% + 1.5px), transparent calc(50% + 1.5px));
}

/* Langue DÉSACTIVÉE pour cette image : grisé ET barré. Le grisé seul se
   confond avec un drapeau peu contrasté (l'allemand, notamment) — la barre
   dit sans ambiguïté « pas ici ». */
.nk-flag--off { filter: grayscale(1); opacity: .5; }
.nk-flag--off::after {
  content: ""; position: absolute; inset: 0; border-radius: 3px;
  background: linear-gradient(to top right, transparent 44%, var(--danger) 44% 56%, transparent 56%);
}

/* Refus d'un garde-fou : l'état ne change pas, la secousse dit que le geste a
   été VU et rejeté — sans elle, un clic sans effet passe pour un bug. */
@keyframes nk-shake {
  10%, 90% { transform: translateX(-1px); }
  20%, 80% { transform: translateX(2px); }
  30%, 50%, 70% { transform: translateX(-3px); }
  40%, 60% { transform: translateX(3px); }
}
.nk-shake { animation: nk-shake .5s cubic-bezier(.36, .07, .19, .97) both; }
@media (prefers-reduced-motion: reduce) { .nk-shake { animation: none; } }

/* Ligne « Langues » sous une carte d'image du rail PIM.
   Espacement à 4 px et libellé sans deux-points : sur une carte NON principale
   le bouton étoile réduit la colonne à ~155 px pour ~170 px nécessaires, et le
   quatrième drapeau passait à la ligne sur chaque carte — douze fois sur une
   fiche de douze images. Mesuré à l'écran, pas estimé. */
.nk-img-langs { flex: 1 0 100%; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: var(--fs-sm); line-height: 1.3; color: var(--muted); }
.nk-fe__overlay { align-items: center; justify-content: center; }
.nk-fe__badge { font-size: 9px; padding: 1px 5px; }
.nk-fe__stats { margin-left: 4px; }
.nk-fe__foot { padding: 10px 16px; border-top: 1px solid var(--border); background: var(--surface); margin-bottom: 0; }
.nk-fe__toolbar { display: flex; align-items: center; gap: 2px; padding: 6px 10px; border-bottom: 1px solid var(--border); background: var(--surface); flex-wrap: wrap; }
.nk-fe__btn { display: inline-flex; align-items: center; justify-content: center; min-width: 28px; height: 28px; padding: 0 6px; border: none; background: transparent; color: var(--navy); cursor: pointer; border-radius: var(--r-sm); font-size: 13px; }
.nk-fe__btn:hover { background: var(--border); }
.nk-fe__btn.on { background: var(--cyan); color: #fff; }
/* Icônes d'encadré colorées (alerte / info / OK), même code couleur que les
   encadrés rendus dans la zone éditable. */
.nk-fe__btn--alert { color: #dc2626; }
.nk-fe__btn--info  { color: #d97706; }
.nk-fe__btn--ok    { color: #16a34a; }
/* Bouton OPT-IN « ✦ Ajouter une consigne » (consignes de rédaction apprises) :
   poussé à droite de la barre (margin-left:auto), accent IA violet, désactivé
   tant qu'aucune sélection non vide n'existe dans la zone éditable. */
.nk-fe__btn--rule { margin-left: auto; gap: 5px; padding: 0 10px; color: var(--ia); font-weight: 600; font-size: 13px; white-space: nowrap; }
.nk-fe__btn--rule:hover:not(:disabled) { background: var(--ia-l); color: var(--ia); }
.nk-fe__btn--rule:disabled { color: var(--muted); opacity: .5; cursor: not-allowed; }
.nk-fe__sep { width: 1px; height: 18px; background: var(--border); margin: 0 4px; flex-shrink: 0; }
.nk-fe__fields { padding: 12px 20px 4px; border-bottom: 1px solid var(--border); background: var(--surface); }
.nk-fe__statsbar { padding: 4px 14px; border-top: 1px solid var(--border); background: var(--surface); text-align: right; }
.nk-fe__body { flex: 1; display: flex; flex-direction: column; overflow: hidden; min-height: 0; }
.nk-fe__rich { flex: 1; padding: 18px 20px; outline: none; font-size: 14px; line-height: 1.8; color: var(--navy); overflow: auto; }
.nk-fe__html { flex: 1; width: 100%; padding: 18px 20px; border: none; border-top: 1px solid var(--border); resize: none; outline: none; color: var(--navy); background: var(--surface); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; line-height: 1.5; }
/* Variante INLINE de l'éditeur de champ (NkFieldEditor.mount) : boîte bordée
   autonome dans la page (pas de contexte modale flex), hauteur bornée. */
.nk-fe--inline { border: 1px solid var(--border); border-radius: var(--r-md); overflow: hidden; background: var(--white); }
.nk-fe--inline .nk-fe__toolbar { border-radius: var(--r-md) var(--r-md) 0 0; }
.nk-fe--inline .nk-fe__rich { min-height: 110px; max-height: 320px; padding: 12px 14px; line-height: 1.7; }
.nk-fe--inline .nk-fe__html { min-height: 110px; max-height: 320px; padding: 12px 14px; }
/* Aperçu rendu d'un contenu HTML (description occasion, etc.). */
.nk-html-preview { border: 1px solid var(--border); border-radius: var(--r-md); padding: 14px 16px; background: var(--white); max-height: 340px; overflow: auto; font-size: 13px; line-height: 1.7; color: var(--navy); }
/* Variante iframe (aperçu sandboxé, ex. fiche CMS) : un iframe n'a pas de
   hauteur intrinsèque (150px UA) — on cale la hauteur sur le max-height du
   composant pour un rendu identique à la variante div. */
iframe.nk-html-preview { width: 100%; height: 340px; }

/* ── `.nk-richtext` — « ce conteneur rend du HTML éditorial » ──────────────
   Ajoutée le 2026-08-23. Les règles ci-dessous ÉNUMÉRAIENT leurs conteneurs, et
   cette liste a grossi DEUX FOIS, chaque fois après un défaut vu en production :
   `.nk-pb-preview` le 2026-07-30 (encadré rendu en texte nu dans la carte),
   `.nk-pim-legacy-panel` pour les tableaux. Un troisième cas est arrivé le
   2026-08-23 : l'aperçu multilingue rendait un `nc-alert` sans son encadré.

   Une énumération qu'on doit compléter à chaque nouveau conteneur finit
   toujours par en oublier un, et l'oubli est MUET — le texte s'affiche, sans sa
   mise en forme. `.nk-richtext` est la porte d'entrée : un conteneur qui rend du
   HTML d'éditeur la porte, et il hérite de tout.

   Les quatre conteneurs historiques restent listés : les migrer demanderait de
   toucher quatre modules, et l'ajout est déjà additif. À reprendre au fil de
   l'eau, en posant `.nk-richtext` sur chacun puis en retirant son nom d'ici.

   Source de vérité unique pour ces encadrés — pas d'injection CSS runtime. */
.nk-fe__rich .nc-alert, .nk-html-preview .nc-alert, .nk-pb-richtext .nc-alert, .nk-pb-preview .nc-alert, .nk-richtext .nc-alert { background: #fef2f2; border-left: 4px solid #dc2626; color: #991b1b; padding: 8px 12px; margin: 6px 0; border-radius: 4px; }
.nk-fe__rich .nc-info,  .nk-html-preview .nc-info,  .nk-pb-richtext .nc-info,  .nk-pb-preview .nc-info,  .nk-richtext .nc-info  { background: #fffbeb; border-left: 4px solid #d97706; color: #92400e; padding: 8px 12px; margin: 6px 0; border-radius: 4px; }
.nk-fe__rich .nc-ok,    .nk-html-preview .nc-ok,    .nk-pb-richtext .nc-ok,    .nk-pb-preview .nc-ok,    .nk-richtext .nc-ok    { background: #f0fdf4; border-left: 4px solid #16a34a; color: #166534; padding: 8px 12px; margin: 6px 0; border-radius: 4px; }

/* Tableaux des rendus HTML riches (aperçu d'édition, cartes de blocs, et panneau
   « Ancienne description » brut de PrestaShop). Ce HTML importé ne porte AUCUNE
   classe `.nk-table` → sans ces règles le tableau s'affichait sans la moindre
   bordure (illisible, ex. tableau des dimensions d'une planche). Bordures légères
   + entête discret, via les tokens nocika.css. Ajout 2026-07-30 (décision produit).
   Le tableau défile horizontalement plutôt que de déborder la page (contenu large
   importé, largeurs inline `width:…px`). */
.nk-html-preview table, .nk-fe__rich table, .nk-pb-preview table, .nk-pim-legacy-panel table, .nk-richtext table { border-collapse: collapse; margin: var(--space-md) 0; max-width: 100%; }
.nk-html-preview .table-responsive, .nk-fe__rich .table-responsive, .nk-pb-preview .table-responsive, .nk-pim-legacy-panel .table-responsive, .nk-richtext .table-responsive { overflow-x: auto; }
.nk-html-preview th, .nk-html-preview td,
.nk-fe__rich th, .nk-fe__rich td,
.nk-pb-preview th, .nk-pb-preview td,
.nk-pim-legacy-panel th, .nk-pim-legacy-panel td,
.nk-richtext th, .nk-richtext td { border: 1px solid var(--border-d); padding: 5px 9px; text-align: left; vertical-align: top; }
.nk-html-preview th, .nk-fe__rich th, .nk-pb-preview th, .nk-pim-legacy-panel th, .nk-richtext th { background: var(--surface); font-weight: 600; }

/* Marqueur « iframe · <hôte> » posé par le module `pim_legacy_description` à la
   place d'une <iframe> de l'ancienne description (jamais rendue inline — sécurité).
   Encadré rouge discret ; la classe est conservée par le sanitizer riche.
   Ajout 2026-07-30 (retour de revue MR !222 : ex-style inline hex → tokens). */
.nk-legacy-embed { display: inline-block; border: 2px solid var(--danger); background: var(--danger-l); color: var(--danger); padding: var(--space-xs) var(--space-md); border-radius: var(--r-sm); font-weight: var(--fw-semibold); font-size: var(--fs-sm); }

/* Boutons de l'entête du panneau « Ancienne description » ([HTML] / croix) et
   modale de consultation du HTML brut — styles extraits ici (retour de revue
   MR !222 : plus de style inline ni de px en dur). Le corps de la modale
   (`__code`) est à `--fs-base` = 13px, plancher de police des modales. */
.nk-legacy-panel__btn { padding: var(--space-xs) var(--space-md); font-size: var(--fs-sm); color: var(--danger); }
.nk-legacy-panel__btn--label { font-weight: var(--fw-semibold); }
.nk-legacy-html-modal__intro { margin: 0 0 var(--space-md); font-size: var(--fs-base); color: var(--muted); line-height: 1.5; }
.nk-legacy-html-modal__code { width: 100%; box-sizing: border-box; min-height: 320px; max-height: 52vh; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: var(--fs-base); line-height: 1.5; white-space: pre; overflow: auto; resize: vertical; }
.nk-legacy-html-modal__actions { margin-top: var(--space-lg); display: flex; justify-content: flex-end; }

/* Modale Comparer (NkCompareModal) — rythme vertical du contenu de bloc.
   Le reset global (`*{margin:0;padding:0}`) annule les marges natives des
   <p>/<ul> : les paragraphes d'un bloc éditorial s'affichaient collés, sans
   respiration (relevé 2026-07-09 sur Description longue). Scopé à la modale
   (panneau proposition IA ET zone éditable). */
#nk-pim-compare-modal p { margin: 0 0 .75em; }
#nk-pim-compare-modal p:last-child { margin-bottom: 0; }
#nk-pim-compare-modal ul, #nk-pim-compare-modal ol { margin: 0 0 .75em; padding-left: 1.3em; }
#nk-pim-compare-modal li { margin-bottom: .25em; }

/* ============================================================
   CONTEXT BAR
   Barre d'identité de module : titre, sélecteurs, statut, actions rapides.
   Elle reste compacte pour laisser la place au contenu des écrans métier.
   ============================================================ */
.nk-context-bar {
  display: flex; align-items: center; gap: 8px;
  padding: 0 20px; height: 50px;
  background: var(--ecume);
  border-bottom: 2px solid var(--border-d);
  flex-shrink: 0; width: 100%;
}

/* Trait vertical de séparation. Déclaration UNIQUE depuis le 2026-08-05 : une
   seconde règle plus bas ajoutait `color` — mort, le span est vide — et la
   marge latérale. Les deux fusionnées ici ; la marge est conservée telle quelle,
   elle donne au trait plus d'air que le `gap: 8px` de la barre et vaut pour
   toutes les context bars du hub. */
.nk-context-bar__sep    { width: 1px; height: 22px; margin: 0 6px; background: var(--border-d); flex-shrink: 0; }
/* 13 → 15 le 2026-06-12 : titre de page aligné sur la base 15px hub-wide. */
.nk-context-bar__num    { font-size: 15px; font-weight: 700; color: var(--navy); }

.nk-context-bar__select {
  height: 28px;
  padding: 0 26px 0 9px;
  border: 1px solid var(--border-d);
  border-radius: var(--r-md);
  background: var(--white);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2.5' stroke-linecap='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 7px center;
  appearance: none; -webkit-appearance: none;
  font-family: var(--font); font-size: 12px; font-weight: 600; color: var(--navy);
  outline: none; cursor: pointer;
  transition: border-color var(--t-fast);
}
.nk-context-bar__select:focus { border-color: var(--cyan); }

.nk-context-bar__status               { font-size: 12px; font-weight: 600; display: inline-flex; align-items: center; gap: 4px; }
.nk-context-bar__status--ok           { color: var(--success); }
.nk-context-bar__status--dirty        { color: var(--warning); }

.nk-context-bar__title { font-size: 13px; font-weight: 600; color: var(--navy); }
.nk-context-bar__sub   { color: var(--slate); font-weight: 400; font-size: 14px; }
.nk-context-bar__tabs  { display: flex; gap: 4px; margin-left: 16px; }
/* Onglets EN TÊTE de barre : le retrait de 16px sépare les onglets du titre
   qu'ils suivent d'habitude. Quand ils ouvrent la barre, ce retrait s'ajoute au
   padding et décale le premier onglet sans raison. */
.nk-context-bar__tabs--lead { margin-left: 0; }
/* Onglets qui changent de SUJET et non de vue : ils vivent dans le bandeau
   PRINCIPAL, à côté du nom de l'écran, et doivent peser le même poids que lui
   — d'où le corps 15, celui de `.nk-context-bar__num` (Lionel, 2026-08-07).
   À ne pas employer pour des onglets de présentation, qui restent en `--sm`
   dans le bandeau secondaire : la taille est ce qui distingue les deux. */
.nk-context-bar__tabs--lg .nk-btn { font-size: 15px; }

/* ─── PANNEAU « AFFINER » ───
   Colonne de raffinement d'une liste : familles, statuts, tout critère qui
   se choisit dans une liste PLATE plutôt que dans un menu déroulant. Une
   liste déroulante fermée ne montre ni les valeurs disponibles ni leur poids ;
   c'est exactement ce qu'on veut voir avant de choisir. */
.nk-affiner__titre {
  padding: var(--space-md) var(--space-md) var(--space-xs);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  letter-spacing: .5px;
  text-transform: uppercase;
  color: var(--muted);
}
.nk-affiner__item {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  width: 100%;
  padding: 7px var(--space-md);
  border: 0;
  background: transparent;
  font-family: inherit;
  font-size: var(--fs-base);
  color: var(--ink);
  text-align: left;
  cursor: pointer;
  transition: background var(--t-fast);
}
.nk-affiner__item:hover { background: var(--surface); }
.nk-affiner__item.is-active { background: var(--cyan-l); font-weight: var(--fw-semibold); color: var(--cyan-d); }
/* Sous-famille : 3ᵉ niveau, révélé sous la famille active seulement. */
.nk-affiner__item--sous { padding-left: var(--space-2xl); font-size: 12px; }

/* En-tête de GROUPE dans un tableau : une ligne qui nomme le paquet suivant.
   `<tr>` porteur et non un second tableau par groupe — des tableaux distincts
   auraient des colonnes de largeurs différentes, et la lecture verticale, celle
   qui compte sur une liste, deviendrait impossible. */
table.nk-table tbody tr.nk-table__group > td {
  background: var(--bg);
  border-top: 1px solid var(--border-d);
  padding: 7px var(--space-md);
}
table.nk-table tbody tr.nk-table__group { cursor: default; }
/* `.nk-context-bar__tab-count` supprimée 2026-08-05 : doublon de
   `.nk-count-pill`, la pastille de compte du hub, déjà employée dans un
   `nk-btn--outline nk-btn--sm` — exactement le contexte d'un onglet de barre.
   `.nk-context-bar__filtre` supprimée le même jour : une liste déroulante de
   barre de contexte se pose avec `.nk-context-bar__select`, qui porte déjà son
   apparence complète (hauteur, chevron, focus). */
/* Barre de contexte REPLIABLE. Le défaut est une ligne unique de 50px de haut,
   ce qui suffit à un titre et deux actions. Une barre qui porte des onglets, une
   recherche, des listes de filtrage ET des boutons dépasse en écran étroit :
   sans repli elle pousse un défilement horizontal, et la dernière action —
   toujours la plus engageante — sort de l'écran. */
.nk-context-bar--wrap {
  flex-wrap: wrap;
  height: auto;
  min-height: 50px;
  padding-top: var(--space-sm);
  padding-bottom: var(--space-sm);
}

/* SECOND NIVEAU de barre de contexte (Lionel, 2026-08-07, modèle fiche PIM).
   La barre PRINCIPALE dit où l'on est : elle court sur toute la largeur, menu
   compris, et garde le bleu `--ecume`. La SECONDAIRE porte les actions de
   l'écran et prend le fond des pages — deux bandeaux du même bleu se lisaient
   comme deux titres empilés, et on ne savait plus lequel nommait la page.
   Le FILET n'est pas surchargé : elle garde celui de `.nk-context-bar`, épais et
   foncé (2px `--border-d`), comme la fiche PIM. J'avais tenté un trait fin pour
   ne pas découper l'écran en bandes — Lionel a tranché l'inverse le 2026-08-07,
   et il a raison sur ce point : c'est le trait qui sépare les actions du contenu
   sur lequel elles agissent, un filet discret le laissait flotter. */
.nk-context-bar--secondary {
  background: var(--ecume-bg);
}

.nk-btn--outline.on {
  background: var(--surface);
  border-color: var(--border-d);
  color: var(--navy);
  font-weight: 600;
}

/* ============================================================
   LAYOUT ÉDITION DEVIS
   Structure trois zones : colonne principale, récapitulatif droit et éventuel
   drawer poussé dans le flux (`.nk-drawer--push`).
   ============================================================ */
.nk-devis-body  { display: flex; flex-direction: column; flex: 1; overflow: hidden; min-height: 0; }
.nk-devis-cols  { display: flex; flex: 1; min-height: 0; overflow: hidden; }

/* ============================================================
   PLAN DE TRAVAIL EN COLONNES (2026-08-05)

   Un écran d'atelier : deux ou trois colonnes pleine hauteur, séparées par un
   filet, chacune avec son en-tête fixe et son corps qui défile seul. Le module
   Filtres l'emploie deux fois (composition d'un groupe : bibliothèque / contenu
   / aperçu ; affectation : arbre / panneau), et il reposait ces règles en
   quinze styles inline distincts, aux largeurs de colonne toutes différentes.

   Générique à dessein : rien ici ne nomme les filtres. Ce n'est PAS
   `.nk-devis-cols` juste au-dessus, qui n'est que le conteneur — il manquait
   les colonnes elles-mêmes, leur en-tête et leur corps.
   ============================================================ */
.nk-panes { display: flex; flex: 1; min-height: 0; overflow: hidden; }
/* Filet de séparation d'avec ce qui précède (barre de contexte, onglets). */
.nk-panes--ruled { border-top: 1px solid var(--border); }

/* `min-width: 0` autant que `min-height: 0` : un élément flex vaut `min-width:auto`
   par défaut, donc un contenu LARGE — un tableau à libellés longs — élargit la
   colonne au lieu de défiler dedans, et c'est toute la PAGE qui part en
   défilement horizontal (topbar et menu de gauche compris). Constaté le
   2026-08-07 sur Catalogue ajustement. */
.nk-pane { display: flex; flex-direction: column; min-height: 0; min-width: 0; flex: 1; }
/* Colonne LATÉRALE, de largeur fixe. Une seule largeur pour toutes : trois
   valeurs voisines (288 / 300 / 320) ne se distinguaient pas à l'œil et ne
   faisaient que rendre les colonnes irrégulières d'un écran à l'autre. */
.nk-pane--side  { flex: 0 0 300px; }
.nk-pane--ruled { border-right: 1px solid var(--border); }
/* Colonne de SYNTHÈSE : fond gris, pour se lire comme un à-côté et non comme
   une troisième zone de travail. */
.nk-pane--tinted { background: var(--bg); }

/* En-tête FIXE : il peut porter un titre, une aide, un champ de recherche et
   des listes de filtrage — d'où un bloc, et non une ligne de hauteur figée
   comme `.nk-panel__head`. */
.nk-pane__head {
  flex-shrink: 0;
  padding: var(--space-md);
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}
.nk-pane__title { font-size: 15px; font-weight: var(--fw-semibold); color: var(--ink); }
/* Corps DÉFILANT — seule zone qui scrolle, le fond blanc la détache de
   l'en-tête teinté. */
.nk-pane__body { flex: 1; overflow-y: auto; min-height: 0; background: var(--white); }
.nk-pane__body--padded { padding: var(--space-md); }
/* Cible d'un dépôt par glisser-déposer : la transition existe pour que le
   retour visuel du survol ne clignote pas. Le module pose l'état par
   `.is-drop-target`, jamais par un `style.background` en JavaScript. */
.nk-pane__body--dropzone { transition: background var(--t-fast); }
.nk-pane__body--dropzone.is-drop-target { background: var(--cyan-l); }
/* Ligne d'une colonne : hauteur de frappe confortable, filet de séparation. */
.nk-pane__row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: 10px var(--space-md);
  border-bottom: 1px solid var(--border);
}
/* Ligne ATTÉNUÉE — déjà prise, ou sans effet ici. Elle reste cliquable, d'où
   une simple opacité et non `.nk-disabled`, qui poserait `not-allowed`. */
.nk-pane__row--dim { opacity: .6; }

/* Trait d'insertion pendant un glisser-déposer, posé sur la ligne survolée.
   Bordure haute TRANSPARENTE par défaut, et non absence de bordure : sans elle
   la ligne se décalerait de 2px à l'apparition du trait. Le trait BAS passe par
   une ombre interne, pour ne pas entrer en conflit avec le filet de séparation
   de `.nk-pane__row`. */
.nk-drop-item { border-top: 2px solid transparent; }
.nk-drop-item.is-drop-before { border-top-color: var(--cyan); }
.nk-drop-item.is-drop-after  { box-shadow: inset 0 -2px 0 var(--cyan); }
/* Ligne en cours de déplacement. */
.nk-drop-item.is-dragging { opacity: .35; }

.nk-col-left {
  flex: 1; align-self: stretch;
  display: flex; flex-direction: column;
  overflow-y: auto; min-height: 0;
  padding: 10px 12px; gap: 9px;
}
.nk-col-left > * { flex-shrink: 0; }

/* `.nk-col-right` supprimée 2026-05-23 (chantier C, Phase 0a) :
   legacy devis, jamais appliquée (la colonne droite actuelle utilise
   d'autres sélecteurs). Les responsive variants `1080px` et `720px`
   plus bas ont également été nettoyés. */

.nk-drawer--push {
  position: relative;
  width: 0;
  flex-shrink: 0;
  align-self: stretch;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  border-left: 0 solid var(--border);
  background: var(--white);
  transform: none;
  transition: width .2s cubic-bezier(.4,0,.2,1), border-left-width .2s;
  z-index: 1;
}
.nk-drawer--push.open {
  width: 300px;
  border-left-width: 1px;
  overflow-y: auto;
}

/* ============================================================
   STATUT DEVIS
   Micro-composant dédié au changement d'état d'un devis.
   ============================================================ */
.nk-status { display: flex; align-items: center; gap: 8px; }
.nk-status__label { font-size: 11px; font-weight: 700; color: var(--slate); white-space: nowrap; }
.nk-status__select { flex: 1; padding: 6px 10px; border: 1px solid var(--border); border-radius: var(--r-md); font-size: 12px; font-family: var(--font); color: var(--navy); background: var(--white); outline: none; cursor: pointer; }
.nk-status__select:focus { border-color: var(--cyan); }

/* ============================================================
   SÉLECTEUR DE SITE
   Tuiles de sélection de site utilisées au démarrage d'un devis ou d'un flux
   nécessitant un contexte boutique explicite.
   ============================================================ */
.nk-site-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 18px; }
.nk-site-pick { display: flex; flex-direction: column; gap: 3px; padding: 12px 14px; border: 1.5px solid var(--border); border-radius: var(--r-md); cursor: pointer; transition: all var(--t-fast); background: var(--white); }
.nk-site-pick:hover { border-color: var(--navy); background: var(--surface); }
.nk-site-pick.selected { border-color: var(--navy); background: var(--cyan-l); }
.nk-site-pick__code { font-size: 10px; font-weight: 700; padding: 2px 6px; border-radius: var(--r-sm); align-self: flex-start; margin-bottom: 2px; }
.nk-site-pick__name { font-size: 13px; font-weight: 600; color: var(--navy); }
.nk-site-pick__url { font-size: 11px; color: var(--muted); }

/* ============================================================
   SPINNER
   Loader simple réutilisable dans les zones d'attente courtes.
   ============================================================ */
.nk-spinner {
  width: 28px; height: 28px;
  border: 3px solid var(--border);
  border-top-color: var(--orange);
  border-radius: 50%;
  animation: nk-spin .75s linear infinite;
}
.nk-spinner--inline {
  display: inline-block;
  width: 14px; height: 14px;
  border: 2px solid rgba(255,255,255,.4);
  border-top-color: var(--white);
  border-radius: 50%;
  animation: nk-spin .75s linear infinite;
  vertical-align: middle;
  margin-right: 6px;
}

/* ============================================================
   RESPONSIVE
   Deux paliers :
   - 1080px : on compresse surtout la colonne droite.
   - 720px  : on bascule les mises en page à colonnes en pile verticale.
   ============================================================ */
@media (max-width: 1080px) {
  .nk-kpis { flex-wrap: wrap; }
}

@media (max-width: 720px) {
  .nk-devis-cols  { flex-direction: column; }
  .nk-drawer      { width: 100%; }
  /* Padding LATÉRAL uniquement, en propriétés longues : le raccourci `padding`
     réécrivait aussi le haut et le bas, et cette règle vient APRÈS
     `.nk-context-bar--wrap` à spécificité égale — elle écrasait donc le
     `padding-top/bottom` de la barre repliable, dans le cas exact pour lequel
     cette variante a été créée : sous 720px les deux rangées se touchaient. */
  .nk-context-bar { padding-left: 14px; padding-right: 14px; gap: 6px; }
  .nk-list__content { padding: 10px 14px; }
}

/* ============================================================
   HUB — composants spécifiques
   À migrer progressivement vers les classes nk-* ci-dessus
   Cette zone regroupe encore plusieurs familles historiques :
   - shell global du Hub (topbar, sidebar, workspace)
   - tuiles de home et listes admin
   - drawers/modales maison antérieures au design system `nk-*`
   - composants clients/hub encore en compatibilité
   ============================================================ */

/* Shell principal du Hub : topbar fixe blanche (Lot 4 refonte
   navigation 2026-05-27) — fond blanc, NOCIKA et libellés en bleu
   foncé `--navy`, séparation visuelle via filet bas 1px `--border`.
   La sidebar Deep Blue a été supprimée au Lot 5 (2026-05-27) :
   navigation services désormais centralisée dans le launcher
   topbar (`.nk-launcher-btn` + `nk-launcher.js`). */
.topbar{height:var(--top);background:var(--white);border-bottom:1px solid var(--border);display:flex;align-items:center;padding:0 18px 0 0;flex-shrink:0;z-index:var(--z-topbar);gap:0}
.nk-brand{display:inline-flex;align-items:center;text-decoration:none}
.nk-brand__img{display:block;height:24px;width:auto;max-width:140px;object-fit:contain}
/* Zone logo : largeur auto (le `width:var(--sb)` héritait de l'ancienne
   sidebar Deep Blue supprimée 2026-05-27 — il créait un grand espace
   blanc à droite du logo). `padding-right:0` + `.tb-section{padding-left:11px}`
   alignent l'écart NOCIKA→breadcrumb sur le `gap:11px` interne
   4-carrés→NOCIKA. */
.tb-logo{display:flex;align-items:center;gap:11px;height:100%;width:auto;padding:0 0 0 16px;cursor:pointer;flex-shrink:0;transition:background .15s;text-decoration:none;background:var(--white);overflow:hidden}
.tb-logo:hover{background:var(--surface)}
/* `.tb-svg` + `.tb-sep` supprimées 2026-05-23 (chantier C, Phase 0a) :
   topbar legacy, jamais utilisée dans la topbar actuelle. */
.tb-section{font-size:12px;font-weight:600;color:var(--muted);padding-left:11px;white-space:nowrap}
.tb-right{margin-left:auto;display:flex;align-items:center;gap:12px}
.clk{text-align:right;cursor:default}
.clk-h{font-family:var(--font);font-size:14px;font-weight:900;color:var(--navy);line-height:1}
.clk-d{font-size:10px;color:var(--muted);margin-top:1px}
.tb-divider{width:1px;height:20px;background:var(--border)}
.upill{display:flex;align-items:center;gap:7px;padding:4px 12px 4px 4px;border:1px solid var(--border);border-radius:30px;cursor:pointer;background:var(--surface);transition:all var(--t-fast)}
.upill:hover{background:var(--bg);border-color:var(--border-d)}
.nk-avatar{width:28px;height:28px;border-radius:50%;background:var(--avatar-bg,var(--cyan-l));color:var(--avatar-color,var(--cyan-d));display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;flex-shrink:0}
.uav{width:26px;height:26px;border-radius:50%;background:var(--cyan);display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;color:#fff;flex-shrink:0}
.un{font-size:12px;font-weight:600;color:var(--navy)}
/* `.logout-btn` supprimée 2026-05-23 (chantier C, Phase 0a) :
   bouton logout style legacy, jamais appliqué (le logout actuel passe
   par d'autres classes). */

/* ============================================================
   NK-TOPBAR — nouvelle topbar Nocika (2026-05-31)
   ----------------------------------------------------------------
   Refonte de la zone gauche + raccourcis : chip Service + chip
   Module + chip Raccourcis, chacun avec dropdown contextuel. Le
   wrapper `<header class="topbar">` (hauteur, fond, bordure)
   reste réutilisé tel quel — ces classes ne touchent qu'aux
   éléments internes.

   Convention : un seul dropdown ouvert à la fois ; clic extérieur
   ou ESC ferme tout. État `--open` sur le chip = bordure cyan +
   fond cyan clair + chevron retourné.
   ============================================================ */

/* Zone brand : logo + bouton home + séparateur visuel implicite */
.nk-topbar__brand{display:flex;align-items:center;gap:var(--space-md);padding:0 var(--space-md) 0 var(--space-lg);flex-shrink:0}
/* Espacement entre les 2 chips Service / Module : tuned 2026-05-31
   sur retour Lionel (gap trop serré au premier jet). */
.nk-topbar__chips{display:flex;align-items:center;gap:var(--space-lg);padding:0 var(--space-md);flex-shrink:0}
.nk-topbar__sep{color:var(--border-d);font-size:18px;font-weight:300;user-select:none;margin:0 var(--space-xs)}

/* Variante du bouton topbar dimensionnée pour matcher la hauteur
   exacte d'un chip (cf. retour Lionel 2026-05-31, 3 passes).
   Double classe `.nk-topbar-btn.nk-topbar-btn--chip` pour booster la
   spécificité au-dessus de `.nk-topbar-btn { width:30px; height:30px }`
   (qui vient plus loin dans le fichier — sans ce boost, l'ordre source
   l'emporte avec spécificité égale).
   Hauteur calculée : padding 6+6 + bordure 1+1 + contenu 2 lignes
   (label 9px + gap 1px + value 13px line-height 1.2) ≈ 41px. */
.nk-topbar-btn.nk-topbar-btn--chip{
  width:41px;
  height:41px;
  border-radius:var(--r-lg);
}

/* Chip topbar (Service / Module / Raccourcis) */
.nk-topbar-chip{
  display:inline-flex;align-items:center;gap:var(--space-sm);
  background:var(--white);border:1px solid var(--border);
  border-radius:var(--r-lg);
  padding:6px 12px;cursor:pointer;font-family:inherit;
  transition:background var(--t-fast),border-color var(--t-fast);
  min-width:140px;
}
.nk-topbar-chip:hover:not(.nk-topbar-chip--open){background:var(--surface);border-color:var(--border-d)}
.nk-topbar-chip:focus-visible{outline:none;box-shadow:var(--focus-ring)}
.nk-topbar-chip--open{background:var(--cyan-l);border-color:var(--cyan)}
.nk-topbar-chip__inner{display:flex;flex-direction:column;align-items:flex-start;gap:1px;flex:1;text-align:left;min-width:0}
.nk-topbar-chip__label{font-size:9px;font-weight:var(--fw-semibold);color:var(--muted);text-transform:uppercase;letter-spacing:.5px;line-height:1.1}
.nk-topbar-chip__value{font-size:13px;font-weight:var(--fw-semibold);color:var(--navy);line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:200px}
.nk-topbar-chip__chev{flex-shrink:0;color:var(--muted);transition:transform var(--t-fast)}
.nk-topbar-chip--open .nk-topbar-chip__chev{transform:rotate(180deg);color:var(--cyan)}
.nk-topbar-chip__leading{flex-shrink:0;color:var(--muted);margin-right:2px;display:inline-flex}
.nk-topbar-chip--open .nk-topbar-chip__leading{color:var(--cyan)}

/* Variante "raccourcis" : juste icône + label (pas d'eyebrow) */
.nk-topbar-chip--single .nk-topbar-chip__value{font-size:13px;font-weight:var(--fw-semibold);color:var(--navy)}

/* Dropdown ancré sous le chip */
.nk-topbar-dropdown{
  position:absolute;top:calc(100% + 6px);
  min-width:280px;max-width:420px;
  background:var(--white);
  border:1px solid var(--border);border-radius:var(--r-lg);
  box-shadow:0 8px 24px rgba(15,23,42,0.08);
  z-index:var(--z-overlay);
  overflow:hidden;
  display:none;
}
.nk-topbar-dropdown.open{display:block}
.nk-topbar-dropdown__head{
  display:flex;align-items:center;justify-content:space-between;
  padding:10px 14px 6px;
  font-size:9px;font-weight:var(--fw-semibold);
  color:var(--muted);text-transform:uppercase;letter-spacing:.5px;
}
.nk-topbar-dropdown__lock{
  display:inline-flex;align-items:center;gap:4px;
  font-size:10px;font-weight:var(--fw-normal);
  color:var(--muted);text-transform:none;letter-spacing:0;
  background:var(--surface);
  border:1px solid var(--border);
  border-radius:var(--r-pill);
  padding:2px 8px;
}
.nk-topbar-dropdown__list{padding:6px;max-height:60vh;overflow-y:auto}
.nk-topbar-dropdown__item{
  display:flex;align-items:center;gap:var(--space-sm);
  padding:8px 10px;border-radius:var(--r-sm);
  cursor:pointer;background:transparent;border:none;width:100%;
  text-align:left;font-family:inherit;
  transition:background var(--t-fast);
}
.nk-topbar-dropdown__item:hover{background:var(--surface)}
.nk-topbar-dropdown__item--active{background:var(--cyan-l)}
.nk-topbar-dropdown__item--active:hover{background:var(--cyan-l)}
.nk-topbar-dropdown__item-icon{
  flex-shrink:0;width:32px;height:32px;border-radius:var(--r-sm);
  background:var(--surface);
  display:inline-flex;align-items:center;justify-content:center;
  color:var(--cyan-d);
}
.nk-topbar-dropdown__item--active .nk-topbar-dropdown__item-icon{background:var(--white)}
.nk-topbar-dropdown__item-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.nk-topbar-dropdown__item-title{font-size:13px;font-weight:var(--fw-semibold);color:var(--navy);line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.nk-topbar-dropdown__item--active .nk-topbar-dropdown__item-title{color:var(--cyan-d)}
.nk-topbar-dropdown__item-sub{font-size:11px;color:var(--muted);line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.nk-topbar-dropdown__item-meta{flex-shrink:0;color:var(--cyan);display:inline-flex;align-items:center}
.nk-topbar-dropdown__item-meta--muted{color:var(--muted)}
.nk-topbar-dropdown__foot{
  border-top:1px solid var(--border);
  padding:8px;background:var(--surface);
}
.nk-topbar-dropdown__empty{
  padding:18px 14px;text-align:center;
  font-size:12px;color:var(--muted);
}

/* Wrapper relatif pour positionner les dropdowns */
.nk-topbar-chip-wrap{position:relative;display:inline-flex;align-items:center}

/* Bouton "Accueil du service" intégré à la row service (cliquable séparément) */
.nk-topbar-dropdown__home-btn{
  flex-shrink:0;width:32px;height:32px;border-radius:var(--r-sm);
  background:var(--surface);border:none;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;
  color:var(--cyan-d);transition:background var(--t-fast);
}
.nk-topbar-dropdown__home-btn:hover{background:var(--cyan-l)}



/* Structure générale du workspace : contenu principal sur toute la
   largeur depuis le Lot 5 de la refonte navigation (2026-05-27) — la
   sidebar gauche Deep Blue a été supprimée au profit du launcher
   topbar (`.nk-launcher-btn` + `nk-launcher.js`). Les classes `.sb`,
   `.sb-sec`, `.sb-lbl`, `.si*`, `.sb-admin-*`, `.menu-ico` et toutes
   les règles `body.sb-off *` ont disparu de la cascade. */
.ws{display:flex;flex:1;overflow:hidden;min-height:0}
.main{flex:1;overflow:hidden;min-height:0;display:flex;flex-direction:column}
#nk-module,#hub-views{background:var(--ecume-bg)}
.view{display:none;padding:24px 28px 52px;animation:fup .18s ease}
.view.active{display:block}
@keyframes fup{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:translateY(0)}}

/* Home Hub : titres de service et tuiles de microservices / liens externes. */
.ph{margin-bottom:22px;padding-bottom:16px;border-bottom:1px solid var(--border)}
.ph-name{font-family:var(--font);font-size:20px;font-weight:900;color:var(--navy)}
.ph-sub{font-size:12px;color:var(--muted);margin-top:3px}
.svc-block{margin-bottom:22px}
.blk-hd{display:flex;align-items:center;gap:12px;margin-bottom:10px}
.blk-label{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:1.4px;color:var(--muted);white-space:nowrap}
.blk-line{flex:1;height:1px;background:var(--border)}
.blk-badge{font-size:9px;font-weight:700;padding:2px 8px;border-radius:20px;white-space:nowrap}
.bb-ms{background:var(--cyan-l);color:var(--cyan-d)}.bb-ext{background:var(--bg);color:var(--muted);border:1px solid var(--border)}.bb-int{background:var(--orange-l);color:var(--orange)}
.link-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(148px,1fr));gap:8px}
.ms-tile{grid-column:span 2;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);display:flex;align-items:center;box-shadow:var(--shadow-sm);transition:all .16s;overflow:hidden}
.ms-tile-link{cursor:pointer}
.ms-tile-inactive{cursor:default}
.ms-tile:hover{border-color:var(--tc,var(--cyan));box-shadow:0 4px 14px rgba(15,39,68,.1),0 0 0 1.5px var(--tc,var(--cyan))}
.ms-bar{width:3px;align-self:stretch;background:var(--tc,var(--cyan));flex-shrink:0}
.ms-ico{width:36px;height:36px;border-radius:8px;display:flex;align-items:center;justify-content:center;flex-shrink:0;margin:10px 10px 10px 12px}
.ms-body{flex:1;min-width:0;padding:10px 12px 10px 0}
.ms-name{font-size:13px;font-weight:700;color:var(--navy);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ms-desc{font-size:10px;color:var(--muted);margin-top:2px}
.link-tile{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-md);padding:9px 11px;display:block;text-decoration:none;transition:all .16s;position:relative}
.link-tile:hover{border-color:var(--tc,var(--cyan));background:#fff;box-shadow:var(--shadow-md)}
/* `.lt-name/-host/-note` supprimées 2026-05-23 (chantier C, Phase 0a) :
   sous-classes des tuiles liens du Home, jamais appliquées au DOM. */
/* `.nk-content` supprimée 2026-05-23 (chantier C, Phase 0a) :
   nommage générique en conflit potentiel, jamais appliquée. */

/* Outils de listes admin legacy : toolbar, recherche, tableau simplifie. */
.tbar{display:flex;align-items:center;gap:10px;margin-bottom:12px}
.tbar-r{margin-left:auto;display:flex;gap:8px;align-items:center}
.sbox{display:flex;align-items:center;gap:6px;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-md);padding:6px 10px;transition:border-color var(--t-fast);position:relative}
.sbox:focus-within{border-color:var(--cyan);box-shadow:var(--focus-ring)}
.sbox input{border:none;background:none;outline:none;font-size:12px;color:var(--navy);font-family:var(--font);width:180px}
.sbox input::placeholder{color:var(--muted)}
.sbox-ico{font-size:11px;color:var(--muted)}
.sbox-clear{font-size:12px;color:var(--muted);cursor:pointer;display:none;border:none;background:none;padding:0 2px;line-height:1}
.sbox-clear:hover{color:var(--navy)}
/* `.tbox` + `.thd` + `.trow` + `.cell-name/sub/email` supprimées
   2026-05-24 (lot dette legacy). Les 3 pages PHP standalone qui les
   consommaient (`admin-users.php`, `admin-poles.php`, `admin-links.php`,
   ainsi que `_admin_header.php` / `_admin_footer.php` partagés) ont
   été supprimées dans la foulée — dead code remplacé par les SPA
   `#/admin/users`, `#/admin/poles`, `#/admin/links` qui utilisent
   déjà la convention moderne `<table class="nk-table">`. */
/* `.rbadge` + `.rbadge.admin/.user` supprimées 2026-05-23 (chantier C,
   Phase 0a) : ancien badge rôle jamais appliqué. */
/* `.stk-live` / `.stk-beta` / `.stk-soon` supprimées 2026-05-28 :
   anciens badges statut Live/Bêta/Bientôt des liens. La notion de statut
   a été retirée du modèle (cf. sqlite_bootstrap_migrate_links_simplify
   et simplification Admin > Liens). */
.pole-dot{width:9px;height:9px;border-radius:50%;display:inline-block;flex-shrink:0}
/* `.act-btns` + `.act-btn*` supprimées 2026-05-24 (lot dette legacy).
   Remplacées partout par `.nk-actions` + `.nk-btn--icon` (avec variants
   `.nk-btn--icon-edit` / `.nk-btn--icon-del`). */
/* `.sp-on` + `.sp-arc` supprimées 2026-05-23 (chantier C, Phase 0a) :
   anciens badges statut (sp = status pill), jamais appliqués. */

/* `.btn` + `.btn-g/cy/sm/del` supprimées 2026-05-24 (lot dette legacy).
   Remplacées par `.nk-btn` + `.nk-btn--outline/primary/sm/danger`.

   Famille modales legacy `.mov` + `.modal` + `.modal.sm/.xs` + `.mhd` +
   `.mhd-title` + `.mcl` + `.mbody` + `.mfoot` supprimée 2026-05-24 :
   les 4 derniers consommateurs (mNote dans index.php + injection JS
   dans hub-index.js, mLink dans index.php, modale BO dans
   nk-client-block.js) ont été migrés vers `.nk-modal__overlay` +
   `.nk-modal` + `.nk-modal__head/__title/__body/__foot` (cf. plus
   haut dans ce fichier). Le bouton croix `.mcl` a été remplacé par
   un simple `.nk-btn--icon` avec style inline minimal. */

/* ── hub-index : modal note ── */
.nk-mn-url{margin-bottom:10px;font-size:12px}
.nk-mn-body{white-space:pre-wrap;font-size:13px;line-height:1.6;min-height:60px;color:var(--text)}
.nk-mn-footer{display:flex;justify-content:flex-end;margin-top:12px}
.nk-mn-link{color:var(--cy)}
.nk-mn-type{color:var(--muted)}

/* Helpers de formulaires courts utilises dans les drawers/modales legacy. */
.fr{display:flex;gap:10px}.fg{display:flex;flex-direction:column;gap:5px;flex:1}
.fl{font-size:9px;font-weight:700;letter-spacing:.8px;text-transform:uppercase;color:var(--muted)}
.fi,.fs,.fta{padding:8px 10px;border:1px solid var(--border);border-radius:var(--r-md);background:var(--bg);color:var(--navy);font-size:12px;font-family:var(--font);outline:none;width:100%;transition:all var(--t-fast)}
.fi:focus,.fs:focus,.fta:focus{border-color:var(--cyan);box-shadow:var(--focus-ring);background:#fff}
.fta{resize:vertical}
.rcards{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.rcard{border:1.5px solid var(--border);border-radius:var(--r-lg);padding:11px 13px;cursor:pointer;transition:all var(--t-fast);background:var(--bg)}
.rcard:hover{border-color:var(--border-d);background:#fff}.rcard.on{border-color:var(--cyan);background:var(--cyan-l)}
.rcard-title{font-size:12px;font-weight:700;color:var(--navy);margin-bottom:3px}
.rcard-desc{font-size:10px;color:var(--muted)}

/* Selection de services / poles dans le drawer utilisateur. */
.sc-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:6px}
.sc{display:flex;align-items:center;gap:7px;padding:8px 10px;border:1.5px solid var(--border);border-radius:var(--r-md);cursor:pointer;transition:all var(--t-fast);background:var(--bg)}
.sc:hover{border-color:var(--border-d);background:#fff}.sc.on{border-color:var(--cyan);background:var(--cyan-l)}
.sc input{display:none}.sc-name{font-size:11px;font-weight:600;color:var(--navy)}
.sc-ck{margin-left:auto;font-size:10px;color:var(--cyan-d);opacity:0}.sc.on .sc-ck{opacity:1}
/* ── note interne (drawer utilisateur) ── */
.note-section{margin-top:12px}
.note-section-hd{display:flex;align-items:center;gap:8px;margin-bottom:6px}
.note-section-title{font-size:12px;font-weight:600;color:var(--navy)}
.note-section-badge{font-size:10px;font-weight:600;color:var(--muted);background:var(--surface);border:1px solid var(--border);border-radius:4px;padding:1px 6px}
.note-section-body{display:flex;flex-direction:column}

/* Choix de couleur/type/statut pour certaines entites Hub. */
.col-row{display:flex;gap:7px;flex-wrap:wrap}
.co{width:22px;height:22px;border-radius:50%;cursor:pointer;border:2.5px solid transparent;transition:all .12s}
.co:hover,.co.on{transform:scale(1.18);border-color:var(--navy)}
.type-row{display:flex;gap:6px}
.topt{flex:1;padding:7px 6px;border:1.5px solid var(--border);border-radius:var(--r-md);text-align:center;cursor:pointer;font-size:11px;font-weight:600;transition:all var(--t-fast);background:var(--bg);color:var(--muted)}
.topt.on{border-color:var(--cyan);background:var(--cyan-l);color:var(--cyan-d)}
/* `.sopt` (+ variantes `.live` / `.beta` / `.soon`) supprimées
   2026-05-28 : ancien chip de statut Live/Bêta/Bientôt des liens.
   La notion de statut a été retirée du modèle. */

/* Drawer utilisateur legacy (`ud-*`) : edition d'utilisateurs admin. */
.ud{position:fixed;top:0;right:-320px;width:300px;height:100vh;background:var(--surface);border-left:1px solid var(--border);box-shadow:-4px 0 20px rgba(15,39,68,.1);z-index:var(--z-drawer);display:flex;flex-direction:column;transition:right .22s cubic-bezier(.25,.8,.25,1)}
.ud.open{right:0}
.ud-hd{padding:14px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px}
.ud-av{width:36px;height:36px;border-radius:50%;background:var(--cyan);display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:700;color:#fff;flex-shrink:0}
.ud-name{font-family:var(--font);font-size:13px;color:var(--navy);font-weight:900}
.ud-sub{font-size:10px;color:var(--muted)}
.ud-close{margin-left:auto;width:24px;height:24px;border-radius:6px;border:1px solid var(--border);background:none;cursor:pointer;font-size:12px;color:var(--muted);display:flex;align-items:center;justify-content:center}
.ud-close:hover{background:var(--bg)}
.ud-body{flex:1;padding:16px;display:flex;flex-direction:column;gap:12px;overflow-y:auto}
.ud-field{display:flex;flex-direction:column;gap:4px}
.ud-lbl{font-size:9px;font-weight:700;letter-spacing:.8px;text-transform:uppercase;color:var(--muted)}
.ud-inp{padding:8px 10px;border:1px solid var(--border);border-radius:var(--r-md);font-size:12px;font-family:var(--font);background:var(--bg);color:var(--navy);outline:none;transition:all var(--t-fast);width:100%}
.ud-inp:focus{border-color:var(--cyan);background:#fff;box-shadow:var(--focus-ring)}
.ud-hint{font-size:10px;color:var(--muted);font-style:italic;line-height:1.4;margin-top:2px}
.ud-hint--strong{font-size:12px;color:var(--navy);font-style:normal;font-weight:500;margin-top:4px}
.ud-sep{height:1px;background:var(--border)}
.ud-foot{padding:12px 16px;border-top:1px solid var(--border);display:flex;flex-direction:column;gap:8px}
.ud-foot-row{display:flex;gap:8px;justify-content:flex-end}
.btn-logout{width:100%;padding:8px;border-radius:var(--r-md);font-size:12px;font-weight:600;line-height:1.5;cursor:pointer;border:1px solid var(--danger-b);background:var(--danger-l);color:var(--danger);font-family:var(--font);text-align:center;text-decoration:none;display:block}
.btn-logout:hover{background:#fee2e2}
/* `.sb-admin-toggle` + `.sb-admin-arrow` supprimés au Lot 5 (2026-05-27)
   avec la sidebar gauche. La navigation Administration passe désormais
   par la card dédiée du launcher (`nk-launcher.js`) puis la page
   `#/admin` (`hub-admin-home.js`). */

/* ── CLIENT PAGE — classes spécifiques ────────────────────────
   Fiche client Hub : grille 2 colonnes, cartes métiers, adresses et actions.
   On reste ici car ces classes sont encore très liées au markup historique
   de `nk-client-page.js`.
*/
.nk-cp-grid         { display: grid; grid-template-columns: 1fr 260px; flex: 1; min-height: 0; overflow: hidden; }
.nk-cp-left         { overflow-y: auto; padding: 12px; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.nk-cp-right        { overflow-y: auto; border-left: 1px solid var(--border); background: var(--surface); padding: 12px; display: flex; flex-direction: column; gap: 0; }
/* `.nk-cp-cname`, `.nk-cp-cemail`, `.nk-cp-sec--center`, `.nk-cp-sites`
   supprimées 2026-05-23 (chantier C, Phase 0a) : sous-éléments fiche
   client jamais appliqués au DOM. */
.nk-cp-sec          { background: var(--white); border: 0.5px solid var(--border); border-radius: var(--r-lg); padding: 14px; margin-bottom: 8px; }
.nk-cp-sec-hd       { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.nk-cp-sec-title    { font-size: 12px; font-weight: 500; color: var(--navy); border-left: 2px solid var(--cyan); padding-left: 6px; }
.nk-cp-sec-title::before { display: none; }
.nk-cp-note         { font-size: 10px; color: var(--slate); font-style: italic; background: #fffbf0; border-radius: var(--r-md); padding: 6px 8px; border: 0.5px solid #e8e0c0; line-height: 1.5; }
.nk-cp-addr         { font-size: 12px; color: #374151; line-height: 1.6; background: var(--surface); border-radius: var(--r-md); padding: 9px; border: 0.5px solid var(--border); display: flex; flex-direction: column; gap: 6px; }
.nk-cp-addr-body    { font-size: 12px; line-height: 1.6; }
.nk-cp-addr-actions { display: flex; gap: 4px; border-top: 1px solid var(--border); padding-top: 6px; }
.nk-cp-addr-label   { font-size: 11px; font-weight: 500; color: var(--slate); margin-bottom: 4px; }
.nk-cp-addr--add    { border-style: dashed; cursor: pointer; display: flex; align-items: center; justify-content: center; color: var(--muted); font-size: 12px; font-weight: 500; min-height: 80px; }
.nk-cp-addr--add:hover { background: var(--white); color: var(--cyan); border-color: var(--cyan); }

/* Carte adresse radio (sélecteur dans devis) — variantes `.nk-addr-card*`
   supprimées 2026-05-23 (chantier C, Phase 0a) : sélecteur d'adresse
   alternatif jamais utilisé (le sélecteur actuel passe par `.nk-cp-addr*`
   au-dessus). */

/* Bandeau de la modale d'adresse : type + aide contextuelle. */
.nk-am-type-bar     { display: flex; align-items: center; justify-content: space-between; background: var(--surface); border-radius: var(--r-md); padding: 10px 14px; margin-bottom: 14px; }
.nk-am-type-radios  { display: flex; gap: 16px; }
.nk-am-type-hint    { font-size: 11px; color: var(--muted); font-style: italic; margin: -8px 0 12px; }

/* Poignee de redimensionnement pour sidebars pilotées en JS.
   - `align-self: stretch` : indispensable pour que la poignée prenne toute
     la hauteur de la cellule grid, même si le parent a `align-items: start`.
     Sans ça, la poignée n'a pas de hauteur intrinsèque et se rend à 0px.
   - Au repos : trait fin gris-bleu, juste assez visible pour signaler que la
     zone est interactive sans dominer le layout.
   - Au hover : trait + fond cyan pour donner un feedback fort que la zone
     est draggable. */
.nk-resize-handle       { width: 6px; cursor: col-resize; background: transparent; flex-shrink: 0; align-self: stretch; min-height: 100%; border-left: 1px solid var(--border-d); transition: background var(--t-fast), border-color var(--t-fast); }
.nk-resize-handle:hover { background: var(--cyan-l); border-color: var(--cyan); }

/* Poignée de la page « Image » (2026-07-20, demande Lionel) : une ligne NETTE et
   CENTRÉE (espace identique à gauche et à droite) au lieu du filet 1px collé à
   gauche du handle partagé. La hauteur suit la colonne la plus haute (le rail
   d'images) → la ligne s'arrête à la dernière image quand la zone de recherche
   est plus courte. */
#nk-pim-img-split .nk-resize-handle {
  width: 19px;
  border-left: none;
  background-color: transparent;
  background-image: linear-gradient(var(--border-d), var(--border-d));
  background-position: center;
  background-size: 1px 100%;
  background-repeat: no-repeat;
}
#nk-pim-img-split .nk-resize-handle:hover {
  background-color: var(--cyan-l);
  background-image: linear-gradient(var(--cyan), var(--cyan));
  background-position: center;
  background-size: 2px 100%;
  background-repeat: no-repeat;
}

/* Conteneur scindé redimensionnable (colonne gauche / poignée / colonne
   droite) piloté par `NocikaShared.initResizableSidebar`, qui pose la
   largeur réelle via `gridTemplateColumns` inline dès l'init (localStorage
   ou defaultRightWidth). Le template déclaré ici n'est qu'un fallback
   avant init. Utilisé par la fiche CMS ; guide-fiche et reprise-edit
   portent encore le même pattern en inline (migration à prévoir). */
.nk-split {
  display: grid;
  grid-template-columns: 1fr auto 38%;
  height: 100%;
  min-height: 0;
}
/* Variante qui OCCUPE la hauteur restante par le flux flex plutôt que par
   `height:100%`. Un pourcentage ne résout que contre un parent de hauteur
   DÉFINIE ; dans une colonne flex il retombe sur `auto`, donc sur la hauteur du
   contenu — et la séparation entre les deux colonnes s'arrête au milieu de la
   page dès que le contenu est court. `flex:1` ne dépend, lui, d'aucune hauteur
   parente. `min-width:0` pour la même raison qu'ailleurs : sans lui un tableau
   large pousse la colonne au lieu de défiler dedans. */
.nk-split--grow { flex: 1; min-height: 0; min-width: 0; }
/* Colonne de gauche à largeur RÉGLABLE : elle porte son propre filet, pour que
   la séparation coure sur toute la hauteur même quand la poignée ne la couvre
   pas encore (avant l'init du composant). */
.nk-split > .nk-pane--ruled { border-right: 1px solid var(--border); }

/* ─── CARTES D'ATTERRISSAGE ───
   Porte d'entrée d'une page à plusieurs écrans (« Gestion des catalogues »).
   Grille qui se replie toute seule : `auto-fill` + `minmax` évite un point de
   rupture écrit à la main, qui aurait cassé au premier écran ajouté. */
.nk-cat-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space-md);
}
.nk-cat-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding: var(--space-md) var(--space-lg);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  text-decoration: none;
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.nk-cat-card:hover { border-color: var(--cyan); box-shadow: var(--shadow-md); }
.nk-cat-card:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
.nk-cat-card__nom  { font-size: 15px; font-weight: var(--fw-semibold); color: var(--ink); }
/* Aide au corps de base et en encre pleine : c'est elle qui dit à quoi sert
   l'écran, ce n'est pas une métadonnée à reléguer en gris pâle. */
.nk-cat-card__aide { font-size: var(--fs-base); color: var(--slate); line-height: 1.45; }

/* Barre de module partagée sur quelques écrans hors shell principal. */
.nk-module-bar      { display: flex; align-items: center; gap: 8px; padding: 8px 14px; background: var(--bg); border-bottom: 1px solid var(--border); flex-shrink: 0; }
.nmb-btn            { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; border-radius: var(--r-md); border: none; background: var(--border); color: var(--navy); font-size: 12px; font-weight: 600; cursor: pointer; font-family: var(--font); }
.nmb-btn:hover      { background: var(--border-d); }
.nmb-sep            { width: 1px; height: 18px; background: var(--border-d); flex-shrink: 0; }
.nmb-num            { font-size: 13px; font-weight: 700; color: var(--navy); }

/* `.btn-add` / `.btn-save` / `.btn-cancel` / `.btn-delete-confirm` /
   `.btn-sm` (couche de compatibilité) supprimées 2026-05-24 (lot dette
   legacy). Remplacées par `.nk-btn` + variants `.nk-btn--primary/success/ghost/danger/sm`.

   `.status-badge.sb-{live,encours,relire,refuse}` + `.status-badge--xs`
   supprimées 2026-05-24. Remplacées par `.nk-badge` + variants
   `.nk-badge--success/cyan/danger` + `.nk-badge--xs` (taille compacte). */

/* `.site-badge` + `.site-badge.site-{fs,oma,ng,src}` +
   `.site-badge--xs/--sm/--muted/--clickable` supprimées 2026-05-24
   (lot dette legacy). Remplacées par `.nk-badge.nk-badge--site` +
   `.nk-site--{slug}` (couleurs métier dans la section BADGES SITE
   ci-dessus) avec :
     - `.nk-badge--xs` pour la variante compacte
     - `.nk-badge--grey` pour la variante atténuée (équivalent visuel)
     - `.nk-clickable` pour le curseur pointeur
   La variante `--sm` (11px) n'a pas d'équivalent — les call-sites
   migrés utilisent la taille standard (`.nk-badge--site` = 10px). */

/* Utilitaires visuels tres simples pour etats vides, alignements et micro-helpers. */
.nk-list-state {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 60px 20px;
  color: var(--muted);
  font-size: 13px;
  text-align: center;
}
.nk-cell-title { font-size: 12px; font-weight: 600; color: var(--navy); }
.nk-cell-muted { font-size: 11px; color: var(--slate); }
.nk-cell-date { font-size: 11px; color: var(--slate); white-space: nowrap; }
.mu { color: var(--muted); }

.nk-grow { flex: 1; min-width: 0; }
.nk-module-host { flex: 1; overflow: hidden; min-height: 0; display: flex; flex-direction: column; }
.nk-hidden { display: none !important; }
.nk-inline-row { display: flex; align-items: center; gap: 8px; }
.nk-inline-row--top { align-items: flex-start; }
.nk-inline-row--wrap { flex-wrap: wrap; }
.nk-inline-row--between { justify-content: space-between; }
.nk-inline-row--end { justify-content: flex-end; }
.nk-stack-sm { display: flex; flex-direction: column; gap: 5px; }
.nk-search-clear {
  display: none;
  cursor: pointer;
  color: var(--muted);
  font-size: 13px;
  line-height: 1;
  padding: 0 2px;
}
.nk-search-clear.is-visible { display: block; }
.nk-search-clear:hover { color: var(--navy); }
.nk-count-pill {
  display: inline-block;
  margin-left: 4px;
  padding: 1px 7px;
  border-radius: 10px;
  background: var(--cyan-l);
  color: var(--cyan-d);
  font-size: 9px;
  font-weight: 700;
}
.nk-text-link { color: var(--cyan-d); text-decoration: none; font-size: 12px; }
.nk-text-link:hover { text-decoration: underline; }
/* `.nk-text-link--sm` supprimée 2026-05-23 (chantier C, Phase 0a) :
   variante jamais appliquée. */
.nk-textarea-vertical { resize: vertical; }
.nk-input--disabled { opacity: .6; cursor: not-allowed; }
.nk-checkbox-accent { width: 15px; height: 15px; cursor: pointer; accent-color: var(--cyan); }
/* Emplacement d'une case ABSENTE — même encombrement que `.nk-checkbox-accent`,
   rien de dessiné. Une ligne qui ne peut pas être cochée ne porte pas une case
   grisée (elle se lit comme « désactivé temporairement ») : elle porte un VIDE,
   et son info-bulle dit pourquoi. Sans cet aplomb, la colonne se décalerait de
   15 px d'une ligne à l'autre et le décalage se lirait comme un défaut de rendu.
   ⚠️ Classe AJOUTÉE le 2026-08-12 pour l'écran Affectation, en attente de
   validation de Lionel (règle d'or : uniquement ce qui existe, sauf accord
   nommé). Triviale à retirer si refusée. */
.nk-tree__check-slot { display: inline-block; width: 15px; height: 15px; flex-shrink: 0; }
.nk-form-row--top { align-items: flex-start; gap: 14px; }
.nk-form-error.is-visible { display: block; }
.nk-modal--danger { border-left: 4px solid var(--danger); }
/* Classes `.nk-modal__overlay--high|higher|highest` supprimées 2026-05-23
   au profit de la règle d'or « rien au-dessus de --z-toast ». L'empilement
   modale-sur-modale s'appuie désormais sur le DOM order natif (le 2e
   overlay créé passe au-dessus du 1er à z-index égal). Voir docs/UX.md
   § échelle z-index canonique. */
/* `.nk-warning-copy` supprimée 2026-05-23 (chantier C, Phase 0a) :
   probablement remplacée par `.nk-alert--warning`, jamais appliquée. */
.nk-list__state--sm { padding: 40px 20px; font-size: 13px; }
.nk-empty-inline {
  padding: 8px 0;
  color: var(--muted);
  font-size: 12px;
  font-style: italic;
}
.nk-host-muted {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--muted);
  font-size: 10px;
}
.nk-dash-muted { color: var(--border-d); }
.nk-section-pad-bottom { padding-bottom: 24px; }
/* Classes `.nk-sort-head/-trigger/-arrow/-arrow--muted` supprimées
   2026-05-23 (chantier C, Phase 0a) : système de tri jamais utilisé,
   le tri canonique passe par `.nk-th__sort` + `sortTableRows()`. */
.nk-td--sub-strong { font-weight: 600; }
.nk-pole-list { display: flex; flex-wrap: wrap; gap: 2px; }
.nk-copy-note { margin-bottom: 10px; }
.nk-helper-title {
  margin-bottom: 6px;
  color: var(--slate);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
}
.nk-sub-handle { color: var(--muted); font-size: 11px; cursor: grab; }

/* Bloc client partage (`NkClientBlock`) : recherche, resume client, liens BO. */
.nk-cb-card {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;
}
/* Classes `.nk-cb-panel`, `.nk-cb-heading`, `.nk-cb-input`,
   `.nk-cb-input-row` supprimées 2026-05-23 (chantier C, Phase 0a) :
   variantes éphémères de NkClientBlock jamais appliquées au DOM. */
.nk-cb-clear {
  position: absolute;
  right: 46px;
  color: var(--muted);
  font-size: 12px;
  cursor: pointer;
  line-height: 1;
}
.nk-cb-clear:hover { color: var(--navy); }
.nk-cb-msg { display: none; margin-top: 4px; font-size: 11px; }
.nk-cb-msg.is-visible { display: block; }
/* Modifiers `.nk-cb-msg--muted/--danger` et bouton `.nk-cb-create`
   supprimés 2026-05-23 (chantier C, Phase 0a) : jamais appliqués au DOM
   (le JS message client utilise un style inline). */
.nk-cb-card-head { display: flex; align-items: center; gap: 8px; padding: 8px 10px; }
.nk-cb-avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: #deeaf5;
  color: #002c50;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 700;
  flex-shrink: 0;
}
.nk-cb-name { font-size: 12px; font-weight: 600; color: var(--navy); }
.nk-cb-email {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 10px;
  color: var(--slate);
}
.nk-cb-actions { display: flex; align-items: center; gap: 4px; }
.nk-cb-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  padding: 4px 10px 6px;
  background: #e2f4e8;
  border-top: 1px solid #a7f3d0;
}
.nk-icon-btn {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  cursor: pointer;
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast);
}
.nk-icon-btn--info { background: #deeaf5; border: 1px solid #b8d4e8; color: #002c50; }
.nk-icon-btn--unlink { background: #f1f5f9; border: 1px solid #d0d5dd; color: var(--muted); }
.nk-icon-btn--unlink:hover { background: var(--danger-l); border-color: #fca5a5; color: var(--danger); }
.nk-cb-mono-chip {
  display: inline-block;
  padding: 0 4px;
  border-radius: 3px;
  background: var(--cyan-l);
  color: var(--cyan-d);
  font-family: monospace;
}
.nk-cb-bo-modal { z-index: var(--z-modal); }
/* `.nk-cb-unlink-modal` supprimée 2026-05-23 (chantier C, Phase 0a) :
   modale unlink remplacée par NkConfirmModal. */
.nk-cb-bo-dialog { width: 340px; max-width: 96vw; }
.nk-cb-bo-head { display: flex; align-items: flex-start; gap: 6px; }
.nk-cb-bo-meta { margin-left: 6px; }
.nk-cb-bo-site { font-size: 12px; font-weight: 700; color: var(--navy); }
.nk-cb-bo-id { font-size: 10px; color: var(--muted); }
.nk-cb-bo-list { display: flex; flex-direction: column; gap: 5px; }
.nk-cb-bo-link {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 1px solid #eef0f3;
  border-radius: 7px;
  background: var(--white);
  text-decoration: none;
  transition: background var(--t-fast), border-color var(--t-fast);
}
.nk-cb-bo-link:hover { background: #f0f9fa; border-color: var(--cyan); }
.nk-cb-bo-ico {
  width: 26px;
  height: 26px;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.nk-cb-bo-lbl { font-size: 12px; font-weight: 700; color: var(--navy); }
.nk-cb-bo-sub { margin-top: 1px; font-size: 10px; color: var(--muted); }
.nk-cb-bo-arr { margin-left: auto; font-size: 11px; color: var(--muted); }
/* `.nk-cb-unlink-copy` supprimée 2026-05-23 (chantier C, Phase 0a) :
   texte unlink jamais appliqué (idem ci-dessus, NkConfirmModal). */
.nk-avatar--client { --avatar-bg: #deeaf5; --avatar-color: #002c50; }

/* Raffinements visuels specifiques a la fiche client. */
.nk-cp-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.nk-cp-kpi-label { font-size: 11px; }
.nk-cp-kpi-value { font-size: 20px; font-weight: 500; }
.nk-card-head--between { justify-content: space-between; }
.nk-cp-note-body { font-size: 12px; line-height: 1.6; white-space: pre-wrap; }
.nk-cp-note-empty { font-size: 12px; font-style: italic; }
.nk-cp-card-head {
  background: var(--cp-card-bg, var(--surface));
  border-bottom: 1px solid var(--cp-card-border, var(--border));
}
.nk-cp-card-head-main { display: flex; align-items: center; gap: 8px; }
.nk-cp-card-icon {
  width: 22px;
  height: 22px;
  border-radius: 5px;
  background: var(--cp-card-accent-bg, var(--surface));
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.nk-cp-card-title { font-size: 12px; font-weight: 500; color: var(--navy); }
.nk-cp-count-badge {
  display: inline-flex;
  align-items: center;
  padding: 1px 7px;
  border-radius: 10px;
  font-size: 11px;
  font-weight: 600;
}
.nk-cp-count-badge--cyan { background: rgba(14,165,176,.13); color: var(--cyan-d); }
.nk-cp-count-badge--blue { background: rgba(26,74,138,.13); color: #1a4a8a; }
.nk-cp-balance { margin-left: 4px; font-size: 11px; font-weight: 700; }
.nk-cp-balance--good { color: var(--success); }
.nk-cp-balance--bad { color: var(--danger); }
.nk-cp-arrow { color: var(--cyan-d); font-size: 11px; }
.nk-cp-empty { padding: 12px; font-size: 13px; }
.nk-modal--md { max-width: 420px; }
.nk-cp-modal { width: 440px; }
.nk-cp-modal--sm { width: 400px; }
.nk-drawer__val--accent { color: var(--cyan-d); }
.nk-btn--ghost-danger { color: var(--danger); }
.nk-btn--ghost-danger:hover { background: var(--danger-l); }

/* Etat vide Hub quand aucun pole n'est disponible pour l'utilisateur. */
.nk-no-pole {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: #0f172a;
  color: #e2e8f0;
}
.nk-no-pole__card {
  width: 100%;
  max-width: 560px;
  padding: 28px 32px;
  border: 1px solid rgba(148,163,184,.22);
  border-radius: 18px;
  background: rgba(15,23,42,.92);
  box-shadow: 0 18px 48px rgba(15,23,42,.35);
}
.nk-no-pole__eyebrow {
  margin: 0 0 10px;
  color: #94a3b8;
  font-size: 12px;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.nk-no-pole__title { margin: 0 0 12px; font-size: 28px; line-height: 1.15; }
.nk-no-pole__text { margin: 0 0 18px; color: #cbd5e1; line-height: 1.6; }
.nk-no-pole__hint { margin: 0 0 24px; color: #94a3b8; line-height: 1.6; }
.nk-no-pole__actions { display: flex; gap: 12px; flex-wrap: wrap; }
.nk-no-pole__btn {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 10px 16px;
  border: 0;
  border-radius: 10px;
  cursor: pointer;
  font-family: inherit;
  font-size: inherit;
  font-weight: 600;
  line-height: 1.5;
  text-align: center;
  text-decoration: none;
}
.nk-no-pole__btn--primary { background: #f8fafc; color: #0f172a; }
.nk-no-pole__btn--secondary { border: 1px solid rgba(148,163,184,.35); color: #e2e8f0; }
.nk-topbar-btn {
  position: relative;            /* ancre la pastille compteur .nk-topbar-badge */
  width: 30px;
  height: 30px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: none;
  color: var(--slate);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  cursor: pointer;
  transition: all .14s;
}
.nk-topbar-btn:hover { background: var(--surface); border-color: var(--border-d); color: var(--navy); }
/* Pastille compteur sur un bouton topbar (ex. nb de demandes de modification
   ouvertes sur l'icône « Demander une modification »). Stacking LOCAL au bouton
   (z-index 1 < 100, toléré pour un badge sur élément — cf. CLAUDE.md § Z-index). */
.nk-topbar-badge {
  position: absolute;
  top: -5px;
  right: -5px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: var(--r-pill);
  background: var(--danger);
  color: #fff;
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  line-height: 16px;
  text-align: center;
  z-index: 1;
  pointer-events: none;
}
.nk-topbar-badge[hidden] { display: none; }
.nk-link-reset { color: inherit; text-decoration: none; }
/* Classes `.nk-breadcrumb`, `.nk-bc-*` et `.nk-bc-modules-popover*`
   retirées au Lot 3 du chantier topbar (2026-05-31). Service + Module
   sont désormais portés par les chips `.nk-topbar-chip` de
   `nk-topbar.js` ; le niveau profond reste signalé par la context bar
   de chaque module (`.nk-context-bar__num` / `__sub`). */
.nk-hub-main { display: flex; flex-direction: column; padding: 0; }
.nk-hub-scroll { flex: 1; overflow-y: auto; padding: 24px 28px 60px; }
.ud-lbl-note {
  font-size: 9px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--muted2);
}
.ud-row { display: flex; gap: 8px; }
.ud-field--grow { flex: 1; }
.sb-admin-label {
  flex: 1;
  color: var(--muted2);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 1.8px;
  text-transform: uppercase;
  white-space: nowrap;
}
.nk-mn-actions { display: flex; justify-content: flex-end; margin-top: 12px; }

/* ============================================================
   ALERTES — boîtes contextuelles (info, danger, warning, success, ia)
   ============================================================ */
.nk-alert {
  padding: 10px 14px;
  border-radius: var(--r-sm);
  border-left: 4px solid var(--border);
  font-size: 13px;
  line-height: 1.5;
  background: var(--surface);
  color: var(--navy);
}
.nk-alert--success { background: var(--success-l); border-left-color: var(--success); color: var(--success); }
.nk-alert--danger  { background: var(--danger-l);  border-left-color: var(--danger);  color: var(--danger); }
.nk-alert--warning { background: var(--warning-l); border-left-color: var(--warning); color: var(--warning); }
.nk-alert--info    { background: var(--info-l);    border-left-color: var(--info);    color: var(--info); }
.nk-alert--ia      { background: var(--ia-l);      border-left-color: var(--ia);      color: var(--ia); }
/* Modifier `--row` (audit secu 2026-05-30 [finding 10]) : layout icône
   à gauche, texte à droite, alignés verticalement. Permet de remplacer
   les `style="display:flex;align-items:center;gap:10px"` inline. */
.nk-alert--row     { display: flex; align-items: center; gap: var(--space-md); }

/* ============================================================
   SWITCH — toggle binaire (on/off) avec track + thumb animé.
   Utilisation :
     <label class="nk-switch"><input type="checkbox">
       <span class="nk-switch__track"></span>
       <span class="nk-switch__label">Libellé dynamique</span>
     </label>
   Le JS coche/décoche l'input ; CSS gère l'apparence via :checked.
   ============================================================ */
.nk-switch {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 11px; color: var(--slate); cursor: pointer; user-select: none;
  position: relative;
}
/* Variante LECTURE SEULE : le composant dit un état, il ne le règle pas. La
   main au survol promettait un clic qui ne fait rien (relevé en revue à froid
   le 2026-08-25, sur l'état d'affichage d'une langue du pilote de traduction). */
.nk-switch--ro { cursor: default; }
.nk-switch input {
  position: absolute;
  top: 0;
  left: 0;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.nk-switch__track {
  width: 28px; height: 16px; border-radius: 999px;
  background: var(--border-d); position: relative;
  transition: background .15s ease; flex-shrink: 0;
}
.nk-switch__track::after {
  content: ''; position: absolute; top: 2px; left: 2px;
  width: 12px; height: 12px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.2);
  transition: left .15s ease;
}
.nk-switch input:checked ~ .nk-switch__track { background: var(--cyan); }
.nk-switch input:checked ~ .nk-switch__track::after { left: 14px; }
.nk-switch__label { font-weight: 600; }

/* ============================================================
   DUAL-RANGE — curseur double poignée (aperçu facette filtres)
   Usage :
     <div class="nk-dual-range">
       <div class="nk-dual-range__track"><div class="nk-dual-range__fill"></div></div>
       <input type="range" id="pv-range-min" ...>
       <input type="range" id="pv-range-max" ...>
     </div>
============================================================ */
.nk-dual-range {
  position: relative;
  height: 24px;
}
.nk-dual-range input[type=range] {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 24px;
  margin: 0;
  background: transparent;
  -webkit-appearance: none;
  appearance: none;
  pointer-events: none;
}
.nk-dual-range input[type=range]::-webkit-slider-runnable-track { height: 0; }
.nk-dual-range input[type=range]:focus { outline: none; }
.nk-dual-range input[type=range]::-webkit-slider-thumb {
  pointer-events: all;
  -webkit-appearance: none;
  appearance: none;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid var(--primary);
  cursor: pointer;
  box-shadow: 0 1px 4px rgba(0,0,0,.18);
  margin-top: 3px;
}
.nk-dual-range input[type=range]::-moz-range-thumb {
  pointer-events: all;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid var(--primary);
  cursor: pointer;
  box-shadow: 0 1px 4px rgba(0,0,0,.18);
}
.nk-dual-range__track {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 4px;
  transform: translateY(-50%);
  background: var(--border);
  border-radius: 2px;
  pointer-events: none;
  overflow: hidden;
}
.nk-dual-range__fill {
  position: absolute;
  top: 0;
  height: 100%;
  background: var(--primary);
  border-radius: 2px;
}

/* ============================================================
   SEGMENTED TOGGLE — bascule 2 boutons (icône + texte) en pill
   Utilisation :
     <div class="nk-seg">
       <button class="is-on">icon Libellé A</button>
       <button>icon Libellé B</button>
     </div>
   ============================================================ */
.nk-seg {
  display: inline-flex; background: #fff; flex-shrink: 0;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 2px; gap: 2px; white-space: nowrap;
  /* Évite que le drag HTML5 remonte au parent draggable=true (ex. dans un bloc
     composant) et réordonne le groupe sur simple clic. */
  -webkit-user-drag: none;
}
/* Déclaration UNIQUE : `-webkit-user-drag` était posé par une seconde règle
   `.nk-seg button` juste au-dessus, fusionnée ici le 2026-08-05. Même raison
   que sur `.nk-seg` — empêcher le drag de remonter au parent draggable. */
.nk-seg button {
  display: inline-flex; align-items: center; gap: 4px;
  height: 20px; padding: 0 8px; border: 0; background: transparent;
  color: var(--muted); cursor: pointer; font-size: 10px; font-weight: 600;
  border-radius: 4px; font-family: inherit; white-space: nowrap; flex-shrink: 0;
  -webkit-user-drag: none;
}
.nk-seg button.is-on { background: var(--cyan); color: #fff; }
.nk-seg button:not(.is-on):hover { color: var(--navy); background: #f3f5f8; }
.nk-seg svg { width: 11px; height: 11px; flex-shrink: 0; }

/* ============================================================
   STOCK PILL — indicateur stock en pastille colorée compacte
   Valeurs : .nk-stock--ok (vert), --low (orange), --out (rouge)
   Alias visuel de nk-badge mais plus petit et dédié stock.
   ============================================================ */
.nk-stock {
  display: inline-flex; align-items: center; height: 18px; padding: 0 7px;
  border-radius: 999px; font-size: 10px; font-weight: 600; white-space: nowrap;
}
.nk-stock--ok  { background: var(--success-l); color: var(--success); }
.nk-stock--low { background: var(--warning-l); color: var(--warning); }
.nk-stock--out { background: var(--danger-l);  color: var(--danger);  }

/* ============================================================
   NCPACK — MODALE « Ajouter un composant au pack »
   Layout A2 : article + déclinaisons dépliables inline, sélection
   multiple, filtre marque, footer persistant. Utilisée par
   `NkNcpackShared.openProductPicker` (cf ncpack-shared.js).
   ============================================================ */
.nk-pp-modal { max-width: 720px; width: 92vw; display: flex; flex-direction: column; max-height: 86vh; }
.nk-pp-head  { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 14px; }
.nk-pp-search { display: flex; gap: 8px; margin-bottom: 10px; }
.nk-pp-search__input { flex: 1; margin-bottom: 0; }
.nk-pp-brand { height: 34px; min-width: 180px; max-width: 240px; }
.nk-pp-results {
  flex: 1; min-height: 0; overflow-y: auto;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--white);
}
.nk-pp-empty {
  padding: 18px; text-align: center; color: var(--muted); font-size: 12px;
}
.nk-pp-empty--err { color: var(--danger); }

/* Ligne produit */
.nk-pp-prod {
  border-bottom: 1px solid var(--border);
  transition: background .12s ease;
}
.nk-pp-prod:last-child { border-bottom: 0; }
.nk-pp-prod--single {
  display: grid; grid-template-columns: 20px 1fr auto; gap: 10px;
  align-items: center; padding: 10px 12px; cursor: pointer;
}
.nk-pp-prod--single:hover,
.nk-pp-prod--parent:hover > .nk-pp-prod__head { background: #f8fbfd; }
.nk-pp-prod.is-selected { background: #f5fbfe; }

.nk-pp-prod__head {
  display: grid; grid-template-columns: 20px 16px 1fr; gap: 8px;
  align-items: center; padding: 10px 12px; cursor: pointer;
}
.nk-pp-prod__meta { min-width: 0; }
.nk-pp-prod__name {
  font-size: 13px; font-weight: 600; color: var(--navy);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  display: flex; align-items: center; gap: 6px;
}
.nk-pp-prod__sub {
  font-size: 11px; color: var(--muted); margin-top: 2px;
  display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
}
.nk-pp-prod__stock { min-width: 0; }
.nk-pp-hidden { color: var(--muted); flex-shrink: 0; }
.nk-pp-brand-tag {
  display: inline-flex; align-items: center; height: 16px; padding: 0 6px;
  border-radius: 999px; font-size: 10px; font-weight: 600;
  background: var(--surface); color: var(--slate); border: 1px solid var(--border);
}
.nk-pp-counter {
  display: inline-flex; align-items: center; height: 16px; padding: 0 6px;
  border-radius: 999px; font-size: 10px; font-weight: 600;
  background: var(--cyan-l); color: var(--cyan-d);
}

/* Case à cocher NON NATIVE du hub, à trois états (vide / partiel / coché).
   Née dans le sélecteur de produits NCPACK, d'où son préfixe `nk-pp-`, elle est
   depuis le 2026-08-05 la case commune à tous les écrans qui ne peuvent pas
   utiliser `.nk-checkbox-accent` :
   - il faut un état PARTIEL, que `<input type="checkbox">` n'expose qu'en
     pilotant `indeterminate` en JavaScript après chaque rendu — un détour
     fragile pour un écran qui réécrit son `innerHTML` ;
   - ou la case est purement DÉCORATIVE dans une ligne entièrement cliquable, où
     un vrai `<input>` capterait le clic de la ligne (arbre de catégories).
   Balisage : `<button>` cliquable, `<span>` si décoratif. Un appelant qui gère
   trois états ajoute `role="checkbox"` + `aria-checked="true|mixed|false"` ;
   `is-on` / `is-indeterminate` portent l'apparence. */
.nk-pp-cb {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 4px;
  border: 1.5px solid var(--border-d); background: var(--white);
  cursor: pointer; padding: 0; color: var(--white); transition: all .12s ease;
  flex-shrink: 0;
}
.nk-pp-cb:hover { border-color: var(--cyan); }
.nk-pp-cb.is-on,
.nk-pp-cb.is-indeterminate { background: var(--cyan); border-color: var(--cyan); }
.nk-pp-cb__dash {
  display: block; width: 9px; height: 2px; border-radius: 1px; background: var(--white);
}
/* Case VERROUILLÉE : présente mais pas cochable ici (déclinaison inactive côté
   PrestaShop dans NCPACK, catégorie parente en mode feuille-cascade dans le
   sélecteur de catégories). Le contour pointillé dit « emplacement de case,
   sans case ». Extraite le 2026-08-05 de trois styles inline qui la
   reposaient à l'identique. */
.nk-pp-cb--locked {
  border-style: dashed;
  background: transparent;
  cursor: not-allowed;
  opacity: .6;
}
.nk-pp-cb:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* Chevron déploiement */
.nk-pp-chev {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; border: 0; background: transparent;
  color: var(--muted); cursor: pointer; padding: 0;
}
.nk-pp-chev:hover { color: var(--navy); }

/* Volet déclinaisons */
.nk-pp-decl-panel {
  background: var(--surface); border-top: 1px dashed var(--border);
  padding: 4px 0 4px 28px;
}
.nk-pp-decl {
  display: grid; grid-template-columns: 20px 1fr auto auto; gap: 10px;
  align-items: center; padding: 6px 12px; cursor: pointer;
  border-radius: 4px;
}
.nk-pp-decl:hover { background: var(--white); }
.nk-pp-decl.is-selected { background: #f0f8fc; }
.nk-pp-decl__label {
  font-size: 12px; color: var(--navy); font-weight: 500;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nk-pp-decl__ref {
  font-size: 10px; color: var(--muted); font-family: ui-monospace, Menlo, Consolas, monospace;
}

/* Footer persistant */
.nk-pp-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; border-top: 1px solid var(--border);
  padding: 12px 0 0; margin-top: 12px;
}
.nk-pp-foot__info { font-size: 12px; color: var(--slate); font-weight: 500; }
.nk-pp-foot__actions { display: flex; gap: 8px; }

/* Classes `.nk-pp-skel*` supprimées 2026-05-23 (chantier C, Phase 0a) :
   skeleton de chargement NCPACK jamais branché côté JS. Le keyframes
   `nk-pp-shimmer` est conservé — réutilisé par `.nk-tree__skel`. */
@keyframes nk-pp-shimmer {
  0%   { background-position: 100% 0; }
  100% { background-position: -100% 0; }
}

/* Dernier palier mobile des écrans clients. */
@media (max-width: 720px) {
  .nk-cp-grid  { grid-template-columns: 1fr; }
  .nk-cp-right { border-left: none; border-top: 1px solid var(--border); }
}

/* ============================================================
   TREE PICKER — Modale de sélection dans une arborescence
   Composant : frontend/shared/nk-category-picker.js
   Contrat : conteneur .nk-tree-picker (overlay), panneau fixe, corps
   scrollable, classes .nk-tree__node pour chaque ligne.
   ============================================================ */
.nk-tree-picker { align-items: center; padding: 24px; }
.nk-tree-picker__panel {
  width: 640px; max-width: 100%;
  max-height: min(640px, calc(100vh - 48px));
  padding: 0;
  display: flex; flex-direction: column;
  overflow: hidden;
}

/* Variante drawer latéral (mode multi) : panneau collé à droite,
   pleine hauteur, slide depuis la droite. Overlay navy 30% pour
   garder la fiche lisible derrière. */
.nk-tree-picker--drawer {
  align-items: stretch;
  justify-content: flex-end;
  padding: 0;
  background: rgba(15, 39, 68, 0.30);
}
.nk-tree-picker--drawer .nk-tree-picker__panel {
  width: 520px;
  max-width: 100%;
  height: 100vh;
  max-height: 100vh;
  border-radius: 0;
  border-left: 1px solid var(--border);
  transform: translateX(100%);
  transition: transform .2s cubic-bezier(.4,0,.2,1);
}
.nk-tree-picker--drawer.open .nk-tree-picker__panel {
  transform: translateX(0);
}

.nk-tree-picker__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.nk-tree-picker__title {
  font-size: 14px; font-weight: 700; color: var(--navy);
}

/* Indicateur de chargement async des compteurs nb_products_recursive.
   Visible uniquement pendant que loadCounts est en cours (5-10 s typique).
   Le dot et le texte pulsent en décalé pour rendre l'animation lisible
   sans être agressive. `margin-right: auto` pousse le bouton close à droite. */
.nk-tree-picker__counts-loading {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; font-weight: 500; color: var(--muted);
  margin-left: 12px; margin-right: auto;
  animation: nk-pulse-soft 1.4s ease-in-out infinite;
}
.nk-tree-picker__counts-dot {
  display: inline-block;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--cyan);
  animation: nk-pulse-dot 1.0s ease-in-out infinite;
}
@keyframes nk-pulse-soft { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }
@keyframes nk-pulse-dot  { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.35); opacity: 0.45; } }
.nk-tree-picker__close {
  width: 28px; height: 28px; border-radius: var(--r-sm);
  border: 1px solid var(--border); background: var(--white);
  display: flex; align-items: center; justify-content: center;
  color: var(--slate); cursor: pointer;
  transition: all var(--t-fast);
}
.nk-tree-picker__close:hover  { border-color: var(--border-d); color: var(--navy); }
/* `.nk-tree-picker__refresh` supprimée 2026-05-23 (chantier C, Phase 0a) :
   bouton refresh non implémenté dans le tree picker. */

.nk-tree-picker__toolbar {
  display: flex; gap: 8px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.nk-tree-picker__search {
  flex: 1;
  display: flex; align-items: center; gap: 8px;
  padding: 0 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--slate);
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.nk-tree-picker__search:focus-within { border-color: var(--cyan); box-shadow: var(--focus-ring); background: var(--white); }
.nk-tree-picker__search-input {
  flex: 1; border: 0; background: transparent; outline: 0;
  padding: 8px 0; font: inherit; font-size: 13px; color: var(--navy);
}
.nk-tree-picker__ref {
  display: flex; align-items: center; gap: 4px;
  padding: 0 6px 0 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.nk-tree-picker__ref:focus-within { border-color: var(--cyan); box-shadow: var(--focus-ring); background: var(--white); }
.nk-tree-picker__ref.is-invalid,
.nk-tree-picker__ref-input.is-invalid { border-color: var(--danger); }
.nk-tree-picker__ref-label {
  font-size: 10px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--slate);
  padding: 0 8px; border-right: 1px solid var(--border);
  align-self: stretch; display: flex; align-items: center;
}
.nk-tree-picker__ref-input {
  width: 80px; border: 0; background: transparent; outline: 0;
  padding: 8px 4px; font: inherit; font-size: 13px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--navy);
}

/* Barre d'options optionnelle (toggle « catégories cachées »). */
.nk-tree-picker__options {
  display: flex; align-items: center; gap: 12px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  flex-shrink: 0;
}
.nk-tree-picker__toggle {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 12px; color: var(--slate);
  cursor: pointer; user-select: none;
}
.nk-tree-picker__toggle-input {
  width: 14px; height: 14px; accent-color: var(--cyan);
  cursor: pointer; margin: 0;
}
.nk-tree-picker__toggle:hover { color: var(--navy); }

.nk-tree-picker__body {
  flex: 1;
  overflow-y: auto;
  padding: 6px 0;
  min-height: 280px;
}

.nk-tree-picker__foot {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 14px;
  border-top: 1px solid var(--border);
  background: var(--surface);
  flex-shrink: 0;
}
.nk-tree-picker__summary {
  flex: 1;
  font-size: 12px; color: var(--navy);
  line-height: 1.4;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nk-tree-picker__summary--empty { color: var(--muted); font-style: italic; }
.nk-tree-picker__summary-id {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  color: var(--slate);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 1px 5px;
  margin-left: 2px;
}
.nk-tree-picker__summary-crumb { color: var(--slate); font-size: 11px; }
.nk-tree-picker__actions { display: flex; gap: 8px; flex-shrink: 0; }

/* États vides / erreur du corps. */
.nk-tree-picker__empty,
.nk-tree-picker__error {
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  padding: 40px 16px;
  color: var(--slate);
  font-size: 13px; text-align: center;
}
.nk-tree-picker__error { color: var(--danger); }
.nk-tree-picker__error-msg { color: var(--muted); font-size: 12px; }

/* ─── Lignes d'arbre (.nk-tree__node) ─── */
.nk-tree__node {
  display: grid;
  grid-template-columns: 20px 20px 1fr auto auto;
  align-items: center;
  gap: 6px;
  padding: 6px 14px 6px 8px;
  font-size: 13px; color: var(--navy);
  cursor: pointer;
  user-select: none;
  transition: background var(--t-fast);
  border-radius: 0;
}
/* Mode multi : ajoute une colonne pour la checkbox visuelle entre le
   chevron et l'icône folder. Sans cette règle, la checkbox déborde de
   la grille à 5 cols et l'#id se retrouve sur une 2e ligne (RTL apparent). */
.nk-tree__node--multi {
  grid-template-columns: 20px 16px 20px 1fr auto auto auto;
}
/* Étoile « principale » entre le label et le count en mode multi-principal.
   Cliquable, hover discret. La case n'est rendue que sur les lignes
   cochées (cf. JS) — sur les autres, le slot reste vide mais réservé. */
.nk-tree__star {
  width: 22px; height: 22px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; background: transparent; cursor: pointer; padding: 0;
  border-radius: var(--r-sm);
  transition: background var(--t-fast);
}
.nk-tree__star:hover { background: rgba(14,165,176,.10); }
/* Étoile ACTIVE en cyan (demande Lionel du 2026-08-14) — MÊME token que
   `.nk-pim-cat-principal__star`, l'étoile de lecture de l'arbre. Sans cette
   règle, le glyphe héritait de `currentColor`, donc de l'encre de la ligne : la
   marque de la branche principale se lisait en noir à droite et en cyan à
   gauche, pour désigner la même chose. */
.nk-tree__star--on { color: var(--cyan); }
.nk-tree__star--on:hover { background: rgba(14,165,176,.18); }
.nk-tree__node:hover { background: var(--surface); }
.nk-tree__node--selected {
  background: var(--cyan-l);
  font-weight: 600;
}
.nk-tree__node--selected:hover { background: var(--cyan-l); }
.nk-tree__node:focus-visible { outline: 2px solid var(--cyan); outline-offset: -2px; }

/* ── ANCÊTRE DÉRIVÉ (règle R2) — autorisation nominale de Lionel, 2026-08-13.
      Un ancêtre accroché par CASCADE parce qu'une feuille choisie en descend.
      Il est réellement écrit en boutique, mais il n'a jamais été choisi : il ne
      se décoche donc pas, et il ne doit pas se lire comme une feuille CHOISIE —
      c'est tout l'objet de R2. D'où l'encre atténuée et l'absence de pointeur,
      là où `--selected` porte le fond cyan et la graisse.
      Classe du NOYAU VISUEL d'arbre, pas de la fiche PIM : les écrans
      Rangement, Affectation et Remplissage partagent le même gabarit de ligne.
      Uniquement des tokens existants — aucune couleur nouvelle. ── */
.nk-tree__node--derive {
  color: var(--muted);
  cursor: default;
}
/* La coche reste VISIBLE — l'attachement est réel, le cacher mentirait — mais
   en gris de bordure et non en cyan : « accroché », pas « choisi ». */
.nk-tree__node--derive .nk-pp-cb.is-on {
  background: var(--border-d);
  border-color: var(--border-d);
}

/* ── LIGNE VERROUILLÉE — cesse d'être une classe fantôme (2026-08-13).
      Elle était émise par `nk-category-picker.js` depuis le 2026-08-12 sans
      AUCUNE règle : seule la CASE portait le verrou (`.nk-pp-cb--locked`), la
      ligne, elle, gardait le pointeur « main » d'un élément cliquable et
      promettait une sélection qui n'arrivait jamais. Le clic déplie toujours la
      branche — la ligne reste donc interactive, d'où `cursor: default` et non
      `not-allowed`, qui dirait à tort que le clic ne fait rien. ── */
.nk-tree__node--locked {
  cursor: default;
}

.nk-tree__chev {
  width: 20px; height: 20px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; background: transparent;
  color: var(--slate); cursor: pointer; padding: 0;
  border-radius: var(--r-sm);
}
.nk-tree__chev:hover:not(.nk-tree__chev--placeholder) { background: rgba(15,23,42,.06); color: var(--navy); }
.nk-tree__chev--placeholder { cursor: default; pointer-events: none; }
.nk-tree__chev svg { transition: transform var(--t-fast); }
/* Nœud DÉPLIÉ. Le chevron pivotait jusqu'ici par un `style="transform:…"` écrit
   au rendu, dans chaque module qui dessine un arbre — un style inline que le
   lint ne voit pas et que personne ne peut surcharger. L'état vit donc ici. */
.nk-tree__chev--open svg { transform: rotate(90deg); }

/* ─── POIGNÉE DE GLISSEMENT ───
   `.nk-sub-handle` (11px, `--muted`) était trop discrète pour une ligne de
   tableau : on ne la voyait pas, donc on ne savait pas qu'on pouvait glisser.
   Cible de 22px — la taille minimale confortable au pointeur — et surtout DEUX
   ÉTATS, parce qu'une poignée qui a toujours le même air ment dans un cas sur
   deux : grise elle est inerte, bleue elle est prête. Le `cursor` suit l'état,
   il ne le contredit jamais. */
.nk-drag-grip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px; height: 22px;
  border-radius: var(--r-sm);
  color: var(--muted);
  cursor: default;
  transition: color var(--t-fast), background var(--t-fast);
}
.nk-drag-grip--on         { color: var(--cyan-d); cursor: grab; }
.nk-drag-grip--on:hover   { background: var(--cyan-l); }
.nk-drag-grip--on:active  { cursor: grabbing; }

/* ─── LIGNE D'ARBRE COMME CIBLE DE DÉPÔT ───
   `.nk-pane__body--dropzone` vise le CORPS ENTIER d'une colonne ; ici on dépose
   DANS une ligne précise. Et `.nk-drop-item` ne convient pas non plus : il
   dessine un trait d'insertion ENTRE deux lignes, pour du réordonnancement.
   L'état de refus n'a AUCUN équivalent, et il n'est pas décoratif : sans lui,
   un dépôt impossible ne produit rien du tout — le pire retour possible, parce
   que « il ne s'est rien passé » se lit comme une panne. */
.nk-tree-drop { transition: background var(--t-fast), box-shadow var(--t-fast); }
.nk-tree-drop.is-drop-target {
  background: var(--cyan-l);
  box-shadow: inset 0 0 0 2px var(--cyan);
}
.nk-tree-drop.is-drop-refused {
  background: var(--danger-l);
  cursor: not-allowed;
}

/* ─── PROFONDEUR D'ARBRE ───
   L'indentation est une CLASSE, pas un `style="padding-left:Npx"` calculé au
   rendu : c'est le dernier style inline qu'un arbre a besoin d'écrire, et il
   apparaît une fois par ligne — donc trois cents fois sur l'arbre nocika.
   Pas de token d'espacement ici : ce n'est pas une marge de mise en page mais
   un pas de gouttière d'arbre, constant, dont la valeur (18px) doit rester
   celle du décalage historique pour que les arbres du Hub se ressemblent.
   Au-delà du niveau 9 l'indentation SATURE plutôt que de continuer : à cette
   profondeur le libellé sortirait de la colonne, et une hiérarchie illisible
   est pire qu'une hiérarchie aplatie. */
.nk-tree__node--d0 { padding-left: 8px; }
.nk-tree__node--d1 { padding-left: 26px; }
.nk-tree__node--d2 { padding-left: 44px; }
.nk-tree__node--d3 { padding-left: 62px; }
.nk-tree__node--d4 { padding-left: 80px; }
.nk-tree__node--d5 { padding-left: 98px; }
.nk-tree__node--d6 { padding-left: 116px; }
.nk-tree__node--d7 { padding-left: 134px; }
.nk-tree__node--d8 { padding-left: 152px; }
.nk-tree__node--d9 { padding-left: 170px; }

.nk-tree__icon { flex-shrink: 0; }

/* ─── NATURE D'UN NŒUD D'ARBRE — la feuille est ce qui compte ───
   Règle Lionel du 2026-08-11, valable dans TOUTE arborescence du Hub : la
   feuille porte les produits et c'est elle qu'on vise pour écrire ; la branche
   est de la navigation. L'orange va donc à la feuille, la branche recule.

   ⚠️ C'est une RÈGLE PAR DÉFAUT, plus un modifieur d'écran : `.nk-tree--leaf-only`
   a été supprimée le 2026-08-11. Le markup vient de `NocikaShared.treeIcon()`,
   source unique — le récit complet (ce qui avait divergé, et pourquoi) est dans
   son JSDoc, `frontend/shared/nocika.js`.

   ⚠️ Teintes : les tokens `--nature-file` / `--nature-hidden` de la maquette du
   2026-08-10, et non le `#ea580c` qui était écrit en dur ici. Nommés par leur
   SENS, déjà employés par le board ncmenu — les surfaces convergent.
   ⚠️ Effacé et non blanc : un dossier plein en blanc sur fond blanc disparaît,
   et un trou dans la colonne se lit comme un défaut de rendu. */
.nk-tree__icon--folder { color: var(--nature-hidden); }
.nk-tree__icon--file   { color: var(--nature-file); }

/* ─── ARBRE À NIVEAUX NOMMÉS (catalogue → univers → sous-univers) ───
   Autorisation nominale de Lionel, 2026-08-16, pour l'arbre des guides d'achat.

   Ici les trois niveaux SONT des dossiers — c'est leur nature, pas leur état.
   Dessiner en document un univers encore vide était juste au regard de la règle
   générale (« la feuille est ce qui compte ») et FAUX au regard de celle-ci :
   un bouton « nouvelle famille » qui produit une feuille ne se comprend pas.

   Seul le DERNIER niveau accueille des documents : c'est lui qu'on peint en
   orange, via `matiere` — le même axe que pour les feuilles, appliqué au
   dossier. Les quatre autres écrans d'arbre ne passent pas ce drapeau sur un
   dossier : rien ne change pour eux.
   Cf. docs/UX.md § Arborescences de catégories. */
.nk-tree__icon--folder.nk-tree__icon--matiere { color: var(--nature-file); }

/* Ligne de FAMILLE (par opposition aux lignes de document qu'elle contient) :
   police à 15px, au-dessus de la base de l'arbre. Une arborescence à trois
   niveaux se parcourt du regard — les libellés y sont l'information, pas un
   détail de tableau. Modifieur porté par l'écran, jamais par `.nk-tree__node`
   lui-même : les arbres à trois cents nœuds des Filtres restent à 13px. */
.nk-tree__node--famille { font-size: 15px; }

/* UNIVERS — le niveau intermédiaire, celui qui structure la lecture. Gras et
   dossier en gris FONCÉ : sans ce contraste, les trois niveaux se ressemblent
   et l'œil ne trouve plus où commence une branche. */
.nk-tree__node--univers .nk-tree__label { font-weight: var(--fw-semibold); }
.nk-tree__node--univers .nk-tree__icon--folder { color: var(--slate); }

/* ─── GRILLE DE LIGNE DE CET ARBRE-LÀ (six pistes) ───
   Lionel, 2026-08-16 : la poignée de glissement passe EN TÊTE de ligne, et le
   compteur se colle au libellé.

   `.nk-tree__node` n'est PAS touchée — quatre autres arbres en dépendent
   (picker, sidebar d'audit, Filtres, Rangement) et ils gardent leurs cinq
   pistes `20px 20px 1fr auto auto`. Le modifieur est posé sur le CONTENEUR de
   cet arbre (`.nk-tree--guides`, écrans #/admin/catalogue-guides et
   #/admin/categories-cms) plutôt que sur chaque ligne : la ligne de famille et
   la ligne de guide doivent partager exactement les mêmes pistes, sans quoi
   les libellés d'un guide ne s'alignent plus sous celui de sa famille.

     poignée · chevron · icône · libellé · compteur · actions

   La poignée ouvre la ligne (22px, la largeur de `.nk-drag-grip`) : on
   l'attrape avant de lire, et en fin de ligne elle se confondait avec les
   boutons d'action.

   ⚠️ Le `1fr` est sur le COMPTEUR, pas sur le libellé — c'est toute l'astuce.
   Un libellé élastique poussait le compteur à l'autre bout de la ligne, si loin
   qu'il ne se rapportait plus à rien. En `auto`, le libellé hugge son texte et
   le compteur démarre juste derrière ; le libellé peut toujours rétrécir et
   passer en ellipse (`min-width:0` + `overflow:hidden`), la piste `auto`
   descend jusqu'à ce minimum. Les actions ferment la ligne en `auto`. */
.nk-tree--guides .nk-tree__node {
  grid-template-columns: 22px 20px 20px auto 1fr auto;
}

/* Compteur à 13px dans cet arbre : la ligne de famille est à 15px
   (`.nk-tree__node--famille`), le compteur doit rester DESSOUS pour être plus
   discret que le nom — mais pas au plancher de 11px des métas, illisible collé
   à un libellé de cette taille. Valeur propre à l'écran, comme le 15. */
/* Le compteur reprend la BASE typographique du Hub, sous la ligne de famille
   qui est au-dessus. `--fs-base` et non `13px` recopié : la valeur était
   annotée « propre à l'écran », ce qui était faux — c'est le token de corps du
   Hub, et le recopier aurait décroché cette règle le jour où il bouge. */
.nk-tree--guides .nk-tree__count { font-size: var(--fs-base); }

/* Anomalie de STRUCTURE sur une ligne d'arbre — posée à côté de l'icône de
   nature, jamais à sa place : « ce que c'est » et « est-ce sain » sont deux
   informations. En `--warning` : ce n'est pas une erreur du Hub, c'est un
   catalogue à réparer côté PrestaShop. */
.nk-tree__alerte { flex-shrink: 0; margin-left: 4px; color: var(--warning); }

/* Feuille qui a de la MATIÈRE — peinte pleine. L'axe plein/contour existait
   déjà dans les deux icônes, il devient porteur de sens : contour = feuille
   vide, plein = il y a de quoi faire. Ce que « matière » signifie appartient à
   l'écran (par défaut : elle porte des produits).
   Le pli du document est retracé en clair PAR-DESSUS le remplissage — sans lui
   la silhouette pleine perd son identité de document et se confond avec une
   pastille. */
.nk-tree__icon--matiere path     { fill: currentColor; }
.nk-tree__icon--matiere polyline { stroke: var(--white); }

/* Sélection : seul le DOSSIER vire au cyan, comme avant l'inversion. Étendre
   la règle à la feuille aurait été un changement visuel de plus, non demandé
   et non couvert — la feuille garde donc son orange sur une ligne
   sélectionnée. */
.nk-tree__node--selected .nk-tree__icon--folder { color: var(--cyan-d); }

.nk-tree__label {
  min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nk-tree__mark { background: #fef3c7; color: inherit; padding: 0 1px; border-radius: 2px; }

/* Catégorie cachée (active=0) : visuel discret pour ne pas confondre
   avec une catégorie normale à l'œil. */
.nk-tree__node--hidden .nk-tree__label,
.nk-tree__node--hidden .nk-tree__count { color: var(--muted); font-style: italic; }
.nk-tree__node--hidden .nk-tree__icon--folder,
.nk-tree__node--hidden .nk-tree__icon--file { opacity: .55; }
.nk-tree__badge-hidden {
  display: inline-block;
  margin-left: 8px;
  font-size: 10px; font-weight: 600;
  color: var(--warning); background: var(--warning-l);
  border: 1px solid var(--warning-b);
  padding: 0 5px; border-radius: var(--r-sm);
  font-style: normal; letter-spacing: .02em;
  vertical-align: middle;
}

/* ─── Cases d'un arbre — TROIS classes supprimées le 2026-08-05 ───
   `.nk-check-etat`, `.nk-check-selection` et `.nk-check-tri` vivaient ici et
   redessinaient des composants qui existaient déjà :
   - `.nk-check-etat` → `.nk-checkbox-accent`, la case native du hub ;
   - `.nk-check-selection` → `.nk-checkbox-accent` aussi. Sa forme ronde
     distinguait « cocher VISE une branche » de « cocher ÉCRIT en base », mais un
     rond signifie « choix exclusif » partout ailleurs dans la plateforme. La
     distinction est portée par le libellé de l'écran et le `title` de la case,
     pas par la forme ;
   - `.nk-check-tri` → `.nk-pp-cb` / `.nk-pp-cb__dash`, la case à trois états du
     hub (cf. plus bas), au même balisage `<button>`.
   Le hub n'a plus que DEUX représentations de case : la native
   (`.nk-checkbox-accent`) là où la case EST le contrôle, et `.nk-pp-cb` là où
   il faut un troisième état ou une case purement décorative dans une ligne
   cliquable. */

.nk-tree__count {
  font-size: 11px; color: var(--muted);
  font-variant-numeric: tabular-nums;
  padding-left: 8px;
}
.nk-tree__count--empty { padding: 0; }

.nk-tree__id {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10px;
  color: var(--slate);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 1px 5px;
}

/* ─── Skeleton de chargement ─── */
.nk-tree__skel-row {
  display: grid;
  grid-template-columns: 20px 20px 1fr auto;
  gap: 8px; align-items: center;
  padding: 8px 14px 8px 8px;
}
.nk-tree__skel {
  display: inline-block; height: 14px; border-radius: 4px;
  background: linear-gradient(90deg, #eef2f7 0%, #f7f9fc 50%, #eef2f7 100%);
  background-size: 200% 100%;
  animation: nk-pp-shimmer 1.2s ease-in-out infinite;
}
.nk-tree__skel--chev { width: 12px; height: 12px; }
.nk-tree__skel--icon { width: 14px; height: 14px; border-radius: 3px; }
.nk-tree__skel--pill { width: 32px; height: 14px; border-radius: 999px; }

/* ════════════════════════════════════════════════════════════════
   Sidebar catalogue (NkCatalogSidebar)
   2 modes : expanded (240 px) / collapsed (56 px). Le mode `hidden`
   historique a été retiré pour éviter une sidebar invisible sans
   porte de sortie — la bascule reste portée par le toggle interne.
   Réutilise le skin du picker — nœuds rendus en .nk-tree__*.
   Spec : docs/UX.md section « choix catalogue ».
   ════════════════════════════════════════════════════════════════ */
.nk-cat-sidebar {
  background: var(--white);
  border-right: 1px solid var(--border);
  display: flex; flex-direction: column;
  overflow: hidden;
  transition: width .2s cubic-bezier(.4,0,.2,1);
  flex-shrink: 0;
  position: relative;
  height: 100%;
}
/* Mode déplié : la sidebar remplit pile sa cellule grid parente (la
   poignée drag pilotée par NocikaShared.initResizableSidebar pose la
   largeur côté grid via gridTemplateColumns, la sidebar suit en width:100%).
   Pas de min/max ici : la borne minLeft est appliquée côté handler de drag.
   Le mode collapsed garde une largeur fixe (pastilles 3 lettres). */
.nk-cat-sidebar--expanded  { width: 100%; }
.nk-cat-sidebar--collapsed { width: 56px; }

.nk-cat-sidebar__head {
  height: 40px;
  display: flex; align-items: center;
  padding: 0 6px 0 14px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  background: var(--surface);
}
.nk-cat-sidebar__title {
  flex: 1;
  font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .6px; color: var(--muted);
  white-space: nowrap; overflow: hidden;
}

/* Site picker (header sidebar) — remplace .nk-cat-sidebar__title quand
   l'opt siteSelector est fournie. Trigger inline dans le header,
   dropdown attaché au body via .nk-cat-sidebar__site-menu. */
.nk-cat-sidebar__site-trigger {
  flex: 1; min-width: 0;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 8px;
  font-size: 12px; font-weight: 600;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  cursor: pointer;
  color: var(--navy);
  overflow: hidden;
  text-align: left;
  font-family: inherit;
}
.nk-cat-sidebar__site-trigger:hover { border-color: var(--border-d); background: var(--surface); }
.nk-cat-sidebar__site-trigger.is-open { border-color: var(--cyan); background: var(--cyan-l); }
.nk-cat-sidebar__site-dot {
  width: 8px; height: 8px; border-radius: 50%;
  display: inline-block; flex-shrink: 0;
}
.nk-cat-sidebar__site-label {
  flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nk-cat-sidebar__site-chev { color: var(--muted); flex-shrink: 0; }

/* Mode collapsed : trigger réduit à la pastille seule (cliquable). */
.nk-cat-sidebar--collapsed .nk-cat-sidebar__site-trigger {
  width: 36px; height: 36px;
  padding: 0;
  justify-content: center;
  flex: 0 0 auto;
}
.nk-cat-sidebar--collapsed .nk-cat-sidebar__site-label,
.nk-cat-sidebar--collapsed .nk-cat-sidebar__site-chev { display: none; }
.nk-cat-sidebar--collapsed .nk-cat-sidebar__site-dot { width: 12px; height: 12px; }

/* Dropdown menu sites — attaché au <body> pour échapper à l'overflow.
   Doit passer au-dessus de la topbar (--z-topbar) mais sous les modales,
   d'où --z-overlay. */
.nk-cat-sidebar__site-menu {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 12px 32px rgba(15,39,68,.18);
  min-width: 220px;
  z-index: var(--z-overlay);
  overflow: hidden;
  font-family: inherit;
}
.nk-cat-sidebar__site-item {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 14px;
  font-size: 13px; color: var(--navy);
  cursor: pointer;
  background: var(--white);
  border: 0; width: 100%; text-align: left;
  font-family: inherit;
}
.nk-cat-sidebar__site-item:hover { background: var(--surface); }
.nk-cat-sidebar__site-item.is-active { background: var(--cyan-l); font-weight: 600; }
.nk-cat-sidebar__toggle {
  width: 26px; height: 26px;
  border: 0; background: transparent;
  color: var(--slate); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-sm);
}
.nk-cat-sidebar__toggle:hover { background: rgba(15,23,42,.06); color: var(--navy); }
.nk-cat-sidebar__body {
  flex: 1;
  overflow-y: auto; overflow-x: hidden;
  padding: 4px 0;
}
.nk-cat-sidebar--collapsed .nk-cat-sidebar__title { display: none; }
.nk-cat-sidebar--collapsed .nk-cat-sidebar__head  { padding: 0 6px; justify-content: center; }

/* Mode collapsed : pastilles 3 lettres + badge compteur sous le label */
.nk-cat-sidebar__pill {
  display: flex; flex-direction: column; align-items: center;
  padding: 8px 4px; cursor: pointer;
  font-size: 10px; font-weight: 700; color: var(--slate);
  text-transform: uppercase; letter-spacing: .5px;
  border-left: 3px solid transparent;
  transition: background var(--t-fast);
  user-select: none;
  text-align: center;
  line-height: 1.2;
}
.nk-cat-sidebar__pill:hover { background: var(--surface); color: var(--navy); }
.nk-cat-sidebar__pill--active {
  background: var(--cyan-l); color: var(--cyan-d);
  border-left-color: var(--cyan);
}
.nk-cat-sidebar__pill-badge {
  margin-top: 4px;
  font-size: 9px; font-weight: 700;
  min-width: 18px;
  padding: 1px 5px; border-radius: 999px;
  background: var(--surface); color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.nk-cat-sidebar__pill-badge--has {
  background: var(--warning-l); color: var(--warning);
}

/* Indicateur de scan dans le node tree (révision 2026-05-01).
   Visuel binaire sobre, sans fond/bordure :
     - `.nk-tree__defect-icon--direct` → triangle orange, défaut détecté
       directement sur cette catégorie.
     - `.nk-tree__defect-icon--ok` → check vert, catégorie auditée et propre.
   Variante `--inherited` (gris atténué) retirée : avec l'auto-expand
   post-scan, l'utilisateur voit le défaut directement sur les feuilles,
   pas besoin de signal sur les ancêtres. */
.nk-tree__defect-icon {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  line-height: 1;
}
.nk-tree__defect-icon--direct {
  color: var(--danger, #dc2626);
}
.nk-tree__defect-icon--ok {
  color: var(--success, #16a34a);
}
/* État « non audité » : `?` gris, dimensions identiques à un SVG 14px
   pour aligner verticalement avec ✓/✗. Signale explicitement « inconnu »
   plutôt qu'omettre le glyph (qui se lirait comme « propre »). */
.nk-tree__defect-icon--unknown {
  color: var(--muted);
  width: 14px;
  height: 14px;
  font-size: 13px;
  font-weight: 700;
  justify-content: center;
}

/* ─── Audit catalogue : table flat (refonte 2026-05-01) ─── */

/* Pastille type catégorie (folder/file) en première colonne. Disque clair
   encadrant l'icône, aligné verticalement sur le titre + sous-titre de la
   cellule catégorie. Le code couleur distingue les deux natures :
     - `--intermediate` → violet/indigo (catégorie container)
     - `--final` → vert (catégorie feuille où sont classés les produits) */
.nk-cat-type-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  flex-shrink: 0;
}
.nk-cat-type-badge--intermediate {
  background: #ede9fe;
  color: #6366f1;
}
.nk-cat-type-badge--final {
  background: var(--success-l, #f0fdf4);
  color: var(--success, #16a34a);
}

/* Cellule catégorie : titre principal + sous-titre « Intermédiaire · niveau X/Y ».
   `nk-cat-row__name` n'apporte rien hors d'éviter un wrapping bizarre du
   badge `cachée`. `nk-cat-row__sub` reprend la couleur du badge type pour
   renforcer le code visuel. */
.nk-cat-row__name {
  display: inline;
  line-height: 1.3;
}
.nk-cat-row__sub {
  font-size: 11px;
  margin-top: 2px;
  font-weight: 500;
}
.nk-cat-row__sub--intermediate {
  color: #6366f1;
}
.nk-cat-row__sub--final {
  color: var(--success, #16a34a);
}
.nk-cat-row__sub-sep {
  margin: 0 4px;
  color: var(--muted);
  opacity: 0.6;
}

/* Barre de progression % dispo. Couleur déterminée par variante (success /
   warning / danger). Compactée 2026-05-02 pour gagner de la place
   horizontale dans la table audit catalogue (4 colonnes numériques + 2
   barres + défauts + liens = beaucoup). */
.nk-cat-progress {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 70px;
}
.nk-cat-progress__bar {
  flex: 1;
  height: 5px;
  border-radius: 999px;
  background: #f1f5f9;
  overflow: hidden;
  min-width: 40px;
  max-width: 70px;
}
.nk-cat-progress__fill {
  height: 100%;
  border-radius: 999px;
  transition: width .25s ease;
}
.nk-cat-progress__label {
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  min-width: 30px;
  text-align: right;
}

/* Compactage scopé audit catalogue : la table peut dépasser le container
   horizontalement, on laisse `.nk-table-wrap { overflow:auto }` (défaut
   nocika.css) gérer le scrollbar. Sans ça, `width:100%` du `table.nk-table`
   tassait les colonnes et masquait visuellement les dernières.
   `min-width: max-content` force le respect de la largeur intrinsèque. */
.nk-audit-cat table.nk-table {
  min-width: max-content;
}
.nk-audit-cat table.nk-table th,
.nk-audit-cat table.nk-table td {
  white-space: nowrap;
}
/* Cellules numériques (Total/Stock/Rupture/Gam2) : padding réduit, elles
   n'affichent que 1-4 chiffres, le padding standard 14px était excessif. */
.nk-audit-cat table.nk-table td.nk-td--center,
.nk-audit-cat table.nk-table th[data-sort="produits_actifs"],
.nk-audit-cat table.nk-table th[data-sort="produits_en_stock"],
.nk-audit-cat table.nk-table th[data-sort="produits_rupture"],
.nk-audit-cat table.nk-table th[data-sort="produits_gam2"] {
  padding-left: 8px;
  padding-right: 8px;
}

/* Cellule indicateurs combinée (Dispo + Gam2 empilés). Chaque ligne porte
   son libellé en préfixe + sa barre de progression. Largeur fixe pour
   garder l'alignement vertical des barres entre lignes du tableau. */
.nk-cat-rates {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 130px;
}
.nk-cat-rates__row {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
}
.nk-cat-rates__label {
  color: var(--muted);
  font-weight: 600;
  min-width: 38px;
  flex-shrink: 0;
}

/* Cellule défauts : grid à 2 colonnes max pour limiter à 2 chips par ligne.
   Les chips au-delà du 2e wrappent sur la ligne suivante (refonte 2026-05-02).
   On override `white-space:nowrap` (posé sur tous les td en mode audit-cat)
   pour autoriser le wrap intra-cellule. */
.nk-audit-cat table.nk-table td:has(.nk-cat-defects-grid) {
  white-space: normal;
}
.nk-cat-defects-grid {
  display: grid;
  grid-template-columns: repeat(2, max-content);
  gap: 3px 4px;
  justify-content: start;
  align-items: start;
}
.nk-cat-defect-chip {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 10px;
  font-weight: 600;
  white-space: nowrap;
  /* Tronque proprement si un chip très long (URL en erreur (HTTP 404)) tombe
     sur une cellule étroite — sinon le grid déborde. */
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.nk-cat-progress--success .nk-cat-progress__fill { background: var(--success, #16a34a); }
.nk-cat-progress--success .nk-cat-progress__label { color: var(--success, #16a34a); }
.nk-cat-progress--warning .nk-cat-progress__fill { background: var(--warning, #d97706); }
.nk-cat-progress--warning .nk-cat-progress__label { color: var(--warning, #d97706); }
.nk-cat-progress--danger  .nk-cat-progress__fill { background: var(--danger, #dc2626); }
.nk-cat-progress--danger  .nk-cat-progress__label { color: var(--danger, #dc2626); }

/* Pastille « catalogue scanné » sur les nœuds niveau-1 dont le sous-arbre
   est dans la portée d'un scan en cache. Indique à l'utilisateur que le
   clic ouvre le drill-down sans relancer de scan (UX 2026-05-01). */
.nk-cat-sidebar__scanned-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--success, #16a34a);
  margin-left: 6px;
  vertical-align: middle;
  flex-shrink: 0;
}

/* ════════════════════════════════════════════════════════════════
   Shell audit catalogue (NkAuditShell)
   Layout : sidebar gauche + zone main avec topbar + KPI + content.
   Display:grid pour permettre l'insertion de .nk-resize-handle entre
   les 2 colonnes (poignée Hub partagée — NocikaShared.initResizableSidebar).
   Spec : docs/UX.md section « Pages à panneaux (split-pane) ».
   ════════════════════════════════════════════════════════════════ */
.nk-audit-shell {
  display: grid;
  grid-template-columns: auto 1fr;
  /* La row implicite `auto` prendrait la hauteur du contenu intrinsèque,
     ce qui fait sauter le scroll interne du body sidebar (`flex:1`) parce
     qu'il n'a plus de hauteur de référence. minmax(0, 1fr) force la row
     à occuper exactement la hauteur du conteneur, ce qui propage la
     contrainte au sidebar-host puis à la sidebar et son body scrollable. */
  grid-template-rows: minmax(0, 1fr);
  height: 100%;
  min-height: 0;
  background: var(--bg);
}
.nk-audit-shell__main {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; min-height: 0;
}
.nk-audit-shell__topbar {
  height: 50px; flex-shrink: 0;
  display: flex; align-items: center; gap: 8px;
  padding: 0 14px;
  border-bottom: 2px solid var(--border-d);
  background: var(--ecume);
}
.nk-audit-shell__crumb {
  flex: 1; min-width: 0;
  font-size: 13px; color: var(--slate);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nk-audit-shell__crumb b { color: var(--navy); font-weight: 600; }
/* `.nk-audit-shell__tabs`, `__tab`, `__tab--active` supprimées 2026-05-23
   (chantier C, Phase 0a) : onglets Technique/Commercial/Complet retirés
   du SPA depuis 2026-04-28, vue flat unique. */
.nk-audit-shell__refresh {
  margin-left: 8px;
  padding: 4px 10px;
  font-size: 12px;
}

.nk-audit-shell__body {
  flex: 1; min-height: 0;
  overflow-y: auto; overflow-x: hidden;
  padding: 16px;
}
/* `.nk-audit-shell__kpis` reste utilisée — conteneur grid des KPI
   instancié par `audit-shell.js:497` et `audit-cat-shell.js:408` (même si
   ces sites masquent le conteneur via `display:none` aujourd'hui).
   Les sous-éléments `__kpi`, `__kpi-label`, `__kpi-value`, `__kpi-sub`
   ne sont pas appliqués au DOM : supprimés 2026-05-23 (chantier C,
   Phase 0a). À ré-introduire si on rebranche un affichage KPI. */
.nk-audit-shell__kpis {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  margin-bottom: 12px;
}

.nk-audit-shell__alert {
  margin-bottom: 12px;
  padding: 10px 14px;
  border-radius: var(--r-md);
  background: var(--warning-l);
  border: 1px solid var(--warning-b);
  color: var(--warning);
  font-size: 12px;
}

/* Bandeau fraîcheur du scan affiché : visible quand techData vient du
   cache sessionStorage et a > 30 min. Invite à relancer pour avoir frais. */
.nk-audit-shell__freshness {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 12px;
  padding: 8px 14px;
  border-radius: var(--r-md);
  background: var(--warning-l);
  border: 1px solid var(--warning-b);
  color: var(--warning);
  font-size: 12px;
}
.nk-audit-shell__freshness span { flex: 1; }
.nk-audit-shell__freshness button { color: var(--warning); }
/* `.nk-audit-shell__viewtoggle` (et descendants) supprimée 2026-05-23
   (chantier C, Phase 0a) : toggle catégorie/produits retiré du SPA
   audit, vue flat unique. */

.nk-audit-shell__content {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  min-height: 200px;
  padding: 16px;
}
.nk-audit-shell__placeholder {
  text-align: center; color: var(--muted); font-size: 13px;
  padding: 48px 24px;
}
.nk-audit-shell__placeholder svg { display: block; margin: 0 auto 12px; opacity: .35; }

/* `.nk-audit-types-stub` (et descendants) supprimée 2026-05-23
   (chantier C, Phase 0a) : squelette « En construction » des modes
   Commercial/Complet retirés depuis 2026-04-28. */

/* ══════ Tooltip global (NkTooltip) ══════
   Singleton dark, déclenché au hover par data-nk-tt-title. Cf.
   docs/UX.md § « Tooltips / info-bulles » et frontend/shared/nk-tooltip.js.
   La position est gérée en JS (top/left absolute), la flèche pointe
   vers l'ancre via --nk-tt-arrow-x (défini par place()). */
.nk-tooltip {
  position: absolute;
  z-index: var(--z-tooltip);
  /* Police remontée à la base hub (15px) le 2026-06-17 : les infobulles
     étaient trop petites (12/11.5/11). Source de vérité unique ici → toutes
     les infobulles du Hub grossissent d'un coup. Largeur + padding élargis
     en conséquence pour laisser respirer le texte plus grand. */
  max-width: 340px;
  padding: 12px 14px;
  background: #0f172a;
  color: #ffffff;
  border-radius: 6px;
  font-size: 15px;
  line-height: 1.45;
  box-shadow: 0 4px 16px rgba(0, 0, 0, .25);
  pointer-events: auto;
}
.nk-tooltip__title {
  font-weight: 700;
  font-size: 15px;
  color: #ffffff;
  margin-bottom: 4px;
}
.nk-tooltip__desc {
  color: #cbd5e1;
  font-size: 15px;
  margin-bottom: 6px;
  /* Préserve les retours à la ligne d'une desc multi-lignes (ex. checklist de
     complétude de la Synthèse PIM) tout en gardant l'habillage normal. */
  white-space: pre-line;
}
.nk-tooltip__rule {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid rgba(255, 255, 255, .12);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 14px;
  color: #67e8f9; /* cyan-300 */
  word-break: break-word;
}
/* Flèche : triangle CSS pointant vers le bas (tooltip au-dessus de l'ancre).
   La position horizontale est pilotée par --nk-tt-arrow-x (set par JS). */
.nk-tooltip::after {
  content: "";
  position: absolute;
  bottom: -6px;
  left: var(--nk-tt-arrow-x, 50%);
  transform: translateX(-50%);
  width: 0; height: 0;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-top: 6px solid #0f172a;
}
/* Variante « tooltip en dessous de l'ancre » (fallback faute de place
   au-dessus). La flèche se retrouve en haut, pointant vers le haut. */
.nk-tooltip--below::after {
  bottom: auto;
  top: -6px;
  border-top: 0;
  border-bottom: 6px solid #0f172a;
}

/* Icône ⓘ déclencheur d'infobulle (porte les data-nk-tt-*). Repère visuel
   recommandé depuis le 2026-06-17 (règle « pas d'icône » inversée) — d'abord
   sur Page CMS, généralisation à venir. Classe partagée : ne jamais ré-inliner
   ces styles dans un module. */
.nk-tt-info {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  cursor: help;
  flex-shrink: 0;
  transition: color var(--t-fast, .15s);
}
.nk-tt-info:hover,
.nk-tt-info:focus {
  color: var(--cyan, #0ea5b0);
  outline: none;
}

/* Petite icône-lien cyan (par ex. lien fiche produit PS dans NCPACK) :
   hover propre via :hover plutôt que via attributs JS `onmouseover` inline,
   pour rester compatible avec une CSP stricte (`script-src 'self'`). */
.nk-icon-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  color: var(--cyan-d, #007A99);
  flex-shrink: 0;
  text-decoration: none;
  border-radius: 4px;
  background: transparent;
  transition: background 120ms ease-out;
}
.nk-icon-link:hover { background: #e0f7fd; }

/* NCPACK — colonne « Prix live détail » (cellule + décomposition prix).
   Extrait des styles inline de ncpack-editor.js (audit 2026-06-06). */
.nk-pd-live  { padding: 6px 8px; font-size: 11px; text-align: right; white-space: nowrap; }
.nk-pd-promo { color: var(--danger); font-weight: 600; }
.nk-pd-pct   { color: var(--muted); }
.nk-pd-err   { color: var(--danger); }

/* ========================================================================
   UTILITAIRES — classes courtes pour remplacer les inline styles fréquents.
   Ajoutées 2026-05-23 (chantier C). Voir docs/UX.md § Tokens d'espacement
   et typographie pour la liste complète et les anti-patterns.
   ======================================================================== */

/* Layout */
.nk-flex-1 { flex: 1; }
.nk-w-full { width: 100%; }
/* Réservé aux lecteurs d'écran : retiré du flux visuel sans être retiré de
   l'arbre d'accessibilité (`display:none` et `visibility:hidden` le seraient).
   Utilisé par les régions `aria-live` persistantes — cf. NkPagination. */
.nk-sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.nk-col-id { width: 70px; }
.nk-label-title {
  display: block;
  margin-bottom: var(--space-xs);
  font-size: var(--fs-md);
}
.nk-field-row--compact {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-top: var(--space-md);
}
.nk-select--compact {
  max-width: 220px;
  height: 30px;
  padding: var(--space-xs) var(--space-md);
  font-size: var(--fs-sm);
}
.nk-file-input--hidden { display: none; }
.nk-loading-row {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  color: var(--muted);
  font-size: var(--fs-base);
}
/* Audit secu 2026-05-30 [finding 10] : utilitaires couleur pour les
   messages d'alerte inline, extraits depuis pim-article-section-
   caracteristiques.js (chip danger, avertissement court). Réutilisables
   ailleurs. */
.nk-text-danger      { color: var(--danger); }
.nk-text-danger-sm   { font-size: var(--fs-sm); color: var(--danger); }
.nk-text-warn-xs     { font-size: var(--fs-xs); color: var(--danger); }
.nk-text-success     { color: var(--success); }

/* Texte */
.nk-text-muted-sm {
  font-size: var(--fs-sm);
  color: var(--muted);
}
.nk-text-muted-xs {
  font-size: var(--fs-xs);
  color: var(--muted);
}
.nk-text-sm { font-size: var(--fs-sm); }
.nk-text-base { font-size: var(--fs-base); }
.nk-text-center { text-align: center; }

/* ══════════════════════════════════════════════════════════════
   NATURE D'UN NŒUD D'ARBRE — dossier / feuille, original / copie, masqué
   (maquette Lionel, 2026-08-10)

   TROIS dimensions ORTHOGONALES, et c'est tout l'intérêt : chacune a son canal,
   donc elles se lisent ensemble. Une copie masquée reste grise ET pointillée.
     · nature  → la FORME (dossier / feuille), doublée par la couleur ;
     · copie   → le TRAIT (plein 1.5 / pointillé 1.05, tirets 2.2/2) ;
     · masqué  → la COULEUR seule, forme et trait inchangés.
   Une tentative antérieure faisait tout porter par la couleur (orange
   principal / cyan copie / gris masqué) : elle butait sur la copie masquée, qui
   ne pouvait pas être des deux couleurs. Le trait règle ça, et il survit au
   daltonisme comme à une capture en noir et blanc.

   ⚠️ Famille NOUVELLE, et non un ajout à `.nk-tree__icon--folder/--file` :
   celles-ci sont partagées par quatre autres écrans (audit-catalogue, filtres,
   rangement, remplissage) qui n'ont rien demandé.

   Le COMPTE vit dans l'icône. Son décalage vertical est optique et non
   géométrique : la languette d'un dossier attire le regard vers le haut, le
   coin plié d'une feuille vers le bas. */
.nk-nature {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  /* 28 px (maquette Lionel, 2026-08-10). Ce n'est pas un confort : le trait
     pointillé qui distingue une copie n'est lisible qu'à cette taille — à 24
     les tirets de 2,2 px se referment à l'œil et une copie redevient un
     original. La colonne peut se le permettre, un niveau ne dépassant guère
     10 à 15 entrées. */
  width: 28px;
  height: 28px;
  color: var(--ink);
}
.nk-nature svg { width: 100%; height: 100%; }
.nk-nature--file { color: var(--nature-file); }
/* Le trait dit COPIE. Déclaré en CSS et non dans le SVG : un seul glyphe par
   forme, deux styles — deux fichiers SVG auraient divergé au premier ajustement. */
.nk-nature.is-copie svg {
  stroke-width: 1.05;
  stroke-dasharray: 2.2 2;
}
/* Masquée : la couleur écrase la nature, mais la FORME et le TRAIT restent —
   sans quoi on ne saurait plus si l'on regarde une copie ou un original. */
.nk-nature.is-masquee { color: var(--nature-hidden); }
.nk-nature__n {
  position: absolute;
  /* Le glyphe passe à `--fs-sm` avec l'icône à 28 px : c'est le PLANCHER des
     textes lisibles du hub, et un compte se lit — il n'est pas un
     micro-indicateur décoratif. `--fs-xs` (9 px) lui serait réservé. */
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
/* Décalage OPTIQUE, pas géométrique : la languette d'un dossier tire le regard
   vers le haut, le coin plié d'une feuille vers le bas. Suit la taille. */
.nk-nature--folder .nk-nature__n { transform: translateY(3px); }
.nk-nature--file   .nk-nature__n { transform: translateY(4px); }

/* Une feuille dont le compte ouvre la liste filtrée. Elle reste neutre au
   repos — elle est d'abord une icône — et ne se révèle cliquable qu'au survol,
   au moment où le geste devient possible. L'anneau de focus est celui du hub :
   ce contrôle se prend au clavier (`role="button" tabindex="0"`). */
.nk-nature[data-nm-pim] { cursor: pointer; }
.nk-nature[data-nm-pim]:hover { color: var(--orange-h); }
.nk-nature[data-nm-pim]:focus-visible {
  outline: none;
  border-radius: var(--r-sm);
  box-shadow: var(--focus-ring);
}

/* OR — « catégorie principale / spéciale ». Le token `--gold` existait depuis
   longtemps avec exactement cette description (« folder-star, médaille ») mais
   AUCUNE classe ne l'exploitait : l'étoile de place principale se coloriait
   donc en cyan, en style inline, dans le panneau ncmenu — alors que celle de
   l'arbre était déjà dorée. Deux couleurs pour le même sens obligent à
   réapprendre le code d'un écran à l'autre (Lionel, 2026-08-10). */
.nk-text-gold { color: var(--gold); }

/* Méta-données techniques affichées sous forme « LABEL valeur »
   (ex. REF/ID PrestaShop dans la context bar PIM). Le label est en
   uppercase eyebrow muted, la valeur est en monospace lisible
   (alignement vertical garanti par tabular-nums + line-height stable).
   Extrait inline → classe 2026-05-29 (cf. CLAUDE.md § Styles et CSS). */
.nk-meta-eyebrow {
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  color: var(--muted);
  letter-spacing: 0.4px;
}
.nk-meta-value-mono {
  font-family: Menlo, Consolas, monospace;
  font-size: var(--fs-sm);
  color: var(--text);
  font-weight: var(--fw-medium);
}

/* Bouton icône minimal : transparent, sans bordure, taille intrinsèque
   à son SVG. À utiliser pour des actions accessoires inline (copier
   une valeur, retirer une chip, etc.) — pour les boutons icône avec
   bordure utiliser `.nk-btn--icon` (30×30). */
.nk-icon-btn--bare {
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
}
.nk-icon-btn--bare:hover { color: var(--text); }

/* Toggle pill — structure du toggle « pill » (label + track + thumb
   horizontaux dans un bouton arrondi). Pattern utilisé par le toggle
   « Visible / Masqué sur PrestaShop » de la context bar PIM.
   Seules les COULEURS (background, border-color, color label, track
   background) restent inline côté JS car elles dépendent de l'état
   métier (success vs neutre). La transition + le déplacement du thumb
   selon on/off restent inline aussi (translateX dynamique).
   Audit pre-commit 2026-05-30, finding #5. */
.nk-toggle-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 6px 4px 12px;
  border-radius: 999px;
  border: 1px solid var(--border);
  cursor: pointer;
  font-family: inherit;
  flex-shrink: 0;
}
.nk-toggle-pill__label {
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}
.nk-toggle-pill__track {
  position: relative;
  width: 30px;
  height: 16px;
  border-radius: 999px;
  display: inline-block;
  flex-shrink: 0;
  transition: background 0.15s;
}
.nk-toggle-pill__thumb {
  position: absolute;
  top: 2px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--white);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
  transition: left 0.15s;
}

/* Interaction */
.nk-clickable { cursor: pointer; }

/* Reset */
.nk-m-0 { margin: 0; }

/* Sémantique métier */
.nk-text-struck {
  text-decoration: line-through;
  color: var(--muted);
}

/* Poids de police */
.nk-font-semibold { font-weight: var(--fw-semibold); }
/* Remet la graisse NORMALE là où le parent en impose une (ex. « (optionnel) »
   dans un `<label>` en gras). */
.nk-font-normal { font-weight: var(--fw-normal); }
/* `.nk-font-bold` supprimée 2026-08-05 : un libellé qui doit ressortir
   franchement d'un texte courant se balise `<strong>`, qui porte le sens ET la
   graisse sans classe. */

/* Micro-utilitaires complétant les familles ci-dessus (2026-08-05). Ils
   existent pour la raison affichée en tête de section : remplacer des styles
   inline fréquents. Chacun couvrait plus de dix occurrences inline dans le
   seul module Filtres, et aucun n'avait d'équivalent — `.nk-td--nowrap` ne
   s'applique qu'à une cellule de tableau, `.nk-hint` porte l'italique mais
   aussi une taille, une couleur et une marge. */
/* Filet de séparation entre deux blocs empilés. Complète `.nk-panes--ruled` et
   `.nk-pane--ruled`, qui règlent les filets d'un plan de travail en colonnes. */
.nk-ruled-b         { border-bottom: 1px solid var(--border); }
.nk-nowrap          { white-space: nowrap; }
.nk-shrink-0        { flex-shrink: 0; }
.nk-block           { display: block; }
.nk-overflow-hidden { overflow: hidden; }
.nk-scroll-y        { overflow-y: auto; min-height: 0; }
.nk-text-italic     { font-style: italic; }
/* Texte tronqué sur une ligne : le trio nowrap + hidden + ellipsis ne veut
   rien dire séparé, et se reposait à l'identique dans six écrans. */
.nk-truncate        { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Colonne flex qui remplit son parent — pendant vertical de `.nk-grow`. */
.nk-col             { display: flex; flex-direction: column; min-height: 0; }

/* ========================================================================
   UTILITAIRES MÉTRIQUES — Marges / Paddings / Gaps
   Ajoutés 2026-05-23 (chantier D Phase 0b). Basés sur les tokens
   --space-* (2 / 4 / 8 / 12 / 16 / 24 px). Voir docs/UX.md §
   Utilitaires métriques.
   ======================================================================== */

/* Margin top */
.nk-mt-xs  { margin-top: var(--space-xs); }
.nk-mt-sm  { margin-top: var(--space-sm); }
.nk-mt-md  { margin-top: var(--space-md); }
.nk-mt-lg  { margin-top: var(--space-lg); }
.nk-mt-xl  { margin-top: var(--space-xl); }
.nk-mt-2xl { margin-top: var(--space-2xl); }

/* Margin bottom */
.nk-mb-xs  { margin-bottom: var(--space-xs); }
.nk-mb-sm  { margin-bottom: var(--space-sm); }
.nk-mb-md  { margin-bottom: var(--space-md); }
.nk-mb-lg  { margin-bottom: var(--space-lg); }
.nk-mb-xl  { margin-bottom: var(--space-xl); }
.nk-mb-2xl { margin-bottom: var(--space-2xl); }

/* Margin left */
/* `auto` : pousse l'élément (et ceux qui le suivent) à droite dans une rangée
   flex. Complète la famille, qui n'avait que les crans de la grille. */
.nk-ml-auto { margin-left: auto; }
.nk-ml-xs  { margin-left: var(--space-xs); }
.nk-ml-sm  { margin-left: var(--space-sm); }
.nk-ml-md  { margin-left: var(--space-md); }
.nk-ml-lg  { margin-left: var(--space-lg); }
.nk-ml-xl  { margin-left: var(--space-xl); }
.nk-ml-2xl { margin-left: var(--space-2xl); }

/* Margin right */
.nk-mr-xs  { margin-right: var(--space-xs); }
.nk-mr-sm  { margin-right: var(--space-sm); }
.nk-mr-md  { margin-right: var(--space-md); }
.nk-mr-lg  { margin-right: var(--space-lg); }
.nk-mr-xl  { margin-right: var(--space-xl); }
.nk-mr-2xl { margin-right: var(--space-2xl); }

/* Margin sur tous les côtés (.nk-m-0 est défini plus haut). */
.nk-m-xs  { margin: var(--space-xs); }
.nk-m-sm  { margin: var(--space-sm); }
.nk-m-md  { margin: var(--space-md); }
.nk-m-lg  { margin: var(--space-lg); }
.nk-m-xl  { margin: var(--space-xl); }
.nk-m-2xl { margin: var(--space-2xl); }

/* Padding top */
.nk-pt-xs  { padding-top: var(--space-xs); }
.nk-pt-sm  { padding-top: var(--space-sm); }
.nk-pt-md  { padding-top: var(--space-md); }
.nk-pt-lg  { padding-top: var(--space-lg); }
.nk-pt-xl  { padding-top: var(--space-xl); }
.nk-pt-2xl { padding-top: var(--space-2xl); }

/* Padding bottom */
.nk-pb-xs  { padding-bottom: var(--space-xs); }
.nk-pb-sm  { padding-bottom: var(--space-sm); }
.nk-pb-md  { padding-bottom: var(--space-md); }
.nk-pb-lg  { padding-bottom: var(--space-lg); }
.nk-pb-xl  { padding-bottom: var(--space-xl); }
.nk-pb-2xl { padding-bottom: var(--space-2xl); }

/* Padding left */
.nk-pl-xs  { padding-left: var(--space-xs); }
.nk-pl-sm  { padding-left: var(--space-sm); }
.nk-pl-md  { padding-left: var(--space-md); }
.nk-pl-lg  { padding-left: var(--space-lg); }
.nk-pl-xl  { padding-left: var(--space-xl); }
.nk-pl-2xl { padding-left: var(--space-2xl); }

/* Padding right */
.nk-pr-xs  { padding-right: var(--space-xs); }
.nk-pr-sm  { padding-right: var(--space-sm); }
.nk-pr-md  { padding-right: var(--space-md); }
.nk-pr-lg  { padding-right: var(--space-lg); }
.nk-pr-xl  { padding-right: var(--space-xl); }
.nk-pr-2xl { padding-right: var(--space-2xl); }

/* Padding sur tous les côtés */
.nk-p-xs  { padding: var(--space-xs); }
.nk-p-sm  { padding: var(--space-sm); }
.nk-p-md  { padding: var(--space-md); }
.nk-p-lg  { padding: var(--space-lg); }
.nk-p-xl  { padding: var(--space-xl); }
.nk-p-2xl { padding: var(--space-2xl); }

/* Gap (flex / grid) */
.nk-gap-xs  { gap: var(--space-xs); }
.nk-gap-sm  { gap: var(--space-sm); }
.nk-gap-md  { gap: var(--space-md); }
.nk-gap-lg  { gap: var(--space-lg); }
.nk-gap-xl  { gap: var(--space-xl); }
.nk-gap-2xl { gap: var(--space-2xl); }

/* ========================================================================
   UTILITAIRES SÉMANTIQUES — patterns transverses extraits 2026-05-24
   (chantier D Phase 1, fichier source : ncpack-editor.js).
   Toutes les classes ci-dessous sont génériques et réutilisables dans
   les autres modules du Hub. Voir docs/UX.md § Utilitaires sémantiques.
   ======================================================================== */

/* Alignement icône SVG dans une row flex (corrige décalage baseline 1px). */
.nk-icon-aligned {
  flex-shrink: 0;
  margin-top: 1px;
}

/* Pastille indicateur héritant la couleur du parent (`currentColor`). */
.nk-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}

/* Cellule numérique alignée à droite (médium, tabular). */
.nk-num-right {
  text-align: right;
  font-weight: var(--fw-medium);
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

/* Variante muted : sous-totaux discrets. */
.nk-num-right-muted {
  text-align: right;
  color: var(--slate);
  font-variant-numeric: tabular-nums;
}

/* Texte en évidence rouge gras (prix promo, erreur valorisée). */
.nk-text-error-bold {
  color: var(--danger);
  font-weight: var(--fw-bold);
  font-size: var(--fs-md);
}

/* Nombre mis en avant d'un relevé (« 223 catalogues sans filtre »). Le chiffre
   PORTE l'information, sa légende le commente : d'où un corps très au-dessus du
   texte courant et un interlignage serré. Ce n'est pas `.nk-kpi__value`, qui
   vit dans une carte de tableau de bord et reste à la taille d'un libellé.
   Extrait 2026-08-05 de deux écrans qui le reposaient en inline.

   AUCUNE `color` ici, volontairement : le chiffre hérite de celle de son bloc,
   et un relevé lui accole souvent une couleur d'état (`.nk-text-danger`,
   `.nk-text-success`…). La poser ici la ferait gagner — ces utilitaires sont
   déclarés PLUS HAUT dans le fichier, à spécificité égale. */
.nk-stat-number {
  font-size: 30px;
  font-weight: var(--fw-semibold);
  line-height: 1;
}

/* État vide centré (écran de chargement, ressource introuvable…). */
.nk-empty-state {
  padding: var(--space-2xl);
  text-align: center;
  color: var(--muted);
  font-size: var(--fs-base);
}

/* Bouton icône carré 28×28 (Enregistrer, chevron, etc.). */
.nk-icon-btn-28 {
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  background: var(--white);
  border-radius: var(--r-sm);
  color: var(--muted);
  cursor: pointer;
  flex-shrink: 0;
  transition: background var(--t-fast), color var(--t-fast), border-color var(--t-fast);
}

/* Bandeau warning soft (orange). */
.nk-banner-warning {
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
  padding: var(--space-md) var(--space-lg);
  background: #fff7ed;
  border: 1px solid #fed7aa;
  border-radius: var(--r-sm);
  color: #9a3412;
  font-size: var(--fs-sm);
  line-height: 1.35;
}

/* Bandeau erreur (rouge). */
.nk-banner-danger {
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
  padding: var(--space-md) var(--space-lg);
  background: #fef2f2;
  border: 1px solid #fecaca;
  border-radius: var(--r-sm);
  color: #991b1b;
  font-size: var(--fs-sm);
  line-height: 1.4;
}

/* Badge environnement DEV (pastille jaune). */
.nk-tag-env-dev {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-xs) var(--space-md);
  border-radius: var(--r-pill);
  background: #fff5cc;
  color: #8b6914;
  border: 1px solid #f5c518;
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: .4px;
  text-transform: uppercase;
}

/* Lecture seule visuelle (opacité 40 %, interaction bloquée). */
.nk-disabled-soft {
  opacity: 0.4;
  pointer-events: none;
}

/* Désactivé interactif (opacité 55 %, curseur not-allowed). */
.nk-disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

/* ========================================================================
   UTILITAIRES SÉMANTIQUES — patterns transverses extraits 2026-05-24
   (chantier D Phase 2, fichier source : frontend/pim/pim-article.js).
   Patterns IA-related récurrents (≥ 3 occurrences) et titres de section.
   ======================================================================== */

/* Titre de section PIM (uppercase navy 13px, prend tout l'espace flex). */
.nk-pim-section-title {
  font-size: var(--fs-base);
  font-weight: var(--fw-bold);
  color: var(--navy);
  text-transform: uppercase;
  letter-spacing: .04em;
  margin: 0;
  flex: 1;
}

/* Label IA violet uppercase (« Conclusion », « Suggestions IA »…). */
.nk-label-ia {
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  color: var(--ia, #7c3aed);
  text-transform: uppercase;
  letter-spacing: .05em;
  margin-bottom: var(--space-md);
}

/* Eyebrow label (sur-titre uppercase muted, sert d'étiquette de bloc). */
.nk-eyebrow-label {
  display: block;
  font-size: var(--fs-xs);
  color: var(--muted);
  text-transform: uppercase;
  font-weight: var(--fw-semibold);
  margin-bottom: 3px;
  letter-spacing: .3px;
}

/* Callout IA : encart d'information IA (border-left violet sur surface). */
.nk-callout-ia {
  background: var(--surface);
  border-left: 3px solid var(--ia, #7c3aed);
  padding: var(--space-lg) var(--space-xl);
  margin-top: var(--space-lg);
  font-size: var(--fs-base);
  line-height: 1.5;
  color: var(--text);
}

/* Carte PIM (fond blanc, bordure, radius standard). */
.nk-pim-card {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 8px;
  margin-bottom: var(--space-xl);
  overflow: hidden;
}

/* Label de cellule PIM (flex:1 + 13px semibold). */
.nk-pim-cell-label {
  flex: 1;
  font-size: var(--fs-base);
  font-weight: var(--fw-semibold);
}

/* Couleurs texte utilitaires (transverses). */
.nk-text-cyan { color: var(--cyan); }
.nk-text-warning { color: var(--warning); }

/* Row inline-flex compacte (≠ `.nk-inline-row` qui est `display: flex`).
   Conserve l'élément en flux inline tout en alignant ses enfants en flex. */
.nk-inline-row--inline {
  display: inline-flex;
  align-items: center;
  gap: var(--space-md);
}

/* Pièces jointes image — zone de dépôt + vignettes réutilisables. */
.nk-attachment-dropzone {
  padding: var(--space-md);
  border: 1px dashed var(--border-d);
  border-radius: var(--r-md);
  background: var(--surface);
  text-align: center;
  cursor: pointer;
}
.nk-attachment-dropzone:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}
.nk-attachment-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-top: var(--space-sm);
}
.nk-attachment-grid--flush { margin-top: 0; }
.nk-attachment-tile {
  position: relative;
  display: block;
  width: 84px;
  height: 84px;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--white);
}
.nk-attachment-tile--lg {
  width: 96px;
  height: 96px;
}
.nk-attachment-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.nk-attachment-remove {
  position: absolute;
  top: var(--space-xs);
  right: var(--space-xs);
  width: 18px;
  height: 18px;
  padding: 0;
  border: 0;
  border-radius: var(--r-pill);
  background: rgba(15, 23, 42, .78);
  color: var(--white);
  font-size: var(--fs-sm);
  line-height: 18px;
  text-align: center;
  cursor: pointer;
}

/* Row flex avec wrap et alignement bas (toolbar de filtres PIM). */
.nk-pim-toolbar-row {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: var(--space-lg);
}

/* Cellule en-tête de tableau dense (PIM : pricing, blocs SEO). */
.nk-pim-th-dense {
  padding: var(--space-md) var(--space-md);
  font-size: var(--fs-sm);
  color: var(--muted);
  font-weight: var(--fw-semibold);
}

/* Eyebrow compact uppercase pour cellule étroite (truncate-friendly). */
.nk-pim-mini-eyebrow {
  min-width: 0;
  font-size: var(--fs-sm);
  text-transform: uppercase;
  letter-spacing: .4px;
}

/* Row d'accordion / section cliquable (header de panel PIM). */
.nk-pim-section-row {
  display: flex;
  align-items: center;
  padding: var(--space-xl) 28px;
  cursor: pointer;
  user-select: none;
  border-bottom: 1px solid var(--bg);
}

/* Badge IA inline (violet + texte blanc). */
.nk-badge-ia-row {
  display: inline-flex;
  align-items: center;
  gap: var(--space-md);
  background: var(--ia, #7c3aed);
  color: #fff;
}

/* Audit pre-commit 2026-05-30 [F5] : classes pour le layout 3 lignes
   de la ligne « A modifier » en mode demande active (fiche PIM card
   Suivi, section-synthese.js). Remplacent les styles inline residuels. */
.nk-pim-cr-actions {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
}
.nk-pim-cr-info {
  flex-basis: 100%;
  padding-left: 110px;
}

/* Audit secu 2026-05-30 [finding 10] : classes specifiques au tableau
   PIM des caracteristiques (pim-article-section-caracteristiques.js)
   pour remplacer les styles inline lourds. */

/* Ligne en doublon : fond rouge clair (signale a l'utilisateur que cette
   ligne porte la meme (feature, valeur) qu'une autre). Le toggle est
   gere via classList.toggle dans refreshFeaturesDuplicateMarking. */
.nk-pim-feat-row--dup { background: var(--danger-l); }

/* Cellule « sous-ligne » : ligne secondaire de feature (valeur N+1
   pour la meme caracteristique, ex : Truck C5/C7/CX). Padding-left
   marque la dependance visuelle a la ligne primaire. */
.nk-pim-feat-td--sub {
  padding-left: var(--space-2xl);
  color: var(--slate);
  font-size: var(--fs-base);
}

/* Chevron SVG inline dans la cellule sous-ligne. */
.nk-pim-feat-sub-chevron {
  vertical-align: -1px;
  margin-right: var(--space-sm);
  opacity: .6;
}

/* Input dont la valeur cree un doublon : bordure rouge (mis en
   place par toggle de classe via refreshFeaturesDuplicateMarking). */
.nk-pim-feat-input--dup { border-color: var(--danger); }

/* ========================================================================
   NkToast — notifications transitoires haut-droite (chantier D, sprint
   NkToast 2026-05-24). Voir frontend/shared/nk-toast.js et docs/UX.md.
   ======================================================================== */

/* Conteneur stack (un seul élément par page, attaché au body au premier
   appel `NkToast.show()`). `pointer-events: none` pour ne pas bloquer
   l'interaction avec le contenu situé derrière. */
.nk-toast-stack {
  position: fixed;
  top: var(--space-2xl);
  right: var(--space-2xl);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  /* Élargi de 360 à 440 px avec le passage du corps à 15 px (Lionel,
     2026-08-10) : à largeur constante, grossir le texte multipliait les lignes
     et rendait le pavé plus dense, pas plus lisible. */
  max-width: 440px;
  pointer-events: none;
}

/* Toast individuel. Animation d'entrée par fade + slide depuis la droite. */
.nk-toast {
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
  padding: var(--space-md) var(--space-lg);
  background: var(--white);
  border: 1px solid var(--border);
  border-left: 4px solid var(--slate);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-md);
  /* CORPS 15 (Lionel, 2026-08-10) — valeur PROPRE au composant, aucun token ne
     la porte (`--fs-md` vaut 14, `--fs-lg` 18).
     Le toast était en `--fs-sm` (11 px), c'est-à-dire au PLANCHER du hub, celui
     que docs/UX.md réserve aux métas, compteurs et badges. Or un toast n'est pas
     une méta : c'est un message d'action, lu une seule fois, en trois secondes,
     souvent à l'autre bout de l'écran de celui qui vient de cliquer. Il se lit
     ou il ne sert à rien — et il disparaît avant qu'on ait pu se pencher. */
  font-size: 15px;
  line-height: 1.45;
  color: var(--text);
  cursor: pointer;
  pointer-events: auto;
  opacity: 0;
  transform: translateX(8px);
  transition: opacity .18s ease, transform .18s ease;
}

.nk-toast.is-visible { opacity: 1; transform: translateX(0); }
.nk-toast.is-leaving { opacity: 0; transform: translateX(8px); }

.nk-toast__icon {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  line-height: 1;
}

.nk-toast__message {
  flex: 1;
  min-width: 0;
}

/* Croix de fermeture (Lionel, 2026-08-10). Le clic sur le toast fermait déjà,
   mais rien ne le disait — une affordance invisible n'existe pas. Elle devient
   nécessaire depuis que la durée suit la longueur du message : un toast qui
   tient dix secondes doit pouvoir être écarté.
   `align-self: flex-start` et non `center` : sur un message de plusieurs
   lignes, une croix centrée verticalement flotte au milieu du paragraphe au
   lieu de rester à son coin. */
.nk-toast__close {
  flex-shrink: 0;
  align-self: flex-start;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--muted);
  font-family: var(--font);
  font-size: var(--fs-lg);
  line-height: 1;
  cursor: pointer;
}
.nk-toast__close:hover { color: var(--ink); }
.nk-toast__close:focus-visible {
  outline: none;
  border-radius: var(--r-sm);
  box-shadow: var(--focus-ring);
}

/* Variantes : couleur du liseré gauche + couleur de l'icône. */
.nk-toast--success                  { border-left-color: var(--success); }
.nk-toast--success .nk-toast__icon  { color: var(--success); }

.nk-toast--error                    { border-left-color: var(--danger); }
.nk-toast--error .nk-toast__icon    { color: var(--danger); }

.nk-toast--info                     { border-left-color: var(--info); }
.nk-toast--info .nk-toast__icon     { color: var(--info); }

.nk-toast--warning                  { border-left-color: var(--warning); }
.nk-toast--warning .nk-toast__icon  { color: var(--warning); }

/* ============================================================
   MENU CONTEXTUEL GAUCHE (.nk-side-nav)
   ------------------------------------------------------------
   Convention canonique pour tout menu vertical à gauche d'une
   page (sidebar de module : PIM, Admin > Prompts, …).

   Structure attendue :
     <aside class="nk-side-nav">
       <div class="nk-side-nav__group nk-side-nav__group--open">
         <button class="nk-side-nav__group-head"
                 data-group="key" aria-expanded="true">
           <span class="nk-flex-1">Libellé groupe</span>
           <svg class="nk-side-nav__group-chevron">…</svg>
         </button>
         <div class="nk-side-nav__group-children">
           <a class="nk-side-nav__item nk-side-nav__item--active"
              data-key="…">
             <svg class="nk-side-nav__item-chevron">›</svg>
             Libellé item
           </a>
           …
         </div>
       </div>
       …
     </aside>

   Comportement JS attendu (à reproduire dans chaque module) :
   - Accordéon : un seul groupe ouvert à la fois.
   - Click sur groupe ouvert → on le ferme.
   - Click sur groupe fermé → on l'ouvre et on ferme les autres.
   - State : `_collapsedGroups = { key: bool }`.
   - `--active` reste posé sur le groupe contenant l'item actif
     même si l'accordéon a fermé le groupe → fond sable persistant
     comme repère « groupe en cours ».
   ============================================================ */

/* Container : fond sable, bordure droite marquée, scroll vertical
   indépendant. Largeur fixe `240px` (uniforme sur tous les modules) +
   `flex-shrink: 0` pour ne pas se faire écraser par le panneau de
   droite. `min-height: 0` + `height: 100%` indispensables pour que
   `overflow-y: auto` se déclenche quand le parent est en grid (sinon
   le contenu pousse la cellule au-delà du viewport et la partie
   basse est coupée). */
.nk-side-nav {
  flex-shrink: 0;
  width: 240px;
  background: var(--ecume-bg);
  border-right: 1px solid var(--border-strong);
  height: 100%;
  min-height: 0;
  overflow-y: auto;
  padding-bottom: var(--space-2xl);
}

/* Groupe (niveau 1) — wrapper relatif (le chevron de l'item actif
   se positionne absolu à l'intérieur du groupe). */
.nk-side-nav__group { position: relative; }

/* Header de groupe : uppercase navy gras, padding 11/14, separator
   bottom marqué, hover sable clair. Reset de tous les défauts de
   <button> (background transparent, border 0, text-align left). */
.nk-side-nav__group-head {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding: 11px 14px;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--border-strong);
  font-size: var(--fs-md); /* 11px → 14px (2026-06-09, alignement base 15px) */
  font-weight: var(--fw-bold);
  color: var(--navy);
  letter-spacing: .02em;
  text-transform: uppercase;
  text-align: left;
  cursor: pointer;
  transition: background .12s ease;
}
.nk-side-nav__group-head:hover { background: var(--ecume); }

/* En-tête d'ACCÈS DIRECT (groupe `href` sans sous-menu, ex. PIM Synthèse) :
   rendu en <a> qui navigue. On retire le soulignement de lien ; le reste
   (uppercase navy, hover, fond `--active`) est hérité de `.nk-side-nav__group-head`
   et du modificateur `--active` sur le groupe. Ajouté 2026-07-07. */
.nk-side-nav__group-head--direct { text-decoration: none; }

/* Header sable foncé persistant dans 2 cas (pattern BO PrestaShop) :
   - `--open` : groupe ouvert (sous-liste visible) → indique le groupe
     que l'utilisateur consulte actuellement.
   - `--active` : groupe contenant l'item actif → indique le groupe
     « en cours d'utilisation » même si l'accordéon l'a fermé.
   Spécificité (0,2,0) > :hover (0,1,1) donc le fond reste appliqué
   au survol (pas de flash visuel). */
.nk-side-nav__group--open .nk-side-nav__group-head,
.nk-side-nav__group--active .nk-side-nav__group-head { background: var(--surface-open); }

/* Chevron du header (▼) — pivote à -90° quand le groupe est fermé,
   à 0° quand il est ouvert. Positionné via flex (le label utilise
   `.nk-flex-1`, le chevron occupe l'espace restant). */
.nk-side-nav__group-chevron {
  display: inline-flex;
  width: 11px;
  height: 11px;
  flex-shrink: 0;
  transition: transform .15s ease;
  transform: rotate(-90deg);
}
.nk-side-nav__group--open .nk-side-nav__group-chevron { transform: rotate(0deg); }

/* Container niveau 2 : sable plus clair que le container, separator
   bottom marqué (entre le sous-menu d'un groupe et le groupe suivant). */
.nk-side-nav__group-children {
  background: var(--white);
  padding: 4px 0;
  border-bottom: 1px solid var(--border-strong);
}

/* Dernier groupe : pas de séparateur en bas (ni sur le header
   quand sub absent, ni sur le sub quand présent) pour ne pas
   doubler avec un éventuel séparateur global. */
.nk-side-nav__group:last-child > .nk-side-nav__group-head,
.nk-side-nav__group:last-child > .nk-side-nav__group-children { border-bottom: 0; }

/* Item niveau 2 (lien section). Padding-left 28px réservé pour
   le chevron actif (qui occupe l'espace sans décaler le label).
   Flex pour permettre un libellé qui prend l'espace + un badge
   trailing optionnel (compteur, info) aligné à droite. Hover :
   pas de fond, juste gras + souligné (cohérent avec l'état actif
   mais sans le chevron). */
.nk-side-nav__item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px 8px 28px;
  font-size: 15px; /* 13px → 15px (2026-06-09, base par défaut du hub) */
  color: var(--muted);
  text-decoration: none;
  cursor: pointer;
  transition: color .12s ease;
}
.nk-side-nav__item:hover {
  font-weight: var(--fw-semibold);
  text-decoration: underline;
  text-underline-offset: 3px;
  color: var(--navy);
}

/* État actif : pas de fond, navy gras, underline + chevron `›` à
   gauche (SVG injecté dans le markup côté JS quand l'item est
   actif, positionné absolu via `.nk-side-nav__item-chevron`). */
.nk-side-nav__item--active {
  color: var(--navy);
  font-weight: var(--fw-semibold);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.nk-side-nav__item-chevron {
  position: absolute;
  left: 16px;
  top: 50%;
  transform: translateY(-50%);
  width: 12px;
  height: 12px;
  color: var(--navy);
  pointer-events: none;
}

/* État désactivé (Lot C.2 — 2026-05-29) : item grisé non navigable,
   utilisé par PIM pour griser Composants/Déclinaisons selon `is_pack`.
   Le `pointer-events` reste à `auto` (≠ `none`) pour conserver le tooltip
   natif via `title=""` au survol — c'est le handler JS qui bloque la
   navigation (cf. nk-page-shell.js : `if (aria-disabled) return`). */
.nk-side-nav__item--disabled {
  cursor: not-allowed;
  opacity: 0.4;
  color: var(--muted);
}
.nk-side-nav__item--disabled:hover {
  background: transparent;
  color: var(--muted);
}

/* Item d'action IA (2026-07-19) — ex. PIM « Génération auto ». Le code couleur
   IA du hub est le violet (--ia). Rendu en pastille pleine largeur « inset »
   (marge + radius) avec une étoile IA à gauche, pour se démarquer des items de
   navigation classiques : cet item ne navigue pas vers une simple consultation,
   il lance un traitement IA. Réutilisable : passer
   `className:'nk-side-nav__item--ia'` sur l'item dans la config NkPageShell. */
.nk-side-nav__item--ia {
  margin: 6px 8px;
  padding: 8px 12px 8px 34px;
  border-radius: var(--r-md);
  background: var(--ia-l);
  color: var(--ia);
  font-weight: var(--fw-semibold);
}
/* Étoile IA à gauche (★ 4 branches, U+2726) — remplace le chevron de nav. */
.nk-side-nav__item--ia::before {
  content: "\2726";
  position: absolute;
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 12px;
  color: var(--ia);
  pointer-events: none;
}
.nk-side-nav__item--ia:hover {
  background: var(--ia-b);
  color: var(--ia-d);
  text-decoration: none;
}
/* Actif : pastille violette pleine, texte blanc (pas d'underline de nav). */
.nk-side-nav__item--ia.nk-side-nav__item--active {
  background: var(--ia);
  color: var(--white);
  text-decoration: none;
}
.nk-side-nav__item--ia.nk-side-nav__item--active::before { color: var(--white); }
/* Le chevron `›` de l'item actif n'a pas de sens sur la pastille IA. */
.nk-side-nav__item--ia .nk-side-nav__item-chevron { display: none; }

/* Groupes épinglés en bas de sidebar (2026-07-19) — cf. NkPageShell `pinBottom`.
   Le container passe en flex colonne et le spacer flexible pousse les groupes
   épinglés (ex. PIM : Audits + Info technique) vers le bas quand le contenu ne
   remplit pas la hauteur ; en cas de débordement, le spacer se réduit (basis 0)
   et les groupes épinglés suivent naturellement le scroll. */
.nk-side-nav--has-pinned { display: flex; flex-direction: column; }
/* Les groupes gardent leur hauteur intrinsèque (pas de compression flex quand la
   sidebar déborde) ; seul le spacer absorbe/rend l'espace. */
.nk-side-nav--has-pinned > .nk-side-nav__group { flex-shrink: 0; }
.nk-side-nav__spacer { flex: 1 1 auto; min-height: var(--space-md); }

/* PIM — marge de respiration en bas du contenu scrollable. Sans elle, le
   dernier bouton d'un bloc éditorial déplié reste collé/coupé contre le bord
   bas du `<main id="nk-page-shell-content">` (overflow-y:auto sans padding).
   Scopé au PIM (`#nk-pim-root`) pour ne pas décaler les autres pages shell. */
#nk-pim-root #nk-page-shell-content { padding-bottom: 56px; }

/* Drawer d'édition ncmenu — typographie plus lisible (labels + champs + aides). */
.nm-edit-panel .nk-label { font-size: 13.5px; }
.nm-edit-panel .nk-input { font-size: 14.5px; }
.nm-edit-panel .nk-input::placeholder { font-size: 13px; }
.nm-edit-panel .nk-text-muted-xs { font-size: 12px; }
.nm-edit-panel .nk-text-muted-sm { font-size: 13px; }

/* ══════════════════════════════════════════════════════════════
   BIBLIOTHÈQUE MÉDIA — grille + tuile (admin « Bibliothèque » et,
   à terme, le picker). Évite de réinventer une tuile en styles inline
   (cf. hub-admin-bibliotheque.js). Modèle : .nk-attachment-*.
   ══════════════════════════════════════════════════════════════ */
.nk-media-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: var(--space-md);
}
.nk-media-tile {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--white);
}
.nk-media-tile__thumb {
  height: 110px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-sm);
  background: var(--surface-2, #f4f6f8);
}
.nk-media-tile__thumb img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}
.nk-media-tile__meta { padding: var(--space-sm); }
.nk-media-tile__name {
  font-size: var(--fs-sm);
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.nk-media-tile__foot {
  display: flex;
  justify-content: flex-end;
  padding: 0 var(--space-sm) var(--space-sm);
}
/* Vignette CHOISIE, quand la grille sert de sélecteur (fenêtre Image d'une
   famille de guides, 2026-08-17). L'anneau double la bordure au lieu de
   l'épaissir : une bordure qui grossit décale la vignette d'un pixel et fait
   sauter toute la grille au moindre survol de sélection. */
.nk-media-tile--choisie { border-color: var(--cyan); box-shadow: 0 0 0 2px var(--cyan-l); }
/* La grille devient cliquable : le curseur doit le dire. */
.nk-media-grid--selectionnable .nk-media-tile { cursor: pointer; }
.nk-media-grid--selectionnable .nk-media-tile:hover { border-color: var(--cyan-d); }

/* Densité des vignettes (3 niveaux, sélecteur d'affichage de l'admin) :
   .nk-media-grid = Grande (défaut, ci-dessus), --md = Moyenne, --sm = Petite.
   Colonnes et hauteur de vignette décroissantes → plus d'icônes visibles. */
.nk-media-grid--md { grid-template-columns: repeat(auto-fill, minmax(115px, 1fr)); gap: var(--space-sm); }
.nk-media-grid--md .nk-media-tile__thumb { height: 84px; }
.nk-media-grid--sm { grid-template-columns: repeat(auto-fill, minmax(90px, 1fr)); gap: var(--space-sm); }
.nk-media-grid--sm .nk-media-tile__thumb { height: 60px; }
.nk-media-grid--sm .nk-media-tile__meta { padding: var(--space-xs); }
.nk-media-grid--sm .nk-media-tile__foot { padding: 0 var(--space-xs) var(--space-xs); }

/* Repère « SVG » sur une tuile média : petit point violet en haut-droite. */
/* Fond damier — visualise la transparence (aperçu/inspection des SVG & PNG). */
.nk-checker {
  background-color: #fff;
  background-image:
    linear-gradient(45deg, #e2e8f0 25%, transparent 25%),
    linear-gradient(-45deg, #e2e8f0 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #e2e8f0 75%),
    linear-gradient(-45deg, transparent 75%, #e2e8f0 75%);
  background-size: 20px 20px;
  background-position: 0 0, 0 10px, 10px -10px, -10px 0;
}

/* Bibliothèque média — grille enrichie (sélection, badge ALT, chips, barre). */
.nk-media-tile.is-selected { border-color: var(--cyan, #00B4D8); box-shadow: 0 0 0 2px var(--cyan, #00B4D8); }
/* Média archivé (library=0) : image grisée/atténuée pour le distinguer des actifs. */
.nk-media-tile.is-archived .nk-media-tile__thumb img { filter: grayscale(1); opacity: .5; }
/* Ligne ALT (badge + texte tronqué) et rangée de chips de tags sous la vignette. */
.nk-media-tile__altrow { display: flex; align-items: center; gap: var(--space-sm); margin: var(--space-sm) 0; min-width: 0; }
.nk-media-tile__alttext { font-size: var(--fs-sm); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.nk-media-tile__tags { display: flex; flex-wrap: wrap; gap: var(--space-sm); }
.nk-media-tile__chk { position: absolute; top: 6px; left: 6px; width: 18px; height: 18px; cursor: pointer; z-index: 2; }
.nk-media-badge { display: inline-flex; align-items: center; font-size: 9px; font-weight: 700; letter-spacing: .03em; padding: 1px 5px; border-radius: 4px; flex: 0 0 auto; }
.nk-media-badge--ok { background: #dcfce7; color: #166534; }
.nk-media-badge--warn { background: #f1f5f9; color: #64748b; }
.nk-media-chip { display: inline-flex; align-items: center; font-size: 11px; padding: 1px 7px; border-radius: 999px; background: #eef2ff; border: 1px solid #c7d2fe; color: #3730a3; }
/* Barre d'actions groupées de la sélection multiple. Échelle de police UNIQUE
   (--fs-base = taille des boutons --sm) pour compteur + actions. Layout :
   [compteur] │ [actions…]                              [✕]. */
.nk-media-selbar { align-items: center; gap: var(--space-md); flex-wrap: wrap; padding: var(--space-sm) var(--space-md); margin-bottom: var(--space-md); background: #ecfeff; border: 1px solid #a5f3fc; border-radius: var(--r-md); font-size: var(--fs-base); }
.nk-media-selbar__count { font-size: var(--fs-base); font-weight: 600; color: var(--slate); }
.nk-media-selbar__actions { display: inline-flex; align-items: center; gap: var(--space-sm); flex-wrap: wrap; }
/* Croix de fermeture (décoche tout + referme) : lecture « fermeture », pas action. */
.nk-media-selbar__close { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border: 0; background: transparent; color: var(--slate); font-size: var(--fs-md); line-height: 1; cursor: pointer; border-radius: var(--r-sm); }
.nk-media-selbar__close:hover { background: var(--white); color: var(--ink); }

/* ══════════════════════════════════════════════════════════════
   BIBLIOTHÈQUE MÉDIA — barre de recherche / filtres
   Une seule ligne alignée à GAUCHE : recherche COMPACTE puis TOUS les filtres
   à la suite (plus de vide au milieu, plus de margin-left:auto sur `__right`) :
   dropdown Type · tri · case « Inclure les archivés » · toggle Format · densité
   de grille. Espaces réguliers (--space-lg) entre les contrôles, avec un écart
   un peu plus marqué avant la densité (marge dédiée sur `__dens`). Toute la
   barre partage une ÉCHELLE TYPOGRAPHIQUE UNIQUE (--fs-base), scopée ici pour
   ne pas toucher les classes globales partagées (.nk-input, .nk-segment,
   .nk-segment__btn). Les toggles réutilisent le segment générique `.nk-segment`.
   ══════════════════════════════════════════════════════════════ */
.nk-media-filterbar {
  display: flex;
  align-items: center;
  gap: var(--space-lg);
  flex-wrap: wrap;
  margin: 0 0 var(--space-md);
}
/* Groupe de filtres : `display:contents` → ses enfants deviennent des items
   DIRECTS de .nk-media-filterbar et s'enroulent un par un, à la suite de la
   recherche (au lieu de basculer en bloc sous elle quand la place manque). */
.nk-media-filterbar__right { display: contents; }
/* Écart un peu plus marqué avant la densité de grille (dernier groupe segmenté). */
.nk-media-filterbar__dens { margin-left: var(--space-md); }
/* Échelle typographique UNIQUE de la barre (SCOPED) : recherche, selects, libellé
   Format, case archivés et boutons de segment tous sur --fs-base — aucune
   modification des classes globales partagées (.nk-input, .nk-segment__btn). */
.nk-media-filterbar,
.nk-media-filterbar .nk-input,
.nk-media-filterbar .nk-segment__btn,
.nk-media-filterbar__lbl,
.nk-media-filterbar__check { font-size: var(--fs-base); }
/* Segments de la barre biblio plus hauts (plus aérés) — modificateur SCOPED à
   la barre, pour ne pas alourdir le `.nk-segment` générique partagé par le Hub. */
.nk-media-filterbar .nk-segment__btn { padding-top: 8px; padding-bottom: 8px; }
/* Zone de recherche COMPACTE, ancrée à gauche : ne mange plus toute la ligne, les
   filtres la suivent immédiatement (flex-grow:0). La loupe est posée en absolu à
   gauche, l'input reçoit un padding gauche pour ne pas la chevaucher. */
.nk-media-search { position: relative; flex: 0 1 320px; min-width: 240px; display: flex; align-items: center; }
.nk-media-search__icon { position: absolute; left: 10px; width: 15px; height: 15px; color: var(--slate); pointer-events: none; }
.nk-media-search .nk-input { padding-left: 32px; }
/* Dropdown Type : largeur au contenu (ne s'étire pas comme un .nk-input plein). */
.nk-media-filterbar__type { width: auto; flex: 0 0 auto; min-width: 150px; }
/* Libellé « Format » précédant son segment (méta) — même taille ET même poids
   que le reste de la barre (plus de gras isolé). Taille via la règle unifiée. */
.nk-media-filterbar__lbl { font-weight: var(--fw-normal); color: var(--muted); }
/* Case « Inclure les archivés » : label cliquable aligné, même taille ET même
   poids que le reste de la barre (plus de gras isolé). */
.nk-media-filterbar__check { display: inline-flex; align-items: center; gap: var(--space-xs); font-weight: var(--fw-normal); color: var(--ink); cursor: pointer; white-space: nowrap; }
.nk-media-filterbar__check input { cursor: pointer; }
/* Bouton de segment réduit à une icône (densité de grille). */
.nk-segment__btn--icon { padding: 4px 9px; display: inline-flex; align-items: center; }
.nk-segment__btn--icon svg { width: 15px; height: 15px; display: block; }

/* ══════════════════════════════════════════════════════════════
   BIBLIOTHÈQUE MÉDIA — modale d'inspection / édition (plein cadre)
   Éditeur haut (.nk-modal--editor, plafond ~85vh) ÉLARGI : visuel zoom/pan
   à gauche + panneau métadonnées à droite. Anatomie : en-tête fixe → corps
   2 colonnes (seul le panneau droit défile) → pied fixe (Fermer/Enregistrer).
   Les actions média (copie / téléchargement / archive / suppression) sont
   regroupées dans un pied FIXE du panneau droit, jamais éparpillées.
   ══════════════════════════════════════════════════════════════ */
.nk-media-inspect { width: 95vw; max-width: 1400px; }
/* Corps en 2 colonnes pleine hauteur (override du .nk-modal__body en colonne). */
.nk-media-inspect__body { flex-direction: row; gap: var(--space-md); flex: 1; min-height: 0; margin-bottom: 0; padding: var(--space-md); overflow: hidden; }
/* Colonne gauche : outils zoom + viewport plein cadre extensible. */
.nk-media-inspect__visual { display: flex; flex-direction: column; gap: var(--space-sm); flex: 1; min-width: 0; }
.nk-media-inspect__tools { display: flex; align-items: center; gap: var(--space-sm); flex-wrap: wrap; }
.nk-media-inspect__vp { flex: 1; min-height: 180px; position: relative; overflow: hidden; border: 1px solid var(--border); border-radius: var(--r-sm); cursor: grab; background: var(--surface-2, #f4f6f8); }
.nk-media-inspect__vp img { position: absolute; user-select: none; }
/* Colonne droite : corps défilant (champs + infos) + pied d'actions fixe. */
.nk-media-inspect__panel { flex: 0 0 360px; width: 360px; display: flex; flex-direction: column; min-height: 0; border-left: 1px solid var(--border); }
.nk-media-inspect__fields { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: var(--space-lg); padding: 0 var(--space-sm) var(--space-md) var(--space-md); }
/* Un champ = label (~15px) au-dessus de son unique contrôle (un champ / ligne). */
.nk-media-inspect__field { display: flex; flex-direction: column; gap: var(--space-xs); }
.nk-media-inspect__label { font-size: 15px; font-weight: 600; color: var(--ink); }
/* Valeur en lecture seule (libellé figé). */
.nk-media-inspect__ro { font-size: var(--fs-md); font-weight: 600; color: var(--ink); padding: var(--space-sm); border: 1px solid var(--border); border-radius: var(--r-sm); background: var(--surface-2, #f4f6f8); }
/* Grille d'infos clé / valeur (corps 13, ≥ plancher modale). */
.nk-media-inspect__info { display: flex; flex-direction: column; gap: var(--space-xs); font-size: var(--fs-base); }
.nk-media-inspect__inforow { display: flex; gap: var(--space-sm); }
.nk-media-inspect__infokey { min-width: 84px; color: var(--slate); }
.nk-media-inspect__infoval { font-weight: 600; color: var(--ink); }
/* Puces de tags dans l'inspecteur (corps 13). */
.nk-media-inspect__chips { display: flex; flex-wrap: wrap; gap: var(--space-xs); }
.nk-media-inspect__chip { display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; background: #eef2ff; border: 1px solid #c7d2fe; border-radius: 999px; font-size: var(--fs-base); color: #3730a3; }
.nk-media-inspect__chip button { border: 0; background: none; cursor: pointer; font-size: 14px; line-height: 1; padding: 0; color: inherit; }
/* Pied d'actions FIXE du panneau droit : actions à droite, danger pleine largeur. */
.nk-media-inspect__actions { flex: 0 0 auto; display: flex; flex-direction: column; gap: var(--space-sm); padding: var(--space-md) var(--space-sm) 0 var(--space-md); border-top: 1px solid var(--border); }
.nk-media-inspect__actions-row { display: flex; flex-wrap: wrap; gap: var(--space-sm); justify-content: flex-end; }
/* Suppression définitive volontairement dévalorisée : simple lien texte à droite,
   jamais un bouton mis en valeur (l'action recommandée est Archiver, en rouge). */
.nk-media-inspect__del-wrap { display: flex; justify-content: flex-end; }
.nk-media-inspect__del { background: none; border: 0; padding: 2px 0; font-family: inherit; font-size: var(--fs-base); color: var(--muted); cursor: pointer; text-decoration: underline; }
.nk-media-inspect__del:hover:not(:disabled) { color: var(--ink); }
.nk-media-inspect__del:disabled { opacity: .5; cursor: not-allowed; text-decoration: none; }
/* Pied fixe de la modale (padding propre, car .nk-modal--editor met padding:0). */
.nk-media-inspect__modalfoot { padding: var(--space-md); border-top: 1px solid var(--border); background: var(--surface); }

/* ─────────────────────────────────────────────
   MATRICE DES DÉCLINAISONS (PIM ▸ fiche ▸ Déclinaisons) — 2026-08-04

   Grille de CARTES et non tableau bordé. La différence n'est pas décorative :
   des bordures collées se lisent comme une liste à plusieurs colonnes, alors
   qu'un espacement régulier entre cellules donne à voir le CROISEMENT
   ligne × colonne, qui est tout l'intérêt de cette vue.

   On reste sur `<table>` — et non sur une grille CSS — pour garder
   l'alignement automatique des colonnes sur leur contenu le plus large et le
   défilement horizontal de `.nk-table-wrap` ; `border-collapse: separate` +
   `border-spacing` donnent l'espacement demandé sans rien réimplémenter.
   ───────────────────────────────────────────── */
.nk-decli-matrice {
  border-collapse: separate;
  border-spacing: var(--space-md);
  /* `auto` et non `100%` : la table ne s'étire pas, donc la première colonne
     se cale sur son contenu au lieu d'absorber la largeur restante. */
  width: auto;
  font-size: var(--fs-base);
}
.nk-decli-matrice th,
.nk-decli-matrice td {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--white);
  /* Couleur de texte posée ICI et non sur chaque contenu : la table n'est pas
     une `.nk-table` et n'hérite donc d'aucune couleur de composant. */
  color: var(--ink);
  padding: var(--space-md) var(--space-lg);
  vertical-align: middle;
}
/* En-têtes — teintés, pour se détacher des cellules de données. */
.nk-decli-matrice thead th,
.nk-decli-matrice tbody th { background: var(--surface); font-weight: var(--fw-semibold); }
.nk-decli-matrice thead th { text-align: center; white-space: nowrap; }
/* Première colonne : `width: 1%` est la façon canonique de dire à un tableau
   « prends la place du contenu, pas une de plus ». Libellé collé à gauche et
   à sa pastille — sans quoi il flotte au milieu d'une colonne trop large. */
.nk-decli-matrice tbody th {
  text-align: center;
  white-space: nowrap;
  width: 1%;
}
/* `.nk-decli-matrice__val` supprimée 2026-08-05 : une rangée inline-flex, déjà
   couverte par `.nk-inline-row--inline`. La cellule étant centrée par
   `text-align`, son `justify-content` ne servait à rien. */
/* Pastille de couleur, quand PrestaShop en déclare une pour la valeur.
   Le fond reste le SEUL style inline légitime de cette vue : c'est une couleur
   de donnée, envoyée par la boutique, qu'aucune classe ne peut prévoir. */
.nk-decli-matrice__dot {
  width: 10px; height: 10px; border-radius: 50%;
  border: 1px solid var(--border-d);
  flex: 0 0 auto;
}
/* Coin haut-gauche : il nomme les deux axes, mais ne doit PAS imposer sa
   largeur à la colonne — d'où le texte petit et le retour à la ligne permis.
   Sélecteur qualifié par `thead th` (0,3,2) plutôt que deux `!important` : il
   bat `.nk-decli-matrice thead th` (0,1,2) par simple spécificité. */
.nk-decli-matrice thead th.nk-decli-matrice__coin {
  white-space: normal;
  font-size: var(--fs-sm);
  color: var(--muted);
  font-weight: var(--fw-normal);
}
/* Cellules de données.

   ⚠️ Les fonds d'état sont qualifiés par `td` — `.nk-decli-matrice td` pose
   déjà `background:#fff` et le simple ajout d'une classe ne suffisait PAS à
   le battre (0,1,1 contre 0,1,0) : les ruptures restaient blanches et les
   cases absentes opaques. Ne pas retirer le `td`. */
.nk-decli-matrice__cell { text-align: center; cursor: pointer; }
.nk-decli-matrice td.nk-decli-matrice__cell--rupture { background: var(--danger-l); }
.nk-decli-matrice td.nk-decli-matrice__cell--masquee { background: var(--bg); }
/* Combinaison ABSENTE : pas de carte pleine, un cadre pointillé. Le vide est
   une information — c'est la déclinaison qui n'existe pas. */
.nk-decli-matrice td.nk-decli-matrice__cell--vide {
  background: transparent;
  border-style: dashed;
  text-align: center;
  color: var(--muted);
  font-size: var(--fs-sm);
  font-style: italic;
}
/* Ligne ou colonne mise en évidence : le reste s'atténue, rien ne disparaît. */
.nk-decli-matrice__cell--attenue { opacity: .35; }
/* TROIS classes supprimées le 2026-08-05, toutes remplaçables par l'existant :
   - `__ref`    → `.nk-inline-row--inline` ; sa taille et sa couleur viennent
                  maintenant de `.nk-decli-matrice` et de ses cellules ;
   - `__etat`   → plus rien à poser, `.nk-decli-matrice` porte déjà `--fs-base`,
                  et la couleur d'état passe par `.nk-text-danger` /
                  `.nk-text-success` / `.mu` ;
   - `__legende` → `.nk-inline-row .nk-inline-row--wrap .nk-gap-lg .nk-mt-md
                  .nk-text-muted-sm`. Elle reste SOUS la grille : accolée à la
                  bascule « Liste / Matrice », elle n'apparaissait qu'en vue
                  matrice et faisait bouger la bascule d'une vue à l'autre. */

/* ══════════════════════════════════════════════════════════════════════════
   LISTE À CHOIX MULTIPLE (.nk-multipick)
   ══════════════════════════════════════════════════════════════════════════
   Un bouton qui ouvre un panneau de cases à cocher, sur le modèle des filtres
   de catalogue e-commerce. Remplace le `<select multiple>` natif, inutilisable
   sans le savoir : cocher une seconde valeur y exige Ctrl/Cmd + clic, que
   personne ne devine — le second clic REMPLACE le premier choix en silence.

   Le panneau reste OUVERT tant qu'on coche : il ne se referme qu'au bouton
   « Fermer » ou en rouvrant son déclencheur. Même esprit que la règle des
   modales (docs/UX.md) — on ne perd pas une saisie sur un clic de trop.

   Premier usage : zone de travail en masse de Filtres ▸ Contrôle ▸ Produits. */

/* ⚠️ Plus de `position: relative` ici depuis le 2026-08-11 : le panneau est
   passé en `position: fixed` (voir plus bas) et ne s'ancre donc plus sur cette
   boîte. La laisser aurait promis un point d'ancrage qui n'ancre plus rien. */
.nk-multipick { display: inline-block; }

.nk-multipick__btn {
  display: inline-flex; align-items: center; justify-content: space-between; gap: var(--space-sm);
  min-width: 180px; padding: 6px 10px;
  border: 1px solid var(--border-d); border-radius: var(--r-sm);
  background: var(--white); color: var(--ink);
  font-size: var(--fs-base); font-family: inherit; cursor: pointer;
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.nk-multipick__btn:hover      { border-color: var(--cyan); }
.nk-multipick__btn:focus-visible { border-color: var(--cyan); box-shadow: var(--focus-ring); outline: none; }
/* Ouvert : le déclencheur reste visuellement lié à son panneau. */
.nk-multipick__btn.on         { border-color: var(--cyan); box-shadow: var(--focus-ring); }

/* Résumé du choix. Vide, il dit ce qu'on attend de l'utilisateur plutôt que de
   laisser un blanc qu'on lit comme un champ cassé. */
.nk-multipick__resume         { color: var(--muted); }
.nk-multipick__resume--on     { color: var(--cyan-d); font-weight: var(--fw-bold); }

/* ⚠️ `position: fixed` — et surtout PAS `absolute` (correctif du 2026-08-11).
   Un absolu est DÉCOUPÉ par le premier ancêtre qui rogne, quel que soit son
   `z-index`, et les deux écrans qui emploient ce composant en ont un chacun :
   la zone défilante qui porte les filtres ET ce qui les suit (`.nk-pane__body`
   côté Filtres ▸ Contrôle ▸ Produits, le `<main>` du shell côté fiche PIM,
   tous deux en `overflow-y: auto` — ce qui fait passer l'axe X à `auto` aussi,
   donc rogne dans les deux sens). Ouvrir une liste sur un filtre proche du bas
   coupait le panneau au bord et allongeait la surface défilante : la barre de
   défilement changeait de taille au clic. `.nk-card--debordant` ne règle, lui,
   que le rognage des CARTES, un cran plus bas.

   ⚠️ AUCUN `top` / `left` / `min-width` ici, volontairement : les coordonnées
   d'un `fixed` dépendent de la position du bouton à l'écran, aucune classe ne
   peut les porter. Elles sont calculées à l'exécution par
   `frontend/shared/nk-multipick.js § ANCRAGE`, qui les recalcule à chaque
   défilement d'ancêtre et à chaque redimensionnement, bascule le panneau
   au-dessus du bouton quand la place manque dessous, et plafonne sa hauteur
   pour qu'il tienne dans la fenêtre. Un `top: 0` de repli pincerait le panneau
   dans le coin de l'écran avant ce calcul ; sans aucun décalage, il se pose sur
   sa position statique — c'est-à-dire là où il faut.

   `--z-overlay` inchangé, et pour la raison qui vaut déjà au menu des sites
   attaché au `<body>` : le panneau peut maintenant passer au-dessus de la
   topbar (`--z-topbar`), il doit rester sous les modales (`--z-modal`).

   ⚠️ Et c'est bien pourquoi le JS le MASQUE (`visibility`, posé à l'exécution)
   dès que son bouton quitte sa zone défilante : sans ça, un `fixed` à ce niveau
   d'empilement continue de se peindre par-dessus la barre de contexte et la
   topbar, aux coordonnées d'un bouton qu'on ne voit plus. Le rognage d'un
   ancêtre ne le protège plus — c'est justement ce qu'on lui a retiré. */
.nk-multipick__panel {
  position: fixed; z-index: var(--z-overlay);
  max-height: 260px; overflow-y: auto;
  display: flex; flex-direction: column;
  border: 1px solid var(--border-d); border-radius: var(--r-md);
  background: var(--white); box-shadow: var(--shadow-lg);
}

.nk-multipick__opt {
  display: flex; align-items: center; gap: var(--space-md);
  padding: 6px 12px; cursor: pointer;
  font-size: var(--fs-base); color: var(--ink); white-space: nowrap;
}
.nk-multipick__opt:hover { background: var(--surface); }
/* La case ne se comprime jamais : sur un libellé long, un `flex` qui la rogne
   la rend indistincte de son texte et l'espace du `gap` disparaît avec elle. */
.nk-multipick__opt > input { flex: 0 0 auto; }

/* Valeur COCHÉE : même encre et même graisse que le résumé du bouton fermé
   (`__resume--on`). Les deux disent la même chose — « c'est ce qui est
   choisi » — et les distinguer ferait chercher une différence qui n'existe
   pas. La case seule ne suffisait pas : sur une liste de trente valeurs, il
   faut balayer trente cases pour retrouver les deux qui comptent. */
.nk-multipick__opt--on { color: var(--cyan-d); font-weight: var(--fw-bold); }

/* Pastille de couleur d'une valeur, à gauche de son libellé — elle distingue
   « navy » de « bleu » là où seuls les mots les séparaient. La FORME est ici ;
   le fond, lui, est une donnée réglée par le merch et arrive en `style=` (seule
   forme possible pour une couleur variable, déjà la pratique de l'éditeur de
   filtre). Le contour évite qu'une valeur blanche disparaisse sur le panneau. */
.nk-multipick__pastille {
  flex: 0 0 auto; display: inline-block;
  width: 14px; height: 14px;
  border: 1px solid var(--border-d); border-radius: var(--r-circle);
}

/* Pied COLLANT : sur une longue liste, le bouton de fermeture doit rester
   atteignable sans remonter. */
.nk-multipick__foot {
  position: sticky; bottom: 0;
  padding: var(--space-sm); border-top: 1px solid var(--border);
  background: var(--white);
}

/* ══════════════════════════════════════════════════════════════════════════
   GRILLE DE FILTRES (.nk-filtres-*)
   ══════════════════════════════════════════════════════════════════════════
   Présentation PARTAGÉE des filtres rangés par groupe. Deux écrans la
   consomment et doivent se ressembler exactement (décision Lionel,
   2026-08-07) :
     - fiche PIM ▸ section « Filtres »        (pim-article-section-caracteristiques-filtres.js)
     - Filtres ▸ Contrôle ▸ Produits          (hub-filtres-remplissage.js)

   Elle vit ici et non en styles inline dans les deux fichiers : deux blocs
   d'inline jumeaux divergent au premier ajustement, et c'est exactement ce
   que le partage de `NkMultipick` existe déjà pour empêcher côté rendu du
   champ. Ici on partage la MISE EN PAGE autour du champ.

   DEUX DISPOSITIONS, et c'est voulu : les blocs vont CÔTE À CÔTE là où la
   largeur est libre (travail en masse), et EN COLONNE là où ils occupent la
   moitié droite d'un bloc à deux colonnes (fiche PIM, `--colonne`). */

.nk-filtres-blocs {
  display: flex; flex-wrap: wrap; align-items: flex-start;
  gap: var(--space-lg);
}

/* Empilement : un bloc par ligne, pleine largeur de sa colonne. */
.nk-filtres-blocs--colonne { flex-direction: column; flex-wrap: nowrap; }
.nk-filtres-blocs--colonne > .nk-filtres-bloc { flex: 0 0 auto; width: 100%; }

/* ─── ZONE EN ATTENTE — VOILE D'ACTIVATION (Lionel, 2026-08-11) ───
   Consommé par Filtres ▸ Contrôle ▸ Produits, où la zone s'affiche dès qu'une
   branche est choisie PARCE QU'ELLE DIT quels filtres la famille impose — mais
   où l'on n'écrit rien tant qu'aucun produit n'est coché. Cette inertie était
   jusqu'ici invisible : les champs avaient l'air normaux, la liste s'ouvrait, et
   le geste n'aboutissait nulle part sans que rien ne l'explique.

   TRANSLUCIDE, et ce n'est pas une question de goût : les noms des filtres
   doivent rester LISIBLES dessous, puisque c'est la seule raison pour laquelle
   la zone s'affiche avant toute sélection. Un voile opaque annulerait
   exactement ce qu'il recouvre.

   Il intercepte AUSSI les clics — c'est lui qui reçoit le pointeur, donc plus
   aucune liste déroulante ne s'ouvre. L'écran double néanmoins la garde côté
   JavaScript : une feuille de style n'est pas un contrat de comportement.

   `z-index: 2` : empilement LOCAL à la zone, jamais un overlay global — le
   voile ne couvre qu'un bloc de la page (CLAUDE.md § Z-index, < 100 toléré). */
.nk-filtres-blocs--voile { position: relative; }

.nk-filtres-voile {
  position: absolute; inset: 0; z-index: 2;
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-lg);
  border-radius: var(--r-md);
  /* Gris froid translucide : assez présent pour dire « pas maintenant », assez
     clair pour qu'on lise encore les noms de filtres au travers. */
  background: rgba(100, 116, 139, .30);
  /* Le curseur ne promet rien : la zone n'attend pas un clic ICI, elle attend
     une coche dans la liste des produits. */
  cursor: default;
}

/* La consigne se pose sur une plaque OPAQUE. À même le voile, elle se lirait
   par-dessus les bandeaux et les champs — illisible là où elle doit justement
   expliquer quoi faire. */
.nk-filtres-voile__mot {
  max-width: 420px; text-align: center;
  padding: var(--space-md) var(--space-xl);
  border: 1px solid var(--border-d); border-radius: var(--r-md);
  background: var(--white); box-shadow: var(--shadow-md);
  font-size: var(--fs-base); font-weight: var(--fw-semibold); color: var(--ink);
}

/* Un bloc = un GROUPE de filtres. La base de `flex` fixe la largeur en deçà
   de laquelle on préfère passer à la ligne plutôt que comprimer les champs.

   `margin-bottom: 0` n'est pas cosmétique : les blocs sont des `.nk-card`, qui
   porte 22 px de marge basse pour aérer des blocs EMPILÉS. En rangée, cette
   marge s'ajoute au `gap` du conteneur — 34 px sous un bloc contre 12 px entre
   deux, alors que rien ne justifie que le vertical et l'horizontal diffèrent.
   L'espacement d'une rangée appartient à la rangée. */
.nk-filtres-bloc { flex: 1 1 320px; min-width: 260px; margin-bottom: 0; }

/* Titre du groupe : c'est le repère de lecture de toute la zone, d'où la
   graisse pleine et le filet qui le sépare de ses champs. */
.nk-filtres-bloc__titre {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-md);
  padding-bottom: var(--space-sm); margin-bottom: var(--space-lg);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-base); font-weight: var(--fw-bold); color: var(--ink);
}

/* Verdict du groupe, poussé à droite du titre. Il est PAR GROUPE et pas
   seulement global : un compteur d'écran dit combien il manque, il ne dit pas
   OÙ — et c'est la seule question que se pose celui qui doit corriger. */
.nk-filtres-bloc__verdict { margin-left: auto; font-weight: var(--fw-semibold); }

/* Groupe INCOMPLET : filet et teinte, pour qu'il se repère sans lire.
   La teinte porte sur l'en-tête seul — teinter tout le bloc rendrait ses
   champs moins lisibles au moment précis où il faut les remplir. */
.nk-filtres-bloc--manque { border-left: 3px solid var(--warning); }
.nk-filtres-bloc--manque > .nk-card-body > .nk-filtres-bloc__titre {
  background: var(--warning-l);
  margin: calc(-1 * var(--space-md)) calc(-1 * var(--space-xl)) var(--space-lg);
  padding: var(--space-md) var(--space-xl) var(--space-sm);
  border-bottom-color: var(--warning);
}

/* `.nk-filtres-bloc--attenue` — mise en retrait des groupes qu'un emplacement
   sélectionné ne portait pas — SUPPRIMÉE le 2026-08-07 avec la sélection
   elle-même (décision Lionel). Une atténuation se lit comme un état de la
   donnée ; celle-ci ne disait que « j'ai cliqué à côté ». */

.nk-filtres-champs {
  display: flex; flex-wrap: wrap; align-items: flex-start;
  gap: var(--space-lg);
}

/* UN CADRE PAR FILTRE (maquette Lionel « 1a », 2026-08-11).
   Le cadre absorbe n'importe quel contrôle — liste, puces multiples, bascule
   oui/non, intervalle, champ de mesure — ce qui est la raison d'être du
   changement : le module déclare SEPT rendus, et ces écrans n'en savaient
   afficher qu'un. Il donne aussi une frontière visible à chaque filtre, là où
   des champs posés côte à côte se lisaient comme une seule grille continue.

   ⚠️ Ces classes ne servent QU'AUX DEUX ÉCRANS DE SAISIE (fiche PIM ▸ Filtres
   et Filtres ▸ Contrôle ▸ Produits), qui doivent se ressembler exactement. Les
   modifier ici les change tous les deux d'un coup — c'est voulu, et c'est
   précisément ce qui les empêche de re-diverger.

   `flex: 1 1 260px` : les cadres se répartissent la ligne et passent à la
   suivante quand ils ne tiennent plus, sans colonnes déclarées. Un filtre plus
   large que les autres (le numérique, qui porte un champ de mesure et son
   bouton) prend naturellement sa place sans réglage particulier. */
/* ⚠️ Toujours PAS d'`overflow: hidden` ici, même pour arrondir le bandeau.
   Le panneau de `.nk-multipick` s'ouvrait par-dessus ce cadre en
   `position: absolute`, et le moindre ancêtre qui rogne le DÉCOUPAIT : la liste
   s'ouvrait DANS le cadre au lieu de passer par-dessus (défaut introduit et
   corrigé le 2026-08-11, recette Lionel). Le panneau est passé en
   `position: fixed` depuis, ce qui le met hors d'atteinte du rognage — mais
   n'en fait pas une invitation à rogner ici : le cadre porte aussi le champ de
   mesure et ses notes, et un `hidden` y couperait un texte long sans le dire.
   L'arrondi du bandeau se fait donc sur le bandeau lui-même. */
.nk-filtres-champ {
  flex: 1 1 260px;
  min-width: 200px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--white);
}

/* Nom du filtre : c'est le libellé du champ qu'on remplit, donc en encre
   pleine et au corps de base — jamais une métadonnée grisée.
   Devenu le BANDEAU d'en-tête du cadre : fond léger, filet dessous, et plus de
   marge basse (il est collé à son corps, pas posé au-dessus de lui). */
.nk-filtres-champ__nom {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-md);
  padding: var(--space-md) var(--space-lg);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  /* Arrondi PORTÉ PAR LE BANDEAU, et non obtenu en rognant le cadre : rogner
     découperait le panneau déroulant (cf. l'avertissement ci-dessus). */
  border-radius: var(--r-md) var(--r-md) 0 0;
  font-size: var(--fs-base); font-weight: var(--fw-semibold); color: var(--ink);
}

/* Corps du cadre : le contrôle, puis ce qui l'accompagne (champ de mesure,
   consigne, notes, avertissements). En colonne, pour que chaque élément prenne
   la largeur et qu'aucun ne se retrouve tassé contre un voisin. */
.nk-filtres-champ__corps {
  display: flex; flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-lg);
}

/* Ligne d'explication sous un champ (valeur conservée, unité attendue, endroit
   où se remplit un filtre qui ne se saisit pas ici). L'encre vient d'une classe
   utilitaire — `nk-text-muted-sm`, `nk-text-warning` — cette classe-ci ne pose
   rien d'autre que son appartenance au cadre.
   ⚠️ AUCUNE RÈGLE, volontairement — et c'est pour ça qu'elle n'apparaît pas
   ci-dessous. L'écart vient du corps (`gap: var(--space-sm)`), qui est un
   conteneur en colonne depuis le 2026-08-11. Le `margin-top` que cette classe
   portait avant s'y AJOUTAIT, doublant l'écart entre deux notes. Relecture à
   froid du 2026-08-11. */

/* Champ NON RENSEIGNÉ : l'anneau porte sur le contrôle lui-même, là où se
   fait le geste. Un bloc entier teinté dit « quelque chose manque ici » ;
   l'anneau dit QUOI remplir. */
.nk-filtres-champ--manque .nk-multipick__btn {
  border-color: var(--warning);
  box-shadow: 0 0 0 3px var(--warning-l);
}

/* ── Bloc « Catalogues et filtres » de la fiche PIM ──
   Deux colonnes : à gauche OÙ l'article se trouve, à droite CE QU'IL FAUT y
   renseigner. Les deux se lisaient jusqu'ici dans deux cartes séparées, et la
   gauche répétait ce que la droite montrait en entier. */
.nk-filtres-shell { display: flex; align-items: stretch; flex-wrap: wrap; }

.nk-filtres-shell__aside {
  flex: 0 0 280px; min-width: 240px;
  padding: var(--space-lg);
  border-right: 1px solid var(--border);
}

.nk-filtres-shell__corps { flex: 1; min-width: 320px; padding: var(--space-lg); }

/* Intitulé de colonne. Capitales et interlettrage : c'est une étiquette de
   zone, pas un titre de contenu — elle ne doit pas concurrencer les noms de
   groupes à droite. */
.nk-filtres-shell__label {
  text-transform: uppercase; letter-spacing: .04em;
  margin-bottom: var(--space-md);
}

/* Un emplacement catalogue. PUREMENT INFORMATIF : ni curseur, ni survol, ni
   état actif (décision Lionel, 2026-08-07). Il dit où l'article se trouve, et
   rien d'autre — tout affordance de clic promettrait une action qui n'existe
   pas. */
.nk-filtres-empl { padding: var(--space-md); }

/* Chemin : ancêtres en gris, feuille en gras. L'article est rangé DANS la
   feuille ; les ancêtres ne servent qu'à savoir de quelle branche on parle. */
.nk-filtres-empl__chemin { font-size: var(--fs-base); color: var(--ink); }
.nk-filtres-empl__groupes { margin-top: var(--space-xs); }

/* Note de bas de colonne : elle explique la FUSION, c'est-à-dire pourquoi la
   colonne de droite ne se découpe pas par emplacement. Sans elle, un filtre
   commun à deux emplacements se lirait comme un oubli. */
.nk-filtres-shell__note {
  margin-top: var(--space-lg); padding-top: var(--space-lg);
  border-top: 1px solid var(--border);
}

/* Carte qui laisse DÉBORDER son contenu. `.nk-card` coupe (`overflow:hidden`)
   pour que ses angles arrondis rognent proprement en-têtes et tableaux — mais
   ce clipping mange aussi les panneaux flottants posés dedans : la liste de
   choix d'un `.nk-multipick` s'ouvrait invisible, à l'intérieur du bloc.
   À réserver aux cartes qui portent un menu ou un panneau.
   ⚠️ Depuis le 2026-08-11, `.nk-multipick__panel` est en `position: fixed` et
   n'a PLUS besoin de cette classe : elle reste posée sur les cartes de filtres
   des deux écrans, sans effet utile pour elles, et se retirera de là au fil de
   l'eau. Elle garde tout son sens pour un menu resté en `absolute`. */
.nk-card--debordant { overflow: visible; }

/* ══════════════════════════════════════════════════════════════════════════
   CHOIX D'ACTION, TRAJET ET LISTE DE LOT — famille partagée
   ══════════════════════════════════════════════════════════════════════════

   Ajoutée le 2026-08-08 sur autorisation nommée de Lionel, pour la modale de
   rangement de « Déplacement articles ». Elle vit ICI et non dans une feuille
   d'écran parce qu'aucun de ces motifs ne lui appartient : choisir UNE action
   parmi deux en la comprenant, montrer un trajet DÉPART → ARRIVÉE, lister les
   objets d'un lot avec leur référence — trois besoins que d'autres écrans
   auront, et trois copies qui divergeraient.

   Le fil conducteur : une action irréversible se décide sur ce qu'on LIT, pas
   sur ce qu'on devine. D'où l'explication sous chaque choix, et le départ
   montré aussi gros que l'arrivée. */

/* ── Libellé de section ──
   PAS de nouvelle classe : `.nk-eyebrow-label` existe déjà et porte exactement
   ce besoin. Une revue à froid a rattrapé ici un doublon `.nk-eyebrow` — soit la
   dette même que le lot CSS Filtres venait de résorber.
   Une seule chose lui manquait : dans une MODALE le plancher n'est pas celui du
   hub mais 13 px (CLAUDE.md § Modales). D'où cette règle unique, portée par le
   CONTEXTE et non par un second nom — elle ne touche aucun des écrans qui
   emploient déjà cette classe ailleurs. */
.nk-modal .nk-eyebrow-label {
  font-size: var(--fs-base);
}

/* Pile de SECTIONS. `.nk-stack-sm` (5 px) est l'écart entre champs d'un même
   bloc ; entre blocs il faut l'espace d'un paragraphe, sinon les sections d'une
   modale dense se touchent.
   La classe était RÉFÉRENCÉE par la modale de rangement sans exister nulle part
   dans le dépôt : un `gap` tombé à zéro qu'aucun lint ne voit — `lint_dead_refs`
   vérifie les appels JS, pas les classes CSS. */
.nk-stack { display: flex; flex-direction: column; gap: var(--space-lg); }

/* ── Cartes de choix ──
   Deux options côte à côte, chacune avec son titre et ce qu'elle FAIT. Le
   `grid` à colonnes égales est volontaire : deux cartes de largeur différente
   suggèrent que l'une est le défaut, alors que le défaut se dit par la
   sélection. Elles repassent en colonne sous 560 px, où deux colonnes
   couperaient les explications en bouillie. */
.nk-choices {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-md);
}

@media (max-width: 560px) {
  .nk-choices { grid-template-columns: 1fr; }
}

/* EMPILÉES (2026-08-09). Au-delà de deux options, ou quand chaque explication
   fait plusieurs lignes, la grille à colonnes égales rétrécit les cartes au
   point de hacher le texte qui porte justement la décision. Modificateur du
   MÊME composant, et non seconde famille de classes : le lot « déplacer /
   copier / remonter » avait commencé par redéclarer `.nk-choice*` sous les
   mêmes noms, en ignorant celles-ci — deux définitions du même composant dans
   le même fichier, dont c'est la plus tardive qui gagnait. */
.nk-choices--empilees { grid-template-columns: 1fr; }

.nk-choice {
  display: block;
  width: 100%;
  padding: var(--space-lg);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--ink);
  cursor: pointer;
  font-family: var(--font);
  text-align: left;
}

.nk-choice:hover { border-color: var(--cyan); }

/* Anneau de focus standard du hub : la carte est un bouton, elle se prend au
   clavier. */
.nk-choice:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* SÉLECTIONNÉE. Le liseré doublé par `inset` plutôt qu'une bordure de 2 px :
   une bordure qui s'épaissit décale le contenu de 1 px et fait tressauter les
   deux cartes à chaque changement d'avis. */
.nk-choice.is-selected {
  background: var(--cyan-l);
  border-color: var(--cyan);
  box-shadow: inset 0 0 0 1px var(--cyan);
}

.nk-choice__title {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  color: var(--navy);
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
}

/* L'explication n'est pas une aide facultative : c'est elle qui distingue
   « copier » de « déplacer » pour qui ne connaît pas le modèle PrestaShop. */
.nk-choice__desc {
  /* `display:block` : le texte vit dans un `<span>` — un `<p>` dans un `<button>`
     est invalide (un bouton n'accepte que du contenu de phrasé), relevé en revue
     à froid. */
  display: block;
  margin: var(--space-sm) 0 0;
  color: var(--slate);
  font-size: var(--fs-base);
  line-height: 1.45;
}

/* ── Trajet : deux blocs de MÊME poids ──
   Le départ était un pavé gris et l'arrivée un simple champ : on lisait « voici
   un rappel, et voici la vraie question », alors que ce qui se joue est ce
   qu'on QUITTE autant que ce qu'on rejoint. Même cadre, même largeur, reliés
   par une flèche. */
.nk-trajet {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: stretch;
  gap: var(--space-md);
}

@media (max-width: 560px) {
  .nk-trajet { grid-template-columns: 1fr; }
  /* En colonne la flèche pointe vers le bas, sinon elle désigne le vide. */
  .nk-trajet__fleche { transform: rotate(90deg); }
}

.nk-trajet__bloc {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  min-width: 0;
  padding: var(--space-lg);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}

/* Bloc de DÉPART d'une action qui retire : il perd quelque chose, et ça se
   voit. Réservé au cas destructeur — l'appliquer aux deux modes banaliserait
   le seul qui mérite l'alerte. */
.nk-trajet__bloc--retire {
  background: var(--warning-l);
  border-color: var(--warning-b);
}

.nk-trajet__valeur {
  color: var(--navy);
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
  overflow-wrap: anywhere;
}

.nk-trajet__effet {
  color: var(--slate);
  font-size: var(--fs-base);
}

.nk-trajet__fleche {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
}

/* ── Liste des objets d'un lot ──
   Hauteur plafonnée et défilement PROPRE au bloc : dans une modale, une liste
   de trois cents lignes repousse les boutons hors de l'écran, et on valide une
   action irréversible sans avoir vu son bouton. */
.nk-lot {
  max-height: 240px;
  overflow-y: auto;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}

.nk-lot__ligne {
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
  padding: var(--space-md) var(--space-lg);
  border-top: 1px solid var(--border);
}

.nk-lot__ligne:first-child { border-top: none; }

/* Référence en chasse fixe : on la compare à un écran de gestion de stock ou à
   une étiquette, caractère par caractère. */
.nk-lot__ref {
  flex-shrink: 0;
  padding: 1px var(--space-md);
  background: var(--surface);
  border-radius: var(--r-sm);
  color: var(--slate);
  font-family: monospace;
  /* 13 px et non 12 : plancher de modale. Une référence se compare caractère par
     caractère à une étiquette ou à un écran de stock — c'est le pire endroit du
     hub pour rapetisser le texte. */
  font-size: var(--fs-base);
}

.nk-lot__corps {
  flex: 1;
  min-width: 0;
}

/* `block` et non l'inline par défaut d'un `<span>` : sans lui le nom et les
   chemins se suivaient sur la MÊME ligne, en un seul pavé illisible — constaté
   en recette le 2026-08-08. */
.nk-lot__nom {
  display: block;
  color: var(--ink);
  font-size: var(--fs-base);
  font-weight: var(--fw-medium);
}

/* Chemins actuels : ils tiennent sur plusieurs lignes plutôt que d'être coupés.
   Un chemin tronqué au milieu ne permet pas de vérifier qu'on tient le bon
   produit, ce qui est toute sa raison d'être ici. */
.nk-lot__chemins {
  display: block;
  margin-top: 2px;
  color: var(--slate);
  /* Les chemins sont là pour être LUS — c'est leur seule raison d'être avant une
     action destructive. Plancher de modale, donc, et pas 12 px. */
  font-size: var(--fs-base);
  line-height: 1.4;
}

/* `.nk-lot__corps` est un `<span>` pour rester neutre dans le flux du parent
   flex ; il doit néanmoins se comporter en bloc pour que ses enfants
   s'empilent. */
.nk-lot__corps { display: block; }

/* ══════════════════════════════════════════════════════════════════════════
   FICHE PIM — AFFECTATION CATALOGUE ET CHEMINS DE CATÉGORIE (.nk-pim-cat-*)
   ══════════════════════════════════════════════════════════════════════════
   Déplacé le 2026-08-13 depuis les chaînes JavaScript de
   « frontend/pim/pim-article.js » (feuille de style injectée dans le head à
   l'exécution), où ces règles échappaient à tout lint CSS et à toute relecture.

   ⚠️ DÉPLACEMENT À L'IDENTIQUE — aucune valeur n'a été touchée, et l'ORDRE
   d'origine est conservé, y compris le doublon signalé ci-dessous.

   ⚠️ « .nk-pim-cat-block » est déclaré DEUX FOIS, et ce n'était pas voulu : la
   première déclaration vient de la section « Affectation catalogue », la
   seconde de la card Catalogue de la Synthèse, écrite plus tard sous le même
   nom. Les deux s'appliquent donc aux DEUX écrans — les blocs de la section
   Affectation portent le padding et le border-bottom de la Synthèse sans que
   personne l'ait demandé. Fusionner les deux CHANGERAIT le rendu : c'est un
   lot à part, pas une correction de passage.

   Trois consommateurs : « pim-article-section-catalogue.js » (toute la
   famille), « pim-article-section-synthese.js » (-block, -block__label, -path,
   -seg, -leaf, -sep) et « pim-article-section-seo.js » (-principal__url seul).
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Section « Affectation catalogue » : bloc-titre (uppercase muted), liste
      (bordure + rayon), ligne (dossier + libellé + #id + bouton). Variantes
      pour les catégories masquées (italique gris, opacité réduite) et pour les
      anomalies (ambre). ── */
.nk-pim-cat-block{margin-bottom:14px}
.nk-pim-cat-block__title{font-size:11px;font-weight:700;color:var(--muted);letter-spacing:.5px;text-transform:uppercase;margin-bottom:6px}
.nk-pim-cat-block__title--principal{color:var(--text)}
.nk-pim-cat-block__title--anomaly{color:var(--warning)}
.nk-pim-cat-list{border:1px solid var(--border);border-radius:var(--r-sm);overflow:hidden;background:var(--white)}
.nk-pim-cat-list--anomaly{border-color:var(--warning-b);background:var(--warning-l)}
.nk-pim-cat-row{display:flex;align-items:center;gap:10px;padding:10px 14px}
.nk-pim-cat-row + .nk-pim-cat-row{border-top:1px solid var(--border)}
.nk-pim-cat-list--anomaly .nk-pim-cat-row + .nk-pim-cat-row{border-top-color:var(--warning-b)}
.nk-pim-cat-row__icon{flex-shrink:0;color:var(--muted)}
.nk-pim-cat-row__label{flex:1;font-size:13px;color:var(--text)}
.nk-pim-cat-row__id{color:var(--muted);font-family:Menlo,Consolas,monospace;font-size:11px;flex-shrink:0}
.nk-pim-cat-row__btn{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border:0;background:transparent;color:var(--muted);cursor:pointer;border-radius:50%;flex-shrink:0;transition:background .12s ease,color .12s ease}
.nk-pim-cat-row__btn:hover{background:var(--surface);color:var(--danger)}
.nk-pim-cat-row__btn:focus-visible{outline:2px solid var(--cyan);outline-offset:1px}
.nk-pim-cat-row--hidden{opacity:.85}
.nk-pim-cat-row--hidden .nk-pim-cat-row__icon{opacity:.55}
.nk-pim-cat-row--hidden .nk-pim-cat-row__label{color:var(--muted);font-style:italic}
.nk-pim-cat-row--anomaly .nk-pim-cat-row__icon{color:var(--warning);opacity:1}
.nk-pim-cat-row--anomaly .nk-pim-cat-row__label{color:var(--warning);font-style:italic}
.nk-pim-cat-row--anomaly .nk-pim-cat-row__id{color:var(--warning);font-weight:600}
/* Identifiant de catégorie affiché à l'écran, corps 13 (demande Lionel du
   2026-08-14 : l'id remplace le compteur de produits dans « Catalogue [beta] »,
   des deux côtés de l'écran).
   ⚠️ Classe DISTINCTE, et c'est le point : `.nk-tree__count` est partagée avec
   `nk-catalog-sidebar.js` et `nk-category-picker.js`, `.nk-pim-cat-row__id` avec
   l'ancienne section « Catalogue » qui cohabite pendant toute la beta. Les
   restyler aurait changé la typographie de quatre écrans pour une demande qui
   n'en concerne qu'un.
   Déclarée APRÈS `.nk-pim-cat-row__id` pour gagner sur sa taille quand les deux
   sont posées ; la règle `--anomaly` ci-dessus reste plus spécifique, donc les
   identifiants hors arbre gardent leur orange. */
.nk-catid{font-family:Menlo,Consolas,monospace;font-size:var(--fs-base);color:var(--muted);flex-shrink:0}

/* ── Card « Catégorie principale » : fond blanc + bordure standard, icône
      dossier-étoile or, étoile cyan à gauche du #id, note SEO en pied, URL
      canonique Google en mono bleu lien. ── */
.nk-pim-cat-principal{background:var(--white);border:1px solid var(--border);border-radius:var(--r-sm);padding:14px 16px}
.nk-pim-cat-principal__head{display:flex;align-items:center;gap:10px}
.nk-pim-cat-principal__icon{color:var(--gold);flex-shrink:0;display:inline-flex;align-items:center}
.nk-pim-cat-principal__path{flex:1;font-size:14px;color:var(--text);line-height:1.4}
.nk-pim-cat-principal__path-empty{color:var(--muted);font-style:italic}
.nk-pim-cat-principal__id-block{display:inline-flex;align-items:center;gap:6px;flex-shrink:0}
.nk-pim-cat-principal__star{color:var(--cyan)}
.nk-pim-cat-principal__id{color:var(--muted);font-family:Menlo,Consolas,monospace;font-size:11px}
.nk-pim-cat-principal__foot{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--slate);margin-top:8px;padding-top:8px;border-top:1px solid var(--border)}
.nk-pim-cat-principal__url{display:block;font-family:Menlo,Consolas,monospace;font-size:11px;color:var(--link-google);margin-top:8px;word-break:break-all;line-height:1.5;text-decoration:none}
.nk-pim-cat-principal__url:hover{text-decoration:underline}

/* ── Synthèse ▸ card Catalogue : chemins de catégorie, feuille en gras. ── */
.nk-pim-cat-block{padding:8px 0;border-bottom:1px solid var(--border)}
.nk-pim-cat-block:last-child{border-bottom:0}
.nk-pim-cat-block__label{font-size:11px;letter-spacing:.04em;text-transform:uppercase;color:var(--muted);font-weight:700;margin-bottom:5px}
.nk-pim-cat-path{font-size:13px;line-height:1.5;color:var(--slate)}
.nk-pim-cat-path+.nk-pim-cat-path{margin-top:4px}
.nk-pim-cat-seg{color:var(--slate)}
.nk-pim-cat-leaf{color:var(--ink);font-weight:700}
.nk-pim-cat-sep{color:var(--muted);margin:0 6px}

/* ========================================================================
   ATTRIBUTS & DÉCLINAISONS (Admin ▸ Données métier)
   ------------------------------------------------------------------------
   Écran `#/admin/attributs` : les groupes d'attributs de nocika (Taille,
   Couleur…) et leurs valeurs, lus et réécrits dans PrestaShop.

   ⚠️ La COULEUR d'une valeur est une DONNÉE, pas un choix de style : elle
   vient de la boutique et change d'une valeur à l'autre. Aucune classe ne
   peut la porter. Elle transite donc par la propriété personnalisée
   `--nk-attr-color`, posée par le script sur l'élément ; TOUTE la mise en
   forme (taille, rayon, filet, damier du vide) reste ici. C'est la seule
   forme d'écriture de style admise par la règle d'or pour une valeur
   pilotée par la donnée — et elle est bornée à cette propriété.
   ======================================================================== */

/* Pastille de couleur d'une valeur d'attribut. */
.nk-attr-swatch {
  display: inline-block; width: 18px; height: 18px; flex-shrink: 0;
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  background: var(--nk-attr-color, transparent); vertical-align: middle;
}
/* Absence de couleur : damier discret, pour ne pas la confondre avec du blanc
   — `#ffffff` est une couleur légitime et fréquente sur ces valeurs. */
.nk-attr-swatch--vide {
  background:
    linear-gradient(45deg, var(--border) 25%, transparent 25%, transparent 75%, var(--border) 75%) 0 0 / 8px 8px,
    linear-gradient(45deg, var(--border) 25%, transparent 25%, transparent 75%, var(--border) 75%) 4px 4px / 8px 8px,
    var(--white);
}
/* Pastille agrandie de l'éditeur, à côté du sélecteur natif. */
.nk-attr-swatch--lg { width: 30px; height: 30px; border-radius: var(--r-md); }

/* Code hexadécimal en regard de la pastille. */
.nk-attr-hex { font-family: Menlo, Consolas, monospace; font-size: var(--fs-sm); color: var(--slate); }

/* Cellule couleur : pastille + code sur une ligne. */
.nk-attr-couleur { display: inline-flex; align-items: center; gap: 8px; }


/* Encart TEXTURE — état, pas action : le Webservice PrestaShop n'expose pas la
   texture (fichier `img/co/*.jpg`), elle se gère au back-office. L'encart le
   dit et n'offre aucun contrôle : un champ grisé se lirait comme une panne. */
.nk-attr-texture {
  display: flex; align-items: flex-start; gap: 8px;
  padding: var(--space-sm) var(--space-md);
  border: 1px dashed var(--border-strong); border-radius: var(--r-md);
  background: var(--surface); color: var(--ink); font-size: var(--fs-base);
}
.nk-attr-texture__icon { flex-shrink: 0; color: var(--muted); margin-top: 1px; }
.nk-attr-texture__texte { flex: 1; line-height: 1.5; }

/* Avertissement « données partagées » de la barre de contexte : renommer une
   valeur la renomme sur toutes les fiches qui la portent. */
.nk-attr-partage {
  display: flex; align-items: center; gap: 8px;
  padding: var(--space-sm) var(--space-md); margin-bottom: var(--space-lg);
  border: 1px solid var(--warning-b); border-radius: var(--r-md);
  background: var(--warning-l); color: var(--ink); font-size: var(--fs-base);
}
.nk-attr-partage__icon { flex-shrink: 0; color: var(--warning); }

/* Grille de l'éditeur de groupe : le formulaire à gauche, le rappel à droite. */
.nk-attr-editeur { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: var(--space-lg); align-items: start; }
@media (max-width: 900px) { .nk-attr-editeur { grid-template-columns: minmax(0, 1fr); } }


/* Ligne « aperçu » de l'éditeur de couleur : grande pastille + saisies. */
.nk-attr-couleur-editeur { display: flex; align-items: center; gap: var(--space-md); }

/* Sélecteur de couleur natif de l'éditeur : borné en largeur, sinon il occupe
   toute la ligne et écrase la saisie hexadécimale à côté. */
.nk-attr-picker { width: 52px; padding: 2px; cursor: pointer; }


/* Valeur d'un champ en lecture, avec son globe de traduction en fin de ligne :
   même gabarit que `.nk-attr-couleur`, nommé pour ce qu'il porte. */
.nk-attr-champ { display: inline-flex; align-items: center; gap: 8px; }

/* ── Modale « Traduire toute la fiche » (page Rédaction) ──────────────────
   Une grille blocs × langues : les cases sont le contenu, pas la décoration.
   La barre de raccourcis et le décompte vivent au-dessus du tableau. */
.nk-trad-fiche__barre {
  display: flex; align-items: center; gap: var(--space-sm);
  margin: 0 0 var(--space-md);
}
.nk-trad-fiche__compte { font-size: var(--fs-base); color: var(--slate); }
/* Case de croisement : cible de clic confortable, la case restant centrée. */
.nk-trad-fiche__cell {
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  padding: 2px 6px; cursor: pointer;
}
/* En-tête de colonne : la pastille de langue COCHE toute sa colonne. */
.nk-trad-fiche__col { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
/* Coche discrète d'un croisement DÉJÀ traduit — elle explique pourquoi la case
   est décochée, sans prétendre être un état cliquable. */
.nk-trad-fiche__fait { color: var(--success); font-weight: 700; font-size: var(--fs-sm); }

/* ========================================================================
   APERÇU ÉDITORIAL MULTILINGUE (fiche PIM ▸ Aperçu)
   ------------------------------------------------------------------------
   Lire une fiche ENTIÈRE dans une langue, comme le client la lira, puis la
   valider ou la corriger. C'est la lecture qui manquait pour que
   « Valider cette langue » — un geste qui estampille toute la fiche d'un
   coup — soit honnête.

   ⚠️ Cette section EXISTE parce que l'aperçu construisait toute sa mise en
   page en `style=` inline (fond, rayons, tailles, couleurs), au mépris de la
   règle d'or. Le passage au multilingue aurait propagé cette dette dans du
   code neuf : elle est rapatriée ici (décision Lionel du 2026-08-23).

   L'aperçu SIMULE une page boutique : c'est le seul endroit du Hub où des
   couleurs de rendu externe (le bleu de lien Google, le vert de son URL) sont
   légitimes. Elles sont nommées ici plutôt que dispersées dans le script.
   ======================================================================== */

/* Cadre : fond neutre, page centrée — on regarde une page, pas un écran. */
.nk-pim-apercu        { background: var(--surface); border-radius: var(--r-sm); padding: 24px 20px; }
.nk-pim-apercu__page  { max-width: 960px; margin: 0 auto; }

/* Mode PLEIN ÉCRAN : la fiche occupe la fenêtre, plus rien autour d'elle ne
   distrait. Sous `--z-overlay` : c'est une surface de lecture, pas une modale
   — une vraie modale (l'éditeur d'un bloc) doit pouvoir s'ouvrir PAR-DESSUS. */
.nk-pim-apercu--plein {
  position: fixed; inset: 0; z-index: var(--z-overlay);
  overflow-y: auto; border-radius: 0; padding: 0;
}
.nk-pim-apercu--plein .nk-pim-apercu__page { padding: 20px 24px 96px; }

/* ── COQUE À DEUX COLONNES : sommaire ancré + document ────────────────────
   Le sommaire ne défile pas avec le document : c'est la raison d'être du plein
   écran permanent (2026-08-24). Sous 1000 px il repasse en ligne, au-dessus du
   document — un rail de 230 px y prendrait un quart de la largeur utile. */
.nk-pim-apercu__shell {
  display: grid; grid-template-columns: 230px minmax(0, 1fr); align-items: start;
}
.nk-pim-apercu__rail {
  position: sticky; top: 62px; padding: 20px 12px 40px;
  max-height: calc(100vh - 62px); overflow: auto;
}
.nk-pim-apercu__rail-t {
  font-size: var(--fs-sm); letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted); padding: 0 10px 8px;
}
.nk-pim-apercu__rail-l {
  display: flex; align-items: center; gap: 10px; padding: 7px 10px;
  border-radius: var(--r-sm); color: var(--slate); text-decoration: none;
  font-size: 13px; font-weight: var(--fw-semibold);
  border-left: 2px solid transparent;
}
.nk-pim-apercu__rail-l:hover { background: var(--white); color: var(--ink); }
.nk-pim-apercu__rail-l--on {
  background: var(--white); color: var(--ink);
  border-left-color: var(--cyan); font-weight: 700;
}
.nk-pim-apercu__rail-n { font-size: var(--fs-sm); color: var(--muted); flex: 0 0 auto; }
.nk-pim-apercu__rail-l--on .nk-pim-apercu__rail-n { color: var(--cyan); }

/* La zone de lecture, ÉLARGIE (demande Lionel, 2026-08-24) : 960 → 1160. Il y a
   la place, et le confort de lecture prime ici — c'est le seul écran du Hub
   qu'on parcourt comme un document. */
.nk-pim-apercu--plein .nk-pim-apercu__page { max-width: 1160px; }

/* Une SECTION visée par le sommaire ne doit pas se retrouver sous la barre
   ancrée quand on l'atteint. */
.nk-pim-apercu__carte,
.nk-pim-apercu__tete,
.nk-pim-apercu__serp { scroll-margin-top: 74px; }

/* ── CHAMPS (libellé, slug) ──────────────────────────────────────────────── */
.nk-pim-apercu__champ {
  display: grid; grid-template-columns: 136px minmax(0, 1fr); gap: 18px;
  padding: 13px 0; border-bottom: 1px solid var(--border); align-items: start;
}
.nk-pim-apercu__champ:first-child { padding-top: 0; }
.nk-pim-apercu__champ:last-child  { border-bottom: 0; padding-bottom: 0; }
.nk-pim-apercu__champ-k { font-size: 13px; font-weight: var(--fw-semibold); color: var(--muted); }
.nk-pim-apercu__champ-v { font-size: 15px; min-width: 0; word-break: break-word; }
.nk-pim-apercu__champ-nom { font-size: 18px; font-weight: 700; color: var(--navy); }
.nk-pim-apercu__champ-src { display: block; font-size: 13px; color: var(--muted); margin-top: 6px; }
.nk-pim-apercu__champ-vide { font-size: 15px; color: var(--danger); font-style: italic; }
.nk-pim-apercu__slug {
  font-size: 13px; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 6px 10px; display: inline-block; word-break: break-all;
}

/* ── TEXTES ALT ──────────────────────────────────────────────────────────── */
.nk-pim-apercu__alts {
  display: flex; flex-direction: column; gap: 1px; background: var(--border);
  border: 1px solid var(--border); border-radius: var(--r-sm); overflow: hidden;
}
.nk-pim-apercu__alt {
  background: var(--white); display: grid;
  grid-template-columns: 76px minmax(0, 1fr) auto; gap: 18px;
  padding: 12px 14px; align-items: center;
}
.nk-pim-apercu__alt-vig {
  width: 76px; height: 57px; border-radius: var(--r-sm); object-fit: cover;
  background: var(--surface); display: block;
}
.nk-pim-apercu__alt-txt { font-size: 14px; min-width: 0; color: var(--ink); }
.nk-pim-apercu__alt-src { display: block; font-size: 13px; color: var(--muted); margin-top: 3px; }
.nk-pim-apercu__alt-fic { font-size: var(--fs-sm); color: var(--muted); white-space: nowrap; }
/* Un ALT manquant se voit à sa PLACE dans la liste, pas dans un total au pied —
   un compteur ne dirait pas LAQUELLE des images est concernée. */
.nk-pim-apercu__alt--manque .nk-pim-apercu__alt-txt { color: var(--danger); font-style: italic; }

/* ── LISTE DE LA CONFIRMATION DE PUBLICATION ─────────────────────────────── */
.nk-pim-apercu__will { margin: 0 0 12px; padding-left: 18px; font-size: 15px; color: var(--ink); }
.nk-pim-apercu__will li { margin-bottom: 6px; }
.nk-pim-apercu__will--warn { color: var(--danger); font-weight: var(--fw-semibold); }

/* ── RAPPELS CLAVIER, barre basse fixe ───────────────────────────────────── */
.nk-pim-apercu__pied-kb {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 1;
  display: flex; align-items: center; gap: 18px;
  padding: 8px 24px; background: var(--white); border-top: 1px solid var(--border);
  font-size: 13px; color: var(--muted);
}
.nk-pim-apercu__kb { display: inline-flex; align-items: center; gap: 6px; }
.nk-pim-apercu__k {
  font-size: var(--fs-sm); border: 1px solid var(--border-d); border-bottom-width: 2px;
  border-radius: 4px; padding: 1px 5px; color: var(--slate); background: var(--surface);
}

@media (max-width: 1000px) {
  .nk-pim-apercu__shell { grid-template-columns: 1fr; }
  .nk-pim-apercu__rail {
    position: static; max-height: none; padding: 12px 20px 0;
  }
  .nk-pim-apercu__rail nav { display: flex; flex-wrap: wrap; gap: 6px; }
  .nk-pim-apercu__rail-l { border-left: 0; border: 1px solid var(--border); }
  .nk-pim-apercu__champ { grid-template-columns: 1fr; gap: 5px; }
  .nk-pim-apercu__alt { grid-template-columns: 60px minmax(0, 1fr); }
  .nk-pim-apercu__alt-fic { display: none; }
  /* Les rappels clavier disparaissent : le clavier n'est pas le geste
     principal sur cette largeur, et la barre mangerait le pied du document. */
  .nk-pim-apercu__pied-kb { display: none; }
}

/* Barre de tête : sélecteur de langue, comparaison au français, publication et
   fermeture. (Elle a porté le titre de la fiche et la bascule plein écran
   jusqu'au 2026-08-24 ; elle ne rend plus ni l'un ni l'autre.)
   `position: relative` : le bouton de sortie s'y ancre (cf. plus bas). */
.nk-pim-apercu__barre {
  position: relative;
  display: flex; align-items: center; gap: var(--space-md);
  padding: 10px 14px; background: var(--white);
  border: 1px solid var(--border); border-radius: var(--r-md);
  margin-bottom: var(--space-md);
}
/* CADRE INTERNE — il aligne la barre sur la COLONNE DE LECTURE (demande
   Lionel, 2026-08-25). Les drapeaux tombent sur le bord gauche des cartes, les
   actions sur leur bord droit, et « Valider et publier » se retrouve à l'aplomb
   de son jumeau du pied. En mode non plein écran il ne fait rien : la barre y
   est déjà à la largeur de la page. */
.nk-pim-apercu__barre-in {
  display: flex; align-items: center; gap: var(--space-md);
  flex: 1; min-width: 0;
}
/* La sortie ne se comprime jamais : c'est le seul chemin hors du plein écran,
   un libellé replié sur deux lignes le ferait passer pour un rappel. */
.nk-pim-apercu__sortir { flex: 0 0 auto; white-space: nowrap; }
/* ⚠️ LA BARRE EST SORTIE DE `__page` le 2026-08-24 : elle coiffe désormais la
   coque à deux colonnes, donc plus de marges négatives à compenser — elles
   étaient calculées sur un parent qui n'existe plus.

   ⚠️ LE PADDING SEUL NE SUFFISAIT PAS, et c'est le défaut corrigé le
   2026-08-25. Il valait `230px + 24px`, ce qui alignait les pastilles sur le
   bord des cartes UNIQUEMENT tant que la colonne de droite mesurait exactement
   1160 px. Or `__page` est CENTRÉE dans cette colonne : au-delà de 1390 px de
   fenêtre, son bord gauche recule d'une demi-gouttière et la barre restait,
   elle, collée au sommaire. Les drapeaux flottaient loin à gauche des cartes,
   les actions loin à leur droite.

   La barre ne réserve donc plus que le RAIL (230 px) ; c'est `__barre-in` qui
   porte la géométrie de `__page` — mêmes largeur maximale, centrage et retrait,
   déclarés une seule fois de chaque côté. */
.nk-pim-apercu--plein .nk-pim-apercu__barre {
  position: sticky; top: 0; z-index: 2; border-radius: 0;
  border-left: 0; border-right: 0; border-top: 0; margin: 0;
  padding-left: 230px; padding-right: 0;
}
.nk-pim-apercu--plein .nk-pim-apercu__barre-in {
  max-width: 1160px; margin: 0 auto; padding: 0 24px;
}
/* Bouton de SORTIE : ancré dans la gouttière du sommaire — 230 px garantis,
   vides dans la barre — et HORS DU FLUX, sans quoi il décalerait le cadre
   interne et ruinerait l'alignement qu'on vient d'établir. À droite, ce serait
   impossible : la gouttière y est calculée et tombe à zéro sous 1390 px. */
.nk-pim-apercu--plein .nk-pim-apercu__sortir {
  position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
}
@media (max-width: 1000px) {
  /* Plus de rail à côté : la barre n'a plus rien à réserver, et c'est
     `__barre-in` qui garde le retrait — les MÊMES 24 px que `__page`, sinon la
     barre et les cartes se décalent de 4 px, exactement le défaut que ce lot
     corrige au-dessus de 1000 px (relevé en revue à froid le 2026-08-25).
     Le bouton de sortie revient dans le flux, faute de gouttière où l'ancrer. */
  .nk-pim-apercu--plein .nk-pim-apercu__barre { padding-left: 0; padding-right: 0; }
  .nk-pim-apercu--plein .nk-pim-apercu__barre-in { padding: 0 24px; }
  .nk-pim-apercu--plein .nk-pim-apercu__sortir { position: static; transform: none; }
}

/* Rangée des langues : les pastilles disent AUSSI l'avancement. */
.nk-pim-apercu__langues { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.nk-pim-apercu__langues-label { font-size: var(--fs-sm); color: var(--muted); margin-right: 2px; }

/* En-tête produit — ce que le client voit en premier. */
.nk-pim-apercu__tete {
  background: var(--white); border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 24px 28px; margin-bottom: var(--space-md);
}
.nk-pim-apercu__marque {
  font-size: var(--fs-sm); color: var(--slate);
  text-transform: uppercase; letter-spacing: .06em; margin-bottom: 4px;
}
.nk-pim-apercu__titre { font-size: 22px; font-weight: 800; color: var(--navy); margin: 0 0 6px; line-height: 1.3; }
.nk-pim-apercu__refs  { font-size: var(--fs-sm); color: var(--border-d); margin-bottom: 12px; }
.nk-pim-apercu__prix  { display: flex; align-items: baseline; gap: 6px; margin-bottom: 12px; }
.nk-pim-apercu__prix-int { font-size: 28px; font-weight: 800; color: var(--danger); }
.nk-pim-apercu__prix-dev { font-size: 16px; font-weight: 800; color: var(--danger); }
.nk-pim-apercu__prix-dec { font-size: var(--fs-sm); vertical-align: super; }
.nk-pim-apercu__stock { font-size: var(--fs-sm); color: var(--success); font-weight: var(--fw-semibold); margin-left: 8px; }
/* ⚠️ 13 → 17 px le 2026-08-24 (demande Lionel), et `--muted` → `--ink`.
   Ce n'est pas un réglage esthétique : c'est le SEUL écran du Hub qu'on lit
   comme un document, du début à la fin, pour juger un texte. À 13 px en gris,
   on survolait — or survoler est exactement ce que cet écran existe pour
   empêcher, puisqu'on publie au bout. La distinction « texte de boutique » /
   « interface » est portée par la taille, l'interligne et la largeur de
   colonne, faute de police serif dans le Hub. */
.nk-pim-apercu__chapo {
  font-size: 17px; color: var(--ink); line-height: 1.62;
  border-top: 1px solid var(--bg); padding-top: 12px;
}

/* Bloc de contenu. L'en-tête porte le titre, le stylo et le chevron. */
.nk-pim-apercu__carte {
  background: var(--white); border: 1px solid var(--border); border-radius: var(--r-md);
  margin-bottom: var(--space-md); overflow: hidden;
}
.nk-pim-apercu__hd {
  display: flex; align-items: center; gap: 8px;
  padding: 16px 28px; cursor: pointer; user-select: none;
}
.nk-pim-apercu__hd-titre { flex: 1; font-size: 15px; font-weight: 700; color: var(--navy); margin: 0; }
.nk-pim-apercu__chevron { color: var(--border-d); transition: transform .2s; flex-shrink: 0; }
.nk-pim-apercu__chevron--plie { transform: rotate(-90deg); }
.nk-pim-apercu__corps { padding: 0 28px 18px; font-size: 17px; color: var(--ink); line-height: 1.62; }
.nk-pim-apercu__corps--plie { display: none; }
/* Avis de l equipe : un filet, comme sur la boutique. */
.nk-pim-apercu__citation {
  margin: 0; padding: 0 0 0 16px; border-left: 3px solid var(--cyan);
  font-style: italic; line-height: 1.7;
}

/* TROU de traduction : un bloc écrit en français, absent dans cette langue.
   Il est MONTRÉ et non sauté — sauter reviendrait à faire valider une fiche
   dont la FAQ manque sans que personne ne le voie. */
.nk-pim-apercu__trou {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 16px; margin-bottom: var(--space-md);
  border: 1px dashed var(--danger-b); border-radius: var(--r-md);
  background: var(--danger-l);
}
.nk-pim-apercu__trou-txt   { flex: 1; }
/* Deux LIGNES, pas deux fragments : sans `display:block` le nom du bloc et
   son explication se collaient (« … rien dans cette langueÉcrit en français »). */
.nk-pim-apercu__trou-titre { display: block; font-size: 13px; color: var(--danger); font-weight: var(--fw-semibold); }
.nk-pim-apercu__trou-sous  { display: block; font-size: var(--fs-sm); color: var(--slate); }

/* Lecture EN REGARD du français, à la demande. Deux colonnes, la source à
   gauche et effacée : c'est la langue lue qui compte, la source n'est qu'un
   recours. */
.nk-pim-apercu__duo { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-lg); }
.nk-pim-apercu__duo-src { color: var(--slate); }
.nk-pim-apercu__duo-tag {
  display: block; font-size: var(--fs-sm); font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase; color: var(--border-d); margin-bottom: 6px;
}
@media (max-width: 900px) { .nk-pim-apercu__duo { grid-template-columns: 1fr; } }

/* Aperçu Google — couleurs du moteur, pas du Hub : les imiter est le but. */
.nk-pim-apercu__serp {
  background: var(--white); border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 20px 28px;
}
.nk-pim-apercu__serp-hd {
  font-size: var(--fs-sm); font-weight: 700; color: var(--border-d);
  text-transform: uppercase; letter-spacing: .04em; margin-bottom: 10px;
}
.nk-pim-apercu__serp-corps { max-width: 600px; }
.nk-pim-apercu__serp-titre { font-size: 18px; color: #1a0dab; line-height: 1.3; margin-bottom: 2px; }
.nk-pim-apercu__serp-url   { font-size: 12px; color: #006621; margin-bottom: 2px; }
.nk-pim-apercu__serp-desc  { font-size: 13px; color: #545454; line-height: 1.4; }

/* Les cinq classes de VIDÉO et le `.nk-pim-apercu__nom` ont été retirés le
   2026-08-23 : la réécriture de l'aperçu multilingue a cessé de rendre les
   vidéos et les champs libres, laissant ces styles sans aucun émetteur.
   ⚠️ Ce n'est PAS une simple suppression de code mort : ces deux familles de
   blocs ne sont pas traduisibles (elles ne figurent pas dans les dix types de
   `pim_editorial_bloc_labels`), et l'écran l'AVOUE désormais dans son pied —
   « Non montré ici, et jamais traduit : 1 champ libre ». Le style avait été
   écrit, le markup jamais : relevé en revue à froid. */

/* Pied COLLANT : ce qu'on vient de lire, et le geste qui le valide. */
.nk-pim-apercu__pied {
  display: flex; align-items: center; gap: var(--space-md);
  padding: 12px 16px; background: var(--white);
  border: 1px solid var(--border-strong); border-radius: var(--r-md);
  margin-top: var(--space-lg);
}
/* ⚠️ `bottom` DÉCALÉ DE LA HAUTEUR DE LA BARRE DE RACCOURCIS, et `z-index`
   au-dessus d'elle (2026-08-25). `.nk-pim-apercu__pied-kb` est `fixed;
   bottom: 0; z-index: 1` ; ce pied est `sticky; bottom: 0` sans z-index, donc
   dans le MÊME contexte d'empilement il passait DESSOUS. Tant que le pied
   n'était qu'un bilan textuel, la superposition ne coûtait que de la lisibilité
   en fin de page ; depuis qu'il porte « Valider et publier », elle masquait
   l'essentiel du bouton pendant toute la lecture. Relevé en revue à froid.

   `bottom: 40px` : les 8+8 px de padding de la barre de raccourcis, sa bordure
   et son `kbd`. Le décalage plutôt que le seul z-index, pour que le pied ne
   RECOUVRE pas les raccourcis à son tour. */
.nk-pim-apercu--plein .nk-pim-apercu__pied {
  position: sticky; bottom: 40px; z-index: 2; border-radius: 0;
  border-left: 0; border-right: 0; border-bottom: 0;
  margin: var(--space-lg) -24px 0; padding-left: 24px; padding-right: 24px;
}
@media (max-width: 1000px) {
  /* La barre de raccourcis est masquée sous 1000 px : plus rien à éviter. */
  .nk-pim-apercu--plein .nk-pim-apercu__pied { bottom: 0; }
}
.nk-pim-apercu__bilan { font-size: 13px; color: var(--slate); }

/* Marque « ce texte est encore en français » posée après un titre : le libellé
   produit DOIT s'afficher (c'est le h1), mais le montrer en français sans rien
   dire masquerait exactement le défaut que l'écran sert à repérer. */
.nk-pim-apercu__non-trad {
  display: inline-block; margin-left: 10px; padding: 2px 8px; vertical-align: middle;
  border: 1px solid var(--danger-b); border-radius: var(--r-pill);
  background: var(--danger-l); color: var(--danger);
  font-size: var(--fs-sm); font-weight: var(--fw-semibold);
}

/* ========================================================================
   SUIVI DE COMPLÉTUDE (fiche PIM) — bouton de menu + panneau ancré
   ------------------------------------------------------------------------
   Le suivi vivait dans le `nk-page-header` de CHAQUE section, en styles
   inline. Il devient un bouton de sidebar, en tête du menu, qui ouvre un
   panneau ancré à sa droite (demande Lionel du 2026-08-23).

   Trois gains, dans l'ordre où ils comptent :
     - l'en-tête de section est LIBÉRÉ ;
     - les CINQ sections qui n'appellent pas `sectionHeader` (Traduction,
       Composants, Groupement, Caractéristiques & filtres, Liens) n'avaient
       AUCUN suivi : il devient uniforme ;
     - la mise en forme quitte les attributs `style` pour cette feuille.

   ⚠️ Ce panneau se ferme au CLIC EXTÉRIEUR, ce que la règle des modales
   interdit. L'exception est assumée et nommée (CLAUDE.md § Modales) : cette
   règle protège d'une PERTE DE SAISIE, et ce panneau ne contient aucun champ
   — c'est une liste de liens en lecture. Une modale à formulaire garde, elle,
   `backdropClose:false`.
   ======================================================================== */

/* ── Le bouton, en tête de menu ─────────────────────────────────────────── */

/* Jauge posée APRÈS le rendu du menu, dans l'en-tête d'accès direct. Elle ne
   peut pas vivre dans le libellé : `NkPageShell` l'échappe, et surtout un
   libellé qui change à chaque état ferait réarracher toute la sidebar (sa
   signature est un `JSON.stringify` des groupes). */
.nk-pim-compl-btn { display: flex; align-items: center; gap: 10px; }
.nk-pim-compl-jauge { display: inline-flex; align-items: center; gap: 8px; flex-shrink: 0; }
.nk-pim-compl-jauge__ring { width: 30px; height: 30px; flex-shrink: 0; transform: rotate(-90deg); }
/* L'AVANCEMENT est une donnée : il transite par `--nk-compl-dash`, posée par
   le script. Toute la forme (rayon, épaisseur, couleurs) reste ici. */
.nk-pim-compl-jauge__piste { fill: none; stroke: var(--border); stroke-width: 3.5; }
.nk-pim-compl-jauge__arc {
  fill: none; stroke-width: 3.5; stroke-linecap: round;
  stroke: var(--warning); stroke-dasharray: var(--nk-compl-dash, 0 100);
  transition: stroke-dasharray .3s ease;
}
.nk-pim-compl-jauge--ok .nk-pim-compl-jauge__arc { stroke: var(--success); }
.nk-pim-compl-jauge__chiffre {
  font-size: var(--fs-sm); font-weight: 700; fill: var(--slate);
  text-anchor: middle; transform: rotate(90deg); transform-origin: center;
}
.nk-pim-compl-btn__txt  { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.nk-pim-compl-btn__sous { font-size: var(--fs-sm); font-weight: 400; color: var(--warning); }
.nk-pim-compl-btn__sous--ok { color: var(--success); }

/* ── Le panneau ─────────────────────────────────────────────────────────── */

/* `--z-drawer` (500) et non `--z-modal` : une VRAIE modale — l'éditeur d'un
   bloc, la checklist de validation — doit pouvoir s'ouvrir PAR-DESSUS. */
.nk-pim-compl {
  position: fixed; z-index: var(--z-drawer);
  width: 380px; max-height: 78vh; display: flex; flex-direction: column;
  background: var(--white); border: 1px solid var(--border-strong);
  border-radius: var(--r-md); box-shadow: 0 10px 34px rgba(15, 23, 42, .16);
}
.nk-pim-compl__head {
  display: flex; align-items: flex-start; gap: var(--space-md);
  padding: 14px 16px 10px; border-bottom: 1px solid var(--border);
}
.nk-pim-compl__titre { flex: 1; font-size: 15px; font-weight: 700; color: var(--ink); line-height: 1.3; }
.nk-pim-compl__sous  { display: block; font-size: 13px; font-weight: 400; color: var(--slate); margin-top: 2px; }
.nk-pim-compl__body  { flex: 1; overflow-y: auto; padding: 12px 16px; min-height: 0; }
.nk-pim-compl__grp {
  font-size: var(--fs-sm); font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); margin: 4px 0 8px;
}
.nk-pim-compl__grp + .nk-pim-compl__grp,
.nk-pim-compl__carte + .nk-pim-compl__grp,
.nk-pim-compl__ligne + .nk-pim-compl__grp { margin-top: var(--space-lg); }

/* À TRAITER : une carte par contrôle, avec ce qui manque et où le faire. */
.nk-pim-compl__carte {
  border: 1px solid var(--warning-b); border-radius: var(--r-md);
  background: var(--warning-l); padding: 10px 12px; margin-bottom: 10px;
}
.nk-pim-compl__carte--bloc { border-color: var(--danger-b); background: var(--danger-l); }
.nk-pim-compl__carte-hd { display: flex; align-items: center; gap: 8px; }
.nk-pim-compl__carte-nom { flex: 1; font-size: 13px; font-weight: 700; color: var(--ink); }
.nk-pim-compl__carte-sous { display: block; font-size: 13px; color: var(--slate); margin: 2px 0 6px; line-height: 1.45; }
.nk-pim-compl__carte-lien {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 13px; font-weight: var(--fw-semibold); color: var(--warning);
  background: none; border: 0; padding: 0; cursor: pointer;
}
.nk-pim-compl__carte-lien:hover { text-decoration: underline; }
.nk-pim-compl__carte--bloc .nk-pim-compl__carte-lien { color: var(--danger); }
.nk-pim-compl__ico--todo { color: var(--warning); flex-shrink: 0; }
.nk-pim-compl__carte--bloc .nk-pim-compl__ico--todo { color: var(--danger); }

/* VALIDÉ : une ligne par contrôle, sa valeur à droite. Cliquable — on relit
   souvent ce qui est déjà bon. */
.nk-pim-compl__ligne {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 7px 8px; border: 0; background: none; border-radius: var(--r-sm);
  text-align: left; cursor: pointer;
}
.nk-pim-compl__ligne:hover { background: var(--surface); }
.nk-pim-compl__ligne-nom { flex: 1; font-size: 13px; color: var(--ink); }
.nk-pim-compl__ligne-val { font-size: var(--fs-sm); color: var(--muted); text-align: right; }
.nk-pim-compl__ico--ok { color: var(--success); flex-shrink: 0; }
/* Contrôle purement INFORMATIF (Données de référence) : ni vert ni orange —
   il n'entre pas dans les minimums, le dire par la couleur évite de faire
   croire à un blocage. */
.nk-pim-compl__ico--info { color: var(--muted); flex-shrink: 0; }

.nk-pim-compl__foot {
  padding: 12px 16px; border-top: 1px solid var(--border);
  background: var(--surface); border-radius: 0 0 var(--r-md) var(--r-md);
}
.nk-pim-compl__foot .nk-btn { width: 100%; justify-content: center; }
.nk-pim-compl__note {
  display: block; margin-top: 8px; font-size: var(--fs-sm);
  color: var(--muted); text-align: center; line-height: 1.4;
}

/* ══════════════════════════════════════════════════════════════════════════
   PILOTE DE TRADUCTION (panneau du menu de la fiche PIM) — 2026-08-23
   ══════════════════════════════════════════════════════════════════════════
   Deuxième panneau ancré du menu de gauche, sur la mécanique du suivi de
   complétude ci-dessus. Il a remplacé la PAGE « Traduction ».

   POURQUOI IL EST PLUS LARGE (560 px contre 380). La langue est une LIGNE :
   drapeau, nom, jauge, compteur, « Relire », « Valider » et l'exclusion sur un
   seul rang. À 380 px il faudrait les empiler sur trois rangs, et chaque langue
   redeviendrait une carte — la chose même que cette refonte enlève. Le suivi de
   complétude, lui, reste étroit : ses lignes sont du texte court.

   ⚠️ Le tableau du repli « Détail par surface » tient jusqu'à 4 langues cibles ;
   au-delà il défile horizontalement dans son `nk-table-wrap`, jamais la page. */

/* ── Le bouton de menu ──────────────────────────────────────────────────── */

.nk-pim-trad-btn { display: flex; align-items: center; gap: 10px; }
.nk-pim-trad-jauge { display: inline-flex; align-items: center; gap: 8px; flex-shrink: 0; }
.nk-pim-trad-jauge__ring { width: 30px; height: 30px; flex-shrink: 0; transform: rotate(-90deg); }
/* L'AVANCEMENT est une donnée : il transite par `--nk-trad-dash`, posée par le
   script. Toute la forme (rayon, épaisseur, couleurs) reste ici. */
.nk-pim-trad-jauge__piste { fill: none; stroke: var(--border); stroke-width: 3.5; }
.nk-pim-trad-jauge__arc {
  fill: none; stroke-width: 3.5; stroke-linecap: round;
  stroke: var(--trad-ia); stroke-dasharray: var(--nk-trad-dash, 0 100);
  transition: stroke-dasharray .3s ease;
}
.nk-pim-trad-jauge--ok .nk-pim-trad-jauge__arc { stroke: var(--success); }
.nk-pim-trad-jauge__chiffre {
  font-size: var(--fs-sm); font-weight: 700; fill: var(--slate);
  text-anchor: middle; transform: rotate(90deg); transform-origin: center;
}
.nk-pim-trad-btn__txt  { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.nk-pim-trad-btn__sous { font-size: var(--fs-sm); font-weight: 400; color: var(--muted); }
/* Framboise et non orange : ce sous-titre compte des LANGUES à finir, pas des
   contrôles à corriger. La complétude garde l'orange, qui dit « il manque » ;
   la traduction porte la couleur de son domaine. */
.nk-pim-trad-btn__sous--todo { color: var(--trad-ia); }
.nk-pim-trad-btn__sous--ok   { color: var(--success); }

/* ── Le panneau ─────────────────────────────────────────────────────────── */

/* 560 → 720 → 880 → 1040 le 2026-08-24 : la ligne de langue porte désormais
   l'état de CHAQUE surface en pastilles, là où elle ne portait qu'un ratio, et
   les pastilles ont ensuite reçu leurs libellés COMPLETS (« Meta description »
   et non « Meta D. ») — une abréviation qu'il faut décoder n'est pas une
   simplification. Ces six libellés demandent ~610 px à eux seuls ; en dessous
   ils repassent sur deux rangs et une langue redevient une CARTE, exactement ce
   que le passage en panneau avait supprimé. D'où la largeur, calculée sur le
   contenu et non choisie : identité + pastilles + « Valider » + interrupteur.

   `max-width` : un panneau plus large que la fenêtre serait rogné sans qu'on
   sache par quel bout. L'ancrage le recale déjà à gauche (`positionnerPanneau`),
   mais rien ne bornait sa LARGEUR — sur un portable en 1280, 1040 px passe ;
   en dessous, c'est la borne qui rend la main au repli sur deux rangs. */
.nk-pim-trad {
  position: fixed; z-index: var(--z-drawer);
  width: 1040px; max-width: calc(100vw - 24px);
  max-height: 78vh; display: flex; flex-direction: column;
  background: var(--white); border: 1px solid var(--border-strong);
  border-radius: var(--r-md); box-shadow: 0 10px 34px rgba(15, 23, 42, .16);
}
.nk-pim-trad__head {
  display: flex; align-items: flex-start; gap: var(--space-sm);
  padding: 14px 16px 10px; border-bottom: 1px solid var(--border);
}
.nk-pim-trad__titre { flex: 1; font-size: 15px; font-weight: 700; color: var(--ink); line-height: 1.3; }
.nk-pim-trad__sous  { display: block; font-size: 13px; font-weight: 400; color: var(--slate); margin-top: 2px; }
.nk-pim-trad__body  { flex: 1; overflow-y: auto; padding: 12px 16px; min-height: 0; }

/* ── La ligne de langue : UN rang, quatre colonnes ──────────────────────── */

.nk-pim-trad__lg {
  display: grid; grid-template-columns: 130px minmax(70px, 1fr) auto;
  align-items: center; gap: 12px;
  padding: 9px 10px; margin-bottom: 5px;
  border: 1px solid var(--border); border-radius: var(--r-sm);
}

/* ── Les PASTILLES d'état, une par surface (2026-08-24, demande Lionel) ────
   Elles remplacent la jauge + le ratio de la ligne ET le repli « Détail par
   surface », qui disait la même chose ailleurs et repliée. Le TEXTE est DANS
   la pastille : un rang d'en-têtes au-dessus des colonnes coûtait une hauteur
   de ligne au panneau et ne servait qu'une fois.

   Ce sont des BOUTONS : une pastille rouge est ce qu'il faut aller corriger,
   la rendre cliquable supprime le détour par « Relire ». */
.nk-pim-trad__past { display: flex; flex-wrap: wrap; gap: 4px; min-width: 0; }
.nk-pim-trad__pst {
  flex: 0 0 auto; display: inline-flex; align-items: center;
  padding: 2px 7px; border-radius: var(--r-pill);
  border: 1px solid transparent; background: var(--surface);
  font-family: var(--font); font-size: var(--fs-sm); font-weight: 600;
  color: var(--slate); white-space: nowrap; cursor: pointer;
  transition: filter .12s ease;
}
.nk-pim-trad__pst:hover:not(:disabled) { filter: brightness(.95); }
.nk-pim-trad__pst--ok  { background: var(--success-l); border-color: var(--success-b); color: var(--success); }
.nk-pim-trad__pst--mid { background: var(--warning-l); border-color: var(--warning-b); color: var(--warning); }
.nk-pim-trad__pst--ko  { background: var(--danger-l);  border-color: var(--danger-b);  color: var(--danger); }
/* « Sans objet » : vide en français, donc rien à traduire. Ni rouge (ce n'est
   pas un retard) ni vert (ce n'est pas fait) — neutre, et sans curseur, parce
   qu'il n'y a nulle part où aller. */
.nk-pim-trad__pst--na  { opacity: .55; cursor: default; }
.nk-pim-trad__lg:hover { background: var(--ecume-bg); }
/* Forme à TROIS colonnes : la source n'a pas de ratio de complétude — elle EST
   le dénominateur. Son mot occupe la place de la jauge.
   ⚠️ `.nk-pim-trad__lg--off` (langue exclue : fond grisé, nom barré, drapeau
   désaturé) a été RETIRÉ le 2026-08-25 avec son émetteur : l'exclusion est
   devenue inerte côté écran, plus aucune ligne ne porte cette forme. */
.nk-pim-trad__lg--src { grid-template-columns: 130px minmax(70px, 1fr) auto; }

.nk-pim-trad__lg-id  { display: flex; align-items: center; gap: 8px; min-width: 0; }
.nk-pim-trad__lg-nom { font-size: 13px; font-weight: 600; color: var(--ink); white-space: nowrap; }
.nk-pim-trad__lg-act { display: flex; gap: 6px; align-items: center; }

/* Estampille de relecture : un ÉTAT, donc fond clair et contour — jamais un
   bouton plein, qui se cliquerait. */
.nk-pim-trad__vu { white-space: nowrap; font-variant-numeric: tabular-nums; }

/* ── Les deux replis ────────────────────────────────────────────────────── */

.nk-pim-trad__pli { margin-top: 12px; border-top: 1px solid var(--border); padding-top: 10px; }
.nk-pim-trad__pli-t {
  font-size: 13px; font-weight: 600; color: var(--cyan-d);
  cursor: pointer; list-style: none; padding: 4px 0;
}
.nk-pim-trad__pli-t::-webkit-details-marker { display: none; }
.nk-pim-trad__pli-t::before { content: '▸ '; color: var(--slate); }
.nk-pim-trad__pli[open] > .nk-pim-trad__pli-t::before { content: '▾ '; }
.nk-pim-trad__pli-b { padding-top: 8px; }
/* Le repli porte des cartes entières (« Traduit ailleurs ») : elles perdent
   leur ombre et leur bord ici, le panneau en est déjà un. */
.nk-pim-trad__pli-b .nk-card { border: 0; box-shadow: none; background: transparent; }
.nk-pim-trad__pli-b .nk-card-head { padding-left: 0; padding-right: 0; }
.nk-pim-trad__pli-b .nk-card-body { padding-left: 0; padding-right: 0; }

/* ── Le pied ────────────────────────────────────────────────────────────── */

/* ⚠️ `.nk-pim-trad__foot`, `__bloc` et `__note` ont été RETIRÉS le 2026-08-25
   avec le pied du panneau : ses deux gestes de masse ont disparu et aucune de
   ces trois classes n'a plus d'émetteur. Une règle sans émetteur ne fait rien,
   et laisse croire que la forme existe encore. */
