:root {
  --bg: #f4f5f7;
  --sidebar-bg: #1b1f27;
  --sidebar-fg: #c7cbd4;
  --sidebar-fg-active: #ffffff;
  --accent: #4f6df5;
  --accent-hover: #3d59d9;
  --card-bg: #ffffff;
  --border: #e2e4e9;
  --text: #1c1f26;
  --text-muted: #6b7180;
  --success-bg: #e6f7ee;
  --success-fg: #1a7f4f;
  --error-bg: #fdecec;
  --error-fg: #b3261e;
  --radius: 10px;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
}

.auth-shell {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.auth-card {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 40px;
  width: 100%;
  max-width: 380px;
  box-shadow: 0 10px 30px rgba(20, 22, 30, 0.06);
}

/* Logo en tête des écrans hors session. Ni plaque ni fond : la carte est déjà
   blanche, contrairement au bandeau latéral sombre.

   Largeur bornée plutôt que 100 % : à pleine largeur de carte le logo ferait près de
   200 px de haut et écraserait le formulaire, qui est ce pour quoi on vient. */
.auth-brand {
  margin: 0 0 22px;
  text-align: center;
}

.auth-brand img {
  width: 100%;
  max-width: 190px;
  height: auto;
  display: inline-block;
}

.auth-card h1 {
  font-size: 1.3rem;
  margin: 0 0 6px;
}

.auth-card p.subtitle {
  color: var(--text-muted);
  margin: 0 0 24px;
  font-size: 0.9rem;
}

.app-shell {
  display: flex;
  min-height: 100vh;
}

/* Épinglée : les intitulés de groupe ont allongé la barre d'environ 140 px, et sur un
   écran de portable un administrateur (quinze entrées) pouvait voir « Déconnexion »
   passer sous la ligne de flottaison dès qu'une page défilait. La hauteur fixée à
   100vh la garde entièrement visible ; overflow-y lui donne son propre défilement dans
   le cas — rare — où elle dépasse quand même. */
.sidebar {
  width: 240px;
  flex-shrink: 0;
  background: var(--sidebar-bg);
  color: var(--sidebar-fg);
  padding: 24px 16px;
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}

/* Le logo est noir et bleu sur fond transparent : posé tel quel sur la barre
   latérale sombre, la partie noire disparaîtrait. D'où la plaque claire, qui
   préserve les couleurs de la marque plutôt que de les écraser en blanc. */
.sidebar .brand {
  margin: 0 4px 10px;
  padding: 14px 16px;
  background: #fff;
  border-radius: 10px;
}

.sidebar .brand img {
  display: block;
  width: 100%;
  height: auto;
}

.sidebar .brand-version {
  color: rgba(255, 255, 255, .45);
  font-size: 11px;
  letter-spacing: .5px;
  padding: 0 12px 24px;
  text-align: right;
}

.sidebar nav {
  display: flex;
  flex-direction: column;
  gap: 18px;
  flex: 1;
}

/* Le blanc entre deux groupes fait le regroupement à lui seul ; l'intitulé ne fait
   que le nommer. D'où un gap de nav (18px) nettement supérieur à celui des entrées
   (3px) : c'est le rapport entre les deux, et non la ligne de titre, qui rend la
   structure lisible d'un coup d'œil. */
.sidebar nav .nav-group {
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.sidebar nav .nav-group-title {
  color: rgba(255, 255, 255, 0.4);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.9px;
  text-transform: uppercase;
  padding: 0 12px 5px;
}

.sidebar nav a {
  color: var(--sidebar-fg);
  text-decoration: none;
  padding: 10px 12px;
  border-radius: 8px;
  font-size: 0.92rem;
}

.sidebar nav a:hover {
  background: rgba(255, 255, 255, 0.06);
}

.sidebar nav a.active {
  background: var(--accent);
  color: #fff;
}

.sidebar .logout {
  color: var(--sidebar-fg);
  text-decoration: none;
  font-size: 0.85rem;
  padding: 10px 12px;
  opacity: 0.8;
}

.sidebar .logout:hover {
  opacity: 1;
}

/* Pied de la barre latérale : qui est connecté, son compte, sa déconnexion. */
.sidebar-account {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}

.sidebar-account a {
  color: var(--sidebar-fg);
  text-decoration: none;
  font-size: 0.85rem;
  padding: 8px 12px;
  border-radius: 8px;
  opacity: 0.8;
}

.sidebar-account a:hover,
.sidebar-account a.active {
  opacity: 1;
  background: rgba(255, 255, 255, 0.06);
}

.sidebar-user {
  display: flex;
  flex-direction: column;
  padding: 4px 12px 8px;
  line-height: 1.3;
}

.sidebar-user-name {
  color: var(--sidebar-fg);
  font-size: 0.85rem;
  font-weight: 600;
}

.sidebar-user-role {
  color: var(--sidebar-fg);
  font-size: 0.72rem;
  opacity: 0.6;
}

/* Colonne de droite : le bandeau collant, puis le contenu. Le flex:1 et le min-width:0
   sont portés ici et non plus par .content, sans quoi le bandeau serait plafonné à la
   largeur de lecture d'un formulaire (900 px) au lieu de tenir toute la page. */
.content-col {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  /* Largeur de lecture de la page, reprise par la liste du bandeau pour se caler sur les
     cartes. Portée ici parce que le bandeau ne sait pas, lui, quelle page il coiffe. */
  --content-max: 900px;
}

.content-col.is-wide { --content-max: 1700px; }

.content {
  padding: 32px 40px;
  max-width: 900px;
}

/* Pages à deux colonnes : la limite de 900 px sert la lisibilité d'un formulaire,
   elle empêche ici la mise côte à côte et laisse un grand vide à droite. */
.content.is-wide {
  max-width: 1700px;
}

.content h1 {
  font-size: 1.4rem;
  margin: 0 0 4px;
}

.content p.subtitle {
  color: var(--text-muted);
  margin: 0 0 28px;
  font-size: 0.92rem;
}

.card {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 28px;
  margin-bottom: 24px;
}

.card h2 {
  font-size: 1.05rem;
  margin: 0 0 18px;
}

.form-row {
  margin-bottom: 16px;
}

.form-row label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  margin-bottom: 6px;
  color: var(--text);
}

.form-row .hint {
  font-size: 0.78rem;
  color: var(--text-muted);
  margin-top: 4px;
}

/* ------------------------------------------------- assistant de démarrage */

.wizard-steps {
  display: flex;
  gap: 8px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}

.wizard-step {
  flex: 1 1 140px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: #fff;
  border-left: 3px solid var(--border);
}

.wizard-step.is-done { border-left-color: #3aa76d; }
.wizard-step.is-current { border-left-color: #1591cf; box-shadow: 0 1px 4px rgba(21, 145, 207, .18); }

.wizard-step-name {
  display: block;
  font-weight: 600;
  font-size: 0.85rem;
}

.wizard-step-hint {
  display: block;
  font-size: 0.75rem;
  color: var(--text-muted);
  margin-top: 2px;
}

.wizard-abandon { padding-top: 14px; padding-bottom: 14px; }

/* Carte d'appel : un liseré à l'accent, pas un fond coloré. Le tableau de bord est
   déjà une pile de cartes ; en repeindre une entière ferait un bandeau publicitaire,
   alors qu'il s'agit de la prochaine chose à faire. */
.card-highlight {
  border-left: 3px solid var(--accent);
  box-shadow: 0 1px 6px rgba(21, 145, 207, .14);
}

/* Marche à suivre dans un autre logiciel : les numéros comptent ici, ce sont de vraies
   étapes à exécuter dans l'ordre, dans une fenêtre Akeneo ouverte à côté. */
.wizard-howto {
  margin: 0;
  padding-left: 22px;
}

.wizard-howto > li {
  margin-bottom: 12px;
  line-height: 1.55;
}

.wizard-howto .hint {
  display: block;
  margin-top: 2px;
}

/* Étape de mapping : les zones à gauche, le panneau d'aide à droite. Le panneau
   colle au défilement — on fait des allers-retours entre une zone et l'aperçu. */
.wizard-split {
  display: grid;
  /* Le panneau prend une part de la largeur plutôt qu'une taille fixe : 300 px
     suffisent sur un écran étroit, jusqu'à 440 px quand la place le permet. */
  grid-template-columns: minmax(0, 1fr) clamp(300px, 30%, 440px);
  gap: 18px;
  align-items: start;
}

/* Seuil bas et volontairement prudent : un écran haute densité de 2447 px physiques
   ne fait qu'environ 1223 px CSS, et un seuil à 1250 px y repliait la page en une
   colonne alors qu'il restait largement la place. On ne replie qu'une fois la
   colonne de gauche réellement inutilisable. */
@media (max-width: 1100px) {
  .wizard-split { grid-template-columns: minmax(0, 1fr); }
  .wizard-aside { position: static !important; max-height: none !important; }
}

.wizard-aside {
  position: sticky;
  top: 18px;
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  max-height: calc(100vh - 40px);
  display: flex;
  flex-direction: column;
}

.aside-tabs {
  display: flex;
  border-bottom: 1px solid var(--border);
  background: #f7f9fb;
  flex: 0 0 auto;
}

.aside-tab {
  flex: 1;
  width: auto;
  border: 0;
  background: transparent;
  padding: 11px 10px;
  font: inherit;
  font-size: 0.82rem;
  color: var(--text-muted);
  cursor: pointer;
  border-bottom: 2px solid transparent;
}

.aside-tab.is-active {
  color: var(--text);
  font-weight: 600;
  background: var(--card-bg);
  border-bottom-color: var(--accent);
}

.aside-panel {
  padding: 14px 16px;
  overflow-y: auto;
  flex: 1 1 auto;
}

.aside-preview {
  display: block;
  width: 100%;
  border: 1px solid var(--border);
  border-radius: 6px;
  margin-bottom: 10px;
}

.aside-search { display: flex; gap: 8px; }
.aside-search input { flex: 1; }
.aside-search .btn { white-space: nowrap; }

.aside-result { margin-top: 12px; }

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

.aside-table th,
.aside-table td {
  text-align: left;
  vertical-align: top;
  padding: 7px 6px;
  border-top: 1px solid #eef2f6;
}

.aside-table th { width: 45%; font-weight: 400; }
.aside-table th code { font-size: 0.72rem; color: var(--text-muted); }
.aside-table td { word-break: break-word; }
.aside-table td.is-empty { color: var(--text-muted); }

.aside-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 999px;
  background: #eaf7ef;
  color: #2e7d51;
  font-size: 0.68rem;
}

.zone-card {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 14px 16px;
  margin-bottom: 14px;
  background: #fbfcfd;
}

/* Une zone repliée tient sur une ligne : nom de la zone à gauche, ce qu'elle produira à
   droite. Dix zones dépliées faisaient quatre mille pixels et ne montraient jamais l'état
   d'ensemble, qui est pourtant la seule question de cette étape. */
details.zone-card { padding: 0; }
details.zone-card > .zone-summary {
  /* La mise en ligne est portée par .zone-summary-row ; le summary reste en block pour
     que le navigateur n'émette pas son propre chevron (cf la section du bandeau). */
  padding: 12px 16px;
  cursor: pointer;
  border-radius: 8px;
}
details.zone-card > .zone-summary:hover { background: #f2f6fa; }
details.zone-card > .zone-summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
details.zone-card[open] > .zone-summary { border-bottom: 1px solid var(--border); border-radius: 8px 8px 0 0; }

.zone-panel { padding: 14px 16px 16px; }

/* Le chevron SVG a été retiré des deux étapes : c'est la marque native du navigateur qui
   sert d'indicateur, ici comme dans le sélecteur de PIM (cf la section du bandeau). */

/* L'état occupe la place restante et se tronque : le nom de la zone ne doit jamais être
   repoussé hors du cadre par une liste d'attributs longue. */
.zone-state {
  flex: 1;
  min-width: 0;
  text-align: right;
  font-size: 0.82rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.zone-state.is-set { color: #2e7d51; }
.zone-state.is-unset { color: var(--text-muted); font-style: italic; }

.zone-toolbar {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 6px;
}
.zone-progress { font-size: 0.86rem; font-weight: 600; }
.zone-empty-note { margin: 0 0 14px; }
.zone-picker-hint { margin: 8px 0 0; }

.zone-head { margin-bottom: 10px; }
details.zone-card .zone-head { margin-bottom: 0; min-width: 0; }

.zone-name {
  font-size: 0.9rem;
  font-weight: 600;
  background: #eef4f9;
  padding: 2px 7px;
  border-radius: 5px;
}

.zone-pair {
  display: block;
  font-size: 0.75rem;
  color: var(--text-muted);
  margin-top: 5px;
}

.zone-choice { display: flex; gap: 18px; margin-bottom: 10px; font-size: 0.85rem; }
.zone-choice label { display: flex; align-items: center; gap: 6px; }
.zone-choice input { width: auto; }

.zone-body textarea { min-height: 0; font-family: inherit; font-size: 0.9rem; }

.zone-picker {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 8px;
  align-items: start;
}

.zone-picker .zone-search { grid-column: 1 / -1; }
.zone-picker .zone-list { width: 100%; font-size: 0.85rem; }
.zone-picker .zone-add { white-space: nowrap; }

.zone-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }

.zone-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: #e8f4fb;
  border: 1px solid #c7e2f2;
  color: #11324d;
  border-radius: 999px;
  padding: 3px 6px 3px 11px;
  font-size: 0.8rem;
}

.zone-chip.is-image { background: #eaf7ef; border-color: #c5e6d3; }

.zone-chip-x {
  border: 0;
  background: transparent;
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  padding: 0 4px;
  color: #55707f;
  width: auto;
}

.zone-empty { font-size: 0.8rem; color: var(--text-muted); }
.zone-separator-row { margin-top: 12px; margin-bottom: 0; }

/* Intertitre à l'intérieur d'un formulaire long (fiche client, connexion…). */
.form-section {
  margin: 26px 0 12px;
  padding-top: 18px;
  border-top: 1px solid var(--border);
  font-size: 0.95rem;
  color: var(--text);
}

.form-section:first-of-type {
  margin-top: 20px;
}

/* Distingue un modèle de produit d'un produit dans l'historique. */
.entity-tag {
  font-size: 0.75rem;
  color: var(--text-muted);
  white-space: nowrap;
}

.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

@media (max-width: 720px) {
  .app-shell {
    flex-direction: column;
  }
  /* Empilée au-dessus du contenu : l'épingler la ferait occuper tout l'écran. */
  .sidebar {
    width: 100%;
    position: static;
    height: auto;
    overflow: visible;
  }
  .form-grid {
    grid-template-columns: 1fr;
  }
  .content {
    padding: 24px;
  }
  /* Même retrait latéral que le contenu, et le bandeau cesse de coller : la barre
     latérale est alors empilée au-dessus, un bandeau collant sous elle flotterait au
     milieu de l'écran. */
  .topbar {
    position: static;
    padding: 10px 24px;
  }
  .pim-current { min-width: 0; max-width: none; }
  /* Le retrait du bandeau passe de 40 à 24 px : la liste, qui s'aligne dessus, doit
     suivre — sinon elle déborde de seize pixels à droite. */
  .pim-list { left: 24px; width: calc(100% - 48px); }
}

/* Tous les champs de saisie, désignés par exclusion plutôt que par énumération :
   une liste de types à rallonge finit toujours par en oublier un — c'est ce qui
   est arrivé à type="email", resté au style par défaut du navigateur au milieu de
   champs stylés. Les types ci-dessous ont leur propre rendu (bouton, case, fichier)
   et ne doivent pas hériter de la boîte de saisie. */
input:not([type="submit"]):not([type="button"]):not([type="hidden"]):not([type="radio"]):not([type="checkbox"]):not([type="file"]),
select,
textarea {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 0.92rem;
  background: #fff;
  color: var(--text);
  font-family: inherit;
}

/* Le champ fichier garde son rendu natif (bouton du navigateur) : on ne l'aligne
   que sur la typographie des autres champs. */
input[type="file"] {
  font-size: 0.85rem;
  padding: 6px 0;
  font-family: inherit;
}

textarea {
  resize: vertical;
  min-height: 100px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.82rem;
}

input:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(79, 109, 245, 0.15);
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 18px;
  border-radius: 8px;
  border: none;
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
}

.btn-primary {
  background: var(--accent);
  color: #fff;
}

.btn-primary:hover {
  background: var(--accent-hover);
}

.btn-secondary {
  background: #eef0f4;
  color: var(--text);
}

.btn-secondary:hover {
  background: #e2e5ec;
}

.btn-row {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
}

.alert {
  padding: 12px 16px;
  border-radius: 8px;
  font-size: 0.88rem;
  margin-bottom: 20px;
}

.alert-success {
  background: var(--success-bg);
  color: var(--success-fg);
}

.alert-error {
  background: var(--error-bg);
  color: var(--error-fg);
}

/* Information neutre : ni succès ni échec (accès de secours, mode transitoire). */
.alert-info {
  background: #eef4fd;
  color: #23486f;
}

/* Étape manquante, pas panne : sur une installation neuve la configuration EST
   incomplète, et la peindre en rouge ferait passer le déroulement normal pour un
   incident. */
.alert-warn {
  background: #fdf3e2;
  color: #7d5510;
}

/* Ce qui reste à faire, énuméré. Chaque ligne pointe vers la page où le régler
   quand le compte y a droit — un bouton grisé qui ne dit ni quoi ni où laisse la
   personne devant un refus sans issue. */
.setup-blockers {
  margin: 8px 0 0;
  padding-left: 20px;
}

.setup-blockers li {
  margin-bottom: 3px;
}

.auth-links {
  margin-top: 16px;
  text-align: center;
  font-size: 0.85rem;
}

/* Sélecteur de langue des écrans publics : discret, sous les liens d'action. La
   langue en cours est en gras et sans lien — cliquer sur celle qu'on lit déjà
   n'apprend rien. Séparateurs posés en CSS pour ne pas les traduire. */
.auth-lang {
  margin-top: 20px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  color: var(--text-muted);
  font-size: 0.8rem;
}

.auth-lang a,
.auth-lang strong {
  display: inline-block;
  padding: 0 6px;
}

.auth-lang a + a::before,
.auth-lang strong + a::before,
.auth-lang a + strong::before {
  content: "·";
  margin-right: 12px;
  color: var(--border);
}

/* Action de tableau rendue par un <form> : doit se lire comme un lien, pas comme
   un bouton, sinon chaque ligne devient une barre d'outils. */
.btn-link {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--accent);
  cursor: pointer;
  text-decoration: underline;
}

.btn-link:hover {
  color: var(--accent-hover);
}

.btn-link.is-danger {
  color: #a12020;
}

.row-actions {
  display: flex;
  gap: 12px;
  align-items: center;
  flex-wrap: wrap;
  white-space: nowrap;
}

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

table.history th,
table.history td {
  text-align: left;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
}

table.history th {
  color: var(--text-muted);
  font-weight: 600;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.empty-state {
  color: var(--text-muted);
  font-size: 0.9rem;
  padding: 20px 0;
}

.filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: flex-end;
  margin-bottom: 20px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--border);
}

.filter-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 150px;
}

.filter-field label {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-muted);
}

.filter-field input,
.filter-field select {
  padding: 8px 10px;
  font-size: 0.85rem;
}

.activity-row-batch > td {
  background: #f7f9fb;
}

.activity-detail {
  margin: 0;
}

.activity-detail summary {
  cursor: pointer;
  font-size: 0.82rem;
  color: var(--text-muted);
}

.activity-detail ul {
  margin: 8px 0 0;
  padding-left: 18px;
  font-size: 0.82rem;
}

.activity-detail li {
  margin-bottom: 4px;
}

.activity-detail .failed {
  color: #a12020;
}

.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin-top: 20px;
}

.pagination-info {
  font-size: 0.85rem;
  color: var(--text-muted);
}

.btn.disabled {
  pointer-events: none;
  opacity: 0.4;
}

.template-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 12px;
  margin-top: 4px;
}

/* La carte est enveloppée pour porter le bouton loupe en frère plutôt qu'en
   descendant : sur la page de génération la carte est un <label>, et un bouton à
   l'intérieur serait à la fois du HTML invalide et un clic de sélection. */
.template-card-wrap {
  position: relative;
  display: flex;
}

.template-card {
  display: flex;
  flex: 1;
  min-width: 0;
  flex-direction: column;
  border: 2px solid var(--border);
  border-radius: var(--radius);
  padding: 8px;
  cursor: pointer;
  background: #fff;
}

.template-card input[type="radio"] {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.template-card:has(input:checked) {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(79, 109, 245, 0.15);
}

/* Ratio A4 portrait : c'est le format de la très grande majorité des templates.
   En 16/9 avec un recadrage « cover », une fiche portrait n'apparaissait que par
   une bande prise en son milieu — illisible pour reconnaître un modèle. */
.template-thumb {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 210 / 297;
  background: #f4f5f7;
  border-radius: 6px;
  overflow: hidden;
  margin-bottom: 8px;
}

/* « contain » et non « cover » : une présentation au format paysage doit rester
   entièrement visible dans un cadre portrait, quitte à laisser des marges. */
.template-thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.template-thumb-placeholder {
  font-size: 0.72rem;
  color: var(--text-muted);
  text-align: center;
  padding: 0 8px;
}

.template-name {
  font-size: 0.82rem;
  text-align: center;
  word-break: break-word;
}

/* Carte de template non sélectionnable (tableau de bord) : elle porte une action
   propre au lieu d'un bouton radio. */
.template-card.is-static {
  cursor: default;
}

.template-action {
  margin-top: auto;
  justify-content: center;
  gap: 6px;
  padding: 6px 8px;
  font-size: 0.76rem;
}

/* Deux actions empilées (édition + rafraîchissement de l'aperçu, page Gestion des
   templates) : le conteneur porte le margin-top:auto qui les colle en bas de la
   carte, chaque .template-action garde son style mais perd le sien pour ne pas
   créer d'espace entre les deux boutons. */
.template-actions {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.template-actions .template-action {
  margin-top: 0;
  width: 100%;
}
.template-refresh-form {
  margin: 0;
}

/* ---------- Rapport de vérification des placeholders ---------- */
.placeholder-report {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.placeholder-finding {
  border: 1px solid var(--border);
  border-left-width: 4px;
  border-radius: 8px;
  padding: 10px 14px;
  background: #fff;
}
.placeholder-finding-error { border-left-color: #a12020; }
.placeholder-finding-warning { border-left-color: #9a6a12; }
.placeholder-finding-ok { border-left-color: #2f7d4f; }
.placeholder-finding-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 4px;
}
.placeholder-finding-head code {
  font-size: 0.82rem;
  background: #f4f5f7;
  padding: 1px 6px;
  border-radius: 4px;
}
.placeholder-finding p {
  margin: 0;
  font-size: 0.86rem;
  color: var(--text-muted);
}

/* margin-top:auto ci-dessus colle le bouton en bas : sans lui, deux cartes dont
   les noms tiennent sur un nombre de lignes différent désalignent leurs boutons. */
.template-action svg {
  width: 13px;
  height: 13px;
  flex: none;
}

/* Voile + loupe centrée recouvrant la vignette, révélés au survol de la carte.
   Le bouton se cale sur la vignette par géométrie : celle-ci est le premier enfant
   de la carte, dont la bordure (2px) et le rembourrage (8px) donnent l'inset de
   10px, et elle porte le même aspect-ratio. */
.template-zoom {
  position: absolute;
  top: 10px;
  left: 10px;
  right: 10px;
  aspect-ratio: 210 / 297;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: rgba(10, 12, 15, 0.45);
  color: #fff;
  opacity: 0;
  transition: opacity 0.25s ease;

  /* Le voile laisse passer les clics : sur la page de génération, cliquer l'image
     doit continuer à SÉLECTIONNER le template. Seule la pastille centrale
     ci-dessous les capte, et rouvre l'aperçu. */
  pointer-events: none;
}

.template-zoom-icon {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border-radius: 999px;
  background: rgba(10, 12, 15, 0.35);
  /* Le curseur est porté ici : sur le voile, pointer-events:none l'empêcherait de
     s'appliquer. */
  cursor: zoom-in;
  pointer-events: auto;
  transform: scale(0.8);
  transition: transform 0.25s ease, background-color 0.2s ease;
}

.template-zoom-icon svg {
  width: 26px;
  height: 26px;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.5));
}

.template-card-wrap:hover .template-zoom,
.template-zoom:focus-visible {
  opacity: 1;
}

.template-card-wrap:hover .template-zoom-icon,
.template-zoom:focus-visible .template-zoom-icon {
  transform: scale(1);
}

.template-zoom-icon:hover {
  background: rgba(21, 145, 207, 0.85);
}

.template-zoom:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Léger rapprochement de la vignette au survol, comme sur une galerie : le cadre a
   déjà overflow:hidden, l'image ne déborde donc pas. */
.template-thumb img {
  transition: transform 0.35s ease;
}

.template-card-wrap:hover .template-thumb img {
  transform: scale(1.06);
}

/* Le survol n'existe pas au tactile : la loupe y est affichée en permanence plutôt
   qu'inaccessible, mais sans assombrir la vignette. */
@media (hover: none) {
  .template-zoom {
    opacity: 1;
    background: transparent;
  }

  .template-zoom-icon {
    transform: scale(1);
  }
}

/* Ouverture par classe (cf layout.php) : visibility, et non display, pour que
   l'apparition puisse être animée. Fermée, la boîte sort aussi du parcours clavier. */
.template-zoom-overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(8, 9, 12, 0.92);
  backdrop-filter: blur(4px);
  cursor: zoom-out;
  opacity: 0;
  visibility: hidden;
  /* visibility bascule SANS transition à l'ouverture, et seulement après le fondu à
     la fermeture. Transitionnée dans les deux sens, elle reste « hidden » pendant la
     première image : le bouton de fermeture n'est alors pas focusable et le focus
     reste derrière l'aperçu. */
  transition: opacity 0.2s ease, visibility 0s linear 0.2s;
}

.template-zoom-overlay.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.2s ease, visibility 0s;
}

.template-zoom-figure {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin: 0;
  max-width: 100%;
  max-height: 100%;
}

/* min-height:0 : sans lui, l'image d'un élément flex ne peut pas se réduire sous sa
   hauteur intrinsèque et déborde de l'écran au lieu de s'y adapter. */
.template-zoom-figure img {
  min-height: 0;
  max-width: 100%;
  max-height: 88vh;
  object-fit: contain;
  border-radius: 6px;
  background: #fff;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
  cursor: default;
  transform: scale(0.96);
  transition: transform 0.2s ease;
}

.template-zoom-overlay.is-open img {
  transform: scale(1);
}

.template-zoom-figure figcaption {
  color: #fff;
  font-size: 0.86rem;
  text-align: center;
  word-break: break-word;
}

.template-zoom-close {
  position: absolute;
  top: 16px;
  right: 20px;
  width: 40px;
  height: 40px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.1);
  color: #fff;
  font-size: 1.375rem;
  line-height: 1;
  cursor: pointer;
}

.template-zoom-close:hover {
  background: rgba(255, 255, 255, 0.2);
}

@media (prefers-reduced-motion: reduce) {
  .template-zoom,
  .template-zoom-icon,
  .template-thumb img,
  .template-zoom-overlay,
  .template-zoom-figure img {
    transition: none;
  }

  .template-card-wrap:hover .template-thumb img {
    transform: none;
  }
}

.client-logo-preview {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  max-width: 220px;
  padding: 8px;
  margin-bottom: 8px;
  background: #f4f5f7;
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.client-logo-preview img {
  max-width: 100%;
  max-height: 80px;
  display: block;
}

/* Case à cocher au fil du texte d'une aide : surcharge le label bloc de .form-row. */
.form-row label.inline-check,
.inline-check {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 4px 0 0;
  font-size: inherit;
  font-weight: 400;
  color: inherit;
}

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 12px;
  margin-bottom: 24px;
}

.stat-tile {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px 18px;
}

.stat-tile .stat-label {
  font-size: 0.78rem;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  margin-bottom: 6px;
}

.stat-tile .stat-value {
  font-size: 1.6rem;
  font-weight: 700;
  color: var(--text);
}

.stat-tile.stat-total .stat-value {
  color: var(--accent);
}

.history-chart {
  width: 100%;
  height: auto;
  display: block;
}

.chart-grid {
  stroke: var(--border);
  stroke-width: 1;
}

.chart-line {
  stroke: rgba(79, 109, 245, 0.35);
  stroke-width: 3;
}

.chart-dot {
  fill: #2d3f6b;
}

.chart-dot-today {
  fill: var(--accent);
}

.chart-value {
  font-size: 13px;
  font-weight: 700;
  fill: #2d3f6b;
}

.chart-day-label {
  font-size: 12px;
  fill: var(--text-muted);
}

.chart-day-today {
  fill: var(--accent);
  font-weight: 700;
}

/* Bordure en currentColor : la toupie sert désormais sur les boutons secondaires
   (texte sombre sur fond clair) autant que sur les primaires. Un blanc en dur y était
   invisible. */
.spinner {
  display: inline-block;
  width: 14px;
  height: 14px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  opacity: 0.85;
  vertical-align: -2px;
  animation: spin 0.7s linear infinite;
}

/* Un bouton en attente reste lisible : le griser jusqu'à l'illisibilité ferait douter
   qu'il se passe encore quelque chose, ce que la toupie est justement là pour dire. */
.btn[disabled] .spinner {
  opacity: 1;
}

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

.docs h2 {
  margin-top: 0;
}

.docs h3 {
  font-size: 0.95rem;
  margin: 22px 0 8px;
}

.docs p,
.docs li {
  font-size: 0.9rem;
  line-height: 1.55;
  color: var(--text);
}

.docs ul,
.docs ol {
  margin: 0 0 12px;
  padding-left: 22px;
}

.docs code {
  background: #eef0f4;
  border-radius: 4px;
  padding: 1px 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.85em;
}

.docs pre {
  background: #1b1f27;
  color: #e4e6eb;
  padding: 14px 16px;
  border-radius: 8px;
  overflow-x: auto;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.8rem;
  line-height: 1.5;
}

.docs table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.87rem;
  margin-bottom: 12px;
}

.docs table th,
.docs table td {
  text-align: left;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}

.docs table th {
  color: var(--text-muted);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.docs .toc {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 0;
}

.docs .toc a {
  font-size: 0.82rem;
  color: var(--accent);
  text-decoration: none;
  background: #eef0f4;
  padding: 6px 12px;
  border-radius: 999px;
}

.docs .toc a:hover {
  background: #e2e5ec;
}

/* ---------- Captures d'écran de la documentation ---------- */

/* Vignette, pas illustration pleine largeur : une capture d'interface fait 1 000 à
   1 800 px de haut, et insérée telle quelle elle coupait le chapitre en deux. La loupe
   ouvre la même visionneuse que les templates (cf layout_zoom_button).

   inline-block plutôt qu'un conteneur flex : deux appels consécutifs à doc_figure()
   sont deux frères directs dans le flux du texte, sans emballage commun — ils se
   rangent donc côte à côte tout seuls, et passent à la ligne quand la place manque. */
.doc-figure {
  display: inline-block;
  vertical-align: top;
  width: 248px;
  max-width: 100%;
  margin: 6px 14px 18px 0;
}

.doc-figure-thumb {
  position: relative;
  display: block;
  height: 150px;
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  /* Damier discret : une capture d'écran a souvent un fond blanc, qui se fondrait
     dans la carte et donnerait une vignette sans contour perceptible. */
  background: #f2f4f7;
}

/* cover cadré sur le HAUT, et non contain.
   Ces captures vont de 232x758 (la barre latérale, haute et étroite) à 1686x1051 (le
   suivi d'activité). Réduites entières dans un cadre commun, les plus étroites
   tombaient à une cinquantaine de pixels de large — un filet illisible bordé de vide.
   À 248 px aucune ne serait de toute façon lisible : le rôle de la vignette est
   d'annoncer l'image et d'appeler le clic, pas de la restituer. On remplit donc le
   cadre avec le HAUT de la capture, qui est la partie identifiante d'une copie d'écran
   — titre de page, en-tête, premières entrées de menu. */
.doc-figure-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  display: block;
  transition: transform 0.35s ease;
}

.doc-figure:hover .doc-figure-thumb img {
  transform: scale(1.04);
}

/* Le voile des templates est calé sur la géométrie d'une carte de template
   (aspect-ratio A4, retrait de 10 px). Ici il doit couvrir la vignette entière. */
.doc-figure .template-zoom {
  inset: 0;
  aspect-ratio: auto;
  border-radius: 0;
}

.doc-figure:hover .template-zoom {
  opacity: 1;
}

.doc-figure:hover .template-zoom-icon {
  transform: scale(1);
}

.doc-figure figcaption {
  margin-top: 8px;
  font-size: 0.78rem;
  color: var(--text-muted);
  line-height: 1.45;
}

/* Emplacement en attente, visible des seuls administrateurs : le trait discontinu
   marque un espace réservé et non un encadré de contenu. */
.doc-figure-todo {
  margin: 16px 0 20px;
  padding: 14px 16px;
  border: 1px dashed #d8b981;
  border-radius: 8px;
  background: #fffaf1;
}

.doc-figure-todo strong {
  display: block;
  margin-top: 8px;
}

.doc-figure-todo p {
  margin: 6px 0 0;
}

details > summary {
  cursor: pointer;
  font-weight: 600;
  font-size: 0.92rem;
  padding: 4px 0;
  list-style: none;
}

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

details > summary::before {
  content: "▶ ";
  font-size: 0.75em;
}

details[open] > summary::before {
  content: "▼ ";
}

details[open] > summary {
  margin-bottom: 12px;
}

pre.log {
  background: #1b1f27;
  color: #e4e6eb;
  padding: 14px 16px;
  border-radius: 8px;
  overflow-x: auto;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.78rem;
  line-height: 1.5;
  max-height: 420px;
  overflow-y: auto;
  white-space: pre-wrap;
  word-break: break-all;
  margin: 0;
}

/* ---------- En-tête de contenu et lien d'aide ---------- */
.content-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}
.content-head h1 { margin-bottom: 0; }

/* Discret par construction : c'est un recours, pas une action. Il doit se trouver quand
   on le cherche, sans disputer l'attention au titre ni au premier bouton de la page. */
.doc-help {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 12.5px;
  color: #6b7784;
  text-decoration: none;
  padding: 5px 10px 5px 6px;
  border-radius: 999px;
  transition: background .15s, color .15s;
}
.doc-help:hover,
.doc-help:focus-visible { background: #eef2f6; color: #22272c; }
.doc-help-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: #dde5ec;
  color: #4a5560;
  font-size: 11.5px;
  font-weight: 700;
  line-height: 1;
}
.doc-help:hover .doc-help-mark { background: #5992c7; color: #fff; }

/* Marque d'un logiciel tiers devant un titre (cf layout_akeneo_mark). Le logo Akeneo
   porte son propre fond violet : posé tel quel sur une carte claire il se suffit, aucune
   plaque n'est nécessaire — contrairement au logo Doculians de la barre sombre.
   Dimensionné sur la hauteur du texte (em) et non en pixels, pour rester aligné qu'il
   précède un h2 ou un h3. */
.brand-mark {
  width: 1.15em;
  height: 1.15em;
  border-radius: 4px;
  vertical-align: -0.18em;
  margin-right: 0.45em;
  flex-shrink: 0;
}

/* ---------- Bandeau supérieur et PIM courant ---------- */
/* Collant : sur une page longue on perdait de vue le client sur lequel on travaille. */
.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 10px 40px;
  background: var(--card-bg);
  border-bottom: 1px solid var(--border);
  /* Hauteur FIXE : c'est elle qui garantit que rien ne bouge à l'ouverture de la liste.
     Le contenu du bandeau n'a pas le droit de la faire varier. Le position:sticky
     ci-dessus sert aussi de référence à la liste, qui s'étend d'un bord à l'autre du
     bandeau plutôt que de la seule largeur du sélecteur. */
  height: 62px;
}
.topbar-end { display: flex; align-items: center; gap: 12px; }

.pim-box {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}
.pim-box-title {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .9px;
  text-transform: uppercase;
  color: var(--text-muted);
  flex-shrink: 0;
}

/* La flèche de déroulement est celle du NAVIGATEUR, et c'est délibéré.
   Deux tentatives de suppression ont échoué (summary en flex puis en block, avec
   list-style:none, ::marker et ::-webkit-details-marker) : les moteurs ne traitent pas
   tous cette marque comme un marqueur de liste, et elle n'est donc pas supprimable de
   façon fiable. On l'adopte : elle est déjà à gauche, avant le contenu, et elle pivote à
   l'ouverture sans une ligne de code. Notre chevron SVG a disparu — c'est lui qui faisait
   la seconde flèche. */
.pim-switch > summary,
details.zone-card > .zone-summary { display: list-item; list-style-position: inside; }

/* Le contenu est en INLINE-flex : la marque étant rendue dans le flux du summary, un bloc
   la repousserait à la ligne — c'est exactement ce qui se produisait en display:block.
   La largeur laisse la place de la marque, sinon la ligne déborde. */
.pim-row,
details.zone-card > .zone-summary > .zone-summary-row {
  display: inline-flex;
  vertical-align: middle;
  align-items: center;
  gap: 12px;
  width: calc(100% - 1.6em);
  min-width: 0;
}

/* Hauteur figée de la LIGNE, et non du summary : c'est elle qui dicte la hauteur de la
   boîte, et elle interdit ainsi à la marque native — dont les glyphes fermé et ouvert
   n'ont pas la même métrique — de faire grandir la carte à l'ouverture. Sans cela, le
   bandeau collant s'agrandissait de quelques pixels et toute la page sautait. */
.pim-row { height: 34px; }
details.zone-card > .zone-summary > .zone-summary-row { min-height: 28px; }

.pim-current {
  padding: 6px 14px 6px 10px;
  border-radius: 9px;
  border: 1px solid var(--border);
  background: #fff;
  /* La largeur suit le nom du client, dans ces bornes : assez pour qu'un intitulé
     ordinaire tienne en entier — ce que la barre latérale ne permettait pas —, borné
     pour qu'un nom à rallonge ne pousse pas le lien d'aide hors de l'écran. */
  min-width: 260px;
  max-width: 460px;
}

.pim-switch > summary { cursor: pointer; }
.pim-switch > summary:hover { border-color: #b9c8d6; background: #f7f9fb; }
.pim-switch > summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.pim-identity {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  flex: 1;
}
.pim-logo {
  width: 30px; height: 30px;
  flex-shrink: 0;
  border-radius: 6px;
  object-fit: contain;
  background: #fff;
  border: 1px solid var(--border);
  padding: 2px;
}
.pim-logo-letter {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--accent);
  color: #fff;
  border: 0;
  font-size: 14px;
  font-weight: 700;
  padding: 0;
}
.pim-text { display: flex; flex-direction: column; min-width: 0; }
.pim-name {
  color: var(--text);
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.35;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pim-code {
  color: var(--text-muted);
  font-size: 11px;
  line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Calée sur la COLONNE DE CONTENU : même bord gauche et même bord droit que les cartes
   de la page, ce qui l'aligne sur ce qu'on lit en dessous plutôt que sur la largeur du
   seul sélecteur. Les lignes portent l'adresse du PIM, qui a besoin de cette place.

   La variable est posée par .content-col et suit le mode large de la page — le bandeau,
   lui, ne sait pas quelle page il coiffe.

   En surimpression, jamais dans le flux : le bandeau est collant et de hauteur fixe, une
   liste qui pousserait le contenu ferait sauter la page à chaque ouverture. */
.pim-list {
  position: absolute;
  top: calc(100% + 6px);
  left: 40px;
  width: calc(min(var(--content-max, 900px), 100%) - 80px);
  list-style: none;
  margin: 0;
  padding: 5px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--card-bg);
  box-shadow: 0 12px 28px rgba(20, 30, 45, .16);
  max-height: 60vh;
  overflow-y: auto;
  z-index: 30;
}
.pim-list a {
  display: flex;
  align-items: center;
  padding: 9px 12px;
  border-radius: 7px;
  text-decoration: none;
}
.pim-list a:hover { background: #f2f6fa; }
.pim-list a.is-current { background: #eaf2fa; }
.pim-list a:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Le nom occupe la place nécessaire, l'adresse prend le reste et s'aligne à droite :
   les adresses forment ainsi une colonne, comparable d'un coup d'œil. */
.pim-list .pim-text { flex: 0 1 auto; }
.pim-url {
  flex: 1 1 auto;
  min-width: 0;
  text-align: right;
  font-size: 11.5px;
  color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pim-url.is-empty { font-style: italic; opacity: .65; }

/* Nommer la commande pour un lecteur d'écran sans l'écrire à l'écran, où le chevron
   et la liste qu'il déplie suffisent. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.client-table { width: 100%; border-collapse: collapse; }
.client-table th,
.client-table td { padding: 10px 12px; border-bottom: 1px solid #e6eaee; text-align: left; font-size: 14px; }
.client-table th { font-size: 12px; text-transform: uppercase; letter-spacing: .4px; color: #6b7784; }
.client-table tr.is-current td { background: #f2f7fb; }
.badge {
  display: inline-block; padding: 2px 9px; border-radius: 999px;
  font-size: 11px; font-weight: 600; background: #eef2f6; color: #6b7784;
}
.badge.ok { background: #e7f6ec; color: #2f7d4f; }
.badge.ko { background: #fdecec; color: #a12020; }
.badge.warn { background: #fdf3e2; color: #9a6a12; }

/* ---------- Barre de couverture (Suivi des traductions) ---------- */
.coverage-bar {
  display: inline-block; width: 140px; height: 8px; border-radius: 999px;
  background: #eef2f6; overflow: hidden; vertical-align: middle; margin-right: 8px;
}
.coverage-bar-fill { height: 100%; border-radius: 999px; }
.coverage-bar-fill.coverage-ok { background: #2f7d4f; }
.coverage-bar-fill.coverage-warn { background: #9a6a12; }
.coverage-bar-fill.coverage-ko { background: #a12020; }
.coverage-label { font-size: 12.5px; color: #6b7784; vertical-align: middle; }

/* ---------- Onglets d'emplacement (UI Extension) ---------- */
.panel-tabs { display: flex; flex-wrap: wrap; gap: 12px; }
.panel-tab {
  flex: 1 1 240px;
  display: flex; flex-direction: column; gap: 4px;
  padding: 14px 18px;
  border: 2px solid #e6eaee; border-radius: 10px;
  text-decoration: none; color: #22272c;
  transition: border-color .15s, box-shadow .15s, background .15s;
}
.panel-tab:hover { border-color: #b9c8d6; }
.panel-tab.active {
  border-color: #5992c7; background: #f2f7fb;
  box-shadow: 0 0 0 3px rgba(89, 146, 199, .16);
}
.panel-tab strong { font-size: 14px; }
.panel-tab span { font-size: 11px; color: #6b7784; }
.panel-tab code { font-size: 11px; }

/* ---------- Arbre de catégories à cocher (page de génération) ----------
   Le triangle de pliage est celui du navigateur (<details>/<summary>), comme le
   sélecteur de PIM et les cartes de zones de l'assistant : aucun moteur ne le
   supprime de façon fiable, on ne lui superpose donc pas le nôtre. La case à cocher
   vit dans le <summary> pour rester collée à son intitulé — c'est includes/
   category_picker.php qui empêche un clic dessus de déplier le nœud. */
.cat-toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}

.cat-filter {
  flex: 0 1 260px;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font: inherit;
}

.cat-count {
  font-size: 0.85rem;
  color: var(--text-muted);
}

.cat-count.is-set {
  color: var(--accent);
  font-weight: 600;
}

.cat-toolbar-actions {
  margin-left: auto;
  display: flex;
  gap: 14px;
  font-size: 0.85rem;
}

/* Hauteur bornée : un catalogue réel fait plusieurs milliers de lignes, et les
   boutons qui suivent doivent rester atteignables sans traverser tout l'arbre. */
.cat-picker {
  max-height: 460px;
  overflow: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 8px 12px;
  background: #fbfcfd;
}

.cat-tree,
.cat-children {
  list-style: none;
  margin: 0;
  padding: 0;
}

.cat-children {
  padding-left: 20px;
}

.cat-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 3px 4px;
  border-radius: 5px;
  font-size: 0.88rem;
  cursor: default;
}

.cat-row:hover {
  background: #eef2f8;
}

/* La feuille n'a pas de triangle : sans ce retrait, son intitulé ne s'alignerait pas
   sur celui de ses voisines qui en ont un. */
.cat-leaf > .cat-row {
  padding-left: 20px;
}

.cat-row label {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0;
  font-weight: 400;
  cursor: pointer;
  min-width: 0;
}

.cat-icon {
  width: 15px;
  height: 13px;
  flex: 0 0 auto;
  background: no-repeat center/contain url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 16'%3E%3Cpath fill='none' stroke='%238a94a6' stroke-width='1.6' d='M1 14.2V2.8A1.8 1.8 0 0 1 2.8 1h4.3l1.9 2.3h8.2A1.8 1.8 0 0 1 19 5.1v9.1a1.8 1.8 0 0 1-1.8 1.8H2.8A1.8 1.8 0 0 1 1 14.2Z'/%3E%3C/svg%3E");
}

.cat-node.is-checked > .cat-row .cat-icon,
.cat-node.is-checked > details > .cat-row .cat-icon {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 16'%3E%3Cpath fill='%234f6df5' d='M1 14.2V2.8A1.8 1.8 0 0 1 2.8 1h4.3l1.9 2.3h8.2A1.8 1.8 0 0 1 19 5.1v9.1a1.8 1.8 0 0 1-1.8 1.8H2.8A1.8 1.8 0 0 1 1 14.2Z'/%3E%3C/svg%3E");
}

.cat-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cat-node.is-checked > .cat-row .cat-name,
.cat-node.is-checked > details > .cat-row .cat-name {
  font-weight: 600;
  color: var(--accent);
}

/* Le code technique lève l'ambiguïté entre deux catégories de même intitulé, sans
   prendre le pas sur l'intitulé lui-même. */
.cat-code {
  font-size: 0.75rem;
  color: #97a0b0;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Un ancêtre gardé visible uniquement parce qu'un de ses descendants correspond au
   filtre : il ne correspond pas lui-même, il ne doit donc pas se lire comme un
   résultat. */
.cat-node.is-forced > .cat-row,
.cat-node.is-forced > details > .cat-row {
  opacity: .55;
}

.cat-branch > summary {
  list-style-position: outside;
}

.cat-preview {
  margin-top: 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 14px;
  background: #fbfcfd;
}

.cat-preview > summary {
  font-weight: 600;
  font-size: 0.9rem;
  cursor: pointer;
}

.cat-preview-list {
  columns: 3 160px;
  gap: 18px;
  margin: 10px 0 0;
  padding-left: 18px;
  font-size: 0.83rem;
}

.check-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* Le bloc canal/langue n'est pas une .form-row (il en contient deux) : sans marge, son
   aide venait toucher l'intitulé du champ suivant. */
.gen-context {
  margin-bottom: 20px;
}

.gen-context h3 {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 4px 0 12px;
  font-size: 1rem;
}

/* Total des produits de la sélection, rempli par requête. Les états sont distincts
   parce qu'ils appellent des gestes différents : un chiffre se lit, « trop large »
   renvoie vers « Compter la sélection », une erreur ne dit rien du catalogue. */
.cat-products {
  font-size: 0.85rem;
  color: var(--text-muted);
}

.cat-products.is-set {
  color: var(--text);
  font-weight: 600;
}

.cat-products.is-pending {
  font-style: italic;
  opacity: .7;
}

.cat-products.is-wide,
.cat-products.is-error {
  color: #9a6a12;
}

/* Le séparateur n'apparaît qu'entre deux valeurs présentes : au chargement, la barre
   ne doit pas commencer par un tiret orphelin. */
.cat-count.is-set + .cat-products:not(:empty)::before {
  content: '— ';
  color: var(--text-muted);
  font-weight: 400;
}
