/* ============================================================
   POLICES — hebergees ici, JAMAIS depuis Google Fonts
   /!\ L'espace client est AUTHENTIFIE : l'adresse IP envoyee a Google a
   chaque visite serait une donnee nominative, et cette pratique a deja ete
   sanctionnee en Europe. Six ecrans les chargeaient depuis Google
   (connexion, inscription, mots de passe oublies, demande, souscription) —
   rapatriees le 2026-08-16.
   Geist et JetBrains Mono sont sous licence OFL : l'hebergement local est
   permis. Seuls les sous-ensembles latin et latin-ext sont embarques (308 Ko) ;
   les alphabets cyrillique, grec et vietnamien de Google sont ecartes.
   ============================================================ */
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url('/static/fonts/Geist-300-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url('/static/fonts/Geist-300-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/Geist-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/Geist-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/Geist-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/Geist-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/Geist-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/Geist-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/Geist-700-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/Geist-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/JetBrainsMono-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/JetBrainsMono-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/JetBrainsMono-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/JetBrainsMono-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ============================================================
   TVNI — Charte graphique v3 (light only, vanilla CSS)
   Source de vérité unique pour tous les tokens design.
   ============================================================ */

/* ============================================================
   :root — Design tokens
   ============================================================ */
:root {
  /* Surfaces */
  /* ======================================================================
     SOMBRE — le seul thème. Décision du fondateur, 2026-08-16.
     Palette reprise de la vitrine (`landing/index.html`) : les deux
     partageaient déjà `#4A3B7A`, l'application n'en était que l'expression
     claire. Elle rejoint la sombre.
     /!\ Le mode clair est ABANDONNÉ. Ses valeurs sont dans l'historique git —
     ne pas les laisser traîner en commentaire ici.
     ====================================================================== */
  --background:          #0A0B10;
  --card:                #14161F;
  --muted:               #1A1D27;
  --border:              rgba(255,255,255,0.07);
  /* Liseré plus affirmé, pour ce qui doit se détacher du fond : boutons
     secondaires, champs au survol, cartes posées sur une surface déjà sombre.
     Vient des écrans d'authentification, qui portaient leur propre vocabulaire
     (`--border-2`) — rapatrié ici le 2026-08-16. */
  --border-strong:       rgba(255,255,255,0.12);
  /* Haut du degrade du bouton principal, et lueur qui le fait flotter. Viennent
     des ecrans d'authentification, ou ce bouton etait le plus abouti de
     l'application — promu ici le 2026-08-16 pour que tous les ecrans en heritent. */
  --accent-hi:           #C8BEFF;
  --accent-glow:         rgba(139,127,224,0.30);
  --accent-glow-strong:  rgba(139,127,224,0.45);
  /* Survol (repris de la vitrine, 2026-08-16) : un liseré d'accent tres discret
     sur une carte qu'on souleve, et un voile de la meme teinte sur un bloc qui
     s'eclaire sans bouger. Trois valeurs, pas une : le liseré d'une carte doit
     s'effacer devant celui d'un bloc directement survolé. */
  --accent-ring:         rgba(180,168,255,0.12);
  --accent-ring-strong:  rgba(180,168,255,0.25);
  --accent-wash:         rgba(180,168,255,0.04);
  --ring:                #B4A8FF;

  /* Texte */
  --foreground:          #F5F4F0;
  --muted-foreground:    rgba(245,244,240,0.62);
  --disabled-foreground: rgba(245,244,240,0.40);
  --on-primary:          #0A0B10;
  --on-accent:           #14161F;

  /* Marque
     /!\ L'accent CHANGE de valeur par rapport au thème clair (#4A3B7A) : un
     violet foncé sur fond noir ne se lit pas. C'est le RÔLE qui est stable,
     pas la valeur — c'est exactement à ça que servent ces variables. */
  --primary:             #8B7FE0;
  --accent:              #B4A8FF;

  /* Sémantique */
  --success:             #4ADE80;
  --error:               #F87171;
  --warning:             #FBBF24;
  --warning-text:        #FCD34D;
  --info:                #7DD3FC;

  /* Trading */
  --profit:              #4ADE80;
  --loss:                #F87171;
  --neutral:             rgba(245,244,240,0.55);

  /* Charts */
  --chart-1:             #B4A8FF;
  --chart-2:             #8B7FE0;
  --chart-3:             #7DD3FC;
  --chart-4:             #4ADE80;
  --chart-5:             rgba(245,244,240,0.55);

  /* Typographie */
  --font-sans:           'Geist', -apple-system, BlinkMacSystemFont, sans-serif;
  /* /!\ `--font-mono` n'est PLUS une police a chasse fixe (2026-08-24, demande du
     fondateur : « je n'aime pas la police des titres de KPI, mets la meme que
     l'autre »). Le nom reste parce qu'il est cite ~120 fois, dans la charte ET
     dans les gabarits : le repointer ICI change toute l'application d'un geste —
     etiquettes, chiffres, badges compris. L'alignement des colonnes ne tenait
     deja pas a la chasse fixe mais a `font-variant-numeric: tabular-nums`, pose
     partout ou il compte. Pour du VRAI monospace (cle, code, chemin), il reste
     `--font-code`. */
  --font-mono:           'Geist', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-code:           'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  --text-display-1:      48px;
  --text-display-2:      36px;
  --text-display-3:      24px;
  --text-h4:             18px;
  --text-body:           14px;
  --text-body-sm:        13px;
  --text-caption:        12px;
  --text-eyebrow:        10px;
  --text-kpi:            36px;
  --text-data-cell:      13px;

  --leading-tight:       1.0;
  --leading-snug:        1.2;
  --leading-normal:      1.5;
  --leading-relaxed:     1.6;

  --tracking-wordmark:   0.28em;
  --tracking-eyebrow:    0.22em;
  --tracking-mono-data:  0.04em;
  --tracking-tight:      -0.01em;

  /* Le CADRE : la largeur d'une page, et la colonne d'explication d'un encadre
     de reglage. Deux valeurs, jamais reecrites dans un gabarit : voir la section
     « Le CADRE » plus bas pour le pourquoi. */
  --cadre:               1280px;
  --explication:         300px;

  /* L'ECHELLE de toute l'application. Voir la section « L'ECHELLE » plus bas :
     c'est le seul bouton pour grossir ou reduire l'ensemble, et il n'en existait
     aucun avant le 2026-08-18. */
  /* Pose a 1.2 le matin du 2026-08-18 (« il faut qu'on agrandisse la police, on ne
     voit pas grand chose »), ramene a 1 l'apres-midi (« dezoome l'appli »), arrete
     a 1.1 dans la foulee (« agrandis tout de 10 % au lieu de 20 »). Un seul nombre,
     trois essais, aucune ligne de code touchee entre les deux.

     /!\ POURQUOI un zoom et pas une taille de police, malgre la demande litterale
     « uniquement la taille des textes ». Essaye le 2026-08-18 : l'application ecrit
     ses tailles EN DUR, 796 fois dans 63 fichiers, sans variable qui les commande.
     Les grossir demande de reecrire les 796 — ce qui a ete fait, puis defait le
     jour meme. Et cela laisse le texte grossir DANS des boites restees petites :
     les etiquettes en capitales sont a 10 px dans des pastilles de 22.

     Le zoom fait grossir le texte ET ce qui le contient, dans la meme proportion.
     C'est la seule facon, ici, d'agrandir sans rien casser. */
  --echelle:             1.1;
  /* La hauteur reellement disponible, une fois l'echelle appliquee. TOUTE hauteur
     calee sur la fenetre doit passer par elle : `100vh` seul deborde de l'ecran
     du facteur d'echelle. */
  --fenetre:             calc(100vh / var(--echelle));

  /* Spacing (grille 4) */
  --space-1:             4px;
  --space-2:             8px;
  --space-3:             12px;
  --space-4:             16px;
  --space-6:             24px;
  --space-8:             32px;
  --space-12:            48px;
  --space-16:            64px;

  /* Radius (plafond 12) */
  --radius-none:         0;
  --radius-sm:           4px;
  --radius-md:           6px;
  --radius-lg:           10px;
  /* Grande carte posée sur le fond — l'arrondi de la vitrine. Rapatrie le
     `--r-xl` que les écrans d'authentification déclaraient chacun de leur côté.
     /!\ L'échelle existante (4/6/10) n'est PAS modifiée : seuls deux écrans
     employaient cet arrondi, et le changer aurait adouci tous les coins de
     l'application sans que personne l'ait demandé. */
  --radius-xl:           22px;
  --radius-pill:         999px;

  /* Shadows (teintées slate) */
  --shadow-xs:           0 1px 2px rgb(28 35 51 / 0.04);
  --shadow-sm:           0 1px 3px rgb(28 35 51 / 0.06), 0 1px 2px rgb(28 35 51 / 0.04);
  --shadow-md:           0 4px 8px rgb(28 35 51 / 0.08), 0 2px 4px rgb(28 35 51 / 0.04);
  --shadow-lg:           0 8px 16px rgb(28 35 51 / 0.10), 0 4px 6px rgb(28 35 51 / 0.04);
  --shadow-drawer:       -8px 0 24px rgb(28 35 51 / 0.10);

  /* Motion */
  --motion-fast:         100ms;
  --motion-base:         150ms;
  --motion-slow:         200ms;
  --motion-slower:       300ms;
  --ease-out:            cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out:         cubic-bezier(0.4, 0, 0.2, 1);
  --ease-in:             cubic-bezier(0.4, 0, 1, 1);

  /* Z-index */
  --z-dropdown:          100;
  --z-sticky:            200;
  --z-drawer:            300;
  --z-modal:             400;
  --z-toast:             500;
}

/* ============================================================
   UN SEUL THÈME — le sombre
   /!\ La consigne « le projet est light only, ne pas ajouter de
   prefers-color-scheme: dark » qui occupait ce bloc a été RÉVOQUÉE le
   2026-08-16, à la demande du fondateur. Ne pas la rétablir.
   Il n'y a plus de bascule ni de second thème : les valeurs sombres sont
   dans `:root` ci-dessus, point. Un `[data-theme]` serait du décor.
   ============================================================ */

/* ============================================================
   Reset minimal + base body
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* /!\ Ce que le NAVIGATEUR dessine lui-meme ne suit pas nos variables : une
     case a cocher non cochee, le menu deroulant d'un `<select>`, le calendrier
     d'un champ date, les fleches d'un champ nombre s'ouvraient en BLANC au
     milieu d'une page noire. Une seule ligne les aligne tous. Ajoutee le
     2026-08-16, en constatant des cases blanches sur la fiche d'un trader ;
     c'est le complement de la decision « le sombre est le seul theme », pas un
     changement de style — sans elle, la moitie des controles ignore le theme. */
  color-scheme: dark;
}

/* ============================================================
   L'ECHELLE : le seul bouton pour grossir toute l'application (2026-08-18)
   ============================================================
   Demande du fondateur : « il faut qu'on agrandisse la police de l'application,
   on ne voit pas grand chose. »

   /!\ Ce bouton n'existait PAS, et c'est pour ca que rien n'avait jamais bouge.
   L'echelle de tailles declaree dans `:root` (`--text-body: 14px` et les dix
   autres) est DECORATIVE : l'application ecrit ses tailles en dur, environ 900
   fois entre cette feuille et les gabarits (175 fois `13px`, 130 fois `11px`,
   129 fois `12px`, 113 fois `12.5px`). Monter `--text-body` ne deplacerait
   presque rien a l'ecran. Meme piege que `.wrap`, qui n'avait aucune regle.

   /!\ `zoom` et pas une taille de police, et c'est le point : le texte seul
   grossirait DANS des boites restees petites. Les etiquettes en capitales sont a
   10 px dans des pastilles de 22 : elles en deborderaient. Ici les cadres, les
   boutons, les icones et les espaces suivent, dans la meme proportion.

   /!\ TOUTE hauteur calee sur la fenetre doit passer par `--fenetre`. Un
   `100vh` brut est multiplie par l'echelle comme le reste : la barre laterale
   depassait du bas de l'ecran et « Se deconnecter » passait sous la ligne de
   flottaison. Defaut vu sur la maquette a +20 %, avant livraison.

   /!\ Les points de bascule (`@media (max-width: 1023px)`) ne connaissent PAS
   cette echelle : ils se declenchent toujours a la largeur reelle. Entre 1024 et
   ~1230 px de large, la mise en page de bureau est donc un peu a l'etroit avant
   de basculer. Assume : les clients travaillent sur le poste qui fait tourner
   NinjaTrader, en grand. Si le cas devient reel, multiplier les bornes par
   l'echelle est le geste, pas un rustine locale. */
body { zoom: var(--echelle); }

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  color: var(--foreground);
  background: var(--background);
  font-feature-settings: "cv11", "ss01";
}

img, svg, video { display: block; max-width: 100%; }

/* /!\ `hidden` doit gagner, toujours. La regle du navigateur est
   `[hidden] { display: none }` a specificite ZERO : la moindre classe qui pose
   un `display` (`.champ` en flex, `.champs` en grille) la bat, et l'element
   marque cache reste A L'ECRAN. Le defaut se voit rarement en developpement —
   il apparait sur le premier bloc replie. */
[hidden] { display: none !important; }

/* ============================================================
   Le CADRE : la largeur d'une page, et son centrage (2026-08-18)
   ============================================================
   Le conteneur de TOUTE page de l'espace authentifie. Il portait jusqu'ici
   une largeur ecrite en dur dans chaque gabarit (`style="max-width:900px"`,
   17 fois, sept valeurs differentes) et AUCUNE regle : `.wrap` n'existait
   nulle part dans cette feuille.

   /!\ C'est le defaut signale par le fondateur le 2026-08-18 : un bloc plus
   etroit que la place disponible se colle A GAUCHE. Tant que la fenetre est
   petite, la colonne remplit l'ecran et personne ne le voit ; en plein ecran,
   tout se tasse a gauche et il reste 400 px de vide a droite. Ce n'etait donc
   pas un defaut des ecrans refaits : c'etait le socle, sur les 50 gabarits.

   /!\ Le centrage seul aurait cree un second defaut : chaque page ayant sa
   propre largeur, le titre et le fil d'Ariane se seraient DEPLACES a chaque
   changement de page. D'ou une seule et meme largeur partout. Ce qui doit
   rester etroit ne retrecit pas le cadre : c'est l'interieur d'un encadre de
   reglage qui se reorganise, voir la regle `--reglages` juste en dessous.

   Pourquoi 1280 : c'est la place disponible a droite du menu sur un ecran de
   1536 points (le poste du fondateur), au point pres. La page y remplit donc
   l'ecran d'un bord a l'autre ; au-dela elle s'arrete et se centre, pour
   qu'une ligne reste lisible d'un seul regard.

   /!\ `.wrap` est le nom historique, porte par les 50 gabarits ; `.cadre` est
   le nom du vocabulaire courant. Les deux designent la meme chose et vivent
   sur la meme regle : un gabarit refait ecrit `.cadre`, les autres gardent
   `.wrap` jusqu'a leur tour. Ne pas renommer les 50 d'un coup : c'est la
   migration ecran par ecran tranchee le 2026-08-16. */
.cadre, .wrap {
  width: 100%;
  max-width: var(--cadre);
  margin-inline: auto;
}

/* Les pages de REGLAGE : le titre de chaque encadre et la phrase qui l'explique
   passent A GAUCHE, sur une colonne etroite ; les champs et les boutons prennent
   toute la droite.

   /!\ Ceci REMPLACE une premiere reponse, du meme jour, qui bornait tout le
   contenu d'une page de reglage a une colonne de 820. Elle reglait le vide de la
   page mais en laissait un autre a droite des champs, que le fondateur a
   immediatement signale : « pareil pour les pages de detail ».

   /!\ La solution evidente (laisser le formulaire s'etirer sur le cadre) a ete
   ESSAYEE et jetee : `.champs` est en `auto-fit`, donc deux champs dans une boite
   de 1280 deviennent deux champs de 590 points pour saisir un nom. Plus large et
   moins bon. C'est la colonne d'explication qui absorbe la largeur en trop, pas
   les saisies.

   /!\ Portee `--reglages` et pas `.encadre` tout court : l'encadre sert aussi aux
   pages de consultation (« Mes trades », le detail d'une analyse), ou il n'y a
   ni champ ni explication a mettre en regard.

   /!\ Flottant, PAS une grille a deux colonnes, et c'est mesure. En grille, le
   titre occupe la ligne 1 et l'explication la ligne 2 ; une ligne 1 haute a
   droite (un bandeau d'alerte, une paire de saisies) etire la ligne et
   l'explication DECROCHE de son titre, jusqu'a 110 points de vide entre les deux.
   Un flottant ignore les lignes : l'explication reste collee sous son titre quoi
   qu'il arrive a droite.
   /!\ `display: flow-root` est ce qui contient les flottants sans `overflow`
   (qui, lui, couperait les bulles d'aide au survol). */
.cadre--reglages .encadre,
.wrap--reglages  .encadre { display: flow-root; }
.cadre--reglages .encadre > .encadre-titre,
.wrap--reglages  .encadre > .encadre-titre,
.cadre--reglages .encadre > .encadre-sous,
.wrap--reglages  .encadre > .encadre-sous {
  float: left; clear: left;
  width: var(--explication);
  margin-bottom: 0;
}
.cadre--reglages .encadre > *:not(.encadre-titre):not(.encadre-sous),
.wrap--reglages  .encadre > *:not(.encadre-titre):not(.encadre-sous) {
  margin-left: calc(var(--explication) + 56px);
}
/* L'air que `.encadre-titre + .champs` posait sous le titre n'a plus lieu d'etre :
   les champs ne sont plus SOUS le titre, ils sont A COTE. */
.cadre--reglages .encadre > .encadre-titre + .champs,
.wrap--reglages  .encadre > .encadre-titre + .champs,
.cadre--reglages .encadre > .encadre-titre + .champ,
.wrap--reglages  .encadre > .encadre-titre + .champ { margin-top: 0; }

/* Sous 1024 px, la barre laterale disparait et l'ecran n'a plus de largeur a
   donner : les deux colonnes se remettent l'une sous l'autre. Meme borne que le
   reste de l'application, une seule requete media. */
@media (max-width: 1023px) {
  .cadre--reglages .encadre > .encadre-titre,
  .wrap--reglages  .encadre > .encadre-titre,
  .cadre--reglages .encadre > .encadre-sous,
  .wrap--reglages  .encadre > .encadre-sous { float: none; width: auto; }
  .cadre--reglages .encadre > .encadre-sous,
  .wrap--reglages  .encadre > .encadre-sous { margin-bottom: 20px; }
  .cadre--reglages .encadre > *:not(.encadre-titre):not(.encadre-sous),
  .wrap--reglages  .encadre > *:not(.encadre-titre):not(.encadre-sous) { margin-left: 0; }
}

/* L'espacement entre deux encadres qui s'empilent. Il vivait dans le `<style>` de
   la fiche d'un trader, seul gabarit a en empiler ; il est monte ici le
   2026-08-18, quand cinq pages de reglage de plus sont passees a ce vocabulaire.
   Meme valeur, pour ne rien deplacer sur l'ecran qui l'avait. */
.encadre + .encadre { margin-top: 16px; }

/* ============================================================
   Typographie — classes utilitaires
   ============================================================ */
.t-display-1 { font-size: var(--text-display-1); font-weight: 300; line-height: var(--leading-tight); letter-spacing: var(--tracking-tight); color: var(--primary); }
.t-display-2 { font-size: var(--text-display-2); font-weight: 300; line-height: var(--leading-tight); letter-spacing: var(--tracking-tight); color: var(--foreground); }
.t-display-3 { font-size: var(--text-display-3); font-weight: 400; line-height: var(--leading-snug); color: var(--foreground); }
.t-h4 { font-size: var(--text-h4); font-weight: 500; line-height: var(--leading-snug); color: var(--foreground); }
.t-body { font-size: var(--text-body); line-height: var(--leading-normal); }
.t-body-sm { font-size: var(--text-body-sm); line-height: var(--leading-normal); color: var(--muted-foreground); }
.t-caption { font-size: var(--text-caption); line-height: var(--leading-normal); color: var(--muted-foreground); }
.t-eyebrow { font-family: var(--font-mono); font-size: var(--text-eyebrow); font-weight: 500; letter-spacing: var(--tracking-eyebrow); text-transform: uppercase; color: var(--muted-foreground); }
.t-kpi { font-family: var(--font-sans); font-size: var(--text-kpi); font-weight: 500; line-height: var(--leading-tight); letter-spacing: var(--tracking-tight); color: var(--primary); font-variant-numeric: tabular-nums; }
.t-mono { font-family: var(--font-mono); font-size: var(--text-data-cell); letter-spacing: var(--tracking-mono-data); font-variant-numeric: tabular-nums; }
.t-wordmark { font-family: var(--font-sans); font-weight: 400; letter-spacing: var(--tracking-wordmark); text-transform: uppercase; color: var(--primary); }
.num { font-variant-numeric: tabular-nums; text-align: right; }

/* ============================================================
   Focus visible — règle universelle
   ============================================================ */
*:focus { outline: none; }
*:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: inherit;
}

/* ============================================================
   prefers-reduced-motion
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ============================================================
   Sélection texte + scrollbar
   ============================================================ */
::selection { background: rgb(74 59 122 / 0.20); color: var(--foreground); }

::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgb(91 101 119 / 0.3); border-radius: var(--radius-pill); }
::-webkit-scrollbar-thumb:hover { background: rgb(91 101 119 / 0.5); }

/* ============================================================
   Composants — primitives
   ============================================================ */

/* ── LES BOUTONS — direction « console » (2026-08-23) ─────────────────────────
   Choisie par le fondateur sur maquette, contre la pilule brillante d'avant. Ce
   qui change, et pourquoi :

   /!\ LE COIN N'EST PLUS UNE PILULE. Elle venait de la page de vente et n'avait
   aucun cousin dans l'application : cartes, tiroirs et tableaux sont arrondis a
   4, 6 ou 10 px. Le bouton rejoint `--radius-md` (6 px), de loin le coin le plus
   employe de la charte — 33 declarations, contre 13 pour 8 px et 12 pour 10.

   /!\ Reste UN ecart connu : `.saisie` est a 8 px en dur. Un bouton pose a cote
   d'un champ montre donc encore 2 px de difference. A aligner, mais pas en
   passant : ce sont tous les formulaires de l'application.

   /!\ PLUS RIEN NE SAUTE AU SURVOL. `translateY(-1px)` s'appliquait a TOUS les
   boutons, fantomes et icones compris : quand tout bouge, le mouvement ne
   signale plus rien. Il ne reste que l'enfonce au clic, qui, lui, repond a un
   geste. Le survol se dit par la surface.

   /!\ Une PASTILLE reste une pilule (`.filter-chip`, `.seg`, les badges). Ce
   n'est pas une incoherence : une pastille se lit comme une etiquette, un
   bouton comme une touche. */
/* ── LA TAILLE, revue le 2026-08-23 ──────────────────────────────────────────
   Demande du fondateur, sur maquette : « tous les boutons dans la dimension du
   bouton Inviter un proche ». C'est 30 px de haut, 12 px de texte, 6 px de coin.

   /!\ CE N'ETAIT PAS QU'UN CHOIX DE GOUT : la coquille de l'application
   (`admin_base.html`) redeclarait `.btn { height: 38px }`, ce qui ecrasait la
   charte ET rendait `.btn-sm` INOPERANT dans tout l'espace authentifie. Un
   ecran qui voulait un petit bouton n'en obtenait pas, et personne ne savait
   pourquoi. La declaration de la coquille a ete SUPPRIMEE : la taille se regle
   ici, en un seul endroit. Ne pas la remettre.

   /!\ Les trois ecrans d'authentification (connexion, mot de passe oublie,
   reinitialisation) gardent leur propre `.btn`, plus grand : ce sont des pages
   autonomes ou le bouton fait toute la largeur d'un formulaire centre. Les
   reduire a 30 px les rendrait maigres. C'est une exception ASSUMEE, et la
   seule. */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 6px;
  height: 30px; padding: 0 14px; box-sizing: border-box;
  font-family: var(--font-sans); font-size: 12px; font-weight: 500; line-height: 1;
  border: 1px solid transparent; border-radius: var(--radius-md);
  cursor: pointer; text-decoration: none;
  transition: background var(--motion-base) var(--ease-out),
              border-color var(--motion-base) var(--ease-out),
              color var(--motion-base) var(--ease-out),
              transform var(--motion-fast) var(--ease-out),
              box-shadow var(--motion-base) var(--ease-out);
  white-space: nowrap;
}
.btn:disabled { opacity: 0.4; cursor: not-allowed; }
.btn:active:not(:disabled) { transform: translateY(1px); }

/* L'etiquette d'un champ : petite, en chasse fixe, en capitales espacees.
   C'est la seule rescapee de la grille `.spec-grid` posee ici le matin du
   2026-08-16 : « Mes traders » l'a remplacee le soir meme par deux groupes
   (par trade / par jour), et plus aucun ecran ne s'en servait. Une primitive
   sans usage est du code mort, meme jeune — elle reviendra le jour ou un ecran
   en aura vraiment besoin. L'etiquette, elle, sert toujours. */
.spec-label {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--muted-foreground);
  margin-bottom: 4px;
}

/* LE GESTE PRINCIPAL — un aplat violet, sans degrade ni halo (2026-08-23).
   /!\ Le fond était `#4A3B7A` CODÉ EN DUR — l'accent de l'ancien thème clair —
   tandis que la couleur du texte, elle, suivait `var(--on-accent)`. Au passage
   au sombre, le texte est devenu foncé et s'est retrouvé illisible sur ce
   violet foncé figé. C'est LE défaut type que le mode sombre révèle : une
   moitié du composant suit le vocabulaire, l'autre non — et rien ne le signale
   tant qu'un seul thème existe. Le texte reste donc sur `--on-accent`.

   /!\ LE HALO EST PARTI. `0 8px 24px` de violet sous chaque bouton principal :
   sur un ecran qui n'en porte qu'un, il flotte ; sur un tiroir qui en aligne
   trois, l'ecran devient brumeux. Le bouton se voit maintenant parce qu'il est
   le seul objet plein et colore de la page, pas parce qu'il brille. Rien
   d'autre dans l'application n'a besoin d'une lueur pour se faire remarquer.

   /!\ LE DEGRADE AUSSI. Il obligeait a rattraper l'enfonce par une seconde
   declaration de degrade — le raccourci `background` effacait le premier, et le
   texte sombre se retrouvait sur un violet sombre pendant le clic (defaut vecu
   le 2026-08-16). Un aplat n'a pas ce piege : les trois etats sont trois
   valeurs de la meme rampe, `--accent-hi` > `--accent` > `--primary`. */
/* LA VOIX FORTE — un aplat violet, et le SEUL de l'écran.
   /!\ RÉSERVÉE AUX GESTES QUI ENGAGENT DE L'ARGENT : souscrire, passer à N
   traders facturés, mettre à jour la carte d'un impayé. Nulle part ailleurs.
   C'est le § B de la maquette du 2026-08-23, choisie par le fondateur : tout
   le reste des boutons s'est calmé, et si la page de vente se calmait aussi,
   l'oeil n'aurait plus aucun point d'ancrage là où ça compte.
   /!\ Deux `.btn-fort` sur un même écran, c'est un de trop : la règle ne tient
   que tant qu'il n'y en a qu'un. */
.btn-fort {
  background: var(--accent);
  color: var(--on-accent);
  border: 1px solid var(--accent);
  font-weight: 600;
}
.btn-fort:hover:not(:disabled) { background: var(--accent-hi); border-color: var(--accent-hi); }
.btn-fort:active:not(:disabled) { background: var(--primary); border-color: var(--primary); }

/* LE GESTE PRINCIPAL D'UN ÉCRAN — l'habit du bouton « Inviter un proche ».
   Le fondateur l'a désigné comme la référence : violet lavé, liseré violet,
   libellé clair. Il se distingue d'un geste neutre sans peser comme un aplat. */
.btn-primary {
  background: rgba(167,139,250,0.18);
  color: #C9BCFF;
  border: 1px solid rgba(167,139,250,0.30);
}
.btn-primary:hover:not(:disabled) {
  background: rgba(167,139,250,0.28);
  border-color: rgba(167,139,250,0.50);
  color: #FFFFFF;
}
.btn-primary:active:not(:disabled) { background: rgba(167,139,250,0.34); }

/* LE GESTE D'À CÔTÉ — une surface, un liseré, aucune teinte. */
.btn-secondary { background: var(--card); color: var(--foreground); border: 1px solid rgba(255,255,255,0.14); }
.btn-secondary:hover:not(:disabled) { background: var(--muted); border-color: rgba(255,255,255,0.24); }

/* LE GESTE SECONDAIRE.
   /!\ Le liseré passe de `--border` (7 % de blanc) à `--border-strong` (12 %) :
   à 7 % le bouton se devinait plus qu'il ne se voyait, et il n'existait aucun
   niveau entre lui et le bouton plein. C'est le manque que la maquette du
   2026-08-23 nommait en quatrieme point.

   /!\ Il ne VIRE PLUS A L'ACCENT au survol. Un fantome qui devient violet se
   lit comme un second bouton principal ; deux gestes de meme couleur cote a
   cote annulent la hierarchie qu'on vient de poser. Seule la surface change. */
.btn-ghost {
  background: transparent;
  color: var(--foreground);
  border: 1px solid rgba(255,255,255,0.14);
}
.btn-ghost:hover:not(:disabled) {
  background: rgba(255,255,255,0.06);
  border-color: rgba(255,255,255,0.24);
}

/* LE GESTE DESTRUCTEUR — un contour teinté, jamais un aplat rouge.
   /!\ CE N'EST PAS UN CHOIX DE GOUT. Du blanc sur `--error` (#F87171) donne un
   contraste de 2,8 pour 1, la ou la norme en demande 4,5 : le libelle du seul
   bouton qu'on ne veut PAS cliquer par erreur etait le moins lisible de
   l'application. Le rouge passe donc au texte, sur un fond transparent.

   /!\ L'affirmative d'une confirmation reste `.btn-primary` (cf.
   `confirmation.js`) : ce bouton-ci n'est jamais le dernier rempart, il ouvre
   la fenetre qui l'est. Il n'a donc pas besoin du poids d'un aplat. */
.btn-danger {
  background: transparent;
  color: var(--error);
  border-color: rgba(248,113,113,0.35);
}
.btn-danger:hover:not(:disabled) {
  background: rgba(248,113,113,0.10);
  border-color: rgba(248,113,113,0.60);
}

/* Variantes "soft" tintées — actions secondaires (drawers : Supprimer, Archiver,
   Annuler, Réactiver...). À utiliser avec .btn pour le gabarit. Radius 8px. */
/* /!\ Le `border-radius: 8px` codé en dur a sauté le 2026-08-23 : ces deux
   variantes se posaient a cote d'un `.btn` en pilule et devaient donc corriger
   sa forme. Le `.btn` etant passe a `--radius-md`, elles n'ont plus rien a
   redresser — et un coin de 8 px a cote d'un coin de 6 se voit. */
.btn-danger-ghost {
  background: rgba(159,63,78,.07); color: var(--error);
  border: 1px solid rgba(159,63,78,.28);
}
.btn-danger-ghost:hover:not(:disabled) {
  background: rgba(159,63,78,.13); border-color: rgba(159,63,78,.48);
}
.btn-success-ghost {
  background: rgba(63,110,92,.10); color: var(--success);
  border: 1px solid rgba(63,110,92,.30);
}
.btn-success-ghost:hover:not(:disabled) {
  background: rgba(63,110,92,.16); border-color: rgba(63,110,92,.50);
}

/* /!\ `display: inline-flex` est INDISPENSABLE ici, pas decoratif. La regle
   demandait un centrage (`justify-content`) sans jamais poser de contexte flex :
   elle ne marchait que sur les boutons qui portaient AUSSI `.btn` (lui en flex).
   Un `.btn-icon` employe seul — l'oeil de « Mon profil », les icones de cle de
   « Mes traders » — collait son icone contre le bord gauche, a 1 px au lieu de
   11,5 dans un cadre de 38. Mesure du 2026-08-16. */
/* /!\ Le rond est devenu un carre adouci le 2026-08-23, avec le reste des
   boutons : une icone en pastille ronde posee a cote d'un bouton a coins droits
   se lit comme un autre composant. Et l'icone au repos est ATTENUEE
   (`--muted-foreground`) : elle ne reprend la couleur du texte qu'au survol,
   pour que trois icones de barre d'outils ne pesent pas autant que le libelle
   qu'elles accompagnent. */
.btn-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; padding: 0;
  background: transparent; color: var(--muted-foreground);
  border-radius: var(--radius-md);
  border: 1px solid transparent;
}
.btn-icon:hover:not(:disabled) { background: var(--muted); color: var(--foreground); }

/* Compact variant used in tight UI (drawer headers, table actions) */
/* La taille réduite — pour une action posée DANS une ligne ou une carte, à
   côté d'un texte de 12 px. Elle était inopérante depuis que la coquille
   imposait 38 px à tout ; elle redevient une vraie taille. */
.btn-sm { height: 26px; padding: 0 10px; font-size: 11.5px; gap: 5px; }

/* ── Une ACTION DE LISTE : du texte, pas une pilule (2026-08-17) ──
   Sur une carte de resume ou une rangee, trois `.btn-ghost` cote a cote pesent plus lourd
   que le contenu qu'ils accompagnent : trois cadres arrondis, trois bordures, et l'oeil ne
   sait plus ou regarder. Le fondateur, en designant ceux de « Mes traders » : « je n'aime
   pas du tout ».

   /!\ Ne PAS combiner avec `.btn` : celui-ci porte une bordure, une pilule et un
   soulevement au survol, exactement ce qu'on retire ici. `.action` se suffit.

   /!\ La cible tactile reste correcte : le padding vertical donne 30 px de hauteur, et le
   trait de survol n'est pas la seule marque du survol (la couleur change aussi), pour qui
   ne distingue pas les nuances.

   /!\ Une action DESTRUCTRICE prend `.action--rouge`, jamais un `style` en ligne : la
   couleur d'un danger appartient a la charte, pas a l'ecran. */
.action {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 0;
  background: transparent; border: 0;
  font-family: var(--font-sans); font-size: 12.5px; font-weight: 500; line-height: 1.2;
  color: var(--muted-foreground);
  text-decoration: none; cursor: pointer;
  border-bottom: 1px solid transparent;
  transition: color var(--motion-base) var(--ease-out),
              border-color var(--motion-base) var(--ease-out);
}
.action:hover, .action:focus-visible { color: var(--foreground); border-bottom-color: var(--accent); }
.action svg { width: 13px; height: 13px; opacity: 0.75; }
.action:hover svg { opacity: 1; }
/* L'action qui COMPTE sur la rangee : elle garde la couleur du texte au repos, les autres
   sont atténuées. Une hiérarchie sans une seule bordure. */
.action--forte { color: var(--foreground); }
.action--forte:hover, .action--forte:focus-visible { color: var(--accent); border-bottom-color: var(--accent); }
.action--rouge { color: var(--error); }
.action--rouge:hover, .action--rouge:focus-visible { color: var(--error); border-bottom-color: var(--error); }

@media (max-width: 768px) {
  .btn-icon { width: 44px; height: 44px; }
}

/* Selecteur segmente — un choix EXCLUSIF parmi peu d'options, toutes visibles.
   Ne pas confondre avec `.filter-chip`, qui retire un filtre deja pose : ici on
   bascule entre des vues, il y en a toujours exactement une d'active.

   /!\ Chaque segment porte son EFFECTIF (« Actifs 2 ») : sans lui on choisit a
   l'aveugle, et un segment vide se decouvre en cliquant dessus. Meme regle que
   les onglets de mode de « Mes trades ».

   /!\ Ne l'afficher que lorsqu'il y a quelque chose a departager. Un selecteur
   pose au-dessus d'un seul element est un piege deja rencontre le 2026-08-15
   (le selecteur de version, inatteignable a un seul trader). */
.seg {
  display: inline-flex; align-items: center; gap: 2px;
  padding: 3px;
  background: var(--muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
}
.seg > * {
  appearance: none; background: none; border: 0; cursor: pointer;
  padding: 6px 14px;
  font-family: inherit; font-size: 13px; font-weight: 500;
  color: var(--muted-foreground);
  border-radius: var(--radius-pill);
  text-decoration: none; white-space: nowrap;
  transition: background var(--motion-fast) var(--ease-out),
              color var(--motion-fast) var(--ease-out);
}
.seg > *:hover { color: var(--foreground); }
/* Deux facons de dire « celui-la ». `aria-selected` quand le segment change une VUE
   sans quitter la page (un filtre, un mode d'affichage) ; `aria-current="page"`
   quand ce sont des LIENS vers d'autres pages, ou l'un est la page ouverte. Le
   second n'existait pas, et un selecteur de pages restait donc sans surbrillance :
   il paraissait casse. */
.seg > [aria-selected="true"],
.seg > [aria-current="page"] {
  background: var(--card);
  color: var(--foreground);
  box-shadow: 0 1px 2px rgba(0,0,0,0.35);
}
/* Un segment PRESENT mais indisponible ICI (2026-08-21). Il garde sa place et son
   libelle, et dit « ca existe, pas a cet endroit ».

   /!\ Grise plutot que RETIRE, et c'est la lecon du cul-de-sac du 2026-08-17 : un
   controle qui disparait avec ce qu'il commandait laisse sans moyen de revenir, sinon
   en editant l'adresse a la main. Retirer le segment ferait en outre sauter la largeur
   du selecteur d'un cran a l'autre.

   /!\ Un segment grise sans motif se lit comme une panne : l'ecran qui l'emploie DOIT
   dire pourquoi, a cote. Le CSS ne peut pas porter cette phrase, mais il peut le
   rappeler ici. */
.seg > [aria-disabled="true"] {
  cursor: default; color: var(--disabled-foreground);
}
.seg > [aria-disabled="true"]:hover { color: var(--disabled-foreground); }

.seg .n { color: var(--muted-foreground); font-weight: 400; margin-left: 6px; }
.seg > [aria-selected="true"] .n,
.seg > [aria-current="page"] .n { color: var(--accent); }


/* ============================================================
   Les JETONS : des filtres qu'on allume et qu'on eteint (2026-08-18)
   ============================================================
   Une rangee de petits filtres independants, chacun a deux etats. Promu depuis
   la page News, ou les huit devises etaient une TROISIEME famille de pastilles
   posee a cote de deux autres, aucune des trois n'etant celle du reste de
   l'application.

   Ce que `.seg` ne sait pas faire, et qui justifie une brique de plus :
   un segment retient TOUJOURS un choix (il faut donc y ajouter un « Tous »
   artificiel pour pouvoir ne rien filtrer), et il en retient UN SEUL. Un jeton
   s'eteint tout seul, et rien n'empeche d'en allumer plusieurs si l'ecran qui
   s'en sert le permet.

   /!\ L'etat passe par `aria-pressed`, jamais par une classe. C'est l'attribut
   qu'un bouton a deux etats doit porter, il est annonce au lecteur d'ecran, et
   il evite le piege vecu ici : le JS posait une classe `.on` que le vocabulaire
   commun ne connait pas, donc les filtres restaient eteints a l'oeil.

   /!\ Un jeton est un FILTRE, pas une etiquette. Ce qui ne se clique pas est un
   `.badge-*`. */
.jetons { display: inline-flex; flex-wrap: wrap; gap: 6px; }
.jeton {
  appearance: none; cursor: pointer;
  padding: 4px 11px;
  font-family: var(--font-mono); font-size: var(--text-caption); font-weight: 500;
  color: var(--muted-foreground);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  white-space: nowrap;
  transition: color var(--motion-fast) var(--ease-out),
              border-color var(--motion-fast) var(--ease-out),
              background var(--motion-fast) var(--ease-out);
}
.jeton:hover { color: var(--foreground); border-color: var(--muted-foreground); }
.jeton[aria-pressed="true"] {
  color: var(--accent);
  border-color: var(--accent);
  background: rgb(74 59 122 / 0.14);
}
.jeton:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }


/* ============================================================
   Les FILTRES : une barre de selecteurs etiquetes (2026-08-17)
   ============================================================
   Plusieurs filtres cote a cote, chacun avec son etiquette : « Periode [ce mois-ci] »,
   « Mode (segments) ». La barre passe a la ligne toute seule.

   /!\ Chaque filtre porte son ETIQUETTE. Une rangee de selecteurs nus oblige a
   deviner ce que chacun filtre, et un filtre qu'on ne comprend pas ne se touche pas.

   /!\ Un filtre a une seule valeur possible ne s'affiche pas. C'est le piege du
   2026-08-15 : un selecteur de version pose au-dessus d'une seule version etait
   inatteignable, donc l'ecran paraissait casse. La regle vaut ici aussi.

   /!\ La barre de filtres n'est PAS un formulaire de saisie : ses controles sont
   compacts (`.liste--compact`). A la taille d'un champ de reglage, cinq filtres
   coutaient plus de hauteur que la bande de chiffres qu'ils gouvernent. */
.filtres { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 18px; }
.filtre { display: flex; align-items: center; gap: 8px; min-width: 0; }
.filtre-l {
  font-family: var(--font-mono); font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  color: var(--muted-foreground); white-space: nowrap;
}
/* La liste deroulante en version barre de filtres : meme dessin, moitie moins haute.

   /!\ DEUX selecteurs, et il faut les deux. `liste.js` remplace l'affichage du
   `<select>` par un `.liste-bouton` et masque le natif a 1x1 px (il reste dans le
   formulaire, il porte la valeur). Ne styler que `select.saisie` habillait donc un
   element invisible : mesure au banc, le filtre rendait un controle de 1 px de haut
   sur 1 px de large. Le natif garde quand meme sa regle, c'est lui qui s'affiche si le
   script ne se charge pas. */
.liste--compact { display: inline-block; }
.liste--compact > select.saisie,
.liste--compact > .liste-bouton {
  width: auto; padding: 6px 13px 6px 11px;
  font-size: 13px; border-radius: var(--radius-pill);
}
.liste--compact > select.saisie { padding-right: 32px; }
.liste--compact > .liste-bouton { gap: 8px; }
.liste--compact::after { right: 13px; width: 6px; height: 6px; margin-top: -4px; }
/* Le panneau deplie ne peut pas suivre la largeur d'un bouton compact : « La semaine
   derniere » y serait coupe. Il repart du bord gauche et prend la place qu'il lui faut. */
.liste--compact > .liste-panneau { right: auto; min-width: 100%; width: max-content; }

/* ============================================================
   Verre — la surface de la vitrine, portee dans l'application (2026-08-16)
   ============================================================
   Ce qui fait le verre : un degrade diagonal tres clair par-dessus un fond
   sombre translucide, un REFLET d'un pixel en haut de la surface, et un LISERE
   en degrade qui s'allume dans l'angle superieur gauche et s'eteint en bas.

   /!\ SANS `backdrop-filter`, et c'est un choix mesure, pas un oubli. La vitrine
   elle-meme avertit qu'il « recompose sa zone a CHAQUE frame de defilement » et
   n'en pose que deux dans toute la page. Ici le fond de l'application est un
   APLAT (`--background`) : il n'y a rien derriere a flouter, le filtre ne
   changerait donc RIEN a l'image tout en coutant une recomposition par carte, sur
   une liste qui peut en compter dix. Le jour ou un fond texture apparait, c'est
   ici qu'on ajoute `backdrop-filter: blur(32px) saturate(165%)`.

   /!\ La bordure est TRANSPARENTE et le liseré est peint par `::before` : un
   `border-color` classique donnerait un trait d'epaisseur uniforme, alors que
   tout l'effet tient dans son degrade. Ne pas rajouter de `border` par-dessus. */
.verre {
  position: relative;
  background:
    linear-gradient(160deg, rgba(255,255,255,0.07) 0%,
                            rgba(255,255,255,0.02) 42%,
                            rgba(255,255,255,0.035) 100%),
    rgba(18, 20, 29, 0.55);
  border: 1px solid transparent;
  border-radius: var(--radius-xl);
  box-shadow:
    0 34px 90px -24px rgba(0,0,0,0.68),
    inset 0 1px 0 0 rgba(255,255,255,0.14);
}
.verre::before {
  content: "";
  position: absolute; inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(140deg, rgba(255,255,255,0.28) 0%,
                                      rgba(180,168,255,0.16) 38%,
                                      rgba(255,255,255,0.04) 70%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}

/* Une zone DANS une surface de verre. Pas de bordure : un cadre dans un cadre
   fabrique l'empilement de boites que la refonte cherche a supprimer. Elle se
   distingue par un voile et un filet en haut, rien de plus. */
.verre-zone {
  position: relative;
  background: rgba(255,255,255,0.028);
  border: 0;
  border-radius: var(--radius-md);
}

/* ============================================================
   Survol — les deux idiomes de la vitrine, repris tels quels (2026-08-16)
   ============================================================
   La vitrine en a DEUX, et ils ne disent pas la meme chose :

   `--survol`      la CARTE se souleve, prend un liseré violet et une ombre
                   portee. Elle se detache du fond : « cet ensemble est un objet ».
   `--survol-bloc` le BLOC INTERNE se teinte, sans bouger. Il reste dans la carte :
                   « cette zone est vivante », sans concurrencer la carte.

   /!\ OPT-IN, jamais sur `.panel` tout court. Une liste d'administration ou un
   tableau dense ou chaque ligne se souleverait au passage de la souris devient
   illisible. Le mouvement se merite : on le pose sur ce qu'on manipule.

   /!\ Le bloc `prefers-reduced-motion` plus haut ramene deja les durees a 0,01 ms
   pour qui demande moins d'animation — le deplacement reste, mais instantane. */
.panel--survol {
  transition: transform 320ms var(--ease-out),
              border-color 320ms var(--ease-out),
              box-shadow 320ms var(--ease-out);
}
.panel--survol:hover {
  transform: translateY(-4px);
  border-color: var(--border-strong);
  box-shadow: 0 24px 60px -20px rgba(0,0,0,0.6),
              0 0 0 1px var(--accent-ring);
}
/* Sur une surface de verre, le liseré est peint par `::before` : ecraser
   `border-color` n'aurait aucun effet, et l'ombre doit garder le reflet interne
   sans quoi il disparait au survol. */
.verre.panel--survol:hover {
  box-shadow: 0 40px 100px -24px rgba(0,0,0,0.72),
              0 0 0 1px var(--accent-ring),
              inset 0 1px 0 0 rgba(255,255,255,0.20);
}
.verre.panel--survol:hover::before {
  background: linear-gradient(140deg, rgba(255,255,255,0.38) 0%,
                                      rgba(180,168,255,0.30) 38%,
                                      rgba(255,255,255,0.06) 70%);
}
.bloc--survol {
  transition: border-color 240ms var(--ease-out),
              background 240ms var(--ease-out);
}
.bloc--survol:hover {
  border-color: var(--accent-ring-strong);
  background: var(--accent-wash);
}

/* Une valeur qu'on recopie ailleurs — une cle, un identifiant, une commande.
   Le bouton REMPLACE la selection a la souris : sur une chaine de 40 caracteres en
   chasse fixe, un double-clic n'attrape qu'un morceau et l'utilisateur ne le voit
   pas. Le retour ecrit (« Copie ») est obligatoire : sans lui, rien ne distingue un
   clic reussi d'un clic rate, et l'utilisateur recolle l'ancienne valeur.

   /!\ `navigator.clipboard` n'existe QUE sur une origine sure (https ou localhost).
   En clair sur une IP, il est `undefined` : le script le teste et laisse alors le
   texte selectionnable plutot que d'echouer en silence. */
.copiable {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 12px;
  background: var(--muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.copiable code {
  flex: 1 1 auto; min-width: 0;
  font-family: var(--font-mono); font-size: var(--text-body-sm);
  color: var(--foreground);
  overflow-wrap: anywhere;
}
.copiable .copier {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 6px;
  appearance: none; cursor: pointer;
  padding: 6px 12px;
  font-family: inherit; font-size: 12.5px; font-weight: 500;
  color: var(--foreground);
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  transition: border-color var(--motion-fast) var(--ease-out),
              color var(--motion-fast) var(--ease-out);
}
.copiable .copier:hover { border-color: var(--accent); color: var(--accent); }
.copiable .copier[data-fait="oui"] { color: var(--success); border-color: var(--success); }

/* ============================================================
   L'ENCADRE et le CHAMP : le modele du formulaire de connexion (2026-08-16)
   ============================================================
   L'ecran de connexion etait le seul endroit ou une boite et un champ avaient
   ete dessines ; partout ailleurs regnait `.panel` > `.drawer-section`, herite
   du theme clair (bordures et ombres bleu-gris sur une page devenue noire) et
   qui empile DEUX cadres pour une seule idee. Ces deux primitives sont sa
   promotion : une boite, un champ, definis une fois.

   /!\ Ce qu'on promeut est le MODELE, pas le fichier. `login.html` garde sa
   carte a lui (`.login-card`) : elle est plein ecran, seule, avec sa lueur
   haute. `.encadre` est la meme surface reglee pour une SUITE de boites qui
   s'empilent dans une page.

   /!\ Migration ECRAN PAR ECRAN, decision du fondateur du 2026-08-16. Les
   ~470 champs `.fi`/`.fl` du reste de l'application ne bougent pas : ils
   changeront quand leur ecran sera refait et regarde. Les deux vocabulaires
   cohabitent donc pour un temps, c'est voulu et pas un oubli. */
.encadre, .carte {
  position: relative;
  padding: 26px 28px;
  background:
    radial-gradient(600px 300px at 50% -10%, rgba(139,127,224,0.15), transparent 60%),
    linear-gradient(180deg, rgba(26,29,39,0.92) 0%, rgba(20,22,31,0.92) 100%);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-xl);
  box-shadow:
    0 30px 80px -20px rgba(0,0,0,0.6),
    0 0 0 1px rgba(255,255,255,0.04) inset;
}

/* ============================================================
   La CARTE : meme surface que l'encadre, mais VIVANTE (2026-08-17)
   ============================================================
   Demande du fondateur : les cartes de « Mes traders » prennent le style des
   encadres, « mais par contre pour les cards elle doit etre plus dynamique,
   c'est-a-dire qu'au survol il y a une petite animation avec une surbrillance
   violette. Ce sera un modele a part qu'on reutilisera pour toutes les pages
   avec des cards. »

   La difference entre les deux dit quelque chose de vrai :
     `.encadre`  une boite qui CONTIENT des reglages. On la lit, on la remplit.
                 Elle ne bouge pas : dix encadres qui se soulevent au passage de
                 la souris, sur un formulaire de trente champs, rendent l'ecran
                 nerveux et illisible.
     `.carte`    un OBJET de la liste, qu'on manipule (ouvrir, activer, dupliquer).
                 Elle se souleve et s'allume : le survol dit « celle-ci ».

   /!\ La surbrillance est peinte par `::before` AU-DESSUS du fond, et le contenu
   passe au-dessus d'elle par `.carte > *`. Premiere tentative, ratee : `::before`
   en `z-index: -1` avec `isolation: isolate` sur la carte. Sur le papier le
   negatif passe entre le fond et le contenu ; en pratique il s'est retrouve
   DERRIERE le fond de la carte, qui est opaque a 92 % — donc invisible. Verifie
   en peignant la couche en rouge vif : rien ne se voyait. Ne pas y revenir.

   /!\ `.carte > *` prend donc `position: relative`, ce qui fait de chaque enfant
   direct un bloc de reference pour ce qu'il contient en absolu. Rien a signaler
   sur les cartes actuelles ; a garder en tete si une carte doit un jour porter un
   panneau flottant qui depasse d'elle.

   /!\ Le bloc `prefers-reduced-motion` plus haut ramene les durees a 0,01 ms
   pour qui demande moins d'animation. La lueur reste, elle devient instantanee.

   /!\ Ne pas empiler `.carte` et `.panel--survol` : deux mouvements de survol sur
   le meme element se combattent. `.panel--survol` reste pour les surfaces qui ne
   sont pas des cartes. */
.carte {
  /* La marge horizontale de la carte, pour que le filet d'une `.carte-zone` aille
     d'un bord a l'autre. Elle vaut le padding declare plus haut ; un ecran qui
     resserre ce padding (une liste de dix cartes) redeclare les deux ensemble. */
  --carte-marge: 28px;
  transition: transform 320ms var(--ease-out),
              border-color 320ms var(--ease-out),
              box-shadow 320ms var(--ease-out);
}
.carte::before {
  content: "";
  position: absolute; inset: 0;
  border-radius: inherit;
  background:
    radial-gradient(900px 300px at 50% -8%, rgba(139,127,224,0.34), transparent 62%),
    linear-gradient(180deg, rgba(180,168,255,0.05) 0%, transparent 45%);
  opacity: 0;
  transition: opacity 320ms var(--ease-out);
  pointer-events: none;
}
.carte > * { position: relative; }
.carte:hover {
  transform: translateY(-3px);
  border-color: var(--accent-ring-strong);
  box-shadow:
    0 38px 96px -24px rgba(0,0,0,0.72),
    0 0 0 1px var(--accent-ring-strong),
    0 0 48px -10px var(--accent-glow-strong),
    inset 0 1px 0 0 rgba(200,190,255,0.28);
}
.carte:hover::before { opacity: 1; }
/* Le clavier doit voir la meme chose que la souris, sinon le survol est un
   privilege de la souris. Plus discret : on ne souleve pas la carte, le
   deplacement pendant une tabulation ferait sauter la page. */
.carte:focus-within {
  border-color: var(--accent-ring-strong);
  box-shadow: 0 30px 80px -20px rgba(0,0,0,0.6),
              0 0 0 1px var(--accent-ring),
              inset 0 1px 0 0 rgba(200,190,255,0.20);
}
.carte:focus-within::before { opacity: 0.55; }
/* Le titre d'une carte : plus present que celui d'un encadre, parce qu'il NOMME
   l'objet (« Scalp ES matin ») au lieu d'annoncer une rubrique. */
/* ── Une carte ENTIEREMENT cliquable (2026-08-17) ──
   Demande du fondateur : « lorsqu'on clique sur la carte, ça affiche le détail ». Elle se
   soulevait deja au survol ; il ne lui manquait que d'etre cliquable.

   Le contrat tient en deux classes :
     `.carte-lien`    le VRAI lien (le titre, en general). Son `::after` s'etend sur toute
                      la carte et devient la surface cliquable.
     `.carte-dessus`  tout ce qui doit rester cliquable PAR-DESSUS : un autre lien, un
                      bouton, un formulaire.

   /!\ Pas de `<a>` autour de la carte, et pas de `onclick` sur un `<div>`. Un lien qui
   enveloppe d'autres liens est du HTML invalide, et un `div` cliquable est invisible au
   clavier comme au lecteur d'ecran. Ici il y a UN lien, il porte un nom, et la tabulation
   l'atteint.

   /!\ Tout element interactif de la carte DOIT porter `.carte-dessus`. Un element
   positionne avec `z-index: 1` passe au-dessus de tous les `z-index: auto`, quel que soit
   l'ordre du code : oublier la classe rend le bouton inerte, et c'est le genre de panne
   qu'on ne voit qu'en essayant de cliquer.

   /!\ Contrepartie assumee : la selection du texte a la souris devient difficile sur la
   zone couverte. Sur une carte de resume, on clique bien plus souvent qu'on ne copie. */
/* /!\ Le lien doit etre un ENFANT DIRECT de la carte, et il reste `position: static`.
   `.carte > *` pose `position: relative` sur tous les enfants directs (c'est ce qui les
   fait passer au-dessus de la lueur) : un lien positionne devient le bloc conteneur de son
   propre `::after`, et la surface ne couvre alors que le lien. Premiere tentative faite
   sur le titre : la zone cliquable s'arretait au nom du trader, et seule une sonde
   `elementFromPoint` l'a montre. */
.carte > .carte-lien { position: static; }
.carte-lien { text-decoration: none; color: inherit; }
.carte-lien::after {
  content: ""; position: absolute; inset: 0;
  border-radius: inherit; z-index: 1;
}
/* Le lien porte un texte pour lecteur d'ecran, donc pas de boite visible : l'anneau de
   focus se pose sur la SURFACE, ce qui entoure la carte entiere au clavier. */
.carte-lien:focus-visible { outline: none; }
.carte-lien:focus-visible::after {
  outline: 2px solid var(--ring); outline-offset: -3px;
}
.carte-dessus { position: relative; z-index: 2; }

.carte-titre {
  margin: 0 0 4px;
  font-family: var(--font-sans);
  font-size: 19px; font-weight: 600;
  letter-spacing: -0.02em; line-height: 1.2;
  color: var(--foreground);
}
.cartes { display: flex; flex-direction: column; gap: 14px; }

/* ============================================================
   La MISE EN ROUTE (2026-08-28) : cinq marches, chacune cochee sur un fait
   observe par le serveur, jamais a la main.
   ============================================================
   /!\ La puce dit un ETAT, pas une identite : pleine et de la couleur du
   succes quand la marche est faite, creuse (juste un contour) sinon. Aucune
   couleur codee en dur, tout vient des variables de la charte. */
.mise-en-route-sous {
  margin: 0 0 16px;
  color: var(--muted-foreground);
  font-size: var(--text-body-sm);
}
.marches { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.marche { display: flex; align-items: flex-start; gap: 12px; }
.marche-puce {
  flex: 0 0 auto;
  width: 14px; height: 14px;
  margin-top: 3px;
  border-radius: var(--radius-pill);
  border: 2px solid var(--border-strong);
  background: transparent;
  transition: background 220ms var(--ease-out), border-color 220ms var(--ease-out);
}
.marche--faite .marche-puce {
  background: var(--success);
  border-color: var(--success);
}
.marche-corps { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.marche-titre {
  font-weight: 600;
  color: var(--foreground);
}
.marche--faite .marche-titre {
  color: var(--muted-foreground);
  text-decoration: line-through;
  text-decoration-color: var(--border-strong);
}
.marche-phrase { color: var(--muted-foreground); font-size: var(--text-body-sm); }

/* ============================================================
   LE BANDEAU de mise en route (2026-08-30) : la meme liste, mais sur TOUTES les
   pages de l'espace client, en une ligne au lieu d'une carte.
   ============================================================
   /!\ Il se pose EN HAUT du contenu et prend toute la largeur du `main`, dont
   il annule le padding lateral par des marges negatives : un bandeau qui
   s'arrete avant le bord se lit comme une carte, pas comme un bandeau.
   /!\ AUCUNE regle `display` sur `.bandeau-mer-liste` : le repli passe par
   l'attribut `hidden`, qu'une regle d'auteur ecraserait (piege connu du depot).
   Le JS du fragment ne fait que poser/retirer `hidden`. */
.bandeau-mer {
  margin: -32px -40px 24px;
  background: linear-gradient(90deg,
              var(--accent-ring-strong), var(--accent-wash));
  border-bottom: 1px solid var(--border);
}
.bandeau-mer-ligne {
  display: flex; align-items: center; gap: 14px;
  padding: 12px 40px;
}
.bandeau-mer-jauge { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; }
.bandeau-mer-jauge-barre {
  width: 120px; height: 6px; border-radius: var(--radius-pill);
  background: var(--border-strong); overflow: hidden;
}
.bandeau-mer-jauge-barre > span {
  display: block; height: 100%; border-radius: var(--radius-pill);
  background: var(--accent);
  transition: width 320ms var(--ease-out);
}
.bandeau-mer-compte {
  font-size: var(--text-body-sm); color: var(--muted-foreground);
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.bandeau-mer-texte { flex: 1 1 auto; min-width: 0; }
.bandeau-mer-texte b { font-weight: 600; color: var(--foreground); }
.bandeau-mer-texte i { font-style: normal; color: var(--muted-foreground); }
/* Le replieur est une PASTILLE discrete, pas un lien souligne : pose a cote du
   bouton plein « Continuer », un souligne brut se lit comme une note de bas de
   page collee sur un bandeau. Le chevron porte le sens du geste. */
.bandeau-mer-plier {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px; border-radius: var(--radius-pill);
  background: none; border: 1px solid var(--border); cursor: pointer;
  color: var(--muted-foreground); font-size: var(--text-body-sm);
  font-family: inherit; white-space: nowrap;
  transition: color 160ms var(--ease-out), background 160ms var(--ease-out),
              border-color 160ms var(--ease-out);
}
.bandeau-mer-plier:hover {
  color: var(--foreground);
  background: var(--accent-wash); border-color: var(--border-strong);
}
.bandeau-mer-chevron {
  width: 12px; height: 12px; flex: 0 0 auto;
  transition: transform 200ms var(--ease-out);
}
.bandeau-mer-plier[aria-expanded="true"] .bandeau-mer-chevron {
  transform: rotate(180deg);
}
.bandeau-mer-btn {
  flex: 0 0 auto;
  padding: 7px 14px; border-radius: var(--radius-md);
  background: var(--primary); color: var(--on-primary);
  font-size: var(--text-body-sm); font-weight: 600;
  text-decoration: none; white-space: nowrap;
}
.bandeau-mer-btn:hover { background: var(--accent-hi); }
.bandeau-mer-liste { padding: 2px 40px 16px; }

/* Sous 900 points la ligne ne tient plus : le texte passe sous la jauge et les
   deux commandes restent cote a cote, plutot que de se compresser jusqu'a
   l'illisible. */
@media (max-width: 900px) {
  .bandeau-mer { margin: -20px -20px 18px; }
  .bandeau-mer-ligne { flex-wrap: wrap; padding: 12px 20px; }
  .bandeau-mer-texte { flex-basis: 100%; order: 3; }
  .bandeau-mer-liste { padding: 2px 20px 16px; }
}

/* La GRILLE de cartes : elles se posent cote a cote et reviennent a la ligne toutes
   seules (2026-08-18, demande du fondateur sur « Mes traders »).

   /!\ Les cartes d'une meme rangee ont la MEME HAUTEUR, et c'est ce qui separe une
   grille rangee d'un escalier. C'est `align-items: stretch` (le defaut d'une grille)
   qui le donne ; encore faut-il que la carte, elle, sache occuper la hauteur qu'on
   lui offre : d'ou la colonne flexible ci-dessous. Un contenu plus court laisse alors
   son vide en BAS de la carte, jamais entre deux blocs.

   /!\ La largeur d'une colonne se regle en un seul endroit, `--carte-min`, et jamais
   dans un gabarit : c'est la lecon du 2026-08-18 (dix-sept largeurs ecrites en dur).
   `auto-fill` et non `auto-fit` : `auto-fit` etire une carte unique sur tout le cadre,
   ce qui donne une carte de 1280 de large quand un client n'a qu'un trader. */
.cartes--grille {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--carte-min, 320px), 1fr));
  /* /!\ `1fr` sur les rangees implicites, et non `auto` : sans lui, seules les cartes
     d'une MEME rangee ont la meme hauteur, et la deuxieme rangee est plus courte que la
     premiere. Les boutons d'une carte du bas ne tombent alors pas a la meme hauteur que
     ceux d'une carte du haut. Avec `1fr`, toutes les rangees prennent la hauteur de la
     plus grande et TOUTES les cartes de la grille sont identiques. */
  grid-auto-rows: 1fr;
  gap: 14px;
}
/* La carte devient une colonne : ce qui doit tomber en bas (les actions) porte
   `margin-top: auto` et s'y colle, quelle que soit la hauteur de la rangee. */
.cartes--grille > .carte {
  display: flex; flex-direction: column; margin-bottom: 0;
}
.carte-bas { margin-top: auto; }

/* Les ZONES d'une carte, separees par un filet (2026-08-18).

   Demande du fondateur, apres avoir vu la grille : « l'interieur manque de relief ».
   Il ne manquait pas de relief, il manquait de STRUCTURE : tout etait du texte gris
   aligne a gauche, a la meme taille, sauf le titre. Une carte se lit mieux en trois
   temps : ce que c'est, ce que ça produit, ce qu'on peut en faire.

   /!\ Le filet est pose en `border-top` sur la zone SUIVANTE, jamais en `<hr>` : un
   element vide de plus dans le flux serait lu a voix haute par un lecteur d'ecran
   sans rien signifier.

   /!\ Il deborde du padding de la carte (marges negatives) pour aller d'un bord a
   l'autre. Un filet qui s'arrete avant les bords ressemble a un soulignement rate. */
.carte-zone {
  border-top: 1px solid var(--border);
  margin-left: calc(-1 * var(--carte-marge, 28px));
  margin-right: calc(-1 * var(--carte-marge, 28px));
  padding: 12px var(--carte-marge, 28px) 0;
}


/* ============================================================
   Les MESURES : etiquette AU-DESSUS, valeur en dessous (2026-08-18)
   ============================================================
   Deux ou trois chiffres poses cote a cote dans une carte. Plus leger que `.chiffre`,
   qui est une TUILE (fond, bordure, lisere, survol) : une tuile posee dans une carte
   fait une boite dans une boite, et deux boites imbriquees se disputent le regard.

   /!\ L'etiquette est AU-DESSUS de la valeur, jamais apres. La carte ecrivait
   « rien analyse  aujourd'hui » : on lit la valeur avant de savoir de quoi elle
   parle, et deux mesures empilees ne ressemblent plus a une paire. Au-dessus, l'oeil
   descend d'une colonne a l'autre et compare.

   /!\ Les colonnes sont de largeur EGALE (`1fr`). Deux mesures qui repondent a deux
   questions de meme rang doivent avoir le meme poids ; la premiere qui s'elargirait
   parce que sa valeur est plus longue ferait croire a une hierarchie qui n'existe pas. */
/* ── CE QU'UN TRADER FAIT EN CE MOMENT (2026-08-21) ─────────────────────────
   La zone qui a remplace les chiffres de performance sur la carte d'un trader.

   /!\ La COULEUR porte l'etat, et c'est tout l'interet : dix cartes se lisent d'un
   regard sans lire un mot. Bleu en position, orange ordre posé, gris au repos.

   /!\ Ce sont les couleurs SEMANTIQUES (`--info`, `--warning`), pas l'accent de
   marque : elles disent un etat, pas une identite. Et jamais `--success`/`--error`
   ici -- une position ouverte n'est ni gagnante ni perdante, le serveur ne recoit
   aucun prix en direct et l'ignore.

   /!\ La pastille double la couleur du texte au lieu de la remplacer : une teinte
   seule est illisible pour huit hommes sur cent, et le mot reste le porteur. */
/* Le bloc de CLE POSE SUR UNE CARTE (2026-08-21). Le meme composant qu'ailleurs,
   resserre pour tenir dans une colonne de 320 px.

   /!\ L'etiquette « Cle du plugin » n'est pas SUPPRIMEE, elle est rendue invisible a
   l'oeil et gardee pour les lecteurs d'ecran. Elle occupait 110 px de la premiere
   ligne, ce qui renvoyait les trois boutons a la ligne suivante et faisait grandir
   chaque carte d'une rangee -- soit dix rangees pour dix traders, exactement le
   « ce sera indigestible » qui avait fait retirer ce bloc des cartes le 2026-08-17.
   Sur la carte, le contexte et les trois icones disent deja de quoi il s'agit. */
.trader-carte .trader-cle .spec-label {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; border: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.trader-carte .trader-cle { padding: 7px 9px; gap: 6px; }
/* Le masque ne revient JAMAIS a la ligne : coupe, il se lit comme deux cles. */
.trader-carte .trader-cle code { overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; overflow-wrap: normal; }

.vivant { display: flex; align-items: flex-start; gap: 10px; }
/* Le resultat du JOUR, pousse au bout de la ligne d'etat (2026-08-21). Il tient la
   parce que la carte fait 460 px ; sous 400, la grille passe a une colonne et la
   ligne a de la place de reste. Aligne a droite : un montant se lit par sa fin. */
.vivant-jour { margin-left: auto; text-align: right; flex: none; }
.vivant-pastille {
  width: 8px; height: 8px; border-radius: 50%; flex: none; margin-top: 6px;
  background: var(--disabled-foreground);
}
.vivant-txt { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.vivant-titre { font-size: 14px; font-weight: 500; color: var(--muted-foreground); }
.vivant-note {
  font-family: var(--font-mono); font-size: var(--text-caption);
  color: var(--muted-foreground);
}
.vivant--position .vivant-pastille {
  background: var(--info); box-shadow: 0 0 0 3px rgba(125,211,252,0.16);
}
.vivant--position .vivant-titre { color: var(--info); }
.vivant--ordre .vivant-pastille {
  background: var(--warning); box-shadow: 0 0 0 3px rgba(251,191,36,0.16);
}
.vivant--ordre .vivant-titre { color: var(--warning); }
/* Au repos et a l'arret : rien ne se detache. Une carte calme ne doit pas attirer
   l'oeil autant qu'une carte qui porte de l'argent en risque. */
.vivant--rien .vivant-titre,
.vivant--arret .vivant-titre { font-weight: 400; }

.mesures { display: grid; grid-template-columns: repeat(var(--mesures, 2), 1fr); gap: 14px; }
.mesure { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.mesure-l {
  font-family: var(--font-mono); font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  color: var(--muted-foreground);
}
.mesure-v {
  font-family: var(--font-mono); font-size: 17px; font-weight: 600;
  font-variant-numeric: tabular-nums; color: var(--foreground);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Une mesure qui n'a pas de nombre a montrer (« hors seance ») : le mot prend la
   place du chiffre, mais pas son poids. Il DIT quelque chose, il ne se compare pas. */
.mesure-v--mot {
  font-family: var(--font-sans); font-size: var(--text-body);
  font-weight: 500; color: var(--muted-foreground);
}
.mesure-n { font-size: var(--text-caption); color: var(--muted-foreground); }
/* Une mesure cliquable garde le texte du dessus et prend la teinte au survol. */
a.mesure { text-decoration: none; border-radius: var(--radius-md);
  margin: -4px -6px; padding: 4px 6px;
  transition: background var(--motion-base) var(--ease-out); }
a.mesure:hover, a.mesure:focus-visible { background: var(--accent-wash); }

/* ============================================================
   Le SOMMAIRE : naviguer dans une page longue (2026-08-17)
   ============================================================
   Demande du fondateur : « c'est assez chiant de devoir tout defiler pour acceder
   aux differentes sections ». La fiche d'un trader en compte une dizaine.

   Une barre qui reste en haut, une puce par section, celle qu'on lit s'allume.
   `sommaire.js` s'occupe de la surbrillance.

   /!\ Pourquoi PAS des onglets DANS le formulaire : il est UN tout, valide d'un
   bloc, et un refus portant sur un champ range dans un onglet ferme serait
   invisible. Le client lirait « refuse » sans voir quoi. Le formulaire reste donc
   entier sur sa page, et le sommaire n'y deplace rien : il fait defiler.

   /!\ A ne pas confondre avec le decoupage en PAGES du 2026-08-17 (Reglages /
   Regles / Gerer). Ce qui est parti sur une autre page n'appartenait pas au
   formulaire : ni la cle du plugin, ni les regles, ni l'arret ne s'enregistrent
   avec lui, ils agissent tout de suite. L'objection ci-dessus tient toujours, elle
   ne visait que le fait de CACHER une partie d'un meme formulaire.

   /!\ `top` vaut la hauteur de la barre d'application (56 px), qui est elle-meme
   collante. Sans cela le sommaire se glisserait DESSOUS et disparaitrait.

   /!\ `backdrop-filter` est ici justifie, a l'inverse de `.verre` : du contenu
   defile REELLEMENT derriere cette barre. Une seule zone a recomposer, pas dix
   cartes.

   /!\ Les sections cibles ont besoin de `scroll-margin-top` : sans lui, l'ancre
   les amene sous les deux barres collantes, et on atterrit sur un titre cache. */
.sommaire {
  position: sticky; top: 56px; z-index: 40;
  display: flex; align-items: center; gap: 2px;
  margin: 0 0 18px;
  padding: 5px;
  overflow-x: auto;
  background: rgba(16, 18, 26, 0.88);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  scrollbar-width: none;
}
.sommaire::-webkit-scrollbar { height: 0; }
.sommaire > a {
  flex: 0 0 auto;
  padding: 6px 13px;
  font-family: var(--font-sans); font-size: 12.5px; font-weight: 500;
  line-height: 1;
  color: var(--muted-foreground);
  text-decoration: none;
  border-radius: var(--radius-pill);
  white-space: nowrap;
  transition: background var(--motion-fast) var(--ease-out),
              color var(--motion-fast) var(--ease-out);
}
.sommaire > a:hover { color: var(--foreground); background: rgba(255,255,255,0.05); }
.sommaire > a[aria-current="true"] {
  background: var(--card);
  color: var(--accent);
  box-shadow: 0 1px 2px rgba(0,0,0,0.35);
}
/* 56 de barre d'application + 46 de sommaire + une respiration. */
[data-section] { scroll-margin-top: 116px; }

/* La CHAINE : les etapes d'un ordre, de gauche a droite (2026-08-18).
   ============================================================
   Choix du fondateur parmi trois maquettes (frise verticale, chaine, tableau) :
   « j'aime bien le B, avec toutes les informations utiles ».

   /!\ Ce qu'elle dit et qu'une liste ne disait pas : que c'est une SUITE. Chaque
   etape pointe vers la suivante, et la derniere se teinte du resultat. Les rangees
   d'avant alignaient trois blocs sans lien visible entre eux, et le montant partait
   a l'extreme droite, a mille pixels de l'etape qui l'avait produit.

   /!\ Sa limite est REELLE et il faut la connaitre avant d'y mettre autre chose :
   au-dela de quatre ou cinq etapes elle se replie sur deux lignes, et le detail de
   chaque etape doit rester court. Un ordre en compte trois dans le cas courant
   (pose, rempli, sorti), deux s'il est annule, un s'il attend encore.

   /!\ La fleche est un CARRE tourne a 45 degres, pas un caractere : elle doit
   prendre le fond et la bordure de l'etape qu'elle prolonge, y compris quand celle-ci
   est teintee. Un « > » textuel ne saurait pas suivre. */
.chaine { display: flex; align-items: stretch; flex-wrap: wrap; }
.chaine-etape {
  flex: 1 1 190px; min-width: 0; position: relative;
  padding: 14px 18px 14px 22px;
  background: rgba(255,255,255,0.02);
  border: 1px solid var(--border); border-left: 0;
}
.chaine-etape:first-child {
  border-left: 1px solid var(--border);
  border-radius: var(--radius-lg) 0 0 var(--radius-lg);
  padding-left: 18px;
}
.chaine-etape:last-child { border-radius: 0 var(--radius-lg) var(--radius-lg) 0; }
.chaine-etape + .chaine-etape::before {
  content: ""; position: absolute; left: -7px; top: 50%;
  width: 12px; height: 12px; margin-top: -6px;
  background: rgba(255,255,255,0.02);
  border-top: 1px solid var(--border); border-right: 1px solid var(--border);
  transform: rotate(45deg);
}
.chaine-rang {
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 500;
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  color: var(--muted-foreground);
}
.chaine-quoi {
  margin-top: 4px;
  font-family: var(--font-sans); font-size: 14px; font-weight: 600;
  color: var(--foreground);
}
.chaine-motif { font-weight: 400; color: var(--muted-foreground); }
.chaine-heure {
  margin-top: 3px;
  font-family: var(--font-mono); font-size: 12px; color: var(--muted-foreground);
}
.chaine-detail {
  margin-top: 7px; font-size: 12px; line-height: 1.5;
  color: var(--disabled-foreground);
}
.chaine-montant {
  margin-top: 7px;
  font-family: var(--font-mono); font-size: 15px; font-weight: 600;
}
/* L'etape qui SOLDE prend la teinte de son resultat — et sa fleche avec elle, sinon
   le raccord se voit. */
.chaine-etape--gain { background: rgba(60,190,130,0.06); border-color: rgba(60,190,130,0.30); }
.chaine-etape--gain::before { background: rgba(60,190,130,0.06); }
.chaine-etape--perte { background: rgba(214,90,90,0.06); border-color: rgba(214,90,90,0.30); }
.chaine-etape--perte::before { background: rgba(214,90,90,0.06); }
/* Sous 640 px les etapes s'empilent : la fleche laterale n'a plus de sens, les coins
   se remettent d'aplomb. */
@media (max-width: 640px) {
  .chaine-etape,
  .chaine-etape:first-child,
  .chaine-etape:last-child {
    flex: 1 1 100%; border-left: 1px solid var(--border); border-radius: var(--radius-lg);
  }
  .chaine-etape + .chaine-etape { margin-top: 8px; }
  .chaine-etape + .chaine-etape::before { display: none; }
}

/* Le VERDICT : la reponse a la question qu'on vient poser (2026-08-18).
   ============================================================
   Une bande, pas une rangee de tuiles : le montant a gauche, gros, et ce qui le
   qualifie a droite, plus petit. C'etait cinq tuiles de meme poids, ou le resultat
   -- la seule chose qu'un client vient chercher -- se lisait comme le cout de
   l'analyse.

   /!\ Le lisere suit le SIGNE du montant, jamais l'etat. Une issue inconnue et une
   abstention restent neutres : il n'y a pas de montant a annoncer, et un vert sur
   « aucun trade » se lirait comme un gain. */
.verdict {
  display: flex; align-items: stretch; flex-wrap: wrap;
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  overflow: hidden; margin-bottom: 16px; background: var(--card);
}
.verdict-gros {
  flex: 0 0 auto; min-width: 210px; padding: 16px 26px 16px 20px;
  border-right: 1px solid var(--border);
}
.verdict-reste {
  flex: 1 1 380px; display: grid;
  grid-template-columns: repeat(auto-fit, minmax(165px, 1fr));
}
.verdict-case { padding: 16px 18px; border-left: 1px solid var(--border); }
.verdict-case:first-child { border-left: 0; }
.verdict-l {
  display: block; font-size: 10.5px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--muted-foreground); margin-bottom: 5px;
}
.verdict-v {
  display: block; font-size: 27px; font-weight: 600; line-height: 1.05;
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
}
.verdict-case .verdict-v { font-size: 17px; font-weight: 500; }
.verdict-v--mot { font-family: var(--font-sans); font-size: 19px; font-weight: 500; }
.verdict-n { display: block; font-size: 11.5px; color: var(--muted-foreground); margin-top: 4px; }
.verdict--gain { border-color: rgba(60,190,130,0.34); background: rgba(60,190,130,0.05); }
.verdict--gain .verdict-gros { border-right-color: rgba(60,190,130,0.30); }
.verdict--perte { border-color: rgba(214,90,90,0.34); background: rgba(214,90,90,0.05); }
.verdict--perte .verdict-gros { border-right-color: rgba(214,90,90,0.30); }
@media (max-width: 720px) {
  .verdict-gros { flex: 1 1 100%; border-right: 0; border-bottom: 1px solid var(--border); }
}

/* L'ECHELLE DE PRIX : le trade entier sur un axe (2026-08-18).
   ============================================================
   Maquette « C », choisie par le fondateur parmi trois. Ce qu'elle dit et qu'aucune
   liste ne disait : les DISTANCES. Trois nombres empiles (5209 / 5200 / 5191) ne
   montrent pas qu'on risquait exactement ce qu'on visait ; deux zones a l'echelle le
   montrent sans une phrase. Et quand la sortie tombe sur l'objectif, le point s'y
   pose tout seul.

   /!\ Les hauteurs viennent de `performance.echelle_de_prix`, en POURCENTAGES : c'est
   de l'arithmetique sur des prix, pas de la mise en page. Le CSS ne fait que les poser.

   /!\ CE PARAGRAPHE DISAIT LE CONTRAIRE JUSQU'AU 2026-08-20, et il faut savoir ce qui
   l'a fait changer. Il affirmait qu'on gardait les positions vraies « plutot que de les
   ecarter, ecarter mentirait ». La gestion de position a tranche la question dans les
   faits : un stop suiveur produit une dizaine de deplacements a quelques ticks les uns
   des autres, et sept lignes l'une sur l'autre ne montrent RIEN du tout. Un dessin
   illisible ne dit pas la verite plus fidelement, il ne dit plus rien.

   Le compromis retenu, et il ne ment pas : le POINT et le TRAIT restent a leur hauteur
   vraie (`y`), seul le TEXTE se decale du minimum necessaire (`y_etiq` a gauche,
   `y_texte` a droite). Et avant meme d'ecarter, `performance.etapes_du_trade` REGROUPE :
   une serie de crans est un seul fait, donc une seule marque. Regrouper d'abord,
   ecarter ensuite. */
/* /!\ Le CADRE interieur n'est pas une coquetterie : les etiquettes sont centrees sur
   leur trait (`translateY(-50%)`), donc celle du prix le plus haut deborde de la moitie
   de sa hauteur au-dessus de `top: 0`. Sans cette marge elle chevauchait le titre de la
   section, et celle du bas mordait sur la frise. Les pourcentages se lisent DANS ce
   cadre, ce qui garde les distances exactes. */
.echelle { position: relative; height: 268px; margin: 4px 0 18px; }
.echelle-cadre { position: absolute; left: 0; right: 0; top: 26px; bottom: 26px; }
.echelle-axe {
  position: absolute; left: 190px; top: 0; bottom: 0; width: 2px;
  background: var(--border);
}
.ech-trait { position: absolute; left: 190px; right: 0; border-top: 1px dashed var(--border); }
.ech-etiq {
  position: absolute; left: 0; width: 178px; text-align: right;
  transform: translateY(-50%); font-size: 12px; line-height: 1.35;
}
.ech-etiq b {
  display: block; font-size: 15px; font-weight: 600;
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
}
.ech-etiq span { display: block; color: var(--muted-foreground); font-size: 11.5px; }
.ech-pt {
  position: absolute; left: 184px; width: 14px; height: 14px; border-radius: 50%;
  transform: translateY(-50%); border: 2px solid var(--background);
}
.ech-pt--stop     { background: var(--error); }
.ech-pt--objectif { background: var(--success); }
.ech-pt--entree,
.ech-pt--annulation { background: var(--muted-foreground); }
/* Les FAITS remontes par le plugin, colonne de droite.
   /!\ `right: 184px` reserve la bande ou vivent les libelles de zone. Les deux
   partageaient la meme colonne, et « risque -98 $ · 2.5 pts » passait par-dessus une
   marque : vu a l'ecran le 2026-08-20, sur un trade ou le suiveur avait travaille.
   La zone de risque est MINCE quand le R:R est de 8, donc son libelle tombe pile a
   hauteur d'entree -- exactement la ou toutes les marques se concentrent. */
.ech-fait {
  position: absolute; left: 218px; right: 184px; transform: translateY(-50%);
  font-size: 12px; color: var(--muted-foreground);
}
.ech-fait b { color: var(--foreground); font-weight: 500; }
.ech-zone { position: absolute; left: 192px; right: 0; }
.ech-zone--risque { background: rgba(214,90,90,0.07); border-left: 2px solid rgba(214,90,90,0.40); }
.ech-zone--gain   { background: rgba(60,190,130,0.07); border-left: 2px solid rgba(60,190,130,0.40); }
/* /!\ A DROITE, et non a gauche de la zone : voir `.ech-fait` ci-dessus. Le libelle
   d'une zone n'a pas d'ancrage horizontal naturel -- il decrit une BANDE, pas un point --
   donc c'est lui qui cede la colonne aux marques, qui ont un prix a designer. */
.ech-zone-t {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  max-width: 168px; text-align: right; font-size: 11.5px;
}
/* Sous 720 px l'axe n'a plus la place de porter trois colonnes : les annotations du
   reel passent SOUS leur etiquette, et le dessin garde ses distances. */
@media (max-width: 720px) {
  .echelle { height: 330px; }
  .echelle-axe, .ech-trait { left: 118px; }
  .ech-zone { left: 120px; }
  .ech-etiq { width: 106px; }
  .ech-pt { left: 112px; }
  .ech-fait { left: 146px; font-size: 11px; }
  /* Le libelle de zone disparait ici, donc `.ech-fait` reprend toute la largeur :
     lui laisser la reserve de 184 px la couperait pour rien. */
  .ech-fait { right: 8px; }
  .ech-zone-t { display: none; }
}

/* La FRISE : les memes etapes, en toutes lettres, sous le dessin.
   /!\ Elle n'est pas un doublon de l'echelle : le dessin porte les PRIX, la frise
   porte les HEURES et les durees, qu'aucune hauteur ne peut dire. */
.frise {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden;
}
/* /!\ Les separateurs sont une OMBRE, pas une bordure, et ce n'est pas un caprice.
   La grille est en `auto-fit` : elle se replie sur plusieurs rangees des que les cartes
   sont nombreuses, ce que la gestion de position rend courant. Avec une `border-left`,
   `:first-child` ne dé-bordait que la premiere carte du DOM -- a partir de la deuxieme
   rangee, la carte de gauche gardait un trait orphelin, et rien ne separait les rangees.

   Une ombre portee ne prend AUCUNE place, donc elle ne decale pas la grille ; celles
   des bords tombent sous la bordure du cadre, que `overflow: hidden` rogne. Et les
   cellules vides de la derniere rangee restent invisibles, ce qu'un `gap` colore sur
   fond de bordure n'aurait pas permis : il y aurait dessine un bloc gris. */
.frise-e {
  padding: 11px 14px;
  box-shadow: -1px 0 0 var(--border), 0 -1px 0 var(--border);
}
.frise-q { font-size: 13px; font-weight: 500; }
.frise-h {
  font-size: 11.5px; color: var(--muted-foreground); margin-top: 3px;
  font-variant-numeric: tabular-nums;
}
.frise-e--gain  { background: rgba(60,190,130,0.06); }
.frise-e--perte { background: rgba(214,90,90,0.06); }

/* Le graphique en BARRES EMPILEES de « Mes analyses » (2026-08-18).
   ============================================================
   Maquette « A », choisie par le fondateur parmi trois (empilees, groupees,
   divergentes). Une barre par periode : sa HAUTEUR dit combien le trader a analyse,
   ses trois segments disent ce qu'il en a fait.

   /!\ Pourquoi les empilees : l'abstention est la decision LA PLUS FREQUENTE (six
   analyses sur dix sur les donnees reelles du jour). Empilee, elle garde son poids
   visuel et le volume analyse se lit d'un coup ; groupee, elle devient une barre grise
   parmi trois et le total disparait.

   /!\ Chaque barre est un LIEN, comme sa rangee : on clique ce qu'on regarde. C'est
   aussi pour ca qu'elle porte `data-bulle` -- le meme mecanisme d'infobulle que les
   rangees de trades, pas un second. */
.barres {
  display: flex; align-items: flex-end; gap: 8px;
  height: 190px; padding: 0 2px; margin-bottom: 26px;
}
.barre {
  flex: 1 1 0; min-width: 0; height: 100%;
  display: flex; flex-direction: column; justify-content: flex-end;
  position: relative; text-decoration: none;
  border-radius: 4px 4px 0 0;
  transition: opacity var(--motion-fast) var(--ease-out);
}
/* /!\ L'ensemble palit et la barre visee reste pleine : teinter la seule barre survolee
   ne se voit pas sur une colonne de 6 px de large. */
.barres:hover .barre { opacity: .45; }
.barres .barre:hover { opacity: 1; }
.barre-pile {
  width: 100%; display: flex; flex-direction: column; justify-content: flex-end;
  border-radius: 3px 3px 0 0; overflow: hidden;
  /* Une periode VIDE garde sa colonne : un trait suffit a dire qu'elle existe et qu'il
     ne s'y est rien passe. La faire disparaitre resserrerait les autres. */
  min-height: 2px;
}
.barre-seg { display: block; width: 100%; }
.barre-seg--achat { background: var(--success); }
.barre-seg--vente { background: var(--error); }
.barre-seg--abst  { background: rgba(255,255,255,0.13); }
.barre-quand {
  position: absolute; bottom: -20px; left: 0; right: 0; text-align: center;
  font-size: 10.5px; color: var(--muted-foreground);
  font-variant-numeric: tabular-nums;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.barres-axe {
  display: flex; justify-content: space-between;
  font-size: 11px; color: var(--muted-foreground); margin-bottom: 6px;
}
.barres-legende {
  display: flex; gap: 18px; align-items: center; flex-wrap: wrap;
  font-size: 12px; color: var(--muted-foreground);
}
/* /!\ « puce » et non « pastille » : le second appartient aux FILTRES de « Mes trades »
   (le mot du fondateur, valide sur maquette). Les deux ont coexiste une heure le
   2026-08-18, et les pastilles de filtre heritaient des 10 px de ce point de legende --
   elles se chevauchaient toutes. Deux objets differents ne partagent pas un nom. */
.puce {
  display: inline-block; width: 10px; height: 10px; border-radius: 3px;
  margin-right: 6px; vertical-align: -1px;
}
.puce--achat { background: var(--success); }
.puce--vente { background: var(--error); }
.puce--abst  { background: rgba(255,255,255,0.16); }
/* Sous 640 px les etiquettes de date ne tiennent plus sous des colonnes de 8 px : on les
   retire plutot que de les laisser se chevaucher. L'infobulle les porte toujours. */
@media (max-width: 640px) {
  .barres { gap: 4px; }
  .barre-quand { display: none; }
}

/* La PAGINATION d'une longue liste (2026-08-18).
   /!\ Elle porte son ETAT en toutes lettres (« analyses 201 a 400 sur 1 894 »), et
   pas seulement des fleches. Un « suivant » seul ne dit ni ou l'on est, ni combien
   il reste : le client ne sait pas s'il a tout vu, ce qui est exactement le defaut
   que la pagination vient corriger. */
.pagination {
  display: flex; align-items: center; justify-content: center; gap: 14px;
  flex-wrap: wrap;
  margin-top: 18px; padding-top: 16px;
  border-top: 1px solid var(--border);
}
.pagination-etat { font-size: 13px; color: var(--muted-foreground); }
.pagination-etat strong { color: var(--foreground); font-weight: 600; }

/* La RANGEE D'AJOUT : ce qui ouvre le formulaire d'un element de liste
   (2026-08-18). Remarque du fondateur sur « Ajouter une regle » : « je trouve le
   bouton pas du tout esthetique ».

   /!\ Il avait raison, et pas seulement sur le dessin : un bouton PLEIN, de la
   couleur d'accent, pose au-dessus de la liste, criait plus fort que les regles
   elles-memes. C'est l'action la plus rare de l'ecran (on ecrit une regle une
   fois, on relit la liste souvent) et elle portait l'accent le plus vif.

   /!\ Il descend donc EN BOUT DE LISTE, en pointille : c'est la ligne suivante
   qu'on n'a pas encore ecrite. Le geste se lit sans qu'aucune couleur ne le
   reclame, et il est la ou l'oeil finit sa lecture. */
.rangee-ajout {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  width: 100%; margin-top: 10px; padding: 13px 16px;
  appearance: none; background: none; cursor: pointer;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-lg);
  font-family: inherit; font-size: 13px; font-weight: 500;
  color: var(--muted-foreground);
  transition: color var(--motion-fast) var(--ease-out),
              border-color var(--motion-fast) var(--ease-out),
              background var(--motion-fast) var(--ease-out);
}
.rangee-ajout:hover, .rangee-ajout:focus-visible {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-wash);
}

/* ============================================================
   Les GESTES d'un en-tete, et leur menu (2026-08-18)
   ============================================================
   Demande du fondateur : arreter un trader, le dupliquer, voir ses trades « en un
   clic », « dans l'en-tete, pour que ce soit user-friendly ». Une page « Gerer »
   entiere a disparu au profit de cette rangee.

   /!\ `align-items: flex-start` : l'en-tete peut tenir sur deux lignes de titre,
   les gestes restent en haut, alignes sur la premiere.

   /!\ `flex-wrap` : cinq boutons plus un menu ne tiennent pas a cote d'un titre sur
   un ecran etroit. Ils passent a la ligne plutot que de comprimer le titre. */
.e-gestes {
  display: flex; align-items: flex-start; flex-wrap: wrap;
  gap: 8px; flex-shrink: 0;
}

/* L'ETAT, dans le titre. C'est la premiere question qu'on se pose en ouvrant la
   fiche d'un trader — est-ce qu'il tourne ? — et elle n'etait ecrite nulle part.
   /!\ La pastille est GRISE par defaut et verte seulement si le trader est actif :
   un point colore qui ne dit rien ne sert a rien. */
.titre-avec-etat { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.etat-pastille {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 5px 13px 5px 11px;
  background: var(--muted); border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  font-family: var(--font-sans); font-size: 12.5px; font-weight: 400;
  color: var(--muted-foreground);
}
.etat-pastille b { color: var(--foreground); font-weight: 600; }
.etat-pastille > span + span::before {
  content: "·"; margin-right: 8px; color: var(--muted-foreground);
}
.etat-point {
  width: 7px; height: 7px; flex: 0 0 auto; border-radius: 50%;
  background: var(--disabled-foreground);
}
.etat-pastille--on .etat-point { background: var(--success); }

/* Le MENU des gestes rares. Un `<details>` : il s'ouvre, se ferme, se prend au
   clavier et s'annonce, sans une ligne de script. `menu.js` ne fait que le refermer
   quand on clique ailleurs — ce que `<details>` ne sait pas faire seul.

   /!\ Ce qui vit ici n'y est pas par manque de place : ce sont les gestes qui NE SE
   DEFONT PAS. Un bouton « Supprimer » aligne a cote de « Dupliquer » est un accident
   qui attend son heure. */
.menu { position: relative; }
/* La fleche que le navigateur ajoute a tout `<summary>` n'a pas sa place sur un
   bouton d'icone : elle doublerait les trois points. */
.menu > summary { list-style: none; cursor: pointer; }
.menu > summary::-webkit-details-marker { display: none; }
.menu > summary::marker { content: ""; }
.menu-panneau {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 60;
  min-width: 220px;
  display: flex; flex-direction: column; gap: 2px;
  padding: 6px;
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  box-shadow: 0 18px 44px -16px rgba(0,0,0,0.7);
}
.menu-item {
  appearance: none; background: none; border: 0; cursor: pointer;
  display: flex; align-items: center; gap: 9px;
  width: 100%; padding: 9px 11px;
  font-family: inherit; font-size: 13px; font-weight: 500; text-align: left;
  color: var(--foreground);
  border-radius: var(--radius-md);
  transition: background var(--motion-fast) var(--ease-out);
}
.menu-item:hover, .menu-item:focus-visible { background: var(--muted); }
/* /!\ SANS CETTE LIGNE, `hidden` NE CACHE RIEN ICI. Le `display: flex` juste
   au-dessus est une regle d'AUTEUR, et elle bat le `display: none` que l'agent
   utilisateur applique a `[hidden]`. Piege deja paye dans ce depot. C'est ce
   qui permet a `rappel-guide.js` de ne laisser qu'une entree d'onglet dans le
   menu du point d'interrogation ; ne pas la retirer en croyant nettoyer. */
.menu-item[hidden] { display: none; }
.menu-item--danger { color: var(--error); }
/* /!\ Le trait n'est pas decoratif : il separe ce qui se defait de ce qui ne se
   defait pas. Sous lui, arreter et supprimer. */
.menu-trait { height: 1px; background: var(--border); margin: 5px 4px; }
a.menu-item { text-decoration: none; }
/* Le titre d'un GROUPE d'entrees, quand le menu en porte deux sortes (le rappel
   du guide, dans la barre du haut : les ecrans a rejouer, puis le guide ecrit).

   /!\ UN NOM NEUF, et c'est delibere. La tentation etait de reprendre
   `.menu-item` en le neutralisant, ou l'un des `.popover-label` d'a cote : une
   classe partagee par deux objets differents a deja coute deux fois ici, avec
   des boutons ecrases sans qu'aucun test ne le voie. Ce titre n'est pas
   cliquable, il n'a donc rien a partager avec une entree. */
.menu-titre {
  margin: 2px 4px 4px; padding: 0 7px;
  font-size: 11px; font-weight: 600; letter-spacing: .02em;
  text-transform: uppercase; color: var(--muted-foreground);
}

/* ============================================================
   Les ONGLETS : une page longue coupee en vues (2026-08-18)
   ============================================================
   Demande du fondateur : « je trouve la page Mes Trades tres lourde avec les
   trois parties ». Le sommaire FAISAIT DEFILER une page de dix mille pixels ;
   les onglets n'en affichent qu'un tiers a la fois.

   /!\ Ce n'est PAS un composant de plus : c'est `.seg`, le selecteur segmente
   deja en place partout (« Tous 3 · Actifs 2 · Desactives 1 » sur la liste des
   traders), avec le seul ajout dont un onglet a besoin. Le fondateur l'a tranche
   le 2026-08-18 en montrant ce selecteur : « mets juste les onglets sur ce
   gabarit-la ». Un premier jet avait duplique `.seg` sous le nom `.onglets`, avec
   ses propres teintes — deux composants pour un geste, qui auraient diverge.

   Ce que le modificateur ajoute, et rien de plus :
     - il ne deborde pas (neuf onglets sur un ecran etroit defilent a plat) ;
     - il peut suivre le lecteur (`--collant`), pour une page dont un onglet fait
       dix mille pixels.

   /!\ Deux facons de dire « celui-la », et `.seg` les porte deja toutes deux :
   `aria-current="page"` quand l'onglet est une ADRESSE (« Mes trades », ou chaque
   onglet est une page), `aria-selected` quand il bascule SANS quitter la page (les
   parties des reglages d'un trader, qui sont UN formulaire et ne peuvent pas se
   recharger sans perdre ce qui est saisi).

   /!\ `type="button"` est obligatoire sur la seconde forme : un bouton dans un
   formulaire l'ENVOIE par defaut, et cliquer un onglet enregistrerait le trader. */
.seg--onglets {
  /* /!\ `inline-flex`, comme `.seg` : la barre EPOUSE ses onglets au lieu de
     s'etirer sur toute la largeur. C'est ce que le fondateur a montre en pointant
     le selecteur « Tous 3 · Actifs 2 · Desactives 1 ». Etiree, une barre de trois
     onglets laissait huit cents points de pilule vide a droite.
     `max-width` + `overflow-x` gardent le cas inverse : neuf onglets sur un ecran
     etroit defilent a plat plutot que de deborder. */
  display: inline-flex;
  vertical-align: top;
  max-width: 100%;
  overflow-x: auto;
  scrollbar-width: none;
  margin: 0 0 18px;
}
.seg--onglets::-webkit-scrollbar { height: 0; }
.seg--onglets > * { flex: 0 0 auto; }
/* /!\ `top` vaut la hauteur de la barre d'application (56 px), qui est elle-meme
   collante. Sans cela la barre d'onglets se glisserait DESSOUS et disparaitrait. */
.seg--collant { position: sticky; top: 56px; z-index: 40; }

/* ============================================================
   Le PERIMETRE : la barre de filtres qui se replie (2026-08-18)
   ============================================================
   Meme demande. Les cinq filtres qui gouvernent toute la page occupaient deux
   rangees en permanence, sur chacun des trois onglets.

   Elle se replie sur UNE ligne, et cette ligne continue de DIRE le perimetre.

   /!\ Le RESUME n'est pas decoratif, il est la condition du repli. Replier une
   barre de filtres sans rien laisser voir transforme tous les chiffres du dessous
   en chiffres sans leur cadre -- « +412 $ » sur quoi, sur quelle periode, sur quel
   trader ? C'est precisement ce que cette page combat depuis le 2026-08-12. Le
   repli cache les COMMANDES, jamais l'etat.

   /!\ L'en-tete est un vrai `<button>`, pas un `<div>` cliquable : il se prend au
   clavier, il s'annonce, et `aria-expanded` dit dans quel etat il est.

   /!\ Elle n'est PAS collante, a l'inverse des onglets. Deux barres collantes
   l'une sur l'autre mangent 110 px de hauteur en permanence -- soit ce qu'on
   vient de gagner en repliant. C'est la barre d'onglets qui suit le lecteur ;
   celle-ci se consulte quand on la cherche. */
.perimetre { margin: 0 0 12px; }
/* La barre et sa remise a zero sur la meme ligne. /!\ Le lien est DEHORS du bouton de
   repli : un `<a>` dans un `<button>` est du HTML invalide, et le clic partirait au
   repli plutot qu'au lien. */
/* /!\ FINDING C, ronde 2 de revue (2026-08-20) : `flex-wrap` n'attendait plus le
   passage sous 640px pour être nécessaire. Depuis que la rangée porte aussi le
   déclencheur des vues et, quand une vue ouverte est retouchée, trois actions en
   clair, leur largeur cumulée peut dépasser celle de la rangée sur un écran large
   aussi. Sans un wrap, le seul enfant à `min-width:0` (`.perimetre-tete`, juste en
   dessous) absorbait tout le manque -- jusqu'à l'écraser à zéro. Le résumé du
   périmètre EST la condition du repli (cf. le commentaire du gabarit) : le perdre
   revient à refermer la barre sans rien laisser voir, au moment où le client vient
   justement de la toucher. */
.perimetre-rangee { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* /!\ `flex: 1 1 240px` et pas la `width: 100%` de la regle de base (2026-08-21,
   demande du fondateur : « les vues sur la meme ligne que le filtre pour economiser
   de la hauteur »). Une `width: 100%` sur un enfant de flex lui donne une taille de
   base egale a TOUTE la rangee : le bouton de repli reclamait donc la ligne entiere,
   et le declencheur des vues -- pose avant lui depuis le 2026-08-20 -- se retrouvait
   REJETE SUR SA PROPRE LIGNE par le `flex-wrap` ci-dessus. Ce n'etait pas une mise en
   page choisie, c'etait un effet de bord : deux rangees la ou une suffit, sur chacun
   des trois onglets.

   /!\ La base reste 240px et le plancher aussi : le resume du perimetre garde de quoi
   se lire (cf. le test `test_le_perimetre_tete_garde_un_plancher_de_largeur`), et
   quand la rangee « vue modifiee » porte ses trois actions, c'est le `flex-wrap` qui
   recoupe la ligne -- jamais l'ecrasement du resume. */
.perimetre-rangee .perimetre-tete { flex: 1 1 240px; min-width: 240px; }
.perimetre-raz {
  /* /!\ C'est un `<button>` dans un `<form>` depuis 2026-08-20 (tache 9), plus un
     `<a>` : `appearance`, `cursor` et `font-family` remettent a plat ce qu'un bouton
     natif imposerait sinon (curseur fleche, police systeme), que les regles
     suivantes -- pensees pour un lien -- ne couvraient pas. */
  appearance: none; cursor: pointer; font-family: inherit;
  flex: 0 0 auto; padding: 8px 14px;
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  background: var(--card); color: var(--muted-foreground);
  font-size: 12.5px; white-space: nowrap; text-decoration: none;
  transition: color var(--motion-fast) var(--ease-out),
              border-color var(--motion-fast) var(--ease-out);
}
.perimetre-raz:hover { color: var(--foreground); border-color: var(--border-strong); }
@media (max-width: 640px) {
  .perimetre-rangee { flex-wrap: wrap; }
  .perimetre-raz { flex: 1 1 100%; text-align: center; }
}
.perimetre-tete {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 9px 14px;
  background: var(--card);
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  cursor: pointer; text-align: left;
  font: inherit; color: var(--foreground);
  transition: border-color var(--motion-fast) var(--ease-out);
}
.perimetre-tete:hover { border-color: var(--border-strong); }
.perimetre-tete .chev {
  flex: 0 0 auto; color: var(--muted-foreground);
  transition: transform var(--motion-base) var(--ease-out);
}
.perimetre[data-replie="non"] .perimetre-tete .chev { transform: rotate(180deg); }
.perimetre-l {
  flex: 0 0 auto;
  font-family: var(--font-mono); font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  color: var(--muted-foreground);
}
.perimetre-resume {
  flex: 1 1 auto; min-width: 0;
  font-size: 13px; color: var(--foreground);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Les faits du resume, separes par un point median pose en CSS : le gabarit ecrit
   des `<span>`, jamais des separateurs -- un fait absent ne doit pas laisser un
   « · » orphelin. */
.perimetre-resume > span + span::before {
  content: "\00b7"; margin: 0 7px; color: var(--muted-foreground);
}

/* ============================================================
   L'ACCES RAPIDE : Trader et Periode, hors des pastilles (2026-08-21)
   ============================================================
   Demande du fondateur : « les filtres Trader et Periode, tu les laisses en acces
   rapide, pas besoin de mettre un filtre, pour pouvoir switcher rapidement ».

   /!\ Les periodes sont A PLAT, pas dans un menu, et c'est tout l'objet de la
   demande. Un raccourci qu'il faut deplier avant de cliquer coute deux gestes,
   soit exactement ce qu'on venait supprimer. Le trader garde son menu : les noms
   sont longs, et leur nombre n'est pas borne.

   /!\ Ces deux rangees sont DEHORS de `.perimetre` : elles ne se replient pas avec
   lui. Une commande d'acces rapide qui disparait au repli n'est plus rapide. */
.rapide {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 0 0 8px;
}
.rapide-l {
  flex: 0 0 auto;
  font-family: var(--font-mono); font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  color: var(--muted-foreground);
}
/* Le libelle d'une seconde commande sur la meme rangee (la version) respire un peu
   plus a gauche, sinon il se colle au selecteur qui le precede. */
.rapide-l--suite { margin-left: 10px; }
.rapide-pastilles { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
/* Le declencheur d'un menu d'acces rapide (trader, version). */
.rapide-choix {
  list-style: none;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 11px;
  background: var(--card);
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  font-size: 12.5px; color: var(--foreground);
  cursor: pointer; white-space: nowrap;
}
.rapide-choix::-webkit-details-marker { display: none; }
.rapide-choix::marker { content: ""; }
.rapide-choix:hover { border-color: var(--border-strong); }
.rapide-choix .chev { font-size: 10px; color: var(--muted-foreground); }
.rapide-liste { left: 0; right: auto; min-width: 240px; }
.rapide-liste .menu-item[aria-current="page"] {
  background: rgba(180,168,255,0.14); font-weight: 600;
}
/* L'effectif, pousse a droite de la ligne : sans lui on choisit a l'aveugle -- meme
   raison que sur la bascule Reel/Rejeu et sur les onglets. */
.rapide-n {
  margin-left: auto; padding-left: 12px;
  font-family: var(--font-mono); font-size: 11px; color: var(--muted-foreground);
}
/* Un raccourci de periode. */
.rapide-p {
  padding: 5px 12px;
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  background: var(--card); color: var(--muted-foreground);
  font-size: 12.5px; white-space: nowrap; text-decoration: none;
  cursor: pointer;
  transition: color var(--motion-fast) var(--ease-out),
              border-color var(--motion-fast) var(--ease-out);
}
.rapide-p:hover { color: var(--foreground); border-color: var(--border-strong); }
.rapide-p[aria-current="page"] {
  background: rgba(180,168,255,0.14); border-color: rgba(180,168,255,0.5);
  color: var(--foreground); font-weight: 600;
}
/* /!\ « Personnalise » est un `<span>`, pas un lien : il n'a pas de bornes a lui,
   ce sont les deux dates saisies. Le trait discontinu le dit avant le clic. */
.rapide-p--perso { border-style: dashed; cursor: default; }
.rapide-perso {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-left: 6px; font-size: 12.5px; color: var(--muted-foreground);
}
.rapide-perso input[type="date"] {
  font-family: var(--font-mono); font-size: 12px; padding: 4px 8px;
  border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--card); color: var(--foreground);
}
@media (max-width: 640px) {
  .rapide-l { flex: 1 1 100%; }
}

/* ============================================================
   LA BARRE DE COMMANDES : une seule ligne (2026-08-21)
   ============================================================
   Demande du fondateur, apres avoir juge le haut de page illisible : quatre rangees
   de commandes s'y empilaient sans hierarchie (le bandeau repliable et son corps, la
   rangee Trader, la rangee Periode), plus la bascule Reel/Rejeu seule dans son coin.

   Elle porte, DANS CET ORDRE : les vues, les QUATRE FIXES (execution, trader, compte,
   periode), les deux boutons d'ajout, puis les filtres et tris ajoutes.

   /!\ LES DEUX BOUTONS D'AJOUT NE BOUGENT JAMAIS. C'est la seule raison qui a fait
   preferer cet agencement au « tout au fil de la barre » : un bouton qui se deplace a
   chaque ajout se cherche des yeux a chaque fois, alors qu'un bouton immobile finit
   par se cliquer sans regarder.

   /!\ L'ordre est tenu par `order` et non par le gabarit. Les pastilles viennent d'un
   partiel qui rend d'abord les filtres PUIS leur bouton, puis les tris PUIS le leur :
   le reecrire aurait casse `pastilles.js`, qui s'attache a sa structure (ajout,
   reglage, retrait, glisser-deposer). `display: contents` fait participer ses rangees
   a la ligne, et `order` remet les boutons devant. */
.cmdbar {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  padding: 10px 12px; margin: 0 0 12px;
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: var(--card);
}
/* /!\ `display: contents` : le partiel des pastilles n'est plus une boite, ses enfants
   deviennent des elements de CETTE ligne. Sans cela il formerait un bloc a lui seul et
   on retomberait sur une seconde rangee -- exactement ce qu'on vient de supprimer. */
.cmdbar .pastilles, .cmdbar .pastilles-rangee { display: contents; }
/* Les libelles « Filtres » et « Tri » du partiel : le mot « Filtre » est deja sur le
   bouton d'ajout, et il etait ecrit DEUX FOIS sur l'ancienne page. */
.cmdbar .pastilles-l { display: none; }
.cmdbar .pastilles-ajouter { order: 1; }
.cmdbar .pastille { order: 2; }
/* Les deux phrases du partiel (une pastille ecartee, l'origine de l'etat) prennent
   une ligne entiere SOUS la barre : ce sont des avertissements, pas des commandes. */
.cmdbar .pastilles > p { order: 3; flex: 1 1 100%; margin: 4px 0 0; }
.cmdbar .vue-actions { order: 4; }
.cmdbar .cmd-raz { order: 4; }
/* /!\ LES QUATRE FIXES NE SE RENDENT PAS DEUX FOIS. Trader, version, compte et
   periode ont chacun leur commande dans la barre depuis le 2026-08-21 ; leur pastille
   les doublerait, et deux commandes pour une seule chose finissent toujours par ne
   plus dire pareil.

   /!\ `display: none` et SURTOUT PAS un retrait du gabarit : `pastilles.js` reconstruit
   l'etat depuis le DOM (`etatCourant()` lit `[data-pastille]`). Une pastille absente du
   DOM disparaitrait de l'etat au premier geste sur une AUTRE pastille -- ajouter un
   filtre effacerait le trader choisi, en silence. Elle reste donc la, invisible. */
.cmdbar .pastille[data-champ="trader"],
.cmdbar .pastille[data-champ="version"],
.cmdbar .pastille[data-champ="compte"],
.cmdbar .pastille[data-champ="periode"] { display: none; }
.cmdbar .emporter { order: 5; margin-left: auto; }
.cmd-sep {
  order: 0; flex: 0 0 auto; width: 1px; height: 20px; background: var(--border);
  margin: 0 2px;
}
/* La BASCULE Reel/Rejeu, dans la barre depuis le 2026-08-21. Elle se CLIQUE (demande
   du fondateur : « comme c'etait le cas, pour que ce soit rapide »), elle ne se deplie
   pas -- et elle n'a toujours que DEUX positions. */
.basc {
  order: 0; flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 2px; padding: 2px;
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  background: var(--background);
}
.basc a {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 11px; border-radius: var(--radius-pill);
  font-size: 12.5px; color: var(--muted-foreground); white-space: nowrap;
  text-decoration: none;
}
.basc a:hover { color: var(--foreground); }
.basc a .n { font-family: var(--font-mono); font-size: 11px; opacity: .85; }
.basc a[aria-current="page"] {
  background: rgba(180,168,255,0.16); color: var(--foreground); font-weight: 600;
}
/* Le declencheur d'une commande fixe (trader, compte, periode). */
.cmd-choix {
  order: 0; list-style: none;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 11px;
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  background: var(--background);
  font-size: 12.5px; color: var(--foreground);
  cursor: pointer; white-space: nowrap;
}
/* La RECHERCHE de la barre de commandes (2026-09-02, demande du fondateur : « la barre
   de recherche n'est pas dans le theme »). Elle etait un `input` nu, donc peinte par le
   navigateur : fond blanc, angles droits, au milieu d'une barre de pastilles sombres.

   /!\ Elle reprend les MEMES jetons que `.cmd-choix` juste au-dessus, et non des
   valeurs qui y ressemblent : la pilule, la bordure, le fond, la taille. Recopier des
   nombres approchants aurait donne deux hauteurs a un pixel pres sur la meme ligne.

   /!\ Une CLASSE, jamais le selecteur `input` : le depot a deja paye qu'un TAG
   habille aussi les voisins qu'on n'avait pas en tete. */
.cmd-cherche {
  order: 0;
  /* /!\ `appearance: none` D'ABORD. Sans lui, Chrome peint un `type="search"` avec
     son propre fond gris, ses angles droits et son encadrement en creux : la pilule
     etait ecrite, et le navigateur passait par-dessus. Vu a l'ecran le 2026-09-02. */
  -webkit-appearance: none; appearance: none;
  padding: 5px 11px;
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  background-color: var(--background);
  font-family: var(--font-sans); font-size: 12.5px; color: var(--foreground);
  outline: none; min-width: 150px;
  transition: border-color var(--motion-slow) var(--ease-out),
              box-shadow var(--motion-slow) var(--ease-out);
}
.cmd-cherche::placeholder { color: var(--muted-foreground); }
.cmd-cherche:hover { border-color: var(--border-strong); }
/* /!\ Le MEME halo que `.saisie:focus`, et non l'anneau bleu-violet du navigateur :
   deux facons d'annoncer le focus sur le meme ecran se lisent comme deux composants. */
.cmd-cherche:focus {
  border-color: rgba(180,168,255,0.5);
  background-color: rgba(180,168,255,0.06);
  box-shadow: 0 0 0 3px rgba(180,168,255,0.10);
}
/* /!\ La croix que Chrome peint dans un `type="search"` est blanche et casse la
   pilule : on la retire et on garde « Tout remettre a zero », qui fait deja ce geste. */
.cmd-cherche::-webkit-search-cancel-button { display: none; }

.cmd-choix::-webkit-details-marker { display: none; }
.cmd-choix::marker { content: ""; }
.cmd-choix:hover { border-color: var(--border-strong); }
.cmd-choix .k { color: var(--muted-foreground); }
.cmd-choix .vv { color: var(--muted-foreground); }
.cmd-choix .chev { font-size: 10px; color: var(--muted-foreground); }
/* /!\ Pose = teinte de marque. C'est ce qui distingue d'un coup d'oeil les commandes
   qui MORDENT de celles qui laissent tout passer. */
.cmd-choix[data-pose="oui"] {
  border-color: rgba(180,168,255,0.5); background: rgba(180,168,255,0.14);
}
.cmd-choix[data-pose="oui"] .v { font-weight: 600; }
/* La TEINTE DE MARQUE demandée à la main (2026-09-02 : « en reprenant le style violet
   transparent »), pour une commande qui n'a pas d'état « posé » à annoncer : le menu
   « Actions » de la ligne du titre.

   /!\ Les MÊMES valeurs que `[data-pose="oui"]` juste au-dessus, et non des valeurs
   approchantes : deux violets à deux pourcents près sur le même écran se lisent comme
   deux composants différents. */
.cmd-choix--marque {
  border-color: rgba(180,168,255,0.5); background: rgba(180,168,255,0.14);
  font-weight: 600;
}
.cmd-choix--marque:hover { background: rgba(180,168,255,0.20); }

/* Le menu d'actions de la ligne du titre. /!\ `position: relative` porté par le
   conteneur et non par le `<details>` : son panneau est en absolu, et sans ancêtre
   positionné il se serait accroché à la page, donc décalé dès qu'on défile. */
/* Les DEUX gestes de la ligne du titre : le fantome, puis le plein. */
.sec-actions-menu {
  position: relative; margin-left: auto; flex-shrink: 0;
  display: inline-flex; align-items: center; gap: 8px;
}
/* /!\ Un `<summary>` porte `display: list-item` par defaut, et la puce reapparait
   des qu'on lui donne une classe de bouton : `.btn` ne la retire pas, il ne s'en
   occupe pas. Sans ces trois lignes, « Actions » s'affichait avec un triangle noir
   collé à son icone. */
.cmd-summary { list-style: none; cursor: pointer; }
.cmd-summary::-webkit-details-marker { display: none; }
.cmd-summary::marker { content: ""; }

/* ── LE PIED D'UNE LISTE PAGINEE (2026-09-02, lot 6) ──────────────────────────
   /!\ Le COMPTE d'abord, les pages ensuite. Une liste paginee sans son total laisse
   croire que la page EST la liste. */
.pied-liste {
  display: flex; align-items: center; flex-wrap: wrap; gap: 14px;
  padding: 12px 16px;
  border-top: 1px solid var(--border);
  font-size: 12.5px; color: var(--muted-foreground);
}
.pied-liste strong { color: var(--foreground); font-weight: 600; }
.pied-par-page { margin-left: auto; display: inline-flex; align-items: center; gap: 6px; }
.pied-pages, .pied-par-page { display: inline-flex; align-items: center; gap: 4px; }
.pied-pages a, .pied-par-page a {
  padding: 3px 9px; border-radius: var(--radius-pill);
  border: 1px solid transparent; color: var(--muted-foreground);
  text-decoration: none; font-variant-numeric: tabular-nums;
}
.pied-pages a:hover, .pied-par-page a:hover {
  border-color: var(--border-strong); color: var(--foreground);
}
/* /!\ La page COURANTE porte la teinte de marque, comme une commande posee : sans
   elle, on ne sait pas ou l'on est et on reclique la meme page. */
.pied-pages a[aria-current="page"], .pied-par-page a[aria-current="page"] {
  border-color: rgba(180,168,255,0.5); background: rgba(180,168,255,0.14);
  color: var(--foreground); font-weight: 600;
}

/* L'en-tete d'une colonne TRIABLE. /!\ Aucune couleur d'accent : c'est un en-tete,
   pas un bouton, et dix-huit accents cote a cote ne designeraient plus rien. */
.tri-en-tete {
  color: inherit; text-decoration: none;
  display: inline-flex; align-items: center; gap: 4px;
}
.tri-en-tete:hover { color: var(--foreground); }
.tri-sens { color: var(--accent); font-size: 11px; }
/* /!\ Le panneau s'ouvre vers la GAUCHE. `.cmd-liste` pose `left: 0` parce que ses
   menus vivent au DEBUT d'une barre ; ancre au bord droit de l'ecran, il sortait de
   la fenetre et les intitules etaient coupes en deux. Vu a l'ecran le 2026-09-02. */
.sec-actions-menu .cmd-liste { left: auto; right: 0; }

/* /!\ `--fenetre` et JAMAIS `70vh` : `100vh` ignore le `zoom` du corps, donc a
   l'echelle 1.2 la liste se croirait autorisee a 20 % de plus que la hauteur reelle et
   deborderait de l'ecran. Un test du depot epingle cette regle, et il a attrape
   celle-ci le jour meme ou elle a ete ecrite. */
.cmd-liste {
  left: 0; right: auto; min-width: 250px;
  max-height: calc(var(--fenetre) * 0.7); overflow-y: auto;
}
.cmd-liste .menu-item[aria-current="page"] {
  background: rgba(180,168,255,0.14); font-weight: 600;
}
/* L'INTITULE d'un panneau de commande (« Sur quel trader », « Quand »).
   /!\ Il etait ECRIT dans les gabarits depuis le 2026-08-21 et n'avait AUCUNE
   regle : `.v-titre` n'existait que sous `.vue-liste`, si bien que ces titres se
   rendaient en texte courant, a la meme graisse que les entrees qu'ils coiffent.
   Releve le 2026-08-23 en montant les memes menus sur « Parrainage » et « Mes
   gains ». Meme habit que dans la liste des vues : c'est le meme role. */
.cmd-liste .v-titre {
  display: block; padding: 4px 11px 6px;
  font-family: var(--font-mono); font-size: 10px;
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  color: var(--disabled-foreground);
}
.cmd-n {
  margin-left: auto; padding-left: 12px;
  font-family: var(--font-mono); font-size: 11px; color: var(--muted-foreground);
}
/* Une VERSION, sous le trader courant : en retrait, reliee par un filet, pour qu'on
   voie qu'elle appartient a la ligne du dessus et non a la liste. */
.menu-version {
  position: relative;
  display: flex; align-items: center; gap: 9px;
  padding: 6px 11px 6px 26px; margin-left: 4px;
  border-radius: var(--radius-md);
  font-size: 12.5px; color: var(--muted-foreground); text-decoration: none;
}
.menu-version::before {
  content: ""; position: absolute; left: 12px; top: 0; bottom: 0;
  width: 1px; background: var(--border);
}
.menu-version:hover { background: var(--muted); color: var(--foreground); }
.menu-version[aria-current="page"] {
  background: rgba(180,168,255,0.10); color: var(--foreground); font-weight: 600;
}
.cmd-note {
  margin: 6px 4px 2px; padding: 0 7px;
  font-size: 11.5px; line-height: 1.5; color: var(--muted-foreground);
}
.cmd-perso {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  padding: 4px 11px 6px;
  font-size: 12px; color: var(--muted-foreground);
}
.cmd-perso input[type="date"] {
  font-family: var(--font-mono); font-size: 12px; padding: 4px 7px;
  border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--background); color: var(--foreground);
}
@media (max-width: 640px) {
  .cmd-sep { display: none; }
  .cmdbar .emporter { margin-left: 0; }
}

/* ============================================================
   EMPORTER SES DONNEES : une icone au bout de la rangee (2026-08-21)
   ============================================================
   Demande du fondateur : « la partie telechargement des donnees, je verrais plus
   une icone quelque part avec une info-bulle pour les explications ». C'etait un
   pave de six lignes en bas de la page, rendu en permanence, pour un bouton et un
   lien -- il fallait faire defiler toute la page pour l'atteindre.

   /!\ Un PANNEAU au clic, pas une bulle au survol, et c'est l'arbitrage de la
   maquette du 2026-08-21 (variante B, validee). Une bulle s'efface des qu'on bouge
   la souris : elle ne peut porter ni le lien « Que veut dire chaque colonne ? »
   (on ne clique pas dans ce qui disparait), ni l'AVERTISSEMENT de plafond -- et
   `partials/aide.html` dit deja la regle : « ce qui AVERTIT ne passe jamais ici ».
   Un client qui batit ses regles sur un historique qu'il croit complet tire des
   conclusions fausses sans jamais pouvoir s'en apercevoir.

   /!\ Le meme `<details class="menu">` que les vues et les gestes rares d'un
   trader : il s'ouvre, se ferme et se prend au clavier sans une ligne de script, et
   `menu.js` le referme quand on clique ailleurs. */
.emporter { flex: 0 0 auto; }
/* Sous 640px la rangee se recoupe et l'icone tombe seule sur sa ligne : elle se cale
   contre le bord droit plutot que de flotter a gauche, sous le declencheur des vues. */
@media (max-width: 640px) { .emporter { margin-left: auto; } }
.emporter-bouton {
  list-style: none;
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto; width: 34px; height: 34px; padding: 0;
  background: var(--card);
  border: 1px solid var(--border); border-radius: 50%;
  color: var(--muted-foreground); cursor: pointer;
  transition: color var(--motion-fast) var(--ease-out),
              border-color var(--motion-fast) var(--ease-out);
}
/* La fleche que le navigateur ajoute a tout `<summary>` doublerait l'icone. */
.emporter-bouton::-webkit-details-marker { display: none; }
.emporter-bouton::marker { content: ""; }
.emporter-bouton:hover, .emporter-bouton:focus-visible {
  color: var(--accent); border-color: var(--accent-ring-strong);
}
.emporter-bouton svg { width: 16px; height: 16px; }
.emporter-panneau { min-width: 320px; max-width: 340px; padding: 14px 15px; gap: 0; }
.emporter-panneau p {
  margin: 0 0 9px; font-size: 12.5px; line-height: 1.55; color: var(--muted-foreground);
}
.emporter-panneau p strong { color: var(--foreground); }
.emporter-titre {
  margin: 0 0 7px; font-size: 13.5px; font-weight: 600; color: var(--foreground);
}
.emporter-panneau .btn { margin: 4px 0 10px; }
/* /!\ L'avertissement de plafond prend `--warning-text`, la meme convention que
   `.vue-modifiee` : il previent, il n'alarme pas. */
.emporter-panneau .emporter-plafond { color: var(--warning-text); }
/* La BARRE A PASTILLES (2026-08-18).
   ============================================================
   Chaque filtre est une pastille : une poignee pour la deplacer, un libelle, sa valeur,
   une croix pour la retirer. Les trois predefinis (trader, periode, execution) n'ont
   AUCUN statut a part -- ils se retirent comme les autres, et c'est la demande.

   /!\ La poignee et la croix restent VISIBLES, pas revelees au survol. Une commande qui
   n'apparait qu'au survol n'existe pas au doigt, et le fondateur consulte aussi sur
   tablette. C'est le defaut qu'on a corrige sur `.filter-chip`, dont la croix etait a
   `opacity: 0`.

   /!\ La pastille est un `<span>` avec `tabindex`, pas un `<button>` : elle CONTIENT un
   bouton (la croix), et un bouton dans un bouton est du HTML invalide -- le clic partirait
   au mauvais endroit. Meme piege que la rangee d'un trade, le meme jour. */
.pastilles { display: flex; flex-direction: column; gap: 10px; }
.pastilles-rangee { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.pastilles-l {
  flex: 0 0 auto; min-width: 52px;
  font-family: var(--font-mono); font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  color: var(--muted-foreground);
}
.pastille {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 6px 5px 8px;
  background: rgba(180,168,255,0.06);
  border: 1px solid rgba(180,168,255,0.22);
  border-radius: var(--radius-md);
  font-size: 12.5px; line-height: 1.4; cursor: pointer;
  transition: background var(--motion-fast) var(--ease-out),
              border-color var(--motion-fast) var(--ease-out);
}
.pastille:hover { background: rgba(180,168,255,0.12); border-color: rgba(180,168,255,0.45); }
.pastille:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
.pastille-poignee {
  color: var(--disabled-foreground); cursor: grab;
  letter-spacing: -2px; font-size: 12px;
}
.pastille-champ { font-weight: 600; color: var(--foreground); }
.pastille-op { color: var(--muted-foreground); font-size: 11px; }
.pastille-valeur { color: var(--accent); font-weight: 500; }
.pastille-retirer {
  width: 18px; height: 18px; padding: 0; border: 0; border-radius: 3px;
  background: transparent; color: var(--disabled-foreground);
  font-size: 14px; line-height: 1; cursor: pointer;
}
.pastille-retirer:hover { background: rgba(214,90,90,0.12); color: var(--error); }
/* /!\ LA PASTILLE DE TRI A VECU MOINS DE TROIS HEURES (2026-08-21). Elle venait de
   gagner sa teinte neutre et son mot « TRI », sur remarque du fondateur (« on ne
   distingue pas trop les filtres des tris ») ; il a ensuite retire le tri
   ENTIEREMENT le meme soir. Le style part avec lui : une regle qui ne peint plus rien
   est un piege pour qui la lira ensuite. */

/* La pastille qu'on deplace, et le trou ou elle atterrira. */
.pastille--attrapee { opacity: .4; }
.pastille--cible { outline: 2px dashed var(--accent); outline-offset: 2px; }
.pastilles-ajouter { white-space: nowrap; }

/* Le PANNEAU de reglage d'une pastille (2026-08-18).
   /!\ Il n'avait AUCUN style : `.pop-l` et `.pop-pied` venaient de la maquette et
   n'existaient pas dans la charte. Resultat vu a l'ecran par le fondateur -- « Retirer »
   sous « Appliquer », des champs deux fois trop hauts, des libelles sans hierarchie.
   Une maquette qui invente des classes doit les apporter avec elle. */
.panneau-pastille { min-width: 240px; max-width: 320px; }
/* Le titre d'une famille dans le selecteur de proprietes. /!\ Il est pose dans la liste
   des boutons : sans `display:block` il s'alignerait sur eux et se lirait comme une
   entree cliquable qui ne repond pas. */
.filter-popover .picker-groupe {
  display: block; padding: 9px 8px 4px;
  font-family: var(--font-mono); font-size: 10px;
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  color: var(--disabled-foreground);
}
.filter-popover .picker-groupe:first-child { padding-top: 2px; }
/* Une propriete DEJA posee dans la barre. /!\ Elle reste cliquable -- deux bornes sur
   le meme prix s'ecrivent en deux pastilles -- donc elle est ATTENUEE, jamais grisee :
   un bouton grise annonce qu'il ne repond pas. Le point remplace le texte « deja pose »,
   retire le 2026-08-18 : il doublait la longueur de la ligne pour redire ce qu'un coup
   d'oeil a la barre montre deja. */
.filter-popover .picker-item[data-deja] {
  /* /!\ `flex` ici SEULEMENT, pas sur `.picker-item` : cette classe sert aussi les
     selecteurs d'administration, et passer la base en flex les toucherait tous. */
  display: flex; align-items: center; gap: 8px;
  color: var(--muted-foreground);
}
.filter-popover .picker-item[data-deja]::after {
  content: ""; width: 5px; height: 5px; margin-left: auto; flex: 0 0 auto;
  border-radius: 50%; background: var(--accent); opacity: 0.75;
}
.filter-popover .picker-item[data-deja]:hover { color: var(--foreground); }
/* Ce qu'une pastille PAS ENCORE REGLEE dit d'elle-meme. /!\ En italique et attenue :
   ce n'est pas une valeur, c'est ce qu'elle attend. Une pastille qui afficherait « a
   regler » du meme ton que « 200 » se lirait comme une valeur nommee « a regler ». */
.pastille-valeur--attente { color: var(--muted-foreground); font-style: italic; }
.panneau-pastille .pop-l {
  display: block;
  font-family: var(--font-mono); font-size: 10.5px;
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  color: var(--muted-foreground); margin: 0 0 5px;
}
.panneau-pastille .pop-l + * { margin-bottom: 10px; }
.panneau-pastille .saisie--compacte {
  width: 100%; padding: 7px 10px; font-size: 13px; border-radius: var(--radius-md);
}
/* Les deux commandes sur UNE ligne, separees : c'est leur chevauchement qui a fait
   dire au fondateur que ce n'etait « vraiment pas esthetique ». */
.panneau-pastille .pop-pied {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border);
}
/* /!\ « Retirer » reste discret et ROUGE au survol seulement : c'est un geste
   destructeur pose a cote du geste courant, et il ne doit pas attirer le clic. */
.panneau-pastille .pop-pied .action { color: var(--muted-foreground); }
.panneau-pastille .pop-pied .action:hover { color: var(--error); }

/* Les RANGEES de la barre de filtres (2026-08-18). Trois familles, trois rangees :
   le perimetre (change les chiffres), la selection (les recalcule sur une partie), les
   outils de liste (n'en changent aucun). Un trait les separe, discret : ce n'est pas
   une frontiere, c'est un souffle. */
.filtres--rangee { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }

/* Le TIROIR des filtres rares (compte, modele). `<details>` natif : il s'ouvre sans
   JavaScript, et le gabarit l'ouvre d'office quand un des filtres qu'il cache est pose
   -- sinon un client verrait ses chiffres resserres sans voir par quoi. */
.tiroir { display: inline-block; }
.tiroir-tete {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 13px; border-radius: var(--radius-pill);
  border: 1px solid var(--border); background: transparent;
  font-size: 13px; color: var(--muted-foreground);
  cursor: pointer; list-style: none; white-space: nowrap;
}
.tiroir-tete::-webkit-details-marker { display: none; }
.tiroir-tete:hover { color: var(--foreground); border-color: var(--border-strong); }
.tiroir[open] .tiroir-tete { color: var(--foreground); border-color: var(--border-strong); }
.tiroir-corps { margin-top: 10px; }

/* Les COLONNES PROPOSEES du filtre avance (2026-08-18, demande du fondateur).
   /!\ Le bouton seul n'annoncait rien de ce qu'il ouvre : il fallait cliquer pour
   decouvrir qu'on peut filtrer sur le prix d'entree ou l'amplitude. Les pastilles sont
   un RACCOURCI vers les sept colonnes, pas un remplacement du panneau -- lui seul porte
   la recherche de colonne et les groupes de conditions. */
.colonnes-proposees { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
/* /!\ Les pastilles portent une BORDURE. Sans elle, `.action` les rend en texte nu :
   sept libelles alignes se lisaient comme une phrase (« Instrument Contrats Prix
   d'entree… »), et rien ne disait qu'on peut cliquer dedans. */
.action--colonne {
  font-size: 12.5px; padding: 5px 11px;
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  color: var(--muted-foreground); background: transparent;
}
.action--colonne:hover { color: var(--foreground); border-color: var(--border-strong); }
.filtre--avance { align-items: flex-start; }
@media (max-width: 720px) {
  .filtre--avance { flex-direction: column; gap: 6px; }
}

/* La VUE, dans la tete de la barre de filtres. Elle prend la place du libelle
   « Mes filtres » : la vue EST les filtres, la nommer deux fois serait du bruit. */
.vue-bouton {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 10px 5px 11px;
  background: rgba(180,168,255,0.09);
  border: 1px solid rgba(180,168,255,0.30);
  border-radius: var(--radius-pill);
  font-size: 13px; font-weight: 600; color: var(--foreground);
  cursor: pointer; white-space: nowrap;
}
.vue-bouton:hover { background: rgba(180,168,255,0.16); border-color: rgba(180,168,255,0.5); }
.vue-bouton .chev { font-size: 10px; color: var(--muted-foreground); }
/* La PASTILLE « modifiee » : un point, pas un mot en majuscules. Elle dit qu'il y
   a quelque chose a enregistrer, elle n'alarme pas. */
/* /!\ TEXTE vs POINT (petit point de la revue finale, 2026-08-20) : le texte prend
   `--warning-text`, le point prend `--warning`, comme `.barre-enregistrement
   [data-modifie="oui"] .etat` un peu plus bas dans ce fichier -- la meme convention.
   Portait avant un repli `#E0B341` MORT (`--warning` est toujours defini dans
   `:root`, il n'y a pas d'autre theme) et d'une teinte DIFFERENTE de `--warning`
   lui-meme (`#FBBF24`) : un repli qui ne sert jamais mais aurait menti sur la
   couleur s'il avait jamais servi. */
/* LE BANDEAU DES POSITIONS OUVERTES (2026-08-22).
   Il se lit AVANT les chiffres et hors de tout filtre : une position ouverte est de
   l'argent en risque. La teinte d'accent le detache sans crier -- ce n'est pas une
   alerte, c'est un etat. */
.positions-ouvertes {
  margin: 0 0 16px;
  border-color: rgba(180, 168, 255, 0.35);
  background: rgba(180, 168, 255, 0.06);
}
.positions-titre {
  display: flex; align-items: center; gap: 7px;
  margin: 0 0 10px;
  font-size: 13px; font-weight: 600; letter-spacing: 0;
  color: var(--foreground);
}
.positions-titre svg { color: var(--accent); }
/* /!\ La liste DEFILE plutot que d'etre tronquee. Quatre lignes visibles, le reste
   atteignable : un plugin qui tombe souvent laisse des dizaines de positions ouvertes,
   et c'est justement ce client-la qui a besoin de la liste ENTIERE. Tronquer aurait
   cache de l'argent en risque derriere un clic. */
.positions-liste { max-height: 176px; overflow-y: auto; }
.position-ligne {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 7px 0;
  border-top: 1px solid var(--border);
}
.position-ligne:first-of-type { border-top: 0; }
.position-quoi {
  display: inline-flex; align-items: center; gap: 8px;
  color: var(--foreground); text-decoration: none;
}
.position-quoi:hover strong { text-decoration: underline; }
.position-qty { font-size: 12.5px; color: var(--muted-foreground); }
/* LE COMPTE sur lequel la position tourne (2026-08-23). En chasse fixe et sur sa propre
   surface, pas fondu dans le detail : c'est le seul element de la ligne qui dit si
   l'argent en risque est reel ou simule, et on le cherche a l'oeil, sans lire. */
.position-compte {
  font-family: var(--font-mono); font-size: 11px; line-height: 1;
  letter-spacing: var(--tracking-mono-data);
  color: var(--muted-foreground);
  background: var(--muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 4px 7px;
  white-space: nowrap;
}
.position-detail {
  font-size: 12px; color: var(--muted-foreground);
  font-variant-numeric: tabular-nums;
}
/* /!\ `margin-left:auto` pousse le geste au bord droit, comme partout ailleurs dans
   cette page. Il passe a la ligne sur mobile grace au `flex-wrap` de la rangee. */
.position-geste { margin: 0 0 0 auto; }

/* Le POINT de vue RETOUCHEE (2026-08-22). Il remplace trois boutons en toutes lettres
   dans la barre : le fondateur les trouvait trop encombrants, et l'etat qu'ils
   annoncaient tient dans six pixels. Les gestes eux-memes sont en tete du menu.
   /!\ `::after` et pas `::before` : le chevron ferme le bouton, le point doit rester
   colle au NOM de la vue, sinon il se lit comme une puce du chevron. */
.vue-bouton[data-modifiee] { border-color: rgba(251, 191, 36, 0.45); }
.vue-bouton[data-modifiee] .chev { order: 2; }
.vue-bouton[data-modifiee]::after {
  content: ""; order: 1;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--warning); flex: 0 0 auto;
}

/* La COCHE du menu a selection multiple (le filtre Compte, 2026-08-22).
   /!\ Largeur FIXE, meme vide : sans elle, les comptes coches et non coches
   s'aligneraient sur deux colonnes differentes et la liste se lirait en escalier. */
.cmd-liste .coche {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; flex: 0 0 14px; color: var(--accent);
}

/* Une rangee de SAISIE dans un menu : champ + bouton sur UNE ligne (2026-08-22).
   Remplace un pave de trois hauteurs de ligne (titre, champ, bouton pleine largeur)
   pour les gestes renommer et creer une vue. */
.menu-item--saisie {
  display: flex; flex-direction: row; align-items: center; gap: 6px;
}
.menu-item--saisie .saisie { flex: 1 1 auto; min-width: 0; }
.menu-item--saisie .btn { flex: 0 0 auto; padding: 4px 8px; }
.menu-item--saisie .menu-item-pico {
  display: inline-flex; flex: 0 0 auto; color: var(--muted-foreground);
}

.vue-modifiee {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11.5px; color: var(--warning-text); font-weight: 500;
}
.vue-modifiee::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--warning);
}
.vue-actions { display: inline-flex; align-items: center; gap: 14px; margin-left: auto; }
/* La liste des vues, ouverte. Reprend `.menu-panneau`, en plus large et ancree a
   GAUCHE : elle descend d'un bouton pose a gauche, pas d'un bouton d'icone.

   /!\ PAS de `position: static` ici (corrige le 2026-08-20, tache 9) : cette regle
   se charge APRES `.menu-panneau` dans la feuille, donc un `position: static` posait
   ici ecrasait son `position: absolute` -- le menu poussait alors toute la page vers
   le bas au lieu de se superposer. Cette valeur venait d'une maquette figee ou elle
   etait une commodite pour montrer tous les etats sur une page qui ne defilait pas ;
   recopiee au caractere pres dans la charte, elle est devenue un bug a l'ecran. */
.vue-liste {
  left: 0; right: auto;
  width: 360px; min-width: 0; margin: 8px 0 0 26px;
}
.vue-liste .menu-item[aria-selected="true"] { background: var(--muted); }
.vue-liste .v-titre {
  display: block; padding: 4px 11px 6px;
  font-family: var(--font-mono); font-size: 10px;
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  color: var(--disabled-foreground);
}
.vue-liste .menu-item .v-def {
  margin-left: auto; font-size: 10px; font-family: var(--font-mono);
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  color: var(--accent); font-weight: 400;
}

.perimetre-corps {
  margin-top: 8px; padding: 14px 16px;
  background: var(--card);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
}
.perimetre[data-replie="oui"] .perimetre-corps { display: none; }
/* /!\ Sans JavaScript, rien ne replie : la barre reste ouverte et la page marche.
   Le script pose `data-replie` des qu'il tourne. */

/* ============================================================
   Une page de reglages EN ONGLETS (2026-08-18)
   ============================================================
   Remarque du fondateur, le jour ou la fiche d'un trader est passee en onglets :
   « chaque onglet a un encadre et l'encadre est disproportionne ».

   Il avait raison, et pour deux raisons qui se cumulent :

   /!\ 1. UNE BOITE DANS UNE BOITE. Tant que les sept parties se suivaient sur une
   page, le cadre les SEPARAIT — c'etait son travail. Seule sous une barre
   d'onglets, la partie n'a plus rien dont se separer : le cadre ne fait plus que
   redessiner un contour a l'interieur d'un contour.

   /!\ 2. LE VIDE A GAUCHE. La colonne d'explication fait 300 points ; quatre
   parties sur huit n'y mettaient qu'un titre de trois mots. Empilees, elles se
   compensaient ; seule a l'ecran, « Identite et marche » donnait 250 points a
   moitie vides face a deux champs. Le cadre le RENDAIT VISIBLE, il ne le causait
   pas — d'ou les quatre explications ecrites le meme jour dans le gabarit. Retirer
   le cadre sans les ecrire n'aurait fait que cacher le trou.

   /!\ Portee `--onglets` et pas `--reglages` : les sept AUTRES pages de reglages de
   l'application (profil, facturation, admin…) empilent toujours leurs parties et
   ont toujours besoin du cadre qui les separe. */
.wrap--reglages.wrap--onglets .encadre[data-section] {
  padding: 6px 0 0;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
/* Sans cadre, c'est l'espace qui doit tenir la partie a distance de la barre
   d'onglets, sinon le premier champ lui colle dessous. */
.wrap--reglages.wrap--onglets .encadre[data-section] > .encadre-titre { margin-top: 0; }

/* /!\ La colonne d'explication a gauche est ANNULEE dans une page a onglets. C'est
   elle que le fondateur a rejetee le 2026-08-18 : « quand on clique sur un reglage,
   le texte a gauche et les champs a droite, ca cree un desequilibre ». Elle vaut
   toujours pour les sept autres pages de reglages, qui empilent leurs parties — ici
   chaque partie occupe seule l'ecran, et un titre de trois mots face a deux champs
   laissait une moitie d'ecran vide. */
/* /!\ Les DEUX classes du conteneur sont nommees, et ce n'est pas du zele : la
   regle a defaire (`.wrap--reglages .encadre > *:not(…):not(…)`) compte quatre
   classes, une de plus qu'une version courte de celle-ci. Elle aurait gagne, et le
   contenu serait reste decale de 356 points vers la droite — avec une colonne vide
   a gauche, soit exactement le defaut qu'on corrige. */
.wrap--reglages.wrap--onglets .encadre[data-section] > .encadre-titre,
.wrap--reglages.wrap--onglets .encadre[data-section] > .encadre-sous {
  float: none; width: auto; max-width: none;
}
.wrap--reglages.wrap--onglets .encadre[data-section] > *:not(.encadre-titre):not(.encadre-sous),
.wrap--reglages.wrap--onglets .encadre[data-section] > * { margin-left: 0; }
/* /!\ `.encadre + .encadre { margin-top: 16px }` SÉPARE deux encadrés qui se
   suivent — c'est son travail quand ils sont empilés. Dans une page à onglets, un
   seul est visible : cette marge ne separe plus rien, elle DÉCALE. Et comme elle ne
   s'applique qu'aux sections precedees d'une autre dans le DOM, l'ecart sous la
   barre d'onglets valait 20 px ou 37 px SELON LA POSITION de l'onglet — remarque du
   fondateur le 2026-08-18 : « les espaces entre l'onglet et le texte different d'un
   onglet a l'autre ». Une seule source pour cet ecart : la marge basse de la barre. */
.wrap--onglets [data-section] + [data-section],
.wrap--onglets [data-section] { margin-top: 0; }

/* ============================================================
   Les SOUS-GROUPES d'un onglet (2026-08-18)
   ============================================================
   Maquette « onglet C », choisie par le fondateur. Les champs se rangent par
   QUESTION — « ou je sors », « combien je mets », « ou j'entre » — et chaque
   groupe porte son titre et sa phrase courte.

   /!\ Ce que ca resout, et que ni le chapeau ni les champs nus ne resolvaient :
   « Strategie » compte quatorze reglages. En liste, c'est un rouleau ; en grille
   nue, c'est quatorze etiquettes sans hierarchie. Groupes, ce sont quatre
   questions.

   /!\ Et une partie a DEUX champs ne fait qu'un groupe : la grille se remplit
   d'elle-meme, rien n'est jamais a moitie vide. C'est le defaut d'origine.

   /!\ Le groupe est plus discret que `.encadre` — bordure simple, fond a peine
   marque, pas d'ombre. Il structure, il ne se met pas en avant : sinon on aurait
   remplace une boite dans une boite par quatre. */
/* Le CHAPEAU d'un onglet : la phrase qui vaut pour tous ses groupes, quand il y en
   a une.

   /!\ PLEINE LARGEUR, sur decision du fondateur (2026-08-18) : « les textes en
   en-tete sur certains onglets ne prennent pas toute la largeur alors que certains
   la prennent, fais qu'ils la prennent tous ». Le premier jet la bornait a 74
   caracteres, par souci de confort de lecture ; ce qui se voyait a l'ecran, c'est
   que les onglets ne se ressemblaient pas — « Regles » et « Notifications », dont
   l'intro n'est pas un `.chapeau`, couraient sur toute la largeur, les six autres
   s'arretaient au tiers. Une regle de confort qui produit une incoherence visible
   coute plus qu'elle ne rapporte. */
.chapeau { margin: 0 0 20px; font-size: 13.5px; line-height: 1.65;
           color: var(--muted-foreground); }
.chapeau strong { color: var(--foreground); font-weight: 500; }
.chapeau code { font-family: var(--font-code); font-size: .92em; }
/* /!\ Un lien dans un chapeau DOIT se voir. `admin_base.html` pose
   `a { color: inherit; text-decoration: none }` sur toute l'application : sans
   cette règle, un lien posé dans un chapeau prend le gris du texte et devient
   invisible — défaut relevé à l'écran le 2026-08-22 sur « Modifier mes valeurs
   de départ », que rien ne distinguait de la phrase qui le portait. */
.chapeau a { color: var(--accent); text-decoration: none; }
.chapeau a:hover { text-decoration: underline; }

/* /!\ Le nombre de colonnes est DECIDE, grille par grille, et n'est plus laisse a
   `auto-fit`. Avec `auto-fit`, c'est la LARGEUR DE LA FENETRE qui choisissait :
   la grille de quatre cartes de « Entrer et sortir » rendait 3 + 1, la quatrieme
   seule sur sa ligne avec un grand vide a droite, et cet orphelin apparaissait ou
   disparaissait selon l'ecran du client. Trois colonnes est le DEFAUT ; une grille
   qui n'a pas de quoi les remplir declare `.groupes--2` ou `.groupes--1`. C'est le
   gabarit qui sait combien de cartes il pose, pas la feuille de style. */
.groupes {
  display: grid; gap: 16px;
  /* /!\ `minmax(0, 1fr)` et JAMAIS `1fr` : sans le zero, une carte qui porte un
     tableau ou un champ large refuse de retrecir et deborde de la grille. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  /* /!\ `stretch` et non `start` : avec `start`, les cartes d'une meme ligne
     gardent chacune sa hauteur et leurs bords bas font un escalier. C'est le
     reproche numero un du fondateur, « il y a des encadres qui ne sont pas
     alignes ». Etirees, elles se terminent toutes sur la meme ligne. */
  align-items: stretch;
}
.groupes--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.groupes--1 { grid-template-columns: minmax(0, 1fr); }

/* /!\ Les bornes tiennent compte du `zoom: 1.1` du corps ET de la barre laterale :
   une fenetre de 1280 points reels ne laisse que ~884 points a la grille
   (1280 / 1,1 - 200 de barre - 80 de marges), pas 1280. Trois colonnes n'y
   tiendraient qu'a ~295 points chacune, sous le confort de 330 points de la
   maquette d'origine. D'ou la bascule a 1400 et non a 1100.
   /!\ Chaque bloc REDECLARE `.groupes--1` : les deux selecteurs ont la meme
   specificite, et sans ce rappel la regle du media, ecrite plus bas, gagnerait et
   rendrait deux colonnes a une grille qui n'a qu'une carte. */
@media (max-width: 1400px) {
  .groupes { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .groupes--1 { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 760px) {
  .groupes, .groupes--2, .groupes--1 { grid-template-columns: minmax(0, 1fr); }
}
.groupe {
  padding: 18px 20px 20px;
  background: rgba(255,255,255,0.02);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.groupe-titre {
  font-family: var(--font-sans); font-size: 14px; font-weight: 600;
  color: var(--foreground); margin: 0 0 4px;
}
.groupe-sous {
  margin: 0 0 16px;
  font-size: 12.5px; line-height: 1.55; color: var(--muted-foreground);
}
.groupe-sous:last-child { margin-bottom: 0; }
.groupe-sous strong { color: var(--foreground); font-weight: 500; }
/* Une phrase de carte qui n'OUVRE pas la carte (2026-08-29).

   `.groupe-sous` n'a qu'une marge BASSE, et c'est juste : elle suit presque
   toujours le titre, qui pose deja l'air au-dessus. L'application en pose
   TRENTE-SEPT (`abonnement.html` 3, `user_profile.html` 13,
   `user_trader_edit.html` 21, compte du 2026-08-29). Trente-cinq suivent bien
   leur titre. Les DEUX autres :

     - le pave de « Site public » suit une CASE A COCHER : rien ne l'en ecartait,
       il lui collait, et c'est ce modificateur qui l'en ecarte ;
     - le lien vers la console d'Anthropic (`user_trader_edit.html`) suit une
       autre `.groupe-sous`, et se RAPPROCHE d'elle expres, par un
       `margin-top:-8px` pose a la main : la phrase et son lien se lisent comme
       un seul bloc. Defaut inverse, remede inverse ; ce modificateur-ci ne la
       concerne pas.

   /!\ La marge de `.groupe-sous` n'est PAS touchee : la changer deplacerait les
   trente-six autres, dont aucune n'a de defaut. C'est un modificateur qui porte
   le cas particulier, et il porte un nom a lui : une classe partagee par deux
   usages est le piege le plus cher de ce depot.

   /!\ IL NE SE POSE QUE SUR UNE `.groupe-sous`. `.case` est en `inline-flex` et
   n'a aucune marge, donc l'ecran des reglages ecarte deux de ses cases par un
   `style=` pose a la main (`user_trader_edit.html`, `margin-top:16px` et
   `margin-bottom:16px`). Ce ne sont PAS des candidats a `--suite` : le poser sur
   une `.case` serait mettre le modificateur d'un objet sur un AUTRE objet, la
   classe partagee que le paragraphe ci-dessus interdit, et sur la seconde ce
   serait de toute facon sans effet, `--suite` n'ayant qu'un `margin-top`. Ils
   restent en ligne parce qu'ils sont DEUX, uniques, a deux valeurs opposees :
   une classe par emploi ne serait que du bruit. Le jour ou l'un des deux se
   repete, le remede est un modificateur de LEUR objet (`.case--…`), jamais
   celui-ci. */
.groupe-sous--suite { margin-top: 16px; }
/* Un groupe qui ne porte qu'un ou deux champs les empile : ses colonnes sont deja
   celles de la grille des groupes. */
.groupe .champs {
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 16px;
}
/* Un groupe LARGE, pour ce qui ne se coupe pas en deux (un selecteur suivi de ses
   deux montants deduits).
   /!\ Il prend toute la ligne, donc il se pose en FIN de grille SEULEMENT. Pose au
   milieu, il coupe le flux : les cartes qui le suivent repartent d'une ligne neuve
   et rouvrent plus bas l'orphelin qu'on vient de fermer. */
.groupe--large { grid-column: 1 / -1; }
.groupe--large .champs { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }

/* UNE LIGNE DE L'EDITEUR DES DONNEES : une seance, un graphique, une lecture.
   /!\ ELLE PORTE AUSSI `.champs`, QUI EST UNE GRILLE a colonnes de 240 px minimum —
   et c'est ce qui la cassait : le petit bouton « x » occupait une cellule entiere et
   s'etalait en BARRE GRISE sur sa propre ligne, sous chaque graphique. Vu en regardant
   l'ecran, pas en lisant le HTML. Une ligne d'objets se lit de gauche a droite : c'est
   une rangee souple, pas une grille.
   /!\ `align-items: flex-end` pour que les champs s'alignent par le BAS : ceux qui
   portent un libelle au-dessus sont plus hauts que les autres. */
.champs.ligne-objet {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 10px;
}
.champs.ligne-objet .saisie { width: auto; min-width: 0; }
.champs.ligne-objet > .liste { flex: 0 0 auto; }

/* Le bouton qui RETIRE une ligne : discret, carre, au bout de sa rangee. */
.oter-ligne {
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  border: 1px solid var(--bord, rgba(255, 255, 255, .12));
  background: transparent;
  color: var(--texte-doux, #8b95a7);
  cursor: pointer;
  line-height: 1;
}
.oter-ligne:hover { color: var(--danger, #ff6b6b); border-color: currentColor; }

/* Le bouton qui AJOUTE une ligne : un lien d'action, pas un pave. */
.ajouter-ligne {
  margin-top: 6px;
  padding: 6px 12px;
  border-radius: 8px;
  border: 1px dashed var(--bord, rgba(255, 255, 255, .18));
  background: transparent;
  color: var(--texte-doux, #8b95a7);
  cursor: pointer;
  font-size: 13px;
}
.ajouter-ligne:hover { color: var(--texte, #e6e9ef); border-style: solid; }

/* Un REGLAGE DE LECTURE dans l'editeur des donnees : son libelle au-dessus de son
   champ, compact, pour qu'une ligne en porte plusieurs sans se casser.
   /!\ La classe `.mini` etait deja posee dans le gabarit et n'existait NULLE PART
   ici — elle habillait donc deux champs, en apparence par hasard. Depuis le
   2026-09-05 chaque reglage du catalogue a son champ, donc une ligne peut en porter
   six : sans regle, ils s'alignaient bout a bout sans libelle lisible.
   /!\ `[hidden]` porte `display:none !important` plus haut dans ce fichier, donc un
   reglage que le type ne porte pas disparait vraiment — c'est ce qui evite qu'une
   ligne de lecture affiche vingt et un champs. */
.mini {
  display: inline-flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
  color: var(--texte-doux, #8b95a7);
  white-space: nowrap;
}

/* ============================================================
   La BARRE D'ENREGISTREMENT (2026-08-17)
   ============================================================
   Demande du fondateur : « c'est un peu flou, le fait d'enregistrer les parametres
   a un endroit ». Le bouton etait au milieu d'une page de six cents lignes de
   champs, et on continuait de defiler apres lui. Il suit maintenant le lecteur.

   /!\ `position: fixed`, PAS `sticky`, et c'est mesure. Ici c'est la FENETRE qui
   defile (`.main` n'a ni hauteur ni `overflow`), et `.main` porte un
   `padding-bottom` de 64 px : une barre collante cesserait de coller 64 px avant le
   bas de page et se mettrait a defiler avec le contenu sur le dernier ecran.

   /!\ Elle est posee DANS le formulaire, malgre les apparences. Un element fixe se
   voit au meme endroit ou qu'il soit dans le document, et y etre lui donne deux
   choses gratuitement : son bouton envoie le formulaire sans attribut `form`, et le
   champ « motif » qu'elle porte part avec le reste.

   /!\ Le decalage a gauche suit la barre laterale (200 px), qui disparait sous
   1024 px comme partout ailleurs dans l'application. Meme borne, une seule requete
   media.

   /!\ La fenetre de confirmation (`dialog.verre`) vit dans la couche superieure du
   navigateur : elle passe au-dessus sans qu'on ait a arbitrer un `z-index`.

   /!\ La barre doit rester DANS `.wrap`, jamais enfant direct de `.main`.
   `admin_base.html` pose `.main > * { position: relative }` avec la MEME specificite
   que la regle ci-dessous, dans un `<style>` charge APRES cette feuille : en enfant
   direct de `.main`, la barre redeviendrait `relative` et se contenterait de flotter
   en bas du flux. Le defaut ne se verrait meme pas sur une page courte.
   Bonus de ce placement : `.wrap` porte `z-index: 1`, donc un contexte
   d'empilement ; la barre passe automatiquement SOUS la barre d'application et sous
   le menu lateral mobile, quel que soit son propre `z-index`.

   /!\ Le jour ou quelqu'un pose `transform`, `filter`, `backdrop-filter` ou
   `contain` sur `.main` ou `.wrap`, la barre s'ancrera a ce conteneur au lieu de
   l'ecran et cessera d'etre fixe. Aucun test HTML n'attrape cette casse.

   /!\ Sur un telephone, ouvrir le clavier virtuel pour saisir le motif peut masquer
   la barre : le navigateur ne recalcule le `fixed` qu'en fin de geste. Le motif est
   facultatif, l'enregistrement ne depend pas de lui. Les clients travaillent sur le
   poste Windows qui fait tourner NinjaTrader ; si le tactile devient un usage reel,
   c'est ici qu'il faudra sortir le motif de la barre. */
.barre-enregistrement {
  position: fixed;
  left: 200px; right: 0; bottom: 0;
  z-index: 45;
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 12px 40px;
  padding-bottom: calc(12px + env(safe-area-inset-bottom));
  background: rgba(14, 16, 24, 0.94);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  border-top: 1px solid var(--border-strong);
  box-shadow: 0 -16px 44px -18px rgba(0,0,0,0.75);
}
@media (max-width: 1023px) {
  .barre-enregistrement { left: 0; padding: 10px 16px; }
}
/* La barre ne se montre QUE s'il y a quelque chose a enregistrer (2026-08-18,
   demande du fondateur). Elle occupait 132 points en bas de chaque ecran de
   reglages, en permanence, pour dire « tout est enregistre » — c'est-a-dire pour
   ne rien demander.

   /!\ `data-cachable` est pose par `enregistrer.js`, JAMAIS ecrit dans le gabarit.
   Sans JavaScript, rien ne detecte une modification : la barre doit alors rester
   visible en permanence, sinon le formulaire n'a plus de bouton pour l'envoyer.
   C'est le meme raisonnement que le repli du perimetre de « Mes trades » et que
   les onglets d'un formulaire — le script RETIRE, il ne donne jamais l'acces. */
.barre-enregistrement[data-cachable] { display: none; }
.barre-enregistrement[data-cachable][data-modifie="oui"] { display: flex; }
/* La reserve sous la page : sans elle, la barre recouvre le dernier champ. Elle est
   posee sur `.main` et non sur la barre, parce que c'est la PAGE qui doit rendre de
   la place, pas la barre qui doit en prendre moins.
   /!\ Elle suit la barre : reserver 132 points sous une barre absente laisserait un
   trou permanent au bas de chaque page de reglages — on aurait deplace le probleme
   au lieu de le resoudre. */
body:has(.barre-enregistrement:not([data-cachable])) .main,
body:has(.barre-enregistrement[data-modifie="oui"]) .main { padding-bottom: 132px; }

/* L'etat. Muet quand rien n'a change, ocre des qu'un champ bouge : c'est le meme
   ocre que partout ailleurs pour « attention, lis ca » (`--warning-text`). */
.barre-enregistrement .etat {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 13px; color: var(--muted-foreground);
  margin-right: auto;
}
.barre-enregistrement .etat::before {
  content: ""; width: 7px; height: 7px; flex: 0 0 auto;
  border-radius: 50%; background: var(--disabled-foreground);
  transition: background var(--motion-base) var(--ease-out);
}
.barre-enregistrement[data-modifie="oui"] .etat { color: var(--warning-text); }
.barre-enregistrement[data-modifie="oui"] .etat::before { background: var(--warning); }
/* Ce qui n'a de sens qu'une fois quelque chose modifie : le motif du changement, et
   le renoncement. Les montrer en permanence, c'est proposer d'annuler ce qu'on n'a
   pas fait. */
.barre-enregistrement .si-modifie { display: none; }
.barre-enregistrement[data-modifie="oui"] .si-modifie { display: inline-flex; }
.barre-enregistrement[data-modifie="oui"] .sinon-modifie { display: none; }
/* Le motif est une PHRASE : chasse proportionnelle, comme les listes et la
   recherche. En chasse fixe, son texte d'invite depassait deja du champ. */
.barre-enregistrement .saisie {
  max-width: 280px;
  font-family: var(--font-sans); font-size: 13.5px;
  padding: 10px 14px;
}

/* ============================================================
   Les RANGEES : une liste d'elements courts, qu'on parcourt (2026-08-17)
   ============================================================
   Pour ce qui s'accumule et se relit vite : des regles, des versions, des
   activites. Une rangee tient sur une ou deux lignes, porte son RANG quand
   l'ordre compte, et garde ses actions a droite.

   /!\ Les actions restent VISIBLES. L'habitude de les cacher jusqu'au survol
   (cf. `.data-table .row-actions` dans cette meme feuille) les rend introuvables
   au doigt : sur un ecran tactile, il n'y a pas de survol. Elles sont donc
   discretes, pas cachees.

   /!\ Le texte est limite a deux lignes. Une regle peut faire un paragraphe ;
   vingt paragraphes empiles rendent la liste impossible a parcourir, ce qui est
   precisement le probleme qu'on vient corriger. Le texte entier s'affiche au
   depliage. */
.rangees { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.rangee {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start; gap: 12px;
  padding: 11px 13px;
  background: rgba(255,255,255,0.028);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  transition: border-color var(--motion-base) var(--ease-out),
              background var(--motion-base) var(--ease-out);
}
/* Une rangee qui MENE quelque part (2026-08-17). L'element entier EST le lien : pas de
   surface posee par-dessus.

   /!\ Et c'est pour une raison, pas par gout. `.carte-lien` couvre sa carte par un
   `::after` en `position: absolute`, qui se resout sur le premier ancetre POSITIONNE --
   or `.rangee` ne l'est pas. La surface serait donc remontee jusqu'au premier parent
   positionne, voire au cadre de la page, et aurait recouvert l'ecran entier d'un lien
   invisible. Rendre `.rangee` relative pour la circonstance deplacerait les elements
   absolus des autres ecrans qui l'emploient. */
/* La colonne d'ARGENT d'une rangee : le montant, puis son cumul en legende.

   /!\ Elle remplace un `style="text-align:right;white-space:nowrap"` ecrit en dur dans
   deux gabarits (2026-08-21). Deux copies d'une meme intention finissent par ne plus
   dire la meme chose, et un ecran ne doit porter aucun style local.

   /!\ `white-space: nowrap` est le point qui compte : un montant coupe entre son signe
   et son chiffre (« + » puis « 137.00 $ » a la ligne) se lit comme deux nombres. */
.rangee-argent { text-align: right; white-space: nowrap; }

/* La tete d'un graphique : ce qu'il dit a gauche, de quoi en changer a droite.

   /!\ La bascule est poussee a droite par `margin-left: auto` sur elle-meme, et non
   par un `justify-content: space-between` : la note de gauche est CONDITIONNELLE (elle
   n'existe qu'au cran final), et `space-between` aurait colle le selecteur a gauche des
   qu'elle disparait. Une commande qui change de bord selon l'echelle se cherche. */
.graphe-tete {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  margin: 0 0 12px;
}
.graphe-tete > .seg { margin-left: auto; }
.graphe-note { font-size: 11.5px; margin: 0; max-width: 46ch; }

.rangee--lien { text-decoration: none; color: inherit; cursor: pointer; }
.rangee--lien:focus-visible {
  outline: 2px solid var(--ring); outline-offset: 2px;
}
.rangee:hover, .rangee:focus-within {
  border-color: var(--accent-ring-strong);
  background: var(--accent-wash);
}
.rangee-rang {
  font-family: var(--font-mono); font-size: 11.5px;
  color: var(--muted-foreground);
  min-width: 1.4em; text-align: right; padding-top: 3px;
  font-variant-numeric: tabular-nums;
}
.rangee-corps { min-width: 0; display: flex; flex-direction: column; gap: 5px; }
/* Le texte replie, borne a deux lignes.
   /!\ `max-height` et NON `-webkit-line-clamp`. Le clamp ne compte que des lignes de
   texte : sur un contenu fait de plusieurs BLOCS (des paragraphes, des listes, ce
   qu'une regle de soixante lignes produit), il ne coupe rien du tout. La rangee
   s'affichait alors sur mille cinq cents pixels dans une liste censee etre
   parcourable. Trouve en mesurant, apres avoir cru l'affaire reglee.
   /!\ Les marges du markdown rendu sont posees ici : sans elles, les marges par
   defaut du navigateur (1 em au-dessus ET en dessous de chaque paragraphe) rendent la
   hauteur de repli imprevisible. */
.rangee-texte {
  font-size: 13.5px; line-height: 1.5;
  overflow-wrap: anywhere;
  max-height: 3em;                    /* deux lignes a cette interligne */
  overflow: hidden;
}
.rangee-texte > *:first-child { margin-top: 0; }
.rangee-texte > *:last-child { margin-bottom: 0; }
.rangee-texte p { margin: 0 0 6px; }
.rangee-texte ul, .rangee-texte ol { margin: 0 0 6px; padding-left: 20px; }
.rangee-texte li { margin: 1px 0; }
/* /!\ Depliee POUR EDITION, la rangee cache son texte de lecture : le panneau qui
   s'ouvre en dessous contient exactement le meme contenu, editable. Les montrer tous
   les deux affichait la regle DEUX FOIS, et sur une regle de soixante lignes cela
   faisait deux mille huit cents pixels de doublon. Trouve en creant une regle longue
   a la demande du fondateur : la version courte ne le laissait pas voir. */
.rangee[data-ouverte="oui"] .rangee-texte { display: none; }

/* ── Lire en entier SANS ouvrir l'editeur (2026-08-17) ──
   Demande du fondateur : « comment voir la totalite d'une regle sans cliquer sur
   modifier ». Il fallait passer en edition pour lire, ce qui est le mauvais geste :
   on relit dix fois plus souvent qu'on ne corrige.

   Deux etats distincts, a ne pas confondre :
     `data-deplie`  on LIT en entier, rien n'est modifiable ;
     `data-ouverte` l'editeur est ouvert, et le texte de lecture s'efface.

   /!\ Le chevron n'apparait QUE sur une rangee dont le texte est reellement coupe
   (`data-tronquee`, pose par `rangees.js` apres mesure). Le montrer partout
   promettrait un depliage sur une rangee d'une ligne, ou il ne se passerait rien. */
.rangee[data-deplie="oui"] .rangee-texte { max-height: none; }
/* Le bas du texte coupe s'efface : une coupe franche au milieu d'une ligne se lit
   comme un defaut d'affichage, un degrade se lit comme « il y a une suite ». */
.rangee[data-tronquee="oui"] .rangee-texte {
  cursor: pointer;
  -webkit-mask-image: linear-gradient(180deg, #000 60%, transparent 100%);
          mask-image: linear-gradient(180deg, #000 60%, transparent 100%);
}
.rangee-deplier {
  display: none;                      /* rendu visible par `data-tronquee` */
  align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0;
  flex: 0 0 auto;
  background: transparent; border: 1px solid transparent;
  border-radius: 999px;
  color: var(--muted-foreground);
  cursor: pointer;
  transition: color var(--motion-base) var(--ease-out),
              background var(--motion-base) var(--ease-out);
}
.rangee[data-tronquee="oui"] .rangee-deplier { display: inline-flex; }
.rangee-deplier:hover { color: var(--accent); background: rgba(255,255,255,0.06); }
.rangee-deplier svg {
  width: 12px; height: 12px;
  transition: transform var(--motion-slow) var(--ease-out);
}
.rangee[data-deplie="oui"] .rangee-deplier svg { transform: rotate(180deg); }
/* Le meme chevron, quand il ouvre un PANNEAU (`data-detail`) et non un texte coupe :
   toujours actionnable, parce qu'il ne depend d'aucune mesure de troncature, et il
   tourne sur `data-ouverte` puisque c'est le panneau qui s'ouvre. */
.rangee-deplier[data-detail] { display: inline-flex; }
.rangee[data-ouverte="oui"] .rangee-deplier svg { transform: rotate(180deg); }
.rangee-actions { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
/* Sous 560 px, les actions prennent leur propre ligne. Quatre icones occupent 150 des
   375 pixels d'un telephone : le texte se retrouvait sur une colonne de soixante
   pixels, et une rangee d'une ligne s'etalait sur six. Mesure prise, pas devinee. */
@media (max-width: 560px) {
  .rangee { grid-template-columns: auto minmax(0, 1fr); }
  .rangees[data-reordonnable] .rangee { grid-template-columns: auto auto minmax(0, 1fr); }
  .rangee-actions { grid-column: 1 / -1; justify-content: flex-end; }
}
.rangee-detail {
  grid-column: 1 / -1;
  margin-top: 12px; padding-top: 12px;
  border-top: 1px solid var(--border);
}
/* Ce que la liste dit quand le filtre ne laisse rien passer. Un vide muet se lit
   comme une panne d'affichage. */
.rangees-vide { padding: 18px 4px; font-size: 13px; color: var(--muted-foreground); }

/* ── Reordonner a la souris (2026-08-17) ──
   Demande du fondateur : « ca serait possible de faire un systeme de drag and drop
   pour reorganiser l'ordre plutot que des fleches ». Deux boutons par rangee, sur
   une liste de vingt, c'est quarante boutons pour deplacer une ligne de trois crans.

   /!\ La poignee est un vrai `<button>`, et les FLECHES DU CLAVIER l'animent. Le
   glisser-deposer seul est inaccessible : ni clavier, ni lecteur d'ecran, et
   maladroit au doigt. Les fleches n'ont pas disparu, elles ont juste quitte l'ecran.

   /!\ `touch-action: none` sur la poignee, sinon un glissement au doigt fait
   defiler la page au lieu de deplacer la rangee. C'est le geste par defaut du
   navigateur, et il gagne tant qu'on ne le lui retire pas explicitement. */
/* /!\ La poignee est une QUATRIEME colonne : sans cette regle, elle deborde de la
   grille a trois colonnes de `.rangee` et renvoie les actions a la ligne suivante.
   Elle ne s'applique qu'aux listes reordonnables, pour que les autres gardent leurs
   trois colonnes. */
.rangees[data-reordonnable] .rangee {
  grid-template-columns: auto auto minmax(0, 1fr) auto;
}
.rangee-poignee {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 26px; padding: 0;
  flex: 0 0 auto;
  background: transparent; border: 0; border-radius: var(--radius-sm);
  color: var(--disabled-foreground);
  cursor: grab;
  touch-action: none;
  transition: color var(--motion-base) var(--ease-out),
              background var(--motion-base) var(--ease-out);
}
.rangee:hover .rangee-poignee, .rangee-poignee:focus-visible { color: var(--accent); }
.rangee-poignee:hover { background: rgba(255,255,255,0.06); }
.rangee-poignee svg { width: 11px; height: 15px; }
/* La rangee qu'on tient : soulevee, et son ombre dit qu'elle flotte au-dessus. */
.rangee[data-tenue="oui"] {
  cursor: grabbing;
  border-color: var(--accent-ring-strong);
  background: var(--accent-wash);
  box-shadow: 0 18px 40px -14px rgba(0,0,0,0.7);
  opacity: 0.96;
}
.rangees[data-reordonne="oui"] .rangee { transition: transform 160ms var(--ease-out); }
/* Pendant un glissement, plus rien ne se selectionne : sans cela le navigateur
   surligne le texte des rangees traversees, et le geste devient sale. */
body[data-glisse="oui"] { user-select: none; -webkit-user-select: none; }

/* ── Une rangee qui porte un TITRE et des metadonnees (2026-08-17) ──
   `.rangee-texte` a ete taille pour une regle : un bloc de texte long, coupe a deux
   lignes. Une version de trader, une activite, un evenement ont une autre forme : une
   ligne qui NOMME, et une ligne de faits courts separes par des points medians.

   /!\ Les separateurs sont poses par la feuille, pas ecrits dans le gabarit. Un « · »
   tape a la main entre deux `{% if %}` laisse un point orphelin des que la condition
   qui le suit est fausse, et cela se voit sur la ligne la plus lue de l'ecran. */
.rangee-titre {
  font-size: 13.5px; line-height: 1.45; color: var(--foreground);
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  overflow-wrap: anywhere;
}
/* /!\ Attenue, jamais en italique. La charte est SANS italique (un test l'epingle sur
   les gabarits) : « Sans motif note » se distingue par sa couleur, pas par sa pente. */
.rangee-titre--vide { color: var(--muted-foreground); }
.rangee-meta {
  display: flex; flex-wrap: wrap; align-items: baseline;
  font-size: 12px; color: var(--muted-foreground);
}
.rangee-meta > * + *::before {
  content: "\00b7"; margin: 0 7px; color: var(--disabled-foreground);
}

/* ── Les ECARTS : « ce qui a change », libelle, avant, apres (2026-08-17) ──
   Deux ecrans le demandaient deja : la comparaison de deux versions d'un trader, et
   l'historique des versions, ou chaque ligne dit ce qu'elle a change. Le premier
   l'avait ecrit en `table` avec son `<style>` en ligne.

   /!\ Le libelle affiche est celui de `versionning._LIBELLES` (« Stop loss (x ATR) »),
   jamais le nom de la colonne. Un client ne lit pas `sl_atr`. */
/* Lu par les lecteurs d'ecran, invisible a l'oeil. Sert quand la mise en page porte un
   sens que le texte seul ne dit pas : « 1,5 [devient] 2,0 » sans la fleche. */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; border: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.ecarts { list-style: none; margin: 0; padding: 0; }
.ecart {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto auto;
  align-items: baseline; gap: 10px;
  padding: 7px 0; border-bottom: 1px solid var(--border);
  font-size: 13px;
}
.ecart:last-child { border-bottom: 0; }
.ecart-libelle { color: var(--muted-foreground); min-width: 0; }
.ecart-avant, .ecart-apres {
  font-family: var(--font-mono); font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}
.ecart-avant { color: var(--muted-foreground); }
.ecart-fleche { color: var(--disabled-foreground); }
.ecart-apres { color: var(--foreground); font-weight: 600; }
/* Sous 560 px, quatre colonnes ne tiennent pas : le libelle prend sa ligne. */
@media (max-width: 560px) {
  .ecart { grid-template-columns: auto auto auto; }
  .ecart-libelle { grid-column: 1 / -1; }
}

/* ── Un MONTANT gagne ou perdu, en texte (2026-08-17) ──
   Le pendant de `.saisie--gain` / `.saisie--perte`, pour un chiffre affiche et non
   saisi. Les deux ecrans de versions codaient `#1a7f4b` et `#b3261e` en dur : deux
   teintes de theme CLAIR posees sur un fond sombre, illisibles.
   /!\ `tabular-nums` n'est pas cosmetique : sans lui, une colonne de montants danse
   d'une ligne a l'autre et ne se compare plus a l'oeil. */
.montant-gain, .montant-perte, .montant-nul {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.montant-gain  { color: var(--success); }
.montant-perte { color: var(--error); }
.montant-nul   { color: var(--muted-foreground); font-weight: 500; }


/* ============================================================
   Les NIVEAUX d'un ordre : entree, stop, objectif (2026-08-17)
   ============================================================
   Demande du fondateur : « les analyses, il n'y a pas assez de couleurs sur les
   elements, notamment l'objectif. Le stop devrait etre colore. »

   Trois prix, trois roles, trois couleurs : ce qu'on RISQUE en rouge, ce qu'on VISE en
   vert, ce qu'on DEMANDE en accent. Les trois etaient du meme gris, sur une ligne ou
   seule leur position les distinguait -- il fallait lire l'etiquette de chacun pour
   savoir lequel etait le stop.

   /!\ Ce n'est PAS `.montant-gain` / `.montant-perte`, malgre la meme paire de couleurs,
   et la distinction compte : un montant dit ce qui est ARRIVE sur le compte, un niveau
   dit ce qui etait VISE. Les confondre en une seule classe menerait un jour a colorer un
   objectif en rouge parce que le trade a perdu -- alors que l'objectif, lui, n'a pas
   bouge.

   /!\ La couleur est un RENFORT, jamais le seul porteur : chaque niveau garde son
   etiquette en toutes lettres (« stop 6397.5 »). Une teinte seule ne se lit pas pour huit
   hommes sur cent.

   /!\ Les trois modificateurs sont ecrits `.niveau.niveau--x` et non `.niveau--x` seul.
   Ce n'est pas une coquetterie : `.bilan-valeur` pose `color: var(--foreground)` et
   vit PLUS BAS dans cette feuille, donc a specificite egale c'est elle qui gagnait --
   les niveaux du detail d'une analyse restaient gris pendant que ceux du journal
   etaient colores. Deux classes rendent la couleur independante de l'ordre du fichier,
   qui n'est pas un contrat. */
.niveau {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.niveau.niveau--entree   { color: var(--accent); }
.niveau.niveau--stop     { color: var(--error); }
.niveau.niveau--objectif { color: var(--success); }
/* L'etiquette reste en retrait : c'est le CHIFFRE qui porte la couleur, pas le mot.
   Colorer les deux donnerait une ligne entierement rouge et verte, qui ne se lit plus. */
.niveau-l {
  font-family: var(--font-sans); font-weight: 400;
  color: var(--muted-foreground);
}


/* ============================================================
   Les CHIFFRES : une tuile etiquette / valeur / nuance (2026-08-17)
   ============================================================
   Trois copies divergentes de cette meme idee existaient, aucune dans la charte :
   `.kpi` dans « Mes trades », `.cmp-kpis` dans la comparaison de versions, et
   `.kpi-card` ici meme, vestige de theme clair (ombres bleutees, barre de ratio en
   #4A3B7A code en dur). Celle-ci est la forme reellement employee pour les chiffres
   d'un client : ce qu'on mesure, combien, et sur quel effectif.

   /!\ Posee, RETIREE le meme jour faute d'utilisateur, puis remise le lendemain : la
   carte compacte de « Mes traders » n'en voulait pas (73 px la ou une ligne en coute
   25), la bande d'en-tete de « Mes trades » est son bon usage. La lecon n'est pas
   « ne pas la creer » mais « une primitive suit son premier ecran reel ».

   /!\ La NUANCE porte l'effectif, jamais un pourcentage nu. « +840 $ » au-dessus de
   « 24 trades » se lit ; « 62 % » seul est un mensonge poli. C'est la regle de tout le
   module de performance, et le seuil de fiabilite y est a 140.

   /!\ `tabular-nums` sur la valeur : sans lui, une colonne de montants danse d'une
   tuile a l'autre et ne se compare plus a l'oeil.

   /!\ REVISION du 2026-08-17, sur remarque du fondateur : « je trouve les KPI trop
   figes, et ca manque un peu de couleur pour les mettre en evidence ». La tuile etait un
   aplat translucide uniforme, sans bordure ni relief : a un metre, une bande de sept
   rectangles identiques ou rien ne disait lequel regarder. Trois changements.
   1. Un DEGRADE et une bordure, pour que la tuile soit un objet POSE sur la page et non
      une zone un peu plus claire du fond.
   2. Un LISERE de couleur en haut, qui dit la NATURE du chiffre : vert pour un gain,
      rouge pour une perte, accent pour un chiffre informatif, gris pour ce qui n'a rien
      a annoncer. C'est lui qui donne la couleur demandee, sans peindre le fond entier --
      un aplat vert derriere un chiffre le rend illisible et crie plus fort que le
      chiffre lui-meme.
   3. La tuile mise en avant recoit en plus un lavis de sa propre couleur.

   /!\ La couleur suit le SIGNE d'un montant, JAMAIS le niveau d'un taux. Teinter en vert
   un « 62 % de gagnants » serait exactement le mensonge poli que ce module combat : un
   taux sur vingt trades est un chiffre exact et une information fausse, et une couleur
   le transformerait en jugement. Les tuiles de ratio restent donc a l'accent quelle que
   soit leur valeur -- le seuil de fiabilite est a 140, et il ne se peint pas.

   /!\ La bordure n'existait pas, au motif qu'une tuile posee dans une `.carte` aurait
   donne un cadre dans un cadre. L'usage a tranche : la bande est posee a meme la page,
   jamais dans une carte. Si un ecran l'y met un jour, c'est a lui de retirer la bordure,
   pas a la primitive de rester plate pour un cas qui n'existe pas.

   /!\ 190 px et pas 148 : mesure a 1280 px, colonne de 960. A 148 px, l'etiquette
   « Facteur de profit » (mono, capitales, interlettrage) passait a la ligne et faisait
   monter TOUTE la rangee de 89 a 122 px -- une grille aligne les hauteurs. A 190 px la
   grille rend quatre colonnes, une etiquette tient sur sa ligne, et sept chiffres (la
   tuile forte en vaut deux) tombent en deux rangees pleines au lieu de 5 + 2. */
.chiffres {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 10px;
}

/* L'INTERTITRE d'une rangee de chiffres (2026-08-21, fusion des onglets de Trades).

   Il nomme une FAMILLE de tuiles. Quand deux rangees se suivent -- l'argent puis
   l'activite -- deux grilles identiques separees par du vide se lisent comme une seule
   rangee qui a deborde, pas comme deux familles. Il reprend le travail que faisaient
   les onglets supprimes : l'information ne disparait pas avec eux, elle change de forme.

   /!\ Un intertitre a filet, PAS un `.encadre` par groupe : `.encadre` coute 26/28 px de
   padding, un radius, un degrade radial et une ombre portee, soit ~110 px de hauteur
   pour deux boites -- et une bordure dans laquelle les tuiles ont elles-memes une
   bordure. La regle de `.chiffre` le dit : la bande est posee a meme la page, jamais
   dans une carte. Cout ici : ~24 px par groupe, aucune boite.

   /!\ `--foreground` et pas `--muted-foreground` comme `.t-eyebrow` : en gris il se
   confondrait avec les `.chiffre-l` des tuiles qu'il coiffe, au meme registre de
   capitales. Un titre qui ressemble a ce qu'il titre ne titre plus rien. */
.chiffres-titre {
  display: flex; align-items: center; gap: 10px;
  margin: 0 0 8px;
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 600;
  text-transform: uppercase; letter-spacing: var(--tracking-eyebrow);
  color: var(--foreground);
}
.chiffres-titre::after {
  content: ""; flex: 1; height: 1px; background: var(--border);
}
/* La note qui QUALIFIE la rangee juste au-dessus (le trou du cout et de la duree).
   Le retrait negatif n'est pas cosmetique : il la colle a la bande, et c'est ce
   collage qui dit « ca parle de CES tuiles-la ». Posee a distance normale, elle se
   lirait comme qualifiant tout ce qui precede, y compris le resultat cumule -- alors
   qu'elle ne parle que du cout. */
.chiffres-note { font-size: 11.5px; margin: -8px 0 12px; }
.chiffre {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; gap: 4px;
  padding: 13px 15px 12px;
  background: linear-gradient(158deg,
              rgba(255,255,255,0.055) 0%, rgba(255,255,255,0.018) 62%);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  min-width: 0;
  transition: transform var(--motion-base) var(--ease-out),
              border-color var(--motion-base) var(--ease-out),
              box-shadow var(--motion-base) var(--ease-out);
}
/* Le SURVOL (2026-08-17, demande du fondateur : « j'aimerais des petits effets lorsque
   je survole les KPI »).

   /!\ Un fond qui s'eclaircit, un lisere plus net, une bordure plus franche, une ombre
   portee -- et RIEN d'autre. Ces tuiles ne sont pas cliquables pour la plupart : un
   effet trop marque (curseur main, agrandissement, changement de couleur du chiffre)
   promettrait une action qui n'existe pas, et un client qui clique sans rien obtenir
   conclut que l'ecran est casse. Ce qu'on veut est que la surface REPONDE, pas
   qu'elle invite.

   /!\ L'EFFET et le MOUVEMENT sont separes, et c'est la lecon de la mesure : le panneau
   d'apercu declare `prefers-reduced-motion: reduce`, et une premiere version qui
   suspendait TOUT le survol sous cette preference ne montrait donc rien du tout -- la
   demande serait restee lettre morte chez celui qui l'a faite. La preference demande de
   ne pas faire BOUGER les choses, pas de les rendre inertes. Le souleve est donc la
   seule chose conditionnelle ; la lumiere reste pour tout le monde.

   /!\ `transform` et `box-shadow`, jamais `margin` ni `height` : les premiers sont
   composes par la carte graphique, les seconds relancent la mise en page et feraient
   sauter les six tuiles voisines a chaque passage de souris. */
.chiffre:hover {
  background: linear-gradient(158deg,
              rgba(255,255,255,0.085) 0%, rgba(255,255,255,0.03) 62%);
  border-color: var(--border-strong);
  box-shadow: 0 6px 18px -8px rgba(0,0,0,0.55);
}
.chiffre--fort.chiffre--gain:hover {
  background: linear-gradient(158deg,
              rgba(74,222,128,0.16) 0%, rgba(255,255,255,0.03) 62%);
  border-color: rgba(74,222,128,0.38);
}
.chiffre--fort.chiffre--perte:hover {
  background: linear-gradient(158deg,
              rgba(248,113,113,0.16) 0%, rgba(255,255,255,0.03) 62%);
  border-color: rgba(248,113,113,0.38);
}
.chiffre:hover::before { height: 3px; }
.chiffre--fort:hover::before { height: 4px; }
.chiffre::before {
  transition: height var(--motion-base) var(--ease-out);
}
/* Le seul MOUVEMENT, et il est conditionnel. */
@media (prefers-reduced-motion: no-preference) {
  .chiffre:hover { transform: translateY(-2px); }
}
@media (prefers-reduced-motion: reduce) {
  .chiffre, .chiffre::before { transition: none; }
}
/* Le lisere. Il occupe le haut de la tuile sur toute sa largeur, s'eteint vers la
   droite : un trait plein d'un bord a l'autre ferait une barre de titre. */
.chiffre::before {
  content: "";
  position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: linear-gradient(90deg, var(--accent) 0%, rgba(180,168,255,0.15) 100%);
}
.chiffre--gain::before {
  background: linear-gradient(90deg, var(--success) 0%, rgba(74,222,128,0.12) 100%);
}
.chiffre--perte::before {
  background: linear-gradient(90deg, var(--error) 0%, rgba(248,113,113,0.12) 100%);
}
/* Ce qui n'a rien a annoncer -- pas encore de trade, pas de denominateur : un lisere
   eteint plutot qu'aucun, sinon la tuile parait cassee a cote de ses voisines. */
.chiffre--muet::before {
  background: linear-gradient(90deg, var(--disabled-foreground) 0%, transparent 100%);
}
/* La tuile mise en avant : son lisere s'epaissit et sa couleur passe DERRIERE le
   chiffre, tres diluee. C'est la seule qui recoit un fond teinte -- deux suffiraient a
   annuler l'effet, qui n'existe que par contraste avec les autres. */
.chiffre--fort::before { height: 3px; }
.chiffre--fort.chiffre--gain {
  background: linear-gradient(158deg,
              rgba(74,222,128,0.10) 0%, rgba(255,255,255,0.018) 62%);
  border-color: rgba(74,222,128,0.22);
}
.chiffre--fort.chiffre--perte {
  background: linear-gradient(158deg,
              rgba(248,113,113,0.10) 0%, rgba(255,255,255,0.018) 62%);
  border-color: rgba(248,113,113,0.22);
}
.chiffre-l {
  font-family: var(--font-mono); font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  color: var(--muted-foreground);
}
.chiffre-v {
  font-family: var(--font-mono); font-size: 19px; font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--foreground); line-height: 1.15;
  overflow-wrap: anywhere;
}
/* Une valeur qui est une phrase et non un nombre (« aucun trade ») : la police de
   texte, plus petite, pour qu'elle ne se lise pas comme un chiffre. */
.chiffre-v--mot {
  font-family: var(--font-sans); font-size: var(--text-body);
  font-weight: 500; color: var(--muted-foreground);
}
.chiffre-n { font-size: var(--text-caption); color: var(--muted-foreground); }
/* Une tuile dont la DESCRIPTION est passee en infobulle (2026-08-24, demande du
   fondateur sur « Mes trades »). Le gabarit ajoute `aide-texte` au `.chiffre-n` — la
   charte le masque alors a l'oeil sans le retirer aux lecteurs d'ecran — et pose
   `data-bulle` sur la tuile, qui devient le declencheur. Ne reste a l'ecran que
   l'etiquette et le chiffre.
   /!\ `cursor: help` n'est pas decoratif : sans lui, rien ne dit qu'il y a quelque
   chose a survoler, et la description devient invisible pour de bon. */
.chiffre[data-bulle] { cursor: help; }
/* Une description de tuile tient en une ligne ou deux ; 320 px l'etaleraient. */
#tvni-bulle[data-forme="chiffre"] {
  max-width: min(260px, calc((100vw - 24px) / var(--echelle)));
}
/* La tuile qu'on veut voir EN PREMIER (le resultat cumule) : meme boite, valeur plus
   grande. Une hierarchie par la taille, pas par une couleur de plus. */
.chiffre--fort .chiffre-v { font-size: 26px; }
.chiffre--fort { grid-column: span 2; }
@media (max-width: 520px) { .chiffre--fort { grid-column: auto; } }
/* Une tuile CLIQUABLE mene quelque part sans se deguiser en lien : pas de soulignement,
   et c'est le survol qui le dit. */
a.chiffre { text-decoration: none; transition: background var(--motion-base) var(--ease-out); }
a.chiffre:hover, a.chiffre:focus-visible { background: var(--accent-wash); }


/* ============================================================
   La COURBE que l'on parcourt (2026-08-17)
   ============================================================
   Demande du fondateur : « on peut afficher le cumul lorsque l'on navigue le long de la
   courbe, et voir la date et les informations qu'on retrouve dans le tableau ? »

   Un curseur vertical et une bulle qui suivent le point le plus proche. Sans eux, une
   courbe sans axes ne repond a aucune question precise : elle montre une pente, jamais
   un chiffre.

   /!\ La figure est le repere de position (`position: relative`) : le curseur et la
   bulle s'y placent en pixels. Sans elle, ils remonteraient au premier ancetre positionne
   -- le meme piege que `.carte-lien` dans une rangee.

   /!\ La bulle ne capte PAS la souris (`pointer-events: none`). Sans cela, elle passe
   sous le curseur, declenche un `mouseleave` sur la courbe, se cache, ce qui redonne le
   survol a la courbe, qui la remontre : elle clignote a l'infini. */
.courbe { position: relative; }
.courbe svg { cursor: crosshair; }
/* Le dessin se prend au CLAVIER (fleches gauche/droite pour parcourir les seances) :
   il lui faut donc un anneau de focus, et un anneau qui ne se declenche PAS au clic de
   souris -- d'ou `:focus-visible`. Sans lui, le navigateur pose son trait noir par
   defaut, invisible sur un fond sombre. */
.courbe svg:focus { outline: none; }
.courbe svg:focus-visible { outline: 2px solid var(--ring); outline-offset: 3px; }
.courbe-curseur {
  position: absolute; width: 1px;
  background: var(--accent); opacity: 0.45;
  pointer-events: none;
}
/* Les REPERES de la courbe -- sommet, creux, arrivee (2026-08-18).
   /!\ Ils existent en double, et c'est voulu : en `<circle>` dans le gabarit pour qui
   n'a pas de JavaScript, et en HTML ici pour tous les autres. Le SVG est etire
   (`preserveAspectRatio="none"`), donc ses cercles sortent en ELLIPSES deux fois plus
   larges que hautes -- on croyait voir deux points cote a cote. Le script pose
   `data-courbe-js` sur la figure des qu'il prend la main, et les cercles s'effacent. */
.courbe-repere {
  position: absolute; width: 9px; height: 9px; border-radius: 50%;
  margin: -4.5px 0 0 -4.5px;
  border: 2px solid var(--background);
  pointer-events: none;
}
.courbe-repere--gain   { background: var(--success); }
.courbe-repere--perte  { background: var(--error); }
.courbe-repere--accent { background: var(--accent); }
[data-courbe-js] svg circle { display: none; }

/* La PASTILLE du point survole (2026-08-18). Le trait vertical disait la colonne, jamais
   l'endroit ou la courbe passe : l'oeil devait deviner l'intersection. */
.courbe-marque {
  position: absolute; width: 11px; height: 11px; border-radius: 50%;
  margin: -5.5px 0 0 -5.5px;
  background: var(--accent); border: 2px solid var(--background);
  /* Le halo reprend l'accent reel (#B4A8FF) : une valeur inventee aurait jure. */
  box-shadow: 0 0 0 3px rgba(180,168,255,0.20);
  pointer-events: none;
}
.courbe-bulle {
  position: absolute; z-index: var(--z-dropdown);
  display: flex; flex-direction: column; gap: 2px;
  padding: 8px 11px;
  background: linear-gradient(180deg, #1A1D27 0%, #14161F 100%);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: 0 16px 40px -12px rgba(0,0,0,0.7);
  font-size: var(--text-caption); line-height: 1.45;
  white-space: nowrap;
  pointer-events: none;
}
.courbe-bulle-titre {
  font-family: var(--font-mono); font-weight: 600;
  color: var(--foreground);
}
.courbe-bulle-ligne { color: var(--muted-foreground); }
/* Un montant DANS la bulle garde sa couleur : c'est la meme information que dans la
   rangee correspondante, elle ne doit pas changer de sens en changeant de place. */
.courbe-bulle .montant-gain, .courbe-bulle .montant-perte, .courbe-bulle .montant-nul {
  font-size: var(--text-caption);
}


/* ============================================================
   Le BILAN : une liste libelle -> valeur (2026-08-17)
   ============================================================
   Par ligne : ce qu'on mesure a gauche, ce que ca vaut a droite, et au besoin un
   marqueur devant. Deux ecrans en portaient chacun leur copie sous un autre nom
   (`.cmp-kpis` sur la comparaison de versions, une table locale sur l'audit d'un appel)
   alors que c'est le meme objet.

   /!\ Ce n'est PAS `.chiffres`. Une tuile de `.chiffres` se regarde, une ligne de
   `.bilan` se parcourt : des qu'il y a plus de cinq ou six mesures, ou qu'elles sont
   posees dans une colonne etroite, la liste bat la grille. Le choix se fait sur le
   NOMBRE et la LARGEUR, pas sur le gout.

   /!\ Le marqueur est optionnel : `.bilan--marque` sur le conteneur ouvre sa colonne.
   L'ouvrir toujours laisserait un creux de 18 px devant chaque libelle d'une liste qui
   n'en a pas besoin.

   /!\ Sur une liste de completude, DEUX signaux distincts, et la distinction est tout
   le sujet : `--absent` dit qu'une donnee attendue n'est pas arrivee (une panne),
   `.bilan-court` dit qu'elle est arrivee plus courte que demandee (un reglage du
   client). Les confondre en un seul rouge ferait crier l'ecran a chaque demarrage de
   plugin, quand la profondeur n'est pas encore remplie ; et un rouge permanent ne se
   lit plus.

   /!\ Le marqueur est un caractere ET une couleur, jamais une couleur seule : une
   ligne rouge ne se distingue pas d'une ligne verte pour huit hommes sur cent. */
.bilan { display: flex; flex-direction: column; }
.bilan-ligne {
  display: grid; grid-template-columns: 1fr auto;
  align-items: baseline; gap: 12px;
  padding: 6px 8px; border-radius: var(--radius-sm);
  font-size: var(--text-body-sm);
}
.bilan--marque .bilan-ligne { grid-template-columns: 18px 1fr auto; gap: 10px; }
.bilan-ligne + .bilan-ligne { border-top: 1px solid var(--border); }
.bilan-marque { font-weight: 700; color: var(--success); text-align: center; }
.bilan-libelle { color: var(--muted-foreground); min-width: 0; }
.bilan-valeur {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  color: var(--foreground); font-weight: 600; text-align: right;
}
/* La NUANCE sous la valeur : l'effectif, la periode, la mise en garde. Meme regle que
   `.chiffre-n` -- un taux ne voyage jamais sans elle. */
.bilan-n {
  display: block; font-family: var(--font-sans); font-weight: 400;
  font-size: var(--text-caption); color: var(--muted-foreground);
}
.bilan-ligne--absent { background: rgb(220 60 60 / 0.10); }
.bilan-ligne--absent .bilan-marque,
.bilan-ligne--absent .bilan-valeur { color: var(--error); }
.bilan-ligne--absent .bilan-libelle { color: var(--foreground); }
/* Le neutre : une donnee facultative absente n'est pas une anomalie. */
.bilan-ligne--neutre .bilan-marque { color: var(--disabled-foreground); }
/* La mention « courte », accolee a la valeur. */
.bilan-court { color: var(--warning-text); font-family: var(--font-sans); font-weight: 500; }


/* ============================================================
   Le TEXTE BRUT : une zone technique qu'on lit sans la subir (2026-08-17)
   ============================================================
   Du texte machine (un contexte envoye au modele, une reponse, un fil JSON) pose dans
   une page qui n'est pas faite pour ca. Trois ecrans en portaient une copie locale,
   toutes en couleurs de theme clair codees en dur.

   /!\ Plafonnee en hauteur et defilante DANS sa boite : sans ce plafond, vingt mille
   caracteres poussent tout le reste de la page hors de vue et l'ecran n'a plus de bas.
   /!\ `pre-wrap` + `anywhere` : une ligne de JSON sans espace mesure des milliers de
   caracteres et provoquerait un defilement HORIZONTAL de la page entiere. */
.texte-brut {
  margin: 0;
  white-space: pre-wrap; overflow-wrap: anywhere;
  font-family: var(--font-mono); font-size: var(--text-data-cell);
  line-height: 1.55; color: var(--muted-foreground);
  background: rgba(255,255,255,0.028);
  border: 1px solid var(--border); border-radius: var(--radius-md);
  padding: 12px 14px;
  max-height: 420px; overflow: auto;
}
.texte-brut--court { max-height: 200px; }


/* ============================================================
   La TETE d'une carte, et le texte TRONQUE (2026-08-17)
   ============================================================
   Une ligne de tete : ce qui identifie a gauche, des marqueurs au milieu, un chiffre
   pousse a droite. `.trader-tete` en portait deja une copie locale ; c'est la deuxieme
   occurrence qui la fait monter ici.

   /!\ `align-items: baseline` et non `center` : les enfants n'ont pas la meme taille de
   police (un titre en 13.5, un badge en 10.5), et un centrage vertical les fait flotter
   les uns par rapport aux autres au lieu de poser leur texte sur la meme ligne. */
.tete { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 10px; }
.tete-titre {
  font-family: var(--font-mono); font-size: var(--text-data-cell); font-weight: 600;
  color: var(--foreground); font-variant-numeric: tabular-nums;
}
/* Ce qui est pousse a droite : le chiffre qu'on vient chercher. `margin-left: auto` et
   pas un `.spacer` vide -- un element sans contenu se retrouve lu par les lecteurs
   d'ecran et casse la ligne differemment quand la tete passe a la ligne. */
.tete-fin { margin-left: auto; }

/* Un texte TRONQUE a quelques lignes, le reste etant a un clic. Le nombre de lignes se
   regle par `--lignes` : `style="--lignes:4"`.

   /!\ On tronque le RESUME, jamais l'explication complete. Ici, le raisonnement du
   modele est l'element principal de la page -- ce que le client paie. Il reste a la
   taille du texte courant et sur six lignes par defaut, pas sur deux : tronquer plus
   court transformerait la liste en index, et il faudrait ouvrir chaque ligne pour
   savoir laquelle ouvrir. */
.texte-clamp {
  display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: var(--lignes, 6);
  overflow: hidden;
}


/* ============================================================
   L'EDITEUR : la mise en forme DANS le texte (2026-08-17)
   ============================================================
   Demande du fondateur : « l'editeur je ne le trouve pas fluide, je veux que la mise
   en forme se fasse directement dans le texte pas avec des boutons ». Il y avait une
   barre « B / Liste / 1. Liste » au-dessus d'une zone de texte brut, ou l'on voyait
   `**gras**` et non du gras.

   Desormais une zone `contenteditable` : le gras est gras, les listes sont des
   listes, et rien ne se clique. Ctrl+B, Ctrl+I, et taper « - » ou « 1. » en debut de
   ligne ouvre une liste.

   /!\ Ce qui est STOCKE reste le markdown limite de `md_safe.py` (gras, italique,
   code, liens, deux sortes de listes) : la base ne change pas de format. Le serveur
   convertit markdown -> HTML pour remplir l'editeur, `editeur.js` reconvertit
   HTML -> markdown a l'enregistrement. Le tour est exact parce que les deux cotes
   parlent du MEME petit vocabulaire.

   /!\ Le collage est force en TEXTE BRUT. Sans cela, un copier-coller depuis Word ou
   une page web injecte des balises et des styles que le markdown ne sait pas
   representer : la mise en forme apparaitrait a l'ecran puis disparaitrait a
   l'enregistrement, sans un mot.

   /!\ Une zone `contenteditable` n'est PAS un champ de formulaire : elle n'a ni
   `name`, ni `value`, et n'est pas envoyee par un `<form>`. Ici les regles partent
   en `fetch`, ce qui tombe bien ; pour un vrai formulaire il faudrait recopier le
   markdown dans un champ cache avant l'envoi. */
.editeur {
  position: relative;
  background-color: rgba(255,255,255,0.035);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  transition: border-color var(--motion-slow) var(--ease-out),
              background-color var(--motion-slow) var(--ease-out),
              box-shadow var(--motion-slow) var(--ease-out);
}
.editeur:focus-within {
  border-color: rgba(180,168,255,0.5);
  background-color: rgba(180,168,255,0.06);
  box-shadow: 0 0 0 3px rgba(180,168,255,0.10);
}
.editeur-zone {
  /* Pas de hauteur maximale : une regle peut faire un paragraphe entier, et un
     ascenseur dans une zone de six lignes est exactement ce qu'on vient corriger. */
  min-height: 84px;
  padding: 13px 15px;
  font-family: var(--font-sans); font-size: 13.5px; line-height: 1.6;
  color: var(--foreground);
  outline: none;
  overflow-wrap: anywhere;
}
.editeur-zone > *:first-child { margin-top: 0; }
.editeur-zone > *:last-child { margin-bottom: 0; }
.editeur-zone p { margin: 0 0 8px; }
.editeur-zone ul, .editeur-zone ol { margin: 0 0 8px; padding-left: 22px; }
.editeur-zone li { margin: 2px 0; }
.editeur-zone strong { color: var(--foreground); font-weight: 600; }
.editeur-zone code {
  font-family: var(--font-mono); font-size: 12.5px;
  color: var(--accent);
  background: rgba(180,168,255,0.10);
  border-radius: 4px; padding: 1px 5px;
}
/* Le texte d'invite d'une zone vide. `contenteditable` n'a pas de `placeholder` :
   c'est un `::before` conditionne par un attribut que le script tient a jour. */
.editeur-zone[data-vide="oui"]::before {
  content: attr(data-invite);
  color: var(--disabled-foreground);
  pointer-events: none;
}
/* Ce que l'on peut taper. Sans barre de boutons, personne ne devine que Ctrl+B
   existe : la ou l'ancienne version montrait des boutons, celle-ci l'ECRIT. Une
   fonction cachee est une fonction absente. */
.editeur-aide {
  margin: 6px 0 0;
  font-size: var(--text-caption); line-height: 1.5;
  color: var(--muted-foreground);
}
.editeur-aide kbd {
  font-family: var(--font-mono); font-size: 11px;
  color: var(--foreground);
  background: var(--muted);
  border: 1px solid var(--border-strong);
  border-radius: 4px; padding: 1px 5px;
}
.encadre-titre {
  margin: 0 0 6px;
  font-family: var(--font-sans);
  font-size: 17px; font-weight: 600;
  letter-spacing: -0.02em; line-height: 1.25;
  color: var(--foreground);
}
/* La phrase qui explique la boite. Elle se pose SOUS le titre et respire avant
   les champs — c'est le `.login-sub` de l'ecran de connexion. */
.encadre-sous {
  margin: 0 0 20px;
  font-size: 13.5px; line-height: 1.55;
  color: var(--muted-foreground);
}
.encadre-sous strong { color: var(--foreground); font-weight: 500; }
.encadre-titre:last-child, .encadre-sous:last-child { margin-bottom: 0; }
/* Un titre sans phrase garde quand meme l'air avant le premier champ. */
.encadre-titre + .champ, .encadre-titre + .champs { margin-top: 20px; }

/* Le CHAMP : une etiquette au-dessus, une saisie en dessous.
   /!\ L'etiquette est en chasse fixe et en capitales — c'est le trait de
   l'ecran de connexion et de la vitrine, pas une fantaisie. Elle est donc a
   10,5 px avec un interlettrage REDUIT (0,16 em au lieu de 0,20) : les
   etiquettes d'ici sont des phrases francaises (« Zone d'entree — borne
   basse »), pas les deux mots d'un ecran de connexion, et l'espacement de la
   vitrine les rendait illisibles. */
.champ { display: flex; flex-direction: column; gap: 8px; }
.champs {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 18px 22px;
}
.etiquette {
  font-family: var(--font-mono);
  font-size: 10.5px; font-weight: 500;
  letter-spacing: 0.16em; text-transform: uppercase;
  line-height: 1.45;
  color: var(--muted-foreground);
}
/* La saisie. Chasse fixe, comme le mot de passe de l'ecran de connexion : ici
   presque tout est un nombre (multiples d'ATR, montants, minutes) et la chasse
   fixe les aligne au chiffre. */
.saisie {
  width: 100%;
  padding: 13px 15px;
  font-family: var(--font-mono); font-size: 13.5px; letter-spacing: 0;
  color: var(--foreground);
  /* /!\ `background-color`, PAS le raccourci `background` : celui-ci remet
     `background-image` a `none`, et l'etat de survol effacerait alors le chevron
     d'une liste deroulante — qui, lui, est peint par-dessus. */
  background-color: rgba(255,255,255,0.035);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  outline: none;
  transition: border-color var(--motion-slow) var(--ease-out),
              background-color var(--motion-slow) var(--ease-out),
              box-shadow var(--motion-slow) var(--ease-out);
}
.saisie::placeholder { color: var(--disabled-foreground); }
.saisie:focus {
  border-color: rgba(180,168,255,0.5);
  background-color: rgba(180,168,255,0.06);
  box-shadow: 0 0 0 3px rgba(180,168,255,0.10);
}
.saisie:disabled { opacity: 0.5; cursor: not-allowed; }
textarea.saisie { min-height: 84px; resize: vertical; line-height: 1.55; }
.saisie-note {
  margin: 0; font-size: var(--text-caption); line-height: 1.5;
  color: var(--muted-foreground);
}
/* L'ALERTE : la meme place que `.saisie-note`, mais quand ca ne va pas
   (2026-08-24).

   /!\ Cette classe etait utilisee par SEPT gabarits et n'avait AUCUNE regle
   nulle part. « Ce creneau vient d'etre pris », « Ce fuseau horaire n'existe
   pas. Rien n'a ete enregistre. », « Ce compte n'existe pas » se rendaient
   tous en texte courant, indiscernables de la phrase d'a cote : le visiteur ne
   voyait pas qu'on lui parlait d'une erreur. Trouve en REGARDANT l'ecran,
   jamais par un test : un test lit la chaine, il ne voit pas sa couleur.

   /!\ Elle vit ICI, pas dans le coin d'un gabarit. La premiere correction
   l'avait posee en local dans `rdv_gestion.html` : bon reglage, mauvais
   endroit, cinq autres ecrans restaient muets. C'est la lecon des quatre
   copies d'une meme tuile que cette charte a deja du reunir.

   /!\ La taille est celle de `.saisie-note` : sous un champ, l'alerte remplace
   la note neutre et prend exactement sa place. La couleur vient du jeton
   `--error`, jamais d'un rouge ecrit a la main. A 12 px sur le fond de
   l'application, le contraste mesure est de 7,1:1 (AA demande 4,5).

   /!\ DEUX marges, et c'est ce que le premier jet avait rate : pose en
   `margin: 0` partout, le message collait au titre des qu'il n'etait pas dans
   un champ (mesure a 0 px sur la page du lien prive, vu a l'ecran). Un
   `.champ` est un flex a `gap: 8px` : LUI donne deja l'air, et une marge en
   plus detacherait le message de la saisie a laquelle il se rapporte. Partout
   ailleurs, l'alerte prend son air elle-meme.

   /!\ Elle marche aussi sur un `<span>` (l'etat d'une ligne de tableau) : rien
   ici n'impose un bloc, ni padding, ni bordure, ni fond, et les marges
   verticales ne s'appliquent pas a un element en ligne. Un bandeau encadre est
   un AUTRE composant ; `signup.html` en porte un localement, plus appuye, et il
   n'est pas promu ici sans decision du fondateur. */
.alerte {
  margin: 14px 0;
  font-size: var(--text-caption);
  line-height: 1.5;
  color: var(--error);
}
.champ > .alerte { margin: 0; }
/* Un champ de RECHERCHE porte de la prose, pas des nombres : chasse
   proportionnelle, comme les listes deroulantes. En chasse fixe, « Rechercher une
   regle » depassait deja du champ. */
input[type="search"].saisie { font-family: var(--font-sans); font-size: 13.5px; }

/* La LISTE DEROULANTE (2026-08-16, revue le soir meme sur remarque du fondateur :
   « ce n'est pas du tout esthetique »).

   Ce qui n'allait pas, et que l'apparence native impose : une fleche dessinee par
   le systeme, minuscule et collee au bord droit ; aucune reaction au survol la ou
   tous les autres champs en ont une ; et un libelle long coupe NET au milieu d'un
   mot (« New York — actions et indices US (ES, NQ, Y »).

   /!\ Le chevron est peint par le PARENT (`.liste::after`), pas par une image de
   fond de la liste elle-meme. Une image de fond obligerait a ecrire la couleur du
   chevron EN DUR dans une adresse `data:` — impossible a relier a une variable, et
   c'est precisement ce que la charte interdit. Ici il suit `--muted-foreground`,
   et passe a l'accent au survol comme le reste.

   /!\ La liste DEPLIEE reste dessinee par le systeme : aucune feuille de style ne
   l'atteint. C'est `color-scheme: dark` (sur `html`) qui la rend sombre — sans
   elle, elle s'ouvrait en blanc au milieu d'une page noire. */
.liste { position: relative; display: block; }
.liste > select.saisie {
  appearance: none; -webkit-appearance: none;
  /* De la place pour le chevron, et l'ellipse plutot que la coupe franche. */
  padding-right: 42px;
  text-overflow: ellipsis;
  /* /!\ Chasse PROPORTIONNELLE ici, a l'inverse des autres champs. Les autres
     portent des nombres, que la chasse fixe aligne au chiffre ; une liste porte
     des phrases (« Pétrole brut WTI (CL) »), et la chasse fixe les etale sans
     rien aligner — elle aggravait la coupe en fin de champ. */
  font-family: var(--font-sans); font-size: 14px;
  cursor: pointer;
}
.liste::after {
  content: "";
  position: absolute; right: 17px; top: 50%;
  width: 7px; height: 7px; margin-top: -6px;
  border-right: 1.6px solid var(--muted-foreground);
  border-bottom: 1.6px solid var(--muted-foreground);
  transform: rotate(45deg);
  pointer-events: none;                 /* le clic traverse, il ouvre la liste */
  transition: border-color var(--motion-slow) var(--ease-out);
}
.liste:hover::after, .liste:focus-within::after { border-color: var(--accent); }
/* Le peu que certains navigateurs acceptent sur la liste depliee NATIVE, tant
   que `liste.js` n'a pas pris le relais (ou s'il ne se charge pas). */
.liste > select.saisie option,
.liste > select.saisie optgroup { background: var(--card); color: var(--foreground); }
.liste > select.saisie optgroup { font-style: normal; font-weight: 600; }

/* ── La liste DEPLIEE, dessinee par nous (2026-08-16, second retour du
   fondateur : « les bords sont rectangulaires, ca casse avec le design des
   encadres ») ──

   /!\ Une liste depliee NATIVE est hors d'atteinte de toute feuille de style :
   coins carres, liseré dur, aplat clair sur certains systemes. `color-scheme:
   dark` la fonce dans les navigateurs qui l'ecoutent, et rien de plus — ni
   l'arrondi, ni l'ombre, ni le survol. C'est pourquoi elle est REFAITE.

   /!\ Le `<select>` d'origine RESTE dans le formulaire, jamais remplace : c'est
   lui qui porte la valeur envoyee, la validation `required`, et c'est lui qui
   s'affiche si le script ne se charge pas. On ne le masque pas en
   `display: none` — un champ `required` invalide et non affichable bloque
   l'envoi du formulaire avec une erreur que personne ne voit. */
.liste[data-enrichie] > select.saisie {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  opacity: 0;
}
.liste[data-enrichie]::after { display: none; }   /* le chevron passe au bouton */

.liste-bouton {
  display: flex; align-items: center; gap: 10px;
  text-align: left;
  font-family: var(--font-sans); font-size: 14px;
  padding-right: 15px;
}
.liste-valeur {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.liste-chevron {
  flex: 0 0 auto;
  width: 7px; height: 7px;
  border-right: 1.6px solid var(--muted-foreground);
  border-bottom: 1.6px solid var(--muted-foreground);
  transform: translateY(-2px) rotate(45deg);
  transition: border-color var(--motion-slow) var(--ease-out),
              transform var(--motion-slow) var(--ease-out);
}
.liste-bouton:hover .liste-chevron,
.liste-bouton[aria-expanded="true"] .liste-chevron { border-color: var(--accent); }
/* Deplie, le chevron se retourne : la liste dit dans quel sens elle va. */
.liste-bouton[aria-expanded="true"] .liste-chevron {
  transform: translateY(2px) rotate(-135deg);
}

.liste-panneau {
  position: absolute; left: 0; right: 0; top: calc(100% + 6px);
  z-index: var(--z-dropdown);
  max-height: 300px; overflow-y: auto;
  padding: 6px;
  background: linear-gradient(180deg, #1A1D27 0%, #14161F 100%);
  border: 1px solid var(--border-strong);
  /* L'arrondi des encadres, en plus petit : la liste appartient a la meme
     famille de surfaces, elle ne doit pas ramener un coin carre dans la page. */
  border-radius: 12px;
  box-shadow: 0 24px 60px -16px rgba(0,0,0,0.75),
              0 0 0 1px rgba(255,255,255,0.04) inset;
}
/* Pres du bas de l'ecran, elle s'ouvre VERS LE HAUT plutot que de sortir de la
   fenetre — posee par le script, qui mesure la place restante. */
.liste--haut > .liste-panneau { top: auto; bottom: calc(100% + 6px); }

.liste-option {
  display: block; width: 100%;
  padding: 9px 12px;
  text-align: left;
  font-family: var(--font-sans); font-size: 13.5px; line-height: 1.4;
  color: var(--foreground);
  background: transparent; border: 0; border-radius: 8px;
  cursor: pointer;
}
/* /!\ `data-actif` est la ligne que le CLAVIER designe, `aria-selected` celle
   qui est CHOISIE. Les confondre ferait perdre de vue son propre reglage des
   qu'on presse une fleche. */
.liste-option:hover,
.liste-option[data-actif="oui"] { background: rgba(180,168,255,0.10); }
.liste-option[aria-selected="true"] { color: var(--accent); font-weight: 500; }
/* La PRECISION d'une option, sur sa propre ligne (`data-note`, 2026-09-06). Posee
   pour les seances — « Globex » puis « 17:00 → 16:00 Chicago · 18:00 → 17:00 chez
   vous » — parce que tout mettre sur une ligne donnait un pave qui se repliait sur
   deux et qu'on ne lisait plus. Elle sert a toute liste qui a une precision a
   donner. */
.liste-option-note {
  display: block; margin-top: 2px;
  font-size: 12px; line-height: 1.35; font-weight: 400;
  color: var(--muted-foreground);
}
.liste-option[aria-selected="true"] .liste-option-note { color: var(--muted-foreground); }
.liste-groupe {
  padding: 12px 12px 5px;
  font-family: var(--font-mono); font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  color: var(--muted-foreground);
}
.liste-groupe:first-child { padding-top: 6px; }

/* Une case a cocher ou un bouton radio, avec son libelle sur la meme ligne.
   `accent-color` suffit a teinter la coche native : la refaire a la main
   couterait un faux carre, un vrai `<input>` masque et trois etats a peindre,
   pour le meme resultat en moins accessible. */
.case {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: 13.5px; line-height: 1.4;
  color: var(--foreground); cursor: pointer;
}
.case > input[type="checkbox"], .case > input[type="radio"] {
  flex: 0 0 auto;
  width: 16px; height: 16px; margin: 0;
  accent-color: var(--primary);
  cursor: pointer;
}
.cases { display: flex; flex-wrap: wrap; gap: 12px 22px; align-items: center; }
/* /!\ `.case` est en `inline-flex` : deux cases posees a la suite se rangent sur
   la MEME ligne. Quand ce sont deux choix independants — « ne pas trader autour
   des annonces » et « solder avant l'annonce » — les mettre cote a cote les fait
   lire comme un seul reglage a deux moities. Elles s'empilent alors. */
.cases--liste { flex-direction: column; align-items: flex-start; gap: 12px; }

/* La BASCULE : le selecteur segmente `.seg`, mais fait de vrais boutons radio
   (2026-08-17, demande du fondateur : « je verrais bien un bouton de bascule
   d'un mode de calcul a l'autre »).

   Deux ronds a cocher poses cote a cote ne DISENT pas qu'ils commandent la meme
   chose : ils se lisent comme deux reglages independants dont on cocherait l'un.
   Une bascule dit « c'est un seul reglage, a deux positions ».

   /!\ Les `<input type="radio">` restent la, seulement invisibles. C'est eux que
   le formulaire envoie, eux que les fleches du clavier parcourent, eux que le
   lecteur d'ecran annonce comme un groupe. Les remplacer par des boutons aurait
   coute tout cela pour la meme image. */
.seg--champ > label { position: relative; }
.seg--champ > label > input {
  position: absolute; width: 1px; height: 1px;
  margin: 0; opacity: 0; pointer-events: none;
}
.seg--champ > label:has(input:checked) {
  background: var(--card);
  color: var(--foreground);
  box-shadow: 0 1px 2px rgba(0,0,0,0.35);
}
/* Le contour de mise au point doit suivre le bouton radio CACHE : sans cette
   regle, la bascule se parcourt au clavier sans qu'on voie ou l'on est. */
.seg--champ > label:has(input:focus-visible) {
  outline: 2px solid var(--ring); outline-offset: 2px;
}

/* Le DUO : deux valeurs qui vont ensemble, dans une zone a elles.
   Son SEUL appelant est desormais l'ecran d'inscription (`signup.html`), qui
   redeclare ses colonnes dans son propre `<style>` mais herite ce remplissage.
   /!\ Ne pas le supprimer en le croyant mort : le retirer changerait cet ecran-la.
   /!\ Les reglages d'un trader l'ont quitte le 2026-08-29. Il y etait toujours
   pose avec `verre-zone`, DANS une carte `.groupe` qui a deja sa bordure : le
   cadre dans un cadre que le commentaire de `.verre-zone` interdit, et trois
   alignements differents dans une meme carte. Les deux valeurs continuent de se
   lire l'une par rapport a l'autre, par la COULEUR des saisies
   (`.saisie--perte` / `.saisie--gain`), qui ne demande aucune boite. */
.duo {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 16px 24px;
  padding: 16px 18px;
}

/* Ce qui COUTE en rouge, ce qui RAPPORTE en vert : le meme code que partout
   ailleurs dans l'application (`--loss` / `--profit`), pose sur la saisie
   elle-meme. Le risque et la perte partagent le role « ce qui coute ».
   /!\ Le rouge et le vert ne disent pas « mal » et « bien » : ils disent de quel
   cote on est. Un client qui augmente son objectif ne fait rien de « bien ». */
.saisie--perte { color: var(--loss); }
.saisie--gain { color: var(--profit); }

/* Une valeur que le serveur ou le navigateur CALCULE, et que l'on ne tape pas.
   Le liseré en pointilles le dit sans une phrase. Elle reste lisible : la griser
   comme un champ desactive ferait croire a un champ en panne, alors que c'est la
   moitie de l'information.
   /!\ Ces champs portent `disabled`, et c'est VOULU : un champ desactive n'est
   pas envoye par le formulaire. Sans cela, les deux moities du duo partiraient
   sous le meme nom et le serveur en choisirait une au hasard. */
.saisie--deduite {
  background-color: transparent;
  border-style: dashed;
  cursor: default;
}
.saisie--deduite:disabled { opacity: 1; }

/* ============================================================
   La BULLE d'aide (2026-08-16)
   ============================================================
   Une description longue sous chaque champ transforme un formulaire de trente
   reglages en mur de texte, et on finit par ne plus rien lire. Elle passe donc
   derriere une petite icone, et ne s'affiche qu'a la demande.

   /!\ Ce n'est PAS un avertissement. Ce qui coute cher s'il n'est pas lu — « le
   cout affiche sera faux pour ce modele » — reste ECRIT, en clair. La bulle est
   pour ce qui aide, pas pour ce qui protege.

   /!\ Le declencheur est un vrai `<button>`, jamais un `<span>` : le bouton
   donne le clavier (tabulation + Entree) et le tactile (une tape) sans une
   ligne de script, la ou un survol seul n'existe ni au clavier ni au doigt.
   Le texte d'aide vit DANS le bouton, masque a l'oeil mais lu par les lecteurs
   d'ecran : la bulle peut disparaitre, l'aide reste.

   /!\ La bulle elle-meme est posee sur `<body>` par `bulle.js`, en
   `position: fixed`. Elle ne peut PAS vivre dans le champ : `.panel` et
   `.encadre` coupent ce qui depasse (`overflow`, `border-radius`), et une bulle
   ancree pres d'un bord serait tronquee. */
.aide {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; padding: 0; margin-left: 6px;
  flex: 0 0 auto; vertical-align: -3px;
  background: transparent;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  color: var(--muted-foreground);
  cursor: help;
  transition: color var(--motion-base) var(--ease-out),
              border-color var(--motion-base) var(--ease-out);
}
.aide svg { width: 9px; height: 9px; }
.aide:hover, .aide:focus-visible {
  color: var(--accent);
  border-color: var(--accent-ring-strong);
}
/* Le texte d'aide : invisible a l'oeil, present pour les lecteurs d'ecran.
   Pas de `display:none` ni de `visibility:hidden` — ils le retireraient AUSSI
   des lecteurs d'ecran, ce qui est exactement ce qu'on veut eviter. */
/* /!\ Le selecteur porte sur `.aide-texte` SEUL depuis le 2026-08-18. Il etait
   `.aide > .aide-texte`, donc reserve au bouton d'aide ; la rangee d'un trade, qui
   porte le meme texte sans etre un bouton, l'affichait EN CLAIR au milieu de la ligne
   et poussait tout le reste. Le texte de bulle est masque parce qu'il est un texte de
   bulle, pas parce qu'il est dans un bouton. */
.aide-texte {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap; border: 0;
}
#tvni-bulle {
  position: fixed; top: 0; left: 0;
  z-index: var(--z-toast);
  /* /!\ `100vw` ignore le `zoom` de `body` : a l'echelle 1.1 la bulle pouvait se
     croire autorisee a 10 % de plus que la largeur reelle. On divise par l'echelle,
     comme `bulle.js` le fait pour la position. */
  max-width: min(320px, calc((100vw - 24px) / var(--echelle)));
  padding: 11px 14px;
  font-family: var(--font-sans); font-size: 12.5px; line-height: 1.55;
  font-weight: 400; letter-spacing: 0; text-transform: none;
  color: var(--foreground);
  background: linear-gradient(180deg, #1A1D27 0%, #14161F 100%);
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  box-shadow: 0 18px 40px -12px rgba(0,0,0,0.7);
  opacity: 0; visibility: hidden;
  transition: opacity var(--motion-base) var(--ease-out),
              visibility var(--motion-base) var(--ease-out);
  pointer-events: none;
}
#tvni-bulle[data-ouverte="oui"] { opacity: 1; visibility: visible; }
#tvni-bulle code {
  font-family: var(--font-mono); font-size: 11.5px;
  color: var(--accent);
}
#tvni-bulle strong { color: var(--foreground); font-weight: 600; }
/* La bulle d'un TRADE (2026-08-18) : elle porte le plan, l'issue et le raisonnement du
   modele, la ou une aide de champ porte deux phrases. 320 px la rendaient illisible. */
#tvni-bulle[data-forme="periode"] {
  max-width: min(300px, calc((100vw - 24px) / var(--echelle)));
}
#tvni-bulle[data-forme="trade"] {
  max-width: min(440px, calc((100vw - 24px) / var(--echelle)));
}
.bulle-titre {
  display: block; font-family: var(--font-mono); font-weight: 600;
  margin-bottom: 6px;
}
.bulle-faits {
  display: grid; grid-template-columns: auto 1fr; gap: 2px 12px;
  font-size: 12px; margin-bottom: 8px;
}
.bulle-faits dt { color: var(--muted-foreground); }
.bulle-faits dd { margin: 0; font-variant-numeric: tabular-nums; }
.bulle-pourquoi {
  display: block; padding-top: 8px; border-top: 1px solid var(--border);
  font-size: 12px; line-height: 1.5; color: var(--muted-foreground);
}
/* /!\ Le raisonnement est CLAMPE a cinq lignes, pas tronque par le serveur : une bulle
   qui prendrait la moitie de l'ecran ne se survole plus, elle se subit. Le texte entier
   reste a un clic, et la ligne suivante le dit. */
.bulle-texte {
  display: -webkit-box; -webkit-line-clamp: 5; -webkit-box-orient: vertical;
  overflow: hidden; color: var(--foreground);
}
.bulle-suite { display: block; margin-top: 7px; color: var(--disabled-foreground); }

/* ============================================================
   La FENETRE de confirmation (2026-08-16)
   ============================================================
   /!\ JAMAIS `confirm()`. C'est la boite du NAVIGATEUR : hors de la charte,
   elle tronque les textes longs, et certains contextes l'ecartent sans rien
   afficher — le clic semble alors n'avoir rien fait. Une fenetre de
   l'application a la place de NOMMER la consequence au lieu de demander
   « etes-vous sur ? ».

   /!\ `<dialog>` natif : superposition, piege du focus et touche Echap sans une
   ligne de script. Une fausse fenetre en `<div>` rate presque toujours les
   trois. `.verre` lui donne sa surface ; il ne reste que la largeur, le voile
   de fond et l'espacement, que `<dialog>` ne pose pas seul. */
dialog.verre {
  width: min(460px, calc(100vw - 32px));
  padding: 24px;
  color: var(--foreground);
}
dialog.verre::backdrop { background: rgba(5, 6, 10, 0.72); }
.dialogue-titre {
  margin: 0 0 12px;
  font-family: var(--font-sans);
  font-size: 16px; font-weight: 600; letter-spacing: -0.015em; line-height: 1.3;
  color: var(--foreground);
}
.dialogue-texte {
  margin: 0 0 8px;
  font-size: 13.5px; line-height: 1.55; color: var(--muted-foreground);
}
.dialogue-texte strong { color: var(--foreground); font-weight: 500; }
.dialogue-alerte { color: var(--warning-text); }
.dialogue-actions {
  display: flex; gap: 8px; justify-content: flex-end; margin-top: 18px;
}

/* Cards */
.card {
  padding: var(--space-6) 26px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
}

/* Inputs */
.input, .select, textarea.textarea {
  height: 38px; padding: 0 var(--space-3);
  font-family: var(--font-sans); font-size: var(--text-body); color: var(--foreground);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  transition: border-color var(--motion-base) var(--ease-out),
              box-shadow var(--motion-base) var(--ease-out);
}
textarea.textarea { height: auto; min-height: 80px; padding: var(--space-3); }
.input::placeholder { color: var(--disabled-foreground); }
.input:focus, .select:focus, textarea.textarea:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgb(74 59 122 / 0.12);
}
.input:disabled { background: var(--muted); opacity: 0.6; }
.input[aria-invalid="true"] { border-color: var(--error); }

/* Search input with leading icon */
.search-wrapper { position: relative; max-width: 360px; }
.search-icon {
  position: absolute;
  left: 12px; top: 50%; transform: translateY(-50%);
  color: var(--muted-foreground);
  pointer-events: none;
  display: flex; align-items: center;
}
.search-input { width: 100%; padding-left: 40px; }

/* KPI — polish v4 (sentence-case label, elevation, hover lift, ratio bar) */
.kpi-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 16px 18px;
  min-height: 88px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  box-shadow: 0 1px 2px rgba(14,17,38,.04), 0 8px 24px -8px rgba(14,17,38,.06);
  transition: transform .22s cubic-bezier(.2,.8,.2,1), box-shadow .22s ease, border-color .22s ease;
  animation: kpiFadeIn .5s cubic-bezier(.2,.8,.2,1) backwards;
}
.kpi-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 2px 4px rgba(14,17,38,.06), 0 16px 32px -8px rgba(74,59,122,.16);
  border-color: rgba(74,59,122,.25);
}
@keyframes kpiFadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
.kpi-strip .kpi-card:nth-child(1) { animation-delay: 0ms; }
.kpi-strip .kpi-card:nth-child(2) { animation-delay: 60ms; }
.kpi-strip .kpi-card:nth-child(3) { animation-delay: 120ms; }
.kpi-strip .kpi-card:nth-child(4) { animation-delay: 180ms; }
.kpi-strip .kpi-card:nth-child(5) { animation-delay: 240ms; }
.kpi-strip .kpi-card:nth-child(6) { animation-delay: 300ms; }

.kpi-card .kpi-label {
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--muted-foreground);
  font-weight: 500;
  letter-spacing: -0.005em;
  text-transform: none;
  margin-bottom: 8px;
  display: block;
}
.kpi-card .kpi-row {
  display: flex;
  align-items: baseline;
  gap: 12px;
}
.kpi-card .kpi-value {
  font-size: 34px;
  font-weight: 500;
  color: var(--foreground);
  font-variant-numeric: tabular-nums;
  line-height: 1.0;
  letter-spacing: -0.025em;
}
.kpi-card .kpi-value.sage       { color: var(--success); }
.kpi-card .kpi-value.terracotta { color: var(--error); }
.kpi-card .kpi-value.muted      { color: var(--disabled-foreground); }
.kpi-card .kpi-unit {
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--disabled-foreground);
  font-weight: 400;
}
.kpi-card .kpi-delta-pct {
  margin-left: auto;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  display: inline-flex;
  align-items: center;
  gap: 3px;
}
.kpi-card .kpi-delta-pct.up::before    { content: '↑'; font-size: 10px; }
.kpi-card .kpi-delta-pct.down::before  { content: '↓'; font-size: 10px; }
.kpi-card .kpi-delta-pct.up    { color: var(--success); }
.kpi-card .kpi-delta-pct.down  { color: var(--error); }
.kpi-card .kpi-delta-pct.stable,
.kpi-card .kpi-delta-pct.new   { color: var(--muted-foreground); }
.kpi-card .kpi-secondary {
  font-family: var(--font-sans);
  font-size: 11px;
  color: var(--muted-foreground);
  margin-top: 10px;
}
.kpi-card .kpi-ratio {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  font-size: 11px;
  color: var(--muted-foreground);
}
.kpi-card .kpi-ratio-bar {
  flex: 1;
  height: 4px;
  background: var(--muted);
  border-radius: 2px;
  overflow: hidden;
}
.kpi-card .kpi-ratio-bar > i {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, #4A3B7A 0%, #6B5BA3 100%);
  border-radius: 2px;
  transform-origin: left;
  animation: kpiBarFill 1.1s cubic-bezier(.2,.8,.2,1) .25s backwards;
}
@keyframes kpiBarFill { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.kpi-card .kpi-ratio-pct {
  font-weight: 600;
  color: var(--foreground);
  font-variant-numeric: tabular-nums;
}

/* Data table — polish v4 (gradient header, accent bar on hover, row actions) */
.data-table { width: 100%; border-collapse: separate; border-spacing: 0; }
.data-table th {
  font-family: var(--font-sans); font-size: 10px;
  text-transform: uppercase; letter-spacing: 0.16em;
  color: var(--muted-foreground);
  padding: 12px 16px; text-align: left;
  font-weight: 600;
  /* Le degrade clair code en dur (#F4F5F8) et sa bordure #D6DAE2 ont ete
     retires le 2026-08-23 : ils peignaient une BARRE BLANCHE en haut de chacun
     des 19 tableaux de l application, qui est sombre. Le meme couple etait
     duplique dans `admin_base.html` ; les deux ont ete corriges ensemble. */
  background: var(--muted);
  border-bottom: 1px solid var(--border);
}
.data-table td {
  font-size: var(--text-body); color: var(--foreground);
  padding: 14px 16px;
  border-bottom: 1px solid var(--muted);
  vertical-align: middle;
}
.data-table tbody tr:last-child td { border-bottom: none; }
.data-table tbody tr { transition: background .22s ease; }
.data-table tbody tr:hover {
  background: linear-gradient(90deg, rgba(74,59,122,.05) 0%, rgba(74,59,122,0) 100%);
}
.data-table tbody tr.selected,
.data-table tbody tr.selected:hover {
  background: rgba(74,59,122,.11);
  box-shadow: inset 3px 0 0 var(--accent);
}
/* Accent bar handled via border-left on td:first-child in admin_base.html (layout-safe) */

/* Numeric / muted helpers for data cells */
.data-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.data-table td.num.muted, .data-table td .muted-cell { color: var(--disabled-foreground); }
.data-table .row-id {
  font-size: 11px; color: var(--disabled-foreground);
  margin-left: 8px; padding: 1px 6px;
  background: var(--muted); border-radius: 3px;
  font-variant-numeric: tabular-nums;
}
.data-table .row-name { font-weight: 600; color: var(--foreground); font-size: 13.5px; }
.data-table .row-email { font-size: 13px; color: var(--foreground); }
.data-table .row-phone {
  font-size: 12px; color: var(--muted-foreground);
  margin-top: 2px; display: block; font-variant-numeric: tabular-nums;
}
.data-table .row-date { font-variant-numeric: tabular-nums; font-size: 12.5px; color: var(--muted-foreground); }
.data-table .row-actions {
  opacity: 0;
  transition: opacity .22s ease;
  display: inline-flex; gap: 4px;
  margin-left: 12px;
  vertical-align: middle;
}
.data-table tbody tr:hover .row-actions { opacity: 1; }
.data-table .row-actions button,
.data-table .row-actions a {
  width: 24px; height: 24px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); background: var(--card);
  border-radius: 4px; color: var(--muted-foreground);
  cursor: pointer; transition: all .14s; text-decoration: none;
}
.data-table .row-actions button:hover,
.data-table .row-actions a:hover {
  border-color: var(--accent); color: var(--accent);
  background: rgba(74,59,122,.05);
}

/* Colonne de case à cocher (sélection multiple) — Décisions/Users (A2/D1) */
.data-table th.sel-col,
.data-table td.sel-col {
  width: 36px;
  padding-left: var(--space-3);
  padding-right: 0;
  text-align: center;
}
.sel-col input[type="checkbox"] { vertical-align: middle; cursor: pointer; }

/* Barre d'actions de sélection multiple (corbeille Décisions, rôles Users — A2/D1) */
.trade-selection-bar:not([hidden]) {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.tsb-count {
  font-size: var(--text-body-sm);
  color: var(--muted-foreground);
  white-space: nowrap;
}
.tsb-count b { color: var(--foreground); font-variant-numeric: tabular-nums; }
.tsb-actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* Status dot — defined in admin_base.html for higher specificity context. */

/* Badges */
.badge {
  display: inline-flex; align-items: center;
  padding: 3px 9px;
  font-family: var(--font-sans); font-size: 11px; font-weight: 500;
  line-height: 1; letter-spacing: 0.02em;
  border-radius: var(--radius-pill);
}
/* /!\ Fonds RENFORCES le 2026-08-17, sur remarque du fondateur (« c'est un peu trop
   gris, je trouve »). Les teintes etaient posees a 0.12 d'opacite, et dans une famille
   de couleur (verts et bruns sourds) HERITEE DU THEME CLAIR, sans rapport avec la
   couleur du texte qu'elles portent. Resultat : a un metre de l'ecran un badge de
   couleur et un badge neutre se ressemblaient, et il fallait lire le mot pour savoir si
   l'etat etait bon ou mauvais.
   Le fond reprend desormais la teinte du TEXTE qu'il porte, a 0.16. La couleur du texte,
   elle, ne bouge pas : c'est elle qui porte le sens, le fond ne fait que la detacher. */
.badge-success    { background: rgb(74 222 128 / 0.16);  color: var(--success); }
.badge-warning    { background: rgb(251 191 36 / 0.16);  color: var(--warning); }
.badge-error      { background: rgb(248 113 113 / 0.16); color: var(--error); }
.badge-info       { background: rgb(125 211 252 / 0.16); color: var(--info); }
.badge-neutral    { background: var(--muted);           color: var(--muted-foreground); }
.badge-prospect   { background: var(--muted);           color: var(--muted-foreground); }
.badge-client     { background: rgb(44 58 82 / 0.10);   color: var(--primary); }
.badge-ambassador { background: rgb(74 59 122 / 0.12);  color: var(--accent); }
/* Le meme badge que `-ambassador`, sous un nom qui n'est pas un role de CRM : « active »
   sur une version de trader n'a rien a voir avec un ambassadeur. */
.badge-accent     { background: rgb(74 59 122 / 0.12);  color: var(--accent); }

/* Sidebar nav */
.nav-item {
  display: flex; align-items: center; gap: 10px;
  padding: 9px var(--space-3);
  font-size: var(--text-body); font-weight: 500;
  color: var(--muted-foreground); text-decoration: none;
  border-radius: var(--radius-md);
  transition: background var(--motion-base) var(--ease-out),
              color var(--motion-base) var(--ease-out);
}
.nav-item:hover { background: var(--muted); color: var(--foreground); }
.nav-item.active,
.nav-item[aria-current="page"] {
  background: rgb(74 59 122 / 0.08);
  color: var(--accent);
  border-left: 2px solid var(--accent);
}

/* ══ La LIGNE DE FAITS sous un titre de page (montee dans la charte le
   2026-08-23) ══════════════════════════════════════════════════════════════
   « 2 traders crees · 1 actif sur 3 disponibles · aucun plugin connecte ». Elle
   repond, avant tout le reste, a la question qu'on se pose en arrivant sur
   l'ecran. Le CHIFFRE est en clair, le mot qui le qualifie en gris : c'est ce
   qui la rend lisible d'un coup d'oeil sans qu'elle pese comme une rangee de
   tuiles.

   /!\ Elle vivait en local dans `user_traders.html` depuis le 2026-08-16. Elle
   monte ici parce qu'un DEUXIEME ecran l'emploie (« Parrainage », puis « Mes
   gains ») -- la regle de cette feuille : la charte ne prend que ce qui a servi
   deux fois, et une primitive suit son premier ecran reel.

   /!\ Elle n'apparait JAMAIS sur un ecran vide. « 0 filleul, 0 client » est un
   tableau de bord pose au-dessus du rien ; c'est `.vide-accueil` qui parle a ce
   moment-la. */
.resume-etat {
  display: flex; gap: 18px; flex-wrap: wrap; align-items: baseline;
  margin-top: 10px;
}
.resume-etat .item { font-size: var(--text-body-sm); color: var(--muted-foreground); }
.resume-etat .item strong { color: var(--foreground); }

/* Empty state */
.empty-state {
  display: flex; flex-direction: column; align-items: center;
  gap: var(--space-3);
  padding: var(--space-12) var(--space-6);
  text-align: center;
}
.empty-state svg { width: 48px; height: 48px; color: var(--muted-foreground); opacity: 0.4; }
.empty-title { font-size: var(--text-h4); font-weight: 500; color: var(--foreground); margin: 0; }
.empty-body { font-size: var(--text-body); color: var(--muted-foreground); margin: 0; }

/* ══ L'ACCUEIL D'UN ECRAN VIDE (2026-08-23) ═══════════════════════════════════
   /!\ A ne pas confondre avec `.empty-state` juste au-dessus, qui reste ce qu'il
   est : la petite ligne grise d'un tableau d'administration qui n'a rien a
   montrer. Ce bloc-ci s'adresse a un CLIENT qui vient de payer et qui ouvre un
   ecran pour la premiere fois. Il ne dit pas « aucun resultat », il dit ce qu'il
   trouvera ici et par quoi commencer.

   Il sert DEUX ecrans, et c'est ce qui l'a fait monter dans la charte plutot que
   de rester local : « Mes traders » sans trader, et « Mes trades » sans la
   moindre analyse. `user_trades.html` n'a d'ailleurs plus aucun style local
   depuis le 2026-08-17 -- il ne pouvait pas les declarer chez lui.

   /!\ La pastille d'icone est TEINTEE d'accent, pas grise. Un rond gris pose
   au-dessus de « vous n'avez rien » se lit comme une erreur ; cet ecran
   accueille. C'est la difference de ton avec `.empty-state`. */
.vide-accueil { text-align: center; max-width: 640px; margin: 14px auto 30px; }
.vide-accueil-marque {
  width: 52px; height: 52px; margin: 0 auto 16px; border-radius: var(--radius-lg);
  display: flex; align-items: center; justify-content: center;
  background: rgb(180 168 255 / 0.10); border: 1px solid rgb(180 168 255 / 0.22);
  color: var(--accent);
}
/* /!\ 24 px en dur : l'echelle de la charte s'arrete a `--text-h4` (18 px) et il
   n'existe aucun jeton au-dessus. Le `h1` de ces ecrans est lui aussi en dur, a
   26 px, dans la coquille ; ce titre se pose juste en dessous de lui. */
.vide-accueil-titre { font-size: 24px; margin: 0 0 8px; letter-spacing: -0.02em; }
.vide-accueil-lede { margin: 0; font-size: var(--text-body); }

/* La rangee de cartes qui explique. Trois sur un ecran normal, une seule sur un
   ecran etroit -- le nombre par rangee se deduit de la place, il n'est ecrit
   nulle part.
   /!\ 300 px de colonne, et non les 460 px des cartes de trader : c'est ce qui
   tient les TROIS sur une rangee. Un parcours rendu en 2 + 1 ne se lit plus
   comme un parcours. */
.vide-cartes { --carte-min: 300px; margin-bottom: 22px; }
.vide-cartes .carte { --carte-marge: 0; padding: 20px; }
.vide-cartes .carte > b { font-size: var(--text-body); }
.vide-cartes .carte .lede { margin: 8px 0 0; font-size: var(--text-body-sm); }
.vide-cartes .carte .vide-cartes-tete + .lede { margin: 0 0 14px; }
/* /!\ `align-self` n'est pas cosmetique : dans une carte en colonne, un `.btn`
   s'etire sur toute la largeur et prend l'allure d'un champ de saisie. Vu a
   l'ecran sur la maquette du 2026-08-23, pas devine. */
.vide-cartes .carte .btn,
.vide-cartes .carte .action { align-self: flex-start; }

/* La tete numerotee d'une carte d'etape (« Mes traders » seulement pour l'instant). */
.vide-cartes-tete { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.vide-cartes-tete b { font-size: var(--text-body); }
.vide-num {
  width: 24px; height: 24px; border-radius: 50%; flex: 0 0 24px;
  display: flex; align-items: center; justify-content: center;
  font-size: var(--text-caption); font-weight: 600;
  background: rgb(255 255 255 / 0.06); color: var(--muted-foreground);
}
/* L'etape ou l'on en est. Elle SEULE porte l'accent : c'est ce qui dit, sans une
   phrase de plus, laquelle des trois se fait maintenant. */
.vide-num--courant { background: rgb(180 168 255 / 0.14); color: var(--accent); }

/* Le pied de l'ecran d'attente : l'etat a gauche, le geste a droite. */
.vide-pied {
  display: flex; gap: 14px; flex-wrap: wrap;
  align-items: center; justify-content: space-between;
}

/* ⚠️ `.api-key-display` (clé masquée + oeil + copier) retiré le 2026-08-25 avec
   la section « Authentification API » du tiroir utilisateur, son unique porteur. */

/* Liens */
a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}
a:hover { text-decoration-thickness: 2px; }

/* ----------------------- Wave D — Profile page ----------------------- */

.profile-body {
  background: var(--background, #F4F4F2);
  color: var(--foreground, #1E1E1E);
  font-family: var(--font-sans, 'Inter', sans-serif);
  margin: 0;
}

.profile-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 32px;
  background: var(--surface, #FFFFFF);
  border-bottom: 1px solid var(--border, #E5E5E5);
}

.profile-nav-brand .profile-nav-logo {
  font-weight: 600;
  font-size: 18px;
  color: var(--marine, #2C3A52);
  letter-spacing: 0.02em;
}

.profile-nav-actions {
  display: flex;
  align-items: center;
  gap: 16px;
}

.profile-nav-user {
  font-size: 14px;
  color: var(--foreground-muted, #555);
}

.profile-container {
  max-width: 720px;
  margin: 48px auto;
  padding: 0 32px;
}

.profile-container h1 {
  font-size: 32px;
  font-weight: 400;
  margin: 0 0 32px;
  letter-spacing: -0.01em;
}

.profile-section {
  background: var(--surface, #FFFFFF);
  border: 1px solid var(--border, #E5E5E5);
  border-radius: 8px;
  padding: 24px;
  margin-bottom: 24px;
}

.profile-section h2 {
  font-size: 18px;
  font-weight: 500;
  margin: 0 0 16px;
}

.profile-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.profile-form label {
  display: flex;
  flex-direction: column;
  font-size: 13px;
  color: var(--foreground-muted, #555);
}

.profile-form input {
  padding: 10px 12px;
  border: 1px solid var(--border, #E5E5E5);
  border-radius: 6px;
  font-size: 14px;
  font-family: inherit;
  margin-top: 4px;
}

.profile-btn {
  padding: 8px 16px;
  border-radius: 6px;
  border: 1px solid transparent;
  font-size: 14px;
  font-family: inherit;
  cursor: pointer;
  text-decoration: none;
  display: inline-block;
}

.profile-btn-primary {
  background: var(--marine, #2C3A52);
  color: #fff;
}

.profile-btn-ghost {
  background: transparent;
  border-color: var(--border, #E5E5E5);
  color: var(--foreground, #1E1E1E);
}

.profile-btn-danger {
  background: transparent;
  border-color: #C0392B;
  color: #C0392B;
}

.profile-key-row {
  display: flex;
  align-items: center;
  gap: 12px;
  font-family: var(--font-mono, 'JetBrains Mono', monospace);
  padding: 12px;
  background: var(--surface-alt, #FAFAFA);
  border-radius: 6px;
}

.profile-key-row code {
  flex: 1;
  font-size: 13px;
}

.profile-new-key {
  font-family: var(--font-mono, 'JetBrains Mono', monospace);
  background: #fff;
  padding: 12px;
  border-radius: 6px;
  font-size: 14px;
  word-break: break-all;
  margin: 8px 0;
}

.profile-alert {
  padding: 16px;
  border-radius: 8px;
  margin-bottom: 24px;
}

.profile-alert-success { background: #E6F7EC; border: 1px solid #5FCB80; color: #1E5A35; }
.profile-alert-error   { background: #FDECEA; border: 1px solid #E57373; color: #8B2D24; }
.profile-alert-warning { background: #FFF8E1; border: 1px solid #E0B324; color: #6B5318; }

.profile-dl {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: 8px 16px;
  margin: 0;
}

.profile-dl dt { color: var(--foreground-muted, #555); font-size: 13px; }
.profile-dl dd { margin: 0; font-size: 14px; }

/* Le bloc « V2 — Dashboard charts » (.dashboard-gran-toggle,
   .dashboard-charts-grid, .dashboard-chart-card) est parti le 2026-09-01 avec
   la page « Tableau de bord » : plus aucun gabarit ne portait ces classes. */

/* ----------------------- V3 — Filter toolbar + drawer flashes ----------------------- */

.filter-toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  background: var(--card, #FFFFFF);
  border: 1px solid var(--border, #E1E4EC);
  border-radius: 8px;
  position: sticky;
  top: 0;
  z-index: 10;
  flex-wrap: wrap;
  box-shadow: 0 1px 2px rgba(14,17,38,.04), 0 4px 12px -4px rgba(14,17,38,.04);
  margin-bottom: 16px;
  animation: kpiFadeIn .5s cubic-bezier(.2,.8,.2,1) 240ms backwards;
}

.filter-toolbar input[type="search"] {
  flex: 1;
  min-width: 200px;
  max-width: 320px;
  padding: 6px 12px;
  border: 1px solid var(--border, #E5E5E5);
  border-radius: 6px;
  font-size: 14px;
  font-family: inherit;
}

.filter-inline-select {
  padding: 6px 12px;
  border: 1px solid var(--border, #E5E5E5);
  border-radius: 6px;
  font-size: 14px;
  font-family: inherit;
  background: #fff;
}

.btn-filter-advanced {
  margin-left: auto;
  padding: 6px 12px;
  border: 1px solid var(--border, #E5E5E5);
  background: transparent;
  border-radius: 6px;
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
  position: relative;
}

.filter-badge {
  background: var(--marine, #2C3A52);
  color: #fff;
  border-radius: 10px;
  padding: 1px 8px;
  font-size: 11px;
  margin-left: 6px;
}

.drawer-flash {
  padding: 12px 16px;
  margin: 12px 0;
  border-radius: 6px;
  font-size: 14px;
}

/* /!\ Ces trois encadres etaient des APLATS CLAIRS (#E6F7EC, #FDECEA) avec un texte
   sombre : herites du theme clair, ils restaient blancs au milieu d'une page noire.
   Trouves a l'oeil sur « Mes traders » le 2026-08-16. Ils passent au principe du
   theme sombre : la couleur ne remplit plus le fond, elle le TEINTE (voile a 12 %),
   et c'est le TEXTE qui la porte en clair. Meme construction que `-warning`, qui
   etait deja le seul des trois a etre bati ainsi. */
.drawer-flash-success {
  background: rgba(74, 222, 128, 0.12);
  border: 1px solid rgba(74, 222, 128, 0.40);
  color: var(--success);
}

.drawer-flash-error {
  background: rgba(248, 113, 113, 0.12);
  border: 1px solid rgba(248, 113, 113, 0.40);
  color: var(--error);
}

/* Avertissement : ce qui suit n'est pas faux, mais ne dit pas ce qu'on croit. Le rejeu,
   typiquement. Distinct de `-error` à dessein — réutiliser le rouge crierait à la panne
   là où il n'y en a aucune. Même ocre que `.badge-warning` et que `.mode-replay` : sur
   une page, tout ce qui porte cette teinte est du rejeu. */
.drawer-flash-warning {
  background: rgba(251, 191, 36, 0.12);
  border: 1px solid rgba(251, 191, 36, 0.40);
  color: var(--warning-text);   /* #8a5d00 sur fond sombre etait illisible */
}

@media (max-width: 768px) {
  .filter-toolbar { flex-direction: column; align-items: stretch; }
  .btn-filter-advanced { margin-left: 0; }
}

/* ----------------------- V4 — Filter chips (Notion-style) ----------------------- */

.chips-area {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  flex: 1;
  min-width: 200px;
  align-items: center;
}

.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 0;
  padding: 5px 4px 5px 11px;
  background: rgba(74, 59, 122, 0.06);
  border: 1px solid rgba(74, 59, 122, 0.22);
  border-radius: 5px;
  font-size: 12.5px;
  line-height: 1.4;
  cursor: pointer;
  font-family: var(--font-sans, inherit);
  transition: background .18s ease, border-color .18s ease, box-shadow .18s ease, transform .18s ease;
}
.filter-chip:hover {
  background: rgba(74, 59, 122, 0.12);
  border-color: rgba(74, 59, 122, 0.45);
  box-shadow: 0 2px 6px -2px rgba(74,59,122,.25);
  transform: translateY(-1px);
}
.filter-chip .col { font-weight: 600; color: var(--foreground); }
.filter-chip .op { color: var(--muted-foreground, #6B7280); margin: 0 6px; font-size: 11px; }
.filter-chip .val { color: var(--accent); font-weight: 500; margin-right: 4px; }
.filter-chip .remove {
  width: 18px;
  height: 18px;
  border: none;
  background: transparent;
  cursor: pointer;
  color: var(--disabled-foreground);
  border-radius: 2px;
  font-size: 14px;
  line-height: 1;
  padding: 0;
  opacity: 0;
  transition: opacity .14s ease, color .14s ease, background .14s ease;
}
.filter-chip:hover .remove { opacity: 1; }
.filter-chip .remove:hover {
  background: rgba(159, 63, 78, 0.10);
  color: var(--error);
}

.filter-group {
  display: inline-flex;
  flex-direction: column;
  gap: 8px;
  border: 1px solid var(--border, #E1E4EC);
  background: rgba(250, 250, 251, .5);
  border-radius: 6px;
  padding: 8px 10px;
}
.filter-group-header {
  font-size: 9px;
  font-weight: 700;
  color: var(--accent);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  cursor: pointer;
  border: none;
  background: rgba(74, 59, 122, 0.10);
  padding: 2px 6px;
  border-radius: 3px;
  font-family: inherit;
  transition: background .14s ease;
}
.filter-group-header:hover {
  background: rgba(74, 59, 122, 0.18);
}
.filter-group-add {
  font-size: 11px;
  color: var(--muted-foreground, #5B6577);
  border: 1px dashed var(--border, #E1E4EC);
  background: transparent;
  border-radius: 4px;
  padding: 3px 8px;
  cursor: pointer;
  font-family: var(--font-sans, inherit);
  align-self: flex-start;
  font-weight: 500;
  transition: all .14s ease;
}
.filter-group-add:hover {
  color: var(--accent);
  border-color: var(--accent);
  border-style: solid;
  background: rgba(74, 59, 122, 0.04);
}
.filter-group-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.filter-group-remove {
  border: none;
  background: transparent;
  color: var(--disabled-foreground);
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  padding: 0 4px;
  border-radius: 2px;
  transition: color .14s ease, background .14s ease;
}
.filter-group-remove:hover {
  color: var(--error);
  background: rgba(159, 63, 78, 0.08);
}
.filter-top-logic {
  font-size: 9px;
  font-weight: 700;
  color: var(--muted-foreground);
  text-transform: uppercase;
  letter-spacing: 0.16em;
  border: none;
  background: transparent;
  border-radius: 3px;
  padding: 0 4px;
  cursor: pointer;
  font-family: inherit;
  align-self: center;
  transition: color .14s ease;
}
.filter-top-logic:hover {
  color: var(--accent);
}

.btn-add-filter {
  font-size: 12.5px;
  padding: 6px 12px;
  border: 1px dashed #C9CDD7;
  background: transparent;
  border-radius: 5px;
  color: var(--muted-foreground);
  cursor: pointer;
  font-family: var(--font-sans, inherit);
  font-weight: 500;
  transition: all .18s ease;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.btn-add-filter:hover {
  border-color: var(--accent);
  color: var(--accent);
  border-style: solid;
  background: rgba(74, 59, 122, 0.04);
  transform: translateY(-1px);
}

.icon-btn {
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 1px solid var(--border, #E1E4EC);
  background: var(--card);
  border-radius: 5px;
  cursor: pointer;
  font-family: var(--font-sans, inherit);
  color: var(--muted-foreground);
  transition: all .18s ease;
}
.icon-btn:hover {
  border-color: var(--accent);
  color: var(--accent);
  background: rgba(74, 59, 122, 0.05);
  box-shadow: 0 2px 6px -2px rgba(74,59,122,.20);
}
.icon-btn[data-active="1"] {
  background: rgba(74, 59, 122, 0.08);
  color: var(--accent);
  border-color: var(--accent);
}

/* Search input — focus ring violet */
.filter-toolbar .search-wrapper,
.filter-toolbar > div:has(> input[type="text"][name="q"]),
.filter-toolbar .search-input-wrapper {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: var(--background);
  transition: border-color .18s ease, background .18s ease, box-shadow .18s ease;
}
.filter-toolbar .search-wrapper:focus-within,
.filter-toolbar .search-input-wrapper:focus-within {
  border-color: var(--accent);
  background: var(--card);
  box-shadow: 0 0 0 3px rgba(74, 59, 122, 0.10);
}
.filter-toolbar input[type="text"][name="q"] {
  border: none;
  outline: none;
  background: transparent;
  font-size: 13px;
  font-family: inherit;
  min-width: 180px;
}

/* ── Les POPOVERS de filtre, repasses en SOMBRE (2026-08-17) ──
   Remarque du fondateur, sur la liste des operateurs : « revois le style des listes
   deroulantes dans les filtres, ca ne colle pas avec le theme ».

   /!\ Ce bloc entier datait du theme CLAIR, abandonne le 2026-08-16, et il le trahissait
   de trois facons a la fois :
   - des REPLIS en dur (`var(--card, #FFFFFF)`) : la variable existe, donc le repli ne
     servait jamais... sauf a masquer que la valeur n'avait pas ete revue ;
   - des survols et des ombres en `rgba(0,0,0,…)`, c'est-a-dire du NOIR pose sur du noir :
     invisibles ;
   - et surtout AUCUN fond ni AUCUNE couleur de texte sur les champs et les listes. Un
     `<select>` sans fond declare retombe sur celui du navigateur, donc BLANC : c'est
     exactement ce que le fondateur voyait, une liste blanche au milieu d'un panneau
     sombre.

   /!\ Les survols passent au blanc translucide (`rgba(255,255,255,…)`), la seule teinte
   qui eclaircisse un fond sombre. C'est le meme geste que partout ailleurs dans cette
   feuille ; le noir translucide y assombrit et ne se voit pas. */
.filter-popover {
  position: absolute;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 8px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55);
  min-width: 240px;
  max-width: 360px;
  z-index: 1000;
  font-family: var(--font-sans, inherit);
  font-size: 13px;
  color: var(--foreground);
}
.filter-popover .picker-search {
  width: 100%;
  padding: 7px 11px;
  color: var(--foreground);
  background-color: rgba(255, 255, 255, 0.035);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-size: 13px;
  margin-bottom: 6px;
  font-family: inherit;
  outline: none;
}
.filter-popover .picker-search::placeholder { color: var(--disabled-foreground); }
.filter-popover .picker-search:focus {
  border-color: rgba(180, 168, 255, 0.5);
  background-color: rgba(180, 168, 255, 0.06);
}
.filter-popover .picker-list {
  max-height: 320px;
  overflow-y: auto;
}
.filter-popover .picker-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 7px 10px;
  border: none;
  background: transparent;
  color: var(--foreground);
  cursor: pointer;
  font-size: 13px;
  font-family: inherit;
  border-radius: var(--radius-sm);
}
.filter-popover .picker-item:hover {
  background: rgba(255, 255, 255, 0.06);
  color: var(--foreground);
}

.popover-section {
  margin-bottom: 10px;
}
.popover-label {
  display: block;
  font-family: var(--font-mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted-foreground);
  margin-bottom: 5px;
}
.popover-input,
.popover-op-select {
  width: 100%;
  padding: 7px 11px;
  color: var(--foreground);
  /* `background-color` et PAS le raccourci `background` : celui-ci remettrait
     `background-image` a `none`, et effacerait le chevron que certains navigateurs
     peignent sur un `<select>`. Meme piege que `.saisie`. */
  background-color: rgba(255, 255, 255, 0.035);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-size: 13px;
  font-family: inherit;
  box-sizing: border-box;
  outline: none;
}
.popover-op-select { cursor: pointer; }
.popover-input:focus,
.popover-op-select:focus {
  border-color: rgba(180, 168, 255, 0.5);
  background-color: rgba(180, 168, 255, 0.06);
}
/* Le peu qu'un navigateur accepte sur la liste DEPLIEE native. Le reste vient de
   `color-scheme: dark` sur `html` : sans lui, elle s'ouvre en blanc. Ce repli ne sert
   que si `liste.js` ne se charge pas -- sinon la liste est redessinee, cf. ci-dessous. */
.popover-op-select option { background: var(--card); color: var(--foreground); }

/* ── Les listes de la fenetre de filtre, redessinees (2026-08-17) ──
   Demande du fondateur : « inspire-toi des listes deroulantes qu'on a utilisees sur la
   fiche d'un trader ». `filter-chip-popover.js` enveloppe desormais ses `<select>` et
   appelle `liste.js` : le champ ferme devient un `.liste-bouton`, et le menu ouvert un
   `.liste-panneau` arrondi. Le natif restait blanc et carre des qu'il s'ouvrait -- une
   liste depliee native est hors d'atteinte de toute feuille de style.

   /!\ Ici le bouton prend TOUTE la largeur, a l'inverse de la barre de filtres ou il
   se dimensionne a son texte : dans un panneau de 240 px, un bouton court laisserait un
   creux a droite et casserait l'alignement avec le champ de valeur juste en dessous. */
.filter-popover .liste { display: block; width: 100%; }
.filter-popover .liste > .liste-bouton {
  width: 100%;
  padding: 7px 11px;
  font-size: 13px;
  border-radius: var(--radius-sm);
}
.filter-popover .liste > .liste-panneau { min-width: 100%; }
.popover-checkboxes {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-height: 200px;
  overflow-y: auto;
}
.popover-checkbox-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px;
  font-size: 13px;
  cursor: pointer;
}
.popover-checkbox-row:hover {
  background: rgba(255, 255, 255, 0.06);
  border-radius: var(--radius-sm);
}
.popover-between {
  display: flex;
  align-items: center;
  gap: 8px;
}
.popover-between input {
  flex: 1;
  padding: 7px 11px;
  color: var(--foreground);
  background-color: rgba(255, 255, 255, 0.035);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-size: 13px;
  font-family: inherit;
  outline: none;
}
.popover-hint {
  margin: 0;
  font-size: 12px;
  color: var(--muted-foreground, #6B7280);
  font-style: italic;
}
/* Date filter → period-preset buttons (filter-chip-popover.js renderDateEditor). */
.popover-date-presets {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin-bottom: 8px;
}
.popover-preset-btn {
  text-align: left;
  padding: 7px 10px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  font-size: 13px;
  font-family: inherit;
  color: var(--foreground);
  cursor: pointer;
}
.popover-preset-btn:hover {
  background: rgba(255, 255, 255, 0.06);
}
/* /!\ La periode retenue se marque a l'ACCENT, et non par une inversion
   fond-sombre/texte-clair : sur un theme deja sombre, l'inversion donnait un bouton
   presque blanc, le seul aplat clair de toute l'application. */
.popover-preset-btn.active {
  background: rgba(180, 168, 255, 0.14);
  color: var(--accent);
  border-color: rgba(180, 168, 255, 0.35);
}
.popover-preset-custom {
  border-top: 1px solid var(--border);
  border-radius: 0;
  margin-top: 2px;
}
.popover-date-custom {
  margin-top: 4px;
}
.popover-actions {
  border-top: 1px solid var(--border);
  padding-top: 8px;
  margin-top: 8px;
}
/* `#C0392B` etait un rouge de theme clair, absent de la palette : le rouge semantique
   est `--error`. */
.popover-delete {
  background: transparent;
  color: var(--error);
  border: none;
  font-size: 12px;
  cursor: pointer;
  font-family: inherit;
  padding: 5px 8px;
  width: 100%;
  text-align: left;
  border-radius: var(--radius-sm);
}
.popover-delete:hover {
  background: rgb(248 113 113 / 0.12);
}

/* ----------------------- V4 — Convert-to-group buttons ----------------------- */

.popover-convert-and,
.popover-convert-or {
  background: transparent;
  color: var(--muted-foreground, #6B7280);
  border: none;
  font-size: 12px;
  cursor: pointer;
  font-family: inherit;
  padding: 4px 8px;
  width: 100%;
  text-align: left;
}
.popover-convert-and:hover,
.popover-convert-or:hover {
  background: rgba(0, 0, 0, 0.05);
  border-radius: 4px;
}

/* ----------------------- V4 — Sort + Group popovers ----------------------- */

.sort-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 8px;
}

.sort-row {
  display: flex;
  align-items: center;
  gap: 6px;
}

.sort-row select {
  flex: 1;
  padding: 4px 8px;
  border: 1px solid var(--border, #E5E5E5);
  border-radius: 4px;
  font-size: 13px;
  font-family: inherit;
}

.sort-dir-btn,
.sort-remove-btn {
  width: 28px;
  height: 28px;
  border: 1px solid var(--border, #E5E5E5);
  background: transparent;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.sort-dir-btn:hover,
.sort-remove-btn:hover {
  background: rgba(0, 0, 0, 0.05);
}

.popover-add-sort {
  width: 100%;
  padding: 6px 10px;
  border: 1px dashed var(--border, #E5E5E5);
  background: transparent;
  border-radius: 4px;
  font-size: 12px;
  color: var(--muted-foreground, #6B7280);
  cursor: pointer;
  font-family: inherit;
  margin-bottom: 8px;
}

.popover-clear-sort {
  background: transparent;
  color: var(--muted-foreground, #6B7280);
  border: none;
  font-size: 12px;
  cursor: pointer;
  font-family: inherit;
  padding: 4px 8px;
  width: 100%;
  text-align: left;
}

.popover-radio-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px;
  font-size: 13px;
  cursor: pointer;
  border-radius: 4px;
}
.popover-radio-row:hover {
  background: rgba(0, 0, 0, 0.04);
}

.group-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* /profile/strategies — cards grid */
.strat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
  gap: 16px;
}
.strat-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-left: 3px solid var(--border);
  border-radius: 10px;
  padding: 16px 18px;
  box-shadow: 0 1px 2px rgba(14,17,38,.04), 0 8px 24px -8px rgba(14,17,38,.08);
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
  cursor: pointer;
}
.strat-card:hover {
  transform: translateY(-2px);
  border-color: rgba(74,59,122,.25);
  box-shadow: 0 2px 4px rgba(14,17,38,.06), 0 14px 32px -8px rgba(74,59,122,.16);
}
/* Light colored left border by status. */
.strat-card--active { border-left-color: var(--success, #16a34a); }
.strat-card--active:hover { border-left-color: var(--success, #16a34a); }
.strat-card--archived { border-left-color: var(--disabled-foreground); }
.strat-card--archived:hover { border-left-color: var(--disabled-foreground); }
.strat-card .card-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
}
.strat-card .card-head-id { min-width: 0; }
.strat-card .name {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -.01em;
}
.strat-card .inst {
  font-size: 12px;
  color: var(--muted-foreground);
  margin-top: 2px;
}
.strat-card .params {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 8px 10px;
  padding: 12px 14px;
  background: var(--background);
  border-radius: 6px;
  margin-bottom: 10px;
}
.strat-card .params--essentials {
  grid-template-columns: repeat(5, 1fr);
}
.strat-card .param { min-width: 0; }
.strat-card .param .label {
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--disabled-foreground);
  font-weight: 600;
  margin-bottom: 3px;
  white-space: nowrap;
}
.strat-card .param .val {
  font-size: 15px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.strat-card .param .val--text {
  font-size: 13px;
  font-variant-numeric: normal;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.strat-card .param .val-unit {
  font-size: 10px;
  font-weight: 500;
  color: var(--muted-foreground);
}
/* Période de trading + blackout news (compact). */
.strat-card .strat-period {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 14px;
  padding: 8px 12px;
  margin-bottom: 10px;
  background: var(--background);
  border-radius: 6px;
  font-size: 11.5px;
  color: var(--muted-foreground);
}
.strat-card .strat-period .sp-item {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.strat-card .strat-period svg { flex: none; opacity: .75; }
.strat-card .qty-input-inline {
  width: 58px;
  padding: 3px 6px;
  font-size: 14px;
  border: 1px solid #C9CDD7;
  border-radius: 4px;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.strat-card .desc {
  font-size: 13px;
  color: var(--muted-foreground);
  line-height: 1.5;
  margin-bottom: 12px;
}
.strat-card .foot {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--muted);
  font-size: 12px;
  color: var(--muted-foreground);
}
.strat-card .foot-qty {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--disabled-foreground);
  font-weight: 600;
}
.strat-card .foot-qty b {
  color: var(--foreground);
  font-variant-numeric: tabular-nums;
}
.strat-card .foot .open-link { margin-left: auto; }
.strat-card .notes-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  color: var(--accent);
}
.strat-card .open-link {
  font-size: 12px;
  color: var(--accent);
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
}
.strat-card .open-link:hover { text-decoration: underline; }

/* ── Refonte liste U3 §4 : carte enrichie (pill statut, stats, meta, perf) ── */
.strat-card .card-head {
  align-items: flex-start;
  justify-content: space-between;
}
.strat-card .card-head-right {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  flex: none;
}
/* Pill de statut (Actif=vert · Inactif=gris · Brouillon=ambre). */
.strat-card .st {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  padding: 3px 9px;
  border-radius: 20px;
  white-space: nowrap;
}
.strat-card .st .dot { width: 7px; height: 7px; border-radius: 50%; }
.strat-card .st-actif { background: #e7f1ec; color: #3f7d5e; }
.strat-card .st-actif .dot { background: #3f7d5e; }
.strat-card .st-inactif { background: #eceef2; color: #6b7280; }
.strat-card .st-inactif .dot { background: #9aa0ac; }
.strat-card .st-brouillon { background: #f3f1e6; color: #b8860b; }
.strat-card .st-brouillon .dot { background: #b8860b; }
/* Boutons d'action de carte (Dupliquer / Supprimer). */
.strat-card .cardacts { display: flex; gap: 4px; }
.strat-card .cardact-form { display: inline; margin: 0; }
.strat-card .ca {
  width: 28px; height: 28px;
  display: grid; place-items: center;
  border: 1px solid var(--border);
  background: var(--card);
  border-radius: 7px;
  cursor: pointer;
  color: var(--muted-foreground);
  padding: 0;
}
.strat-card .ca:hover { background: var(--background); color: var(--foreground); }
.strat-card .ca-del:hover { background: #f9efec; color: #c0654e; border-color: #e7d2ca; }
/* Grille de stats 5 colonnes. */
.strat-card .stats--5 {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 10px;
  background: var(--background);
  border: 1px solid var(--muted);
  border-radius: 8px;
  padding: 11px 13px;
  margin-bottom: 10px;
}
.strat-card .stats--5 .stat { min-width: 0; }
.strat-card .stats--5 .stat .l {
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--disabled-foreground);
  font-weight: 600;
  white-space: nowrap;
}
.strat-card .stats--5 .stat .v {
  font-size: 15px;
  font-weight: 700;
  margin-top: 3px;
  font-variant-numeric: tabular-nums;
}
.strat-card .stats--5 .stat .v--text { font-size: 13px; font-variant-numeric: normal; }
.strat-card .stats--5 .stat .val-unit { font-size: 10px; font-weight: 500; color: var(--muted-foreground); }
/* Badge break-even. */
.strat-card .be { font-size: 11px; margin-top: 2px; color: #3f7d5e; font-weight: 600; }
/* Ligne meta (sessions · news · caps/jour). */
.strat-card .meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 14px;
  color: var(--muted-foreground);
  font-size: 12px;
  margin-bottom: 10px;
}
.strat-card .meta .it { display: inline-flex; align-items: center; gap: 6px; }
.strat-card .meta svg { flex: none; opacity: .75; }
/* Résumé de perf. */
.strat-card .perf {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 16px;
  font-size: 12.5px;
  color: var(--muted-foreground);
  border-top: 1px dashed var(--border);
  padding-top: 11px;
  margin-bottom: 4px;
}
.strat-card .perf b { color: var(--foreground); }
.strat-card .perf .pnl.pos { color: #3f7d5e; font-weight: 700; }
.strat-card .perf .pnl.neg { color: #c0654e; font-weight: 700; }
.strat-card .perf.empty { color: var(--disabled-foreground); font-style: italic; }
.strat-card .foot .acc {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

/* Responsive : essentials grid → 2 columns on mobile. */
@media (max-width: 640px) {
  .strat-card .params--essentials,
  .strat-card .stats--5 {
    grid-template-columns: 1fr 1fr;
  }
  .strat-card .strat-period {
    flex-direction: column;
    align-items: flex-start;
  }
}

/* ── Drawer trade : bloc « Calcul SL/TP · ATR » + tags de provenance ─────────
   .tagsrc = pastille indiquant d'où vient un bloc de paramètres (App/edge vs
   NinjaTrader). .calc = liste de dérivation monospace (ATR → SL → volume → TP).
   Aligné sur la maquette + les tokens TVNI. */
.tagsrc {
  margin-left: auto;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .02em;
  padding: 2px 9px;
  border-radius: 999px;
}
.tagsrc.app { background: rgb(74 59 122 / .10); color: var(--accent, #4A3B7A); }
.tagsrc.nt  { background: rgb(59 110 168 / .12); color: var(--info, #3B6EA8); }
.atrbadge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: rgb(181 121 60 / .10);
  border: 1px solid rgb(181 121 60 / .25);
  color: #9A6328;
  border-radius: 8px;
  padding: 7px 11px;
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  margin-bottom: 11px;
}
.calc {
  background: linear-gradient(180deg, #F7F8FC, #F1F3F9);
  border: 1px solid var(--border, #E1E4EC);
  border-radius: 10px;
  padding: 13px 15px;
  font-size: 13px;
  font-family: var(--font-mono, 'JetBrains Mono', monospace);
}
.calc .line {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 5px 0;
  border-bottom: 1px dashed var(--border, #E1E4EC);
}
.calc .line:last-child { border: 0; }
.calc .lbl { color: var(--muted-foreground, #5B6577); }
.calc .val { font-weight: 500; font-variant-numeric: tabular-nums; text-align: right; }
.calc .val .arr { color: var(--accent, #4A3B7A); }
.calc .step { font-size: 10.5px; color: var(--disabled-foreground, #9CA3B0); font-family: var(--font-sans, 'Inter', sans-serif); }
.coh {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--success, #3F6E5C);
}
.coh .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--success, #3F6E5C); }

/* ── Bascule segmentée Infos / Suivi (drawer contact v2, §C) ─────────────────
   Control à deux états dans le sous-en-tête ; le bouton actif prend un fond
   blanc surélevé. Le badge « ct » porte le nombre d'activités. */
.drawer-seg {
  display: inline-flex;
  background: var(--muted, #F2F3F7);
  border-radius: 10px;
  padding: 4px;
  margin-top: 14px;
}
/* Bascule des deux vues + masquage des chips en vue Suivi. */
.contact-view.is-hidden { display: none; }

/* ── Stratégies open source (onglet « Toutes les stratégies ») ─────────────── */
.os-tabs { gap: 6px; }

/* ── Sous-onglets secondaires (Pages / Mails d'un événement) ───────────────── */
/* Segmented control léger : plus discret que les onglets principaux. */
.ev-subtabs {
  background: var(--muted, #F7F7FB);
  border: 1px solid var(--border, #E1E4EC);
  border-radius: 8px;
  padding: 4px;
  gap: 4px;
}
.ev-subtabs a {
  padding: 5px 12px;
  font-size: 12px;
  border-radius: 6px;
}
.ev-subtabs a.active {
  background: var(--card, #fff);
  border-color: var(--border, #E1E4EC);
}
/* « Toutes les stratégies » : toolbar alignée sur la command-bar de
   « Mes stratégies » (search-wrapper stylé + tri stylé). */
.os-command-bar {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-bottom: 16px;
}
/* Recherche — même look que .filter-toolbar .search-wrapper (focus ring violet). */
.os-search-wrapper {
  flex: 1 1 240px; min-width: 200px; max-width: 360px;
  display: flex; align-items: center; gap: 6px;
  padding: 5px 10px;
  border: 1px solid var(--border); border-radius: 5px;
  background: var(--background);
  transition: border-color .18s ease, background .18s ease, box-shadow .18s ease;
}
.os-search-wrapper:focus-within {
  border-color: var(--accent);
  background: var(--card);
  box-shadow: 0 0 0 3px rgba(74, 59, 122, 0.10);
}
.os-search-wrapper input {
  border: none; outline: none; background: transparent;
  font-size: 13px; font-family: inherit; color: var(--foreground);
  flex: 1; min-width: 0;
}
.os-sort { display: inline-flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--muted-foreground); flex-shrink: 0; }
.os-sort > label { display: inline-flex; align-items: center; gap: 5px; font-weight: 500; }
/* Sélecteur stylé (caret custom) cohérent avec les contrôles de la charte. */
.os-sort-control { position: relative; display: inline-flex; align-items: center; }
.os-sort-control select {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  padding: 6px 28px 6px 12px; font-size: 12.5px; font-weight: 500;
  border: 1px solid var(--border); border-radius: 5px;
  background: var(--card); color: var(--foreground); font-family: inherit;
  cursor: pointer; transition: all .18s ease;
}
.os-sort-control select:hover { border-color: var(--accent); background: rgba(74, 59, 122, 0.04); }
.os-sort-control select:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(74, 59, 122, 0.10);
}
.os-sort-caret {
  position: absolute; right: 9px; pointer-events: none;
  color: var(--muted-foreground);
}
.os-card { border-left-color: var(--accent, #4a3b7a); }
.os-card .params {
  grid-template-columns: repeat(3, 1fr);
}
.os-badge-mine {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 10.5px; font-weight: 600; letter-spacing: .02em;
  padding: 2px 8px; border-radius: 999px;
  background: rgba(22,163,74,.10); color: var(--success, #16a34a);
  border: 1px solid rgba(22,163,74,.25); white-space: nowrap;
}
.os-perf {
  display: flex; align-items: center; gap: 14px;
  padding: 10px 12px; margin-bottom: 10px;
  background: var(--background); border-radius: 6px;
}
.os-perf-stats {
  flex: 1; display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px 10px;
}
.os-stat .os-stat-lbl {
  font-size: 9px; text-transform: uppercase; letter-spacing: .1em;
  color: var(--disabled-foreground); font-weight: 600; margin-bottom: 2px;
}
.os-stat .os-stat-val {
  font-size: 13.5px; font-weight: 600; font-variant-numeric: tabular-nums;
}
.os-stat-val.pos, .os-kpi-val.pos { color: var(--success, #16a34a); }
.os-stat-val.neg, .os-kpi-val.neg { color: var(--error, #dc2626); }
.os-spark { flex: none; color: var(--accent, #4a3b7a); display: flex; align-items: center; }
.os-spark.pos { color: var(--success, #16a34a); }
.os-spark.neg { color: var(--error, #dc2626); }
.os-users-pill {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; color: var(--muted-foreground);
}
.os-users-pill svg { opacity: .7; }

/* Détail canonique. */
.os-detail-kpis {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;
}
.os-kpi {
  padding: 12px 14px; background: var(--background); border-radius: 8px;
  border: 1px solid var(--border);
}
.os-kpi-lbl {
  font-size: 9.5px; text-transform: uppercase; letter-spacing: .1em;
  color: var(--disabled-foreground); font-weight: 600; margin-bottom: 4px;
}
.os-kpi-val { font-size: 19px; font-weight: 700; font-variant-numeric: tabular-nums; }
.os-equity { color: var(--accent, #4a3b7a); }
.os-equity.pos { color: var(--success, #16a34a); }
.os-equity.neg { color: var(--error, #dc2626); }
.os-equity-lbl {
  font-size: 10px; text-transform: uppercase; letter-spacing: .1em;
  color: var(--disabled-foreground); font-weight: 600; margin-bottom: 6px;
}
@media (max-width: 720px) {
  .os-detail-kpis { grid-template-columns: repeat(2, 1fr); }
  .os-perf { flex-direction: column; align-items: stretch; }
}

/* ─────────────────────────────────────────────────────────────────────────────
   LE TABLEAU DE PALIERS (gestion d'un trade, 2026-08-20)

   La charte n'avait AUCUNE classe de tableau : le premier jet rendait des colonnes
   de largeurs aleatoires, la premiere trois fois trop large pour un nombre a une
   decimale. Les largeurs sont donc posees ici, une fois, sur les en-tetes.

   /!\ `table-layout: fixed` est ce qui fait tenir les largeurs : sans lui le
   navigateur les recalcule sur le contenu, et une ligne longue les redistribue.
   ───────────────────────────────────────────────────────────────────────────── */
.tableau--paliers {
  width: 100%; max-width: 720px;
  border-collapse: collapse; table-layout: fixed;
  margin: 4px 0 10px;
}
.tableau--paliers th {
  text-align: left; padding: 0 12px 8px 0;
  font-family: var(--font-mono);
  font-size: 10.5px; font-weight: 500;
  letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--muted-foreground);
}
.tableau--paliers td { padding: 0 12px 10px 0; vertical-align: top; }
.tableau--paliers th:nth-child(1) { width: 22%; }
.tableau--paliers th:nth-child(2) { width: 18%; }
.tableau--paliers th:nth-child(3) { width: 32%; }
.tableau--paliers th:nth-child(4) { width: 20%; }
.tableau--paliers th:nth-child(5) { width: 8%; padding-right: 0; }
.tableau--paliers td:last-child { padding-right: 0; text-align: right; }
.tableau--paliers .saisie { width: 100%; }

/* Les trois modeles prets a l'emploi, au-dessus du tableau. */
.modeles-gestion {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin-bottom: 14px;
}
.modeles-gestion .etiquette { margin: 0; }
.modeles-gestion .saisie-note { flex-basis: 100%; margin: 0; }

/* Une case a cocher qui suit un bloc de champs a besoin d'air (2026-08-20).
   /!\ DEUX noms, pas un seul porte par deux objets : `.champ--espace` marque un
   champ isole, `.champs--espace` la GRILLE de champs. Ils partagent une valeur,
   jamais une classe -- ce depot a deja paye une classe unique posee sur deux
   objets differents (cf. `.puce`), et un modificateur se lit avec le bloc qu'il
   modifie.
   `.champs--espace` sert la ou une grille de champs suit une case a cocher ou une
   bascule : sans lui, elle la touche. Elle etait separee par le remplissage d'une
   `verre-zone`, qui posait au passage un cadre dans un cadre (2026-08-29). */
.champ--espace { margin-top: 16px; }
.champs--espace { margin-top: 16px; }

/* ============================================================
   Le GUIDE (2026-08-23)
   /!\ Ces regles vivaient dans un `{% block extra_css %}` de 200 lignes qui
   redeclarait un `:root` en THEME CLAIR -- une page blanche au milieu d'une
   application sombre. Elles sont ici pour la meme raison que tout le reste :
   un ecran ne possede pas ses couleurs.
   ============================================================ */
.guide-hero { margin: 0 0 var(--space-6); }
.guide-eyebrow {
  font-family: var(--font-mono); font-size: var(--text-eyebrow);
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  color: var(--accent); margin: 0 0 var(--space-3);
}
.guide h1 { font-size: var(--text-display-2); line-height: 1.1;
            letter-spacing: var(--tracking-tight); margin: 0 0 var(--space-3);
            font-weight: 600; }
.guide-chapeau { font-size: 16px; line-height: var(--leading-relaxed);
                 color: var(--muted-foreground); max-width: 62ch; margin: 0; }
.guide-article h2 { font-size: var(--text-h4); font-weight: 500;
                    letter-spacing: var(--tracking-tight);
                    margin: var(--space-8) 0 var(--space-3); }
.guide-article p, .guide-article li {
  font-size: 15px; line-height: var(--leading-relaxed);
  color: var(--muted-foreground); max-width: 68ch;
}
.guide-article p { margin: 0 0 var(--space-3); }
.guide-article ul { padding-left: 20px; margin: 0 0 var(--space-3); }
.guide-article li { margin: 6px 0; }
.guide-article strong { color: var(--foreground); font-weight: 500; }
/* /!\ `:not(.btn)` N'EST PAS DECORATIF. Sans lui, cette regle (0,1,1) l'emporte
   sur `.btn-primary` (0,1,0) et repeint le libelle du bouton en `--accent` —
   c'est-a-dire du violet sur un fond violet. Le bouton « Aller a mes traders »
   du dernier chapitre etait un rectangle VIDE, et le test ne l'a pas vu : il
   verifiait le `href`, pas la lisibilite. Trouve a l'oeil le 2026-08-23. */
.guide-article a:not(.btn) { color: var(--accent); text-decoration: none;
                             border-bottom: 1px solid rgba(180,168,255,0.3); }
.guide-article a:not(.btn):hover { border-bottom-color: var(--accent); }
/* Un AVERTISSEMENT dans le corps d'un chapitre : un filet d'accent a gauche,
   pas un emoji. La charte proscrit les pictogrammes dans l'interface, et un
   « /!\ » en toutes lettres serait pire. */
.guide-article p.guide-attention {
  border-left: 2px solid var(--warning);
  padding-left: var(--space-3);
  color: var(--muted-foreground);
}
/* Un SOUS-TITRE dans une section : c'est lui qui porte le nom d'une plateforme
   sous une regle qui vaut pour toutes (guide, chapitres 2 et 4). Sans cette
   regle, `h3` retombe sur le style de l'agent utilisateur -- donc PLUS GROS que
   le `h2` de la section qui le contient. */
/* Le filet d'accent n'est pas décoratif : à 15,5 px sans lui, ce sous-titre se
   lisait comme une simple ligne en gras au milieu du texte (vu à l'écran le
   2026-08-28). Il marque le début du bloc PROPRE À UNE PLATEFORME -- violet,
   la couleur de l'application, là où `guide-attention` prend l'ambre. */
.guide-article h3 { font-size: 16px; font-weight: 500;
                    color: var(--foreground);
                    letter-spacing: var(--tracking-tight);
                    border-left: 2px solid var(--accent);
                    padding-left: var(--space-3);
                    margin: var(--space-6) 0 var(--space-3); }
/* /!\ `nowrap`, comme le `.k` de la page Plateformes : un libellé d'interface
   coupé en deux (« NinjaScript Add- / On… ») ne se retrouve plus à l'écran de
   la plateforme, et c'est tout l'intérêt de le recopier au caractère près. */
.guide-article .k { font-family: var(--font-code); font-size: 13px;
                    background: var(--muted); border: 1px solid var(--border);
                    border-radius: var(--radius-sm); padding: 1px 6px;
                    white-space: nowrap;
                    color: var(--foreground); }

/* La carte du gestionnaire et celle des jalons. */
.guide-gestionnaire { margin-bottom: var(--space-4); }
.guide-gest-nom { font-size: var(--text-h4); font-weight: 500;
                  color: var(--foreground); margin: 0 0 2px; }
.guide-gest-contacts { display: flex; gap: var(--space-4); flex-wrap: wrap;
                       margin: var(--space-3) 0 0; }
.guide-gest-contacts a {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--text-body-sm); color: var(--accent); text-decoration: none;
}
.guide-gest-contacts a:hover { text-decoration: underline; }

.guide-jalon-liste { list-style: none; margin: var(--space-3) 0 var(--space-4);
                     padding: 0; display: grid; gap: 2px; }
.guide-jalon { display: flex; align-items: center; gap: var(--space-3);
               padding: 9px 0; font-size: var(--text-body);
               color: var(--disabled-foreground); }
.guide-jalon-marque {
  display: grid; place-items: center; width: 22px; height: 22px;
  flex: 0 0 22px; border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  font-family: var(--font-mono); font-size: 11px;
}
.guide-jalon.est-fait { color: var(--muted-foreground); }
.guide-jalon.est-fait .guide-jalon-marque {
  background: rgba(74, 222, 128, 0.14); border-color: rgba(74, 222, 128, 0.45);
  color: var(--success);
}
.guide-jalon.est-courant { color: var(--foreground); font-weight: 500; }
.guide-jalon.est-courant .guide-jalon-marque {
  border-color: var(--accent); color: var(--accent);
}
.guide-note { font-size: var(--text-body-sm); color: var(--muted-foreground);
              border-left: 2px solid var(--accent); padding-left: var(--space-3);
              margin: var(--space-4) 0; }

/* Le sommaire de l'accueil. */
.guide-sommaire { margin-top: var(--space-8); }
.guide-sommaire-titre { font-size: var(--text-h4); font-weight: 500;
                        margin: 0 0 var(--space-3); }
.guide-carte {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-4); margin-bottom: var(--space-2);
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius-lg); text-decoration: none;
  transition: border-color var(--motion-base) var(--ease-out),
              background var(--motion-base) var(--ease-out);
}
.guide-carte:hover { border-color: var(--accent-ring-strong);
                     background: var(--accent-wash); }
.guide-carte svg { color: var(--muted-foreground); flex: 0 0 auto; }
.guide-carte-num { font-family: var(--font-mono); font-size: var(--text-h4);
                   color: var(--accent); flex: 0 0 auto; width: 22px; }
.guide-carte-corps { display: grid; gap: 3px; min-width: 0; flex: 1 1 auto; }
.guide-carte-titre { font-size: var(--text-body); font-weight: 500;
                     color: var(--foreground); }
.guide-carte-resume { font-size: var(--text-body-sm);
                      color: var(--muted-foreground); }

/* Un chapitre : le rail a gauche, l'article a droite. */
.guide-doc { display: grid; grid-template-columns: 250px 1fr;
             gap: var(--space-12); align-items: start; }
.guide-rail { position: sticky; top: var(--space-6); }
.guide-rail-retour { display: inline-flex; align-items: center; gap: 6px;
                     font-size: var(--text-caption);
                     color: var(--muted-foreground); text-decoration: none;
                     margin-bottom: var(--space-4); }
.guide-rail-retour:hover { color: var(--foreground); }
.guide-rail-liste { list-style: none; margin: 0; padding: 0; display: grid;
                    gap: 1px; }
.guide-rail-liste a {
  display: flex; align-items: baseline; gap: var(--space-2);
  padding: 8px 10px; border-radius: var(--radius-md);
  font-size: var(--text-body-sm); color: var(--muted-foreground);
  text-decoration: none; line-height: 1.35;
}
.guide-rail-liste a:hover { background: var(--muted); color: var(--foreground); }
.guide-rail-liste a.est-courant {
  background: var(--muted); color: var(--foreground); font-weight: 500;
  box-shadow: inset 2px 0 0 var(--accent);
}
.guide-rail-num { font-family: var(--font-mono); font-size: 10.5px;
                  color: var(--disabled-foreground); flex: 0 0 auto; }
.guide-article { min-width: 0; }
.guide-suite { display: flex; gap: var(--space-3); flex-wrap: wrap;
               margin-top: var(--space-8); padding-top: var(--space-6);
               border-top: 1px solid var(--border); }

@media (max-width: 900px) {
  .guide-doc { grid-template-columns: 1fr; gap: var(--space-6); }
  .guide-rail { position: static; }
}

/* ==========================================================================
   La VISITE GUIDEE (2026-08-23) : une bulle posee sur l'element dont elle
   parle, le reste de l'ecran assombri. Style A de la maquette, choisi par le
   fondateur.

   /!\ Le voile est SOUS le halo et la bulle, jamais au-dessus : c'est lui qui
   efface la page, et l'element commente doit rester net.
   ========================================================================== */
.visite-halo {
  position: fixed; z-index: 901; pointer-events: none;
  border-radius: var(--radius-md);
  box-shadow: 0 0 0 2px var(--accent), 0 0 0 8px rgba(180,168,255,0.10),
              0 0 40px rgba(139,127,224,0.45);
  /* Le trou dans le voile : c'est ce rectangle qui laisse voir l'element. */
  background: transparent;
  /* L'`outline` géante EST le voile : elle assombrit tout sauf ce rectangle.
     Une seule couche, sinon la page devient noire. */
  outline: 9999px solid rgba(6, 7, 12, 0.70);
  animation: visite-apparait .18s ease-out;
  transition: top .2s ease, left .2s ease, width .2s ease, height .2s ease;
}
.visite-bulle {
  position: fixed; z-index: 902; box-sizing: border-box;
  /* /!\ `100vw` ignore le `zoom` du corps : a l'echelle 1.1, une bulle bornee a
     `100vw` deborde encore de 10 %. Meme correction que `.bulle` et `--fenetre`. */
  max-width: calc((100vw - 24px) / var(--echelle));
  background: var(--card); border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg); padding: 16px 18px;
  box-shadow: 0 20px 50px rgba(0,0,0,0.6);
  display: flex; flex-direction: column; gap: 10px;
  animation: visite-monte .2s ease-out;
  /* /!\ AUCUNE transition sur `top`/`left`, et ce n'est pas un detail de gout.
     L'application declare `transition: all` en global ; une bulle qui saute d'un
     element a l'autre partirait donc en glissade a chaque « Suivant », et
     surtout : tant que la transition n'est pas finie, la position LUE est celle
     du depart. Le calcul de placement, qui mesure puis repositionne, se
     retrouvait a lire des zeros. Vu au banc le 2026-08-23. */
  transition: none;
}
.visite-bulle.sans-animation { animation: none; }
.visite-tete { display: flex; align-items: baseline; justify-content: space-between;
               gap: 10px; }
.visite-titre { font-size: var(--text-body); font-weight: 500; color: var(--foreground); }
.visite-compteur { font-family: var(--font-mono); font-size: var(--text-caption);
                   color: var(--accent); flex: 0 0 auto; }
.visite-texte { margin: 0; font-size: var(--text-body-sm); line-height: 1.55;
                color: var(--muted-foreground); }
/* /!\ `flex-end` + `margin-right: auto` sur « Passer », et NON `space-between`
   (2026-08-30, avec l'arrivee du bouton « Precedent »). A deux enfants les deux
   ecritures donnent le meme rendu ; a TROIS, `space-between` etalait les boutons
   a intervalles egaux et « Precedent » se retrouvait seul au milieu de la bulle,
   loin du « Suivant » avec lequel il fait paire. */
.visite-pied { display: flex; align-items: center; justify-content: flex-end;
               gap: 10px; margin-top: 2px; }
.visite-passer {
  margin-right: auto;
  background: none; border: none; padding: 0; cursor: pointer;
  font-family: inherit; font-size: var(--text-caption);
  color: var(--disabled-foreground);
}
.visite-passer:hover { color: var(--muted-foreground); }
.visite-suivant, .visite-precedent { cursor: pointer; }

@keyframes visite-apparait { from { opacity: 0; } to { opacity: 1; } }
@keyframes visite-monte { from { opacity: 0; transform: translateY(6px); }
                          to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  .visite-bulle { animation: none; }
  .visite-halo { transition: none; }
}

/* /!\ La classe `guide-revoir` a ete RETIREE le 2026-08-29 avec son bouton
   (« Revoir la visite », en tete du guide). Ce bouton relancait la visite
   ENTIERE ; il n'y a plus de « tout », et le rappel vit maintenant dans le point
   d'interrogation de la barre du haut, qui reutilise `.menu` / `.menu-panneau`
   plutot que d'inventer un decor de plus. Ne pas recreer ce nom. */

/* ============================================================
   LA COQUILLE : la barre du haut, le menu, le cadre d'une page
   ============================================================
   Rapatriee depuis le `<style>` de `admin_base.html` le 2026-08-25 (chantier
   « la coquille », § 12 de la spec de l'espace collaborateur).

   /!\ **Elle habille les DEUX espaces.** Les pages du client etendent le meme
   gabarit que celles de l'equipe : tant que ce style vivait dans la page, un
   ecran client chargeait mille cent lignes de CSS d'administration dont il
   n'utilisait rien, et aucun des deux ne pouvait le corriger sans toucher
   l'autre a l'aveugle.

   /!\ Ce qui est ici est la COQUILLE, pas les ecrans. Le tiroir, les barres
   d'outils, les filtres rapides et les cartes de KPI restent dans le gabarit :
   ils seront SUPPRIMES au profit de `.tiroir`, `.filtres` et `.seg` quand leur
   ecran sera repris, pas demenages. Les ranger ici serait les classer avant de
   les jeter.
   ============================================================ */
html,body{margin:0;padding:0}
body{background:var(--background);color:var(--foreground);font-family:var(--font-sans);font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased}
a{color:inherit;text-decoration:none}

/* Topbar -- indigo gradient + global search + notif */
.topbar{position:sticky;top:0;z-index:50;background:#0F0F1A;background-image:radial-gradient(120% 200% at 20% 0%, rgba(167,139,250,.10) 0%, transparent 50%), linear-gradient(180deg, #0F0F1A 0%, #14111E 100%);border-bottom:1px solid rgba(255,255,255,.06);height:56px;box-shadow:0 1px 3px rgba(14,17,38,.20)}
.topbar-inner{display:flex;align-items:center;gap:16px;height:100%;padding:0 24px}
.mark{display:flex;align-items:center;gap:12px;text-decoration:none;color:#FFFFFF;flex-shrink:0}
.logo-img{display:inline-block;height:28px;width:auto;filter:brightness(0) invert(1)}
.wordmark{font-family:var(--font-sans);font-weight:500;font-size:16px;letter-spacing:0.20em;text-transform:uppercase;color:#FFFFFF}

/* Global search -- V4 design : right-aligned, soft compact */
.topbar-search{margin-left:auto;width:320px;display:flex;align-items:center;gap:8px;padding:0 12px;height:30px;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.10);border-radius:6px;transition:all .18s ease}
.topbar-search:focus-within{background:rgba(255,255,255,.12);border-color:rgba(167,139,250,.40);box-shadow:0 0 0 3px rgba(167,139,250,.08)}
.topbar-search svg{color:rgba(255,255,255,.55);flex-shrink:0}
.topbar-search input{flex:1;border:none;outline:none;background:transparent;color:#FFFFFF;font-size:12.5px;font-family:inherit}
.topbar-search input::placeholder{color:rgba(255,255,255,.45)}
.topbar-search .kbd{font-size:10.5px;color:rgba(255,255,255,.50);padding:1px 6px;border:1px solid rgba(255,255,255,.18);border-radius:3px;font-family:var(--font-mono);letter-spacing:0}

/* Topbar right group : tout colle à droite, gap léger */
.topbar-right{display:flex;align-items:center;gap:6px;flex-shrink:0}

/* Option C -- Soft modern, icônes discrètes */
.topbar-icon-btn{position:relative;width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.10);border-radius:6px;color:rgba(255,255,255,.78);cursor:pointer;transition:all .14s ease}
.topbar-icon-btn:hover{background:rgba(255,255,255,.12);border-color:rgba(167,139,250,.40);color:#FFFFFF}
.topbar-icon-btn .badge-dot{position:absolute;top:5px;right:5px;width:7px;height:7px;background:var(--error);border:2px solid #0F0F1A;border-radius:50%}

/* Bouton Inviter -- accent violet doux qui rappelle la marque */
/* L'appel a l'achat (2026-08-23). Plein, teinte d'accent : c'est le seul bouton
   de la barre qui demande de l'argent, il doit se distinguer de « Inviter » (qui
   est un geste gratuit) sans crier. Sur mobile il garde son libelle, contrairement
   a « Inviter » : c'est le geste qui rapporte. */
/* /!\ APLAT, sans degrade ni halo (2026-08-23, direction « B » validee) : c'est
   la voix forte de la charte (`.btn-fort`), et le seul geste d'argent de la
   coquille. Il ne s'affiche PAS sur la page profil, ou l'onglet Abonnement
   porte deja le vrai bouton — deux voix fortes sur un ecran, c'est une de trop. */
.topbar-cta{display:inline-flex;align-items:center;gap:6px;height:30px;padding:0 13px;background:var(--accent);border:1px solid var(--accent);border-radius:6px;color:var(--on-accent);text-decoration:none;font-size:12px;font-weight:600;font-family:inherit;transition:background .14s ease,border-color .14s ease;flex-shrink:0}
.topbar-cta:hover{background:var(--accent-hi);border-color:var(--accent-hi)}
.topbar-cta svg{opacity:.9}
/* Bouton « Parrainez un proche » (refonte 2026-08-23, maquette validée).
   /!\ Volontairement DIFFÉRENT de `.topbar-cta`, son voisin immédiat : verre
   violet + pastille verte du gain, contre un aplat d'accent. Deux boutons du
   même aplat côte à côte se lisent comme un seul. */
.topbar-invite-btn{display:inline-flex;align-items:center;gap:8px;height:30px;padding:0 12px;background:linear-gradient(180deg,rgba(200,190,255,.18),rgba(139,127,224,.14));border:1px solid rgba(180,168,255,.34);border-radius:8px;color:#E4DEFF;cursor:pointer;font-size:12px;font-weight:600;font-family:inherit;letter-spacing:-.005em;box-shadow:0 1px 0 rgba(255,255,255,.06) inset,0 4px 14px -8px rgba(139,127,224,.7);transition:all .16s ease;flex-shrink:0}
.topbar-invite-btn:hover{background:linear-gradient(180deg,rgba(200,190,255,.30),rgba(139,127,224,.22));border-color:rgba(180,168,255,.55);color:#FFFFFF}
.topbar-invite-btn svg{opacity:.95;flex-shrink:0}
.topbar-invite-gain{display:inline-flex;align-items:center;height:18px;padding:0 6px;background:rgba(74,222,128,.14);border:1px solid rgba(74,222,128,.28);border-radius:999px;font-size:10.5px;font-weight:600;color:#7CE7A6;letter-spacing:.01em}
/* Bouton « Télécharger le plugin NinjaTrader » (§3) -- neutre, admin + user. */
/* Sous 1024px, on réduit le bouton plugin à l'icône seule (gain de place). */

.hamburger{display:none;background:none;border:none;padding:8px;cursor:pointer}
.hamburger span{display:block;width:20px;height:1px;background:#FFFFFF;margin:5px 0;transition:transform .2s}

/* App shell : sidebar + main */
/* /!\ `--fenetre` et jamais `100vh` : cf. la section « L'ECHELLE » de charte.css. */
.app-shell{display:flex;min-height:calc(var(--fenetre) - 56px)}
.app-shell-anon{justify-content:center;align-items:flex-start}
.sidebar{width:200px;flex-shrink:0;background:var(--card);border-right:1px solid var(--border);padding:16px 12px;position:sticky;top:56px;align-self:flex-start;height:calc(var(--fenetre) - 56px);display:flex;flex-direction:column;overflow:hidden}
.sidebar nav{display:flex;flex-direction:column;gap:2px;flex:1;overflow-y:auto;min-height:0}

/* Sidebar bottom -- profile card + admin/user toggle + logout */
.sidebar-bottom{flex-shrink:0;padding-top:12px;margin-top:12px;border-top:1px solid var(--border);display:flex;flex-direction:column;gap:8px}
.sidebar-space-toggle{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:rgba(74,59,122,.06);border:1px solid rgba(74,59,122,.20);border-radius:6px;color:var(--accent);font-size:12px;font-weight:500;text-decoration:none;cursor:pointer;transition:all .14s ease}
.sidebar-space-toggle:hover{background:rgba(74,59,122,.12);border-color:rgba(74,59,122,.40)}
.sidebar-space-toggle svg{flex-shrink:0;opacity:.7}
.sidebar-profile-card{display:flex;align-items:center;gap:10px;padding:8px 10px;background:transparent;border:1px solid transparent;border-radius:6px;color:var(--foreground);text-decoration:none;cursor:pointer;transition:background .14s ease,border-color .14s ease}
.sidebar-profile-card:hover{background:var(--background);border-color:var(--border)}
.sidebar-profile-avatar{width:32px;height:32px;border-radius:50%;background:linear-gradient(135deg,var(--accent) 0%,#6B5BA3 100%);color:#FFFFFF;display:inline-flex;align-items:center;justify-content:center;font-size:12px;font-weight:600;letter-spacing:.02em;flex-shrink:0}
.sidebar-profile-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.sidebar-profile-name{font-size:13px;font-weight:600;color:var(--foreground);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sidebar-profile-email{font-size:11px;color:var(--muted-foreground);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sidebar-logout{display:flex;align-items:center;gap:8px;padding:8px 12px;font-size:12.5px;color:var(--muted-foreground);background:transparent;border:none;border-radius:6px;cursor:pointer;font-family:inherit;transition:color .14s ease,background .14s ease;text-decoration:none;width:100%;text-align:left}
.sidebar-logout:hover{color:var(--error);background:rgba(159,63,78,.06)}
.sidebar-logout svg{flex-shrink:0;opacity:.8}
.sidebar nav a, .sidebar-mobile nav a {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px; font-family: var(--font-sans); font-size: 13px;
  color: var(--muted-foreground); text-decoration: none;
  border-radius: var(--radius-md);
  transition: background var(--motion-base) var(--ease-out), color var(--motion-base) var(--ease-out);
}
.sidebar nav a:hover, .sidebar-mobile nav a:hover {
  background: var(--muted); color: var(--foreground);
}
.sidebar nav a.active, .sidebar nav a[aria-current="page"],
.sidebar-mobile nav a.active, .sidebar-mobile nav a[aria-current="page"] {
  background: rgb(74 59 122 / 0.08); color: var(--accent);
  font-weight: 500; border-left: 2px solid var(--accent); padding-left: 10px;
}
.sidebar nav a svg, .sidebar-mobile nav a svg { flex-shrink: 0; }
.sidebar-section {
  font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.22em;
  text-transform: uppercase; color: var(--accent); font-weight: 500;
  padding: 20px 12px 8px; margin-top: 12px; border-top: 1px solid var(--border);
}
/* Le mot d'un menu VIDE (2026-08-25, F3 tache 2, tour de correction 1).
   Un collaborateur a qui personne n'a encore attribue de role recevait un
   <nav> vide, sans un mot : il croyait a une panne. Le filet en pointilles
   reprend `.empty-state-notes` -- il dit « il n'y a rien ICI », par opposition
   a une carte pleine qu'on aurait pu croire coupee. */
.sidebar-vide {
  margin: 4px 0 0; padding: 14px 12px;
  border: 1px dashed var(--border); border-radius: var(--radius-md);
  font-family: var(--font-sans); font-size: 12px; line-height: 1.5;
  color: var(--muted-foreground);
}
/* ⚠️ Ce dégradé était CLAIR et codé en dur : au passage au thème sombre, le
   texte est devenu clair et se retrouvait illisible sur ce fond. C'était le
   seul vrai défaut visible de la bascule -- les panneaux, la barre latérale et
   le corps employaient déjà `var(--…)` et ont suivi tout seuls. */
.main{flex:1;padding:32px 40px 64px;min-width:0;background:var(--background);position:relative}
.main::before{content:'';position:absolute;inset:0;background-image:radial-gradient(circle,rgba(74,59,122,.04) 1px,transparent 1px);background-size:24px 24px;pointer-events:none;z-index:0}
.main > *{position:relative;z-index:1}
.main-centered{display:flex;align-items:center;justify-content:center;padding:64px 24px}

/* Drawer hamburger mobile */
.drawer-backdrop{position:fixed;inset:0;background:rgb(28 35 51 / 0.40);z-index:80;display:none;transition:opacity var(--motion-slow) var(--ease-out)}
.drawer-backdrop.open{display:block}

/* Global search dropdown */
.global-search-popover{position:absolute;z-index:200;background:#FFFFFF;border:1px solid var(--border);border-radius:8px;box-shadow:0 12px 40px rgba(14,17,38,.16),0 4px 8px rgba(14,17,38,.04);padding:6px;max-height:480px;overflow-y:auto;animation:gsFade .14s ease}
@keyframes gsFade{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}
.global-search-popover .gs-empty{padding:24px 16px;text-align:center;color:var(--muted-foreground);font-size:13px}
.global-search-popover .gs-group-label{font-size:10px;text-transform:uppercase;letter-spacing:.14em;color:var(--muted-foreground);font-weight:600;padding:10px 12px 6px}
.global-search-popover .gs-item{display:flex;align-items:center;gap:10px;padding:8px 12px;border-radius:5px;text-decoration:none;color:var(--foreground);transition:background .12s ease}
.global-search-popover .gs-item:hover,.global-search-popover .gs-item.active{background:rgba(74,59,122,.06)}
.global-search-popover .gs-icon{width:24px;height:24px;display:inline-flex;align-items:center;justify-content:center;background:var(--muted);color:var(--accent);border-radius:4px;flex-shrink:0}
.global-search-popover .gs-text{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
.global-search-popover .gs-label{font-size:13px;font-weight:500;color:var(--foreground);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.global-search-popover .gs-subtitle{font-size:11.5px;color:var(--muted-foreground);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.global-search-popover mark{background:rgba(74,59,122,.18);color:var(--accent);padding:0 2px;border-radius:2px}

.sidebar-mobile{position:fixed;top:0;left:0;bottom:0;width:80%;max-width:320px;background:var(--card);border-right:1px solid var(--border);padding:20px 16px;z-index:90;transform:translateX(-100%);transition:transform .2s ease-out;overflow-y:auto}
.sidebar-mobile.open{transform:translateX(0)}
.sidebar-mobile .wordmark{display:block;margin-bottom:24px;padding-bottom:16px;border-bottom:1px solid var(--border)}

/* Responsive */
@media (max-width:1023px){
  .sidebar{display:none}
  .hamburger{display:block}
  .main{padding:24px 16px 48px}
  .topbar-search{display:none}
  .topbar-invite-btn{display:none}  /* label "Inviter" mange trop sur mobile ; le bouton reste accessible via le drawer mobile */
  .wordmark{font-size:13px;letter-spacing:.14em}
}

/* Headings & basic typography */
h1,h2,h3{margin:0;font-family:var(--font-sans);font-weight:500;color:var(--foreground)}
p{margin:0 0 12px}

/* Section headers */
.sec-head{margin-bottom:32px}
.sec-body{display:flex;align-items:flex-start;justify-content:space-between;gap:20px;flex-wrap:wrap}
.sec-body h1{font-family:var(--font-sans);font-weight:400;font-size:36px;line-height:1.1;letter-spacing:-.01em;margin:0 0 12px;color:var(--foreground)}
.sec-body h1 em,h1 em,h2 em,h3 em{font-style:normal;font-weight:300;color:var(--muted-foreground)}
.sec-body .lede{margin:0;color:var(--foreground);font-size:14px;font-weight:300;max-width:600px;line-height:1.55}
.sec-actions{display:flex;gap:10px;align-items:center;flex-wrap:wrap}

@media (max-width:1023px){
  .sec-body h1{font-size:28px}
}

/* ───── Breadcrumb (Admin / Page) ───── */
.admin-bcr{font-size:11.5px;color:var(--muted-foreground);margin-bottom:6px;display:flex;align-items:center;gap:6px;font-family:var(--font-sans)}
.admin-bcr a{color:inherit;text-decoration:none;transition:color .14s}
.admin-bcr a:hover{color:var(--accent)}
.admin-bcr b{color:var(--foreground);font-weight:500}
.admin-bcr .sep{opacity:.5;font-size:10px}

/* ───── Sec-head compact variant (replaces giant title) ───── */
.sec-head--compact{margin-bottom:18px}
.sec-head--compact .sec-body h1{font-size:22px;font-weight:600;letter-spacing:-.01em;margin:0 0 4px}
.sec-head--compact .sec-body .lede{font-size:13px}
@media(max-width:1023px){.sec-head--compact .sec-body h1{font-size:20px}}

/* ── La barre d'ONGLETS D'UNE ENTREE (2026-08-25) ─────────────────────────────
   Les pages d'une meme entree de menu : « Tous » et « Doublons » pour Contacts,
   les dix ecrans de Reglages. Rendue par la COQUILLE depuis
   `espace/navigation.py`, jamais par l'ecran -- c'est ce qui permet de regrouper
   une quarantaine de pages sans en modifier une seule.

   /!\ Volontairement DIFFERENTE de `.seg--onglets`, la pilule. Plusieurs ecrans
   portent deja une barre de VUES en pilule (« Tous 2 · Clients 2 · Prospects 0 »)
   quelques dizaines de pixels plus bas : deux pilules empilees se lisent comme
   une seule commande coupee en deux. Celle-ci est un SOULIGNEMENT pleine
   largeur, le vocabulaire d'une navigation de section.

   /!\ Elle est au-dessus du titre parce qu'elle ne parle pas de la page mais de
   la SECTION qui la contient -- le fil d'Ariane la reprend juste en dessous.

   /!\ Elle defile a plat quand elle deborde (onze onglets sur Reglages), elle ne
   passe pas a la ligne : une barre de navigation sur deux lignes ne se lit plus
   comme une barre. */
.onglets-entree {
  display: flex;
  gap: 2px;
  margin: -8px 0 22px;
  border-bottom: 1px solid var(--border);
  max-width: 100%;
  overflow-x: auto;
  scrollbar-width: none;
}
.onglets-entree::-webkit-scrollbar { height: 0; }
.onglets-entree > a {
  flex: 0 0 auto;
  padding: 9px 14px;
  font-size: 13px;
  font-weight: 500;
  color: var(--muted-foreground);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color var(--motion-base) var(--ease-out),
              border-color var(--motion-base) var(--ease-out);
}
.onglets-entree > a:hover { color: var(--foreground); }
.onglets-entree > a[aria-current="page"] {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* Un titre de carte qui porte une icone (2026-08-25, page Reglages).
   /!\ En `flex`, et non l'icone posee dans le flux : `.carte-titre` porte une
   marge basse, donc il est employe sur un BLOC -- l'icone tombait alors sur sa
   propre ligne, au-dessus du titre. */
.carte-titre--icone { display: flex; align-items: center; gap: 8px; }
.carte-titre--icone svg { flex-shrink: 0; color: var(--accent); }

/* Le titre et le sous-titre du RECAPITULATIF d'arrivee (« Votre trader est
   pret »), dans `user_trader_edit.html`.

   /!\ Ces deux regles sont nees le 2026-08-28 dans la section de l'assistant de
   creation, qui portait ses six questions dans des `.etape`. L'assistant est
   parti le 2026-08-29 ; elles restent, parce que le recapitulatif les emploie.
   Les emporter avec le reste laissait la carte d'arrivee sans titre ni
   sous-titre : aucune erreur, aucun test de logique, ca ne se voit qu'a l'oeil.
   `tests/test_assistant_retire.py` les epingle desormais. */
.etape-titre {
  margin: 0 0 6px;
  font-family: var(--font-sans);
  font-size: 21px; font-weight: 600;
  letter-spacing: -0.02em; line-height: 1.25;
  color: var(--foreground);
}
.etape-sous {
  margin: 0 0 20px;
  font-size: 13.5px; line-height: 1.65;
  color: var(--muted-foreground);
}
.etape-sous:last-child { margin-bottom: 0; }
.etape-sous a { color: var(--accent); text-decoration: none; }
.etape-sous a:hover { text-decoration: underline; }

/* -----------------------------------------------------------------------------
   LES DEUX TABLEAUX DE /team/relances (2026-08-30)

   « Les miennes » et « La pioche » se lisent comme un SEUL tableau : le
   commercial descend de l'un a l'autre, et rien ne doit bouger sous ses yeux.

   /!\ `table-layout: fixed` est ce qui fait tenir les largeurs, exactement
   comme pour `.tableau--paliers` plus haut. Sans lui, le navigateur les
   recalcule sur le CONTENU : le bouton « Prendre », present dans le second
   tableau seulement, elargissait sa colonne et decalait toutes les autres. Les
   deux tableaux ne s'alignaient donc plus entre eux.

   /!\ **`text-align: left` sur les en-tetes, et c'est la moitie du defaut.**
   Un `<th>` est CENTRE par defaut (regle de l'agent utilisateur), les cellules
   sont a gauche : l'en-tete flottait au milieu de sa colonne. Rien dans ce
   depot ne posait la regle, parce que `.tbl` n'a AUCUNE feuille de style — il
   n'est qu'un nom, porte par quatre autres ecrans. D'ou le modificateur : une
   classe partagee par deux objets est un piege paye au moins une fois ici.

   /!\ La colonne d'action RESTE dans « Les miennes », vide. La retirer
   redonnerait deux grilles differentes, c'est-a-dire le defaut lui-meme.
   -------------------------------------------------------------------------- */
.tbl--relances { width: 100%; border-collapse: collapse; table-layout: fixed; }
.tbl--relances th {
  text-align: left; padding: 0 10px 8px 0; vertical-align: bottom;
  font-size: 10.5px; font-weight: 600; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted-foreground);
}
.tbl--relances td {
  padding: 10px 10px 10px 0; vertical-align: top;
  border-top: 1px solid var(--border);
}
.tbl--relances th:last-child, .tbl--relances td:last-child { padding-right: 0; }
/* Les SEPT largeurs, en POURCENTAGES : posees une seule fois, sur les
   en-tetes, et les deux tableaux sortent du meme macro de gabarit.
   /!\ La septieme est arrivee avec le bouton « Fait » (lot 3) : sans
   `table-layout: fixed`, elle aurait a elle seule redecale les six autres,
   c'est-a-dire exactement le defaut que ce bloc existe pour empecher. */
.tbl--relances th:nth-child(1) { width: 9%; }
.tbl--relances th:nth-child(2) { width: 17%; }
.tbl--relances th:nth-child(3) { width: 11%; }
.tbl--relances th:nth-child(4) { width: 19%; }
.tbl--relances th:nth-child(5) { width: 27%; }
.tbl--relances th:nth-child(6) { width: 8%; }
.tbl--relances th:nth-child(7) { width: 9%; }
/* Le commentaire est une PHRASE : elle s'enroule dans sa colonne au lieu de la
   pousser. `overflow-wrap` couvre une adresse ou un mot sans espace. */
.tbl--relances .relance-commentaire {
  overflow-wrap: anywhere;
  color: var(--muted-foreground); font-size: 12.5px; line-height: 1.45;
}
/* /!\ Le paragraphe d'explication de la pioche courait jusqu'au bord de la
   FENETRE, bien au-dela du tableau. Meme mesure que `.sec-body .lede` (600 px,
   plus haut dans ce fichier) : une explication se lit sur une colonne, jamais
   sur un ecran. */
.relances-explication { max-width: 600px; }

/* -----------------------------------------------------------------------------
   LES DEUX TABLEAUX PROPRES A « MA JOURNEE » (2026-08-30)

   /!\ **Un modificateur a eux, pas `.tbl--relances`.** Leurs colonnes ne sont
   pas les memes (quatre au lieu de sept) : leur preter la grille des relances
   les aurait ecrases sur les largeurs d'un autre objet. Une classe partagee
   par deux objets est le piege le plus cher de ce depot, ecrit trois fois
   dans ce fichier.

   /!\ **ET C'EST POURQUOI ILS EN ONT DEUX, PAS UN.** Un seul modificateur,
   nomme tbl--journee, les habillait tous les DEUX (sous un commentaire qui
   promettait pourtant le contraire) :
   les rendez-vous (Heure / Personne / Objet / Ou) et les dossiers silencieux
   (Silence / Dossier / Phase / bouton) n'ont pas les memes colonnes, et la
   justification de la 1re largeur ci-dessous — « elle porte aussi *jamais de
   nouvelles* » — appartient au SECOND et s'imposait au premier. Une heure n'a
   jamais eu besoin de 15 % de la ligne.

   /!\ Le squelette reste ECRIT UNE FOIS, en listant les deux selecteurs ;
   seules les LARGEURS distinguent les deux objets. Recopier le squelette pour
   chacun serait le defaut symetrique : deux copies derivent.

   /!\ `.tbl` n'a AUCUNE feuille de style : il n'est qu'un nom. Tout ce qui
   suit doit donc etre pose ici, y compris le `text-align: left` des en-tetes,
   qu'un `<th>` centre par defaut.
   -------------------------------------------------------------------------- */
.tbl--reunions, .tbl--silence {
  width: 100%; border-collapse: collapse; table-layout: fixed;
}
.tbl--reunions th, .tbl--silence th {
  text-align: left; padding: 0 10px 8px 0; vertical-align: bottom;
  font-size: 10.5px; font-weight: 600; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted-foreground);
}
.tbl--reunions td, .tbl--silence td {
  padding: 10px 10px 10px 0; vertical-align: top;
  border-top: 1px solid var(--border);
}
.tbl--reunions th:last-child, .tbl--reunions td:last-child,
.tbl--silence th:last-child, .tbl--silence td:last-child { padding-right: 0; }
/* Les rendez-vous : une heure tient en peu de place, l'objet en demande. */
.tbl--reunions th:nth-child(1) { width: 10%; }
.tbl--reunions th:nth-child(2) { width: 30%; }
.tbl--reunions th:nth-child(3) { width: 40%; }
.tbl--reunions th:nth-child(4) { width: 20%; }
/* /!\ Les dossiers silencieux : la 1re colonne est plus large que ce qu'un
   nombre de jours demande, EXPRES, parce qu'elle porte aussi « jamais de
   nouvelles » (un dossier sans aucun signal ne s'annonce pas par un nombre de
   jours, qui serait exact et trompeur). A 10 % elle cassait cette phrase sur
   trois lignes. */
.tbl--silence th:nth-child(1) { width: 15%; }
.tbl--silence th:nth-child(2) { width: 31%; }
.tbl--silence th:nth-child(3) { width: 34%; }
.tbl--silence th:nth-child(4) { width: 20%; }

/* ============================================================
   Le BANDEAU DE TRAITEMENT d'une fiche : `.traiter` (2026-08-31)
   ============================================================
   Deux colonnes, au-dessus des onglets d'une fiche contact : a gauche ce qui
   s'est DIT (l'etat du dossier, les deux derniers echanges en clair), a droite
   les faits qu'on CITE au telephone (`.bilan`, deja de la charte).

   /!\ Il repond a « que ca prenne tres peu de temps de traiter un contact ».
   L'information la plus utile de l'ecran etait la plus enterree : le texte du
   dernier echange vivait dans un onglet, donc a un clic et un defilement.

   /!\ IL NE REUTILISE PAS `.vivant`. Cette classe existe plus haut dans ce
   fichier et aucun gabarit ne la porte, mais ses quatre modificateurs
   qualifient un TRADER : une position ouverte, un ordre en attente, l'arret.
   Les emprunter pour la phase d'un dossier CRM ferait une classe partagee par
   deux objets, le piege le plus cher de ce depot, ecrit trois fois ici.

   /!\ La colonne droite ne descend pas sous 300 px : `.bilan-valeur` est en
   chasse fixe, et un numero de telephone qui revient a la ligne se lit comme
   deux numeros. Sous 900 px les deux colonnes s'empilent, seul arbitrage
   possible sur un ecran etroit.

   /!\ Le texte d'un echange garde ses retours a la ligne (`pre-wrap`) : un
   commentaire d'appel se saisit en plusieurs lignes, et les aplatir le rend
   illisible. */
.traiter {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(300px, 380px);
  gap: 20px;
  padding: 18px 20px;
  margin-bottom: 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
}
@media (max-width: 900px) {
  .traiter { grid-template-columns: 1fr; }
}
.traiter-dit   { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.traiter-faits { display: flex; flex-direction: column; gap: 12px; min-width: 0; }

/* L'etat du dossier. La pastille DOUBLE la couleur du texte au lieu de la
   remplacer : une teinte seule est illisible pour huit hommes sur cent. */
.traiter-dossier { display: flex; align-items: flex-start; gap: 10px; }
.traiter-dossier-pastille {
  width: 8px; height: 8px; border-radius: 50%; flex: none; margin-top: 6px;
  background: var(--disabled-foreground);
}
.traiter-dossier-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.traiter-dossier--open .traiter-dossier-pastille { background: var(--info); }
.traiter-dossier--won  .traiter-dossier-pastille { background: var(--success); }
.traiter-dossier--lost .traiter-dossier-pastille { background: var(--error); }

.traiter-echange {
  padding: 10px 12px; border-radius: var(--radius-sm);
  background: var(--muted); min-width: 0;
}
.traiter-echange-tete {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  font-size: var(--text-body-sm);
}
/* /!\ DEUX classes, et la difference est `pre-wrap`. `.traiter-echange-texte`
   porte un texte SAISI par un commercial : ses retours a la ligne comptent.
   `.traiter-phrase` porte un texte du GABARIT, ecrit sur plusieurs lignes dans
   le fichier pour tenir dans quatre-vingts colonnes : sous `pre-wrap`, son
   indentation de source devenait visible a l'ecran et la premiere ligne
   commencait plus a gauche que les suivantes (vu par le fondateur le
   2026-08-31, sur le bandeau d'un contact sans historique). Une phrase du
   gabarit ne doit jamais dependre de la facon dont on l'a coupee dans le
   fichier. */
.traiter-echange-texte, .traiter-phrase {
  margin: 6px 0 0; overflow-wrap: anywhere;
  font-size: var(--text-body-sm); color: var(--foreground);
}
.traiter-echange-texte { white-space: pre-wrap; }
.traiter-etiquettes { display: flex; flex-wrap: wrap; gap: 6px; }

/* /!\ Le bandeau DEGRADE le dit. `fiche_contact.bandeau` ne leve jamais : sur
   une exception il rend des replis, qui se lisent comme des faits. Le ton est
   celui d'un avertissement, pas d'une erreur : la page, elle, est bonne. */
.traiter-incomplet {
  padding: 8px 12px; border-radius: var(--radius-sm);
  border: 1px solid var(--warning); color: var(--foreground);
  font-size: var(--text-body-sm);
}

/* /!\ LE TEXTE D'UN ECHANGE DANS LE FIL, et il a sa propre regle EXPRES.
   Le voisin evident, `.rangee-texte`, tronque a `max-height: 3em` (deux lignes)
   pour une liste de regles depliables : le fil d'un contact n'a pas de
   depliement, un commentaire d'appel y serait donc coupe net et le reste
   deviendrait irrecuperable. Cette classe n'avait AUCUNE regle jusqu'au
   2026-08-31, ce qui invitait a la « reparer » vers celle qui tronque. Elle en a
   une, et c'est la meme que le texte d'un echange du bandeau : les retours a la
   ligne d'un commentaire saisi comptent. */
.fil-texte {
  margin-top: 4px; white-space: pre-wrap; overflow-wrap: anywhere;
  font-size: 13.5px; line-height: 1.5;
}

/* ============================================================
   L'ETIQUETTE d'un contact : `.tag-chip` (2026-08-31)
   ============================================================
   /!\ CE N'EST PAS UNE BRIQUE NEUVE, c'est un RAPATRIEMENT. Ces regles vivaient
   dans le `<style>` du gabarit du tiroir contact (`admin_contact_drawer.html`,
   retire le 2026-08-31), et nulle part ailleurs : ni dans cette feuille, ni dans
   `admin_base.html`. La fiche etend `admin_base.html`, donc sans ce
   rapatriement une etiquette serait un texte sur un aplat de couleur pleine,
   sans arrondi et sans marge.

   /!\ UN SEUL `<style>` local redefinit `.tag-chip` (`admin_contacts.html`), et
   il se charge APRES cette feuille : il continue de gagner sur son ecran, ce
   rapatriement ne change rien pour lui. /!\ **La redaction du 2026-08-31 en
   nommait DEUX et se trompait sur le second** : `admin_rules.html` definit
   `.tag-chip-preview`, une AUTRE classe, et n'emploie `.tag-chip` nulle part.
   Sans consequence, et c'est justement le genre de phrase qu'on relit dans deux
   mois pour decider quelque chose.

   /!\ La couleur, elle, reste INLINE : elle vient de la base
   (`tags.color`), une regle statique ne peut pas la porter. La convention est
   celle des cinq emplois du depot : fond a 10 %, texte et bordure a pleine
   teinte. */
.tag-chip {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; padding: 2px 9px; border-radius: 999px; line-height: 1.6;
  border: 1px solid transparent; font-family: var(--font-sans);
}
.tag-chip--editable { padding-right: 4px; }
.tag-chip-x {
  border: none; background: transparent; color: inherit; cursor: pointer;
  font-size: 14px; line-height: 1; padding: 0 2px; opacity: .7;
}
.tag-chip-x:hover { opacity: 1; }
.tag-editor-chips {
  display: flex; flex-wrap: wrap; gap: 6px; align-items: center; min-height: 24px;
}

/* ============================================================
   La BARRE DES GESTES d'une fiche : `.barre-fiche` (2026-08-31)
   ============================================================
   Six gestes toujours sous la main, en bas de la fiche d'un contact.
   Emplacement retenu par le fondateur apres comparaison de trois maquettes.

   /!\ ELLE NE REUTILISE PAS `.barre-enregistrement`, et c'est mesure : cette
   derniere se CACHE tant que rien n'a change (`[data-cachable]`, plus haut dans
   ce fichier), parce qu'elle sert a ENREGISTRER. Les six gestes, eux, doivent
   rester visibles en permanence. Une variante de la meme classe ferait dependre
   les gestes d'un attribut pose par `enregistrer.js` pour un autre objet, et une
   classe partagee par deux objets est le piege le plus cher de ce depot.

   La GEOMETRIE, elle, est recopiee, avec ses raisons :

   /!\ `position: fixed`, PAS `sticky` : c'est la FENETRE qui defile (`.main` n'a
   ni hauteur ni `overflow`) et `.main` porte un `padding-bottom` de 64 px. Une
   barre collante cesserait de coller 64 px avant le bas de page.

   /!\ Elle doit rester DANS `.wrap`, jamais enfant direct de `.main` :
   `admin_base.html` pose `.main > * { position: relative }` avec la MEME
   specificite, dans un `<style>` charge APRES cette feuille. En enfant direct,
   elle redeviendrait `relative` et flotterait en bas du flux -- et le defaut ne
   se verrait meme pas sur une page courte. Bonus du bon placement : `.wrap`
   porte `z-index: 1`, donc la barre passe seule sous la barre d'application et
   sous le menu mobile.

   /!\ Le decalage a gauche suit la barre laterale (200 px), qui disparait sous
   1024 px comme partout ailleurs. Meme borne, une seule requete media.

   /!\ La reserve sous la page est posee sur `.main`, pas sur la barre : c'est la
   PAGE qui doit rendre de la place, pas la barre qui doit en prendre moins.

   /!\ Le jour ou quelqu'un pose `transform`, `filter`, `backdrop-filter` ou
   `contain` sur `.main` ou `.wrap`, la barre s'ancrera a ce conteneur au lieu de
   l'ecran et cessera d'etre fixe. Aucun test HTML n'attrape cette casse. */
.barre-fiche {
  position: fixed;
  left: 200px; right: 0; bottom: 0;
  z-index: 45;
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 12px 40px;
  padding-bottom: calc(12px + env(safe-area-inset-bottom));
  background: rgba(14, 16, 24, 0.94);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  border-top: 1px solid var(--border-strong);
  box-shadow: 0 -16px 44px -18px rgba(0,0,0,0.75);
}
@media (max-width: 1023px) {
  .barre-fiche { left: 0; padding: 10px 16px; }
}
body:has(.barre-fiche) .main { padding-bottom: 132px; }
.barre-fiche-gestes { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }

/* UN geste. La couleur est sur le PICTOGRAMME, jamais en aplat sur le fond :
   six pastilles pleines cote a cote se lisent comme six boutons principaux, et
   l'ecran n'a plus de voix forte. Au survol, le bouton s'entoure d'un lisere et
   d'un voile de SA couleur. */
.geste {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 12px; border-radius: var(--radius-sm);
  border: 1px solid transparent; background: transparent;
  color: var(--muted-foreground); font-size: var(--text-body-sm);
  cursor: pointer; text-decoration: none;
  transition: border-color var(--motion-base) var(--ease-out),
              background var(--motion-base) var(--ease-out);
}
.geste[data-couleur="success"] svg { color: var(--success); }
.geste[data-couleur="info"]    svg { color: var(--info); }
.geste[data-couleur="accent"]  svg { color: var(--accent); }
.geste[data-couleur="warning"] svg { color: var(--warning); }
/* /!\ Le ROUGE d'« A relancer » n'est PAS un avertissement, c'est un repere de
   famille. La charte reserve `--error` aux erreurs partout ailleurs ; ici il
   qualifie un geste banal, et le fondateur l'a valide a l'ecran. Si l'ambiguite
   gene un jour, c'est ce geste qui prend `--warning` et « Note » un autre
   repere -- pas `--error` qui change de sens. */
.geste[data-couleur="error"]   svg { color: var(--error); }
.geste:hover { border-color: currentColor; background: var(--muted); }

/* /!\ Un geste IMPOSSIBLE reste AFFICHE, grise, avec sa raison. Un bouton absent
   fait chercher ce qu'on a mal fait ; c'est la regle deja ecrite pour le bouton
   de retrait de « Mes gains ». */
.geste[aria-disabled="true"] { opacity: 0.55; cursor: default; }
.geste[aria-disabled="true"]:hover { border-color: transparent; background: transparent; }
.geste-raison {
  font-style: normal; font-size: var(--text-caption);
  color: var(--disabled-foreground);
}

/* La touche, REVELEE AU SURVOL. Un raccourci que rien n'annonce n'existe pour
   personne ; six raccourcis affiches en permanence ajoutent six symboles a lire
   en travaillant. Le survol est le compromis, et le `title` porte la meme
   information pour le clavier et les lecteurs d'ecran.
   /!\ `visibility` et non `display` : un `display:none` ferait sauter la largeur
   du bouton au survol, et les six gestes se decaleraient les uns les autres. */
.geste-kbd {
  visibility: hidden;
  font-family: var(--font-mono); font-size: 10.5px; line-height: 1;
  padding: 2px 5px; border: 1px solid currentColor; border-radius: 3px;
  color: inherit; opacity: 0.7;
}
.geste:hover .geste-kbd, .geste:focus-visible .geste-kbd { visibility: visible; }

/* Les six raccourcis de la fenetre « A relancer ».
   /!\ ELLE NE S'APPELLE PAS `.jetons`, et c'est mesure : `.jetons` existe deja,
   c'est le conteneur des `.jeton`, les pastilles de FILTRE, et le commentaire
   juste au-dessus dit « Un jeton est un FILTRE, pas une etiquette ». Elle est
   employee par `admin_calendar_fragment.html`. Ajoutee a la fin du fichier, une
   seconde `.jetons` gagne (derniere regle) et fait passer les pastilles de
   devise du calendrier de `inline-flex` a `flex`, avec une marge verticale en
   prime : une regression visuelle sur un AUTRE ecran, qu'aucun test ne voit. */
.jetons-report { display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0; }

/* La rangee d'enregistrement d'une fenetre ou d'un panneau de fiche. DANS LE
   FLUX, jamais fixe : `.barre-fiche` occupe deja le bas de l'ecran.
   /!\ Ce n'est PAS `.actions-partie` : cette classe-la n'existe pas dans
   `charte.css`, elle est definie localement dans `abonnement.html`,
   `user_gains.html` et `user_profile.html`, trois gabarits que
   `admin_contact_fiche.html` n'etend pas. L'employer ici donnerait une rangee
   sans style, et aucun test ne le verrait. */
.fiche-actions { display: flex; align-items: center; gap: 10px; margin-top: 16px; }

/* La TETE d'une ligne de fil : le type de l'echange, sa marque « A venir », son
   issue. Une ligne a part, parce que ces trois-la se lisent ensemble et
   doivent pouvoir passer a la ligne sans desaligner la date en dessous.
   /!\ Elle ne touche PAS a `.rangee-corps`, portee par cinq autres gabarits :
   voir le commentaire de `partials/fil_activites.html`. */
.rangee-tete { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }

/* Les GESTES d'une ligne de fil (2026-08-31), dans la TROISIEME colonne de
   `.rangee`. Ils viennent du panneau d'activites du tiroir de contact, retire ce
   jour-la : « Modifier » et « Supprimer » ne vivaient que la.
   /!\ Ils ne touchent PAS a `.rangee-corps`, portee par cinq autres gabarits :
   voir le commentaire de `partials/fil_activites.html`. */
.fil-gestes { display: flex; align-items: flex-start; gap: 6px; }

/* Le depliant « Modifier » d'une ligne de fil.
   /!\ La fleche que le navigateur ajoute a tout `<summary>` doublerait le bouton :
   le `<summary>` EST le bouton ici. Meme geste que `.menu > summary`
   (`charte.css:1989`), qu'on ne peut pas reutiliser tel quel — `.menu` est un
   menu deroulant positionne, pas un depliant en ligne. */
/* Le depliant « C'etait une reunion » de la fenetre « Consigner » (2026-08-31).
   Meme motif que `.fil-editer` ci-dessous, et meme raison : le `<summary>` EST
   le bouton, sa fleche native le doublerait. */
.fiche-reunion { margin-top: 14px; border-top: 1px solid var(--muted); padding-top: 14px; }
.fiche-reunion > summary { list-style: none; cursor: pointer; display: inline-flex; }
.fiche-reunion > summary::-webkit-details-marker { display: none; }
.fiche-reunion > summary::marker { content: ""; }

.fil-editer { margin-top: 6px; }
.fil-editer > summary { list-style: none; cursor: pointer; display: inline-flex; }
.fil-editer > summary::-webkit-details-marker { display: none; }
.fil-editer > summary::marker { content: ""; }
.fil-editer form { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.fil-editer .btn { align-self: flex-start; }

/* Le compteur d'un onglet de `.seg--onglets` (« Le fil 12 »). Meme objet que
   `.drawer-seg-ct` (`charte.css:6158`), mais celle-la est reservee a la barre
   d'un TIROIR : on ne partage pas un nom entre deux objets, on en ecrit un pour
   celui-ci. */
.seg-ct {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--text-caption); color: var(--muted-foreground);
  margin-left: 6px;
}

/* ============================================================
   L'EDITEUR MULTI-VALEURS : `.mv-*` (2026-08-31)
   ============================================================
   /!\ CE N'EST PAS UNE BRIQUE NEUVE, c'est un RAPATRIEMENT, comme `.tag-chip`
   plus haut. Ces huit regles vivaient dans le `<style>` du gabarit du tiroir
   contact (`admin_contact_drawer.html:12-20`, retire le 2026-08-31), et NULLE
   PART ailleurs. La fiche etend `admin_base.html` : sans elles, l'editeur
   d'e-mails et de telephones est une pile de champs pleine largeur, l'indicatif
   pays occupe une ligne a lui seul et la croix de retrait est un gros « x »
   noir.

   /!\ Elles n'ecrasaient rien : le `<style>` du tiroir se chargeait APRES cette
   feuille et continue de gagner sur son ecran, a l'identique, jusqu'a son
   retrait. */
.mv-rows { display: flex; flex-direction: column; gap: 8px; }
.mv-row { display: flex; align-items: center; gap: 8px; }
.mv-row .mv-input { flex: 1; min-width: 0; }
.mv-row .mv-dial { flex: 0 0 92px; width: 92px; }
.mv-primary {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; color: var(--muted-foreground);
  white-space: nowrap; cursor: pointer; user-select: none;
}
.mv-primary input { margin: 0; }
.mv-remove {
  border: none; background: transparent; color: var(--muted-foreground);
  cursor: pointer; font-size: 18px; line-height: 1; padding: 0 4px;
  border-radius: 6px;
}
.mv-remove:hover { color: var(--danger, #b4451f); background: var(--muted); }
.mv-add { margin-top: 8px; }

/* La NAVIGATION d'une fiche : retour, deux fleches, et « 3 sur 128 » entre
   elles (2026-08-31). Elle vit dans le fil d'Ariane, au-dessus du titre : c'est
   la place ou l'oeil cherche « ou suis-je », et elle ne prend pas de ligne. */
/* ── LES ANCRES D'UNE FICHE D'OBJET (2026-09-02, lot 7) ───────────────────────
   /!\ Des LIENS `#id` natifs, pas des boutons. Le tiroir avait besoin de
   `drawer-anchors.js` (boutons `data-go`, observateur de mutations, defilement
   calcule) parce que son corps defilait dans un panneau ; une page defile toute
   seule. /!\ Classe PROPRE et non `.basc` : celle-la habille une bascule
   (un choix exclusif), et deux objets differents sous une meme classe est le piege
   `.puce` deja paye dans ce depot. */
.fiche-ancres {
  display: flex; flex-wrap: wrap; gap: 4px;
  margin: 0 0 18px; padding-bottom: 10px;
  border-bottom: 1px solid var(--border);
}
.fiche-ancres a {
  padding: 4px 11px; border-radius: var(--radius-pill);
  border: 1px solid transparent;
  font-size: 12.5px; color: var(--muted-foreground); text-decoration: none;
}
.fiche-ancres a:hover {
  border-color: var(--border-strong); color: var(--foreground);
}
/* /!\ `:target` habille la section VISEE, pas l'ancre : c'est la seule facon, sans
   script, de dire ou l'on vient d'atterrir. */
.encadre:target { border-color: rgba(180,168,255,0.55); }

/* ── LA BARRE D'ENREGISTREMENT D'UNE FICHE (2026-09-03) ──────────────────────
   /!\ Ces classes ont ete ECRITES au lot 7 et jamais HABILLEES : `.fiche-barre`,
   `.role-chips` et `.role-chip-toggle` n'avaient AUCUNE regle. Le bouton
   « Enregistrer » se collait donc au bas de la carte precedente, et la liste des
   roles empilait ses cases a cocher brutes. Dans le tiroir, ces memes elements
   portaient leur mise en page en STYLE EN LIGNE ; la page les a repris en classes,
   sans emporter le style avec. Un nom de classe sans regle ne casse rien de
   visible dans un test : il ne se voit qu'a l'ecran. */
.fiche-barre {
  display: flex; align-items: center; gap: 12px;
  margin: 14px 0 4px;
}
.fiche-barre .muted { font-size: 12.5px; }

/* Les ROLES d'un compte : une case a cocher, son libelle, et ce que le role ouvre. */
.role-chips { display: flex; flex-direction: column; gap: 6px; }
.role-chip-toggle {
  display: flex; align-items: flex-start; gap: 9px;
  padding: 8px 11px;
  border: 1px solid var(--border); border-radius: var(--radius-md);
  font-size: 12.5px; cursor: pointer;
  transition: border-color .14s, background .14s;
}
.role-chip-toggle:hover { border-color: var(--border-strong); }
.role-chip-toggle > input { margin: 2px 0 0; flex-shrink: 0; }
.role-chip-toggle > span {
  display: flex; flex-direction: column; gap: 2px; min-width: 0;
}
/* /!\ Le NOM du role et son cadenas sur UNE ligne : les icones de ce depot sont
   des `svg` en display:block, donc le libelle tombait sous le cadenas. */
.role-chip-toggle > span > span:first-child {
  display: flex; align-items: center; gap: 6px;
  font-weight: 500; color: var(--foreground);
}
.role-chip-toggle .muted {
  font-size: 11px; line-height: 1.35;
  font-weight: 400; color: var(--muted-foreground);
}
/* /!\ L'etat coche s'habille en CSS (`:has`), pas en Jinja comme le faisait le
   tiroir : cote serveur, la case cochee ne changeait de couleur qu'au rechargement
   suivant — on cochait, rien ne bougeait, et on recochait. */
.role-chip-toggle:has(input:checked) {
  background: rgb(74 59 122 / 0.10);
  border-color: var(--accent);
}

.fiche-nav { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }
.fiche-nav-rang {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--text-caption); color: var(--muted-foreground);
  padding: 0 4px; white-space: nowrap;
}

/* ============================================================
   .t-onglets — les QUATRE ecrans d'un trader (chantier C9, lot L1)
   ------------------------------------------------------------
   /!\ C'est une NAVIGATION, pas un selecteur de panneaux : quatre liens vers
   quatre adresses, sans une ligne de script. A ne pas confondre avec la barre
   DANS les reglages, qui masque des panneaux d'un meme formulaire et vit sur
   le crochet `[data-onglets]` que `onglets-formulaire.js` cherche UNE seule
   fois dans la page. Poser ce crochet ici ferait cesser les sections de
   basculer.

   /!\ L'ecran courant se marque par `aria-current="page"`, pas par une classe :
   l'etat est alors dit au lecteur d'ecran en meme temps qu'il est peint.
   ============================================================ */
.t-onglets {
  display: flex; gap: 2px; flex-wrap: wrap;
  border-bottom: 1px solid var(--border);
  margin: 0 0 28px;
}
.t-onglets a {
  padding: 9px 15px; text-decoration: none; font-size: 13.5px;
  color: var(--muted-foreground);
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  transition: color .14s, border-color .14s;
}
/* ============================================================
   .cfg-ligne — une CONFIGURATION du client (chantier C5, 2026-09-07)
   ------------------------------------------------------------
   Un nom court a gauche, sa description a droite, la croix au bout. Deux
   niveaux, aucun cycle de vie : ni poignee de glissement, ni rang, ni badge
   d'archive — c'est ce greffage qui avait porte les anciennes regles a 1 955
   lignes, et il ne doit pas revenir par le style.
   ============================================================ */
.cfg-ligne {
  display: grid;
  grid-template-columns: minmax(140px, 220px) minmax(0, 1fr) auto;
  gap: 10px;
  align-items: start;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}
.cfg-ligne:last-of-type { border-bottom: 0; }
.cfg-ligne .cfg-nom {
  /* Le nom sert d'etiquette et de colonne de regroupement : on le montre tel
     qu'il partira, en capitales, pour que le client voie ce qu'il ecrit. */
  text-transform: uppercase;
  letter-spacing: .04em;
  font-weight: 500;
}
.cfg-ligne .cfg-desc { resize: vertical; min-height: 44px; }
@media (max-width: 720px) {
  .cfg-ligne { grid-template-columns: minmax(0, 1fr) auto; }
  .cfg-ligne .cfg-desc { grid-column: 1 / -1; }
}

.t-onglets a:hover { color: var(--foreground); }
.t-onglets a[aria-current="page"] {
  color: var(--foreground); border-bottom-color: var(--accent);
}

/* ============================================================
   Les sections des REGLAGES passent SUR LE COTE (chantier C9, lot L2)
   ------------------------------------------------------------
   Demande du fondateur (2026-09-07) : « pour l'affichage des onglets dans les
   reglages, j'aimerais bien l'affichage sur le cote et non pas en ligne comme
   les onglets secondaires ». Deux barres horizontales l'une sous l'autre — la
   navigation entre ECRANS puis celle entre SECTIONS — se lisaient comme un
   seul niveau ; les mettre a la perpendiculaire dit qu'elles ne font pas la
   meme chose.

   /!\ RIEN NE CHANGE DANS LE HTML. La barre `.seg--onglets` et le `<form>`
   sont deja freres dans `.wrap--onglets` : il suffit de poser une grille. On
   ne touche donc ni au contrat `data-onglets` d'`onglets-formulaire.js`, ni a
   l'ordre des panneaux (dont depend l'affichage d'une cle regeneree), ni a
   `visite.py`.

   /!\ L'en-tete et la navigation entre ECRANS restent PLEINE LARGEUR : elles
   parlent du trader, pas de ses reglages.
   ============================================================ */
@media (min-width: 900px) {
  .wrap--reglages.wrap--onglets {
    display: grid;
    grid-template-columns: 210px minmax(0, 1fr);
    column-gap: 32px;
    align-items: start;
  }
  /* Tout ce qui n'est ni la barre ni le formulaire tient la largeur entiere :
     l'en-tete, la barre des quatre ecrans, les bandeaux d'arrivee. */
  .wrap--reglages.wrap--onglets > *:not(.seg--onglets):not(form) {
    grid-column: 1 / -1;
  }
  .wrap--reglages.wrap--onglets > .seg--onglets {
    /* /!\ On ANNULE `inline-flex` : la barre horizontale epousait ses onglets,
       celle-ci doit tenir sa colonne. */
    display: flex;
    flex-direction: column;
    align-items: stretch;
    overflow-x: visible;
    position: sticky;
    top: 72px;
    margin: 0;
    gap: 2px;
    /* /!\ ET ON ANNULE LA PILULE. `.seg` porte `border-radius: pill` avec un
       fond et un liseré : etire en colonne, ce rayon dessine un enorme DISQUE
       pale derriere les entrees. Vu a l'ecran le 2026-09-07 — un test ne l'aurait
       jamais montre. Une navigation laterale n'est pas un selecteur de segments :
       elle n'a ni fond ni contour. */
    background: none;
    border: 0;
    border-radius: 0;
    padding: 0;
  }
  .wrap--reglages.wrap--onglets > .seg--onglets > button {
    text-align: left;
    border-radius: var(--radius-sm, 8px);
    border-left: 2px solid transparent;
    padding: 8px 12px;
  }
  /* `aria-selected` est ce que pose `onglets-formulaire.js` — le seul marqueur
     de l'onglet ouvert, et il le pose deja pour les lecteurs d'ecran. */
  .wrap--reglages.wrap--onglets > .seg--onglets > button[aria-selected="true"] {
    border-left-color: var(--accent);
  }
  .wrap--reglages.wrap--onglets > form { grid-column: 2; }
}
