/* ============================================================
   PLCORP Design System — v3.0 (Crystal Network)
   Bleu PLCORP #2F80ED · Bleu Nuit #102A43 · Sora + Inter
   Charte Crystal Network — mis à jour 2026-07-03
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=Sora:wght@600;700&family=Inter:wght@400;500;600&display=swap');

/* ── Tokens — Crystal Network ────────────────────────────── */
:root {
  /* Couleurs primaires / secondaires (noms inchangés pour compat) */
  --primary:       #2F80ED;
  --primary-light: #5A9DF4;
  --primary-dim:   rgba(47,128,237,.10);
  --secondary:     #102A43;
  --surface-0:     #FFFFFF;
  --surface-1:     #F8FAFC;
  --surface-2:     #E2E8F0;
  --surface-dark:  #0F172A;
  --border:        #E2E8F0;
  --text-main:     #0F172A;
  --text-muted:    #475569;
  --text-light:    #64748B;
  --success:       #10B981;
  --success-dim:   rgba(16,185,129,.12);
  --warning:       #F2994A;
  --warning-dim:   rgba(242,153,74,.15);
  --error:         #DC3545;
  --error-dim:     rgba(220,53,69,.12);
  --info:          #56CCF2;

  /* Radius (existants conservés + nouveaux charte) */
  --radius-sm:     4px;
  --radius-md:     8px;
  --radius-lg:     12px;
  --radius-full:   9999px;
  --radius-btn:    14px;
  --radius-card:   20px;
  --radius-input:  14px;
  --radius-badge:  8px;

  /* Ombres */
  --shadow-sm:     0 1px 2px rgba(15,23,42,.04);
  --shadow-md:     0 2px 8px rgba(15,23,42,.06);
  --shadow-lg:     0 6px 20px rgba(15,23,42,.08);
  --transition:    .18s ease;
  --nav-h:         60px;
  --sidebar-w:     240px;

  /* ── Tokens additionnels — Crystal Network ──────────────── */
  --primary-hover:       #2668C4;
  --primary-ultra-dim:   rgba(47,128,237,.05);
  --secondary-light:     #1A3A5C;
  --secondary-dim:       rgba(16,42,67,.07);
  --text-1:              #0F172A;   /* alias text-main */
  --text-2:              #334155;   /* secondaire */
  --text-3:              #475569;   /* muet */
  --text-4:              #64748B;   /* placeholder */
  --border-medium:       rgba(16,42,67,.12);
  --shadow-xs:           0 1px 2px rgba(0,0,0,.04);
  --ease:                cubic-bezier(.4,0,.2,1);

  /* ── Couleurs nommées charte ────────────────────────────── */
  --navy:          #102A43;
  --blue:          #2F80ED;
  --cyan:          #56CCF2;
  --violet:        #7C4DFF;
  --action:        #F2994A;
  --valid:         #10B981;
  --violet-dim:    rgba(124,77,255,.12);
  --text-on-accent: #FFFFFF;   /* texte sur fond coloré (primary/accent) */

  /* ── Neutres charte ────────────────────────────────────── */
  --n-50:          #F8FAFC;
  --n-100:         #F1F5F9;
  --n-200:         #E2E8F0;
  --n-300:         #CBD5E1;
  --n-400:         #64748B;
  --n-500:         #64748B;
  --n-600:         #475569;
  --n-700:         #334155;
  --n-800:         #1E293B;
  --n-900:         #0F172A;

  /* ── Échelle typographique (tokens — remplace les rem en dur) ── */
  --font-size-xs:   0.75rem;   /* 12px — labels, méta */
  --font-size-sm:   0.875rem;  /* 14px — corps secondaire */
  --font-size-base: 1rem;      /* 16px — corps */
  --font-size-lg:   1.125rem;  /* 18px — sous-titres */

  /* ── Tailles récurrentes (avatars, icônes) ─────────────── */
  --size-avatar:    40px;
  --size-avatar-sm: 32px;
  --size-icon:      18px;
  --size-icon-sm:   14px;
  --size-touch:     44px;      /* cible tactile minimale (WCAG/mobile) */

  /* ── Largeurs mini de colonnes pour grilles auto-fit ───── */
  --grid-min-xs:    180px;
  --grid-min-sm:    220px;
  --grid-min-md:    280px;

  /* ── Glassmorphism (digital seulement — PAS sur documents) ── */
  --glass-bg:      rgba(255,255,255,0.14);
  --glass-border:  rgba(255,255,255,0.22);
  --glass-blur:    24px;
  --glass-shadow:  0 8px 40px rgba(8,24,39,0.45);
  --glass-highlight: inset 0 1px 0 rgba(255,255,255,0.18);
}

/* ── Reset léger ─────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
  font-family: 'Inter', sans-serif;
  font-size: 16px;
  color: var(--text-main);
  background: var(--surface-0);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
a { color: var(--primary); text-decoration: none; }
a:hover { opacity: .8; }
img, svg { display: block; max-width: 100%; }

/* ── Typo — Crystal Network (Sora titres, Inter corps) ──── */
h1,h2,h3,h4,h5,h6 { font-family: 'Sora', sans-serif; font-weight: 700; line-height: 1.25; }
h1 { font-size: 1.75rem; }  /* 28px */
h2 { font-size: 1.375rem; } /* 22px */
h3 { font-size: 1.125rem; } /* 18px */
h4 { font-size: 1rem; font-weight: 600; } /* 16px */
p  { color: var(--text-muted); }

/* ── Boutons — Crystal Network ───────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: 'Inter', sans-serif;
  font-weight: 500;
  font-size: .9375rem;
  min-height: 48px;
  padding: 10px 24px;
  border: 1.5px solid transparent;
  cursor: pointer;
  border-radius: var(--radius-btn);
  transition: all 200ms ease-out;
  white-space: nowrap;
}
.btn:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}
.btn:active {
  transform: translateY(0);
}
.btn-primary {
  background: var(--primary);
  color: #fff;
}
.btn-primary:hover {
  background: var(--primary-hover);
  color: #fff;
}
.btn-secondary {
  background: transparent;
  color: var(--secondary);
  border: 1.5px solid var(--secondary);
}
.btn-secondary:hover {
  background: var(--secondary-dim);
  color: var(--secondary);
}
.btn-ghost {
  background: transparent;
  color: var(--primary);
  border: 1.5px solid transparent;
}
.btn-ghost:hover {
  background: var(--primary-dim);
  color: var(--primary);
}
.btn-danger {
  background: var(--error);
  color: #fff;
}
.btn-danger:hover {
  opacity: 0.9;
}
.btn-success {
  background: var(--success);
  color: #fff;
}
.btn-success:hover {
  background: var(--valid);
  opacity: 0.9;
}
.btn-warning {
  background: var(--warning);
  color: #fff;
}
.btn-warning:hover {
  background: var(--action);
  opacity: 0.9;
}
.btn-info {
  background: var(--info);
  color: var(--text-main);
}
.btn-info:hover {
  opacity: 0.9;
}
.btn:disabled, .btn.disabled {
  opacity: 0.6;
  cursor: not-allowed;
  pointer-events: none;
  transform: none !important;
  box-shadow: none !important;
}
.btn-sm {
  min-height: 36px;
  padding: 6px 16px;
  font-size: .8125rem;
}
.btn-xs {
  min-height: 28px;
  padding: 4px 12px;
  font-size: .75rem;
}
.btn-pill {
  border-radius: var(--radius-full);
}
.btn-icon {
  padding: 0;
  width: 48px;
  height: 48px;
  border-radius: var(--radius-btn);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.btn-icon.btn-sm {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-md);
}
.btn-icon.btn-xs {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-sm);
}

/* ── Cartes — Crystal Network ────────────────────────────── */
.card, .bo-card {
  background: var(--surface-0);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-md);
  padding: 20px;
}
.card-sm { padding: 16px; }
.card-flush { padding: 0; overflow: hidden; }
.card-header {
  padding: 16px 20px; border-bottom: 1px solid var(--border);
  font-family: 'Sora', sans-serif; font-weight: 600; font-size: .9375rem;
}
.card-body { padding: 20px 20px; }
.card-footer { padding: 12px 20px; border-top: 1px solid var(--border); background: var(--surface-1); }

/* ── Badges & statuts — Crystal Network ──────────────────── */
.badge {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: .75rem; font-weight: 600; font-family: 'Inter', sans-serif;
  padding: 4px 10px; border-radius: var(--radius-badge); letter-spacing: .02em;
  white-space: nowrap; flex-shrink: 0;
}
/* Le TEXTE d'un badge n'est jamais la couleur de remplissage : posée sur une teinte à
   12-15 %, elle donnait 1,99:1 à 2,26:1 (mesuré le 2026-09-02), là où WCAG AA exige
   4,5:1. Et ce sont ces badges qui portent le statut de paiement — l'information même
   pour laquelle l'écran existe. `--ink-*` est dérivée du thème par `encre_lisible()`
   (apps/core/theme_presets.py) : elle suit la charte et reste lisible. Le repli en dur
   couvre le cas où `_theme_runtime.html` n'est pas injecté. */
.badge-success, .badge-valid  { background: rgba(16,185,129,.12);  color: var(--ink-valid, #0A7753); }
.badge-warning  { background: var(--warning-dim);  color: var(--ink-warning, #AB570C); }
.badge-error    { background: var(--error-dim);    color: var(--ink-error, #D01212); }
.badge-primary, .badge-blue  { background: var(--primary-dim);  color: var(--ink-primary, #1366D6); }
.badge-neutral  { background: var(--surface-2);    color: var(--text-muted); }
.badge-dark     { background: var(--secondary);    color: #fff; }
.badge-violet   { background: rgba(124,77,255,.12); color: var(--ink-violet, #6D39FF); }
.badge-cyan     { background: rgba(86,204,242,.12); color: var(--ink-info, #0C7799); }

/* Statuts commandes */
.status-NEW       { background: var(--surface-2);   color: var(--text-muted); border-radius: var(--radius-badge); }
.status-ANALYSIS  { background: #E3F2FD;            color: #1565C0; border-radius: var(--radius-badge); }
.status-QUOTE     { background: #F3E5F5;            color: #7B1FA2; border-radius: var(--radius-badge); }
.status-IN_PROGRESS { background: var(--warning-dim); color: #856404; border-radius: var(--radius-badge); }
.status-DONE      { background: var(--success-dim); color: var(--success); border-radius: var(--radius-badge); }
.status-DESIGN    { background: #F3E5F5;            color: #7B1FA2; border-radius: var(--radius-badge); }
.status-PRODUCTION{ background: var(--warning-dim); color: #856404; border-radius: var(--radius-badge); }
.status-REVIEW    { background: #FFF3E0;            color: #E65100; border-radius: var(--radius-badge); }
.status-READY     { background: var(--success-dim); color: var(--success); border-radius: var(--radius-badge); }
.status-DELIVERED { background: var(--primary-dim); color: var(--primary); border-radius: var(--radius-badge); }
.status-CANCELLED { background: var(--error-dim);   color: var(--error); border-radius: var(--radius-badge); }

/* ── Formulaires — Crystal Network ───────────────────────── */
.form-group { display: flex; flex-direction: column; gap: 6px; }
.form-label { font-size: .875rem; font-weight: 500; color: var(--text-main); }
.form-control, .form-input, .input {
  font-family: 'Inter', sans-serif; font-size: .9375rem;
  padding: 10px 16px; border: 1.5px solid var(--border);
  border-radius: var(--radius-input); background: var(--surface-0);
  color: var(--text-main); transition: all 200ms ease-out;
  width: 100%;
  height: 48px;
}
.form-control:focus, .form-input:focus, .input:focus {
  outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-dim);
}
.form-control::placeholder, .form-input::placeholder, .input::placeholder {
  color: var(--text-light);
}
select.form-control, select.form-input, select.input {
  appearance: none; cursor: pointer;
  background-image: url("data:image/svg+xml;utf8,<svg fill='%23475569' height='20' viewBox='0 0 24 24' width='20' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/></svg>");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 40px;
}
textarea.form-control, textarea.form-input, textarea.input { height: auto; min-height: 100px; }

/* ── File input stylisé (remplace le "Choose File" natif) ── */
input[type=file].form-control {
  height: auto; padding: 6px 10px; cursor: pointer;
  display: flex; align-items: center;
}
input[type=file].form-control::file-selector-button {
  background: var(--primary, #2F80ED); color: #fff;
  border: none; padding: 6px 14px;
  border-radius: var(--radius-sm, 6px); margin-right: 12px;
  cursor: pointer; font-size: 13px; font-weight: 500;
  font-family: 'Inter', sans-serif;
  transition: background 0.15s ease;
}
input[type=file].form-control::file-selector-button:hover {
  background: var(--primary-hover, #1a6fd4);
}

/* ── Zone de dépôt d'image ──────────────────────────────────
   Partiel : templates/backoffice/fragments/_image_dropzone.html
   Remplace visuellement le « Choose File / No file chosen » natif, que le
   navigateur impose en anglais et qu'aucune règle CSS ne peut traduire. */
.dropzone {
  position: relative;
  padding: 20px;
  border: 2px dashed var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-1);
  transition: border-color var(--transition), background var(--transition);
}
.dropzone:hover, .dropzone:focus-within { border-color: var(--primary); }
.dropzone:focus-within { box-shadow: 0 0 0 3px var(--primary-dim); }
/* État visuel pendant le survol d'un fichier glissé */
.dropzone.is-dragover {
  border-color: var(--primary);
  background: var(--primary-dim);
}
/* `.btn` force un `display:inline-flex` plus spécifique que `[hidden]` : on
   rétablit explicitement le masquage pour les éléments pilotés en JS. */
.dropzone [hidden] { display: none !important; }

/* ── Invite (aucune image sélectionnée) ── */
.dropzone-empty {
  display: flex; flex-direction: column; align-items: center;
  gap: 6px; text-align: center;
}
.dropzone.has-file .dropzone-empty { display: none; }
.dropzone-icon { color: var(--text-light); }
.dropzone-text { margin: 0; font-size: .875rem; color: var(--text-muted); }
.dropzone-hint { margin: 0; font-size: .75rem; color: var(--text-light); }
.dropzone-trigger {
  color: var(--primary); font-weight: 600; cursor: pointer;
  text-decoration: underline; text-underline-offset: 2px;
}
.dropzone-trigger:hover { color: var(--primary-hover); }

/* ── Fichier sélectionné (vignette + nom + poids + actions) ── */
.dropzone-file { display: none; align-items: center; gap: 14px; }
.dropzone.has-file .dropzone-file { display: flex; }
.dropzone-thumb {
  flex: 0 0 72px; width: 72px; height: 72px;
  object-fit: cover; border-radius: var(--radius-md);
  background: var(--surface-2); border: 1px solid var(--border);
}
.dropzone-info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.dropzone-filename {
  font-size: .875rem; font-weight: 500; color: var(--text-main);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dropzone-filesize { font-size: .75rem; color: var(--text-muted); }
.dropzone-alert { font-size: .75rem; color: var(--error); }
.dropzone-actions { flex-shrink: 0; display: flex; align-items: center; gap: 8px; }
.dropzone-actions .btn { cursor: pointer; }

/* ── Widget natif Django (jamais remplacé) ──
   `font-size:0` avale les nœuds texte « Actuellement: » / « Modifier: » du
   template `clearable_file_input.html`, impossibles à cibler autrement. */
.dropzone-native { font-size: 0; line-height: 0; }
.dropzone-native > a, .dropzone-native > br { display: none; }
/* L'input reste dans le DOM et focusable (pas de `display:none`) : c'est lui
   qui porte la validation Django et l'envoi multipart. */
.dropzone-native input[type=file] {
  position: absolute; opacity: 0;
  width: 1px; height: 1px; padding: 0; margin: 0; border: 0; overflow: hidden;
}
/* Case « Effacer » de ClearableFileInput : restylée, JAMAIS masquée — sans
   elle, plus aucun moyen de retirer une photo déjà enregistrée. */
.dropzone-native input[type=checkbox] {
  width: 16px; height: 16px; margin: 12px 6px 0 0;
  accent-color: var(--primary); vertical-align: middle; cursor: pointer;
}
.dropzone-native label {
  display: inline-block; margin-top: 12px; vertical-align: middle;
  font-size: .8125rem; line-height: 1.4; color: var(--text-muted); cursor: pointer;
}
.dropzone-native label:hover { color: var(--error); }

@media (max-width: 480px) {
  .dropzone-file { flex-wrap: wrap; }
  .dropzone-actions { width: 100%; }
}

/* ── Tableaux ────────────────────────────────────────────── */
.table-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: .875rem; }
thead tr { background: var(--surface-1); }
thead th {
  padding: 12px 16px; text-align: left;
  font-family: 'Sora', sans-serif; font-weight: 600; font-size: .8125rem;
  color: var(--text-muted); text-transform: uppercase; letter-spacing: .05em;
  border-bottom: 1px solid var(--border); white-space: nowrap;
}
tbody td { padding: 12px 16px; border-bottom: 1px solid var(--border); vertical-align: middle; }
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover td { background: var(--surface-1); }

/* ── Alertes ─────────────────────────────────────────────── */
.alert {
  padding: 14px 18px; border-radius: var(--radius-md);
  font-size: .875rem; display: flex; align-items: flex-start; gap: 10px;
  border-left: 4px solid;
}
.alert-success { background: var(--success-dim); border-color: var(--success); color: #155724; }
.alert-warning { background: var(--warning-dim); border-color: var(--warning); color: #856404; }
.alert-error   { background: var(--error-dim);   border-color: var(--error);   color: #721c24; }
.alert-info    { background: #e8f4f8;             border-color: var(--info);    color: #055160; }

/* ── Divider ─────────────────────────────────────────────── */
.divider { border: none; border-top: 1px solid var(--border); margin: 24px 0; }

/* ── Skeleton loader ─────────────────────────────────────── */
.skeleton {
  background: linear-gradient(90deg, var(--surface-1) 25%, var(--surface-2) 50%, var(--surface-1) 75%);
  background-size: 200% 100%; animation: shimmer 1.4s infinite; border-radius: var(--radius-sm);
}
@keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

/* ── HTMX indicators ─────────────────────────────────────── */
.htmx-indicator { opacity: 0; transition: opacity .2s; }
.htmx-request .htmx-indicator { opacity: 1; }
.htmx-request.htmx-indicator  { opacity: 1; }

/* ====================================================
   PORTAL — Portail client public
   ==================================================== */

.portal-nav {
  position: sticky; top: 0; z-index: 100;
  height: var(--nav-h);
  background: var(--surface-0);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
}
.portal-nav-inner {
  max-width: 1200px; margin: 0 auto;
  height: 100%; padding: 0 24px;
  display: flex; align-items: center; justify-content: space-between;
}
.portal-logo {
  display: flex; align-items: center; gap: 10px;
  font-family: 'Sora', sans-serif; font-weight: 700;
  font-size: 1.25rem; color: var(--secondary);
  text-decoration: none;
}
.portal-logo span { color: var(--primary); }
.portal-logo-icon {
  width: 36px; height: 36px; border-radius: var(--radius-sm);
  background: var(--primary); display: flex; align-items: center; justify-content: center;
  color: #fff; font-weight: 700; font-size: 1rem;
}
.portal-nav-links { display: flex; align-items: center; gap: 8px; }
.portal-nav-link {
  font-size: .9rem; font-weight: 500; color: var(--text-muted);
  padding: 7px 14px; border-radius: var(--radius-full);
  transition: var(--transition); text-decoration: none;
}
.portal-nav-link:hover { background: var(--surface-1); color: var(--text-main); }
/* `--primary` (#2F80ED) ne tient que 3,44:1 sur `--primary-dim` et 3,87:1 sous du blanc :
   c'est le défaut que `--blue-aa` corrige déjà dans la boutique, et que la barre de
   navigation avait gardé — le harnais l'excluait de son périmètre, donc personne ne le
   mesurait. */
.portal-nav-link.active { color: var(--blue-aa); background: var(--primary-dim); }
.portal-nav-link.btn-nav {
  background: var(--blue-aa); color: #fff; padding: 7px 18px;
}
.portal-nav-link.btn-nav:hover { background: var(--blue-aa-hover); color: #fff; }

.portal-main { max-width: 1200px; margin: 0 auto; padding: 48px 24px; }

.portal-hero {
  text-align: center; padding: 80px 24px 64px;
  max-width: 700px; margin: 0 auto;
}
.portal-hero h1 { font-size: 2.5rem; color: var(--secondary); margin-bottom: 16px; }
.portal-hero p { font-size: 1.1rem; color: var(--text-muted); margin-bottom: 32px; }
.portal-hero-actions { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }

.portal-section { margin-bottom: 56px; }
.portal-section-title {
  font-family: 'Sora', sans-serif; font-weight: 700;
  font-size: 1.375rem; color: var(--secondary); margin-bottom: 24px;
  padding-bottom: 12px; border-bottom: 2px solid var(--border);
  display: flex; align-items: center; justify-content: space-between;
}
.portal-section-title span { color: var(--primary); }

.portal-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 20px;
}
.portal-service-card {
  background: var(--surface-0); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 24px;
  transition: box-shadow var(--transition), transform var(--transition);
  cursor: pointer;
}
.portal-service-card:hover {
  box-shadow: var(--shadow-md); transform: translateY(-2px);
}
.portal-service-card h3 { font-size: 1rem; color: var(--secondary); margin-bottom: 8px; }
.portal-service-card .price {
  font-size: 1.25rem; font-weight: 700; color: var(--primary);
  font-family: 'Sora', sans-serif;
}
.portal-service-card .unit { font-size: .8125rem; color: var(--text-muted); margin-top: 4px; }

.portal-branch-tabs {
  display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 28px;
}
.portal-branch-tab {
  padding: 8px 18px; border-radius: var(--radius-full);
  font-size: .875rem; font-weight: 500; cursor: pointer;
  border: 1.5px solid var(--border); background: var(--surface-0);
  color: var(--text-muted); transition: var(--transition);
}
.portal-branch-tab:hover,
.portal-branch-tab.htmx-request { background: var(--surface-1); }
.portal-branch-tab[aria-selected="true"] {
  background: var(--primary); border-color: var(--primary);
  color: #fff;
}

.portal-order-row {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 0; border-bottom: 1px solid var(--border); gap: 16px;
}
.portal-order-row:last-child { border-bottom: none; }
.portal-order-type { font-weight: 600; font-size: .9375rem; color: var(--secondary); }
.portal-order-meta { font-size: .8125rem; color: var(--text-muted); margin-top: 3px; }

.portal-footer {
  background: var(--secondary); color: rgba(255,255,255,.6);
  text-align: center; padding: 32px 24px; font-size: .8125rem;
  margin-top: 80px;
}
.portal-footer a { color: rgba(255,255,255,.7); }
.portal-footer strong { color: #fff; }

/* ====================================================
   BACK-OFFICE — Admin / Kanban
   ==================================================== */

.bo-layout {
  display: flex; min-height: 100vh;
  color: var(--text-main) !important;
}


.bo-sidebar {
  width: var(--sidebar-w); min-height: 100vh;
  background: var(--secondary); position: sticky; top: 0;
  display: flex; flex-direction: column; flex-shrink: 0;
  overflow-y: auto;
}
.bo-sidebar-logo {
  height: var(--nav-h); display: flex; align-items: center; gap: 10px;
  padding: 0 20px; border-bottom: 1px solid rgba(255,255,255,.07);
}
.bo-sidebar-logo-icon {
  width: 32px; height: 32px; background: var(--primary); border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center;
  font-family: 'Sora', sans-serif; font-weight: 700; font-size: .9rem; color: #fff;
}
.bo-sidebar-brand {
  font-family: 'Sora', sans-serif; font-weight: 700;
  font-size: .9375rem; color: var(--text-main) !important;
}
.bo-sidebar-brand small { display: block; font-size: .7rem; font-weight: 400; color: var(--text-muted) !important; }
.bo-sidebar-nav { padding: 16px 0; flex: 1; }
.bo-sidebar-section {
  padding: 8px 16px 4px; font-size: .65rem; font-weight: 600;
  color: var(--text-muted) !important; text-transform: uppercase; letter-spacing: .08em;
}
.bo-sidebar-link {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 20px; font-size: .875rem; font-weight: 500;
  color: var(--text-muted) !important; text-decoration: none;
  transition: var(--transition); border-left: 3px solid transparent;
}
.bo-sidebar-link:hover { background: rgba(255,255,255,.06); color: var(--text-main) !important; }
.bo-sidebar-link.active {
  background: rgba(47, 128, 237, 0.15); color: #fff !important;
  border-left-color: var(--blue);
}
.bo-sidebar-link svg, .bo-sidebar-link .icon { flex-shrink: 0; }
.bo-sidebar-footer {
  padding: 16px 20px; border-top: 1px solid var(--border);
  font-size: .8rem; color: var(--text-muted) !important;
}

.bo-main { flex: 1; display: flex; flex-direction: column; overflow: hidden; }

.bo-topbar {
  height: var(--nav-h); background: var(--surface-0);
  border-bottom: 1px solid var(--border);
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 28px; gap: 16px; position: sticky; top: 0; z-index: 50;
  box-shadow: var(--shadow-sm); flex-shrink: 0;
}
.bo-topbar-title {
  font-family: 'Sora', sans-serif; font-weight: 700;
  font-size: 1.125rem; color: var(--secondary);
  white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; max-width: 55vw;
  flex-shrink: 1; min-width: 0;
}
.bo-topbar-actions { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
.bo-avatar {
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--primary); color: #fff;
  font-weight: 600; font-size: .875rem;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; flex-shrink: 0;
}

.bo-content { flex: 1; padding: 28px; overflow-y: auto; max-width: 1440px; margin: 0 auto; width: 100%; }
.bo-content-header {
  display: flex; align-items: flex-start; justify-content: space-between;
  margin-bottom: 24px; gap: 16px; flex-wrap: wrap;
}
.bo-content-header h1 { font-size: 1.5rem; color: var(--secondary); }

/* Stats row */
.bo-stats { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 16px; margin-bottom: 28px; }
.bo-stat-card {
  background: var(--surface-0); border: 1px solid var(--border);
  border-radius: var(--radius-card); padding: 20px; box-shadow: var(--shadow-sm);
}
.bo-stat-card .stat-label {
  font-size: .75rem; font-weight: 600; color: var(--text-muted);
  text-transform: uppercase; letter-spacing: .06em; margin-bottom: 8px;
}
.bo-stat-card .stat-value {
  font-family: 'Sora', sans-serif; font-weight: 700;
  font-size: 2.1rem; color: var(--secondary); line-height: 1;
}
.bo-stat-card .stat-sub { font-size: .8rem; color: var(--text-muted); margin-top: 6px; }
.bo-stat-card.accent { border-left: 4px solid var(--primary); }
.bo-stat-card.success { border-left: 4px solid var(--success); }
.bo-stat-card.warning { border-left: 4px solid var(--warning); }
.bo-stat-card.info { border-left: 4px solid var(--cyan); }
.bo-stat-card.error { border-left: 4px solid var(--error); }

/* Filtre bar */
.bo-filter-bar {
  background: var(--surface-0); border: 1px solid var(--border);
  border-radius: var(--radius-card); padding: 16px 20px;
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
  margin-bottom: 24px; box-shadow: var(--shadow-sm);
}
.bo-filter-bar .form-control { width: auto; min-width: 160px; }

/* Kanban */
.kanban-board {
  display: flex; gap: 14px; overflow-x: auto;
  padding-bottom: 16px; align-items: flex-start;
}
.kanban-col {
  flex: 0 0 270px; min-width: 270px;
  background: var(--surface-1); border-radius: var(--radius-md);
  border: 1px solid var(--border);
}
.kanban-col-header {
  padding: 14px 16px; border-bottom: 1px solid var(--border);
  display: flex; align-items: center; justify-content: space-between;
}
.kanban-col-title {
  font-family: 'Sora', sans-serif; font-weight: 600;
  font-size: .875rem; color: var(--secondary); display: flex; align-items: center; gap: 8px;
}
.kanban-col-title .dot {
  width: 8px; height: 8px; border-radius: 50%;
}
.kanban-count {
  font-size: .75rem; font-weight: 600; padding: 2px 8px;
  background: var(--surface-2); border-radius: var(--radius-full);
  color: var(--text-muted);
}
.kanban-cards { padding: 10px; min-height: 120px; display: flex; flex-direction: column; gap: 8px; }

/* Kanban card */
.kanban-card {
  background: var(--surface-0); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 14px; box-shadow: var(--shadow-sm);
  transition: box-shadow var(--transition), transform var(--transition);
}
.kanban-card:hover { box-shadow: var(--shadow-md); transform: translateY(-1px); }
.kanban-card-id {
  font-size: .7rem; font-weight: 600; color: var(--text-light);
  font-family: 'Sora', sans-serif; margin-bottom: 6px;
  display: flex; align-items: center; justify-content: space-between;
}
.kanban-card-title {
  font-size: .875rem; font-weight: 600; color: var(--secondary);
  margin-bottom: 4px;
}
.kanban-card-client { font-size: .8rem; color: var(--text-muted); margin-bottom: 10px; }
.kanban-card-footer {
  display: flex; align-items: center; justify-content: space-between;
  margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border);
}
.kanban-card-assignee {
  font-size: .75rem; color: var(--text-muted);
  display: flex; align-items: center; gap: 5px;
}
.kanban-card-form { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border); }
.kanban-card-form form { display: flex; align-items: center; gap: 6px; }
.kanban-card-form select { font-size: .8rem; padding: 5px 8px; flex: 1; }

/* Dots couleurs par statut Kanban */
.dot-NEW        { background: var(--text-light); }
.dot-ANALYSIS   { background: #1565C0; }
.dot-QUOTE      { background: #7B1FA2; }
.dot-IN_PROGRESS{ background: var(--warning); }
.dot-DONE       { background: var(--success); }
.dot-DESIGN     { background: #7B1FA2; }
.dot-PRODUCTION { background: var(--warning); }
.dot-REVIEW     { background: #E65100; }
.dot-READY      { background: var(--success); }
.dot-DELIVERED  { background: var(--primary); }
.dot-CANCELLED  { background: var(--error); }

/* Detail commande */
.bo-detail-grid {
  display: grid; grid-template-columns: 2fr 1fr; gap: 20px; align-items: start;
}
@media (max-width: 1024px) { .bo-detail-grid { grid-template-columns: 1fr; } }

.bo-timeline { list-style: none; position: relative; padding-left: 20px; }
.bo-timeline::before {
  content: ''; position: absolute; left: 5px; top: 6px; bottom: 0;
  width: 2px; background: var(--border);
}
.bo-timeline li {
  position: relative; padding: 0 0 20px 20px; font-size: .875rem;
}
.bo-timeline li::before {
  content: ''; position: absolute; left: -4px; top: 4px;
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--primary); border: 2px solid var(--surface-0);
  box-shadow: 0 0 0 2px var(--primary);
}
.bo-timeline .tl-time { font-size: .75rem; color: var(--text-muted); margin-bottom: 2px; }
.bo-timeline .tl-label { font-weight: 600; color: var(--secondary); }
.bo-timeline .tl-by { font-size: .8rem; color: var(--text-muted); }

/* Réseau */
.device-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px;
}
.device-card {
  background: var(--surface-0); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 16px; box-shadow: var(--shadow-sm);
  display: flex; flex-direction: column; gap: 8px;
}
.device-card-name { font-weight: 600; font-size: .9375rem; color: var(--secondary); }
.device-card-type { font-size: .75rem; color: var(--text-muted); }
.device-status {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: .75rem; font-weight: 600;
}
.device-status::before {
  content: ''; width: 7px; height: 7px; border-radius: 50%;
}
.device-status.online  { color: var(--success); }
.device-status.online::before  { background: var(--success); }
.device-status.offline { color: var(--text-muted); }
.device-status.offline::before { background: var(--text-light); }

/* ── Ticket de caisse ──────────────────────────────────────── */
.receipt-wrap {
  max-width: 380px; margin: 48px auto; padding: 24px;
  font-family: 'Inter', monospace; font-size: .875rem;
}
.receipt-header { text-align: center; margin-bottom: 20px; border-bottom: 1px dashed var(--border); padding-bottom: 16px; }
.receipt-header h1 { font-size: 1.25rem; }
.receipt-row { display: flex; justify-content: space-between; padding: 4px 0; }
.receipt-row.total { font-weight: 700; font-size: 1rem; border-top: 1px dashed var(--border); margin-top: 8px; padding-top: 8px; }
.receipt-qr { text-align: center; margin-top: 20px; }
.receipt-footer { text-align: center; margin-top: 16px; font-size: .75rem; color: var(--text-muted); }

/* ── Login allauth ──────────────────────────────────────────── */
.auth-wrap {
  min-height: 100vh; display: flex; align-items: center;
  justify-content: center; background: var(--surface-1); padding: 24px;
}
.auth-card {
  width: 100%; max-width: 420px; background: var(--surface-0);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); overflow: hidden;
}
.auth-card-header {
  background: var(--secondary); padding: 32px 36px 24px; text-align: center;
}
.auth-card-logo { font-family: 'Sora', sans-serif; font-weight: 700; font-size: 1.5rem; color: #fff; }
.auth-card-logo span { color: var(--primary); }
.auth-card-body { padding: 32px 36px; }
.auth-card-body h2 { font-size: 1.25rem; margin-bottom: 6px; }
.auth-card-body p.sub { font-size: .875rem; margin-bottom: 24px; }
.auth-card-body .form-group + .form-group { margin-top: 16px; }

/* ── Responsive ─────────────────────────────────────────────── */
/* ── Lisibilité / contraste (correctifs thème sombre) ─────── */
/* Boîtes de sélection : fond et texte contrastés (+ options natives) */
select, select.form-control, select.form-input, select.input {
  background-color: var(--surface-0); color: var(--text-main);
}
select option { background-color: #ffffff; color: #0f172a; }
body.portal-body select, body.bo-dark select { background-color: rgba(15,44,73,.92); color: #F1F5F9; }
body.portal-body select option, body.bo-dark select option { background-color: #0f2c49; color: #F1F5F9; }
/* Liens de contenu (non stylés) : lisibles sur fond sombre (cyan clair au lieu de bleu-sur-navy) */
body.portal-body a:not([class]), body.bo-dark a:not([class]),
body.portal-body a.link, body.bo-dark a.link {
  color: var(--cyan);
}
body.portal-body a:not([class]):hover, body.bo-dark a:not([class]):hover { color: #a5e3fb; }

/* Navigation mobile back-office (drawer) */
.bo-hamburger {
  display: none; align-items: center; justify-content: center;
  width: 40px; height: 40px; border: none; background: transparent;
  color: var(--text-main); cursor: pointer; border-radius: 8px; margin-right: 6px; flex-shrink: 0;
}
.bo-hamburger:hover { background: var(--surface-2); }
.bo-drawer-overlay { display: none; position: fixed; inset: 0; background: rgba(8,20,35,.55); z-index: 250; }

/* Navigation mobile basse (Bottom-nav) */
.bo-bottomnav { display: none; }

@media (max-width: 768px) {
  .bo-sidebar {
    position: fixed; top: 0; left: 0; height: 100vh; z-index: 300;
    transform: translateX(-100%); transition: transform .28s ease;
    box-shadow: 2px 0 24px rgba(0,0,0,.45);
  }
  .bo-sidebar.bo-drawer-open { transform: translateX(0); }
  .bo-drawer-overlay.bo-drawer-open { display: block; }
  .bo-hamburger { display: inline-flex; }
  .bo-topbar { padding: 0 14px; }
  .bo-topbar-title { max-width: none; }
  .bo-content { padding: 0; }
  /* Tableaux de données : scroll horizontal au lieu de déborder l'écran */
  .bo-content table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; white-space: nowrap; }
  .table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  /* Confort tactile : cibles ≥ 44px */
  .btn, .form-control, select.form-control, input.form-control { min-height: 44px; }
  .bo-sidebar-link { min-height: 44px; }
  /* M3 — Tableaux denses en cartes empilées sur mobile */
  .bo-content .table-cards { white-space: normal; }
  .bo-content .table-cards thead { display: none; }
  /* Le `tr` passe en `display: block`, mais le `tbody` restait un groupe de lignes de
     tableau : la carte se dimensionnait alors sur son CONTENU, pas sur son conteneur.
     D'où des cartes de largeurs différentes dans deux sections voisines du même écran
     (317 px pour les débiteurs, 265 px pour les créditeurs — mesuré à 393 px) et une
     bande blanche morte le long du bord droit, qui se lit comme un bug d'affichage. */
  .bo-content .table-cards tbody { display: block; width: 100%; }
  .bo-content .table-cards tr {
    display: block; width: 100%; border: 1px solid var(--border); border-radius: 12px;
    padding: 4px 14px; margin-bottom: 12px; background: var(--surface-0);
  }
  .bo-content .table-cards td {
    display: flex; justify-content: space-between; align-items: center; gap: 12px;
    text-align: right; border: none; border-bottom: 1px solid var(--border); padding: 10px 0; white-space: normal;
  }
  .bo-content .table-cards tr td:last-child { border-bottom: none; }
  .bo-content .table-cards td::before {
    content: attr(data-label); font-weight: 600; color: var(--text-muted); text-align: left; flex-shrink: 0;
  }
  .bo-content .table-cards td[colspan]::before { content: ""; }
  /* Cellule complexe (formulaire, groupe de boutons) : pleine largeur empilée sur mobile */
  .bo-content .table-cards td.cell-stack {
    display: block; text-align: left;
  }
  .bo-content .table-cards td.cell-stack::before { display: block; margin-bottom: 8px; }
  .bo-content .table-cards td.cell-stack > div { justify-content: flex-start !important; }
  .bo-content .table-cards td.cell-stack form { flex: 1; }
  .bo-content .table-cards td.cell-stack .form-control { flex: 1; width: auto; }
  /* M3 — Titres fluides */
  h1 { font-size: clamp(1.4rem, 5.5vw, 2rem); }
  h2 { font-size: clamp(1.15rem, 4.5vw, 1.5rem); }
  /* M4 — Safe-area (encoches) */
  .bo-topbar { padding-left: max(14px, env(safe-area-inset-left)); padding-right: max(14px, env(safe-area-inset-right)); }
  .portal-nav-inner { padding-left: max(16px, env(safe-area-inset-left)); padding-right: max(16px, env(safe-area-inset-right)); }
  .portal-hero h1 { font-size: 1.75rem; }
  .kanban-board { flex-direction: row; overflow-x: auto; -webkit-overflow-scrolling: touch; scroll-snap-type: x proximity; padding-bottom: 8px; }
  .kanban-col { flex: 0 0 82vw; min-width: 260px; max-width: 320px; scroll-snap-align: start; }
  .bo-content { padding: 16px; padding-bottom: max(84px, calc(60px + env(safe-area-inset-bottom, 0px))); }
  .portal-main { padding: 28px 16px; }

  /* M5 — Bottom nav */
  .bo-bottomnav {
    display: flex;
    justify-content: space-around;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    background: var(--surface-1);
    border-top: 1px solid var(--border);
    z-index: 1000;
    padding-bottom: env(safe-area-inset-bottom);
  }
  .bo-bottomnav-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    padding: 8px 4px;
    flex: 1;
    min-height: 56px;
    color: var(--text-muted);
    font-size: 0.65rem;
    font-weight: 600;
    text-transform: uppercase;
    text-decoration: none;
    background: transparent;
    border: none;
    cursor: pointer;
  }
  .bo-bottomnav-item.active {
    color: var(--primary);
  }
  .bo-bottomnav-item svg {
    margin-bottom: 2px;
  }
  /* Bug fix caisse : barre auto-masquée au scroll + lisibilité renforcée (coordination du FAB panier dans pos_cashier.html) */
  .bo-bottomnav {
    transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
    box-shadow: 0 -3px 14px rgba(0, 0, 0, 0.14);
  }
  body.bottomnav-hidden .bo-bottomnav { transform: translateY(110%); }

  /* P0: Masquer le badge WA et le libellé conn-status (évite le débordement de la topbar droite) */
  .bo-topbar-wa-status { display: none !important; }
  #bo-conn-text { display: none; }
  /* P1b: Masquer le hamburger — la bottom-nav a déjà un bouton Menu pour le drawer */
  .bo-hamburger { display: none !important; }

  /* Services liste mobile — fiches compactes (nom + prix + statut) */
  .table-services td[data-label="Nom"] { display: block; text-align: left; border-bottom: none; padding-bottom: 2px; }
  .table-services td[data-label="Nom"]::before { display: none; }
  .table-services td[data-label="Nom"] .cell-lead { justify-content: flex-start; }
  .table-services td[data-label="Unité"],
  .table-services td[data-label="Vente rapide"] { display: none; }

  /* Catalogue liste mobile — fiches compactes */
  .table-catalogue td[data-label="Nom"]::before { display: none; }
  .table-catalogue td[data-label="Nom"] { padding-bottom: 0; border-bottom: none; }
  .table-catalogue td[data-label="Unité"] { display: none; }
  .table-catalogue td[data-label="Type"],
  .table-catalogue td[data-label="Secteur"] {
    display: inline-flex; width: auto; border-bottom: none;
    padding: 2px 0; margin-right: 8px; font-size: 0.82rem;
  }
  .table-catalogue td[data-label="Type"]::after { content: " ·"; margin-left: 4px; color: var(--text-light); }
  /* Cibles tactiles catalogue ≥ 44px */
  .table-catalogue td[data-label="Actions"] .btn {
    min-height: 44px; padding: 10px 12px;
    display: inline-flex; align-items: center; gap: 5px;
  }
}

/* ── Utilitaires ────────────────────────────────────────────── */
.flex { display: flex; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.gap-2 { gap: 8px; }
.gap-3 { gap: 12px; }
.gap-4 { gap: 16px; }
.mt-1 { margin-top: 4px; }
.mt-2 { margin-top: 8px; }
.mt-4 { margin-top: 16px; }
.mt-6 { margin-top: 24px; }
.mb-2 { margin-bottom: 8px; }
.mb-4 { margin-bottom: 16px; }
.mb-6 { margin-bottom: 24px; }
.p-4 { padding: 16px; }
.text-muted { color: var(--text-muted); font-size: .875rem; }
.text-sm { font-size: .875rem; }
.text-xs { font-size: .75rem; }
.font-bold { font-weight: 700; }
.font-medium { font-weight: 500; }
.text-primary { color: var(--primary); }
.text-secondary { color: var(--secondary); }
.truncate { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.w-full { width: 100%; }
.hidden { display: none; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); }

/* ====================================================
   PORTAL — Améliorations v2.0 (standard NEXUS)
   ==================================================== */

/* ── Menu mobile responsive ────────────────────────── */
.portal-mobile-link {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 16px; border-radius: var(--radius-md);
  color: var(--text-muted); font-size: .9rem; font-weight: 500;
  text-decoration: none; transition: var(--transition);
  border-left: 3px solid transparent;
  min-height: var(--size-touch);   /* cible tactile 44px garantie */
}
.portal-mobile-link:hover {
  background: var(--primary-ultra-dim);
  color: var(--text-main);
}
/* Même défaut que `.portal-nav-link` plus haut, et même correctif : `--primary` (#2F80ED) ne
   tient que 3,44:1 sur `--primary-dim`. Le bandeau desktop avait été rattrapé, le menu mobile
   oublié — alors que c'est la SEULE navigation du téléphone. Le trait de gauche reste sur
   `--primary` : c'est une bordure décorative, elle ne porte aucun texte. */
.portal-mobile-link.nav-mobile-active {
  border-left-color: var(--primary);
  background: var(--primary-dim);
  color: var(--blue-aa);
  font-weight: 600;
}

/* Le bouton plein du menu mobile héritait de `.btn-primary`, donc de `--primary` : blanc sur
   #2F80ED = 3,87:1, sous AA. On ne touche pas `.btn-primary` lui-même, qui doit rester
   pilotable depuis `/backoffice/apparence/` (ThemeConfig) ; on aligne ce bouton-ci sur le
   traitement déjà appliqué à son jumeau du bandeau desktop, `.portal-nav-link.btn-nav`. */
.portal-mobile-cta { background: var(--blue-aa); color: #fff; }
.portal-mobile-cta:hover { background: var(--blue-aa-hover); color: #fff; }

/* ── Footer 3 colonnes ─────────────────────────────── */
.portal-footer {
  background: var(--surface-0);
  border-top: 1px solid var(--border);
  margin-top: 5rem;
  padding: 0;
}
.portal-footer-inner {
  max-width: 1200px; margin: 0 auto; padding: 48px 24px;
}
.portal-footer-grid {
  display: flex; flex-wrap: wrap; gap: 40px;
  justify-content: space-between; align-items: flex-start;
}
/* Cibles tactiles ≥ 44 px. Ces liens n'avaient jamais été mesurés : le harnais excluait la
   barre de navigation et le pied de page de son périmètre, en les disant « audités
   ailleurs ». « Services » tenait dans 104 × 20 px, au pouce. */
.portal-footer a { display: inline-flex; align-items: center; min-height: 44px; }
.portal-logo { min-height: 44px; }
.portal-nav-link { display: inline-flex; align-items: center; min-height: 44px; }

.portal-footer-bottom {
  margin-top: 32px; padding-top: 24px;
  border-top: 1px solid var(--border);
  display: flex; flex-wrap: wrap;
  align-items: center; justify-content: space-between;
  gap: 8px; font-size: .75rem; color: var(--text-light);
}

/* ── Topbar back-office amélioré ───────────────────── */
.bo-topbar-actions {
  display: flex; align-items: center; gap: 12px; flex-shrink: 0;
}

/* ── Conn status (portail + back-office) ───────────── */
.conn-status-pill {
  display: flex; align-items: center; gap: 6px;
  padding: 4px 10px; border-radius: 999px;
  font-size: .75rem; font-weight: 600;
  background: rgba(40,167,69,.10); color: #28A745;
  transition: var(--transition);
}
.conn-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: #28A745; flex-shrink: 0;
}

/* ── Animate slide-down ────────────────────────────── */
@keyframes plSlideDown {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}
.animate-slide-down { animation: plSlideDown .2s var(--ease) forwards; }

/* ============================================================
   PORTAL DIGITAL GLASS SYSTEM (Phase 3)
   ============================================================ */
body.portal-body {
  background: linear-gradient(160deg, #122F4E 0%, #183A5E 55%, #0F2C49 100%) !important;
  background-attachment: fixed !important;
  color: #E2E8F0 !important;
  min-height: 100vh;
  position: relative;
}

body.portal-body, body.bo-dark .bo-layout, body.bo-dark .bo-content {
  --surface-0: rgba(255,255,255,0.06);
  --surface-1: rgba(255,255,255,0.03);
  --surface-2: rgba(255,255,255,0.09);
  --border: rgba(255,255,255,0.14);
  --text-main: #F1F5F9;
  --text-muted: #C0CBDA;
  --text-light: #93A2B8;
}


/* Background system with ambient halos and crystal motif */
.portal-bg-ambient {
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  z-index: -999;
  pointer-events: none;
  overflow: hidden;
}
.portal-crystal-overlay {
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  background-image: url("/static/images/brand/services/crystal.5098edd1c334.svg");
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
  opacity: 0.015;
  mix-blend-mode: overlay;
}
.portal-halo {
  position: absolute;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.10;
}
.portal-halo-1 {
  top: -10%; left: -10%;
  width: 45vw; height: 45vw;
  background: var(--blue);
}
.portal-halo-2 {
  top: 40%; right: -10%;
  width: 40vw; height: 40vw;
  background: var(--violet);
}
.portal-halo-3 {
  bottom: -10%; left: 20%;
  width: 35vw; height: 35vw;
  background: var(--cyan);
}

body.portal-body .portal-nav {
  background: rgba(8, 24, 39, 0.6) !important;
  backdrop-filter: blur(var(--glass-blur)) saturate(140%) !important;
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(140%) !important;
  border-bottom: 1px solid var(--glass-border) !important;
  box-shadow: var(--glass-shadow) !important;
}

body.portal-body .portal-logo {
  color: #FFFFFF !important;
}

body.portal-body .portal-logo-icon {
  background: var(--blue) !important;
}

body.portal-body .portal-nav-link {
  color: #CBD5E1 !important;
}

body.portal-body .portal-nav-link:hover {
  background: rgba(255, 255, 255, 0.05) !important;
  color: #FFFFFF !important;
}

body.portal-body .portal-nav-link.nav-active {
  color: #FFFFFF !important;
  background: var(--blue) !important;
}

body.portal-body .portal-section-title {
  color: #FFFFFF !important;
  border-bottom: 2px solid rgba(255, 255, 255, 0.1) !important;
}

/* Category Cards in Catalogue page */
.portal-category-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
  margin-bottom: 40px;
}

.portal-category-card {
  /* La section « Nos Services » est sur fond BLANC : carte claire + titre foncé lisible.
     (Les variantes -imprimerie/-pressing/… ont un fond photo sombre → texte blanc réaffirmé
     dans le <style> du template.) */
  background: #FFFFFF;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: 24px 20px;
  text-align: center;
  transition: all var(--transition);
  cursor: pointer;
  box-shadow: var(--shadow-xs);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  color: var(--secondary);
}

.portal-category-card:hover {
  background: var(--bg-subtle, #F8FAFC);
  border-color: var(--primary);
  transform: translateY(-3px);
  box-shadow: 0 10px 30px rgba(47, 128, 237, 0.15);
}

.portal-category-card.active {
  background: rgba(47, 128, 237, 0.10);
  border-color: var(--primary);
  box-shadow: 0 0 0 2px rgba(47, 128, 237, 0.25);
}

.portal-category-icon {
  width: 48px;
  height: 48px;
  color: var(--cyan);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform var(--transition);
}

.portal-category-card:hover .portal-category-icon {
  transform: scale(1.1);
}

.portal-category-title {
  font-family: 'Sora', sans-serif;
  font-weight: 700;
  font-size: 1rem;
}

/* Service cards with glassmorphism */
body.portal-body .portal-service-card {
  background: var(--glass-bg) !important;
  border: 1px solid var(--glass-border) !important;
  backdrop-filter: blur(var(--glass-blur)) saturate(140%) !important;
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(140%) !important;
  border-radius: var(--radius-card) !important;
  padding: 24px !important;
  box-shadow: var(--glass-shadow) !important;
  transition: all var(--transition);
  color: #E2E8F0 !important;
  display: flex;
  flex-direction: column;
}

body.portal-body .portal-service-card:hover {
  background: rgba(255, 255, 255, 0.15) !important;
  border-color: rgba(255, 255, 255, 0.25) !important;
  transform: translateY(-4px);
  box-shadow: 0 12px 30px rgba(47, 128, 237, 0.25) !important;
}

body.portal-body .portal-service-card h3 {
  color: #FFFFFF !important;
  font-size: 1.125rem !important;
  margin-bottom: 8px !important;
}

body.portal-body .portal-service-card p {
  color: #94A3B8 !important;
  line-height: 1.5;
}

body.portal-body .portal-service-card .price {
  color: var(--cyan) !important;
  font-size: 1.35rem !important;
  margin-top: auto;
  padding-top: 12px;
}

body.portal-body .portal-service-card .unit {
  color: #64748B !important;
}

body.portal-body .card {
  background: var(--glass-bg) !important;
  border: 1px solid var(--glass-border) !important;
  backdrop-filter: blur(var(--glass-blur)) saturate(140%) !important;
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(140%) !important;
  border-radius: var(--radius-card) !important;
  box-shadow: var(--glass-shadow) !important;
  color: #E2E8F0 !important;
}

body.portal-body .card-header {
  border-bottom: 1px solid var(--glass-border) !important;
  color: #FFFFFF !important;
  font-family: 'Sora', sans-serif;
  font-weight: 700;
  background: transparent !important;
}

body.portal-body .card-body {
  color: #CBD5E1 !important;
}

body.portal-body .form-control,
body.portal-body .form-input,
body.portal-body .input {
  background: var(--glass-bg) !important;
  border: 1px solid var(--glass-border) !important;
  backdrop-filter: blur(var(--glass-blur)) saturate(140%) !important;
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(140%) !important;
  color: #FFFFFF !important;
}

body.portal-body .form-control:focus,
body.portal-body .form-input:focus,
body.portal-body .input:focus {
  border-color: var(--blue) !important;
  box-shadow: 0 0 0 2px rgba(47, 128, 237, 0.25) !important;
}

body.portal-body h1,
body.portal-body h2,
body.portal-body h3,
body.portal-body h4 {
  color: #FFFFFF !important;
}

/* Helper overrides for inline styles and elements in portal */
body.portal-body [style*="background: var(--surface-0)"],
body.portal-body [style*="background:var(--surface-0)"] {
  background: var(--glass-bg) !important;
  border-color: var(--glass-border) !important;
  backdrop-filter: blur(var(--glass-blur)) saturate(140%) !important;
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(140%) !important;
  color: #FFFFFF !important;
}
body.portal-body [style*="color: var(--text-main)"],
body.portal-body [style*="color:var(--text-main)"] {
  color: #CBD5E1 !important;
}
body.portal-body [style*="background:var(--surface-1)"],
body.portal-body [style*="background: var(--surface-1)"] {
  background: rgba(255, 255, 255, 0.04) !important;
  border-color: var(--glass-border) !important;
}
body.portal-body [style*="border:1.5px solid var(--border)"] {
  border-color: var(--glass-border) !important;
  background: var(--glass-bg) !important;
}
body.portal-body [style*="border:1.5px solid var(--primary)"] {
  border-color: var(--blue) !important;
  background: rgba(47, 128, 237, 0.1) !important;
}

/* ── Back-office styling overrides (Phase 5 / Dark Glass) ──────────────── */
.bo-topbar, .bo-sidebar {
  background: var(--glass-panel-bg) !important;
  backdrop-filter: blur(var(--glass-blur)) saturate(150%) !important;
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(150%) !important;
  border-bottom: 1px solid var(--glass-border) !important;
  box-shadow: var(--glass-highlight) !important;
}

.bo-sidebar {
  border-right: 1px solid var(--glass-border) !important;
}

.bo-topbar-title {
  color: var(--text-main) !important;
}
.bo-content-header h1 {
  color: var(--text-main) !important;
}

.bo-card, .bo-stat-card, .bo-filter-bar, .kanban-col, .kanban-card, .device-card {
  background: var(--glass-bg) !important;
  border: 1px solid var(--glass-border) !important;
  backdrop-filter: blur(var(--glass-blur)) saturate(150%) !important;
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(150%) !important;
  box-shadow: var(--glass-shadow), var(--glass-highlight) !important;
  color: var(--text-main) !important;
  transition: all var(--transition);
}

.bo-card:hover, .bo-stat-card:hover, .kanban-card:hover, .device-card:hover {
  background: rgba(255, 255, 255, 0.18) !important;
  box-shadow: 0 12px 40px rgba(47, 128, 237, 0.3), var(--glass-highlight) !important;
}

.bo-stat-card .stat-value { color: var(--text-main) !important; }
.bo-stat-card .stat-label { color: var(--text-muted) !important; }

/* Sidebar */
.bo-sidebar-link.active {
  background: var(--active-link-bg) !important;
  color: var(--active-link-color) !important;
  border-left-color: var(--primary) !important;
  box-shadow: var(--active-link-shadow) !important;
}

/* Titles and labels */
.kanban-col-title, .kanban-card-title, .device-card-name {
  color: var(--text-main) !important;
}
.kanban-card-client, .kanban-card-assignee, .device-card-type, .text-muted, .bo-timeline .tl-time, .bo-timeline .tl-by {
  color: var(--text-muted) !important;
}
.bo-timeline .tl-label { color: var(--text-main) !important; }

/* Kanban columns */
.kanban-col-header {
  border-bottom: 1px solid var(--glass-border) !important;
}

body.bo-dark .bo-content thead tr {
  background: rgba(16, 42, 67, 0.6) !important;
}
body.bo-dark .bo-content thead th {
  color: #FFFFFF !important;
  border-bottom: 2px solid var(--blue) !important;
  font-family: 'Sora', sans-serif;
  font-weight: 600;
  padding: 14px 16px !important;
}
body.bo-dark .bo-content tbody td {
  padding: 14px 16px !important;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important;
  color: var(--text-main) !important;
}
body.bo-dark .bo-content tbody tr:hover td {
  background: rgba(255, 255, 255, 0.1) !important;
}

/* Accents pour Kanban dots */
.dot-ANALYSIS { background: var(--cyan) !important; }
.dot-QUOTE    { background: var(--violet) !important; }
.dot-IN_PROGRESS { background: var(--warning) !important; }
.dot-DONE     { background: var(--valid) !important; }
.dot-DESIGN   { background: var(--violet) !important; }
.dot-READY    { background: var(--valid) !important; }
.dot-DELIVERED { background: var(--blue) !important; }

/* Badge warning and error customization.
   ⚠️ Ce bloc en `!important` écrasait la règle accessible définie plus haut : c'est LUI
   qui gagnait, avec `var(--action)` (#F2994A) et `var(--error)` sur une teinte pâle. Le
   corriger seul en haut du fichier n'aurait rien changé à l'écran. */
.badge-warning {
  background: rgba(242, 153, 74, 0.12) !important;
  color: var(--ink-warning, #AB570C) !important;
}
.badge-error {
  background: rgba(220, 53, 69, 0.12) !important;
  color: var(--ink-error, #D01212) !important;
}

.bo-datefields-row { display: flex; gap: 12px; }
@media (max-width: 768px) {
  .bo-datefields-row { flex-direction: column; }
}

.po-line-row { display: flex; gap: 8px; align-items: flex-end; margin-bottom: 8px; }
@media (max-width: 768px) {
  .po-line-row { flex-direction: column; align-items: stretch; }
}

@media (max-width: 768px) {
  .portal-category-grid {
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  }
}

.badge-type { font-size:10px; padding:1px 6px; border-radius:10px; font-weight:600; display: inline-block; margin-top: 2px; }
.badge-article { background:#d1fae5; color:#065f46; }
.badge-commande { background:#fed7aa; color:#92400e; }
.stock-info { font-size:10px; color:#6b7280; display:block; margin-top: 2px; }

/* ============================================================
   Composants & utilitaires unifiés — v3.1 (audit UI 2026-07)
   Objectif : dédupliquer les styles inline récurrents.
   Hiérarchie des cartes :
     .card       → carte générique portail/public
     .bo-card    → carte back-office (surface + bordure + ombre)
     .bo-stat-card / .kpi-card (alias) → carte KPI/statistique
     .pos-card   → tuile service POS (spécifique caisse)
   ============================================================ */

/* Alias : .kpi-card === .bo-stat-card (ne réinventez plus) */
.kpi-card {
  background: var(--surface-0); border: 1px solid var(--border);
  border-radius: var(--radius-card); padding: 20px; box-shadow: var(--shadow-sm);
}

/* Grille de statistiques auto-fit (remplace les minmax en dur) */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--grid-min-sm), 1fr)); gap: 16px; margin-bottom: 28px; }
.stat-grid.compact { grid-template-columns: repeat(auto-fill, minmax(var(--grid-min-xs), 1fr)); }
.stat-grid.wide { grid-template-columns: repeat(auto-fill, minmax(var(--grid-min-md), 1fr)); }

/* Avatar cercle + initiales (remplace 15+ styles inline) */
.avatar, .avatar-sm {
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0; border-radius: 50%; font-weight: 600; color: #fff;
  background: var(--primary); text-transform: uppercase; line-height: 1;
}
.avatar { width: var(--size-avatar); height: var(--size-avatar); font-size: var(--font-size-sm); }
.avatar-sm { width: var(--size-avatar-sm); height: var(--size-avatar-sm); font-size: var(--font-size-xs); }

/* Message d'erreur de formulaire (remplace 28× color:var(--error);margin-top:4px) */
.form-error { color: var(--error); font-size: var(--font-size-xs); margin-top: 4px; }

/* Icônes SVG dimensionnées par token */
.icon { width: var(--size-icon); height: var(--size-icon); flex-shrink: 0; }
.icon-sm { width: var(--size-icon-sm); height: var(--size-icon-sm); flex-shrink: 0; }

/* Cible tactile garantie (mobile) */
.touch-target { min-height: var(--size-touch); min-width: var(--size-touch); }

/* Accessibilité clavier — anneau de focus visible partout */
:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
.btn:focus-visible, a:focus-visible { outline-offset: 3px; }

/* ════════════════════════════════════════════════════════════════════════
   Utilitaires de mise en page — complètent la charte (grille + flex + espacements).
   N'ajoutent QUE des classes absentes de la charte (aucun écrasement).
   Corrige le rendu des pages back-office écrites avec des classes de type Bootstrap.
   ════════════════════════════════════════════════════════════════════════ */
.row { display: flex; flex-wrap: wrap; margin-left: -8px; margin-right: -8px; }
.row > [class*="col"] { padding-left: 8px; padding-right: 8px; box-sizing: border-box; }
.col { flex: 1 1 0; min-width: 0; }
.col-md-1{flex:0 0 8.3333%;max-width:8.3333%}.col-md-2{flex:0 0 16.6667%;max-width:16.6667%}
.col-md-3{flex:0 0 25%;max-width:25%}.col-md-4{flex:0 0 33.3333%;max-width:33.3333%}
.col-md-5{flex:0 0 41.6667%;max-width:41.6667%}.col-md-6{flex:0 0 50%;max-width:50%}
.col-md-7{flex:0 0 58.3333%;max-width:58.3333%}.col-md-8{flex:0 0 66.6667%;max-width:66.6667%}
.col-md-9{flex:0 0 75%;max-width:75%}.col-md-10{flex:0 0 83.3333%;max-width:83.3333%}
.col-md-11{flex:0 0 91.6667%;max-width:91.6667%}.col-md-12{flex:0 0 100%;max-width:100%}
.col-lg-5{flex:0 0 41.6667%;max-width:41.6667%}.col-lg-7{flex:0 0 58.3333%;max-width:58.3333%}
@media (max-width: 820px){ .row > [class*="col"]{ flex:0 0 100%; max-width:100%; } }
.d-flex{display:flex}.d-block{display:block !important}.d-inline{display:inline}
.align-items-center{align-items:center}.align-items-end{align-items:flex-end}
.justify-content-end{justify-content:flex-end}.justify-content-between{justify-content:space-between}
.flex-grow-1{flex:1 1 auto}.flex-wrap{flex-wrap:wrap}
.text-right{text-align:right}.text-center{text-align:center}.text-left{text-align:left}
.text-uppercase{text-transform:uppercase;letter-spacing:.03em}.font-weight-bold{font-weight:700}
.small{font-size:.82em}.btn-block{display:block;width:100%}
.mr-1{margin-right:4px}.mr-3{margin-right:16px}.ml-1{margin-left:4px}.my-4{margin-top:24px;margin-bottom:24px}
.mb-1{margin-bottom:4px}.mb-3{margin-bottom:16px}
.p-3{padding:16px}.px-4{padding-left:24px;padding-right:24px}.py-3{padding-top:16px;padding-bottom:16px}.py-4{padding-top:24px;padding-bottom:24px}
.border{border:1px solid var(--border)}.border-0{border:0 !important}
.shadow-sm{box-shadow:var(--shadow-sm)}
/* Badges complémentaires (cohérents avec la charte) */
.badge-light{background:var(--surface-1,#f1f5f9);color:var(--text-muted);border:1px solid var(--border)}
.badge-secondary{background:var(--text-muted);color:#fff}
/* Le cyan Crystal est une couleur claire : du blanc dessus donnait 1,86:1 — le badge
   « Particulier » de la fiche client était délavé, presque fondu dans son fond. On garde
   le cyan comme TEINTE et on écrit dessus à l'encre, comme les autres badges. */
.badge-info{background:rgba(86,204,242,.16);color:var(--ink-info,#0C7799)}
/* Input fichier */
.form-control-file{font-size:.9rem;color:var(--text-muted)}

/* ════════════════════════════════════════════════════════════════════════
   DESIGN SYSTEM V2 — Back-office « SaaS premium »
   Squelette universel : Breadcrumb → Titre → Sous-titre → KPIs → Filtres →
   Contenu → Actions. Tout par tokens (aucune couleur en dur), zéro dépendance.
   ════════════════════════════════════════════════════════════════════════ */

/* ── En-tête de page (breadcrumb + titre Sora + sous-titre + action unique) ── */
.page-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;flex-wrap:wrap;margin-bottom:22px}
.page-head-txt{min-width:0}
.page-breadcrumb{display:flex;align-items:center;gap:6px;font-size:.75rem;color:var(--text-muted);margin-bottom:6px}
.page-breadcrumb a{color:var(--text-muted);text-decoration:none}
.page-breadcrumb a:hover{color:var(--primary)}
.page-breadcrumb .sep{opacity:.5}
.page-title{font-family:var(--font-title-family);font-weight:700;font-size:1.75rem;line-height:1.15;color:var(--secondary);margin:0}
.page-subtitle{color:var(--text-muted);font-size:.95rem;margin:4px 0 0;max-width:70ch}
.page-head-actions{display:flex;align-items:center;gap:10px;flex-shrink:0}

/* ── KPIs enrichis (étend .bo-stat-card : icône + variation) ── */
.kpi-head{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px}
.kpi-icon{width:38px;height:38px;border-radius:11px;display:flex;align-items:center;justify-content:center;
  background:var(--primary-dim,rgba(47,128,237,.12));color:var(--primary);font-size:1.1rem;flex-shrink:0}
.kpi-icon.success{background:rgba(16,185,129,.12);color:var(--success)}
.kpi-icon.warning{background:var(--warning-dim,rgba(242,153,74,.14));color:var(--warning)}
.kpi-icon.info{background:rgba(86,204,242,.14);color:var(--cyan)}
.kpi-icon.error{background:var(--error-dim,rgba(239,68,68,.12));color:var(--error)}
.kpi-trend{font-size:.72rem;font-weight:600;color:var(--text-muted)}
.kpi-trend.up{color:var(--success)}
.kpi-trend.down{color:var(--error)}

/* ── Pastilles de statut (point coloré + libellé) ── */
.dot{display:inline-block;width:8px;height:8px;border-radius:50%;flex-shrink:0}
.dot-on{background:var(--success)}
.dot-off{background:var(--text-muted)}
.dot-warn{background:var(--warning)}
.dot-err{background:var(--error)}
.status-pill{display:inline-flex;align-items:center;gap:7px;font-size:.78rem;font-weight:600;color:var(--text-main)}

/* ── Cellules « ligne-carte » (avatar + nom + slug secondaire, badges groupés) ── */
.cell-lead{display:flex;align-items:center;gap:11px;min-width:0}
.cell-logo{width:38px;height:38px;border-radius:10px;flex-shrink:0;display:flex;align-items:center;justify-content:center;
  background:var(--surface-1);border:1px solid var(--border);font-size:1.1rem;overflow:hidden}
.cell-logo img{width:100%;height:100%;object-fit:cover}
.cell-title{font-weight:600;color:var(--text-main);line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cell-sub{font-size:.74rem;color:var(--text-muted);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;margin-top:1px}
.badge-group{display:flex;flex-wrap:wrap;gap:5px;align-items:center}

/* ── Menu contextuel « ⋮ » (dropdown, JS délégué dans base.html) ── */
.bo-menu{position:relative;display:inline-block}
.bo-menu-toggle{background:none;border:1px solid transparent;border-radius:9px;width:34px;height:34px;cursor:pointer;
  color:var(--text-muted);font-size:1.2rem;line-height:1;display:flex;align-items:center;justify-content:center;transition:var(--transition)}
.bo-menu-toggle:hover{background:var(--surface-1);color:var(--text-main);border-color:var(--border)}
.bo-menu-list{position:absolute;right:0;top:calc(100% + 4px);min-width:186px;background:var(--surface-0);
  border:1px solid var(--border);border-radius:12px;box-shadow:0 12px 32px rgba(15,23,42,.16);
  padding:6px;z-index:60;display:none;opacity:0;transform:translateY(-6px);transition:opacity .14s,transform .14s}
.bo-menu.open .bo-menu-list{display:block;opacity:1;transform:translateY(0)}
.bo-menu-item{display:flex;align-items:center;gap:9px;width:100%;padding:9px 11px;border:0;background:none;
  border-radius:8px;font-size:.86rem;color:var(--text-main);text-align:left;text-decoration:none;cursor:pointer;font-family:inherit}
.bo-menu-item:hover{background:var(--surface-1)}
.bo-menu-item.danger{color:var(--error)}
.bo-menu-sep{height:1px;background:var(--border);margin:5px 0}

/* ── Overlay + drawer/modale custom (remplace définitivement les modales Bootstrap) ── */
.bo-overlay{position:fixed;inset:0;background:rgba(15,23,42,.55);backdrop-filter:blur(2px);z-index:300;
  display:none;align-items:center;justify-content:center;padding:20px;opacity:0;transition:opacity .2s}
.bo-overlay.open{display:flex;opacity:1}
.bo-modal{background:var(--surface-0);border-radius:18px;width:100%;max-width:620px;max-height:90vh;overflow:auto;
  box-shadow:0 24px 64px rgba(15,23,42,.32);transform:translateY(14px) scale(.985);transition:transform .2s}
.bo-overlay.open .bo-modal{transform:none}

/* ── Stepper (indicateur d'étapes 1→N) ── */
.stepper{display:flex;align-items:center;gap:0;margin:0 0 26px;flex-wrap:wrap}
.step{display:flex;align-items:center;gap:9px;flex:1;min-width:150px}
.step-num{width:30px;height:30px;border-radius:50%;flex-shrink:0;display:flex;align-items:center;justify-content:center;
  font-weight:700;font-size:.85rem;background:var(--surface-2);color:var(--text-muted);border:2px solid transparent;transition:var(--transition)}
.step-label{font-size:.82rem;font-weight:600;color:var(--text-muted);line-height:1.1}
.step-bar{flex:1;height:2px;background:var(--surface-2);margin:0 8px;min-width:16px}
.step.done .step-num{background:var(--success);color:#fff}
.step.active .step-num{background:var(--primary);color:#fff;border-color:var(--primary-dim,rgba(47,128,237,.3))}
.step.active .step-label,.step.done .step-label{color:var(--text-main)}
.step-pane{display:none}
.step-pane.active{display:block;animation:stepIn .22s ease}
@keyframes stepIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}

/* ── Cartes de sélection (étape Activité : cartes d'activité cliquables) ── */
.pick-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:12px}
.pick-card{border:1.5px solid var(--border);border-radius:14px;padding:16px 14px;cursor:pointer;text-align:center;
  background:var(--surface-0);transition:var(--transition);display:flex;flex-direction:column;align-items:center;gap:7px}
.pick-card:hover{border-color:var(--primary);transform:translateY(-2px);box-shadow:var(--shadow-sm)}
.pick-card.selected{border-color:var(--primary);background:var(--primary-dim,rgba(47,128,237,.08));box-shadow:0 0 0 3px rgba(47,128,237,.12)}
.pick-card .pick-emoji{width:44px;height:44px;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:1.7rem;line-height:1}
.pick-card .pick-name{font-size:.85rem;font-weight:600;color:var(--text-main)}
.pick-card input{position:absolute;opacity:0;pointer-events:none}

/* ── Carte de prévisualisation config (étape Activité) ── */
.preview-card{border:1px solid var(--border);border-left:4px solid var(--primary);border-radius:14px;
  padding:16px 18px;background:var(--surface-1)}
.preview-card h4{font-family:var(--font-title-family);font-size:1rem;color:var(--secondary);margin:0 0 10px}
.preview-line{display:flex;align-items:center;gap:8px;font-size:.87rem;color:var(--text-main);padding:3px 0}
.preview-line .ok{color:var(--success);font-weight:700}

/* ── Récap final (étape Administrateur) ── */
.recap{border:1px solid var(--border);border-radius:14px;padding:16px 18px;background:var(--surface-1)}
.recap-row{display:flex;justify-content:space-between;gap:12px;padding:6px 0;font-size:.88rem;border-bottom:1px dashed var(--border)}
.recap-row:last-child{border-bottom:0}
.recap-row .k{color:var(--text-muted)}
.recap-row .v{font-weight:600;color:var(--text-main);text-align:right}

/* Réactif : en-tête empilé sur mobile */
@media (max-width:768px){
  .page-title{font-size:1.4rem}
  /* `flex:1` seul ne suffit pas : `.btn` interdit la coupure du texte, donc la largeur
     MINIMALE des trois boutons dépasse l'écran et ils débordent au lieu de rétrécir.
     Mesuré sur la fiche client à 393 px : « Supprimer » finissait à 448 px, coupé net
     par le bord, sans défilement possible — une action destructive tronquée.
     Le `wrap` ne change rien tant que le contenu tient : il ne s'active que dans le
     cas déjà cassé. */
  .page-head-actions{width:100%;flex-wrap:wrap}
  .page-head-actions .btn{flex:1 1 auto;min-width:calc(50% - 5px)}
  .stepper .step-label{display:none}
  .step{min-width:0;flex:0 0 auto}
}

/* ════════════════════════════════════════════════════════════════════════
   DESIGN SYSTEM V2 — Raffinement (revue UI/UX)
   #2 ombres douces · #3 espacements · #8 bordures légères · #9 boutons
   #10 sidebar active · #15 micro-interactions
   ════════════════════════════════════════════════════════════════════════ */

/* #3 — échelle d'espacement unique (8/16/24/32/48/64) */
:root{
  --space-1:8px;  --space-2:16px; --space-3:24px;
  --space-4:32px; --space-5:48px; --space-6:64px;
  --border-soft: rgba(15,23,42,.06);   /* séparateur discret */
}

/* #8 — lignes de tableau plus discrètes dans le back-office (laisser respirer) */
.bo-content .table-cards tbody td{ border-bottom:1px solid var(--border-soft); }
.bo-content .table-cards thead th{ border-bottom:1px solid var(--border-soft); background:transparent; }
.bo-content .table-cards tbody tr{ transition: background var(--transition); }
.bo-content .table-cards tbody tr:hover td{ background: var(--surface-1); }

/* #9 — boutons bien différenciés : secondaire = surface neutre (pas un clone du primaire) */
.btn-secondary{ background:var(--surface-1); color:var(--text-main); border:1px solid var(--border); }
.btn-secondary:hover{ background:var(--surface-2); color:var(--text-main); }
.btn-text{ background:transparent; border:1px solid transparent; color:var(--primary); padding-left:8px; padding-right:8px; }
.btn-text:hover{ background:var(--primary-dim,rgba(47,128,237,.10)); }

/* #10 — état actif sidebar : fond bleu plein, texte/icône blancs, transition douce */
.bo-sidebar-link{ transition: background var(--transition), color var(--transition); border-radius:10px; margin:1px 8px; }
.bo-sidebar-link.active{
  background:var(--primary) !important; color:#fff !important;
  border-left-color:transparent; box-shadow: var(--shadow-sm);
}
.bo-sidebar-link.active svg,.bo-sidebar-link.active .icon{ color:#fff; }
.bo-sidebar-section{ padding-left:16px; }

/* #15 — micro-interactions : transitions homogènes + focus visible */
.btn{ transition: background var(--transition), box-shadow var(--transition), transform var(--transition), opacity var(--transition); }
.btn:active{ transform: translateY(1px); }
.pick-card,.bo-menu-item,.status-pill,.badge{ transition: var(--transition); }
.cell-logo{ transition: transform var(--transition); }
.table-cards tbody tr:hover .cell-logo{ transform: scale(1.04); }

/* ── #7 — Personnalité couleur des badges de plan ── */
.badge-plan-trial   { background: rgba(242,153,74,.14); color:#B45309; }   /* Essai — ambre */
.badge-plan-standard{ background: var(--primary-dim,rgba(47,128,237,.12)); color: var(--primary); } /* Standard — bleu */
.badge-plan-premium { background: rgba(124,77,255,.16); color: var(--violet); }  /* Premium — violet */

/* ── #5 — Tendance KPI ── */
.kpi-trend .arrow{ font-weight:700; }

/* ── #12 — Modules en cartes toggle (switch), garde la sémantique checkbox (POST inchangé) ── */
.module-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(180px,1fr)); gap:10px; max-height:380px; overflow:auto; padding:2px; }
.module-toggle{ display:flex; align-items:center; justify-content:flex-start; gap:9px; padding:11px 13px;
  border:1px solid var(--border); border-radius:12px; background:var(--surface-0); cursor:pointer; transition:var(--transition); margin:0; }
.module-toggle:hover{ border-color:var(--primary); }
.module-toggle input{ position:absolute; opacity:0; pointer-events:none; }
.module-toggle .m-ic{ display:flex; flex-shrink:0; color:var(--text-muted); opacity:.85; }
.module-toggle .m-name{ font-size:.85rem; font-weight:600; color:var(--text-main); line-height:1.2; }
.module-toggle:has(input:checked) .m-ic{ color:var(--success); opacity:1; }
.module-switch{ width:38px; height:22px; border-radius:999px; background:var(--surface-2); position:relative; flex-shrink:0; transition:var(--transition); margin-left:auto; }
.module-switch::after{ content:""; position:absolute; top:2px; left:2px; width:18px; height:18px; border-radius:50%;
  background:#fff; box-shadow:var(--shadow-sm); transition:transform var(--transition); }
.module-toggle input:checked ~ .module-switch{ background:var(--success); }
.module-toggle input:checked ~ .module-switch::after{ transform:translateX(16px); }
.module-toggle:has(input:checked){ border-color:var(--success); background:rgba(16,185,129,.05); }

/* ── #13 — Tableau compact (cache les cellules secondaires sur mobile) ── */
@media (max-width:768px){
  .bo-content .table-compact td.cell-optional{ display:none; }
  .bo-content .table-compact tr{ padding:10px 14px; }
  .bo-content .table-compact td{ border-bottom:0; padding:2px 0; }
  .bo-content .table-compact td::before{ font-size:.68rem; opacity:.7; }
  .bo-content .table-compact .cell-lead{ gap:9px; }
}

/* ════════════════════════════════════════════════════════════════════════
   Sprint composants — Icônes Lucide + identité (revue V2 : #6 icônes, #7 badges,
   personnalité catégories)
   ════════════════════════════════════════════════════════════════════════ */

/* Icône Lucide inline — hérite de la couleur du contexte (currentColor) */
.lucide{ display:inline-block; vertical-align:middle; flex-shrink:0; }
.kpi-icon .lucide, .cell-logo .lucide, .bo-menu-item .lucide, .pick-emoji .lucide{ display:block; }
.bo-menu-item .lucide{ opacity:.85; }

/* Boutons : alignement icône + texte homogène */
.btn{ display:inline-flex; align-items:center; justify-content:center; gap:7px; }

/* Personnalité couleur par catégorie (teintes déterministes cat-tint-0..7) */
.cat-tint-0{ background:rgba(47,128,237,.12);  color:#2F80ED; }
.cat-tint-1{ background:rgba(124,77,255,.14);  color:#7C4DFF; }
.cat-tint-2{ background:rgba(86,204,242,.18);  color:#0E7FB8; }
.cat-tint-3{ background:rgba(16,185,129,.14);  color:#0E9F6E; }
.cat-tint-4{ background:rgba(242,153,74,.16);  color:#B45309; }
.cat-tint-5{ background:rgba(239,68,68,.12);   color:#DC2626; }
.cat-tint-6{ background:rgba(99,102,241,.14);  color:#4F46E5; }
.cat-tint-7{ background:rgba(20,184,166,.16);  color:#0D9488; }

/* #2 revue — palette de badges officielle par TYPE de produit (personnalité) */
.badge-type-service { background: var(--primary-dim,rgba(47,128,237,.12)); color: var(--primary); }
.badge-type-article { background: rgba(16,185,129,.14); color:#0E9F6E; }
.badge-type-commande{ background: rgba(242,153,74,.16); color:#B45309; }

/* Alignement icône Lucide dans les badges */
.badge{ display:inline-flex; align-items:center; gap:4px; }
.badge .lucide{ margin:0; }

/* ════════════════════════════════════════════════════════════════════════
   Sprint composants 2 — Micro-interactions & États d'UI
   (revue V2 : #5 hover cartes, #6 menus animés, #10 micro-interactions,
    + loading/skeleton/empty/error)
   ════════════════════════════════════════════════════════════════════════ */
:root{ --transition-fast:.14s ease; }

/* Cartes : hover progressif (scale léger + ombre montante, 140ms) */
.bo-card, .bo-stat-card, .device-card, .pick-card{ transition: box-shadow var(--transition-fast), transform var(--transition-fast), border-color var(--transition-fast); }

/* Lignes de tableau : hover plus lisible + ligne cliquable (JS base.html) */
.bo-content .table-cards tbody tr{ transition: background var(--transition-fast), box-shadow var(--transition-fast); }
.bo-content .table-cards tbody tr[data-row-href]{ cursor:pointer; }
.bo-content .table-cards tbody tr[data-row-href]:hover{ box-shadow: inset 3px 0 0 var(--primary); }
.bo-content .table-cards tbody tr[data-row-href]:hover td{ background: var(--surface-1); }

/* Badges : léger fade à l'apparition */
@keyframes badgeIn{ from{opacity:0; transform:translateY(2px)} to{opacity:1; transform:none} }
.badge{ animation: badgeIn .18s ease both; }

/* Menu ⋮ : ombre + apparition animée (déjà translateY ; renforce l'envie d'interagir) */
.bo-menu-toggle:hover{ box-shadow: var(--shadow-sm); }

/* ── Empty state réutilisable ── */
.empty-state{ text-align:center; padding:48px 24px; color:var(--text-muted); }
.empty-state .es-ic{ width:58px; height:58px; border-radius:16px; display:inline-flex; align-items:center;
  justify-content:center; background:var(--surface-1); border:1px solid var(--border); color:var(--text-muted); margin-bottom:14px; }
.empty-state h3{ font-family:var(--font-title-family); font-size:1.05rem; color:var(--text-main); margin:0 0 6px; }
.empty-state p{ font-size:.9rem; margin:0 auto 16px; max-width:44ch; }

/* ── Skeleton de chargement (barres shimmer) ── */
@keyframes skShimmer{ 0%{background-position:200% 0} 100%{background-position:-200% 0} }
.sk-line{ height:12px; border-radius:6px; margin:8px 0;
  background:linear-gradient(90deg, var(--surface-1) 25%, var(--surface-2) 50%, var(--surface-1) 75%);
  background-size:200% 100%; animation: skShimmer 1.3s linear infinite; }
.sk-line.w-40{ width:40%; } .sk-line.w-60{ width:60%; } .sk-line.w-80{ width:80%; }

/* ── Indicateur HTMX : fondu doux du contenu en cours de chargement ── */
.htmx-request.hx-fade, .hx-fade.htmx-request{ opacity:.5; transition: opacity var(--transition-fast); }

/* ════════════════════════════════════════════════════════════════════════
   Sprint composants 3 — Graphiques SVG inline (donut, sparkline, barres)
   ════════════════════════════════════════════════════════════════════════ */
.donut{ display:block; }
.sparkline{ display:block; width:100%; height:auto; overflow:visible; }
.chart-grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(280px,1fr)); gap:20px; margin-bottom:20px; }
.chart-center{ display:flex; align-items:center; gap:18px; flex-wrap:wrap; }
.chart-legend{ display:flex; flex-direction:column; gap:8px; font-size:.85rem; }
.chart-legend .lg{ display:flex; align-items:center; gap:8px; }
.chart-legend .dotc{ width:10px; height:10px; border-radius:3px; flex-shrink:0; }
.chart-legend .lg .v{ font-weight:700; color:var(--text-main); margin-left:auto; }

/* Barres horizontales (classement) */
.bar-row{ margin-bottom:12px; }
.bar-row .bar-top{ display:flex; justify-content:space-between; font-size:.84rem; font-weight:600; color:var(--text-main); margin-bottom:5px; gap:10px; }
.bar-row .bar-top .bar-val{ color:var(--primary); font-weight:700; }
.bar-track{ background:var(--surface-2); height:9px; border-radius:6px; overflow:hidden; }
.bar-fill{ height:100%; border-radius:6px; background:var(--primary); transition:width .5s ease; }
.bar-fill.success{ background:var(--success); }
.bar-fill.warning{ background:var(--warning); }
.bar-fill.violet{ background:var(--violet); }

/* KPI : petite sparkline sous la valeur */
.kpi-spark{ margin-top:10px; }

/* ── Alertes / états succès-erreur (icône + slide-in) ── */
.alert{ display:flex; align-items:center; gap:9px; }
.alert .lucide{ flex-shrink:0; }
@keyframes alertIn{ from{opacity:0; transform:translateY(-6px)} to{opacity:1; transform:none} }
.alert-slide{ animation: alertIn .25s ease both; }

/* Respecte les préférences de mouvement réduit */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{ animation-duration:.001ms !important; transition-duration:.001ms !important; }
}


/* ============================================================
   Boutique & vitrine publiques — refonte 2026-08
   Portée : templates/portal/catalogue.html, service_detail.html, fragments.
   Aucun style en dur dans les templates : la page reste pilotable
   depuis /backoffice/apparence/ (ThemeConfig).

   ⚠ Le portail existe en DEUX thèmes : clair (par défaut) et sombre
   (`body.portal-body`, Crystal). Les styles de base sont donc écrits
   pour le thème CLAIR, et le sombre les surcharge en fin de bloc.
   Une première version n'avait été écrite que pour le sombre : sur le
   thème clair, la barre de rayons et la bande de réassurance
   ressortaient en blanc sur blanc, donc invisibles.

   Parti pris : les cartes produit restent CLAIRES dans les deux thèmes.
   C'est le standard e-commerce — la photo ressort et le prix passe le
   contraste AA.
   ============================================================ */

:root {
  /* Bleu de marque assombri, pour TOUT texte porteur d'information sur fond clair
     et pour le blanc posé dessus : prix, boutons pleins. #2F80ED ne donne que
     3,87:1 dans les deux sens (texte bleu sur blanc, comme blanc sur bleu) — sous
     le seuil AA. C'est le défaut déjà relevé sur la caisse, et mesuré à nouveau ici
     sur le bouton des cartes. Cette variante tient 5,8:1 en restant « bleu PLCORP ». */
  --blue-aa:       #1F62C4;
  --blue-aa-hover: #17509F;
  --shop-gap: 20px;
}

.pl-visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* ── Boutons ────────────────────────────────────────────────── */
.pl-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 48px; padding: 0 24px; border-radius: var(--radius-btn);
  font-family: 'Inter', sans-serif; font-weight: 600; font-size: .95rem;
  text-decoration: none; border: 1.5px solid transparent; cursor: pointer;
  transition: background var(--transition), border-color var(--transition), transform var(--transition);
}
.pl-btn--primary { background: var(--blue-aa); color: #fff; box-shadow: 0 4px 14px rgba(47,128,237,.35); }
.pl-btn--primary:hover { background: var(--blue-aa-hover); color: #fff; transform: translateY(-1px); }
.pl-btn--ghost { background: var(--surface-0); border-color: var(--border); color: var(--secondary); }
.pl-btn--ghost:hover { background: var(--surface-1); border-color: var(--primary); }
.pl-btn:focus-visible { outline: 3px solid var(--primary); outline-offset: 2px; }
/* WhatsApp est le canal de commande réel à Gagnoa : son bouton doit se reconnaître avant
   d'être lu, donc porter sa couleur et son glyphe plutôt que le bleu de tous les autres. */
/* #128C7E sous du blanc ne donne que 4,14:1 à cette taille : nuance WhatsApp assombrie
   pour tenir AA sans perdre la reconnaissance de la marque. */
.pl-btn--whatsapp { background: #0B6E60; color: #fff; gap: 8px; }
.pl-btn--whatsapp:hover { background: #085449; color: #fff; }
.pl-btn--whatsapp svg { width: 20px; height: 20px; flex: 0 0 20px; }
/* Dans le bandeau (toujours sombre, photo + voile), le bouton secondaire doit
   rester clair sur fond sombre quel que soit le thème de la page. */
.pl-hero .pl-btn--ghost { background: rgba(255,255,255,.10); border-color: rgba(255,255,255,.30); color: #fff; }
.pl-hero .pl-btn--ghost:hover { background: rgba(255,255,255,.20); border-color: rgba(255,255,255,.45); }

/* L'ordre de lecture est le même partout : marque, rayons, produits, puis engagements.
   Les promesses gagnent à être lues APRÈS avoir vu un prix — au-dessus, elles ne font que
   retarder la marchandise. */
.pl-shop--liste { display: flex; flex-direction: column; }
.pl-shop--liste > .pl-hero       { order: 1; }
.pl-shop--liste > .pl-breadcrumb,
.pl-shop--liste > .pl-rayon-titre { order: 1; }
.pl-shop--liste > .pl-rayons     { order: 2; }
.pl-shop--liste > .pl-results    { order: 3; }
.pl-shop--liste > .pl-trust      { order: 8; }
.pl-shop--liste > .pl-aide       { order: 9; }
.pl-shop--liste > .pl-closing    { order: 10; }

/* ── Bandeau d'accroche (sombre dans les deux thèmes) ───────── */
.pl-hero {
  position: relative; overflow: hidden;
  /* Descendu de 380 à 260, puis à 190 : sur un portable 1440×900, il restait 742 px de
     discours avant la première carte, soit 87 % du pli — ni nom ni prix à l'arrivée. Le
     mobile, lui, montrait déjà le prix au premier écran. */
  min-height: 190px; display: flex; align-items: center;
  padding: 24px 32px; margin-bottom: 18px;
  border-radius: var(--radius-card);
  background-color: var(--secondary);
  /* Deux déclarations : la première est le repli des navigateurs qui ignorent
     `image-set()` et invalideraient sinon toute la règle — il leur resterait le
     `background-color`, donc un fond uni sans dégradé. La seconde sert le WebP
     (39 Ko contre 83 Ko) aux navigateurs modernes. Le JPEG reste par ailleurs
     l'`og:image` des partages WhatsApp, où la qualité prime sur le poids. */
  background-image: linear-gradient(120deg, rgba(8,24,39,.92) 0%, rgba(16,42,67,.72) 100%),
                    url("../images/brand/photos/hero.0b81c76a81ce.jpg");
  background-image: linear-gradient(120deg, rgba(8,24,39,.92) 0%, rgba(16,42,67,.72) 100%),
                    image-set(url("../images/brand/photos/hero.7f6bec18ad2c.webp") type('image/webp'),
                              url("../images/brand/photos/hero.0b81c76a81ce.jpg") type('image/jpeg'));
  background-size: cover; background-position: center;
}
.pl-hero__inner { max-width: 640px; }
.pl-hero__eyebrow {
  font-size: .8rem; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
  color: #7FD9FA; margin: 0 0 8px;
}
.pl-hero__title {
  font-family: 'Sora', sans-serif; font-weight: 700; font-size: 2.1rem; line-height: 1.15;
  letter-spacing: -.02em; color: #fff; margin: 0 0 10px;
}
.pl-hero__subtitle {
  font-size: 1.05rem; line-height: 1.5; color: #E2E8F0; margin: 0 0 18px; max-width: 34em;
}
.pl-hero__actions { display: flex; gap: 12px; flex-wrap: wrap; }

/* ── Bande de réassurance ───────────────────────────────────── */
.pl-trust {
  list-style: none; margin: 36px 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px;
}
.pl-trust__item {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 16px; border-radius: var(--radius-lg);
  background: var(--surface-0); border: 1px solid var(--border);
  font-size: .85rem; line-height: 1.35; color: var(--text-muted);
  box-shadow: var(--shadow-xs);
}
.pl-trust__item svg { width: 22px; height: 22px; flex: 0 0 22px; color: var(--primary); }
.pl-trust__item strong { display: block; color: var(--secondary); font-weight: 600; }

/* ── Rayons ─────────────────────────────────────────────────── */
/* 160px donnait exactement 6 colonnes à 1440 : le 7e rayon passait seul à la ligne et
   coûtait 110 px de vitrine au-dessus du pli, pour une bande vide sur les 5/6 restants.
   La hauteur du pli était donc fonction du nombre de rayons — elle changeait sans commit,
   au gré des articles publiés. 132px absorbe jusqu'à 8 rayons sur une seule ligne. */
.pl-rayons {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
  gap: 12px; margin-bottom: 22px;
}
/* Rayon vidé par la recherche courante : présent pour le rebond, mais qui ne se fait pas
   passer pour un résultat. */
.pl-rayon.is-empty { opacity: .55; }
.pl-rayon.is-empty:hover { opacity: 1; }
.pl-rayon {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  min-height: 78px; height: 100%; padding: 12px 14px; text-decoration: none;
  border-radius: var(--radius-lg);
  background: var(--surface-0); border: 1px solid var(--border);
  color: var(--secondary); box-shadow: var(--shadow-xs);
  transition: all var(--transition);
}
.pl-rayon:hover { border-color: var(--primary); transform: translateY(-2px); box-shadow: var(--shadow-md); }
.pl-rayon:focus-visible { outline: 3px solid var(--primary); outline-offset: 2px; }
.pl-rayon.is-active { background: var(--primary-dim); border-color: var(--primary); }
.pl-rayon__icon { color: var(--primary); }
.pl-rayon__label { font-family: 'Sora', sans-serif; font-weight: 600; font-size: .9rem; line-height: 1.25; }
.pl-rayon__count { font-size: .75rem; color: var(--text-muted); }

/* ── Barre d'outils ─────────────────────────────────────────── */
.pl-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; margin-bottom: 20px;
}
.pl-toolbar__count {
  margin: 0; font-family: 'Sora', sans-serif; font-weight: 600; font-size: 1.05rem;
  color: var(--secondary);
}
.pl-toolbar__count output { color: var(--blue-aa); }
.pl-toolbar__controls { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; flex: 1; }
.pl-search { position: relative; flex: 1; min-width: 220px; }
.pl-search__icon {
  position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
  width: 18px; height: 18px; color: var(--text-light); pointer-events: none;
}
.pl-search__input {
  min-height: 48px; width: 100%;
  padding: 0 16px 0 42px; border-radius: var(--radius-btn);
  background: var(--surface-0); border: 1px solid var(--border);
  color: var(--text-main); font-size: .95rem; font-family: 'Inter', sans-serif;
}
.pl-search__input::placeholder { color: var(--text-light); }
.pl-search__input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-dim); }
.pl-select {
  min-height: 48px; padding: 0 36px 0 14px; border-radius: var(--radius-btn);
  background-color: var(--surface-0); border: 1px solid var(--border);
  color: var(--text-main); font-size: .9rem; font-family: 'Inter', sans-serif; cursor: pointer;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23475569' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19.5 8.25l-7.5 7.5-7.5-7.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center; background-size: 16px;
}

/* ── Grille produits ────────────────────────────────────────── */
.pl-grid { display: grid; gap: var(--shop-gap); grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* ── Carte produit (claire dans les deux thèmes) ────────────── */
.pl-card {
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--surface-0); border: 1px solid var(--border);
  border-radius: var(--radius-lg); text-decoration: none;
  box-shadow: var(--shadow-md);
  transition: transform var(--transition), box-shadow var(--transition);
}
.pl-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); }
.pl-card:focus-visible { outline: 3px solid var(--primary); outline-offset: 2px; }
.pl-card__media {
  position: relative; aspect-ratio: 4 / 3; background: var(--surface-1);
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.pl-card__img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pl-card__placeholder { color: var(--primary); opacity: .34; }
.pl-badge {
  position: absolute; top: 10px; left: 10px;
  padding: 4px 10px; border-radius: var(--radius-full);
  font-size: .7rem; font-weight: 700; letter-spacing: .02em;
}
/* Convention marchande : ambre = stock bas (encore achetable tout de suite), neutre =
   à commander. L'inverse mettait le signal le plus alarmant sur le produit disponible,
   et quatre pastilles rouges par écran finissent par ne plus rien vouloir dire. */
.pl-badge--precommande { background: #E2E8F0; color: #334155; }
/* Ambre foncé : stock bas, pas rupture. Le rouge est réservé à ce qui bloque l'achat. */
.pl-badge--derniers    { background: #B45309;        color: #fff; }
.pl-badge--commande    { background: rgba(124,77,255,.14); color: #5B34C7; }
.pl-badge--stock       { background: var(--success); color: #04291D; }

.pl-card__body { display: flex; flex-direction: column; flex: 1; padding: 14px 14px 16px; gap: 4px; }
.pl-card__rayon {
  margin: 0; font-size: .7rem; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-light);
}
.pl-card__name {
  margin: 0 0 10px; font-family: 'Sora', sans-serif; font-weight: 600;
  font-size: 1rem; line-height: 1.3; color: var(--secondary);
  /* Deux lignes maximum : au-delà, les cartes se désalignent.
     Pas de `min-height` : la réserver laissait une bande blanche sous chaque titre court,
     qui se lisait comme une ligne restée en cours de chargement. Les pieds de carte sont
     déjà alignés par `margin-top: auto` dans une grille à lignes égales. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.pl-card__foot { margin-top: auto; display: flex; align-items: flex-end; justify-content: space-between; gap: 10px; }
.pl-card__price {
  margin: 0; font-family: 'Sora', sans-serif; font-weight: 800;
  font-size: 1.25rem; line-height: 1.2; color: var(--blue-aa);
}
.pl-card__from {
  /* « dès » en ligne devant le montant : le surtitre sur sa propre ligne obligeait à
     réserver la place partout, ce qui creusait toutes les cartes pour aligner les prix. */
  font-size: .72rem; font-weight: 600; color: var(--text-light); margin-right: 3px;
}
.pl-card__currency { font-size: .72rem; font-weight: 600; margin-left: 3px; }
.pl-card__unit { display: block; font-size: .72rem; font-weight: 500; color: var(--text-light); margin-top: 2px; }
.pl-card__cta {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 0 14px; border-radius: var(--radius-btn);
  background: var(--blue-aa); color: #fff; font-size: .82rem; font-weight: 600;
  transition: background var(--transition);
}
.pl-card:hover .pl-card__cta { background: var(--blue-aa-hover); }

/* ── États annexes ──────────────────────────────────────────── */
.pl-empty {
  grid-column: 1 / -1; text-align: center; padding: 56px 24px;
  border-radius: var(--radius-card);
  background: var(--surface-1); border: 1px dashed var(--surface-2);
}
.pl-empty svg { width: 40px; height: 40px; color: var(--text-light); margin-bottom: 12px; }
.pl-empty__title {
  margin: 0 0 6px; font-family: 'Sora', sans-serif; font-weight: 600; font-size: 1.05rem;
  color: var(--secondary);
  /* Une requête de 30 caractères sans espace élargissait la page : le serveur en accepte 200. */
  overflow-wrap: anywhere;
}
.pl-empty__hint { margin: 0 0 20px; font-size: .9rem; color: var(--text-light); }
.pl-loadmore {
  grid-column: 1 / -1; display: flex; flex-direction: column; align-items: center;
  gap: 10px; padding: 24px 0 4px;
}
.pl-loadmore__progress { margin: 0; font-size: .8rem; color: var(--text-light); }
.pl-loadmore__end {
  grid-column: 1 / -1; margin: 0; padding: 24px 0 4px;
  text-align: center; font-size: .8rem; color: var(--text-light);
}
.pl-rayon-titre {
  margin: 0 0 24px; font-family: 'Sora', sans-serif; font-weight: 700;
  font-size: 1.9rem; color: var(--secondary);
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
}
.pl-rayon-titre__count { font-family: 'Inter', sans-serif; font-size: .9rem; font-weight: 500; color: var(--text-light); }
.pl-sort-label { font-size: .85rem; font-weight: 600; color: var(--text-muted); white-space: nowrap; }
.pl-specs { margin: 0 0 24px; display: grid; gap: 8px; }
.pl-specs > div { display: flex; gap: 10px; font-size: .88rem; padding-bottom: 8px; border-bottom: 1px solid var(--border); }
.pl-specs dt { flex: 0 0 42%; color: var(--text-light); }
.pl-specs dd { margin: 0; color: var(--secondary); font-weight: 600; }
.pl-aide {
  margin: 32px 0 0; padding: 20px 24px; border-radius: var(--radius-lg);
  background: var(--surface-1); border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center; gap: 16px; flex-wrap: wrap;
}
.pl-aide__texte { margin: 0; font-size: .95rem; color: var(--text-muted); }
.pl-closing {
  margin: 40px 0 0; text-align: center; font-size: .85rem; color: var(--text-light);
  padding-top: 24px; border-top: 1px solid var(--border);
}

/* ── Fiche produit ──────────────────────────────────────────── */
.pl-breadcrumb {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin-bottom: 20px; font-size: .82rem; color: var(--text-light);
}
.pl-breadcrumb a {
  color: var(--text-muted); text-decoration: none;
  /* Sur mobile, c'est le seul chemin de retour vers le rayon depuis la fiche. */
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 2px;
}
.pl-breadcrumb a:hover { color: var(--primary); text-decoration: underline; }
.pl-breadcrumb [aria-current] { color: var(--secondary); font-weight: 600; }

.pl-detail {
  display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); gap: 32px;
  padding: 24px; border-radius: var(--radius-card);
  background: var(--surface-0); border: 1px solid var(--border); box-shadow: var(--shadow-md);
}
.pl-detail__media {
  aspect-ratio: 4 / 3; overflow: hidden; border-radius: var(--radius-lg);
  background: var(--surface-1); display: flex; align-items: center; justify-content: center;
}
.pl-detail__media img { width: 100%; height: 100%; object-fit: cover; }
.pl-detail__placeholder { color: var(--primary); opacity: .3; }
.pl-detail__rayon {
  margin: 0 0 8px; font-size: .75rem; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--blue-aa);
}
.pl-detail__title {
  margin: 0 0 16px; font-family: 'Sora', sans-serif; font-weight: 700;
  font-size: 2rem; line-height: 1.2; color: var(--secondary);
}
.pl-detail__price {
  margin: 0 0 14px; font-family: 'Sora', sans-serif; font-weight: 800;
  font-size: 2rem; line-height: 1.1; color: var(--blue-aa);
}
.pl-detail__currency { font-size: .95rem; font-weight: 600; margin-left: 5px; }
.pl-detail__from { font-size: .95rem; font-weight: 600; color: var(--text-light); margin-right: 5px; }
.pl-detail__unit { display: block; font-size: .85rem; font-weight: 500; color: var(--text-light); margin-top: 4px; }
.pl-detail__availability {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  margin: 0 0 20px; font-size: .88rem; line-height: 1.5; color: var(--text-muted);
}
.pl-detail__availability .pl-badge { position: static; }
.pl-detail__description { margin: 0 0 24px; font-size: 1rem; line-height: 1.65; color: var(--text-muted); }
.pl-detail__actions { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 24px; }
.pl-detail__lieu {
  display: inline-flex; align-items: center; min-height: 44px; margin-top: 12px;
  color: var(--blue-aa); font-size: .9rem; font-weight: 600;
}
.pl-detail__howto {
  margin: 0; padding: 16px 16px 16px 34px; list-style: disc;
  border-radius: var(--radius-lg); background: var(--surface-1); border: 1px solid var(--border);
  font-size: .85rem; line-height: 1.7; color: var(--text-muted);
}
.pl-related { margin-top: 48px; }
.pl-related__title {
  margin: 0 0 20px; font-family: 'Sora', sans-serif; font-weight: 700; font-size: 1.3rem;
  color: var(--secondary); padding-bottom: 12px; border-bottom: 1px solid var(--border);
}

/* ── Thème sombre (body.portal-body) ────────────────────────── */
body.portal-body .pl-trust__item,
body.portal-body .pl-rayon {
  background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.12); box-shadow: none;
}
body.portal-body .pl-trust__item { color: #CBD5E1; }
body.portal-body .pl-trust__item strong { color: #fff; }
body.portal-body .pl-trust__item svg,
body.portal-body .pl-rayon__icon { color: var(--cyan, #56CCF2); }
body.portal-body .pl-rayon { color: #E2E8F0; }
body.portal-body .pl-rayon:hover { background: rgba(255,255,255,.13); border-color: rgba(86,204,242,.45); }
body.portal-body .pl-rayon.is-active { background: rgba(47,128,237,.24); border-color: var(--primary); color: #fff; }
body.portal-body .pl-rayon.is-active .pl-rayon__icon { color: #fff; }
body.portal-body .pl-rayon__count { color: #94A3B8; }
body.portal-body .pl-toolbar__count { color: #fff; }
body.portal-body .pl-toolbar__count output { color: var(--cyan, #56CCF2); }
body.portal-body .pl-search__input,
body.portal-body .pl-select {
  background-color: rgba(255,255,255,.08); border-color: rgba(255,255,255,.16); color: #fff;
}
body.portal-body .pl-search__input::placeholder { color: #94A3B8; }
body.portal-body .pl-search__icon { color: #94A3B8; }
body.portal-body .pl-select option { background: var(--secondary); color: #fff; }
body.portal-body .pl-btn--ghost { background: rgba(255,255,255,.08); border-color: rgba(255,255,255,.22); color: #fff; }
body.portal-body .pl-btn--ghost:hover { background: rgba(255,255,255,.16); }
body.portal-body .pl-card { border-color: rgba(255,255,255,.10); box-shadow: 0 4px 16px rgba(8,24,39,.30); }
body.portal-body .pl-card:hover { box-shadow: 0 12px 28px rgba(8,24,39,.45); }
body.portal-body .pl-empty { background: rgba(255,255,255,.05); border-color: rgba(255,255,255,.16); }
body.portal-body .pl-empty__title { color: #fff; }
body.portal-body .pl-empty__hint,
body.portal-body .pl-empty svg { color: #94A3B8; }
body.portal-body .pl-closing { color: #94A3B8; border-top-color: rgba(255,255,255,.10); }
body.portal-body .pl-loadmore__progress { color: #94A3B8; }
body.portal-body .pl-loadmore__end { color: #94A3B8; }
body.portal-body .pl-rayon-titre { color: #fff; }
body.portal-body .pl-rayon-titre__count,
body.portal-body .pl-sort-label,
body.portal-body .pl-aide__texte { color: #CBD5E1; }
body.portal-body .pl-specs > div { border-bottom-color: rgba(255,255,255,.10); }
body.portal-body .pl-specs dt { color: #94A3B8; }
body.portal-body .pl-specs dd { color: #fff; }
body.portal-body .pl-aide { background: rgba(255,255,255,.05); border-color: rgba(255,255,255,.10); }
body.portal-body .pl-breadcrumb { color: #94A3B8; }
body.portal-body .pl-breadcrumb a { color: #CBD5E1; }
body.portal-body .pl-breadcrumb a:hover { color: var(--cyan, #56CCF2); }
body.portal-body .pl-breadcrumb [aria-current] { color: #fff; }
body.portal-body .pl-detail {
  background: rgba(255,255,255,.05); border-color: rgba(255,255,255,.10); box-shadow: none;
}
body.portal-body .pl-detail__rayon { color: var(--cyan, #56CCF2); }
body.portal-body .pl-detail__title,
body.portal-body .pl-detail__price { color: #fff; }
body.portal-body .pl-detail__currency { color: var(--cyan, #56CCF2); }
body.portal-body .pl-detail__unit { color: #94A3B8; }
body.portal-body .pl-detail__availability,
body.portal-body .pl-detail__description { color: #CBD5E1; }
body.portal-body .pl-detail__howto {
  background: rgba(255,255,255,.04); border-color: rgba(255,255,255,.08); color: #CBD5E1;
}
body.portal-body .pl-related__title { color: #fff; border-bottom-color: rgba(255,255,255,.10); }

/* ── Tablette ───────────────────────────────────────────────── */
@media (max-width: 1100px) {
  .pl-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .pl-trust { grid-template-columns: repeat(2, 1fr); }
  .pl-hero__title { font-size: 2rem; }
}

/* ── Mobile (cible : Android ~393 px) ───────────────────────── */
@media (max-width: 900px) {
  .pl-detail { grid-template-columns: 1fr; gap: 24px; padding: 16px; }
  .pl-detail__title { font-size: 1.6rem; }
  .pl-detail__price { font-size: 1.7rem; }
  .pl-detail__actions { flex-direction: column; }
  .pl-detail__actions .pl-btn { width: 100%; }
}
@media (max-width: 720px) {
  :root { --shop-gap: 12px; }

  /* ── Marchandise au-dessus de la ligne de flottaison ──────────────────
     Le premier produit arrivait à 751 px sur un écran de 852 px : sur un Android réel,
     barres du navigateur déduites, aucun prix n'était visible sans défiler. Jumia montre
     un produit dans les 300 premiers pixels, et c'est la bonne référence pour un marché
     où le mobile 4G est la surface principale.

     Trois leviers, aucun ne supprime d'information :
       · le bandeau se réduit à son message (les deux boutons ne faisaient que dérouler
         la page ou mener à l'autre surface, tous deux atteignables par la navigation) ;
       · la barre de recherche remonte juste sous le bandeau, comme chez Jumia ;
       · les rayons deviennent des pastilles hautes de 44 px au lieu de tuiles de 106 px. */
  .pl-hero { min-height: 0; padding: 16px 18px; margin-bottom: 10px; border-radius: 16px; }
  .pl-hero__title { font-size: 1.25rem; margin-bottom: 6px; line-height: 1.25; }
  .pl-hero__subtitle {
    font-size: .85rem; margin-bottom: 0; line-height: 1.45;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  }
  .pl-hero__eyebrow { margin-bottom: 6px; font-size: .7rem; }
  .pl-hero__actions { display: none; }

  /* Sur mobile, la grille sort de sa section pour que les rayons puissent se glisser
     entre le bandeau et la barre d'outils. */
  .pl-shop--liste > .pl-results { display: contents; }
  .pl-shop--liste .pl-rayons  { order: 2; }
  .pl-shop--liste .pl-toolbar { order: 3; }
  .pl-shop--liste .pl-grid    { order: 4; }

  /* Rayons en pastilles : une tuile de 106 px de haut coûtait un tiers d'écran. */
  .pl-rayon {
    flex: 0 0 auto; min-width: 0; min-height: 44px;
    flex-direction: row; align-items: center; gap: 8px; padding: 0 14px;
    border-radius: var(--radius-full); white-space: nowrap;
  }
  .pl-rayon .pl-rayon__icon { width: 18px; height: 18px; }
  .pl-rayon__label { font-size: .82rem; }
  .pl-rayon__count::before { content: '('; }
  .pl-rayon__count::after  { content: ')'; }
  /* Bandeau resserré : chaque pixel ici retarde le premier prix visible, et le mobile 4G
     est la surface principale. Arbitrage assumé entre l'accroche de marque (utile à la
     première visite) et l'accès au produit (utile à toutes les suivantes). */

  /* Réassurance et rayons passent en rail horizontal : ils informent sans
     repousser les produits sous deux écrans de défilement. */
  .pl-trust {
    display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px;
    margin: 24px 0 0; padding: 0;
  }
  .pl-rayons::-webkit-scrollbar { display: none; }
  .pl-trust__item { padding: 12px; font-size: .78rem; align-items: flex-start; }
  .pl-trust__item svg { flex: 0 0 18px; width: 18px; height: 18px; margin-top: 1px; }

  .pl-rayons {
    display: flex; gap: 8px; overflow-x: auto; margin: 0 -16px 12px; padding: 0 16px 4px;
    /* Dégradé de bord : seul le découpage d'une tuile signalait qu'on pouvait pousser. */
    -webkit-mask-image: linear-gradient(to right, #000 0, #000 88%, transparent 100%);
            mask-image: linear-gradient(to right, #000 0, #000 88%, transparent 100%);
  }
  .pl-rayon { flex: 0 0 auto; min-width: 132px; min-height: 84px; }

  /* Deux blocs francs : le compteur seul sur sa ligne, puis recherche et tri côte à côte.
     La grille précédente coupait « 32 / articles » sur deux lignes et laissait le compteur
     aligné sur rien — l'écran avait l'air cassé plutôt que dense. */
  .pl-toolbar { display: flex; flex-direction: column; align-items: stretch; gap: 10px; margin-bottom: 14px; }
  .pl-toolbar__count { font-size: .95rem; white-space: nowrap; }
  /* La recherche prend toute la ligne, le tri passe dessous : à 46 % de la largeur, le
     texte d'aide était tranché en plein mot (« Rechercher u ») sur le contrôle le plus
     utilisé de la page. */
  .pl-toolbar__controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
  .pl-search { flex: 1 0 100%; min-width: 0; }
  .pl-sort-label { margin-left: auto; }
  .pl-search__input { width: 100%; }
  .pl-sort-label { font-size: .8rem; }
  .pl-select { min-height: 44px; font-size: .82rem; padding: 0 30px 0 10px; }

  .pl-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pl-card__body { padding: 10px 10px 12px; }
  .pl-card__name { font-size: .875rem; margin-bottom: 8px; }
  .pl-card__price { font-size: 1.05rem; }
  /* Sous 2 colonnes il ne reste pas la place d'un libellé à côté du prix :
     l'action passe en pleine largeur sous le prix, cible tactile préservée. */
  .pl-card__foot { flex-direction: column; align-items: stretch; gap: 8px; }
  .pl-card__cta { min-height: 44px; width: 100%; }
  .pl-loadmore .pl-btn { width: 100%; }
}

/* ── Pièces dépliées des Créances : lignes en cartes, à toute largeur ────────
   Le mode « cartes » défini plus haut (@media max-width: 768px) n'est pas une
   affaire de petit écran : il vaut partout où la COLONNE est étroite, quelle
   que soit la fenêtre. Dans `/backoffice/creances/`, chaque pièce se déplie
   à l'intérieur de la cellule « Encaisser / relancer » du tableau des
   débiteurs, large d'environ 520 px.

   Mesuré sur staging en 1440 px : le tableau des lignes s'étendait jusqu'à
   1650 px — et, c'est là le piège, sans qu'aucune barre de défilement
   n'apparaisse. Le `.table-wrap` rapportait `scrollWidth == clientWidth`, donc
   il se croyait à l'aise, et `document.scrollWidth` valait exactement la
   largeur de la fenêtre. 18 cellules (prix unitaires, sous-totaux, « Sous-total
   brut », « Remise accordée », « Total ») étaient hors écran SANS aucun moyen
   d'y accéder — précisément les montants que cet écran existe pour montrer.
   Le commentaire de `creances.html` pariait l'inverse ; le pari était faux.

   On réapplique donc le motif des lignes 893-907 à ce seul contexte. Aucune
   règle existante n'est modifiée : le sélecteur est plus spécifique et
   n'attrape que les tableaux imbriqués dans une pièce dépliée. Vérifié après
   coup : 18 cellules hors écran → 0 en desktop, et 0 → 0 en mobile (le mode
   cartes y était déjà actif, donc aucune régression). */
.bo-content .piece-lignes .table-cards { white-space: normal; }
.bo-content .piece-lignes .table-cards thead { display: none; }
.bo-content .piece-lignes .table-cards tr {
  display: block; border: 1px solid var(--border); border-radius: 12px;
  padding: 4px 14px; margin-bottom: 12px; background: var(--surface-0);
}
.bo-content .piece-lignes .table-cards td {
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
  text-align: right; border: none; border-bottom: 1px solid var(--border);
  padding: 10px 0; white-space: normal;
}
.bo-content .piece-lignes .table-cards tr td:last-child { border-bottom: none; }
.bo-content .piece-lignes .table-cards td::before {
  content: attr(data-label); font-weight: 600; color: var(--text-muted);
  text-align: left; flex-shrink: 0;
}
/* Les lignes de pied portent un colspan et aucun data-label : pas de préfixe. */
.bo-content .piece-lignes .table-cards td[colspan]::before { content: ""; }
/* P1 — "38 000 F" ne doit pas wrapper sur deux lignes dans une cellule étroite.
   `white-space: normal` est nécessaire pour les libellés longs (Article), mais les
   cellules de montant (.text-right) doivent rester sur une seule ligne. */
.bo-content .piece-lignes .table-cards td.text-right,
.bo-content .piece-lignes .table-cards th.text-right { white-space: nowrap; }

/* ════════════════════════════════════════════════════════════════════════
   Flux de supervision des agents (/backoffice/agents/)
   ────────────────────────────────────────────────────────────────────────
   Le gabarit émettait déjà `agent-event-critical` / `-warning` / `-info` sur
   chaque ligne, sans qu'aucune règle n'existe : une panne de passerelle était
   visuellement identique à un rapport comptable, et la question « quelque chose
   exige-t-il mon attention ? » restait sans réponse au coup d'œil. Relevé par
   l'audit à l'aveugle du 2026-09-02 (P1).

   Le liseré gauche est le code couleur de statut Crystal déjà en place sur
   `.bo-stat-card.error / .warning / .info` (l. 689-693). Rien de neuf : mêmes
   tokens, appliqués en `inset box-shadow` parce qu'un `border-left` sur un
   `<tr>` ne tient pas en `border-collapse` — motif déjà utilisé l. 1853.
   ════════════════════════════════════════════════════════════════════════ */
.bo-content .agent-event-row td { vertical-align: top; }
.bo-content .agent-event-critical { box-shadow: inset 4px 0 0 var(--error); }
.bo-content .agent-event-warning  { box-shadow: inset 4px 0 0 var(--warning); }
.bo-content .agent-event-info     { box-shadow: inset 4px 0 0 var(--border); }

/* Un événement NON acquitté épaissit son liseré au lieu de teinter la ligne.
   Première tentative : un fond `--error-dim` sur la ligne. Mesuré aussitôt
   après — les badges posés dessus tombaient à 3,99:1 et 4,06:1, sous le seuil
   AA, parce que leur propre fond rgba se compose avec celui de la ligne. Le
   correctif d'un P1 en fabriquait un autre. Le liseré ne compose rien. */
.bo-content .agent-event-pending { box-shadow: inset 6px 0 0 currentColor; }
.bo-content .agent-event-critical.agent-event-pending { box-shadow: inset 6px 0 0 var(--error); }
.bo-content .agent-event-warning.agent-event-pending  { box-shadow: inset 6px 0 0 var(--warning); }

/* Le filet du dernier `td` collait au bord arrondi de la carte
   (`card-body` en padding:0). */
.bo-content .agent-event-feed-table tbody tr:last-child td { border-bottom: 0; }

@media (max-width: 768px) {
  /* En mode cartes, chaque `td` devient une rangée. Deux d'entre elles
     n'affichaient qu'un tiret sur la majorité des événements : ~90 px de vide
     sur une carte de ~300 px, payés à chaque coup de pouce. On masque la
     rangée quand elle n'a rien à dire, en gardant la colonne sur desktop. */
  .bo-content .table-cards td.cell-vide-mobile { display: none; }

  /* En mode cartes, le liseré passe sur la bordure gauche de la carte.
     ⚠️ Spécificité : `.bo-content .table-cards tr` (0,2,1) bat
     `.bo-content .agent-event-critical` (0,2,0) — première version mesurée à
     `border-left: 1px` gris pour les trois gravités, c'est-à-dire AUCUN code
     couleur sur mobile, alors que la capture semblait correcte. D'où le
     sélecteur qualifié `tr.agent-event-*`. */
  .bo-content .table-cards tr.agent-event-row {
    box-shadow: none; border-left-width: 4px; border-left-style: solid;
  }
  .bo-content .table-cards tr.agent-event-critical { border-left-color: var(--error); }
  .bo-content .table-cards tr.agent-event-warning  { border-left-color: var(--warning); }
  .bo-content .table-cards tr.agent-event-info     { border-left-color: var(--border); }
  .bo-content .table-cards tr.agent-event-pending  { border-left-width: 6px; }
}

@media (max-width: 768px) {
  /* Les trois groupes de filtres (gravité, source, période) occupaient ~500 px
     en s'empilant sur 4 rangées : sur un écran de 852 px, le fondateur voyait
     des filtres et presque aucun événement. En bande défilable, chaque groupe
     tient sur une ligne. Le conteneur déborde de LUI-MÊME, donc le défilement
     tactile fonctionne (contrairement au piège documenté dans
     `ux-visibility-audit`, où c'est l'élément entier qui sortait de la fenêtre). */
  .bo-content .feed-chips {
    flex-wrap: nowrap !important;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 4px;
    scrollbar-width: thin;
  }
  .bo-content .feed-chips > .btn { flex: 0 0 auto; }
  .bo-content .feed-chips > span { flex: 0 0 auto; }
}
