/* ---------------------------------------------------------------
 * design-system.css — FICHIER GÉNÉRÉ, NE PAS ÉDITER À LA MAIN.
 * Sources : bookconnect_fr/assets/css/design-system/
 * Reconstruire : php _dev/scripts/build_design_system.php
 * --------------------------------------------------------------- */


/* ===== _ds-tokens.css (174 lignes) ===== */
/* =====================================================================
   _ds-tokens.css — Socle de jetons du design system LivreConnect
   ---------------------------------------------------------------------
   Chantier « harmonisation sur la navbar choisie ».
   Vocabulaire aligné sur les jetons --nv-* des navbars (_navbars-base.css)
   pour que la barre de navigation et les pages parlent la même langue.

   Préfixe --ds-* : choisi pour ne heurter aucun des quatre systèmes déjà
   présents dans le projet — --nv-* (navbars), --color-* (tokens.css, chargé
   sur 38 pages seulement), --primary-color (main.css/bundle-base), et
   --navbar-color (navbar Classique, injecté en style inline).

   L'ACCENT est la couleur de rôle de l'utilisateur. Elle est injectée à
   l'exécution par includes/design_system.php dans un <style nonce> qui
   redéfinit --ds-accent après le chargement de cette feuille. La valeur
   ci-dessous n'est qu'un repli (violet Maquettiste, comme le reste du code).
   ===================================================================== */

/* ---------------------------------------------------------------------
   Fontes auto-hébergées.
   Les navbars déclaraient 'Hanken Grotesk' et 'Fraunces' depuis l'origine
   sans qu'aucune page ne les charge : les utilisateurs voyaient system-ui.
   Servies depuis notre domaine (font-src 'self') et non par un CDN, pour
   la même raison qui vaut déjà des avertissements « Tracking Prevention »
   à jsDelivr. Fontes variables : un fichier couvre toute la plage.
   --------------------------------------------------------------------- */
/* ⚠ Chemins RACINE et non relatifs : les sources vivent dans
   assets/css/design-system/ mais le fichier construit est depose dans
   assets/css/. Un « ../../fonts/ » correct dans la source pointait donc a
   cote apres concatenation, et les deux fontes n'ont jamais ete chargees
   entre le 01/08 et le 04/08 — l'application tournait en repli systeme
   sans que rien ne le signale. Verification : document.fonts.check(). */
@font-face{
  font-family:'Hanken Grotesk';
  src:url('/assets/fonts/hanken-grotesk-latin.woff2') format('woff2');
  font-weight:100 900;
  font-style:normal;
  font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
                U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,
                U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'Fraunces';
  src:url('/assets/fonts/fraunces-latin.woff2') format('woff2');
  font-weight:100 900;
  font-style:normal;
  font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
                U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,
                U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

:root{
  /* ---- Neutres — repris à l'identique de _navbars-base.css ---------- */
  --ds-ink:#17151f;              /* texte principal        */
  --ds-ink-soft:#3f3c4d;         /* texte secondaire       */
  --ds-muted:#6c6a78;            /* texte atténué (AA sur paper et canvas) */
  --ds-paper:#ffffff;            /* surface de bloc        */
  --ds-canvas:#f5f3f7;           /* fond de page           */
  --ds-sunken:#efedf3;           /* creux, pistes d'onglets */
  --ds-line:#e7e4ee;             /* filet                  */
  --ds-line-soft:#f2f0f6;        /* filet discret          */

  /* ---- Accent de rôle ----------------------------------------------
     --ds-accent est réécrit à l'exécution avec la couleur du rôle.

     RÈGLE DE CONTRASTE — l'accent BRUT ne porte JAMAIS ni texte, ni aplat,
     ni bordure signifiante. Les 29 couleurs de rôle sont des couleurs de
     marque, pas des couleurs d'interface : certaines sont très claires
     (Libraire Indépendant #51d709 tombe à 1,72:1 sur le fond de page).
     Deux dérivées assument donc tous les usages porteurs de sens :

       --ds-accent-ink (56 %) → texte, aplats pleins, survols.  Seuil 4,5:1.
       --ds-accent-ui  (72 %) → bordures, filets d'état, focus. Seuil 3:1.

     Pourcentages déterminés par recherche du mélange le plus CLAIR qui
     tienne le seuil pour les 29 rôles, puis reculés d'un cran pour garder
     de la marge (les couleurs de rôle sont modifiables en administration —
     elles l'ont déjà été). Pires cas mesurés : 5,01 et 3,24.
     Vérification : php _dev/scripts/check_contrast_roles.php               */
  --ds-accent:#7c3aed;
  --ds-accent-ink:color-mix(in srgb, var(--ds-accent) 56%, #000);
  --ds-accent-ui:color-mix(in srgb, var(--ds-accent) 72%, #000);
  --ds-accent-weak:color-mix(in srgb, var(--ds-accent) 12%, #fff);
  --ds-accent-soft:color-mix(in srgb, var(--ds-accent) 22%, #fff);
  --ds-accent-line:color-mix(in srgb, var(--ds-accent) 38%, #fff); /* décoratif seulement */

  /* ---- Couleurs sémantiques — triplet fond / filet / encre ----------
     Encres vérifiées AA sur leur propre fond (script de contraste). */
  --ds-success:#0f7a4f;  --ds-success-bg:#e3f5ec;  --ds-success-line:#b6e2ce;
  --ds-warning:#8a5a00;  --ds-warning-bg:#fdf1dc;  --ds-warning-line:#f0d3a0;
  --ds-danger:#b32338;   --ds-danger-bg:#fce8eb;   --ds-danger-line:#f2c2ca;
  --ds-info:#0a5f96;     --ds-info-bg:#e3f0f8;     --ds-info-line:#b8d9ec;

  /* ---- Espacement — pas de 4 px ------------------------------------- */
  --ds-s0:0; --ds-s1:4px; --ds-s2:8px;  --ds-s3:12px; --ds-s4:16px;
  --ds-s5:20px; --ds-s6:24px; --ds-s7:32px; --ds-s8:40px; --ds-s9:56px;

  /* ---- Rayons — six crans (le projet en comptait six SANS échelle) --
     --ds-radius et --ds-radius-control sont pilotés par la SAVEUR.      */
  --ds-r-xs:4px; --ds-r-sm:8px; --ds-r-md:12px;
  --ds-r-lg:18px; --ds-r-xl:24px; --ds-r-pill:999px;
  --ds-radius:var(--ds-r-md);          /* blocs : cartes, panneaux      */
  --ds-radius-control:var(--ds-r-sm);  /* contrôles : boutons, champs   */

  /* ---- Élévation — cinq crans ; --ds-shadow est piloté par la saveur */
  --ds-e0:none;
  --ds-e1:0 1px 2px rgba(23,21,31,.06);
  --ds-e2:0 4px 12px -4px rgba(23,21,31,.12);
  --ds-e3:0 6px 24px -8px rgba(23,21,31,.18);
  --ds-e4:0 18px 50px -12px rgba(23,21,31,.30);
  --ds-shadow:var(--ds-e2);
  --ds-shadow-raised:var(--ds-e3);
  --ds-shadow-overlay:var(--ds-e4);

  /* ---- Typographie -------------------------------------------------- */
  /* UNE SEULE POLICE, celle de la barre de navigation choisie.
     Reference : _navbars-base.css:1 declare 'Hanken Grotesk' sur .nv-root,
     donc pour les styles 1 a 8. La saveur 0 la surcharge avec celle de la
     navbar Classique. La police d'AFFICHAGE n'est pas une police differente :
     elle vaut la police de texte, et les titres se distinguent par la graisse
     et la taille, pas par un changement de famille. */
  --ds-font-text:'Hanken Grotesk',system-ui,-apple-system,'Segoe UI',sans-serif;
  --ds-font-display:var(--ds-font-text);
  /* Re-declaree sur body plus bas : sinon une saveur qui change
     --ds-font-text laisserait --ds-font-display figee sur la valeur de
     :root (titres en Hanken alors que le corps passait en Segoe UI). */
  --ds-font-mono:ui-monospace,'Cascadia Mono','Segoe UI Mono',Menlo,monospace;

  --ds-t-xs:.75rem;  --ds-t-sm:.8125rem; --ds-t-md:.875rem; --ds-t-base:1rem;
  --ds-t-lg:1.125rem; --ds-t-xl:1.375rem; --ds-t-2xl:1.75rem; --ds-t-3xl:2.25rem;
  --ds-w-normal:400; --ds-w-medium:500; --ds-w-semi:600; --ds-w-bold:700;
  --ds-lh-tight:1.2; --ds-lh-snug:1.35; --ds-lh-normal:1.55;
  --ds-tracking-tight:-.02em; --ds-tracking-wide:.06em;

  /* ---- Densité — hauteur des contrôles, pilotée par la saveur ------- */
  --ds-control-h:40px;
  --ds-control-px:14px;

  /* ---- Mouvement ---------------------------------------------------- */
  --ds-dur-fast:.12s; --ds-dur:.2s; --ds-dur-slow:.32s;
  --ds-ease:cubic-bezier(.4,0,.2,1);
  --ds-ease-out:cubic-bezier(0,0,.2,1);

  /* ---- Focus — sur l'accent de rôle, jamais sur le bleu Bootstrap
     codé en dur (#0d6efd, relevé 5 fois) ni sur le rouge du module
     contact (components/contact.css:140), qui signale une erreur là où
     il n'y en a pas.                                                    */
  --ds-focus-width:2.5px;
  --ds-focus-color:var(--ds-accent-ink);
  --ds-focus-ring:0 0 0 3px color-mix(in srgb, var(--ds-accent) 28%, transparent);

  /* ---- Mesure de lecture — surchargée par les saveurs 4 et 6 -------- */
  --ds-measure:none;
}

/* Anneau de focus unifié. Portée volontairement large : c'est le seul
   endroit du projet où le focus est défini de façon cohérente. */
:focus-visible{
  outline:var(--ds-focus-width) solid var(--ds-focus-color);
  outline-offset:2px;
  border-radius:var(--ds-r-xs);
}

@media (prefers-reduced-motion:reduce){
  :root{ --ds-dur-fast:0s; --ds-dur:0s; --ds-dur-slow:0s; }
}

/* Re-substitution de la police d'affichage au niveau du corps, pour qu'elle
   suive la police de texte de la saveur active. Une saveur qui veut vraiment
   une police d'affichage distincte la redeclare : son selecteur
   `body:has(#ds-root[...])` a une specificite superieure. */
body{ --ds-font-display:var(--ds-font-text); }


/* ===== _ds-bridge.css (565 lignes) ===== */
/* =====================================================================
   _ds-bridge.css — Pont vers Bootstrap 5.3
   ---------------------------------------------------------------------
   Bootstrap 5.3 est piloté par variables CSS, mais PAS à la racine :
   chaque composant déclare ses variables SUR SON PROPRE SÉLECTEUR
   (`.card{--bs-card-border-radius:...}`). Une redéfinition en `:root`
   serait donc écrasée par le composant lui-même.
   → On surcharge les variables AU MÊME NIVEAU, sur le même sélecteur.
     Spécificité identique, feuille chargée après : c'est nous qui gagnons,
     sans un seul !important.

   De même, les variantes de couleur (`.btn-primary`, `.alert-danger`…)
   sont compilées avec des valeurs en dur par Bootstrap : redéfinir
   --bs-primary en racine n'a AUCUN effet sur elles. Chacune est reprise
   explicitement ci-dessous.

   Volumétrie visée (mesurée sur le markup) : 5 139 boutons, 1 353 cartes,
   1 177 modales, 1 199 étiquettes, 1 131 champs, 791 alertes, 580 badges,
   457 fils d'Ariane, 450 tableaux.
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. Racine Bootstrap
   --------------------------------------------------------------------- */
:root{
  --bs-body-font-family:var(--ds-font-text);
  --bs-body-font-size:var(--ds-t-base);
  --bs-body-line-height:var(--ds-lh-normal);
  --bs-body-color:var(--ds-ink);
  --bs-body-bg:var(--ds-canvas);
  --bs-emphasis-color:var(--ds-ink);
  --bs-secondary-color:var(--ds-muted);
  --bs-tertiary-bg:var(--ds-sunken);

  --bs-border-color:var(--ds-line);
  --bs-border-color-translucent:var(--ds-line);
  --bs-border-radius:var(--ds-radius);
  --bs-border-radius-sm:var(--ds-r-sm);
  --bs-border-radius-lg:var(--ds-r-lg);
  --bs-border-radius-xl:var(--ds-r-xl);
  --bs-border-radius-pill:var(--ds-r-pill);

  --bs-link-color:var(--ds-accent-ink);
  --bs-link-hover-color:var(--ds-accent-ink);
  --bs-link-color-rgb:initial;

  --bs-primary:var(--ds-accent-ink);
  --bs-heading-color:var(--ds-ink);
  --bs-code-color:var(--ds-accent-ink);
}

body{
  font-family:var(--ds-font-text);
  color:var(--ds-ink);
  -webkit-font-smoothing:antialiased;
}

h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{
  font-family:var(--ds-font-display);
  color:var(--ds-ink);
  font-weight:var(--ds-w-semi);
  line-height:var(--ds-lh-tight);
  letter-spacing:var(--ds-tracking-tight);
}

a{ color:var(--ds-accent-ink); }
a:hover{ color:var(--ds-accent-ink); }
.text-muted{ color:var(--ds-muted) !important; }  /* riposte à l'utilitaire Bootstrap, lui-même en !important */

/* ---------------------------------------------------------------------
   2. Boutons — 5 139 occurrences
   --------------------------------------------------------------------- */
.btn{
  --bs-btn-padding-y:0;
  --bs-btn-padding-x:var(--ds-control-px);
  --bs-btn-font-family:var(--ds-font-text);
  --bs-btn-font-size:var(--ds-t-md);
  --bs-btn-font-weight:var(--ds-w-semi);
  --bs-btn-border-radius:var(--ds-radius-control);
  --bs-btn-border-width:1px;
  min-height:var(--ds-control-h);
  display:inline-flex;
  align-items:center;
  justify-content:center;
  gap:var(--ds-s2);
  transition:background-color var(--ds-dur-fast) var(--ds-ease),
             border-color var(--ds-dur-fast) var(--ds-ease),
             color var(--ds-dur-fast) var(--ds-ease);
}
.btn-sm{ --bs-btn-font-size:var(--ds-t-sm); --bs-btn-padding-x:var(--ds-s3);
  min-height:calc(var(--ds-control-h) - 8px); }
.btn-lg{ --bs-btn-font-size:var(--ds-t-lg); --bs-btn-padding-x:var(--ds-s5);
  min-height:calc(var(--ds-control-h) + 8px); }
.btn:focus-visible{ box-shadow:var(--ds-focus-ring); }

/* Principal — sur --ds-accent-ink, jamais sur l'accent brut (contraste AA) */
.btn-primary{
  --bs-btn-bg:var(--ds-accent-ink);
  --bs-btn-border-color:var(--ds-accent-ink);
  --bs-btn-color:#fff;
  --bs-btn-hover-bg:color-mix(in srgb, var(--ds-accent) 48%, #000);
  --bs-btn-hover-border-color:color-mix(in srgb, var(--ds-accent) 48%, #000);
  --bs-btn-hover-color:#fff;
  --bs-btn-active-bg:color-mix(in srgb, var(--ds-accent) 40%, #000);
  --bs-btn-active-border-color:color-mix(in srgb, var(--ds-accent) 40%, #000);
  /* Neutres et non un accent delave : un aplat clair sous du texte blanc
     etait illisible a la recette visuelle (saveurs 0 a 8, capture du 01/08). */
  --bs-btn-disabled-bg:var(--ds-sunken);
  --bs-btn-disabled-border-color:var(--ds-line);
  --bs-btn-disabled-color:var(--ds-muted);
}
.btn-outline-primary{
  --bs-btn-color:var(--ds-accent-ink);
  --bs-btn-border-color:var(--ds-accent-ui);
  --bs-btn-bg:transparent;
  --bs-btn-hover-bg:var(--ds-accent-weak);
  --bs-btn-hover-border-color:var(--ds-accent-ink);
  --bs-btn-hover-color:var(--ds-accent-ink);
  --bs-btn-active-bg:var(--ds-accent-soft);
  --bs-btn-active-color:var(--ds-accent-ink);
  --bs-btn-active-border-color:var(--ds-accent-ink);
}
.btn-secondary{
  --bs-btn-bg:var(--ds-paper); --bs-btn-border-color:var(--ds-line);
  --bs-btn-color:var(--ds-ink);
  --bs-btn-hover-bg:var(--ds-sunken); --bs-btn-hover-border-color:var(--ds-line);
  --bs-btn-hover-color:var(--ds-ink);
  --bs-btn-active-bg:var(--ds-sunken); --bs-btn-active-color:var(--ds-ink);
  --bs-btn-active-border-color:var(--ds-line);
}
.btn-outline-secondary{
  --bs-btn-color:var(--ds-ink-soft); --bs-btn-border-color:var(--ds-line);
  --bs-btn-hover-bg:var(--ds-sunken); --bs-btn-hover-color:var(--ds-ink);
  --bs-btn-hover-border-color:var(--ds-line);
  --bs-btn-active-bg:var(--ds-sunken); --bs-btn-active-color:var(--ds-ink);
  --bs-btn-active-border-color:var(--ds-line);
}
.btn-light{
  --bs-btn-bg:var(--ds-sunken); --bs-btn-border-color:transparent;
  --bs-btn-color:var(--ds-ink);
  --bs-btn-hover-bg:var(--ds-line); --bs-btn-hover-color:var(--ds-ink);
  --bs-btn-hover-border-color:transparent;
}
.btn-danger{
  --bs-btn-bg:var(--ds-danger); --bs-btn-border-color:var(--ds-danger);
  --bs-btn-color:#fff;
  --bs-btn-hover-bg:#8f1c2d; --bs-btn-hover-border-color:#8f1c2d; --bs-btn-hover-color:#fff;
  --bs-btn-active-bg:#7a1826; --bs-btn-active-border-color:#7a1826;
}
.btn-outline-danger{
  --bs-btn-color:var(--ds-danger); --bs-btn-border-color:var(--ds-danger-line);
  --bs-btn-hover-bg:var(--ds-danger-bg); --bs-btn-hover-color:var(--ds-danger);
  --bs-btn-hover-border-color:var(--ds-danger);
}
/* .btn-success était figé en !important dans bundle-base.css:442-444 —
   riposte nommée, seul endroit du pont qui en a besoin. */
.btn.btn-success{
  background-color:var(--ds-success) !important;
  border-color:var(--ds-success) !important;
  color:#fff !important;
}
.btn-warning{
  --bs-btn-bg:var(--ds-warning); --bs-btn-border-color:var(--ds-warning);
  --bs-btn-color:#fff; --bs-btn-hover-color:#fff;
  --bs-btn-hover-bg:#6f4800; --bs-btn-hover-border-color:#6f4800;
}
.btn-link{
  --bs-btn-color:var(--ds-accent-ink); --bs-btn-hover-color:var(--ds-accent-ink);
  --bs-btn-font-weight:var(--ds-w-medium);
  text-decoration:none;
}
.btn-link:hover{ text-decoration:underline; }
/* Le rayon des groupes de boutons est figé en !important à deux endroits
   (bundle-base.css:403, main.css:205). Riposte nommée. */
.btn-group > .btn{ border-radius:0 !important; }
.btn-group > .btn:first-child{
  border-top-left-radius:var(--ds-radius-control) !important;
  border-bottom-left-radius:var(--ds-radius-control) !important;
}
.btn-group > .btn:last-child{
  border-top-right-radius:var(--ds-radius-control) !important;
  border-bottom-right-radius:var(--ds-radius-control) !important;
}

/* ---------------------------------------------------------------------
   3. Cartes — 1 353 occurrences
   --------------------------------------------------------------------- */
.card{
  --bs-card-bg:var(--ds-paper);
  --bs-card-color:var(--ds-ink);
  --bs-card-border-width:1px;
  --bs-card-border-color:var(--ds-line);
  --bs-card-border-radius:var(--ds-radius);
  --bs-card-inner-border-radius:calc(var(--ds-radius) - 1px);
  --bs-card-spacer-y:var(--ds-s5);
  --bs-card-spacer-x:var(--ds-s5);
  --bs-card-cap-bg:transparent;
  --bs-card-cap-padding-y:var(--ds-s4);
  --bs-card-cap-padding-x:var(--ds-s5);
  --bs-card-title-spacer-y:var(--ds-s2);
  box-shadow:var(--ds-shadow);
}
.card-header{
  border-bottom:1px solid var(--ds-line);
  font-weight:var(--ds-w-semi);
  color:var(--ds-ink);
}
.card-footer{ border-top:1px solid var(--ds-line); background:transparent; }
.card-title{ font-family:var(--ds-font-display); font-weight:var(--ds-w-semi); }
.card-subtitle,.card-text small{ color:var(--ds-muted); }

/* ---------------------------------------------------------------------
   4. Alertes — 791 occurrences
   --------------------------------------------------------------------- */
.alert{
  --bs-alert-border-radius:var(--ds-radius);
  --bs-alert-padding-y:var(--ds-s4);
  --bs-alert-padding-x:var(--ds-s4);
  --bs-alert-border:1px solid;
  border-width:1px;
  font-size:var(--ds-t-md);
}
.alert-success{ --bs-alert-bg:var(--ds-success-bg); --bs-alert-color:var(--ds-success);
  --bs-alert-border-color:var(--ds-success-line); --bs-alert-link-color:var(--ds-success); }
.alert-warning{ --bs-alert-bg:var(--ds-warning-bg); --bs-alert-color:var(--ds-warning);
  --bs-alert-border-color:var(--ds-warning-line); --bs-alert-link-color:var(--ds-warning); }
.alert-danger{ --bs-alert-bg:var(--ds-danger-bg); --bs-alert-color:var(--ds-danger);
  --bs-alert-border-color:var(--ds-danger-line); --bs-alert-link-color:var(--ds-danger); }
.alert-info{ --bs-alert-bg:var(--ds-info-bg); --bs-alert-color:var(--ds-info);
  --bs-alert-border-color:var(--ds-info-line); --bs-alert-link-color:var(--ds-info); }
.alert-primary{ --bs-alert-bg:var(--ds-accent-weak); --bs-alert-color:var(--ds-accent-ink);
  --bs-alert-border-color:var(--ds-accent-line); --bs-alert-link-color:var(--ds-accent-ink); }
.alert-secondary,.alert-light{ --bs-alert-bg:var(--ds-sunken); --bs-alert-color:var(--ds-ink-soft);
  --bs-alert-border-color:var(--ds-line); }

/* ---------------------------------------------------------------------
   5. Modales — 1 177 occurrences
   --------------------------------------------------------------------- */
.modal{
  --bs-modal-bg:var(--ds-paper);
  --bs-modal-color:var(--ds-ink);
  --bs-modal-border-width:0;
  --bs-modal-border-radius:var(--ds-r-lg);
  --bs-modal-inner-border-radius:var(--ds-r-lg);
  --bs-modal-box-shadow:var(--ds-shadow-overlay);
  --bs-modal-padding:var(--ds-s6);
  --bs-modal-header-padding-y:var(--ds-s5);
  --bs-modal-header-padding-x:var(--ds-s6);
  --bs-modal-header-border-color:var(--ds-line);
  --bs-modal-footer-border-color:var(--ds-line);
  --bs-modal-footer-gap:var(--ds-s2);
}
.modal-title{ font-family:var(--ds-font-display); font-weight:var(--ds-w-semi);
  font-size:var(--ds-t-xl); }
.modal-backdrop{ --bs-backdrop-bg:#17151f; --bs-backdrop-opacity:.44; }

/* ---------------------------------------------------------------------
   6. Badges — 580 occurrences (les deux API de couleur cohabitent)
   --------------------------------------------------------------------- */
.badge{
  --bs-badge-padding-x:var(--ds-s2);
  --bs-badge-padding-y:.35em;
  --bs-badge-font-size:var(--ds-t-xs);
  --bs-badge-font-weight:var(--ds-w-semi);
  --bs-badge-border-radius:var(--ds-r-pill);
  letter-spacing:.01em;
}
.badge.bg-primary,.badge.text-bg-primary{
  background-color:var(--ds-accent-weak) !important; color:var(--ds-accent-ink) !important; }
.badge.bg-secondary,.badge.text-bg-secondary{
  background-color:var(--ds-sunken) !important; color:var(--ds-ink-soft) !important; }
.badge.bg-success,.badge.text-bg-success{
  background-color:var(--ds-success-bg) !important; color:var(--ds-success) !important; }
.badge.bg-warning,.badge.text-bg-warning{
  background-color:var(--ds-warning-bg) !important; color:var(--ds-warning) !important; }
.badge.bg-danger,.badge.text-bg-danger{
  background-color:var(--ds-danger-bg) !important; color:var(--ds-danger) !important; }
.badge.bg-info,.badge.text-bg-info{
  background-color:var(--ds-info-bg) !important; color:var(--ds-info) !important; }
.badge.bg-light,.badge.text-bg-light{
  background-color:var(--ds-sunken) !important; color:var(--ds-ink-soft) !important; }
.badge.bg-dark,.badge.text-bg-dark{
  background-color:var(--ds-ink) !important; color:#fff !important; }

/* ---------------------------------------------------------------------
   7. Tableaux — 450 occurrences
   --------------------------------------------------------------------- */
.table{
  --bs-table-color:var(--ds-ink);
  --bs-table-bg:transparent;
  --bs-table-border-color:var(--ds-line);
  --bs-table-striped-bg:var(--ds-canvas);
  --bs-table-striped-color:var(--ds-ink);
  --bs-table-hover-bg:var(--ds-accent-weak);
  --bs-table-hover-color:var(--ds-ink);
  font-size:var(--ds-t-md);
  vertical-align:middle;
}
.table > thead th,.table-light > th,.table > thead.table-light th{
  background-color:var(--ds-sunken);
  color:var(--ds-ink-soft);
  font-weight:var(--ds-w-semi);
  font-size:var(--ds-t-sm);
  letter-spacing:var(--ds-tracking-wide);
  text-transform:uppercase;
  border-bottom-color:var(--ds-line);
  white-space:nowrap;
}
.table > :not(caption) > * > *{ padding:var(--ds-s3) var(--ds-s4); }
.table-responsive{ border-radius:var(--ds-radius); }

/* ---------------------------------------------------------------------
   8. Fils d'Ariane — 457 occurrences
   --------------------------------------------------------------------- */
.breadcrumb{
  --bs-breadcrumb-divider-color:var(--ds-muted);
  --bs-breadcrumb-item-active-color:var(--ds-ink);
  --bs-breadcrumb-font-size:var(--ds-t-md);
  --bs-breadcrumb-margin-bottom:var(--ds-s4);
  --bs-breadcrumb-item-padding-x:var(--ds-s2);
}
.breadcrumb-item a{ color:var(--ds-muted); text-decoration:none; }
.breadcrumb-item a:hover{ color:var(--ds-accent-ink); text-decoration:underline; }
.breadcrumb-item.active{ font-weight:var(--ds-w-medium); }

/* ---------------------------------------------------------------------
   9. Onglets et pilules
   --------------------------------------------------------------------- */
.nav{
  --bs-nav-link-color:var(--ds-muted);
  --bs-nav-link-hover-color:var(--ds-accent-ink);
  --bs-nav-link-font-size:var(--ds-t-md);
  --bs-nav-link-font-weight:var(--ds-w-medium);
  --bs-nav-link-padding-y:var(--ds-s3);
  --bs-nav-link-padding-x:var(--ds-s4);
}
.nav-tabs{
  --bs-nav-tabs-border-color:var(--ds-line);
  --bs-nav-tabs-link-active-color:var(--ds-accent-ink);
  --bs-nav-tabs-link-active-bg:transparent;
  --bs-nav-tabs-link-active-border-color:transparent transparent var(--ds-accent-ui) transparent;
  --bs-nav-tabs-link-hover-border-color:transparent;
  --bs-nav-tabs-border-width:2px;
  --bs-nav-tabs-border-radius:0;
}
.nav-tabs .nav-link{ border-width:0 0 2px 0; margin-bottom:-2px; }
.nav-tabs .nav-link.active{ font-weight:var(--ds-w-semi); }
.nav-pills{
  --bs-nav-pills-border-radius:var(--ds-r-pill);
  --bs-nav-pills-link-active-color:var(--ds-accent-ink);
  --bs-nav-pills-link-active-bg:var(--ds-accent-weak);
}
.nav-pills .nav-link.active{ font-weight:var(--ds-w-semi); }

/* ---------------------------------------------------------------------
   10. Menus déroulants, groupes de liste, accordéons
   --------------------------------------------------------------------- */
.dropdown-menu{
  --bs-dropdown-bg:var(--ds-paper);
  --bs-dropdown-border-color:var(--ds-line);
  --bs-dropdown-border-radius:var(--ds-r-lg);
  --bs-dropdown-box-shadow:var(--ds-shadow-overlay);
  --bs-dropdown-link-color:var(--ds-ink);
  --bs-dropdown-link-hover-bg:var(--ds-accent-weak);
  --bs-dropdown-link-hover-color:var(--ds-accent-ink);
  --bs-dropdown-link-active-bg:var(--ds-accent-weak);
  --bs-dropdown-link-active-color:var(--ds-accent-ink);
  --bs-dropdown-item-padding-y:var(--ds-s2);
  --bs-dropdown-item-padding-x:var(--ds-s3);
  --bs-dropdown-padding-y:var(--ds-s2);
  --bs-dropdown-font-size:var(--ds-t-md);
  --bs-dropdown-divider-bg:var(--ds-line);
}
.dropdown-item{ border-radius:var(--ds-r-xs); }
.list-group{
  --bs-list-group-bg:var(--ds-paper);
  --bs-list-group-border-color:var(--ds-line);
  --bs-list-group-border-radius:var(--ds-radius);
  --bs-list-group-color:var(--ds-ink);
  --bs-list-group-item-padding-y:var(--ds-s3);
  --bs-list-group-item-padding-x:var(--ds-s4);
  --bs-list-group-action-hover-bg:var(--ds-canvas);
  --bs-list-group-active-bg:var(--ds-accent-weak);
  --bs-list-group-active-color:var(--ds-accent-ink);
  --bs-list-group-active-border-color:var(--ds-accent-ui);
}
.accordion{
  --bs-accordion-bg:var(--ds-paper);
  --bs-accordion-border-color:var(--ds-line);
  --bs-accordion-border-radius:var(--ds-radius);
  --bs-accordion-inner-border-radius:calc(var(--ds-radius) - 1px);
  --bs-accordion-btn-color:var(--ds-ink);
  --bs-accordion-btn-bg:transparent;
  --bs-accordion-btn-focus-box-shadow:var(--ds-focus-ring);
  --bs-accordion-active-color:var(--ds-accent-ink);
  --bs-accordion-active-bg:var(--ds-accent-weak);
  --bs-accordion-btn-padding-y:var(--ds-s4);
  --bs-accordion-btn-padding-x:var(--ds-s5);
}

/* ---------------------------------------------------------------------
   11. Pagination — moitié en markup, moitié générée par PaginationHelper
   --------------------------------------------------------------------- */
.pagination{
  --bs-pagination-color:var(--ds-ink-soft);
  --bs-pagination-bg:var(--ds-paper);
  --bs-pagination-border-color:var(--ds-line);
  --bs-pagination-border-radius:var(--ds-radius-control);
  --bs-pagination-hover-bg:var(--ds-accent-weak);
  --bs-pagination-hover-color:var(--ds-accent-ink);
  --bs-pagination-hover-border-color:var(--ds-accent-ui);
  --bs-pagination-active-bg:var(--ds-accent-ink);
  --bs-pagination-active-border-color:var(--ds-accent-ink);
  --bs-pagination-active-color:#fff;
  --bs-pagination-disabled-bg:var(--ds-canvas);
  --bs-pagination-disabled-color:var(--ds-muted);
  --bs-pagination-disabled-border-color:var(--ds-line);
  --bs-pagination-padding-y:var(--ds-s2);
  --bs-pagination-padding-x:var(--ds-s3);
  --bs-pagination-font-size:var(--ds-t-md);
  gap:var(--ds-s1);
}
/* bundle-feed-shell.css:949/962 verrouille l'actif et le désactivé */
.pagination .page-item.active .page-link{
  background-color:var(--ds-accent-ink) !important;
  border-color:var(--ds-accent-ink) !important;
  color:#fff !important;
}
.pagination .page-link{ border-radius:var(--ds-radius-control); }

/* ---------------------------------------------------------------------
   12. Divers : progression, toasts, hors-canevas, infobulles
   --------------------------------------------------------------------- */
.progress,.progress-stacked{
  --bs-progress-bg:var(--ds-sunken);
  --bs-progress-border-radius:var(--ds-r-pill);
  --bs-progress-bar-bg:var(--ds-accent-ink);
  --bs-progress-height:8px;
  --bs-progress-font-size:var(--ds-t-xs);
}
.toast{
  --bs-toast-bg:var(--ds-paper);
  --bs-toast-border-color:var(--ds-line);
  --bs-toast-border-radius:var(--ds-radius);
  --bs-toast-box-shadow:var(--ds-shadow-overlay);
  --bs-toast-header-color:var(--ds-ink);
  --bs-toast-header-bg:transparent;
  --bs-toast-header-border-color:var(--ds-line);
}
.offcanvas,.offcanvas-start,.offcanvas-end,.offcanvas-top,.offcanvas-bottom{
  --bs-offcanvas-bg:var(--ds-paper);
  --bs-offcanvas-border-color:var(--ds-line);
  --bs-offcanvas-padding-y:var(--ds-s5);
  --bs-offcanvas-padding-x:var(--ds-s5);
  --bs-offcanvas-box-shadow:var(--ds-shadow-overlay);
}
.tooltip{
  --bs-tooltip-bg:var(--ds-ink);
  --bs-tooltip-color:#fff;
  --bs-tooltip-border-radius:var(--ds-r-sm);
  --bs-tooltip-font-size:var(--ds-t-sm);
  --bs-tooltip-opacity:1;
}
.popover{
  --bs-popover-bg:var(--ds-paper);
  --bs-popover-border-color:var(--ds-line);
  --bs-popover-border-radius:var(--ds-radius);
  --bs-popover-header-bg:var(--ds-canvas);
  --bs-popover-font-size:var(--ds-t-md);
}
.spinner-border,.spinner-grow{ color:var(--ds-accent-ink); }

/* =====================================================================
   13. RIPOSTES DE PROPRIÉTÉ — la limite du pont par variables
   ---------------------------------------------------------------------
   Tout ce qui précède repose sur un postulat : Bootstrap 5.3 lit ses
   variables, donc les redéfinir suffit. Ce postulat TOMBE partout où le
   projet écrase la PROPRIÉTÉ elle-même au lieu de la variable.

   Cas d'école mesuré le 04/08 sur le tableau de bord Attaché de Presse :
     bundle-base.css:244  .btn { border-radius: var(--border-radius) }
     bundle-base.css:429  .btn-primary { background-color: #007bff }
   Ces déclarations, à spécificité (0,1,0) comme les nôtres mais chargées
   avant, gagnent contre la déclaration `border-radius: var(--bs-btn-*)`
   de Bootstrap. Résultat : `--bs-btn-border-radius` valait bien 999px et
   le bouton s'affichait quand même à 8 px, en bleu Bootstrap.

   Recensement sur les feuilles chargées par TOUTES les pages
   authentifiées : 49 règles concernées, 23 sans !important (il suffit de
   déclarer la même propriété, nous sommes chargés en dernier) et 26 avec
   !important, presque toutes cantonnées à la navbar Classique — celles-là
   sont légitimes et restent en place.
   ===================================================================== */

/* --- Boutons : géométrie --- */
.btn{
  border-radius:var(--ds-radius-control);
  padding:0 var(--ds-control-px);
}
.btn-sm{ border-radius:var(--ds-radius-control); padding:0 var(--ds-s3); }
.btn-lg{ border-radius:var(--ds-radius-control); padding:0 var(--ds-s5); }
/* bundle-base.css:264 ajoute un soulèvement au survol, étranger au
   vocabulaire des navbars : on le neutralise pour tout le monde. */
.btn:hover{ transform:none; box-shadow:none; }
.btn:focus-visible{ box-shadow:var(--ds-focus-ring); }

/* --- Boutons : couleurs (bundle-base.css:267, 418, 429 les figent) --- */
.btn-primary{
  background-color:var(--ds-accent-ink);
  border-color:var(--ds-accent-ink);
  color:#fff;
}
.btn-primary:hover{
  background-color:color-mix(in srgb, var(--ds-accent) 44%, #000);
  border-color:color-mix(in srgb, var(--ds-accent) 44%, #000);
  color:#fff;
}
.btn-outline-primary{
  background-color:transparent;
  border-color:var(--ds-accent-ui);
  color:var(--ds-accent-ink);
}
.btn-outline-primary:hover{
  background-color:var(--ds-accent-weak);
  border-color:var(--ds-accent-ink);
  color:var(--ds-accent-ink);
}

/* --- Menus déroulants (bundle-feed-shell.css:301 fige les 4 propriétés).
   Portée restreinte au corps de page : la navbar Classique garde le sien. */
.main-content .dropdown-menu,
#main-content .dropdown-menu,
main .dropdown-menu{
  border-radius:var(--ds-r-lg);
  background:var(--ds-paper);
  border:1px solid var(--ds-line);
  box-shadow:var(--ds-shadow-overlay);
}

/* =====================================================================
   14. PROPAGATION DE LA POLICE
   ---------------------------------------------------------------------
   Poser font-family sur `body` ne propage que par HÉRITAGE : tout élément
   qui déclare la sienne l'emporte, quel que soit l'ordre de chargement.
   Le recensement du 04/08 relève 63 déclarations de police dans le projet,
   pour 25 valeurs distinctes. La plupart sont légitimes (icônes, monospace,
   vitrine publique). Ne restent que ces quelques îlots dans l'application
   authentifiée, qui imposaient une police étrangère à la barre choisie.
   ===================================================================== */
.auteur-guide,
.editor-content,
.legal-wrapper,
.error-page,
.voltr-container,
.voltr-input-field,
.voltr-textarea{
  font-family:var(--ds-font-text);
}

/* Le monospace, lui, reste : c'est une information, pas une décoration. */
code,kbd,samp,pre,
.promo-code,.fiche-isbn,.diag-key,.ticket-print,
.voltr-msg-content code{
  font-family:var(--ds-font-mono);
}


/* ===== _ds-components.css (293 lignes) ===== */
/* =====================================================================
   _ds-components.css — Ce que le pont Bootstrap ne peut pas atteindre
   ---------------------------------------------------------------------
   Trois catégories :
   1. Les composants que Bootstrap 5.3 ne pilote PAS par variables —
      les formulaires au premier chef (valeurs compilées en dur).
   2. Les RIPOSTES NOMMÉES aux !important qui verrouillent une propriété
      que le thème doit reprendre. Chacune est justifiée par un
      fichier:ligne. Aucune riposte « au cas où ».
   3. Les composants MAISON canoniques, absents des 289 pages du shell :
      components/empty-state.css, kpi-card.css et consorts ne sont chargés
      que par header.php, soit 38 pages sur 327.
   ===================================================================== */

/* =====================================================================
   1. FORMULAIRES
   ---------------------------------------------------------------------
   Bootstrap compile .form-control avec des valeurs en dur : aucune
   variable à surcharger, il faut écrire les propriétés.
   Bonne nouvelle mesurée : aucun fichier du projet ne restyle
   .form-control globalement (les 27 sélecteurs relevés sont tous
   préfixés d'un contexte), donc ces règles ne se battent contre rien
   dans le cas général.
   ===================================================================== */
.form-label{
  font-size:var(--ds-t-md);
  font-weight:var(--ds-w-medium);
  color:var(--ds-ink-soft);
  margin-bottom:var(--ds-s2);
}
/* Marqueur d'obligation : le projet en comptait TROIS (astérisque en dur,
   span aria-hidden, deux règles ::after). Un seul, porté par la donnée. */
.form-label[data-requis="1"]::after,
.ds-field[data-requis="1"] > .form-label::after{
  content:"*";
  margin-inline-start:.25em;
  color:var(--ds-danger);
  font-weight:var(--ds-w-bold);
}

.form-control,.form-select{
  font-family:var(--ds-font-text);
  font-size:var(--ds-t-md);
  color:var(--ds-ink);
  background-color:var(--ds-paper);
  border:1px solid var(--ds-line);
  border-radius:var(--ds-radius-control);
  min-height:var(--ds-control-h);
  padding:var(--ds-s2) var(--ds-s3);
  transition:border-color var(--ds-dur-fast) var(--ds-ease),
             box-shadow var(--ds-dur-fast) var(--ds-ease);
}
/* Une zone de texte suit le rayon des BLOCS, jamais celui des contrôles :
   sur les saveurs à capsule (1, 2, 4, 5, 6, 7), un rayon de 999 px sur un
   champ multiligne rogne la première et la dernière ligne. Défaut relevé à
   la recette visuelle du 02/08 sur la saveur 5. */
textarea.form-control{
  min-height:calc(var(--ds-control-h) * 2.2);
  line-height:var(--ds-lh-normal);
  border-radius:var(--ds-radius);
  padding:var(--ds-s3) var(--ds-s4);
}
.form-control-sm,.form-select-sm{
  font-size:var(--ds-t-sm); min-height:calc(var(--ds-control-h) - 8px);
  padding:var(--ds-s1) var(--ds-s2); border-radius:var(--ds-r-xs);
}
.form-control-lg,.form-select-lg{
  font-size:var(--ds-t-lg); min-height:calc(var(--ds-control-h) + 8px);
}
.form-control::placeholder{ color:var(--ds-muted); opacity:1; }

/* Focus sur l'accent de rôle. Remplace le bleu Bootstrap en dur (#0d6efd,
   relevé 5 fois) ET le focus ROUGE de components/contact.css:140, qui
   signalait une erreur là où il n'y en a pas. */
.form-control:focus,.form-select:focus{
  border-color:var(--ds-accent-ui);
  box-shadow:var(--ds-focus-ring);
  outline:none;
}
.form-control:disabled,.form-select:disabled,
.form-control[readonly]{
  background-color:var(--ds-canvas);
  color:var(--ds-muted);
  cursor:not-allowed;
}
.form-control[readonly]{ cursor:default; }

/* Cases, boutons radio, interrupteurs */
.form-check-input{
  width:1.15em; height:1.15em; margin-top:.19em;
  background-color:var(--ds-paper);
  border:1px solid var(--ds-line);
}
.form-check-input:checked{
  background-color:var(--ds-accent-ink);
  border-color:var(--ds-accent-ink);
}
.form-check-input:focus{ border-color:var(--ds-accent-ui); box-shadow:var(--ds-focus-ring); }
.form-check-label{ font-size:var(--ds-t-md); color:var(--ds-ink); }
.form-switch .form-check-input{ width:2.2em; border-radius:var(--ds-r-pill); }
.form-range::-webkit-slider-thumb{ background:var(--ds-accent-ink); }
.form-range::-moz-range-thumb{ background:var(--ds-accent-ink); }

.input-group-text{
  background-color:var(--ds-canvas);
  border-color:var(--ds-line);
  color:var(--ds-muted);
  font-size:var(--ds-t-md);
  border-radius:var(--ds-radius-control);
}

/* Aide sous le champ. Le projet employait quatre conventions
   concurrentes (form-text, small.form-text.text-muted, small.text-muted,
   classes maison) : elles convergent ici. */
.form-text,.ds-field-help{
  font-size:var(--ds-t-sm);
  color:var(--ds-muted);
  margin-top:var(--ds-s1);
}

/* ---------------------------------------------------------------------
   ÉTATS DE VALIDATION
   Ils n'existaient que dans 5 fichiers de l'ERP Éditeur — 16 champs sur
   1 653. Le vocabulaire est ici défini pour toute l'application ; le
   helper PHP champ() (includes/design_system.php) le produit.
   L'état d'erreur de auth/register.php, piloté par [aria-invalid], est
   repris à l'identique pour ne pas casser l'existant.
   --------------------------------------------------------------------- */
.form-control.is-invalid,.form-select.is-invalid,
.form-control[aria-invalid="true"],.form-select[aria-invalid="true"]{
  border-color:var(--ds-danger);
  background-image:none;
  padding-right:var(--ds-s3);
}
.form-control.is-invalid:focus,.form-select.is-invalid:focus,
.form-control[aria-invalid="true"]:focus{
  border-color:var(--ds-danger);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--ds-danger) 22%, transparent);
}
.form-control.is-valid,.form-select.is-valid{
  border-color:var(--ds-success); background-image:none;
}
.form-control.is-valid:focus,.form-select.is-valid:focus{
  border-color:var(--ds-success);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--ds-success) 22%, transparent);
}
.invalid-feedback,.field-error,.ds-field-error{
  display:block;
  font-size:var(--ds-t-sm);
  font-weight:var(--ds-w-medium);
  color:var(--ds-danger);
  margin-top:var(--ds-s1);
}
.invalid-feedback:empty,.ds-field-error:empty{ display:none; }
.valid-feedback{ display:block; font-size:var(--ds-t-sm); color:var(--ds-success);
  margin-top:var(--ds-s1); }
.ds-field{ margin-bottom:var(--ds-s4); }

/* =====================================================================
   2. RIPOSTES NOMMÉES
   ===================================================================== */

/* Avatars — bundle-base.css:717-888 fige rayon ET bordure en !important
   sur des sélecteurs très larges ([class*="avatar"] img, img[width="80"]).
   On garde le cercle, on ne reprend QUE la couleur du filet, pour qu'il
   suive le thème au lieu du gris #e9ecef figé. */
.profile-avatar,.user-avatar,.avatar-sm,.avatar-md,.avatar-lg,
.user-avatar-sm,.user-avatar-md,.user-avatar-lg,
.card .rounded-circle,.card.shadow-sm img.rounded-circle,
.user-list-item img,.connection-item img,.suggestion-item img{
  border-color:var(--ds-line) !important;
}
.profile-avatar:hover,.user-avatar:hover{
  border-color:var(--ds-accent-ui) !important;
}
.avatar-placeholder{
  background:var(--ds-accent-weak) !important;
  color:var(--ds-accent-ink) !important;
}

/* Étoiles de notation — components/badges.css:131 fige #f59e0b, une des
   couleurs qui échouent AA. On la remplace par l'ambre du jeu sémantique. */
.rating-stars .fa-star.text-warning{ color:var(--ds-warning) !important; }

/* =====================================================================
   3. COMPOSANTS CANONIQUES
   ===================================================================== */

/* En-tête de page : fil d'Ariane + titre + actions. N'existait pas. */
.ds-page-head{
  display:flex; flex-wrap:wrap; align-items:flex-start;
  gap:var(--ds-s4); margin-bottom:var(--ds-s6);
}
.ds-page-head__text{ flex:1 1 320px; min-width:0; }
.ds-page-head__title{
  font-family:var(--ds-font-display);
  font-size:var(--ds-t-2xl); font-weight:var(--ds-w-bold);
  line-height:var(--ds-lh-tight); letter-spacing:var(--ds-tracking-tight);
  color:var(--ds-ink); margin:0;
}
.ds-page-head__sub{ color:var(--ds-muted); font-size:var(--ds-t-md); margin-top:var(--ds-s2); }
.ds-page-head__actions{ display:flex; gap:var(--ds-s2); flex-wrap:wrap; }

/* Carte statistique unique.
   Remplace SIX implémentations concurrentes — .kpi-card (défini dans 9
   fichiers avec des valeurs divergentes), .illu-kpi-card, .illu-stat-card,
   .stat-item, .stat-box, .stat-card — dont deux coexistaient sur la même
   page (pages/graphistes/statistiques.php). Les anciennes classes sont
   alignées ici en attendant la reprise du markup. */
.ds-stat,
.kpi-card,.illu-kpi-card,.illu-stat-card,.stat-card,.stat-box{
  display:flex; flex-direction:column; gap:var(--ds-s1);
  padding:var(--ds-s5);
  background:var(--ds-paper);
  border:1px solid var(--ds-line);
  border-radius:var(--ds-radius);
  box-shadow:var(--ds-shadow);
}
.ds-stat__value,.kpi-value,.illu-kpi-value,.stat-value,.stat-number{
  font-family:var(--ds-font-display);
  font-size:var(--ds-t-2xl); font-weight:var(--ds-w-bold);
  line-height:var(--ds-lh-tight); color:var(--ds-ink);
}
.ds-stat__label,.kpi-label,.illu-kpi-label,.stat-label{
  font-size:var(--ds-t-sm); font-weight:var(--ds-w-medium);
  color:var(--ds-muted);
  letter-spacing:var(--ds-tracking-wide); text-transform:uppercase;
}
.ds-stat__icon,.kpi-icon{
  width:36px; height:36px; border-radius:var(--ds-radius-control);
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--ds-accent-weak); color:var(--ds-accent-ink);
  font-size:var(--ds-t-base);
}

/* État vide. La version canonique existe (components/empty-state.css)
   mais n'est chargée que sur 38 pages : d'où les réécritures en dur dans
   network.css:220, rentabilite.css:693, recherche.css:575. */
.ds-empty,.empty-state{
  display:flex; flex-direction:column; align-items:center; text-align:center;
  gap:var(--ds-s3); padding:var(--ds-s9) var(--ds-s5);
  color:var(--ds-muted);
  background:var(--ds-paper);
  border:1px dashed var(--ds-line);
  border-radius:var(--ds-radius);
}
.ds-empty__icon,.empty-state i{ font-size:var(--ds-t-3xl); color:var(--ds-accent-line); }
.ds-empty__title,.empty-state h3,.empty-state h4{
  font-family:var(--ds-font-display); font-size:var(--ds-t-lg);
  font-weight:var(--ds-w-semi); color:var(--ds-ink); margin:0;
}

/* Squelettes de chargement. Le projet n'avait que 54 spinners, aucun
   squelette — or sur une liste, le squelette dit ce qui arrive. */
.ds-skeleton{
  background:linear-gradient(90deg,
    var(--ds-sunken) 25%, var(--ds-line-soft) 37%, var(--ds-sunken) 63%);
  background-size:400% 100%;
  border-radius:var(--ds-r-xs);
  animation:ds-shimmer 1.4s ease infinite;
}
.ds-skeleton--text{ height:.85em; margin:.35em 0; }
.ds-skeleton--title{ height:1.4em; width:45%; }
.ds-skeleton--avatar{ width:40px; height:40px; border-radius:50%; }
.ds-skeleton--card{ height:120px; border-radius:var(--ds-radius); }
@keyframes ds-shimmer{ from{background-position:100% 50%} to{background-position:0 50%} }
@media (prefers-reduced-motion:reduce){ .ds-skeleton{ animation:none; } }

/* Groupe segmenté — remplace les cinq systèmes d'onglets maison. */
.ds-segmented{
  display:inline-flex; gap:var(--ds-s1); padding:var(--ds-s1);
  background:var(--ds-sunken); border:1px solid var(--ds-line);
  /* suit la saveur : capsule pour la plupart, angles nets pour le rail (3) */
  border-radius:calc(var(--ds-radius-control) + var(--ds-s1));
}
.ds-segmented > *{
  border:0; background:transparent; cursor:pointer;
  padding:var(--ds-s2) var(--ds-s4);
  border-radius:var(--ds-radius-control);
  font-size:var(--ds-t-md); font-weight:var(--ds-w-medium);
  color:var(--ds-muted);
  transition:background var(--ds-dur-fast) var(--ds-ease), color var(--ds-dur-fast) var(--ds-ease);
}
.ds-segmented > [aria-pressed="true"],.ds-segmented > .is-active{
  background:var(--ds-paper); color:var(--ds-ink);
  font-weight:var(--ds-w-semi); box-shadow:var(--ds-e1);
}

/* Mesure de lecture — appliquée par les saveurs 4 et 6 uniquement. */
.main-content > .container,.main-content > .container-fluid,
#main-content > .container,#main-content > .container-fluid{
  max-width:var(--ds-measure, none);
}


/* ===== _ds-flavors.css (203 lignes) ===== */
/* =====================================================================
   _ds-flavors.css — Les 9 saveurs
   ---------------------------------------------------------------------
   Chaque utilisateur choisit sa barre de navigation parmi 9 ; les pages
   en héritent la personnalité. Sélection par le marqueur émis en pied de
   page (includes/design_system.php), lu en pur CSS via :has() — même
   mécanisme que le contrat de géométrie des navbars, en production
   depuis la v1.2. Aucun JavaScript, donc aucun clignotement au chargement.

   D'où viennent ces valeurs : de la mesure des 9 fichiers de navbar, pas
   d'une intention. Quatre styles (3, 5, 7, 8) ont une personnalité
   visuelle réellement propre. Trois (1, 2, 6) n'en avaient AUCUNE — ils
   ne déclarent pas une seule variable locale — leur saveur est donc
   composée à partir du seul trait mesurable qui les distingue, sans quoi
   trois utilisateurs sur neuf ne verraient aucune différence.
   Le style 0 est l'anomalie : chez lui l'accent n'est pas une teinte,
   c'est LE FOND.

   Note : la police de texte reste 'Hanken Grotesk' pour les neuf. Le
   'Segoe UI' de la navbar Classique n'est pas un choix de design, c'est
   la police par défaut du système ; la reproduire n'apporterait rien.
   Seule la police d'AFFICHAGE varie (saveur 5).
   ===================================================================== */

/* ---------------------------------------------------------------------
   0 — Classique : l'accent est le fond, l'ombre est teintée par le rôle
   --------------------------------------------------------------------- */
body:has(#ds-root[data-ds-flavor="0"]){
  /* Police de la navbar Classique elle-meme (bundle-feed-shell.css:126/156). */
  --ds-font-text:'Segoe UI',system-ui,-apple-system,sans-serif;
  --ds-radius:var(--ds-r-sm);
  --ds-radius-control:6px;
  --ds-control-h:44px;
  --ds-shadow:0 2px 10px color-mix(in srgb, var(--ds-accent) 15%, transparent);
  --ds-shadow-raised:0 6px 18px color-mix(in srgb, var(--ds-accent) 20%, transparent);
}
body:has(#ds-root[data-ds-flavor="0"]) .ds-page-head{
  padding:var(--ds-s5) var(--ds-s6);
  border-radius:var(--ds-radius);
  background:linear-gradient(to right,
    color-mix(in srgb, var(--ds-accent) 22%, #fff) 0%,
    var(--ds-paper) 72%);
  border:1px solid var(--ds-line);
}

/* ---------------------------------------------------------------------
   1 — App-native : capsules, teinte, dégradé descendant
   --------------------------------------------------------------------- */
body:has(#ds-root[data-ds-flavor="1"]){
  --ds-radius:var(--ds-r-md);
  --ds-radius-control:var(--ds-r-pill);
  --ds-control-h:40px;
  --ds-shadow:var(--ds-e1);
  --ds-accent-grad:linear-gradient(135deg, var(--ds-accent), var(--ds-accent-ink));
}
body:has(#ds-root[data-ds-flavor="1"]) .ds-stat__icon,
body:has(#ds-root[data-ds-flavor="1"]) .kpi-icon{
  background:var(--ds-accent-grad); color:#fff;
}

/* ---------------------------------------------------------------------
   2 — Command : cibles de 44 px, champs en creux, accent en bordure
   --------------------------------------------------------------------- */
body:has(#ds-root[data-ds-flavor="2"]){
  --ds-radius:var(--ds-r-lg);
  --ds-radius-control:var(--ds-r-pill);
  --ds-control-h:44px;
  --ds-shadow:var(--ds-e2);
}
body:has(#ds-root[data-ds-flavor="2"]) .form-control,
body:has(#ds-root[data-ds-flavor="2"]) .form-select{
  background-color:var(--ds-canvas);
}
body:has(#ds-root[data-ds-flavor="2"]) .form-control:hover,
body:has(#ds-root[data-ds-flavor="2"]) .form-select:hover{
  border-color:var(--ds-accent-ui); background-color:var(--ds-paper);
}
body:has(#ds-root[data-ds-flavor="2"]) .ds-stat__label,
body:has(#ds-root[data-ds-flavor="2"]) .kpi-label{ font-family:var(--ds-font-mono); }

/* ---------------------------------------------------------------------
   3 — Rail : aucune capsule, aucune ombre, filet vertical, compact
   --------------------------------------------------------------------- */
body:has(#ds-root[data-ds-flavor="3"]){
  --ds-radius:var(--ds-r-md);
  --ds-radius-control:var(--ds-r-sm);   /* zéro 999px dans tout style_3.css */
  --ds-control-h:36px;
  --ds-shadow:none;
  --ds-line:#eceaf2;
}
body:has(#ds-root[data-ds-flavor="3"]) .card,
body:has(#ds-root[data-ds-flavor="3"]) .ds-stat,
body:has(#ds-root[data-ds-flavor="3"]) .kpi-card{
  box-shadow:none; border-color:var(--ds-line);
}
body:has(#ds-root[data-ds-flavor="3"]) .nav-pills .nav-link.active,
body:has(#ds-root[data-ds-flavor="3"]) .list-group-item.active{
  border-radius:0 var(--ds-r-xs) var(--ds-r-xs) 0;
  border-left:3px solid var(--ds-accent-ui);
}

/* ---------------------------------------------------------------------
   4 — Mega-menu : anneau d'accent, panneaux larges
   --------------------------------------------------------------------- */
body:has(#ds-root[data-ds-flavor="4"]){
  --ds-radius:var(--ds-r-md);
  --ds-radius-control:var(--ds-r-pill);
  --ds-control-h:40px;
  --ds-shadow:var(--ds-e2);
  --ds-measure:1120px;
}
body:has(#ds-root[data-ds-flavor="4"]) .nav-pills .nav-link.active,
body:has(#ds-root[data-ds-flavor="4"]) .ds-segmented > [aria-pressed="true"]{
  box-shadow:0 0 0 2px var(--ds-accent-ui);
}
body:has(#ds-root[data-ds-flavor="4"]) .card-header{
  border-bottom-color:var(--ds-accent-line);
}

/* ---------------------------------------------------------------------
   5 — Context bar : serif d'affichage + bande teintée persistante
   --------------------------------------------------------------------- */
body:has(#ds-root[data-ds-flavor="5"]){
  --ds-radius:var(--ds-r-lg);
  --ds-radius-control:var(--ds-r-pill);
  --ds-control-h:40px;
  --ds-shadow:0 1px 0 var(--ds-line);
  /* Le serif 'Fraunces' a ete RETIRE le 04/08 sur demande : la navbar 5 ne
     l'emploie que sur sa marque, qui est un logo en image en production —
     l'utilisateur ne le voit donc jamais dans sa barre, et le voir surgir sur
     les titres de page donnait deux polices a l'ecran. La regle est desormais
     : une seule police, celle de la barre. Pour le retablir, il suffit de
     redeclarer --ds-font-display ici. */
}
body:has(#ds-root[data-ds-flavor="5"]) .ds-page-head{
  padding:var(--ds-s5) var(--ds-s6);
  background:var(--ds-accent-weak);
  border-radius:var(--ds-radius);
}
body:has(#ds-root[data-ds-flavor="5"]) .ds-page-head__sub{ color:var(--ds-accent-ink); }
body:has(#ds-root[data-ds-flavor="5"]) .nav-pills .nav-link.active{
  background:var(--ds-accent-ink); color:#fff;
}

/* ---------------------------------------------------------------------
   6 — Workspace : mesure bridée, creux/actif, titres en encre d'accent
   --------------------------------------------------------------------- */
body:has(#ds-root[data-ds-flavor="6"]){
  --ds-radius:var(--ds-r-md);
  --ds-radius-control:var(--ds-r-pill);
  --ds-control-h:40px;
  --ds-shadow:var(--ds-e1);
  --ds-measure:920px;
}
body:has(#ds-root[data-ds-flavor="6"]) .ds-page-head__title,
body:has(#ds-root[data-ds-flavor="6"]) .card-header{ color:var(--ds-accent-ink); }
body:has(#ds-root[data-ds-flavor="6"]) .card:hover{
  border-color:var(--ds-accent-line);
}
body:has(#ds-root[data-ds-flavor="6"]) .ds-segmented > [aria-pressed="true"]{
  box-shadow:var(--ds-e3);
}

/* ---------------------------------------------------------------------
   7 — Speed-dial : dégradé INVERSÉ (clair vers accent), petits objets ombrés
   --------------------------------------------------------------------- */
body:has(#ds-root[data-ds-flavor="7"]){
  --ds-radius:var(--ds-r-lg);
  --ds-radius-control:var(--ds-r-pill);
  --ds-control-h:40px;
  --ds-shadow:var(--ds-e2);
  --ds-accent-grad:linear-gradient(135deg,
    color-mix(in srgb, var(--ds-accent) 82%, #fff), var(--ds-accent));
}
body:has(#ds-root[data-ds-flavor="7"]) .ds-page-head__title{ color:var(--ds-accent-ink); }
body:has(#ds-root[data-ds-flavor="7"]) .badge{ box-shadow:var(--ds-e1); }
body:has(#ds-root[data-ds-flavor="7"]) .ds-stat__icon,
body:has(#ds-root[data-ds-flavor="7"]) .kpi-icon{
  background:var(--ds-accent-grad); color:var(--ds-accent-ink);
}

/* ---------------------------------------------------------------------
   8 — Dock : rayon 24 px, gros objets, bascule vers l'aplat plein
   --------------------------------------------------------------------- */
body:has(#ds-root[data-ds-flavor="8"]){
  --ds-radius:var(--ds-r-xl);
  --ds-radius-control:var(--ds-r-md);
  --ds-control-h:48px;
  --ds-shadow:var(--ds-e3);
}
body:has(#ds-root[data-ds-flavor="8"]) .ds-stat__icon,
body:has(#ds-root[data-ds-flavor="8"]) .kpi-icon{ width:44px; height:44px; font-size:var(--ds-t-lg); }
body:has(#ds-root[data-ds-flavor="8"]) .card{ transition:transform var(--ds-dur) var(--ds-ease); }
body:has(#ds-root[data-ds-flavor="8"]) .card:hover{ transform:translateY(-3px); }
body:has(#ds-root[data-ds-flavor="8"]) .nav-pills .nav-link:hover,
body:has(#ds-root[data-ds-flavor="8"]) .list-group-item-action:hover{
  background:var(--ds-accent-ink); color:#fff;
}

/* ---------------------------------------------------------------------
   Repli : si le marqueur manque (page hors shell), les valeurs par défaut
   de _ds-tokens.css s'appliquent telles quelles. Rien à faire ici.
   --------------------------------------------------------------------- */

