/* Overrides + classes propres au projet, par-dessus Bootstrap 5. */

:root {
  --nw-accent: #28a790;        /* jade/teal — couleur de marque (aplats, focus, filets) */
  --nw-accent-dark: #137a68;   /* teal foncé — survol/actif + couleur de lien (contraste AA) */
  --nw-warm: #e07a5f;          /* complémentaire chaud (corail) — touche artistique, parcimonieuse */
  --nw-warm-soft: rgba(var(--nw-warm-rgb, 224, 122, 95),.14);
  --nw-admin: #6c5ce7;
}

body { background: #faf8f5; }
body.admin { background: #f4f3f7; }
body.install {
  /* Dégradé crème → teal très doux (cohérence de marque ; partagé par les
     écrans d'installation et la page de maintenance). */
  background: linear-gradient(135deg, #f6f0e8 0%, #e3f3ef 100%);
  min-height: 100vh;
}

.navbar-brand { font-weight: 700; }

/* Badges de statut */
.status {
  display: inline-block;
  padding: .2rem .6rem;
  border-radius: 999px;
  font-size: .78rem;
  font-weight: 600;
  text-transform: capitalize;
  border: 1px solid transparent;
  line-height: 1.4;
}
.status-pending     { background: #fdf4dd; color: #966400; border-color: #ead29a; }
.status-requested   { background: #fdf4dd; color: #966400; border-color: #ead29a; }
.status-quoted      { background: #e8f4f8; color: #1e7789; border-color: #b3d9e5; }
.status-accepted    { background: #e9f1fa; color: #2a6fb3; border-color: #b9d3ec; }
.status-in_progress { background: #e1f4f0; color: #137a68; border-color: #b6e2da; }
.status-review      { background: #fff0e6; color: #a7590d; border-color: #f4c79c; }
.status-revision    { background: #ffe7e0; color: #a8431a; border-color: #f4b59c; }
.status-delivered   { background: #ecf8f1; color: #297d50; border-color: #b8e0c8; }
.status-completed   { background: #ecf8f1; color: #297d50; border-color: #b8e0c8; }
.status-cancelled   { background: #f3f3f3; color: #6d6d6d;    border-color: #ddd; }
.status-refunded    { background: #efeae3; color: #5c4836; border-color: #d0c7bb; }

/* Badges de statut PAIEMENT (axe parallèle au workflow). Réutilisent la
   base .status pour la forme pilule ; seules les couleurs diffèrent. */
.pay-none             { background: #f3f3f3; color: #6d6d6d;    border-color: #ddd; }
.pay-deposit_due      { background: #fdf4dd; color: #966400; border-color: #ead29a; }
.pay-deposit_received { background: #e8f4f8; color: #1e7789; border-color: #b3d9e5; }
.pay-balance_due      { background: #fff0e6; color: #a7590d; border-color: #f4c79c; }
.pay-paid             { background: #ecf8f1; color: #297d50; border-color: #b8e0c8; }
.pay-refunded_partial { background: #efeae3; color: #5c4836; border-color: #d0c7bb; }
.pay-refunded_full    { background: #efeae3; color: #5c4836; border-color: #d0c7bb; }

/* v2.10.0 — Badges axe expédition (phase 3). Forme identique aux badges
   paiement (classe de base .status), palette légèrement décalée pour
   différencier visuellement. `none` rarement affiché (commande numérique). */
.ship-none            { background: #f3f3f3; color: #6d6d6d;    border-color: #ddd; }
.ship-to_prepare      { background: #fff4e1; color: #a15f00; border-color: #f0d4a0; }
.ship-ready           { background: #e8f7ee; color: #1e7a45; border-color: #b8e0c6; }
.ship-shipped         { background: #e6efff; color: #1f4ea8; border-color: #b4c8ee; }
.ship-delivered       { background: #ecf8f1; color: #297d50; border-color: #b8e0c8; }

.badge-admin {
  display: inline-block;
  background: var(--nw-admin);
  color: #fff;
  padding: .1rem .45rem;
  border-radius: 999px;
  font-size: .72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
}

/* Indicateur de force de mot de passe (front + BO ; cf. public/js/password-strength.js) */
.pw-strength-bar { height: 5px; background: #e6e4ee; border-radius: 999px; overflow: hidden; }
.pw-strength-bar > span { display: block; height: 100%; width: 0; transition: width .2s ease; border-radius: 999px; }
.pw-strength-bar > span.pw-lvl-0 { background: #c0392b; }
.pw-strength-bar > span.pw-lvl-1 { background: #b8740d; }
.pw-strength-bar > span.pw-lvl-2 { background: #b8a40d; }
.pw-strength-bar > span.pw-lvl-3 { background: #2f8f5c; }
.pw-strength-bar > span.pw-lvl-4 { background: #1e7a45; }

/* Champ mot de passe avec bouton afficher/masquer (cf. public/js/ui-behaviors.js,
   data-password-toggle). L'indicateur de force est inséré APRÈS ce wrapper. */
.nw-pw-field { position: relative; }
.nw-pw-field > .form-control { padding-right: 2.75rem; }
.nw-pw-toggle {
  position: absolute; top: 0; right: 0; height: 100%; width: 2.75rem;
  display: flex; align-items: center; justify-content: center;
  border: 0; background: transparent; padding: 0; cursor: pointer;
  color: var(--bs-secondary-color, #6c757d);
  border-radius: 0 var(--bs-border-radius, .375rem) var(--bs-border-radius, .375rem) 0;
}
.nw-pw-toggle:hover { color: var(--bs-body-color, #212529); }
.nw-pw-toggle:focus-visible { outline: 2px solid var(--nw-admin, #6c5ce7); outline-offset: -2px; }

/* Messages (chat) */
.msg {
  padding: .85rem 1rem;
  border-radius: .5rem;
  background: #f7f3ed;
  border: 1px solid #e8e3dc;
}
.msg + .msg { margin-top: .65rem; }
.msg-admin { background: #eeeaf9; border-color: #d9d3f0; }
.msg-head { font-size: .85rem; margin-bottom: .35rem; }
.prewrap { white-space: pre-wrap; word-wrap: break-word; }

/* Timeline de commission : events système + messages texte, fusionnés
   chronologiquement avec une ligne verticale à gauche. */
.timeline {
  position: relative;
  padding-left: 1.75rem;
  margin: 0;
  list-style: none;
}
.timeline::before {
  content: '';
  position: absolute;
  left: .55rem;
  top: .25rem;
  bottom: .25rem;
  width: 2px;
  background: #e5dfd6;
}
.timeline > li { position: relative; margin-bottom: 1rem; }
.timeline > li::before {
  content: '';
  position: absolute;
  left: -1.45rem;
  top: .5rem;
  width: .65rem;
  height: .65rem;
  border-radius: 50%;
  background: #c4bcae;
  box-shadow: 0 0 0 3px #faf8f5;
}
body.admin .timeline > li::before { box-shadow: 0 0 0 3px #f4f3f7; }
.timeline > li.timeline-highlighted::before { background: var(--nw-accent); width: .9rem; height: .9rem; left: -1.6rem; }

/* v2.15.38 — Mise en avant temporaire d'une action ciblée par un lien `#ie…`
   (depuis le chat ou un lien copié). Pulse discret de ~2,5 s puis retour. */
.timeline > li.action-flash { animation: nw-action-flash 2.5s ease-out 1; border-radius: .5rem; }
@keyframes nw-action-flash {
  0%   { background: var(--nw-accent-soft, #efe9fb); box-shadow: 0 0 0 .35rem var(--nw-accent-soft, #efe9fb); }
  70%  { background: var(--nw-accent-soft, #efe9fb); box-shadow: 0 0 0 .35rem var(--nw-accent-soft, #efe9fb); }
  100% { background: transparent; box-shadow: 0 0 0 0 transparent; }
}

/* v2.15.38 — Bouton « copier le lien » d'une action (icône bi-copy). Discret,
   s'éclaire au survol ; confirme la copie en vert. */
.action-copy-link {
  border: 0; background: transparent; padding: 0 .15rem; line-height: 1;
  color: var(--bs-secondary-color, #6c757d); cursor: pointer; opacity: .6;
  transition: opacity .15s, color .15s;
}
.action-copy-link:hover { opacity: 1; color: var(--nw-accent, #6f42c1); }
.action-copy-link.copied { color: #198754; opacity: 1; }

/* v2.15.38 — Référence d'action collée dans le chat, rendue cliquable. */
.chat-action-link {
  white-space: nowrap; text-decoration: none; font-weight: 500;
  border: 1px solid var(--nw-accent-soft, #e4dcf7); border-radius: 999px;
  padding: 0 .45rem; font-size: .85em;
}
.chat-action-link i { margin-right: .15rem; }
.chat-action-link:hover { background: var(--nw-accent-soft, #efe9fb); }
.timeline-event {
  font-size: .85em;
  color: #5a5046;
  padding: .35rem 0;
}
.timeline-event-card {
  background: #fffdf9;
  border: 1px solid #ead29a;
  border-left: 3px solid var(--nw-accent);
  border-radius: .35rem;
  padding: .65rem .85rem;
  /* v2.11.13 — taille unique pour TOUT le contenu de la carte d'event :
     sans ça, titre + montants héritaient de la taille du body et
     paraissaient démesurés (surtout sur mobile) face aux events simples.
     Unité `em` (et pas `rem`) pour respecter aussi la police de thème du
     front (themeFontSizePx) — admin comme front à la même échelle relative. */
  font-size: .85em;
  color: #5a5046;
}
/* Titre de l'event : même taille que le corps, juste mis en avant. Les
   montants (<strong> dans le corps) suivent donc aussi cette taille.
   On neutralise la taille des titres Bootstrap (.h6/h3) dans la carte. */
.timeline-event-card strong { font-weight: 600; }
.timeline-event-card .h6,
.timeline-event-card h3,
.timeline-event-card h4 { font-size: 1em; margin-bottom: 0; }
.timeline-event-meta { font-size: .8rem; color: #8a7f72; }

/* v2.18.24 — Historique des actions (timeline) : harmonisation Soft UI sur le
   FRONT. Les cartes d'événement gardaient l'ancienne bordure dorée (#ead29a) et
   un texte brun chaud (héritage terracotta) → surface blanche, bordure neutre
   douce + filet teal à gauche, coins arrondis et légère profondeur, dans
   l'esprit des autres cartes. Connecteur/pastilles neutralisés. BO inchangé. */
body:not(.admin) .timeline::before { background: rgba(31, 29, 43, .12); }
body:not(.admin) .timeline > li::before { background: #cfcac3; }
body:not(.admin) .timeline-event,
body:not(.admin) .timeline-event-card { color: var(--bs-body-color, #1f1d2b); }
body:not(.admin) .timeline-event-card {
  background: #fff;
  border-color: rgba(31, 29, 43, .08);
  border-left-color: var(--nw-accent);
  border-radius: var(--nw-radius-sm, 9px);
  box-shadow: var(--nw-shadow-sm);
}
body:not(.admin) .timeline-event-meta { color: var(--nw-muted, #78716c); }
/* v2.18.27 — le point des événements IMPORTANTS (`timeline-highlighted`, =
   ceux rendus en carte) doit rester à la couleur de marque pour les repérer
   d'un coup d'œil : la règle front ci-dessus (`li::before` gris #cfcac3) est
   plus spécifique que `.timeline > li.timeline-highlighted::before` (l.118) et
   écrasait son teal → on le restaure, scopé front, avec un léger halo pour le
   détacher du filet. */
body:not(.admin) .timeline > li.timeline-highlighted::before {
  background: var(--nw-accent);
  box-shadow: 0 0 0 3px #faf8f5, 0 0 0 4px rgba(var(--nw-accent-rgb, 40, 167, 144),.25);
}

/* v2.18.24 — Mise en avant d'un événement ciblé depuis le chat (lien #ie…) :
   le pulse de base portait sur le <li>, dont le fond est maintenant masqué par
   la carte blanche → quasi invisible. Sur le front, on porte le flash sur la
   CARTE (fond teal visible + halo, puis retour au blanc/ombre douce), et on
   neutralise le pulse <li> pour ne pas doubler l'effet. BO inchangé. */
body:not(.admin) .timeline > li.action-flash { animation: none; }
body:not(.admin) .timeline > li.action-flash .timeline-event-card {
  animation: nw-action-flash-card 2.4s ease-out 1;
}
@keyframes nw-action-flash-card {
  0%, 55% {
    background: var(--nw-accent-soft);
    box-shadow: 0 0 0 .22rem var(--nw-accent-soft), var(--nw-shadow);
  }
  100% {
    background: #fff;
    box-shadow: var(--nw-shadow-sm);
  }
}
.signature-info {
  margin-top: .45rem;
  padding-top: .4rem;
  border-top: 1px dashed #e3d9c8;
  font-size: .72rem;
  color: #8a7f72;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  word-break: break-all;
}
.draft-pending { border-color: #ead29a; background: #fff8e8; }
.draft-approved { border-color: #b8e0c8; background: #effaf2; }
.draft-rejected { border-color: #f4b59c; background: #ffece4; }

/* ===== Chat live ===== */
.chat-card .chat-messages {
  max-height: 480px;
  min-height: 240px;
  overflow-y: auto;
  padding: 1rem;
  background: #fafaf7;
  display: flex;
  flex-direction: column;
  gap: .55rem;
}
.chat-card .msg {
  margin: 0;
  max-width: 85%;
  align-self: flex-start;
  background: #fff;
  border-color: #e8e3dc;
  /* Bulle moderne : coins arrondis avec une petite « pointe » côté émetteur
     (bas-gauche pour les messages reçus) + ombre douce (Soft UI). */
  border-radius: 1rem 1rem 1rem .25rem;
  box-shadow: 0 1px 1.5px rgba(31, 29, 43, .05);
}
.chat-card .msg-from-admin {
  background: #eeeaf9;
  border-color: #d9d3f0;
}
.chat-card .msg-self {
  align-self: flex-end;
  background: #dceaf7;
  border-color: #b3cce0;
  /* Pointe côté bas-droite pour les messages envoyés (alignés à droite). */
  border-radius: 1rem 1rem .25rem 1rem;
}
.chat-card .msg-self.msg-from-admin {
  /* Si l'admin envoie depuis sa vue, il voit son message à droite avec
     son fond violet d'admin pour cohérence visuelle */
  background: #ddd5f4;
  border-color: #c0b5e7;
}

/* Front (vue client) : bulles aux couleurs de marque — « soi » = teal (droite),
   artiste = neutre clair (gauche), pour une discussion alignée sur la charte.
   Le back-office (vue artiste) garde son code couleur violet (non scopé ici). */
body:not(.admin) .chat-card .msg-self {
  background: var(--nw-accent-soft);
  border-color: rgba(var(--nw-accent-rgb, 40, 167, 144),.30);
}
body:not(.admin) .chat-card .msg-from-admin {
  background: #fff;
  border-color: #e8e3dc;
}
/* v2.18.20 — Quand l'ARTISTE consulte/écrit depuis le FRONT (aperçu client),
   son propre message (à droite) doit rester à la couleur de marque (teal),
   comme « vos messages » côté client. Sans cette règle, `.msg-from-admin`
   (artiste = clair) gagnait sur `.msg-self` (spécificité égale, défini après)
   → bulle blanche non teintée (cf. capture mobile). La spécificité à 2 classes
   tranche en faveur du teal pour CE cas, sans changer la vue du client. */
body:not(.admin) .chat-card .msg-self.msg-from-admin {
  background: var(--nw-accent-soft);
  border-color: rgba(var(--nw-accent-rgb, 40, 167, 144),.30);
}
/* Badge « artiste » (chat) et « Back office » (nav) : couleur de marque sur le
   front. Le back-office conserve le violet (.badge-admin non scopé ailleurs). */
body:not(.admin) .badge-admin { background: var(--nw-accent); }
.msg-receipt {
  display: inline-block;
  font-size: .85rem;
  line-height: 1;
  vertical-align: middle;
}
.msg-receipt-sent { color: #999; }
.msg-receipt-read { color: #2a8a4f; }

/* Indicateur "en direct" dans l'en-tête du chat */
.chat-status {
  font-size: .78rem;
  color: #888;
  display: inline-flex;
  align-items: center;
  gap: .35rem;
}
.chat-status-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: #2a8a4f;
  animation: chat-pulse 2.5s ease-in-out infinite;
}
.chat-status-dot-error { background: #c44; animation: none; }
@keyframes chat-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .35; }
}

/* Emoji picker dans le chat */
.emoji-picker {
  width: 280px;
  max-height: 320px;
  overflow-y: auto;
}
.emoji-picker-cat-title {
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  padding: .25rem .2rem 0;
}
.emoji-picker-grid {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: .15rem;
  padding: .2rem 0;
}
.emoji-picker-item {
  background: transparent;
  border: 0;
  padding: .25rem 0;
  font-size: 1.25rem;
  line-height: 1;
  border-radius: .25rem;
  cursor: pointer;
  transition: background .1s ease;
}
.emoji-picker-item:hover {
  background: rgba(0, 0, 0, .06);
}

/* Badge messages non lus dans le tableau du dashboard admin */
.unread-badge {
  display: inline-block;
  background: var(--nw-accent);
  color: #fff;
  font-size: .72rem;
  font-weight: 700;
  padding: .12rem .45rem;
  border-radius: 999px;
  vertical-align: middle;
}

/* ===== Galerie de fichiers partagés ===== */
.attachment-tile-wrap {
  position: relative;
}
.attachment-tile {
  display: block;
  border: 1px solid #e8e3dc;
  border-radius: .4rem;
  overflow: hidden;
  text-decoration: none;
  background: #fff;
  color: #333;
  transition: transform .12s ease, box-shadow .12s ease;
  height: 100%;
}
/* Bouton supprimer (admin) : visible au survol de la tuile uniquement.
   Sur mobile / tablette tactile, on l'affiche en permanence (pas de hover). */
.attachment-delete-form {
  position: absolute;
  top: .35rem;
  right: .85rem;
  margin: 0;
  opacity: 0;
  transition: opacity .15s ease;
  z-index: 2;
}
.attachment-tile-wrap:hover .attachment-delete-form,
.attachment-tile-wrap:focus-within .attachment-delete-form {
  opacity: 1;
}
@media (hover: none) {
  .attachment-delete-form { opacity: 1; }
}
.attachment-delete-btn {
  background: rgba(255, 255, 255, .96);
  border: 1px solid #e8e3dc;
  border-radius: 50%;
  width: 28px;
  height: 28px;
  padding: 0;
  color: #c44;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: .85rem;
  box-shadow: 0 2px 5px rgba(0,0,0,.15);
  transition: background .12s ease, color .12s ease;
}
.attachment-delete-btn:hover {
  background: #c44;
  color: #fff;
}
.attachment-tile:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 14px rgba(0,0,0,.08);
  color: #333;
}
.attachment-thumb {
  width: 100%;
  height: 110px;
  background: #f4ede2 center/cover no-repeat;
}
.attachment-icon {
  width: 100%;
  height: 110px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #f4ede2;
  font-size: 2.5rem;
  color: var(--nw-accent);
}
.attachment-meta {
  padding: .4rem .55rem;
}
.attachment-name {
  font-size: .82rem;
  font-weight: 500;
}
.attachment-info {
  font-size: .7rem;
  color: #888;
  margin-top: .15rem;
}

/* ===== Sidebar admin : sous-menu collapse façon Prestashop ===== */
.sidebar-collapse-toggle .sidebar-chevron {
  transition: transform .2s ease;
}
.sidebar-collapse-toggle[aria-expanded="true"] .sidebar-chevron {
  transform: rotate(180deg);
}
.sidebar-sub {
  padding: .25rem 0;
}
.sidebar-sub-link {
  display: block;
  padding: .4rem .6rem .4rem 2.6rem;
  font-size: .88rem;
  color: #5a5046;
  text-decoration: none;
  border-radius: .35rem;
  transition: color .12s ease, background .12s ease;
  position: relative;
}
body.admin .sidebar-sub-link { color: #4a4451; }
.sidebar-sub-link:hover {
  color: var(--nw-admin);
  background: rgba(108, 92, 231, .08);
}
.sidebar-sub-link.active {
  color: var(--nw-admin);
  font-weight: 600;
}
.sidebar-sub-link.active::before {
  content: '';
  position: absolute;
  left: 1.4rem;
  top: 50%;
  transform: translateY(-50%);
  width: 4px;
  height: 14px;
  background: var(--nw-admin);
  border-radius: 2px;
}

/* Install card */
.install-card {
  max-width: 680px;
  margin: 2rem auto;
  background: #fff;
  border-radius: 12px;
  /* Fin filet teal en tête (cohérent avec les cartes d'authentification). */
  border-top: 3px solid var(--nw-accent);
  padding: 2rem 2.25rem;
  box-shadow: 0 8px 32px rgba(0,0,0,.08);
}
.stepper {
  display: flex;
  gap: .35rem;
  list-style: none;
  padding: 0;
  margin: 0 0 1.5rem;
  font-size: .78rem;
  flex-wrap: wrap;
}
.stepper li {
  padding: .3rem .65rem;
  border-radius: 999px;
  background: #f0eae1;
  color: #6b6660;
  border: 1px solid #e8e3dc;
}
.stepper li.active { background: var(--nw-accent); color: #fff; border-color: var(--nw-accent); }
.stepper li.done   { background: #ecf8f1; color: #297d50; border-color: #b8e0c8; }

.checklist { list-style: none; padding: 0; margin: 1rem 0; }
.checklist li {
  padding: .55rem .8rem;
  border-radius: .375rem;
  display: flex;
  align-items: center;
  gap: .65rem;
  margin-bottom: .25rem;
  border: 1px solid #e8e3dc;
  background: #fbfaf8;
}
.checklist li.ok { color: #2f8f5c; }
.checklist li.ko { color: #c33d3d; }

.recap {
  background: #fbfaf8;
  border: 1px solid #e8e3dc;
  border-radius: .5rem;
  padding: 1rem 1.25rem;
}
.recap-row {
  display: flex;
  justify-content: space-between;
  padding: .35rem 0;
  gap: 1rem;
  flex-wrap: wrap;
}
.recap-label { color: #6b6660; }
.recap-value { font-weight: 600; font-family: ui-monospace, "SF Mono", Menlo, monospace; word-break: break-all; }

/* Hero (home) */
.hero { padding: 3rem 0 2rem; text-align: center; }
.hero h1 { font-size: 2.4rem; font-weight: 700; }

/* Couleur d'accent appliquée aux btn-primary du front (admin garde le bleu/violet bootstrap) */
body:not(.admin) .btn-primary {
  --bs-btn-bg: var(--nw-accent);
  --bs-btn-border-color: var(--nw-accent);
  --bs-btn-hover-bg: var(--nw-accent-dark);
  --bs-btn-hover-border-color: var(--nw-accent-dark);
  --bs-btn-active-bg: var(--nw-accent-dark);
  --bs-btn-active-border-color: var(--nw-accent-dark);
}
body:not(.admin) a:not(.btn) { color: var(--nw-accent); }
body:not(.admin) a:not(.btn):hover { color: var(--nw-accent-dark); }

body.admin .btn-primary {
  --bs-btn-bg: var(--nw-admin);
  --bs-btn-border-color: var(--nw-admin);
  --bs-btn-hover-bg: #5747c7;
  --bs-btn-hover-border-color: #5747c7;
}
/* v2.19.2 — `.btn-outline-primary` en BO : le remap `--bs-primary` ne couvrait
   que `.btn-primary` (plein), pas l'outline ni l'état coché d'un `.btn-check`
   (segmented control) → ils retombaient sur le BLEU Bootstrap, hors charte.
   On themise donc l'outline primaire au violet (symétrique du front teal). */
body.admin .btn-outline-primary {
  --bs-btn-color: var(--nw-admin);
  --bs-btn-border-color: var(--nw-admin);
  --bs-btn-hover-color: #fff;
  --bs-btn-hover-bg: var(--nw-admin);
  --bs-btn-hover-border-color: var(--nw-admin);
  --bs-btn-active-color: #fff;
  --bs-btn-active-bg: var(--nw-admin-strong, #5747c7);
  --bs-btn-active-border-color: var(--nw-admin-strong, #5747c7);
  --bs-btn-focus-shadow-rgb: 108, 92, 231;
}
/* v2.20.9 — Boutons & texte « avertissement » (ambre) en BO. Le jaune Bootstrap
   par défaut (#ffc107) est illisible sur fond blanc (~1,6:1), et le correctif
   --nw-amount du front est scopé `body:not(.admin)` → il ne couvre PAS le BO.
   On themise donc .btn-warning / .btn-outline-warning / .text-warning sur l'ambre
   accessible --nw-warning (#b45309, ~4,9:1 AA), texte blanc sur aplat plein.
   Sémantique « prudence / mise en garde » conservée (bloquer, fermer, réinit.). */
body.admin .btn-warning {
  --bs-btn-bg: var(--nw-warning);
  --bs-btn-border-color: var(--nw-warning);
  --bs-btn-color: #fff;
  --bs-btn-hover-bg: var(--nw-warning-strong);
  --bs-btn-hover-border-color: var(--nw-warning-strong);
  --bs-btn-hover-color: #fff;
  --bs-btn-active-bg: var(--nw-warning-strong);
  --bs-btn-active-border-color: var(--nw-warning-strong);
  --bs-btn-active-color: #fff;
  --bs-btn-focus-shadow-rgb: 180, 83, 9;
}
body.admin .btn-outline-warning {
  --bs-btn-color: var(--nw-warning);
  --bs-btn-border-color: var(--nw-warning);
  --bs-btn-hover-color: #fff;
  --bs-btn-hover-bg: var(--nw-warning);
  --bs-btn-hover-border-color: var(--nw-warning);
  --bs-btn-active-color: #fff;
  --bs-btn-active-bg: var(--nw-warning-strong);
  --bs-btn-active-border-color: var(--nw-warning-strong);
  --bs-btn-focus-shadow-rgb: 180, 83, 9;
}
body.admin .text-warning { color: var(--nw-warning) !important; }
/* Bordures « warning » (ex. carte mode maintenance, pilote d'avenant) : le jaune
   Bootstrap #ffc107 est quasi invisible sur blanc → ambre accessible. Les FONDS
   `.bg-warning text-dark` (badges) gardent, eux, le jaune vif + texte noir
   (12,9:1, documenté au style-guide) : un remap global du token les casserait. */
body.admin .border-warning { border-color: var(--nw-warning) !important; }

/* ============================================================================
   Pages CMS — mode texte enrichi (TinyMCE)
   ============================================================================ */

/* TinyMCE produit du HTML standard sans classes Bootstrap. On applique les
   styles équivalents par sélecteurs de descendance pour que le rendu front
   soit cohérent avec le reste du site sans avoir à polluer le HTML stocké. */
.cms-content table {
  width: 100%;
  margin-bottom: 1rem;
  vertical-align: top;
  border-color: #dee2e6;
}
.cms-content table th,
.cms-content table td {
  padding: 0.55rem 0.65rem;
  border: 1px solid #dee2e6;
  vertical-align: top;
}
.cms-content table thead th {
  background-color: #f8f9fa;
  font-weight: 700;
  border-bottom: 2px solid #dee2e6;
}
.cms-content table tbody tr:hover {
  background-color: rgba(0, 0, 0, 0.04);
}
.cms-content img {
  max-width: 100%;
  height: auto;
  border-radius: 0.375rem;
}
.cms-content blockquote {
  padding: 0.5rem 0 0.5rem 1rem;
  margin: 0 0 1rem;
  border-left: 3px solid var(--nw-accent);
  color: #6c757d;
  font-style: italic;
}
.cms-content pre {
  background-color: #f8f9fa;
  padding: 1rem;
  border-radius: 0.375rem;
  border: 1px solid #dee2e6;
  overflow-x: auto;
}
.cms-content code {
  background-color: #f8f9fa;
  padding: 0.125rem 0.35rem;
  border-radius: 0.25rem;
  font-size: 0.9em;
}
.cms-content pre code {
  background: transparent;
  padding: 0;
  border-radius: 0;
}
.cms-content hr {
  margin: 1.5rem 0;
  border: 0;
  border-top: 1px solid #dee2e6;
}
.cms-content h1, .cms-content h2, .cms-content h3,
.cms-content h4, .cms-content h5, .cms-content h6 {
  margin-top: 1.25rem;
  margin-bottom: 0.75rem;
}
.cms-content ul, .cms-content ol {
  padding-left: 2rem;
  margin-bottom: 1rem;
}
.cms-content a:not(.btn) {
  color: var(--nw-accent-dark);
  text-decoration: underline;
}

/* ============================================================================
   EditorJS — host area dans le back office
   ============================================================================ */

/* Quand on édite une page en mode visuel (EditorJS), le conteneur de l'éditeur
   reçoit les blocs au fil de la frappe. On lui donne un look discret qui matche
   les autres champs de formulaire du back office. */
.editorjs-host { min-height: 400px; background: #fff; }
.editorjs-host .ce-block__content,
.editorjs-host .ce-toolbar__content { max-width: none; }
/* Aligne les styles des blocs édités avec ce qu'on rend sur le front pour
   que l'admin voie immédiatement à quoi ça ressemblera (Bootstrap-like). */
.editorjs-host .cdx-list { padding-left: 1.5rem; }
.editorjs-host .cdx-quote { border-left: 4px solid #dee2e6; padding-left: 1rem; }
.editorjs-host .cdx-warning { background: #fff3cd; border: 1px solid #ffe69c; border-radius: .375rem; padding: .75rem 1rem; }
/* Empilement des popovers EditorJS — la stratégie a deux étages.
   Contexte : chaque `.ce-block` crée son propre stacking context (position:
   relative implicite), donc un popover INSIDE un bloc table reste localement
   piégé sous le bloc suivant. Solution : `:has()` hisse le bloc parent.

   Mais ATTENTION : la toolbar inline (`.ce-inline-toolbar`) est appendée à
   l'`Editor.UI.nodes.wrapper` (root de l'éditeur), PAS dans un `.ce-block`.
   Donc si on monte un `.ce-block` à 1051 (cas où il a un popover ouvert),
   il finit AU-DESSUS de la toolbar inline qui était à 1050. Fix : toolbar
   inline à 1100 (au-dessus des blocs hissés). Les popovers de TABLE
   (`.tc-popover`/`.tc-toolbox`) sont, eux, à l'intérieur du bloc → couverts par
   le hissage du bloc ci-dessous. */
.editorjs-host .ce-block:has(.tc-popover--opened),
.editorjs-host .ce-block:has(.tc-toolbox--showed),
.editorjs-host .ce-block:has(.ce-popover--opened) {
  position: relative;
  z-index: 1051;
}
.editorjs-host .tc-popover,
.editorjs-host .tc-toolbox,
.editorjs-host .ce-popover,
.editorjs-host .ce-popover--opened,
.editorjs-host .ce-conversion-toolbar { z-index: 1050; }
/* Toolbar inline (Gras/Italique/Lien/etc.) au-dessus des blocs hissés. */
.editorjs-host .ce-inline-toolbar,
.editorjs-host .ce-inline-toolbar .ce-popover,
.editorjs-host .ce-inline-toolbar .ce-popover--opened { z-index: 1100; }
/* v2.15.43 — Menu ⚙️ « block tunes » (Monter/Descendre/Supprimer…). Depuis
   EditorJS 2.30+, ce menu est un `.ce-popover` rendu dans `.ce-toolbar`
   (zone `.ce-toolbar__actions`, z-index:2 vendor), PAS dans `.ce-block` → le
   hissage de bloc ci-dessus ne l'attrape pas, et il passe sous le bloc suivant
   qui crée un contexte d'empilement (outil table : `.tc-wrap` position:relative
   z-index:0). On donne à la toolbar flottante un contexte au-dessus de tout
   le contenu, UNIQUEMENT quand son popover est ouvert (état fermé inchangé). */
.editorjs-host .ce-toolbar:has(.ce-popover--opened) { z-index: 1101; }

/* ============================================================================
   Back office — sidebar
   ============================================================================ */
/* === Shell admin : sidebar collée au bord gauche + contenu full-width ===
   Inspiré de PrestaShop : navbar full-width en haut, puis colonne sidebar
   fixe à gauche (sticky scroll) et zone main qui prend tout l'espace
   restant. Mobile (< lg) : la sidebar est cachée via d-none d-lg-block,
   navigation par le burger menu. */
.admin-shell {
  background: #f5f4f7;
}
/* === Sidebar SOMBRE (v2.18.41 ; tiroir mobile aligné en sombre v2.18.59) ===
   La sidebar desktop ET le tiroir off-canvas mobile (.nw-admin-drawer) partagent
   le thème sombre. Les couleurs restent scopées par sélecteur (sidebar / drawer)
   car le drawer doit aussi habiller son en-tête offcanvas et son bas de menu. */
.admin-sidebar {
  --side: #1f1d2b;          /* fond sidebar */
  --side2: #2a2740;         /* survol / collapse */
  --side-ink: #e9e7f5;      /* texte principal (liens de 1er niveau) */
  --side-link: #c5c1de;     /* sous-liens (lisibles sur fond sombre) */
  --side-mut: #9a96b8;      /* texte secondaire (badge version…) */
  --side-lab: #8480a6;      /* libellés de section */
  --side-bd: #332f4d;       /* filets internes */
  width: 248px;
  flex-shrink: 0;
  background: var(--side);
  padding: .25rem .5rem .75rem;
  /* L'aside s'étire sur toute la hauteur du shell (align-items: stretch) → le
     fond sombre descend jusqu'en bas même si le contenu est court. */
}
.admin-sidebar > nav {
  /* La nav reste épinglée au scroll (sticky) ; pas de scroll interne (un seul
     ascenseur global, comme avant — c'est la PAGE qui s'allonge). */
  position: sticky;
  top: .5rem;
  flex-wrap: nowrap; /* empilement vertical (Bootstrap .nav est wrap par défaut) */
}
/* Marque en haut de la sidebar (remplace le bandeau navbar plein largeur). */
.admin-brand {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .85rem .8rem 1rem;
  color: #fff;
  font-weight: 700;
  text-decoration: none;
  font-size: 1.05rem;
}
.admin-brand:hover { color: #fff; }
.admin-brand .admin-brand-ico { color: var(--nw-admin); font-size: 1.2rem; }
.admin-brand .admin-brand-name { line-height: 1; }
.admin-brand .admin-brand-v {
  font-size: .6rem; background: var(--side2); color: var(--side-mut);
  padding: .12rem .38rem; border-radius: 5px; font-weight: 600; letter-spacing: .02em;
}
/* Libellés de section (Travail / Configuration). */
.admin-sidebar .sidebar-section {
  font-size: .62rem; letter-spacing: .09em; text-transform: uppercase;
  color: var(--side-lab); font-weight: 700;
  padding: .9rem .9rem .25rem;
}
.admin-sidebar .sidebar-section:first-child { padding-top: .3rem; }

.admin-main {
  min-width: 0; /* évite que le contenu déborde en flex avec des tables/longs strings */
  padding: 1.5rem;
}
@media (min-width: 768px) {
  .admin-main { padding: 1.5rem 2rem; }
}

/* --- Liens : forme commune (sidebar sombre + drawer clair) --- */
.admin-sidebar .nav-pills .nav-link,
.nw-admin-drawer .nav-pills .nav-link {
  border-radius: 8px;
  padding: .5rem .8rem;
  margin: .05rem .25rem;
  font-weight: 500;
  font-size: .92rem;
}
.admin-sidebar .nav-pills .nav-link i.bi,
.nw-admin-drawer .nav-pills .nav-link i.bi {
  width: 1.15rem;
  display: inline-block;
}
/* --- Couleurs : SIDEBAR SOMBRE --- */
.admin-sidebar .nav-pills .nav-link { color: var(--side-ink); }
.admin-sidebar .nav-pills .nav-link:hover { background: var(--side2); color: #fff; }
.admin-sidebar .nav-pills .nav-link.active {
  background: var(--nw-admin); color: #fff;
  box-shadow: 0 4px 12px rgba(108, 92, 231, .35);
}
.admin-sidebar .sidebar-sub {
  margin: .1rem .25rem .35rem 1.55rem;
  border-left: 1px solid var(--side-bd);
  padding-left: .2rem;
}
/* ⚠️ Préfixé `body.admin` pour passer DEVANT `body.admin .sidebar-sub-link`
   (couleur claire #…, sinon le tag `body` lui donnait une spécificité
   supérieure → sous-liens rendus en gris foncé #4a4451, illisibles sur le
   fond sombre — bug v2.18.41). */
body.admin .admin-sidebar .sidebar-sub-link { color: var(--side-link); }
body.admin .admin-sidebar .sidebar-sub-link:hover { color: #fff; background: var(--side2); }
body.admin .admin-sidebar .sidebar-sub-link.active { color: #fff; background: rgba(108, 92, 231, .22); font-weight: 600; }
body.admin .admin-sidebar .sidebar-sub-link.active::before { background: var(--nw-admin); }
/* --- DRAWER MOBILE : thème SOMBRE identique à la sidebar (v2.18.59) --- */
.nw-admin-drawer {
  --side: #1f1d2b; --side2: #2a2740; --side-ink: #e9e7f5; --side-link: #c5c1de;
  --side-mut: #9a96b8; --side-lab: #8480a6; --side-bd: #332f4d;
  --bs-offcanvas-bg: var(--side);
  --bs-offcanvas-color: var(--side-ink);
}
.nw-admin-drawer .offcanvas-header { border-bottom-color: var(--side-bd) !important; }
.nw-admin-drawer .offcanvas-title { color: #fff; }
.nw-admin-drawer .btn-close { filter: invert(1) grayscale(100%) brightness(200%); }
.nw-admin-drawer .text-muted { color: var(--side-mut) !important; }
.nw-admin-drawer .border-bottom,
.nw-admin-drawer .border-top { border-color: var(--side-bd) !important; }
/* Liens de nav (1er niveau) */
.nw-admin-drawer .nav-pills .nav-link { color: var(--side-ink); }
.nw-admin-drawer .nav-pills .nav-link:hover { background: var(--side2); color: #fff; }
.nw-admin-drawer .nav-pills .nav-link.active {
  background: var(--nw-admin); color: #fff; box-shadow: 0 4px 12px rgba(108, 92, 231, .35);
}
.nw-admin-drawer .sidebar-section {
  font-size: .62rem; letter-spacing: .09em; text-transform: uppercase;
  color: var(--side-lab); font-weight: 700; padding: .8rem .9rem .2rem;
}
/* Sous-liens (préfixé body.admin pour la spécificité, cf. sidebar) */
.nw-admin-drawer .sidebar-sub {
  margin: .1rem .25rem .35rem 1.55rem; border-left: 1px solid var(--side-bd); padding-left: .2rem;
}
body.admin .nw-admin-drawer .sidebar-sub-link { color: var(--side-link); }
body.admin .nw-admin-drawer .sidebar-sub-link:hover { color: #fff; background: var(--side2); }
body.admin .nw-admin-drawer .sidebar-sub-link.active { color: #fff; background: rgba(108, 92, 231, .22); font-weight: 600; }
body.admin .nw-admin-drawer .sidebar-sub-link.active::before { background: var(--nw-admin); }
/* Bas de menu (Mon compte / Front office / Déconnexion) */
.nw-admin-drawer .nw-mobile-nav .nav-link { color: var(--side-ink); border-bottom-color: var(--side-bd); }
.nw-admin-drawer .nw-mobile-nav .nav-link:hover,
.nw-admin-drawer .nw-mobile-nav .nav-link:active { background: var(--side2); }

/* === Top bar fine du BO (≈40px, desktop) === */
.admin-topbar {
  height: 40px;
  margin: -1.5rem -2rem 1.25rem;   /* full-bleed : annule le padding de .admin-main (lg) */
  padding: 0 1rem 0 1.25rem;
  background: #fff;
  border-bottom: 1px solid var(--nw-border, #e6e4ee);
}
.admin-topbar .admin-topbar-link,
.admin-topbar .admin-topbar-btn {
  display: inline-flex; align-items: center;
  height: 28px; padding: 0 .55rem;
  border: 0; background: transparent;
  color: #5a5570; text-decoration: none;
  font-size: .82rem; border-radius: 7px;
}
.admin-topbar .admin-topbar-link:hover,
.admin-topbar .admin-topbar-btn:hover { background: var(--nw-admin-soft, rgba(108,92,231,.1)); color: var(--nw-admin); }
.admin-topbar .admin-topbar-btn .bi { font-size: 1.05rem; }
.admin-topbar .admin-topbar-profile { font-weight: 500; }
/* Encart « Mise à jour dispo » : pilule pleine (violet de marque) bien visible,
   avec un léger halo pulsé pour attirer l'œil sans être agressif. */
.admin-topbar .admin-topbar-update {
  display: inline-flex; align-items: center;
  margin-right: .25rem; padding: .15rem .65rem;
  border-radius: 999px; white-space: nowrap;
  background: var(--nw-admin, #6c5ce7); color: #fff;
  font-size: .8rem; font-weight: 600; text-decoration: none;
  box-shadow: 0 0 0 0 rgba(var(--nw-admin-rgb, 108,92,231), .5);
  animation: nw-update-pulse 2.4s ease-out infinite;
}
.admin-topbar .admin-topbar-update:hover {
  background: var(--nw-admin-strong, #5747c7); color: #fff; animation: none;
}
@keyframes nw-update-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(var(--nw-admin-rgb, 108,92,231), .45); }
  70%  { box-shadow: 0 0 0 .4rem rgba(var(--nw-admin-rgb, 108,92,231), 0); }
  100% { box-shadow: 0 0 0 0 rgba(var(--nw-admin-rgb, 108,92,231), 0); }
}
@media (prefers-reduced-motion: reduce) {
  .admin-topbar .admin-topbar-update { animation: none; }
}
/* Pastille de la cloche (top bar + barre d'actions mobile) */
.admin-bell-badge {
  position: absolute; top: 2px; right: 2px;
  min-width: 16px; height: 16px; padding: 0 4px;
  background: var(--dash-danger, #c0392b); color: #fff;
  font-size: .62rem; font-weight: 700; line-height: 16px;
  border-radius: 999px; text-align: center;
}
.admin-bell-menu { width: 340px; max-width: 92vw; padding: 0; overflow: hidden; }
/* Présence de l'équipe (top bar) — pastille verte = en ligne, grise = absent. */
.nw-presence-badge {
  position: absolute; top: 2px; right: 0;
  min-width: 15px; height: 15px; padding: 0 3px;
  background: var(--nw-success, #2f8f5c); color: #fff;
  font-size: .6rem; font-weight: 700; line-height: 15px;
  border-radius: 999px; text-align: center;
}
.nw-presence-dot {
  width: .55rem; height: .55rem; border-radius: 50%; flex: 0 0 auto;
  background: #c7c5d2; box-shadow: 0 0 0 2px rgba(0,0,0,.03);
}
.nw-presence-dot.is-online { background: var(--nw-success, #2f8f5c); }
/* Menu déroulant de la loupe de recherche (top bar). */
.admin-search-menu { width: 320px; max-width: 92vw; }
.admin-bell-menu .admin-bell-head {
  padding: .6rem .85rem; border-bottom: 1px solid var(--nw-border, #e6e4ee);
  font-weight: 600; font-size: .85rem; display: flex; justify-content: space-between; align-items: center;
}
.admin-bell-menu .admin-bell-list { max-height: 60vh; overflow-y: auto; }
.admin-bell-menu .admin-bell-item {
  display: flex; gap: .6rem; align-items: center;
  padding: .55rem .85rem; text-decoration: none; color: inherit;
  border-bottom: 1px solid #f1eff7;
}
.admin-bell-menu .admin-bell-item:hover { background: var(--nw-admin-soft, rgba(108,92,231,.08)); }
.admin-bell-menu .admin-bell-ico {
  width: 2rem; height: 2rem; flex: 0 0 2rem; border-radius: 7px;
  display: inline-flex; align-items: center; justify-content: center; font-size: 1rem;
  background: var(--nw-admin-soft, rgba(108,92,231,.1)); color: var(--nw-admin);
}
.admin-bell-menu .admin-bell-ico.tone-amber { background: var(--dash-amber-soft, #fdf3e3); color: var(--dash-amber, #b8740d); }
.admin-bell-menu .admin-bell-ico.tone-danger { background: var(--dash-danger-soft, #fdecec); color: var(--dash-danger, #c0392b); }
.admin-bell-menu .admin-bell-n {
  margin-left: auto; flex: 0 0 auto; font-weight: 700; font-size: .72rem;
  min-width: 1.25rem; height: 1.25rem; padding: 0 .35rem; line-height: 1.25rem;
  text-align: center; border-radius: 999px;
  background: var(--nw-admin-soft, rgba(108,92,231,.12)); color: var(--nw-admin);
}
.admin-bell-menu .admin-bell-foot { padding: .5rem .85rem; text-align: center; border-top: 1px solid var(--nw-border, #e6e4ee); }
/* Cellule cloche de la barre d'actions mobile */
.nw-action-bar .nw-bell-cell { flex: 1 1 0; display: flex; }
.nw-action-bar .nw-bell-cell .nw-action-cell { width: 100%; }
/* Badge version dans le header admin (à côté du nom, façon PrestaShop). */
.nw-version-badge {
  background: rgba(255, 255, 255, .15);
  color: rgba(255, 255, 255, .85);
  font-weight: 600;
  font-size: .7rem;
  letter-spacing: .02em;
  margin-left: .5rem;
}

/* ===========================================================================
   Tableau de bord BO (v2.18.37) — style « data-dense », identité violette
   (--nw-admin). Scopé body.admin : n'affecte pas le front (teal Soft UI).
   Cartes nettes à fine bordure + ombre très légère, grille serrée.
   ======================================================================== */
body.admin {
  /* === Design system BO (v2.18.40) — source de vérité unique ===
     Tokens sémantiques scopés body.admin. Identité VIOLETTE conservée
     (--nw-admin). Les anciens noms (--dash-*) restent comme alias pour ne
     rien casser dans les vues existantes. */
  --nw-admin-soft: rgba(108, 92, 231, .10);
  --nw-row-hover: #f4f2fd;   /* survol de ligne : violet doux (aplat, lit violet et non gris) */
  --nw-admin-strong: #5747c7;          /* survol / actif appuyé */
  /* v2.18.54 — Remap de la couleur « primary » de Bootstrap sur le violet de
     marque : tout ce qui s'appuie sur --bs-primary (bg-primary, text-primary,
     border-primary, badge bg-primary, nav-pills actif, pagination…) devient
     VIOLET côté BO, au lieu du bleu Bootstrap par défaut. Scopé body.admin →
     le front garde son teal. Évite des dizaines de surcharges ponctuelles. */
  --bs-primary: #6c5ce7;
  --bs-primary-rgb: 108, 92, 231;
  /* Liens de contenu Bootstrap en violet (les liens de composants — nav-link,
     dropdown-item, page-link — gardent leurs couleurs propres, plus spécifiques). */
  --bs-link-color: #5747c7;
  --bs-link-color-rgb: 87, 71, 199;
  --bs-link-hover-color: #4536a8;
  --nw-bg: #f4f3f7;                     /* fond de page */
  --nw-surface: #fff;                   /* surface panneau (cartes) */
  --nw-surface-2: #fafafb;              /* surface secondaire (en-têtes, filtres) */
  --nw-border: #e6e4ee;                 /* filets / bordures */
  --nw-ink: #2b2740;                    /* encre titres */
  --nw-ink-muted: #6f6c86;              /* encre secondaire / labels */
  --nw-radius: 12px;                    /* rayon carte */
  --nw-radius-sm: 8px;                  /* rayon contrôle */
  --nw-shadow-sm: 0 1px 2px rgba(31, 36, 48, .04);
  --nw-shadow: 0 4px 14px rgba(31, 36, 48, .08);
  --nw-success: #2f8f5c;
  --nw-success-soft: #ecf8f1;
  --nw-info: #1f7a8c;
  --nw-info-soft: #e8f4f8;
  /* v2.20.9 — Ambre « avertissement » ACCESSIBLE pour le BO. Le jaune Bootstrap
     par défaut (#ffc107) est illisible sur blanc (~1,6:1) ; on s'aligne sur
     l'ambre foncé du front (--nw-amount #b45309, ~4,9:1 AA). */
  --nw-warning: #b45309;
  --nw-warning-strong: #92400e;        /* ambre appuyé (survol / actif plein) */
  --nw-warning-soft: #fdf3e3;          /* aplat doux (encarts) */
  /* Alias historiques (conservés) */
  --dash-amber: #b8740d;
  --dash-amber-soft: #fdf3e3;
  --dash-danger: #c0392b;
  --dash-danger-soft: #fdecec;
  --dash-border: var(--nw-border);
}
body.admin .dash-card {
  background: var(--nw-surface);
  border: 1px solid var(--nw-border);
  border-radius: var(--nw-radius);
  box-shadow: var(--nw-shadow-sm);
}
body.admin .dash-h {
  font-size: .7rem; text-transform: uppercase; letter-spacing: .04em;
  font-weight: 700; color: #6f6c86;
}
/* File d'actions */
body.admin .dash-action {
  display: flex; align-items: center; gap: .7rem; text-decoration: none; color: inherit;
  padding: .65rem .8rem; background: #fff; border: 1px solid var(--dash-border); border-radius: 10px;
  height: 100%; transition: box-shadow .15s ease, transform .15s ease, border-color .15s ease;
}
body.admin .dash-action:hover {
  box-shadow: 0 4px 14px rgba(31, 36, 48, .08); transform: translateY(-2px); border-color: #d8d4ec;
}
body.admin .dash-action.is-zero { opacity: .5; }
body.admin .dash-action-ico {
  width: 2.4rem; height: 2.4rem; flex: 0 0 2.4rem; border-radius: 8px;
  display: inline-flex; align-items: center; justify-content: center; font-size: 1.15rem;
}
body.admin .dash-action-ico.tone-violet { background: var(--nw-admin-soft); color: var(--nw-admin); }
body.admin .dash-action-ico.tone-amber { background: var(--dash-amber-soft); color: var(--dash-amber); }
body.admin .dash-action-ico.tone-danger { background: var(--dash-danger-soft); color: var(--dash-danger); }
body.admin .dash-action-body { min-width: 0; line-height: 1.15; }
body.admin .dash-action-num { display: block; font-size: 1.45rem; font-weight: 700; }
body.admin .dash-action-lbl { display: block; font-size: .8rem; color: #6b6b80; }
/* Indicateurs */
body.admin .kpi-lbl {
  font-size: .72rem; text-transform: uppercase; letter-spacing: .03em;
  font-weight: 600; color: #6f6c86; margin-bottom: .15rem;
}
body.admin .kpi-val { font-size: 1.6rem; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
/* Tendance / sous-texte d'un KPI (v2.25.8). La couleur ne porte JAMAIS seule
   l'info : une flèche (↗/↘) accompagne toujours le signe (a11y color-not-only). */
body.admin .kpi-trend {
  display: flex; align-items: center; gap: .25rem;
  margin-top: .3rem; font-size: .78rem; font-weight: 600;
  font-variant-numeric: tabular-nums;
}
body.admin .kpi-trend.is-up { color: var(--nw-success, #2f8f5c); }
body.admin .kpi-trend.is-down { color: #c0392b; }
body.admin .kpi-trend-ref {
  margin-top: .3rem; font-size: .72rem; font-weight: 500;
  color: var(--nw-ink-muted, #6f6c86); line-height: 1.25;
}
body.admin .kpi-trend .kpi-trend-ref { margin-top: 0; font-weight: 400; }
/* Conteneur de graphique : hauteur FIXE + position relative (requis par
   Chart.js en maintainAspectRatio:false, sinon le canvas grandit à l'infini). */
body.admin .dash-chart { position: relative; width: 100%; height: 240px; }
/* En-tête + barre de filtres internes à une carte (liste encapsulée) */
body.admin .dash-card-head { padding: .8rem 1rem; border-bottom: 1px solid var(--dash-border); }
body.admin .dash-card-filters { padding: .8rem 1rem; border-bottom: 1px solid var(--dash-border); background: #fafafb; }
/* En-têtes de table data-dense */
body.admin .dash-card .table thead th {
  font-size: .72rem; text-transform: uppercase; letter-spacing: .03em;
  color: #6f6c86; border-bottom: 1px solid var(--dash-border); background: #fafafb;
}
body.admin .dash-card .table-hover tbody tr:hover { background: var(--nw-row-hover); }
/* État vide BO */
body.admin .nw-empty-admin {
  display: inline-flex; align-items: center; justify-content: center;
  width: 3.5rem; height: 3.5rem; border-radius: 50%;
  background: var(--nw-admin-soft); color: var(--nw-admin); font-size: 1.5rem;
}
@media (prefers-reduced-motion: reduce) {
  body.admin .dash-action { transition: none; }
  body.admin .dash-action:hover { transform: none; }
}

/* ===========================================================================
   Harmonisation BO (v2.18.40) — généralise la surface « panneau » du
   tableau de bord (.dash-card) à TOUTES les cartes Bootstrap (.card) du
   back-office, plus un en-tête de page partagé (.admin-page-head) et le
   survol de ligne sur toutes les tables. Scopé body.admin → n'affecte pas
   le front. Aucune vue à éditer : les 54 vues partagent .card / .table.
   ======================================================================== */

/* Cartes : même surface nette à fine bordure + ombre douce que .dash-card.
   On neutralise l'ombre Bootstrap par défaut pour une densité homogène. */
body.admin .card {
  border: 1px solid var(--nw-border);
  border-radius: var(--nw-radius);
  box-shadow: var(--nw-shadow-sm);
}
/* Tables/headers à fleur de carte : les coins CARRÉS du thead (table-light) ou
   d'un .card-header « coupent » l'arrondi de la carte. On clippe la carte à son
   rayon — UNIQUEMENT quand un enfant direct est une table ou un table-responsive
   (= cartes-listes), pour ne PAS rogner les menus déroulants des cartes à
   formulaire/chat (qui ont leur table dans un .card-body, pas en enfant direct). */
body.admin .card:has(.table-responsive):not(:has(.dropdown-menu)),
body.admin .card:has(> .table):not(:has(.dropdown-menu)),
body.admin .dash-card:has(> .table-responsive),
body.admin .dash-card:has(> .dash-card-head) {
  overflow: hidden;
}
body.admin .card > .card-header {
  background: var(--nw-surface-2);
  border-bottom: 1px solid var(--nw-border);
  font-weight: 600;
  color: var(--nw-ink);
}
/* En-têtes de table « capitales discrètes » même hors .dash-card, pour que
   toutes les listes (clients, commandes, factures, logs…) soient identiques. */
body.admin .card .table > thead > tr > th {
  background: var(--nw-surface-2);
  color: var(--nw-ink-muted);
  border-bottom: 1px solid var(--nw-border);
}
/* Survol de ligne violet doux, partout (avant : uniquement dans .dash-card). */
body.admin .table-hover > tbody > tr:hover > * {
  background: var(--nw-row-hover);
}
/* Chiffres alignés (prix, quantités, références) → pas de décalage de colonne. */
body.admin .table td .nw-num,
body.admin .table .text-end,
body.admin .nw-tabular { font-variant-numeric: tabular-nums; }
/* Gouttières de bord des tables-listes BO (1re/dernière cellule) : le contenu
   ne colle pas les bords arrondis de la carte (ex. icône PDF à droite, date à
   gauche). table-sm garde des colonnes denses au milieu. */
body.admin .table-responsive > table > thead > tr > :first-child,
body.admin .table-responsive > table > tbody > tr > :first-child,
body.admin .table-responsive > table > tfoot > tr > :first-child { padding-left: 1rem; }
body.admin .table-responsive > table > thead > tr > :last-child,
body.admin .table-responsive > table > tbody > tr > :last-child,
body.admin .table-responsive > table > tfoot > tr > :last-child { padding-right: 1rem; }
/* Ligne « Total » (tfoot) : séparateur net + respiration (sinon « écrasée » en
   table-sm) + teinte de surface au lieu du gris Bootstrap. */
body.admin .table-responsive > table > tfoot > tr > td,
body.admin .table-responsive > table > tfoot > tr > th {
  background: var(--nw-surface-2);
  border-top: 2px solid var(--nw-border);
  padding-top: .55rem; padding-bottom: .55rem;
  font-weight: 600; color: var(--nw-ink);
}

/* ── Front (espace client) : finitions « Soft UI » teal des cartes-listes ────
   Les règles de table ci-dessus sont scopées `body.admin`. On en porte
   l'équivalent au FRONT (ex. « Mes commissions ») : la carte clippe la table à
   son rayon (coins bas arrondis, le bug constaté), en-tête teinté teal, survol
   de ligne teal doux, chiffres tabulaires. `body:not(.admin)` = espace client. */
body:not(.admin) .card:has(> .table-responsive):not(:has(.dropdown-menu)),
body:not(.admin) .card:has(> .table):not(:has(.dropdown-menu)) {
  overflow: hidden;
}
body:not(.admin) .card .table > thead > tr > th {
  background: rgba(var(--nw-accent-rgb, 40, 167, 144), .06);
  color: #5b6b67;
  font-weight: 600;
  border-bottom: 1px solid #e7efed;
}
body:not(.admin) .table-hover > tbody > tr:hover > * {
  background: rgba(var(--nw-accent-rgb, 40, 167, 144), .05);
}
body:not(.admin) .table td .nw-num,
body:not(.admin) .table .text-end { font-variant-numeric: tabular-nums; }

/* Lignes de liste cliquables (data-row-href, JS ui-behaviors) : curseur main.
   Le survol de ligne existant sert déjà d'indice visuel ; les liens/boutons
   internes gardent leur propre curseur. */
tr[data-row-href] { cursor: pointer; }

/* En-tête de page partagé (_admin-page-head.ejs) : titre + icône + sous-titre
   + zone d'actions à droite. Remplace le d-flex recopié dans chaque vue. */
body.admin .admin-page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: .75rem;
  margin-bottom: 1.25rem;
}
/* Titre/sous-titre/retour : utilisables DANS .admin-page-head (vues d'index)
   OU seuls (pages détail/formulaire, où un lien retour précède le titre). */
body.admin .admin-page-title {
  font-size: 1.35rem;
  font-weight: 700;
  color: var(--nw-ink);
  margin: 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .5rem;
}
/* Seule l'icône de tête (enfant DIRECT) est violette ; les icônes des badges
   imbriqués gardent la couleur de leur badge. */
body.admin .admin-page-title > .bi { color: var(--nw-admin); }
body.admin .admin-page-sub {
  font-size: .85rem;
  color: var(--nw-ink-muted);
  margin-top: .15rem;
}
/* Lien retour des pages détail/formulaire (← Liste). */
body.admin .admin-page-back {
  display: inline-flex;
  align-items: center;
  font-size: .8rem;
  color: var(--nw-ink-muted);
  text-decoration: none;
  margin-bottom: .4rem;
}
body.admin .admin-page-back:hover { color: var(--nw-admin); }
body.admin .admin-page-head .admin-page-actions {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  align-items: center;
}

/* Bouton secondaire/outline cohérent avec l'identité violette. */
body.admin .btn-outline-secondary {
  --bs-btn-color: #5a5570;
  --bs-btn-border-color: #c7c2da;   /* bordure nette : le bouton reste lisible même sur une ligne survolée */
  --bs-btn-hover-bg: var(--nw-admin-soft);
  --bs-btn-hover-border-color: var(--nw-admin);
  --bs-btn-hover-color: var(--nw-admin);
  --bs-btn-active-bg: var(--nw-admin-soft);
  --bs-btn-active-border-color: var(--nw-admin);
  --bs-btn-active-color: var(--nw-admin);
}
body.admin .btn-primary { --bs-btn-active-bg: var(--nw-admin-strong); --bs-btn-active-border-color: var(--nw-admin-strong); }

/* (Le « polish sidebar v2.18.40 » a été remplacé par le thème sombre v2.18.41
   plus haut — padding/ombre/sous-menu y sont définis.) */

/* ===== Mobile (v2.18.40) ===== */
/* Sur téléphone : on resserre la gouttière de la zone principale pour laisser
   plus de largeur aux tables denses et aux cartes KPI. */
@media (max-width: 575.98px) {
  body.admin .admin-main { padding: 1rem .75rem; }
  body.admin .admin-page-head { margin-bottom: 1rem; }
  body.admin .admin-page-title { font-size: 1.2rem; }
  /* L'en-tête de page passe en pile : actions sous le titre, pleine largeur. */
  body.admin .admin-page-head .admin-page-actions { width: 100%; }
  body.admin .admin-page-head .admin-page-actions .btn { flex: 1 1 auto; }
  /* Indicateurs KPI un peu plus compacts pour tenir à deux par ligne. */
  body.admin .kpi-val { font-size: 1.35rem; }
  body.admin .dash-action-num { font-size: 1.25rem; }
  /* Boutons d'action dans les tables : cible tactile plus confortable. */
  body.admin .table td .btn-sm { min-height: 40px; }
  body.admin .table td .btn-sm + .btn-sm,
  body.admin .table td form + form,
  body.admin .table td .btn-sm + form { margin-left: .35rem; }
}
/* Tiroir mobile : liens confortables au doigt (≥ 44px, cf. skill « touch
   target »), comme la sidebar. */
.nw-admin-drawer .nav-pills .nav-link,
.nw-admin-drawer .nw-mobile-nav .nav-link {
  padding: .65rem .85rem; min-height: 44px; display: flex; align-items: center;
}
/* Scroll horizontal fluide (momentum iOS) pour les tables qui débordent. */
body.admin .table-responsive { -webkit-overflow-scrolling: touch; }

/* Titre "Mon compte" (front) — petit accent coloré sous le titre,
   façon PrestaShop. Couleur héritée de l'accent du thème. */
.nw-account-title {
  position: relative;
  font-weight: 700;
  padding-bottom: .5rem;
}
.nw-account-title::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 2.5rem;
  height: 3px;
  background: var(--nw-accent, #c95a4a);
  border-radius: 2px;
}

/* Sidebar "Mon compte" — list-group façon PrestaShop. Item actif :
   texte en gras avec couleur d'accent, pas de background plein ni de
   barre verticale. Scopé .nw-account-nav pour ne pas affecter les
   autres list-group de l'app. */
.nw-account-nav .list-group-item-action {
  color: #5a5046;
  transition: color .12s ease, background .12s ease;
}
.nw-account-nav .list-group-item-action:hover {
  color: var(--nw-accent, #c95a4a);
  background: rgba(0, 0, 0, .035);
}
.nw-account-nav .list-group-item-action.active {
  background: transparent;
  border-color: rgba(0, 0, 0, .125);
  color: var(--nw-accent, #c95a4a);
  font-weight: 600;
}

/* === Page /prestations — cards de la grille tarifs ===
   Image au format portrait (3/4) cohérent quel que soit le ratio source,
   recadrage centré via object-fit. Effet hover discret. */
.nw-pres-card {
  /* Largeur max d'une card : empêche qu'une seule prestation (ou un grand
     écran) n'étire la card. mx-auto (dans la vue) la centre dans sa colonne. */
  max-width: 280px;
  width: 100%;
  transition: box-shadow .15s ease, transform .15s ease;
}
.nw-pres-card:hover { box-shadow: 0 .4rem .8rem rgba(0,0,0,.08); transform: translateY(-2px); }
.nw-pres-img {
  aspect-ratio: 3 / 4;
  object-fit: cover;
  background: #f0eee9;
}


/* === Page /prestations — section Options === */
.nw-section-title { position: relative; padding-bottom: .4rem; }
.nw-section-title::after {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 2.5rem; height: 3px; background: var(--nw-accent, #c95a4a); border-radius: 2px;
}
.nw-option-card { overflow: hidden; }
.nw-option-img { object-fit: cover; min-height: 200px; background: #f0eee9; }

/* ===== UI primitives partagées (front + back office) =====
   Tiroir off-canvas + barre d'actions sous l'en-tête (façon PrestaShop).
   Déplacées depuis theme.js (où elles n'étaient générées que pour le
   front) → désormais toujours chargées via app.css côté admin aussi,
   sans quoi les cellules de la barre d'actions du BO retombaient en
   liens inline. Couleurs sobres et neutres ; variantes thématiques
   (color: var(--nw-link)…) appliquées en surcharge depuis theme.js. */
.nw-mobile-menu { max-width: 86vw; width: 320px; }
.nw-mobile-nav { --bs-nav-link-padding-y: 0; }
.nw-mobile-nav .nav-link {
  padding: .85rem 1rem;
  color: #333;
  border-bottom: 1px solid rgba(0,0,0,.06);
  font-weight: 500;
}
.nw-mobile-nav .nav-link:hover,
.nw-mobile-nav .nav-link:active { background: rgba(0,0,0,.03); }
.nw-mobile-nav.border-top .nav-link { font-weight: 400; }

.nw-action-bar { display: flex; align-items: stretch; }
.nw-action-bar .nw-action-cell {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .15rem; padding: .55rem .25rem;
  background: none; border: 0; border-right: 1px solid rgba(0,0,0,.08);
  color: #333; text-decoration: none;
  font-size: .78rem; line-height: 1.1;
}
.nw-action-bar .nw-action-cell:last-child { border-right: 0; }
.nw-action-bar .nw-action-cell i { font-size: 1.35rem; line-height: 1; }
.nw-action-bar .nw-action-cell span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nw-action-bar .nw-action-cell:hover,
.nw-action-bar .nw-action-cell:active { background: rgba(0,0,0,.03); text-decoration: none; }
/* Variante sombre pour la barre d'actions du back office. */
.nw-action-bar-dark { background: #1f1d2b; }
.nw-action-bar-dark .nw-action-cell {
  color: rgba(255,255,255,.85);
  border-right-color: rgba(255,255,255,.12);
}
.nw-action-bar-dark .nw-action-cell:hover,
.nw-action-bar-dark .nw-action-cell:active { color: #fff; background: rgba(255,255,255,.06); }

/* === Uniformisation des tableaux du back-office (v2.9.8) ===
   Avant : densité hétérogène (certaines vues en `table-sm`, d'autres non)
   et en-têtes volumineux, gênants sur mobile. On harmonise ici toutes les
   listes admin d'un coup, sans toucher chaque vue : en-têtes compacts en
   petites capitales discrètes, corps en densité réduite. Scopé `body.admin`
   pour ne pas impacter le front-office. */
body.admin .table {
  font-size: .875rem;
}
body.admin .table > thead th {
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .03em;
  font-weight: 600;
  color: #6c757d;
  white-space: nowrap;
  padding-top: .55rem;
  padding-bottom: .55rem;
}
body.admin .table > tbody > tr > td {
  padding-top: .5rem;
  padding-bottom: .5rem;
  vertical-align: middle;
}
/* Mobile : on resserre encore d'un cran pour limiter le débordement. */
@media (max-width: 575.98px) {
  body.admin .table { font-size: .8rem; }
  body.admin .table > thead th { font-size: .66rem; letter-spacing: .02em; }
  body.admin .table > tbody > tr > td { padding-top: .4rem; padding-bottom: .4rem; }
}

/* === Même traitement côté front-office (v2.9.10) ===
   Reprend l'allègement des tableaux pour l'espace client (dashboard
   commissions, Mes commandes, détail commande). DEUX différences clés
   par rapport à la version admin :
     1. Scopé `body:not(.admin)` (comme le CSS de thème), pour ne viser
        que le front.
     2. Unités en `em` (et PAS `rem`) afin de RESPECTER la taille de
        police configurable du thème (`themeFontSizePx`). En rem on
        écraserait le réglage Apparence ; en em les tableaux suivent
        proportionnellement la police choisie. */
body:not(.admin) .table {
  font-size: .9em;
}
body:not(.admin) .table > thead th {
  font-size: .8em;
  text-transform: uppercase;
  letter-spacing: .03em;
  font-weight: 600;
  /* Pas de couleur fixe : on atténue la couleur de texte héritée du thème
     (themeContentText) pour que les en-têtes s'adaptent à n'importe quel
     fond/couleur personnalisé(e) dans Apparence, sans réglage dédié. */
  opacity: .62;
  white-space: nowrap;
  padding-top: .55rem;
  padding-bottom: .55rem;
}
body:not(.admin) .table > tbody > tr > td {
  padding-top: .5rem;
  padding-bottom: .5rem;
  vertical-align: middle;
}
@media (max-width: 575.98px) {
  body:not(.admin) .table { font-size: .82em; }
  body:not(.admin) .table > thead th { font-size: .72em; letter-spacing: .02em; }
  body:not(.admin) .table > tbody > tr > td { padding-top: .4rem; padding-bottom: .4rem; }
}

/* === Formulaires « compacts » côté front-office (v2.9.14) ===
   Le rendu Bootstrap par défaut des formulaires front (taille héritée de
   `themeFontSizePx`) paraît massif dans les écrans dédiés à la saisie de
   commission. Cette classe `nw-form-compact` resserre labels / inputs /
   textes d'aide d'un cran (~85 %), tout en restant relative au thème via
   des unités `em`. Ne s'applique pas au back-office (réservé front). À
   poser sur le `<form>` (ou un wrapper) pour rendre tout le bloc compact
   sans toucher chaque champ. */
body:not(.admin) .nw-form-compact .form-label {
  font-size: .85em;
  margin-bottom: .25rem;
}
body:not(.admin) .nw-form-compact .form-control,
body:not(.admin) .nw-form-compact .form-select {
  font-size: .9em;
  padding: .35rem .65rem;
  min-height: auto;
}
body:not(.admin) .nw-form-compact textarea.form-control {
  padding-top: .5rem;
}
body:not(.admin) .nw-form-compact .form-text {
  font-size: .78em;
}
body:not(.admin) .nw-form-compact .form-check-label {
  font-size: .9em;
}
body:not(.admin) .nw-form-compact h2,
body:not(.admin) .nw-form-compact h3 {
  font-size: 1.1em;
}
/* Resserre aussi le padding interne des `card-body` qui hébergent le
   formulaire — sans ça on récupère la place gagnée en interne mais le
   bloc reste imposant à l'écran. */
body:not(.admin) .nw-form-compact.card .card-body,
body:not(.admin) .nw-form-compact .card .card-body {
  padding: 1rem 1.1rem;
}

/* ===== Réorganisation des cartes (fiche commission, v2.13.0) =====
   Bouton « Réorganiser » discret par colonne ; en mode édition, chaque carte
   reçoit un contour primaire + un curseur de préhension. Le tri est
   strictement vertical (intra-colonne) — voir public/js/commission-layout.js. */
.layout-bar { line-height: 1; }
.layout-toggle { font-size: .8rem; }
.layout-toggle.active { color: var(--bs-primary) !important; font-weight: 600; }
.layout-col.layout-editing {
  outline: 1px dashed var(--bs-border-color);
  outline-offset: 6px;
  border-radius: .5rem;
}
.layout-col.layout-editing .layout-card { cursor: grab; border-radius: .375rem; box-shadow: 0 0 0 2px var(--bs-primary); }
.layout-col.layout-editing .layout-card:active { cursor: grabbing; }
.layout-card-ghost { opacity: .45; }
.layout-card-chosen { box-shadow: 0 0 0 2px var(--bs-primary); }

/* v2.13.1 — slots live qui peuvent être vides (ex. pas de commande encore) :
   on les masque totalement quand ils n'ont aucun contenu, sinon ils
   créeraient un trou réordonnable invisible. Quand le live injecte du HTML
   dans le slot, il redevient automatiquement visible. */
.layout-card-live:empty { display: none; }
/* v2.13.2 — un fragment de slot « vide » contient en réalité des blancs (les
   retours-ligne entre les `<% if %>` du partial) → `:empty` ne matche pas.
   `:not(:has(*))` cible l'absence d'enfant ÉLÉMENT (blancs ignorés) et masque
   donc le slot sans contenu réel (ex. carte d'action client épinglée pendant
   les phases accepted/in_progress). Règle séparée : si `:has` n'est pas
   supporté, la règle `:empty` ci-dessus reste valide. */
.layout-card-live:not(:has(*)) { display: none; }

/* v2.13.2 — cartes repliables (Discussion, Fichiers partagés, Historique).
   L'en-tête sert de bascule ; la zone .card-collapse est masquée quand la
   carte porte .is-collapsed. Le chevron pivote, et un badge « +N » signale du
   contenu arrivé pendant que la carte était repliée. */
.collapsible-card .collapse-toggle { cursor: pointer; user-select: none; }
.collapsible-card .collapse-chevron { transition: transform .15s ease; }
.collapsible-card.is-collapsed .collapse-chevron { transform: rotate(-90deg); }
.collapsible-card.is-collapsed > .card-collapse { display: none; }
.collapsible-card.is-collapsed { /* en-tête seul : on enlève le bord arrondi bas superflu */ }
.collapse-activity { font-weight: 600; }

/* v2.13.16 — Séparateur « Nouveaux messages » dans la discussion (pattern
   Slack/Discord). Trait fin + libellé centré, visible jusqu'au prochain
   refresh — au reload, markLastSeen a posé une nouvelle dernière visite,
   donc le séparateur disparaît naturellement. */
.chat-card .chat-unread-divider {
  display: flex; align-items: center;
  gap: .6rem; margin: .2rem 0 .1rem;
  color: var(--bs-danger); font-size: .75em; font-weight: 600;
  text-transform: uppercase; letter-spacing: .04em;
}
.chat-card .chat-unread-divider::before,
.chat-card .chat-unread-divider::after {
  content: ''; flex: 1 1 auto; height: 1px; background: currentColor; opacity: .55;
}
.chat-card .chat-unread-label { white-space: nowrap; }

/* v2.18.31 — Séparateur de JOUR (Aujourd'hui / Hier / date) : ligne centrée
   neutre, distincte du séparateur « nouveaux » (rouge). */
.chat-card .chat-day-sep {
  display: flex; align-items: center; gap: .6rem;
  margin: .8rem 0 .35rem;
  color: var(--bs-secondary-color, #6c757d); font-size: .72em; font-weight: 600;
  text-transform: uppercase; letter-spacing: .04em;
}
.chat-card .chat-day-sep::before,
.chat-card .chat-day-sep::after {
  content: ''; flex: 1 1 auto; height: 1px; background: currentColor; opacity: .25;
}
.chat-card .chat-day-sep > span { white-space: nowrap; }

/* v2.18.31/32/36 — Regroupement : un message « de suite » (même auteur, < 5 min)
   se colle au précédent. On masque nom/badge/indice ⓘ. v2.18.36 — l'accusé ✓✓
   (+ corbeille au survol) est posé À DROITE du corps, en ligne (flex), aligné en
   bas : nickel pour les messages courts (« 999 ✓✓ ») comme longs (✓✓ en bas à
   droite), sans ligne vide ni espace réservé disproportionné. */
.chat-card .msg.msg-cont {
  margin-top: 2px;
  display: flex;
  align-items: flex-end;
  gap: .35rem;
}
.chat-card .msg.msg-cont > * { min-width: 0; } /* le corps peut rétrécir / passer à la ligne */
.chat-card .msg.msg-cont .msg-head {
  order: 2;          /* après le corps → à droite */
  flex: 0 0 auto;
  margin: 0;
  display: inline-flex; align-items: center; gap: .15rem; line-height: 1;
}
.chat-card .msg.msg-cont .msg-head > strong,
.chat-card .msg.msg-cont .msg-head .badge-admin,
.chat-card .msg.msg-cont .msg-head .msg-time-hint { display: none; }

/* v2.18.31 — « est en train d'écrire » : bulle discrète sous le fil + 3 points. */
.chat-card .chat-typing {
  display: flex; align-items: center; gap: .5rem;
  padding: .3rem .2rem .1rem; font-size: .8em;
  color: var(--bs-secondary-color, #6c757d);
}
.chat-card .chat-typing[hidden] { display: none; }
.chat-card .chat-typing-dots { display: inline-flex; gap: 3px; }
.chat-card .chat-typing-dots > span {
  width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; opacity: .5;
  animation: nw-typing 1.2s infinite ease-in-out;
}
.chat-card .chat-typing-dots > span:nth-child(2) { animation-delay: .2s; }
.chat-card .chat-typing-dots > span:nth-child(3) { animation-delay: .4s; }
@keyframes nw-typing {
  0%, 60%, 100% { transform: translateY(0); opacity: .35; }
  30% { transform: translateY(-3px); opacity: .9; }
}

/* v2.18.31 — Pastille « ↓ nouveaux messages » (apparait si on a scrollé vers le
   haut au moment où un message arrive). */
.chat-card .chat-scroll-down {
  display: block; margin: .25rem auto .1rem;
  border: 0; border-radius: 999px;
  background: var(--nw-accent, #28a790); color: #fff;
  font-size: .8em; font-weight: 600;
  padding: .25rem .8rem; cursor: pointer;
  box-shadow: 0 2px 6px rgba(0, 0, 0, .15);
}
.chat-card .chat-scroll-down[hidden] { display: none; }
.chat-card .chat-scroll-down:hover { background: var(--nw-accent-dark, #137a68); }

/* v2.13.18 — Indice ⓘ discret sur les messages reçus : quasi invisible au
   repos, révélé au survol de la bulle, pour signaler que l'heure d'envoi est
   consultable (title). Sans réintroduire de date inline permanente. */
.chat-card .msg-time-hint {
  color: var(--bs-secondary-color, #6c757d);
  opacity: 0;
  transition: opacity .12s ease;
  cursor: help;
  font-size: .85em;
}
.chat-card .msg:hover .msg-time-hint,
.chat-card .msg-time-hint:focus { opacity: .6; }
.chat-card .msg-time-hint:hover { opacity: 1; }

/* v2.13.21 — Soft-delete dans la discussion : trace « message/fichier
   supprimé » + bouton corbeille discret (révélé au survol du message). */
.chat-card .msg-deleted .msg-deleted-body { color: var(--bs-secondary-color, #6c757d); font-size: .9em; }
.chat-card .msg-deleted { opacity: .85; }
.chat-card .msg-att-deleted { cursor: default; opacity: .7; }
.chat-card .message-delete-form { display: inline; }
.chat-card .message-delete-btn {
  border: 0; background: transparent; padding: 0 .15rem; line-height: 1;
  color: var(--bs-secondary-color, #6c757d); cursor: pointer;
  opacity: 0; transition: opacity .12s ease;
}
.chat-card .msg:hover .message-delete-btn { opacity: .55; }
.chat-card .message-delete-btn:hover { opacity: 1; color: var(--bs-danger); }

/* ===== Consentement aux cookies (RGPD / CNIL) ===== */
/* Bandeau bas de page : accepter / continuer sans accepter. Affiché par
   public/js/cookie-consent.js tant qu'aucun choix valide n'a été posé. */
.nw-cookie-banner {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 1080;
  background: #1f1d2b; color: #f5f3ef;
  box-shadow: 0 -2px 14px rgba(0, 0, 0, .25);
}
.nw-cookie-banner[hidden] { display: none; }
.nw-cookie-banner-inner {
  max-width: 1100px; margin: 0 auto; padding: .85rem 1rem;
  display: flex; gap: 1rem; align-items: center;
  flex-wrap: wrap; justify-content: space-between;
}
.nw-cookie-banner-txt { flex: 1 1 320px; min-width: 0; }
.nw-cookie-banner-txt p { color: #d8d4cd; }
.nw-cookie-link { color: #fff; text-decoration: underline; }
.nw-cookie-banner-actions { display: flex; gap: .5rem; flex-shrink: 0; }
@media (max-width: 575.98px) {
  .nw-cookie-banner-actions { width: 100%; }
  .nw-cookie-banner-actions .btn { flex: 1 1 0; }
}

/* Contenus tiers différés (iframes YouTube/Vimeo/Spotify…) : tant que non
   consentis, on affiche une façade par-dessus l'iframe (sans src distant). */
.nw-consent-embed {
  position: relative; display: block;
  width: 100%; height: 100%; min-height: 180px;
}
.nw-consent-embed > iframe { width: 100%; height: 100%; border: 0; }
.nw-consent-embed .nw-consent-ph {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: .5rem;
  text-align: center; padding: 1rem;
  background: #f1efec; color: #5a5249;
  border: 1px dashed #cbbfb3; border-radius: 6px;
}
.nw-consent-embed .nw-consent-ph i { font-size: 1.5rem; }
.nw-consent-embed .nw-consent-ph-txt { font-size: .85rem; max-width: 32rem; }
.nw-consent-embed.is-loaded .nw-consent-ph { display: none; }

/* Bouton rendu comme un lien inline : hérite de la taille de police du
   conteneur (ex. le « Gérer les cookies » du pied de page, aligné sur les
   liens voisins) au lieu d'imposer la taille des boutons Bootstrap. */
.nw-linkbtn {
  background: none; border: 0; padding: 0;
  font: inherit; color: inherit; vertical-align: baseline; cursor: pointer;
}

/* ===== Réorganisation par glisser-déposer (tableaux BO), activable au bouton ===== */
/* Poignée masquée tant que le mode « Réorganiser » n'est pas activé. */
.reorder-handle { display: none; cursor: grab; color: var(--bs-secondary-color, #6c757d); }
.reorder-editing .reorder-handle { display: inline-block; }
.reorder-editing tr { cursor: grab; }
.reorder-editing tr:active { cursor: grabbing; }
/* Ligne en cours de déplacement (ghost SortableJS par défaut). */
.reorder-editing tr.sortable-ghost { opacity: .5; background: var(--bs-light, #f8f9fa); }
/* Bouton « Réorganiser » en état actif (mode édition en cours). */
[data-reorder-toggle].active { font-weight: 600; }

/* ===== Lightbox image (modal générique, mode data-modal-image) ===== */
.nw-modal-image .modal-body {
  background: #f7f7f7;
  text-align: center;
  padding: .75rem;
}
.nw-modal-image .modal-body img {
  max-height: 78vh;
  width: auto;
  max-width: 100%;
  border-radius: .25rem;
}

/* v2.15.47 — Progress bar « Suivi estimé » d'une commande (Livrable →
   Expédition → Livraison), avec dates estimées/réelles et état d'avancement. */
.order-timeline { list-style: none; display: flex; padding: 0; margin: 0; }
.order-timeline .ot-step {
  flex: 1 1 0; text-align: center; position: relative; padding-top: 1.7rem; min-width: 0;
}
.order-timeline .ot-step::before { /* connecteur vers le jalon précédent */
  content: ''; position: absolute; top: .5rem; right: 50%; width: 100%; height: 3px;
  background: #ded9d0; z-index: 0;
}
.order-timeline .ot-step:first-child::before { display: none; }
.order-timeline .ot-dot {
  position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: 1.15rem; height: 1.15rem; border-radius: 50%; background: #ded9d0; z-index: 1;
  display: flex; align-items: center; justify-content: center; color: #fff; font-size: .72rem;
}
.order-timeline .ot-label { display: block; font-size: .8rem; font-weight: 600; line-height: 1.1; }
.order-timeline .ot-date { display: block; font-size: .75rem; color: #6c757d; }
/* Étape franchie (réelle) : vert, connecteur vert. */
.order-timeline .ot-done .ot-dot { background: #198754; }
.order-timeline .ot-done::before { background: #198754; }
/* Étape en cours : accent + halo. */
.order-timeline .ot-current .ot-dot {
  background: var(--nw-accent, #6f42c1);
  box-shadow: 0 0 0 .22rem rgba(111, 66, 193, .18);
}
.order-timeline .ot-current .ot-label { color: var(--nw-accent, #6f42c1); }

/* ===========================================================================
   Tableaux à édition inline → CARTES EMPILÉES sur mobile (v2.16.17).
   Classe `.table-stack-sm` : sous 576px, chaque ligne devient une carte et
   chaque cellule une rangée libellée (via data-label). Évite la colonne « nom »
   écrasée des pages International (zones, transporteurs…). Un seul jeu de champs
   → compatible avec l'édition inline `form="…"` (pas de doublon de <form>).
   ======================================================================== */
@media (max-width: 575.98px) {
  .table-stack-sm thead { display: none; }
  .table-stack-sm, .table-stack-sm tbody, .table-stack-sm tr, .table-stack-sm td { display: block; width: 100%; }
  .table-stack-sm tr { border: 1px solid #dee2e6; border-radius: .6rem; margin-bottom: .65rem; padding: .35rem .6rem; background: #fff; }
  .table-stack-sm tr:last-child { margin-bottom: 0; }
  .table-stack-sm td { border: 0 !important; padding: .3rem 0; display: flex; align-items: center; gap: .6rem; }
  .table-stack-sm td[data-label]::before {
    content: attr(data-label); flex: 0 0 84px; min-width: 84px;
    font-size: .68rem; letter-spacing: .03em; text-transform: uppercase; color: #6c757d;
  }
  .table-stack-sm td[data-label] > * { flex: 1 1 auto; min-width: 0; }
  /* Cellule sans libellé (actions) : boutons alignés à droite, pleine largeur. */
  .table-stack-sm td:not([data-label]) { justify-content: flex-end; padding-top: .5rem; }
  .table-stack-sm td input[type="number"] { width: 100% !important; max-width: 120px; }
}

/* ===========================================================================
   Polissage UI de l'espace client — « Soft UI Evolution »
   ---------------------------------------------------------------------------
   Couche de modernisation DOUCE, strictement scopée au front-office
   (`body:not(.admin)`) pour ne pas régresser le back-office. Principes (réf.
   guide UI/UX, style « Soft UI Evolution ») : profondeur par ombres douces
   multi-couches plutôt que bordures sèches, arrondis cohérents, transitions
   200 ms, focus visibles (WCAG AA+). AUCUNE couleur figée : on réutilise
   `--nw-accent` et la palette de thème (l'admin garde la main via Apparence).
   Unités relatives (`em`) là où la taille de police de thème doit primer.
   ======================================================================== */
body:not(.admin) {
  --nw-radius: 12px;
  --nw-radius-sm: 9px;
  --nw-shadow-sm: 0 1px 2px rgba(31, 29, 43, .04), 0 2px 5px rgba(31, 29, 43, .05);
  --nw-shadow: 0 2px 8px rgba(31, 29, 43, .05), 0 10px 24px rgba(31, 29, 43, .07);
  --nw-shadow-lg: 0 12px 34px rgba(31, 29, 43, .12);
  /* Teinte douce dérivée de l'accent jade/teal (sert aussi de repli aux liens
     d'action / au pulse de la timeline, qui étaient en violet codé en dur). */
  --nw-accent-soft: rgba(var(--nw-accent-rgb, 40, 167, 144),.10);
}

/* Accent complémentaire CHAUD (corail) — à utiliser avec parcimonie pour une
   action secondaire mise en avant ou une étiquette « artistique », sans voler
   la vedette au CTA principal teal. */
body:not(.admin) .btn-warm {
  --bs-btn-color: #fff;
  --bs-btn-bg: var(--nw-warm);
  --bs-btn-border-color: var(--nw-warm);
  --bs-btn-hover-color: #fff;
  --bs-btn-hover-bg: var(--nw-warm-dark, #c96348);
  --bs-btn-hover-border-color: var(--nw-warm-dark, #c96348);
  --bs-btn-active-color: #fff;
  --bs-btn-active-bg: var(--nw-warm-dark, #c96348);
  --bs-btn-active-border-color: var(--nw-warm-dark, #c96348);
}
body:not(.admin) .nw-tag-warm {
  display: inline-block;
  background: var(--nw-warm-soft);
  color: #b14e36;
  border: 1px solid rgba(var(--nw-warm-rgb, 224, 122, 95),.35);
  border-radius: 999px;
  padding: .15rem .6rem;
  font-size: .74em;
  font-weight: 600;
}
/* Étiquette « info » à la couleur de marque (pendant neutre de .nw-tag-warm) —
   ex. marqueur « envoi physique ». */
body:not(.admin) .nw-tag {
  display: inline-block;
  background: var(--nw-accent-soft);
  color: var(--nw-accent-dark);
  border: 1px solid rgba(var(--nw-accent-rgb, 40, 167, 144),.35);
  border-radius: 999px;
  padding: .15rem .55rem;
  font-size: .74em;
  font-weight: 600;
}

/* Cartes : profondeur douce + arrondi cohérent, via les variables Bootstrap
   (les coins internes — card-header/img — restent alignés). */
body:not(.admin) .card {
  --bs-card-border-color: rgba(31, 29, 43, .07);
  --bs-card-border-radius: var(--nw-radius);
  --bs-card-inner-border-radius: calc(var(--nw-radius) - 1px);
  /* NB : Bootstrap 5.3 définit `--bs-card-box-shadow` mais ne l'applique PAS
     sur `.card` (la propriété box-shadow est absente de la règle de base) →
     on pose donc l'ombre EN DIRECT, sinon aucune profondeur ne s'afficherait. */
  box-shadow: var(--nw-shadow-sm);
  transition: box-shadow .2s ease, transform .2s ease;
}

/* Cartes de SECTION encadrée — remplacent les bordures Bootstrap success/warning
   (vert/jaune, qui juraient avec le teal) par le langage de marque : filet à
   gauche + en-tête teinté doux. « accent » = positif/validation (teal),
   « attention » = action requise / modifications (corail, le ton « touche
   artistique » du thème). On surcharge `--bs-card-border-color` (teinte douce
   sur tout le pourtour) puis `border-left` repeint le filet à plein. */
body:not(.admin) .nw-card-accent {
  --bs-card-border-color: rgba(var(--nw-accent-rgb, 40, 167, 144),.22);
  border-left: 3px solid var(--nw-accent);
}
body:not(.admin) .nw-card-accent > .card-header {
  background: var(--nw-accent-soft);
  border-bottom-color: rgba(var(--nw-accent-rgb, 40, 167, 144),.18);
}
body:not(.admin) .nw-card-attention {
  --bs-card-border-color: rgba(var(--nw-warm-rgb, 224, 122, 95),.30);
  border-left: 3px solid var(--nw-warm);
}
body:not(.admin) .nw-card-attention > .card-header {
  background: var(--nw-warm-soft);
  border-bottom-color: rgba(var(--nw-warm-rgb, 224, 122, 95),.22);
}

/* Carte interactive (lien/CTA) : léger soulèvement au survol, SANS décaler la
   mise en page (transform + ombre seulement). À poser explicitement sur les
   cartes cliquables — les cartes informatives ne bougent pas. */
body:not(.admin) .nw-lift:hover,
body:not(.admin) .nw-lift:focus-within {
  box-shadow: var(--nw-shadow);
  transform: translateY(-3px);
}
/* Curseur « pointer » UNIQUEMENT si toute la carte est cliquable (lien),
   pas sur une carte informative dotée d'un simple bouton interne. */
body:not(.admin) a.nw-lift,
body:not(.admin) .nw-lift[role="button"] { cursor: pointer; }

/* Boutons : arrondi un cran plus doux + transitions homogènes. */
body:not(.admin) .btn {
  --bs-btn-border-radius: 9px;
  transition: background-color .15s ease, border-color .15s ease,
              color .15s ease, box-shadow .15s ease;
}
body:not(.admin) .btn-sm,
body:not(.admin) .btn-group-sm > .btn { --bs-btn-border-radius: 7px; }
body:not(.admin) .btn-lg { --bs-btn-border-radius: 11px; }

/* Focus visibles à l'accent du thème (au lieu du bleu Bootstrap par défaut) —
   cohérence de marque + repère clavier net (accessibilité). */
body:not(.admin) .btn:focus-visible {
  box-shadow: 0 0 0 .22rem var(--nw-accent-soft);
}
body:not(.admin) .form-control:focus,
body:not(.admin) .form-select:focus,
body:not(.admin) .form-check-input:focus {
  border-color: var(--nw-accent);
  box-shadow: 0 0 0 .22rem var(--nw-accent-soft);
}
body:not(.admin) .form-check-input:checked {
  background-color: var(--nw-accent);
  border-color: var(--nw-accent);
}

/* Pendant back-office : repère clavier net à l'accent admin (violet). Le bloc
   de focus ci-dessus est scopé au front ; sans cela le BO retombait sur
   l'outline Bootstrap par défaut (peu visible). */
body.admin .btn:focus-visible {
  box-shadow: 0 0 0 .22rem rgba(108, 92, 231, .25);
}
body.admin .form-control:focus,
body.admin .form-select:focus,
body.admin .form-check-input:focus {
  border-color: var(--nw-admin);
  box-shadow: 0 0 0 .22rem rgba(108, 92, 231, .25);
}

/* === Formulaires BO (v2.18.49) — cohérence avec l'identité violette +
   lisibilité « data-dense ». Scopé body.admin : le front garde son teal.
   CSS-only → harmonise tous les formulaires sans toucher les vues. === */
/* Cases & interrupteurs en VIOLET (au lieu du bleu Bootstrap par défaut). */
body.admin .form-check-input:checked {
  background-color: var(--nw-admin);
  border-color: var(--nw-admin);
}
/* Champs de saisie : bordure + arrondi alignés sur les cartes du BO. */
body.admin .form-control,
body.admin .form-select {
  border-color: var(--nw-border);
  border-radius: var(--nw-radius-sm);
}
/* Libellés : encre ferme, un cran plus lisibles. */
body.admin .form-label {
  font-weight: 600;
  font-size: .85rem;
  color: var(--nw-ink);
  margin-bottom: .3rem;
}
/* Texte d'aide : gris homogène. */
body.admin .form-text { color: var(--nw-ink-muted); font-size: .8rem; }
/* Titres de section des cartes de formulaire (h6 capitales) : interlettrage
   pour la lisibilité des capitales (enfant direct du card-body = titre). */
body.admin .card-body > .text-uppercase { letter-spacing: .04em; }

/* Barre d'enregistrement (v2.18.50) : toolbar blanche COLLANTE en bas des
   longs formulaires → le bouton « Enregistrer » reste toujours atteignable.
   À l'arrêt naturel (fin du form), c'est une simple carte d'actions. */
body.admin .admin-form-bar {
  position: sticky;
  bottom: .75rem;
  z-index: 4;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: .5rem;
  margin-top: 1.25rem;
  padding: .7rem 1rem;
  background: var(--nw-surface);
  border: 1px solid var(--nw-border);
  border-radius: var(--nw-radius);
  box-shadow: 0 -2px 12px rgba(31, 36, 48, .07);
}
@media (max-width: 575.98px) {
  body.admin .admin-form-bar { bottom: 0; border-radius: var(--nw-radius) var(--nw-radius) 0 0; }
  body.admin .admin-form-bar .btn { flex: 1 1 auto; }
}

/* === Matrice des frais de port (fiche transporteur, v2.18.67) =============
   Beaucoup de zones (lignes) × quelques tranches de poids (colonnes). Cadre
   défilant avec en-têtes de tranches ET colonne « Zone » GELÉS (sticky) pour
   rester repérables en scrollant 18+ zones ; un filtre (data-filter-input)
   masque les lignes hors recherche. La colonne gelée a un fond opaque pour que
   les cellules de tarif ne transparaissent pas dessous au défilement latéral. */
body.admin .nw-rate-scroll {
  max-height: 65vh;
  overflow: auto;
  border: 1px solid var(--nw-border);
  border-radius: var(--nw-radius-sm);
}
body.admin .nw-rate-matrix { margin-bottom: 0; }
body.admin .nw-rate-matrix thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--nw-surface-2);
}
body.admin .nw-rate-matrix .nw-rate-zone-col {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--nw-surface);
}
body.admin .nw-rate-matrix thead th.nw-rate-zone-col { z-index: 3; }
/* Cellule non éditable : curseur « interdit » + fond grisé. Deux cas —
   `disabled` (zone décochée → non soumise) et `readonly` (tranche sans max, ou
   min auto-calculé : verrouillé mais PRÉSERVÉ à l'enregistrement). */
body.admin .nw-rate-matrix input:disabled,
body.admin .nw-rate-matrix input[readonly] { cursor: not-allowed; background-color: var(--nw-surface-2); }
body.admin .nw-rate-matrix .input-group:has(input:disabled) .input-group-text,
body.admin .nw-rate-matrix .input-group:has(input[readonly]) .input-group-text { cursor: not-allowed; }

/* === Médiathèque (v2.18.70) ===============================================
   Grille de vignettes carrées « remplies » (object-fit cover), survol violet,
   case de sélection en overlay, zone glisser-déposer et barre d'action de
   masse. Identité BO violette (tokens --nw-*). */
body.admin .nw-media-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
  gap: .75rem;
}
body.admin .nw-media-tile {
  position: relative;
  border: 1px solid var(--nw-border);
  border-radius: var(--nw-radius-sm);
  background: var(--nw-surface);
  overflow: hidden;
  transition: border-color .15s, box-shadow .15s;
}
body.admin .nw-media-tile:hover { border-color: var(--nw-admin); box-shadow: var(--nw-shadow-sm); }
body.admin .nw-media-tile.is-selected { border-color: var(--nw-admin); box-shadow: 0 0 0 2px var(--nw-admin-soft); }
body.admin .nw-media-tile.is-picker { cursor: pointer; }
body.admin .nw-media-thumb {
  display: block;
  aspect-ratio: 1 / 1;
  background: var(--nw-surface-2);
  overflow: hidden;
  cursor: pointer;
}
body.admin .nw-media-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .2s; }
body.admin .nw-media-tile:hover .nw-media-thumb img { transform: scale(1.04); }
body.admin .nw-media-thumb-file {
  display: flex; align-items: center; justify-content: center;
  color: var(--nw-ink-muted); font-size: 2.4rem; text-decoration: none;
}
body.admin .nw-media-check {
  position: absolute; top: .35rem; left: .35rem; z-index: 2; margin: 0;
  background: rgba(255, 255, 255, .88); border-radius: 5px; padding: .12rem .28rem; line-height: 0;
}
body.admin .nw-media-meta { padding: .4rem .5rem .45rem; }
body.admin .nw-media-name { font-size: .78rem; color: var(--nw-ink); }
body.admin .nw-media-sub { font-size: .68rem; color: var(--nw-ink-muted); }
body.admin .nw-media-actions { display: flex; gap: .25rem; align-items: center; }
body.admin .nw-media-actions .btn { padding: .12rem .4rem; }
body.admin .nw-media-actions form { display: contents; } /* le <form> n'interrompt pas le flex */

/* Zone glisser-déposer (le vrai <input file> est masqué mais focusable). */
body.admin .nw-dropzone {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .2rem; width: 100%; margin: 0; padding: 1.4rem 1rem; text-align: center; cursor: pointer;
  border: 2px dashed var(--nw-border); border-radius: var(--nw-radius-sm);
  background: var(--nw-surface-2); color: var(--nw-ink-muted);
  transition: border-color .15s, background .15s;
}
body.admin .nw-dropzone:hover { border-color: var(--nw-admin); }
body.admin .nw-dropzone.is-drag { border-color: var(--nw-admin); background: var(--nw-admin-soft); }
body.admin .nw-dropzone > i { font-size: 1.6rem; color: var(--nw-admin); }
body.admin .nw-dropzone-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }

/* Barre d'action de masse — masquée par défaut, affichée si ≥1 sélection. */
body.admin .nw-media-bar { display: none; }
body.admin .nw-media-bar.is-active {
  display: flex; align-items: center; flex-wrap: wrap; gap: .6rem;
  position: sticky; top: .5rem; z-index: 5; margin-bottom: .75rem; padding: .5rem .75rem;
  background: var(--nw-surface); border: 1px solid var(--nw-admin);
  border-radius: var(--nw-radius-sm); box-shadow: var(--nw-shadow-sm);
}

/* v2.20.7 — list-group actif (ex. menu « Dossiers » de la médiathèque) en violet
   de marque. Bootstrap définit `--bs-list-group-active-bg` LOCALEMENT sur
   `.list-group` (#0d6efd) : il faut donc surcharger AU MÊME niveau (sur
   `.list-group`), pas sur `body.admin` (sinon la valeur locale, plus proche dans
   l'arbre, l'emporte et l'actif reste BLEU). */
body.admin .list-group {
  --bs-list-group-active-bg: var(--nw-admin);
  --bs-list-group-active-border-color: var(--nw-admin);
}

/* Pagination (v2.18.51) — couleur de marque violette (au lieu du bleu
   Bootstrap), homogène sur Factures/Avoirs, Logs, Mails, Consentements. */
body.admin .page-link { color: var(--nw-admin); }
body.admin .page-link:hover { background: var(--nw-admin-soft); color: var(--nw-admin); }
body.admin .page-link:focus { box-shadow: 0 0 0 .22rem rgba(108, 92, 231, .25); }
body.admin .page-item.active .page-link {
  background: var(--nw-admin);
  border-color: var(--nw-admin);
  color: #fff;
}

/* Alertes « info » du BO en violet doux (au lieu du cyan vif Bootstrap),
   cohérent avec l'identité. A11y : texte violet foncé sur fond violet très
   clair. NB : on ne remappe PAS --bs-info (les badges bg-info mélangent
   text-dark / text-white → un remap global casserait la lisibilité). */
body.admin .alert-info {
  --bs-alert-color: #4536a8;
  --bs-alert-bg: #efeafe;
  --bs-alert-border-color: #d8d0f7;
}

/* Champs de saisie : arrondi aligné sur le reste. */
body:not(.admin) .form-control,
body:not(.admin) .form-select { border-radius: var(--nw-radius-sm); }

/* Nav « Mon compte » + list-groups : arrondi homogène. */
body:not(.admin) .list-group { border-radius: var(--nw-radius); }

/* État vide soigné : carte en pointillés douce (dashboard sans commission,
   « Mes commandes » vide…). `.border-dashed` n'existe pas dans Bootstrap. */
body:not(.admin) .border-dashed {
  border: 1.5px dashed rgba(31, 29, 43, .16) !important;
  box-shadow: none;
  background: rgba(255, 255, 255, .55);
}

/* Titres de section dans les cartes (ex. « Ma demande », « Discussion ») :
   petites capitales discrètes mais lisibles, avec un filet d'accent à gauche
   pour structurer la fiche commission sans alourdir. Ciblé sur les <h3>
   en `.text-uppercase` déjà présents dans les vues (aucune modif de balise). */
body:not(.admin) .card-body > h3.text-uppercase:first-child {
  letter-spacing: .04em;
  font-size: .74em;
  font-weight: 700;
  padding-left: .6rem;
  border-left: 3px solid var(--nw-accent);
  margin-bottom: .85rem;
}

/* Liens d'action de la timeline / chat : la teinte douce suit maintenant
   l'accent terracotta (cf. --nw-accent-soft ci-dessus). */
body:not(.admin) .hero h1 { letter-spacing: -.01em; }

/* Icône d'état vide : pastille douce teintée accent, façon illustration
   légère (dashboard sans commission, listes vides…). */
body:not(.admin) .nw-empty-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 4.5rem;
  height: 4.5rem;
  border-radius: 50%;
  background: var(--nw-accent-soft);
  color: var(--nw-accent);
  font-size: 2rem;
  line-height: 1;
}

/* Carte « accentuée » (récap/validation de devis, choix de livraison…) :
   remplace les bg-info/border-info hérités par une teinte à la couleur de
   marque, pour une cohérence teal sur les pages client clés. */
body:not(.admin) .nw-card-accent { border-color: rgba(var(--nw-accent-rgb, 40, 167, 144),.30); }
body:not(.admin) .nw-card-accent > .card-header {
  background: var(--nw-accent-soft);
  border-bottom-color: rgba(var(--nw-accent-rgb, 40, 167, 144),.18);
}

/* Harmonise le `.text-primary` Bootstrap (bleu) avec la marque teal sur le
   front (ex. résumés `<summary>`), sans toucher au back-office. */
body:not(.admin) .text-primary { color: var(--nw-accent-dark) !important; }

/* v2.18.29 — Élimine les derniers BLEUS Bootstrap du front. Les overrides
   précédents couvraient `.btn-primary`, `.text-primary` et `a:not(.btn)`, mais
   PAS les boutons « lien » (crayons d'édition `.btn-link`), les boutons outline
   primaires (« Ajouter ») ni les variables de thème. On redéfinit le primaire
   et la couleur de lien Bootstrap côté front (le `.btn-link` lit `--bs-link-color`)
   + l'outline primaire. BO inchangé. */
body:not(.admin) {
  --bs-primary: var(--nw-accent);
  --bs-primary-rgb: 40, 167, 144;
  --bs-link-color: var(--nw-accent-dark);
  --bs-link-color-rgb: 19, 122, 104;
  --bs-link-hover-color: var(--nw-accent);
}
body:not(.admin) .btn-outline-primary {
  --bs-btn-color: var(--nw-accent-dark);
  --bs-btn-border-color: var(--nw-accent-dark);
  --bs-btn-hover-color: #fff;
  --bs-btn-hover-bg: var(--nw-accent-dark);
  --bs-btn-hover-border-color: var(--nw-accent-dark);
  --bs-btn-active-color: #fff;
  --bs-btn-active-bg: var(--nw-accent-dark);
  --bs-btn-active-border-color: var(--nw-accent-dark);
  --bs-btn-focus-shadow-rgb: 40, 167, 144;
}

/* v2.18.29 — `.text-warning` (montants « Restant dû » / « à régler » / « attendu »
   et avertissements) : le jaune Bootstrap #ffc107 est illisible sur fond blanc
   (~1,6:1). On le passe à un amber FONCÉ lisible (~5:1, AA) — distinct du rouge
   « Remboursé » (danger). */
body:not(.admin) .text-warning { color: var(--nw-amount, #b45309) !important; }

/* v2.18.20 — Alertes « info » : le cyan Bootstrap par défaut jure avec le teal
   de marque (couleurs voisines mais distinctes → impression d'incohérence).
   On les teinte à la couleur de marque sur le front (les alertes success /
   warning / danger gardent leur sémantique de couleur). Bootstrap 5.3 pilote
   l'alerte via les variables `--bs-alert-*`. */
body:not(.admin) .alert-info {
  --bs-alert-color: var(--nw-accent-dark);
  --bs-alert-bg: var(--nw-accent-soft);
  --bs-alert-border-color: rgba(var(--nw-accent-rgb, 40, 167, 144),.30);
  --bs-alert-link-color: var(--nw-accent-dark);
}

/* ===========================================================================
   Vitrine publique (accueil, prestations, auth, footer) — identité teal
   ======================================================================== */

/* Héro d'accueil : halo teal doux derrière le titre + coins arrondis. */
body:not(.admin) .hero {
  background: radial-gradient(120% 130% at 50% 0%, var(--nw-accent-soft) 0%, rgba(var(--nw-accent-rgb, 40, 167, 144),0) 62%);
  border-radius: var(--nw-radius);
  padding: 3.5rem 1rem 2.75rem;
}

/* Étapes « Comment ça marche » : pastille numérotée à la couleur de marque. */
body:not(.admin) .nw-step-num {
  width: 2.4rem; height: 2.4rem; flex: 0 0 2.4rem;
  border-radius: 50%;
  background: var(--nw-accent); color: #fff;
  font-weight: 700; font-size: 1.05rem;
  display: inline-flex; align-items: center; justify-content: center;
}

/* Bandeau d'intro de catalogue (prestations) : large carte teal douce. */
body:not(.admin) .nw-intro {
  background: radial-gradient(120% 180% at 0% 0%, var(--nw-accent-soft) 0%, rgba(var(--nw-accent-rgb, 40, 167, 144),0) 60%);
  border: 1px solid rgba(var(--nw-accent-rgb, 40, 167, 144),.18);
  border-radius: var(--nw-radius);
  padding: 1.5rem;
}

/* Cartes prestations / options : en-tête teal + prix mis en avant. */
body:not(.admin) .nw-pres-card .card-header {
  background: var(--nw-accent-soft);
  color: var(--nw-accent-dark);
  border-bottom-color: rgba(var(--nw-accent-rgb, 40, 167, 144),.18);
  font-weight: 700;
}
body:not(.admin) .nw-pres-price { color: var(--nw-accent-dark); }
/* Coches « inclus » à la couleur de marque (page prestations). */
body:not(.admin) .nw-check { color: var(--nw-accent); }

/* Carte d'authentification (login/register) : fin filet teal en tête. */
body:not(.admin) .nw-auth-card { border-top: 3px solid var(--nw-accent); }

/* Mappe la couleur « primary » de Bootstrap sur le teal de marque (front
   uniquement) → harmonise d'un coup .bg-primary, .border-primary, les badges
   bg-primary, .text-bg-primary, etc. (ex. bandeau « suite de commission » et
   pastilles « +N » d'activité). `.btn-primary` passe déjà par le thème, et
   `.text-primary` conserve son override foncé lisible. Le back-office n'est pas
   touché (il garde son bleu/violet). */
body:not(.admin) {
  --bs-primary: #28a790;
  --bs-primary-rgb: 40, 167, 144;
}

/* Footer : fin filet d'accent teal (discret, signe de marque). Le `!important`
   bat la classe utilitaire `.border-top` de Bootstrap (elle-même !important). */
body:not(.admin) footer.border-top {
  border-top-color: rgba(var(--nw-accent-rgb, 40, 167, 144),.45) !important;
  border-top-width: 2px !important;
}

/* v2.18.23 — Le footer est du texte SECONDAIRE (liens utilitaires, mentions
   légales, ©) : le seuil « 16px » de la skill ne vise que le CORPS de texte ;
   le secondaire suit l'échelle 12·14·16 et peut être plus petit. On l'uniformise
   à ~13px (au lieu des ~14-16px hérités), sans cumuler avec `.small` (qui le
   rendrait trop petit). L'espacement des liens reste confortable. Front seul. */
body:not(.admin) footer,
body:not(.admin) footer .small,
body:not(.admin) footer small { font-size: .8125rem; }

/* Confort tactile (réf. règles UX « touch-target-size » / « tap-delay ») —
   front uniquement. `touch-action: manipulation` supprime le délai de tap de
   ~300 ms (sensation de réactivité). Sur mobile, on garantit des cibles d'au
   moins 44×44 px sur les boutons et champs principaux : le `inline-flex`
   recentre le contenu dans la hauteur accrue, et reste compatible largeur avec
   `.w-100`/`.d-grid` (blockifié dans une grille → s'étire normalement). Les
   micro-contrôles d'angle révélés au survol (corbeilles de chat/galerie, lien
   « copier ») gardent leur taille : ce ne sont pas des `.btn`. */
body:not(.admin) .btn,
body:not(.admin) a,
body:not(.admin) .form-control,
body:not(.admin) .form-select,
body:not(.admin) .form-check-input,
body:not(.admin) [role="button"] { touch-action: manipulation; }

@media (max-width: 575.98px) {
  body:not(.admin) .btn {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  body:not(.admin) .form-control,
  body:not(.admin) .form-select { min-height: 44px; }
}

/* v2.18.21 — Harmonisation des formulaires front sur MOBILE, dans le BON sens
   (réf. skill ui-ux-pro-max, règles « Readable Font Size » + « Touch Target »,
   sévérité haute) : on ne RÉTRÉCIT PAS les champs (un champ < 16px déclenche le
   ZOOM auto d'iOS au focus + nuit à la lisibilité). On garantit au contraire
   ≥ 16px sur TOUS les champs en mobile — y compris ceux en `.form-control-sm`
   ou dans un bloc `.nw-form-compact` (qui restent compacts sur DESKTOP). La
   densité « homogène » recherchée vient alors des LIBELLÉS et ESPACEMENTS
   resserrés, pas de champs minuscules. Les sélecteurs `.nw-form-compact …`
   sont listés explicitement pour battre la règle compacte (même spécificité,
   définie après). */
@media (max-width: 767.98px) {
  body:not(.admin) .form-control,
  body:not(.admin) .form-select,
  body:not(.admin) textarea.form-control,
  body:not(.admin) .form-control-sm,
  body:not(.admin) .form-select-sm,
  body:not(.admin) .nw-form-compact .form-control,
  body:not(.admin) .nw-form-compact .form-select {
    font-size: 16px;        /* anti-zoom iOS + lisibilité (jamais < 16px) */
  }
  /* Libellés & aide : un cran sous le corps mais lisibles, marges resserrées
     → densité homogène SANS rapetisser les champs. */
  body:not(.admin) .form-label,
  body:not(.admin) .nw-form-compact .form-label { font-size: .9rem; margin-bottom: .25rem; }
  body:not(.admin) .form-text,
  body:not(.admin) .nw-form-compact .form-text { font-size: .8rem; }
}

/* Respect de prefers-reduced-motion : on neutralise les transitions de la
   couche polish pour les utilisateurs sensibles au mouvement. */
@media (prefers-reduced-motion: reduce) {
  body:not(.admin) .card,
  body:not(.admin) .nw-lift,
  body:not(.admin) .btn { transition: none; }
  body:not(.admin) .nw-lift:hover,
  body:not(.admin) .nw-lift:focus-within { transform: none; }
  /* Neutralise aussi les animations à mouvement répété/flash, dans TOUS les
     contextes (front ET back-office) : le pulse « en ligne » du chat est
     infini, et les flashs de mise en avant de la timeline (lien d'événement)
     restaient actifs malgré la préférence (WCAG 2.3.3). */
  .chat-status-dot,
  .chat-card .chat-typing-dots > span,
  .timeline > li.action-flash,
  body:not(.admin) .timeline > li.action-flash .timeline-event-card { animation: none; }
}
