/* Copilote — Chris (Fixer & Videographer)
   Thème clair (2026-08-02) - remplace le thème sombre du 2026-07-27.
   Alexandre : "make everything clear the interface. more white more
   visible" (confirmé : bascule complète, pas juste plus de contraste dans le
   thème sombre). Structure des tokens inchangée : toute page qui utilise
   var(--accent) etc. hérite automatiquement, aucune couleur à retoucher page
   par page - seul ce bloc :root change.
   Tous les tons de texte/accent/statut ci-dessous ont été recalculés (pas
   juste inversés) pour repasser le contraste WCAG AA (4.5:1) en tant que
   TEXTE sur fond clair - plusieurs couleurs de l'ancien thème sombre (accent
   bleu, teal, vert/violet/rose/orange des market-slot) étaient choisies pour
   rester lisibles en CLAIR SUR FONCÉ et échouent nettement une fois utilisées
   en foncé sur blanc (ex. l'ancien accent #5b93f5 en texte blanc sur lui-même
   ne fait que 3.0:1) - une bascule de thème n'est pas qu'une inversion de
   fond/texte, chaque teinte doit repasser le test dans le nouveau sens. */

:root {
  --page-bg: #f4f5f7;
  --panel-bg: #ffffff;
  --raised-bg: #eef0f3;
  --surface-1: #ffffff;
  --text-primary: #14161c;
  --text-secondary: #3f4353;
  /* #5e6373 : passe 4.5:1 sur les TROIS fonds de l'appli (le pire cas est
     --raised-bg, le plus foncé des trois tons clairs, à 4.19:1 avant ajustement
     - même méthodologie que le fix du thème sombre du 2026-08-01, juste
     réappliquée dans l'autre sens). */
  --text-muted: #5e6373;
  --gridline: #e4e6ec;
  --border: rgba(20, 22, 28, 0.10);
  --baseline: #aeb2c0;

  /* #2f5fc4 : l'ancien bleu #5b93f5 assombri jusqu'à repasser 4.5:1 en texte
     blanc sur lui-même (boutons .btn-send) ET en texte bleu sur blanc (liens,
     nav active) - les deux usages coexistent dans ce fichier, une seule
     teinte doit satisfaire les deux sens. */
  --accent: #2f5fc4;
  --accent-ink: #ffffff;
  --accent-2: #0d7d72;
  --series-dedicated: #0a7a4d;
  --series-outsourcing: #2f5fc4;

  --market-slot-1: #2f5fc4;
  --market-slot-2: #0a7a4d;
  --market-slot-3: #9c6208;
  --market-slot-4: #6d4fd1;
  --market-slot-5: #c22a7c;
  --market-slot-6: #b6510a;

  /* --good reste un ton "point/bordure" plus vif (pas de règle de contraste
     texte à tenir, cf usages : dots, box-shadow, border-left) ;
     --good-text est la variante assombrie utilisée partout où la couleur
     porte du texte (même distinction que dans l'ancien thème sombre,
     recalculée dans l'autre sens). --critical et --warn portent du texte
     directement (pas de variante -text séparée) donc doivent eux-mêmes
     passer 4.5:1. */
  --good: #16a34a;
  --good-text: #0a7a4d;
  --critical: #c92a35;
  --warn: #a85a12;

  /* Violet réservé exclusivement à "ce que l'IA a fait/raisonné" (2026-07-29,
     convention reprise d'Insik OS/Alexandre : "violet = tout ce qui est
     automatique... rien d'autre n'est violet") - distinct de
     --market-slot-4 (même famille de teinte mais déjà utilisée pour un
     marché/segment, sans rapport avec le raisonnement IA). */
  --ai: #6d4fd1;
  --ai-soft: rgba(109, 79, 209, 0.08);

  --radius: 12px;

  /* Échelle typographique (2026-08-01) — le fichier avait dérivé vers 22
     tailles distinctes, dont un empilement quasi indistinguable entre 11.5px
     et 13.5px utilisé 106 fois à travers des dizaines de composants (même
     rôle "texte secondaire/meta", juste tapé différemment au fil des sessions
     - même anti-pattern que le cas fondateur documenté par l'outil de design
     "Impeccable" utilisé pour cet audit). Toutes les déclarations existantes
     référencent maintenant ces 7 tokens (migré le jour même) ; nouveau CSS
     doit choisir un rôle ici plutôt qu'inventer une nouvelle valeur px.

     Valeurs relevées le même jour ("so small... hard to navigate") - la
     consolidation avait corrigé le désordre mais pas la taille de base :
     +1 à +2px par palier, un ajustement qui se voit sans devenir un thème
     "grand texte" séparé. Un seul changement ici suffit pour toute l'appli.
     --fs-micro  11px : icône/label le plus petit (nav-icon, format-label)
     --fs-label  12px : badge, tag, footnote, donnée tabulaire courte
     --fs-meta   13.5px : texte secondaire par défaut - le rôle le plus courant
     --fs-title  14.5px : libellé de carte en gras (distinct d'un nom d'entité)
     --fs-name   15px : nom d'entité en gras (prospect/client/devis), valeur par défaut
     --fs-lead   16px : question/prompt, sous-titre de section
     --fs-heading 18px : titre de panneau/section
     Au-delà (18-34px) : titres de marque, chiffres clés - assez rares et déjà
     bien différenciés pour rester en valeurs directes plutôt qu'en token. */
  --fs-micro: 11px;
  --fs-label: 12px;
  --fs-meta: 13.5px;
  --fs-title: 14.5px;
  --fs-name: 15px;
  --fs-lead: 16px;
  --fs-heading: 18px;
}

* { box-sizing: border-box; }

/* 2026-08-15, Alexandre : "make the client journey guided... not by text, by
   animation and where the client land automatically" - toute navigation par
   ancre sur cette appli (tuiles stat-row/parcours de /flux qui sautent vers
   #flux-hot, #flux-followup...) défile maintenant en douceur au lieu d'un
   saut instantané - le déplacement lui-même EST le signal "voilà où je
   t'emmène", pas un texte à lire. Coupé si l'utilisateur a demandé moins de
   mouvement (accessibilité, jamais outrepassé). */
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

/* Pulse d'arrivée (2026-08-15, même demande) : :target matche automatiquement
   l'élément dont l'id est dans l'URL au moment de l'atterrissage - un cadre
   accent qui s'estompe confirme "tu es bien arrivé ici", sans JS, sans texte
   à lire. Réutilisable par n'importe quelle ancre de la page (#flux-hot,
   #flux-followup...), pas seulement celles posées aujourd'hui. */
:target { animation: flux-target-arrive 1.6s ease-out; }
@keyframes flux-target-arrive {
  0% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 45%, transparent); border-radius: 10px; }
  100% { box-shadow: 0 0 0 3px transparent; border-radius: 10px; }
}
@media (prefers-reduced-motion: reduce) { :target { animation: none; } }

/* Base pour tout input/textarea/select SANS style explicite (beaucoup de
   formulaires du Copilote sont écrits avec un style inline qui ne fixe que
   padding/taille, jamais fond/couleur - ça ne se voyait pas sur l'ancien
   thème clair, où le blanc par défaut du navigateur passait inaperçu sur un
   fond déjà clair. Sur fond sombre, ce même défaut ressort comme un pavé
   blanc cru. Règle de base ici plutôt qu'un correctif dans chaque template :
   toute règle plus spécifique (ex. .search-form input) continue de gagner. */
input, textarea, select {
  background: var(--raised-bg);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: 8px;
}
input::placeholder, textarea::placeholder { color: var(--text-muted); }

body {
  margin: 0;
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  background: var(--page-bg);
  color: var(--text-primary);
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }
button { font-family: inherit; }

/* ---- Shell layout : sidebar + main, pas de topbar générique ---- */
body.shell { display: flex; min-height: 100vh; }

.sidebar {
  flex: 0 0 232px;
  background: var(--panel-bg);
  border-right: 1px solid var(--border);
  padding: 28px 22px;
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
}
/* 2026-08-14, audit UX/accès - tiroir mobile (cf @media 900px plus bas et
   base.html) : sur bureau, .sidebar-top ne montre que la marque (le bouton
   ci-dessous reste display:none) et .sidebar-nav-wrap se comporte comme s'il
   n'existait pas dans l'arbre (display:contents) - la nav garde exactement
   le même flux vertical qu'avant l'ajout de ce wrapper. */
.sidebar-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.sidebar-toggle {
  display: none;
  all: unset; box-sizing: border-box; cursor: pointer;
  width: 36px; height: 36px; border-radius: 8px;
  align-items: center; justify-content: center;
  font-size: 17px; color: var(--text-secondary);
  background: var(--raised-bg); flex: none;
}
.sidebar-toggle:hover { color: var(--text-primary); }
.sidebar-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.sidebar-nav-wrap { display: contents; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

.brand { display: flex; align-items: center; gap: 9px; }
.brand-dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 20%, transparent);
}
.brand-word { font-weight: 900; font-size: 19px; letter-spacing: -0.02em; }
.brand-sub { font-size: var(--fs-meta); color: var(--text-muted); margin-top: 4px; margin-left: 18px; }

.side-nav { display: flex; flex-direction: column; gap: 3px; margin-top: 10px; }
.side-nav a {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 10px 11px;
  border-radius: 8px;
  font-weight: 700;
  font-size: var(--fs-name);
  color: var(--text-secondary);
  background: var(--raised-bg);
  border-left: 2px solid transparent;
}
.nav-icon { font-size: var(--fs-micro); color: var(--text-muted); }
.side-nav a.active { color: var(--text-primary); background: color-mix(in srgb, var(--accent) 16%, var(--raised-bg)); border-left-color: var(--accent); }
.side-nav a.active .nav-icon { color: var(--accent); }
.side-nav a:hover:not(.active) { color: var(--text-primary); background: var(--gridline); }
/* Badge de compte dans la barre laterale (ex. sequences en brouillon a
   relire) - savoir ou cliquer avant meme d'ouvrir la page. */
/* Couleurs d'urgence (2026-07-29, convention Insik OS) : rouge = à traiter
   maintenant, orange = action du jour en attente, bleu (défaut) = en cours
   pas urgent, gris = compteur informatif. */
.nav-badge-red { background: var(--critical); color: var(--accent-ink); }
.nav-badge-orange { background: var(--warn); color: var(--accent-ink); }
.nav-badge-gray { background: var(--raised-bg); color: var(--text-muted); }
.nav-badge {
  margin-left: auto;
  min-width: 18px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--fs-label);
  font-weight: 800;
  text-align: center;
  line-height: 1.5;
}

.side-section-label {
  margin-top: 20px;
  font-size: var(--fs-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  padding: 0 2px 8px;
}
.side-section-label:first-of-type { margin-top: 26px; }
.track-filter { display: flex; flex-direction: column; gap: 1px; margin-top: 10px; }
.track-opt {
  padding: 7px 10px;
  border-radius: 8px;
  font-size: var(--fs-title);
  font-weight: 600;
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  gap: 8px;
}
.track-opt::before {
  content: "";
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--text-muted);
}
.track-opt-videographer::before { background: var(--series-outsourcing); }
.track-opt-fixer::before { background: var(--series-dedicated); }
.track-opt.active { background: var(--raised-bg); color: var(--text-primary); }
.track-opt:hover:not(.active) { color: var(--text-primary); }

.logout { margin-top: auto; font-size: var(--fs-meta); color: var(--text-muted); font-weight: 600; padding: 10px; }
.logout:hover { color: var(--text-primary); }

.page { flex: 1; padding: 40px 44px 64px; min-width: 0; }
.page-noauth { padding: 0; }

/* ---- Login ---- */
.login-screen { min-height: 100vh; display: flex; align-items: center; justify-content: center; background: var(--page-bg); }
.login-card {
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 44px 38px;
  width: 320px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.login-mark { font-weight: 900; font-size: 22px; letter-spacing: -0.02em; display: flex; align-items: center; gap: 9px; }
.login-sub { margin: -8px 0 6px 18px; color: var(--text-secondary); font-size: var(--fs-meta); }
.login-error { color: var(--critical); font-size: var(--fs-meta); margin: 0; }
/* 2026-08-13 (audit "check everything... needs to be perfect") : plusieurs
   routes POST (édition de gabarit, statut, portfolio, mots-clés signaux)
   avalaient une ValueError de validation sans jamais le dire à l'écran -
   Chris pouvait vider un champ, cliquer "Enregistrer", et rien ne se
   passait sans le moindre indice pourquoi. Même ton que .login-error
   (déjà établi), nom générique pour être réutilisable partout où un
   formulaire redirige après un échec de validation côté serveur. */
.save-error { color: var(--critical); font-size: var(--fs-meta); font-weight: 600; margin: 0 0 14px; }
.login-card input {
  padding: 12px 14px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--raised-bg);
  color: var(--text-primary);
  font-size: var(--fs-name);
}
.login-card button {
  padding: 12px 14px;
  border-radius: 10px;
  border: none;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 700;
  font-size: var(--fs-name);
  cursor: pointer;
}

/* ---- Flux d'intégrations : rend visible ce à quoi le système est branché ---- */
.flow-panel {
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 26px 28px 30px;
  margin-bottom: 36px;
}
.flow-panel-head { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 26px; gap: 20px; flex-wrap: wrap; }
.flow-title { font-size: var(--fs-heading); font-weight: 800; letter-spacing: -0.01em; }
.flow-sub { font-size: var(--fs-meta); color: var(--text-secondary); margin-top: 4px; }

.mode-badge {
  display: flex; align-items: center; gap: 7px;
  font-size: var(--fs-label); font-weight: 800; letter-spacing: 0.05em;
  padding: 6px 12px; border-radius: 999px;
  white-space: nowrap;
}
.mode-dot { width: 7px; height: 7px; border-radius: 50%; }
.mode-dry { background: color-mix(in srgb, var(--market-slot-3) 16%, transparent); color: var(--market-slot-3); }
.mode-dry .mode-dot { background: var(--market-slot-3); }
.mode-live { background: color-mix(in srgb, var(--good) 18%, transparent); color: var(--good-text); }
.mode-live .mode-dot { background: var(--good); box-shadow: 0 0 0 4px color-mix(in srgb, var(--good) 25%, transparent); }

.flow-diagram { display: flex; align-items: stretch; overflow-x: auto; padding-bottom: 4px; }
.flow-node {
  flex: 0 0 150px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 3px;
}
.flow-node-icon {
  width: 44px; height: 44px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-heading);
  background: var(--raised-bg);
  border: 1.5px solid var(--border);
  color: var(--text-muted);
  margin-bottom: 8px;
}
.flow-node.connected .flow-node-icon {
  color: var(--accent);
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, var(--raised-bg));
}
.flow-node-label { font-size: var(--fs-title); font-weight: 800; }
.flow-node-sub { font-size: var(--fs-label); color: var(--text-muted); }
.flow-node-metric { font-size: var(--fs-label); color: var(--text-secondary); font-weight: 600; margin-top: 6px; font-variant-numeric: tabular-nums; }
.flow-node-status { display: flex; align-items: center; gap: 5px; margin-top: 8px; font-size: var(--fs-label); font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-muted); }
.status-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--text-muted); }
.flow-node.connected .flow-node-status { color: var(--good-text); }
.flow-node.connected .status-dot { background: var(--good); box-shadow: 0 0 0 3px color-mix(in srgb, var(--good) 25%, transparent); }
.flow-node.pending .flow-node-icon { border-style: dashed; }

.flow-connector {
  flex: 1 1 32px;
  min-width: 24px;
  align-self: center;
  height: 1.5px;
  background: var(--gridline);
  margin: 0 2px 34px;
  position: relative;
}
.flow-connector::after {
  content: "";
  position: absolute; right: 0; top: 50%;
  width: 5px; height: 5px;
  border-right: 1.5px solid var(--gridline);
  border-bottom: 1.5px solid var(--gridline);
  transform: translateY(-50%) rotate(-45deg);
}

/* ---- Stat strip : plate, pas de boîtes bordées, séparateurs fins ---- */
.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 14px;
  margin-bottom: 40px;
}
.stat-tile {
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  padding: 18px 20px;
}
.stat-tile.stat-good { border-left-color: var(--good); }
.stat-tile.stat-muted { border-left-color: var(--text-muted); }
/* 2026-08-12, critique impeccable P2 : un solde API épuisé bloque le
   pipeline (exactement le scénario vécu le 2026-08-09/10, plusieurs heures
   à chercher un repli faute de ce signal) - --critical partout ailleurs
   dans l'app signale "agir maintenant", ce tuile-là ne l'utilisait jamais. */
.stat-tile.stat-critical { border-left-color: var(--critical); }
.stat-critical .stat-value { color: var(--critical); }
/* 2026-08-13, flux.html::"Emails trouvés (7j)" - un taux bas mérite d'attirer
   l'œil sans crier "agir maintenant" comme --critical (rien n'est bloqué,
   juste une piste à garder à l'œil) - même ton que .quote-badge-final. */
.stat-tile.stat-warn { border-left-color: var(--warn); }
.stat-warn .stat-value { color: var(--warn); }
.stat-label { font-size: var(--fs-label); color: var(--text-muted); font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; }
.stat-value { font-size: 34px; font-weight: 800; margin-top: 8px; letter-spacing: -0.03em; line-height: 1; }
.stat-good .stat-value { color: var(--good-text); }
.stat-muted .stat-value { color: var(--text-secondary); }
.stat-footnote { font-size: var(--fs-label); color: var(--text-muted); margin-top: 6px; }
/* 2026-08-13, flux.html - pile d'alertes système (solde API bas, source de
   signaux en échec...) : réutilise .stat-tile.stat-critical (déjà le ton
   "agir maintenant" de l'app) mais en pleine largeur et empilées, pas dans
   la grille de métriques - un message texte, pas un nombre. */
.flux-alert-stack { display: flex; flex-direction: column; gap: 10px; margin-bottom: 24px; }
.flux-alert-tile { padding: 14px 20px; }
.flux-alert-message { font-size: var(--fs-meta); font-weight: 600; margin-top: 4px; letter-spacing: normal; line-height: 1.5; }
.stat-denom { font-size: 20px; font-weight: 500; color: var(--text-muted); }

/* ---- Contenu : file de brouillons de posts LinkedIn ---- */
.content-grid-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: 16px; }

/* Défilement horizontal (2026-08-06, Alexandre : "en gardant les
   brouillons prêts en dessous que l'on peut scroller... de gauche à
   droite", puis "plus sleek... on peut pas faire défiler... ca marche
   pas" + "cool si ca défile automatiquement et on l'arrête") - remplace
   .content-grid-cards pour cette liste précise, demande explicite ; le
   reste du Copilote continue d'empiler verticalement.

   Le "ça ne marche pas" du deuxième retour est un piège connu : une
   molette de souris classique n'envoie que du deltaY (vertical), un
   conteneur overflow-x seul l'ignore totalement - JS ci-dessous
   (contenu.html) traduit deltaY en scrollLeft, plus un vrai glisser-
   déposer à la souris. Barre de défilement native masquée (scrollbar-
   width:none) au profit d'un fondu aux bords (mask-image) - signal visuel
   "il y a plus à voir" plus sobre qu'une barre, cohérent avec le
   défilement automatique ajouté par-dessus (cf JS : ping-pong, pause au
   survol/glisser/clic, respecte prefers-reduced-motion). */
.content-hscroll-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 4px; }
.hscroll-toggle {
  all: unset; box-sizing: border-box; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 11px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--panel-bg); color: var(--text-secondary); font-size: var(--fs-label); font-weight: 700;
  transition: border-color .12s ease, color .12s ease;
}
.hscroll-toggle:hover { border-color: var(--accent); color: var(--accent); }
.hscroll-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.hscroll-toggle-icon { width: 13px; height: 13px; }

.content-hscroll-wrap { position: relative; }
.content-hscroll {
  display: flex; gap: 16px; overflow-x: auto; cursor: grab;
  scrollbar-width: none;
  -webkit-mask-image: linear-gradient(to right, transparent, black 20px, black calc(100% - 20px), transparent);
  mask-image: linear-gradient(to right, transparent, black 20px, black calc(100% - 20px), transparent);
}
.content-hscroll.is-dragging { cursor: grabbing; user-select: none; scroll-behavior: auto; }
.content-hscroll::-webkit-scrollbar { display: none; }
.content-hscroll > .content-card { flex: 0 0 360px; }
@media (max-width: 640px) {
  .content-hscroll > .content-card { flex-basis: 88vw; }
}
.content-card {
  background: var(--raised-bg);
  border-left: 3px solid var(--market-slot-1);
  border-radius: 8px;
  padding: 18px 20px;
}
.content-card-slot-1 { border-left-color: var(--market-slot-1); }
.content-card-slot-2 { border-left-color: var(--market-slot-2); }
.content-card-slot-3 { border-left-color: var(--market-slot-3); }
.content-card-slot-4 { border-left-color: var(--market-slot-4); }
.content-card-slot-5 { border-left-color: var(--market-slot-5); }
.content-card-slot-6 { border-left-color: var(--market-slot-6); }
.content-card-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 12px; }
.content-card-label { font-weight: 800; font-size: var(--fs-title); }
.content-copy-btn {
  padding: 5px 11px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--panel-bg); color: var(--text-secondary); font-size: var(--fs-label); font-weight: 700;
  cursor: pointer; font-family: inherit; transition: all .12s ease;
}
.content-copy-btn:hover { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.content-hook { font-weight: 700; font-size: var(--fs-title); margin-bottom: 8px; line-height: 1.4; }
.content-body { font-size: var(--fs-meta); line-height: 1.6; color: var(--text-secondary); margin: 0; white-space: pre-line; }
.content-price {
  margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border);
  font-size: var(--fs-meta); font-weight: 700; color: var(--good-text);
}
.content-last-posted { margin-top: 8px; font-size: var(--fs-label); color: var(--text-muted); }
.carousel-block {
  margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--border);
}
.carousel-block summary {
  cursor: pointer; font-size: var(--fs-label); font-weight: 700; color: var(--accent);
  list-style: none;
}
.carousel-block summary::-webkit-details-marker { display: none; }
.carousel-block summary::before { content: "▸ "; }
.carousel-block[open] summary::before { content: "▾ "; }
.carousel-slide-list {
  margin: 10px 0; padding-left: 20px; font-size: var(--fs-meta); color: var(--text-secondary); line-height: 1.6;
}
.carousel-slide-list li { margin-bottom: 6px; }

.track-badge { font-size: var(--fs-micro); font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); }
.track-badge-videographer { color: var(--series-outsourcing); }
.track-badge-fixer { color: var(--series-dedicated); }
.btn-good:hover { color: var(--good) !important; }
.btn-muted:hover { color: var(--critical) !important; }

/* ---- Diagnostic ---- */
.panel {
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 28px 30px;
  margin-bottom: 24px;
}
.panel-title { margin: 0; font-size: 18px; font-weight: 800; letter-spacing: -0.01em; }
.panel-sub { margin: 5px 0 24px; font-size: var(--fs-meta); color: var(--text-secondary); }

/* 2026-08-15, Alexandre : "ca doit vraiment etre actualisé tout le temps...
   pour qu'il voit que c'est frais" - avant ce correctif, le seul indicateur
   "mis à jour à l'instant" de la page vivait DANS le schéma technique replié
   par défaut (jamais vu en pratique). Un vrai indicateur visible, toujours en
   haut de page, plus un bouton explicite (pas seulement un poll silencieux en
   fond - un contrôle qu'on peut presser lit comme "c'est vivant" plus qu'un
   automatisme invisible). */
.freshness-row {
  display: flex; align-items: center; gap: 10px; margin-top: 10px;
  font-size: var(--fs-label); color: var(--text-muted);
}
.freshness-dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--good);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--good) 25%, transparent);
  flex: none;
  animation: freshness-pulse 2.4s ease-in-out infinite;
}
@keyframes freshness-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}
@media (prefers-reduced-motion: reduce) { .freshness-dot { animation: none; } }
.freshness-refresh {
  all: unset; box-sizing: border-box; cursor: pointer;
  display: inline-flex; align-items: center; gap: 5px;
  color: var(--accent); font-weight: 700; font-size: var(--fs-label);
  padding: 3px 8px; border-radius: 999px;
  transition: background .12s ease;
}
.freshness-refresh:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.freshness-refresh:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.freshness-refresh .icon { display: inline-block; transition: transform .5s ease; }
.freshness-refresh.is-spinning .icon { transform: rotate(360deg); }

/* Tuiles cliquables du stat-row (2026-08-15, audit "buttons, how information
   is presented") - une tuile <a> et une tuile <div> rendaient identiques,
   aucun moyen de savoir laquelle répond au clic avant d'essayer. Chevron +
   léger relief au survol, jamais sur les tuiles statiques (.stat-tile sans
   ce modificateur). */
.stat-tile { transition: transform .12s ease, box-shadow .12s ease, border-color .12s ease; position: relative; }
.stat-tile-link { text-decoration: none; padding-right: 34px; }
.stat-tile-link::after {
  content: "→"; position: absolute; top: 18px; right: 18px;
  color: var(--text-muted); font-weight: 700; opacity: 0; transform: translateX(-3px);
  transition: opacity .12s ease, transform .12s ease;
}
.stat-tile-link:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 16px -8px rgba(20, 22, 28, 0.18);
  border-color: color-mix(in srgb, var(--accent) 30%, var(--border));
}
.stat-tile-link:hover::after { opacity: 1; transform: translateX(0); }
.stat-tile-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* 2026-08-15, Alexandre : "ca ressemble a plein de rectangles qui se suivent
   les uns apres les autres... be smarter with how the client will navigate
   the information" - avant ce correctif, /flux empilait 5-6 .panel blancs
   identiques (héros, aperçu des lots, réponses, à recontacter, accès rapide)
   à la suite, y compris deux qui CONTIENNENT déjà des cartes bordées-couleur
   (.queue-item) - un panel autour de cartes déjà cadrées est un double
   cadrage, jamais un vrai signal de hiérarchie. Le poids visuel doit suivre
   L'IMPORTANCE, pas "chaque section a sa boîte" : .flux-hero et
   .flux-bare-section posent leur contenu directement sur le fond de page
   (--page-bg, gris clair - contraste réel avec les .panel blancs restants,
   volontairement réservés au contenu secondaire/aperçu comme "Accès rapide"
   et "Lots en attente"). Les .queue-item (déjà bordées par couleur selon le
   type - réponse, relance...) deviennent le vrai point focal de la page
   plutôt que d'être aplaties dans encore un niveau de carte grise. */
.flux-hero {
  margin-bottom: 28px;
}
.flux-hero-title { margin: 0; font-size: 26px; font-weight: 800; letter-spacing: -0.02em; line-height: 1.15; }
.flux-hero-sub { margin: 8px 0 0; font-size: var(--fs-lead); color: var(--text-secondary); }
.flux-bare-section { margin: 36px 0 0; }

/* 2026-08-15, Alexandre : "some bento or something that's connected where
   they can visually understand they need to click on envoi and go through
   them, then they are naturally led to réponses à traiter, and then à
   recontacter... like almost guided. Of course they can directly focus on
   one section in particular" - remplace la partie "étapes de travail" du
   stat-row (Envoi/Réponses/À recontacter/Contenu) par une rangée bento
   connectée par des flèches, plus grande et plus affirmée que les tuiles
   stat-row restantes (Signaux/Emails trouvés/Envoyés, purement
   informatives, cf .stat-row juste en dessous) - la hiérarchie visuelle
   distingue maintenant "ton parcours à faire" de "l'état du système en
   arrière-plan" au lieu de tout aplatir dans la même grille. Chaque étape
   reste un lien direct et indépendant (jamais verrouillée derrière la
   précédente) - la flèche suggère un ordre naturel, ne l'impose pas. */
.flux-journey {
  display: flex; align-items: stretch; flex-wrap: wrap; gap: 10px;
  margin-bottom: 28px;
}
.flux-journey-step {
  flex: 1 1 200px;
  display: flex; align-items: center; gap: 12px;
  background: var(--panel-bg); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 16px 18px; text-decoration: none;
  transition: transform .12s ease, box-shadow .12s ease, border-color .12s ease;
}
.flux-journey-step:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 20px -10px rgba(20, 22, 28, 0.22);
  border-color: color-mix(in srgb, var(--accent) 35%, var(--border));
}
.flux-journey-step:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.flux-journey-icon {
  flex: none; width: 38px; height: 38px; border-radius: 10px;
  display: flex; align-items: center; justify-content: center;
  font-size: 17px; background: color-mix(in srgb, var(--accent) 12%, var(--panel-bg));
  color: var(--accent);
}
.flux-journey-step.is-urgent .flux-journey-icon { background: color-mix(in srgb, var(--critical) 14%, var(--panel-bg)); color: var(--critical); }
.flux-journey-body { min-width: 0; }
.flux-journey-label { display: block; font-size: var(--fs-label); font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); }
.flux-journey-value { display: block; font-size: var(--fs-name); font-weight: 800; color: var(--text-primary); margin-top: 2px; }
.flux-journey-arrow {
  flex: none; align-self: center; color: var(--text-muted); font-size: 16px; font-weight: 700;
  padding: 0 2px;
}
@media (max-width: 720px) {
  .flux-journey-arrow { display: none; }
}

.funnel { display: flex; flex-direction: column; gap: 14px; }
.funnel-row { display: grid; grid-template-columns: 170px 1fr 56px 56px; align-items: center; gap: 14px; }
.funnel-label { font-size: var(--fs-meta); color: var(--text-secondary); font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; }
.funnel-bar-track { background: var(--raised-bg); border-radius: 6px; height: 24px; overflow: hidden; }
.funnel-bar {
  height: 100%; max-height: 24px; border-radius: 6px; min-width: 2px;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  box-shadow: 0 0 12px color-mix(in srgb, var(--accent) 45%, transparent);
}
.funnel-value { font-size: var(--fs-name); font-weight: 800; text-align: right; font-variant-numeric: tabular-nums; }
.funnel-conversion { font-size: var(--fs-meta); color: var(--text-muted); text-align: right; font-variant-numeric: tabular-nums; }

/* ---- Graphique barres horizontales : répartition réelle par secteur ---- */
.hbar-chart { display: flex; flex-direction: column; gap: 12px; }
.hbar-row { display: grid; grid-template-columns: 190px 1fr 40px; align-items: center; gap: 14px; }
.hbar-label { display: flex; align-items: center; gap: 8px; font-size: var(--fs-meta); font-weight: 600; color: var(--text-secondary); }
.hbar-dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; }
.hbar-track { background: var(--raised-bg); border-radius: 6px; height: 18px; overflow: hidden; }
.hbar-fill { height: 100%; border-radius: 6px; min-width: 3px; }
.hbar-value { font-size: var(--fs-meta); font-weight: 800; text-align: right; font-variant-numeric: tabular-nums; }

/* ---- Graphique barres verticales : activité réelle par mois ---- */
.trend-chart { display: flex; align-items: flex-end; gap: 8px; height: 140px; padding-top: 10px; }
.trend-bar-wrap { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; height: 100%; justify-content: flex-end; }
.trend-bar {
  width: 100%; max-width: 34px; border-radius: 5px 5px 0 0; min-height: 3px;
  background: linear-gradient(180deg, var(--accent-2), var(--accent));
}
.trend-count { font-size: var(--fs-label); font-weight: 700; color: var(--text-secondary); }
.trend-label { font-size: var(--fs-micro); color: var(--text-muted); white-space: nowrap; }

.market-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 20px; }
.market-tile { display: flex; align-items: flex-start; gap: 10px; }
.market-dot { width: 10px; height: 10px; border-radius: 50%; margin-top: 5px; flex: 0 0 auto; }
.market-slot-1 { background: var(--market-slot-1); }
.market-slot-2 { background: var(--market-slot-2); }
.market-slot-3 { background: var(--market-slot-3); }
.market-slot-4 { background: var(--market-slot-4); }
.market-slot-5 { background: var(--market-slot-5); }
.market-slot-6 { background: var(--market-slot-6); }
.market-label { font-size: var(--fs-title); font-weight: 700; }
.market-total { font-size: var(--fs-meta); color: var(--text-secondary); margin-top: 2px; }
.market-won { color: var(--good-text); font-weight: 700; margin-left: 6px; }
/* 2026-08-14, audit "improve everything" - taux de rejet/enrichissement par
   marché (dashboard/data.py::market_breakdown()), jusque-là calculés mais
   jamais montrés nulle part. */
.market-meta { font-size: var(--fs-label); color: var(--text-muted); margin-top: 2px; }
.market-meta-warn { color: var(--warn); font-weight: 700; }

.empty-banner {
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: 8px;
  padding: 14px 18px;
  margin-bottom: 24px;
  font-size: var(--fs-meta);
  color: var(--text-secondary);
  line-height: 1.5;
}
.empty-banner strong { color: var(--text-primary); }
.empty-banner a { color: var(--accent); font-weight: 600; text-decoration: underline; }

/* ---- Historique client (section 4 bis) ---- */
.empty-note { color: var(--text-muted); font-size: var(--fs-meta); }

.autre-context {
  background: color-mix(in srgb, var(--accent) 6%, var(--raised-bg));
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 14px 16px;
  margin-bottom: 16px;
}
.autre-context-title { font-weight: 800; font-size: var(--fs-meta); margin-bottom: 5px; }
.autre-context-sub { font-size: var(--fs-meta); color: var(--text-secondary); line-height: 1.5; margin: 0 0 10px; }

.quote-list { display: flex; flex-direction: column; gap: 10px; }
/* .quote-row est un <details> (2026-07-21) : replié par défaut (juste la
   .quote-summary-bar visible), déplié au clic pour voir le brouillon complet
   et les boutons - remplace la liste plate qui devenait illisible avec 380+
   lignes identiques d'un coup. */
.quote-row {
  padding: 12px 18px; border: 1px solid var(--border); border-radius: 12px;
  background: var(--panel-bg); transition: border-color .15s ease, box-shadow .15s ease;
}
.quote-row:hover { border-color: color-mix(in srgb, var(--accent) 35%, var(--border)); box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05); }
.quote-row[open] { padding-bottom: 16px; }
.quote-summary-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  cursor: pointer; list-style: none; padding: 4px 0;
}
.quote-summary-bar::-webkit-details-marker { display: none; }
.quote-summary-bar::before {
  content: "▸"; font-size: var(--fs-label); color: var(--text-muted); flex: 0 0 auto;
  transition: transform .12s ease;
}
.quote-row[open] .quote-summary-bar::before { transform: rotate(90deg); }
.quote-subline-compact {
  font-size: var(--fs-meta); color: var(--text-muted); overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; flex: 1 1 auto; min-width: 60px;
}
.quote-row-body {
  display: flex; justify-content: space-between; align-items: flex-start; gap: 18px;
  margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--gridline);
}
.quote-dormant { background: color-mix(in srgb, var(--good) 5%, var(--panel-bg)); }
.quote-probably-closed { opacity: 0.6; background: var(--panel-bg); }
.quote-probably-closed:hover { opacity: 1; }
/* Neutre, pas --critical (2026-08-12, critique impeccable P2) : "probablement
   clos" veut dire "déprioriser", pas "urgent" - rouge partout ailleurs dans
   l'app (réponses escaladées, nœuds de validation) signale l'inverse, agir
   maintenant. --text-muted est déjà le signal utilisé par .quote-probably-
   closed (opacity réduite) sur la ligne entière, ce badge doit rester cohérent. */
.quote-badge-closed { background: var(--raised-bg); color: var(--text-muted); border-color: var(--border); }
.quote-filters {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px;
  margin: 10px 0 16px; padding: 10px 12px;
  background: var(--raised-bg); border: 1px solid var(--border); border-radius: 8px;
}
.quote-filters select { padding: 6px; font-family: inherit; font-size: var(--fs-meta); border-radius: 6px; border: 1px solid var(--border); background: var(--panel-bg); color: var(--text-primary); }
.quote-filters .amount-range { display: inline-flex; gap: 6px; align-items: center; }
.quote-filters .amount-range input {
  width: 80px; padding: 6px; font-family: inherit; font-size: var(--fs-meta);
  border-radius: 6px; border: 1px solid var(--border); background: var(--panel-bg); color: var(--text-primary);
}
.quote-filters .amount-range-sep { color: var(--text-muted); }
.quote-filters-note { font-size: var(--fs-label); color: var(--text-muted); margin-top: 4px; width: 100%; }
.quote-filters-reset { font-size: var(--fs-label); color: var(--text-muted); text-decoration: underline; }
.quote-relance-reason { font-size: var(--fs-meta); color: var(--critical); font-style: italic; margin: 6px 0 0; }
.quote-info { min-width: 0; flex: 1; }
.quote-main { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.quote-subline { font-size: var(--fs-meta); color: var(--text-muted); margin-top: 3px; }
.quote-summary { font-size: var(--fs-meta); color: var(--text-secondary); line-height: 1.5; margin: 6px 0 0; max-width: 62ch; }
.quote-name { font-weight: 700; font-size: var(--fs-name); }
.quote-company { color: var(--text-secondary); font-size: var(--fs-meta); }
.quote-date {
  font-size: var(--fs-label); font-weight: 700; font-variant-numeric: tabular-nums;
  color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, var(--panel-bg));
  border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent);
  padding: 2px 8px; border-radius: 999px;
}
.quote-amount {
  font-weight: 800; font-size: var(--fs-lead); font-variant-numeric: tabular-nums;
  max-width: 150px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; text-align: right;
}
.quote-dormant .quote-amount { color: var(--good-text); }
.quote-badge {
  font-size: var(--fs-micro); font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em;
  padding: 2px 8px; border-radius: 999px; background: var(--raised-bg); color: var(--text-muted);
  border: 1px solid var(--border);
}
.quote-badge-good { background: color-mix(in srgb, var(--good) 18%, transparent); color: var(--good-text); border-color: transparent; }
/* "Relance finale" (rushs, 3e et dernier passage avant d'arrêter tout seul) -
   avant ce fix la classe existait dans _relance_row.html mais n'était définie
   nulle part, donc rendait avec le badge gris par défaut, indistinguable
   d'un simple "Dormant" alors que c'est le signal le plus urgent des trois
   paliers de relance. */
.quote-badge-final { background: color-mix(in srgb, var(--warn) 18%, transparent); color: var(--warn); border-color: transparent; }
.quote-badge-critical { background: color-mix(in srgb, var(--critical) 18%, transparent); color: var(--critical); border-color: transparent; }

.quote-actions { display: flex; flex-direction: column; align-items: flex-end; gap: 9px; flex: 0 0 auto; }
.quote-buttons { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.quote-action-form { flex: 0 0 auto; }
.quote-action-form button {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 7px 12px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--raised-bg);
  color: var(--text-secondary);
  font-size: var(--fs-meta);
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
  flex-shrink: 0;
  transition: background .12s ease, color .12s ease, border-color .12s ease, transform .08s ease;
}
.quote-action-form button:active { transform: scale(0.96); }
.btn-icon { font-size: var(--fs-label); line-height: 1; }

.quote-action-form button.btn-relance {
  background: color-mix(in srgb, var(--accent) 10%, var(--raised-bg));
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 30%, var(--border));
}
.quote-action-form button.btn-relance:hover { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.quote-action-form button.btn-relance-done {
  background: color-mix(in srgb, var(--good) 14%, var(--raised-bg));
  color: var(--good-text);
  border-color: transparent;
}
.quote-action-form button.btn-relance-done:hover { background: var(--good); color: var(--accent-ink); }

.quote-action-form button.btn-relance-dismiss {
  background: transparent;
  color: var(--text-muted);
  border-color: var(--border);
}
.quote-action-form button.btn-relance-dismiss:hover {
  background: color-mix(in srgb, var(--critical) 12%, var(--raised-bg));
  color: var(--critical);
  border-color: color-mix(in srgb, var(--critical) 30%, var(--border));
}

/* ---- Relances : brouillon éditable + envoi direct depuis Gmail (2026-07-20) ---- */
.relance-textarea {
  width: 100%; max-width: 62ch; margin-top: 8px; padding: 8px 10px;
  border: 1px solid var(--border); border-radius: 8px; background: var(--raised-bg);
  color: var(--text-primary); font-family: inherit; font-size: var(--fs-meta); line-height: 1.5;
  resize: vertical; min-height: 64px;
}
.relance-status { font-size: var(--fs-label); margin-top: 4px; min-height: 14px; }
.relance-status-ok { color: var(--good-text); font-weight: 700; }
.relance-status-warn { color: var(--text-muted); }
.relance-status-error { color: var(--critical); font-weight: 700; }
.quote-buttons-vertical { flex-direction: column; align-items: stretch; gap: 6px; }
.quote-buttons-vertical .quote-action-form { width: 100%; }
.quote-buttons-vertical .quote-action-form button { width: 100%; justify-content: center; }
/* Copier reste une action utilitaire secondaire, pas au même niveau que
   Envoyer/Relancé/Gagné/Perdu - align-items:stretch du conteneur
   l'étirait avant ce fix à la même largeur que les boutons principaux,
   noyant la hiérarchie réelle dans une pile de 4-5 boutons identiques. */
.quote-buttons-vertical .content-copy-btn { align-self: flex-start; }
/* Gagné/Perdu forment une paire de résultat mutuellement exclusive - les
   afficher côte à côte (au lieu de 2 lignes pleine largeur empilées avec
   les autres actions) réduit la hauteur de la pile et montre qu'il s'agit
   d'un même choix, pas de deux actions indépendantes de plus. */
.quote-outcome-pair { display: flex; gap: 6px; width: 100%; }
.quote-outcome-pair .quote-action-form { flex: 1 1 0; }
.btn-send {
  padding: 7px 12px; border-radius: 999px; border: 1px solid transparent;
  background: var(--accent); color: var(--accent-ink); font-size: var(--fs-meta); font-weight: 700;
  cursor: pointer; white-space: nowrap; font-family: inherit;
  transition: opacity .12s ease, transform .08s ease;
}
.btn-send:hover { opacity: 0.88; }
.btn-send:active { transform: scale(0.96); }
.btn-send:disabled { opacity: 0.6; cursor: default; }

.quote-action-form button.btn-good {
  background: color-mix(in srgb, var(--good) 10%, var(--raised-bg));
  color: var(--good-text);
  border-color: color-mix(in srgb, var(--good) 25%, var(--border));
}
.quote-action-form button.btn-good:hover { background: var(--good); color: var(--accent-ink); border-color: var(--good); }

.quote-action-form button.btn-muted { color: var(--text-muted); }
.quote-action-form button.btn-muted:hover { background: var(--critical); color: var(--accent-ink); border-color: var(--critical); }

/* ---- Historique client v2 : deux colonnes campagnes / devis ---- */
.split-layout { display: grid; grid-template-columns: 260px 1fr; gap: 20px; align-items: start; margin-bottom: 24px; }

.panel-eyebrow {
  font-size: var(--fs-label); font-weight: 800; text-transform: uppercase; letter-spacing: 0.07em;
  color: var(--accent); margin-bottom: 6px;
}
.panel-title-accent { color: var(--text-secondary); font-weight: 600; }
.panel-title-row { display: flex; justify-content: space-between; align-items: flex-end; gap: 16px; flex-wrap: wrap; }

/* ---- Résumé exécutif : par où commencer ---- */
.priorities-callout {
  background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 10%, var(--panel-bg)), var(--panel-bg));
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 22px 26px;
  margin-bottom: 24px;
}
.priorities-title { font-size: var(--fs-heading); font-weight: 800; letter-spacing: -0.01em; }
.priorities-sub { font-size: var(--fs-meta); color: var(--text-secondary); margin: 4px 0 16px; }
.priorities-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; }
.priority-card {
  display: flex; align-items: center; gap: 12px;
  background: var(--raised-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 14px 16px;
  color: var(--text-primary);
}
.priority-card:hover { border-color: var(--accent); }
.priority-rank {
  flex: 0 0 auto;
  width: 28px; height: 28px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 800; font-size: var(--fs-meta);
  display: flex; align-items: center; justify-content: center;
}
.priority-body { flex: 1; min-width: 0; }
.priority-label { font-weight: 700; font-size: var(--fs-title); }
.priority-detail { font-size: var(--fs-label); color: var(--text-secondary); margin-top: 2px; }
.priority-arrow { color: var(--text-muted); font-weight: 700; }

/* ---- Fiches playbook (une par segment, au lieu d'un mur de texte) ---- */
.playbook-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 14px; }
.playbook-card {
  background: var(--raised-bg);
  border-left: 3px solid var(--market-slot-1);
  border-radius: 8px;
  padding: 16px 18px;
}
.playbook-slot-1 { border-left-color: var(--market-slot-1); }
.playbook-slot-2 { border-left-color: var(--market-slot-2); }
.playbook-slot-3 { border-left-color: var(--market-slot-3); }
.playbook-slot-4 { border-left-color: var(--market-slot-4); }
.playbook-slot-5 { border-left-color: var(--market-slot-5); }
.playbook-slot-6 { border-left-color: var(--market-slot-6); }
.playbook-slot-7 { border-left-color: var(--market-slot-1); }
.playbook-slot-8 { border-left-color: var(--market-slot-2); }
.playbook-name { font-weight: 800; font-size: var(--fs-meta); text-transform: uppercase; letter-spacing: 0.03em; margin-bottom: 7px; }
.playbook-body { font-size: var(--fs-meta); line-height: 1.6; color: var(--text-secondary); margin: 0; }
.playbook-price {
  margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border);
  font-size: var(--fs-meta); font-weight: 700; color: var(--good-text);
}

/* ---- Prospection LinkedIn : brief de campagne par segment ---- */
.campaign-target-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: 16px; }
.campaign-target-card {
  background: var(--raised-bg);
  border-left: 3px solid var(--market-slot-1);
  border-radius: 8px;
  padding: 18px 20px;
}
.campaign-target-slot-1 { border-left-color: var(--market-slot-1); }
.campaign-target-slot-2 { border-left-color: var(--market-slot-2); }
.campaign-target-slot-3 { border-left-color: var(--market-slot-3); }
.campaign-target-slot-4 { border-left-color: var(--market-slot-4); }
.campaign-target-slot-5 { border-left-color: var(--market-slot-5); }
.campaign-target-slot-6 { border-left-color: var(--market-slot-6); }
.campaign-target-slot-7 { border-left-color: var(--market-slot-1); }
.campaign-target-slot-8 { border-left-color: var(--market-slot-2); }

.campaign-target-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 12px; }
.campaign-target-label { font-weight: 800; font-size: var(--fs-name); }

.campaign-target-row { margin-bottom: 12px; }
.campaign-target-key {
  display: block; font-size: var(--fs-label); font-weight: 800; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--text-muted); margin-bottom: 6px;
}
.tag-list { display: flex; flex-wrap: wrap; gap: 5px; }
.tag {
  font-size: var(--fs-label); font-weight: 600; padding: 3px 9px; border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 12%, var(--panel-bg));
  color: var(--accent); border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent);
}
.tag-muted { background: var(--panel-bg); color: var(--text-secondary); border-color: var(--border); }
.tag-signal {
  background: color-mix(in srgb, var(--good) 12%, var(--panel-bg));
  color: var(--good-text); border-color: color-mix(in srgb, var(--good) 25%, transparent);
}

.campaign-target-angle { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border); }
.campaign-target-angle p { font-size: var(--fs-meta); line-height: 1.55; color: var(--text-secondary); margin: 0; }
.campaign-target-angle-empty p { color: var(--text-muted); font-style: italic; }

.campaign-panel {
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 22px 18px;
  position: sticky;
  top: 24px;
}
.campaign-list { display: flex; flex-direction: column; gap: 1px; }
.campaign-row {
  display: flex; justify-content: space-between; align-items: center; gap: 8px;
  padding: 9px 10px; border-radius: 7px;
  border: 1px solid transparent;
  color: var(--text-primary);
}
.campaign-row:hover { background: var(--raised-bg); }
.campaign-active { background: color-mix(in srgb, var(--accent) 14%, var(--raised-bg)); border-color: var(--accent); }
.campaign-label { font-weight: 600; font-size: var(--fs-meta); line-height: 1.3; }
.campaign-count {
  font-size: var(--fs-meta); font-weight: 800; font-variant-numeric: tabular-nums;
  color: var(--text-muted); flex: 0 0 auto;
}
.campaign-active .campaign-count { color: var(--accent); }

.quotes-panel { min-width: 0; }

.search-form { display: flex; gap: 8px; }
.search-form input {
  padding: 8px 12px; border-radius: 8px; border: 1px solid var(--border);
  background: var(--raised-bg); color: var(--text-primary); font-size: var(--fs-meta); min-width: 240px;
}
.search-form button {
  padding: 8px 14px; border-radius: 8px; border: 1px solid var(--border);
  background: var(--raised-bg); color: var(--text-primary); font-size: var(--fs-meta); font-weight: 700; cursor: pointer;
}
.search-form button:hover { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }

.pagination { display: flex; align-items: center; justify-content: center; gap: 18px; margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--border); }
.pagination a { font-size: var(--fs-meta); font-weight: 700; color: var(--accent); }
.pagination-current { font-size: var(--fs-meta); color: var(--text-muted); }

@media (max-width: 900px) {
  .split-layout { grid-template-columns: 1fr; }
  .campaign-panel { position: static; }
}

.insight-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 20px; }
.insight-block { background: var(--raised-bg); border-radius: 10px; padding: 18px 20px; }
.insight-label { font-size: var(--fs-label); font-weight: 800; text-transform: uppercase; letter-spacing: 0.05em; color: var(--accent); margin-bottom: 8px; }
.insight-content { font-size: var(--fs-title); line-height: 1.6; color: var(--text-secondary); margin: 0; }

/* ---- Bento : hub Aujourd'hui (2026-07-27), remplace les listes empilées
   d'accordions par une grille de cartes compactes qui se lisent d'un coup
   d'œil - chaque carte porte tout son contexte (qui, quoi, action) au lieu
   d'obliger à déplier une ligne à la fois. ---- */
.bento-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 12px; }
.bento-card {
  display: flex; flex-direction: column; gap: 6px;
  background: var(--raised-bg);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: 10px;
  padding: 14px 16px;
  color: var(--text-primary);
  transition: border-color .12s ease, transform .08s ease;
}
a.bento-card:hover { border-color: var(--accent); transform: translateY(-1px); }
.bento-card-cat { font-size: var(--fs-label); font-weight: 800; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); }
.bento-card-title { font-weight: 700; font-size: var(--fs-name); line-height: 1.3; }
.bento-card-title-sub { font-weight: 500; color: var(--text-secondary); }
.bento-card-body { font-size: var(--fs-meta); color: var(--text-secondary); line-height: 1.45; }
.bento-card-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: auto; padding-top: 8px; }
.bento-card-foot-actions { flex-wrap: wrap; justify-content: flex-start; }
.bento-card-arrow { font-size: var(--fs-meta); font-weight: 700; color: var(--accent); white-space: nowrap; }

.bento-card-sequence { border-left-color: var(--market-slot-4); }
.bento-card-signal { border-left-color: var(--market-slot-2); }
.bento-card-partner { border-left-color: var(--market-slot-3); }
.bento-card-relance { border-left-color: var(--accent); }
.bento-card-dormant { background: color-mix(in srgb, var(--good) 6%, var(--raised-bg)); }

/* Brouillon toujours visible (2026-07-29, retiré du <details> caché derrière
   "Voir / modifier" - convention Insik OS : l'email complet se voit
   directement dans la carte, jamais un clic pour le révéler). */
.bento-draft { margin-top: 2px; }
.bento-draft-label {
  font-size: var(--fs-micro); font-weight: 800; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--text-muted); margin-bottom: 4px;
}

.btn-relance-mini {
  padding: 5px 10px; border-radius: 999px; border: 1px solid var(--border);
  background: transparent; color: var(--text-muted); font-size: var(--fs-label); font-weight: 700;
  cursor: pointer; font-family: inherit;
}
.btn-relance-mini:hover { background: color-mix(in srgb, var(--critical) 12%, var(--raised-bg)); color: var(--critical); border-color: color-mix(in srgb, var(--critical) 30%, var(--border)); }

@media (max-width: 900px) {
  .bento-grid { grid-template-columns: 1fr; }
}

/* ---- Grille d'entrée Contenu (2026-08-06, Alexandre : "sous forme de
   bento grid" + "Créer un post... connecté au truc dans son ensemble" +
   "Formats... c'est juste des conseils" + "Portfolio et signature... pas
   souvent utilisé"). Même famille que .bento-grid ci-dessus (remplace une
   liste empilée par une grille lisible d'un coup d'œil), mais avec deux
   poids délibérément différents : Créer un post/LinkedIn/Emails types sont
   du quotidien, Formats/Portfolio & signature sont du "réglé une fois,
   consulté rarement" - la rangée secondaire est volontairement plus petite
   et sans fond plutôt que de leur donner le même poids par défaut. ---- */
.content-hub { display: flex; flex-direction: column; gap: 10px; margin: 4px 0 18px; }
.content-hub-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; }
.content-hub-tile {
  all: unset; box-sizing: border-box; cursor: pointer;
  display: flex; flex-direction: column; gap: 8px;
  background: var(--panel-bg); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 16px; color: var(--text-primary);
  transition: border-color .12s ease, transform .08s ease, box-shadow .12s ease;
}
.content-hub-tile:hover { border-color: var(--accent); transform: translateY(-1px); box-shadow: 0 4px 16px rgba(20, 22, 28, 0.08); }
.content-hub-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.content-hub-tile.active {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 7%, var(--panel-bg));
  box-shadow: inset 0 0 0 1px var(--accent);
}
.content-hub-tile.active .content-hub-icon { background: color-mix(in srgb, var(--accent) 16%, var(--panel-bg)); color: var(--accent); }
.content-hub-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
.content-hub-icon {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: 9px;
  background: var(--raised-bg); color: var(--text-secondary);
}
.content-hub-icon svg { width: 19px; height: 19px; }
.content-hub-title { font-weight: 700; font-size: var(--fs-name); }
.content-hub-desc { font-size: var(--fs-meta); color: var(--text-secondary); line-height: 1.4; }
.content-hub-count {
  flex: none; font-size: var(--fs-label); font-weight: 800; font-variant-numeric: tabular-nums;
  color: var(--text-secondary); background: var(--raised-bg); padding: 2px 8px; border-radius: 999px;
}
.content-hub-badge {
  flex: none; font-size: var(--fs-label); font-weight: 700; color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, var(--panel-bg));
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border));
  padding: 3px 9px; border-radius: 999px;
}
/* Génération indisponible (2026-08-06, Alexandre : "il faut l'indiquer
   cela" après avoir demandé si Créer un post marche vraiment - crédits
   Anthropic à zéro, confirmé en direct). Rouge plutôt que la teinte accent
   du badge normal : c'est un blocage réel, pas une simple notification. */
.content-hub-badge-warn {
  color: var(--critical);
  background: color-mix(in srgb, var(--critical) 10%, var(--panel-bg));
  border-color: color-mix(in srgb, var(--critical) 30%, var(--border));
  cursor: help;
}

.content-hub-tile-primary {
  grid-column: span 2;
  background: color-mix(in srgb, var(--accent) 5%, var(--panel-bg));
  border-color: color-mix(in srgb, var(--accent) 26%, var(--border));
}
.content-hub-tile-primary .content-hub-icon { background: color-mix(in srgb, var(--accent) 16%, var(--panel-bg)); color: var(--accent); }
.content-hub-tile-primary .content-hub-title { font-size: var(--fs-lead); }

.content-hub-grid-secondary { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.content-hub-tile-secondary {
  flex-direction: row; align-items: center; padding: 10px 14px;
  background: transparent; border-style: dashed;
}
.content-hub-tile-secondary:hover { transform: none; box-shadow: none; }
.content-hub-tile-secondary.active { border-style: solid; box-shadow: none; }
.content-hub-tile-secondary .content-hub-icon { width: 26px; height: 26px; background: transparent; color: var(--text-muted); }
.content-hub-tile-secondary .content-hub-icon svg { width: 15px; height: 15px; }
.content-hub-tile-secondary .content-hub-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.content-hub-tile-secondary .content-hub-title { font-size: var(--fs-meta); font-weight: 700; }
.content-hub-tile-secondary .content-hub-desc { font-size: var(--fs-label); color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.content-hub-tile-secondary .content-hub-count { margin-left: auto; }

@media (max-width: 720px) {
  .content-hub-tile-primary { grid-column: span 1; }
}

/* [data-section] : uniquement les 5 .acc-section pilotées par la grille
   d'entrée ci-dessus (jamais .acc-head, retiré sur cette page) - sans ça,
   une section fermée resterait un rectangle bordé vide (le fond visible de
   .acc-section, plus rien dedans pour le remplir). Scopé à data-section
   pour ne pas toucher les .acc-section à en-tête classique des autres
   pages (Signaux, Anciens clients, Profil), qui doivent rester visibles
   fermées (c'est l'en-tête cliquable qui les rouvre). */
.acc-section[data-section]:not(.open) { display: none; }

/* ---- Onglets internes à une page (2026-07-27) : "trop d'infos et de texte
   sur les pages" - au lieu d'empiler 4-6 panneaux pleine largeur les uns sous
   les autres (tout visible et à faire défiler d'un coup), un seul panneau
   affiché à la fois. Widget générique, réutilisable sur n'importe quelle
   page (cf le petit script dans base.html qui l'active partout). ---- */
.tabs-bar { display: flex; gap: 2px; margin-bottom: 22px; border-bottom: 1px solid var(--border); overflow-x: auto; }
.tab-btn {
  padding: 10px 16px; border: none; background: transparent; color: var(--text-secondary);
  font-family: inherit; font-size: var(--fs-title); font-weight: 700; cursor: pointer;
  border-bottom: 2px solid transparent; white-space: nowrap; transition: color .12s ease, border-color .12s ease;
}
.tab-btn:hover { color: var(--text-primary); }
.tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); }
.tab-count { margin-left: 6px; font-size: var(--fs-label); font-weight: 800; color: var(--text-muted); }
.tab-btn.active .tab-count { color: var(--accent); }
/* 2026-08-14, audit UX/accès ("Signal review — wrong tab open by default") -
   /signaux a 6 accordéons dont un seul, "File de revue", porte de vraies
   décisions (Promouvoir/Ignorer) ; son compteur rendait aussi neutre/gris que
   les 4 accordéons de configuration/analyse à côté, qu'il y ait 0 ou 30 en
   attente. Réservé aux compteurs qui signalent une VRAIE file d'attente
   (jamais un simple total informatif comme les tab-count de /historique-
   client ou /profil ci-dessus, appliqué au cas par cas). */
.tab-count-warn { color: var(--warn); }

.client-list { display: flex; flex-direction: column; gap: 4px; }
.client-row { padding: 14px 4px; border-bottom: 1px solid var(--border); }
.client-row:last-child { border-bottom: none; }
/* 2026-08-14, audit UX/accès ("Signal review — actionable rows look
   identical to read-only log rows") - .client-row sert à la fois pour du pur
   log (ex. "Les 20 plus récents") et pour des lignes avec de vraies actions
   Promouvoir/Ignorer (signaux.html::"File de revue") sans aucune différence
   visuelle entre les deux. Même idée que .queue-item (border-left coloré,
   flux.html) mais sans restructurer le HTML existant de ces lignes. */
.client-row-actionable { border-left: 3px solid var(--warn); padding-left: 10px; }
.client-row-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.client-name { font-weight: 700; font-size: var(--fs-name); }
.client-company { color: var(--text-secondary); font-size: var(--fs-meta); }
/* text-transform: capitalize retiré (2026-08-07, audit visuel Chris - "il
   a besoin d'indications plus précises" a mené à screenshoter /templates en
   vrai) : cette classe, pensée à l'origine pour un court libellé (un nom de
   marché seul), est maintenant réutilisée partout pour des PHRASES entières
   (email_template_edit.html, _emailing_nav.html, contenu.html...) - capitalize
   met une majuscule à CHAQUE mot d'une phrase, illisible en pratique
   ("Approuver Porte Sur Les 3 Emails D'un Coup"). .prospect-market a sa
   propre règle capitalize séparée pour le seul cas où un mot seul en profite
   réellement - rien ne dépendait de celle-ci. */
.client-meta { font-size: var(--fs-meta); color: var(--text-muted); margin-top: 3px; }
.client-summary { font-size: var(--fs-meta); color: var(--text-secondary); margin: 6px 0 0; line-height: 1.5; }

/* ---- Fil de conversation façon Gmail (2026-08-14, Alexandre : "we have
   these long text zones where we can't distinguish client answers from
   [Chris]... make the design more like gmail bubbles") - sequence_thread.html
   entrelaçait déjà nos emails et les réponses du client dans le même ordre
   chronologique (cf dashboard/data.py::sequence_thread(), qui ne renvoyait
   même pas les réponses avant ce jour), mais tout rendait en blocs de texte
   identiques (.client-row). Deux couleurs/alignements distincts, comme un
   client mail : NOUS à droite (teinte accent), LE CLIENT à gauche (neutre,
   bordé) - jamais une nouvelle palette, seulement --accent/--panel-bg déjà
   établis. Repris tel quel partout où un futur fil à deux voix en aurait
   besoin (pas seulement cette page). ---- */
.thread-bubble-list { display: flex; flex-direction: column; gap: 14px; }
.thread-bubble-row { display: flex; }
.thread-bubble-row.is-outbound { justify-content: flex-end; }
.thread-bubble-row.is-inbound { justify-content: flex-start; }
.thread-bubble {
  max-width: 78%;
  border-radius: 14px;
  padding: 12px 16px;
}
.thread-bubble.is-outbound {
  background: color-mix(in srgb, var(--accent) 10%, var(--panel-bg));
  border: 1px solid color-mix(in srgb, var(--accent) 22%, var(--border));
  border-bottom-right-radius: 4px;
}
.thread-bubble.is-inbound {
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-bottom-left-radius: 4px;
}
/* Réponse client jamais encore vue/traitée - même rouge d'urgence que le
   badge de nav (nav-badge-red), pour repérer d'un coup d'œil laquelle des
   bulles à gauche a réellement besoin d'attention. */
.thread-bubble.is-inbound.is-escalated { border-color: color-mix(in srgb, var(--critical) 45%, var(--border)); }
.thread-bubble-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 5px; }
.thread-bubble-sender { font-weight: 800; font-size: var(--fs-label); text-transform: uppercase; letter-spacing: 0.04em; }
.thread-bubble.is-outbound .thread-bubble-sender { color: var(--accent); }
.thread-bubble.is-inbound .thread-bubble-sender { color: var(--text-secondary); }
.thread-bubble-time { font-size: var(--fs-micro); color: var(--text-muted); }
.thread-bubble-subject { font-weight: 700; font-size: var(--fs-meta); margin-bottom: 4px; }
.thread-bubble-body { white-space: pre-wrap; font-size: var(--fs-meta); line-height: 1.55; color: var(--text-primary); margin: 0; }
.thread-bubble-foot { margin-top: 8px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

@media (max-width: 640px) {
  .thread-bubble { max-width: 92%; }
}

/* 2026-08-14, audit UX/accès ("mobile-shell", sévérité haute x2) - avant ce
   correctif, .sidebar passait juste en flex-direction:row + flex-wrap : tout
   le contenu (marque, sous-titre, 3 groupes de nav avec badges, filtre Piste,
   Déconnexion - une dizaine d'éléments) s'empilait en plusieurs rangées de
   "pilules" AU-DESSUS du contenu de CHAQUE page, y compris /flux, le hub
   pensé pour aller droit au but sur téléphone. Remplacé par un vrai tiroir :
   fermé par défaut (.sidebar-nav-wrap masqué), un bouton compact
   (.sidebar-toggle, cf plus haut) l'ouvre en pleine largeur sous la marque.
   Comme c'est maintenant un empilement vertical normal plutôt qu'une rangée
   compressée, les libellés de section (.side-section-label) redeviennent
   utiles - ils ne sont donc plus masqués ici. */
@media (max-width: 900px) {
  body.shell { flex-direction: column; }
  /* flex-basis REMIS a auto (2026-08-16, audit mobile). .sidebar porte
     `flex: 0 0 232px` pour sa LARGEUR en desktop ; des que body.shell passe
     en colonne, cette meme base de 232px s'applique a la HAUTEUR. Resultat
     mesure sur iPhone : une barre de 232px de haut dont le contenu ne fait
     que 36px, soit ~200px de blanc avant le premier titre, sur chaque page.
     `height: auto` seul ne corrigeait rien puisque c'est flex-basis qui
     imposait la taille. */
  .sidebar {
    position: static;
    flex: 0 0 auto;
    height: auto;
    flex-direction: column;
    align-items: stretch;
    padding: 12px 16px;
  }
  .sidebar-toggle { display: inline-flex; }
  .sidebar-nav-wrap { display: none; flex-direction: column; margin-top: 14px; }
  .sidebar.nav-open .sidebar-nav-wrap { display: flex; }
  .side-nav, .track-filter { flex-direction: column; margin-top: 0; }
  .logout { margin-top: 16px; margin-left: 0; }
  .page { padding: 24px; }
  .funnel-row { grid-template-columns: 110px 1fr 40px 40px; }
  .hbar-row { grid-template-columns: 110px 1fr 34px; }
  .insight-grid { grid-template-columns: 1fr; }
}

/* Stat-row à deux colonnes sur un vrai téléphone (375-430px) plutôt qu'une
   colonne unique par défaut de auto-fit/minmax(190px,...) - qui ne laisse
   jamais assez de place pour 2x190px+gap sous ~400px de large. Gardé séparé
   du bloc 900px ci-dessus : entre 480 et 900px (tablette/fenêtre réduite),
   auto-fit tient déjà naturellement 3-4 colonnes, un forçage à 2 partout
   sous 900px aurait été un vrai recul là. */
@media (max-width: 480px) {
  .stat-row { grid-template-columns: repeat(2, 1fr); gap: 10px; }
}

/* ---- État de l'automatisation + Réponses reçues (2026-07-29) - "je vois ou
   tout ca" / "incompréhensible comment tout est lié" : deux sections neuves
   sur le hub Aujourd'hui pour répondre à "est-ce que ça tourne" et "qui a
   répondu et quoi". ---- */
.job-status-list { display: flex; flex-direction: column; gap: 1px; }
.job-status-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 4px; border-bottom: 1px solid var(--border);
}
.job-status-row:last-child { border-bottom: none; }
.job-status-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.job-status-dot.status-success { background: var(--good); box-shadow: 0 0 0 3px color-mix(in srgb, var(--good) 25%, transparent); }
.job-status-dot.status-failed { background: var(--critical); box-shadow: 0 0 0 3px color-mix(in srgb, var(--critical) 25%, transparent); }
.job-status-dot.status-never_run { background: var(--text-muted); }
.job-status-title { font-weight: 700; font-size: var(--fs-meta); flex: 1 1 auto; min-width: 0; }
.job-status-meta { font-size: var(--fs-label); color: var(--text-muted); white-space: nowrap; }
.job-status-meta.status-failed-text { color: var(--critical); }

.reply-card { border-left: 3px solid var(--accent); }
.reply-card-interested { border-left-color: var(--good); }
.reply-card-not_interested, .reply-card-unsubscribe { border-left-color: var(--text-muted); }
.reply-card-question { border-left-color: var(--market-slot-3); }
.intent-badge {
  font-size: var(--fs-micro); font-weight: 800; text-transform: uppercase; letter-spacing: 0.03em;
  padding: 2px 8px; border-radius: 999px; background: var(--raised-bg); color: var(--text-muted);
}
.intent-badge-interested { background: color-mix(in srgb, var(--good) 18%, transparent); color: var(--good-text); }
.intent-badge-question { background: color-mix(in srgb, var(--market-slot-3) 20%, transparent); color: var(--market-slot-3); }
.intent-badge-not_interested, .intent-badge-unsubscribe { background: var(--raised-bg); color: var(--text-muted); }
.escalated-flag { color: var(--critical); font-weight: 800; font-size: var(--fs-label); }

/* ---- /pipeline, réponses par campagne (2026-08-15) - bucket positif/
   négatif/neutre (data.py::campaign_reply_sentiment), distinct des variantes
   .intent-badge-{{ intent brut }} ci-dessus (emailing à froid) : ici le
   rouge/vert est voulu, la page existe justement pour rendre ce contraste
   visible d'un coup d'œil plutôt que de le nuancer dans un badge discret. */
.intent-badge-positive { background: color-mix(in srgb, var(--good) 18%, transparent); color: var(--good-text); }
.intent-badge-negative { background: color-mix(in srgb, var(--critical) 16%, transparent); color: var(--critical); }
.intent-badge-neutral { background: var(--raised-bg); color: var(--text-muted); }

.campaign-card { margin: 0 0 28px; }
.campaign-sentiment-bar {
  display: flex; height: 8px; border-radius: 4px; overflow: hidden;
  background: var(--raised-bg); margin-top: 8px;
}
.campaign-sentiment-seg { display: block; min-width: 3px; }
.campaign-sentiment-positive { background: var(--good); }
.campaign-sentiment-negative { background: var(--critical); }
.campaign-sentiment-neutral { background: var(--text-muted); }

.reply-row {
  background: var(--panel-bg); border: 1px solid var(--border); border-left: 3px solid var(--text-muted);
  border-radius: var(--radius); padding: 12px 14px;
}
.reply-row-positive { border-left-color: var(--good); }
.reply-row-negative { border-left-color: var(--critical); }
.reply-row-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.reply-row-who { font-size: var(--fs-name); font-weight: 700; }
.reply-row-snippet { margin: 6px 0 0; font-size: var(--fs-meta); color: var(--text-secondary); }
.reply-row-date { display: block; margin-top: 8px; font-size: var(--fs-label); color: var(--text-muted); }
.reply-row-date a { color: var(--accent); font-weight: 600; }

/* Badge générique (2026-07-29, inspiré du .badge de content-system - même
   principe de pilule arrondie, adapté au thème sombre existant plutôt que de
   basculer tout le dashboard vers le light mode du projet source) */
.badge {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 9px; border-radius: 999px; font-size: var(--fs-label); font-weight: 700;
  background: color-mix(in srgb, var(--accent) 16%, var(--raised-bg));
  color: var(--accent); white-space: nowrap;
}
div.bento-card { transition: transform 0.15s ease, border-color 0.15s ease; }
div.bento-card:hover { border-color: var(--accent); transform: translateY(-1px); }

/* Bibliothèque de formats (2026-07-29, onglet Formats de Contenu) - cartes
   dépliables, même principe que content-system/app/formats/page.tsx (nom +
   badges + description en résumé, détail riche au clic) traduit dans le
   thème sombre existant plutôt que copié en light mode. */
.format-grid { display: flex; flex-direction: column; gap: 10px; }
.format-card {
  background: var(--panel-bg); border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; transition: border-color 0.15s ease;
}
.format-card[open] { border-color: var(--accent); }
.format-card summary {
  list-style: none; cursor: pointer; padding: 14px 16px; display: flex;
  align-items: flex-start; justify-content: space-between; gap: 12px;
}
.format-card summary::-webkit-details-marker { display: none; }
.format-card summary:hover { background: var(--raised-bg); }
.format-card-head { display: flex; flex-direction: column; gap: 6px; }
.format-card-badges { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.format-card-name { font-weight: 700; font-size: var(--fs-name); }
.format-card-desc { font-size: var(--fs-meta); color: var(--text-secondary); line-height: 1.5; max-width: 62ch; }
.format-card-chevron { color: var(--text-muted); font-size: var(--fs-meta); flex-shrink: 0; margin-top: 2px; }
.format-card[open] .format-card-chevron { color: var(--accent); }
.format-card-body { padding: 0 16px 16px; display: flex; flex-direction: column; gap: 12px; border-top: 1px solid var(--border); padding-top: 12px; }
.format-label { font-size: var(--fs-micro); font-weight: 800; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); margin-bottom: 4px; }
.format-hook-pattern { font-size: var(--fs-meta); font-style: italic; color: var(--text-primary); }
.format-structure { padding-left: 0; margin: 0; list-style: none; display: flex; flex-direction: column; gap: 4px; }
.format-structure li { display: flex; gap: 8px; font-size: var(--fs-meta); color: var(--text-secondary); }
.format-structure li b { color: var(--text-muted); flex-shrink: 0; }
.format-meta-row { display: flex; gap: 24px; }
.format-info-box { border-radius: 8px; padding: 10px 12px; font-size: var(--fs-meta); line-height: 1.5; color: var(--text-secondary); }
.format-info-good { background: color-mix(in srgb, var(--good) 10%, var(--panel-bg)); border: 1px solid color-mix(in srgb, var(--good) 25%, transparent); }
.format-info-good .format-label { color: var(--good-text); }
.format-info-warn { background: color-mix(in srgb, var(--critical) 10%, var(--panel-bg)); border: 1px solid color-mix(in srgb, var(--critical) 25%, transparent); }
.format-info-warn .format-label { color: var(--critical); }
.format-filter-bar { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }
.format-filter-btn {
  border-radius: 999px; padding: 5px 12px; font-size: var(--fs-meta); font-weight: 600;
  background: var(--raised-bg); color: var(--text-secondary); border: 1px solid transparent; cursor: pointer;
}
.format-filter-btn.active { background: var(--accent); color: var(--accent-ink); }
.format-filter-btn:hover:not(.active) { border-color: var(--accent); color: var(--text-primary); }

/* Refonte de la hiérarchie d'Aujourd'hui (2026-07-29, Alexandre : "rien
   n'est relié, visuellement super difficile à naviguer... on doit avoir un
   dashboard bien organisé avec des trucs qu'on doit ouvrir, des signaux à
   valider si besoin, mais sinon ça doit bien fonctionner"). Trois niveaux
   visuels distincts plutôt que des panels empilés au même poids : bandeau
   d'état compact (le système "marche juste", pas à lire), zone d'action
   (poids visuel fort), zone optionnelle (poids visuel réduit). */
.status-strip {
  display: flex; align-items: center; gap: 8px; font-size: var(--fs-meta);
  color: var(--text-secondary); padding: 10px 14px; margin-bottom: 20px;
  background: var(--panel-bg); border: 1px solid var(--border); border-radius: 10px;
}
.status-strip-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.status-strip-dot.ok { background: var(--good); }
.status-strip-dot.warn { background: var(--critical); }
.status-strip a { color: var(--accent); font-weight: 600; margin-left: auto; white-space: nowrap; }

.zone-heading {
  display: flex; align-items: baseline; gap: 10px; margin: 32px 0 4px;
}
.zone-heading:first-of-type { margin-top: 0; }
.zone-heading h2 { font-size: var(--fs-heading); font-weight: 800; margin: 0; }
.zone-tag {
  font-size: var(--fs-micro); font-weight: 800; text-transform: uppercase; letter-spacing: 0.06em;
  padding: 2px 8px; border-radius: 999px;
}
.zone-tag-action { background: color-mix(in srgb, var(--warn) 18%, transparent); color: var(--warn); }
.zone-tag-optional { background: var(--raised-bg); color: var(--text-muted); }
.zone-sub { font-size: var(--fs-meta); color: var(--text-muted); margin: 0 0 16px; }
.zone-optional { opacity: 0.88; }
.zone-optional .panel { border-style: dashed; }

/* Tiroir de signaux repliable par défaut (2026-07-29) - réduit le bruit
   visuel au chargement plutôt que d'afficher des cartes ouvertes pour un
   contenu explicitement "aucun effet si tu n'y touches pas". */
.signals-tray { list-style: none; }
.signals-tray > summary {
  cursor: pointer; list-style: none; display: flex; align-items: center; gap: 8px;
  font-weight: 700; font-size: var(--fs-name); color: var(--text-primary);
}
.signals-tray > summary::-webkit-details-marker { display: none; }
.signals-tray > summary::before { content: "▸"; color: var(--text-muted); }
.signals-tray[open] > summary::before { content: "▾"; }
.signals-tray-body { margin-top: 16px; }

/* Aujourd'hui en 3 colonnes spatiales (2026-07-29, référence directe :
   joomonglabs.com/demo - "Signaux chauds" à gauche / métriques compactes au
   centre / "À recontacter" à droite), adapté aux vraies données de
   Copilote : gauche = réponses (quelqu'un attend), centre = compteurs,
   droite = relances + séquences dues (rappels programmés). Remplace le
   rendu en liste/grille fusionnée du passage précédent, jugé pénible. */
/* 2026-08-12 - grille à 3 colonnes de largeur FIXE, y compris quand une
   colonne est vide (Alexandre : "ca prend de la place et y'a rien" sur
   Signaux chauds/métriques, "les à recontacter sont trop petits en
   comparaison" sur le vrai contenu en face) - une colonne vide au même
   ratio qu'une colonne pleine comprime le vrai contenu dans le même espace
   qu'une note "rien à traiter". Remplacé par .today-columns-2 (deux
   colonnes de contenu, sans la 3e colonne métriques - déplacées en
   stat-row au-dessus, cf flux.html) quand les deux ont du contenu, et un
   rendu pleine largeur en grille (.today-col-list-wide) sinon - jamais
   deux tailles au forceps pour un seul et même ratio figé.
   .today-columns non supprimée : gardée pour compat visuelle si un futur
   usage la reference, mais flux.html ne l'utilise plus telle quelle. */
.today-columns { display: grid; grid-template-columns: 1.2fr 0.7fr 1.2fr; gap: 20px; align-items: start; }
.today-columns-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; align-items: start; }
.today-col-list-wide { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 10px; }
.today-col-title {
  display: flex; align-items: center; gap: 8px; font-size: var(--fs-meta); font-weight: 800;
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); margin-bottom: 12px;
}
.today-col-title .count { color: var(--text-primary); font-weight: 800; }
.today-col-list { display: flex; flex-direction: column; gap: 10px; }
.today-metrics { display: flex; flex-direction: column; gap: 10px; }
/* 2026-08-13, flux.html::"À recontacter" - regroupé par catégorie (devis,
   rushs, projet...) plutôt qu'aplati en une seule liste (Alexandre :
   "l'organisation des a recontacter... n'a pas de sens"). Un sous-titre
   discret par catégorie, jamais aussi lourd que .today-col-title (qui
   reste réservé aux deux sections de premier niveau, Signaux chauds/À
   recontacter). */
.flux-followup-group { margin-top: 26px; }
.flux-followup-group:first-of-type { margin-top: 0; }
.flux-followup-group-title {
  display: flex; align-items: center; gap: 8px; font-size: var(--fs-label); font-weight: 800;
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-secondary);
  margin-bottom: 12px; padding-bottom: 8px; border-bottom: 1px solid var(--border);
}
.flux-followup-group-title .count { color: var(--text-primary); }
@media (max-width: 900px) {
  .today-columns-2 { grid-template-columns: 1fr; }
}
.today-metric {
  background: var(--panel-bg); border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px;
}
.today-metric-value { font-size: 26px; font-weight: 900; line-height: 1; }
.today-metric-label { font-size: var(--fs-label); color: var(--text-muted); margin-top: 4px; }
@media (max-width: 1024px) {
  .today-columns { grid-template-columns: 1fr; }
}
.queue-item {
  background: var(--panel-bg); border: 1px solid var(--border); border-left: 3px solid var(--baseline);
  border-radius: 10px; padding: 16px 18px;
}
.queue-item-reply { border-left-color: var(--good); }
.queue-item-reply.queue-item-escalated { border-left-color: var(--critical); }
.queue-item-client_reply { border-left-color: var(--good); }
.queue-item-client_reply.queue-item-escalated { border-left-color: var(--critical); }
.queue-item-relance { border-left-color: var(--accent); }
.queue-item-sequence { border-left-color: var(--market-slot-4); }
.queue-item-sms { border-left-color: var(--market-slot-3); }
/* 2026-08-13, flux.html::"Lots en attente de validation" - seule carte de
   file qui est un lien entier (pas juste un bouton interne à l'intérieur),
   d'où l'affordance de survol dédiée. */
.flux-lot-card { border-left-color: var(--warn); color: inherit; transition: border-color .15s, background .15s; }
.flux-lot-card:hover { background: var(--raised-bg); border-left-color: var(--critical); }
.queue-item-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 8px; }
.queue-item-who { font-weight: 700; font-size: var(--fs-name); }
.queue-item-company { font-weight: 500; color: var(--text-secondary); margin-left: 4px; }
.queue-item-subtitle { font-size: var(--fs-label); font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); white-space: nowrap; }
.queue-item-body { font-size: var(--fs-meta); color: var(--text-secondary); line-height: 1.55; margin: 0 0 8px; }
.queue-item-actions { display: flex; align-items: center; gap: 8px; margin-top: 10px; flex-wrap: wrap; }

/* Bulle "pourquoi" (2026-07-29, inspiré d'Insik OS - le raisonnement qui a
   fait remonter une carte reste toujours visible, jamais dans un tooltip ou
   un clic en plus). Violet réservé à ça uniquement, cf --ai ci-dessus. */
.ai-bubble {
  /* 2026-08-14, audit UX/accès ("design-tokens" - --ai landmine) : la
     bordure était un rgba(167,139,250) codé en dur, une teinte violette
     DIFFÉRENTE de --ai (#6d4fd1) au lieu d'en être dérivée par color-mix
     comme tout le reste ici - si le thème (déjà basculé une fois, cf le
     commentaire de :root en tête de fichier) change encore, cette bordure
     ne suivrait pas --ai. */
  background: var(--ai-soft); border: 1px solid color-mix(in srgb, var(--ai) 22%, transparent);
  border-radius: 8px; padding: 8px 11px; margin-top: 8px;
  font-size: var(--fs-label); line-height: 1.5; color: var(--text-secondary);
}
.ai-bubble-label {
  font-size: var(--fs-micro); font-weight: 800; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ai); margin-bottom: 3px; display: block;
}

/* ---- Flux quotidien (2026-07-31) - "un dashboard central avec un schéma
   animé, actionable" représentant la chaîne cron du jour dans l'espace,
   à la place de graphs éparpillés. Chaîne de noeuds reliés par un connecteur
   animé ; réutilise .job-status-dot / status-success / status-failed /
   status-never_run déjà établis ailleurs plutôt que d'inventer une nouvelle
   palette de statut.

   Préfixe pflow- (et non flow-) : .flow-diagram/.flow-node/.flow-connector
   existaient déjà pour le panneau "Système connecté" (ci-dessus, .flow-panel).
   Un nom identique ici écrasait silencieusement ces règles plus anciennes
   partout où le CSS est chargé - donc déjà sur /pipeline avant même la fusion
   dans Profil, pas seulement en cas de coexistence sur une même page. Trouvé
   et corrigé le jour où les deux panneaux ont fini sur la même page (Profil,
   onglets "Chiffres"/"Système connecté"). ---- */
.pflow-diagram {
  display: flex; align-items: stretch; gap: 0; overflow-x: auto; padding: 8px 2px 20px;
  margin: 20px 0 0;
}
.pflow-node-wrap { display: flex; flex-direction: column; flex: 0 0 auto; width: 168px; }
.pflow-node {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  width: 100%; text-align: left; cursor: pointer;
  background: var(--panel-bg); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 12px 14px; position: relative; font: inherit; color: inherit;
  transition: border-color 0.15s ease, transform 0.15s ease;
}
.pflow-node:hover { border-color: color-mix(in srgb, var(--accent) 40%, var(--border)); transform: translateY(-1px); }
.pflow-node-active { border-color: var(--accent); }
.pflow-node-time { font-size: var(--fs-label); font-weight: 700; color: var(--text-muted); letter-spacing: 0.02em; }
.pflow-node-icon { font-size: 20px; line-height: 1; margin: 2px 0; }
.pflow-node-label { font-size: var(--fs-meta); font-weight: 700; line-height: 1.3; }
.pflow-node-dot {
  position: absolute; top: 12px; right: 12px; width: 8px; height: 8px; border-radius: 50%;
  background: var(--text-muted);
}
.pflow-node-success .pflow-node-dot { background: var(--good); box-shadow: 0 0 0 3px color-mix(in srgb, var(--good) 25%, transparent); }
.pflow-node-failed .pflow-node-dot { background: var(--critical); box-shadow: 0 0 0 3px color-mix(in srgb, var(--critical) 25%, transparent); }
.pflow-node-never_run .pflow-node-dot { background: var(--text-muted); }
.pflow-node-unknown .pflow-node-dot { background: var(--text-muted); opacity: 0.4; }
.pflow-node-failed { border-color: color-mix(in srgb, var(--critical) 45%, var(--border)); }

.pflow-connector {
  flex: 0 0 36px; align-self: center; height: 3px; margin-top: -20px;
  background: repeating-linear-gradient(
    90deg,
    var(--accent) 0, var(--accent) 6px,
    transparent 6px, transparent 12px
  );
  background-size: 24px 3px;
  animation: pflow-connector-move 1.1s linear infinite;
  opacity: 0.55;
}
@keyframes pflow-connector-move {
  from { background-position: 0 0; }
  to { background-position: 24px 0; }
}

.pflow-node-detail {
  margin-top: 8px; padding: 10px 12px; background: var(--raised-bg);
  border: 1px solid var(--border); border-radius: 10px; font-size: var(--fs-meta);
}
.pflow-node-desc { margin: 0 0 6px; color: var(--text-secondary); line-height: 1.5; }
.pflow-node-meta { margin: 0 0 4px; color: var(--text-muted); font-size: var(--fs-label); }
.pflow-node-summary { margin: 0 0 6px; color: var(--text-secondary); font-size: var(--fs-meta); line-height: 1.5; }
.pflow-node-goto { font-size: var(--fs-meta); font-weight: 700; color: var(--accent); text-decoration: none; }
.pflow-node-goto:hover { text-decoration: underline; }

@media (max-width: 900px) {
  .pflow-diagram { flex-direction: column; align-items: stretch; }
  .pflow-node-wrap { width: 100%; }
  .pflow-connector { width: 3px; height: 20px; margin: 0 0 0 24px; background: repeating-linear-gradient(
      180deg, var(--accent) 0, var(--accent) 6px, transparent 6px, transparent 12px
    ); background-size: 3px 24px; animation: pflow-connector-move-v 1.1s linear infinite; }
}
@keyframes pflow-connector-move-v {
  from { background-position: 0 0; }
  to { background-position: 0 24px; }
}

/* ---- Assistant guidé Contenu (2026-07-31) - "je veux qu'il soit guidé à
   travers la plateforme" : stepper vanilla (pas de lib JS), un choix par
   écran, avance automatique dès qu'un choix est cliqué (sauf le sujet, en
   texte libre). ---- */
.wizard { max-width: 640px; }
.wizard-progress { display: flex; gap: 6px; margin-bottom: 22px; flex-wrap: wrap; }
.wizard-progress-step {
  font-size: var(--fs-label); font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em;
  color: var(--text-muted); padding: 4px 10px; border-radius: 999px; background: var(--raised-bg);
}
.wizard-progress-step.active { color: var(--accent-ink); background: var(--accent); }
.wizard-question { font-size: var(--fs-lead); font-weight: 700; margin: 0 0 14px; }
.wizard-choice-row { display: flex; flex-direction: column; gap: 10px; }
.wizard-choice {
  display: flex; align-items: center; gap: 10px; text-align: left; cursor: pointer;
  background: var(--panel-bg); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 14px 16px; font: inherit; color: var(--text-primary);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.wizard-choice:hover { border-color: color-mix(in srgb, var(--accent) 40%, var(--border)); }
.wizard-choice.selected { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, var(--panel-bg)); }
.wizard-format-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; }
.wizard-format-card {
  display: flex; flex-direction: column; align-items: flex-start; gap: 5px; text-align: left; cursor: pointer;
  background: var(--panel-bg); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 12px 14px; font: inherit; color: var(--text-primary);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.wizard-format-card:hover { border-color: color-mix(in srgb, var(--accent) 40%, var(--border)); }
.wizard-format-card.selected { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, var(--panel-bg)); }
.wizard-format-name { font-size: var(--fs-meta); font-weight: 700; }
.wizard-format-desc { font-size: var(--fs-label); color: var(--text-secondary); line-height: 1.45; }
.wizard-nav { margin-top: 18px; }

/* ---- Champs libres du wizard (2026-08-01, assistant "nouveau marché") -
   contenu.html avait un seul textarea stylé en inline ; celui-ci en a
   quatre (2 input + 2 textarea), une classe partagée plutôt que répéter le
   style à chaque champ. ---- */
.wizard-sub { font-size: var(--fs-meta); color: var(--text-secondary); margin: -8px 0 16px; line-height: 1.55; }
.wizard-field-label { display: block; font-size: var(--fs-label); font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); margin: 14px 0 6px; }
.wizard-field-label:first-of-type { margin-top: 0; }
.wizard-input, .wizard-textarea {
  width: 100%; box-sizing: border-box; padding: 10px 12px; border-radius: 8px;
  border: 1px solid var(--border); background: var(--raised-bg); color: var(--text-primary);
  font-family: inherit; font-size: var(--fs-meta);
}
.wizard-textarea { line-height: 1.6; resize: vertical; }
.wizard-input:focus, .wizard-textarea:focus { outline: none; border-color: var(--accent); }
.wizard-field-help { font-size: var(--fs-label); color: var(--text-muted); margin: 6px 0 0; line-height: 1.5; }
.wizard-review { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
.wizard-review-row { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 10px 12px; background: var(--raised-bg); border-radius: 8px; }
.wizard-review-label { font-size: var(--fs-label); color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; flex: none; }
.wizard-review-value { font-size: var(--fs-meta); color: var(--text-primary); text-align: right; white-space: pre-line; }
.wizard-entry {
  display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  padding: 14px 18px; margin-bottom: 20px; background: var(--raised-bg); border: 1px solid var(--border);
  border-radius: var(--radius);
}
.wizard-entry-text { font-size: var(--fs-meta); color: var(--text-secondary); }

/* ---- Signaux — onglet Configuration (2026-07-31) - mots-clés éditables par
   marché, un bloc par marché, chaque mot-clé un tag cliquable (actif/inactif)
   + un petit bouton de suppression séparé. ---- */
.signal-market-block { border-top: 1px solid var(--border); padding: 14px 0; }
.signal-market-block:first-of-type { border-top: none; padding-top: 0; }
.signal-market-head { display: flex; align-items: center; gap: 8px; }
.tag-clickable { cursor: pointer; font: inherit; }
.tag-delete-btn {
  border: none; background: none; color: var(--text-muted); cursor: pointer;
  font-size: var(--fs-label); padding: 2px 4px;
}
.tag-delete-btn:hover { color: var(--critical); }
.signal-add-form { display: flex; gap: 8px; margin-top: 10px; max-width: 440px; }
.signal-add-form input[type="text"] {
  flex: 1; padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border);
  background: var(--raised-bg); color: var(--text-primary); font-size: var(--fs-meta);
}

/* ---- Signaux — onglet Testeur (2026-07-31) ---- */
.signal-test-form { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.signal-test-form select, .signal-test-form input[type="text"] {
  padding: 7px 10px; border-radius: 6px; border: 1px solid var(--border);
  background: var(--raised-bg); color: var(--text-primary); font-size: var(--fs-meta);
}
.signal-test-form input[type="text"] { flex: 1; min-width: 220px; }

/* ---- Schéma /machine (2026-08-01) — deux chaînes de nœuds + passerelles
   entre chaînes. Préfixe mflow- (ni flow- ni pflow-, déjà pris - cf le
   commentaire au-dessus de .pflow-diagram sur la collision de préfixe déjà
   rencontrée le jour où deux panneaux de ce type ont fini sur la même page).
   Détail au clic en INLINE sous le nœud (même mécanique que .pflow-node-detail,
   pas un panneau latéral séparé) pour rester cohérent avec /flux plutôt que
   d'introduire un deuxième pattern d'interaction dans la même appli. ---- */
.mflow-legend {
  display: flex; flex-wrap: wrap; gap: 16px; margin: 14px 0 22px; padding: 12px 16px;
  background: var(--raised-bg); border: 1px solid var(--border); border-radius: var(--radius);
  font-size: var(--fs-meta); color: var(--text-secondary);
}
.mflow-legend-item { display: flex; align-items: center; gap: 7px; }
.mflow-legend-swatch { width: 13px; height: 13px; flex: none; border-radius: 4px; }
.mflow-legend-swatch.circle { border-radius: 50%; }
.mflow-legend-swatch.gate { border: 2px solid var(--critical); background: color-mix(in srgb, var(--critical) 16%, transparent); }
.mflow-legend-swatch.decision { border: 2px dashed var(--text-muted); background: none; }
.mflow-legend-line { width: 18px; height: 0; border-top: 2px solid var(--accent); flex: none; }
.mflow-legend-line.bridge { border-top: 2px dashed var(--warn); }

.mflow-engine { margin-bottom: 26px; }
.mflow-engine-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 12px; flex-wrap: wrap; }
.mflow-engine-name { font-size: var(--fs-lead); font-weight: 800; letter-spacing: -0.01em; }
.mflow-engine-tag {
  font-size: var(--fs-label); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  padding: 3px 8px; border-radius: 5px;
}
.mflow-engine-tag.acq { color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent); }
.mflow-engine-tag.rel { color: var(--accent-2); background: color-mix(in srgb, var(--accent-2) 14%, transparent); }
.mflow-engine-sub { color: var(--text-muted); font-size: var(--fs-meta); }

/* Grille qui s'enroule (flex-wrap) plutôt qu'une seule rangée qui déborde à
   l'infini — trouvé en usage réel (2026-08-01, Alexandre : "on doit scroller
   de gauche à droite... idéalement mieux réparti sur une page") : un scroll
   horizontal forcé sur 11 nœuds cassait aussi l'alignement des passerelles
   (calculées depuis la position DOM réelle des nœuds, fausse dès qu'une
   chaîne scrolle indépendamment de l'autre) et poussait toute la page à
   déborder horizontalement (légende/bandeau coupés au bord). Plus de
   connecteurs <div> entre nœuds adjacents : TOUS les traits (dans un moteur
   ET entre les deux) sont désormais des chemins SVG calculés en JS depuis la
   vraie position DOM de chaque nœud, avec routage orthogonal (coude à angle
   droit) quand deux nœuds consécutifs ne sont pas sur la même rangée - même
   technique que les passerelles, plus besoin de deux systèmes différents. */
.mflow-diagram { position: relative; }
.mflow-lane { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 22px 34px; position: relative; z-index: 2; }
.mflow-seg { display: flex; flex-direction: column; flex: none; width: 156px; }

.mflow-node {
  all: unset; cursor: pointer; width: 100%; min-height: 96px; box-sizing: border-box;
  background: var(--panel-bg); border: 1.5px solid var(--border); border-radius: var(--radius);
  padding: 11px 12px; display: flex; flex-direction: column; gap: 5px; position: relative;
  transition: border-color 0.15s ease, transform 0.15s ease;
}
.mflow-node:hover { border-color: color-mix(in srgb, var(--mflow-color, var(--accent)) 45%, var(--border)); transform: translateY(-2px); }
.mflow-node-active { border-color: var(--mflow-color, var(--accent)); }
.mflow-node-badge {
  position: absolute; top: 8px; right: 9px; font-size: var(--fs-micro); font-weight: 700;
  color: var(--text-muted); font-variant-numeric: tabular-nums;
}
.mflow-node-label { font-size: var(--fs-meta); font-weight: 700; letter-spacing: 0.01em; }
.mflow-node-hint { font-size: var(--fs-label); color: var(--text-muted); line-height: 1.35; }

.mflow-node.shape-source { border-radius: 999px; align-items: center; text-align: center; }
.mflow-node.shape-decision { border-style: dashed; }
.mflow-node.shape-gate {
  background: color-mix(in srgb, var(--critical) 10%, var(--panel-bg));
  border-width: 2px; border-color: var(--critical);
}
.mflow-node.shape-gate .mflow-node-label { color: var(--critical); }

.mflow-source-chips { display: flex; flex-wrap: wrap; gap: 4px; justify-content: center; margin-bottom: 2px; }
.mflow-src-chip {
  font-size: var(--fs-micro); font-weight: 600; padding: 3px 5px; border-radius: 5px;
  background: var(--raised-bg); color: var(--text-secondary); border: 1px solid var(--border);
}

.mflow-node-detail {
  margin-top: 8px; padding: 10px 11px; background: var(--raised-bg);
  border: 1px solid var(--border); border-radius: 10px; font-size: var(--fs-meta);
}
.mflow-node-desc { margin: 0 0 8px; color: var(--text-secondary); line-height: 1.55; }
.mflow-node-fact {
  display: flex; align-items: baseline; gap: 7px; padding: 8px 10px; background: var(--panel-bg);
  border-radius: 8px; margin-bottom: 7px; border-left: 2.5px solid var(--mflow-color, var(--accent));
}
.mflow-node-fact b { font-size: var(--fs-name); font-weight: 800; font-variant-numeric: tabular-nums; color: var(--mflow-color, var(--accent)); }
.mflow-node-fact span { font-size: var(--fs-label); color: var(--text-muted); }
.mflow-node-table { font-size: var(--fs-label); color: var(--text-muted); font-family: ui-monospace, "SF Mono", Consolas, monospace; }

.mflow-diagram-wrap { position: relative; }
svg.mflow-bridges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 1; overflow: visible; }
/* .primary = trait dans un même moteur (plein, couleur du moteur, discret -
   c'est le point qui voyage dessus qui porte l'attention, pas le trait).
   .bridge = passerelle entre les deux moteurs (pointillé orange, jamais
   confondu avec le flux principal). */
.mflow-path { fill: none; stroke-linecap: square; }
.mflow-path.primary {
  stroke: var(--mflow-path-color, var(--accent)); stroke-width: 1.5; opacity: 0.4;
}
.mflow-path.bridge {
  stroke: var(--warn); stroke-width: 1.5;
  stroke-dasharray: var(--len, 1000); stroke-dashoffset: var(--len, 1000);
  transition: stroke-dashoffset 1.1s cubic-bezier(.85, 0, .15, 1);
}
.mflow-path.bridge.drawn { stroke-dashoffset: 0; }
.mflow-path.bridge.settled { stroke-dasharray: 5,6; transition: none; }
.mflow-path-dot { fill: var(--mflow-path-color, var(--warn)); }
.mflow-path-label {
  position: absolute; transform: translate(-50%, -50%); z-index: 3;
  font-size: var(--fs-micro); font-weight: 600; color: var(--warn); background: var(--page-bg);
  padding: 3px 7px; border: 1px solid var(--warn); border-radius: 6px; max-width: 160px;
  text-align: center; pointer-events: none; font-family: ui-monospace, "SF Mono", Consolas, monospace;
}
.mflow-node-goto { display: inline-block; margin-top: 2px; font-size: var(--fs-meta); font-weight: 700; color: var(--mflow-color, var(--accent)); text-decoration: none; }
.mflow-node-goto:hover { text-decoration: underline; }

.mflow-gate-note {
  display: flex; align-items: flex-start; gap: 10px; margin: 4px 0 22px; padding: 12px 14px;
  border: 1px solid var(--critical); background: color-mix(in srgb, var(--critical) 10%, transparent);
  border-radius: var(--radius); font-size: var(--fs-meta);
}
.mflow-gate-note strong { color: var(--critical); }

/* ---- Compteur "live" du jour (2026-08-02, Alexandre : "combien de signaux
   trouvés jusqu'à présent, avec une indication live") - point qui pulse en
   continu (contrairement au reste du schéma, où l'animation reste discrète/
   ponctuelle) pour marquer clairement que CE nombre-là se met à jour tout
   seul, contrairement au reste de la page qui ne bouge qu'au clic. ---- */
.mflow-live-strip {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 0 0 18px;
  padding: 12px 16px; background: var(--raised-bg); border: 1px solid var(--border); border-radius: var(--radius);
}
.mflow-live-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--good); flex: none; position: relative; }
.mflow-live-dot::after {
  content: ""; position: absolute; inset: -5px; border-radius: 50%; border: 1.5px solid var(--good);
  animation: mflow-live-ping 1.8s cubic-bezier(0, 0, 0.2, 1) infinite;
}
@keyframes mflow-live-ping { 0% { opacity: 0.7; scale: 1; } 100% { opacity: 0; scale: 1.9; } }
.mflow-live-count { font-size: var(--fs-heading); font-weight: 800; font-variant-numeric: tabular-nums; color: var(--text-primary); }
.mflow-live-label { font-size: var(--fs-label); color: var(--text-muted); }
.mflow-live-breakdown { font-size: var(--fs-meta); color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.mflow-live-updated { font-size: var(--fs-micro); color: var(--text-muted); margin-left: auto; font-variant-numeric: tabular-nums; }
@media (prefers-reduced-motion: reduce) { .mflow-live-dot::after { animation: none; } }

/* ---- Horaire réel d'une étape (2026-08-02) - lu depuis job_runs via
   pipeline_flow_by_label(), affiché dans le detail d'un nœud. Même
   convention de couleur que .job-status-dot/.pflow-node-dot ailleurs
   (vert=succès, rouge=échec, gris=jamais lancé) plutôt qu'une nouvelle
   palette de statut. ---- */
.mflow-node-schedule {
  display: flex; align-items: center; gap: 6px; font-size: var(--fs-label); color: var(--text-muted);
  margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border);
}
.mflow-node-schedule .job-status-dot { flex: none; width: 6px; height: 6px; border-radius: 50%; background: var(--text-muted); }

@media (prefers-reduced-motion: reduce) {
  .mflow-path.bridge, .mflow-node { transition: none !important; }
  .mflow-path.bridge { stroke-dashoffset: 0 !important; stroke-dasharray: 5,6 !important; }
}

/* ---- Accordéon borné (2026-08-02, v2) - remplace l'accordéon v1 du même
   jour (6 onglets de /historique-client, puis généralisé à Signaux/Contenu/
   Profil). Retour d'Alexandre sur v1 : "beaucoup trop long à défiler". Le
   mécanisme (glissement 0fr -> 1fr, plusieurs sections ouvertes en même
   temps) est conservé tel quel ; ce qui change, c'est que le corps d'une
   section n'ajoute plus sa hauteur RÉELLE à la page. Mesuré avant correctif :
   "Devis à relancer" ouverte = ~24 000px (380 lignes x 63px), soit ~27
   écrans à franchir avant d'atteindre l'en-tête de la 6e section. Trois
   verrous :
     1. --acc-cap : le corps plafonne à ~1 écran et défile chez lui.
     2. .acc-head sticky : l'en-tête reste épinglé pendant qu'on défile
        dedans (d'où overflow:clip sur .acc-section et non hidden - hidden
        crée un conteneur de défilement et neutralise sticky).
     3. data-acc-chunk (base.html) : on ne rend pas 380 lignes d'un coup,
        sinon le plafond ne fait que déplacer les 24 000px dans une barre de
        défilement de 15px.
   Petites sections : max-height ne mord jamais, elles gardent leur hauteur
   naturelle (aucun vide artificiel). Le dépliage PAR ITEM à l'intérieur
   (chaque .quote-row) reste le <details> natif, inchangé. */
.acc-list {
  display: flex; flex-direction: column; gap: 10px; margin-top: 4px;
  --acc-cap: 62vh;
  --acc-head-h: 48px;
}
.acc-section {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--panel-bg);
  overflow: clip; /* pas hidden : casserait le sticky de .acc-head */
}

.acc-head {
  all: unset;
  box-sizing: border-box; width: 100%; min-height: var(--acc-head-h);
  cursor: pointer; display: flex; align-items: center; gap: 10px;
  padding: 13px 18px; font: inherit; color: var(--text-primary);
  background: var(--panel-bg);
  position: sticky; top: 0; z-index: 3;
}
.acc-head:hover { background: color-mix(in srgb, var(--accent) 8%, var(--panel-bg)); }
.acc-head:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.acc-section.open > .acc-head { border-bottom: 1px solid var(--border); }

.acc-label { font-size: var(--fs-name); font-weight: 700; flex: none; }
.acc-count {
  flex: none; font-size: var(--fs-label); font-weight: 800;
  font-variant-numeric: tabular-nums; color: var(--text-secondary);
  background: var(--raised-bg); padding: 2px 8px; border-radius: 999px;
}
.acc-hint {
  font-size: var(--fs-label); color: var(--text-muted); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.acc-chevron { margin-left: auto; color: var(--text-muted); transition: transform 0.25s ease; flex: none; }
.acc-section.open .acc-chevron { transform: rotate(180deg); }

.acc-body { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.32s cubic-bezier(0.4, 0, 0.2, 1); }
.acc-section.open > .acc-body { grid-template-rows: 1fr; }
/* min-width: 0 (2026-08-06) : .acc-body-inner est l'item d'un conteneur grid
   (.acc-body) - par défaut un item grid refuse de rétrécir sous la largeur
   min-content de son contenu (min-width:auto implicite), invisible tant que
   rien à l'intérieur n'est plus large que la page. Le défilement horizontal
   de "Brouillons prêts" (.content-hscroll) l'a révélé : sans ce correctif,
   .acc-body-inner grandissait pour accueillir les 10 cartes à plat (~3700px)
   au lieu de les laisser déborder DANS .content-hscroll - scrollWidth ==
   clientWidth, donc plus rien à faire défiler. Même symétrie que min-height:0
   ci-dessous (déjà nécessaire pour l'animation grid-template-rows), jamais
   requis avant côté largeur faute de contenu assez large pour l'exposer. */
.acc-body-inner { overflow: clip; min-height: 0; min-width: 0; }

/* Barre d'outils : filtres / recherche / pagination. Ne défile PAS avec la
   liste qu'elle pilote (avant, la pagination du Répertoire était sous
   4 200px de lignes). Toujours avant .acc-scroll, jamais dedans. */
.acc-tools {
  position: sticky; top: var(--acc-head-h); z-index: 2;
  background: var(--panel-bg);
  padding: 14px 18px; border-bottom: 1px solid var(--border);
}
.acc-tools > :last-child { margin-bottom: 0; }

/* Le plafond. max-height, pas height : sans effet sur une section courte.
   Affordance renforcée (2026-08-02, retour direct : "pourquoi c'est coupé" -
   la section EST scrollable (vérifié : scrollHeight 2158 vs clientHeight 496
   sur un cas réel), le défilement fonctionnait déjà, mais rien ne le
   signalait assez fort pour être remarqué - scrollbar "thin" quasi invisible
   sur fond clair + ombre à 14% d'opacité, ça se lit comme "coupé/cassé" pas
   comme "défile ici". Trois renforts : scrollbar pleine largeur (plus
   "thin"), teinte de repos plus soutenue (--baseline au lieu de --gridline,
   visible sans survol), et un texte explicite en ::after qui reste collé en
   bas de la zone tant qu'il reste du contenu à voir (position:sticky,
   disparaît naturellement une fois arrivé en bas puisqu'il devient alors le
   dernier élément normal du flux, pas une bulle flottante à part). */
.acc-scroll {
  max-height: var(--acc-cap);
  overflow-y: auto;
  overscroll-behavior: contain; /* "on stop si on veut" : pas d'enchaînement */
  scrollbar-gutter: stable;
  scrollbar-color: var(--baseline) var(--raised-bg);
}
.acc-scroll.is-scrollable { box-shadow: inset 0 -20px 16px -18px rgba(0, 0, 0, 0.22); }
.acc-scroll::-webkit-scrollbar { width: 12px; }
.acc-scroll::-webkit-scrollbar-track { background: var(--raised-bg); }
.acc-scroll::-webkit-scrollbar-thumb {
  background: var(--baseline); border-radius: 999px;
  border: 3px solid var(--panel-bg); background-clip: padding-box;
}
.acc-scroll::-webkit-scrollbar-thumb:hover { background: var(--text-muted); background-clip: padding-box; }
.acc-scroll.is-scrollable::after {
  content: "↓ Fais défiler ici pour voir la suite";
  position: sticky; bottom: 0; left: 0; right: 0;
  display: block; margin-top: 10px;
  padding: 7px 12px; text-align: center;
  font-size: var(--fs-label); font-weight: 700; color: var(--accent);
  background: color-mix(in srgb, var(--accent) 9%, var(--panel-bg));
  border-top: 1px solid color-mix(in srgb, var(--accent) 25%, var(--border));
}

.acc-body-pad { padding: 16px 18px 18px; }

/* .panel imbriqué dans un accordéon : la .acc-section EST déjà le panneau.
   Le double cadre coûtait du chrome inutile (Chiffres empile 8 .panel). */
.acc-body-pad .panel { background: none; border: none; border-radius: 0; padding: 0; margin-bottom: 26px; }
.acc-body-pad .panel + .panel { border-top: 1px solid var(--border); padding-top: 24px; }
.acc-body-pad .panel:last-child { margin-bottom: 0; }
.acc-scroll .campaign-panel { top: 0; }

/* Révélation par paquets - voir base.html. */
.acc-more {
  all: unset; box-sizing: border-box; width: 100%; cursor: pointer;
  text-align: center; padding: 11px 14px; margin-top: 6px;
  border: 1px dashed var(--border); border-radius: 10px;
  font-size: var(--fs-meta); font-weight: 700; color: var(--accent);
}
.acc-more:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.acc-more:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
[data-acc-chunk] > [hidden] { display: none !important; }

@media (max-width: 900px) {
  .acc-list { --acc-cap: 70vh; }
  .acc-hint { display: none; }
  /* 2026-08-14, audit UX/accès ("batch track/market context invisible on
     mobile at the validation gate") - .acc-hint a été pensé pour un
     sous-titre explicatif secondaire (ex. "Silencieux depuis 7 jours et
     plus" sur /historique-client), sans conséquence à masquer sous 900px.
     lots.html détourne la même classe pour la piste/le marché/la date d'un
     lot - une donnée dont Chris a besoin pour approuver un envoi réel en
     connaissance de cause, jamais un simple sous-titre. Exemption ciblée
     plutôt que de changer le comportement par défaut de .acc-hint partout. */
  .acc-hint.acc-hint-keep { display: inline; }
}
@media (prefers-reduced-motion: reduce) {
  .acc-body, .acc-chevron { transition: none !important; }
  .acc-more, .acc-head { transition: none !important; }
}

/* ==========================================================================
   Radar studios (Dragon Radar, 2026-08-16)

   Les couleurs reprennent les tokens existants plutot que d'en introduire de
   nouveaux : --series-outsourcing et --series-dedicated portent deja la
   distinction des deux pistes ailleurs dans l'appli, et les reutiliser ici
   garde une seule signification par couleur sur tout le dashboard.
   ========================================================================== */

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.92rem;
}
.data-table thead th {
  text-align: left;
  font-weight: 600;
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--gridline);
  white-space: nowrap;
}
.data-table tbody td {
  padding: 0.75rem;
  border-bottom: 1px solid var(--gridline);
  vertical-align: top;
  color: var(--text-primary);
}
.data-table tbody tr:last-child td { border-bottom: none; }
.data-table .num { text-align: right; font-variant-numeric: tabular-nums; }

/* Bande accentuee a gauche plutot qu'un fond colore : le fond aurait reduit
   le contraste du texte de la ligne, deja calibre a 4.5:1 sur --panel-bg. */
.data-table tr.row-hot td:first-child {
  box-shadow: inset 3px 0 0 0 var(--accent);
}

.cell-sub {
  font-size: 0.8rem;
  color: var(--text-muted);
  margin-top: 0.2rem;
  line-height: 1.4;
}
.cell-quote { font-style: italic; color: var(--text-secondary); }
.cell-warn  { color: var(--accent-2); }
.cell-lead  { color: var(--text-secondary); }

.chip {
  display: inline-block;
  padding: 0.15rem 0.5rem;
  margin: 0 0.25rem 0.25rem 0;
  border-radius: 999px;
  background: var(--raised-bg);
  border: 1px solid var(--border);
  font-size: 0.78rem;
  white-space: nowrap;
}

/* Texte blanc sur les deux teintes de serie : toutes deux sont assez foncees
   pour tenir 4.5:1 en blanc (meme calibrage que .btn-send sur --accent). */
.badge-outsourcing,
.badge-dedicated {
  display: inline-block;
  padding: 0.2rem 0.55rem;
  border-radius: 4px;
  font-size: 0.78rem;
  font-weight: 600;
  color: #ffffff;
  white-space: nowrap;
}
.badge-outsourcing { background: var(--series-outsourcing); }
.badge-dedicated   { background: var(--series-dedicated); }

.stat-hint {
  font-size: 0.75rem;
  color: var(--text-muted);
  margin-top: 0.15rem;
}

.alert-warn {
  margin: 0.75rem 0 1rem;
  padding: 0.75rem 1rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent-2);
  background: var(--raised-bg);
  color: var(--text-secondary);
  font-size: 0.88rem;
  line-height: 1.5;
}
.alert-warn strong { color: var(--text-primary); }

@media (max-width: 900px) {
  /* Un tableau a 6 colonnes ne se replie pas proprement : on le fait defiler
     dans son propre conteneur plutot que de laisser la page deborder. */
  .data-table { display: block; overflow-x: auto; white-space: nowrap; }
  .data-table .cell-sub { white-space: normal; }
}

/* ==========================================================================
   Fiche studio (Dragon Radar, 2026-08-16)
   La page de demonstration : offres publiques -> discipline -> portfolio ->
   emails. Le rendu des emails doit ressembler a un email, pas a un champ de
   formulaire : c'est ce que le lecteur doit pouvoir juger.
   ========================================================================== */

.back-link {
  display: inline-block;
  margin-bottom: 0.5rem;
  font-size: 0.85rem;
  color: var(--text-muted);
  text-decoration: none;
}
.back-link:hover { color: var(--accent); }

.stat-unit { font-size: 0.6em; font-weight: 400; color: var(--text-muted); }

/* Ligne grisee : presente pour la transparence (on montre ce qui a ete
   ecarte et pourquoi) mais visuellement en retrait. */
.data-table tr.row-muted td { opacity: 0.62; }

.portfolio-card {
  margin-top: 1rem;
  padding: 0.9rem 1.1rem;
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: 6px;
  background: var(--raised-bg);
}
.portfolio-head {
  display: flex;
  gap: 0.75rem;
  align-items: baseline;
  flex-wrap: wrap;
  margin-bottom: 0.35rem;
}
.portfolio-line {
  margin-top: 0.6rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--gridline);
  font-size: 0.88rem;
  font-style: italic;
  color: var(--text-secondary);
}

.email-card {
  margin: 1rem 0;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface-1);
  overflow: hidden;
}
.email-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding: 0.5rem 1rem;
  background: var(--raised-bg);
  border-bottom: 1px solid var(--gridline);
  font-size: 0.8rem;
}
.email-step { font-weight: 600; color: var(--text-primary); }
.email-when { color: var(--text-muted); }
.email-subject {
  padding: 0.75rem 1rem 0.25rem;
  font-weight: 600;
  color: var(--text-primary);
}
.email-field {
  display: inline-block;
  margin-right: 0.5rem;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-muted);
}
/* pre plutot qu'un rendu HTML : le corps doit s'afficher EXACTEMENT tel
   qu'il partira, sauts de ligne compris. white-space: pre-wrap pour que les
   longues lignes se replient au lieu de deborder. */
.email-body {
  margin: 0;
  padding: 0.5rem 1rem 1rem;
  font-family: inherit;
  font-size: 0.9rem;
  line-height: 1.55;
  white-space: pre-wrap;
  word-break: break-word;
  color: var(--text-primary);
}

/* Barre de filtre du radar studios (2026-08-16) */
.filter-bar {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  flex-wrap: wrap;
  margin: 0.75rem 0 1rem;
}
.filter-chip {
  padding: 0.3rem 0.75rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--panel-bg);
  color: var(--text-secondary);
  font-size: 0.85rem;
  text-decoration: none;
}
.filter-chip:hover { border-color: var(--accent); color: var(--accent); }
.filter-chip.is-active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
}
.filter-hint { font-size: 0.78rem; color: var(--text-muted); }

/* Board dormant : signale sans masquer. --accent-2 tient 4.5:1 sur les trois
   fonds clairs de l'appli, comme .cell-warn qui l'utilise deja. */
.cell-stale { color: var(--accent-2); font-weight: 600; }

/* ==========================================================================
   Carte mondiale des signaux (2026-08-16)

   Le SVG contient les continents ET les marqueurs : meme systeme de
   coordonnees, donc alignement garanti. Aucune librairie front.
   ========================================================================== */

.map-wrap {
  position: relative;
  margin: 0.5rem 0 0.25rem;
  border-radius: 8px;
  border: 1px solid var(--gridline);
  background: #f7f8fa;
  overflow: hidden;
}
.world-map { display: block; width: 100%; height: auto; }

/* Les continents restent en retrait : ce sont les points qui portent
   l'information, la carte n'est qu'un repere. */
/* Les continents doivent se LIRE. Premiere version : fill --panel-bg (blanc)
   sur un fond --raised-bg (gris tres clair) - a la capture, les masses
   terrestres etaient quasi invisibles et la carte paraissait vide. Le
   rapport est inverse ici : terres en gris moyen sur fond clair. */
.world-land {
  fill: #d9dce4;
  stroke: #c3c7d2;
  stroke-width: 0.5;
}

.map-dot {
  fill-opacity: 0.85;
  stroke: #ffffff;
  stroke-width: 1.4;
  cursor: pointer;
  transition: fill-opacity 0.15s ease;
}
.map-dot:hover, .map-dot:focus { fill-opacity: 1; outline: none; }
.map-dot:focus { stroke: var(--text-primary); stroke-width: 2; }

.map-outsourcing { fill: var(--series-outsourcing); }
.map-dedicated   { fill: var(--series-dedicated); }

/* Halo pulsant sur les lieux ou une offre a ete publiee recemment. Le
   decalage d'animation par point evite un clignotement synchronise, qui
   ferait sapin de Noel plutot que radar. */
/* transform-box: fill-box est INDISPENSABLE ici. Sans lui, `transform-origin:
   center` se resout contre le viewport SVG et non contre le cercle : les
   halos derivaient vers le centre de la carte en grandissant, produisant de
   grands cercles vides au nord du Canada et au Kazakhstan, sans aucun point
   au centre. Trouve a la capture d'ecran, invisible dans le HTML rendu. */
.map-pulse {
  fill: none;
  stroke-width: 1.5;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: map-pulse 3.2s ease-out infinite;
}
.map-outsourcing.map-pulse { stroke: var(--series-outsourcing); }
.map-dedicated.map-pulse   { stroke: var(--series-dedicated); }

@keyframes map-pulse {
  0%   { opacity: 0.55; transform: scale(1); }
  70%  { opacity: 0;    transform: scale(2.6); }
  100% { opacity: 0;    transform: scale(2.6); }
}

/* Respect de prefers-reduced-motion : une carte qui pulse en permanence est
   penible pour qui y est sensible, et l'information (recent ou non) reste
   portee par l'infobulle et le tableau. */
@media (prefers-reduced-motion: reduce) {
  .map-pulse { animation: none; opacity: 0.35; }
}

.map-tip {
  position: absolute;
  z-index: 5;
  transform: translate(-50%, calc(-100% - 12px));
  min-width: 180px;
  max-width: 260px;
  padding: 0.6rem 0.75rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--panel-bg);
  box-shadow: 0 6px 20px rgba(20, 22, 28, 0.16);
  font-size: 0.82rem;
  pointer-events: none;
}
.map-tip.flip { transform: translate(-100%, calc(-100% - 12px)); }
.tip-head { font-weight: 600; color: var(--text-primary); }
.tip-approx { font-weight: 400; color: var(--text-muted); font-size: 0.9em; }
.tip-count { color: var(--text-secondary); margin-top: 0.1rem; }
.tip-studios { color: var(--text-muted); margin-top: 0.3rem; }
.tip-roles {
  margin: 0.4rem 0 0;
  padding-left: 1rem;
  color: var(--text-secondary);
  line-height: 1.4;
}
.tip-roles li { margin-bottom: 0.1rem; }

.legend-inline {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.78rem;
  color: var(--text-muted);
}
.legend-dot {
  display: inline-block;
  width: 9px; height: 9px;
  border-radius: 50%;
  margin-left: 0.5rem;
}
.legend-outsourcing { background: var(--series-outsourcing); }
.legend-dedicated   { background: var(--series-dedicated); }
.legend-pulse {
  display: inline-block;
  width: 9px; height: 9px;
  border-radius: 50%;
  margin-left: 0.5rem;
  border: 1.5px solid var(--text-muted);
}

/* Carte du flux quotidien : PLEINE TAILLE (2026-08-16, Alexandre : "tu as
   diminue la taille de la carte... tu peux la faire plus grande et chevaucher
   des informations dessus"). Le plafond a 340px d'origine ecrasait la carte
   au point de rendre les marqueurs europeens illisibles - or c'est la zone la
   plus dense. Les chiffres passent EN SURIMPRESSION plutot qu'au-dessus, ce
   qui rend la place a la carte sans perdre l'information. */
.map-wrap.map-compact { background: #f7f8fa; }

.map-overlay {
  position: absolute;
  z-index: 3;
  pointer-events: none;
  border-radius: 8px;
  /* Fond translucide + flou : les marqueurs continuent d'exister derriere,
     la carte ne parait pas amputee d'un coin. Repli solide si le navigateur
     ne gere pas backdrop-filter. */
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: blur(8px);
  border: 1px solid var(--gridline);
  box-shadow: 0 2px 10px rgba(20, 22, 28, 0.06);
  padding: 0.7rem 0.9rem;
}
@supports not (backdrop-filter: blur(8px)) {
  .map-overlay { background: rgba(255, 255, 255, 0.96); }
}

.map-overlay-tl { top: 0.85rem; left: 0.85rem; }
.map-overlay-bl { bottom: 0.85rem; left: 0.85rem; }
.map-overlay-tr { top: 0.85rem; right: 0.85rem; }

.map-stat { display: flex; align-items: baseline; gap: 0.4rem; }
.map-stat + .map-stat { margin-top: 0.35rem; }
.map-stat-n {
  font-size: 1.5rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
  line-height: 1.1;
}
.map-stat-k { font-size: 0.78rem; color: var(--text-muted); }
.map-stat-fresh .map-stat-n { color: var(--accent); }

.map-overlay-title {
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 0.35rem;
}
.map-poles { list-style: none; margin: 0; padding: 0; }
.map-poles li {
  display: flex;
  justify-content: space-between;
  gap: 1.25rem;
  font-size: 0.82rem;
  color: var(--text-secondary);
  padding: 0.12rem 0;
}
.map-poles b { color: var(--text-primary); font-variant-numeric: tabular-nums; }

.map-legend-v { font-size: 0.76rem; color: var(--text-muted); line-height: 1.7; }
/* legend-pulse porte un margin-left dans la legende horizontale de /carte ;
   ici il faut l'inverse, sinon le rond se colle au texte et la ligne se lit
   "Orecrutement en cours". */
.map-legend-v span.legend-dot,
.map-legend-v span.legend-pulse { margin: 0 0.4rem 0 0; vertical-align: baseline; }

/* Sous 720px, les surimpressions mangeraient la carte : elles repassent en
   dessous, en flux normal. */
@media (max-width: 720px) {
  .map-overlay {
    position: static;
    background: var(--raised-bg);
    backdrop-filter: none;
    box-shadow: none;
    margin-top: 0.6rem;
  }
}

.panel-link {
  font-size: 0.82rem;
  color: var(--accent);
  text-decoration: none;
  white-space: nowrap;
}
.panel-link:hover { text-decoration: underline; }

/* ==========================================================================
   Statistiques (2026-08-16)
   Barres en CSS pur : ce sont des parts d'un total, une barre horizontale les
   lit mieux qu'un camembert et ne coute aucune dependance.
   ========================================================================== */

.stat-columns {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: 1rem;
  align-items: start;
}
/* min-width: 0 sur les elements de grille. Sans lui, un enfant de grille
   garde `min-width: auto`, donc sa largeur MIN-CONTENT impose la taille de
   la colonne : sur un ecran de 390px, le tableau interne poussait le panneau
   a 461px et faisait defiler la page entiere horizontalement. Le
   `grid-template-columns: 1fr` du media query ne suffisait pas, il ne
   contraint que la piste, pas le contenu. */
.stat-columns > * { min-width: 0; }

.stat-bars { margin-top: 0.5rem; }
.stat-bar-row {
  display: grid;
  grid-template-columns: minmax(110px, 190px) 1fr auto;
  align-items: center;
  gap: 0.75rem;
  padding: 0.3rem 0;
}
.stat-bar-label {
  font-size: 0.85rem;
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.stat-bar-track {
  height: 9px;
  border-radius: 999px;
  background: var(--raised-bg);
  overflow: hidden;
}
.stat-bar-fill {
  height: 100%;
  border-radius: 999px;
  /* Largeur minimale : une part a 0.5% doit rester visible, sinon la ligne
     parait vide et on croit a une erreur d'affichage. */
  min-width: 3px;
  transition: width 0.4s ease;
}
.stat-bar-accent  { background: var(--series-outsourcing); }
.stat-bar-accent2 { background: var(--series-dedicated); }
.stat-bar-neutral { background: var(--baseline); }

.stat-bar-value {
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
  white-space: nowrap;
}
.stat-bar-pct {
  display: inline-block;
  min-width: 48px;
  text-align: right;
  color: var(--text-muted);
  font-size: 0.78rem;
}

/* Consigne sous une tuile de statistique : dit POURQUOI un chiffre est bas
   plutot que de laisser lire un echec la ou il n'y a qu'une cle manquante. */
.stat-tile-note {
  margin-top: 0.35rem;
  font-size: 0.72rem;
  line-height: 1.35;
  color: var(--accent-2);
}

/* ==========================================================================
   Vue mobile (2026-08-16, Alexandre : "adapte tout pour vue sur portable")
   Audit mene a 390px sur les six pages, corrections tirees de ce qui cassait
   reellement et non de suppositions.
   ========================================================================== */
@media (max-width: 640px) {
  .page { padding: 16px 14px 40px; }

  /* Bandeau DRY_RUN : il debordait a droite, le texte etant sur une seule
     ligne insecable dans une pastille a rayon fixe. */
  .dry-run-banner,
  .banner-dry-run {
    white-space: normal;
    line-height: 1.4;
    border-radius: 8px;
    text-align: left;
  }

  /* Tuiles de statistiques : deux colonnes plutot qu'une grille auto qui
     produisait des cartes de hauteurs tres inegales. */
  .stat-row { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .stat-value { font-size: 1.5rem; }
  .stat-tile { padding: 12px; }

  /* Une seule colonne pour les paires de panneaux des statistiques. */
  .stat-columns { grid-template-columns: 1fr; }

  /* Barres de repartition : le libelle prenait la moitie de la largeur et
     ecrasait la barre. Il passe au-dessus. */
  .stat-bar-row {
    grid-template-columns: 1fr auto;
    grid-template-areas: "label value" "track track";
    gap: 0.25rem 0.5rem;
  }
  .stat-bar-label { grid-area: label; white-space: normal; }
  .stat-bar-value { grid-area: value; }
  .stat-bar-track { grid-area: track; }

  /* Carte : les surimpressions repassent en flux normal sous 720px (regle
     existante) ; ici on reduit encore la hauteur utile pour qu'elle ne
     mange pas tout l'ecran d'accueil. */
  .map-wrap .world-map { min-height: 200px; }

  /* Barre de filtre : les puces passent a la ligne proprement au lieu de
     compresser la mention explicative. */
  .filter-bar { gap: 0.4rem; }
  .filter-hint { flex-basis: 100%; }

  /* Emails de la fiche studio : le corps en pre doit rester lisible sans
     zoom, et ne jamais forcer un defilement horizontal de page. */
  .email-body { font-size: 0.82rem; padding: 0.5rem 0.75rem 0.85rem; }
  .email-subject { padding: 0.6rem 0.75rem 0.2rem; font-size: 0.92rem; }
  .email-head { padding: 0.45rem 0.75rem; }

  /* Titre + lien d'un panneau : le lien passait a la ligne colle au titre. */
  .panel-title-row { flex-wrap: wrap; gap: 0.25rem 0.75rem; }

  /* Cible tactile : 44px de haut minimum sur les liens de navigation, sinon
     ils sont trop serres au doigt une fois le tiroir ouvert. */
  .side-nav a { min-height: 44px; display: flex; align-items: center; }
  .filter-chip { min-height: 38px; display: inline-flex; align-items: center; }
}
