/* ══════════════════════════════════════════════════════════════════════════════
   Jetons de dimension  (G · identité premium)
   ══════════════════════════════════════════════════════════════════════════════

   Les couleurs étaient déjà en propriétés personnalisées, aucune dimension ne
   l'était. Mesuré à la revue du 2026-07-26 : 80 tailles de police distinctes sur
   l'ensemble du site, 149 valeurs de `padding`, 17 rayons. Dans cette feuille
   seule, cinq tailles tenaient dans un intervalle de 0,05 rem (.76 .77 .78 .79
   .81), c'est-à-dire visuellement identiques et choisies une par une : la
   signature d'une absence d'échelle plutôt que d'un choix.

   Les jetons sont dans `:root` et non dans les blocs de thème : une dimension ne
   dépend pas du thème, seules les couleurs en dépendent.

   Échelle typographique : pas de 1 px de 10 à 16 px, exprimée en rem pour
   respecter la taille de police choisie par l'utilisateur dans son navigateur.
   Un pas plus large sonnerait mieux en théorie, mais l'application est dense en
   tableaux et a réellement besoin de ces paliers rapprochés.

   Échelle d'espacement : multiples de 4 px, la trame qui colle aux composants
   Bootstrap déjà en place.
   ────────────────────────────────────────────────────────────────────────────── */
:root {
  /* Typographie */
  --fs-4xs: .625rem;    /* 10px, mentions de bas de tableau */
  --fs-3xs: .6875rem;   /* 11px, la taille la plus fréquente du site */
  --fs-2xs: .75rem;     /* 12px */
  --fs-xs:  .8125rem;   /* 13px */
  --fs-sm:  .875rem;    /* 14px, corps de tableau */
  --fs-md:  .9375rem;   /* 15px */
  --fs-base: 1rem;      /* 16px, corps de texte */
  --fs-lg:  1.125rem;   /* 18px, sous-titres */
  --fs-xl:  1.5rem;     /* 24px, titres de page */

  /* Espacement, multiples de 4 */
  --sp-1: 2px;  --sp-2: 4px;  --sp-3: 6px;  --sp-4: 8px;
  --sp-5: 12px; --sp-6: 16px; --sp-7: 24px; --sp-8: 32px;

  /* Rayons */
  --r-sm: 4px; --r-md: 8px; --r-lg: 12px; --r-xl: 20px; --r-pill: 999px;

  /* Ombres, une seule direction de lumière */
  --sh-sm: 0 1px 2px rgba(0,0,0,.10);
  --sh-md: 0 2px 8px rgba(0,0,0,.14);
  --sh-lg: 0 8px 28px rgba(0,0,0,.22);

  /* Mouvement sobre : deux durées, une seule courbe */
  --t-fast: .12s cubic-bezier(.4,0,.2,1);
  --t-base: .2s  cubic-bezier(.4,0,.2,1);
}

/* ── Thèmes ─────────────────────────────────────────────────────────────────── */

[data-theme="dark"] {
  --bg:        #0f172a;
  --bg2:       #1e293b;
  --bg3:       #253348;
  --card-bg:   #1e293b;
  --border:    #334155;
  --text:      #e2e8f0;
  --text2:     #94a3b8;
  --text3:     #64748b;
  --primary:   #38bdf8;
  --success:   #4ade80;
  --danger:    #f87171;
  --warning:   #fbbf24;
  --card-shadow: 0 2px 8px rgba(0,0,0,.4);
  --accent:    #6366f1;
  --text-muted:#94a3b8;
  --row-hover: rgba(255,255,255,.04);
  --accent-soft: rgba(99,102,241,.12);
  /* Les trois roles de MARQUE. Ils n'existaient que dans le bloc
     `<style>` de la page d'accueil, donc le logo de l'application ne
     pouvait pas les lire : il portait un vert ecrit en dur, et les cinq
     pages d'authentification un indigo ecrit en dur. Les declarer ici
     est ce qui fait suivre le logo au theme ET a la palette. */
  --brand:        #6366f1;
  --brand-strong: #4f46e5;
  --brand-ink:    #c7d2fe;
}

[data-theme="light"] {
  --bg:        #f1f5f9;
  --bg2:       #ffffff;
  --bg3:       #e2e8f0;
  --card-bg:   #ffffff;
  --border:    #cbd5e1;
  --text:      #0f172a;
  --text2:     #475569;
  --text3:     #94a3b8;
  --primary:   #0284c7;
  --success:   #16a34a;
  --danger:    #dc2626;
  --warning:   #d97706;
  --card-shadow: 0 2px 8px rgba(0,0,0,.08);
  --accent:    #6366f1;
  --text-muted:#64748b;
  --row-hover: rgba(0,0,0,.04);
  --accent-soft: rgba(99,102,241,.08);
  --brand:        #6366f1;
  --brand-strong: #4338ca;
  --brand-ink:    #4f46e5;
}

[data-theme="beige"] {
  --bg:        #f5f0e8;
  --bg2:       #faf7f2;
  --bg3:       #ede8df;
  --card-bg:   #faf7f2;
  --border:    #d4c9b8;
  --text:      #2c2416;
  --text2:     #6b5c44;
  --text3:     #9c8b74;
  --primary:   #b45309;
  --success:   #15803d;
  --danger:    #b91c1c;
  --warning:   #92400e;
  --card-shadow: 0 2px 8px rgba(0,0,0,.06);
  --accent:    #7c3aed;
  --text-muted:#9c8b74;
  --row-hover: rgba(0,0,0,.04);
  --accent-soft: rgba(124,58,237,.08);
  --brand:        #7c3aed;
  --brand-strong: #4a3cc0;
  --brand-ink:    #5a4bd4;
}

/* ── Base ───────────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }

/* Le fond de page est porte par <html> et NON par <body>, et body reste
   transparent. Raison : le fond anime (_neuralbg.html) est un canevas en
   z-index -1, qui se peint apres le fond de l'element racine mais AVANT celui de
   body. Un fond opaque sur body le rendrait donc purement invisible. En le
   portant sur html, le canevas reste visible et tout le contenu passe au-dessus
   sans qu'aucune page n'ait a se donner un z-index. */
html {
  background:  var(--bg);
  transition:  background .2s;
}

body {
  background:  transparent;
  color:       var(--text);
  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
  font-size: var(--fs-sm);
  line-height: 1.5;
  transition:  color .2s;
}

/* ── Navbar ─────────────────────────────────────────────────────────────────── */
.navbar {
  background:   var(--bg2) !important;
  border-bottom: 1px solid var(--border);
  padding:      .6rem 1.5rem;
}
.navbar-brand {
  color:       var(--primary) !important;
  font-weight: 700;
  font-size: var(--fs-lg);
  display:     flex !important;
  align-items: center;
  gap:         9px;
  line-height: 1;
}

/* ── Logo Auralfa : Lettre A + Triangle impossible (Penrose) ─────── */
.aur-logo-wrap {
  display: flex;
  align-items: center;
  gap: 8px;
}
/* Le SVG principal : figé, aucun mouvement global */
.aur-logo {
  flex-shrink: 0;
  overflow: visible;
}
/* ─── Les trois faces : TROIS NUANCES DERIVEES de la couleur de marque ────
   Elles portaient trois verts ecrits ici (#b8f060 / #5db81a / #2d5c0a), donc le
   logo restait vert quelle que soit la palette, pendant que les cinq pages
   d'authentification en affichaient un indigo ecrit en dur.

   Les nuances sont declarees par `_logo.html`, en style en ligne, et cette
   feuille ne fait que les LIRE : la page d'accueil ne la charge pas, donc les
   taux de melange y seraient ecrits une seconde fois et finiraient par diverger.
   Le raisonnement complet et les deux versions refusees par la mesure sont dans
   le partiel.

   Chaque segment cycle sur les trois nuances, decale d'un tiers : c'est
   l'animation d'origine, qui echangeait les trois verts. */
.aur-face-l, .aur-cap-t { fill: var(--aur-clair);  animation: aurFaceL 2.4s ease-in-out infinite; }
.aur-face-r, .aur-cap-r { fill: var(--aur-sombre); animation: aurFaceR 2.4s ease-in-out infinite; }
.aur-face-b, .aur-cap-l { fill: var(--aur-moyen);  animation: aurFaceB 2.4s ease-in-out infinite; }

@keyframes aurFaceL {
  0%   { fill: var(--aur-clair); }
  33%  { fill: var(--aur-sombre); }
  66%  { fill: var(--aur-moyen); }
  100% { fill: var(--aur-clair); }
}
@keyframes aurFaceR {
  0%   { fill: var(--aur-sombre); }
  33%  { fill: var(--aur-moyen); }
  66%  { fill: var(--aur-clair); }
  100% { fill: var(--aur-sombre); }
}
@keyframes aurFaceB {
  0%   { fill: var(--aur-moyen); }
  33%  { fill: var(--aur-clair); }
  66%  { fill: var(--aur-sombre); }
  100% { fill: var(--aur-moyen); }
}

/* Texte : `--brand-ink` est le role « accent lisible en petit texte », donc le
   seul des trois qui puisse porter un mot sans se battre avec le fond. */
.aur-text-aur   { color: var(--brand-ink); font-weight: 800; letter-spacing: -.02em; }
.aur-text-alpha { color: var(--brand-ink); font-weight: 400; opacity: .80; letter-spacing: .01em; }
.nav-link {
  color: var(--text2) !important;
  font-size: var(--fs-sm);
  padding: .4rem .8rem !important;
  border-radius: var(--r-md);
  transition: background var(--t-fast), color var(--t-fast);
}
.nav-link:hover, .nav-link.active {
  color:      var(--text) !important;
  background: var(--bg3);
}
.navbar-toggler {
  border-color: var(--border) !important;
}
.navbar-toggler-icon {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3E%3Cpath stroke='%23888' stroke-width='2' stroke-linecap='round' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E") !important;
}
[data-theme="dark"] .navbar-toggler-icon {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3E%3Cpath stroke='%23e2e8f0' stroke-width='2' stroke-linecap='round' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E") !important;
}
[data-theme="light"] .navbar-toggler-icon {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3E%3Cpath stroke='%230f172a' stroke-width='2' stroke-linecap='round' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E") !important;
}
[data-theme="beige"] .navbar-toggler-icon {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3E%3Cpath stroke='%232c2416' stroke-width='2' stroke-linecap='round' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E") !important;
}

/* ── Navbar settings button ────────────────────────────────────────────────── */
.nav-settings-btn {
  background: rgba(255,255,255,.12);
  border: 1px solid rgba(255,255,255,.18);
  color: var(--text);
  border-radius: var(--r-md);
  padding: 4px 10px;
  font-size: var(--fs-base);
  line-height: 1.4;
}
.nav-settings-btn:hover { background: rgba(255,255,255,.2); color: var(--text); }
.nav-settings-btn::after { display: none; } /* cache le caret Bootstrap */
[data-theme="light"] .nav-settings-btn,
[data-theme="beige"] .nav-settings-btn {
  background: rgba(0,0,0,.08);
  border-color: var(--border);
}

.nav-settings-menu {
  min-width: 220px;
  background: var(--bg2);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: 0 8px 24px rgba(0,0,0,.25);
  padding: 6px 0;
}

/* ── Menus deroulants plus hauts que l'ecran ────────────────────────────────
   Signale sur telephone : le menu Reglages et le menu du profil depassent la
   fenetre, donc les faire defiler faisait defiler LA PAGE derriere eux. Le menu
   restait ancre a son bouton et se retrouvait decale au milieu du tableau, et
   son bas restait inatteignable.

   Borner la hauteur ne suffit PAS et c'est le piege : un menu ancre sous son
   bouton commence deja bas dans la page, donc meme haut de `100vh - 96px` il
   depasse encore. Mesure faite : hauteur 748 px pour une fenetre de 844, mais
   bas a 984 px. Sur telephone le menu devient donc une FEUILLE par le bas,
   comme les feuilles de filtres de la meme application : plus d'ancrage, plus
   de decalage possible, et le bas toujours atteignable.

   `overscroll-behavior: contain` est la moitie qui compte : sans elle, arriver
   au bout du menu enchaine le defilement sur la page, ce qui reproduit
   exactement le defaut signale. */
.menu-defilant,
.nav-settings-menu {
  max-height: calc(100vh - 96px);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
/* ── Menus deroulants : les couleurs du theme, pas celles de Bootstrap ──────
   Aucune regle ne visait `.dropdown-menu` : seul `.nav-settings-menu` etait
   habille, et tous les autres menus gardaient le blanc de Bootstrap, y compris
   en theme sombre. Le defaut existait avant ce lot ; il devient voyant depuis
   que ces menus s'ouvrent en feuille pleine largeur sur telephone, ou une
   surface blanche couvre la moitie d'une page sombre. */
.dropdown-menu {
  background-color: var(--bg2);
  border-color: var(--border);
  color: var(--text);
}
.dropdown-menu .dropdown-item { color: var(--text); }
.dropdown-menu .dropdown-item:hover,
.dropdown-menu .dropdown-item:focus { background-color: var(--bg3); color: var(--text); }
.dropdown-menu .dropdown-item.active,
.dropdown-menu .dropdown-item:active { background-color: var(--primary); color: #fff; }
.dropdown-menu .dropdown-divider { border-color: var(--border); }
.dropdown-menu .dropdown-header { color: var(--text-muted); }
.dropdown-menu .dropdown-item-text { color: var(--text); }

/* Croix de fermeture d'une feuille par le bas. La ligne est posee par
   `_poserCroix` (base.html) uniquement quand le menu est devenu feuille ; elle
   reste collee en haut, la feuille pouvant etre plus longue que l'ecran. */
.menu-fermer-ligne {
  position: sticky; top: 0; z-index: 2;
  display: flex; align-items: center; justify-content: space-between;
  /* `inherit` et non une couleur nommee : les deux menus n'ont pas le meme
     fond, et une bande d'une autre teinte se lit comme un defaut de rendu. */
  background-color: inherit;
  padding: 8px 14px 8px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
  font-size: .95rem;
}
.menu-fermer {
  background: none; border: none; color: var(--text-muted);
  font-size: 1.5rem; line-height: 1; padding: 0 6px; cursor: pointer;
}
.menu-fermer:hover { color: var(--text); }
/* Filet : si la fenetre s'elargit pendant que le menu est ouvert, il redevient
   un deroulant ancre et la croix n'a plus lieu d'etre. */
@media (min-width: 992px) { .menu-fermer-ligne { display: none; } }

@media (max-width: 991.98px) {
  /* `!important` obligatoire : Popper pose position et transform EN LIGNE. */
  .menu-defilant.show,
  .nav-settings-menu.show {
    position: fixed !important;
    inset: auto 0 0 0 !important;
    transform: none !important;
    margin: 0 !important;
    width: auto; max-width: none;
    max-height: 82vh;
    border-radius: 14px 14px 0 0;
    z-index: 1080;
  }
}
.nav-settings-menu .dropdown-item {
  color: var(--text);
  font-size: var(--fs-sm);
  padding: 7px 16px;
  border-radius: 0;
}
.nav-settings-menu .dropdown-item:hover { background: var(--bg3); color: var(--text); }
.nav-settings-menu .dropdown-item-text { color: var(--text); }
.nav-settings-menu .dropdown-divider { border-color: var(--border); }

/* ── Aide de niveau : une ligne, pas quatre ───────────────────────────────────
 *
 * Le bloc decrivait les TROIS niveaux plus le cas du telephone, soit quatre
 * paragraphes pour un reglage a un seul choix : il occupait a lui seul la
 * moitie du menu, et les trois quarts de son texte portaient sur des niveaux
 * qu'on n'a pas retenus. `_majAideNiveau` (base.html) pose `hidden` sur les
 * lignes des autres niveaux ; ce fichier ne fait que la mise en forme.
 *
 * La ligne « Sur telephone » est la seule a ne pas suivre le niveau : elle dit
 * qu'a cette largeur la carte montre les memes cellules a TOUS les niveaux.
 * Elle est donc affichee la ou elle est vraie, et nulle part ailleurs : sur
 * bureau elle decrirait un ecran que le lecteur n'a pas sous les yeux. */
/* `:not([hidden])` n'est pas une precaution de style : une regle d'auteur
 * `display: block` BAT le `[hidden] { display: none }` du navigateur. Ecrite
 * sans cette condition, la ligne restait visible alors que le script avait
 * bien pose l'attribut, et les trois niveaux s'affichaient comme avant. */
.lvl-aide:not([hidden]) { display: block; }
.lvl-aide-mobile { display: none; }

@media (max-width: 575.98px) {
  .lvl-aide-mobile { display: block; }
}

/* ── Navbar search ─────────────────────────────────────────────────────────── */
.nav-search-input {
  background: rgba(255,255,255,.10);
  border-color: rgba(255,255,255,.2);
  color: var(--text);
  border-radius: var(--r-xl);
  padding-left: 14px;
}
.nav-search-input::placeholder { color: rgba(255,255,255,.45); }
.nav-search-input:focus {
  background: var(--bg2);
  border-color: var(--primary);
  color: var(--text);
  box-shadow: 0 0 0 2px rgba(56,189,248,.25);
}
[data-theme="light"] .nav-search-input,
[data-theme="beige"] .nav-search-input {
  background: rgba(0,0,0,.07);
  border-color: var(--border);
  color: var(--text);
}
[data-theme="light"] .nav-search-input::placeholder,
[data-theme="beige"] .nav-search-input::placeholder { color: var(--text2); }

.nav-sug-dropdown {
  position: absolute;
  z-index: 1050;
  width: 100%;
  min-width: 280px;
  background: var(--bg2);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: 0 8px 24px rgba(0,0,0,.28);
  top: calc(100% + 6px);
  max-height: 320px;
  overflow-y: auto;
}
.nav-sug-item {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 9px 14px;
  cursor: pointer;
  border-bottom: 1px solid var(--border);
  transition: background var(--t-fast);
  font-size: var(--fs-xs);
}
.nav-sug-item:last-child { border-bottom: none; }
.nav-sug-item:hover, .nav-sug-item.active { background: var(--bg3); }
.nav-sug-name { flex: 1; font-size: var(--fs-2xs); color: var(--text2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Une definition du glossaire dans la barre : le terme, puis deux lignes au plus. */
.nav-sug-def { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.nav-sug-def-texte { font-size: var(--fs-2xs); color: var(--text2); white-space: normal;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.nav-sug-exch { font-size: var(--fs-3xs); color: var(--text3); flex-shrink: 0; }
/* En-tete de groupe dans les suggestions : « Pages » puis « Titres ». Sans
   separation, une page et un ticker se ressemblent trop pour qu'on sache
   lequel des deux on est en train de choisir. */
.nav-sug-head {
  padding: 5px 10px 3px; font-size: var(--fs-3xs); font-weight: 600;
  text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted);
  border-bottom: 1px solid var(--border); background: var(--bg2);
}
/* Page hors formule : signalee, jamais masquee. Le clic mene a /tarifs avec
   une fenetre qui nomme le module, ce qui informe ; la masquer priverait
   l'utilisateur de la seule occasion de decouvrir ce qu'il n'a pas. */
.nav-sug-lock {
  font-size: var(--fs-3xs); flex-shrink: 0; padding: 1px 6px; border-radius: 999px;
  border: 1px solid var(--accent); color: var(--accent); white-space: nowrap;
}

/* ── Theme switcher ─────────────────────────────────────────────────────────── */
.theme-btn {
  display:     inline-flex;
  align-items: center;
  gap:         6px;
  padding:     4px 10px;
  border:      1px solid var(--border);
  border-radius: var(--r-xl);
  background:  var(--bg3);
  color:       var(--text2);
  font-size: var(--fs-2xs);
  cursor:      pointer;
  transition:  all .15s;
  white-space: nowrap;
}
.theme-btn:hover { border-color: var(--primary); color: var(--primary); }
.theme-btn.active { background: var(--primary); color: #fff; border-color: var(--primary); }

/* ── Cards ──────────────────────────────────────────────────────────────────── */
.card {
  background:    var(--bg2);
  border:        1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow:    var(--card-shadow);
  transition:    background .2s, border-color .2s;
}
.card-header {
  background:    var(--bg3);
  border-bottom: 1px solid var(--border);
  border-radius: 12px 12px 0 0 !important;
  padding:       .75rem 1rem;
  font-weight:   600;
  color:         var(--text);
}
.card-body { padding: 1rem; color: var(--text); }

/* ── Stat cards (dashboard KPIs) ────────────────────────────────────────────── */
.stat-card {
  background:    var(--bg2);
  border:        1px solid var(--border);
  border-radius: var(--r-lg);
  padding:       1rem 1.25rem;
  box-shadow:    var(--card-shadow);
}
.stat-card .stat-label {
  font-size: var(--fs-3xs);
  text-transform: uppercase;
  letter-spacing: .06em;
  color:      var(--text3);
  margin-bottom: 4px;
}
.stat-card .stat-value {
  font-size: var(--fs-xl);
  font-weight: 700;
  color:       var(--text);
  line-height: 1;
}
.stat-card .stat-sub {
  font-size: var(--fs-2xs);
  color:     var(--text2);
  margin-top: 4px;
}

/* ── Backtest market cards ──────────────────────────────────────────────────── */
.bt-card {
  background:    var(--bg2);
  border:        1px solid var(--border);
  border-radius: var(--r-lg);
  overflow:      hidden;
  box-shadow:    var(--card-shadow);
}
.bt-card-header {
  background:  var(--bg3);
  border-bottom: 1px solid var(--border);
  padding:     .75rem 1rem;
  display:     flex;
  align-items: center;
  justify-content: space-between;
}
.bt-card-header .market-name { font-size: var(--fs-md); font-weight: 700; color: var(--text); }
.bt-metric-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0;
  border-bottom: 1px solid var(--border);
}
.bt-metric {
  padding:    .75rem .75rem .6rem;
  text-align: center;
  border-right: 1px solid var(--border);
}
.bt-metric:last-child { border-right: none; }
.bt-metric .lbl {
  font-size: var(--fs-4xs);
  text-transform: uppercase;
  letter-spacing: .07em;
  color:      var(--text3);
  margin-bottom: 4px;
}
.bt-metric .val {
  font-size: var(--fs-lg);
  font-weight: 700;
  color:       var(--text);
}
.bt-trades-section { padding: .75rem 1rem; }
.bt-trade-row {
  display:     flex;
  justify-content: space-between;
  align-items: center;
  padding:     3px 0;
  font-size: var(--fs-xs);
  color:       var(--text);
  border-bottom: 1px solid var(--border);
}
.bt-trade-row:last-child { border-bottom: none; }
.bt-trade-row .ticker { font-weight: 600; min-width: 60px; color: var(--text); }
.bt-trade-row .date   { color: var(--text3); font-size: var(--fs-3xs); }
.bt-trade-row .pnl    { font-weight: 700; min-width: 70px; text-align: right; }

/* ── Tables ─────────────────────────────────────────────────────────────────── */
.table {
  color: var(--text);
  border-color: var(--border);
}
.table > :not(caption) > * > * {
  background: transparent;
  color:      var(--text);
  border-color: var(--border);
  padding:    .6rem .75rem;
}
.table thead th {
  font-size: var(--fs-3xs);
  text-transform: uppercase;
  letter-spacing: .06em;
  color:      var(--text3);
  font-weight: 600;
  border-top: none;
}

/* ── Un en-tete de colonne ne se REPLIE jamais ───────────────────────────────
   L'AUTORITE UNIQUE sur la question : c'est la seule regle du site qui decide
   si un EN-TETE de tableau se replie. (`.syn-th-signe` plus bas porte encore un
   `nowrap`, mais la regle y sert son jumeau `.syn-td-signe`, une cellule de
   corps, qui n'est pas couverte ici.)

   Le defaut paye : `/portfolio` porte quinze colonnes. Sur telephone, le
   conteneur a `overflow-x: auto` mais la table Bootstrap porte `width: 100%`,
   donc elle se cale sur la largeur de la boite au lieu de la depasser -- et
   l'algorithme de table comprime alors CHAQUE colonne jusqu'a son min-content.
   Min-content d'un en-tete en majuscules espacees, c'est UNE LETTRE : « PRIX
   D'ACHAT » s'affiche sur onze lignes, une lettre par ligne. Mesure a 390 px
   (iPhone 14, Chromium) : rangee d'en-tete de 201 px de haut, table de 522 px.
   Le texte se lit alors en vertical et les cellules se chevauchent.

   `nowrap` sur les en-etes seuls suffit et c'est le correctif MINIMAL : la
   largeur min-content de la table devient la somme des libelles entiers (1170
   px), donc la table DEPASSE sa boite et le conteneur defile, ce pour quoi il
   est fait. Meme mesure apres : rangee d'en-tete de 38 px.

   Ne PAS y ajouter `width: max-content` sur la table : mesure a 1346 px, parce
   que la cellule de nom (« NYSE · Energie · Oil & Gas Midstream ») cesse de se
   replier. Le corps du tableau DOIT garder le droit de se replier ; seul
   l'en-tete est un libelle, pas de la prose.

   La regle est globale et non limitee a une classe de conteneur : ils sont
   cinq (`table-responsive`, `table-sticky-wrap`, `aa-table-wrap`,
   `ipo-table-wrap`, `tbl-scroll`) plus des `style="overflow-x:auto"` en ligne,
   et les enumerer laisserait tomber le prochain. La contrepartie -- un tableau
   large POSE HORS d'un conteneur ferait defiler la page entiere -- est tenue
   par `test_tableaux_mobile.py`, qui derive la liste des tableaux depuis les
   gabarits au lieu d'enumerer ceux d'aujourd'hui. */
thead th { white-space: nowrap; }
/* `:not(.tbl-section)` : un en-tete de section n'est pas une ligne de donnees.
   Le survol l'aurait repeint du gris des lignes, effacant justement ce qui le
   distingue -- et il aurait suggere qu'on peut cliquer dessus. */
.table tbody tr:not(.tbl-section):hover > * { background: var(--bg3) !important; }

/* ── Sections d'un tableau regroupe ──────────────────────────────────────────
   Portefeuille et watchlist inserent des lignes d'en-tete entre les groupes
   (`static/regroupement.js`). Elles ne portaient AUCUNE regle d'affichage :
   `wl-grp` et `pf-grp` n'apparaissaient dans aucune feuille de style. Un titre
   de section se lisait donc exactement comme la ligne de donnees suivante, et
   un tableau de quarante lignes en huit groupes se lisait comme une seule
   liste.

   La classe est posee par le MODULE et non par les gabarits : c'est la meme
   decision d'affichage sur les deux ecrans.

   Le trait de gauche est un `box-shadow` interne et non un `border-left` : la
   cellule porte un `colspan`, et une bordure gauche sur une cellule fusionnee
   se decale d'un pixel selon le navigateur. */
.tbl-section > td {
  background:    var(--accent-soft);
  border-top:    2px solid var(--accent);
  border-bottom: 1px solid var(--border);
  box-shadow:    inset 4px 0 0 var(--accent);
  padding:       var(--sp-3) var(--sp-5);
  /* Porte sur la CELLULE et non sur le libelle : l'appelant peut y ajouter un
     element a lui (le portefeuille y met le total du groupe), et il doit se
     poser sur la meme ligne de base que le titre, pas a la taille du corps du
     tableau. */
  font-size:     var(--fs-xs);
}
.tbl-section-nom {
  font-weight:    700;
  letter-spacing: .02em;
  color:          var(--text);
}
/* L'effectif en pastille CREUSE : un fond plein demanderait une couleur de
   texte qui contraste avec lui dans les trois themes et les quatre palettes,
   donc une teinte ecrite en dur. Le contour prend celle du theme. */
.tbl-section-n {
  display:       inline-block;
  margin-left:   var(--sp-4);
  padding:       0 var(--sp-3);
  border:        1px solid var(--accent);
  border-radius: var(--r-pill);
  color:         var(--accent);
  font-size:     var(--fs-4xs);
  font-weight:   700;
  line-height:   1.5;
  vertical-align: middle;
}

/* ── Densite des grands tableaux ─────────────────────────────────────────────
   Le remplissage general (.6rem) est celui d'un tableau de quelques lignes. Sur
   des ecrans qui en affichent des dizaines, il ajoute une vingtaine de pixels
   par ligne : moins de la moitie d'un ecran de tableau tient a l'ecran, et les
   sections qu'on vient de rendre lisibles se retrouvent hors champ.

   `cell-nom` et `cell-ajout` bornent les deux cellules qui font DEBORDER une
   ligne sur trois hauteurs de texte -- un nom d'ETF long, et un prix d'ajout
   dont le bouton tombait sous la date faute de largeur. Le nom complet reste
   accessible : il est dans l'attribut `title` et dans le texte de la cellule,
   que les filtres lisent -- la coupe est purement visuelle. */
.tbl-dense > tbody > tr > td { padding: var(--sp-2) var(--sp-4); line-height: 1.3; }
.cell-nom {
  max-width:     190px;
  overflow:      hidden;
  text-overflow: ellipsis;
  white-space:   nowrap;
}
.cell-ajout { white-space: nowrap; }

/* Lignes par signal */
.row-strong-buy > * { background: rgba(74,222,128,.06) !important; }
.row-buy        > * { background: rgba(56,189,248,.05) !important; }
.row-neutral    > * { background: transparent !important; }
.row-avoid      > * { background: rgba(248,113,113,.05) !important; }

/* Badges signal dans la colonne Signal */
.signal-badge            { display:inline-block; padding:2px 8px; border-radius: var(--r-xl); font-size: var(--fs-2xs); font-weight:600; white-space:nowrap; }
.signal-strong-buy       { background:rgba(74,222,128,.18); color:#22c55e; }
.signal-buy              { background:rgba(56,189,248,.15); color:#38bdf8; }
.signal-neutral          { background:rgba(148,163,184,.12); color:var(--text-muted); }
.signal-avoid            { background:rgba(248,113,113,.15); color:#f87171; }
[data-theme="light"] .signal-strong-buy { color:#16a34a; }
[data-theme="light"] .signal-buy        { color:#0284c7; }
[data-theme="light"] .signal-avoid      { color:#dc2626; }
[data-theme="beige"] .signal-strong-buy { color:#15803d; }
[data-theme="beige"] .signal-buy        { color:#0369a1; }
[data-theme="beige"] .signal-avoid      { color:#b91c1c; }

/* Pastille de signal. Portee par la CLASSE et non ecrite dans les gabarits :
   le badge est rendu a une vingtaine d'endroits (tableau de bord, colonne
   court terme, colonne francaise, AuraInsight, aides), et y recopier le
   pictogramme garantirait qu'un jour l'un d'eux affiche une pastille qui
   contredit sa propre couleur. Ici la couleur et la pastille viennent de la
   meme classe. Les memes pictogrammes cote email sont dans
   `scoring.RANKING_EMOJI` : un email ne sait pas lire un pseudo-element.

   Le `/ ""` donne un texte alternatif VIDE : la pastille repete une
   information deja portee par le mot du badge, un lecteur d'ecran qui
   annoncerait « grand cercle vert » avant « Strong Buy » ajouterait du bruit.
   Les navigateurs qui ignorent cette syntaxe rendent simplement le glyphe. */
.signal-badge::before { margin-right:.35em; }
/* Sante financiere d'une fiche : trois lectures, couleurs semantiques du theme. */
.sante-lecture { font-size: var(--fs-2xs); font-weight:600; }
.sante-solide  { color: var(--success); }
.sante-moyen   { color: var(--text-muted); }
.sante-fragile { color: var(--danger); }
/* La mention d'un marche sans pouvoir predictif mesure (pouvoir_predictif.py). */
.mention-marche { display:block; font-size: var(--fs-2xs); line-height:1.25; color: var(--text-muted); white-space:normal; margin-top:2px; font-weight:400; }
.signal-strong-buy::before { content: "🟢" / ""; }
.signal-buy::before        { content: "🔵" / ""; }
.signal-neutral::before    { content: "🟡" / ""; }
.signal-avoid::before      { content: "🔴" / ""; }

/* ── Hyper-Growth badge (licornes potentielles) ──────────────────────────── */
.hyper-growth-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: var(--r-xl);
  font-size: var(--fs-3xs);
  font-weight: 700;
  background: linear-gradient(90deg, rgba(168,85,247,.20), rgba(236,72,153,.20));
  color: #c084fc;
  border: 1px solid rgba(168,85,247,.35);
  cursor: help;
  vertical-align: middle;
  white-space: nowrap;
}
[data-theme="light"] .hyper-growth-badge { color:#7c3aed; }
[data-theme="beige"] .hyper-growth-badge { color:#6d28d9; }

/* ── Filter row ─────────────────────────────────────────────────────────────── */
.table-filter-row td { padding: 4px 4px; background: var(--card-bg); }
.col-filter { min-width: 50px; font-size: var(--fs-2xs); padding: 2px 5px;
              background: var(--input-bg, var(--bg)); color: var(--text);
              border-color: var(--border); }
.col-filter::placeholder { opacity: .45; }

/* ── Dashboard / Watchlist : scroll horizontal du tableau + header figé ─────── */
/* overflow-x: auto sur le wrapper → la table défile dans sa boîte, pas dans la page.
   Les thead sont sticky par rapport au wrapper (scroll-container); comme le wrapper
   n'a pas de hauteur fixe, les navigateurs modernes (Chrome/FF/Safari) les traitent
   de façon identique à un sticky relatif au viewport en pratique.
   --thead-h est mesuré en JS pour caler la filter row pile sous les intitulés. */
.table-sticky-wrap {
  overflow-x: auto;
}
.table-sticky-wrap thead th {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--bg2);
}
.table-sticky-wrap .table-filter-row td {
  position: sticky;
  top: var(--thead-h, 34px);
  z-index: 19;
  background: var(--card-bg);
}
/* Le coin haut-gauche (checkbox) doit passer au-dessus des deux rangées figées */
.table-sticky-wrap thead th:first-child { z-index: 21; }

/* ── Score badge ────────────────────────────────────────────────────────────── */
.score-badge {
  display:     inline-block;
  padding:     2px 8px;
  border-radius: var(--r-xl);
  font-size: var(--fs-2xs);
  font-weight: 700;
}
.score-high { background: rgba(74,222,128,.18); color: var(--success); }
.score-mid  { background: rgba(56,189,248,.18); color: var(--primary); }
.score-low  { background: rgba(248,113,113,.18); color: var(--danger); }
/* Pas de score : NEUTRE et non rouge. Reprendre .score-low ferait lire
   « mauvais » la ou il n'y a aucune mesure, ce que ce marqueur existe
   precisement pour eviter. Pas de fond : la pastille ne doit pas attirer
   l'oeil comme un verdict. */
.score-absent { background: none; color: var(--text3); font-weight: 400; }

/* ── Win rate badge ─────────────────────────────────────────────────────────── */
.wr-badge {
  font-size: var(--fs-2xs);
  font-weight: 700;
  padding:     3px 10px;
  border-radius: var(--r-xl);
}
.wr-good    { background: rgba(74,222,128,.18); color: var(--success); }
.wr-neutral { background: rgba(251,191,36,.18);  color: var(--warning); }
.wr-bad     { background: rgba(248,113,113,.18); color: var(--danger); }

/* ── Level badges ───────────────────────────────────────────────────────────── */
.badge-level {
  display:     inline-block;
  padding:     2px 8px;
  border-radius: var(--r-xl);
  font-size: var(--fs-3xs);
  font-weight: 600;
  margin-left: 6px;
}
.badge-beginner     { background: rgba(56,189,248,.18); color: var(--primary); }
.badge-intermediate { background: rgba(251,191,36,.18); color: var(--warning); }
.badge-expert       { background: rgba(248,113,113,.18); color: var(--danger); }

/* ── Forms ──────────────────────────────────────────────────────────────────── */
.form-control, .form-select {
  background:   var(--bg3);
  border-color: var(--border);
  color:        var(--text);
}
.form-control:focus, .form-select:focus {
  background:   var(--bg3);
  border-color: var(--primary);
  color:        var(--text);
  box-shadow:   0 0 0 3px color-mix(in srgb, var(--primary) 20%, transparent);
}
.form-control::placeholder { color: var(--text3); }
.form-text { color: var(--text3); }

/* ── Champs de saisie sur écran tactile : jamais sous 16px ─────────────────── */
/* iOS Safari agrandit la page entière dès qu'un champ prend le focus avec une
   police sous 16 px, et ne la remet pas comme elle était en sortant. Le
   symptôme a été signalé sur la bulle d'avis, mais ce n'est pas un défaut de
   cette bulle : le corps du site est à 14 px (`--fs-sm`) et le dépôt compte
   ~148 `form-control-sm` / `form-select-sm`, dont cinq dans `base.html`, donc
   sur TOUTES les pages. Tous les formulaires du site étaient concernés.
   Corriger la bulle seule aurait déplacé la plainte, pas réglé la cause.

   **`pointer: coarse` plutôt qu'une largeur.** La condition réelle n'est pas
   « écran étroit » mais « le doigt est le pointeur » : c'est là que le zoom
   automatique existe. Un `max-width` aurait aussi frappé une fenêtre de bureau
   réduite, où il élargirait pour rien les colonnes de filtre du tableau de
   bord (`.col-filter`, 12 px, dimensionnée pour des colonnes serrées).

   **`!important` est nécessaire, pas confortable.** Dix-neuf champs portent une
   taille en attribut `style` (`dashboard.html`, `recipients.html`,
   `asset_allocator.html`, ...), et une déclaration en ligne bat toute règle de
   feuille de style qui n'est pas `!important`. Sans lui, la correction serait
   fausse précisément sur les champs les plus petits. La règle énonce un
   invariant, pas une préférence : sur un écran tactile, un champ de saisie fait
   au moins 16 px, quoi qu'en dise le reste.

   **Ce qu'il ne faut surtout PAS faire** : poser `maximum-scale=1` ou
   `user-scalable=no` dans la balise `viewport`. Cela supprimerait le symptôme
   en retirant le zoom par pincement à tout le monde, y compris à ceux qui en
   ont besoin pour lire. C'est un manquement d'accessibilité (WCAG 1.4.4) sur un
   site qui vient de gagner ses points sur ce terrain. `test_mobile_zoom.py`
   échoue si l'un des deux réapparaît. */
@media (pointer: coarse) {
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="hidden"]),
  select,
  textarea,
  .form-control, .form-select,
  .form-control-sm, .form-select-sm,
  .col-filter {
    font-size: 16px !important;
  }
}

/* ── Buttons ────────────────────────────────────────────────────────────────── */
.btn-primary {
  background:   var(--primary);
  border-color: var(--primary);
  color:        #fff;
  font-weight:  600;
}
.btn-primary:hover { filter: brightness(1.1); color: #fff; }
.btn-outline-primary { border-color: var(--primary); color: var(--primary); }
.btn-outline-primary:hover { background: var(--primary); color: #fff; }
.btn-outline-secondary { border-color: var(--border); color: var(--text2); }
.btn-outline-secondary:hover { background: var(--bg3); color: var(--text); }
.btn-outline-danger { border-color: var(--danger); color: var(--danger); }
.btn-outline-danger:hover { background: var(--danger); color: #fff; }

/* ── Auth card ──────────────────────────────────────────────────────────────── */
.auth-card {
  background:    var(--bg2);
  border:        1px solid var(--border);
  border-radius: var(--r-lg);
  padding:       2rem;
  width:         100%;
  max-width:     420px;
  box-shadow:    var(--card-shadow);
}

/* ── Modal ──────────────────────────────────────────────────────────────────── */
.modal-content {
  background:   var(--bg2);
  border:       1px solid var(--border);
  border-radius: var(--r-lg);
}
.modal-header { border-bottom-color: var(--border); }
.modal-footer { border-top-color:    var(--border); }
/* Croix de fermeture. Bootstrap 5 dessine un chevron **sombre**, et son propre
   basculement de thème s'appuie sur `data-bs-theme`, attribut que ce site
   n'utilise pas : il ne s'est donc jamais déclenché. Deux symptômes mesurés au
   test du 2026-07-26 : croix noire invisible sur fond sombre pour les 20 boutons
   sans filtre, et croix blanche invisible sur fond clair pour les 2 qui
   portaient un `filter: invert(1)` en style en ligne. Une règle centrale traite
   les deux cas, et les styles en ligne ont été retirés, un style en ligne
   battant toujours une feuille. */
.btn-close, .btn-close-white { filter: none; }
[data-theme="dark"] .btn-close,
[data-theme="dark"] .btn-close-white,
[data-theme="midnight"] .btn-close,
[data-theme="midnight"] .btn-close-white { filter: invert(1) grayscale(1); }

/* ── Alert ──────────────────────────────────────────────────────────────────── */
.alert-danger {
  background:   rgba(248,113,113,.12);
  border-color: rgba(248,113,113,.3);
  color:        var(--danger);
}

/* ── Misc ───────────────────────────────────────────────────────────────────── */
hr { border-color: var(--border); }
.text-muted { color: var(--text2) !important; }
code { background: var(--bg3); color: var(--primary); padding: 1px 5px; border-radius: var(--r-sm); }

::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: var(--r-sm); }

/* ── Watchlist star button ──────────────────────────────────────────────────── */
.watchlist-btn {
  background: none;
  border: none;
  cursor: pointer;
  font-size: var(--fs-xs);
  padding: 0 3px 0 0;
  color: var(--text2);
  opacity: .5;
  transition: opacity .15s, transform .1s;
  line-height: 1;
  vertical-align: middle;
}
.watchlist-btn:hover { opacity: 1; transform: scale(1.2); }
.watchlist-btn.watching { color: #f59e0b; opacity: 1; }

/* ── Admin btn-xs ───────────────────────────────────────────────────────────── */
.btn-xs { font-size: var(--fs-3xs); padding: 2px 8px; }

/* ── Period buttons (score curve chart) ─────────────────────────────────────── */
.period-btn {
  font-size: var(--fs-3xs);
  padding: 2px 9px;
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text2);
  cursor: pointer;
  transition: background var(--t-fast), color var(--t-fast);
}
.period-btn:hover { background: var(--bg3); color: var(--text); }
.period-btn.active {
  background: var(--accent, #38bdf8);
  color: #fff;
  border-color: var(--accent, #38bdf8);
  font-weight: 600;
}


/* ── Leçons d'indicateurs (bulles driver.js) ─────────────────────────────────
   Le contenu vient de static/indicator_lessons.js. Tout est exprimé en variables
   de thème : ces bulles doivent rester lisibles en sombre, clair et beige. */
/* `body .driver-popover` et non `.driver-popover` : driver.css est chargé APRÈS
   ce fichier dans base.html, donc à spécificité égale c'est lui qui gagnerait.
   Un sélecteur descendant suffit à reprendre la main, sans !important. */
/* La bulle elle-même suit le thème. driver.css impose un fond blanc et un texte
   sombre : mes blocs de leçon utilisant les variables de thème, en thème sombre
   on obtenait du texte clair sur fond blanc, donc illisible. Habiller la bulle
   règle le problème à la source et supprime au passage la boîte blanche au milieu
   d'une application sombre. */
body .driver-popover {
  background: var(--card-bg);
  color: var(--text);
  border: 1px solid var(--border);
  box-shadow: 0 10px 34px rgba(0, 0, 0, .35);
  max-width: min(460px, calc(100vw - 24px));
  /* La bulle doit tenir dans l'écran : sur un téléphone en portrait, une leçon
     complète dépassait la page et son début était inaccessible. */
  max-height: min(76vh, 640px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
@media (max-width: 640px) {
  body .driver-popover { max-height: 70vh; font-size: var(--fs-md); }
}

.learn-image {
  font-weight: 600; font-size: var(--fs-md); line-height: 1.4;
  margin-bottom: 8px; color: var(--text);
}
.learn-analogy {
  font-size: var(--fs-sm); line-height: 1.55; margin: 0 0 10px;
  color: var(--text-muted);
}
.learn-cases { margin-bottom: 10px; }
.learn-cases-t {
  font-size: var(--fs-3xs); font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--text-muted); margin-bottom: 5px;
}
.learn-case {
  border-left: 2px solid var(--border);
  padding: 3px 0 3px 9px; margin-bottom: 7px;
}
/* La suite de prix : en chiffres à largeur fixe, pour qu'on voie la progression. */
.learn-case-p {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--fs-2xs); color: var(--text); word-break: break-word;
}
.learn-case-v {
  font-size: var(--fs-2xs); font-weight: 700; color: var(--accent); margin: 1px 0;
}
.learn-case-r { font-size: var(--fs-2xs); line-height: 1.5; color: var(--text-muted); }

/* La lecture sur le titre réellement affiché : c'est le seul chiffre mesuré de
   la bulle, il doit se distinguer des exemples illustratifs. */
.learn-live {
  margin: 10px 0 0; padding: 7px 9px; border-radius: var(--r-md);
  background: var(--accent-soft, rgba(99,102,241,.10));
  border: 1px solid var(--accent);
  font-size: var(--fs-xs); line-height: 1.5;
}
.learn-trap {
  margin-top: 9px; padding-top: 8px; border-top: 1px dashed var(--border);
  font-size: var(--fs-2xs); line-height: 1.5; color: var(--text-muted);
}
.learn-def { margin-top: 8px; font-size: var(--fs-2xs); color: var(--text-muted); }
.learn-def summary { cursor: pointer; }

/* Le bloc d'exemples devient un `details` sur écran étroit : le résumé doit
   rester lisible comme un titre de section, et cliquable. */
details.learn-cases > summary.learn-cases-t {
  cursor: pointer; list-style: revert; margin-bottom: 7px;
}
details.learn-cases[open] > summary.learn-cases-t { margin-bottom: 8px; }

body .driver-popover-title { color: var(--text); }
body .driver-popover-description { color: var(--text-muted); }
body .driver-popover-progress-text { color: var(--text-muted); }
/* Croix de fermeture. Trois etats et non un seul, et c'est la tout le bug :
   driver.css porte `.driver-popover-close-btn:hover, .driver-popover-close-btn:focus
   { color: #2d2d2d }`, soit une specificite de (0,2,0). Notre ancienne regle
   `body .driver-popover-close-btn` pesait (0,1,1) : elle habillait donc l'etat au
   repos, perdait sur `:hover` (rattrape par la ligne suivante) et perdait
   surtout sur `:focus`, jamais couvert.

   Or driver.js DONNE LE FOCUS a cette croix a l'ouverture de la bulle. En theme
   sombre elle s'affichait donc a #2d2d2d sur un fond #1e293b : un contraste de
   1,1 pour 1, invisible en pratique. Le symptome trompait, il n'y avait rien
   d'invisible dans la couleur au repos, tout venait de l'etat focus.

   `:focus-visible` est cite en plus de `:focus` pour les navigateurs qui
   distinguent les deux, et `:active` pour ne pas rejouer le meme oubli au clic. */
body .driver-popover-close-btn { color: var(--text-muted); }
body .driver-popover-close-btn:hover,
body .driver-popover-close-btn:focus,
body .driver-popover-close-btn:focus-visible,
body .driver-popover-close-btn:active { color: var(--text); }
body .driver-popover-navigation-btns button {
  background: var(--bg2); color: var(--text);
  border: 1px solid var(--border); text-shadow: none;
}
body .driver-popover-navigation-btns button:hover {
  background: var(--bg3); color: var(--text);
}
/* La flèche de la bulle est un triangle CSS coloré en blanc par driver.css :
   sans ces quatre règles, une flèche blanche pointerait depuis une bulle sombre. */
body .driver-popover-arrow-side-left   { border-left-color:   var(--card-bg); }
body .driver-popover-arrow-side-right  { border-right-color:  var(--card-bg); }
body .driver-popover-arrow-side-top    { border-top-color:    var(--card-bg); }
body .driver-popover-arrow-side-bottom { border-bottom-color: var(--card-bg); }

/* ══════════════════════════════════════════════════════════════════════════════
   Accessibilité (WCAG 2.2 AA)
   ══════════════════════════════════════════════════════════════════════════════

   Trois manques mesurés à la revue du 2026-07-26 : aucune règle `:focus-visible`
   dans toute la feuille, aucune prise en compte de `prefers-reduced-motion`, et
   pas de lien d'évitement pour sauter la navigation.
   ────────────────────────────────────────────────────────────────────────────── */

/* 2.4.7 Focus visible, et 1.4.11 Contraste des éléments non textuels.
   `:focus-visible` et non `:focus` : le second dessinerait aussi un anneau au
   clic à la souris, ce que les navigateurs ont justement cessé de faire. Le
   double contour, sombre puis clair, garantit le contraste sur les trois thèmes
   sans avoir à décliner la couleur par thème. */
/* Piège de spécificité, corrigé après le test sur le site réel. La première
   version utilisait `:where(a, button, …)`, et `:where()` a une spécificité
   **nulle** : Bootstrap pose `outline: 0` sur `.btn:focus-visible`,
   `.form-control:focus`, `.nav-link:focus-visible` et `.btn-close:focus`, donc
   c'est Bootstrap qui gagnait et l'anneau n'apparaissait jamais. Le test initial
   ne l'avait pas vu parce que la page témoin ne chargeait pas Bootstrap.

   D'où des sélecteurs réels, préfixés par `body` : `body .btn:focus-visible`
   pèse (0,2,1) contre (0,2,0) pour la règle de Bootstrap. */
body a:focus-visible,
body button:focus-visible,
body input:focus-visible,
body select:focus-visible,
body textarea:focus-visible,
body summary:focus-visible,
body [tabindex]:focus-visible,
body .btn:focus-visible,
body .btn-close:focus-visible,
body .nav-link:focus-visible,
body .dropdown-item:focus-visible,
body .form-control:focus-visible,
body .form-select:focus-visible,
body .form-check-input:focus-visible,
body .page-link:focus-visible {
  outline: 2px solid var(--accent, #6366f1);
  outline-offset: 2px;
  border-radius: var(--r-sm);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 28%, transparent);
}

/* 2.4.1 Contourner des blocs. Le lien reste hors écran jusqu'à ce qu'il reçoive
   le focus, donc invisible à la souris et atteignable au clavier. `clip-path`
   plutôt que `display: none`, qui le retirerait de l'ordre de tabulation. */
.skip-link {
  /* `fixed` et non `absolute` : en absolu, le lien se place par rapport au
     document, donc sur une page déjà défilée il apparaîtrait tout en haut, hors
     de la fenêtre, et resterait invisible alors qu'il a bien le focus. C'est le
     défaut trouvé au test du 2026-07-26. */
  position: fixed; left: 8px; top: -100px; z-index: 2000;
  padding: 10px 16px; border-radius: 0 0 8px 8px;
  background: var(--accent, #6366f1); color: #fff;
  font-weight: 600; text-decoration: none;
  transition: top .12s ease-out;
}
.skip-link:focus { top: 0; }
/* La cible du lien reçoit le focus par programme : sans `outline: none` elle
   afficherait un anneau autour de toute la zone de contenu. */
#contenu:focus { outline: none; }

/* 2.5.8 Taille de la cible (minimum). 24 par 24 pixels CSS suffisent au critère
   AA ; on ne touche pas aux cellules de tableau, où l'espacement suffit. */
.btn-close, .nav-link, .dropdown-item,
.form-check-input[type="checkbox"], .form-check-input[type="radio"] {
  min-width: 24px; min-height: 24px;
}

/* Contrepartie de la règle ci-dessus, oubliée lors du lot accessibilité.
   Bootstrap réserve `padding-left: 1.5em` (24 px) à la case et l'y tire par
   `margin-left: -1.5em`. En portant la case de 16 à 24 px, elle remplit
   exactement cette gouttière : il ne reste que 3 px avant le libellé, et le
   `margin-top: .25em` prévu pour une case de 16 px la décale d'un pixel vers le
   bas. Mesuré dans Chromium, avant puis après : gouttière 3 px puis 8 px,
   décalage vertical 1 px puis 0.

   `:not(.form-switch)` est indispensable : un interrupteur a sa propre
   géométrie (2em de large, 2.5em de gouttière), et lui appliquer ces valeurs le
   casserait. */
body .form-check:not(.form-switch) {
  padding-left: 2rem;
  min-height: 1.5rem;
}
body .form-check:not(.form-switch) .form-check-input {
  margin-left: -2rem;
  margin-top: 0;
}
body .form-switch .form-check-input { margin-top: 0; }
body .form-check-label { line-height: 1.5; }

/* 2.3.3 Animation à l'interaction. Le fond animé respecte déjà la préférence en
   JavaScript ; la feuille de style ne la respectait pas du tout. On neutralise
   sans passer par `display`, pour ne casser aucune mise en page. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ── Modules annoncés mais pas encore ouverts ────────────────────────────────
   « Bientôt » doit se lire comme une promesse, pas comme une erreur : on garde
   la teinte d'accent plutôt qu'un gris d'élément désactivé. « Masqué » n'est vu
   que par un administrateur, d'où le ton neutre. */
.nav-soon { opacity: .75; cursor: default; }
a.nav-soon { cursor: pointer; }
/* ── Apercu d'un module (etat « flou ») ──────────────────────────────────────
   La page est SERVIE et brouillee au-dela de son debut : le haut se lit, le
   reste est flou, et un bandeau net dit ce que le module apporte.

   CE N'EST PAS UNE PROTECTION. Le contenu reste dans le document, les API
   repondent, et cette couche s'enleve en trois secondes. Un module dont le
   contenu ne doit pas etre lu se met sur « Masque ». La regle est ecrite dans
   `app/feature_flags.py`, dite sur la page de reglages, et tenue par un garde.

   UNE seule couche, posee en pseudo-element sur `<main>` : envelopper le
   contenu dans deux div supplementaires changerait la mise en page de toutes
   les pages, y compris celles qui ne sont pas floutees.

   Le degrade de masque est ce qui rend « une partie visible, le reste flou » :
   la couche est transparente sur les premiers pixels, donc le debut de la page
   se lit net, puis elle devient opaque. `backdrop-filter` brouille ce qui est
   DERRIERE, donc rien n'est duplique. */
.flou-zone { position: relative; }
.flou-zone::after {
  content: ""; position: absolute; inset: 0; z-index: 5;
  backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
  background: color-mix(in srgb, var(--bg) 30%, transparent);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, transparent 140px, #000 300px);
  mask-image: linear-gradient(to bottom, transparent 0, transparent 140px, #000 300px);
}
/* Un navigateur sans `backdrop-filter` afficherait une page parfaitement nette
   sous un bandeau qui annonce un apercu : le voile opaque tient alors le role,
   moins joliment mais sans mentir. */
@supports not (backdrop-filter: blur(1px)) {
  .flou-zone::after { background: var(--bg); opacity: .92; }
}
.flou-bandeau { border-color: var(--accent); }

.nav-badge-soon {
  display: inline-block; vertical-align: middle;
  margin-left: var(--sp-2);
  padding: 1px var(--sp-3);
  border-radius: var(--r-pill);
  font-size: var(--fs-4xs); font-weight: 600; letter-spacing: .02em;
  background: var(--accent-soft); color: var(--accent);
  border: 1px solid var(--accent);
}

/* ── Intitules courts sur petit ecran ────────────────────────────────────────
   « Ajouter la selection au portefeuille » sur un telephone : le bouton
   s'etalait sur quatre lignes et poussait la carte au-dela de l'ecran, ce qui
   faisait defiler la PAGE de gauche a droite. On raccourcit l'intitule plutot
   que de le laisser deborder ; le titre (attribut `title`) garde la phrase
   complete. Un `display:none` et non un retrait du DOM : le texte long reste
   disponible aux lecteurs d'ecran sur grand ecran, et la bascule est purement
   visuelle. */
.alloc-court { display: none; }
@media (max-width: 640px) {
  .alloc-long  { display: none; }
  .alloc-court { display: inline; }
  /* Les boutons d'action prennent la largeur restante et se repartissent au
     lieu de forcer une rangee unique plus large que l'ecran. */
  .alloc-actions { width: 100%; }
  .alloc-actions .btn { flex: 1 1 auto; white-space: nowrap; }
}

/* ── Navigation par sections : colonne a gauche, onglets defilants sur mobile ─
   Un seul motif pour /admin-panel (navigation entre PAGES) et /account
   (navigation entre SECTIONS d'une page). Ecrire deux fois la meme mise en
   page produirait deux comportements sur telephone, et c'est la que le detail
   compte : l'espace lateral n'existe pas.

   Le point de rupture est 992 px et non une detection de tactile : ici la
   contrainte est la LARGEUR disponible pour une colonne, pas la nature du
   pointeur (contrairement au zoom des champs, qui depend bien du doigt). */
.sect-layout { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 24px; }
.sect-nav { display: flex; flex-direction: column; gap: 2px; align-self: start; position: sticky; top: 12px; }
.sect-nav a, .sect-nav button {
  display: block; width: 100%; text-align: left; background: none; border: none;
  color: var(--text-muted); text-decoration: none; padding: 8px 12px;
  border-radius: 8px; font-size: .88rem; line-height: 1.25; cursor: pointer;
  border-left: 3px solid transparent;
}
.sect-nav a:hover, .sect-nav button:hover { background: var(--bg-card); color: var(--text); }
.sect-nav .active {
  background: var(--bg-card); color: var(--text); font-weight: 600;
  border-left-color: var(--accent, #6366f1);
}
.sect-nav .sect-sub { display: block; font-size: 11px; color: var(--text-muted); font-weight: 400; }
.sect-title { font-size: .7rem; text-transform: uppercase; letter-spacing: .06em;
  color: var(--text-muted); margin: 14px 0 4px; padding-left: 12px; }
.sect-title:first-child { margin-top: 0; }

@media (max-width: 991.98px) {
  /* Sous 992 px, la colonne devient une barre d'onglets qui DEFILE
     horizontalement. Elle ne passe pas a la ligne : une barre qui grandit en
     hauteur repousse le contenu et fait perdre le fil de ce qu'on regle. */
  .sect-layout { grid-template-columns: minmax(0, 1fr); gap: 12px; }
  .sect-nav {
    flex-direction: row; position: static; gap: 6px;
    overflow-x: auto; -webkit-overflow-scrolling: touch;
    padding-bottom: 6px; border-bottom: 1px solid var(--border);
    scrollbar-width: thin;
  }
  .sect-nav a, .sect-nav button {
    width: auto; white-space: nowrap; flex: 0 0 auto;
    border-left: none; border-bottom: 3px solid transparent; border-radius: 8px 8px 0 0;
  }
  .sect-nav .active { border-left-color: transparent; border-bottom-color: var(--accent, #6366f1); }
  /* Le sous-titre n'a pas de place sur un onglet : il reste dans le titre. */
  .sect-nav .sect-sub { display: none; }
  .sect-title { display: none; }
}

/* ── Onglets de Mon compte : une section a la fois ───────────────────────────
   Demande explicite : cliquer remplace la section affichee au lieu de faire
   defiler jusqu'a elle.

   `:not(.acc-on)` et NON `.acc-sect { display:none }` avec un `!important` :
   la carte « Mon avis » porte son propre `style="display:none"` en ligne,
   pose par le code qui sait si le compte a depose un avis. Un `!important`
   battrait ce style et afficherait une carte vide ; ici l'attribut en ligne
   garde autorite sur « cette carte existe-t-elle ? », et la classe ne decide
   que de « cet onglet est-il choisi ? ». Les deux questions restent separees. */
.acc-sect:not(.acc-on) { display: none; }

@media print {
  /* A l'impression on veut la page ENTIERE : un onglet est un confort d'ecran,
     pas une amputation du document. La carte « Mon avis » masquee en ligne le
     reste, son attribut n'etant pas touche ici. */
  .acc-sect:not(.acc-on) { display: block; }
  .sect-nav { display: none; }
}

/* Barre des pages d'administration : toujours une rangee, y compris sur grand
   ecran. `.sect-nav` seule passerait en colonne au-dessus de 992 px, ce qui
   n'a de sens que dans une mise en page a deux colonnes ; ici c'est un bandeau
   pose au-dessus du contenu de la page. */
.sect-nav.sect-nav-h {
  flex-direction: row; position: static; gap: 6px;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  padding-bottom: 6px; border-bottom: 1px solid var(--border);
}
.sect-nav.sect-nav-h a {
  width: auto; white-space: nowrap; flex: 0 0 auto;
  border-left: none; border-bottom: 3px solid transparent; border-radius: 8px 8px 0 0;
}
.sect-nav.sect-nav-h .active { border-left-color: transparent; border-bottom-color: var(--accent, #6366f1); }

/* ── Navigation mobile : rangee du haut + sous-rangees ──────────────────────
   La barre reprend `.sect-nav.sect-nav-h` (soulignement, defilement,
   recentrage). Ici : la pastille de profil et les volets de sous-categories. */
.nav-avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 50%;
  background: var(--accent, #6366f1); color: #fff;
  font-size: .72rem; font-weight: 700; letter-spacing: .02em;
}
.mnav-wrap .sect-nav { border-bottom: 1px solid var(--border); }
/* `rangee-defilante` porte display/overflow/defilement tactile et le
   non-retrecissement des enfants : ne restent ici que les reglages PROPRES a
   la sous-navigation mobile. Elle etait juste, mais c'etait la cinquieme
   ecriture de la meme regle. */
.mnav-sub {
  gap: 6px;
  padding: 6px 2px; border-bottom: 1px solid var(--border);
  background: var(--bg-card);
}
.mnav-sub a, .mnav-sub .nav-soon {
  text-decoration: none;
  color: var(--text-muted); font-size: .84rem; padding: 5px 10px; border-radius: 8px;
}
.mnav-sub a:hover { color: var(--text); }
.mnav-sub a.active { background: var(--bg); color: var(--text); font-weight: 600; }
.mnav-sub .nav-soon { opacity: .55; }
/* Sur telephone la recherche doit pouvoir se comprimer entre le logo et la
   pastille : sans min-width:0, le champ refuse et pousse la pastille dehors. */
@media (max-width: 991.98px) {
  .nav-search-input { min-width: 0; }

  /* ── Trois reperes, trois places ──────────────────────────────────────────
     Logo colle a gauche, recherche au MILIEU DE LA BARRE, profil colle a
     droite. En disposition flexible les trois elements se suivaient, donc la
     recherche se retrouvait juste apres le logo et non au centre : des marges
     automatiques ne l'auraient centree que si le logo et la pastille avaient
     la meme largeur, ce qui n'est pas le cas (environ 130 px contre 30).

     Une grille a trois colonnes dont les deux exterieures partagent le reste a
     parts egales place l'element du milieu au centre exact, quelles que soient
     les largeurs des deux autres. `1fr` et non `minmax(0,1fr)` : sur un ecran
     tres etroit, mieux vaut decaler le centre de quelques pixels que rogner le
     logo. `#nav` est `display:none` a cette largeur, donc il n'est pas une
     cellule de la grille : il y a bien trois elements. */
  /* Le retrait vient de DEUX endroits : la barre elle-meme (`.navbar`, 1,5 rem)
     et le conteneur (`px-4`). Ne corriger que le second laissait 24 px, donc
     ni le logo ni la pastille ne touchaient leur bord. */
  .navbar { padding-left: 0; padding-right: 0; }
  .navbar .nav-bar-inner {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    column-gap: 8px;
    /* `px-4` est une utilitaire Bootstrap, donc `!important` : sans le meme
       poids, le logo et la pastille gardent 24 px de retrait de chaque bord. */
    padding-left: 8px !important; padding-right: 8px !important;
  }
  .nav-bar-inner > .navbar-brand { justify-self: start; margin-right: 0; }
  .nav-bar-inner > .nav-search-wrap { justify-self: center; }
  /* `ms-auto` pose margin-left: auto !important : il faut le meme poids. */
  .nav-bar-inner > .navbar-nav { justify-self: end; margin-left: 0 !important; }
  /* Ouverte, la recherche prend toute la place restante : c'est la colonne du
     milieu qui devient extensible, les deux autres se reduisent a leur contenu.

     `:has(.nav-search-wrap:focus-within)` et NON `:focus-within` sur la barre :
     celui-ci se declenche pour n'importe quel descendant qui prend le focus,
     donc ouvrir le menu du profil elargissait la recherche. Il faut selectionner
     le parent a partir de l'enfant, ce que seul `:has()` permet. Un navigateur
     qui ne le connaitrait pas ignore ces deux regles et garde la pastille
     repliee : la recherche reste utilisable, elle ne s'agrandit simplement pas. */
  .nav-bar-inner:has(.nav-search-wrap:focus-within) {
    grid-template-columns: auto minmax(0, 1fr) auto;
  }
  .nav-bar-inner:has(.nav-search-wrap:focus-within) > .nav-search-wrap {
    justify-self: stretch; width: auto; max-width: none;
  }

  /* Recherche repliee : une pastille de la largeur de sa loupe, qui s'ouvre au
     toucher. Deployee en permanence, elle prenait toute la barre du haut pour
     une fonction qu'on utilise par intermittence, et son texte d'invite etait
     de toute facon coupe au milieu.

     `:focus-within` et non un `onfocus` en JavaScript : l'etat ouvert est celui
     du champ lui-meme, donc il se ferme tout seul, et la liste de suggestions
     etant DANS le meme conteneur, la choisir ne referme pas la recherche. */
  .nav-search-wrap {
    flex: 0 0 auto !important;
    width: 42px;
    transition: width .2s ease;
  }
  .nav-search-wrap:focus-within { flex: 1 1 auto !important; width: auto; }
  /* Repliee, la loupe est le SEUL contenu visible de la pastille : elle doit
     donc en occuper le centre. Ouverte, elle reprend sa place a gauche, devant
     le texte saisi. `left: 50%` plutot qu'une valeur en pixels : la pastille
     peut changer de largeur sans que l'icone se decale. */
  .nav-search-wrap::before {
    content: "";
    position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
    width: 15px; height: 15px; opacity: .55; pointer-events: none;
    background: no-repeat center/contain
      url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2.4' stroke-linecap='round'><circle cx='10.5' cy='10.5' r='6.5'/><line x1='15.5' y1='15.5' x2='21' y2='21'/></svg>");
  }
  .nav-search-wrap:focus-within::before { left: 13px; transform: translate(0, -50%); }
  .nav-search-wrap .nav-search-input { padding-left: 34px; }

  /* Pastille de profil : le rond colore SE SUFFIT. Le fond et le cadre du
     bouton dessinaient un carre arrondi autour d'un cercle, soit deux formes
     concentriques pour un seul objet. Sur ordinateur le bouton porte le mot
     « Reglages » et garde son fond : c'est la qu'il en a besoin. */
  .nav-settings-btn {
    background: none !important;
    border: none !important;
    padding: 0 !important;
  }
  .nav-settings-btn:hover,
  .nav-settings-btn:focus,
  .nav-settings-btn:active { background: none !important; box-shadow: none !important; }
  /* Repliee, l'invite serait tronquee a « Tick » : elle ne parait qu'ouverte. */
  .nav-search-wrap .nav-search-input::placeholder { color: transparent; }
  .nav-search-wrap:focus-within .nav-search-input::placeholder { color: var(--text2); }
}

/* ── La barre du haut ne pousse pas la page de cote, et ne passe pas sur DEUX
   lignes ─────────────────────────────────────────────────────────────────────
   Mesure au navigateur, avant premiere correction : avec les 10 onglets que
   donne le catalogue par defaut a un compte expert, `documentElement.scrollWidth`
   depassait `innerWidth` des 1280 px, largeur d'ordinateur portable tres
   courante ; a 992 px, la plus etroite ou la barre horizontale est encore
   affichee, des 7 onglets. La page entiere defilait alors lateralement et les
   derniers onglets sortaient de l'ecran.

   Premiere correction, insuffisante : `flex-wrap: wrap` arretait le
   debordement de la PAGE mais deplacait le defaut au lieu de le resoudre --
   la barre passait sur deux lignes des que les onglets ne tenaient plus sur
   une seule, un menu casse au premier coup d'oeil. La correction reprend
   plutot le mecanisme deja en place pour la barre mobile et la barre
   d'administration (`.sect-nav.sect-nav-h`, plus bas) : UNE seule rangee, qui
   defile horizontalement au lieu de deborder ou de s'empiler.

   `overflow-x: auto` seul sur `.navbar-nav` ne suffit pas : `#nav`, son
   PARENT, est lui-meme un element flexible (`flex-grow-1`, enfant de
   `.nav-bar-inner`) dont la largeur minimale automatique se calcule par
   defaut sur le contenu de ses descendants -- ici, la somme des onglets en
   `flex: 0 0 auto`. Sans `min-width: 0` sur `#nav`, cette largeur remonte
   telle quelle jusqu'a la barre puis a la page : c'est le piege classique de
   flexbox derriere « ca deborde meme avec `overflow-x: auto` posee ». */
#nav { min-width: 0; }
#nav .navbar-nav {
  flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
}
/* Un onglet ne doit pas se comprimer pour tenir : il deviendrait illisible
   avant meme que la barre ne defile. */
#nav .navbar-nav > .nav-item { flex: 0 0 auto; white-space: nowrap; }

/* ── Le menu d'un onglet a sous-categories (« Actions ▾ ») ───────────────────
   `overflow-x: auto` ci-dessus cree un contexte de DECOUPE, et il decoupe sur
   les DEUX axes : par specification, `overflow-y` ne peut pas rester `visible`
   en face d'un `overflow-x` qui ne l'est pas, il devient `auto` a son tour. Un
   menu deroulant, enfant de la rangee et en `position: absolute`, tombe donc
   sous la barre... et se fait rogner net. Mesure au navigateur : le menu
   s'ouvrait bel et bien (classe `show`, 220 x 82 px) mais aucun de ses points
   n'etait atteignable -- il ne s'affichait plus du tout.

   C'est le meme piege que la barre mobile evite en preferant une SOUS-RANGEE a
   un menu deroulant (voir `.mnav-sub` plus bas). Ici l'onglet est un vrai menu
   Bootstrap, et la sortie est `position: fixed`, qui echappe au decoupage de
   tout ancetre a defilement.

   Le placement, lui, ne peut pas venir du CSS : `fixed` se repere au VIEWPORT,
   pas a l'onglet. C'est le script « menu deroulant d'une barre qui defile »
   (base.html) qui pose `left`/`top` a l'ouverture.

   A noter : Popper ne sert a rien ici. Dans une `.navbar`, Bootstrap desactive
   lui-meme son moteur de placement (`applyStyles` off) et laisse le CSS
   positionner le menu -- c'est pourquoi une option `strategy: fixed` passee a
   Popper reste SANS EFFET, verifie au navigateur. */
#nav .navbar-nav .dropdown-menu { position: fixed; }

/* ── Reglage « Affichage » : hierarchie des onglets (/account) ───────────────
   Deux colonnes : a gauche on RANGE, a droite on VOIT le resultat. L'apercu
   n'est pas un ornement : sans lui, « ranger Watchlist dans Signaux » ne dit
   pas ce que la barre affichera, et il faut enregistrer pour le decouvrir.

   Une grille et non une disposition flexible : le menu parent, l'intitule et
   les fleches doivent s'aligner d'une ligne a l'autre, sinon la liste se lit
   comme un empilement de controles et non comme une hierarchie. L'en-tete
   partage EXACTEMENT le meme gabarit de colonnes que les lignes, sans quoi les
   titres designeraient la mauvaise colonne. */
.nav-ord-layout {
  display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: 18px; align-items: start;
}

/* Menus du compte. Le bloc est AU-DESSUS du tableau et non a cote : on cree un
   menu pour y ranger des pages, donc dans cet ordre, et un menu cree doit
   apparaitre dans les selecteurs juste en dessous sans qu'on ait a le chercher. */
.nav-menus { margin-bottom: 18px; }
.nav-menu-row {
  display: grid; grid-template-columns: minmax(0, 260px) 80px auto;
  gap: 8px; align-items: center; padding: 5px 0;
}
.nav-ord-btn:disabled { opacity: .35; cursor: default; }
.nav-ord-btn:disabled:hover { color: var(--text-muted); border-color: var(--border); }
.nav-menu-note {
  font-size: var(--fs-2xs); color: var(--text-muted);
  text-transform: uppercase; letter-spacing: .04em;
}
.nav-menu-suppr { width: 36px; }
.nav-menu-suppr:hover { color: var(--danger); border-color: var(--danger); }
.nav-ord-entete,
.nav-ord-row {
  display: grid;
  /* 175 px et non 150 : « Barre principale » est le plus long des quatre
     choix, et il ressortait tronque a « Barre principal ». */
  grid-template-columns: 175px minmax(0, 1fr) 80px;
  gap: 8px; align-items: center;
}
.nav-ord-entete {
  font-size: var(--fs-xs, .75rem); text-transform: uppercase;
  letter-spacing: .04em; color: var(--text-muted);
  padding-bottom: 6px; border-bottom: 1px solid var(--border);
}
.nav-ord-entete-simple { display: block; }
.nav-ord-row { padding: 7px 0; border-bottom: 1px solid var(--border); }
.nav-ord-lbl { font-size: var(--fs-sm); }
.nav-ord-fleches { display: grid; grid-template-columns: 36px 36px; gap: 8px; }
.nav-ord-btn {
  background: none; border: 1px solid var(--border); border-radius: 8px;
  color: var(--text-muted); font-size: 1rem; line-height: 1; padding: 6px 0;
}
.nav-ord-btn:hover { color: var(--text); border-color: var(--primary); }

/* Apercu : un onglet de barre est aligne a gauche, une entree de menu est
   decalee. C'est la seule chose qui distingue « parent » d'« enfant » d'un
   coup d'oeil, et elle doit se lire sans legende. */
.nav-apercu { padding-top: 6px; }
.nav-apercu-row {
  font-size: var(--fs-sm); padding: 4px 0 4px 2px; color: var(--text);
}
.nav-apercu-menu { font-weight: 600; }
.nav-apercu-enfant {
  padding-left: 20px; color: var(--text-muted);
  border-left: 2px solid var(--border); margin-left: 6px;
}
.nav-problemes { margin-top: 10px; }
.nav-probleme {
  font-size: var(--fs-sm); border-radius: 8px;
  padding: 7px 9px; margin-top: 6px; border: 1px solid var(--border);
}
.nav-probleme-bloquant { color: var(--danger); border-color: var(--danger); }
.nav-probleme-avertissement { color: var(--text-muted); }

@media (max-width: 991.98px) {
  .nav-ord-layout { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 575.98px) {
  .nav-ord-entete,
  .nav-ord-row { grid-template-columns: minmax(0, 1fr) 80px; }
  /* Le menu parent passe sur sa propre rangee : son titre de colonne n'a plus
     de colonne a titrer. `:not(.nav-ord-entete-simple)` est indispensable, sans
     quoi l'unique titre de l'apercu disparait avec lui. */
  .nav-ord-entete:not(.nav-ord-entete-simple) span:first-child { display: none; }
  .nav-ord-grp { grid-column: 1 / -1; }
}

/* ── Corps d'article de blog ─────────────────────────────────────────────────
   Le seul endroit du site ou l'on ecrit de la PROSE longue. Les regles vivent
   ici et non en attributs `style=` dans chaque article : un article est du
   texte, pas une page a mettre en page, et la prochaine plume ne doit pas avoir
   a recopier douze declarations pour ecrire un paragraphe.

   La largeur de ligne est bornee a 68 caracteres (`max-width` sur le conteneur
   parent, plus l'interlignage ici) : au-dela, l'oeil rate le debut de la ligne
   suivante. C'est la seule mesure de lisibilite qui fasse consensus en
   typographie. */
.blog-corps { font-size: 1rem; line-height: 1.72; color: var(--text2, var(--text-muted)); }
.blog-corps p { margin: 0 0 18px; }
.blog-corps h2 {
  font-size: 1.16rem; line-height: 1.4; color: var(--text);
  margin: 34px 0 12px;
}
.blog-corps h2:first-child { margin-top: 0; }
.blog-corps ul { margin: 0 0 20px; padding-left: 22px; }
.blog-corps li { margin-bottom: 9px; }
.blog-corps b { color: var(--text); font-weight: 600; }
.blog-corps a { color: var(--accent); }

/* ── Chargement entre les pages ────────────────────────────────────────────
   Barre fine en haut, pilotee par `static/chargement.js`. Une barre plutot
   qu'un rond centre : elle est peripherique, elle ne masque pas une page
   encore utilisable, et sa LARGEUR porte une information qu'un rond qui
   tourne ne porte pas.

   Elle est posee sur `--accent`, donc elle suit la palette choisie dans
   /admin/system-params. Une couleur ecrite ici en dur serait bleue au milieu
   d'une page bronze : c'est exactement le defaut que la section suivante
   corrige sur les indicateurs existants. */
.chargement-barre {
  position: fixed; top: 0; left: 0; height: 3px; width: 0;
  background: var(--accent);
  /* Au-dessus de la barre de navigation collante, sinon elle disparait
     derriere elle sur toutes les pages qui en ont une. */
  z-index: 2000;
  opacity: 0; pointer-events: none;
  transition: width .35s ease-out, opacity .2s ease;
  /* Une lueur en tete du trace : c'est ce qui distingue une barre qui avance
     d'un filet decoratif, au premier coup d'oeil et sans lire la largeur. */
  box-shadow: 0 0 8px var(--accent), 0 0 3px var(--accent);
}
.chargement-barre.actif { opacity: 1; }

/* Mouvement reduit : la CROISSANCE reste, parce qu'elle porte l'information ;
   c'est l'adoucissement et la lueur qui partent. Supprimer la barre entiere
   priverait ces lecteurs du seul retour visuel de l'attente (WCAG 2.3.3 vise
   le mouvement superflu, pas l'information). */
.chargement-barre.sans-mouvement { transition: opacity .2s ease; box-shadow: none; }
@media (prefers-reduced-motion: reduce) {
  .chargement-barre { transition: opacity .2s ease; box-shadow: none; }
}

/* ── Indicateurs de chargement : la couleur suit la palette ────────────────
   Les 20 indicateurs du site portaient `text-primary`, c'est-a-dire le bleu
   Bootstrap `#0d6efd`, FIGE. Or la palette va de l'indigo au bronze dore et au
   cuivre : sur les palettes chaudes, chaque chargement etait un bleu vif au
   milieu d'une page creme. Une couleur ecrite hors de `app/themes.py`, a vingt
   endroits, c'est la faute que ce module existe pour empecher.

   La regle est posee sur la CLASSE et non recopiee dans les gabarits : le jour
   ou un vingt-et-unieme indicateur apparait, il est deja de la bonne couleur. */
.spinner-border, .spinner-grow { color: var(--accent); }

/* ── Paysage sur telephone : la hauteur est la ressource rare ──────────────
   Mesure a 844x390 avant ce bloc : la coque (barre du haut + barre de filtres)
   prenait 134 px sur 390, soit 34 % de l'ecran, pour QUATRE cartes visibles
   contre onze en portrait. Le reflexe est de traiter le paysage comme un
   ecran large ; c'est l'inverse qu'il faut faire, la largeur y est abondante
   et la hauteur rare.

   `(pointer: coarse)` et non une largeur : la condition reelle est « le doigt
   est le pointeur sur un ecran court », pas « fenetre basse ». Une fenetre de
   bureau reduite n'a pas ce probleme et perdrait ses reperes pour rien. Meme
   raison que la regle de zoom des champs. */
@media (orientation: landscape) and (max-height: 500px) and (pointer: coarse) {
  /* La barre du haut passe de 80 a ~52 px : le logo et la pastille de profil
     restent lisibles, c'est le rembourrage qui part. */
  .navbar { padding-top: .3rem; padding-bottom: .3rem; }
  .navbar-brand { font-size: var(--fs-md); }

  /* La rangee d'onglets du telephone se resserre. Elle DEFILE deja
     horizontalement, donc elle n'a besoin d'aucune largeur supplementaire :
     seule sa hauteur se paie. */
  .mnav-wrap { padding-top: 0 !important; }
  .mnav-wrap .sect-nav a, .mnav-wrap .sect-nav button { padding: 5px 10px; }

  /* La barre de filtres collante : meme traitement, et elle reste collante,
     c'est ce qui permet de filtrer sans remonter. */
  #mobileFilterBar { padding: 5px 0 6px; margin-bottom: 6px; }
  .mfb-filter-btn, .mfb-chip { padding-top: 4px; padding-bottom: 4px; }

  /* Les feuilles par le bas couvriraient tout l'ecran sur 390 px de haut :
     elles s'y limitent a 85 % et defilent a l'interieur, sinon leur bouton
     de validation devient inatteignable. */
  .dropdown-menu.show, .focused-sheet { max-height: 85vh; }
}


/* ── Plein ecran d'un graphique ────────────────────────────────────────────
   Deux classes, deux responsabilites, et c'est la distinction qui compte :

   `.pe-plein`  la MISE EN PAGE, posee des que le plein ecran est actif, natif
                compris. Elle vivait dans `.pe-repli` seule, donc en natif le
                graphique gardait ses 260 px au milieu d'une dalle entiere :
                le bouton paraissait inerte alors qu'il avait bien bascule.
   `.pe-repli`  ce qui SUBSTITUE le plein ecran quand le navigateur ne l'a
                pas. Necessaire sur Safari iOS, qui n'implemente
                `requestFullscreen` que sur <video>.

   `background` explicite dans les deux cas : transparent, la page continuerait
   de se voir derriere le graphique (en natif le navigateur peindrait du noir,
   qui n'est le fond d'aucun des trois themes). */
.pe-plein {
  display: flex;
  flex-direction: column;
  background: var(--bg, #0f172a);
}
.pe-plein .pe-zone { flex: 1 1 auto; min-height: 0; }
.pe-repli {
  position: fixed;
  inset: 0;
  z-index: 1080;
  width: 100vw !important;
  height: 100vh !important;
  height: 100dvh !important;   /* iOS : exclut les barres du navigateur */
  max-width: none !important;
  margin: 0 !important;
  padding: 8px !important;
}
/* Le plein ecran natif ne redimensionne PAS le contenu : sans padding, le
   trace colle aux bords de la dalle et les graduations sont coupees. */
.pe-plein:fullscreen { padding: 8px; }


/* ── Debordement horizontal sur telephone ──────────────────────────────────
   Un seul element trop large fait glisser la PAGE ENTIERE vers la droite, et
   le coupable est invisible : ce qu'on voit, c'est le titre qui part hors de
   l'ecran. Ces regles bornent les suspects habituels a la largeur de leur
   parent, chacune a la SOURCE plutot que par un `overflow-x: hidden` sur
   `body`, qui masquerait le defaut au lieu de le corriger (et couperait le
   contenu sans que rien ne le dise).

   `min-width: 0` sur les enfants de flex : sans lui, un enfant refuse de
   descendre sous sa largeur de contenu, et pousse le conteneur au-dela du
   viewport -- c'est la cause la plus frequente et la moins visible. */
@media (max-width: 576px) {
  .card-body, .card-header { min-width: 0; }
  .d-flex > * { min-width: 0; }
  /* Un mot sans espace (ticker, URL, cle technique) ne peut pas etre coupe
     par le navigateur : il deborde tel quel. */
  .card-body, .card-header, td, th, dd, dt, p, li { overflow-wrap: anywhere; }
  /* Les images et canvas ne depassent jamais leur boite. */
  img, canvas, svg { max-width: 100%; }
  /* Une cellule DANS un tableau a son propre cadre de defilement n'a pas
     besoin de la coupe generique ci-dessus : ce que cette regle empeche --
     un mot qui ferait glisser la PAGE ENTIERE -- ne peut de toute facon pas
     arriver la, puisque seul le cadre du tableau (`.table-responsive`)
     defile, jamais la page. La regle n'y sert donc a rien et y coute tout :
     un nombre a plusieurs decimales (`15,3245`) se lisait alors chiffre par
     chiffre, une lettre par ligne -- MEME defaut que celui deja corrige sur
     `.fig-recap-nom` juste plus bas, ici sur des valeurs et non un libelle.
     `word-break: normal` est necessaire EN PLUS d'`overflow-wrap: normal` :
     le premier ne suffit pas seul a annuler le second sur tous les
     navigateurs. */
  .table-responsive td, .table-responsive th { overflow-wrap: normal; word-break: normal; }
}


/* ── Catalogue des figures (/figures) ──────────────────────────────────────
   Treize fiches, chacune portant sa forme ET sa mesure : la page tenait sur
   quatre ecrans de prose dense, ou rien ne se comparait a rien. Trois remedes,
   et aucun ne cache une mesure -- ils la RANGENT.

   1. Le recapitulatif AVANT les fiches : une ligne par figure, les memes
      valeurs que la fiche (jamais un second calcul), pour comparer d'un coup
      d'oeil ce qui demandait treize allers-retours.
   2. Les chiffres d'une fiche sous un `<details>` NATIF : ni script, ni
      dependance, ouvrable au clavier, et le verdict reste toujours visible
      dans le resume -- c'est lui qu'on lit, pas l'effectif.
   3. Un filtre qui MASQUE des lignes ne ment pas tant qu'il DIT combien il en
      masque : `#figFiltreCompte` porte le compte a chaque changement. */
.fig-recap { font-size: 12px; }
/* Le `white-space: nowrap` qui etait ici est desormais porte par la regle
   `thead th` unique, plus haut dans ce fichier. */
.fig-recap th { font-weight: 600; }
.fig-recap td, .fig-recap th { padding: 5px 8px; vertical-align: middle; }
/* La regle mobile generale coupe N'IMPORTE OU (`overflow-wrap: anywhere`) :
   necessaire pour un ticker ou une URL, desastreux pour un libelle, ou elle
   rendait « Biais hauss / ier » sur deux lignes. Le tableau ayant son propre
   cadre de defilement, ces deux colonnes peuvent rester entieres. */
.fig-recap .fig-recap-nom { overflow-wrap: normal; word-break: normal; }
/* Une ligne mene a sa fiche : c'est un lien, le curseur doit le dire. */
.fig-recap tbody tr { cursor: pointer; }
.fig-recap tbody tr:hover { background: var(--bg3, rgba(255, 255, 255, .04)); }
/* La fiche visee par le recapitulatif se signale une seconde : sans cela, un
   saut de quatre ecrans laisse chercher laquelle des deux colonnes on vient
   d'ouvrir. */
.fig-carte-visee { outline: 2px solid var(--accent, #38bdf8); outline-offset: 2px; }

.fig-details > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.fig-details > summary::-webkit-details-marker { display: none; }
/* Sans `min-width: 0`, la pastille de verdict refuse de descendre sous sa
   largeur de texte et pousse le resume hors de sa carte sur telephone : c'est
   la cause de debordement la plus frequente et la moins visible. */
.fig-details > summary { min-width: 0; }
.fig-details > summary > * { min-width: 0; }
.fig-details > summary .badge { white-space: normal; text-align: left; }
/* Le chevron dit qu'il y a quelque chose dessous : un resume sans marqueur se
   lit comme une ligne morte et personne ne l'ouvre. */
.fig-details > summary::after {
  content: "\203A";
  margin-left: auto;
  font-size: 15px;
  line-height: 1;
  color: var(--text-muted, #94a3b8);
  transform: rotate(90deg);
  transition: transform .15s;
}
.fig-details[open] > summary::after { transform: rotate(-90deg); }

/* Le curseur de confiance : le meme rang visuel que les interrupteurs de
   couches, dont il partage la barre. */
.fig-curseur { width: 120px; max-width: 40vw; vertical-align: middle; }

/* ── Les cartes de figures menent au trace ─────────────────────────────────
   Elles annoncaient une figure quotidienne, hebdomadaire et mensuelle sans que
   rien a l'ecran ne dise LAQUELLE des bougies chacune decrit -- et pour cause :
   la figure hebdomadaire n'est visible que sur une plage en bougies
   hebdomadaires. Une carte portant une figure est donc un bouton.

   `fig-carte-vue` marque celle que le trace montre en ce moment. La bordure
   suffit : un fond plein ferait concurrence a l'aplat de la figure sur le
   graphique, qui est ce qu'on veut regarder. */
.fig-carte { border-color: var(--border) !important; }
.fig-carte-active { cursor: pointer; transition: border-color var(--t-fast); }
.fig-carte-active:hover,
.fig-carte-active:focus-visible { border-color: var(--accent) !important; }
.fig-carte-vue { border-color: var(--accent) !important; border-width: 2px !important; }

/* ── « Ce qui s'est passe apres, dans le passe » ────────────────────────────
   Une barre par horizon : elle couvre la moitie centrale des ecarts observes,
   un trait marque leur mediane, un repere marque le zero (« comme le reste du
   marche »). L'echelle est COMMUNE aux horizons -- une echelle par ligne
   ferait paraitre identiques des dispersions qui ne le sont pas.

   Aucune de ces formes n'est posee dans l'espace du futur : c'est une mesure
   du passe, elle se lit sous le graphique et non a droite de la derniere
   bougie. */
.fig-disp { display: flex; flex-direction: column; gap: 4px; margin-top: 4px; }
.fig-disp-ligne { display: flex; align-items: center; gap: 8px; font-size: 11px; }
.fig-disp-h { flex: 0 0 auto; min-width: 74px; color: var(--text2); }
.fig-disp-piste {
  position: relative; flex: 1 1 auto; min-width: 0; height: 14px;
  background: var(--bg3, rgba(255, 255, 255, .05)); border-radius: 3px;
}
.fig-disp-boite {
  position: absolute; top: 3px; height: 8px; border-radius: 2px;
  background: var(--accent); opacity: .35;
}
.fig-disp-med {
  position: absolute; top: 1px; height: 12px; width: 2px; margin-left: -1px;
  background: var(--accent);
}
.fig-disp-zero {
  position: absolute; top: 0; height: 14px; width: 1px;
  background: var(--text-muted); opacity: .5;
}
.fig-disp-n { flex: 0 0 auto; color: var(--text-muted); font-size: 10px; }
@media (max-width: 576px) {
  /* Sur telephone la piste doit garder la priorite : c'est elle qui porte la
     mesure, les deux libelles peuvent se serrer. */
  .fig-disp-h { min-width: 58px; }
  .fig-disp-n { display: none; }
}

/* ── Une rangee qui DEFILE horizontalement ──────────────────────────────────
   L'AUTORITE UNIQUE sur la question, parce qu'elle etait ecrite quatre fois et
   juste une seule : `.mfb-chips` du tableau de bord posait `flex: 0 0 auto` sur
   ses pastilles, les trois autres rangees l'oubliaient.

   Sans ce `flex: 0 0 auto`, les enfants d'un conteneur `flex` RETRECISSENT sous
   la largeur de leur contenu des que la place manque. `white-space: nowrap`
   empeche alors le repli, le texte deborde de sa boite, et `overflow-x: auto`
   du parent le ROGNE : les libelles s'affichent coupes -- « Synthe », « Publica »,
   « Histor » -- au lieu que la rangee defile, ce pour quoi elle est faite.
   Le defaut ne se voit sur AUCUN ecran large, seulement sur telephone. */
.rangee-defilante {
  display: flex;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.rangee-defilante > * {
  flex: 0 0 auto;
  white-space: nowrap;
}

/* ── Le descriptif d'une page, range derriere une pastille ──────────────────
   Voir `templates/_aide_bulle.html`. Ces phrases separent une mesure d'une
   recommandation : elles ne se suppriment pas, elles se rangent. */
.aide-bulle { position: relative; display: inline-block; vertical-align: middle; }
.aide-pastille {
  width: 22px; height: 22px; padding: 0; line-height: 1;
  border-radius: 50%; cursor: pointer;
  border: 1px solid var(--accent); background: transparent;
  color: var(--accent); font-size: .78rem; font-weight: 700;
}
.aide-pastille:hover,
.aide-pastille[aria-expanded="true"] { background: var(--accent); color: #fff; }
.aide-panneau {
  position: absolute; top: 28px; right: 0; z-index: 1035;
  width: min(320px, calc(100vw - 32px));
  display: flex; flex-direction: column; gap: 8px;
  padding: 11px 13px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--card-bg, var(--bg));
  color: var(--text); box-shadow: 0 8px 24px rgba(0,0,0,.18);
  font-size: .78rem; line-height: 1.5; font-weight: 400; text-align: left;
  white-space: normal;
}
/* `hidden` porte l'etat ; sans ce rappel, `display:flex` l'ecraserait et le
   panneau resterait ouvert en permanence. */
.aide-panneau[hidden] { display: none; }
.aide-para { display: block; }

/* ── La synthese redigee : une page a LIRE, pas un journal de sortie ────────
   Le texte etait rendu en `white-space: pre-wrap`, d'un seul bloc, titres de
   section en capitales noyes dedans. Le signalement disait « la vue est
   horrible, pas tres beau a lire », et il avait raison : une coulee de six
   cents mots sans respiration ni mesure ne se lit pas, elle se subit. */
.syn-corps { padding: 18px 20px 16px; }
.syn-sect { margin: 0 0 20px; }
.syn-sect:last-of-type { margin-bottom: 22px; }

/* Les titres cessent de crier. Petites capitales espacees, discretes, en
   couleur d'accent : ils STRUCTURENT au lieu d'interrompre. */
.syn-titre {
  font-size: .72rem; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--accent);
  margin: 0 0 7px; padding-bottom: 5px;
  border-bottom: 1px solid var(--border);
}

/* La prose passe en SERIF. C'est ce qui change le plus la lecture d'un texte
   long : la graisse variable et les empattements guident l'oeil de mot en mot,
   la ou une sans-serif de tableau de bord fatigue au bout de trois lignes.

   AUCUNE borne de mesure : la prose prend la largeur de la carte. Elle a
   d'abord ete bornee a 78 caracteres -- au-dela, l'oeil perd sa ligne en
   revenant a la marge -- mais sur un ecran large la carte fait le double, et
   la moitie droite restait vide. Entre une ligne longue et un blanc de six
   cents pixels, c'est le blanc qui se voit. Le jour ou la ligne parait trop
   longue, la reponse est de borner LA CARTE, pas le texte : tout s'aligne
   alors, titres et tableau compris. */
.syn-para {
  font-family: Georgia, "Iowan Old Style", "Times New Roman", ui-serif, serif;
  font-size: .95rem; line-height: 1.68; margin: 0 0 10px;
  color: var(--text);
}
.syn-para:last-child { margin-bottom: 0; }

/* La derniere section d'une synthese dit ce que la lecture NE dit PAS : une
   reserve, pas une affirmation. L'italique porte cette nuance -- c'est le seul
   endroit ou il aide au lieu de ralentir. */
.syn-sect:last-of-type .syn-para { font-style: italic; opacity: .9; }

.syn-table-wrap { overflow-x: auto; margin: 0 0 4px; }
.syn-table { width: 100%; border-collapse: collapse; font-size: .82rem; }
/* Le `white-space: nowrap` qui etait ici est desormais porte par la regle
   `thead th` unique, plus haut dans ce fichier. */
.syn-table th {
  text-align: left; font-weight: 600; font-size: .68rem;
  letter-spacing: .05em; text-transform: uppercase; color: var(--text-muted);
  padding: 0 10px 6px 0; border-bottom: 1px solid var(--border);
}
.syn-table td {
  padding: 7px 10px 7px 0; border-bottom: 1px solid var(--border);
  vertical-align: baseline;
}
.syn-table tr:last-child td { border-bottom: none; }
/* La colonne du moteur portait la cle FRED brute, donc du monospace efface.
   Elle porte maintenant un LIBELLE : du texte ordinaire, a la meme couleur que
   le reste du tableau. Seule la cle, en pastille, reste en monospace. */
.syn-td-serie { white-space: nowrap; }
.syn-via { display: block; font-size: .68rem; opacity: .7; }
.syn-ticker {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .7rem; opacity: .55; margin-left: 4px;
}
.syn-meca {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; margin-left: 5px; border-radius: 50%;
  border: 1px solid var(--border); color: var(--text-muted);
  font-size: .58rem; font-style: italic; cursor: help; vertical-align: middle;
}
/* Le pivot sectoriel : une cellule peut porter PLUSIEURS moteurs. Ils
   s'empilent, un par ligne, plutot que de couler en phrase -- une liste de
   trois moteurs separes par des virgules se relit deux fois. */
.syn-td-moteurs { min-width: 11rem; }
.syn-moteur {
  display: block; font-size: .78rem; line-height: 1.5; white-space: normal;
}
.syn-rien { font-size: .74rem; font-style: italic; color: var(--text-muted); }

/* Les liens du tableau. Discrets au repos -- souligner vingt lignes ferait du
   tableau un hachurage -- mais soulignes au survol et au FOCUS clavier, sans
   quoi rien ne signale qu'ils sont cliquables a qui ne promene pas de souris. */
.syn-lien { color: inherit; text-decoration: none; }
.syn-lien:hover, .syn-lien:focus-visible { text-decoration: underline; }

/* La marque d'une ligne dont le signe a ete LU et non derive. Elle porte le
   mot, pas seulement une teinte : une pastille distinguee par la seule couleur
   ne dit rien a l'impression ni a qui ne separe pas les nuances. */
.syn-lu {
  display: inline-block; margin-left: 5px; padding: 0 5px; border-radius: 3px;
  border: 1px solid var(--border); color: var(--text-muted);
  font-size: .62rem; letter-spacing: .03em; text-transform: uppercase;
  white-space: nowrap; cursor: help; vertical-align: middle;
}

.syn-th-signe, .syn-td-signe { text-align: right; white-space: nowrap; }
.syn-signe { font-weight: 600; font-size: .78rem; }
.syn-positif { color: var(--green, #15803d); }
.syn-negatif { color: var(--red, #b91c1c); }
.syn-mixte   { color: var(--warning, #b45309); }

/* ── Tableau d'impacts groupe par moteur ─────────────────────────────────────
   Une ligne par moteur, ses cibles alignees dans une seule cellule. Le tableau
   s'ecrivait a plat : un moteur qui touche cinq actifs occupait cinq lignes et
   repetait son libelle cinq fois. */

/* Le bandeau de comptes. `auto-fit` et non quatre colonnes fixes : a 390 px
   quatre tuiles cote a cote rendent chaque nombre illisible. */
.syn-tuiles {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(112px, 1fr));
  gap: 8px; margin: 0 0 12px;
}
.syn-tuile {
  background: var(--bg2); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: 7px 12px;
}
.syn-tuile b { display: block; font-size: 1.15rem; line-height: 1.25; }
.syn-tuile small { font-size: var(--fs-4xs); color: var(--text-muted); }
/* Le fond teinte DOUBLE le signe deja porte par la fleche et par le mot ;
   il ne le porte jamais seul. */
.syn-tuile-pos { border-color: var(--green, #15803d); }
.syn-tuile-pos b { color: var(--green, #15803d); }
.syn-tuile-neg { border-color: var(--red, #b91c1c); }
.syn-tuile-neg b { color: var(--red, #b91c1c); }

/* La cellule des cibles : elles s'ENROULENT au lieu de forcer une largeur.
   Une ligne de cinq cibles sur un ecran etroit deborderait le tableau, et
   c'est le corps de la page qui defilerait.

   Le `display: flex` se pose sur un CONTENEUR interne et JAMAIS sur la
   cellule : sur un `<td>`, il en chasse le `display: table-cell`, le navigateur
   l'enveloppe d'une cellule anonyme, et la cellule cesse alors de s'etirer a la
   hauteur de la ligne comme de partager sa ligne de base. Les trois colonnes
   se decalaient et le filet de separation se tracait a mi-hauteur. */
.syn-td-cibles { white-space: normal; }
.syn-table-imp .syn-cibles { display: flex; flex-wrap: wrap; gap: 4px 14px; }
.syn-cible { font-size: .78rem; line-height: 1.5; white-space: nowrap; }
/* Le pictogramme est DECORATIF : il repete une information que le libelle
   porte a cote, d'ou `aria-hidden` dans le gabarit. La largeur fixe aligne les
   libelles entre eux, les emojis n'ayant pas tous la meme chasse. */
.syn-ico {
  display: inline-block; width: 1.15em; margin-right: 2px;
  text-align: center; font-style: normal;
}
.syn-compte {
  display: block; font-size: var(--fs-4xs); color: var(--text-muted);
  font-weight: 400;
}

/* Bloc « A surveiller cette semaine », PARTAGE par /calendrier et /macro.
   Les regles vivaient dans le <style> de /calendrier ; le partiel etant
   desormais inclus par les deux pages, elles doivent vivre la ou les deux les
   trouvent. */
.cal-top-ev {
  display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap;
  padding: 5px 0; border-top: 1px solid var(--border);
}
.cal-top-ev:first-of-type { border-top: 0; }
.cal-pays {
  display: inline-block; border: 1px solid var(--border); border-radius: 6px;
  padding: 0 6px; font-size: .72rem; font-weight: 600;
}

/* La legende est REPLIEE : vingt-et-une entrees identiques a chaque visite,
   sous un tableau qui change tous les matins. Le trait de separation passe sur
   le depliant, qui est ce qu'on voit fermee. */
.syn-legende-d {
  margin: 8px 0 0; padding-top: 8px; border-top: 1px dashed var(--border);
}
.syn-legende {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 3px 12px;
  margin: 6px 0 0;
  font-size: var(--fs-4xs); color: var(--text-muted);
}
.syn-legende-t {
  font-weight: 600; text-transform: uppercase; letter-spacing: .05em;
  font-size: var(--fs-4xs); color: var(--text-muted); cursor: pointer;
}

.syn-note {
  margin: 14px 0 0; padding-top: 10px; border-top: 1px solid var(--border);
  font-size: .72rem; font-style: italic; color: var(--text-muted);
  line-height: 1.5;
}

@media (max-width: 576px) {
  .syn-corps { padding: 14px; }
  /* La colonne du moteur passe sous le libelle plutot que de comprimer les
     deux : a 390 px, trois colonnes rendent chaque cellule illisible. */
  .syn-table, .syn-table tbody, .syn-table tr, .syn-table td { display: block; }
  .syn-table thead { display: none; }
  .syn-table tr {
    padding: 9px 0; border-bottom: 1px solid var(--border);
  }
  .syn-table tr:last-child { border-bottom: none; }
  .syn-table td { padding: 0; border: none; }
  .syn-td-signe { text-align: left; margin-top: 3px; }
  /* La cle FRED se faisait COUPER au bord droit : en pile, la cellule du
     moteur n'a plus la largeur du tableau, et `nowrap` la poussait dehors. */
  .syn-td-serie { white-space: normal; }
}

/* ── « De quoi cette page est faite » : panneau d'administration ─────────────
   Visible du seul administrateur, sur toutes les pages. En bas a GAUCHE :
   la bulle d'avis occupe deja le coin droit, et deux pastilles superposees
   rendraient les deux inutilisables sur telephone. */
.tp-bouton {
  position: fixed; left: 14px; bottom: 14px; z-index: 1035;
  border: 1px solid var(--bordure, #334155);
  background: var(--bg2, #1e293b); color: var(--texte-doux, #94a3b8);
  border-radius: 999px; padding: 4px 12px;
  font-size: 11px; letter-spacing: .04em; text-transform: uppercase;
  opacity: .55; transition: opacity .15s ease;
}
.tp-bouton:hover, .tp-bouton:focus-visible { opacity: 1; }

.tp-panneau {
  position: fixed; left: 14px; bottom: 52px; z-index: 1036;
  width: min(560px, calc(100vw - 28px));
  max-height: min(60vh, 520px); overflow-y: auto;
  background: var(--bg2, #1e293b); color: var(--texte, #e2e8f0);
  border: 1px solid var(--bordure, #334155); border-radius: 10px;
  box-shadow: 0 18px 50px #00000066;
}
/* `[hidden]` est une regle de l'agent utilisateur : une declaration d'auteur
   la bat. Sans `:not([hidden])`, le panneau reste ouvert quoi qu'il arrive. */
.tp-panneau:not([hidden]) { display: block; }

.tp-tete {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; padding: 10px 12px;
  border-bottom: 1px solid var(--bordure, #334155);
  font-size: 13px;
}
.tp-fermer {
  background: none; border: none; color: var(--texte-doux, #94a3b8);
  font-size: 20px; line-height: 1; padding: 0 4px;
}
.tp-corps { padding: 10px 12px; font-size: 12px; }
.tp-ou {
  font-family: monospace; font-size: 11px;
  color: var(--texte-doux, #94a3b8); margin-bottom: 8px;
  word-break: break-all;
}
/* Le tableau defile DANS son cadre : c'est lui qui absorbe le debordement,
   jamais la page, qui ne doit pas se decaler lateralement. */
.tp-cadre { overflow-x: auto; }
.tp-table { width: 100%; border-collapse: collapse; }
.tp-table th, .tp-table td {
  padding: 5px 8px; text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--bordure, #334155);
}
.tp-table th { font-size: 11px; color: var(--texte-doux, #94a3b8); font-weight: 600; }
.tp-nom { font-family: monospace; white-space: nowrap; }
.tp-modele { color: var(--texte-doux, #94a3b8); white-space: nowrap; }
.tp-sql { white-space: nowrap; font-size: 11px; }
.tp-vide, .tp-reserve {
  color: var(--texte-doux, #94a3b8); font-size: 11px; font-style: italic;
  margin: 8px 0 0;
}

/* ── Ajuster la largeur d'une colonne a la main ────────────────────────────
   Le mecanisme vit dans `static/colonnes.js`, qui porte le POURQUOI de chaque
   choix (feuille de style plutot que largeurs sur les cellules, tout fige des
   la premiere poignee, rien sur le telephone). Ici, seulement l'apparence. */
table[data-colonnes] > thead > tr > th { position: relative; }
.colonnes-poignee {
  position: absolute; top: 0; right: 0; width: 9px; height: 100%;
  cursor: col-resize; touch-action: none; user-select: none; z-index: 3;
}
.colonnes-poignee::before {
  content: ""; position: absolute; top: 20%; bottom: 20%; right: 3px;
  width: 2px; background: var(--border); border-radius: 2px;
}
.colonnes-poignee:hover::before, .colonnes-poignee.tire::before {
  top: 0; bottom: 0; background: var(--accent);
}
body.colonnes-en-cours { cursor: col-resize; user-select: none; }
table.colonnes-fixees { table-layout: fixed; }
/* Une largeur choisie a la main doit etre TENUE. Deux regles s'y opposaient :
   un en-tete `nowrap` (le tableau de bord en pose un sur tous les siens)
   empeche la colonne de se resserrer sous la largeur de son titre, et un mot
   insecable deborde sur la colonne voisine au lieu de se couper. */
table.colonnes-fixees > thead > tr > th { white-space: normal; }
table.colonnes-fixees > tbody > tr > td { overflow: hidden; overflow-wrap: anywhere; }
.colonnes-raz {
  display: block; margin: 0 0 4px auto; border: 1px solid var(--border);
  background: var(--card-bg); color: var(--text-muted); border-radius: 6px;
  font: inherit; font-size: .72rem; padding: 2px 9px; cursor: pointer;
}
.colonnes-raz:hover { border-color: var(--accent); color: var(--text); }
@media (max-width: 640px) {
  .colonnes-poignee, .colonnes-raz { display: none !important; }
}

/* ── La bulle « ? » d'un terme technique (app/glossaire.py) ────────────────
   Un `<details>` et non un `title=` ni un composant Bootstrap : le `title`
   natif n'existe pas au doigt -- donc pas sur telephone -- et un composant
   demanderait une initialisation JavaScript, que `script-src` refuse depuis
   qu'il n'a plus 'unsafe-inline'. Celui-ci s'ouvre au clic comme au doigt,
   sans une ligne de script. */
.aide { display: inline; position: relative; }
.aide > summary {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.05em; height: 1.05em; margin-left: .25em; padding: 0;
  border: 1px solid var(--border); border-radius: 50%;
  font-size: .78em; line-height: 1; font-weight: 700;
  color: var(--text-muted); background: var(--bg2);
  cursor: help; list-style: none; user-select: none; vertical-align: baseline;
}
.aide > summary::-webkit-details-marker { display: none; }
.aide > summary:hover, .aide > summary:focus-visible {
  color: var(--text); border-color: var(--text-muted); outline: none;
}
.aide[open] > summary { color: var(--text); border-color: var(--text-muted); }
.aide-terme {
  position: absolute; z-index: 30; left: 0; top: 1.6em;
  display: block; width: max-content; max-width: min(30rem, 82vw);
  padding: .6rem .75rem;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg2); color: var(--text);
  font-size: .82rem; font-weight: 400; line-height: 1.45; text-align: left;
  white-space: normal; box-shadow: 0 6px 20px rgba(0, 0, 0, .28);
}
.aide-terme b { display: block; margin-bottom: .2rem; }
/* Pres du bord droit, la bulle sortirait de l'ecran : on l'ancre a droite. */
td:last-child .aide-terme, th:last-child .aide-terme,
.aide.aide-fin .aide-terme { left: auto; right: 0; }
@media (max-width: 576px) {
  .aide-terme { position: fixed; left: 4vw; right: 4vw; width: auto; max-width: 92vw; }
}
